@puresnake/snake-ui 0.0.0-stage → 0.0.2

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.
Files changed (279) hide show
  1. package/LICENSE +21 -0
  2. package/changelog.md +83 -0
  3. package/components/snake-activity-list/snake-activity-list.vue +365 -0
  4. package/components/snake-activity-list/types.ts +29 -0
  5. package/components/snake-auxiliary-segmentation/snake-auxiliary-segmentation.vue +44 -0
  6. package/components/snake-auxiliary-segmentation/types.ts +5 -0
  7. package/components/snake-button/snake-button.vue +357 -0
  8. package/components/snake-button/types.ts +103 -0
  9. package/components/snake-button-sort/snake-button-sort.vue +72 -0
  10. package/components/snake-button-sort/types.ts +22 -0
  11. package/components/snake-calendar/snake-calendar.vue +998 -0
  12. package/components/snake-calendar/types.ts +189 -0
  13. package/components/snake-cell-group/snake-cell-group.vue +55 -0
  14. package/components/snake-cell-group/types.ts +19 -0
  15. package/components/snake-cell-item/snake-cell-item.vue +159 -0
  16. package/components/snake-cell-item/types.ts +66 -0
  17. package/components/snake-checkbox/snake-checkbox.vue +264 -0
  18. package/components/snake-checkbox/types.ts +70 -0
  19. package/components/snake-checkbox-group/snake-checkbox-group.vue +154 -0
  20. package/components/snake-checkbox-group/types.ts +52 -0
  21. package/components/snake-cms/blocks/v2/cms-v2-carousel.vue +87 -0
  22. package/components/snake-cms/blocks/v2/cms-v2-coupon.vue +25 -0
  23. package/components/snake-cms/blocks/v2/cms-v2-custom.vue +12 -0
  24. package/components/snake-cms/blocks/v2/cms-v2-divider.vue +33 -0
  25. package/components/snake-cms/blocks/v2/cms-v2-floatbutton.vue +28 -0
  26. package/components/snake-cms/blocks/v2/cms-v2-goods-list.vue +150 -0
  27. package/components/snake-cms/blocks/v2/cms-v2-hotzone.vue +41 -0
  28. package/components/snake-cms/blocks/v2/cms-v2-images.vue +41 -0
  29. package/components/snake-cms/blocks/v2/cms-v2-magiccube.vue +93 -0
  30. package/components/snake-cms/blocks/v2/cms-v2-notice.vue +80 -0
  31. package/components/snake-cms/blocks/v2/cms-v2-popup.vue +46 -0
  32. package/components/snake-cms/blocks/v2/cms-v2-richtext.vue +18 -0
  33. package/components/snake-cms/blocks/v2/cms-v2-search.vue +115 -0
  34. package/components/snake-cms/blocks/v2/cms-v2-video.vue +25 -0
  35. package/components/snake-cms/blocks/v2/shared.ts +84 -0
  36. package/components/snake-cms/snake-cms.vue +409 -0
  37. package/components/snake-cms/types.ts +50 -0
  38. package/components/snake-commodity-search/snake-commodity-search.vue +234 -0
  39. package/components/snake-commodity-search/types.ts +11 -0
  40. package/components/snake-config-provider/snake-config-provider.vue +61 -0
  41. package/components/snake-config-provider/types.ts +22 -0
  42. package/components/snake-count-down/snake-count-down.vue +231 -0
  43. package/components/snake-count-down/types.ts +103 -0
  44. package/components/snake-coupon/snake-coupon.vue +209 -0
  45. package/components/snake-coupon/types.ts +21 -0
  46. package/components/snake-custom-module/snake-custom-module.vue +25 -0
  47. package/components/snake-custom-module/types.ts +11 -0
  48. package/components/snake-drop-menu/snake-drop-menu.vue +123 -0
  49. package/components/snake-drop-menu/types.ts +95 -0
  50. package/components/snake-drop-menu-item/snake-drop-menu-item.vue +181 -0
  51. package/components/snake-drop-menu-item/types.ts +48 -0
  52. package/components/snake-empty/snake-empty.vue +100 -0
  53. package/components/snake-empty/types.ts +48 -0
  54. package/components/snake-filter-sorts/filter-page.vue +695 -0
  55. package/components/snake-filter-sorts/snake-filter-sorts.vue +185 -0
  56. package/components/snake-filter-sorts/types.ts +57 -0
  57. package/components/snake-floating-bubble/snake-floating-bubble.vue +157 -0
  58. package/components/snake-floating-bubble/types.ts +43 -0
  59. package/components/snake-follow/snake-follow.vue +18 -0
  60. package/components/snake-follow/types.ts +2 -0
  61. package/components/snake-form/snake-form.vue +124 -0
  62. package/components/snake-form/types.ts +96 -0
  63. package/components/snake-form-item/snake-form-item.vue +331 -0
  64. package/components/snake-form-item/types.ts +70 -0
  65. package/components/snake-goods-list/snake-goods-list.vue +792 -0
  66. package/components/snake-goods-list/types.ts +55 -0
  67. package/components/snake-graphic-navigation/snake-graphic-navigation.vue +198 -0
  68. package/components/snake-graphic-navigation/types.ts +43 -0
  69. package/components/snake-icon/icon-names.ts +222 -0
  70. package/components/snake-icon/snake-icon.vue +220 -0
  71. package/components/snake-icon/types.ts +45 -0
  72. package/components/snake-image/snake-image.vue +230 -0
  73. package/components/snake-image/types.ts +70 -0
  74. package/components/snake-images/snake-images.vue +80 -0
  75. package/components/snake-images/types.ts +5 -0
  76. package/components/snake-index-anchor/snake-index-anchor.vue +83 -0
  77. package/components/snake-index-anchor/types.ts +14 -0
  78. package/components/snake-index-list/anchor-registry.ts +80 -0
  79. package/components/snake-index-list/snake-index-list.vue +259 -0
  80. package/components/snake-index-list/types.ts +115 -0
  81. package/components/snake-index-list-panel/snake-index-list-panel.vue +277 -0
  82. package/components/snake-index-list-panel/types.ts +10 -0
  83. package/components/snake-input/snake-input.vue +225 -0
  84. package/components/snake-input/types.ts +99 -0
  85. package/components/snake-jump-applet/snake-jump-applet.vue +99 -0
  86. package/components/snake-jump-applet/types.ts +17 -0
  87. package/components/snake-line/snake-line.vue +42 -0
  88. package/components/snake-line/types.ts +30 -0
  89. package/components/snake-line-progress/snake-line-progress.vue +105 -0
  90. package/components/snake-line-progress/types.ts +26 -0
  91. package/components/snake-link/snake-link.vue +74 -0
  92. package/components/snake-link/types.ts +30 -0
  93. package/components/snake-list-switching/components/tree-select/index.vue +156 -0
  94. package/components/snake-list-switching/snake-list-switching.vue +1285 -0
  95. package/components/snake-list-switching/types.ts +11 -0
  96. package/components/snake-loading/snake-loading.vue +241 -0
  97. package/components/snake-loading/types.ts +34 -0
  98. package/components/snake-loadmore/snake-loadmore.vue +103 -0
  99. package/components/snake-loadmore/types.ts +56 -0
  100. package/components/snake-magic-cube/snake-magic-cube.vue +622 -0
  101. package/components/snake-magic-cube/types.ts +5 -0
  102. package/components/snake-modal/snake-modal.vue +434 -0
  103. package/components/snake-modal/types.ts +71 -0
  104. package/components/snake-navbar/snake-navbar.vue +194 -0
  105. package/components/snake-navbar/types.ts +54 -0
  106. package/components/snake-no-network/snake-no-network.vue +125 -0
  107. package/components/snake-no-network/types.ts +27 -0
  108. package/components/snake-notice/snake-notice.vue +44 -0
  109. package/components/snake-notice/types.ts +5 -0
  110. package/components/snake-notice-bar/snake-notice-bar.vue +336 -0
  111. package/components/snake-notice-bar/types.ts +82 -0
  112. package/components/snake-overlay/snake-overlay.vue +73 -0
  113. package/components/snake-overlay/types.ts +35 -0
  114. package/components/snake-page-popup/snake-page-popup.vue +146 -0
  115. package/components/snake-page-popup/types.ts +28 -0
  116. package/components/snake-parse/audio/audio.vue +286 -0
  117. package/components/snake-parse/audio/context.js +11 -0
  118. package/components/snake-parse/audio/index.js +34 -0
  119. package/components/snake-parse/node/node.vue +830 -0
  120. package/components/snake-parse/parser.js +1480 -0
  121. package/components/snake-parse/snake-parse.vue +548 -0
  122. package/components/snake-parse/static/app-plus/snake-parse/js/handler.js +109 -0
  123. package/components/snake-parse/static/app-plus/snake-parse/js/uni.webview.js +0 -0
  124. package/components/snake-parse/static/app-plus/snake-parse/js/uni.webview.min.js +1 -0
  125. package/components/snake-parse/static/app-plus/snake-parse/local.html +3 -0
  126. package/components/snake-parse/style/index.js +132 -0
  127. package/components/snake-parse/style/parser.js +175 -0
  128. package/components/snake-parse/types.ts +49 -0
  129. package/components/snake-picture-ads/snake-picture-ads.vue +292 -0
  130. package/components/snake-picture-ads/types.ts +5 -0
  131. package/components/snake-popup/snake-popup.vue +348 -0
  132. package/components/snake-popup/types.ts +88 -0
  133. package/components/snake-qr-code/qrcode.ts +1214 -0
  134. package/components/snake-qr-code/snake-qr-code.vue +408 -0
  135. package/components/snake-qr-code/types.ts +78 -0
  136. package/components/snake-radio/snake-radio.vue +271 -0
  137. package/components/snake-radio/types.ts +64 -0
  138. package/components/snake-radio-group/snake-radio-group.vue +98 -0
  139. package/components/snake-radio-group/types.ts +48 -0
  140. package/components/snake-read-more/snake-read-more.vue +140 -0
  141. package/components/snake-read-more/types.ts +53 -0
  142. package/components/snake-rich-text/snake-rich-text.vue +24 -0
  143. package/components/snake-rich-text/types.ts +5 -0
  144. package/components/snake-root-portal/snake-root-portal.vue +92 -0
  145. package/components/snake-root-portal/types.ts +9 -0
  146. package/components/snake-search/snake-search.vue +202 -0
  147. package/components/snake-search/types.ts +84 -0
  148. package/components/snake-select/snake-select.vue +371 -0
  149. package/components/snake-select/types.ts +75 -0
  150. package/components/snake-skeleton/snake-skeleton.vue +104 -0
  151. package/components/snake-skeleton/types.ts +32 -0
  152. package/components/snake-skeleton-item/snake-skeleton-item.vue +90 -0
  153. package/components/snake-skeleton-item/types.ts +30 -0
  154. package/components/snake-slider/snake-slider.vue +249 -0
  155. package/components/snake-slider/types.ts +59 -0
  156. package/components/snake-sticky/snake-sticky.vue +178 -0
  157. package/components/snake-sticky/types.ts +48 -0
  158. package/components/snake-subsection/snake-subsection.vue +188 -0
  159. package/components/snake-subsection/types.ts +54 -0
  160. package/components/snake-swiper/snake-swiper.vue +229 -0
  161. package/components/snake-swiper/types.ts +79 -0
  162. package/components/snake-switch/snake-switch.vue +100 -0
  163. package/components/snake-switch/types.ts +41 -0
  164. package/components/snake-tabbar/snake-tabbar.vue +306 -0
  165. package/components/snake-tabbar/types.ts +93 -0
  166. package/components/snake-tabs/snake-tabs.vue +305 -0
  167. package/components/snake-tabs/types.ts +79 -0
  168. package/components/snake-text/snake-text.vue +292 -0
  169. package/components/snake-text/types.ts +100 -0
  170. package/components/snake-textarea/snake-textarea.vue +171 -0
  171. package/components/snake-textarea/types.ts +79 -0
  172. package/components/snake-toast/snake-toast.vue +330 -0
  173. package/components/snake-toast/types.ts +55 -0
  174. package/components/snake-tooltip/position.ts +33 -0
  175. package/components/snake-tooltip/snake-tooltip.vue +339 -0
  176. package/components/snake-tooltip/types.ts +80 -0
  177. package/components/snake-transition/snake-transition.vue +315 -0
  178. package/components/snake-transition/types.ts +65 -0
  179. package/components/snake-upload/snake-upload.vue +1801 -0
  180. package/components/snake-upload/types.ts +256 -0
  181. package/components/snake-videos/snake-videos.vue +122 -0
  182. package/components/snake-videos/types.ts +7 -0
  183. package/iconfont.css +881 -0
  184. package/index.d.ts +7 -0
  185. package/index.scss +26 -0
  186. package/index.ts +94 -0
  187. package/libs/cms/blocks.ts +103 -0
  188. package/libs/cms/config.ts +25 -0
  189. package/libs/cms/index.ts +7 -0
  190. package/libs/cms/normalize.ts +135 -0
  191. package/libs/cms/resolve.ts +12 -0
  192. package/libs/cms/types.ts +164 -0
  193. package/libs/cms/use-commerce.ts +385 -0
  194. package/libs/cms/v2.ts +193 -0
  195. package/libs/config/index.ts +4 -0
  196. package/libs/config/modal.ts +93 -0
  197. package/libs/config/state.ts +33 -0
  198. package/libs/config/theme.ts +351 -0
  199. package/libs/config/toast.ts +71 -0
  200. package/libs/config/types.ts +135 -0
  201. package/libs/config/zIndex.ts +44 -0
  202. package/libs/css/color.scss +105 -0
  203. package/libs/css/common.scss +171 -0
  204. package/libs/css/style.h5.scss +7 -0
  205. package/libs/css/style.mp.scss +7 -0
  206. package/libs/css/style.theme.scss +70 -0
  207. package/libs/css/style.vue.scss +140 -0
  208. package/libs/form/async-validator.min.d.ts +75 -0
  209. package/libs/form/async-validator.min.js +3 -0
  210. package/libs/form/index.ts +3 -0
  211. package/libs/form/path.ts +40 -0
  212. package/libs/form/types.ts +51 -0
  213. package/libs/form/validate.ts +149 -0
  214. package/libs/foundation.ts +96 -0
  215. package/libs/function/$parent.ts +33 -0
  216. package/libs/function/clipboard.ts +13 -0
  217. package/libs/function/color.ts +55 -0
  218. package/libs/function/debounce.ts +34 -0
  219. package/libs/function/deepClone.ts +30 -0
  220. package/libs/function/deepMerge.ts +25 -0
  221. package/libs/function/emitter.ts +30 -0
  222. package/libs/function/format.ts +27 -0
  223. package/libs/function/getParent.ts +53 -0
  224. package/libs/function/getRect.ts +29 -0
  225. package/libs/function/guid.ts +11 -0
  226. package/libs/function/index.ts +31 -0
  227. package/libs/function/queryParams.ts +73 -0
  228. package/libs/function/random.ts +6 -0
  229. package/libs/function/randomArray.ts +14 -0
  230. package/libs/function/route.ts +155 -0
  231. package/libs/function/sleep.ts +7 -0
  232. package/libs/function/style.ts +58 -0
  233. package/libs/function/styleUtils.ts +83 -0
  234. package/libs/function/system.ts +14 -0
  235. package/libs/function/test.ts +202 -0
  236. package/libs/function/throttle.ts +36 -0
  237. package/libs/function/timeFormat.ts +27 -0
  238. package/libs/function/timeFrom.ts +27 -0
  239. package/libs/function/toast.ts +10 -0
  240. package/libs/function/trim.ts +11 -0
  241. package/libs/function/type2icon.ts +11 -0
  242. package/libs/function/unit.ts +45 -0
  243. package/libs/hooks/index.ts +11 -0
  244. package/libs/hooks/useChildren.ts +125 -0
  245. package/libs/hooks/useColor.ts +5 -0
  246. package/libs/hooks/useDebounce.ts +5 -0
  247. package/libs/hooks/useLocale.ts +13 -0
  248. package/libs/hooks/useModal.ts +131 -0
  249. package/libs/hooks/useParent.ts +62 -0
  250. package/libs/hooks/useRect.ts +44 -0
  251. package/libs/hooks/useRouter.ts +5 -0
  252. package/libs/hooks/useTheme.ts +31 -0
  253. package/libs/hooks/useThrottle.ts +5 -0
  254. package/libs/hooks/useToast.ts +116 -0
  255. package/libs/index.ts +18 -0
  256. package/libs/request/auto-http.ts +1 -0
  257. package/libs/request/index.ts +343 -0
  258. package/libs/runtime/accessors.ts +55 -0
  259. package/libs/runtime/context.ts +40 -0
  260. package/libs/runtime/index.ts +18 -0
  261. package/libs/runtime/types.ts +49 -0
  262. package/libs/util/calendar.ts +6 -0
  263. package/locale/index.ts +130 -0
  264. package/locale/lang/en-US.ts +67 -0
  265. package/locale/lang/zh-CN.ts +67 -0
  266. package/package.json +134 -5
  267. package/plugins/index.ts +1 -0
  268. package/plugins/root/README.md +86 -0
  269. package/plugins/root/index.ts +123 -0
  270. package/plugins/root/page.ts +77 -0
  271. package/plugins/root/root.ts +53 -0
  272. package/plugins/root/utils.ts +137 -0
  273. package/readme.md +79 -0
  274. package/theme.scss +118 -0
  275. package/types/components.d.ts +236 -0
  276. package/types/global.d.ts +21 -0
  277. package/types/index.d.ts +6 -0
  278. package/types/uni-app.d.ts +16 -0
  279. package/README.md +0 -3
@@ -0,0 +1,1214 @@
1
+ /**
2
+ * QRCode for JavaScript —— 纯算法二维码编码器(无 DOM / Canvas 依赖)。
3
+ *
4
+ * 移植自 wot-ui `wd-qr-code` 组件(MIT License),其原始实现基于
5
+ * davidshimjs / Kazuhiko Arase 的 qrcode-generator。算法本体未做改动,
6
+ * 仅按 snake-ui 代码风格重排并裁剪为二维码矩阵生成所需的最小接口。
7
+ */
8
+
9
+ /** 错误纠正等级:L 约 7%、M 约 15%、Q 约 25%、H 约 30%。 */
10
+ const QRErrorCorrectLevel = {
11
+ L: 1,
12
+ M: 0,
13
+ Q: 3,
14
+ H: 2
15
+ };
16
+
17
+ /** 编码模式枚举。 */
18
+ const QRMode = {
19
+ MODE_NUMBER: 1 << 0,
20
+ MODE_ALPHA_NUM: 1 << 1,
21
+ MODE_8BIT_BYTE: 1 << 2,
22
+ MODE_KANJI: 1 << 3
23
+ };
24
+
25
+ /** 掩码模式枚举。 */
26
+ const QRMaskPattern = {
27
+ PATTERN000: 0,
28
+ PATTERN001: 1,
29
+ PATTERN010: 2,
30
+ PATTERN011: 3,
31
+ PATTERN100: 4,
32
+ PATTERN101: 5,
33
+ PATTERN110: 6,
34
+ PATTERN111: 7
35
+ };
36
+
37
+ type QRErrorCorrectLevelValue = (typeof QRErrorCorrectLevel)[keyof typeof QRErrorCorrectLevel];
38
+ type QRModeValue = (typeof QRMode)[keyof typeof QRMode];
39
+ type QRMaskPatternValue = (typeof QRMaskPattern)[keyof typeof QRMaskPattern];
40
+ type QRModule = boolean | null;
41
+ type QRModuleMatrix = QRModule[][];
42
+ type QRCodeDataMatrix = boolean[][];
43
+
44
+ /**
45
+ * QR 码版本(1-40)对应每个纠错等级下的最大数据长度。
46
+ * 列顺序为 [L, M, Q, H]。
47
+ */
48
+ const QRCodeLimitLength = [
49
+ [17, 14, 11, 7],
50
+ [32, 26, 20, 14],
51
+ [53, 42, 32, 24],
52
+ [78, 62, 46, 34],
53
+ [106, 84, 60, 44],
54
+ [134, 106, 74, 58],
55
+ [154, 122, 86, 64],
56
+ [192, 152, 108, 84],
57
+ [230, 180, 130, 98],
58
+ [271, 213, 151, 119],
59
+ [321, 251, 177, 137],
60
+ [367, 287, 203, 155],
61
+ [425, 331, 241, 177],
62
+ [458, 362, 258, 194],
63
+ [520, 412, 292, 220],
64
+ [586, 450, 322, 250],
65
+ [644, 504, 364, 280],
66
+ [718, 560, 394, 310],
67
+ [792, 624, 442, 338],
68
+ [858, 666, 482, 382],
69
+ [929, 711, 509, 403],
70
+ [1003, 779, 565, 439],
71
+ [1091, 857, 611, 461],
72
+ [1171, 911, 661, 511],
73
+ [1273, 997, 715, 535],
74
+ [1367, 1059, 751, 593],
75
+ [1465, 1125, 805, 625],
76
+ [1528, 1190, 868, 658],
77
+ [1628, 1264, 908, 698],
78
+ [1732, 1370, 982, 742],
79
+ [1840, 1452, 1030, 790],
80
+ [1952, 1538, 1112, 842],
81
+ [2068, 1628, 1168, 898],
82
+ [2188, 1722, 1228, 958],
83
+ [2303, 1809, 1283, 983],
84
+ [2431, 1911, 1351, 1051],
85
+ [2563, 1989, 1423, 1093],
86
+ [2699, 2099, 1499, 1139],
87
+ [2809, 2213, 1579, 1219],
88
+ [2953, 2331, 1663, 1273]
89
+ ];
90
+
91
+ /** 8bit 字节模式数据:把字符串按 UTF-8 编码成字节序列。 */
92
+ class QR8bitByte {
93
+ mode: QRModeValue;
94
+ data: string;
95
+ parsedData: number[];
96
+
97
+ constructor(data: string) {
98
+ this.mode = QRMode.MODE_8BIT_BYTE;
99
+ this.data = data;
100
+ this.parsedData = [];
101
+ this._encodeUTF8();
102
+ }
103
+
104
+ _encodeUTF8(): void {
105
+ for (let i = 0, l = this.data.length; i < l; i++) {
106
+ const code = this.data.codePointAt(i);
107
+ const byteArray: number[] = [];
108
+
109
+ if (code === undefined) {
110
+ continue;
111
+ }
112
+
113
+ if (code >= 0x10000) {
114
+ byteArray[0] = 0xf0 | ((code & 0x1c0000) >>> 18);
115
+ byteArray[1] = 0x80 | ((code & 0x3f000) >>> 12);
116
+ byteArray[2] = 0x80 | ((code & 0xfc0) >>> 6);
117
+ byteArray[3] = 0x80 | (code & 0x3f);
118
+ i++;
119
+ } else if (code >= 0x800) {
120
+ byteArray[0] = 0xe0 | ((code & 0xf000) >>> 12);
121
+ byteArray[1] = 0x80 | ((code & 0xfc0) >>> 6);
122
+ byteArray[2] = 0x80 | (code & 0x3f);
123
+ } else if (code >= 0x80) {
124
+ byteArray[0] = 0xc0 | ((code & 0x7c0) >>> 6);
125
+ byteArray[1] = 0x80 | (code & 0x3f);
126
+ } else {
127
+ byteArray[0] = code;
128
+ }
129
+
130
+ this.parsedData.push(...byteArray);
131
+ }
132
+ }
133
+
134
+ getLength(): number {
135
+ return this.parsedData.length;
136
+ }
137
+
138
+ write(buffer: QRBitBuffer): void {
139
+ for (let i = 0, l = this.parsedData.length; i < l; i++) {
140
+ buffer.put(this.parsedData[i], 8);
141
+ }
142
+ }
143
+ }
144
+
145
+ /** 位缓冲区:按位写入数据码流。 */
146
+ class QRBitBuffer {
147
+ buffer: number[];
148
+ length: number;
149
+
150
+ constructor() {
151
+ this.buffer = [];
152
+ this.length = 0;
153
+ }
154
+
155
+ get(index: number): boolean {
156
+ const bufIndex = Math.floor(index / 8);
157
+ return ((this.buffer[bufIndex] >>> (7 - (index % 8))) & 1) === 1;
158
+ }
159
+
160
+ put(num: number, length: number): void {
161
+ for (let i = 0; i < length; i++) {
162
+ this.putBit(((num >>> (length - i - 1)) & 1) === 1);
163
+ }
164
+ }
165
+
166
+ getLengthInBits(): number {
167
+ return this.length;
168
+ }
169
+
170
+ putBit(bit: boolean): void {
171
+ const bufIndex = Math.floor(this.length / 8);
172
+ if (this.buffer.length <= bufIndex) {
173
+ this.buffer.push(0);
174
+ }
175
+ if (bit) {
176
+ this.buffer[bufIndex] |= 0x80 >>> (this.length % 8);
177
+ }
178
+ this.length++;
179
+ }
180
+ }
181
+
182
+ /** GF(256) 域上的对数 / 指数表,供 Reed-Solomon 多项式运算使用。 */
183
+ const QRMath = {
184
+ EXP_TABLE: new Array<number>(256),
185
+ LOG_TABLE: new Array<number>(256),
186
+
187
+ init(): void {
188
+ for (let i = 0; i < 8; i++) {
189
+ QRMath.EXP_TABLE[i] = 1 << i;
190
+ }
191
+ for (let i = 8; i < 256; i++) {
192
+ QRMath.EXP_TABLE[i] =
193
+ QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^ QRMath.EXP_TABLE[i - 6] ^ QRMath.EXP_TABLE[i - 8];
194
+ }
195
+
196
+ for (let i = 0; i < 255; i++) {
197
+ QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i;
198
+ }
199
+ },
200
+
201
+ glog(n: number): number {
202
+ if (n < 1) {
203
+ throw new Error(`glog(${n})`);
204
+ }
205
+ return QRMath.LOG_TABLE[n];
206
+ },
207
+
208
+ gexp(n: number): number {
209
+ while (n < 0) n += 255;
210
+ while (n >= 256) n -= 255;
211
+ return QRMath.EXP_TABLE[n];
212
+ }
213
+ };
214
+
215
+ QRMath.init();
216
+
217
+ /** GF(256) 域上的多项式。 */
218
+ class QRPolynomial {
219
+ num: number[];
220
+
221
+ constructor(num: number[], shift: number) {
222
+ if (num.length === undefined) {
223
+ throw new Error(`${num.length}/${shift}`);
224
+ }
225
+
226
+ let offset = 0;
227
+ while (offset < num.length && num[offset] === 0) {
228
+ offset++;
229
+ }
230
+
231
+ this.num = new Array<number>(num.length - offset + shift);
232
+ for (let i = 0; i < num.length - offset; i++) {
233
+ this.num[i] = num[i + offset];
234
+ }
235
+ }
236
+
237
+ get(index: number): number {
238
+ return this.num[index];
239
+ }
240
+
241
+ getLength(): number {
242
+ return this.num.length;
243
+ }
244
+
245
+ multiply(other: QRPolynomial): QRPolynomial {
246
+ const num: number[] = new Array<number>(this.getLength() + other.getLength() - 1).fill(0);
247
+ for (let i = 0; i < this.getLength(); i++) {
248
+ const ai = QRMath.glog(this.get(i));
249
+ for (let j = 0; j < other.getLength(); j++) {
250
+ num[i + j] ^= QRMath.gexp(ai + QRMath.glog(other.get(j)));
251
+ }
252
+ }
253
+ return new QRPolynomial(num, 0);
254
+ }
255
+
256
+ mod(other: QRPolynomial): QRPolynomial {
257
+ if (this.getLength() - other.getLength() < 0) {
258
+ return this;
259
+ }
260
+
261
+ const ratio = QRMath.glog(this.get(0)) - QRMath.glog(other.get(0));
262
+ const num: number[] = new Array<number>(this.getLength());
263
+ for (let i = 0; i < this.getLength(); i++) {
264
+ num[i] = this.get(i);
265
+ }
266
+
267
+ for (let i = 0; i < other.getLength(); i++) {
268
+ num[i] ^= QRMath.gexp(QRMath.glog(other.get(i)) + ratio);
269
+ }
270
+
271
+ return new QRPolynomial(num, 0).mod(other);
272
+ }
273
+ }
274
+
275
+ /** Reed-Solomon 纠错块。 */
276
+ class QRRSBlock {
277
+ static RS_BLOCK_TABLE: number[][];
278
+
279
+ totalCount: number;
280
+ dataCount: number;
281
+
282
+ constructor(totalCount: number, dataCount: number) {
283
+ this.totalCount = totalCount;
284
+ this.dataCount = dataCount;
285
+ }
286
+
287
+ static getRSBlocks(typeNumber: number, errorCorrectLevel: QRErrorCorrectLevelValue): QRRSBlock[] {
288
+ const rsBlock = QRRSBlock.getRsBlockTable(typeNumber, errorCorrectLevel);
289
+ if (!rsBlock) {
290
+ throw new Error(`bad rs block @ typeNumber:${typeNumber}/errorCorrectLevel:${errorCorrectLevel}`);
291
+ }
292
+
293
+ const length = rsBlock.length / 3;
294
+ const list: QRRSBlock[] = [];
295
+
296
+ for (let i = 0; i < length; i++) {
297
+ const count = rsBlock[i * 3];
298
+ const totalCount = rsBlock[i * 3 + 1];
299
+ const dataCount = rsBlock[i * 3 + 2];
300
+
301
+ for (let j = 0; j < count; j++) {
302
+ list.push(new QRRSBlock(totalCount, dataCount));
303
+ }
304
+ }
305
+
306
+ return list;
307
+ }
308
+
309
+ static getRsBlockTable(typeNumber: number, errorCorrectLevel: QRErrorCorrectLevelValue): number[] | undefined {
310
+ const index = (typeNumber - 1) * 4;
311
+ switch (errorCorrectLevel) {
312
+ case QRErrorCorrectLevel.L:
313
+ return QRRSBlock.RS_BLOCK_TABLE[index];
314
+ case QRErrorCorrectLevel.M:
315
+ return QRRSBlock.RS_BLOCK_TABLE[index + 1];
316
+ case QRErrorCorrectLevel.Q:
317
+ return QRRSBlock.RS_BLOCK_TABLE[index + 2];
318
+ case QRErrorCorrectLevel.H:
319
+ return QRRSBlock.RS_BLOCK_TABLE[index + 3];
320
+ default:
321
+ return undefined;
322
+ }
323
+ }
324
+ }
325
+
326
+ // 各版本的 RS 块表:每个版本 4 行,按 L / M / Q / H 排列。
327
+ QRRSBlock.RS_BLOCK_TABLE = [
328
+ // 1
329
+ [1, 26, 19],
330
+ [1, 26, 16],
331
+ [1, 26, 13],
332
+ [1, 26, 9],
333
+
334
+ // 2
335
+ [1, 44, 34],
336
+ [1, 44, 28],
337
+ [1, 44, 22],
338
+ [1, 44, 16],
339
+
340
+ // 3
341
+ [1, 70, 55],
342
+ [1, 70, 44],
343
+ [2, 35, 17],
344
+ [2, 35, 13],
345
+
346
+ // 4
347
+ [1, 100, 80],
348
+ [2, 50, 32],
349
+ [2, 50, 24],
350
+ [4, 25, 9],
351
+
352
+ // 5
353
+ [1, 134, 108],
354
+ [2, 67, 43],
355
+ [2, 33, 15, 2, 34, 16],
356
+ [2, 33, 11, 2, 34, 12],
357
+
358
+ // 6
359
+ [2, 86, 68],
360
+ [4, 43, 27],
361
+ [4, 43, 19],
362
+ [4, 43, 15],
363
+
364
+ // 7
365
+ [2, 98, 78],
366
+ [4, 49, 31],
367
+ [2, 32, 14, 4, 33, 15],
368
+ [4, 39, 13, 1, 40, 14],
369
+
370
+ // 8
371
+ [2, 121, 97],
372
+ [2, 60, 38, 2, 61, 39],
373
+ [4, 40, 18, 2, 41, 19],
374
+ [4, 40, 14, 2, 41, 15],
375
+
376
+ // 9
377
+ [2, 146, 116],
378
+ [3, 58, 36, 2, 59, 37],
379
+ [4, 36, 16, 4, 37, 17],
380
+ [4, 36, 12, 4, 37, 13],
381
+
382
+ // 10
383
+ [2, 86, 68, 2, 87, 69],
384
+ [4, 69, 43, 1, 70, 44],
385
+ [6, 43, 19, 2, 44, 20],
386
+ [6, 43, 15, 2, 44, 16],
387
+
388
+ // 11
389
+ [4, 101, 81],
390
+ [1, 80, 50, 4, 81, 51],
391
+ [4, 50, 22, 4, 51, 23],
392
+ [3, 36, 12, 8, 37, 13],
393
+
394
+ // 12
395
+ [2, 116, 92, 2, 117, 93],
396
+ [6, 58, 36, 2, 59, 37],
397
+ [4, 46, 20, 6, 47, 21],
398
+ [7, 42, 14, 4, 43, 15],
399
+
400
+ // 13
401
+ [4, 133, 107],
402
+ [8, 59, 37, 1, 60, 38],
403
+ [8, 44, 20, 4, 45, 21],
404
+ [12, 33, 11, 4, 34, 12],
405
+
406
+ // 14
407
+ [3, 145, 115, 1, 146, 116],
408
+ [4, 64, 40, 5, 65, 41],
409
+ [11, 36, 16, 5, 37, 17],
410
+ [11, 36, 12, 5, 37, 13],
411
+
412
+ // 15
413
+ [5, 109, 87, 1, 110, 88],
414
+ [5, 65, 41, 5, 66, 42],
415
+ [5, 54, 24, 7, 55, 25],
416
+ [11, 36, 12, 7, 37, 13],
417
+
418
+ // 16
419
+ [5, 122, 98, 1, 123, 99],
420
+ [7, 73, 45, 3, 74, 46],
421
+ [15, 43, 19, 2, 44, 20],
422
+ [3, 45, 15, 13, 46, 16],
423
+
424
+ // 17
425
+ [1, 135, 107, 5, 136, 108],
426
+ [10, 74, 46, 1, 75, 47],
427
+ [1, 50, 22, 15, 51, 23],
428
+ [2, 42, 14, 17, 43, 15],
429
+
430
+ // 18
431
+ [5, 150, 120, 1, 151, 121],
432
+ [9, 69, 43, 4, 70, 44],
433
+ [17, 50, 22, 1, 51, 23],
434
+ [2, 42, 14, 19, 43, 15],
435
+
436
+ // 19
437
+ [3, 141, 113, 4, 142, 114],
438
+ [3, 70, 44, 11, 71, 45],
439
+ [17, 47, 21, 4, 48, 22],
440
+ [9, 39, 13, 16, 40, 14],
441
+
442
+ // 20
443
+ [3, 135, 107, 5, 136, 108],
444
+ [3, 67, 41, 13, 68, 42],
445
+ [15, 54, 24, 5, 55, 25],
446
+ [15, 43, 15, 10, 44, 16],
447
+
448
+ // 21
449
+ [4, 144, 116, 4, 145, 117],
450
+ [17, 68, 42],
451
+ [17, 50, 22, 6, 51, 23],
452
+ [19, 46, 16, 6, 47, 17],
453
+
454
+ // 22
455
+ [2, 139, 111, 7, 140, 112],
456
+ [17, 74, 46],
457
+ [7, 54, 24, 16, 55, 25],
458
+ [34, 37, 13],
459
+
460
+ // 23
461
+ [4, 151, 121, 5, 152, 122],
462
+ [4, 75, 47, 14, 76, 48],
463
+ [11, 54, 24, 14, 55, 25],
464
+ [16, 45, 15, 14, 46, 16],
465
+
466
+ // 24
467
+ [6, 147, 117, 4, 148, 118],
468
+ [6, 73, 45, 14, 74, 46],
469
+ [11, 54, 24, 16, 55, 25],
470
+ [30, 46, 16, 2, 47, 17],
471
+
472
+ // 25
473
+ [8, 132, 106, 4, 133, 107],
474
+ [8, 75, 47, 13, 76, 48],
475
+ [7, 54, 24, 22, 55, 25],
476
+ [22, 45, 15, 13, 46, 16],
477
+
478
+ // 26
479
+ [10, 142, 114, 2, 143, 115],
480
+ [19, 74, 46, 4, 75, 47],
481
+ [28, 50, 22, 6, 51, 23],
482
+ [33, 46, 16, 4, 47, 17],
483
+
484
+ // 27
485
+ [8, 152, 122, 4, 153, 123],
486
+ [22, 73, 45, 3, 74, 46],
487
+ [8, 53, 23, 26, 54, 24],
488
+ [12, 45, 15, 28, 46, 16],
489
+
490
+ // 28
491
+ [3, 147, 117, 10, 148, 118],
492
+ [3, 73, 45, 23, 74, 46],
493
+ [4, 54, 24, 31, 55, 25],
494
+ [11, 45, 15, 31, 46, 16],
495
+
496
+ // 29
497
+ [7, 146, 116, 7, 147, 117],
498
+ [21, 73, 45, 7, 74, 46],
499
+ [1, 53, 23, 37, 54, 24],
500
+ [19, 45, 15, 26, 46, 16],
501
+
502
+ // 30
503
+ [5, 145, 115, 10, 146, 116],
504
+ [19, 75, 47, 10, 76, 48],
505
+ [15, 54, 24, 25, 55, 25],
506
+ [23, 45, 15, 25, 46, 16],
507
+
508
+ // 31
509
+ [13, 145, 115, 3, 146, 116],
510
+ [2, 74, 46, 29, 75, 47],
511
+ [42, 54, 24, 1, 55, 25],
512
+ [23, 45, 15, 28, 46, 16],
513
+
514
+ // 32
515
+ [17, 145, 115],
516
+ [10, 74, 46, 23, 75, 47],
517
+ [10, 54, 24, 35, 55, 25],
518
+ [19, 45, 15, 35, 46, 16],
519
+
520
+ // 33
521
+ [17, 145, 115, 1, 146, 116],
522
+ [14, 74, 46, 21, 75, 47],
523
+ [29, 54, 24, 19, 55, 25],
524
+ [11, 45, 15, 46, 46, 16],
525
+
526
+ // 34
527
+ [13, 145, 115, 6, 146, 116],
528
+ [14, 74, 46, 23, 75, 47],
529
+ [44, 54, 24, 7, 55, 25],
530
+ [59, 46, 16, 1, 47, 17],
531
+
532
+ // 35
533
+ [12, 151, 121, 7, 152, 122],
534
+ [12, 75, 47, 26, 76, 48],
535
+ [39, 54, 24, 14, 55, 25],
536
+ [22, 45, 15, 41, 46, 16],
537
+
538
+ // 36
539
+ [6, 151, 121, 14, 152, 122],
540
+ [6, 75, 47, 34, 76, 48],
541
+ [46, 54, 24, 10, 55, 25],
542
+ [2, 45, 15, 64, 46, 16],
543
+
544
+ // 37
545
+ [17, 152, 122, 4, 153, 123],
546
+ [29, 74, 46, 14, 75, 47],
547
+ [49, 54, 24, 10, 55, 25],
548
+ [24, 45, 15, 46, 46, 16],
549
+
550
+ // 38
551
+ [4, 152, 122, 18, 153, 123],
552
+ [13, 74, 46, 32, 75, 47],
553
+ [48, 54, 24, 14, 55, 25],
554
+ [42, 45, 15, 32, 46, 16],
555
+
556
+ // 39
557
+ [20, 147, 117, 4, 148, 118],
558
+ [40, 75, 47, 7, 76, 48],
559
+ [43, 54, 24, 22, 55, 25],
560
+ [10, 45, 15, 67, 46, 16],
561
+
562
+ // 40
563
+ [19, 148, 118, 6, 149, 119],
564
+ [18, 75, 47, 31, 76, 48],
565
+ [34, 54, 24, 34, 55, 25],
566
+ [20, 45, 15, 61, 46, 16]
567
+ ];
568
+
569
+ /** QR 码工具函数:BCH 校验、掩码评估、格式信息等。 */
570
+ const QRUtil = {
571
+ PATTERN_POSITION_TABLE: [
572
+ [],
573
+ [6, 18],
574
+ [6, 22],
575
+ [6, 26],
576
+ [6, 30],
577
+ [6, 34],
578
+ [6, 22, 38],
579
+ [6, 24, 42],
580
+ [6, 26, 46],
581
+ [6, 28, 50],
582
+ [6, 30, 54],
583
+ [6, 32, 58],
584
+ [6, 34, 62],
585
+ [6, 26, 46, 66],
586
+ [6, 26, 48, 70],
587
+ [6, 26, 50, 74],
588
+ [6, 30, 54, 78],
589
+ [6, 30, 56, 82],
590
+ [6, 30, 58, 86],
591
+ [6, 34, 62, 90],
592
+ [6, 28, 50, 72, 94],
593
+ [6, 26, 50, 74, 98],
594
+ [6, 30, 54, 78, 102],
595
+ [6, 28, 54, 80, 106],
596
+ [6, 32, 58, 84, 110],
597
+ [6, 30, 58, 86, 114],
598
+ [6, 34, 62, 90, 118],
599
+ [6, 26, 50, 74, 98, 122],
600
+ [6, 30, 54, 78, 102, 126],
601
+ [6, 26, 52, 78, 104, 130],
602
+ [6, 30, 56, 82, 108, 134],
603
+ [6, 34, 60, 86, 112, 138],
604
+ [6, 30, 58, 86, 114, 142],
605
+ [6, 34, 62, 90, 118, 146],
606
+ [6, 30, 54, 78, 102, 126, 150],
607
+ [6, 24, 50, 76, 102, 128, 154],
608
+ [6, 28, 54, 80, 106, 132, 158],
609
+ [6, 32, 58, 84, 110, 136, 162],
610
+ [6, 26, 54, 82, 110, 138, 166],
611
+ [6, 30, 58, 86, 114, 142, 170]
612
+ ],
613
+ G15: (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0),
614
+ G18: (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0),
615
+ G15_MASK: (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1),
616
+
617
+ getBCHTypeInfo(data: number): number {
618
+ let d = data << 10;
619
+ while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) {
620
+ d ^= QRUtil.G15 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15));
621
+ }
622
+ return ((data << 10) | d) ^ QRUtil.G15_MASK;
623
+ },
624
+
625
+ getBCHTypeNumber(data: number): number {
626
+ let d = data << 12;
627
+ while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) {
628
+ d ^= QRUtil.G18 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18));
629
+ }
630
+ return (data << 12) | d;
631
+ },
632
+
633
+ getBCHDigit(data: number): number {
634
+ let digit = 0;
635
+ while (data !== 0) {
636
+ digit++;
637
+ data >>>= 1;
638
+ }
639
+ return digit;
640
+ },
641
+
642
+ getPatternPosition(typeNumber: number): number[] {
643
+ return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1];
644
+ },
645
+
646
+ getMask(maskPattern: QRMaskPatternValue, i: number, j: number): boolean {
647
+ switch (maskPattern) {
648
+ case QRMaskPattern.PATTERN000:
649
+ return (i + j) % 2 === 0;
650
+ case QRMaskPattern.PATTERN001:
651
+ return i % 2 === 0;
652
+ case QRMaskPattern.PATTERN010:
653
+ return j % 3 === 0;
654
+ case QRMaskPattern.PATTERN011:
655
+ return (i + j) % 3 === 0;
656
+ case QRMaskPattern.PATTERN100:
657
+ return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 === 0;
658
+ case QRMaskPattern.PATTERN101:
659
+ return ((i * j) % 2) + ((i * j) % 3) === 0;
660
+ case QRMaskPattern.PATTERN110:
661
+ return (((i * j) % 2) + ((i * j) % 3)) % 2 === 0;
662
+ case QRMaskPattern.PATTERN111:
663
+ return (((i * j) % 3) + ((i + j) % 2)) % 2 === 0;
664
+ default:
665
+ throw new Error(`bad maskPattern:${maskPattern}`);
666
+ }
667
+ },
668
+
669
+ getErrorCorrectPolynomial(errorCorrectLength: number): QRPolynomial {
670
+ let a = new QRPolynomial([1], 0);
671
+ for (let i = 0; i < errorCorrectLength; i++) {
672
+ a = a.multiply(new QRPolynomial([1, QRMath.gexp(i)], 0));
673
+ }
674
+ return a;
675
+ },
676
+
677
+ getLengthInBits(mode: QRModeValue, type: number): number {
678
+ if (1 <= type && type < 10) {
679
+ switch (mode) {
680
+ case QRMode.MODE_NUMBER:
681
+ return 10;
682
+ case QRMode.MODE_ALPHA_NUM:
683
+ return 9;
684
+ case QRMode.MODE_8BIT_BYTE:
685
+ return 8;
686
+ case QRMode.MODE_KANJI:
687
+ return 8;
688
+ default:
689
+ throw new Error(`mode:${mode}`);
690
+ }
691
+ } else if (type < 27) {
692
+ switch (mode) {
693
+ case QRMode.MODE_NUMBER:
694
+ return 12;
695
+ case QRMode.MODE_ALPHA_NUM:
696
+ return 11;
697
+ case QRMode.MODE_8BIT_BYTE:
698
+ return 16;
699
+ case QRMode.MODE_KANJI:
700
+ return 10;
701
+ default:
702
+ throw new Error(`mode:${mode}`);
703
+ }
704
+ } else if (type < 41) {
705
+ switch (mode) {
706
+ case QRMode.MODE_NUMBER:
707
+ return 14;
708
+ case QRMode.MODE_ALPHA_NUM:
709
+ return 13;
710
+ case QRMode.MODE_8BIT_BYTE:
711
+ return 16;
712
+ case QRMode.MODE_KANJI:
713
+ return 12;
714
+ default:
715
+ throw new Error(`mode:${mode}`);
716
+ }
717
+ } else {
718
+ throw new Error(`type:${type}`);
719
+ }
720
+ },
721
+
722
+ getLostPoint(qrCode: QRCodeModel): number {
723
+ const moduleCount = qrCode.getModuleCount();
724
+ let lostPoint = 0;
725
+
726
+ for (let row = 0; row < moduleCount; row++) {
727
+ for (let col = 0; col < moduleCount; col++) {
728
+ let sameCount = 0;
729
+ const dark = qrCode.isDark(row, col);
730
+
731
+ for (let r = -1; r <= 1; r++) {
732
+ if (row + r < 0 || moduleCount <= row + r) continue;
733
+ for (let c = -1; c <= 1; c++) {
734
+ if (col + c < 0 || moduleCount <= col + c) continue;
735
+ if (r === 0 && c === 0) continue;
736
+ if (dark === qrCode.isDark(row + r, col + c)) {
737
+ sameCount++;
738
+ }
739
+ }
740
+ }
741
+
742
+ if (sameCount > 5) {
743
+ lostPoint += 3 + sameCount - 5;
744
+ }
745
+ }
746
+ }
747
+
748
+ for (let row = 0; row < moduleCount - 1; row++) {
749
+ for (let col = 0; col < moduleCount - 1; col++) {
750
+ let count = 0;
751
+ if (qrCode.isDark(row, col)) count++;
752
+ if (qrCode.isDark(row + 1, col)) count++;
753
+ if (qrCode.isDark(row, col + 1)) count++;
754
+ if (qrCode.isDark(row + 1, col + 1)) count++;
755
+ if (count === 0 || count === 4) {
756
+ lostPoint += 3;
757
+ }
758
+ }
759
+ }
760
+
761
+ for (let row = 0; row < moduleCount; row++) {
762
+ for (let col = 0; col < moduleCount - 6; col++) {
763
+ if (
764
+ qrCode.isDark(row, col) &&
765
+ !qrCode.isDark(row, col + 1) &&
766
+ qrCode.isDark(row, col + 2) &&
767
+ qrCode.isDark(row, col + 3) &&
768
+ qrCode.isDark(row, col + 4) &&
769
+ !qrCode.isDark(row, col + 5) &&
770
+ qrCode.isDark(row, col + 6)
771
+ ) {
772
+ lostPoint += 40;
773
+ }
774
+ }
775
+ }
776
+
777
+ for (let col = 0; col < moduleCount; col++) {
778
+ for (let row = 0; row < moduleCount - 6; row++) {
779
+ if (
780
+ qrCode.isDark(row, col) &&
781
+ !qrCode.isDark(row + 1, col) &&
782
+ qrCode.isDark(row + 2, col) &&
783
+ qrCode.isDark(row + 3, col) &&
784
+ qrCode.isDark(row + 4, col) &&
785
+ !qrCode.isDark(row + 5, col) &&
786
+ qrCode.isDark(row + 6, col)
787
+ ) {
788
+ lostPoint += 40;
789
+ }
790
+ }
791
+ }
792
+
793
+ let darkCount = 0;
794
+ for (let col = 0; col < moduleCount; col++) {
795
+ for (let row = 0; row < moduleCount; row++) {
796
+ if (qrCode.isDark(row, col)) {
797
+ darkCount++;
798
+ }
799
+ }
800
+ }
801
+
802
+ const ratio = Math.abs((100 * darkCount) / moduleCount / moduleCount - 50) / 5;
803
+ lostPoint += ratio * 10;
804
+
805
+ return lostPoint;
806
+ }
807
+ };
808
+
809
+ /** 二维码数据模型:负责矩阵构建与掩码选择。 */
810
+ class QRCodeModel {
811
+ static PAD0: number;
812
+ static PAD1: number;
813
+
814
+ typeNumber: number;
815
+ errorCorrectLevel: QRErrorCorrectLevelValue;
816
+ modules: QRModuleMatrix | null;
817
+ moduleCount: number;
818
+ dataCache: number[] | null;
819
+ dataList: QR8bitByte[];
820
+
821
+ constructor(typeNumber: number, errorCorrectLevel: QRErrorCorrectLevelValue) {
822
+ this.typeNumber = typeNumber;
823
+ this.errorCorrectLevel = errorCorrectLevel;
824
+ this.modules = null;
825
+ this.moduleCount = 0;
826
+ this.dataCache = null;
827
+ this.dataList = [];
828
+ }
829
+
830
+ addData(data: string): void {
831
+ const newData = new QR8bitByte(data);
832
+ this.dataList.push(newData);
833
+ this.dataCache = null;
834
+ }
835
+
836
+ isDark(row: number, col: number): boolean {
837
+ if (row < 0 || this.moduleCount <= row || col < 0 || this.moduleCount <= col) {
838
+ throw new Error(`${row},${col}`);
839
+ }
840
+ return this.modules![row][col] === true;
841
+ }
842
+
843
+ getModuleCount(): number {
844
+ return this.moduleCount;
845
+ }
846
+
847
+ make(): void {
848
+ this.makeImpl(false, this.getBestMaskPattern());
849
+ }
850
+
851
+ makeImpl(test: boolean, maskPattern: QRMaskPatternValue): void {
852
+ this.moduleCount = this.typeNumber * 4 + 17;
853
+ this.modules = new Array(this.moduleCount);
854
+
855
+ for (let row = 0; row < this.moduleCount; row++) {
856
+ this.modules[row] = new Array(this.moduleCount);
857
+ for (let col = 0; col < this.moduleCount; col++) {
858
+ this.modules[row][col] = null;
859
+ }
860
+ }
861
+
862
+ this.setupPositionProbePattern(0, 0);
863
+ this.setupPositionProbePattern(this.moduleCount - 7, 0);
864
+ this.setupPositionProbePattern(0, this.moduleCount - 7);
865
+ this.setupPositionAdjustPattern();
866
+ this.setupTimingPattern();
867
+ this.setupTypeInfo(test, maskPattern);
868
+
869
+ if (this.typeNumber >= 7) {
870
+ this.setupTypeNumber(test);
871
+ }
872
+
873
+ if (this.dataCache === null) {
874
+ this.dataCache = QRCodeModel.createData(this.typeNumber, this.errorCorrectLevel, this.dataList);
875
+ }
876
+
877
+ this.mapData(this.dataCache, maskPattern);
878
+ }
879
+
880
+ setupPositionProbePattern(row: number, col: number): void {
881
+ for (let r = -1; r <= 7; r++) {
882
+ if (row + r <= -1 || this.moduleCount <= row + r) continue;
883
+ for (let c = -1; c <= 7; c++) {
884
+ if (col + c <= -1 || this.moduleCount <= col + c) continue;
885
+
886
+ const isBorder =
887
+ (0 <= r && r <= 6 && (c === 0 || c === 6)) ||
888
+ (0 <= c && c <= 6 && (r === 0 || r === 6)) ||
889
+ (2 <= r && r <= 4 && 2 <= c && c <= 4);
890
+
891
+ this.modules![row + r][col + c] = isBorder;
892
+ }
893
+ }
894
+ }
895
+
896
+ getBestMaskPattern(): QRMaskPatternValue {
897
+ let minLostPoint = 0;
898
+ let pattern = 0;
899
+
900
+ for (let i = 0; i < 8; i++) {
901
+ this.makeImpl(true, i);
902
+ const lostPoint = QRUtil.getLostPoint(this);
903
+
904
+ if (i === 0 || minLostPoint > lostPoint) {
905
+ minLostPoint = lostPoint;
906
+ pattern = i;
907
+ }
908
+ }
909
+
910
+ return pattern;
911
+ }
912
+
913
+ setupTimingPattern(): void {
914
+ for (let r = 8; r < this.moduleCount - 8; r++) {
915
+ if (this.modules![r][6] !== null) continue;
916
+ this.modules![r][6] = r % 2 === 0;
917
+ }
918
+
919
+ for (let c = 8; c < this.moduleCount - 8; c++) {
920
+ if (this.modules![6][c] !== null) continue;
921
+ this.modules![6][c] = c % 2 === 0;
922
+ }
923
+ }
924
+
925
+ setupPositionAdjustPattern(): void {
926
+ const pos = QRUtil.getPatternPosition(this.typeNumber);
927
+
928
+ for (let i = 0; i < pos.length; i++) {
929
+ for (let j = 0; j < pos.length; j++) {
930
+ const row = pos[i];
931
+ const col = pos[j];
932
+
933
+ if (this.modules![row][col] !== null) continue;
934
+
935
+ for (let r = -2; r <= 2; r++) {
936
+ for (let c = -2; c <= 2; c++) {
937
+ const isBorder = r === -2 || r === 2 || c === -2 || c === 2 || (r === 0 && c === 0);
938
+ this.modules![row + r][col + c] = isBorder;
939
+ }
940
+ }
941
+ }
942
+ }
943
+ }
944
+
945
+ setupTypeNumber(test: boolean): void {
946
+ const bits = QRUtil.getBCHTypeNumber(this.typeNumber);
947
+
948
+ for (let i = 0; i < 18; i++) {
949
+ const mod = !test && ((bits >> i) & 1) === 1;
950
+ this.modules![Math.floor(i / 3)][(i % 3) + this.moduleCount - 8 - 3] = mod;
951
+ this.modules![(i % 3) + this.moduleCount - 8 - 3][Math.floor(i / 3)] = mod;
952
+ }
953
+ }
954
+
955
+ setupTypeInfo(test: boolean, maskPattern: QRMaskPatternValue): void {
956
+ const data = (this.errorCorrectLevel << 3) | maskPattern;
957
+ const bits = QRUtil.getBCHTypeInfo(data);
958
+
959
+ for (let i = 0; i < 15; i++) {
960
+ const mod = !test && ((bits >> i) & 1) === 1;
961
+ if (i < 6) {
962
+ this.modules![i][8] = mod;
963
+ } else if (i < 8) {
964
+ this.modules![i + 1][8] = mod;
965
+ } else {
966
+ this.modules![this.moduleCount - 15 + i][8] = mod;
967
+ }
968
+ }
969
+
970
+ for (let i = 0; i < 15; i++) {
971
+ const mod = !test && ((bits >> i) & 1) === 1;
972
+ if (i < 8) {
973
+ this.modules![8][this.moduleCount - i - 1] = mod;
974
+ } else if (i < 9) {
975
+ this.modules![8][15 - i - 1 + 1] = mod;
976
+ } else {
977
+ this.modules![8][15 - i - 1] = mod;
978
+ }
979
+ }
980
+
981
+ this.modules![this.moduleCount - 8][8] = !test;
982
+ }
983
+
984
+ mapData(data: number[], maskPattern: QRMaskPatternValue): void {
985
+ let inc = -1;
986
+ let row = this.moduleCount - 1;
987
+ let bitIndex = 7;
988
+ let byteIndex = 0;
989
+
990
+ for (let col = this.moduleCount - 1; col > 0; col -= 2) {
991
+ if (col === 6) col--;
992
+
993
+ while (row >= 0 && row < this.moduleCount) {
994
+ for (let c = 0; c < 2; c++) {
995
+ if (this.modules![row][col - c] === null) {
996
+ let dark = false;
997
+
998
+ if (byteIndex < data.length) {
999
+ dark = ((data[byteIndex] >>> bitIndex) & 1) === 1;
1000
+ }
1001
+
1002
+ const mask = QRUtil.getMask(maskPattern, row, col - c);
1003
+ if (mask) {
1004
+ dark = !dark;
1005
+ }
1006
+
1007
+ this.modules![row][col - c] = dark;
1008
+
1009
+ bitIndex--;
1010
+ if (bitIndex === -1) {
1011
+ byteIndex++;
1012
+ bitIndex = 7;
1013
+ }
1014
+ }
1015
+ }
1016
+
1017
+ row += inc;
1018
+ }
1019
+
1020
+ row -= inc;
1021
+ inc = -inc;
1022
+ }
1023
+ }
1024
+
1025
+ static createData(
1026
+ typeNumber: number,
1027
+ errorCorrectLevel: QRErrorCorrectLevelValue,
1028
+ dataList: QR8bitByte[]
1029
+ ): number[] {
1030
+ const rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel);
1031
+ const buffer = new QRBitBuffer();
1032
+
1033
+ for (let i = 0; i < dataList.length; i++) {
1034
+ const data = dataList[i];
1035
+ buffer.put(data.mode, 4);
1036
+ buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber));
1037
+ data.write(buffer);
1038
+ }
1039
+
1040
+ let totalDataCount = 0;
1041
+ for (let i = 0; i < rsBlocks.length; i++) {
1042
+ totalDataCount += rsBlocks[i].dataCount;
1043
+ }
1044
+
1045
+ if (buffer.getLengthInBits() > totalDataCount * 8) {
1046
+ throw new Error(`code length overflow. (${buffer.getLengthInBits()}>${totalDataCount * 8})`);
1047
+ }
1048
+
1049
+ if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) {
1050
+ buffer.put(0, 4);
1051
+ }
1052
+
1053
+ while (buffer.getLengthInBits() % 8 !== 0) {
1054
+ buffer.putBit(false);
1055
+ }
1056
+
1057
+ while (buffer.getLengthInBits() < totalDataCount * 8) {
1058
+ buffer.put(QRCodeModel.PAD0, 8);
1059
+ if (buffer.getLengthInBits() >= totalDataCount * 8) break;
1060
+ buffer.put(QRCodeModel.PAD1, 8);
1061
+ }
1062
+
1063
+ return QRCodeModel.createBytes(buffer, rsBlocks);
1064
+ }
1065
+
1066
+ static createBytes(buffer: QRBitBuffer, rsBlocks: QRRSBlock[]): number[] {
1067
+ let offset = 0;
1068
+ let maxDcCount = 0;
1069
+ let maxEcCount = 0;
1070
+
1071
+ const dcdata: number[][] = new Array(rsBlocks.length);
1072
+ const ecdata: number[][] = new Array(rsBlocks.length);
1073
+
1074
+ for (let r = 0; r < rsBlocks.length; r++) {
1075
+ const dcCount = rsBlocks[r].dataCount;
1076
+ const ecCount = rsBlocks[r].totalCount - dcCount;
1077
+
1078
+ maxDcCount = Math.max(maxDcCount, dcCount);
1079
+ maxEcCount = Math.max(maxEcCount, ecCount);
1080
+
1081
+ dcdata[r] = new Array<number>(dcCount);
1082
+ for (let i = 0; i < dcdata[r].length; i++) {
1083
+ dcdata[r][i] = 0xff & buffer.buffer[i + offset];
1084
+ }
1085
+ offset += dcCount;
1086
+
1087
+ const rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
1088
+ const rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1);
1089
+ const modPoly = rawPoly.mod(rsPoly);
1090
+
1091
+ ecdata[r] = new Array<number>(rsPoly.getLength() - 1);
1092
+ for (let i = 0; i < ecdata[r].length; i++) {
1093
+ const modIndex = i + modPoly.getLength() - ecdata[r].length;
1094
+ ecdata[r][i] = modIndex >= 0 ? modPoly.get(modIndex) : 0;
1095
+ }
1096
+ }
1097
+
1098
+ let totalCodeCount = 0;
1099
+ for (let i = 0; i < rsBlocks.length; i++) {
1100
+ totalCodeCount += rsBlocks[i].totalCount;
1101
+ }
1102
+
1103
+ const data: number[] = new Array<number>(totalCodeCount);
1104
+ let index = 0;
1105
+
1106
+ for (let i = 0; i < maxDcCount; i++) {
1107
+ for (let r = 0; r < rsBlocks.length; r++) {
1108
+ if (i < dcdata[r].length) {
1109
+ data[index++] = dcdata[r][i];
1110
+ }
1111
+ }
1112
+ }
1113
+
1114
+ for (let i = 0; i < maxEcCount; i++) {
1115
+ for (let r = 0; r < rsBlocks.length; r++) {
1116
+ if (i < ecdata[r].length) {
1117
+ data[index++] = ecdata[r][i];
1118
+ }
1119
+ }
1120
+ }
1121
+
1122
+ return data;
1123
+ }
1124
+ }
1125
+
1126
+ QRCodeModel.PAD0 = 0xec;
1127
+ QRCodeModel.PAD1 = 0x11;
1128
+
1129
+ function getUTF8Length(text: string): number {
1130
+ const replacedText = encodeURI(text)
1131
+ .toString()
1132
+ .replace(/%[0-9a-fA-F]{2}/g, 'a');
1133
+ return replacedText.length;
1134
+ }
1135
+
1136
+ /** 按内容长度与纠错等级推导最小可用版本(1-40)。 */
1137
+ function getTypeNumber(text: string, correctLevel: QRErrorCorrectLevelValue): number {
1138
+ const length = getUTF8Length(text);
1139
+ let type = 1;
1140
+
1141
+ for (let i = 0, len = QRCodeLimitLength.length; i < len; i++) {
1142
+ let limit = 0;
1143
+
1144
+ switch (correctLevel) {
1145
+ case QRErrorCorrectLevel.L:
1146
+ limit = QRCodeLimitLength[i][0];
1147
+ break;
1148
+ case QRErrorCorrectLevel.M:
1149
+ limit = QRCodeLimitLength[i][1];
1150
+ break;
1151
+ case QRErrorCorrectLevel.Q:
1152
+ limit = QRCodeLimitLength[i][2];
1153
+ break;
1154
+ case QRErrorCorrectLevel.H:
1155
+ limit = QRCodeLimitLength[i][3];
1156
+ break;
1157
+ }
1158
+
1159
+ if (length <= limit) {
1160
+ break;
1161
+ } else {
1162
+ type++;
1163
+ }
1164
+ }
1165
+
1166
+ if (type > QRCodeLimitLength.length) {
1167
+ throw new Error('Too long data');
1168
+ }
1169
+
1170
+ return type;
1171
+ }
1172
+
1173
+ function normalizeModules(modules: QRModuleMatrix | null): QRCodeDataMatrix {
1174
+ if (!modules) return [];
1175
+ return modules.map(row => row.map(item => item === true));
1176
+ }
1177
+
1178
+ /** generateQRCode 的生成结果。 */
1179
+ export interface QRCodeResult {
1180
+ /** 二维码模块矩阵,true 为深色模块。 */
1181
+ modules: QRCodeDataMatrix;
1182
+ /** 每边模块数量。 */
1183
+ moduleCount: number;
1184
+ /** 使用的版本(1-40)。 */
1185
+ typeNumber: number;
1186
+ /** 使用的纠错等级。 */
1187
+ errorCorrectLevel: QRErrorCorrectLevelValue;
1188
+ }
1189
+
1190
+ /**
1191
+ * 生成二维码模块矩阵。
1192
+ * @param text 二维码内容(UTF-8)
1193
+ * @param options.errorCorrectLevel 纠错等级,默认 H
1194
+ * @returns 模块矩阵与版本信息
1195
+ */
1196
+ export function generateQRCode(
1197
+ text: string,
1198
+ options: { errorCorrectLevel?: QRErrorCorrectLevelValue } = {}
1199
+ ): QRCodeResult {
1200
+ const { errorCorrectLevel = QRErrorCorrectLevel.H } = options;
1201
+ const typeNumber = getTypeNumber(text, errorCorrectLevel);
1202
+ const qr = new QRCodeModel(typeNumber, errorCorrectLevel);
1203
+ qr.addData(text);
1204
+ qr.make();
1205
+
1206
+ return {
1207
+ modules: normalizeModules(qr.modules),
1208
+ moduleCount: qr.getModuleCount(),
1209
+ typeNumber,
1210
+ errorCorrectLevel
1211
+ };
1212
+ }
1213
+
1214
+ export { QRErrorCorrectLevel };