предпросмотр и скрипт для сборки бинарей
This commit is contained in:
16
build.sh
Executable file
16
build.sh
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo "Building Trashbox CGI scripts..."
|
||||||
|
|
||||||
|
echo "Building index.cgi..."
|
||||||
|
gcc -Wall -O2 -o index.cgi index.c -DBASE_WWW_PATH='"/home/romkazvo/www"' -DTEMPLATE_PATH='"/home/romkazvo/www/cgi-bin/template.html"'
|
||||||
|
|
||||||
|
echo "Building style.cgi..."
|
||||||
|
gcc -Wall -O2 -o style.cgi style.c -DCSS_PATH='"/home/romkazvo/www/cgi-bin/style.css"'
|
||||||
|
|
||||||
|
echo "Building status.cgi..."
|
||||||
|
gcc -Wall -O2 -o status.cgi status.c
|
||||||
|
|
||||||
|
echo "Setting permissions..."
|
||||||
|
chmod +x *.cgi
|
||||||
|
|
||||||
|
echo "Build complete"
|
||||||
81
index.c
81
index.c
@@ -1,5 +1,4 @@
|
|||||||
// /home/romkazvo/www/cgi-bin/index.c
|
// /home/romkazvo/www/cgi-bin/index.c
|
||||||
// Основной файловый сервер
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -23,6 +22,7 @@ typedef struct {
|
|||||||
char file_url[1024];
|
char file_url[1024];
|
||||||
} entry_t;
|
} entry_t;
|
||||||
|
|
||||||
|
// Быстрый буферизированный вывод
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *data;
|
char *data;
|
||||||
size_t size;
|
size_t size;
|
||||||
@@ -90,6 +90,47 @@ const char* get_file_icon(const char* filename) {
|
|||||||
return "📄";
|
return "📄";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Проверяет, можно ли открыть файл в браузере
|
||||||
|
int is_previewable(const char* filename) {
|
||||||
|
const char *ext = strrchr(filename, '.');
|
||||||
|
if (!ext) return 0;
|
||||||
|
|
||||||
|
ext++;
|
||||||
|
|
||||||
|
// Изображения
|
||||||
|
if (strcasecmp(ext, "jpg") == 0 || strcasecmp(ext, "jpeg") == 0 ||
|
||||||
|
strcasecmp(ext, "png") == 0 || strcasecmp(ext, "gif") == 0 ||
|
||||||
|
strcasecmp(ext, "webp") == 0 || strcasecmp(ext, "bmp") == 0 ||
|
||||||
|
strcasecmp(ext, "svg") == 0 || strcasecmp(ext, "ico") == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
// Текстовые файлы
|
||||||
|
if (strcasecmp(ext, "txt") == 0 || strcasecmp(ext, "md") == 0 ||
|
||||||
|
strcasecmp(ext, "html") == 0 || strcasecmp(ext, "htm") == 0 ||
|
||||||
|
strcasecmp(ext, "css") == 0 || strcasecmp(ext, "js") == 0 ||
|
||||||
|
strcasecmp(ext, "json") == 0 || strcasecmp(ext, "xml") == 0 ||
|
||||||
|
strcasecmp(ext, "csv") == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
// PDF
|
||||||
|
if (strcasecmp(ext, "pdf") == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
// Аудио
|
||||||
|
if (strcasecmp(ext, "mp3") == 0 || strcasecmp(ext, "wav") == 0 ||
|
||||||
|
strcasecmp(ext, "ogg") == 0 || strcasecmp(ext, "flac") == 0 ||
|
||||||
|
strcasecmp(ext, "m4a") == 0 || strcasecmp(ext, "aac") == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
// Видео
|
||||||
|
if (strcasecmp(ext, "mp4") == 0 || strcasecmp(ext, "webm") == 0 ||
|
||||||
|
strcasecmp(ext, "ogv") == 0 || strcasecmp(ext, "mov") == 0 ||
|
||||||
|
strcasecmp(ext, "avi") == 0 || strcasecmp(ext, "mkv") == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int compare_entries(const void *a, const void *b) {
|
int compare_entries(const void *a, const void *b) {
|
||||||
const entry_t *entryA = (const entry_t *)a;
|
const entry_t *entryA = (const entry_t *)a;
|
||||||
const entry_t *entryB = (const entry_t *)b;
|
const entry_t *entryB = (const entry_t *)b;
|
||||||
@@ -251,6 +292,7 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Предварительная обработка записей
|
||||||
while ((entry = readdir(dir)) != NULL && entry_count < MAX_ENTRIES) {
|
while ((entry = readdir(dir)) != NULL && entry_count < MAX_ENTRIES) {
|
||||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||||
continue;
|
continue;
|
||||||
@@ -268,6 +310,7 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|||||||
entries[entry_count].size = 0;
|
entries[entry_count].size = 0;
|
||||||
strcpy(entries[entry_count].icon, "📁");
|
strcpy(entries[entry_count].icon, "📁");
|
||||||
|
|
||||||
|
// Предварительно кодируем путь для папок
|
||||||
char new_path[2048];
|
char new_path[2048];
|
||||||
snprintf(new_path, sizeof(new_path), "%s%s%s", display_path,
|
snprintf(new_path, sizeof(new_path), "%s%s%s", display_path,
|
||||||
display_path[0] ? "/" : "", entries[entry_count].name);
|
display_path[0] ? "/" : "", entries[entry_count].name);
|
||||||
@@ -279,6 +322,7 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|||||||
entries[entry_count].size = file_stat.st_size;
|
entries[entry_count].size = file_stat.st_size;
|
||||||
strcpy(entries[entry_count].icon, get_file_icon(entry->d_name));
|
strcpy(entries[entry_count].icon, get_file_icon(entry->d_name));
|
||||||
|
|
||||||
|
// Предварительно формируем URL для файлов
|
||||||
snprintf(entries[entry_count].file_url, sizeof(entries[0].file_url),
|
snprintf(entries[entry_count].file_url, sizeof(entries[0].file_url),
|
||||||
"%s%s%s", display_path, display_path[0] ? "/" : "", entries[entry_count].name);
|
"%s%s%s", display_path, display_path[0] ? "/" : "", entries[entry_count].name);
|
||||||
|
|
||||||
@@ -305,6 +349,7 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|||||||
|
|
||||||
buffer_append(buf, "<ul>\n");
|
buffer_append(buf, "<ul>\n");
|
||||||
|
|
||||||
|
// Вывод папок с предварительно закодированными путями
|
||||||
if (dir_count > 0) {
|
if (dir_count > 0) {
|
||||||
char section_title[128];
|
char section_title[128];
|
||||||
snprintf(section_title, sizeof(section_title), " <li class=\"section-title\">📁 Папки (%d)</li>\n", dir_count);
|
snprintf(section_title, sizeof(section_title), " <li class=\"section-title\">📁 Папки (%d)</li>\n", dir_count);
|
||||||
@@ -330,6 +375,7 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Вывод файлов с предварительно сформированными URL
|
||||||
if (file_count > 0) {
|
if (file_count > 0) {
|
||||||
char section_title[128];
|
char section_title[128];
|
||||||
snprintf(section_title, sizeof(section_title), " <li class=\"section-title\">📄 Файлы (%d)</li>\n", file_count);
|
snprintf(section_title, sizeof(section_title), " <li class=\"section-title\">📄 Файлы (%d)</li>\n", file_count);
|
||||||
@@ -341,21 +387,34 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|||||||
format_size(entries[i].size, size_str);
|
format_size(entries[i].size, size_str);
|
||||||
|
|
||||||
buffer_append(buf, " <li>\n");
|
buffer_append(buf, " <li>\n");
|
||||||
char file_link[512];
|
buffer_append(buf, " <div class=\"file-row\">\n");
|
||||||
snprintf(file_link, sizeof(file_link),
|
|
||||||
" <a class=\"file-link\" href=\"/%s\" download>\n",
|
// Основная ссылка для скачивания
|
||||||
entries[i].file_url);
|
buffer_append(buf, " <a class=\"file-link\" href=\"/");
|
||||||
buffer_append(buf, file_link);
|
buffer_append(buf, entries[i].file_url);
|
||||||
buffer_append(buf, " <span class=\"file-icon\">");
|
buffer_append(buf, "\" download>\n");
|
||||||
|
buffer_append(buf, " <span class=\"file-icon\">");
|
||||||
buffer_append(buf, entries[i].icon);
|
buffer_append(buf, entries[i].icon);
|
||||||
buffer_append(buf, "</span>\n");
|
buffer_append(buf, "</span>\n");
|
||||||
buffer_append(buf, " <span class=\"file-name\">");
|
buffer_append(buf, " <span class=\"file-name\">");
|
||||||
buffer_append(buf, entries[i].name);
|
buffer_append(buf, entries[i].name);
|
||||||
buffer_append(buf, "</span>\n");
|
buffer_append(buf, "</span>\n");
|
||||||
buffer_append(buf, " </a>\n");
|
buffer_append(buf, " </a>\n");
|
||||||
buffer_append(buf, " <span class=\"size\">");
|
|
||||||
|
buffer_append(buf, " <div class=\"file-controls\">\n");
|
||||||
|
|
||||||
|
// Кнопка предпросмотра (если поддерживается)
|
||||||
|
if (is_previewable(entries[i].name)) {
|
||||||
|
buffer_append(buf, " <button class=\"preview-btn\" onclick=\"openPreview('/");
|
||||||
|
buffer_append(buf, entries[i].file_url);
|
||||||
|
buffer_append(buf, "')\" title=\"Открыть в браузере\">👁</button>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_append(buf, " <span class=\"size\">");
|
||||||
buffer_append(buf, size_str);
|
buffer_append(buf, size_str);
|
||||||
buffer_append(buf, "</span>\n");
|
buffer_append(buf, "</span>\n");
|
||||||
|
buffer_append(buf, " </div>\n");
|
||||||
|
buffer_append(buf, " </div>\n");
|
||||||
buffer_append(buf, " </li>\n");
|
buffer_append(buf, " </li>\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -363,6 +422,7 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|||||||
|
|
||||||
buffer_append(buf, "</ul>\n");
|
buffer_append(buf, "</ul>\n");
|
||||||
|
|
||||||
|
// Статистика
|
||||||
char total_size_str[32];
|
char total_size_str[32];
|
||||||
format_size(total_size, total_size_str);
|
format_size(total_size, total_size_str);
|
||||||
char stats[256];
|
char stats[256];
|
||||||
@@ -394,6 +454,7 @@ void print_template(const char *base_path, const char *display_path) {
|
|||||||
}
|
}
|
||||||
fclose(file);
|
fclose(file);
|
||||||
|
|
||||||
|
// Один быстрый вывод вместо множества printf
|
||||||
fwrite(output.data, 1, output.size, stdout);
|
fwrite(output.data, 1, output.size, stdout);
|
||||||
buffer_free(&output);
|
buffer_free(&output);
|
||||||
}
|
}
|
||||||
|
|||||||
287
style.css
287
style.css
@@ -187,32 +187,48 @@ li:hover {
|
|||||||
background-color: var(--hover-bg);
|
background-color: var(--hover-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
/* Стили для строки файла */
|
||||||
text-decoration: none;
|
.file-row {
|
||||||
flex-grow: 1;
|
display: flex;
|
||||||
display: flex;
|
align-items: center;
|
||||||
align-items: center;
|
justify-content: space-between;
|
||||||
min-width: 0;
|
flex-grow: 1;
|
||||||
padding-right: 0.5rem;
|
min-width: 0;
|
||||||
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-link {
|
.file-link {
|
||||||
color: var(--accent-blue);
|
color: var(--accent-blue);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
text-decoration: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-grow: 1;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dir-link {
|
.dir-link {
|
||||||
color: var(--accent-green);
|
color: var(--accent-green);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
text-decoration: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.size {
|
.file-controls {
|
||||||
color: var(--text-secondary);
|
display: flex;
|
||||||
font-size: 0.85rem;
|
align-items: center;
|
||||||
margin-left: auto;
|
gap: 0.75rem;
|
||||||
padding-left: 0.5rem;
|
flex-shrink: 0;
|
||||||
flex-shrink: 0;
|
}
|
||||||
white-space: nowrap;
|
|
||||||
|
.size {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
min-width: 70px;
|
||||||
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-icon {
|
.file-icon {
|
||||||
@@ -299,6 +315,186 @@ a {
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Стили для кнопки предпросмотра */
|
||||||
|
.preview-btn {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 6px;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-btn:hover {
|
||||||
|
background: var(--accent-blue);
|
||||||
|
color: white;
|
||||||
|
border-color: var(--accent-blue);
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Модальное окно предпросмотра */
|
||||||
|
.preview-modal {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 2000;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0,0,0,0.8);
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
margin: 2% auto;
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 90%;
|
||||||
|
height: 90%;
|
||||||
|
max-width: 1200px;
|
||||||
|
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Размеры для разных типов контента */
|
||||||
|
.preview-content.image-type {
|
||||||
|
max-width: 90%;
|
||||||
|
max-height: 90%;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content.text-type {
|
||||||
|
max-width: 800px;
|
||||||
|
max-height: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content.audio-type {
|
||||||
|
max-width: 500px;
|
||||||
|
max-height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content.video-type {
|
||||||
|
max-width: 90%;
|
||||||
|
max-height: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content.pdf-type {
|
||||||
|
max-width: 90%;
|
||||||
|
max-height: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content.default-type {
|
||||||
|
max-width: 90%;
|
||||||
|
max-height: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 1rem;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
background: var(--bg-primary);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-title {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary);
|
||||||
|
margin: 0;
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-preview {
|
||||||
|
background: #dc3545;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-preview:hover {
|
||||||
|
background: #c82333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-iframe {
|
||||||
|
flex-grow: 1;
|
||||||
|
border: none;
|
||||||
|
background: white;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-iframe.pdf {
|
||||||
|
background: var(--bg-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Адаптивность */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.preview-content {
|
||||||
|
width: 95%;
|
||||||
|
height: 95%;
|
||||||
|
margin: 2.5% auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content.image-type {
|
||||||
|
max-width: 95%;
|
||||||
|
max-height: 95%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content.text-type {
|
||||||
|
max-width: 95%;
|
||||||
|
max-height: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content.audio-type {
|
||||||
|
max-width: 95%;
|
||||||
|
max-height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content.video-type {
|
||||||
|
max-width: 95%;
|
||||||
|
max-height: 95%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content.pdf-type {
|
||||||
|
max-width: 95%;
|
||||||
|
max-height: 95%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-header {
|
||||||
|
padding: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-title {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
.theme-toggle {
|
.theme-toggle {
|
||||||
top: 5px;
|
top: 5px;
|
||||||
@@ -331,9 +527,15 @@ a {
|
|||||||
li {
|
li {
|
||||||
padding: 0.75rem 0.25rem;
|
padding: 0.75rem 0.25rem;
|
||||||
}
|
}
|
||||||
|
.file-row {
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
.file-controls {
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
.size {
|
.size {
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
padding-left: 0.25rem;
|
min-width: 60px;
|
||||||
}
|
}
|
||||||
.file-icon {
|
.file-icon {
|
||||||
margin-right: 0.75rem;
|
margin-right: 0.75rem;
|
||||||
@@ -352,6 +554,34 @@ a {
|
|||||||
padding: 0.75rem;
|
padding: 0.75rem;
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
}
|
}
|
||||||
|
.preview-btn {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
.preview-content {
|
||||||
|
width: 98%;
|
||||||
|
height: 98%;
|
||||||
|
margin: 1% auto;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content.image-type,
|
||||||
|
.preview-content.video-type,
|
||||||
|
.preview-content.pdf-type {
|
||||||
|
max-width: 98%;
|
||||||
|
max-height: 98%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content.text-type {
|
||||||
|
max-width: 98%;
|
||||||
|
max-height: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content.audio-type {
|
||||||
|
max-width: 98%;
|
||||||
|
max-height: 250px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 360px) {
|
@media (max-width: 360px) {
|
||||||
@@ -368,4 +598,19 @@ a {
|
|||||||
.size {
|
.size {
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
}
|
}
|
||||||
|
.file-row {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
.file-controls {
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
.file-link {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content.audio-type {
|
||||||
|
max-height: 200px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
111
template.html
111
template.html
@@ -80,6 +80,17 @@
|
|||||||
|
|
||||||
<button class="scroll-top" onclick="scrollToTop()">↑</button>
|
<button class="scroll-top" onclick="scrollToTop()">↑</button>
|
||||||
|
|
||||||
|
<!-- Модальное окно предпросмотра -->
|
||||||
|
<div id="previewModal" class="preview-modal">
|
||||||
|
<div class="preview-content" id="previewContent">
|
||||||
|
<div class="preview-header">
|
||||||
|
<h3 class="preview-title" id="previewTitle">Предпросмотр файла</h3>
|
||||||
|
<button class="close-preview" onclick="closePreview()">×</button>
|
||||||
|
</div>
|
||||||
|
<iframe id="previewFrame" class="preview-iframe" sandbox="allow-scripts allow-same-origin"></iframe>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function scrollToTop() {
|
function scrollToTop() {
|
||||||
window.scrollTo({
|
window.scrollTo({
|
||||||
@@ -112,6 +123,106 @@
|
|||||||
document.cookie = `theme=${newTheme}; expires=${date.toUTCString()}; path=/`;
|
document.cookie = `theme=${newTheme}; expires=${date.toUTCString()}; path=/`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Функции для предпросмотра файлов
|
||||||
|
function openPreview(fileUrl) {
|
||||||
|
const modal = document.getElementById('previewModal');
|
||||||
|
const frame = document.getElementById('previewFrame');
|
||||||
|
const content = document.getElementById('previewContent');
|
||||||
|
const title = document.getElementById('previewTitle');
|
||||||
|
|
||||||
|
// Получаем имя файла из URL
|
||||||
|
const fileName = fileUrl.split('/').pop();
|
||||||
|
title.textContent = `Предпросмотр: ${fileName}`;
|
||||||
|
|
||||||
|
// Определяем тип файла для адаптивного масштабирования
|
||||||
|
const fileExt = fileName.split('.').pop().toLowerCase();
|
||||||
|
const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'svg', 'ico'];
|
||||||
|
const textTypes = ['txt', 'md', 'html', 'htm', 'css', 'js', 'json', 'xml', 'csv'];
|
||||||
|
const audioTypes = ['mp3', 'wav', 'ogg', 'flac', 'm4a', 'aac'];
|
||||||
|
const videoTypes = ['mp4', 'webm', 'ogv', 'mov', 'avi', 'mkv'];
|
||||||
|
const isPDF = fileExt === 'pdf';
|
||||||
|
|
||||||
|
// Устанавливаем размеры в зависимости от типа файла
|
||||||
|
if (imageTypes.includes(fileExt)) {
|
||||||
|
// Для изображений - автоматический размер
|
||||||
|
content.style.width = 'auto';
|
||||||
|
content.style.height = 'auto';
|
||||||
|
content.style.maxWidth = '90vw';
|
||||||
|
content.style.maxHeight = '90vh';
|
||||||
|
} else if (textTypes.includes(fileExt)) {
|
||||||
|
// Для текстовых файлов - компактное окно
|
||||||
|
content.style.width = '80vw';
|
||||||
|
content.style.height = '70vh';
|
||||||
|
content.style.maxWidth = '800px';
|
||||||
|
content.style.maxHeight = '600px';
|
||||||
|
} else if (audioTypes.includes(fileExt)) {
|
||||||
|
// Для аудио - маленькое окно
|
||||||
|
content.style.width = '500px';
|
||||||
|
content.style.height = '300px';
|
||||||
|
content.style.maxWidth = '90vw';
|
||||||
|
content.style.maxHeight = '400px';
|
||||||
|
} else if (videoTypes.includes(fileExt)) {
|
||||||
|
// Для видео - большое окно
|
||||||
|
content.style.width = '90vw';
|
||||||
|
content.style.height = '80vh';
|
||||||
|
content.style.maxWidth = '1200px';
|
||||||
|
content.style.maxHeight = '800px';
|
||||||
|
} else if (isPDF) {
|
||||||
|
// Для PDF - большое окно
|
||||||
|
content.style.width = '90vw';
|
||||||
|
content.style.height = '90vh';
|
||||||
|
content.style.maxWidth = '1200px';
|
||||||
|
content.style.maxHeight = '900px';
|
||||||
|
frame.classList.add('pdf');
|
||||||
|
} else {
|
||||||
|
// По умолчанию - средний размер
|
||||||
|
content.style.width = '80vw';
|
||||||
|
content.style.height = '80vh';
|
||||||
|
content.style.maxWidth = '1000px';
|
||||||
|
content.style.maxHeight = '800px';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Просто открываем файл напрямую в iframe
|
||||||
|
frame.src = fileUrl;
|
||||||
|
|
||||||
|
// Показываем модальное окно
|
||||||
|
modal.style.display = 'block';
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
}
|
||||||
|
|
||||||
|
function closePreview() {
|
||||||
|
const modal = document.getElementById('previewModal');
|
||||||
|
const frame = document.getElementById('previewFrame');
|
||||||
|
const content = document.getElementById('previewContent');
|
||||||
|
|
||||||
|
// Скрываем модальное окно и сбрасываем iframe
|
||||||
|
modal.style.display = 'none';
|
||||||
|
frame.src = '';
|
||||||
|
frame.classList.remove('pdf');
|
||||||
|
|
||||||
|
// Сбрасываем стили
|
||||||
|
content.style.width = '';
|
||||||
|
content.style.height = '';
|
||||||
|
content.style.maxWidth = '';
|
||||||
|
content.style.maxHeight = '';
|
||||||
|
|
||||||
|
document.body.style.overflow = 'auto';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Закрытие модального окна по клику вне контента
|
||||||
|
document.getElementById('previewModal').addEventListener('click', function(e) {
|
||||||
|
if (e.target === this) {
|
||||||
|
closePreview();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Закрытие по ESC
|
||||||
|
document.addEventListener('keydown', function(e) {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
closePreview();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
const savedTheme = getCookie('theme');
|
const savedTheme = getCookie('theme');
|
||||||
const button = document.querySelector('.theme-toggle');
|
const button = document.querySelector('.theme-toggle');
|
||||||
|
|||||||
Reference in New Issue
Block a user