Warning: file_exists(): open_basedir restriction in effect. File(/bin/bash) is not within the allowed path(s): (/var/www/vhosts/nothing-news.de/:/tmp/) in /var/www/vhosts/nothing-news.de/wiki.nothing-news.de/w/includes/shell/CommandFactory.php on line 119
MediaWiki:Common.js: Difference between revisions - Nothing Wiki
Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

MediaWiki:Common.js: Difference between revisions

MediaWiki interface page
No edit summary
No edit summary
 
(9 intermediate revisions by the same user not shown)
Line 1: Line 1:
/* Any JavaScript here will be loaded for all users on every page load. */
/* Any JavaScript here will be loaded for all users on every page load. */
(function () {
/* Schönes Tooltip-System auf Basis von alt-Attributen
   const tooltip = document.createElement("div");
  Funktioniert sicher mit Citizen & MediaWiki UI */
   tooltip.className = "custom-tooltip";
 
(function() {
  'use strict';
 
  // Tooltip erstellen
   const tooltip = document.createElement('div');
   tooltip.id = 'citizen-tooltip';
  Object.assign(tooltip.style, {
    position: 'absolute',
    zIndex: 999999,
    padding: '10px 14px',
    borderRadius: '8px',
    maxWidth: '240px',
    wordWrap: 'break-word',
    fontSize: '13px',
    lineHeight: '1.4',
    pointerEvents: 'none',
    opacity: '0',
    transform: 'translateY(4px)',
    transition: 'opacity 0.25s ease, transform 0.25s ease',
    boxShadow: '0 4px 12px rgba(0,0,0,0.3)',
    backdropFilter: 'blur(6px)',
    backgroundColor: 'var(--citizen-tooltip-bg, rgba(25,25,25,0.95))',
    color: 'var(--citizen-tooltip-text, #fff)',
  });
   document.body.appendChild(tooltip);
   document.body.appendChild(tooltip);


   let active = null;
  // Pfeil
  const arrow = document.createElement('div');
  Object.assign(arrow.style, {
    position: 'absolute',
    width: '0',
    height: '0',
    borderLeft: '7px solid transparent',
    borderRight: '7px solid transparent',
    borderTop: '7px solid var(--citizen-tooltip-bg, rgba(25,25,25,0.95))',
    pointerEvents: 'none',
  });
  tooltip.appendChild(arrow);
 
   let activeEl = null;
   let storedTitle = null;
   let storedTitle = null;


   function showTooltip(target, x, y) {
   function showTooltip(e) {
     const text = target.getAttribute("alt") || target.getAttribute("title");
     const el = e.target.closest('[title]');
     if (!text) return;
     if (!el) return;


     // Browser-Tooltip deaktivieren
     storedTitle = el.getAttribute('title');
     if (target.hasAttribute("title")) {
     if (!storedTitle) return;
       storedTitle = target.getAttribute("title");
 
      target.removeAttribute("title");
    activeEl = el;
     }
    el.removeAttribute('title');
    tooltip.textContent = storedTitle;
    tooltip.appendChild(arrow);
    tooltip.style.opacity = '0';
    tooltip.style.transform = 'translateY(4px)';
 
    // Animation / Bounce
    requestAnimationFrame(() => {
       positionTooltip(el);
      tooltip.style.opacity = '1';
      tooltip.style.transform = 'translateY(0)';
    });
 
    el.addEventListener('mouseleave', hideTooltip, { once: true });
  }
 
  function hideTooltip() {
    if (!activeEl) return;
    tooltip.style.opacity = '0';
    tooltip.style.transform = 'translateY(4px)';
    activeEl.setAttribute('title', storedTitle);
     activeEl = null;
    storedTitle = null;
  }
 
  function positionTooltip(el) {
    const rect = el.getBoundingClientRect();
    const ttRect = tooltip.getBoundingClientRect();
    const spacing = 8;


     tooltip.textContent = text;
     const spaceAbove = rect.top;
     tooltip.style.display = "block";
    const spaceBelow = window.innerHeight - rect.bottom;
     const spaceLeft = rect.left;
    const spaceRight = window.innerWidth - rect.right;


     const updatePosition = (cx, cy) => {
     let position = 'top';
      const rect = tooltip.getBoundingClientRect();
    if (spaceAbove < ttRect.height + spacing && spaceBelow > ttRect.height + spacing) position = 'bottom';
      const offset = 12;
    if (spaceRight > ttRect.width + spacing && rect.left < 50) position = 'right';
      let left = cx + offset;
    if (spaceLeft > ttRect.width + spacing && rect.right > window.innerWidth - 50) position = 'left';
      let top = cy + offset;
      if (left + rect.width > window.innerWidth - 10)
        left = cx - rect.width - offset;
      if (top + rect.height > window.innerHeight - 10)
        top = cy - rect.height - offset;
      tooltip.style.left = `${left}px`;
      tooltip.style.top = `${top}px`;
    };


     const moveHandler = e => updatePosition(e.clientX, e.clientY);
     let top, left;
    updatePosition(x, y);
    switch(position) {
    document.addEventListener("mousemove", moveHandler);
      case 'top':
        top = rect.top - ttRect.height - spacing;
        left = rect.left + rect.width/2 - ttRect.width/2;
        Object.assign(arrow.style, {
          top: '100%',
          left: '50%',
          transform: 'translateX(-50%) rotate(0deg)'
        });
        break;
      case 'bottom':
        top = rect.bottom + spacing;
        left = rect.left + rect.width/2 - ttRect.width/2;
        Object.assign(arrow.style, {
          top: '-7px',
          left: '50%',
          transform: 'translateX(-50%) rotate(180deg)'
        });
        break;
      case 'right':
        top = rect.top + rect.height/2 - ttRect.height/2;
        left = rect.right + spacing;
        Object.assign(arrow.style, {
          top: '50%',
          left: '-7px',
          transform: 'translateY(-50%) rotate(90deg)'
        });
        break;
      case 'left':
        top = rect.top + rect.height/2 - ttRect.height/2;
        left = rect.left - ttRect.width - spacing;
        Object.assign(arrow.style, {
          top: '50%',
          left: '100%',
          transform: 'translateY(-50%) rotate(-90deg)'
        });
        break;
    }


     target.addEventListener(
     // Clamp an Viewport
      "mouseleave",
    if (left < 4) left = 4;
      () => {
    if (left + ttRect.width > window.innerWidth - 4)
        tooltip.classList.remove("visible");
      left = window.innerWidth - ttRect.width - 4;
        tooltip.style.display = "none";
    if (top < 4) top = 4;
        document.removeEventListener("mousemove", moveHandler);
    if (top + ttRect.height > window.innerHeight - 4)
        if (storedTitle) {
      top = window.innerHeight - ttRect.height - 4;
          target.setAttribute("title", storedTitle);
          storedTitle = null;
        }
        active = null;
      },
      { once: true }
    );


     requestAnimationFrame(() => tooltip.classList.add("visible"));
     tooltip.style.top = `${top + window.scrollY}px`;
    tooltip.style.left = `${left + window.scrollX}px`;
   }
   }


  // Globaler Mouseover – auch für nachgeladene Elemente
   document.addEventListener('mouseover', showTooltip);
   document.addEventListener("mouseover", e => {
    const target = e.target.closest("[title], [alt]");
    if (!target || active === target) return;
    active = target;
    const x = e.clientX;
    const y = e.clientY;
    showTooltip(target, x, y);
  });
 
  // optional: bei DOM-Änderungen Tooltips neu aktivieren (z. B. wenn Skin-Module UI nachladen)
  const observer = new MutationObserver(() => {
    // nichts zu tun – der Eventlistener ist global, aber dies stellt sicher, dass der Tooltip-Container immer oben bleibt
    document.body.appendChild(tooltip);
  });
  observer.observe(document.body, { childList: true, subtree: true });
})();
})();

Latest revision as of 23:59, 1 November 2025

/* Any JavaScript here will be loaded for all users on every page load. */
/* Schönes Tooltip-System auf Basis von alt-Attributen
   Funktioniert sicher mit Citizen & MediaWiki UI */

(function() {
  'use strict';

  // Tooltip erstellen
  const tooltip = document.createElement('div');
  tooltip.id = 'citizen-tooltip';
  Object.assign(tooltip.style, {
    position: 'absolute',
    zIndex: 999999,
    padding: '10px 14px',
    borderRadius: '8px',
    maxWidth: '240px',
    wordWrap: 'break-word',
    fontSize: '13px',
    lineHeight: '1.4',
    pointerEvents: 'none',
    opacity: '0',
    transform: 'translateY(4px)',
    transition: 'opacity 0.25s ease, transform 0.25s ease',
    boxShadow: '0 4px 12px rgba(0,0,0,0.3)',
    backdropFilter: 'blur(6px)',
    backgroundColor: 'var(--citizen-tooltip-bg, rgba(25,25,25,0.95))',
    color: 'var(--citizen-tooltip-text, #fff)',
  });
  document.body.appendChild(tooltip);

  // Pfeil
  const arrow = document.createElement('div');
  Object.assign(arrow.style, {
    position: 'absolute',
    width: '0',
    height: '0',
    borderLeft: '7px solid transparent',
    borderRight: '7px solid transparent',
    borderTop: '7px solid var(--citizen-tooltip-bg, rgba(25,25,25,0.95))',
    pointerEvents: 'none',
  });
  tooltip.appendChild(arrow);

  let activeEl = null;
  let storedTitle = null;

  function showTooltip(e) {
    const el = e.target.closest('[title]');
    if (!el) return;

    storedTitle = el.getAttribute('title');
    if (!storedTitle) return;

    activeEl = el;
    el.removeAttribute('title');
    tooltip.textContent = storedTitle;
    tooltip.appendChild(arrow);
    tooltip.style.opacity = '0';
    tooltip.style.transform = 'translateY(4px)';

    // Animation / Bounce
    requestAnimationFrame(() => {
      positionTooltip(el);
      tooltip.style.opacity = '1';
      tooltip.style.transform = 'translateY(0)';
    });

    el.addEventListener('mouseleave', hideTooltip, { once: true });
  }

  function hideTooltip() {
    if (!activeEl) return;
    tooltip.style.opacity = '0';
    tooltip.style.transform = 'translateY(4px)';
    activeEl.setAttribute('title', storedTitle);
    activeEl = null;
    storedTitle = null;
  }

  function positionTooltip(el) {
    const rect = el.getBoundingClientRect();
    const ttRect = tooltip.getBoundingClientRect();
    const spacing = 8;

    const spaceAbove = rect.top;
    const spaceBelow = window.innerHeight - rect.bottom;
    const spaceLeft = rect.left;
    const spaceRight = window.innerWidth - rect.right;

    let position = 'top';
    if (spaceAbove < ttRect.height + spacing && spaceBelow > ttRect.height + spacing) position = 'bottom';
    if (spaceRight > ttRect.width + spacing && rect.left < 50) position = 'right';
    if (spaceLeft > ttRect.width + spacing && rect.right > window.innerWidth - 50) position = 'left';

    let top, left;
    switch(position) {
      case 'top':
        top = rect.top - ttRect.height - spacing;
        left = rect.left + rect.width/2 - ttRect.width/2;
        Object.assign(arrow.style, {
          top: '100%',
          left: '50%',
          transform: 'translateX(-50%) rotate(0deg)'
        });
        break;
      case 'bottom':
        top = rect.bottom + spacing;
        left = rect.left + rect.width/2 - ttRect.width/2;
        Object.assign(arrow.style, {
          top: '-7px',
          left: '50%',
          transform: 'translateX(-50%) rotate(180deg)'
        });
        break;
      case 'right':
        top = rect.top + rect.height/2 - ttRect.height/2;
        left = rect.right + spacing;
        Object.assign(arrow.style, {
          top: '50%',
          left: '-7px',
          transform: 'translateY(-50%) rotate(90deg)'
        });
        break;
      case 'left':
        top = rect.top + rect.height/2 - ttRect.height/2;
        left = rect.left - ttRect.width - spacing;
        Object.assign(arrow.style, {
          top: '50%',
          left: '100%',
          transform: 'translateY(-50%) rotate(-90deg)'
        });
        break;
    }

    // Clamp an Viewport
    if (left < 4) left = 4;
    if (left + ttRect.width > window.innerWidth - 4)
      left = window.innerWidth - ttRect.width - 4;
    if (top < 4) top = 4;
    if (top + ttRect.height > window.innerHeight - 4)
      top = window.innerHeight - ttRect.height - 4;

    tooltip.style.top = `${top + window.scrollY}px`;
    tooltip.style.left = `${left + window.scrollX}px`;
  }

  document.addEventListener('mouseover', showTooltip);
})();
Cookies help us deliver our services. By using our services, you agree to our use of cookies.