about summary refs log tree commit diff stats
path: root/js/leibovitz/index.html
diff options
context:
space:
mode:
Diffstat (limited to 'js/leibovitz/index.html')
-rw-r--r--js/leibovitz/index.html548
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>