MediaWiki:Common.js: Difference between revisions
OCR review: no-team rows still show who/page/change |
YouTube click-to-play and watch-next |
||
| Line 381: | Line 381: | ||
$slot.prepend($iframe); | $slot.prepend($iframe); | ||
} | } | ||
}); | |||
}()); | |||
(function buildYouTubeWatchlist() { | |||
var YT_ID = /^[A-Za-z0-9_-]{11}$/; | |||
function splitSoldiers(raw) { | |||
return String(raw || '').split(';').map(function (s) { | |||
return $.trim(s); | |||
}).filter(Boolean); | |||
} | |||
function overlap(a, b) { | |||
var i, j; | |||
for (i = 0; i < a.length; i++) { | |||
for (j = 0; j < b.length; j++) { | |||
if (a[i] === b[j]) { | |||
return true; | |||
} | |||
} | |||
} | |||
return false; | |||
} | |||
function playEmbed($slot, id, title) { | |||
var src = 'https://www.youtube-nocookie.com/embed/' + | |||
encodeURIComponent(id) + '?autoplay=1&rel=0'; | |||
var $iframe = $('<iframe class="yt-embed-frame" allowfullscreen allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"></iframe>') | |||
.attr({ | |||
src: src, | |||
title: title || 'YouTube video', | |||
loading: 'lazy' | |||
}); | |||
$slot.empty().append($iframe).addClass('yt-embed-playing'); | |||
} | |||
$('.mw-parser-output .yt-embed[data-youtube-id]').each(function () { | |||
var $slot = $(this); | |||
if ($slot.data('rbYtEmbed')) { | |||
return; | |||
} | |||
var id = String($slot.attr('data-youtube-id') || ''); | |||
if (!YT_ID.test(id)) { | |||
return; | |||
} | |||
$slot.data('rbYtEmbed', 1); | |||
var title = String($slot.attr('data-title') || 'YouTube video'); | |||
var $fallback = $slot.find('.yt-embed-fallback').first(); | |||
var $btn = $('<button type="button" class="yt-embed-play"></button>') | |||
.attr('aria-label', 'Play ' + title) | |||
.append($('<span class="yt-embed-play-icon" aria-hidden="true"></span>')) | |||
.append($('<span class="yt-embed-play-text"></span>').text('Play video')); | |||
var $thumb = $('<img class="yt-embed-thumb" alt="">') | |||
.attr({ | |||
src: 'https://i.ytimg.com/vi/' + encodeURIComponent(id) + '/hqdefault.jpg', | |||
alt: title | |||
}); | |||
var $poster = $('<div class="yt-embed-poster"></div>').append($thumb).append($btn); | |||
if ($fallback.length) { | |||
$fallback.before($poster); | |||
$fallback.hide(); | |||
} else { | |||
$slot.prepend($poster); | |||
} | |||
$poster.on('click', function (ev) { | |||
ev.preventDefault(); | |||
playEmbed($slot, id, title); | |||
}); | |||
}); | |||
var $cards = $('.mw-parser-output .yt-interview[data-youtube-id]'); | |||
$cards.each(function (idx) { | |||
var $card = $(this); | |||
if ($card.data('rbYtNext')) { | |||
return; | |||
} | |||
$card.data('rbYtNext', 1); | |||
var $slot = $card.find('.yt-interview-next').first(); | |||
if (!$slot.length) { | |||
return; | |||
} | |||
var mine = splitSoldiers($card.attr('data-soldiers')); | |||
var picks = []; | |||
var seen = {}; | |||
function addCard($other, why) { | |||
var oid = String($other.attr('data-youtube-id') || ''); | |||
if (!oid || seen[oid] || oid === $card.attr('data-youtube-id')) { | |||
return; | |||
} | |||
seen[oid] = 1; | |||
picks.push({ | |||
id: oid, | |||
heading: String($other.attr('data-heading') || $other.find('h3, h2').first().text() || 'Interview'), | |||
short: String($other.attr('data-short') || ''), | |||
why: why | |||
}); | |||
} | |||
$cards.each(function (j) { | |||
if (j === idx || picks.length >= 3) { | |||
return; | |||
} | |||
var $other = $($cards.get(j)); | |||
if (overlap(mine, splitSoldiers($other.attr('data-soldiers')))) { | |||
addCard($other, 'same soldier'); | |||
} | |||
}); | |||
if (idx + 1 < $cards.length) { | |||
addCard($($cards.get(idx + 1)), 'next'); | |||
} | |||
if (!picks.length) { | |||
return; | |||
} | |||
var $list = $('<ul class="yt-interview-next-list"></ul>'); | |||
picks.slice(0, 3).forEach(function (item) { | |||
var label = item.short ? (item.heading + ' — ' + item.short) : item.heading; | |||
var $a = $('<a></a>').attr('href', '#yt-' + item.id).text(label); | |||
var $li = $('<li></li>').append($a); | |||
if (item.why === 'same soldier') { | |||
$li.append($('<span class="yt-interview-next-why"></span>').text(' (same person)')); | |||
} | |||
$list.append($li); | |||
}); | |||
$slot.empty() | |||
.append($('<div class="yt-interview-next-label"></div>').text('Watch next')) | |||
.append($list); | |||
}); | }); | ||
}()); | }()); | ||
| Line 447: | Line 574: | ||
if ($leadCue.length) { | if ($leadCue.length) { | ||
$leadCue.after($storyCue); | $leadCue.after($storyCue); | ||
} | |||
} | |||
// Interview / film cue sits with the story cue under the lead. | |||
var $mediaCue = $out.find('.soldier-media-cue').first(); | |||
if ($mediaCue.length && !$mediaCue.data('rbMediaCuePlaced')) { | |||
$mediaCue.data('rbMediaCuePlaced', 1); | |||
var $afterStory = $out.find('.soldier-story-cue').first(); | |||
if ($afterStory.length) { | |||
$afterStory.after($mediaCue); | |||
} else { | |||
var $leadMedia = $out.find('.soldier-lead, .page-lead').first(); | |||
if ($leadMedia.length) { | |||
$leadMedia.after($mediaCue); | |||
} | |||
} | } | ||
} | } | ||