🐘
pixelEditor.php
Back
📝 Php ⚡ Executable Ctrl+S: Save • Ctrl+R: Run • Ctrl+F: Find
<?php error_reporting(E_ALL); ini_set('display_errors', 1); session_start(); if (empty($_SESSION['csrftoken'])) { $_SESSION['csrftoken'] = bin2hex(random_bytes(16)); } $CSRF = $_SESSION['csrftoken']; $file_path = isset($_GET['file']) ? $_GET['file'] : ''; $allowed_dir = realpath('.'); if (empty($file_path)) die("No file specified."); $file_path = realpath($file_path); if ($file_path === false || strpos($file_path, $allowed_dir) !== 0) die("Access denied."); if (!file_exists($file_path)) die("File not found."); if (!is_file($file_path)) die("Not a file."); $file_content = file_get_contents($file_path); $file_extension = strtolower(pathinfo($file_path, PATHINFO_EXTENSION)); if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (($_POST['csrf'] ?? '') !== $CSRF) die("CSRF failed"); if (isset($_POST['action']) && $_POST['action'] === 'save') { file_put_contents($file_path, $_POST['content'] ?? ''); $_SESSION['flash'] = "Saved!"; header("Location: " . $_SERVER['REQUEST_URI']); exit; } } $file_name = basename($file_path); ?><!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1"> <title><?= htmlspecialchars($file_name) ?></title> <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.32.9/ace.js"></script> <script src="fold-finder.js"></script> <style> *{box-sizing:border-box;margin:0;padding:0} html,body{height:100%;overflow:hidden} body{display:flex;flex-direction:column;background:#0f172a;color:#e5e7eb;font:13px system-ui,sans-serif} .bar{display:flex;gap:4px;padding:6px;background:#1e293b;border-bottom:1px solid #334155;flex-wrap:wrap} .bar button,.bar a{padding:6px 10px;background:#0f172a;color:#e5e7eb;border:1px solid #475569;border-radius:4px;font-size:13px;text-decoration:none;cursor:pointer;white-space:nowrap} .bar button:active{background:#334155} .spacer{flex:1;min-width:10px} .find{display:flex;gap:4px;padding:6px;background:#1e293b;border-bottom:1px solid #334155} .find input{flex:1;padding:6px;background:#0f172a;color:#e5e7eb;border:1px solid #475569;border-radius:4px} .find button{padding:6px 12px;background:#0f172a;color:#e5e7eb;border:1px solid #475569;border-radius:4px} .find .count{padding:6px;color:#94a3b8;font-size:12px} #editor{flex:1;width:100%;height:100%} .msg{padding:6px;background:#166534;color:#dcfce7;font-size:12px} .match_marker{position:absolute;background:rgba(255,224,102,.3);border:1px solid rgba(255,224,102,.5)} .match_marker_current{position:absolute;background:rgba(96,165,250,.3);border:1px solid rgba(96,165,250,.7)} .overlay{position:fixed;inset:0;background:rgba(0,0,0,.7);display:none;align-items:center;justify-content:center;z-index:1000} .overlay.open{display:flex} .modal{background:#1e293b;border:1px solid #475569;border-radius:8px;width:90%;max-width:500px;max-height:80vh;display:flex;flex-direction:column} .modal-head{display:flex;justify-content:space-between;align-items:center;padding:10px;border-bottom:1px solid #475569} .modal-head h3{font-size:14px;font-weight:600} .modal-head button{background:transparent;border:none;color:#e5e7eb;font-size:20px;cursor:pointer;padding:0;width:24px;height:24px;line-height:20px} .modal-body{padding:10px;flex:1;overflow:auto} .modal-body select{width:100%;padding:8px;background:#0f172a;color:#e5e7eb;border:1px solid #475569;border-radius:4px;margin-bottom:8px;font-size:13px} .modal-body textarea{width:100%;min-height:200px;background:#0f172a;color:#e5e7eb;border:1px solid #475569;border-radius:4px;padding:8px;font:13px monospace;resize:vertical} .modal-foot{padding:10px;border-top:1px solid #475569;display:flex;gap:6px;justify-content:flex-end} .modal-foot button{padding:8px 16px;background:#0f172a;color:#e5e7eb;border:1px solid #475569;border-radius:4px;cursor:pointer} .modal-foot button:active{background:#334155} .status{margin-top:8px;padding:6px;background:#0f172a;border:1px solid #475569;border-radius:4px;font-size:12px;color:#94a3b8;min-height:32px} </style> </head> <body> <?php if (!empty($_SESSION['flash'])): ?> <div class="msg"><?= htmlspecialchars($_SESSION['flash']); $_SESSION['flash'] = null; ?></div> <?php endif; ?> <div class="bar"> <button id="saveBtn" onclick="save()">Save</button> <button id="selectBtn" onclick="selectFold()">Select Fold</button> <button id="findPasteBtn" onclick="openFindPaste()">Find</button> <a href="siteExplorer.php?dir=<?= urlencode(dirname($file_path)) ?>">Back</a> <div class="spacer"></div> <span style="font-size:12px;color:#94a3b8"><?= htmlspecialchars($file_name) ?></span> </div> <div class="find"> <input id="query" placeholder="Search..."> <button onclick="prev()">◀</button> <button onclick="next()">▶</button> <span class="count" id="count"></span> </div> <div id="editor"></div> <form id="form" method="post" style="display:none"> <input type="hidden" name="csrf" value="<?= htmlspecialchars($CSRF) ?>"> <input type="hidden" name="action" value="save"> <input type="hidden" name="content" id="content"> </form> <div id="overlay" class="overlay"> <div class="modal"> <div class="modal-head"> <h3>Find Pasted Code</h3> <button onclick="closeFindPaste()">×</button> </div> <div class="modal-body"> <select id="langSelect"> <option value="auto">Auto-detect</option> <option value="js">JavaScript</option> <option value="php">PHP</option> <option value="html">HTML</option> <option value="python">Python</option> </select> <textarea id="pasteArea" placeholder="Paste function or code block here..."></textarea> <div class="status" id="findStatus">Paste code and click Find to locate it</div> </div> <div class="modal-foot"> <button onclick="findPasted()">Find</button> <button onclick="closeFindPaste()">Close</button> </div> </div> </div> <script> const ed = ace.edit('editor'); ed.setTheme('ace/theme/monokai'); ed.setOptions({fontSize:14,showPrintMargin:false,wrap:true}); ed.setValue(<?= json_encode($file_content) ?>,-1); const modes={php:'php',html:'html',css:'css',js:'javascript',json:'json',py:'python',sql:'sql',md:'markdown'}; const mode=modes['<?= $file_extension ?>']||'text'; if(mode!=='text')ed.getSession().setMode('ace/mode/'+mode); function save(){ document.getElementById('content').value=ed.getValue(); document.getElementById('form').submit(); } ed.commands.addCommand({ name:'save', bindKey:{win:'Ctrl-S',mac:'Command-S'}, exec:()=>save() }); // Find pasted overlay function openFindPaste(){ document.getElementById('overlay').classList.add('open'); document.getElementById('pasteArea').focus(); document.getElementById('findStatus').textContent = 'Paste code and click Find to locate it'; } function closeFindPaste(){ document.getElementById('overlay').classList.remove('open'); } function findPasted(){ const pastedText = document.getElementById('pasteArea').value.trim(); if(!pastedText){ document.getElementById('findStatus').textContent = 'Please paste some code first'; return; } let lang = document.getElementById('langSelect').value; if(lang === 'auto'){ lang = FoldFinder.detectLanguage(pastedText); } const fullText = ed.getValue(); const fold = FoldFinder.findFold(fullText, pastedText, lang); if(!fold){ document.getElementById('findStatus').textContent = 'Could not find matching code block'; return; } // Convert character positions to row/col const beforeStart = fullText.slice(0, fold.start); const beforeEnd = fullText.slice(0, fold.end); const startRow = (beforeStart.match(/\n/g) || []).length; const startCol = fold.start - beforeStart.lastIndexOf('\n') - 1; const endRow = (beforeEnd.match(/\n/g) || []).length; const endCol = fold.end - beforeEnd.lastIndexOf('\n') - 1; // Select the fold const R = ace.require('ace/range').Range; const range = new R(startRow, startCol, endRow, endCol); ed.selection.setRange(range); ed.scrollToLine(startRow, true, true, ()=>{}); document.getElementById('findStatus').textContent = `Found at line ${startRow + 1} (${Math.round(fold.matchRatio * 100)}% match, ${lang})`; setTimeout(() => closeFindPaste(), 1500); } // Fold selection logic let lastFoldLevel = -1; let lastCursorPos = null; function selectFold(){ const pos = ed.getCursorPosition(); const session = ed.getSession(); const R = ace.require('ace/range').Range; if(!lastCursorPos || lastCursorPos.row !== pos.row || lastCursorPos.column !== pos.column){ lastFoldLevel = -1; } lastCursorPos = {row: pos.row, column: pos.column}; const folds = findFoldsAtCursor(pos.row); if(folds.length === 0){ const line = session.getLine(pos.row); ed.selection.setRange(new R(pos.row, 0, pos.row, line.length)); lastFoldLevel = -1; return; } lastFoldLevel++; if(lastFoldLevel >= folds.length){ lastFoldLevel = 0; } const fold = folds[lastFoldLevel]; ed.selection.setRange(new R(fold.start, 0, fold.end, session.getLine(fold.end).length)); ed.scrollToLine(fold.start, true, true, ()=>{}); } function findFoldsAtCursor(row){ const session = ed.getSession(); const lines = session.getDocument().getAllLines(); const folds = []; const opens = [ /^\s*function\s+\w+/, /^\s*(?:const|let|var)\s+\w+\s*=\s*(?:function|\([^)]*\)\s*=>)/, /^\s*(?:if|while|for|foreach)\s*\(/, /^\s*class\s+\w+/, /^\s*def\s+\w+/, /^\s*<(\w+)[^>]*>/, /\{\s*$/, /^\s*switch\s*\(/, /^\s*try\s*\{/ ]; const phpOpen = /^\s*<\?php/; const phpClose = /^\s*\?>/; for(let i = 0; i < lines.length; i++){ const line = lines[i]; const indent = line.match(/^\s*/)[0].length; let isOpen = false; let tagName = null; const tagMatch = line.match(/^\s*<(\w+)[^>]*>/); if(tagMatch && !line.match(/<\/\1>/)){ tagName = tagMatch[1]; isOpen = true; } if(phpOpen.test(line)){ isOpen = true; tagName = 'php'; } if(!isOpen){ for(let pattern of opens){ if(pattern.test(line)){ isOpen = true; break; } } } if(isOpen && i <= row){ let closeRow = -1; if(tagName === 'php'){ for(let j = i + 1; j < lines.length; j++){ if(phpClose.test(lines[j])){ closeRow = j; break; } } } else if(tagName){ let depth = 1; for(let j = i + 1; j < lines.length; j++){ if(lines[j].match(new RegExp('<' + tagName + '\\b'))){ depth++; } if(lines[j].match(new RegExp('</' + tagName + '>'))){ depth--; if(depth === 0){ closeRow = j; break; } } } } else { let depth = (line.match(/\{/g) || []).length - (line.match(/\}/g) || []).length; for(let j = i + 1; j < lines.length && depth > 0; j++){ const jLine = lines[j]; depth += (jLine.match(/\{/g) || []).length; depth -= (jLine.match(/\}/g) || []).length; if(depth === 0){ closeRow = j; break; } } } if(closeRow > row){ folds.push({start: i, end: closeRow, indent: indent}); } } } folds.sort((a, b) => (a.end - a.start) - (b.end - b.start)); return folds; } // Search functionality let state={matches:[],idx:-1,markers:[]}; function clear(){ state.markers.forEach(id=>ed.getSession().removeMarker(id)); state.markers=[]; } function mark(){ clear(); const R=ace.require('ace/range').Range; state.matches.forEach((m,i)=>{ const r=new R(m.r,m.s,m.r,m.e); const cls=i===state.idx?'match_marker_current':'match_marker'; state.markers.push(ed.getSession().addMarker(r,cls,'text')); }); } function go(){ if(state.idx<0||!state.matches.length)return; const m=state.matches[state.idx]; const R=ace.require('ace/range').Range; const r=new R(m.r,m.s,m.r,m.e); ed.selection.setRange(r,false); ed.scrollToLine(m.r,true,true,()=>{}); mark(); document.getElementById('count').textContent=state.matches.length?`${state.idx+1}/${state.matches.length}`:''; } function search(){ const q=document.getElementById('query').value.trim(); if(!q){state={matches:[],idx:-1,markers:[]};clear();document.getElementById('count').textContent='';return;} const rx=new RegExp(q.replace(/[.*+?^${}()|[\]\\]/g,'\\$&'),'gi'); const lines=ed.getSession().getDocument().getAllLines(); const matches=[]; for(let r=0;r<lines.length;r++){ let m;rx.lastIndex=0; while((m=rx.exec(lines[r]))){ matches.push({r:r,s:m.index,e:m.index+m[0].length}); if(m.index===rx.lastIndex)rx.lastIndex++; } } state.matches=matches; state.idx=matches.length?0:-1; go(); } function next(){ if(!state.matches.length)return; state.idx=(state.idx+1)%state.matches.length; go(); } function prev(){ if(!state.matches.length)return; state.idx=(state.idx-1+state.matches.length)%state.matches.length; go(); } document.getElementById('query').addEventListener('input',search); document.getElementById('query').addEventListener('keydown',e=>{ if(e.key==='Enter'){ e.preventDefault(); e.shiftKey?prev():next(); } }); // Close overlay on outside click document.getElementById('overlay').addEventListener('click', (e) => { if(e.target.id === 'overlay') closeFindPaste(); }); </script> </body> </html>