MediaWiki:Common.js: відмінності між версіями

Матеріал з darnytsa_hero
Перейти до навігації Перейти до пошуку
Немає опису редагування
Мітка: Ручний відкіт
Немає опису редагування
Рядок 106: Рядок 106:
//OVERLAY  
//OVERLAY  
(function() {
(function() {
     // Функция для создания кастомного overlay (только для обычных картинок без MediaViewer)
    'use strict';
     function showCustomOverlay(url) {
   
    // Конфигурация - легко настраивается
    const config = {
        overlayBg: 'rgba(0,0,0,0.92)',
        maxWidth: '95%',
        maxHeight: '95%',
        closeBtnText: '✕ Закрити',
        closeBtnStyle: {
            background: '#d32f2f',
            color: 'white',
            border: 'none',
            borderRadius: '4px',
            padding: '12px 24px',
            fontSize: '16px',
            cursor: 'pointer',
            marginTop: '20px',
            transition: 'background 0.3s ease'
        },
        imageStyle: {
            borderRadius: '8px',
            boxShadow: '0 4px 20px rgba(0,0,0,0.3)'
        },
        // Исключения - классы/атрибуты, которые не должны открывать оверлей
        excludeSelectors: [
            '.no-overlay',
            '[data-no-overlay]',
            '.mw-editsection img',
            '.sprite',
            '.icon'
        ]
    };
 
    let currentOverlay = null;
 
     // Функция создания оверлея
     function createOverlay() {
         const overlay = document.createElement('div');
         const overlay = document.createElement('div');
         overlay.style.position = 'fixed';
         overlay.className = 'custom-image-overlay';
         overlay.style.top = 0;
         overlay.style.cssText = `
        overlay.style.left = 0;
            position: fixed;
        overlay.style.width = '100%';
            top: 0;
        overlay.style.height = '100%';
            left: 0;
        overlay.style.background = 'rgba(0,0,0,0.85)';
            width: 100%;
        overlay.style.display = 'flex';
            height: 100%;
         overlay.style.justifyContent = 'center';
            background: ${config.overlayBg};
         overlay.style.alignItems = 'center';
            display: none;
        overlay.style.flexDirection = 'column';
            justify-content: center;
         overlay.style.zIndex = 9999;
            align-items: center;
            flex-direction: column;
            z-index: 10000;
            cursor: pointer;
        `;
 
         const imgContainer = document.createElement('div');
         imgContainer.style.cssText = `
            position: relative;
            max-width: ${config.maxWidth};
            max-height: ${config.maxHeight};
            display: flex;
            justify-content: center;
            align-items: center;
         `;


         const img = document.createElement('img');
         const img = document.createElement('img');
         img.src = url;
         img.style.cssText = `
        img.style.maxWidth = '90%';
            max-width: 100%;
        img.style.maxHeight = '90%';
            max-height: 100%;
        overlay.appendChild(img);
            object-fit: contain;
            ${Object.entries(config.imageStyle).map(([key, value]) =>
                `${key.replace(/([A-Z])/g, '-$1').toLowerCase()}: ${value};`).join('')}
        `;


         const closeBtn = document.createElement('button');
         const closeBtn = document.createElement('button');
         closeBtn.innerText = 'Закрити';
         closeBtn.innerHTML = config.closeBtnText;
         closeBtn.style.marginTop = '20px';
         closeBtn.style.cssText = Object.entries(config.closeBtnStyle).map(([key, value]) =>
        closeBtn.style.padding = '10px 20px';
            `${key.replace(/([A-Z])/g, '-$1').toLowerCase()}: ${value};`).join('');
        closeBtn.style.fontSize = '18px';
       
         closeBtn.style.cursor = 'pointer';
        // Добавляем hover эффект для кнопки
        closeBtn.style.background = '#f44336';
         closeBtn.onmouseover = () => closeBtn.style.background = '#b71c1c';
         closeBtn.style.color = 'white';
         closeBtn.onmouseout = () => closeBtn.style.background = config.closeBtnStyle.background;
        closeBtn.style.border = 'none';
 
        closeBtn.style.borderRadius = '5px';
         imgContainer.appendChild(img);
         closeBtn.addEventListener('click', () => overlay.remove());
        overlay.appendChild(imgContainer);
         overlay.appendChild(closeBtn);
         overlay.appendChild(closeBtn);
        // Закрытие по клику на оверлей или кнопку
        const closeOverlay = () => {
            overlay.style.display = 'none';
            document.body.style.overflow = 'auto';
            currentOverlay = null;
        };
        overlay.addEventListener('click', (e) => {
            if (e.target === overlay) closeOverlay();
        });
        closeBtn.addEventListener('click', closeOverlay);
        // Закрытие по ESC
        document.addEventListener('keydown', function escHandler(e) {
            if (e.key === 'Escape' && currentOverlay) {
                closeOverlay();
                document.removeEventListener('keydown', escHandler);
            }
        });


         document.body.appendChild(overlay);
         document.body.appendChild(overlay);
        return { overlay, img };
     }
     }


     // Перехватываем клики на обычные картинки (без MediaViewer)
     // Проверка, должна ли картинка открываться в оверлее
    function shouldOpenInOverlay(element) {
        // Проверяем исключения
        if (config.excludeSelectors.some(selector => element.matches(selector) || element.closest(selector))) {
            return false;
        }
 
        // Игнорируем маленькие картинки (иконки)
        if (element.width < 50 || element.height < 50) {
            return false;
        }
 
        // Игнорируем картинки внутри ссылок, которые ведут не на изображения
        const parentLink = element.closest('a');
        if (parentLink) {
            const href = parentLink.href;
            if (href && !href.match(/\.(jpg|jpeg|png|gif|webp|svg)(\?|$)/i)) {
                return false;
            }
        }
 
        return true;
    }
 
    // Показ кастомного оверлея
    function showCustomOverlay(url, alt = '') {
        if (!currentOverlay) {
            currentOverlay = createOverlay();
        }
 
        currentOverlay.img.src = url;
        currentOverlay.img.alt = alt;
        currentOverlay.overlay.style.display = 'flex';
       
        // Блокируем прокрутку body
        document.body.style.overflow = 'hidden';
    }
 
    // Обработчик кликов
     document.body.addEventListener('click', function(e) {
     document.body.addEventListener('click', function(e) {
         const target = e.target;
         const target = e.target;


         // Если картинка не часть MediaViewer overlay и нет ссылки на /w/images/
         // Клик на картинку
         if (target.tagName === 'IMG' && !target.closest('.mediaViewerOverlay')) {
         if (target.tagName === 'IMG' && shouldOpenInOverlay(target)) {
             const parentLink = target.closest('a[href*="/w/images/"]');
             // Проверяем, не является ли это частью MediaViewer или других компонентов
            if (!parentLink) {
            if (!target.closest('.mediaViewerOverlay, .mwe-popups, .gallery, .thumb')) {
                 e.preventDefault();
                 e.preventDefault();
                 e.stopPropagation();
                 e.stopPropagation();
                 showCustomOverlay(target.src);
                 showCustomOverlay(target.src, target.alt);
             }
             }
         }
         }


         // Ссылки на файлы, где MediaViewer не срабатывает
         // Клик на ссылку с изображением
         if (target.tagName === 'A' && target.href && target.href.includes('/w/images/')) {
         if (target.tagName === 'A' && target.href) {
            // проверка: если кликнули на миниатюру с MediaViewer, не блокируем
            const img = target.querySelector('img');
             if (!target.closest('.thumb')) {
             if (img && shouldOpenInOverlay(img)) {
                 e.preventDefault();
                 e.preventDefault();
                 e.stopPropagation();
                 e.stopPropagation();
                 showCustomOverlay(target.href);
                 showCustomOverlay(target.href, img.alt);
             }
             }
         }
         }
     }, true);
     }, true);


     // Функция добавления кнопки "Закрити" к MediaViewer overlay
     // Улучшенный MutationObserver для MediaViewer
     function addCloseButtonToMediaViewer() {
     function enhanceMediaViewer() {
         const overlay = document.querySelector('.mediaViewerOverlay, .mwe-popups');
         const overlay = document.querySelector('.mediaViewerOverlay');
         if (!overlay || overlay.dataset.closeBtnAdded) return;
         if (overlay && !overlay.dataset.enhanced) {
        overlay.dataset.closeBtnAdded = true;
            overlay.dataset.enhanced = true;
 
            const closeBtn = document.createElement('button');
            closeBtn.innerHTML = config.closeBtnText;
            closeBtn.style.cssText = `
                position: absolute;
                top: 20px;
                right: 20px;
                z-index: 10001;
                ${Object.entries(config.closeBtnStyle).map(([key, value]) =>
                    `${key.replace(/([A-Z])/g, '-$1').toLowerCase()}: ${value};`).join('')}
            `;
 
            closeBtn.addEventListener('click', () => {
                overlay.style.display = 'none';
            });


        const closeBtn = document.createElement('button');
            overlay.appendChild(closeBtn);
        closeBtn.innerText = 'Закрити';
        closeBtn.style.position = 'absolute';
        closeBtn.style.top = '20px';
        closeBtn.style.right = '20px';
        closeBtn.style.padding = '10px 20px';
        closeBtn.style.fontSize = '18px';
        closeBtn.style.cursor = 'pointer';
        closeBtn.style.background = '#f44336';
        closeBtn.style.color = 'white';
        closeBtn.style.border = 'none';
        closeBtn.style.borderRadius = '5px';
        closeBtn.addEventListener('click', () => {
            overlay.style.display = 'none';
        });


        overlay.appendChild(closeBtn);
            // Добавляем закрытие по ESC для MediaViewer
            overlay.addEventListener('keydown', (e) => {
                if (e.key === 'Escape') {
                    overlay.style.display = 'none';
                }
            });
        }
     }
     }


     // MutationObserver отслеживает появление overlay MediaViewer
     // Более эффективный observer
     const observer = new MutationObserver(() => addCloseButtonToMediaViewer());
     const observer = new MutationObserver((mutations) => {
     observer.observe(document.body, { childList: true, subtree: true });
        for (const mutation of mutations) {
            if (mutation.type === 'childList') {
                for (const node of mutation.addedNodes) {
                    if (node.nodeType === 1) { // Element node
                        if (node.classList && node.classList.contains('mediaViewerOverlay')) {
                            enhanceMediaViewer();
                        } else if (node.querySelector) {
                            const mediaViewer = node.querySelector('.mediaViewerOverlay');
                            if (mediaViewer) enhanceMediaViewer();
                        }
                    }
                }
            }
        }
    });
 
     observer.observe(document.body, {  
        childList: true,  
        subtree: true  
    });
 
    // Инициализация при загрузке DOM
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', enhanceMediaViewer);
    } else {
        enhanceMediaViewer();
    }


})();
})();

Версія за 16:02, 24 вересня 2025

$(function () {
    // Теми
    var themes = {
        light: '/w/index.php?title=MediaWiki:Light.css&action=raw&ctype=text/css',
        dark: '/w/index.php?title=MediaWiki:Dark.css&action=raw&ctype=text/css'
    };

    var theme = localStorage.getItem('selectedTheme');
    if (!theme) {
        theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
    }
    if (themes[theme]) mw.loader.load(themes[theme], 'text/css');

    function createButton(text, bottom, onClick, title) {
        var $btn = $('<button>').text(text).attr('title', title).css({
            position: 'fixed',
            bottom: bottom + 'px',
            right: '10px',
            padding: '10px 16px',
            border: 'none',
            borderRadius: '25px',
            background: '#1a73e8',
            color: '#ffffff',
            fontWeight: 'bold',
            fontSize: '14px',
            cursor: 'pointer',
            zIndex: 9999,
            textAlign: 'center',
            boxShadow: '0 2px 6px rgba(0,0,0,0.3)',
            whiteSpace: 'nowrap'
        }).click(onClick);
        $('body').append($btn);
        return $btn;
    }

    // Кнопка Темна/Світла тема
    var $themeBtn = createButton(
        theme === 'dark' ? 'Світла тема ☀️' : 'Темна тема 🌙',
        10,
        function () {
            var newTheme = theme === 'dark' ? 'light' : 'dark';
            localStorage.setItem('selectedTheme', newTheme);
            location.reload();
        },
        'Змінити тему'
    );

    // Змінна для зберігання розміру шрифту
    var fontSize = parseInt($('body').css('font-size'), 10) || 16;
    
    // Функція для застосування розміру шрифту
    function applyFontSize() {
        $('body').css('font-size', fontSize + 'px');
    }

    // Кнопка доступності
    var $accessBtn = createButton(
        'Доступність',
        70,
        function () {
            if (!$('body').hasClass('accessibility-mode')) {
                $('body').addClass('accessibility-mode');
                localStorage.setItem('accessibilityMode', 'on');
                $accessBtn.css('background', '#ff6600');
                $accessBtn.text('Доступність ON');
            } else {
                $('body').removeClass('accessibility-mode');
                localStorage.setItem('accessibilityMode', 'off');
                $accessBtn.css('background', '#1a73e8');
                $accessBtn.text('Доступність OFF');
            }
        },
        'Увімкнути/вимкнути режим доступності'
    );

    // Відновлення стану доступності
    if (localStorage.getItem('accessibilityMode') === 'on') {
        $('body').addClass('accessibility-mode');
        $accessBtn.css('background', '#ff6600');
        $accessBtn.text('Доступність ON');
    }

    // Лупа
    createButton('🔍 +', 130, function () {
        fontSize += 2;
        if (fontSize > 30) fontSize = 30;
        localStorage.setItem('fontSize', fontSize);
        applyFontSize();
    }, 'Збільшити шрифт');

    createButton('🔍 -', 170, function () {
        fontSize -= 2;
        if (fontSize < 12) fontSize = 12;
        localStorage.setItem('fontSize', fontSize);
        applyFontSize();
    }, 'Зменшити шрифт');

    // Відновлення розміру шрифту
    if (localStorage.getItem('fontSize')) {
        fontSize = parseInt(localStorage.getItem('fontSize'), 10);
    }
    applyFontSize();
});


//OVERLAY 
(function() {
    'use strict';
    
    // Конфигурация - легко настраивается
    const config = {
        overlayBg: 'rgba(0,0,0,0.92)',
        maxWidth: '95%',
        maxHeight: '95%',
        closeBtnText: '✕ Закрити',
        closeBtnStyle: {
            background: '#d32f2f',
            color: 'white',
            border: 'none',
            borderRadius: '4px',
            padding: '12px 24px',
            fontSize: '16px',
            cursor: 'pointer',
            marginTop: '20px',
            transition: 'background 0.3s ease'
        },
        imageStyle: {
            borderRadius: '8px',
            boxShadow: '0 4px 20px rgba(0,0,0,0.3)'
        },
        // Исключения - классы/атрибуты, которые не должны открывать оверлей
        excludeSelectors: [
            '.no-overlay',
            '[data-no-overlay]',
            '.mw-editsection img',
            '.sprite',
            '.icon'
        ]
    };

    let currentOverlay = null;

    // Функция создания оверлея
    function createOverlay() {
        const overlay = document.createElement('div');
        overlay.className = 'custom-image-overlay';
        overlay.style.cssText = `
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: ${config.overlayBg};
            display: none;
            justify-content: center;
            align-items: center;
            flex-direction: column;
            z-index: 10000;
            cursor: pointer;
        `;

        const imgContainer = document.createElement('div');
        imgContainer.style.cssText = `
            position: relative;
            max-width: ${config.maxWidth};
            max-height: ${config.maxHeight};
            display: flex;
            justify-content: center;
            align-items: center;
        `;

        const img = document.createElement('img');
        img.style.cssText = `
            max-width: 100%;
            max-height: 100%;
            object-fit: contain;
            ${Object.entries(config.imageStyle).map(([key, value]) => 
                `${key.replace(/([A-Z])/g, '-$1').toLowerCase()}: ${value};`).join('')}
        `;

        const closeBtn = document.createElement('button');
        closeBtn.innerHTML = config.closeBtnText;
        closeBtn.style.cssText = Object.entries(config.closeBtnStyle).map(([key, value]) => 
            `${key.replace(/([A-Z])/g, '-$1').toLowerCase()}: ${value};`).join('');
        
        // Добавляем hover эффект для кнопки
        closeBtn.onmouseover = () => closeBtn.style.background = '#b71c1c';
        closeBtn.onmouseout = () => closeBtn.style.background = config.closeBtnStyle.background;

        imgContainer.appendChild(img);
        overlay.appendChild(imgContainer);
        overlay.appendChild(closeBtn);

        // Закрытие по клику на оверлей или кнопку
        const closeOverlay = () => {
            overlay.style.display = 'none';
            document.body.style.overflow = 'auto';
            currentOverlay = null;
        };

        overlay.addEventListener('click', (e) => {
            if (e.target === overlay) closeOverlay();
        });

        closeBtn.addEventListener('click', closeOverlay);

        // Закрытие по ESC
        document.addEventListener('keydown', function escHandler(e) {
            if (e.key === 'Escape' && currentOverlay) {
                closeOverlay();
                document.removeEventListener('keydown', escHandler);
            }
        });

        document.body.appendChild(overlay);
        return { overlay, img };
    }

    // Проверка, должна ли картинка открываться в оверлее
    function shouldOpenInOverlay(element) {
        // Проверяем исключения
        if (config.excludeSelectors.some(selector => element.matches(selector) || element.closest(selector))) {
            return false;
        }

        // Игнорируем маленькие картинки (иконки)
        if (element.width < 50 || element.height < 50) {
            return false;
        }

        // Игнорируем картинки внутри ссылок, которые ведут не на изображения
        const parentLink = element.closest('a');
        if (parentLink) {
            const href = parentLink.href;
            if (href && !href.match(/\.(jpg|jpeg|png|gif|webp|svg)(\?|$)/i)) {
                return false;
            }
        }

        return true;
    }

    // Показ кастомного оверлея
    function showCustomOverlay(url, alt = '') {
        if (!currentOverlay) {
            currentOverlay = createOverlay();
        }

        currentOverlay.img.src = url;
        currentOverlay.img.alt = alt;
        currentOverlay.overlay.style.display = 'flex';
        
        // Блокируем прокрутку body
        document.body.style.overflow = 'hidden';
    }

    // Обработчик кликов
    document.body.addEventListener('click', function(e) {
        const target = e.target;

        // Клик на картинку
        if (target.tagName === 'IMG' && shouldOpenInOverlay(target)) {
            // Проверяем, не является ли это частью MediaViewer или других компонентов
            if (!target.closest('.mediaViewerOverlay, .mwe-popups, .gallery, .thumb')) {
                e.preventDefault();
                e.stopPropagation();
                showCustomOverlay(target.src, target.alt);
            }
        }

        // Клик на ссылку с изображением
        if (target.tagName === 'A' && target.href) {
            const img = target.querySelector('img');
            if (img && shouldOpenInOverlay(img)) {
                e.preventDefault();
                e.stopPropagation();
                showCustomOverlay(target.href, img.alt);
            }
        }
    }, true);

    // Улучшенный MutationObserver для MediaViewer
    function enhanceMediaViewer() {
        const overlay = document.querySelector('.mediaViewerOverlay');
        if (overlay && !overlay.dataset.enhanced) {
            overlay.dataset.enhanced = true;

            const closeBtn = document.createElement('button');
            closeBtn.innerHTML = config.closeBtnText;
            closeBtn.style.cssText = `
                position: absolute;
                top: 20px;
                right: 20px;
                z-index: 10001;
                ${Object.entries(config.closeBtnStyle).map(([key, value]) => 
                    `${key.replace(/([A-Z])/g, '-$1').toLowerCase()}: ${value};`).join('')}
            `;

            closeBtn.addEventListener('click', () => {
                overlay.style.display = 'none';
            });

            overlay.appendChild(closeBtn);

            // Добавляем закрытие по ESC для MediaViewer
            overlay.addEventListener('keydown', (e) => {
                if (e.key === 'Escape') {
                    overlay.style.display = 'none';
                }
            });
        }
    }

    // Более эффективный observer
    const observer = new MutationObserver((mutations) => {
        for (const mutation of mutations) {
            if (mutation.type === 'childList') {
                for (const node of mutation.addedNodes) {
                    if (node.nodeType === 1) { // Element node
                        if (node.classList && node.classList.contains('mediaViewerOverlay')) {
                            enhanceMediaViewer();
                        } else if (node.querySelector) {
                            const mediaViewer = node.querySelector('.mediaViewerOverlay');
                            if (mediaViewer) enhanceMediaViewer();
                        }
                    }
                }
            }
        }
    });

    observer.observe(document.body, { 
        childList: true, 
        subtree: true 
    });

    // Инициализация при загрузке DOM
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', enhanceMediaViewer);
    } else {
        enhanceMediaViewer();
    }

})();