PageSourceSearch

https://janiszzz.github.io/js/utils.js?v=1.44.0

js janiszzz.github.io collected 2026-10-03 09:22:10 UTC 25,587 bytes, 741 lines download raw bytes

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.