Create QR Code for lit
This commit is contained in:
parent
0acf45dae6
commit
96d3c4c82a
599
qortal-ui-plugins/plugins/core/components/QRCode.js
Normal file
599
qortal-ui-plugins/plugins/core/components/QRCode.js
Normal file
@ -0,0 +1,599 @@
|
||||
var VERSIONS = [
|
||||
null,
|
||||
[[10, 7,17,13], [ 1, 1, 1, 1], []],
|
||||
[[16,10,28,22], [ 1, 1, 1, 1], [4,16]],
|
||||
[[26,15,22,18], [ 1, 1, 2, 2], [4,20]],
|
||||
[[18,20,16,26], [ 2, 1, 4, 2], [4,24]],
|
||||
[[24,26,22,18], [ 2, 1, 4, 4], [4,28]],
|
||||
[[16,18,28,24], [ 4, 2, 4, 4], [4,32]],
|
||||
[[18,20,26,18], [ 4, 2, 5, 6], [4,20,36]],
|
||||
[[22,24,26,22], [ 4, 2, 6, 6], [4,22,40]],
|
||||
[[22,30,24,20], [ 5, 2, 8, 8], [4,24,44]],
|
||||
[[26,18,28,24], [ 5, 4, 8, 8], [4,26,48]],
|
||||
[[30,20,24,28], [ 5, 4,11, 8], [4,28,52]],
|
||||
[[22,24,28,26], [ 8, 4,11,10], [4,30,56]],
|
||||
[[22,26,22,24], [ 9, 4,16,12], [4,32,60]],
|
||||
[[24,30,24,20], [ 9, 4,16,16], [4,24,44,64]],
|
||||
[[24,22,24,30], [10, 6,18,12], [4,24,46,68]],
|
||||
[[28,24,30,24], [10, 6,16,17], [4,24,48,72]],
|
||||
[[28,28,28,28], [11, 6,19,16], [4,28,52,76]],
|
||||
[[26,30,28,28], [13, 6,21,18], [4,28,54,80]],
|
||||
[[26,28,26,26], [14, 7,25,21], [4,28,56,84]],
|
||||
[[26,28,28,30], [16, 8,25,20], [4,32,60,88]],
|
||||
[[26,28,30,28], [17, 8,25,23], [4,26,48,70,92]],
|
||||
[[28,28,24,30], [17, 9,34,23], [4,24,48,72,96]],
|
||||
[[28,30,30,30], [18, 9,30,25], [4,28,52,76,100]],
|
||||
[[28,30,30,30], [20,10,32,27], [4,26,52,78,104]],
|
||||
[[28,26,30,30], [21,12,35,29], [4,30,56,82,108]],
|
||||
[[28,28,30,28], [23,12,37,34], [4,28,56,84,112]],
|
||||
[[28,30,30,30], [25,12,40,34], [4,32,60,88,116]],
|
||||
[[28,30,30,30], [26,13,42,35], [4,24,48,72,96,120]],
|
||||
[[28,30,30,30], [28,14,45,38], [4,28,52,76,100,124]],
|
||||
[[28,30,30,30], [29,15,48,40], [4,24,50,76,102,128]],
|
||||
[[28,30,30,30], [31,16,51,43], [4,28,54,80,106,132]],
|
||||
[[28,30,30,30], [33,17,54,45], [4,32,58,84,110,136]],
|
||||
[[28,30,30,30], [35,18,57,48], [4,28,56,84,112,140]],
|
||||
[[28,30,30,30], [37,19,60,51], [4,32,60,88,116,144]],
|
||||
[[28,30,30,30], [38,19,63,53], [4,28,52,76,100,124,148]],
|
||||
[[28,30,30,30], [40,20,66,56], [4,22,48,74,100,126,152]],
|
||||
[[28,30,30,30], [43,21,70,59], [4,26,52,78,104,130,156]],
|
||||
[[28,30,30,30], [45,22,74,62], [4,30,56,82,108,134,160]],
|
||||
[[28,30,30,30], [47,24,77,65], [4,24,52,80,108,136,164]],
|
||||
[[28,30,30,30], [49,25,81,68], [4,28,56,84,112,140,168]]];
|
||||
|
||||
var MODE_TERMINATOR = 0;
|
||||
var MODE_NUMERIC = 1, MODE_ALPHANUMERIC = 2, MODE_OCTET = 4, MODE_KANJI = 8;
|
||||
|
||||
var NUMERIC_REGEXP = /^\d*$/;
|
||||
var ALPHANUMERIC_REGEXP = /^[A-Za-z0-9 $%*+\-./:_]*$/;
|
||||
var ALPHANUMERIC_OUT_REGEXP = /^[A-Z0-9 $%*+\-./:_]*$/;
|
||||
|
||||
var ECCLEVEL_L = 1, ECCLEVEL_M = 0, ECCLEVEL_Q = 3, ECCLEVEL_H = 2;
|
||||
|
||||
var GF256_MAP = [], GF256_INVMAP = [-1];
|
||||
for (var i = 0, v = 1; i < 255; ++i) {
|
||||
GF256_MAP.push(v);
|
||||
GF256_INVMAP[v] = i;
|
||||
v = (v * 2) ^ (v >= 128 ? 0x11d : 0);
|
||||
}
|
||||
|
||||
var GF256_GENPOLY = [[]];
|
||||
for (var i = 0; i < 30; ++i) {
|
||||
var prevpoly = GF256_GENPOLY[i], poly = [];
|
||||
for (var j = 0; j <= i; ++j) {
|
||||
var a = (j < i ? GF256_MAP[prevpoly[j]] : 0);
|
||||
var b = GF256_MAP[(i + (prevpoly[j-1] || 0)) % 255];
|
||||
poly.push(GF256_INVMAP[a ^ b]);
|
||||
}
|
||||
GF256_GENPOLY.push(poly);
|
||||
}
|
||||
|
||||
var ALPHANUMERIC_MAP = {};
|
||||
for (var i = 0; i < 45; ++i) {
|
||||
ALPHANUMERIC_MAP['0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:'.charAt(i)] = i;
|
||||
}
|
||||
|
||||
var MASKFUNCS = [
|
||||
function(i,j) { return (i+j) % 2 == 0; },
|
||||
function(i,j) { return i % 2 == 0; },
|
||||
function(i,j) { return j % 3 == 0; },
|
||||
function(i,j) { return (i+j) % 3 == 0; },
|
||||
function(i,j) { return (((i/2)|0) + ((j/3)|0)) % 2 == 0; },
|
||||
function(i,j) { return (i*j) % 2 + (i*j) % 3 == 0; },
|
||||
function(i,j) { return ((i*j) % 2 + (i*j) % 3) % 2 == 0; },
|
||||
function(i,j) { return ((i+j) % 2 + (i*j) % 3) % 2 == 0; }];
|
||||
|
||||
var needsverinfo = function(ver) { return ver > 6; };
|
||||
|
||||
var getsizebyver = function(ver) { return 4 * ver + 17; };
|
||||
|
||||
var nfullbits = function(ver) {
|
||||
var v = VERSIONS[ver];
|
||||
var nbits = 16*ver*ver + 128*ver + 64;
|
||||
if (needsverinfo(ver)) nbits -= 36;
|
||||
if (v[2].length) {
|
||||
nbits -= 25 * v[2].length * v[2].length - 10 * v[2].length - 55;
|
||||
}
|
||||
return nbits;
|
||||
};
|
||||
|
||||
var ndatabits = function(ver, ecclevel) {
|
||||
var nbits = nfullbits(ver) & ~7;
|
||||
var v = VERSIONS[ver];
|
||||
nbits -= 8 * v[0][ecclevel] * v[1][ecclevel];
|
||||
return nbits;
|
||||
}
|
||||
|
||||
var ndatalenbits = function(ver, mode) {
|
||||
switch (mode) {
|
||||
case MODE_NUMERIC: return (ver < 10 ? 10 : ver < 27 ? 12 : 14);
|
||||
case MODE_ALPHANUMERIC: return (ver < 10 ? 9 : ver < 27 ? 11 : 13);
|
||||
case MODE_OCTET: return (ver < 10 ? 8 : 16);
|
||||
case MODE_KANJI: return (ver < 10 ? 8 : ver < 27 ? 10 : 12);
|
||||
}
|
||||
};
|
||||
|
||||
var getmaxdatalen = function(ver, mode, ecclevel) {
|
||||
var nbits = ndatabits(ver, ecclevel) - 4 - ndatalenbits(ver, mode);
|
||||
switch (mode) {
|
||||
case MODE_NUMERIC:
|
||||
return ((nbits/10) | 0) * 3 + (nbits%10 < 4 ? 0 : nbits%10 < 7 ? 1 : 2);
|
||||
case MODE_ALPHANUMERIC:
|
||||
return ((nbits/11) | 0) * 2 + (nbits%11 < 6 ? 0 : 1);
|
||||
case MODE_OCTET:
|
||||
return (nbits/8) | 0;
|
||||
case MODE_KANJI:
|
||||
return (nbits/13) | 0;
|
||||
}
|
||||
};
|
||||
|
||||
var validatedata = function(mode, data) {
|
||||
switch (mode) {
|
||||
case MODE_NUMERIC:
|
||||
if (!data.match(NUMERIC_REGEXP)) return null;
|
||||
return data;
|
||||
|
||||
case MODE_ALPHANUMERIC:
|
||||
if (!data.match(ALPHANUMERIC_REGEXP)) return null;
|
||||
return data.toUpperCase();
|
||||
|
||||
case MODE_OCTET:
|
||||
if (typeof data === 'string') {
|
||||
var newdata = [];
|
||||
for (var i = 0; i < data.length; ++i) {
|
||||
var ch = data.charCodeAt(i);
|
||||
if (ch < 0x80) {
|
||||
newdata.push(ch);
|
||||
} else if (ch < 0x800) {
|
||||
newdata.push(0xc0 | (ch >> 6),
|
||||
0x80 | (ch & 0x3f));
|
||||
} else if (ch < 0x10000) {
|
||||
newdata.push(0xe0 | (ch >> 12),
|
||||
0x80 | ((ch >> 6) & 0x3f),
|
||||
0x80 | (ch & 0x3f));
|
||||
} else {
|
||||
newdata.push(0xf0 | (ch >> 18),
|
||||
0x80 | ((ch >> 12) & 0x3f),
|
||||
0x80 | ((ch >> 6) & 0x3f),
|
||||
0x80 | (ch & 0x3f));
|
||||
}
|
||||
}
|
||||
return newdata;
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var encode = function(ver, mode, data, maxbuflen) {
|
||||
var buf = [];
|
||||
var bits = 0, remaining = 8;
|
||||
var datalen = data.length;
|
||||
|
||||
var pack = function(x, n) {
|
||||
if (n >= remaining) {
|
||||
buf.push(bits | (x >> (n -= remaining)));
|
||||
while (n >= 8) buf.push((x >> (n -= 8)) & 255);
|
||||
bits = 0;
|
||||
remaining = 8;
|
||||
}
|
||||
if (n > 0) bits |= (x & ((1 << n) - 1)) << (remaining -= n);
|
||||
};
|
||||
|
||||
var nlenbits = ndatalenbits(ver, mode);
|
||||
pack(mode, 4);
|
||||
pack(datalen, nlenbits);
|
||||
|
||||
switch (mode) {
|
||||
case MODE_NUMERIC:
|
||||
for (var i = 2; i < datalen; i += 3) {
|
||||
pack(parseInt(data.substring(i-2,i+1), 10), 10);
|
||||
}
|
||||
pack(parseInt(data.substring(i-2), 10), [0,4,7][datalen%3]);
|
||||
break;
|
||||
|
||||
case MODE_ALPHANUMERIC:
|
||||
for (var i = 1; i < datalen; i += 2) {
|
||||
pack(ALPHANUMERIC_MAP[data.charAt(i-1)] * 45 +
|
||||
ALPHANUMERIC_MAP[data.charAt(i)], 11);
|
||||
}
|
||||
if (datalen % 2 == 1) {
|
||||
pack(ALPHANUMERIC_MAP[data.charAt(i-1)], 6);
|
||||
}
|
||||
break;
|
||||
|
||||
case MODE_OCTET:
|
||||
for (var i = 0; i < datalen; ++i) {
|
||||
pack(data[i], 8);
|
||||
}
|
||||
break;
|
||||
};
|
||||
|
||||
pack(MODE_TERMINATOR, 4);
|
||||
if (remaining < 8) buf.push(bits);
|
||||
|
||||
while (buf.length + 1 < maxbuflen) buf.push(0xec, 0x11);
|
||||
if (buf.length < maxbuflen) buf.push(0xec);
|
||||
return buf;
|
||||
};
|
||||
|
||||
var calculateecc = function(poly, genpoly) {
|
||||
var modulus = poly.slice(0);
|
||||
var polylen = poly.length, genpolylen = genpoly.length;
|
||||
for (var i = 0; i < genpolylen; ++i) modulus.push(0);
|
||||
for (var i = 0; i < polylen; ) {
|
||||
var quotient = GF256_INVMAP[modulus[i++]];
|
||||
if (quotient >= 0) {
|
||||
for (var j = 0; j < genpolylen; ++j) {
|
||||
modulus[i+j] ^= GF256_MAP[(quotient + genpoly[j]) % 255];
|
||||
}
|
||||
}
|
||||
}
|
||||
return modulus.slice(polylen);
|
||||
};
|
||||
|
||||
var augumenteccs = function(poly, nblocks, genpoly) {
|
||||
var subsizes = [];
|
||||
var subsize = (poly.length / nblocks) | 0, subsize0 = 0;
|
||||
var pivot = nblocks - poly.length % nblocks;
|
||||
for (var i = 0; i < pivot; ++i) {
|
||||
subsizes.push(subsize0);
|
||||
subsize0 += subsize;
|
||||
}
|
||||
for (var i = pivot; i < nblocks; ++i) {
|
||||
subsizes.push(subsize0);
|
||||
subsize0 += subsize+1;
|
||||
}
|
||||
subsizes.push(subsize0);
|
||||
|
||||
var eccs = [];
|
||||
for (var i = 0; i < nblocks; ++i) {
|
||||
eccs.push(calculateecc(poly.slice(subsizes[i], subsizes[i+1]), genpoly));
|
||||
}
|
||||
|
||||
var result = [];
|
||||
var nitemsperblock = (poly.length / nblocks) | 0;
|
||||
for (var i = 0; i < nitemsperblock; ++i) {
|
||||
for (var j = 0; j < nblocks; ++j) {
|
||||
result.push(poly[subsizes[j] + i]);
|
||||
}
|
||||
}
|
||||
for (var j = pivot; j < nblocks; ++j) {
|
||||
result.push(poly[subsizes[j+1] - 1]);
|
||||
}
|
||||
for (var i = 0; i < genpoly.length; ++i) {
|
||||
for (var j = 0; j < nblocks; ++j) {
|
||||
result.push(eccs[j][i]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
var augumentbch = function(poly, p, genpoly, q) {
|
||||
var modulus = poly << q;
|
||||
for (var i = p - 1; i >= 0; --i) {
|
||||
if ((modulus >> (q+i)) & 1) modulus ^= genpoly << i;
|
||||
}
|
||||
return (poly << q) | modulus;
|
||||
};
|
||||
|
||||
var makebasematrix = function(ver) {
|
||||
var v = VERSIONS[ver], n = getsizebyver(ver);
|
||||
var matrix = [], reserved = [];
|
||||
for (var i = 0; i < n; ++i) {
|
||||
matrix.push([]);
|
||||
reserved.push([]);
|
||||
}
|
||||
|
||||
var blit = function(y, x, h, w, bits) {
|
||||
for (var i = 0; i < h; ++i) {
|
||||
for (var j = 0; j < w; ++j) {
|
||||
matrix[y+i][x+j] = (bits[i] >> j) & 1;
|
||||
reserved[y+i][x+j] = 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
blit(0, 0, 9, 9, [0x7f, 0x41, 0x5d, 0x5d, 0x5d, 0x41, 0x17f, 0x00, 0x40]);
|
||||
blit(n-8, 0, 8, 9, [0x100, 0x7f, 0x41, 0x5d, 0x5d, 0x5d, 0x41, 0x7f]);
|
||||
blit(0, n-8, 9, 8, [0xfe, 0x82, 0xba, 0xba, 0xba, 0x82, 0xfe, 0x00, 0x00]);
|
||||
|
||||
for (var i = 9; i < n-8; ++i) {
|
||||
matrix[6][i] = matrix[i][6] = ~i & 1;
|
||||
reserved[6][i] = reserved[i][6] = 1;
|
||||
}
|
||||
|
||||
var aligns = v[2], m = aligns.length;
|
||||
for (var i = 0; i < m; ++i) {
|
||||
var minj = (i==0 || i==m-1 ? 1 : 0), maxj = (i==0 ? m-1 : m);
|
||||
for (var j = minj; j < maxj; ++j) {
|
||||
blit(aligns[i], aligns[j], 5, 5, [0x1f, 0x11, 0x15, 0x11, 0x1f]);
|
||||
}
|
||||
}
|
||||
|
||||
if (needsverinfo(ver)) {
|
||||
var code = augumentbch(ver, 6, 0x1f25, 12);
|
||||
var k = 0;
|
||||
for (var i = 0; i < 6; ++i) {
|
||||
for (var j = 0; j < 3; ++j) {
|
||||
matrix[i][(n-11)+j] = matrix[(n-11)+j][i] = (code >> k++) & 1;
|
||||
reserved[i][(n-11)+j] = reserved[(n-11)+j][i] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {matrix: matrix, reserved: reserved};
|
||||
};
|
||||
|
||||
var putdata = function(matrix, reserved, buf) {
|
||||
var n = matrix.length;
|
||||
var k = 0, dir = -1;
|
||||
for (var i = n-1; i >= 0; i -= 2) {
|
||||
if (i == 6) --i;
|
||||
var jj = (dir < 0 ? n-1 : 0);
|
||||
for (var j = 0; j < n; ++j) {
|
||||
for (var ii = i; ii > i-2; --ii) {
|
||||
if (!reserved[jj][ii]) {
|
||||
matrix[jj][ii] = (buf[k >> 3] >> (~k&7)) & 1;
|
||||
++k;
|
||||
}
|
||||
}
|
||||
jj += dir;
|
||||
}
|
||||
dir = -dir;
|
||||
}
|
||||
return matrix;
|
||||
};
|
||||
|
||||
var maskdata = function(matrix, reserved, mask) {
|
||||
var maskf = MASKFUNCS[mask];
|
||||
var n = matrix.length;
|
||||
for (var i = 0; i < n; ++i) {
|
||||
for (var j = 0; j < n; ++j) {
|
||||
if (!reserved[i][j]) matrix[i][j] ^= maskf(i,j);
|
||||
}
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
var putformatinfo = function(matrix, reserved, ecclevel, mask) {
|
||||
var n = matrix.length;
|
||||
var code = augumentbch((ecclevel << 3) | mask, 5, 0x537, 10) ^ 0x5412;
|
||||
for (var i = 0; i < 15; ++i) {
|
||||
var r = [0,1,2,3,4,5,7,8,n-7,n-6,n-5,n-4,n-3,n-2,n-1][i];
|
||||
var c = [n-1,n-2,n-3,n-4,n-5,n-6,n-7,n-8,7,5,4,3,2,1,0][i];
|
||||
matrix[r][8] = matrix[8][c] = (code >> i) & 1;
|
||||
}
|
||||
return matrix;
|
||||
};
|
||||
|
||||
var evaluatematrix = function(matrix) {
|
||||
var PENALTY_CONSECUTIVE = 3;
|
||||
var PENALTY_TWOBYTWO = 3;
|
||||
var PENALTY_FINDERLIKE = 40;
|
||||
var PENALTY_DENSITY = 10;
|
||||
|
||||
var evaluategroup = function(groups) {
|
||||
var score = 0;
|
||||
for (var i = 0; i < groups.length; ++i) {
|
||||
if (groups[i] >= 5) score += PENALTY_CONSECUTIVE + (groups[i]-5);
|
||||
}
|
||||
for (var i = 5; i < groups.length; i += 2) {
|
||||
var p = groups[i];
|
||||
if (groups[i-1] == p && groups[i-2] == 3*p && groups[i-3] == p &&
|
||||
groups[i-4] == p && (groups[i-5] >= 4*p || groups[i+1] >= 4*p)) {
|
||||
score += PENALTY_FINDERLIKE;
|
||||
}
|
||||
}
|
||||
return score;
|
||||
};
|
||||
|
||||
var n = matrix.length;
|
||||
var score = 0, nblacks = 0;
|
||||
for (var i = 0; i < n; ++i) {
|
||||
var row = matrix[i];
|
||||
var groups;
|
||||
|
||||
groups = [0];
|
||||
for (var j = 0; j < n; ) {
|
||||
var k;
|
||||
for (k = 0; j < n && row[j]; ++k) ++j;
|
||||
groups.push(k);
|
||||
for (k = 0; j < n && !row[j]; ++k) ++j;
|
||||
groups.push(k);
|
||||
}
|
||||
score += evaluategroup(groups);
|
||||
|
||||
groups = [0];
|
||||
for (var j = 0; j < n; ) {
|
||||
var k;
|
||||
for (k = 0; j < n && matrix[j][i]; ++k) ++j;
|
||||
groups.push(k);
|
||||
for (k = 0; j < n && !matrix[j][i]; ++k) ++j;
|
||||
groups.push(k);
|
||||
}
|
||||
score += evaluategroup(groups);
|
||||
|
||||
var nextrow = matrix[i+1] || [];
|
||||
nblacks += row[0];
|
||||
for (var j = 1; j < n; ++j) {
|
||||
var p = row[j];
|
||||
nblacks += p;
|
||||
if (row[j-1] == p && nextrow[j] === p && nextrow[j-1] === p) {
|
||||
score += PENALTY_TWOBYTWO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
score += PENALTY_DENSITY * ((Math.abs(nblacks / n / n - 0.5) / 0.05) | 0);
|
||||
return score;
|
||||
};
|
||||
|
||||
var generate = function(data, ver, mode, ecclevel, mask) {
|
||||
var v = VERSIONS[ver];
|
||||
var buf = encode(ver, mode, data, ndatabits(ver, ecclevel) >> 3);
|
||||
buf = augumenteccs(buf, v[1][ecclevel], GF256_GENPOLY[v[0][ecclevel]]);
|
||||
|
||||
var result = makebasematrix(ver);
|
||||
var matrix = result.matrix, reserved = result.reserved;
|
||||
putdata(matrix, reserved, buf);
|
||||
|
||||
if (mask < 0) {
|
||||
maskdata(matrix, reserved, 0);
|
||||
putformatinfo(matrix, reserved, ecclevel, 0);
|
||||
var bestmask = 0, bestscore = evaluatematrix(matrix);
|
||||
maskdata(matrix, reserved, 0);
|
||||
for (mask = 1; mask < 8; ++mask) {
|
||||
maskdata(matrix, reserved, mask);
|
||||
putformatinfo(matrix, reserved, ecclevel, mask);
|
||||
var score = evaluatematrix(matrix);
|
||||
if (bestscore > score) {
|
||||
bestscore = score;
|
||||
bestmask = mask;
|
||||
}
|
||||
maskdata(matrix, reserved, mask);
|
||||
}
|
||||
mask = bestmask;
|
||||
}
|
||||
|
||||
maskdata(matrix, reserved, mask);
|
||||
putformatinfo(matrix, reserved, ecclevel, mask);
|
||||
return matrix;
|
||||
};
|
||||
|
||||
var QRCode = {
|
||||
'generate': function(data, options) {
|
||||
var MODES = {'numeric': MODE_NUMERIC, 'alphanumeric': MODE_ALPHANUMERIC,
|
||||
'octet': MODE_OCTET};
|
||||
var ECCLEVELS = {'L': ECCLEVEL_L, 'M': ECCLEVEL_M, 'Q': ECCLEVEL_Q,
|
||||
'H': ECCLEVEL_H};
|
||||
|
||||
options = options || {};
|
||||
var ver = options.version || -1;
|
||||
var ecclevel = ECCLEVELS[(options.ecclevel || 'L').toUpperCase()];
|
||||
var mode = options.mode ? MODES[options.mode.toLowerCase()] : -1;
|
||||
var mask = 'mask' in options ? options.mask : -1;
|
||||
|
||||
if (mode < 0) {
|
||||
if (typeof data === 'string') {
|
||||
if (data.match(NUMERIC_REGEXP)) {
|
||||
mode = MODE_NUMERIC;
|
||||
} else if (data.match(ALPHANUMERIC_OUT_REGEXP)) {
|
||||
mode = MODE_ALPHANUMERIC;
|
||||
} else {
|
||||
mode = MODE_OCTET;
|
||||
}
|
||||
} else {
|
||||
mode = MODE_OCTET;
|
||||
}
|
||||
} else if (!(mode == MODE_NUMERIC || mode == MODE_ALPHANUMERIC ||
|
||||
mode == MODE_OCTET)) {
|
||||
throw 'invalid or unsupported mode';
|
||||
}
|
||||
|
||||
data = validatedata(mode, data);
|
||||
if (data === null) throw 'invalid data format';
|
||||
|
||||
if (ecclevel < 0 || ecclevel > 3) throw 'invalid ECC level';
|
||||
|
||||
if (ver < 0) {
|
||||
for (ver = 1; ver <= 40; ++ver) {
|
||||
if (data.length <= getmaxdatalen(ver, mode, ecclevel)) break;
|
||||
}
|
||||
if (ver > 40) throw 'too large data';
|
||||
} else if (ver < 1 || ver > 40) {
|
||||
throw 'invalid version';
|
||||
}
|
||||
|
||||
if (mask != -1 && (mask < 0 || mask > 8)) throw 'invalid mask';
|
||||
|
||||
return generate(data, ver, mode, ecclevel, mask);
|
||||
},
|
||||
|
||||
'generateHTML': function(data, options) {
|
||||
options = options || {};
|
||||
var matrix = QRCode['generate'](data, options);
|
||||
var modsize = Math.max(options.modulesize || 5, 0.5);
|
||||
var margin = Math.max(options.margin !== null ? options.margin : 4, 0.0);
|
||||
|
||||
var e = document.createElement('div');
|
||||
var n = matrix.length;
|
||||
var html = ['<table border="0" cellspacing="0" cellpadding="0" style="border:' + modsize*margin + 'px solid transparent;background:transparent;">'];
|
||||
for (var i = 0; i < n; ++i) {
|
||||
html.push('<tr>');
|
||||
for (var j = 0; j < n; ++j) {
|
||||
html.push('<td style="width:' + modsize + 'px;height:' + modsize + 'px' +
|
||||
(matrix[i][j] ? ';background:var(--black);' : '') + '"></td>');
|
||||
}
|
||||
html.push('</tr>');
|
||||
}
|
||||
e.className = 'qrcode';
|
||||
e.innerHTML = html.join('') + '</table>';
|
||||
return e;
|
||||
},
|
||||
|
||||
'generateSVG': function(data, options) {
|
||||
options = options || {};
|
||||
var matrix = QRCode['generate'](data, options);
|
||||
var n = matrix.length;
|
||||
var modsize = Math.max(options.modulesize || 5, 0.5);
|
||||
var margin = Math.max(options.margin? options.margin : 4, 0.0);
|
||||
var size = modsize * (n + 2 * margin);
|
||||
|
||||
var common = ' class= "fg"'+' width="'+modsize+'" height="'+modsize+'"/>';
|
||||
|
||||
var e = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
e.setAttribute('viewBox', '0 0 '+size+' '+size);
|
||||
e.setAttribute('style', 'shape-rendering:crispEdges');
|
||||
|
||||
var svg = [
|
||||
'<style scoped>.bg{fill:#FFF}.fg{fill:#03A9F4}</style>',
|
||||
'<rect class="bg" x="0" y="0"',
|
||||
'width="'+size+'" height="'+size+'"/>',
|
||||
];
|
||||
|
||||
var yo = margin * modsize;
|
||||
for (var y = 0; y < n; ++y) {
|
||||
var xo = margin * modsize;
|
||||
for (var x = 0; x < n; ++x) {
|
||||
if (matrix[y][x])
|
||||
svg.push('<rect x="'+xo+'" y="'+yo+'"', common);
|
||||
xo += modsize;
|
||||
}
|
||||
yo += modsize;
|
||||
}
|
||||
e.innerHTML = svg.join('');
|
||||
return e;
|
||||
},
|
||||
|
||||
'generatePNG': function(data, options) {
|
||||
options = options || {};
|
||||
var matrix = QRCode['generate'](data, options);
|
||||
var modsize = Math.max(options.modulesize || 5, 0.5);
|
||||
var margin = Math.max(options.margin != null ? options.margin : 4, 0.0);
|
||||
var n = matrix.length;
|
||||
var size = modsize * (n + 2 * margin);
|
||||
|
||||
var canvas = document.createElement('canvas'), context;
|
||||
canvas.width = canvas.height = size;
|
||||
context = canvas.getContext('2d');
|
||||
if (!context) throw 'canvas support is needed for PNG output';
|
||||
|
||||
context.fillStyle = '#fff';
|
||||
context.fillRect(0, 0, size, size);
|
||||
context.fillStyle = '#000';
|
||||
for (var i = 0; i < n; ++i) {
|
||||
for (var j = 0; j < n; ++j) {
|
||||
if (matrix[i][j]) {
|
||||
context.fillRect(modsize * (margin + j),
|
||||
modsize * (margin + i),
|
||||
modsize, modsize);
|
||||
}
|
||||
}
|
||||
}
|
||||
return canvas.toDataURL();
|
||||
}
|
||||
};
|
||||
|
||||
export { QRCode };
|
||||
export default QRCode;
|
@ -0,0 +1,192 @@
|
||||
import { LitElement, html, css } from 'lit'
|
||||
import { render } from 'lit/html.js'
|
||||
import QRCode from './QRCode.js'
|
||||
|
||||
export class QortalQrcodeGenerator extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
auto: { type: Boolean },
|
||||
data: { type: String },
|
||||
debug: { type: Boolean },
|
||||
ecclevel: { type: String },
|
||||
format: { type: String },
|
||||
margin: { type: Number },
|
||||
mask: { type: Number, value: -1 },
|
||||
mode: { type: String },
|
||||
modulesize: { type: Number },
|
||||
version: { type: Number },
|
||||
qrcode: { type: Object }
|
||||
}
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.auto = false
|
||||
this.debug = false
|
||||
this.ecclevel = 'L'
|
||||
this.format = 'html'
|
||||
this.mode = 'numeric'
|
||||
this.margin = 4
|
||||
this.mask = -1
|
||||
this.modulesize = 5
|
||||
this.version = -1
|
||||
this.qrcode = null
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div id="qrCodeContainer">
|
||||
${this.qrcode ? this.qrcode : ''}
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
validateParams() {
|
||||
return (
|
||||
this.validateEcclevel() &&
|
||||
this.validateFormat() &&
|
||||
this.validateMask() &&
|
||||
this.validateMode() &&
|
||||
this.validateModulesize() &&
|
||||
this.validateVersion()
|
||||
)
|
||||
}
|
||||
|
||||
validateEcclevel() {
|
||||
if (
|
||||
this.ecclevel === 'L' ||
|
||||
this.ecclevel === 'M' ||
|
||||
this.ecclevel === 'Q' ||
|
||||
this.ecclevel === 'H'
|
||||
) {
|
||||
return true
|
||||
}
|
||||
console.error('[qortal-qrcode-generator] validateEcclevel - Invalid value of `ecclevel`', this.ecclevel)
|
||||
return false
|
||||
}
|
||||
|
||||
validateFormat() {
|
||||
if (this.format == 'html' || this.format == 'png') {
|
||||
return true
|
||||
}
|
||||
console.error('[qortal-qrcode-generator] validateFormat - Invalid value of `format`', this.format)
|
||||
return false
|
||||
}
|
||||
|
||||
validateMargin() {
|
||||
if (this.margin >= -1) {
|
||||
return true
|
||||
}
|
||||
console.error('[qortal-qrcode-generator] validateMargin - Invalid value of `margin`', this.margin)
|
||||
return false
|
||||
}
|
||||
|
||||
validateMask() {
|
||||
if (this.mask >= -1 && this.mask <= 7) {
|
||||
return true
|
||||
}
|
||||
console.error('[qortal-qrcode-generator] validateMask - Invalid value of `mask`', this.mask)
|
||||
return false
|
||||
}
|
||||
|
||||
validateMode() {
|
||||
if (this.mode === 'numeric' || this.mode === 'alphanumeric' || this.mode === 'octet') {
|
||||
return true
|
||||
}
|
||||
console.error('[qortal-qrcode-generator] validateMode - Invalid value of `mode`', this.mode)
|
||||
return false
|
||||
}
|
||||
|
||||
validateModulesize() {
|
||||
if (this.modulesize >= 0.5) {
|
||||
return true
|
||||
}
|
||||
console.error('[qortal-qrcode-generator] validateModulesize - Invalid value of `modulesize`', this.modulesize)
|
||||
return false
|
||||
}
|
||||
|
||||
validateVersion() {
|
||||
if (this.version == -1 || (this.version >= 0 && this.version <= 40)) {
|
||||
return true
|
||||
}
|
||||
console.error('[qortal-qrcode-generator] validateVersion - Invalid value of `version`', this.version)
|
||||
return false
|
||||
}
|
||||
|
||||
getOptions() {
|
||||
return {
|
||||
modulesize: this.modulesize,
|
||||
margin: this.margin,
|
||||
version: this.version,
|
||||
mode: this.mode,
|
||||
ecclevel: this.ecclevel,
|
||||
mask: this.mask,
|
||||
}
|
||||
}
|
||||
|
||||
generateQRCodePNG() {
|
||||
let img
|
||||
try {
|
||||
img = document.createElement('img')
|
||||
img.src = QRCode.generatePNG(this.data, this.getOptions())
|
||||
this.qrcode = img;
|
||||
} catch (e) {
|
||||
console.error('[qortal-qrcode-generator] generateQRCodePNG - No canvas support', e)
|
||||
}
|
||||
}
|
||||
|
||||
generateQRCodeHTML() {
|
||||
if (this.debug) {
|
||||
console.debug('[qortal-qrcode-generator] generateQRCodeHTML - data ', this.data)
|
||||
}
|
||||
this.qrcode = QRCode.generateHTML(this.data, this.getOptions())
|
||||
}
|
||||
|
||||
generateQRCode() {
|
||||
if (this.debug) {
|
||||
console.log('[qortal-qrcode-generator] generateQRCode', this.validateParams());
|
||||
}
|
||||
if (!this.validateParams()) {
|
||||
return
|
||||
}
|
||||
if (this.format === 'png') {
|
||||
this.generateQRCodePNG()
|
||||
} else {
|
||||
this.generateQRCodeHTML()
|
||||
}
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('qrcode-generated', {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
updated(changedProperties) {
|
||||
if (this.debug) {
|
||||
console.log('[qortal-qrcode-generator] updated');
|
||||
}
|
||||
|
||||
if (
|
||||
(changedProperties.has('auto') ||
|
||||
changedProperties.has('data') ||
|
||||
changedProperties.has('ecclevel') ||
|
||||
changedProperties.has('mask') ||
|
||||
changedProperties.has('mode') ||
|
||||
changedProperties.has('version')) &&
|
||||
this.auto
|
||||
) {
|
||||
this.generateQRCode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.customElements.define('qortal-qrcode-generator', QortalQrcodeGenerator)
|
Loading…
x
Reference in New Issue
Block a user