@codady/utils 0.0.38 → 0.0.40

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.
Files changed (58) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/utils.cjs.js +576 -24
  3. package/dist/utils.cjs.min.js +3 -3
  4. package/dist/utils.esm.js +576 -24
  5. package/dist/utils.esm.min.js +3 -3
  6. package/dist/utils.umd.js +576 -24
  7. package/dist/utils.umd.min.js +3 -3
  8. package/dist.zip +0 -0
  9. package/examples/ajax-download.html +94 -0
  10. package/examples/ajax-get.html +59 -0
  11. package/examples/ajax-hook.html +55 -0
  12. package/examples/ajax-method.html +36 -0
  13. package/examples/ajax-post.html +37 -0
  14. package/examples/ajax-signal.html +91 -0
  15. package/examples/ajax-timeout.html +85 -0
  16. package/examples/buildUrl.html +99 -0
  17. package/examples/getUrlHash.html +71 -0
  18. package/examples/stringToEncodings-collision-test-registry.html +117 -0
  19. package/examples/stringToEncodings-collision-test.html +71 -0
  20. package/examples/stringToEncodings.html +138 -0
  21. package/examples/unicodeToEncodings.html +195 -0
  22. package/modules.js +17 -1
  23. package/modules.ts +17 -1
  24. package/package.json +1 -1
  25. package/src/ajax.js +380 -0
  26. package/src/ajax.ts +470 -0
  27. package/src/buildUrl.js +64 -0
  28. package/src/buildUrl.ts +86 -0
  29. package/src/capitalize.js +19 -0
  30. package/src/capitalize.ts +20 -0
  31. package/src/cleanQueryString.js +19 -0
  32. package/src/cleanQueryString.ts +20 -0
  33. package/src/getBodyHTML.js +53 -0
  34. package/src/getBodyHTML.ts +61 -0
  35. package/src/getEl.js +1 -1
  36. package/src/getEl.ts +6 -5
  37. package/src/getEls.js +1 -1
  38. package/src/getEls.ts +5 -5
  39. package/src/getUrlHash.js +37 -0
  40. package/src/getUrlHash.ts +39 -0
  41. package/src/isEmpty.js +24 -23
  42. package/src/isEmpty.ts +26 -23
  43. package/src/sliceStrEnd.js +63 -0
  44. package/src/sliceStrEnd.ts +60 -0
  45. package/src/stringToEncodings.js +56 -0
  46. package/src/stringToEncodings.ts +110 -0
  47. package/src/unicodeToEncodings.js +51 -0
  48. package/src/unicodeToEncodings.ts +55 -0
  49. package/src/arrayMutableMethods - /345/211/257/346/234/254.js" +0 -5
  50. package/src/comma - /345/211/257/346/234/254.js" +0 -2
  51. package/src/deepCloneToJSON - /345/211/257/346/234/254.js" +0 -47
  52. package/src/deepMergeMaps - /345/211/257/346/234/254.js" +0 -78
  53. package/src/escapeHTML - /345/211/257/346/234/254.js" +0 -29
  54. package/src/getDataType - /345/211/257/346/234/254.js" +0 -38
  55. package/src/isEmpty - /345/211/257/346/234/254.js" +0 -45
  56. package/src/mapMutableMethods - /345/211/257/346/234/254.js" +0 -5
  57. package/src/setMutableMethods - /345/211/257/346/234/254.js" +0 -5
  58. package/src/wrapMap - /345/211/257/346/234/254.js" +0 -119
@@ -1,7 +1,7 @@
1
1
  /*!
2
- * @since Last modified: 2026-1-16 15:18:30
2
+ * @since Last modified: 2026-2-5 17:4:41
3
3
  * @name Utils for web front-end.
4
- * @version 0.0.38
4
+ * @version 0.0.40
5
5
  * @author AXUI development team <3217728223@qq.com>
6
6
  * @description This is a set of general-purpose JavaScript utility functions developed by the AXUI team. All functions are pure and do not involve CSS or other third-party libraries. They are suitable for any web front-end environment.
7
7
  * @see {@link https://www.axui.cn|Official website}
@@ -12,4 +12,4 @@
12
12
  * @copyright This software supports the MIT License, allowing free learning and commercial use, but please retain the terms 'ax,' 'axui,' 'AX,' and 'AXUI' within the software.
13
13
  * @license MIT license
14
14
  */
15
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).utils=t()}(this,function(){"use strict";const getDataType=e=>{let t,r=Object.prototype.toString.call(e).slice(8,-1);return t="Function"===r&&/^\s*class\s+/.test(e.toString())?"Class":"Object"===r&&Object.getPrototypeOf(e)!==Object.prototype?"Instance":r,t},deepClone=(e,t={})=>{const r=getDataType(e),n=Object.assign({cloneSet:!0,cloneMap:!0,cloneObject:!0,cloneArray:!0,cloneDate:!0,cloneRegex:!0},t);if(n.interceptor&&"function"==typeof n.interceptor){let t=n.interceptor({input:e,type:r,parent:n.parent});if(t)return t}n.onBeforeClone?.({input:e,type:r,parent:n.parent});let a,s=!0;if("Object"===r&&n.cloneObject){const t={},r=Object.getOwnPropertySymbols(e);for(const r in e)t[r]=deepClone(e[r],n);if(r.length>0)for(const a of r)t[a]=deepClone(e[a],{...n,parent:e});a=t}else if("Array"===r&&n.cloneArray)a=e.map(t=>deepClone(t,{...n,parent:e}));else if("Map"===r&&n.cloneMap){const t=new Map;for(const[r,a]of e)t.set(deepClone(r,n),deepClone(a,{...n,parent:e}));a=t}else if("Set"===r&&n.cloneSet){const t=new Set;for(const r of e)t.add(deepClone(r,{...n,parent:e}));a=t}else if("Date"===r&&n.cloneDate)a=new Date(e.getTime());else if("RegExp"===r&&n.cloneRegex){const t=e;a=new RegExp(t.source,t.flags)}else a=e,s=!1;return n.onAfterClone?.({output:a,input:e,type:r,cloned:s,parent:n.parent}),a},deepCloneToJSON=e=>{const t=getDataType(e);if("Object"===t){const t={};for(const r in e)t[r]=deepCloneToJSON(e[r]);for(const e in t)void 0===t[e]&&Reflect.deleteProperty(t,e);return t}if("Array"===t){return e.map((e,t)=>deepCloneToJSON(e)).filter(e=>void 0!==e)}return["Number","String","Boolean","Null"].includes(t)?e:void 0},e=["push","pop","shift","unshift","splice","sort","reverse","copyWithin","fill"],t={basic:{"&":"&amp;","<":"&lt;",">":"&gt;"},attribute:{"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&apos;","`":"&#x60;"},content:{"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&apos;","/":"&#x2F;"},uri:{"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&apos;","(":"&#40;",")":"&#41;","[":"&#91;","]":"&#93;"},paranoid:{"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&apos;","`":"&#x60;","/":"&#x2F;","=":"&#x3D;","!":"&#x21;","#":"&#x23;","(":"&#40;",")":"&#41;","[":"&#91;","]":"&#93;","{":"&#x7B;","}":"&#x7D;",":":"&#x3A;",";":"&#x3B;"}},r=Object.keys(t).reduce((e,r)=>{const n=Object.keys(t[r]).map(e=>e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"));return e[r]=new RegExp(`[${n.join("")}]`,"g"),e},{}),escapeHTML=(e,n="attribute")=>{if("string"!=typeof e)return"";const a=t[n],s=r[n];return e.replace(s,e=>a[e])},getUniqueId=(e={})=>{const t=e.prefix,r=e.suffix,n=e.base10,a=e.base36;return`${t?t+"-":""}${Date.now()}${a?"-"+Math.random().toString(36).substring(2,11):""}${n?"-"+Math.floor(1e4*Math.random()).toString().padStart(4,"0"):""}${r?"-"+r:""}`},requireTypes=(e,t,r)=>{let n=Array.isArray(t)?t:[t],a=getDataType(e),s=a.toLowerCase(),o=n.map(e=>e.toLowerCase()),i=s.includes("html")?"element":s;if(r)try{if(!o.includes(i))throw new TypeError(`Expected data type(s): [${o.join(", ")}], but got: ${i}`)}catch(e){r(e,a)}else if(!o.includes(i))throw new TypeError(`Expected data type(s): [${o.join(", ")}], but got: ${i}`);return a},toSingleLine=(e,t=!1)=>{const r=e.replace(/[\r\t\n]/g,"");return t?r.replace(/\s+/g," "):r},n=["add","delete","clear"],a=["set","delete","clear"],copyObjectWithSymbol=e=>{if(!e||"object"!=typeof e)return e;const t=e,r=Object.getOwnPropertySymbols(t).reduce((e,r)=>(e[r]=t[r],e),{});return{...t,...r}},shallowCopy=(e,t={})=>{const r=getDataType(e);return"Set"===r?new Set([...e]):"Map"===r?new Map([...e]):Array.isArray(e)?[...e]:"object"===r?copyObjectWithSymbol(e):"Date"===r?new Date(e.getTime()):"RegExp"===r?new RegExp(e.source,e.flags):"Buffer"===r?Buffer.from(e):"ArrayBuffer"===r||ArrayBuffer.isView(e)?e.slice(0):"WeakSet"===r?new WeakSet([...e]):"WeakMap"===r?new WeakMap([...e]):"Error"===r?new Error(e.message):e},getEl=(e,t=document.body)=>{let r=getDataType(e),n=getDataType(t),a=n.includes("HTML")||"ShadowRoot"===n?t:document.querySelector(t),s=a&&a instanceof HTMLTemplateElement?a.content:a,o=null;if(e)if(r.includes("HTML"))o=e;else if("String"===r)try{o=(s||document).querySelector(e.trim())}catch{o=null}return o},isEmpty=e=>{let t,r=getDataType(e);return t=!e||("Object"===r?0===Object.keys(e).length:"Array"===r?""===e.join(""):"Function"===r?"{}"===e.toString().replace(/\s+/g,"").match(/{.*}/g)[0]:"Symbol"===r?"()"===e.toString().replace(/\s+/g,"").match(/\(.*\)/g)[0]:"Set"===r||"Map"===r?0===e.size:"Date"===r?isNaN(e.getTime()):"RegExp"===r?""===e.source:"ArrayBuffer"===r?0===e.byteLength:"NodeList"===r||"HTMLCollection"===r||"length"in e&&"number"==typeof e.length?0===e.length:"size"in e&&"number"==typeof e.size?0===e.size:"Error"===r||e instanceof Error?""===e.message:!(!r.includes("Array")||!["Uint8Array","Int8Array","Uint16Array","Int16Array","Uint32Array","Int32Array","Float32Array","Float64Array"].includes(r))&&0===e.length),t},createEl=(e,t,r)=>{let n=(e=e||"div").toUpperCase().trim(),a=document.createElement(n),s=getDataType(t);if(t&&"Object"===s)for(let e in t)t.hasOwnProperty(e)&&a.setAttribute(e,"string"==typeof t[e]?t[e]:JSON.stringify(t[e]));return((e,t)=>{if(""===t||null==t)return!1;let r=getDataType(t);if("TEMPLATE"===n)e.innerHTML=t.toString();else if("Array"===r&&t.length>0)for(let r of t){if(getDataType(r).includes("HTML"))e.appendChild(r);else{let t=createEl(r.name,r.attrs,r.content);t&&e.appendChild(t)}}else if(r.includes("HTML"))e.appendChild(t);else if("String"===r&&t.trim().startsWith("#")&&t.trim().length>1){let r=getEl(t);if(!r)return;"TEMPLATE"===r.nodeName?e.appendChild(r.content.cloneNode(!0)):e.insertAdjacentHTML("beforeEnd",r.innerHTML)}else e.insertAdjacentHTML("beforeEnd",t)})(a,r),a},s="rep",trim=(e,t="compress")=>{if("string"!=typeof e)return"";switch(t){case"start":return e.trimStart();case"end":return e.trimEnd();case"both":return e.trim();case"global":return e.replace(/[\s\r\n]+/g,"");default:return e.trim().replace(/[\s\r\n]+/g," ")}},parseClasses=e=>{let t,r=[];return Array.isArray(e)?r=e.filter(e=>e&&"string"==typeof e):(t=(e=trim(e)).includes(",")?",":" ",r=e.split(t)),r.map(e=>trim(e,"global")).filter(Boolean)},addClasses=(e,t,r)=>{const n=getEl(e),a=parseClasses(t);n&&0!==a.length&&a.forEach(e=>{let t;r?(t=r(e),!0===t?n.classList.add(e):"string"==typeof t&&t&&n.classList.add(t)):n.classList.add(e)})};return{getDataType:getDataType,requireTypes:requireTypes,deepClone:deepClone,deepCloneToJSON:deepCloneToJSON,wrapArrayMethods:({target:t,onBeforeMutate:r=()=>{},onAfterMutate:n=()=>{},allowList:a,props:s={}})=>{if(!Array.isArray(t))throw new TypeError("The 'target' parameter must be an array.");a&&!a?.length||(a=e);const o={};for(let e of a)o[e]=function(...a){const o={},i=t.length;switch(e){case"push":case"unshift":o.addedItems=[...a];break;case"pop":o.poppedItem=t[i-1];break;case"shift":o.shiftedItem=t[0];break;case"splice":const[e,r]=a,n=e<0?Math.max(i+e,0):Math.min(e,i),s=void 0===r?i-n:r;o.deletedItems=t.slice(n,n+s);break;case"sort":case"reverse":o.oldSnapshot=[...t];break;case"fill":case"copyWithin":const l=a[1]||0,c=void 0===a[2]?i:a[2];o.oldItems=t.slice(l,c),o.start=l,o.end=c}r?.(o);const l=Array.prototype[e].apply(t,a),c={value:l,key:e,args:a,context:o,target:t,...s};return n?.(c),l};return o},arrayMutableMethods:e,setMutableMethods:n,mapMutableMethods:a,wrapSetMethods:({target:e,onBeforeMutate:t=()=>{},onAfterMutate:r=()=>{},allowList:a=n,props:s={}})=>{if(!(e instanceof Set))throw new TypeError("The 'target' parameter must be a Set.");const o={},createWrappedMethod=n=>function(...a){const o={};switch(n){case"add":{const[t]=a;o.addedItem=t,o.existed=e.has(t);break}case"delete":{const[t]=a;o.existed=e.has(t),o.deletedItem=o.existed?t:void 0;break}case"clear":o.clearedItems=Array.from(e),o.previousSize=e.size}t(o);const i=e[n].apply(e,a),l={method:n,result:i,args:a,context:o,target:e,...s};return r(l),i};for(const e of a)n.includes(e)&&(o[e]=createWrappedMethod(e));return Object.defineProperty(o,"target",{get:()=>e,enumerable:!1,configurable:!1}),o},wrapMapMethods:({target:e,onBeforeMutate:t=()=>{},onAfterMutate:r=()=>{},allowList:n=a,props:s={}})=>{if(!(e instanceof Map))throw new TypeError("The 'target' parameter must be a Map.");const o={},createWrappedMethod=n=>function(...a){const o={};switch(n){case"set":{const[t,r]=a;o.key=t,o.newValue=r,o.existed=e.has(t),o.oldValue=o.existed?e.get(t):void 0;break}case"delete":{const[t]=a;o.key=t,o.existed=e.has(t),o.value=o.existed?e.get(t):void 0;break}case"clear":o.clearedItems=Array.from(e.entries()),o.previousSize=e.size}t(o);const i=e[n].apply(e,a),l={method:n,result:i,args:a,context:o,target:e,...s};return r(l),i};for(const e of n)a.includes(e)&&(o[e]=createWrappedMethod(e));return Object.defineProperty(o,"target",{get:()=>e,enumerable:!1,configurable:!1}),o},getUniqueId:getUniqueId,deepMerge:(e,t,r={})=>{const n=Object.assign({dataMode:"clear",inheritMissing:!0,targetClone:!1,useEnable:!0,useSymbol:!0,nullBehavior:"preserve",undefinedBehavior:"preserve",deepClone:{},onBeforeMerge:void 0,onAfterMerge:void 0},r),smartMerger=(e,t,n)=>{let a,s,o=getDataType(e),i=getDataType(t),l=!0;if(n.interceptor&&"function"==typeof n.interceptor){let r=n.interceptor({target:e,source:t,targetType:o,sourceType:i,parent:n.parent});if(r){if(null===r?.target||null===r?.source)return r;e=r.target,t=r.source}}return n?.onBeforeMerge?.({target:e,source:t,targetType:o,sourceType:i,parent:n.parent}),"Object"===o&&"Object"===i?(s=deepMergeObjects(e,t,n),a="Object"):"Array"===o&&"Array"===i?(s=deepMergeArrays(e,t,n),a="Array"):"Set"===o&&"Set"===i?(s=deepMergeSets(e,t,n),a="Set"):"Map"===o&&"Map"===i?(s=deepMergeMaps(e,t,n),a="Map"):(l=!1,s=e),n?.onAfterMerge?.({result:s,target:e,source:t,targetType:o,sourceType:i,mergeType:a,parent:r.parent}),{result:s,flag:l,mergeType:a}},mergeEnableObject=(e,t)=>e?.hasOwnProperty("enable")&&"boolean"==typeof t?(e.enable=t,e):t?.hasOwnProperty("enable")&&"boolean"==typeof e?Object.assign({enable:e},t):t,deepMergeObjects=(e,t,r={})=>{let n=getDataType(e),a=getDataType(t);if("Object"!==n||"Object"!==a)return e;const s=Object.assign({inheritMissing:!0,targetClone:!1,useEnable:!0},r);let o={};o=s.targetClone?shallowCopy(e):e;for(let e in t){const n=o[e],a=t[e];if(t.hasOwnProperty(e)&&o.hasOwnProperty(e)){const t=smartMerger(n,a,{...r,parent:o});if(t.flag)t.mergeType?"Object"===t.mergeType&&(o[e]=t.result):o[e]=a;else{let t=s.useEnable?mergeEnableObject(n,a):a;n!==t&&null===t?"ignore"===s.nullBehavior||("delete"===s.nullBehavior?Reflect.deleteProperty(o,e):o[e]=t):n!==t&&void 0===t?"ignore"===s.undefinedBehavior||("delete"===s.undefinedBehavior?Reflect.deleteProperty(o,e):o[e]=a):o[e]=a}}else t.hasOwnProperty(e)&&!o.hasOwnProperty(e)&&s.inheritMissing&&(o[e]=a)}if(s.useSymbol){let e=Object.getOwnPropertySymbols(t);if(e.length)for(let r of e)o[r]=t[r]}return o},deepMergeArrays=(e,t,r={})=>{if(!Array.isArray(e)||!Array.isArray(t))return e;const n=Object.assign({dataMode:"clear",inheritMissing:!0,targetClone:!1},r),a=n.targetClone?[...e]:e;if("replace"===n.dataMode)for(let e=0;e<t.length&&(n.inheritMissing||!(e>=a.length));e++){smartMerger(a[e],t[e],{...n,parent:a}).flag||(a[e]=t[e])}else"concat"===n.dataMode||(a.length=0),a.push(...t);return a},deepMergeMaps=(e,t,r={})=>{if(!(e instanceof Map&&t instanceof Map))return e;const n=Object.assign({inheritMissing:!0,targetClone:!1,useEnable:!0},r),a=n.targetClone?new Map([...e]):e;for(const[e,s]of t.entries())if(a.has(e)){const t=a.get(e),r=s,o=smartMerger(t,r,n);o.flag?"Object"===o.mergeType&&a.set(e,o.result):a.set(e,r)}else r.inheritMissing&&a.set(e,s);return a},deepMergeSets=(e,t,r={})=>{if(!(e instanceof Set&&t instanceof Set))return e;const n=Object.assign({dataMode:"clear",inheritMissing:!0,targetClone:!1,useEnable:!0},r),a=n.targetClone?new Set(...e):e;if("replace"===n.dataMode){const e=[...a],r=[...t],s=smartMerger(e,r,n);a.clear();for(let e of s.result)a.add(e)}else if("concat"===n.dataMode)for(let e of t)a.add(e);else{a.clear();for(let e of t)a.add(e)}return a};return smartMerger(e,t,n).result},shallowCopy:shallowCopy,copyObjectWithSymbol:copyObjectWithSymbol,getEl:getEl,getEls:(e,t=document.body)=>{let r=getDataType(e),n=getEl(t),a=n&&n instanceof HTMLTemplateElement?n.content:n||document,s=[];return isEmpty(e)?s:(r.includes("HTML")?s.push(e):"String"===r?s=(e=e.trim()).split(",").map(e=>[...a.querySelectorAll(e)]).flat():"Array"===r&&(s=e.map(e=>getEl(e,n))),s.filter(Boolean))},createEl:createEl,getSvgUri:e=>`data:image/svg+xml;utf8,${e.replace(/\n/g,"").replace(/\s+/g," ").trim().replace(/%/g,"%25").replace(/#/g,"%23").replace(/{/g,"%7B").replace(/}/g,"%7D").replace(/</g,"%3C").replace(/>/g,"%3E")}`,fileToBase64:e=>new Promise((t,r)=>{const n=new FileReader;n.onload=()=>{"string"==typeof n.result?t(n.result):r(new Error("FileReader result is not a string"))},n.onerror=()=>{r(n.error||new Error("Unknown error occurred during file reading"))},n.readAsDataURL(e)}),NAMESPACE:"ax",ALIAS:s,COMMA:",",SPACE:" ",trim:trim,parseClasses:parseClasses,getClasses:e=>{let t=getEl(e);return t?parseClasses(t.getAttribute("class")||""):[]},addClasses:addClasses,removeClasses:(e,t,r)=>{const n=getEl(e),a=parseClasses(t);n&&0!==a.length&&a.forEach(e=>{let t;r?(t=r(e),!0===t?n.classList.remove(e):"string"==typeof t&&t&&n.classList.remove(t)):n.classList.remove(e)})},createTools:e=>{const t=createEl("span",{class:"ax-box-tools"}),renderFn=e=>{const t={},r=e.extendable?`<i ${s}="arrow"></i>`:"",n=(e.icon?`<i ${s}="icon">${e.icon}</i>`:"")+(e.disk?`<i ${s}="disk"><img src="${e.disk}"/></i>`:"")+(e.cube?`<i ${s}="cube"><img src="${e.cube}"/></i>`:"")+(e.image?`<i ${s}="image"><img src="${e.image}"/></i>`:"")+(e.label?`<i ${s}="label">${e.label}</i>`:"")+r;e.title&&(t.title=e.title),e.focusable&&(t.tabindex=1),e.wrapEl=createEl(e.nodeName||"span",Object.assign(t,e.attrs),n),e.iconEl=e.wrapEl.querySelector(`[${s}="icon"]`),e.cubeEl=e.wrapEl.querySelector(`[${s}="cube"]`),e.diskEl=e.wrapEl.querySelector(`[${s}="disk"]`),e.imageEl=e.wrapEl.querySelector(`[${s}="image"]`),e.labelEl=e.wrapEl.querySelector(`[${s}="label"]`),!isEmpty(e.classes)&&addClasses(e.wrapEl,e.classes),!isEmpty(e.styles)&&(e.wrapEl.style.cssText+=e.styles)};for(let r of e)renderFn(r),t.appendChild(r.wrapEl),r?.action?.(r);return t},typeWriter:(e,t)=>{const r=t.speed||100;return new Promise(n=>{t?.onBeforeType?.(e);let a=0;const s=setInterval(()=>{if(a<e.length){const r=e.charAt(a),n=e.substring(0,a+1);t?.onDuringType?.(r,n),a++}else clearInterval(s),n(e),t?.onAfterType?.(e)},r)})},parseLLMStream:async(e,t)=>{if(!(e&&e instanceof ReadableStream))throw new Error("Invalid input: ReadableStream is missing or not an instance of ReadableStream.");const r=e.getReader(),n=new TextDecoder("utf-8");let a="";const s={fullText:"",finishReason:null,usage:null,isCompleted:!1};try{for(;;){const{done:e,value:o}=await r.read();if(e)break;a+=n.decode(o,{stream:!0});let i=a.split("\n");a=i.pop()||"";for(const e of i){const r=e.trim();if(!r||!r.startsWith("data: "))continue;const n=r.substring(6);if("[DONE]"!==n)try{const e=JSON.parse(n),r=e.choices?.[0],a=r?.delta?.content||"";a&&(s.fullText+=a,t?.(a)),r?.finish_reason&&(s.finishReason=r.finish_reason),e.usage&&(s.usage=e.usage)}catch(e){}else s.isCompleted=!0}}}catch(e){throw e}return s},toKebabCase:(e,t="",r="")=>`${t}${e.replace(/([A-Z])/g,"-$1").toLowerCase()}${r}`,trimEmptyLines:e=>null==e?"":e.replace(/^\s*\n|\n\s*$/g,"")||"",decodeHtmlEntities:e=>{if(!e)return"";const t=document.createElement("textarea");return t.innerHTML=e,t.value},escapeCharsMaps:t,escapeRegexMaps:r,escapeHTML:escapeHTML,toSingleLine:toSingleLine,renderTpl:(e,t,r={})=>{if(requireTypes(e,"string",e=>""),!e.trim())return"";let n=requireTypes(t,["array","object"],t=>e);if(0===Object.keys(t).length)return e;let a,s=Object.assign({strict:!1,start:"{{",end:"}}",suffix:"/"},r),o=s.start.split("").map(e=>"\\"+e).join(""),i=s.end.split("").map(e=>"\\"+e).join(""),l=new RegExp(`${o}([\\s\\S]+?)?${i}`,"g"),c='"use strict";let str=[];\n',p=0,u="",f=`__esc__${getUniqueId()}`,add=(e,t)=>(t?e.endsWith(s.suffix)?c+=e.slice(0,-s.suffix.length)+"\n":c+=s.escape?`str.push(${f}(String(${e}), "${s.escape}"));\n`:`str.push(${e});\n`:c+=""!==e?'str.push("'+e.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r")+'");\n':"",add);for(;a=l.exec(e);)add(e.slice(p,a.index))(a[1],!0),p=a.index+a[0].length;add(e.slice(p)),c+="return str.join('');",c=toSingleLine(c);try{if(s.strict||"Array"===n)u=new Function(f,c).apply(t,[escapeHTML]);else{let e=Object.keys(t),r=Object.values(t);u=new Function(...e,f,c).bind(t)(...r,escapeHTML)}}catch(e){}return u}}});
15
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).utils=t()}(this,function(){"use strict";const getDataType=e=>{let t,r=Object.prototype.toString.call(e).slice(8,-1);return t="Function"===r&&/^\s*class\s+/.test(e.toString())?"Class":"Object"===r&&Object.getPrototypeOf(e)!==Object.prototype?"Instance":r,t},deepClone=(e,t={})=>{const r=getDataType(e),n=Object.assign({cloneSet:!0,cloneMap:!0,cloneObject:!0,cloneArray:!0,cloneDate:!0,cloneRegex:!0},t);if(n.interceptor&&"function"==typeof n.interceptor){let t=n.interceptor({input:e,type:r,parent:n.parent});if(t)return t}n.onBeforeClone?.({input:e,type:r,parent:n.parent});let s,o=!0;if("Object"===r&&n.cloneObject){const t={},r=Object.getOwnPropertySymbols(e);for(const r in e)t[r]=deepClone(e[r],n);if(r.length>0)for(const s of r)t[s]=deepClone(e[s],{...n,parent:e});s=t}else if("Array"===r&&n.cloneArray)s=e.map(t=>deepClone(t,{...n,parent:e}));else if("Map"===r&&n.cloneMap){const t=new Map;for(const[r,s]of e)t.set(deepClone(r,n),deepClone(s,{...n,parent:e}));s=t}else if("Set"===r&&n.cloneSet){const t=new Set;for(const r of e)t.add(deepClone(r,{...n,parent:e}));s=t}else if("Date"===r&&n.cloneDate)s=new Date(e.getTime());else if("RegExp"===r&&n.cloneRegex){const t=e;s=new RegExp(t.source,t.flags)}else s=e,o=!1;return n.onAfterClone?.({output:s,input:e,type:r,cloned:o,parent:n.parent}),s},deepCloneToJSON=e=>{const t=getDataType(e);if("Object"===t){const t={};for(const r in e)t[r]=deepCloneToJSON(e[r]);for(const e in t)void 0===t[e]&&Reflect.deleteProperty(t,e);return t}if("Array"===t){return e.map((e,t)=>deepCloneToJSON(e)).filter(e=>void 0!==e)}return["Number","String","Boolean","Null"].includes(t)?e:void 0},e=["push","pop","shift","unshift","splice","sort","reverse","copyWithin","fill"],t={basic:{"&":"&amp;","<":"&lt;",">":"&gt;"},attribute:{"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&apos;","`":"&#x60;"},content:{"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&apos;","/":"&#x2F;"},uri:{"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&apos;","(":"&#40;",")":"&#41;","[":"&#91;","]":"&#93;"},paranoid:{"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&apos;","`":"&#x60;","/":"&#x2F;","=":"&#x3D;","!":"&#x21;","#":"&#x23;","(":"&#40;",")":"&#41;","[":"&#91;","]":"&#93;","{":"&#x7B;","}":"&#x7D;",":":"&#x3A;",";":"&#x3B;"}},r=Object.keys(t).reduce((e,r)=>{const n=Object.keys(t[r]).map(e=>e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"));return e[r]=new RegExp(`[${n.join("")}]`,"g"),e},{}),escapeHTML=(e,n="attribute")=>{if("string"!=typeof e)return"";const s=t[n],o=r[n];return e.replace(o,e=>s[e])},getUniqueId=(e={})=>{const t=e.prefix,r=e.suffix,n=e.base10,s=e.base36;return`${t?t+"-":""}${Date.now()}${s?"-"+Math.random().toString(36).substring(2,11):""}${n?"-"+Math.floor(1e4*Math.random()).toString().padStart(4,"0"):""}${r?"-"+r:""}`},requireTypes=(e,t,r)=>{let n=Array.isArray(t)?t:[t],s=getDataType(e),o=s.toLowerCase(),a=n.map(e=>e.toLowerCase()),i=o.includes("html")?"element":o;if(r)try{if(!a.includes(i))throw new TypeError(`Expected data type(s): [${a.join(", ")}], but got: ${i}`)}catch(e){r(e,s)}else if(!a.includes(i))throw new TypeError(`Expected data type(s): [${a.join(", ")}], but got: ${i}`);return s},toSingleLine=(e,t=!1)=>{const r=e.replace(/[\r\t\n]/g,"");return t?r.replace(/\s+/g," "):r},n=["add","delete","clear"],s=["set","delete","clear"],copyObjectWithSymbol=e=>{if(!e||"object"!=typeof e)return e;const t=e,r=Object.getOwnPropertySymbols(t).reduce((e,r)=>(e[r]=t[r],e),{});return{...t,...r}},shallowCopy=(e,t={})=>{const r=getDataType(e);return"Set"===r?new Set([...e]):"Map"===r?new Map([...e]):Array.isArray(e)?[...e]:"object"===r?copyObjectWithSymbol(e):"Date"===r?new Date(e.getTime()):"RegExp"===r?new RegExp(e.source,e.flags):"Buffer"===r?Buffer.from(e):"ArrayBuffer"===r||ArrayBuffer.isView(e)?e.slice(0):"WeakSet"===r?new WeakSet([...e]):"WeakMap"===r?new WeakMap([...e]):"Error"===r?new Error(e.message):e},getEl=(e,t=document.body)=>{let r=getDataType(e),n=getDataType(t),s=n.includes("HTML")||"ShadowRoot"===n?t:document.querySelector(t),o=s&&s instanceof HTMLTemplateElement?s.content:s,a=null;if(e)if(r.includes("HTML"))a=e;else if("String"===r)try{a=(o||document).querySelector(e.trim())}catch{a=null}return a},isEmpty=e=>{let t,r=getDataType(e);return!e||!["String","Number","Boolean"].includes(r)&&(t="Object"===r?0===Object.keys(e).length:"Array"===r?""===e.join(""):"Function"===r?"{}"===e.toString().replace(/\s+/g,"").match(/{.*}/g)[0]:"Symbol"===r?"()"===e.toString().replace(/\s+/g,"").match(/\(.*\)/g)[0]:"Set"===r||"Map"===r?0===e.size:"Date"===r?isNaN(e.getTime()):"RegExp"===r?""===e.source:"ArrayBuffer"===r?0===e.byteLength:"NodeList"===r||"HTMLCollection"===r||"length"in e&&"number"==typeof e.length?0===e.length:"size"in e&&"number"==typeof e.size?0===e.size:"Error"===r||e instanceof Error?""===e.message:!(!r.includes("Array")||!["Uint8Array","Int8Array","Uint16Array","Int16Array","Uint32Array","Int32Array","Float32Array","Float64Array"].includes(r))&&0===e.length,t)},createEl=(e,t,r)=>{let n=(e=e||"div").toUpperCase().trim(),s=document.createElement(n),o=getDataType(t);if(t&&"Object"===o)for(let e in t)t.hasOwnProperty(e)&&s.setAttribute(e,"string"==typeof t[e]?t[e]:JSON.stringify(t[e]));return((e,t)=>{if(""===t||null==t)return!1;let r=getDataType(t);if("TEMPLATE"===n)e.innerHTML=t.toString();else if("Array"===r&&t.length>0)for(let r of t){if(getDataType(r).includes("HTML"))e.appendChild(r);else{let t=createEl(r.name,r.attrs,r.content);t&&e.appendChild(t)}}else if(r.includes("HTML"))e.appendChild(t);else if("String"===r&&t.trim().startsWith("#")&&t.trim().length>1){let r=getEl(t);if(!r)return;"TEMPLATE"===r.nodeName?e.appendChild(r.content.cloneNode(!0)):e.insertAdjacentHTML("beforeEnd",r.innerHTML)}else e.insertAdjacentHTML("beforeEnd",t)})(s,r),s},o="rep",trim=(e,t="compress")=>{if("string"!=typeof e)return"";switch(t){case"start":return e.trimStart();case"end":return e.trimEnd();case"both":return e.trim();case"global":return e.replace(/[\s\r\n]+/g,"");default:return e.trim().replace(/[\s\r\n]+/g," ")}},parseClasses=e=>{let t,r=[];return Array.isArray(e)?r=e.filter(e=>e&&"string"==typeof e):(t=(e=trim(e)).includes(",")?",":" ",r=e.split(t)),r.map(e=>trim(e,"global")).filter(Boolean)},addClasses=(e,t,r)=>{const n=getEl(e),s=parseClasses(t);n&&0!==s.length&&s.forEach(e=>{let t;r?(t=r(e),!0===t?n.classList.add(e):"string"==typeof t&&t&&n.classList.add(t)):n.classList.add(e)})},getBodyHTML=(e,t)=>{if(!e||"string"!=typeof e)return"";try{const r=(new DOMParser).parseFromString(e,"text/html"),n=r.body.innerHTML;if(t){const e=r.querySelector(t);if(e)return e.innerHTML}return n?n.trim():e}catch(t){return e}},getUrlHash=e=>{if(!e||"string"!=typeof e)return"";try{const t=window?.location?.origin||"https://www.axui.cn";return new URL(e,t).hash}catch(e){return""}},cleanQueryString=e=>"string"==typeof e&&(e.startsWith("?")||e.startsWith("&"))?e.slice(1):e,buildUrl=({url:e,data:t,cacheBustKey:r="_t",appendCacheBust:n=!0})=>{const s=e.indexOf("#");let o="",a=e;-1!==s&&(o=e.slice(s),a=e.slice(0,s));const i=new URL(a,window.location.origin);if(!isEmpty(t)){let e,r=getDataType(t);e="URLSearchParams"===r?t:"object"===r?new URLSearchParams(t):new URLSearchParams(cleanQueryString(t)),e.forEach((e,t)=>{i.searchParams.append(t,e)})}return n&&r&&i.searchParams.set(r,Date.now().toString()),i.toString()+o},capitalize=e=>e?e.charAt(0).toUpperCase()+e.slice(1):e,ajax=e=>{if(isEmpty(e))return Promise.reject(new Error("Options are required"));if(!e.url||"string"!=typeof e.url)return Promise.reject(new Error("URL is required and must be a string"));const t={url:"",method:"POST",async:!0,selector:"",data:null,timeout:36e5,headers:{},responseType:"",catchError:!1,signal:null,xhrFields:{},cacheBustKey:"_t",precision:2,onAbort:null,onTimeout:null,onBeforeSend:null,onCreated:null,onOpened:null,onHeadersReceived:null,onLoading:null,onSuccess:null,onFailure:null,onInformation:null,onRedirection:null,onClientError:null,onServerError:null,onUnknownError:null,onError:null,onFinish:null,onDownload:null,onUpload:null,onComplete:null};Object.assign(t,e);const r=t.method.toUpperCase()||"POST",n=["GET","HEAD","TRACE"];let s=new XMLHttpRequest,o=null,a=t?.headers?.["Content-Type"]||t?.headers?.["content-type"];if(!isEmpty(t.data)){let e=getDataType(t.data);"FormData"===e?(o=t.data,a&&(delete t.headers["Content-Type"],delete t.headers["content-type"])):"Object"===e?a?o=a?.includes("application/json")?JSON.stringify(t.data):t.data:(o=new URLSearchParams(t.data).toString(),n.includes(r)||(t.headers["Content-Type"]="application/x-www-form-urlencoded")):"String"===e&&(!a||a.includes("urlencoded"))?(o=cleanQueryString(t.data.trim()),n.includes(r)||a||(t.headers["Content-Type"]="application/x-www-form-urlencoded")):o=t.data}s.timeout=t.timeout,t.responseType&&(s.responseType=t.responseType);const i=new Promise((e,a)=>{const timeoutHandler=()=>{cleanup();let r={...i,status:s.status,content:s.response,type:"timeout"};t?.onTimeout?.(r),t.catchError?a(r):e(r),t?.onFailure?.(r),t?.onFinish?.(r)},errorHandler=r=>{"client-error"===r.type?t?.onClientError?.({...i}):"server-error"===r.type?t?.onServerError?.({...i}):"unknown-error"===r.type&&t?.onUnknownError?.({...i}),t?.onError?.(r),t.catchError?a(r):e(r)},abortHandler=()=>{cleanup();const r={...i,status:s.status,type:"abort"};t.catchError?a(r):e(r),t?.onAbort?.(r),t?.onFinish?.(r)},abortHandlerWithSignal=()=>{s.abort(),abortHandler()},cleanup=()=>{t.signal&&t.signal.removeEventListener("abort",abortHandlerWithSignal),t.onError&&s.removeEventListener("error",errorHandler),t.onTimeout&&s.removeEventListener("timeout",timeoutHandler),t.onUpload&&s.upload.removeEventListener("progress",uploadProgressHandler),t.onDownload&&s.removeEventListener("progress",downloadProgressHandler),s.onreadystatechange=null},i={xhr:s,data:o,abort:abortHandler,status:"",content:null,stage:0,type:"unset",progress:{}},getProgressValues=e=>{let r=(100*e).toFixed(t.precision);return{percent:parseFloat(r),text:r}},progressHandler=(e,r,n)=>{if(r.lengthComputable){const o={...i,status:s.status},a=r.loaded/r.total,{percent:l,text:c}=getProgressValues(a);o.progress={name:e,loaded:r.loaded,total:r.total,timestamp:new Date(r.timeStamp).getTime(),ratio:a,percent:l,text:c},n?.(o),a>=1&&(Object.assign(o.progress,getProgressValues(1)),t?.onComplete?.(o))}},uploadProgressHandler=e=>{progressHandler("upload",e,e=>t.onUpload(e))},downloadProgressHandler=e=>{progressHandler("download",e,e=>t.onDownload(e))};if(t.signal){if(t.signal.aborted)return abortHandlerWithSignal();t.signal.addEventListener("abort",abortHandlerWithSignal)}t.onUpload&&s.upload.addEventListener("progress",uploadProgressHandler),t.onDownload&&s.addEventListener("progress",downloadProgressHandler),t.onError&&s.addEventListener("error",errorHandler),t.onTimeout&&s.addEventListener("timeout",timeoutHandler),t.onAbort&&s.addEventListener("abort",abortHandler),t.onCreated?.({...i,type:"created"}),s.onreadystatechange=function(){i.stage=s.readyState,i.status=s.status;const r={1:"opened",2:"headersReceived",3:"loading"};if(s.readyState<4){if(!s.readyState)return;return i.type=r[s.readyState],void t[`on${capitalize(i.type)}`]?.({...i})}if(0===s.status&&"abort"!==i.type)return;cleanup();const n=s.status>=100&&s.status<200,o=s.status>=200&&s.status<300||304===s.status,a=s.status>=300&&s.status<400,l=s.status>=400&&s.status<500,c=s.status>=500&&s.status<600;if(o){if(t.responseType&&"text"!==s.responseType)i.content=s.response;else{let e=s.responseText.trim(),r="";if(e.startsWith("[")&&e.endsWith("]")||e.startsWith("{")&&e.endsWith("}"))try{r=JSON.parse(e)}catch{r=s.responseText}else if(/(<\/html>|<\/body>)/i.test(e)){let n=getUrlHash(t.url);r=getBodyHTML(e,t.selector||n)}else r=s.responseText;i.content=r}i.type="success",p={...i},t?.onSuccess?.(p),e(p)}else i.content=s.response,i.type=n?"infomation":a?"redirection":l?"client-error":c?"server-error":"unknown-error",n?t?.onInformation?.({...i}):a?t?.onRedirection?.({...i}):errorHandler({...i}),t?.onFailure?.({...i});var p;t?.onFinish?.({...i})};let l=[r,t.url,t.async];if(n.includes(r)){const e=buildUrl({url:t.url,data:o,cacheBustKey:t.cacheBustKey,appendCacheBust:!0});l=[r,e,t.async]}for(let e in t.xhrFields)t.xhrFields.hasOwnProperty(e)&&(s[e]=t.xhrFields[e]);s.open(...l);for(let e in t.headers)t.headers.hasOwnProperty(e)&&!isEmpty(t.headers[e])&&s.setRequestHeader(e,t.headers[e]);t?.onBeforeSend?.({...i,status:s.status,type:"beforeSend"}),s.send(n.includes(r)?null:o||null)});return i.xhr=s,i.abort=()=>s.abort(),i};["post","put","delete","patch","options","get","head","trace"].forEach(e=>{ajax[e]=(t,r,n={url:""})=>ajax({...n,method:e,url:t,data:r})}),ajax.all=e=>Promise.all(e.map(ajax));return{getDataType:getDataType,requireTypes:requireTypes,deepClone:deepClone,deepCloneToJSON:deepCloneToJSON,wrapArrayMethods:({target:t,onBeforeMutate:r=()=>{},onAfterMutate:n=()=>{},allowList:s,props:o={}})=>{if(!Array.isArray(t))throw new TypeError("The 'target' parameter must be an array.");s&&!s?.length||(s=e);const a={};for(let e of s)a[e]=function(...s){const a={},i=t.length;switch(e){case"push":case"unshift":a.addedItems=[...s];break;case"pop":a.poppedItem=t[i-1];break;case"shift":a.shiftedItem=t[0];break;case"splice":const[e,r]=s,n=e<0?Math.max(i+e,0):Math.min(e,i),o=void 0===r?i-n:r;a.deletedItems=t.slice(n,n+o);break;case"sort":case"reverse":a.oldSnapshot=[...t];break;case"fill":case"copyWithin":const l=s[1]||0,c=void 0===s[2]?i:s[2];a.oldItems=t.slice(l,c),a.start=l,a.end=c}r?.(a);const l=Array.prototype[e].apply(t,s),c={value:l,key:e,args:s,context:a,target:t,...o};return n?.(c),l};return a},arrayMutableMethods:e,setMutableMethods:n,mapMutableMethods:s,wrapSetMethods:({target:e,onBeforeMutate:t=()=>{},onAfterMutate:r=()=>{},allowList:s=n,props:o={}})=>{if(!(e instanceof Set))throw new TypeError("The 'target' parameter must be a Set.");const a={},createWrappedMethod=n=>function(...s){const a={};switch(n){case"add":{const[t]=s;a.addedItem=t,a.existed=e.has(t);break}case"delete":{const[t]=s;a.existed=e.has(t),a.deletedItem=a.existed?t:void 0;break}case"clear":a.clearedItems=Array.from(e),a.previousSize=e.size}t(a);const i=e[n].apply(e,s),l={method:n,result:i,args:s,context:a,target:e,...o};return r(l),i};for(const e of s)n.includes(e)&&(a[e]=createWrappedMethod(e));return Object.defineProperty(a,"target",{get:()=>e,enumerable:!1,configurable:!1}),a},wrapMapMethods:({target:e,onBeforeMutate:t=()=>{},onAfterMutate:r=()=>{},allowList:n=s,props:o={}})=>{if(!(e instanceof Map))throw new TypeError("The 'target' parameter must be a Map.");const a={},createWrappedMethod=n=>function(...s){const a={};switch(n){case"set":{const[t,r]=s;a.key=t,a.newValue=r,a.existed=e.has(t),a.oldValue=a.existed?e.get(t):void 0;break}case"delete":{const[t]=s;a.key=t,a.existed=e.has(t),a.value=a.existed?e.get(t):void 0;break}case"clear":a.clearedItems=Array.from(e.entries()),a.previousSize=e.size}t(a);const i=e[n].apply(e,s),l={method:n,result:i,args:s,context:a,target:e,...o};return r(l),i};for(const e of n)s.includes(e)&&(a[e]=createWrappedMethod(e));return Object.defineProperty(a,"target",{get:()=>e,enumerable:!1,configurable:!1}),a},getUniqueId:getUniqueId,deepMerge:(e,t,r={})=>{const n=Object.assign({dataMode:"clear",inheritMissing:!0,targetClone:!1,useEnable:!0,useSymbol:!0,nullBehavior:"preserve",undefinedBehavior:"preserve",deepClone:{},onBeforeMerge:void 0,onAfterMerge:void 0},r),smartMerger=(e,t,n)=>{let s,o,a=getDataType(e),i=getDataType(t),l=!0;if(n.interceptor&&"function"==typeof n.interceptor){let r=n.interceptor({target:e,source:t,targetType:a,sourceType:i,parent:n.parent});if(r){if(null===r?.target||null===r?.source)return r;e=r.target,t=r.source}}return n?.onBeforeMerge?.({target:e,source:t,targetType:a,sourceType:i,parent:n.parent}),"Object"===a&&"Object"===i?(o=deepMergeObjects(e,t,n),s="Object"):"Array"===a&&"Array"===i?(o=deepMergeArrays(e,t,n),s="Array"):"Set"===a&&"Set"===i?(o=deepMergeSets(e,t,n),s="Set"):"Map"===a&&"Map"===i?(o=deepMergeMaps(e,t,n),s="Map"):(l=!1,o=e),n?.onAfterMerge?.({result:o,target:e,source:t,targetType:a,sourceType:i,mergeType:s,parent:r.parent}),{result:o,flag:l,mergeType:s}},mergeEnableObject=(e,t)=>e?.hasOwnProperty("enable")&&"boolean"==typeof t?(e.enable=t,e):t?.hasOwnProperty("enable")&&"boolean"==typeof e?Object.assign({enable:e},t):t,deepMergeObjects=(e,t,r={})=>{let n=getDataType(e),s=getDataType(t);if("Object"!==n||"Object"!==s)return e;const o=Object.assign({inheritMissing:!0,targetClone:!1,useEnable:!0},r);let a={};a=o.targetClone?shallowCopy(e):e;for(let e in t){const n=a[e],s=t[e];if(t.hasOwnProperty(e)&&a.hasOwnProperty(e)){const t=smartMerger(n,s,{...r,parent:a});if(t.flag)t.mergeType?"Object"===t.mergeType&&(a[e]=t.result):a[e]=s;else{let t=o.useEnable?mergeEnableObject(n,s):s;n!==t&&null===t?"ignore"===o.nullBehavior||("delete"===o.nullBehavior?Reflect.deleteProperty(a,e):a[e]=t):n!==t&&void 0===t?"ignore"===o.undefinedBehavior||("delete"===o.undefinedBehavior?Reflect.deleteProperty(a,e):a[e]=s):a[e]=s}}else t.hasOwnProperty(e)&&!a.hasOwnProperty(e)&&o.inheritMissing&&(a[e]=s)}if(o.useSymbol){let e=Object.getOwnPropertySymbols(t);if(e.length)for(let r of e)a[r]=t[r]}return a},deepMergeArrays=(e,t,r={})=>{if(!Array.isArray(e)||!Array.isArray(t))return e;const n=Object.assign({dataMode:"clear",inheritMissing:!0,targetClone:!1},r),s=n.targetClone?[...e]:e;if("replace"===n.dataMode)for(let e=0;e<t.length&&(n.inheritMissing||!(e>=s.length));e++){smartMerger(s[e],t[e],{...n,parent:s}).flag||(s[e]=t[e])}else"concat"===n.dataMode||(s.length=0),s.push(...t);return s},deepMergeMaps=(e,t,r={})=>{if(!(e instanceof Map&&t instanceof Map))return e;const n=Object.assign({inheritMissing:!0,targetClone:!1,useEnable:!0},r),s=n.targetClone?new Map([...e]):e;for(const[e,o]of t.entries())if(s.has(e)){const t=s.get(e),r=o,a=smartMerger(t,r,n);a.flag?"Object"===a.mergeType&&s.set(e,a.result):s.set(e,r)}else r.inheritMissing&&s.set(e,o);return s},deepMergeSets=(e,t,r={})=>{if(!(e instanceof Set&&t instanceof Set))return e;const n=Object.assign({dataMode:"clear",inheritMissing:!0,targetClone:!1,useEnable:!0},r),s=n.targetClone?new Set(...e):e;if("replace"===n.dataMode){const e=[...s],r=[...t],o=smartMerger(e,r,n);s.clear();for(let e of o.result)s.add(e)}else if("concat"===n.dataMode)for(let e of t)s.add(e);else{s.clear();for(let e of t)s.add(e)}return s};return smartMerger(e,t,n).result},shallowCopy:shallowCopy,copyObjectWithSymbol:copyObjectWithSymbol,getEl:getEl,getEls:(e,t=document.body)=>{let r=getDataType(e),n=getEl(t),s=n&&n instanceof HTMLTemplateElement?n.content:n||document,o=[];return isEmpty(e)?o:(r.includes("HTML")?o.push(e):"String"===r?o=(e=e.trim()).split(",").map(e=>[...s.querySelectorAll(e)]).flat():"Array"===r&&(o=e.map(e=>getEl(e,n))),o.filter(Boolean))},createEl:createEl,getSvgUri:e=>`data:image/svg+xml;utf8,${e.replace(/\n/g,"").replace(/\s+/g," ").trim().replace(/%/g,"%25").replace(/#/g,"%23").replace(/{/g,"%7B").replace(/}/g,"%7D").replace(/</g,"%3C").replace(/>/g,"%3E")}`,fileToBase64:e=>new Promise((t,r)=>{const n=new FileReader;n.onload=()=>{"string"==typeof n.result?t(n.result):r(new Error("FileReader result is not a string"))},n.onerror=()=>{r(n.error||new Error("Unknown error occurred during file reading"))},n.readAsDataURL(e)}),NAMESPACE:"ax",ALIAS:o,COMMA:",",SPACE:" ",trim:trim,parseClasses:parseClasses,getClasses:e=>{let t=getEl(e);return t?parseClasses(t.getAttribute("class")||""):[]},addClasses:addClasses,removeClasses:(e,t,r)=>{const n=getEl(e),s=parseClasses(t);n&&0!==s.length&&s.forEach(e=>{let t;r?(t=r(e),!0===t?n.classList.remove(e):"string"==typeof t&&t&&n.classList.remove(t)):n.classList.remove(e)})},createTools:e=>{const t=createEl("span",{class:"ax-box-tools"}),renderFn=e=>{const t={},r=e.extendable?`<i ${o}="arrow"></i>`:"",n=(e.icon?`<i ${o}="icon">${e.icon}</i>`:"")+(e.disk?`<i ${o}="disk"><img src="${e.disk}"/></i>`:"")+(e.cube?`<i ${o}="cube"><img src="${e.cube}"/></i>`:"")+(e.image?`<i ${o}="image"><img src="${e.image}"/></i>`:"")+(e.label?`<i ${o}="label">${e.label}</i>`:"")+r;e.title&&(t.title=e.title),e.focusable&&(t.tabindex=1),e.wrapEl=createEl(e.nodeName||"span",Object.assign(t,e.attrs),n),e.iconEl=e.wrapEl.querySelector(`[${o}="icon"]`),e.cubeEl=e.wrapEl.querySelector(`[${o}="cube"]`),e.diskEl=e.wrapEl.querySelector(`[${o}="disk"]`),e.imageEl=e.wrapEl.querySelector(`[${o}="image"]`),e.labelEl=e.wrapEl.querySelector(`[${o}="label"]`),!isEmpty(e.classes)&&addClasses(e.wrapEl,e.classes),!isEmpty(e.styles)&&(e.wrapEl.style.cssText+=e.styles)};for(let r of e)renderFn(r),t.appendChild(r.wrapEl),r?.action?.(r);return t},typeWriter:(e,t)=>{const r=t.speed||100;return new Promise(n=>{t?.onBeforeType?.(e);let s=0;const o=setInterval(()=>{if(s<e.length){const r=e.charAt(s),n=e.substring(0,s+1);t?.onDuringType?.(r,n),s++}else clearInterval(o),n(e),t?.onAfterType?.(e)},r)})},parseLLMStream:async(e,t)=>{if(!(e&&e instanceof ReadableStream))throw new Error("Invalid input: ReadableStream is missing or not an instance of ReadableStream.");const r=e.getReader(),n=new TextDecoder("utf-8");let s="";const o={fullText:"",finishReason:null,usage:null,isCompleted:!1};try{for(;;){const{done:e,value:a}=await r.read();if(e)break;s+=n.decode(a,{stream:!0});let i=s.split("\n");s=i.pop()||"";for(const e of i){const r=e.trim();if(!r||!r.startsWith("data: "))continue;const n=r.substring(6);if("[DONE]"!==n)try{const e=JSON.parse(n),r=e.choices?.[0],s=r?.delta?.content||"";s&&(o.fullText+=s,t?.(s)),r?.finish_reason&&(o.finishReason=r.finish_reason),e.usage&&(o.usage=e.usage)}catch(e){}else o.isCompleted=!0}}}catch(e){throw e}return o},toKebabCase:(e,t="",r="")=>`${t}${e.replace(/([A-Z])/g,"-$1").toLowerCase()}${r}`,trimEmptyLines:e=>null==e?"":e.replace(/^\s*\n|\n\s*$/g,"")||"",decodeHtmlEntities:e=>{if(!e)return"";const t=document.createElement("textarea");return t.innerHTML=e,t.value},escapeCharsMaps:t,escapeRegexMaps:r,escapeHTML:escapeHTML,toSingleLine:toSingleLine,renderTpl:(e,t,r={})=>{if(requireTypes(e,"string",e=>""),!e.trim())return"";let n=requireTypes(t,["array","object"],t=>e);if(0===Object.keys(t).length)return e;let s,o=Object.assign({strict:!1,start:"{{",end:"}}",suffix:"/"},r),a=o.start.split("").map(e=>"\\"+e).join(""),i=o.end.split("").map(e=>"\\"+e).join(""),l=new RegExp(`${a}([\\s\\S]+?)?${i}`,"g"),c='"use strict";let str=[];\n',p=0,u="",d=`__esc__${getUniqueId()}`,add=(e,t)=>(t?e.endsWith(o.suffix)?c+=e.slice(0,-o.suffix.length)+"\n":c+=o.escape?`str.push(${d}(String(${e}), "${o.escape}"));\n`:`str.push(${e});\n`:c+=""!==e?'str.push("'+e.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r")+'");\n':"",add);for(;s=l.exec(e);)add(e.slice(p,s.index))(s[1],!0),p=s.index+s[0].length;add(e.slice(p)),c+="return str.join('');",c=toSingleLine(c);try{if(o.strict||"Array"===n)u=new Function(d,c).apply(t,[escapeHTML]);else{let e=Object.keys(t),r=Object.values(t);u=new Function(...e,d,c).bind(t)(...r,escapeHTML)}}catch(e){}return u},getBodyHTML:getBodyHTML,getUrlHash:getUrlHash,buildUrl:buildUrl,ajax:ajax,capitalize:capitalize,cleanQueryString:cleanQueryString,stringToEncodings:(e,t={})=>{const r=t.start??983040,n=t.end??1114109,s=BigInt(n-r+1),o=t.registryMap,formatResult=(e,t,r,n)=>{const s=t.toString(16).toUpperCase();return{name:e,unicode:`U+${s}`,htmlDec:`&#${t};`,htmlHex:`&#x${s};`,hex:s,codePoint:t,hash:r,collision:n}};let a=BigInt("0xcbf29ce484222325");const i=BigInt("0x100000001b3");for(const t of e)a^=BigInt(t.codePointAt(0)),a*=i;const l=a.toString(16).toUpperCase();if(!o){return formatResult(e,r+Number(a%s),l,!1)}if(o.has(e))return formatResult(e,o.get(e),l,!1);let c=Number(a%s),p=r+c,u=!1;const d=new Set(o.values());for(;d.has(p);)u=!0,c=(c+1)%Number(s),p=r+c;return o.set(e,p),formatResult(e,p,l,u)},unicodeToEncodings:e=>{let t;if("number"==typeof e)t=e;else{const r=e.trim().replace(/^U\+/i,"").replace(/^0x/i,"");t=/^[0-9A-F]+$/i.test(r)?parseInt(r,16):parseInt(r,10)}if(!Number.isFinite(t))throw new Error("Invalid Unicode input");const r=t.toString(16).toUpperCase();return{unicode:`U+${r}`,hex:r,codePoint:t,htmlDec:`&#${t};`,htmlHex:`&#x${r};`}}}});
package/dist.zip CHANGED
Binary file
@@ -0,0 +1,94 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>AJAX Download Progress Demo</title>
6
+ <style>
7
+ .download-card { border: 1px solid #e0e0e0; border-radius: 12px; padding: 20px; max-width: 500px; font-family: sans-serif; }
8
+ .progress-container { height: 10px; background: #eee; border-radius: 5px; margin: 15px 0; overflow: hidden; }
9
+ #progressBar { height: 100%; background: #2ecc71; width: 0%; transition: width 0.1s ease; }
10
+ .stats { display: flex; justify-content: space-between; font-size: 13px; color: #666; }
11
+ #statusText { font-weight: bold; margin-bottom: 5px; display: block; }
12
+ </style>
13
+ </head>
14
+ <body>
15
+ <h2>Demo 6: Large File Download</h2>
16
+
17
+ <div class="download-card">
18
+ <span id="statusText">Ready to download</span>
19
+ <div class="progress-container">
20
+ <div id="progressBar"></div>
21
+ </div>
22
+ <div class="stats">
23
+ <span id="percentText">0.00%</span>
24
+ <span id="sizeText">0 / 0 MB</span>
25
+ </div>
26
+ <hr>
27
+ <button id="btnDownload">Start 10MB Download</button>
28
+ <button id="btnAbort" disabled>Cancel</button>
29
+ </div>
30
+
31
+ <script src="../dist/utils.umd.js"></script>
32
+ <script>
33
+ let downloadCtrl = null;
34
+ const btnDownload = document.getElementById('btnDownload');
35
+ const btnAbort = document.getElementById('btnAbort');
36
+ const progressBar = document.getElementById('progressBar');
37
+ const percentText = document.getElementById('percentText');
38
+ const sizeText = document.getElementById('sizeText');
39
+ const statusText = document.getElementById('statusText');
40
+
41
+ btnDownload.onclick = () => {
42
+ // 重置 UI
43
+ progressBar.style.width = '0%';
44
+ btnDownload.disabled = true;
45
+ btnAbort.disabled = false;
46
+ statusText.innerText = "Connecting...";
47
+
48
+ downloadCtrl = utils.ajax({
49
+ // 使模拟 10 秒钟内持续吐出 1MB 的数据。
50
+ url: 'https://httpbin.org/drip?duration=10&numbytes=1000000&code=200',
51
+ method: 'GET',
52
+ precision: 2, // 演示你刚才要求的:保留两位小数
53
+ onCreated: (res) => {
54
+ downloadCtrl = res;
55
+ },
56
+ onDownload: (res) => {
57
+ const p = res.progress;
58
+ // 使用你建议的 text 字段(带补零)
59
+ progressBar.style.width = p.text + '%';
60
+ percentText.innerText = p.text + '%';
61
+
62
+ // 转换单位为 MB
63
+ const loadedMB = (p.loaded / 1024 / 1024).toFixed(2);
64
+ const totalMB = (p.total / 1024 / 1024).toFixed(2);
65
+ sizeText.innerText = `${loadedMB} / ${totalMB} MB`;
66
+ statusText.innerText = "Downloading...";
67
+ },
68
+ onSuccess: (res) => {
69
+ statusText.innerText = "Download Complete!";
70
+ statusText.style.color = "#2ecc71";
71
+ // 这里可以将 res.content (Blob) 转化为 URL 进行下载保存
72
+ // const url = window.URL.createObjectURL(res.content);
73
+ },
74
+ onAbort: () => {
75
+ statusText.innerText = "Download Canceled";
76
+ statusText.style.color = "#e67e22";
77
+ },
78
+ onFailure: (res) => {
79
+ statusText.innerText = "Download Failed";
80
+ statusText.style.color = "#e74c3c";
81
+ },
82
+ onFinish: () => {
83
+ btnDownload.disabled = false;
84
+ btnAbort.disabled = true;
85
+ }
86
+ });
87
+ };
88
+
89
+ btnAbort.onclick = () => {
90
+ downloadCtrl && downloadCtrl.abort();
91
+ };
92
+ </script>
93
+ </body>
94
+ </html>
@@ -0,0 +1,59 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <body>
4
+ <h2>Demo 1: GET / HEAD / TRACE</h2>
5
+ <button id="btnGet">GET Data (with params)</button>
6
+ <button id="btnSelector">GET with Selector (#main)</button>
7
+ <button id="btnSelector2">GET with Selector (#main)</button>
8
+ <button id="btnHead">HEAD (Headers Only)</button>
9
+ <div id="result" style="border:1px solid #ccc; padding:10px; margin-top:10px;">Results will appear here...</div>
10
+
11
+
12
+ <script src="../dist/utils.umd.js"></script>
13
+ <script >
14
+
15
+
16
+ // 1. GET with automatic params conversion
17
+ document.getElementById('btnGet').onclick = () => {
18
+ utils.ajax({
19
+ url: 'https://jsonplaceholder.typicode.com/posts',
20
+ method: 'GET',
21
+ data: { userId: 1, _limit: 3 } // Automatically becomes ?userId=1&_limit=3
22
+ }).then(res => {
23
+ document.getElementById('result').innerText = JSON.stringify(res.content, null, 2);
24
+ });
25
+ };
26
+
27
+ // 2. Selector Filter: Extract specific part of a page
28
+ document.getElementById('btnSelector').onclick = () => {
29
+ utils.ajax({
30
+ url: 'https://req.axui.cn/v3/html/his01.html', // In real use, this would be your own page
31
+ method: 'GET',
32
+ selector: '.post' // Only get the <h1> content
33
+ }).then(res => {
34
+ document.getElementById('result').innerHTML = `Extracted .post: ${res.content}`;
35
+ });
36
+ };
37
+
38
+ document.getElementById('btnSelector2').onclick = () => {
39
+ utils.ajax({
40
+ url: 'https://req.axui.cn/v3/html/his01.html#post03', // user hash
41
+ method: 'GET',
42
+ }).then(res => {
43
+ document.getElementById('result').innerHTML = `Extracted #post03: ${res.content}`;
44
+ });
45
+ };
46
+
47
+ // 3. HEAD request: No body, just metadata
48
+ document.getElementById('btnHead').onclick = () => {
49
+ utils.ajax({
50
+ url: 'https://jsonplaceholder.typicode.com/posts/1',
51
+ method: 'HEAD'
52
+ }).then(res => {
53
+ const contentType = res.xhr.getResponseHeader('Content-Type');
54
+ document.getElementById('result').innerText = `Status: ${res.status}\nContent-Type: ${contentType}`;
55
+ });
56
+ };
57
+ </script>
58
+ </body>
59
+ </html>
@@ -0,0 +1,55 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <body>
5
+ <h2>Demo 3: Lifecycle & Progress</h2>
6
+ <progress id="prog" value="0" max="100" style="width:100%"></progress>
7
+ <div id="status">Ready</div>
8
+ <button id="btnUpload">Upload Sim</button>
9
+ <button id="btnAbort">Abort Request</button>
10
+ <ul id="logs" style="font-family:monospace; font-size:12px;"></ul>
11
+
12
+
13
+ <script src="../dist/utils.umd.js"></script>
14
+ <script>
15
+ let currentReq = null;
16
+
17
+ document.getElementById('btnUpload').onclick = () => {
18
+ const logs = document.getElementById('logs');
19
+ logs.innerHTML = '';
20
+ const addLog = (txt) => logs.innerHTML += `<li>${txt}</li>`;
21
+
22
+ currentReq = utils.ajax({
23
+ url: 'https://httpbin.org/post',
24
+ method: 'POST',
25
+ data: new Array(100000).fill('data-chunk').join(''), // Large data for progress
26
+ onCreated: () => addLog('Lifecycle: Created'),
27
+ onHeadersReceived: () => addLog('Lifecycle: HeadersReceived'),
28
+ onLoading: () => addLog('Lifecycle: Loading'),
29
+ onBeforeSend: () => addLog('Lifecycle: BeforeSend'),
30
+ onOpened: () => addLog('Lifecycle: Opened'),
31
+ onUpload: (res) => {
32
+ document.getElementById('prog').value = res.progress.percent;
33
+ document.getElementById('status').innerText = `Uploading: ${res.progress.percent}%`;
34
+ console.log(res.progress.percent,res.progress.ratio,res.progress.text)
35
+ },
36
+ onSuccess: () => addLog('<b>Lifecycle: Success!</b>'),
37
+ onFinish: () => addLog('Lifecycle: Finished (Done)'),
38
+ onComplete: () => addLog('Lifecycle: Complete (100% Done)'),
39
+ onAbort: () => addLog('Lifecycle: Abort'),
40
+ onFailure: () => addLog('Lifecycle: Failure'),
41
+ onServerError: () => addLog('Lifecycle: ServerError'),
42
+ onClientError: () => addLog('Lifecycle: ClientError'),
43
+ onUnknownError: () => addLog('Lifecycle: UnknownError'),
44
+ onInformation: () => addLog('Lifecycle: Information'),
45
+ onRedirection: () => addLog('Lifecycle: Redirection'),
46
+ });
47
+ };
48
+
49
+ document.getElementById('btnAbort').onclick = () => {
50
+ currentReq && currentReq.abort();
51
+ };
52
+ </script>
53
+ </body>
54
+
55
+ </html>
@@ -0,0 +1,36 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <body>
5
+ <h2>Demo 4: Static Methods & All</h2>
6
+ <button id="btnAll">Fetch All (3 APIs)</button>
7
+ <div id="result"></div>
8
+
9
+
10
+ <script src="../dist/utils.umd.js"></script>
11
+ <script>
12
+ let currentReq = null;
13
+
14
+ // Using static ajax.all
15
+ document.getElementById('btnAll').onclick = async () => {
16
+ let elem = document.getElementById('result');
17
+ elem.innerText = 'Loading multiple...';
18
+
19
+ await utils.ajax.all([
20
+ { url: 'https://jsonplaceholder.typicode.com/posts/1', method: 'GET' },
21
+ { url: 'https://jsonplaceholder.typicode.com/posts/2', method: 'GET' },
22
+ { url: 'https://jsonplaceholder.typicode.com/posts/3', method: 'GET' }
23
+ ]).then(responses => {
24
+ const titles = responses.map(r => r.content.title);
25
+ elem.innerHTML = `
26
+ <h3>All Fetched:</h3>
27
+ <ul>${titles.map(t => `<li>${t}</li>`).join('')}</ul>
28
+ `;
29
+ });
30
+ elem.innerHTML += `<h3>Finish</h3>`
31
+
32
+ };
33
+ </script>
34
+ </body>
35
+
36
+ </html>
@@ -0,0 +1,37 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <body>
4
+ <h2>Demo 2: POST / PUT / DELETE</h2>
5
+ <button id="btnPostJson">POST JSON</button>
6
+ <button id="btnDelete">DELETE (Silent Error)</button>
7
+ <div id="result">Submit data to see result...</div>
8
+
9
+
10
+ <script src="../dist/utils.umd.js"></script>
11
+ <script >
12
+
13
+ // POST as JSON
14
+ document.getElementById('btnPostJson').onclick = () => {
15
+ utils.ajax({
16
+ url: 'https://jsonplaceholder.typicode.com/posts',
17
+ method: 'POST',
18
+ headers: { 'Content-Type': 'application/json' },
19
+ data: { title: 'Hello', body: 'World', userId: 1 }
20
+ }).then(res => {
21
+ document.getElementById('result').innerHTML = `<b>Success:</b> ${JSON.stringify(res.content)}`;
22
+ });
23
+ };
24
+
25
+ // DELETE with catchError: false (No red error in console if 404)
26
+ document.getElementById('btnDelete').onclick = () => {
27
+ utils.ajax({
28
+ url: 'https://jsonplaceholder.typicode.com/invalid-url',
29
+ method: 'DELETE',
30
+ catchError: false
31
+ }).then(res => {
32
+ document.getElementById('result').innerHTML = `Caught logic error without console red: ${res.type}`;
33
+ });
34
+ };
35
+ </script>
36
+ </body>
37
+ </html>
@@ -0,0 +1,91 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>AJAX AbortSignal Demo</title>
6
+ <style>
7
+ .box { border: 1px solid #ddd; padding: 20px; border-radius: 8px; margin-bottom: 20px; }
8
+ .req-item { margin: 5px 0; font-family: monospace; }
9
+ .status { font-weight: bold; }
10
+ .status-cancel { color: #faad14; }
11
+ .status-success { color: #52c41a; }
12
+ </style>
13
+ </head>
14
+ <body>
15
+ <h2>Demo 7: Control via AbortSignal</h2>
16
+
17
+ <div class="box">
18
+ <button id="btnGroupStart">Start Group Requests (3 Requests)</button>
19
+ <button id="btnCancelAll" style="color: red;" disabled>Cancel All via Signal</button>
20
+
21
+ <div id="results">
22
+ <div class="req-item">Request 1: <span id="s1" class="status">-</span></div>
23
+ <div class="req-item">Request 2: <span id="s2" class="status">-</span></div>
24
+ <div class="req-item">Request 3: <span id="s3" class="status">-</span></div>
25
+ </div>
26
+ </div>
27
+
28
+ <script src="../dist/utils.umd.js"></script>
29
+ <script>
30
+ const btnStart = document.getElementById('btnGroupStart');
31
+ const btnCancel = document.getElementById('btnCancelAll');
32
+
33
+ // 外部维护控制器
34
+ let controller = null;
35
+
36
+ btnStart.onclick = () => {
37
+ // 1. 创建新的控制器
38
+ controller = new AbortController();
39
+ const signal = controller.signal;
40
+
41
+ // UI 状态重置
42
+ [1, 2, 3].forEach(i => {
43
+ const el = document.getElementById('s' + i);
44
+ el.innerText = 'Pending...';
45
+ el.className = 'status';
46
+ });
47
+ btnStart.disabled = true;
48
+ btnCancel.disabled = false;
49
+
50
+ // 2. 同时发起 3 个请求,共享同一个 signal
51
+ [1, 2, 3].forEach(i => {
52
+ utils.ajax({
53
+ // 使用 delay 模拟长时间请求,方便我们有时间点取消
54
+ url: `https://httpbin.org/delay/${i + 2}`,
55
+ method: 'GET',
56
+ signal: signal, // <--- 关键参数:传入外部信号
57
+ onAbort: (data) => {
58
+ const el = document.getElementById('s' + i);
59
+ el.innerText = 'Canceled';
60
+ el.classList.add('status-cancel');
61
+ console.log('abort')
62
+ },
63
+ onSuccess: (data) => {
64
+ const el = document.getElementById('s' + i);
65
+ el.innerText = 'Success';
66
+ el.classList.add('status-success');
67
+ console.log('success')
68
+ },
69
+ onFinish: (data) => {
70
+ // 如果所有请求都结束了,重置按钮
71
+ if (i === 3) {
72
+ btnStart.disabled = false;
73
+ btnCancel.disabled = true;
74
+ }
75
+ console.log('finish')
76
+ }
77
+ });
78
+ });
79
+ };
80
+
81
+ // 3. 外部一键取消
82
+ btnCancel.onclick = () => {
83
+ if (controller) {
84
+ // 只要这一个指令,所有绑定了该 signal 的 ajax 请求都会立即停止
85
+ controller.abort();
86
+ console.log('Signal sent: All associated requests aborted.');
87
+ }
88
+ };
89
+ </script>
90
+ </body>
91
+ </html>
@@ -0,0 +1,85 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <body>
4
+ <h2>Demo 5: Timeout & Manual Abort</h2>
5
+
6
+ <div class="control-panel">
7
+ <p>Target: <code>/delay/10</code> (Server waits 10s before responding)</p>
8
+ <button id="btnTimeout">Start (3s Timeout)</button>
9
+
10
+ <button id="btnStartManual">Start (Manual Cancel)</button>
11
+ <button id="btnAbort" disabled style="color: red;">Abort Now!</button>
12
+ </div>
13
+
14
+ <h4>Execution Logs:</h4>
15
+ <ul id="logs"></ul>
16
+
17
+ <script src="../dist/utils.umd.js"></script>
18
+ <script >
19
+
20
+ const logs = document.getElementById('logs');
21
+ const btnAbort = document.getElementById('btnAbort');
22
+ let currentXHR = null;
23
+
24
+ const addLog = (msg, color = '') => {
25
+ const time = new Date().toLocaleTimeString().split(' ')[0];
26
+ const li = document.createElement('li');
27
+ li.innerHTML = `<span class="log-time">[${time}]</span> <span style="color:${color}">${msg}</span>`;
28
+ logs.appendChild(li);
29
+ logs.scrollTop = logs.scrollHeight;
30
+ };
31
+
32
+ // --- 场景 1: 自动超时 ---
33
+ document.getElementById('btnTimeout').onclick = () => {
34
+ addLog('Request started: Expecting timeout in 3s...', '#569cd6');
35
+
36
+ utils.ajax({
37
+ url: 'https://httpbin.org/delay/10',
38
+ method: 'GET',
39
+ timeout: 3000, // 3秒超时
40
+ onTimeout: (res) => {
41
+ addLog('HOOK: onTimeout triggered!', '#faad14');
42
+ },
43
+ onFailure: (res) => {
44
+ if (res.type === 'timeout') {
45
+ addLog('RESULT: Promise caught timeout error.', '#ff4d4f');
46
+ }
47
+ },
48
+ onFinish: () => addLog('Lifecycle: Finished.')
49
+ });
50
+ };
51
+
52
+ // --- 场景 2: 手动中止 ---
53
+ document.getElementById('btnStartManual').onclick = () => {
54
+ addLog('Request started: Use "Abort" button to cancel.', '#569cd6');
55
+ btnAbort.disabled = false;
56
+
57
+ utils.ajax({
58
+ url: 'https://httpbin.org/delay/10',
59
+ method: 'GET',
60
+ onCreated: (res) => {
61
+ // 获取内部的 abort 方法
62
+ currentXHR = res;
63
+ addLog('Lifecycle: Created (Abort function captured)');
64
+ },
65
+ onAbort: () => {
66
+ addLog('HOOK: onAbort triggered!', '#faad14');
67
+ },
68
+ onSuccess: () => addLog('Success! (If you see this, you were too slow to click abort)'),
69
+ onFinish: () => {
70
+ addLog('Lifecycle: Finished.');
71
+ btnAbort.disabled = true;
72
+ }
73
+ });
74
+ };
75
+
76
+ document.getElementById('btnAbort').onclick = () => {
77
+ if (currentXHR) {
78
+ currentXHR.abort(); // 调用封装好的 abort 方法
79
+ addLog('Action: .abort() called manually.', '#ff4d4f');
80
+ currentXHR = null;
81
+ }
82
+ };
83
+ </script>
84
+ </body>
85
+ </html>