<!DOCTYPE html>

<html lang="id">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Short URL - Blogger URL Shortener</title>

    <style>

        * {

            margin: 0;

            padding: 0;

            box-sizing: border-box;

        }


        body {

            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;

            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);

            min-height: 100vh;

            display: flex;

            align-items: center;

            justify-content: center;

            padding: 20px;

        }


        .container {

            background: white;

            border-radius: 20px;

            box-shadow: 0 20px 40px rgba(0,0,0,0.1);

            padding: 40px;

            width: 100%;

            max-width: 600px;

            position: relative;

            overflow: hidden;

        }


        .container::before {

            content: '';

            position: absolute;

            top: 0;

            left: 0;

            right: 0;

            height: 4px;

            background: linear-gradient(90deg, #667eea, #764ba2);

        }


        .header {

            text-align: center;

            margin-bottom: 30px;

        }


        .logo {

            font-size: 28px;

            font-weight: bold;

            color: #333;

            margin-bottom: 10px;

        }


        .subtitle {

            color: #666;

            font-size: 16px;

        }


        .input-group {

            margin-bottom: 20px;

        }


        .input-group label {

            display: block;

            margin-bottom: 8px;

            font-weight: 600;

            color: #333;

        }


        .input-group input {

            width: 100%;

            padding: 15px;

            border: 2px solid #e1e1e1;

            border-radius: 10px;

            font-size: 16px;

            transition: all 0.3s ease;

        }


        .input-group input:focus {

            outline: none;

            border-color: #667eea;

            box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);

        }


        .custom-alias {

            display: flex;

            gap: 10px;

            align-items: center;

        }


        .domain-prefix {

            background: #f8f9fa;

            padding: 15px;

            border: 2px solid #e1e1e1;

            border-radius: 10px;

            font-weight: 600;

            color: #666;

            white-space: nowrap;

        }


        .alias-input {

            flex: 1;

        }


        .btn {

            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);

            color: white;

            border: none;

            padding: 15px 30px;

            border-radius: 10px;

            font-size: 16px;

            font-weight: 600;

            cursor: pointer;

            transition: all 0.3s ease;

            width: 100%;

            margin-bottom: 20px;

        }


        .btn:hover {

            transform: translateY(-2px);

            box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);

        }


        .btn:active {

            transform: translateY(0);

        }


        .btn:disabled {

            background: #ccc;

            cursor: not-allowed;

            transform: none;

        }


        .result {

            background: #f8f9fa;

            border: 2px solid #e1e1e1;

            border-radius: 10px;

            padding: 20px;

            margin-bottom: 20px;

            display: none;

        }


        .result.show {

            display: block;

            animation: slideIn 0.3s ease;

        }


        @keyframes slideIn {

            from {

                opacity: 0;

                transform: translateY(20px);

            }

            to {

                opacity: 1;

                transform: translateY(0);

            }

        }


        .result-title {

            font-weight: 600;

            color: #333;

            margin-bottom: 10px;

        }


        .short-url {

            background: white;

            border: 2px solid #667eea;

            border-radius: 8px;

            padding: 12px;

            font-family: monospace;

            font-size: 16px;

            word-break: break-all;

            margin-bottom: 10px;

            color: #667eea;

            font-weight: 600;

        }


        .copy-btn {

            background: #28a745;

            color: white;

            border: none;

            padding: 10px 20px;

            border-radius: 6px;

            cursor: pointer;

            font-size: 14px;

            font-weight: 600;

            transition: background 0.3s ease;

        }


        .copy-btn:hover {

            background: #218838;

        }


        .copy-btn.copied {

            background: #17a2b8;

        }


        .stats {

            display: flex;

            gap: 20px;

            margin-top: 20px;

            flex-wrap: wrap;

        }


        .stat-item {

            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);

            color: white;

            padding: 15px;

            border-radius: 10px;

            text-align: center;

            flex: 1;

            min-width: 120px;

        }


        .stat-number {

            font-size: 24px;

            font-weight: bold;

            margin-bottom: 5px;

        }


        .stat-label {

            font-size: 12px;

            opacity: 0.8;

        }


        .url-list {

            margin-top: 30px;

        }


        .url-item {

            background: white;

            border: 2px solid #e1e1e1;

            border-radius: 10px;

            padding: 15px;

            margin-bottom: 10px;

            display: flex;

            justify-content: space-between;

            align-items: center;

            flex-wrap: wrap;

            gap: 10px;

        }


        .url-info {

            flex: 1;

            min-width: 200px;

        }


        .original-url {

            font-size: 14px;

            color: #666;

            margin-bottom: 5px;

            word-break: break-all;

        }


        .short-url-item {

            font-family: monospace;

            color: #667eea;

            font-weight: 600;

            font-size: 16px;

            cursor: pointer;

            text-decoration: underline;

        }


        .short-url-item:hover {

            color: #764ba2;

        }


        .url-actions {

            display: flex;

            gap: 10px;

            align-items: center;

        }


        .click-count {

            background: #f8f9fa;

            padding: 8px 12px;

            border-radius: 6px;

            font-size: 12px;

            font-weight: 600;

            color: #666;

        }


        .delete-btn {

            background: #dc3545;

            color: white;

            border: none;

            padding: 8px 12px;

            border-radius: 6px;

            cursor: pointer;

            font-size: 12px;

        }


        .delete-btn:hover {

            background: #c82333;

        }


        .error {

            background: #f8d7da;

            border: 2px solid #f5c6cb;

            color: #721c24;

            padding: 15px;

            border-radius: 10px;

            margin-bottom: 20px;

            display: none;

        }


        .error.show {

            display: block;

        }


        .redirect-container {

            display: none;

            text-align: center;

            padding: 40px;

        }


        .redirect-container.show {

            display: block;

        }


        .redirect-animation {

            margin-bottom: 20px;

        }


        .spinner {

            width: 40px;

            height: 40px;

            border: 4px solid #f3f3f3;

            border-top: 4px solid #667eea;

            border-radius: 50%;

            animation: spin 1s linear infinite;

            margin: 0 auto 20px;

        }


        @keyframes spin {

            0% { transform: rotate(0deg); }

            100% { transform: rotate(360deg); }

        }


        .redirect-info {

            color: #333;

            font-size: 16px;

            margin-bottom: 10px;

        }


        .redirect-url {

            color: #667eea;

            font-weight: 600;

            word-break: break-all;

            margin-bottom: 20px;

        }


        .redirect-timer {

            color: #666;

            font-size: 14px;

        }


        .manual-redirect {

            background: #667eea;

            color: white;

            border: none;

            padding: 10px 20px;

            border-radius: 6px;

            cursor: pointer;

            font-size: 14px;

            margin-top: 15px;

            text-decoration: none;

            display: inline-block;

        }


        .manual-redirect:hover {

            background: #764ba2;

        }


        .info-box {

            background: #e3f2fd;

            border: 2px solid #bbdefb;

            border-radius: 10px;

            padding: 20px;

            margin-bottom: 20px;

        }


        .info-title {

            font-weight: 600;

            color: #1976d2;

            margin-bottom: 10px;

        }


        .info-text {

            color: #0d47a1;

            line-height: 1.6;

        }


        @media (max-width: 600px) {

            .container {

                padding: 20px;

            }

            

            .custom-alias {

                flex-direction: column;

            }

            

            .domain-prefix {

                width: 100%;

                text-align: center;

            }

            

            .stats {

                flex-direction: column;

            }

            

            .url-item {

                flex-direction: column;

                align-items: flex-start;

            }

        }

    </style>

</head>

<body>

    <div class="container">

        <!-- Main Form -->

        <div id="mainForm">

            <div class="header">

                <div class="logo">🔗 Short URL</div>

                <div class="subtitle">Blogger URL Shortener - Buat link pendek untuk blog Anda</div>

            </div>


            <div class="info-box">

                <div class="info-title">📋 Cara Menggunakan:</div>

                <div class="info-text">

                    1. Masukkan URL yang ingin diperpendek<br>

                    2. (Opsional) Buat custom alias<br>

                    3. Klik "Perpendek URL"<br>

                    4. Copy dan bagikan URL pendek Anda<br>

                    5. Klik URL pendek untuk redirect otomatis

                </div>

            </div>


            <div class="error" id="error"></div>


            <div class="input-group">

                <label for="originalUrl">URL Asli:</label>

                <input type="url" id="originalUrl" placeholder="https://example.com/very-long-url-here">

            </div>


            <div class="input-group">

                <label for="customAlias">Custom Alias (Opsional):</label>

                <div class="custom-alias">

                    <div class="domain-prefix" id="domainPrefix">Loading...</div>

                    <input type="text" id="customAlias" class="alias-input" placeholder="custom-name">

                </div>

            </div>


            <button class="btn" id="shortenBtn">

                <span id="btnText">🚀 Perpendek URL</span>

            </button>


            <div class="result" id="result">

                <div class="result-title">URL Pendek Berhasil Dibuat!</div>

                <div class="short-url" id="shortUrl"></div>

                <button class="copy-btn" id="copyBtn">📋 Copy URL</button>

            </div>


            <div class="stats">

                <div class="stat-item">

                    <div class="stat-number" id="totalUrls">0</div>

                    <div class="stat-label">Total URL</div>

                </div>

                <div class="stat-item">

                    <div class="stat-number" id="totalClicks">0</div>

                    <div class="stat-label">Total Klik</div>

                </div>

                <div class="stat-item">

                    <div class="stat-number" id="todayUrls">0</div>

                    <div class="stat-label">Hari Ini</div>

                </div>

            </div>


            <div class="url-list" id="urlList">

                <h3 style="margin-bottom: 15px; color: #333;">📊 Daftar URL Terpendek</h3>

            </div>

        </div>


        <!-- Redirect Container -->

        <div class="redirect-container" id="redirectContainer">

            <div class="redirect-animation">

                <div class="spinner"></div>

                <div class="redirect-info">🔄 Mengarahkan ke...</div>

                <div class="redirect-url" id="redirectUrl"></div>

                <div class="redirect-timer" id="redirectTimer">Redirect dalam 3 detik...</div>

            </div>

            <a href="#" class="manual-redirect" id="manualRedirect">Klik di sini jika tidak redirect otomatis</a>

        </div>

    </div>


    <script>

        class URLShortener {

            constructor() {

                this.urls = JSON.parse(localStorage.getItem('shortUrls') || '[]');

                this.currentDomain = window.location.hostname || 'localhost';

                this.currentPath = window.location.pathname.replace(/\/[^\/]*$/, '');

                this.baseUrl = `${window.location.protocol}//${this.currentDomain}${this.currentPath}`;

                this.init();

            }


            init() {

                this.updateDomainDisplay();

                this.checkForRedirect();

                this.updateStats();

                this.renderUrlList();

                this.bindEvents();

            }


            updateDomainDisplay() {

                document.getElementById('domainPrefix').textContent = `${this.currentDomain}/?s=`;

            }


            checkForRedirect() {

                const urlParams = new URLSearchParams(window.location.search);

                const shortCode = urlParams.get('s');

                

                if (shortCode) {

                    this.handleRedirect(shortCode);

                }

            }


            handleRedirect(shortCode) {

                const url = this.urls.find(u => u.shortCode === shortCode);

                

                if (url) {

                    // Update click count

                    url.clicks++;

                    this.saveUrls();

                    

                    // Show redirect container

                    document.getElementById('mainForm').style.display = 'none';

                    document.getElementById('redirectContainer').classList.add('show');

                    

                    // Update redirect info

                    document.getElementById('redirectUrl').textContent = url.originalUrl;

                    document.getElementById('manualRedirect').href = url.originalUrl;

                    

                    // Start countdown

                    let countdown = 3;

                    const timer = document.getElementById('redirectTimer');

                    

                    const interval = setInterval(() => {

                        countdown--;

                        timer.textContent = `Redirect dalam ${countdown} detik...`;

                        

                        if (countdown <= 0) {

                            clearInterval(interval);

                            window.location.href = url.originalUrl;

                        }

                    }, 1000);

                    

                    // Auto redirect after 3 seconds

                    setTimeout(() => {

                        window.location.href = url.originalUrl;

                    }, 3000);

                } else {

                    // Short code not found

                    this.showError('❌ URL pendek tidak ditemukan!');

                }

            }


            bindEvents() {

                document.getElementById('shortenBtn').addEventListener('click', () => this.shortenUrl());

                document.getElementById('copyBtn').addEventListener('click', () => this.copyToClipboard());

                document.getElementById('originalUrl').addEventListener('keypress', (e) => {

                    if (e.key === 'Enter') this.shortenUrl();

                });

                document.getElementById('customAlias').addEventListener('keypress', (e) => {

                    if (e.key === 'Enter') this.shortenUrl();

                });

            }


            generateShortCode() {

                const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';

                let result = '';

                for (let i = 0; i < 6; i++) {

                    result += chars.charAt(Math.floor(Math.random() * chars.length));

                }

                return result;

            }


            validateUrl(url) {

                try {

                    new URL(url);

                    return true;

                } catch {

                    return false;

                }

            }


            showError(message) {

                const errorDiv = document.getElementById('error');

                errorDiv.textContent = message;

                errorDiv.classList.add('show');

                setTimeout(() => {

                    errorDiv.classList.remove('show');

                }, 5000);

            }


            shortenUrl() {

                const originalUrl = document.getElementById('originalUrl').value.trim();

                const customAlias = document.getElementById('customAlias').value.trim();

                const btn = document.getElementById('shortenBtn');

                const btnText = document.getElementById('btnText');


                if (!originalUrl) {

                    this.showError('❌ Silakan masukkan URL yang valid!');

                    return;

                }


                if (!this.validateUrl(originalUrl)) {

                    this.showError('❌ Format URL tidak valid! Pastikan URL dimulai dengan http:// atau https://');

                    return;

                }


                // Check if custom alias already exists

                if (customAlias && this.urls.some(url => url.shortCode === customAlias)) {

                    this.showError('❌ Custom alias sudah digunakan! Silakan pilih yang lain.');

                    return;

                }


                // Validate custom alias

                if (customAlias && !/^[a-zA-Z0-9-_]+$/.test(customAlias)) {

                    this.showError('❌ Custom alias hanya boleh menggunakan huruf, angka, - dan _');

                    return;

                }


                // Show loading state

                btn.disabled = true;

                btnText.textContent = '⏳ Memproses...';


                setTimeout(() => {

                    const shortCode = customAlias || this.generateShortCode();

                    const shortUrl = `${this.baseUrl}?s=${shortCode}`;

                    

                    const urlData = {

                        id: Date.now(),

                        originalUrl,

                        shortCode,

                        shortUrl,

                        clicks: 0,

                        createdAt: new Date().toISOString(),

                        createdDate: new Date().toDateString()

                    };


                    this.urls.unshift(urlData);

                    this.saveUrls();

                    this.showResult(shortUrl);

                    this.updateStats();

                    this.renderUrlList();

                    

                    // Clear inputs

                    document.getElementById('originalUrl').value = '';

                    document.getElementById('customAlias').value = '';

                    

                    // Reset button

                    btn.disabled = false;

                    btnText.textContent = '🚀 Perpendek URL';

                }, 1000);

            }


            showResult(shortUrl) {

                const resultDiv = document.getElementById('result');

                const shortUrlDiv = document.getElementById('shortUrl');

                

                shortUrlDiv.textContent = shortUrl;

                resultDiv.classList.add('show');

                

                // Scroll to result

                resultDiv.scrollIntoView({ behavior: 'smooth', block: 'center' });

            }


            copyToClipboard() {

                const shortUrl = document.getElementById('shortUrl').textContent;

                const copyBtn = document.getElementById('copyBtn');

                

                navigator.clipboard.writeText(shortUrl).then(() => {

                    const originalText = copyBtn.textContent;

                    copyBtn.textContent = '✅ Tersalin!';

                    copyBtn.classList.add('copied');

                    

                    setTimeout(() => {

                        copyBtn.textContent = originalText;

                        copyBtn.classList.remove('copied');

                    }, 2000);

                }).catch(() => {

                    // Fallback for older browsers

                    const textarea = document.createElement('textarea');

                    textarea.value = shortUrl;

                    document.body.appendChild(textarea);

                    textarea.select();

                    document.execCommand('copy');

                    document.body.removeChild(textarea);

                    

                    copyBtn.textContent = '✅ Tersalin!';

                    setTimeout(() => {

                        copyBtn.textContent = '📋 Copy URL';

                    }, 2000);

                });

            }


            updateStats() {

                const totalUrls = this.urls.length;

                const totalClicks = this.urls.reduce((sum, url) => sum + url.clicks, 0);

                const today = new Date().toDateString();

                const todayUrls = this.urls.filter(url => url.createdDate === today).length;


                document.getElementById('totalUrls').textContent = totalUrls;

                document.getElementById('totalClicks').textContent = totalClicks;

                document.getElementById('todayUrls').textContent = todayUrls;

            }


            renderUrlList() {

                const urlList = document.getElementById('urlList');

                

                if (this.urls.length === 0) {

                    urlList.innerHTML = '<h3 style="margin-bottom: 15px; color: #333;">📊 Daftar URL Terpendek</h3><p style="text-align: center; color: #666; padding: 20px;">Belum ada URL yang diperpendek. Buat yang pertama!</p>';

                    return;

                }


                let html = '<h3 style="margin-bottom: 15px; color: #333;">📊 Daftar URL Terpendek</h3>';

                

                this.urls.forEach(url => {

                    const createdDate = new Date(url.createdAt).toLocaleDateString('id-ID', {

                        year: 'numeric',

                        month: 'short',

                        day: 'numeric',

                        hour: '2-digit',

                        minute: '2-digit'

                    });


                    html += `

                        <div class="url-item">

                            <div class="url-info">

                                <div class="original-url">📎 ${url.originalUrl}</div>

                                <div class="short-url-item" onclick="urlShortener.redirectTo('${url.shortCode}')">🔗 ${url.shortUrl}</div>

                                <div style="font-size: 12px; color: #999; margin-top: 5px;">📅 ${createdDate}</div>

                            </div>

                            <div class="url-actions">

                                <div class="click-count">👆 ${url.clicks} klik</div>

                                <button class="copy-btn" onclick="urlShortener.copyUrl('${url.shortUrl}')">📋</button>

                                <button class="delete-btn" onclick="urlShortener.deleteUrl(${url.id})">🗑️</button>

                            </div>

                        </div>

                    `;

                });


                urlList.innerHTML = html;

            }


            redirectTo(shortCode) {

                window.location.href = `${this.baseUrl}?s=${shortCode}`;

            }


            copyUrl(shortUrl) {

                navigator.clipboard.writeText(shortUrl).then(() => {

                    // Visual feedback

                    const notification = document.createElement('div');

                    notification.style.cssText = `

                        position: fixed;

                        top: 20px;

                        right: 20px;

                        background: #28a745;

                        color: white;

                        padding: 10px 20px;

                        border-radius: 5px;

                        z-index: 1000;

                        font-size: 14px;

                    `;

                    notification.textContent = '✅ URL berhasil disalin!';

                    document.body.appendChild(notification);

                    

                    setTimeout(() => {

                        document.body.removeChild(notification);

                    }, 2000);

                });

            }


            deleteUrl(id) {

                if (confirm('Apakah Anda yakin ingin menghapus URL ini?')) {

                    this.urls = this.urls.filter(url => url.id !== id);

                    this.saveUrls();

                    this.updateStats();

                    this.renderUrlList();

                }

            }


            saveUrls() {

                localStorage.setItem('shortUrls', JSON.stringify(this.urls));

            }

        }


        // Initialize the URL shortener

        const urlShortener = new URLShortener();

    </script>

</body>

</html>

Komentar

Postingan populer dari blog ini