| 1 |
(function () { |
| 2 |
'use strict'; |
| 3 |
|
| 4 |
function typoCssVarsForEl(typo, prefix, el) { |
| 5 |
if (!typo) return; |
| 6 |
if (typo.family) el.style.setProperty(prefix + 'font-family', "'" + typo.family + "', sans-serif"); |
| 7 |
if (typo.weight) el.style.setProperty(prefix + 'font-weight', typo.weight); |
| 8 |
if (typo.transform) el.style.setProperty(prefix + 'text-transform', typo.transform); |
| 9 |
if (typo.style) el.style.setProperty(prefix + 'font-style', typo.style); |
| 10 |
if (typo.decoration) el.style.setProperty(prefix + 'text-decoration', typo.decoration); |
| 11 |
var su = typo.sizeUnit || 'px'; |
| 12 |
if (typo.sizeDesktop !== '' && typo.sizeDesktop != null) el.style.setProperty(prefix + 'font-size-d', typo.sizeDesktop + su); |
| 13 |
if (typo.sizeTablet !== '' && typo.sizeTablet != null) el.style.setProperty(prefix + 'font-size-t', typo.sizeTablet + su); |
| 14 |
if (typo.sizeMobile !== '' && typo.sizeMobile != null) el.style.setProperty(prefix + 'font-size-m', typo.sizeMobile + su); |
| 15 |
var lhu = typo.lineHeightUnit || 'px'; |
| 16 |
if (typo.lineHeightDesktop !== '' && typo.lineHeightDesktop != null) el.style.setProperty(prefix + 'line-height-d', typo.lineHeightDesktop + lhu); |
| 17 |
if (typo.lineHeightTablet !== '' && typo.lineHeightTablet != null) el.style.setProperty(prefix + 'line-height-t', typo.lineHeightTablet + lhu); |
| 18 |
if (typo.lineHeightMobile !== '' && typo.lineHeightMobile != null) el.style.setProperty(prefix + 'line-height-m', typo.lineHeightMobile + lhu); |
| 19 |
var lsu = typo.letterSpacingUnit || 'px'; |
| 20 |
if (typo.letterSpacingDesktop !== '' && typo.letterSpacingDesktop != null) { el.style.setProperty(prefix + 'letter-spacing-d', typo.letterSpacingDesktop + lsu); el.style.setProperty(prefix + 'letter-spacing', typo.letterSpacingDesktop + lsu); } |
| 21 |
if (typo.letterSpacingTablet !== '' && typo.letterSpacingTablet != null) el.style.setProperty(prefix + 'letter-spacing-t', typo.letterSpacingTablet + lsu); |
| 22 |
if (typo.letterSpacingMobile !== '' && typo.letterSpacingMobile != null) el.style.setProperty(prefix + 'letter-spacing-m', typo.letterSpacingMobile + lsu); |
| 23 |
var wsu = typo.wordSpacingUnit || 'px'; |
| 24 |
if (typo.wordSpacingDesktop !== '' && typo.wordSpacingDesktop != null) { el.style.setProperty(prefix + 'word-spacing-d', typo.wordSpacingDesktop + wsu); el.style.setProperty(prefix + 'word-spacing', typo.wordSpacingDesktop + wsu); } |
| 25 |
if (typo.wordSpacingTablet !== '' && typo.wordSpacingTablet != null) el.style.setProperty(prefix + 'word-spacing-t', typo.wordSpacingTablet + wsu); |
| 26 |
if (typo.wordSpacingMobile !== '' && typo.wordSpacingMobile != null) el.style.setProperty(prefix + 'word-spacing-m', typo.wordSpacingMobile + wsu); |
| 27 |
} |
| 28 |
|
| 29 |
/* ===================================================================== |
| 30 |
Code128-B encoder (pure JS, no dependencies) |
| 31 |
Reference: ISO/IEC 15417 |
| 32 |
===================================================================== */ |
| 33 |
|
| 34 |
var CODE128_B = { |
| 35 |
START: [2,1,1,4,1,2], |
| 36 |
STOP: [2,3,3,1,1,1,2], |
| 37 |
// Value → [bar widths ×1] for each code 0–102 |
| 38 |
TABLE: [ |
| 39 |
[2,1,2,2,2,2],[2,2,2,1,2,2],[2,2,2,2,2,1],[1,2,1,2,2,3],[1,2,1,3,2,2], |
| 40 |
[1,3,1,2,2,2],[1,2,2,2,1,3],[1,2,2,3,1,2],[1,3,2,2,1,2],[2,2,1,2,1,3], |
| 41 |
[2,2,1,3,1,2],[2,3,1,2,1,2],[1,1,2,2,3,2],[1,2,2,1,3,2],[1,2,2,2,3,1], |
| 42 |
[1,1,3,2,2,2],[1,2,3,1,2,2],[1,2,3,2,2,1],[2,2,3,2,1,1],[2,2,1,1,3,2], |
| 43 |
[2,2,1,2,3,1],[2,1,3,2,1,2],[2,2,3,1,1,2],[3,1,2,1,3,1],[3,1,1,2,2,2], |
| 44 |
[3,2,1,1,2,2],[3,2,1,2,2,1],[3,1,2,2,1,2],[3,2,2,1,1,2],[3,2,2,2,1,1], |
| 45 |
[2,1,2,1,2,3],[2,1,2,3,2,1],[2,3,2,1,2,1],[1,1,1,3,2,3],[1,3,1,1,2,3], |
| 46 |
[1,3,1,3,2,1],[1,1,2,3,1,3],[1,3,2,1,1,3],[1,3,2,3,1,1],[2,1,1,3,1,3], |
| 47 |
[2,3,1,1,1,3],[2,3,1,3,1,1],[1,1,3,1,2,3],[1,1,3,3,2,1],[1,3,3,1,2,1], |
| 48 |
[1,1,2,1,3,3],[1,1,2,3,3,1],[1,3,2,1,3,1],[1,1,3,2,1,3],[1,1,3,3,1,2], // 49 |
| 49 |
[1,3,3,1,1,2],[1,3,1,2,3,1],[2,1,1,2,3,2],[1,1,2,2,1,3],[1,2,2,1,1,3], // ok to 54 |
| 50 |
[1,2,1,3,3,1],[1,1,3,2,3,1],[1,2,3,1,3,1],[1,2,3,1,1,3],[1,2,1,1,3,3], |
| 51 |
[1,1,1,2,3,3],[1,3,1,2,1,3],[1,3,2,1,1,3],[1,2,1,3,1,3],[1,3,1,3,1,2], // 64 |
| 52 |
[2,1,3,1,2,2],[2,1,3,2,2,1],[2,1,2,2,3,1],[2,3,2,1,1,2],[3,1,1,1,2,3], |
| 53 |
[3,1,1,3,2,1],[3,3,1,1,2,1],[3,1,2,1,1,3],[3,1,2,3,1,1],[3,3,2,1,1,1], |
| 54 |
[3,1,4,1,1,1],[2,2,1,4,1,1],[4,3,1,1,1,1],[1,1,1,2,2,4],[1,1,1,4,2,2], |
| 55 |
[1,2,1,1,2,4],[1,2,1,4,2,1],[1,4,1,1,2,2],[1,4,1,2,2,1],[1,1,2,2,1,4], |
| 56 |
[1,1,2,4,1,2],[1,2,2,1,1,4],[1,2,2,4,1,1],[1,4,2,1,1,2],[1,4,2,2,1,1], |
| 57 |
[2,4,1,2,1,1],[2,2,1,1,1,4],[4,1,3,1,1,1],[2,4,1,1,1,2],[1,3,4,1,1,1], |
| 58 |
[1,1,1,2,4,2],[1,2,1,1,4,2],[1,2,1,2,4,1],[1,1,4,2,1,2],[1,2,4,1,1,2], |
| 59 |
[1,2,4,2,1,1],[4,1,1,2,1,2],[4,2,1,1,1,2],[4,2,1,2,1,1],[2,1,2,1,4,1], |
| 60 |
[2,1,4,1,2,1],[4,1,2,1,2,1],[1,1,1,1,4,3],[1,1,1,3,4,1],[1,3,1,1,4,1], |
| 61 |
[1,1,4,1,1,3],[1,1,4,3,1,1],[4,1,1,1,1,3],[4,1,1,3,1,1],[1,1,3,1,4,1], |
| 62 |
[1,1,4,1,3,1],[3,1,1,1,4,1],[4,1,1,1,3,1],[2,1,1,4,1,2],[6,1,1,1,1,2] // 106 |
| 63 |
] |
| 64 |
}; |
| 65 |
|
| 66 |
// Encode text as Code128-B (supports ASCII 32-126) |
| 67 |
function encodeCode128(text) { |
| 68 |
var bars = []; |
| 69 |
|
| 70 |
// Append pattern helper |
| 71 |
function addCode(idx) { |
| 72 |
var row = CODE128_B.TABLE[idx]; |
| 73 |
if (!row) return; |
| 74 |
for (var i = 0; i < row.length; i++) { bars.push({ w: row[i], bar: i % 2 === 0 }); } |
| 75 |
} |
| 76 |
|
| 77 |
// START B |
| 78 |
for (var s = 0; s < CODE128_B.START.length; s++) { |
| 79 |
bars.push({ w: CODE128_B.START[s], bar: s % 2 === 0 }); |
| 80 |
} |
| 81 |
|
| 82 |
var checksum = 104; // Start-B value |
| 83 |
for (var i = 0; i < text.length; i++) { |
| 84 |
var code = text.charCodeAt(i) - 32; |
| 85 |
if (code < 0 || code > 94) return null; // unsupported char |
| 86 |
checksum += (i + 1) * code; |
| 87 |
addCode(code); |
| 88 |
} |
| 89 |
|
| 90 |
// Checksum |
| 91 |
addCode(checksum % 103); |
| 92 |
|
| 93 |
// STOP |
| 94 |
for (var t = 0; t < CODE128_B.STOP.length; t++) { |
| 95 |
bars.push({ w: CODE128_B.STOP[t], bar: t % 2 === 0 }); |
| 96 |
} |
| 97 |
|
| 98 |
return bars; |
| 99 |
} |
| 100 |
|
| 101 |
function drawBarcode(canvas, text, opts) { |
| 102 |
var barW = opts.barWidth || 2; |
| 103 |
var barH = opts.barHeight || 80; |
| 104 |
var barColor = opts.barColor || '#000'; |
| 105 |
var bgColor = opts.bgColor || '#fff'; |
| 106 |
var showLabel= opts.showLabel !== false; |
| 107 |
|
| 108 |
var bars = encodeCode128(text); |
| 109 |
if (!bars) return false; |
| 110 |
|
| 111 |
var totalBars = 0; |
| 112 |
for (var i = 0; i < bars.length; i++) totalBars += bars[i].w; |
| 113 |
|
| 114 |
var padX = 10, padY = 8; |
| 115 |
var labelH = showLabel ? 20 : 0; |
| 116 |
var w = totalBars * barW + padX * 2; |
| 117 |
var h = barH + labelH + padY * 2; |
| 118 |
|
| 119 |
canvas.width = w; |
| 120 |
canvas.height = h; |
| 121 |
|
| 122 |
var ctx = canvas.getContext('2d'); |
| 123 |
ctx.fillStyle = bgColor; |
| 124 |
ctx.fillRect(0, 0, w, h); |
| 125 |
|
| 126 |
var x = padX; |
| 127 |
for (var j = 0; j < bars.length; j++) { |
| 128 |
if (bars[j].bar) { |
| 129 |
ctx.fillStyle = barColor; |
| 130 |
ctx.fillRect(x, padY, bars[j].w * barW, barH); |
| 131 |
} |
| 132 |
x += bars[j].w * barW; |
| 133 |
} |
| 134 |
|
| 135 |
if (showLabel) { |
| 136 |
ctx.fillStyle = barColor; |
| 137 |
ctx.font = '11px monospace'; |
| 138 |
ctx.textAlign = 'center'; |
| 139 |
ctx.fillText(text, w / 2, padY + barH + 14); |
| 140 |
} |
| 141 |
|
| 142 |
return true; |
| 143 |
} |
| 144 |
|
| 145 |
function showToast(msg) { |
| 146 |
var t = document.createElement('div'); |
| 147 |
t.className = 'bkbg-brc-toast bkbg-brc-toast-show'; |
| 148 |
t.textContent = msg; |
| 149 |
document.body.appendChild(t); |
| 150 |
setTimeout(function () { |
| 151 |
t.classList.remove('bkbg-brc-toast-show'); |
| 152 |
setTimeout(function () { t.parentNode && t.parentNode.removeChild(t); }, 400); |
| 153 |
}, 1800); |
| 154 |
} |
| 155 |
|
| 156 |
function initBlock(root) { |
| 157 |
var opts = {}; |
| 158 |
try { opts = JSON.parse(root.getAttribute('data-opts') || '{}'); } catch (e) {} |
| 159 |
|
| 160 |
var accentColor = opts.accentColor || '#1f2937'; |
| 161 |
var barColor = opts.barColor || '#000000'; |
| 162 |
var bgColor = opts.bgColor || '#ffffff'; |
| 163 |
var sectionBg = opts.sectionBg || '#f9fafb'; |
| 164 |
var titleColor = opts.titleColor || '#111827'; |
| 165 |
var buttonBg = opts.buttonBg || '#1f2937'; |
| 166 |
var barHeight = opts.barHeight || 80; |
| 167 |
var barWidth = opts.barWidth || 2; |
| 168 |
var showLabel = opts.showLabel !== false; |
| 169 |
var showDownload= opts.showDownload !== false; |
| 170 |
var showCopy = opts.showCopy !== false; |
| 171 |
var defaultText = opts.defaultText || 'BLOCKENBERG-2024'; |
| 172 |
var fontSize = opts.fontSize || 26; |
| 173 |
var subtitleSize= opts.subtitleSize|| 14; |
| 174 |
|
| 175 |
root.style.background = sectionBg; |
| 176 |
root.style.borderRadius = '16px'; |
| 177 |
root.style.padding = '28px 20px'; |
| 178 |
root.style.textAlign = 'center'; |
| 179 |
|
| 180 |
// Title / subtitle already rendered by save() as .bkbg-brc-title / .bkbg-brc-subtitle |
| 181 |
// Apply styling to them |
| 182 |
var titleEl = root.querySelector('.bkbg-brc-title'); |
| 183 |
var subtitleEl = root.querySelector('.bkbg-brc-subtitle'); |
| 184 |
if (titleEl) { titleEl.style.color = titleColor; titleEl.style.margin = '0 0 4px'; } |
| 185 |
if (subtitleEl) { subtitleEl.style.color = titleColor + 'bb'; subtitleEl.style.margin = '0 0 18px'; } |
| 186 |
typoCssVarsForEl(opts.titleTypo, '--bkbg-brc-title-', root); |
| 187 |
typoCssVarsForEl(opts.subtitleTypo, '--bkbg-brc-sub-', root); |
| 188 |
|
| 189 |
// Build inner UI |
| 190 |
var inner = document.createElement('div'); |
| 191 |
|
| 192 |
// Input row |
| 193 |
var inputRow = document.createElement('div'); |
| 194 |
inputRow.className = 'bkbg-brc-input-row'; |
| 195 |
|
| 196 |
var input = document.createElement('input'); |
| 197 |
input.type = 'text'; |
| 198 |
input.className = 'bkbg-brc-input'; |
| 199 |
input.value = defaultText; |
| 200 |
input.placeholder = 'Enter text or number…'; |
| 201 |
input.setAttribute('maxlength', '80'); |
| 202 |
input.style.setProperty('--bkbg-brc-accent', accentColor); |
| 203 |
|
| 204 |
var genBtn = document.createElement('button'); |
| 205 |
genBtn.className = 'bkbg-brc-generate-btn'; |
| 206 |
genBtn.textContent = 'Generate'; |
| 207 |
genBtn.style.background = buttonBg; |
| 208 |
|
| 209 |
inputRow.appendChild(input); |
| 210 |
inputRow.appendChild(genBtn); |
| 211 |
inner.appendChild(inputRow); |
| 212 |
|
| 213 |
// Error msg |
| 214 |
var errEl = document.createElement('div'); |
| 215 |
errEl.className = 'bkbg-brc-error'; |
| 216 |
inner.appendChild(errEl); |
| 217 |
|
| 218 |
// Canvas |
| 219 |
var canvasWrap = document.createElement('div'); |
| 220 |
canvasWrap.className = 'bkbg-brc-canvas-wrap'; |
| 221 |
var canvas = document.createElement('canvas'); |
| 222 |
canvas.className = 'bkbg-brc-canvas'; |
| 223 |
canvasWrap.appendChild(canvas); |
| 224 |
inner.appendChild(canvasWrap); |
| 225 |
|
| 226 |
// Actions |
| 227 |
var actions = document.createElement('div'); |
| 228 |
actions.className = 'bkbg-brc-actions'; |
| 229 |
|
| 230 |
function generate() { |
| 231 |
var text = input.value.trim(); |
| 232 |
if (!text) { errEl.textContent = 'Please enter text or a number.'; return; } |
| 233 |
|
| 234 |
var ok = drawBarcode(canvas, text, { |
| 235 |
barWidth: barWidth, barHeight: barHeight, |
| 236 |
barColor: barColor, bgColor: bgColor, showLabel: showLabel |
| 237 |
}); |
| 238 |
if (!ok) { |
| 239 |
errEl.textContent = 'Error: Text contains unsupported characters. Use ASCII 32–126.'; |
| 240 |
canvas.width = 0; |
| 241 |
canvas.height = 0; |
| 242 |
} else { |
| 243 |
errEl.textContent = ''; |
| 244 |
} |
| 245 |
} |
| 246 |
|
| 247 |
genBtn.addEventListener('click', generate); |
| 248 |
input.addEventListener('keydown', function (e) { if (e.key === 'Enter') generate(); }); |
| 249 |
|
| 250 |
if (showDownload) { |
| 251 |
var dlBtn = document.createElement('button'); |
| 252 |
dlBtn.className = 'bkbg-brc-action-btn'; |
| 253 |
dlBtn.style.borderColor = buttonBg; |
| 254 |
dlBtn.style.color = buttonBg; |
| 255 |
dlBtn.textContent = '⬇ Download PNG'; |
| 256 |
dlBtn.addEventListener('click', function () { |
| 257 |
if (!canvas.width) { generate(); } |
| 258 |
var link = document.createElement('a'); |
| 259 |
link.download = 'barcode-' + (input.value.trim() || 'code') + '.png'; |
| 260 |
link.href = canvas.toDataURL('image/png'); |
| 261 |
link.click(); |
| 262 |
}); |
| 263 |
actions.appendChild(dlBtn); |
| 264 |
} |
| 265 |
|
| 266 |
if (showCopy) { |
| 267 |
var cpBtn = document.createElement('button'); |
| 268 |
cpBtn.className = 'bkbg-brc-action-btn'; |
| 269 |
cpBtn.style.borderColor = buttonBg; |
| 270 |
cpBtn.style.color = buttonBg; |
| 271 |
cpBtn.textContent = '📋 Copy Image'; |
| 272 |
cpBtn.addEventListener('click', function () { |
| 273 |
if (!canvas.width) { generate(); } |
| 274 |
canvas.toBlob(function (blob) { |
| 275 |
if (!blob) return; |
| 276 |
try { |
| 277 |
var item = new ClipboardItem({ 'image/png': blob }); |
| 278 |
navigator.clipboard.write([item]).then(function () { |
| 279 |
showToast('Barcode copied to clipboard!'); |
| 280 |
}).catch(function () { |
| 281 |
showToast('Copy not supported in this browser.'); |
| 282 |
}); |
| 283 |
} catch (err) { |
| 284 |
showToast('Copy not supported in this browser.'); |
| 285 |
} |
| 286 |
}); |
| 287 |
}); |
| 288 |
actions.appendChild(cpBtn); |
| 289 |
} |
| 290 |
|
| 291 |
inner.appendChild(actions); |
| 292 |
root.appendChild(inner); |
| 293 |
|
| 294 |
// Auto-generate initial barcode |
| 295 |
generate(); |
| 296 |
} |
| 297 |
|
| 298 |
function init() { |
| 299 |
document.querySelectorAll('.bkbg-brc-app').forEach(function (root) { |
| 300 |
initBlock(root); |
| 301 |
}); |
| 302 |
} |
| 303 |
|
| 304 |
if (document.readyState === 'loading') { |
| 305 |
document.addEventListener('DOMContentLoaded', init); |
| 306 |
} else { |
| 307 |
init(); |
| 308 |
} |
| 309 |
})(); |
| 310 |
|