@longmo-utils/browser 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +28 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -72,6 +72,12 @@ declare function openWindow(url: string, options?: OpenWindowOptions): void;
|
|
|
72
72
|
//#region src/window/openRouteInNewWindow.d.ts
|
|
73
73
|
declare function openRouteInNewWindow(path: string): void;
|
|
74
74
|
//#endregion
|
|
75
|
+
//#region src/window/getURLSearchParams.d.ts
|
|
76
|
+
declare function getURLSearchParams(url?: string): Record<string, string>;
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region src/window/setURLSearchParams.d.ts
|
|
79
|
+
declare function setURLSearchParams(url?: string, params?: Record<string, string | undefined>): string;
|
|
80
|
+
//#endregion
|
|
75
81
|
//#region src/download/types.d.ts
|
|
76
82
|
interface DownloadOptions<T = string> {
|
|
77
83
|
fileName?: string;
|
|
@@ -119,5 +125,26 @@ declare function urlToBase64(url: string, mineType?: string): Promise<string>;
|
|
|
119
125
|
//#region src/download/triggerDownload.d.ts
|
|
120
126
|
declare function triggerDownload(href: string, fileName: string | undefined, revokeDelay?: number): void;
|
|
121
127
|
//#endregion
|
|
122
|
-
|
|
128
|
+
//#region src/binary/blobToArrayBuffer.d.ts
|
|
129
|
+
declare function blobToArrayBuffer(blob: Blob): Promise<ArrayBuffer>;
|
|
130
|
+
//#endregion
|
|
131
|
+
//#region src/binary/arrayBufferToBlob.d.ts
|
|
132
|
+
declare function arrayBufferToBlob(arrayBuffer: ArrayBuffer, options?: BlobPropertyBag): Blob;
|
|
133
|
+
//#endregion
|
|
134
|
+
//#region src/binary/arrayBufferToBase64.d.ts
|
|
135
|
+
declare function arrayBufferToBase64(arrayBuffer: ArrayBuffer): string;
|
|
136
|
+
//#endregion
|
|
137
|
+
//#region src/binary/base64ToBlob.d.ts
|
|
138
|
+
declare function base64ToBlob(base64: string, options?: BlobPropertyBag, sliceSize?: number): Blob;
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region src/binary/blobToBase64.d.ts
|
|
141
|
+
declare function blobToBase64(blob: Blob): Promise<string>;
|
|
142
|
+
//#endregion
|
|
143
|
+
//#region src/binary/blobToObjectURL.d.ts
|
|
144
|
+
declare function blobToObjectURL(blob: Blob): string;
|
|
145
|
+
//#endregion
|
|
146
|
+
//#region src/binary/blobToDataURL.d.ts
|
|
147
|
+
declare function blobToDataURL(blob: Blob): Promise<string>;
|
|
148
|
+
//#endregion
|
|
149
|
+
export { $, $$, DEFAULT_FILENAME, DownloadOptions, OpenWindowOptions, StorageManager, addClass, arrayBufferToBase64, arrayBufferToBlob, base64ToBlob, blobToArrayBuffer, blobToBase64, blobToDataURL, blobToObjectURL, clearLocalStorage, copyToClipboard, createElement, downloadFileFromBase64, downloadFileFromBlob, downloadFileFromBlobPart, downloadFileFromImageUrl, downloadFileFromUrl, getLocalStorage, getSessionStorage, getStyle, getURLSearchParams, isInViewport, off, on, once, openRouteInNewWindow, openWindow, removeClass, removeLocalStorage, removeSessionStorage, scrollIntoView, setLocalStorage, setSessionStorage, setStyle, setURLSearchParams, toggleClass, triggerDownload, urlToBase64 };
|
|
123
150
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/storage/index.ts","../src/dom/selector.ts","../src/dom/creation.ts","../src/dom/class.ts","../src/dom/style.ts","../src/dom/viewport.ts","../src/dom/event.ts","../src/clipboard/index.ts","../src/cache/storage-manager.ts","../src/window/types.ts","../src/window/openWindow.ts","../src/window/openRouteInNewWindow.ts","../src/download/types.ts","../src/download/constants.ts","../src/download/downloadFileFromUrl.ts","../src/download/downloadFileFromBase64.ts","../src/download/downloadFileFromImageUrl.ts","../src/download/downloadFileFromBlob.ts","../src/download/downloadFileFromBlobPart.ts","../src/download/urlToBase64.ts","../src/download/triggerDownload.ts"],"mappings":";iBAOgB,eAAA,SAAA,CAAyB,GAAA,WAAc,CAAA;AAAA,iBAavC,eAAA,GAAA,CAAmB,GAAA,UAAa,KAAA,EAAO,CAAA;AAAA,iBAavC,kBAAA,CAAmB,GAAA;AAAA,iBAanB,iBAAA,CAAA;AAAA,iBAaA,iBAAA,SAAA,CAA2B,GAAA,WAAc,CAAA;AAAA,iBAazC,iBAAA,GAAA,CAAqB,GAAA,UAAa,KAAA,EAAO,CAAA;AAAA,iBAazC,oBAAA,CAAqB,GAAA;;;iBC7CrB,CAAA,CAAE,QAAA,WAAmB,WAAA;AAAA,iBAuCrB,EAAA,CAAG,QAAA,WAAmB,WAAA;;;iBCGtB,aAAA,iBAA8B,qBAAA,CAAA,CAC5C,GAAA,EAAK,CAAA,EACL,UAAA,GAAa,MAAA,kBACb,QAAA,aAAqB,IAAA,MACpB,qBAAA,CAAsB,CAAA;;;iBCCT,QAAA,CAAS,OAAA,EAAS,WAAA,KAAgB,UAAA;AAAA,iBA+ClC,WAAA,CAAY,OAAA,EAAS,WAAA,KAAgB,UAAA;AAAA,iBAiErC,WAAA,CAAY,OAAA,EAAS,WAAA,EAAa,SAAA;;;iBCtIlC,QAAA,CAAS,OAAA,EAAS,WAAA,EAAa,QAAA;AAAA,iBAoE/B,QAAA,CAAS,OAAA,EAAS,WAAA,EAAa,MAAA,EAAQ,MAAA;;;iBCzDvC,YAAA,CAAa,OAAA,EAAS,WAAA;AAAA,iBA+FtB,cAAA,CAAe,OAAA,EAAS,WAAA,EAAa,OAAA,GAAU,qBAAA;;;iBCpG/C,EAAA,CACd,OAAA,EAAS,WAAA,GAAc,QAAA,GAAW,MAAA,EAClC,KAAA,UACA,OAAA,EAAS,kCAAA,EACT,UAAA;AAAA,iBA8Dc,GAAA,CACd,OAAA,EAAS,WAAA,GAAc,QAAA,GAAW,MAAA,EAClC,KAAA,UACA,OAAA,EAAS,kCAAA,EACT,UAAA;AAAA,iBAmEc,IAAA,CACd,OAAA,EAAS,WAAA,GAAc,QAAA,GAAW,MAAA,EAClC,KAAA,UACA,OAAA,EAAS,kCAAA,EACT,UAAA;;;UClNQ,WAAA;EACR,MAAA,EAAQ,WAAA;AAAA;AAAA,iBAeM,eAAA,CAAgB,IAAA,UAAc,OAAA,GAAU,WAAA;;;KClBnD,WAAA;AAAA,UAEK,qBAAA;EACR,MAAA;EACA,WAAA,GAAc,WAAA;AAAA;AAAA,cAQV,cAAA;EAAA,QACI,MAAA;EAAA,QACA,OAAA;;IAGN,MAAA;IACA;EAAA,IACC,qBAAA;EAWH,KAAA,CAAA;EAcA,iBAAA,CAAA;EAgBA,OAAA,GAAA,CAAW,GAAA,UAAa,YAAA,UAAqB,CAAA,UAAkB,CAAA;EAyB/D,UAAA,CAAW,GAAA;EAWX,OAAA,GAAA,CAAW,GAAA,UAAa,KAAA,EAAO,CAAA,EAAG,GAAA;EAAA,QAgB1B,UAAA;AAAA;;;UCrGO,iBAAA;EACf,QAAA;EACA,UAAA;EACA,MAAA;AAAA;;;iBCsBc,UAAA,CACd,GAAA,UACA,OAAA,GAAS,iBAAA;;;iBCNK,oBAAA,CAAqB,IAAA;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/storage/index.ts","../src/dom/selector.ts","../src/dom/creation.ts","../src/dom/class.ts","../src/dom/style.ts","../src/dom/viewport.ts","../src/dom/event.ts","../src/clipboard/index.ts","../src/cache/storage-manager.ts","../src/window/types.ts","../src/window/openWindow.ts","../src/window/openRouteInNewWindow.ts","../src/window/getURLSearchParams.ts","../src/window/setURLSearchParams.ts","../src/download/types.ts","../src/download/constants.ts","../src/download/downloadFileFromUrl.ts","../src/download/downloadFileFromBase64.ts","../src/download/downloadFileFromImageUrl.ts","../src/download/downloadFileFromBlob.ts","../src/download/downloadFileFromBlobPart.ts","../src/download/urlToBase64.ts","../src/download/triggerDownload.ts","../src/binary/blobToArrayBuffer.ts","../src/binary/arrayBufferToBlob.ts","../src/binary/arrayBufferToBase64.ts","../src/binary/base64ToBlob.ts","../src/binary/blobToBase64.ts","../src/binary/blobToObjectURL.ts","../src/binary/blobToDataURL.ts"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e from"copy-text-to-clipboard";function t(e){try{let t=window.localStorage.getItem(e);return t?JSON.parse(t):null}catch(e){return console.error(`Error reading from localStorage: ${e}`),null}}function n(e,t){try{return window.localStorage.setItem(e,JSON.stringify(t)),!0}catch(e){return console.error(`Error writing to localStorage: ${e}`),!1}}function r(e){try{return window.localStorage.removeItem(e),!0}catch(e){return console.error(`Error removing from localStorage: ${e}`),!1}}function i(){try{return window.localStorage.clear(),!0}catch(e){return console.error(`Error clearing localStorage: ${e}`),!1}}function a(e){try{let t=window.sessionStorage.getItem(e);return t?JSON.parse(t):null}catch(e){return console.error(`Error reading from sessionStorage: ${e}`),null}}function o(e,t){try{return window.sessionStorage.setItem(e,JSON.stringify(t)),!0}catch(e){return console.error(`Error writing to sessionStorage: ${e}`),!1}}function s(e){try{return window.sessionStorage.removeItem(e),!0}catch(e){return console.error(`Error removing from sessionStorage: ${e}`),!1}}function c(e){return document.querySelector(e)}function l(e){return Array.from(document.querySelectorAll(e))}function u(e,t,n){let r=document.createElement(e);if(t)for(let[e,n]of Object.entries(t))r.setAttribute(e,n);if(n)for(let e of n)typeof e==`string`?r.appendChild(document.createTextNode(e)):r.appendChild(e);return r}function d(e,...t){e.classList.add(...t)}function f(e,...t){e.classList.remove(...t)}function p(e,t){return e.classList.toggle(t)}function m(e,t){return window.getComputedStyle(e).getPropertyValue(t)}function h(e,t){for(let[n,r]of Object.entries(t))e.style.setProperty(n,r)}function g(e){let t=e.getBoundingClientRect();return t.top>=0&&t.left>=0&&t.bottom<=(window.innerHeight||document.documentElement.clientHeight)&&t.right<=(window.innerWidth||document.documentElement.clientWidth)}function _(e,t){e.scrollIntoView({behavior:`smooth`,...t})}function v(e,t,n,r=!1){e&&t&&n&&e.addEventListener(t,n,r)}function y(e,t,n,r=!1){e&&t&&n&&e.removeEventListener(t,n,r)}function b(e,t,n,r=!1){if(!e||!t||!n)return;let i=a=>{typeof n==`function`?n(a):n.handleEvent&&n.handleEvent(a),e.removeEventListener(t,i,r)};e.addEventListener(t,i,r)}function x(t,n){return e(t,n)}var S=class{constructor({prefix:e=``,storageType:t=`localStorage`}={}){this.prefix=e,this.storage=t===`localStorage`?window.localStorage:window.sessionStorage}clear(){let e=[];for(let t=0;t<this.storage.length;t++){let n=this.storage.key(t);n&&n.startsWith(this.prefix)&&e.push(n)}e.forEach(e=>this.storage.removeItem(e))}clearExpiredItems(){for(let e=0;e<this.storage.length;e++){let t=this.storage.key(e);if(t&&t.startsWith(this.prefix)){let e=t.replace(this.prefix,``);this.getItem(e)}}}getItem(e,t=null){let n=this.getFullKey(e),r=this.storage.getItem(n);if(!r)return t;try{let e=JSON.parse(r);return e.expiry&&Date.now()>e.expiry?(this.storage.removeItem(n),t):e.value}catch(e){return console.error(`Error parsing item with key "${n}":`,e),this.storage.removeItem(n),t}}removeItem(e){let t=this.getFullKey(e);this.storage.removeItem(t)}setItem(e,t,n){let r=this.getFullKey(e),i={expiry:n?Date.now()+n:void 0,value:t};try{this.storage.setItem(r,JSON.stringify(i))}catch(e){console.error(`Error setting item with key "${r}":`,e)}}getFullKey(e){return`${this.prefix}-${e}`}};function C(e,t={}){let{noopener:n=!0,noreferrer:r=!0,target:i=`_blank`}=t,a=[n&&`noopener=yes`,r&&`noreferrer=yes`].filter(Boolean).join(`,`);window.open(e,i,a)}function w(e){let{hash:t,origin:n}=location,r=e.startsWith(`/`)?e:`/${e}`;C(`${n}${t&&!r.startsWith(`/#`)?`/#`:``}${r}`,{target:`_blank`})}const
|
|
1
|
+
import e from"copy-text-to-clipboard";function t(e){try{let t=window.localStorage.getItem(e);return t?JSON.parse(t):null}catch(e){return console.error(`Error reading from localStorage: ${e}`),null}}function n(e,t){try{return window.localStorage.setItem(e,JSON.stringify(t)),!0}catch(e){return console.error(`Error writing to localStorage: ${e}`),!1}}function r(e){try{return window.localStorage.removeItem(e),!0}catch(e){return console.error(`Error removing from localStorage: ${e}`),!1}}function i(){try{return window.localStorage.clear(),!0}catch(e){return console.error(`Error clearing localStorage: ${e}`),!1}}function a(e){try{let t=window.sessionStorage.getItem(e);return t?JSON.parse(t):null}catch(e){return console.error(`Error reading from sessionStorage: ${e}`),null}}function o(e,t){try{return window.sessionStorage.setItem(e,JSON.stringify(t)),!0}catch(e){return console.error(`Error writing to sessionStorage: ${e}`),!1}}function s(e){try{return window.sessionStorage.removeItem(e),!0}catch(e){return console.error(`Error removing from sessionStorage: ${e}`),!1}}function c(e){return document.querySelector(e)}function l(e){return Array.from(document.querySelectorAll(e))}function u(e,t,n){let r=document.createElement(e);if(t)for(let[e,n]of Object.entries(t))r.setAttribute(e,n);if(n)for(let e of n)typeof e==`string`?r.appendChild(document.createTextNode(e)):r.appendChild(e);return r}function d(e,...t){e.classList.add(...t)}function f(e,...t){e.classList.remove(...t)}function p(e,t){return e.classList.toggle(t)}function m(e,t){return window.getComputedStyle(e).getPropertyValue(t)}function h(e,t){for(let[n,r]of Object.entries(t))e.style.setProperty(n,r)}function g(e){let t=e.getBoundingClientRect();return t.top>=0&&t.left>=0&&t.bottom<=(window.innerHeight||document.documentElement.clientHeight)&&t.right<=(window.innerWidth||document.documentElement.clientWidth)}function _(e,t){e.scrollIntoView({behavior:`smooth`,...t})}function v(e,t,n,r=!1){e&&t&&n&&e.addEventListener(t,n,r)}function y(e,t,n,r=!1){e&&t&&n&&e.removeEventListener(t,n,r)}function b(e,t,n,r=!1){if(!e||!t||!n)return;let i=a=>{typeof n==`function`?n(a):n.handleEvent&&n.handleEvent(a),e.removeEventListener(t,i,r)};e.addEventListener(t,i,r)}function x(t,n){return e(t,n)}var S=class{constructor({prefix:e=``,storageType:t=`localStorage`}={}){this.prefix=e,this.storage=t===`localStorage`?window.localStorage:window.sessionStorage}clear(){let e=[];for(let t=0;t<this.storage.length;t++){let n=this.storage.key(t);n&&n.startsWith(this.prefix)&&e.push(n)}e.forEach(e=>this.storage.removeItem(e))}clearExpiredItems(){for(let e=0;e<this.storage.length;e++){let t=this.storage.key(e);if(t&&t.startsWith(this.prefix)){let e=t.replace(this.prefix,``);this.getItem(e)}}}getItem(e,t=null){let n=this.getFullKey(e),r=this.storage.getItem(n);if(!r)return t;try{let e=JSON.parse(r);return e.expiry&&Date.now()>e.expiry?(this.storage.removeItem(n),t):e.value}catch(e){return console.error(`Error parsing item with key "${n}":`,e),this.storage.removeItem(n),t}}removeItem(e){let t=this.getFullKey(e);this.storage.removeItem(t)}setItem(e,t,n){let r=this.getFullKey(e),i={expiry:n?Date.now()+n:void 0,value:t};try{this.storage.setItem(r,JSON.stringify(i))}catch(e){console.error(`Error setting item with key "${r}":`,e)}}getFullKey(e){return`${this.prefix}-${e}`}};function C(e,t={}){let{noopener:n=!0,noreferrer:r=!0,target:i=`_blank`}=t,a=[n&&`noopener=yes`,r&&`noreferrer=yes`].filter(Boolean).join(`,`);window.open(e,i,a)}function w(e){let{hash:t,origin:n}=location,r=e.startsWith(`/`)?e:`/${e}`;C(`${n}${t&&!r.startsWith(`/#`)?`/#`:``}${r}`,{target:`_blank`})}function T(e){let t;t=e?new URL(e).search:window.location.search;let n=t.startsWith(`?`)?t.slice(1):t;if(typeof URLSearchParams<`u`){let e=new URLSearchParams(n),t={};return e.forEach((e,n)=>{t[n]=e}),t}let r={};if(!n)return r;let i=n.split(`&`);for(let e of i){let[t,n=``]=e.split(`=`);if(!t)continue;let i=decodeURIComponent(t.replace(/\+/g,` `));r[i]=decodeURIComponent(n.replace(/\+/g,` `))}return r}function E(e,t){let n=e||window.location.href,r=new URL(n);if(!t)return r.toString();if(typeof URLSearchParams<`u`){let e=r.searchParams;for(let[n,r]of Object.entries(t))r===void 0?e.delete(n):e.set(n,r);return r.toString()}let i=r.search.slice(1),a=i?i.split(`&`):[],o={};for(let e of a){let[t,n=``]=e.split(`=`);t&&(o[t]=decodeURIComponent(n.replace(/\+/g,` `)))}for(let[e,n]of Object.entries(t))n===void 0?delete o[e]:o[e]=n;let s=Object.entries(o).map(([e,t])=>`${encodeURIComponent(e)}=${encodeURIComponent(t)}`).join(`&`);return r.search=s?`?${s}`:``,r.toString()}const D=`downloaded_file`;function O(e,t,n=100){let r=t||`downloaded_file`,i=document.createElement(`a`);i.href=e,i.download=r,i.style.display=`none`,i.download===void 0&&i.setAttribute(`target`,`_blank`),document.body.append(i),i.click(),i.remove(),setTimeout(()=>URL.revokeObjectURL(e),n)}function k(e,t){return t||e.slice(e.lastIndexOf(`/`)+1)||D}async function A({fileName:e,source:t,target:n=`_blank`}){if(!t||typeof t!=`string`)throw Error(`Invalid URL.`);let r=window.navigator.userAgent.toLowerCase().includes(`chrome`),i=window.navigator.userAgent.toLowerCase().includes(`safari`);if(/iP/.test(window.navigator.userAgent)){console.error(`Your browser does not support download!`);return}if(r||i){O(t,k(t,e));return}t.includes(`?`)||(t+=`?download`),C(t,{target:n})}function j({fileName:e,source:t}){if(!t||typeof t!=`string`)throw Error(`Invalid Base64 data.`);O(t,e||D)}function M(e,t){return new Promise((n,r)=>{let i=document.createElement(`CANVAS`),a=i?.getContext(`2d`),o=new Image;o.crossOrigin=``,o.addEventListener(`load`,()=>{if(!i||!a)return r(Error(`Failed to create canvas.`));i.height=o.height,i.width=o.width,a.drawImage(o,0,0);let e=i.toDataURL(t||`image/png`);i=null,n(e)}),o.src=e})}async function N({fileName:e,source:t}){j({fileName:e,source:await M(t)})}function P({fileName:e=D,source:t}){if(!(t instanceof Blob))throw TypeError(`Invalid Blob data.`);O(URL.createObjectURL(t),e)}function F({fileName:e=D,source:t}){let n=t instanceof Blob?t:new Blob([t],{type:`application/octet-stream`});O(URL.createObjectURL(n),e)}async function I(e){return typeof e.arrayBuffer==`function`?await e.arrayBuffer():new Promise((t,n)=>{let r=new FileReader;r.onload=()=>{r.result instanceof ArrayBuffer?t(r.result):n(Error(`FileReader did not return an ArrayBuffer`))},r.onerror=()=>{n(Error(`Failed to read Blob: ${r.error?.message||`Unknown error`}`))},r.readAsArrayBuffer(e)})}function L(e,t){return new Blob([e],t)}function R(e){let t=new Uint8Array(e),n=``,r=8192;for(let e=0;e<t.length;e+=r){let i=t.subarray(e,e+r);n+=String.fromCharCode.apply(null,Array.from(i))}return btoa(n)}function z(e,t,n=8192){let r=atob(e),i=[];for(let e=0;e<r.length;e+=n){let t=r.slice(e,e+n),a=new Uint8Array(t.length);for(let e=0;e<t.length;e++)a[e]=t.charCodeAt(e);i.push(a)}return new Blob(i,t)}async function B(e){return R(await I(e))}function V(e){return URL.createObjectURL(e)}async function H(e){return new Promise((t,n)=>{let r=new FileReader;r.onload=()=>{typeof r.result==`string`?t(r.result):n(Error(`FileReader did not return a Data URL`))},r.onerror=()=>{n(Error(`Failed to read Blob: ${r.error?.message||`Unknown error`}`))},r.readAsDataURL(e)})}export{c as $,l as $$,D as DEFAULT_FILENAME,S as StorageManager,d as addClass,R as arrayBufferToBase64,L as arrayBufferToBlob,z as base64ToBlob,I as blobToArrayBuffer,B as blobToBase64,H as blobToDataURL,V as blobToObjectURL,i as clearLocalStorage,x as copyToClipboard,u as createElement,j as downloadFileFromBase64,P as downloadFileFromBlob,F as downloadFileFromBlobPart,N as downloadFileFromImageUrl,A as downloadFileFromUrl,t as getLocalStorage,a as getSessionStorage,m as getStyle,T as getURLSearchParams,g as isInViewport,y as off,v as on,b as once,w as openRouteInNewWindow,C as openWindow,f as removeClass,r as removeLocalStorage,s as removeSessionStorage,_ as scrollIntoView,n as setLocalStorage,o as setSessionStorage,h as setStyle,E as setURLSearchParams,p as toggleClass,O as triggerDownload,M as urlToBase64};
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/storage/index.ts","../src/dom/selector.ts","../src/dom/creation.ts","../src/dom/class.ts","../src/dom/style.ts","../src/dom/viewport.ts","../src/dom/event.ts","../src/clipboard/index.ts","../src/cache/storage-manager.ts","../src/window/openWindow.ts","../src/window/openRouteInNewWindow.ts","../src/download/constants.ts","../src/download/triggerDownload.ts","../src/download/utils.ts","../src/download/downloadFileFromUrl.ts","../src/download/downloadFileFromBase64.ts","../src/download/urlToBase64.ts","../src/download/downloadFileFromImageUrl.ts","../src/download/downloadFileFromBlob.ts","../src/download/downloadFileFromBlobPart.ts"],"sourcesContent":["/**\n * Storage utilities using localStorage and sessionStorage\n */\n\n/**\n * Gets an item from localStorage\n */\nexport function getLocalStorage<T = any>(key: string): T | null {\n try {\n const item = window.localStorage.getItem(key)\n return item ? JSON.parse(item) : null\n } catch (error) {\n console.error(`Error reading from localStorage: ${error}`)\n return null\n }\n}\n\n/**\n * Sets an item in localStorage\n */\nexport function setLocalStorage<T>(key: string, value: T): boolean {\n try {\n window.localStorage.setItem(key, JSON.stringify(value))\n return true\n } catch (error) {\n console.error(`Error writing to localStorage: ${error}`)\n return false\n }\n}\n\n/**\n * Removes an item from localStorage\n */\nexport function removeLocalStorage(key: string): boolean {\n try {\n window.localStorage.removeItem(key)\n return true\n } catch (error) {\n console.error(`Error removing from localStorage: ${error}`)\n return false\n }\n}\n\n/**\n * Clears all items from localStorage\n */\nexport function clearLocalStorage(): boolean {\n try {\n window.localStorage.clear()\n return true\n } catch (error) {\n console.error(`Error clearing localStorage: ${error}`)\n return false\n }\n}\n\n/**\n * Gets an item from sessionStorage\n */\nexport function getSessionStorage<T = any>(key: string): T | null {\n try {\n const item = window.sessionStorage.getItem(key)\n return item ? JSON.parse(item) : null\n } catch (error) {\n console.error(`Error reading from sessionStorage: ${error}`)\n return null\n }\n}\n\n/**\n * Sets an item in sessionStorage\n */\nexport function setSessionStorage<T>(key: string, value: T): boolean {\n try {\n window.sessionStorage.setItem(key, JSON.stringify(value))\n return true\n } catch (error) {\n console.error(`Error writing to sessionStorage: ${error}`)\n return false\n }\n}\n\n/**\n * Removes an item from sessionStorage\n */\nexport function removeSessionStorage(key: string): boolean {\n try {\n window.sessionStorage.removeItem(key)\n return true\n } catch (error) {\n console.error(`Error removing from sessionStorage: ${error}`)\n return false\n }\n}\n","/**\n * 选择器工具模块\n * 提供 DOM 元素选择功能\n */\n\n/**\n * 通过 CSS 选择器获取单个 DOM 元素\n *\n * @param selector - CSS 选择器字符串 (例如 '.class', '#id', 'div > p')\n * @returns 第一个匹配的 DOM 元素,如果未找到则返回 null\n *\n * @example 基础选择器\n * ```typescript\n * const header = $('header');\n * const nav = $('nav');\n * const footer = $('footer');\n * ```\n *\n * @example 类选择器和 ID 选择器\n * ```typescript\n * const activeItem = $('.menu-item.active');\n * const submitButton = $('#submit-btn');\n * const firstCard = $('.card:first-child');\n * ```\n *\n * @example 属性选择器和后代选择器\n * ```typescript\n * const inputText = $('input[type=\"text\"]');\n * const navLink = $('nav a.external');\n * const checkedCheckbox = $('input[type=\"checkbox\"]:checked');\n * ```\n *\n * @example 空值检查\n * ```typescript\n * const element = $('header');\n * if (element) {\n * console.log(element.textContent);\n * }\n * ```\n */\nexport function $(selector: string): HTMLElement | null {\n return document.querySelector(selector)\n}\n\n/**\n * 通过 CSS 选择器获取所有匹配的 DOM 元素\n *\n * @param selector - CSS 选择器字符串 (例如 '.class', '#id', 'div > p')\n * @returns 匹配的 DOM 元素数组\n *\n * @example 收集元素\n * ```typescript\n * const buttons = $$('button');\n * const links = $$('.nav a');\n * const inputs = $$('input');\n * ```\n *\n * @example 遍历元素\n * ```typescript\n * const cards = $$('.card');\n * cards.forEach(card => {\n * card.style.opacity = '0.8';\n * });\n * ```\n *\n * @example 过滤和处理元素\n * ```typescript\n * const images = $$('img');\n * const largeImages = images.filter(img => {\n * const width = parseInt(img.getAttribute('width') || '0');\n * return width > 300;\n * });\n * ```\n *\n * @example 多选择器组合\n * ```typescript\n * const focusableElements = $$('button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])');\n * ```\n */\nexport function $$(selector: string): HTMLElement[] {\n return Array.from(document.querySelectorAll(selector))\n}\n","/**\n * 元素创建工具模块\n * 提供 DOM 元素创建功能\n */\n\n/**\n * 创建 DOM 元素,可指定属性和子元素\n *\n * @param tag - HTML 标签名 (例如 'div', 'span', 'a')\n * @param attributes - 可选的属性键值对对象\n * @param children - 可选的子元素或文本节点数组\n * @returns 创建的 DOM 元素\n *\n * @example 基础元素创建\n * ```typescript\n * // 创建一个简单的 div\n * const div = createElement('div');\n *\n * // 创建带文本的 span\n * const span = createElement('span', {}, ['Hello, World!']);\n *\n * // 创建图片元素\n * const img = createElement('img', {\n * src: 'image.jpg',\n * alt: 'Image description',\n * width: '200'\n * });\n * ```\n *\n * @example 表单元素\n * ```typescript\n * // 创建输入框\n * const input = createElement('input', {\n * type: 'text',\n * placeholder: 'Enter your name',\n * name: 'username'\n * });\n *\n * // 创建带多个属性的按钮\n * const button = createElement('button', {\n * id: 'submit',\n * type: 'submit',\n * class: 'btn btn-primary',\n * 'data-action': 'submit-form'\n * }, ['Submit']);\n * ```\n *\n * @example 复杂的嵌套结构\n * ```typescript\n * // 创建带头部、主体和底部的卡片\n * const card = createElement('div', { class: 'card' }, [\n * createElement('div', { class: 'card-header' }, [\n * createElement('h3', {}, ['Card Title']),\n * createElement('button', { class: 'close' }, ['×'])\n * ]),\n * createElement('div', { class: 'card-body' }, [\n * createElement('p', {}, ['This is the card content'])\n * ]),\n * createElement('div', { class: 'card-footer' }, [\n * createElement('button', {}, ['Cancel']),\n * createElement('button', { class: 'primary' }, ['Save'])\n * ])\n * ]);\n * ```\n *\n * @example 列表和表格\n * ```typescript\n * // 创建无序列表\n * const ul = createElement('ul', {}, [\n * createElement('li', {}, ['First item']),\n * createElement('li', {}, ['Second item']),\n * createElement('li', {}, ['Third item'])\n * ]);\n *\n * // 创建表格行\n * const tr = createElement('tr', {}, [\n * createElement('td', {}, ['John']),\n * createElement('td', {}, ['30']),\n * createElement('td', {}, ['john@example.com'])\n * ]);\n * ```\n */\nexport function createElement<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n attributes?: Record<string, string>,\n children?: (string | Node)[]\n): HTMLElementTagNameMap[K] {\n const element = document.createElement(tag)\n\n if (attributes) {\n for (const [key, value] of Object.entries(attributes)) {\n element.setAttribute(key, value)\n }\n }\n\n if (children) {\n for (const child of children) {\n if (typeof child === 'string') {\n element.appendChild(document.createTextNode(child))\n } else {\n element.appendChild(child)\n }\n }\n }\n\n return element\n}\n","/**\n * 类名操作工具模块\n * 提供 DOM 元素的类名增删改功能\n * 底层的 classList.add() 方法原生支持两种方式:\n * 接受多个独立的字符串参数\n * 接受一个包含空格分隔的字符串(会自动拆分)\n *\n * 虽然当前实现两种方式都能工作,但从代码清晰度和TypeScript 类型安全的角度考虑:\n * 推荐的用法是使用多个独立参数:\n * ```typescript\n * addClass(element, 'class1', 'class2');\n * ```\n * 原因是:\n *\n * 当前函数签名使用的是剩余参数 ...classNames: string[],设计意图就是接收多个独立字符串\n * 独立参数的方式在 TypeScript 中有更好的类型提示和智能感知\n * 代码意图更明确,便于阅读和维护\n *\n * 因为底层调用的是浏览器原生的 classList.add() 方法,该方法具有以下特性:\n * 如果类名已存在,不会重复添加\n * 即使你多次传入相同的类名,DOM 中最终只会保留一个\n *\n * 说明哪些情况下的元素/对象没有 classList 属性\n * 1. 基本节点类型(Node 的子类): 例如 Text、Comment、Document、Fragment 等\n * 2. SVG 元素 : SVG 元素不是 HTMLElement,而是 SVGElement,早期浏览器可能不支持 classList\n * 3. 非元素节点: 例如 Text、Comment、Document、DocumentFragment 等\n * 4. 全局对象: 例如 window、document 等\n * 5. 自定义或非标准对象\n *\n * 完整的继承关系图\n * Node (没有 classList)\n * ├── Document (没有 classList)\n * ├── DocumentFragment (没有 classList)\n * ├── CharacterData (没有 classList)\n * │ ├── Text (没有 classList)\n * │ └── Comment (没有 classList)\n * └── Element (有 classList - 在现代浏览器中)\n * ├── HTMLElement (有 classList)\n * │ ├── HTMLDivElement\n * │ ├── HTMLButtonElement\n * │ └── ...\n * └── SVGElement (有 classList - 现代浏览器)\n */\n\n/**\n * 向元素添加一个或多个 CSS 类\n *\n * @param element - 要添加类的 DOM 元素\n * @param classNames - 一个或多个要添加的类名\n *\n * @example 添加单个或多个类\n * ```typescript\n * const button = $('button');\n * addClass(button, 'active');\n * addClass(button, 'highlight', 'primary');\n * ```\n *\n * @example 动态添加类\n * ```typescript\n * const card = $('.card');\n * if (card) {\n * const isFeatured = card.dataset.featured === 'true';\n * if (isFeatured) {\n * addClass(card, 'featured', 'highlight');\n * }\n * }\n * ```\n *\n * @example 批量处理元素\n * ```typescript\n * const buttons = $$('button');\n * buttons.forEach(button => {\n * addClass(button, 'btn', 'btn-primary');\n * });\n * ```\n *\n * @example 条件样式\n * ```typescript\n * const alerts = $$('.alert');\n * alerts.forEach(alert => {\n * const type = alert.dataset.type;\n * if (type === 'error') addClass(alert, 'alert-error');\n * if (type === 'warning') addClass(alert, 'alert-warning');\n * if (type === 'success') addClass(alert, 'alert-success');\n * });\n * ```\n */\nexport function addClass(element: HTMLElement, ...classNames: string[]): void {\n element.classList.add(...classNames)\n}\n\n/**\n * 从元素中移除一个或多个 CSS 类\n *\n * @param element - 要移除类的 DOM 元素\n * @param classNames - 一个或多个要移除的类名\n *\n * @example 移除单个或多个类\n * ```typescript\n * const button = $('button');\n * removeClass(button, 'active');\n * removeClass(button, 'highlight', 'primary');\n * ```\n *\n * @example 重置元素状态\n * ```typescript\n * const modal = $('.modal');\n * if (modal) {\n * removeClass(modal, 'open', 'visible');\n * addClass(modal, 'closed');\n * }\n * ```\n *\n * @example 基于切换的移除\n * ```typescript\n * const menu = $('.menu');\n * if (menu) {\n * if (menu.classList.contains('active')) {\n * removeClass(menu, 'active');\n * }\n * }\n * ```\n *\n * @example 清理动态类\n * ```typescript\n * const container = $('.container');\n * if (container) {\n * // 移除所有工具类\n * removeClass(container, 'mt-2', 'mb-4', 'p-4', 'bg-gray-100');\n * // 保留基础类\n * addClass(container, 'base-style');\n * }\n * ```\n */\nexport function removeClass(element: HTMLElement, ...classNames: string[]): void {\n element.classList.remove(...classNames)\n}\n\n/**\n * 切换元素的 CSS 类(存在则移除,不存在则添加)\n *\n * @param element - 要切换类的 DOM 元素\n * @param className - 要切换的类名\n * @returns 如果类被添加则返回 true,如果被移除则返回 false\n *\n * @example 简单切换\n * ```typescript\n * const button = $('button');\n * const isActive = toggleClass(button, 'active');\n * console.log('Active:', isActive); // 添加时为 true,移除时为 false\n * ```\n *\n * @example 事件处理器切换\n * ```typescript\n * const menuToggle = $('#menu-toggle');\n * const menu = $('#menu');\n *\n * menuToggle?.addEventListener('click', () => {\n * if (menu) {\n * const isOpen = toggleClass(menu, 'open');\n * if (isOpen) {\n * addClass(menuToggle, 'active');\n * } else {\n * removeClass(menuToggle, 'active');\n * }\n * }\n * });\n * ```\n *\n * @example 表单验证反馈\n * ```typescript\n * const emailInput = $('input[type=\"email\"]');\n *\n * emailInput?.addEventListener('input', () => {\n * const isValid = emailInput.value.includes('@');\n * toggleClass(emailInput, 'valid', isValid);\n * toggleClass(emailInput, 'invalid', !isValid);\n * });\n * ```\n *\n * @example 多元素切换\n * ```typescript\n * const buttons = $$('.tab-button');\n * const panels = $$('.tab-panel');\n *\n * buttons.forEach((button, index) => {\n * button.addEventListener('click', () => {\n * // 切换按钮的激活状态\n * buttons.forEach(btn => removeClass(btn, 'active'));\n * addClass(button, 'active');\n *\n * // 切换面板的可见性\n * panels.forEach((panel, i) => {\n * toggleClass(panel, 'active', i === index);\n * });\n * });\n * });\n * ```\n */\nexport function toggleClass(element: HTMLElement, className: string): boolean {\n return element.classList.toggle(className)\n}\n","/**\n * 样式操作工具模块\n * 提供 DOM 元素的样式读写功能\n */\n\n/**\n * 获取元素指定 CSS 属性的计算值\n *\n * @param element - 要获取样式的 DOM 元素\n * @param property - CSS 属性名 (例如 'color', 'font-size')\n * @returns CSS 属性的计算值\n *\n * @example 基础样式获取\n * ```typescript\n * const header = $('header');\n * const bgColor = getStyle(header, 'background-color');\n * const fontSize = getStyle(header, 'font-size');\n * const color = getStyle(header, 'color');\n * ```\n *\n * @example 响应式设计检查\n * ```typescript\n * const element = $('.responsive-element');\n * if (element) {\n * const display = getStyle(element, 'display');\n * const isVisible = display !== 'none';\n *\n * if (isVisible) {\n * const width = getStyle(element, 'width');\n * console.log('Element width:', width);\n * }\n * }\n * ```\n *\n * @example 比较和逻辑判断\n * ```typescript\n * const header = $('header');\n * const footer = $('footer');\n *\n * if (header && footer) {\n * const headerHeight = parseInt(getStyle(header, 'height'));\n * const footerHeight = parseInt(getStyle(footer, 'height'));\n *\n * if (headerHeight > footerHeight) {\n * console.log('Header is taller than footer');\n * }\n * }\n * ```\n *\n * @example 颜色处理\n * ```typescript\n * const button = $('button');\n * if (button) {\n * const bgColor = getStyle(button, 'background-color');\n * const textColor = getStyle(button, 'color');\n *\n * // 转换为 RGB 并调整亮度\n * const rgb = bgColor.match(/\\d+/g);\n * if (rgb) {\n * const brightness = (parseInt(rgb[0]) * 299 + parseInt(rgb[1]) * 587 + parseInt(rgb[2]) * 114) / 1000;\n * const shouldUseLightText = brightness < 128;\n * }\n * }\n * ```\n */\nexport function getStyle(element: HTMLElement, property: string): string {\n return window.getComputedStyle(element).getPropertyValue(property)\n}\n\n/**\n * 为元素设置多个 CSS 属性\n *\n * @param element - 要应用样式的 DOM 元素\n * @param styles - CSS 属性键值对对象\n *\n * @example 基础样式设置\n * ```typescript\n * const header = $('header');\n * setStyle(header, {\n * 'color': '#333',\n * 'font-size': '16px',\n * 'background-color': '#f5f5f5'\n * });\n * ```\n *\n * @example 动画和过渡效果\n * ```typescript\n * const modal = $('.modal');\n * if (modal) {\n * setStyle(modal, {\n * 'opacity': '0',\n * 'transform': 'translateY(-20px)',\n * 'transition': 'opacity 0.3s ease, transform 0.3s ease'\n * });\n *\n * setTimeout(() => {\n * setStyle(modal, {\n * 'opacity': '1',\n * 'transform': 'translateY(0)'\n * });\n * }, 100);\n * }\n * ```\n *\n * @example 响应式和动态样式\n * ```typescript\n * const card = $('.card');\n * if (card) {\n * const isDark = document.documentElement.classList.contains('dark');\n * setStyle(card, {\n * 'background-color': isDark ? '#1f2937' : '#ffffff',\n * 'color': isDark ? '#f9fafb' : '#111827',\n * 'box-shadow': isDark ? '0 4px 6px rgba(0, 0, 0, 0.5)' : '0 4px 6px rgba(0, 0, 0, 0.1)'\n * });\n * }\n * ```\n *\n * @example 动态定位\n * ```typescript\n * const tooltip = $('.tooltip');\n * const trigger = $('.trigger');\n *\n * if (tooltip && trigger) {\n * const triggerRect = trigger.getBoundingClientRect();\n * setStyle(tooltip, {\n * 'position': 'absolute',\n * 'top': `${triggerRect.bottom + 10}px`,\n * 'left': `${triggerRect.left}px`,\n * 'z-index': '1000'\n * });\n * }\n * ```\n */\nexport function setStyle(element: HTMLElement, styles: Record<string, string>): void {\n for (const [property, value] of Object.entries(styles)) {\n element.style.setProperty(property, value)\n }\n}\n","/**\n * 视口和滚动工具模块\n * 提供元素可视性检查和滚动控制功能\n */\n\n/**\n * 检查元素是否完全可见于视口中\n *\n * @param element - 要检查的 DOM 元素\n * @returns 如果元素在视口中完全可见则返回 true,否则返回 false\n *\n * @example 基础可见性检查\n * ```typescript\n * const footer = $('footer');\n * if (isInViewport(footer)) {\n * console.log('Footer is visible');\n * }\n * ```\n *\n * @example 懒加载内容\n * ```typescript\n * const images = $$('img[data-src]');\n *\n * function loadVisibleImages() {\n * images.forEach(img => {\n * if (isInViewport(img) && !img.src) {\n * img.src = img.dataset.src;\n * delete img.dataset.src;\n * }\n * });\n * }\n *\n * window.addEventListener('scroll', loadVisibleImages);\n * window.addEventListener('load', loadVisibleImages);\n * ```\n *\n * @example 无限滚动\n * ```typescript\n * const sentinel = $('.sentinel');\n *\n * function loadMoreContent() {\n * if (sentinel && isInViewport(sentinel)) {\n * console.log('Load more content...');\n * // 获取并追加新内容\n * }\n * }\n *\n * window.addEventListener('scroll', () => {\n * requestAnimationFrame(loadMoreContent);\n * });\n * ```\n *\n * @example 统计和跟踪\n * ```typescript\n * const sections = $$('section[data-track]');\n *\n * const observer = new IntersectionObserver((entries) => {\n * entries.forEach(entry => {\n * if (entry.isIntersecting) {\n * const section = entry.target;\n * const trackId = section.dataset.track;\n * console.log('Section viewed:', trackId);\n * // 发送统计事件\n * }\n * });\n * }, { threshold: 0.5 });\n *\n * sections.forEach(section => {\n * if (isInViewport(section)) {\n * const trackId = section.dataset.track;\n * console.log('Initial view:', trackId);\n * }\n * observer.observe(section);\n * });\n * ```\n */\nexport function isInViewport(element: HTMLElement): boolean {\n const rect = element.getBoundingClientRect()\n return (\n rect.top >= 0 &&\n rect.left >= 0 &&\n rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&\n rect.right <= (window.innerWidth || document.documentElement.clientWidth)\n )\n}\n\n/**\n * 将指定元素滚动到浏览器窗口的可见区域\n *\n * @param element - 要滚动到可见区域的 DOM 元素\n * @param options - 可选的滚动行为选项 (默认为平滑滚动)\n *\n * @example 基础滚动\n * ```typescript\n * const section = $('section');\n * scrollIntoView(section);\n * ```\n *\n * @example 不同的滚动位置\n * ```typescript\n * const section = $('section');\n * scrollIntoView(section);\n *\n * // 滚动到顶部\n * scrollIntoView(section, { block: 'start' });\n *\n * // 滚动到中间\n * scrollIntoView(section, { block: 'center' });\n *\n * // 滚动到底部\n * scrollIntoView(section, { block: 'end' });\n * ```\n *\n * @example 导航和锚点链接\n * ```typescript\n * const navLinks = $$('.nav a');\n *\n * navLinks.forEach(link => {\n * link.addEventListener('click', (e) => {\n * e.preventDefault();\n * const targetId = link.getAttribute('href');\n * const target = $(targetId);\n * if (target) {\n * scrollIntoView(target, { block: 'start' });\n * }\n * });\n * });\n * ```\n *\n * @example 表单验证反馈\n * ```typescript\n * const form = $('form');\n * const submitButton = $('button[type=\"submit\"]');\n *\n * form?.addEventListener('submit', (e) => {\n * e.preventDefault();\n *\n * const invalidInputs = $$('input:invalid');\n * if (invalidInputs.length > 0) {\n * // 滚动到第一个无效输入框\n * scrollIntoView(invalidInputs[0], { block: 'center' });\n *\n * // 添加错误高亮\n * invalidInputs.forEach(input => {\n * addClass(input, 'error');\n * });\n * }\n * });\n * ```\n *\n * @example 目录导航\n * ```typescript\n * const tocLinks = $$('.toc a');\n *\n * tocLinks.forEach(link => {\n * link.addEventListener('click', (e) => {\n * e.preventDefault();\n *\n * const targetId = link.getAttribute('href');\n * const target = $(targetId);\n * if (target) {\n * scrollIntoView(target, {\n * block: 'start',\n * inline: 'nearest',\n * behavior: 'smooth'\n * });\n * }\n * });\n * });\n * ```\n */\nexport function scrollIntoView(element: HTMLElement, options?: ScrollIntoViewOptions): void {\n element.scrollIntoView({ behavior: 'smooth', ...options })\n}\n","/**\n * 事件监听工具模块\n * 提供 DOM 元素的事件绑定、解绑和一次性监听功能\n *\n * 支持的事件目标类型:\n * - HTMLElement: DOM 元素节点\n * - Document: 文档对象\n * - Window: 窗口对象\n *\n * 注意事项:\n * - 使用相同参数多次调用 on() 会添加多个监听器,而不是覆盖\n * - 移除监听器时,参数必须与添加时完全一致(包括 useCapture)\n * - once() 监听器在触发后会自动移除,无需手动解绑\n * - 使用事件对象处理函数时,需要确保 handleEvent 方法正确实现\n */\n\n/**\n * 事件绑定\n * 在指定的元素上绑定事件监听器\n *\n * @param element - 目标元素\n * @param event - 事件名称(如 'click', 'mousedown' 等)\n * @param handler - 事件处理函数\n * @param useCapture - 是否在捕获阶段触发事件,默认为 false(冒泡阶段)\n *\n * @example 基础点击事件\n * ```typescript\n * on(document.body, 'click', () => {\n * console.log('Body clicked');\n * }, false);\n * ```\n *\n * @example 元素悬停效果\n * ```typescript\n * const button = $('button');\n * on(button, 'mouseenter', () => {\n * addClass(button, 'hover');\n * });\n * on(button, 'mouseleave', () => {\n * removeClass(button, 'hover');\n * });\n * ```\n *\n * @example 表单输入监听\n * ```typescript\n * const input = $('input[type=\"text\"]');\n * on(input, 'input', (e) => {\n * console.log('Input value:', (e.target as HTMLInputElement).value);\n * });\n * ```\n *\n * @example 使用事件对象处理函数\n * ```typescript\n * const handler = {\n * count: 0,\n * handleEvent(e: Event) {\n * this.count++;\n * console.log(`Event fired ${this.count} times`);\n * }\n * };\n * on(button, 'click', handler);\n * ```\n *\n * @example 捕获阶段监听\n * ```typescript\n * // 在父元素上使用捕获阶段拦截子元素事件\n * on(container, 'click', (e) => {\n * console.log('Click captured at container');\n * }, true);\n * ```\n */\nexport function on(\n element: HTMLElement | Document | Window,\n event: string,\n handler: EventListenerOrEventListenerObject,\n useCapture = false\n): void {\n if (element && event && handler) {\n element.addEventListener(event, handler, useCapture);\n }\n}\n\n/**\n * 事件解绑\n * 移除元素上的事件监听器\n *\n * @param element - 目标元素\n * @param event - 事件名称\n * @param handler - 事件处理函数\n * @param useCapture - 是否为捕获阶段的事件,必须与绑定时一致\n *\n * @example 基础解绑\n * ```typescript\n * const handler = () => console.log('Clicked');\n * on(button, 'click', handler);\n * // ... 某些条件下\n * off(button, 'click', handler);\n * ```\n *\n * @example 组件卸载时清理\n * ```typescript\n * // 在组件挂载时绑定\n * const resizeHandler = () => console.log('Window resized');\n * on(window, 'resize', resizeHandler);\n *\n * // 在组件卸载时清理\n * function cleanup() {\n * off(window, 'resize', resizeHandler);\n * }\n * ```\n *\n * @example 动态清理多个监听器\n * ```typescript\n * const handlers = {\n * click: null as (() => void) | null,\n * scroll: null as (() => void) | null\n * };\n *\n * // 绑定\n * handlers.click = () => console.log('Clicked');\n * handlers.scroll = () => console.log('Scrolled');\n * on(button, 'click', handlers.click);\n * on(window, 'scroll', handlers.scroll);\n *\n * // 清理\n * if (handlers.click) off(button, 'click', handlers.click);\n * if (handlers.scroll) off(window, 'scroll', handlers.scroll);\n * ```\n *\n * @example 捕获阶段事件解绑\n * ```typescript\n * const handler = () => console.log('Captured');\n * on(container, 'click', handler, true);\n * // 移除时必须也传入 true\n * off(container, 'click', handler, true);\n * ```\n */\nexport function off(\n element: HTMLElement | Document | Window,\n event: string,\n handler: EventListenerOrEventListenerObject,\n useCapture = false\n): void {\n if (element && event && handler) {\n element.removeEventListener(event, handler, useCapture);\n }\n}\n\n/**\n * 一次性事件监听\n * 绑定一个只触发一次的事件监听器,触发后自动移除\n *\n * @param element - 目标元素\n * @param event - 事件名称\n * @param handler - 事件处理函数\n * @param useCapture - 是否在捕获阶段触发事件,默认为 false\n *\n * @example 只执行一次的操作\n * ```typescript\n * once(button, 'click', () => {\n * console.log('This will only log once');\n * });\n * ```\n *\n * @example 页面加载初始化\n * ```typescript\n * once(document, 'DOMContentLoaded', () => {\n * console.log('DOM fully loaded');\n * // 执行初始化逻辑\n * });\n * ```\n *\n * @example 首次滚动触发\n * ```typescript\n * once(window, 'scroll', () => {\n * console.log('User scrolled for the first time');\n * // 显示提示或记录数据\n * });\n * ```\n *\n * @example 表单首次提交\n * ```typescript\n * once(form, 'submit', (e) => {\n * e.preventDefault();\n * console.log('First form submission');\n * // 特殊处理首次提交\n * });\n * ```\n *\n * @example 资源加载完成后清理\n * ```typescript\n * const image = new Image();\n * once(image, 'load', () => {\n * console.log('Image loaded successfully');\n * // 显示图片或触发其他逻辑\n * });\n * image.src = 'path/to/image.jpg';\n * ```\n *\n * @example 与事件对象结合\n * ```typescript\n * const counter = { clicks: 0 };\n * once(button, 'click', (e) => {\n * counter.clicks++;\n * console.log(`Clicked at: ${e.timeStamp}`);\n * });\n * ```\n */\nexport function once(\n element: HTMLElement | Document | Window,\n event: string,\n handler: EventListenerOrEventListenerObject,\n useCapture = false\n): void {\n if (!element || !event || !handler) return;\n\n const onceHandler: EventListenerOrEventListenerObject = (e: Event) => {\n if (typeof handler === 'function') {\n handler(e);\n } else if (handler.handleEvent) {\n handler.handleEvent(e);\n }\n element.removeEventListener(event, onceHandler, useCapture);\n };\n\n element.addEventListener(event, onceHandler, useCapture);\n}\n","import copy from 'copy-text-to-clipboard'\n\ninterface optionsType {\n target: HTMLElement\n}\n\n/**\n * 复制文本到剪贴板\n * @param text - 需要复制到剪贴板的文本\n * @param options - target: HTMLElement\n * @returns 是否复制成功(Boolean)\n * @example\n * ```ts\n * copyToClipboard('复制') // true\n * copyToClipboard('指定临时创建的 dom 存放处', { target: document.querySelector('#text') }) // true\n * ```\n * @public\n */\nexport function copyToClipboard(text: string, options?: optionsType): boolean {\n return copy(text, options)\n}\n","type StorageType = 'localStorage' | 'sessionStorage';\n\ninterface StorageManagerOptions {\n prefix?: string;\n storageType?: StorageType;\n}\n\ninterface StorageItem<T> {\n expiry?: number;\n value: T;\n}\n\nclass StorageManager {\n private prefix: string;\n private storage: Storage;\n\n constructor({\n prefix = '',\n storageType = 'localStorage',\n }: StorageManagerOptions = {}) {\n this.prefix = prefix;\n this.storage =\n storageType === 'localStorage'\n ? window.localStorage\n : window.sessionStorage;\n }\n\n /**\n * 清除所有带前缀的存储项\n */\n clear(): void {\n const keysToRemove: string[] = [];\n for (let i = 0; i < this.storage.length; i++) {\n const key = this.storage.key(i);\n if (key && key.startsWith(this.prefix)) {\n keysToRemove.push(key);\n }\n }\n keysToRemove.forEach((key) => this.storage.removeItem(key));\n }\n\n /**\n * 清除所有过期的存储项\n */\n clearExpiredItems(): void {\n for (let i = 0; i < this.storage.length; i++) {\n const key = this.storage.key(i);\n if (key && key.startsWith(this.prefix)) {\n const shortKey = key.replace(this.prefix, '');\n this.getItem(shortKey); // 调用 getItem 方法检查并移除过期项\n }\n }\n }\n\n /**\n * 获取存储项\n * @param key 键\n * @param defaultValue 当项不存在或已过期时返回的默认值\n * @returns 值,如果项已过期或解析错误则返回默认值\n */\n getItem<T>(key: string, defaultValue: null | T = null): null | T {\n const fullKey = this.getFullKey(key);\n const itemStr = this.storage.getItem(fullKey);\n if (!itemStr) {\n return defaultValue;\n }\n\n try {\n const item: StorageItem<T> = JSON.parse(itemStr);\n if (item.expiry && Date.now() > item.expiry) {\n this.storage.removeItem(fullKey);\n return defaultValue;\n }\n return item.value;\n } catch (error) {\n console.error(`Error parsing item with key \"${fullKey}\":`, error);\n this.storage.removeItem(fullKey); // 如果解析失败,删除该项\n return defaultValue;\n }\n }\n\n /**\n * 移除存储项\n * @param key 键\n */\n removeItem(key: string): void {\n const fullKey = this.getFullKey(key);\n this.storage.removeItem(fullKey);\n }\n\n /**\n * 设置存储项\n * @param key 键\n * @param value 值\n * @param ttl 存活时间(毫秒)\n */\n setItem<T>(key: string, value: T, ttl?: number): void {\n const fullKey = this.getFullKey(key);\n const expiry = ttl ? Date.now() + ttl : undefined;\n const item: StorageItem<T> = { expiry, value };\n try {\n this.storage.setItem(fullKey, JSON.stringify(item));\n } catch (error) {\n console.error(`Error setting item with key \"${fullKey}\":`, error);\n }\n }\n\n /**\n * 获取完整的存储键\n * @param key 原始键\n * @returns 带前缀的完整键\n */\n private getFullKey(key: string): string {\n return `${this.prefix}-${key}`;\n }\n}\n\nexport { StorageManager };\n","import type { OpenWindowOptions } from './types';\n\n/**\n * 在新窗口中打开 URL\n *\n * 使用 window.open 方法打开指定 URL,支持配置窗口行为和安全特性\n *\n * @param url - 需要打开的网址\n * @param options - 打开窗口的选项\n * @param options.noopener - 是否启用 noopener,默认为 true\n * @param options.noreferrer - 是否启用 noreferrer,默认为 true\n * @param options.target - 打开目标窗口,默认为 '_blank'\n *\n * @example 打开新窗口\n * ```typescript\n * openWindow('https://example.com');\n * ```\n *\n * @example 在当前窗口打开\n * ```typescript\n * openWindow('https://example.com', { target: '_self' });\n * ```\n *\n * @example 禁用安全特性\n * ```typescript\n * openWindow('https://example.com', {\n * noopener: false,\n * noreferrer: false\n * });\n * ```\n *\n * @example 在父窗口打开\n * ```typescript\n * openWindow('https://example.com', { target: '_parent' });\n * ```\n */\nexport function openWindow(\n url: string,\n options: OpenWindowOptions = {}\n): void {\n // 解构并设置默认值\n const {\n noopener = true,\n noreferrer = true,\n target = '_blank'\n } = options;\n\n // 基于选项创建特性字符串\n const features = [\n noopener && 'noopener=yes',\n noreferrer && 'noreferrer=yes'\n ]\n .filter(Boolean)\n .join(',');\n\n // 打开窗口\n window.open(url, target, features);\n}\n","import { openWindow } from './openWindow';\n\n/**\n * 在新窗口中打开当前应用的路由\n *\n * 基于当前页面的 origin 和 hash 构建完整的路由地址,然后在新窗口中打开\n *\n * @param path - 路由路径\n *\n * @example 打开路由\n * ```typescript\n * openRouteInNewWindow('/dashboard');\n * ```\n *\n * @example 打开带参数的路由\n * ```typescript\n * openRouteInNewWindow('/user/profile?id=123');\n * ```\n *\n * @example 不带前导斜杠\n * ```typescript\n * openRouteInNewWindow('settings');\n * // 等同于 '/settings'\n * ```\n *\n * @example 在 Hash 路由模式下\n * ```typescript\n * // 当前 URL: https://example.com/#/home\n * openRouteInNewWindow('/about');\n * // 将打开: https://example.com/#/about\n * ```\n */\nexport function openRouteInNewWindow(path: string): void {\n const { hash, origin } = location;\n const fullPath = path.startsWith('/') ? path : `/${path}`;\n const url = `${origin}${hash && !fullPath.startsWith('/#') ? '/#' : ''}${fullPath}`;\n openWindow(url, { target: '_blank' });\n}\n","/**\n * 下载功能常量\n */\n\n/**\n * 默认下载文件名\n */\nexport const DEFAULT_FILENAME = 'downloaded_file';\n","/**\n * 通用下载触发函数\n *\n * 通过创建临时 `<a>` 标签并触发点击事件来下载文件,支持自动清理内存\n *\n * @param href - 文件下载的 URL 或 Blob URL\n * @param fileName - 下载文件的名称,如果未提供则使用默认值\n * @param revokeDelay - 清理 URL 的延迟时间(毫秒),默认为 100ms\n *\n * @example 下载文件\n * ```typescript\n * triggerDownload('https://example.com/file.pdf', 'document.pdf');\n * ```\n *\n * @example 使用 Blob URL\n * ```typescript\n * const blob = new Blob(['content'], { type: 'text/plain' });\n * const blobUrl = URL.createObjectURL(blob);\n * triggerDownload(blobUrl, 'file.txt', 200);\n * ```\n *\n * @example 使用默认文件名\n * ```typescript\n * triggerDownload('https://example.com/data.json', undefined);\n * ```\n */\nexport function triggerDownload(\n href: string,\n fileName: string | undefined,\n revokeDelay: number = 100,\n): void {\n const defaultFileName = 'downloaded_file';\n const finalFileName = fileName || defaultFileName;\n\n const link = document.createElement('a');\n link.href = href;\n link.download = finalFileName;\n link.style.display = 'none';\n\n if (link.download === undefined) {\n link.setAttribute('target', '_blank');\n }\n\n document.body.append(link);\n link.click();\n link.remove();\n\n // 清理临时 URL 以释放内存\n setTimeout(() => URL.revokeObjectURL(href), revokeDelay);\n}\n","/**\n * 下载功能工具函数\n */\n\nimport { DEFAULT_FILENAME } from './constants';\n\n/**\n * 解析文件名\n *\n * 从 URL 中提取文件名,如果未提供文件名或 URL 中没有文件名,则使用默认文件名\n *\n * @param url - 文件 URL 地址\n * @param fileName - 指定的文件名(可选)\n *\n * @returns string - 解析后的文件名\n *\n * @example 使用指定文件名\n * ```typescript\n * const name = resolveFileName('https://example.com/file.pdf', 'custom.pdf');\n * console.log(name); // 'custom.pdf'\n * ```\n *\n * @example 从 URL 提取文件名\n * ```typescript\n * const name = resolveFileName('https://example.com/path/to/document.pdf');\n * console.log(name); // 'document.pdf'\n * ```\n *\n * @example 使用默认文件名\n * ```typescript\n * const name = resolveFileName('https://example.com/path/');\n * console.log(name); // 'downloaded_file'\n * ```\n */\nexport function resolveFileName(\n url: string,\n fileName?: string,\n): string {\n return fileName || url.slice(url.lastIndexOf('/') + 1) || DEFAULT_FILENAME;\n}\n\n","import { openWindow } from '../window';\nimport { DownloadOptions } from './types';\nimport { triggerDownload } from './triggerDownload';\nimport { resolveFileName } from './utils';\n\n/**\n * 通过 URL 下载文件,支持跨域\n *\n * @param options - 下载选项\n * @param options.fileName - 下载文件名\n * @param options.source - 文件 URL\n * @param options.target - 打开目标,默认为 '_blank'\n *\n * @throws {Error} - 当下载失败时抛出错误\n *\n * @example 下载 URL 文件\n * ```typescript\n * await downloadFileFromUrl({\n * source: 'https://example.com/file.pdf',\n * fileName: 'document.pdf'\n * });\n * ```\n *\n * @example 跨域下载\n * ```typescript\n * await downloadFileFromUrl({\n * source: 'https://cdn.example.com/image.jpg',\n * fileName: 'image.jpg'\n * });\n * ```\n */\nexport async function downloadFileFromUrl({\n fileName,\n source,\n target = '_blank',\n}: DownloadOptions): Promise<void> {\n if (!source || typeof source !== 'string') {\n throw new Error('Invalid URL.');\n }\n\n const isChrome = window.navigator.userAgent.toLowerCase().includes('chrome');\n const isSafari = window.navigator.userAgent.toLowerCase().includes('safari');\n\n if (/iP/.test(window.navigator.userAgent)) {\n console.error('Your browser does not support download!');\n return;\n }\n\n if (isChrome || isSafari) {\n triggerDownload(source, resolveFileName(source, fileName));\n return;\n }\n if (!source.includes('?')) {\n source += '?download';\n }\n\n openWindow(source, { target });\n}\n","import { DownloadOptions } from './types';\nimport { triggerDownload } from './triggerDownload';\nimport { DEFAULT_FILENAME } from './constants';\n\n/**\n * 通过 Base64 数据下载文件\n *\n * @param options - 下载选项\n * @param options.fileName - 下载文件名\n * @param options.source - Base64 编码的文件数据\n *\n * @throws {Error} - 当 Base64 数据无效时抛出错误\n *\n * @example 下载 Base64 图片\n * ```typescript\n * downloadFileFromBase64({\n * source: 'data:image/png;base64,iVBORw0KGgoAAAANS...',\n * fileName: 'image.png'\n * });\n * ```\n *\n * @example 使用默认文件名\n * ```typescript\n * downloadFileFromBase64({\n * source: 'data:text/plain;base64,SGVsbG8gV29ybGQ='\n * });\n * ```\n */\nexport function downloadFileFromBase64({ fileName, source }: DownloadOptions) {\n if (!source || typeof source !== 'string') {\n throw new Error('Invalid Base64 data.');\n }\n\n const resolvedFileName = fileName || DEFAULT_FILENAME;\n triggerDownload(source, resolvedFileName);\n}\n","/**\n * 将图片 URL 转换为 Base64 编码\n *\n * @param url - 图片 URL 地址\n * @param mineType - 指定输出的 MIME 类型,默认为 'image/png'\n *\n * @returns 返回 Base64 编码的图片数据\n *\n * @example 转换为 PNG\n * ```typescript\n * const base64 = await urlToBase64('https://example.com/image.jpg');\n * console.log(base64); // 'data:image/png;base64,...'\n * ```\n *\n * @example 指定 MIME 类型\n * ```typescript\n * const jpegBase64 = await urlToBase64(\n * 'https://example.com/image.png',\n * 'image/jpeg'\n * );\n * ```\n *\n * @example 下载图片\n * ```typescript\n * const base64Data = await urlToBase64('https://example.com/photo.jpg');\n * downloadFileFromBase64({\n * source: base64Data,\n * fileName: 'photo.jpg'\n * });\n * ```\n */\nexport function urlToBase64(url: string, mineType?: string): Promise<string> {\n return new Promise((resolve, reject) => {\n let canvas = document.createElement('CANVAS') as HTMLCanvasElement | null;\n const ctx = canvas?.getContext('2d');\n const img = new Image();\n img.crossOrigin = '';\n img.addEventListener('load', () => {\n if (!canvas || !ctx) {\n return reject(new Error('Failed to create canvas.'));\n }\n canvas.height = img.height;\n canvas.width = img.width;\n ctx.drawImage(img, 0, 0);\n const dataURL = canvas.toDataURL(mineType || 'image/png');\n canvas = null;\n resolve(dataURL);\n });\n img.src = url;\n });\n}\n","import { DownloadOptions } from './types';\nimport { urlToBase64 } from './urlToBase64';\nimport { downloadFileFromBase64 } from './downloadFileFromBase64';\n\n/**\n * 通过图片 URL 下载图片文件\n *\n * @param options - 下载选项\n * @param options.fileName - 下载文件名\n * @param options.source - 图片 URL\n *\n * @example 下载网络图片\n * ```typescript\n * await downloadFileFromImageUrl({\n * source: 'https://example.com/image.jpg',\n * fileName: 'downloaded-image.jpg'\n * });\n * ```\n *\n * @example 下载 SVG 图片\n * ```typescript\n * await downloadFileFromImageUrl({\n * source: 'https://example.com/icon.svg',\n * fileName: 'icon.svg'\n * });\n * ```\n */\nexport async function downloadFileFromImageUrl({\n fileName,\n source,\n}: DownloadOptions) {\n const base64 = await urlToBase64(source);\n downloadFileFromBase64({ fileName, source: base64 });\n}\n","import { DownloadOptions } from './types';\nimport { triggerDownload } from './triggerDownload';\nimport { DEFAULT_FILENAME } from './constants';\n\n/**\n * 通过 Blob 对象下载文件\n *\n * @param options - 下载选项\n * @param options.fileName - 下载文件名,默认为 'downloaded_file'\n * @param options.source - Blob 对象\n *\n * @throws {TypeError} - 当源数据不是 Blob 对象时抛出错误\n *\n * @example 下载文本 Blob\n * ```typescript\n * const textBlob = new Blob(['Hello World'], { type: 'text/plain' });\n * downloadFileFromBlob({\n * source: textBlob,\n * fileName: 'hello.txt'\n * });\n * ```\n *\n * @example 下载 JSON Blob\n * ```typescript\n * const jsonData = { name: 'test', value: 123 };\n * const jsonBlob = new Blob([JSON.stringify(jsonData)], { type: 'application/json' });\n * downloadFileFromBlob({\n * source: jsonBlob,\n * fileName: 'data.json'\n * });\n * ```\n */\nexport function downloadFileFromBlob({\n fileName = DEFAULT_FILENAME,\n source,\n}: DownloadOptions<Blob>): void {\n if (!(source instanceof Blob)) {\n throw new TypeError('Invalid Blob data.');\n }\n\n const url = URL.createObjectURL(source);\n triggerDownload(url, fileName);\n}\n","import { DownloadOptions } from './types';\nimport { triggerDownload } from './triggerDownload';\nimport { DEFAULT_FILENAME } from './constants';\n\n/**\n * 通过 BlobPart 数据下载文件\n *\n * 支持 Blob、字符串和其他 BlobPart 类型,如果不是 Blob 会自动转换\n *\n * @param options - 下载选项\n * @param options.fileName - 下载文件名,默认为 'downloaded_file'\n * @param options.source - BlobPart 数据 (Blob、字符串、ArrayBuffer 等)\n *\n * @example 下载字符串\n * ```typescript\n * downloadFileFromBlobPart({\n * source: 'Hello World',\n * fileName: 'text.txt'\n * });\n * ```\n *\n * @example 下载 ArrayBuffer\n * ```typescript\n * const arrayBuffer = new Uint8Array([72, 101, 108, 108, 111]);\n * downloadFileFromBlobPart({\n * source: arrayBuffer,\n * fileName: 'data.bin'\n * });\n * ```\n *\n * @example 下载 Blob 对象\n * ```typescript\n * const blob = new Blob(['content'], { type: 'text/plain' });\n * downloadFileFromBlobPart({\n * source: blob,\n * fileName: 'file.txt'\n * });\n * ```\n */\nexport function downloadFileFromBlobPart({\n fileName = DEFAULT_FILENAME,\n source,\n}: DownloadOptions<BlobPart>): void {\n // 如果 data 不是 Blob,则转换为 Blob\n const blob =\n source instanceof Blob\n ? source\n : new Blob([source], { type: 'application/octet-stream' });\n\n // 创建对象 URL 并触发下载\n const url = URL.createObjectURL(blob);\n triggerDownload(url, fileName);\n}\n"],"mappings":"sCAOA,SAAgB,EAAyB,EAAuB,CAC9D,GAAI,CACF,IAAM,EAAO,OAAO,aAAa,QAAQ,EAAI,CAC7C,OAAO,EAAO,KAAK,MAAM,EAAK,CAAG,WAC1B,EAAO,CAEd,OADA,QAAQ,MAAM,oCAAoC,IAAQ,CACnD,MAOX,SAAgB,EAAmB,EAAa,EAAmB,CACjE,GAAI,CAEF,OADA,OAAO,aAAa,QAAQ,EAAK,KAAK,UAAU,EAAM,CAAC,CAChD,SACA,EAAO,CAEd,OADA,QAAQ,MAAM,kCAAkC,IAAQ,CACjD,IAOX,SAAgB,EAAmB,EAAsB,CACvD,GAAI,CAEF,OADA,OAAO,aAAa,WAAW,EAAI,CAC5B,SACA,EAAO,CAEd,OADA,QAAQ,MAAM,qCAAqC,IAAQ,CACpD,IAOX,SAAgB,GAA6B,CAC3C,GAAI,CAEF,OADA,OAAO,aAAa,OAAO,CACpB,SACA,EAAO,CAEd,OADA,QAAQ,MAAM,gCAAgC,IAAQ,CAC/C,IAOX,SAAgB,EAA2B,EAAuB,CAChE,GAAI,CACF,IAAM,EAAO,OAAO,eAAe,QAAQ,EAAI,CAC/C,OAAO,EAAO,KAAK,MAAM,EAAK,CAAG,WAC1B,EAAO,CAEd,OADA,QAAQ,MAAM,sCAAsC,IAAQ,CACrD,MAOX,SAAgB,EAAqB,EAAa,EAAmB,CACnE,GAAI,CAEF,OADA,OAAO,eAAe,QAAQ,EAAK,KAAK,UAAU,EAAM,CAAC,CAClD,SACA,EAAO,CAEd,OADA,QAAQ,MAAM,oCAAoC,IAAQ,CACnD,IAOX,SAAgB,EAAqB,EAAsB,CACzD,GAAI,CAEF,OADA,OAAO,eAAe,WAAW,EAAI,CAC9B,SACA,EAAO,CAEd,OADA,QAAQ,MAAM,uCAAuC,IAAQ,CACtD,ICnDX,SAAgB,EAAE,EAAsC,CACtD,OAAO,SAAS,cAAc,EAAS,CAsCzC,SAAgB,EAAG,EAAiC,CAClD,OAAO,MAAM,KAAK,SAAS,iBAAiB,EAAS,CAAC,CCExD,SAAgB,EACd,EACA,EACA,EAC0B,CAC1B,IAAM,EAAU,SAAS,cAAc,EAAI,CAE3C,GAAI,EACF,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,EAAW,CACnD,EAAQ,aAAa,EAAK,EAAM,CAIpC,GAAI,EACF,IAAK,IAAM,KAAS,EACd,OAAO,GAAU,SACnB,EAAQ,YAAY,SAAS,eAAe,EAAM,CAAC,CAEnD,EAAQ,YAAY,EAAM,CAKhC,OAAO,EClBT,SAAgB,EAAS,EAAsB,GAAG,EAA4B,CAC5E,EAAQ,UAAU,IAAI,GAAG,EAAW,CA8CtC,SAAgB,EAAY,EAAsB,GAAG,EAA4B,CAC/E,EAAQ,UAAU,OAAO,GAAG,EAAW,CAgEzC,SAAgB,EAAY,EAAsB,EAA4B,CAC5E,OAAO,EAAQ,UAAU,OAAO,EAAU,CCvI5C,SAAgB,EAAS,EAAsB,EAA0B,CACvE,OAAO,OAAO,iBAAiB,EAAQ,CAAC,iBAAiB,EAAS,CAmEpE,SAAgB,EAAS,EAAsB,EAAsC,CACnF,IAAK,GAAM,CAAC,EAAU,KAAU,OAAO,QAAQ,EAAO,CACpD,EAAQ,MAAM,YAAY,EAAU,EAAM,CC3D9C,SAAgB,EAAa,EAA+B,CAC1D,IAAM,EAAO,EAAQ,uBAAuB,CAC5C,OACE,EAAK,KAAO,GACZ,EAAK,MAAQ,GACb,EAAK,SAAW,OAAO,aAAe,SAAS,gBAAgB,eAC/D,EAAK,QAAU,OAAO,YAAc,SAAS,gBAAgB,aAyFjE,SAAgB,EAAe,EAAsB,EAAuC,CAC1F,EAAQ,eAAe,CAAE,SAAU,SAAU,GAAG,EAAS,CAAC,CCrG5D,SAAgB,EACd,EACA,EACA,EACA,EAAa,GACP,CACF,GAAW,GAAS,GACtB,EAAQ,iBAAiB,EAAO,EAAS,EAAW,CA2DxD,SAAgB,EACd,EACA,EACA,EACA,EAAa,GACP,CACF,GAAW,GAAS,GACtB,EAAQ,oBAAoB,EAAO,EAAS,EAAW,CAgE3D,SAAgB,EACd,EACA,EACA,EACA,EAAa,GACP,CACN,GAAI,CAAC,GAAW,CAAC,GAAS,CAAC,EAAS,OAEpC,IAAM,EAAmD,GAAa,CAChE,OAAO,GAAY,WACrB,EAAQ,EAAE,CACD,EAAQ,aACjB,EAAQ,YAAY,EAAE,CAExB,EAAQ,oBAAoB,EAAO,EAAa,EAAW,EAG7D,EAAQ,iBAAiB,EAAO,EAAa,EAAW,CC/M1D,SAAgB,EAAgB,EAAc,EAAgC,CAC5E,OAAO,EAAK,EAAM,EAAQ,CCP5B,IAAM,EAAN,KAAqB,CAInB,YAAY,CACV,SAAS,GACT,cAAc,gBACW,EAAE,CAAE,CAC7B,KAAK,OAAS,EACd,KAAK,QACH,IAAgB,eACZ,OAAO,aACP,OAAO,eAMf,OAAc,CACZ,IAAM,EAAyB,EAAE,CACjC,IAAK,IAAI,EAAI,EAAG,EAAI,KAAK,QAAQ,OAAQ,IAAK,CAC5C,IAAM,EAAM,KAAK,QAAQ,IAAI,EAAE,CAC3B,GAAO,EAAI,WAAW,KAAK,OAAO,EACpC,EAAa,KAAK,EAAI,CAG1B,EAAa,QAAS,GAAQ,KAAK,QAAQ,WAAW,EAAI,CAAC,CAM7D,mBAA0B,CACxB,IAAK,IAAI,EAAI,EAAG,EAAI,KAAK,QAAQ,OAAQ,IAAK,CAC5C,IAAM,EAAM,KAAK,QAAQ,IAAI,EAAE,CAC/B,GAAI,GAAO,EAAI,WAAW,KAAK,OAAO,CAAE,CACtC,IAAM,EAAW,EAAI,QAAQ,KAAK,OAAQ,GAAG,CAC7C,KAAK,QAAQ,EAAS,GAW5B,QAAW,EAAa,EAAyB,KAAgB,CAC/D,IAAM,EAAU,KAAK,WAAW,EAAI,CAC9B,EAAU,KAAK,QAAQ,QAAQ,EAAQ,CAC7C,GAAI,CAAC,EACH,OAAO,EAGT,GAAI,CACF,IAAM,EAAuB,KAAK,MAAM,EAAQ,CAKhD,OAJI,EAAK,QAAU,KAAK,KAAK,CAAG,EAAK,QACnC,KAAK,QAAQ,WAAW,EAAQ,CACzB,GAEF,EAAK,YACL,EAAO,CAGd,OAFA,QAAQ,MAAM,gCAAgC,EAAQ,IAAK,EAAM,CACjE,KAAK,QAAQ,WAAW,EAAQ,CACzB,GAQX,WAAW,EAAmB,CAC5B,IAAM,EAAU,KAAK,WAAW,EAAI,CACpC,KAAK,QAAQ,WAAW,EAAQ,CASlC,QAAW,EAAa,EAAU,EAAoB,CACpD,IAAM,EAAU,KAAK,WAAW,EAAI,CAE9B,EAAuB,CAAE,OADhB,EAAM,KAAK,KAAK,CAAG,EAAM,IAAA,GACD,QAAO,CAC9C,GAAI,CACF,KAAK,QAAQ,QAAQ,EAAS,KAAK,UAAU,EAAK,CAAC,OAC5C,EAAO,CACd,QAAQ,MAAM,gCAAgC,EAAQ,IAAK,EAAM,EASrE,WAAmB,EAAqB,CACtC,MAAO,GAAG,KAAK,OAAO,GAAG,MC7E7B,SAAgB,EACd,EACA,EAA6B,EAAE,CACzB,CAEN,GAAM,CACJ,WAAW,GACX,aAAa,GACb,SAAS,UACP,EAGE,EAAW,CACf,GAAY,eACZ,GAAc,iBACf,CACE,OAAO,QAAQ,CACf,KAAK,IAAI,CAGZ,OAAO,KAAK,EAAK,EAAQ,EAAS,CCxBpC,SAAgB,EAAqB,EAAoB,CACvD,GAAM,CAAE,OAAM,UAAW,SACnB,EAAW,EAAK,WAAW,IAAI,CAAG,EAAO,IAAI,IAEnD,EADY,GAAG,IAAS,GAAQ,CAAC,EAAS,WAAW,KAAK,CAAG,KAAO,KAAK,IACzD,CAAE,OAAQ,SAAU,CAAC,CC7BvC,MAAa,EAAmB,kBCmBhC,SAAgB,EACd,EACA,EACA,EAAsB,IAChB,CAEN,IAAM,EAAgB,GADE,kBAGlB,EAAO,SAAS,cAAc,IAAI,CACxC,EAAK,KAAO,EACZ,EAAK,SAAW,EAChB,EAAK,MAAM,QAAU,OAEjB,EAAK,WAAa,IAAA,IACpB,EAAK,aAAa,SAAU,SAAS,CAGvC,SAAS,KAAK,OAAO,EAAK,CAC1B,EAAK,OAAO,CACZ,EAAK,QAAQ,CAGb,eAAiB,IAAI,gBAAgB,EAAK,CAAE,EAAY,CCd1D,SAAgB,EACd,EACA,EACQ,CACR,OAAO,GAAY,EAAI,MAAM,EAAI,YAAY,IAAI,CAAG,EAAE,EAAI,ECP5D,eAAsB,EAAoB,CACxC,WACA,SACA,SAAS,UACwB,CACjC,GAAI,CAAC,GAAU,OAAO,GAAW,SAC/B,MAAU,MAAM,eAAe,CAGjC,IAAM,EAAW,OAAO,UAAU,UAAU,aAAa,CAAC,SAAS,SAAS,CACtE,EAAW,OAAO,UAAU,UAAU,aAAa,CAAC,SAAS,SAAS,CAE5E,GAAI,KAAK,KAAK,OAAO,UAAU,UAAU,CAAE,CACzC,QAAQ,MAAM,0CAA0C,CACxD,OAGF,GAAI,GAAY,EAAU,CACxB,EAAgB,EAAQ,EAAgB,EAAQ,EAAS,CAAC,CAC1D,OAEG,EAAO,SAAS,IAAI,GACvB,GAAU,aAGZ,EAAW,EAAQ,CAAE,SAAQ,CAAC,CC5BhC,SAAgB,EAAuB,CAAE,WAAU,UAA2B,CAC5E,GAAI,CAAC,GAAU,OAAO,GAAW,SAC/B,MAAU,MAAM,uBAAuB,CAIzC,EAAgB,EADS,GAAY,EACI,CCH3C,SAAgB,EAAY,EAAa,EAAoC,CAC3E,OAAO,IAAI,SAAS,EAAS,IAAW,CACtC,IAAI,EAAS,SAAS,cAAc,SAAS,CACvC,EAAM,GAAQ,WAAW,KAAK,CAC9B,EAAM,IAAI,MAChB,EAAI,YAAc,GAClB,EAAI,iBAAiB,WAAc,CACjC,GAAI,CAAC,GAAU,CAAC,EACd,OAAO,EAAW,MAAM,2BAA2B,CAAC,CAEtD,EAAO,OAAS,EAAI,OACpB,EAAO,MAAQ,EAAI,MACnB,EAAI,UAAU,EAAK,EAAG,EAAE,CACxB,IAAM,EAAU,EAAO,UAAU,GAAY,YAAY,CACzD,EAAS,KACT,EAAQ,EAAQ,EAChB,CACF,EAAI,IAAM,GACV,CCtBJ,eAAsB,EAAyB,CAC7C,WACA,UACkB,CAElB,EAAuB,CAAE,WAAU,OADpB,MAAM,EAAY,EAAO,CACW,CAAC,CCAtD,SAAgB,EAAqB,CACnC,WAAW,EACX,UAC8B,CAC9B,GAAI,EAAE,aAAkB,MACtB,MAAU,UAAU,qBAAqB,CAI3C,EADY,IAAI,gBAAgB,EAAO,CAClB,EAAS,CCFhC,SAAgB,EAAyB,CACvC,WAAW,EACX,UACkC,CAElC,IAAM,EACJ,aAAkB,KACd,EACA,IAAI,KAAK,CAAC,EAAO,CAAE,CAAE,KAAM,2BAA4B,CAAC,CAI9D,EADY,IAAI,gBAAgB,EAAK,CAChB,EAAS"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/storage/index.ts","../src/dom/selector.ts","../src/dom/creation.ts","../src/dom/class.ts","../src/dom/style.ts","../src/dom/viewport.ts","../src/dom/event.ts","../src/clipboard/index.ts","../src/cache/storage-manager.ts","../src/window/openWindow.ts","../src/window/openRouteInNewWindow.ts","../src/window/getURLSearchParams.ts","../src/window/setURLSearchParams.ts","../src/download/constants.ts","../src/download/triggerDownload.ts","../src/download/utils.ts","../src/download/downloadFileFromUrl.ts","../src/download/downloadFileFromBase64.ts","../src/download/urlToBase64.ts","../src/download/downloadFileFromImageUrl.ts","../src/download/downloadFileFromBlob.ts","../src/download/downloadFileFromBlobPart.ts","../src/binary/blobToArrayBuffer.ts","../src/binary/arrayBufferToBlob.ts","../src/binary/arrayBufferToBase64.ts","../src/binary/base64ToBlob.ts","../src/binary/blobToBase64.ts","../src/binary/blobToObjectURL.ts","../src/binary/blobToDataURL.ts"],"sourcesContent":["/**\n * Storage utilities using localStorage and sessionStorage\n */\n\n/**\n * Gets an item from localStorage\n */\nexport function getLocalStorage<T = any>(key: string): T | null {\n try {\n const item = window.localStorage.getItem(key)\n return item ? JSON.parse(item) : null\n } catch (error) {\n console.error(`Error reading from localStorage: ${error}`)\n return null\n }\n}\n\n/**\n * Sets an item in localStorage\n */\nexport function setLocalStorage<T>(key: string, value: T): boolean {\n try {\n window.localStorage.setItem(key, JSON.stringify(value))\n return true\n } catch (error) {\n console.error(`Error writing to localStorage: ${error}`)\n return false\n }\n}\n\n/**\n * Removes an item from localStorage\n */\nexport function removeLocalStorage(key: string): boolean {\n try {\n window.localStorage.removeItem(key)\n return true\n } catch (error) {\n console.error(`Error removing from localStorage: ${error}`)\n return false\n }\n}\n\n/**\n * Clears all items from localStorage\n */\nexport function clearLocalStorage(): boolean {\n try {\n window.localStorage.clear()\n return true\n } catch (error) {\n console.error(`Error clearing localStorage: ${error}`)\n return false\n }\n}\n\n/**\n * Gets an item from sessionStorage\n */\nexport function getSessionStorage<T = any>(key: string): T | null {\n try {\n const item = window.sessionStorage.getItem(key)\n return item ? JSON.parse(item) : null\n } catch (error) {\n console.error(`Error reading from sessionStorage: ${error}`)\n return null\n }\n}\n\n/**\n * Sets an item in sessionStorage\n */\nexport function setSessionStorage<T>(key: string, value: T): boolean {\n try {\n window.sessionStorage.setItem(key, JSON.stringify(value))\n return true\n } catch (error) {\n console.error(`Error writing to sessionStorage: ${error}`)\n return false\n }\n}\n\n/**\n * Removes an item from sessionStorage\n */\nexport function removeSessionStorage(key: string): boolean {\n try {\n window.sessionStorage.removeItem(key)\n return true\n } catch (error) {\n console.error(`Error removing from sessionStorage: ${error}`)\n return false\n }\n}\n","/**\n * 选择器工具模块\n * 提供 DOM 元素选择功能\n */\n\n/**\n * 通过 CSS 选择器获取单个 DOM 元素\n *\n * @param selector - CSS 选择器字符串 (例如 '.class', '#id', 'div > p')\n * @returns 第一个匹配的 DOM 元素,如果未找到则返回 null\n *\n * @example 基础选择器\n * ```typescript\n * const header = $('header');\n * const nav = $('nav');\n * const footer = $('footer');\n * ```\n *\n * @example 类选择器和 ID 选择器\n * ```typescript\n * const activeItem = $('.menu-item.active');\n * const submitButton = $('#submit-btn');\n * const firstCard = $('.card:first-child');\n * ```\n *\n * @example 属性选择器和后代选择器\n * ```typescript\n * const inputText = $('input[type=\"text\"]');\n * const navLink = $('nav a.external');\n * const checkedCheckbox = $('input[type=\"checkbox\"]:checked');\n * ```\n *\n * @example 空值检查\n * ```typescript\n * const element = $('header');\n * if (element) {\n * console.log(element.textContent);\n * }\n * ```\n */\nexport function $(selector: string): HTMLElement | null {\n return document.querySelector(selector)\n}\n\n/**\n * 通过 CSS 选择器获取所有匹配的 DOM 元素\n *\n * @param selector - CSS 选择器字符串 (例如 '.class', '#id', 'div > p')\n * @returns 匹配的 DOM 元素数组\n *\n * @example 收集元素\n * ```typescript\n * const buttons = $$('button');\n * const links = $$('.nav a');\n * const inputs = $$('input');\n * ```\n *\n * @example 遍历元素\n * ```typescript\n * const cards = $$('.card');\n * cards.forEach(card => {\n * card.style.opacity = '0.8';\n * });\n * ```\n *\n * @example 过滤和处理元素\n * ```typescript\n * const images = $$('img');\n * const largeImages = images.filter(img => {\n * const width = parseInt(img.getAttribute('width') || '0');\n * return width > 300;\n * });\n * ```\n *\n * @example 多选择器组合\n * ```typescript\n * const focusableElements = $$('button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])');\n * ```\n */\nexport function $$(selector: string): HTMLElement[] {\n return Array.from(document.querySelectorAll(selector))\n}\n","/**\n * 元素创建工具模块\n * 提供 DOM 元素创建功能\n */\n\n/**\n * 创建 DOM 元素,可指定属性和子元素\n *\n * @param tag - HTML 标签名 (例如 'div', 'span', 'a')\n * @param attributes - 可选的属性键值对对象\n * @param children - 可选的子元素或文本节点数组\n * @returns 创建的 DOM 元素\n *\n * @example 基础元素创建\n * ```typescript\n * // 创建一个简单的 div\n * const div = createElement('div');\n *\n * // 创建带文本的 span\n * const span = createElement('span', {}, ['Hello, World!']);\n *\n * // 创建图片元素\n * const img = createElement('img', {\n * src: 'image.jpg',\n * alt: 'Image description',\n * width: '200'\n * });\n * ```\n *\n * @example 表单元素\n * ```typescript\n * // 创建输入框\n * const input = createElement('input', {\n * type: 'text',\n * placeholder: 'Enter your name',\n * name: 'username'\n * });\n *\n * // 创建带多个属性的按钮\n * const button = createElement('button', {\n * id: 'submit',\n * type: 'submit',\n * class: 'btn btn-primary',\n * 'data-action': 'submit-form'\n * }, ['Submit']);\n * ```\n *\n * @example 复杂的嵌套结构\n * ```typescript\n * // 创建带头部、主体和底部的卡片\n * const card = createElement('div', { class: 'card' }, [\n * createElement('div', { class: 'card-header' }, [\n * createElement('h3', {}, ['Card Title']),\n * createElement('button', { class: 'close' }, ['×'])\n * ]),\n * createElement('div', { class: 'card-body' }, [\n * createElement('p', {}, ['This is the card content'])\n * ]),\n * createElement('div', { class: 'card-footer' }, [\n * createElement('button', {}, ['Cancel']),\n * createElement('button', { class: 'primary' }, ['Save'])\n * ])\n * ]);\n * ```\n *\n * @example 列表和表格\n * ```typescript\n * // 创建无序列表\n * const ul = createElement('ul', {}, [\n * createElement('li', {}, ['First item']),\n * createElement('li', {}, ['Second item']),\n * createElement('li', {}, ['Third item'])\n * ]);\n *\n * // 创建表格行\n * const tr = createElement('tr', {}, [\n * createElement('td', {}, ['John']),\n * createElement('td', {}, ['30']),\n * createElement('td', {}, ['john@example.com'])\n * ]);\n * ```\n */\nexport function createElement<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n attributes?: Record<string, string>,\n children?: (string | Node)[]\n): HTMLElementTagNameMap[K] {\n const element = document.createElement(tag)\n\n if (attributes) {\n for (const [key, value] of Object.entries(attributes)) {\n element.setAttribute(key, value)\n }\n }\n\n if (children) {\n for (const child of children) {\n if (typeof child === 'string') {\n element.appendChild(document.createTextNode(child))\n } else {\n element.appendChild(child)\n }\n }\n }\n\n return element\n}\n","/**\n * 类名操作工具模块\n * 提供 DOM 元素的类名增删改功能\n * 底层的 classList.add() 方法原生支持两种方式:\n * 接受多个独立的字符串参数\n * 接受一个包含空格分隔的字符串(会自动拆分)\n *\n * 虽然当前实现两种方式都能工作,但从代码清晰度和TypeScript 类型安全的角度考虑:\n * 推荐的用法是使用多个独立参数:\n * ```typescript\n * addClass(element, 'class1', 'class2');\n * ```\n * 原因是:\n *\n * 当前函数签名使用的是剩余参数 ...classNames: string[],设计意图就是接收多个独立字符串\n * 独立参数的方式在 TypeScript 中有更好的类型提示和智能感知\n * 代码意图更明确,便于阅读和维护\n *\n * 因为底层调用的是浏览器原生的 classList.add() 方法,该方法具有以下特性:\n * 如果类名已存在,不会重复添加\n * 即使你多次传入相同的类名,DOM 中最终只会保留一个\n *\n * 说明哪些情况下的元素/对象没有 classList 属性\n * 1. 基本节点类型(Node 的子类): 例如 Text、Comment、Document、Fragment 等\n * 2. SVG 元素 : SVG 元素不是 HTMLElement,而是 SVGElement,早期浏览器可能不支持 classList\n * 3. 非元素节点: 例如 Text、Comment、Document、DocumentFragment 等\n * 4. 全局对象: 例如 window、document 等\n * 5. 自定义或非标准对象\n *\n * 完整的继承关系图\n * Node (没有 classList)\n * ├── Document (没有 classList)\n * ├── DocumentFragment (没有 classList)\n * ├── CharacterData (没有 classList)\n * │ ├── Text (没有 classList)\n * │ └── Comment (没有 classList)\n * └── Element (有 classList - 在现代浏览器中)\n * ├── HTMLElement (有 classList)\n * │ ├── HTMLDivElement\n * │ ├── HTMLButtonElement\n * │ └── ...\n * └── SVGElement (有 classList - 现代浏览器)\n */\n\n/**\n * 向元素添加一个或多个 CSS 类\n *\n * @param element - 要添加类的 DOM 元素\n * @param classNames - 一个或多个要添加的类名\n *\n * @example 添加单个或多个类\n * ```typescript\n * const button = $('button');\n * addClass(button, 'active');\n * addClass(button, 'highlight', 'primary');\n * ```\n *\n * @example 动态添加类\n * ```typescript\n * const card = $('.card');\n * if (card) {\n * const isFeatured = card.dataset.featured === 'true';\n * if (isFeatured) {\n * addClass(card, 'featured', 'highlight');\n * }\n * }\n * ```\n *\n * @example 批量处理元素\n * ```typescript\n * const buttons = $$('button');\n * buttons.forEach(button => {\n * addClass(button, 'btn', 'btn-primary');\n * });\n * ```\n *\n * @example 条件样式\n * ```typescript\n * const alerts = $$('.alert');\n * alerts.forEach(alert => {\n * const type = alert.dataset.type;\n * if (type === 'error') addClass(alert, 'alert-error');\n * if (type === 'warning') addClass(alert, 'alert-warning');\n * if (type === 'success') addClass(alert, 'alert-success');\n * });\n * ```\n */\nexport function addClass(element: HTMLElement, ...classNames: string[]): void {\n element.classList.add(...classNames)\n}\n\n/**\n * 从元素中移除一个或多个 CSS 类\n *\n * @param element - 要移除类的 DOM 元素\n * @param classNames - 一个或多个要移除的类名\n *\n * @example 移除单个或多个类\n * ```typescript\n * const button = $('button');\n * removeClass(button, 'active');\n * removeClass(button, 'highlight', 'primary');\n * ```\n *\n * @example 重置元素状态\n * ```typescript\n * const modal = $('.modal');\n * if (modal) {\n * removeClass(modal, 'open', 'visible');\n * addClass(modal, 'closed');\n * }\n * ```\n *\n * @example 基于切换的移除\n * ```typescript\n * const menu = $('.menu');\n * if (menu) {\n * if (menu.classList.contains('active')) {\n * removeClass(menu, 'active');\n * }\n * }\n * ```\n *\n * @example 清理动态类\n * ```typescript\n * const container = $('.container');\n * if (container) {\n * // 移除所有工具类\n * removeClass(container, 'mt-2', 'mb-4', 'p-4', 'bg-gray-100');\n * // 保留基础类\n * addClass(container, 'base-style');\n * }\n * ```\n */\nexport function removeClass(element: HTMLElement, ...classNames: string[]): void {\n element.classList.remove(...classNames)\n}\n\n/**\n * 切换元素的 CSS 类(存在则移除,不存在则添加)\n *\n * @param element - 要切换类的 DOM 元素\n * @param className - 要切换的类名\n * @returns 如果类被添加则返回 true,如果被移除则返回 false\n *\n * @example 简单切换\n * ```typescript\n * const button = $('button');\n * const isActive = toggleClass(button, 'active');\n * console.log('Active:', isActive); // 添加时为 true,移除时为 false\n * ```\n *\n * @example 事件处理器切换\n * ```typescript\n * const menuToggle = $('#menu-toggle');\n * const menu = $('#menu');\n *\n * menuToggle?.addEventListener('click', () => {\n * if (menu) {\n * const isOpen = toggleClass(menu, 'open');\n * if (isOpen) {\n * addClass(menuToggle, 'active');\n * } else {\n * removeClass(menuToggle, 'active');\n * }\n * }\n * });\n * ```\n *\n * @example 表单验证反馈\n * ```typescript\n * const emailInput = $('input[type=\"email\"]');\n *\n * emailInput?.addEventListener('input', () => {\n * const isValid = emailInput.value.includes('@');\n * toggleClass(emailInput, 'valid', isValid);\n * toggleClass(emailInput, 'invalid', !isValid);\n * });\n * ```\n *\n * @example 多元素切换\n * ```typescript\n * const buttons = $$('.tab-button');\n * const panels = $$('.tab-panel');\n *\n * buttons.forEach((button, index) => {\n * button.addEventListener('click', () => {\n * // 切换按钮的激活状态\n * buttons.forEach(btn => removeClass(btn, 'active'));\n * addClass(button, 'active');\n *\n * // 切换面板的可见性\n * panels.forEach((panel, i) => {\n * toggleClass(panel, 'active', i === index);\n * });\n * });\n * });\n * ```\n */\nexport function toggleClass(element: HTMLElement, className: string): boolean {\n return element.classList.toggle(className)\n}\n","/**\n * 样式操作工具模块\n * 提供 DOM 元素的样式读写功能\n */\n\n/**\n * 获取元素指定 CSS 属性的计算值\n *\n * @param element - 要获取样式的 DOM 元素\n * @param property - CSS 属性名 (例如 'color', 'font-size')\n * @returns CSS 属性的计算值\n *\n * @example 基础样式获取\n * ```typescript\n * const header = $('header');\n * const bgColor = getStyle(header, 'background-color');\n * const fontSize = getStyle(header, 'font-size');\n * const color = getStyle(header, 'color');\n * ```\n *\n * @example 响应式设计检查\n * ```typescript\n * const element = $('.responsive-element');\n * if (element) {\n * const display = getStyle(element, 'display');\n * const isVisible = display !== 'none';\n *\n * if (isVisible) {\n * const width = getStyle(element, 'width');\n * console.log('Element width:', width);\n * }\n * }\n * ```\n *\n * @example 比较和逻辑判断\n * ```typescript\n * const header = $('header');\n * const footer = $('footer');\n *\n * if (header && footer) {\n * const headerHeight = parseInt(getStyle(header, 'height'));\n * const footerHeight = parseInt(getStyle(footer, 'height'));\n *\n * if (headerHeight > footerHeight) {\n * console.log('Header is taller than footer');\n * }\n * }\n * ```\n *\n * @example 颜色处理\n * ```typescript\n * const button = $('button');\n * if (button) {\n * const bgColor = getStyle(button, 'background-color');\n * const textColor = getStyle(button, 'color');\n *\n * // 转换为 RGB 并调整亮度\n * const rgb = bgColor.match(/\\d+/g);\n * if (rgb) {\n * const brightness = (parseInt(rgb[0]) * 299 + parseInt(rgb[1]) * 587 + parseInt(rgb[2]) * 114) / 1000;\n * const shouldUseLightText = brightness < 128;\n * }\n * }\n * ```\n */\nexport function getStyle(element: HTMLElement, property: string): string {\n return window.getComputedStyle(element).getPropertyValue(property)\n}\n\n/**\n * 为元素设置多个 CSS 属性\n *\n * @param element - 要应用样式的 DOM 元素\n * @param styles - CSS 属性键值对对象\n *\n * @example 基础样式设置\n * ```typescript\n * const header = $('header');\n * setStyle(header, {\n * 'color': '#333',\n * 'font-size': '16px',\n * 'background-color': '#f5f5f5'\n * });\n * ```\n *\n * @example 动画和过渡效果\n * ```typescript\n * const modal = $('.modal');\n * if (modal) {\n * setStyle(modal, {\n * 'opacity': '0',\n * 'transform': 'translateY(-20px)',\n * 'transition': 'opacity 0.3s ease, transform 0.3s ease'\n * });\n *\n * setTimeout(() => {\n * setStyle(modal, {\n * 'opacity': '1',\n * 'transform': 'translateY(0)'\n * });\n * }, 100);\n * }\n * ```\n *\n * @example 响应式和动态样式\n * ```typescript\n * const card = $('.card');\n * if (card) {\n * const isDark = document.documentElement.classList.contains('dark');\n * setStyle(card, {\n * 'background-color': isDark ? '#1f2937' : '#ffffff',\n * 'color': isDark ? '#f9fafb' : '#111827',\n * 'box-shadow': isDark ? '0 4px 6px rgba(0, 0, 0, 0.5)' : '0 4px 6px rgba(0, 0, 0, 0.1)'\n * });\n * }\n * ```\n *\n * @example 动态定位\n * ```typescript\n * const tooltip = $('.tooltip');\n * const trigger = $('.trigger');\n *\n * if (tooltip && trigger) {\n * const triggerRect = trigger.getBoundingClientRect();\n * setStyle(tooltip, {\n * 'position': 'absolute',\n * 'top': `${triggerRect.bottom + 10}px`,\n * 'left': `${triggerRect.left}px`,\n * 'z-index': '1000'\n * });\n * }\n * ```\n */\nexport function setStyle(element: HTMLElement, styles: Record<string, string>): void {\n for (const [property, value] of Object.entries(styles)) {\n element.style.setProperty(property, value)\n }\n}\n","/**\n * 视口和滚动工具模块\n * 提供元素可视性检查和滚动控制功能\n */\n\n/**\n * 检查元素是否完全可见于视口中\n *\n * @param element - 要检查的 DOM 元素\n * @returns 如果元素在视口中完全可见则返回 true,否则返回 false\n *\n * @example 基础可见性检查\n * ```typescript\n * const footer = $('footer');\n * if (isInViewport(footer)) {\n * console.log('Footer is visible');\n * }\n * ```\n *\n * @example 懒加载内容\n * ```typescript\n * const images = $$('img[data-src]');\n *\n * function loadVisibleImages() {\n * images.forEach(img => {\n * if (isInViewport(img) && !img.src) {\n * img.src = img.dataset.src;\n * delete img.dataset.src;\n * }\n * });\n * }\n *\n * window.addEventListener('scroll', loadVisibleImages);\n * window.addEventListener('load', loadVisibleImages);\n * ```\n *\n * @example 无限滚动\n * ```typescript\n * const sentinel = $('.sentinel');\n *\n * function loadMoreContent() {\n * if (sentinel && isInViewport(sentinel)) {\n * console.log('Load more content...');\n * // 获取并追加新内容\n * }\n * }\n *\n * window.addEventListener('scroll', () => {\n * requestAnimationFrame(loadMoreContent);\n * });\n * ```\n *\n * @example 统计和跟踪\n * ```typescript\n * const sections = $$('section[data-track]');\n *\n * const observer = new IntersectionObserver((entries) => {\n * entries.forEach(entry => {\n * if (entry.isIntersecting) {\n * const section = entry.target;\n * const trackId = section.dataset.track;\n * console.log('Section viewed:', trackId);\n * // 发送统计事件\n * }\n * });\n * }, { threshold: 0.5 });\n *\n * sections.forEach(section => {\n * if (isInViewport(section)) {\n * const trackId = section.dataset.track;\n * console.log('Initial view:', trackId);\n * }\n * observer.observe(section);\n * });\n * ```\n */\nexport function isInViewport(element: HTMLElement): boolean {\n const rect = element.getBoundingClientRect()\n return (\n rect.top >= 0 &&\n rect.left >= 0 &&\n rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&\n rect.right <= (window.innerWidth || document.documentElement.clientWidth)\n )\n}\n\n/**\n * 将指定元素滚动到浏览器窗口的可见区域\n *\n * @param element - 要滚动到可见区域的 DOM 元素\n * @param options - 可选的滚动行为选项 (默认为平滑滚动)\n *\n * @example 基础滚动\n * ```typescript\n * const section = $('section');\n * scrollIntoView(section);\n * ```\n *\n * @example 不同的滚动位置\n * ```typescript\n * const section = $('section');\n * scrollIntoView(section);\n *\n * // 滚动到顶部\n * scrollIntoView(section, { block: 'start' });\n *\n * // 滚动到中间\n * scrollIntoView(section, { block: 'center' });\n *\n * // 滚动到底部\n * scrollIntoView(section, { block: 'end' });\n * ```\n *\n * @example 导航和锚点链接\n * ```typescript\n * const navLinks = $$('.nav a');\n *\n * navLinks.forEach(link => {\n * link.addEventListener('click', (e) => {\n * e.preventDefault();\n * const targetId = link.getAttribute('href');\n * const target = $(targetId);\n * if (target) {\n * scrollIntoView(target, { block: 'start' });\n * }\n * });\n * });\n * ```\n *\n * @example 表单验证反馈\n * ```typescript\n * const form = $('form');\n * const submitButton = $('button[type=\"submit\"]');\n *\n * form?.addEventListener('submit', (e) => {\n * e.preventDefault();\n *\n * const invalidInputs = $$('input:invalid');\n * if (invalidInputs.length > 0) {\n * // 滚动到第一个无效输入框\n * scrollIntoView(invalidInputs[0], { block: 'center' });\n *\n * // 添加错误高亮\n * invalidInputs.forEach(input => {\n * addClass(input, 'error');\n * });\n * }\n * });\n * ```\n *\n * @example 目录导航\n * ```typescript\n * const tocLinks = $$('.toc a');\n *\n * tocLinks.forEach(link => {\n * link.addEventListener('click', (e) => {\n * e.preventDefault();\n *\n * const targetId = link.getAttribute('href');\n * const target = $(targetId);\n * if (target) {\n * scrollIntoView(target, {\n * block: 'start',\n * inline: 'nearest',\n * behavior: 'smooth'\n * });\n * }\n * });\n * });\n * ```\n */\nexport function scrollIntoView(element: HTMLElement, options?: ScrollIntoViewOptions): void {\n element.scrollIntoView({ behavior: 'smooth', ...options })\n}\n","/**\n * 事件监听工具模块\n * 提供 DOM 元素的事件绑定、解绑和一次性监听功能\n *\n * 支持的事件目标类型:\n * - HTMLElement: DOM 元素节点\n * - Document: 文档对象\n * - Window: 窗口对象\n *\n * 注意事项:\n * - 使用相同参数多次调用 on() 会添加多个监听器,而不是覆盖\n * - 移除监听器时,参数必须与添加时完全一致(包括 useCapture)\n * - once() 监听器在触发后会自动移除,无需手动解绑\n * - 使用事件对象处理函数时,需要确保 handleEvent 方法正确实现\n */\n\n/**\n * 事件绑定\n * 在指定的元素上绑定事件监听器\n *\n * @param element - 目标元素\n * @param event - 事件名称(如 'click', 'mousedown' 等)\n * @param handler - 事件处理函数\n * @param useCapture - 是否在捕获阶段触发事件,默认为 false(冒泡阶段)\n *\n * @example 基础点击事件\n * ```typescript\n * on(document.body, 'click', () => {\n * console.log('Body clicked');\n * }, false);\n * ```\n *\n * @example 元素悬停效果\n * ```typescript\n * const button = $('button');\n * on(button, 'mouseenter', () => {\n * addClass(button, 'hover');\n * });\n * on(button, 'mouseleave', () => {\n * removeClass(button, 'hover');\n * });\n * ```\n *\n * @example 表单输入监听\n * ```typescript\n * const input = $('input[type=\"text\"]');\n * on(input, 'input', (e) => {\n * console.log('Input value:', (e.target as HTMLInputElement).value);\n * });\n * ```\n *\n * @example 使用事件对象处理函数\n * ```typescript\n * const handler = {\n * count: 0,\n * handleEvent(e: Event) {\n * this.count++;\n * console.log(`Event fired ${this.count} times`);\n * }\n * };\n * on(button, 'click', handler);\n * ```\n *\n * @example 捕获阶段监听\n * ```typescript\n * // 在父元素上使用捕获阶段拦截子元素事件\n * on(container, 'click', (e) => {\n * console.log('Click captured at container');\n * }, true);\n * ```\n */\nexport function on(\n element: HTMLElement | Document | Window,\n event: string,\n handler: EventListenerOrEventListenerObject,\n useCapture = false\n): void {\n if (element && event && handler) {\n element.addEventListener(event, handler, useCapture);\n }\n}\n\n/**\n * 事件解绑\n * 移除元素上的事件监听器\n *\n * @param element - 目标元素\n * @param event - 事件名称\n * @param handler - 事件处理函数\n * @param useCapture - 是否为捕获阶段的事件,必须与绑定时一致\n *\n * @example 基础解绑\n * ```typescript\n * const handler = () => console.log('Clicked');\n * on(button, 'click', handler);\n * // ... 某些条件下\n * off(button, 'click', handler);\n * ```\n *\n * @example 组件卸载时清理\n * ```typescript\n * // 在组件挂载时绑定\n * const resizeHandler = () => console.log('Window resized');\n * on(window, 'resize', resizeHandler);\n *\n * // 在组件卸载时清理\n * function cleanup() {\n * off(window, 'resize', resizeHandler);\n * }\n * ```\n *\n * @example 动态清理多个监听器\n * ```typescript\n * const handlers = {\n * click: null as (() => void) | null,\n * scroll: null as (() => void) | null\n * };\n *\n * // 绑定\n * handlers.click = () => console.log('Clicked');\n * handlers.scroll = () => console.log('Scrolled');\n * on(button, 'click', handlers.click);\n * on(window, 'scroll', handlers.scroll);\n *\n * // 清理\n * if (handlers.click) off(button, 'click', handlers.click);\n * if (handlers.scroll) off(window, 'scroll', handlers.scroll);\n * ```\n *\n * @example 捕获阶段事件解绑\n * ```typescript\n * const handler = () => console.log('Captured');\n * on(container, 'click', handler, true);\n * // 移除时必须也传入 true\n * off(container, 'click', handler, true);\n * ```\n */\nexport function off(\n element: HTMLElement | Document | Window,\n event: string,\n handler: EventListenerOrEventListenerObject,\n useCapture = false\n): void {\n if (element && event && handler) {\n element.removeEventListener(event, handler, useCapture);\n }\n}\n\n/**\n * 一次性事件监听\n * 绑定一个只触发一次的事件监听器,触发后自动移除\n *\n * @param element - 目标元素\n * @param event - 事件名称\n * @param handler - 事件处理函数\n * @param useCapture - 是否在捕获阶段触发事件,默认为 false\n *\n * @example 只执行一次的操作\n * ```typescript\n * once(button, 'click', () => {\n * console.log('This will only log once');\n * });\n * ```\n *\n * @example 页面加载初始化\n * ```typescript\n * once(document, 'DOMContentLoaded', () => {\n * console.log('DOM fully loaded');\n * // 执行初始化逻辑\n * });\n * ```\n *\n * @example 首次滚动触发\n * ```typescript\n * once(window, 'scroll', () => {\n * console.log('User scrolled for the first time');\n * // 显示提示或记录数据\n * });\n * ```\n *\n * @example 表单首次提交\n * ```typescript\n * once(form, 'submit', (e) => {\n * e.preventDefault();\n * console.log('First form submission');\n * // 特殊处理首次提交\n * });\n * ```\n *\n * @example 资源加载完成后清理\n * ```typescript\n * const image = new Image();\n * once(image, 'load', () => {\n * console.log('Image loaded successfully');\n * // 显示图片或触发其他逻辑\n * });\n * image.src = 'path/to/image.jpg';\n * ```\n *\n * @example 与事件对象结合\n * ```typescript\n * const counter = { clicks: 0 };\n * once(button, 'click', (e) => {\n * counter.clicks++;\n * console.log(`Clicked at: ${e.timeStamp}`);\n * });\n * ```\n */\nexport function once(\n element: HTMLElement | Document | Window,\n event: string,\n handler: EventListenerOrEventListenerObject,\n useCapture = false\n): void {\n if (!element || !event || !handler) return;\n\n const onceHandler: EventListenerOrEventListenerObject = (e: Event) => {\n if (typeof handler === 'function') {\n handler(e);\n } else if (handler.handleEvent) {\n handler.handleEvent(e);\n }\n element.removeEventListener(event, onceHandler, useCapture);\n };\n\n element.addEventListener(event, onceHandler, useCapture);\n}\n","import copy from 'copy-text-to-clipboard'\n\ninterface optionsType {\n target: HTMLElement\n}\n\n/**\n * 复制文本到剪贴板\n * @param text - 需要复制到剪贴板的文本\n * @param options - target: HTMLElement\n * @returns 是否复制成功(Boolean)\n * @example\n * ```ts\n * copyToClipboard('复制') // true\n * copyToClipboard('指定临时创建的 dom 存放处', { target: document.querySelector('#text') }) // true\n * ```\n * @public\n */\nexport function copyToClipboard(text: string, options?: optionsType): boolean {\n return copy(text, options)\n}\n","type StorageType = 'localStorage' | 'sessionStorage';\n\ninterface StorageManagerOptions {\n prefix?: string;\n storageType?: StorageType;\n}\n\ninterface StorageItem<T> {\n expiry?: number;\n value: T;\n}\n\nclass StorageManager {\n private prefix: string;\n private storage: Storage;\n\n constructor({\n prefix = '',\n storageType = 'localStorage',\n }: StorageManagerOptions = {}) {\n this.prefix = prefix;\n this.storage =\n storageType === 'localStorage'\n ? window.localStorage\n : window.sessionStorage;\n }\n\n /**\n * 清除所有带前缀的存储项\n */\n clear(): void {\n const keysToRemove: string[] = [];\n for (let i = 0; i < this.storage.length; i++) {\n const key = this.storage.key(i);\n if (key && key.startsWith(this.prefix)) {\n keysToRemove.push(key);\n }\n }\n keysToRemove.forEach((key) => this.storage.removeItem(key));\n }\n\n /**\n * 清除所有过期的存储项\n */\n clearExpiredItems(): void {\n for (let i = 0; i < this.storage.length; i++) {\n const key = this.storage.key(i);\n if (key && key.startsWith(this.prefix)) {\n const shortKey = key.replace(this.prefix, '');\n this.getItem(shortKey); // 调用 getItem 方法检查并移除过期项\n }\n }\n }\n\n /**\n * 获取存储项\n * @param key 键\n * @param defaultValue 当项不存在或已过期时返回的默认值\n * @returns 值,如果项已过期或解析错误则返回默认值\n */\n getItem<T>(key: string, defaultValue: null | T = null): null | T {\n const fullKey = this.getFullKey(key);\n const itemStr = this.storage.getItem(fullKey);\n if (!itemStr) {\n return defaultValue;\n }\n\n try {\n const item: StorageItem<T> = JSON.parse(itemStr);\n if (item.expiry && Date.now() > item.expiry) {\n this.storage.removeItem(fullKey);\n return defaultValue;\n }\n return item.value;\n } catch (error) {\n console.error(`Error parsing item with key \"${fullKey}\":`, error);\n this.storage.removeItem(fullKey); // 如果解析失败,删除该项\n return defaultValue;\n }\n }\n\n /**\n * 移除存储项\n * @param key 键\n */\n removeItem(key: string): void {\n const fullKey = this.getFullKey(key);\n this.storage.removeItem(fullKey);\n }\n\n /**\n * 设置存储项\n * @param key 键\n * @param value 值\n * @param ttl 存活时间(毫秒)\n */\n setItem<T>(key: string, value: T, ttl?: number): void {\n const fullKey = this.getFullKey(key);\n const expiry = ttl ? Date.now() + ttl : undefined;\n const item: StorageItem<T> = { expiry, value };\n try {\n this.storage.setItem(fullKey, JSON.stringify(item));\n } catch (error) {\n console.error(`Error setting item with key \"${fullKey}\":`, error);\n }\n }\n\n /**\n * 获取完整的存储键\n * @param key 原始键\n * @returns 带前缀的完整键\n */\n private getFullKey(key: string): string {\n return `${this.prefix}-${key}`;\n }\n}\n\nexport { StorageManager };\n","import type { OpenWindowOptions } from './types';\n\n/**\n * 在新窗口中打开 URL\n *\n * 使用 window.open 方法打开指定 URL,支持配置窗口行为和安全特性\n *\n * @param url - 需要打开的网址\n * @param options - 打开窗口的选项\n * @param options.noopener - 是否启用 noopener,默认为 true\n * @param options.noreferrer - 是否启用 noreferrer,默认为 true\n * @param options.target - 打开目标窗口,默认为 '_blank'\n *\n * @example 打开新窗口\n * ```typescript\n * openWindow('https://example.com');\n * ```\n *\n * @example 在当前窗口打开\n * ```typescript\n * openWindow('https://example.com', { target: '_self' });\n * ```\n *\n * @example 禁用安全特性\n * ```typescript\n * openWindow('https://example.com', {\n * noopener: false,\n * noreferrer: false\n * });\n * ```\n *\n * @example 在父窗口打开\n * ```typescript\n * openWindow('https://example.com', { target: '_parent' });\n * ```\n */\nexport function openWindow(\n url: string,\n options: OpenWindowOptions = {}\n): void {\n // 解构并设置默认值\n const {\n noopener = true,\n noreferrer = true,\n target = '_blank'\n } = options;\n\n // 基于选项创建特性字符串\n const features = [\n noopener && 'noopener=yes',\n noreferrer && 'noreferrer=yes'\n ]\n .filter(Boolean)\n .join(',');\n\n // 打开窗口\n window.open(url, target, features);\n}\n","import { openWindow } from './openWindow';\n\n/**\n * 在新窗口中打开当前应用的路由\n *\n * 基于当前页面的 origin 和 hash 构建完整的路由地址,然后在新窗口中打开\n *\n * @param path - 路由路径\n *\n * @example 打开路由\n * ```typescript\n * openRouteInNewWindow('/dashboard');\n * ```\n *\n * @example 打开带参数的路由\n * ```typescript\n * openRouteInNewWindow('/user/profile?id=123');\n * ```\n *\n * @example 不带前导斜杠\n * ```typescript\n * openRouteInNewWindow('settings');\n * // 等同于 '/settings'\n * ```\n *\n * @example 在 Hash 路由模式下\n * ```typescript\n * // 当前 URL: https://example.com/#/home\n * openRouteInNewWindow('/about');\n * // 将打开: https://example.com/#/about\n * ```\n */\nexport function openRouteInNewWindow(path: string): void {\n const { hash, origin } = location;\n const fullPath = path.startsWith('/') ? path : `/${path}`;\n const url = `${origin}${hash && !fullPath.startsWith('/#') ? '/#' : ''}${fullPath}`;\n openWindow(url, { target: '_blank' });\n}\n","/**\n * 获取 URL 中的查询参数\n *\n * 支持从当前页面 URL 或指定 URL 中提取查询参数\n *\n * @param url - 要解析的 URL 字符串。如果不提供,则使用当前页面 URL\n * @returns 返回包含所有查询参数的对象\n *\n * @example 获取当前页面查询参数\n * ```typescript\n * // 当前 URL: https://example.com?page=1&sort=desc&filter=active\n * const params = getURLSearchParams();\n * console.log(params); // { page: '1', sort: 'desc', filter: 'active' }\n * ```\n *\n * @example 从指定 URL 获取参数\n * ```typescript\n * const url = 'https://example.com/search?q=hello&lang=zh-CN';\n * const params = getURLSearchParams(url);\n * console.log(params); // { q: 'hello', lang: 'zh-CN' }\n * ```\n *\n * @example 获取单个参数\n * ```typescript\n * const params = getURLSearchParams();\n * const page = params.page; // '1'\n * const count = parseInt(params.count || '0'); // 带默认值转换\n * ```\n *\n * @example 处理重复参数(只取最后一个值)\n * ```typescript\n * // URL: https://example.com?id=1&id=2&id=3\n * const params = getURLSearchParams('https://example.com?id=1&id=2&id=3');\n * console.log(params.id); // '3'(取最后一个)\n * ```\n *\n * @example 处理无参数的情况\n * ```typescript\n * const params = getURLSearchParams('https://example.com');\n * console.log(params); // {}\n * ```\n */\nexport function getURLSearchParams(url?: string): Record<string, string> {\n // 获取查询字符串\n let search: string;\n if (url) {\n search = new URL(url).search;\n } else {\n search = window.location.search;\n }\n\n // 移除开头的 '?'\n const queryString = search.startsWith('?') ? search.slice(1) : search;\n\n // 优先使用 URLSearchParams API(性能更好)\n if (typeof URLSearchParams !== 'undefined') {\n const searchParams = new URLSearchParams(queryString);\n const params: Record<string, string> = {};\n searchParams.forEach((value, key) => {\n params[key] = value;\n });\n return params;\n }\n\n // 降级方案:手动解析(兼容旧浏览器)\n const params: Record<string, string> = {};\n if (!queryString) {\n return params;\n }\n\n const pairs = queryString.split('&');\n for (const pair of pairs) {\n const [encodedKey, encodedValue = ''] = pair.split('=');\n if (!encodedKey) continue;\n\n // 解码键和值\n const key = decodeURIComponent(encodedKey.replace(/\\+/g, ' '));\n const value = decodeURIComponent(encodedValue.replace(/\\+/g, ' '));\n\n // 只保留最后一个值(与 URLSearchParams 行为一致)\n params[key] = value;\n }\n\n return params;\n}\n","/**\n * 给 URL 设置或更新查询参数\n *\n * 支持添加新参数、更新现有参数、删除参数\n *\n * @param url - 要处理的 URL 字符串。如果不提供,则使用当前页面 URL\n * @param params - 要设置的查询参数对象\n * @returns 返回包含更新后查询参数的 URL\n *\n * @example 添加新参数\n * ```typescript\n * const url = 'https://example.com';\n * const newUrl = setURLSearchParams(url, { page: '1', sort: 'desc' });\n * console.log(newUrl); // 'https://example.com?page=1&sort=desc'\n * ```\n *\n * @example 更新现有参数\n * ```typescript\n * const url = 'https://example.com?page=1&sort=desc';\n * const newUrl = setURLSearchParams(url, { page: '2', filter: 'active' });\n * console.log(newUrl); // 'https://example.com?page=2&sort=desc&filter=active'\n * ```\n *\n * @example 删除参数(设为 undefined)\n * ```typescript\n * const url = 'https://example.com?page=1&sort=desc&filter=active';\n * const newUrl = setURLSearchParams(url, { filter: undefined });\n * console.log(newUrl); // 'https://example.com?page=1&sort=desc'\n * ```\n *\n * @example 使用当前页面 URL\n * ```typescript\n * // 当前 URL: https://example.com?page=1\n * const newUrl = setURLSearchParams(undefined, { page: '2', size: '10' });\n * console.log(newUrl); // 'https://example.com?page=2&size=10'\n * ```\n *\n * @example 处理特殊字符\n * ```typescript\n * const url = 'https://example.com/search';\n * const newUrl = setURLSearchParams(url, { q: 'hello world' });\n * console.log(newUrl); // 'https://example.com/search?q=hello%20world'\n * ```\n */\nexport function setURLSearchParams(\n url?: string,\n params?: Record<string, string | undefined>\n): string {\n // 确定 URL\n const baseUrl = url || window.location.href;\n\n // 创建 URL 对象\n const urlObj = new URL(baseUrl);\n\n // 如果没有参数,直接返回原 URL\n if (!params) {\n return urlObj.toString();\n }\n\n // 使用 URLSearchParams API(优先)\n if (typeof URLSearchParams !== 'undefined') {\n const searchParams = urlObj.searchParams;\n\n // 更新参数\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined) {\n searchParams.delete(key);\n } else {\n searchParams.set(key, value);\n }\n }\n\n return urlObj.toString();\n }\n\n // 降级方案:手动处理\n let queryString = urlObj.search.slice(1); // 移除 '?'\n const paramPairs = queryString ? queryString.split('&') : [];\n const paramMap: Record<string, string> = {};\n\n // 解析现有参数\n for (const pair of paramPairs) {\n const [key, encodedValue = ''] = pair.split('=');\n if (key) {\n paramMap[key] = decodeURIComponent(encodedValue.replace(/\\+/g, ' '));\n }\n }\n\n // 更新/删除参数\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined) {\n delete paramMap[key];\n } else {\n paramMap[key] = value;\n }\n }\n\n // 重新构建查询字符串\n const newParams = Object.entries(paramMap)\n .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)\n .join('&');\n\n urlObj.search = newParams ? `?${newParams}` : '';\n\n return urlObj.toString();\n}\n","/**\n * 下载功能常量\n */\n\n/**\n * 默认下载文件名\n */\nexport const DEFAULT_FILENAME = 'downloaded_file';\n","/**\n * 通用下载触发函数\n *\n * 通过创建临时 `<a>` 标签并触发点击事件来下载文件,支持自动清理内存\n *\n * @param href - 文件下载的 URL 或 Blob URL\n * @param fileName - 下载文件的名称,如果未提供则使用默认值\n * @param revokeDelay - 清理 URL 的延迟时间(毫秒),默认为 100ms\n *\n * @example 下载文件\n * ```typescript\n * triggerDownload('https://example.com/file.pdf', 'document.pdf');\n * ```\n *\n * @example 使用 Blob URL\n * ```typescript\n * const blob = new Blob(['content'], { type: 'text/plain' });\n * const blobUrl = URL.createObjectURL(blob);\n * triggerDownload(blobUrl, 'file.txt', 200);\n * ```\n *\n * @example 使用默认文件名\n * ```typescript\n * triggerDownload('https://example.com/data.json', undefined);\n * ```\n */\nexport function triggerDownload(\n href: string,\n fileName: string | undefined,\n revokeDelay: number = 100,\n): void {\n const defaultFileName = 'downloaded_file';\n const finalFileName = fileName || defaultFileName;\n\n const link = document.createElement('a');\n link.href = href;\n link.download = finalFileName;\n link.style.display = 'none';\n\n if (link.download === undefined) {\n link.setAttribute('target', '_blank');\n }\n\n document.body.append(link);\n link.click();\n link.remove();\n\n // 清理临时 URL 以释放内存\n setTimeout(() => URL.revokeObjectURL(href), revokeDelay);\n}\n","/**\n * 下载功能工具函数\n */\n\nimport { DEFAULT_FILENAME } from './constants';\n\n/**\n * 解析文件名\n *\n * 从 URL 中提取文件名,如果未提供文件名或 URL 中没有文件名,则使用默认文件名\n *\n * @param url - 文件 URL 地址\n * @param fileName - 指定的文件名(可选)\n *\n * @returns string - 解析后的文件名\n *\n * @example 使用指定文件名\n * ```typescript\n * const name = resolveFileName('https://example.com/file.pdf', 'custom.pdf');\n * console.log(name); // 'custom.pdf'\n * ```\n *\n * @example 从 URL 提取文件名\n * ```typescript\n * const name = resolveFileName('https://example.com/path/to/document.pdf');\n * console.log(name); // 'document.pdf'\n * ```\n *\n * @example 使用默认文件名\n * ```typescript\n * const name = resolveFileName('https://example.com/path/');\n * console.log(name); // 'downloaded_file'\n * ```\n */\nexport function resolveFileName(\n url: string,\n fileName?: string,\n): string {\n return fileName || url.slice(url.lastIndexOf('/') + 1) || DEFAULT_FILENAME;\n}\n\n","import { openWindow } from '../window';\nimport { DownloadOptions } from './types';\nimport { triggerDownload } from './triggerDownload';\nimport { resolveFileName } from './utils';\n\n/**\n * 通过 URL 下载文件,支持跨域\n *\n * @param options - 下载选项\n * @param options.fileName - 下载文件名\n * @param options.source - 文件 URL\n * @param options.target - 打开目标,默认为 '_blank'\n *\n * @throws {Error} - 当下载失败时抛出错误\n *\n * @example 下载 URL 文件\n * ```typescript\n * await downloadFileFromUrl({\n * source: 'https://example.com/file.pdf',\n * fileName: 'document.pdf'\n * });\n * ```\n *\n * @example 跨域下载\n * ```typescript\n * await downloadFileFromUrl({\n * source: 'https://cdn.example.com/image.jpg',\n * fileName: 'image.jpg'\n * });\n * ```\n */\nexport async function downloadFileFromUrl({\n fileName,\n source,\n target = '_blank',\n}: DownloadOptions): Promise<void> {\n if (!source || typeof source !== 'string') {\n throw new Error('Invalid URL.');\n }\n\n const isChrome = window.navigator.userAgent.toLowerCase().includes('chrome');\n const isSafari = window.navigator.userAgent.toLowerCase().includes('safari');\n\n if (/iP/.test(window.navigator.userAgent)) {\n console.error('Your browser does not support download!');\n return;\n }\n\n if (isChrome || isSafari) {\n triggerDownload(source, resolveFileName(source, fileName));\n return;\n }\n if (!source.includes('?')) {\n source += '?download';\n }\n\n openWindow(source, { target });\n}\n","import { DownloadOptions } from './types';\nimport { triggerDownload } from './triggerDownload';\nimport { DEFAULT_FILENAME } from './constants';\n\n/**\n * 通过 Base64 数据下载文件\n *\n * @param options - 下载选项\n * @param options.fileName - 下载文件名\n * @param options.source - Base64 编码的文件数据\n *\n * @throws {Error} - 当 Base64 数据无效时抛出错误\n *\n * @example 下载 Base64 图片\n * ```typescript\n * downloadFileFromBase64({\n * source: 'data:image/png;base64,iVBORw0KGgoAAAANS...',\n * fileName: 'image.png'\n * });\n * ```\n *\n * @example 使用默认文件名\n * ```typescript\n * downloadFileFromBase64({\n * source: 'data:text/plain;base64,SGVsbG8gV29ybGQ='\n * });\n * ```\n */\nexport function downloadFileFromBase64({ fileName, source }: DownloadOptions) {\n if (!source || typeof source !== 'string') {\n throw new Error('Invalid Base64 data.');\n }\n\n const resolvedFileName = fileName || DEFAULT_FILENAME;\n triggerDownload(source, resolvedFileName);\n}\n","/**\n * 将图片 URL 转换为 Base64 编码\n *\n * @param url - 图片 URL 地址\n * @param mineType - 指定输出的 MIME 类型,默认为 'image/png'\n *\n * @returns 返回 Base64 编码的图片数据\n *\n * @example 转换为 PNG\n * ```typescript\n * const base64 = await urlToBase64('https://example.com/image.jpg');\n * console.log(base64); // 'data:image/png;base64,...'\n * ```\n *\n * @example 指定 MIME 类型\n * ```typescript\n * const jpegBase64 = await urlToBase64(\n * 'https://example.com/image.png',\n * 'image/jpeg'\n * );\n * ```\n *\n * @example 下载图片\n * ```typescript\n * const base64Data = await urlToBase64('https://example.com/photo.jpg');\n * downloadFileFromBase64({\n * source: base64Data,\n * fileName: 'photo.jpg'\n * });\n * ```\n */\nexport function urlToBase64(url: string, mineType?: string): Promise<string> {\n return new Promise((resolve, reject) => {\n let canvas = document.createElement('CANVAS') as HTMLCanvasElement | null;\n const ctx = canvas?.getContext('2d');\n const img = new Image();\n img.crossOrigin = '';\n img.addEventListener('load', () => {\n if (!canvas || !ctx) {\n return reject(new Error('Failed to create canvas.'));\n }\n canvas.height = img.height;\n canvas.width = img.width;\n ctx.drawImage(img, 0, 0);\n const dataURL = canvas.toDataURL(mineType || 'image/png');\n canvas = null;\n resolve(dataURL);\n });\n img.src = url;\n });\n}\n","import { DownloadOptions } from './types';\nimport { urlToBase64 } from './urlToBase64';\nimport { downloadFileFromBase64 } from './downloadFileFromBase64';\n\n/**\n * 通过图片 URL 下载图片文件\n *\n * @param options - 下载选项\n * @param options.fileName - 下载文件名\n * @param options.source - 图片 URL\n *\n * @example 下载网络图片\n * ```typescript\n * await downloadFileFromImageUrl({\n * source: 'https://example.com/image.jpg',\n * fileName: 'downloaded-image.jpg'\n * });\n * ```\n *\n * @example 下载 SVG 图片\n * ```typescript\n * await downloadFileFromImageUrl({\n * source: 'https://example.com/icon.svg',\n * fileName: 'icon.svg'\n * });\n * ```\n */\nexport async function downloadFileFromImageUrl({\n fileName,\n source,\n}: DownloadOptions) {\n const base64 = await urlToBase64(source);\n downloadFileFromBase64({ fileName, source: base64 });\n}\n","import { DownloadOptions } from './types';\nimport { triggerDownload } from './triggerDownload';\nimport { DEFAULT_FILENAME } from './constants';\n\n/**\n * 通过 Blob 对象下载文件\n *\n * @param options - 下载选项\n * @param options.fileName - 下载文件名,默认为 'downloaded_file'\n * @param options.source - Blob 对象\n *\n * @throws {TypeError} - 当源数据不是 Blob 对象时抛出错误\n *\n * @example 下载文本 Blob\n * ```typescript\n * const textBlob = new Blob(['Hello World'], { type: 'text/plain' });\n * downloadFileFromBlob({\n * source: textBlob,\n * fileName: 'hello.txt'\n * });\n * ```\n *\n * @example 下载 JSON Blob\n * ```typescript\n * const jsonData = { name: 'test', value: 123 };\n * const jsonBlob = new Blob([JSON.stringify(jsonData)], { type: 'application/json' });\n * downloadFileFromBlob({\n * source: jsonBlob,\n * fileName: 'data.json'\n * });\n * ```\n */\nexport function downloadFileFromBlob({\n fileName = DEFAULT_FILENAME,\n source,\n}: DownloadOptions<Blob>): void {\n if (!(source instanceof Blob)) {\n throw new TypeError('Invalid Blob data.');\n }\n\n const url = URL.createObjectURL(source);\n triggerDownload(url, fileName);\n}\n","import { DownloadOptions } from './types';\nimport { triggerDownload } from './triggerDownload';\nimport { DEFAULT_FILENAME } from './constants';\n\n/**\n * 通过 BlobPart 数据下载文件\n *\n * 支持 Blob、字符串和其他 BlobPart 类型,如果不是 Blob 会自动转换\n *\n * @param options - 下载选项\n * @param options.fileName - 下载文件名,默认为 'downloaded_file'\n * @param options.source - BlobPart 数据 (Blob、字符串、ArrayBuffer 等)\n *\n * @example 下载字符串\n * ```typescript\n * downloadFileFromBlobPart({\n * source: 'Hello World',\n * fileName: 'text.txt'\n * });\n * ```\n *\n * @example 下载 ArrayBuffer\n * ```typescript\n * const arrayBuffer = new Uint8Array([72, 101, 108, 108, 111]);\n * downloadFileFromBlobPart({\n * source: arrayBuffer,\n * fileName: 'data.bin'\n * });\n * ```\n *\n * @example 下载 Blob 对象\n * ```typescript\n * const blob = new Blob(['content'], { type: 'text/plain' });\n * downloadFileFromBlobPart({\n * source: blob,\n * fileName: 'file.txt'\n * });\n * ```\n */\nexport function downloadFileFromBlobPart({\n fileName = DEFAULT_FILENAME,\n source,\n}: DownloadOptions<BlobPart>): void {\n // 如果 data 不是 Blob,则转换为 Blob\n const blob =\n source instanceof Blob\n ? source\n : new Blob([source], { type: 'application/octet-stream' });\n\n // 创建对象 URL 并触发下载\n const url = URL.createObjectURL(blob);\n triggerDownload(url, fileName);\n}\n","/**\n * 将 Blob 转换为 ArrayBuffer(高性能版本)\n *\n * 使用 Blob.arrayBuffer() API,比 FileReader 性能更优\n * 兼容性:Chrome 76+, Firefox 69+, Safari 14+, Edge 79+\n *\n * @param blob - 要转换的 Blob 对象\n *\n * @returns 返回包含 ArrayBuffer 的 Promise\n *\n * @example 基本使用\n * ```typescript\n * const blob = new Blob(['hello world'], { type: 'text/plain' });\n * const arrayBuffer = await blobToArrayBuffer(blob);\n * console.log(arrayBuffer.byteLength); // 11\n * ```\n *\n * @example 从文件读取\n * ```typescript\n * const fileInput = document.querySelector('input[type=\"file\"]');\n * const file = fileInput.files[0];\n * const arrayBuffer = await blobToArrayBuffer(file);\n * const text = new TextDecoder().decode(arrayBuffer);\n * console.log(text); // 文件内容\n * ```\n *\n * @example 配合 fetch 使用\n * ```typescript\n * const blob = await fetch('https://example.com/data.bin').then(r => r.blob());\n * const arrayBuffer = await blobToArrayBuffer(blob);\n * // 处理二进制数据...\n * ```\n */\nexport async function blobToArrayBuffer(blob: Blob): Promise<ArrayBuffer> {\n // 优先使用 Blob.arrayBuffer(),性能最优\n if (typeof blob.arrayBuffer === 'function') {\n return await blob.arrayBuffer();\n }\n\n // 降级方案:使用 FileReader(兼容旧浏览器)\n return new Promise<ArrayBuffer>((resolve, reject) => {\n const reader = new FileReader();\n\n reader.onload = () => {\n if (reader.result instanceof ArrayBuffer) {\n resolve(reader.result);\n } else {\n reject(new Error('FileReader did not return an ArrayBuffer'));\n }\n };\n\n reader.onerror = () => {\n reject(new Error(`Failed to read Blob: ${reader.error?.message || 'Unknown error'}`));\n };\n\n reader.readAsArrayBuffer(blob);\n });\n}\n","/**\n * 将 ArrayBuffer 转换为 Blob\n *\n * @param arrayBuffer - 要转换的 ArrayBuffer 对象\n * @param options - Blob 配置选项\n * @param options.type - MIME 类型\n * @param options.endings - 行结束符处理方式('transparent' | 'native')\n *\n * @returns 返回 Blob 对象\n *\n * @example 基本使用\n * ```typescript\n * const text = 'hello world';\n * const arrayBuffer = new TextEncoder().encode(text);\n * const blob = arrayBufferToBlob(arrayBuffer);\n * console.log(blob.type); // ''\n * console.log(blob.size); // 11\n * ```\n *\n * @example 指定 MIME 类型\n * ```typescript\n * const arrayBuffer = new Uint8Array([255, 216, 255, 224]);\n * const jpegBlob = arrayBufferToBlob(arrayBuffer, { type: 'image/jpeg' });\n * console.log(jpegBlob.type); // 'image/jpeg'\n * ```\n *\n * @example 配合下载使用\n * ```typescript\n * const data = { name: 'test' };\n * const jsonStr = JSON.stringify(data);\n * const arrayBuffer = new TextEncoder().encode(jsonStr);\n * const blob = arrayBufferToBlob(arrayBuffer, { type: 'application/json' });\n * // 保存或上传 blob...\n * ```\n */\nexport function arrayBufferToBlob(\n arrayBuffer: ArrayBuffer,\n options?: BlobPropertyBag\n): Blob {\n return new Blob([arrayBuffer], options);\n}\n","/**\n * 将 ArrayBuffer 转换为 Base64 字符串\n *\n * @param arrayBuffer - 要转换的 ArrayBuffer 对象\n *\n * @returns 返回 Base64 编码字符串\n *\n * @example 基本使用\n * ```typescript\n * const text = 'hello world';\n * const arrayBuffer = new TextEncoder().encode(text);\n * const base64 = arrayBufferToBase64(arrayBuffer);\n * console.log(base64); // 'aGVsbG8gd29ybGQ='\n * ```\n *\n * @example 配合文件上传\n * ```typescript\n * const fileInput = document.querySelector('input[type=\"file\"]');\n * const file = fileInput.files[0];\n * const arrayBuffer = await file.arrayBuffer();\n * const base64 = arrayBufferToBase64(arrayBuffer);\n * // 可用于上传或显示\n * ```\n *\n * @example 生成图片 Base64\n * ```typescript\n * const blob = await fetch('image.png').then(r => r.blob());\n * const arrayBuffer = await blobToArrayBuffer(blob);\n * const base64 = arrayBufferToBase64(arrayBuffer);\n * const dataUrl = `data:image/png;base64,${base64}`;\n * ```\n */\nexport function arrayBufferToBase64(arrayBuffer: ArrayBuffer): string {\n const bytes = new Uint8Array(arrayBuffer);\n let binary = '';\n\n // 每次处理 8192 个字节,避免字符串过长导致性能问题\n const chunkSize = 8192;\n for (let i = 0; i < bytes.length; i += chunkSize) {\n const chunk = bytes.subarray(i, i + chunkSize);\n binary += String.fromCharCode.apply(null, Array.from(chunk));\n }\n\n return btoa(binary);\n}\n","/**\n * 将 Base64 字符串转换为 Blob\n *\n * 使用分片处理,兼顾性能和内存占用\n *\n * @param base64 - Base64 编码字符串\n * @param options - Blob 配置选项\n * @param options.type - MIME 类型\n * @param options.endings - 行结束符处理方式('transparent' | 'native')\n * @param sliceSize - 分片大小(字节),默认 8192\n *\n * @returns 返回 Blob 对象\n *\n * @example 基本使用\n * ```typescript\n * const base64 = 'aGVsbG8gd29ybGQ=';\n * const blob = base64ToBlob(base64);\n * console.log(blob.size); // 11\n * ```\n *\n * @example 指定 MIME 类型\n * ```typescript\n * const jpegBase64 = '/9j/4AAQSkZJRg...';\n * const jpegBlob = base64ToBlob(jpegBase64, { type: 'image/jpeg' });\n * ```\n *\n * @example 从 Data URL 转换\n * ```typescript\n * const dataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANS...';\n * const base64 = dataUrl.split(',')[1];\n * const blob = base64ToBlob(base64, { type: 'image/png' });\n * ```\n *\n * @example 自定义分片大小(处理大文件)\n * ```typescript\n * const largeBase64 = '...'; // 大文件 Base64\n * const blob = base64ToBlob(largeBase64, { type: 'application/octet-stream' }, 16384);\n * ```\n */\nexport function base64ToBlob(\n base64: string,\n options?: BlobPropertyBag,\n sliceSize: number = 8192\n): Blob {\n // 将 Base64 解码为二进制字符串\n const binaryString = atob(base64);\n const byteArrays: BlobPart[] = [];\n\n // 分片处理,避免大数组导致的性能问题\n for (let offset = 0; offset < binaryString.length; offset += sliceSize) {\n const slice = binaryString.slice(offset, offset + sliceSize);\n const bytes = new Uint8Array(slice.length);\n\n for (let i = 0; i < slice.length; i++) {\n bytes[i] = slice.charCodeAt(i);\n }\n\n byteArrays.push(bytes);\n }\n\n return new Blob(byteArrays, options);\n}\n","import {blobToArrayBuffer} from \"./blobToArrayBuffer\";\nimport {arrayBufferToBase64} from \"./arrayBufferToBase64\";\n\n/**\n * 将 Blob 转换为 Base64 字符串\n *\n * @param blob - 要转换的 Blob 对象\n *\n * @returns 返回 Base64 编码字符串\n *\n * @example 基本使用\n * ```typescript\n * const blob = new Blob(['hello world'], { type: 'text/plain' });\n * const base64 = await blobToBase64(blob);\n * console.log(base64); // 'aGVsbG8gd29ybGQ='\n * ```\n *\n * @example 从文件转 Base64\n * ```typescript\n * const fileInput = document.querySelector('input[type=\"file\"]');\n * const file = fileInput.files[0];\n * const base64 = await blobToBase64(file);\n * const dataUrl = `data:${file.type};base64,${base64}`;\n * ```\n *\n * @example 配合上传使用\n * ```typescript\n * const blob = await fetch('https://example.com/image.png').then(r => r.blob());\n * const base64 = await blobToBase64(blob);\n * // 上传 base64 数据...\n * ```\n */\nexport async function blobToBase64(blob: Blob): Promise<string> {\n const arrayBuffer = await blobToArrayBuffer(blob);\n return arrayBufferToBase64(arrayBuffer);\n}\n","/**\n * 将 Blob 转换为 Object URL\n *\n * 生成的 URL 必须手动调用 URL.revokeObjectURL() 释放,否则会造成内存泄漏\n *\n * @param blob - 要转换的 Blob 对象\n *\n * @returns 返回 Object URL(blob: 开头的 URL)\n *\n * @example 基本使用\n * ```typescript\n * const blob = new Blob(['hello world'], { type: 'text/plain' });\n * const url = blobToObjectURL(blob);\n * console.log(url); // 'blob:https://example.com/uuid'\n * // 使用完后记得释放\n * URL.revokeObjectURL(url);\n * ```\n *\n * @example 显示图片\n * ```typescript\n * const blob = await fetch('image.png').then(r => r.blob());\n * const url = blobToObjectURL(blob);\n * const img = document.createElement('img');\n * img.src = url;\n * document.body.appendChild(img);\n * // 图片加载后或组件卸载时释放\n * URL.revokeObjectURL(url);\n * ```\n *\n * @example 下载文件\n * ```typescript\n * const blob = new Blob(['file content'], { type: 'text/plain' });\n * const url = blobToObjectURL(blob);\n * const a = document.createElement('a');\n * a.href = url;\n * a.download = 'file.txt';\n * a.click();\n * // 点击后释放\n * URL.revokeObjectURL(url);\n * ```\n *\n * @example 结合 React 使用\n * ```typescript\n * useEffect(() => {\n * const blob = createBlob();\n * const url = blobToObjectURL(blob);\n * setSrc(url);\n *\n * return () => {\n * URL.revokeObjectURL(url); // 清理函数中释放\n * };\n * }, []);\n * ```\n */\nexport function blobToObjectURL(blob: Blob): string {\n return URL.createObjectURL(blob);\n}\n","/**\n * 将 Blob 转换为 Data URL\n *\n * Data URL 格式: `data:[<mediatype>][;base64],<data>`\n *\n * @param blob - 要转换的 Blob 对象\n *\n * @returns 返回 Data URL 字符串\n *\n * @example 基本使用\n * ```typescript\n * const blob = new Blob(['hello world'], { type: 'text/plain' });\n * const dataUrl = await blobToDataURL(blob);\n * console.log(dataUrl); // 'data:text/plain;base64,aGVsbG8gd29ybGQ='\n * ```\n *\n * @example 显示图片\n * ```typescript\n * const blob = await fetch('image.png').then(r => r.blob());\n * const dataUrl = await blobToDataURL(blob);\n * const img = document.createElement('img');\n * img.src = dataUrl;\n * document.body.appendChild(img);\n * ```\n *\n * @example 背景图片\n * ```typescript\n * const blob = await fetch('background.jpg').then(r => r.blob());\n * const dataUrl = await blobToDataURL(blob);\n * element.style.backgroundImage = `url(${dataUrl})`;\n * ```\n *\n * @example 下载文件\n * ```typescript\n * const blob = new Blob(['file content'], { type: 'text/plain' });\n * const dataUrl = await blobToDataURL(blob);\n * const a = document.createElement('a');\n * a.href = dataUrl;\n * a.download = 'file.txt';\n * a.click();\n * ```\n */\nexport async function blobToDataURL(blob: Blob): Promise<string> {\n return new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n\n reader.onload = () => {\n if (typeof reader.result === 'string') {\n resolve(reader.result);\n } else {\n reject(new Error('FileReader did not return a Data URL'));\n }\n };\n\n reader.onerror = () => {\n reject(new Error(`Failed to read Blob: ${reader.error?.message || 'Unknown error'}`));\n };\n\n reader.readAsDataURL(blob);\n });\n}\n"],"mappings":"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"}
|