MediaWiki:Common.js: відмінності між версіями
Перейти до навігації
Перейти до пошуку
Wiki (обговорення | внесок) Немає опису редагування |
Wiki (обговорення | внесок) Немає опису редагування |
||
| Рядок 108: | Рядок 108: | ||
'use strict'; | 'use strict'; | ||
// Конфигурация | // Конфигурация в стиле MediaViewer | ||
const config = { | const config = { | ||
overlayBg: 'rgba(0,0,0,0. | overlayBg: 'rgba(0,0,0,0.95)', | ||
imageMaxWidth: '90%', | imageMaxWidth: '90%', | ||
imageMaxHeight: '90%', | imageMaxHeight: '90%', | ||
closeBtnText: ' | closeBtnText: '×', | ||
closeBtnStyle: { | closeBtnStyle: { | ||
background: ' | background: 'rgba(0,0,0,0.7)', | ||
color: 'white', | color: 'white', | ||
border: 'none', | border: 'none', | ||
borderRadius: ' | borderRadius: '50%', | ||
width: '40px', | |||
fontSize: ' | height: '40px', | ||
fontSize: '24px', | |||
cursor: 'pointer', | cursor: 'pointer', | ||
position: 'absolute', | |||
top: '20px', | |||
right: '20px' | |||
}, | |||
zoomBtnStyle: { | |||
background: 'rgba(0,0,0,0.7)', | |||
color: 'white', | |||
border: 'none', | |||
borderRadius: '50%', | |||
width: '40px', | |||
height: '40px', | |||
fontSize: '20px', | |||
cursor: 'pointer', | |||
position: 'absolute', | |||
right: '20px' | |||
} | } | ||
}; | }; | ||
// Функция для создания | let currentScale = 1; | ||
const minScale = 0.5; | |||
const maxScale = 3; | |||
const scaleStep = 0.25; | |||
// Функция для создания оверлея в стиле MediaViewer | |||
function showCustomOverlay(url) { | function showCustomOverlay(url) { | ||
const overlay = document.createElement('div'); | const overlay = document.createElement('div'); | ||
| Рядок 138: | Рядок 158: | ||
overlay.style.justifyContent = 'center'; | overlay.style.justifyContent = 'center'; | ||
overlay.style.alignItems = 'center'; | overlay.style.alignItems = 'center'; | ||
overlay.style.zIndex = '10000'; | overlay.style.zIndex = '10000'; | ||
overlay.style.cursor = ' | overlay.style.cursor = 'default'; | ||
// Контейнер для изображения и кнопок | |||
const container = document.createElement('div'); | |||
container.style.position = 'relative'; | |||
container.style.display = 'flex'; | |||
container.style.justifyContent = 'center'; | |||
container.style.alignItems = 'center'; | |||
container.style.maxWidth = '95vw'; | |||
container.style.maxHeight = '95vh'; | |||
// Изображение | |||
const img = document.createElement('img'); | const img = document.createElement('img'); | ||
img.src = url; | img.src = url; | ||
| Рядок 148: | Рядок 177: | ||
img.style.objectFit = 'contain'; | img.style.objectFit = 'contain'; | ||
img.style.borderRadius = '8px'; | img.style.borderRadius = '8px'; | ||
img.style.boxShadow = '0 10px 30px rgba(0,0,0,0.5)'; | |||
img.style.transition = 'transform 0.3s ease'; | |||
img.style.cursor = 'default'; // Убираем курсор лупы | |||
// Кнопка закрытия (как в MediaViewer) | |||
const closeBtn = document.createElement('button'); | const closeBtn = document.createElement('button'); | ||
closeBtn. | closeBtn.innerHTML = config.closeBtnText; | ||
closeBtn.style. | closeBtn.title = 'Закрити (Esc)'; | ||
closeBtn.style. | closeBtn.style.cssText = Object.entries(config.closeBtnStyle).map(([key, value]) => | ||
`${key}: ${value};`).join(''); | |||
closeBtn.style.zIndex = '10001'; | |||
// Кнопки зума | |||
const zoomInBtn = document.createElement('button'); | |||
zoomInBtn.innerHTML = '+'; | |||
zoomInBtn.title = 'Збільшити (Ctrl + +)'; | |||
// Закрытие | zoomInBtn.style.cssText = Object.entries(config.zoomBtnStyle).map(([key, value]) => | ||
`${key}: ${value};`).join(''); | |||
zoomInBtn.style.top = '70px'; | |||
zoomInBtn.style.zIndex = '10001'; | |||
const zoomOutBtn = document.createElement('button'); | |||
zoomOutBtn.innerHTML = '−'; | |||
zoomOutBtn.title = 'Зменшити (Ctrl + -)'; | |||
zoomOutBtn.style.cssText = Object.entries(config.zoomBtnStyle).map(([key, value]) => | |||
`${key}: ${value};`).join(''); | |||
zoomOutBtn.style.top = '120px'; | |||
zoomOutBtn.style.zIndex = '10001'; | |||
const resetZoomBtn = document.createElement('button'); | |||
resetZoomBtn.innerHTML = '1:1'; | |||
resetZoomBtn.title = 'Скинути масштаб'; | |||
resetZoomBtn.style.cssText = Object.entries(config.zoomBtnStyle).map(([key, value]) => | |||
`${key}: ${value};`).join(''); | |||
resetZoomBtn.style.top = '170px'; | |||
resetZoomBtn.style.zIndex = '10001'; | |||
resetZoomBtn.style.fontSize = '16px'; | |||
// Функции зума | |||
function zoomImage(scale) { | |||
currentScale = Math.max(minScale, Math.min(maxScale, scale)); | |||
img.style.transform = `scale(${currentScale})`; | |||
updateZoomButtons(); | |||
} | |||
function updateZoomButtons() { | |||
zoomInBtn.disabled = currentScale >= maxScale; | |||
zoomOutBtn.disabled = currentScale <= minScale; | |||
zoomInBtn.style.opacity = zoomInBtn.disabled ? '0.5' : '1'; | |||
zoomOutBtn.style.opacity = zoomOutBtn.disabled ? '0.5' : '1'; | |||
} | |||
// Обработчики зума | |||
zoomInBtn.addEventListener('click', (e) => { | |||
e.stopPropagation(); | |||
zoomImage(currentScale + scaleStep); | |||
}); | |||
zoomOutBtn.addEventListener('click', (e) => { | |||
e.stopPropagation(); | |||
zoomImage(currentScale - scaleStep); | |||
}); | |||
resetZoomBtn.addEventListener('click', (e) => { | |||
e.stopPropagation(); | |||
currentScale = 1; | |||
img.style.transform = 'scale(1)'; | |||
updateZoomButtons(); | |||
}); | |||
// Закрытие оверлея | |||
const closeOverlay = () => { | const closeOverlay = () => { | ||
document.body.removeChild(overlay); | document.body.removeChild(overlay); | ||
document.body.style.overflow = 'auto'; | document.body.style.overflow = 'auto'; | ||
currentScale = 1; // Сбрасываем масштаб | |||
}; | }; | ||
closeBtn.addEventListener('click', closeOverlay); | |||
// Закрытие по клику на фон (но не на изображение или кнопки) | |||
overlay.addEventListener('click', (e) => { | overlay.addEventListener('click', (e) => { | ||
if (e.target === overlay) closeOverlay(); | if (e.target === overlay) closeOverlay(); | ||
}); | }); | ||
// Закрытие по ESC | // Закрытие по ESC | ||
const keyHandler = (e) => { | const keyHandler = (e) => { | ||
if (e.key === 'Escape') closeOverlay(); | if (e.key === 'Escape') { | ||
closeOverlay(); | |||
document.removeEventListener('keydown', keyHandler); | |||
} else if (e.ctrlKey) { | |||
if (e.key === '+' || e.key === '=') { | |||
e.preventDefault(); | |||
zoomImage(currentScale + scaleStep); | |||
} else if (e.key === '-') { | |||
e.preventDefault(); | |||
zoomImage(currentScale - scaleStep); | |||
} else if (e.key === '0') { | |||
e.preventDefault(); | |||
currentScale = 1; | |||
img.style.transform = 'scale(1)'; | |||
updateZoomButtons(); | |||
} | |||
} | |||
}; | }; | ||
document.addEventListener('keydown', keyHandler); | document.addEventListener('keydown', keyHandler); | ||
// | // Hover эффекты для кнопок | ||
[closeBtn, zoomInBtn, zoomOutBtn, resetZoomBtn].forEach(btn => { | |||
btn.addEventListener('mouseenter', () => { | |||
btn.style.background = 'rgba(0,0,0,0.9)'; | |||
btn.style.transform = 'scale(1.1)'; | |||
}); | |||
btn.addEventListener('mouseleave', () => { | |||
btn.style.background = config.zoomBtnStyle.background; | |||
btn.style.transform = 'scale(1)'; | |||
}); | |||
}); | }); | ||
overlay.appendChild( | // Сборка интерфейса | ||
container.appendChild(img); | |||
container.appendChild(closeBtn); | |||
container.appendChild(zoomInBtn); | |||
container.appendChild(zoomOutBtn); | |||
container.appendChild(resetZoomBtn); | |||
overlay.appendChild(container); | |||
document.body.appendChild(overlay); | document.body.appendChild(overlay); | ||
document.body.style.overflow = 'hidden'; | |||
// | // Инициализация кнопок зума | ||
updateZoomButtons(); | |||
} | } | ||
| Рядок 208: | Рядок 327: | ||
// Ссылки на файлы, где MediaViewer не срабатывает | // Ссылки на файлы, где MediaViewer не срабатывает | ||
if (target.tagName === 'A' && target.href && target.href.includes('/w/images/')) { | if (target.tagName === 'A' && target.href && target.href.includes('/w/images/')) { | ||
if (!target.closest('.thumb')) { | if (!target.closest('.thumb')) { | ||
e.preventDefault(); | e.preventDefault(); | ||
| Рядок 217: | Рядок 335: | ||
}, true); | }, true); | ||
// Функция добавления кнопки "Закрити" к MediaViewer overlay | // Функция добавления кнопки "Закрити" к MediaViewer overlay | ||
function addCloseButtonToMediaViewer() { | function addCloseButtonToMediaViewer() { | ||
const overlay = document.querySelector('.mediaViewerOverlay, .mwe-popups'); | const overlay = document.querySelector('.mediaViewerOverlay, .mwe-popups'); | ||
| Рядок 224: | Рядок 342: | ||
const closeBtn = document.createElement('button'); | const closeBtn = document.createElement('button'); | ||
closeBtn.innerText = | closeBtn.innerText = 'Закрити'; | ||
closeBtn.style.position = 'absolute'; | closeBtn.style.position = 'absolute'; | ||
closeBtn.style.top = '20px'; | closeBtn.style.top = '20px'; | ||
closeBtn.style.right = '20px'; | closeBtn.style.right = '20px'; | ||
closeBtn.style.padding = | closeBtn.style.padding = '10px 20px'; | ||
closeBtn.style.fontSize = | closeBtn.style.fontSize = '18px'; | ||
closeBtn.style.cursor = | closeBtn.style.cursor = 'pointer'; | ||
closeBtn.style.background = | closeBtn.style.background = '#f44336'; | ||
closeBtn.style.color = | closeBtn.style.color = 'white'; | ||
closeBtn.style.border = | closeBtn.style.border = 'none'; | ||
closeBtn.style.borderRadius = | closeBtn.style.borderRadius = '5px'; | ||
closeBtn.style.zIndex = '10001'; | closeBtn.style.zIndex = '10001'; | ||
| Рядок 242: | Рядок 360: | ||
overlay.appendChild(closeBtn); | overlay.appendChild(closeBtn); | ||
} | } | ||
// MutationObserver отслеживает появление overlay MediaViewer | // MutationObserver отслеживает появление overlay MediaViewer | ||
const observer = new MutationObserver(( | const observer = new MutationObserver(() => addCloseButtonToMediaViewer()); | ||
observer.observe(document.body, { childList: true, subtree: true }); | observer.observe(document.body, { childList: true, subtree: true }); | ||
// | // Стили для улучшения внешнего вида | ||
const style = document.createElement('style'); | const style = document.createElement('style'); | ||
style.textContent = ` | style.textContent = ` | ||
button { | |||
transition: | transition: all 0.2s ease !important; | ||
} | } | ||
button:hover:not(:disabled) { | |||
transform: scale(1. | transform: scale(1.1) !important; | ||
background: rgba(0,0,0,0.9) !important; | |||
} | } | ||
button: | button:disabled { | ||
cursor: not-allowed !important; | |||
} | |||
@media (max-width: 768px) { | |||
button { | |||
width: 35px !important; | |||
height: 35px !important; | |||
font-size: 18px !important; | |||
} | |||
} | } | ||
`; | `; | ||
Версія за 16:23, 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';
// Конфигурация в стиле MediaViewer
const config = {
overlayBg: 'rgba(0,0,0,0.95)',
imageMaxWidth: '90%',
imageMaxHeight: '90%',
closeBtnText: '×',
closeBtnStyle: {
background: 'rgba(0,0,0,0.7)',
color: 'white',
border: 'none',
borderRadius: '50%',
width: '40px',
height: '40px',
fontSize: '24px',
cursor: 'pointer',
position: 'absolute',
top: '20px',
right: '20px'
},
zoomBtnStyle: {
background: 'rgba(0,0,0,0.7)',
color: 'white',
border: 'none',
borderRadius: '50%',
width: '40px',
height: '40px',
fontSize: '20px',
cursor: 'pointer',
position: 'absolute',
right: '20px'
}
};
let currentScale = 1;
const minScale = 0.5;
const maxScale = 3;
const scaleStep = 0.25;
// Функция для создания оверлея в стиле MediaViewer
function showCustomOverlay(url) {
const overlay = document.createElement('div');
overlay.style.position = 'fixed';
overlay.style.top = '0';
overlay.style.left = '0';
overlay.style.width = '100%';
overlay.style.height = '100%';
overlay.style.background = config.overlayBg;
overlay.style.display = 'flex';
overlay.style.justifyContent = 'center';
overlay.style.alignItems = 'center';
overlay.style.zIndex = '10000';
overlay.style.cursor = 'default';
// Контейнер для изображения и кнопок
const container = document.createElement('div');
container.style.position = 'relative';
container.style.display = 'flex';
container.style.justifyContent = 'center';
container.style.alignItems = 'center';
container.style.maxWidth = '95vw';
container.style.maxHeight = '95vh';
// Изображение
const img = document.createElement('img');
img.src = url;
img.style.maxWidth = config.imageMaxWidth;
img.style.maxHeight = config.imageMaxHeight;
img.style.objectFit = 'contain';
img.style.borderRadius = '8px';
img.style.boxShadow = '0 10px 30px rgba(0,0,0,0.5)';
img.style.transition = 'transform 0.3s ease';
img.style.cursor = 'default'; // Убираем курсор лупы
// Кнопка закрытия (как в MediaViewer)
const closeBtn = document.createElement('button');
closeBtn.innerHTML = config.closeBtnText;
closeBtn.title = 'Закрити (Esc)';
closeBtn.style.cssText = Object.entries(config.closeBtnStyle).map(([key, value]) =>
`${key}: ${value};`).join('');
closeBtn.style.zIndex = '10001';
// Кнопки зума
const zoomInBtn = document.createElement('button');
zoomInBtn.innerHTML = '+';
zoomInBtn.title = 'Збільшити (Ctrl + +)';
zoomInBtn.style.cssText = Object.entries(config.zoomBtnStyle).map(([key, value]) =>
`${key}: ${value};`).join('');
zoomInBtn.style.top = '70px';
zoomInBtn.style.zIndex = '10001';
const zoomOutBtn = document.createElement('button');
zoomOutBtn.innerHTML = '−';
zoomOutBtn.title = 'Зменшити (Ctrl + -)';
zoomOutBtn.style.cssText = Object.entries(config.zoomBtnStyle).map(([key, value]) =>
`${key}: ${value};`).join('');
zoomOutBtn.style.top = '120px';
zoomOutBtn.style.zIndex = '10001';
const resetZoomBtn = document.createElement('button');
resetZoomBtn.innerHTML = '1:1';
resetZoomBtn.title = 'Скинути масштаб';
resetZoomBtn.style.cssText = Object.entries(config.zoomBtnStyle).map(([key, value]) =>
`${key}: ${value};`).join('');
resetZoomBtn.style.top = '170px';
resetZoomBtn.style.zIndex = '10001';
resetZoomBtn.style.fontSize = '16px';
// Функции зума
function zoomImage(scale) {
currentScale = Math.max(minScale, Math.min(maxScale, scale));
img.style.transform = `scale(${currentScale})`;
updateZoomButtons();
}
function updateZoomButtons() {
zoomInBtn.disabled = currentScale >= maxScale;
zoomOutBtn.disabled = currentScale <= minScale;
zoomInBtn.style.opacity = zoomInBtn.disabled ? '0.5' : '1';
zoomOutBtn.style.opacity = zoomOutBtn.disabled ? '0.5' : '1';
}
// Обработчики зума
zoomInBtn.addEventListener('click', (e) => {
e.stopPropagation();
zoomImage(currentScale + scaleStep);
});
zoomOutBtn.addEventListener('click', (e) => {
e.stopPropagation();
zoomImage(currentScale - scaleStep);
});
resetZoomBtn.addEventListener('click', (e) => {
e.stopPropagation();
currentScale = 1;
img.style.transform = 'scale(1)';
updateZoomButtons();
});
// Закрытие оверлея
const closeOverlay = () => {
document.body.removeChild(overlay);
document.body.style.overflow = 'auto';
currentScale = 1; // Сбрасываем масштаб
};
closeBtn.addEventListener('click', closeOverlay);
// Закрытие по клику на фон (но не на изображение или кнопки)
overlay.addEventListener('click', (e) => {
if (e.target === overlay) closeOverlay();
});
// Закрытие по ESC
const keyHandler = (e) => {
if (e.key === 'Escape') {
closeOverlay();
document.removeEventListener('keydown', keyHandler);
} else if (e.ctrlKey) {
if (e.key === '+' || e.key === '=') {
e.preventDefault();
zoomImage(currentScale + scaleStep);
} else if (e.key === '-') {
e.preventDefault();
zoomImage(currentScale - scaleStep);
} else if (e.key === '0') {
e.preventDefault();
currentScale = 1;
img.style.transform = 'scale(1)';
updateZoomButtons();
}
}
};
document.addEventListener('keydown', keyHandler);
// Hover эффекты для кнопок
[closeBtn, zoomInBtn, zoomOutBtn, resetZoomBtn].forEach(btn => {
btn.addEventListener('mouseenter', () => {
btn.style.background = 'rgba(0,0,0,0.9)';
btn.style.transform = 'scale(1.1)';
});
btn.addEventListener('mouseleave', () => {
btn.style.background = config.zoomBtnStyle.background;
btn.style.transform = 'scale(1)';
});
});
// Сборка интерфейса
container.appendChild(img);
container.appendChild(closeBtn);
container.appendChild(zoomInBtn);
container.appendChild(zoomOutBtn);
container.appendChild(resetZoomBtn);
overlay.appendChild(container);
document.body.appendChild(overlay);
document.body.style.overflow = 'hidden';
// Инициализация кнопок зума
updateZoomButtons();
}
// Перехватываем клики на обычные картинки (без MediaViewer)
document.body.addEventListener('click', function(e) {
const target = e.target;
// Если картинка не часть MediaViewer overlay и нет ссылки на /w/images/
if (target.tagName === 'IMG' && !target.closest('.mediaViewerOverlay')) {
const parentLink = target.closest('a[href*="/w/images/"]');
if (!parentLink) {
e.preventDefault();
e.stopPropagation();
showCustomOverlay(target.src);
}
}
// Ссылки на файлы, где MediaViewer не срабатывает
if (target.tagName === 'A' && target.href && target.href.includes('/w/images/')) {
if (!target.closest('.thumb')) {
e.preventDefault();
e.stopPropagation();
showCustomOverlay(target.href);
}
}
}, true);
// Функция добавления кнопки "Закрити" к MediaViewer overlay
function addCloseButtonToMediaViewer() {
const overlay = document.querySelector('.mediaViewerOverlay, .mwe-popups');
if (!overlay || overlay.dataset.closeBtnAdded) return;
overlay.dataset.closeBtnAdded = true;
const closeBtn = document.createElement('button');
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.style.zIndex = '10001';
closeBtn.addEventListener('click', () => {
overlay.style.display = 'none';
});
overlay.appendChild(closeBtn);
}
// MutationObserver отслеживает появление overlay MediaViewer
const observer = new MutationObserver(() => addCloseButtonToMediaViewer());
observer.observe(document.body, { childList: true, subtree: true });
// Стили для улучшения внешнего вида
const style = document.createElement('style');
style.textContent = `
button {
transition: all 0.2s ease !important;
}
button:hover:not(:disabled) {
transform: scale(1.1) !important;
background: rgba(0,0,0,0.9) !important;
}
button:disabled {
cursor: not-allowed !important;
}
@media (max-width: 768px) {
button {
width: 35px !important;
height: 35px !important;
font-size: 18px !important;
}
}
`;
document.head.appendChild(style);
})();