diff --git a/src/dist/js/qrcode-style-tools.js b/src/dist/js/qrcode-style-tools.js index a488750..7efc934 100644 --- a/src/dist/js/qrcode-style-tools.js +++ b/src/dist/js/qrcode-style-tools.js @@ -1,12 +1,23 @@ // Preset system + random style button for the qr code generation forms (Fase 3, priority 2). +// +// The static qr "add" page stacks all qr-type forms (text/email/.../wifi/bitcoin/...) into +// the DOM at once as Bootstrap tab-panes, and every one of them repeats the same element ids +// (foreground, background, size, random_style_btn, ...). getElementById/getElementsBy* only +// ever finds the *first* of those (the "Text" tab), so every helper below is scoped to the +// specific tab-pane/form the triggering element lives in, and wired up via querySelectorAll +// so every tab gets working listeners, not just the first one. (function () { function csrfToken() { var meta = document.querySelector('meta[name="csrf-token"]'); return meta ? meta.getAttribute('content') : ''; } - function setColor(id, hex) { - var input = document.getElementById(id); + function scopeOf(el) { + return el.closest('.tab-pane') || el.closest('form') || document; + } + + function setColor(scope, id, hex) { + var input = scope.querySelector('#' + id); if (!input) { return; } @@ -27,12 +38,35 @@ return '#' + ('000000' + value).slice(-6); } - function loadPresets() { - var select = document.getElementById('preset_select'); - if (!select) { + function updateStylePreview(scope) { + var swatch = scope.querySelector('#style_preview_swatch'); + var text = scope.querySelector('#style_preview_text'); + if (!swatch || !text) { return; } + var foreground = scope.querySelector('#foreground'); + var background = scope.querySelector('#background'); + var levelSelect = scope.querySelector('select[name="level"]'); + var sizeSelect = scope.querySelector('#size'); + + var fg = foreground ? foreground.value : '#000000'; + var bg = background ? background.value : '#ffffff'; + + swatch.style.background = bg; + swatch.style.borderColor = fg; + + var parts = []; + if (levelSelect) { + parts.push('Precision: ' + levelSelect.value); + } + if (sizeSelect) { + parts.push('Size: ' + sizeSelect.value + 'px'); + } + text.textContent = parts.join(' ยท '); + } + + function loadPresetsInto(select) { fetch('presets.php?action=list') .then(function (response) { return response.json(); }) .then(function (json) { @@ -54,43 +88,69 @@ } document.addEventListener('DOMContentLoaded', function () { - loadPresets(); + document.querySelectorAll('#preset_select').forEach(loadPresetsInto); - var randomBtn = document.getElementById('random_style_btn'); - if (randomBtn) { - randomBtn.addEventListener('click', function () { - setColor('foreground', randomHexColor()); - setColor('background', randomHexColor()); + document.querySelectorAll('#style_preview').forEach(function (row) { + updateStylePreview(scopeOf(row)); + }); + + document.querySelectorAll('#foreground, #background').forEach(function (input) { + input.addEventListener('change', function () { + updateStylePreview(scopeOf(input)); }); - } + }); - var presetSelect = document.getElementById('preset_select'); - if (presetSelect) { + document.querySelectorAll('select[name="level"]').forEach(function (select) { + select.addEventListener('change', function () { + updateStylePreview(scopeOf(select)); + }); + }); + + document.querySelectorAll('#size').forEach(function (select) { + select.addEventListener('change', function () { + updateStylePreview(scopeOf(select)); + }); + }); + + document.querySelectorAll('#random_style_btn').forEach(function (randomBtn) { + randomBtn.addEventListener('click', function () { + var scope = scopeOf(randomBtn); + setColor(scope, 'foreground', randomHexColor()); + setColor(scope, 'background', randomHexColor()); + updateStylePreview(scope); + }); + }); + + document.querySelectorAll('#preset_select').forEach(function (presetSelect) { presetSelect.addEventListener('change', function () { var option = presetSelect.options[presetSelect.selectedIndex]; if (!option.value) { return; } - setColor('foreground', option.dataset.foreground); - setColor('background', option.dataset.background); + var scope = scopeOf(presetSelect); - var levelSelect = document.querySelector('select[name="level"]'); + setColor(scope, 'foreground', option.dataset.foreground); + setColor(scope, 'background', option.dataset.background); + + var levelSelect = scope.querySelector('select[name="level"]'); if (levelSelect) { levelSelect.value = option.dataset.level; } - var sizeSelect = document.getElementById('size'); + var sizeSelect = scope.querySelector('#size'); if (sizeSelect) { sizeSelect.value = option.dataset.size; } - }); - } - var saveBtn = document.getElementById('preset_save_btn'); - if (saveBtn) { + updateStylePreview(scope); + }); + }); + + document.querySelectorAll('#preset_save_btn').forEach(function (saveBtn) { saveBtn.addEventListener('click', function () { - var nameInput = document.getElementById('preset_name'); + var scope = scopeOf(saveBtn); + var nameInput = scope.querySelector('#preset_name'); var name = nameInput.value.trim(); if (!name) { @@ -98,13 +158,18 @@ return; } + var foreground = scope.querySelector('#foreground').value; + var background = scope.querySelector('#background').value; + var level = scope.querySelector('select[name="level"]').value; + var size = scope.querySelector('#size').value; + var body = new URLSearchParams(); body.set('action', 'save'); body.set('name', name); - body.set('foreground', document.getElementById('foreground').value); - body.set('background', document.getElementById('background').value); - body.set('level', document.querySelector('select[name="level"]').value); - body.set('size', document.getElementById('size').value); + body.set('foreground', foreground); + body.set('background', background); + body.set('level', level); + body.set('size', size); fetch('presets.php', { method: 'POST', @@ -118,24 +183,33 @@ return; } - var option = document.createElement('option'); - option.value = json.data.id; - option.textContent = json.data.name; - option.dataset.foreground = document.getElementById('foreground').value; - option.dataset.background = document.getElementById('background').value; - option.dataset.level = document.querySelector('select[name="level"]').value; - option.dataset.size = document.getElementById('size').value; - presetSelect.appendChild(option); - presetSelect.value = option.value; + // The preset list is shared across every tab, so mirror the new + // option into every preset_select on the page, not just this one. + document.querySelectorAll('#preset_select').forEach(function (select) { + var option = document.createElement('option'); + option.value = json.data.id; + option.textContent = json.data.name; + option.dataset.foreground = foreground; + option.dataset.background = background; + option.dataset.level = level; + option.dataset.size = size; + select.appendChild(option); + + if (select === scope.querySelector('#preset_select')) { + select.value = option.value; + } + }); + nameInput.value = ''; }) .catch(function () { alert('Could not save preset (network error).'); }); }); - } + }); - var deleteBtn = document.getElementById('preset_delete_btn'); - if (deleteBtn) { + document.querySelectorAll('#preset_delete_btn').forEach(function (deleteBtn) { deleteBtn.addEventListener('click', function () { + var scope = scopeOf(deleteBtn); + var presetSelect = scope.querySelector('#preset_select'); var option = presetSelect.options[presetSelect.selectedIndex]; if (!option.value) { return; @@ -157,13 +231,19 @@ .then(function (response) { return response.json(); }) .then(function (json) { if (json.status === 200) { - option.remove(); + // Remove the matching option from every preset_select on the page. + document.querySelectorAll('#preset_select').forEach(function (select) { + var match = select.querySelector('option[value="' + option.value + '"]'); + if (match) { + match.remove(); + } + }); } else { alert('Could not delete preset: ' + json.data); } }) .catch(function () { alert('Could not delete preset (network error).'); }); }); - } + }); }); })(); diff --git a/src/dynamic_qrcode.php b/src/dynamic_qrcode.php index f8e2783..cfbd8bc 100644 --- a/src/dynamic_qrcode.php +++ b/src/dynamic_qrcode.php @@ -37,7 +37,7 @@ if($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST["edit"])) { $dynamic_qrcode_instance->editQrcode($_POST); } -if($_SERVER["REQUEST_METHOD"] === "POST" && !isset($_POST["edit"])) { +if($_SERVER["REQUEST_METHOD"] === "POST" && !isset($_POST["edit"]) && !isset($_POST["del_id"])) { if( isset($_POST["foreground"]) && isset($_POST["background"]) && @@ -45,8 +45,17 @@ if($_SERVER["REQUEST_METHOD"] === "POST" && !isset($_POST["edit"])) { isset($_POST["filename"]) && isset($_POST["format"]) && isset($_POST["id_owner"]) - ) + ) { + $icon_upload = qr_handle_icon_upload('icon'); + if (!$icon_upload['ok']) { + $_SESSION['failure'] = $icon_upload['error']; + header('Location: ' . basename(__FILE__)); + exit; + } + $_POST['icon_tmp_path'] = $icon_upload['path']; + $dynamic_qrcode_instance->addQrcode($_POST); + } } ?> diff --git a/src/forms/form_dynamic_add.php b/src/forms/form_dynamic_add.php index b6e424e..fb975b5 100644 --- a/src/forms/form_dynamic_add.php +++ b/src/forms/form_dynamic_add.php @@ -54,6 +54,7 @@
+ @@ -80,6 +81,14 @@
+ +
+ +
+ + +
+
@@ -136,6 +145,35 @@ Optional label rendered below the code. Only applies to PNG/JPEG/GIF, not SVG/EPS. + +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + + Optional. PNG/JPEG/GIF, max 1MB. Shown above the code (not embedded in it). Only applies to PNG/JPEG/GIF output. +
+
@@ -146,7 +184,7 @@
diff --git a/src/forms/form_static_add.php b/src/forms/form_static_add.php index 1d9bf90..9511f80 100644 --- a/src/forms/form_static_add.php +++ b/src/forms/form_static_add.php @@ -105,4 +105,6 @@ + + \ No newline at end of file diff --git a/src/forms/qrcode_options.php b/src/forms/qrcode_options.php index a9fbbd2..7f1507e 100644 --- a/src/forms/qrcode_options.php +++ b/src/forms/qrcode_options.php @@ -58,6 +58,7 @@ if (QRCODE_GENERATOR === "internal-chillerlan.qrcode") {
+ @@ -84,10 +85,25 @@ if (QRCODE_GENERATOR === "internal-chillerlan.qrcode") {
+ +
+ +
+ + +
+
- +