MediaWiki:Common.js
MediaWiki interface page
More actions
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/**
* ╔══════════════════════════════════════════════════════════╗
* ║ DREAMCORE / WEIRDCORE — MediaWiki Common.js ║
* ║ Paste into MediaWiki:Common.js on your wiki ║
* ╚══════════════════════════════════════════════════════════╝
*
* Features:
* • Floating SVG decorations (mushrooms, eyes, stars, moons)
* • Glitch effect on page title
* • Ambient particle field
* • Subtle cursor trail
* • Random "dream" quotes in footer
* • Occasional blinking eye in sidebar
*/
( function () {
'use strict';
/* ─── SVG Icon Library ──────────────────────────────── */
const ICONS = {
mushroom: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 70" width="54" height="63">
<ellipse cx="30" cy="42" rx="11" ry="18" fill="#7a5aaa" opacity="0.65"/>
<ellipse cx="30" cy="42" rx="7" ry="15" fill="#9070c8" opacity="0.4"/>
<path d="M10 38 Q30 10 50 38 Q40 44 30 44 Q20 44 10 38Z" fill="#c4a0d8" opacity="0.82"/>
<path d="M10 38 Q30 10 50 38" fill="none" stroke="#e0c8f0" stroke-width="0.8" opacity="0.4"/>
<circle cx="18" cy="28" r="3.5" fill="white" opacity="0.5"/>
<circle cx="32" cy="22" r="2.8" fill="white" opacity="0.5"/>
<circle cx="43" cy="30" r="2" fill="white" opacity="0.5"/>
<circle cx="24" cy="20" r="1.5" fill="white" opacity="0.4"/>
<ellipse cx="30" cy="44" rx="8" ry="2.5" fill="#3d2860" opacity="0.4"/>
</svg>`,
eye: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 45" width="72" height="40">
<path d="M4 22 Q40 2 76 22 Q40 42 4 22Z" fill="#2a1f48" stroke="#7a5aaa" stroke-width="0.8" opacity="0.9"/>
<circle cx="40" cy="22" r="10" fill="#1a1030"/>
<circle cx="40" cy="22" r="7" fill="#8a5ac8"/>
<circle cx="40" cy="22" r="4" fill="#0d0818"/>
<circle cx="43" cy="19" r="2.5" fill="white" opacity="0.85"/>
<circle cx="37" cy="25" r="1" fill="white" opacity="0.4"/>
<ellipse cx="40" cy="22" rx="10" ry="10" fill="none" stroke="#c4a0d8" stroke-width="0.4" opacity="0.5"/>
<path d="M4 22 Q40 2 76 22" fill="none" stroke="#a07ad4" stroke-width="0.5" opacity="0.3"/>
</svg>`,
star: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="44" height="44">
<polygon points="25,3 30,18 46,18 33,29 38,45 25,35 12,45 17,29 4,18 20,18"
fill="#c4a04a" opacity="0.7" stroke="#e0c880" stroke-width="0.5"/>
<polygon points="25,8 29,20 42,20 32,28 36,41 25,33 14,41 18,28 8,20 21,20"
fill="#e0c880" opacity="0.35"/>
</svg>`,
moon: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 55" width="44" height="48">
<path d="M28 5 A22 22 0 1 0 28 48 A14 14 0 1 1 28 5Z"
fill="#c8b0e8" opacity="0.65" stroke="#d8c8f0" stroke-width="0.6"/>
<circle cx="20" cy="18" r="2" fill="white" opacity="0.2"/>
<circle cx="30" cy="36" r="1.5" fill="white" opacity="0.15"/>
<circle cx="16" cy="32" r="1" fill="white" opacity="0.12"/>
</svg>`,
flower: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55 55" width="48" height="48">
<g opacity="0.7">
<ellipse cx="27" cy="14" rx="6" ry="10" fill="#c46890" opacity="0.65" transform="rotate(0,27,27)"/>
<ellipse cx="27" cy="14" rx="6" ry="10" fill="#c46890" opacity="0.65" transform="rotate(45,27,27)"/>
<ellipse cx="27" cy="14" rx="6" ry="10" fill="#c46890" opacity="0.65" transform="rotate(90,27,27)"/>
<ellipse cx="27" cy="14" rx="6" ry="10" fill="#c46890" opacity="0.65" transform="rotate(135,27,27)"/>
<ellipse cx="27" cy="14" rx="6" ry="10" fill="#d888b0" opacity="0.55" transform="rotate(22.5,27,27)"/>
<ellipse cx="27" cy="14" rx="6" ry="10" fill="#d888b0" opacity="0.55" transform="rotate(67.5,27,27)"/>
<ellipse cx="27" cy="14" rx="6" ry="10" fill="#d888b0" opacity="0.55" transform="rotate(112.5,27,27)"/>
<ellipse cx="27" cy="14" rx="6" ry="10" fill="#d888b0" opacity="0.55" transform="rotate(157.5,27,27)"/>
</g>
<circle cx="27" cy="27" r="7" fill="#f0e070" opacity="0.8"/>
<circle cx="27" cy="27" r="4" fill="#c4a04a" opacity="0.9"/>
</svg>`,
droplet: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 35 50" width="30" height="43">
<path d="M17 3 Q32 22 32 32 A15 15 0 0 1 2 32 Q2 22 17 3Z"
fill="#56a99f" opacity="0.55" stroke="#88c8c0" stroke-width="0.7"/>
<path d="M17 12 Q26 26 26 32 A9 9 0 0 1 8 32 Q8 26 17 12Z"
fill="white" opacity="0.12"/>
</svg>`,
spiral: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55 55" width="48" height="48">
<path d="M27 27 m0,-20 a20,20 0 1,1 -0.1,0
m0,5 a15,15 0 1,1 -0.1,0
m0,5 a10,10 0 1,1 -0.1,0
m0,5 a5,5 0 1,1 -0.1,0"
fill="none" stroke="#a07ad4" stroke-width="1.2" opacity="0.5"
stroke-linecap="round"/>
</svg>`,
};
/* ─── Dream Quotes ───────────────────────────────────── */
const DREAM_QUOTES = [
'you have been here before.',
'the door was always open.',
'something is watching through the grass.',
'the hallway goes on.',
'you almost remember.',
'it looks like home but it isn\'t.',
'the light is wrong here.',
'you recognize the music but not the words.',
'the stairs lead somewhere new.',
'the sky here has no sun.',
'you\'ve lost count of the rooms.',
'the window shows a different outside.',
];
/* ─── Utility ────────────────────────────────────────── */
function rand( min, max ) {
return Math.random() * ( max - min ) + min;
}
function randInt( min, max ) {
return Math.floor( rand( min, max + 1 ) );
}
function randPick( arr ) {
return arr[ Math.floor( Math.random() * arr.length ) ];
}
/* ─── 1. Floating SVG Decorations ───────────────────── */
function spawnDecorations() {
const icons = Object.entries( ICONS );
const count = 7;
for ( let i = 0; i < count; i++ ) {
const [ name, svg ] = randPick( icons );
const el = document.createElement( 'div' );
el.className = 'dreamcore-deco deco-' + name;
el.innerHTML = svg;
el.style.cssText = [
'position:fixed',
'pointer-events:none',
'z-index:1',
'left:' + rand( 2, 92 ) + 'vw',
'top:' + rand( 5, 90 ) + 'vh',
'opacity:0',
'animation-delay:' + rand( 0, 4 ) + 's',
'animation-duration:' + rand( 9, 18 ) + 's',
'transform:rotate(' + rand( -25, 25 ) + 'deg) scale(' + rand( 0.5, 1.3 ) + ')',
].join( ';' );
// Float up animation
const duration = rand( 14, 28 );
const delay = rand( 0, 6 );
el.style.animation = [
'fadeInDeco 3s ' + delay + 's ease forwards',
'floatSymbol ' + duration + 's ' + delay + 's ease-in-out infinite',
].join( ', ' );
document.body.appendChild( el );
}
}
/* ─── 2. Ambient Particle Field ─────────────────────── */
function createParticleField() {
const canvas = document.createElement( 'canvas' );
canvas.style.cssText = [
'position:fixed', 'inset:0', 'width:100vw', 'height:100vh',
'pointer-events:none', 'z-index:0', 'opacity:0.35',
].join( ';' );
document.body.appendChild( canvas );
const ctx = canvas.getContext( '2d' );
let W = canvas.width = window.innerWidth;
let H = canvas.height = window.innerHeight;
window.addEventListener( 'resize', () => {
W = canvas.width = window.innerWidth;
H = canvas.height = window.innerHeight;
} );
const PARTICLE_COUNT = 55;
const COLORS = [ '#a07ad4', '#56a99f', '#c46890', '#c4a04a', '#7da8cc' ];
const particles = Array.from( { length: PARTICLE_COUNT }, () => ( {
x: rand( 0, W ), y: rand( 0, H ),
vx: rand( -0.15, 0.15 ), vy: rand( -0.22, -0.05 ),
r: rand( 0.8, 2.2 ),
color: randPick( COLORS ),
alpha: rand( 0.08, 0.35 ),
pulse: rand( 0, Math.PI * 2 ),
pulseSpeed: rand( 0.008, 0.025 ),
} ) );
function tick() {
ctx.clearRect( 0, 0, W, H );
for ( const p of particles ) {
p.pulse += p.pulseSpeed;
p.x += p.vx;
p.y += p.vy;
if ( p.y < -10 ) { p.y = H + 5; p.x = rand( 0, W ); }
if ( p.x < -10 ) p.x = W + 5;
if ( p.x > W + 10 ) p.x = -5;
const a = p.alpha * ( 0.6 + 0.4 * Math.sin( p.pulse ) );
ctx.beginPath();
ctx.arc( p.x, p.y, p.r, 0, Math.PI * 2 );
ctx.fillStyle = p.color;
ctx.globalAlpha = a;
ctx.fill();
}
ctx.globalAlpha = 1;
// Draw faint connecting lines between nearby particles
for ( let i = 0; i < particles.length; i++ ) {
for ( let j = i + 1; j < particles.length; j++ ) {
const dx = particles[i].x - particles[j].x;
const dy = particles[i].y - particles[j].y;
const dist = Math.sqrt( dx * dx + dy * dy );
if ( dist < 90 ) {
ctx.beginPath();
ctx.moveTo( particles[i].x, particles[i].y );
ctx.lineTo( particles[j].x, particles[j].y );
ctx.strokeStyle = '#a07ad4';
ctx.globalAlpha = ( 1 - dist / 90 ) * 0.06;
ctx.lineWidth = 0.5;
ctx.stroke();
}
}
}
ctx.globalAlpha = 1;
requestAnimationFrame( tick );
}
tick();
}
/* ─── 3. Glitch Title ────────────────────────────────── */
function initGlitchTitle() {
const h1 = document.getElementById( 'firstHeading' ) ||
document.querySelector( '.firstHeading' );
if ( !h1 ) return;
const text = h1.textContent.trim();
h1.setAttribute( 'data-title', text );
h1.classList.add( 'glitch-title' );
// Occasional flicker
setInterval( () => {
if ( Math.random() > 0.7 ) {
h1.style.opacity = '0.6';
setTimeout( () => { h1.style.opacity = ''; }, 60 + Math.random() * 80 );
}
}, 4000 );
}
/* ─── 4. Cursor Trail ───────────────────────────────── */
function initCursorTrail() {
const TRAIL_COUNT = 8;
const trail = [];
for ( let i = 0; i < TRAIL_COUNT; i++ ) {
const dot = document.createElement( 'div' );
dot.style.cssText = [
'position:fixed',
'width:' + ( 3 - i * 0.3 ) + 'px',
'height:' + ( 3 - i * 0.3 ) + 'px',
'border-radius:50%',
'background:' + ( i % 2 === 0 ? '#a07ad4' : '#56a99f' ),
'pointer-events:none',
'z-index:9999',
'opacity:' + ( 0.5 - i * 0.06 ),
'transform:translate(-50%,-50%)',
'transition:left ' + ( 0.04 + i * 0.02 ) + 's,top ' + ( 0.04 + i * 0.02 ) + 's',
'will-change:left,top',
].join( ';' );
document.body.appendChild( dot );
trail.push( dot );
}
document.addEventListener( 'mousemove', ( e ) => {
trail.forEach( ( dot ) => {
dot.style.left = e.clientX + 'px';
dot.style.top = e.clientY + 'px';
} );
} );
}
/* ─── 5. Dream Quote in Footer ──────────────────────── */
function injectDreamQuote() {
const footer = document.getElementById( 'footer' ) ||
document.querySelector( '.mw-footer' );
if ( !footer ) return;
const quote = randPick( DREAM_QUOTES );
const el = document.createElement( 'div' );
el.id = 'dreamcore-quote';
el.style.cssText = [
'text-align:center',
'font-family:"IM Fell English",Georgia,serif',
'font-style:italic',
'font-size:13px',
'color:rgba(92,82,128,0.6)',
'letter-spacing:0.12em',
'padding:12px 0 4px',
'opacity:0',
'animation:fadeInDeco 4s 2s ease forwards',
].join( ';' );
el.textContent = '— ' + quote;
footer.insertBefore( el, footer.firstChild );
}
/* ─── 6. Sidebar Blinking Eye ───────────────────────── */
function addSidebarEye() {
const panel = document.getElementById( 'mw-panel' ) ||
document.querySelector( '.mw-sidebar' );
if ( !panel ) return;
const eye = document.createElement( 'div' );
eye.style.cssText = [
'text-align:center',
'padding:16px 0 8px',
'opacity:0',
'animation:fadeInDeco 5s 3s ease forwards',
].join( ';' );
eye.innerHTML = ICONS.eye;
eye.querySelector( 'svg' ).style.cssText = [
'width:56px', 'height:32px', 'opacity:0.2', 'display:inline-block',
].join( ';' );
// Blink occasionally
setInterval( () => {
const svg = eye.querySelector( 'svg' );
if ( Math.random() > 0.5 ) {
svg.style.transform = 'scaleY(0.08)';
svg.style.transition = 'transform 0.07s';
setTimeout( () => {
svg.style.transform = 'scaleY(1)';
}, 100 + Math.random() * 60 );
}
}, 3500 );
panel.appendChild( eye );
}
/* ─── 7. Haiku Tooltips on headings ────────────────── */
const HEADING_HAIKUS = [
'a door left ajar\nthe room beyond holds no walls\nonly more hallway',
'the clock face is blank\nbut you know the time somehow\nit\'s always the same',
'you recognize this\nbut have never been here—how\nlong have you been here',
'through the window: green\nthrough the other window: green\nboth look different',
];
function addHeadingTooltips() {
const headings = document.querySelectorAll( '.mw-body-content h2' );
headings.forEach( ( h, i ) => {
h.title = HEADING_HAIKUS[ i % HEADING_HAIKUS.length ];
h.style.cursor = 'default';
} );
}
/* ─── 8. Random Mushroom near TOC ───────────────────── */
function addTocMushroom() {
const toc = document.getElementById( 'toc' ) ||
document.querySelector( '.toc' );
if ( !toc ) return;
const wrap = document.createElement( 'div' );
wrap.style.cssText = [
'position:absolute',
'right:-24px',
'top:-20px',
'opacity:0.22',
'transform:rotate(12deg)',
'pointer-events:none',
].join( ';' );
wrap.innerHTML = ICONS.mushroom;
wrap.querySelector( 'svg' ).style.cssText = 'width:48px;height:54px;';
toc.style.position = 'relative';
toc.appendChild( wrap );
}
/* ─── 9. Subtle Page-load Fade ─────────────────────── */
function pageLoadFade() {
const content = document.getElementById( 'content' ) ||
document.querySelector( '.mw-body' );
if ( !content ) return;
content.style.opacity = '0';
content.style.transform = 'translateY(6px)';
content.style.transition = 'opacity 0.7s ease, transform 0.7s ease';
requestAnimationFrame( () => {
requestAnimationFrame( () => {
content.style.opacity = '';
content.style.transform = '';
} );
} );
}
/* ─── Init ───────────────────────────────────────────── */
function init() {
spawnDecorations();
createParticleField();
initGlitchTitle();
initCursorTrail();
injectDreamQuote();
addSidebarEye();
addHeadingTooltips();
addTocMushroom();
pageLoadFade();
}
if ( document.readyState === 'loading' ) {
document.addEventListener( 'DOMContentLoaded', init );
} else {
init();
}
}() );