import { DOMUtils, log, MenuRegister, pops, utils } from "@/env"; import { CookieBackUpManager } from "@/main/cookie/backup/CookieBackUpManager"; import { CookieRule } from "@/main/cookieRule/CookieRule"; import { UISelect } from "@components/setting/components/ui-select"; import { UISwitch } from "@components/setting/components/ui-switch"; import { Panel } from "@components/setting/panel"; import { PanelUISize } from "@components/setting/panel-ui-size"; import Qmsg from "qmsg"; import { CookieManager } from "../CookieManager"; import { CookieManagerEditView } from "./CookieManagerEditView"; type EmitterFrom = "refreshButton"; export const CookieManagerView = { $data: { /** * 当前的cookie列表 */ cookieList: [] as (CookieStoreData | GMCookieInstance)[], }, init() { this.registerMenu(); }, /** * 显示视图 */ async showView() { const $alert = pops.alert({ title: { text: "Cookie编辑器", html: false, position: "center", }, content: { text: /*html*/ ` `, html: true, }, btn: { ok: { enable: false, }, }, mask: { enable: true, }, drag: true, width: PanelUISize.setting.width, height: PanelUISize.setting.height, style: /*css*/ ` ${pops.config.cssText.panelCSS} .pops .pops-content{ overflow: hidden; display: flex; flex-direction: column; } .cookie-controls{ display: flex; flex-direction: column; padding: 10px; gap: 10px; } .cookie-wrapper{ display: flex; flex-direction: column; padding: 10px; padding-top: 0px; gap: 10px; overflow: auto; height: 100%; } .cookie-control-wrapper{ --button-margin-left: 0px; display: flex; flex-wrap: nowrap; padding: 0px 10px; gap: 5px; overflow-x: auto; } .cookie-search-wrapper{ display: flex; align-items: center; } .cookie-search-inner{ width: 100%; padding: 0px 10px; } .cookie-search-inner input{ height: 30px; padding: 5px 8px; width: 100%; border-radius: 6px; } .cookie-search-inner input::placeholder{ display: flex; align-items: baseline; } .cookie-search-inner input:focus-visible{ outline: none; } .cookie-setting, .cookie-search-setting{ display: flex; align-items: center; } .cookie-setting svg, .cookie-search-setting svg{ cursor: pointer; } .cookie-list-wrapper{ display: flex; flex-wrap: wrap; gap: 10px; } .cookie-item{ display: flex; flex-direction: column; padding: 10px 10px; margin: 0px 10px; background: #f1efef; border-radius: 10px; gap: 5px; box-sizing: border-box; width: 100%; } .cookie-item-group{ display: flex; align-items: stretch; } .cookie-item-group-left{ width: 100px; min-width: 100px; max-width: 100px; text-transform: capitalize } .cookie-item-group-control{ align-items: center; } .cookie-item-group-control .cookie-item-group-right{ display: flex; align-items: center; gap: 10px; } .cookie-item-group-control .cookie-item-group-control-copy, .cookie-item-group-control .cookie-item-group-control-edit, .cookie-item-group-control .cookie-item-group-control-delete{ display: flex; align-items: center; } .cookie-item-group-control .cookie-item-group-control-delete svg{ width: 16px; height: 16px; } .cookie-item-group-control svg{ cursor: pointer; } `, darkStyle: /*css*/ ` .cookie-item, #cookie-item-property-expires{ background: #232323; } svg path{ fill: currentColor; } .cookie-search-inner input{ background: #000000; color: #ffffff; border-color: #ffffff; } .cookie-search-inner input::placeholder{ } .cookie-search-inner input:focus-visible{ } `, }); const $search = $alert.$shadowRoot.querySelector(".cookie-search-inner input")!; const $searchSetting = $alert.$shadowRoot.querySelector(".cookie-search-setting")!; const $refresh = $alert.$shadowRoot.querySelector(".cookie-control-refresh")!; const $add = $alert.$shadowRoot.querySelector(".cookie-control-add")!; const $export = $alert.$shadowRoot.querySelector(".cookie-control-export")!; const $import = $alert.$shadowRoot.querySelector(".cookie-control-import")!; const $clearAll = $alert.$shadowRoot.querySelector(".cookie-control-clear-all")!; const $ruleManager = $alert.$shadowRoot.querySelector(".cookie-control-rule-manager")!; const $setting = $alert.$shadowRoot.querySelector(".cookie-setting")!; const $cookieListWrapper = $alert.$shadowRoot.querySelector(".cookie-list-wrapper")!; /** * 生成单个cookie的元素 */ const createCookieItemElement = (cookieInfo: GMCookieInstance | CookieStoreData) => { const $cookieItem = DOMUtils.createElement("div", { className: "cookie-item", innerHTML: /*html*/ ` `, "data-cookie-info": cookieInfo, }); const cookieProperty: { leftText: string; rightText: string; }[] = [ { leftText: "name", rightText: cookieInfo.name, }, { leftText: "value", // 解码值 rightText: Panel.getValue("decode-cookie-value") ? decodeURIComponent(cookieInfo.value) : encodeURIComponent(cookieInfo.value), }, ]; if (CookieManager.baseCookieHandler === "GM_cookie" || CookieManager.baseCookieHandler === "GM.cookie") { cookieInfo = cookieInfo as GMCookieInstance; cookieProperty.push( { leftText: "domain", rightText: cookieInfo.domain, }, { leftText: "path", rightText: cookieInfo.path, }, { leftText: "session", rightText: JSON.stringify(cookieInfo.session), }, { leftText: "expires", rightText: cookieInfo.session ? "会话" : cookieInfo.expirationDate ? new Date(cookieInfo.expirationDate * 1000).toISOString() : "未知", }, { leftText: "httpOnly", rightText: JSON.stringify(cookieInfo.httpOnly), }, { leftText: "hostOnly", rightText: JSON.stringify(cookieInfo.hostOnly), }, { leftText: "secure", rightText: JSON.stringify(cookieInfo.secure), }, { leftText: "sameSite", rightText: cookieInfo.sameSite, } ); } else if (CookieManager.baseCookieHandler === "cookieStore") { cookieInfo = cookieInfo as CookieStoreData; cookieProperty.push( { leftText: "domain", rightText: cookieInfo.domain, }, { leftText: "path", rightText: cookieInfo.path, }, { leftText: "expires", rightText: cookieInfo.expires ? new Date(cookieInfo.expires).toISOString() : "会话", }, { leftText: "secure", rightText: JSON.stringify(cookieInfo.secure), }, { leftText: "sameSite", rightText: cookieInfo.sameSite, } ); } cookieProperty.forEach((it) => { const $cookieItemGroup = DOMUtils.createElement("div", { className: "cookie-item-group", innerHTML: /*html*/ ` `, }); Reflect.set($cookieItemGroup, "data-key", it.leftText); Reflect.set($cookieItemGroup, "data-value", it.rightText); DOMUtils.append($cookieItem, $cookieItemGroup); }); const $cookieItemGroupControl = DOMUtils.createElement("div", { className: "cookie-item-group cookie-item-group-control", innerHTML: /*html*/ ` `, }); const $cookieItemCopy = $cookieItemGroupControl.querySelector(".cookie-item-group-control-copy")!; const $cookieItemEdit = $cookieItemGroupControl.querySelector(".cookie-item-group-control-edit")!; const $cookieItemDelete = $cookieItemGroupControl.querySelector( ".cookie-item-group-control-delete" )!; // 单个cookie的复制事件 DOMUtils.on($cookieItemCopy, "click", (event) => { DOMUtils.preventEvent(event); const cookieText = cookieInfo.value; utils.copy(cookieText).then((status) => { if (status) { Qmsg.success("复制成功"); } else { Qmsg.error("复制失败"); } }); }); // 单个cookie的编辑事件 DOMUtils.on($cookieItemEdit, "click", (event) => { DOMUtils.preventEvent(event); CookieManagerEditView.showView(cookieInfo, (__cookieInfo__) => { // 添加新的 const $newCookieItem = createCookieItemElement(__cookieInfo__); DOMUtils.after($cookieItem, $newCookieItem); // 删除旧的 $cookieItem.parentElement?.removeChild($cookieItem); }); }); // 单个cookie的删除事件 DOMUtils.on($cookieItemDelete, "click", (event) => { DOMUtils.preventEvent(event); const result = confirm("确定删除该Cookie?"); if (!result) { return; } CookieManager.delete(cookieInfo).then((status) => { if (!status) { Qmsg.success("删除成功"); $cookieItem.parentElement?.removeChild($cookieItem); } else { log.error(status); Qmsg.error("删除失败"); } }); }); DOMUtils.append($cookieItem, [$cookieItemGroupControl]); return $cookieItem; }; /** * 更新cookie列表 */ const updateCookieListGroup = async ( /** * @returns * + true 需要 * + false 不需要 */ filterCallBack?: (cookieInfo: GMCookieInstance | CookieStoreData) => boolean ) => { const cookieList = await CookieManager.listAll(); DOMUtils.empty($cookieListWrapper); const $fragment = document.createDocumentFragment(); const excludeSessionCookie = Panel.getValue("exclude-session-cookie"); cookieList.forEach((cookieInfo) => { if (excludeSessionCookie) { // 屏蔽session的cookie if ((cookieInfo).session) { return; } if (CookieManager.baseCookieHandler === "cookieStore" && (cookieInfo).expires == null) { // 如果Api是cookieStore的话,expires的值为空时就是session的cookie return; } } if (typeof filterCallBack === "function") { const filterResult = filterCallBack(cookieInfo); if (!filterResult) { return; } } const $cookieItem = createCookieItemElement(cookieInfo); $fragment.appendChild($cookieItem); }); this.$data.cookieList = cookieList; $cookieListWrapper.appendChild($fragment); }; // 设置搜索事件 DOMUtils.on( $search, ["input", "propertychange"], utils.debounce(async (event) => { const searchText = DOMUtils.val($search); // 是否过滤 const isNotFilter = searchText.trim() === ""; const enableRegExp = Panel.getValue("search-config-use-regexp"); await updateCookieListGroup((cookieItem) => { if (isNotFilter) { return true; } return enableRegExp ? Boolean(cookieItem.name.match(new RegExp(searchText))) : cookieItem.name.includes(searchText); }); // @ts-expect-error const from = event.from as EmitterFrom; if (from === "refreshButton") { Qmsg.success("刷新成功"); } }) ); // 点击某个属性的时候复制值 DOMUtils.on($alert.$shadowRoot, "click", ".cookie-item-group:has(.cookie-item-group-right > p)", (evt, $click) => { // @ts-expect-error const selectRange: Selection | null = $alert.$shadowRoot?.getSelection?.(); if (selectRange?.type === "Range") { // 光标选中,不复制 return; } const key = Reflect.get($click, "data-key"); const value = Reflect.get($click, "data-value"); if (!key) { log.error("该项上未获取到data-key值"); return; } if (!value) { log.error("该项上未获取到data-value值"); return; } utils.copy(value).then((status) => { if (status) { Qmsg.success(`复制 ${key} 成功`); } else { Qmsg.error(`复制 ${key} 失败`); } }); }); // 监听回车事件 DOMUtils.onKeyboard($search, "keyup", (keyName, keyValue, otherCodeList) => { if (keyName === "Enter" && otherCodeList.length === 0) { emitUpdateCookieListGroupWithSearchFilter(); } }); // 设置搜索设置的点击事件 DOMUtils.on($searchSetting, "click", (event) => { DOMUtils.preventEvent(event); const $settingAlert = pops.alert({ title: { text: "搜索配置", position: "center", }, content: { text: "", html: true, }, btn: { ok: { enable: false, }, }, drag: true, mask: { clickEvent: { toClose: true, }, }, width: PanelUISize.info.width, height: PanelUISize.info.height, style: /*css*/ ` ${pops.config.cssText.panelCSS} .pops-alert-content li{ display: flex; justify-content: space-between; align-items: center; padding: 10px; } .pops-panel-item-left-desc-text{ line-height: normal; margin-top: 6px; font-size: 0.8em; color: rgb(108, 108, 108); } `, }); const $content = $settingAlert.$shadowRoot.querySelector(".pops-alert-content")!; const panelHandlerComponents = pops.fn.PanelHandlerComponents(); const $useRegExp = panelHandlerComponents.createSectionContainerItem_switch( UISwitch("启用正则表达式", "search-config-use-regexp", false, void 0, "使用正则表达式搜索Cookie名称", () => { emitUpdateCookieListGroupWithSearchFilter(); }) ).$el; DOMUtils.append($content, $useRegExp); }); // 刷新 - 点击事件 DOMUtils.on($refresh, "click", (event) => { DOMUtils.preventEvent(event); emitUpdateCookieListGroupWithSearchFilter("refreshButton"); }); // 添加 - 点击事件 DOMUtils.on($add, "click", (event) => { DOMUtils.preventEvent(event); CookieManagerEditView.showView(void 0, (__cookieInfo__) => { emitUpdateCookieListGroupWithSearchFilter(); }); }); // 导出 - 点击事件 DOMUtils.on($export, "click", async (event) => { DOMUtils.preventEvent(event); CookieBackUpManager.showExportDialog(); }); // 导入 - 点击事件 DOMUtils.on($import, "click", async (event) => { DOMUtils.preventEvent(event); CookieBackUpManager.showImportDialog(); }); // 清空全部 - 点击事件 DOMUtils.on($clearAll, "click", async (event) => { DOMUtils.preventEvent(event); const result = window.confirm("确定清除全部Cookie?"); if (!result) { return; } const deleteInfo = await CookieManager.clear(); if (deleteInfo.error) { Qmsg.warning(`清除成功:${deleteInfo.success} 失败:${deleteInfo.error}`); } else { Qmsg.success("清除成功"); } emitUpdateCookieListGroupWithSearchFilter(); }); // 规则管理 - 点击事件 DOMUtils.on($ruleManager, "click", (event) => { DOMUtils.preventEvent(event); CookieRule.showView(); }); // 设置 - 点击事件 DOMUtils.on($setting, "click", (event) => { DOMUtils.preventEvent(event); const $settingAlert = pops.alert({ title: { text: "设置", position: "center", }, content: { text: "", html: true, }, btn: { ok: { enable: false, }, }, drag: true, mask: { clickEvent: { toClose: true, }, }, width: PanelUISize.settingMiddle.width, height: PanelUISize.settingMiddle.height, style: /*css*/ ` ${pops.config.cssText.panelCSS} .pops-alert-content li{ display: flex; justify-content: space-between; align-items: center; padding: 10px; } .pops-panel-item-left-desc-text{ line-height: normal; margin-top: 6px; font-size: 0.8em; color: rgb(108, 108, 108); }`, }); const $content = $settingAlert.$shadowRoot.querySelector(".pops-alert-content")!; const panelHandlerComponents = pops.fn.PanelHandlerComponents(); const $useGM_cookie = panelHandlerComponents.createSectionContainerItem_select( UISelect( "CookieManager Api", "cookie-manager-api", "document.cookie", CookieManager.totalCookieManagerApiNameList.map((it) => { return { text: it, value: it, }; }), void 0, "操作Cookie的Api函数", () => { // 更新Cookie列表 emitUpdateCookieListGroupWithSearchFilter(); } ) ).$el; const $decodeValue = panelHandlerComponents.createSectionContainerItem_switch( UISwitch( "解码Cookie值", "decode-cookie-value", false, () => { emitUpdateCookieListGroupWithSearchFilter(); }, "对Cookie值进行解码" ) ).$el; const $excludeSessionCookie = panelHandlerComponents.createSectionContainerItem_switch( UISwitch( "排除Session Cookie", "exclude-session-cookie", false, () => { emitUpdateCookieListGroupWithSearchFilter(); }, "过滤掉浏览器会话Cookie" ) ).$el; DOMUtils.append($content, [$useGM_cookie, $decodeValue, $excludeSessionCookie]); }); /** * 主动触发更新Cookie列表(根据搜索内容) */ const emitUpdateCookieListGroupWithSearchFilter = (from?: EmitterFrom) => { DOMUtils.emit($search, "input", { from: from, }); }; emitUpdateCookieListGroupWithSearchFilter(); }, /** * 注册脚本菜单 */ registerMenu() { MenuRegister.add({ key: "cookie_manager_view", text: "⚙ Cookie管理", autoReload: false, isStoreValue: false, showText(text) { return text; }, callback: () => { this.showView(); }, }); }, };