@aquera/nile-elements 1.5.4 → 1.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/demo/index.css +9 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.js +871 -255
- package/dist/nile-auto-complete/nile-auto-complete.css.cjs.js +1 -1
- package/dist/nile-auto-complete/nile-auto-complete.css.cjs.js.map +1 -1
- package/dist/nile-auto-complete/nile-auto-complete.css.esm.js +13 -2
- package/dist/nile-button/nile-button.cjs.js +1 -1
- package/dist/nile-button/nile-button.cjs.js.map +1 -1
- package/dist/nile-button/nile-button.css.cjs.js +1 -1
- package/dist/nile-button/nile-button.css.cjs.js.map +1 -1
- package/dist/nile-button/nile-button.css.esm.js +89 -33
- package/dist/nile-button/nile-button.esm.js +2 -2
- package/dist/nile-calendar/nile-calendar.cjs.js +1 -1
- package/dist/nile-calendar/nile-calendar.cjs.js.map +1 -1
- package/dist/nile-calendar/nile-calendar.css.cjs.js +1 -1
- package/dist/nile-calendar/nile-calendar.css.cjs.js.map +1 -1
- package/dist/nile-calendar/nile-calendar.css.esm.js +59 -2
- package/dist/nile-calendar/nile-calendar.esm.js +16 -10
- package/dist/nile-checkbox/nile-checkbox.css.cjs.js +1 -1
- package/dist/nile-checkbox/nile-checkbox.css.cjs.js.map +1 -1
- package/dist/nile-checkbox/nile-checkbox.css.esm.js +4 -5
- package/dist/nile-chip/nile-chip.css.cjs.js +1 -1
- package/dist/nile-chip/nile-chip.css.cjs.js.map +1 -1
- package/dist/nile-chip/nile-chip.css.esm.js +37 -23
- package/dist/nile-code-editor/nile-code-editor.cjs.js +1 -1
- package/dist/nile-code-editor/nile-code-editor.cjs.js.map +1 -1
- package/dist/nile-code-editor/nile-code-editor.css.cjs.js +1 -1
- package/dist/nile-code-editor/nile-code-editor.css.cjs.js.map +1 -1
- package/dist/nile-code-editor/nile-code-editor.css.esm.js +7 -6
- package/dist/nile-code-editor/nile-code-editor.esm.js +1 -1
- package/dist/nile-date-picker/nile-date-picker.cjs.js +1 -1
- package/dist/nile-date-picker/nile-date-picker.cjs.js.map +1 -1
- package/dist/nile-date-picker/nile-date-picker.esm.js +4 -4
- package/dist/nile-detail/index.cjs.js +2 -0
- package/dist/nile-detail/index.cjs.js.map +1 -0
- package/dist/nile-detail/index.esm.js +1 -0
- package/dist/nile-detail/nile-detail.cjs.js +2 -0
- package/dist/nile-detail/nile-detail.cjs.js.map +1 -0
- package/dist/nile-detail/nile-detail.css.cjs.js +2 -0
- package/dist/nile-detail/nile-detail.css.cjs.js.map +1 -0
- package/dist/nile-detail/nile-detail.css.esm.js +149 -0
- package/dist/nile-detail/nile-detail.esm.js +45 -0
- package/dist/nile-detail/nile-detail.utils.cjs.js +2 -0
- package/dist/nile-detail/nile-detail.utils.cjs.js.map +1 -0
- package/dist/nile-detail/nile-detail.utils.esm.js +1 -0
- package/dist/nile-dropdown/nile-dropdown.cjs.js +1 -1
- package/dist/nile-dropdown/nile-dropdown.cjs.js.map +1 -1
- package/dist/nile-dropdown/nile-dropdown.esm.js +1 -1
- package/dist/nile-file-upload/utils/file-validation.util.cjs.js +2 -2
- package/dist/nile-file-upload/utils/file-validation.util.cjs.js.map +1 -1
- package/dist/nile-file-upload/utils/file-validation.util.esm.js +1 -1
- package/dist/nile-filter-chip/nile-filter-chip.cjs.js +1 -1
- package/dist/nile-filter-chip/nile-filter-chip.cjs.js.map +1 -1
- package/dist/nile-filter-chip/nile-filter-chip.css.cjs.js +1 -1
- package/dist/nile-filter-chip/nile-filter-chip.css.cjs.js.map +1 -1
- package/dist/nile-filter-chip/nile-filter-chip.css.esm.js +24 -2
- package/dist/nile-filter-chip/nile-filter-chip.esm.js +2 -2
- package/dist/nile-inline-edit/nile-inline-edit.cjs.js +1 -1
- package/dist/nile-inline-edit/nile-inline-edit.cjs.js.map +1 -1
- package/dist/nile-inline-edit/nile-inline-edit.css.cjs.js +1 -1
- package/dist/nile-inline-edit/nile-inline-edit.css.cjs.js.map +1 -1
- package/dist/nile-inline-edit/nile-inline-edit.css.esm.js +15 -6
- package/dist/nile-inline-edit/nile-inline-edit.esm.js +2 -2
- package/dist/nile-inline-sidebar/nile-inline-sidebar.cjs.js +1 -1
- package/dist/nile-inline-sidebar/nile-inline-sidebar.cjs.js.map +1 -1
- package/dist/nile-inline-sidebar/nile-inline-sidebar.css.cjs.js +1 -1
- package/dist/nile-inline-sidebar/nile-inline-sidebar.css.cjs.js.map +1 -1
- package/dist/nile-inline-sidebar/nile-inline-sidebar.css.esm.js +19 -0
- package/dist/nile-inline-sidebar/nile-inline-sidebar.esm.js +16 -14
- package/dist/nile-inline-sidebar-item/nile-inline-sidebar-item.cjs.js +1 -1
- package/dist/nile-inline-sidebar-item/nile-inline-sidebar-item.cjs.js.map +1 -1
- package/dist/nile-inline-sidebar-item/nile-inline-sidebar-item.esm.js +8 -3
- package/dist/nile-input/nile-input.css.cjs.js +1 -1
- package/dist/nile-input/nile-input.css.cjs.js.map +1 -1
- package/dist/nile-input/nile-input.css.esm.js +4 -4
- package/dist/nile-link/nile-link.cjs.js +1 -1
- package/dist/nile-link/nile-link.cjs.js.map +1 -1
- package/dist/nile-link/nile-link.css.cjs.js +1 -1
- package/dist/nile-link/nile-link.css.cjs.js.map +1 -1
- package/dist/nile-link/nile-link.css.esm.js +3 -4
- package/dist/nile-link/nile-link.esm.js +2 -2
- package/dist/nile-pagination/nile-pagination.cjs.js +1 -1
- package/dist/nile-pagination/nile-pagination.cjs.js.map +1 -1
- package/dist/nile-pagination/nile-pagination.css.cjs.js +1 -1
- package/dist/nile-pagination/nile-pagination.css.cjs.js.map +1 -1
- package/dist/nile-pagination/nile-pagination.css.esm.js +109 -5
- package/dist/nile-pagination/nile-pagination.esm.js +70 -3
- package/dist/nile-qr-code/index.cjs.js +2 -0
- package/dist/nile-qr-code/index.cjs.js.map +1 -0
- package/dist/nile-qr-code/index.esm.js +1 -0
- package/dist/nile-qr-code/nile-qr-code-utils.cjs.js +2 -0
- package/dist/nile-qr-code/nile-qr-code-utils.cjs.js.map +1 -0
- package/dist/nile-qr-code/nile-qr-code-utils.esm.js +1 -0
- package/dist/nile-qr-code/nile-qr-code.cjs.js +2 -0
- package/dist/nile-qr-code/nile-qr-code.cjs.js.map +1 -0
- package/dist/nile-qr-code/nile-qr-code.css.cjs.js +2 -0
- package/dist/nile-qr-code/nile-qr-code.css.cjs.js.map +1 -0
- package/dist/nile-qr-code/nile-qr-code.css.esm.js +12 -0
- package/dist/nile-qr-code/nile-qr-code.esm.js +9 -0
- package/dist/nile-radio/nile-radio.css.cjs.js +1 -1
- package/dist/nile-radio/nile-radio.css.cjs.js.map +1 -1
- package/dist/nile-radio/nile-radio.css.esm.js +1 -1
- package/dist/nile-rich-text-editor/nile-rich-text-editor.cjs.js +1 -1
- package/dist/nile-rich-text-editor/nile-rich-text-editor.cjs.js.map +1 -1
- package/dist/nile-rich-text-editor/nile-rich-text-editor.css.cjs.js +1 -1
- package/dist/nile-rich-text-editor/nile-rich-text-editor.css.cjs.js.map +1 -1
- package/dist/nile-rich-text-editor/nile-rich-text-editor.css.esm.js +25 -6
- package/dist/nile-rich-text-editor/nile-rich-text-editor.esm.js +1 -1
- package/dist/nile-rich-text-editor/nile-rte-select.cjs.js +1 -1
- package/dist/nile-rich-text-editor/nile-rte-select.cjs.js.map +1 -1
- package/dist/nile-rich-text-editor/nile-rte-select.esm.js +4 -3
- package/dist/nile-select/nile-select.css.cjs.js +1 -1
- package/dist/nile-select/nile-select.css.cjs.js.map +1 -1
- package/dist/nile-select/nile-select.css.esm.js +7 -4
- package/dist/nile-slide-toggle/nile-slide-toggle.css.cjs.js +1 -1
- package/dist/nile-slide-toggle/nile-slide-toggle.css.cjs.js.map +1 -1
- package/dist/nile-slide-toggle/nile-slide-toggle.css.esm.js +5 -1
- package/dist/nile-textarea/nile-textarea.css.cjs.js +1 -1
- package/dist/nile-textarea/nile-textarea.css.cjs.js.map +1 -1
- package/dist/nile-textarea/nile-textarea.css.esm.js +10 -5
- package/dist/nile-virtual-select/nile-virtual-select.css.cjs.js +1 -1
- package/dist/nile-virtual-select/nile-virtual-select.css.cjs.js.map +1 -1
- package/dist/nile-virtual-select/nile-virtual-select.css.esm.js +5 -3
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/nile-auto-complete/nile-auto-complete.css.js +11 -0
- package/dist/src/nile-auto-complete/nile-auto-complete.css.js.map +1 -1
- package/dist/src/nile-button/nile-button.css.js +89 -33
- package/dist/src/nile-button/nile-button.css.js.map +1 -1
- package/dist/src/nile-button/nile-button.js +5 -5
- package/dist/src/nile-button/nile-button.js.map +1 -1
- package/dist/src/nile-calendar/nile-calendar.css.js +57 -0
- package/dist/src/nile-calendar/nile-calendar.css.js.map +1 -1
- package/dist/src/nile-calendar/nile-calendar.d.ts +1 -0
- package/dist/src/nile-calendar/nile-calendar.js +24 -6
- package/dist/src/nile-calendar/nile-calendar.js.map +1 -1
- package/dist/src/nile-checkbox/nile-checkbox.css.js +4 -5
- package/dist/src/nile-checkbox/nile-checkbox.css.js.map +1 -1
- package/dist/src/nile-chip/nile-chip.css.js +35 -21
- package/dist/src/nile-chip/nile-chip.css.js.map +1 -1
- package/dist/src/nile-code-editor/nile-code-editor.css.js +7 -6
- package/dist/src/nile-code-editor/nile-code-editor.css.js.map +1 -1
- package/dist/src/nile-code-editor/nile-code-editor.d.ts +1 -1
- package/dist/src/nile-code-editor/nile-code-editor.js +7 -1
- package/dist/src/nile-code-editor/nile-code-editor.js.map +1 -1
- package/dist/src/nile-date-picker/nile-date-picker.d.ts +3 -0
- package/dist/src/nile-date-picker/nile-date-picker.js +22 -2
- package/dist/src/nile-date-picker/nile-date-picker.js.map +1 -1
- package/dist/src/nile-detail/index.d.ts +1 -0
- package/dist/src/nile-detail/index.js +2 -0
- package/dist/src/nile-detail/index.js.map +1 -0
- package/dist/src/nile-detail/nile-detail.css.d.ts +3 -0
- package/dist/src/nile-detail/nile-detail.css.js +152 -0
- package/dist/src/nile-detail/nile-detail.css.js.map +1 -0
- package/dist/src/nile-detail/nile-detail.d.ts +29 -0
- package/dist/src/nile-detail/nile-detail.js +143 -0
- package/dist/src/nile-detail/nile-detail.js.map +1 -0
- package/dist/src/nile-detail/nile-detail.test.d.ts +1 -0
- package/dist/src/nile-detail/nile-detail.test.js +168 -0
- package/dist/src/nile-detail/nile-detail.test.js.map +1 -0
- package/dist/src/nile-detail/nile-detail.utils.d.ts +8 -0
- package/dist/src/nile-detail/nile-detail.utils.js +117 -0
- package/dist/src/nile-detail/nile-detail.utils.js.map +1 -0
- package/dist/src/nile-dropdown/nile-dropdown.d.ts +1 -0
- package/dist/src/nile-dropdown/nile-dropdown.js +11 -0
- package/dist/src/nile-dropdown/nile-dropdown.js.map +1 -1
- package/dist/src/nile-file-upload/utils/file-validation.util.js +11 -6
- package/dist/src/nile-file-upload/utils/file-validation.util.js.map +1 -1
- package/dist/src/nile-filter-chip/nile-filter-chip.css.js +22 -0
- package/dist/src/nile-filter-chip/nile-filter-chip.css.js.map +1 -1
- package/dist/src/nile-filter-chip/nile-filter-chip.d.ts +1 -0
- package/dist/src/nile-filter-chip/nile-filter-chip.js +6 -0
- package/dist/src/nile-filter-chip/nile-filter-chip.js.map +1 -1
- package/dist/src/nile-inline-edit/nile-inline-edit.css.js +15 -6
- package/dist/src/nile-inline-edit/nile-inline-edit.css.js.map +1 -1
- package/dist/src/nile-inline-edit/nile-inline-edit.d.ts +2 -0
- package/dist/src/nile-inline-edit/nile-inline-edit.js +7 -0
- package/dist/src/nile-inline-edit/nile-inline-edit.js.map +1 -1
- package/dist/src/nile-inline-sidebar/nile-inline-sidebar.css.js +19 -0
- package/dist/src/nile-inline-sidebar/nile-inline-sidebar.css.js.map +1 -1
- package/dist/src/nile-inline-sidebar/nile-inline-sidebar.d.ts +6 -0
- package/dist/src/nile-inline-sidebar/nile-inline-sidebar.js +88 -4
- package/dist/src/nile-inline-sidebar/nile-inline-sidebar.js.map +1 -1
- package/dist/src/nile-inline-sidebar/nile-inline-sidebar.test.d.ts +3 -0
- package/dist/src/nile-inline-sidebar/nile-inline-sidebar.test.js +110 -0
- package/dist/src/nile-inline-sidebar/nile-inline-sidebar.test.js.map +1 -0
- package/dist/src/nile-inline-sidebar-group/nile-inline-sidebar-group.test.d.ts +2 -0
- package/dist/src/nile-inline-sidebar-group/nile-inline-sidebar-group.test.js +109 -0
- package/dist/src/nile-inline-sidebar-group/nile-inline-sidebar-group.test.js.map +1 -0
- package/dist/src/nile-inline-sidebar-item/nile-inline-sidebar-item.d.ts +4 -0
- package/dist/src/nile-inline-sidebar-item/nile-inline-sidebar-item.js +30 -2
- package/dist/src/nile-inline-sidebar-item/nile-inline-sidebar-item.js.map +1 -1
- package/dist/src/nile-inline-sidebar-item/nile-inline-sidebar-item.test.d.ts +2 -0
- package/dist/src/nile-inline-sidebar-item/nile-inline-sidebar-item.test.js +109 -0
- package/dist/src/nile-inline-sidebar-item/nile-inline-sidebar-item.test.js.map +1 -0
- package/dist/src/nile-input/nile-input.css.js +4 -4
- package/dist/src/nile-input/nile-input.css.js.map +1 -1
- package/dist/src/nile-link/nile-link.css.js +1 -2
- package/dist/src/nile-link/nile-link.css.js.map +1 -1
- package/dist/src/nile-link/nile-link.js +1 -0
- package/dist/src/nile-link/nile-link.js.map +1 -1
- package/dist/src/nile-pagination/nile-pagination.css.js +107 -3
- package/dist/src/nile-pagination/nile-pagination.css.js.map +1 -1
- package/dist/src/nile-pagination/nile-pagination.d.ts +5 -1
- package/dist/src/nile-pagination/nile-pagination.js +84 -1
- package/dist/src/nile-pagination/nile-pagination.js.map +1 -1
- package/dist/src/nile-pagination/nile-pagination.test.js +1187 -103
- package/dist/src/nile-pagination/nile-pagination.test.js.map +1 -1
- package/dist/src/nile-qr-code/index.d.ts +1 -0
- package/dist/src/nile-qr-code/index.js +2 -0
- package/dist/src/nile-qr-code/index.js.map +1 -0
- package/dist/src/nile-qr-code/nile-qr-code-utils.d.ts +15 -0
- package/dist/src/nile-qr-code/nile-qr-code-utils.js +678 -0
- package/dist/src/nile-qr-code/nile-qr-code-utils.js.map +1 -0
- package/dist/src/nile-qr-code/nile-qr-code.css.d.ts +12 -0
- package/dist/src/nile-qr-code/nile-qr-code.css.js +24 -0
- package/dist/src/nile-qr-code/nile-qr-code.css.js.map +1 -0
- package/dist/src/nile-qr-code/nile-qr-code.d.ts +127 -0
- package/dist/src/nile-qr-code/nile-qr-code.js +381 -0
- package/dist/src/nile-qr-code/nile-qr-code.js.map +1 -0
- package/dist/src/nile-qr-code/nile-qr-code.test.d.ts +1 -0
- package/dist/src/nile-qr-code/nile-qr-code.test.js +719 -0
- package/dist/src/nile-qr-code/nile-qr-code.test.js.map +1 -0
- package/dist/src/nile-radio/nile-radio.css.js +1 -1
- package/dist/src/nile-radio/nile-radio.css.js.map +1 -1
- package/dist/src/nile-rich-text-editor/nile-rich-text-editor.css.js +25 -6
- package/dist/src/nile-rich-text-editor/nile-rich-text-editor.css.js.map +1 -1
- package/dist/src/nile-rich-text-editor/nile-rich-text-editor.d.ts +1 -0
- package/dist/src/nile-rich-text-editor/nile-rich-text-editor.js +17 -6
- package/dist/src/nile-rich-text-editor/nile-rich-text-editor.js.map +1 -1
- package/dist/src/nile-rich-text-editor/nile-rte-select.d.ts +1 -0
- package/dist/src/nile-rich-text-editor/nile-rte-select.js +7 -2
- package/dist/src/nile-rich-text-editor/nile-rte-select.js.map +1 -1
- package/dist/src/nile-select/nile-select.css.js +7 -4
- package/dist/src/nile-select/nile-select.css.js.map +1 -1
- package/dist/src/nile-slide-toggle/nile-slide-toggle.css.js +5 -1
- package/dist/src/nile-slide-toggle/nile-slide-toggle.css.js.map +1 -1
- package/dist/src/nile-textarea/nile-textarea.css.js +10 -5
- package/dist/src/nile-textarea/nile-textarea.css.js.map +1 -1
- package/dist/src/nile-virtual-select/nile-virtual-select.css.js +5 -3
- package/dist/src/nile-virtual-select/nile-virtual-select.css.js.map +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/version.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -2
- package/src/index.ts +3 -2
- package/src/nile-auto-complete/nile-auto-complete.css.ts +11 -0
- package/src/nile-button/nile-button.css.ts +89 -33
- package/src/nile-button/nile-button.ts +6 -5
- package/src/nile-calendar/nile-calendar.css.ts +57 -0
- package/src/nile-calendar/nile-calendar.ts +17 -6
- package/src/nile-checkbox/nile-checkbox.css.ts +4 -5
- package/src/nile-chip/nile-chip.css.ts +35 -21
- package/src/nile-code-editor/nile-code-editor.css.ts +7 -6
- package/src/nile-code-editor/nile-code-editor.ts +7 -1
- package/src/nile-date-picker/nile-date-picker.ts +22 -2
- package/src/nile-detail/index.ts +1 -0
- package/src/nile-detail/nile-detail.css.ts +153 -0
- package/src/nile-detail/nile-detail.test.ts +215 -0
- package/src/nile-detail/nile-detail.ts +140 -0
- package/src/nile-detail/nile-detail.utils.ts +133 -0
- package/src/nile-dropdown/nile-dropdown.ts +11 -0
- package/src/nile-file-upload/utils/file-validation.util.ts +10 -5
- package/src/nile-filter-chip/nile-filter-chip.css.ts +22 -0
- package/src/nile-filter-chip/nile-filter-chip.ts +2 -0
- package/src/nile-inline-edit/nile-inline-edit.css.ts +15 -6
- package/src/nile-inline-edit/nile-inline-edit.ts +4 -0
- package/src/nile-inline-sidebar/nile-inline-sidebar.css.ts +19 -0
- package/src/nile-inline-sidebar/nile-inline-sidebar.test.ts +108 -0
- package/src/nile-inline-sidebar/nile-inline-sidebar.ts +108 -5
- package/src/nile-inline-sidebar-group/nile-inline-sidebar-group.test.ts +107 -0
- package/src/nile-inline-sidebar-item/nile-inline-sidebar-item.test.ts +107 -0
- package/src/nile-inline-sidebar-item/nile-inline-sidebar-item.ts +34 -3
- package/src/nile-input/nile-input.css.ts +4 -4
- package/src/nile-link/nile-link.css.ts +1 -2
- package/src/nile-link/nile-link.ts +1 -0
- package/src/nile-pagination/nile-pagination.css.ts +107 -3
- package/src/nile-pagination/nile-pagination.test.ts +1388 -101
- package/src/nile-pagination/nile-pagination.ts +87 -2
- package/src/nile-qr-code/index.ts +1 -0
- package/src/nile-qr-code/nile-qr-code-utils.ts +733 -0
- package/src/nile-qr-code/nile-qr-code.css.ts +26 -0
- package/src/nile-qr-code/nile-qr-code.test.ts +879 -0
- package/src/nile-qr-code/nile-qr-code.ts +431 -0
- package/src/nile-radio/nile-radio.css.ts +1 -1
- package/src/nile-rich-text-editor/nile-rich-text-editor.css.ts +25 -6
- package/src/nile-rich-text-editor/nile-rich-text-editor.ts +14 -6
- package/src/nile-rich-text-editor/nile-rte-select.ts +5 -2
- package/src/nile-select/nile-select.css.ts +7 -4
- package/src/nile-slide-toggle/nile-slide-toggle.css.ts +5 -1
- package/src/nile-textarea/nile-textarea.css.ts +10 -5
- package/src/nile-virtual-select/nile-virtual-select.css.ts +5 -3
- package/vscode-html-custom-data.json +163 -10
- package/web-test-runner.config.mjs +1 -1
|
@@ -0,0 +1,733 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Aquera Inc 2023
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the BSD-3-Clause license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Types
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
export type ErrorCorrectionLevel = 'L' | 'M' | 'Q' | 'H';
|
|
15
|
+
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Constants
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
/** Error-correction level ordinals used as array indices. */
|
|
21
|
+
const EC_ORDINALS: Record<ErrorCorrectionLevel, number> = { L: 0, M: 1, Q: 2, H: 3 };
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Maximum number of *data* codewords for each (version, ecl) combination.
|
|
25
|
+
* Index: [version-1][ecl ordinal] (versions 1–40).
|
|
26
|
+
* Source: ISO/IEC 18004 Table 7.
|
|
27
|
+
*/
|
|
28
|
+
const DATA_CODEWORDS: number[][] = [
|
|
29
|
+
[19, 16, 13, 9],
|
|
30
|
+
[34, 28, 22, 16],
|
|
31
|
+
[55, 44, 34, 26],
|
|
32
|
+
[80, 64, 48, 36],
|
|
33
|
+
[108, 86, 62, 46],
|
|
34
|
+
[136, 108, 76, 60],
|
|
35
|
+
[156, 124, 88, 66],
|
|
36
|
+
[194, 154, 110, 86],
|
|
37
|
+
[232, 182, 132, 100],
|
|
38
|
+
[274, 216, 154, 122],
|
|
39
|
+
[324, 254, 180, 140],
|
|
40
|
+
[370, 290, 206, 158],
|
|
41
|
+
[428, 334, 244, 180],
|
|
42
|
+
[461, 365, 261, 197],
|
|
43
|
+
[523, 415, 295, 223],
|
|
44
|
+
[589, 453, 325, 253],
|
|
45
|
+
[647, 507, 367, 283],
|
|
46
|
+
[721, 563, 397, 313],
|
|
47
|
+
[795, 627, 445, 341],
|
|
48
|
+
[861, 669, 485, 385],
|
|
49
|
+
[932, 714, 512, 406],
|
|
50
|
+
[1006, 782, 568, 442],
|
|
51
|
+
[1094, 860, 614, 464],
|
|
52
|
+
[1174, 914, 664, 514],
|
|
53
|
+
[1276, 1000, 718, 538],
|
|
54
|
+
[1370, 1062, 754, 596],
|
|
55
|
+
[1468, 1128, 808, 628],
|
|
56
|
+
[1531, 1193, 871, 661],
|
|
57
|
+
[1631, 1267, 911, 701],
|
|
58
|
+
[1735, 1373, 985, 745],
|
|
59
|
+
[1843, 1455, 1033, 793],
|
|
60
|
+
[1955, 1541, 1115, 845],
|
|
61
|
+
[2071, 1631, 1171, 901],
|
|
62
|
+
[2191, 1725, 1231, 961],
|
|
63
|
+
[2306, 1812, 1286, 986],
|
|
64
|
+
[2434, 1914, 1354, 1054],
|
|
65
|
+
[2566, 1992, 1426, 1096],
|
|
66
|
+
[2702, 2102, 1502, 1142],
|
|
67
|
+
[2812, 2216, 1582, 1222],
|
|
68
|
+
[2956, 2334, 1666, 1276],
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Total codewords per version (data + EC).
|
|
73
|
+
*/
|
|
74
|
+
const TOTAL_CODEWORDS: number[] = [
|
|
75
|
+
26, 44, 70, 100, 134, 172, 196, 242, 292, 346,
|
|
76
|
+
404, 466, 532, 581, 655, 733, 815, 901, 991, 1085,
|
|
77
|
+
1156, 1258, 1364, 1474, 1588, 1706, 1828, 1921, 2051, 2185,
|
|
78
|
+
2323, 2465, 2611, 2761, 2876, 3034, 3196, 3362, 3532, 3706,
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Number of EC blocks (group-1 count, group-1 data-cw, group-2 count, group-2 data-cw)
|
|
83
|
+
* for each (version, ecl).
|
|
84
|
+
* Index: [version-1][ecl ordinal] → [g1Count, g1DataCw, g2Count, g2DataCw]
|
|
85
|
+
*/
|
|
86
|
+
const EC_BLOCKS: number[][][] = [
|
|
87
|
+
/* v1 */ [[1,19,0,0],[1,16,0,0],[1,13,0,0],[1,9,0,0]],
|
|
88
|
+
/* v2 */ [[1,34,0,0],[1,28,0,0],[1,22,0,0],[1,16,0,0]],
|
|
89
|
+
/* v3 */ [[1,55,0,0],[1,44,0,0],[2,17,0,0],[2,13,0,0]],
|
|
90
|
+
/* v4 */ [[1,80,0,0],[2,32,0,0],[2,24,0,0],[4,9,0,0]],
|
|
91
|
+
/* v5 */ [[1,108,0,0],[2,43,0,0],[2,15,2,16],[2,11,2,12]],
|
|
92
|
+
/* v6 */ [[2,68,0,0],[4,27,0,0],[4,19,0,0],[4,15,0,0]],
|
|
93
|
+
/* v7 */ [[2,78,0,0],[4,31,0,0],[2,14,4,15],[4,13,1,14]],
|
|
94
|
+
/* v8 */ [[2,97,0,0],[2,38,2,39],[4,18,2,19],[4,14,2,15]],
|
|
95
|
+
/* v9 */ [[2,116,0,0],[3,36,2,37],[4,16,4,17],[4,12,4,13]],
|
|
96
|
+
/* v10 */ [[2,68,2,69],[4,43,1,44],[6,19,2,20],[6,15,2,16]],
|
|
97
|
+
/* v11 */ [[4,81,0,0],[1,50,4,51],[4,22,4,23],[3,12,8,13]],
|
|
98
|
+
/* v12 */ [[2,92,2,93],[6,36,2,37],[4,20,6,21],[7,14,4,15]],
|
|
99
|
+
/* v13 */ [[4,107,0,0],[8,37,1,38],[8,20,4,21],[12,11,4,12]],
|
|
100
|
+
/* v14 */ [[3,115,1,116],[4,40,5,41],[11,16,5,17],[11,12,5,13]],
|
|
101
|
+
/* v15 */ [[5,87,1,88],[5,41,5,42],[5,24,7,25],[11,12,7,13]],
|
|
102
|
+
/* v16 */ [[5,98,1,99],[7,45,3,46],[15,19,2,20],[3,15,13,16]],
|
|
103
|
+
/* v17 */ [[1,107,5,108],[10,46,1,47],[1,22,15,23],[2,14,17,15]],
|
|
104
|
+
/* v18 */ [[5,120,1,121],[9,43,4,44],[17,22,1,23],[2,14,19,15]],
|
|
105
|
+
/* v19 */ [[3,113,4,114],[3,44,11,45],[17,21,4,22],[9,13,16,14]],
|
|
106
|
+
/* v20 */ [[3,107,5,108],[3,41,13,42],[15,24,5,25],[15,15,10,16]],
|
|
107
|
+
/* v21 */ [[4,116,4,117],[17,42,0,0],[17,22,6,23],[19,16,6,17]],
|
|
108
|
+
/* v22 */ [[2,111,7,112],[17,46,0,0],[7,24,16,25],[34,13,0,0]],
|
|
109
|
+
/* v23 */ [[4,121,5,122],[4,47,14,48],[11,24,14,25],[16,15,14,16]],
|
|
110
|
+
/* v24 */ [[6,117,4,118],[6,45,14,46],[11,24,16,25],[30,16,2,17]],
|
|
111
|
+
/* v25 */ [[8,106,4,107],[8,47,13,48],[7,24,22,25],[22,15,13,16]],
|
|
112
|
+
/* v26 */ [[10,114,2,115],[19,46,4,47],[28,22,6,23],[33,16,4,17]],
|
|
113
|
+
/* v27 */ [[8,122,4,123],[22,45,3,46],[8,23,26,24],[12,15,28,16]],
|
|
114
|
+
/* v28 */ [[3,117,10,118],[3,45,23,46],[4,24,31,25],[11,15,31,16]],
|
|
115
|
+
/* v29 */ [[7,116,7,117],[21,45,7,46],[1,23,37,24],[19,15,26,16]],
|
|
116
|
+
/* v30 */ [[5,115,10,116],[19,47,10,48],[15,24,25,25],[23,15,25,16]],
|
|
117
|
+
/* v31 */ [[13,115,3,116],[2,46,29,47],[42,24,1,25],[23,15,28,16]],
|
|
118
|
+
/* v32 */ [[17,115,0,0],[10,46,23,47],[10,24,35,25],[19,15,35,16]],
|
|
119
|
+
/* v33 */ [[17,115,1,116],[14,46,21,47],[29,24,19,25],[11,15,46,16]],
|
|
120
|
+
/* v34 */ [[13,115,6,116],[14,46,23,47],[44,24,7,25],[59,16,1,17]],
|
|
121
|
+
/* v35 */ [[12,121,7,122],[12,47,26,48],[39,24,14,25],[22,15,41,16]],
|
|
122
|
+
/* v36 */ [[6,121,14,122],[6,47,34,48],[46,24,10,25],[2,15,64,16]],
|
|
123
|
+
/* v37 */ [[17,122,4,123],[29,46,14,47],[49,24,10,25],[24,15,46,16]],
|
|
124
|
+
/* v38 */ [[4,122,18,123],[13,46,32,47],[48,24,14,25],[42,15,32,16]],
|
|
125
|
+
/* v39 */ [[20,117,4,118],[40,47,7,48],[43,24,22,25],[10,15,67,16]],
|
|
126
|
+
/* v40 */ [[19,118,6,119],[18,47,31,48],[34,24,34,25],[20,15,61,16]],
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Alignment-pattern center coordinates per version (empty for v1).
|
|
131
|
+
*/
|
|
132
|
+
const ALIGNMENT_COORDS: number[][] = [
|
|
133
|
+
[],
|
|
134
|
+
[6, 18],
|
|
135
|
+
[6, 22],
|
|
136
|
+
[6, 26],
|
|
137
|
+
[6, 30],
|
|
138
|
+
[6, 34],
|
|
139
|
+
[6, 22, 38],
|
|
140
|
+
[6, 24, 42],
|
|
141
|
+
[6, 26, 46],
|
|
142
|
+
[6, 28, 50],
|
|
143
|
+
[6, 30, 54],
|
|
144
|
+
[6, 32, 58],
|
|
145
|
+
[6, 34, 62],
|
|
146
|
+
[6, 26, 46, 66],
|
|
147
|
+
[6, 26, 48, 70],
|
|
148
|
+
[6, 26, 50, 74],
|
|
149
|
+
[6, 30, 54, 78],
|
|
150
|
+
[6, 30, 56, 82],
|
|
151
|
+
[6, 30, 58, 86],
|
|
152
|
+
[6, 34, 62, 90],
|
|
153
|
+
[6, 28, 50, 72, 94],
|
|
154
|
+
[6, 26, 50, 74, 98],
|
|
155
|
+
[6, 30, 54, 78, 102],
|
|
156
|
+
[6, 28, 54, 80, 106],
|
|
157
|
+
[6, 32, 58, 84, 110],
|
|
158
|
+
[6, 30, 58, 86, 114],
|
|
159
|
+
[6, 34, 62, 90, 118],
|
|
160
|
+
[6, 26, 50, 74, 98, 122],
|
|
161
|
+
[6, 30, 54, 78, 102, 126],
|
|
162
|
+
[6, 26, 52, 78, 104, 130],
|
|
163
|
+
[6, 30, 56, 82, 108, 134],
|
|
164
|
+
[6, 34, 60, 86, 112, 138],
|
|
165
|
+
[6, 30, 58, 86, 114, 142],
|
|
166
|
+
[6, 34, 62, 90, 118, 146],
|
|
167
|
+
[6, 30, 54, 78, 102, 126, 150],
|
|
168
|
+
[6, 24, 50, 76, 102, 128, 154],
|
|
169
|
+
[6, 28, 54, 80, 106, 132, 158],
|
|
170
|
+
[6, 32, 58, 84, 110, 136, 162],
|
|
171
|
+
[6, 26, 54, 82, 110, 138, 166],
|
|
172
|
+
[6, 30, 58, 86, 114, 142, 170],
|
|
173
|
+
];
|
|
174
|
+
|
|
175
|
+
/** Format information bits (15-bit BCH) for each (ecl, mask) pair. */
|
|
176
|
+
const FORMAT_INFO: number[][] = [
|
|
177
|
+
/* L */ [0x77C4, 0x72F3, 0x7DAA, 0x789D, 0x662F, 0x6318, 0x6C41, 0x6976],
|
|
178
|
+
/* M */ [0x5412, 0x5125, 0x5E7C, 0x5B4B, 0x45F9, 0x40CE, 0x4F97, 0x4AA0],
|
|
179
|
+
/* Q */ [0x355F, 0x3068, 0x3F31, 0x3A06, 0x24B4, 0x2183, 0x2EDA, 0x2BED],
|
|
180
|
+
/* H */ [0x1689, 0x13BE, 0x1CE7, 0x19D0, 0x0762, 0x0255, 0x0D0C, 0x083B],
|
|
181
|
+
];
|
|
182
|
+
|
|
183
|
+
/** Version information bits (18-bit BCH) for versions 7–40. */
|
|
184
|
+
const VERSION_INFO: number[] = [
|
|
185
|
+
0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6, 0x0C762, 0x0D847, 0x0E60D,
|
|
186
|
+
0x0F928, 0x10B78, 0x1145D, 0x12A17, 0x13532, 0x149A6, 0x15683, 0x168C9,
|
|
187
|
+
0x177EC, 0x18EC4, 0x191E1, 0x1AFAB, 0x1B08E, 0x1CC1A, 0x1D33F, 0x1ED75,
|
|
188
|
+
0x1F250, 0x209D5, 0x216F0, 0x228BA, 0x2379F, 0x24B0B, 0x2542E, 0x26A64,
|
|
189
|
+
0x27541, 0x28C69,
|
|
190
|
+
];
|
|
191
|
+
|
|
192
|
+
// ---------------------------------------------------------------------------
|
|
193
|
+
// GF(256) arithmetic for Reed-Solomon
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
|
|
196
|
+
const GF_EXP = new Uint8Array(512);
|
|
197
|
+
const GF_LOG = new Uint8Array(256);
|
|
198
|
+
|
|
199
|
+
(function initGF() {
|
|
200
|
+
let x = 1;
|
|
201
|
+
for (let i = 0; i < 255; i++) {
|
|
202
|
+
GF_EXP[i] = x;
|
|
203
|
+
GF_LOG[x] = i;
|
|
204
|
+
x <<= 1;
|
|
205
|
+
if (x >= 256) x ^= 0x11D; // primitive polynomial
|
|
206
|
+
}
|
|
207
|
+
for (let i = 255; i < 512; i++) {
|
|
208
|
+
GF_EXP[i] = GF_EXP[i - 255];
|
|
209
|
+
}
|
|
210
|
+
})();
|
|
211
|
+
|
|
212
|
+
function gfMul(a: number, b: number): number {
|
|
213
|
+
if (a === 0 || b === 0) return 0;
|
|
214
|
+
return GF_EXP[GF_LOG[a] + GF_LOG[b]];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
// Reed-Solomon EC codeword generation
|
|
219
|
+
// ---------------------------------------------------------------------------
|
|
220
|
+
|
|
221
|
+
function rsGeneratorPoly(degree: number): Uint8Array {
|
|
222
|
+
const gen = new Uint8Array(degree + 1);
|
|
223
|
+
gen[0] = 1;
|
|
224
|
+
for (let i = 0; i < degree; i++) {
|
|
225
|
+
for (let j = degree; j >= 1; j--) {
|
|
226
|
+
gen[j] = gen[j] ^ gfMul(gen[j - 1], GF_EXP[i]);
|
|
227
|
+
}
|
|
228
|
+
// gen[0] stays 1 (leading coeff)
|
|
229
|
+
}
|
|
230
|
+
return gen;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function rsEncode(data: Uint8Array, ecCount: number): Uint8Array {
|
|
234
|
+
const gen = rsGeneratorPoly(ecCount);
|
|
235
|
+
const result = new Uint8Array(ecCount);
|
|
236
|
+
for (let i = 0; i < data.length; i++) {
|
|
237
|
+
const coeff = data[i] ^ result[0];
|
|
238
|
+
// shift result left
|
|
239
|
+
for (let j = 0; j < ecCount - 1; j++) {
|
|
240
|
+
result[j] = result[j + 1] ^ gfMul(gen[j + 1], coeff);
|
|
241
|
+
}
|
|
242
|
+
result[ecCount - 1] = gfMul(gen[ecCount], coeff);
|
|
243
|
+
}
|
|
244
|
+
return result;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ---------------------------------------------------------------------------
|
|
248
|
+
// Data encoding (Byte mode only)
|
|
249
|
+
// ---------------------------------------------------------------------------
|
|
250
|
+
|
|
251
|
+
function encodeData(text: string, version: number, ecl: ErrorCorrectionLevel): Uint8Array {
|
|
252
|
+
const eclOrd = EC_ORDINALS[ecl];
|
|
253
|
+
const totalDataCw = DATA_CODEWORDS[version - 1][eclOrd];
|
|
254
|
+
const bytes = new TextEncoder().encode(text);
|
|
255
|
+
|
|
256
|
+
// Build bit stream
|
|
257
|
+
const bits: number[] = [];
|
|
258
|
+
const pushBits = (value: number, length: number) => {
|
|
259
|
+
for (let i = length - 1; i >= 0; i--) {
|
|
260
|
+
bits.push((value >>> i) & 1);
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
// Mode indicator: byte = 0100
|
|
265
|
+
pushBits(0b0100, 4);
|
|
266
|
+
|
|
267
|
+
// Character count indicator
|
|
268
|
+
const ccBits = version <= 9 ? 8 : 16;
|
|
269
|
+
pushBits(bytes.length, ccBits);
|
|
270
|
+
|
|
271
|
+
// Data
|
|
272
|
+
for (const b of bytes) {
|
|
273
|
+
pushBits(b, 8);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Terminator (up to 4 bits of zeros)
|
|
277
|
+
const totalDataBits = totalDataCw * 8;
|
|
278
|
+
const terminatorLen = Math.min(4, totalDataBits - bits.length);
|
|
279
|
+
pushBits(0, terminatorLen);
|
|
280
|
+
|
|
281
|
+
// Pad to byte boundary
|
|
282
|
+
while (bits.length % 8 !== 0) {
|
|
283
|
+
bits.push(0);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Pad codewords
|
|
287
|
+
const padBytes = [0xEC, 0x11];
|
|
288
|
+
let padIdx = 0;
|
|
289
|
+
while (bits.length < totalDataBits) {
|
|
290
|
+
pushBits(padBytes[padIdx], 8);
|
|
291
|
+
padIdx ^= 1;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Convert to byte array
|
|
295
|
+
const codewords = new Uint8Array(totalDataCw);
|
|
296
|
+
for (let i = 0; i < totalDataCw; i++) {
|
|
297
|
+
let byte = 0;
|
|
298
|
+
for (let b = 0; b < 8; b++) {
|
|
299
|
+
byte = (byte << 1) | bits[i * 8 + b];
|
|
300
|
+
}
|
|
301
|
+
codewords[i] = byte;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return codewords;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// ---------------------------------------------------------------------------
|
|
308
|
+
// Interleave data + EC blocks
|
|
309
|
+
// ---------------------------------------------------------------------------
|
|
310
|
+
|
|
311
|
+
function buildFinalMessage(data: Uint8Array, version: number, ecl: ErrorCorrectionLevel): Uint8Array {
|
|
312
|
+
const eclOrd = EC_ORDINALS[ecl];
|
|
313
|
+
const [g1Count, g1DataCw, g2Count, g2DataCw] = EC_BLOCKS[version - 1][eclOrd];
|
|
314
|
+
const totalCw = TOTAL_CODEWORDS[version - 1];
|
|
315
|
+
const totalBlocks = g1Count + g2Count;
|
|
316
|
+
const ecCwPerBlock = Math.floor((totalCw - DATA_CODEWORDS[version - 1][eclOrd]) / totalBlocks);
|
|
317
|
+
|
|
318
|
+
// Split data into blocks
|
|
319
|
+
const dataBlocks: Uint8Array[] = [];
|
|
320
|
+
const ecBlocks: Uint8Array[] = [];
|
|
321
|
+
let offset = 0;
|
|
322
|
+
|
|
323
|
+
for (let i = 0; i < g1Count; i++) {
|
|
324
|
+
const block = data.slice(offset, offset + g1DataCw);
|
|
325
|
+
dataBlocks.push(block);
|
|
326
|
+
ecBlocks.push(rsEncode(block, ecCwPerBlock));
|
|
327
|
+
offset += g1DataCw;
|
|
328
|
+
}
|
|
329
|
+
for (let i = 0; i < g2Count; i++) {
|
|
330
|
+
const block = data.slice(offset, offset + g2DataCw);
|
|
331
|
+
dataBlocks.push(block);
|
|
332
|
+
ecBlocks.push(rsEncode(block, ecCwPerBlock));
|
|
333
|
+
offset += g2DataCw;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Interleave data codewords
|
|
337
|
+
const result: number[] = [];
|
|
338
|
+
const maxDataCw = Math.max(g1DataCw, g2DataCw);
|
|
339
|
+
for (let i = 0; i < maxDataCw; i++) {
|
|
340
|
+
for (let j = 0; j < totalBlocks; j++) {
|
|
341
|
+
if (i < dataBlocks[j].length) {
|
|
342
|
+
result.push(dataBlocks[j][i]);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Interleave EC codewords
|
|
348
|
+
for (let i = 0; i < ecCwPerBlock; i++) {
|
|
349
|
+
for (let j = 0; j < totalBlocks; j++) {
|
|
350
|
+
result.push(ecBlocks[j][i]);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return new Uint8Array(result);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// ---------------------------------------------------------------------------
|
|
358
|
+
// QR matrix construction
|
|
359
|
+
// ---------------------------------------------------------------------------
|
|
360
|
+
|
|
361
|
+
/** Module size = 4 * version + 17 */
|
|
362
|
+
function getModuleCount(version: number): number {
|
|
363
|
+
return version * 4 + 17;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/** Create a blank matrix (-1 = unset). */
|
|
367
|
+
function createMatrix(size: number): number[][] {
|
|
368
|
+
return Array.from({ length: size }, () => Array(size).fill(-1));
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function setModule(matrix: number[][], row: number, col: number, value: number): void {
|
|
372
|
+
if (row >= 0 && row < matrix.length && col >= 0 && col < matrix.length) {
|
|
373
|
+
matrix[row][col] = value;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/** Place a finder pattern with its separator. */
|
|
378
|
+
function placeFinderPattern(matrix: number[][], row: number, col: number): void {
|
|
379
|
+
for (let r = -1; r <= 7; r++) {
|
|
380
|
+
for (let c = -1; c <= 7; c++) {
|
|
381
|
+
const isPattern = r >= 0 && r <= 6 && c >= 0 && c <= 6;
|
|
382
|
+
let value = 0;
|
|
383
|
+
if (isPattern) {
|
|
384
|
+
if (r === 0 || r === 6 || c === 0 || c === 6 ||
|
|
385
|
+
(r >= 2 && r <= 4 && c >= 2 && c <= 4)) {
|
|
386
|
+
value = 1;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
setModule(matrix, row + r, col + c, value);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/** Place alignment patterns. */
|
|
395
|
+
function placeAlignmentPatterns(matrix: number[][], version: number): void {
|
|
396
|
+
if (version < 2) return;
|
|
397
|
+
const coords = ALIGNMENT_COORDS[version - 1];
|
|
398
|
+
for (const r of coords) {
|
|
399
|
+
for (const c of coords) {
|
|
400
|
+
// Skip if overlapping finder patterns
|
|
401
|
+
if (matrix[r][c] !== -1) continue;
|
|
402
|
+
for (let dr = -2; dr <= 2; dr++) {
|
|
403
|
+
for (let dc = -2; dc <= 2; dc++) {
|
|
404
|
+
const value = (Math.abs(dr) === 2 || Math.abs(dc) === 2 ||
|
|
405
|
+
(dr === 0 && dc === 0)) ? 1 : 0;
|
|
406
|
+
setModule(matrix, r + dr, c + dc, value);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/** Place timing patterns. */
|
|
414
|
+
function placeTimingPatterns(matrix: number[][]): void {
|
|
415
|
+
const size = matrix.length;
|
|
416
|
+
for (let i = 8; i < size - 8; i++) {
|
|
417
|
+
if (matrix[6][i] === -1) matrix[6][i] = (i + 1) % 2;
|
|
418
|
+
if (matrix[i][6] === -1) matrix[i][6] = (i + 1) % 2;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/** Reserve format info areas (filled later). */
|
|
423
|
+
function reserveFormatAreas(matrix: number[][]): void {
|
|
424
|
+
const size = matrix.length;
|
|
425
|
+
// Around top-left finder
|
|
426
|
+
for (let i = 0; i <= 8; i++) {
|
|
427
|
+
if (matrix[8][i] === -1) matrix[8][i] = 0;
|
|
428
|
+
if (matrix[i][8] === -1) matrix[i][8] = 0;
|
|
429
|
+
}
|
|
430
|
+
// Around top-right finder
|
|
431
|
+
for (let i = 0; i <= 7; i++) {
|
|
432
|
+
if (matrix[8][size - 1 - i] === -1) matrix[8][size - 1 - i] = 0;
|
|
433
|
+
}
|
|
434
|
+
// Around bottom-left finder
|
|
435
|
+
for (let i = 0; i <= 7; i++) {
|
|
436
|
+
if (matrix[size - 1 - i][8] === -1) matrix[size - 1 - i][8] = 0;
|
|
437
|
+
}
|
|
438
|
+
// Dark module
|
|
439
|
+
matrix[size - 8][8] = 1;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/** Reserve version info areas (versions >= 7). */
|
|
443
|
+
function reserveVersionAreas(matrix: number[][], version: number): void {
|
|
444
|
+
if (version < 7) return;
|
|
445
|
+
const size = matrix.length;
|
|
446
|
+
for (let i = 0; i < 6; i++) {
|
|
447
|
+
for (let j = 0; j < 3; j++) {
|
|
448
|
+
if (matrix[i][size - 11 + j] === -1) matrix[i][size - 11 + j] = 0;
|
|
449
|
+
if (matrix[size - 11 + j][i] === -1) matrix[size - 11 + j][i] = 0;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/** Place data bits in the matrix using the zigzag pattern. */
|
|
455
|
+
function placeDataBits(matrix: number[][], data: Uint8Array): void {
|
|
456
|
+
const size = matrix.length;
|
|
457
|
+
let bitIndex = 0;
|
|
458
|
+
const totalBits = data.length * 8;
|
|
459
|
+
let upward = true;
|
|
460
|
+
|
|
461
|
+
for (let right = size - 1; right >= 1; right -= 2) {
|
|
462
|
+
// Skip timing pattern column
|
|
463
|
+
if (right === 6) right = 5;
|
|
464
|
+
|
|
465
|
+
for (let vert = 0; vert < size; vert++) {
|
|
466
|
+
const row = upward ? size - 1 - vert : vert;
|
|
467
|
+
for (let colOffset = 0; colOffset <= 1; colOffset++) {
|
|
468
|
+
const col = right - colOffset;
|
|
469
|
+
if (matrix[row][col] !== -1) continue;
|
|
470
|
+
if (bitIndex < totalBits) {
|
|
471
|
+
const byteIdx = bitIndex >>> 3;
|
|
472
|
+
const bitIdx = 7 - (bitIndex & 7);
|
|
473
|
+
matrix[row][col] = (data[byteIdx] >>> bitIdx) & 1;
|
|
474
|
+
bitIndex++;
|
|
475
|
+
} else {
|
|
476
|
+
matrix[row][col] = 0;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
upward = !upward;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// ---------------------------------------------------------------------------
|
|
485
|
+
// Masking
|
|
486
|
+
// ---------------------------------------------------------------------------
|
|
487
|
+
|
|
488
|
+
type MaskFn = (row: number, col: number) => boolean;
|
|
489
|
+
|
|
490
|
+
const MASK_FUNCTIONS: MaskFn[] = [
|
|
491
|
+
(r, c) => (r + c) % 2 === 0,
|
|
492
|
+
(r, _c) => r % 2 === 0,
|
|
493
|
+
(_r, c) => c % 3 === 0,
|
|
494
|
+
(r, c) => (r + c) % 3 === 0,
|
|
495
|
+
(r, c) => (Math.floor(r / 2) + Math.floor(c / 3)) % 2 === 0,
|
|
496
|
+
(r, c) => ((r * c) % 2 + (r * c) % 3) === 0,
|
|
497
|
+
(r, c) => ((r * c) % 2 + (r * c) % 3) % 2 === 0,
|
|
498
|
+
(r, c) => ((r + c) % 2 + (r * c) % 3) % 2 === 0,
|
|
499
|
+
];
|
|
500
|
+
|
|
501
|
+
/** Check if a module is a function pattern (not data). */
|
|
502
|
+
function isFunctionModule(matrix: number[][], reserved: number[][], row: number, col: number): boolean {
|
|
503
|
+
return reserved[row][col] !== -1;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function applyMask(matrix: number[][], reserved: number[][], maskIdx: number): number[][] {
|
|
507
|
+
const size = matrix.length;
|
|
508
|
+
const result = matrix.map(row => [...row]);
|
|
509
|
+
const fn = MASK_FUNCTIONS[maskIdx];
|
|
510
|
+
for (let r = 0; r < size; r++) {
|
|
511
|
+
for (let c = 0; c < size; c++) {
|
|
512
|
+
if (!isFunctionModule(result, reserved, r, c) && fn(r, c)) {
|
|
513
|
+
result[r][c] ^= 1;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return result;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// ---------------------------------------------------------------------------
|
|
521
|
+
// Format & version info writing
|
|
522
|
+
// ---------------------------------------------------------------------------
|
|
523
|
+
|
|
524
|
+
function writeFormatInfo(matrix: number[][], ecl: ErrorCorrectionLevel, maskIdx: number): void {
|
|
525
|
+
const eclOrd = EC_ORDINALS[ecl];
|
|
526
|
+
const bits = FORMAT_INFO[eclOrd][maskIdx];
|
|
527
|
+
const size = matrix.length;
|
|
528
|
+
|
|
529
|
+
for (let i = 0; i <= 5; i++) {
|
|
530
|
+
matrix[8][i] = (bits >>> (14 - i)) & 1;
|
|
531
|
+
}
|
|
532
|
+
matrix[8][7] = (bits >>> 8) & 1;
|
|
533
|
+
matrix[8][8] = (bits >>> 7) & 1;
|
|
534
|
+
matrix[7][8] = (bits >>> 6) & 1;
|
|
535
|
+
for (let i = 0; i <= 5; i++) {
|
|
536
|
+
matrix[5 - i][8] = (bits >>> (i)) & 1;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
for (let i = 0; i <= 7; i++) {
|
|
540
|
+
matrix[8][size - 1 - i] = (bits >>> i) & 1;
|
|
541
|
+
}
|
|
542
|
+
for (let i = 0; i <= 6; i++) {
|
|
543
|
+
matrix[size - 1 - i][8] = (bits >>> (14 - i)) & 1;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
matrix[size - 8][8] = 1; // dark module
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
function writeVersionInfo(matrix: number[][], version: number): void {
|
|
550
|
+
if (version < 7) return;
|
|
551
|
+
const bits = VERSION_INFO[version - 7];
|
|
552
|
+
const size = matrix.length;
|
|
553
|
+
for (let i = 0; i < 6; i++) {
|
|
554
|
+
for (let j = 0; j < 3; j++) {
|
|
555
|
+
const bitIdx = i * 3 + j;
|
|
556
|
+
const bit = (bits >>> bitIdx) & 1;
|
|
557
|
+
matrix[i][size - 11 + j] = bit;
|
|
558
|
+
matrix[size - 11 + j][i] = bit;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// ---------------------------------------------------------------------------
|
|
564
|
+
// Penalty scoring
|
|
565
|
+
// ---------------------------------------------------------------------------
|
|
566
|
+
|
|
567
|
+
function penaltyScore(matrix: number[][]): number {
|
|
568
|
+
const size = matrix.length;
|
|
569
|
+
let score = 0;
|
|
570
|
+
|
|
571
|
+
// Rule 1: 5+ consecutive same-color modules in row/col
|
|
572
|
+
for (let r = 0; r < size; r++) {
|
|
573
|
+
let count = 1;
|
|
574
|
+
for (let c = 1; c < size; c++) {
|
|
575
|
+
if (matrix[r][c] === matrix[r][c - 1]) {
|
|
576
|
+
count++;
|
|
577
|
+
} else {
|
|
578
|
+
if (count >= 5) score += count - 2;
|
|
579
|
+
count = 1;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
if (count >= 5) score += count - 2;
|
|
583
|
+
}
|
|
584
|
+
for (let c = 0; c < size; c++) {
|
|
585
|
+
let count = 1;
|
|
586
|
+
for (let r = 1; r < size; r++) {
|
|
587
|
+
if (matrix[r][c] === matrix[r - 1][c]) {
|
|
588
|
+
count++;
|
|
589
|
+
} else {
|
|
590
|
+
if (count >= 5) score += count - 2;
|
|
591
|
+
count = 1;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
if (count >= 5) score += count - 2;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Rule 2: 2x2 blocks of same color
|
|
598
|
+
for (let r = 0; r < size - 1; r++) {
|
|
599
|
+
for (let c = 0; c < size - 1; c++) {
|
|
600
|
+
const val = matrix[r][c];
|
|
601
|
+
if (val === matrix[r][c + 1] && val === matrix[r + 1][c] && val === matrix[r + 1][c + 1]) {
|
|
602
|
+
score += 3;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Rule 3: Finder-like patterns
|
|
608
|
+
const pattern1 = [1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0];
|
|
609
|
+
const pattern2 = [0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1];
|
|
610
|
+
for (let r = 0; r < size; r++) {
|
|
611
|
+
for (let c = 0; c <= size - 11; c++) {
|
|
612
|
+
let match1 = true;
|
|
613
|
+
let match2 = true;
|
|
614
|
+
for (let i = 0; i < 11; i++) {
|
|
615
|
+
if (matrix[r][c + i] !== pattern1[i]) match1 = false;
|
|
616
|
+
if (matrix[r][c + i] !== pattern2[i]) match2 = false;
|
|
617
|
+
}
|
|
618
|
+
if (match1 || match2) score += 40;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
for (let c = 0; c < size; c++) {
|
|
622
|
+
for (let r = 0; r <= size - 11; r++) {
|
|
623
|
+
let match1 = true;
|
|
624
|
+
let match2 = true;
|
|
625
|
+
for (let i = 0; i < 11; i++) {
|
|
626
|
+
if (matrix[r + i][c] !== pattern1[i]) match1 = false;
|
|
627
|
+
if (matrix[r + i][c] !== pattern2[i]) match2 = false;
|
|
628
|
+
}
|
|
629
|
+
if (match1 || match2) score += 40;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// Rule 4: Proportion of dark modules
|
|
634
|
+
let dark = 0;
|
|
635
|
+
for (let r = 0; r < size; r++) {
|
|
636
|
+
for (let c = 0; c < size; c++) {
|
|
637
|
+
if (matrix[r][c] === 1) dark++;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
const total = size * size;
|
|
641
|
+
const percent = (dark / total) * 100;
|
|
642
|
+
const prev5 = Math.floor(percent / 5) * 5;
|
|
643
|
+
const next5 = prev5 + 5;
|
|
644
|
+
score += Math.min(Math.abs(prev5 - 50) / 5, Math.abs(next5 - 50) / 5) * 10;
|
|
645
|
+
|
|
646
|
+
return score;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// ---------------------------------------------------------------------------
|
|
650
|
+
// Public API
|
|
651
|
+
// ---------------------------------------------------------------------------
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* Find the minimum version that can hold the given data.
|
|
655
|
+
*/
|
|
656
|
+
function findMinVersion(text: string, ecl: ErrorCorrectionLevel): number {
|
|
657
|
+
const bytes = new TextEncoder().encode(text);
|
|
658
|
+
const dataLen = bytes.length;
|
|
659
|
+
const eclOrd = EC_ORDINALS[ecl];
|
|
660
|
+
|
|
661
|
+
for (let v = 1; v <= 40; v++) {
|
|
662
|
+
const ccBits = v <= 9 ? 8 : 16;
|
|
663
|
+
const totalBits = 4 + ccBits + dataLen * 8;
|
|
664
|
+
const capacity = DATA_CODEWORDS[v - 1][eclOrd] * 8;
|
|
665
|
+
if (totalBits <= capacity) return v;
|
|
666
|
+
}
|
|
667
|
+
throw new Error('Data too long for any QR version');
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Generate a QR code matrix from a string.
|
|
672
|
+
*
|
|
673
|
+
* @param text The text to encode.
|
|
674
|
+
* @param ecl Error-correction level (L, M, Q, H).
|
|
675
|
+
* @returns A 2-D boolean-like matrix where `1` = dark module.
|
|
676
|
+
*/
|
|
677
|
+
export function generateQR(text: string, ecl: ErrorCorrectionLevel = 'H'): number[][] {
|
|
678
|
+
if (!text) {
|
|
679
|
+
// Return a minimal empty 21x21 (version 1) blank grid
|
|
680
|
+
return Array.from({ length: 21 }, () => Array(21).fill(0));
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
const version = findMinVersion(text, ecl);
|
|
684
|
+
const moduleCount = getModuleCount(version);
|
|
685
|
+
|
|
686
|
+
// 1. Build function-pattern matrix (reserved areas)
|
|
687
|
+
const reserved = createMatrix(moduleCount);
|
|
688
|
+
placeFinderPattern(reserved, 0, 0);
|
|
689
|
+
placeFinderPattern(reserved, 0, moduleCount - 7);
|
|
690
|
+
placeFinderPattern(reserved, moduleCount - 7, 0);
|
|
691
|
+
placeAlignmentPatterns(reserved, version);
|
|
692
|
+
placeTimingPatterns(reserved);
|
|
693
|
+
reserveFormatAreas(reserved);
|
|
694
|
+
reserveVersionAreas(reserved, version);
|
|
695
|
+
|
|
696
|
+
// 2. Build actual matrix with function patterns
|
|
697
|
+
const matrix = createMatrix(moduleCount);
|
|
698
|
+
placeFinderPattern(matrix, 0, 0);
|
|
699
|
+
placeFinderPattern(matrix, 0, moduleCount - 7);
|
|
700
|
+
placeFinderPattern(matrix, moduleCount - 7, 0);
|
|
701
|
+
placeAlignmentPatterns(matrix, version);
|
|
702
|
+
placeTimingPatterns(matrix);
|
|
703
|
+
reserveFormatAreas(matrix);
|
|
704
|
+
reserveVersionAreas(matrix, version);
|
|
705
|
+
|
|
706
|
+
// 3. Encode & place data
|
|
707
|
+
const dataCw = encodeData(text, version, ecl);
|
|
708
|
+
const finalMsg = buildFinalMessage(dataCw, version, ecl);
|
|
709
|
+
placeDataBits(matrix, finalMsg);
|
|
710
|
+
|
|
711
|
+
// 4. Try all 8 masks, pick the best
|
|
712
|
+
let bestMask = 0;
|
|
713
|
+
let bestScore = Infinity;
|
|
714
|
+
let bestMatrix = matrix;
|
|
715
|
+
|
|
716
|
+
for (let mask = 0; mask < 8; mask++) {
|
|
717
|
+
const candidate = applyMask(matrix, reserved, mask);
|
|
718
|
+
writeFormatInfo(candidate, ecl, mask);
|
|
719
|
+
writeVersionInfo(candidate, version);
|
|
720
|
+
const score = penaltyScore(candidate);
|
|
721
|
+
if (score < bestScore) {
|
|
722
|
+
bestScore = score;
|
|
723
|
+
bestMask = mask;
|
|
724
|
+
bestMatrix = candidate;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// Re-apply best mask (already stored)
|
|
729
|
+
writeFormatInfo(bestMatrix, ecl, bestMask);
|
|
730
|
+
writeVersionInfo(bestMatrix, version);
|
|
731
|
+
|
|
732
|
+
return bestMatrix;
|
|
733
|
+
}
|