Jump to content

MediaWiki:Common.js: Difference between revisions

From Yusupov's House
No edit summary
No edit summary
 
(2 intermediate revisions by the same user not shown)
Line 1: Line 1:
/* Promote infobox image to a 10:7 hero above the title (touch devices only) */
/* Hero image above the title, cloned from infobox (touch devices only) */
mw.hook('wikipage.content').add(function ($content) {
(function () {
  // Only on touch devices
  if (!window.matchMedia || !matchMedia('(pointer: coarse)').matches) return;
  if (!window.matchMedia || !window.matchMedia('(pointer: coarse)').matches) return;
  if (document.querySelector('.article-hero')) return;
 
  function bestSrc(img) {
    // prefer the largest srcset candidate if present
    var ss = img.getAttribute('srcset');
    if (!ss) return img.currentSrc || img.src || '';
    var last = ss.split(',').pop().trim().split(/\s+/)[0];
    return last || img.currentSrc || img.src || '';
  }
 
  function run() {
    var imgCell = document.querySelector('table.infobox .infobox-image-cell');
    if (!imgCell) return;
 
    // Prefer cloning a wrapper so link/captions survive if present
    var wrapper = imgCell.querySelector('a, span, figure, picture') || imgCell.querySelector('img');
    if (!wrapper) return;


  // Avoid duplicates
    var header = document.querySelector('.mw-body-header') || document.getElementById('firstHeading');
  if (document.querySelector('.article-hero')) return;
    if (!header) return;
 
    // Build hero before header (reserves space up-front)
    var hero = document.createElement('div');
    hero.className = 'article-hero';
    header.parentNode.insertBefore(hero, header);


  // First infobox image
    // Clone the wrapper; if we only have <img>, create a fresh <img> with best candidate
  var $img = $content.find('table.infobox .infobox-image-cell img').first();
    var cloneNode;
  if (!$img.length) return;
    if (wrapper.tagName.toLowerCase() === 'img') {
      cloneNode = document.createElement('img');
      cloneNode.src = bestSrc(wrapper);
      cloneNode.alt = '';                // decorative hero; alt remains in infobox
      cloneNode.decoding = 'async';
      cloneNode.loading = 'eager';
      cloneNode.fetchPriority = 'high';
      cloneNode.sizes = '100vw';
    } else {
      cloneNode = wrapper.cloneNode(true);
      // Ensure nested <img> uses a high-res candidate
      var heroImg = cloneNode.querySelector('img');
      if (heroImg) {
        heroImg.src = bestSrc(heroImg);
        heroImg.alt = '';
        heroImg.decoding = 'async';
        heroImg.loading = 'eager';
        heroImg.fetchPriority = 'high';
        heroImg.sizes = '100vw';
      }
    }


  // Page title (Vector 2022 still uses #firstHeading)
    hero.appendChild(cloneNode);
  var $heading = $('#firstHeading');
  if (!$heading.length) return;


  // Build hero container and clone the image
    // Reveal smoothly on the next frame
  var $hero = $('<div>', { 'class': 'article-hero', 'aria-hidden': 'true' });
    requestAnimationFrame(function () {
  var $clone = $('<img>', {
      hero.classList.add('is-ready');
    src: $img.attr('src'),
     });
     srcset: $img.attr('srcset') || '',
   }
    sizes: '100vw',
    alt: '',                        // decorative hero; real alt stays in infobox
    loading: 'eager',
    decoding: 'async',
    fetchpriority: 'high'
   });


   $hero.append($clone);
   if (document.readyState === 'loading') {
   $hero.insertBefore($heading);
    document.addEventListener('DOMContentLoaded', run, { once: true });
   } else {
    run();
  }
})();


  // Hide the original infobox image on touch devices (via CSS class)
  $img.closest('table.infobox').addClass('infobox--image-promoted');
});


/* Quick facts: collapse infobox under first paragraph on touch devices */
/* Quick facts: collapse infobox under first paragraph on touch devices */

Latest revision as of 19:19, 2 October 2025

/* Hero image above the title, cloned from infobox (touch devices only) */
(function () {
  if (!window.matchMedia || !matchMedia('(pointer: coarse)').matches) return;
  if (document.querySelector('.article-hero')) return;

  function bestSrc(img) {
    // prefer the largest srcset candidate if present
    var ss = img.getAttribute('srcset');
    if (!ss) return img.currentSrc || img.src || '';
    var last = ss.split(',').pop().trim().split(/\s+/)[0];
    return last || img.currentSrc || img.src || '';
  }

  function run() {
    var imgCell = document.querySelector('table.infobox .infobox-image-cell');
    if (!imgCell) return;

    // Prefer cloning a wrapper so link/captions survive if present
    var wrapper = imgCell.querySelector('a, span, figure, picture') || imgCell.querySelector('img');
    if (!wrapper) return;

    var header = document.querySelector('.mw-body-header') || document.getElementById('firstHeading');
    if (!header) return;

    // Build hero before header (reserves space up-front)
    var hero = document.createElement('div');
    hero.className = 'article-hero';
    header.parentNode.insertBefore(hero, header);

    // Clone the wrapper; if we only have <img>, create a fresh <img> with best candidate
    var cloneNode;
    if (wrapper.tagName.toLowerCase() === 'img') {
      cloneNode = document.createElement('img');
      cloneNode.src = bestSrc(wrapper);
      cloneNode.alt = '';                 // decorative hero; alt remains in infobox
      cloneNode.decoding = 'async';
      cloneNode.loading = 'eager';
      cloneNode.fetchPriority = 'high';
      cloneNode.sizes = '100vw';
    } else {
      cloneNode = wrapper.cloneNode(true);
      // Ensure nested <img> uses a high-res candidate
      var heroImg = cloneNode.querySelector('img');
      if (heroImg) {
        heroImg.src = bestSrc(heroImg);
        heroImg.alt = '';
        heroImg.decoding = 'async';
        heroImg.loading = 'eager';
        heroImg.fetchPriority = 'high';
        heroImg.sizes = '100vw';
      }
    }

    hero.appendChild(cloneNode);

    // Reveal smoothly on the next frame
    requestAnimationFrame(function () {
      hero.classList.add('is-ready');
    });
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', run, { once: true });
  } else {
    run();
  }
})();


/* Quick facts: collapse infobox under first paragraph on touch devices */
mw.hook('wikipage.content').add(function ($content) {
  // Only on touch devices
  if (!window.matchMedia || !window.matchMedia('(pointer: coarse)').matches) return;

  var $ibox = $content.find('table.infobox').first();
  if (!$ibox.length) return;

  // First real paragraph in article content
  var $firstP = $content
    .find('.mw-parser-output > p')
    .filter(function () { return $(this).text().trim().length > 0; })
    .first();
  if (!$firstP.length) return;

  // Build <details> wrapper
  var $details = $('<details>', { 'class': 'quickfacts', 'aria-label': 'Quick facts' });
  var $summary = $('<summary>', { 'class': 'quickfacts-summary', text: 'Quick facts' });

  $details.append($summary);

  // Mark infobox so CSS can remove float/width inside quickfacts
  $ibox.addClass('infobox--in-quickfacts');

  // Move the infobox inside the details (not cloning; avoids duplicate content for SR)
  $details.append($ibox);

  // Insert directly after the first paragraph
  $details.insertAfter($firstP);
});