|
|
| Строка 1: |
Строка 1: |
| /** | | // Переключение вкладок и классов "chaos-*". |
| * 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 = $container.find('.hj-chaos-tab-button'); | | var $buttons = $container.find('.hj-chaos-tab-button'); |
| var $blocks = $container.find('.hj-chaos-block'); | | var $blocks = $container.find('.hj-chaos-block'); |
|
| |
|
| if (!$buttons.length || !$blocks.length) return; | | if (!$buttons.length || !$blocks.length) return; |
| Строка 17: |
Строка 11: |
|
| |
|
| 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(); |
| var $targetBlock = $blocks.filter('[data-chaos="' + key + '"]'); | | $blocks |
| $targetBlock.addClass('active').fadeIn(200);
| | .filter('[data-chaos="' + key + '"]') |
| | .addClass('active') |
| | .show(); |
|
| |
|
| // Container class for styling | | // класс на контейнере для раскраски рамок/фона |
| $container.removeClass(CHAOS_CLASSES).addClass('chaos-' + key); | | $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) { |
| Строка 39: |
Строка 37: |
| } | | } |
|
| |
|
| // Click handlers | | // Клики |
| $buttons.off('click.hjChaos').on('click.hjChaos', function () { | | $buttons.off('click.hjChaos').on('click.hjChaos', function () { |
| var $btn = $(this); | | var $btn = $(this); |
| Строка 45: |
Строка 43: |
| }); | | }); |
| }); | | }); |
|
| |
| // ================== 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);
| |
| })();
| |