Candidate:Gadget-pickipedia-show.js
From PickiPedia: A knowledge base of bluegrass, old time psychedelic jams, and other public domain music
// |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 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);
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>' : '';
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 +
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, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"');
}
}());