diff options
Diffstat (limited to 'js/leibovitz/index.html')
-rw-r--r-- | js/leibovitz/index.html | 548 |
1 files changed, 548 insertions, 0 deletions
diff --git a/js/leibovitz/index.html b/js/leibovitz/index.html new file mode 100644 index 0000000..6207f07 --- /dev/null +++ b/js/leibovitz/index.html @@ -0,0 +1,548 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Leibovitz</title> + <meta name="description" content="Leibovitz is a web-based camera that lets you make fun photos."> + <link rel="apple-touch-icon" sizes="57x57" href="apple-icon-57x57.png"> + <link rel="apple-touch-icon" sizes="60x60" href="apple-icon-60x60.png"> + <link rel="apple-touch-icon" sizes="72x72" href="apple-icon-72x72.png"> + <link rel="apple-touch-icon" sizes="76x76" href="apple-icon-76x76.png"> + <link rel="apple-touch-icon" sizes="114x114" href="apple-icon-114x114.png"> + <link rel="apple-touch-icon" sizes="120x120" href="apple-icon-120x120.png"> + <link rel="apple-touch-icon" sizes="144x144" href="apple-icon-144x144.png"> + <link rel="apple-touch-icon" sizes="152x152" href="apple-icon-152x152.png"> + <link rel="apple-touch-icon" sizes="180x180" href="apple-icon-180x180.png"> + <link rel="icon" type="image/png" sizes="192x192" href="android-icon-192x192.png"> + <link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png"> + <link rel="icon" type="image/png" sizes="96x96" href="favicon-96x96.png"> + <link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png"> + <link rel="manifest" href="manifest.json"> + <meta name="msapplication-TileColor" content="#ffffff"> + <meta name="msapplication-TileImage" content="ms-icon-144x144.png"> + <meta name="theme-color" content="#ffffff"> + <style> + @font-face { + font-family: 'ChicagoFLF'; + src: url('./ChicagoFLF.ttf') format('truetype'); + font-weight: normal; + font-style: normal; + } + body, html { + margin: 0; + padding: 0; + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + background-color: beige; + overflow: hidden; + font-family: 'ChicagoFLF', sans-serif; + font-size: 16px; + } + .preview-container { + flex: 1; + position: relative; + margin: 0; + min-height: 0; + padding-top: 28px; + } + #canvas { + width: 100%; + height: 100%; + object-fit: contain; + display: none; + background-color: transparent; + border-radius: 8px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + } + .slide-controls { + position: absolute; + bottom: 20px; + left: 0; + right: 0; + display: none; + justify-content: space-around; + align-items: center; + background-color: rgba(255, 255, 255, 0.8); + padding: 10px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + z-index: 10; + margin: 0 20px; + } + .slide-controls.visible { + display: flex; + } + .slider-group { + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; + flex: 1; + max-width: 200px; + } + .slider-group label { + font-size: 12px; + color: #666; + font-family: 'ChicagoFLF', sans-serif; + } + .slider-group input[type="range"] { + width: 100%; + height: 4px; + -webkit-appearance: none; + background: rgba(0, 0, 0, 0.2); + border-radius: 2px; + outline: none; + } + .slider-group input[type="range"]::-webkit-slider-thumb { + -webkit-appearance: none; + width: 16px; + height: 16px; + background: teal; + border-radius: 0; + cursor: pointer; + } + .slider-group input[type="range"]::-moz-range-thumb { + width: 16px; + height: 16px; + background: teal; + border-radius: 0; + cursor: pointer; + border: none; + } + .slider-group .value { + font-size: 12px; + color: #666; + font-family: 'ChicagoFLF', sans-serif; + } + .side-control { + position: absolute; + top: 50%; + height: 100%; + width: auto; + transform: translateY(-50%); + display: flex; + flex-direction: column; + align-items: center; + gap: 1em; + padding: 1em 0; + background-color: rgba(255, 255, 255, 0.8); + z-index: 10; + } + .side-control.left { + left: 0; + } + .side-control.right { + right: 0; + } + + .vertical-slider { + transform: rotate(-90deg); + width: 200px; + margin: 90px -80px; + cursor: pointer; + } + input[type="range"]::-webkit-slider-thumb, .vertical-slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 20px; + height: 20px; + background: teal; + border-radius: 0; + cursor: pointer; + } + input[type="range"]::-webkit-slider-runnable-track, .vertical-slider::-webkit-slider-runnable-track { + width: 100%; + height: 4px; + background: rgba(255, 255, 255, 0.8); + border-radius: 2px; + } + input[type="range"]::-moz-range-thumb, .vertical-slider::-moz-range-thumb { + width: 20px; + height: 20px; + background: teal; + border-radius: 0; + cursor: pointer; + border: none; + } + input[type="range"]::-moz-range-track, .vertical-slider::-moz-range-track { + width: 100%; + height: 4px; + background: rgba(255, 255, 255, 0.8); + border-radius: 2px; + } + .vertical-label { + transform: rotate(90deg); + font-size: 12px; + color: #666; + user-select: none; + white-space: nowrap; + margin: 20px 0; + font-family: 'ChicagoFLF', sans-serif; + padding: 0 5px; + } + #blur-value { + font-size: 12px; + color: #666; + user-select: none; + white-space: nowrap; + font-family: 'ChicagoFLF', sans-serif; + padding: 0 5px; + } + #contrast-value { + font-size: 12px; + color: #666; + user-select: none; + white-space: nowrap; + font-family: 'ChicagoFLF', sans-serif; + padding: 0 5px; + } + #controls { + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + gap: 0; + padding: 0; + background-color: rgba(255, 255, 255, 0.8); + box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.1); + flex-shrink: 0; + } + #settings-container { + width: 100%; + display: none; + flex-direction: column; + gap: 10px; + align-items: center; + background-color: rgba(255, 255, 255, 0.8); + padding: 10px; + box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.1); + flex-shrink: 0; + position: relative; + } + #settings-container.visible { + display: flex; + } + #offline-status { + position: fixed; + top: 0; + left: 0; + right: 0; + width: 100%; + font-size: 12px; + color: #666; + display: none; + font-family: 'ChicagoFLF', sans-serif; + background-color: rgba(255, 255, 255, 0.8); + text-align: center; + padding: 4px; + z-index: 1000; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + } + #offline-status.visible { + display: block; + } + .top-controls { + display: flex; + gap: 10px; + align-items: center; + width: 100%; + justify-content: center; + flex-wrap: nowrap; + } + .color-controls { + display: flex; + gap: 5px; + align-items: center; + flex-shrink: 0; + } + #reset-color { + padding: 8px; + font-size: 18px; + background: none; + border: 1px solid #ccc; + border-radius: 4px; + cursor: pointer; + transition: all 0.2s ease; + font-family: 'ChicagoFLF', sans-serif; + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + } + input[type="color"] { + width: 40px; + height: 40px; + padding: 0; + border: 1px solid #ccc; + border-radius: 4px; + cursor: pointer; + } + select { + padding: 10px 15px; + font-family: 'ChicagoFLF', sans-serif; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: #f0f0f0; + border: 1px solid #ccc; + border-radius: 4px; + cursor: pointer; + font-size: 16px; + line-height: 1.2; + color: #333; + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' stroke='%23333'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 10px center; + background-size: 16px; + padding-right: 35px; + } + select:focus { + outline: none; + border-color: teal; + box-shadow: 0 0 0 2px rgba(0, 128, 128, 0.2); + } + #dither-select { + min-width: 200px; + max-width: none; + flex-shrink: 0; + } + button.capture { + background-color: teal; + color: #FFFFFF; + padding: 10px 20px; + font-family: 'ChicagoFLF', sans-serif; + } + #toggle-camera { + padding: 10px 20px; + font-family: 'ChicagoFLF', sans-serif; + border-right: 1px solid rgba(0, 0, 0, 0.1); + } + #toggle-camera.hidden { + display: none; + } + button, select, input[type="color"] { + padding: 10px; + font-size: 18px; + cursor: pointer; + font-family: 'ChicagoFLF', sans-serif; + } + + button:hover, button:focus { + outline: none; + } + + button.capture:disabled { + background-color: #ccc; + color: #5c5c5c; + } + + .contrast-control { + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; + width: 100%; + max-width: 300px; + margin: 0 auto; + padding: 0 10px; + } + .contrast-control label { + font-size: 12px; + color: #666; + width: 100%; + text-align: center; + font-family: 'ChicagoFLF', sans-serif; + } + .contrast-control input[type="range"] { + width: 100%; + } + .contrast-control .slider-container { + display: flex; + align-items: center; + gap: 10px; + width: 100%; + } + .contrast-control .slider-container input[type="range"] { + flex: 1; + } + #block-size-value { + font-size: 12px; + color: #666; + min-width: 40px; + text-align: right; + font-family: 'ChicagoFLF', sans-serif; + } + #focus-container { + display: none; + } + #toggle-camera, button.capture, #edit-image { + font-size: 18px; + padding: 20px; + font-family: 'ChicagoFLF', sans-serif; + border-radius: 0; + text-align: center; + flex: 1; + } + button.capture:disabled { + background-color: #ccc; + color: #5c5c5c; + } + + @media (max-width: 600px) { + #controls { + flex-direction: column; + } + + #toggle-camera, button.capture, #edit-image { + width: 100%; + border-right: none; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + } + + #toggle-camera:last-child, button.capture:last-child, #edit-image:last-child { + border-bottom: none; + } + + .slide-controls { + flex-direction: column; + gap: 12px; + padding: 15px; + } + .slider-group { + width: 100%; + max-width: none; + flex-direction: row; + align-items: center; + gap: 12px; + } + .slider-group label { + min-width: 80px; + text-align: left; + font-size: 12px; + color: #666; + } + .slider-group input[type="range"] { + flex: 1; + } + .slider-group .value { + min-width: 40px; + text-align: right; + font-size: 12px; + color: #666; + } + select { + width: 100%; + max-width: 100%; + } + .top-controls { + flex-wrap: nowrap; + width: auto; + gap: 5px; + } + + #dither-select { + min-width: 150px; + max-width: none; + } + + .color-controls { + flex-shrink: 0; + } + } + #edit-image { + display: block; + padding: 20px; + font-family: 'ChicagoFLF', sans-serif; + border-radius: 0; + text-align: center; + background-color: teal; + color: #FFFFFF; + } + #edit-image.hidden { + display: none; + } + </style> +</head> +<body> + +<div class="preview-container"> + <canvas id="canvas"></canvas> + <div class="slide-controls"> + <div class="slider-group"> + <label for="blur-slider">Blur</label> + <input type="range" id="blur-slider" min="0" max="20" value="0" step="1"> + <span class="value" id="blur-value">0%</span> + </div> + <div class="slider-group"> + <label for="contrast-slider">Contrast</label> + <input type="range" id="contrast-slider" min="-255" max="255" value="0" step="1"> + <span class="value" id="contrast-value">0</span> + </div> + <div class="slider-group"> + <label for="balance-slider">Balance</label> + <input type="range" id="balance-slider" min="2000" max="10000" value="6500" step="100"> + <span class="value" id="balance-value">6500K</span> + </div> + <div class="slider-group" id="focus-control" style="display: none;"> + <label for="focus-slider">Focus</label> + <input type="range" id="focus-slider" min="0" max="100" step="1" value="50" disabled> + <span class="value" id="focus-value">50%</span> + </div> + <div class="slider-group" id="pixel-size-control" style="display: none;"> + <label for="block-size-slider">Pixel Size</label> + <input type="range" id="block-size-slider" min="1" max="12" value="4" step="1"> + <span class="value" id="block-size-value">4px</span> + </div> + </div> +</div> + +<div id="settings-container"> + <div id="offline-status">Offline Mode</div> + <div class="top-controls"> + <select id="dither-select"> + <option value="none">No Dithering</option> + <option value="floyd-steinberg">Floyd-Steinberg</option> + <option value="ordered">Ordered</option> + <option value="atkinson">Atkinson</option> + <option value="bayer">Bayer</option> + </select> + <div class="color-controls"> + <input type="color" id="color-tint" title="Color Tint"> + <button id="reset-color" title="Reset Color Tint">↺</button> + </div> + </div> +</div> + +<div id="controls"> + <button id="toggle-camera">Camera On</button> + <button id="edit-image" class="edit-image">Upload Image</button> + <button id="capture" disabled class="capture">Capture Image</button> + <input type="file" id="image-input" accept="image/*" style="display: none;"> +</div> + +<script src="dither.js"></script> +<script src="contrast.js"></script> +<script src="color.js"></script> +<script src="blur.js"></script> +<script src="balance.js"></script> +<script src="leibovitz.js"></script> +<script> +// Add offline status handling +window.addEventListener('online', () => { + document.getElementById('offline-status').classList.remove('visible'); +}); + +window.addEventListener('offline', () => { + document.getElementById('offline-status').classList.add('visible'); +}); + +// Check initial online status +if (!navigator.onLine) { + document.getElementById('offline-status').classList.add('visible'); +} +</script> +</body> +</html> |