diff --git a/dist/fanqie-assistant.user.js b/dist/fanqie-assistant.user.js index 5b675ec..5fb910e 100644 --- a/dist/fanqie-assistant.user.js +++ b/dist/fanqie-assistant.user.js @@ -1,11 +1,14 @@ // ==UserScript== // @name 番茄小说助手 // @namespace https://github.com/naiyQAQ/fanqie-assistant -// @version 0.0.2 +// @version 0.0.3 // @author naiyQAQ // @description 番茄小说助手,去广告、去推广、解锁章节、优化体验。 +// @license GPLv3 // @icon data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjQiIGhlaWdodD0iNjQiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4NCiAgICA8cGF0aA0KICAgICAgICBkPSJNMjMuMzExNSAxSDYuNjg4NTNDMy41NDY4NCAxIDEgMy41NDY4NCAxIDYuNjg4NTNWMjMuMzExNUMxIDI2LjQ1MzIgMy41NDY4NCAyOSA2LjY4ODUzIDI5SDIzLjMxMTVDMjYuNDUzMiAyOSAyOSAyNi40NTMyIDI5IDIzLjMxMTVWNi42ODg1M0MyOSAzLjU0Njg0IDI2LjQ1MzIgMSAyMy4zMTE1IDFaIg0KICAgICAgICBmaWxsPSJ3aGl0ZSI+PC9wYXRoPg0KICAgIDxwYXRoDQogICAgICAgIGQ9Ik0yMy4zMTE1IDAuNzVINi42ODg1M0MzLjQwODc3IDAuNzUgMC43NSAzLjQwODc3IDAuNzUgNi42ODg1M1YyMy4zMTE1QzAuNzUgMjYuNTkxMiAzLjQwODc3IDI5LjI1IDYuNjg4NTMgMjkuMjVIMjMuMzExNUMyNi41OTEyIDI5LjI1IDI5LjI1IDI2LjU5MTIgMjkuMjUgMjMuMzExNVY2LjY4ODUzQzI5LjI1IDMuNDA4NzcgMjYuNTkxMiAwLjc1IDIzLjMxMTUgMC43NVoiDQogICAgICAgIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1vcGFjaXR5PSIwLjA4IiBzdHJva2Utd2lkdGg9IjAuNSI+PC9wYXRoPg0KICAgIDxtYXNrIGlkPSJtYXNrMF80NzBfNDgzNjQiIG1hc2tVbml0cz0idXNlclNwYWNlT25Vc2UiIHg9IjEiIHk9IjEiIHdpZHRoPSIyOCIgaGVpZ2h0PSIyOCI+DQogICAgICAgIDxwYXRoDQogICAgICAgICAgICBkPSJNMjMuMzExNSAxSDYuNjg4NTNDMy41NDY4NCAxIDEgMy41NDY4NCAxIDYuNjg4NTNWMjMuMzExNUMxIDI2LjQ1MzIgMy41NDY4NCAyOSA2LjY4ODUzIDI5SDIzLjMxMTVDMjYuNDUzMiAyOSAyOSAyNi40NTMyIDI5IDIzLjMxMTVWNi42ODg1M0MyOSAzLjU0Njg0IDI2LjQ1MzIgMSAyMy4zMTE1IDFaIg0KICAgICAgICAgICAgZmlsbD0id2hpdGUiPjwvcGF0aD4NCiAgICA8L21hc2s+DQogICAgPGcgbWFzaz0idXJsKCNtYXNrMF80NzBfNDgzNjQpIj4NCiAgICAgICAgPHBhdGgNCiAgICAgICAgICAgIGQ9Ik0yMy4zMTE1IDFINi42ODg1M0MzLjU0Njg0IDEgMSAzLjU0Njg0IDEgNi42ODg1M1YyMy4zMTE1QzEgMjYuNDUzMiAzLjU0Njg0IDI5IDYuNjg4NTMgMjlIMjMuMzExNUMyNi40NTMyIDI5IDI5IDI2LjQ1MzIgMjkgMjMuMzExNVY2LjY4ODUzQzI5IDMuNTQ2ODQgMjYuNDUzMiAxIDIzLjMxMTUgMVoiDQogICAgICAgICAgICBmaWxsPSJ3aGl0ZSI+PC9wYXRoPg0KICAgICAgICA8cGF0aA0KICAgICAgICAgICAgZD0iTTE1LjAwMDggNDguNjY0MkMyNS40MDE3IDQ4LjY2NDIgMzMuODMzNCA0MC4yMzI2IDMzLjgzMzQgMjkuODMxNkMzMy44MzM0IDE5LjQzMDcgMjUuNDAxNyAxMC45OTkgMTUuMDAwOCAxMC45OTlDNC41OTk4NSAxMC45OTkgLTMuODMxNzkgMTkuNDMwNyAtMy44MzE3OSAyOS44MzE2Qy0zLjgzMTc5IDQwLjIzMjYgNC41OTk4NSA0OC42NjQyIDE1LjAwMDggNDguNjY0MloiDQogICAgICAgICAgICBmaWxsPSJ1cmwoI3BhaW50MF9yYWRpYWxfNDcwXzQ4MzY0KSI+PC9wYXRoPg0KICAgICAgICA8cGF0aCBkPSJNMjMuMjY4OCAxVjcuMjEyOTRMMjAuNjY2MyA1LjcxNDM3TDE4LjA2NzQgNy4yMTI5NFYxSDIzLjI2ODhaIiBmaWxsPSIjRkY1RjAwIj48L3BhdGg+DQogICAgICAgIDxwYXRoDQogICAgICAgICAgICBkPSJNMTUuMTM0MyAxOC44ODFDMTUuMTM0MyAxOC44ODEgMTYuMTAxNCAxNy41NTEzIDE2LjEwMTQgMTYuNDA2NUMxNi4xMDE0IDE1LjI2MTcgMTUuNjY3NiAxNC43MzczIDE1LjEzNDMgMTQuNzM3M0MxNC42MDEgMTQuNzM3MyAxNC4xNjczIDE1LjI2MzUgMTQuMTY3MyAxNi40MDY1QzE0LjE2NzMgMTcuNTQ5NiAxNS4xMzQzIDE4Ljg4MSAxNS4xMzQzIDE4Ljg4MVoiDQogICAgICAgICAgICBmaWxsPSJ3aGl0ZSI+PC9wYXRoPg0KICAgICAgICA8cGF0aA0KICAgICAgICAgICAgZD0iTTcuNjI3MjQgMjIuNjU4NUM4Ljc3MjA1IDIyLjY1ODUgMTAuMTAxNyAyMy42MjU2IDEwLjEwMTcgMjMuNjI1NkMxMC4xMDE3IDIzLjYyNTYgOC43NzAyNyAyNC41OTI2IDcuNjI3MjQgMjQuNTkyNkM2LjQ4NDIgMjQuNTkyNiA1Ljk1ODAxIDI0LjE1ODkgNS45NTgwMSAyMy42MjU2QzUuOTU4MDEgMjMuMDkyMyA2LjQ4MjQyIDIyLjY1ODUgNy42MjcyNCAyMi42NTg1WiINCiAgICAgICAgICAgIGZpbGw9IndoaXRlIj48L3BhdGg+DQogICAgICAgIDxwYXRoDQogICAgICAgICAgICBkPSJNMjIuNjM5NiAyNC41OTI2QzIxLjQ5NDggMjQuNTkyNiAyMC4xNjUxIDIzLjYyNTYgMjAuMTY1MSAyMy42MjU2QzIwLjE2NTEgMjMuNjI1NiAyMS40OTY2IDIyLjY1ODUgMjIuNjM5NiAyMi42NTg1QzIzLjc4MjYgMjIuNjU4NSAyNC4zMDg4IDIzLjA5MjMgMjQuMzA4OCAyMy42MjU2QzI0LjMwODggMjQuMTU4OSAyMy43ODQ0IDI0LjU5MjYgMjIuNjM5NiAyNC41OTI2WiINCiAgICAgICAgICAgIGZpbGw9IndoaXRlIj48L3BhdGg+DQogICAgICAgIDxwYXRoDQogICAgICAgICAgICBkPSJNMTAuNDU1NSAxOC4zMTM5QzExLjI2NDMgMTkuMTIyNyAxMS41MjIxIDIwLjc0NzUgMTEuNTIyMSAyMC43NDc1QzExLjUyMjEgMjAuNzQ3NSA5Ljg5NzMyIDIwLjQ4OTcgOS4wODg0OCAxOS42ODA5QzguMjc5NjQgMTguODcyMSA4LjIxMzg3IDE4LjE5NDggOC41OTI1MSAxNy44MTYxQzguOTcxMTUgMTcuNDM3NSA5LjY0NjY2IDE3LjUwMzMgMTAuNDU3MyAxOC4zMTIxTDEwLjQ1NTUgMTguMzEzOVoiDQogICAgICAgICAgICBmaWxsPSJ3aGl0ZSI+PC9wYXRoPg0KICAgICAgICA8cGF0aA0KICAgICAgICAgICAgZD0iTTE4Ljc0NjUgMjAuNzQ3NkMxOC43NDY1IDIwLjc0NzYgMTkuMDA0MyAxOS4xMjI4IDE5LjgxMzEgMTguMzE0TDE5LjgxMTMgMTguMzEyMkMyMC42MjIgMTcuNTAzMyAyMS4yOTkzIDE3LjQzOTMgMjEuNjc2MSAxNy44MTYyQzIyLjA1NDggMTguMTk0OSAyMS45ODkgMTguODcyMSAyMS4xODAyIDE5LjY4MUMyMC4zNzEzIDIwLjQ4OTggMTguNzQ2NSAyMC43NDc2IDE4Ljc0NjUgMjAuNzQ3NloiDQogICAgICAgICAgICBmaWxsPSJ3aGl0ZSI+PC9wYXRoPg0KICAgIDwvZz4NCiAgICA8ZGVmcz4NCiAgICAgICAgPHJhZGlhbEdyYWRpZW50IGlkPSJwYWludDBfcmFkaWFsXzQ3MF80ODM2NCIgY3g9IjAiIGN5PSIwIiByPSIxIg0KICAgICAgICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiDQogICAgICAgICAgICBncmFkaWVudFRyYW5zZm9ybT0idHJhbnNsYXRlKDE1LjAwMDggMjkuODMxNikgc2NhbGUoMTguODMyNikiPg0KICAgICAgICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iI0NDMDUwMCI+PC9zdG9wPg0KICAgICAgICAgICAgPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjRkY1RjAwIj48L3N0b3A+DQogICAgICAgIDwvcmFkaWFsR3JhZGllbnQ+DQogICAgPC9kZWZzPg0KPC9zdmc+ // @match *://*.fanqienovel.com/* +// @require https://s4.zstatic.net/npm/vue@3.5.40/dist/vue.global.prod.js +// @require https://s4.zstatic.net/npm/moment@2.30.1/min/moment.min.js // @connect fanqienovel.com // @connect api5-sinfonlinec.jxbhmy.com // @connect api3-sinfonlinec.jxbhmy.com @@ -19,5446 +22,2638 @@ // @run-at document-start // ==/UserScript== -(function () { +(function (moment, vue) { 'use strict'; - var __getOwnPropNames = Object.getOwnPropertyNames; - var __commonJS = (cb, mod) => function __require() { - return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; + var __defProp = Object.defineProperty; + var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __publicField = (obj, key, value) => __defNormalProp(obj, key + "" , value); + const shared_key = new Uint8Array([172, 37, 198, 125, 221, 143, 56, 193, 179, 122, 35, 72, 130, 142, 34, 46]).buffer; + const fetch = unsafeWindow.fetch; + unsafeWindow.XMLHttpRequest; + const defaultConfig = { + install_id: "2187355326270644", + device_id: "2187355326004404", + device_type: "P30" }; - var require_main_001 = __commonJS({ - "main-BS5h560h.js"(exports, module) { - const shared_key = new Uint8Array([172, 37, 198, 125, 221, 143, 56, 193, 179, 122, 35, 72, 130, 142, 34, 46]).buffer; - const fetch = unsafeWindow.fetch; - unsafeWindow.XMLHttpRequest; - const defaultConfig = { - install_id: "2187355326270644", - device_id: "2187355326004404", - device_type: "P30" - }; - const _config = { - currentConfig: defaultConfig - }; - const scriptcss = "/* 移除章节锁定图标 */\r\n.muyeicon-lock {\r\n display: none;\r\n}\r\n/* 移除APP推广图标 */\r\n.muye-to-fanqie {\r\n display: none!important;\r\n}\r\n.reader-toolbar-item-download {\r\n display: none!important;\r\n}\r\n.download-btn {\r\n display: none!important;\r\n}\r\n.download-icon {\r\n display: none!important;\r\n}\r\n\r\n.fqa-hide {\r\n display: none!important;\r\n}\r\n/* 404 */\r\n.no-content {\r\n display: none!important;\r\n}\r\n\r\n.fqa-comic-img {\r\n width: 100%!important;\r\n height: 100%!important;\r\n max-width: 100%!important;\r\n max-height: 100%!important;\r\n padding-top: 0!important;\r\n padding-bottom: 0!important;\r\n margin-top: 0!important;\r\n margin-bottom: 0!important;\r\n}\r\n\r\n.fqa-comic-reader {\r\n line-height: 0!important;\r\n}\r\n\r\n.fqa-menu-item,\r\n.arco-menu-item {\r\n width: 100%!important;\r\n}"; - async function sleep(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)); - } - function cloneElement(element) { - return element.cloneNode(true); - } - async function inject() { - while (!document.body) { - console.log("Waiting for body..."); - await sleep(500); + const _config = { + currentConfig: defaultConfig + }; + const scriptcss = "/* 移除章节锁定图标 */\r\n.muyeicon-lock {\r\n display: none;\r\n}\r\n/* 移除APP推广图标 */\r\n.muye-to-fanqie {\r\n display: none!important;\r\n}\r\n.reader-toolbar-item-download {\r\n display: none!important;\r\n}\r\n.download-btn {\r\n display: none!important;\r\n}\r\n.download-icon {\r\n display: none!important;\r\n}\r\n\r\n.fqa-hide {\r\n display: none!important;\r\n}\r\n/* 404 */\r\n.no-content {\r\n display: none!important;\r\n}\r\n\r\n.fqa-comic-img {\r\n width: 100%!important;\r\n height: 100%!important;\r\n max-width: 100%!important;\r\n max-height: 100%!important;\r\n padding-top: 0!important;\r\n padding-bottom: 0!important;\r\n margin-top: 0!important;\r\n margin-bottom: 0!important;\r\n}\r\n\r\n.fqa-comic-reader {\r\n line-height: 0!important;\r\n}\r\n\r\n.fqa-menu-item,\r\n.arco-menu-item {\r\n width: 100%!important;\r\n}\r\n\r\n#dynamic-el {\r\n display: none!important;\r\n}"; + async function sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); + } + function cloneElement(element) { + return element.cloneNode(true); + } + function waitForElement(selector, timeout = 15e3) { + const existing = document.querySelector(selector); + if (existing) return Promise.resolve(existing); + return new Promise((resolve) => { + let timer; + const observer2 = new MutationObserver(() => { + const el = document.querySelector(selector); + if (el) { + if (timer) clearTimeout(timer); + observer2.disconnect(); + resolve(el); } - GM_addStyle(scriptcss); - console.log("CSS injected successfully!"); + }); + const start = () => { + observer2.observe(document.documentElement, { childList: true, subtree: true }); + timer = setTimeout(() => { + observer2.disconnect(); + resolve(document.querySelector(selector)); + }, timeout); + }; + if (document.documentElement) { + start(); + } else { + document.addEventListener("DOMContentLoaded", start, { once: true }); } - const enTag = ".font-DNMrHsV173Pd4pgy"; - const code_ed = 58715; - const code_st = 58344; - const mapping = [ - "D", - "在", - "主", - "特", - "家", - "军", - "然", - "表", - "场", - "4", - "要", - "只", - "v", - "和", - "?", - "6", - "别", - "还", - "g", - "现", - "儿", - "岁", - "?", - "?", - "此", - "象", - "月", - "3", - "出", - "战", - "工", - "相", - "o", - "男", - "直", - "失", - "世", - "F", - "都", - "平", - "文", - "什", - "V", - "O", - "将", - "真", - "T", - "那", - "当", - "?", - "会", - "立", - "些", - "u", - "是", - "十", - "张", - "学", - "气", - "大", - "爱", - "两", - "命", - "全", - "后", - "东", - "性", - "通", - "被", - "1", - "它", - "乐", - "接", - "而", - "感", - "车", - "山", - "公", - "了", - "常", - "以", - "何", - "可", - "话", - "先", - "p", - "i", - "叫", - "轻", - "M", - "士", - "w", - "着", - "变", - "尔", - "快", - "l", - "个", - "说", - "少", - "色", - "里", - "安", - "花", - "远", - "7", - "难", - "师", - "放", - "t", - "报", - "认", - "面", - "道", - "S", - "?", - "克", - "地", - "度", - "I", - "好", - "机", - "U", - "民", - "写", - "把", - "万", - "同", - "水", - "新", - "没", - "书", - "电", - "吃", - "像", - "斯", - "5", - "为", - "y", - "白", - "几", - "日", - "教", - "看", - "但", - "第", - "加", - "候", - "作", - "上", - "拉", - "住", - "有", - "法", - "r", - "事", - "应", - "位", - "利", - "你", - "声", - "身", - "国", - "问", - "马", - "女", - "他", - "Y", - "比", - "父", - "x", - "A", - "H", - "N", - "s", - "X", - "边", - "美", - "对", - "所", - "金", - "活", - "回", - "意", - "到", - "z", - "从", - "j", - "知", - "又", - "内", - "因", - "点", - "Q", - "三", - "定", - "8", - "R", - "b", - "正", - "或", - "夫", - "向", - "德", - "听", - "更", - "?", - "得", - "告", - "并", - "本", - "q", - "过", - "记", - "L", - "让", - "打", - "f", - "人", - "就", - "者", - "去", - "原", - "满", - "体", - "做", - "经", - "K", - "走", - "如", - "孩", - "c", - "G", - "给", - "使", - "物", - "?", - "最", - "笑", - "部", - "?", - "员", - "等", - "受", - "k", - "行", - "一", - "条", - "果", - "动", - "光", - "门", - "头", - "见", - "往", - "自", - "解", - "成", - "处", - "天", - "能", - "于", - "名", - "其", - "发", - "总", - "母", - "的", - "死", - "手", - "入", - "路", - "进", - "心", - "来", - "h", - "时", - "力", - "多", - "开", - "已", - "许", - "d", - "至", - "由", - "很", - "界", - "n", - "小", - "与", - "Z", - "想", - "代", - "么", - "分", - "生", - "口", - "再", - "妈", - "望", - "次", - "西", - "风", - "种", - "带", - "J", - "?", - "实", - "情", - "才", - "这", - "?", - "E", - "我", - "神", - "格", - "长", - "觉", - "间", - "年", - "眼", - "无", - "不", - "亲", - "关", - "结", - "0", - "友", - "信", - "下", - "却", - "重", - "己", - "老", - "2", - "音", - "字", - "m", - "呢", - "明", - "之", - "前", - "高", - "P", - "B", - "目", - "太", - "e", - "9", - "起", - "稜", - "她", - "也", - "W", - "用", - "方", - "子", - "英", - "每", - "理", - "便", - "四", - "数", - "期", - "中", - "C", - "外", - "样", - "a", - "海", - "们", - "任" - ]; - function decryptText(text) { - let result = ""; - let changed = false; - for (const char of text) { - const codePoint = char.codePointAt(0); - if (typeof codePoint !== "number") { - return text; + }); + } + function chunk(list, size) { + const result = []; + for (let i = 0; i < list.length; i += size) { + result.push(list.slice(i, i + size)); + } + return result; + } + async function inject() { + while (!document.body) { + console.log("Waiting for body..."); + await sleep(200); + } + GM_addStyle(scriptcss); + console.log("CSS injected successfully!"); + } + const enTag = ".font-DNMrHsV173Pd4pgy"; + const code_ed = 58715; + const code_st = 58344; + const mapping = [ + "D", + "在", + "主", + "特", + "家", + "军", + "然", + "表", + "场", + "4", + "要", + "只", + "v", + "和", + "?", + "6", + "别", + "还", + "g", + "现", + "儿", + "岁", + "?", + "?", + "此", + "象", + "月", + "3", + "出", + "战", + "工", + "相", + "o", + "男", + "直", + "失", + "世", + "F", + "都", + "平", + "文", + "什", + "V", + "O", + "将", + "真", + "T", + "那", + "当", + "?", + "会", + "立", + "些", + "u", + "是", + "十", + "张", + "学", + "气", + "大", + "爱", + "两", + "命", + "全", + "后", + "东", + "性", + "通", + "被", + "1", + "它", + "乐", + "接", + "而", + "感", + "车", + "山", + "公", + "了", + "常", + "以", + "何", + "可", + "话", + "先", + "p", + "i", + "叫", + "轻", + "M", + "士", + "w", + "着", + "变", + "尔", + "快", + "l", + "个", + "说", + "少", + "色", + "里", + "安", + "花", + "远", + "7", + "难", + "师", + "放", + "t", + "报", + "认", + "面", + "道", + "S", + "?", + "克", + "地", + "度", + "I", + "好", + "机", + "U", + "民", + "写", + "把", + "万", + "同", + "水", + "新", + "没", + "书", + "电", + "吃", + "像", + "斯", + "5", + "为", + "y", + "白", + "几", + "日", + "教", + "看", + "但", + "第", + "加", + "候", + "作", + "上", + "拉", + "住", + "有", + "法", + "r", + "事", + "应", + "位", + "利", + "你", + "声", + "身", + "国", + "问", + "马", + "女", + "他", + "Y", + "比", + "父", + "x", + "A", + "H", + "N", + "s", + "X", + "边", + "美", + "对", + "所", + "金", + "活", + "回", + "意", + "到", + "z", + "从", + "j", + "知", + "又", + "内", + "因", + "点", + "Q", + "三", + "定", + "8", + "R", + "b", + "正", + "或", + "夫", + "向", + "德", + "听", + "更", + "?", + "得", + "告", + "并", + "本", + "q", + "过", + "记", + "L", + "让", + "打", + "f", + "人", + "就", + "者", + "去", + "原", + "满", + "体", + "做", + "经", + "K", + "走", + "如", + "孩", + "c", + "G", + "给", + "使", + "物", + "?", + "最", + "笑", + "部", + "?", + "员", + "等", + "受", + "k", + "行", + "一", + "条", + "果", + "动", + "光", + "门", + "头", + "见", + "往", + "自", + "解", + "成", + "处", + "天", + "能", + "于", + "名", + "其", + "发", + "总", + "母", + "的", + "死", + "手", + "入", + "路", + "进", + "心", + "来", + "h", + "时", + "力", + "多", + "开", + "已", + "许", + "d", + "至", + "由", + "很", + "界", + "n", + "小", + "与", + "Z", + "想", + "代", + "么", + "分", + "生", + "口", + "再", + "妈", + "望", + "次", + "西", + "风", + "种", + "带", + "J", + "?", + "实", + "情", + "才", + "这", + "?", + "E", + "我", + "神", + "格", + "长", + "觉", + "间", + "年", + "眼", + "无", + "不", + "亲", + "关", + "结", + "0", + "友", + "信", + "下", + "却", + "重", + "己", + "老", + "2", + "音", + "字", + "m", + "呢", + "明", + "之", + "前", + "高", + "P", + "B", + "目", + "太", + "e", + "9", + "起", + "稜", + "她", + "也", + "W", + "用", + "方", + "子", + "英", + "每", + "理", + "便", + "四", + "数", + "期", + "中", + "C", + "外", + "样", + "a", + "海", + "们", + "任" + ]; + function decryptText(text) { + let result = ""; + let changed = false; + for (const char of text) { + const codePoint = char.codePointAt(0); + if (typeof codePoint !== "number") { + return text; + } + if (codePoint < code_st || codePoint > code_ed) { + result += char; + continue; + } + const mapped = mapping[codePoint - code_st]; + if (mapped && mapped !== "?") { + result += mapped; + changed = true; + } else { + result += char; + } + } + return changed ? result : text; + } + function decryptElement(element) { + const walker = document.createTreeWalker( + element, + NodeFilter.SHOW_TEXT + ); + let textNode; + while (textNode = walker.nextNode()) { + const oldText = textNode.nodeValue; + if (!oldText) { + continue; + } + const newText = decryptText(oldText); + if (newText !== oldText) { + textNode.nodeValue = newText; + } + } + } + function decryptPage(root) { + if (root instanceof Element && root.matches(enTag)) { + decryptElement(root); + } + root.querySelectorAll(enTag).forEach(decryptElement); + } + function initFontDecrypt() { + const observer2 = new MutationObserver((mutations) => { + var _a, _b; + for (const mutation of mutations) { + if (mutation.type === "characterData") { + const encryptedElement = (_a = mutation.target.parentElement) == null ? void 0 : _a.closest( + enTag + ); + if (encryptedElement) { + decryptElement(encryptedElement); } - if (codePoint < code_st || codePoint > code_ed) { - result += char; + continue; + } + for (const node of mutation.addedNodes) { + if (node.nodeType === Node.ELEMENT_NODE) { + decryptPage(node); continue; } - const mapped = mapping[codePoint - code_st]; - if (mapped && mapped !== "?") { - result += mapped; - changed = true; - } else { - result += char; - } - } - return changed ? result : text; - } - function decryptElement(element) { - const walker = document.createTreeWalker( - element, - NodeFilter.SHOW_TEXT - ); - let textNode; - while (textNode = walker.nextNode()) { - const oldText = textNode.nodeValue; - if (!oldText) { - continue; - } - const newText = decryptText(oldText); - if (newText !== oldText) { - textNode.nodeValue = newText; - } - } - } - function decryptPage(root) { - if (root instanceof Element && root.matches(enTag)) { - decryptElement(root); - } - root.querySelectorAll(enTag).forEach(decryptElement); - } - function initFontDecrypt() { - const observer = new MutationObserver((mutations) => { - var _a, _b; - for (const mutation of mutations) { - if (mutation.type === "characterData") { - const encryptedElement = (_a = mutation.target.parentElement) == null ? void 0 : _a.closest( - enTag - ); - if (encryptedElement) { - decryptElement(encryptedElement); - } - continue; - } - for (const node of mutation.addedNodes) { - if (node.nodeType === Node.ELEMENT_NODE) { - decryptPage(node); - continue; - } - if (node.nodeType === Node.TEXT_NODE) { - const encryptedElement = (_b = node.parentElement) == null ? void 0 : _b.closest( - enTag - ); - if (encryptedElement) { - decryptElement(encryptedElement); - } - } + if (node.nodeType === Node.TEXT_NODE) { + const encryptedElement = (_b = node.parentElement) == null ? void 0 : _b.closest( + enTag + ); + if (encryptedElement) { + decryptElement(encryptedElement); } } - }); - observer.observe(document, { - subtree: true, - childList: true, - characterData: true - }); - decryptPage(document); + } } - const supportedMethods = /* @__PURE__ */ new Set([ - "GET", - "HEAD", - "POST", - "PUT", - "DELETE" - ]); - function apiFetch(url, options = {}) { - return new Promise((resolve, reject) => { - const { signal } = options; - if (signal == null ? void 0 : signal.aborted) { - reject(signal.reason ?? new DOMException("The operation was aborted", "AbortError")); - return; - } - const headers = normalizeHeaders(options.headers); - const data = normalizeBody(options.body); - const method = options.method ?? (data ? "POST" : "GET"); - if (!supportedMethods.has(method)) { - reject(new TypeError(`Unsupported request method: ${method}`)); - return; - } - let request; - const abort = () => request == null ? void 0 : request.abort(); - function cleanup() { - signal == null ? void 0 : signal.removeEventListener("abort", abort); - } - request = GM_xmlhttpRequest({ - url, - method, - headers, - data, - anonymous: options.credentials === "omit", - redirect: options.redirect === "error" ? "error" : "follow", - onload(response) { - cleanup(); - resolve(Object.assign(response, { - json() { - return JSON.parse(this.responseText); - } - })); - }, - onerror(response) { - cleanup(); - reject(createRequestError("Network request failed", response)); - }, - ontimeout() { - cleanup(); - reject(createRequestError("Network request timed out", { - status: 0, - statusText: "Timeout", - url - })); - }, - onabort() { - cleanup(); - reject( - (signal == null ? void 0 : signal.reason) ?? new DOMException("The operation was aborted", "AbortError") - ); + }); + observer2.observe(document, { + subtree: true, + childList: true, + characterData: true + }); + decryptPage(document); + } + const supportedMethods = /* @__PURE__ */ new Set([ + "GET", + "HEAD", + "POST", + "PUT", + "DELETE" + ]); + function apiFetch(url, options = {}) { + return new Promise((resolve, reject) => { + const { signal } = options; + if (signal == null ? void 0 : signal.aborted) { + reject(signal.reason ?? new DOMException("The operation was aborted", "AbortError")); + return; + } + const headers = normalizeHeaders(options.headers); + const data = normalizeBody(options.body); + const method = options.method ?? (data ? "POST" : "GET"); + if (!supportedMethods.has(method)) { + reject(new TypeError(`Unsupported request method: ${method}`)); + return; + } + let request; + const abort = () => request == null ? void 0 : request.abort(); + function cleanup() { + signal == null ? void 0 : signal.removeEventListener("abort", abort); + } + request = GM_xmlhttpRequest({ + url, + method, + headers, + data, + anonymous: options.credentials === "omit", + redirect: options.redirect === "error" ? "error" : "follow", + onload(response) { + cleanup(); + resolve(Object.assign(response, { + json() { + return JSON.parse(this.responseText); } - }); - signal == null ? void 0 : signal.addEventListener("abort", abort, { once: true }); - }); - } - function normalizeHeaders(headers) { - if (!headers) { - return void 0; - } - return Object.fromEntries(new Headers(headers).entries()); - } - function normalizeBody(body) { - if (body == null) { - return void 0; - } - if (body instanceof URLSearchParams) { - return body.toString(); - } - if (typeof body === "string" || body instanceof Blob || body instanceof ArrayBuffer || body instanceof FormData) { - return body; - } - if (ArrayBuffer.isView(body)) { - return body.buffer.slice( - body.byteOffset, - body.byteOffset + body.byteLength + })); + }, + onerror(response) { + cleanup(); + reject(createRequestError("Network request failed", response)); + }, + ontimeout() { + cleanup(); + reject(createRequestError("Network request timed out", { + status: 0, + statusText: "Timeout", + url + })); + }, + onabort() { + cleanup(); + reject( + (signal == null ? void 0 : signal.reason) ?? new DOMException("The operation was aborted", "AbortError") ); } - throw new TypeError( - "GM_xmlhttpRequest does not support ReadableStream request bodies" - ); - } - function createRequestError(message, response) { - const error = new TypeError(message); - Object.defineProperty(error, "response", { - configurable: true, - enumerable: false, - value: response - }); - return error; - } - unsafeWindow.localStorage; - function write(key, value) { - GM_setValue(key, JSON.stringify(value)); - } - function read(key) { - return JSON.parse(GM_getValue(key) || "null"); - } - function del(key) { - GM_deleteValue(key); - } - function getCookie(key) { - var _a; - return ((_a = document.cookie.split(";").find((c) => c.trim().startsWith(key + "="))) == null ? void 0 : _a.split("=")[1]) || null; - } - const baseUrl = "https://api5-sinfonlinec.jxbhmy.com/redcandle/search"; - const baseQuery = { - aid: "1967", - app_name: "novelapp" - }; - async function get$3(path, cookie, query, headers, optionsOverride) { - let url = baseUrl + path; - getCookie("sessionid"); - const h = { - Cookie: "", - ...headers - }; - const deviceQuery = { - device_id: _config.currentConfig.device_id, - iid: _config.currentConfig.install_id - }; - const finalQuery = { - ...baseQuery, - ...deviceQuery, - ...query - }; - url += "?" + new URLSearchParams(finalQuery).toString(); - const options = { - method: "GET", - headers: h, - ...optionsOverride - }; - console.log("---start--- GET ", url, options); - const res = await apiFetch(url, options); - console.log("---complete--- GET ", url, res); - return res; - } - async function post(path, cookie, query, body, headers, optionsOverride) { - let url = baseUrl + path; - getCookie("sessionid"); - const h = { - Cookie: "", - ...headers - }; - const deviceQuery = { - device_id: _config.currentConfig.device_id, - iid: _config.currentConfig.install_id - }; - const finalQuery = { - ...baseQuery, - ...deviceQuery, - ...query - }; - url += "?" + new URLSearchParams(finalQuery).toString(); - const options = { - method: "POST", - headers: h, - body, - ...optionsOverride - }; - console.log("---start--- POST ", url, options); - const res = await apiFetch(url, options); - console.log("---complete--- POST ", url, res); - return res; - } - function getCrypto() { - const c = globalThis.crypto ?? unsafeWindow.crypto; - if (!(c == null ? void 0 : c.subtle)) { - throw new Error("Crypto API不可用,请检查浏览器版本是否支持该API"); - } - return c; - } - function getSubtle() { - return getCrypto().subtle; - } - function b64decode(b64) { - const binaryString = atob(b64); - const len = binaryString.length; - const bytes = new Uint8Array(len); - for (let i = 0; i < len; i++) { - bytes[i] = binaryString.charCodeAt(i); - } - return bytes.buffer; - } - function b64encode(buffer) { - const bytes = new Uint8Array(buffer); - const chunkSize = 32768; - const chunks = []; - for (let i = 0; i < bytes.length; i += chunkSize) { - chunks.push( - String.fromCharCode(...bytes.subarray(i, i + chunkSize)) - ); - } - return btoa(chunks.join("")); - } - function unhex(hex) { - if (hex.length % 2 !== 0) { - throw new Error("Invalid hex string"); - } - const bytes = new Uint8Array(hex.length / 2); - for (let i = 0; i < hex.length; i += 2) { - const byte = parseInt(hex.slice(i, i + 2), 16); - if (Number.isNaN(byte)) { - throw new Error("Invalid hex string"); - } - bytes[i / 2] = byte; - } - return bytes.buffer; - } - function randomString(length) { - const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - let result = ""; - const array = new Uint8Array(length); - getCrypto().getRandomValues(array); - for (let i = 0; i < length; i++) { - result += chars.charAt(array[i] % chars.length); - } - return result; - } - async function decryptChapter(encrypted, config = defaultConfig) { - var _a; - if (!encrypted) { - throw new Error("Invalid encrypted chapter"); - } - const buf = b64decode(encrypted); - const iv = buf.slice(0, 16); - const data = buf.slice(16); - const key = (_a = config.key_info) == null ? void 0 : _a.key; - if (!key) { - throw new Error("Missing decrypt key"); - } - const subtle = getSubtle(); - const cryptoKey = await subtle.importKey( - "raw", - key, - { name: "AES-CBC" }, - false, - ["decrypt"] - ); - return subtle.decrypt( - { name: "AES-CBC", iv }, - cryptoKey, - data - ).then((decrypted) => { - const decoder = new TextDecoder(); - const plain = decoder.decode(decrypted); - if (plain.trim().startsWith("<")) { - return plain; - } - try { - return JSON.parse(plain); - } catch (e) { - console.warn("Invalid chapter content: ", plain, e); - return void 0; - } - }); - } - function reverseHex(value) { - const be = BigInt(value).toString(16).padStart(32, "0"); - let result = ""; - for (let i = be.length; i > 0; i -= 2) result += be.slice(i - 2, i); - return result; - } - async function encryptKeyinfoBody(config) { - const deviceId = config.device_id; - const iv = new TextEncoder().encode(randomString(16)); - const data = new Uint8Array(unhex(reverseHex(deviceId))).slice(0, 8); - console.log(data); - const subtle = getSubtle(); - const k = await subtle.importKey( - "raw", - shared_key, - { name: "AES-CBC" }, - false, - ["encrypt"] - ); - const encrypted = await subtle.encrypt( - { name: "AES-CBC", iv }, - k, - data - ); - const final = new Uint8Array(iv.length + encrypted.byteLength); - console.log(final); - final.set(iv, 0); - final.set(new Uint8Array(encrypted), iv.length); - return JSON.stringify({ - content: b64encode(final.buffer) - }); - } - async function decryptKeyinfoResponse(encrypted) { - const buf = b64decode(encrypted); - const iv = buf.slice(0, 16); - const data = buf.slice(16); - const subtle = getSubtle(); - const k = await subtle.importKey( - "raw", - shared_key, - { name: "AES-CBC" }, - false, - ["decrypt"] - ); - return subtle.decrypt( - { name: "AES-CBC", iv }, - k, - data - ); - } - async function decryptComicImage(image, key) { - const subtle = getSubtle(); - const cryptoKey = await subtle.importKey( - "raw", - unhex(key), - { name: "AES-GCM" }, - false, - ["decrypt"] - ); - const iv = image.slice(0, 12); - const data = image.slice(12); - return await subtle.decrypt( - { name: "AES-GCM", iv }, - cryptoKey, - data - ); - } - async function refreshKeyinfo() { - var _a, _b, _c; - const b = await encryptKeyinfoBody(_config.currentConfig); - const res = await post("/reader/crypt/registerkey/v1", false, {}, b); - console.log(res); - const j = res.json(); - console.log("response: ", j); - const ek = (_a = j == null ? void 0 : j.data) == null ? void 0 : _a.key; - if (!ek) { - throw new Error("Failed to get key info"); - } - const key = await decryptKeyinfoResponse(ek); - const keyinfo = { - key, - keyver: (_b = j == null ? void 0 : j.data) == null ? void 0 : _b.keyver - }; - console.log("Refreshed key info:", keyinfo); - _config.currentConfig.key_info = keyinfo; - write("keyinfo", { - key: b64encode(key), - keyver: (_c = j == null ? void 0 : j.data) == null ? void 0 : _c.keyver - }); - } - async function ensureKeyinfo(expectedKeyVersion) { - const keyinfo = _config.currentConfig.key_info; - const cachedKeyInfo = read("keyinfo"); - console.log("cached key info: ", cachedKeyInfo); - if (cachedKeyInfo) { - const cki = { - key: b64decode(cachedKeyInfo.key), - keyver: cachedKeyInfo.keyver - }; - if (typeof expectedKeyVersion === "undefined" || cki.keyver === expectedKeyVersion) { - _config.currentConfig.key_info = cki; - return; - } - } - if (!keyinfo) { - return await refreshKeyinfo(); - } - if ((keyinfo == null ? void 0 : keyinfo.keyver) !== expectedKeyVersion) { - return await refreshKeyinfo(); - } - } - async function getChapter(itemId, _retry) { - var _a, _b; - if (typeof _retry === "undefined") _retry = 0; - if (_retry > 5) { - throw new Error(`Failed to get chapter: ${itemId}`); - } - if (!_config.currentConfig.key_info) { - await ensureKeyinfo(); - } - const res = await get$3(`/reader/full/v1`, false, { item_id: itemId }); - const j = (_a = res.json()) == null ? void 0 : _a.data; - if (!j) { - console.warn("Failed to get chapter: ", itemId, ", response: ", j); - return await getChapter(itemId, _retry + 1); - } - if ((j == null ? void 0 : j.content) === "Invalid" || (j == null ? void 0 : j.key_version) !== ((_b = _config.currentConfig.key_info) == null ? void 0 : _b.keyver)) { - console.warn("Key reg expired, regster again and retrying..."); - await ensureKeyinfo(parseInt(j == null ? void 0 : j.key_version)); - return await getChapter(itemId, _retry + 1); - } - j.content = await decryptChapter(j == null ? void 0 : j.content, _config.currentConfig); - return j; - } - //! moment.js - //! version : 2.30.1 - //! authors : Tim Wood, Iskren Chernev, Moment.js contributors - //! license : MIT - //! momentjs.com - var hookCallback; - function hooks$1() { - return hookCallback.apply(null, arguments); - } - function setHookCallback(callback) { - hookCallback = callback; - } - function isArray(input) { - return input instanceof Array || Object.prototype.toString.call(input) === "[object Array]"; - } - function isObject(input) { - return input != null && Object.prototype.toString.call(input) === "[object Object]"; - } - function hasOwnProp(a, b) { - return Object.prototype.hasOwnProperty.call(a, b); - } - function isObjectEmpty(obj) { - if (Object.getOwnPropertyNames) { - return Object.getOwnPropertyNames(obj).length === 0; - } else { - var k; - for (k in obj) { - if (hasOwnProp(obj, k)) { - return false; - } - } - return true; - } - } - function isUndefined(input) { - return input === void 0; - } - function isNumber(input) { - return typeof input === "number" || Object.prototype.toString.call(input) === "[object Number]"; - } - function isDate(input) { - return input instanceof Date || Object.prototype.toString.call(input) === "[object Date]"; - } - function map(arr, fn) { - var res = [], i, arrLen = arr.length; - for (i = 0; i < arrLen; ++i) { - res.push(fn(arr[i], i)); - } - return res; - } - function extend(a, b) { - for (var i in b) { - if (hasOwnProp(b, i)) { - a[i] = b[i]; - } - } - if (hasOwnProp(b, "toString")) { - a.toString = b.toString; - } - if (hasOwnProp(b, "valueOf")) { - a.valueOf = b.valueOf; - } - return a; - } - function createUTC(input, format2, locale2, strict) { - return createLocalOrUTC(input, format2, locale2, strict, true).utc(); - } - function defaultParsingFlags() { - return { - empty: false, - unusedTokens: [], - unusedInput: [], - overflow: -2, - charsLeftOver: 0, - nullInput: false, - invalidEra: null, - invalidMonth: null, - invalidFormat: false, - userInvalidated: false, - iso: false, - parsedDateParts: [], - era: null, - meridiem: null, - rfc2822: false, - weekdayMismatch: false - }; - } - function getParsingFlags(m) { - if (m._pf == null) { - m._pf = defaultParsingFlags(); - } - return m._pf; - } - var some; - if (Array.prototype.some) { - some = Array.prototype.some; - } else { - some = function(fun) { - var t = Object(this), len = t.length >>> 0, i; - for (i = 0; i < len; i++) { - if (i in t && fun.call(this, t[i], i, t)) { - return true; - } - } - return false; - }; - } - function isValid(m) { - var flags = null, parsedParts = false, isNowValid = m._d && !isNaN(m._d.getTime()); - if (isNowValid) { - flags = getParsingFlags(m); - parsedParts = some.call(flags.parsedDateParts, function(i) { - return i != null; - }); - isNowValid = flags.overflow < 0 && !flags.empty && !flags.invalidEra && !flags.invalidMonth && !flags.invalidWeekday && !flags.weekdayMismatch && !flags.nullInput && !flags.invalidFormat && !flags.userInvalidated && (!flags.meridiem || flags.meridiem && parsedParts); - if (m._strict) { - isNowValid = isNowValid && flags.charsLeftOver === 0 && flags.unusedTokens.length === 0 && flags.bigHour === void 0; - } - } - if (Object.isFrozen == null || !Object.isFrozen(m)) { - m._isValid = isNowValid; - } else { - return isNowValid; - } - return m._isValid; - } - function createInvalid(flags) { - var m = createUTC(NaN); - if (flags != null) { - extend(getParsingFlags(m), flags); - } else { - getParsingFlags(m).userInvalidated = true; - } - return m; - } - var momentProperties = hooks$1.momentProperties = [], updateInProgress = false; - function copyConfig(to2, from2) { - var i, prop, val, momentPropertiesLen = momentProperties.length; - if (!isUndefined(from2._isAMomentObject)) { - to2._isAMomentObject = from2._isAMomentObject; - } - if (!isUndefined(from2._i)) { - to2._i = from2._i; - } - if (!isUndefined(from2._f)) { - to2._f = from2._f; - } - if (!isUndefined(from2._l)) { - to2._l = from2._l; - } - if (!isUndefined(from2._strict)) { - to2._strict = from2._strict; - } - if (!isUndefined(from2._tzm)) { - to2._tzm = from2._tzm; - } - if (!isUndefined(from2._isUTC)) { - to2._isUTC = from2._isUTC; - } - if (!isUndefined(from2._offset)) { - to2._offset = from2._offset; - } - if (!isUndefined(from2._pf)) { - to2._pf = getParsingFlags(from2); - } - if (!isUndefined(from2._locale)) { - to2._locale = from2._locale; - } - if (momentPropertiesLen > 0) { - for (i = 0; i < momentPropertiesLen; i++) { - prop = momentProperties[i]; - val = from2[prop]; - if (!isUndefined(val)) { - to2[prop] = val; - } - } - } - return to2; - } - function Moment(config) { - copyConfig(this, config); - this._d = new Date(config._d != null ? config._d.getTime() : NaN); - if (!this.isValid()) { - this._d = /* @__PURE__ */ new Date(NaN); - } - if (updateInProgress === false) { - updateInProgress = true; - hooks$1.updateOffset(this); - updateInProgress = false; - } - } - function isMoment(obj) { - return obj instanceof Moment || obj != null && obj._isAMomentObject != null; - } - function warn(msg) { - if (hooks$1.suppressDeprecationWarnings === false && typeof console !== "undefined" && console.warn) { - console.warn("Deprecation warning: " + msg); - } - } - function deprecate(msg, fn) { - var firstTime = true; - return extend(function() { - if (hooks$1.deprecationHandler != null) { - hooks$1.deprecationHandler(null, msg); - } - if (firstTime) { - var args = [], arg, i, key, argLen = arguments.length; - for (i = 0; i < argLen; i++) { - arg = ""; - if (typeof arguments[i] === "object") { - arg += "\n[" + i + "] "; - for (key in arguments[0]) { - if (hasOwnProp(arguments[0], key)) { - arg += key + ": " + arguments[0][key] + ", "; - } - } - arg = arg.slice(0, -2); - } else { - arg = arguments[i]; - } - args.push(arg); - } - warn( - msg + "\nArguments: " + Array.prototype.slice.call(args).join("") + "\n" + new Error().stack - ); - firstTime = false; - } - return fn.apply(this, arguments); - }, fn); - } - var deprecations = {}; - function deprecateSimple(name2, msg) { - if (hooks$1.deprecationHandler != null) { - hooks$1.deprecationHandler(name2, msg); - } - if (!deprecations[name2]) { - warn(msg); - deprecations[name2] = true; - } - } - hooks$1.suppressDeprecationWarnings = false; - hooks$1.deprecationHandler = null; - function isFunction(input) { - return typeof Function !== "undefined" && input instanceof Function || Object.prototype.toString.call(input) === "[object Function]"; - } - function set(config) { - var prop, i; - for (i in config) { - if (hasOwnProp(config, i)) { - prop = config[i]; - if (isFunction(prop)) { - this[i] = prop; - } else { - this["_" + i] = prop; - } - } - } - this._config = config; - this._dayOfMonthOrdinalParseLenient = new RegExp( - (this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) + "|" + /\d{1,2}/.source - ); - } - function mergeConfigs(parentConfig, childConfig) { - var res = extend({}, parentConfig), prop; - for (prop in childConfig) { - if (hasOwnProp(childConfig, prop)) { - if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) { - res[prop] = {}; - extend(res[prop], parentConfig[prop]); - extend(res[prop], childConfig[prop]); - } else if (childConfig[prop] != null) { - res[prop] = childConfig[prop]; - } else { - delete res[prop]; - } - } - } - for (prop in parentConfig) { - if (hasOwnProp(parentConfig, prop) && !hasOwnProp(childConfig, prop) && isObject(parentConfig[prop])) { - res[prop] = extend({}, res[prop]); - } - } - return res; - } - function Locale(config) { - if (config != null) { - this.set(config); - } - } - var keys; - if (Object.keys) { - keys = Object.keys; - } else { - keys = function(obj) { - var i, res = []; - for (i in obj) { - if (hasOwnProp(obj, i)) { - res.push(i); - } - } - return res; - }; - } - var defaultCalendar = { - sameDay: "[Today at] LT", - nextDay: "[Tomorrow at] LT", - nextWeek: "dddd [at] LT", - lastDay: "[Yesterday at] LT", - lastWeek: "[Last] dddd [at] LT", - sameElse: "L" - }; - function calendar(key, mom, now2) { - var output = this._calendar[key] || this._calendar["sameElse"]; - return isFunction(output) ? output.call(mom, now2) : output; - } - function zeroFill(number, targetLength, forceSign) { - var absNumber = "" + Math.abs(number), zerosToFill = targetLength - absNumber.length, sign2 = number >= 0; - return (sign2 ? forceSign ? "+" : "" : "-") + Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber; - } - var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|N{1,5}|YYYYYY|YYYYY|YYYY|YY|y{2,4}|yo?|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g, localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, formatFunctions = {}, formatTokenFunctions = {}; - function addFormatToken(token2, padded, ordinal2, callback) { - var func = callback; - if (typeof callback === "string") { - func = function() { - return this[callback](); - }; - } - if (token2) { - formatTokenFunctions[token2] = func; - } - if (padded) { - formatTokenFunctions[padded[0]] = function() { - return zeroFill(func.apply(this, arguments), padded[1], padded[2]); - }; - } - if (ordinal2) { - formatTokenFunctions[ordinal2] = function() { - return this.localeData().ordinal( - func.apply(this, arguments), - token2 - ); - }; - } - } - function removeFormattingTokens(input) { - if (input.match(/\[[\s\S]/)) { - return input.replace(/^\[|\]$/g, ""); - } - return input.replace(/\\/g, ""); - } - function makeFormatFunction(format2) { - var array = format2.match(formattingTokens), i, length; - for (i = 0, length = array.length; i < length; i++) { - if (formatTokenFunctions[array[i]]) { - array[i] = formatTokenFunctions[array[i]]; - } else { - array[i] = removeFormattingTokens(array[i]); - } - } - return function(mom) { - var output = "", i2; - for (i2 = 0; i2 < length; i2++) { - output += isFunction(array[i2]) ? array[i2].call(mom, format2) : array[i2]; - } - return output; - }; - } - function formatMoment(m, format2) { - if (!m.isValid()) { - return m.localeData().invalidDate(); - } - format2 = expandFormat(format2, m.localeData()); - formatFunctions[format2] = formatFunctions[format2] || makeFormatFunction(format2); - return formatFunctions[format2](m); - } - function expandFormat(format2, locale2) { - var i = 5; - function replaceLongDateFormatTokens(input) { - return locale2.longDateFormat(input) || input; - } - localFormattingTokens.lastIndex = 0; - while (i >= 0 && localFormattingTokens.test(format2)) { - format2 = format2.replace( - localFormattingTokens, - replaceLongDateFormatTokens - ); - localFormattingTokens.lastIndex = 0; - i -= 1; - } - return format2; - } - var defaultLongDateFormat = { - LTS: "h:mm:ss A", - LT: "h:mm A", - L: "MM/DD/YYYY", - LL: "MMMM D, YYYY", - LLL: "MMMM D, YYYY h:mm A", - LLLL: "dddd, MMMM D, YYYY h:mm A" - }; - function longDateFormat(key) { - var format2 = this._longDateFormat[key], formatUpper = this._longDateFormat[key.toUpperCase()]; - if (format2 || !formatUpper) { - return format2; - } - this._longDateFormat[key] = formatUpper.match(formattingTokens).map(function(tok) { - if (tok === "MMMM" || tok === "MM" || tok === "DD" || tok === "dddd") { - return tok.slice(1); - } - return tok; - }).join(""); - return this._longDateFormat[key]; - } - var defaultInvalidDate = "Invalid date"; - function invalidDate() { - return this._invalidDate; - } - var defaultOrdinal = "%d", defaultDayOfMonthOrdinalParse = /\d{1,2}/; - function ordinal(number) { - return this._ordinal.replace("%d", number); - } - var defaultRelativeTime = { - future: "in %s", - past: "%s ago", - s: "a few seconds", - ss: "%d seconds", - m: "a minute", - mm: "%d minutes", - h: "an hour", - hh: "%d hours", - d: "a day", - dd: "%d days", - w: "a week", - ww: "%d weeks", - M: "a month", - MM: "%d months", - y: "a year", - yy: "%d years" - }; - function relativeTime(number, withoutSuffix, string, isFuture) { - var output = this._relativeTime[string]; - return isFunction(output) ? output(number, withoutSuffix, string, isFuture) : output.replace(/%d/i, number); - } - function pastFuture(diff2, output) { - var format2 = this._relativeTime[diff2 > 0 ? "future" : "past"]; - return isFunction(format2) ? format2(output) : format2.replace(/%s/i, output); - } - var aliases = { - D: "date", - dates: "date", - date: "date", - d: "day", - days: "day", - day: "day", - e: "weekday", - weekdays: "weekday", - weekday: "weekday", - E: "isoWeekday", - isoweekdays: "isoWeekday", - isoweekday: "isoWeekday", - DDD: "dayOfYear", - dayofyears: "dayOfYear", - dayofyear: "dayOfYear", - h: "hour", - hours: "hour", - hour: "hour", - ms: "millisecond", - milliseconds: "millisecond", - millisecond: "millisecond", - m: "minute", - minutes: "minute", - minute: "minute", - M: "month", - months: "month", - month: "month", - Q: "quarter", - quarters: "quarter", - quarter: "quarter", - s: "second", - seconds: "second", - second: "second", - gg: "weekYear", - weekyears: "weekYear", - weekyear: "weekYear", - GG: "isoWeekYear", - isoweekyears: "isoWeekYear", - isoweekyear: "isoWeekYear", - w: "week", - weeks: "week", - week: "week", - W: "isoWeek", - isoweeks: "isoWeek", - isoweek: "isoWeek", - y: "year", - years: "year", - year: "year" - }; - function normalizeUnits(units) { - return typeof units === "string" ? aliases[units] || aliases[units.toLowerCase()] : void 0; - } - function normalizeObjectUnits(inputObject) { - var normalizedInput = {}, normalizedProp, prop; - for (prop in inputObject) { - if (hasOwnProp(inputObject, prop)) { - normalizedProp = normalizeUnits(prop); - if (normalizedProp) { - normalizedInput[normalizedProp] = inputObject[prop]; - } - } - } - return normalizedInput; - } - var priorities = { - date: 9, - day: 11, - weekday: 11, - isoWeekday: 11, - dayOfYear: 4, - hour: 13, - millisecond: 16, - minute: 14, - month: 8, - quarter: 7, - second: 15, - weekYear: 1, - isoWeekYear: 1, - week: 5, - isoWeek: 5, - year: 1 - }; - function getPrioritizedUnits(unitsObj) { - var units = [], u; - for (u in unitsObj) { - if (hasOwnProp(unitsObj, u)) { - units.push({ unit: u, priority: priorities[u] }); - } - } - units.sort(function(a, b) { - return a.priority - b.priority; - }); - return units; - } - var match1 = /\d/, match2 = /\d\d/, match3 = /\d{3}/, match4 = /\d{4}/, match6 = /[+-]?\d{6}/, match1to2 = /\d\d?/, match3to4 = /\d\d\d\d?/, match5to6 = /\d\d\d\d\d\d?/, match1to3 = /\d{1,3}/, match1to4 = /\d{1,4}/, match1to6 = /[+-]?\d{1,6}/, matchUnsigned = /\d+/, matchSigned = /[+-]?\d+/, matchOffset = /Z|[+-]\d\d:?\d\d/gi, matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi, matchTimestamp = /[+-]?\d+(\.\d{1,3})?/, matchWord = /[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i, match1to2NoLeadingZero = /^[1-9]\d?/, match1to2HasZero = /^([1-9]\d|\d)/, regexes; - regexes = {}; - function addRegexToken(token2, regex, strictRegex) { - regexes[token2] = isFunction(regex) ? regex : function(isStrict, localeData2) { - return isStrict && strictRegex ? strictRegex : regex; - }; - } - function getParseRegexForToken(token2, config) { - if (!hasOwnProp(regexes, token2)) { - return new RegExp(unescapeFormat(token2)); - } - return regexes[token2](config._strict, config._locale); - } - function unescapeFormat(s) { - return regexEscape( - s.replace("\\", "").replace( - /\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, - function(matched, p1, p2, p3, p4) { - return p1 || p2 || p3 || p4; - } - ) - ); - } - function regexEscape(s) { - return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&"); - } - function absFloor(number) { - if (number < 0) { - return Math.ceil(number) || 0; - } else { - return Math.floor(number); - } - } - function toInt(argumentForCoercion) { - var coercedNumber = +argumentForCoercion, value = 0; - if (coercedNumber !== 0 && isFinite(coercedNumber)) { - value = absFloor(coercedNumber); - } - return value; - } - var tokens = {}; - function addParseToken(token2, callback) { - var i, func = callback, tokenLen; - if (typeof token2 === "string") { - token2 = [token2]; - } - if (isNumber(callback)) { - func = function(input, array) { - array[callback] = toInt(input); - }; - } - tokenLen = token2.length; - for (i = 0; i < tokenLen; i++) { - tokens[token2[i]] = func; - } - } - function addWeekParseToken(token2, callback) { - addParseToken(token2, function(input, array, config, token3) { - config._w = config._w || {}; - callback(input, config._w, config, token3); - }); - } - function addTimeToArrayFromToken(token2, input, config) { - if (input != null && hasOwnProp(tokens, token2)) { - tokens[token2](input, config._a, config, token2); - } - } - function isLeapYear(year) { - return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0; - } - var YEAR = 0, MONTH = 1, DATE = 2, HOUR = 3, MINUTE = 4, SECOND = 5, MILLISECOND = 6, WEEK = 7, WEEKDAY = 8; - addFormatToken("Y", 0, 0, function() { - var y = this.year(); - return y <= 9999 ? zeroFill(y, 4) : "+" + y; }); - addFormatToken(0, ["YY", 2], 0, function() { - return this.year() % 100; - }); - addFormatToken(0, ["YYYY", 4], 0, "year"); - addFormatToken(0, ["YYYYY", 5], 0, "year"); - addFormatToken(0, ["YYYYYY", 6, true], 0, "year"); - addRegexToken("Y", matchSigned); - addRegexToken("YY", match1to2, match2); - addRegexToken("YYYY", match1to4, match4); - addRegexToken("YYYYY", match1to6, match6); - addRegexToken("YYYYYY", match1to6, match6); - addParseToken(["YYYYY", "YYYYYY"], YEAR); - addParseToken("YYYY", function(input, array) { - array[YEAR] = input.length === 2 ? hooks$1.parseTwoDigitYear(input) : toInt(input); - }); - addParseToken("YY", function(input, array) { - array[YEAR] = hooks$1.parseTwoDigitYear(input); - }); - addParseToken("Y", function(input, array) { - array[YEAR] = parseInt(input, 10); - }); - function daysInYear(year) { - return isLeapYear(year) ? 366 : 365; - } - hooks$1.parseTwoDigitYear = function(input) { - return toInt(input) + (toInt(input) > 68 ? 1900 : 2e3); - }; - var getSetYear = makeGetSet("FullYear", true); - function getIsLeapYear() { - return isLeapYear(this.year()); - } - function makeGetSet(unit, keepTime) { - return function(value) { - if (value != null) { - set$1(this, unit, value); - hooks$1.updateOffset(this, keepTime); - return this; - } else { - return get(this, unit); - } - }; - } - function get(mom, unit) { - if (!mom.isValid()) { - return NaN; - } - var d = mom._d, isUTC = mom._isUTC; - switch (unit) { - case "Milliseconds": - return isUTC ? d.getUTCMilliseconds() : d.getMilliseconds(); - case "Seconds": - return isUTC ? d.getUTCSeconds() : d.getSeconds(); - case "Minutes": - return isUTC ? d.getUTCMinutes() : d.getMinutes(); - case "Hours": - return isUTC ? d.getUTCHours() : d.getHours(); - case "Date": - return isUTC ? d.getUTCDate() : d.getDate(); - case "Day": - return isUTC ? d.getUTCDay() : d.getDay(); - case "Month": - return isUTC ? d.getUTCMonth() : d.getMonth(); - case "FullYear": - return isUTC ? d.getUTCFullYear() : d.getFullYear(); - default: - return NaN; - } - } - function set$1(mom, unit, value) { - var d, isUTC, year, month, date; - if (!mom.isValid() || isNaN(value)) { - return; - } - d = mom._d; - isUTC = mom._isUTC; - switch (unit) { - case "Milliseconds": - return void (isUTC ? d.setUTCMilliseconds(value) : d.setMilliseconds(value)); - case "Seconds": - return void (isUTC ? d.setUTCSeconds(value) : d.setSeconds(value)); - case "Minutes": - return void (isUTC ? d.setUTCMinutes(value) : d.setMinutes(value)); - case "Hours": - return void (isUTC ? d.setUTCHours(value) : d.setHours(value)); - case "Date": - return void (isUTC ? d.setUTCDate(value) : d.setDate(value)); - // case 'Day': // Not real - // return void (isUTC ? d.setUTCDay(value) : d.setDay(value)); - // case 'Month': // Not used because we need to pass two variables - // return void (isUTC ? d.setUTCMonth(value) : d.setMonth(value)); - case "FullYear": - break; - // See below ... - default: - return; - } - year = value; - month = mom.month(); - date = mom.date(); - date = date === 29 && month === 1 && !isLeapYear(year) ? 28 : date; - void (isUTC ? d.setUTCFullYear(year, month, date) : d.setFullYear(year, month, date)); - } - function stringGet(units) { - units = normalizeUnits(units); - if (isFunction(this[units])) { - return this[units](); - } - return this; - } - function stringSet(units, value) { - if (typeof units === "object") { - units = normalizeObjectUnits(units); - var prioritized = getPrioritizedUnits(units), i, prioritizedLen = prioritized.length; - for (i = 0; i < prioritizedLen; i++) { - this[prioritized[i].unit](units[prioritized[i].unit]); - } - } else { - units = normalizeUnits(units); - if (isFunction(this[units])) { - return this[units](value); - } - } - return this; - } - function mod(n, x) { - return (n % x + x) % x; - } - var indexOf; - if (Array.prototype.indexOf) { - indexOf = Array.prototype.indexOf; - } else { - indexOf = function(o) { - var i; - for (i = 0; i < this.length; ++i) { - if (this[i] === o) { - return i; - } - } - return -1; - }; - } - function daysInMonth(year, month) { - if (isNaN(year) || isNaN(month)) { - return NaN; - } - var modMonth = mod(month, 12); - year += (month - modMonth) / 12; - return modMonth === 1 ? isLeapYear(year) ? 29 : 28 : 31 - modMonth % 7 % 2; - } - addFormatToken("M", ["MM", 2], "Mo", function() { - return this.month() + 1; - }); - addFormatToken("MMM", 0, 0, function(format2) { - return this.localeData().monthsShort(this, format2); - }); - addFormatToken("MMMM", 0, 0, function(format2) { - return this.localeData().months(this, format2); - }); - addRegexToken("M", match1to2, match1to2NoLeadingZero); - addRegexToken("MM", match1to2, match2); - addRegexToken("MMM", function(isStrict, locale2) { - return locale2.monthsShortRegex(isStrict); - }); - addRegexToken("MMMM", function(isStrict, locale2) { - return locale2.monthsRegex(isStrict); - }); - addParseToken(["M", "MM"], function(input, array) { - array[MONTH] = toInt(input) - 1; - }); - addParseToken(["MMM", "MMMM"], function(input, array, config, token2) { - var month = config._locale.monthsParse(input, token2, config._strict); - if (month != null) { - array[MONTH] = month; - } else { - getParsingFlags(config).invalidMonth = input; - } - }); - var defaultLocaleMonths = "January_February_March_April_May_June_July_August_September_October_November_December".split( - "_" - ), defaultLocaleMonthsShort = "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"), MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/, defaultMonthsShortRegex = matchWord, defaultMonthsRegex = matchWord; - function localeMonths(m, format2) { - if (!m) { - return isArray(this._months) ? this._months : this._months["standalone"]; - } - return isArray(this._months) ? this._months[m.month()] : this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format2) ? "format" : "standalone"][m.month()]; - } - function localeMonthsShort(m, format2) { - if (!m) { - return isArray(this._monthsShort) ? this._monthsShort : this._monthsShort["standalone"]; - } - return isArray(this._monthsShort) ? this._monthsShort[m.month()] : this._monthsShort[MONTHS_IN_FORMAT.test(format2) ? "format" : "standalone"][m.month()]; - } - function handleStrictParse(monthName, format2, strict) { - var i, ii, mom, llc = monthName.toLocaleLowerCase(); - if (!this._monthsParse) { - this._monthsParse = []; - this._longMonthsParse = []; - this._shortMonthsParse = []; - for (i = 0; i < 12; ++i) { - mom = createUTC([2e3, i]); - this._shortMonthsParse[i] = this.monthsShort( - mom, - "" - ).toLocaleLowerCase(); - this._longMonthsParse[i] = this.months(mom, "").toLocaleLowerCase(); - } - } - if (strict) { - if (format2 === "MMM") { - ii = indexOf.call(this._shortMonthsParse, llc); - return ii !== -1 ? ii : null; - } else { - ii = indexOf.call(this._longMonthsParse, llc); - return ii !== -1 ? ii : null; - } - } else { - if (format2 === "MMM") { - ii = indexOf.call(this._shortMonthsParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._longMonthsParse, llc); - return ii !== -1 ? ii : null; - } else { - ii = indexOf.call(this._longMonthsParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._shortMonthsParse, llc); - return ii !== -1 ? ii : null; - } - } - } - function localeMonthsParse(monthName, format2, strict) { - var i, mom, regex; - if (this._monthsParseExact) { - return handleStrictParse.call(this, monthName, format2, strict); - } - if (!this._monthsParse) { - this._monthsParse = []; - this._longMonthsParse = []; - this._shortMonthsParse = []; - } - for (i = 0; i < 12; i++) { - mom = createUTC([2e3, i]); - if (strict && !this._longMonthsParse[i]) { - this._longMonthsParse[i] = new RegExp( - "^" + this.months(mom, "").replace(".", "") + "$", - "i" - ); - this._shortMonthsParse[i] = new RegExp( - "^" + this.monthsShort(mom, "").replace(".", "") + "$", - "i" - ); - } - if (!strict && !this._monthsParse[i]) { - regex = "^" + this.months(mom, "") + "|^" + this.monthsShort(mom, ""); - this._monthsParse[i] = new RegExp(regex.replace(".", ""), "i"); - } - if (strict && format2 === "MMMM" && this._longMonthsParse[i].test(monthName)) { - return i; - } else if (strict && format2 === "MMM" && this._shortMonthsParse[i].test(monthName)) { - return i; - } else if (!strict && this._monthsParse[i].test(monthName)) { - return i; - } - } - } - function setMonth(mom, value) { - if (!mom.isValid()) { - return mom; - } - if (typeof value === "string") { - if (/^\d+$/.test(value)) { - value = toInt(value); - } else { - value = mom.localeData().monthsParse(value); - if (!isNumber(value)) { - return mom; - } - } - } - var month = value, date = mom.date(); - date = date < 29 ? date : Math.min(date, daysInMonth(mom.year(), month)); - void (mom._isUTC ? mom._d.setUTCMonth(month, date) : mom._d.setMonth(month, date)); - return mom; - } - function getSetMonth(value) { - if (value != null) { - setMonth(this, value); - hooks$1.updateOffset(this, true); - return this; - } else { - return get(this, "Month"); - } - } - function getDaysInMonth() { - return daysInMonth(this.year(), this.month()); - } - function monthsShortRegex(isStrict) { - if (this._monthsParseExact) { - if (!hasOwnProp(this, "_monthsRegex")) { - computeMonthsParse.call(this); - } - if (isStrict) { - return this._monthsShortStrictRegex; - } else { - return this._monthsShortRegex; - } - } else { - if (!hasOwnProp(this, "_monthsShortRegex")) { - this._monthsShortRegex = defaultMonthsShortRegex; - } - return this._monthsShortStrictRegex && isStrict ? this._monthsShortStrictRegex : this._monthsShortRegex; - } - } - function monthsRegex(isStrict) { - if (this._monthsParseExact) { - if (!hasOwnProp(this, "_monthsRegex")) { - computeMonthsParse.call(this); - } - if (isStrict) { - return this._monthsStrictRegex; - } else { - return this._monthsRegex; - } - } else { - if (!hasOwnProp(this, "_monthsRegex")) { - this._monthsRegex = defaultMonthsRegex; - } - return this._monthsStrictRegex && isStrict ? this._monthsStrictRegex : this._monthsRegex; - } - } - function computeMonthsParse() { - function cmpLenRev(a, b) { - return b.length - a.length; - } - var shortPieces = [], longPieces = [], mixedPieces = [], i, mom, shortP, longP; - for (i = 0; i < 12; i++) { - mom = createUTC([2e3, i]); - shortP = regexEscape(this.monthsShort(mom, "")); - longP = regexEscape(this.months(mom, "")); - shortPieces.push(shortP); - longPieces.push(longP); - mixedPieces.push(longP); - mixedPieces.push(shortP); - } - shortPieces.sort(cmpLenRev); - longPieces.sort(cmpLenRev); - mixedPieces.sort(cmpLenRev); - this._monthsRegex = new RegExp("^(" + mixedPieces.join("|") + ")", "i"); - this._monthsShortRegex = this._monthsRegex; - this._monthsStrictRegex = new RegExp( - "^(" + longPieces.join("|") + ")", - "i" - ); - this._monthsShortStrictRegex = new RegExp( - "^(" + shortPieces.join("|") + ")", - "i" - ); - } - function createDate(y, m, d, h, M, s, ms) { - var date; - if (y < 100 && y >= 0) { - date = new Date(y + 400, m, d, h, M, s, ms); - if (isFinite(date.getFullYear())) { - date.setFullYear(y); - } - } else { - date = new Date(y, m, d, h, M, s, ms); - } - return date; - } - function createUTCDate(y) { - var date, args; - if (y < 100 && y >= 0) { - args = Array.prototype.slice.call(arguments); - args[0] = y + 400; - date = new Date(Date.UTC.apply(null, args)); - if (isFinite(date.getUTCFullYear())) { - date.setUTCFullYear(y); - } - } else { - date = new Date(Date.UTC.apply(null, arguments)); - } - return date; - } - function firstWeekOffset(year, dow, doy) { - var fwd = 7 + dow - doy, fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7; - return -fwdlw + fwd - 1; - } - function dayOfYearFromWeeks(year, week, weekday, dow, doy) { - var localWeekday = (7 + weekday - dow) % 7, weekOffset = firstWeekOffset(year, dow, doy), dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset, resYear, resDayOfYear; - if (dayOfYear <= 0) { - resYear = year - 1; - resDayOfYear = daysInYear(resYear) + dayOfYear; - } else if (dayOfYear > daysInYear(year)) { - resYear = year + 1; - resDayOfYear = dayOfYear - daysInYear(year); - } else { - resYear = year; - resDayOfYear = dayOfYear; - } - return { - year: resYear, - dayOfYear: resDayOfYear - }; - } - function weekOfYear(mom, dow, doy) { - var weekOffset = firstWeekOffset(mom.year(), dow, doy), week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1, resWeek, resYear; - if (week < 1) { - resYear = mom.year() - 1; - resWeek = week + weeksInYear(resYear, dow, doy); - } else if (week > weeksInYear(mom.year(), dow, doy)) { - resWeek = week - weeksInYear(mom.year(), dow, doy); - resYear = mom.year() + 1; - } else { - resYear = mom.year(); - resWeek = week; - } - return { - week: resWeek, - year: resYear - }; - } - function weeksInYear(year, dow, doy) { - var weekOffset = firstWeekOffset(year, dow, doy), weekOffsetNext = firstWeekOffset(year + 1, dow, doy); - return (daysInYear(year) - weekOffset + weekOffsetNext) / 7; - } - addFormatToken("w", ["ww", 2], "wo", "week"); - addFormatToken("W", ["WW", 2], "Wo", "isoWeek"); - addRegexToken("w", match1to2, match1to2NoLeadingZero); - addRegexToken("ww", match1to2, match2); - addRegexToken("W", match1to2, match1to2NoLeadingZero); - addRegexToken("WW", match1to2, match2); - addWeekParseToken( - ["w", "ww", "W", "WW"], - function(input, week, config, token2) { - week[token2.substr(0, 1)] = toInt(input); - } + signal == null ? void 0 : signal.addEventListener("abort", abort, { once: true }); + }); + } + function normalizeHeaders(headers) { + if (!headers) { + return void 0; + } + return Object.fromEntries(new Headers(headers).entries()); + } + function normalizeBody(body) { + if (body == null) { + return void 0; + } + if (body instanceof URLSearchParams) { + return body.toString(); + } + if (typeof body === "string" || body instanceof Blob || body instanceof ArrayBuffer || body instanceof FormData) { + return body; + } + if (ArrayBuffer.isView(body)) { + return body.buffer.slice( + body.byteOffset, + body.byteOffset + body.byteLength ); - function localeWeek(mom) { - return weekOfYear(mom, this._week.dow, this._week.doy).week; + } + throw new TypeError( + "GM_xmlhttpRequest does not support ReadableStream request bodies" + ); + } + function createRequestError(message, response) { + const error = new TypeError(message); + Object.defineProperty(error, "response", { + configurable: true, + enumerable: false, + value: response + }); + return error; + } + unsafeWindow.localStorage; + function write(key, value) { + GM_setValue(key, JSON.stringify(value)); + } + function read(key) { + return JSON.parse(GM_getValue(key) || "null"); + } + function del(key) { + GM_deleteValue(key); + } + function getCookie(key) { + var _a; + return ((_a = document.cookie.split(";").find((c) => c.trim().startsWith(key + "="))) == null ? void 0 : _a.split("=")[1]) || null; + } + const baseUrl = "https://api5-sinfonlinec.jxbhmy.com/redcandle/search"; + const baseQuery = { + aid: "1967", + app_name: "novelapp" + }; + async function get(path, cookie, query, headers, optionsOverride) { + let url = baseUrl + path; + getCookie("sessionid"); + const h = { + Cookie: "", + ...headers + }; + const deviceQuery = { + device_id: _config.currentConfig.device_id, + iid: _config.currentConfig.install_id + }; + const finalQuery = { + ...baseQuery, + ...deviceQuery, + ...query + }; + url += "?" + new URLSearchParams(finalQuery).toString(); + const options = { + method: "GET", + headers: h, + ...optionsOverride + }; + console.log("---start--- GET ", url, options); + const res = await apiFetch(url, options); + console.log("---complete--- GET ", url, res); + return res; + } + async function post(path, cookie, query, body, headers, optionsOverride) { + let url = baseUrl + path; + getCookie("sessionid"); + const h = { + Cookie: "", + ...headers + }; + const deviceQuery = { + device_id: _config.currentConfig.device_id, + iid: _config.currentConfig.install_id + }; + const finalQuery = { + ...baseQuery, + ...deviceQuery, + ...query + }; + url += "?" + new URLSearchParams(finalQuery).toString(); + const options = { + method: "POST", + headers: h, + body, + ...optionsOverride + }; + console.log("---start--- POST ", url, options); + const res = await apiFetch(url, options); + console.log("---complete--- POST ", url, res); + return res; + } + function getCrypto() { + const c = globalThis.crypto ?? unsafeWindow.crypto; + if (!(c == null ? void 0 : c.subtle)) { + throw new Error("Crypto API不可用,请检查浏览器版本是否支持该API"); + } + return c; + } + function getSubtle() { + return getCrypto().subtle; + } + function b64decode(b64) { + const binaryString = atob(b64); + const len = binaryString.length; + const bytes = new Uint8Array(len); + for (let i = 0; i < len; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + return bytes.buffer; + } + function b64encode(buffer) { + const bytes = new Uint8Array(buffer); + const chunkSize = 32768; + const chunks = []; + for (let i = 0; i < bytes.length; i += chunkSize) { + chunks.push( + String.fromCharCode(...bytes.subarray(i, i + chunkSize)) + ); + } + return btoa(chunks.join("")); + } + function unhex(hex) { + if (hex.length % 2 !== 0) { + throw new Error("Invalid hex string"); + } + const bytes = new Uint8Array(hex.length / 2); + for (let i = 0; i < hex.length; i += 2) { + const byte = parseInt(hex.slice(i, i + 2), 16); + if (Number.isNaN(byte)) { + throw new Error("Invalid hex string"); } - var defaultLocaleWeek = { - dow: 0, - // Sunday is the first day of the week. - doy: 6 - // The week that contains Jan 6th is the first week of the year. + bytes[i / 2] = byte; + } + return bytes.buffer; + } + function randomString(length) { + const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + let result = ""; + const array = new Uint8Array(length); + getCrypto().getRandomValues(array); + for (let i = 0; i < length; i++) { + result += chars.charAt(array[i] % chars.length); + } + return result; + } + async function decryptChapter(encrypted, config = defaultConfig) { + var _a; + if (!encrypted) { + throw new Error("Invalid encrypted chapter"); + } + const buf = b64decode(encrypted); + const iv = buf.slice(0, 16); + const data = buf.slice(16); + const key = (_a = config.key_info) == null ? void 0 : _a.key; + if (!key) { + throw new Error("Missing decrypt key"); + } + const subtle = getSubtle(); + const cryptoKey = await subtle.importKey( + "raw", + key, + { name: "AES-CBC" }, + false, + ["decrypt"] + ); + return subtle.decrypt( + { name: "AES-CBC", iv }, + cryptoKey, + data + ).then((decrypted) => { + const decoder = new TextDecoder(); + const plain = decoder.decode(decrypted); + if (plain.trim().startsWith("<")) { + return plain; + } + try { + return JSON.parse(plain); + } catch (e) { + console.warn("Invalid chapter content: ", plain, e); + return void 0; + } + }); + } + function reverseHex(value) { + const be = BigInt(value).toString(16).padStart(32, "0"); + let result = ""; + for (let i = be.length; i > 0; i -= 2) result += be.slice(i - 2, i); + return result; + } + async function encryptKeyinfoBody(config) { + const deviceId = config.device_id; + const iv = new TextEncoder().encode(randomString(16)); + const data = new Uint8Array(unhex(reverseHex(deviceId))).slice(0, 8); + console.log(data); + const subtle = getSubtle(); + const k = await subtle.importKey( + "raw", + shared_key, + { name: "AES-CBC" }, + false, + ["encrypt"] + ); + const encrypted = await subtle.encrypt( + { name: "AES-CBC", iv }, + k, + data + ); + const final = new Uint8Array(iv.length + encrypted.byteLength); + console.log(final); + final.set(iv, 0); + final.set(new Uint8Array(encrypted), iv.length); + return JSON.stringify({ + content: b64encode(final.buffer) + }); + } + async function decryptKeyinfoResponse(encrypted) { + const buf = b64decode(encrypted); + const iv = buf.slice(0, 16); + const data = buf.slice(16); + const subtle = getSubtle(); + const k = await subtle.importKey( + "raw", + shared_key, + { name: "AES-CBC" }, + false, + ["decrypt"] + ); + return subtle.decrypt( + { name: "AES-CBC", iv }, + k, + data + ); + } + async function decryptComicImage(image, key) { + const subtle = getSubtle(); + const cryptoKey = await subtle.importKey( + "raw", + unhex(key), + { name: "AES-GCM" }, + false, + ["decrypt"] + ); + const iv = image.slice(0, 12); + const data = image.slice(12); + return await subtle.decrypt( + { name: "AES-GCM", iv }, + cryptoKey, + data + ); + } + async function refreshKeyinfo() { + var _a, _b, _c; + const b = await encryptKeyinfoBody(_config.currentConfig); + const res = await post("/reader/crypt/registerkey/v1", false, {}, b); + console.log(res); + const j = res.json(); + console.log("response: ", j); + const ek = (_a = j == null ? void 0 : j.data) == null ? void 0 : _a.key; + if (!ek) { + throw new Error("Failed to get key info"); + } + const key = await decryptKeyinfoResponse(ek); + const keyinfo = { + key, + keyver: (_b = j == null ? void 0 : j.data) == null ? void 0 : _b.keyver + }; + console.log("Refreshed key info:", keyinfo); + _config.currentConfig.key_info = keyinfo; + write("keyinfo", { + key: b64encode(key), + keyver: (_c = j == null ? void 0 : j.data) == null ? void 0 : _c.keyver + }); + } + async function ensureKeyinfo(expectedKeyVersion) { + const keyinfo = _config.currentConfig.key_info; + const cachedKeyInfo = read("keyinfo"); + console.log("cached key info: ", cachedKeyInfo); + if (cachedKeyInfo) { + const cki = { + key: b64decode(cachedKeyInfo.key), + keyver: cachedKeyInfo.keyver }; - function localeFirstDayOfWeek() { - return this._week.dow; + if (typeof expectedKeyVersion === "undefined" || cki.keyver === expectedKeyVersion) { + _config.currentConfig.key_info = cki; + return; } - function localeFirstDayOfYear() { - return this._week.doy; - } - function getSetWeek(input) { - var week = this.localeData().week(this); - return input == null ? week : this.add((input - week) * 7, "d"); - } - function getSetISOWeek(input) { - var week = weekOfYear(this, 1, 4).week; - return input == null ? week : this.add((input - week) * 7, "d"); - } - addFormatToken("d", 0, "do", "day"); - addFormatToken("dd", 0, 0, function(format2) { - return this.localeData().weekdaysMin(this, format2); - }); - addFormatToken("ddd", 0, 0, function(format2) { - return this.localeData().weekdaysShort(this, format2); - }); - addFormatToken("dddd", 0, 0, function(format2) { - return this.localeData().weekdays(this, format2); - }); - addFormatToken("e", 0, 0, "weekday"); - addFormatToken("E", 0, 0, "isoWeekday"); - addRegexToken("d", match1to2); - addRegexToken("e", match1to2); - addRegexToken("E", match1to2); - addRegexToken("dd", function(isStrict, locale2) { - return locale2.weekdaysMinRegex(isStrict); - }); - addRegexToken("ddd", function(isStrict, locale2) { - return locale2.weekdaysShortRegex(isStrict); - }); - addRegexToken("dddd", function(isStrict, locale2) { - return locale2.weekdaysRegex(isStrict); - }); - addWeekParseToken(["dd", "ddd", "dddd"], function(input, week, config, token2) { - var weekday = config._locale.weekdaysParse(input, token2, config._strict); - if (weekday != null) { - week.d = weekday; - } else { - getParsingFlags(config).invalidWeekday = input; - } - }); - addWeekParseToken(["d", "e", "E"], function(input, week, config, token2) { - week[token2] = toInt(input); - }); - function parseWeekday(input, locale2) { - if (typeof input !== "string") { - return input; - } - if (!isNaN(input)) { - return parseInt(input, 10); - } - input = locale2.weekdaysParse(input); - if (typeof input === "number") { - return input; - } - return null; - } - function parseIsoWeekday(input, locale2) { - if (typeof input === "string") { - return locale2.weekdaysParse(input) % 7 || 7; - } - return isNaN(input) ? null : input; - } - function shiftWeekdays(ws, n) { - return ws.slice(n, 7).concat(ws.slice(0, n)); - } - var defaultLocaleWeekdays = "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), defaultLocaleWeekdaysShort = "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"), defaultLocaleWeekdaysMin = "Su_Mo_Tu_We_Th_Fr_Sa".split("_"), defaultWeekdaysRegex = matchWord, defaultWeekdaysShortRegex = matchWord, defaultWeekdaysMinRegex = matchWord; - function localeWeekdays(m, format2) { - var weekdays = isArray(this._weekdays) ? this._weekdays : this._weekdays[m && m !== true && this._weekdays.isFormat.test(format2) ? "format" : "standalone"]; - return m === true ? shiftWeekdays(weekdays, this._week.dow) : m ? weekdays[m.day()] : weekdays; - } - function localeWeekdaysShort(m) { - return m === true ? shiftWeekdays(this._weekdaysShort, this._week.dow) : m ? this._weekdaysShort[m.day()] : this._weekdaysShort; - } - function localeWeekdaysMin(m) { - return m === true ? shiftWeekdays(this._weekdaysMin, this._week.dow) : m ? this._weekdaysMin[m.day()] : this._weekdaysMin; - } - function handleStrictParse$1(weekdayName, format2, strict) { - var i, ii, mom, llc = weekdayName.toLocaleLowerCase(); - if (!this._weekdaysParse) { - this._weekdaysParse = []; - this._shortWeekdaysParse = []; - this._minWeekdaysParse = []; - for (i = 0; i < 7; ++i) { - mom = createUTC([2e3, 1]).day(i); - this._minWeekdaysParse[i] = this.weekdaysMin( - mom, - "" - ).toLocaleLowerCase(); - this._shortWeekdaysParse[i] = this.weekdaysShort( - mom, - "" - ).toLocaleLowerCase(); - this._weekdaysParse[i] = this.weekdays(mom, "").toLocaleLowerCase(); - } - } - if (strict) { - if (format2 === "dddd") { - ii = indexOf.call(this._weekdaysParse, llc); - return ii !== -1 ? ii : null; - } else if (format2 === "ddd") { - ii = indexOf.call(this._shortWeekdaysParse, llc); - return ii !== -1 ? ii : null; - } else { - ii = indexOf.call(this._minWeekdaysParse, llc); - return ii !== -1 ? ii : null; - } - } else { - if (format2 === "dddd") { - ii = indexOf.call(this._weekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._shortWeekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._minWeekdaysParse, llc); - return ii !== -1 ? ii : null; - } else if (format2 === "ddd") { - ii = indexOf.call(this._shortWeekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._weekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._minWeekdaysParse, llc); - return ii !== -1 ? ii : null; - } else { - ii = indexOf.call(this._minWeekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._weekdaysParse, llc); - if (ii !== -1) { - return ii; - } - ii = indexOf.call(this._shortWeekdaysParse, llc); - return ii !== -1 ? ii : null; - } - } - } - function localeWeekdaysParse(weekdayName, format2, strict) { - var i, mom, regex; - if (this._weekdaysParseExact) { - return handleStrictParse$1.call(this, weekdayName, format2, strict); - } - if (!this._weekdaysParse) { - this._weekdaysParse = []; - this._minWeekdaysParse = []; - this._shortWeekdaysParse = []; - this._fullWeekdaysParse = []; - } - for (i = 0; i < 7; i++) { - mom = createUTC([2e3, 1]).day(i); - if (strict && !this._fullWeekdaysParse[i]) { - this._fullWeekdaysParse[i] = new RegExp( - "^" + this.weekdays(mom, "").replace(".", "\\.?") + "$", - "i" - ); - this._shortWeekdaysParse[i] = new RegExp( - "^" + this.weekdaysShort(mom, "").replace(".", "\\.?") + "$", - "i" - ); - this._minWeekdaysParse[i] = new RegExp( - "^" + this.weekdaysMin(mom, "").replace(".", "\\.?") + "$", - "i" - ); - } - if (!this._weekdaysParse[i]) { - regex = "^" + this.weekdays(mom, "") + "|^" + this.weekdaysShort(mom, "") + "|^" + this.weekdaysMin(mom, ""); - this._weekdaysParse[i] = new RegExp(regex.replace(".", ""), "i"); - } - if (strict && format2 === "dddd" && this._fullWeekdaysParse[i].test(weekdayName)) { - return i; - } else if (strict && format2 === "ddd" && this._shortWeekdaysParse[i].test(weekdayName)) { - return i; - } else if (strict && format2 === "dd" && this._minWeekdaysParse[i].test(weekdayName)) { - return i; - } else if (!strict && this._weekdaysParse[i].test(weekdayName)) { - return i; - } - } - } - function getSetDayOfWeek(input) { - if (!this.isValid()) { - return input != null ? this : NaN; - } - var day = get(this, "Day"); - if (input != null) { - input = parseWeekday(input, this.localeData()); - return this.add(input - day, "d"); - } else { - return day; - } - } - function getSetLocaleDayOfWeek(input) { - if (!this.isValid()) { - return input != null ? this : NaN; - } - var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; - return input == null ? weekday : this.add(input - weekday, "d"); - } - function getSetISODayOfWeek(input) { - if (!this.isValid()) { - return input != null ? this : NaN; - } - if (input != null) { - var weekday = parseIsoWeekday(input, this.localeData()); - return this.day(this.day() % 7 ? weekday : weekday - 7); - } else { - return this.day() || 7; - } - } - function weekdaysRegex(isStrict) { - if (this._weekdaysParseExact) { - if (!hasOwnProp(this, "_weekdaysRegex")) { - computeWeekdaysParse.call(this); - } - if (isStrict) { - return this._weekdaysStrictRegex; - } else { - return this._weekdaysRegex; - } - } else { - if (!hasOwnProp(this, "_weekdaysRegex")) { - this._weekdaysRegex = defaultWeekdaysRegex; - } - return this._weekdaysStrictRegex && isStrict ? this._weekdaysStrictRegex : this._weekdaysRegex; - } - } - function weekdaysShortRegex(isStrict) { - if (this._weekdaysParseExact) { - if (!hasOwnProp(this, "_weekdaysRegex")) { - computeWeekdaysParse.call(this); - } - if (isStrict) { - return this._weekdaysShortStrictRegex; - } else { - return this._weekdaysShortRegex; - } - } else { - if (!hasOwnProp(this, "_weekdaysShortRegex")) { - this._weekdaysShortRegex = defaultWeekdaysShortRegex; - } - return this._weekdaysShortStrictRegex && isStrict ? this._weekdaysShortStrictRegex : this._weekdaysShortRegex; - } - } - function weekdaysMinRegex(isStrict) { - if (this._weekdaysParseExact) { - if (!hasOwnProp(this, "_weekdaysRegex")) { - computeWeekdaysParse.call(this); - } - if (isStrict) { - return this._weekdaysMinStrictRegex; - } else { - return this._weekdaysMinRegex; - } - } else { - if (!hasOwnProp(this, "_weekdaysMinRegex")) { - this._weekdaysMinRegex = defaultWeekdaysMinRegex; - } - return this._weekdaysMinStrictRegex && isStrict ? this._weekdaysMinStrictRegex : this._weekdaysMinRegex; - } - } - function computeWeekdaysParse() { - function cmpLenRev(a, b) { - return b.length - a.length; - } - var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [], i, mom, minp, shortp, longp; - for (i = 0; i < 7; i++) { - mom = createUTC([2e3, 1]).day(i); - minp = regexEscape(this.weekdaysMin(mom, "")); - shortp = regexEscape(this.weekdaysShort(mom, "")); - longp = regexEscape(this.weekdays(mom, "")); - minPieces.push(minp); - shortPieces.push(shortp); - longPieces.push(longp); - mixedPieces.push(minp); - mixedPieces.push(shortp); - mixedPieces.push(longp); - } - minPieces.sort(cmpLenRev); - shortPieces.sort(cmpLenRev); - longPieces.sort(cmpLenRev); - mixedPieces.sort(cmpLenRev); - this._weekdaysRegex = new RegExp("^(" + mixedPieces.join("|") + ")", "i"); - this._weekdaysShortRegex = this._weekdaysRegex; - this._weekdaysMinRegex = this._weekdaysRegex; - this._weekdaysStrictRegex = new RegExp( - "^(" + longPieces.join("|") + ")", - "i" - ); - this._weekdaysShortStrictRegex = new RegExp( - "^(" + shortPieces.join("|") + ")", - "i" - ); - this._weekdaysMinStrictRegex = new RegExp( - "^(" + minPieces.join("|") + ")", - "i" - ); - } - function hFormat() { - return this.hours() % 12 || 12; - } - function kFormat() { - return this.hours() || 24; - } - addFormatToken("H", ["HH", 2], 0, "hour"); - addFormatToken("h", ["hh", 2], 0, hFormat); - addFormatToken("k", ["kk", 2], 0, kFormat); - addFormatToken("hmm", 0, 0, function() { - return "" + hFormat.apply(this) + zeroFill(this.minutes(), 2); - }); - addFormatToken("hmmss", 0, 0, function() { - return "" + hFormat.apply(this) + zeroFill(this.minutes(), 2) + zeroFill(this.seconds(), 2); - }); - addFormatToken("Hmm", 0, 0, function() { - return "" + this.hours() + zeroFill(this.minutes(), 2); - }); - addFormatToken("Hmmss", 0, 0, function() { - return "" + this.hours() + zeroFill(this.minutes(), 2) + zeroFill(this.seconds(), 2); - }); - function meridiem(token2, lowercase) { - addFormatToken(token2, 0, 0, function() { - return this.localeData().meridiem( - this.hours(), - this.minutes(), - lowercase - ); - }); - } - meridiem("a", true); - meridiem("A", false); - function matchMeridiem(isStrict, locale2) { - return locale2._meridiemParse; - } - addRegexToken("a", matchMeridiem); - addRegexToken("A", matchMeridiem); - addRegexToken("H", match1to2, match1to2HasZero); - addRegexToken("h", match1to2, match1to2NoLeadingZero); - addRegexToken("k", match1to2, match1to2NoLeadingZero); - addRegexToken("HH", match1to2, match2); - addRegexToken("hh", match1to2, match2); - addRegexToken("kk", match1to2, match2); - addRegexToken("hmm", match3to4); - addRegexToken("hmmss", match5to6); - addRegexToken("Hmm", match3to4); - addRegexToken("Hmmss", match5to6); - addParseToken(["H", "HH"], HOUR); - addParseToken(["k", "kk"], function(input, array, config) { - var kInput = toInt(input); - array[HOUR] = kInput === 24 ? 0 : kInput; - }); - addParseToken(["a", "A"], function(input, array, config) { - config._isPm = config._locale.isPM(input); - config._meridiem = input; - }); - addParseToken(["h", "hh"], function(input, array, config) { - array[HOUR] = toInt(input); - getParsingFlags(config).bigHour = true; - }); - addParseToken("hmm", function(input, array, config) { - var pos = input.length - 2; - array[HOUR] = toInt(input.substr(0, pos)); - array[MINUTE] = toInt(input.substr(pos)); - getParsingFlags(config).bigHour = true; - }); - addParseToken("hmmss", function(input, array, config) { - var pos1 = input.length - 4, pos2 = input.length - 2; - array[HOUR] = toInt(input.substr(0, pos1)); - array[MINUTE] = toInt(input.substr(pos1, 2)); - array[SECOND] = toInt(input.substr(pos2)); - getParsingFlags(config).bigHour = true; - }); - addParseToken("Hmm", function(input, array, config) { - var pos = input.length - 2; - array[HOUR] = toInt(input.substr(0, pos)); - array[MINUTE] = toInt(input.substr(pos)); - }); - addParseToken("Hmmss", function(input, array, config) { - var pos1 = input.length - 4, pos2 = input.length - 2; - array[HOUR] = toInt(input.substr(0, pos1)); - array[MINUTE] = toInt(input.substr(pos1, 2)); - array[SECOND] = toInt(input.substr(pos2)); - }); - function localeIsPM(input) { - return (input + "").toLowerCase().charAt(0) === "p"; - } - var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i, getSetHour = makeGetSet("Hours", true); - function localeMeridiem(hours2, minutes2, isLower) { - if (hours2 > 11) { - return isLower ? "pm" : "PM"; - } else { - return isLower ? "am" : "AM"; - } - } - var baseConfig = { - calendar: defaultCalendar, - longDateFormat: defaultLongDateFormat, - invalidDate: defaultInvalidDate, - ordinal: defaultOrdinal, - dayOfMonthOrdinalParse: defaultDayOfMonthOrdinalParse, - relativeTime: defaultRelativeTime, - months: defaultLocaleMonths, - monthsShort: defaultLocaleMonthsShort, - week: defaultLocaleWeek, - weekdays: defaultLocaleWeekdays, - weekdaysMin: defaultLocaleWeekdaysMin, - weekdaysShort: defaultLocaleWeekdaysShort, - meridiemParse: defaultLocaleMeridiemParse + } + if (!keyinfo) { + return await refreshKeyinfo(); + } + if ((keyinfo == null ? void 0 : keyinfo.keyver) !== expectedKeyVersion) { + return await refreshKeyinfo(); + } + } + async function getChapter(itemId, _retry) { + var _a, _b; + if (typeof _retry === "undefined") _retry = 0; + if (_retry > 5) { + throw new Error(`Failed to get chapter: ${itemId}`); + } + if (!_config.currentConfig.key_info) { + await ensureKeyinfo(); + } + const res = await get(`/reader/full/v1`, false, { item_id: itemId }); + const j = (_a = res.json()) == null ? void 0 : _a.data; + if (!j) { + console.warn("Failed to get chapter: ", itemId, ", response: ", j); + return await getChapter(itemId, _retry + 1); + } + if ((j == null ? void 0 : j.content) === "Invalid" || (j == null ? void 0 : j.key_version) !== ((_b = _config.currentConfig.key_info) == null ? void 0 : _b.keyver)) { + console.warn("Key reg expired, regster again and retrying..."); + await ensureKeyinfo(parseInt(j == null ? void 0 : j.key_version)); + return await getChapter(itemId, _retry + 1); + } + j.content = await decryptChapter(j == null ? void 0 : j.content, _config.currentConfig); + return j; + } + async function getCatalogRaw(bookId) { + const response = await apiFetch(`https://fanqienovel.com/api/reader/directory/detail?bookId=${bookId}`); + const j = response.json(); + if (j.code !== 0 || j.data.chapterListWithVolume.length === 0) { + throw new Error("Empty catalog"); + } + return [j.data.chapterListWithVolume, j.data.allItemIds]; + } + async function getCatalog(bookId) { + const r = await getCatalogRaw(bookId); + const catalogRaw = r[0]; + const allItemIds = r[1]; + const vmap = {}; + const chapters = []; + catalogRaw[0].map((item) => { + const chapterItem = { + item_id: item.itemId, + title: item.title, + // YYYY-MM-DD HH:mm:ss + update_time: moment(item.firstPassTime * 1e3).format("YYYY-MM-DD HH:mm:ss"), + char_count: item.charCount || 0, + volume_title: item.volume_name }; - var locales = {}, localeFamilies = {}, globalLocale; - function commonPrefix(arr1, arr2) { - var i, minl = Math.min(arr1.length, arr2.length); - for (i = 0; i < minl; i += 1) { - if (arr1[i] !== arr2[i]) { - return i; - } - } - return minl; - } - function normalizeLocale(key) { - return key ? key.toLowerCase().replace("_", "-") : key; - } - function chooseLocale(names) { - var i = 0, j, next, locale2, split; - while (i < names.length) { - split = normalizeLocale(names[i]).split("-"); - j = split.length; - next = normalizeLocale(names[i + 1]); - next = next ? next.split("-") : null; - while (j > 0) { - locale2 = loadLocale(split.slice(0, j).join("-")); - if (locale2) { - return locale2; - } - if (next && next.length >= j && commonPrefix(split, next) >= j - 1) { - break; - } - j--; - } - i++; - } - return globalLocale; - } - function isLocaleNameSane(name2) { - return !!(name2 && name2.match("^[^/\\\\]*$")); - } - function loadLocale(name2) { - var oldLocale = null, aliasedRequire; - if (locales[name2] === void 0 && typeof module !== "undefined" && module && module.exports && isLocaleNameSane(name2)) { - try { - oldLocale = globalLocale._abbr; - aliasedRequire = require; - aliasedRequire("./locale/" + name2); - getSetGlobalLocale(oldLocale); - } catch (e) { - locales[name2] = null; - } - } - return locales[name2]; - } - function getSetGlobalLocale(key, values) { - var data; - if (key) { - if (isUndefined(values)) { - data = getLocale(key); - } else { - data = defineLocale(key, values); - } - if (data) { - globalLocale = data; - } else { - if (typeof console !== "undefined" && console.warn) { - console.warn( - "Locale " + key + " not found. Did you forget to load it?" - ); - } - } - } - return globalLocale._abbr; - } - function defineLocale(name2, config) { - if (config !== null) { - var locale2, parentConfig = baseConfig; - config.abbr = name2; - if (locales[name2] != null) { - deprecateSimple( - "defineLocaleOverride", - "use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info." - ); - parentConfig = locales[name2]._config; - } else if (config.parentLocale != null) { - if (locales[config.parentLocale] != null) { - parentConfig = locales[config.parentLocale]._config; - } else { - locale2 = loadLocale(config.parentLocale); - if (locale2 != null) { - parentConfig = locale2._config; - } else { - if (!localeFamilies[config.parentLocale]) { - localeFamilies[config.parentLocale] = []; - } - localeFamilies[config.parentLocale].push({ - name: name2, - config - }); - return null; - } - } - } - locales[name2] = new Locale(mergeConfigs(parentConfig, config)); - if (localeFamilies[name2]) { - localeFamilies[name2].forEach(function(x) { - defineLocale(x.name, x.config); - }); - } - getSetGlobalLocale(name2); - return locales[name2]; - } else { - delete locales[name2]; - return null; - } - } - function updateLocale(name2, config) { - if (config != null) { - var locale2, tmpLocale, parentConfig = baseConfig; - if (locales[name2] != null && locales[name2].parentLocale != null) { - locales[name2].set(mergeConfigs(locales[name2]._config, config)); - } else { - tmpLocale = loadLocale(name2); - if (tmpLocale != null) { - parentConfig = tmpLocale._config; - } - config = mergeConfigs(parentConfig, config); - if (tmpLocale == null) { - config.abbr = name2; - } - locale2 = new Locale(config); - locale2.parentLocale = locales[name2]; - locales[name2] = locale2; - } - getSetGlobalLocale(name2); - } else { - if (locales[name2] != null) { - if (locales[name2].parentLocale != null) { - locales[name2] = locales[name2].parentLocale; - if (name2 === getSetGlobalLocale()) { - getSetGlobalLocale(name2); - } - } else if (locales[name2] != null) { - delete locales[name2]; - } - } - } - return locales[name2]; - } - function getLocale(key) { - var locale2; - if (key && key._locale && key._locale._abbr) { - key = key._locale._abbr; - } - if (!key) { - return globalLocale; - } - if (!isArray(key)) { - locale2 = loadLocale(key); - if (locale2) { - return locale2; - } - key = [key]; - } - return chooseLocale(key); - } - function listLocales() { - return keys(locales); - } - function checkOverflow(m) { - var overflow, a = m._a; - if (a && getParsingFlags(m).overflow === -2) { - overflow = a[MONTH] < 0 || a[MONTH] > 11 ? MONTH : a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE : a[HOUR] < 0 || a[HOUR] > 24 || a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0) ? HOUR : a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE : a[SECOND] < 0 || a[SECOND] > 59 ? SECOND : a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND : -1; - if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { - overflow = DATE; - } - if (getParsingFlags(m)._overflowWeeks && overflow === -1) { - overflow = WEEK; - } - if (getParsingFlags(m)._overflowWeekday && overflow === -1) { - overflow = WEEKDAY; - } - getParsingFlags(m).overflow = overflow; - } - return m; - } - var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/, basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d|))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/, tzRegex = /Z|[+-]\d\d(?::?\d\d)?/, isoDates = [ - ["YYYYYY-MM-DD", /[+-]\d{6}-\d\d-\d\d/], - ["YYYY-MM-DD", /\d{4}-\d\d-\d\d/], - ["GGGG-[W]WW-E", /\d{4}-W\d\d-\d/], - ["GGGG-[W]WW", /\d{4}-W\d\d/, false], - ["YYYY-DDD", /\d{4}-\d{3}/], - ["YYYY-MM", /\d{4}-\d\d/, false], - ["YYYYYYMMDD", /[+-]\d{10}/], - ["YYYYMMDD", /\d{8}/], - ["GGGG[W]WWE", /\d{4}W\d{3}/], - ["GGGG[W]WW", /\d{4}W\d{2}/, false], - ["YYYYDDD", /\d{7}/], - ["YYYYMM", /\d{6}/, false], - ["YYYY", /\d{4}/, false] - ], isoTimes = [ - ["HH:mm:ss.SSSS", /\d\d:\d\d:\d\d\.\d+/], - ["HH:mm:ss,SSSS", /\d\d:\d\d:\d\d,\d+/], - ["HH:mm:ss", /\d\d:\d\d:\d\d/], - ["HH:mm", /\d\d:\d\d/], - ["HHmmss.SSSS", /\d\d\d\d\d\d\.\d+/], - ["HHmmss,SSSS", /\d\d\d\d\d\d,\d+/], - ["HHmmss", /\d\d\d\d\d\d/], - ["HHmm", /\d\d\d\d/], - ["HH", /\d\d/] - ], aspNetJsonRegex = /^\/?Date\((-?\d+)/i, rfc2822 = /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/, obsOffsets = { - UT: 0, - GMT: 0, - EDT: -4 * 60, - EST: -5 * 60, - CDT: -5 * 60, - CST: -6 * 60, - MDT: -6 * 60, - MST: -7 * 60, - PDT: -7 * 60, - PST: -8 * 60 - }; - function configFromISO(config) { - var i, l, string = config._i, match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string), allowTime, dateFormat, timeFormat, tzFormat, isoDatesLen = isoDates.length, isoTimesLen = isoTimes.length; - if (match) { - getParsingFlags(config).iso = true; - for (i = 0, l = isoDatesLen; i < l; i++) { - if (isoDates[i][1].exec(match[1])) { - dateFormat = isoDates[i][0]; - allowTime = isoDates[i][2] !== false; - break; - } - } - if (dateFormat == null) { - config._isValid = false; - return; - } - if (match[3]) { - for (i = 0, l = isoTimesLen; i < l; i++) { - if (isoTimes[i][1].exec(match[3])) { - timeFormat = (match[2] || " ") + isoTimes[i][0]; - break; - } - } - if (timeFormat == null) { - config._isValid = false; - return; - } - } - if (!allowTime && timeFormat != null) { - config._isValid = false; - return; - } - if (match[4]) { - if (tzRegex.exec(match[4])) { - tzFormat = "Z"; - } else { - config._isValid = false; - return; - } - } - config._f = dateFormat + (timeFormat || "") + (tzFormat || ""); - configFromStringAndFormat(config); - } else { - config._isValid = false; - } - } - function extractFromRFC2822Strings(yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr) { - var result = [ - untruncateYear(yearStr), - defaultLocaleMonthsShort.indexOf(monthStr), - parseInt(dayStr, 10), - parseInt(hourStr, 10), - parseInt(minuteStr, 10) - ]; - if (secondStr) { - result.push(parseInt(secondStr, 10)); - } - return result; - } - function untruncateYear(yearStr) { - var year = parseInt(yearStr, 10); - if (year <= 49) { - return 2e3 + year; - } else if (year <= 999) { - return 1900 + year; - } - return year; - } - function preprocessRFC2822(s) { - return s.replace(/\([^()]*\)|[\n\t]/g, " ").replace(/(\s\s+)/g, " ").replace(/^\s\s*/, "").replace(/\s\s*$/, ""); - } - function checkWeekday(weekdayStr, parsedInput, config) { - if (weekdayStr) { - var weekdayProvided = defaultLocaleWeekdaysShort.indexOf(weekdayStr), weekdayActual = new Date( - parsedInput[0], - parsedInput[1], - parsedInput[2] - ).getDay(); - if (weekdayProvided !== weekdayActual) { - getParsingFlags(config).weekdayMismatch = true; - config._isValid = false; - return false; - } - } - return true; - } - function calculateOffset(obsOffset, militaryOffset, numOffset) { - if (obsOffset) { - return obsOffsets[obsOffset]; - } else if (militaryOffset) { - return 0; - } else { - var hm = parseInt(numOffset, 10), m = hm % 100, h = (hm - m) / 100; - return h * 60 + m; - } - } - function configFromRFC2822(config) { - var match = rfc2822.exec(preprocessRFC2822(config._i)), parsedArray; - if (match) { - parsedArray = extractFromRFC2822Strings( - match[4], - match[3], - match[2], - match[5], - match[6], - match[7] - ); - if (!checkWeekday(match[1], parsedArray, config)) { - return; - } - config._a = parsedArray; - config._tzm = calculateOffset(match[8], match[9], match[10]); - config._d = createUTCDate.apply(null, config._a); - config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); - getParsingFlags(config).rfc2822 = true; - } else { - config._isValid = false; - } - } - function configFromString(config) { - var matched = aspNetJsonRegex.exec(config._i); - if (matched !== null) { - config._d = /* @__PURE__ */ new Date(+matched[1]); - return; - } - configFromISO(config); - if (config._isValid === false) { - delete config._isValid; - } else { - return; - } - configFromRFC2822(config); - if (config._isValid === false) { - delete config._isValid; - } else { - return; - } - if (config._strict) { - config._isValid = false; - } else { - hooks$1.createFromInputFallback(config); - } - } - hooks$1.createFromInputFallback = deprecate( - "value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.", - function(config) { - config._d = /* @__PURE__ */ new Date(config._i + (config._useUTC ? " UTC" : "")); - } - ); - function defaults(a, b, c) { - if (a != null) { - return a; - } - if (b != null) { - return b; - } - return c; - } - function currentDateArray(config) { - var nowValue = new Date(hooks$1.now()); - if (config._useUTC) { - return [ - nowValue.getUTCFullYear(), - nowValue.getUTCMonth(), - nowValue.getUTCDate() - ]; - } - return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()]; - } - function configFromArray(config) { - var i, date, input = [], currentDate, expectedWeekday, yearToUse; - if (config._d) { - return; - } - currentDate = currentDateArray(config); - if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { - dayOfYearFromWeekInfo(config); - } - if (config._dayOfYear != null) { - yearToUse = defaults(config._a[YEAR], currentDate[YEAR]); - if (config._dayOfYear > daysInYear(yearToUse) || config._dayOfYear === 0) { - getParsingFlags(config)._overflowDayOfYear = true; - } - date = createUTCDate(yearToUse, 0, config._dayOfYear); - config._a[MONTH] = date.getUTCMonth(); - config._a[DATE] = date.getUTCDate(); - } - for (i = 0; i < 3 && config._a[i] == null; ++i) { - config._a[i] = input[i] = currentDate[i]; - } - for (; i < 7; i++) { - config._a[i] = input[i] = config._a[i] == null ? i === 2 ? 1 : 0 : config._a[i]; - } - if (config._a[HOUR] === 24 && config._a[MINUTE] === 0 && config._a[SECOND] === 0 && config._a[MILLISECOND] === 0) { - config._nextDay = true; - config._a[HOUR] = 0; - } - config._d = (config._useUTC ? createUTCDate : createDate).apply( - null, - input - ); - expectedWeekday = config._useUTC ? config._d.getUTCDay() : config._d.getDay(); - if (config._tzm != null) { - config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); - } - if (config._nextDay) { - config._a[HOUR] = 24; - } - if (config._w && typeof config._w.d !== "undefined" && config._w.d !== expectedWeekday) { - getParsingFlags(config).weekdayMismatch = true; - } - } - function dayOfYearFromWeekInfo(config) { - var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow, curWeek; - w = config._w; - if (w.GG != null || w.W != null || w.E != null) { - dow = 1; - doy = 4; - weekYear = defaults( - w.GG, - config._a[YEAR], - weekOfYear(createLocal(), 1, 4).year - ); - week = defaults(w.W, 1); - weekday = defaults(w.E, 1); - if (weekday < 1 || weekday > 7) { - weekdayOverflow = true; - } - } else { - dow = config._locale._week.dow; - doy = config._locale._week.doy; - curWeek = weekOfYear(createLocal(), dow, doy); - weekYear = defaults(w.gg, config._a[YEAR], curWeek.year); - week = defaults(w.w, curWeek.week); - if (w.d != null) { - weekday = w.d; - if (weekday < 0 || weekday > 6) { - weekdayOverflow = true; - } - } else if (w.e != null) { - weekday = w.e + dow; - if (w.e < 0 || w.e > 6) { - weekdayOverflow = true; - } - } else { - weekday = dow; - } - } - if (week < 1 || week > weeksInYear(weekYear, dow, doy)) { - getParsingFlags(config)._overflowWeeks = true; - } else if (weekdayOverflow != null) { - getParsingFlags(config)._overflowWeekday = true; - } else { - temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy); - config._a[YEAR] = temp.year; - config._dayOfYear = temp.dayOfYear; - } - } - hooks$1.ISO_8601 = function() { - }; - hooks$1.RFC_2822 = function() { - }; - function configFromStringAndFormat(config) { - if (config._f === hooks$1.ISO_8601) { - configFromISO(config); - return; - } - if (config._f === hooks$1.RFC_2822) { - configFromRFC2822(config); - return; - } - config._a = []; - getParsingFlags(config).empty = true; - var string = "" + config._i, i, parsedInput, tokens2, token2, skipped, stringLength = string.length, totalParsedInputLength = 0, era, tokenLen; - tokens2 = expandFormat(config._f, config._locale).match(formattingTokens) || []; - tokenLen = tokens2.length; - for (i = 0; i < tokenLen; i++) { - token2 = tokens2[i]; - parsedInput = (string.match(getParseRegexForToken(token2, config)) || [])[0]; - if (parsedInput) { - skipped = string.substr(0, string.indexOf(parsedInput)); - if (skipped.length > 0) { - getParsingFlags(config).unusedInput.push(skipped); - } - string = string.slice( - string.indexOf(parsedInput) + parsedInput.length - ); - totalParsedInputLength += parsedInput.length; - } - if (formatTokenFunctions[token2]) { - if (parsedInput) { - getParsingFlags(config).empty = false; - } else { - getParsingFlags(config).unusedTokens.push(token2); - } - addTimeToArrayFromToken(token2, parsedInput, config); - } else if (config._strict && !parsedInput) { - getParsingFlags(config).unusedTokens.push(token2); - } - } - getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength; - if (string.length > 0) { - getParsingFlags(config).unusedInput.push(string); - } - if (config._a[HOUR] <= 12 && getParsingFlags(config).bigHour === true && config._a[HOUR] > 0) { - getParsingFlags(config).bigHour = void 0; - } - getParsingFlags(config).parsedDateParts = config._a.slice(0); - getParsingFlags(config).meridiem = config._meridiem; - config._a[HOUR] = meridiemFixWrap( - config._locale, - config._a[HOUR], - config._meridiem - ); - era = getParsingFlags(config).era; - if (era !== null) { - config._a[YEAR] = config._locale.erasConvertYear(era, config._a[YEAR]); - } - configFromArray(config); - checkOverflow(config); - } - function meridiemFixWrap(locale2, hour, meridiem2) { - var isPm; - if (meridiem2 == null) { - return hour; - } - if (locale2.meridiemHour != null) { - return locale2.meridiemHour(hour, meridiem2); - } else if (locale2.isPM != null) { - isPm = locale2.isPM(meridiem2); - if (isPm && hour < 12) { - hour += 12; - } - if (!isPm && hour === 12) { - hour = 0; - } - return hour; - } else { - return hour; - } - } - function configFromStringAndArray(config) { - var tempConfig, bestMoment, scoreToBeat, i, currentScore, validFormatFound, bestFormatIsValid = false, configfLen = config._f.length; - if (configfLen === 0) { - getParsingFlags(config).invalidFormat = true; - config._d = /* @__PURE__ */ new Date(NaN); - return; - } - for (i = 0; i < configfLen; i++) { - currentScore = 0; - validFormatFound = false; - tempConfig = copyConfig({}, config); - if (config._useUTC != null) { - tempConfig._useUTC = config._useUTC; - } - tempConfig._f = config._f[i]; - configFromStringAndFormat(tempConfig); - if (isValid(tempConfig)) { - validFormatFound = true; - } - currentScore += getParsingFlags(tempConfig).charsLeftOver; - currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10; - getParsingFlags(tempConfig).score = currentScore; - if (!bestFormatIsValid) { - if (scoreToBeat == null || currentScore < scoreToBeat || validFormatFound) { - scoreToBeat = currentScore; - bestMoment = tempConfig; - if (validFormatFound) { - bestFormatIsValid = true; - } - } - } else { - if (currentScore < scoreToBeat) { - scoreToBeat = currentScore; - bestMoment = tempConfig; - } - } - } - extend(config, bestMoment || tempConfig); - } - function configFromObject(config) { - if (config._d) { - return; - } - var i = normalizeObjectUnits(config._i), dayOrDate = i.day === void 0 ? i.date : i.day; - config._a = map( - [i.year, i.month, dayOrDate, i.hour, i.minute, i.second, i.millisecond], - function(obj) { - return obj && parseInt(obj, 10); - } - ); - configFromArray(config); - } - function createFromConfig(config) { - var res = new Moment(checkOverflow(prepareConfig(config))); - if (res._nextDay) { - res.add(1, "d"); - res._nextDay = void 0; - } - return res; - } - function prepareConfig(config) { - var input = config._i, format2 = config._f; - config._locale = config._locale || getLocale(config._l); - if (input === null || format2 === void 0 && input === "") { - return createInvalid({ nullInput: true }); - } - if (typeof input === "string") { - config._i = input = config._locale.preparse(input); - } - if (isMoment(input)) { - return new Moment(checkOverflow(input)); - } else if (isDate(input)) { - config._d = input; - } else if (isArray(format2)) { - configFromStringAndArray(config); - } else if (format2) { - configFromStringAndFormat(config); - } else { - configFromInput(config); - } - if (!isValid(config)) { - config._d = null; - } - return config; - } - function configFromInput(config) { - var input = config._i; - if (isUndefined(input)) { - config._d = new Date(hooks$1.now()); - } else if (isDate(input)) { - config._d = new Date(input.valueOf()); - } else if (typeof input === "string") { - configFromString(config); - } else if (isArray(input)) { - config._a = map(input.slice(0), function(obj) { - return parseInt(obj, 10); - }); - configFromArray(config); - } else if (isObject(input)) { - configFromObject(config); - } else if (isNumber(input)) { - config._d = new Date(input); - } else { - hooks$1.createFromInputFallback(config); - } - } - function createLocalOrUTC(input, format2, locale2, strict, isUTC) { - var c = {}; - if (format2 === true || format2 === false) { - strict = format2; - format2 = void 0; - } - if (locale2 === true || locale2 === false) { - strict = locale2; - locale2 = void 0; - } - if (isObject(input) && isObjectEmpty(input) || isArray(input) && input.length === 0) { - input = void 0; - } - c._isAMomentObject = true; - c._useUTC = c._isUTC = isUTC; - c._l = locale2; - c._i = input; - c._f = format2; - c._strict = strict; - return createFromConfig(c); - } - function createLocal(input, format2, locale2, strict) { - return createLocalOrUTC(input, format2, locale2, strict, false); - } - var prototypeMin = deprecate( - "moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/", - function() { - var other = createLocal.apply(null, arguments); - if (this.isValid() && other.isValid()) { - return other < this ? this : other; - } else { - return createInvalid(); - } - } - ), prototypeMax = deprecate( - "moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/", - function() { - var other = createLocal.apply(null, arguments); - if (this.isValid() && other.isValid()) { - return other > this ? this : other; - } else { - return createInvalid(); - } - } - ); - function pickBy(fn, moments) { - var res, i; - if (moments.length === 1 && isArray(moments[0])) { - moments = moments[0]; - } - if (!moments.length) { - return createLocal(); - } - res = moments[0]; - for (i = 1; i < moments.length; ++i) { - if (!moments[i].isValid() || moments[i][fn](res)) { - res = moments[i]; - } - } - return res; - } - function min() { - var args = [].slice.call(arguments, 0); - return pickBy("isBefore", args); - } - function max() { - var args = [].slice.call(arguments, 0); - return pickBy("isAfter", args); - } - var now = function() { - return Date.now ? Date.now() : +/* @__PURE__ */ new Date(); - }; - var ordering = [ - "year", - "quarter", - "month", - "week", - "day", - "hour", - "minute", - "second", - "millisecond" - ]; - function isDurationValid(m) { - var key, unitHasDecimal = false, i, orderLen = ordering.length; - for (key in m) { - if (hasOwnProp(m, key) && !(indexOf.call(ordering, key) !== -1 && (m[key] == null || !isNaN(m[key])))) { - return false; - } - } - for (i = 0; i < orderLen; ++i) { - if (m[ordering[i]]) { - if (unitHasDecimal) { - return false; - } - if (parseFloat(m[ordering[i]]) !== toInt(m[ordering[i]])) { - unitHasDecimal = true; - } - } - } - return true; - } - function isValid$1() { - return this._isValid; - } - function createInvalid$1() { - return createDuration(NaN); - } - function Duration(duration) { - var normalizedInput = normalizeObjectUnits(duration), years2 = normalizedInput.year || 0, quarters = normalizedInput.quarter || 0, months2 = normalizedInput.month || 0, weeks2 = normalizedInput.week || normalizedInput.isoWeek || 0, days2 = normalizedInput.day || 0, hours2 = normalizedInput.hour || 0, minutes2 = normalizedInput.minute || 0, seconds2 = normalizedInput.second || 0, milliseconds2 = normalizedInput.millisecond || 0; - this._isValid = isDurationValid(normalizedInput); - this._milliseconds = +milliseconds2 + seconds2 * 1e3 + // 1000 - minutes2 * 6e4 + // 1000 * 60 - hours2 * 1e3 * 60 * 60; - this._days = +days2 + weeks2 * 7; - this._months = +months2 + quarters * 3 + years2 * 12; - this._data = {}; - this._locale = getLocale(); - this._bubble(); - } - function isDuration(obj) { - return obj instanceof Duration; - } - function absRound(number) { - if (number < 0) { - return Math.round(-1 * number) * -1; - } else { - return Math.round(number); - } - } - function compareArrays(array1, array2, dontConvert) { - var len = Math.min(array1.length, array2.length), lengthDiff = Math.abs(array1.length - array2.length), diffs = 0, i; - for (i = 0; i < len; i++) { - if (toInt(array1[i]) !== toInt(array2[i])) { - diffs++; - } - } - return diffs + lengthDiff; - } - function offset(token2, separator) { - addFormatToken(token2, 0, 0, function() { - var offset2 = this.utcOffset(), sign2 = "+"; - if (offset2 < 0) { - offset2 = -offset2; - sign2 = "-"; - } - return sign2 + zeroFill(~~(offset2 / 60), 2) + separator + zeroFill(~~offset2 % 60, 2); - }); - } - offset("Z", ":"); - offset("ZZ", ""); - addRegexToken("Z", matchShortOffset); - addRegexToken("ZZ", matchShortOffset); - addParseToken(["Z", "ZZ"], function(input, array, config) { - config._useUTC = true; - config._tzm = offsetFromString(matchShortOffset, input); - }); - var chunkOffset = /([\+\-]|\d\d)/gi; - function offsetFromString(matcher, string) { - var matches = (string || "").match(matcher), chunk, parts, minutes2; - if (matches === null) { - return null; - } - chunk = matches[matches.length - 1] || []; - parts = (chunk + "").match(chunkOffset) || ["-", 0, 0]; - minutes2 = +(parts[1] * 60) + toInt(parts[2]); - return minutes2 === 0 ? 0 : parts[0] === "+" ? minutes2 : -minutes2; - } - function cloneWithOffset(input, model) { - var res, diff2; - if (model._isUTC) { - res = model.clone(); - diff2 = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf(); - res._d.setTime(res._d.valueOf() + diff2); - hooks$1.updateOffset(res, false); - return res; - } else { - return createLocal(input).local(); - } - } - function getDateOffset(m) { - return -Math.round(m._d.getTimezoneOffset()); - } - hooks$1.updateOffset = function() { - }; - function getSetOffset(input, keepLocalTime, keepMinutes) { - var offset2 = this._offset || 0, localAdjust; - if (!this.isValid()) { - return input != null ? this : NaN; - } - if (input != null) { - if (typeof input === "string") { - input = offsetFromString(matchShortOffset, input); - if (input === null) { - return this; - } - } else if (Math.abs(input) < 16 && !keepMinutes) { - input = input * 60; - } - if (!this._isUTC && keepLocalTime) { - localAdjust = getDateOffset(this); - } - this._offset = input; - this._isUTC = true; - if (localAdjust != null) { - this.add(localAdjust, "m"); - } - if (offset2 !== input) { - if (!keepLocalTime || this._changeInProgress) { - addSubtract( - this, - createDuration(input - offset2, "m"), - 1, - false - ); - } else if (!this._changeInProgress) { - this._changeInProgress = true; - hooks$1.updateOffset(this, true); - this._changeInProgress = null; - } - } - return this; - } else { - return this._isUTC ? offset2 : getDateOffset(this); - } - } - function getSetZone(input, keepLocalTime) { - if (input != null) { - if (typeof input !== "string") { - input = -input; - } - this.utcOffset(input, keepLocalTime); - return this; - } else { - return -this.utcOffset(); - } - } - function setOffsetToUTC(keepLocalTime) { - return this.utcOffset(0, keepLocalTime); - } - function setOffsetToLocal(keepLocalTime) { - if (this._isUTC) { - this.utcOffset(0, keepLocalTime); - this._isUTC = false; - if (keepLocalTime) { - this.subtract(getDateOffset(this), "m"); - } - } - return this; - } - function setOffsetToParsedOffset() { - if (this._tzm != null) { - this.utcOffset(this._tzm, false, true); - } else if (typeof this._i === "string") { - var tZone = offsetFromString(matchOffset, this._i); - if (tZone != null) { - this.utcOffset(tZone); - } else { - this.utcOffset(0, true); - } - } - return this; - } - function hasAlignedHourOffset(input) { - if (!this.isValid()) { - return false; - } - input = input ? createLocal(input).utcOffset() : 0; - return (this.utcOffset() - input) % 60 === 0; - } - function isDaylightSavingTime() { - return this.utcOffset() > this.clone().month(0).utcOffset() || this.utcOffset() > this.clone().month(5).utcOffset(); - } - function isDaylightSavingTimeShifted() { - if (!isUndefined(this._isDSTShifted)) { - return this._isDSTShifted; - } - var c = {}, other; - copyConfig(c, this); - c = prepareConfig(c); - if (c._a) { - other = c._isUTC ? createUTC(c._a) : createLocal(c._a); - this._isDSTShifted = this.isValid() && compareArrays(c._a, other.toArray()) > 0; - } else { - this._isDSTShifted = false; - } - return this._isDSTShifted; - } - function isLocal() { - return this.isValid() ? !this._isUTC : false; - } - function isUtcOffset() { - return this.isValid() ? this._isUTC : false; - } - function isUtc() { - return this.isValid() ? this._isUTC && this._offset === 0 : false; - } - var aspNetRegex = /^(-|\+)?(?:(\d*)[. ])?(\d+):(\d+)(?::(\d+)(\.\d*)?)?$/, isoRegex = /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/; - function createDuration(input, key) { - var duration = input, match = null, sign2, ret, diffRes; - if (isDuration(input)) { - duration = { - ms: input._milliseconds, - d: input._days, - M: input._months - }; - } else if (isNumber(input) || !isNaN(+input)) { - duration = {}; - if (key) { - duration[key] = +input; - } else { - duration.milliseconds = +input; - } - } else if (match = aspNetRegex.exec(input)) { - sign2 = match[1] === "-" ? -1 : 1; - duration = { - y: 0, - d: toInt(match[DATE]) * sign2, - h: toInt(match[HOUR]) * sign2, - m: toInt(match[MINUTE]) * sign2, - s: toInt(match[SECOND]) * sign2, - ms: toInt(absRound(match[MILLISECOND] * 1e3)) * sign2 - // the millisecond decimal point is included in the match - }; - } else if (match = isoRegex.exec(input)) { - sign2 = match[1] === "-" ? -1 : 1; - duration = { - y: parseIso(match[2], sign2), - M: parseIso(match[3], sign2), - w: parseIso(match[4], sign2), - d: parseIso(match[5], sign2), - h: parseIso(match[6], sign2), - m: parseIso(match[7], sign2), - s: parseIso(match[8], sign2) - }; - } else if (duration == null) { - duration = {}; - } else if (typeof duration === "object" && ("from" in duration || "to" in duration)) { - diffRes = momentsDifference( - createLocal(duration.from), - createLocal(duration.to) - ); - duration = {}; - duration.ms = diffRes.milliseconds; - duration.M = diffRes.months; - } - ret = new Duration(duration); - if (isDuration(input) && hasOwnProp(input, "_locale")) { - ret._locale = input._locale; - } - if (isDuration(input) && hasOwnProp(input, "_isValid")) { - ret._isValid = input._isValid; - } - return ret; - } - createDuration.fn = Duration.prototype; - createDuration.invalid = createInvalid$1; - function parseIso(inp, sign2) { - var res = inp && parseFloat(inp.replace(",", ".")); - return (isNaN(res) ? 0 : res) * sign2; - } - function positiveMomentsDifference(base, other) { - var res = {}; - res.months = other.month() - base.month() + (other.year() - base.year()) * 12; - if (base.clone().add(res.months, "M").isAfter(other)) { - --res.months; - } - res.milliseconds = +other - +base.clone().add(res.months, "M"); - return res; - } - function momentsDifference(base, other) { - var res; - if (!(base.isValid() && other.isValid())) { - return { milliseconds: 0, months: 0 }; - } - other = cloneWithOffset(other, base); - if (base.isBefore(other)) { - res = positiveMomentsDifference(base, other); - } else { - res = positiveMomentsDifference(other, base); - res.milliseconds = -res.milliseconds; - res.months = -res.months; - } - return res; - } - function createAdder(direction, name2) { - return function(val, period) { - var dur, tmp; - if (period !== null && !isNaN(+period)) { - deprecateSimple( - name2, - "moment()." + name2 + "(period, number) is deprecated. Please use moment()." + name2 + "(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info." - ); - tmp = val; - val = period; - period = tmp; - } - dur = createDuration(val, period); - addSubtract(this, dur, direction); - return this; - }; - } - function addSubtract(mom, duration, isAdding, updateOffset) { - var milliseconds2 = duration._milliseconds, days2 = absRound(duration._days), months2 = absRound(duration._months); - if (!mom.isValid()) { - return; - } - updateOffset = updateOffset == null ? true : updateOffset; - if (months2) { - setMonth(mom, get(mom, "Month") + months2 * isAdding); - } - if (days2) { - set$1(mom, "Date", get(mom, "Date") + days2 * isAdding); - } - if (milliseconds2) { - mom._d.setTime(mom._d.valueOf() + milliseconds2 * isAdding); - } - if (updateOffset) { - hooks$1.updateOffset(mom, days2 || months2); - } - } - var add = createAdder(1, "add"), subtract = createAdder(-1, "subtract"); - function isString(input) { - return typeof input === "string" || input instanceof String; - } - function isMomentInput(input) { - return isMoment(input) || isDate(input) || isString(input) || isNumber(input) || isNumberOrStringArray(input) || isMomentInputObject(input) || input === null || input === void 0; - } - function isMomentInputObject(input) { - var objectTest = isObject(input) && !isObjectEmpty(input), propertyTest = false, properties = [ - "years", - "year", - "y", - "months", - "month", - "M", - "days", - "day", - "d", - "dates", - "date", - "D", - "hours", - "hour", - "h", - "minutes", - "minute", - "m", - "seconds", - "second", - "s", - "milliseconds", - "millisecond", - "ms" - ], i, property, propertyLen = properties.length; - for (i = 0; i < propertyLen; i += 1) { - property = properties[i]; - propertyTest = propertyTest || hasOwnProp(input, property); - } - return objectTest && propertyTest; - } - function isNumberOrStringArray(input) { - var arrayTest = isArray(input), dataTypeTest = false; - if (arrayTest) { - dataTypeTest = input.filter(function(item) { - return !isNumber(item) && isString(input); - }).length === 0; - } - return arrayTest && dataTypeTest; - } - function isCalendarSpec(input) { - var objectTest = isObject(input) && !isObjectEmpty(input), propertyTest = false, properties = [ - "sameDay", - "nextDay", - "lastDay", - "nextWeek", - "lastWeek", - "sameElse" - ], i, property; - for (i = 0; i < properties.length; i += 1) { - property = properties[i]; - propertyTest = propertyTest || hasOwnProp(input, property); - } - return objectTest && propertyTest; - } - function getCalendarFormat(myMoment, now2) { - var diff2 = myMoment.diff(now2, "days", true); - return diff2 < -6 ? "sameElse" : diff2 < -1 ? "lastWeek" : diff2 < 0 ? "lastDay" : diff2 < 1 ? "sameDay" : diff2 < 2 ? "nextDay" : diff2 < 7 ? "nextWeek" : "sameElse"; - } - function calendar$1(time, formats) { - if (arguments.length === 1) { - if (!arguments[0]) { - time = void 0; - formats = void 0; - } else if (isMomentInput(arguments[0])) { - time = arguments[0]; - formats = void 0; - } else if (isCalendarSpec(arguments[0])) { - formats = arguments[0]; - time = void 0; - } - } - var now2 = time || createLocal(), sod = cloneWithOffset(now2, this).startOf("day"), format2 = hooks$1.calendarFormat(this, sod) || "sameElse", output = formats && (isFunction(formats[format2]) ? formats[format2].call(this, now2) : formats[format2]); - return this.format( - output || this.localeData().calendar(format2, this, createLocal(now2)) - ); - } - function clone() { - return new Moment(this); - } - function isAfter(input, units) { - var localInput = isMoment(input) ? input : createLocal(input); - if (!(this.isValid() && localInput.isValid())) { - return false; - } - units = normalizeUnits(units) || "millisecond"; - if (units === "millisecond") { - return this.valueOf() > localInput.valueOf(); - } else { - return localInput.valueOf() < this.clone().startOf(units).valueOf(); - } - } - function isBefore(input, units) { - var localInput = isMoment(input) ? input : createLocal(input); - if (!(this.isValid() && localInput.isValid())) { - return false; - } - units = normalizeUnits(units) || "millisecond"; - if (units === "millisecond") { - return this.valueOf() < localInput.valueOf(); - } else { - return this.clone().endOf(units).valueOf() < localInput.valueOf(); - } - } - function isBetween(from2, to2, units, inclusivity) { - var localFrom = isMoment(from2) ? from2 : createLocal(from2), localTo = isMoment(to2) ? to2 : createLocal(to2); - if (!(this.isValid() && localFrom.isValid() && localTo.isValid())) { - return false; - } - inclusivity = inclusivity || "()"; - return (inclusivity[0] === "(" ? this.isAfter(localFrom, units) : !this.isBefore(localFrom, units)) && (inclusivity[1] === ")" ? this.isBefore(localTo, units) : !this.isAfter(localTo, units)); - } - function isSame(input, units) { - var localInput = isMoment(input) ? input : createLocal(input), inputMs; - if (!(this.isValid() && localInput.isValid())) { - return false; - } - units = normalizeUnits(units) || "millisecond"; - if (units === "millisecond") { - return this.valueOf() === localInput.valueOf(); - } else { - inputMs = localInput.valueOf(); - return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf(); - } - } - function isSameOrAfter(input, units) { - return this.isSame(input, units) || this.isAfter(input, units); - } - function isSameOrBefore(input, units) { - return this.isSame(input, units) || this.isBefore(input, units); - } - function diff(input, units, asFloat) { - var that, zoneDelta, output; - if (!this.isValid()) { - return NaN; - } - that = cloneWithOffset(input, this); - if (!that.isValid()) { - return NaN; - } - zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4; - units = normalizeUnits(units); - switch (units) { - case "year": - output = monthDiff(this, that) / 12; - break; - case "month": - output = monthDiff(this, that); - break; - case "quarter": - output = monthDiff(this, that) / 3; - break; - case "second": - output = (this - that) / 1e3; - break; - // 1000 - case "minute": - output = (this - that) / 6e4; - break; - // 1000 * 60 - case "hour": - output = (this - that) / 36e5; - break; - // 1000 * 60 * 60 - case "day": - output = (this - that - zoneDelta) / 864e5; - break; - // 1000 * 60 * 60 * 24, negate dst - case "week": - output = (this - that - zoneDelta) / 6048e5; - break; - // 1000 * 60 * 60 * 24 * 7, negate dst - default: - output = this - that; - } - return asFloat ? output : absFloor(output); - } - function monthDiff(a, b) { - if (a.date() < b.date()) { - return -monthDiff(b, a); - } - var wholeMonthDiff = (b.year() - a.year()) * 12 + (b.month() - a.month()), anchor = a.clone().add(wholeMonthDiff, "months"), anchor2, adjust; - if (b - anchor < 0) { - anchor2 = a.clone().add(wholeMonthDiff - 1, "months"); - adjust = (b - anchor) / (anchor - anchor2); - } else { - anchor2 = a.clone().add(wholeMonthDiff + 1, "months"); - adjust = (b - anchor) / (anchor2 - anchor); - } - return -(wholeMonthDiff + adjust) || 0; - } - hooks$1.defaultFormat = "YYYY-MM-DDTHH:mm:ssZ"; - hooks$1.defaultFormatUtc = "YYYY-MM-DDTHH:mm:ss[Z]"; - function toString() { - return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ"); - } - function toISOString(keepOffset) { - if (!this.isValid()) { - return null; - } - var utc = keepOffset !== true, m = utc ? this.clone().utc() : this; - if (m.year() < 0 || m.year() > 9999) { - return formatMoment( - m, - utc ? "YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]" : "YYYYYY-MM-DD[T]HH:mm:ss.SSSZ" - ); - } - if (isFunction(Date.prototype.toISOString)) { - if (utc) { - return this.toDate().toISOString(); - } else { - return new Date(this.valueOf() + this.utcOffset() * 60 * 1e3).toISOString().replace("Z", formatMoment(m, "Z")); - } - } - return formatMoment( - m, - utc ? "YYYY-MM-DD[T]HH:mm:ss.SSS[Z]" : "YYYY-MM-DD[T]HH:mm:ss.SSSZ" - ); - } - function inspect() { - if (!this.isValid()) { - return "moment.invalid(/* " + this._i + " */)"; - } - var func = "moment", zone = "", prefix, year, datetime, suffix; - if (!this.isLocal()) { - func = this.utcOffset() === 0 ? "moment.utc" : "moment.parseZone"; - zone = "Z"; - } - prefix = "[" + func + '("]'; - year = 0 <= this.year() && this.year() <= 9999 ? "YYYY" : "YYYYYY"; - datetime = "-MM-DD[T]HH:mm:ss.SSS"; - suffix = zone + '[")]'; - return this.format(prefix + year + datetime + suffix); - } - function format(inputString) { - if (!inputString) { - inputString = this.isUtc() ? hooks$1.defaultFormatUtc : hooks$1.defaultFormat; - } - var output = formatMoment(this, inputString); - return this.localeData().postformat(output); - } - function from(time, withoutSuffix) { - if (this.isValid() && (isMoment(time) && time.isValid() || createLocal(time).isValid())) { - return createDuration({ to: this, from: time }).locale(this.locale()).humanize(!withoutSuffix); - } else { - return this.localeData().invalidDate(); - } - } - function fromNow(withoutSuffix) { - return this.from(createLocal(), withoutSuffix); - } - function to(time, withoutSuffix) { - if (this.isValid() && (isMoment(time) && time.isValid() || createLocal(time).isValid())) { - return createDuration({ from: this, to: time }).locale(this.locale()).humanize(!withoutSuffix); - } else { - return this.localeData().invalidDate(); - } - } - function toNow(withoutSuffix) { - return this.to(createLocal(), withoutSuffix); - } - function locale(key) { - var newLocaleData; - if (key === void 0) { - return this._locale._abbr; - } else { - newLocaleData = getLocale(key); - if (newLocaleData != null) { - this._locale = newLocaleData; - } - return this; - } - } - var lang = deprecate( - "moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.", - function(key) { - if (key === void 0) { - return this.localeData(); - } else { - return this.locale(key); - } - } - ); - function localeData() { - return this._locale; - } - var MS_PER_SECOND = 1e3, MS_PER_MINUTE = 60 * MS_PER_SECOND, MS_PER_HOUR = 60 * MS_PER_MINUTE, MS_PER_400_YEARS = (365 * 400 + 97) * 24 * MS_PER_HOUR; - function mod$1(dividend, divisor) { - return (dividend % divisor + divisor) % divisor; - } - function localStartOfDate(y, m, d) { - if (y < 100 && y >= 0) { - return new Date(y + 400, m, d) - MS_PER_400_YEARS; - } else { - return new Date(y, m, d).valueOf(); - } - } - function utcStartOfDate(y, m, d) { - if (y < 100 && y >= 0) { - return Date.UTC(y + 400, m, d) - MS_PER_400_YEARS; - } else { - return Date.UTC(y, m, d); - } - } - function startOf(units) { - var time, startOfDate; - units = normalizeUnits(units); - if (units === void 0 || units === "millisecond" || !this.isValid()) { - return this; - } - startOfDate = this._isUTC ? utcStartOfDate : localStartOfDate; - switch (units) { - case "year": - time = startOfDate(this.year(), 0, 1); - break; - case "quarter": - time = startOfDate( - this.year(), - this.month() - this.month() % 3, - 1 - ); - break; - case "month": - time = startOfDate(this.year(), this.month(), 1); - break; - case "week": - time = startOfDate( - this.year(), - this.month(), - this.date() - this.weekday() - ); - break; - case "isoWeek": - time = startOfDate( - this.year(), - this.month(), - this.date() - (this.isoWeekday() - 1) - ); - break; - case "day": - case "date": - time = startOfDate(this.year(), this.month(), this.date()); - break; - case "hour": - time = this._d.valueOf(); - time -= mod$1( - time + (this._isUTC ? 0 : this.utcOffset() * MS_PER_MINUTE), - MS_PER_HOUR - ); - break; - case "minute": - time = this._d.valueOf(); - time -= mod$1(time, MS_PER_MINUTE); - break; - case "second": - time = this._d.valueOf(); - time -= mod$1(time, MS_PER_SECOND); - break; - } - this._d.setTime(time); - hooks$1.updateOffset(this, true); - return this; - } - function endOf(units) { - var time, startOfDate; - units = normalizeUnits(units); - if (units === void 0 || units === "millisecond" || !this.isValid()) { - return this; - } - startOfDate = this._isUTC ? utcStartOfDate : localStartOfDate; - switch (units) { - case "year": - time = startOfDate(this.year() + 1, 0, 1) - 1; - break; - case "quarter": - time = startOfDate( - this.year(), - this.month() - this.month() % 3 + 3, - 1 - ) - 1; - break; - case "month": - time = startOfDate(this.year(), this.month() + 1, 1) - 1; - break; - case "week": - time = startOfDate( - this.year(), - this.month(), - this.date() - this.weekday() + 7 - ) - 1; - break; - case "isoWeek": - time = startOfDate( - this.year(), - this.month(), - this.date() - (this.isoWeekday() - 1) + 7 - ) - 1; - break; - case "day": - case "date": - time = startOfDate(this.year(), this.month(), this.date() + 1) - 1; - break; - case "hour": - time = this._d.valueOf(); - time += MS_PER_HOUR - mod$1( - time + (this._isUTC ? 0 : this.utcOffset() * MS_PER_MINUTE), - MS_PER_HOUR - ) - 1; - break; - case "minute": - time = this._d.valueOf(); - time += MS_PER_MINUTE - mod$1(time, MS_PER_MINUTE) - 1; - break; - case "second": - time = this._d.valueOf(); - time += MS_PER_SECOND - mod$1(time, MS_PER_SECOND) - 1; - break; - } - this._d.setTime(time); - hooks$1.updateOffset(this, true); - return this; - } - function valueOf() { - return this._d.valueOf() - (this._offset || 0) * 6e4; - } - function unix() { - return Math.floor(this.valueOf() / 1e3); - } - function toDate() { - return new Date(this.valueOf()); - } - function toArray() { - var m = this; - return [ - m.year(), - m.month(), - m.date(), - m.hour(), - m.minute(), - m.second(), - m.millisecond() - ]; - } - function toObject() { - var m = this; - return { - years: m.year(), - months: m.month(), - date: m.date(), - hours: m.hours(), - minutes: m.minutes(), - seconds: m.seconds(), - milliseconds: m.milliseconds() - }; - } - function toJSON() { - return this.isValid() ? this.toISOString() : null; - } - function isValid$2() { - return isValid(this); - } - function parsingFlags() { - return extend({}, getParsingFlags(this)); - } - function invalidAt() { - return getParsingFlags(this).overflow; - } - function creationData() { - return { - input: this._i, - format: this._f, - locale: this._locale, - isUTC: this._isUTC, - strict: this._strict - }; - } - addFormatToken("N", 0, 0, "eraAbbr"); - addFormatToken("NN", 0, 0, "eraAbbr"); - addFormatToken("NNN", 0, 0, "eraAbbr"); - addFormatToken("NNNN", 0, 0, "eraName"); - addFormatToken("NNNNN", 0, 0, "eraNarrow"); - addFormatToken("y", ["y", 1], "yo", "eraYear"); - addFormatToken("y", ["yy", 2], 0, "eraYear"); - addFormatToken("y", ["yyy", 3], 0, "eraYear"); - addFormatToken("y", ["yyyy", 4], 0, "eraYear"); - addRegexToken("N", matchEraAbbr); - addRegexToken("NN", matchEraAbbr); - addRegexToken("NNN", matchEraAbbr); - addRegexToken("NNNN", matchEraName); - addRegexToken("NNNNN", matchEraNarrow); - addParseToken( - ["N", "NN", "NNN", "NNNN", "NNNNN"], - function(input, array, config, token2) { - var era = config._locale.erasParse(input, token2, config._strict); - if (era) { - getParsingFlags(config).era = era; - } else { - getParsingFlags(config).invalidEra = input; - } - } - ); - addRegexToken("y", matchUnsigned); - addRegexToken("yy", matchUnsigned); - addRegexToken("yyy", matchUnsigned); - addRegexToken("yyyy", matchUnsigned); - addRegexToken("yo", matchEraYearOrdinal); - addParseToken(["y", "yy", "yyy", "yyyy"], YEAR); - addParseToken(["yo"], function(input, array, config, token2) { - var match; - if (config._locale._eraYearOrdinalRegex) { - match = input.match(config._locale._eraYearOrdinalRegex); - } - if (config._locale.eraYearOrdinalParse) { - array[YEAR] = config._locale.eraYearOrdinalParse(input, match); - } else { - array[YEAR] = parseInt(input, 10); - } - }); - function localeEras(m, format2) { - var i, l, date, eras = this._eras || getLocale("en")._eras; - for (i = 0, l = eras.length; i < l; ++i) { - switch (typeof eras[i].since) { - case "string": - date = hooks$1(eras[i].since).startOf("day"); - eras[i].since = date.valueOf(); - break; - } - switch (typeof eras[i].until) { - case "undefined": - eras[i].until = Infinity; - break; - case "string": - date = hooks$1(eras[i].until).startOf("day").valueOf(); - eras[i].until = date.valueOf(); - break; - } - } - return eras; - } - function localeErasParse(eraName, format2, strict) { - var i, l, eras = this.eras(), name2, abbr, narrow; - eraName = eraName.toUpperCase(); - for (i = 0, l = eras.length; i < l; ++i) { - name2 = eras[i].name.toUpperCase(); - abbr = eras[i].abbr.toUpperCase(); - narrow = eras[i].narrow.toUpperCase(); - if (strict) { - switch (format2) { - case "N": - case "NN": - case "NNN": - if (abbr === eraName) { - return eras[i]; - } - break; - case "NNNN": - if (name2 === eraName) { - return eras[i]; - } - break; - case "NNNNN": - if (narrow === eraName) { - return eras[i]; - } - break; - } - } else if ([name2, abbr, narrow].indexOf(eraName) >= 0) { - return eras[i]; - } - } - } - function localeErasConvertYear(era, year) { - var dir = era.since <= era.until ? 1 : -1; - if (year === void 0) { - return hooks$1(era.since).year(); - } else { - return hooks$1(era.since).year() + (year - era.offset) * dir; - } - } - function getEraName() { - var i, l, val, eras = this.localeData().eras(); - for (i = 0, l = eras.length; i < l; ++i) { - val = this.clone().startOf("day").valueOf(); - if (eras[i].since <= val && val <= eras[i].until) { - return eras[i].name; - } - if (eras[i].until <= val && val <= eras[i].since) { - return eras[i].name; - } - } - return ""; - } - function getEraNarrow() { - var i, l, val, eras = this.localeData().eras(); - for (i = 0, l = eras.length; i < l; ++i) { - val = this.clone().startOf("day").valueOf(); - if (eras[i].since <= val && val <= eras[i].until) { - return eras[i].narrow; - } - if (eras[i].until <= val && val <= eras[i].since) { - return eras[i].narrow; - } - } - return ""; - } - function getEraAbbr() { - var i, l, val, eras = this.localeData().eras(); - for (i = 0, l = eras.length; i < l; ++i) { - val = this.clone().startOf("day").valueOf(); - if (eras[i].since <= val && val <= eras[i].until) { - return eras[i].abbr; - } - if (eras[i].until <= val && val <= eras[i].since) { - return eras[i].abbr; - } - } - return ""; - } - function getEraYear() { - var i, l, dir, val, eras = this.localeData().eras(); - for (i = 0, l = eras.length; i < l; ++i) { - dir = eras[i].since <= eras[i].until ? 1 : -1; - val = this.clone().startOf("day").valueOf(); - if (eras[i].since <= val && val <= eras[i].until || eras[i].until <= val && val <= eras[i].since) { - return (this.year() - hooks$1(eras[i].since).year()) * dir + eras[i].offset; - } - } - return this.year(); - } - function erasNameRegex(isStrict) { - if (!hasOwnProp(this, "_erasNameRegex")) { - computeErasParse.call(this); - } - return isStrict ? this._erasNameRegex : this._erasRegex; - } - function erasAbbrRegex(isStrict) { - if (!hasOwnProp(this, "_erasAbbrRegex")) { - computeErasParse.call(this); - } - return isStrict ? this._erasAbbrRegex : this._erasRegex; - } - function erasNarrowRegex(isStrict) { - if (!hasOwnProp(this, "_erasNarrowRegex")) { - computeErasParse.call(this); - } - return isStrict ? this._erasNarrowRegex : this._erasRegex; - } - function matchEraAbbr(isStrict, locale2) { - return locale2.erasAbbrRegex(isStrict); - } - function matchEraName(isStrict, locale2) { - return locale2.erasNameRegex(isStrict); - } - function matchEraNarrow(isStrict, locale2) { - return locale2.erasNarrowRegex(isStrict); - } - function matchEraYearOrdinal(isStrict, locale2) { - return locale2._eraYearOrdinalRegex || matchUnsigned; - } - function computeErasParse() { - var abbrPieces = [], namePieces = [], narrowPieces = [], mixedPieces = [], i, l, erasName, erasAbbr, erasNarrow, eras = this.eras(); - for (i = 0, l = eras.length; i < l; ++i) { - erasName = regexEscape(eras[i].name); - erasAbbr = regexEscape(eras[i].abbr); - erasNarrow = regexEscape(eras[i].narrow); - namePieces.push(erasName); - abbrPieces.push(erasAbbr); - narrowPieces.push(erasNarrow); - mixedPieces.push(erasName); - mixedPieces.push(erasAbbr); - mixedPieces.push(erasNarrow); - } - this._erasRegex = new RegExp("^(" + mixedPieces.join("|") + ")", "i"); - this._erasNameRegex = new RegExp("^(" + namePieces.join("|") + ")", "i"); - this._erasAbbrRegex = new RegExp("^(" + abbrPieces.join("|") + ")", "i"); - this._erasNarrowRegex = new RegExp( - "^(" + narrowPieces.join("|") + ")", - "i" - ); - } - addFormatToken(0, ["gg", 2], 0, function() { - return this.weekYear() % 100; - }); - addFormatToken(0, ["GG", 2], 0, function() { - return this.isoWeekYear() % 100; - }); - function addWeekYearFormatToken(token2, getter) { - addFormatToken(0, [token2, token2.length], 0, getter); - } - addWeekYearFormatToken("gggg", "weekYear"); - addWeekYearFormatToken("ggggg", "weekYear"); - addWeekYearFormatToken("GGGG", "isoWeekYear"); - addWeekYearFormatToken("GGGGG", "isoWeekYear"); - addRegexToken("G", matchSigned); - addRegexToken("g", matchSigned); - addRegexToken("GG", match1to2, match2); - addRegexToken("gg", match1to2, match2); - addRegexToken("GGGG", match1to4, match4); - addRegexToken("gggg", match1to4, match4); - addRegexToken("GGGGG", match1to6, match6); - addRegexToken("ggggg", match1to6, match6); - addWeekParseToken( - ["gggg", "ggggg", "GGGG", "GGGGG"], - function(input, week, config, token2) { - week[token2.substr(0, 2)] = toInt(input); - } - ); - addWeekParseToken(["gg", "GG"], function(input, week, config, token2) { - week[token2] = hooks$1.parseTwoDigitYear(input); - }); - function getSetWeekYear(input) { - return getSetWeekYearHelper.call( - this, - input, - this.week(), - this.weekday() + this.localeData()._week.dow, - this.localeData()._week.dow, - this.localeData()._week.doy - ); - } - function getSetISOWeekYear(input) { - return getSetWeekYearHelper.call( - this, - input, - this.isoWeek(), - this.isoWeekday(), - 1, - 4 - ); - } - function getISOWeeksInYear() { - return weeksInYear(this.year(), 1, 4); - } - function getISOWeeksInISOWeekYear() { - return weeksInYear(this.isoWeekYear(), 1, 4); - } - function getWeeksInYear() { - var weekInfo = this.localeData()._week; - return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); - } - function getWeeksInWeekYear() { - var weekInfo = this.localeData()._week; - return weeksInYear(this.weekYear(), weekInfo.dow, weekInfo.doy); - } - function getSetWeekYearHelper(input, week, weekday, dow, doy) { - var weeksTarget; - if (input == null) { - return weekOfYear(this, dow, doy).year; - } else { - weeksTarget = weeksInYear(input, dow, doy); - if (week > weeksTarget) { - week = weeksTarget; - } - return setWeekAll.call(this, input, week, weekday, dow, doy); - } - } - function setWeekAll(weekYear, week, weekday, dow, doy) { - var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy), date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear); - this.year(date.getUTCFullYear()); - this.month(date.getUTCMonth()); - this.date(date.getUTCDate()); - return this; - } - addFormatToken("Q", 0, "Qo", "quarter"); - addRegexToken("Q", match1); - addParseToken("Q", function(input, array) { - array[MONTH] = (toInt(input) - 1) * 3; - }); - function getSetQuarter(input) { - return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); - } - addFormatToken("D", ["DD", 2], "Do", "date"); - addRegexToken("D", match1to2, match1to2NoLeadingZero); - addRegexToken("DD", match1to2, match2); - addRegexToken("Do", function(isStrict, locale2) { - return isStrict ? locale2._dayOfMonthOrdinalParse || locale2._ordinalParse : locale2._dayOfMonthOrdinalParseLenient; - }); - addParseToken(["D", "DD"], DATE); - addParseToken("Do", function(input, array) { - array[DATE] = toInt(input.match(match1to2)[0]); - }); - var getSetDayOfMonth = makeGetSet("Date", true); - addFormatToken("DDD", ["DDDD", 3], "DDDo", "dayOfYear"); - addRegexToken("DDD", match1to3); - addRegexToken("DDDD", match3); - addParseToken(["DDD", "DDDD"], function(input, array, config) { - config._dayOfYear = toInt(input); - }); - function getSetDayOfYear(input) { - var dayOfYear = Math.round( - (this.clone().startOf("day") - this.clone().startOf("year")) / 864e5 - ) + 1; - return input == null ? dayOfYear : this.add(input - dayOfYear, "d"); - } - addFormatToken("m", ["mm", 2], 0, "minute"); - addRegexToken("m", match1to2, match1to2HasZero); - addRegexToken("mm", match1to2, match2); - addParseToken(["m", "mm"], MINUTE); - var getSetMinute = makeGetSet("Minutes", false); - addFormatToken("s", ["ss", 2], 0, "second"); - addRegexToken("s", match1to2, match1to2HasZero); - addRegexToken("ss", match1to2, match2); - addParseToken(["s", "ss"], SECOND); - var getSetSecond = makeGetSet("Seconds", false); - addFormatToken("S", 0, 0, function() { - return ~~(this.millisecond() / 100); - }); - addFormatToken(0, ["SS", 2], 0, function() { - return ~~(this.millisecond() / 10); - }); - addFormatToken(0, ["SSS", 3], 0, "millisecond"); - addFormatToken(0, ["SSSS", 4], 0, function() { - return this.millisecond() * 10; - }); - addFormatToken(0, ["SSSSS", 5], 0, function() { - return this.millisecond() * 100; - }); - addFormatToken(0, ["SSSSSS", 6], 0, function() { - return this.millisecond() * 1e3; - }); - addFormatToken(0, ["SSSSSSS", 7], 0, function() { - return this.millisecond() * 1e4; - }); - addFormatToken(0, ["SSSSSSSS", 8], 0, function() { - return this.millisecond() * 1e5; - }); - addFormatToken(0, ["SSSSSSSSS", 9], 0, function() { - return this.millisecond() * 1e6; - }); - addRegexToken("S", match1to3, match1); - addRegexToken("SS", match1to3, match2); - addRegexToken("SSS", match1to3, match3); - var token, getSetMillisecond; - for (token = "SSSS"; token.length <= 9; token += "S") { - addRegexToken(token, matchUnsigned); - } - function parseMs(input, array) { - array[MILLISECOND] = toInt(("0." + input) * 1e3); - } - for (token = "S"; token.length <= 9; token += "S") { - addParseToken(token, parseMs); - } - getSetMillisecond = makeGetSet("Milliseconds", false); - addFormatToken("z", 0, 0, "zoneAbbr"); - addFormatToken("zz", 0, 0, "zoneName"); - function getZoneAbbr() { - return this._isUTC ? "UTC" : ""; - } - function getZoneName() { - return this._isUTC ? "Coordinated Universal Time" : ""; - } - var proto = Moment.prototype; - proto.add = add; - proto.calendar = calendar$1; - proto.clone = clone; - proto.diff = diff; - proto.endOf = endOf; - proto.format = format; - proto.from = from; - proto.fromNow = fromNow; - proto.to = to; - proto.toNow = toNow; - proto.get = stringGet; - proto.invalidAt = invalidAt; - proto.isAfter = isAfter; - proto.isBefore = isBefore; - proto.isBetween = isBetween; - proto.isSame = isSame; - proto.isSameOrAfter = isSameOrAfter; - proto.isSameOrBefore = isSameOrBefore; - proto.isValid = isValid$2; - proto.lang = lang; - proto.locale = locale; - proto.localeData = localeData; - proto.max = prototypeMax; - proto.min = prototypeMin; - proto.parsingFlags = parsingFlags; - proto.set = stringSet; - proto.startOf = startOf; - proto.subtract = subtract; - proto.toArray = toArray; - proto.toObject = toObject; - proto.toDate = toDate; - proto.toISOString = toISOString; - proto.inspect = inspect; - if (typeof Symbol !== "undefined" && Symbol.for != null) { - proto[Symbol.for("nodejs.util.inspect.custom")] = function() { - return "Moment<" + this.format() + ">"; - }; - } - proto.toJSON = toJSON; - proto.toString = toString; - proto.unix = unix; - proto.valueOf = valueOf; - proto.creationData = creationData; - proto.eraName = getEraName; - proto.eraNarrow = getEraNarrow; - proto.eraAbbr = getEraAbbr; - proto.eraYear = getEraYear; - proto.year = getSetYear; - proto.isLeapYear = getIsLeapYear; - proto.weekYear = getSetWeekYear; - proto.isoWeekYear = getSetISOWeekYear; - proto.quarter = proto.quarters = getSetQuarter; - proto.month = getSetMonth; - proto.daysInMonth = getDaysInMonth; - proto.week = proto.weeks = getSetWeek; - proto.isoWeek = proto.isoWeeks = getSetISOWeek; - proto.weeksInYear = getWeeksInYear; - proto.weeksInWeekYear = getWeeksInWeekYear; - proto.isoWeeksInYear = getISOWeeksInYear; - proto.isoWeeksInISOWeekYear = getISOWeeksInISOWeekYear; - proto.date = getSetDayOfMonth; - proto.day = proto.days = getSetDayOfWeek; - proto.weekday = getSetLocaleDayOfWeek; - proto.isoWeekday = getSetISODayOfWeek; - proto.dayOfYear = getSetDayOfYear; - proto.hour = proto.hours = getSetHour; - proto.minute = proto.minutes = getSetMinute; - proto.second = proto.seconds = getSetSecond; - proto.millisecond = proto.milliseconds = getSetMillisecond; - proto.utcOffset = getSetOffset; - proto.utc = setOffsetToUTC; - proto.local = setOffsetToLocal; - proto.parseZone = setOffsetToParsedOffset; - proto.hasAlignedHourOffset = hasAlignedHourOffset; - proto.isDST = isDaylightSavingTime; - proto.isLocal = isLocal; - proto.isUtcOffset = isUtcOffset; - proto.isUtc = isUtc; - proto.isUTC = isUtc; - proto.zoneAbbr = getZoneAbbr; - proto.zoneName = getZoneName; - proto.dates = deprecate( - "dates accessor is deprecated. Use date instead.", - getSetDayOfMonth - ); - proto.months = deprecate( - "months accessor is deprecated. Use month instead", - getSetMonth - ); - proto.years = deprecate( - "years accessor is deprecated. Use year instead", - getSetYear - ); - proto.zone = deprecate( - "moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/", - getSetZone - ); - proto.isDSTShifted = deprecate( - "isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information", - isDaylightSavingTimeShifted - ); - function createUnix(input) { - return createLocal(input * 1e3); - } - function createInZone() { - return createLocal.apply(null, arguments).parseZone(); - } - function preParsePostFormat(string) { - return string; - } - var proto$1 = Locale.prototype; - proto$1.calendar = calendar; - proto$1.longDateFormat = longDateFormat; - proto$1.invalidDate = invalidDate; - proto$1.ordinal = ordinal; - proto$1.preparse = preParsePostFormat; - proto$1.postformat = preParsePostFormat; - proto$1.relativeTime = relativeTime; - proto$1.pastFuture = pastFuture; - proto$1.set = set; - proto$1.eras = localeEras; - proto$1.erasParse = localeErasParse; - proto$1.erasConvertYear = localeErasConvertYear; - proto$1.erasAbbrRegex = erasAbbrRegex; - proto$1.erasNameRegex = erasNameRegex; - proto$1.erasNarrowRegex = erasNarrowRegex; - proto$1.months = localeMonths; - proto$1.monthsShort = localeMonthsShort; - proto$1.monthsParse = localeMonthsParse; - proto$1.monthsRegex = monthsRegex; - proto$1.monthsShortRegex = monthsShortRegex; - proto$1.week = localeWeek; - proto$1.firstDayOfYear = localeFirstDayOfYear; - proto$1.firstDayOfWeek = localeFirstDayOfWeek; - proto$1.weekdays = localeWeekdays; - proto$1.weekdaysMin = localeWeekdaysMin; - proto$1.weekdaysShort = localeWeekdaysShort; - proto$1.weekdaysParse = localeWeekdaysParse; - proto$1.weekdaysRegex = weekdaysRegex; - proto$1.weekdaysShortRegex = weekdaysShortRegex; - proto$1.weekdaysMinRegex = weekdaysMinRegex; - proto$1.isPM = localeIsPM; - proto$1.meridiem = localeMeridiem; - function get$1(format2, index, field, setter) { - var locale2 = getLocale(), utc = createUTC().set(setter, index); - return locale2[field](utc, format2); - } - function listMonthsImpl(format2, index, field) { - if (isNumber(format2)) { - index = format2; - format2 = void 0; - } - format2 = format2 || ""; - if (index != null) { - return get$1(format2, index, field, "month"); - } - var i, out = []; - for (i = 0; i < 12; i++) { - out[i] = get$1(format2, i, field, "month"); - } - return out; - } - function listWeekdaysImpl(localeSorted, format2, index, field) { - if (typeof localeSorted === "boolean") { - if (isNumber(format2)) { - index = format2; - format2 = void 0; - } - format2 = format2 || ""; - } else { - format2 = localeSorted; - index = format2; - localeSorted = false; - if (isNumber(format2)) { - index = format2; - format2 = void 0; - } - format2 = format2 || ""; - } - var locale2 = getLocale(), shift = localeSorted ? locale2._week.dow : 0, i, out = []; - if (index != null) { - return get$1(format2, (index + shift) % 7, field, "day"); - } - for (i = 0; i < 7; i++) { - out[i] = get$1(format2, (i + shift) % 7, field, "day"); - } - return out; - } - function listMonths(format2, index) { - return listMonthsImpl(format2, index, "months"); - } - function listMonthsShort(format2, index) { - return listMonthsImpl(format2, index, "monthsShort"); - } - function listWeekdays(localeSorted, format2, index) { - return listWeekdaysImpl(localeSorted, format2, index, "weekdays"); - } - function listWeekdaysShort(localeSorted, format2, index) { - return listWeekdaysImpl(localeSorted, format2, index, "weekdaysShort"); - } - function listWeekdaysMin(localeSorted, format2, index) { - return listWeekdaysImpl(localeSorted, format2, index, "weekdaysMin"); - } - getSetGlobalLocale("en", { - eras: [ - { - since: "0001-01-01", - until: Infinity, - offset: 1, - name: "Anno Domini", - narrow: "AD", - abbr: "AD" - }, - { - since: "0000-12-31", - until: -Infinity, - offset: 1, - name: "Before Christ", - narrow: "BC", - abbr: "BC" - } - ], - dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/, - ordinal: function(number) { - var b = number % 10, output = toInt(number % 100 / 10) === 1 ? "th" : b === 1 ? "st" : b === 2 ? "nd" : b === 3 ? "rd" : "th"; - return number + output; - } - }); - hooks$1.lang = deprecate( - "moment.lang is deprecated. Use moment.locale instead.", - getSetGlobalLocale - ); - hooks$1.langData = deprecate( - "moment.langData is deprecated. Use moment.localeData instead.", - getLocale - ); - var mathAbs = Math.abs; - function abs() { - var data = this._data; - this._milliseconds = mathAbs(this._milliseconds); - this._days = mathAbs(this._days); - this._months = mathAbs(this._months); - data.milliseconds = mathAbs(data.milliseconds); - data.seconds = mathAbs(data.seconds); - data.minutes = mathAbs(data.minutes); - data.hours = mathAbs(data.hours); - data.months = mathAbs(data.months); - data.years = mathAbs(data.years); - return this; - } - function addSubtract$1(duration, input, value, direction) { - var other = createDuration(input, value); - duration._milliseconds += direction * other._milliseconds; - duration._days += direction * other._days; - duration._months += direction * other._months; - return duration._bubble(); - } - function add$1(input, value) { - return addSubtract$1(this, input, value, 1); - } - function subtract$1(input, value) { - return addSubtract$1(this, input, value, -1); - } - function absCeil(number) { - if (number < 0) { - return Math.floor(number); - } else { - return Math.ceil(number); - } - } - function bubble() { - var milliseconds2 = this._milliseconds, days2 = this._days, months2 = this._months, data = this._data, seconds2, minutes2, hours2, years2, monthsFromDays; - if (!(milliseconds2 >= 0 && days2 >= 0 && months2 >= 0 || milliseconds2 <= 0 && days2 <= 0 && months2 <= 0)) { - milliseconds2 += absCeil(monthsToDays(months2) + days2) * 864e5; - days2 = 0; - months2 = 0; - } - data.milliseconds = milliseconds2 % 1e3; - seconds2 = absFloor(milliseconds2 / 1e3); - data.seconds = seconds2 % 60; - minutes2 = absFloor(seconds2 / 60); - data.minutes = minutes2 % 60; - hours2 = absFloor(minutes2 / 60); - data.hours = hours2 % 24; - days2 += absFloor(hours2 / 24); - monthsFromDays = absFloor(daysToMonths(days2)); - months2 += monthsFromDays; - days2 -= absCeil(monthsToDays(monthsFromDays)); - years2 = absFloor(months2 / 12); - months2 %= 12; - data.days = days2; - data.months = months2; - data.years = years2; - return this; - } - function daysToMonths(days2) { - return days2 * 4800 / 146097; - } - function monthsToDays(months2) { - return months2 * 146097 / 4800; - } - function as(units) { - if (!this.isValid()) { - return NaN; - } - var days2, months2, milliseconds2 = this._milliseconds; - units = normalizeUnits(units); - if (units === "month" || units === "quarter" || units === "year") { - days2 = this._days + milliseconds2 / 864e5; - months2 = this._months + daysToMonths(days2); - switch (units) { - case "month": - return months2; - case "quarter": - return months2 / 3; - case "year": - return months2 / 12; - } - } else { - days2 = this._days + Math.round(monthsToDays(this._months)); - switch (units) { - case "week": - return days2 / 7 + milliseconds2 / 6048e5; - case "day": - return days2 + milliseconds2 / 864e5; - case "hour": - return days2 * 24 + milliseconds2 / 36e5; - case "minute": - return days2 * 1440 + milliseconds2 / 6e4; - case "second": - return days2 * 86400 + milliseconds2 / 1e3; - // Math.floor prevents floating point math errors here - case "millisecond": - return Math.floor(days2 * 864e5) + milliseconds2; - default: - throw new Error("Unknown unit " + units); - } - } - } - function makeAs(alias) { - return function() { - return this.as(alias); - }; - } - var asMilliseconds = makeAs("ms"), asSeconds = makeAs("s"), asMinutes = makeAs("m"), asHours = makeAs("h"), asDays = makeAs("d"), asWeeks = makeAs("w"), asMonths = makeAs("M"), asQuarters = makeAs("Q"), asYears = makeAs("y"), valueOf$1 = asMilliseconds; - function clone$1() { - return createDuration(this); - } - function get$2(units) { - units = normalizeUnits(units); - return this.isValid() ? this[units + "s"]() : NaN; - } - function makeGetter(name2) { - return function() { - return this.isValid() ? this._data[name2] : NaN; - }; - } - var milliseconds = makeGetter("milliseconds"), seconds = makeGetter("seconds"), minutes = makeGetter("minutes"), hours = makeGetter("hours"), days = makeGetter("days"), months = makeGetter("months"), years = makeGetter("years"); - function weeks() { - return absFloor(this.days() / 7); - } - var round = Math.round, thresholds = { - ss: 44, - // a few seconds to seconds - s: 45, - // seconds to minute - m: 45, - // minutes to hour - h: 22, - // hours to day - d: 26, - // days to month/week - w: null, - // weeks to month - M: 11 - // months to year - }; - function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale2) { - return locale2.relativeTime(number || 1, !!withoutSuffix, string, isFuture); - } - function relativeTime$1(posNegDuration, withoutSuffix, thresholds2, locale2) { - var duration = createDuration(posNegDuration).abs(), seconds2 = round(duration.as("s")), minutes2 = round(duration.as("m")), hours2 = round(duration.as("h")), days2 = round(duration.as("d")), months2 = round(duration.as("M")), weeks2 = round(duration.as("w")), years2 = round(duration.as("y")), a = seconds2 <= thresholds2.ss && ["s", seconds2] || seconds2 < thresholds2.s && ["ss", seconds2] || minutes2 <= 1 && ["m"] || minutes2 < thresholds2.m && ["mm", minutes2] || hours2 <= 1 && ["h"] || hours2 < thresholds2.h && ["hh", hours2] || days2 <= 1 && ["d"] || days2 < thresholds2.d && ["dd", days2]; - if (thresholds2.w != null) { - a = a || weeks2 <= 1 && ["w"] || weeks2 < thresholds2.w && ["ww", weeks2]; - } - a = a || months2 <= 1 && ["M"] || months2 < thresholds2.M && ["MM", months2] || years2 <= 1 && ["y"] || ["yy", years2]; - a[2] = withoutSuffix; - a[3] = +posNegDuration > 0; - a[4] = locale2; - return substituteTimeAgo.apply(null, a); - } - function getSetRelativeTimeRounding(roundingFunction) { - if (roundingFunction === void 0) { - return round; - } - if (typeof roundingFunction === "function") { - round = roundingFunction; - return true; - } - return false; - } - function getSetRelativeTimeThreshold(threshold, limit) { - if (thresholds[threshold] === void 0) { - return false; - } - if (limit === void 0) { - return thresholds[threshold]; - } - thresholds[threshold] = limit; - if (threshold === "s") { - thresholds.ss = limit - 1; - } - return true; - } - function humanize(argWithSuffix, argThresholds) { - if (!this.isValid()) { - return this.localeData().invalidDate(); - } - var withSuffix = false, th = thresholds, locale2, output; - if (typeof argWithSuffix === "object") { - argThresholds = argWithSuffix; - argWithSuffix = false; - } - if (typeof argWithSuffix === "boolean") { - withSuffix = argWithSuffix; - } - if (typeof argThresholds === "object") { - th = Object.assign({}, thresholds, argThresholds); - if (argThresholds.s != null && argThresholds.ss == null) { - th.ss = argThresholds.s - 1; - } - } - locale2 = this.localeData(); - output = relativeTime$1(this, !withSuffix, th, locale2); - if (withSuffix) { - output = locale2.pastFuture(+this, output); - } - return locale2.postformat(output); - } - var abs$1 = Math.abs; - function sign(x) { - return (x > 0) - (x < 0) || +x; - } - function toISOString$1() { - if (!this.isValid()) { - return this.localeData().invalidDate(); - } - var seconds2 = abs$1(this._milliseconds) / 1e3, days2 = abs$1(this._days), months2 = abs$1(this._months), minutes2, hours2, years2, s, total = this.asSeconds(), totalSign, ymSign, daysSign, hmsSign; - if (!total) { - return "P0D"; - } - minutes2 = absFloor(seconds2 / 60); - hours2 = absFloor(minutes2 / 60); - seconds2 %= 60; - minutes2 %= 60; - years2 = absFloor(months2 / 12); - months2 %= 12; - s = seconds2 ? seconds2.toFixed(3).replace(/\.?0+$/, "") : ""; - totalSign = total < 0 ? "-" : ""; - ymSign = sign(this._months) !== sign(total) ? "-" : ""; - daysSign = sign(this._days) !== sign(total) ? "-" : ""; - hmsSign = sign(this._milliseconds) !== sign(total) ? "-" : ""; - return totalSign + "P" + (years2 ? ymSign + years2 + "Y" : "") + (months2 ? ymSign + months2 + "M" : "") + (days2 ? daysSign + days2 + "D" : "") + (hours2 || minutes2 || seconds2 ? "T" : "") + (hours2 ? hmsSign + hours2 + "H" : "") + (minutes2 ? hmsSign + minutes2 + "M" : "") + (seconds2 ? hmsSign + s + "S" : ""); - } - var proto$2 = Duration.prototype; - proto$2.isValid = isValid$1; - proto$2.abs = abs; - proto$2.add = add$1; - proto$2.subtract = subtract$1; - proto$2.as = as; - proto$2.asMilliseconds = asMilliseconds; - proto$2.asSeconds = asSeconds; - proto$2.asMinutes = asMinutes; - proto$2.asHours = asHours; - proto$2.asDays = asDays; - proto$2.asWeeks = asWeeks; - proto$2.asMonths = asMonths; - proto$2.asQuarters = asQuarters; - proto$2.asYears = asYears; - proto$2.valueOf = valueOf$1; - proto$2._bubble = bubble; - proto$2.clone = clone$1; - proto$2.get = get$2; - proto$2.milliseconds = milliseconds; - proto$2.seconds = seconds; - proto$2.minutes = minutes; - proto$2.hours = hours; - proto$2.days = days; - proto$2.weeks = weeks; - proto$2.months = months; - proto$2.years = years; - proto$2.humanize = humanize; - proto$2.toISOString = toISOString$1; - proto$2.toString = toISOString$1; - proto$2.toJSON = toISOString$1; - proto$2.locale = locale; - proto$2.localeData = localeData; - proto$2.toIsoString = deprecate( - "toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)", - toISOString$1 - ); - proto$2.lang = lang; - addFormatToken("X", 0, 0, "unix"); - addFormatToken("x", 0, 0, "valueOf"); - addRegexToken("x", matchSigned); - addRegexToken("X", matchTimestamp); - addParseToken("X", function(input, array, config) { - config._d = new Date(parseFloat(input) * 1e3); - }); - addParseToken("x", function(input, array, config) { - config._d = new Date(toInt(input)); - }); - //! moment.js - hooks$1.version = "2.30.1"; - setHookCallback(createLocal); - hooks$1.fn = proto; - hooks$1.min = min; - hooks$1.max = max; - hooks$1.now = now; - hooks$1.utc = createUTC; - hooks$1.unix = createUnix; - hooks$1.months = listMonths; - hooks$1.isDate = isDate; - hooks$1.locale = getSetGlobalLocale; - hooks$1.invalid = createInvalid; - hooks$1.duration = createDuration; - hooks$1.isMoment = isMoment; - hooks$1.weekdays = listWeekdays; - hooks$1.parseZone = createInZone; - hooks$1.localeData = getLocale; - hooks$1.isDuration = isDuration; - hooks$1.monthsShort = listMonthsShort; - hooks$1.weekdaysMin = listWeekdaysMin; - hooks$1.defineLocale = defineLocale; - hooks$1.updateLocale = updateLocale; - hooks$1.locales = listLocales; - hooks$1.weekdaysShort = listWeekdaysShort; - hooks$1.normalizeUnits = normalizeUnits; - hooks$1.relativeTimeRounding = getSetRelativeTimeRounding; - hooks$1.relativeTimeThreshold = getSetRelativeTimeThreshold; - hooks$1.calendarFormat = getCalendarFormat; - hooks$1.prototype = proto; - hooks$1.HTML5_FMT = { - DATETIME_LOCAL: "YYYY-MM-DDTHH:mm", - // - DATETIME_LOCAL_SECONDS: "YYYY-MM-DDTHH:mm:ss", - // - DATETIME_LOCAL_MS: "YYYY-MM-DDTHH:mm:ss.SSS", - // - DATE: "YYYY-MM-DD", - // - TIME: "HH:mm", - // - TIME_SECONDS: "HH:mm:ss", - // - TIME_MS: "HH:mm:ss.SSS", - // - WEEK: "GGGG-[W]WW", - // - MONTH: "YYYY-MM" - // - }; - async function getCatalogRaw(bookId) { - const response = await apiFetch(`https://fanqienovel.com/api/reader/directory/detail?bookId=${bookId}`); - const j = response.json(); - if (j.code !== 0 || j.data.chapterListWithVolume.length === 0) { - throw new Error("Empty catalog"); - } - return [j.data.chapterListWithVolume, j.data.allItemIds]; - } - async function getCatalog(bookId) { - const r = await getCatalogRaw(bookId); - const catalogRaw = r[0]; - const allItemIds = r[1]; - const vmap = {}; - const chapters = []; - catalogRaw[0].map((item) => { - const chapterItem = { - item_id: item.itemId, - title: item.title, - // YYYY-MM-DD HH:mm:ss - update_time: hooks$1(item.firstPassTime * 1e3).format("YYYY-MM-DD HH:mm:ss"), - char_count: item.charCount || 0, - volume_title: item.volume_name - }; - chapters.push(chapterItem); - if (!vmap[item.volume_name]) { - vmap[item.volume_name] = { - title: item.volume_name, - book_id: bookId, - chapter_list: [] - }; - } - vmap[item.volume_name].chapter_list.push(chapterItem); - }); - return { + chapters.push(chapterItem); + if (!vmap[item.volume_name]) { + vmap[item.volume_name] = { + title: item.volume_name, book_id: bookId, - volume_list: Object.values(vmap), - chapter_list: chapters, - all_item_ids: allItemIds + chapter_list: [] }; } - function mappingCreationStatus(status) { - switch (status) { - case "0": - return "完结"; - case "1": - return "连载"; - case "4": - return "断更"; - default: - return "未知"; + vmap[item.volume_name].chapter_list.push(chapterItem); + }); + return { + book_id: bookId, + volume_list: Object.values(vmap), + chapter_list: chapters, + all_item_ids: allItemIds + }; + } + function mappingCreationStatus(status) { + switch (status) { + case "0": + return "完结"; + case "1": + return "连载"; + case "4": + return "断更"; + default: + return "未知"; + } + } + async function getBookInfoRaw(bookId) { + const response = await apiFetch(`https://api5-sinfonlinec.jxbhmy.com/reading/bookapi/multi-detail/v?book_id=${bookId}&aid=1967`); + const j = response.json(); + console.log("Book Info:", j); + if (typeof j === "object" && j !== null && "data" in j && Array.isArray(j.data) && j.data.length > 0) return j.data[0]; + return null; + } + async function getBookInfo(bookId) { + const bookInfo = await getBookInfoRaw(bookId); + if (!bookInfo) { + throw new Error("Book not found"); + } + return { + book_id: bookInfo.book_id, + title: bookInfo.book_name || bookInfo.original_book_name, + author: bookInfo.author, + cover_url: bookInfo.thumb_url, + summary: bookInfo.abstract, + // volume_list: bookInfo.volume_list, + update_time: moment(bookInfo.last_chapter_first_pass_time * 1e3).format("YYYY-MM-DD HH:mm:ss"), + status: mappingCreationStatus(bookInfo.creation_status) + // chapter_count: bookInfo.chapter_count, + }; + } + async function getBookInfoAndCatalog(book) { + if (typeof book !== "string") { + book = book.book_id; + } + const bookInfo = await getBookInfo(book); + if (!bookInfo) { + throw new Error("Book not found"); + } + const catalog = await getCatalog(bookInfo.book_id); + console.log("Catalog:", catalog); + bookInfo.volume_list = catalog.volume_list; + bookInfo.chapter_list = catalog.chapter_list; + return bookInfo; + } + let currentBook = null; + let latestItemId = null; + async function insertContent() { + var _a, _b, _c, _d, _e, _f, _g, _h; + const itemId = ((_a = window.location.pathname.split("/").pop()) == null ? void 0 : _a.substring(0, 19)) || ""; + if (!itemId) { + console.warn("No item_id found in URL"); + return; + } + latestItemId = itemId; + const chapter = await getChapter(itemId); + if (!chapter) { + console.warn("No chapter found for item_id:", itemId); + return; + } + if (latestItemId !== itemId) { + console.debug("Stale chapter response discarded:", itemId); + return; + } + console.log("Chapter:", chapter); + const pageState = unsafeWindow.__INITIAL_STATE__; + const chapterTitle = ((_b = chapter.novel_data) == null ? void 0 : _b.title) || ((_d = (_c = pageState == null ? void 0 : pageState.reader) == null ? void 0 : _c.chapterData) == null ? void 0 : _d.title); + if (typeof chapter.content === "string") { + const dp = new DOMParser(); + const doc = dp.parseFromString(chapter.content, "text/html"); + const body = doc.body; + const article = body.querySelector("article"); + const toProcess = article || body; + for (let i = 0; i < toProcess.childNodes.length; i++) { + if (i < 1 && ((_e = toProcess.childNodes[i]) == null ? void 0 : _e.innerHTML.includes(chapterTitle))) { + toProcess.removeChild(toProcess.childNodes[i]); } } - async function getBookInfoRaw(bookId) { - const response = await apiFetch(`https://api5-sinfonlinec.jxbhmy.com/reading/bookapi/multi-detail/v?book_id=${bookId}&aid=1967`); - const j = response.json(); - console.log("Book Info:", j); - if (typeof j === "object" && j !== null && "data" in j && Array.isArray(j.data) && j.data.length > 0) return j.data[0]; - return null; + const readerContainer = document.querySelector("div.muye-reader-content:not(.fqa)"); + if (readerContainer) { + let scriptContainer = document.getElementById("fqa-reader-content"); + if (!scriptContainer) { + scriptContainer = cloneElement(readerContainer); + scriptContainer.id = "fqa-reader-content"; + scriptContainer.classList.add("fqa"); + scriptContainer.classList.remove("noselect"); + readerContainer.insertAdjacentElement("beforebegin", scriptContainer); + } + scriptContainer.innerHTML = ""; + readerContainer.classList.add("fqa-hide"); + scriptContainer.appendChild(toProcess); } - async function getBookInfo(bookId) { - const bookInfo = await getBookInfoRaw(bookId); - if (!bookInfo) { - throw new Error("Book not found"); + } else if (chapter.content.picInfos) { + if (chapter.content.encrypt) { + const imgs = []; + for (let i = 0; i < chapter.content.picInfos.length; i++) { + const picInfo = chapter.content.picInfos[i]; + const img = document.createElement("img"); + img.className = "fqa-comic-img fqa-comic-encrypted"; + img.alt = `第${i + 1}页`; + img.dataset.encryptedUrl = picInfo.picUrl; + img.dataset.encryptKey = chapter.content.encrypt_key; + img.dataset.pageIndex = i.toString(); + img.style.minHeight = "500px"; + img.style.backgroundColor = "#f0f0f0"; + imgs.push(img); } - return { - book_id: bookInfo.book_id, - title: bookInfo.book_name || bookInfo.original_book_name, - author: bookInfo.author, - cover_url: bookInfo.thumb_url, - summary: bookInfo.abstract, - // volume_list: bookInfo.volume_list, - update_time: hooks$1(bookInfo.last_chapter_first_pass_time * 1e3).format("YYYY-MM-DD HH:mm:ss"), - status: mappingCreationStatus(bookInfo.creation_status) - // chapter_count: bookInfo.chapter_count, - }; - } - async function getBookInfoAndCatalog(book) { - if (typeof book !== "string") { - book = book.book_id; - } - const bookInfo = await getBookInfo(book); - if (!bookInfo) { - throw new Error("Book not found"); - } - const catalog = await getCatalog(bookInfo.book_id); - console.log("Catalog:", catalog); - bookInfo.volume_list = catalog.volume_list; - bookInfo.chapter_list = catalog.chapter_list; - return bookInfo; - } - let currentBook = null; - let latestItemId = null; - async function insertContent() { - var _a, _b, _c, _d, _e, _f, _g, _h; - const itemId = ((_a = window.location.pathname.split("/").pop()) == null ? void 0 : _a.substring(0, 19)) || ""; - if (!itemId) { - console.warn("No item_id found in URL"); - return; - } - latestItemId = itemId; - const chapter = await getChapter(itemId); - if (!chapter) { - console.warn("No chapter found for item_id:", itemId); - return; - } - if (latestItemId !== itemId) { - console.debug("Stale chapter response discarded:", itemId); - return; - } - console.log("Chapter:", chapter); - const pageState = unsafeWindow.__INITIAL_STATE__; - const chapterTitle = ((_b = chapter.novel_data) == null ? void 0 : _b.title) || ((_d = (_c = pageState == null ? void 0 : pageState.reader) == null ? void 0 : _c.chapterData) == null ? void 0 : _d.title); - if (typeof chapter.content === "string") { - const dp = new DOMParser(); - const doc = dp.parseFromString(chapter.content, "text/html"); - const body = doc.body; - const article = body.querySelector("article"); - const toProcess = article || body; - for (let i = 0; i < toProcess.childNodes.length; i++) { - if (i < 1 && ((_e = toProcess.childNodes[i]) == null ? void 0 : _e.innerHTML.includes(chapterTitle))) { - toProcess.removeChild(toProcess.childNodes[i]); - } + const readerContainer = document.querySelector("div.muye-reader-content:not(.fqa)"); + if (readerContainer) { + let scriptContainer = document.getElementById("fqa-comic-content"); + if (!scriptContainer) { + scriptContainer = cloneElement(readerContainer); + scriptContainer.id = "fqa-reader-content"; + scriptContainer.classList.add("fqa"); + scriptContainer.classList.add("fqa-comic-reader"); + scriptContainer.classList.remove("noselect"); + readerContainer.insertAdjacentElement("beforebegin", scriptContainer); } - const readerContainer = document.querySelector("div.muye-reader-content:not(.fqa)"); - if (readerContainer) { - let scriptContainer = document.getElementById("fqa-reader-content"); - if (!scriptContainer) { - scriptContainer = cloneElement(readerContainer); - scriptContainer.id = "fqa-reader-content"; - scriptContainer.classList.add("fqa"); - scriptContainer.classList.remove("noselect"); - readerContainer.insertAdjacentElement("beforebegin", scriptContainer); - } - scriptContainer.innerHTML = ""; - readerContainer.classList.add("fqa-hide"); - scriptContainer.appendChild(toProcess); - } - } else if (chapter.content.picInfos) { - if (chapter.content.encrypt) { - const imgs = []; - for (let i = 0; i < chapter.content.picInfos.length; i++) { - const picInfo = chapter.content.picInfos[i]; - const img = document.createElement("img"); - img.className = "fqa-comic-img fqa-comic-encrypted"; - img.alt = `第${i + 1}页`; - img.dataset.encryptedUrl = picInfo.picUrl; - img.dataset.encryptKey = chapter.content.encrypt_key; - img.dataset.pageIndex = i.toString(); - img.style.minHeight = "500px"; - img.style.backgroundColor = "#f0f0f0"; - imgs.push(img); - } - const readerContainer = document.querySelector("div.muye-reader-content:not(.fqa)"); - if (readerContainer) { - let scriptContainer = document.getElementById("fqa-comic-content"); - if (!scriptContainer) { - scriptContainer = cloneElement(readerContainer); - scriptContainer.id = "fqa-reader-content"; - scriptContainer.classList.add("fqa"); - scriptContainer.classList.add("fqa-comic-reader"); - scriptContainer.classList.remove("noselect"); - readerContainer.insertAdjacentElement("beforebegin", scriptContainer); - } - scriptContainer.innerHTML = ""; - readerContainer.classList.add("fqa-hide"); - imgs.forEach((img) => scriptContainer.appendChild(img)); - const observer = new IntersectionObserver( - async (entries) => { - for (const entry of entries) { - if (entry.isIntersecting) { - const img = entry.target; - if (img.dataset.encryptedUrl && img.dataset.encryptKey && !img.src) { - observer.unobserve(img); - try { - const response = await fetch(img.dataset.encryptedUrl); - const encryptedBuffer = await response.arrayBuffer(); - const decryptedBuffer = await decryptComicImage( - encryptedBuffer, - img.dataset.encryptKey - ); - const blob = new Blob([decryptedBuffer], { type: "image/jpeg" }); - const blobUrl = URL.createObjectURL(blob); - img.src = blobUrl; - img.style.minHeight = ""; - img.style.backgroundColor = ""; - img.onload = () => { - URL.revokeObjectURL(blobUrl); - }; - } catch (error) { - console.error(`解密图片失败 (页 ${img.dataset.pageIndex}):`, error); - img.alt = `第${Number(img.dataset.pageIndex) + 1}页 - 解密失败`; - img.style.backgroundColor = "#ffebee"; - } - } + scriptContainer.innerHTML = ""; + readerContainer.classList.add("fqa-hide"); + imgs.forEach((img) => scriptContainer.appendChild(img)); + const observer2 = new IntersectionObserver( + async (entries) => { + for (const entry of entries) { + if (entry.isIntersecting) { + const img = entry.target; + if (img.dataset.encryptedUrl && img.dataset.encryptKey && !img.src) { + observer2.unobserve(img); + try { + const response = await fetch(img.dataset.encryptedUrl); + const encryptedBuffer = await response.arrayBuffer(); + const decryptedBuffer = await decryptComicImage( + encryptedBuffer, + img.dataset.encryptKey + ); + const blob = new Blob([decryptedBuffer], { type: "image/jpeg" }); + const blobUrl = URL.createObjectURL(blob); + img.src = blobUrl; + img.style.minHeight = ""; + img.style.backgroundColor = ""; + img.onload = () => { + URL.revokeObjectURL(blobUrl); + }; + } catch (error) { + console.error(`解密图片失败 (页 ${img.dataset.pageIndex}):`, error); + img.alt = `第${Number(img.dataset.pageIndex) + 1}页 - 解密失败`; + img.style.backgroundColor = "#ffebee"; } } - }, - { - rootMargin: "200px" } - ); - imgs.forEach((img) => observer.observe(img)); - } - } else { - const imgs = []; - for (let i = 0; i < chapter.content.picInfos.length; i++) { - const picInfo = chapter.content.picInfos[i]; - const img = document.createElement("img"); - img.className = "fqa-comic-img"; - img.alt = `第${i + 1}页`; - img.src = picInfo.picUrl; - imgs.push(img); - } - const readerContainer = document.querySelector("div.muye-reader-content:not(.fqa)"); - if (readerContainer) { - let scriptContainer = document.getElementById("fqa-comic-content"); - if (!scriptContainer) { - scriptContainer = cloneElement(readerContainer); - scriptContainer.id = "fqa-reader-content"; - scriptContainer.classList.add("fqa"); - scriptContainer.classList.add("fqa-comic-reader"); - scriptContainer.classList.remove("noselect"); - readerContainer.insertAdjacentElement("beforebegin", scriptContainer); } - scriptContainer.innerHTML = ""; - readerContainer.classList.add("fqa-hide"); - imgs.forEach((img) => scriptContainer.appendChild(img)); + }, + { + rootMargin: "200px" } + ); + imgs.forEach((img) => observer2.observe(img)); + } + } else { + const imgs = []; + for (let i = 0; i < chapter.content.picInfos.length; i++) { + const picInfo = chapter.content.picInfos[i]; + const img = document.createElement("img"); + img.className = "fqa-comic-img"; + img.alt = `第${i + 1}页`; + img.src = picInfo.picUrl; + imgs.push(img); + } + const readerContainer = document.querySelector("div.muye-reader-content:not(.fqa)"); + if (readerContainer) { + let scriptContainer = document.getElementById("fqa-comic-content"); + if (!scriptContainer) { + scriptContainer = cloneElement(readerContainer); + scriptContainer.id = "fqa-reader-content"; + scriptContainer.classList.add("fqa"); + scriptContainer.classList.add("fqa-comic-reader"); + scriptContainer.classList.remove("noselect"); + readerContainer.insertAdjacentElement("beforebegin", scriptContainer); } + scriptContainer.innerHTML = ""; + readerContainer.classList.add("fqa-hide"); + imgs.forEach((img) => scriptContainer.appendChild(img)); } - const muyeReaderTitle = document.querySelector("h1.muye-reader-title"); - let muyeReaderSubtitle = document.querySelector("div.muye-reader-subtitle"); - (_f = document.querySelector("#fqa-subtitle")) == null ? void 0 : _f.remove(); - if (muyeReaderSubtitle) { - let _cloned = cloneElement(muyeReaderSubtitle); - muyeReaderSubtitle.classList.add("fqa-hide"); - _cloned.id = "fqa-subtitle"; - muyeReaderSubtitle.insertAdjacentElement("afterend", _cloned); - muyeReaderSubtitle = _cloned; - _cloned.classList.remove("fqa-hide"); - console.log("clone subtitle: ", _cloned); - } - if (muyeReaderTitle) { - muyeReaderTitle.textContent = chapterTitle; - } - console.log("Current book:", currentBook); - if (!currentBook || currentBook == null || currentBook.book_id !== ((_g = chapter.novel_data) == null ? void 0 : _g.book_id)) { - currentBook = await getBookInfoAndCatalog((_h = chapter.novel_data) == null ? void 0 : _h.book_id); - console.log("Current book:", currentBook); - } - if (currentBook && currentBook.chapter_list) { - const currentChapterItem = currentBook.chapter_list.find((c) => c.item_id === itemId); - if (currentChapterItem) { - console.log("Current chapter:", currentChapterItem); - document.title = currentChapterItem.title + " - " + currentBook.title + " - 番茄小说"; - if (document.getElementById("fqa-current-chapter-volume")) { - const c = document.getElementById("fqa-current-chapter-volume"); - if (c) { - c.textContent = currentChapterItem.volume_title; - } - } else { - const volSpan = document.createElement("span"); - volSpan.className = "desc-item"; - volSpan.id = "fqa-current-chapter-volume"; - volSpan.textContent = currentChapterItem.volume_title; - const c = muyeReaderSubtitle == null ? void 0 : muyeReaderSubtitle.firstChild; - if (c) { - c.insertAdjacentElement("beforebegin", volSpan); - } - } - let updateTimeSpans = (muyeReaderSubtitle == null ? void 0 : muyeReaderSubtitle.querySelectorAll("span.desc-item")) || []; - if (updateTimeSpans.length >= 2) { - let updateTimeSpan = updateTimeSpans[updateTimeSpans.length - 1]; - let uttspan = updateTimeSpan.firstChild; - uttspan == null ? void 0 : uttspan.remove(); - updateTimeSpan.innerHTML = "更新时间:" + currentChapterItem.update_time; - } else { - let updateTimeSpan = document.createElement("span"); - updateTimeSpan.className = "desc-item"; - updateTimeSpan.textContent = `更新时间:${currentChapterItem.update_time}`; - } + } + } + const muyeReaderTitle = document.querySelector("h1.muye-reader-title"); + let muyeReaderSubtitle = document.querySelector("div.muye-reader-subtitle"); + (_f = document.querySelector("#fqa-subtitle")) == null ? void 0 : _f.remove(); + if (muyeReaderSubtitle) { + let _cloned = cloneElement(muyeReaderSubtitle); + muyeReaderSubtitle.classList.add("fqa-hide"); + _cloned.id = "fqa-subtitle"; + muyeReaderSubtitle.insertAdjacentElement("afterend", _cloned); + muyeReaderSubtitle = _cloned; + _cloned.classList.remove("fqa-hide"); + console.log("clone subtitle: ", _cloned); + } + if (muyeReaderTitle) { + muyeReaderTitle.textContent = chapterTitle; + } + console.log("Current book:", currentBook); + if (!currentBook || currentBook == null || currentBook.book_id !== ((_g = chapter.novel_data) == null ? void 0 : _g.book_id)) { + currentBook = await getBookInfoAndCatalog((_h = chapter.novel_data) == null ? void 0 : _h.book_id); + console.log("Current book:", currentBook); + } + if (currentBook && currentBook.chapter_list) { + const currentChapterItem = currentBook.chapter_list.find((c) => c.item_id === itemId); + if (currentChapterItem) { + console.log("Current chapter:", currentChapterItem); + document.title = currentChapterItem.title + " - " + currentBook.title + " - 番茄小说"; + if (document.getElementById("fqa-current-chapter-volume")) { + const c = document.getElementById("fqa-current-chapter-volume"); + if (c) { + c.textContent = currentChapterItem.volume_title; } - } - } - async function onUrlChange$1(_previous) { - await insertContent(); - } - async function onHashChange$1(_previous) { - } - async function onLoad$1() { - document.querySelector("div.muye-reader-btns"); - await insertContent(); - } - function readerFilter(path, _query, _hash) { - return path.startsWith("/reader") || path.startsWith("reader"); - } - const _exports$2 = [ - { - id: "readerHook_load", - event: "load", - handler: onLoad$1, - filter: readerFilter - }, - { - id: "readerHook_urlChange", - event: "onUrlChange", - handler: onUrlChange$1, - filter: readerFilter - }, - { - id: "readerHook_hashChange", - event: "onHashChange", - handler: onHashChange$1, - filter: readerFilter - } - ]; - const blackList = [ - "mcs.zijieapi.com", - "vcs.zijieapi.com/vc/setting", - "mon.zijieapi.com", - "mssdk.bytedance.com/web/common", - "hm.baidu.com" - ]; - function checkBlack(url) { - return blackList.some((black) => url.includes(black)); - } - const originalFetch = unsafeWindow.fetch.bind(unsafeWindow); - unsafeWindow.fetch = function fetch2(input, init2) { - let url; - if (input instanceof Request) { - url = input.url; - } else if (input instanceof URL) { - url = input.href; } else { - url = input; + const volSpan = document.createElement("span"); + volSpan.className = "desc-item"; + volSpan.id = "fqa-current-chapter-volume"; + volSpan.textContent = currentChapterItem.volume_title; + const c = muyeReaderSubtitle == null ? void 0 : muyeReaderSubtitle.firstChild; + if (c) { + c.insertAdjacentElement("beforebegin", volSpan); + } } - if (checkBlack(url)) { - console.log("blocked request: " + url); - return new Promise(() => { - return new Response("{}", { status: 403 }); + let updateTimeSpans = (muyeReaderSubtitle == null ? void 0 : muyeReaderSubtitle.querySelectorAll("span.desc-item")) || []; + if (updateTimeSpans.length >= 2) { + let updateTimeSpan = updateTimeSpans[updateTimeSpans.length - 1]; + let uttspan = updateTimeSpan.firstChild; + uttspan == null ? void 0 : uttspan.remove(); + updateTimeSpan.innerHTML = "更新时间:" + currentChapterItem.update_time; + } else { + let updateTimeSpan = document.createElement("span"); + updateTimeSpan.className = "desc-item"; + updateTimeSpan.textContent = `更新时间:${currentChapterItem.update_time}`; + } + } + } + } + async function onUrlChange$1(_previous) { + await insertContent(); + } + async function onHashChange$1(_previous) { + } + async function onLoad$1() { + document.querySelector("div.muye-reader-btns"); + await insertContent(); + } + function readerFilter(path, _query, _hash) { + return path.startsWith("/reader") || path.startsWith("reader"); + } + const _exports$3 = [ + { + id: "readerHook_load", + event: "load", + handler: onLoad$1, + filter: readerFilter + }, + { + id: "readerHook_urlChange", + event: "onUrlChange", + handler: onUrlChange$1, + filter: readerFilter + }, + { + id: "readerHook_hashChange", + event: "onHashChange", + handler: onHashChange$1, + filter: readerFilter + } + ]; + const blackList = [ + "mcs.zijieapi.com", + "vcs.zijieapi.com/vc/setting", + "mon.zijieapi.com", + "mssdk.bytedance.com/web/common", + "hm.baidu.com" + ]; + const BLOCKED_BODY = JSON.stringify({ + e: 0, + sc: 10, + tc: 10 + }); + function checkBlack(url) { + return blackList.some((black) => url.includes(black)); + } + const originalFetch = unsafeWindow.fetch.bind(unsafeWindow); + unsafeWindow.fetch = function fetch2(input, init2) { + let url; + if (input instanceof Request) { + url = input.url; + } else if (input instanceof URL) { + url = input.href; + } else { + url = input; + } + if (checkBlack(url)) { + console.log("blocked request: " + url); + return Promise.resolve(new Response(BLOCKED_BODY, { + status: 200, + headers: { "Content-Type": "application/json" } + })); + } + return originalFetch(input, init2); + }; + const originalXMLHttpRequest = unsafeWindow.XMLHttpRequest; + unsafeWindow.XMLHttpRequest = class XMLHttpRequest extends originalXMLHttpRequest { + constructor() { + super(...arguments); + __publicField(this, "_blockedUrl"); + } + open(method, url, async = true, user, password) { + if (checkBlack(url)) { + console.log("blocked request: " + url); + this._blockedUrl = url; + return; + } + this._blockedUrl = void 0; + super.open(method, url, async, user, password); + } + setRequestHeader(name2, value) { + if (this._blockedUrl !== void 0) return; + super.setRequestHeader(name2, value); + } + send(body) { + if (this._blockedUrl === void 0) { + super.send(body); + return; + } + const url = this._blockedUrl; + const shadow = (prop, value) => Object.defineProperty(this, prop, { configurable: true, get: () => value }); + setTimeout(() => { + shadow("readyState", 4); + shadow("status", 200); + shadow("statusText", "OK"); + shadow("responseURL", url); + shadow("responseText", this.responseType === "" || this.responseType === "text" ? BLOCKED_BODY : ""); + shadow("response", this.responseType === "json" ? { + "e": 0, + "sc": 10, + "tc": 10 + } : BLOCKED_BODY); + this.dispatchEvent(new Event("readystatechange")); + this.dispatchEvent(new ProgressEvent("load")); + this.dispatchEvent(new ProgressEvent("loadend")); + }, 0); + } + abort() { + if (this._blockedUrl !== void 0) return; + super.abort(); + } + getAllResponseHeaders() { + if (this._blockedUrl !== void 0) return "content-type: application/json\r\n"; + return super.getAllResponseHeaders(); + } + getResponseHeader(name2) { + if (this._blockedUrl !== void 0) { + return name2.toLowerCase() === "content-type" ? "application/json" : null; + } + return super.getResponseHeader(name2); + } + }; + const _exports$2 = []; + let userState = { + isLogin: false, + userInfo: null + }; + if (read("userState")) { + userState = read("userState"); + } + console.log("userState:", userState); + async function getDetailedUserInfo() { + if (!(userState == null ? void 0 : userState.isLogin) || !(userState == null ? void 0 : userState.userInfo)) { + return null; + } + if (userState.userInfo.gender !== void 0 && userState.userInfo.recommend_gender !== void 0 && userState.userInfo.fans_num !== void 0 && userState.userInfo.following_num !== void 0 && userState.userInfo.is_author !== void 0 && userState.userInfo.author_desc !== void 0 && userState.userInfo.read_book_num !== void 0 && userState.userInfo.read_book_time !== void 0) { + return userState.userInfo; + } + const response = await fetch("https://fanqienovel.com/reading/user/basic_info/get/v?aid=1967"); + const j = await response.json(); + if (j == null ? void 0 : j.data) { + const data = j.data; + userState.userInfo.gender = data.profile_gender; + userState.userInfo.recommend_gender = data.gender; + userState.userInfo.fans_num = data.fans_num; + userState.userInfo.following_num = data.follow_user_num; + userState.userInfo.is_author = data.is_author; + userState.userInfo.author_desc = data.author_desc; + userState.userInfo.read_book_num = data.read_book_num; + userState.userInfo.read_book_time = BigInt(data.read_book_time); + return userState.userInfo; + } + write("userState", userState); + return userState.userInfo; + } + async function checkLogin() { + var _a, _b, _c, _d, _e, _f; + const response = await fetch("https://fanqienovel.com/api/user/info/v2"); + const j = await response.json(); + const _userInfo = { + id: (_a = j == null ? void 0 : j.data) == null ? void 0 : _a.id, + username: (_b = j == null ? void 0 : j.data) == null ? void 0 : _b.name, + avatar: (_c = j == null ? void 0 : j.data) == null ? void 0 : _c.avatar, + desc: (_d = j == null ? void 0 : j.data) == null ? void 0 : _d.desc, + age: (_e = j == null ? void 0 : j.data) == null ? void 0 : _e.age + }; + if (((_f = j == null ? void 0 : j.data) == null ? void 0 : _f.id) > 1) { + userState.isLogin = true; + userState.userInfo = _userInfo; + write("userState", userState); + return true; + } else { + del("userState"); + return false; + } + } + async function init() { + var _a; + await checkLogin(); + if (userState.isLogin) { + console.log("Hello, ", (_a = userState == null ? void 0 : userState.userInfo) == null ? void 0 : _a.username); + } + } + const bookshelf = '\r\n \r\n \r\n \r\n \r\n \r\n'; + function formatReadingTime(readBookTime) { + let minutes = readBookTime / 60000n; + const hours = minutes / 60n; + minutes = minutes % 60n; + return `${hours} 时 ${minutes} 分`; + } + function createMenuItem(text, icon, onclick) { + const item = document.createElement("div"); + item.role = "menuitem"; + item.classList.add("fqa-menu-item", "arco-menu-item", "serial-menu-item", "slogin-user-avatar__menu-item-wrapper"); + const inner1 = document.createElement("div"); + inner1.classList.add("slogin-user-avatar__menu-item"); + if (icon) { + const iconDiv = document.createElement("div"); + iconDiv.classList.add("slogin-user-avatar__menu-item__icon"); + iconDiv.innerHTML = icon; + inner1.appendChild(iconDiv); + } + const inner2 = document.createElement("div"); + inner2.classList.add("slogin-user-avatar__menu-item__content"); + inner2.textContent = text; + inner1.appendChild(inner2); + item.appendChild(inner1); + if (onclick) { + item.addEventListener("click", onclick); + } + return item; + } + async function mainHook$1(_previous) { + const userInfo = await getDetailedUserInfo(); + if (!userInfo) { + return; + } + const injected = /* @__PURE__ */ new WeakSet(); + const inject2 = (menuInner) => { + if (injected.has(menuInner)) { + return; + } + injected.add(menuInner); + console.log("user action dialog created", menuInner); + const firstDiv = createMenuItem(`阅读 ${userInfo.read_book_num} 本书`); + menuInner.insertAdjacentElement("afterbegin", firstDiv); + const secondDiv = createMenuItem(formatReadingTime(userInfo.read_book_time ?? 0n)); + firstDiv.insertAdjacentElement("afterend", secondDiv); + const thirdDiv = createMenuItem("我的书架", bookshelf, () => { + window.open("https://fanqienovel.com/bookshelf", "_blank"); + }); + secondDiv.insertAdjacentElement("afterend", thirdDiv); + }; + const scan = (root) => { + if (root.classList.contains("arco-menu-inner")) { + inject2(root); + } + root.querySelectorAll(".arco-menu-inner").forEach(inject2); + }; + const observer2 = new MutationObserver((mutationsList) => { + mutationsList.forEach((mutation) => { + if (mutation.type === "childList") { + mutation.addedNodes.forEach((node) => { + if (node instanceof HTMLElement) { + scan(node); + } }); } - return originalFetch(input, init2); - }; - const originalXMLHttpRequest = unsafeWindow.XMLHttpRequest; - unsafeWindow.XMLHttpRequest = class XMLHttpRequest extends originalXMLHttpRequest { - open(method, url, async = true, user, password) { - if (checkBlack(url)) { - console.log("blocked request: " + url); - this.abort(); - } else { - super.open(method, url, async, user, password); - } - } - }; - const _exports$1 = []; - let userState = { - isLogin: false, - userInfo: null - }; - if (read("userState")) { - userState = read("userState"); - } - async function getDetailedUserInfo() { - if (!(userState == null ? void 0 : userState.isLogin) || !(userState == null ? void 0 : userState.userInfo)) { - return null; - } - if (userState.userInfo.gender !== void 0 && userState.userInfo.recommend_gender !== void 0 && userState.userInfo.fans_num !== void 0 && userState.userInfo.following_num !== void 0 && userState.userInfo.is_author !== void 0 && userState.userInfo.author_desc !== void 0 && userState.userInfo.read_book_num !== void 0 && userState.userInfo.read_book_time !== void 0) { - return userState.userInfo; - } - const response = await fetch("https://fanqienovel.com/reading/user/basic_info/get/v?aid=1967"); - const j = await response.json(); - if (j == null ? void 0 : j.data) { - const data = j.data; - userState.userInfo.gender = data.profile_gender; - userState.userInfo.recommend_gender = data.gender; - userState.userInfo.fans_num = data.fans_num; - userState.userInfo.following_num = data.follow_user_num; - userState.userInfo.is_author = data.is_author; - userState.userInfo.author_desc = data.author_desc; - userState.userInfo.read_book_num = data.read_book_num; - userState.userInfo.read_book_time = BigInt(data.read_book_time); - return userState.userInfo; - } - return userState.userInfo; - } - async function checkLogin() { - var _a, _b, _c, _d, _e, _f; - const response = await fetch("https://fanqienovel.com/api/user/info/v2"); - const j = await response.json(); - const _userInfo = { - id: (_a = j == null ? void 0 : j.data) == null ? void 0 : _a.id, - username: (_b = j == null ? void 0 : j.data) == null ? void 0 : _b.name, - avatar: (_c = j == null ? void 0 : j.data) == null ? void 0 : _c.avatar, - desc: (_d = j == null ? void 0 : j.data) == null ? void 0 : _d.desc, - age: (_e = j == null ? void 0 : j.data) == null ? void 0 : _e.age - }; - if (((_f = j == null ? void 0 : j.data) == null ? void 0 : _f.id) > 1) { - userState.isLogin = true; - userState.userInfo = _userInfo; - write("userState", userState); - return true; - } else { - del("userState"); - return false; - } - } - async function init() { + }); + }); + observer2.observe(document.body, { childList: true, subtree: true }); + scan(document.body); + } + function filter$1(path, _query, _hash) { + return userState.isLogin && !path.startsWith("/writer") && !path.startsWith("/welfare"); + } + const _exports$1 = [ + { + id: "userHook", + event: "load", + filter: filter$1, + handler: mainHook$1 + } + ]; + const _hoisted_1$3 = ["aria-label"]; + const _hoisted_2$3 = { class: "fqa-cover" }; + const _hoisted_3$3 = ["src", "alt"]; + const _hoisted_4$3 = { + key: 1, + class: "fqa-cover-progress" + }; + const _hoisted_5$3 = ["title"]; + const _hoisted_6$3 = ["title"]; + const _sfc_main$3 = /* @__PURE__ */ vue.defineComponent({ + __name: "BookCard", + props: { + entry: {} + }, + emits: ["hover", "leave", "open", "visible"], + setup(__props, { emit: __emit }) { + const props = __props; + const emit = __emit; + const detail = vue.computed(() => props.entry.detail); + const title = vue.computed(() => { var _a; - await checkLogin(); - if (userState.isLogin) { - console.log("Hello, ", (_a = userState == null ? void 0 : userState.userInfo) == null ? void 0 : _a.username); + return ((_a = detail.value) == null ? void 0 : _a.title) ?? ""; + }); + const tag = vue.computed(() => { + const d = detail.value; + if (!d) return null; + if (d.update_status === "1") return { text: "更新", cls: "" }; + if (d.status === "4") return { text: "断更", cls: "fqa-cover-tag-gray" }; + if (d.status === "0") return { text: "完结", cls: "fqa-cover-tag-gray" }; + if (d.status === "1") return { text: "连载", cls: "fqa-cover-tag-gray" }; + return null; + }); + const progressText = vue.computed(() => { + const d = detail.value; + if (!d) return ""; + const total = d.total_chapter_count || 0; + const order = Math.max(0, Math.min(d.current_chapter_order || 0, total)); + if (!order) return total ? `未读 · 共${total}章` : "未读"; + return `${order}章/${total}章`; + }); + const progressPercent = vue.computed(() => { + const d = detail.value; + if (!d || !d.total_chapter_count) return 0; + const ratio = (d.current_chapter_order || 0) / d.total_chapter_count; + return Math.max(0, Math.min(1, ratio)) * 100; + }); + function onEnter(event) { + emit("hover", { entry: props.entry, el: event.currentTarget }); + } + const imgLoaded = vue.ref(false); + vue.watch( + () => { + var _a; + return (_a = detail.value) == null ? void 0 : _a.cover_url; + }, + () => { + imgLoaded.value = false; } - } - function formatReadingTime(readBookTime) { - let minutes2 = readBookTime / 60000n; - const hours2 = minutes2 / 60n; - minutes2 = minutes2 % 60n; - return `${hours2} 时 ${minutes2} 分`; - } - function createMenuItem(text) { - const item = document.createElement("div"); - item.role = "menuitem"; - item.classList.add("fqa-menu-item", "arco-menu-item", "serial-menu-item", "slogin-user-avatar__menu-item-wrapper"); - const inner1 = document.createElement("div"); - inner1.classList.add("slogin-user-avatar__menu-item"); - const inner2 = document.createElement("div"); - inner2.classList.add("slogin-user-avatar__menu-item__content"); - inner2.textContent = text; - inner1.appendChild(inner2); - item.appendChild(inner1); - return item; - } - async function mainHook(_previous) { - const userInfo = await getDetailedUserInfo(); - if (!userInfo) { + ); + const cardRef = vue.ref(null); + let io = null; + vue.onMounted(() => { + if (typeof IntersectionObserver === "undefined") { + emit("visible", props.entry); return; } - const injected = /* @__PURE__ */ new WeakSet(); - const inject2 = (menuInner) => { - if (injected.has(menuInner)) { - return; - } - injected.add(menuInner); - console.log("user action dialog created", menuInner); - const firstDiv = createMenuItem(`阅读 ${userInfo.read_book_num} 本书`); - menuInner.insertAdjacentElement("afterbegin", firstDiv); - const secondDiv = createMenuItem(formatReadingTime(userInfo.read_book_time ?? 0n)); - firstDiv.insertAdjacentElement("afterend", secondDiv); - }; - const scan = (root) => { - if (root.classList.contains("arco-menu-inner")) { - inject2(root); - } - root.querySelectorAll(".arco-menu-inner").forEach(inject2); - }; - const observer = new MutationObserver((mutationsList) => { - mutationsList.forEach((mutation) => { - if (mutation.type === "childList") { - mutation.addedNodes.forEach((node) => { - if (node instanceof HTMLElement) { - scan(node); - } - }); + io = new IntersectionObserver( + (entries) => { + if (entries.some((e) => e.isIntersecting)) { + emit("visible", props.entry); } - }); - }); - observer.observe(document.body, { childList: true, subtree: true }); - scan(document.body); - } - function filter(_path, _query, _hash) { - return userState.isLogin; - } - const _exports = [ - { - id: "userHook", - event: "load", - filter, - handler: mainHook - } - ]; - const hooks = [ - ..._exports$2, - ..._exports$1, - ..._exports - ]; - async function onEvent(event, previous) { - console.log(`onEvent, event: ${event}, previous: ${previous}`); - const path = window.location.pathname; - const hash = window.location.hash; - const params = new URLSearchParams(window.location.search); - const tasks = []; - for (const hook of hooks) { - if (hook.event === event && hook.filter(path, params, hash)) { - tasks.push(async () => { - try { - await hook.handler(previous); - } catch (err) { - console.error(`[hook:${hook.id}] handler failed:`, err); - } - }); - } - } - console.log(`tasks: `, tasks); - if (tasks.length > 0) { - await Promise.allSettled(tasks.map((task) => task())); - } - } - async function onUrlChange(previous) { - return await onEvent("onUrlChange", previous); - } - async function onHashChange(previous) { - return await onEvent("onHashChange", previous); - } - async function onLoad() { - return await onEvent("load"); - } - const name = "fanqie-assistant"; - const version = "0.0.2"; - const win = unsafeWindow; - let previousUrl = win.location.href; - let previousHash = win.location.hash; - function installNavigationHooks() { - for (const method of ["pushState", "replaceState"]) { - const original = win.history[method]; - win.history[method] = function(...args) { - const result = original.apply(this, args); - console.debug(`history.${method} called with args:`, args); - void onUrlChange(previousUrl); - previousUrl = win.location.href; - return result; - }; - } - win.addEventListener("popstate", () => { - void onUrlChange(previousUrl); - previousUrl = win.location.href; - }); - win.addEventListener("hashchange", () => { - void onHashChange(previousHash); - previousHash = win.location.hash; - }); - } - async function mainInit() { - console.log(`================================================`); - console.log(`== ${name} - ${version} ==`); - console.log(`================================================`); - installNavigationHooks(); - initFontDecrypt(); - await inject(); - await init(); - void onLoad(); - } - mainInit(); + }, + { rootMargin: "200px" } + ); + if (cardRef.value) io.observe(cardRef.value); + }); + vue.onBeforeUnmount(() => { + io == null ? void 0 : io.disconnect(); + io = null; + }); + return (_ctx, _cache) => { + return vue.openBlock(), vue.createElementBlock("div", { + ref_key: "cardRef", + ref: cardRef, + class: "fqa-card", + role: "link", + tabindex: "0", + "aria-label": title.value, + onMouseenter: onEnter, + onMouseleave: _cache[2] || (_cache[2] = ($event) => emit("leave")), + onClick: _cache[3] || (_cache[3] = ($event) => emit("open", __props.entry)), + onKeydown: [ + _cache[4] || (_cache[4] = vue.withKeys(vue.withModifiers(($event) => emit("open", __props.entry), ["prevent"]), ["enter"])), + _cache[5] || (_cache[5] = vue.withKeys(vue.withModifiers(($event) => emit("open", __props.entry), ["prevent"]), ["space"])) + ] + }, [ + !detail.value ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 0 }, [ + _cache[6] || (_cache[6] = vue.createElementVNode("div", { class: "fqa-sk-cover fqa-sk-anim" }, null, -1)), + _cache[7] || (_cache[7] = vue.createElementVNode("div", { + class: "fqa-sk-line fqa-sk-anim", + style: { "width": "90%" } + }, null, -1)), + _cache[8] || (_cache[8] = vue.createElementVNode("div", { + class: "fqa-sk-line fqa-sk-anim", + style: { "width": "55%" } + }, null, -1)) + ], 64)) : (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 1 }, [ + vue.createElementVNode("div", _hoisted_2$3, [ + vue.createElementVNode("img", { + class: vue.normalizeClass(["fqa-cover-img", { "fqa-cover-img-loading": !imgLoaded.value }]), + crossorigin: "anonymous", + loading: "lazy", + referrerpolicy: "no-referrer", + src: detail.value.cover_url, + alt: title.value, + onLoad: _cache[0] || (_cache[0] = ($event) => imgLoaded.value = true), + onError: _cache[1] || (_cache[1] = ($event) => imgLoaded.value = true) + }, null, 42, _hoisted_3$3), + tag.value ? (vue.openBlock(), vue.createElementBlock("span", { + key: 0, + class: vue.normalizeClass(["fqa-cover-tag", tag.value.cls]) + }, vue.toDisplayString(tag.value.text), 3)) : vue.createCommentVNode("", true), + progressPercent.value > 0 ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_4$3, [ + vue.createElementVNode("span", { + class: "fqa-cover-progress-bar", + style: vue.normalizeStyle({ width: progressPercent.value + "%" }) + }, null, 4) + ])) : vue.createCommentVNode("", true) + ]), + vue.createElementVNode("div", { + class: "fqa-card-title", + title: title.value + }, vue.toDisplayString(title.value), 9, _hoisted_5$3), + vue.createElementVNode("div", { + class: "fqa-card-sub", + title: detail.value.current_chapter_title + }, vue.toDisplayString(progressText.value), 9, _hoisted_6$3) + ], 64)) + ], 40, _hoisted_1$3); + }; } }); - require_main_001(); + const _hoisted_1$2 = ["aria-label"]; + const _hoisted_2$2 = { class: "fqa-group-cover" }; + const _hoisted_3$2 = { class: "fqa-group-grid" }; + const _hoisted_4$2 = ["src", "alt"]; + const _hoisted_5$2 = ["title"]; + const _hoisted_6$2 = { class: "fqa-card-sub" }; + const _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({ + __name: "BookGroupCard", + props: { + group: {} + }, + emits: ["open", "visible"], + setup(__props, { emit: __emit }) { + const props = __props; + const emit = __emit; + const previewBooks = vue.computed(() => props.group.books.slice(0, 4)); + const covers = vue.computed( + () => previewBooks.value.map((entry) => entry.detail).filter((detail) => !!detail) + ); + const cardRef = vue.ref(null); + let io = null; + function request() { + const pending = previewBooks.value.filter((entry) => !entry.detail); + if (pending.length) emit("visible", pending); + } + vue.onMounted(() => { + if (typeof IntersectionObserver === "undefined") { + request(); + return; + } + io = new IntersectionObserver( + (entries) => { + if (entries.some((e) => e.isIntersecting)) request(); + }, + { rootMargin: "200px" } + ); + if (cardRef.value) io.observe(cardRef.value); + }); + vue.onBeforeUnmount(() => { + io == null ? void 0 : io.disconnect(); + io = null; + }); + return (_ctx, _cache) => { + return vue.openBlock(), vue.createElementBlock("div", { + ref_key: "cardRef", + ref: cardRef, + class: "fqa-card", + role: "button", + tabindex: "0", + "aria-label": `分组 ${__props.group.name}`, + onClick: _cache[0] || (_cache[0] = ($event) => emit("open", __props.group)), + onKeydown: [ + _cache[1] || (_cache[1] = vue.withKeys(vue.withModifiers(($event) => emit("open", __props.group), ["prevent"]), ["enter"])), + _cache[2] || (_cache[2] = vue.withKeys(vue.withModifiers(($event) => emit("open", __props.group), ["prevent"]), ["space"])) + ] + }, [ + vue.createElementVNode("div", _hoisted_2$2, [ + vue.createElementVNode("div", _hoisted_3$2, [ + (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(covers.value, (detail) => { + return vue.openBlock(), vue.createElementBlock("div", { + key: detail.book_id, + class: "fqa-group-cell" + }, [ + vue.createElementVNode("img", { + crossorigin: "anonymous", + loading: "lazy", + referrerpolicy: "no-referrer", + src: detail.cover_url, + alt: detail.title + }, null, 8, _hoisted_4$2) + ]); + }), 128)) + ]) + ]), + vue.createElementVNode("div", { + class: "fqa-card-title", + title: __props.group.name + }, vue.toDisplayString(__props.group.name), 9, _hoisted_5$2), + vue.createElementVNode("div", _hoisted_6$2, "共" + vue.toDisplayString(__props.group.books.length) + "本书", 1) + ], 40, _hoisted_1$2); + }; + } + }); + const SECOND = 1e3; + const MINUTE = 60 * SECOND; + const HOUR = 60 * MINUTE; + const DAY = 24 * HOUR; + const MONTH = 30 * DAY; + const YEAR = 365 * DAY; + function normalizeTimestamp(ts) { + if (!ts || ts <= 0) return 0; + return ts < 1e12 ? ts * 1e3 : ts; + } + function fromNow(ts) { + const time = normalizeTimestamp(ts); + if (!time) return "从未"; + const diff = Date.now() - time; + if (diff < MINUTE) return "刚刚"; + if (diff < HOUR) return `${Math.floor(diff / MINUTE)}分钟前`; + if (diff < DAY) return `${Math.floor(diff / HOUR)}小时前`; + if (diff < MONTH) return `${Math.floor(diff / DAY)}天前`; + if (diff < YEAR) return `${Math.floor(diff / MONTH)}个月前`; + return `${Math.floor(diff / YEAR)}年前`; + } + function formatDateTime(ts) { + const time = normalizeTimestamp(ts); + if (!time) return "未知"; + const d = new Date(time); + const pad = (n) => String(n).padStart(2, "0"); + return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`; + } + const _hoisted_1$1 = ["title"]; + const _hoisted_2$1 = { + key: 0, + class: "fqa-hover-author" + }; + const _hoisted_3$1 = { class: "fqa-hover-stats" }; + const _hoisted_4$1 = ["title"]; + const _hoisted_5$1 = { class: "fqa-hover-stat-k" }; + const _hoisted_6$1 = { class: "fqa-hover-stat" }; + const _hoisted_7$1 = { class: "fqa-hover-stat-v" }; + const _hoisted_8$1 = { class: "fqa-hover-stat" }; + const _hoisted_9$1 = { class: "fqa-hover-stat-v" }; + const _hoisted_10$1 = { class: "fqa-hover-seg" }; + const _hoisted_11$1 = { class: "fqa-hover-abstract" }; + const _hoisted_12$1 = { + key: 0, + class: "fqa-hover-chapter" + }; + const _sfc_main$1 = /* @__PURE__ */ vue.defineComponent({ + __name: "BookHoverCard", + props: { + entry: {}, + x: {}, + y: {}, + height: {}, + visible: { type: Boolean } + }, + emits: ["panel-enter", "panel-leave"], + setup(__props, { emit: __emit }) { + const props = __props; + const emit = __emit; + const detail = vue.computed(() => { + var _a; + return ((_a = props.entry) == null ? void 0 : _a.detail) ?? null; + }); + const item = vue.computed(() => { + var _a; + return ((_a = props.entry) == null ? void 0 : _a.item) ?? null; + }); + const readAt = vue.computed(() => { + var _a; + return fromNow((_a = item.value) == null ? void 0 : _a.last_read_timestamp); + }); + const addedAt = vue.computed(() => { + var _a; + return fromNow((_a = item.value) == null ? void 0 : _a.add_shelf_time); + }); + const updatedAt = vue.computed(() => { + var _a; + return fromNow((_a = detail.value) == null ? void 0 : _a.last_chapter_update_time); + }); + const updatedAtFull = vue.computed(() => { + var _a; + return formatDateTime((_a = detail.value) == null ? void 0 : _a.last_chapter_update_time); + }); + const showUpdateTime = vue.ref(false); + const latestChapter = vue.computed(() => { + const d = detail.value; + if (!d) return "—"; + return d.total_chapter_count ? `${d.total_chapter_count}章` : "—"; + }); + const pickedTab = vue.ref(null); + const chapterText = vue.computed(() => { + var _a, _b; + return ((_b = (_a = detail.value) == null ? void 0 : _a.current_chapter_summary) == null ? void 0 : _b.trim()) ?? ""; + }); + const bookText = vue.computed(() => { + var _a, _b; + return ((_b = (_a = detail.value) == null ? void 0 : _a.summary) == null ? void 0 : _b.trim()) ?? ""; + }); + const activeTab = vue.computed(() => { + if (pickedTab.value) return pickedTab.value; + return chapterText.value ? "chapter" : "book"; + }); + const abstractText = vue.computed(() => { + const text = activeTab.value === "chapter" ? chapterText.value : bookText.value; + return text || "暂无数据"; + }); + vue.watch( + () => { + var _a; + return (_a = props.entry) == null ? void 0 : _a.item.book_id; + }, + () => { + pickedTab.value = null; + showUpdateTime.value = false; + } + ); + const root = vue.ref(null); + const supportsPopover = typeof HTMLElement !== "undefined" && typeof HTMLElement.prototype.showPopover === "function"; + vue.watch( + () => props.visible && !!detail.value, + (show) => { + const el = root.value; + if (!el || !supportsPopover) return; + try { + if (show) el.showPopover(); + else el.hidePopover(); + } catch { + } + } + ); + return (_ctx, _cache) => { + return vue.openBlock(), vue.createElementBlock("div", { + id: "fqa-bookshelf-hover", + ref_key: "root", + ref: root, + popover: "manual", + class: vue.normalizeClass({ "fqa-visible": __props.visible && !!detail.value }), + style: vue.normalizeStyle({ left: __props.x + "px", top: __props.y + "px" }), + onMouseenter: _cache[4] || (_cache[4] = ($event) => emit("panel-enter")), + onMouseleave: _cache[5] || (_cache[5] = ($event) => emit("panel-leave")) + }, [ + detail.value ? (vue.openBlock(), vue.createElementBlock("div", { + key: 0, + class: "fqa-hover-inner", + style: vue.normalizeStyle(__props.height ? { height: __props.height + "px" } : void 0) + }, [ + vue.createElementVNode("div", { + class: "fqa-hover-title", + title: detail.value.title + }, vue.toDisplayString(detail.value.title), 9, _hoisted_1$1), + detail.value.author ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_2$1, vue.toDisplayString(detail.value.author), 1)) : vue.createCommentVNode("", true), + vue.createElementVNode("div", _hoisted_3$1, [ + vue.createElementVNode("div", { + class: "fqa-hover-stat", + onMouseenter: _cache[0] || (_cache[0] = ($event) => showUpdateTime.value = true), + onMouseleave: _cache[1] || (_cache[1] = ($event) => showUpdateTime.value = false) + }, [ + vue.createElementVNode("div", { + class: "fqa-hover-stat-v", + title: updatedAtFull.value + }, vue.toDisplayString(showUpdateTime.value ? updatedAt.value : latestChapter.value), 9, _hoisted_4$1), + vue.createElementVNode("div", _hoisted_5$1, vue.toDisplayString(showUpdateTime.value ? "更新于" : "最新章"), 1) + ], 32), + vue.createElementVNode("div", _hoisted_6$1, [ + vue.createElementVNode("div", _hoisted_7$1, vue.toDisplayString(readAt.value), 1), + _cache[6] || (_cache[6] = vue.createElementVNode("div", { class: "fqa-hover-stat-k" }, "阅读过", -1)) + ]), + vue.createElementVNode("div", _hoisted_8$1, [ + vue.createElementVNode("div", _hoisted_9$1, vue.toDisplayString(addedAt.value), 1), + _cache[7] || (_cache[7] = vue.createElementVNode("div", { class: "fqa-hover-stat-k" }, "已加入书架", -1)) + ]) + ]), + vue.createElementVNode("div", _hoisted_10$1, [ + vue.createElementVNode("button", { + class: vue.normalizeClass(["fqa-hover-seg-btn", { "fqa-hover-seg-active": activeTab.value === "chapter" }]), + onClick: _cache[2] || (_cache[2] = ($event) => pickedTab.value = "chapter") + }, " 本章梗概 ", 2), + vue.createElementVNode("button", { + class: vue.normalizeClass(["fqa-hover-seg-btn", { "fqa-hover-seg-active": activeTab.value === "book" }]), + onClick: _cache[3] || (_cache[3] = ($event) => pickedTab.value = "book") + }, " 全书简介 ", 2) + ]), + vue.createElementVNode("div", _hoisted_11$1, [ + activeTab.value === "chapter" && detail.value.current_chapter_title ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_12$1, vue.toDisplayString(detail.value.current_chapter_title), 1)) : vue.createCommentVNode("", true), + vue.createTextVNode(" " + vue.toDisplayString(abstractText.value), 1) + ]) + ], 4)) : vue.createCommentVNode("", true) + ], 38); + }; + } + }); + async function getBookshelf() { + const responses = await Promise.all([ + fetch("https://fanqienovel.com/reading/bookapi/bookshelf/info/v:version/?aid=1967&iid=0&version_code=57700&update_version_code=57700"), + fetch("https://fanqienovel.com/api/reader/book/progress") + ]); + const response = responses[0]; + const response2 = responses[1]; + const data = await response.json(); + const info = data.data.book_shelf_info; + const data_ = await response2.json(); + const data2 = data_.data; + const all_items = info.map((item) => item.book_id); + const response3 = await fetch("https://fanqienovel.com/api/book/simple/info", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + book_ids: all_items + }) + }); + const data__ = await response3.json(); + const data3 = data__.data.bookList; + const results = []; + for (let i of data.data.book_shelf_info) { + const progress = data2.find((it) => it.book_id === i.book_id); + const simple = data3.find((it) => it.book_id === i.book_id); + const item = { + book_id: i.book_id, + last_operate_time: progress ? progress.read_timestamp : i.last_operate_time, + add_shelf_time: i.add_shelf_time, + group_name: i.group_name, + last_read_timestamp: progress ? progress.read_timestamp : 0, + last_read_chapter_id: progress ? progress.item_id : "0", + is_publish: (simple == null ? void 0 : simple.genre) === "6" + }; + results.push(item); + } + return results; + } + async function multidetail(books) { + const response = await fetch("https://fanqienovel.com/api/bookshelf/multidetail", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + books: books.map((book) => { + return { + book_id: book.book_id, + item_id: book.last_read_chapter_id + }; + }) + }) + }); + const data = await response.json(); + const list = data.data.detail_list; + const results = []; + for (let i of list) { + const item = { + book_id: i.book_id, + summary: i.abstract, + title: i.book_name, + author: i.author_name, + cover_url: i.thumb_url, + current_chapter_title: i.item_show_title, + current_chapter_id: i.item_id, + current_chapter_order: i.real_chapter_order, + total_chapter_count: i.serial_count, + last_chapter_update_time: i.last_chapter_update_time * 1e3, + last_chapter_id: i.last_chapter_item_id, + current_chapter_summary: i.item_abstract, + // last_chapter_title: i.last_chapter_show_title, + status: i.update_stop === "1" ? 4 : i.creation_status, + update_status: i.update_status + }; + results.push(item); + } + return results; + } + const DETAIL_BATCH_SIZE = 20; + const PAGE_SIZE = 24; + const TABS = [ + { key: "all", label: "全部" }, + { key: "group", label: "分组" }, + { key: "publish", label: "出版" } + ]; + function useBookshelf() { + const entries = vue.ref([]); + const loading = vue.ref(false); + const detailLoading = vue.ref(false); + const error = vue.ref(null); + let loadToken = 0; + let detailCache = /* @__PURE__ */ new Map(); + let inflight = /* @__PURE__ */ new Set(); + function applyDetails(details) { + if (!details.length) return; + const byId = new Map(details.map((d) => [d.book_id, d])); + entries.value = entries.value.map((entry) => { + const detail = byId.get(entry.item.book_id); + return detail ? { ...entry, detail } : entry; + }); + } + async function ensureDetails(items) { + const token = loadToken; + const pending = items.filter( + (item) => !detailCache.has(item.book_id) && !inflight.has(item.book_id) + ); + if (!pending.length) return; + pending.forEach((item) => inflight.add(item.book_id)); + detailLoading.value = true; + try { + for (const batch of chunk(pending, DETAIL_BATCH_SIZE)) { + if (token !== loadToken) return; + try { + const details = await multidetail(batch); + if (token !== loadToken) return; + details.forEach((d) => detailCache.set(d.book_id, d)); + applyDetails(details); + } catch (err) { + console.error("[fqa:bookshelf] 加载书籍详情失败:", err); + } + } + } finally { + pending.forEach((item) => inflight.delete(item.book_id)); + if (token === loadToken && inflight.size === 0) detailLoading.value = false; + } + } + async function load(force = false) { + const token = ++loadToken; + inflight.clear(); + if (force) detailCache = /* @__PURE__ */ new Map(); + loading.value = true; + error.value = null; + try { + const items = await getBookshelf(); + if (token !== loadToken) return; + items.sort((a, b) => b.last_operate_time - a.last_operate_time); + entries.value = items.map((item) => ({ + item, + detail: detailCache.get(item.book_id) ?? null + })); + loading.value = false; + } catch (err) { + if (token !== loadToken) return; + console.error("[fqa:bookshelf] 加载书架失败:", err); + error.value = err instanceof Error ? err.message : String(err); + loading.value = false; + } + } + const groups = vue.computed(() => { + const map = /* @__PURE__ */ new Map(); + for (const entry of entries.value) { + const name2 = entry.item.group_name; + if (!name2) continue; + let group = map.get(name2); + if (!group) { + group = { name: name2, books: [], last_operate_time: 0 }; + map.set(name2, group); + } + group.books.push(entry); + group.last_operate_time = Math.max(group.last_operate_time, entry.item.last_operate_time); + } + return [...map.values()].sort((a, b) => b.last_operate_time - a.last_operate_time); + }); + const allCells = vue.computed(() => { + const sortable = []; + for (const entry of entries.value) { + if (entry.item.group_name) continue; + sortable.push({ + time: entry.item.last_operate_time, + cell: { kind: "book", key: `book:${entry.item.book_id}`, entry } + }); + } + for (const group of groups.value) { + sortable.push({ + time: group.last_operate_time, + cell: { kind: "group", key: `group:${group.name}`, group } + }); + } + return sortable.sort((a, b) => b.time - a.time).map((s) => s.cell); + }); + const groupCells = vue.computed( + () => groups.value.map((group) => ({ kind: "group", key: `group:${group.name}`, group })) + ); + const publishCells = vue.computed( + () => entries.value.filter((entry) => entry.item.is_publish).map((entry) => ({ kind: "book", key: `book:${entry.item.book_id}`, entry })) + ); + const counts = vue.computed(() => ({ + all: entries.value.length, + group: groups.value.length, + publish: entries.value.filter((entry) => entry.item.is_publish).length + })); + function cellsOf(tab) { + if (tab === "group") return groupCells.value; + if (tab === "publish") return publishCells.value; + return allCells.value; + } + function findGroup(name2) { + return groups.value.find((group) => group.name === name2) ?? null; + } + return { + entries, + loading, + detailLoading, + error, + groups, + counts, + load, + ensureDetails, + cellsOf, + findGroup, + PAGE_SIZE + }; + } + const _hoisted_1 = { id: "fqa-bookshelf" }; + const _hoisted_2 = { class: "fqa-bs-header" }; + const _hoisted_3 = { class: "fqa-bs-actions" }; + const _hoisted_4 = { key: 0 }; + const _hoisted_5 = ["disabled"]; + const _hoisted_6 = ["aria-selected", "onClick", "onKeydown"]; + const _hoisted_7 = { class: "fqa-tab-count" }; + const _hoisted_8 = { + key: 0, + class: "fqa-groupbar" + }; + const _hoisted_9 = { class: "fqa-groupbar-name" }; + const _hoisted_10 = { class: "fqa-groupbar-count" }; + const _hoisted_11 = { + key: 1, + class: "fqa-status" + }; + const _hoisted_12 = { + key: 2, + class: "fqa-status" + }; + const _hoisted_13 = { class: "fqa-status-title" }; + const _hoisted_14 = { class: "fqa-grid" }; + const HOVER_DELAY = 300; + const HIDE_DELAY = 160; + const HOVER_WIDTH = 280; + const HOVER_GAP = 12; + const VIEWPORT_MARGIN = 8; + const _sfc_main = /* @__PURE__ */ vue.defineComponent({ + __name: "BookshelfView", + setup(__props) { + const { loading, detailLoading, error, counts, load, ensureDetails, cellsOf, findGroup, PAGE_SIZE: PAGE_SIZE2 } = useBookshelf(); + const activeTab = vue.ref("all"); + const openedGroupName = vue.ref(null); + const tabsRef = vue.ref(null); + const inkStyle = vue.ref({ left: "0px", width: "0px" }); + const hoverEntry = vue.ref(null); + const hoverVisible = vue.ref(false); + const hoverPos = vue.ref({ x: 0, y: 0 }); + const hoverHeight = vue.ref(0); + let hoverTimer; + let hideTimer; + let hoverAnchor = null; + const pointerInPanel = vue.ref(false); + const openedGroup = vue.computed( + () => openedGroupName.value ? findGroup(openedGroupName.value) : null + ); + const allCells = vue.computed(() => { + const group = openedGroup.value; + if (group) { + return group.books.map((entry) => ({ + kind: "book", + key: `book:${entry.item.book_id}`, + entry + })); + } + return cellsOf(activeTab.value); + }); + const visibleCount = vue.ref(PAGE_SIZE2); + const cells = vue.computed(() => allCells.value.slice(0, visibleCount.value)); + const hasMore = vue.computed(() => visibleCount.value < allCells.value.length); + const sentinel = vue.ref(null); + let pageObserver = null; + function resetPaging() { + visibleCount.value = PAGE_SIZE2; + } + async function refresh() { + resetPaging(); + await load(true); + } + vue.watch([activeTab, openedGroupName], resetPaging); + const isEmpty = vue.computed(() => !loading.value && !error.value && allCells.value.length === 0); + const emptyText = vue.computed(() => { + if (openedGroup.value) return "这个分组还没有书"; + if (activeTab.value === "group") return "还没有创建任何分组"; + if (activeTab.value === "publish") return "书架里还没有出版物"; + return "书架空空如也,去首页找几本书看看吧"; + }); + function updateInk() { + const wrap = tabsRef.value; + if (!wrap) return; + const index = TABS.findIndex((tab) => tab.key === activeTab.value); + const el = wrap.querySelectorAll(".fqa-tab")[index]; + if (!el) return; + inkStyle.value = { + left: `${el.offsetLeft}px`, + width: `${el.offsetWidth}px` + }; + } + function selectTab(key) { + openedGroupName.value = null; + activeTab.value = key; + hideHover(true); + } + vue.watch(activeTab, () => vue.nextTick(updateInk)); + vue.watch(counts, () => vue.nextTick(updateInk), { deep: true }); + function computePosition(el) { + const cover = el.querySelector(".fqa-cover") ?? el; + const rect = cover.getBoundingClientRect(); + let x = rect.right + HOVER_GAP; + if (x + HOVER_WIDTH > window.innerWidth - VIEWPORT_MARGIN) { + x = rect.left - HOVER_WIDTH - HOVER_GAP; + } + x = Math.max(VIEWPORT_MARGIN, Math.min(x, window.innerWidth - HOVER_WIDTH - VIEWPORT_MARGIN)); + const topLimit = VIEWPORT_MARGIN; + const bottomLimit = window.innerHeight - VIEWPORT_MARGIN; + const height = Math.min(rect.height, bottomLimit - topLimit); + const y = Math.max(topLimit, Math.min(rect.top, bottomLimit - height)); + hoverHeight.value = height; + hoverPos.value = { x, y }; + } + function onCardHover({ entry, el }) { + if (hoverTimer) clearTimeout(hoverTimer); + if (hideTimer) { + clearTimeout(hideTimer); + hideTimer = void 0; + } + if (!entry.detail) return; + hoverAnchor = el; + if (hoverVisible.value && hoverEntry.value !== entry) { + computePosition(el); + hoverEntry.value = entry; + return; + } + hoverTimer = setTimeout(() => { + hoverTimer = void 0; + computePosition(el); + hoverEntry.value = entry; + hoverVisible.value = true; + }, HOVER_DELAY); + } + function scheduleHide() { + if (hoverTimer) { + clearTimeout(hoverTimer); + hoverTimer = void 0; + } + if (hideTimer) clearTimeout(hideTimer); + hideTimer = setTimeout(() => { + hideTimer = void 0; + if (!pointerInPanel.value) hideHover(true); + }, HIDE_DELAY); + } + function hideHover(immediate = false) { + if (hoverTimer) { + clearTimeout(hoverTimer); + hoverTimer = void 0; + } + if (hideTimer) { + clearTimeout(hideTimer); + hideTimer = void 0; + } + pointerInPanel.value = false; + hoverAnchor = null; + hoverVisible.value = false; + if (immediate) hoverEntry.value = null; + } + function onPanelEnter() { + if (hideTimer) { + clearTimeout(hideTimer); + hideTimer = void 0; + } + pointerInPanel.value = true; + } + function onPanelLeave() { + pointerInPanel.value = false; + scheduleHide(); + } + let pendingItems = []; + let flushTimer; + function queueDetails(entries) { + const pending = entries.filter((entry) => !entry.detail); + if (!pending.length) return; + pendingItems.push(...pending.map((entry) => entry.item)); + if (flushTimer) return; + flushTimer = setTimeout(() => { + flushTimer = void 0; + const batch = pendingItems; + pendingItems = []; + void ensureDetails(batch); + }, 50); + } + function onCardVisible(entry) { + queueDetails([entry]); + } + function onGroupVisible(entries) { + queueDetails(entries); + } + function openBook(entry) { + var _a; + hideHover(true); + const chapterId = ((_a = entry.detail) == null ? void 0 : _a.current_chapter_id) || entry.item.last_read_chapter_id; + const url = chapterId && chapterId !== "0" ? `https://fanqienovel.com/reader/${chapterId}` : `https://fanqienovel.com/page/${entry.item.book_id}`; + unsafeWindow.location.href = url; + } + function openGroup(group) { + hideHover(true); + openedGroupName.value = group.name; + } + function backToList() { + openedGroupName.value = null; + hideHover(true); + } + function onScrollOrResize() { + if (hoverAnchor && (hoverVisible.value || hoverTimer)) { + if (hoverAnchor.isConnected) { + computePosition(hoverAnchor); + } else { + hideHover(true); + } + } + updateInk(); + } + vue.onMounted(async () => { + await load(); + await vue.nextTick(); + updateInk(); + window.addEventListener("scroll", onScrollOrResize, true); + window.addEventListener("resize", onScrollOrResize); + if (typeof IntersectionObserver !== "undefined") { + pageObserver = new IntersectionObserver( + (entries) => { + if (entries.some((e) => e.isIntersecting) && hasMore.value) { + visibleCount.value += PAGE_SIZE2; + } + }, + { rootMargin: "400px" } + ); + vue.watch( + sentinel, + (el) => { + pageObserver == null ? void 0 : pageObserver.disconnect(); + if (el) pageObserver == null ? void 0 : pageObserver.observe(el); + }, + { immediate: true } + ); + } + }); + vue.onBeforeUnmount(() => { + if (hoverTimer) clearTimeout(hoverTimer); + if (hideTimer) clearTimeout(hideTimer); + if (flushTimer) clearTimeout(flushTimer); + pageObserver == null ? void 0 : pageObserver.disconnect(); + pageObserver = null; + window.removeEventListener("scroll", onScrollOrResize, true); + window.removeEventListener("resize", onScrollOrResize); + }); + return (_ctx, _cache) => { + return vue.openBlock(), vue.createElementBlock("div", _hoisted_1, [ + vue.createElementVNode("div", _hoisted_2, [ + _cache[0] || (_cache[0] = vue.createElementVNode("div", { class: "fqa-bs-title" }, "我的书架", -1)), + vue.createElementVNode("div", _hoisted_3, [ + vue.unref(detailLoading) ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_4, "正在补全详情…")) : vue.createCommentVNode("", true), + vue.createElementVNode("button", { + class: "fqa-btn", + disabled: vue.unref(loading), + onClick: refresh + }, vue.toDisplayString(vue.unref(loading) ? "刷新中…" : "刷新"), 9, _hoisted_5) + ]) + ]), + vue.createElementVNode("div", { + ref_key: "tabsRef", + ref: tabsRef, + class: "fqa-tabs", + role: "tablist" + }, [ + (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(vue.unref(TABS), (tab) => { + return vue.openBlock(), vue.createElementBlock("div", { + key: tab.key, + class: vue.normalizeClass(["fqa-tab", { "fqa-tab-active": activeTab.value === tab.key }]), + role: "tab", + tabindex: "0", + "aria-selected": activeTab.value === tab.key, + onClick: ($event) => selectTab(tab.key), + onKeydown: vue.withKeys(vue.withModifiers(($event) => selectTab(tab.key), ["prevent"]), ["enter"]) + }, [ + vue.createTextVNode(vue.toDisplayString(tab.label), 1), + vue.createElementVNode("span", _hoisted_7, vue.toDisplayString(vue.unref(counts)[tab.key]), 1) + ], 42, _hoisted_6); + }), 128)), + vue.createElementVNode("span", { + class: "fqa-tab-ink", + style: vue.normalizeStyle(inkStyle.value) + }, null, 4) + ], 512), + openedGroup.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_8, [ + vue.createElementVNode("button", { + class: "fqa-btn", + onClick: backToList + }, "← 返回"), + vue.createElementVNode("span", _hoisted_9, vue.toDisplayString(openedGroup.value.name), 1), + vue.createElementVNode("span", _hoisted_10, "共" + vue.toDisplayString(openedGroup.value.books.length) + "本书", 1) + ])) : vue.createCommentVNode("", true), + vue.unref(error) ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_11, [ + _cache[1] || (_cache[1] = vue.createElementVNode("div", { class: "fqa-status-title" }, "书架加载失败", -1)), + vue.createElementVNode("div", null, vue.toDisplayString(vue.unref(error)), 1), + vue.createElementVNode("button", { + class: "fqa-btn", + onClick: refresh + }, "重试") + ])) : isEmpty.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_12, [ + vue.createElementVNode("div", _hoisted_13, vue.toDisplayString(emptyText.value), 1) + ])) : (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 3 }, [ + vue.createElementVNode("div", _hoisted_14, [ + vue.unref(loading) ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 0 }, vue.renderList(8, (n) => { + return vue.createElementVNode("div", { + key: `sk-${n}`, + class: "fqa-card" + }, [..._cache[2] || (_cache[2] = [ + vue.createElementVNode("div", { class: "fqa-sk-cover fqa-sk-anim" }, null, -1), + vue.createElementVNode("div", { + class: "fqa-sk-line fqa-sk-anim", + style: { "width": "90%" } + }, null, -1), + vue.createElementVNode("div", { + class: "fqa-sk-line fqa-sk-anim", + style: { "width": "55%" } + }, null, -1) + ])]); + }), 64)) : (vue.openBlock(true), vue.createElementBlock(vue.Fragment, { key: 1 }, vue.renderList(cells.value, (cell) => { + return vue.openBlock(), vue.createElementBlock(vue.Fragment, { + key: cell.key + }, [ + cell.kind === "book" ? (vue.openBlock(), vue.createBlock(_sfc_main$3, { + key: 0, + entry: cell.entry, + onHover: onCardHover, + onLeave: scheduleHide, + onOpen: openBook, + onVisible: onCardVisible + }, null, 8, ["entry"])) : (vue.openBlock(), vue.createBlock(_sfc_main$2, { + key: 1, + group: cell.group, + onOpen: openGroup, + onVisible: onGroupVisible + }, null, 8, ["group"])) + ], 64); + }), 128)) + ]), + !vue.unref(loading) && hasMore.value ? (vue.openBlock(), vue.createElementBlock("div", { + key: 0, + ref_key: "sentinel", + ref: sentinel, + class: "fqa-loadmore" + }, "加载中…", 512)) : vue.createCommentVNode("", true) + ], 64)), + (vue.openBlock(), vue.createBlock(vue.Teleport, { to: "body" }, [ + vue.createVNode(_sfc_main$1, { + entry: hoverEntry.value, + x: hoverPos.value.x, + y: hoverPos.value.y, + height: hoverHeight.value, + visible: hoverVisible.value, + onPanelEnter, + onPanelLeave + }, null, 8, ["entry", "x", "y", "height", "visible"]) + ])) + ]); + }; + } + }); + const bookshelfcss = "#fqa-bookshelf {\n --fqa-text: #1f2329;\n --fqa-text-sub: #646a73;\n --fqa-text-weak: #8f959e;\n --fqa-border: rgba(31, 35, 41, 0.08);\n --fqa-hover: rgba(31, 35, 41, 0.04);\n --fqa-accent: #ff6f3d;\n --fqa-skeleton: rgba(31, 35, 41, 0.06);\n --fqa-skeleton-hl: rgba(31, 35, 41, 0.12);\n --fqa-shadow: 0 4px 16px rgba(31, 35, 41, 0.08);\n\n display: block;\n box-sizing: border-box;\n width: 100%;\n max-width: 1100px;\n margin: 0 auto;\n /* 顶部留出原站 fixed 顶栏(80px)的高度,否则标题和 tab 会被压在下面 */\n padding: calc(80px + 24px) 16px 64px;\n color: var(--fqa-text);\n font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', Arial,\n sans-serif;\n}\n\n#fqa-bookshelf *,\n#fqa-bookshelf *::before,\n#fqa-bookshelf *::after {\n box-sizing: border-box;\n}\n\n#fqa-bookshelf div,\n#fqa-bookshelf span,\n#fqa-bookshelf h1,\n#fqa-bookshelf ul,\n#fqa-bookshelf li {\n margin: 0;\n padding: 0;\n border: 0;\n list-style: none;\n float: none;\n position: static;\n}\n\n/* ------------------------------ 顶部 / Tabs ------------------------------ */\n\n#fqa-bookshelf .fqa-bs-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n margin-bottom: 8px;\n}\n\n#fqa-bookshelf .fqa-bs-title {\n font-size: 24px;\n font-weight: 600;\n line-height: 1.4;\n}\n\n#fqa-bookshelf .fqa-bs-actions {\n display: flex;\n align-items: center;\n gap: 12px;\n font-size: 13px;\n color: var(--fqa-text-weak);\n}\n\n#fqa-bookshelf .fqa-btn {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 6px 14px;\n border: 1px solid var(--fqa-border);\n border-radius: 999px;\n background: transparent;\n color: var(--fqa-text-sub);\n font-size: 13px;\n font-family: inherit;\n line-height: 1.4;\n cursor: pointer;\n transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;\n}\n\n#fqa-bookshelf .fqa-btn:hover:not(:disabled) {\n border-color: var(--fqa-accent);\n color: var(--fqa-accent);\n background: rgba(255, 111, 61, 0.06);\n}\n\n#fqa-bookshelf .fqa-btn:disabled {\n opacity: 0.5;\n cursor: default;\n}\n\n#fqa-bookshelf .fqa-tabs {\n position: relative;\n display: flex;\n align-items: center;\n gap: 4px;\n margin-bottom: 24px;\n border-bottom: 1px solid var(--fqa-border);\n}\n\n#fqa-bookshelf .fqa-tab {\n padding: 10px 16px;\n color: var(--fqa-text-sub);\n font-size: 15px;\n line-height: 22px;\n cursor: pointer;\n user-select: none;\n transition: color 0.15s ease;\n}\n\n#fqa-bookshelf .fqa-tab:hover {\n color: var(--fqa-text);\n}\n\n#fqa-bookshelf .fqa-tab-active {\n color: var(--fqa-accent);\n font-weight: 600;\n}\n\n#fqa-bookshelf .fqa-tab-count {\n margin-left: 4px;\n font-size: 12px;\n font-weight: 400;\n color: var(--fqa-text-weak);\n}\n\n#fqa-bookshelf .fqa-tab-ink {\n position: absolute;\n bottom: -1px;\n left: 0;\n width: 0;\n height: 2px;\n border-radius: 2px;\n background: var(--fqa-accent);\n transition: left 0.25s ease, width 0.25s ease;\n}\n\n/* ------------------------------- 书架网格 ------------------------------- */\n\n/* 原站一排最多 4 本;窄屏逐级降到 3 / 2 */\n#fqa-bookshelf .fqa-grid {\n display: grid;\n grid-template-columns: repeat(4, minmax(0, 1fr));\n gap: 28px 24px;\n align-items: start;\n}\n\n@media (max-width: 900px) {\n #fqa-bookshelf .fqa-grid {\n grid-template-columns: repeat(3, minmax(0, 1fr));\n }\n}\n\n@media (max-width: 600px) {\n #fqa-bookshelf .fqa-grid {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n}\n\n#fqa-bookshelf .fqa-card {\n display: block;\n border-radius: 8px;\n cursor: pointer;\n outline: none;\n}\n\n#fqa-bookshelf .fqa-card:focus-visible {\n box-shadow: 0 0 0 2px var(--fqa-accent);\n}\n\n/* 封面:3:4,靠 aspect-ratio 定高,内部元素绝对定位 */\n#fqa-bookshelf .fqa-cover {\n position: relative;\n display: block;\n width: 100%;\n aspect-ratio: 3 / 4;\n border-radius: 6px;\n overflow: hidden;\n background: var(--fqa-skeleton);\n transition: transform 0.2s ease, box-shadow 0.2s ease;\n}\n\n#fqa-bookshelf .fqa-card:hover .fqa-cover {\n transform: translateY(-4px);\n box-shadow: var(--fqa-shadow);\n}\n\n#fqa-bookshelf .fqa-cover-img {\n position: absolute;\n inset: 0;\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n transition: opacity 0.25s ease;\n}\n\n#fqa-bookshelf .fqa-cover-img-loading {\n opacity: 0;\n}\n\n#fqa-bookshelf .fqa-cover-tag {\n position: absolute;\n top: 0;\n right: 0;\n z-index: 2;\n padding: 2px 6px;\n border-radius: 0 6px 0 6px;\n background: var(--fqa-accent);\n color: #fff;\n font-size: 11px;\n line-height: 16px;\n font-weight: 500;\n white-space: nowrap;\n}\n\n/* 连载 / 完结 / 断更共用:灰底,弱化于“更新”角标 */\n#fqa-bookshelf .fqa-cover-tag-gray {\n background: rgba(31, 35, 41, 0.55);\n}\n\n@media (prefers-color-scheme: dark) {\n #fqa-bookshelf .fqa-cover-tag-gray {\n background: rgba(0, 0, 0, 0.6);\n }\n}\n\n#fqa-bookshelf .fqa-cover-progress {\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 2;\n height: 3px;\n background: rgba(255, 255, 255, 0.35);\n}\n\n#fqa-bookshelf .fqa-cover-progress-bar {\n display: block;\n height: 100%;\n background: var(--fqa-accent);\n transition: width 0.3s ease;\n}\n\n/* 文字区:与封面同为普通流元素,不会重叠 */\n#fqa-bookshelf .fqa-card-title {\n display: -webkit-box;\n margin-top: 8px;\n color: var(--fqa-text);\n font-size: 14px;\n line-height: 20px;\n font-weight: 500;\n -webkit-line-clamp: 2;\n line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n word-break: break-all;\n}\n\n#fqa-bookshelf .fqa-card-sub {\n margin-top: 4px;\n color: var(--fqa-text-weak);\n font-size: 12px;\n line-height: 18px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* ------------------------------- 分组卡片 ------------------------------- */\n\n#fqa-bookshelf .fqa-group-cover {\n position: relative;\n display: block;\n width: 100%;\n aspect-ratio: 3 / 4;\n border-radius: 6px;\n overflow: hidden;\n background: linear-gradient(135deg, rgba(255, 111, 61, 0.12), rgba(78, 131, 253, 0.12));\n transition: transform 0.2s ease, box-shadow 0.2s ease;\n}\n\n#fqa-bookshelf .fqa-card:hover .fqa-group-cover {\n transform: translateY(-4px);\n box-shadow: var(--fqa-shadow);\n}\n\n#fqa-bookshelf .fqa-group-grid {\n position: absolute;\n inset: 0;\n display: grid;\n grid-template-columns: 1fr 1fr;\n grid-template-rows: 1fr 1fr;\n gap: 4px;\n padding: 6px;\n}\n\n#fqa-bookshelf .fqa-group-cell {\n position: relative;\n border-radius: 3px;\n overflow: hidden;\n background: rgba(31, 35, 41, 0.06);\n}\n\n#fqa-bookshelf .fqa-group-cell img {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n/* 分组详情返回条 */\n\n#fqa-bookshelf .fqa-groupbar {\n display: flex;\n align-items: center;\n gap: 10px;\n margin-bottom: 16px;\n}\n\n#fqa-bookshelf .fqa-groupbar-name {\n font-size: 16px;\n font-weight: 600;\n}\n\n#fqa-bookshelf .fqa-groupbar-count {\n color: var(--fqa-text-weak);\n font-size: 13px;\n}\n\n/* --------------------------- hover 详情浮层 --------------------------- */\n\n/*\n * 用 popover 进入浏览器顶层,不参与页面 z-index 竞争,\n * 因此不会被相邻卡片或原站的层叠上下文盖住。z-index 仅作降级保险。\n */\n#fqa-bookshelf-hover {\n position: fixed;\n z-index: 2147483000;\n /* 容器本身透传,只有内部卡片接收事件,避免空白区挡住下层 */\n pointer-events: none;\n opacity: 0;\n transform: translateY(4px);\n /* allow-discrete:顶层元素从 display:none 切入时也能播放淡入 */\n transition: opacity 0.16s ease, transform 0.16s ease, display 0.16s allow-discrete;\n font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', Arial,\n sans-serif;\n}\n\n/* popover 默认带边框/内边距/居中定位,全部清掉,改由 left/top 控制 */\n#fqa-bookshelf-hover:popover-open,\n#fqa-bookshelf-hover[popover] {\n margin: 0;\n padding: 0;\n border: 0;\n background: transparent;\n overflow: visible;\n inset: auto;\n width: auto;\n height: auto;\n max-width: none;\n max-height: none;\n color: inherit;\n}\n\n#fqa-bookshelf-hover::backdrop {\n background: transparent;\n}\n\n#fqa-bookshelf-hover.fqa-visible {\n opacity: 1;\n transform: translateY(0);\n}\n\n@starting-style {\n #fqa-bookshelf-hover.fqa-visible {\n opacity: 0;\n transform: translateY(4px);\n }\n}\n\n/* 高度由 JS 按封面尺寸设定;纵向 flex 让简介吃掉剩余空间 */\n#fqa-bookshelf-hover .fqa-hover-inner {\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n width: 280px;\n padding: 12px 14px;\n border-radius: 10px;\n background: #fff;\n box-shadow: 0 8px 32px rgba(31, 35, 41, 0.16);\n color: #1f2329;\n overflow: hidden;\n /* 卡片可交互:鼠标可以移进来而不触发收起 */\n pointer-events: auto;\n}\n\n#fqa-bookshelf-hover .fqa-hover-title {\n flex: none;\n margin: 0;\n font-size: 14px;\n line-height: 20px;\n font-weight: 600;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n#fqa-bookshelf-hover .fqa-hover-author {\n flex: none;\n margin-top: 2px;\n color: #8f959e;\n font-size: 12px;\n line-height: 17px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n#fqa-bookshelf-hover .fqa-hover-stats {\n display: flex;\n flex: none;\n margin-top: 10px;\n}\n\n#fqa-bookshelf-hover .fqa-hover-stat {\n flex: 1 1 0;\n min-width: 0;\n padding: 0 6px;\n text-align: center;\n}\n\n#fqa-bookshelf-hover .fqa-hover-stat:first-child {\n padding-left: 0;\n}\n\n#fqa-bookshelf-hover .fqa-hover-stat:last-child {\n padding-right: 0;\n}\n\n#fqa-bookshelf-hover .fqa-hover-stat + .fqa-hover-stat {\n border-left: 1px solid rgba(31, 35, 41, 0.08);\n}\n\n/* 第一栏可悬停切换为更新时间,给个可交互提示 */\n#fqa-bookshelf-hover .fqa-hover-stat:first-child {\n border-radius: 4px;\n cursor: default;\n transition: background 0.15s ease;\n}\n\n#fqa-bookshelf-hover .fqa-hover-stat:first-child:hover {\n background: rgba(31, 35, 41, 0.05);\n}\n\n#fqa-bookshelf-hover .fqa-hover-stat-v {\n font-size: 13px;\n line-height: 18px;\n font-weight: 600;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n#fqa-bookshelf-hover .fqa-hover-stat-k {\n margin-top: 1px;\n color: #8f959e;\n font-size: 11px;\n line-height: 16px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* 梗概 / 简介 双栏切换 */\n#fqa-bookshelf-hover .fqa-hover-seg {\n display: flex;\n flex: none;\n gap: 4px;\n margin-top: 10px;\n padding-top: 10px;\n border-top: 1px solid rgba(31, 35, 41, 0.08);\n}\n\n#fqa-bookshelf-hover .fqa-hover-seg-btn {\n flex: 1 1 0;\n padding: 4px 0;\n border: 0;\n border-radius: 5px;\n background: rgba(31, 35, 41, 0.05);\n color: #646a73;\n font-family: inherit;\n font-size: 12px;\n line-height: 18px;\n cursor: pointer;\n transition: background 0.15s ease, color 0.15s ease;\n}\n\n#fqa-bookshelf-hover .fqa-hover-seg-btn:hover {\n color: #1f2329;\n}\n\n#fqa-bookshelf-hover .fqa-hover-seg-active {\n background: rgba(255, 111, 61, 0.12);\n color: #ff6f3d;\n font-weight: 500;\n}\n\n#fqa-bookshelf-hover .fqa-hover-seg-active:hover {\n color: #ff6f3d;\n}\n\n/*\n * 撑满剩余高度。行数不再写死,由容器高度自然裁切;\n * min-height:0 让 flex 子项允许被压缩,否则 overflow 不生效。\n */\n#fqa-bookshelf-hover .fqa-hover-abstract {\n flex: 1 1 auto;\n min-height: 0;\n margin-top: 8px;\n color: #646a73;\n font-size: 12px;\n line-height: 18px;\n overflow-y: auto;\n overscroll-behavior: contain;\n}\n\n#fqa-bookshelf-hover .fqa-hover-abstract::-webkit-scrollbar {\n width: 4px;\n}\n\n#fqa-bookshelf-hover .fqa-hover-abstract::-webkit-scrollbar-thumb {\n border-radius: 2px;\n background: rgba(31, 35, 41, 0.18);\n}\n\n\n#fqa-bookshelf-hover .fqa-hover-chapter {\n display: block;\n margin-bottom: 1px;\n color: #1f2329;\n font-weight: 500;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* ------------------------------- 骨架屏 ------------------------------- */\n\n#fqa-bookshelf .fqa-sk-cover {\n display: block;\n width: 100%;\n aspect-ratio: 3 / 4;\n border-radius: 6px;\n background: var(--fqa-skeleton);\n}\n\n#fqa-bookshelf .fqa-sk-line {\n height: 12px;\n margin-top: 8px;\n border-radius: 4px;\n background: var(--fqa-skeleton);\n}\n\n#fqa-bookshelf .fqa-sk-anim {\n position: relative;\n overflow: hidden;\n}\n\n#fqa-bookshelf .fqa-sk-anim::after {\n content: '';\n position: absolute;\n inset: 0;\n transform: translateX(-100%);\n background: linear-gradient(90deg, transparent, var(--fqa-skeleton-hl), transparent);\n animation: fqa-shimmer 1.4s infinite;\n}\n\n@keyframes fqa-shimmer {\n 100% {\n transform: translateX(100%);\n }\n}\n\n/* --------------------------- 空态 / 错误态 --------------------------- */\n\n#fqa-bookshelf .fqa-loadmore {\n padding: 24px 0;\n text-align: center;\n color: var(--fqa-text-weak);\n font-size: 13px;\n}\n\n#fqa-bookshelf .fqa-status {\n padding: 80px 16px;\n text-align: center;\n color: var(--fqa-text-weak);\n font-size: 14px;\n line-height: 22px;\n}\n\n#fqa-bookshelf .fqa-status-title {\n margin-bottom: 8px;\n color: var(--fqa-text);\n font-size: 16px;\n font-weight: 500;\n}\n\n#fqa-bookshelf .fqa-status .fqa-btn {\n margin-top: 16px;\n}\n\n/* ------------------------------- 深色模式 ------------------------------- */\n\n@media (prefers-color-scheme: dark) {\n #fqa-bookshelf {\n --fqa-text: #e6e6e6;\n --fqa-text-sub: #a6a6a6;\n --fqa-text-weak: #7a7a7a;\n --fqa-border: rgba(255, 255, 255, 0.1);\n --fqa-hover: rgba(255, 255, 255, 0.06);\n --fqa-skeleton: rgba(255, 255, 255, 0.08);\n --fqa-skeleton-hl: rgba(255, 255, 255, 0.14);\n --fqa-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);\n }\n\n #fqa-bookshelf-hover .fqa-hover-inner {\n background: #212125;\n color: #e6e6e6;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);\n }\n\n #fqa-bookshelf-hover .fqa-hover-chapter {\n color: #e6e6e6;\n }\n\n #fqa-bookshelf-hover .fqa-hover-abstract {\n color: #a6a6a6;\n border-top-color: rgba(255, 255, 255, 0.1);\n }\n\n #fqa-bookshelf-hover .fqa-hover-stat + .fqa-hover-stat {\n border-left-color: rgba(255, 255, 255, 0.1);\n }\n}\n"; + const CONTAINER_ID = "fqa-bookshelf-root"; + const STYLE_ID = "fqa-bookshelf-style"; + const ORIGIN_SELECTOR = ".muye-bookshelf, .muye-bookshelf-home-page, .bookshelf-tabs"; + let app = null; + let container = null; + let observer = null; + function injectStyle() { + if (document.getElementById(STYLE_ID)) return; + const style = document.createElement("style"); + style.id = STYLE_ID; + style.textContent = bookshelfcss; + document.head.appendChild(style); + } + function hideOrigin(root = document) { + root.querySelectorAll(ORIGIN_SELECTOR).forEach((el) => { + if (el.id === CONTAINER_ID || el.closest(`#${CONTAINER_ID}`)) return; + el.classList.add("fqa-hide"); + }); + } + function isBookshelfPath(path) { + return path.startsWith("/bookshelf"); + } + function unmount() { + var _a; + observer == null ? void 0 : observer.disconnect(); + observer = null; + app == null ? void 0 : app.unmount(); + app = null; + container == null ? void 0 : container.remove(); + container = null; + (_a = document.getElementById("fqa-bookshelf-hover")) == null ? void 0 : _a.remove(); + document.querySelectorAll(ORIGIN_SELECTOR).forEach((el) => { + el.classList.remove("fqa-hide"); + }); + } + async function mainHook(_previous) { + if (!isBookshelfPath(window.location.pathname)) { + unmount(); + return; + } + if (app) { + hideOrigin(); + return; + } + injectStyle(); + const origin = await waitForElement(ORIGIN_SELECTOR); + if (!isBookshelfPath(window.location.pathname)) return; + if (app) return; + hideOrigin(); + container = document.createElement("div"); + container.id = CONTAINER_ID; + const anchor = origin ?? document.querySelector("#root") ?? document.body; + if (origin == null ? void 0 : origin.parentElement) { + origin.insertAdjacentElement("beforebegin", container); + } else { + anchor.appendChild(container); + } + app = vue.createApp(_sfc_main); + app.config.errorHandler = (err, _instance, info) => { + console.error(`[fqa:bookshelf] Vue error (${info}):`, err); + }; + app.mount(container); + console.log("[fqa:bookshelf] 书架视图已挂载"); + document.title = "我的书架 - 番茄小说"; + observer = new MutationObserver((mutations) => { + var _a; + for (const mutation of mutations) { + for (const node of mutation.addedNodes) { + if (!(node instanceof HTMLElement)) continue; + if (node.id === CONTAINER_ID || node.closest(`#${CONTAINER_ID}`)) continue; + if ((_a = node.matches) == null ? void 0 : _a.call(node, ORIGIN_SELECTOR)) { + node.classList.add("fqa-hide"); + } else { + hideOrigin(node); + } + } + } + }); + observer.observe(document.body, { childList: true, subtree: true }); + } + function filter(path, _query, _hash) { + return isBookshelfPath(path) || !!app; + } + const _exports = [ + { + id: "bookshelfHook_onload", + event: "load", + filter, + handler: mainHook + }, + { + id: "bookshelfHook_onurlchange", + event: "onUrlChange", + filter, + handler: mainHook + } + ]; + const hooks = [ + ..._exports$3, + ..._exports$2, + ..._exports$1, + ..._exports + ]; + async function onEvent(event, previous) { + console.log(`onEvent, event: ${event}, previous: ${previous}`); + const path = window.location.pathname; + const hash = window.location.hash; + const params = new URLSearchParams(window.location.search); + const tasks = []; + for (const hook of hooks) { + if (hook.event === event && hook.filter(path, params, hash)) { + tasks.push(async () => { + try { + await hook.handler(previous); + } catch (err) { + console.error(`[hook:${hook.id}] handler failed:`, err); + } + }); + } + } + console.log(`tasks: `, tasks); + if (tasks.length > 0) { + await Promise.allSettled(tasks.map((task) => task())); + } + } + async function onUrlChange(previous) { + return await onEvent("onUrlChange", previous); + } + async function onHashChange(previous) { + return await onEvent("onHashChange", previous); + } + async function onLoad() { + return await onEvent("load"); + } + const name = "fanqie-assistant"; + const version = "0.0.3"; + const win = unsafeWindow; + let previousUrl = win.location.href; + let previousHash = win.location.hash; + function installNavigationHooks() { + for (const method of ["pushState", "replaceState"]) { + const original = win.history[method]; + win.history[method] = function(...args) { + const result = original.apply(this, args); + console.debug(`history.${method} called with args:`, args); + void onUrlChange(previousUrl); + previousUrl = win.location.href; + return result; + }; + } + win.addEventListener("popstate", () => { + void onUrlChange(previousUrl); + previousUrl = win.location.href; + }); + win.addEventListener("hashchange", () => { + void onHashChange(previousHash); + previousHash = win.location.hash; + }); + } + async function mainInit() { + console.log(`================================================`); + console.log(`== ${name} - ${version} ==`); + console.log(`================================================`); + installNavigationHooks(); + initFontDecrypt(); + await inject(); + await init(); + void onLoad(); + } + mainInit(); -})(); \ No newline at end of file +})(moment, Vue); \ No newline at end of file