MediaWiki:Common.js: відмінності між версіями
Перейти до навігації
Перейти до пошуку
Wiki (обговорення | внесок) Немає опису редагування Мітка: Ручний відкіт |
Wiki (обговорення | внесок) Немає опису редагування |
||
| Рядок 106: | Рядок 106: | ||
//OVERLAY | //OVERLAY | ||
(function() { | (function() { | ||
// Функция | 'use strict'; | ||
function | |||
// Конфигурация - легко настраивается | |||
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. | overlay.className = 'custom-image-overlay'; | ||
overlay.style.top | 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'); | const img = document.createElement('img'); | ||
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'); | const closeBtn = document.createElement('button'); | ||
closeBtn. | closeBtn.innerHTML = config.closeBtnText; | ||
closeBtn.style. | closeBtn.style.cssText = Object.entries(config.closeBtnStyle).map(([key, value]) => | ||
`${key.replace(/([A-Z])/g, '-$1').toLowerCase()}: ${value};`).join(''); | |||
closeBtn. | // Добавляем hover эффект для кнопки | ||
closeBtn.onmouseover = () => closeBtn.style.background = '#b71c1c'; | |||
closeBtn. | closeBtn.onmouseout = () => closeBtn.style.background = config.closeBtnStyle.background; | ||
imgContainer.appendChild(img); | |||
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 }; | |||
} | } | ||
// | // Проверка, должна ли картинка открываться в оверлее | ||
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; | ||
// | // Клик на картинку | ||
if (target.tagName === 'IMG' && | if (target.tagName === 'IMG' && shouldOpenInOverlay(target)) { | ||
// Проверяем, не является ли это частью MediaViewer или других компонентов | |||
if (!target.closest('.mediaViewerOverlay, .mwe-popups, .gallery, .thumb')) { | |||
e.preventDefault(); | e.preventDefault(); | ||
e.stopPropagation(); | e.stopPropagation(); | ||
showCustomOverlay(target.src); | showCustomOverlay(target.src, target.alt); | ||
} | } | ||
} | } | ||
// | // Клик на ссылку с изображением | ||
if (target.tagName === 'A' && target.href | if (target.tagName === 'A' && target.href) { | ||
const img = target.querySelector('img'); | |||
if ( | if (img && shouldOpenInOverlay(img)) { | ||
e.preventDefault(); | e.preventDefault(); | ||
e.stopPropagation(); | e.stopPropagation(); | ||
showCustomOverlay(target.href); | showCustomOverlay(target.href, img.alt); | ||
} | } | ||
} | } | ||
}, true); | }, true); | ||
// | // Улучшенный MutationObserver для MediaViewer | ||
function | function enhanceMediaViewer() { | ||
const overlay = document.querySelector('.mediaViewerOverlay | const overlay = document.querySelector('.mediaViewerOverlay'); | ||
if (!overlay | 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(() => | 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();
}
})();