diff --git a/index.html b/index.html index 5c58a80..fed8caa 100644 --- a/index.html +++ b/index.html @@ -154,7 +154,7 @@ margin: 0 0.2vw; } - #card_template, #svg_card_template { + #card_template, #svg_card_template, #svg_barcode_template { display: none; } @@ -253,6 +253,10 @@

Batch Card Printer

+ +
+ +
@@ -299,6 +303,10 @@

Batch Card Printer

+ + + +

@@ -1346,11 +1354,13 @@

Batch Card Printer

} let random_batch_button = document.getElementById('random_batch_button'); random_batch_button.onclick = ()=>{ - this.random_batch(); + let barcode = document.getElementById('Batch_Option_Barcode'); + barcode.checked ? this.random_batch_with_barcode() : this.random_batch(); } let unique_batch_button = document.getElementById('unique_batch_button'); unique_batch_button.onclick = ()=>{ - this.unique_batch(); + let barcode = document.getElementById('Batch_Option_Barcode'); + barcode.checked ? this.unique_batch_with_barcode() : this.unique_batch(); } } @@ -1367,6 +1377,18 @@

Batch Card Printer

this.print_batch(cards); } + random_batch_with_barcode(){ + let batcher = new this.Batcher(this.bingo); + let cards = batcher.generate(parseInt(document.getElementById('batch_amount').value)); + this.print_batch_with_barcode(cards); + } + + unique_batch_with_barcode(){ + let batcher = new this.Batcher(this.bingo); + let cards = batcher.generate_unique_winners(parseInt(document.getElementById('batch_amount').value)); + this.print_batch_with_barcode(cards); + } + print_batch(cards){ let batch_cards = []; for (let card of cards){ @@ -1394,6 +1416,49 @@

Batch Card Printer

a.print(); } + print_batch_with_barcode(cards){ + let batch_cards = []; + for (let card of cards){ + let {numbers, card_id} = card; + this.load([card_id, this.bingo.cards.arr_2d_to_3d(numbers.slice(0,25), 5)]); + let printer_card = document.getElementById("printer_card").cloneNode(true); + printer_card.style.display = "flex"; + printer_card.style['place-content'] = ""; + + let card_div = printer_card.children[1].children[0]; + card_div.style.width = '80vw'; + card_div.style.height = '98vh'; + card_div.style.display='table'; + card_div.style['table-layout'] = 'fixed'; + + let div = document.createElement('div'); + div.style.position = 'relative'; + div.style.width = '0'; + div.style.height = '0'; + div.style.top = '98vh'; + div.style.rotate = '-90deg'; + + let bcode = new Barcode(); + let svg = bcode.dashArrayToSVG(bcode.textToCode128B(card_id)); + svg.setAttribute('viewBox', '0 0 320 50'); + svg.style.width = '94vh'; + div.appendChild(svg); + + let card_id_element = printer_card.children[0]; + card_id_element.style['font-size'] = '12vh'; + card_id_element.style.width = '97vh'; + card_id_element.remove(); + div.appendChild(card_id_element); + + printer_card.appendChild(div); + batch_cards.push(printer_card.outerHTML); + } + var a = window.open('', '', 'height=500, width=500'); + a.document.write(''+batch_cards.join('
')+''); + a.document.close(); + a.print(); + } + print(){ var divContents = document.getElementById("printer_card").outerHTML; var a = window.open('', '', 'height=500, width=500'); @@ -1473,6 +1538,269 @@

Batch Card Printer

} + class Barcode{ + // start/stop: "100101101101" + code39 = {} + + code39_raw = [ + [" ", "100110101101"], + ["$", "100100100101"], + ["%", "101001001001"], + ["+", "100101001001"], + ["-", "100101011011"], + ["/", "100100101001"], + [".", "110010101101"], + ["0", "101001101101"], + ["1", "110100101011"], + ["2", "101100101011"], + ["3", "110110010101"], + ["4", "101001101011"], + ["5", "110100110101"], + ["6", "101100110101"], + ["7", "101001011011"], + ["8", "110100101101"], + ["9", "101100101101"], + ["A", "110101001011"], + ["B", "101101001011"], + ["C", "110110100101"], + ["D", "101011001011"], + ["E", "110101100101"], + ["F", "101101100101"], + ["G", "101010011011"], + ["H", "110101001101"], + ["I", "101101001101"], + ["J", "101011001101"], + ["K", "110101010011"], + ["L", "101101010011"], + ["M", "110110101001"], + ["N", "101011010011"], + ["O", "110101101001"], + ["P", "101101101001"], + ["Q", "101010110011"], + ["R", "110101011001"], + ["S", "101101011001"], + ["T", "101011011001"], + ["U", "110010101011"], + ["V", "100110101011"], + ["W", "110011010101"], + ["X", "100101101011"], + ["Y", "110010110101"], + ["Z", "100110110101"], + ] + + code128 = { + "a": {}, + "b": {}, + "c": {}, + // for reversing the pattern to it's value/index and back + "p_i": {}, + "i_p": {} + } + + code128_raw = [ + [[" ", " ", "00"], "11011001100"], + [["!", "!", "01"], "11001101100"], + [["\"", "\"", "02"], "11001100110"], + [["#", "#", "03"], "10010011000"], + [["$", "$", "04"], "10010001100"], + [["%", "%", "05"], "10001001100"], + [["&", "&", "06"], "10011001000"], + [["'", "'", "07"], "10011000100"], + [["(", "(", "08"], "10001100100"], + [[")", ")", "09"], "11001001000"], + [["*", "*", "10"], "11001000100"], + [["+", "+", "11"], "11000100100"], + [[", ", ", ", "12"], "10110011100"], + [["-", "-", "13"], "10011011100"], + [[".", ".", "14"], "10011001110"], + [["/", "/", "15"], "10111001100"], + [["0", "0", "16"], "10011101100"], + [["1", "1", "17"], "10011100110"], + [["2", "2", "18"], "11001110010"], + [["3", "3", "19"], "11001011100"], + [["4", "4", "20"], "11001001110"], + [["5", "5", "21"], "11011100100"], + [["6", "6", "22"], "11001110100"], + [["7", "7", "23"], "11101101110"], + [["8", "8", "24"], "11101001100"], + [["9", "9", "25"], "11100101100"], + [[":", ":", "26"], "11100100110"], + [[";", ";", "27"], "11101100100"], + [["<", "<", "28"], "11100110100"], + [["=", "=", "29"], "11100110010"], + [[">", ">", "30"], "11011011000"], + [["?", "?", "31"], "11011000110"], + [["@", "@", "32"], "11000110110"], + [["A", "A", "33"], "10100011000"], + [["B", "B", "34"], "10001011000"], + [["C", "C", "35"], "10001000110"], + [["D", "D", "36"], "10110001000"], + [["E", "E", "37"], "10001101000"], + [["F", "F", "38"], "10001100010"], + [["G", "G", "39"], "11010001000"], + [["H", "H", "40"], "11000101000"], + [["I", "I", "41"], "11000100010"], + [["J", "J", "42"], "10110111000"], + [["K", "K", "43"], "10110001110"], + [["L", "L", "44"], "10001101110"], + [["M", "M", "45"], "10111011000"], + [["N", "N", "46"], "10111000110"], + [["O", "O", "47"], "10001110110"], + [["P", "P", "48"], "11101110110"], + [["Q", "Q", "49"], "11010001110"], + [["R", "R", "50"], "11000101110"], + [["S", "S", "51"], "11011101000"], + [["T", "T", "52"], "11011100010"], + [["U", "U", "53"], "11011101110"], + [["V", "V", "54"], "11101011000"], + [["W", "W", "55"], "11101000110"], + [["X", "X", "56"], "11100010110"], + [["Y", "Y", "57"], "11101101000"], + [["Z", "Z", "58"], "11101100010"], + [["[", "[", "59"], "11100011010"], + [["\\", "\\", "60"], "11101111010"], + [["]", "]", "61"], "11001000010"], + [["^", "^", "62"], "11110001010"], + [["_", "_", "63"], "10100110000"], + [[String.fromCharCode(0), "", "64"], "10100001100"], + [[String.fromCharCode(1), "a", "65"], "10010110000"], + [[String.fromCharCode(2), "b", "66"], "10010000110"], + [[String.fromCharCode(3), "c", "67"], "10000101100"], + [[String.fromCharCode(4), "d", "68"], "10000100110"], + [[String.fromCharCode(5), "e", "69"], "10110010000"], + [[String.fromCharCode(6), "f", "70"], "10110000100"], + [[String.fromCharCode(7), "g", "71"], "10011010000"], + [[String.fromCharCode(8), "h", "72"], "10011000010"], + [[String.fromCharCode(9), "i", "73"], "10000110100"], + [[String.fromCharCode(10), "j", "74"], "10000110010"], + [[String.fromCharCode(11), "k", "75"], "11000010010"], + [[String.fromCharCode(12), "l", "76"], "11001010000"], + [[String.fromCharCode(13), "m", "77"], "11110111010"], + [[String.fromCharCode(14), "n", "78"], "11000010100"], + [[String.fromCharCode(15), "o", "79"], "10001111010"], + [[String.fromCharCode(16), "p", "80"], "10100111100"], + [[String.fromCharCode(17), "q", "81"], "10010111100"], + [[String.fromCharCode(18), "r", "82"], "10010011110"], + [[String.fromCharCode(19), "s", "83"], "10111100100"], + [[String.fromCharCode(20), "t", "84"], "10011110100"], + [[String.fromCharCode(21), "u", "85"], "10011110010"], + [[String.fromCharCode(22), "v", "86"], "11110100100"], + [[String.fromCharCode(23), "w", "87"], "11110010100"], + [[String.fromCharCode(24), "x", "88"], "11110010010"], + [[String.fromCharCode(25), "y", "89"], "11011011110"], + [[String.fromCharCode(26), "z", "90"], "11011110110"], + [[String.fromCharCode(27), "{", "91"], "11110110110"], + [[String.fromCharCode(28), "|", "92"], "10101111000"], + [[String.fromCharCode(29), "}", "93"], "10100011110"], + [[String.fromCharCode(30), "~", "94"], "10001011110"], + [[String.fromCharCode(31), "", "95"], "10111101000"], + + // Control pattern onwards + // not really intend for use in this webapp + // but included for completeness + + // FNC3 + [[96, 96, "96"], "10111100010"], + // FNC2 + [[97, 97, "97"], "11110101000"], + // SHIFT B, Shift A + [[98, 98, "98"], "11110100010"], + // Code C + [[99, 99, "99"], "10111011110"], + // Code B, FNC 4, Code B + [[100, 100, 100], "10111101110"], + // FNC 4, Code A, Code A + [[101, 101, 101], "11101011110"], + // FNC 1 + [[102, 102, 102], "11110101110"], + // Start Code A + [[103, 103, 103], "11010000100"], + // Start Code B + [[104, 104, 104], "11010010000"], + // Start Code C + [[105, 105, 105], "11010011100"], + // Stop + [[106, 106, 106], "11000111010"], + // Reverse Stop + [[107, 107, 107], "11010111000"], + // Stop pattern (7 bars/spaces) + [[108, 108, 108], "1100011101011"], + ] + + constructor(){ + // code39 characters are separated by an additional narrow space. + this.code39_raw.forEach((set)=>{ + let [code, pattern] = set; + if (!pattern.endsWith('1')){throw new Error('Code39 data pattern error')} + this.code39[code] = pattern + "0" + }) + + this.code128_raw.forEach((set, i)=>{ + let [[a,b,c], p] = set; + this.code128.a[a] = p; + this.code128.b[b] = p; + this.code128.c[c] = p; + this.code128.p_i[p] = i; + this.code128.i_p[i] = p; + }) + } + + patternToWidth(pattern){ + return pattern.match(/(.)\1*/g).map((x)=>x.length).join(' ') + } + + charToWidth(cha, codeset){ + if (codeset == undefined){ + cha = cha.toUpperCase(); + codeset = this.code39; + } + + if (cha in codeset === false){ + throw new Error(`character '${cha}' not in selected codeset!`); + } + return this.patternToWidth(codeset[cha]); + } + + textToWidth(text, codeset){ + return text.split('').map((x)=>this.charToWidth(x, codeset)).join(' '); + } + + textToCode128B(text){ + if (!/[\x20-\x7F\xC8-\xCF]/.test(text)){ + throw new Error(`text '${text}' is not encodable to code128b`); + } + + // start with 104, the start code of 128b + let check_total = text.split("").reduce((p, x, i)=>p+this.code128.p_i[this.code128.b[x]]*(i+1), 104); + + return [this.patternToWidth(this.code128.b[104]), + this.textToWidth(text, this.code128.b), + this.patternToWidth(this.code128.i_p[check_total%103]), + this.patternToWidth(this.code128.b[108])].join(' ') + } + + textToCode39(text){ + if (/[^0-9A-Z\-\.\ \$\/\+\%]/.test(text)){ + throw new Error(`text '${text}' is not encodable to code39`); + } + + let s = this.patternToWidth('1001011011010'); + return [s, this.textToWidth(text, this.code39), s].join(' ') + } + + dashArrayToSVG(dasharray){ + let len = dasharray.split(' ').reduce((p,x)=>p+parseInt(x),0); + + let svg = document.getElementById('svg_barcode_template').cloneNode(true); + svg.removeAttribute('id'); + let line = svg.children[0]; + line.setAttribute('x2', len); + line.setAttribute('transform',`matrix(${320/len} 0 0 100 0 0)`); + line.setAttribute('stroke-dasharray', dasharray); + return svg + } + } + class Loader{ constructor(bingo){ this.bingo = bingo;