a2bei4-utils 1.0.4 → 1.0.6
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/a2bei4.utils.cjs.js +221 -7
- package/dist/a2bei4.utils.cjs.js.map +1 -1
- package/dist/a2bei4.utils.cjs.min.js +1 -1
- package/dist/a2bei4.utils.cjs.min.js.map +1 -1
- package/dist/a2bei4.utils.esm.js +220 -8
- package/dist/a2bei4.utils.esm.js.map +1 -1
- package/dist/a2bei4.utils.esm.min.js +1 -1
- package/dist/a2bei4.utils.esm.min.js.map +1 -1
- package/dist/a2bei4.utils.umd.js +221 -7
- package/dist/a2bei4.utils.umd.js.map +1 -1
- package/dist/a2bei4.utils.umd.min.js +1 -1
- package/dist/a2bei4.utils.umd.min.js.map +1 -1
- package/dist/menu.cjs +202 -0
- package/dist/menu.cjs.map +1 -0
- package/dist/menu.js +200 -0
- package/dist/menu.js.map +1 -0
- package/dist/tree.cjs +22 -7
- package/dist/tree.cjs.map +1 -1
- package/dist/tree.js +22 -8
- package/dist/tree.js.map +1 -1
- package/package.json +12 -7
- package/types/index.d.ts +85 -2
- package/types/menu.d.ts +62 -0
- package/types/tree.d.ts +24 -2
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function e(e){for(let t=e.length-1;t>0;t--){const n=Math.floor(Math.random()*(t+1));[e[t],e[n]]=[e[n],e[t]]}return e}function t(e,t,n){e.splice(n,0,e.splice(t,1)[0])}class n{constructor(e){this.onData=e.onData||(()=>{}),this.onStateChange=e.onStateChange||(()=>{}),this.processorOptions=e.processorOptions||{},this.saveFullPcm=e.saveFullPcm??!1,this.audioContext=null,this.workletNode=null,this.source=null,this.workletUrl=null,this.fullPcmData=this.saveFullPcm?[]:null,this.isInitialized=!1,this.isProcessing=!1}async init(){this.onStateChange("initializing","正在初始化音频环境...");try{this.audioContext=new(window.AudioContext||window.webkitAudioContext);const e=new Blob(["\nclass AudioStreamResamplerProcessor extends AudioWorkletProcessor {\n constructor(options) {\n super();\n const config = options.processorOptions || {};\n this.targetSampleRate = config.targetSampleRate || 16000;\n this.sourceSampleRate = sampleRate;\n this.downsampleRatio = this.sourceSampleRate / this.targetSampleRate;\n\n // 16000Hz 用 60ms chunk (960 samples),其他用 1024\n this.chunkSize = this.targetSampleRate === 16000 ? 960 : 1024;\n\n const sourceBufferSize = config.sourceBufferSize || 16384; // ~340ms @48kHz\n const pcmBufferSize = config.pcmBufferSize || (this.chunkSize * 10); // 更大缓冲,减少溢出概率\n\n this.sourceBuffer = new Float32Array(sourceBufferSize);\n this.sourceBufferLength = 0;\n\n this.pcmBuffer = new Int16Array(pcmBufferSize);\n this.pcmBufferIndex = 0;\n }\n\n process(inputs, outputs, parameters) {\n const input = inputs[0];\n if (!input || input.length === 0 || input[0].length === 0) return true;\n const inputChannel = input[0];\n\n // 1. 写入源缓冲区(溢出时覆盖最旧数据)\n const newLength = this.sourceBufferLength + inputChannel.length;\n if (newLength > this.sourceBuffer.length) {\n const overflow = newLength - this.sourceBuffer.length;\n this.sourceBuffer.copyWithin(0, overflow);\n this.sourceBufferLength = this.sourceBuffer.length - inputChannel.length;\n }\n this.sourceBuffer.set(inputChannel, this.sourceBufferLength);\n this.sourceBufferLength += inputChannel.length;\n\n // 2. 计算可降采样样本数\n const availableOutputSamples = Math.floor(this.sourceBufferLength / this.downsampleRatio);\n if (availableOutputSamples === 0) return true;\n\n // 3. 线性插值降采样\n const downsampled = new Float32Array(availableOutputSamples);\n for (let i = 0; i < availableOutputSamples; i++) {\n const srcIndex = i * this.downsampleRatio;\n const srcIndexInt = Math.floor(srcIndex);\n const fraction = srcIndex - srcIndexInt;\n const val0 = this.sourceBuffer[srcIndexInt];\n const val1 = srcIndexInt + 1 < this.sourceBufferLength ? this.sourceBuffer[srcIndexInt + 1] : val0;\n downsampled[i] = val0 + (val1 - val0) * fraction;\n }\n\n // 4. Float32 → Int16 PCM\n const pcmData = this.floatTo16BitPCM(downsampled);\n\n // 5. 写入 PCM 缓冲区(空间不足时直接覆盖最旧,缓冲区足够大基本不会触发)\n if (this.pcmBufferIndex + pcmData.length > this.pcmBuffer.length) {\n // 简单策略:从头覆盖(丢弃最旧数据)\n this.pcmBufferIndex = 0;\n }\n this.pcmBuffer.set(pcmData, this.pcmBufferIndex);\n this.pcmBufferIndex += pcmData.length;\n\n // 6. 发送所有完整的 chunk(关键:复制到新数组再转移)\n while (this.pcmBufferIndex >= this.chunkSize) {\n // 方法1:推荐,使用 slice(隐式复制到新 ArrayBuffer)\n const chunk = this.pcmBuffer.slice(0, this.chunkSize);\n\n // 方法2:等价写法\n // const chunk = new Int16Array(this.pcmBuffer.subarray(0, this.chunkSize));\n\n this.port.postMessage(chunk, [chunk.buffer]); // 转移新缓冲区,安全!\n\n // 移动剩余数据到开头\n this.pcmBuffer.copyWithin(0, this.chunkSize, this.pcmBufferIndex);\n this.pcmBufferIndex -= this.chunkSize;\n }\n\n // 7. 清理已消费的源数据\n const consumedSrc = Math.floor(availableOutputSamples * this.downsampleRatio + 0.5); // 四舍五入更准\n if (consumedSrc > 0) {\n this.sourceBuffer.copyWithin(0, consumedSrc, this.sourceBufferLength);\n this.sourceBufferLength -= consumedSrc;\n }\n\n return true;\n }\n\n floatTo16BitPCM(input) {\n const output = new Int16Array(input.length);\n for (let i = 0; i < input.length; i++) {\n const s = Math.max(-1, Math.min(1, input[i]));\n output[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;\n }\n return output;\n }\n}\n\nregisterProcessor('audio-stream-resampler-processor', AudioStreamResamplerProcessor);\n"],{type:"application/javascript"});this.workletUrl=URL.createObjectURL(e),await this.audioContext.audioWorklet.addModule(this.workletUrl),this.workletNode=new AudioWorkletNode(this.audioContext,"audio-stream-resampler-processor",{processorOptions:this.processorOptions}),this.workletNode.port.onmessage=e=>{const t=e.data;this.onData(t),this.saveFullPcm&&this.fullPcmData.push(t)},this.isInitialized=!0,this.onStateChange("ready","音频环境已就绪")}catch(e){console.error("AudioStreamResampler init error:",e),this.onStateChange("error",`初始化失败: ${e.message}`)}}setMediaStream(e){this.isInitialized?(this.source&&this.source.disconnect(),this.source=this.audioContext.createMediaStreamSource(e),this.source.connect(this.workletNode),this.isProcessing=!0,this.onStateChange("processing","正在处理音频流...")):console.error("请先调用 init()")}stop(e){if(this.isProcessing&&(this.onStateChange("stopping","正在停止..."),this.source&&(this.source.disconnect(),this.source=null),this._cleanup(),this.onStateChange("stopped","已停止"),this.saveFullPcm&&e&&"function"==typeof e)){const t=this.fullPcmData.reduce((e,t)=>e+t.length,0),n=new Int16Array(t);let s=0;for(const e of this.fullPcmData)n.set(e,s),s+=e.length;e(n)}}_cleanup(){this.workletNode&&(this.workletNode.disconnect(),this.workletNode.port.close(),this.workletNode=null),this.audioContext&&"closed"!==this.audioContext.state&&(this.audioContext.close(),this.audioContext=null),this.workletUrl&&(URL.revokeObjectURL(this.workletUrl),this.workletUrl=null),this.isProcessing=!1,this.isInitialized=!1,this.fullPcmData&&(this.fullPcmData.length=0)}}function s(e,t=16e3){const n=e.length,s=new ArrayBuffer(44+2*n),o=new DataView(s),i=(e,t)=>{for(let n=0;n<t.length;n++)o.setUint8(e+n,t.charCodeAt(n))};i(0,"RIFF"),o.setUint32(4,36+2*n,!0),i(8,"WAVE"),i(12,"fmt "),o.setUint32(16,16,!0),o.setUint16(20,1,!0),o.setUint16(22,1,!0),o.setUint32(24,t,!0),o.setUint32(28,2*t,!0),o.setUint16(32,2,!0),o.setUint16(34,16,!0),i(36,"data"),o.setUint32(40,2*n,!0);let r=44;for(let t=0;t<n;t++)o.setInt16(r,e[t],!0),r+=2;return new Blob([s],{type:"audio/wav"})}function o(){const e=document,t=e.documentElement,n=e.body;return{w:window.innerWidth||t.clientWidth||n.clientWidth,h:window.innerHeight||t.clientHeight||n.clientHeight}}function i(){const e=new URLSearchParams(location.search);return Object.fromEntries(e.entries())}function r(e){return i()[e]}function a(e){return Object.prototype.toString.call(e).replace(/^\[object\s(\w+)\]$/,"$1").toLowerCase()}function c(e){return"blob"===a(e)}function h(e){return"object"===a(e)}function l(e){return"promise"===a(e)}function u(e){return"date"===a(e)}function f(e){return"function"==typeof e}function m(e){return"string"===a(e)&&e.length>0}function d(e,t){if(!Number.isInteger(e)||!Number.isInteger(t))throw new TypeError("Arguments must be integers");return e>t&&([e,t]=[t,e]),Math.floor(Math.random()*(t-e+1))+e}function p(e=!0,t=!1,n=!1){const s=[];if(e&&s.push({min:19968,max:40869,surrogate:!1}),t&&s.push({min:13312,max:19903,surrogate:!1}),n&&s.push({min:131072,max:191471,surrogate:!0}),0===s.length)throw new RangeError("At least one range must be enabled");let o=d(0,s.reduce((e,t)=>e+(t.max-t.min+1),0)-1);for(const{min:e,max:t,surrogate:n}of s){const s=t-e+1;if(o<s){const t=e+o;if(!n)return String.fromCharCode(t);const s=t-65536,i=55296+(s>>10),r=56320+(1023&s);return String.fromCharCode(i,r)}o-=s}}function g(e){const t="abcdefghijklmnopqrstuvwxyz",n="ABCDEFGHIJKLMNOPQRSTUVWXYZ",s=d(0,25);switch(e){case"lower":return t[s];case"upper":return n[s];default:return(Math.random()<.5?t:n)[s]}}function w(e,t=.5){e=Math.max(1,Math.floor(e));const n=[];for(let s=0;s<e;s++)n.push(Math.random()<t?p():g());return n.join("")}function y(e,t,n=!1){if("function"!=typeof e)throw new TypeError("fn must be function");let s;t=Math.max(0,Number(t)||0);let o=0;function i(...i){const r=0===o,a=Date.now()-o>=t;if(clearTimeout(s),n&&(r||a))return o=Date.now(),e.apply(this,i);s=setTimeout(()=>{o=Date.now(),e.apply(this,i)},t)}return i.cancel=()=>{clearTimeout(s),o=0},i}function b(e,t,{leading:n=!0,trailing:s=!0}={}){if("function"!=typeof e)throw new TypeError("fn must be function");t=Math.max(0,Number(t)||0);let o=null,i=0;function r(...r){const a=t-(Date.now()-i);n&&(0===i||a<=0)?(i=Date.now(),e.apply(this,r)):s&&!o&&(o=setTimeout(()=>{o=null,i=Date.now(),e.apply(this,r)},a>0?a:t))}return r.cancel=()=>{clearTimeout(o),o=null,i=0},r}function S(e){return JSON.parse(JSON.stringify(e))}function _(e,...t){return t.forEach(t=>{Object.keys(t).forEach(n=>{e.hasOwnProperty(n)&&(e[n]=t[n])})}),e}function v(e){const t=/^\s*(_?)(\S+?)\1\s*$/,n=Function.prototype.toString.call(e).replace(/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm,""),s=n.match(/^([^(]+?)=>/)||n.match(/^[^(]*\(\s*([^)]*)\)/m),o=[];return[].forEach.call(s[1].split(/,/),function(e){e.replace(t,function(e,t,n){o.push(n)})}),o}function x(e,t=!0){return new Promise((n,s)=>{const o=new FileReader;o.onload=e=>{const s=e.target.result;if(t)try{n(JSON.parse(s))}catch(e){console.error(e),n(s)}else n(s)},o.onerror=s,o.readAsText(e)})}function T(e){if(null==e)return null;if(e instanceof Date)return isNaN(e)?null:e;if("number"==typeof e||"string"==typeof e&&/^-?\d+(\.\d+)?$/.test(e.trim())){const t=new Date(+e);return isNaN(t)?null:t}if("string"==typeof e){const t=new Date(e);return isNaN(t)?null:t}if("object"==typeof e){const t=e.valueOf?e.valueOf():Object.prototype.valueOf.call(e);if("number"==typeof t&&!isNaN(t)){const e=new Date(t);return isNaN(e)?null:e}const n=e.toString?e.toString():String(e),s=new Date(n);return isNaN(s)?null:s}return null}function M(e,t){let n=e.getTime(),s=t.getTime();return n>s&&([n,s]=[s,n]),new Date(n+Math.floor(Math.random()*(s-n+1)))}function C(e,t={yearDays:365,monthDays:30}){if("number"!=typeof e||isNaN(e))throw new TypeError("milliseconds must be a valid number");if(e<0)throw new RangeError("milliseconds must be a non-negative number");const{yearDays:n=365,monthDays:s=30}=t;if(!Number.isInteger(n)||n<=0)throw new RangeError("yearDays must be a positive integer");if(!Number.isInteger(s)||s<=0)throw new RangeError("monthDays must be a positive integer");const o=Math.floor(e),i=o%1e3,r=Math.floor(o/1e3),a=r%60,c=Math.floor(r/60)%60,h=Math.floor(r/3600)%24,l=Math.floor(r/3600/24),u=l%n;return{years:Math.floor(l/n),months:Math.floor(u/s),days:u%s,hours:h,minutes:c,seconds:a,milliseconds:i}}function N(e){if("number"!=typeof e||isNaN(e))throw new TypeError("milliseconds must be a valid number");if(e<0)throw new RangeError("milliseconds must be a non-negative number");const t=Math.floor(e),n=t%1e3,s=Math.floor(t/1e3),o=s%60,i=Math.floor(s/60)%60,r=Math.floor(s/3600)%24;return{days:Math.floor(s/3600/24),hours:r,minutes:i,seconds:o,milliseconds:n}}function E(e){if("number"!=typeof e||isNaN(e))throw new TypeError("milliseconds must be a valid number");if(e<0)throw new RangeError("milliseconds must be a non-negative number");const t=Math.floor(e),n=t%1e3,s=Math.floor(t/1e3),o=s%60,i=Math.floor(s/60)%60;return{hours:Math.floor(s/3600),minutes:i,seconds:o,milliseconds:n}}function I(e,t={yearDays:365,monthDays:30}){if("number"!=typeof e||isNaN(e))throw new TypeError("seconds must be a valid number");if(e<0)throw new RangeError("seconds must be a non-negative number");return C(1e3*e,t)}function P(e){if("number"!=typeof e||isNaN(e))throw new TypeError("seconds must be a valid number");if(e<0)throw new RangeError("seconds must be a non-negative number");return N(1e3*e)}function D(e){if("number"!=typeof e||isNaN(e))throw new TypeError("seconds must be a valid number");if(e<0)throw new RangeError("seconds must be a non-negative number");return E(1e3*e)}function k(e,t={earlyMorning:"凌晨",morning:"上午",noon:"中午",afternoon:"下午",evening:"晚上"}){if(!Number.isInteger(e)||e<0||e>23)throw new RangeError("hour 必须是 0-23 的整数");return e>=0&&e<6?t.earlyMorning:e<12?t.morning:e<14?t.noon:e<18?t.afternoon:t.evening}function O(e,t={justNow:"刚刚",today:"今天",yesterday:"昨天",beforeYesterday:"前天",year:"年",month:"月",day:"日",timePeriod:{earlyMorning:"凌晨",morning:"上午",noon:"中午",afternoon:"下午",evening:"晚上"},weekDays:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"]}){const n=new Date,s=new Date(e),o=n.getTime()-s.getTime(),i=Math.floor(o/6e4),r=s.getFullYear(),a=s.getMonth()+1,c=s.getDate(),h=s.getHours(),l=s.getMinutes().toString().padStart(2,"0");if(i<1)return t.justNow;const u=new Date(n.getFullYear(),n.getMonth(),n.getDate()),f=new Date(r,a-1,c),m=Math.floor((u.getTime()-f.getTime())/864e5),d=k(h,t.timePeriod),p=`${h}:${l}`;if(0===m)return`${t.today} ${d}${p}`;if(1===m)return`${t.yesterday} ${d}${p}`;if(2===m)return`${t.beforeYesterday} ${d}${p}`;const g=n.getDay()||7,w=s.getDay()||7,y=new Date(u.getTime()-24*(g-1)*60*60*1e3),b=new Date(f.getTime()-24*(w-1)*60*60*1e3);if(y.getTime()===b.getTime()&&m<7){return`${t.weekDays[s.getDay()]}${d} ${p}`}const S=n.getFullYear(),_=n.getMonth();return r===S&&s.getMonth()===_||r===S?`${a}${t.month}${c}${t.day} ${d}${p}`:`${r}${t.year}${a}${t.month}${c}${t.day} ${d}${p}`}function R(e,t){const n=document.createElement("a");n.style.display="none",n.rel="noopener",n.href=e,n.download=t||Date.now(),document.body.appendChild(n),n.click(),document.body.removeChild(n)}function B(e,t){const n=URL.createObjectURL(e);R(n,t),setTimeout(()=>URL.revokeObjectURL(n),0)}function W(e,t,n="application/octet-stream"){B(new Blob([e],{type:n}),t)}function $(e,t){W(e,t,"application/vnd.ms-excel")}function L(e,t){W(e,t,"application/json")}async function A(e,t){if(!t){try{const n=new URL(e).pathname;t=n.substring(n.lastIndexOf("/")+1).split("?")[0]}catch(e){}t||(t=Date.now().toString())}try{const n=await fetch(e,{method:"GET",mode:"cors",cache:"no-cache"});if(!n.ok)throw new Error(`HTTP error! ${n.status}: ${n.statusText}`);B(await n.blob(),t)}catch(n){R(e,t)}}class U{constructor(){this.evtPool=new Map}on(e,t){let n=Date.now()+"_"+parseInt(1e8*Math.random());const s={flag:n,fn:t};return this.evtPool.has(e)?this.evtPool.get(e).push(s):this.evtPool.set(e,[s]),n}once(e,t){const n=this;let s;return s=function(o){n.off(e,s),t.call(this,o)},this.on(e,s)}off(e,t){if(!this.evtPool.has(e))return;const n=this.evtPool.get(e).filter(e=>e.fn!==t&&e.flag!==t);0===n.length?this.evtPool.delete(e):this.evtPool.set(e,n)}emit(e,t,n){if(!this.evtPool.has(e))return;this.evtPool.get(e).forEach(s=>{try{s.fn.call(n,t)}catch(t){console.error(`Error in event listener for "${e}":`,t)}})}}const z=(()=>{const e=new WeakSet;return{install(t,n={}){if(e.has(t))return;e.add(t);const s=`___my-event-cross-page-${n.namespace||"default"}___`,o=n.throttle||16;let i=0;const r=t.emit;function a(e){if(!e.key||!e.key.startsWith(s))return;let n;try{n=JSON.parse(e.newValue||"{}")}catch{return}!n.ts||n.ts<=i||r.call(t,e.key.slice(s.length),n.data)}t.emit=function(e,n,a){r.call(t,e,n,a);const c=Date.now();if(c-i<o)return;i=c;const h=s+e;try{localStorage.setItem(h,JSON.stringify({name:e,data:n,ts:c})),localStorage.removeItem(h)}catch(e){}},addEventListener("storage",a),t._uninstallCrossPage=()=>{removeEventListener("storage",a),t.emit=r,e.delete(t)}},uninstall(e){"function"==typeof e._uninstallCrossPage&&e._uninstallCrossPage()}}})();function H(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let t=16*Math.random()|0;return("x"==e?t:3&t|8).toString(16).toUpperCase()})}class F{#e=Date.now();#t=0;#n="";#s=5;constructor(e={}){e&&("string"==typeof e.flag&&(this.#n=e.flag),Number.isSafeInteger(e.len)&&len>=0&&(this.#s=e.len))}nextId(){let e=Date.now();return e===this.#e?(this.#t++,this.#t>=10**this.#s&&console.log("长度不够用了!!!")):(this.#t=0,this.#e=e),e.toString()+this.#n+this.#t.toString().padStart(this.#s,"0")}}class j{constructor(e,t=1e3){if("function"!=typeof e)throw new TypeError("IntervalTimer: 必须传入一个函数");this._fn=e,this._ms=t,this._timerId=null}start(){this.stop();const e=()=>{this._fn(),this._timerId=setTimeout(e,this._ms)};e()}stop(){null!==this._timerId&&(clearTimeout(this._timerId),this._timerId=null)}isRunning(){return null!==this._timerId}}function J(e,t="id",n="children"){const s={};return function e(o){Array.isArray(o)&&o.length>0&&o.forEach(o=>{s[o[t]]={...o},s[o[t]][n]=null,e(o[n])})}(e),s}function Q(e,t=0,{idKey:n="id",parentKey:s="parentId",childrenKey:o="children"}={}){const i=new Map,r=[];for(const t of e){const e={...t,[o]:[]};i.set(t[n],e)}for(const a of e){const e=i.get(a[n]),c=a[s];if(c===t)r.push(e);else{const t=i.get(c);t&&t[o].push(e)}}return r}const V=function e(t,n,s="name",o="id",i="children"){if(Array.isArray(n)&&n.length>0)for(let r=0;r<n.length;r++){const a=n[r];if(a[o]?.toString()===t?.toString())return a[s];if(Array.isArray(a[i])&&a[i].length>0){const n=e(t,a[i],s,o,i);if(n)return n}}};function Y(e,t,n="id",s="children"){const o=new Set(t),i=new Set,r=new Set;return e.forEach(function e(t){const a=t[n],c=t[s]||[];if(!c.length)return o.has(a)?(i.add(a),2):0;let h=!0,l=!1;return c.forEach(t=>{const n=e(t);2!==n&&(h=!1),n>=1&&(l=!0)}),o.has(a)?h?(i.add(a),2):(r.add(a),1):h?(i.add(a),2):l?(r.add(a),1):0}),{checked:[...i],halfChecked:[...r]}}class G{constructor(e,t={}){this.url=e;this.options={heartbeatInterval:3e4,heartbeatTimeout:1e4,reconnectBaseInterval:1e3,maxReconnectInterval:3e4,maxReconnectAttempts:1/0,autoConnect:!0,serializeData:!1,deserializeData:!1,getPingMessage:()=>JSON.stringify({type:"ping"}),isPongMessage:e=>{try{return"pong"===JSON.parse(e.data).type}catch(e){return!1}},...t},this.ws=null,this.readyState=WebSocket.CLOSED,this.reconnectAttempts=0,this.forcedClose=!1,this.isReconnecting=!1,this.heartbeatTimer=null,this.heartbeatTimeoutTimer=null,this.reconnectTimer=null,this.messageQueue=[],this.listeners=new Map,this._onOpen=this._onOpen.bind(this),this._onMessage=this._onMessage.bind(this),this._onClose=this._onClose.bind(this),this._onError=this._onError.bind(this),this._handleVisibilityChange=this._handleVisibilityChange.bind(this),this._handleOnline=this._handleOnline.bind(this),this._handleOffline=this._handleOffline.bind(this),this._setupEventListeners(),this.options.autoConnect&&this.connect()}connect(){if(!this.ws||this.ws.readyState!==WebSocket.CONNECTING&&this.ws.readyState!==WebSocket.OPEN){this.forcedClose=!1,this._updateReadyState(WebSocket.CONNECTING),console.log(`[WS] 正在连接到 ${this.url}...`),this._emit("connecting");try{this.ws=new WebSocket(this.url,this.options.protocols),this.ws.onopen=this._onOpen,this.ws.onmessage=this._onMessage,this.ws.onclose=this._onClose,this.ws.onerror=this._onError}catch(e){console.error("[WS] 连接失败:",e),this._onError(e)}}}send(e){if(this.readyState===WebSocket.OPEN){let t;t=this.options.serializeData?JSON.stringify(e):e,this.ws.send(t),console.log("[WS] 消息已发送:",t)}else console.warn("[WS] 连接未打开,消息已加入队列:",e),this.messageQueue.push(e)}close(e=1e3,t="Normal closure"){this.forcedClose=!0,this._stopHeartbeat(),this._clearReconnectTimer(),this.ws&&this.ws.readyState===WebSocket.OPEN?this.ws.close(e,t):(this._updateReadyState(WebSocket.CLOSED),this._emit("close",{code:e,reason:t,wasClean:!0}))}destroy(){console.log("[WS] 正在销毁实例..."),this.close(1e3,"Instance destroyed"),this._removeEventListeners(),this.messageQueue=[],this.listeners.clear(),this.ws=null}on(e,t){this.listeners.has(e)||this.listeners.set(e,[]),this.listeners.get(e).push(t)}off(e,t){if(this.listeners.has(e)){const n=this.listeners.get(e),s=n.indexOf(t);s>-1&&n.splice(s,1)}}_onOpen(e){console.log("[WS] 连接已建立"),this._updateReadyState(WebSocket.OPEN),this.reconnectAttempts=0,this.isReconnecting=!1,this.options.getPingMessage&&this._startHeartbeat(),this._flushMessageQueue(),this._emit("open",e)}_onMessage(e){if(this.options.isPongMessage&&this.options.isPongMessage(e))this._handlePong();else if(this.options.deserializeData)try{const t=JSON.parse(e.data);this._emit("message",t,e)}catch(t){this._emit("message",e.data,e)}else this._emit("message",e.data,e)}_onClose(e){console.log("[WS] 连接已关闭",e),this._updateReadyState(WebSocket.CLOSED),this._stopHeartbeat(),this._emit("close",e),this.forcedClose||this._scheduleReconnect()}_onError(e){console.error("[WS] 连接发生错误:",e),this._emit("error",e)}_startHeartbeat(){this.options.getPingMessage&&(this._stopHeartbeat(),this.heartbeatTimer=setInterval(()=>{if(this.ws&&this.ws.readyState===WebSocket.OPEN)try{const e=this.options.getPingMessage();this.ws.send(e),console.log("[WS] 发送 Ping:",e),this._setHeartbeatTimeout()}catch(e){console.error("[WS] 发送 Ping 消息失败:",e)}},this.options.heartbeatInterval))}_stopHeartbeat(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null),this._clearHeartbeatTimeout()}_setHeartbeatTimeout(){this._clearHeartbeatTimeout(),this.heartbeatTimeoutTimer=setTimeout(()=>{console.error("[WS] 心跳超时,主动断开连接"),this.ws.close(1006,"Heartbeat timeout")},this.options.heartbeatTimeout)}_clearHeartbeatTimeout(){this.heartbeatTimeoutTimer&&(clearTimeout(this.heartbeatTimeoutTimer),this.heartbeatTimeoutTimer=null)}_handlePong(){console.log("[WS] 收到 Pong"),this._clearHeartbeatTimeout()}_scheduleReconnect(){if(this.forcedClose||this.isReconnecting||this.reconnectAttempts>=this.options.maxReconnectAttempts)return void(this.reconnectAttempts>=this.options.maxReconnectAttempts&&(console.error("[WS] 已达到最大重连次数,停止重连"),this._emit("reconnect-failed")));this.isReconnecting=!0;const e=Math.min(this.options.reconnectBaseInterval*Math.pow(2,this.reconnectAttempts),this.options.maxReconnectInterval);console.log(`[WS] ${e/1e3}秒后将尝试第 ${this.reconnectAttempts+1} 次重连...`),this._emit("reconnect-attempt",{attempt:this.reconnectAttempts+1,interval:e}),this.reconnectTimer=setTimeout(()=>{this.reconnectAttempts++,this.connect()},e)}_clearReconnectTimer(){this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null)}_flushMessageQueue(){if(0===this.messageQueue.length)return;console.log(`[WS] 发送队列中的 ${this.messageQueue.length} 条消息`);const e=[...this.messageQueue];this.messageQueue=[],e.forEach(e=>this.send(e))}_emit(e,...t){this.listeners.has(e)&&this.listeners.get(e).forEach(e=>e(...t))}_updateReadyState(e){this.readyState=e,this._emit("ready-state-change",e)}_setupEventListeners(){document.addEventListener("visibilitychange",this._handleVisibilityChange),window.addEventListener("online",this._handleOnline),window.addEventListener("offline",this._handleOffline)}_removeEventListeners(){document.removeEventListener("visibilitychange",this._handleVisibilityChange),window.removeEventListener("online",this._handleOnline),window.removeEventListener("offline",this._handleOffline)}_handleVisibilityChange(){this.options.getPingMessage&&(document.hidden?(console.log("[WS] 页面隐藏,停止心跳"),this._stopHeartbeat()):(console.log("[WS] 页面可见,检查连接状态"),this.ws&&this.ws.readyState===WebSocket.OPEN?this._startHeartbeat():this.forcedClose||this.isReconnecting||this.connect()))}_handleOnline(){console.log("[WS] 网络已恢复,尝试重连"),this.forcedClose||this.readyState===WebSocket.OPEN||(this._clearReconnectTimer(),this.connect())}_handleOffline(){console.log("[WS] 网络已断开"),this._clearReconnectTimer(),this.ws&&this.ws.onclose({code:1006,reason:"Network offline"})}}export{n as AudioStreamResampler,j as IntervalTimer,U as MyEvent,z as MyEvent_CrossPagePlugin,F as MyId,G as WebSocketManager,_ as assignExisting,y as debounce,S as deepCloneByJSON,B as downloadByBlob,W as downloadByData,R as downloadByUrl,$ as downloadExcel,L as downloadJSON,Y as extractFullyCheckedKeys,A as fetchOrDownloadByUrl,V as findObjAttrValueById,Q as flatCompleteTree2NestedTree,O as formatTimeForLocale,i as getAllSearchParams,a as getDataType,v as getFunctionArgNames,H as getGUID,r as getSearchParam,k as getTimePeriodName,o as getViewportSize,c as isBlob,u as isDate,f as isFunction,m as isNonEmptyString,h as isPlainObject,l as isPromise,C as millisecond2Duration,N as millisecond2DurationMaxDay,E as millisecond2DurationMaxHour,t as moveItem,J as nestedTree2IdMap,s as pcmToWavBlob,M as randomDateInRange,g as randomEnLetter,p as randomHan,w as randomHanOrEn,d as randomIntInRange,x as readBlobAsText,I as second2Duration,P as second2DurationMaxDay,D as second2DurationMaxHour,e as shuffle,b as throttle,T as toDate};
|
|
1
|
+
function e(e){for(let t=e.length-1;t>0;t--){const n=Math.floor(Math.random()*(t+1));[e[t],e[n]]=[e[n],e[t]]}return e}function t(e,t,n){e.splice(n,0,e.splice(t,1)[0])}class n{constructor(e){this.onData=e.onData||(()=>{}),this.onStateChange=e.onStateChange||(()=>{}),this.processorOptions=e.processorOptions||{},this.saveFullPcm=e.saveFullPcm??!1,this.audioContext=null,this.workletNode=null,this.source=null,this.workletUrl=null,this.fullPcmData=this.saveFullPcm?[]:null,this.isInitialized=!1,this.isProcessing=!1}async init(){this.onStateChange("initializing","正在初始化音频环境...");try{this.audioContext=new(window.AudioContext||window.webkitAudioContext);const e=new Blob(["\nclass AudioStreamResamplerProcessor extends AudioWorkletProcessor {\n constructor(options) {\n super();\n const config = options.processorOptions || {};\n this.targetSampleRate = config.targetSampleRate || 16000;\n this.sourceSampleRate = sampleRate;\n this.downsampleRatio = this.sourceSampleRate / this.targetSampleRate;\n\n // 16000Hz 用 60ms chunk (960 samples),其他用 1024\n this.chunkSize = this.targetSampleRate === 16000 ? 960 : 1024;\n\n const sourceBufferSize = config.sourceBufferSize || 16384; // ~340ms @48kHz\n const pcmBufferSize = config.pcmBufferSize || (this.chunkSize * 10); // 更大缓冲,减少溢出概率\n\n this.sourceBuffer = new Float32Array(sourceBufferSize);\n this.sourceBufferLength = 0;\n\n this.pcmBuffer = new Int16Array(pcmBufferSize);\n this.pcmBufferIndex = 0;\n }\n\n process(inputs, outputs, parameters) {\n const input = inputs[0];\n if (!input || input.length === 0 || input[0].length === 0) return true;\n const inputChannel = input[0];\n\n // 1. 写入源缓冲区(溢出时覆盖最旧数据)\n const newLength = this.sourceBufferLength + inputChannel.length;\n if (newLength > this.sourceBuffer.length) {\n const overflow = newLength - this.sourceBuffer.length;\n this.sourceBuffer.copyWithin(0, overflow);\n this.sourceBufferLength = this.sourceBuffer.length - inputChannel.length;\n }\n this.sourceBuffer.set(inputChannel, this.sourceBufferLength);\n this.sourceBufferLength += inputChannel.length;\n\n // 2. 计算可降采样样本数\n const availableOutputSamples = Math.floor(this.sourceBufferLength / this.downsampleRatio);\n if (availableOutputSamples === 0) return true;\n\n // 3. 线性插值降采样\n const downsampled = new Float32Array(availableOutputSamples);\n for (let i = 0; i < availableOutputSamples; i++) {\n const srcIndex = i * this.downsampleRatio;\n const srcIndexInt = Math.floor(srcIndex);\n const fraction = srcIndex - srcIndexInt;\n const val0 = this.sourceBuffer[srcIndexInt];\n const val1 = srcIndexInt + 1 < this.sourceBufferLength ? this.sourceBuffer[srcIndexInt + 1] : val0;\n downsampled[i] = val0 + (val1 - val0) * fraction;\n }\n\n // 4. Float32 → Int16 PCM\n const pcmData = this.floatTo16BitPCM(downsampled);\n\n // 5. 写入 PCM 缓冲区(空间不足时直接覆盖最旧,缓冲区足够大基本不会触发)\n if (this.pcmBufferIndex + pcmData.length > this.pcmBuffer.length) {\n // 简单策略:从头覆盖(丢弃最旧数据)\n this.pcmBufferIndex = 0;\n }\n this.pcmBuffer.set(pcmData, this.pcmBufferIndex);\n this.pcmBufferIndex += pcmData.length;\n\n // 6. 发送所有完整的 chunk(关键:复制到新数组再转移)\n while (this.pcmBufferIndex >= this.chunkSize) {\n // 方法1:推荐,使用 slice(隐式复制到新 ArrayBuffer)\n const chunk = this.pcmBuffer.slice(0, this.chunkSize);\n\n // 方法2:等价写法\n // const chunk = new Int16Array(this.pcmBuffer.subarray(0, this.chunkSize));\n\n this.port.postMessage(chunk, [chunk.buffer]); // 转移新缓冲区,安全!\n\n // 移动剩余数据到开头\n this.pcmBuffer.copyWithin(0, this.chunkSize, this.pcmBufferIndex);\n this.pcmBufferIndex -= this.chunkSize;\n }\n\n // 7. 清理已消费的源数据\n const consumedSrc = Math.floor(availableOutputSamples * this.downsampleRatio + 0.5); // 四舍五入更准\n if (consumedSrc > 0) {\n this.sourceBuffer.copyWithin(0, consumedSrc, this.sourceBufferLength);\n this.sourceBufferLength -= consumedSrc;\n }\n\n return true;\n }\n\n floatTo16BitPCM(input) {\n const output = new Int16Array(input.length);\n for (let i = 0; i < input.length; i++) {\n const s = Math.max(-1, Math.min(1, input[i]));\n output[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;\n }\n return output;\n }\n}\n\nregisterProcessor('audio-stream-resampler-processor', AudioStreamResamplerProcessor);\n"],{type:"application/javascript"});this.workletUrl=URL.createObjectURL(e),await this.audioContext.audioWorklet.addModule(this.workletUrl),this.workletNode=new AudioWorkletNode(this.audioContext,"audio-stream-resampler-processor",{processorOptions:this.processorOptions}),this.workletNode.port.onmessage=e=>{const t=e.data;this.onData(t),this.saveFullPcm&&this.fullPcmData.push(t)},this.isInitialized=!0,this.onStateChange("ready","音频环境已就绪")}catch(e){console.error("AudioStreamResampler init error:",e),this.onStateChange("error",`初始化失败: ${e.message}`)}}setMediaStream(e){this.isInitialized?(this.source&&this.source.disconnect(),this.source=this.audioContext.createMediaStreamSource(e),this.source.connect(this.workletNode),this.isProcessing=!0,this.onStateChange("processing","正在处理音频流...")):console.error("请先调用 init()")}stop(e){if(this.isProcessing&&(this.onStateChange("stopping","正在停止..."),this.source&&(this.source.disconnect(),this.source=null),this._cleanup(),this.onStateChange("stopped","已停止"),this.saveFullPcm&&e&&"function"==typeof e)){const t=this.fullPcmData.reduce((e,t)=>e+t.length,0),n=new Int16Array(t);let s=0;for(const e of this.fullPcmData)n.set(e,s),s+=e.length;e(n)}}_cleanup(){this.workletNode&&(this.workletNode.disconnect(),this.workletNode.port.close(),this.workletNode=null),this.audioContext&&"closed"!==this.audioContext.state&&(this.audioContext.close(),this.audioContext=null),this.workletUrl&&(URL.revokeObjectURL(this.workletUrl),this.workletUrl=null),this.isProcessing=!1,this.isInitialized=!1,this.fullPcmData&&(this.fullPcmData.length=0)}}function s(e,t=16e3){const n=e.length,s=new ArrayBuffer(44+2*n),o=new DataView(s),i=(e,t)=>{for(let n=0;n<t.length;n++)o.setUint8(e+n,t.charCodeAt(n))};i(0,"RIFF"),o.setUint32(4,36+2*n,!0),i(8,"WAVE"),i(12,"fmt "),o.setUint32(16,16,!0),o.setUint16(20,1,!0),o.setUint16(22,1,!0),o.setUint32(24,t,!0),o.setUint32(28,2*t,!0),o.setUint16(32,2,!0),o.setUint16(34,16,!0),i(36,"data"),o.setUint32(40,2*n,!0);let r=44;for(let t=0;t<n;t++)o.setInt16(r,e[t],!0),r+=2;return new Blob([s],{type:"audio/wav"})}function o(){const e=document,t=e.documentElement,n=e.body;return{w:window.innerWidth||t.clientWidth||n.clientWidth,h:window.innerHeight||t.clientHeight||n.clientHeight}}function i(){const e=new URLSearchParams(location.search);return Object.fromEntries(e.entries())}function r(e){return i()[e]}function a(e){return Object.prototype.toString.call(e).replace(/^\[object\s(\w+)\]$/,"$1").toLowerCase()}function c(e){return"blob"===a(e)}function h(e){return"object"===a(e)}function l(e){return"promise"===a(e)}function u(e){return"date"===a(e)}function f(e){return"function"==typeof e}function m(e){return"string"===a(e)&&e.length>0}function d(e,t){if(!Number.isInteger(e)||!Number.isInteger(t))throw new TypeError("Arguments must be integers");return e>t&&([e,t]=[t,e]),Math.floor(Math.random()*(t-e+1))+e}function p(e=!0,t=!1,n=!1){const s=[];if(e&&s.push({min:19968,max:40869,surrogate:!1}),t&&s.push({min:13312,max:19903,surrogate:!1}),n&&s.push({min:131072,max:191471,surrogate:!0}),0===s.length)throw new RangeError("At least one range must be enabled");let o=d(0,s.reduce((e,t)=>e+(t.max-t.min+1),0)-1);for(const{min:e,max:t,surrogate:n}of s){const s=t-e+1;if(o<s){const t=e+o;if(!n)return String.fromCharCode(t);const s=t-65536,i=55296+(s>>10),r=56320+(1023&s);return String.fromCharCode(i,r)}o-=s}}function g(e){const t="abcdefghijklmnopqrstuvwxyz",n="ABCDEFGHIJKLMNOPQRSTUVWXYZ",s=d(0,25);switch(e){case"lower":return t[s];case"upper":return n[s];default:return(Math.random()<.5?t:n)[s]}}function w(e,t=.5){e=Math.max(1,Math.floor(e));const n=[];for(let s=0;s<e;s++)n.push(Math.random()<t?p():g());return n.join("")}function y(e,t,n=!1){if("function"!=typeof e)throw new TypeError("fn must be function");let s;t=Math.max(0,Number(t)||0);let o=0;function i(...i){const r=0===o,a=Date.now()-o>=t;if(clearTimeout(s),n&&(r||a))return o=Date.now(),e.apply(this,i);s=setTimeout(()=>{o=Date.now(),e.apply(this,i)},t)}return i.cancel=()=>{clearTimeout(s),o=0},i}function b(e,t,{leading:n=!0,trailing:s=!0}={}){if("function"!=typeof e)throw new TypeError("fn must be function");t=Math.max(0,Number(t)||0);let o=null,i=0;function r(...r){const a=t-(Date.now()-i);n&&(0===i||a<=0)?(i=Date.now(),e.apply(this,r)):s&&!o&&(o=setTimeout(()=>{o=null,i=Date.now(),e.apply(this,r)},a>0?a:t))}return r.cancel=()=>{clearTimeout(o),o=null,i=0},r}function S(e){return JSON.parse(JSON.stringify(e))}function _(e,...t){return t.forEach(t=>{Object.keys(t).forEach(n=>{e.hasOwnProperty(n)&&(e[n]=t[n])})}),e}function v(e){const t=/^\s*(_?)(\S+?)\1\s*$/,n=Function.prototype.toString.call(e).replace(/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm,""),s=n.match(/^([^(]+?)=>/)||n.match(/^[^(]*\(\s*([^)]*)\)/m),o=[];return[].forEach.call(s[1].split(/,/),function(e){e.replace(t,function(e,t,n){o.push(n)})}),o}function x(e,t=!0){return new Promise((n,s)=>{const o=new FileReader;o.onload=e=>{const s=e.target.result;if(t)try{n(JSON.parse(s))}catch(e){console.error(e),n(s)}else n(s)},o.onerror=s,o.readAsText(e)})}function M(e){if(null==e)return null;if(e instanceof Date)return isNaN(e)?null:e;if("number"==typeof e||"string"==typeof e&&/^-?\d+(\.\d+)?$/.test(e.trim())){const t=new Date(+e);return isNaN(t)?null:t}if("string"==typeof e){const t=new Date(e);return isNaN(t)?null:t}if("object"==typeof e){const t=e.valueOf?e.valueOf():Object.prototype.valueOf.call(e);if("number"==typeof t&&!isNaN(t)){const e=new Date(t);return isNaN(e)?null:e}const n=e.toString?e.toString():String(e),s=new Date(n);return isNaN(s)?null:s}return null}function T(e,t){let n=e.getTime(),s=t.getTime();return n>s&&([n,s]=[s,n]),new Date(n+Math.floor(Math.random()*(s-n+1)))}function N(e,t={yearDays:365,monthDays:30}){if("number"!=typeof e||isNaN(e))throw new TypeError("milliseconds must be a valid number");if(e<0)throw new RangeError("milliseconds must be a non-negative number");const{yearDays:n=365,monthDays:s=30}=t;if(!Number.isInteger(n)||n<=0)throw new RangeError("yearDays must be a positive integer");if(!Number.isInteger(s)||s<=0)throw new RangeError("monthDays must be a positive integer");const o=Math.floor(e),i=o%1e3,r=Math.floor(o/1e3),a=r%60,c=Math.floor(r/60)%60,h=Math.floor(r/3600)%24,l=Math.floor(r/3600/24),u=l%n;return{years:Math.floor(l/n),months:Math.floor(u/s),days:u%s,hours:h,minutes:c,seconds:a,milliseconds:i}}function C(e){if("number"!=typeof e||isNaN(e))throw new TypeError("milliseconds must be a valid number");if(e<0)throw new RangeError("milliseconds must be a non-negative number");const t=Math.floor(e),n=t%1e3,s=Math.floor(t/1e3),o=s%60,i=Math.floor(s/60)%60,r=Math.floor(s/3600)%24;return{days:Math.floor(s/3600/24),hours:r,minutes:i,seconds:o,milliseconds:n}}function E(e){if("number"!=typeof e||isNaN(e))throw new TypeError("milliseconds must be a valid number");if(e<0)throw new RangeError("milliseconds must be a non-negative number");const t=Math.floor(e),n=t%1e3,s=Math.floor(t/1e3),o=s%60,i=Math.floor(s/60)%60;return{hours:Math.floor(s/3600),minutes:i,seconds:o,milliseconds:n}}function I(e,t={yearDays:365,monthDays:30}){if("number"!=typeof e||isNaN(e))throw new TypeError("seconds must be a valid number");if(e<0)throw new RangeError("seconds must be a non-negative number");return N(1e3*e,t)}function P(e){if("number"!=typeof e||isNaN(e))throw new TypeError("seconds must be a valid number");if(e<0)throw new RangeError("seconds must be a non-negative number");return C(1e3*e)}function k(e){if("number"!=typeof e||isNaN(e))throw new TypeError("seconds must be a valid number");if(e<0)throw new RangeError("seconds must be a non-negative number");return E(1e3*e)}function D(e,t={earlyMorning:"凌晨",morning:"上午",noon:"中午",afternoon:"下午",evening:"晚上"}){if(!Number.isInteger(e)||e<0||e>23)throw new RangeError("hour 必须是 0-23 的整数");return e>=0&&e<6?t.earlyMorning:e<12?t.morning:e<14?t.noon:e<18?t.afternoon:t.evening}function O(e,t={justNow:"刚刚",today:"今天",yesterday:"昨天",beforeYesterday:"前天",year:"年",month:"月",day:"日",timePeriod:{earlyMorning:"凌晨",morning:"上午",noon:"中午",afternoon:"下午",evening:"晚上"},weekDays:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"]}){const n=new Date,s=new Date(e),o=n.getTime()-s.getTime(),i=Math.floor(o/6e4),r=s.getFullYear(),a=s.getMonth()+1,c=s.getDate(),h=s.getHours(),l=s.getMinutes().toString().padStart(2,"0");if(i<1)return t.justNow;const u=new Date(n.getFullYear(),n.getMonth(),n.getDate()),f=new Date(r,a-1,c),m=Math.floor((u.getTime()-f.getTime())/864e5),d=D(h,t.timePeriod),p=`${h}:${l}`;if(0===m)return`${t.today} ${d}${p}`;if(1===m)return`${t.yesterday} ${d}${p}`;if(2===m)return`${t.beforeYesterday} ${d}${p}`;const g=n.getDay()||7,w=s.getDay()||7,y=new Date(u.getTime()-24*(g-1)*60*60*1e3),b=new Date(f.getTime()-24*(w-1)*60*60*1e3);if(y.getTime()===b.getTime()&&m<7){return`${t.weekDays[s.getDay()]}${d} ${p}`}const S=n.getFullYear(),_=n.getMonth();return r===S&&s.getMonth()===_||r===S?`${a}${t.month}${c}${t.day} ${d}${p}`:`${r}${t.year}${a}${t.month}${c}${t.day} ${d}${p}`}function R(e,t){const n=document.createElement("a");n.style.display="none",n.rel="noopener",n.href=e,n.download=t||Date.now(),document.body.appendChild(n),n.click(),document.body.removeChild(n)}function B(e,t){const n=URL.createObjectURL(e);R(n,t),setTimeout(()=>URL.revokeObjectURL(n),0)}function A(e,t,n="application/octet-stream"){B(new Blob([e],{type:n}),t)}function W(e,t){A(e,t,"application/vnd.ms-excel")}function $(e,t){A(e,t,"application/json")}async function L(e,t){if(!t){try{const n=new URL(e).pathname;t=n.substring(n.lastIndexOf("/")+1).split("?")[0]}catch(e){}t||(t=Date.now().toString())}try{const n=await fetch(e,{method:"GET",mode:"cors",cache:"no-cache"});if(!n.ok)throw new Error(`HTTP error! ${n.status}: ${n.statusText}`);B(await n.blob(),t)}catch(n){R(e,t)}}class U{constructor(){this.evtPool=new Map}on(e,t){let n=Date.now()+"_"+parseInt(1e8*Math.random());const s={flag:n,fn:t};return this.evtPool.has(e)?this.evtPool.get(e).push(s):this.evtPool.set(e,[s]),n}once(e,t){const n=this;let s;return s=function(o){n.off(e,s),t.call(this,o)},this.on(e,s)}off(e,t){if(!this.evtPool.has(e))return;const n=this.evtPool.get(e).filter(e=>e.fn!==t&&e.flag!==t);0===n.length?this.evtPool.delete(e):this.evtPool.set(e,n)}emit(e,t,n){if(!this.evtPool.has(e))return;this.evtPool.get(e).forEach(s=>{try{s.fn.call(n,t)}catch(t){console.error(`Error in event listener for "${e}":`,t)}})}}const z=(()=>{const e=new WeakSet;return{install(t,n={}){if(e.has(t))return;e.add(t);const s=`___my-event-cross-page-${n.namespace||"default"}___`,o=n.throttle||16;let i=0;const r=t.emit;function a(e){if(!e.key||!e.key.startsWith(s))return;let n;try{n=JSON.parse(e.newValue||"{}")}catch{return}!n.ts||n.ts<=i||r.call(t,e.key.slice(s.length),n.data)}t.emit=function(e,n,a){r.call(t,e,n,a);const c=Date.now();if(c-i<o)return;i=c;const h=s+e;try{localStorage.setItem(h,JSON.stringify({name:e,data:n,ts:c})),localStorage.removeItem(h)}catch(e){}},addEventListener("storage",a),t._uninstallCrossPage=()=>{removeEventListener("storage",a),t.emit=r,e.delete(t)}},uninstall(e){"function"==typeof e._uninstallCrossPage&&e._uninstallCrossPage()}}})();function H(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let t=16*Math.random()|0;return("x"==e?t:3&t|8).toString(16).toUpperCase()})}class j{#e=Date.now();#t=0;#n="";#s=5;constructor(e={}){e&&("string"==typeof e.flag&&(this.#n=e.flag),Number.isSafeInteger(e.len)&&len>=0&&(this.#s=e.len))}nextId(){let e=Date.now();return e===this.#e?(this.#t++,this.#t>=10**this.#s&&console.log("长度不够用了!!!")):(this.#t=0,this.#e=e),e.toString()+this.#n+this.#t.toString().padStart(this.#s,"0")}}function F(e,t={}){const{idKey:n="id",codeKey:s="code",labelKey:o="text",childrenKey:i="children",extendKey:r="extend",menuIdKey:a="key",menuLabelKey:c="label",menuChildrenKey:h="children",menuExtendKey:l="extend",routeNameKey:u="name",routePathKey:f="path",routeMetaKey:m="meta",handleNodeItem:d,handleMenuItem:p,handleRouteItem:g}=t,w=[],y=[],b=new Map,S=new Map,_=[];for(Array.isArray(e)&&e.forEach(e=>_.push({node:e,parentPath:[]}));_.length;){const{node:e,parentPath:t}=_.pop(),s=e[n],o=[...t,{[n]:s}];S.set(s,o);const a={...e,[r]:{},[i]:null};b.set(s,a);const c=e[i];if(Array.isArray(c))for(let e=c.length-1;e>=0;e--)_.push({node:c[e],parentPath:o})}const v=[];if(Array.isArray(e))for(let t=e.length-1;t>=0;t--)v.push({node:e[t],parentUi:void 0});for(;v.length;){const{node:e,parentUi:t}=v.pop(),_=e[n],x=b.get(_),M={[a]:_,[c]:e[o],[l]:{}},T=e[i];if(Array.isArray(T)&&T.length>0)for(let e=T.length-1;e>=0;e--)v.push({node:T[e],parentUi:M});else{const e=[],t=[];S.get(_).forEach(o=>{const i=o[n],r=b.get(i);t.push(i),e.push(r[s])});const o=e.join("."),i="/"+e.join("/"),a={[u]:o,[f]:i,[m]:{[n]:_}};"function"==typeof g&&g(a,x),y.push(a),M[l].routeName=o,M[l].keyPaths=t,x[r].routeName=o,x[r].keyPaths=t}t?(t[h]||(t[h]=[])).push(M):("function"==typeof p&&p(M,x),w.push(M)),"function"==typeof d&&d(x)}return{uiMenuItems:w,routeItems:y,nodeMap:b}}class K{constructor(e,t=1e3){if("function"!=typeof e)throw new TypeError("IntervalTimer: 必须传入一个函数");this._fn=e,this._ms=t,this._timerId=null}start(){this.stop();const e=()=>{this._fn(),this._timerId=setTimeout(e,this._ms)};e()}stop(){null!==this._timerId&&(clearTimeout(this._timerId),this._timerId=null)}isRunning(){return null!==this._timerId}}function J(e,t="id",n="children"){const s={};return function e(o){Array.isArray(o)&&o.length>0&&o.forEach(o=>{s[o[t]]={...o},s[o[t]][n]=null,e(o[n])})}(e),s}function Q(e,t=0,{idKey:n="id",parentKey:s="parentId",childrenKey:o="children"}={}){const i=new Map,r=[];for(const t of e){const e={...t,[o]:[]};i.set(t[n],e)}for(const a of e){const e=i.get(a[n]),c=a[s];if(c===t)r.push(e);else{const t=i.get(c);t&&t[o].push(e)}}return r}function V(e,t,n="id",s="children"){if(Array.isArray(t)&&t.length>0)for(let o=0;o<t.length;o++){const i=t[o];if(i[n]?.toString()===e?.toString())return i;if(Array.isArray(i[s])&&i[s].length>0){const t=V(e,i[s],n,s);if(t)return t}}}function Y(e,t,n="name",s="id",o="children"){return V(e,t,s,o)?.[n]}function G(e,t,n="id",s="children"){const o=new Set(t),i=new Set,r=new Set;return e.forEach(function e(t){const a=t[n],c=t[s]||[];if(!c.length)return o.has(a)?(i.add(a),2):0;let h=!0,l=!1;return c.forEach(t=>{const n=e(t);2!==n&&(h=!1),n>=1&&(l=!0)}),o.has(a)?h?(i.add(a),2):(r.add(a),1):h?(i.add(a),2):l?(r.add(a),1):0}),{checked:[...i],halfChecked:[...r]}}class q{constructor(e,t={}){this.url=e;this.options={heartbeatInterval:3e4,heartbeatTimeout:1e4,reconnectBaseInterval:1e3,maxReconnectInterval:3e4,maxReconnectAttempts:1/0,autoConnect:!0,serializeData:!1,deserializeData:!1,getPingMessage:()=>JSON.stringify({type:"ping"}),isPongMessage:e=>{try{return"pong"===JSON.parse(e.data).type}catch(e){return!1}},...t},this.ws=null,this.readyState=WebSocket.CLOSED,this.reconnectAttempts=0,this.forcedClose=!1,this.isReconnecting=!1,this.heartbeatTimer=null,this.heartbeatTimeoutTimer=null,this.reconnectTimer=null,this.messageQueue=[],this.listeners=new Map,this._onOpen=this._onOpen.bind(this),this._onMessage=this._onMessage.bind(this),this._onClose=this._onClose.bind(this),this._onError=this._onError.bind(this),this._handleVisibilityChange=this._handleVisibilityChange.bind(this),this._handleOnline=this._handleOnline.bind(this),this._handleOffline=this._handleOffline.bind(this),this._setupEventListeners(),this.options.autoConnect&&this.connect()}connect(){if(!this.ws||this.ws.readyState!==WebSocket.CONNECTING&&this.ws.readyState!==WebSocket.OPEN){this.forcedClose=!1,this._updateReadyState(WebSocket.CONNECTING),console.log(`[WS] 正在连接到 ${this.url}...`),this._emit("connecting");try{this.ws=new WebSocket(this.url,this.options.protocols),this.ws.onopen=this._onOpen,this.ws.onmessage=this._onMessage,this.ws.onclose=this._onClose,this.ws.onerror=this._onError}catch(e){console.error("[WS] 连接失败:",e),this._onError(e)}}}send(e){if(this.readyState===WebSocket.OPEN){let t;t=this.options.serializeData?JSON.stringify(e):e,this.ws.send(t),console.log("[WS] 消息已发送:",t)}else console.warn("[WS] 连接未打开,消息已加入队列:",e),this.messageQueue.push(e)}close(e=1e3,t="Normal closure"){this.forcedClose=!0,this._stopHeartbeat(),this._clearReconnectTimer(),this.ws&&this.ws.readyState===WebSocket.OPEN?this.ws.close(e,t):(this._updateReadyState(WebSocket.CLOSED),this._emit("close",{code:e,reason:t,wasClean:!0}))}destroy(){console.log("[WS] 正在销毁实例..."),this.close(1e3,"Instance destroyed"),this._removeEventListeners(),this.messageQueue=[],this.listeners.clear(),this.ws=null}on(e,t){this.listeners.has(e)||this.listeners.set(e,[]),this.listeners.get(e).push(t)}off(e,t){if(this.listeners.has(e)){const n=this.listeners.get(e),s=n.indexOf(t);s>-1&&n.splice(s,1)}}_onOpen(e){console.log("[WS] 连接已建立"),this._updateReadyState(WebSocket.OPEN),this.reconnectAttempts=0,this.isReconnecting=!1,this.options.getPingMessage&&this._startHeartbeat(),this._flushMessageQueue(),this._emit("open",e)}_onMessage(e){if(this.options.isPongMessage&&this.options.isPongMessage(e))this._handlePong();else if(this.options.deserializeData)try{const t=JSON.parse(e.data);this._emit("message",t,e)}catch(t){this._emit("message",e.data,e)}else this._emit("message",e.data,e)}_onClose(e){console.log("[WS] 连接已关闭",e),this._updateReadyState(WebSocket.CLOSED),this._stopHeartbeat(),this._emit("close",e),this.forcedClose||this._scheduleReconnect()}_onError(e){console.error("[WS] 连接发生错误:",e),this._emit("error",e)}_startHeartbeat(){this.options.getPingMessage&&(this._stopHeartbeat(),this.heartbeatTimer=setInterval(()=>{if(this.ws&&this.ws.readyState===WebSocket.OPEN)try{const e=this.options.getPingMessage();this.ws.send(e),console.log("[WS] 发送 Ping:",e),this._setHeartbeatTimeout()}catch(e){console.error("[WS] 发送 Ping 消息失败:",e)}},this.options.heartbeatInterval))}_stopHeartbeat(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null),this._clearHeartbeatTimeout()}_setHeartbeatTimeout(){this._clearHeartbeatTimeout(),this.heartbeatTimeoutTimer=setTimeout(()=>{console.error("[WS] 心跳超时,主动断开连接"),this.ws.close(1006,"Heartbeat timeout")},this.options.heartbeatTimeout)}_clearHeartbeatTimeout(){this.heartbeatTimeoutTimer&&(clearTimeout(this.heartbeatTimeoutTimer),this.heartbeatTimeoutTimer=null)}_handlePong(){console.log("[WS] 收到 Pong"),this._clearHeartbeatTimeout()}_scheduleReconnect(){if(this.forcedClose||this.isReconnecting||this.reconnectAttempts>=this.options.maxReconnectAttempts)return void(this.reconnectAttempts>=this.options.maxReconnectAttempts&&(console.error("[WS] 已达到最大重连次数,停止重连"),this._emit("reconnect-failed")));this.isReconnecting=!0;const e=Math.min(this.options.reconnectBaseInterval*Math.pow(2,this.reconnectAttempts),this.options.maxReconnectInterval);console.log(`[WS] ${e/1e3}秒后将尝试第 ${this.reconnectAttempts+1} 次重连...`),this._emit("reconnect-attempt",{attempt:this.reconnectAttempts+1,interval:e}),this.reconnectTimer=setTimeout(()=>{this.reconnectAttempts++,this.connect()},e)}_clearReconnectTimer(){this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null)}_flushMessageQueue(){if(0===this.messageQueue.length)return;console.log(`[WS] 发送队列中的 ${this.messageQueue.length} 条消息`);const e=[...this.messageQueue];this.messageQueue=[],e.forEach(e=>this.send(e))}_emit(e,...t){this.listeners.has(e)&&this.listeners.get(e).forEach(e=>e(...t))}_updateReadyState(e){this.readyState=e,this._emit("ready-state-change",e)}_setupEventListeners(){document.addEventListener("visibilitychange",this._handleVisibilityChange),window.addEventListener("online",this._handleOnline),window.addEventListener("offline",this._handleOffline)}_removeEventListeners(){document.removeEventListener("visibilitychange",this._handleVisibilityChange),window.removeEventListener("online",this._handleOnline),window.removeEventListener("offline",this._handleOffline)}_handleVisibilityChange(){this.options.getPingMessage&&(document.hidden?(console.log("[WS] 页面隐藏,停止心跳"),this._stopHeartbeat()):(console.log("[WS] 页面可见,检查连接状态"),this.ws&&this.ws.readyState===WebSocket.OPEN?this._startHeartbeat():this.forcedClose||this.isReconnecting||this.connect()))}_handleOnline(){console.log("[WS] 网络已恢复,尝试重连"),this.forcedClose||this.readyState===WebSocket.OPEN||(this._clearReconnectTimer(),this.connect())}_handleOffline(){console.log("[WS] 网络已断开"),this._clearReconnectTimer(),this.ws&&this.ws.onclose({code:1006,reason:"Network offline"})}}export{n as AudioStreamResampler,K as IntervalTimer,U as MyEvent,z as MyEvent_CrossPagePlugin,j as MyId,q as WebSocketManager,_ as assignExisting,y as debounce,S as deepCloneByJSON,B as downloadByBlob,A as downloadByData,R as downloadByUrl,W as downloadExcel,$ as downloadJSON,G as extractFullyCheckedKeys,L as fetchOrDownloadByUrl,Y as findObjAttrValueById,V as findTreeNodeById,Q as flatCompleteTree2NestedTree,O as formatTimeForLocale,i as getAllSearchParams,a as getDataType,v as getFunctionArgNames,H as getGUID,r as getSearchParam,D as getTimePeriodName,o as getViewportSize,F as handleDbMenuItems,c as isBlob,u as isDate,f as isFunction,m as isNonEmptyString,h as isPlainObject,l as isPromise,N as millisecond2Duration,C as millisecond2DurationMaxDay,E as millisecond2DurationMaxHour,t as moveItem,J as nestedTree2IdMap,s as pcmToWavBlob,T as randomDateInRange,g as randomEnLetter,p as randomHan,w as randomHanOrEn,d as randomIntInRange,x as readBlobAsText,I as second2Duration,P as second2DurationMaxDay,k as second2DurationMaxHour,e as shuffle,b as throttle,M as toDate};
|
|
2
2
|
//# sourceMappingURL=a2bei4.utils.esm.min.js.map
|