parent
							
								
									e8cd98c25d
								
							
						
					
					
						commit
						951143d63d
					
				
							
								
								
									
										4
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -129,15 +129,18 @@ declare module '@vue/runtime-core' { | ||||
|     NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default'] | ||||
|     NCode: typeof import('naive-ui')['NCode'] | ||||
|     NCollapseTransition: typeof import('naive-ui')['NCollapseTransition'] | ||||
|     NColorPicker: typeof import('naive-ui')['NColorPicker'] | ||||
|     NConfigProvider: typeof import('naive-ui')['NConfigProvider'] | ||||
|     NDivider: typeof import('naive-ui')['NDivider'] | ||||
|     NEllipsis: typeof import('naive-ui')['NEllipsis'] | ||||
|     NForm: typeof import('naive-ui')['NForm'] | ||||
|     NFormItem: typeof import('naive-ui')['NFormItem'] | ||||
|     NGi: typeof import('naive-ui')['NGi'] | ||||
|     NGrid: typeof import('naive-ui')['NGrid'] | ||||
|     NH1: typeof import('naive-ui')['NH1'] | ||||
|     NH3: typeof import('naive-ui')['NH3'] | ||||
|     NIcon: typeof import('naive-ui')['NIcon'] | ||||
|     NImage: typeof import('naive-ui')['NImage'] | ||||
|     NInputNumber: typeof import('naive-ui')['NInputNumber'] | ||||
|     NLabel: typeof import('naive-ui')['NLabel'] | ||||
|     NLayout: typeof import('naive-ui')['NLayout'] | ||||
| @ -152,6 +155,7 @@ declare module '@vue/runtime-core' { | ||||
|     PdfSignatureDetails: typeof import('./src/tools/pdf-signature-checker/components/pdf-signature-details.vue')['default'] | ||||
|     PercentageCalculator: typeof import('./src/tools/percentage-calculator/percentage-calculator.vue')['default'] | ||||
|     PhoneParserAndFormatter: typeof import('./src/tools/phone-parser-and-formatter/phone-parser-and-formatter.vue')['default'] | ||||
|     QrCodeDecoder: typeof import('./src/tools/qr-code-decoder/qr-code-decoder.vue')['default'] | ||||
|     QrCodeGenerator: typeof import('./src/tools/qr-code-generator/qr-code-generator.vue')['default'] | ||||
|     RandomPortGenerator: typeof import('./src/tools/random-port-generator/random-port-generator.vue')['default'] | ||||
|     ResultRow: typeof import('./src/tools/ipv4-range-expander/result-row.vue')['default'] | ||||
|  | ||||
| @ -64,6 +64,7 @@ | ||||
|     "highlight.js": "^11.7.0", | ||||
|     "iarna-toml-esm": "^3.0.5", | ||||
|     "ibantools": "^4.3.3", | ||||
|     "ical.js": "^2.0.1", | ||||
|     "json5": "^2.2.3", | ||||
|     "jwt-decode": "^3.1.2", | ||||
|     "libphonenumber-js": "^1.10.28", | ||||
|  | ||||
							
								
								
									
										24
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										24
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							| @ -92,6 +92,9 @@ dependencies: | ||||
|   ibantools: | ||||
|     specifier: ^4.3.3 | ||||
|     version: 4.3.3 | ||||
|   ical.js: | ||||
|     specifier: ^2.0.1 | ||||
|     version: 2.0.1 | ||||
|   json5: | ||||
|     specifier: ^2.2.3 | ||||
|     version: 2.2.3 | ||||
| @ -3354,7 +3357,7 @@ packages: | ||||
|     dependencies: | ||||
|       '@unhead/dom': 0.5.1 | ||||
|       '@unhead/schema': 0.5.1 | ||||
|       '@vueuse/shared': 10.9.0(vue@3.3.4) | ||||
|       '@vueuse/shared': 11.0.3(vue@3.3.4) | ||||
|       unhead: 0.5.1 | ||||
|       vue: 3.3.4 | ||||
|     transitivePeerDependencies: | ||||
| @ -3996,10 +3999,10 @@ packages: | ||||
|       - vue | ||||
|     dev: false | ||||
| 
 | ||||
|   /@vueuse/shared@10.9.0(vue@3.3.4): | ||||
|     resolution: {integrity: sha512-Uud2IWncmAfJvRaFYzv5OHDli+FbOzxiVEQdLCKQKLyhz94PIyFC3CHcH7EDMwIn8NPtD06+PNbC/PiO0LGLtw==} | ||||
|   /@vueuse/shared@11.0.3(vue@3.3.4): | ||||
|     resolution: {integrity: sha512-0rY2m6HS5t27n/Vp5cTDsKTlNnimCqsbh/fmT2LgE+aaU42EMfXo8+bNX91W9I7DDmxfuACXMmrd7d79JxkqWA==} | ||||
|     dependencies: | ||||
|       vue-demi: 0.14.7(vue@3.3.4) | ||||
|       vue-demi: 0.14.10(vue@3.3.4) | ||||
|     transitivePeerDependencies: | ||||
|       - '@vue/composition-api' | ||||
|       - vue | ||||
| @ -6094,6 +6097,10 @@ packages: | ||||
|     resolution: {integrity: sha512-RUTlGuFj3cU/Qfu5YIrsIZjW34/VDgKOz5fDr64Mc4NWP9b2i48vQ39r5xCl1yyFQeyEG/lASstIQHAUX18rRA==} | ||||
|     dev: false | ||||
| 
 | ||||
|   /ical.js@2.0.1: | ||||
|     resolution: {integrity: sha512-uYYb1CwTXbd9NP/xTtgQZ5ivv6bpUjQu9VM98s3X78L3XRu00uJW5ZtmnLwyxhztpf5fSiRyDpFW7ZNCePlaPw==} | ||||
|     dev: false | ||||
| 
 | ||||
|   /iconv-lite@0.6.3: | ||||
|     resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} | ||||
|     engines: {node: '>=0.10.0'} | ||||
| @ -9149,8 +9156,8 @@ packages: | ||||
|       vue: 3.3.4 | ||||
|     dev: false | ||||
| 
 | ||||
|   /vue-demi@0.14.5(vue@3.3.4): | ||||
|     resolution: {integrity: sha512-o9NUVpl/YlsGJ7t+xuqJKx8EBGf1quRhCiT6D/J0pfwmk9zUwYkC7yrF4SZCe6fETvSM3UNL2edcbYrSyc4QHA==} | ||||
|   /vue-demi@0.14.10(vue@3.3.4): | ||||
|     resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} | ||||
|     engines: {node: '>=12'} | ||||
|     hasBin: true | ||||
|     requiresBuild: true | ||||
| @ -9164,8 +9171,8 @@ packages: | ||||
|       vue: 3.3.4 | ||||
|     dev: false | ||||
| 
 | ||||
|   /vue-demi@0.14.7(vue@3.3.4): | ||||
|     resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==} | ||||
|   /vue-demi@0.14.5(vue@3.3.4): | ||||
|     resolution: {integrity: sha512-o9NUVpl/YlsGJ7t+xuqJKx8EBGf1quRhCiT6D/J0pfwmk9zUwYkC7yrF4SZCe6fETvSM3UNL2edcbYrSyc4QHA==} | ||||
|     engines: {node: '>=12'} | ||||
|     hasBin: true | ||||
|     requiresBuild: true | ||||
| @ -9455,6 +9462,7 @@ packages: | ||||
| 
 | ||||
|   /workbox-google-analytics@7.0.0: | ||||
|     resolution: {integrity: sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==} | ||||
|     deprecated: It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained | ||||
|     dependencies: | ||||
|       workbox-background-sync: 7.0.0 | ||||
|       workbox-core: 7.0.0 | ||||
|  | ||||
							
								
								
									
										3
									
								
								src/tools/qr-code-decoder/ical.js.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/tools/qr-code-decoder/ical.js.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| declare module "ical.js" { | ||||
|     export function parse(content: string): object; | ||||
| } | ||||
							
								
								
									
										151
									
								
								src/tools/qr-code-decoder/qr-code-decoder.service.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								src/tools/qr-code-decoder/qr-code-decoder.service.test.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,151 @@ | ||||
| import { describe, expect, test } from 'vitest'; | ||||
| import { parseQRData } from './qr-code-decoder.service'; | ||||
| 
 | ||||
| describe('qr-code-decoder', () => { | ||||
|   test('parseQRData should parse content correctly', () => { | ||||
|     expect(parseQRData(null)).toEqual({ | ||||
|       type: 'Unknown', | ||||
|       value: '', | ||||
|     }); | ||||
|     expect(parseQRData('')).toEqual({ | ||||
|       type: 'Unknown', | ||||
|       value: '', | ||||
|     }); | ||||
|     expect(parseQRData('TEL:+33123456')).toEqual({ | ||||
|       type: 'Phone', | ||||
|       value: '+33123456', | ||||
|     }); | ||||
|     expect(parseQRData('MATMSG:TO: email@example.com;SUB:email subject;BODY:Email text;;')).toEqual({ | ||||
|       type: 'Email', | ||||
|       value: { | ||||
|         body: 'Email text', | ||||
|         subject: 'email subject', | ||||
|         to: 'email@example.com', | ||||
|       }, | ||||
|     }); | ||||
|     expect(parseQRData('mailto:email@example.com?subject=email subject&body=Email text')).toEqual({ | ||||
|       type: 'Email', | ||||
|       value: { | ||||
|         body: 'Email text', | ||||
|         subject: 'email subject', | ||||
|         to: 'email@example.com', | ||||
|       }, | ||||
|     }); | ||||
|     expect(parseQRData('SMTP:email@example.com:email subject:Email text')).toEqual({ | ||||
|       type: 'Email', | ||||
|       value: { | ||||
|         body: 'Email text', | ||||
|         subject: 'email subject', | ||||
|         to: 'email@example.com', | ||||
|       }, | ||||
|     }); | ||||
|     expect(parseQRData('smsto:+33315555:message')).toEqual({ | ||||
|       type: 'SMS', | ||||
|       value: { | ||||
|         message: 'message', | ||||
|         to: '+33315555', | ||||
|       }, | ||||
|     }); | ||||
|     expect(parseQRData('WIFI:T:nopass;S:ssid;H:true;')).toEqual({ | ||||
|       type: 'Wifi', | ||||
|       value: { | ||||
|         authentication: 'nopass', | ||||
|         hidden: 'true', | ||||
|         name: 'ssid', | ||||
|         password: undefined, | ||||
|       }, | ||||
|     }); | ||||
|     expect(parseQRData('WIFI:T:WPA;S:ssid;P:password;H:false;')).toEqual({ | ||||
|       type: 'Wifi', | ||||
|       value: { | ||||
|         authentication: 'WPA', | ||||
|         hidden: 'false', | ||||
|         name: 'ssid', | ||||
|         password: 'password', | ||||
|       }, | ||||
|     }); | ||||
|     expect(parseQRData('BEGIN:VCALENDAR\nPRODID:-//xyz Corp//NONSGML PDA Calendar Version 1.0//EN\nVERSION:2.0\nBEGIN:VEVENT\nDTSTAMP:19960704T120000Z\nUID:uid1@example.com\nORGANIZER:mailto:jsmith@example.com\nDTSTART:19960918T143000Z\nDTEND:19960920T220000Z\nSTATUS:CONFIRMED\nCATEGORIES:CONFERENCE\nSUMMARY:Networld+Interop Conference\nDESCRIPTION:Networld+Interop Conference\n  and Exhibit\\nAtlanta World Congress Center\\n\n Atlanta\\, Georgia\nEND:VEVENT\nEND:VCALENDAR')) | ||||
|       .toEqual({ | ||||
|         type: 'iCal', | ||||
|         value: [ | ||||
|           'vcalendar', | ||||
|           [ | ||||
|             [ | ||||
|               'prodid', | ||||
|               {}, | ||||
|               'text', | ||||
|               '-//xyz Corp//NONSGML PDA Calendar Version 1.0//EN', | ||||
|             ], | ||||
|             [ | ||||
|               'version', | ||||
|               {}, | ||||
|               'text', | ||||
|               '2.0', | ||||
|             ], | ||||
|           ], | ||||
|           [ | ||||
|             [ | ||||
|               'vevent', | ||||
|               [ | ||||
|                 [ | ||||
|                   'dtstamp', | ||||
|                   {}, | ||||
|                   'date-time', | ||||
|                   '1996-07-04T12:00:00Z', | ||||
|                 ], | ||||
|                 [ | ||||
|                   'uid', | ||||
|                   {}, | ||||
|                   'text', | ||||
|                   'uid1@example.com', | ||||
|                 ], | ||||
|                 [ | ||||
|                   'organizer', | ||||
|                   {}, | ||||
|                   'cal-address', | ||||
|                   'mailto:jsmith@example.com', | ||||
|                 ], | ||||
|                 [ | ||||
|                   'dtstart', | ||||
|                   {}, | ||||
|                   'date-time', | ||||
|                   '1996-09-18T14:30:00Z', | ||||
|                 ], | ||||
|                 [ | ||||
|                   'dtend', | ||||
|                   {}, | ||||
|                   'date-time', | ||||
|                   '1996-09-20T22:00:00Z', | ||||
|                 ], | ||||
|                 [ | ||||
|                   'status', | ||||
|                   {}, | ||||
|                   'text', | ||||
|                   'CONFIRMED', | ||||
|                 ], | ||||
|                 [ | ||||
|                   'categories', | ||||
|                   {}, | ||||
|                   'text', | ||||
|                   'CONFERENCE', | ||||
|                 ], | ||||
|                 [ | ||||
|                   'summary', | ||||
|                   {}, | ||||
|                   'text', | ||||
|                   'Networld+Interop Conference', | ||||
|                 ], | ||||
|                 [ | ||||
|                   'description', | ||||
|                   {}, | ||||
|                   'text', | ||||
|                   'Networld+Interop Conference and Exhibit\nAtlanta World Congress Center\nAtlanta, Georgia', | ||||
|                 ], | ||||
|               ], | ||||
|               [], | ||||
|             ], | ||||
|           ], | ||||
|         ], | ||||
|       }); | ||||
|   }); | ||||
| }); | ||||
							
								
								
									
										83
									
								
								src/tools/qr-code-decoder/qr-code-decoder.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/tools/qr-code-decoder/qr-code-decoder.service.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,83 @@ | ||||
| import ICAL from 'ical.js'; | ||||
| 
 | ||||
| export function parseQRData(qrContent: string | null) { | ||||
|   if (!qrContent) { | ||||
|     return { type: 'Unknown', value: '' }; | ||||
|   } | ||||
|   if (qrContent.startsWith('BEGIN:VCALENDAR')) { | ||||
|     return { type: 'iCal', value: ICAL.parse(qrContent?.trim()) }; | ||||
|   } | ||||
|   if (qrContent.startsWith('TEL:')) { | ||||
|     return { type: 'Phone', value: qrContent.substring(4)?.trim() }; | ||||
|   } | ||||
|   if (qrContent.startsWith('MATMSG:')) { | ||||
|     // MATMSG:TO: email@example.com;SUB:email subject;BODY:Email text;;
 | ||||
|     const parsing = /^MATMSG:(?:TO:([^;]*);)?(?:SUB:([^;]*);)?(?:BODY:([^;]*))?;;$/.exec(qrContent) || []; | ||||
|     return { | ||||
|       type: 'Email', | ||||
|       value: { | ||||
|         to: parsing[1]?.trim(), | ||||
|         subject: parsing[2]?.trim(), | ||||
|         body: parsing[3]?.trim(), | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|   if (qrContent.startsWith('mailto:')) { | ||||
|     // mailto:email@example.com?subject=email subject&body=Email text
 | ||||
|     const parsing = /^mailto:([^\?]+)\?subject=([^\&]*)(?:&body=(.*))$/.exec(qrContent) || []; | ||||
|     return { | ||||
|       type: 'Email', | ||||
|       value: { | ||||
|         to: parsing[1]?.trim(), | ||||
|         subject: parsing[2]?.trim(), | ||||
|         body: parsing[3]?.trim(), | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|   if (qrContent.startsWith('SMTP:')) { | ||||
|     // SMTP:email@example.com:email subject:Email text
 | ||||
|     const parsing = /^SMTP:([^:]+)(?::([^:]*))(?::([^:]*))?$/.exec(qrContent) || []; | ||||
|     return { | ||||
|       type: 'Email', | ||||
|       value: { | ||||
|         to: parsing[1]?.trim(), | ||||
|         subject: parsing[2]?.trim(), | ||||
|         body: parsing[3]?.trim(), | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|   if (qrContent.startsWith('smsto:')) { | ||||
|     // smsto:${phoneNumber}:${message}
 | ||||
|     const parsing = /^smsto:([^:]+)(?::(.+))$/.exec(qrContent) || []; | ||||
|     return { | ||||
|       type: 'SMS', | ||||
|       value: { | ||||
|         to: parsing[1]?.trim(), | ||||
|         message: parsing[2]?.trim(), | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|   if (qrContent.startsWith('WIFI:')) { | ||||
|     // WIFI:T:${authentication};S:${name};${authentication !== 'nopass' ? `P:${password};` : ''}H:${hidden};
 | ||||
|     const parsing = /^WIFI:T:([^;]+);S:([^;]+);(?:P:([^;]+);)?(?:H:([^;]+);)?$/.exec(qrContent) || []; | ||||
|     return { | ||||
|       type: 'Wifi', | ||||
|       value: { | ||||
|         authentication: parsing[1]?.trim(), | ||||
|         name: parsing[2]?.trim(), | ||||
|         password: parsing[3]?.trim(), | ||||
|         hidden: parsing[4]?.trim(), | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|   if (/\w+:\/\//.test(qrContent)) { | ||||
|     return { | ||||
|       type: 'Url', | ||||
|       value: qrContent, | ||||
|     }; | ||||
|   } | ||||
|   return { | ||||
|     type: 'Text', | ||||
|     value: qrContent, | ||||
|   }; | ||||
| } | ||||
| @ -1,6 +1,7 @@ | ||||
| <script setup lang="ts"> | ||||
| import type { Ref } from 'vue'; | ||||
| import qrcodeParser from 'qrcode-parser'; | ||||
| import { parseQRData } from './qr-code-decoder.service'; | ||||
| import TextareaCopyable from '@/components/TextareaCopyable.vue'; | ||||
| 
 | ||||
| const fileInput = ref() as Ref<File>; | ||||
| @ -12,6 +13,15 @@ const qrCode = computedAsync(async () => { | ||||
|     return e.toString(); | ||||
|   } | ||||
| }); | ||||
| const qrCodeParsed = computed(() => { | ||||
|   try { | ||||
|     const parsed = parseQRData(qrCode.value); | ||||
|     return `Type: ${parsed.type}\nValue:${JSON.stringify(parsed.value, null, 2)}`; | ||||
|   } | ||||
|   catch (e: any) { | ||||
|     return e.toString(); | ||||
|   } | ||||
| }); | ||||
| 
 | ||||
| async function onUpload(file: File) { | ||||
|   if (file) { | ||||
| @ -37,6 +47,13 @@ async function onUpload(file: File) { | ||||
|         :word-wrap="true" | ||||
|       /> | ||||
|     </div> | ||||
|     <div> | ||||
|       <h3>Parsed</h3> | ||||
|       <TextareaCopyable | ||||
|         :value="qrCodeParsed" | ||||
|         :word-wrap="true" | ||||
|       /> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user