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*/ `
操作
${pops.config.iconSVG.delete}
`,
});
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();
},
});
},
};