Spaces:
Running
Running
| <html lang="id"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>FarOneSquare Calculator - Square of 9 Level</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;600;700;900&family=Rajdhani:wght@300;400;500;600;700&display=swap'); | |
| * { | |
| font-family: 'Rajdhani', sans-serif; | |
| } | |
| h1, h2, h3, .font-orbitron { | |
| font-family: 'Orbitron', sans-serif; | |
| } | |
| body { | |
| background: #030712; | |
| background-image: | |
| radial-gradient(circle at 15% 50%, rgba(0, 150, 255, 0.25) 0%, transparent 40%), | |
| radial-gradient(circle at 85% 50%, rgba(0, 150, 255, 0.25) 0%, transparent 40%), | |
| radial-gradient(circle at 50% 20%, rgba(0, 200, 255, 0.15) 0%, transparent 50%), | |
| linear-gradient(180deg, #000814 0%, #001d3d 50%, #000814 100%); | |
| background-attachment: fixed; | |
| min-height: 100vh; | |
| overflow-x: hidden; | |
| } | |
| body::before { | |
| content: ''; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background-image: | |
| linear-gradient(rgba(0, 150, 255, 0.03) 1px, transparent 1px), | |
| linear-gradient(90deg, rgba(0, 150, 255, 0.03) 1px, transparent 1px); | |
| background-size: 50px 50px; | |
| pointer-events: none; | |
| z-index: 0; | |
| } | |
| body::after { | |
| content: ''; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background-image: | |
| radial-gradient(circle at 10% 30%, rgba(0, 200, 255, 0.08) 0%, transparent 25%), | |
| radial-gradient(circle at 90% 70%, rgba(0, 200, 255, 0.08) 0%, transparent 25%); | |
| pointer-events: none; | |
| z-index: 0; | |
| animation: pulseGlow 8s ease-in-out infinite; | |
| } | |
| @keyframes pulseGlow { | |
| 0%, 100% { opacity: 0.5; } | |
| 50% { opacity: 1; } | |
| } | |
| .cyber-frame { | |
| position: relative; | |
| border: 1px solid rgba(0, 150, 255, 0.3); | |
| box-shadow: | |
| 0 0 40px rgba(0, 150, 255, 0.2), | |
| inset 0 0 30px rgba(0, 150, 255, 0.05); | |
| animation: borderPulse 4s ease-in-out infinite; | |
| } | |
| @keyframes borderPulse { | |
| 0%, 100% { | |
| box-shadow: 0 0 40px rgba(0, 150, 255, 0.2), inset 0 0 30px rgba(0, 150, 255, 0.05); | |
| } | |
| 50% { | |
| box-shadow: 0 0 60px rgba(0, 150, 255, 0.4), inset 0 0 40px rgba(0, 150, 255, 0.1); | |
| } | |
| } | |
| .cyber-frame::before, | |
| .cyber-frame::after { | |
| content: ''; | |
| position: absolute; | |
| width: 40px; | |
| height: 40px; | |
| border: 2px solid #00d9ff; | |
| filter: drop-shadow(0 0 10px rgba(0, 217, 255, 0.8)); | |
| pointer-events: none; | |
| } | |
| .cyber-frame::before { | |
| top: -2px; | |
| left: -2px; | |
| border-right: none; | |
| border-bottom: none; | |
| } | |
| .cyber-frame::after { | |
| bottom: -2px; | |
| right: -2px; | |
| border-left: none; | |
| border-top: none; | |
| } | |
| .corner-tech::before, | |
| .corner-tech::after { | |
| content: ''; | |
| position: absolute; | |
| width: 40px; | |
| height: 40px; | |
| border: 2px solid #00d9ff; | |
| filter: drop-shadow(0 0 10px rgba(0, 217, 255, 0.8)); | |
| pointer-events: none; | |
| } | |
| .corner-tech::before { | |
| top: -2px; | |
| right: -2px; | |
| border-left: none; | |
| border-bottom: none; | |
| } | |
| .corner-tech::after { | |
| bottom: -2px; | |
| left: -2px; | |
| border-right: none; | |
| border-top: none; | |
| } | |
| .glass-card { | |
| background: rgba(3, 7, 18, 0.6); | |
| backdrop-filter: blur(20px); | |
| -webkit-backdrop-filter: blur(20px); | |
| border: 1px solid rgba(0, 150, 255, 0.2); | |
| box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.5); | |
| } | |
| .glass-card-light { | |
| background: rgba(3, 7, 18, 0.4); | |
| backdrop-filter: blur(12px); | |
| -webkit-backdrop-filter: blur(12px); | |
| border: 1px solid rgba(0, 150, 255, 0.15); | |
| } | |
| .neon-text { | |
| text-shadow: | |
| 0 0 10px rgba(0, 217, 255, 0.8), | |
| 0 0 20px rgba(0, 217, 255, 0.5), | |
| 0 0 30px rgba(0, 217, 255, 0.3); | |
| } | |
| .neon-blue { | |
| color: #00d9ff; | |
| text-shadow: 0 0 20px rgba(0, 217, 255, 0.6); | |
| } | |
| .info-card { | |
| background: rgba(3, 7, 18, 0.7); | |
| backdrop-filter: blur(16px); | |
| -webkit-backdrop-filter: blur(16px); | |
| border: 1px solid rgba(0, 150, 255, 0.3); | |
| box-shadow: | |
| 0 0 20px rgba(0, 150, 255, 0.15), | |
| inset 0 1px 0 rgba(255, 255, 255, 0.1); | |
| transition: all 0.3s ease; | |
| } | |
| .info-card:hover { | |
| border-color: rgba(0, 217, 255, 0.5); | |
| box-shadow: | |
| 0 0 30px rgba(0, 150, 255, 0.3), | |
| inset 0 1px 0 rgba(255, 255, 255, 0.2); | |
| transform: translateY(-2px); | |
| } | |
| .cardinal-badge { | |
| width: 28px; | |
| height: 28px; | |
| border-radius: 8px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 11px; | |
| font-weight: 700; | |
| font-family: 'Orbitron', sans-serif; | |
| box-shadow: 0 0 15px currentColor; | |
| position: relative; | |
| } | |
| .cardinal-badge::after { | |
| content: ''; | |
| position: absolute; | |
| inset: -2px; | |
| border-radius: 10px; | |
| padding: 2px; | |
| background: linear-gradient(135deg, currentColor, transparent); | |
| -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); | |
| -webkit-mask-composite: xor; | |
| mask-composite: exclude; | |
| opacity: 0.5; | |
| } | |
| .cardinal-badge.r-green { | |
| background: rgba(0, 255, 150, 0.2); | |
| border: 1px solid rgba(0, 255, 150, 0.5); | |
| color: #00ff96; | |
| } | |
| .cardinal-badge.s-red { | |
| background: rgba(255, 50, 100, 0.2); | |
| border: 1px solid rgba(255, 50, 100, 0.5); | |
| color: #ff3264; | |
| } | |
| .cardinal-badge.r-purple { | |
| background: rgba(168, 85, 247, 0.2); | |
| border: 1px solid rgba(168, 85, 247, 0.5); | |
| color: #a855f7; | |
| } | |
| .cardinal-badge.r-blue { | |
| background: rgba(0, 217, 255, 0.2); | |
| border: 1px solid rgba(0, 217, 255, 0.5); | |
| color: #00d9ff; | |
| } | |
| .btn-primary { | |
| background: linear-gradient(135deg, #0077ff 0%, #00d9ff 100%); | |
| border: 1px solid rgba(0, 217, 255, 0.5); | |
| box-shadow: | |
| 0 0 20px rgba(0, 150, 255, 0.4), | |
| inset 0 1px 0 rgba(255, 255, 255, 0.2); | |
| transition: all 0.3s ease; | |
| } | |
| .btn-primary:hover { | |
| box-shadow: | |
| 0 0 30px rgba(0, 150, 255, 0.6), | |
| inset 0 1px 0 rgba(255, 255, 255, 0.3); | |
| transform: translateY(-2px); | |
| } | |
| .btn-primary:active { | |
| transform: translateY(0px); | |
| } | |
| .btn-secondary { | |
| background: rgba(0, 150, 255, 0.1); | |
| border: 1px solid rgba(0, 150, 255, 0.3); | |
| backdrop-filter: blur(10px); | |
| transition: all 0.3s ease; | |
| } | |
| .btn-secondary:hover { | |
| background: rgba(0, 150, 255, 0.2); | |
| box-shadow: 0 0 20px rgba(0, 150, 255, 0.3); | |
| } | |
| input[type="number"], select { | |
| background: rgba(3, 7, 18, 0.7); | |
| border: 1px solid rgba(0, 150, 255, 0.3); | |
| color: #00d9ff; | |
| transition: all 0.3s ease; | |
| } | |
| input[type="number"]:focus, select:focus { | |
| outline: none; | |
| border-color: #00d9ff; | |
| box-shadow: 0 0 15px rgba(0, 217, 255, 0.4); | |
| background: rgba(3, 7, 18, 0.9); | |
| } | |
| .level-buy { | |
| background: linear-gradient(90deg, rgba(255, 50, 100, 0.15) 0%, rgba(255, 50, 100, 0.05) 100%); | |
| border-left: 3px solid #ff3264; | |
| } | |
| .level-sell { | |
| background: linear-gradient(90deg, rgba(0, 255, 150, 0.15) 0%, rgba(0, 255, 150, 0.05) 100%); | |
| border-left: 3px solid #00ff96; | |
| } | |
| .level-neutral { | |
| background: linear-gradient(90deg, rgba(0, 217, 255, 0.1) 0%, rgba(0, 217, 255, 0.05) 100%); | |
| border-left: 3px solid #00d9ff; | |
| } | |
| .cube-wrapper { | |
| perspective: 1000px; | |
| } | |
| .cube-3d { | |
| transform-style: preserve-3d; | |
| animation: rotateCube 15s linear infinite; | |
| } | |
| @keyframes rotateCube { | |
| 0% { transform: rotateX(15deg) rotateY(0deg); } | |
| 100% { transform: rotateX(15deg) rotateY(360deg); } | |
| } | |
| .toast { | |
| animation: slideInRight 0.3s ease, fadeOut 0.3s ease 2.7s; | |
| } | |
| @keyframes slideInRight { | |
| from { transform: translateX(400px); opacity: 0; } | |
| to { transform: translateX(0); opacity: 1; } | |
| } | |
| @keyframes fadeOut { | |
| to { opacity: 0; } | |
| } | |
| .table-row { | |
| transition: all 0.2s ease; | |
| } | |
| .table-row:hover { | |
| background: rgba(0, 150, 255, 0.08); | |
| transform: scale(1.01); | |
| } | |
| .degree-highlight { | |
| background: linear-gradient(90deg, rgba(0, 217, 255, 0.2) 0%, rgba(0, 217, 255, 0.05) 100%); | |
| box-shadow: inset 0 0 20px rgba(0, 217, 255, 0.1); | |
| } | |
| .stat-number { | |
| background: linear-gradient(135deg, #00d9ff, #0077ff); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| } | |
| .toggle-switch { | |
| position: relative; | |
| width: 54px; | |
| height: 28px; | |
| } | |
| .toggle-switch input { | |
| opacity: 0; | |
| width: 0; | |
| height: 0; | |
| } | |
| .toggle-slider { | |
| position: absolute; | |
| cursor: pointer; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background-color: rgba(0, 150, 255, 0.2); | |
| border: 1px solid rgba(0, 150, 255, 0.3); | |
| transition: 0.4s; | |
| border-radius: 28px; | |
| } | |
| .toggle-slider:before { | |
| position: absolute; | |
| content: ""; | |
| height: 20px; | |
| width: 20px; | |
| left: 3px; | |
| bottom: 3px; | |
| background: linear-gradient(135deg, #0077ff 0%, #00d9ff 100%); | |
| transition: 0.4s; | |
| border-radius: 50%; | |
| box-shadow: 0 0 10px rgba(0, 217, 255, 0.6); | |
| } | |
| input:checked + .toggle-slider { | |
| background-color: rgba(0, 217, 255, 0.3); | |
| border-color: #00d9ff; | |
| box-shadow: 0 0 15px rgba(0, 217, 255, 0.4); | |
| } | |
| input:checked + .toggle-slider:before { | |
| transform: translateX(26px); | |
| } | |
| @keyframes pulse-realtime { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0.5; } | |
| } | |
| .realtime-active { | |
| animation: pulse-realtime 2s ease-in-out infinite; | |
| } | |
| .author-footnote { | |
| font-weight: 700; | |
| text-align: center; | |
| font-size: 0.95rem; | |
| color: #00d9ff; | |
| text-shadow: | |
| 0 0 5px rgba(0, 217, 255, 0.8), | |
| 0 0 10px rgba(0, 217, 255, 0.6), | |
| 0 0 20px rgba(0, 150, 255, 0.4), | |
| 0 0 30px rgba(0, 100, 255, 0.2); | |
| letter-spacing: 1px; | |
| animation: authorGlow 3s ease-in-out infinite; | |
| } | |
| @keyframes authorGlow { | |
| 0%, 100% { | |
| text-shadow: | |
| 0 0 5px rgba(0, 217, 255, 0.8), | |
| 0 0 10px rgba(0, 217, 255, 0.6), | |
| 0 0 20px rgba(0, 150, 255, 0.4); | |
| } | |
| 50% { | |
| text-shadow: | |
| 0 0 10px rgba(0, 217, 255, 1), | |
| 0 0 20px rgba(0, 217, 255, 0.8), | |
| 0 0 30px rgba(0, 150, 255, 0.6), | |
| 0 0 40px rgba(0, 100, 255, 0.4); | |
| } | |
| } | |
| @media (max-width: 390px) { | |
| .cyber-frame::before, | |
| .cyber-frame::after, | |
| .corner-tech::before, | |
| .corner-tech::after { | |
| width: 25px; | |
| height: 25px; | |
| } | |
| h1 { font-size: 1.5rem; } | |
| .info-card { padding: 0.875rem; } | |
| th, td { font-size: 0.7rem; padding: 0.5rem 0.15rem ; } | |
| .cardinal-badge { width: 24px; height: 24px; font-size: 9px; } | |
| .author-footnote { font-size: 0.85rem; } | |
| } | |
| </style> | |
| </head> | |
| <body class="text-gray-200 p-4 md:p-8"> | |
| <div class="max-w-7xl mx-auto relative z-10"> | |
| <!-- Main Container with Cyber Frame --> | |
| <div class="cyber-frame corner-tech rounded-2xl p-6 md:p-10 relative"> | |
| <!-- Header --> | |
| <header class="mb-8 md:mb-10"> | |
| <div class="flex flex-col lg:flex-row lg:items-start lg:justify-between gap-6 mb-6"> | |
| <div class="flex-1"> | |
| <div class="flex items-center gap-3 mb-3"> | |
| <div class="w-12 h-12 rounded-xl bg-gradient-to-br from-cyan-500 to-blue-600 flex items-center justify-center shadow-lg shadow-cyan-500/50"> | |
| <i class="fas fa-calculator text-2xl text-white"></i> | |
| </div> | |
| <h1 class="text-3xl md:text-5xl font-bold neon-text font-orbitron"> | |
| FarOneSquare Calculator | |
| </h1> | |
| </div> | |
| <p class="text-base md:text-lg text-cyan-300 opacity-90"> | |
| Square of 9 Level - Support & Resistance 360° | |
| </p> | |
| <div class="mt-4 flex flex-wrap gap-2"> | |
| <div class="inline-flex items-center gap-2 px-4 py-2 glass-card-light rounded-full"> | |
| <i class="fas fa-calculator neon-blue text-sm"></i> | |
| <span class="text-xs md:text-sm text-cyan-400">Rumus: (√Price ± Factor)²</span> | |
| </div> | |
| <div id="modeIndicator" class="inline-flex items-center gap-2 px-4 py-2 glass-card-light rounded-full transition-all"> | |
| <i id="modeIcon" class="fas fa-sync-alt neon-blue text-sm"></i> | |
| <span id="modeText" class="text-xs md:text-sm text-cyan-400">Real-time Calculation: ON</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Quick Inputs --> | |
| <div class="flex flex-col gap-4 lg:w-auto"> | |
| <!-- Toggle Real-time --> | |
| <div class="glass-card-light rounded-lg px-4 py-3 flex items-center justify-between gap-4"> | |
| <div class="flex items-center gap-2"> | |
| <i class="fas fa-bolt text-cyan-400"></i> | |
| <span class="text-sm font-semibold text-cyan-300">Real-time Mode</span> | |
| </div> | |
| <label class="toggle-switch"> | |
| <input type="checkbox" id="realtimeToggle" checked> | |
| <span class="toggle-slider"></span> | |
| </label> | |
| </div> | |
| <div class="flex flex-col sm:flex-row gap-4"> | |
| <div> | |
| <label class="block text-xs font-medium text-cyan-300 mb-1.5 uppercase tracking-wider"> | |
| <i class="fas fa-dollar-sign mr-1"></i>Harga Dasar | |
| </label> | |
| <input type="number" id="basePrice" value="4370" step="1" | |
| class="w-full sm:w-44 px-4 py-2.5 rounded-lg text-base font-semibold font-orbitron"> | |
| </div> | |
| <div> | |
| <label class="block text-xs font-medium text-cyan-300 mb-1.5 uppercase tracking-wider"> | |
| <i class="fas fa-ruler mr-1"></i>Tick Size | |
| </label> | |
| <input type="number" id="tickSize" value="5" min="1" step="1" | |
| class="w-full sm:w-36 px-4 py-2.5 rounded-lg text-base font-semibold font-orbitron"> | |
| </div> | |
| <div class="flex items-end"> | |
| <button id="calcBtn" onclick="manualCalculate()" class="btn-primary px-6 py-2.5 rounded-lg font-semibold text-white flex items-center gap-2 w-full sm:w-auto"> | |
| <i class="fas fa-play"></i> | |
| <span>Hitung</span> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- 4 Info Cards Row --> | |
| <div class="grid grid-cols-2 lg:grid-cols-4 gap-3 md:gap-4"> | |
| <!-- Card 1: Square Root --> | |
| <div class="info-card rounded-xl p-4 md:p-5"> | |
| <div class="text-[10px] md:text-xs uppercase text-gray-400 mb-2 tracking-wider font-semibold flex items-center gap-1"> | |
| <i class="fas fa-square-root-alt"></i> | |
| Square Root | |
| </div> | |
| <div id="squareRootValue" class="text-2xl md:text-4xl font-bold stat-number font-orbitron"> | |
| 66.106 | |
| </div> | |
| <div class="text-[10px] text-gray-500 mt-1">√Harga Dasar</div> | |
| </div> | |
| <!-- Card 2: Cardinal --> | |
| <div class="info-card rounded-xl p-4 md:p-5"> | |
| <div class="text-[10px] md:text-xs uppercase text-gray-400 mb-2 tracking-wider font-semibold flex items-center gap-1"> | |
| <i class="fas fa-compass"></i> | |
| Cardinal 90° 180° 270° 360° | |
| </div> | |
| <div class="flex items-center gap-2 md:gap-2.5"> | |
| <div class="cardinal-badge r-green" title="Resistance">R</div> | |
| <div class="cardinal-badge s-red" title="Support">S</div> | |
| <div class="cardinal-badge r-purple" title="Resistance">R</div> | |
| <div class="cardinal-badge r-blue" title="Resistance">R</div> | |
| </div> | |
| <div class="text-[10px] text-gray-500 mt-1">R/S Pattern</div> | |
| </div> | |
| <!-- Card 3: Prev 360 Support --> | |
| <div class="info-card rounded-xl p-4 md:p-5"> | |
| <div class="text-[10px] md:text-xs uppercase text-gray-400 mb-2 tracking-wider font-semibold flex items-center gap-1"> | |
| <i class="fas fa-arrow-down text-red-400"></i> | |
| Prev 360° Support | |
| </div> | |
| <div id="prevSupportValue" class="text-2xl md:text-4xl font-bold text-red-400 font-orbitron"> | |
| 4,110 | |
| </div> | |
| <div class="text-[10px] text-gray-500 mt-1">Support Utama</div> | |
| </div> | |
| <!-- Card 4: Next 360 Resist --> | |
| <div class="info-card rounded-xl p-4 md:p-5"> | |
| <div class="text-[10px] md:text-xs uppercase text-gray-400 mb-2 tracking-wider font-semibold flex items-center gap-1"> | |
| <i class="fas fa-arrow-up text-green-400"></i> | |
| Next 360° Resist | |
| </div> | |
| <div id="nextResistValue" class="text-2xl md:text-4xl font-bold text-green-400 font-orbitron"> | |
| 4,640 | |
| </div> | |
| <div class="text-[10px] text-gray-500 mt-1">Resistance Utama</div> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- Results Table --> | |
| <div class="glass-card rounded-xl p-6 md:p-8 mb-6 md:mb-8"> | |
| <div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6"> | |
| <h2 class="text-xl md:text-2xl font-semibold neon-blue font-orbitron flex items-center gap-3"> | |
| <i class="fas fa-table"></i> | |
| Level Square of 9 | |
| </h2> | |
| <div class="flex gap-3 flex-wrap"> | |
| <button onclick="toggleFilter()" class="btn-secondary px-4 py-2 rounded-lg font-semibold text-cyan-300 flex items-center gap-2 text-sm"> | |
| <i class="fas fa-filter"></i> | |
| <span id="filterText">Show All</span> | |
| </button> | |
| <button onclick="copyResults()" class="btn-secondary px-4 py-2 rounded-lg font-semibold text-cyan-300 flex items-center gap-2 text-sm"> | |
| <i class="fas fa-copy"></i> | |
| Copy | |
| </button> | |
| <button onclick="exportCSV()" class="btn-secondary px-4 py-2 rounded-lg font-semibold text-cyan-300 flex items-center gap-2 text-sm"> | |
| <i class="fas fa-file-export"></i> | |
| CSV | |
| </button> | |
| <button onclick="exportJSON()" class="btn-secondary px-4 py-2 rounded-lg font-semibold text-cyan-300 flex items-center gap-2 text-sm"> | |
| <i class="fas fa-code"></i> | |
| JSON | |
| </button> | |
| </div> | |
| </div> | |
| <div class="overflow-x-auto rounded-lg"> | |
| <table class="w-full text-sm md:text-base"> | |
| <thead> | |
| <tr class="border-b-2 border-cyan-900 bg-cyan-950/30"> | |
| <th class="text-left py-3 px-3 text-cyan-300 font-semibold font-orbitron">DERAJAT</th> | |
| <th class="text-right py-3 px-3 text-cyan-300 font-semibold font-orbitron">RESISTANCE</th> | |
| <th class="text-right py-3 px-3 text-cyan-300 font-semibold font-orbitron">JARAK R</th> | |
| <th class="text-right py-3 px-3 text-cyan-300 font-semibold font-orbitron">SUPPORT</th> | |
| <th class="text-right py-3 px-3 text-cyan-300 font-semibold font-orbitron">JARAK S</th> | |
| <th class="text-center py-3 px-3 text-cyan-300 font-semibold font-orbitron">TYPE</th> | |
| </tr> | |
| </thead> | |
| <tbody id="resultsBody"> | |
| <!-- Results will be inserted here --> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| <!-- Info Sections --> | |
| <div class="grid grid-cols-1 lg:grid-cols-2 gap-6 md:gap-8"> | |
| <!-- Cara Baca Level --> | |
| <div class="glass-card rounded-xl p-6 md:p-8"> | |
| <h2 class="text-xl md:text-2xl font-semibold mb-6 neon-blue font-orbitron flex items-center gap-3"> | |
| <i class="fas fa-book-open"></i> | |
| Cara Baca Level | |
| </h2> | |
| <div class="space-y-4"> | |
| <div class="glass-card-light rounded-lg p-4 level-sell"> | |
| <h3 class="font-semibold text-green-400 mb-2 flex items-center gap-2"> | |
| <i class="fas fa-arrow-up"></i>Resistance (Hijau) | |
| </h3> | |
| <p class="text-sm text-gray-300">Level di atas harga dasar. Area supply kuat, berpotensi tertahan/reversal turun. Semakin tinggi derajat, semakin kuat resistance.</p> | |
| </div> | |
| <div class="glass-card-light rounded-lg p-4 level-buy"> | |
| <h3 class="font-semibold text-red-400 mb-2 flex items-center gap-2"> | |
| <i class="fas fa-arrow-down"></i>Support (Merah) | |
| </h3> | |
| <p class="text-sm text-gray-300">Level di bawah harga dasar. Area demand kuat, berpotensi memantul naik. Semakin rendah derajat, semakin kuat support.</p> | |
| </div> | |
| <div class="glass-card-light rounded-lg p-4"> | |
| <h3 class="font-semibold text-cyan-400 mb-2 flex items-center gap-2"> | |
| <i class="fas fa-compass"></i>Type Level | |
| </h3> | |
| <div class="text-sm text-gray-300 space-y-2"> | |
| <div class="flex items-center gap-2"><span class="w-20 text-blue-400 font-semibold">Full (360°)</span> <span class="text-xs">- Level paling kuat</span></div> | |
| <div class="flex items-center gap-2"><span class="w-20 text-purple-400 font-semibold">3Q (270°)</span> <span class="text-xs">- Three Quarter</span></div> | |
| <div class="flex items-center gap-2"><span class="w-20 text-orange-400 font-semibold">Mid (180°)</span> <span class="text-xs">- Midpoint</span></div> | |
| <div class="flex items-center gap-2"><span class="w-20 text-green-400 font-semibold">Quarter (90°)</span> <span class="text-xs">- Level penting</span></div> | |
| <div class="flex items-center gap-2"><span class="w-20 text-gray-400 font-semibold">Minor</span> <span class="text-xs">- Level tambahan</span></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Rumus --> | |
| <div class="glass-card rounded-xl p-6 md:p-8"> | |
| <h2 class="text-xl md:text-2xl font-semibold mb-6 neon-blue font-orbitron flex items-center gap-3"> | |
| <i class="fas fa-square-root-alt"></i> | |
| Rumus Square of 9 | |
| </h2> | |
| <div class="flex flex-col items-center"> | |
| <div class="mb-6 cube-wrapper"> | |
| <div class="cube-3d relative w-32 h-32"> | |
| <div class="absolute inset-0 bg-gradient-to-br from-cyan-500 to-blue-600 rounded-lg opacity-80" style="box-shadow: 0 0 40px rgba(0, 217, 255, 0.8);"></div> | |
| <div class="absolute inset-0 flex items-center justify-center"> | |
| <span class="text-white font-bold font-orbitron text-2xl">9</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="w-full space-y-3"> | |
| <div class="glass-card-light rounded-lg p-4"> | |
| <div class="text-cyan-400 font-semibold mb-2 flex items-center gap-2"> | |
| <i class="fas fa-calculator"></i>Formula Utama | |
| </div> | |
| <div class="space-y-1"> | |
| <code class="text-cyan-400 font-mono text-sm block">Factor = Derajat / 180</code> | |
| <code class="text-green-400 font-mono text-sm block">Resistance = (√Price + Factor)²</code> | |
| <code class="text-red-400 font-mono text-sm block">Support = (√Price - Factor)²</code> | |
| </div> | |
| </div> | |
| <div class="glass-card-light rounded-lg p-4"> | |
| <div class="text-cyan-400 font-semibold mb-2 flex items-center gap-2"> | |
| <i class="fas fa-ruler"></i>Tick Size | |
| </div> | |
| <p class="text-sm text-gray-300">Hasil dibulatkan ke kelipatan Tick Size terdekat. Default: 5 untuk saham Indonesia.</p> | |
| </div> | |
| <div class="glass-card-light rounded-lg p-4"> | |
| <div class="text-cyan-400 font-semibold mb-2 flex items-center gap-2"> | |
| <i class="fas fa-chart-line"></i>Contoh Harga <span id="examplePrice" class="text-white">4370</span> | |
| </div> | |
| <div id="exampleCalc" class="text-xs text-gray-300 space-y-1 font-mono"> | |
| <div>√4370 = 66.106</div> | |
| <div class="text-green-400">360° R: (66.106 + 2.0)² = 4,640</div> | |
| <div class="text-red-400">360° S: (66.106 - 2.0)² = 4,110</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Footer --> | |
| <footer class="mt-8 md:mt-12 text-center"> | |
| <div class="glass-card-light rounded-xl p-6"> | |
| <p class="text-sm text-gray-400 mb-2"> | |
| <i class="fas fa-info-circle mr-2 neon-blue"></i> | |
| FarOneSquare Calculator menggunakan rumus Square of 9 untuk memproyeksikan level support & resistance | |
| </p> | |
| <p class="text-xs text-gray-500"> | |
| Hasil perhitungan bukan saran finansial. Gunakan sebagai referensi analisis teknikal. DYOR - Do Your Own Research. | |
| </p> | |
| <div class="mt-4 flex justify-center gap-4 text-xs text-gray-600 mb-4"> | |
| <span><i class="fas fa-code mr-1"></i>v2.1</span> | |
| <span><i class="fas fa-palette mr-1"></i>Cyberpunk UI</span> | |
| <span><i class="fas fa-bolt mr-1"></i>Real-time Mode</span> | |
| </div> | |
| <div class="pt-4 border-t border-cyan-900/30"> | |
| <p class="author-footnote font-orbitron"> | |
| <i class="fas fa-users mr-2"></i>Authors Setiawan F | Selviana R | |
| </p> | |
| </div> | |
| </div> | |
| </footer> | |
| </div> | |
| </div> | |
| <!-- Toast Container --> | |
| <div id="toastContainer" class="fixed top-4 right-4 z-50 space-y-2 max-w-sm"></div> | |
| <script> | |
| // Data derajat & type Square of 9 | |
| const squareLevels = [ | |
| { degree: 45, type: 'Minor', color: 'text-gray-400', bgColor: 'bg-gray-500/20' }, | |
| { degree: 90, type: 'Quarter', color: 'text-green-400', bgColor: 'bg-green-500/20' }, | |
| { degree: 135, type: 'Minor', color: 'text-gray-400', bgColor: 'bg-gray-500/20' }, | |
| { degree: 180, type: 'Mid', color: 'text-orange-400', bgColor: 'bg-orange-500/20' }, | |
| { degree: 225, type: 'Minor', color: 'text-gray-400', bgColor: 'bg-gray-500/20' }, | |
| { degree: 270, type: '3Q', color: 'text-purple-400', bgColor: 'bg-purple-500/20' }, | |
| { degree: 315, type: 'Minor', color: 'text-gray-400', bgColor: 'bg-gray-500/20' }, | |
| { degree: 360, type: 'Full', color: 'text-blue-400', bgColor: 'bg-blue-500/20' } | |
| ]; | |
| let currentData = []; | |
| let showOnlyMajor = false; | |
| let realtimeMode = true; | |
| let debounceTimer; | |
| // Round to nearest tick size | |
| function roundToTick(value, tickSize) { | |
| return Math.round(value / tickSize) * tickSize; | |
| } | |
| // Format number with thousand separator | |
| function formatNumber(num) { | |
| return num.toLocaleString('id-ID'); | |
| } | |
| // Update mode indicator UI | |
| function updateModeIndicator() { | |
| const indicator = document.getElementById('modeIndicator'); | |
| const icon = document.getElementById('modeIcon'); | |
| const text = document.getElementById('modeText'); | |
| const calcBtn = document.getElementById('calcBtn'); | |
| if (realtimeMode) { | |
| icon.className = 'fas fa-bolt neon-blue text-sm realtime-active'; | |
| text.textContent = 'Real-time Calculation: ON'; | |
| indicator.style.borderColor = 'rgba(0, 217, 255, 0.5)'; | |
| calcBtn.style.opacity = '0.5'; | |
| calcBtn.style.cursor = 'not-allowed'; | |
| } else { | |
| icon.className = 'fas fa-pause neon-blue text-sm'; | |
| text.textContent = 'Manual Mode: OFF'; | |
| indicator.style.borderColor = 'rgba(255, 100, 0.3)'; | |
| calcBtn.style.opacity = '1'; | |
| calcBtn.style.cursor = 'pointer'; | |
| } | |
| } | |
| // Main calculation function - Rumus Square of 9 | |
| function calculateLevels(isManual = false) { | |
| const basePrice = parseFloat(document.getElementById('basePrice').value) || 0; | |
| const tickSize = parseFloat(document.getElementById('tickSize').value) || 1; | |
| if (basePrice <= 0) { | |
| if (isManual) showToast('Harga dasar harus lebih dari 0', 'error'); | |
| return; | |
| } | |
| // Step 1: Akar kuadrat harga dasar | |
| const sqrtBase = Math.sqrt(basePrice); | |
| // Update Square Root card with animation | |
| const sqrtEl = document.getElementById('squareRootValue'); | |
| sqrtEl.style.transform = 'scale(1.05)'; | |
| sqrtEl.textContent = sqrtBase.toFixed(3); | |
| setTimeout(() => sqrtEl.style.transform = 'scale(1)', 150); | |
| currentData = squareLevels.map(level => { | |
| // Step 2: Factor = Derajat / 180 | |
| const factor = level.degree / 180; | |
| // Step 3: Resistance = (√Price + Factor)² | |
| // Step 4: Support = (√Price - Factor)² | |
| const resistanceRaw = Math.pow(sqrtBase + factor, 2); | |
| const supportRaw = Math.pow(sqrtBase - factor, 2); | |
| // Step 5: Bulatkan ke tick size terdekat | |
| const resistance = roundToTick(resistanceRaw, tickSize); | |
| const support = roundToTick(supportRaw, tickSize); | |
| // Jarak dari harga dasar | |
| const jarakR = resistance - basePrice; | |
| const jarakS = basePrice - support; | |
| return { | |
| degree: level.degree, | |
| type: level.type, | |
| typeColor: level.color, | |
| bgColor: level.bgColor, | |
| resistance: resistance, | |
| support: support, | |
| jarakR: jarakR, | |
| jarakS: jarakS | |
| }; | |
| }); | |
| // Update 360° cards with animation | |
| const level360 = currentData.find(d => d.degree === 360); | |
| if (level360) { | |
| const prevEl = document.getElementById('prevSupportValue'); | |
| const nextEl = document.getElementById('nextResistValue'); | |
| prevEl.style.transform = 'scale(1.05)'; | |
| nextEl.style.transform = 'scale(1.05)'; | |
| setTimeout(() => { | |
| prevEl.textContent = formatNumber(level360.support); | |
| nextEl.textContent = formatNumber(level360.resistance); | |
| prevEl.style.transform = 'scale(1)'; | |
| nextEl.style.transform = 'scale(1)'; | |
| }, 100); | |
| } | |
| // Update example calculation | |
| document.getElementById('examplePrice').textContent = basePrice; | |
| document.getElementById('exampleCalc').innerHTML = ` | |
| <div>√${basePrice} = ${sqrtBase.toFixed(3)}</div> | |
| <div class="text-green-400">360° R: (${sqrtBase.toFixed(3)} + 2.0)² = ${formatNumber(level360.resistance)}</div> | |
| <div class="text-red-400">360° S: (${sqrtBase.toFixed(3)} - 2.0)² = ${formatNumber(level360.support)}</div> | |
| `; | |
| renderTable(); | |
| if (isManual) showToast('Kalkulasi berhasil!', 'success'); | |
| } | |
| // Manual calculate with button | |
| function manualCalculate() { | |
| if (!realtimeMode) { | |
| calculateLevels(true); | |
| } else { | |
| showToast('Real-time mode aktif, kalkulasi otomatis', 'info'); | |
| } | |
| } | |
| // Render table | |
| function renderTable() { | |
| const tbody = document.getElementById('resultsBody'); | |
| tbody.innerHTML = ''; | |
| let displayData = currentData; | |
| if (showOnlyMajor) { | |
| displayData = currentData.filter(row => row.type !== 'Minor'); | |
| } | |
| displayData.forEach((row, index) => { | |
| const tr = document.createElement('tr'); | |
| const isHighlight = [90, 180, 270, 360].includes(row.degree); | |
| tr.className = `table-row border-b border-cyan-900/30 ${isHighlight ? 'degree-highlight' : ''}`; | |
| tr.style.animationDelay = `${index * 30}ms`; | |
| tr.innerHTML = ` | |
| <td class="py-3 px-3 font-semibold font-orbitron ${isHighlight ? 'text-cyan-300' : ''}">${row.degree}°</td> | |
| <td class="py-3 px-3 text-right font-semibold text-green-400 font-orbitron">${formatNumber(row.resistance)}</td> | |
| <td class="py-3 px-3 text-right text-sm ${row.jarakR >= 0 ? 'text-green-400' : 'text-red-400'}">+${formatNumber(row.jarakR)}</td> | |
| <td class="py-3 px-3 text-right font-semibold text-red-400 font-orbitron">${formatNumber(row.support)}</td> | |
| <td class="py-3 px-3 text-right text-sm ${row.jarakS >= 0 ? 'text-red-400' : 'text-green-400'}">-${formatNumber(row.jarakS)}</td> | |
| <td class="py-3 px-3 text-center"> | |
| <span class="inline-block px-3 py-1 rounded-full text-xs font-semibold ${row.typeColor} ${row.bgColor} border border-current">${row.type}</span> | |
| </td> | |
| `; | |
| tbody.appendChild(tr); | |
| }); | |
| } | |
| // Toggle filter | |
| function toggleFilter() { | |
| showOnlyMajor = !showOnlyMajor; | |
| document.getElementById('filterText').textContent = showOnlyMajor ? 'Show All' : 'Major Only'; | |
| renderTable(); | |
| showToast(showOnlyMajor ? 'Menampilkan level major saja' : 'Menampilkan semua level', 'info'); | |
| } | |
| // Copy results | |
| function copyResults() { | |
| const basePrice = document.getElementById('basePrice').value; | |
| const tickSize = document.getElementById('tickSize').value; | |
| let text = `FarOneSquare Calculator - Square of 9 Level\n`; | |
| text += `Harga Dasar: ${basePrice} | Tick Size: ${tickSize}\n`; | |
| text += `Mode: ${realtimeMode ? 'Real-time' : 'Manual'}\n`; | |
| text += `Generated: ${new Date().toLocaleString('id-ID')}\n\n`; | |
| text += `DERAJAT\tRESISTANCE\tJARAK R\tSUPPORT\tJARAK S\tTYPE\n`; | |
| text += `${'='.repeat(70)}\n`; | |
| currentData.forEach(row => { | |
| text += `${row.degree}°\t${row.resistance}\t+${row.jarakR}\t${row.support}\t-${row.jarakS}\t${row.type}\n`; | |
| }); | |
| navigator.clipboard.writeText(text).then(() => { | |
| showToast('Data berhasil dicopy ke clipboard!', 'success'); | |
| }).catch(() => { | |
| showToast('Gagal copy data', 'error'); | |
| }); | |
| } | |
| // Export CSV | |
| function exportCSV() { | |
| const basePrice = document.getElementById('basePrice').value; | |
| const tickSize = document.getElementById('tickSize').value; | |
| let csv = `FarOneSquare Calculator - Square of 9 Level\n`; | |
| csv += `Harga Dasar,${basePrice}\n`; | |
| csv += `Tick Size,${tickSize}\n`; | |
| csv += `Mode,${realtimeMode ? 'Real-time' : 'Manual'}\n`; | |
| csv += `Generated,${new Date().toLocaleString('id-ID')}\n\n`; | |
| csv += `DERAJAT,RESISTANCE,JARAK R,SUPPORT,JARAK S,TYPE\n`; | |
| currentData.forEach(row => { | |
| csv += `${row.degree},${row.resistance},${row.jarakR},${row.support},${row.jarakS},${row.type}\n`; | |
| }); | |
| const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' }); | |
| const url = window.URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = `faronesquare_${basePrice}_${Date.now()}.csv`; | |
| a.click(); | |
| window.URL.revokeObjectURL(url); | |
| showToast('CSV berhasil didownload!', 'success'); | |
| } | |
| // Export JSON | |
| function exportJSON() { | |
| const basePrice = document.getElementById('basePrice').value; | |
| const tickSize = document.getElementById('tickSize').value; | |
| const sqrtBase = Math.sqrt(basePrice); | |
| const jsonData = { | |
| app: 'FarOneSquare Calculator', | |
| version: '2.1', | |
| timestamp: new Date().toISOString(), | |
| mode: realtimeMode ? 'realtime' : 'manual', | |
| input: { | |
| basePrice: basePrice, | |
| tickSize: tickSize, | |
| sqrtPrice: sqrtBase.toFixed(3) | |
| }, | |
| summary: { | |
| level360: currentData.find(d => d.degree === 360) | |
| }, | |
| levels: currentData | |
| }; | |
| const blob = new Blob([JSON.stringify(jsonData, null, 2)], { type: 'application/json' }); | |
| const url = window.URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = `faronesquare_${basePrice}_${Date.now()}.json`; | |
| a.click(); | |
| window.URL.revokeObjectURL(url); | |
| showToast('JSON berhasil didownload!', 'success'); | |
| } | |
| // Toast notification | |
| function showToast(message, type = 'info') { | |
| const container = document.getElementById('toastContainer'); | |
| const toast = document.createElement('div'); | |
| const icons = { | |
| success: 'fa-check-circle text-green-400', | |
| error: 'fa-times-circle text-red-400', | |
| info: 'fa-info-circle text-cyan-400' | |
| }; | |
| const borders = { | |
| success: 'border-green-400', | |
| error: 'border-red-400', | |
| info: 'border-cyan-400' | |
| }; | |
| toast.className = `toast glass-card px-5 py-3 rounded-lg shadow-xl flex items-center gap-3 border-l-4 ${borders[type]}`; | |
| toast.innerHTML = ` | |
| <i class="fas ${icons[type]}"></i> | |
| <span class="text-white font-semibold text-sm">${message}</span> | |
| `; | |
| container.appendChild(toast); | |
| setTimeout(() => { | |
| toast.style.opacity = '0'; | |
| setTimeout(() => toast.remove(), 300); | |
| }, 2700); | |
| } | |
| // Debounced auto-calculation for real-time mode | |
| function debounceCalculate() { | |
| if (!realtimeMode) return; | |
| clearTimeout(debounceTimer); | |
| debounceTimer = setTimeout(() => calculateLevels(false), 400); | |
| } | |
| // Toggle real-time mode | |
| document.getElementById('realtimeToggle').addEventListener('change', function(e) { | |
| realtimeMode = e.target.checked; | |
| updateModeIndicator(); | |
| if (realtimeMode) { | |
| showToast('Real-time mode ON - Update otomatis saat input berubah', 'success'); | |
| calculateLevels(false); // Calculate immediately | |
| } else { | |
| showToast('Manual mode ON - Klik Hitung untuk update', 'info'); | |
| } | |
| }); | |
| // Input listeners | |
| document.getElementById('basePrice').addEventListener('input', debounceCalculate); | |
| document.getElementById('tickSize').addEventListener('input', debounceCalculate); | |
| // Initial setup | |
| updateModeIndicator(); | |
| calculateLevels(); | |
| // Keyboard shortcuts | |
| document.addEventListener('keydown', (e) => { | |
| if (e.ctrlKey || e.metaKey) { | |
| if (e.key === 'c' && e.target.tagName !== 'INPUT') { | |
| e.preventDefault(); | |
| copyResults(); | |
| } | |
| if (e.key === 's') { | |
| e.preventDefault(); | |
| exportCSV(); | |
| } | |
| } | |
| if (e.key === 'Enter' && !realtimeMode) { | |
| calculateLevels(true); | |
| } | |
| }); | |
| </script> | |
| <script>(function(){document.addEventListener("click",function(e){var a=e.target.closest("[data-product-id]");if(!a)return;e.preventDefault();var pid=a.getAttribute("data-product-id");if(pid)parent.postMessage({type:"ecto-artifact-link-click",productId:pid},"*")})})();</script> | |
| </body> | |
| </html> |