1(function () { 2 // é²é夿§è¡ï¼utils.js å¯è½è¢«éå¤å è½½ï¼äºæ¬¡æ§è¡ç´æ¥è·³è¿ï¼é¿å éå¤å£°ææ¥éã 3 if (window.__stellarUtilsLoaded) return; 4 window.__stellarUtilsLoaded = true; 5 6 function RunItem() { 7 this.list = []; // åæ¾åè°å½æ° 8 this.start = () => { 9 for (var i = 0; i < this.list.length; i++) { 10 this.list[i].run(); 11 } 12 }; 13 this.push = (fn, name, setRequestAnimationFrame = true) => { 14 let myfn = fn 15 if (setRequestAnimationFrame) { 16 myfn = () => { 17 utils.requestAnimationFrame(fn) 18 } 19 } 20 var f = new Item(myfn, name); 21 this.list.push(f); 22 }; 23 this.remove = (name) => { 24 // ååºéåé¿å splice åçç´¢å¼é®é¢ 25 for (let index = this.list.length - 1; index >= 0; index--) { 26 const e = this.list[index]; 27 if (e.name === name) { 28 this.list.splice(index, 1); 29 } 30 } 31 } 32 // æé ä¸ä¸ªå¯ä»¥runç对象 33 function Item(fn, name) { 34 // 彿°åç§° 35 this.name = name || fn.name; 36 // runæ¹æ³ 37 this.run = () => { 38 try { 39 fn() 40 } catch (error) { 41 console.log(error); 42 } 43 }; 44 } 45 } 46 47 const utils = { 48 // å·²å è½½æ ·å¼ç¼å 49 _loadedStyles: new Set(), 50 51 // æå è½½ css https://github.com/filamentgroup/loadCSS 52 css: (href, before, media, attributes) => { 53 // å¦ææ ·å¼å·²å è½½ï¼ç´æ¥è¿å null 54 if (utils._loadedStyles.has(href)) { 55 return null; 56 } 57 var doc = window.document; 58 var ss = doc.createElement("link"); 59 var ref; 60 if (before) { 61 ref = before; 62 } else { 63 var refs = (doc.body || doc.getElementsByTagName("head")[0]).childNodes; 64 ref = refs[refs.length - 1]; 65 } 66 var sheets = doc.styleSheets; 67 if (attributes) { 68 for (var attributeName in attributes) { 69 if (Object.prototype.hasOwnProperty.call(attributes, attributeName)) { 70 ss.setAttribute(attributeName, attributes[attributeName]); 71 } 72 } 73 } 74 ss.rel = "stylesheet"; 75 ss.href = href; 76 ss.media = "only x"; 77 // æ è®°æ ·å¼ä¸ºå·²å è½½ (å¨å建å ç´ åç«å³æ è®°ï¼é²æ¢éå¤å建) 78 utils._loadedStyles.add(href); 79 function ready(cb) { 80 if (doc.body) { 81 return cb(); 82 } 83 setTimeout(function () { 84 ready(cb); 85 }); 86 } 87 ready(function () { 88 ref.parentNode.insertBefore(ss, before ? ref : ref.nextSibling); 89 }); 90 var onloadcssdefined = function (cb) { 91 var resolvedHref = ss.href; 92 var i = sheets.length; 93 while (i--) { 94 if (sheets[i].href === resolvedHref) { 95 return cb(); 96 } 97 } 98 setTimeout(function () { 99 onloadcssdefined(cb); 100 }); 101 }; 102 function loadCB() { 103 if (ss.addEventListener) { 104 ss.removeEventListener("load", loadCB); 105 } 106 ss.media = media || "all"; 107 } 108 if (ss.addEventListener) { 109 ss.addEventListener("load", loadCB); 110 } 111 ss.onloadcssdefined = onloadcssdefined; 112 onloadcssdefined(loadCB); 113 return ss; 114 }, 115 116 // å·²å è½½èæ¬ç¼å 117 _loadedScripts: new Set(), 118 119 // å·²å è½½å ç´ ç¼å (ä½¿ç¨ WeakSet 追踪å ç´ å®ä¾,é¿å éå¤å è½½) 120 _loadedElements: new WeakSet(), 121 122 js: (src, opt) => new Promise((resolve, reject) => { 123 if (src.startsWith('/')) { 124 src = ctx.root + src.substring(1); 125 } 126 // å¦æèæ¬å·²å è½½ï¼ç´æ¥è¿å 127 if (utils._loadedScripts.has(src)) { 128 resolve(); 129 return; 130 } 131 var script = document.createElement('script'); 132 script.src = src; 133 if (opt) { 134 for (let key of Object.keys(opt)) { 135 script[key] = opt[key] 136 } 137 } else { 138 // é»è®¤å¼æ¥ï¼å¦æéè¦åæ¥ï¼ç¬¬äºä¸ªåæ°ä¼ å ¥ {} å³å¯ 139 script.async = true 140 } 141 script.onerror = reject 142 script.onload = script.onreadystatechange = function () { 143 const loadState = this.readyState 144 if (loadState && loadState !== 'loaded' && loadState !== 'complete') return 145 script.onload = script.onreadystatechange = null 146 utils._loadedScripts.add(src); 147 resolve() 148 } 149 document.head.appendChild(script) 150 }), 151 152 // åç DOM å·¥å ·ï¼querySelector / querySelectorAll ç®å 153 qs: (sel, ctx) => (ctx || document).querySelector(sel), 154 qsa: (sel, ctx) => Array.prototype.slice.call((ctx || document).querySelectorAll(sel)), 155 156 // åç DOM å°è£ ï¼å¸¸ç¨ DOM æä½æ¹æ³åéï¼find/append/class/attr/äºä»¶çï¼ 157 dom: (selector, ctx) => { 158 var els = []; 159 if (typeof selector === 'string') { 160 els = utils.qsa(selector, ctx); 161 } else if (selector && selector.nodeType === 1) { 162 els = [selector]; 163 } else if (selector && typeof selector.length === 'number') { 164 els = Array.prototype.slice.call(selector); 165 } 166 var api = { 167 length: els.length, 168 each: function (fn) {
169 els.forEach(function (el, i) { 170 fn.call(el, i, el); 171 }); 172 return api; 173 }, 174 find: function (sel) { 175 var result = []; 176 els.forEach(function (el) { 177 Array.prototype.push.apply(result, el.querySelectorAll(sel)); 178 }); 179 return utils.dom(result); 180 }, 181 append: function (content) { 182 els.forEach(function (el) { 183 if (typeof content === 'string') { 184 el.insertAdjacentHTML('beforeend', content); 185 } else if (content && content.nodeType === 1) { 186 el.appendChild(content); 187 } else if (content && typeof content.length === 'number') { 188 Array.prototype.forEach.call(content, function (child) { 189 if (child && child.nodeType === 1) el.appendChild(child); 190 }); 191 } 192 }); 193 return api; 194 }, 195 remove: function () { 196 els.forEach(function (el) { 197 el.remove(); 198 }); 199 }, 200 addClass: function (cls) { 201 els.forEach(function (el) { 202 cls.trim().split(/\s+/).forEach(function (c) { 203 if (c) el.classList.add(c); 204 }); 205 }); 206 return api; 207 }, 208 removeClass: function (cls) { 209 els.forEach(function (el) { 210 cls.trim().split(/\s+/).forEach(function (c) { 211 if (c) el.classList.remove(c); 212 }); 213 }); 214 return api; 215 }, 216 toggleClass: function (cls, force) { 217 els.forEach(function (el) { 218 cls.trim().split(/\s+/).forEach(function (c) { 219 if (c) el.classList.toggle(c, force); 220 }); 221 }); 222 return api; 223 }, 224 attr: function (name, value) { 225 if (value === undefined) { 226 return els[0] ? els[0].getAttribute(name) : undefined; 227 } 228 els.forEach(function (el) { 229 el.setAttribute(name, value); 230 }); 231 return api; 232 }, 233 data: function (name) { 234 return els[0] ? els[0].getAttribute('data-' + name) : undefined; 235 }, 236 text: function (value) { 237 if (value === undefined) { 238 return els[0] ? els[0].textContent : undefined; 239 } 240 els.forEach(function (el) { 241 el.textContent = value; 242 }); 243 return api; 244 }, 245 html: function (content) { 246 if (content === undefined) { 247 return els[0] ? els[0].innerHTML : undefined; 248 } 249 els.forEach(function (el) { 250 if (typeof content !== 'string' && content && typeof content.length === 'number' && content[0] && content[0].nodeType === 1) { 251 var frag = document.createDocumentFragment(); 252 Array.prototype.forEach.call(content, function (child) { 253 frag.appendChild(child); 254 }); 255 el.replaceChildren(frag); 256 } else { 257 el.innerHTML = content; 258 } 259 }); 260 return api; 261 }, 262 val: function (value) { 263 if (value === undefined) { 264 return els[0] ? els[0].value : undefined; 265 } 266 els.forEach(function (el) { 267 el.value = value; 268 }); 269 return api; 270 }, 271 offset: function () { 272 var el = els[0]; 273 if (!el) return { top: 0, left: 0 }; 274 var rect = el.getBoundingClientRect(); 275 return { top: rect.top + window.scrollY, left: rect.left + window.scrollX }; 276 }, 277 on: function (event, cb) { 278 els.forEach(function (el) { 279 el.addEventListener(event, cb); 280 }); 281 return api; 282 }, 283 click: function (cb) { return api.on('click', cb); }, 284 focus: function (cb) { return api.on('focus', cb); }, 285 keydown: function (cb) { return api.on('keydown', cb); }, 286 empty: function () { 287 els.forEach(function (el) { 288 el.replaceChildren(); 289 }); 290 return api; 291 } 292 };
293 els.forEach(function (el, i) { 294 api[i] = el; 295 }); 296 return api; 297 }, 298 299 onLoading: (el) => { 300 if (el) { 301 if (el.querySelector('.loading-wrap') === null) { 302 el.insertAdjacentHTML('beforeend', `<div class="loading-wrap"><div class="lazy-icon"></div></div>`); 303 } 304 } 305 }, 306 onLoadSuccess: (el) => { 307 if (el) { 308 var wrap = el.querySelector('.loading-wrap'); 309 if (wrap) wrap.remove(); 310 } 311 }, 312 onLoadFailure: (el) => { 313 if (el) { 314 var wrap = el.querySelector('.loading-wrap'); 315 if (wrap) { 316 var svg = wrap.querySelector('svg'); 317 if (svg) svg.remove(); 318 wrap.insertAdjacentHTML('beforeend', ctx.icons['default:warning']); 319 wrap.classList.add('error'); 320 } 321 } 322 }, 323 /********************** data cache ********************************/ 324 // å¨ææ°æ®æ¬å°ç¼åï¼TTL æªè¿æç´æ¥å½ä¸ï¼è¿æåå æ¾ç¤ºç¼åååå°å·æ°ï¼stale-while-revalidateï¼ 325 _cacheEnabled: true, 326 _cacheMaxEntryBytes: 200 * 1024, 327 // å URL å¹¶å请æ±å»éï¼é¡µé¢å å¤ä¸ªç»ä»¶è¯·æ±å䏿¥å£æ¶å ±äº«ä¸æ¬¡ fetch 328 _pendingRequests: {}, 329 _fetchShared: (url, options) => { 330 const key = (options && options.method || 'GET') + ' ' + url; 331 if (utils._pendingRequests[key]) { 332 return utils._pendingRequests[key].then(resp => resp.clone()); 333 } 334 const promise = fetch(url, options).then(resp => { 335 if (!resp.ok) throw new Error('ååºå¤±è´¥'); 336 return resp; 337 }).finally(() => { 338 delete utils._pendingRequests[key]; 339 }); 340 utils._pendingRequests[key] = promise; 341 // æ¯ä¸ªè°ç¨æ¹åèª cloneï¼é¿å Response body 被æ¶è´¹åå ¶ä»è°ç¨æ¹åä¸å°æ°æ® 342 return promise.then(resp => resp.clone()); 343 }, 344 // å»¶è¿æ§è¡ï¼ç¼ååå ¥çéå ³é®æä½è®©åºä¸»çº¿ç¨ 345 _defer: (fn) => { 346 if (window.requestIdleCallback) { 347 window.requestIdleCallback(fn, { timeout: 3000 }); 348 } else { 349 setTimeout(fn, 0); 350 } 351 }, 352 cache: { 353 prefix: 'Stellar.data_cache.v1.', 354 enabled: () => !!def.data_cache?.enable && utils._cacheEnabled, 355 serviceId: (el, options) => { 356 if (options && options.service) return options.service; 357 if (el) { 358 const match = String(el.className || '').match(/\bds-([\w-]+)\b/); 359 if (match) return match[1]; 360 } 361 return null; 362 }, 363 ttl: (el, options) => { 364 const conf = def.data_cache || {}; 365 const id = utils.cache.serviceId(el, options); 366 const value = id && conf.ttl && conf.ttl[id] != null ? conf.ttl[id] : conf.default_ttl; 367 return typeof value === 'number' && value > 0 ? value : 0; 368 }, 369 shouldCache: (url, options) => { 370 if (!utils.cache.enabled()) return false; 371 if (options && (options.cache === false || options.cache === 'no-store')) return false; 372 if (options && options.method && options.method !== 'GET') return false; 373 // 带æ¶é´æ³ç¼åç ´ååæ°ç请æ±ä¸ç¼åï¼å¦ mdrender ç ?t=ï¼ 374 if (/[?&]t=\d{10,}/.test(url)) return false; 375 return true; 376 }, 377 get: (url) => { 378 try { 379 const raw = localStorage.getItem(utils.cache.prefix + url); 380 if (!raw) return null; 381 const entry = JSON.parse(raw); 382 if (!entry || typeof entry.text !== 'string' || typeof entry.ts !== 'number') return null; 383 return entry; 384 } catch (e) { 385 console.warn('[cache] 读å失败:', url, e); 386 return null; 387 } 388 }, 389 set: (url, text, contentType, ttl) => { 390 if (!(ttl > 0)) return; 391 if (text.length > utils._cacheMaxEntryBytes) return; 392 const entry = JSON.stringify({ 393 text: text, 394 contentType: contentType || 'application/json', 395 ts: Date.now(), 396 ttl: ttl 397 }); 398 const trySet = () => { 399 try { 400 localStorage.setItem(utils.cache.prefix + url, entry); 401 utils.cache.trim(); 402 return true; 403 } catch (e) { 404 return false;
405 } 406 }; 407 if (!trySet()) { 408 // é é¢ä¸è¶³æ¶æ·æ±°ææ§æ¡ç®åéè¯ä¸æ¬¡ï¼ä»å¤±è´¥åæ¬ä¼è¯ç¦ç¨ç¼å 409 utils.cache.evictOldest(url); 410 if (!trySet()) { 411 console.warn('[cache] åå ¥å¤±è´¥ï¼æ¬ä¼è¯ç¦ç¨ç¼å:', url); 412 utils._cacheEnabled = false; 413 } 414 } 415 }, 416 trim: () => { 417 try { 418 const max = (def.data_cache && def.data_cache.max_entries) || 200; 419 const keys = []; 420 for (let i = 0; i < localStorage.length; i++) { 421 const key = localStorage.key(i); 422 if (key && key.indexOf(utils.cache.prefix) === 0) keys.push(key); 423 } 424 if (keys.length <= max) return; 425 const sorted = keys.map((key) => { 426 let ts = 0; 427 try { 428 const entry = JSON.parse(localStorage.getItem(key) || 'null'); 429 if (entry && typeof entry.ts === 'number') ts = entry.ts; 430 } catch (e) {} 431 return { key, ts }; 432 }).sort((a, b) => a.ts - b.ts); 433 for (let i = 0; i < sorted.length - max; i++) { 434 localStorage.removeItem(sorted[i].key); 435 } 436 } catch (e) { 437 console.warn('[cache] æ¸ ç失败:', e); 438 } 439 }, 440 evictOldest: (exceptUrl) => { 441 try { 442 let oldestKey = null; 443 let oldestTs = Infinity; 444 const exceptKey = utils.cache.prefix + exceptUrl; 445 for (let i = 0; i < localStorage.length; i++) { 446 const key = localStorage.key(i); 447 if (!key || key.indexOf(utils.cache.prefix) !== 0 || key === exceptKey) continue; 448 let ts = 0; 449 try { 450 const entry = JSON.parse(localStorage.getItem(key) || 'null'); 451 if (entry && typeof entry.ts === 'number') ts = entry.ts; 452 } catch (e) {} 453 if (ts < oldestTs) { 454 oldestTs = ts; 455 oldestKey = key; 456 } 457 } 458 if (oldestKey) localStorage.removeItem(oldestKey); 459 } catch (e) {} 460 }, 461 isFresh: (entry) => { 462 if (!entry || typeof entry.ts !== 'number' || typeof entry.ttl !== 'number') return false; 463 if (!(entry.ttl > 0)) return false; 464 return Date.now() - entry.ts < entry.ttl * 1000; 465 }, 466 // ç¨ç¼åææ¬é建 Responseï¼å ¼å®¹åè°ä¸ç resp.json() / resp.text() 467 toResponse: (entry) => new Response(entry.text, { 468 status: 200, 469 statusText: 'OK', 470 headers: { 'Content-Type': entry.contentType } 471 }) 472 }, 473 request: (el, url, callback, onFailure, options) => { 474 // æ£æ¥å ç´ å®ä¾æ¯å¦å·²å è½½ (è䏿¯æ£æ¥å±æ§) 475 if (el && utils._loadedElements.has(el)) { 476 return; 477 } 478 const maxRetry = 3; 479 let retryCount = 0; 480 const ttl = utils.cache.ttl(el, options); 481 const cacheable = utils.cache.shouldCache(url, options) && ttl > 0; 482 const cached = cacheable ? utils.cache.get(url) : null; 483 // ç¼å渲æåçåå§ç»æï¼åå°å·æ°æ¸²æåæ¢å¤ï¼é¿å é夿¸²æ 484 let initialHTML = null; 485 // ç¼å渲æå®æåï¼æè¶ æ¶å åºï¼åå¯å¨åå°å·æ°ï¼é¿å 弿¥æ¸²ææªç»æå°±æ¸ 空éç» 486 let cacheRenderDone = null; 487 488 if (cached) { 489 if (el) { 490 // å·²æç¼åæ¶ä¸æ¾ç¤ºå è½½å¨ç» 491 utils.onLoadSuccess?.(el); 492 initialHTML = el.innerHTML; 493 } 494 try { 495 cacheRenderDone = Promise.resolve(callback(utils.cache.toResponse(cached))).catch(e => { 496 console.warn('[request] ç¼å渲æå¤±è´¥:', url, e); 497 }); 498 } catch (e) { 499 console.warn('[request] ç¼å渲æå¤±è´¥:', url, e); 500 } 501 // ç¼åæªè¿æï¼ç´æ¥å®æï¼ä¸åè¯·æ± 502 if (utils.cache.isFresh(cached)) { 503 if (el) utils._loadedElements.add(el); 504 return Promise.resolve(cached.text); 505 } 506 } else { 507 utils.onLoading?.(el); 508 } 509 510 return new Promise((resolve, reject) => { 511 const load = () => { 512 let timedOut = false;
513 const timeout = setTimeout(() => { 514 timedOut = true; 515 console.warn('[request] è¶ æ¶:', url); 516 517 if (++retryCount >= maxRetry) { 518 if (cached) { 519 // å·²æç¼å渲æï¼ä¿çç¼åå 容ï¼ä¸æ¾ç¤ºå¤±è´¥ 520 resolve(cached.text); 521 } else { 522 utils.onLoadFailure?.(el); 523 onFailure?.(); 524 reject('请æ±è¶ æ¶'); 525 } 526 } else { 527 setTimeout(load, 1000); 528 } 529 }, 5000); 530 531 utils._fetchShared(url).then(resp => { 532 if (timedOut) return; 533 clearTimeout(timeout); 534 return resp; 535 }).then(data => { 536 if (timedOut) return; 537 // æ è®°å ç´ å®ä¾ä¸ºå·²å è½½ 538 if (el) utils._loadedElements.add(el); 539 // åå ¥ç¼åï¼åè°ä¼ç«å³æ¶è´¹ data ç bodyï¼å¿ é¡»å 忥 clone åå»¶è¿è¯»å 540 if (cacheable && data.ok) { 541 const cacheClone = data.clone(); 542 const contentType = data.headers.get('Content-Type') || 'application/json'; 543 utils._defer(() => { 544 cacheClone.text().then(text => { 545 utils.cache.set(url, text, contentType, ttl); 546 }).catch(() => {}); 547 }); 548 } 549 // åå°å·æ°æ¸²æåæ¢å¤åå§ç»æï¼é¿å ç¼å渲æçå 容éå¤ 550 if (el && initialHTML !== null) { 551 el.innerHTML = initialHTML; 552 } 553 utils.onLoadSuccess?.(el); 554 callback(data); 555 resolve(data); 556 }).catch(err => { 557 clearTimeout(timeout); 558 console.warn('[request] é误:', err); 559 560 if (++retryCount >= maxRetry) { 561 if (cached) { 562 resolve(cached.text); 563 } else { 564 utils.onLoadFailure?.(el); 565 onFailure?.(); 566 reject(err); 567 } 568 } else { 569 setTimeout(load, 1000); 570 } 571 }); 572 }; 573 574 if (cacheRenderDone) { 575 Promise.race([ 576 cacheRenderDone, 577 new Promise(resolve => setTimeout(resolve, 5000)) 578 ]).then(load); 579 } else { 580 load(); 581 } 582 }); 583 }, 584 requestWithoutLoading: (url, options = {}, maxRetry = 2, timeout = 5000) => { 585 const ttl = utils.cache.ttl(null, options); 586 const cacheable = utils.cache.shouldCache(url, options) && ttl > 0; 587 const cached = cacheable ? utils.cache.get(url) : null; 588 589 if (cached && utils.cache.isFresh(cached)) { 590 return Promise.resolve(utils.cache.toResponse(cached)); 591 } 592 593 return new Promise((resolve, reject) => { 594 let retryCount = 0; 595 // ä¸ request ä¿æä¸è´ï¼æç»å¤±è´¥æ¶åé stale ç¼åï¼é¿å éªå¤±è´¥æ 596 const fallback = () => { 597 if (cached) { 598 resolve(utils.cache.toResponse(cached)); 599 } else { 600 reject('timeout'); 601 } 602 }; 603 604 const tryRequest = () => { 605 let timedOut = false; 606 const timer = setTimeout(() => { 607 timedOut = true; 608 if (++retryCount > maxRetry) fallback(); 609 else tryRequest(); 610 }, timeout); 611 612 utils._fetchShared(url, options) 613 .then(resp => { 614 clearTimeout(timer); 615 if (timedOut) return; 616 if (cacheable) { 617 // è°ç¨æ¹ä¼æ¶è´¹ resp ç bodyï¼å¿ é¡»å 忥 clone åå»¶è¿è¯»å 618 const cacheClone = resp.clone(); 619 const contentType = resp.headers.get('Content-Type') || 'application/json'; 620 utils._defer(() => { 621 cacheClone.text().then(text => { 622 utils.cache.set(url, text, contentType, ttl); 623 }).catch(() => {}); 624 }); 625 } 626 resolve(resp); 627 }) 628 .catch(err => { 629 clearTimeout(timer); 630 if (timedOut) return; 631 if (++retryCount > maxRetry) { 632 if (cached) { 633 resolve(utils.cache.toResponse(cached)); 634 } else { 635 reject(err); 636 } 637 } else { 638 setTimeout(tryRequest, 500); 639 } 640 }); 641 }; 642 643 tryRequest(); 644 }); 645 }, 646 /********************** requestAnimationFrame ********************************/ 647 // 1ãrequestAnimationFrame 伿æ¯ä¸å¸§ä¸çææ DOM æä½éä¸èµ·æ¥ï¼å¨ä¸æ¬¡éç»æåæµä¸å°±å®æï¼å¹¶ä¸éç»æåæµçæ¶é´é´éç´§ç´§è·éæµè§å¨çå·æ°é¢çï¼ä¸è¬æ¥è¯´ï¼è¿ä¸ªé¢ç为æ¯ç§60帧ã 648 // 2ãå¨éèæä¸å¯è§çå ç´ ä¸ï¼requestAnimationFrame å°ä¸ä¼è¿è¡éç»æåæµï¼è¿å½ç¶å°±æ
648å³çæ´å°çç cpuï¼gpu åå å使ç¨éã 649 requestAnimationFrame: (fn) => { 650 if (!window.requestAnimationFrame) { 651 window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame; 652 } 653 window.requestAnimationFrame(fn) 654 }, 655 dark: {}, 656 657 // æä»¶åå§å管çå¨ - ç»ä¸å¤ç DOMContentLoaded äºä»¶ 658 _pluginInitializers: [], 659 _pluginCleanups: new Map(), // å卿¯ä¸ªæä»¶çæ¸ ç彿° 660 661 initPlugin: (initFn, name, options = {}) => { 662 if (!initFn || typeof initFn !== 'function') return; 663 664 // é¿å é夿³¨å 665 const pluginName = name || initFn.name; 666 if (utils._pluginInitializers.some(p => p.name === pluginName)) return; 667 668 // å è£ åå§å彿°ï¼æ·»å é²é夿§è¡çä¿æ¤ 669 const wrappedInit = () => { 670 try { 671 // 卿§è¡åå æ¸ çæ§çèµæº 672 if (utils._pluginCleanups.has(pluginName)) { 673 const cleanup = utils._pluginCleanups.get(pluginName); 674 if (typeof cleanup === 'function') { 675 cleanup(); 676 } 677 } 678 679 // æ§è¡åå§åï¼å¯è½è¿åæ¸ ç彿° 680 const cleanup = initFn(); 681 682 // 妿åå§å彿°è¿åäºæ¸ ç彿°ï¼ä¿åå® 683 if (typeof cleanup === 'function') { 684 utils._pluginCleanups.set(pluginName, cleanup); 685 } 686 } catch (error) { 687 console.error(`[Plugin ${pluginName}] åå§å失败:`, error); 688 } 689 }; 690 691 utils._pluginInitializers.push({ 692 fn: wrappedInit, 693 name: pluginName, 694 options: options 695 }); 696 697 // 妿 DOM å·²ç»å è½½å®æï¼ç«å³æ§è¡ 698 if (document.readyState === 'complete' || document.readyState === 'interactive') { 699 wrappedInit(); 700 } else { 701 // å¦åçå¾ DOMContentLoaded 702 window.addEventListener('DOMContentLoaded', wrappedInit, { once: true }); 703 } 704 }, 705 706 // æ¸ çæææä»¶èµæº 707 cleanupPlugins: () => { 708 utils._pluginCleanups.forEach((cleanup, name) => { 709 try { 710 cleanup(); 711 } catch (error) { 712 console.error(`[Plugin ${name}] æ¸ ç失败:`, error); 713 } 714 }); 715 utils._pluginCleanups.clear(); 716 }, 717 718 // æ¸ çææèµæºï¼ç¨äºé¡µé¢å¸è½½æéç½®ï¼ 719 cleanupAll: () => { 720 utils.cleanupPlugins(); 721 }, 722 }; 723 724 // utils.dark.mode å½åæ¨¡å¼ dark or light 725 // utils.dark.toggle() æé»æ¨¡å¼è§¦åå¨ 726 // utils.dark.push(callBack[,"callBackName"]) ä¼ å ¥è§¦åå¨åè°å½æ° 727 utils.dark.method = { 728 toggle: new RunItem(), 729 }; 730 utils.dark = Object.assign(utils.dark, { 731 push: utils.dark.method.toggle.push, 732 }); 733 734 // æ´é²å° windowï¼é¡µå°¾æä»¶ç段ç»è£¸æ è¯ç¬¦ utils å³å¯ä½¿ç¨ 735 window.utils = utils; 736 737 // è¡¥è·è§£æææ³¨åçæä»¶éå 738 if (window.stellar && typeof window.stellar._flushPlugins === 'function') { 739 window.stellar._flushPlugins(); 740 } 741})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.