Skip to content

Instantly share code, notes, and snippets.

@ekanna
Forked from ianobermiller/index.html
Created July 14, 2021 18:49
Show Gist options
  • Select an option

  • Save ekanna/a45d85dd6794e60b41c21bde497545c5 to your computer and use it in GitHub Desktop.

Select an option

Save ekanna/a45d85dd6794e60b41c21bde497545c5 to your computer and use it in GitHub Desktop.
WiFi QR Code, single HTML file
<!DOCTYPE html>
<html>
<head>
<title>WiFi Login</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- https://news.ycombinator.com/item?id=26923316 -->
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>&#128272;</text></svg>">
<style>
body, textarea {
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
font-size: 16px;
}
main {
margin: 0 auto;
max-width: 400px;
text-align: center;
}
canvas {
height: 200px;
width: 200px;
}
label {
display: block;
margin: 12px;
text-align: left;
}
textarea {
box-sizing: border-box;
font-family: Menlo, Consola, monospace;
font-weight: bold;
margin-top: 4px;
padding: 8px;
width: 100%;
}
textarea:invalid {
border: 2px dashed red;
}
/* https://css-tricks.com/the-cleanest-trick-for-autogrowing-textareas/ */
@supports (display: grid) {
.input {
/* easy way to plop the elements on top of each other and have them both sized based on the tallest one's height */
display: grid;
}
.input::after {
/* Note the weird space! Needed to preventy jumpy behavior */
content: attr(data-replicated-value) " ";
/* This is how textarea text behaves */
white-space: pre-wrap;
/* Hidden from view, clicks, and screen readers */
visibility: hidden;
}
.input > textarea {
/* You could leave this, but after a user resizes, then it ruins the auto sizing */
resize: none;
/* Firefox shows scrollbar on growth, you can hide like this. */
overflow: hidden;
}
.input > textarea,
.input::after {
/* Identical styling required!! */
border: 1px solid black;
border-radius: 4px;
box-sizing: border-box;
margin-top: 4px;
padding: 8px;
font: inherit;
/* Place on top of each other */
grid-area: 1 / 1 / 2 / 2;
}
}
</style>
</head>
<body>
<main>
<h1>WiFi Login</h1>
<p>Scan with your phone to join automatically!</p>
<canvas id="qr"></canvas>
<label>
Network Name
<div class="input">
<textarea
id="ssid"
type="text"
rows="1"
autocapitalize="off"
autocorrect="off"
autofocus="true"></textarea>
</div>
</label>
<label>
Password
<div class="input">
<textarea
id="password"
type="text"
minlength="8"
rows="1"
autocapitalize="off"
autocorrect="off"
title="Must be at least 8 characters."></textarea>
</div>
</label>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrious/4.0.2/qrious.min.js"></script>
<script>
(function() {
var qr = new QRious({
element: document.getElementById('qr'),
value: 'https://github.com/neocotic/qrious',
size: 400 // twice the size for extra crispiness
});
var ssidInput = document.getElementById('ssid');
var passwordInput = document.getElementById('password');
// https://github.com/bndw/wifi-card/blob/master/src/components/Card.js
var needsEscape = {'"': true, ';': true, ',': true, ':': true, '\\': true};
function escape(v) {
return v.split('').map(c => needsEscape[c] ? '\\' + c : c).join('');
}
function updateQRCode() {
this.parentNode.dataset.replicatedValue = this.value;
var ssid = escape(ssidInput.value);
var password = escape(passwordInput.value);
qr.value = 'WIFI:T:WPA;S:' + ssid + ';P:' + password + ';;';
}
ssidInput.addEventListener('input', updateQRCode);
passwordInput.addEventListener('input', updateQRCode);
})();
</script>
</body>
</html>
@sennoalvinn-afk
Copy link
Copy Markdown

<title>Wifi Scanner Simulasi</title> <style> /* --- CSS Styles --- */ :root { --primary: #00ff88; --bg: #0f172a; --card-bg: #1e293b; --text: #f1f5f9; --text-muted: #94a3b8; }
    * { box-sizing: border-box; margin: 0; padding: 0; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; }

    body {
        background-color: var(--bg);
        color: var(--text);
        padding: 20px;
        display: flex;
        flex-direction: column;
        align-items: center;
    }

    .container {
        max-width: 600px;
        width: 100%;
    }

    header {
        text-align: center;
        margin-bottom: 30px;
    }

    h1 {
        color: var(--primary);
        font-size: 2rem;
        margin-bottom: 10px;
    }

    p.subtitle {
        color: var(--text-muted);
        font-size: 0.9rem;
    }

    /* Search Bar */
    .search-box {
        margin-bottom: 20px;
        position: relative;
    }

    input[type="text"] {
        width: 100%;
        padding: 12px 20px;
        border-radius: 25px;
        border: 1px solid #334155;
        background-color: var(--card-bg);
        color: var(--text);
        font-size: 1rem;
        outline: none;
    }

    input[type="text"]:focus {
        border-color: var(--primary);
        box-shadow: 0 0 8px rgba(0, 255, 136, 0.2);
    }

    /* Network List */
    .network-list {
        display: flex;
        flex-direction: column;
        gap: 15px;
    }

    .card {
        background-color: var(--card-bg);
        padding: 20px;
        border-radius: 15px;
        border: 1px solid #334155;
        transition: transform 0.2s;
        display: flex;
        justify-content: space-between;
        align-items: center;
    }

    .card:hover {
        transform: translateY(-3px);
        border-color: var(--primary);
    }

    .card-left {
        flex: 1;
    }

    .wifi-name {
        font-weight: bold;
        font-size: 1.1rem;
        margin-bottom: 5px;
        display: flex;
        align-items: center;
        gap: 8px;
    }

    .badge {
        font-size: 0.7rem;
        padding: 2px 8px;
        border-radius: 4px;
        background-color: #334155;
        color: var(--text-muted);
    }

    .wifi-details {
        font-size: 0.85rem;
        color: var(--text-muted);
        display: flex;
        gap: 15px;
        margin-bottom: 8px;
    }

    /* Signal Bar */
    .signal-bar-container {
        width: 100px;
        height: 4px;
        background-color: #334155;
        border-radius: 2px;
        overflow: hidden;
    }

    .signal-fill {
        height: 100%;
        border-radius: 2px;
        transition: width 0.5s ease;
    }

    /* Password Section */
    .card-right {
        text-align: right;
    }

    .password-box {
        background-color: #0f172a;
        padding: 8px 12px;
        border-radius: 8px;
        font-family: monospace;
        color: var(--primary);
        margin-bottom: 5px;
        cursor: pointer;
        display: inline-block;
        font-size: 0.9rem;
    }

    .password-box:active {
        transform: scale(0.95);
    }

    .btn-copy {
        background: none;
        border: none;
        color: var(--text-muted);
        font-size: 0.75rem;
        cursor: pointer;
        text-decoration: underline;
    }

    .btn-copy:hover {
        color: var(--text);
    }

    .empty-state {
        text-align: center;
        padding: 20px;
        color: var(--text-muted);
    }
    
    /* Lock Icon for Protected */
    .locked { color: #fbbf24; }
</style>
<div class="container">
    <header>
        <h1>Wi-Fi Detector</h1>
        <p class="subtitle">Menampilkan jaringan terdekat (Data Simulasi)</p>
    </header>

    <div class="search-box">
        <input type="text" id="searchInput" placeholder="Cari nama wifi..." onkeyup="filterWifi()">
    </div>

    <div id="networkContainer" class="network-list">
        <!-- Menu akan dihasilkan oleh Javascript -->
    </div>
</div>

<script>
    /* --- JavaScript Logic --- */

    // 1. Data Dummy (Simulasi)
    const wifiData = [
        { ssid: "Wifi_Kantor_01", password: "KantorBes2024!", signal: 95, type: "WPA2" },
        { ssid: "RonaldHome_5G", password: "ronald123", signal: 88, type: "WPA2" },
        { ssid: "IndieCoffee_Shop", password: "kopi勾苓!9", signal: 40, type: "WPA2" },
        { ssid: "TPLINK_8832", password: "password", signal: 30, type: "Open" },
        { ssid: "师范附属小学", password: "sekolah123", signal: 65, type: "WPA2" },
        { ssid: "Starbucks_Free", password: "Netsuke_B", signal: 20, type: "WPA2" },
        { ssid: "DevNetwork_X", password: "codingaja", signal: 92, type: "WPA3" }
    ];

    const container = document.getElementById('networkContainer');
    const searchInput = document.getElementById('searchInput');

    // 2. Fungsi untuk menentukan warna sinyal
    function getSignalColor(level) {
        if (level > 75) return '#00ff88'; // Hijau (Kuat)
        if (level > 40) return '#fbbf24'; // Kuning (Sedang)
        return '#ef4444'; // Merah (Lemah)
    }

    // 3. Fungsi Render Data ke HTML
    function renderList(data) {
        container.innerHTML = ''; // Clear current list

        if (data.length === 0) {
            container.innerHTML = '<div class="empty-state">Jaringan tidak ditemukan</div>';
            return;
        }

        data.forEach(wifi => {
            const signalColor = getSignalColor(wifi.signal);
            const isLocked = wifi.type !== 'Open' ? '🔒' : '🔓';
            
            const html = `
                <div class="card">
                    <div class="card-left">
                        <div class="wifi-name">
                            ${isLocked} ${wifi.ssid}
                            <span class="badge">${wifi.type}</span>
                        </div>
                        <div class="wifi-details">
                            <span>Sinyal: ${wifi.signal}%</span>
                        </div>
                        <div class="signal-bar-container">
                            <div class="signal-fill" style="width: ${wifi.signal}%; background-color: ${signalColor}"></div>
                        </div>
                    </div>
                    <div class="card-right">
                        <div class="password-box" onclick="copyPassword('${wifi.password}')">
                            ${wifi.password}
                        </div>
                        <br>
                        <button class="btn-copy" onclick="copyPassword('${wifi.password}')">Klik Sandi untuk Salin</button>
                    </div>
                </div>
            `;
            container.innerHTML += html;
        });
    }

    // 4. Fungsi Filter (Cari)
    function filterWifi() {
        const query = searchInput.value.toLowerCase();
        const filtered = wifiData.filter(wifi => wifi.ssid.toLowerCase().includes(query));
        renderList(filtered);
    }

    // 5. Fungsi Copy ke Clipboard
    function copyPassword(pass) {
        navigator.clipboard.writeText(pass).then(() => {
            //alert("Sandi '" + pass + "' telah disalin!"); // Alert bisa diganti toast notification
            const originalText = event.target.innerText;
            
            // Feedback visual sederhana
            if(event.target.tagName === 'DIV') {
                event.target.innerText = "Tersalin!";
                setTimeout(() => {
                    event.target.innerText = pass;
                }, 1000);
            } else if (event.target.tagName === 'BUTTON') {
                let btn = event.target;
                btn.innerText = "Tersalin!";
                btn.style.color = "#00ff88";
                setTimeout(() => {
                    btn.innerText = "Klik Sandi untuk Salin";
                    btn.style.color = "#94a3b8";
                }, 1000);
            }
        });
    }

    // Initial Render
    renderList(wifiData);

</script>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment