/** * Универсальная интеграция медиа-менеджера в AdminJS * Заменяет все стандартные диалоги выбора файлов на медиа-менеджер */ (function() { 'use strict'; console.log('🚀 Загружается универсальный медиа-менеджер для AdminJS...'); let mediaManagerModal = null; let currentCallback = null; // Создание модального окна медиа-менеджера function createMediaManagerModal() { if (mediaManagerModal) return mediaManagerModal; const modal = document.createElement('div'); modal.className = 'universal-media-modal'; modal.innerHTML = `

📁 Выбор изображения

`; // CSS стили const style = document.createElement('style'); style.textContent = ` .universal-media-modal { position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 10000; display: none; align-items: center; justify-content: center; } .universal-media-modal.active { display: flex; } .universal-media-overlay { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.8); } .universal-media-content { position: relative; width: 90vw; height: 90vh; max-width: 1200px; max-height: 800px; background: white; border-radius: 12px; overflow: hidden; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); } .universal-media-header { display: flex; align-items: center; justify-content: space-between; padding: 15px 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; } .universal-media-header h3 { margin: 0; font-size: 18px; font-weight: 600; } .universal-media-close { background: none; border: none; color: white; font-size: 24px; cursor: pointer; padding: 0; width: 30px; height: 30px; display: flex; align-items: center; justify-content: center; border-radius: 50%; transition: background-color 0.2s ease; } .universal-media-close:hover { background: rgba(255, 255, 255, 0.2); } .universal-media-frame { width: 100%; height: calc(100% - 60px); border: none; } /* Стили для кнопок медиа-менеджера */ .media-manager-btn { display: inline-flex; align-items: center; gap: 8px; padding: 8px 16px; background: #007bff; color: white; border: none; border-radius: 6px; font-size: 14px; font-weight: 500; cursor: pointer; transition: all 0.3s ease; margin: 5px; } .media-manager-btn:hover { background: #0056b3; transform: translateY(-1px); } .media-manager-btn.small { padding: 4px 8px; font-size: 12px; } /* Скрываем стандартные input[type="file"] */ .media-replaced input[type="file"] { display: none !important; } /* Стили для preview изображений */ .media-preview { max-width: 200px; max-height: 150px; border-radius: 6px; margin: 10px 0; border: 2px solid #e9ecef; object-fit: cover; } .media-preview.selected { border-color: #28a745; } `; if (!document.querySelector('#universal-media-styles')) { style.id = 'universal-media-styles'; document.head.appendChild(style); } // События const closeBtn = modal.querySelector('.universal-media-close'); const overlay = modal.querySelector('.universal-media-overlay'); closeBtn.addEventListener('click', closeMediaManager); overlay.addEventListener('click', closeMediaManager); document.body.appendChild(modal); mediaManagerModal = modal; return modal; } // Открытие медиа-менеджера function openMediaManager(callback, options = {}) { const modal = createMediaManagerModal(); currentCallback = callback; // Обновляем заголовок если нужно const header = modal.querySelector('.universal-media-header h3'); header.textContent = options.title || '📁 Выбор изображения'; modal.classList.add('active'); document.body.style.overflow = 'hidden'; } // Закрытие медиа-менеджера function closeMediaManager() { if (mediaManagerModal) { mediaManagerModal.classList.remove('active'); document.body.style.overflow = ''; currentCallback = null; } } // Обработка сообщений от медиа-менеджера window.addEventListener('message', function(event) { if (event.data.type === 'media-manager-selection' && currentCallback) { const files = event.data.files; if (files && files.length > 0) { currentCallback(files); closeMediaManager(); } } }); // Замена стандартных input[type="file"] на медиа-менеджер function replaceFileInputs() { const fileInputs = document.querySelectorAll('input[type="file"]:not(.media-replaced)'); fileInputs.forEach(input => { if (input.accept && !input.accept.includes('image')) { return; // Пропускаем не-изображения } input.classList.add('media-replaced'); // Создаем кнопку медиа-менеджера const button = document.createElement('button'); button.type = 'button'; button.className = 'media-manager-btn'; button.innerHTML = '📷 Выбрать изображение'; // Добавляем preview const preview = document.createElement('img'); preview.className = 'media-preview'; preview.style.display = 'none'; // Добавляем скрытый input для хранения пути const hiddenInput = document.createElement('input'); hiddenInput.type = 'hidden'; hiddenInput.name = input.name; hiddenInput.value = input.value || ''; // Событие клика button.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); openMediaManager((files) => { const file = files[0]; // Обновляем значения hiddenInput.value = file.url; input.value = file.url; // Показываем preview preview.src = file.url; preview.style.display = 'block'; preview.alt = file.name; // Обновляем кнопку button.innerHTML = '✏️ Заменить изображение'; // Добавляем кнопку удаления if (!button.nextElementSibling?.classList.contains('media-remove-btn')) { const removeBtn = document.createElement('button'); removeBtn.type = 'button'; removeBtn.className = 'media-manager-btn small'; removeBtn.style.background = '#dc3545'; removeBtn.innerHTML = '🗑️ Удалить'; removeBtn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); // Очищаем значения hiddenInput.value = ''; input.value = ''; // Скрываем preview preview.style.display = 'none'; // Восстанавливаем кнопку button.innerHTML = '📷 Выбрать изображение'; removeBtn.remove(); }); button.parentElement.insertBefore(removeBtn, button.nextSibling); } // Вызываем событие change для совместимости const changeEvent = new Event('change', { bubbles: true }); input.dispatchEvent(changeEvent); }, { title: input.dataset.title || 'Выбор изображения' }); }); // Вставляем элементы input.parentElement.insertBefore(button, input.nextSibling); input.parentElement.insertBefore(preview, button.nextSibling); input.parentElement.insertBefore(hiddenInput, preview.nextSibling); // Если есть начальное значение, показываем preview if (input.value) { preview.src = input.value; preview.style.display = 'block'; button.innerHTML = '✏️ Заменить изображение'; hiddenInput.value = input.value; } }); } // Замена кнопок "Browse" в формах AdminJS function replaceAdminJSBrowseButtons() { // Ищем кнопки загрузки файлов AdminJS const browseButtons = document.querySelectorAll('button[type="button"]:not(.media-replaced)'); browseButtons.forEach(button => { const buttonText = button.textContent.toLowerCase(); if (buttonText.includes('browse') || buttonText.includes('выбрать') || buttonText.includes('загрузить') || buttonText.includes('upload')) { button.classList.add('media-replaced'); // Заменяем обработчик клика const originalHandler = button.onclick; button.onclick = null; button.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); openMediaManager((files) => { const file = files[0]; // Ищем соответствующий input const container = button.closest('.form-group, .field, .input-group'); const input = container?.querySelector('input[type="text"], input[type="url"], input[type="hidden"]'); if (input) { input.value = file.url; // Вызываем событие change const changeEvent = new Event('change', { bubbles: true }); input.dispatchEvent(changeEvent); // Обновляем preview если есть const preview = container.querySelector('img'); if (preview) { preview.src = file.url; } } }); }); // Обновляем текст кнопки button.innerHTML = '📷 Медиа-менеджер'; } }); } // Интеграция с полями изображений AdminJS function integrateWithAdminJSImageFields() { // Ищем поля с атрибутом accept="image/*" const imageFields = document.querySelectorAll('input[accept*="image"]:not(.media-replaced)'); imageFields.forEach(field => { field.classList.add('media-replaced'); const container = field.closest('.form-group, .field'); if (!container) return; // Создаем кнопку медиа-менеджера const mediaBtn = document.createElement('button'); mediaBtn.type = 'button'; mediaBtn.className = 'media-manager-btn'; mediaBtn.innerHTML = '📷 Открыть медиа-менеджер'; mediaBtn.addEventListener('click', (e) => { e.preventDefault(); openMediaManager((files) => { const file = files[0]; // Обновляем поле field.value = file.url; // Создаем событие change const event = new Event('change', { bubbles: true }); field.dispatchEvent(event); // Если есть label, обновляем его const label = container.querySelector('label'); if (label && !label.querySelector('.selected-file')) { const selectedSpan = document.createElement('span'); selectedSpan.className = 'selected-file'; selectedSpan.style.cssText = 'color: #28a745; font-weight: 500; margin-left: 10px;'; selectedSpan.textContent = `✓ ${file.name}`; label.appendChild(selectedSpan); } }); }); // Вставляем кнопку после поля field.parentElement.insertBefore(mediaBtn, field.nextSibling); }); } // Основная функция инициализации function initMediaManager() { console.log('🔧 Инициализация медиа-менеджера...'); // Замена различных типов полей replaceFileInputs(); replaceAdminJSBrowseButtons(); integrateWithAdminJSImageFields(); console.log('✅ Медиа-менеджер инициализирован'); } // Наблюдатель за изменениями DOM const observer = new MutationObserver((mutations) => { let shouldReinit = false; mutations.forEach((mutation) => { if (mutation.type === 'childList') { mutation.addedNodes.forEach((node) => { if (node.nodeType === 1) { // Element node if (node.querySelector && ( node.querySelector('input[type="file"]') || node.querySelector('input[accept*="image"]') || node.querySelector('button[type="button"]') )) { shouldReinit = true; } } }); } }); if (shouldReinit) { setTimeout(initMediaManager, 100); } }); // Запуск function start() { // Ждем загрузки DOM if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initMediaManager); } else { initMediaManager(); } // Запуск наблюдателя observer.observe(document.body, { childList: true, subtree: true }); // Переинициализация при изменениях в SPA let lastUrl = location.href; setInterval(() => { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(initMediaManager, 500); } }, 1000); } // Глобальная функция для ручного открытия медиа-менеджера window.openUniversalMediaManager = function(callback, options) { openMediaManager(callback, options); }; // Запуск start(); })();