diff --git a/assets/preview.png b/assets/preview.png new file mode 100644 index 0000000..9e059b8 Binary files /dev/null and b/assets/preview.png differ diff --git a/index.html b/index.html index 45297f0..1292154 100644 --- a/index.html +++ b/index.html @@ -8,20 +8,38 @@
+
+

Instagram Formatter

- +
-
- + + - + +
+ + + + + + + + + + + +
diff --git a/script.js b/script.js index c868cf8..a062296 100644 --- a/script.js +++ b/script.js @@ -1,42 +1,159 @@ -document.getElementById('uploadForm').addEventListener('submit', async function (event) { - event.preventDefault(); +document.addEventListener('DOMContentLoaded', function () { + const uploadForm = document.getElementById('uploadForm'); + const imageInput = document.getElementById('imageInput'); + const doneImgsDiv = document.getElementById('done-imgs'); + const progressContainer = document.getElementById('progress-container'); + const errorMessage = document.getElementById('error-message'); - const formData = new FormData(); - const imageInput = document.getElementById('imageInput').files[0]; + // Advanced Tab Toggle Functionality + document.querySelector(".collapsible").addEventListener("click", function () { + this.classList.toggle("active"); + let content = this.nextElementSibling; + if (content.style.display === "block") { + content.style.display = "none"; + } else { + content.style.display = "block"; + } + }); - if (!imageInput) { - alert("Please select an image to upload."); - return; - } + uploadForm.addEventListener('submit', function (event) { + document.getElementById('download-all').style.display = 'inline-block'; + event.preventDefault(); - formData.append('image', imageInput); + const files = imageInput.files; - try { - const response = await fetch('https://api.nevets.tech/igformatter/upload', { - method: 'POST', - body: formData - }); - - if (!response.ok) { - throw new Error('Image upload failed'); + if (files.length === 0) { + alert("Please select at least one image to upload."); + return; + } else if (files.length > 10) { + errorMessage.style.display = 'block'; + return; + } else { + errorMessage.style.display = 'none'; } - const arrayBuffer = await response.arrayBuffer(); - downloadImage(arrayBuffer, imageInput.value); + // Clear previous progress bars and images if needed + progressContainer.innerHTML = ''; + doneImgsDiv.innerHTML = ''; - } catch (error) { - console.error('Error:', error); - alert('There was an error processing your image.'); + // Get user inputs + const color = document.getElementById("color")?.value || "#ffffff"; + const width = document.getElementById("width")?.value || "1080"; + const height = document.getElementById("height")?.value || "1080"; + const minBorder = document.getElementById("min-border")?.value || "16"; + + Array.from(files).forEach((file) => { + uploadAndProcessImage(file, { color, width, height, minBorder }); + }); + }); + + function uploadAndProcessImage(file, options) { + const { color, width, height, minBorder } = options; + + // Create progress bar elements + const progressWrapper = document.createElement('div'); + progressWrapper.className = 'progress-wrapper'; + + const fileNameLabel = document.createElement('div'); + fileNameLabel.className = 'file-name'; + fileNameLabel.textContent = file.name; + + const progressBar = document.createElement('div'); + progressBar.className = 'progress-bar'; + + const progressBarFill = document.createElement('div'); + progressBarFill.className = 'progress-bar-fill'; + + const progressStatus = document.createElement('div'); + progressStatus.className = 'progress-status'; + progressStatus.textContent = 'Starting upload...'; + + progressBar.appendChild(progressBarFill); + progressWrapper.appendChild(fileNameLabel); + progressWrapper.appendChild(progressBar); + progressWrapper.appendChild(progressStatus); + progressContainer.appendChild(progressWrapper); + + const formData = new FormData(); + formData.append('image', file); + + const xhr = new XMLHttpRequest(); + + xhr.open('POST', `https://api.nevets.tech/igformatter/upload?color=${encodeURIComponent(color)}&width=${encodeURIComponent(width)}&height=${encodeURIComponent(height)}&minBorder=${encodeURIComponent(minBorder)}`, true); + xhr.responseType = 'blob'; + + // Upload progress event + xhr.upload.addEventListener('progress', function (e) { + if (e.lengthComputable) { + const percentComplete = ((e.loaded / e.total) * 50).toFixed(2); // 0% to 50% + progressBarFill.style.width = `${percentComplete}%`; + progressStatus.textContent = `Uploading... ${percentComplete}%`; + } + }); + + // Download progress event + xhr.addEventListener('progress', function (e) { + if (e.lengthComputable) { + const percentComplete = (50 + ((e.loaded / e.total) * 50)).toFixed(2); // 50% to 100% + progressBarFill.style.width = `${percentComplete}%`; + progressStatus.textContent = `Processing... ${percentComplete}%`; + } else { + progressStatus.textContent = `Processing...`; + } + }); + + // On successful completion + xhr.addEventListener('load', function () { + if (xhr.status === 200) { + progressBarFill.style.width = '100%'; + progressStatus.textContent = 'Completed'; + progressWrapper.classList.add('success'); + + const blob = xhr.response; + const imageUrl = URL.createObjectURL(blob); + + const anchor = document.createElement('a'); + anchor.href = imageUrl; + anchor.className = "card-link"; + anchor.download = `${file.name.replace(/\.[^/.]+$/, "")}_formatted.png`; + + const img = document.createElement('img'); + img.src = imageUrl; + img.className = "card-img"; + img.alt = "Formatted Image Preview"; + + anchor.appendChild(img); + doneImgsDiv.appendChild(anchor); + } else { + handleError(`Processing failed for ${file.name}`, progressWrapper, progressStatus, progressBarFill); + } + }); + + // On error + xhr.addEventListener('error', function () { + handleError(`An error occurred while uploading ${file.name}`, progressWrapper, progressStatus, progressBarFill); + }); + + // On abort + xhr.addEventListener('abort', function () { + handleError(`Upload aborted for ${file.name}`, progressWrapper, progressStatus, progressBarFill); + }); + + xhr.send(formData); + } + + function handleError(message, progressWrapper, progressStatus, progressBarFill) { + console.error(message); + progressStatus.textContent = message; + progressWrapper.classList.add('error'); + progressBarFill.style.width = '100%'; } }); -function downloadImage(arrayBuffer, fileName) { - const blob = new Blob([arrayBuffer], { type: "image/png" }); - const cardElement = document.getElementById("card"); - - let urlCreator = window.URL || window.webkitURL; - cardElement.href = urlCreator.createObjectURL(blob); - cardElement.style.display = 'unset'; - cardElement.download = fileName + ".png"; - cardElement.click(); -} +// Handle the download all functionality +document.getElementById('download-all').addEventListener('click', function() { + const links = document.querySelectorAll('#done-imgs .card-link'); + links.forEach(link => { + link.click(); + }); +}); diff --git a/styles.css b/styles.css index d6fa938..2d89506 100644 --- a/styles.css +++ b/styles.css @@ -3,7 +3,7 @@ body { background-color: #f4f4f4; display: flex; justify-content: center; - align-items: center; + align-items: flex-start; height: 100vh; margin: 0; } @@ -12,8 +12,10 @@ body { background: white; padding: 20px; border-radius: 10px; - box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.1); + box-shadow: 0 0 20px rgba(0, 0, 0, 0.1); text-align: center; + max-width: 800px; + margin: auto; } h1 { @@ -23,6 +25,9 @@ h1 { input[type="file"] { margin-bottom: 15px; + display: block; + margin-left: auto; + margin-right: auto; } button { @@ -38,20 +43,160 @@ button:hover { background-color: #0a85d9; } -#card { - margin-top: 20px; - display: inline-block; - padding: 10px 20px; - background-color: #28a745; - color: white; +/* Center the progress bars */ +#progress-container { + width: 100%; + max-width: 800px; + margin: 20px auto; + text-align: center; +} + +.progress-wrapper { + margin-bottom: 10px; + padding: 10px; + background-color: #f9f9f9; + border: 1px solid #ddd; border-radius: 5px; - text-decoration: none; + width: 100%; + max-width: 400px; + margin-left: auto; + margin-right: auto; } -#card:hover { - background-color: #218838; +.progress-wrapper .file-name { + margin-bottom: 5px; + font-weight: bold; + color: #333; } -#preview { +.progress-bar { + width: 100%; + background-color: #e6e6e6; + border-radius: 5px; + overflow: hidden; + height: 20px; + position: relative; +} + +.progress-bar-fill { + height: 100%; + width: 0%; + background-color: #1da1f2; + transition: width 0.2s ease; +} + +.progress-status { + margin-top: 5px; + font-size: 12px; + color: #555; +} + +.progress-wrapper.success .progress-bar-fill { + background-color: #28a745; +} + +.progress-wrapper.success .progress-status { + color: #28a745; +} + +.progress-wrapper.error .progress-bar-fill { + background-color: #dc3545; +} + +.progress-wrapper.error .progress-status { + color: #dc3545; +} + +/* Responsive grid for images */ +#done-imgs { + display: flex; + flex-wrap: wrap; + justify-content: center; margin-top: 20px; } + +.card-link { + flex: 1 1 calc(25% - 20px); /* 4 images per row with space */ + margin: 10px; + max-width: calc(25% - 20px); + text-align: center; +} + +.card-link img { + width: 100%; + height: auto; + border: 2px solid gray; + border-radius: 5px; +} + +/* Advanced tab styling */ +.collapsible { + background-color: #1da1f2; + color: white; + padding: 10px 20px; + border: none; + border-radius: 5px; + cursor: pointer; + width: 100%; + text-align: center; + outline: none; + font-size: 16px; + margin-top: 20px; +} + +.collapsible:hover { + background-color: #0a85d9; +} + +.collapsible:after { + content: '\002B'; /* Unicode character for "plus" sign (+) */ + font-weight: bold; +} + +.collapsible.active:after { + content: "\2212"; /* Unicode character for "minus" sign (−) */ +} + +.content { + padding: 10px 20px; + display: none; + overflow: hidden; + background-color: #f1f1f1; + margin-top: 10px; + border-radius: 5px; + text-align: left; +} + +.content input[type="text"], +.content input[type="number"], +.content input[type="color"] { + margin: 5px 10px; + padding: 8px; + width: 80px; + box-sizing: border-box; + border: 1px solid #ccc; + border-radius: 5px; + display: inline-block; + vertical-align: middle; +} + +.content label { + margin: 5px 10px; + display: inline-block; + text-align: left; + color: #333; + vertical-align: middle; +} + +.content input[type="color"] { + width: 50px; + height: 30px; + padding: 0; + border: 1px solid #ccc; + border-radius: 5px; + cursor: pointer; + background-color: #fff; + -webkit-appearance: none; + appearance: none; + box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); +}