Mediawiki:common.js

Вернуться

Строка 2221: Строка 2221:
};
};


var AprilFoolsCubeTransitionModule = {
var AprilFoolsPrankModule = {
    config: {
        // Пока только по флагу:
        enableForAll: false,        // <-- чтобы включить всем, просто поставь true
        enableOnAprilFirst: false,  // <-- если потом захочешь автозапуск 1 апреля
        onlyMainPage: true,        // запуск только на главной
        runOncePerSession: true,    // работает только для обычного режима (НЕ для ?prank=1)
 
        overlayDurationMs: 4200,    // сколько показывать картинку до куб-перехода
        cubeDurationMs: 1200,
 
        wikiImageUrl: 'https://upload.wikimedia.org/wikipedia/commons/d/d4/2019_Screenshot_of_English_Wikipedia_homepage.png',
        clockAudioUrl: 'https://zvukogram.com/mp3/34/the-ticking-of-a-quartz-clock.mp3',
        lampAudioUrl: 'https://zvukitop.com/wp-content/uploads/2021/04/luminesc-lampa-r4fh.mp3'
    },
 
     started: false,
     started: false,
    closing: false,
     overlay: null,
     overlay: null,
     clockAudio: null,
     imageLayer: null,
     lampAudio: null,
     audio: null,
    needsGestureUnlock: false,


     init: function () {
     init: function () {
Строка 2247: Строка 2232:
         this.started = true;
         this.started = true;


         if (this.config.onlyMainPage) {
         if (!window.mw || !mw.config || !mw.config.get('wgIsMainPage')) {
            if (!window.mw || !mw.config || !mw.config.get('wgIsMainPage')) {
            return;
                return;
            }
         }
         }


         var url = new URL(window.location.href);
         var params = new URLSearchParams(window.location.search);
         var prankFlag = url.searchParams.get('prank') === '1';
         var prankByFlag = params.get('prank') === '1';
 
        var now = new Date();
        var isAprilFirst = now.getMonth() === 3 && now.getDate() === 1;
 
        var shouldRun =
            prankFlag ||
            this.config.enableForAll ||
            (this.config.enableOnAprilFirst && isAprilFirst);


         if (!shouldRun) return;
         var enableForAll = false;


        // Важно: для ?prank=1 НЕ ограничиваем одним показом
         if (!prankByFlag && !enableForAll) {
         if (!prankFlag && this.config.runOncePerSession) {
             return;
             if (sessionStorage.getItem('april-fools-cube-shown') === '1') {
                return;
            }
            sessionStorage.setItem('april-fools-cube-shown', '1');
         }
         }


         this.injectStyles();
         this.injectStyles();
         this.createOverlay();
         this.createOverlay();
         this.prepareAudio();
         this.lockPageScroll();
        this.startSequence();
     },
     },


     injectStyles: function () {
     injectStyles: function () {
         if (document.getElementById('april-fools-cube-v2-styles')) return;
         if (document.getElementById('april-fools-prank-styles')) return;


         var style = document.createElement('style');
         var style = document.createElement('style');
         style.id = 'april-fools-cube-v2-styles';
         style.id = 'april-fools-prank-styles';
         style.textContent =
         style.textContent =
             '#april-fools-cube-overlay {' +
             '#april-fools-prank-overlay{' +
                 'position: fixed;' +
                 'position:fixed;' +
                 'inset: 0;' +
                 'inset:0;' +
                 'z-index: 2147483647;' +
                 'z-index:2147483647;' +
                'pointer-events: auto;' +
                 'background:#000;' +
                 'background: #000;' +
                 'overflow-y:auto;' +
                 'overflow: hidden;' +
                 'overflow-x:hidden;' +
            '}' +
                 'cursor:pointer;' +
            '#april-fools-cube-overlay .af-scene {' +
                 'transform:translateY(0);' +
                'position: absolute;' +
                 'transition:transform 5s linear;' +
                'inset: 0;' +
                'perspective: 1800px;' +
                'perspective-origin: 50% 50%;' +
                 'overflow: hidden;' +
            '}' +
            '#april-fools-cube-overlay .af-cube {' +
                'position: absolute;' +
                'inset: 0;' +
                'transform-style: preserve-3d;' +
                'transform-origin: right center;' +
                'will-change: transform, opacity;' +
            '}' +
            '#april-fools-cube-overlay.af-exit .af-cube {' +
                'animation: af-cube-rotate 1200ms cubic-bezier(0.2, 0.8, 0.2, 1) forwards;' +
            '}' +
            '#april-fools-cube-overlay .af-face {' +
                'position: absolute;' +
                'inset: 0;' +
                'backface-visibility: hidden;' +
                '-webkit-backface-visibility: hidden;' +
                'overflow: hidden;' +
            '}' +
            '#april-fools-cube-overlay .af-front {' +
                'transform: translateZ(1px);' +
                'background: #000;' +
            '}' +
            '#april-fools-cube-overlay .af-front img {' +
                'display: block;' +
                'width: 100%;' +
                'height: 100%;' +
                'object-fit: cover;' +
                'object-position: center top;' +
                'user-select: none;' +
                 '-webkit-user-drag: none;' +
                'pointer-events: none;' +
            '}' +
            '#april-fools-cube-overlay .af-right {' +
                 'transform-origin: left center;' +
                'transform: rotateY(90deg) translateX(0);' +
                // прозрачная грань: видна реальная страница как "другая сторона куба"
                 'background: transparent;' +
                'box-shadow: inset 0 0 120px rgba(0, 0, 0, 0.35);' +
             '}' +
             '}' +
             '#april-fools-cube-overlay .af-edge {' +
             '#april-fools-prank-overlay.closing{' +
                 'position: absolute;' +
                 'transform:translateY(-100%);' +
                'top: 0;' +
                'right: 0;' +
                'width: 36px;' +
                'height: 100%;' +
                'background: linear-gradient(to left, rgba(0,0,0,0.45), rgba(0,0,0,0));' +
                'pointer-events: none;' +
             '}' +
             '}' +
             '@keyframes af-cube-rotate {' +
             '#april-fools-prank-image{' +
                 '0% {' +
                 'display:block;' +
                    'transform: translateZ(0) rotateY(0deg);' +
                'width:100vw;' +
                    'opacity: 1;' +
                 'min-height:100vh;' +
                 '}' +
                 'height:auto;' +
                 '100% {' +
                'object-fit:cover;' +
                    'transform: translateZ(0) rotateY(-90deg);' +
                 'object-position:top center;' +
                    'opacity: 0.02;' +
                 '}' +
             '}';
             '}';
         document.head.appendChild(style);
         document.head.appendChild(style);
Строка 2363: Строка 2283:
     createOverlay: function () {
     createOverlay: function () {
         var overlay = document.createElement('div');
         var overlay = document.createElement('div');
         overlay.id = 'april-fools-cube-overlay';
         overlay.id = 'april-fools-prank-overlay';
 
        var scene = document.createElement('div');
        scene.className = 'af-scene';
 
        var cube = document.createElement('div');
        cube.className = 'af-cube';
 
        var front = document.createElement('div');
        front.className = 'af-face af-front';


         var img = document.createElement('img');
         var img = document.createElement('img');
         img.src = this.config.wikiImageUrl;
        img.id = 'april-fools-prank-image';
         img.alt = 'Wikipedia homepage screenshot';
         img.src = 'https://upload.wikimedia.org/wikipedia/commons/d/d4/2019_Screenshot_of_English_Wikipedia_homepage.png';
         img.alt = 'Wikipedia homepage';


         var right = document.createElement('div');
         overlay.appendChild(img);
        right.className = 'af-face af-right';
         document.body.appendChild(overlay);
 
        var edge = document.createElement('div');
        edge.className = 'af-edge';
 
        front.appendChild(img);
         cube.appendChild(front);
        cube.appendChild(right);
        cube.appendChild(edge);
        scene.appendChild(cube);
        overlay.appendChild(scene);


        document.body.appendChild(overlay);
         this.overlay = overlay;
         this.overlay = overlay;
    },
        this.imageLayer = img;
        this.audio = new Audio('https://zvukogram.com/mp3/40/stone-plate.mp3');
        this.audio.preload = 'auto';


    prepareAudio: function () {
         var self = this;
         this.clockAudio = new Audio(this.config.clockAudioUrl);
         overlay.addEventListener('click', function () {
         this.lampAudio = new Audio(this.config.lampAudioUrl);
            self.closeWithSound();
 
         }, { once: true });
        this.clockAudio.preload = 'auto';
        this.lampAudio.preload = 'auto';
 
         this.clockAudio.loop = true;
        this.clockAudio.volume = 0.45;
        this.lampAudio.loop = false;
        this.lampAudio.volume = 0.6;
     },
     },


     startSequence: function () {
     closeWithSound: function () {
         var self = this;
         if (this.closing || !this.overlay) return;
        this.closing = true;


         // Пробуем запустить сразу оба звука одновременно
         try {
        this.playBothSoundsNow();
            this.audio.currentTime = 0;
            this.audio.play();
        } catch (e) {
        }


         setTimeout(function () {
         this.overlay.classList.add('closing');
            self.startCubeTransition();
        }, this.config.overlayDurationMs);
    },


    playBothSoundsNow: function () {
         var self = this;
         var self = this;
         var playClock = this.clockAudio ? this.clockAudio.play() : Promise.resolve();
         this.overlay.addEventListener('transitionend', function handleEnd(e) {
        var playLamp = this.lampAudio ? this.lampAudio.play() : Promise.resolve();
            if (e.propertyName !== 'transform') return;
 
            self.overlay.removeEventListener('transitionend', handleEnd);
        Promise.allSettled([playClock, playLamp]).then(function (results) {
             self.unlockPageScroll();
             var hasRejected = results.some(function (r) { return r.status === 'rejected'; });
             if (self.overlay && self.overlay.parentNode) {
             if (hasRejected) {
                 self.overlay.parentNode.removeChild(self.overlay);
                 self.enableGestureUnlock();
             }
             }
            self.overlay = null;
         });
         });
     },
     },


     enableGestureUnlock: function () {
     lockPageScroll: function () {
         if (this.needsGestureUnlock) return;
         document.documentElement.style.overflow = 'hidden';
        this.needsGestureUnlock = true;
         document.body.style.overflow = 'hidden';
 
        var self = this;
        function unlockAndPlay() {
            self.needsGestureUnlock = false;
 
            if (self.clockAudio) {
                self.clockAudio.currentTime = 0;
                self.clockAudio.play().catch(function () {});
            }
            if (self.lampAudio) {
                self.lampAudio.currentTime = 0;
                self.lampAudio.play().catch(function () {});
            }
 
            document.removeEventListener('pointerdown', unlockAndPlay);
            document.removeEventListener('keydown', unlockAndPlay);
            document.removeEventListener('touchstart', unlockAndPlay);
        }
 
         document.addEventListener('pointerdown', unlockAndPlay, { once: true });
        document.addEventListener('keydown', unlockAndPlay, { once: true });
        document.addEventListener('touchstart', unlockAndPlay, { once: true });
    },
 
    startCubeTransition: function () {
        if (!this.overlay) return;
 
        // В момент вращения ещё раз запускаем "лампу", часы продолжают тикать
        if (this.lampAudio) {
            try {
                this.lampAudio.currentTime = 0;
                this.lampAudio.play().catch(function () {});
            } catch (e) {}
        }
 
        this.overlay.classList.add('af-exit');
 
        var self = this;
        setTimeout(function () {
            self.finish();
        }, this.config.cubeDurationMs + 80);
     },
     },


     finish: function () {
     unlockPageScroll: function () {
         if (this.clockAudio) {
         document.documentElement.style.overflow = '';
            this.clockAudio.pause();
         document.body.style.overflow = '';
            this.clockAudio.currentTime = 0;
         }
 
        if (this.overlay && this.overlay.parentNode) {
            this.overlay.parentNode.removeChild(this.overlay);
        }
        this.overlay = null;
     }
     }
};
};