@macle-community/simulator-web 0.0.0-stage → 1.0.0

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/web/qrcode.js ADDED
@@ -0,0 +1,1145 @@
1
+ let QRCode;
2
+
3
+ (function () {
4
+ let wholeExtension;
5
+ const QRMath = {
6
+ glog: function (idx) {
7
+ let n = idx;
8
+ if (n < 1) {
9
+ throw new Error(`glog(${n}) argument must be greater than 0`);
10
+ }
11
+ return QRMath.LOG_TABLE[n];
12
+ },
13
+ gexp: function (idx) {
14
+ let n = idx;
15
+ while (n < 0) {
16
+ n += 255;
17
+ }
18
+ while (n >= 256) {
19
+ n -= 255;
20
+ }
21
+ return QRMath.EXP_TABLE[n];
22
+ },
23
+ EXP_TABLE: new Array(256), LOG_TABLE: new Array(256)
24
+ };
25
+ const QRMaskPattern = {
26
+ PATTERN000: 0,
27
+ PATTERN001: 1,
28
+ PATTERN010: 2,
29
+ PATTERN011: 3,
30
+ PATTERN100: 4,
31
+ PATTERN101: 5,
32
+ PATTERN110: 6,
33
+ PATTERN111: 7
34
+ };
35
+ const QR_CODE_Util = {
36
+ PATTERN_POSITION_TABLE: [],
37
+ G15: (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0),
38
+ G18: (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0),
39
+ G15_MASK: (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1),
40
+ getBCHTypeInfo: function (data) {
41
+ let d = data << 10;
42
+ while (QR_CODE_Util.getBCHDigit(d) - QR_CODE_Util.getBCHDigit(QR_CODE_Util.G15) >= 0) {
43
+ d ^= (QR_CODE_Util.G15 << (QR_CODE_Util.getBCHDigit(d) - QR_CODE_Util.getBCHDigit(QR_CODE_Util.G15)));
44
+ }
45
+ return ((data << 10) | d) ^ QR_CODE_Util.G15_MASK;
46
+ },
47
+ getBCHTypeNumber: function (data) {
48
+ let d = data << 12;
49
+ while (QR_CODE_Util.getBCHDigit(d) - QR_CODE_Util.getBCHDigit(QR_CODE_Util.G18) >= 0) {
50
+ d ^= (QR_CODE_Util.G18 << (QR_CODE_Util.getBCHDigit(d) - QR_CODE_Util.getBCHDigit(QR_CODE_Util.G18)));
51
+ }
52
+ return (data << 12) | d;
53
+ },
54
+ getBCHDigit: function (data) {
55
+ let digit = 0;
56
+ while (data !== 0) {
57
+ digit += 1;
58
+ data >>>= 1;
59
+ }
60
+ return digit;
61
+ },
62
+ getPatternPosition: function (typeNumber) {
63
+ return QR_CODE_Util.PATTERN_POSITION_TABLE[typeNumber - 1];
64
+ },
65
+ getMask: function (maskPattern, positionX, positionY) {
66
+ const MASK_PATTERNS = [
67
+ // PATTERN000
68
+ (i, j) => (i + j) % 2 === 0,
69
+ // PATTERN001
70
+ (i, j) => i % 2 === 0,
71
+ // PATTERN010
72
+ (i, j) => j % 3 === 0,
73
+ // PATTERN011
74
+ (i, j) => (i + j) % 3 === 0,
75
+ // PATTERN100
76
+ (i, j) => (Math.floor(i / 2) + Math.floor(j / 3)) % 2 === 0,
77
+ // PATTERN101
78
+ (i, j) => ((i * j) % 2) + ((i * j) % 3) === 0,
79
+ // PATTERN110
80
+ (i, j) => (((i * j) % 2) + ((i * j) % 3)) % 2 === 0,
81
+ // PATTERN111
82
+ (i, j) => (((i * j) % 3) + (i + j) % 2) % 2 === 0
83
+ ];
84
+ const handler = MASK_PATTERNS[maskPattern];
85
+ if (typeof handler !== 'function') {
86
+ throw new Error(`Invalid maskPattern: ${maskPattern}. Expected one of ${Object.values(QRMaskPattern).join(', ')}`);
87
+ }
88
+
89
+ return handler(positionX, positionY);
90
+ },
91
+ getErrorCorrectPolynomial: function (errorCorrectLength) {
92
+ let a = new QRPolynomial([1], 0);
93
+ for (let index = 0; index < errorCorrectLength; index++) {
94
+ a = a.multiply(new QRPolynomial([1, QRMath.gexp(index)], 0));
95
+ }
96
+ return a;
97
+ },
98
+ getLengthInBits: function (mode, type) {
99
+ if (type < 1 || type > 40) {
100
+ throw new Error(`Invalid type: ${type}`);
101
+ }
102
+
103
+ const modeMap = {
104
+ [QRMode.MODE_NUMBER]: [10, 12, 14],
105
+ [QRMode.MODE_ALPHA_NUM]: [9, 11, 13],
106
+ [QRMode.MODE_8BIT_BYTE]: [8, 16, 16],
107
+ [QRMode.MODE_KANJI]: [8, 10, 12]
108
+ };
109
+
110
+ const index = type <= 9 ? 0 : (type <= 26 ? 1 : 2);
111
+
112
+ if (!modeMap[mode]) {
113
+ throw new Error(`Invalid mode: ${mode}`);
114
+ }
115
+
116
+ return modeMap[mode][index];
117
+ },
118
+ getLostPoint: function (qrCode) {
119
+ const moduleCount = qrCode.getModuleCount();
120
+ let lostPoint = 0;
121
+ const WEIGHT_SEQUENCE_PATTERN = 40;
122
+
123
+ function isValidCell(row, col) {
124
+ return row >= 0 && col >= 0 && row < moduleCount && col < moduleCount;
125
+ }
126
+
127
+ const neighbors = [
128
+ { dr: -1, dc: -1 },
129
+ { dr: -1, dc: 0 },
130
+ { dr: -1, dc: 1 },
131
+ { dr: 0, dc: -1 },
132
+ { dr: 0, dc: 1 },
133
+ { dr: 1, dc: -1 },
134
+ { dr: 1, dc: 0 },
135
+ { dr: 1, dc: 1 }
136
+ ];
137
+
138
+ for (let row = 0; row < moduleCount; row++) {
139
+ for (let col = 0; col < moduleCount; col++) {
140
+ const center = qrCode.isDark(row, col);
141
+ let sameCount = 0;
142
+
143
+ for (const neighbor of neighbors) {
144
+ const { dr, dc } = neighbor;
145
+ const newRow = row + dr;
146
+ const newCol = col + dc;
147
+ if (!isValidCell(newRow, newCol)) {
148
+ continue;
149
+ }
150
+
151
+ if (qrCode.isDark(newRow, newCol) === center) {
152
+ sameCount++;
153
+ }
154
+ }
155
+
156
+ if (sameCount > 5) {
157
+ lostPoint += 3 + (sameCount - 5);
158
+ }
159
+ }
160
+ }
161
+
162
+ for (let row = 0; row < moduleCount - 1; row++) {
163
+ for (let col = 0; col < moduleCount - 1; col++) {
164
+ const a = qrCode.isDark(row, col);
165
+ const b = qrCode.isDark(row + 1, col);
166
+ const c = qrCode.isDark(row, col + 1);
167
+ const d = qrCode.isDark(row + 1, col + 1);
168
+ const count = a + b + c + d;
169
+ if (count === 0 || count === 4) {
170
+ lostPoint += 3;
171
+ }
172
+ }
173
+ }
174
+
175
+ function detectPattern(row, col, isHorizontal) {
176
+ let offset = isHorizontal ? 0 : 1;
177
+ let step = isHorizontal ? 1 : 0;
178
+
179
+ return (
180
+ qrCode.isDark(row, col) &&
181
+ !qrCode.isDark(row + offset, col + step) &&
182
+ qrCode.isDark(row + (2 * offset), col + (2 * step)) &&
183
+ qrCode.isDark(row + (3 * offset), col + (3 * step)) &&
184
+ qrCode.isDark(row + (4 * offset), col + (4 * step)) &&
185
+ !qrCode.isDark(row + (5 * offset), col + (5 * step)) &&
186
+ qrCode.isDark(row + (6 * offset), col + (6 * step))
187
+ );
188
+ }
189
+
190
+ // 检测伪定位图案模式
191
+ for (let row = 0; row < moduleCount; row++) {
192
+ for (let col = 0; col < moduleCount - 6; col++) {
193
+ if (detectPattern(row, col, true)) {
194
+ lostPoint += WEIGHT_SEQUENCE_PATTERN;
195
+ }
196
+ }
197
+ }
198
+
199
+ // 垂直检测伪定位图案模式
200
+ for (let col = 0; col < moduleCount; col++) {
201
+ for (let row = 0; row < moduleCount - 6; row++) {
202
+ if (detectPattern(row, col, false)) {
203
+ lostPoint += WEIGHT_SEQUENCE_PATTERN;
204
+ }
205
+ }
206
+ }
207
+
208
+ // 计算暗色模块的数量
209
+ let darkCount = 0;
210
+ for (let row = 0; row < moduleCount; row++) {
211
+ for (let col = 0; col < moduleCount; col++) {
212
+ if (qrCode.isDark(row, col)) {
213
+ darkCount += 1;
214
+ }
215
+ }
216
+ }
217
+
218
+ const ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;
219
+ lostPoint += ratio * 10;
220
+
221
+ return lostPoint;
222
+ }
223
+ };
224
+
225
+ function QR8bitByte(data) {
226
+ this.mode = QRMode.MODE_8BIT_BYTE;
227
+ this.data = data;
228
+ this.parsedData = [];
229
+
230
+ for (let i = 0, l = this.data.length; i < l; i++) {
231
+ let byteArr = [];
232
+ const code = this.data.charCodeAt(i);
233
+
234
+ if (code > 0x10000) {
235
+ byteArr[0] = 0xF0 | ((code & 0x1C0000) >>> 18);
236
+ byteArr[1] = 0x80 | ((code & 0x3F000) >>> 12);
237
+ byteArr[2] = 0x80 | ((code & 0xFC0) >>> 6);
238
+ byteArr[3] = 0x80 | (code & 0x3F);
239
+ } else if (code > 0x800) {
240
+ byteArr[0] = 0xE0 | ((code & 0xF000) >>> 12);
241
+ byteArr[1] = 0x80 | ((code & 0xFC0) >>> 6);
242
+ byteArr[2] = 0x80 | (code & 0x3F);
243
+ } else if (code > 0x80) {
244
+ byteArr[0] = 0xC0 | ((code & 0x7C0) >>> 6);
245
+ byteArr[1] = 0x80 | (code & 0x3F);
246
+ } else {
247
+ byteArr[0] = code;
248
+ }
249
+
250
+ this.parsedData.push(byteArr);
251
+ }
252
+
253
+ this.parsedData = Array.prototype.concat.apply([], this.parsedData);
254
+
255
+ if (this.parsedData.length !== this.data.length) {
256
+ this.parsedData.unshift(191);
257
+ this.parsedData.unshift(187);
258
+ this.parsedData.unshift(239);
259
+ }
260
+ }
261
+
262
+ QR8bitByte.prototype = {
263
+ getLength: function () {
264
+ return this.parsedData.length;
265
+ },
266
+ write: function (buffer) {
267
+ this.parsedData.forEach(byte => buffer.put(byte, 8));
268
+ }
269
+ };
270
+
271
+ function QRCodeModel(typeNumber, errorCorrectLevel, moduleCount = 0, dataList = []) {
272
+ this.typeNumber = typeNumber;
273
+ this.errorCorrectLevel = errorCorrectLevel;
274
+ this.modules = null;
275
+ this.moduleCount = moduleCount;
276
+ this.dataCache = null;
277
+ this.dataList = dataList;
278
+ }
279
+
280
+ QRCodeModel.prototype = {
281
+ addData: function (data) {
282
+ const qr8bitByte = new QR8bitByte(data);
283
+ this.dataList.push(qr8bitByte);
284
+ this.dataCache = null;
285
+ },
286
+ isDark: function (row, col) {
287
+ if (row < 0 || this.moduleCount <= row || col < 0 || this.moduleCount <= col) {
288
+ throw new Error(`Invalid position: row ${row}, col ${col}`);
289
+ }
290
+ return this.modules[row][col];
291
+ },
292
+ getModuleCount: function () {
293
+ return this.moduleCount;
294
+ },
295
+ make: function () {
296
+ this.makeImpl(false, this.getBestMaskPattern());
297
+ },
298
+ makeImpl: function (test, maskPattern) {
299
+ const MODULE_COUNT_MULTIPLIER = 4;
300
+ const MODULE_COUNT_BASE = 17;
301
+ const PROBE_PATTERN_OFFSET_X = 0;
302
+ const PROBE_PATTERN_OFFSET_Y = 0;
303
+ const PROBE_PATTERN_MIDDLE_OFFSET = 7;
304
+ const MIN_TYPE_FOR_SETUP_TYPE_NUMBER = 7;
305
+
306
+ this.moduleCount = this.typeNumber * MODULE_COUNT_MULTIPLIER + MODULE_COUNT_BASE;
307
+ this.modules = new Array(this.moduleCount);
308
+ for (let rowNum = 0; rowNum < this.moduleCount; rowNum++) {
309
+ this.modules[rowNum] = new Array(this.moduleCount);
310
+ for (let col = 0; col < this.moduleCount; col++) {
311
+ this.modules[rowNum][col] = null;
312
+ }
313
+ }
314
+ // 设置 3 个定位图案(探针图案)位置
315
+ this.setupPositionProbePattern(PROBE_PATTERN_OFFSET_X, PROBE_PATTERN_OFFSET_Y);
316
+ this.setupPositionProbePattern(this.moduleCount - PROBE_PATTERN_MIDDLE_OFFSET, PROBE_PATTERN_OFFSET_Y);
317
+ this.setupPositionProbePattern(PROBE_PATTERN_OFFSET_X, this.moduleCount - PROBE_PATTERN_MIDDLE_OFFSET);
318
+
319
+ // 调整图案
320
+ this.setupPositionAdjustPattern();
321
+ // 定时图案
322
+ this.setupTimingPattern();
323
+ // 类型信息设置
324
+ this.setupTypeInfo(test, maskPattern);
325
+ if (this.typeNumber >= MIN_TYPE_FOR_SETUP_TYPE_NUMBER) {
326
+ this.setupTypeNumber(test);
327
+ }
328
+ if (this.dataCache == null) {
329
+ this.dataCache = QRCodeModel.createData(this.typeNumber, this.errorCorrectLevel, this.dataList);
330
+ }
331
+ this.mapData(this.dataCache, maskPattern);
332
+ },
333
+ setupPositionProbePattern: function (row, col) {
334
+ const moduleCount = this.moduleCount;
335
+ const modules = this.modules;
336
+
337
+ for (let r = -1; r <= 7; r++) {
338
+ for (let c = -1; c <= 7; c++) {
339
+ if (row + r < 0 || row + r >= moduleCount || col + c < 0 || col + c >= moduleCount) {
340
+ continue;
341
+ }
342
+
343
+ const isEdge = (0 <= r && r <= 6) && (c === 0 || c === 6);
344
+ const isOtherEdge = (0 <= c && c <= 6) && (r === 0 || r === 6);
345
+ const isCenter = (2 <= r && r <= 4) && (2 <= c && c <= 4);
346
+ modules[row + r][col + c] = isEdge || isOtherEdge || isCenter;
347
+ }
348
+ }
349
+ },
350
+
351
+ getBestMaskPattern: function () {
352
+ let minLostPoint = 0;
353
+ let pattern = 0;
354
+ for (let i = 0; i < 8; i++) {
355
+ this.makeImpl(true, i);
356
+ const lostPoint = QR_CODE_Util.getLostPoint(this);
357
+ if (i === 0 || minLostPoint > lostPoint) {
358
+ minLostPoint = lostPoint;
359
+ pattern = i;
360
+ }
361
+ }
362
+ return pattern;
363
+ },
364
+ setupTimingPattern: function () {
365
+ for (let r = 8; r < this.moduleCount - 8; r++) {
366
+ if (this.modules[r][6] != null) {
367
+ continue;
368
+ }
369
+ this.modules[r][6] = (r % 2 === 0);
370
+ }
371
+ for (let c = 8; c < this.moduleCount - 8; c++) {
372
+ if (this.modules[6][c] != null) {
373
+ continue;
374
+ }
375
+ this.modules[6][c] = (c % 2 === 0);
376
+ }
377
+ },
378
+ setupPositionAdjustPattern: function () {
379
+ if (wholeExtension.PATTERN_POSITION_TABLE) {
380
+ QR_CODE_Util.PATTERN_POSITION_TABLE = wholeExtension.PATTERN_POSITION_TABLE;
381
+ }
382
+ const pos = QR_CODE_Util.getPatternPosition(this.typeNumber);
383
+ const modules = this.modules;
384
+ const n = pos.length;
385
+
386
+ for (let i = 0; i < n; i++) {
387
+ const row = pos[i];
388
+ for (let j = 0; j < n; j++) {
389
+ const col = pos[j];
390
+ if (row < 0 || col < 0 || row >= modules.length || col >= modules[row].length) {
391
+ continue;
392
+ }
393
+ if (modules[row][col] != null) {
394
+ continue;
395
+ }
396
+ this.applyPattern(modules, row, col);
397
+ }
398
+ }
399
+ },
400
+ applyPattern: function (modules, row, col) {
401
+ for (let r = -2; r <= 2; r++) {
402
+ for (let c = -2; c <= 2; c++) {
403
+ modules[row + r][col + c] = r === -2 || r === 2 || c === -2 || c === 2 || (r === 0 && c === 0);
404
+ }
405
+ }
406
+ },
407
+ setupTypeNumber: function (test) {
408
+ let module;
409
+ const bits = QR_CODE_Util.getBCHTypeNumber(this.typeNumber);
410
+ for (let i = 0; i < 18; i++) {
411
+ module = (!test && ((bits >> i) & 1) === 1);
412
+ this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = module;
413
+ }
414
+ for (let i = 0; i < 18; i++) {
415
+ module = (!test && ((bits >> i) & 1) === 1);
416
+ this.modules[i % 3 + this.moduleCount - 8 - 3][Math.floor(i / 3)] = module;
417
+ }
418
+ },
419
+ setupTypeInfo: function (test, maskPattern) {
420
+ let module;
421
+ const data = (this.errorCorrectLevel << 3) | maskPattern;
422
+ const bits = QR_CODE_Util.getBCHTypeInfo(data);
423
+ for (let i = 0; i < 15; i++) {
424
+ module = (!test && ((bits >> i) & 1) === 1);
425
+ if (i < 6) {
426
+ this.modules[i][8] = module;
427
+ } else if (i < 8) {
428
+ this.modules[i + 1][8] = module;
429
+ } else {
430
+ this.modules[this.moduleCount - 15 + i][8] = module;
431
+ }
432
+ }
433
+ for (let i = 0; i < 15; i++) {
434
+ module = (!test && ((bits >> i) & 1) === 1);
435
+ if (i < 8) {
436
+ this.modules[8][this.moduleCount - i - 1] = module;
437
+ } else if (i < 9) {
438
+ this.modules[8][15 - i - 1 + 1] = module;
439
+ } else {
440
+ this.modules[8][15 - i - 1] = module;
441
+ }
442
+ }
443
+ this.modules[this.moduleCount - 8][8] = (!test);
444
+ },
445
+ mapData: function (data, maskPattern) {
446
+ let inc = -1;
447
+ let row = this.moduleCount - 1;
448
+ let bitIndex = 7;
449
+ let byteIndex = 0;
450
+
451
+ for (let col = this.moduleCount - 1; col > 0; col -= 2) {
452
+ if (col === 6) {
453
+ col--;
454
+ }
455
+
456
+ while (true) {
457
+ const leftCol = col;
458
+ if (this.modules[row][leftCol] === null) {
459
+ let dark = false;
460
+
461
+ if (byteIndex < data.length) {
462
+ dark = Boolean((data[byteIndex] >>> bitIndex) & 1);
463
+ }
464
+
465
+ const mask = QR_CODE_Util.getMask(maskPattern, row, leftCol);
466
+ if (mask) {
467
+ dark = !dark;
468
+ }
469
+
470
+ this.modules[row][leftCol] = dark;
471
+ bitIndex--;
472
+ if (bitIndex === -1) {
473
+ byteIndex++;
474
+ bitIndex = 7;
475
+ }
476
+ }
477
+
478
+ const rightCol = col - 1;
479
+ if (this.modules[row][rightCol] === null) {
480
+ let dark = false;
481
+
482
+ if (byteIndex < data.length) {
483
+ dark = Boolean((data[byteIndex] >>> bitIndex) & 1);
484
+ }
485
+
486
+ const mask = QR_CODE_Util.getMask(maskPattern, row, rightCol);
487
+ if (mask) {
488
+ dark = !dark;
489
+ }
490
+
491
+ this.modules[row][rightCol] = dark;
492
+ bitIndex--;
493
+ if (bitIndex === -1) {
494
+ byteIndex++;
495
+ bitIndex = 7;
496
+ }
497
+ }
498
+
499
+ row += inc;
500
+
501
+ if (row < 0 || row >= this.moduleCount) {
502
+ row = row - inc;
503
+ inc = -inc;
504
+ break;
505
+ }
506
+ }
507
+ }
508
+ }
509
+ };
510
+ // 设置QRCodeModel的PAD0属性为0xEC,用于二维码模型的填充
511
+ QRCodeModel.PAD0 = 0xEC;
512
+ QRCodeModel.PAD1 = 0x11;
513
+ QRCodeModel.createData = function (typeNumber, errorCorrectLevel, dataList) {
514
+ const rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel);
515
+ const qrBitBuffer = new QRBitBuffer();
516
+ let totalDataCount = 0;
517
+ for (const rsBlock of rsBlocks) {
518
+ totalDataCount += rsBlock.dataCount;
519
+ }
520
+ const maxBits = totalDataCount * 8;
521
+ let totalBits = 0;
522
+
523
+ for (const data of dataList) {
524
+ qrBitBuffer.put(data.mode, 4);
525
+ const lengthBits = QR_CODE_Util.getLengthInBits(data.mode, typeNumber);
526
+ qrBitBuffer.put(data.getLength(), lengthBits);
527
+ data.write(qrBitBuffer);
528
+ totalBits += lengthBits + data.getLength();
529
+ }
530
+
531
+ if (totalBits > maxBits) {
532
+ throw new Error(`code length overflow. (${totalBits} > ${maxBits})`);
533
+ }
534
+ if (totalBits + 4 <= maxBits) {
535
+ qrBitBuffer.put(0, 4);
536
+ totalBits += 4;
537
+ }
538
+
539
+ while (totalBits % 8 !== 0) {
540
+ qrBitBuffer.putBit(false);
541
+ totalBits++;
542
+ }
543
+
544
+ while (totalBits < maxBits) {
545
+ qrBitBuffer.put(QRCodeModel.PAD0, 8);
546
+ totalBits += 8;
547
+ if (totalBits >= maxBits) {
548
+ break;
549
+ }
550
+ qrBitBuffer.put(QRCodeModel.PAD1, 8);
551
+ totalBits += 8;
552
+ }
553
+ return QRCodeModel.createBytes(qrBitBuffer, rsBlocks);
554
+ };
555
+ QRCodeModel.createBytes = function (buffer, rsBlocks) {
556
+ let offset = 0;
557
+ let maxDcCount = 0;
558
+ let maxEcCount = 0;
559
+ const dcData = new Array(rsBlocks.length);
560
+ const ecData = new Array(rsBlocks.length);
561
+ for (let r = 0; r < rsBlocks.length; r++) {
562
+ const dcCount = rsBlocks[r].dataCount;
563
+ const ecCount = rsBlocks[r].totalCount - dcCount;
564
+ maxDcCount = Math.max(maxDcCount, dcCount);
565
+ maxEcCount = Math.max(maxEcCount, ecCount);
566
+ dcData[r] = new Array(dcCount);
567
+ for (let i = 0; i < dcData[r].length; i++) {
568
+ dcData[r][i] = 0xff & buffer.buffer[i + offset];
569
+ }
570
+ offset += dcCount;
571
+ const rsPoly = QR_CODE_Util.getErrorCorrectPolynomial(ecCount);
572
+ const rawPoly = new QRPolynomial(dcData[r], rsPoly.getLength() - 1);
573
+ const modPoly = rawPoly.mod(rsPoly);
574
+ ecData[r] = new Array(rsPoly.getLength() - 1);
575
+ for (let i = 0; i < ecData[r].length; i++) {
576
+ const modIndex = i + modPoly.getLength() - ecData[r].length;
577
+ ecData[r][i] = (modIndex >= 0) ? modPoly.get(modIndex) : 0;
578
+ }
579
+ }
580
+ let totalCodeCount = 0;
581
+ rsBlocks.forEach(rsBlock => {
582
+ totalCodeCount += rsBlock.totalCount;
583
+ });
584
+ const data = new Array(totalCodeCount);
585
+ let index = 0;
586
+ for (let i = 0; i < maxDcCount; i++) {
587
+ for (let r = 0; r < rsBlocks.length; r++) {
588
+ const currentDcData = dcData[r];
589
+ if (i < currentDcData.length) {
590
+ data[index] = currentDcData[i];
591
+ index++;
592
+ }
593
+ }
594
+ }
595
+ for (let i = 0; i < maxEcCount; i++) {
596
+ for (let r = 0; r < rsBlocks.length; r++) {
597
+ if (i < ecData[r].length) {
598
+ data[index++] = ecData[r][i];
599
+ }
600
+ }
601
+ }
602
+ return data;
603
+ };
604
+ let QRMode = { MODE_NUMBER: 1 << 0, MODE_ALPHA_NUM: 1 << 1, MODE_8BIT_BYTE: 1 << 2, MODE_KANJI: 1 << 3 };
605
+ const QRErrorCorrectLevel = { L: 1, M: 0, Q: 3, H: 2 };
606
+
607
+ for (let index = 0; index < 8; index++) {
608
+ QRMath.EXP_TABLE[index] = 1 << index;
609
+ }
610
+ for (let index = 8; index < 256; index++) {
611
+ QRMath.EXP_TABLE[index] = QRMath.EXP_TABLE[index - 4] ^ QRMath.EXP_TABLE[index - 5] ^ QRMath.EXP_TABLE[index - 6] ^ QRMath.EXP_TABLE[index - 8];
612
+ }
613
+ for (let index = 0; index < 255; index++) {
614
+ QRMath.LOG_TABLE[QRMath.EXP_TABLE[index]] = index;
615
+ }
616
+
617
+ function QRPolynomial(num, shift) {
618
+ const numLength = num.length;
619
+ if (numLength === undefined) {
620
+ throw new Error(`Invalid input: ${numLength}/${shift}`);
621
+ }
622
+ let offset = 0;
623
+ while (offset < numLength && num[offset] === 0) {
624
+ offset++;
625
+ }
626
+ this.num = new Array(numLength - offset + shift);
627
+ for (let i = 0; i < numLength - offset; i++) {
628
+ this.num[i] = num[i + offset];
629
+ }
630
+ }
631
+
632
+ QRPolynomial.prototype = {
633
+ get: function (index) {
634
+ return this.num[index];
635
+ },
636
+ getLength: function () {
637
+ return this.num.length;
638
+ },
639
+ multiply: function (e) {
640
+ const num = new Array(this.getLength() + e.getLength() - 1);
641
+ for (let i = 0; i < this.getLength(); i++) {
642
+ for (let j = 0; j < e.getLength(); j++) {
643
+ num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i)) + QRMath.glog(e.get(j)));
644
+ }
645
+ }
646
+ return new QRPolynomial(num, 0);
647
+ },
648
+ mod: function (divisor) {
649
+ if (this.getLength() - divisor.getLength() < 0) {
650
+ return this;
651
+ }
652
+ const ratio = QRMath.glog(this.get(0)) - QRMath.glog(divisor.get(0));
653
+ const num = new Array(this.getLength());
654
+ for (let i = 0; i < this.getLength(); i++) {
655
+ num[i] = this.get(i);
656
+ }
657
+ for (let i = 0; i < divisor.getLength(); i++) {
658
+ num[i] ^= QRMath.gexp(QRMath.glog(divisor.get(i)) + ratio);
659
+ }
660
+ return new QRPolynomial(num, 0).mod(divisor);
661
+ }
662
+ };
663
+
664
+ /**
665
+ * QRRSBlock 类用于表示一个包含总数量和数据数量的块。
666
+ * @class
667
+ * @param {number} totalCount - 总数量,必须是一个非负整数。
668
+ * @param {number} dataCount - 数据数量,必须是一个非负整数且不大于 totalCount。
669
+ */
670
+ function QRRSBlock(totalCount, dataCount) {
671
+ this.totalCount = totalCount;
672
+ this.dataCount = dataCount;
673
+ }
674
+
675
+ QRRSBlock.RS_BLOCK_TABLE = wholeExtension?.RS_BLOCK_TABLE || [];
676
+ QRRSBlock.getRSBlocks = function (typeNumber, errorCorrectLevel) {
677
+ QRRSBlock.RS_BLOCK_TABLE = wholeExtension?.RS_BLOCK_TABLE || [];
678
+ const rsBlock = QRRSBlock.getRsBlockTable(typeNumber, errorCorrectLevel);
679
+ if (!rsBlock) {
680
+ throw new Error(`bad rs block @ typeNumber: ${typeNumber} / errorCorrectLevel: ${errorCorrectLevel}`);
681
+ }
682
+ const length = rsBlock.length / 3;
683
+ const list = [];
684
+ for (let index = 0; index < length; index++) {
685
+ const count = rsBlock[index * 3];
686
+ const totalCount = rsBlock[index * 3 + 1];
687
+ const dataCount = rsBlock[index * 3 + 2];
688
+ for (let j = 0; j < count; j++) {
689
+ list.push(new QRRSBlock(totalCount, dataCount));
690
+ }
691
+ }
692
+ return list;
693
+ };
694
+ QRRSBlock.getRsBlockTable = function (typeNumber, errorCorrectLevel) {
695
+ const levelMap = {
696
+ [QRErrorCorrectLevel.L]: 0,
697
+ [QRErrorCorrectLevel.M]: 1,
698
+ [QRErrorCorrectLevel.Q]: 2,
699
+ [QRErrorCorrectLevel.H]: 3
700
+ };
701
+
702
+ const index = (typeNumber - 1) * 4 + (levelMap[errorCorrectLevel] || -1);
703
+ return index >= 0 ? QRRSBlock.RS_BLOCK_TABLE[index] : undefined;
704
+ };
705
+
706
+ /**
707
+ * QRBitBuffer构造函数,用于创建一个二进制缓冲区对象
708
+ * @constructor
709
+ */
710
+ function QRBitBuffer() {
711
+ this.buffer = [];
712
+ this.length = 0;
713
+ }
714
+
715
+ QRBitBuffer.prototype = {
716
+ get: function (idx) {
717
+ const bufIndex = Math.floor(idx / 8);
718
+ return ((this.buffer[bufIndex] >>> (7 - idx % 8)) & 1) === 1;
719
+ },
720
+ put: function (num, length) {
721
+ for (let i = 0; i < length; i++) {
722
+ this.putBit(((num >>> (length - i - 1)) & 1) === 1);
723
+ }
724
+ },
725
+ getLengthInBits: function () {
726
+ return this.length;
727
+ },
728
+ putBit: function (bit) {
729
+ const bufferIndex = Math.floor(this.length / 8);
730
+ if (this.buffer.length <= bufferIndex) {
731
+ this.buffer.push(0);
732
+ }
733
+ if (bit) {
734
+ this.buffer[bufferIndex] |= (0x80 >>> (this.length % 8));
735
+ }
736
+ this.length++;
737
+ }
738
+ };
739
+ let QRCodeLimitLength = wholeExtension?.QRCodeLimitLength || [];
740
+ function _isSupportCanvas() {
741
+ return typeof CanvasRenderingContext2D !== "undefined";
742
+ }
743
+
744
+ function _getAndroid() {
745
+ const userAgent = navigator.userAgent;
746
+ const androidRegex = /android (\d+\.\d+)/i;
747
+ const match = userAgent.match(androidRegex);
748
+
749
+ if (match && match[1]) {
750
+ return parseFloat(match[1]);
751
+ }
752
+
753
+ return false;
754
+ }
755
+
756
+ const svgDrawer = (function () {
757
+
758
+ /**
759
+ * Drawing构造函数,用于创建一个Drawing对象
760
+ * @param {HTMLElement} el - 需要进行绘制的HTML元素
761
+ * @param {Object} htOption - 绘制选项,包含绘制所需的各种参数
762
+ */
763
+ const Drawing = function (el, htOption) {
764
+ this._el = el;
765
+ this._htOption = htOption;
766
+ };
767
+
768
+ Drawing.prototype.draw = function (oQRCode) {
769
+ const { _htOption, _el } = this;
770
+ const nCount = oQRCode.getModuleCount();
771
+ if (nCount <= 0) {
772
+ throw new Error("Invalid module count: must be greater than zero");
773
+ }
774
+ const nWidth = Math.floor(_htOption.width / nCount);
775
+ const nHeight = Math.floor(_htOption.height / nCount);
776
+
777
+ this.clear();
778
+
779
+
780
+ /**
781
+ * 创建一个SVG元素
782
+ * @param {string} tag SVG元素的标签名
783
+ * @param {object} attrs SVG元素的属性集合
784
+ * @return {object} 返回创建的SVG元素
785
+ * @throws {Error} 如果属性的键或值不是字符串或者为空字符串,会抛出错误
786
+ */
787
+ function makeSVG(tag, attrs) {
788
+ const svgNS = 'http://www.w3.org/2000/svg';
789
+ const el = document.createElementNS(svgNS, tag);
790
+
791
+ for (const [k, v] of Object.entries(attrs)) {
792
+ if (typeof k !== 'string' || k.trim() === '') {
793
+ throw new Error('Invalid attribute key');
794
+ }
795
+ if (typeof v !== 'string' || v.trim() === '') {
796
+ throw new Error('Invalid attribute value');
797
+ }
798
+ el.setAttributeNS(svgNS, k, v);
799
+ }
800
+ return el;
801
+ }
802
+
803
+ const svg = makeSVG("svg", {
804
+ viewBox: `0 0 ${nCount} ${nCount}`,
805
+ width: '100%',
806
+ height: '100%',
807
+ fill: _htOption.colorLight
808
+ });
809
+ svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink");
810
+ _el.appendChild(svg);
811
+
812
+ svg.appendChild(makeSVG("rect", { "fill": _htOption.colorLight, "width": "100%", "height": "100%" }));
813
+ svg.appendChild(makeSVG("rect", { "fill": _htOption.colorDark, "width": "1", "height": "1", "id": "template" }));
814
+
815
+ for (let row = 0; row < nCount; row++) {
816
+ for (let col = 0; col < nCount; col++) {
817
+ if (oQRCode.isDark(row, col)) {
818
+ const childElement = makeSVG("use", { "x": String(col), "y": String(row) });
819
+ childElement.setAttributeNS("http://www.w3.org/1999/xlink", "href", "#template");
820
+ svg.appendChild(childElement);
821
+ }
822
+ }
823
+ }
824
+ };
825
+ /**
826
+ * 清除Drawing对象中的所有子节点
827
+ * @return {void}
828
+ */
829
+ Drawing.prototype.clear = function () {
830
+ while (this._el.hasChildNodes()) {
831
+ // 移除Drawing对象的最后一个子节点
832
+ this._el.removeChild(this._el.lastChild);
833
+ }
834
+ };
835
+ return Drawing;
836
+ })();
837
+
838
+ const useSVG = document.documentElement.tagName.toLowerCase() === "svg";
839
+ let noSVGDrawer = !_isSupportCanvas() ? (function () {
840
+ const Drawing = function (el, htOption) {
841
+ this._el = el;
842
+ this._htOption = htOption;
843
+ };
844
+
845
+ Drawing.prototype.draw = function (oQRCode) {
846
+ const { _htOption, _el } = this;
847
+ const nCount = oQRCode.getModuleCount();
848
+ if (nCount <= 0) {
849
+ throw new Error("Invalid module count: must be greater than zero.");
850
+ }
851
+
852
+ const nWidth = Math.floor(_htOption.width / nCount);
853
+ const nHeight = Math.floor(_htOption.height / nCount);
854
+
855
+ if (nWidth <= 0 || nHeight <= 0) {
856
+ throw new Error("Invalid width or height calculation: must be greater than zero.");
857
+ }
858
+
859
+ const aHTML = ['<table style="border:0;border-collapse:collapse;">'];
860
+
861
+ for (let row = 0; row < nCount; row++) {
862
+ aHTML.push('<tr>');
863
+ for (let col = 0; col < nCount; col++) {
864
+ const isDark = oQRCode.isDark(row, col);
865
+ const { colorDark, colorLight } = _htOption;
866
+ const tdStyle = `border:0;border-collapse:collapse;padding:0;margin:0;width:${nWidth}px;height:${nHeight}px;background-color:${isDark ? colorDark : colorLight};`;
867
+ aHTML.push(`<td style="${tdStyle}"></td>`);
868
+ }
869
+ aHTML.push('</tr>');
870
+ }
871
+
872
+ aHTML.push('</table>');
873
+ _el.innerHTML = aHTML.join('');
874
+
875
+ const elTable = _el.childNodes[0];
876
+ const nLeftMarginTable = (_htOption.width - elTable.offsetWidth) / 2;
877
+ const nTopMarginTable = (_htOption.height - elTable.offsetHeight) / 2;
878
+
879
+ if (nLeftMarginTable > 0 && nTopMarginTable > 0) {
880
+ elTable.style.margin = `${nTopMarginTable}px ${nLeftMarginTable}px`;
881
+ }
882
+ };
883
+
884
+ Drawing.prototype.clear = function () {
885
+ this._el.innerHTML = '';
886
+ };
887
+
888
+ return Drawing;
889
+ })() : (function () {
890
+ function _onMakeImage() {
891
+ this._elImage.src = this._elCanvas.toDataURL("image/png");
892
+ this._elImage.style.display = "block";
893
+ this._elCanvas.style.display = "none";
894
+ }
895
+
896
+ if (this._android && this._android <= 2.1) {
897
+ const factorInner = 1 / window.devicePixelRatio;
898
+ const drawImage = CanvasRenderingContext2D.prototype.drawImage;
899
+ CanvasRenderingContext2D.prototype.drawImage = function (...args) {
900
+ const image = args[0];
901
+ const factor = 2;
902
+ const argsCopy = [...args];
903
+ const isValidImage = typeof image.nodeName === 'string' && /img/i.test(image.nodeName);
904
+ if ( image && isValidImage ) {
905
+ for (let i = 1; i < argsCopy.length; i++) {
906
+ if (typeof argsCopy[i] === 'number') {
907
+ argsCopy[i] *= factor;
908
+ }
909
+ }
910
+ } else {
911
+ const slice = argsCopy.slice(1, 5);
912
+ for (let i = 0; i < slice.length; i++) {
913
+ if (typeof slice[i] === 'number') {
914
+ slice[i] *= factor;
915
+ }
916
+ }
917
+ argsCopy.splice(1, 4, ...slice);
918
+ }
919
+ return drawImage.apply(this, argsCopy);
920
+ };
921
+ }
922
+
923
+ function _safeSetDataURI(fSuccess, fFail) {
924
+ let setDataURI = this;
925
+ setDataURI._fFail = fFail;
926
+ setDataURI._fSuccess = fSuccess;
927
+
928
+ if (setDataURI._bSupportDataURI === null) {
929
+ const element = document.createElement("img");
930
+ const fOnError = function () {
931
+ setDataURI._bSupportDataURI = false;
932
+
933
+ if (setDataURI._fFail) {
934
+ setDataURI._fFail.call(setDataURI);
935
+ }
936
+ };
937
+ const fOnSuccess = function () {
938
+ setDataURI._bSupportDataURI = true;
939
+
940
+ if (setDataURI._fSuccess) {
941
+ setDataURI._fSuccess.call(setDataURI);
942
+ }
943
+ };
944
+
945
+ element.onabort = fOnError;
946
+ element.onerror = fOnError;
947
+ element.onload = fOnSuccess;
948
+ element.src = "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
949
+ } else if (setDataURI._bSupportDataURI === true && setDataURI._fSuccess) {
950
+ setDataURI._fSuccess.call(setDataURI);
951
+ } else if (setDataURI._bSupportDataURI === false && setDataURI._fFail) {
952
+ setDataURI._fFail.call(setDataURI);
953
+ } else {
954
+ throw new Error("makeImage: Invalid state");
955
+ }
956
+ }
957
+
958
+ const Drawing = function (el, htOption) {
959
+ this._bIsPainted = false;
960
+ this._android = _getAndroid();
961
+
962
+ this._htOption = htOption;
963
+ this._elCanvas = document.createElement("canvas");
964
+ this._elCanvas.width = htOption.width;
965
+ this._elCanvas.height = htOption.height;
966
+ el.appendChild(this._elCanvas);
967
+ this._el = el;
968
+ this._oContext = this._elCanvas.getContext("2d");
969
+ this._bIsPainted = false;
970
+ this._elImage = document.createElement("img");
971
+ this._elImage.alt = "";
972
+ this._elImage.style.display = "none";
973
+ this._el.appendChild(this._elImage);
974
+ this._bSupportDataURI = null;
975
+ };
976
+
977
+ Drawing.prototype.draw = function (oQRCode) {
978
+ const _elImage = this._elImage;
979
+ const canvasContext = this._oContext;
980
+ const _htOption = this._htOption;
981
+
982
+ const moduleCount = oQRCode.getModuleCount();
983
+ const moduleWidth = _htOption.width / moduleCount;
984
+ const moduleHeight = _htOption.height / moduleCount;
985
+ const roundedModuleWidth = Math.round(moduleWidth);
986
+ const roundedModuleHeight = Math.round(moduleHeight);
987
+
988
+ _elImage.style.display = "none";
989
+ this.clear();
990
+
991
+ for (let row = 0; row < moduleCount; row++) {
992
+ for (let col = 0; col < moduleCount; col++) {
993
+ const bIsDark = oQRCode.isDark(row, col);
994
+ const nLeft = col * moduleWidth;
995
+ const nTop = row * moduleHeight;
996
+ canvasContext.strokeStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight;
997
+ canvasContext.lineWidth = 1;
998
+ canvasContext.fillStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight;
999
+ canvasContext.fillRect(nLeft, nTop, moduleWidth, moduleHeight);
1000
+
1001
+ canvasContext.strokeRect(
1002
+ Math.floor(nLeft) + 0.5,
1003
+ Math.floor(nTop) + 0.5,
1004
+ roundedModuleWidth,
1005
+ roundedModuleHeight
1006
+ );
1007
+
1008
+ canvasContext.strokeRect(
1009
+ Math.ceil(nLeft) - 0.5,
1010
+ Math.ceil(nTop) - 0.5,
1011
+ roundedModuleWidth,
1012
+ roundedModuleHeight
1013
+ );
1014
+ }
1015
+ }
1016
+
1017
+ this._bIsPainted = true;
1018
+ };
1019
+
1020
+ Drawing.prototype.makeImage = function () {
1021
+ if (this._bIsPainted) {
1022
+ _safeSetDataURI.call(this, _onMakeImage);
1023
+ }
1024
+ };
1025
+
1026
+ Drawing.prototype.isPainted = function () {
1027
+ return this._bIsPainted;
1028
+ };
1029
+
1030
+ Drawing.prototype.clear = function () {
1031
+ this._oContext.clearRect(0, 0, this._elCanvas.width, this._elCanvas.height);
1032
+ this._bIsPainted = false;
1033
+ };
1034
+
1035
+ Drawing.prototype.round = function (inputNumber) {
1036
+ if (!inputNumber) {
1037
+ return inputNumber;
1038
+ }
1039
+
1040
+ return Math.floor(inputNumber * 1000) / 1000;
1041
+ };
1042
+
1043
+ return Drawing;
1044
+ })();
1045
+ let DrawingObj = useSVG ? svgDrawer : noSVGDrawer;
1046
+
1047
+ function _getTypeNumber(sText, nCorrectLevel) {
1048
+ let nType = 1;
1049
+ const length = _getUTF8Length(sText);
1050
+ QRCodeLimitLength = wholeExtension?.QRCodeLimitLength || [];
1051
+
1052
+ for (let i = 0, len = QRCodeLimitLength.length; i <= len; i++) {
1053
+ let nLimit = 0;
1054
+
1055
+ const errorLevelToLimitIndex = {
1056
+ [QRErrorCorrectLevel.L]: 0,
1057
+ [QRErrorCorrectLevel.M]: 1,
1058
+ [QRErrorCorrectLevel.Q]: 2,
1059
+ [QRErrorCorrectLevel.H]: 3
1060
+ };
1061
+
1062
+ nLimit = QRCodeLimitLength[i][errorLevelToLimitIndex[nCorrectLevel]];
1063
+
1064
+ if (length <= nLimit) {
1065
+ break;
1066
+ } else {
1067
+ nType++;
1068
+ }
1069
+ }
1070
+
1071
+ if (nType > QRCodeLimitLength.length) {
1072
+ throw new Error("Too long data");
1073
+ }
1074
+
1075
+ return nType;
1076
+ }
1077
+
1078
+ function _getUTF8Length(sText) {
1079
+ const replacedText = encodeURI(sText).toString().replace(/%[0-9a-fA-F]{2}/g, 'a');
1080
+ return replacedText.length + (replacedText.length !== sText ? 3 : 0);
1081
+ }
1082
+
1083
+ QRCode = function (targetEl, opt, extendJson) {
1084
+ let el = targetEl;
1085
+ let vOption = opt;
1086
+ this._htOption = {
1087
+ width: 257,
1088
+ typeNumber: 1,
1089
+ colorDark: "#000000",
1090
+ height: 257,
1091
+ colorLight: "#ffffff",
1092
+ correctLevel: QRErrorCorrectLevel.H
1093
+ };
1094
+ wholeExtension = extendJson;
1095
+
1096
+ if (typeof vOption === 'string') {
1097
+ vOption = {
1098
+ text: vOption
1099
+ };
1100
+ }
1101
+ if (vOption) {
1102
+ Object.keys(vOption).forEach((key) => {
1103
+ this._htOption[key] = vOption[key];
1104
+ });
1105
+ }
1106
+
1107
+ if (typeof el === "string") {
1108
+ el = document.getElementById(el);
1109
+ }
1110
+
1111
+ if (this._htOption.useSVG) {
1112
+ DrawingObj = svgDrawer;
1113
+ }
1114
+
1115
+ this._android = _getAndroid();
1116
+ this._el = el;
1117
+ this._oQRCode = null;
1118
+ this._oDrawing = new DrawingObj(this._el, this._htOption);
1119
+
1120
+ if (this._htOption.text) {
1121
+ this.makeCode(this._htOption.text);
1122
+ }
1123
+ };
1124
+
1125
+ QRCode.prototype.makeCode = function (paramText) {
1126
+ this._oQRCode = new QRCodeModel(_getTypeNumber(paramText, this._htOption.correctLevel), this._htOption.correctLevel);
1127
+ this._oQRCode.addData(paramText);
1128
+ this._oQRCode.make();
1129
+ this._el.title = paramText;
1130
+ this._oDrawing.draw(this._oQRCode);
1131
+ this.makeImage();
1132
+ };
1133
+
1134
+ QRCode.prototype.makeImage = function () {
1135
+ if (typeof this._oDrawing.makeImage === "function" && (!this._android || this._android >= 3)) {
1136
+ this._oDrawing.makeImage();
1137
+ }
1138
+ };
1139
+
1140
+ QRCode.prototype.clear = function () {
1141
+ this._oDrawing.clear();
1142
+ };
1143
+
1144
+ QRCode.CorrectLevel = QRErrorCorrectLevel;
1145
+ })();