@msom/common
Version:
@msom/common
1,556 lines (1,529 loc) • 46.5 kB
JavaScript
//#region src/assert/index.ts
function assert(condition, message = "") {
if (!condition) {
if (typeof message === "function") throw message();
throw typeof message === "string" ? Error(message) : message;
}
}
function nil(data, defaultData) {
if (data === void 0 || data === null) return defaultData;
return data;
}
//#endregion
//#region src/collection/index.ts
/**
* 集合类,提供基于键值的元素存储和管理
* @template T 元素类型
*/
var Collection = class {
/**
* 创建集合实例
* @param getKey 获取元素键值的函数
*/
constructor(getKey) {
assert(getKey, "miss get unique key");
this.getKey = getKey;
this.elements = new Array();
this.elMap = /* @__PURE__ */new Map();
this.indexMap = /* @__PURE__ */new Map();
}
/**
* 获得集合的实际元素数量
*/
size() {
return Reflect.ownKeys(this.elements).length;
}
/**
* 根据键值获取元素
* @param key 元素的键值
* @returns 对应的元素或undefined
*/
get(key) {
return this.elMap.get(key);
}
/**
* 检查是否存在指定键值的元素
* @param key 要检查的键值
* @returns 是否存在
*/
hasKey(key) {
return this.elMap.has(key);
}
/**
* 检查元素是否在集合中
* @param element 要检查的元素
* @returns 是否存在
*/
hasElement(element) {
return this.hasKey(this.getKey(element));
}
/**
* 添加元素到集合
* @param element 要添加的元素
* @param force 当元素已存在时是否强制替换,默认为false
*/
add(element, force) {
const key = this.getKey(element);
const has = this.elMap.has(key);
if (!has) {
const index = this.elements.push(element) - 1;
this.indexMap.set(key, index);
this.elMap.set(key, element);
} else if (force) {
const index = this.indexMap.get(key);
assert(index);
this.elMap.set(key, element);
this.elements.splice(index, 1, element);
}
}
/**
* 使用指定的键值添加元素
* @param key 指定的键值
* @param element 要添加的元素
* @param force 当键值已存在时是否强制替换,默认为false
*/
addKey(key, element, force) {
const has = this.elMap.has(key);
if (!has) {
const index = this.elements.push(element) - 1;
this.indexMap.set(key, index);
this.elMap.set(key, element);
} else if (force) {
const index = this.indexMap.get(key);
assert(index != void 0);
this.elMap.set(key, element);
this.elements.splice(index, 1, element);
}
}
/**
* 批量添加元素
* @param iterator 可迭代的元素集合
* @param force 当元素已存在时是否强制替换,默认为false
*/
addAll(iterator, force) {
const { next } = iterator[Symbol.iterator]();
let result = next();
while (!result.done) {
this.add(result.value, force);
result = next();
}
}
/**
* 在指定位置插入元素
* @param element 待插入的元素
* @param index 插入位置,范围[0, length]。如果超出范围会被自动调整到有效范围内
* @param exist 当元素已存在时的处理选项
* @param exist.index 是否保持原有元素的位置。true: 保持原位置,false: 使用新位置
* @param exist.element 是否使用新元素替换原有元素。true: 使用新元素,false: 保持原有元素
*/
insert(element, index, exist) {
const key = this.getKey(element);
const has = this.elMap.has(key);
const { index: cIndex, element: cElement } = exist || {};
if (!has) {
this.elMap.set(key, element);
index = Math.min(this.elements.length, Math.max(0, index));
this.elements.splice(index, 0, element);
} else {
const oIndex = this.indexMap.get(key);
assert(oIndex);
const oElement = this.elements[oIndex];
const placeholder = Symbol("placegholder");
this.elements[oIndex] = placeholder;
if (!cIndex) index = oIndex;
if (!cElement) element = oElement;
this.elements.splice(index, 0, element);
this.elements = this.elements.filter((v) => v !== placeholder);
this.updateIndexMap();
}
}
/**
* 移除指定元素
* @param element 要移除的元素
* @returns 是否成功移除
*/
removeElement(element) {
const key = this.getKey(element);
return !!this.remove(key);
}
/**
* 根据键值移除元素
* @param key 要移除的元素的键值
* @returns 被移除的元素,如果元素不存在则返回undefined
*/
remove(key) {
const has = this.elMap.has(key);
if (has) {
const index = this.indexMap.get(key);
assert(index);
this.elements.splice(index, 1);
this.updateIndexMap();
this.elMap.delete(key);
}
return void 0;
}
/**
* 清空集合中的所有元素
*/
clear() {
this.elMap.clear();
this.elements.length = 0;
this.indexMap.clear();
}
/**
* 更新索引映射
* 当元素数组发生变化时,需要重新计算每个元素的索引
* @private
*/
updateIndexMap() {
const { elements, indexMap } = this;
const { length } = elements;
indexMap.clear();
for (let i = 0; i < length; i++) {
const element = elements[i];
const key = this.getKey(element);
indexMap.set(key, i);
}
}
/**
* 实现Iterable接口,使集合可以被迭代
* 使用生成器函数遍历集合中的所有元素
* @yields 集合中的每个元素
*/
[Symbol.iterator]() {
let i = 0;
return { next: () => {
const j = i;
i++;
return {
value: this.elements[j],
done: j >= this.elements.length
};
} };
}
/**
* 遍历集合中的所有元素
* @param handler 处理每个元素的回调函数
*/
each(handler) {
this.elMap.forEach((el, k) => handler(el, k, this));
}
toArray(filter, mapper) {
const result = [];
const elements = this.elements;
for (let i = 0; i < elements.length; i++) {
const element = elements[i];
const key = this.getKey(element);
if (filter && !filter(element, key, this)) continue;
const value = mapper ? mapper(element, key, this) : element;
result.push(value);
}
return result;
}
};
//#endregion
//#region src/global/index.ts
const symbolKeys = new Collection((keys) => keys.key);
function GeneratSymbolKey(key) {
if (symbolKeys.hasKey(key)) return symbolKeys.get(key).symbolKey;else
{
const symbolKey = Symbol(key);
symbolKeys.add({
key,
symbolKey
});
return symbolKey;
}
}
function setGlobalData(key, data) {
const symbolKey = GeneratSymbolKey(key);
Object.assign(globalThis, { [symbolKey]: data });
return data;
}
function getGlobalData(key) {
const symbolKey = GeneratSymbolKey(key);
const data = Reflect.get(globalThis, symbolKey);
if (!data) {
if (key.startsWith("@msom/")) throw `The GlobalData of ${key} is must init before get.`;
return setGlobalData(key, {});
}
return data;
}
const ENUMERABLE = 4;
const WRITABLE = 2;
const CONFIGURABLE = 1;
/**
* @param target
* @param propKey
* @param flag 7
* * const enumerable = 0x04;
* * const writable = 0x02;
* * const configurable = 0x01;
* @param value
*/
function defineProperty(target, propKey, flag = 7, value) {
Object.defineProperty(target, propKey, {
value,
writable: !!(WRITABLE & flag),
enumerable: !!(ENUMERABLE & flag),
configurable: !!(CONFIGURABLE & flag)
});
}
/**
* @param target
* @param propKey
* @param flag 5
* * const enumerable = 0x04;
* * const writable = 0x02;
* * const configurable = 0x01;
* * 访问器属性修饰符无法设置writable
* @param getter
* @param setter
*/
function defineAccesser(target, propKey, flag = 5, getter, setter) {
Object.defineProperty(target, propKey, {
enumerable: !!(ENUMERABLE & flag),
configurable: !!(CONFIGURABLE & flag),
get: getter,
set: setter
});
}
function tryCall(call, data, receiver, error) {
if (typeof call === "function") try {
return Reflect.apply(call, receiver, data || []);
} catch (e) {
throw error ? error(e) : e;
}
throw `${typeof call === "object" ? Reflect.get(call, "name", call) : call} is not a function.`;
}
function equal(value, otherValue) {
return Object.is(value, otherValue);
}
function ownKeysAndPrototypeOwnKeys($events, keys = new Collection((key) => key)) {
Object.keys($events).forEach((key) => keys.add(key));
const prototype = Reflect.getPrototypeOf($events);
if (prototype) ownKeysAndPrototypeOwnKeys(prototype, keys);
return keys;
}
//#endregion
//#region src/Event/context.ts
const EVENTS = Symbol("__EVENTS__");
//#endregion
//#region src/Event/Event.ts
const EK = EVENTS;
var Event = class {
constructor() {
defineProperty(this, EK, 0, Object.create(null));
}
on(type, handler) {
const _events = this[EK];
let handlers = _events[type];
if (!handlers) handlers = _events[type] = [];
handlers.push(handler);
return this;
}
un(type, handler) {
const _events = this[EK];
const handlers = _events[type];
if (!handlers) return this;
const index = handlers.findIndex((_handler) => handler === _handler);
if (index === -1) return this;
handlers.splice(index, 1);
return this;
}
emit(type, event) {
const _events = this[EK];
const handlers = _events[type];
if (!handlers) return;
handlers.forEach((handler) => handler(event, type, this));
}
};
function clearEvent(target) {
if (!target || !(target instanceof Event)) throw "the target should be Event instance";
return Reflect.deleteProperty(target, EK);
}
//#endregion
//#region src/utils/currie.ts
/**
* 将多参数函数转换为柯里化形式
* @template A 原函数的参数类型元组
* @template R 原函数的返回值类型
* @param cb 要柯里化的原函数
* @returns 柯里化后的函数
*
* @example
* // 原函数
* function add(a: number, b: number): number {
* return a + b;
* }
*
* // 柯里化
* const curriedAdd = curry(add);
* const result = curriedAdd(1)(2); // 返回 3
*/
function curry(cb) {
if (cb.length < 2) return cb;
const args = [];
/**
* 内部柯里化函数
* @template T 剩余参数类型元组
*/
const _curry = () => {
return (arg) => {
args.push(arg);
if (args.length < cb.length) return _curry();else
if (args.length === cb.length) return cb(...args);else
throw new Error(`This function '${cb.name}' can take up to ${cb.length} parameters at most`);
};
};
return _curry();
}
//#endregion
//#region src/promise/OcPromise/OcPromiseError.ts
var OcPromiseRejectError = class extends Error {};
//#endregion
//#region src/promise/OcPromise/types.ts
const PENDDING = "pendding";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
const CANCELED = "canceled";
//#endregion
//#region src/promise/nextTick/index.ts
/**
* 于执行延迟调用,并返回一个取消执行的函数
* @param task 延迟执行的函数
* @returns cancel
*/
const nextTickStore = {
id: 0,
tickMap: /* @__PURE__ */new Map()
};
/**
*
* @param task
* @returns
*/
const nextTick = (task) => {
const { id, tickMap } = nextTickStore;
const option = Object.create(null);
const _option = { canceled: false };
tickMap.set(id, option);
nextTickStore.id++;
const _task = () => {
option.cancel = () => {
_option.canceled = true;
option.cancel = void 0;
};
return () => {
if (!_option.canceled) task();
option.cancel = void 0;
};
};
if (typeof process !== "undefined" && process.nextTick) process.nextTick(_task());else
if (typeof queueMicrotask !== "undefined") queueMicrotask(_task());else
if (typeof MutationObserver !== "undefined") {
const ob = new MutationObserver(() => {
if (!_option.canceled) task();
option.cancel = void 0;
});
const dom = document.createTextNode(String(id));
ob.observe(dom, { characterData: true });
dom.data = String(id + 1);
option.cancel = () => {
_option.canceled = true;
option.cancel = void 0;
};
ob.disconnect();
dom.remove();
} else {
const id$1 = setTimeout(task, 0);
option.cancel = () => {
clearTimeout(id$1);
option.cancel = void 0;
};
}
return id;
};
//#endregion
//#region src/promise/OcPromise/utils.ts
function isPromiseLike(data) {
return !!data && (typeof data === "function" || typeof data === "object" && data !== null) && typeof data["then"] === "function";
}
function isOcPromiseLike(data) {
return isPromiseLike(data) && typeof data["cancel"] === "function";
}
//#endregion
//#region src/promise/OcPromise/OcPromise.ts
/**
* OcPromise 类 - 扩展的 Promise 实现,支持取消操作
* @template R - 成功状态的返回值类型
* @template E - 错误类型,默认为 OcPromiseRejectError
* @template C - 取消操作的原因类型
*/
var OcPromise = class OcPromise {
/**
* 创建 OcPromise 实例
* @param executor 执行器函数,接收 resolve、reject 和 cancel 函数
*/
constructor(executor) {
this.status = PENDDING;
this.handlers = [];
const resolve = (data) => {
if (isOcPromise(data) || isOcPromiseLike(data)) data.then(resolve, reject, cancel);else
if (isPromiseLike(data)) data.then(resolve, reject);else
this.changeStatus(FULFILLED, data);
};
const reject = (reason) => {
this.changeStatus(REJECTED, reason);
};
const cancel = (reason) => {
this.changeStatus(CANCELED, reason);
};
try {
executor(resolve, reject, cancel);
} catch (e) {
reject(e);
}
}
/**
* 添加完成、错误和取消的处理函数
*/
then(onfulfilled, onrejected, oncanceled) {
const res = new OcPromise((resolve, reject, cancel) => {
this.handlers.push({
resolve,
reject,
cancel,
onfulfilled,
onrejected,
oncanceled
});
this._runThens();
});
res.parrent = this;
return res;
}
/**
* 改变 Promise 状态
* @private
* @template T - 目标状态类型
* @template D - 数据类型
* @param status - 新状态
* @param data - 相关数据
*/
changeStatus(status, data) {
if (this.status !== PENDDING) return;
this.status = status;
this.data = data;
this._runThens();
}
/**
* 执行处理函数队列
* @private
*/
_runThens() {
if (this.status === PENDDING) return;
while (this.handlers.length) {
const handler = this.handlers.shift();
const { resolve, reject, cancel, onfulfilled, onrejected, oncanceled } = handler;
const exe = this.status === FULFILLED ? onfulfilled ? () => tryCall(onfulfilled, [this.data]) : (resolve(this.data), void 0) : this.status === REJECTED ? onrejected ? () => tryCall(onrejected, [this.data]) : (reject(this.data), void 0) : oncanceled ? () => tryCall(oncanceled, [this.data]) : (cancel(this.data), void 0);
if (!exe) continue;
const task = () => {
try {
const data = exe();
if (isOcPromise(data) || isOcPromiseLike(data)) nextTick(() => {
data.then(resolve, reject, (reason) => (this.cancel(reason), cancel(reason)));
});else
if (isPromiseLike(data)) nextTick(() => {
data.then(resolve, reject);
});else
resolve(data);
} catch (e) {
reject(e);
}
};
nextTick(task);
}
}
/**
* 取消 Promise
* @param reason - 取消原因
*/
cancel(reason) {
if (this.parrent && this.parrent.status === PENDDING) this.parrent.cancel(reason);else
this.changeStatus(CANCELED, reason);
}
/**
* 等待所有 Promise 完成
* @static
* @template T - 元素类型
* @param proms - Promise 或值的可迭代对象
* @returns 包含所有结果的 Promise
*/
static all(proms) {
const result = [];
return new OcPromise((resolve, reject, cancel) => {
const _resolve = (data, index) => {
result[index] = data;
finished++;
if (finished === i) resolve(result);
};
let i = 0,finished = 0;
const iterator = proms[Symbol.iterator]();
let next = iterator.next();
while (!next.done) {
const j = i;
i++;
const { value } = next;
if (isOcPromise(value)) value.then((data) => _resolve(data, j), reject, cancel);else
if (isPromiseLike(value)) value.then((data) => _resolve(data, j), reject);else
{
result[j] = value;
finished++;
}
next = iterator.next();
}
if (finished === i) resolve(result);
});
}
/**
* 创建一个已完成的 Promise
* @static
* @template T - 值的类型
* @param value - 要解析的值
*/
static resolve(value) {
if (isOcPromise(value)) return value;
if (isOcPromiseLike(value)) return new OcPromise((resolve, reject, cancel) => {
value.then(resolve, reject, cancel);
});
if (isPromiseLike(value)) return new OcPromise((resolve, reject) => {
value.then(resolve, reject);
});
return new OcPromise((resolve) => {
resolve(value);
});
}
/**
* 创建一个已拒绝的 Promise
* @static
* @template E - 错误类型
* @param reason - 拒绝原因
*/
static reject(reason) {
return new OcPromise((_, reject) => {
reject(reason);
});
}
/**
* 添加取消处理函数
* @param oncanceled - 取消处理函数
*/
canceled(oncanceled) {
return this.then(null, null, oncanceled);
}
/**
* 添加错误处理函数
* @param onRejected - 错误处理函数
*/
catch(onRejected) {
return this.then(null, onRejected, null);
}
/** 获取当前数据 */
getData() {
return this.data;
}
/** 获取当前状态 */
getStatus() {
return this.status;
}
};
/**
* 检查值是否为 OcPromise 实例
* @template PR - Promise 结果类型
* @template PE - Promise 错误类型
* @template PC - Promise 取消类型
* @param data - 要检查的值
*/
function isOcPromise(data) {
return data instanceof OcPromise;
}
OcPromise.resolve("A").then((data) => {
return OcPromise.resolve("C");
}).then((data) => {});
Promise.resolve().then(null, () => {
return new Promise(() => {});
}).then((data) => {});
//#endregion
//#region src/promise/fetch/createRequest.ts
function createCancelRequest(url, fetchInit = {}) {
const controller = new AbortController();
const signalOption = fetchInit.signal;
if (signalOption) {
const handleAbort = () => {
controller.abort();
externalSignalCleanup();
};
signalOption.addEventListener("abort", handleAbort, { once: true });
const externalSignalCleanup = () => {
signalOption.removeEventListener("abort", handleAbort);
};
}
const params = fetchInit.params;
if (params) {
const applyParams = (urlObj) => {
Object.entries(params).forEach(([key, value]) => {
urlObj.searchParams.delete(key);
if (Array.isArray(value)) value.forEach((v) => v != null && urlObj.searchParams.append(key, String(v)));else
if (value != null) urlObj.searchParams.set(key, String(value));
});
};
try {
if (typeof url === "string") {
const baseURL = url.startsWith("/") ? window.location.origin : void 0;
const urlObj = new URL(url, baseURL);
applyParams(urlObj);
url = urlObj.toString();
} else if (url instanceof URL) applyParams(url);else
if (url instanceof Request) {
const urlObj = new URL(url.url);
applyParams(urlObj);
url = new Request(urlObj.toString(), {
...url,
signal: controller.signal
});
}
} catch (e) {
console.error("URL处理错误:", e);
}
}
fetchInit.signal = controller.signal;
const promise = OcPromise.resolve(fetch(url, fetchInit));
promise.canceled(() => controller.abort());
return promise;
}
/**
* 创建请求体是application/json的请求
* @param {FetchUrl} url
* @param {JsonRequestOptions} init
* @returns
*/
function createJsonRequest(url, init) {
const headers = new Headers(init?.headers);
headers.delete("content-type");
headers.append("content-type", "application/json");
return createCancelRequest(url, {
...(init || {}),
headers
});
}
/**
* @template T
* @param {OcPromise<Response>} response
* @returns {OcPromise<T>}
*/
function json(response) {
return response.then((res) => res.json());
}
/**
* 创建响应体是json格式的请求
* @template T
* @param {FetchUrl} url
* @param {FetchOption} init
* @returns {OcPromise<T>}
*/
function createRequestJson(url, init) {
return json(createCancelRequest(url, init));
}
/**
* 创建请求体是application/json、响应体是json格式的请求
* @template T
* @param {FetchUrl} url
* @param {JsonRequestOptions} init
* @returns {OcPromise<T>}
*/
function createJsonRequestJson(url, init) {
return json(createJsonRequest(url, init));
}
//#endregion
//#region src/utils/SuperTaskController.ts
/**
* 超级任务控制器
* 用于管理和控制异步任务的执行,支持并发控制
*/
var SuperTaskController = class {
/**
* 创建任务控制器实例
* @param option 控制器配置选项
*/
constructor(option) {
option = option || {};
this.accompanyingCount = Number(option.accompanyingCount) || 2;
this.tasks = [];
this.runningCount = 0;
}
/**
* 添加新任务到控制器
* @template T 任务返回值的类型
* @param task 要执行的任务函数
* @returns 返回一个Promise,当任务执行完成时解决
*/
addTask(task) {
return new OcPromise((resolve, reject) => {
this.tasks.push({
task,
resolve,
reject
});
this.run();
});
}
/**
* 执行任务的私有方法
* 根据并发限制和任务队列状态来执行任务
* @private
*/
run() {
while (this.runningCount <= this.accompanyingCount && this.tasks.length > 0) {
const { task, resolve, reject } = this.tasks.shift();
this.runningCount++;
new Promise((resolve$1) => resolve$1(task())).then(resolve, reject).finally(() => {
this.runningCount--;
this.run();
});
}
}
};
//#endregion
//#region src/utils/ImageSplitter.ts
/**
* 图片分割器类 - 用于加载图片并根据配置分割图片
*/
var ImageSplitter = class {
processedImages;
taskQueue;
isProcessing;
/**
* 构造函数 - 初始化图片分割器
*/
constructor() {
this.processedImages = /* @__PURE__ */new Map();
this.taskQueue = [];
this.isProcessing = false;
}
/**
* 添加图片分割任务(链式调用)
* @param imageUrl - 要分割的图片URL
* @param configUrl - 分割配置的JSON文件URL
* @returns 当前实例(支持链式调用)
*/
add(imageUrl, configUrl) {
this.taskQueue.push({
imageUrl,
configUrl
});
if (!this.isProcessing) this.processQueue();
return this;
}
/**
* 获取分割后的图片base64数据
* @param name - 配置中定义的图片名称
* @returns base64图片数据或null(如果不存在)
*/
get(name) {
if (this.processedImages.has(name)) return this.processedImages.get(name);
console.warn(`[ImageSplitter] 图片名称 "${name}" 不存在`);
return null;
}
/**
* 处理任务队列(私有方法)
*/
async processQueue() {
if (this.taskQueue.length === 0) {
this.isProcessing = false;
return;
}
this.isProcessing = true;
const task = this.taskQueue.shift();
try {
const [image, config] = await Promise.all([this.loadImage(task.imageUrl), this.fetchConfig(task.configUrl)]);
for (const [name, region] of Object.entries(config)) {
if (this.processedImages.has(name)) {
console.warn(`[ImageSplitter] 名称冲突: "${name}" 已存在,跳过`);
continue;
}
const base64 = this.cropImage(image, region);
this.processedImages.set(name, base64);
}
} catch (error) {
console.error("[ImageSplitter] 处理任务失败:", error);
}
this.processQueue();
}
/**
* 加载图片(私有方法)
* @param url - 图片URL
* @returns 加载完成的Image对象
*/
loadImage(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = "Anonymous";
img.onload = () => resolve(img);
img.onerror = (e) => reject(/* @__PURE__ */new Error(`图片加载失败: ${url}`));
img.src = url;
});
}
/**
* 获取配置(私有方法)
* @param url - 配置JSON的URL
* @returns 解析后的配置对象
*/
async fetchConfig(url) {
const response = await fetch(url);
if (!response.ok) throw new Error(`配置加载失败: ${url}`);
return response.json();
}
/**
* 裁剪图片(私有方法)
* @param image - 原始图片对象
* @param region - 裁剪区域 { x, y, width, height }
* @returns base64格式的图片数据
*/
cropImage(image, region) {
const canvas = document.createElement("canvas");
canvas.width = region.width;
canvas.height = region.height;
const ctx = canvas.getContext("2d");
if (!ctx) throw new Error("无法获取canvas上下文");
ctx.drawImage(image, region.x, region.y, region.width, region.height, 0, 0, region.width, region.height);
return canvas.toDataURL("image/png");
}
};
//#endregion
//#region src/number/index.ts
/**
* @deprecated
* @param data
* @param param1
* @returns
*/
function inRange(data, { min, max }) {
if (min != void 0 && max != void 0) {
assert(min <= max, "max can't less than min");
return Math.min(max, Math.max(min, data));
}
if (min != void 0) return Math.max(min, data);
if (max != void 0) return Math.min(max, data);
return data;
}
const parseRange = (range) => {
let [min, max] = range;
min = typeof min === "number" ? {
value: min,
include: true
} : min;
max = typeof max === "number" ? {
value: max,
include: true
} : max;
assert(min && max, () => /* @__PURE__ */new TypeError("Invalid Range"));
return [min, max];
};
const regressRange = (data, range) => {
const [min, max] = parseRange(range);
{
const { include = true, value } = min;
data = (include ? data >= value : data > value) ? data : value;
}
{
const { include = false, value } = max;
data = (include ? data <= value : data < value) ? data : value;
}
return data;
};
const isInRangeNumber = (data, range) => {
const [min, max] = parseRange(range);
let IS = true;
{
const { include = true, value } = min;
IS = include ? data >= value : data > value;
}
{
const { include = false, value } = max;
IS = include ? data <= value : data < value;
}
return IS;
};
//#endregion
//#region src/utils/debounce.ts
/**
* 防抖函数:延迟执行目标函数,在频繁触发时只执行最后一次
*
* @template T 目标函数的类型签名
* @param callable 需要防抖的目标函数
* @param {number | undefined} [wait=100] wait 等待时间(毫秒),默认 100ms
* @param options 配置选项
* @param {boolean | undefined} [options.leading=false] options.leading 前置执行
* @param {boolean | undefined} [options.trailing=true] options.trailing 后置执行
* @param {number | undefined} options.maxWait 如果超过maxWait等待时间,则调用时等同于前置执行
* @returns 经过防抖处理的新函数,并附加取消方法
*
* ### 特性
* 1. 支持 TypeScript 类型推断
* 2. 配置前置执行(leading)和后置执行(trailing)
* 3. 提供取消执行方法(cancel)
* 4. 自动管理函数上下文(this)和参数
* 5. 处理异步错误(通过 Promise 返回)
* 6. 边界值安全处理
*/
function debounce(callable, wait = 100, options = {}) {
if (typeof callable !== "function") throw new TypeError("Expected a function");
if (typeof wait !== "number" || wait < 0) throw new TypeError("Wait must be a non-negative number");
let { leading = false, trailing = true, maxWait } = options;
maxWait = typeof maxWait === "number" ? regressRange(maxWait, [wait]) : maxWait;
let timerId = null;
let lastCallTime = null;
let lastArgs = null;
let lastThis;
let lastResult;
let pendingResolve = null;
const clearTimer = () => {
if (timerId) {
clearTimeout(timerId);
timerId = null;
}
};
const invokeFunction = () => {
if (!lastArgs) return;
try {
const result = callable.apply(lastThis, lastArgs);
if (pendingResolve) {
pendingResolve(result);
pendingResolve = null;
}
return result;
} catch (error) {
if (pendingResolve) {
pendingResolve(OcPromise.reject(error));
pendingResolve = null;
}
throw error;
}
};
const startTimer = (pendingCallback, delay) => {
clearTimer();
timerId = setTimeout(pendingCallback, delay);
};
const debounced = function (...args) {
if (lastResult && lastResult.getStatus() === PENDDING) lastResult.cancel("reCall");
const now = Date.now();
lastArgs = args;
lastThis = this;
if (lastCallTime === null) lastCallTime = now;
const timeSinceLastCall = now - (lastCallTime || 0);
const shouldCallLeading = leading && timeSinceLastCall >= wait;
const maxWaitExpired = maxWait !== void 0 && now - (lastCallTime || 0) >= maxWait;
if (shouldCallLeading || maxWaitExpired) {
clearTimer();
lastCallTime = now;
lastResult = new OcPromise((resolve) => {
pendingResolve = resolve;
invokeFunction();
});
} else if (trailing) lastResult = new OcPromise((resolve) => {
pendingResolve = resolve;
startTimer(() => {
lastCallTime = Date.now();
invokeFunction();
}, wait);
});else
lastResult = new OcPromise(() => {});
lastResult.canceled(() => {
clearTimer();
lastCallTime = null;
lastArgs = null;
pendingResolve = null;
});
return lastResult;
};
return debounced;
}
//#endregion
//#region src/performChunk/index.ts
/**
* 执行大量不阻塞浏览器的同步任务
* @param tasks 任务列表
* @param chunkSplitor 分时函数 默认采用requestIdleCallback 没有则判断执行时间是否超过16.6ms
* @returns
*/
function performChunk(tasks, option = {}) {
if (tasks.length === 0) return;
let { chunkSplitor, onEnd } = option;
if (typeof chunkSplitor !== "function" && typeof globalThis.requestIdleCallback === "function") chunkSplitor = (task) => {
globalThis.requestIdleCallback((idle) => {
task((elapsedTime) => idle.timeRemaining() > 0);
});
};
if (!chunkSplitor) chunkSplitor = (task) => {
task((elapsedTime) => elapsedTime < FRAME_INTERVAL);
};
const _chunkSplitor = chunkSplitor;
let i = 0;
function _run() {
if (i === tasks.length) {
onEnd && onEnd();
return;
}
_chunkSplitor((isContinue) => {
const now = Date.now();
while (isContinue(Date.now() - now) && i < tasks.length) {
console.info(i, tasks.length);
tasks[i](i);
i++;
}
});
_run();
}
_run();
}
const FRAME_INTERVAL = 1e3 / 60;
//#endregion
//#region src/overload/createOverload.ts
const TYPE_SPLITOR = ",";
const OVERlOAD_KEY = Symbol("overload");
const ADD_IMPLEMENT = "addImplement";
/**
* 创建一个可重载的函数
* @template T 类型数组的数组,每个数组最后一个类型为返回值类型
*/
function createOverload(impls) {
const overloadCollection = new Collection((m) => {
return Reflect.get(m, OVERlOAD_KEY);
});
const Method = {
method(...args) {
const overloadKey = args.map((v) => typeof v).join(TYPE_SPLITOR);
const overload = overloadCollection.get(overloadKey);
assert(overload, "No implementation found");
return overload.apply(this, args);
},
add(...impl) {
const overload = impl.pop();
if (typeof overload !== "function") throw Error("The last parameter must be a function");
const overloadKey = impl.join(TYPE_SPLITOR);
overloadCollection.addKey(overloadKey, overload, true);
}
};
defineProperty(Method.method, ADD_IMPLEMENT, 0, Method.add);
if (impls) for (const impl of impls) Method.add(...impl);
return Method.method;
}
/**
* 使用示例
*/
const example = createOverload([
[
"string",
"number",
(a, c = 1) => Number(a) + c],
["string", (a) => a],
["number", (a) => a]]
);
example("1", 2);
example("1");
example(1);
example[ADD_IMPLEMENT]("string", "number", (a, c = 1) => Number(a) + c);
//#endregion
//#region src/decorator/DecoratorUsedError.ts
const onlyUsedMap = {
observer: "observer decorator only be used with instance property.",
option: "option decorator only be used with instance property or accessor property for setter.",
component: "component decorator only be used with class.",
computed: "computed decorator only be used with instance method or accessor property for getter."
};
function decoratorUsedErrorOptionHandler(decoratorName, option) {
const { defineMessage } = option;
if (defineMessage) return typeof defineMessage === "function" ? defineMessage() : defineMessage;
const should = [
"(",
onlyUsedMap[decoratorName],
")"];
let notIndex = "";
if (option.NotStatic) notIndex = "static property or method";else
if (option.NotInComponent) notIndex = "outside a Component";else
if (option.NotSetter) notIndex = "accessor property for not setter";else
if (option.NotMethod) notIndex = "a instance method";else
if (option.NotAccessor) notIndex = "accessor property";else
if (option.NotClass) notIndex = "not a class";else
if (option.NotProperty) notIndex = "a instance property";
return `${notIndex ? `not allow used with ${notIndex}.` : ""} ${notIndex ? should.join("") : should[1]}`.trim();
}
/**
* class: ObserverDUE
*/
var ObserverDUE = class extends Error {
constructor(option = {}) {
super(decoratorUsedErrorOptionHandler("observer", option));
}
};
const ObserverDecoratorUsedError = ObserverDUE;
/**
* class: OptionDUE
*/
var OptionDUE = class extends Error {
constructor(option = {}) {
super(decoratorUsedErrorOptionHandler("option", option));
}
};
const OptionDecoratorUsedError = OptionDUE;
/**
* class: ComponentDUE
*/
var ComponentDUE = class extends Error {
constructor(option = {}) {
super(decoratorUsedErrorOptionHandler("component", option));
}
};
const ComponentDecoratorUsedError = ComponentDUE;
/**
* class: ComputedDUE
*/
var ComputedDUE = class extends Error {
constructor(option = {}) {
super(decoratorUsedErrorOptionHandler("computed", option));
}
};
const ComputedDecoratorUsedError = ComputedDUE;
//#endregion
//#region src/array/index.ts
function isArray(o) {
return Array.isArray(o);
}
/**
* 比较新旧数组,找出所有新增和删除的元素
* @param newArr 新数组
* @param oldArr 旧数组
* @param insert 处理新增元素的回调函数
* @param del 处理删除元素的回调函数
*/
function compareArray(newArr, oldArr, insert, del) {
const objectKey = /* @__PURE__ */new WeakMap();
const oldMaps = {
primitiveMap: /* @__PURE__ */new Map(),
objectMap: /* @__PURE__ */new Map()
};
const newMaps = {
primitiveMap: /* @__PURE__ */new Map(),
objectMap: /* @__PURE__ */new Map()
};
/**
* 将元素添加到映射集合中
* @param maps 目标映射集合
* @param key 要添加的元素
*/
const addMap = (maps, key) => {
if (key === null || typeof key === "object" || typeof key === "function") {
const objKey = key;
const keySymbol = objectKey.get(objKey) || createAndSetSymbol(objKey);
const value = maps.objectMap.get(keySymbol) || createObjectMapEntry(maps, keySymbol, objKey);
value.count++;
} else {
const primitiveKey = key;
const value = maps.primitiveMap.get(primitiveKey) || createMapEntry(maps.primitiveMap, primitiveKey);
value.count++;
}
};
/**
* 为对象创建并设置唯一标识符
* @param key 对象
* @returns 创建的唯一标识符
*/
const createAndSetSymbol = (key) => {
const keySymbol = Symbol();
objectKey.set(key, keySymbol);
return keySymbol;
};
/**
* 创建对象类型的计数条目
* @param maps 映射集合
* @param keySymbol 对象的唯一标识符
* @param obj 对象本身
* @returns 创建的计数对象
*/
const createObjectMapEntry = (maps, keySymbol, obj) => {
const value = {
count: 0,
obj
};
maps.objectMap.set(keySymbol, value);
return value;
};
/**
* 创建基本类型的计数条目
* @param map 目标映射
* @param key 基本类型值
* @returns 创建的计数对象
*/
const createMapEntry = (map, key) => {
const value = { count: 0 };
map.set(key, value);
return value;
};
oldArr.forEach((item) => addMap(oldMaps, item));
newArr.forEach((item) => addMap(newMaps, item));
const inserts = [];
const dels = [];
compareMapEntries(oldMaps.primitiveMap, newMaps.primitiveMap, inserts, dels);
compareObjectMapEntries(oldMaps.objectMap, newMaps.objectMap, objectKey, inserts, dels);
if (inserts.length > 0 && insert) tryCall(insert, [inserts]);
if (dels.length > 0 && del) tryCall(del, [dels]);
}
/**
* 比较基本类型元素的映射
* @param oldMap 旧映射
* @param newMap 新映射
* @param inserts 存储新增元素的数组
* @param dels 存储删除元素的数组
*/
function compareMapEntries(oldMap, newMap, inserts, dels) {
oldMap.forEach((value, key) => {
const newValue = newMap.get(key);
updateArrays(key, value.count, newValue?.count || 0, inserts, dels);
newMap.delete(key);
});
newMap.forEach((value, key) => {
inserts.push(...new Array(value.count).fill(key));
});
}
/**
* 比较对象类型元素的映射
* @param oldMap 旧映射
* @param newMap 新映射
* @param objectKey 对象到唯一标识符的映射
* @param inserts 存储新增元素的数组
* @param dels 存储删除元素的数组
*/
function compareObjectMapEntries(oldMap, newMap, objectKey, inserts, dels) {
oldMap.forEach((value) => {
const keySymbol = objectKey.get(value.obj);
if (keySymbol) {
const newValue = newMap.get(keySymbol);
updateArrays(value.obj, value.count, newValue?.count || 0, inserts, dels);
newMap.delete(keySymbol);
}
});
newMap.forEach((value) => {
inserts.push(...new Array(value.count).fill(value.obj));
});
}
/**
* 根据元素数量变化更新新增和删除数组
* @param item 要处理的元素
* @param oldCount 旧数量
* @param newCount 新数量
* @param inserts 存储新增元素的数组
* @param dels 存储删除元素的数组
*/
function updateArrays(item, oldCount, newCount, inserts, dels) {
if (newCount < oldCount) dels.push(...new Array(oldCount - newCount).fill(item));else
if (newCount > oldCount) inserts.push(...new Array(newCount - oldCount).fill(item));
}
//#endregion
//#region src/object/index.ts
/**
* 比较两个对象是否相等
* 支持深度比较对象的所有属性
*
* @template T 对象类型
* @param obj1 第一个对象
* @param obj2 第二个对象
* @returns 如果对象相等返回true,否则返回false
*
* @example
* const a = { x: 1, y: { z: 2 } };
* const b = { x: 1, y: { z: 2 } };
* compareObjects(a, b); // 返回 true
*
* // 数组比较
* compareObjects([1], {'0': 1}); // 返回 false
*/
function compareObjects(obj1, obj2) {
if (obj1 === obj2) return true;
if (!obj1 || !obj2 || typeof obj1 !== "object" || typeof obj2 !== "object") return false;
if (isArray(obj1) !== isArray(obj2)) return false;
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
for (const key of keys1) {
const val1 = obj1[key];
const val2 = obj2[key];
if (typeof val1 === "object" && typeof val2 === "object" && val1 !== null && val2 !== null) {
if (!compareObjects(val1, val2)) return false;
} else if (val1 !== val2) return false;
}
return true;
}
function isObject(value) {
return typeof value === "object" && value !== null;
}
function cloneObject(data, deep) {
function _clone(data$1, cache = /* @__PURE__ */new WeakMap()) {
const _cache = cache.get(data$1);
if (_cache) return _cache;
const cloned = Object.create(Reflect.getPrototypeOf(data$1));
cache.set(data$1, cloned);
const keys = Reflect.ownKeys(data$1);
for (let i = 0; i < keys.length; i++) {
const desc = Reflect.getOwnPropertyDescriptor(data$1, keys[i]);
if (!desc) continue;
if (Reflect.has(desc, "value")) {
const value = Reflect.get(desc, "value", desc);
Reflect.set(desc, "value", deep && isObject(value) ? _clone(value, cache) : value, desc);
}
Reflect.defineProperty(cloned, keys[i], desc);
}
return cloned;
}
return _clone(data);
}
//#endregion
//#region src/string/transformer.ts
function camelToKebab(text, option = {}) {
const _option = {
beforReturn: (text$1) => text$1,
...option
};
const result = text.replace(/([A-Z]+)([A-Z][a-z]?)/g, "$1-$2").replace(/([a-z])([A-Z])/g, "$1-$2").replace(/([A-Z])([A-Z]+)/g, "$1-$2").toLowerCase();
return nil(_option.beforReturn(result, text), result);
}
//#endregion
//#region src/dom/index.ts
function parseClass(classType) {
if (typeof classType === "string") return classType.trim();
if (isArray(classType)) return classType.reduce((className, _classType) => {
if (typeof _classType === "string" && _classType !== "") return `${className} ${_classType}`;
return className;
}, "").trim();
return Object.entries(classType).reduce((className, [_classType, IS]) => IS ? `${className} ${_classType}` : className, "").trim();
}
function parseStyle(style) {
if (typeof style === "string") return style;
if (isArray(style)) style = style.reduce((a, b) => {
a[b[0]] = b[1];
return a;
}, {});
return Object.entries(style).map(([n, v]) => {
n = camelToKebab(n, { beforReturn(text) {
if (text.startsWith("webkit")) return "-" + text;
} });
if (v == void 0) return "";
if (typeof v === "number" && !isNumericCSSProperty(n)) v = `${v}px`;
return `${n}: ${v}`;
}).join("; ").trim();
}
function isNumericCSSProperty(camelCaseProp) {
const cssProperty = camelToKebab(camelCaseProp, { beforReturn(text) {
if (text.startsWith("webkit")) return "-" + text;
} });
const numericProperties = new Set([
"z-index",
"opacity",
"flex-grow",
"flex-shrink",
"order",
"font-weight",
"line-height",
"column-count",
"counter-increment",
"counter-reset",
"grid-row-start",
"grid-row-end",
"grid-column-start",
"grid-column-end",
"orphans",
"widows",
"scale",
"fill-opacity",
"stroke-opacity",
"stroke-width",
"shape-image-threshold"]
);
return numericProperties.has(cssProperty);
}
//#endregion
//#region src/component/addStyle.ts
function addStyle(cssStyle) {
const style = document.createElement("style");
style.innerHTML = cssStyle;
document.head.appendChild(style);
}
//#endregion
//#region src/component/index.ts
const componentGlobalData = setGlobalData("@msom/component", {
componentDefinitionKey: Symbol("component_definition"),
componentMap: /* @__PURE__ */new Map()
});
/**
* 初始化组件定义
* 不会向上继续找原型对象的原型
* @param prototype 组件类或类的原型对象
* @returns 组件定义
*/
function initComponentDefinition(prototype) {
const { componentDefinitionKey } = componentGlobalData;
prototype = typeof prototype === "function" ? prototype.prototype : prototype;
const prototype_prototype = Object.getPrototypeOf(prototype);
const prototype_prototype_definition = getComponentDefinition(prototype_prototype);
try {
Object.setPrototypeOf(prototype, null);
let definition = Reflect.get(prototype, componentDefinitionKey);
if (!definition) {
definition = Object.create(null);
assert(definition);
Object.assign(definition, {
$options: Object.create(prototype_prototype_definition?.["$options"] || null),
$events: Object.create(prototype_prototype_definition?.["$events"] || null),
$observers: Object.create(prototype_prototype_definition?.["$observers"] || null)
});
defineProperty(prototype, componentDefinitionKey, 0, definition);
}
return definition;
} finally {
Object.setPrototypeOf(prototype, prototype_prototype);
}
}
/**
* @param prototype 组件类或类的原型对象
* @returns 组件定义
*/
function getComponentDefinition(prototype) {
const { componentDefinitionKey } = componentGlobalData;
prototype = typeof prototype === "function" ? prototype.prototype : prototype;
const oldProptotype = Object.getPrototypeOf(prototype);
try {
Object.setPrototypeOf(prototype, null);
return Reflect.get(prototype, componentDefinitionKey);
} finally {
Object.setPrototypeOf(prototype, oldProptotype);
}
}
/**
* 判断是否使用 @component 装饰器标记
* @param ctor 类构造器或原型对象
* @returns
*/
function isComponent(ctor) {
const { componentDefinitionKey } = componentGlobalData;
const target = typeof ctor === "function" ? ctor.prototype : ctor;
const prototype = Object.getPrototypeOf(target);
try {
Object.setPrototypeOf(target, null);
return Reflect.has(target, componentDefinitionKey);
} finally {
Object.setPrototypeOf(target, prototype);
}
}
//#endregion
export { CANCELED, CONFIGURABLE, Collection, ComponentDecoratorUsedError, ComputedDecoratorUsedError, ENUMERABLE, Event, FRAME_INTERVAL, FULFILLED, GeneratSymbolKey, ImageSplitter, ObserverDecoratorUsedError, OcPromise, OcPromiseRejectError, OptionDecoratorUsedError, PENDDING, REJECTED, SuperTaskController, WRITABLE, addStyle, assert, clearEvent, cloneObject, compareArray, compareMapEntries, compareObjectMapEntries, compareObjects, createCancelRequest, createJsonRequest, createJsonRequestJson, createOverload, createRequestJson, curry, debounce, defineAccesser, defineProperty, equal, getComponentDefinition, getGlobalData, inRange, initComponentDefinition, isArray, isComponent, isInRangeNumber, isObject, isOcPromise, isOcPromiseLike, isPromiseLike, nil, ownKeysAndPrototypeOwnKeys, parseClass, parseStyle, performChunk, regressRange, setGlobalData, tryCall, updateArrays };
//# sourceMappingURL=index.js.map