MediaWiki:Common.js: Unterschied zwischen den Versionen
Admin (Diskussion | Beiträge) Keine Bearbeitungszusammenfassung |
Admin (Diskussion | Beiträge) Keine Bearbeitungszusammenfassung |
||
| Zeile 394: | Zeile 394: | ||
} | } | ||
}); | |||
/* Fireworks Popup (v1) – once per day, canvas fireworks, motion-safe */ | |||
mw.loader.using(['mediawiki.util','jquery']).then(function(){ | |||
(function($, mw){ | |||
'use strict'; | |||
var CONFIG = { | |||
enabled: true, | |||
id: 'fireworks_popup_v1', // bei Änderungen erhöhen | |||
title: 'Neu: Abfüllungen bewerten 🎉', | |||
messageHTML: | |||
'<p>Ab sofort kannst du im Wiki <strong>jede Abfüllung bewerten</strong> – ' + | |||
'teile deine Meinung und hilf anderen bei der Auswahl!</p>', | |||
cta: { text: 'Jetzt bewerten', url: 'https://ados-wiki.de/index.php?title=Test2' }, // URL anpassen | |||
showOnNamespaces: 'all', // oder z. B. [0,4] | |||
dailyLimit: 1, | |||
escToClose: true, | |||
clickBackdropToClose: true | |||
}; | |||
if (!CONFIG.enabled) return; | |||
// Namespace-Filter | |||
var ns = mw.config.get('wgNamespaceNumber'); | |||
if (CONFIG.showOnNamespaces !== 'all' && | |||
$.isArray(CONFIG.showOnNamespaces) && | |||
$.inArray(ns, CONFIG.showOnNamespaces) === -1) return; | |||
// Einmal pro Tag | |||
var isAnon = (mw.config.get('wgUserName') === null); | |||
function g(k){ try{return localStorage.getItem(k);}catch(e){return null;} } | |||
function s(k,v){ try{localStorage.setItem(k,v);}catch(e){} } | |||
var key = 'popup_' + CONFIG.id + (isAnon?':anon':':user'); | |||
var today = (function(d){return d.getFullYear()+'-'+('0'+(d.getMonth()+1)).slice(-2)+'-'+('0'+d.getDate()).slice(-2);})(new Date()); | |||
if (g(key) === today) return; | |||
function markSeen(){ s(key, today); } | |||
$(function(){ | |||
// Grundgerüst | |||
var $overlay = $('<div>', {'class':'mw-popup-overlay'}); | |||
var $modal = $('<div>', {'class':'mw-popup-modal','role':'dialog','aria-modal':'true','aria-labelledby':'mw-fw-title'}); | |||
// Feuerwerk-Canvas (liegt oben im Modal, hinter Text reservierter Bereich) | |||
var $fwWrap = $('<div>', {'class':'mw-fw-canvas-wrap'}); | |||
var $canvas = $('<canvas>', {'class':'mw-fw-canvas', 'aria-hidden':'true'}); | |||
$fwWrap.append($canvas); | |||
var $title = $('<h2>', { id: 'mw-fw-title' }).text(CONFIG.title); | |||
var $content = $('<div>', {'class':'mw-popup-content'}).html(CONFIG.messageHTML); | |||
var $buttons = $('<div>', {'class':'mw-popup-button-row'}); | |||
var $ok = $('<button>', {'class':'mw-popup-close', type:'button'}).text('OK'); | |||
$buttons.append($ok); | |||
if (CONFIG.cta && CONFIG.cta.url) { | |||
$buttons.append($('<a>', { | |||
'class':'mw-popup-wiki-button', | |||
'href': CONFIG.cta.url, | |||
'target':'_blank', | |||
'rel':'noopener' | |||
}).text(CONFIG.cta.text || 'Mehr')); | |||
} | |||
$modal.append($fwWrap, $title, $content, $buttons); | |||
$('body').append($overlay, $modal); | |||
// Fokus | |||
setTimeout(function(){ try{$modal.attr('tabindex','-1').focus();}catch(e){} }, 0); | |||
function close(){ | |||
stopFireworks(); | |||
markSeen(); | |||
$overlay.remove(); $modal.remove(); | |||
$(document).off('keydown.mwfw'); | |||
} | |||
$ok.on('click', close); | |||
if (CONFIG.clickBackdropToClose) $overlay.on('click', close); | |||
if (CONFIG.escToClose) { | |||
$(document).on('keydown.mwfw', function(e){ | |||
var k = e.key || e.keyCode; | |||
if (k==='Escape' || k==='Esc' || k===27){ e.preventDefault(); close(); } | |||
}); | |||
} | |||
// ==== Fireworks (Canvas) ==== | |||
var canvas = $canvas[0], ctx = canvas.getContext('2d'); | |||
var dpr = Math.max(1, window.devicePixelRatio || 1); | |||
var w=0,h=0, raf=null, particles=[], bursts=[]; | |||
var reduceMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; | |||
function resize(){ | |||
var rect = $fwWrap[0].getBoundingClientRect(); | |||
w = Math.floor(rect.width * dpr); | |||
h = Math.floor(rect.height * dpr); | |||
canvas.width = w; canvas.height = h; | |||
canvas.style.width = rect.width+'px'; | |||
canvas.style.height = rect.height+'px'; | |||
} | |||
function rand(min,max){ return Math.random()*(max-min)+min; } | |||
function hsla(h,s,l,a){ return 'hsla('+h+','+s+'%,'+l+'%,'+a+')'; } | |||
function spawnBurst(x,y){ | |||
var hue = Math.floor(rand(0,360)); | |||
for (var i=0;i<80;i++){ | |||
var angle = rand(0, Math.PI*2); | |||
var speed = rand(1.2, 3.2); | |||
particles.push({ | |||
x:x, y:y, | |||
vx: Math.cos(angle)*speed, | |||
vy: Math.sin(angle)*speed, | |||
life: rand(45, 80), | |||
age: 0, | |||
hue: hue + rand(-15,15), | |||
}); | |||
} | |||
} | |||
function tick(){ | |||
ctx.clearRect(0,0,w,h); | |||
// leichte Verdunkelung für Nachleuchten | |||
ctx.globalCompositeOperation = 'source-over'; | |||
ctx.fillStyle = 'rgba(0,0,0,0.08)'; | |||
ctx.fillRect(0,0,w,h); | |||
ctx.globalCompositeOperation = 'lighter'; | |||
var next = []; | |||
for (var i=0;i<particles.length;i++){ | |||
var p = particles[i]; | |||
p.age++; | |||
// Schwerkraft + Dämpfung | |||
p.vy += 0.02; | |||
p.vx *= 0.99; p.vy *= 0.99; | |||
p.x += p.vx*dpr; p.y += p.vy*dpr; | |||
var alpha = Math.max(0, 1 - p.age/p.life); | |||
if (alpha > 0){ | |||
ctx.beginPath(); | |||
ctx.fillStyle = hsla(p.hue, 100, 60, alpha); | |||
ctx.arc(p.x, p.y, Math.max(0.5, 2*alpha), 0, Math.PI*2); | |||
ctx.fill(); | |||
next.push(p); | |||
} | |||
} | |||
particles = next; | |||
// regelmäßig neue Bursts | |||
if (Math.random() < 0.08) { | |||
var bx = rand(w*0.15, w*0.85); | |||
var by = rand(h*0.2, h*0.6); | |||
spawnBurst(bx, by); | |||
} | |||
raf = requestAnimationFrame(tick); | |||
} | |||
function startFireworks(){ | |||
if (reduceMotion) return; // respektiert Nutzerpräferenz | |||
resize(); | |||
// Initial 2 Bursts | |||
spawnBurst(w*0.3, h*0.45); | |||
spawnBurst(w*0.7, h*0.35); | |||
if (!raf) raf = requestAnimationFrame(tick); | |||
window.addEventListener('resize', resize); | |||
} | |||
function stopFireworks(){ | |||
if (raf){ cancelAnimationFrame(raf); raf=null; } | |||
window.removeEventListener('resize', resize); | |||
} | |||
startFireworks(); | |||
// Direkt als gesehen markieren, damit reload am selben Tag nicht neu zeigt | |||
markSeen(); | |||
}); | |||
})(jQuery, mw); | |||
}); | }); | ||