579 lines
36 KiB
HTML
579 lines
36 KiB
HTML
<!DOCTYPE html>
|
|
<html class="animate-0" lang="en">
|
|
|
|
<head>
|
|
<title>MAZANOKE | Online Image Optimizer That Runs Privately in Your Browser</title>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<meta name="description" content="Optimize images locally and privately by converting and compressing them offline in your browser. Supports JPG, PNG, WebP, HEIC, AVIF, GIF, SVG.">
|
|
<meta name="robots" content="noindex, nofollow">
|
|
<script>
|
|
function setTheme(themeName) {
|
|
localStorage.setItem('theme', themeName);
|
|
document.documentElement.classList.toggle('theme-dark', themeName === 'theme-dark');
|
|
document.documentElement.classList.toggle('theme-light', themeName === 'theme-light');
|
|
setTimeout(() => document.documentElement.classList.remove('animate-0'), 300);
|
|
}
|
|
|
|
function toggleTheme() {
|
|
document.documentElement.classList.add('animate-0');
|
|
setTheme(localStorage.getItem('theme') === 'theme-dark' ? 'theme-light' : 'theme-dark');
|
|
}
|
|
|
|
(function () {
|
|
const theme = localStorage.getItem('theme') || 'theme-dark';
|
|
setTheme(theme);
|
|
})();
|
|
</script>
|
|
<link rel="stylesheet" type="text/css" href="./assets/css/fonts.css">
|
|
<link rel="stylesheet" type="text/css" href="./assets/css/variables.css">
|
|
<link rel="stylesheet" type="text/css" href="./assets/css/style.css">
|
|
<link rel="apple-touch-icon" sizes="180x180" href="./assets/images/apple-touch-icon.png">
|
|
<link rel="icon" type="image/png" sizes="32x32" href="./assets/images/favicon-32x32.png">
|
|
<link rel="icon" type="image/png" sizes="16x16" href="./assets/images/favicon-16x16.png">
|
|
<link rel="manifest" href="/manifest.json">
|
|
<meta name="theme-color" content="#191919">
|
|
</head>
|
|
|
|
<body>
|
|
<div class="body-tint-bottom"></div>
|
|
|
|
<nav class="top-nav">
|
|
<div class="logo-container">
|
|
<img class="logo-image" src="./assets/images/symbol-192x192.png" alt="MAZANOKE logo">
|
|
<h1 class="logo-text">MAZANOKE</h1>
|
|
</div>
|
|
|
|
<div class="top-nav-actions">
|
|
<div class="theme-switch-container">
|
|
<label id="themeSwitch" class="switch switch--theme">
|
|
<input type="checkbox" onchange="toggleTheme()" id="themeSwitchThumb">
|
|
<span class="switch-thumb">
|
|
<span class="switch-icon switch-icon--light">
|
|
<svg height="16" stroke-linejoin="round" viewBox="0 0 16 16" width="16" style="color: currentcolor;"><path fill-rule="evenodd" clip-rule="evenodd" d="M8.75 0.75V0H7.25V0.75V2V2.75H8.75V2V0.75ZM11.182 3.75732L11.7123 3.22699L12.0659 2.87344L12.5962 2.34311L13.6569 3.40377L13.1265 3.9341L12.773 4.28765L12.2426 4.81798L11.182 3.75732ZM8 10.5C9.38071 10.5 10.5 9.38071 10.5 8C10.5 6.61929 9.38071 5.5 8 5.5C6.61929 5.5 5.5 6.61929 5.5 8C5.5 9.38071 6.61929 10.5 8 10.5ZM8 12C10.2091 12 12 10.2091 12 8C12 5.79086 10.2091 4 8 4C5.79086 4 4 5.79086 4 8C4 10.2091 5.79086 12 8 12ZM13.25 7.25H14H15.25H16V8.75H15.25H14H13.25V7.25ZM0.75 7.25H0V8.75H0.75H2H2.75V7.25H2H0.75ZM2.87348 12.0659L2.34315 12.5962L3.40381 13.6569L3.93414 13.1265L4.28769 12.773L4.81802 12.2426L3.75736 11.182L3.22703 11.7123L2.87348 12.0659ZM3.75735 4.81798L3.22702 4.28765L2.87347 3.9341L2.34314 3.40377L3.4038 2.34311L3.93413 2.87344L4.28768 3.22699L4.81802 3.75732L3.75735 4.81798ZM12.0659 13.1265L12.5962 13.6569L13.6569 12.5962L13.1265 12.0659L12.773 11.7123L12.2426 11.182L11.182 12.2426L11.7123 12.773L12.0659 13.1265ZM8.75 13.25V14V15.25V16H7.25V15.25V14V13.25H8.75Z" fill="currentColor"></path></svg>
|
|
</span>
|
|
<span class="switch-icon switch-icon--dark">
|
|
<svg height="16" stroke-linejoin="round" viewBox="0 0 16 16" width="16" style="color: currentcolor;"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 8.00005C1.5 5.53089 2.99198 3.40932 5.12349 2.48889C4.88136 3.19858 4.75 3.95936 4.75 4.7501C4.75 8.61609 7.88401 11.7501 11.75 11.7501C11.8995 11.7501 12.048 11.7454 12.1953 11.7361C11.0955 13.1164 9.40047 14.0001 7.5 14.0001C4.18629 14.0001 1.5 11.3138 1.5 8.00005ZM6.41706 0.577759C2.78784 1.1031 0 4.22536 0 8.00005C0 12.1422 3.35786 15.5001 7.5 15.5001C10.5798 15.5001 13.2244 13.6438 14.3792 10.9921L13.4588 9.9797C12.9218 10.155 12.3478 10.2501 11.75 10.2501C8.71243 10.2501 6.25 7.78767 6.25 4.7501C6.25 3.63431 6.58146 2.59823 7.15111 1.73217L6.41706 0.577759ZM13.25 1V1.75V2.75L14.25 2.75H15V4.25H14.25H13.25V5.25V6H11.75V5.25V4.25H10.75L10 4.25V2.75H10.75L11.75 2.75V1.75V1H13.25Z" fill="currentColor"></path></svg> </span>
|
|
</span>
|
|
</label>
|
|
</div>
|
|
<button id="installPWAPrompt"
|
|
class="button-cta button-secondary minw-auto hidden"
|
|
onclick="installPWADialog.showModal()">
|
|
<svg height="16" stroke-linejoin="round" viewBox="0 0 16 16" width="16" style="color: currentcolor;"><path fill-rule="evenodd" clip-rule="evenodd" d="M8.75 5.25V4.5H7.25V5.25V9.43934L5.78033 7.96967L5.25 7.43934L4.18934 8.5L4.71967 9.03033L7.46967 11.7803C7.76256 12.0732 8.23744 12.0732 8.53033 11.7803L11.2803 9.03033L11.8107 8.5L10.75 7.43934L10.2197 7.96967L8.75 9.43934V5.25ZM1.5 8C1.5 4.41015 4.41015 1.5 8 1.5C11.5899 1.5 14.5 4.41015 14.5 8C14.5 11.5899 11.5899 14.5 8 14.5C4.41015 14.5 1.5 11.5899 1.5 8ZM8 0C3.58172 0 0 3.58172 0 8C0 12.4183 3.58172 16 8 16C12.4183 16 16 12.4183 16 8C16 3.58172 12.4183 0 8 0Z" fill="currentColor"></path></svg>
|
|
<span>Install</span>
|
|
</button>
|
|
</div>
|
|
</nav>
|
|
|
|
<div class="content">
|
|
|
|
<div class="drop-zone" id="compressDropZone">
|
|
<div class="drop-zone__border-dashed"></div>
|
|
<div class="drop-zone__cell-grid"></div>
|
|
<div class="drop-zone__vignette-outer"></div>
|
|
<div class="drop-zone__vignette-inner"></div>
|
|
<div class="drop-zone__shade"></div>
|
|
<div class="drop-zone__shade-top"></div>
|
|
<div class="drop-zone__sheen"></div>
|
|
|
|
|
|
<div class="drop-zone-content">
|
|
<div class="drop-zone-content__border-top-fade"></div>
|
|
<div class="drop-zone-content__border"></div>
|
|
|
|
<div id="dropZoneActions" class="drop-zone-content__actions">
|
|
<div class="flex flex-col items-center gap-3xs w-100">
|
|
<svg width="48" height="48" style="stroke: currentcolor" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path stroke-linejoin="round" stroke-linecap="round" stroke-width="1.6" d="M3.00005 17.0001C3 16.9355 3 16.8689 3 16.8002V7.2002C3 6.08009 3 5.51962 3.21799 5.0918C3.40973 4.71547 3.71547 4.40973 4.0918 4.21799C4.51962 4 5.08009 4 6.2002 4H17.8002C18.9203 4 19.4801 4 19.9079 4.21799C20.2842 4.40973 20.5905 4.71547 20.7822 5.0918C21 5.5192 21 6.07899 21 7.19691V16.8031C21 17.2881 21 17.6679 20.9822 17.9774M3.00005 17.0001C3.00082 17.9884 3.01337 18.5058 3.21799 18.9074C3.40973 19.2837 3.71547 19.5905 4.0918 19.7822C4.5192 20 5.07899 20 6.19691 20H17.8036C18.9215 20 19.4805 20 19.9079 19.7822C20.2842 19.5905 20.5905 19.2837 20.7822 18.9074C20.9055 18.6654 20.959 18.3813 20.9822 17.9774M3.00005 17.0001L7.76798 11.4375L7.76939 11.436C8.19227 10.9426 8.40406 10.6955 8.65527 10.6064C8.87594 10.5282 9.11686 10.53 9.33643 10.6113C9.58664 10.704 9.79506 10.9539 10.2119 11.4541L12.8831 14.6595C13.269 15.1226 13.463 15.3554 13.6986 15.4489C13.9065 15.5313 14.1357 15.5406 14.3501 15.4773C14.5942 15.4053 14.8091 15.1904 15.2388 14.7607L15.7358 14.2637C16.1733 13.8262 16.3921 13.6076 16.6397 13.5361C16.8571 13.4734 17.0896 13.4869 17.2988 13.5732C17.537 13.6716 17.7302 13.9124 18.1167 14.3955L20.9822 17.9774M20.9822 17.9774L21 17.9996M15 10C14.4477 10 14 9.55228 14 9C14 8.44772 14.4477 8 15 8C15.5523 8 16 8.44772 16 9C16 9.55228 15.5523 10 15 10Z"></path></svg>
|
|
<div class="flex flex-col items-center gap-3xs w-100">
|
|
<div class="drop-zone-content__actions-title">Drop or paste images</div>
|
|
<div class="drop-zone-content__actions-description"><span>jpg</span><span>png</span><span>webp</span><span>heic</span><span>avif</span><span>gif</span><span>svg</span></div>
|
|
</div>
|
|
</div>
|
|
<button class="button-cta button-secondary">
|
|
<svg height="16" stroke-linejoin="round" viewBox="0 0 16 16" width="16" style="color: currentcolor;"><path fill-rule="evenodd" clip-rule="evenodd" d="M14.5 7.5V12.5C14.5 13.0523 14.0523 13.5 13.5 13.5H2.5C1.94772 13.5 1.5 13.0523 1.5 12.5V7.5H14.5ZM14.5 6V4H8.83333C8.29241 4 7.76607 3.82456 7.33333 3.5L6 2.5H1.5V6H14.5ZM0 1H1.5H6.16667C6.38304 1 6.59357 1.07018 6.76667 1.2L8.23333 2.3C8.40643 2.42982 8.61696 2.5 8.83333 2.5H14.5H16V4V12.5C16 13.8807 14.8807 15 13.5 15H2.5C1.11929 15 0 13.8807 0 12.5V2.5V1Z" fill="currentColor"></path></svg>
|
|
Browse
|
|
</button>
|
|
</div>
|
|
|
|
<div class="progress-container hidden">
|
|
<div id="compressProgressQueueCount"></div>
|
|
<div id="compressProgressTrack">
|
|
<div id="compressProgressBar"></div>
|
|
</div>
|
|
<div id="compressProgressText" data-progress="0"></div>
|
|
|
|
<div id="compressAbort" class="mt-sm hidden">
|
|
<button class="button-cta button-secondary" onclick="abort(event)">Cancel</button>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<input id="compress" type="file" accept=".svg, .gif, .jpg, .jpeg, .png, .webp, .heic, .heif, .avif, .jxl" multiple />
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<nav class="nav-subpage">
|
|
<div class="segmented-control-group h6" id="selectSettingsSubpage" data-elevation="2">
|
|
<div class="segmented-control segmented-control--is-selected" onclick="selectSubpage('settings')">
|
|
<div class="segmented-control__selected-item">
|
|
<!-- Selected control indicator -->
|
|
</div>
|
|
<div class="segmented-control__text">
|
|
<svg height="20" width="20" stroke-linejoin="round" style="color:currentColor" viewBox="0 0 16 16" ><path fill-rule="evenodd" clip-rule="evenodd" d="M10.75 5.5C11.7165 5.5 12.5 4.7165 12.5 3.75C12.5 2.7835 11.7165 2 10.75 2C9.7835 2 9 2.7835 9 3.75C9 4.7165 9.7835 5.5 10.75 5.5ZM10.75 0.75C12.1479 0.75 13.3225 1.70608 13.6555 3H15.25H16V4.5H15.25H13.6555C13.3225 5.79392 12.1479 6.75 10.75 6.75C9.35212 6.75 8.17754 5.79392 7.84451 4.5H0.75H0V3H0.75H7.84451C8.17754 1.70608 9.35212 0.75 10.75 0.75ZM15.25 13H16V11.5H15.25L8.15549 11.5C7.82245 10.2061 6.64788 9.25 5.25 9.25C3.85212 9.25 2.67755 10.2061 2.34451 11.5H0.75H0V13H0.75H2.34451C2.67755 14.2939 3.85212 15.25 5.25 15.25C6.64788 15.25 7.82246 14.2939 8.15549 13L15.25 13ZM7 12.2513C7 12.2509 7 12.2504 7 12.25C7 12.2496 7 12.2491 7 12.2487C6.99929 11.2828 6.21606 10.5 5.25 10.5C4.2835 10.5 3.5 11.2835 3.5 12.25C3.5 13.2165 4.2835 14 5.25 14C6.21606 14 6.99929 13.2172 7 12.2513Z" fill="currentColor"></path></svg>
|
|
Settings
|
|
</div>
|
|
<input type="radio" name="settingsSubpage" value="settings" class="hidden" checked>
|
|
</div>
|
|
<div class="segmented-control" id="subpageOutput" onclick="selectSubpage('output')" data-count="0">
|
|
<div class="segmented-control__text">
|
|
<svg width="24" height="24" style="stroke: currentcolor" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3.00005 18.0001C3 17.9355 3 17.8689 3 17.8002V6.2002C3 5.08009 3 4.51962 3.21799 4.0918C3.40973 3.71547 3.71547 3.40973 4.0918 3.21799C4.51962 3 5.08009 3 6.2002 3H17.8002C18.9203 3 19.4801 3 19.9079 3.21799C20.2842 3.40973 20.5905 3.71547 20.7822 4.0918C21 4.5192 21 5.07899 21 6.19691V17.8031C21 18.2881 21 18.6679 20.9822 18.9774M3.00005 18.0001C3.00082 18.9884 3.01337 19.5058 3.21799 19.9074C3.40973 20.2837 3.71547 20.5905 4.0918 20.7822C4.5192 21 5.07899 21 6.19691 21H17.8036C18.9215 21 19.4805 21 19.9079 20.7822C20.2842 20.5905 20.5905 20.2837 20.7822 19.9074C20.9055 19.6654 20.959 19.3813 20.9822 18.9774M3.00005 18.0001L7.76798 12.4375L7.76939 12.436C8.19227 11.9426 8.40406 11.6955 8.65527 11.6064C8.87594 11.5282 9.11686 11.53 9.33643 11.6113C9.58664 11.704 9.79506 11.9539 10.2119 12.4541L12.8831 15.6595C13.269 16.1226 13.463 16.3554 13.6986 16.4489C13.9065 16.5313 14.1357 16.5406 14.3501 16.4773C14.5942 16.4053 14.8091 16.1904 15.2388 15.7607L15.7358 15.2637C16.1733 14.8262 16.3921 14.6076 16.6397 14.5361C16.8571 14.4734 17.0896 14.4869 17.2988 14.5732C17.537 14.6716 17.7302 14.9124 18.1167 15.3955L20.9822 18.9774M20.9822 18.9774L21 18.9996M15 9C14.4477 9 14 8.55228 14 8C14 7.44772 14.4477 7 15 7C15.5523 7 16 7.44772 16 8C16 8.55228 15.5523 9 15 9Z" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
|
<span>
|
|
Images
|
|
<span class="badge">
|
|
<span id="compressedImageCount" class="badge-text">
|
|
0
|
|
</span>
|
|
</span>
|
|
</span>
|
|
</div>
|
|
<input type="radio" name="settingsSubpage" value="output" class="hidden">
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<section class="image-subpage-container">
|
|
|
|
<div class="image-subpage">
|
|
|
|
<div class="image-options-container" data-elevation="2">
|
|
|
|
<div class="image-subpage-title-container">
|
|
<h2 class="h4">Settings</h2>
|
|
<div class="image-subpage-title-description">Images are processed locally on your device, ensuring privacy.</div>
|
|
</div>
|
|
|
|
<div id="compressMethodGroup" class="form-group">
|
|
<div class="form-group__label-content">
|
|
<label class="form-group__label-text">
|
|
Optimization method
|
|
</label>
|
|
</div>
|
|
<div class="form-group__input-content">
|
|
|
|
<div class="button-card-radio-group">
|
|
<div class="button-card-radio button-card-radio--is-selected" onclick="setCompressMethod('quality')">
|
|
<div class="button-card-radio__content">
|
|
<div class="flex gap-sm w-100">
|
|
<label class="button-card-radio__label">Set image quality</label>
|
|
<div class="button-card-radio__radio-input">
|
|
<div class="button-card-radio__radio-icon"></div>
|
|
<input class="hidden" type="radio" name="compressMethod" value="quality" checked>
|
|
</div>
|
|
</div>
|
|
<div class="button-card-radio__description">
|
|
<div class="button-card-radio__description-text">
|
|
Higher values retain more detail, while lower values result in smaller file sizes.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
<div class="button-card-radio" onclick="setCompressMethod('limitWeight')">
|
|
<div class="button-card-radio__content">
|
|
<div class="flex gap-sm w-100">
|
|
<label class="button-card-radio__label">Limit file size</label>
|
|
<div class="button-card-radio__radio-input">
|
|
<div class="button-card-radio__radio-icon"></div>
|
|
<input class="hidden" type="radio" name="compressMethod" value="limitWeight">
|
|
</div>
|
|
</div>
|
|
<div class="button-card-radio__description">
|
|
<div class="button-card-radio__description-text">
|
|
Compress the image to a target file size.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div id="qualityField" class="form-group hidden">
|
|
<div class="form-group__label-content">
|
|
<label class="form-group__label-text" for="quality">
|
|
<!-- Quality -->
|
|
</label>
|
|
</div>
|
|
<div class="form-group__input-content">
|
|
<div class="fade-in-up w-100">
|
|
<label class="mb-2xs">Quality</label>
|
|
<div class="flex items-center gap-sm flex-wrap">
|
|
<div class="form-field-container form-field-container--suffix">
|
|
<input type="number" id="quality" name="quality" value="80" min="0" max="100" step="1" oninput="setSlider(this.value, 'qualitySlider')" />
|
|
<label for="quality" class="form-field-suffix">
|
|
%
|
|
</label>
|
|
</div>
|
|
<div class="slider-track flex-1" id="qualitySlider" onmousedown="startSliderDrag(event, 'quality')">
|
|
<div class="slider-fill"></div>
|
|
<div class="slider-thumb"></div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="limitWeightField" class="form-group hidden">
|
|
<div class="form-group__label-content">
|
|
<label class="form-group__label-text" for="maxWeight">
|
|
<!-- Target file size -->
|
|
</label>
|
|
</div>
|
|
<div class="form-group__input-content">
|
|
<div class="fade-in-up w-100">
|
|
<label class="mb-2xs">Target file size</label>
|
|
|
|
<div class="form-field-container">
|
|
<div class="flex flex-wrap gap-xs w-100">
|
|
<div class="flex-1" style="min-width: 200px">
|
|
<div class="form-field-container form-field-container--suffix w-100">
|
|
<input type="number" id="limitWeight" name="limitWeight" value="2" step="0.1" class="w-100" />
|
|
<label for="limitWeight" class="form-field-suffix" data-suffix="MB">MB</label>
|
|
</div>
|
|
</div>
|
|
<div class="flex-1" style="min-width: 200px">
|
|
<select id="limitWeightUnit" class="w-100">
|
|
<option value="MB">Megabytes (MB)</option>
|
|
<option value="KB">Kilobytes (KB)</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div class="form-group mt-lg">
|
|
<div class="form-group__label-content">
|
|
<label class="form-group__label-text" for="limitDimensions">
|
|
Dimensions
|
|
</label>
|
|
<div class="form-group__description">
|
|
</div>
|
|
</div>
|
|
<div class="form-group__input-content">
|
|
|
|
<div id="dimensionsMethodGroup" class="button-card-radio-group">
|
|
<div class="button-card-radio button-card-radio--is-selected" onclick="setDimensionMethod('original')">
|
|
<div class="button-card-radio__content">
|
|
<div class="flex gap-sm w-100">
|
|
<label class="button-card-radio__label">Keep original dimensions</label>
|
|
<div class="button-card-radio__radio-input">
|
|
<div class="button-card-radio__radio-icon"></div>
|
|
<input class="hidden" type="radio" name="dimensionMethod" value="original" checked>
|
|
</div>
|
|
</div>
|
|
<div class="button-card-radio__description">
|
|
<div class="button-card-radio__description-text">
|
|
Do not alter the width and height of the image.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="button-card-radio" onclick="setDimensionMethod('limit')">
|
|
<div class="button-card-radio__content">
|
|
<div class="flex gap-sm w-100">
|
|
<label class="button-card-radio__label">Limit dimensions</label>
|
|
<div class="button-card-radio__radio-input">
|
|
<div class="button-card-radio__radio-icon"></div>
|
|
<input class="hidden" type="radio" name="dimensionMethod" value="limit">
|
|
</div>
|
|
</div>
|
|
<div class="button-card-radio__description">
|
|
<div class="button-card-radio__description-text">
|
|
Limit the width and height of the image.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group hidden" id="resizeDimensionsField">
|
|
<div class="form-group__label-content">
|
|
<label class="form-group__label-text" for="limitDimensions">
|
|
<!-- Max dimension -->
|
|
</label>
|
|
</div>
|
|
<div class="form-group__input-content">
|
|
<div class="fade-in-up w-100">
|
|
<label class="mb-2xs">Limit dimensions</label>
|
|
<div class="form-field-container form-field-container--suffix">
|
|
<input type="number" id="limitDimensions" name="limitDimensions" value="1200" step="50" class="w-100" />
|
|
<label for="limitDimensions" class="form-field-suffix">
|
|
px
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div id="formatMethodGroup" class="form-group mt-lg">
|
|
<div class="form-group__label-content">
|
|
<label class="form-group__label-text">
|
|
Convert to
|
|
</label>
|
|
</div>
|
|
<div class="form-group__input-content">
|
|
<div class="button-card-radio-group">
|
|
<div class="button-card-radio button-card-radio--is-selected" onclick="setConvertMethod('default')">
|
|
<div class="button-card-radio__content">
|
|
<div class="flex gap-sm w-100">
|
|
<label class="button-card-radio__label">Default</label>
|
|
<div class="button-card-radio__radio-input">
|
|
<div class="button-card-radio__radio-icon"></div>
|
|
<input class="hidden" type="radio" name="formatSelect" value="default" checked>
|
|
</div>
|
|
</div>
|
|
<div class="button-card-radio__description">
|
|
<div class="button-card-radio__description-text">
|
|
<div class="flex flex-col gap-3xs">
|
|
<div>• JPG, PNG, WebP → Unchanged.</div>
|
|
<div>• HEIC, AVIF, GIF, SVG → PNG.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="button-card-radio" onclick="setConvertMethod('image/jpeg')">
|
|
<div class="button-card-radio__content">
|
|
<div class="flex gap-sm w-100">
|
|
<label class="button-card-radio__label">JPG</label>
|
|
<div class="button-card-radio__radio-input">
|
|
<div class="button-card-radio__radio-icon"></div>
|
|
<input class="hidden" type="radio" name="formatSelect" value="image/jpeg">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="button-card-radio" onclick="setConvertMethod('image/png')">
|
|
<div class="button-card-radio__content">
|
|
<div class="flex gap-sm w-100">
|
|
<label class="button-card-radio__label">PNG</label>
|
|
<div class="button-card-radio__radio-input">
|
|
<div class="button-card-radio__radio-icon"></div>
|
|
<input class="hidden" type="radio" name="formatSelect" value="image/png">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
<div class="button-card-radio" onclick="setConvertMethod('image/webp')">
|
|
<div class="button-card-radio__content">
|
|
<div class="flex gap-sm w-100">
|
|
<label class="button-card-radio__label">WebP</label>
|
|
<div class="button-card-radio__radio-input">
|
|
<div class="button-card-radio__radio-icon"></div>
|
|
<input class="hidden" type="radio" name="formatSelect" value="image/webp">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div id="outputDownloadContainer" class="image-output-container" data-count="0" data-elevation="2">
|
|
|
|
<div class="image-subpage-title-container">
|
|
<div class="image-subpage-title-content">
|
|
<div class="image-subpage-title__text">
|
|
<h2 class="h4">Images</h2>
|
|
<div class="image-subpage-title-description">Optimized images ready for review and download.</div>
|
|
</div>
|
|
<div class="image-output__actions-container">
|
|
<button id="deleteAllImagesButton" class="button-cta button-secondary" onclick="deleteAllImages()">
|
|
<svg height="16" stroke-linejoin="round" viewBox="0 0 16 16" width="16" style="color: currentcolor;"><path fill-rule="evenodd" clip-rule="evenodd" d="M6.75 2.75C6.75 2.05964 7.30964 1.5 8 1.5C8.69036 1.5 9.25 2.05964 9.25 2.75V3H6.75V2.75ZM5.25 3V2.75C5.25 1.23122 6.48122 0 8 0C9.51878 0 10.75 1.23122 10.75 2.75V3H12.9201H14.25H15V4.5H14.25H13.8846L13.1776 13.6917C13.0774 14.9942 11.9913 16 10.6849 16H5.31508C4.00874 16 2.92263 14.9942 2.82244 13.6917L2.11538 4.5H1.75H1V3H1.75H3.07988H5.25ZM4.31802 13.5767L3.61982 4.5H12.3802L11.682 13.5767C11.6419 14.0977 11.2075 14.5 10.6849 14.5H5.31508C4.79254 14.5 4.3581 14.0977 4.31802 13.5767Z" fill="currentColor"></path></svg>
|
|
<span>Delete all</span>
|
|
</button>
|
|
<button id="downloadAllImagesButton" class="button-cta button-primary" onclick="downloadAllImages()" aria-busy="false">
|
|
<span class="flex aria-busy:visible">
|
|
<svg width="16" height="16" style="fill: currentcolor;" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>.spinner_z9k8{transform-origin:center;animation:spinner_StKS .75s infinite linear}@keyframes spinner_StKS{100%{transform:rotate(360deg)}}</style><path d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z" opacity=".25"/><path d="M12,4a8,8,0,0,1,7.89,6.7A1.53,1.53,0,0,0,21.38,12h0a1.5,1.5,0,0,0,1.48-1.75,11,11,0,0,0-21.72,0A1.5,1.5,0,0,0,2.62,12h0a1.53,1.53,0,0,0,1.49-1.3A8,8,0,0,1,12,4Z" class="spinner_z9k8"/></svg></span>
|
|
<span class="flex aria-busy:hidden">
|
|
<svg height="16" stroke-linejoin="round" viewBox="0 0 16 16" width="16" style="color: currentcolor;"><path fill-rule="evenodd" clip-rule="evenodd" d="M8.75 1V1.75V8.68934L10.7197 6.71967L11.25 6.18934L12.3107 7.25L11.7803 7.78033L8.70711 10.8536C8.31658 11.2441 7.68342 11.2441 7.29289 10.8536L4.21967 7.78033L3.68934 7.25L4.75 6.18934L5.28033 6.71967L7.25 8.68934V1.75V1H8.75ZM13.5 9.25V13.5H2.5V9.25V8.5H1V9.25V14C1 14.5523 1.44771 15 2 15H14C14.5523 15 15 14.5523 15 14V9.25V8.5H13.5V9.25Z" fill="currentColor"></path></svg>
|
|
</span>
|
|
<span>Download all</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="image-output__empty-state">
|
|
<div class="flex flex-col justify-center items-center gap-xs pt-3xl">
|
|
<svg width="64" height="64" style="stroke: currentcolor" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path stroke-linejoin="round" stroke-linecap="round" stroke-width="1.6" d="M3.00005 17.0001C3 16.9355 3 16.8689 3 16.8002V7.2002C3 6.08009 3 5.51962 3.21799 5.0918C3.40973 4.71547 3.71547 4.40973 4.0918 4.21799C4.51962 4 5.08009 4 6.2002 4H17.8002C18.9203 4 19.4801 4 19.9079 4.21799C20.2842 4.40973 20.5905 4.71547 20.7822 5.0918C21 5.5192 21 6.07899 21 7.19691V16.8031C21 17.2881 21 17.6679 20.9822 17.9774M3.00005 17.0001C3.00082 17.9884 3.01337 18.5058 3.21799 18.9074C3.40973 19.2837 3.71547 19.5905 4.0918 19.7822C4.5192 20 5.07899 20 6.19691 20H17.8036C18.9215 20 19.4805 20 19.9079 19.7822C20.2842 19.5905 20.5905 19.2837 20.7822 18.9074C20.9055 18.6654 20.959 18.3813 20.9822 17.9774M3.00005 17.0001L7.76798 11.4375L7.76939 11.436C8.19227 10.9426 8.40406 10.6955 8.65527 10.6064C8.87594 10.5282 9.11686 10.53 9.33643 10.6113C9.58664 10.704 9.79506 10.9539 10.2119 11.4541L12.8831 14.6595C13.269 15.1226 13.463 15.3554 13.6986 15.4489C13.9065 15.5313 14.1357 15.5406 14.3501 15.4773C14.5942 15.4053 14.8091 15.1904 15.2388 14.7607L15.7358 14.2637C16.1733 13.8262 16.3921 13.6076 16.6397 13.5361C16.8571 13.4734 17.0896 13.4869 17.2988 13.5732C17.537 13.6716 17.7302 13.9124 18.1167 14.3955L20.9822 17.9774M20.9822 17.9774L21 17.9996M15 10C14.4477 10 14 9.55228 14 9C14 8.44772 14.4477 8 15 8C15.5523 8 16 8.44772 16 9C16 9.55228 15.5523 10 15 10Z"></path></svg> <div>
|
|
<h3 class="h5">No image optimized yet</h3>
|
|
<p>Upload images above to optimize them. Once compressed, they'll appear here.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="outputDownloadContent" class="image-output-content"></div>
|
|
</div>
|
|
|
|
</div>
|
|
</section>
|
|
|
|
|
|
</div>
|
|
|
|
<dialog id="installPWADialog" data-elevation="2">
|
|
<h2 class="h4">Install MAZANOKE</h2>
|
|
|
|
<div class="pl-sm pr-sm mt-md mb-md text-md" style="max-width: 360px;">
|
|
<div class="mb-sm">
|
|
<div class="flex gap-2xs">
|
|
<svg height="24" width="24" stroke-linejoin="round" viewBox="0 0 16 16"style="color: currentcolor;"><path fill-rule="evenodd" clip-rule="evenodd" d="M8 0.154663L8.34601 0.334591L14.596 3.58459L15 3.79466V4.25V11.75V12.2053L14.596 12.4154L8.34601 15.6654L8 15.8453L7.65399 15.6654L1.40399 12.4154L1 12.2053V11.75V4.25V3.79466L1.40399 3.58459L7.65399 0.334591L8 0.154663ZM2.5 11.2947V5.44058L7.25 7.81559V13.7647L2.5 11.2947ZM8.75 13.7647L13.5 11.2947V5.44056L8.75 7.81556V13.7647ZM8 1.84534L12.5766 4.22519L7.99998 6.51352L3.42335 4.2252L8 1.84534Z" fill="currentColor"></path></svg>
|
|
<p>An app shortcut is added to your device.</p>
|
|
</div>
|
|
</div>
|
|
<div class="mb-sm">
|
|
<div class="flex gap-2xs">
|
|
<svg height="24" width="24" stroke-linejoin="round" viewBox="0 0 16 16" style="color: currentcolor;"><path fill-rule="evenodd" clip-rule="evenodd" d="M1 2.5C7.90356 2.5 13.5 8.09644 13.5 15H15C15 7.26801 8.73199 1 1 1V2.5ZM8 15C8 11.134 4.86599 8 1 8V6.5C5.69442 6.5 9.5 10.3056 9.5 15H8ZM2.5 15C2.5 14.1716 1.82843 13.5 1 13.5V12C2.65685 12 4 13.3431 4 15H2.5Z" fill="currentColor"></path></svg>
|
|
<p>Use even without an internet connection.</p>
|
|
</div>
|
|
</div>
|
|
<div class="mb-sm">
|
|
<div class="flex gap-2xs">
|
|
<svg height="24" width="24" stroke-linejoin="round" viewBox="0 0 16 16" style="color: currentcolor;"><path fill-rule="evenodd" clip-rule="evenodd" d="M10 4.5V6H6V4.5C6 3.39543 6.89543 2.5 8 2.5C9.10457 2.5 10 3.39543 10 4.5ZM4.5 6V4.5C4.5 2.567 6.067 1 8 1C9.933 1 11.5 2.567 11.5 4.5V6H12.5H14V7.5V12.5C14 13.8807 12.8807 15 11.5 15H4.5C3.11929 15 2 13.8807 2 12.5V7.5V6H3.5H4.5ZM11.5 7.5H10H6H4.5H3.5V12.5C3.5 13.0523 3.94772 13.5 4.5 13.5H11.5C12.0523 13.5 12.5 13.0523 12.5 12.5V7.5H11.5Z" fill="currentColor"></path></svg>
|
|
<p>Whether on the web or app, your images are always processed privately on-device.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="button-group button-group--col justify-end mt-md">
|
|
<button class="button-cta button-secondary minw-auto" onclick="installPWADialog.close()">
|
|
Cancel
|
|
</button>
|
|
<button id="installPWA" class="button-cta button-primary">
|
|
<svg height="16" stroke-linejoin="round" viewBox="0 0 16 16" width="16" style="color: currentcolor;"><path fill-rule="evenodd" clip-rule="evenodd" d="M8.75 5.25V4.5H7.25V5.25V9.43934L5.78033 7.96967L5.25 7.43934L4.18934 8.5L4.71967 9.03033L7.46967 11.7803C7.76256 12.0732 8.23744 12.0732 8.53033 11.7803L11.2803 9.03033L11.8107 8.5L10.75 7.43934L10.2197 7.96967L8.75 9.43934V5.25ZM1.5 8C1.5 4.41015 4.41015 1.5 8 1.5C11.5899 1.5 14.5 4.41015 14.5 8C14.5 11.5899 11.5899 14.5 8 14.5C4.41015 14.5 1.5 11.5899 1.5 8ZM8 0C3.58172 0 0 3.58172 0 8C0 12.4183 3.58172 16 8 16C12.4183 16 16 12.4183 16 8C16 3.58172 12.4183 0 8 0Z" fill="currentColor"></path></svg>
|
|
<span>Install</span>
|
|
</button>
|
|
</div>
|
|
</dialog>
|
|
|
|
<div id="updateToast" class="toast hidden" data-elevation="3">
|
|
<div class="toast-content">
|
|
<div class="toast-title">Update available</div>
|
|
<div class="toast-message">Refresh the page to update.</div>
|
|
</div>
|
|
<div class="toast-actions">
|
|
<button class="button-cta button-secondary minw-auto" onclick="updateToast.classList.add('hidden')">Ignore</button>
|
|
<button id="updateToastRefreshButton" class="button-cta button-secondary minw-auto">Refresh</button>
|
|
</div>
|
|
</div>
|
|
|
|
<footer>
|
|
<a class="footer-text" href="https://github.com/civilblur/mazanoke" target="_blank" rel="noopener noreferrer">
|
|
<span>created by civilblur</span>
|
|
<!-- app-version-placeholder -->
|
|
</a>
|
|
</footer>
|
|
|
|
<button id="backToTop">
|
|
<svg height="16" stroke-linejoin="round" style="color:currentColor" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" clip-rule="evenodd" d="M8.70711 1.39644C8.31659 1.00592 7.68342 1.00592 7.2929 1.39644L2.21968 6.46966L1.68935 6.99999L2.75001 8.06065L3.28034 7.53032L7.25001 3.56065V14.25V15H8.75001V14.25V3.56065L12.7197 7.53032L13.25 8.06065L14.3107 6.99999L13.7803 6.46966L8.70711 1.39644Z" fill="currentColor"></path></svg>
|
|
</button>
|
|
|
|
<script type="text/javascript" src="./assets/vendor/browser-image-compression.js"></script>
|
|
<script type="text/javascript" src="./assets/vendor/heic-to.js"></script>
|
|
<script type="text/javascript" src="./assets/vendor/jszip.js"></script>
|
|
<script type="text/javascript" src="./assets/js/global.js"></script>
|
|
<script type="text/javascript" src="./assets/js/utilities.js"></script>
|
|
<script type="text/javascript" src="./assets/js/helpers.js"></script>
|
|
<script type="text/javascript" src="./assets/js/ui.js"></script>
|
|
<script type="text/javascript" src="./assets/js/compression.js"></script>
|
|
<script type="text/javascript" src="./assets/js/download.js"></script>
|
|
<script type="text/javascript" src="./assets/js/events.js"></script>
|
|
<script>
|
|
if ('serviceWorker' in navigator) {
|
|
function promptUpdate(registration) {
|
|
updateToast.classList.remove('hidden');
|
|
updateToastRefreshButton.addEventListener('click', () => {
|
|
if (registration.waiting) {
|
|
registration.waiting.postMessage('SKIP_WAITING')
|
|
}
|
|
})
|
|
}
|
|
|
|
if ('serviceWorker' in navigator) {
|
|
window.addEventListener('load', async () => {
|
|
const registration = await navigator.serviceWorker.register('/service-worker.js')
|
|
|
|
if (registration.waiting) {
|
|
promptUpdate(registration)
|
|
}
|
|
|
|
registration.addEventListener('updatefound', () => {
|
|
if (registration.installing) {
|
|
registration.installing.addEventListener('statechange', () => {
|
|
if (registration.waiting) {
|
|
if (navigator.serviceWorker.controller) {
|
|
promptUpdate(registration)
|
|
} else {
|
|
console.log('Service Worker initialized')
|
|
}
|
|
}
|
|
})
|
|
}
|
|
})
|
|
|
|
let refreshing = false;
|
|
|
|
navigator.serviceWorker.addEventListener('controllerchange', () => {
|
|
if (!refreshing) {
|
|
window.location.reload()
|
|
refreshing = true
|
|
}
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
|
|
let deferredPrompt;
|
|
window.addEventListener('beforeinstallprompt', (e) => {
|
|
e.preventDefault();
|
|
deferredPrompt = e;
|
|
document.getElementById('installPWAPrompt').classList.remove('hidden');
|
|
});
|
|
|
|
document.getElementById('installPWA').addEventListener('click', async () => {
|
|
if (deferredPrompt) {
|
|
deferredPrompt.prompt();
|
|
await deferredPrompt.userChoice;
|
|
deferredPrompt = null;
|
|
}
|
|
});
|
|
|
|
window.addEventListener('appinstalled', (event) => {
|
|
document.getElementById('installPWAPrompt').classList.add('hidden');
|
|
installPWADialog.close();
|
|
});
|
|
</script>
|
|
</body>
|
|
|
|
</html> |