Candidate:Gadget-pickipedia-show.js: Difference between revisions

From PickiPedia: A knowledge base of bluegrass, old time psychedelic jams, and other public domain music
Jump to navigationJump to search
Magent (talk | contribs)
Show: page hero builder extracted into its own gadget (not Common.js)
 
Magent (talk | contribs)
Surface Doors/Show/Ensemble in the poster hero
Line 52: Line 52:
   var venue    = fieldValue('Venue');
   var venue    = fieldValue('Venue');
   var showtime = fieldValue('Showtime');
   var showtime = fieldValue('Showtime');
  var doors    = fieldValue('Doors');
  var show    = fieldValue('Show');
  var ensemble = fieldValue('Ensemble');
   var tickets  = fieldValue('Tickets');
   var tickets  = fieldValue('Tickets');
   var price    = fieldValue('Price');
   var price    = fieldValue('Price');
Line 86: Line 89:
   var showtimeHtml = formatShowtime(showtime);
   var showtimeHtml = formatShowtime(showtime);
   if (showtimeHtml) kickerBits.push(showtimeHtml);
   if (showtimeHtml) kickerBits.push(showtimeHtml);
  if (doors) kickerBits.push('Doors ' + doors);
  if (show)  kickerBits.push('Show ' + show);


   var kickerHtml = kickerBits.length
   var kickerHtml = kickerBits.length
Line 92: Line 97:


   var venueHtml = venue ? '<p class="pp-venue">at ' + venue + '</p>' : '';
   var venueHtml = venue ? '<p class="pp-venue">at ' + venue + '</p>' : '';
  // Ensemble: comma-separated list from Template:Show. Split on commas to
  // render each name with an interpunct separator, matching the kicker style.
  var ensembleHtml = '';
  if (ensemble) {
    var names = ensemble.split(/\s*,\s*/).filter(Boolean);
    if (names.length) {
      ensembleHtml = '<p class="pp-ensemble"><span class="pp-ensemble-label">Featuring</span> ' +
        names.join(' <span class="pp-sep">·</span> ') + '</p>';
    }
  }


   var metaBits = [];
   var metaBits = [];
Line 137: Line 153:
     '<h1 class="pp-title">' + titleHtml + '</h1>' +
     '<h1 class="pp-title">' + titleHtml + '</h1>' +
     venueHtml +
     venueHtml +
    ensembleHtml +
     metaHtml +
     metaHtml +
     imageHtml +
     imageHtml +

Revision as of 19:47, 2 June 2026

// |status=proposed -- bot-staged; remove this line when promoting.
/**
 * Pickipedia — Show: page hero.
 *
 * For mainspace pages whose title starts with "Show:" (the legacy
 * mainspace convention used pre-namespace), adds .pickipedia-show
 * to body and absorbs Template:Show's infobox into a poster-style
 * hero. If the page does not use Template:Show only the body class
 * is added — the setlist typography in Common.css still applies.
 */
(function () {
  'use strict';

  if (typeof mw === 'undefined' || !mw.config) return;

  var nsNumber = mw.config.get('wgNamespaceNumber');
  var title    = mw.config.get('wgTitle') || '';
  var action   = mw.config.get('wgAction') || 'view';

  if (nsNumber !== 0) return;
  if (action !== 'view') return;
  if (title.indexOf('Show:') !== 0) return;

  var body = document.body;
  body.classList.add('pickipedia-show');

  var content = document.querySelector('.mw-parser-output');
  if (!content) return;

  var infobox = content.querySelector('.show-infobox');
  if (!infobox) return;  // Bare show page — body class only.

  // ---------- Pull fields out of the existing infobox ----------
  // Template:Show emits rows of <div><strong>Label:</strong> value</div>
  // plus a top banner with "Artists at Venue" and optional image.
  function fieldValue(label) {
    var rows = infobox.querySelectorAll('div');
    for (var i = 0; i < rows.length; i++) {
      var s = rows[i].querySelector('strong');
      if (!s) continue;
      var labelText = s.textContent.replace(/[:\s]*$/, '').trim();
      if (labelText !== label) continue;
      var clone = rows[i].cloneNode(true);
      var strong = clone.querySelector('strong');
      if (strong) strong.remove();
      return clone.innerHTML.trim().replace(/^[\s:]+/, '');
    }
    return '';
  }

  var artists  = fieldValue('Artists');
  var venue    = fieldValue('Venue');
  var showtime = fieldValue('Showtime');
  var doors    = fieldValue('Doors');
  var show     = fieldValue('Show');
  var ensemble = fieldValue('Ensemble');
  var tickets  = fieldValue('Tickets');
  var price    = fieldValue('Price');
  var ages     = fieldValue('Ages');

  var imageImg = infobox.querySelector('img');

  var status = '';
  if (infobox.classList.contains('show-verified')) status = 'verified';
  else if (infobox.classList.contains('bot-proposal')) status = 'proposed';
  else if (infobox.classList.contains('show-unverified')) status = 'unverified';

  // ---------- Build the poster hero ----------
  // Showtime is rendered by Template:Show as
  // [https://etherscan.io/block/N N] — pull the block number out and
  // format it with commas so the kicker reads "BLOCK 24,144,194".
  function formatShowtime(htmlIn) {
    if (!htmlIn) return '';
    var tmp = document.createElement('div');
    tmp.innerHTML = htmlIn;
    var anchor = tmp.querySelector('a');
    var blockNum = (anchor ? anchor.textContent : tmp.textContent).trim();
    var asInt = parseInt(blockNum.replace(/[^\d]/g, ''), 10);
    if (isNaN(asInt)) return blockNum;
    var formatted = asInt.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
    var href = anchor ? anchor.href : '';
    if (href) {
      return '<a href="' + href + '" target="_blank" rel="noopener">Block ' + formatted + '</a>';
    }
    return 'Block ' + formatted;
  }

  var kickerBits = [];
  var showtimeHtml = formatShowtime(showtime);
  if (showtimeHtml) kickerBits.push(showtimeHtml);
  if (doors) kickerBits.push('Doors ' + doors);
  if (show)  kickerBits.push('Show ' + show);

  var kickerHtml = kickerBits.length
    ? '<div class="pp-kicker">' + kickerBits.join('<span class="pp-sep">·</span>') + '</div>'
    : '';

  var venueHtml = venue ? '<p class="pp-venue">at ' + venue + '</p>' : '';

  // Ensemble: comma-separated list from Template:Show. Split on commas to
  // render each name with an interpunct separator, matching the kicker style.
  var ensembleHtml = '';
  if (ensemble) {
    var names = ensemble.split(/\s*,\s*/).filter(Boolean);
    if (names.length) {
      ensembleHtml = '<p class="pp-ensemble"><span class="pp-ensemble-label">Featuring</span> ' +
        names.join(' <span class="pp-sep">·</span> ') + '</p>';
    }
  }

  var metaBits = [];
  if (price)
    metaBits.push('<span><span class="pp-meta-label">Price</span><span class="pp-meta-value">$' +
      escapeHtml(price.replace(/^\$/, '')) + '</span></span>');
  if (ages)
    metaBits.push('<span><span class="pp-meta-label">Ages</span><span class="pp-meta-value">' +
      ages + '</span></span>');
  var metaHtml = metaBits.length
    ? '<div class="pp-meta">' + metaBits.join('') + '</div>'
    : '';

  var imageHtml = '';
  if (imageImg) {
    imageHtml = '<div class="pp-image"><img src="' + imageImg.src +
      '" alt="' + escapeHtml(imageImg.alt || '') + '"></div>';
  }

  // Tickets field is an anchor; surface its href as a poster action.
  var actionBits = [];
  if (tickets) {
    var tmp = document.createElement('div'); tmp.innerHTML = tickets;
    var ticketAnchor = tmp.querySelector('a');
    if (ticketAnchor && ticketAnchor.href) {
      actionBits.push('<a href="' + ticketAnchor.href +
        '" target="_blank" rel="noopener">Tickets</a>');
    }
  }
  var actionsHtml = actionBits.length
    ? '<div class="pp-actions">' + actionBits.join('') + '</div>'
    : '';

  var statusHtml = status
    ? '<div class="pp-status is-' + status + '">' + status + '</div>'
    : '';

  var titleHtml = artists || escapeHtml(title.replace(/^Show:\s*/, ''));

  var hero = document.createElement('header');
  hero.className = 'show-poster';
  hero.innerHTML =
    statusHtml +
    kickerHtml +
    '<h1 class="pp-title">' + titleHtml + '</h1>' +
    venueHtml +
    ensembleHtml +
    metaHtml +
    imageHtml +
    actionsHtml;

  content.insertBefore(hero, content.firstChild);
  infobox.classList.add('is-absorbed');
  body.classList.add('has-show-hero');

  function escapeHtml(s) {
    return String(s == null ? '' : s)
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;');
  }
}());