MediaWiki:Common.js: различия между версиями
Mei Day (обсуждение | вклад) Нет описания правки |
Mei Day (обсуждение | вклад) Нет описания правки Метка: отменено |
||
| Строка 1: | Строка 1: | ||
/ | /** | ||
* Horny Jail Wiki - Enhanced JavaScript | |||
* Handles tabs, animations, and interactive elements | |||
*/ | |||
mw.hook('wikipage.content').add(function ($root) { | mw.hook('wikipage.content').add(function ($root) { | ||
// ================== CHAOS TABS SYSTEM ================== | |||
$root.find('.hj-chaos-container').each(function () { | $root.find('.hj-chaos-container').each(function () { | ||
var $container = $(this); | var $container = $(this); | ||
var $buttons | var $buttons = $container.find('.hj-chaos-tab-button'); | ||
var $blocks | var $blocks = $container.find('.hj-chaos-block'); | ||
if (!$buttons.length || !$blocks.length) return; | if (!$buttons.length || !$blocks.length) return; | ||
| Строка 11: | Строка 17: | ||
function activate(key, $btn) { | function activate(key, $btn) { | ||
// | // Active button | ||
$buttons.removeClass('active'); | $buttons.removeClass('active'); | ||
$btn.addClass('active'); | $btn.addClass('active'); | ||
// | // Active block with fade animation | ||
$blocks.removeClass('active').hide(); | $blocks.removeClass('active').hide(); | ||
$blocks | var $targetBlock = $blocks.filter('[data-chaos="' + key + '"]'); | ||
$targetBlock.addClass('active').fadeIn(200); | |||
// | // Container class for styling | ||
$container | $container.removeClass(CHAOS_CLASSES).addClass('chaos-' + key); | ||
} | } | ||
// | // Initialize | ||
var $activeBtn = $buttons.filter('.active').first(); | var $activeBtn = $buttons.filter('.active').first(); | ||
if ($activeBtn.length) { | if ($activeBtn.length) { | ||
| Строка 37: | Строка 39: | ||
} | } | ||
// | // Click handlers | ||
$buttons.off('click.hjChaos').on('click.hjChaos', function () { | $buttons.off('click.hjChaos').on('click.hjChaos', function () { | ||
var $btn = $(this); | var $btn = $(this); | ||
| Строка 43: | Строка 45: | ||
}); | }); | ||
}); | }); | ||
// ================== CARD HOVER EFFECTS ================== | |||
$root.find('.hj-nav-card').each(function () { | |||
var $card = $(this); | |||
$card.on('mouseenter', function (e) { | |||
// Add subtle glow effect on mouse position | |||
var rect = this.getBoundingClientRect(); | |||
var x = e.clientX - rect.left; | |||
var y = e.clientY - rect.top; | |||
$card.css({ | |||
'--mouse-x': x + 'px', | |||
'--mouse-y': y + 'px' | |||
}); | |||
}); | |||
}); | |||
// ================== ANIMATE ON SCROLL ================== | |||
if ('IntersectionObserver' in window) { | |||
var observer = new IntersectionObserver(function (entries) { | |||
entries.forEach(function (entry) { | |||
if (entry.isIntersecting) { | |||
$(entry.target).addClass('hj-visible'); | |||
observer.unobserve(entry.target); | |||
} | |||
}); | |||
}, { | |||
threshold: 0.1, | |||
rootMargin: '0px 0px -50px 0px' | |||
}); | |||
$root.find('.hj-animate').each(function () { | |||
observer.observe(this); | |||
}); | |||
} else { | |||
// Fallback for older browsers | |||
$root.find('.hj-animate').addClass('hj-visible'); | |||
} | |||
// ================== SMOOTH SCROLL FOR ANCHORS ================== | |||
$root.find('a[href^="#"]').on('click', function (e) { | |||
var target = $(this.getAttribute('href')); | |||
if (target.length) { | |||
e.preventDefault(); | |||
$('html, body').animate({ | |||
scrollTop: target.offset().top - 100 | |||
}, 500, 'swing'); | |||
} | |||
}); | |||
// ================== COUNTER ANIMATION ================== | |||
$root.find('.hj-stat-value[data-count]').each(function () { | |||
var $this = $(this); | |||
var countTo = parseInt($this.data('count'), 10); | |||
if (!countTo) return; | |||
var observer = new IntersectionObserver(function (entries) { | |||
entries.forEach(function (entry) { | |||
if (entry.isIntersecting) { | |||
$({ count: 0 }).animate({ count: countTo }, { | |||
duration: 1500, | |||
easing: 'swing', | |||
step: function () { | |||
$this.text(Math.floor(this.count)); | |||
}, | |||
complete: function () { | |||
$this.text(countTo); | |||
} | |||
}); | |||
observer.unobserve(entry.target); | |||
} | |||
}); | |||
}, { threshold: 0.5 }); | |||
observer.observe(this); | |||
}); | |||
// ================== TOOLTIP SYSTEM ================== | |||
$root.find('[data-hj-tooltip]').each(function () { | |||
var $el = $(this); | |||
var tooltipText = $el.data('hj-tooltip'); | |||
var $tooltip = $('<div class="hj-tooltip">' + tooltipText + '</div>'); | |||
$tooltip.css({ | |||
position: 'absolute', | |||
background: 'rgba(0, 0, 0, 0.9)', | |||
color: '#fff', | |||
padding: '8px 12px', | |||
borderRadius: '6px', | |||
fontSize: '0.85em', | |||
zIndex: 9999, | |||
pointerEvents: 'none', | |||
opacity: 0, | |||
transition: 'opacity 0.2s ease', | |||
maxWidth: '250px', | |||
whiteSpace: 'normal' | |||
}); | |||
$el.css('position', 'relative'); | |||
$el.on('mouseenter', function (e) { | |||
$('body').append($tooltip); | |||
var rect = this.getBoundingClientRect(); | |||
$tooltip.css({ | |||
top: rect.top + window.scrollY - $tooltip.outerHeight() - 8, | |||
left: rect.left + (rect.width / 2) - ($tooltip.outerWidth() / 2), | |||
opacity: 1 | |||
}); | |||
}); | |||
$el.on('mouseleave', function () { | |||
$tooltip.remove(); | |||
}); | |||
}); | |||
// ================== COLLAPSIBLE SECTIONS ================== | |||
$root.find('.hj-collapsible-header').on('click', function () { | |||
var $header = $(this); | |||
var $content = $header.next('.hj-collapsible-content'); | |||
var $icon = $header.find('.hj-collapsible-icon'); | |||
$content.slideToggle(200); | |||
$header.toggleClass('hj-collapsed'); | |||
if ($header.hasClass('hj-collapsed')) { | |||
$icon.text('▶'); | |||
} else { | |||
$icon.text('▼'); | |||
} | |||
}); | |||
// ================== COPY CODE BUTTON ================== | |||
$root.find('pre').each(function () { | |||
var $pre = $(this); | |||
var $btn = $('<button class="hj-copy-btn" title="Скопировать">📋</button>'); | |||
$btn.css({ | |||
position: 'absolute', | |||
top: '8px', | |||
right: '8px', | |||
background: 'rgba(255,255,255,0.1)', | |||
border: 'none', | |||
borderRadius: '4px', | |||
padding: '4px 8px', | |||
cursor: 'pointer', | |||
opacity: 0, | |||
transition: 'opacity 0.2s' | |||
}); | |||
$pre.css('position', 'relative'); | |||
$pre.append($btn); | |||
$pre.on('mouseenter', function () { | |||
$btn.css('opacity', 1); | |||
}).on('mouseleave', function () { | |||
$btn.css('opacity', 0); | |||
}); | |||
$btn.on('click', function () { | |||
var text = $pre.text().replace('📋', '').trim(); | |||
navigator.clipboard.writeText(text).then(function () { | |||
$btn.text('✓'); | |||
setTimeout(function () { | |||
$btn.text('📋'); | |||
}, 1500); | |||
}); | |||
}); | |||
}); | |||
// ================== PARALLAX HERO ================== | |||
var $hero = $root.find('.hj-hero'); | |||
if ($hero.length) { | |||
$(window).on('scroll.hjParallax', function () { | |||
var scrolled = $(window).scrollTop(); | |||
var heroHeight = $hero.outerHeight(); | |||
if (scrolled < heroHeight) { | |||
var parallaxValue = scrolled * 0.3; | |||
$hero.find('.hj-hero-logo').css('transform', 'translateY(' + (parallaxValue * 0.5) + 'px)'); | |||
} | |||
}); | |||
} | |||
// ================== TYPING EFFECT ================== | |||
$root.find('.hj-typing').each(function () { | |||
var $el = $(this); | |||
var text = $el.text(); | |||
var speed = parseInt($el.data('typing-speed'), 10) || 50; | |||
$el.text(''); | |||
$el.css('visibility', 'visible'); | |||
var i = 0; | |||
function type() { | |||
if (i < text.length) { | |||
$el.text($el.text() + text.charAt(i)); | |||
i++; | |||
setTimeout(type, speed); | |||
} | |||
} | |||
// Start typing when visible | |||
var observer = new IntersectionObserver(function (entries) { | |||
if (entries[0].isIntersecting) { | |||
type(); | |||
observer.unobserve($el[0]); | |||
} | |||
}); | |||
observer.observe($el[0]); | |||
}); | |||
}); | }); | ||
// ================== CSS ANIMATIONS (injected) ================== | |||
(function () { | |||
var style = document.createElement('style'); | |||
style.textContent = ` | |||
.hj-animate { | |||
opacity: 0; | |||
transform: translateY(20px); | |||
transition: opacity 0.6s ease, transform 0.6s ease; | |||
} | |||
.hj-animate.hj-visible { | |||
opacity: 1; | |||
transform: translateY(0); | |||
} | |||
.hj-animate-delay-1 { transition-delay: 0.1s; } | |||
.hj-animate-delay-2 { transition-delay: 0.2s; } | |||
.hj-animate-delay-3 { transition-delay: 0.3s; } | |||
.hj-animate-delay-4 { transition-delay: 0.4s; } | |||
.hj-collapsible-header { | |||
cursor: pointer; | |||
user-select: none; | |||
} | |||
.hj-collapsible-header:hover { | |||
opacity: 0.8; | |||
} | |||
.hj-typing { | |||
visibility: hidden; | |||
} | |||
`; | |||
document.head.appendChild(style); | |||
})(); | |||
Версия от 15:25, 27 февраля 2026
/**
* Horny Jail Wiki - Enhanced JavaScript
* Handles tabs, animations, and interactive elements
*/
mw.hook('wikipage.content').add(function ($root) {
// ================== CHAOS TABS SYSTEM ==================
$root.find('.hj-chaos-container').each(function () {
var $container = $(this);
var $buttons = $container.find('.hj-chaos-tab-button');
var $blocks = $container.find('.hj-chaos-block');
if (!$buttons.length || !$blocks.length) return;
var CHAOS_CLASSES = 'chaos-overview chaos-calm chaos-medium chaos-high';
function activate(key, $btn) {
// Active button
$buttons.removeClass('active');
$btn.addClass('active');
// Active block with fade animation
$blocks.removeClass('active').hide();
var $targetBlock = $blocks.filter('[data-chaos="' + key + '"]');
$targetBlock.addClass('active').fadeIn(200);
// Container class for styling
$container.removeClass(CHAOS_CLASSES).addClass('chaos-' + key);
}
// Initialize
var $activeBtn = $buttons.filter('.active').first();
if ($activeBtn.length) {
activate($activeBtn.data('chaos'), $activeBtn);
} else {
var $first = $buttons.first();
activate($first.data('chaos'), $first);
}
// Click handlers
$buttons.off('click.hjChaos').on('click.hjChaos', function () {
var $btn = $(this);
activate($btn.data('chaos'), $btn);
});
});
// ================== CARD HOVER EFFECTS ==================
$root.find('.hj-nav-card').each(function () {
var $card = $(this);
$card.on('mouseenter', function (e) {
// Add subtle glow effect on mouse position
var rect = this.getBoundingClientRect();
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
$card.css({
'--mouse-x': x + 'px',
'--mouse-y': y + 'px'
});
});
});
// ================== ANIMATE ON SCROLL ==================
if ('IntersectionObserver' in window) {
var observer = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
$(entry.target).addClass('hj-visible');
observer.unobserve(entry.target);
}
});
}, {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
});
$root.find('.hj-animate').each(function () {
observer.observe(this);
});
} else {
// Fallback for older browsers
$root.find('.hj-animate').addClass('hj-visible');
}
// ================== SMOOTH SCROLL FOR ANCHORS ==================
$root.find('a[href^="#"]').on('click', function (e) {
var target = $(this.getAttribute('href'));
if (target.length) {
e.preventDefault();
$('html, body').animate({
scrollTop: target.offset().top - 100
}, 500, 'swing');
}
});
// ================== COUNTER ANIMATION ==================
$root.find('.hj-stat-value[data-count]').each(function () {
var $this = $(this);
var countTo = parseInt($this.data('count'), 10);
if (!countTo) return;
var observer = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
$({ count: 0 }).animate({ count: countTo }, {
duration: 1500,
easing: 'swing',
step: function () {
$this.text(Math.floor(this.count));
},
complete: function () {
$this.text(countTo);
}
});
observer.unobserve(entry.target);
}
});
}, { threshold: 0.5 });
observer.observe(this);
});
// ================== TOOLTIP SYSTEM ==================
$root.find('[data-hj-tooltip]').each(function () {
var $el = $(this);
var tooltipText = $el.data('hj-tooltip');
var $tooltip = $('<div class="hj-tooltip">' + tooltipText + '</div>');
$tooltip.css({
position: 'absolute',
background: 'rgba(0, 0, 0, 0.9)',
color: '#fff',
padding: '8px 12px',
borderRadius: '6px',
fontSize: '0.85em',
zIndex: 9999,
pointerEvents: 'none',
opacity: 0,
transition: 'opacity 0.2s ease',
maxWidth: '250px',
whiteSpace: 'normal'
});
$el.css('position', 'relative');
$el.on('mouseenter', function (e) {
$('body').append($tooltip);
var rect = this.getBoundingClientRect();
$tooltip.css({
top: rect.top + window.scrollY - $tooltip.outerHeight() - 8,
left: rect.left + (rect.width / 2) - ($tooltip.outerWidth() / 2),
opacity: 1
});
});
$el.on('mouseleave', function () {
$tooltip.remove();
});
});
// ================== COLLAPSIBLE SECTIONS ==================
$root.find('.hj-collapsible-header').on('click', function () {
var $header = $(this);
var $content = $header.next('.hj-collapsible-content');
var $icon = $header.find('.hj-collapsible-icon');
$content.slideToggle(200);
$header.toggleClass('hj-collapsed');
if ($header.hasClass('hj-collapsed')) {
$icon.text('▶');
} else {
$icon.text('▼');
}
});
// ================== COPY CODE BUTTON ==================
$root.find('pre').each(function () {
var $pre = $(this);
var $btn = $('<button class="hj-copy-btn" title="Скопировать">📋</button>');
$btn.css({
position: 'absolute',
top: '8px',
right: '8px',
background: 'rgba(255,255,255,0.1)',
border: 'none',
borderRadius: '4px',
padding: '4px 8px',
cursor: 'pointer',
opacity: 0,
transition: 'opacity 0.2s'
});
$pre.css('position', 'relative');
$pre.append($btn);
$pre.on('mouseenter', function () {
$btn.css('opacity', 1);
}).on('mouseleave', function () {
$btn.css('opacity', 0);
});
$btn.on('click', function () {
var text = $pre.text().replace('📋', '').trim();
navigator.clipboard.writeText(text).then(function () {
$btn.text('✓');
setTimeout(function () {
$btn.text('📋');
}, 1500);
});
});
});
// ================== PARALLAX HERO ==================
var $hero = $root.find('.hj-hero');
if ($hero.length) {
$(window).on('scroll.hjParallax', function () {
var scrolled = $(window).scrollTop();
var heroHeight = $hero.outerHeight();
if (scrolled < heroHeight) {
var parallaxValue = scrolled * 0.3;
$hero.find('.hj-hero-logo').css('transform', 'translateY(' + (parallaxValue * 0.5) + 'px)');
}
});
}
// ================== TYPING EFFECT ==================
$root.find('.hj-typing').each(function () {
var $el = $(this);
var text = $el.text();
var speed = parseInt($el.data('typing-speed'), 10) || 50;
$el.text('');
$el.css('visibility', 'visible');
var i = 0;
function type() {
if (i < text.length) {
$el.text($el.text() + text.charAt(i));
i++;
setTimeout(type, speed);
}
}
// Start typing when visible
var observer = new IntersectionObserver(function (entries) {
if (entries[0].isIntersecting) {
type();
observer.unobserve($el[0]);
}
});
observer.observe($el[0]);
});
});
// ================== CSS ANIMATIONS (injected) ==================
(function () {
var style = document.createElement('style');
style.textContent = `
.hj-animate {
opacity: 0;
transform: translateY(20px);
transition: opacity 0.6s ease, transform 0.6s ease;
}
.hj-animate.hj-visible {
opacity: 1;
transform: translateY(0);
}
.hj-animate-delay-1 { transition-delay: 0.1s; }
.hj-animate-delay-2 { transition-delay: 0.2s; }
.hj-animate-delay-3 { transition-delay: 0.3s; }
.hj-animate-delay-4 { transition-delay: 0.4s; }
.hj-collapsible-header {
cursor: pointer;
user-select: none;
}
.hj-collapsible-header:hover {
opacity: 0.8;
}
.hj-typing {
visibility: hidden;
}
`;
document.head.appendChild(style);
})();