UNPKG

@point-of-sale/barcode-parser

Version:

[![npm](https://img.shields.io/npm/v/@point-of-sale/barcode-parser)](https://www.npmjs.com/@point-of-sale/barcode-parser) ![GitHub License](https://img.shields.io/github/license/NielsLeenheer/BarcodeParser)

2,530 lines (2,394 loc) 47.9 kB
'use strict'; class Aim { static decode(aim, value) { let data = { symbology: null }; let code = aim[1]; let modifier = aim[2]; switch (code) { case 'A': data.symbology = 'code39'; break; case 'B': data.symbology = 'telepen'; break; case 'C': data.symbology = 'code128'; if (modifier === '1') { data.symbology = 'gs1-128'; data.fnc1 = 1; } break; case 'D': data.symbology = 'code1'; break; case 'E': if (value.length === 13) { data.symbology = 'ean13'; } else if (value.length === 12) { data.symbology = 'upca'; } else if (value.length === 8) { if (modifier === '4') { data.symbology = 'ean8'; } else { data.symbology = 'upce'; } } break; case 'F': data.symbology = 'codabar'; break; case 'G': data.symbology = 'code93'; break; case 'H': data.symbology = 'code11'; break; case 'I': data.symbology = 'interleaved-2-of-5'; break; case 'K': data.symbology = 'code16k'; break; case 'L': data.symbology = 'pdf417'; break; case 'M': data.symbology = 'msi'; break; case 'N': data.symbology = 'anker'; break; case 'O': if (modifier === '4' || modifier === '5') { data.symbology = 'codablock-f'; } if (modifier === '6') { data.symbology = 'codablock-a'; } break; case 'P': data.symbology = 'plessey'; break; case 'R': data.symbology = 'straight-2-of-5'; break; case 'S': data.symbology = 'straight-2-of-5'; break; case 'Q': data.symbology = 'qr-code'; if (modifier === '0') { data.model = 1; } else { data.model = 2; if (modifier === '3' || modifier === '4') { data.fnc1 = 1; } if (modifier === '5' || modifier === '6') { data.fnc1 = 2; } } break; case 'U': data.symbology = 'maxicode'; break; case 'X': /* Other barcode */ /* According to Honeywell, this is the identifier for Bookland/ISSN EAN-13 */ if (modifier === '0') { data.symbology = 'ean13'; } /* Zebra / Motorola uses it for duplicate identifiers already defined by AIM */ else { switch(modifier) { case '9': data.symbology = 'ean13'; break; case 'C': data.symbology = 'ean8'; break; case 'g': data.symbology = 'upca'; break; case 'k': data.symbology = 'upce'; break; case 'r': data.symbology = 'gs1-databar-omni'; break; case 's': data.symbology = 'gs1-databar-limited'; break; case 't': data.symbology = 'gs1-databar-expanded'; break; case 'V': data.symbology = 'pdf417'; break; case 'S': data.symbology = 'qr-code-micro'; break; } } break; case 'c': data.symbology = 'channel-code'; break; case 'd': data.symbology = 'data-matrix'; if (modifier === '2' || modifier === '5') { data.fnc1 = 1; } if (modifier === '3' || modifier === '6') { data.fnc1 = 2; } break; case 'e': data.symbology = 'gs1-databar-omni'; break; case 'h': data.symbology = 'chinese-sensible-code'; break; case 'o': data.symbology = 'ocr'; break; case 'p': data.symbology = 'posi-code'; break; case 's': data.symbology = 'super-code'; break; case 'z': data.symbology = 'aztec-code'; if (modifier === '1' || modifier === '4' || modifier === '7' || modifier === 'A') { data.fnc1 = 1; } if (modifier === '2' || modifier === '5' || modifier === '8' || modifier === 'B') { data.fnc1 = true; /* Following an initial letter or pair of digits */ } break; } return data; } } const labelValues = [ [ "SSCC", [ "00" ] ], [ "GTIN", [ "01" ] ], [ "CONTENT", [ "02" ] ], [ "BATCH/LOT", [ "10" ] ], [ "PROD DATE", [ "11" ] ], [ "DUE DATE", [ "12" ] ], [ "PACK DATE", [ "13" ] ], [ "BEST BEFORE or BEST BY", [ "15" ] ], [ "SELL BY", [ "16" ] ], [ "USE BY OR EXPIRY", [ "17" ] ], [ "VARIANT", [ "20" ] ], [ "SERIAL", [ "21" ] ], [ "CPV", [ "22" ] ], [ "VAR. COUNT", [ "30" ] ], [ "COUNT", [ "37" ] ], [ "INTERNAL", [ "90", "91", "92", "93", "94", "95", "96", "97", "98", "99" ] ], [ "ADDITIONAL ID", [ "240" ] ], [ "CUST. PART NO.", [ "241" ] ], [ "MTO VARIANT", [ "242" ] ], [ "PCN", [ "243" ] ], [ "SECONDARY SERIAL", [ "250" ] ], [ "REF. TO SOURCE ", [ "251" ] ], [ "GDTI", [ "253" ] ], [ "GLN EXTENSION COMPONENT", [ "254" ] ], [ "GCN", [ "255" ] ], [ "ORDER NUMBER", [ "400" ] ], [ "GINC", [ "401" ] ], [ "GSIN", [ "402" ] ], [ "ROUTE", [ "403" ] ], [ "SHIP TO LOC", [ "410" ] ], [ "BILL TO ", [ "411" ] ], [ "PURCHASE FROM", [ "412" ] ], [ "SHIP FOR LOC", [ "413" ] ], [ "LOC No", [ "414" ] ], [ "PAY TO", [ "415" ] ], [ "PROD/SERV LOC", [ "416" ] ], [ "SHIP TO POST", [ "420", "421" ] ], [ "ORIGIN", [ "422" ] ], [ "COUNTRY - INITIAL PROCESS.", [ "423" ] ], [ "COUNTRY - PROCESS.", [ "424" ] ], [ "COUNTRY - DISASSEMBLY", [ "425" ] ], [ "COUNTRY - FULL PROCESS", [ "426" ] ], [ "ORIGIN SUBDIVISION", [ "427" ] ], [ "NHRN PZN", [ "710" ] ], [ "NHRN CIP", [ "711" ] ], [ "NHRN CN", [ "712" ] ], [ "NHRN DRN", [ "713" ] ], [ "NHRN AIM", [ "714" ] ], [ "NET WEIGHT (kg)", [ "3100", "3101", "3102", "3103", "3104", "3105" ] ], [ "LENGTH (m)", [ "3110", "3111", "3112", "3113", "3114", "3115" ] ], [ "WIDTH (m)", [ "3120", "3121", "3122", "3123", "3124", "3125" ] ], [ "HEIGHT (m)", [ "3130", "3131", "3132", "3133", "3134", "3135" ] ], [ "AREA (m^2)", [ "3140", "3141", "3142", "3143", "3144", "3145" ] ], [ "NET VOLUME (l)", [ "3150", "3151", "3152", "3153", "3154", "3155" ] ], [ "NET VOLUME (m^3)", [ "3160", "3161", "3162", "3163", "3164", "3165" ] ], [ "NET WEIGHT (lb)", [ "3200", "3201", "3202", "3203", "3204", "3205" ] ], [ "LENGTH (in)", [ "3210", "3211", "3212", "3213", "3214", "3215" ] ], [ "LENGTH (ft)", [ "3220", "3221", "3222", "3223", "3224", "3225" ] ], [ "LENGTH (yd)", [ "3230", "3231", "3232", "3233", "3234", "3235" ] ], [ "WIDTH (in)", [ "3240", "3241", "3242", "3243", "3244", "3245" ] ], [ "WIDTH (ft)", [ "3250", "3251", "3252", "3253", "3254", "3255" ] ], [ "WIDTH (yd)", [ "3260", "3261", "3262", "3263", "3264", "3265" ] ], [ "HEIGHT (in)", [ "3270", "3271", "3272", "3273", "3274", "3275" ] ], [ "HEIGHT (ft)", [ "3280", "3281", "3282", "3283", "3284", "3285" ] ], [ "HEIGHT (yd)", [ "3290", "3291", "3292", "3293", "3294", "3295" ] ], [ "GROSS WEIGHT (kg)", [ "3300", "3301", "3302", "3303", "3304", "3305" ] ], [ "LENGTH (m), log", [ "3310", "3311", "3312", "3313", "3314", "3315" ] ], [ "WIDTH (m), log", [ "3320", "3321", "3322", "3323", "3324", "3325" ] ], [ "HEIGHT (m), log", [ "3330", "3331", "3332", "3333", "3334", "3335" ] ], [ "AREA (m^2), log", [ "3340", "3341", "3342", "3343", "3344", "3345" ] ], [ "VOLUME (l), log", [ "3350", "3351", "3352", "3353", "3354", "3355" ] ], [ "VOLUME (m^3), log", [ "3360", "3361", "3362", "3363", "3364", "3365" ] ], [ "KG PER m^2", [ "3370", "3371", "3372", "3373", "3374", "3375" ] ], [ "GROSS WEIGHT (lb)", [ "3400", "3401", "3402", "3403", "3404", "3405" ] ], [ "LENGTH (in), log", [ "3410", "3411", "3412", "3413", "3414", "3415" ] ], [ "LENGTH (ft), log", [ "3420", "3421", "3422", "3423", "3424", "3425" ] ], [ "LENGTH (yd), log", [ "3430", "3431", "3432", "3433", "3434", "3435" ] ], [ "WIDTH (in), log", [ "3440", "3441", "3442", "3443", "3444", "3445" ] ], [ "WIDTH (ft), log", [ "3450", "3451", "3452", "3453", "3454", "3455" ] ], [ "WIDTH (yd), log", [ "3460", "3461", "3462", "3463", "3464", "3465" ] ], [ "HEIGHT (in), log", [ "3470", "3471", "3472", "3473", "3474", "3475" ] ], [ "HEIGHT (ft), log", [ "3480", "3481", "3482", "3483", "3484", "3485" ] ], [ "HEIGHT (yd), log", [ "3490", "3491", "3492", "3493", "3494", "3495" ] ], [ "AREA (in^2)", [ "3500", "3501", "3502", "3503", "3504", "3505" ] ], [ "AREA (ft^2)", [ "3510", "3511", "3512", "3513", "3514", "3515" ] ], [ "AREA (yd^2)", [ "3520", "3521", "3522", "3523", "3524", "3525" ] ], [ "AREA (in^2), log", [ "3530", "3531", "3532", "3533", "3534", "3535" ] ], [ "AREA (ft^2), log", [ "3540", "3541", "3542", "3543", "3544", "3545" ] ], [ "AREA (yd^2), log", [ "3550", "3551", "3552", "3553", "3554", "3555" ] ], [ "NET WEIGHT (t oz)", [ "3560", "3561", "3562", "3563", "3564", "3565" ] ], [ "NET VOLUME (oz)", [ "3570", "3571", "3572", "3573", "3574", "3575" ] ], [ "NET VOLUME (qt)", [ "3600", "3601", "3602", "3603", "3604", "3605" ] ], [ "NET VOLUME (gal.)", [ "3610", "3611", "3612", "3613", "3614", "3615" ] ], [ "VOLUME (qt), log", [ "3620", "3621", "3622", "3623", "3624", "3625" ] ], [ "VOLUME (gal.), log", [ "3630", "3631", "3632", "3633", "3634", "3635" ] ], [ "VOLUME (in^3) ", [ "3640", "3641", "3642", "3643", "3644", "3645" ] ], [ "VOLUME (ft^3) ", [ "3650", "3651", "3652", "3653", "3654", "3655" ] ], [ "VOLUME (yd^3) ", [ "3660", "3661", "3662", "3663", "3664", "3665" ] ], [ "VOLUME (in^3), log", [ "3670", "3671", "3672", "3673", "3674", "3675" ] ], [ "VOLUME (ft^3), log", [ "3680", "3681", "3682", "3683", "3684", "3685" ] ], [ "VOLUME (yd^3), log", [ "3690", "3691", "3692", "3693", "3694", "3695" ] ], [ "AMOUNT", [ "3900", "3901", "3902", "3903", "3904", "3905", "3906", "3907", "3908", "3909", "3910", "3911", "3912", "3913", "3914", "3915", "3916", "3917", "3918", "3919" ] ], [ "PRICE", [ "3920", "3921", "3922", "3923", "3924", "3925", "3926", "3927", "3928", "3929", "3930", "3931", "3932", "3933", "3934", "3935", "3936", "3937", "3938", "3939" ] ], [ "PRCNT OFF", [ "3940", "3941", "3942", "3943" ] ], [ "NSN", [ "7001" ] ], [ "MEAT CUT", [ "7002" ] ], [ "EXPIRY TIME", [ "7003" ] ], [ "ACTIVE POTENCY", [ "7004" ] ], [ "CATCH AREA", [ "7005" ] ], [ "FIRST FREEZE DATE", [ "7006" ] ], [ "HARVEST DATE", [ "7007" ] ], [ "AQUATIC SPECIES", [ "7008" ] ], [ "FISHING GEAR TYPE", [ "7009" ] ], [ "PROD METHOD", [ "7010" ] ], [ "REFURB LOT", [ "7020" ] ], [ "FUNC STAT", [ "7021" ] ], [ "REV STAT", [ "7022" ] ], [ "GIAI - ASSEMBLY", [ "7023" ] ], [ "PROCESSOR # 0", [ "7030" ] ], [ "PROCESSOR # 1", [ "7031" ] ], [ "PROCESSOR # 2", [ "7032" ] ], [ "PROCESSOR # 3", [ "7033" ] ], [ "PROCESSOR # 4", [ "7034" ] ], [ "PROCESSOR # 5", [ "7035" ] ], [ "PROCESSOR # 6", [ "7036" ] ], [ "PROCESSOR # 7", [ "7037" ] ], [ "PROCESSOR # 8", [ "7038" ] ], [ "PROCESSOR # 9", [ "7039" ] ], [ "CERT # 0", [ "7230" ] ], [ "CERT # 1", [ "7231" ] ], [ "CERT # 2", [ "7232" ] ], [ "CERT # 3", [ "7233" ] ], [ "CERT # 4", [ "7234" ] ], [ "CERT # 5", [ "7235" ] ], [ "CERT # 6", [ "7236" ] ], [ "CERT # 7", [ "7237" ] ], [ "CERT # 8", [ "7238" ] ], [ "CERT # 9", [ "7239" ] ], [ "DIMENSIONS", [ "8001" ] ], [ "CMT No", [ "8002" ] ], [ "GRAI", [ "8003" ] ], [ "GIAI", [ "8004" ] ], [ "PRICE PER UNIT", [ "8005" ] ], [ "ITIP", [ "8006" ] ], [ "IBAN", [ "8007" ] ], [ "PROD TIME", [ "8008" ] ], [ "OPT SEN", [ "8009" ] ], [ "CPID", [ "8010" ] ], [ "CPID SERIAL", [ "8011" ] ], [ "VERSION", [ "8012" ] ], [ "GMN (for medical devices, the default, global data title is BUDI-DI )", [ "8013" ] ], [ "GSRN - PROVIDER", [ "8017" ] ], [ "GSRN - RECIPIENT", [ "8018" ] ], [ "SRIN", [ "8019" ] ], [ "REF No", [ "8020" ] ], [ "ITIP CONTENT", [ "8026" ] ], [ "POINTS", [ "8111" ] ], [ "PRODUCT URL", [ "8200" ] ] ]; const labels = Object.fromEntries( labelValues .map(i => i[1] .map(j => [ j, i[0] ])) .reduce((a,c) => [ ...a, ...c], []) ); const fixedLength = { "00": 20, "01": 16, "02": 16, "03": 16, "04": 18, "11": 8, "12": 8, "13": 8, "14": 8, "15": 8, "16": 8, "17": 8, "18": 8, "19": 8, "20": 4, "31": 10, "32": 10, "33": 10, "34": 10, "35": 10, "36": 10, "41": 16 }; const patternsValues = [ [ "(\\d{12,14}|\\d{8})", [ "01" ] ], [ "([\\x21-\\x22\\x25-\\x2F\\x30-\\x3F\\x41-\\x5A\\x5F\\x61-\\x7A]{0,20})", [ "10", "21", "22", "243", "254", "420", "710", "711", "712", "713", "714", "7020", "7021", "7022", "8002", "8012" ] ], [ "(\\d{6})", [ "11", "12", "13", "15", "16", "17", "3100", "3101", "3102", "3103", "3104", "3105", "3110", "3111", "3112", "3113", "3114", "3115", "3120", "3121", "3122", "3123", "3124", "3125", "3130", "3131", "3132", "3133", "3134", "3135", "3140", "3141", "3142", "3143", "3144", "3145", "3150", "3151", "3152", "3153", "3154", "3155", "3160", "3161", "3162", "3163", "3164", "3165", "3200", "3201", "3202", "3203", "3204", "3205", "3210", "3211", "3212", "3213", "3214", "3215", "3220", "3221", "3222", "3223", "3224", "3225", "3230", "3231", "3232", "3233", "3234", "3235", "3240", "3241", "3242", "3243", "3244", "3245", "3250", "3251", "3252", "3253", "3254", "3255", "3260", "3261", "3262", "3263", "3264", "3265", "3270", "3271", "3272", "3273", "3274", "3275", "3280", "3281", "3282", "3283", "3284", "3285", "3290", "3291", "3292", "3293", "3294", "3295", "3300", "3301", "3302", "3303", "3304", "3305", "3310", "3311", "3312", "3313", "3314", "3315", "3320", "3321", "3322", "3323", "3324", "3325", "3330", "3331", "3332", "3333", "3334", "3335", "3340", "3341", "3342", "3343", "3344", "3345", "3350", "3351", "3352", "3353", "3354", "3355", "3360", "3361", "3362", "3363", "3364", "3365", "3370", "3371", "3372", "3373", "3374", "3375", "3400", "3401", "3402", "3403", "3404", "3405", "3410", "3411", "3412", "3413", "3414", "3415", "3420", "3421", "3422", "3423", "3424", "3425", "3430", "3431", "3432", "3433", "3434", "3435", "3440", "3441", "3442", "3443", "3444", "3445", "3450", "3451", "3452", "3453", "3454", "3455", "3460", "3461", "3462", "3463", "3464", "3465", "3470", "3471", "3472", "3473", "3474", "3475", "3480", "3481", "3482", "3483", "3484", "3485", "3490", "3491", "3492", "3493", "3494", "3495", "3500", "3501", "3502", "3503", "3504", "3505", "3510", "3511", "3512", "3513", "3514", "3515", "3520", "3521", "3522", "3523", "3524", "3525", "3530", "3531", "3532", "3533", "3534", "3535", "3540", "3541", "3542", "3543", "3544", "3545", "3550", "3551", "3552", "3553", "3554", "3555", "3560", "3561", "3562", "3563", "3564", "3565", "3570", "3571", "3572", "3573", "3574", "3575", "3600", "3601", "3602", "3603", "3604", "3605", "3610", "3611", "3612", "3613", "3614", "3615", "3620", "3621", "3622", "3623", "3624", "3625", "3630", "3631", "3632", "3633", "3634", "3635", "3640", "3641", "3642", "3643", "3644", "3645", "3650", "3651", "3652", "3653", "3654", "3655", "3660", "3661", "3662", "3663", "3664", "3665", "3670", "3671", "3672", "3673", "3674", "3675", "3680", "3681", "3682", "3683", "3684", "3685", "3690", "3691", "3692", "3693", "3694", "3695", "7006", "8005" ] ], [ "(\\d{2})", [ "20" ] ], [ "(\\d{0,8})", [ "30", "37" ] ], [ "([\\x21-\\x22\\x25-\\x2F\\x30-\\x3F\\x41-\\x5A\\x5F\\x61-\\x7A]{0,30})", [ "90", "240", "241", "250", "251", "400", "401", "403", "7002", "7023", "8004", "8013" ] ], [ "([\\x21-\\x22\\x25-\\x2F\\x30-\\x3F\\x41-\\x5A\\x5F\\x61-\\x7A]{0,90})", [ "91", "92", "93", "94", "95", "96", "97", "98", "99" ] ], [ "(\\d{0,6})", [ "242" ] ], [ "(\\d{13})([\\x21-\\x22\\x25-\\x2F\\x30-\\x3F\\x41-\\x5A\\x5F\\x61-\\x7A]{0,17})", [ "253" ] ], [ "(\\d{13})(\\d{0,12})", [ "255" ] ], [ "(\\d{17})", [ "402" ] ], [ "(\\d{13})", [ "410", "411", "412", "413", "414", "415", "416", "7001" ] ], [ "(\\d{3})([\\x21-\\x22\\x25-\\x2F\\x30-\\x3F\\x41-\\x5A\\x5F\\x61-\\x7A]{0,9})", [ "421" ] ], [ "(\\d{3})", [ "422", "424", "426" ] ], [ "(\\d{3})(\\d{0,12})", [ "423", "425" ] ], [ "([\\x21-\\x22\\x25-\\x2F\\x30-\\x3F\\x41-\\x5A\\x5F\\x61-\\x7A]{0,3})", [ "427", "7008" ] ], [ "(\\d{0,15})", [ "3900", "3901", "3902", "3903", "3904", "3905", "3906", "3907", "3908", "3909", "3920", "3921", "3922", "3923", "3924", "3925", "3926", "3927", "3928", "3929" ] ], [ "(\\d{3})(\\d{0,15})", [ "3910", "3911", "3912", "3913", "3914", "3915", "3916", "3917", "3918", "3919", "3930", "3931", "3932", "3933", "3934", "3935", "3936", "3937", "3938", "3939" ] ], [ "(\\d{4})", [ "3940", "3941", "3942", "3943", "8111" ] ], [ "(\\d{10})", [ "7003" ] ], [ "(\\d{0,4})", [ "7004" ] ], [ "([\\x21-\\x22\\x25-\\x2F\\x30-\\x3F\\x41-\\x5A\\x5F\\x61-\\x7A]{0,12})", [ "7005" ] ], [ "(\\d{6,12})", [ "7007" ] ], [ "([\\x21-\\x22\\x25-\\x2F\\x30-\\x3F\\x41-\\x5A\\x5F\\x61-\\x7A]{0,10})", [ "7009" ] ], [ "([\\x21-\\x22\\x25-\\x2F\\x30-\\x3F\\x41-\\x5A\\x5F\\x61-\\x7A]{0,2})", [ "7010" ] ], [ "(\\d{3})([\\x21-\\x22\\x25-\\x2F\\x30-\\x3F\\x41-\\x5A\\x5F\\x61-\\x7A]{0,27})", [ "7030", "7031", "7032", "7033", "7034", "7035", "7036", "7037", "7038", "7039" ] ], [ "([\\x21-\\x22\\x25-\\x2F\\x30-\\x3F\\x41-\\x5A\\x5F\\x61-\\x7A]{2,30})", [ "7230", "7231", "7232", "7233", "7234", "7235", "7236", "7237", "7238", "7239" ] ], [ "(\\d{14})", [ "8001", "02" ] ], [ "(\\d{14})([\\x21-\\x22\\x25-\\x2F\\x30-\\x3F\\x41-\\x5A\\x5F\\x61-\\x7A]{0,16})", [ "8003" ] ], [ "(\\d{14})(\\d{2})(\\d{2})", [ "8006", "8026" ] ], [ "([\\x21-\\x22\\x25-\\x2F\\x30-\\x3F\\x41-\\x5A\\x5F\\x61-\\x7A]{0,34})", [ "8007" ] ], [ "(\\d{8})(\\d{0,4})", [ "8008" ] ], [ "([\\x21-\\x22\\x25-\\x2F\\x30-\\x3F\\x41-\\x5A\\x5F\\x61-\\x7A]{0,50})", [ "8009" ] ], [ "([\\x23\\x2D\\x2F\\x30-\\x39\\x41-\\x5A]{0,30})", [ "8010" ] ], [ "(\\d{0,12})", [ "8011" ] ], [ "(\\d{18})", [ "8017", "8018", "00" ] ], [ "(\\d{0,10})", [ "8019" ] ], [ "([\\x21-\\x22\\x25-\\x2F\\x30-\\x3F\\x41-\\x5A\\x5F\\x61-\\x7A]{0,25})", [ "8020" ] ], [ "([\\x21-\\x22\\x25-\\x2F\\x30-\\x3F\\x41-\\x5A\\x5F\\x61-\\x7A]{0,70})", [ "8110", "8112", "8200" ] ] ]; const patterns = Object.fromEntries( patternsValues .map(i => i[1] .map(j => [ j, i[0] ])) .reduce((a,c) => [ ...a, ...c], []) ); const checkDigitPositions = { "253": 13, "255": 13, "402": "L", "410": "L", "411": "L", "412": "L", "413": "L", "414": "L", "415": "L", "416": "L", "8003": 14, "8006": 14, "8017": "L", "8018": "L", "8026": 14, "00": "L", "01": "L", "02": "L" }; class DigitalLink { static is(data) { return typeof data === 'string' && ( data.match(/^(https?|HTTPS?):(\/\/((([^\/?#]*)@)?([^\/?#:]*)(:([^\/?#]*))?))?([^?#]*)(((\/(01|8006|8013|8010|414|415|417|8017|8018|255|00|253|401|402|8003|8004|240)\/)(\d{4}[^\/]+)(\/[^/]+\/[^/]+)?[/]?(\?([^?\n]*))?(#([^\n]*))?))/) || data.match(/^(https?|HTTPS?):(\/\/((([^\/?#]*)@)?([^\/?#:]*)(:([^\/?#]*))?))?([^?#]*)((\/[0-9A-Za-z_-]{10,}$))/) ); } static calculateCheckDigit(ai, value) { let counter = 0; let total = 0; let checkDigitPosition = checkDigitPositions[ai] === "L" ? value.length : checkDigitPositions[ai]; for (let i = checkDigitPosition - 2; i >= 0; i--) { let digit = value.charAt(i); let multiplier; if (counter % 2 === 0) { multiplier = 3; } else { multiplier = 1; } total += digit * multiplier; counter++; } return (10 - (total % 10)) % 10; } static verifyCheckDigit(ai, value) { if (!checkDigitPositions[ai]) { return true; } let checkDigitPosition = checkDigitPositions[ai] === "L" ? value.length : checkDigitPositions[ai]; let actualCheckDigit = parseInt(value.charAt(checkDigitPosition - 1), 10); let expectedCheckDigit = DigitalLink.calculateCheckDigit(ai, value); if (actualCheckDigit !== expectedCheckDigit) { console.log(`Invalid check digit: An invalid check digit was found for the primary identification key (${ai})${value}; the correct check digit should be ${expectedCheckDigit} at position ${checkDigitPosition}`); return false; } return true; } static verifySyntax(ai, value) { let re = new RegExp("^" + patterns[ai] + "$"); if (!re.test(value)) { console.log(`Syntax error: Invalid syntax for value of (${ai}): ${value}`); return false; } return true; } static parse(link) { let candidates = []; /* Extract the AI and value pairs from the link */ let url = new URL(link); if (url.pathname != '/') { let pairs = url.pathname.substr(1).split('/'); while (pairs.length >= 2) { candidates.push([ pairs.shift(), decodeURIComponent(pairs.shift()) ]); } } if (url.search != '') { let pairs = [ ...(new URLSearchParams(url.search.replace(/;/, '&'))) ]; for (let [ai, value] of pairs) { candidates.push([ ai, value ]); } } /* Verify the syntax and check digits of the elements */ let elements = []; for (let [ai, value] of candidates) { let valid = true; valid &= DigitalLink.verifySyntax(ai, value); valid &= DigitalLink.verifyCheckDigit(ai, value); if (valid) { if (ai == "01" || ai == "02") { value = value.padStart(14, '0'); } elements.push({ ai, value, label: labels[ai] }); } } /* Sort elements by AI */ elements.sort((a, b) => { return a.ai.localeCompare(b.ai); }); return elements; } } const GS = String.fromCharCode(29); class Elements { static parse(data) { let elements = []; /* Split the data into elements */ let segments = data.split(GS); for (let segment of segments) { while (segment.length) { let ai = segment.substr(0, 2); if (ai in fixedLength) { elements.push({ value: segment.substr(0, fixedLength[ai]) }); segment = segment.substr(fixedLength[ai]); } else { elements.push({ value: segment }); segment = ''; } } } /* Parse the elements */ for (let element of elements) { let aiSize; for (let i = 2; i <=4; i++) { if (element.value.substr(0, i) in labels) { aiSize = i; break; } } if (aiSize) { element.ai = element.value.substr(0, aiSize); element.value = element.value.substr(aiSize); element.label = labels[element.ai]; } } return elements; } } class UPCA { static compress(value) { if (typeof value !== "string" || value.length !== 12 || !/^\d{12}$/.test(value)) { return value; } if (value.slice(4, 8) == "0000" && value[3] >= "0" && value[3] <= "2") { return value.slice(0, 3) + value.slice(8, 11) + value[3] + value[11]; } if (value.slice(4, 9) == "00000") { return value.slice(0, 4) + value.slice(9, 11) + "3" + value[11]; } if (value.slice(5, 10) == "00000") { return value.slice(0, 5) + value.slice(10, 11) + "4" + value[11]; } if (value.slice(6, 10) == "0000" && value[11] >= 5 && value[11] <= 9) { return value.slice(0, 6) + value[10] + value[11]; } return value; } } class UPCE { static expand(value) { /* Digits 2 to 7 (UPC-E has a leading digit) */ const digits = value.substring(1, 7); /* The last digit (check digit) */ const lastDigit = value.charAt(7); let result = value.charAt(0); switch (digits.charAt(5)) { case '0': case '1': case '2': // If the 4th digit is 0, 1, or 2: Expand to form 0XXX00NNNN result += digits.substring(0, 2) + digits.charAt(5) + "0000" + digits.substring(2, 5); break; case '3': // If the 4th digit is 3: Expand to form 0XXX00000NNNN result += digits.substring(0, 3) + "00000" + digits.substring(3, 5); break; case '4': // If the 4th digit is 4: Expand to form 0XXXX00000NNNN result += digits.substring(0, 4) + "00000" + digits.charAt(4); break; default: // If the 4th digit is 5-9: Expand to form 0XXXXXNNNN result += digits.substring(0, 5) + "0000" + + digits.charAt(5); break; } result += lastDigit; return result; } } var Symbologies = { UPCA, UPCE }; class GS1 { static DigitalLink = DigitalLink; static parse(result) { let elements = []; /* Decode GS1 data if we have a FNC1 character or a GS character */ if (result.fnc1 || result.value.includes(String.fromCharCode(29))) { elements = Elements.parse(result.value); } /* Decode GS1 data if it is a GS1 Digital Link */ else if (DigitalLink.is(result.value)) { elements = DigitalLink.parse(result.value); } /* Decode GS1 data based on the symbology */ else if (typeof result.symbology === 'string') { /* Decode GS1 data if it is a GS1 DataBar */ if (result.symbology?.startsWith('gs1-databar')) { elements = Elements.parse(result.value); } /* Extract data from an ITF-14 barcode */ else if (result.symbology === 'itf' && result.value.length === 14) { elements = [ { ai: '01', label: 'GTIN', value: result.value } ]; } /* Extract data from EAN and UPC barcodes */ else if (['upca','ean8','ean13'].includes(result.symbology)) { elements = [ { ai: '01', label: 'GTIN', value: result.value.padStart(14, '0') } ]; } else if (result.symbology === 'upce') { elements = [ { ai: '01', label: 'GTIN', value: Symbologies.UPCE.expand(result.value).padStart(14, '0') } ]; } } /* Prepare return value */ if (elements.length) { let data = { elements }; let gtin = elements.find(e => e.ai === '01'); if (gtin) { data.gtin = gtin.value; } return data; } } } class Detector { static checkdigit(barcode) { const digits = barcode.split('').map(i => parseInt(i, 10)).reverse(); const total = digits.reduce((acc, number, i) => { return acc + (i % 2 === 1 ? number : number * 3); }, 0); return (Math.ceil(total / 10) * 10) - total; } static detect(barcode) { if (barcode.startsWith('http')) { return { symbology: 'qr-code', guess: true }; } else if (barcode.startsWith('X-HM:')) { return { symbology: 'qr-code', guess: false }; } else if (barcode.startsWith('MT:')) { return { symbology: 'qr-code', guess: false }; } else if (barcode.startsWith('WIFI:')) { return { symbology: 'qr-code', guess: false }; } else if (barcode.match(/^[0-9]+$/) && barcode.length == 8) { if (Detector.checkdigit(barcode.slice(0, -1)) == barcode.slice(-1)) { if (barcode.charAt(0) == '0' || barcode.charAt(0) == '1') { return { symbology: 'upce', guess: true }; } return { symbology: 'ean8', guess: false }; } } else if (barcode.match(/^[0-9]+$/) && barcode.length == 12) { if (Detector.checkdigit(barcode.slice(0, -1)) == barcode.slice(-1)) { return { symbology: 'upca', guess: false }; } } else if (barcode.match(/^[0-9]+$/) && barcode.length == 13) { if (Detector.checkdigit(barcode.slice(0, -1)) == barcode.slice(-1)) { return { symbology: 'ean13', guess: false }; } } else if (barcode.match(/^[0-9]+$/) && barcode.length == 14) { return { symbology: 'itf', guess: false }; } else if (barcode.match(/^M[0-9]/)) { return { symbology: 'aztec-code', guess: true }; } else if (barcode.length > 128) { return { symbology: 'pdf417', guess: true }; } else if (barcode.length > 32) { return { symbology: 'qr-code', guess: true }; } } } exports.Aim = Aim; exports.Detector = Detector; exports.GS1 = GS1; exports.Symbologies = Symbologies;