订阅源管理器
订阅源管理器
分享者: guaner001125 (317)发布时间: 6天前
仅支持洛雅橙改版阅读最新测试版3.26.022606及以上版本 https://github.com/Luoyacheng/legado 不知道干嘛,做着玩的,可以配合书源打开这个订阅源管理器,这样隐藏订阅也可以打开订阅了,初次使用要配合web服务拉取。
{
"articleStyle": 0,
"cacheFirst": false,
"customOrder": 34,
"enableJs": true,
"enabled": true,
"enabledCookieJar": false,
"lastUpdateTime": 1772158306402,
"loadWithBaseUrl": true,
"preload": false,
"preloadJs": "window.java = java;\nwindow.ajaxAwait = ajaxAwait;\nwindow.source = source;\nwindow.cache = cache;",
"showWebLog": true,
"singleUrl": false,
"sourceComment": "仅支持洛雅橙改版阅读最新测试版3.26.022606及以上版本\nhttps:\/\/github.com\/Luoyacheng\/legado\n\n不知道干嘛,做着玩的,可以配合书源打开这个订阅源管理器,这样隐藏订阅也可以打开订阅了,初次使用要配合web服务拉取。",
"sourceIcon": "https:\/\/cdn.jsdelivr.net\/gh\/gedoor\/legado@master\/app\/src\/main\/res\/mipmap-hdpi\/ic_launcher.png",
"sourceName": "订阅源管理器",
"sourceUrl": "订阅源管理器",
"startHtml": "<div class=\"modal-overlay\" id=\"modalOverlay\"><\/div>\n <div class=\"color-modal\" id=\"colorModal\">\n \n <div class=\"modal-header\">\n <h2 class=\"modal-title\">颜色设置<\/h2>\n <button class=\"close-btn\" id=\"closeModalBtn\">×<\/button>\n <\/div>\n <div class=\"modal-content\">\n <div class=\"color-options\"> \n <div class=\"color-option\">\n <div class=\"color-label\">\n <span class=\"color-name\">\n <div class=\"color-preview\" id=\"backgroundColorPreview\" style=\"background-color: #ffffff\"><\/div>\n <span>背景颜色<\/span>\n <\/span>\n <span class=\"color-value\" id=\"backgroundColorValue\" contenteditable=\"true\">#ffffff<\/span>\n <\/div>\n <input type=\"color\" class=\"color-input\" id=\"backgroundColorInput\" value=\"#ffffff\">\n <\/div> \n <div class=\"color-option\">\n <div class=\"color-label\">\n <span class=\"color-name\">\n <div class=\"color-preview\" id=\"accentColorPreview\" style=\"background-color: #007aff\"><\/div>\n <span>强调色<\/span>\n <\/span>\n <span class=\"color-value\" id=\"accentColorValue\" contenteditable=\"true\">#007aff<\/span>\n <\/div>\n <input type=\"color\" class=\"color-input\" id=\"accentColorInput\" value=\"#007aff\">\n <\/div> \n <div class=\"color-option\">\n <div class=\"color-label\">\n <span class=\"color-name\">\n <div class=\"color-preview\" id=\"iconBgColorPreview\" style=\"background-color: #f2f2f7\"><\/div>\n <span>图标背景色<\/span>\n <\/span>\n <span class=\"color-value\" id=\"iconBgColorValue\" contenteditable=\"true\">#f2f2f7<\/span>\n <\/div>\n <input type=\"color\" class=\"color-input\" id=\"iconBgColorInput\" value=\"#f2f2f7\">\n <\/div> \n <div class=\"color-option\">\n <div class=\"color-label\">\n <span class=\"color-name\">\n <div class=\"color-preview\" id=\"iconTextColorPreview\" style=\"background-color: #1d1d1f\"><\/div>\n <span>图标文字色<\/span>\n <\/span>\n <span class=\"color-value\" id=\"iconTextColorValue\" contenteditable=\"true\">#1d1d1f<\/span>\n <\/div>\n <input type=\"color\" class=\"color-input\" id=\"iconTextColorInput\" value=\"#1d1d1f\">\n <\/div> \n <div class=\"color-option\">\n <div class=\"color-label\">\n <span class=\"color-name\">\n <div class=\"color-preview\" id=\"topColorPreview\" style=\"background-color: #007aff\"><\/div>\n <span>顶部颜色<\/span>\n <\/span>\n <span class=\"color-value\" id=\"topColorValue\" contenteditable=\"true\">#007aff<\/span>\n <\/div>\n <input type=\"color\" class=\"color-input\" id=\"topColorInput\" value=\"#007aff\">\n <\/div>\n <\/div> \n <\/div>\n\n <div class=\"modal-footer\">\n <button class=\"action-btn reset-btn\" id=\"resetBtn\">重置<\/button>\n <button class=\"action-btn apply-btn\" id=\"applyBtn\">应用<\/button>\n <button class=\"action-btn c-btn no\" id=\"cBtn\">关闭<\/button>\n <\/div>\n <\/div>\n<div id=\"bottomMenu\">\n <div style=\"display:flex;justify-content:center;align-items:center;\">\n <input type=\"checkbox\" style=\"accent-color: var(--active-color);\">\n <p style=\"margin-left:5px\">全选 (共选择<span>0<\/span>个)<\/p>\n <\/div>\n <button class=\"del\">删除<\/button>\n <button class=\"ok\">分组<\/button>\n<\/div>\n\n<div id=\"popupMenu\"> \n <div class=\"popup-item getrssbtn\">\n <span style=\"width:20px;text-align:center;\">📥<\/span>\n <span>拉取订阅<\/span>\n <\/div>\n \n <div style=\"height:1px;background:#e5e7eb;\"><\/div> \n <div class=\"popup-item setwebbtn\">\n <span style=\"width:20px;text-align:center;\">🌐<\/span>\n <span>编辑网站<\/span>\n <\/div>\n \n <div style=\"height:1px;background:#e5e7eb;\"><\/div> \n <div class=\"popup-item setgroupbtn\">\n <span style=\"width:20px;text-align:center;\">📁<\/span>\n <span>编辑分组<\/span>\n <\/div>\n \n <div style=\"height:1px;background:#e5e7eb;\"><\/div>\n \n <div class=\"popup-item setthemebtn\">\n <span style=\"width:20px;text-align:center;\">🎨<\/span>\n <span>设置主题<\/span>\n <\/div> \n \n <div style=\"height:1px;background:#e5e7eb;\"><\/div>\n <div class=\"popup-item pushrssbtn\">\n <span style=\"width:20px;text-align:center;\">📤<\/span>\n <span>同步订阅<\/span>\n <\/div>\n<\/div>\n\n\n<div class=\"header-box\">\n<div class=\"search-box\">\n <input type=\"text\" class=\"input\" placeholder=\"筛选当前分组订阅源\">\n<\/div>\n\n<div class=\"tab-box\">\n <div class=\"groups-box\"><\/div>\n <div class=\"add-group\">⚙️<\/div>\n<\/div>\n<\/div>\n\n\n<div class=\"webs-c\">\n<div class=\"webs-box\">\n<\/div>\n<\/div>\n\n\n<div class=\"editgroup-m m hide\">\n<div class=\"editgroup-box box\">\n <div class=\"editgroup-title title\">\n <h4>编辑分组<\/h4>\n <\/div>\n <div class=\"editgroup-items items\"><\/div>\n <div class=\"editgroup-btn btn\">\n <button class=\"editgroup-add\">新增<\/button>\n <button class=\"editgroup-ok ok\">确认<\/button>\n <button class=\"editgroup-no no\">取消<\/button>\n <\/div>\n<\/div>\n<\/div>\n\n\n<div class=\"editweb-m m hide\">\n<div class=\"editweb-box box\">\n <div class=\"editweb-title title\">\n <h4>编辑订阅源<\/h4>\n <\/div>\n \n <div class=\"editweb\">\n <div class=\"editweb-group\">\n <p>分组<\/p>\n <select class=\"input\" multiple>\n <\/select>\n <\/div>\n \n <div class=\"editweb-name\">\n <p>网站名称<\/p>\n <input class=\"input\">\n <\/div>\n \n <div class=\"editweb-url\">\n <p>源URL(唯一标识)<\/p>\n <input class=\"input\" disabled readonly style=\"color:#888\">\n <\/div>\n \n <div class=\"editweb-icon\">\n <p>图标地址(可填一个文字或图片地址)<\/p>\n <input class=\"input\">\n <\/div>\n <\/div>\n \n <div class=\"editweb-btn btn\"> \n <button class=\"editweb-ok ok\">保存<\/button>\n <button class=\"editweb-no no\">取消<\/button>\n <\/div>\n<\/div>\n<\/div>\n\n\n<div class=\"getrss-m m hide\">\n <div class=\"getrss-box box\">\n <div class=\"getrss-title title\">\n <h4>拉取订阅源<\/h4>\n <\/div>\n <input class=\"input rssurl\" type=\"text\" placeholder=\"请打开web服务,输入web服务的网址\">\n <div class=\"editweb-btn btn\"> \n <button class=\"editweb-ok ok\">确认<\/button>\n <button class=\"editweb-no no\">取消<\/button>\n <\/div> \n <\/div>\n<\/div>\n\n\n\n<div class=\"pushrss-m m hide\">\n <div class=\"pushrss-box box\">\n <div class=\"pushrss-title title\">\n <h4>同步订阅源<\/h4>\n <\/div>\n <div class=\"pushrss-info items hide\"><\/div>\n \n <div class=\"pushrss-edit\">\n <input class=\"input rssurl\" type=\"text\" placeholder=\"请打开web服务,输入web服务的网址\">\n <p><span style=\"color:red;font-weight:bold\">注意<\/span>:此功能会修改阅读里的订阅源信息【包括分组,名称,图标地址】,根据源URL定位<\/p>\n <p><span style=\"color:var(--active-color);font-weight:bold\">完整同步<\/span>:这里没有的订阅源,阅读的订阅源会<span style=\"color:red;font-weight:bold\">同步删除<\/span>掉(谨慎操作)<\/p>\n <p><span style=\"color:var(--active-color);font-weight:bold\">排序同步<\/span>:会将全部里的订阅源顺序同步到阅读的订阅源,不更改其他信息<\/p>\n <p><span style=\"color:var(--active-color);font-weight:bold\">标准同步<\/span>:只会同步信息,<span style=\"color:red;font-weight:bold\">不会删除<\/span>阅读里的订阅源<\/p>\n <div class=\"pushrss-btn btn\"> \n <button class=\"pushrss-ok_1 ok_1\">完整<\/button>\n <button class=\"pushrss-order\" style=\"background:green;color:#fff\">排序<\/button> \n \n <button class=\"pushrss-ok ok\">标准<\/button>\n \n <\/div> \n <\/div>\n <\/div>\n<\/div>\n\n\n<div class=\"rsslist-m m hide\">\n <div class=\"rsslist-box box\">\n <div class=\"getrss-title title\">\n <h4>添加订阅源<\/h4>\n <\/div>\n \n <div class=\"rsslist-input\"> \n <div class=\"rsslist-check\"> \n <input type=\"checkbox\" name=\"all\">\n <label for=\"all\" style=\"margin-left:5px;font-size:12px\">全选<\/label>\n <\/div>\n \n <div class=\"rsslist-search\"> \n <input class=\"input\" type=\"text\" placeholder=\"筛选订阅源\"> \n <\/div>\n \n <\/div>\n \n <div class=\"rsslist-items items\">\n <\/div>\n \n <div class=\"rsslist-btn btn\"> \n <button class=\"rsslist-ok ok\">确认<\/button>\n <button class=\"rsslist-no no\">取消<\/button>\n <\/div> \n <\/div>\n<\/div>",
"startJs": "cache.put(\"rssset\",1,300);\neval(String(java.importScript(\"https:\/\/cdn.jsdelivr.net\/npm\/sortablejs@latest\/Sortable.min.js\")));\n\nlet rssData,getRssData,groups,webs;\nlet isSwiping = false;\nlet sortableInstance = null;\nlet swipeStartX = 0;\nlet scrollTimer = null;\nlet popupVisible = false;\nlet isEditWeb = false;\n\nconst getrss_m = document.querySelector(\".getrss-m\");\nconst pushrss_m = document.querySelector(\".pushrss-m\");\nconst rsslist_m = document.querySelector(\".rsslist-m\");\nconst webs_box = document.querySelector(\".webs-box\");\nconst editgroup_m = document.querySelector(\".editgroup-m\");\nconst editweb_m = document.querySelector(\".editweb-m\");\nconst popupMenu = document.querySelector(\"#popupMenu\");\nconst bottomMenu = document.querySelector(\"#bottomMenu\");\nconst colorModal = document.querySelector(\"#colorModal\");\n\nupGroups();\ncheckMinusTwo();\nlet rssUrl = getRssData.rssUrl ?? \"\";\n\ncloseM(getrss_m);\ncloseM(rsslist_m);\ncloseM(editgroup_m);\ncloseM(editweb_m);\ncloseM(pushrss_m);\n\n\nnew Sortable(document.querySelector('.editgroup-items'), {\n animation: 150,\n fallbackOnBody: false, \n fallbackClass: \"sortable-fallback\",\n fallbackTolerance: 0, \n ghostClass: \"sortable-ghost\",\n dragClass: \"sortable-drag\",\n chosenClass: \"sortable-chosen\",\n dataIdAttr: 'data-id'\n});\n\n\nif(!rssUrl || !getRssData.webs){\n\tjava.longToast(\"请先拉取rss源\");\n\tgetRssSources(rssUrl);\n}\n\n\ndocument.querySelector(\".pushrssbtn\").addEventListener(\"click\", function(event) {\n pushrss_m.classList.remove(\"hide\");\n document.querySelectorAll(\".rssurl\")[1].value = rssUrl;\n document.querySelector(\".pushrss-info\").classList.add(\"hide\");\n document.querySelector(\".pushrss-edit\").classList.remove(\"hide\");\n document.querySelector(\".pushrss-info\").innerHTML = \"\";\n \n})\n\npushrss_m.addEventListener(\"click\", function(event) {\n if(event.target.closest(\".ok\")){\n rssUrl = document.querySelectorAll(\".rssurl\")[1].value;\n if(!\/^http\/.test(rssUrl)){\n java.toast(\"请输入正确的web服务地址\");\n return\n }\n getRssData.rssUrl = rssUrl;\n source.putVariable(JSON.stringify(getRssData));\n pushRss(rssUrl)\n }else if(event.target.closest(\".ok_1\")){\n rssUrl = document.querySelectorAll(\".rssurl\")[1].value;\n if(!\/^http\/.test(rssUrl)){\n java.toast(\"请输入正确的web服务地址\");\n return\n }\n getRssData.rssUrl = rssUrl;\n source.putVariable(JSON.stringify(getRssData));\n pushRss(rssUrl,1)\n }else if(event.target.closest(\".pushrss-order\")){\n rssUrl = document.querySelectorAll(\".rssurl\")[1].value;\n if(!\/^http\/.test(rssUrl)){\n java.toast(\"请输入正确的web服务地址\");\n return\n }\n getRssData.rssUrl = rssUrl;\n source.putVariable(JSON.stringify(getRssData));\n pushRss(rssUrl,2)\n }\n})\n\n\nfunction createPushRss(x,oname,ogroup,f,color){\n let msgBox = document.createElement('div');\n msgBox.className = \"msg-item\";\n msgBox.innerHTML = `\n <div class=\"img\">\n ${\/^http|data:\/.test(x.sourceIcon)?\"<img src=\\\"\"+x.sourceIcon+\"\\\">\":x.sourceName.match(\/[\\u4e00-\\u9fa5a-zA-Z.]\/)[0] }\n <\/div>\n <div class=\"msg-name\">\n <p class=\"name\">\n ${oname===x.sourceName?\"\":\"<span>\"+oname+ \" => <\/span>\"}\n ${x.sourceName}\n <span style=\"color:${color};float:right\"> ${f}<\/span>\n <span style=\"color:black;float:right\">\n ${ogroup===x.sourceGroup?\"\":\"<span>\"+(ogroup?ogroup:\"未分组\")+ \" => <\/span>\"}\n ${x.sourceGroup?\"\"+x.sourceGroup+\"\":\"未分组\"}\n <\/span>\n <\/p>\n <p class=\"rssurl\">${x.sourceUrl}<\/p>\n <\/div>\n `;\n if(f===\"已同步\" || f === \"已删除\")\ndocument.querySelector('.pushrss-info').insertAdjacentElement('afterbegin', msgBox);\n if(f===\"未同步\")\n document.querySelector(\".pushrss-info\").appendChild(msgBox)\n}\n\n\nfunction pushRss(rssUrl,f){\n let getRssUrl = rssUrl+\"\/getRssSources\";\n let delRssUrl = rssUrl+\"\/deleteRssSources\";\n let saveRssUrl = rssUrl+\"\/saveRssSource\";\n \n ajaxAwait(getRssUrl,500)\n .then(r=>{\n if(\/^java.net.ConnectException: Failed|^java.net.S\/.test(r)){\n document.querySelector(\".pushrss-info\").innerHTML = `<div class=\"error\">\n 请检查是否打开web服务<br>\n 请检查网址是否错误\n <\/div>`;\n java.longToast(\"\\n请检查是否打开web服务\\n请检查网址是否错误\")\n return\n }\n \n let delwebs = [];\n document.querySelector(\".pushrss-info\").classList.remove(\"hide\");\n document.querySelector(\".pushrss-edit\").classList.add(\"hide\");\n let rssData = JSON.parse(r).data;\n rssData.forEach(x=>{\n let findweb = webs.find(web=>web.url === x.sourceUrl);\n if(!findweb){\n if(f === 1){\n delwebs.push(x);\n createPushRss(x,x.sourceName,x.sourceGroup,\"已删除\",\"red\");\n }else{\n createPushRss(x,x.sourceName,x.sourceGroup,\"未同步\",\"blue\");\n }\n }else{\n let oname = x.sourceName;\n let ogroup = x.sourceGroup;\n if(f===2){\n if(findweb.sort??\"\"){\n x.customOrder = findweb.sort\n }\n }else{\n let oname = x.sourceName;\n let ogroup = x.sourceGroup;\n x.sourceName = findweb.name.replace(\/\\\\n\/g,'\\n');\n x.sourceGroup = findweb.group[0]===\"-1\"?\"\":findweb.group.join(\",\").replace(\/,$\/g,'');\n x.sourceIcon = findweb.icon.length>5?findweb.icon:\"\";\n }\n fetch(saveRssUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application\/json\"\n },\n body: JSON.stringify(x)\n })\n .then(res =>{\n createPushRss(x,oname,ogroup,\"已同步\",\"green\")\n })\n .catch(error => {\n java.toast(\"出现错误\")\n });\n }\n });\n if(f ==1){\n fetch(delRssUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application\/json\"\n },\n body: JSON.stringify(delwebs)\n })\n .then(res =>{\n java.toast(\"已同步完成\")\n })\n .catch(error => {\n java.toast(\"出现错误\")\n });\n };\n \n java.toast(\"已同步完成\")\n })\n .catch(e=>{\n console.log(e)\n \t document.querySelector(\".pushrss-info\").innerHTML = `<div class=\"error\">\n 请检查是否打开web服务<br>\n 请检查网址是否错误\n <\/div>`;\n java.longToast(\"\\n请检查是否打开web服务\\n请检查网址是否错误\")\n });\n}\n\n\nwebs_box.addEventListener(\"click\", function(event) {\n let web_e = event.target.closest(\".s-box\")\n if(event.target.closest(\".web-box\") || web_e){\n let e = event.target.closest(\".web-box\");\n let url = e.getAttribute(\"data-url\");\n let s = e.getAttribute(\"data-s\");\n if(isEditWeb){\n if(web_e){\n let element = web_e.closest(\".web-box\");\n url = element.dataset.url;\n const matchingWeb = webs.find(web => web.url === url);\n if (matchingWeb) {\n editweb_m.classList.remove(\"hide\");\n let options = \"\";\n groups.forEach(x=>{\n if(x.id!==\"-2\")\n options+=`<option value=\"${x.id}\"${matchingWeb.group.some(item => item === x.id)?\" selected\":\"\"}>${x.name}<\/option>`;\n });\n editweb_m.querySelector(\".editweb-group select\").innerHTML = options\n editweb_m.querySelector(\".editweb-name input\").value = JSON.stringify(matchingWeb.name).replace(\/^\"|\"$\/g,'');\n editweb_m.querySelector(\".editweb-url input\").value = JSON.stringify(matchingWeb.url).replace(\/^\"|\"$\/g,'');\n editweb_m.querySelector(\".editweb-icon input\").value = matchingWeb.icon;\n }\n return;\n }\n e.classList.contains(\"active\")?e.classList.remove(\"active\"):e.classList.add(\"active\");\n let editwebActive= document.querySelectorAll(\".editwebBox.active\");\n let editwebBox= document.querySelectorAll(\".editwebBox\");\n if(editwebActive.length === editwebBox.length){\n bottomMenu.querySelector(\"input\").checked = true;\n }else{\n bottomMenu.querySelector(\"input\").checked = false\n }\n bottomMenu.querySelector(\"span\").textContent = editwebActive.length;\n }else{\n java.open(\"rss\",null,null,url);\n }\n }\n });\n \neditweb_m.addEventListener(\"click\", function(event) { \n if(event.target.closest(\".editweb-ok\")){\n let url = editweb_m.querySelector(\".editweb-url input\").value;\n const matchingWeb = webs.find(web => JSON.stringify(web.url).replace(\/^\"|\"$\/g,'')=== url);\n if (matchingWeb) {\n editweb_m.classList.add(\"hide\");\n matchingWeb.group = Array.from(editweb_m.querySelector(\".editweb-group select\").selectedOptions)\n .map(option => option.value);\n matchingWeb.name = editweb_m.querySelector(\".editweb-name input\").value.replace(\/\\\\n\/g,'\\n');\n matchingWeb.icon = editweb_m.querySelector(\".editweb-icon input\").value.trim();\n getRssData.webs = webs;\n source.putVariable(JSON.stringify(getRssData))\n java.toast(\"更新成功\");\n upGroups();\n }else{\n java.toast(\"未找到对应的订阅源\")\n }\n }\n});\n\ncolorModal.addEventListener(\"click\", function(event) { \n if(event.target.closest(\".no\") || event.target.closest(\".close-btn\") || event.target.closest(\".modal-header\")){\n document.querySelector(\".modal-overlay\").classList.remove(\"active\");\n document.querySelector(\".color-modal\").classList.remove(\"active\")\n }\n})\n\n\ndocument.querySelector(\".setthemebtn\").addEventListener(\"click\", function() { \n document.querySelector(\".modal-overlay\").classList.add(\"active\");\n document.querySelector(\".color-modal\").classList.add(\"active\")\n})\n\n\nbottomMenu.addEventListener(\"click\", function(event) { \n const targetInput = event.target.closest('input');\n const e = event.target;\n let editwebBox= document.querySelectorAll(\".editwebBox\");\n let editwebActive= document.querySelectorAll(\".editwebBox.active\");\n if (targetInput) {\n if (targetInput.checked) {\n editwebBox.forEach(x=>{\n x.classList.add(\"active\")\n });\n bottomMenu.querySelector(\"span\").textContent = editwebBox.length;\n } else {\n editwebBox.forEach(x=>{\n x.classList.remove(\"active\");\n })\n bottomMenu.querySelector(\"span\").textContent = 0\n }\n }else if(e.closest(\".del\")){\n if(editwebActive.length ===0)return;\n editwebActive.forEach(x => {\n let url = x.dataset.url;\n const index = webs.findIndex(web => web.url === url);\n if (index !== -1) {\n webs.splice(index, 1);\n }\n });\n \n getRssData.webs = webs;\n source.putVariable(JSON.stringify(getRssData));\n upGroups();\n bottomMenu.querySelector(\"span\").textContent = 0;\n bottomMenu.querySelector(\"input\").checked = false;\n java.toast(\"删除成功\")\n }else if(e.closest(\".ok\")){\n let items = document.querySelector(\".editgroup-items\");\n items.innerHTML = \"\";\n editgroup_m.classList.remove(\"hide\");\n document.querySelector(\".editgroup-title h4\").textContent = \"移入分组\";\n groups.forEach(x=>{\n const item = document.createElement('div');\n item.className = \"editgroup-item\";\n item.dataset.id = x.id;\n item.innerHTML = `\n <div class=\"editgroup-order\"><input type=\"checkbox\" ${(x.id===\"-1\" || x.id===\"-2\")?\" disabled \":\"\"}><\/div>\n <div class=\"editgroup-name\" contenteditable=\"true\">${x.name}<\/div>`;\n items.appendChild(item);\n })\n \n }\n})\n \n \n \nrsslist_m.addEventListener(\"click\", function(event) {\n if(event.target.closest(\".ok\"))saveRss();\n });\n \n editgroup_m.addEventListener(\"click\", function(event) {\n const items = document.querySelector(\".editgroup-items\");\n if(event.target.closest(\".editgroup-del\")){\n event.target.style.color = \"red\";\n event.target.closest(\".editgroup-item\").remove();\n }else if(event.target.closest(\".editgroup-add\")){\n const item = document.createElement('div');\n item.className = \"editgroup-item\";\n item.innerHTML = `\n <div class=\"editgroup-order\"><input type=\"checkbox\" checked><\/div>\n <div class=\"editgroup-name\" contenteditable=\"true\"><\/div>\n <div class=\"editgroup-del\">×<\/div>`;\n items.appendChild(item);\n }else if(event.target.closest(\".editgroup-ok\")){\n let groupItem = items.querySelectorAll(\".editgroup-item\") ?? [];\nlet newGroups = [];\nlet idMap = new Map(); \nlet seenIds = new Set(); \nconst SPECIAL_GROUP_IDS = [\"-1\", \"-2\"]; \n\n\nlet selectedGroupIds = [];\n let activeWebElements = document.querySelectorAll(\".editwebBox.active\") ?? [];\nif(isEditWeb && activeWebElements.length===0){\n editgroup_m.classList.add(\"hide\");\n java.toast(\"没有选中订阅源\")\n return\n }\n\n\nfor (let item of groupItem) {\n let nameElement = item.querySelector(\".editgroup-name\");\n if (!nameElement) continue;\n let name = nameElement.innerText.trim();\n if (!name) continue;\n let jname = JSON.stringify(name).replace(\/^\"|\"$\/g, '');\n let oldId = item.dataset.id;\n let isSpecialGroup = SPECIAL_GROUP_IDS.includes(oldId);\n let newId = isSpecialGroup ? oldId : jname;\n let checkbox = item.querySelector(\".editgroup-order input[type='checkbox']\");\n let isChecked = checkbox?.checked ?? true; \n \n if (isChecked) {\n selectedGroupIds.push(newId);\n }\n \n if (seenIds.has(newId)) continue;\n seenIds.add(newId);\n if (oldId && !isSpecialGroup && oldId !== newId) {\n idMap.set(oldId, newId);\n }\n let originalGroup = groups.find(g => g.id === oldId);\n let sValue = originalGroup ? originalGroup.s : 1; \n if (!isEditWeb && !isSpecialGroup) {\n sValue = isChecked ? 1 : 0;\n }\n \n let group = originalGroup || { id: newId, name: jname, s: sValue };\n group.id = newId;\n group.name = jname;\n if (!isEditWeb && !isSpecialGroup) {\n group.s = sValue;\n } else if (!originalGroup) {\n group.s = sValue;\n }\n \n newGroups.push(group)\n}\n\nif (isEditWeb) {\n activeWebElements.forEach(webElement => {\n let url = webElement.dataset.url;\n if (!url) return;\n let webObj = webs.find(web => web.url === url);\n if (webObj) {\n webObj.group = selectedGroupIds.length > 0 ? [...selectedGroupIds] : [\"-1\"];\n }\n });\n}\nif (!isEditWeb) {\n webs.forEach(web => {\n if (!Array.isArray(web.group)) return; \n const mappedGroups = web.group.map(groupId => {\n if (SPECIAL_GROUP_IDS.includes(groupId)) return groupId;\n \n const mappedId = idMap.get(groupId) || groupId;\n if (!seenIds.has(mappedId)) {\n return \"-1\"; \n }\n return mappedId;\n });\n web.group = [...new Set(mappedGroups)];\n });\n}\n\ngroups = newGroups;\ngetRssData.groups = groups;\ngetRssData.webs = webs;\nsource.putVariable(JSON.stringify(getRssData));\neditgroup_m.classList.add(\"hide\");\njava.toast(\"保存成功\");\nupGroups();\n\n\n }\n});\n\n \ndocument.querySelector(\".getrssbtn\").addEventListener(\"click\", function() {getRssSources(rssUrl);}) \ndocument.querySelector(\".setwebbtn\").addEventListener(\"click\", function() {\n isEditWeb = true;\n bottomMenu.style.opacity = '1';\n bottomMenu.style.visibility = 'visible';\n bottomMenu.style.transform = 'translateY(10)';\n document.querySelector(\".webs-c\").style.maxHeight = \"calc(100vh - 210px)\";\n upGroups();\n }) \n \n \ndocument.querySelector(\"body\").addEventListener(\"click\", function() {\n if (event.target.closest(\".add-group\")) {\n if (popupVisible || isEditWeb) {\n hidePopup();\n } else {\n showPopup();\n }\n document.querySelector(\".webs-c\").style.maxHeight = \"calc(100vh - 150px)\";\n bottomMenu.style.opacity = '0';\n bottomMenu.style.visibility = 'hidden';\n bottomMenu.style.transform = 'translateY(0)';\n \n isEditWeb = false;\n upGroups();\n }else{\n hidePopup();\n }\n})\n \n\n \nfunction showPopup() {\n popupVisible = true;\n popupMenu.style.opacity = '1';\n popupMenu.style.visibility = 'visible';\n popupMenu.style.transform = 'translateY(0)';\n}\n\nfunction hidePopup() {\n popupVisible = false;\n popupMenu.style.opacity = '0';\n popupMenu.style.visibility = 'hidden';\n popupMenu.style.transform = 'translateY(-10px)';\n}\n\n \ndocument.querySelector(\".setgroupbtn\").addEventListener(\"click\", function() {\n editgroup_m.classList.remove(\"hide\");\n document.querySelector(\".editgroup-title h4\").textContent = \"编辑分组\"\n let editgroupHtml = \"\";\n groups.forEach(x=>{\n \t let del =(x.id===\"-1\" || x.id===\"-2\")?\"\": `<div class=\"editgroup-del\">×<\/div>`;\n \t editgroupHtml+=`<div class=\"editgroup-item\" data-id=\"${x.id}\">\n <div class=\"editgroup-order\"><input type=\"checkbox\" name=\"${x.id}\" ${x.s===1?\" checked\":\"\"}><\/div>\n <div class=\"editgroup-name\" contenteditable=\"true\">${x.name}<\/div>\n ${del}\n <\/div>`;\n \t});\n \tdocument.querySelector(\".editgroup-items\").innerHTML = editgroupHtml;\n}) \n\n\nfunction closeM(ele){\n if(typeof ele === \"string\")ele = document.querySelector(ele);\n document.addEventListener(\"click\", function(event) {\n if (event.target.matches(\".no\") || event.target.matches(\".m\"))\n ele.classList.add(\"hide\");\n });\n}\n \nfunction getRssSources(rssUrl){\n document.querySelectorAll(\".rssurl\").forEach(x=>{\n x.value = rssUrl;\n });\n getrss_m.classList.remove(\"hide\");\n document.querySelector(\".getrss-box\").addEventListener(\"click\",\n function(event) {\n if (event.target.closest(\".ok\")) {\n rssUrl = document.querySelectorAll(\".rssurl\")[0].value;\n if(\/http\/.test(rssUrl)){\n getRssData.rssUrl = rssUrl;\n rsslist_m.classList.remove(\"hide\");\n rssUrl = rssUrl+\"\/getRssSources\";\n ajaxAwait(rssUrl,500)\n .then(r=>{\n source.putVariable(JSON.stringify(getRssData));\n rssData = JSON.parse(r).data;\n let rss_item = \"\";\n for(let i=0;i<rssData.length;i++){\n let x = rssData[i];\n let f = webs.some(item => item.url === x.sourceUrl);\n rss_item += `<div class=\"rsslist-item${f?\" select\":\"\"}\">\n <input type=\"checkbox\" name=\"${i}\"${f?\" disabled checked\":\"\"}>\n <div class=\"rsslist-name\">\n <p class=\"name\">${x.sourceName}\n <span style=\"color:#000;float:right\">${x.sourceGroup?\"(\"+x.sourceGroup+\")\":\"\"}<\/span>\n <\/p>\n <p class=\"rssurl\">\n ${x.sourceUrl}\n <\/p>\n <\/div>\n <\/div>`;\n }\n document.querySelector(\".rsslist-items\").innerHTML = rss_item;\n })\n .catch(e=>{\n \t document.querySelector(\".rsslist-items\").innerHTML = `<div class=\"error\">\n 请检查是否打开web服务<br>\n 请检查网址是否错误\n <\/div>`;\n java.longToast(\"\\n请检查是否打开web服务\\n请检查网址是否错误\")\n })\n }else{\n java.toast(\"请输入正确网址\")\n }\n }\n});\n}\n\nfunction saveRss(){\n const selectitem = document.querySelectorAll(\n '.rsslist-item input[type=\"checkbox\"]:checked:not([disabled])') ?? [];\n if(selectitem.length==0){\n java.toast(\"暂无可添加的订阅源\");\n return;\n }\n \n selectitem.forEach((x,i)=>{\n x.disabled = true;\n x.closest('.rsslist-item').classList.add(\"select\");\n let web = {};\n let index = x.name;\n let r = rssData[index];\n web.name = r.sourceName;\n web.url = r.sourceUrl\n web.icon = r.sourceIcon;\n web.group =(r.sourceGroup?r.sourceGroup.split(\",\"):\"\") || [\"-1\"];\n web.group.forEach(groupItem => {\n if (!groups.some(group => group.id === groupItem)) {\n groups.push({\n id: groupItem,\n s: 1,\n name: groupItem!==\"-1\"?groupItem:\"未分组\"\n });\n }\n });\n webs.push(web);\n });\n \n if (!groups.some(x => x.f == 1)) {\n const uncategorized = groups.find(x => x.id == \"-2\");\n if (uncategorized) {\n uncategorized.f = 1;\n } \n }\n \n getRssData.webs = webs;\n getRssData.groups = groups;\n source.putVariable(JSON.stringify(getRssData));\n upGroups();\n java.toast(\"添加成功\");\n}\n\nfunction upGroups(){\n getRssData = JSON.parse(source.getVariable() || \"{}\");\n groups = getRssData.groups ?? [{id:\"-2\",name:\"全部\",s:1},{id:\"-1\",name:\"未分组\",s:1}];\n webs = getRssData.webs ?? [];\n let groupHtml = \"\";\n let f = groups.find(x=> x.f===1 && x.s === 1);\n groups.forEach((x,i)=>{\n if(x.s===1){\n groupHtml+=`\n <div class=\"group${f?(x.f===1?\" active\":\"\"):(i==0?\" active\":\"\")}\" data-id=\"${x.id}\">${x.name}<\/div>\n `\n }\n });\n document.querySelector('.groups-box').innerHTML = groupHtml;\n initAllEvents()\n}\n\nfunction waitForElement(selector, callback, timeout = 1000) {\n const startTime = Date.now();\n const checkElement = () => {\n const element = document.querySelector(selector);\n if (element) {\n callback(element);\n } else if (Date.now() - startTime < timeout) {\n setTimeout(checkElement, 100);\n } else {\n callback(null);\n }\n };\n checkElement();\n}\n\nfunction getWebs(id){\n let filterWebs;\n if(id===\"-2\"){\n filterWebs = webs.map(web => ({ ...web })).sort((a, b) => a.sort - b.sort);\n }else{\n filterWebs = webs.filter(x => x.group.some(item => item === id));\n }\n \n let websHtml = \"\";\n document.querySelector(\".webs-box\").innerHTML = \"\";\n if(filterWebs.length==0){\n websHtml = `\n <div style=\"margin-top:200px\">\n 暂无订阅源\n <\/div>\n `;\n }\n \n filterWebs.forEach(x => {\n const webBox = document.createElement('div');\n webBox.className = 'web-box';\n webBox.setAttribute('data-url', x.url);\n if(isEditWeb)webBox.classList.add(\"editwebBox\");\n const iconBox = document.createElement('div');\n iconBox.className = 'web-icon-box';\n const iconInner = document.createElement('div');\n let icon = x.icon.trim();\n if (\/^http|data:\/.test(icon)) {\n const img = document.createElement('img');\n img.src = icon;\n iconInner.appendChild(img);\n } else {\n const iconDiv = document.createElement('div');\n const iconText = x.name.trim().match(\/[\\u4e00-\\u9fa5a-zA-Z.]\/)[0];\n iconDiv.textContent = iconText;\n iconInner.appendChild(iconDiv);\n }\n if(isEditWeb){\n const sBox = document.createElement('div');\n sBox.className = \"s-box\";\n sBox.innerHTML = \"📝\"\n webBox.appendChild(sBox);\n }\n \n iconBox.appendChild(iconInner);\n const nameBox = document.createElement('div');\n nameBox.className = 'web-name';\n nameBox.innerHTML = x.name.replace(\/\\n\/g, '<br>'); \n webBox.appendChild(iconBox);\n webBox.appendChild(nameBox);\n document.querySelector(\".webs-box\").appendChild(webBox);\n initSortable();\n});\n}\n\ndocument.querySelector('.rsslist-search input').addEventListener('input', function(e) {\n const searchText = e.target.value.trim().toLowerCase();\n document.querySelectorAll('.rsslist-item').forEach(item => {\n const itemText = item.textContent.toLowerCase();\n item.style.display = (searchText === '' || itemText.includes(searchText)) ? '' : 'none';\n });\n updateSelectAllState();\n});\n\ndocument.querySelector('.search-box input').addEventListener('input', function(e) {\n const searchText = e.target.value.trim().toLowerCase();\n document.querySelectorAll('.web-box').forEach(item => {\n const itemText = item.querySelector(\".web-name\").textContent.toLowerCase()+item.dataset.url.trim().toLowerCase();\n item.style.display = (searchText === '' || itemText.includes(searchText)) ? '' : 'none';\n });\n});\n\ndocument.querySelector('.rsslist-check input').onchange = e => {\n document.querySelectorAll('.rsslist-item:not([style*=\"display: none\"]) input[type=\"checkbox\"]:not([disabled])')\n .forEach(cb => cb.checked = e.target.checked);\n};\n\ndocument.addEventListener('change', e => {\n if (e.target.matches('.rsslist-item input[type=\"checkbox\"]:not([disabled])')) {\n updateSelectAllState();\n }\n});\n\ndocument.addEventListener('click', e => {\n if (e.target.closest('.rsslist-item')) {\n const item = e.target.closest('.rsslist-item');\n const checkbox = item.querySelector('input[type=\"checkbox\"]');\n if (checkbox && !e.target.matches('input[type=\"checkbox\"]') && !checkbox.disabled) {\n checkbox.checked = !checkbox.checked;\n checkbox.dispatchEvent(new Event('change', { bubbles: true }));\n }\n }\n});\n\nfunction updateSelectAllState() {\n const visibleCheckboxes = document.querySelectorAll('.rsslist-item:not([style*=\"display: none\"]) input[type=\"checkbox\"]:not([disabled])');\n \n if (visibleCheckboxes.length === 0) {\n document.querySelector('.rsslist-check input').checked = false;\n return;\n }\n \n const allChecked = Array.from(visibleCheckboxes).every(cb => cb.checked);\n document.querySelector('.rsslist-check input').checked = allChecked;\n}\n\n\nfunction initAllEvents() {\n document.addEventListener('click', function(e) {\n if (e.target.classList.contains('group')) {\n const allGroups = document.querySelectorAll('.group');\n allGroups.forEach(g => g.classList.remove('active'));\n e.target.classList.add('active');\n e.target.scrollIntoView({ behavior: 'smooth', inline: 'center' });\n const groupId = e.target.dataset.id;\n groups.forEach(x=>{\n x.f = x.id===groupId?1:0;\n });\n getRssData.groups = groups;\n source.putVariable(JSON.stringify(getRssData));\n bottomMenu.querySelector(\"span\").textContent = 0;\n bottomMenu.querySelector(\"input\").checked = false;\n getWebs(groupId);\n }\n });\n let g = document.querySelector(\".group.active\")??document.querySelectorAll(\".group\")[0];\n g.scrollIntoView({ behavior: 'smooth', inline: 'center' });\n g.classList.add(\"active\");\n getWebs(g.getAttribute(\"data-id\"));\n initSwipeGesture();\n}\n\nwebs_box.addEventListener('scroll', function() {\n clearTimeout(scrollTimer);\n scrollTimer = setTimeout(() => {\n initSortable();\n }, 50);\n});\n\nfunction initSwipeGesture() {\n const swipeArea = document.querySelector('.webs-c');\n if (!swipeArea) {\n console.error('找不到滑动手势区域');\n return;\n }\n \n let touchStartTime = 0;\n let isPotentialSwipe = true;\n let startX = 0;\n let startY = 0;\n let lastX = 0;\n let lastY = 0;\n const VERTICAL_THRESHOLD = 200; \n const LONG_PRESS_TIME = 200; \/\/ 长按时间阈值(毫秒)\n \n swipeArea.addEventListener('touchstart', function(e) {\n touchStartTime = Date.now();\n swipeStartX = e.touches[0].clientX;\n startX = e.touches[0].clientX;\n startY = e.touches[0].clientY;\n lastX = startX;\n lastY = startY;\n isSwiping = true;\n isPotentialSwipe = true;\n }, { passive: true });\n \n swipeArea.addEventListener('touchmove', function(e) {\n if (!isSwiping || !isPotentialSwipe) return;\n const currentX = e.touches[0].clientX;\n const currentY = e.touches[0].clientY;\n const verticalDiff = Math.abs(currentY - startY);\n if (verticalDiff > VERTICAL_THRESHOLD) {\n isPotentialSwipe = false;\n }\n \n lastX = currentX;\n lastY = currentY;\n }, { passive: true });\n \n swipeArea.addEventListener('touchend', function(e) {\n if (!isSwiping || !isPotentialSwipe) {\n isSwiping = false;\n isPotentialSwipe = true;\n return;\n }\n \n const touchDuration = Date.now() - touchStartTime;\n const endX = e.changedTouches[0].clientX;\n const diffX = endX - swipeStartX;\n if (touchDuration > LONG_PRESS_TIME) {\n isSwiping = false;\n isPotentialSwipe = true;\n return;\n }\n \n if (Math.abs(diffX) > 40) {\n const activeGroup = document.querySelector('.group.active');\n if (activeGroup) {\n const groups = Array.from(document.querySelectorAll('.group'));\n const currentIndex = groups.indexOf(activeGroup);\n \n if (diffX > 0 && currentIndex > 0) {\n groups[currentIndex - 1].click();\n } else if (diffX < 0 && currentIndex < groups.length - 1) {\n groups[currentIndex + 1].click();\n }\n }\n }\n \n isSwiping = false;\n isPotentialSwipe = true;\n }, { passive: true });\n}\n\n\nfunction initSortable() {\n if (sortableInstance) {\n sortableInstance.destroy();\n }\n \n sortableInstance = new Sortable(document.querySelector('.webs-box'), {\n animation: 150,\n fallbackOnBody: false, \n fallbackClass: \"sortable-fallback\",\n fallbackTolerance: 0, \n ghostClass: \"sortable-ghost\",\n dragClass: \"sortable-drag\",\n chosenClass: \"sortable-chosen\",\n dataIdAttr: 'data-url',\n delay: 100,\n \n onEnd: function(evt) {\n const activeGroup = document.querySelector('.group.active');\n if (!activeGroup) return;\n \n const groupId = activeGroup.getAttribute('data-id');\n const container = evt.to;\n const items = Array.from(container.children);\n \n if (groupId === '-2') {\n const sortedDataIds = items.map(item => item.getAttribute('data-url'));\n sortedDataIds.forEach((dataId, sortIndex) => {\n const web = webs.find(w => w.url.toString() === dataId);\n if (web) {\n web.sort = sortIndex; \n }\n });\n}else {\n const sortedDataUrls = items.map(item => item.getAttribute('data-url'));\n const currentGroupWebs = webs.filter(web => web.group === groupId);\n if (currentGroupWebs.length === 0) {\n return;\n }\n const urlToWebMap = {};\n currentGroupWebs.forEach(web => {\n urlToWebMap[web.url] = web;\n });\n const reorderedCurrentGroupWebs = [];\n sortedDataUrls.forEach(dataUrl => {\n if (urlToWebMap[dataUrl]) {\n reorderedCurrentGroupWebs.push(urlToWebMap[dataUrl]);\n }\n });\n webs = webs.filter(web => web.group !== groupId);\n reorderedCurrentGroupWebs.forEach(web => {\n webs.push(web);\n });\n }\n \n getRssData.webs = webs;\n source.putVariable(JSON.stringify(getRssData));\n}\n\n \n });\n}\n\n\nfunction checkMinusTwo() {\n const checkboxes = document.querySelectorAll('.editgroup-item input[type=\"checkbox\"]');\n const allUnchecked = Array.from(checkboxes).every(cb => !cb.checked);\n \n if (allUnchecked) {\n const minusTwo = document.querySelector('.editgroup-item[data-id=\"-2\"] input[type=\"checkbox\"]');\n if (minusTwo) minusTwo.checked = true;\n }\n}\n\ndocument.querySelector('.editgroup-items').addEventListener('change', function(e) {\n if (e.target.type === 'checkbox') {\n setTimeout(checkMinusTwo, 0);\n }\n});\n\nfunction getTextColor(hexColor) {\n const hex = hexColor.replace('#', '');\n const r = parseInt(hex.substr(0, 2), 16);\n const g = parseInt(hex.substr(2, 2), 16);\n const b = parseInt(hex.substr(4, 2), 16);\n const brightness = (r * 299 + g * 587 + b * 114) \/ 1000;\n return brightness > 128 ? '#000000' : '#ffffff';\n}\n\n\nconst resetBtn = document.getElementById('resetBtn');\nconst applyBtn = document.getElementById('applyBtn');\n\n\/\/ 颜色输入元素\nconst backgroundColorInput = document.getElementById('backgroundColorInput');\nconst accentColorInput = document.getElementById('accentColorInput');\nconst iconBgColorInput = document.getElementById('iconBgColorInput');\nconst iconTextColorInput = document.getElementById('iconTextColorInput');\nconst topColorInput = document.getElementById('topColorInput');\n\n\/\/ 颜色值显示元素\nconst backgroundColorValue = document.getElementById('backgroundColorValue');\nconst accentColorValue = document.getElementById('accentColorValue');\nconst iconBgColorValue = document.getElementById('iconBgColorValue');\nconst iconTextColorValue = document.getElementById('iconTextColorValue');\nconst topColorValue = document.getElementById('topColorValue');\n\n\/\/ 颜色预览元素\nconst backgroundColorPreview = document.getElementById('backgroundColorPreview');\nconst accentColorPreview = document.getElementById('accentColorPreview');\nconst iconBgColorPreview = document.getElementById('iconBgColorPreview');\nconst iconTextColorPreview = document.getElementById('iconTextColorPreview');\nconst topColorPreview = document.getElementById('topColorPreview');\n\n\nconst readTheme = JSON.parse(java.getThemeConfig());\n\n\/\/ 默认颜色值\nconst defaultColors = {\n backgroundColor: readTheme[\"backgroundColor\"].replace(\/#..\/,'#') || '#f8f9fa',\n accentColor: readTheme[\"accentColor\"].replace(\/#..\/,'#') || '#367ECE',\n iconBgColor: 'none',\n iconTextColor: readTheme[\"accentColor\"].replace(\/#..\/,'#') || '#367ECE',\n topColor: readTheme[\"primaryColor\"].replace(\/#..\/,'#') || '#ffffff'\n};\n\nlet currentColors = getRssData.theme ?? { ...defaultColors };\n\nfunction updateColorDisplay(type, value) {\n const previewElement = document.getElementById(`${type}Preview`);\n if (previewElement) {\n previewElement.style.backgroundColor = value;\n }\n const valueElement = document.getElementById(`${type}Value`);\n if (valueElement) {\n valueElement.textContent = value;\n }\n currentColors[type] = value;\n if(value !== \"none\")\n document.getElementById(`${type}Input`).value = value;\n}\n\nfunction applyColorsToPage() {\n let textColor = getTextColor(currentColors.accentColor);\n let bgTextColor = getTextColor(currentColors.backgroundColor);\n document.documentElement.style.setProperty('--text-color', textColor);\n document.documentElement.style.setProperty('--bg-text-color', bgTextColor);\n document.documentElement.style.setProperty('--icon-text-color', currentColors.iconTextColor);\n document.documentElement.style.setProperty('--icon-bg-color', currentColors.iconBgColor);\n document.documentElement.style.setProperty('--bg-color', currentColors.backgroundColor);\n document.documentElement.style.setProperty('--active-color', currentColors.accentColor);\n document.documentElement.style.setProperty('--top-color', currentColors.topColor);\n getRssData.theme = currentColors;\n source.putVariable(JSON.stringify(getRssData))\n}\n\nfunction resetColors() {\n updateColorDisplay('backgroundColor', defaultColors.backgroundColor);\n updateColorDisplay('accentColor', defaultColors.accentColor);\n updateColorDisplay('iconBgColor', defaultColors.iconBgColor);\n updateColorDisplay('iconTextColor', defaultColors.iconTextColor);\n updateColorDisplay('topColor', defaultColors.topColor);\n currentColors = { ...defaultColors };\n java.toast('颜色已重置为默认值');\n}\n\nfunction initializeColors() {\n updateColorDisplay('backgroundColor', currentColors.backgroundColor);\n updateColorDisplay('accentColor', currentColors.accentColor);\n updateColorDisplay('iconBgColor', currentColors.iconBgColor);\n updateColorDisplay('iconTextColor', currentColors.iconTextColor);\n updateColorDisplay('topColor', currentColors.topColor);\n applyColorsToPage();\n}\n\nresetBtn.addEventListener('click', resetColors);\napplyBtn.addEventListener('click', () => {\n applyColorsToPage();\n java.toast('颜色设置已应用');\n document.querySelector(\".modal-overlay\").classList.remove(\"active\");\n document.querySelector(\".color-modal\").classList.remove(\"active\")\n});\n\nbackgroundColorInput.addEventListener('input', (e) => updateColorDisplay('backgroundColor', e.target.value));\naccentColorInput.addEventListener('input', (e) => updateColorDisplay('accentColor', e.target.value));\niconBgColorInput.addEventListener('input', (e) => updateColorDisplay('iconBgColor', e.target.value));\niconTextColorInput.addEventListener('input', (e) => updateColorDisplay('iconTextColor', e.target.value));\ntopColorInput.addEventListener('input', (e) => updateColorDisplay('topColor', e.target.value));\n\n\n\/\/ 元素映射\nconst colorElements = {\n backgroundColorValue: 'backgroundColor',\n accentColorValue: 'accentColor',\n iconBgColorValue: 'iconBgColor',\n iconTextColorValue: 'iconTextColor',\n topColorValue: 'topColor'\n};\n\n\/\/ 为每个元素添加监听\nObject.entries(colorElements).forEach(([elementId, type]) => {\n const element = document.getElementById(elementId);\n if (!element) return;\n \n let originalValue = element.textContent;\n \n element.addEventListener('input', function() {\n const text = this.textContent.replace(\/[\\n\\r]\/g, '').substring(0, 7);\n this.textContent = text;\n });\n \n element.addEventListener('keydown', function(e) {\n if (e.key === 'Enter') {\n e.preventDefault();\n this.blur();\n }\n});\n\n element.addEventListener('blur', function() {\n const text = this.textContent.trim();\n const isValid = \/^#[0-9a-fA-F]{6}$|none\/.test(text);\n \n if (isValid) {\n updateColorDisplay(type, text);\n originalValue = text;\n } else {\n this.textContent = originalValue;\n }\n });\n});\n\n\n\n\ninitializeColors();\ndocument.addEventListener('DOMContentLoaded', initAllEvents);",
"startStyle": ":root{\n\t --active-color:#367ECE;\n\t --text-color:#fff;\n\t --top-color:#fff;\n\t --bg-color:#f8f9fa;\n\t --icon-text-color:#000;\n\t --icon-bg-color:#fff;\n\t --bg-text-color:#000;\n}\n * {\n\t margin: 0;\n\t padding: 0;\n\t box-sizing: border-box \n}\n body {\n\t background-color: var(--bg-color);\n\t min-height: 100vh;\n\t padding: 20px;\n\t color: #1a1a1a;\n\t -webkit-font-smoothing: antialiased;\n\t -moz-osx-font-smoothing: grayscale;\n}\n.web-icon-box>div>div{\n background:var(--icon-bg-color);\n color:var(--icon-text-color)\n}\n #bottomMenu{\n\t visibility:hidden;\n\t position:fixed;\n\t bottom:0;\n\t right:0;\n\t left:0;\n\t height:60px;\n\t z-index:1000;\n\t width:100%;\n\t background:white;\n\t border-radius:10px;\n\t box-shadow:0px -8px 10px rgba(0,0,0,0.12);\n\t display:flex;\n\t justify-content:space-between;\n\t align-items:center;\n\t padding:0px 20px;\n\t opacity:0;\n\t transform:translateY(0px);\n\t transition:all 0.25s cubic-bezier(0.4,0,0.2,1);\n}\n div#popupMenu{\n\t background:white;\n\t border-radius:10px;\n\t visibility:hidden;\n\t position:fixed;\n\t right:30px;\n\t top:100px;\n\t transform:translateY(-10px);\n\t box-shadow:0 4px 20px rgba(0,0,0,0.12);\n\t opacity:0;\n\t z-index:1000;\n\t text-align:center \n}\n #bottomMenu button{\n\t padding:5px 20px;\n\t border:0px solid #000;\n\t border-radius:28px;\n\t background:var(--active-color);\n\t color:var(--text-color);\n\t box-shadow:2px 5px 6px rgba(0,0,0,0.2);\n}\n #bottomMenu button.ok{\n\t color:#fff;\n\t background:red \n}\n .popup-item{\n\t padding:10px 16px;\n\t display:flex;\n\t align-items:center;\n\t gap:10px;\n\t color:#374151;\n\t font-size:14px;\n\t transition:all 0.15s ease;\n}\n .popup-item:active {\n\t color: var(--text-color) font-weight:bold;\n\t background:var(--active-color) \n}\n\n .web-icon-box > div::after {\n\t content: '';\n\t position: absolute;\n\t top: 0;\n\t left: 0;\n\t right: 0;\n\t bottom: 0;\n\t z-index: 1;\n\t\n}\n\n .web-box.sortable-chosen:active {\n\t background-color: inherit;\n\t opacity: 0.5;\n}\n .sortable-fallback {\n\t opacity: 0.5;\n\t background: #e6f7ff;\n\t border: 2px dashed #1890ff;\n}\n\n .sortable-ghost {\n\t opacity: 0.4;\n}\n\n .sortable-drag {\n\t opacity: 0.8;\n\t border-radius:8px;\n\t z-index: 9999 !important;\n}\n .hide,.m.hide{\n\t display:none \n}\n .s-box{\n\t position:absolute;\n\t top:10px;\n\t right:5px;\n\t font-size:15px;\n z-index:999;\n}\n .error{\n\t color:red;\n\t font-size:14px;\n\t text-align:center \n}\n\n .header-box {\n\t position:fixed;\n\t display: flex;\n\t flex-direction: column;\n\t align-items: center;\n\t background: var(--top-color);\n\t border-bottom: 1px solid #ddd;\n\t box-shadow: 0px 1px 6px rgba(0, 0, 0, 0.15);\n\t top:0;\n\t left:0;\n\t right:0;\n\t padding:20px 20px 0px;\n\t width:100%;\n}\n\n .modal-overlay {\n\t position: fixed;\n\t top: 0;\n\t left: 0;\n\t right: 0;\n\t bottom: 0;\n\t background-color: rgba(0, 0, 0, 0.5);\n\t z-index: 1000;\n\t opacity: 0;\n\t visibility: hidden;\n\t transition: opacity 0.3s ease, visibility 0.3s ease;\n}\n .modal-overlay.active {\n\t opacity: 1;\n\t visibility: visible;\n}\n\n .color-modal {\n\t position: fixed;\n\t bottom: 0;\n\t left: 0;\n\t right: 0;\n\t background-color: white;\n\t transform: translateY(100%);\n\t border-radius: 24px 24px 0 0;\n\t padding: 0;\n\t z-index: 1001;\n\t transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);\n\t max-height: 85vh;\n\t overflow: hidden;\n\t display: flex;\n\t flex-direction: column;\n\t box-shadow: 0 -10px 40px rgba(0, 0, 0, 0.1);\n}\n .color-modal.active {\n\t transform: translateY(0);\n}\n\n .modal-header {\n\t padding: 14px 16px;\n\t border-bottom: 1px solid #f2f2f7;\n\t display: flex;\n box-shadow: 0 5px 6px rgba(0, 0, 0, 0.2);\n\t justify-content: space-between;\n\t align-items: center;\n background:var(--active-color)\n}\n .modal-title {\n\t font-size: 22px;\n\t font-weight: 600;\n\t color: var(--text-color);\n}\n .close-btn {\n\t background: none;\n\t border: none;\n\t font-size: 20px;\n\t width: 32px;\n\t height: 32px;\n\t border-radius: 8px;\n\t display: flex;\n\t align-items: center;\n\t justify-content: center;\n\t cursor: pointer;\n\t color: var(--text-color);\n\t transition: all 0.2s ease;\n}\n .close-btn:hover {\n\t background-color: #f2f2f7;\n\t color: #1d1d1f;\n}\n\n .modal-content {\n\t flex: 1;\n\t overflow-y: auto;\n\t padding: 0 24px 24px;\n}\n .color-options {\n\t display: flex;\n\t flex-direction: column;\n\t gap: 24px;\n\t margin-top: 20px;\n}\n .color-option {\n\t display: flex;\n\t flex-direction: column;\n\t gap: 10px;\n}\n .color-label {\n\t display: flex;\n\t justify-content: space-between;\n\t align-items: center;\n\t font-size: 16px;\n\t font-weight: 500;\n\t color: #1d1d1f;\n}\n .color-name {\n\t display: flex;\n\t align-items: center;\n\t gap: 8px;\n}\n .color-preview {\n\t width: 24px;\n\t height: 24px;\n\t border-radius: 6px;\n\t border: 2px solid #f2f2f7;\n\t box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n}\n .color-value {\n\t font-size: 14px;\n\t color: #8e8e93;\n\t font-family: 'Monaco', 'Menlo', monospace;\n\t background-color: #f9f9f9;\n\t padding: 2px 8px;\n\t border-radius: 4px;\n}\n .color-input {\n\t width: 100%;\n\t height: 48px;\n\t border: 2px solid #e5e5ea;\n\t border-radius: 12px;\n\t padding: 0;\n\t cursor: pointer;\n\t background: none;\n\t transition: border-color 0.2s ease;\n}\n .color-input:hover {\n\t border-color: #c7c7cc;\n}\n .color-input:focus {\n\t outline: none;\n\t border-color: #007aff;\n}\n\n .modal-footer {\n\t padding: 20px 24px;\n\t border-top: 1px solid #f2f2f7;\n\t display: flex;\n\t gap: 12px;\n}\n .action-btn {\n\t flex: 1;\n\t padding: 14px;\n\t border-radius: 12px;\n\t font-size: 16px;\n\t font-weight: 500;\n\t cursor: pointer;\n\t transition: all 0.2s ease;\n\t border: none;\n}\n .reset-btn {\n\t background-color: #f2f2f7;\n\t color: #1d1d1f;\n}\n .reset-btn:hover {\n\t background-color: #e5e5ea;\n}\n\n .apply-btn {\n\t background-color: var(--active-color);\n\t color: var(--text-color);\n}\n\n .apply-btn:hover {\n\t background-color: #0056cc;\n}\n\n\n .search-box{\n\t margin-bottom:10px;\n\t width:100%;\n}\n .input{\n\t width:100%;\n\t height:30px;\n\t border-radius: 6px;\n\t border: 1px solid #ccc;\n\t padding:10px;\n}\n select.input{\n\t padding:2px \n}\n .input::placeholder{\n\t color: #999;\n\t font-style: italic;\n\t opacity: 0.8;\n}\n .input:focus {\n\t outline: 2px solid var(--active-color);\n\t color:var(--active-color);\n}\n .input:focus::placeholder {\n\t opacity: 0.2;\n}\n\n .tab-box{\n\t position: relative;\n\t display: flex;\n\t align-items: center;\n\t width:100%;\n}\n .groups-box {\n\t display: flex;\n\t max-width:90%;\n\t align-items: center;\n\t overflow: auto;\n\t flex: 1;\n}\n .add-group{\n\t position: absolute;\n\t right:-10px;\n}\n\n .group,.add-group {\n\t text-align: center;\n\t margin-right: 15px;\n\t padding-bottom:8px;\n\t flex-shrink: 0;\n\t\n\t color:#888 \n}\n .group.active{\n\t border-bottom:3px solid var(--active-color);\n\t color:var(--active-color);\n\t font-weight:bold \n}\n .webs-box{\n\t display: flex;\n\t flex-wrap: wrap;\n\t justify-content: flex-start;\n\t width:100%;\n\t max-height:calc(100vh - 150px);\n\t min-height:0;\n\t overflow-y: auto;\n\t overflow-x:hidden;\n\t position: relative;\n}\n .webs-c{\n\t margin-top:100px;\n\t width:100%;\n\t height:calc(100vh - 150px);\n\t max-height:calc(100vh - 150px);\n\t min-height:0;\n\t overflow-y: auto;\n\t overflow-x:hidden;\n}\n .web-box{\n\t position: relative;\n\t width:25%;\n\t max-height:200px;\n\t padding: 10px;\n\t text-align: center;\n\t user-select:none;\n}\n\n .web-box:active ,.web-box.editwebBox.active{\n\t border:2px dashed var(--active-color);\n\t border-radius: 5px;\n\t box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.25);\n}\n .web-icon-box{\n\t width: 100%;\n\t padding-top: 100%;\n\t position: relative;\n}\n .web-icon-box>div {\n\t position: absolute;\n\t top: 0;\n\t left: 0;\n\t right: 0;\n\t bottom: 0;\n\t display: flex;\n\t align-items: center;\n\t justify-content: center;\n}\n .web-icon-box img,.web-icon-box>div>div,.msg-item img{\n\t user-select:none;\n\t width: 80%;\n\t height: 80%;\n\t display: block;\n\t border-radius: 8px;\n\t object-fit: cover;\n\t box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.25);\n\t font-size:1.5em \n}\n\n\n.msg-item .img img{\n border-radius: 2px;\n width: 100%;\n\t height: 100%;\n}\n .web-name{\n\t font-size: 10px;\n\t line-height: 1.4;\n\t display: -webkit-box;\n\t -webkit-line-clamp: 2;\n\t -webkit-box-orient: vertical;\n\t overflow: hidden;\n\t max-height: 4.5em;\n color:var(--bg-text-color)\n}\n .m{\n\t position: fixed;\n\t top: 0;\n\t left: 0;\n\t right: 0;\n\t bottom: 0;\n\t width: 100%;\n\t height: 100%;\n\t background: rgba(0, 0, 0, 0.5);\n\t z-index: 1000;\n\t display: flex;\n\t align-items: center;\n\t justify-content: center;\n\t padding: 20px;\n}\n .box {\n\t position: relative;\n\t width: 90%;\n\t max-width: 600px;\n\t background: #fff;\n\t border-radius: 8px;\n\t padding: 20px;\n\t box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);\n\t max-height: 80vh;\n\t display:flex;\n\t flex-direction: column;\n}\n .title{\n\t position: absolute;\n\t top:0;\n\t left:0;\n\t right:0;\n\t width:100%;\n\t background:var(--active-color);\n\t box-shadow: 0 3px 8px rgba(0, 0, 0, 0.3);\n}\n .title h4{\n\t text-align:center;\n\t color:var(--text-color);\n\t padding:10px \n}\n .editgroup-item{\n\t position: relative;\n\t height:35px;\n\t width:100%;\n\t margin-bottom:10px;\n\t border:1px dashed var(--active-color);\n\t border-radius: 5px;\n\t box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);\n}\n .editgroup-order{\n\t position: absolute;\n\t left:10px;\n\t top:0;\n\t bottom:0;\n}\n .editgroup-name{\n\t position: absolute;\n\t text-indent: 0;\n\t left:30px;\n\t top:0;\n\t bottom:0;\n\t width:70%;\n\t padding:10px;\n\t display: flex;\n\t align-items: center;\n\t justify-content: flex-start;\n\t user-select:none;\n overflow-y: hidden;\n\t word-wrap: normal;\n\t white-space: pre-wrap;\n\t overflow-wrap: break-word;\n\t\n\t max-height: 100%;\n\t scroll-behavior: smooth;\n}\n .editgroup-del{\n\t position: absolute;\n\t right:10px;\n\t top:0;\n\t bottom:0;\n}\n .btn button{\n\t margin:10px;\n\t padding:4px 15px;\n\t border-radius: 4px;\n\t border: none;\n\t outline: none;\n\t box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.20);\n}\n .editgroup-add{\n\t background:#4CAF50;\n\t color:#fff \n}\n .btn .ok{\n\t background:var(--active-color);\n\t color:var(--text-color) \n}\n .btn .no{\n\t background:#ddd \n}\n .editgroup-del,.editgroup-order,.btn,.web-icon-box>div>div,.rsslist-check{\n\t display: flex;\n\t align-items: center;\n\t justify-content: center;\n\t user-select:none \n}\n .btn button:active,#bottomMenu button:active {\n\t animation: floatScale 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);\n}\n .editweb{\n\t display:flex;\n\t margin-top:40px;\n\t flex-direction: column;\n\t justify-content:flex-start;\n}\n .editweb p{\n\t font-size:13px;\n}\n .editweb>div{\n\t margin-bottom:10px \n}\n .input.rssurl{\n\t margin-top:45px;\n\t margin-bottom:10px;\n\t font-size:13px;\n\t width:100%;\n\t height:30px;\n}\n .rsslist-input{\n\t margin-top:40px;\n\t display:flex;\n\t width:100%;\n\t align-items: center;\n\t justify-content:space-between;\n\t margin-left:-5px;\n}\n .rsslist-items{\n\t margin-bottom:10px;\n}\n .rsslist-item,.msg-item{\n\t display:flex;\n\t align-items: center;\n\t justify-content:flex-start;\n\t padding:5px;\n\t width:100%;\n\t margin-bottom:10px;\n\t border:1px dashed var(--active-color);\n\t border-radius: 5px;\n\t box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.3);\n}\n .rsslist-item.select{\n\t border:1px dashed #ddd \n}\n .rsslist-item.select .name{\n\t color:#999 \n}\n .rsslist-name,.msg-name{\n\t width:100%;\n\t max-width: 100%;\n\t margin-left:15px;\n\t overflow: hidden;\n}\n .name{\n\t font-size:12px;\n\t color: var(--active-color);\n\t font-weight:bold;\n\t width:100%;\n}\n .rsslist-name > p,.msg-name > p {\n\t line-height: 1.4;\n\t white-space: nowrap;\n\t overflow: hidden;\n\t text-overflow: ellipsis;\n}\n .rsslist-box input:checked,input:checked{\n\t accent-color: var(--active-color);\n}\n .rssurl{\n\t color:#888;\n\t font-size:10px;\n}\n\n\n.pushrss-edit p{\n font-size:12px;\n margin-bottom:15px\n}\n\n\n .items{\n\t flex: 1;\n\t min-height:50px;\n\t height:400px;\n\t overflow-y: auto;\n\t overflow-x:hidden;\n\t margin-top:20px;\n}\n.msg-item .img{\n width:20px;\n height:auto;\n font-size:15px;\n display:flex;\n\t align-items: center;\n\t justify-content:center\n}\n\n .editgroup-items.items,.pushrss-info{\n\t margin-top:35px;\n}\n @keyframes floatScale {\n\t 0% {\n\t\t transform: translateY(0) scale(1);\n\t}\n\t 30% {\n\t\t transform: translateY(-3px) scale(1.05);\n\t}\n\t 50% {\n\t\t transform: translateY(-5px) scale(1.08);\n\t}\n\t 70% {\n\t\t transform: translateY(-2px) scale(1.03);\n\t}\n\t 85% {\n\t\t transform: translateY(-1px) scale(1.01);\n\t}\n\t 100% {\n\t\t transform: translateY(0) scale(1);\n\t}\n}\n \n @media (min-width: 640px) {\n\t .color-modal {\n\t\t max-width: 500px;\n\t\t left: 50%;\n\t\t transform: translate(-50%, 100%);\n\t\t border-radius: 24px;\n\t}\n\t .color-modal.active {\n\t\t transform: translate(-50%, 0);\n\t}\n}\n @media (max-height: 700px) {\n\t .modal-content {\n\t\t max-height: 50vh;\n\t}\n}",
"type": 0
}