CookieManager

简单而强大的Cookie编辑器,允许您快速创建、编辑和删除Cookie

  1. // ==UserScript==
  2. // @name CookieManager
  3. // @namespace https://github.com/WhiteSevs/TamperMonkeyScript
  4. // @version 2025.8.5
  5. // @author WhiteSevs
  6. // @description 简单而强大的Cookie编辑器,允许您快速创建、编辑和删除Cookie
  7. // @license GPL-3.0-only
  8. // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAA/NJREFUeF7tWm1u4jAQjVHusfR3jMQNlp5ky0l2e5K2Jym9ARLO79JzEMgyKF5FqeN5YzuwQPoHqRl/zPObN5NxVHbnf+rO/c9GAEYG3DkCYwjcOQFGETxbCGitF1mWLeq6/qGUmtZ1PW1+t0qpN2PMn0uwcTAA5vP5tKqqp8bhJ8C5R2PMCrBLajIIAFprOs3fkp0eGfFaluVSMiaFbVIAQhy3Tlw1AA3dXyjGQ0/lagFoxO091PGrZkAq5wmEq2NASuevDgCB889HXVjleb6tqmraaASbHeq6ptpgm+f5cr1eb2PDyzdenAUawfsENuXM60VRvBydQ+oCWmJljHkE1go2EQOgtSbB86q9L54FAJIubMuyfAj2DhgoAmA2m1FlR+nO+6eUWm42m9c+IwREO9YYI9ojt7fuc9HkRVF8Uv0OLPLsq+0F82T/DQDCKs8bu1rrGgDxZELh5LKdTCYfPpah88MMkGy6WdwpghL6A054mQaMx/oBaOy3F2xS2ZtNg/SsqqqocrnrUAqRhBggTF0I8MlsYjUCAiCA/skc5CYaHACO/rZq42oDzpHQ55RyXWNRgWQZ0Ef/xvFlu4sjzBShPkPjXPtzDQwGIM/zB1edzjEG2n06I7aUZgFwpS3u1dVX6LSyg83zv8DiKgSWeABczkSUut82ZJun0h4iggaSJhEGuKq20FLXWRylaKm5ABkMAN/Efb0CbjNDCSiXJlkGeOL5GwuYk/TG4xDiyYFOrGEB8NXuVtCoe0M3Pb445jbDVZs0/rRhpegX7T4nEUFJB8erTX3iyTVJulkHFU4uW6EMEN/y9KFgGdPuFQAC2HuK3JtlKgCIbtF9f8ebom12euncV3DRfFxzlkvXEAM4eiL5OMbGp+Lc3rgMAAHQIM02QmOcZMb23hr7AEDoLwEgeRgIAOstuny1A0J/GACOagJnxKYu4UTiH6E/DAAZpipUQvsHRGlqhDb1Bm3Jd8ME9wrZQsgeGbFgt9u9h765nbN/gJ6+iAGxLDhX/wCNfXuwMANoAFC0OGOcU2TJRQkjImzp2x0vAsCCIA0F7lS4ig5RTu5do28OMQAtJiA3xHbd0P4B4ru1CfrKLAgAqR6E9A8knmdZFuS8WAS7mxI2MaT9AxSDYOejAZBqgqR/wHmPtr25eYJDoD1xbI3AbbL7PFTwXOskAaAljPQBxWBt7r6yWApg2z4ZAO2Kkb4RTtnmHsLxoEJIgjSFxX6/XxwOh5+Cj6L+LdF6Z/gY8kvy5AxwgdQG46S8nc/lm/9RY5U+j/uixid6uSk5lEE1IHYjlxp/FgZcyjlk3REABKVbthkZcMuni/g2MgBB6ZZt/gJwetZfo7objAAAAABJRU5ErkJggg==
  9. // @supportURL https://github.com/WhiteSevs/TamperMonkeyScript/issues
  10. // @match *://*/*
  11. // @require https://fastly.jsdelivr.net/gh/WhiteSevs/TamperMonkeyScript@86be74b83fca4fa47521cded28377b35e1d7d2ac/lib/CoverUMD/index.js
  12. // @require https://fastly.jsdelivr.net/npm/@whitesev/utils@2.7.2/dist/index.umd.js
  13. // @require https://fastly.jsdelivr.net/npm/@whitesev/domutils@1.5.11/dist/index.umd.js
  14. // @require https://fastly.jsdelivr.net/npm/@whitesev/pops@2.3.0/dist/index.umd.js
  15. // @require https://fastly.jsdelivr.net/npm/qmsg@1.4.0/dist/index.umd.js
  16. // @require https://fastly.jsdelivr.net/gh/WhiteSevs/TamperMonkeyScript@886625af68455365e426018ecb55419dd4ea6f30/lib/CryptoJS/index.js
  17. // @connect *
  18. // @grant GM.cookie
  19. // @grant GM_cookie
  20. // @grant GM_deleteValue
  21. // @grant GM_getValue
  22. // @grant GM_info
  23. // @grant GM_registerMenuCommand
  24. // @grant GM_setValue
  25. // @grant GM_unregisterMenuCommand
  26. // @grant GM_xmlhttpRequest
  27. // @grant unsafeWindow
  28. // @run-at document-start
  29. // ==/UserScript==
  30.  
  31. (function (Qmsg, DOMUtils, Utils, pops, CryptoJS) {
  32. 'use strict';
  33.  
  34. var _GM = /* @__PURE__ */ (() => typeof GM != "undefined" ? GM : void 0)();
  35. var _GM_cookie = /* @__PURE__ */ (() => typeof GM_cookie != "undefined" ? GM_cookie : void 0)();
  36. var _GM_deleteValue = /* @__PURE__ */ (() => typeof GM_deleteValue != "undefined" ? GM_deleteValue : void 0)();
  37. var _GM_getValue = /* @__PURE__ */ (() => typeof GM_getValue != "undefined" ? GM_getValue : void 0)();
  38. var _GM_info = /* @__PURE__ */ (() => typeof GM_info != "undefined" ? GM_info : void 0)();
  39. var _GM_registerMenuCommand = /* @__PURE__ */ (() => typeof GM_registerMenuCommand != "undefined" ? GM_registerMenuCommand : void 0)();
  40. var _GM_setValue = /* @__PURE__ */ (() => typeof GM_setValue != "undefined" ? GM_setValue : void 0)();
  41. var _GM_unregisterMenuCommand = /* @__PURE__ */ (() => typeof GM_unregisterMenuCommand != "undefined" ? GM_unregisterMenuCommand : void 0)();
  42. var _GM_xmlhttpRequest = /* @__PURE__ */ (() => typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0)();
  43. var _unsafeWindow = /* @__PURE__ */ (() => typeof unsafeWindow != "undefined" ? unsafeWindow : void 0)();
  44. var _monkeyWindow = /* @__PURE__ */ (() => window)();
  45. const KEY = "GM_Panel";
  46. const ATTRIBUTE_INIT = "data-init";
  47. const ATTRIBUTE_KEY = "data-key";
  48. const ATTRIBUTE_DEFAULT_VALUE = "data-default-value";
  49. const ATTRIBUTE_INIT_MORE_VALUE = "data-init-more-value";
  50. const PROPS_STORAGE_API = "data-storage-api";
  51. const PanelUISize = {
  52. /**
  53. * 一般设置界面的尺寸
  54. */
  55. setting: {
  56. get width() {
  57. if (window.innerWidth < 550) {
  58. return "88vw";
  59. } else if (window.innerWidth < 700) {
  60. return "550px";
  61. } else {
  62. return "700px";
  63. }
  64. },
  65. get height() {
  66. if (window.innerHeight < 450) {
  67. return "70vh";
  68. } else if (window.innerHeight < 550) {
  69. return "450px";
  70. } else {
  71. return "550px";
  72. }
  73. }
  74. },
  75. /**
  76. * 中等的设置界面
  77. */
  78. settingMiddle: {
  79. get width() {
  80. return window.innerWidth < 350 ? "88vw" : "350px";
  81. },
  82. get height() {
  83. return window.innerHeight < 450 ? "88vh" : "450px";
  84. }
  85. },
  86. /**
  87. * 信息界面,一般用于提示信息之类
  88. */
  89. info: {
  90. get width() {
  91. return window.innerWidth < 350 ? "88vw" : "350px";
  92. },
  93. get height() {
  94. return window.innerHeight < 250 ? "88vh" : "250px";
  95. }
  96. }
  97. };
  98. class StorageUtils {
  99. /** 存储的键名 */
  100. storageKey;
  101. listenerData;
  102. /**
  103. * 存储的键名,可以是多层的,如:a.b.c
  104. *
  105. * 那就是
  106. * {
  107. * "a": {
  108. * "b": {
  109. * "c": {
  110. * ...你的数据
  111. * }
  112. * }
  113. * }
  114. * }
  115. * @param key
  116. */
  117. constructor(key) {
  118. if (typeof key === "string") {
  119. let trimKey = key.trim();
  120. if (trimKey == "") {
  121. throw new Error("key参数不能为空字符串");
  122. }
  123. this.storageKey = trimKey;
  124. } else {
  125. throw new Error("key参数类型错误,必须是字符串");
  126. }
  127. this.listenerData = new Utils.Dictionary();
  128. }
  129. /**
  130. * 获取本地值
  131. */
  132. getLocalValue() {
  133. let localValue = _GM_getValue(this.storageKey);
  134. if (localValue == null) {
  135. localValue = {};
  136. this.setLocalValue(localValue);
  137. }
  138. return localValue;
  139. }
  140. /**
  141. * 设置本地值
  142. * @param value
  143. */
  144. setLocalValue(value) {
  145. _GM_setValue(this.storageKey, value);
  146. }
  147. /**
  148. * 设置值
  149. * @param key 键
  150. * @param value 值
  151. */
  152. set(key, value) {
  153. let oldValue = this.get(key);
  154. let localValue = this.getLocalValue();
  155. Reflect.set(localValue, key, value);
  156. this.setLocalValue(localValue);
  157. this.triggerValueChangeListener(key, oldValue, value);
  158. }
  159. /**
  160. * 获取值
  161. * @param key 键
  162. * @param defaultValue 默认值
  163. */
  164. get(key, defaultValue) {
  165. let localValue = this.getLocalValue();
  166. return Reflect.get(localValue, key) ?? defaultValue;
  167. }
  168. /**
  169. * 获取所有值
  170. */
  171. getAll() {
  172. let localValue = this.getLocalValue();
  173. return localValue;
  174. }
  175. /**
  176. * 删除值
  177. * @param key 键
  178. */
  179. delete(key) {
  180. let oldValue = this.get(key);
  181. let localValue = this.getLocalValue();
  182. Reflect.deleteProperty(localValue, key);
  183. this.setLocalValue(localValue);
  184. this.triggerValueChangeListener(key, oldValue, void 0);
  185. }
  186. /**
  187. * 判断是否存在该值
  188. */
  189. has(key) {
  190. let localValue = this.getLocalValue();
  191. return Reflect.has(localValue, key);
  192. }
  193. /**
  194. * 获取所有键
  195. */
  196. keys() {
  197. let localValue = this.getLocalValue();
  198. return Reflect.ownKeys(localValue);
  199. }
  200. /**
  201. * 获取所有值
  202. */
  203. values() {
  204. let localValue = this.getLocalValue();
  205. return Reflect.ownKeys(localValue).map(
  206. (key) => Reflect.get(localValue, key)
  207. );
  208. }
  209. /**
  210. * 清空所有值
  211. */
  212. clear() {
  213. _GM_deleteValue(this.storageKey);
  214. }
  215. /**
  216. * 监听值改变
  217. * + .set
  218. * + .delete
  219. * @param key 监听的键
  220. * @param callback 值改变的回调函数
  221. */
  222. addValueChangeListener(key, callback) {
  223. let listenerId = Math.random();
  224. let listenerData = this.listenerData.get(key) || [];
  225. listenerData.push({
  226. id: listenerId,
  227. key,
  228. callback
  229. });
  230. this.listenerData.set(key, listenerData);
  231. return listenerId;
  232. }
  233. /**
  234. * 移除监听
  235. * @param listenerId 监听的id或键名
  236. */
  237. removeValueChangeListener(listenerId) {
  238. let flag = false;
  239. for (const [key, listenerData] of this.listenerData.entries()) {
  240. for (let index = 0; index < listenerData.length; index++) {
  241. const value = listenerData[index];
  242. if (typeof listenerId === "string" && value.key === listenerId || typeof listenerId === "number" && value.id === listenerId) {
  243. listenerData.splice(index, 1);
  244. index--;
  245. flag = true;
  246. }
  247. }
  248. this.listenerData.set(key, listenerData);
  249. }
  250. return flag;
  251. }
  252. /**
  253. * 主动触发监听器
  254. * @param key 键
  255. * @param oldValue (可选)旧值
  256. * @param newValue (可选)新值
  257. */
  258. triggerValueChangeListener(key, oldValue, newValue) {
  259. if (!this.listenerData.has(key)) {
  260. return;
  261. }
  262. let listenerData = this.listenerData.get(key);
  263. for (let index = 0; index < listenerData.length; index++) {
  264. const data = listenerData[index];
  265. if (typeof data.callback === "function") {
  266. let value = this.get(key);
  267. let __newValue;
  268. let __oldValue;
  269. if (typeof oldValue !== "undefined" && arguments.length >= 2) {
  270. __oldValue = oldValue;
  271. } else {
  272. __oldValue = value;
  273. }
  274. if (typeof newValue !== "undefined" && arguments.length > 2) {
  275. __newValue = newValue;
  276. } else {
  277. __newValue = value;
  278. }
  279. data.callback(key, __oldValue, __newValue);
  280. }
  281. }
  282. }
  283. }
  284. const PopsPanelStorageApi = new StorageUtils(KEY);
  285. const PanelMenu = {
  286. $data: {
  287. __menuOption: [
  288. {
  289. key: "show_pops_panel_setting",
  290. text: "⚙ 设置",
  291. autoReload: false,
  292. isStoreValue: false,
  293. showText(text) {
  294. return text;
  295. },
  296. callback: () => {
  297. Panel.showPanel(PanelContent.getConfig(0));
  298. }
  299. }
  300. ],
  301. get menuOption() {
  302. return this.__menuOption;
  303. }
  304. },
  305. init() {
  306. this.initExtensionsMenu();
  307. },
  308. /**
  309. * 初始化菜单项
  310. */
  311. initExtensionsMenu() {
  312. if (!Panel.isTopWindow()) {
  313. return;
  314. }
  315. GM_Menu.add(this.$data.menuOption);
  316. },
  317. /**
  318. * 添加菜单项
  319. * @param option 菜单配置
  320. */
  321. addMenuOption(option) {
  322. if (!Array.isArray(option)) {
  323. option = [option];
  324. }
  325. this.$data.menuOption.push(...option);
  326. },
  327. /**
  328. * 更新菜单项
  329. * @param option 菜单配置
  330. */
  331. updateMenuOption(option) {
  332. if (!Array.isArray(option)) {
  333. option = [option];
  334. }
  335. option.forEach((optionItem) => {
  336. let findIndex = this.$data.menuOption.findIndex((it) => {
  337. return it.key === optionItem.key;
  338. });
  339. if (findIndex !== -1) {
  340. this.$data.menuOption[findIndex] = optionItem;
  341. }
  342. });
  343. },
  344. /**
  345. * 获取菜单项
  346. * @param [index=0] 索引
  347. */
  348. getMenuOption(index = 0) {
  349. return this.$data.menuOption[index];
  350. },
  351. /**
  352. * 删除菜单项
  353. * @param [index=0] 索引
  354. */
  355. deleteMenuOption(index = 0) {
  356. this.$data.menuOption.splice(index, 1);
  357. }
  358. };
  359. const Panel = {
  360. /** 数据 */
  361. $data: {
  362. /**
  363. * @private
  364. */
  365. __contentConfigInitDefaultValue: null,
  366. /**
  367. * @private
  368. */
  369. __onceExecMenuData: null,
  370. /**
  371. * @private
  372. */
  373. __onceExecData: null,
  374. /**
  375. * @private
  376. */
  377. __panelConfig: {},
  378. $panel: null,
  379. /**
  380. * 菜单项初始化的默认值
  381. */
  382. get contentConfigInitDefaultValue() {
  383. if (this.__contentConfigInitDefaultValue == null) {
  384. this.__contentConfigInitDefaultValue = new utils.Dictionary();
  385. }
  386. return this.__contentConfigInitDefaultValue;
  387. },
  388. /**
  389. * 菜单项初始化时禁用的键
  390. */
  391. contentConfigInitDisabledKeys: [],
  392. /**
  393. * 成功只执行了一次的项
  394. */
  395. get onceExecMenuData() {
  396. if (this.__onceExecMenuData == null) {
  397. this.__onceExecMenuData = new utils.Dictionary();
  398. }
  399. return this.__onceExecMenuData;
  400. },
  401. /**
  402. * 成功只执行了一次的项
  403. */
  404. get onceExecData() {
  405. if (this.__onceExecData == null) {
  406. this.__onceExecData = new utils.Dictionary();
  407. }
  408. return this.__onceExecData;
  409. },
  410. /** 脚本名,一般用在设置的标题上 */
  411. get scriptName() {
  412. return SCRIPT_NAME;
  413. },
  414. /**
  415. * pops.panel的默认配置
  416. */
  417. get panelConfig() {
  418. return this.__panelConfig;
  419. },
  420. set panelConfig(value) {
  421. this.__panelConfig = value;
  422. },
  423. /** 菜单项的总值在本地数据配置的键名 */
  424. key: KEY,
  425. /** 菜单项在attributes上配置的菜单键 */
  426. attributeKeyName: ATTRIBUTE_KEY,
  427. /** 菜单项在attributes上配置的菜单默认值 */
  428. attributeDefaultValueName: ATTRIBUTE_DEFAULT_VALUE
  429. },
  430. init() {
  431. this.initContentDefaultValue();
  432. PanelMenu.init();
  433. },
  434. /** 判断是否是顶层窗口 */
  435. isTopWindow() {
  436. return _unsafeWindow.top === _unsafeWindow.self;
  437. },
  438. /** 初始化菜单项的默认值保存到本地数据中 */
  439. initContentDefaultValue() {
  440. const initDefaultValue = (config) => {
  441. if (!config.attributes) {
  442. return;
  443. }
  444. if (config.type === "button" || config.type === "forms" || config.type === "deepMenu") {
  445. return;
  446. }
  447. let menuDefaultConfig = /* @__PURE__ */ new Map();
  448. let key = config.attributes[ATTRIBUTE_KEY];
  449. if (key != null) {
  450. const defaultValue = config.attributes[ATTRIBUTE_DEFAULT_VALUE];
  451. menuDefaultConfig.set(key, defaultValue);
  452. }
  453. let moreMenuDefaultConfig = config.attributes[ATTRIBUTE_INIT_MORE_VALUE];
  454. if (typeof moreMenuDefaultConfig === "object" && moreMenuDefaultConfig) {
  455. Object.keys(moreMenuDefaultConfig).forEach((key2) => {
  456. menuDefaultConfig.set(key2, moreMenuDefaultConfig[key2]);
  457. });
  458. }
  459. if (!menuDefaultConfig.size) {
  460. log.warn(["请先配置键", config]);
  461. return;
  462. }
  463. let __attr_init__ = config.attributes[ATTRIBUTE_INIT];
  464. if (typeof __attr_init__ === "function") {
  465. let __attr_result__ = __attr_init__();
  466. if (typeof __attr_result__ === "boolean" && !__attr_result__) {
  467. return;
  468. }
  469. }
  470. if (config.type === "switch") {
  471. let disabled = typeof config.disabled === "function" ? config.disabled() : config.disabled;
  472. if (typeof disabled === "boolean" && disabled) {
  473. this.$data.contentConfigInitDisabledKeys.push(
  474. ...menuDefaultConfig.keys()
  475. );
  476. }
  477. }
  478. for (const [__key, __defaultValue] of menuDefaultConfig.entries()) {
  479. this.setDefaultValue(__key, __defaultValue);
  480. }
  481. };
  482. const loopInitDefaultValue = (configList) => {
  483. for (let index = 0; index < configList.length; index++) {
  484. let configItem = configList[index];
  485. initDefaultValue(configItem);
  486. let childForms = configItem.forms;
  487. if (childForms && Array.isArray(childForms)) {
  488. loopInitDefaultValue(childForms);
  489. }
  490. }
  491. };
  492. const contentConfigList = [...PanelContent.getAllContentConfig()];
  493. for (let index = 0; index < contentConfigList.length; index++) {
  494. let leftContentConfigItem = contentConfigList[index];
  495. if (!leftContentConfigItem.forms) {
  496. continue;
  497. }
  498. const rightContentConfigList = leftContentConfigItem.forms;
  499. if (rightContentConfigList && Array.isArray(rightContentConfigList)) {
  500. loopInitDefaultValue(rightContentConfigList);
  501. }
  502. }
  503. this.$data.contentConfigInitDisabledKeys = [
  504. ...new Set(this.$data.contentConfigInitDisabledKeys)
  505. ];
  506. },
  507. /**
  508. * 设置初始化使用的默认值
  509. */
  510. setDefaultValue(key, defaultValue) {
  511. if (this.$data.contentConfigInitDefaultValue.has(key)) {
  512. log.warn("请检查该key(已存在): " + key);
  513. }
  514. this.$data.contentConfigInitDefaultValue.set(key, defaultValue);
  515. },
  516. /**
  517. * 设置值
  518. * @param key 键
  519. * @param value 值
  520. */
  521. setValue(key, value) {
  522. PopsPanelStorageApi.set(key, value);
  523. },
  524. /**
  525. * 获取值
  526. * @param key 键
  527. * @param defaultValue 默认值
  528. */
  529. getValue(key, defaultValue) {
  530. let localValue = PopsPanelStorageApi.get(key);
  531. if (localValue == null) {
  532. if (this.$data.contentConfigInitDefaultValue.has(key)) {
  533. return this.$data.contentConfigInitDefaultValue.get(key);
  534. }
  535. return defaultValue;
  536. }
  537. return localValue;
  538. },
  539. /**
  540. * 删除值
  541. * @param key 键
  542. */
  543. deleteValue(key) {
  544. PopsPanelStorageApi.delete(key);
  545. },
  546. /**
  547. * 判断该键是否存在
  548. * @param key 键
  549. */
  550. hasKey(key) {
  551. return PopsPanelStorageApi.has(key);
  552. },
  553. /**
  554. * 监听调用setValue、deleteValue
  555. * @param key 需要监听的键
  556. * @param callback
  557. */
  558. addValueChangeListener(key, callback) {
  559. let listenerId = PopsPanelStorageApi.addValueChangeListener(
  560. key,
  561. (__key, __newValue, __oldValue) => {
  562. callback(key, __oldValue, __newValue);
  563. }
  564. );
  565. return listenerId;
  566. },
  567. /**
  568. * 移除监听
  569. * @param listenerId 监听的id
  570. */
  571. removeValueChangeListener(listenerId) {
  572. PopsPanelStorageApi.removeValueChangeListener(listenerId);
  573. },
  574. /**
  575. * 主动触发菜单值改变的回调
  576. * @param key 菜单键
  577. * @param newValue 想要触发的新值,默认使用当前值
  578. * @param oldValue 想要触发的旧值,默认使用当前值
  579. */
  580. triggerMenuValueChange(key, newValue, oldValue) {
  581. PopsPanelStorageApi.triggerValueChangeListener(key, oldValue, newValue);
  582. },
  583. /**
  584. * 移除已执行的仅执行一次的菜单
  585. * @param key 键
  586. */
  587. deleteExecMenuOnce(key) {
  588. this.$data.onceExecMenuData.delete(key);
  589. let flag = PopsPanelStorageApi.removeValueChangeListener(key);
  590. return flag;
  591. },
  592. /**
  593. * 移除已执行的仅执行一次的菜单
  594. * @param key 键
  595. */
  596. deleteOnceExec(key) {
  597. this.$data.onceExecData.delete(key);
  598. },
  599. /**
  600. * 执行菜单
  601. *
  602. * @param queryKey 判断的键,如果是字符串列表,那么它们的判断处理方式是与关系
  603. * @param callback 执行的回调函数
  604. * @param checkExec 判断是否执行回调
  605. *
  606. * (默认)如果想要每个菜单是`与`关系,即每个菜单都判断为开启,那么就判断它们的值&就行
  607. *
  608. * 如果想要任意菜单存在true再执行,那么判断它们的值|就行
  609. *
  610. * + 返回值都为`true`,执行回调,如果回调返回了<style>元素,该元素会在监听到值改变时被移除掉
  611. * + 返回值有一个为`false`,则不执行回调,且移除之前回调函数返回的<style>元素
  612. * @param once 是否只执行一次,默认true
  613. *
  614. * + true (默认)只执行一次,且会监听键的值改变
  615. * + false 不会监听键的值改变
  616. */
  617. exec(queryKey, callback, checkExec, once = true) {
  618. const that = this;
  619. let queryKeyFn;
  620. if (typeof queryKey === "string" || Array.isArray(queryKey)) {
  621. queryKeyFn = () => queryKey;
  622. } else {
  623. queryKeyFn = queryKey;
  624. }
  625. let isArrayKey = false;
  626. let queryKeyResult = queryKeyFn();
  627. let keyList = [];
  628. if (Array.isArray(queryKeyResult)) {
  629. isArrayKey = true;
  630. keyList = queryKeyResult;
  631. } else {
  632. keyList.push(queryKeyResult);
  633. }
  634. let findNotInDataKey = keyList.find(
  635. (it) => !this.$data.contentConfigInitDefaultValue.has(it)
  636. );
  637. if (findNotInDataKey) {
  638. log.warn(`${findNotInDataKey} 键不存在`);
  639. return;
  640. }
  641. let storageKey = JSON.stringify(keyList);
  642. if (once) {
  643. if (this.$data.onceExecMenuData.has(storageKey)) {
  644. return;
  645. }
  646. this.$data.onceExecMenuData.set(storageKey, 1);
  647. }
  648. let storeValueList = [];
  649. let listenerIdList = [];
  650. let dynamicAddStyleNodeCallback = (value, $style) => {
  651. let dynamicResultList = [];
  652. if (!Array.isArray($style)) {
  653. $style = [$style];
  654. }
  655. $style.forEach(($styleItem) => {
  656. if ($styleItem == null) {
  657. return;
  658. }
  659. if ($styleItem instanceof HTMLStyleElement) {
  660. dynamicResultList.push($styleItem);
  661. return;
  662. }
  663. });
  664. {
  665. storeValueList = storeValueList.concat(dynamicResultList);
  666. }
  667. };
  668. let getMenuValue = (key) => {
  669. let value = this.getValue(key);
  670. return value;
  671. };
  672. let clearBeforeStoreValue = () => {
  673. for (let index = 0; index < storeValueList.length; index++) {
  674. let $css = storeValueList[index];
  675. $css.remove();
  676. storeValueList.splice(index, 1);
  677. index--;
  678. }
  679. };
  680. let checkMenuExec = () => {
  681. let flag = false;
  682. if (typeof checkExec === "function") {
  683. flag = checkExec(keyList);
  684. } else {
  685. flag = keyList.every((key) => getMenuValue(key));
  686. }
  687. return flag;
  688. };
  689. let valueChangeCallback = (valueOption) => {
  690. let execFlag = checkMenuExec();
  691. let resultList = [];
  692. if (execFlag) {
  693. let valueList = keyList.map((key) => this.getValue(key));
  694. let callbackResult = callback({
  695. value: isArrayKey ? valueList : valueList[0],
  696. addStyleElement: (...args) => {
  697. return dynamicAddStyleNodeCallback(true, ...args);
  698. }
  699. });
  700. if (!Array.isArray(callbackResult)) {
  701. callbackResult = [callbackResult];
  702. }
  703. callbackResult.forEach((it) => {
  704. if (it == null) {
  705. return;
  706. }
  707. if (it instanceof HTMLStyleElement) {
  708. resultList.push(it);
  709. return;
  710. }
  711. });
  712. }
  713. clearBeforeStoreValue();
  714. storeValueList = [...resultList];
  715. };
  716. once && keyList.forEach((key) => {
  717. let listenerId = this.addValueChangeListener(
  718. key,
  719. (key2, newValue, oldValue) => {
  720. valueChangeCallback();
  721. }
  722. );
  723. listenerIdList.push(listenerId);
  724. });
  725. valueChangeCallback();
  726. let result = {
  727. /**
  728. * 清空菜单执行情况
  729. *
  730. * + 清空存储的元素列表
  731. * + 清空值改变的监听器
  732. * + 清空存储的一次执行的键
  733. */
  734. clear() {
  735. this.clearStoreStyleElements();
  736. this.removeValueChangeListener();
  737. once && that.$data.onceExecMenuData.delete(storageKey);
  738. },
  739. /**
  740. * 清空存储的元素列表
  741. */
  742. clearStoreStyleElements: () => {
  743. return clearBeforeStoreValue();
  744. },
  745. /**
  746. * 移除值改变的监听器
  747. */
  748. removeValueChangeListener: () => {
  749. listenerIdList.forEach((listenerId) => {
  750. this.removeValueChangeListener(listenerId);
  751. });
  752. }
  753. };
  754. return result;
  755. },
  756. /**
  757. * 自动判断菜单是否启用,然后执行回调
  758. * @param key 判断的键,如果是字符串列表,那么它们的判断处理方式是与关系
  759. * @param callback 回调
  760. * @param isReverse 逆反判断菜单启用,默认false
  761. * @param once 是否是只执行一次,默认false
  762. */
  763. execMenu(key, callback, isReverse = false, once = false) {
  764. return this.exec(
  765. key,
  766. (option) => {
  767. return callback(option);
  768. },
  769. (keyList) => {
  770. let execFlag = keyList.every((__key__) => {
  771. let flag = !!this.getValue(__key__);
  772. let disabled = Panel.$data.contentConfigInitDisabledKeys.includes(__key__);
  773. if (disabled) {
  774. flag = false;
  775. log.warn(`.execMenu${once ? "Once" : ""} ${__key__} 被禁用`);
  776. }
  777. isReverse && (flag = !flag);
  778. return flag;
  779. });
  780. return execFlag;
  781. },
  782. once
  783. );
  784. },
  785. /**
  786. * 自动判断菜单是否启用,然后执行回调,只会执行一次
  787. *
  788. * 它会自动监听值改变(设置中的修改),改变后如果未执行,则执行一次
  789. * @param key 判断的键,如果是字符串列表,那么它们的判断处理方式是与关系
  790. * @param callback 回调
  791. * @param isReverse 逆反判断菜单启用,默认false
  792. */
  793. execMenuOnce(key, callback, isReverse = false) {
  794. return this.execMenu(key, callback, isReverse, true);
  795. },
  796. /**
  797. * 根据key执行一次
  798. * @param key 键
  799. * @param callback 回调
  800. */
  801. onceExec(key, callback) {
  802. if (typeof key !== "string") {
  803. throw new TypeError("key 必须是字符串");
  804. }
  805. if (this.$data.onceExecData.has(key)) {
  806. return;
  807. }
  808. callback();
  809. this.$data.onceExecData.set(key, 1);
  810. },
  811. /**
  812. * 显示设置面板
  813. * @param content 显示的内容配置
  814. * @param [title] 标题
  815. * @param [preventDefaultContentConfig=false] 是否阻止默认添加内容配置(版本号)
  816. */
  817. showPanel(content, title = `${SCRIPT_NAME}-设置`, preventDefaultContentConfig = false) {
  818. let checkHasBottomVersionContentConfig = content.findIndex((it) => {
  819. let isBottom = typeof it.isBottom === "function" ? it.isBottom() : Boolean(it.isBottom);
  820. return isBottom && it.id === "script-version";
  821. }) !== -1;
  822. if (!preventDefaultContentConfig && !checkHasBottomVersionContentConfig) {
  823. content.push(...PanelContent.getDefaultBottomContentConfig());
  824. }
  825. let $panel = __pops.panel({
  826. ...{
  827. title: {
  828. text: title,
  829. position: "center",
  830. html: false,
  831. style: ""
  832. },
  833. content,
  834. btn: {
  835. close: {
  836. enable: true,
  837. callback: (details, event) => {
  838. details.close();
  839. this.$data.$panel = null;
  840. }
  841. }
  842. },
  843. mask: {
  844. enable: true,
  845. clickEvent: {
  846. toClose: true,
  847. toHide: false
  848. },
  849. clickCallBack: (originalRun, config) => {
  850. originalRun();
  851. this.$data.$panel = null;
  852. }
  853. },
  854. width: PanelUISize.setting.width,
  855. height: PanelUISize.setting.height,
  856. drag: true,
  857. only: true
  858. },
  859. ...this.$data.panelConfig
  860. });
  861. this.$data.$panel = $panel;
  862. }
  863. };
  864. const PanelSettingConfig = {
  865. /** Toast位置 */
  866. qmsg_config_position: {
  867. key: "qmsg-config-position",
  868. defaultValue: "bottom"
  869. },
  870. /** 最多显示的数量 */
  871. qmsg_config_maxnums: {
  872. key: "qmsg-config-maxnums",
  873. defaultValue: 3
  874. },
  875. /** 逆序弹出 */
  876. qmsg_config_showreverse: {
  877. key: "qmsg-config-showreverse",
  878. defaultValue: false
  879. }
  880. };
  881. const utils = Utils.noConflict();
  882. const domUtils = DOMUtils.noConflict();
  883. const __pops = pops;
  884. const log = new utils.Log(
  885. _GM_info,
  886. _unsafeWindow.console || _monkeyWindow.console
  887. );
  888. let SCRIPT_NAME = _GM_info?.script?.name || void 0;
  889. pops.config.Utils.AnyTouch();
  890. const DEBUG = false;
  891. log.config({
  892. debug: DEBUG,
  893. logMaxCount: 1e3,
  894. autoClearConsole: true,
  895. tag: true
  896. });
  897. Qmsg.config({
  898. isHTML: true,
  899. autoClose: true,
  900. showClose: false,
  901. consoleLogContent(qmsgInst) {
  902. const qmsgType = qmsgInst.getSetting().type;
  903. if (qmsgType === "loading") {
  904. return false;
  905. }
  906. const content = qmsgInst.getSetting().content;
  907. if (qmsgType === "warning") {
  908. log.warn(content);
  909. } else if (qmsgType === "error") {
  910. log.error(content);
  911. } else {
  912. log.info(content);
  913. }
  914. return true;
  915. },
  916. get position() {
  917. return Panel.getValue(
  918. PanelSettingConfig.qmsg_config_position.key,
  919. PanelSettingConfig.qmsg_config_position.defaultValue
  920. );
  921. },
  922. get maxNums() {
  923. return Panel.getValue(
  924. PanelSettingConfig.qmsg_config_maxnums.key,
  925. PanelSettingConfig.qmsg_config_maxnums.defaultValue
  926. );
  927. },
  928. get showReverse() {
  929. return Panel.getValue(
  930. PanelSettingConfig.qmsg_config_showreverse.key,
  931. PanelSettingConfig.qmsg_config_showreverse.defaultValue
  932. );
  933. },
  934. get zIndex() {
  935. let maxZIndex = Utils.getMaxZIndex();
  936. let popsMaxZIndex = pops.config.InstanceUtils.getPopsMaxZIndex().zIndex;
  937. return Utils.getMaxValue(maxZIndex, popsMaxZIndex) + 100;
  938. }
  939. });
  940. __pops.GlobalConfig.setGlobalConfig({
  941. zIndex: () => {
  942. let maxZIndex = Utils.getMaxZIndex(void 0, void 0, ($ele) => {
  943. if ($ele?.classList?.contains("qmsg-shadow-container")) {
  944. return false;
  945. }
  946. if ($ele?.closest("qmsg") && $ele.getRootNode() instanceof ShadowRoot) {
  947. return false;
  948. }
  949. });
  950. let popsMaxZIndex = pops.config.InstanceUtils.getPopsMaxZIndex().zIndex;
  951. return Utils.getMaxValue(maxZIndex, popsMaxZIndex) + 100;
  952. },
  953. mask: {
  954. // 开启遮罩层
  955. enable: true,
  956. // 取消点击遮罩层的事件
  957. clickEvent: {
  958. toClose: false,
  959. toHide: false
  960. }
  961. },
  962. drag: true
  963. });
  964. const GM_Menu = new utils.GM_Menu({
  965. GM_getValue: _GM_getValue,
  966. GM_setValue: _GM_setValue,
  967. GM_registerMenuCommand: _GM_registerMenuCommand,
  968. GM_unregisterMenuCommand: _GM_unregisterMenuCommand
  969. });
  970. const httpx = new utils.Httpx({
  971. xmlHttpRequest: _GM_xmlhttpRequest,
  972. logDetails: DEBUG
  973. });
  974. httpx.interceptors.request.use((data) => {
  975. return data;
  976. });
  977. httpx.interceptors.response.use(void 0, (data) => {
  978. log.error("拦截器-请求错误", data);
  979. if (data.type === "onabort") {
  980. Qmsg.warning("请求取消", { consoleLogContent: true });
  981. } else if (data.type === "onerror") {
  982. Qmsg.error("请求异常", { consoleLogContent: true });
  983. } else if (data.type === "ontimeout") {
  984. Qmsg.error("请求超时", { consoleLogContent: true });
  985. } else {
  986. Qmsg.error("其它错误", { consoleLogContent: true });
  987. }
  988. return data;
  989. });
  990. ({
  991. Object: {
  992. defineProperty: _unsafeWindow.Object.defineProperty
  993. },
  994. Function: {
  995. apply: _unsafeWindow.Function.prototype.apply,
  996. call: _unsafeWindow.Function.prototype.call
  997. },
  998. Element: {
  999. appendChild: _unsafeWindow.Element.prototype.appendChild
  1000. },
  1001. setTimeout: _unsafeWindow.setTimeout
  1002. });
  1003. utils.addStyle.bind(utils);
  1004. DOMUtils.selector.bind(DOMUtils);
  1005. DOMUtils.selectorAll.bind(DOMUtils);
  1006. const cookieManager = new utils.GM_Cookie();
  1007. const PanelContent = {
  1008. $data: {
  1009. /**
  1010. * @private
  1011. */
  1012. __contentConfig: null,
  1013. get contentConfig() {
  1014. if (this.__contentConfig == null) {
  1015. this.__contentConfig = new utils.Dictionary();
  1016. }
  1017. return this.__contentConfig;
  1018. }
  1019. },
  1020. /**
  1021. * 设置所有配置项,用于初始化默认的值
  1022. *
  1023. * 如果是第一组添加的话,那么它默认就是设置菜单打开的配置
  1024. * @param configList 配置项
  1025. */
  1026. addContentConfig(configList) {
  1027. if (!Array.isArray(configList)) {
  1028. configList = [configList];
  1029. }
  1030. let index = this.$data.contentConfig.keys().length;
  1031. this.$data.contentConfig.set(index, configList);
  1032. },
  1033. /**
  1034. * 获取所有的配置内容,用于初始化默认的值
  1035. */
  1036. getAllContentConfig() {
  1037. return this.$data.contentConfig.values().flat();
  1038. },
  1039. /**
  1040. * 获取配置内容
  1041. * @param index 配置索引
  1042. */
  1043. getConfig(index = 0) {
  1044. return this.$data.contentConfig.get(index) ?? [];
  1045. },
  1046. /**
  1047. * 获取默认左侧底部的配置项
  1048. */
  1049. getDefaultBottomContentConfig() {
  1050. return [
  1051. {
  1052. id: "script-version",
  1053. title: `版本:${_GM_info?.script?.version || "未知"}`,
  1054. isBottom: true,
  1055. forms: [],
  1056. clickFirstCallback(event, rightHeaderElement, rightContainerElement) {
  1057. let supportURL = _GM_info?.script?.supportURL || _GM_info?.script?.namespace;
  1058. if (typeof supportURL === "string" && utils.isNotNull(supportURL)) {
  1059. window.open(supportURL, "_blank");
  1060. }
  1061. return false;
  1062. }
  1063. }
  1064. ];
  1065. }
  1066. };
  1067. let applyObjectThis = (obj) => {
  1068. if (typeof obj === "object" && obj != null || typeof obj === "function") {
  1069. Object.keys(obj).forEach((key) => {
  1070. if (typeof obj[key] === "function") {
  1071. obj[key] = obj[key].bind(obj);
  1072. }
  1073. });
  1074. }
  1075. };
  1076. const __GM_cookie__ = _GM_cookie;
  1077. applyObjectThis(__GM_cookie__);
  1078. applyObjectThis(cookieManager);
  1079. const CookieManagerApiNameList = [
  1080. "document.cookie",
  1081. "cookieStore",
  1082. "GM_cookie",
  1083. "GM.cookie"
  1084. ];
  1085. class CookieManagerService {
  1086. __apiName;
  1087. /**
  1088. * @param apiName 强制使用Api的名称,是否使用保存的Api名称
  1089. */
  1090. constructor(apiName) {
  1091. if (typeof apiName === "string") {
  1092. if (!CookieManagerApiNameList.includes(apiName)) {
  1093. throw new Error(`未知的apiName${apiName}`);
  1094. }
  1095. }
  1096. this.__apiName = apiName;
  1097. }
  1098. get cookieManagerApiName() {
  1099. let managerApi = Panel.getValue("cookie-manager-api", "document.cookie");
  1100. return this.__apiName || managerApi;
  1101. }
  1102. get cookieManager() {
  1103. if (this.cookieManagerApiName === "GM_cookie") {
  1104. return {
  1105. list(options, callback) {
  1106. __GM_cookie__.list(options, (result) => {
  1107. callback(result);
  1108. });
  1109. },
  1110. set(cookieInfo, callback) {
  1111. __GM_cookie__.set(cookieInfo, (result) => {
  1112. callback(result);
  1113. });
  1114. },
  1115. delete(cookieInfo, callback) {
  1116. __GM_cookie__.delete(cookieInfo, (result) => {
  1117. callback(result);
  1118. });
  1119. }
  1120. };
  1121. } else if (this.cookieManagerApiName === "GM.cookie") {
  1122. return {
  1123. list(options, callback) {
  1124. _GM.cookie.list().then((result) => {
  1125. callback(result);
  1126. });
  1127. },
  1128. set(cookieInfo, callback) {
  1129. _GM.cookie.set(cookieInfo).then((result) => {
  1130. callback(result ?? null);
  1131. }).catch((reason) => {
  1132. callback(reason);
  1133. });
  1134. },
  1135. delete(cookieInfo, callback) {
  1136. _GM.cookie.delete(cookieInfo).then((result) => {
  1137. callback(result ?? null);
  1138. }).catch((reason) => {
  1139. callback(reason);
  1140. });
  1141. }
  1142. };
  1143. } else if (this.cookieManagerApiName === "cookieStore") {
  1144. let cookieStore = _unsafeWindow.cookieStore;
  1145. return {
  1146. list(options, callback) {
  1147. cookieStore.getAll().then((result) => {
  1148. callback(result);
  1149. }).catch((reason) => {
  1150. log.error(reason);
  1151. Qmsg.error(reason.toString());
  1152. });
  1153. },
  1154. set(cookieInfo, callback) {
  1155. cookieStore.set(cookieInfo).then(() => {
  1156. callback();
  1157. }).catch((reason) => {
  1158. callback(reason);
  1159. });
  1160. },
  1161. delete(cookieInfo, callback) {
  1162. cookieStore.delete(cookieInfo).then((result) => {
  1163. callback();
  1164. }).catch((reason) => {
  1165. callback(reason);
  1166. });
  1167. }
  1168. };
  1169. } else {
  1170. return cookieManager;
  1171. }
  1172. }
  1173. /**
  1174. * 查询所有Cookie
  1175. */
  1176. queryAllCookie() {
  1177. return new Promise((resolve, reject) => {
  1178. try {
  1179. this.cookieManager.list({}, (cookieListResult) => {
  1180. let __cookieListResult__ = cookieListResult || [];
  1181. __cookieListResult__ = __cookieListResult__.sort((a, b) => a.name.localeCompare(b.name));
  1182. resolve(__cookieListResult__);
  1183. });
  1184. } catch (error) {
  1185. log.error(error);
  1186. Qmsg.error(error.toString());
  1187. reject(error);
  1188. }
  1189. });
  1190. }
  1191. /**
  1192. * 清除所有Cookie
  1193. */
  1194. deleteAllCookie() {
  1195. return new Promise((resolve, reject) => {
  1196. try {
  1197. this.cookieManager.list({}, async (cookieListResult) => {
  1198. const __cookieListResult__ = cookieListResult || [];
  1199. const result = {
  1200. success: 0,
  1201. error: 0
  1202. };
  1203. for (let index = 0; index < __cookieListResult__.length; index++) {
  1204. const cookieListItem = __cookieListResult__[index];
  1205. let deleteError = await new Promise((deleteResolve) => {
  1206. this.deleteCookie(cookieListItem).then((deleteResult) => {
  1207. deleteResolve(deleteResult);
  1208. });
  1209. });
  1210. if (deleteError) {
  1211. result.error++;
  1212. } else {
  1213. result.success++;
  1214. }
  1215. }
  1216. resolve(result);
  1217. });
  1218. } catch (error) {
  1219. log.error(error);
  1220. Qmsg.error(error.toString());
  1221. reject(error);
  1222. }
  1223. });
  1224. }
  1225. /**
  1226. * 添加Cookie
  1227. */
  1228. addCookie(cookieInfo) {
  1229. return new Promise((resolve, reject) => {
  1230. try {
  1231. Reflect.deleteProperty(cookieInfo, "hostOnly");
  1232. this.cookieManager.set(
  1233. cookieInfo,
  1234. (error) => {
  1235. if (false) ;
  1236. resolve(error);
  1237. }
  1238. );
  1239. } catch (error) {
  1240. log.error(error);
  1241. Qmsg.error(error.toString());
  1242. reject(error);
  1243. }
  1244. });
  1245. }
  1246. /**
  1247. * 删除Cookie
  1248. */
  1249. deleteCookie(cookieInfo) {
  1250. return new Promise((resolve, reject) => {
  1251. try {
  1252. this.cookieManager.delete(
  1253. cookieInfo,
  1254. (error) => {
  1255. if (false) ;
  1256. resolve(error);
  1257. }
  1258. );
  1259. } catch (error) {
  1260. log.error(error);
  1261. Qmsg.error(error.toString());
  1262. reject(error);
  1263. }
  1264. });
  1265. }
  1266. /**
  1267. * 更新Cookie
  1268. */
  1269. updateCookie(cookieInfo) {
  1270. return new Promise(async (resolve, reject) => {
  1271. let result;
  1272. try {
  1273. if (this.cookieManagerApiName === "document.cookie" || this.cookieManagerApiName === "cookieStore") {
  1274. let deleteError = await this.deleteCookie(cookieInfo);
  1275. if (deleteError) {
  1276. throw new TypeError(deleteError.toString());
  1277. }
  1278. }
  1279. let addError = await this.addCookie(cookieInfo);
  1280. if (addError) {
  1281. throw new TypeError(addError.toString());
  1282. }
  1283. } catch (error) {
  1284. result = error;
  1285. } finally {
  1286. resolve(result);
  1287. }
  1288. });
  1289. }
  1290. }
  1291. const CookieManager = new CookieManagerService();
  1292. const PanelComponents = {
  1293. $data: {
  1294. __storeApiFn: null,
  1295. get storeApiValue() {
  1296. if (!this.__storeApiFn) {
  1297. this.__storeApiFn = new Utils.Dictionary();
  1298. }
  1299. return this.__storeApiFn;
  1300. }
  1301. },
  1302. /**
  1303. * 获取自定义的存储接口
  1304. * @param type 组件类型
  1305. */
  1306. getStorageApi(type) {
  1307. if (!this.hasStorageApi(type)) {
  1308. return;
  1309. }
  1310. return this.$data.storeApiValue.get(type);
  1311. },
  1312. /**
  1313. * 判断是否存在自定义的存储接口
  1314. * @param type 组件类型
  1315. */
  1316. hasStorageApi(type) {
  1317. return this.$data.storeApiValue.has(type);
  1318. },
  1319. /**
  1320. * 设置自定义的存储接口
  1321. * @param type 组件类型
  1322. * @param storageApiValue 存储接口
  1323. */
  1324. setStorageApi(type, storageApiValue) {
  1325. this.$data.storeApiValue.set(type, storageApiValue);
  1326. },
  1327. /**
  1328. * 初始化组件的存储接口属性
  1329. *
  1330. * @param type 组件类型
  1331. * @param config 组件配置,必须包含prop属性
  1332. * @param storageApiValue 存储接口
  1333. */
  1334. initComponentsStorageApi(type, config, storageApiValue) {
  1335. let propsStorageApi;
  1336. if (this.hasStorageApi(type)) {
  1337. propsStorageApi = this.getStorageApi(type);
  1338. } else {
  1339. propsStorageApi = storageApiValue;
  1340. }
  1341. this.setComponentsStorageApiProperty(config, propsStorageApi);
  1342. },
  1343. /**
  1344. * 设置组件的存储接口属性
  1345. * @param config 组件配置,必须包含prop属性
  1346. * @param storageApiValue 存储接口
  1347. */
  1348. setComponentsStorageApiProperty(config, storageApiValue) {
  1349. Reflect.set(config.props, PROPS_STORAGE_API, storageApiValue);
  1350. }
  1351. };
  1352. const UISwitch = function(text, key, defaultValue, clickCallBack, description, afterAddToUListCallBack, disabled, valueChangeCallBack) {
  1353. let result = {
  1354. text,
  1355. type: "switch",
  1356. description,
  1357. disabled,
  1358. attributes: {},
  1359. props: {},
  1360. getValue() {
  1361. let storageApiValue = this.props[PROPS_STORAGE_API];
  1362. let value = storageApiValue.get(key, defaultValue);
  1363. return value;
  1364. },
  1365. callback(event, __value) {
  1366. let value = Boolean(__value);
  1367. log.success(`${value ? "开启" : "关闭"} ${text}`);
  1368. if (typeof clickCallBack === "function") {
  1369. let result2 = clickCallBack(event, value);
  1370. if (result2) {
  1371. return;
  1372. }
  1373. }
  1374. let storageApiValue = this.props[PROPS_STORAGE_API];
  1375. storageApiValue.set(key, value);
  1376. },
  1377. afterAddToUListCallBack
  1378. };
  1379. Reflect.set(result.attributes, ATTRIBUTE_KEY, key);
  1380. Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue);
  1381. PanelComponents.initComponentsStorageApi(
  1382. "switch",
  1383. result,
  1384. {
  1385. get(key2, defaultValue2) {
  1386. return Panel.getValue(key2, defaultValue2);
  1387. },
  1388. set(key2, value) {
  1389. Panel.setValue(key2, value);
  1390. }
  1391. }
  1392. );
  1393. return result;
  1394. };
  1395. const CookieInfoTransform = {
  1396. /**
  1397. * 对编辑前的cookie信息进行值转换
  1398. * @param cookieInfo
  1399. * @param isEdit 是否是编辑
  1400. */
  1401. beforeEdit(cookieInfo, isEdit) {
  1402. const cookieManagerApiName = CookieManager.cookieManagerApiName;
  1403. if (cookieManagerApiName === "cookieStore") {
  1404. if (typeof cookieInfo.expires === "number") {
  1405. cookieInfo.expirationDate = cookieInfo.expires;
  1406. }
  1407. } else if (cookieManagerApiName === "GM_cookie" || cookieManagerApiName === "GM.cookie") {
  1408. if (isEdit) {
  1409. if (typeof cookieInfo.expirationDate === "number") {
  1410. cookieInfo.expirationDate = cookieInfo.expirationDate * 1e3;
  1411. }
  1412. }
  1413. }
  1414. return cookieInfo;
  1415. },
  1416. /**
  1417. * 对编辑后的cookie信息进行值转换
  1418. * @param cookieInfo
  1419. */
  1420. afterEdit(cookieInfo) {
  1421. const cookieManagerApiName = CookieManager.cookieManagerApiName;
  1422. if (cookieManagerApiName === "document.cookie") {
  1423. cookieInfo.domain = "";
  1424. } else if (cookieManagerApiName === "cookieStore") {
  1425. if (typeof cookieInfo.expirationDate === "number") {
  1426. cookieInfo.expires = cookieInfo.expirationDate;
  1427. }
  1428. } else if (cookieManagerApiName === "GM_cookie" || cookieManagerApiName === "GM.cookie") {
  1429. if (typeof cookieInfo.expirationDate === "number") {
  1430. cookieInfo.expirationDate = Math.floor(cookieInfo.expirationDate / 1e3);
  1431. }
  1432. }
  1433. return cookieInfo;
  1434. }
  1435. };
  1436. let edit_ui_input = (text, getValue, setValue, disabled) => {
  1437. let config = {
  1438. text,
  1439. type: "input",
  1440. isNumber: false,
  1441. isPassword: false,
  1442. props: {},
  1443. attributes: {},
  1444. description: "",
  1445. getValue() {
  1446. return getValue();
  1447. },
  1448. callback(event, value) {
  1449. setValue(value);
  1450. },
  1451. placeholder: "",
  1452. disabled: Boolean(disabled)
  1453. };
  1454. return config;
  1455. };
  1456. let edit_ui_textarea = (text, getValue, setValue, disabled) => {
  1457. let config = {
  1458. text,
  1459. type: "textarea",
  1460. props: {},
  1461. attributes: {},
  1462. description: "",
  1463. placeholder: "",
  1464. getValue() {
  1465. return getValue();
  1466. },
  1467. disabled,
  1468. callback: function(event, value) {
  1469. setValue(value);
  1470. }
  1471. };
  1472. return config;
  1473. };
  1474. let edit_ui_select = (text, data, getValue, setValue, disabled) => {
  1475. let config = {
  1476. text,
  1477. type: "select",
  1478. description: "",
  1479. attributes: {},
  1480. props: {},
  1481. getValue() {
  1482. return getValue();
  1483. },
  1484. callback(event, isSelectedValue, isSelectedText) {
  1485. let value = isSelectedValue;
  1486. setValue(value);
  1487. },
  1488. data: typeof data === "function" ? data() : data,
  1489. disabled: Boolean(disabled)
  1490. };
  1491. return config;
  1492. };
  1493. const CookieManagerEditView = {
  1494. init() {
  1495. },
  1496. /**
  1497. * 显示视图
  1498. * @param cookieInfo 需要编辑的cookie,为空的话是添加
  1499. * @param dialogCloseCallBack 弹窗关闭的回调
  1500. */
  1501. showView(__cookieInfo__, dialogCloseCallBack) {
  1502. let isEdit = !!__cookieInfo__;
  1503. let defaultCookieInfo = {
  1504. name: "",
  1505. value: "",
  1506. domain: window.location.hostname,
  1507. path: "/",
  1508. secure: false,
  1509. session: false,
  1510. hostOnly: false,
  1511. httpOnly: false,
  1512. sameSite: "lax",
  1513. // 一个月后过期
  1514. // 单位:毫秒
  1515. expirationDate: Date.now() + 60 * 60 * 24 * 30 * 1e3
  1516. };
  1517. let cookieInfo = utils.assign({}, defaultCookieInfo, true);
  1518. utils.assign(cookieInfo, __cookieInfo__ ?? {}, true);
  1519. cookieInfo = CookieInfoTransform.beforeEdit(cookieInfo, isEdit);
  1520. let $dialog = __pops.confirm({
  1521. title: {
  1522. text: isEdit ? "编辑Cookie" : "添加Cookie",
  1523. position: "center"
  1524. },
  1525. content: {
  1526. text: "",
  1527. html: true
  1528. },
  1529. drag: true,
  1530. btn: {
  1531. position: "center",
  1532. ok: {
  1533. text: isEdit ? "编辑" : "添加",
  1534. async callback(eventDetails, event) {
  1535. let valid = CookieManagerEditView.validCookieInfo(cookieInfo);
  1536. if (!valid) {
  1537. return;
  1538. }
  1539. cookieInfo.value = encodeURIComponent(cookieInfo.value);
  1540. cookieInfo = CookieInfoTransform.afterEdit(cookieInfo);
  1541. if (isEdit) {
  1542. let result = await CookieManager.updateCookie(cookieInfo);
  1543. if (result) {
  1544. Qmsg.error(result.toString());
  1545. } else {
  1546. Qmsg.success("修改成功");
  1547. eventDetails.close();
  1548. }
  1549. } else {
  1550. let result = await CookieManager.addCookie(cookieInfo);
  1551. if (result) {
  1552. Qmsg.error(result.toString());
  1553. } else {
  1554. Qmsg.success("添加成功");
  1555. eventDetails.close();
  1556. }
  1557. }
  1558. if (typeof dialogCloseCallBack === "function") {
  1559. dialogCloseCallBack(cookieInfo);
  1560. }
  1561. }
  1562. },
  1563. cancel: {
  1564. text: "取消"
  1565. }
  1566. },
  1567. mask: {
  1568. enable: true
  1569. },
  1570. width: PanelUISize.settingMiddle.width,
  1571. height: "auto",
  1572. style: (
  1573. /*css*/
  1574. `
  1575. ${__pops.config.cssText.panelCSS}
  1576.  
  1577. .pops-panel-input input:disabled{
  1578. color: #b4b4b4;
  1579. }
  1580. .pops-confirm-content{
  1581. padding: 10px;
  1582. }
  1583. .pops-confirm-content li{
  1584. display: flex;
  1585. flex-direction: column;
  1586. }
  1587. .pops-panel-item-left-text{
  1588. margin-bottom: 5px;
  1589. }
  1590. .pops-panel-input.pops-input-disabled{
  1591. border: 1px solid #dcdfe6;
  1592. }
  1593. .pops-panel-textarea textarea{
  1594. resize: auto;
  1595. border-radius: 4px;
  1596. }
  1597. #cookie-item-property-expires{
  1598. border: 1px solid rgb(184, 184, 184, var(--pops-bd-opacity));
  1599. border-radius: 4px;
  1600. background-color: #ffffff;
  1601. width: 100%;
  1602. height: 32px;
  1603. padding: 0px 8px;
  1604. }
  1605. #cookie-item-property-expires:hover{
  1606. border: 1px solid #c0c4cc
  1607. }
  1608. #cookie-item-property-expires:focus,
  1609. #cookie-item-property-expires:focus-within{
  1610. outline: 0;
  1611. border: 1px solid #409eff;
  1612. border-radius: 4px;
  1613. box-shadow: none;
  1614. }
  1615. `
  1616. )
  1617. });
  1618. let $editContent = $dialog.$shadowRoot.querySelector(".pops-confirm-content");
  1619. let panelHandlerComponents = __pops.config.PanelHandlerComponents();
  1620. let $name = panelHandlerComponents.createSectionContainerItem_input(
  1621. edit_ui_input(
  1622. "name",
  1623. () => cookieInfo.name,
  1624. (value) => cookieInfo.name = value,
  1625. isEdit
  1626. )
  1627. );
  1628. let $value = panelHandlerComponents.createSectionContainerItem_textarea(
  1629. edit_ui_textarea(
  1630. "value",
  1631. () => cookieInfo.value,
  1632. (value) => cookieInfo.value = value
  1633. )
  1634. );
  1635. let $domain = panelHandlerComponents.createSectionContainerItem_input(
  1636. edit_ui_input(
  1637. "domain",
  1638. () => cookieInfo.domain,
  1639. (value) => cookieInfo.domain = value
  1640. )
  1641. );
  1642. let $path = panelHandlerComponents.createSectionContainerItem_input(
  1643. edit_ui_input(
  1644. "path",
  1645. () => cookieInfo.path,
  1646. (value) => cookieInfo.path = value
  1647. )
  1648. );
  1649. let $expires;
  1650. if (cookieInfo.session) {
  1651. $expires = panelHandlerComponents.createSectionContainerItem_input(
  1652. edit_ui_input(
  1653. "expires",
  1654. () => "会话",
  1655. (value) => {
  1656. },
  1657. true
  1658. )
  1659. );
  1660. } else {
  1661. $expires = panelHandlerComponents.createSectionContainerItem_own({
  1662. type: "own",
  1663. getLiElementCallBack: function(liElement) {
  1664. let $li = domUtils.createElement("li", {
  1665. innerHTML: (
  1666. /*html*/
  1667. `
  1668. <div class="pops-panel-item-left-text">
  1669. <p class="pops-panel-item-left-main-text">expires</p>
  1670. </div>
  1671. <div class="pops-panel-item-right-wrapper">
  1672. <input type="datetime-local" id="cookie-item-property-expires">
  1673. </div>
  1674. `
  1675. )
  1676. });
  1677. let $dateTime = $li.querySelector("#cookie-item-property-expires");
  1678. $dateTime.valueAsNumber = cookieInfo.expirationDate;
  1679. domUtils.on($dateTime, ["change", "input", "propertychange"], (event) => {
  1680. utils.preventEvent(event);
  1681. cookieInfo.expirationDate = $dateTime.valueAsNumber;
  1682. });
  1683. return $li;
  1684. }
  1685. });
  1686. }
  1687. let $httpOnly = panelHandlerComponents.createSectionContainerItem_select(
  1688. edit_ui_select(
  1689. "httpOnly",
  1690. [
  1691. {
  1692. text: "true",
  1693. value: true
  1694. },
  1695. {
  1696. text: "false",
  1697. value: false
  1698. }
  1699. ],
  1700. () => cookieInfo.httpOnly,
  1701. (value) => cookieInfo.httpOnly = value
  1702. )
  1703. );
  1704. let $secure = panelHandlerComponents.createSectionContainerItem_select(
  1705. edit_ui_select(
  1706. "secure",
  1707. [
  1708. {
  1709. text: "true",
  1710. value: true
  1711. },
  1712. {
  1713. text: "false",
  1714. value: false
  1715. }
  1716. ],
  1717. () => cookieInfo.secure,
  1718. (value) => cookieInfo.secure = value
  1719. )
  1720. );
  1721. let sameSiteData = [
  1722. {
  1723. text: "no_restriction",
  1724. value: "no_restriction"
  1725. },
  1726. {
  1727. text: "lax",
  1728. value: "lax"
  1729. },
  1730. {
  1731. text: "strict",
  1732. value: "strict"
  1733. },
  1734. {
  1735. text: "unspecified",
  1736. value: "unspecified"
  1737. }
  1738. ];
  1739. if (CookieManager.cookieManagerApiName === "cookieStore") {
  1740. sameSiteData = [
  1741. {
  1742. text: "lax",
  1743. value: "lax"
  1744. },
  1745. {
  1746. text: "strict",
  1747. value: "strict"
  1748. },
  1749. {
  1750. text: "none",
  1751. value: "none"
  1752. }
  1753. ];
  1754. }
  1755. let $sameSite = panelHandlerComponents.createSectionContainerItem_select(
  1756. edit_ui_select(
  1757. "sameSite",
  1758. sameSiteData,
  1759. () => cookieInfo.sameSite,
  1760. (value) => cookieInfo.sameSite = value
  1761. )
  1762. );
  1763. domUtils.append($editContent, [$name, $value]);
  1764. if (CookieManager.cookieManagerApiName === "GM_cookie" || CookieManager.cookieManagerApiName === "GM.cookie") {
  1765. domUtils.append($editContent, [$domain, $path, $expires, $httpOnly, $secure, $sameSite]);
  1766. } else if (CookieManager.cookieManagerApiName === "cookieStore") {
  1767. domUtils.append($editContent, [$domain, $path, $expires, $sameSite]);
  1768. }
  1769. },
  1770. /**
  1771. * Cookie信息校验
  1772. */
  1773. validCookieInfo(cookieInfo) {
  1774. if (cookieInfo.name == null || cookieInfo.name == "") {
  1775. Qmsg.error("name不能为空");
  1776. return false;
  1777. }
  1778. if (cookieInfo.domain == null || cookieInfo.domain == "") {
  1779. Qmsg.error("domain不能为空");
  1780. return false;
  1781. }
  1782. if (cookieInfo.path == null || cookieInfo.path == "") {
  1783. Qmsg.error("path不能为空");
  1784. return false;
  1785. }
  1786. return true;
  1787. }
  1788. };
  1789. const UISelect = function(text, key, defaultValue, data, selectCallBack, description, valueChangeCallBack) {
  1790. let selectData = [];
  1791. if (typeof data === "function") {
  1792. selectData = data();
  1793. } else {
  1794. selectData = data;
  1795. }
  1796. let result = {
  1797. text,
  1798. type: "select",
  1799. description,
  1800. attributes: {},
  1801. props: {},
  1802. getValue() {
  1803. let storageApiValue = this.props[PROPS_STORAGE_API];
  1804. return storageApiValue.get(key, defaultValue);
  1805. },
  1806. callback(event, isSelectedValue, isSelectedText) {
  1807. let value = isSelectedValue;
  1808. log.info(`选择:${isSelectedText}`);
  1809. if (typeof selectCallBack === "function") {
  1810. let result2 = selectCallBack(event, value, isSelectedText);
  1811. if (result2) {
  1812. return;
  1813. }
  1814. }
  1815. let storageApiValue = this.props[PROPS_STORAGE_API];
  1816. storageApiValue.set(key, value);
  1817. if (typeof valueChangeCallBack === "function") {
  1818. valueChangeCallBack(event, value, isSelectedText);
  1819. }
  1820. },
  1821. data: selectData
  1822. };
  1823. Reflect.set(result.attributes, ATTRIBUTE_KEY, key);
  1824. Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue);
  1825. PanelComponents.initComponentsStorageApi(
  1826. "select",
  1827. result,
  1828. {
  1829. get(key2, defaultValue2) {
  1830. return Panel.getValue(key2, defaultValue2);
  1831. },
  1832. set(key2, value) {
  1833. Panel.setValue(key2, value);
  1834. }
  1835. }
  1836. );
  1837. return result;
  1838. };
  1839. const UIInput = function(text, key, defaultValue, description, changeCallback, placeholder = "", isNumber, isPassword, afterAddToUListCallBack, valueChangeCallback) {
  1840. let result = {
  1841. text,
  1842. type: "input",
  1843. isNumber: Boolean(isNumber),
  1844. isPassword: Boolean(isPassword),
  1845. attributes: {},
  1846. props: {},
  1847. description,
  1848. afterAddToUListCallBack,
  1849. getValue() {
  1850. let storageApiValue = this.props[PROPS_STORAGE_API];
  1851. return storageApiValue.get(key, defaultValue);
  1852. },
  1853. callback(event, value, valueAsNumber) {
  1854. let storageApiValue = this.props[PROPS_STORAGE_API];
  1855. storageApiValue.set(key, value);
  1856. },
  1857. placeholder
  1858. };
  1859. Reflect.set(result.attributes, ATTRIBUTE_KEY, key);
  1860. Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue);
  1861. PanelComponents.initComponentsStorageApi(
  1862. "input",
  1863. result,
  1864. {
  1865. get(key2, defaultValue2) {
  1866. return Panel.getValue(key2, defaultValue2);
  1867. },
  1868. set(key2, value) {
  1869. Panel.setValue(key2, value);
  1870. }
  1871. }
  1872. );
  1873. return result;
  1874. };
  1875. const UITextArea = function(text, key, defaultValue, description, changeCallback, placeholder = "", disabled, valueChangeCallBack) {
  1876. let result = {
  1877. text,
  1878. type: "textarea",
  1879. attributes: {},
  1880. props: {},
  1881. description,
  1882. placeholder,
  1883. disabled,
  1884. getValue() {
  1885. let storageApiValue = this.props[PROPS_STORAGE_API];
  1886. let value = storageApiValue.get(key, defaultValue);
  1887. if (Array.isArray(value)) {
  1888. return value.join("\n");
  1889. }
  1890. return value;
  1891. },
  1892. callback(event, value) {
  1893. let storageApiValue = this.props[PROPS_STORAGE_API];
  1894. storageApiValue.set(key, value);
  1895. }
  1896. };
  1897. Reflect.set(result.attributes, ATTRIBUTE_KEY, key);
  1898. Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue);
  1899. PanelComponents.initComponentsStorageApi(
  1900. "switch",
  1901. result,
  1902. {
  1903. get(key2, defaultValue2) {
  1904. return Panel.getValue(key2, defaultValue2);
  1905. },
  1906. set(key2, value) {
  1907. Panel.setValue(key2, value);
  1908. }
  1909. }
  1910. );
  1911. return result;
  1912. };
  1913. class RuleEditView {
  1914. option;
  1915. constructor(option) {
  1916. this.option = option;
  1917. }
  1918. /**
  1919. * 显示视图
  1920. */
  1921. async showView() {
  1922. let $dialog = __pops.confirm({
  1923. title: {
  1924. text: this.option.title,
  1925. position: "center"
  1926. },
  1927. content: {
  1928. text: (
  1929. /*html*/
  1930. `
  1931. <form class="rule-form-container" onsubmit="return false">
  1932. <ul class="rule-form-ulist"></ul>
  1933. <input type="submit" style="display: none;" />
  1934. </form>
  1935. `
  1936. ),
  1937. html: true
  1938. },
  1939. btn: utils.assign(
  1940. {
  1941. ok: {
  1942. callback: async () => {
  1943. await submitSaveOption();
  1944. }
  1945. }
  1946. },
  1947. this.option.btn || {},
  1948. true
  1949. ),
  1950. drag: true,
  1951. mask: {
  1952. enable: true
  1953. },
  1954. style: (
  1955. /*css*/
  1956. `
  1957. ${__pops.config.cssText.panelCSS}
  1958. .rule-form-container {
  1959. }
  1960. .rule-form-container li{
  1961. display: flex;
  1962. align-items: center;
  1963. justify-content: space-between;
  1964. padding: 5px 20px;
  1965. gap: 10px;
  1966. }
  1967. .rule-form-ulist-dynamic{
  1968. --button-margin-top: 0px;
  1969. --button-margin-right: 0px;
  1970. --button-margin-bottom: 0px;
  1971. --button-margin-left: 0px;
  1972. display: flex;
  1973. flex-direction: column;
  1974. align-items: flex-start;
  1975. padding: 5px 0px 5px 20px;
  1976. }
  1977. .rule-form-ulist-dynamic__inner{
  1978. width: 100%;
  1979. }
  1980. .rule-form-ulist-dynamic__inner-container{
  1981. display: flex;
  1982. align-items: center;
  1983. }
  1984. .dynamic-forms{
  1985. width: 100%;
  1986. }
  1987. .pops-panel-item-left-main-text{
  1988. max-width: 150px;
  1989. }
  1990. .pops-panel-item-right-text{
  1991. padding-left: 30px;
  1992. }
  1993. .pops-panel-item-right-text,
  1994. .pops-panel-item-right-main-text{
  1995. text-overflow: ellipsis;
  1996. overflow: hidden;
  1997. white-space: nowrap;
  1998. }
  1999. .pops-panel-item-left-desc-text{
  2000. line-height: normal;
  2001. margin-top: 6px;
  2002. font-size: 0.8em;
  2003. color: rgb(108, 108, 108);
  2004. }
  2005.  
  2006. ${this.option?.style ?? ""}
  2007. `
  2008. ),
  2009. width: typeof this.option.width === "function" ? this.option.width() : window.innerWidth > 500 ? "500px" : "88vw",
  2010. height: typeof this.option.height === "function" ? this.option.height() : window.innerHeight > 500 ? "500px" : "80vh"
  2011. });
  2012. let $form = $dialog.$shadowRoot.querySelector(
  2013. ".rule-form-container"
  2014. );
  2015. $dialog.$shadowRoot.querySelector(
  2016. "input[type=submit]"
  2017. );
  2018. let $ulist = $dialog.$shadowRoot.querySelector(".rule-form-ulist");
  2019. let view = await this.option.getView(await this.option.data());
  2020. $ulist.appendChild(view);
  2021. const submitSaveOption = async () => {
  2022. let result = await this.option.onsubmit($form, await this.option.data());
  2023. if (!result.success) {
  2024. return;
  2025. }
  2026. $dialog.close();
  2027. await this.option.dialogCloseCallBack(true);
  2028. };
  2029. }
  2030. }
  2031. class RuleFilterView {
  2032. option;
  2033. constructor(option) {
  2034. this.option = option;
  2035. }
  2036. showView() {
  2037. let $alert = __pops.alert({
  2038. title: {
  2039. text: this.option.title,
  2040. position: "center"
  2041. },
  2042. content: {
  2043. text: (
  2044. /*html*/
  2045. `
  2046. <div class="filter-container"></div>
  2047. `
  2048. )
  2049. },
  2050. btn: {
  2051. ok: {
  2052. text: "关闭",
  2053. type: "default"
  2054. }
  2055. },
  2056. drag: true,
  2057. mask: {
  2058. enable: true
  2059. },
  2060. width: window.innerWidth > 500 ? "350px" : "80vw",
  2061. height: window.innerHeight > 500 ? "300px" : "70vh",
  2062. style: (
  2063. /*css*/
  2064. `
  2065. .filter-container{
  2066. height: 100%;
  2067. display: flex;
  2068. flex-direction: column;
  2069. gap: 20px;
  2070. }
  2071. .filter-container button{
  2072. text-wrap: wrap;
  2073. padding: 8px;
  2074. height: auto;
  2075. text-align: left;
  2076. }
  2077. `
  2078. )
  2079. });
  2080. let $filterContainer = $alert.$shadowRoot.querySelector(".filter-container");
  2081. let $fragment = document.createDocumentFragment();
  2082. this.option.filterOption.forEach((filterOption) => {
  2083. let $button = domUtils.createElement(
  2084. "button",
  2085. {
  2086. innerText: filterOption.name
  2087. },
  2088. {
  2089. type: "button"
  2090. }
  2091. );
  2092. let execFilterAndCloseDialog = async () => {
  2093. let allRuleInfo = await this.option.getAllRuleInfo();
  2094. allRuleInfo.forEach(async (ruleInfo) => {
  2095. let filterResult = await filterOption.filterCallBack(ruleInfo.data);
  2096. if (!filterResult) {
  2097. domUtils.hide(ruleInfo.$el, false);
  2098. } else {
  2099. domUtils.show(ruleInfo.$el, false);
  2100. }
  2101. });
  2102. if (typeof this.option.execFilterCallBack === "function") {
  2103. await this.option.execFilterCallBack();
  2104. }
  2105. $alert.close();
  2106. };
  2107. domUtils.on($button, "click", async (event) => {
  2108. utils.preventEvent(event);
  2109. if (typeof filterOption.callback === "function") {
  2110. let result = await filterOption.callback(
  2111. event,
  2112. execFilterAndCloseDialog
  2113. );
  2114. if (!result) {
  2115. return;
  2116. }
  2117. }
  2118. await execFilterAndCloseDialog();
  2119. });
  2120. $fragment.appendChild($button);
  2121. });
  2122. $filterContainer.appendChild($fragment);
  2123. }
  2124. }
  2125. class RuleView {
  2126. option;
  2127. constructor(option) {
  2128. this.option = option;
  2129. }
  2130. /**
  2131. * 显示视图
  2132. * @param filterCallBack 返回值为false隐藏,true则不隐藏(不处理)
  2133. */
  2134. async showView(filterCallBack) {
  2135. let $popsConfirm = __pops.confirm({
  2136. title: {
  2137. text: this.option.title,
  2138. position: "center"
  2139. },
  2140. content: {
  2141. text: (
  2142. /*html*/
  2143. `
  2144. <div class="rule-view-container">
  2145. </div>
  2146. `
  2147. ),
  2148. html: true
  2149. },
  2150. btn: {
  2151. merge: true,
  2152. reverse: false,
  2153. position: "space-between",
  2154. ok: {
  2155. enable: this.option?.bottomControls?.add?.enable || true,
  2156. type: "primary",
  2157. text: "添加",
  2158. callback: async (event) => {
  2159. this.showEditView(
  2160. false,
  2161. await this.option.getAddData(),
  2162. $popsConfirm.$shadowRoot
  2163. );
  2164. }
  2165. },
  2166. close: {
  2167. enable: true,
  2168. callback(event) {
  2169. $popsConfirm.close();
  2170. }
  2171. },
  2172. cancel: {
  2173. enable: this.option?.bottomControls?.filter?.enable || false,
  2174. type: "default",
  2175. text: "过滤",
  2176. callback: (details, event) => {
  2177. if (typeof this.option?.bottomControls?.filter?.callback === "function") {
  2178. this.option.bottomControls.filter.callback();
  2179. }
  2180. let getAllRuleElement = () => {
  2181. return Array.from(
  2182. $popsConfirm.$shadowRoot.querySelectorAll(
  2183. ".rule-view-container .rule-item"
  2184. )
  2185. );
  2186. };
  2187. let $button = event.target.closest(".pops-confirm-btn").querySelector(".pops-confirm-btn-cancel span");
  2188. if (domUtils.text($button).includes("取消")) {
  2189. getAllRuleElement().forEach(($el) => {
  2190. domUtils.show($el, false);
  2191. });
  2192. domUtils.text($button, "过滤");
  2193. } else {
  2194. let ruleFilterView = new RuleFilterView({
  2195. title: this.option.bottomControls?.filter?.title ?? "过滤规则",
  2196. filterOption: this.option.bottomControls?.filter?.option || [],
  2197. execFilterCallBack() {
  2198. domUtils.text($button, "取消过滤");
  2199. },
  2200. getAllRuleInfo: () => {
  2201. return getAllRuleElement().map(($el) => {
  2202. return {
  2203. data: this.parseRuleItemElement($el).data,
  2204. $el
  2205. };
  2206. });
  2207. }
  2208. });
  2209. ruleFilterView.showView();
  2210. }
  2211. }
  2212. },
  2213. other: {
  2214. enable: this.option?.bottomControls?.clear?.enable || true,
  2215. type: "xiaomi-primary",
  2216. text: `清空所有(${(await this.option.data()).length})`,
  2217. callback: (event) => {
  2218. let $askDialog = __pops.confirm({
  2219. title: {
  2220. text: "提示",
  2221. position: "center"
  2222. },
  2223. content: {
  2224. text: "确定清空所有的数据?",
  2225. html: false
  2226. },
  2227. btn: {
  2228. ok: {
  2229. enable: true,
  2230. callback: async (popsEvent) => {
  2231. log.success("清空所有");
  2232. if (typeof this.option?.bottomControls?.clear?.callback === "function") {
  2233. this.option.bottomControls.clear.callback();
  2234. }
  2235. let data = await this.option.data();
  2236. if (data.length) {
  2237. Qmsg.error("清理失败");
  2238. return;
  2239. } else {
  2240. Qmsg.success("清理成功");
  2241. }
  2242. await this.updateDeleteAllBtnText($popsConfirm.$shadowRoot);
  2243. this.clearContent($popsConfirm.$shadowRoot);
  2244. $askDialog.close();
  2245. }
  2246. },
  2247. cancel: {
  2248. text: "取消",
  2249. enable: true
  2250. }
  2251. },
  2252. mask: { enable: true },
  2253. width: "300px",
  2254. height: "200px"
  2255. });
  2256. }
  2257. }
  2258. },
  2259. mask: {
  2260. enable: true
  2261. },
  2262. width: window.innerWidth > 500 ? "500px" : "88vw",
  2263. height: window.innerHeight > 500 ? "500px" : "80vh",
  2264. style: (
  2265. /*css*/
  2266. `
  2267. ${__pops.config.cssText.panelCSS}
  2268. .rule-item{
  2269. display: flex;
  2270. align-items: center;
  2271. line-height: normal;
  2272. font-size: 16px;
  2273. padding: 4px 8px;
  2274. gap: 8px;
  2275. }
  2276. .rule-name{
  2277. flex: 1;
  2278. white-space: nowrap;
  2279. text-overflow: ellipsis;
  2280. overflow: hidden;
  2281. }
  2282. .rule-controls{
  2283. display: flex;
  2284. align-items: center;
  2285. text-overflow: ellipsis;
  2286. overflow: hidden;
  2287. white-space: nowrap;
  2288. gap: 8px;
  2289. padding: 0px;
  2290. }
  2291. .rule-controls-enable{
  2292. }
  2293. .rule-controls-edit{
  2294. }
  2295. .rule-controls-delete{
  2296. }
  2297. .rule-controls-edit,
  2298. .rule-controls-delete{
  2299. width: 16px;
  2300. height: 16px;
  2301. cursor: pointer;
  2302. }
  2303. `
  2304. )
  2305. });
  2306. let allData = await this.option.data();
  2307. let changeButtonText = false;
  2308. for (let index = 0; index < allData.length; index++) {
  2309. let item = allData[index];
  2310. let $ruleItemList = await this.appendRuleItemElement(
  2311. $popsConfirm.$shadowRoot,
  2312. item
  2313. );
  2314. let flag = typeof filterCallBack === "function" ? filterCallBack(item) : true;
  2315. if (!flag) {
  2316. changeButtonText = true;
  2317. $ruleItemList.forEach(($el) => {
  2318. domUtils.hide($el, false);
  2319. });
  2320. }
  2321. }
  2322. if (changeButtonText) {
  2323. let $button = $popsConfirm.$shadowRoot.querySelector(
  2324. ".pops-confirm-btn-cancel span"
  2325. );
  2326. domUtils.text($button, "取消过滤");
  2327. }
  2328. }
  2329. /**
  2330. * 显示编辑视图
  2331. * @param isEdit 是否是编辑状态
  2332. * @param editData 编辑的数据
  2333. * @param $parentShadowRoot (可选)关闭弹窗后对ShadowRoot进行操作
  2334. * @param $editRuleItemElement (可选)关闭弹窗后对规则行进行更新数据
  2335. * @param updateDataCallBack (可选)关闭添加/编辑弹窗的回调(不更新数据)
  2336. * @param submitCallBack (可选)添加/修改提交的回调
  2337. */
  2338. showEditView(isEdit, editData, $parentShadowRoot, $editRuleItemElement, updateDataCallBack, submitCallBack) {
  2339. let dialogCloseCallBack = async (isSubmit) => {
  2340. if (isSubmit) {
  2341. if (typeof submitCallBack === "function") {
  2342. let newData = await this.option.getData(editData);
  2343. submitCallBack(newData);
  2344. }
  2345. } else {
  2346. if (!isEdit) {
  2347. await this.option.deleteData(editData);
  2348. }
  2349. if (typeof updateDataCallBack === "function") {
  2350. let newData = await this.option.getData(editData);
  2351. updateDataCallBack(newData);
  2352. }
  2353. }
  2354. };
  2355. let editView = new RuleEditView({
  2356. title: isEdit ? "编辑" : "添加",
  2357. data: () => {
  2358. return editData;
  2359. },
  2360. dialogCloseCallBack,
  2361. getView: async (data) => {
  2362. return await this.option.itemControls.edit.getView(data, isEdit);
  2363. },
  2364. btn: {
  2365. ok: {
  2366. enable: true,
  2367. text: isEdit ? "修改" : "添加"
  2368. },
  2369. cancel: {
  2370. callback: async (detail, event) => {
  2371. detail.close();
  2372. await dialogCloseCallBack(false);
  2373. }
  2374. },
  2375. close: {
  2376. callback: async (detail, event) => {
  2377. detail.close();
  2378. await dialogCloseCallBack(false);
  2379. }
  2380. }
  2381. },
  2382. onsubmit: async ($form, data) => {
  2383. let result = await this.option.itemControls.edit.onsubmit(
  2384. $form,
  2385. isEdit,
  2386. data
  2387. );
  2388. if (result.success) {
  2389. if (isEdit) {
  2390. Qmsg.success("修改成功");
  2391. $parentShadowRoot && await this.updateRuleItemElement(
  2392. result.data,
  2393. $editRuleItemElement,
  2394. $parentShadowRoot
  2395. );
  2396. } else {
  2397. $parentShadowRoot && await this.appendRuleItemElement(
  2398. $parentShadowRoot,
  2399. result.data
  2400. );
  2401. }
  2402. } else {
  2403. if (isEdit) {
  2404. log.error("修改失败");
  2405. }
  2406. }
  2407. return result;
  2408. },
  2409. style: this.option.itemControls.edit.style,
  2410. width: this.option.itemControls.edit.width,
  2411. height: this.option.itemControls.edit.height
  2412. });
  2413. editView.showView();
  2414. }
  2415. /**
  2416. * 解析弹窗内的各个元素
  2417. */
  2418. parseViewElement($shadowRoot) {
  2419. let $container = $shadowRoot.querySelector(
  2420. ".rule-view-container"
  2421. );
  2422. let $deleteBtn = $shadowRoot.querySelector(
  2423. ".pops-confirm-btn button.pops-confirm-btn-other"
  2424. );
  2425. return {
  2426. /** 容器 */
  2427. $container,
  2428. /** 左下角的清空按钮 */
  2429. $deleteBtn
  2430. };
  2431. }
  2432. /**
  2433. * 解析每一项的元素
  2434. */
  2435. parseRuleItemElement($ruleElement) {
  2436. let $enable = $ruleElement.querySelector(
  2437. ".rule-controls-enable"
  2438. );
  2439. let $enableSwitch = $enable.querySelector(".pops-panel-switch");
  2440. let $enableSwitchInput = $enable.querySelector(
  2441. ".pops-panel-switch__input"
  2442. );
  2443. let $enableSwitchCore = $enable.querySelector(
  2444. ".pops-panel-switch__core"
  2445. );
  2446. let $edit = $ruleElement.querySelector(".rule-controls-edit");
  2447. let $delete = $ruleElement.querySelector(
  2448. ".rule-controls-delete"
  2449. );
  2450. return {
  2451. /** 启用开关 */
  2452. $enable,
  2453. /** 启用开关的container */
  2454. $enableSwitch,
  2455. /** 启用开关的input */
  2456. $enableSwitchInput,
  2457. /** 启用开关的core */
  2458. $enableSwitchCore,
  2459. /** 编辑按钮 */
  2460. $edit,
  2461. /** 删除按钮 */
  2462. $delete,
  2463. /** 存储在元素上的数据 */
  2464. data: Reflect.get($ruleElement, "data-rule")
  2465. };
  2466. }
  2467. /**
  2468. * 创建一条规则元素
  2469. */
  2470. async createRuleItemElement(data, $shadowRoot) {
  2471. let name = await this.option.getDataItemName(data);
  2472. let $ruleItem = domUtils.createElement("div", {
  2473. className: "rule-item",
  2474. innerHTML: (
  2475. /*html*/
  2476. `
  2477. <div class="rule-name">${name}</div>
  2478. <div class="rule-controls">
  2479. <div class="rule-controls-enable">
  2480. <div class="pops-panel-switch">
  2481. <input class="pops-panel-switch__input" type="checkbox">
  2482. <span class="pops-panel-switch__core">
  2483. <div class="pops-panel-switch__action">
  2484. </div>
  2485. </span>
  2486. </div>
  2487. </div>
  2488. <div class="rule-controls-edit">
  2489. ${__pops.config.iconSVG.edit}
  2490. </div>
  2491. <div class="rule-controls-delete">
  2492. ${__pops.config.iconSVG.delete}
  2493. </div>
  2494. </div>
  2495. `
  2496. )
  2497. });
  2498. Reflect.set($ruleItem, "data-rule", data);
  2499. let switchCheckedClassName = "pops-panel-switch-is-checked";
  2500. const {
  2501. $enable,
  2502. $enableSwitch,
  2503. $enableSwitchCore,
  2504. $enableSwitchInput,
  2505. $delete,
  2506. $edit
  2507. } = this.parseRuleItemElement($ruleItem);
  2508. if (this.option.itemControls.enable.enable) {
  2509. domUtils.on($enableSwitchCore, "click", async (event) => {
  2510. let isChecked = false;
  2511. if ($enableSwitch.classList.contains(switchCheckedClassName)) {
  2512. $enableSwitch.classList.remove(switchCheckedClassName);
  2513. isChecked = false;
  2514. } else {
  2515. $enableSwitch.classList.add(switchCheckedClassName);
  2516. isChecked = true;
  2517. }
  2518. $enableSwitchInput.checked = isChecked;
  2519. await this.option.itemControls.enable.callback(data, isChecked);
  2520. });
  2521. if (await this.option.itemControls.enable.getEnable(data)) {
  2522. $enableSwitch.classList.add(switchCheckedClassName);
  2523. }
  2524. } else {
  2525. $enable.remove();
  2526. }
  2527. if (this.option.itemControls.edit.enable) {
  2528. domUtils.on($edit, "click", (event) => {
  2529. utils.preventEvent(event);
  2530. this.showEditView(true, data, $shadowRoot, $ruleItem, (newData) => {
  2531. data = null;
  2532. data = newData;
  2533. });
  2534. });
  2535. } else {
  2536. $edit.remove();
  2537. }
  2538. if (this.option.itemControls.delete.enable) {
  2539. domUtils.on($delete, "click", (event) => {
  2540. utils.preventEvent(event);
  2541. let $askDialog = __pops.confirm({
  2542. title: {
  2543. text: "提示",
  2544. position: "center"
  2545. },
  2546. content: {
  2547. text: "确定删除该条数据?",
  2548. html: false
  2549. },
  2550. btn: {
  2551. ok: {
  2552. enable: true,
  2553. callback: async (popsEvent) => {
  2554. log.success("删除数据");
  2555. let flag = await this.option.itemControls.delete.deleteCallBack(
  2556. data
  2557. );
  2558. if (flag) {
  2559. Qmsg.success("成功删除该数据");
  2560. $ruleItem.remove();
  2561. await this.updateDeleteAllBtnText($shadowRoot);
  2562. $askDialog.close();
  2563. } else {
  2564. Qmsg.error("删除该数据失败");
  2565. }
  2566. }
  2567. },
  2568. cancel: {
  2569. text: "取消",
  2570. enable: true
  2571. }
  2572. },
  2573. mask: {
  2574. enable: true
  2575. },
  2576. width: "300px",
  2577. height: "200px"
  2578. });
  2579. });
  2580. } else {
  2581. $delete.remove();
  2582. }
  2583. return $ruleItem;
  2584. }
  2585. /**
  2586. * 添加一个规则元素
  2587. */
  2588. async appendRuleItemElement($shadowRoot, data) {
  2589. let { $container } = this.parseViewElement($shadowRoot);
  2590. let $ruleItem = [];
  2591. let iteratorData = Array.isArray(data) ? data : [data];
  2592. for (let index = 0; index < iteratorData.length; index++) {
  2593. let item = iteratorData[index];
  2594. let $item = await this.createRuleItemElement(item, $shadowRoot);
  2595. $container.appendChild($item);
  2596. $ruleItem.push($item);
  2597. }
  2598. await this.updateDeleteAllBtnText($shadowRoot);
  2599. return $ruleItem;
  2600. }
  2601. /**
  2602. * 更新弹窗内容的元素
  2603. */
  2604. async updateRuleContaienrElement($shadowRoot) {
  2605. this.clearContent($shadowRoot);
  2606. const { $container } = this.parseViewElement($shadowRoot);
  2607. let data = await this.option.data();
  2608. await this.appendRuleItemElement($shadowRoot, data);
  2609. await this.updateDeleteAllBtnText($shadowRoot);
  2610. }
  2611. /**
  2612. * 更新规则元素
  2613. */
  2614. async updateRuleItemElement(data, $oldRuleItem, $shadowRoot) {
  2615. let $newRuleItem = await this.createRuleItemElement(data, $shadowRoot);
  2616. $oldRuleItem.after($newRuleItem);
  2617. $oldRuleItem.remove();
  2618. }
  2619. /**
  2620. * 清空内容
  2621. */
  2622. clearContent($shadowRoot) {
  2623. const { $container } = this.parseViewElement($shadowRoot);
  2624. domUtils.html($container, "");
  2625. }
  2626. /**
  2627. * 设置删除按钮的文字
  2628. */
  2629. setDeleteBtnText($shadowRoot, text, isHTML = false) {
  2630. const { $deleteBtn } = this.parseViewElement($shadowRoot);
  2631. if (isHTML) {
  2632. domUtils.html($deleteBtn, text);
  2633. } else {
  2634. domUtils.text($deleteBtn, text);
  2635. }
  2636. }
  2637. /**
  2638. * 更新【清空所有】的按钮的文字
  2639. * @param $shadowRoot
  2640. */
  2641. async updateDeleteAllBtnText($shadowRoot) {
  2642. let data = await this.option.data();
  2643. this.setDeleteBtnText($shadowRoot, `清空所有(${data.length})`);
  2644. }
  2645. }
  2646. const CookieRule = {
  2647. $key: {
  2648. STORAGE_KEY: "cookie-rule"
  2649. },
  2650. $data: {
  2651. /** 匹配到的规则数据 */
  2652. matchedRuleList: []
  2653. },
  2654. /** 初始化数据 */
  2655. init() {
  2656. this.$data.matchedRuleList = [];
  2657. this.$data.matchedRuleList = this.getMatchedRuleList();
  2658. if (this.$data.matchedRuleList.length) {
  2659. GM_Menu.add({
  2660. key: "matched-cookie-rule-list",
  2661. text: `${window.location.hostname} ${this.$data.matchedRuleList.length}条规则`,
  2662. isStoreValue: false,
  2663. autoReload: false,
  2664. showText(text, enable) {
  2665. return text;
  2666. },
  2667. callback(data) {
  2668. console.log(CookieRule.$data.matchedRuleList);
  2669. alert(
  2670. "以下是命中的规则名:\n" + CookieRule.$data.matchedRuleList.map((it) => it.name).join("\n")
  2671. );
  2672. }
  2673. });
  2674. }
  2675. },
  2676. /**
  2677. * 获取匹配的规则
  2678. */
  2679. getMatchedRuleList(url = window.location.href) {
  2680. let allData = this.getData();
  2681. let matchedRuleList = [];
  2682. allData.forEach((data) => {
  2683. if (!data.enable) {
  2684. return;
  2685. }
  2686. let url2 = window.location.href;
  2687. let ruleUrl = data.data.url;
  2688. let enableRegExpToMatchUrl = data.data.enableRegExpToMatchUrl;
  2689. if (enableRegExpToMatchUrl) {
  2690. let regExpUrl = new RegExp(ruleUrl, "i");
  2691. if (!regExpUrl.test(url2)) {
  2692. return;
  2693. }
  2694. } else {
  2695. if (!url2.includes(ruleUrl)) {
  2696. return;
  2697. }
  2698. }
  2699. matchedRuleList.push(data);
  2700. });
  2701. return matchedRuleList;
  2702. },
  2703. /**
  2704. * 显示视图
  2705. */
  2706. showView() {
  2707. let panelHandlerComponents = __pops.config.PanelHandlerComponents();
  2708. function generateStorageApi(data, handler) {
  2709. return {
  2710. get(key, defaultValue) {
  2711. return Reflect.get(data, key) ?? defaultValue;
  2712. },
  2713. set(key, value) {
  2714. Reflect.set(data, key, value);
  2715. }
  2716. };
  2717. }
  2718. const matchedRuleList = this.getMatchedRuleList();
  2719. let ruleView = new RuleView({
  2720. title: "Cookie规则",
  2721. data: () => {
  2722. return this.getData();
  2723. },
  2724. getAddData: () => {
  2725. return this.getTemplateData();
  2726. },
  2727. getDataItemName: (data) => {
  2728. return data["name"];
  2729. },
  2730. updateData: (data) => {
  2731. return this.updateData(data);
  2732. },
  2733. deleteData: (data) => {
  2734. return this.deleteData(data);
  2735. },
  2736. getData: (data) => {
  2737. let allData = this.getData();
  2738. let findValue = allData.find((item) => item.uuid === data.uuid);
  2739. return findValue ?? data;
  2740. },
  2741. itemControls: {
  2742. enable: {
  2743. enable: true,
  2744. getEnable(data) {
  2745. return data.enable;
  2746. },
  2747. callback: (data, enable) => {
  2748. data.enable = enable;
  2749. this.updateData(data);
  2750. }
  2751. },
  2752. edit: {
  2753. enable: true,
  2754. getView: (data, isEdit) => {
  2755. let $fragment = document.createDocumentFragment();
  2756. let templateData = this.getTemplateData();
  2757. if (!isEdit) {
  2758. data = templateData;
  2759. }
  2760. let enable_template = UISwitch(
  2761. "启用",
  2762. "enable",
  2763. templateData.enable
  2764. );
  2765. Reflect.set(
  2766. enable_template.props,
  2767. PROPS_STORAGE_API,
  2768. generateStorageApi(data)
  2769. );
  2770. let $enable = panelHandlerComponents.createSectionContainerItem_switch(
  2771. enable_template
  2772. );
  2773. let name_template = UIInput(
  2774. "规则名称",
  2775. "name",
  2776. "",
  2777. templateData.name,
  2778. void 0,
  2779. "必填"
  2780. );
  2781. Reflect.set(
  2782. name_template.props,
  2783. PROPS_STORAGE_API,
  2784. generateStorageApi(data)
  2785. );
  2786. let $name = panelHandlerComponents.createSectionContainerItem_input(
  2787. name_template
  2788. );
  2789. let apiName_template = UISelect(
  2790. "Cookie管理Api",
  2791. "execApiName",
  2792. templateData.data.execApiName,
  2793. [
  2794. {
  2795. text: "(当前)" + CookieManager.cookieManagerApiName,
  2796. value: "use-global"
  2797. },
  2798. ...CookieManagerApiNameList.map((it) => {
  2799. return {
  2800. text: it,
  2801. value: it
  2802. };
  2803. })
  2804. ],
  2805. void 0,
  2806. "操作Cookie的Api函数"
  2807. );
  2808. Reflect.set(
  2809. apiName_template.props,
  2810. PROPS_STORAGE_API,
  2811. generateStorageApi(data.data)
  2812. );
  2813. let $apiName = panelHandlerComponents.createSectionContainerItem_select(
  2814. apiName_template
  2815. );
  2816. let url_template = UIInput(
  2817. "网址",
  2818. "url",
  2819. templateData.data.url,
  2820. "用于执行该规则的网址",
  2821. void 0,
  2822. "必填"
  2823. );
  2824. Reflect.set(
  2825. url_template.props,
  2826. PROPS_STORAGE_API,
  2827. generateStorageApi(data.data)
  2828. );
  2829. let $url = panelHandlerComponents.createSectionContainerItem_input(
  2830. url_template
  2831. );
  2832. let enableRegExpToMatchUrl_template = UISwitch(
  2833. "启用正则匹配网址",
  2834. "enableRegExpToMatchUrl",
  2835. templateData.data.enableRegExpToMatchUrl
  2836. );
  2837. Reflect.set(
  2838. enableRegExpToMatchUrl_template.props,
  2839. PROPS_STORAGE_API,
  2840. generateStorageApi(data.data)
  2841. );
  2842. let $enableRegExpToMatchUrl = panelHandlerComponents.createSectionContainerItem_switch(
  2843. enableRegExpToMatchUrl_template
  2844. );
  2845. let cookieName_template = UIInput(
  2846. "Cookie名称",
  2847. "cookieName",
  2848. templateData.data.cookieName,
  2849. "用于匹配执行操作的Cookie名",
  2850. void 0,
  2851. "必填"
  2852. );
  2853. Reflect.set(
  2854. cookieName_template.props,
  2855. PROPS_STORAGE_API,
  2856. generateStorageApi(data.data)
  2857. );
  2858. let $cookieName = panelHandlerComponents.createSectionContainerItem_input(
  2859. cookieName_template
  2860. );
  2861. let enableRegExpToMatchCookieName_template = UISwitch(
  2862. "启用正则匹配Cookie名称",
  2863. "enableRegExpToMatchCookieName",
  2864. templateData.data.enableRegExpToMatchCookieName
  2865. );
  2866. Reflect.set(
  2867. enableRegExpToMatchCookieName_template.props,
  2868. PROPS_STORAGE_API,
  2869. generateStorageApi(data.data)
  2870. );
  2871. let $enableRegExpToMatchCookieName = panelHandlerComponents.createSectionContainerItem_switch(
  2872. enableRegExpToMatchCookieName_template
  2873. );
  2874. let operationMode_template = UISelect(
  2875. "操作模式",
  2876. "operationMode",
  2877. templateData.data.operationMode,
  2878. [
  2879. {
  2880. value: "delete",
  2881. text: "删除Cookie"
  2882. },
  2883. {
  2884. value: "extended",
  2885. text: "自动延长Cookie有效期30天"
  2886. },
  2887. {
  2888. value: "extended-90",
  2889. text: "自动延长Cookie有效期90天"
  2890. },
  2891. {
  2892. value: "extended-180",
  2893. text: "自动延长Cookie有效期180天"
  2894. },
  2895. {
  2896. value: "extended-360",
  2897. text: "自动延长Cookie有效期360天"
  2898. }
  2899. ]
  2900. );
  2901. Reflect.set(
  2902. operationMode_template.props,
  2903. PROPS_STORAGE_API,
  2904. generateStorageApi(data.data)
  2905. );
  2906. let $operationMode = panelHandlerComponents.createSectionContainerItem_select(
  2907. operationMode_template
  2908. );
  2909. let remark_template = UITextArea(
  2910. "备注",
  2911. "remark",
  2912. templateData.data.remark
  2913. );
  2914. Reflect.set(
  2915. remark_template.props,
  2916. PROPS_STORAGE_API,
  2917. generateStorageApi(data.data)
  2918. );
  2919. let $remark = panelHandlerComponents.createSectionContainerItem_textarea(
  2920. remark_template
  2921. );
  2922. $fragment.append(
  2923. $enable,
  2924. $name,
  2925. $apiName,
  2926. $url,
  2927. $enableRegExpToMatchUrl,
  2928. $cookieName,
  2929. $enableRegExpToMatchCookieName,
  2930. $operationMode,
  2931. $remark
  2932. );
  2933. return $fragment;
  2934. },
  2935. onsubmit: ($form, isEdit, editData) => {
  2936. let $ulist_li = $form.querySelectorAll(
  2937. ".rule-form-ulist > li"
  2938. );
  2939. let data = this.getTemplateData();
  2940. if (isEdit) {
  2941. data.uuid = editData.uuid;
  2942. }
  2943. try {
  2944. $ulist_li.forEach(($li) => {
  2945. let formConfig = Reflect.get($li, "__formConfig__");
  2946. let attrs = Reflect.get(formConfig, "attributes");
  2947. let storageApi = Reflect.get($li, PROPS_STORAGE_API);
  2948. let key = Reflect.get(attrs, ATTRIBUTE_KEY);
  2949. let defaultValue = Reflect.get(attrs, ATTRIBUTE_DEFAULT_VALUE);
  2950. let value = storageApi.get(key, defaultValue);
  2951. if (Reflect.has(data, key)) {
  2952. Reflect.set(data, key, value);
  2953. } else if (Reflect.has(data.data, key)) {
  2954. Reflect.set(data.data, key, value);
  2955. } else {
  2956. log.error(`${key}不在数据中`);
  2957. }
  2958. });
  2959. if (data.name.trim() === "") {
  2960. Qmsg.error("规则名称不能为空");
  2961. return {
  2962. success: false,
  2963. data
  2964. };
  2965. }
  2966. if (data.data.url.trim() === "") {
  2967. Qmsg.error("网址不能为空");
  2968. return {
  2969. success: false,
  2970. data
  2971. };
  2972. }
  2973. if (data.data.cookieName.trim() === "") {
  2974. Qmsg.error("Cookie名称不能为空");
  2975. return {
  2976. success: false,
  2977. data
  2978. };
  2979. }
  2980. if (isEdit) {
  2981. return {
  2982. success: this.updateData(data),
  2983. data
  2984. };
  2985. } else {
  2986. return {
  2987. success: this.addData(data),
  2988. data
  2989. };
  2990. }
  2991. } catch (error) {
  2992. log.error(error);
  2993. return {
  2994. success: false,
  2995. data
  2996. };
  2997. } finally {
  2998. this.init();
  2999. }
  3000. },
  3001. style: (
  3002. /*css*/
  3003. `
  3004. .pops-panel-textarea textarea{
  3005. height: 150px;
  3006. }
  3007. .pops-panel-item-left-desc-text{
  3008. line-height: normal;
  3009. margin-top: 6px;
  3010. font-size: 0.8em;
  3011. color: rgb(108, 108, 108);
  3012. max-width: 100px;
  3013. }
  3014. `
  3015. )
  3016. },
  3017. delete: {
  3018. enable: true,
  3019. deleteCallBack: (data) => {
  3020. return this.deleteData(data);
  3021. }
  3022. }
  3023. },
  3024. bottomControls: {
  3025. filter: {
  3026. enable: true,
  3027. option: [
  3028. {
  3029. name: "过滤-已启用",
  3030. filterCallBack(data) {
  3031. return data.enable;
  3032. }
  3033. },
  3034. {
  3035. name: "过滤-未启用",
  3036. filterCallBack(data) {
  3037. return !data.enable;
  3038. }
  3039. },
  3040. {
  3041. name: "过滤-当前网站执行",
  3042. filterCallBack(data) {
  3043. return matchedRuleList.some((it) => it.uuid === data.uuid);
  3044. }
  3045. }
  3046. ]
  3047. }
  3048. }
  3049. });
  3050. ruleView.showView();
  3051. },
  3052. /**
  3053. * 获取模板数据
  3054. */
  3055. getTemplateData() {
  3056. return {
  3057. uuid: utils.generateUUID(),
  3058. enable: true,
  3059. name: "",
  3060. data: {
  3061. url: "",
  3062. execApiName: "use-global",
  3063. enableRegExpToMatchUrl: false,
  3064. cookieName: "",
  3065. enableRegExpToMatchCookieName: false,
  3066. operationMode: "delete",
  3067. remark: ""
  3068. }
  3069. };
  3070. },
  3071. /**
  3072. * 获取数据
  3073. */
  3074. getData() {
  3075. return _GM_getValue(this.$key.STORAGE_KEY, []);
  3076. },
  3077. /**
  3078. * 设置数据
  3079. * @param data
  3080. */
  3081. setData(data) {
  3082. _GM_setValue(this.$key.STORAGE_KEY, data);
  3083. },
  3084. /**
  3085. * 添加数据
  3086. * @param data
  3087. */
  3088. addData(data) {
  3089. let localData = this.getData();
  3090. let findIndex = localData.findIndex((item) => item.uuid == data.uuid);
  3091. if (findIndex === -1) {
  3092. localData.push(data);
  3093. _GM_setValue(this.$key.STORAGE_KEY, localData);
  3094. return true;
  3095. } else {
  3096. return false;
  3097. }
  3098. },
  3099. /**
  3100. * 更新数据
  3101. * @param data
  3102. */
  3103. updateData(data) {
  3104. let localData = this.getData();
  3105. let index = localData.findIndex((item) => item.uuid == data.uuid);
  3106. let updateFlag = false;
  3107. if (index !== -1) {
  3108. updateFlag = true;
  3109. localData[index] = data;
  3110. }
  3111. this.setData(localData);
  3112. return updateFlag;
  3113. },
  3114. /**
  3115. * 删除数据
  3116. * @param data
  3117. */
  3118. deleteData(data) {
  3119. let localData = this.getData();
  3120. let index = localData.findIndex((item) => item.uuid == data.uuid);
  3121. let deleteFlag = false;
  3122. if (index !== -1) {
  3123. deleteFlag = true;
  3124. localData.splice(index, 1);
  3125. }
  3126. this.setData(localData);
  3127. return deleteFlag;
  3128. },
  3129. /**
  3130. * 清空数据
  3131. */
  3132. clearData() {
  3133. _GM_deleteValue(this.$key.STORAGE_KEY);
  3134. },
  3135. /**
  3136. * 导出规则
  3137. */
  3138. exportRule(fileName = "rule.json") {
  3139. let allRule = this.getData();
  3140. let blob = new Blob([JSON.stringify(allRule, null, 4)]);
  3141. let blobUrl = window.URL.createObjectURL(blob);
  3142. let $a = domUtils.createElement("a");
  3143. $a.href = blobUrl;
  3144. $a.download = fileName;
  3145. $a.click();
  3146. setTimeout(() => {
  3147. window.URL.revokeObjectURL(blobUrl);
  3148. }, 1500);
  3149. },
  3150. /**
  3151. * 导入规则
  3152. */
  3153. importRule() {
  3154. let $alert = __pops.alert({
  3155. title: {
  3156. text: "请选择导入方式",
  3157. position: "center"
  3158. },
  3159. content: {
  3160. text: (
  3161. /*html*/
  3162. `
  3163. <div class="import-mode" data-mode="local">本地导入</div>
  3164. <div class="import-mode" data-mode="network">网络导入</div>
  3165. `
  3166. ),
  3167. html: true
  3168. },
  3169. width: PanelUISize.info.width,
  3170. height: PanelUISize.info.height,
  3171. style: (
  3172. /*css*/
  3173. `
  3174. .import-mode{
  3175. display: inline-block;
  3176. margin: 10px;
  3177. padding: 10px;
  3178. border: 1px solid #ccc;
  3179. border-radius: 5px;
  3180. cursor: pointer;
  3181. }
  3182. `
  3183. )
  3184. });
  3185. let $local = $alert.$shadowRoot.querySelector(
  3186. ".import-mode[data-mode='local']"
  3187. );
  3188. let $network = $alert.$shadowRoot.querySelector(
  3189. ".import-mode[data-mode='network']"
  3190. );
  3191. domUtils.on($local, "click", (event) => {
  3192. utils.preventEvent(event);
  3193. $alert.close();
  3194. let $input = domUtils.createElement("input", {
  3195. type: "file",
  3196. accept: ".json"
  3197. });
  3198. domUtils.on($input, ["propertychange", "input"], (event2) => {
  3199. if (!$input.files?.length) {
  3200. return;
  3201. }
  3202. let uploadFile = $input.files[0];
  3203. let fileReader = new FileReader();
  3204. fileReader.onload = () => {
  3205. let data = utils.toJSON(fileReader.result);
  3206. if (!Array.isArray(data)) {
  3207. log.error("不是正确的规则文件", data);
  3208. Qmsg.error("不是正确的规则文件");
  3209. return;
  3210. }
  3211. this.setData(data);
  3212. Qmsg.success(`成功导入 ${data.length}条规则`);
  3213. };
  3214. fileReader.readAsText(uploadFile, "UTF-8");
  3215. });
  3216. $input.click();
  3217. });
  3218. domUtils.on($network, "click", (event) => {
  3219. utils.preventEvent(event);
  3220. $alert.close();
  3221. __pops.prompt({
  3222. title: {
  3223. text: "网络导入",
  3224. position: "center"
  3225. },
  3226. content: {
  3227. text: "",
  3228. placeholder: "url",
  3229. focus: true
  3230. },
  3231. btn: {
  3232. ok: {
  3233. callback: async (eventDetails, event2) => {
  3234. let url = eventDetails.text;
  3235. if (utils.isNull(url)) {
  3236. Qmsg.error("请填入完整的url");
  3237. return;
  3238. }
  3239. let response = await httpx.get(url);
  3240. if (!response.status) {
  3241. return;
  3242. }
  3243. let data = utils.toJSON(response.data.responseText);
  3244. if (!Array.isArray(data)) {
  3245. log.error("不是正确的规则文件", response, data);
  3246. Qmsg.error("不是正确的规则文件");
  3247. return;
  3248. }
  3249. this.setData(data);
  3250. eventDetails.close();
  3251. Qmsg.success(`成功导入 ${data.length}条规则`);
  3252. }
  3253. }
  3254. },
  3255. width: PanelUISize.info.width,
  3256. height: "auto"
  3257. });
  3258. });
  3259. }
  3260. };
  3261. const CookieBackUpManager = {
  3262. /**
  3263. * 加密
  3264. * @param text 要加密的文本
  3265. * @param secretKey 密钥
  3266. */
  3267. encrypt(text, secretKey) {
  3268. return CryptoJS.AES.encrypt(text, secretKey).toString();
  3269. },
  3270. /**
  3271. * 解密
  3272. * @param text 要解密的文本
  3273. * @param secretKey 密钥
  3274. */
  3275. decrypt(text, secretKey) {
  3276. const bytes = CryptoJS.AES.decrypt(text, secretKey);
  3277. return bytes.toString(CryptoJS.enc.Utf8);
  3278. },
  3279. /**
  3280. * 格式化导出的cookie
  3281. */
  3282. formatCookie(cookie, type, encodePwd) {
  3283. let cookieText = "";
  3284. if (type === "header_string") {
  3285. cookieText = cookie.map((it) => {
  3286. let cookieValue = it.value;
  3287. return `${it.name}=${cookieValue}; `;
  3288. }).join("");
  3289. } else if (type === "json") {
  3290. cookieText = JSON.stringify(
  3291. {
  3292. api: CookieManager.cookieManagerApiName,
  3293. hostname: window.location.hostname,
  3294. data: cookie
  3295. },
  3296. null,
  3297. 2
  3298. );
  3299. } else {
  3300. throw new Error("不支持的格式化类型:" + type);
  3301. }
  3302. if (encodePwd) {
  3303. cookieText = this.encrypt(cookieText, encodePwd);
  3304. }
  3305. return cookieText;
  3306. },
  3307. /**
  3308. * 显示导出弹窗
  3309. */
  3310. showExportDialog() {
  3311. let $confirm = __pops.confirm({
  3312. title: {
  3313. text: "导出 Cookie",
  3314. position: "center"
  3315. },
  3316. content: {
  3317. text: (
  3318. /*html*/
  3319. `
  3320. <p class="tip-text cookie-format-type-tip-text">您希望以哪种格式导出 Cookie?</p>
  3321. <div class="cookie-format-type-container">
  3322. <div class="cookie-format-type-item">
  3323. <input id="cookie-format-header_string" type="radio" name="format" value="header_string">
  3324. <label for="cookie-format-header_string">Header String</label>
  3325. </div>
  3326. <div class="cookie-format-type-item">
  3327. <input id="cookie-format-json" type="radio" name="format" value="json">
  3328. <label for="cookie-format-json">JSON</label>
  3329. </div>
  3330. </div>
  3331. <p class="tip-text export-example-code-tip-text">示例</p>
  3332. <div class="export-example-code-text-container">
  3333. <pre></pre>
  3334. </div>
  3335. <div class="cookir-format-encode-pwd-container">
  3336. <label for="hostOnly">用于加密 Cookie 的密码</label>
  3337. <input id="encode-pwd" type="password" placeholder="用于加密 Cookie 的密码" value="">
  3338. <p>如果您希望在导出前加密 Cookie,请输入密码(可选)。</p>
  3339. </div>
  3340. `
  3341. ),
  3342. html: true
  3343. },
  3344. width: window.innerWidth < 400 ? "88vw" : "400px",
  3345. height: "auto",
  3346. btn: {
  3347. merge: true,
  3348. position: "space-between",
  3349. ok: {
  3350. text: "导出",
  3351. async callback(eventDetails, event) {
  3352. let cookieList = CookieManagerView.$data.cookieList;
  3353. if (cookieList.length === 0) {
  3354. Qmsg.warning("Cookie为空");
  3355. return;
  3356. }
  3357. let cookieText = CookieBackUpManager.formatCookie(
  3358. cookieList,
  3359. dialogConfig.exportType,
  3360. dialogConfig.encodePwd
  3361. );
  3362. const blob = new Blob([cookieText], { type: "text/plain" });
  3363. const url = URL.createObjectURL(blob);
  3364. let $anchor = domUtils.createElement("a", {
  3365. download: `${window.location.hostname}_${dialogConfig.exportType}_${CookieManager.cookieManagerApiName}_${Date.now()}.txt`,
  3366. href: url,
  3367. target: "_blank"
  3368. });
  3369. $anchor.click();
  3370. setTimeout(() => {
  3371. URL.revokeObjectURL(url);
  3372. }, 500);
  3373. eventDetails.close();
  3374. }
  3375. },
  3376. other: {
  3377. enable: true,
  3378. text: "导出至剪贴板",
  3379. type: "xiaomi-primary",
  3380. async callback(eventDetails, event) {
  3381. let cookieList = CookieManagerView.$data.cookieList;
  3382. if (cookieList.length === 0) {
  3383. Qmsg.warning("Cookie为空");
  3384. return;
  3385. }
  3386. let cookieText = CookieBackUpManager.formatCookie(
  3387. cookieList,
  3388. dialogConfig.exportType,
  3389. dialogConfig.encodePwd
  3390. );
  3391. const status = await utils.setClip(cookieText);
  3392. if (status) {
  3393. Qmsg.success("复制成功");
  3394. } else {
  3395. Qmsg.error("复制失败");
  3396. }
  3397. eventDetails.close();
  3398. }
  3399. }
  3400. },
  3401. style: (
  3402. /*css*/
  3403. `
  3404. ${__pops.config.cssText.panelCSS}
  3405.  
  3406. .pops-content{
  3407. padding: 20px;
  3408. }
  3409. .cookie-format-type-container{
  3410. display: flex;
  3411. gap: 10px;
  3412. margin: 10px 0px;
  3413. align-items: center;
  3414. flex-wrap: wrap;
  3415. justify-content: space-between;
  3416. }
  3417. .cookie-format-type-item input[type="radio"]{
  3418. width: 1rem;
  3419. height: 1rem;
  3420. }
  3421. .export-example-code-text-container{
  3422. padding: 10px;
  3423. background-color: rgb(209 213 219 / 1);
  3424. border-radius: 10px;
  3425. width: 100%;
  3426. margin: 1rem 0px;
  3427. }
  3428. .export-example-code-text-container pre{
  3429. font-feature-settings: normal;
  3430. font-variation-settings: normal;
  3431. font-size: 1em;
  3432. margin: 0;
  3433. white-space: break-spaces;
  3434. }
  3435. .cookie-format-type-container label{
  3436. color: rgb(17 24 39 / 1);
  3437. }
  3438. .cookir-format-encode-pwd-container label{
  3439. color: #111827;
  3440. }
  3441. .cookie-format-type-tip-text,
  3442. .export-example-code-tip-text,
  3443. .cookir-format-encode-pwd-container label{
  3444. font-weight: 600;
  3445. }
  3446. .cookir-format-encode-pwd-container input{
  3447. border-radius: 0.5rem;
  3448. width: 100%;
  3449. border: 1px solid #d1d5db;
  3450. background-color: #f9fafb;
  3451. padding: 0.625rem;
  3452. margin: 0.65rem 0px;
  3453. font-size: 12px;
  3454. color: #111827;
  3455. }
  3456. .cookir-format-encode-pwd-container p{
  3457. color: #6b7280;
  3458. font-size: 12px;
  3459. }
  3460. `
  3461. )
  3462. });
  3463. let $exampleCodeText = $confirm.$shadowRoot.querySelector(
  3464. ".export-example-code-text-container pre"
  3465. );
  3466. let $format_header_string = $confirm.$shadowRoot.querySelector(
  3467. "#cookie-format-header_string"
  3468. );
  3469. let $format_json = $confirm.$shadowRoot.querySelector("#cookie-format-json");
  3470. let $encodePwd = $confirm.$shadowRoot.querySelector("#encode-pwd");
  3471. const DialogConfigManager = {
  3472. key: "cookie-backup-export-dialog-config",
  3473. getConfig() {
  3474. return Panel.getValue(this.key, {
  3475. exportType: "header_string",
  3476. encodePwd: ""
  3477. });
  3478. },
  3479. saveConfig() {
  3480. let exportType = "header_string";
  3481. if ($format_json.checked) {
  3482. exportType = "json";
  3483. }
  3484. Panel.setValue(this.key, {
  3485. exportType,
  3486. encodePwd: domUtils.val($encodePwd)
  3487. });
  3488. dialogConfig = this.getConfig();
  3489. }
  3490. };
  3491. let dialogConfig = DialogConfigManager.getConfig();
  3492. domUtils.on($format_header_string, "input", () => {
  3493. const exampleCooikieList = [
  3494. {
  3495. name: "_ga",
  3496. value: "GA1.2.123456789.987654321",
  3497. domain: window.location.hostname,
  3498. expires: Date.now() + 1e3 * 60 * 60 * 24 * 30,
  3499. partitioned: false,
  3500. path: "/",
  3501. sameSite: "unspecified",
  3502. secure: false
  3503. },
  3504. {
  3505. name: "PHPSESSID",
  3506. value: "28f2d88ee9322cfd2e4f1e",
  3507. domain: window.location.hostname,
  3508. expires: Date.now() + 1e3 * 60 * 60 * 24 * 30,
  3509. partitioned: false,
  3510. path: "/",
  3511. sameSite: "unspecified",
  3512. secure: false
  3513. },
  3514. {
  3515. name: "csrftoken",
  3516. value: "abcdef123456",
  3517. domain: window.location.hostname,
  3518. expires: Date.now() + 1e3 * 60 * 60 * 24 * 30,
  3519. partitioned: false,
  3520. path: "/",
  3521. sameSite: "unspecified",
  3522. secure: false
  3523. },
  3524. {
  3525. name: "logged_in",
  3526. value: "true",
  3527. domain: window.location.hostname,
  3528. expires: Date.now() + 1e3 * 60 * 60 * 24 * 30,
  3529. partitioned: false,
  3530. path: "/",
  3531. sameSite: "unspecified",
  3532. secure: false
  3533. }
  3534. ];
  3535. let exampleText = this.formatCookie(exampleCooikieList, "header_string");
  3536. domUtils.text($exampleCodeText, exampleText);
  3537. DialogConfigManager.saveConfig();
  3538. });
  3539. domUtils.on($format_json, "input", () => {
  3540. const exampleCooikieList = [
  3541. {
  3542. name: "sessionId",
  3543. value: "abc123xyz456",
  3544. domain: ".example.com",
  3545. path: "/",
  3546. secure: true,
  3547. httpOnly: true,
  3548. sameSite: "lax",
  3549. expirationDate: 1713543600,
  3550. hostOnly: false,
  3551. session: false
  3552. }
  3553. ];
  3554. let exampleText = this.formatCookie(exampleCooikieList, "json");
  3555. domUtils.text($exampleCodeText, exampleText);
  3556. DialogConfigManager.saveConfig();
  3557. });
  3558. domUtils.on($encodePwd, ["input", "propertychange"], () => {
  3559. DialogConfigManager.saveConfig();
  3560. });
  3561. if (dialogConfig.exportType === "header_string") {
  3562. $format_header_string.click();
  3563. } else if (dialogConfig.exportType === "json") {
  3564. $format_json.click();
  3565. }
  3566. domUtils.val($encodePwd, dialogConfig.encodePwd);
  3567. },
  3568. /**
  3569. * 显示导入弹窗
  3570. */
  3571. showImportDialog() {
  3572. let $confirm = __pops.confirm({
  3573. title: {
  3574. text: "导入 Cookie",
  3575. position: "center"
  3576. },
  3577. content: {
  3578. text: (
  3579. /*html*/
  3580. `
  3581. <p class="tip-text cookie-format-type-tip-text">您希望如何导入?</p>
  3582. <div class="import-cookie-type-container">
  3583. <div class="import-cookie-type-item">
  3584. <input id="import-cookie-import_from_text" type="radio" name="format" value="import_from_text">
  3585. <label for="import-cookie-import_from_text">Use text</label>
  3586. </div>
  3587. <div class="import-cookie-type-item">
  3588. <input id="import-cookie-import_from_file" type="radio" name="format" value="import_from_file">
  3589. <label for="import-cookie-import_from_file">Use a file</label>
  3590. </div>
  3591. </div>
  3592. <div class="import-cookie-value-container">
  3593. <div class="import-cookie-value-text">
  3594. <label>Cookie value</label>
  3595. <textarea rows="5" placeholder="Header string/JSON"></textarea>
  3596. </div>
  3597. <div class="import-cookie-value-file">
  3598. <label>选择要导入的文件</label>
  3599. <input accept=".txt, .json" type="file">
  3600. </div>
  3601. </div>
  3602. <div class="cookie-format-decode-pwd-container">
  3603. <label for="hostOnly">用于解密 Cookie 的密码</label>
  3604. <input id="decode-pwd" type="password" placeholder="用于解密 Cookie 的密码" value="">
  3605. <p>如果 Cookie 受加密保护,请输入解密密码(可选)。</p>
  3606. </div>
  3607. `
  3608. ),
  3609. html: true
  3610. },
  3611. width: window.innerWidth < 400 ? "88vw" : "400px",
  3612. height: "auto",
  3613. btn: {
  3614. ok: {
  3615. text: "导入",
  3616. async callback(eventDetails, event) {
  3617. try {
  3618. const decodePwd = dialogConfig.decodePwd;
  3619. let cookieListStr = dialogConfig.value;
  3620. if (decodePwd.trim() === "") {
  3621. } else {
  3622. cookieListStr = CookieBackUpManager.decrypt(cookieListStr, decodePwd);
  3623. }
  3624. const cookie = utils.toJSON(cookieListStr);
  3625. if (Array.isArray(cookie)) {
  3626. log.info(`使用${CookieManager.cookieManagerApiName}导入cookie数据`);
  3627. for (const cookieInfo of cookie) {
  3628. await CookieManager.updateCookie(cookieInfo);
  3629. }
  3630. } else if (typeof cookie === "object" && Object.keys(cookie).length && Array.isArray(cookie["data"])) {
  3631. const cookieManager2 = new CookieManagerService(cookie.api);
  3632. log.info(`使用${cookieManager2.cookieManagerApiName}导入cookie数据`);
  3633. for (const cookieInfo of cookie.data) {
  3634. await cookieManager2.updateCookie(cookieInfo);
  3635. }
  3636. } else if (typeof cookie === "object" && !Object.keys(cookie).length) {
  3637. let utilsCookieManager = new utils.GM_Cookie();
  3638. log.info(`使用${CookieManager.cookieManagerApiName}导入cookie数据`);
  3639. let cookieObj = utilsCookieManager.parseCookie(cookieListStr);
  3640. for (const cookieInfo of cookieObj) {
  3641. await CookieManager.updateCookie({
  3642. name: cookieInfo.key,
  3643. value: cookieInfo.value,
  3644. domain: window.location.hostname,
  3645. path: "/",
  3646. sameSite: "unspecified",
  3647. secure: false,
  3648. session: false,
  3649. hostOnly: true,
  3650. httpOnly: false
  3651. });
  3652. }
  3653. } else {
  3654. log.error(cookieListStr, cookie);
  3655. Qmsg.error("cookie格式不符合");
  3656. return;
  3657. }
  3658. eventDetails.close();
  3659. } catch (error) {
  3660. Qmsg.error(error.toString());
  3661. }
  3662. }
  3663. }
  3664. },
  3665. style: (
  3666. /*css*/
  3667. `
  3668. ${__pops.config.cssText.panelCSS}
  3669.  
  3670. .pops-content{
  3671. padding: 20px;
  3672. }
  3673. .import-cookie-type-container{
  3674. display: flex;
  3675. gap: 10px;
  3676. margin: 10px 0px;
  3677. align-items: center;
  3678. flex-wrap: wrap;
  3679. justify-content: space-between;
  3680. }
  3681. .import-cookie-type-item input[type="radio"]{
  3682. width: 1rem;
  3683. height: 1rem;
  3684. }
  3685. .export-example-code-text-container{
  3686. padding: 10px;
  3687. background-color: rgb(209 213 219 / 1);
  3688. border-radius: 10px;
  3689. width: 100%;
  3690. margin: 1rem 0px;
  3691. }
  3692. .export-example-code-text-container pre{
  3693. font-feature-settings: normal;
  3694. font-variation-settings: normal;
  3695. font-size: 1em;
  3696. margin: 0;
  3697. white-space: break-spaces;
  3698. }
  3699. .import-cookie-type-container label{
  3700. color: rgb(17 24 39 / 1);
  3701. }
  3702. .cookie-format-decode-pwd-container label{
  3703. color: #111827;
  3704. }
  3705. .import-cookie-value-text label,
  3706. .import-cookie-value-file label,
  3707. .cookie-format-type-tip-text,
  3708. .cookie-format-decode-pwd-container label{
  3709. font-weight: 600;
  3710. }
  3711. .cookie-format-decode-pwd-container input{
  3712. border-radius: 0.5rem;
  3713. width: 100%;
  3714. border: 1px solid #d1d5db;
  3715. background-color: #f9fafb;
  3716. padding: 0.625rem;
  3717. margin: 0.65rem 0px;
  3718. font-size: 12px;
  3719. color: #111827;
  3720. }
  3721. .cookie-format-decode-pwd-container p{
  3722. color: #6b7280;
  3723. font-size: 12px;
  3724. }
  3725.  
  3726. .import-cookie-value-text{
  3727. display: flex;
  3728. flex-direction: column;
  3729. }
  3730. .import-cookie-value-text label{
  3731.  
  3732. }
  3733. .import-cookie-value-text textarea{
  3734. font-size: 0.875rem;
  3735. line-height: 1.25rem;
  3736. padding: 0.625rem;
  3737. color: rgb(17 24 39 / 1);
  3738. background-color: rgb(249 250 251 / 1);
  3739. border: 1px solid rgb(209 213 219 / 1);
  3740. border-radius: 0.5rem;
  3741. width: 100%;
  3742. margin: 10px 0px;
  3743. }
  3744. .import-cookie-value-file{
  3745. display: flex;
  3746. flex-direction: column;
  3747. }
  3748. .import-cookie-value-file label{
  3749.  
  3750. }
  3751. .import-cookie-value-file input{
  3752. border: 1px solid #d1d5db;
  3753. border-radius: 0.5rem;
  3754. height: 2.25rem;
  3755. width: 100%;
  3756. margin: 1rem 0px;
  3757. }
  3758. .import-cookie-value-file input:hover,
  3759. .import-cookie-value-file input::file-selector-button:hover{
  3760. cursor: pointer;
  3761. }
  3762. .import-cookie-value-file input::file-selector-button{
  3763. background-color: #1E2939;
  3764. color: #ffffff;
  3765. height: 100%;
  3766. box-sizing: border-box;
  3767. }
  3768. .import-cookie-value-file input::file-selector-button:hover{
  3769. background-color: #364153;
  3770. }
  3771. `
  3772. )
  3773. });
  3774. let import_file_text = "";
  3775. let $import_cookie_from_text = $confirm.$shadowRoot.querySelector(
  3776. "#import-cookie-import_from_text"
  3777. );
  3778. let $import_cookie_from_file = $confirm.$shadowRoot.querySelector(
  3779. "#import-cookie-import_from_file"
  3780. );
  3781. $confirm.$shadowRoot.querySelector(
  3782. ".import-cookie-value-container"
  3783. );
  3784. let $importContainer_text = $confirm.$shadowRoot.querySelector(
  3785. ".import-cookie-value-text"
  3786. );
  3787. let $import_text = $importContainer_text.querySelector("textarea");
  3788. let $importContainer_file = $confirm.$shadowRoot.querySelector(
  3789. ".import-cookie-value-file"
  3790. );
  3791. let $import_file = $importContainer_file.querySelector("input");
  3792. let $decodePwd = $confirm.$shadowRoot.querySelector("#decode-pwd");
  3793. const DialogConfigManager = {
  3794. key: "cookie-backup-import-dialog-config",
  3795. getConfig() {
  3796. let config = Panel.getValue(this.key, {
  3797. importType: "import_from_text",
  3798. decodePwd: "",
  3799. value: ""
  3800. });
  3801. if (config.importType === "import_from_text") {
  3802. config.value = $import_text.value;
  3803. } else if (config.importType === "import_from_file") {
  3804. config.value = import_file_text;
  3805. }
  3806. return config;
  3807. },
  3808. saveConfig() {
  3809. let importType = "import_from_text";
  3810. if ($import_cookie_from_file.checked) {
  3811. importType = "import_from_file";
  3812. }
  3813. Panel.setValue(this.key, {
  3814. importType,
  3815. decodePwd: domUtils.val($decodePwd)
  3816. });
  3817. dialogConfig = this.getConfig();
  3818. }
  3819. };
  3820. let dialogConfig = DialogConfigManager.getConfig();
  3821. domUtils.on($import_cookie_from_text, "input", () => {
  3822. DialogConfigManager.saveConfig();
  3823. $import_file.value = "";
  3824. import_file_text = "";
  3825. domUtils.hide($importContainer_file, false);
  3826. domUtils.show($importContainer_text, false);
  3827. });
  3828. domUtils.on($import_cookie_from_file, "input", () => {
  3829. DialogConfigManager.saveConfig();
  3830. $import_text.value = "";
  3831. domUtils.hide($importContainer_text, false);
  3832. domUtils.show($importContainer_file, false);
  3833. });
  3834. domUtils.on(
  3835. $import_text,
  3836. ["input", "propertychange"],
  3837. utils.debounce(() => {
  3838. DialogConfigManager.saveConfig();
  3839. })
  3840. );
  3841. domUtils.on($import_file, ["change", "input"], (evt) => {
  3842. const file = $import_file.files?.[0];
  3843. if (file) {
  3844. const reader = new FileReader();
  3845. reader.onload = function(e) {
  3846. const content = e.target.result;
  3847. if (typeof content === "string") {
  3848. import_file_text = content;
  3849. DialogConfigManager.saveConfig();
  3850. }
  3851. };
  3852. reader.readAsText(file);
  3853. }
  3854. });
  3855. domUtils.on($decodePwd, ["input", "propertychange"], async (evt) => {
  3856. DialogConfigManager.saveConfig();
  3857. });
  3858. if (dialogConfig.importType === "import_from_text") {
  3859. $import_cookie_from_text.click();
  3860. } else if (dialogConfig.importType === "import_from_file") {
  3861. $import_cookie_from_file.click();
  3862. }
  3863. domUtils.val($decodePwd, dialogConfig.decodePwd);
  3864. }
  3865. };
  3866. const CookieManagerView = {
  3867. $data: {
  3868. /**
  3869. * 当前的cookie列表
  3870. */
  3871. cookieList: []
  3872. },
  3873. init() {
  3874. this.registerMenu();
  3875. },
  3876. /**
  3877. * 显示视图
  3878. */
  3879. async showView() {
  3880. const $alert = __pops.alert({
  3881. title: {
  3882. text: "Cookie编辑器",
  3883. html: false,
  3884. position: "center"
  3885. },
  3886. content: {
  3887. text: (
  3888. /*html*/
  3889. `
  3890. <div class="cookie-wrapper">
  3891. <div class="cookie-search-wrapper">
  3892. <div class="cookie-search-inner">
  3893. <input type="text" placeholder="搜索Cookie名称">
  3894. </div>
  3895. <div class="cookie-search-setting">
  3896. <svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4368" width="28" height="28">
  3897. <path fill="#2c2c2c" d="M439.264 208a16 16 0 0 0-16 16v67.968a239.744 239.744 0 0 0-46.496 26.896l-58.912-34a16 16 0 0 0-21.856 5.856l-80 138.56a16 16 0 0 0 5.856 21.856l58.896 34a242.624 242.624 0 0 0 0 53.728l-58.88 34a16 16 0 0 0-6.72 20.176l0.848 1.68 80 138.56a16 16 0 0 0 21.856 5.856l58.912-34a239.744 239.744 0 0 0 46.496 26.88V800a16 16 0 0 0 16 16h160a16 16 0 0 0 16-16v-67.968a239.744 239.744 0 0 0 46.512-26.896l58.912 34a16 16 0 0 0 21.856-5.856l80-138.56a16 16 0 0 0-4.288-20.832l-1.568-1.024-58.896-34a242.624 242.624 0 0 0 0-53.728l58.88-34a16 16 0 0 0 6.72-20.176l-0.848-1.68-80-138.56a16 16 0 0 0-21.856-5.856l-58.912 34a239.744 239.744 0 0 0-46.496-26.88V224a16 16 0 0 0-16-16h-160z m32 48h96v67.376l28.8 12.576c13.152 5.76 25.632 12.976 37.184 21.52l25.28 18.688 58.448-33.728 48 83.136-58.368 33.68 3.472 31.2a194.624 194.624 0 0 1 0 43.104l-3.472 31.2 58.368 33.68-48 83.136-58.432-33.728-25.296 18.688c-11.552 8.544-24.032 15.76-37.184 21.52l-28.8 12.576V768h-96v-67.376l-28.784-12.576c-13.152-5.76-25.632-12.976-37.184-21.52l-25.28-18.688-58.448 33.728-48-83.136 58.368-33.68-3.472-31.2a194.624 194.624 0 0 1 0-43.104l3.472-31.2-58.368-33.68 48-83.136 58.432 33.728 25.296-18.688a191.744 191.744 0 0 1 37.184-21.52l28.8-12.576V256z m47.28 144a112 112 0 1 0 0 224 112 112 0 0 0 0-224z m0 48a64 64 0 1 1 0 128 64 64 0 0 1 0-128z"></path>
  3898. </svg>
  3899. </div>
  3900. </div>
  3901. <div class="cookie-control-wrapper">
  3902. <button class="cookie-control-refresh" type="button" data-type="default">刷新</button>
  3903. <button class="cookie-control-add" type="button" data-type="default">添加</button>
  3904. <button class="cookie-control-export" type="button" data-type="default">导出</button>
  3905. <button class="cookie-control-import" type="button" data-type="default">导入</button>
  3906. <button class="cookie-control-clear-all" type="button" data-type="default">删除</button>
  3907. <button class="cookie-control-rule-manager" type="button" data-type="default">规则管理</button>
  3908. <div class="cookie-setting">
  3909. <svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4368" width="28" height="28">
  3910. <path fill="#2c2c2c" d="M439.264 208a16 16 0 0 0-16 16v67.968a239.744 239.744 0 0 0-46.496 26.896l-58.912-34a16 16 0 0 0-21.856 5.856l-80 138.56a16 16 0 0 0 5.856 21.856l58.896 34a242.624 242.624 0 0 0 0 53.728l-58.88 34a16 16 0 0 0-6.72 20.176l0.848 1.68 80 138.56a16 16 0 0 0 21.856 5.856l58.912-34a239.744 239.744 0 0 0 46.496 26.88V800a16 16 0 0 0 16 16h160a16 16 0 0 0 16-16v-67.968a239.744 239.744 0 0 0 46.512-26.896l58.912 34a16 16 0 0 0 21.856-5.856l80-138.56a16 16 0 0 0-4.288-20.832l-1.568-1.024-58.896-34a242.624 242.624 0 0 0 0-53.728l58.88-34a16 16 0 0 0 6.72-20.176l-0.848-1.68-80-138.56a16 16 0 0 0-21.856-5.856l-58.912 34a239.744 239.744 0 0 0-46.496-26.88V224a16 16 0 0 0-16-16h-160z m32 48h96v67.376l28.8 12.576c13.152 5.76 25.632 12.976 37.184 21.52l25.28 18.688 58.448-33.728 48 83.136-58.368 33.68 3.472 31.2a194.624 194.624 0 0 1 0 43.104l-3.472 31.2 58.368 33.68-48 83.136-58.432-33.728-25.296 18.688c-11.552 8.544-24.032 15.76-37.184 21.52l-28.8 12.576V768h-96v-67.376l-28.784-12.576c-13.152-5.76-25.632-12.976-37.184-21.52l-25.28-18.688-58.448 33.728-48-83.136 58.368-33.68-3.472-31.2a194.624 194.624 0 0 1 0-43.104l3.472-31.2-58.368-33.68 48-83.136 58.432 33.728 25.296-18.688a191.744 191.744 0 0 1 37.184-21.52l28.8-12.576V256z m47.28 144a112 112 0 1 0 0 224 112 112 0 0 0 0-224z m0 48a64 64 0 1 1 0 128 64 64 0 0 1 0-128z"></path>
  3911. </svg>
  3912. </div>
  3913. </div>
  3914. <div class="cookie-list-wrapper">
  3915. </div>
  3916. </div>
  3917. `
  3918. ),
  3919. html: true
  3920. },
  3921. btn: {
  3922. ok: {
  3923. enable: false
  3924. }
  3925. },
  3926. mask: {
  3927. enable: true
  3928. },
  3929. drag: true,
  3930. width: PanelUISize.setting.width,
  3931. height: PanelUISize.setting.height,
  3932. style: (
  3933. /*css*/
  3934. `
  3935. ${__pops.config.cssText.panelCSS}
  3936. .cookie-wrapper{
  3937. display: flex;
  3938. flex-direction: column;
  3939. padding: 10px;
  3940. gap: 10px;
  3941. }
  3942. .cookie-control-wrapper{
  3943. display: flex;
  3944. flex-wrap: wrap;
  3945. padding: 0px 10px;
  3946. gap: 5px;
  3947. --button-margin-left: 0px;
  3948. }
  3949. .cookie-search-wrapper{
  3950. display: flex;
  3951. align-items: center;
  3952. }
  3953. .cookie-search-inner{
  3954. width: 100%;
  3955. padding: 0px 10px;
  3956. }
  3957. .cookie-search-inner input{
  3958. height: 30px;
  3959. padding: 5px 8px;
  3960. width: 100%;
  3961. border-radius: 6px;
  3962. }
  3963. .cookie-search-inner input::placeholder{
  3964. display: flex;
  3965. align-items: baseline;
  3966. }
  3967. .cookie-search-inner input:focus-visible{
  3968. outline: none;
  3969. }
  3970. .cookie-setting,
  3971. .cookie-search-setting{
  3972. display: flex;
  3973. align-items: center;
  3974. }
  3975. .cookie-setting svg,
  3976. .cookie-search-setting svg{
  3977. cursor: pointer;
  3978. }
  3979. .cookie-list-wrapper{
  3980. display: flex;
  3981. flex-wrap: wrap;
  3982. gap: 10px;
  3983. }
  3984. .cookie-item{
  3985. display: flex;
  3986. flex-direction: column;
  3987. padding: 10px 10px;
  3988. margin: 0px 10px;
  3989. background: #f1efef;
  3990. border-radius: 10px;
  3991. gap: 5px;
  3992. box-sizing: border-box;
  3993. width: 100%;
  3994. }
  3995. .cookie-item-group{
  3996. display: flex;
  3997. align-items: center;
  3998. }
  3999. .cookie-item-group-left{
  4000. width: 100px;
  4001. min-width: 100px;
  4002. max-width: 100px;
  4003. text-transform: capitalize
  4004. }
  4005. .cookie-item-group-control .cookie-item-group-right{
  4006. display: flex;
  4007. align-items: center;
  4008. gap: 10px;
  4009. }
  4010. .cookie-item-group-control .cookie-item-group-control-copy,
  4011. .cookie-item-group-control .cookie-item-group-control-edit,
  4012. .cookie-item-group-control .cookie-item-group-control-delete{
  4013. display: flex;
  4014. align-items: center;
  4015. }
  4016. .cookie-item-group-control .cookie-item-group-control-delete svg{
  4017. width: 16px;
  4018. height: 16px;
  4019. }
  4020. .cookie-item-group-control svg{
  4021. cursor: pointer;
  4022. }
  4023. `
  4024. )
  4025. });
  4026. const $search = $alert.$shadowRoot.querySelector(".cookie-search-inner input");
  4027. const $searchSetting = $alert.$shadowRoot.querySelector(".cookie-search-setting");
  4028. const $refresh = $alert.$shadowRoot.querySelector(".cookie-control-refresh");
  4029. const $add = $alert.$shadowRoot.querySelector(".cookie-control-add");
  4030. const $export = $alert.$shadowRoot.querySelector(".cookie-control-export");
  4031. const $import = $alert.$shadowRoot.querySelector(".cookie-control-import");
  4032. const $clearAll = $alert.$shadowRoot.querySelector(".cookie-control-clear-all");
  4033. const $ruleManager = $alert.$shadowRoot.querySelector(".cookie-control-rule-manager");
  4034. const $setting = $alert.$shadowRoot.querySelector(".cookie-setting");
  4035. const $cookieListWrapper = $alert.$shadowRoot.querySelector(".cookie-list-wrapper");
  4036. let createCookieItemElement = (cookieInfo) => {
  4037. const $cookieItem = domUtils.createElement("div", {
  4038. className: "cookie-item",
  4039. innerHTML: (
  4040. /*html*/
  4041. `
  4042. `
  4043. ),
  4044. "data-cookie-info": cookieInfo
  4045. });
  4046. const cookieProperty = [
  4047. {
  4048. leftText: "name",
  4049. rightText: cookieInfo.name
  4050. },
  4051. {
  4052. leftText: "value",
  4053. // 解码值
  4054. rightText: Panel.getValue("decode-cookie-value") ? decodeURIComponent(cookieInfo.value) : encodeURIComponent(cookieInfo.value)
  4055. }
  4056. ];
  4057. if (CookieManager.cookieManagerApiName === "GM_cookie" || CookieManager.cookieManagerApiName === "GM.cookie") {
  4058. cookieInfo = cookieInfo;
  4059. cookieProperty.push(
  4060. {
  4061. leftText: "domain",
  4062. rightText: cookieInfo.domain
  4063. },
  4064. {
  4065. leftText: "path",
  4066. rightText: cookieInfo.path
  4067. },
  4068. {
  4069. leftText: "session",
  4070. rightText: JSON.stringify(cookieInfo.session)
  4071. },
  4072. {
  4073. leftText: "expires",
  4074. rightText: cookieInfo.session ? "会话" : cookieInfo.expirationDate ? new Date(cookieInfo.expirationDate * 1e3).toISOString() : "未知"
  4075. },
  4076. {
  4077. leftText: "httpOnly",
  4078. rightText: JSON.stringify(cookieInfo.httpOnly)
  4079. },
  4080. {
  4081. leftText: "hostOnly",
  4082. rightText: JSON.stringify(cookieInfo.hostOnly)
  4083. },
  4084. {
  4085. leftText: "secure",
  4086. rightText: JSON.stringify(cookieInfo.secure)
  4087. },
  4088. {
  4089. leftText: "sameSite",
  4090. rightText: cookieInfo.sameSite
  4091. }
  4092. );
  4093. } else if (CookieManager.cookieManagerApiName === "cookieStore") {
  4094. cookieInfo = cookieInfo;
  4095. cookieProperty.push(
  4096. {
  4097. leftText: "domain",
  4098. rightText: cookieInfo.domain
  4099. },
  4100. {
  4101. leftText: "path",
  4102. rightText: cookieInfo.path
  4103. },
  4104. {
  4105. leftText: "expires",
  4106. rightText: cookieInfo.expires ? new Date(cookieInfo.expires).toISOString() : "会话"
  4107. },
  4108. {
  4109. leftText: "secure",
  4110. rightText: JSON.stringify(cookieInfo.secure)
  4111. },
  4112. {
  4113. leftText: "sameSite",
  4114. rightText: cookieInfo.sameSite
  4115. }
  4116. );
  4117. }
  4118. cookieProperty.forEach((it) => {
  4119. const $cookieItemGroup = domUtils.createElement("div", {
  4120. className: "cookie-item-group",
  4121. innerHTML: (
  4122. /*html*/
  4123. `
  4124. <div class="cookie-item-group-left">
  4125. <p>${it.leftText}</p>
  4126. </div>
  4127. <div class="cookie-item-group-right">
  4128. <p>${it.rightText}</p>
  4129. </div>
  4130. `
  4131. )
  4132. });
  4133. domUtils.append($cookieItem, $cookieItemGroup);
  4134. });
  4135. let $cookieItemGroupControl = domUtils.createElement("div", {
  4136. className: "cookie-item-group cookie-item-group-control",
  4137. innerHTML: (
  4138. /*html*/
  4139. `
  4140. <div class="cookie-item-group-left">操作</div>
  4141. <div class="cookie-item-group-right">
  4142. <div class="cookie-item-group-control-copy">
  4143. <svg t="1742795616339" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16">
  4144. <path d="M880 247.008l-162.016-166.016Q700.992 64 677.984 64h-316.992q-26.016 0-46.016 18.016-16.992 15.008-23.008 36.992H231.968q-43.008 0-73.504 31.008t-30.496 76v627.008q0 44 30.496 75.488T231.968 960h508q43.008 0 73.504-31.488t30.496-75.488v-63.008q23.008-6.016 37.504-25.504t14.496-44.512V287.008q0-24-16-40z m-168-160.992l-3.008-3.008z m98.016 177.984L744 196z m-126.016-116.992l108 110.016h-108V147.008zM676.992 128zM204.992 948q4 0.992 4.992 2.016-2.016-0.992-4.992-2.016z m27.008 4q-6.016 0-12-0.992 4.992 0.992 12 0.992z m543.008-99.008q0 15.008-10.016 25.504t-24.992 10.496H232q-14.016 0-24.512-10.496t-10.496-25.504V225.984q0-15.008 10.496-25.504t24.512-10.496h58.016v531.008q0 30.016 20.992 51.008t50.016 20.992H775.04v60z m52-132.992q0 2.016-2.016 2.016h-464q-2.016 0-2.016-2.016V136.992q0-2.016 2.016-2.016h251.008v156.992q0 15.008 10.016 24.992t24 10.016h180.992v392.992z m9.984 64q4-0.992 8.992-2.016-4.992 0.992-8.992 2.016z m-244-168.992h-107.008q-15.008 0-24.992 10.496t-10.016 24.992 10.016 24.992 24.992 10.496h107.008q14.016 0 24.512-10.496t10.496-24.992-10.496-24.992-24.512-10.496z m107.008-111.008h-214.016q-14.016 0-24.512 10.496t-10.496 24.992 10.496 24.992 24.512 10.496h214.016q14.016 0 24-10.496t10.016-24.992-10.016-24.992-24-10.496z m-240.992 36q0 4 0.992 8-0.992-4-0.992-8zM700 512z m12 52l4-2.016z m-260.992-135.488q0 14.496 10.496 24.992t24.512 10.496h214.016q14.016 0 24-10.496t10.016-24.992-10.016-24.992-24-10.496h-214.016q-14.016 0-24.512 10.496t-10.496 24.992z m8 1.504z"></path>
  4145. </svg>
  4146. </div>
  4147. <div class="cookie-item-group-control-edit">
  4148. <svg t="1742795710451" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16">
  4149. <path d="M800 960 224 960c-52.928 0-96-43.072-96-96L128 224c0-52.928 43.072-96 96-96l448 0c17.696 0 32 14.336 32 32s-14.304 32-32 32L224 192C206.368 192 192 206.368 192 224l0 640c0 17.664 14.368 32 32 32l576 0c17.664 0 32-14.336 32-32L832 352c0-17.664 14.304-32 32-32s32 14.336 32 32l0 512C896 916.928 852.928 960 800 960zM612 448c-8.192 0-16.384-3.136-22.624-9.376-12.512-12.512-12.512-32.736 0-45.248l318.016-318.016c12.512-12.512 32.736-12.512 45.248 0s12.512 32.736 0 45.248l-318.016 318.016C628.384 444.896 620.192 448 612 448zM480 448 288 448c-17.664 0-32-14.336-32-32s14.336-32 32-32l192 0c17.664 0 32 14.336 32 32S497.664 448 480 448zM672 640 288 640c-17.664 0-32-14.304-32-32s14.336-32 32-32l384 0c17.696 0 32 14.304 32 32S689.696 640 672 640z"></path>
  4150. </svg>
  4151. </div>
  4152. <div class="cookie-item-group-control-delete">
  4153. ${__pops.config.iconSVG.delete}
  4154. </div>
  4155. </div>
  4156. `
  4157. )
  4158. });
  4159. let $cookieItemCopy = $cookieItemGroupControl.querySelector(
  4160. ".cookie-item-group-control-copy"
  4161. );
  4162. let $cookieItemEdit = $cookieItemGroupControl.querySelector(
  4163. ".cookie-item-group-control-edit"
  4164. );
  4165. let $cookieItemDelete = $cookieItemGroupControl.querySelector(
  4166. ".cookie-item-group-control-delete"
  4167. );
  4168. domUtils.on($cookieItemCopy, "click", (event) => {
  4169. utils.preventEvent(event);
  4170. let cookieText = cookieInfo.value;
  4171. utils.setClip(cookieText).then((status) => {
  4172. if (status) {
  4173. Qmsg.success("复制成功");
  4174. } else {
  4175. Qmsg.error("复制失败");
  4176. }
  4177. });
  4178. });
  4179. domUtils.on($cookieItemEdit, "click", (event) => {
  4180. utils.preventEvent(event);
  4181. CookieManagerEditView.showView(cookieInfo, (__cookieInfo__) => {
  4182. let $newCookieItem = createCookieItemElement(__cookieInfo__);
  4183. domUtils.after($cookieItem, $newCookieItem);
  4184. $cookieItem.parentElement?.removeChild($cookieItem);
  4185. });
  4186. });
  4187. domUtils.on($cookieItemDelete, "click", (event) => {
  4188. utils.preventEvent(event);
  4189. let result = confirm("确定删除该Cookie?");
  4190. if (!result) {
  4191. return;
  4192. }
  4193. CookieManager.deleteCookie(cookieInfo).then((status) => {
  4194. if (!status) {
  4195. Qmsg.success("删除成功");
  4196. $cookieItem.parentElement?.removeChild($cookieItem);
  4197. } else {
  4198. log.error(status);
  4199. Qmsg.error("删除失败");
  4200. }
  4201. });
  4202. });
  4203. domUtils.append($cookieItem, [$cookieItemGroupControl]);
  4204. return $cookieItem;
  4205. };
  4206. let updateCookieListGroup = async (filterCallBack) => {
  4207. let cookieList = await CookieManager.queryAllCookie();
  4208. domUtils.empty($cookieListWrapper);
  4209. let $fragment = document.createDocumentFragment();
  4210. let excludeSessionCookie = Panel.getValue("exclude-session-cookie");
  4211. cookieList.forEach((cookieInfo) => {
  4212. if (excludeSessionCookie) {
  4213. if (cookieInfo.session) {
  4214. return;
  4215. }
  4216. if (CookieManager.cookieManagerApiName === "cookieStore" && cookieInfo.expires == null) {
  4217. return;
  4218. }
  4219. }
  4220. if (typeof filterCallBack === "function") {
  4221. let filterResult = filterCallBack(cookieInfo);
  4222. if (!filterResult) {
  4223. return;
  4224. }
  4225. }
  4226. const $cookieItem = createCookieItemElement(cookieInfo);
  4227. $fragment.appendChild($cookieItem);
  4228. });
  4229. this.$data.cookieList = cookieList;
  4230. $cookieListWrapper.appendChild($fragment);
  4231. };
  4232. domUtils.on(
  4233. $search,
  4234. ["input", "propertychange"],
  4235. utils.debounce((event) => {
  4236. let searchText = domUtils.val($search);
  4237. let isNotFilter = searchText.trim() === "";
  4238. let enableRegExp = Panel.getValue("search-config-use-regexp");
  4239. updateCookieListGroup((cookieItem) => {
  4240. if (isNotFilter) {
  4241. return true;
  4242. }
  4243. return enableRegExp ? Boolean(cookieItem.name.match(new RegExp(searchText))) : cookieItem.name.includes(searchText);
  4244. });
  4245. })
  4246. );
  4247. domUtils.listenKeyboard($search, "keypress", (keyName, keyValue, otherCodeList) => {
  4248. if (keyName === "Enter" && otherCodeList.length === 0) {
  4249. triggerUpdateCookieListGroupWithSearchFilter();
  4250. }
  4251. });
  4252. domUtils.on($searchSetting, "click", (event) => {
  4253. utils.preventEvent(event);
  4254. let $settingAlert = __pops.alert({
  4255. title: {
  4256. text: "搜索配置",
  4257. position: "center"
  4258. },
  4259. content: {
  4260. text: "",
  4261. html: true
  4262. },
  4263. btn: {
  4264. ok: {
  4265. enable: false
  4266. }
  4267. },
  4268. drag: true,
  4269. mask: {
  4270. clickEvent: {
  4271. toClose: true
  4272. }
  4273. },
  4274. width: PanelUISize.info.width,
  4275. height: PanelUISize.info.height,
  4276. style: (
  4277. /*css*/
  4278. `
  4279. ${__pops.config.cssText.panelCSS}
  4280.  
  4281. .pops-alert-content li{
  4282. display: flex;
  4283. justify-content: space-between;
  4284. align-items: center;
  4285. padding: 10px;
  4286. }
  4287. .pops-panel-item-left-desc-text{
  4288. line-height: normal;
  4289. margin-top: 6px;
  4290. font-size: 0.8em;
  4291. color: rgb(108, 108, 108);
  4292. }
  4293. `
  4294. )
  4295. });
  4296. let $content = $settingAlert.$shadowRoot.querySelector(".pops-alert-content");
  4297. let panelHandlerComponents = __pops.config.PanelHandlerComponents();
  4298. let $useRegExp = panelHandlerComponents.createSectionContainerItem_switch(
  4299. UISwitch(
  4300. "启用正则表达式",
  4301. "search-config-use-regexp",
  4302. false,
  4303. void 0,
  4304. "使用正则表达式搜索Cookie名称",
  4305. () => {
  4306. triggerUpdateCookieListGroupWithSearchFilter();
  4307. }
  4308. )
  4309. );
  4310. domUtils.append($content, $useRegExp);
  4311. });
  4312. domUtils.on($refresh, "click", (event) => {
  4313. utils.preventEvent(event);
  4314. triggerUpdateCookieListGroupWithSearchFilter();
  4315. });
  4316. domUtils.on($add, "click", (event) => {
  4317. utils.preventEvent(event);
  4318. CookieManagerEditView.showView(void 0, (__cookieInfo__) => {
  4319. triggerUpdateCookieListGroupWithSearchFilter();
  4320. });
  4321. });
  4322. domUtils.on($export, "click", async (event) => {
  4323. utils.preventEvent(event);
  4324. CookieBackUpManager.showExportDialog();
  4325. });
  4326. domUtils.on($import, "click", async (event) => {
  4327. utils.preventEvent(event);
  4328. CookieBackUpManager.showImportDialog();
  4329. });
  4330. domUtils.on($clearAll, "click", async (event) => {
  4331. utils.preventEvent(event);
  4332. let result = window.confirm("确定清除全部Cookie?");
  4333. if (!result) {
  4334. return;
  4335. }
  4336. const deleteInfo = await CookieManager.deleteAllCookie();
  4337. if (deleteInfo.error) {
  4338. Qmsg.warning(`清除成功:${deleteInfo.success} 失败:${deleteInfo.error}`);
  4339. } else {
  4340. Qmsg.success("清除成功");
  4341. }
  4342. triggerUpdateCookieListGroupWithSearchFilter();
  4343. });
  4344. domUtils.on($ruleManager, "click", (event) => {
  4345. utils.preventEvent(event);
  4346. CookieRule.showView();
  4347. });
  4348. domUtils.on($setting, "click", (event) => {
  4349. utils.preventEvent(event);
  4350. let $settingAlert = __pops.alert({
  4351. title: {
  4352. text: "设置",
  4353. position: "center"
  4354. },
  4355. content: {
  4356. text: "",
  4357. html: true
  4358. },
  4359. btn: {
  4360. ok: {
  4361. enable: false
  4362. }
  4363. },
  4364. drag: true,
  4365. mask: {
  4366. clickEvent: {
  4367. toClose: true
  4368. }
  4369. },
  4370. width: PanelUISize.settingMiddle.width,
  4371. height: PanelUISize.settingMiddle.height,
  4372. style: (
  4373. /*css*/
  4374. `
  4375. ${__pops.config.cssText.panelCSS}
  4376.  
  4377. .pops-alert-content li{
  4378. display: flex;
  4379. justify-content: space-between;
  4380. align-items: center;
  4381. padding: 10px;
  4382. }
  4383. .pops-panel-item-left-desc-text{
  4384. line-height: normal;
  4385. margin-top: 6px;
  4386. font-size: 0.8em;
  4387. color: rgb(108, 108, 108);
  4388. }
  4389. `
  4390. )
  4391. });
  4392. let $content = $settingAlert.$shadowRoot.querySelector(".pops-alert-content");
  4393. let panelHandlerComponents = __pops.config.PanelHandlerComponents();
  4394. let $useGM_cookie = panelHandlerComponents.createSectionContainerItem_select(
  4395. UISelect(
  4396. "CookieManager Api",
  4397. "cookie-manager-api",
  4398. "document.cookie",
  4399. CookieManagerApiNameList.map((it) => {
  4400. return {
  4401. text: it,
  4402. value: it
  4403. };
  4404. }),
  4405. void 0,
  4406. "操作Cookie的Api函数",
  4407. (event2) => {
  4408. triggerUpdateCookieListGroupWithSearchFilter();
  4409. }
  4410. )
  4411. );
  4412. let $decodeValue = panelHandlerComponents.createSectionContainerItem_switch(
  4413. UISwitch(
  4414. "解码Cookie值",
  4415. "decode-cookie-value",
  4416. false,
  4417. () => {
  4418. triggerUpdateCookieListGroupWithSearchFilter();
  4419. },
  4420. "对Cookie值进行解码"
  4421. )
  4422. );
  4423. let $excludeSessionCookie = panelHandlerComponents.createSectionContainerItem_switch(
  4424. UISwitch(
  4425. "排除Session Cookie",
  4426. "exclude-session-cookie",
  4427. false,
  4428. () => {
  4429. triggerUpdateCookieListGroupWithSearchFilter();
  4430. },
  4431. "过滤掉浏览器会话Cookie"
  4432. )
  4433. );
  4434. domUtils.append($content, [$useGM_cookie, $decodeValue, $excludeSessionCookie]);
  4435. });
  4436. let triggerUpdateCookieListGroupWithSearchFilter = () => {
  4437. domUtils.trigger($search, "input");
  4438. };
  4439. triggerUpdateCookieListGroupWithSearchFilter();
  4440. },
  4441. /**
  4442. * 注册(不可用)脚本菜单
  4443. */
  4444. registerMenu() {
  4445. const that = this;
  4446. GM_Menu.add({
  4447. key: "cookie_manager_view",
  4448. text: "⚙ Cookie管理",
  4449. autoReload: false,
  4450. isStoreValue: false,
  4451. showText(text, enable) {
  4452. return text;
  4453. },
  4454. callback(data) {
  4455. that.showView();
  4456. }
  4457. });
  4458. }
  4459. };
  4460. const CookieRuleController = {
  4461. init() {
  4462. this.execController();
  4463. domUtils.ready(() => {
  4464. this.execController();
  4465. });
  4466. },
  4467. /**
  4468. * 执行cookie规则处理操作
  4469. */
  4470. async execController() {
  4471. for (let index = 0; index < CookieRule.$data.matchedRuleList.length; index++) {
  4472. const cookieRuleItem = CookieRule.$data.matchedRuleList[index];
  4473. const operationMode = cookieRuleItem.data.operationMode;
  4474. log.success(`执行规则:${cookieRuleItem.name}`);
  4475. let apiName = cookieRuleItem.data.execApiName;
  4476. if (apiName === "use-global") {
  4477. apiName = void 0;
  4478. }
  4479. const cookieManager2 = new CookieManagerService(apiName);
  4480. const cookieListResult = await cookieManager2.queryAllCookie();
  4481. for (let cookieInfoIndex = 0; cookieInfoIndex < cookieListResult.length; cookieInfoIndex++) {
  4482. let cookieInfo = cookieListResult[cookieInfoIndex];
  4483. const cookieName = cookieInfo.name;
  4484. const ruleCookieName = cookieRuleItem.data.cookieName;
  4485. let flag = false;
  4486. if (cookieRuleItem.data.enableRegExpToMatchCookieName) {
  4487. let regExpCookieName = new RegExp(ruleCookieName, "i");
  4488. if (regExpCookieName.test(cookieName)) {
  4489. flag = true;
  4490. }
  4491. } else {
  4492. if (cookieName.includes(ruleCookieName)) {
  4493. flag = true;
  4494. }
  4495. }
  4496. if (flag) {
  4497. if (operationMode === "delete") {
  4498. cookieManager2.deleteCookie(cookieInfo);
  4499. } else if (operationMode.startsWith("extended")) {
  4500. let currentTime = Date.now();
  4501. let oneMonth = 30 * 24 * 60 * 60 * 1e3;
  4502. let threeMonth = oneMonth * 3;
  4503. let halfAYear = oneMonth * 6;
  4504. let oneYear = oneMonth * 12;
  4505. let checkTime = oneMonth;
  4506. if (operationMode === "extended-90") {
  4507. checkTime = threeMonth;
  4508. } else if (operationMode === "extended-180") {
  4509. checkTime = halfAYear;
  4510. } else if (operationMode === "extended-360") {
  4511. checkTime = oneYear;
  4512. }
  4513. let updateFlag = false;
  4514. if (cookieManager2.cookieManagerApiName === "document.cookie") {
  4515. cookieInfo.expirationDate = currentTime + checkTime;
  4516. updateFlag = true;
  4517. } else if (cookieManager2.cookieManagerApiName === "cookieStore") {
  4518. let expireTime = cookieInfo.expires;
  4519. if (typeof expireTime === "number" && expireTime - currentTime < checkTime) {
  4520. cookieInfo.expires = expireTime + checkTime;
  4521. updateFlag = true;
  4522. }
  4523. } else if (cookieManager2.cookieManagerApiName === "GM_cookie" || cookieManager2.cookieManagerApiName === "GM.cookie") {
  4524. let expireTime = cookieInfo.expirationDate;
  4525. if (typeof expireTime === "number" && expireTime * 1e3 - currentTime < checkTime) {
  4526. cookieInfo.expirationDate = expireTime + checkTime / 1e3;
  4527. updateFlag = true;
  4528. }
  4529. } else {
  4530. log.error(
  4531. "未知的cookieManagerApiName",
  4532. cookieManager2.cookieManagerApiName
  4533. );
  4534. }
  4535. if (updateFlag) {
  4536. await cookieManager2.updateCookie(cookieInfo);
  4537. }
  4538. }
  4539. }
  4540. }
  4541. }
  4542. }
  4543. };
  4544. const UIButton = function(text, description, buttonText, buttonIcon, buttonIsRightIcon, buttonIconIsLoading, buttonType, clickCallBack, afterAddToUListCallBack, disable) {
  4545. let result = {
  4546. text,
  4547. type: "button",
  4548. attributes: {},
  4549. props: {},
  4550. description,
  4551. buttonIcon,
  4552. buttonIsRightIcon,
  4553. buttonIconIsLoading,
  4554. buttonType,
  4555. buttonText,
  4556. callback(event) {
  4557. if (typeof clickCallBack === "function") {
  4558. clickCallBack(event);
  4559. }
  4560. },
  4561. afterAddToUListCallBack
  4562. };
  4563. Reflect.set(result.attributes, ATTRIBUTE_INIT, () => {
  4564. result.disable = Boolean(
  4565. disable
  4566. );
  4567. });
  4568. return result;
  4569. };
  4570. const Component_Rule = {
  4571. id: "view-rule",
  4572. title: "规则",
  4573. headerTitle: "Cookie操作规则",
  4574. forms: [
  4575. {
  4576. type: "forms",
  4577. text: "",
  4578. forms: [
  4579. UIButton(
  4580. "自定义规则",
  4581. "操作Cookie的规则",
  4582. "管理",
  4583. void 0,
  4584. false,
  4585. false,
  4586. "default",
  4587. () => {
  4588. CookieRule.showView();
  4589. }
  4590. )
  4591. ]
  4592. },
  4593. {
  4594. type: "forms",
  4595. text: "",
  4596. forms: [
  4597. UIButton(
  4598. "数据导入",
  4599. "导入自定义规则数据",
  4600. "导入",
  4601. void 0,
  4602. false,
  4603. false,
  4604. "primary",
  4605. () => {
  4606. CookieRule.importRule();
  4607. }
  4608. ),
  4609. UIButton(
  4610. "数据导出",
  4611. "导出自定义规则数据",
  4612. "导出",
  4613. void 0,
  4614. false,
  4615. false,
  4616. "primary",
  4617. () => {
  4618. CookieRule.exportRule("CookieManagerRule.json");
  4619. }
  4620. )
  4621. ]
  4622. }
  4623. ]
  4624. };
  4625. const Component_Common = {
  4626. id: "view-general",
  4627. title: "通用",
  4628. forms: [
  4629. {
  4630. text: "Toast配置",
  4631. type: "forms",
  4632. forms: [
  4633. UISelect(
  4634. "Toast位置",
  4635. PanelSettingConfig.qmsg_config_position.key,
  4636. PanelSettingConfig.qmsg_config_position.defaultValue,
  4637. [
  4638. {
  4639. value: "topleft",
  4640. text: "左上角"
  4641. },
  4642. {
  4643. value: "top",
  4644. text: "顶部"
  4645. },
  4646. {
  4647. value: "topright",
  4648. text: "右上角"
  4649. },
  4650. {
  4651. value: "left",
  4652. text: "左边"
  4653. },
  4654. {
  4655. value: "center",
  4656. text: "中间"
  4657. },
  4658. {
  4659. value: "right",
  4660. text: "右边"
  4661. },
  4662. {
  4663. value: "bottomleft",
  4664. text: "左下角"
  4665. },
  4666. {
  4667. value: "bottom",
  4668. text: "底部"
  4669. },
  4670. {
  4671. value: "bottomright",
  4672. text: "右下角"
  4673. }
  4674. ],
  4675. (event, isSelectValue, isSelectText) => {
  4676. log.info("设置当前Qmsg弹出位置" + isSelectText);
  4677. },
  4678. "Toast显示在页面九宫格的位置"
  4679. ),
  4680. UISelect(
  4681. "最多显示的数量",
  4682. PanelSettingConfig.qmsg_config_maxnums.key,
  4683. PanelSettingConfig.qmsg_config_maxnums.defaultValue,
  4684. [
  4685. {
  4686. value: 1,
  4687. text: "1"
  4688. },
  4689. {
  4690. value: 2,
  4691. text: "2"
  4692. },
  4693. {
  4694. value: 3,
  4695. text: "3"
  4696. },
  4697. {
  4698. value: 4,
  4699. text: "4"
  4700. },
  4701. {
  4702. value: 5,
  4703. text: "5"
  4704. }
  4705. ],
  4706. void 0,
  4707. "限制Toast显示的数量"
  4708. ),
  4709. UISwitch(
  4710. "逆序弹出",
  4711. PanelSettingConfig.qmsg_config_showreverse.key,
  4712. PanelSettingConfig.qmsg_config_showreverse.defaultValue,
  4713. void 0,
  4714. "修改Toast弹出的顺序"
  4715. )
  4716. ]
  4717. }
  4718. ]
  4719. };
  4720. PanelContent.addContentConfig([Component_Common, Component_Rule]);
  4721. Panel.init();
  4722. CookieManagerView.init();
  4723. CookieRule.init();
  4724. CookieRuleController.init();
  4725.  
  4726. })(Qmsg, DOMUtils, Utils, pops, CryptoJS);

QingJ © 2025

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