getreadystack commited on
Commit
1ac0749
·
verified ·
1 Parent(s): 8bc8368

Upload index.html with huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +328 -17
index.html CHANGED
@@ -1,19 +1,330 @@
1
  <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
1
  <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <title>Intel HEX & S-record Image Audit — free online check</title>
7
+ <style>
8
+ :root{color-scheme:light}body{margin:0;font:15px/1.55 system-ui,-apple-system,"Segoe UI",sans-serif;background:#faf9f5;color:#1a1512}
9
+ .wrap{max-width:960px;margin:0 auto;padding:28px 18px 60px}h1{font-size:26px;margin:0 0 6px}.lede{color:#4a403a;margin:0 0 16px}
10
+ textarea{width:100%;min-height:260px;font:13px/1.45 ui-monospace,Menlo,Consolas,monospace;padding:12px;border:1px solid #d9d0c4;border-radius:8px;box-sizing:border-box}
11
+ .bar{display:flex;gap:10px;align-items:center;margin:12px 0}button{font:600 15px system-ui;padding:10px 18px;border:0;border-radius:8px;background:#1a1512;color:#fff;cursor:pointer}
12
+ input[type=date]{font:14px system-ui;padding:8px;border:1px solid #d9d0c4;border-radius:6px}
13
+ table{border-collapse:collapse;width:100%;font-size:14px;margin-top:10px}th,td{text-align:left;padding:8px 10px;border-bottom:1px solid #eee;vertical-align:top}th{font-size:12px;letter-spacing:.06em;text-transform:uppercase;color:#7a6e65}
14
+ .sev.error{color:#a8551f;font-weight:700}.sev.warn,.sev.warning{color:#96691f}.ok{color:#2a6446;font-weight:600}.sum{font-weight:600}
15
+ .note{font-size:13px;color:#7a6e65;margin-top:18px}#paid{margin-top:22px;padding:14px 16px;border:1px solid #e2d8cb;border-radius:10px;background:#fffdfa}
16
+ </style>
17
+ <script data-grow-initializer="">!(function(){window.growMe||((window.growMe=function(e){window.growMe._.push(e);}),(window.growMe._=[]));var e=document.createElement("script");(e.type="text/javascript"),(e.src="https://faves.grow.me/main.js"),(e.defer=!0),e.setAttribute("data-grow-faves-site-id","U2l0ZTphZjljMzgyMC0yMmU5LTRhYjgtYTIxNS03OTRhMGY1N2ExYzM=");var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t);})();</script>
18
+ <!--yj-canon-s101--><link rel="canonical" href="https://getreadystack.com/tools/hex-image-audit"></head>
19
+ <body>
20
+ <div class="wrap">
21
+ <h1>Intel HEX & S-record Image Audit</h1>
22
+ <p class="lede">Recomputes every checksum in a .hex/.s19 firmware image and flags bad records, overlaps, gaps, missing EOF and unaligned pages before the image reaches the programmer. Runs entirely in your browser — nothing is uploaded.</p>
23
+ <textarea id="src" spellcheck="false">:020000040800F2
24
+ :100000000714212E3B4855626F7C8996A3B0BDCA68
25
+ :100010000E1B2835424F5C697683909DAAB7C4D12A
26
+ :1000200015222F3C495663707D8A97A4B1BECB40
27
+ # regenerated by patch_tool.py on 2026-09-11
28
+ :100030001c293643505d6a7784919eabb8c5d2dfe8
29
+ :1001000023303D4A5764717E8B98A5B2BFCCD9E6A7
30
+ :100110002A3744515E6B7885929FACB9C6D3E0ED27
31
+ :08010800313E4B5865727F8CFB
32
+ :10FFF8003845525F6C798693A0ADBAC7D4E1EEFB61
33
+ :04014007DEADBEEF7C
34
+ :0400000500000200F5
35
+ :00000001FF
36
+ :100200003F4C596673808D9AA7B4C1CEDBE8F502E6</textarea>
37
+ <div class="bar"><button id="go">Check</button><label>as of <input type="date" id="today"></label></div>
38
+ <div id="out" aria-live="polite"></div>
39
+ <div id="paid"><strong>Free here:</strong> Audit one .hex or .s19 image end to end: every record checksum recomputed, every address range mapped, every structural defect named with its line number.. <strong>In the editor:</strong> Audit the whole build output folder in one pass and export a dated Markdown image-audit report you keep — the artefact a release or CRA technical-documentation file needs. — that part asks for a licence key.</div>
40
+ <p class="note">Same engine as the VS Code extension, byte for byte.</p>
41
+ </div>
42
+ <script>var HEX_RULES = [
43
+ {"id":"record_start","sev":"error","title":"Line is not a record","why":"Every Intel HEX line must begin with ':' and every Motorola line with 'S'. Anything else is a paste artefact, a diff marker or a stray editor line, and most programmers stop at it."},
44
+ {"id":"record_hex","sev":"error","title":"Non-hex or odd-length record","why":"A record is whole bytes of uppercase-or-lowercase hex. An odd digit count or a stray character means a byte was dropped or split when the file was edited or generated."},
45
+ {"id":"record_length","sev":"error","title":"Byte-count field disagrees with the data","why":"The LL field states how many data bytes follow. When a patch adds or removes bytes without fixing LL, loaders read past the end of the record or truncate it silently."},
46
+ {"id":"record_checksum","sev":"error","title":"Intel HEX checksum is wrong","why":"The last byte is the two's complement of the sum of every byte before it. Hand-edited and machine-patched records almost always leave the old checksum behind; strict programmers abort, lax ones flash the record anyway."},
47
+ {"id":"record_type","sev":"error","title":"Unknown Intel HEX record type","why":"Only types 00-05 are defined. An unknown type is a corrupted address field or a file from a different format, and behaviour is undefined across toolchains."},
48
+ {"id":"eof_record","sev":"error","title":"Missing or malformed end-of-file record","why":"An Intel HEX image must end with :00000001FF. Without it a programmer cannot tell a complete image from a truncated download, and some simply hang waiting for more."},
49
+ {"id":"data_after_eof","sev":"error","title":"Records after the end-of-file record","why":"Everything after the EOF record is ignored by conforming loaders. Appending a patch to the end of a .hex file is the classic way to ship a fix that never reaches the device."},
50
+ {"id":"address_overlap","sev":"error","title":"Two records write the same address","why":"When two data records cover the same absolute byte, the flashed value depends on the load order of the tool. Merged images and appended patches are where this comes from."},
51
+ {"id":"extended_address","sev":"error","title":"Address wraps past 64 KiB with no extended-address record","why":"Type 00 records carry only 16 address bits. Past 0xFFFF an image needs a type 04 or type 02 record; without one the data lands back at the bottom of memory."},
52
+ {"id":"srec_checksum","sev":"error","title":"S-record checksum is wrong","why":"An S-record checksum is 0xFF minus the sum of the count, address and data bytes. A wrong one means the line was edited after generation."},
53
+ {"id":"srec_count","sev":"error","title":"S-record count field or terminator is wrong","why":"The count byte covers address + data + checksum, and an S-record image must end with S7, S8 or S9. A wrong count shifts the checksum byte; a missing terminator leaves the entry point undefined."},
54
+ {"id":"srec_tally","sev":"warn","title":"S5/S6 record tally does not match","why":"S5 and S6 declare how many data records the file contains. A tally that no longer matches is proof the file was cut, merged or filtered after it was built."},
55
+ {"id":"address_gap","sev":"warn","title":"Unwritten gap inside the image","why":"A gap means those cells keep whatever was in flash before. If the gap is not deliberate erased space it is a section the linker placed and the image dropped."},
56
+ {"id":"page_alignment","sev":"warn","title":"Block does not sit on a flash page boundary","why":"Page-programmed flash writes whole pages. A block that starts or ends mid-page forces a read-modify-write, and on parts without it the rest of the page is erased."},
57
+ {"id":"start_address","sev":"warn","title":"Start address is outside the written image","why":"A type 03/05 or S7/S8/S9 record names the entry point. If it points at an address the image never writes, the device jumps into unprogrammed flash."},
58
+ {"id":"case_consistency","sev":"warn","title":"Mixed-case hex digits","why":"The format allows either case, but a file that switches case mid-way is a file that two different tools have written, which is worth knowing before you trust its checksums."}
59
+ ]
60
+ ;</script>
61
+ <script>'use strict';
62
+ // Intel HEX / Motorola S-record image audit. Same file runs in Node (extension) and in the browser (free web tool).
63
+ var RULES = (typeof module !== 'undefined') ? require('./rules.json') : window.HEX_RULES;
64
+
65
+ var BY_ID = {};
66
+ for (var _i = 0; _i < RULES.length; _i++) { BY_ID[RULES[_i].id] = RULES[_i]; }
67
+
68
+ var SREC_ADDR_LEN = { '0': 2, '1': 2, '2': 3, '3': 4, '5': 2, '6': 3, '7': 4, '8': 3, '9': 2 };
69
+
70
+ function hh(n) { var s = n.toString(16).toUpperCase(); return '0x' + (s.length < 2 ? '0' + s : s); }
71
+ function ha(n) { var s = n.toString(16).toUpperCase(); while (s.length < 8) { s = '0' + s; } return '0x' + s; }
72
+
73
+ function check(text, opts) {
74
+ opts = opts || {};
75
+ var page = Number(opts.page) > 0 ? Number(opts.page) : 256;
76
+ var findings = [];
77
+ function add(id, line, msg) {
78
+ var r = BY_ID[id];
79
+ findings.push({ check: id, sev: r ? r.sev : 'error', msg: msg, line: line });
80
+ }
81
+
82
+ var lines = String(text == null ? '' : text).split(/\r?\n/);
83
+ var blocks = []; // {start, end, line}
84
+ var written = {}; // absolute address -> line, for overlap
85
+ var overlapBudget = 262144;
86
+ var overlapReported = {};
87
+ var ela = 0, esa = 0;
88
+ var eofLine = 0, seenIntel = false, seenSrec = false, srecTermLine = 0;
89
+ var dataRecords = 0, tally = null, tallyLine = 0;
90
+ var startAddr = null, startLine = 0;
91
+ var fileCase = '', caseFlagged = false;
92
+ var lastLine = 1;
93
+
94
+ function noteCase(body, ln) {
95
+ if (caseFlagged) { return; }
96
+ var hasU = /[A-F]/.test(body), hasL = /[a-f]/.test(body);
97
+ if (hasU && hasL) { caseFlagged = true; add('case_consistency', ln, 'this record mixes upper- and lower-case hex digits'); return; }
98
+ var c = hasU ? 'upper' : (hasL ? 'lower' : '');
99
+ if (!c) { return; }
100
+ if (!fileCase) { fileCase = c; return; }
101
+ if (fileCase !== c) {
102
+ caseFlagged = true;
103
+ add('case_consistency', ln, 'the image starts in ' + fileCase + '-case hex and switches to ' + c + '-case here, so two different tools have written this file');
104
+ }
105
+ }
106
+
107
+ function claim(start, len, ln) {
108
+ blocks.push({ start: start, end: start + len, line: ln });
109
+ if (len > overlapBudget) { overlapBudget = 0; return; }
110
+ for (var a = start; a < start + len; a++) {
111
+ if (overlapBudget <= 0) { return; }
112
+ overlapBudget--;
113
+ if (written[a] !== undefined) {
114
+ var key = written[a] + '>' + ln;
115
+ if (!overlapReported[key]) {
116
+ overlapReported[key] = 1;
117
+ add('address_overlap', ln, 'address ' + ha(a) + ' is already written by the record on line ' + written[a] + ', so the flashed byte depends on the load order of the tool');
118
+ }
119
+ } else {
120
+ written[a] = ln;
121
+ }
122
+ }
123
+ }
124
+
125
+ for (var i = 0; i < lines.length; i++) {
126
+ var ln = i + 1;
127
+ var s = lines[i].trim();
128
+ if (!s) { continue; }
129
+ lastLine = ln;
130
+
131
+ if (s.charAt(0) === ':') {
132
+ seenIntel = true;
133
+ var body = s.slice(1);
134
+ if (!/^[0-9A-Fa-f]+$/.test(body)) {
135
+ add('record_hex', ln, 'the record contains characters that are not hex digits');
136
+ continue;
137
+ }
138
+ if (body.length % 2 !== 0) {
139
+ add('record_hex', ln, 'the record holds ' + body.length + ' hex digits, which is not a whole number of bytes');
140
+ continue;
141
+ }
142
+ if (body.length < 10) {
143
+ add('record_hex', ln, 'the record is ' + body.length + ' hex digits long; the shortest legal record is 10 (count, address, type, checksum)');
144
+ continue;
145
+ }
146
+ noteCase(body, ln);
147
+ var b = [];
148
+ for (var j = 0; j < body.length; j += 2) { b.push(parseInt(body.substr(j, 2), 16)); }
149
+ var ll = b[0], addr = (b[1] << 8) | b[2], tt = b[3], dataLen = b.length - 5;
150
+
151
+ var sum = 0;
152
+ for (var k = 0; k < b.length - 1; k++) { sum += b[k]; }
153
+ var want = (0x100 - (sum & 0xFF)) & 0xFF, got = b[b.length - 1];
154
+ if (want !== got) {
155
+ add('record_checksum', ln, 'the record ends in ' + hh(got) + ' but the recomputed two\u2019s-complement checksum is ' + hh(want));
156
+ }
157
+ if (ll !== dataLen) {
158
+ add('record_length', ln, 'the byte-count field says ' + ll + ' but the record carries ' + dataLen + ' data byte' + (dataLen === 1 ? '' : 's'));
159
+ }
160
+ if (tt > 5) {
161
+ add('record_type', ln, 'record type ' + hh(tt) + ' is not one of the defined types 00-05');
162
+ continue;
163
+ }
164
+ if (eofLine) {
165
+ add('data_after_eof', ln, 'this record sits after the end-of-file record on line ' + eofLine + ', so conforming loaders never read it');
166
+ }
167
+
168
+ if (tt === 0) {
169
+ dataRecords++;
170
+ if (addr + dataLen > 0x10000) {
171
+ add('extended_address', ln, 'the record starts at ' + hh(b[1]) + hh(b[2]).slice(2) + ' and runs ' + dataLen + ' bytes past 0xFFFF; a type 04 record must raise the base address first');
172
+ }
173
+ claim(ela + esa + addr, dataLen, ln);
174
+ } else if (tt === 1) {
175
+ if (dataLen !== 0 || ll !== 0 || addr !== 0) {
176
+ add('eof_record', ln, 'the end-of-file record must be exactly :00000001FF, but this one declares address ' + hh(b[1]) + hh(b[2]).slice(2) + ' and ' + dataLen + ' data byte' + (dataLen === 1 ? '' : 's'));
177
+ }
178
+ if (!eofLine) { eofLine = ln; }
179
+ } else if (tt === 2 || tt === 4) {
180
+ if (dataLen !== 2) {
181
+ add('extended_address', ln, 'a type ' + hh(tt).slice(2) + ' extended-address record must carry exactly 2 data bytes, not ' + dataLen);
182
+ } else if (tt === 4) {
183
+ ela = ((b[4] << 8) | b[5]) * 65536; esa = 0;
184
+ } else {
185
+ esa = ((b[4] << 8) | b[5]) * 16; ela = 0;
186
+ }
187
+ } else if (tt === 3 || tt === 5) {
188
+ if (dataLen !== 4) {
189
+ add('start_address', ln, 'a type ' + hh(tt).slice(2) + ' start-address record must carry exactly 4 data bytes, not ' + dataLen);
190
+ } else {
191
+ startAddr = (tt === 5)
192
+ ? (((b[4] << 24) >>> 0) + (b[5] << 16) + (b[6] << 8) + b[7])
193
+ : ((((b[4] << 8) | b[5]) * 16) + ((b[6] << 8) | b[7]));
194
+ startLine = ln;
195
+ }
196
+ }
197
+ continue;
198
+ }
199
+
200
+ if (/^[Ss][0-9]/.test(s)) {
201
+ seenSrec = true;
202
+ var t = s.charAt(1);
203
+ var sbody = s.slice(2);
204
+ if (!/^[0-9A-Fa-f]+$/.test(sbody) || sbody.length % 2 !== 0) {
205
+ add('record_hex', ln, 'the S-record payload is not a whole number of hex bytes');
206
+ continue;
207
+ }
208
+ noteCase(sbody, ln);
209
+ var sb = [];
210
+ for (var m = 0; m < sbody.length; m += 2) { sb.push(parseInt(sbody.substr(m, 2), 16)); }
211
+ if (sb.length < 3) { add('record_hex', ln, 'the S-record is too short to hold a count, an address and a checksum'); continue; }
212
+ var cnt = sb[0];
213
+ if (cnt !== sb.length - 1) {
214
+ add('srec_count', ln, 'the count byte says ' + cnt + ' but ' + (sb.length - 1) + ' byte' + (sb.length - 1 === 1 ? '' : 's') + ' follow it, which shifts the checksum byte');
215
+ }
216
+ var ssum = 0;
217
+ for (var n = 0; n < sb.length - 1; n++) { ssum += sb[n]; }
218
+ var swant = (0xFF - (ssum & 0xFF)) & 0xFF, sgot = sb[sb.length - 1];
219
+ if (swant !== sgot) {
220
+ add('srec_checksum', ln, 'the record ends in ' + hh(sgot) + ' but the recomputed S-record checksum is ' + hh(swant));
221
+ }
222
+ var alen = SREC_ADDR_LEN[t];
223
+ if (alen === undefined) { add('record_type', ln, 'S' + t + ' is not a defined S-record type'); continue; }
224
+ var sa = 0;
225
+ for (var p = 0; p < alen && 1 + p < sb.length; p++) { sa = (sa * 256) + sb[1 + p]; }
226
+ if (t === '1' || t === '2' || t === '3') {
227
+ dataRecords++;
228
+ var sdl = sb.length - 1 - alen - 1;
229
+ if (sdl > 0) { claim(sa, sdl, ln); }
230
+ } else if (t === '5' || t === '6') {
231
+ tally = sa; tallyLine = ln;
232
+ } else if (t === '7' || t === '8' || t === '9') {
233
+ srecTermLine = ln; startAddr = sa; startLine = ln;
234
+ }
235
+ continue;
236
+ }
237
+
238
+ add('record_start', ln, 'this line begins with \u201c' + s.slice(0, 12) + '\u201d, which is neither an Intel HEX record (\u201c:\u201d) nor an S-record (\u201cS\u201d)');
239
+ }
240
+
241
+ if (seenIntel && !eofLine) {
242
+ add('eof_record', lastLine, 'the image has no :00000001FF end-of-file record, so a programmer cannot tell it apart from a truncated download');
243
+ }
244
+ if (seenSrec && !srecTermLine) {
245
+ add('srec_count', lastLine, 'the S-record image has no S7, S8 or S9 termination record, so the entry point is undefined');
246
+ }
247
+ if (tally !== null && tally !== dataRecords) {
248
+ add('srec_tally', tallyLine, 'the S5/S6 record declares ' + tally + ' data record' + (tally === 1 ? '' : 's') + ' but the file contains ' + dataRecords);
249
+ }
250
+
251
+ var merged = [];
252
+ blocks.slice().sort(function (x, y) { return x.start - y.start; }).forEach(function (bl) {
253
+ var last = merged[merged.length - 1];
254
+ if (last && bl.start <= last.end) { last.end = Math.max(last.end, bl.end); } else { merged.push({ start: bl.start, end: bl.end, line: bl.line }); }
255
+ });
256
+ for (var g = 1; g < merged.length; g++) {
257
+ var gap = merged[g].start - merged[g - 1].end;
258
+ if (gap > 0) {
259
+ add('address_gap', merged[g].line, 'nothing is written between ' + ha(merged[g - 1].end) + ' and ' + ha(merged[g].start) + ' \u2014 a ' + gap + '-byte gap that keeps whatever was in flash before');
260
+ }
261
+ }
262
+ for (var q = 0; q < merged.length; q++) {
263
+ var bs = merged[q].start % page, be = merged[q].end % page;
264
+ if (bs !== 0 || be !== 0) {
265
+ add('page_alignment', merged[q].line, 'the block ' + ha(merged[q].start) + '-' + ha(merged[q].end - 1) + ' does not fill whole ' + page + '-byte pages (' + (bs !== 0 ? 'starts ' + bs + ' byte(s) into a page' : 'starts on a boundary') + ', ' + (be !== 0 ? 'ends ' + (page - be) + ' byte(s) short of one' : 'ends on a boundary') + ')');
266
+ }
267
+ }
268
+ if (startAddr !== null) {
269
+ var inside = false;
270
+ for (var r = 0; r < merged.length; r++) { if (startAddr >= merged[r].start && startAddr < merged[r].end) { inside = true; break; } }
271
+ if (!inside) {
272
+ add('start_address', startLine, 'the entry point ' + ha(startAddr) + ' is not inside any address range this image writes, so the device jumps into unprogrammed flash');
273
+ }
274
+ }
275
+
276
+ findings.sort(function (x, y) { return (x.line || 0) - (y.line || 0); });
277
+ return { findings: findings };
278
+ }
279
+
280
+ var API = { engine: { check: check }, RULES: RULES, RULE_COUNT: RULES.length };
281
+ if (typeof module !== 'undefined') { module.exports = API; }
282
+ if (typeof window !== 'undefined') { window.HEXENGINE = API; }
283
+ </script>
284
+ <script>// ⛔뼈대 — kit/scaffold.py 가 찍는다 (s143). findings [{check,sev,msg,line?}] → 글/HTML. 같은 파일이 Node 와 브라우저에서 산다.
285
+ 'use strict';
286
+ function escapeHtml(s) { return String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;'); }
287
+ function summaryLine(res) {
288
+ const f = (res && res.findings) || []; const e = f.filter(function (x) { return String(x.sev || 'error').toLowerCase().startsWith('err'); }).length;
289
+ return f.length ? (f.length + ' finding' + (f.length === 1 ? '' : 's') + ' (' + e + ' error' + (e === 1 ? '' : 's') + ')') : 'Clean — no findings.';
290
+ }
291
+ function toText(res, label) {
292
+ const f = (res && res.findings) || [];
293
+ const lines = ['== ' + (label || '') + ' — ' + summaryLine(res)];
294
+ f.forEach(function (x) { lines.push(' ' + String(x.sev || 'error').toUpperCase().slice(0, 4) + ' ' + (x.line ? 'L' + x.line + ' ' : '') + (x.check || x.id || '') + ' ' + (x.msg || x.message || '')); });
295
+ return lines.join('\n');
296
+ }
297
+ function toHtml(res) {
298
+ const f = (res && res.findings) || [];
299
+ if (!f.length) return '<p class="ok">' + escapeHtml(summaryLine(res)) + '</p>';
300
+ const rows = f.map(function (x) { return '<tr><td class="sev ' + escapeHtml(String(x.sev || 'error')) + '">' + escapeHtml(String(x.sev || 'error')) + '</td><td>' + (x.line ? 'L' + escapeHtml(x.line) : '') + '</td><td><code>' + escapeHtml(x.check || x.id || '') + '</code></td><td>' + escapeHtml(x.msg || x.message || '') + '</td></tr>'; }).join('');
301
+ return '<p class="sum">' + escapeHtml(summaryLine(res)) + '</p><table><thead><tr><th>Level</th><th>Line</th><th>Check</th><th>What</th></tr></thead><tbody>' + rows + '</tbody></table>';
302
+ }
303
+ var HEXREPORT = { escapeHtml: escapeHtml, summaryLine: summaryLine, toText: toText, toHtml: toHtml };
304
+ if (typeof module !== 'undefined' && module.exports) module.exports = HEXREPORT;
305
+ else window.HEXREPORT = HEXREPORT;
306
+ </script>
307
+ <script>
308
+ (function(){
309
+ var src=document.getElementById('src'), out=document.getElementById('out'), go=document.getElementById('go'), today=document.getElementById('today');
310
+ today.value=new Date().toISOString().slice(0,10);
311
+ function run(){ try { var res=window.HEXENGINE.engine.check(src.value,{today:today.value}); out.innerHTML=window.HEXREPORT.toHtml(res); } catch(e){ out.innerHTML='<p class="sev error">Could not run: '+String(e.message||e)+'</p>'; } }
312
+ go.onclick=run; if (src.value.trim()) run();
313
+ })();
314
+ </script>
315
+
316
+ <section data-yj-alert="rule" style="margin:1rem auto 1.5rem;max-width:46rem;padding:1.1rem 1.25rem;border:1px solid #d9dee6;border-radius:12px;background:#fbfcfd;font:15px/1.55 system-ui,-apple-system,Segoe UI,sans-serif"><b style="font-size:1.05rem">Get one email when this rule changes</b><div style="opacity:.78;margin:.35rem 0 .8rem">We watch the regulation and vendor sources behind Intel HEX &amp; S-record Image Audit every day. When a rule changes, you get a single email with what changed and the updated check. No newsletter.</div><div style="display:flex;gap:8px;flex-wrap:wrap"><input id="yjae" type="email" placeholder="you@example.com" style="flex:1;min-width:210px;padding:11px 13px;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:#fff;color:inherit"><button id="yjab" style="padding:11px 18px;border-radius:8px;border:0;cursor:pointer;background:#0f7b3f;color:#fff;font:600 15px system-ui,sans-serif">Notify me</button></div><div id="yjao" hidden style="margin-top:.6rem;color:#0f7b3f">You're on the list. One email when it changes.</div><script>(function(){var b=document.getElementById("yjab"),e=document.getElementById("yjae");if(!b||!e)return;b.addEventListener("click",function(){var v=e.value.trim();if(!/.+@.+\..+/.test(v))return;try{localStorage.setItem("yj_em","1");}catch(_){}fetch(location.origin+"/api/subscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:v,slug:"hex-image-audit",lang:"en",source:"rule_alert"})}).then(function(r){var o=document.getElementById("yjao");o.hidden=false;if(!r.ok){o.textContent="Could not save - please try again.";}}).catch(function(){var o=document.getElementById("yjao");o.hidden=false;o.textContent="Could not save - please try again.";});});})();</script></section>
317
+
318
+
319
+ <section data-yj-install="free" style="margin:2rem auto 1rem;max-width:46rem;padding:1.25rem 1.25rem;border:1px solid #cfe3d6;border-radius:12px;background:#f6fbf8;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .4rem;font-size:1.2rem">Install free</h2><p style="margin:0 0 .8rem;color:#41505f">Free for the file open in your editor - no key, no limit. The workspace sweep and the report ask for a key.</p><a href="https://marketplace.visualstudio.com/items?itemName=ReadyStack.hex-image-audit" rel="noopener" data-yj-install-door="mp" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'hex-image-audit',src:'mp',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">VS Code Marketplace</a><a href="https://open-vsx.org/extension/readystack/hex-image-audit" rel="noopener" data-yj-install-door="ovsx" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'hex-image-audit',src:'ovsx',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">Open VSX (Cursor, VSCodium)</a><a href="https://www.npmjs.com/package/@readystack/hex-image-audit" rel="noopener" data-yj-install-door="npm" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'hex-image-audit',src:'npm',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">npm</a><a href="https://www.npmjs.com/package/@readystack/hex-image-audit" rel="noopener" data-yj-install-door="mcp" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'hex-image-audit',src:'mcp',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">MCP server (npx)</a><a href="https://hub.docker.com/r/getreadystack/hex-image-audit" rel="noopener" data-yj-install-door="docker" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'hex-image-audit',src:'docker',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">Docker Hub</a><pre style="margin:.5rem 0 0;padding:.6rem .8rem;background:#f3f5f8;border-radius:8px;font-size:.9rem;overflow:auto">ext install ReadyStack.hex-image-audit
320
+ npx @readystack/hex-image-audit &lt;file&gt;
321
+ npx @readystack/hex-image-audit --mcp</pre><p style="margin:.6rem 0 0;font-size:.92rem;color:#41505f"><a href="https://buy.polar.sh/polar_cl_l6iN1uWt0FwWu7tBsczD0jWpP2vxFM54Wdwqb3KPi1G" rel="nofollow" style="color:#0f7b3f;text-decoration:underline">Team? One key for every ReadyStack linter, 5 seats, $149 once</a></p></section>
322
+ <section data-yj-cta="paid" style="margin:3rem auto;max-width:46rem;padding:1.5rem 1.25rem;border:1px solid #d8dee9;border-radius:12px;background:#fbfcfe;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .5rem;font-size:1.25rem">Get the complete version <span class="yj-price">$29</span></h2><p style="margin:.25rem 0 1rem;color:#41505f">This page is the working piece. The full pack has everything below.</p><p style="margin:0 0 1rem;color:#41505f">Recomputes every record checksum in a .hex or .s19 firmware image and names the bad records, the overlaps, the gaps and the missing EOF by line number, before the image reaches the programme</p><p style="margin:0 0 1rem;color:#5b6b7c;font-size:.94rem">Embedded firmware contract work is commonly billed at $90-$150 an hour; one hour of it costs more than this licence.</p><a href="https://buy.polar.sh/polar_cl_AkmNU5yLwVJBdRMHSvuTCQI90j52v56xJsHZH4OTDjV" rel="nofollow" style="display:inline-block;padding:.7rem 1.3rem;border-radius:8px;background:#1b64f2;color:#fff;text-decoration:none;font-weight:600">Buy the full version — $29</a></section>
323
+ <section data-yj="yj-facts-v1" style="max-width:920px;margin:48px auto 0;padding:0 20px;font-family:system-ui,sans-serif"><style>.yj-article p{font:400 15.5px/1.7 system-ui,sans-serif;color:#26313b;margin:0 0 12px;max-width:72ch}.yj-article h2,.yj-article h3,.yj-article h4{font:600 17px/1.35 system-ui,sans-serif;color:#1b1f24;margin:22px 0 8px}.yj-article table{border-collapse:collapse;margin:10px 0 16px;font:14px/1.45 system-ui,sans-serif}.yj-article th,.yj-article td{border:1px solid #e2e6ea;padding:6px 10px;text-align:left}.yj-article th{background:#f4f6f8}.yj-article pre{background:#0f1720;color:#e6edf3;padding:12px 14px;border-radius:8px;overflow:auto;font:13px/1.5 ui-monospace,Menlo,monospace}.yj-article code{font:13px ui-monospace,Menlo,monospace;background:#f1f3f5;padding:1px 4px;border-radius:3px}.yj-article pre code{background:none;padding:0}.yj-article ul,.yj-article ol{font:15px/1.6 system-ui,sans-serif;color:#26313b}</style><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:34px 0 6px;color:#1b1f24">Questions people ask</h2><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What does Intel HEX &amp; S-record Image Audit actually do?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">It reads a .hex, .ihex, .s19, .s28, .s37, .srec or .mot file and runs 16 checks over it. It recomputes the two&#x27;s-complement checksum of every Intel HEX record and the 0xFF-minus-sum checksum of every S-record, compares the byte-count field against the bytes actually present, maps every absolute address the image writes, and reports each defect with its line number.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Who is it for?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Embedded firmware engineers and build engineers who ship a .hex or .s19 image to a flash programmer, a bootloader or a contract manufacturer. It is aimed at the moment an image is merged, hand-patched or regenerated by a script, which is where stale checksums, overlapping records and appended data after the EOF record come from.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Why will a free tool not do this?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Online hex viewers render the records; they do not recompute the checksums or map the address space. A chatbot cannot verify a checksum over bytes it never sees, and it will not notice that two records in a merged image write the same absolute address. Your programmer will only tell you after it has already refused the file, at the bench.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What is free and what needs a licence?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Auditing one image is free and complete: all 16 checks, in the editor or in the browser, with no key and no truncated output. The licence adds a different job - auditing every image in a build output folder in one pass and exporting a dated Markdown audit report you keep as release evidence.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What would this cost me otherwise?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Embedded firmware contract work is commonly billed at $90 to $150 an hour, so one hour spent reading records by hand costs more than the licence. The other price is the one you pay at the flashing station: a batch programmed from an image whose records nobody recomputed.</p></details><script type="application/ld+json" data-yj="yj-faq-ld">{"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [{"@type": "Question", "name": "What does Intel HEX & S-record Image Audit actually do?", "acceptedAnswer": {"@type": "Answer", "text": "It reads a .hex, .ihex, .s19, .s28, .s37, .srec or .mot file and runs 16 checks over it. It recomputes the two's-complement checksum of every Intel HEX record and the 0xFF-minus-sum checksum of every S-record, compares the byte-count field against the bytes actually present, maps every absolute address the image writes, and reports each defect with its line number."}}, {"@type": "Question", "name": "Who is it for?", "acceptedAnswer": {"@type": "Answer", "text": "Embedded firmware engineers and build engineers who ship a .hex or .s19 image to a flash programmer, a bootloader or a contract manufacturer. It is aimed at the moment an image is merged, hand-patched or regenerated by a script, which is where stale checksums, overlapping records and appended data after the EOF record come from."}}, {"@type": "Question", "name": "Why will a free tool not do this?", "acceptedAnswer": {"@type": "Answer", "text": "Online hex viewers render the records; they do not recompute the checksums or map the address space. A chatbot cannot verify a checksum over bytes it never sees, and it will not notice that two records in a merged image write the same absolute address. Your programmer will only tell you after it has already refused the file, at the bench."}}, {"@type": "Question", "name": "What is free and what needs a licence?", "acceptedAnswer": {"@type": "Answer", "text": "Auditing one image is free and complete: all 16 checks, in the editor or in the browser, with no key and no truncated output. The licence adds a different job - auditing every image in a build output folder in one pass and exporting a dated Markdown audit report you keep as release evidence."}}, {"@type": "Question", "name": "What would this cost me otherwise?", "acceptedAnswer": {"@type": "Answer", "text": "Embedded firmware contract work is commonly billed at $90 to $150 an hour, so one hour spent reading records by hand costs more than the licence. The other price is the one you pay at the flashing station: a batch programmed from an image whose records nobody recomputed."}}]}</script><div id="yj-ask" style="margin:36px 0 0;padding:18px 18px 16px;border:1px solid #e2e6ea;border-radius:10px;background:#fbfbfc"><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:0 0 4px;color:#1b1f24">Ask about this tool</h2><p style="font:400 13.5px/1.5 system-ui,sans-serif;margin:0 0 12px;color:#5b6570">One question, answered by the person who built it. Your email only if you want the answer sent.</p><form onsubmit="return yjAsk(event)" style="display:grid;gap:8px"><textarea name="question" required minlength="10" maxlength="2000" rows="3" placeholder="Your question" style="font:14px/1.5 system-ui,sans-serif;padding:10px;border:1px solid #cfd6dd;border-radius:6px"></textarea><div style="display:flex;gap:8px;flex-wrap:wrap"><input type="email" name="email" placeholder="Email (optional)" style="flex:1;min-width:200px;font:14px system-ui,sans-serif;padding:9px 10px;border:1px solid #cfd6dd;border-radius:6px"><button type="submit" style="font:600 14px system-ui,sans-serif;padding:9px 18px;border:0;border-radius:6px;background:#1b1f24;color:#fff;cursor:pointer">Send</button></div><p id="yj-ask-msg" style="font:13px system-ui,sans-serif;margin:2px 0 0;color:#0a6238;min-height:1em"></p></form><script>function yjAsk(e){e.preventDefault();var f=e.target,m=document.getElementById("yj-ask-msg");m.style.color="#0a6238";fetch(location.origin+"/api/ask",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sku:0,slug:"hex-image-audit",lang:"en",question:f.question.value,email:f.email.value,page:location.pathname})}).then(function(r){if(!r.ok)throw 0;m.textContent="Thanks \u2014 noted. Answers land on this page and, if you left an email, in your inbox.";f.question.value="";}).catch(function(){m.style.color="#a8551f";m.textContent="Could not send. Please try again in a minute.";});return false;}</script></div></section><section class="yj-related" style="max-width:920px;margin:56px auto 40px;padding:0 20px"><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Same job, next tool</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 20px;color:#5b6570">Free in the browser. The full version is a one-time licence, 7-day refund.</p><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:14px"><a class="yj-rel-c" href="https://getreadystack.com/tools/base-image-eol-lint"><span class="yj-rel-t">Base Image EOL Lint - Dockerfile and CI end-of-life check</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/dora-ict-contract-clause-lint"><span class="yj-rel-t">DORA Art. 30 ICT Contract Clause Lint</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/en301549-lint"><span class="yj-rel-t">EN 301 549 Lint — European Accessibility Act rules, with the clause number, in</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/firmware-release-gate"><span class="yj-rel-t">Firmware Release Gate for sdkconfig and prj.conf</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/linker-map-auditor"><span class="yj-rel-t">Linker Map Auditor - FLASH &amp; RAM budget for GNU ld map files</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/openapi-security-contract-lint"><span class="yj-rel-t">OpenAPI Security Contract Lint</span><span class="yj-rel-p">$29</span></a></div><style>.yj-rel-c{display:flex;flex-direction:column;gap:8px;padding:15px 16px;border:1px solid #e2e6ea;border-radius:6px;text-decoration:none;background:#fff}.yj-rel-c:hover{border-color:#b9c2cb;background:#fafbfc}.yj-rel-t{font:500 14.5px/1.42 system-ui,sans-serif;color:#1b1f24}.yj-rel-p{font:600 13px system-ui,sans-serif;color:#0a6238}</style></section><!--yj-share-s101--><div style="margin:28px 0 8px;text-align:center"><button id="yjcp" type="button" style="font:inherit;padding:10px 18px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Copy link</button><button id="yjimg" type="button" style="font:inherit;padding:10px 18px;margin-left:8px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Save result as image</button></div><script>(function(){var b=document.getElementById("yjimg");if(!b)return;b.addEventListener("click",function(){var o=document.getElementById("out");var txt=(o&&(o.innerText||o.textContent)||"").trim();if(!txt){b.textContent="(no result yet)";setTimeout(function(){b.textContent="Save result as image";},1500);return;}var lines=txt.split(String.fromCharCode(10)).filter(function(x){return x.trim();}).slice(0,10);var W=1200,P=56,c=document.createElement("canvas");c.width=W;var lh=38,H=P*2+70+lines.length*lh+60;c.height=H;var x=c.getContext("2d");x.fillStyle="#1a1512";x.fillRect(0,0,W,H);x.fillStyle="#f3e9d2";x.font="600 34px system-ui,Segoe UI,Arial";var t=(document.title||"").split("|")[0].trim();x.fillText(t.slice(0,54),P,P+34);x.font="22px ui-monospace,Menlo,Consolas,monospace";x.fillStyle="#e8dcc4";for(var i=0;i<lines.length;i++){x.fillText(lines[i].slice(0,88),P,P+70+i*lh+22);}x.font="600 22px system-ui,Segoe UI,Arial";x.fillStyle="#c9a227";var d=location.hostname.replace("www.","");x.fillText(d,W-P-x.measureText(d).width,H-P+6);c.toBlob(function(bl){var u=URL.createObjectURL(bl),a=document.createElement("a");a.href=u;a.download=(t||"result").replace(/[^a-z0-9]+/gi,"-").toLowerCase()+"-"+d+".png";document.body.appendChild(a);a.click();setTimeout(function(){URL.revokeObjectURL(u);a.remove();},2000);},"image/png");});})();</script><script>(function(){var b=document.getElementById("yjcp");if(!b)return;var t=b.textContent;b.addEventListener("click",function(){var u=location.href.split("#")[0];function d(){b.textContent="Link copied";setTimeout(function(){b.textContent=t;},1800);}if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(u).then(d,function(){prompt("",u);});}else{prompt("",u);}});})();</script><div class="yj-home-find"><div class="yj-lgbar"><a href="/" class="yj-lg on">EN</a><a href="/de/" class="yj-lg">DE</a><a href="/ja/" class="yj-lg">JA</a><a href="/es/" class="yj-lg">ES</a><a href="/pt/" class="yj-lg">PT</a></div><section class="yj-find"><h2>Find a tool</h2><input id="yjq" type="search" autocomplete="off" placeholder="Type a product name, format or task"><div id="yjr"></div></section><style>.yj-lgbar{position:absolute;top:14px;right:18px;display:flex;gap:6px;z-index:60}.yj-lg{font:600 12px/1 system-ui,sans-serif;padding:6px 9px;border-radius:4px;text-decoration:none;color:#8b96a3;border:1px solid rgba(140,150,165,.28)}.yj-lg:hover{color:#e8eaed;border-color:rgba(160,175,195,.6)}.yj-lg.on{color:#0e1116;background:#cfd6de;border-color:#cfd6de}.yj-find{max-width:760px;margin:34px auto 8px;padding:0 20px}.yj-find h2{font:600 20px/1.3 system-ui,sans-serif;margin:0 0 10px}#yjq{width:100%;box-sizing:border-box;padding:13px 15px;font:400 15.5px system-ui,sans-serif;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:rgba(255,255,255,.04);color:inherit}#yjq:focus{outline:none;border-color:#7FD1C4}#yjr{margin-top:10px;display:flex;flex-direction:column;gap:6px}#yjr a{display:flex;justify-content:space-between;gap:12px;padding:11px 14px;border:1px solid rgba(140,150,165,.22);border-radius:6px;text-decoration:none;font:400 14.5px/1.4 system-ui,sans-serif;color:inherit}#yjr a:hover{border-color:#7FD1C4}#yjr .pr{font:600 13px system-ui,sans-serif;color:#5fbf9a;white-space:nowrap}</style><script>(function(){var q=document.getElementById("yjq"),r=document.getElementById("yjr"),D=null;function load(){if(D)return Promise.resolve(D);return fetch("/search-index.json").then(function(x){return x.json();}).then(function(j){D=j;return j;}).catch(function(){D=[];return D;});}function esc(t){return String(t).replace(/[&<>"]/g,function(c){return {"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"}[c];});}function run(){var v=q.value.trim().toLowerCase();if(v.length<2){r.innerHTML="";return;}load().then(function(j){var w=v.split(/\s+/),o=[];for(var i=0;i<j.length&&o.length<12;i++){var t=j[i].t.toLowerCase();var ok=true;for(var k=0;k<w.length;k++){if(t.indexOf(w[k])<0){ok=false;break;}}if(ok)o.push(j[i]);}r.innerHTML=o.length?o.map(function(x){return "<a href=\""+esc(x.u)+"\">"+"<span>"+esc(x.t)+"</span>"+(x.p?"<span class=\"pr\">$"+x.p+"</span>":"")+"</a>";}).join(""):"";});}q.addEventListener("input",run);q.addEventListener("focus",load);})();</script></div>
324
+
325
+
326
+ <script data-yj="yj-view-beacon">(function(){try{var p=location.pathname;var k='yjv:'+p;if(sessionStorage.getItem(k))return;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",ref:(document.referrer||''),src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}catch(e){}})();</script>
327
+
328
+ <script data-yj="yj-use-beacon">(function(){try{var o=document.getElementById('out');if(!o)return;var p=location.pathname,k='yju:'+p,sent=false;function fire(){if(sent||sessionStorage.getItem(k))return;if(!(o.textContent||'').replace(/\s+/g,'').length)return;sent=true;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",t:'use',src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}var mo=new MutationObserver(function(){fire();if(sent)mo.disconnect();});mo.observe(o,{childList:true,characterData:true,subtree:true});}catch(e){}})();</script>
329
+ </body>
330
  </html>