mirror of
https://github.com/bryangerlach/rdgen.git
synced 2025-11-29 00:23:27 +00:00
646 lines
31 KiB
HTML
646 lines
31 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>RustDesk Custom Client Builder</title>
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap" rel="stylesheet">
|
||
<style>
|
||
body {
|
||
font-family: 'Roboto', sans-serif;
|
||
background-color: #000;
|
||
color: #e0e0e0;
|
||
margin: 0;
|
||
padding: 20px;
|
||
}
|
||
.platform {
|
||
display: grid;
|
||
grid-template-columns: 1fr;
|
||
grid-gap: 20px;
|
||
margin: 0 auto;
|
||
padding: 20px;
|
||
max-width: 1200px;
|
||
}
|
||
.container {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr; /* Adjust as needed */
|
||
grid-gap: 20px;
|
||
margin: 0 auto; /* Center the container horizontally */
|
||
padding: 20px;
|
||
max-width: 1200px;
|
||
}
|
||
.column {
|
||
flex: 50%;
|
||
}
|
||
h1 {
|
||
color: #fff;
|
||
text-align: center;
|
||
grid-column: 1 / -1;
|
||
}
|
||
h2 {
|
||
color: #fff;
|
||
margin-top: 0;
|
||
}
|
||
.section {
|
||
background-color: #111;
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||
flex: 50%;
|
||
}
|
||
label {
|
||
display: block;
|
||
margin-bottom: 5px;
|
||
color: #bbb;
|
||
}
|
||
input[type="text"], input[type="password"], select, textarea {
|
||
width: 100%;
|
||
padding: 8px;
|
||
margin-bottom: 10px;
|
||
background-color: #222;
|
||
border: 1px solid #444;
|
||
border-radius: 4px;
|
||
color: #fff;
|
||
}
|
||
input[type="radio"], input[type="checkbox"] {
|
||
margin-right: 5px;
|
||
}
|
||
button {
|
||
background-color: #0077ff;
|
||
color: #fff;
|
||
border: none;
|
||
padding: 10px 20px;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
transition: background-color 0.3s ease;
|
||
}
|
||
button:hover {
|
||
background-color: #0066cc;
|
||
}
|
||
.platform-icons {
|
||
display: flex;
|
||
justify-content: space-around;
|
||
margin-bottom: 20px;
|
||
}
|
||
.platform-icon {
|
||
font-size: 32px;
|
||
color: #bbb;
|
||
cursor: pointer;
|
||
transition: color 0.3s ease;
|
||
}
|
||
.platform-icon:hover, .platform-icon.active {
|
||
color: #2e52f7;
|
||
}
|
||
.checkbox-group {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||
gap: 10px;
|
||
}
|
||
.checkbox-group label {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
.preview-image {
|
||
max-width: 100%;
|
||
max-height: 100px;
|
||
margin-top: 10px;
|
||
}
|
||
.save-load-section-container { /* New container for fixed positioning */
|
||
position: fixed;
|
||
top: 20px; /* Adjust as needed */
|
||
left: 20px; /* Adjust as needed */
|
||
background-color: #111; /* Match your section background */
|
||
padding: 0px;
|
||
border-radius: 8px;
|
||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||
z-index: 100; /* Ensure it's above other content */
|
||
}
|
||
.save-load-section {
|
||
display: none; /* Initially hidden */
|
||
}
|
||
.error {
|
||
color: red;
|
||
}
|
||
.errorlist {
|
||
color: red;
|
||
display: flex; /* Enable flexbox for centering */
|
||
justify-content: center; /* Center horizontally */
|
||
align-items: center; /* Center vertically (if needed) */
|
||
list-style: none; /* Remove bullet points if it's a list */
|
||
padding: 0; /* Remove default padding */
|
||
margin: 10px auto; /* Center the list itself, add some top/bottom margin */
|
||
width: fit-content; /* Make the width fit the content */
|
||
}
|
||
|
||
.errorlist li {
|
||
margin: 5px; /* Add some spacing between list items */
|
||
}
|
||
@keyframes blink {
|
||
0% { opacity: 1; }
|
||
50% { opacity: 0.5; }
|
||
100% { opacity: 1; }
|
||
}
|
||
|
||
.help-text {
|
||
color: #ffd700;
|
||
font-style: italic;
|
||
animation: blink 2s infinite;
|
||
padding: 5px;
|
||
border-radius: 4px;
|
||
display: inline-block;
|
||
margin-left: 10px;
|
||
}
|
||
|
||
.password-requirement {
|
||
display: none; /* Hidden by default */
|
||
color: orange;
|
||
font-size: 0.9em;
|
||
margin-top: 5px;
|
||
}
|
||
|
||
.sponsor-button {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
background: linear-gradient(135deg, #00457C 0%, #0079C1 100%);
|
||
color: white;
|
||
padding: 12px 28px;
|
||
border-radius: 50px;
|
||
text-decoration: none;
|
||
font-weight: 600;
|
||
font-size: 16px;
|
||
letter-spacing: 0.5px;
|
||
transition: all 0.3s ease;
|
||
box-shadow: 0 4px 15px rgba(0, 69, 124, 0.2);
|
||
border: 2px solid rgba(255, 255, 255, 0.1);
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.sponsor-button:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 8px 25px rgba(0, 69, 124, 0.3);
|
||
background: linear-gradient(135deg, #005AA7 0%, #0095EA 100%);
|
||
border-color: rgba(255, 255, 255, 0.2);
|
||
}
|
||
|
||
.sponsor-button i {
|
||
margin-right: 12px;
|
||
font-size: 20px;
|
||
background: white;
|
||
color: #00457C;
|
||
padding: 8px;
|
||
border-radius: 50%;
|
||
width: 20px;
|
||
height: 20px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.sponsor-button:hover i {
|
||
transform: rotate(360deg);
|
||
color: #0095EA;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<h1><i class="fas fa-cogs"></i> RustDesk Custom Client Builder</h1>
|
||
<form id="myForm" action="/generator" method="post" enctype="multipart/form-data">
|
||
<div class="save-load-section-container">
|
||
<div class="section">
|
||
<h2 id="saveLoadTitle">Save/Load Configuration <i class="fas fa-chevron-down"></i></h2>
|
||
<div class="save-load-section">
|
||
<button type="button" onclick="saveFormData()">Save Configuration</button>
|
||
<input type="file" id="fileInput" style="display:none;" accept=".json">
|
||
<button type="button" onclick="loadFormData()">Load Configuration</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% if form.iconfile.errors %}
|
||
<ul class="errorlist">
|
||
{% for error in form.iconfile.errors %}
|
||
<li>{{ error }}</li>
|
||
{% endfor %}
|
||
</ul>
|
||
{% endif %}
|
||
<div class="platform">
|
||
<h2><i class="fas fa-desktop"></i> Select Platform</h2>
|
||
<div class="platform-icons">
|
||
<i class="fab fa-windows platform-icon active" data-platform="windows"></i>
|
||
<i class="fab fa-linux platform-icon" data-platform="linux"></i>
|
||
<i class="fab fa-android platform-icon" data-platform="android"></i>
|
||
<i class="fab fa-apple platform-icon" data-platform="macos"></i>
|
||
</div>
|
||
<select name="platform" id="id_platform">
|
||
<option value="windows" selected>Windows 64Bit</option>
|
||
<option value="windows-x86">Windows 32Bit</option>
|
||
<option value="linux">Linux</option>
|
||
<option value="android">Android</option>
|
||
<option value="macos">macOS</option>
|
||
</select>
|
||
<label for="{{ form.version.id_for_label }}">Rustdesk Version:</label>
|
||
{{ form.version }}
|
||
{% if form.version.help_text %}
|
||
<span class="help-text">{{ form.version.help_text }}</span>
|
||
{% endif %}
|
||
<div class="help-text">{{ form.version.help_text }}</div>
|
||
<label for="{{ form.delayFix.id_for_label }}">{{ form.delayFix }} Fix connection delay when using third-party API</label>
|
||
</div>
|
||
</div>
|
||
<div class="container">
|
||
<div class="section">
|
||
<h2><i class="fas fa-sliders-h"></i> General</h2>
|
||
<label for="{{ form.exename.id_for_label }}">Name of the configuration (no spaces or special characters, English characters only):</label>
|
||
{{ form.exename }}<span id="filenameError" class="error"></span><br><br>
|
||
<label for="{{ form.appname.id_for_label }}">Custom Application Name (leave blank to use default):</label>
|
||
{{ form.appname }}<br><br>
|
||
<label for="{{ form.direction.id_for_label }}">Connection Type:</label>
|
||
{{ form.direction }}<br><br>
|
||
<label for="{{ form.installation.id_for_label }}">Disable Installation:</label>
|
||
{{ form.installation }}<br><br>
|
||
<label for="{{ form.settings.id_for_label }}">Disable Settings:</label>
|
||
{{ form.settings }}<br><br>
|
||
</div>
|
||
|
||
<div class="section">
|
||
<h2><i class="fas fa-server"></i> Custom Server</h2>
|
||
<label for="{{ form.serverIP.id_for_label }}">Host:</label>
|
||
{{ form.serverIP }}<br><br>
|
||
<label for="{{ form.key.id_for_label }}">Key:</label>
|
||
{{ form.key }}<br><br>
|
||
<label for="{{ form.apiServer.id_for_label }}">API:</label>
|
||
{{ form.apiServer }}<br><br>
|
||
<label for="{{ form.urlLink.id_for_label }}">Custom URL for links (replaces https://rustdesk.com):</label>
|
||
{{ form.urlLink }}<br><br>
|
||
<label for="{{ form.downloadLink.id_for_label }}">Custom URL for downloading updates (replaces https://rustdesk.com/download):</label>
|
||
{{ form.downloadLink }}<br><br>
|
||
<label for="{{ form.compname.id_for_label }}">Company name for copyright (replaces Purslane Ltd):</label>
|
||
{{ form.compname }}<br><br>
|
||
</div>
|
||
</div>
|
||
<div class="container">
|
||
<div class="section">
|
||
<h2><i class="fas fa-shield-alt"></i> Security</h2>
|
||
<label for="{{ form.runasadmin.id_for_label }}">Always run as Administrator?</label>
|
||
{{ form.runasadmin }}<br><br>
|
||
<label for="{{ form.passApproveMode.id_for_label }}">Password Approve mode:</label>
|
||
{{ form.passApproveMode }}<br><br>
|
||
<div id="passwordRequirement" class="password-requirement">To use the hide connection window feature, please set a permanent password.</div>
|
||
<label for="{{ form.permanentPassword.id_for_label }}">Set Permanent Password:</label>
|
||
{{ form.permanentPassword }} *The password is used as default, but can be changed by the client<br><br>
|
||
|
||
|
||
<label for="{{ form.denyLan.id_for_label }}">{{ form.denyLan }} Deny LAN discovery</label><br>
|
||
|
||
<label for="{{ form.enableDirectIP.id_for_label }}">{{ form.enableDirectIP }} Enable direct IP access</label><br>
|
||
|
||
<label for="{{ form.autoClose.id_for_label }}">{{ form.autoClose }} Automatically close incoming sessions on user inactivity</label><br>
|
||
|
||
<label for="{{ form.hidecm.id_for_label }}">{{ form.hidecm }} Allow hiding the connection window from remote screen.</label><br>
|
||
</div>
|
||
|
||
<div class="section">
|
||
<h2><i class="fas fa-paint-brush"></i> Visual</h2>
|
||
{{ form.iconbase64.as_hidden }}
|
||
{{ form.logobase64.as_hidden }}
|
||
<label for="{{ form.iconfile.id_for_label }}">Custom App Icon (in .png format)</label>
|
||
{{ form.iconfile }}<br><br>
|
||
<!-- <input type="file" name="iconfile" id="iconfile" accept="image/png"> -->
|
||
<div id="icon-preview"></div><br><br>
|
||
<label for="{{ form.logofile.id_for_label }}">Custom App Logo (in .png format)</label>
|
||
{{ form.logofile }}<br><br>
|
||
<!-- <input type="file" name="logofile" id="logofile" accept="image/png"> -->
|
||
<div id="logo-preview"></div><br><br>
|
||
<label for="{{ form.theme.id_for_label }}">Theme:</label>
|
||
{{ form.theme }} {{ form.themeDorO }} *Default sets the theme but allows the client to change it, Override sets the theme permanently.<br><br>
|
||
</div>
|
||
</div>
|
||
<div class="container">
|
||
<div class="section">
|
||
<h2><i class="fas fa-lock"></i> Permissions</h2>
|
||
The following Permissions can be set as default (the user can change the settings) or override (the settings cannot be changed).<br>
|
||
{{ form.permissionsDorO }}
|
||
<label for="{{ form.permissionsType.id_for_label }}">Permission type:</label>
|
||
{{ form.permissionsType }}<br><br>
|
||
<div class="checkbox-group">
|
||
<label for="{{ form.enableKeyboard.id_for_label }}">{{ form.enableKeyboard }} Enable keyboard/mouse</label>
|
||
<label for="{{ form.enableClipboard.id_for_label }}">{{ form.enableClipboard }} Enable clipboard</label>
|
||
<label for="{{ form.enableFileTransfer.id_for_label }}">{{ form.enableFileTransfer }} Enable file transfer</label>
|
||
<label for="{{ form.enableAudio.id_for_label }}">{{ form.enableAudio }} Enable audio</label>
|
||
<label for="{{ form.enableTCP.id_for_label }}">{{ form.enableTCP }} Enable TCP tunneling</label>
|
||
<label for="{{ form.enableRemoteRestart.id_for_label }}">{{ form.enableRemoteRestart }} Enable remote restart</label>
|
||
<label for="{{ form.enableRecording.id_for_label }}">{{ form.enableRecording }} Enable recording session</label>
|
||
<label for="{{ form.enableBlockingInput.id_for_label }}">{{ form.enableBlockingInput }} Enable blocking user input</label>
|
||
<label for="{{ form.enableRemoteModi.id_for_label }}">{{ form.enableRemoteModi }} Enable remote configuration modification</label>
|
||
<label for="{{ form.enablePrinter.id_for_label }}">{{ form.enablePrinter }} Enable remote printer</label>
|
||
<label for="{{ form.enableCamera.id_for_label }}">{{ form.enableCamera }} Enable remote camera</label>
|
||
<label for="{{ form.enableTerminal.id_for_label }}">{{ form.enableTerminal }} Enable remote terminal</label>
|
||
</div><br>
|
||
<h2><i class="fas fa-code"></i> Code Changes</h2>
|
||
<label for="{{ form.cycleMonitor.id_for_label }}">{{ form.cycleMonitor }} Add a button to cycle through available monitors to the minimized toolbar.</label><br>
|
||
<label for="{{ form.xOffline.id_for_label }}">{{ form.xOffline }} Display an X for offline devices in the addressbook.</label><br>
|
||
<label for="{{ form.removeNewVersionNotif.id_for_label }}">{{ form.removeNewVersionNotif }} Remove notification for new versions.</label><br>
|
||
</div>
|
||
|
||
<div class="section">
|
||
<h2><i class="fas fa-cog"></i> Other</h2>
|
||
<label for="{{ form.removeWallpaper.id_for_label }}">{{ form.removeWallpaper }} Remove wallpaper during incoming sessions</label><br>
|
||
<a href="https://rustdesk.com/docs/en/self-host/client-configuration/advanced-settings/">Click here for a list of Default/Override settings</a>
|
||
<label for="{{ form.defaultManual.id_for_label }}">Default settings</label><br>
|
||
{{ form.defaultManual }}<br><br>
|
||
<label for="{{ form.overrideManual.id_for_label }}">Override settings</label><br>
|
||
{{ form.overrideManual }}<br><br>
|
||
</div>
|
||
</div>
|
||
<div class="platform">
|
||
<div class="section">
|
||
<button type="submit"><i class="fas fa-rocket"></i> Generate Custom Client</button>
|
||
</div>
|
||
</div>
|
||
<div style="text-align: center; margin: 30px 0;">
|
||
<a href="https://github.com/bryangerlach/rdgen"
|
||
target="_blank"
|
||
class="sponsor-button">
|
||
<i class="fab fa-github"></i>
|
||
Source Code on Github
|
||
</a>
|
||
</div>
|
||
<div style="text-align: center; margin: 30px 0;">
|
||
<a href="https://github.com/sponsors/bryangerlach?o=esb"
|
||
target="_blank"
|
||
class="sponsor-button">
|
||
<i class="fab fa-github"></i>
|
||
Donate
|
||
</a>
|
||
</div>
|
||
</form>
|
||
<script>
|
||
document.querySelectorAll('.platform-icon').forEach(icon => {
|
||
icon.addEventListener('click', function() {
|
||
document.querySelectorAll('.platform-icon').forEach(i => i.classList.remove('active'));
|
||
this.classList.add('active');
|
||
document.getElementById('id_platform').value = this.dataset.platform;
|
||
});
|
||
});
|
||
document.getElementById("{{ form.iconfile.id_for_label }}").addEventListener('change', function(event) {
|
||
previewImage(event.target, 'icon-preview');
|
||
});
|
||
document.getElementById("{{ form.logofile.id_for_label }}").addEventListener('change', function(event) {
|
||
previewImage(event.target, 'logo-preview');
|
||
});
|
||
|
||
document.getElementById("{{ form.hidecm.id_for_label }}").addEventListener('change',function() {
|
||
if (this.checked) {
|
||
document.getElementById("passwordRequirement").style.display = 'block';
|
||
document.getElementById("{{ form.permanentPassword.id_for_label }}").focus();
|
||
document.getElementById("{{ form.passApproveMode.id_for_label }}").value = 'password';
|
||
} else {
|
||
document.getElementById("passwordRequirement").style.display = 'none';
|
||
document.getElementById("{{ form.passApproveMode.id_for_label }}").value = 'password-click';
|
||
}
|
||
});
|
||
const enableKeyboard = document.getElementById("{{ form.enableKeyboard.id_for_label }}");
|
||
const enableClipboard = document.getElementById("{{ form.enableClipboard.id_for_label }}");
|
||
const enableFileTransfer = document.getElementById("{{ form.enableFileTransfer.id_for_label }}");
|
||
const enableAudio = document.getElementById("{{ form.enableAudio.id_for_label }}");
|
||
const enableTCP = document.getElementById("{{ form.enableTCP.id_for_label }}");
|
||
const enableRemoteRestart = document.getElementById("{{ form.enableRemoteRestart.id_for_label }}");
|
||
const enableRecording = document.getElementById("{{ form.enableRecording.id_for_label }}");
|
||
const enableBlockingInput = document.getElementById("{{ form.enableBlockingInput.id_for_label }}");
|
||
const enableRemoteModi = document.getElementById("{{ form.enableRemoteModi.id_for_label }}");
|
||
const enablePrinter = document.getElementById("{{ form.enablePrinter.id_for_label }}");
|
||
const enableCamera = document.getElementById("{{ form.enableCamera.id_for_label }}");
|
||
const enableTerminal = document.getElementById("{{ form.enableTerminal.id_for_label }}");
|
||
|
||
document.getElementById("{{ form.permissionsType.id_for_label }}").addEventListener('change', function() {
|
||
if (this.value === 'full') {
|
||
enableKeyboard.checked = true;
|
||
enableClipboard.checked = true;
|
||
enableFileTransfer.checked = true;
|
||
enableAudio.checked = true;
|
||
enableTCP.checked = true;
|
||
enableRemoteRestart.checked = true;
|
||
enableRecording.checked = true;
|
||
enableBlockingInput.checked = true;
|
||
enableRemoteModi.checked = true;
|
||
enablePrinter.checked = true;
|
||
enableCamera.checked = true;
|
||
enableTerminal.checked = true;
|
||
|
||
enableKeyboard.disabled = true;
|
||
enableClipboard.disabled = true;
|
||
enableFileTransfer.disabled = true;
|
||
enableAudio.disabled = true;
|
||
enableTCP.disabled = true;
|
||
enableRemoteRestart.disabled = true;
|
||
enableRecording.disabled = true;
|
||
enableBlockingInput.disabled = true;
|
||
enableRemoteModi.disabled = true;
|
||
enablePrinter.disabled = true;
|
||
enableCamera.disabled = true;
|
||
enableTerminal.disable = true;
|
||
} else if (this.value === 'view') {
|
||
enableKeyboard.checked = false;
|
||
enableClipboard.checked = false;
|
||
enableFileTransfer.checked = false;
|
||
enableAudio.checked = false;
|
||
enableTCP.checked = false;
|
||
enableRemoteRestart.checked = false;
|
||
enableRecording.checked = false;
|
||
enableBlockingInput.checked = false;
|
||
enableRemoteModi.checked = false;
|
||
enablePrinter.checked = false;
|
||
enableCamera.checked = false;
|
||
enableTerminal.checked = false;
|
||
|
||
enableKeyboard.disabled = true;
|
||
enableClipboard.disabled = true;
|
||
enableFileTransfer.disabled = true;
|
||
enableAudio.disabled = true;
|
||
enableTCP.disabled = true;
|
||
enableRemoteRestart.disabled = true;
|
||
enableRecording.disabled = true;
|
||
enableBlockingInput.disabled = true;
|
||
enableRemoteModi.disabled = true;
|
||
enablePrinter.disabled = true;
|
||
enableCamera.disabled = true;
|
||
enableTerminal.disable = true;
|
||
} else if (this.value === 'custom') {
|
||
enableKeyboard.disabled = false;
|
||
enableClipboard.disabled = false;
|
||
enableFileTransfer.disabled = false;
|
||
enableAudio.disabled = false;
|
||
enableTCP.disabled = false;
|
||
enableRemoteRestart.disabled = false;
|
||
enableRecording.disabled = false;
|
||
enableBlockingInput.disabled = false;
|
||
enableRemoteModi.disabled = false;
|
||
enablePrinter.checked = false;
|
||
enableCamera.checked = false;
|
||
enableTerminal.checked = false;
|
||
}
|
||
});
|
||
function previewImage(input, previewContainerId) {
|
||
if (input.files && input.files[0]) {
|
||
var reader = new FileReader();
|
||
reader.onload = function(e) {
|
||
var img = document.createElement('img');
|
||
img.src = e.target.result;
|
||
img.style.maxWidth = '300px';
|
||
img.style.maxHeight = '60px';
|
||
document.getElementById(previewContainerId).innerHTML = '';
|
||
document.getElementById(previewContainerId).appendChild(img);
|
||
};
|
||
reader.readAsDataURL(input.files[0]);
|
||
}
|
||
}
|
||
const saveLoadTitle = document.getElementById("saveLoadTitle");
|
||
const saveLoadSection = document.querySelector(".save-load-section");
|
||
|
||
saveLoadTitle.addEventListener("click", () => {
|
||
if (!saveLoadSection.style.display || saveLoadSection.style.display === "none") {
|
||
saveLoadSection.style.display = "block";
|
||
} else {
|
||
saveLoadSection.style.display = "none";
|
||
}
|
||
|
||
const icon = saveLoadTitle.querySelector("i");
|
||
icon.classList.toggle("fa-chevron-down");
|
||
icon.classList.toggle("fa-chevron-up");
|
||
});
|
||
|
||
async function saveFormData() {
|
||
const filename = document.getElementById("{{ form.exename.id_for_label }}").value;
|
||
if (!filename) {
|
||
document.getElementById("filenameError").textContent = "Filename is required.";
|
||
return;
|
||
} else {
|
||
document.getElementById("filenameError").textContent = "";
|
||
}
|
||
|
||
const formData = {};
|
||
const form = document.getElementById("myForm"); // Get the form element
|
||
|
||
// 1. Use FormData for robust data collection (handles most input types):
|
||
const formDataObj = new FormData(form); // Create a FormData object
|
||
|
||
formDataObj.forEach((value, key) => {
|
||
formData[key] = value; // Add each key/value pair to the formData object.
|
||
});
|
||
|
||
// 2. Handle select elements separately (more reliable):
|
||
const selectElements = form.querySelectorAll('select');
|
||
selectElements.forEach(select => {
|
||
formData[select.name] = select.value;
|
||
});
|
||
|
||
// 3. Handle checkboxes and radio buttons (important!):
|
||
const checkboxRadioElements = form.querySelectorAll('input[type="checkbox"], input[type="radio"]');
|
||
checkboxRadioElements.forEach(input => {
|
||
if (input.name) { //only add to form data if it has a name
|
||
if (input.checked) {
|
||
formData[input.name] = input.value;
|
||
} else if (!formData.hasOwnProperty(input.name) && input.type === 'checkbox') { //if it's a checkbox and it's not checked, add it as false
|
||
formData[input.name] = false;
|
||
}
|
||
}
|
||
});
|
||
|
||
// 4. Handle icon and logo
|
||
const iconPreview = document.getElementById('icon-preview');
|
||
if (iconPreview.firstChild && iconPreview.firstChild.src.startsWith('data:image/png;base64')) {
|
||
formData.iconfile = iconPreview.firstChild.src; // Use existing base64
|
||
} else { //if it's a file upload
|
||
const iconFile = document.getElementById("{{ form.iconfile.id_for_label }}").files[0];
|
||
if (iconFile) {
|
||
formData.iconfile = await readFileAsBase64(iconFile);
|
||
}
|
||
}
|
||
|
||
const logoPreview = document.getElementById('logo-preview');
|
||
if (logoPreview.firstChild && logoPreview.firstChild.src.startsWith('data:image/png;base64')) {
|
||
formData.logofile = logoPreview.firstChild.src; // Use existing base64
|
||
} else { //if it's a file upload
|
||
const logoFile = document.getElementById("{{ form.logofile.id_for_label }}").files[0];
|
||
if (logoFile) {
|
||
formData.logofile = await readFileAsBase64(logoFile);
|
||
}
|
||
}
|
||
|
||
const jsonData = JSON.stringify(formData, null, 2);
|
||
const blob = new Blob([jsonData], { type: "application/json" });
|
||
const url = URL.createObjectURL(blob);
|
||
|
||
const a = document.createElement("a");
|
||
a.href = url;
|
||
a.download = filename + ".json";
|
||
document.body.appendChild(a);
|
||
a.click();
|
||
document.body.removeChild(a);
|
||
URL.revokeObjectURL(url);
|
||
}
|
||
|
||
async function readFileAsBase64(file) {
|
||
return new Promise((resolve, reject) => {
|
||
const reader = new FileReader();
|
||
reader.onload = (event) => resolve(event.target.result);
|
||
reader.onerror = (error) => reject(error);
|
||
reader.readAsDataURL(file);
|
||
});
|
||
}
|
||
|
||
|
||
function loadFormData() {
|
||
const fileInput = document.getElementById("fileInput");
|
||
fileInput.click();
|
||
|
||
fileInput.addEventListener("change", (event) => {
|
||
const file = event.target.files[0];
|
||
if (file) {
|
||
const reader = new FileReader();
|
||
reader.onload = (e) => {
|
||
try {
|
||
const formData = JSON.parse(e.target.result);
|
||
for (const key in formData) {
|
||
// More robust selector: checks for name OR id
|
||
const elements = document.querySelectorAll(`[name="${key}"], [id="${key}"]`);
|
||
|
||
if (elements.length > 0) { // Check if any element(s) exist
|
||
elements.forEach(element => { // Loop through all matching elements (important for radios)
|
||
if (element.type === 'radio') {
|
||
if (element.value === String(formData[key])) { // Compare value, crucial for radios
|
||
element.checked = true;
|
||
} else {
|
||
element.checked = false; // Uncheck others in the group
|
||
}
|
||
} else if (element.type === 'checkbox') {
|
||
element.checked = formData[key];
|
||
} else if (element.type !== 'file') {
|
||
element.value = formData[key];
|
||
}
|
||
|
||
// Handle image previews (as before)
|
||
if (key === 'iconfile' && formData[key]) {
|
||
document.getElementById('id_iconbase64').value = formData[key];
|
||
document.getElementById('icon-preview').innerHTML = `<img src="${formData[key]}" style="max-width: 300px; max-height: 60px;">`;
|
||
}
|
||
if (key === 'logofile' && formData[key]) {
|
||
document.getElementById('id_logobase64').value = formData[key];
|
||
document.getElementById('logo-preview').innerHTML = `<img src="${formData[key]}" style="max-width: 300px; max-height: 60px;">`;
|
||
}
|
||
});
|
||
}
|
||
}
|
||
} catch (error) {
|
||
alert("Error loading data. Invalid JSON file.");
|
||
}
|
||
};
|
||
reader.readAsText(file);
|
||
}
|
||
});
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|