UNPKG

@msom/common

Version:

@msom/common

1,556 lines (1,529 loc) 46.5 kB
//#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