@pluve/logger-sdk 0.0.7 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.html +681 -0
- package/README.md +131 -153
- package/dist/cjs/capture/jsError.js +48 -0
- package/dist/cjs/capture/promiseError.js +49 -0
- package/dist/cjs/capture/resourceError.js +48 -0
- package/dist/cjs/capture/wechatError.js +95 -0
- package/dist/cjs/compress/compression.js +84 -0
- package/dist/cjs/config/index.js +55 -0
- package/dist/cjs/core/fingerprint.js +36 -0
- package/dist/cjs/core/httpClient.js +96 -0
- package/dist/cjs/core/loggerSDK.js +641 -0
- package/dist/cjs/core/queueManager.js +249 -0
- package/dist/cjs/core/retryManager.js +127 -0
- package/dist/cjs/index.js +29 -0
- package/dist/cjs/stack/stacktrace.js +48 -0
- package/dist/cjs/transport/beaconTransport.js +64 -0
- package/dist/cjs/transport/pixelImageTransport.js +100 -0
- package/dist/cjs/transport/transport.js +17 -0
- package/dist/cjs/transport/transportAdapter.js +56 -0
- package/dist/cjs/transport/wechatTransport.js +103 -0
- package/dist/cjs/types/api.js +17 -0
- package/dist/cjs/types/env.js +17 -0
- package/dist/cjs/types/external.d.ts +21 -0
- package/dist/cjs/types/logEvent.js +17 -0
- package/dist/cjs/types/logEventLevel.js +17 -0
- package/dist/cjs/types/sdkOptions.js +17 -0
- package/dist/cjs/types/trackOptions.js +17 -0
- package/dist/cjs/utils/environment.js +183 -0
- package/dist/cjs/utils/session.js +31 -0
- package/dist/cjs/utils/tools.js +82 -0
- package/dist/cjs/utils/uuid.js +35 -0
- package/dist/esm/capture/jsError.js +45 -0
- package/dist/esm/capture/promiseError.js +46 -0
- package/dist/esm/capture/resourceError.js +24 -0
- package/dist/esm/capture/wechatError.js +92 -0
- package/dist/esm/compress/compression.js +82 -0
- package/dist/esm/config/index.js +28 -0
- package/dist/esm/core/fingerprint.js +12 -0
- package/dist/esm/core/httpClient.js +95 -0
- package/dist/esm/core/loggerSDK.js +650 -0
- package/dist/esm/core/queueManager.js +269 -0
- package/dist/esm/core/retryManager.js +129 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/stack/stacktrace.js +37 -0
- package/dist/esm/transport/beaconTransport.js +81 -0
- package/dist/esm/transport/pixelImageTransport.js +99 -0
- package/dist/esm/transport/transport.js +0 -0
- package/dist/esm/transport/transportAdapter.js +32 -0
- package/dist/esm/transport/wechatTransport.js +120 -0
- package/dist/esm/types/api.js +0 -0
- package/dist/esm/types/env.js +0 -0
- package/dist/esm/types/external.d.ts +21 -0
- package/dist/esm/types/logEvent.js +0 -0
- package/dist/esm/types/logEventLevel.js +0 -0
- package/dist/esm/types/sdkOptions.js +0 -0
- package/dist/esm/types/trackOptions.js +0 -0
- package/dist/esm/utils/environment.js +154 -0
- package/dist/esm/utils/session.js +7 -0
- package/dist/esm/utils/tools.js +76 -0
- package/dist/esm/utils/uuid.js +11 -0
- package/dist/types/capture/jsError.d.ts +2 -0
- package/dist/types/capture/promiseError.d.ts +2 -0
- package/dist/types/capture/resourceError.d.ts +2 -0
- package/dist/types/capture/wechatError.d.ts +3 -0
- package/dist/types/compress/compression.d.ts +10 -0
- package/dist/types/config/index.d.ts +9 -0
- package/dist/types/core/fingerprint.d.ts +8 -0
- package/dist/types/core/httpClient.d.ts +11 -0
- package/dist/{loggerSDK.d.ts → types/core/loggerSDK.d.ts} +27 -2
- package/dist/{queueManager.d.ts → types/core/queueManager.d.ts} +9 -1
- package/dist/{retryManager.d.ts → types/core/retryManager.d.ts} +0 -4
- package/dist/types/index.d.ts +1 -0
- package/dist/types/stack/stacktrace.d.ts +2 -0
- package/dist/types/transport/beaconTransport.d.ts +11 -0
- package/dist/types/transport/pixelImageTransport.d.ts +11 -0
- package/dist/types/transport/transport.d.ts +14 -0
- package/dist/types/transport/transportAdapter.d.ts +10 -0
- package/dist/types/transport/wechatTransport.d.ts +11 -0
- package/dist/types/types/api.d.ts +12 -0
- package/dist/types/types/env.d.ts +14 -0
- package/dist/types/types/logEvent.d.ts +57 -0
- package/dist/types/types/logEventLevel.d.ts +2 -0
- package/dist/{types.d.ts → types/types/sdkOptions.d.ts} +23 -42
- package/dist/types/types/trackOptions.d.ts +7 -0
- package/dist/types/utils/environment.d.ts +21 -0
- package/dist/types/utils/session.d.ts +1 -0
- package/dist/types/utils/tools.d.ts +12 -0
- package/dist/types/utils/uuid.d.ts +7 -0
- package/dist/umd/logger-sdk.min.js +1 -0
- package/package.json +29 -5
- package/dist/index.d.ts +0 -10
- package/dist/index.js +0 -13
- package/dist/loggerSDK.js +0 -560
- package/dist/queueManager.js +0 -186
- package/dist/retryManager.js +0 -224
- package/dist/transportAdapter.d.ts +0 -51
- package/dist/transportAdapter.js +0 -315
- package/dist/types.js +0 -1
- package/dist/utils.d.ts +0 -52
- package/dist/utils.js +0 -348
package/README.html
ADDED
|
@@ -0,0 +1,681 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head>
|
|
3
|
+
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="generator" content="quarto-1.8.26">
|
|
6
|
+
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
<title>readme</title>
|
|
11
|
+
<style>
|
|
12
|
+
code{white-space: pre-wrap;}
|
|
13
|
+
span.smallcaps{font-variant: small-caps;}
|
|
14
|
+
div.columns{display: flex; gap: min(4vw, 1.5em);}
|
|
15
|
+
div.column{flex: auto; overflow-x: auto;}
|
|
16
|
+
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
|
17
|
+
ul.task-list{list-style: none;}
|
|
18
|
+
ul.task-list li input[type="checkbox"] {
|
|
19
|
+
width: 0.8em;
|
|
20
|
+
margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */
|
|
21
|
+
vertical-align: middle;
|
|
22
|
+
}
|
|
23
|
+
/* CSS for syntax highlighting */
|
|
24
|
+
html { -webkit-text-size-adjust: 100%; }
|
|
25
|
+
pre > code.sourceCode { white-space: pre; position: relative; }
|
|
26
|
+
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
|
27
|
+
pre > code.sourceCode > span:empty { height: 1.2em; }
|
|
28
|
+
.sourceCode { overflow: visible; }
|
|
29
|
+
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
|
30
|
+
div.sourceCode { margin: 1em 0; }
|
|
31
|
+
pre.sourceCode { margin: 0; }
|
|
32
|
+
@media screen {
|
|
33
|
+
div.sourceCode { overflow: auto; }
|
|
34
|
+
}
|
|
35
|
+
@media print {
|
|
36
|
+
pre > code.sourceCode { white-space: pre-wrap; }
|
|
37
|
+
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
|
38
|
+
}
|
|
39
|
+
pre.numberSource code
|
|
40
|
+
{ counter-reset: source-line 0; }
|
|
41
|
+
pre.numberSource code > span
|
|
42
|
+
{ position: relative; left: -4em; counter-increment: source-line; }
|
|
43
|
+
pre.numberSource code > span > a:first-child::before
|
|
44
|
+
{ content: counter(source-line);
|
|
45
|
+
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
|
46
|
+
border: none; display: inline-block;
|
|
47
|
+
-webkit-touch-callout: none; -webkit-user-select: none;
|
|
48
|
+
-khtml-user-select: none; -moz-user-select: none;
|
|
49
|
+
-ms-user-select: none; user-select: none;
|
|
50
|
+
padding: 0 4px; width: 4em;
|
|
51
|
+
}
|
|
52
|
+
pre.numberSource { margin-left: 3em; padding-left: 4px; }
|
|
53
|
+
div.sourceCode
|
|
54
|
+
{ }
|
|
55
|
+
@media screen {
|
|
56
|
+
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
|
57
|
+
}
|
|
58
|
+
</style>
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
<script src="README_files/libs/clipboard/clipboard.min.js"></script>
|
|
62
|
+
<script src="README_files/libs/quarto-html/quarto.js" type="module"></script>
|
|
63
|
+
<script src="README_files/libs/quarto-html/tabsets/tabsets.js" type="module"></script>
|
|
64
|
+
<script src="README_files/libs/quarto-html/axe/axe-check.js" type="module"></script>
|
|
65
|
+
<script src="README_files/libs/quarto-html/popper.min.js"></script>
|
|
66
|
+
<script src="README_files/libs/quarto-html/tippy.umd.min.js"></script>
|
|
67
|
+
<script src="README_files/libs/quarto-html/anchor.min.js"></script>
|
|
68
|
+
<link href="README_files/libs/quarto-html/tippy.css" rel="stylesheet">
|
|
69
|
+
<link href="README_files/libs/quarto-html/quarto-syntax-highlighting-587c61ba64f3a5504c4d52d930310e48.css" rel="stylesheet" id="quarto-text-highlighting-styles">
|
|
70
|
+
<script src="README_files/libs/bootstrap/bootstrap.min.js"></script>
|
|
71
|
+
<link href="README_files/libs/bootstrap/bootstrap-icons.css" rel="stylesheet">
|
|
72
|
+
<link href="README_files/libs/bootstrap/bootstrap-d6a003b94517c951b2d65075d42fb01b.min.css" rel="stylesheet" append-hash="true" id="quarto-bootstrap" data-mode="light">
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
</head>
|
|
76
|
+
|
|
77
|
+
<body class="fullcontent quarto-light">
|
|
78
|
+
|
|
79
|
+
<div id="quarto-content" class="page-columns page-rows-contents page-layout-article">
|
|
80
|
+
|
|
81
|
+
<main class="content" id="quarto-document-content">
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
<section id="logger-sdk-v2-说明文档" class="level1">
|
|
87
|
+
<h1>Logger SDK v2 说明文档</h1>
|
|
88
|
+
<section id="概览" class="level2">
|
|
89
|
+
<h2 class="anchored" data-anchor-id="概览">概览</h2>
|
|
90
|
+
<ul>
|
|
91
|
+
<li>跨环境支持:浏览器与微信小程序</li>
|
|
92
|
+
<li>采集与采样:开关、日志等级白名单、分级/来源/路径采样率</li>
|
|
93
|
+
<li>传输适配:Beacon、像素图 Image、微信 <code>wx.request</code></li>
|
|
94
|
+
<li>批量与队列:批量阈值与定时器、队列持久化(localforage)</li>
|
|
95
|
+
<li>重试策略:可配置最大次数与指数退避</li>
|
|
96
|
+
<li>压缩能力:可选 gzip 压缩,优先使用浏览器 CompressionStream,回退 fflate.gzipSync</li>
|
|
97
|
+
</ul>
|
|
98
|
+
</section>
|
|
99
|
+
<section id="快速开始" class="level2">
|
|
100
|
+
<h2 class="anchored" data-anchor-id="快速开始">快速开始</h2>
|
|
101
|
+
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1"><pre class="sourceCode ts code-with-copy"><code class="sourceCode typescript"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> { LoggerSDK } <span class="im">from</span> <span class="st">'./core/loggerSDK'</span><span class="op">;</span></span>
|
|
102
|
+
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
|
|
103
|
+
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> sdk <span class="op">=</span> LoggerSDK<span class="op">.</span><span class="fu">getInstance</span>()<span class="op">;</span></span>
|
|
104
|
+
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a></span>
|
|
105
|
+
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>sdk<span class="op">.</span><span class="fu">init</span>({</span>
|
|
106
|
+
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a> <span class="co">// endpoint: 'https://logger.yfpharmacy.com/log', // 实际使用以 config 中 API 为准</span></span>
|
|
107
|
+
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a> appId<span class="op">:</span> <span class="st">'fe-vue-pc-seed-frontend'</span><span class="op">,</span></span>
|
|
108
|
+
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a> env<span class="op">:</span> <span class="st">'develop'</span><span class="op">,</span></span>
|
|
109
|
+
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a> logStage<span class="op">:</span> <span class="st">'develop'</span><span class="op">,</span></span>
|
|
110
|
+
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a> debug<span class="op">:</span> <span class="kw">true</span><span class="op">,</span></span>
|
|
111
|
+
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a> <span class="co">// 采集与性能</span></span>
|
|
112
|
+
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a> enableGzip<span class="op">:</span> <span class="kw">true</span><span class="op">,</span></span>
|
|
113
|
+
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a> enableBatch<span class="op">:</span> <span class="kw">true</span><span class="op">,</span></span>
|
|
114
|
+
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a> batchSize<span class="op">:</span> <span class="dv">10</span><span class="op">,</span></span>
|
|
115
|
+
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a> batchInterval<span class="op">:</span> <span class="dv">15000</span><span class="op">,</span></span>
|
|
116
|
+
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a> maxPixelUrlLen<span class="op">:</span> <span class="dv">8192</span><span class="op">,</span></span>
|
|
117
|
+
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a> <span class="co">// 重试</span></span>
|
|
118
|
+
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a> enableRetry<span class="op">:</span> <span class="kw">true</span><span class="op">,</span></span>
|
|
119
|
+
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a> maxRetries<span class="op">:</span> <span class="dv">3</span><span class="op">,</span></span>
|
|
120
|
+
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a> retryDelay<span class="op">:</span> <span class="dv">1000</span><span class="op">,</span></span>
|
|
121
|
+
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a> retryBackoff<span class="op">:</span> <span class="kw">true</span><span class="op">,</span></span>
|
|
122
|
+
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a> <span class="co">// 身份与鉴权</span></span>
|
|
123
|
+
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a> userId<span class="op">:</span> <span class="st">'u-001'</span><span class="op">,</span></span>
|
|
124
|
+
<span id="cb1-24"><a href="#cb1-24" aria-hidden="true" tabindex="-1"></a> storeCode<span class="op">:</span> <span class="st">'s-001'</span><span class="op">,</span></span>
|
|
125
|
+
<span id="cb1-25"><a href="#cb1-25" aria-hidden="true" tabindex="-1"></a> token<span class="op">:</span> <span class="st">'<token>'</span><span class="op">,</span></span>
|
|
126
|
+
<span id="cb1-26"><a href="#cb1-26" aria-hidden="true" tabindex="-1"></a> <span class="co">// 采样率(可选)</span></span>
|
|
127
|
+
<span id="cb1-27"><a href="#cb1-27" aria-hidden="true" tabindex="-1"></a> sampleRate<span class="op">:</span> <span class="dv">1</span><span class="op">,</span></span>
|
|
128
|
+
<span id="cb1-28"><a href="#cb1-28" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span>
|
|
129
|
+
<span id="cb1-29"><a href="#cb1-29" aria-hidden="true" tabindex="-1"></a></span>
|
|
130
|
+
<span id="cb1-30"><a href="#cb1-30" aria-hidden="true" tabindex="-1"></a><span class="co">// 业务日志</span></span>
|
|
131
|
+
<span id="cb1-31"><a href="#cb1-31" aria-hidden="true" tabindex="-1"></a>sdk<span class="op">.</span><span class="fu">track</span>({</span>
|
|
132
|
+
<span id="cb1-32"><a href="#cb1-32" aria-hidden="true" tabindex="-1"></a> message<span class="op">:</span> <span class="st">'用户点击了购买按钮'</span><span class="op">,</span></span>
|
|
133
|
+
<span id="cb1-33"><a href="#cb1-33" aria-hidden="true" tabindex="-1"></a> traceId<span class="op">:</span> <span class="st">'trace-xyz'</span><span class="op">,</span></span>
|
|
134
|
+
<span id="cb1-34"><a href="#cb1-34" aria-hidden="true" tabindex="-1"></a> logLevel<span class="op">:</span> <span class="st">'INFO'</span><span class="op">,</span></span>
|
|
135
|
+
<span id="cb1-35"><a href="#cb1-35" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span>
|
|
136
|
+
<span id="cb1-36"><a href="#cb1-36" aria-hidden="true" tabindex="-1"></a></span>
|
|
137
|
+
<span id="cb1-37"><a href="#cb1-37" aria-hidden="true" tabindex="-1"></a><span class="co">// 动态设置</span></span>
|
|
138
|
+
<span id="cb1-38"><a href="#cb1-38" aria-hidden="true" tabindex="-1"></a>sdk<span class="op">.</span><span class="fu">identify</span>(<span class="st">'u-002'</span>)<span class="op">;</span></span>
|
|
139
|
+
<span id="cb1-39"><a href="#cb1-39" aria-hidden="true" tabindex="-1"></a>sdk<span class="op">.</span><span class="fu">setStoreCode</span>(<span class="st">'s-002'</span>)<span class="op">;</span></span>
|
|
140
|
+
<span id="cb1-40"><a href="#cb1-40" aria-hidden="true" tabindex="-1"></a>sdk<span class="op">.</span><span class="fu">setStage</span>(<span class="st">'testing'</span>)<span class="op">;</span></span></code></pre></div><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></div>
|
|
141
|
+
</section>
|
|
142
|
+
<section id="配置项节选" class="level2">
|
|
143
|
+
<h2 class="anchored" data-anchor-id="配置项节选">配置项(节选)</h2>
|
|
144
|
+
<ul>
|
|
145
|
+
<li>enableGzip:是否启用 gzip 压缩(默认 true)</li>
|
|
146
|
+
<li>enableBatch:是否启用批量上报(默认需显式开启)</li>
|
|
147
|
+
<li>batchSize:批量阈值(默认 10)</li>
|
|
148
|
+
<li>batchInterval:批量定时器间隔(默认 15000ms)</li>
|
|
149
|
+
<li>maxPixelUrlLen:像素图 URL 最大长度(默认 8192)</li>
|
|
150
|
+
<li>enableStorage:队列持久化开关(默认 true)</li>
|
|
151
|
+
<li>enableRetry/maxRetries/retryDelay/retryBackoff:重试相关配置</li>
|
|
152
|
+
<li>sampleRate/levelSampleRate/sourceSampleRate/pathSampleRate:采样配置</li>
|
|
153
|
+
</ul>
|
|
154
|
+
<p>详细定义参考类型文件:<a href="file:///Users/ddg-dany/work/yf-gitlab/frontend-common/yf-frontend-scaffold/packages/vue-pc-seed/src/pages/logger-sdk-tester/logger-sdk-v2/types/sdkOptions.ts">sdkOptions.ts</a>.</p>
|
|
155
|
+
</section>
|
|
156
|
+
<section id="压缩实现" class="level2">
|
|
157
|
+
<h2 class="anchored" data-anchor-id="压缩实现">压缩实现</h2>
|
|
158
|
+
<ul>
|
|
159
|
+
<li>gzipCompress(data)
|
|
160
|
+
<ul>
|
|
161
|
+
<li>浏览器支持 CompressionStream 时,流式 gzip 压缩,再转 Base64 字符串</li>
|
|
162
|
+
<li>不支持时,回退到 fflate.gzipSync(默认压缩级别),转 Base64</li>
|
|
163
|
+
<li>返回值为 Base64 字符串,适用于传输(像素图 URL/JSON 体)</li>
|
|
164
|
+
</ul></li>
|
|
165
|
+
<li>代码参考:<a href="file:///Users/ddg-dany/work/yf-gitlab/frontend-common/yf-frontend-scaffold/packages/vue-pc-seed/src/pages/logger-sdk-tester/logger-sdk-v2/compress/compression.ts">compression.ts</a>.</li>
|
|
166
|
+
<li>备注:当前默认压缩级别,无级别可调参数暴露;如需级别控制可在 fflate 路径引入配置。</li>
|
|
167
|
+
</ul>
|
|
168
|
+
</section>
|
|
169
|
+
<section id="传输适配" class="level2">
|
|
170
|
+
<h2 class="anchored" data-anchor-id="传输适配">传输适配</h2>
|
|
171
|
+
<ul>
|
|
172
|
+
<li>TransportAdapter 根据环境选择传输器:
|
|
173
|
+
<ul>
|
|
174
|
+
<li>微信:WechatTransport(wx.request POST,支持 gzip)</li>
|
|
175
|
+
<li>浏览器:优先 BeaconTransport(sendBeacon Blob),否则 PixelImageTransport(Image GET)</li>
|
|
176
|
+
</ul></li>
|
|
177
|
+
<li>WechatTransport
|
|
178
|
+
<ul>
|
|
179
|
+
<li>请求体字符串化 safeStringify</li>
|
|
180
|
+
<li>enableGzip 时使用 gzipCompress 生成 Base64,Content-Type 设为 <code>application/json; charset=utf-8</code></li>
|
|
181
|
+
</ul></li>
|
|
182
|
+
<li>PixelImageTransport
|
|
183
|
+
<ul>
|
|
184
|
+
<li>字符串化 items,enableGzip 时压缩为 Base64,URL 参数附带 <code>gzip=1</code></li>
|
|
185
|
+
<li>构造像素上报 URL(含 appId、appStage、items、cacheBuster)</li>
|
|
186
|
+
<li>URL 超长打印警告</li>
|
|
187
|
+
</ul></li>
|
|
188
|
+
<li>BeaconTransport
|
|
189
|
+
<ul>
|
|
190
|
+
<li>使用 Blob 指定 Content-Type,通过 <code>navigator.sendBeacon</code> 发送</li>
|
|
191
|
+
</ul></li>
|
|
192
|
+
</ul>
|
|
193
|
+
<p>代码参考:</p>
|
|
194
|
+
<ul>
|
|
195
|
+
<li>适配器选择:<a href="file:///Users/ddg-dany/work/yf-gitlab/frontend-common/yf-frontend-scaffold/packages/vue-pc-seed/src/pages/logger-sdk-tester/logger-sdk-v2/transport/transportAdapter.ts">transportAdapter.ts</a></li>
|
|
196
|
+
<li>WeChat:<a href="file:///Users/ddg-dany/work/yf-gitlab/frontend-common/yf-frontend-scaffold/packages/vue-pc-seed/src/pages/logger-sdk-tester/logger-sdk-v2/transport/wechatTransport.ts">wechatTransport.ts</a></li>
|
|
197
|
+
<li>Pixel Image:<a href="file:///Users/ddg-dany/work/yf-gitlab/frontend-common/yf-frontend-scaffold/packages/vue-pc-seed/src/pages/logger-sdk-tester/logger-sdk-v2/transport/pixelImageTransport.ts">pixelImageTransport.ts</a></li>
|
|
198
|
+
<li>Beacon:<a href="file:///Users/ddg-dany/work/yf-gitlab/frontend-common/yf-frontend-scaffold/packages/vue-pc-seed/src/pages/logger-sdk-tester/logger-sdk-v2/transport/beaconTransport.ts">beaconTransport.ts</a></li>
|
|
199
|
+
</ul>
|
|
200
|
+
</section>
|
|
201
|
+
<section id="批量与队列" class="level2">
|
|
202
|
+
<h2 class="anchored" data-anchor-id="批量与队列">批量与队列</h2>
|
|
203
|
+
<ul>
|
|
204
|
+
<li>启用批量后,事件入队,当队列大小达到 batchSize 或定时器触发时 flush</li>
|
|
205
|
+
<li>flush:按 appId|stage 分组与分块(最多 200/块),批量发送成功后出队</li>
|
|
206
|
+
<li>队列持久化:localforage 存储,页面刷新/卸载可恢复</li>
|
|
207
|
+
<li>页面事件触发 flush:visibilitychange(hidden)、pagehide、beforeunload</li>
|
|
208
|
+
<li>代码参考:
|
|
209
|
+
<ul>
|
|
210
|
+
<li><a href="file:///Users/ddg-dany/work/yf-gitlab/frontend-common/yf-frontend-scaffold/packages/vue-pc-seed/src/pages/logger-sdk-tester/logger-sdk-v2/core/queueManager.ts">queueManager.ts</a></li>
|
|
211
|
+
<li><a href="file:///Users/ddg-dany/work/yf-gitlab/frontend-common/yf-frontend-scaffold/packages/vue-pc-seed/src/pages/logger-sdk-tester/logger-sdk-v2/core/loggerSDK.ts#L452-L485">loggerSDK.ts(flush/sendBatch)</a></li>
|
|
212
|
+
</ul></li>
|
|
213
|
+
</ul>
|
|
214
|
+
</section>
|
|
215
|
+
<section id="重试策略" class="level2">
|
|
216
|
+
<h2 class="anchored" data-anchor-id="重试策略">重试策略</h2>
|
|
217
|
+
<ul>
|
|
218
|
+
<li>单个事件 sendEvent 与批量 sendBatch 均支持 executeWithRetry</li>
|
|
219
|
+
<li>可配置最大次数、基础延迟、指数退避</li>
|
|
220
|
+
<li>代码参考:<a href="file:///Users/ddg-dany/work/yf-gitlab/frontend-common/yf-frontend-scaffold/packages/vue-pc-seed/src/pages/logger-sdk-tester/logger-sdk-v2/core/retryManager.ts">retryManager.ts</a></li>
|
|
221
|
+
</ul>
|
|
222
|
+
</section>
|
|
223
|
+
<section id="采样与开关" class="level2">
|
|
224
|
+
<h2 class="anchored" data-anchor-id="采样与开关">采样与开关</h2>
|
|
225
|
+
<ul>
|
|
226
|
+
<li>后端注册返回:
|
|
227
|
+
<ul>
|
|
228
|
+
<li>collectSwitch:采集开关(0 关闭)</li>
|
|
229
|
+
<li>collectLogLevel:采集日志等级白名单</li>
|
|
230
|
+
<li>samplingRate:全局采样率</li>
|
|
231
|
+
</ul></li>
|
|
232
|
+
<li>前端 shouldSend:
|
|
233
|
+
<ul>
|
|
234
|
+
<li>综合 level/source/path/global 采样率</li>
|
|
235
|
+
<li>基于 seed 哈希概率决定是否发送</li>
|
|
236
|
+
</ul></li>
|
|
237
|
+
<li>参考:
|
|
238
|
+
<ul>
|
|
239
|
+
<li><a href="file:///Users/ddg-dany/work/yf-gitlab/frontend-common/yf-frontend-scaffold/packages/vue-pc-seed/src/pages/logger-sdk-tester/logger-sdk-v2/core/loggerSDK.ts#L348-L411">loggerSDK.ts(shouldSend)</a></li>
|
|
240
|
+
<li><a href="file:///Users/ddg-dany/work/yf-gitlab/frontend-common/yf-frontend-scaffold/packages/vue-pc-seed/src/pages/logger-sdk-tester/logger-sdk-v2/types/api.ts">api.ts</a></li>
|
|
241
|
+
</ul></li>
|
|
242
|
+
</ul>
|
|
243
|
+
</section>
|
|
244
|
+
<section id="数据格式" class="level2">
|
|
245
|
+
<h2 class="anchored" data-anchor-id="数据格式">数据格式</h2>
|
|
246
|
+
<ul>
|
|
247
|
+
<li>LogEvent:标准化日志格式(logId/seq/appId/stage/level/traceId/frontendId/url/location/message/throwable/userId/storeCode/tags)
|
|
248
|
+
<ul>
|
|
249
|
+
<li>类型参考:<a href="file:///Users/ddg-dany/work/yf-gitlab/frontend-common/yf-frontend-scaffold/packages/vue-pc-seed/src/pages/logger-sdk-tester/logger-sdk-v2/types/logEvent.ts">logEvent.ts</a></li>
|
|
250
|
+
</ul></li>
|
|
251
|
+
<li>ReportData:上报批次(appId/appStage/items[])</li>
|
|
252
|
+
</ul>
|
|
253
|
+
</section>
|
|
254
|
+
<section id="限制与注意" class="level2">
|
|
255
|
+
<h2 class="anchored" data-anchor-id="限制与注意">限制与注意</h2>
|
|
256
|
+
<ul>
|
|
257
|
+
<li>Beacon:浏览器与网络环境兼容性依赖,失败仅打印不抛出</li>
|
|
258
|
+
<li>像素图:URL 长度受限;压缩可显著降低长度但 Base64 会比二进制略增</li>
|
|
259
|
+
<li>压缩:CompressionStream 不提供压缩级别参数;fflate 同步压缩适合小批量数据</li>
|
|
260
|
+
<li>安全:不要在日志中包含敏感数据(token、密码、隐私信息等)</li>
|
|
261
|
+
</ul>
|
|
262
|
+
</section>
|
|
263
|
+
<section id="流程图" class="level2">
|
|
264
|
+
<h2 class="anchored" data-anchor-id="流程图">流程图</h2>
|
|
265
|
+
<ul>
|
|
266
|
+
<li>完整 Mermaid 流程图见文件:
|
|
267
|
+
<ul>
|
|
268
|
+
<li><a href="file:///Users/ddg-dany/work/yf-gitlab/frontend-common/yf-frontend-scaffold/packages/vue-pc-seed/src/pages/logger-sdk-tester/logger-sdk-v2/logger-sdk.mermaid">logger-sdk.mermaid</a></li>
|
|
269
|
+
</ul></li>
|
|
270
|
+
</ul>
|
|
271
|
+
</section>
|
|
272
|
+
</section>
|
|
273
|
+
|
|
274
|
+
</main>
|
|
275
|
+
<!-- /main column -->
|
|
276
|
+
<script id="quarto-html-after-body" type="application/javascript">
|
|
277
|
+
window.document.addEventListener("DOMContentLoaded", function (event) {
|
|
278
|
+
const icon = "";
|
|
279
|
+
const anchorJS = new window.AnchorJS();
|
|
280
|
+
anchorJS.options = {
|
|
281
|
+
placement: 'right',
|
|
282
|
+
icon: icon
|
|
283
|
+
};
|
|
284
|
+
anchorJS.add('.anchored');
|
|
285
|
+
const isCodeAnnotation = (el) => {
|
|
286
|
+
for (const clz of el.classList) {
|
|
287
|
+
if (clz.startsWith('code-annotation-')) {
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
const onCopySuccess = function(e) {
|
|
294
|
+
// button target
|
|
295
|
+
const button = e.trigger;
|
|
296
|
+
// don't keep focus
|
|
297
|
+
button.blur();
|
|
298
|
+
// flash "checked"
|
|
299
|
+
button.classList.add('code-copy-button-checked');
|
|
300
|
+
var currentTitle = button.getAttribute("title");
|
|
301
|
+
button.setAttribute("title", "Copied!");
|
|
302
|
+
let tooltip;
|
|
303
|
+
if (window.bootstrap) {
|
|
304
|
+
button.setAttribute("data-bs-toggle", "tooltip");
|
|
305
|
+
button.setAttribute("data-bs-placement", "left");
|
|
306
|
+
button.setAttribute("data-bs-title", "Copied!");
|
|
307
|
+
tooltip = new bootstrap.Tooltip(button,
|
|
308
|
+
{ trigger: "manual",
|
|
309
|
+
customClass: "code-copy-button-tooltip",
|
|
310
|
+
offset: [0, -8]});
|
|
311
|
+
tooltip.show();
|
|
312
|
+
}
|
|
313
|
+
setTimeout(function() {
|
|
314
|
+
if (tooltip) {
|
|
315
|
+
tooltip.hide();
|
|
316
|
+
button.removeAttribute("data-bs-title");
|
|
317
|
+
button.removeAttribute("data-bs-toggle");
|
|
318
|
+
button.removeAttribute("data-bs-placement");
|
|
319
|
+
}
|
|
320
|
+
button.setAttribute("title", currentTitle);
|
|
321
|
+
button.classList.remove('code-copy-button-checked');
|
|
322
|
+
}, 1000);
|
|
323
|
+
// clear code selection
|
|
324
|
+
e.clearSelection();
|
|
325
|
+
}
|
|
326
|
+
const getTextToCopy = function(trigger) {
|
|
327
|
+
const outerScaffold = trigger.parentElement.cloneNode(true);
|
|
328
|
+
const codeEl = outerScaffold.querySelector('code');
|
|
329
|
+
for (const childEl of codeEl.children) {
|
|
330
|
+
if (isCodeAnnotation(childEl)) {
|
|
331
|
+
childEl.remove();
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return codeEl.innerText;
|
|
335
|
+
}
|
|
336
|
+
const clipboard = new window.ClipboardJS('.code-copy-button:not([data-in-quarto-modal])', {
|
|
337
|
+
text: getTextToCopy
|
|
338
|
+
});
|
|
339
|
+
clipboard.on('success', onCopySuccess);
|
|
340
|
+
if (window.document.getElementById('quarto-embedded-source-code-modal')) {
|
|
341
|
+
const clipboardModal = new window.ClipboardJS('.code-copy-button[data-in-quarto-modal]', {
|
|
342
|
+
text: getTextToCopy,
|
|
343
|
+
container: window.document.getElementById('quarto-embedded-source-code-modal')
|
|
344
|
+
});
|
|
345
|
+
clipboardModal.on('success', onCopySuccess);
|
|
346
|
+
}
|
|
347
|
+
var localhostRegex = new RegExp(/^(?:http|https):\/\/localhost\:?[0-9]*\//);
|
|
348
|
+
var mailtoRegex = new RegExp(/^mailto:/);
|
|
349
|
+
var filterRegex = new RegExp('/' + window.location.host + '/');
|
|
350
|
+
var isInternal = (href) => {
|
|
351
|
+
return filterRegex.test(href) || localhostRegex.test(href) || mailtoRegex.test(href);
|
|
352
|
+
}
|
|
353
|
+
// Inspect non-navigation links and adorn them if external
|
|
354
|
+
var links = window.document.querySelectorAll('a[href]:not(.nav-link):not(.navbar-brand):not(.toc-action):not(.sidebar-link):not(.sidebar-item-toggle):not(.pagination-link):not(.no-external):not([aria-hidden]):not(.dropdown-item):not(.quarto-navigation-tool):not(.about-link)');
|
|
355
|
+
for (var i=0; i<links.length; i++) {
|
|
356
|
+
const link = links[i];
|
|
357
|
+
if (!isInternal(link.href)) {
|
|
358
|
+
// undo the damage that might have been done by quarto-nav.js in the case of
|
|
359
|
+
// links that we want to consider external
|
|
360
|
+
if (link.dataset.originalHref !== undefined) {
|
|
361
|
+
link.href = link.dataset.originalHref;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
function tippyHover(el, contentFn, onTriggerFn, onUntriggerFn) {
|
|
366
|
+
const config = {
|
|
367
|
+
allowHTML: true,
|
|
368
|
+
maxWidth: 500,
|
|
369
|
+
delay: 100,
|
|
370
|
+
arrow: false,
|
|
371
|
+
appendTo: function(el) {
|
|
372
|
+
return el.parentElement;
|
|
373
|
+
},
|
|
374
|
+
interactive: true,
|
|
375
|
+
interactiveBorder: 10,
|
|
376
|
+
theme: 'quarto',
|
|
377
|
+
placement: 'bottom-start',
|
|
378
|
+
};
|
|
379
|
+
if (contentFn) {
|
|
380
|
+
config.content = contentFn;
|
|
381
|
+
}
|
|
382
|
+
if (onTriggerFn) {
|
|
383
|
+
config.onTrigger = onTriggerFn;
|
|
384
|
+
}
|
|
385
|
+
if (onUntriggerFn) {
|
|
386
|
+
config.onUntrigger = onUntriggerFn;
|
|
387
|
+
}
|
|
388
|
+
window.tippy(el, config);
|
|
389
|
+
}
|
|
390
|
+
const noterefs = window.document.querySelectorAll('a[role="doc-noteref"]');
|
|
391
|
+
for (var i=0; i<noterefs.length; i++) {
|
|
392
|
+
const ref = noterefs[i];
|
|
393
|
+
tippyHover(ref, function() {
|
|
394
|
+
// use id or data attribute instead here
|
|
395
|
+
let href = ref.getAttribute('data-footnote-href') || ref.getAttribute('href');
|
|
396
|
+
try { href = new URL(href).hash; } catch {}
|
|
397
|
+
const id = href.replace(/^#\/?/, "");
|
|
398
|
+
const note = window.document.getElementById(id);
|
|
399
|
+
if (note) {
|
|
400
|
+
return note.innerHTML;
|
|
401
|
+
} else {
|
|
402
|
+
return "";
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
const xrefs = window.document.querySelectorAll('a.quarto-xref');
|
|
407
|
+
const processXRef = (id, note) => {
|
|
408
|
+
// Strip column container classes
|
|
409
|
+
const stripColumnClz = (el) => {
|
|
410
|
+
el.classList.remove("page-full", "page-columns");
|
|
411
|
+
if (el.children) {
|
|
412
|
+
for (const child of el.children) {
|
|
413
|
+
stripColumnClz(child);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
stripColumnClz(note)
|
|
418
|
+
if (id === null || id.startsWith('sec-')) {
|
|
419
|
+
// Special case sections, only their first couple elements
|
|
420
|
+
const container = document.createElement("div");
|
|
421
|
+
if (note.children && note.children.length > 2) {
|
|
422
|
+
container.appendChild(note.children[0].cloneNode(true));
|
|
423
|
+
for (let i = 1; i < note.children.length; i++) {
|
|
424
|
+
const child = note.children[i];
|
|
425
|
+
if (child.tagName === "P" && child.innerText === "") {
|
|
426
|
+
continue;
|
|
427
|
+
} else {
|
|
428
|
+
container.appendChild(child.cloneNode(true));
|
|
429
|
+
break;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
if (window.Quarto?.typesetMath) {
|
|
433
|
+
window.Quarto.typesetMath(container);
|
|
434
|
+
}
|
|
435
|
+
return container.innerHTML
|
|
436
|
+
} else {
|
|
437
|
+
if (window.Quarto?.typesetMath) {
|
|
438
|
+
window.Quarto.typesetMath(note);
|
|
439
|
+
}
|
|
440
|
+
return note.innerHTML;
|
|
441
|
+
}
|
|
442
|
+
} else {
|
|
443
|
+
// Remove any anchor links if they are present
|
|
444
|
+
const anchorLink = note.querySelector('a.anchorjs-link');
|
|
445
|
+
if (anchorLink) {
|
|
446
|
+
anchorLink.remove();
|
|
447
|
+
}
|
|
448
|
+
if (window.Quarto?.typesetMath) {
|
|
449
|
+
window.Quarto.typesetMath(note);
|
|
450
|
+
}
|
|
451
|
+
if (note.classList.contains("callout")) {
|
|
452
|
+
return note.outerHTML;
|
|
453
|
+
} else {
|
|
454
|
+
return note.innerHTML;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
for (var i=0; i<xrefs.length; i++) {
|
|
459
|
+
const xref = xrefs[i];
|
|
460
|
+
tippyHover(xref, undefined, function(instance) {
|
|
461
|
+
instance.disable();
|
|
462
|
+
let url = xref.getAttribute('href');
|
|
463
|
+
let hash = undefined;
|
|
464
|
+
if (url.startsWith('#')) {
|
|
465
|
+
hash = url;
|
|
466
|
+
} else {
|
|
467
|
+
try { hash = new URL(url).hash; } catch {}
|
|
468
|
+
}
|
|
469
|
+
if (hash) {
|
|
470
|
+
const id = hash.replace(/^#\/?/, "");
|
|
471
|
+
const note = window.document.getElementById(id);
|
|
472
|
+
if (note !== null) {
|
|
473
|
+
try {
|
|
474
|
+
const html = processXRef(id, note.cloneNode(true));
|
|
475
|
+
instance.setContent(html);
|
|
476
|
+
} finally {
|
|
477
|
+
instance.enable();
|
|
478
|
+
instance.show();
|
|
479
|
+
}
|
|
480
|
+
} else {
|
|
481
|
+
// See if we can fetch this
|
|
482
|
+
fetch(url.split('#')[0])
|
|
483
|
+
.then(res => res.text())
|
|
484
|
+
.then(html => {
|
|
485
|
+
const parser = new DOMParser();
|
|
486
|
+
const htmlDoc = parser.parseFromString(html, "text/html");
|
|
487
|
+
const note = htmlDoc.getElementById(id);
|
|
488
|
+
if (note !== null) {
|
|
489
|
+
const html = processXRef(id, note);
|
|
490
|
+
instance.setContent(html);
|
|
491
|
+
}
|
|
492
|
+
}).finally(() => {
|
|
493
|
+
instance.enable();
|
|
494
|
+
instance.show();
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
} else {
|
|
498
|
+
// See if we can fetch a full url (with no hash to target)
|
|
499
|
+
// This is a special case and we should probably do some content thinning / targeting
|
|
500
|
+
fetch(url)
|
|
501
|
+
.then(res => res.text())
|
|
502
|
+
.then(html => {
|
|
503
|
+
const parser = new DOMParser();
|
|
504
|
+
const htmlDoc = parser.parseFromString(html, "text/html");
|
|
505
|
+
const note = htmlDoc.querySelector('main.content');
|
|
506
|
+
if (note !== null) {
|
|
507
|
+
// This should only happen for chapter cross references
|
|
508
|
+
// (since there is no id in the URL)
|
|
509
|
+
// remove the first header
|
|
510
|
+
if (note.children.length > 0 && note.children[0].tagName === "HEADER") {
|
|
511
|
+
note.children[0].remove();
|
|
512
|
+
}
|
|
513
|
+
const html = processXRef(null, note);
|
|
514
|
+
instance.setContent(html);
|
|
515
|
+
}
|
|
516
|
+
}).finally(() => {
|
|
517
|
+
instance.enable();
|
|
518
|
+
instance.show();
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
}, function(instance) {
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
let selectedAnnoteEl;
|
|
525
|
+
const selectorForAnnotation = ( cell, annotation) => {
|
|
526
|
+
let cellAttr = 'data-code-cell="' + cell + '"';
|
|
527
|
+
let lineAttr = 'data-code-annotation="' + annotation + '"';
|
|
528
|
+
const selector = 'span[' + cellAttr + '][' + lineAttr + ']';
|
|
529
|
+
return selector;
|
|
530
|
+
}
|
|
531
|
+
const selectCodeLines = (annoteEl) => {
|
|
532
|
+
const doc = window.document;
|
|
533
|
+
const targetCell = annoteEl.getAttribute("data-target-cell");
|
|
534
|
+
const targetAnnotation = annoteEl.getAttribute("data-target-annotation");
|
|
535
|
+
const annoteSpan = window.document.querySelector(selectorForAnnotation(targetCell, targetAnnotation));
|
|
536
|
+
const lines = annoteSpan.getAttribute("data-code-lines").split(",");
|
|
537
|
+
const lineIds = lines.map((line) => {
|
|
538
|
+
return targetCell + "-" + line;
|
|
539
|
+
})
|
|
540
|
+
let top = null;
|
|
541
|
+
let height = null;
|
|
542
|
+
let parent = null;
|
|
543
|
+
if (lineIds.length > 0) {
|
|
544
|
+
//compute the position of the single el (top and bottom and make a div)
|
|
545
|
+
const el = window.document.getElementById(lineIds[0]);
|
|
546
|
+
top = el.offsetTop;
|
|
547
|
+
height = el.offsetHeight;
|
|
548
|
+
parent = el.parentElement.parentElement;
|
|
549
|
+
if (lineIds.length > 1) {
|
|
550
|
+
const lastEl = window.document.getElementById(lineIds[lineIds.length - 1]);
|
|
551
|
+
const bottom = lastEl.offsetTop + lastEl.offsetHeight;
|
|
552
|
+
height = bottom - top;
|
|
553
|
+
}
|
|
554
|
+
if (top !== null && height !== null && parent !== null) {
|
|
555
|
+
// cook up a div (if necessary) and position it
|
|
556
|
+
let div = window.document.getElementById("code-annotation-line-highlight");
|
|
557
|
+
if (div === null) {
|
|
558
|
+
div = window.document.createElement("div");
|
|
559
|
+
div.setAttribute("id", "code-annotation-line-highlight");
|
|
560
|
+
div.style.position = 'absolute';
|
|
561
|
+
parent.appendChild(div);
|
|
562
|
+
}
|
|
563
|
+
div.style.top = top - 2 + "px";
|
|
564
|
+
div.style.height = height + 4 + "px";
|
|
565
|
+
div.style.left = 0;
|
|
566
|
+
let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter");
|
|
567
|
+
if (gutterDiv === null) {
|
|
568
|
+
gutterDiv = window.document.createElement("div");
|
|
569
|
+
gutterDiv.setAttribute("id", "code-annotation-line-highlight-gutter");
|
|
570
|
+
gutterDiv.style.position = 'absolute';
|
|
571
|
+
const codeCell = window.document.getElementById(targetCell);
|
|
572
|
+
const gutter = codeCell.querySelector('.code-annotation-gutter');
|
|
573
|
+
gutter.appendChild(gutterDiv);
|
|
574
|
+
}
|
|
575
|
+
gutterDiv.style.top = top - 2 + "px";
|
|
576
|
+
gutterDiv.style.height = height + 4 + "px";
|
|
577
|
+
}
|
|
578
|
+
selectedAnnoteEl = annoteEl;
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
const unselectCodeLines = () => {
|
|
582
|
+
const elementsIds = ["code-annotation-line-highlight", "code-annotation-line-highlight-gutter"];
|
|
583
|
+
elementsIds.forEach((elId) => {
|
|
584
|
+
const div = window.document.getElementById(elId);
|
|
585
|
+
if (div) {
|
|
586
|
+
div.remove();
|
|
587
|
+
}
|
|
588
|
+
});
|
|
589
|
+
selectedAnnoteEl = undefined;
|
|
590
|
+
};
|
|
591
|
+
// Handle positioning of the toggle
|
|
592
|
+
window.addEventListener(
|
|
593
|
+
"resize",
|
|
594
|
+
throttle(() => {
|
|
595
|
+
elRect = undefined;
|
|
596
|
+
if (selectedAnnoteEl) {
|
|
597
|
+
selectCodeLines(selectedAnnoteEl);
|
|
598
|
+
}
|
|
599
|
+
}, 10)
|
|
600
|
+
);
|
|
601
|
+
function throttle(fn, ms) {
|
|
602
|
+
let throttle = false;
|
|
603
|
+
let timer;
|
|
604
|
+
return (...args) => {
|
|
605
|
+
if(!throttle) { // first call gets through
|
|
606
|
+
fn.apply(this, args);
|
|
607
|
+
throttle = true;
|
|
608
|
+
} else { // all the others get throttled
|
|
609
|
+
if(timer) clearTimeout(timer); // cancel #2
|
|
610
|
+
timer = setTimeout(() => {
|
|
611
|
+
fn.apply(this, args);
|
|
612
|
+
timer = throttle = false;
|
|
613
|
+
}, ms);
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
// Attach click handler to the DT
|
|
618
|
+
const annoteDls = window.document.querySelectorAll('dt[data-target-cell]');
|
|
619
|
+
for (const annoteDlNode of annoteDls) {
|
|
620
|
+
annoteDlNode.addEventListener('click', (event) => {
|
|
621
|
+
const clickedEl = event.target;
|
|
622
|
+
if (clickedEl !== selectedAnnoteEl) {
|
|
623
|
+
unselectCodeLines();
|
|
624
|
+
const activeEl = window.document.querySelector('dt[data-target-cell].code-annotation-active');
|
|
625
|
+
if (activeEl) {
|
|
626
|
+
activeEl.classList.remove('code-annotation-active');
|
|
627
|
+
}
|
|
628
|
+
selectCodeLines(clickedEl);
|
|
629
|
+
clickedEl.classList.add('code-annotation-active');
|
|
630
|
+
} else {
|
|
631
|
+
// Unselect the line
|
|
632
|
+
unselectCodeLines();
|
|
633
|
+
clickedEl.classList.remove('code-annotation-active');
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
const findCites = (el) => {
|
|
638
|
+
const parentEl = el.parentElement;
|
|
639
|
+
if (parentEl) {
|
|
640
|
+
const cites = parentEl.dataset.cites;
|
|
641
|
+
if (cites) {
|
|
642
|
+
return {
|
|
643
|
+
el,
|
|
644
|
+
cites: cites.split(' ')
|
|
645
|
+
};
|
|
646
|
+
} else {
|
|
647
|
+
return findCites(el.parentElement)
|
|
648
|
+
}
|
|
649
|
+
} else {
|
|
650
|
+
return undefined;
|
|
651
|
+
}
|
|
652
|
+
};
|
|
653
|
+
var bibliorefs = window.document.querySelectorAll('a[role="doc-biblioref"]');
|
|
654
|
+
for (var i=0; i<bibliorefs.length; i++) {
|
|
655
|
+
const ref = bibliorefs[i];
|
|
656
|
+
const citeInfo = findCites(ref);
|
|
657
|
+
if (citeInfo) {
|
|
658
|
+
tippyHover(citeInfo.el, function() {
|
|
659
|
+
var popup = window.document.createElement('div');
|
|
660
|
+
citeInfo.cites.forEach(function(cite) {
|
|
661
|
+
var citeDiv = window.document.createElement('div');
|
|
662
|
+
citeDiv.classList.add('hanging-indent');
|
|
663
|
+
citeDiv.classList.add('csl-entry');
|
|
664
|
+
var biblioDiv = window.document.getElementById('ref-' + cite);
|
|
665
|
+
if (biblioDiv) {
|
|
666
|
+
citeDiv.innerHTML = biblioDiv.innerHTML;
|
|
667
|
+
}
|
|
668
|
+
popup.appendChild(citeDiv);
|
|
669
|
+
});
|
|
670
|
+
return popup.innerHTML;
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
});
|
|
675
|
+
</script>
|
|
676
|
+
</div> <!-- /content -->
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
</body></html>
|