Files
mars3142_collection/libs/js/utils.js
T
mars3142 fbf40f75b2 first plugins for Ulanzi D200H
- petrol watch
- copilot usage

Signed-off-by: Peter Siegmund <developer@mars3142.org>
2026-04-09 20:42:31 +02:00

549 lines
14 KiB
JavaScript

class UlanziUtils {
/**
* 获取表单数据
* Returns the value from a form using the form controls name property
* @param {Element | string} form
* @returns
*/
getFormValue(form) {
if (typeof form === 'string') {
form = document.querySelector(form);
}
const elements = form ? form.elements : '';
if (!elements) {
console.error('Could not find form!');
}
const formData = new FormData(form);
let formValue = {};
formData.forEach((value, key) => {
if (!Reflect.has(formValue, key)) {
formValue[key] = value;
return;
}
if (!Array.isArray(formValue[key])) {
formValue[key] = [formValue[key]];
}
formValue[key].push(value);
});
return formValue;
}
/**
* 重载表单数据
* Sets the value of form controls using their name attribute and the jsn object key
* @param {*} jsn
* @param {Element | string} form
*/
setFormValue(jsn, form) {
if (!jsn) {
return;
}
if (typeof form === 'string') {
form = document.querySelector(form);
}
const elements = form ? form.elements : '';
if (!elements) {
console.error('Could not find form!');
}
Array.from(elements)
.filter((element) => element ? element.name : null)
.forEach((element) => {
const { name, type } = element;
const value = name in jsn ? jsn[name] : null;
const isCheckOrRadio = type === 'checkbox' || type === 'radio';
if (value === null) return;
if (isCheckOrRadio) {
const isSingle = value === element.value;
if (isSingle || (Array.isArray(value) && value.includes(element.value))) {
element.checked = true;
}
} else {
element.value = value ? value : '';
}
});
}
/**
* 延迟触发
* This provides a slight delay before processing rapid events
* @param {function} fn
* @param {number} wait - delay before processing function (recommended time 150ms)
* @returns
*/
debounce(fn, wait = 150) {
let timeoutId = null
return (...args) => {
window.clearTimeout(timeoutId);
timeoutId = window.setTimeout(() => {
fn.apply(null, args);
}, wait);
};
}
/**
* 返回url的查询参数
*/
getQueryParams(param) {
const searchParams = new URLSearchParams(window.location.search);
return searchParams.get(param);
}
/**
* 获取浏览器语言
* Returns the user language
*/
getLanguage() {
let userLanguage = navigator.languages && navigator.languages.length ? navigator.languages[0] : (navigator.language || navigator.userLanguage);
if (userLanguage == 'zh') {
userLanguage = 'zh_CN'
} else if (userLanguage.indexOf('zh-') >= 0) {
userLanguage = userLanguage.split('-').join('_')
} else if (userLanguage.indexOf('-') !== -1) {
userLanguage = userLanguage.replace(/-/g, '_');
}
return this.adaptLanguage(userLanguage);
}
/**
* 适配语言环境
*/
adaptLanguage(ln) {
let userLanguage = ln;
if (ln.indexOf('zh') == 0) {
if(ln.indexOf('CN') > -1){
userLanguage = 'zh_CN'
}else{
userLanguage = 'zh_HK'
}
} else if (ln.indexOf('en') == 0) {
userLanguage = 'en'
} else if (userLanguage.indexOf('-') !== -1) {
userLanguage = userLanguage.replace(/-/g, '_');
}
return userLanguage
}
/**
* JSON.parse优化
* parse json
* @param {string} jsonString
* @returns {object} json
*/
parseJson(jsonString) {
if (typeof jsonString === 'object') return jsonString;
try {
const o = JSON.parse(jsonString);
if (o && typeof o === 'object') {
return o;
}
} catch (e) { }
return false;
}
/**
* 读取json文件
* Reads a json file
* @param {string} path
* @returns {Promise<any>} json
*/
async readJson(path) {
if (!path) {
console.error('A path is required to readJson.');
}
return new Promise((resolve, reject) => {
try {
const req = new XMLHttpRequest();
req.onerror = reject;
req.overrideMimeType('application/json');
req.open('GET', path, true);
req.onreadystatechange = (response) => {
if (req.readyState === 4) {
const jsonString = response && response.target && response.target.response || '';
if (jsonString) {
try {
resolve(JSON.parse(jsonString));
} catch (e) {
reject();
}
} else {
reject();
}
}
};
req.send();
} catch (e) {
reject();
}
});
}
/**
* 完整图片转base64
* @param {string} url 图片地址
* @param {number} width canvas宽度,默认196
* @param {number} height canvas宽度,默认196
* @param {HTMLCanvasElement} inCanvas canvas元素,默认创建
* @param {boolean} returnCanvas 是否返回canvas,默认false。默认返回base64的图片路径,有些时候需要接着画布添加元素,所以我们添加这个变量
* @return { string | HTMLCanvasElement } 默认返回base64的图片路径,returnCanvas为true返回画布
*/
async drawImage(url, width = 196, height = 196, inCanvas, returnCanvas) {
const canvas = inCanvas && inCanvas instanceof HTMLCanvasElement ? inCanvas : document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
const imgData = await this.loadImagePromise(url)
if (imgData.status == 'ok') {
ctx.drawImage(imgData.img, 0, 0, canvas.width, canvas.height);
}
return returnCanvas ? canvas : canvas.toDataURL('image/png'); //需要是否需要返回画布或者直接返回base64
}
/**
* 裁剪图片转base64
* @param {string} url 图片地址
* @param {number} offsetX 裁剪x的位置
* @param {number} offsetY 裁剪y的位置
* @param {number} width canvas宽度,默认196
* @param {number} height canvas宽度,默认196
* @param {HTMLCanvasElement} inCanvas canvas元素,默认创建
* @param {boolean} returnCanvas 是否返回canvas,默认false。默认返回base64的图片路径,有些时候需要接着画布添加元素,所以我们添加这个变量
* @return { string | HTMLCanvasElement } 默认返回base64的图片路径,returnCanvas为true返回画布
*/
async cropImage(url, offsetX, offsetY, width = 196, height = 196, inCanvas, returnCanvas) {
const canvas = inCanvas && inCanvas instanceof HTMLCanvasElement ? inCanvas : document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = width;
canvas.height = height;
const imgData = await this.loadImagePromise(url)
if (imgData.status == 'ok') {
ctx.drawImage(imgData.img, offsetX, offsetY, width, height, 0, 0, canvas.width, canvas.height);
}
return returnCanvas ? canvas : canvas.toDataURL('image/png'); //需要是否需要返回画布或者直接返回base64
};
/**
* 获取图片数据
* @param {string} url 图片地址
* @return {object} {url, status: 'ok', img} or {url, status: 'error'}
*/
loadImagePromise(url) {
return new Promise(resolve => {
const img = new Image();
img.onload = () => resolve({ url, status: 'ok', img });
img.onerror = () => resolve({ url, status: 'error' });
img.src = url;
});
}
getData(url, param) {
param = Object.assign(param || {}, Utils.joinTimestamp());
//若参数有数组,进行特殊拼接
url = url + '?' + Object.keys(param).map(e => {
let str = ''
//判断数组拼接
if (param[e] instanceof Array) {
str = param[e].map((item) => {
return `${e}=${item}`
}).join('&')
} else {
str = `${e}=${param[e]}`
}
return str
}).join('&');
// console.warn('=====getData url:', url)
return new Promise(function (resolve, reject) {
var req = new XMLHttpRequest();
req.timeout = 1500; // 设置超时时间为 5 秒
req.ontimeout = function () {
console.error('Request timed out');
};
req.onload = function () {
// console.warn('=====getData onload:')
if (req.status === 200) {
// console.warn('=====getData success:')
resolve(req.response);
} else {
// console.warn('=====getData not 200:')
reject(Error(req.statusText));
}
};
req.onerror = function () {
// console.warn('=====getData error:')
reject(Error('Network Error'));
};
req.open('GET', url, true);
req.send();
});
};
/**
* 获取接口数据
* @param {string} url 接口地址
* @param {object} param 接口参数
* @param {string} method 请求方式:GET/POST/PUT/DELETE
* @param {object} headers 请求头
*/
fetchData(url, param, method = 'GET', headers = {}) {
if (method.toUpperCase() === 'GET') {
param = Object.assign(param || {}, Utils.joinTimestamp());
const tag = url.indexOf('?') >= 0 ? '&':'?'
//若参数有数组,进行特殊拼接
url = url + tag + Object.keys(param).map(e => {
let str = ''
//判断数组拼接
if (param[e] instanceof Array) {
str = param[e].map((item) => {
return `${e}=${item}`
}).join('&')
} else {
str = `${e}=${param[e]}`
}
return str
}).join('&');
}
const opts = {
cache: 'no-cache',
headers,
method: method,
body: ['GET', 'HEAD'].includes(method)
? undefined
: param,
};
return new Promise(function (resolve, reject) {
Utils.fetchWithTimeout(url, opts)
.then(async (resp) => {
// console.warn('==fetch success:', url)
if (!resp) {
reject(new Error('No Resp'));
}
if (!resp.ok) {
const errData = await resp.json();
if (errData) {
reject(errData);
} else {
reject(new Error(`{${resp.status}: ${await resp.text()}}`));
}
} else {
resolve(await resp.json());
}
})
.catch((err) => {
// console.warn('==fetch error:', JSON.stringify(err))
reject(err);
})
});
}
/**
* 封装fetch请求,设置超时时间
*/
fetchWithTimeout(url, options = {}) {
const { timeout = 15000 } = options; // 设置默认超时时间为8000ms
// console.warn('====fetchWithTimeout timeout:', timeout)
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
// console.warn('==fetchWithTimeout:', url, JSON.stringify(options))
const response = fetch(url, {
...options,
signal: controller.signal
}).then((response) => {
// console.warn('==fetchWithTimeout success:', JSON.stringify(response))
clearTimeout(id);
return response;
}).catch((error) => {
// console.warn('==fetchWithTimeout error:', JSON.stringify(error))
clearTimeout(id);
throw error;
});
return response;
}
/**
* 获取随机时间戳
*/
joinTimestamp() {
const now = new Date().getTime();
return { _t: now };
}
//判断是否为文件类型
isFile(variable) {
return variable instanceof File;
}
/**
* 浏览器file转base64
*/
htmlFileToBase64(file) {
if (!this.isFile(file)) {
return Promise.reject(new Error('Not a file'));
}
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
}
drawText(text, stroke = "#fff", background = "#000", wh = 196, textLabel, inCanvas) {
// console.log('==drawText:', text, textLabel)
const canvas = inCanvas ? inCanvas : document.createElement('canvas');
const ctx = canvas.getContext('2d');
if(!inCanvas){
canvas.width = wh;
canvas.height = wh;
if (background == "transparent") {
ctx.clearRect(0, 0, canvas.width, canvas.height);
} else {
ctx.fillStyle = background;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
}
const fSize = text.length > 8 ? 30 - text.length / 2 : 40;
ctx.strokeStyle = "#000";
ctx.lineWidth = 4;
ctx.fillStyle = stroke;
ctx.font = `bold ${fSize}px "Source Han Sans SC"`;
ctx.textBaseline = 'middle';
ctx.textAlign = 'center';
ctx.strokeText(text, ctx.canvas.width / 2, ctx.canvas.height / 2);
ctx.fillText(text, ctx.canvas.width / 2, ctx.canvas.height / 2 );
if(textLabel){
ctx.font = `bold 24px "Source Han Sans SC"`;
ctx.textBaseline = 'middle';
ctx.textAlign = 'left';
ctx.fillText(textLabel, 10, 20);
}
return canvas.toDataURL('image/png')
}
getProperty(obj, dotSeparatedKeys, defaultValue) {
if (arguments.length > 1 && typeof dotSeparatedKeys !== 'string') return undefined;
if (typeof obj !== 'undefined' && typeof dotSeparatedKeys === 'string') {
const pathArr = dotSeparatedKeys.split('.');
pathArr.forEach((key, idx, arr) => {
if (typeof key === 'string' && key.includes('[')) {
try {
// extract the array index as string
const pos = /\[([^)]+)\]/.exec(key)[1];
// get the index string length (i.e. '21'.length === 2)
const posLen = pos.length;
arr.splice(idx + 1, 0, Number(pos));
// keep the key (array name) without the index comprehension:
// (i.e. key without [] (string of length 2)
// and the length of the index (posLen))
arr[idx] = key.slice(0, -2 - posLen); // eslint-disable-line no-param-reassign
} catch (e) {
// do nothing
}
}
});
// eslint-disable-next-line no-param-reassign, no-confusing-arrow
obj = pathArr.reduce((o, key) => (o && o[key] !== 'undefined' ? o[key] : undefined), obj);
}
return obj === undefined ? defaultValue : obj;
};
getProp(jsn, str, defaultValue = {}, sep = '.') {
const arr = str.split(sep);
return arr.reduce((obj, key) => (obj && obj.hasOwnProperty(key) ? obj[key] : defaultValue), jsn);
};
/**
* 获取插件根目录路径
*/
getPluginPath(){
const currentFilePath = location.pathname;
let split_tag = '/'
if(currentFilePath.indexOf('\\') > -1){
split_tag = '\\'
}
const pathArr = currentFilePath.split(split_tag);
const idx = pathArr.findIndex(f => f.endsWith('ulanziPlugin'));
const __folderpath = `${pathArr.slice(0, idx + 1).join("/")}`;
return __folderpath;
}
/**
* Logs a message
* @param {any} msg
*/
log(...msg) {
console.warn(`[${new Date().toLocaleString('zh-CN', { hour12: false })}]`, ...msg);
// this.getQueryParams('debug') && console.log(`[${new Date().toLocaleString('zh-CN', {hour12: false})}]`, ...msg);
}
/**
* Logs a warning message
*/
warn(...msg) {
console.warn(`[${new Date().toLocaleString('zh-CN', { hour12: false })}]`, ...msg);
}
/**
* Logs an error message
*/
error(...msg) {
console.error(`[${new Date().toLocaleString('zh-CN', { hour12: false })}]`, ...msg);
}
}
const Utils = new UlanziUtils()