1
This commit is contained in:
736
js/function.js
Normal file
736
js/function.js
Normal file
@@ -0,0 +1,736 @@
|
||||
/**
|
||||
* 小于10的数字前面加0
|
||||
* @param {Number} date
|
||||
* @returns {String|Number}
|
||||
*/
|
||||
function appendZero(date) {
|
||||
return parseInt(date) < 10 ? `0${date}` : date;
|
||||
}
|
||||
|
||||
/**
|
||||
* 秒转格式化成时间
|
||||
* @param {Number} sec
|
||||
* @returns {String}
|
||||
*/
|
||||
function secToTime(sec) {
|
||||
let hour = (sec / 3600) | 0;
|
||||
let min = ((sec % 3600) / 60) | 0;
|
||||
sec = (sec % 60) | 0;
|
||||
let time = hour > 0 ? hour + ":" : "";
|
||||
time += min.toString().padStart(2, '0') + ":";
|
||||
time += sec.toString().padStart(2, '0');
|
||||
return time;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字节转换成大小
|
||||
* @param {Number} byte 大小
|
||||
* @returns {String} 格式化后的文件大小
|
||||
*/
|
||||
function byteToSize(byte) {
|
||||
if (!byte || byte < 1024) { return 0; }
|
||||
if (byte < 1024 * 1024) {
|
||||
return (byte / 1024).toFixed(1) + "KB";
|
||||
} else if (byte < 1024 * 1024 * 1024) {
|
||||
return (byte / 1024 / 1024).toFixed(1) + "MB";
|
||||
} else {
|
||||
return (byte / 1024 / 1024 / 1024).toFixed(1) + "GB";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Firefox download API 无法下载 data URL
|
||||
* @param {String} url
|
||||
* @param {String} fileName 文件名
|
||||
*/
|
||||
function downloadDataURL(url, fileName) {
|
||||
let link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.download = fileName;
|
||||
link.click();
|
||||
link = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断变量是否为空
|
||||
* @param {Object|String} obj 判断的变量
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
function isEmpty(obj) {
|
||||
return (typeof obj == "undefined" ||
|
||||
obj == null ||
|
||||
obj == "" ||
|
||||
obj == " ")
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改请求头
|
||||
* @param {Object} data 请求头数据
|
||||
* @param {Function} callback
|
||||
*/
|
||||
function setRequestHeaders(data = {}, callback = undefined) {
|
||||
chrome.declarativeNetRequest.updateSessionRules({ removeRuleIds: [1] });
|
||||
chrome.tabs.getCurrent(function (tabs) {
|
||||
const rules = { removeRuleIds: [tabs ? tabs.id : 1] };
|
||||
if (Object.keys(data).length) {
|
||||
rules.addRules = [{
|
||||
"id": tabs ? tabs.id : 1,
|
||||
"priority": tabs ? tabs.id : 1,
|
||||
"action": {
|
||||
"type": "modifyHeaders",
|
||||
"requestHeaders": Object.keys(data).map(key => ({ header: key, operation: "set", value: data[key] }))
|
||||
},
|
||||
"condition": {
|
||||
"resourceTypes": ["xmlhttprequest", "media", "image"],
|
||||
}
|
||||
}];
|
||||
if (tabs) {
|
||||
rules.addRules[0].condition.tabIds = [tabs.id];
|
||||
} else {
|
||||
// initiatorDomains 只支持 chrome 101+ firefox 113+
|
||||
if (G.version < 101 || (G.isFirefox && G.version < 113)) {
|
||||
callback && callback();
|
||||
return;
|
||||
}
|
||||
const domain = G.isFirefox
|
||||
? new URL(chrome.runtime.getURL("")).hostname
|
||||
: chrome.runtime.id;
|
||||
rules.addRules[0].condition.initiatorDomains = [domain];
|
||||
}
|
||||
}
|
||||
chrome.declarativeNetRequest.updateSessionRules(rules, function () {
|
||||
callback && callback();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定标签页修改 urlFilter请求头
|
||||
* @param {Object} data 需要修改请求头的对象数组
|
||||
* @param {*} callBack 回调函数
|
||||
* @param {*} tabId 需要修改的tabId
|
||||
*/
|
||||
function setHeaders(data, callBack, tabId = -1) {
|
||||
if (!tabId == -1) {
|
||||
tabId = G.tabId;
|
||||
}
|
||||
const rules = { removeRuleIds: [], addRules: [] };
|
||||
if (!Array.isArray(data)) {
|
||||
data = [data];
|
||||
}
|
||||
for (let item of data) {
|
||||
if (!item.requestHeaders) { continue; }
|
||||
const rule = {
|
||||
"id": parseInt(item.requestId),
|
||||
"action": {
|
||||
"type": "modifyHeaders",
|
||||
"requestHeaders": Object.keys(item.requestHeaders).map(key => ({ header: key, operation: "set", value: item.requestHeaders[key] }))
|
||||
},
|
||||
"condition": {
|
||||
"resourceTypes": ["xmlhttprequest", "media", "image"],
|
||||
"tabIds": [tabId],
|
||||
"urlFilter": item.url
|
||||
}
|
||||
}
|
||||
if (item.cookie) {
|
||||
rule.action.requestHeaders.push({ header: "Cookie", operation: "set", value: item.cookie });
|
||||
}
|
||||
rules.removeRuleIds.push(parseInt(item.requestId));
|
||||
rules.addRules.push(rule);
|
||||
}
|
||||
chrome.declarativeNetRequest.updateSessionRules(rules, () => {
|
||||
callBack && callBack();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 等待全局变量G初始化完成
|
||||
* @param {Function} callback
|
||||
* @param {Number} sec
|
||||
*/
|
||||
function awaitG(callback, sec = 0) {
|
||||
const timer = setInterval(() => {
|
||||
if (G.initSyncComplete && G.initLocalComplete) {
|
||||
clearInterval(timer);
|
||||
callback();
|
||||
}
|
||||
}, sec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分割字符串 不分割引号内的内容
|
||||
* @param {String} text 需要处理的文本
|
||||
* @param {String} separator 分隔符
|
||||
* @returns {String} 返回分割后的字符串
|
||||
*/
|
||||
function splitString(text, separator) {
|
||||
text = text.trim();
|
||||
if (text.length == 0) { return []; }
|
||||
const parts = [];
|
||||
let inQuotes = false;
|
||||
let inSingleQuotes = false;
|
||||
let start = 0;
|
||||
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
if (text[i] === separator && !inQuotes && !inSingleQuotes) {
|
||||
parts.push(text.slice(start, i));
|
||||
start = i + 1;
|
||||
} else if (text[i] === '"' && !inSingleQuotes) {
|
||||
inQuotes = !inQuotes;
|
||||
} else if (text[i] === "'" && !inQuotes) {
|
||||
inSingleQuotes = !inSingleQuotes;
|
||||
}
|
||||
}
|
||||
parts.push(text.slice(start));
|
||||
return parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* 模板的函数处理
|
||||
* @param {String} text 文本
|
||||
* @param {String} action 函数名
|
||||
* @param {Object} data 填充的数据
|
||||
* @returns {String} 返回处理后的字符串
|
||||
*/
|
||||
function templatesFunction(text, action, data) {
|
||||
text = isEmpty(text) ? "" : text.toString();
|
||||
action = splitString(action, "|");
|
||||
for (let item of action) {
|
||||
let action = item.trim(); // 函数
|
||||
let arg = []; //参数
|
||||
// 查找 ":" 区分函数与参数
|
||||
const colon = item.indexOf(":");
|
||||
if (colon != -1) {
|
||||
action = item.slice(0, colon).trim();
|
||||
arg = splitString(item.slice(colon + 1).trim(), ",").map(item => {
|
||||
return item.trim().replace(/^['"]|['"]$/g, "");
|
||||
});
|
||||
}
|
||||
// 字符串不允许为空 除非 exists find prompt函数
|
||||
if (isEmpty(text) && !["exists", "find", "prompt"].includes(action)) { return "" };
|
||||
// 参数不能为空 除非 filter prompt函数
|
||||
if (arg.length == 0 && !["filter", "prompt"].includes(action)) { return text }
|
||||
|
||||
if (action == "slice") {
|
||||
text = text.slice(...arg);
|
||||
} else if (action == "replace") {
|
||||
text = text.replace(...arg);
|
||||
} else if (action == "replaceAll") {
|
||||
text = text.replaceAll(...arg);
|
||||
} else if (action == "regexp") {
|
||||
const result = text.match(new RegExp(...arg));
|
||||
text = "";
|
||||
if (result && result.length >= 2) {
|
||||
for (let i = 1; i < result.length; i++) {
|
||||
if (result[i]) {
|
||||
text += result[i].trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (action == "exists") {
|
||||
if (text) {
|
||||
text = arg[0].replaceAll("*", text);
|
||||
continue;
|
||||
}
|
||||
if (arg[1]) {
|
||||
text = arg[1].replaceAll("*", text);
|
||||
continue;
|
||||
}
|
||||
text = "";
|
||||
} else if (action == "prepend") {
|
||||
text = arg[0] + text;
|
||||
} else if (action == "concat") {
|
||||
text = text + arg[0];
|
||||
} else if (action == "to") {
|
||||
if (arg[0] == "base64") {
|
||||
text = window.Base64 ? Base64.encode(text) : btoa(unescape(encodeURIComponent(text)));
|
||||
} else if (arg[0] == "urlEncode") {
|
||||
text = encodeURIComponent(text);
|
||||
} else if (arg[0] == "urlDecode") {
|
||||
text = decodeURIComponent(text);
|
||||
} else if (arg[0] == "lowerCase") {
|
||||
text = text.toLowerCase();
|
||||
} else if (arg[0] == "upperCase") {
|
||||
text = text.toUpperCase();
|
||||
} else if (arg[0] == "trim") {
|
||||
if (text) { text = text.trim(); }
|
||||
} else if (arg[0] == "filter") {
|
||||
if (text) { text = text.trim(); }
|
||||
text = stringModify(text);
|
||||
}
|
||||
} else if (action == "find") {
|
||||
text = "";
|
||||
if (data.pageDOM) {
|
||||
try {
|
||||
text = data.pageDOM.querySelector(arg[0]).innerText?.trim();
|
||||
} catch (e) { text = ""; }
|
||||
}
|
||||
} else if (action == "filter") {
|
||||
text = stringModify(text, arg[0]);
|
||||
} else if (action == "prompt") {
|
||||
text = window.prompt("", text);
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* 模板替换
|
||||
* @param {String} text 标签模板
|
||||
* @param {Object} data 填充的数据
|
||||
* @returns {String} 返回填充后的字符串
|
||||
*/
|
||||
function templates(text, data) {
|
||||
if (isEmpty(text)) { return ""; }
|
||||
// fullFileName
|
||||
try {
|
||||
data.fullFileName = new URL(data.url).pathname.split("/").pop();
|
||||
} catch (e) {
|
||||
data.fullFileName = 'NULL';
|
||||
}
|
||||
// fileName
|
||||
data.fileName = data.fullFileName.split(".");
|
||||
data.fileName.length > 1 && data.fileName.pop();
|
||||
data.fileName = data.fileName.join(".");
|
||||
// ext
|
||||
if (isEmpty(data.ext)) {
|
||||
data.ext = data.fullFileName.split(".");
|
||||
data.ext = data.ext.length == 1 ? "" : data.ext[data.ext.length - 1];
|
||||
}
|
||||
const date = new Date();
|
||||
const trimData = {
|
||||
// 资源信息
|
||||
url: data.url ?? "",
|
||||
referer: data.requestHeaders?.referer ?? "",
|
||||
origin: data.requestHeaders?.origin ?? "",
|
||||
initiator: data.requestHeaders?.referer ? data.requestHeaders.referer : data.initiator,
|
||||
webUrl: data.webUrl ?? "",
|
||||
title: data._title ?? data.title,
|
||||
pageDOM: data.pageDOM,
|
||||
cookie: data.cookie ?? "",
|
||||
tabId: data.tabId ?? 0,
|
||||
|
||||
// 时间相关
|
||||
year: date.getFullYear(),
|
||||
month: appendZero(date.getMonth() + 1),
|
||||
date: appendZero(date.getDate()),
|
||||
day: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][date.getDay()],
|
||||
fullDate: `${date.getFullYear()}-${appendZero(date.getMonth() + 1)}-${appendZero(date.getDate())}`,
|
||||
time: `${appendZero(date.getHours())}'${appendZero(date.getMinutes())}'${appendZero(date.getSeconds())}`,
|
||||
hours: appendZero(date.getHours()),
|
||||
minutes: appendZero(date.getMinutes()),
|
||||
seconds: appendZero(date.getSeconds()),
|
||||
now: Date.now(),
|
||||
timestamp: new Date().toISOString(),
|
||||
|
||||
// 文件名
|
||||
fullFileName: data.fullFileName ? data.fullFileName : "",
|
||||
fileName: data.fileName ? data.fileName : "",
|
||||
ext: data.ext ?? "",
|
||||
|
||||
// 全局变量
|
||||
mobileUserAgent: G.MobileUserAgent,
|
||||
userAgent: G.userAgent ? G.userAgent : navigator.userAgent,
|
||||
}
|
||||
const _data = { ...data, ...trimData };
|
||||
text = text.replace(reTemplates, function (original, tag, action) {
|
||||
tag = tag.trim();
|
||||
// 特殊标签 data 返回所有数据
|
||||
if (tag == 'data') { return JSON.stringify(trimData); }
|
||||
if (action) {
|
||||
return templatesFunction(_data[tag], action.trim(), _data);
|
||||
}
|
||||
return _data[tag] ?? original;
|
||||
});
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从url中获取文件名
|
||||
* @param {String} url
|
||||
* @returns {String} 文件名
|
||||
*/
|
||||
function getUrlFileName(url) {
|
||||
let pathname = new URL(url).pathname;
|
||||
let filename = pathname.split("/").pop();
|
||||
return filename ? filename : "NULL";
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析json字符串 尝试修复键名没有双引号 解析错误返回默认值
|
||||
* @param {string} str json字符串
|
||||
* @param {object} error 解析错误返回的默认值
|
||||
* @param {number} attempt 尝试修复次数
|
||||
* @returns {object} 返回解析后的对象
|
||||
*/
|
||||
function JSONparse(str, error = {}, attempt = 0) {
|
||||
if (!str) { return error; }
|
||||
try {
|
||||
return JSON.parse(str);
|
||||
} catch (e) {
|
||||
if (attempt === 0) {
|
||||
// 第一次解析失败,修正字符串后递归调用
|
||||
reJSONparse.lastIndex = 0;
|
||||
const fixedStr = str.replace(reJSONparse, '$1"$2"$3');
|
||||
return JSONparse(fixedStr, error, ++attempt);
|
||||
} else {
|
||||
// 第二次解析仍然失败,返回 error 对象
|
||||
return error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ArrayBuffer转Blob 大于2G的做切割
|
||||
* @param {ArrayBuffer|Uint8Array} buffer 原始数据
|
||||
* @param {Object} options Blob配置
|
||||
* @returns {Blob} 返回Blob对象
|
||||
*/
|
||||
function ArrayBufferToBlob(buffer, options = {}) {
|
||||
if (buffer instanceof Blob) {
|
||||
return buffer;
|
||||
}
|
||||
if (buffer instanceof Uint8Array) {
|
||||
buffer = buffer.buffer;
|
||||
}
|
||||
if (!buffer.byteLength) {
|
||||
return new Blob();
|
||||
}
|
||||
if (!buffer instanceof ArrayBuffer) {
|
||||
return new Blob();
|
||||
}
|
||||
if (buffer.byteLength >= 2 * 1024 * 1024 * 1024) {
|
||||
const MAX_CHUNK_SIZE = 1024 * 1024 * 1024;
|
||||
let offset = 0;
|
||||
const blobs = [];
|
||||
while (offset < buffer.byteLength) {
|
||||
const chunkSize = Math.min(MAX_CHUNK_SIZE, buffer.byteLength - offset);
|
||||
const chunk = buffer.slice(offset, offset + chunkSize);
|
||||
blobs.push(new Blob([chunk]));
|
||||
offset += chunkSize;
|
||||
}
|
||||
return new Blob(blobs, options);
|
||||
}
|
||||
return new Blob([buffer], options);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 清理冗余数据
|
||||
*/
|
||||
function clearRedundant() {
|
||||
chrome.tabs.query({}, function (tabs) {
|
||||
const allTabId = new Set(tabs.map(tab => tab.id));
|
||||
|
||||
if (!cacheData.init) {
|
||||
// 清理 缓存数据
|
||||
let cacheDataFlag = false;
|
||||
for (let key in cacheData) {
|
||||
if (!allTabId.has(Number(key))) {
|
||||
cacheDataFlag = true;
|
||||
delete cacheData[key];
|
||||
}
|
||||
}
|
||||
cacheDataFlag && (chrome.storage.session ?? chrome.storage.local).set({ MediaData: cacheData });
|
||||
}
|
||||
|
||||
// 清理
|
||||
G.urlMap.forEach((_, key) => {
|
||||
!allTabId.has(key) && G.urlMap.delete(key);
|
||||
});
|
||||
|
||||
// 清理脚本
|
||||
G.scriptList.forEach(function (scriptList) {
|
||||
scriptList.tabId.forEach(function (tabId) {
|
||||
if (!allTabId.has(tabId)) {
|
||||
scriptList.tabId.delete(tabId);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (!G.initLocalComplete) { return; }
|
||||
|
||||
// 清理 declarativeNetRequest 模拟手机
|
||||
chrome.declarativeNetRequest.getSessionRules(function (rules) {
|
||||
let mobileFlag = false;
|
||||
for (let item of rules) {
|
||||
if (item.condition.tabIds) {
|
||||
// 如果tabIds列表都不存在 则删除该条规则
|
||||
if (!item.condition.tabIds.some(id => allTabId.has(id))) {
|
||||
mobileFlag = true;
|
||||
item.condition.tabIds.forEach(id => G.featMobileTabId.delete(id));
|
||||
chrome.declarativeNetRequest.updateSessionRules({
|
||||
removeRuleIds: [item.id]
|
||||
});
|
||||
}
|
||||
} else if (item.id == 1) {
|
||||
// 清理预览视频增加的请求头
|
||||
chrome.declarativeNetRequest.updateSessionRules({ removeRuleIds: [1] });
|
||||
}
|
||||
}
|
||||
mobileFlag && (chrome.storage.session ?? chrome.storage.local).set({ featMobileTabId: Array.from(G.featMobileTabId) });
|
||||
});
|
||||
// 清理自动下载
|
||||
let autoDownFlag = false;
|
||||
G.featAutoDownTabId.forEach(function (tabId) {
|
||||
if (!allTabId.has(tabId)) {
|
||||
autoDownFlag = true;
|
||||
G.featAutoDownTabId.delete(tabId);
|
||||
}
|
||||
});
|
||||
autoDownFlag && (chrome.storage.session ?? chrome.storage.local).set({ featAutoDownTabId: Array.from(G.featAutoDownTabId) });
|
||||
|
||||
G.blockUrlSet = new Set([...G.blockUrlSet].filter(x => allTabId.has(x)));
|
||||
|
||||
if (G.requestHeaders.size >= 10240) {
|
||||
G.requestHeaders.clear();
|
||||
}
|
||||
});
|
||||
// G.referer.clear();
|
||||
// G.blackList.clear();
|
||||
// G.temp.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换掉文件名中的特殊字符 包含路径
|
||||
* @param {String} str 需要处理的文本
|
||||
* @param {String} text 需要替换的文本
|
||||
* @returns {String} 返回替换后的字符串
|
||||
*/
|
||||
function stringModify(str, text) {
|
||||
if (!str) { return str; }
|
||||
str = filterFileName(str, text);
|
||||
return str.replaceAll("\\", "\").replaceAll("/", "/");
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换掉文件名中的特殊字符 不包含路径
|
||||
* @param {String} str 需要处理的文本
|
||||
* @param {String} text 需要替换的文本
|
||||
* @returns {String} 返回替换后的字符串
|
||||
*/
|
||||
function filterFileName(str, text) {
|
||||
if (!str) { return str; }
|
||||
reFilterFileName.lastIndex = 0;
|
||||
str = str.replaceAll(/\u200B/g, "").replaceAll(/\u200C/g, "").replaceAll(/\u200D/g, "");
|
||||
str = str.replace(reFilterFileName, function (match) {
|
||||
return text || {
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
':': ':',
|
||||
'"': '"',
|
||||
'|': '|',
|
||||
'?': '?',
|
||||
'*': '*',
|
||||
'~': '_'
|
||||
}[match];
|
||||
});
|
||||
|
||||
// 前后不能是 “.”
|
||||
if (str.endsWith(".")) {
|
||||
str = str + "catCatch";
|
||||
}
|
||||
if (str.startsWith(".")) {
|
||||
str = "catCatch" + str;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 展平嵌套对象的函数
|
||||
* @param {Object} obj 参数对象
|
||||
* @param {String} prefix 前缀
|
||||
* @returns 嵌套对象扁平化
|
||||
*/
|
||||
function flattenObject(obj, prefix = '') {
|
||||
let result = {};
|
||||
for (const key in obj) {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||
const value = obj[key];
|
||||
const newKey = prefix ? `${prefix}[${key}]` : key;
|
||||
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
||||
// 递归处理嵌套对象
|
||||
Object.assign(result, flattenObject(value, newKey));
|
||||
} else {
|
||||
// 处理基本类型和数组
|
||||
result[newKey] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送数据到本地
|
||||
* @param {String} action 发送类型
|
||||
* @param {Object|Srting} data 发送的数据
|
||||
* @param {Number} tabId 发送数据的标签页ID
|
||||
*/
|
||||
function send2local(action, data, tabId = 0) {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
// 请求方式
|
||||
const option = { method: G.send2localMethod };
|
||||
|
||||
// 处理替换模板
|
||||
let body = G.send2localBody;
|
||||
// 处理 addKey 请求
|
||||
if (action == 'addKey' || typeof data === 'string') {
|
||||
body = G.send2localBody.replaceAll('${data}', `"${data}"`);
|
||||
data = { tabId: tabId };
|
||||
}
|
||||
|
||||
data.action = action;
|
||||
let postData = templates(body, data);
|
||||
|
||||
// 转为对象
|
||||
postData = JSONparse(postData, { action, data, tabId });
|
||||
|
||||
try {
|
||||
// 处理URL中的模板字符串并检查合法性
|
||||
let send2localURL = templates(G.send2localURL, data);
|
||||
send2localURL = new URL(send2localURL);
|
||||
|
||||
// GET请求拼接参数
|
||||
if (option.method === 'GET') {
|
||||
const flattenedObj = flattenObject(postData);
|
||||
const urlParams = new URLSearchParams(flattenedObj);
|
||||
send2localURL.search = send2localURL.search
|
||||
? `${send2localURL.search}&${urlParams}`
|
||||
: `?${urlParams}`;
|
||||
}
|
||||
// 非GET请求处理不同Content-Type
|
||||
else {
|
||||
const contentType = {
|
||||
0: 'application/json;charset=utf-8',
|
||||
1: 'multipart/form-data',
|
||||
2: 'application/x-www-form-urlencoded',
|
||||
3: 'text/plain'
|
||||
}[G.send2localType];
|
||||
option.headers = { 'Content-Type': contentType };
|
||||
|
||||
switch (contentType) {
|
||||
case 'application/json;charset=utf-8':
|
||||
option.body = JSON.stringify(postData);
|
||||
break;
|
||||
case 'multipart/form-data':
|
||||
const formData = new FormData();
|
||||
const flattened = flattenObject(postData);
|
||||
Object.entries(flattened).forEach(([key, value]) => {
|
||||
formData.append(key, value);
|
||||
});
|
||||
option.body = formData;
|
||||
delete option.headers['Content-Type']; // 浏览器自动生成boundary
|
||||
break;
|
||||
case 'application/x-www-form-urlencoded':
|
||||
const flattenedObj = flattenObject(postData);
|
||||
const urlParams = new URLSearchParams(flattenedObj);
|
||||
option.body = urlParams.toString();
|
||||
break;
|
||||
case 'text/plain':
|
||||
option.body = typeof postData === 'object'
|
||||
? JSON.stringify(postData)
|
||||
: String(postData);
|
||||
break;
|
||||
default:
|
||||
option.body = JSON.stringify(postData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
send2localURL = send2localURL.toString();
|
||||
fetch(send2localURL, option)
|
||||
.then(response => resolve(response))
|
||||
.catch(error => reject(error));
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断url是否在屏蔽网址中
|
||||
* @param {String} url
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
function isLockUrl(url) {
|
||||
for (let key in G.blockUrl) {
|
||||
if (!G.blockUrl[key].state) { continue; }
|
||||
G.blockUrl[key].url.lastIndex = 0;
|
||||
if (G.blockUrl[key].url.test(url)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭标签页 如果tabId为0 则关闭当前标签
|
||||
* 当前只有一个标签页面 创建新标签页 再关闭
|
||||
* @param {Number|Array} tabId
|
||||
*/
|
||||
function closeTab(tabId = 0) {
|
||||
chrome.tabs.query({}, async function (tabs) {
|
||||
if (tabs.length === 1) {
|
||||
await chrome.tabs.create({ url: 'chrome://newtab' });
|
||||
tabId ? chrome.tabs.remove(tabId) : window.close();
|
||||
} else {
|
||||
tabId ? chrome.tabs.remove(tabId) : window.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开解析器
|
||||
* @param {Object} data 资源对象
|
||||
* @param {Object} options 选项
|
||||
*/
|
||||
function openParser(data, options = {}) {
|
||||
chrome.tabs.get(G.tabId, function (tab) {
|
||||
const url = `/${data.parsing ? data.parsing : "m3u8"}.html?${new URLSearchParams({
|
||||
url: data.url,
|
||||
title: data.title,
|
||||
filename: data.downFileName,
|
||||
tabid: data.tabId == -1 ? G.tabId : data.tabId,
|
||||
initiator: data.initiator,
|
||||
requestHeaders: data.requestHeaders ? JSON.stringify(data.requestHeaders) : undefined,
|
||||
...Object.fromEntries(Object.entries(options).map(([key, value]) => [key, typeof value === 'boolean' ? 1 : value])),
|
||||
})}`
|
||||
chrome.tabs.create({
|
||||
url: url,
|
||||
index: tab.index + 1,
|
||||
active: G.isMobile || !options.autoDown
|
||||
});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 加载CSS样式
|
||||
*/
|
||||
function loadCSS() {
|
||||
if (G.isMobile) {
|
||||
const mobileCssLink = document.createElement('link');
|
||||
mobileCssLink.rel = 'stylesheet';
|
||||
mobileCssLink.type = 'text/css';
|
||||
mobileCssLink.href = 'css/mobile.css';
|
||||
document.head.appendChild(mobileCssLink);
|
||||
}
|
||||
const styleElement = document.createElement('style');
|
||||
styleElement.textContent = G.css;
|
||||
document.head.appendChild(styleElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修建数据 不发送不必要的数据
|
||||
* @param {Object} originalData 原始数据
|
||||
* @returns {Object} 返回处理后的数据
|
||||
*/
|
||||
function trimData(originalData) {
|
||||
const data = { ...originalData };
|
||||
// 不发送HTML内容
|
||||
data.html = undefined;
|
||||
data.panelHeading = undefined;
|
||||
data.urlPanel = undefined;
|
||||
data.urlPanelShow = undefined;
|
||||
return data;
|
||||
}
|
||||
Reference in New Issue
Block a user