| <html lang="th"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self'; connect-src 'none'; media-src 'none'; object-src 'none'; frame-src 'none'; base-uri 'none'; form-action 'none';"> | |
| <meta http-equiv="X-Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self'; connect-src 'none'; media-src 'none'; object-src 'none'; frame-src 'none'; base-uri 'none'; form-action 'none';"> | |
| <meta http-equiv="Referrer-Policy" content="no-referrer"> | |
| <title>Chimera Symbiote — AI Code Analyzer</title> | |
| <style> | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| body { font-family: 'Segoe UI', system-ui, sans-serif; background: #0a0a1a; color: #e0e0e0; min-height: 100vh; } | |
| .header { background: linear-gradient(135deg, #0d0d2b, #1a1a3e); padding: 16px 30px; border-bottom: 1px solid #1e1e3a; display: flex; justify-content: space-between; align-items: center; } | |
| .logo { font-size: 22px; font-weight: bold; background: linear-gradient(135deg, #00d4ff, #ff00ff); -webkit-background-clip: text; -webkit-text-fill-color: transparent; } | |
| .header-right { display: flex; align-items: center; gap: 16px; } | |
| .pricing-btn { background: linear-gradient(135deg, #ff00ff, #8855ff); border: none; color: white; padding: 8px 16px; border-radius: 8px; cursor: pointer; font-size: 13px; font-weight: 600; } | |
| .pricing-btn:hover { opacity: 0.9; } | |
| .container { max-width: 1400px; margin: 0 auto; padding: 24px; } | |
| .hero { text-align: center; padding: 40px 20px; } | |
| .hero h1 { font-size: 36px; margin-bottom: 12px; background: linear-gradient(135deg, #00d4ff, #ff00ff); -webkit-background-clip: text; -webkit-text-fill-color: transparent; } | |
| .hero p { color: #8888aa; font-size: 16px; max-width: 600px; margin: 0 auto; } | |
| .main-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 24px; } | |
| @media (max-width: 900px) { .main-grid { grid-template-columns: 1fr; } } | |
| .card { background: #12122a; border: 1px solid #1e1e3a; border-radius: 12px; padding: 20px; } | |
| .card-title { font-size: 14px; color: #8888aa; margin-bottom: 12px; display: flex; align-items: center; gap: 8px; font-weight: 600; } | |
| textarea { width: 100%; height: 350px; background: #0a0a1a; border: 1px solid #1e1e3a; border-radius: 8px; color: #e0e0e0; font-family: 'Consolas', 'Fira Code', monospace; font-size: 13px; padding: 16px; resize: vertical; line-height: 1.6; } | |
| textarea:focus { outline: none; border-color: #00d4ff; } | |
| .controls { display: flex; gap: 10px; margin-top: 12px; flex-wrap: wrap; } | |
| .btn { padding: 10px 20px; border: none; border-radius: 8px; cursor: pointer; font-size: 14px; font-weight: 600; transition: all 0.2s; } | |
| .btn-primary { background: linear-gradient(135deg, #00d4ff, #0088ff); color: white; } | |
| .btn-primary:hover { transform: translateY(-1px); box-shadow: 0 4px 12px #00d4ff44; } | |
| .btn-secondary { background: #1e1e3a; color: #e0e0e0; } | |
| .btn-secondary:hover { background: #2a2a4a; } | |
| .btn-success { background: linear-gradient(135deg, #00ff88, #00cc66); color: #0a0a1a; } | |
| select { background: #0a0a1a; border: 1px solid #1e1e3a; color: #e0e0e0; padding: 10px 14px; border-radius: 8px; font-size: 14px; } | |
| .score-display { text-align: center; padding: 20px; } | |
| .score-circle { width: 120px; height: 120px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 36px; font-weight: bold; margin: 0 auto 12px; } | |
| .score-high { background: #00ff8822; color: #00ff88; border: 3px solid #00ff88; } | |
| .score-mid { background: #ffcc0022; color: #ffcc00; border: 3px solid #ffcc00; } | |
| .score-low { background: #ff446622; color: #ff4466; border: 3px solid #ff4466; } | |
| .issues-list { margin-top: 16px; } | |
| .issue-item { background: #0a0a1a; border-radius: 8px; padding: 12px; margin-bottom: 8px; border-left: 3px solid; } | |
| .issue-critical { border-color: #ff4466; } | |
| .issue-warning { border-color: #ffcc00; } | |
| .issue-info { border-color: #00d4ff; } | |
| .issue-suggestion { border-color: #00ff88; } | |
| .issue-type { font-size: 11px; font-weight: 600; margin-bottom: 4px; } | |
| .issue-critical .issue-type { color: #ff4466; } | |
| .issue-warning .issue-type { color: #ffcc00; } | |
| .issue-info .issue-type { color: #00d4ff; } | |
| .issue-suggestion .issue-type { color: #00ff88; } | |
| .issue-msg { font-size: 13px; color: #ccc; } | |
| .issue-line { font-size: 11px; color: #8888aa; margin-top: 4px; } | |
| .issue-fix { font-size: 12px; color: #00ff88; margin-top: 6px; font-family: 'Consolas', monospace; background: #00ff8811; padding: 6px 8px; border-radius: 4px; } | |
| .stats-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-top: 16px; } | |
| .stat-item { background: #0a0a1a; border-radius: 8px; padding: 12px; text-align: center; } | |
| .stat-value { font-size: 20px; font-weight: bold; } | |
| .stat-label { font-size: 11px; color: #8888aa; margin-top: 4px; } | |
| .sample-btns { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; } | |
| .sample-btn { background: #1e1e3a; border: 1px solid #2a2a4a; color: #8888aa; padding: 6px 12px; border-radius: 6px; cursor: pointer; font-size: 12px; } | |
| .sample-btn:hover { background: #2a2a4a; color: #e0e0e0; } | |
| /* Pricing Modal */ | |
| .modal-overlay { display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.7); z-index: 1000; align-items: center; justify-content: center; } | |
| .modal-overlay.active { display: flex; } | |
| .modal { background: #12122a; border: 1px solid #1e1e3a; border-radius: 16px; padding: 30px; max-width: 900px; width: 90%; max-height: 90vh; overflow-y: auto; } | |
| .modal h2 { font-size: 24px; margin-bottom: 20px; text-align: center; } | |
| .modal-close { float: right; background: none; border: none; color: #8888aa; font-size: 24px; cursor: pointer; } | |
| .pricing-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; } | |
| @media (max-width: 700px) { .pricing-grid { grid-template-columns: 1fr; } } | |
| .pricing-card { background: #0a0a1a; border: 1px solid #1e1e3a; border-radius: 12px; padding: 24px; text-align: center; position: relative; } | |
| .pricing-card.featured { border-color: #00d4ff; } | |
| .pricing-card.featured::before { content: 'POPULAR'; position: absolute; top: -10px; left: 50%; transform: translateX(-50%); background: #00d4ff; color: #0a0a1a; padding: 2px 12px; border-radius: 10px; font-size: 11px; font-weight: 700; } | |
| .pricing-name { font-size: 18px; font-weight: 600; margin-bottom: 8px; } | |
| .pricing-price { font-size: 32px; font-weight: bold; margin-bottom: 4px; } | |
| .pricing-period { font-size: 12px; color: #8888aa; margin-bottom: 16px; } | |
| .pricing-features { list-style: none; text-align: left; margin-bottom: 20px; } | |
| .pricing-features li { padding: 6px 0; font-size: 13px; color: #ccc; border-bottom: 1px solid #1e1e3a; } | |
| .pricing-features li::before { content: '✓ '; color: #00ff88; } | |
| .pricing-cta { width: 100%; padding: 12px; border: none; border-radius: 8px; font-size: 14px; font-weight: 600; cursor: pointer; } | |
| .pricing-card.featured .pricing-cta { background: linear-gradient(135deg, #00d4ff, #0088ff); color: white; } | |
| .pricing-card:not(.featured) .pricing-cta { background: #1e1e3a; color: #e0e0e0; } | |
| .footer { text-align: center; padding: 20px; color: #555577; font-size: 11px; border-top: 1px solid #1e1e3a; margin-top: 40px; } | |
| .hidden { display: none; } | |
| .loading { text-align: center; padding: 40px; color: #8888aa; } | |
| .loading::after { content: ''; display: inline-block; width: 20px; height: 20px; border: 2px solid #00d4ff; border-top-color: transparent; border-radius: 50%; animation: spin 0.8s linear infinite; margin-left: 8px; vertical-align: middle; } | |
| @keyframes spin { to { transform: rotate(360deg); } } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="header"> | |
| <div class="logo">🧬 Chimera Symbiote</div> | |
| <div class="header-right"> | |
| <span style="font-size:12px;color:#8888aa">v2.0 — Free Analyzer</span> | |
| <button class="pricing-btn" onclick="showPricing()">💰 Upgrade to Pro</button> | |
| </div> | |
| </div> | |
| <div class="container"> | |
| <div class="hero"> | |
| <h1>AI Code Analyzer</h1> | |
| <p>วางโค้ดของคุณลงไป — รับคะแนน, พบปัญหา, และคำแนะนำในการปรับปรุงทันที</p> | |
| </div> | |
| <div class="main-grid"> | |
| <!-- Input Panel --> | |
| <div class="card"> | |
| <div class="card-title">📝 ใส่โค้ดที่ต้องการวิเคราะห์</div> | |
| <select id="langSelect" style="margin-bottom:10px"> | |
| <option value="python">Python</option> | |
| <option value="javascript">JavaScript</option> | |
| <option value="html">HTML</option> | |
| </select> | |
| <textarea id="codeInput" placeholder="วางโค้ดที่นี่... หรือเลือกตัวอย่างด้านล่าง"># ตัวอย่างโค้ด Python | |
| def calculate_total(items): | |
| total = 0 | |
| for i in range(len(items)): | |
| total = total + items[i] | |
| return total | |
| def process_data(data): | |
| result = [] | |
| for d in data: | |
| if d > 0: | |
| result.append(d * 2) | |
| return result | |
| x = calculate_total([1,2,3,4,5]) | |
| print(x)</textarea> | |
| <div class="sample-btns"> | |
| <span style="font-size:12px;color:#8888aa;line-height:28px">ตัวอย่าง:</span> | |
| <button class="sample-btn" onclick="loadSample('bad_python')">โค้ดแย่ (Python)</button> | |
| <button class="sample-btn" onclick="loadSample('good_python')">โค้ดดี (Python)</button> | |
| <button class="sample-btn" onclick="loadSample('bad_js')">โค้ดแย่ (JS)</button> | |
| <button class="sample-btn" onclick="loadSample('bad_html')">โค้ดแย่ (HTML)</button> | |
| </div> | |
| <div class="controls"> | |
| <button class="btn btn-primary" onclick="analyzeCode()">🔍 วิเคราะห์โค้ด</button> | |
| <button class="btn btn-secondary" onclick="clearCode()">🗑️ ล้าง</button> | |
| <button class="btn btn-success hidden" id="exportBtn" onclick="exportReport()">📄 Export Report</button> | |
| </div> | |
| </div> | |
| <!-- Results Panel --> | |
| <div class="card" id="resultsCard"> | |
| <div class="card-title">📊 ผลการวิเคราะห์</div> | |
| <div id="resultsArea"> | |
| <div style="text-align:center;padding:60px 20px;color:#8888aa"> | |
| <div style="font-size:48px;margin-bottom:12px">🧬</div> | |
| <p>วางโค้ดแล้วกด "วิเคราะห์โค้ด" เพื่อดูผล</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Pricing Modal --> | |
| <div class="modal-overlay" id="pricingModal"> | |
| <div class="modal"> | |
| <button class="modal-close" onclick="closePricing()">×</button> | |
| <h2>💰 เลือกแพ็กเกจที่เหมาะกับคุณ</h2> | |
| <div class="pricing-grid"> | |
| <div class="pricing-card"> | |
| <div class="pricing-name">Starter</div> | |
| <div class="pricing-price" style="color:#00d4ff">3,415฿</div> | |
| <div class="pricing-period">/เดือน</div> | |
| <ul class="pricing-features"> | |
| <li>วิเคราะห์ 10 ไฟล์/เดือน</li> | |
| <li>รายงานพื้นฐาน</li> | |
| <li>รองรับ Python, JS</li> | |
| <li>Email support</li> | |
| </ul> | |
| <button class="pricing-cta" onclick="contactSales('Starter')">เริ่มต้นใช้งาน</button> | |
| </div> | |
| <div class="pricing-card featured"> | |
| <div class="pricing-name">Professional</div> | |
| <div class="pricing-price" style="color:#ff00ff">17,215฿</div> | |
| <div class="pricing-period">/เดือน</div> | |
| <ul class="pricing-features"> | |
| <li>ไม่จำกัดไฟล์</li> | |
| <li>CI/CD Integration</li> | |
| <li>ทุกภาษา + Custom Rules</li> | |
| <li>Priority support</li> | |
| <li>Team dashboard</li> | |
| </ul> | |
| <button class="pricing-cta" onclick="contactSales('Professional')">เริ่มต้นใช้งาน</button> | |
| </div> | |
| <div class="pricing-card"> | |
| <div class="pricing-name">Enterprise</div> | |
| <div class="pricing-price" style="color:#00ff88">86,215฿</div> | |
| <div class="pricing-period">/เดือน</div> | |
| <ul class="pricing-features"> | |
| <li>ทุกอย่างใน Professional</li> | |
| <li>Custom AI Rules</li> | |
| <li>On-premise deployment</li> | |
| <li>Dedicated support</li> | |
| <li>SLA 99.9%</li> | |
| </ul> | |
| <button class="pricing-cta" onclick="contactSales('Enterprise')">ติดต่อฝ่ายขาย</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="footer"> | |
| 🧬 Chimera Symbiote v2.0 — Powered by Ω NEXUS — 100% Offline | |
| </div> | |
| <script> | |
| // ============================================================ | |
| // 🔒 OFFLINE MODE — BLOCK ALL EXTERNAL CONNECTIONS | |
| // ============================================================ | |
| (function() { | |
| 'use strict'; | |
| // Block fetch() | |
| if (typeof fetch !== 'undefined') { | |
| window.fetch = function() { | |
| console.warn('[Chimera] 🔒 Blocked external fetch() — 100% Offline Mode'); | |
| return Promise.reject(new Error('External connections blocked — Offline Mode')); | |
| }; | |
| } | |
| // Block XMLHttpRequest | |
| const OriginalXHR = window.XMLHttpRequest; | |
| window.XMLHttpRequest = function() { | |
| const xhr = new OriginalXHR(); | |
| const originalOpen = xhr.open; | |
| xhr.open = function(method, url) { | |
| if (typeof url === 'string' && (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('//'))) { | |
| console.warn('[Chimera] 🔒 Blocked XMLHttpRequest to:', url); | |
| throw new Error('External connections blocked — Offline Mode'); | |
| } | |
| return originalOpen.apply(this, arguments); | |
| }; | |
| return xhr; | |
| }; | |
| // Block navigator.sendBeacon | |
| if (navigator.sendBeacon) { | |
| navigator.sendBeacon = function() { | |
| console.warn('[Chimera] 🔒 Blocked sendBeacon() — 100% Offline Mode'); | |
| return false; | |
| }; | |
| } | |
| // Block WebSocket | |
| if (typeof WebSocket !== 'undefined') { | |
| window.WebSocket = function() { | |
| console.warn('[Chimera] 🔒 Blocked WebSocket — 100% Offline Mode'); | |
| throw new Error('External connections blocked — Offline Mode'); | |
| }; | |
| } | |
| // Block window.open for external URLs | |
| const originalOpen = window.open; | |
| window.open = function(url) { | |
| if (url && (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('//'))) { | |
| console.warn('[Chimera] 🔒 Blocked window.open() to:', url); | |
| alert('🔒 Offline Mode — ไม่สามารถเปิดลิงก์ภายนอกได้'); | |
| return null; | |
| } | |
| return originalOpen.apply(this, arguments); | |
| }; | |
| // Block external link clicks | |
| document.addEventListener('click', function(e) { | |
| const link = e.target.closest('a'); | |
| if (link && link.href && (link.href.startsWith('http://') || link.href.startsWith('https://'))) { | |
| e.preventDefault(); | |
| console.warn('[Chimera] 🔒 Blocked external link:', link.href); | |
| alert('🔒 Offline Mode — ไม่สามารถเปิดลิงก์ภายนอกได้'); | |
| } | |
| }, true); | |
| // Block form submissions to external URLs | |
| document.addEventListener('submit', function(e) { | |
| const form = e.target; | |
| if (form.action && (form.action.startsWith('http://') || form.action.startsWith('https://'))) { | |
| e.preventDefault(); | |
| console.warn('[Chimera] 🔒 Blocked external form submission'); | |
| } | |
| }, true); | |
| console.log('[Chimera] 🔒 Offline Mode Active — All external connections blocked'); | |
| })(); | |
| // ============================================================ | |
| // CHIMERA SYMBIOTE — Real Code Analyzer (Browser-based) | |
| // ============================================================ | |
| const SAMPLES = { | |
| bad_python: `# โค้ดที่ไม่มีคุณภาพ | |
| def foo(x,y,z): | |
| if x==1: | |
| if y==2: | |
| if z==3: | |
| return True | |
| else: | |
| return False | |
| else: | |
| return False | |
| else: | |
| return False | |
| data = [] | |
| for i in range(100): | |
| data.append(i) | |
| data.append(i*2) | |
| data.append(i*3) | |
| result = [] | |
| for d in data: | |
| if d > 50: | |
| result.append(d) | |
| x = result | |
| print(x)`, | |
| good_python: `"""Module for data processing.""" | |
| from typing import List, Optional | |
| def is_valid_coordinate(x: int, y: int, z: int) -> bool: | |
| """Check if coordinates match target (1, 2, 3).""" | |
| return x == 1 and y == 2 and z == 3 | |
| def generate_multiples(count: int = 100) -> List[int]: | |
| """Generate multiples of 1, 2, 3 for each number.""" | |
| return [ | |
| val | |
| for i in range(count) | |
| for val in (i, i * 2, i * 3) | |
| ] | |
| def filter_above(data: List[int], threshold: int = 50) -> List[int]: | |
| """Filter values above threshold.""" | |
| return [d for d in data if d > threshold] | |
| def main() -> None: | |
| """Main entry point.""" | |
| data = generate_multiples() | |
| result = filter_above(data) | |
| print(result) | |
| if __name__ == "__main__": | |
| main()`, | |
| bad_js: `var x = 10 | |
| var y = 20 | |
| var z = x + y | |
| function doStuff(arr) { | |
| var result = [] | |
| for(var i = 0; i < arr.length; i++) { | |
| if(arr[i] > 0) { | |
| result.push(arr[i] * 2) | |
| } | |
| } | |
| return result | |
| } | |
| var data = [1,2,3,-1,0,5,-3,8] | |
| var output = doStuff(data) | |
| console.log(output) | |
| var obj = {} | |
| obj.name = "test" | |
| obj.value = 42 | |
| obj.data = [1,2,3]`, | |
| bad_html: `<!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>Bad Page</title> | |
| </head> | |
| <body> | |
| <div> | |
| <div> | |
| <div> | |
| <p>Hello World | |
| <img src="test.jpg"> | |
| <a href="#">Click here</a> | |
| </div> | |
| </div> | |
| </div> | |
| <font size="5">Old font tag</font> | |
| <center>Centered text</center> | |
| <br> | |
| <br> | |
| <br> | |
| <table> | |
| <tr><td>Cell 1<td>Cell 2<td>Cell 3</tr> | |
| </table> | |
| </body> | |
| </html>` | |
| }; | |
| function loadSample(name) { | |
| const lang = name.includes('js') ? 'javascript' : name.includes('html') ? 'html' : 'python'; | |
| document.getElementById('langSelect').value = lang; | |
| document.getElementById('codeInput').value = SAMPLES[name]; | |
| } | |
| function clearCode() { | |
| document.getElementById('codeInput').value = ''; | |
| document.getElementById('resultsArea').innerHTML = ` | |
| <div style="text-align:center;padding:60px 20px;color:#8888aa"> | |
| <div style="font-size:48px;margin-bottom:12px">🧬</div> | |
| <p>วางโค้ดแล้วกด "วิเคราะห์โค้ด" เพื่อดูผล</p> | |
| </div>`; | |
| document.getElementById('exportBtn').classList.add('hidden'); | |
| } | |
| // ============================================================ | |
| // ANALYSIS ENGINES | |
| // ============================================================ | |
| function analyzePython(code) { | |
| const lines = code.split('\n'); | |
| const issues = []; | |
| let score = 100; | |
| let metrics = { lines: lines.length, functions: 0, classes: 0, imports: 0, comments: 0, blankLines: 0 }; | |
| // Check for docstring/module doc | |
| const hasModuleDoc = code.trim().startsWith('"""') || code.trim().startsWith("'''"); | |
| if (!hasModuleDoc && lines.length > 5) { | |
| issues.push({ type: 'warning', msg: 'ไม่มี module docstring', line: 1, fix: 'เพิ่ม """Module description.""" ที่ต้นไฟล์' }); | |
| score -= 5; | |
| } | |
| for (let i = 0; i < lines.length; i++) { | |
| const line = lines[i]; | |
| const trimmed = line.trim(); | |
| if (!trimmed) { metrics.blankLines++; continue; } | |
| if (trimmed.startsWith('#')) { metrics.comments++; continue; } | |
| if (trimmed.startsWith('import ') || trimmed.startsWith('from ')) { metrics.imports++; } | |
| if (trimmed.startsWith('def ')) { metrics.functions++; } | |
| if (trimmed.startsWith('class ')) { metrics.classes++; } | |
| // Deep nesting | |
| const indent = line.search(/\S/); | |
| if (indent >= 12) { | |
| issues.push({ type: 'critical', msg: 'โค้ดซ้อนลึกเกินไป (nested > 3 levels)', line: i + 1, fix: 'แยกเป็น function ย่อย หรือใช้ early return' }); | |
| score -= 10; | |
| } else if (indent >= 8) { | |
| issues.push({ type: 'warning', msg: 'โค้ดซ้อนลึก (nested 2-3 levels)', line: i + 1, fix: 'พิจารณาแยก function' }); | |
| score -= 3; | |
| } | |
| // Bare except | |
| if (trimmed.includes('except:')) { | |
| issues.push({ type: 'critical', msg: 'ใช้ bare except — ควรระบุ exception type', line: i + 1, fix: 'เปลี่ยนเป็น except Exception:' }); | |
| score -= 8; | |
| } | |
| // Global variables | |
| if (trimmed.startsWith('global ')) { | |
| issues.push({ type: 'warning', msg: 'ใช้ global variable', line: i + 1, fix: 'ใช้ class attribute หรือ pass เป็น parameter' }); | |
| score -= 5; | |
| } | |
| // Magic numbers | |
| const magicMatch = trimmed.match(/[^a-zA-Z_"](\d{2,})(?![a-zA-Z_"\d])/); | |
| if (magicMatch && !trimmed.startsWith('#') && !trimmed.startsWith('def') && !trimmed.startsWith('class')) { | |
| // Check if it's in a range() or common pattern | |
| if (!trimmed.includes('range(') && !trimmed.includes('__')) { | |
| issues.push({ type: 'info', msg: `พบ magic number: ${magicMatch[1]}`, line: i + 1, fix: `กำหนดเป็น constant เช่น MAX_VALUE = ${magicMatch[1]}` }); | |
| score -= 2; | |
| } | |
| } | |
| // Long lines | |
| if (line.length > 120) { | |
| issues.push({ type: 'warning', msg: `บรรทัดยาวเกินไป (${line.length} ตัวอักษร, ควร < 120)`, line: i + 1, fix: 'แบ่งบรรทัดหรือใช้ line continuation' }); | |
| score -= 2; | |
| } | |
| // print() in non-main code | |
| if (trimmed.startsWith('print(') && !trimmed.startsWith('#')) { | |
| // Check if inside if __name__ == "__main__" | |
| const inMainBlock = code.includes('if __name__') && i > code.indexOf('if __name__'); | |
| if (!inMainBlock) { | |
| issues.push({ type: 'info', msg: 'ใช้ print() — ควรใช้ logging แทน', line: i + 1, fix: 'import logging; logging.info(...)' }); | |
| score -= 1; | |
| } | |
| } | |
| // Unused variable pattern (assign then never used) | |
| const varAssign = trimmed.match(/^([a-zA-Z_]\w*)\s*=/); | |
| if (varAssign) { | |
| const varName = varAssign[1]; | |
| if (varName !== '_' && !['self', 'cls', 'result', 'data', 'output', 'response', 'ret'].includes(varName)) { | |
| const restOfCode = lines.slice(i + 1).join('\n'); | |
| const usageCount = (restOfCode.match(new RegExp(`\\b${varName}\\b`, 'g')) || []).length; | |
| if (usageCount === 0) { | |
| issues.push({ type: 'warning', msg: `ตัวแปร '${varName}' ถูกกำหนดแต่ไม่ได้ใช้`, line: i + 1, fix: `ลบบรรทัดนี้ หรือใช้ ${varName} ในโค้ดต่อไป` }); | |
| score -= 3; | |
| } | |
| } | |
| } | |
| // String concatenation in loop | |
| if (trimmed.includes('+=') && (trimmed.includes("'") || trimmed.includes('"'))) { | |
| issues.push({ type: 'warning', msg: 'String concatenation — ควรใช้ list + join', line: i + 1, fix: 'ใช้ list.append() แล้ว "".join() แทน' }); | |
| score -= 3; | |
| } | |
| // Type hints missing in function | |
| if (trimmed.startsWith('def ') && !trimmed.includes('->') && !trimmed.includes(': ')) { | |
| // Check next few lines for type hints in params | |
| const funcLine = trimmed; | |
| if (!funcLine.includes(': int') && !funcLine.includes(': str') && !funcLine.includes(': float') && !funcLine.includes(': bool') && !funcLine.includes(': List') && !funcLine.includes(': Dict')) { | |
| issues.push({ type: 'info', msg: 'Function ไม่มี type hints', line: i + 1, fix: 'def func(param: type) -> return_type:' }); | |
| score -= 2; | |
| } | |
| } | |
| } | |
| // Check for if __name__ == "__main__" | |
| if (metrics.functions > 0 && !code.includes('if __name__')) { | |
| issues.push({ type: 'info', msg: 'ไม่มี if __name__ == "__main__" block', line: 1, fix: 'เพิ่ม if __name__ == "__main__": main() ที่ท้ายไฟล์' }); | |
| score -= 3; | |
| } | |
| // Check for list comprehension opportunity | |
| const hasLoopAppend = /for\s+\w+\s+in\s+.+:\s*\n\s*\w+\.append\(/.test(code); | |
| if (hasLoopAppend) { | |
| issues.push({ type: 'suggestion', msg: 'ใช้ for + append — ควรใช้ list comprehension', line: 0, fix: '[expr for item in iterable if condition]' }); | |
| score -= 3; | |
| } | |
| score = Math.max(0, Math.min(100, score)); | |
| return { score, issues, metrics, language: 'Python' }; | |
| } | |
| function analyzeJavaScript(code) { | |
| const lines = code.split('\n'); | |
| const issues = []; | |
| let score = 100; | |
| let metrics = { lines: lines.length, functions: 0, classes: 0, imports: 0, comments: 0, blankLines: 0 }; | |
| for (let i = 0; i < lines.length; i++) { | |
| const line = lines[i]; | |
| const trimmed = line.trim(); | |
| if (!trimmed) { metrics.blankLines++; continue; } | |
| if (trimmed.startsWith('//') || trimmed.startsWith('/*')) { metrics.comments++; continue; } | |
| if (trimmed.startsWith('import ') || trimmed.startsWith('require(')) { metrics.imports++; } | |
| if (trimmed.includes('function ') || trimmed.includes('=>')) { metrics.functions++; } | |
| if (trimmed.includes('class ')) { metrics.classes++; } | |
| // var instead of let/const | |
| if (trimmed.startsWith('var ')) { | |
| issues.push({ type: 'warning', msg: 'ใช้ var — ควรใช้ let หรือ const', line: i + 1, fix: 'เปลี่ยน var เป็น const (หรือ let ถ้ามีการเปลี่ยนค่า)' }); | |
| score -= 5; | |
| } | |
| // Deep nesting | |
| const indent = line.search(/\S/); | |
| if (indent >= 12) { | |
| issues.push({ type: 'critical', msg: 'โค้ดซ้อนลึกเกินไป', line: i + 1, fix: 'แยก function หรือใช้ early return' }); | |
| score -= 10; | |
| } | |
| // console.log | |
| if (trimmed.includes('console.log(')) { | |
| issues.push({ type: 'info', msg: 'พบ console.log — ควรลบก่อน production', line: i + 1, fix: 'ใช้ logging library หรือลบออก' }); | |
| score -= 2; | |
| } | |
| // Long lines | |
| if (line.length > 120) { | |
| issues.push({ type: 'warning', msg: `บรรทัดยาวเกินไป (${line.length} ตัวอักษร)`, line: i + 1, fix: 'แบ่งบรรทัด' }); | |
| score -= 2; | |
| } | |
| // == instead of === | |
| if (trimmed.includes('==') && !trimmed.includes('===') && !trimmed.includes('!==')) { | |
| issues.push({ type: 'warning', msg: 'ใช้ == — ควรใช้ === (strict equality)', line: i + 1, fix: 'เปลี่ยน == เป็น ===' }); | |
| score -= 4; | |
| } | |
| // Semicolon missing (simple check) | |
| if (trimmed && !trimmed.endsWith('{') && !trimmed.endsWith('}') && !trimmed.endsWith(',') && !trimmed.endsWith(';') && !trimmed.startsWith('//') && !trimmed.startsWith('/*') && !trimmed.startsWith('*') && !trimmed.startsWith('import') && !trimmed.startsWith('from')) { | |
| // Only flag simple statements | |
| if (/^(var|let|const|function|return|if|for|while|console)/.test(trimmed)) { | |
| issues.push({ type: 'info', msg: 'ขาด semicolon', line: i + 1, fix: 'เพิ่ม ; ท้ายบรรทัด' }); | |
| score -= 1; | |
| } | |
| } | |
| // Magic numbers | |
| const magicMatch = trimmed.match(/[^a-zA-Z_"](\d{2,})(?![a-zA-Z_"\d])/); | |
| if (magicMatch && !trimmed.startsWith('//')) { | |
| issues.push({ type: 'info', msg: `พบ magic number: ${magicMatch[1]}`, line: i + 1, fix: `กำหนดเป็น constant เช่น const MAX = ${magicMatch[1]}` }); | |
| score -= 2; | |
| } | |
| } | |
| score = Math.max(0, Math.min(100, score)); | |
| return { score, issues, metrics, language: 'JavaScript' }; | |
| } | |
| function analyzeHTML(code) { | |
| const lines = code.split('\n'); | |
| const issues = []; | |
| let score = 100; | |
| let metrics = { lines: lines.length, functions: 0, classes: 0, imports: 0, comments: 0, blankLines: 0 }; | |
| const fullCode = code.toLowerCase(); | |
| // Check DOCTYPE | |
| if (!fullCode.includes('<!doctype html>') && !fullCode.includes('<!DOCTYPE html>')) { | |
| issues.push({ type: 'warning', msg: 'ไม่มี <!DOCTYPE html>', line: 1, fix: 'เพิ่ม <!DOCTYPE html> ที่ต้นไฟล์' }); | |
| score -= 5; | |
| } | |
| // Check lang attribute | |
| if (fullCode.includes('<html') && !fullCode.includes('lang=')) { | |
| issues.push({ type: 'info', msg: '<html> ไม่มี lang attribute', line: 1, fix: '<html lang="th">' }); | |
| score -= 3; | |
| } | |
| // Check meta viewport | |
| if (!fullCode.includes('viewport')) { | |
| issues.push({ type: 'warning', msg: 'ไม่มี meta viewport (ไม่ responsive)', line: 1, fix: '<meta name="viewport" content="width=device-width, initial-scale=1.0">' }); | |
| score -= 5; | |
| } | |
| // Check title | |
| if (!fullCode.includes('<title>') || fullCode.match(/<title>\s*<\/title>/)) { | |
| issues.push({ type: 'critical', msg: 'ไม่มี <title> หรือ title ว่าง', line: 1, fix: '<title>Page Title</title>' }); | |
| score -= 8; | |
| } | |
| for (let i = 0; i < lines.length; i++) { | |
| const line = lines[i]; | |
| const lower = line.toLowerCase(); | |
| // Deprecated tags | |
| if (lower.includes('<font') || lower.includes('<center') || lower.includes('<marquee') || lower.includes('<blink')) { | |
| issues.push({ type: 'critical', msg: 'ใช้ deprecated tag', line: i + 1, fix: 'ใช้ CSS แทน (font-size, text-align, animation)' }); | |
| score -= 10; | |
| } | |
| // Inline styles | |
| if (lower.includes('style=')) { | |
| issues.push({ type: 'info', msg: 'ใช้ inline style', line: i + 1, fix: 'ย้ายไป CSS file หรือ <style> block' }); | |
| score -= 2; | |
| } | |
| // Unclosed tags (simple check) | |
| const selfClosing = ['br', 'hr', 'img', 'input', 'meta', 'link']; | |
| for (const tag of selfClosing) { | |
| const regex = new RegExp(`<${tag}(?![^>]*\/>)(?![^>]*>)`, 'gi'); | |
| if (regex.test(line) && !line.includes(`</${tag}>`)) { | |
| issues.push({ type: 'warning', msg: `Tag <${tag}> ไม่ปิด (ควรเป็น <${tag} />)`, line: i + 1, fix: `<${tag} />` }); | |
| score -= 3; | |
| } | |
| } | |
| // Missing alt on images | |
| if (lower.includes('<img') && !lower.includes('alt=')) { | |
| issues.push({ type: 'warning', msg: '<img> ไม่มี alt attribute', line: i + 1, fix: '<img src="..." alt="description">' }); | |
| score -= 5; | |
| } | |
| // Multiple <br> tags | |
| if ((lower.match(/<br/g) || []).length >= 3) { | |
| issues.push({ type: 'warning', msg: 'ใช้ <br> หลายครั้งติดกัน — ควรใช้ CSS margin/padding', line: i + 1, fix: 'ใช้ CSS margin หรือ padding แทน' }); | |
| score -= 3; | |
| } | |
| // Table without proper structure | |
| if (lower.includes('<table') && !lower.includes('<thead') && !lower.includes('<tbody')) { | |
| issues.push({ type: 'info', msg: '<table> ไม่มี <thead>/<tbody>', line: i + 1, fix: 'เพิ่ม <thead> และ <tbody> สำหรับ accessibility' }); | |
| score -= 3; | |
| } | |
| // Long lines | |
| if (line.length > 200) { | |
| issues.push({ type: 'info', msg: `บรรทัดยาวเกินไป (${line.length} ตัวอักษร)`, line: i + 1, fix: 'แบ่งบรรทัดให้อ่านง่าย' }); | |
| score -= 1; | |
| } | |
| } | |
| score = Math.max(0, Math.min(100, score)); | |
| return { score, issues, metrics, language: 'HTML' }; | |
| } | |
| // ============================================================ | |
| // MAIN ANALYSIS | |
| // ============================================================ | |
| let lastResult = null; | |
| function analyzeCode() { | |
| const code = document.getElementById('codeInput').value.trim(); | |
| if (!code) { | |
| alert('กรุณาใส่โค้ดที่ต้องการวิเคราะห์'); | |
| return; | |
| } | |
| const lang = document.getElementById('langSelect').value; | |
| let result; | |
| if (lang === 'python') result = analyzePython(code); | |
| else if (lang === 'javascript') result = analyzeJavaScript(code); | |
| else if (lang === 'html') result = analyzeHTML(code); | |
| lastResult = { ...result, code, timestamp: new Date().toISOString() }; | |
| renderResults(result); | |
| document.getElementById('exportBtn').classList.remove('hidden'); | |
| } | |
| function renderResults(result) { | |
| const { score, issues, metrics, language } = result; | |
| const scoreClass = score >= 70 ? 'score-high' : score >= 40 ? 'score-mid' : 'score-low'; | |
| const scoreColor = score >= 70 ? '#00ff88' : score >= 40 ? '#ffcc00' : '#ff4466'; | |
| const criticalCount = issues.filter(i => i.type === 'critical').length; | |
| const warningCount = issues.filter(i => i.type === 'warning').length; | |
| const infoCount = issues.filter(i => i.type === 'info').length; | |
| const suggestionCount = issues.filter(i => i.type === 'suggestion').length; | |
| const typeLabels = { critical: '🔴 CRITICAL', warning: '🟡 WARNING', info: '🔵 INFO', suggestion: '🟢 SUGGESTION' }; | |
| let html = ` | |
| <div class="score-display"> | |
| <div class="score-circle ${scoreClass}">${score}</div> | |
| <div style="font-size:14px;color:#8888aa">คะแนนคุณภาพโค้ด (${language})</div> | |
| </div> | |
| <div class="stats-grid"> | |
| <div class="stat-item"> | |
| <div class="stat-value" style="color:#ff4466">${criticalCount}</div> | |
| <div class="stat-label">Critical</div> | |
| </div> | |
| <div class="stat-item"> | |
| <div class="stat-value" style="color:#ffcc00">${warningCount}</div> | |
| <div class="stat-label">Warnings</div> | |
| </div> | |
| <div class="stat-item"> | |
| <div class="stat-value" style="color:#00d4ff">${infoCount + suggestionCount}</div> | |
| <div class="stat-label">Suggestions</div> | |
| </div> | |
| </div> | |
| <div class="issues-list">`; | |
| if (issues.length === 0) { | |
| html += `<div style="text-align:center;padding:20px;color:#00ff88">✅ โค้ดดีมาก! ไม่พบปัญหา</div>`; | |
| } else { | |
| // Sort: critical first, then warning, info, suggestion | |
| const sorted = [...issues].sort((a, b) => { | |
| const order = { critical: 0, warning: 1, info: 2, suggestion: 3 }; | |
| return order[a.type] - order[b.type]; | |
| }); | |
| for (const issue of sorted) { | |
| html += ` | |
| <div class="issue-item issue-${issue.type}"> | |
| <div class="issue-type">${typeLabels[issue.type]}</div> | |
| <div class="issue-msg">${issue.msg}</div> | |
| ${issue.line > 0 ? `<div class="issue-line">📍 บรรทัดที่ ${issue.line}</div>` : ''} | |
| <div class="issue-fix">💡 ${issue.fix}</div> | |
| </div>`; | |
| } | |
| } | |
| html += `</div>`; | |
| document.getElementById('resultsArea').innerHTML = html; | |
| } | |
| function exportReport() { | |
| if (!lastResult) return; | |
| const { score, issues, metrics, language, code, timestamp } = lastResult; | |
| const report = { | |
| tool: 'Chimera Symbiote v2.0', | |
| timestamp, | |
| language, | |
| score, | |
| totalLines: code.split('\n').length, | |
| issues: issues.map(i => ({ | |
| type: i.type, | |
| message: i.msg, | |
| line: i.line, | |
| fix: i.fix | |
| })), | |
| summary: { | |
| critical: issues.filter(i => i.type === 'critical').length, | |
| warning: issues.filter(i => i.type === 'warning').length, | |
| info: issues.filter(i => i.type === 'info').length, | |
| suggestion: issues.filter(i => i.type === 'suggestion').length | |
| } | |
| }; | |
| const blob = new Blob([JSON.stringify(report, null, 2)], { type: 'application/json' }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = `chimera-report-${Date.now()}.json`; | |
| a.click(); | |
| URL.revokeObjectURL(url); | |
| } | |
| // ============================================================ | |
| // PRICING | |
| // ============================================================ | |
| function showPricing() { | |
| document.getElementById('pricingModal').classList.add('active'); | |
| } | |
| function closePricing() { | |
| document.getElementById('pricingModal').classList.remove('active'); | |
| } | |
| function contactSales(plan) { | |
| alert(`สนใจแพ็กเกจ ${plan}\n\nกรุณาติดต่อผ่านช่องทางใน Landing Page`); | |
| closePricing(); | |
| } | |
| // Close modal on overlay click | |
| document.getElementById('pricingModal').addEventListener('click', function(e) { | |
| if (e.target === this) closePricing(); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |
Xet Storage Details
- Size:
- 37 kB
- Xet hash:
- d2af5e164867d1e020764423dfd571f0770e8b10612f5ebf08e8b49b4d1510de
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.