mirror of
https://github.com/elder-plinius/P4RS3LT0NGV3.git
synced 2026-07-11 23:16:34 +02:00
added copy button and error messages to code generator
This commit is contained in:
@@ -508,6 +508,7 @@
|
||||
<script src="js/utils/openrouterModels.js"></script>
|
||||
<script src="js/utils/routing.js"></script>
|
||||
<script src="js/utils/emoji.js"></script>
|
||||
<script src="js/utils/ean13.js"></script>
|
||||
|
||||
<!-- Core modules (feature libraries) -->
|
||||
<script src="js/core/steganography.js"></script>
|
||||
|
||||
+74
-6
@@ -52,7 +52,10 @@ class CodesTool extends Tool {
|
||||
}
|
||||
if (this.codesFormat === 'ean13') {
|
||||
var digits = text.replace(/\D/g, '');
|
||||
if (digits.length !== 12 && digits.length !== 13) {
|
||||
if (!digits.length) {
|
||||
return 'EAN-13 requires digits only (12 or 13 digits). Letters and symbols are not valid.';
|
||||
}
|
||||
if (digits.length < 12 || digits.length > 13) {
|
||||
return 'EAN-13 requires 12 or 13 digits (check digit is calculated automatically for 12).';
|
||||
}
|
||||
}
|
||||
@@ -64,23 +67,34 @@ class CodesTool extends Tool {
|
||||
codesClearOutput: function() {
|
||||
this.codesOutputUrl = '';
|
||||
this.codesOutputSvg = '';
|
||||
},
|
||||
codesResetGenerate: function() {
|
||||
this.codesClearOutput();
|
||||
this.codesError = '';
|
||||
},
|
||||
codesGenerate: function() {
|
||||
var validationError = this.codesValidateGenerateInput();
|
||||
if (validationError) {
|
||||
this.codesError = validationError;
|
||||
this.codesClearOutput();
|
||||
this.codesError = validationError;
|
||||
return;
|
||||
}
|
||||
|
||||
this.codesError = '';
|
||||
this.codesOutputUrl = '';
|
||||
this.codesOutputSvg = '';
|
||||
this.codesClearOutput();
|
||||
|
||||
var text = String(this.codesInput || '').trim();
|
||||
if (this.codesFormat === 'ean13') {
|
||||
text = text.replace(/\D/g, '');
|
||||
if (typeof Ean13Utils === 'undefined' || typeof Ean13Utils.normalize !== 'function') {
|
||||
this.codesError = 'EAN-13 helpers not loaded. Rebuild the app (npm run build).';
|
||||
return;
|
||||
}
|
||||
var ean13 = Ean13Utils.normalize(text);
|
||||
if (!ean13.ok) {
|
||||
this.codesError = ean13.error;
|
||||
return;
|
||||
}
|
||||
text = ean13.value;
|
||||
}
|
||||
if (this.codesFormat === 'code39') {
|
||||
text = text.toUpperCase();
|
||||
@@ -137,6 +151,60 @@ class CodesTool extends Tool {
|
||||
this.codesError = (err && err.message) || 'Failed to generate barcode.';
|
||||
}
|
||||
},
|
||||
codesRasterizeToPng: function(imageUrl) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var img = new Image();
|
||||
img.onload = function() {
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = img.naturalWidth || img.width;
|
||||
canvas.height = img.naturalHeight || img.height;
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.fillStyle = '#ffffff';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.drawImage(img, 0, 0);
|
||||
resolve(canvas.toDataURL('image/png'));
|
||||
};
|
||||
img.onerror = function() {
|
||||
reject(new Error('Failed to render barcode image.'));
|
||||
};
|
||||
img.src = imageUrl;
|
||||
});
|
||||
},
|
||||
codesCopyImage: function() {
|
||||
var self = this;
|
||||
if (!this.codesOutputUrl) {
|
||||
return;
|
||||
}
|
||||
if (!navigator.clipboard || typeof navigator.clipboard.write !== 'function') {
|
||||
this.codesError = 'Image copy is not supported in this browser.';
|
||||
return;
|
||||
}
|
||||
|
||||
var blobPromise;
|
||||
if (this.codesOutputUrl.indexOf('data:image/png') === 0) {
|
||||
blobPromise = fetch(this.codesOutputUrl).then(function(response) {
|
||||
return response.blob();
|
||||
});
|
||||
} else {
|
||||
blobPromise = this.codesRasterizeToPng(this.codesOutputUrl).then(function(pngUrl) {
|
||||
return fetch(pngUrl).then(function(response) {
|
||||
return response.blob();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
blobPromise.then(function(blob) {
|
||||
return navigator.clipboard.write([
|
||||
new ClipboardItem({ 'image/png': blob })
|
||||
]);
|
||||
}).then(function() {
|
||||
if (typeof self.showNotification === 'function') {
|
||||
self.showNotification('Image copied to clipboard', 'success', 'fas fa-copy');
|
||||
}
|
||||
}).catch(function(err) {
|
||||
self.codesError = (err && err.message) || 'Failed to copy image to clipboard.';
|
||||
});
|
||||
},
|
||||
codesDownload: function() {
|
||||
if (!this.codesOutputUrl) {
|
||||
return;
|
||||
@@ -248,7 +316,7 @@ class CodesTool extends Tool {
|
||||
}
|
||||
},
|
||||
codesFormat: function() {
|
||||
this.codesClearOutput();
|
||||
this.codesResetGenerate();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* EAN-13 normalization helpers for the Codes tool.
|
||||
*/
|
||||
(function(global) {
|
||||
'use strict';
|
||||
|
||||
function checksum(base12) {
|
||||
var sum = 0;
|
||||
for (var i = 0; i < 12; i++) {
|
||||
var digit = parseInt(base12.charAt(i), 10);
|
||||
sum += (i % 2 === 0) ? digit : digit * 3;
|
||||
}
|
||||
return String((10 - (sum % 10)) % 10);
|
||||
}
|
||||
|
||||
function upcChecksum(upc12) {
|
||||
var sum = 0;
|
||||
for (var i = 0; i < 11; i++) {
|
||||
var digit = parseInt(upc12.charAt(i), 10);
|
||||
sum += (i % 2 === 0) ? digit * 3 : digit;
|
||||
}
|
||||
return String((10 - (sum % 10)) % 10);
|
||||
}
|
||||
|
||||
function isValidUpcA(digits) {
|
||||
return digits.length === 12
|
||||
&& digits.charAt(0) === '0'
|
||||
&& upcChecksum(digits) === digits.charAt(11);
|
||||
}
|
||||
|
||||
function fromUpcA(upc12) {
|
||||
var ean12 = '0' + upc12.slice(0, 11);
|
||||
return ean12 + checksum(ean12);
|
||||
}
|
||||
|
||||
function normalize(input) {
|
||||
var digits = String(input || '').replace(/\D/g, '');
|
||||
if (digits.length < 12) {
|
||||
return {
|
||||
ok: false,
|
||||
error: 'EAN-13 requires 12 or 13 digits.'
|
||||
};
|
||||
}
|
||||
if (digits.length > 13) {
|
||||
return {
|
||||
ok: false,
|
||||
error: 'EAN-13 accepts at most 13 digits.'
|
||||
};
|
||||
}
|
||||
|
||||
if (digits.length === 12 && isValidUpcA(digits)) {
|
||||
return { ok: true, value: fromUpcA(digits) };
|
||||
}
|
||||
|
||||
var base12 = digits.length === 12 ? digits : digits.slice(0, 12);
|
||||
return { ok: true, value: base12 + checksum(base12) };
|
||||
}
|
||||
|
||||
var api = {
|
||||
checksum: checksum,
|
||||
upcChecksum: upcChecksum,
|
||||
isValidUpcA: isValidUpcA,
|
||||
fromUpcA: fromUpcA,
|
||||
normalize: normalize
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = api;
|
||||
} else {
|
||||
global.Ean13Utils = api;
|
||||
}
|
||||
})(typeof window !== 'undefined' ? window : globalThis);
|
||||
@@ -80,29 +80,32 @@
|
||||
<textarea
|
||||
v-model="codesInput"
|
||||
rows="3"
|
||||
:placeholder="codesFormat === 'ean13' ? '12 or 13 digits, e.g. 5901234123457' : 'Text, URL, or payload…'"
|
||||
:placeholder="codesFormat === 'ean13' ? '12 or 13 digits, e.g. 590123412345' : 'Text, URL, or payload…'"
|
||||
spellcheck="false"
|
||||
></textarea>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div v-if="codesError" class="codes-error" role="alert">{{ codesError }}</div>
|
||||
|
||||
<div class="tool-toolbar u-mb-16">
|
||||
<button type="button" class="transform-button tool-primary-btn" @click="codesGenerate">
|
||||
<i class="fas fa-qrcode"></i> Generate {{ codesFormatLabel(codesFormat) }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="codesError" class="codes-error">{{ codesError }}</div>
|
||||
|
||||
<div v-if="codesOutputUrl" class="codes-output-card">
|
||||
<div class="codes-preview-wrap">
|
||||
<img :src="codesOutputUrl" :alt="codesFormatLabel(codesFormat)" class="codes-preview-image" />
|
||||
</div>
|
||||
<div class="tool-toolbar">
|
||||
<button type="button" class="action-button copy" @click="codesCopyImage">
|
||||
<i class="fas fa-copy"></i> Copy
|
||||
</button>
|
||||
<button type="button" class="action-button download" @click="codesDownload">
|
||||
<i class="fas fa-download"></i> Download
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary" @click="codesClearOutput">
|
||||
<button type="button" class="btn btn-secondary" @click="codesResetGenerate">
|
||||
<i class="fas fa-eraser"></i> Clear
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const assert = require('assert');
|
||||
const path = require('path');
|
||||
|
||||
const Ean13Utils = require(path.join(__dirname, '..', 'js', 'utils', 'ean13.js'));
|
||||
|
||||
assert.strictEqual(Ean13Utils.checksum('590123412345'), '7');
|
||||
assert.strictEqual(Ean13Utils.checksum('400638133393'), '1');
|
||||
|
||||
const from12 = Ean13Utils.normalize('590123412345');
|
||||
assert.strictEqual(from12.ok, true);
|
||||
assert.strictEqual(from12.value, '5901234123457');
|
||||
|
||||
const from13 = Ean13Utils.normalize('5901234123457');
|
||||
assert.strictEqual(from13.ok, true);
|
||||
assert.strictEqual(from13.value, '5901234123457');
|
||||
|
||||
const wrongCheck = Ean13Utils.normalize('5901234123450');
|
||||
assert.strictEqual(wrongCheck.ok, true);
|
||||
assert.strictEqual(wrongCheck.value, '5901234123457');
|
||||
|
||||
const withSeparators = Ean13Utils.normalize('590-1234-12345');
|
||||
assert.strictEqual(withSeparators.ok, true);
|
||||
assert.strictEqual(withSeparators.value, '5901234123457');
|
||||
|
||||
const upcA = Ean13Utils.normalize('036000291452');
|
||||
assert.strictEqual(upcA.ok, true);
|
||||
assert.strictEqual(upcA.value, '0036000291452');
|
||||
|
||||
const tooShort = Ean13Utils.normalize('12345');
|
||||
assert.strictEqual(tooShort.ok, false);
|
||||
|
||||
const tooLong = Ean13Utils.normalize('12345678901234');
|
||||
assert.strictEqual(tooLong.ok, false);
|
||||
|
||||
const EAN13 = require(path.join(__dirname, '..', 'node_modules', 'jsbarcode', 'bin', 'barcodes', 'EAN_UPC', 'EAN13.js')).default;
|
||||
const defaults = require(path.join(__dirname, '..', 'node_modules', 'jsbarcode', 'bin', 'options', 'defaults.js')).default;
|
||||
|
||||
const normalizedWrong = Ean13Utils.normalize('5901234123450');
|
||||
const encoder = new EAN13(normalizedWrong.value, Object.assign({}, defaults, { displayValue: true }));
|
||||
assert.strictEqual(encoder.valid(), true, 'Normalized value should pass JsBarcode validation');
|
||||
|
||||
console.log('EAN-13 codes tests passed');
|
||||
Reference in New Issue
Block a user