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
Show: page hero builder extracted into its own gadget (not Common.js) |
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, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"');
}
}());