MediaWiki:Common.js
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/* Any JavaScript here will be loaded for all users on every page load. */
/* ── Intercept and block TMH attribute changes ── */
$(function () {
if ($('.training-video-wrap').length === 0) return;
function lockControls() {
var video = document.querySelector('.mw-file-element');
if (!video) {
setTimeout(lockControls, 100);
return;
}
/* Block setAttribute from removing controls or adding disabled */
var originalSetAttribute = video.setAttribute.bind(video);
var originalRemoveAttribute = video.removeAttribute.bind(video);
video.setAttribute = function (name, value) {
if (name === 'disabled') return;
if (name === 'controls') {
return originalSetAttribute('controls', '');
}
return originalSetAttribute(name, value);
};
video.removeAttribute = function (name) {
if (name === 'controls') return;
return originalRemoveAttribute(name);
};
/* Block direct property assignment: video.disabled = true */
Object.defineProperty(video, 'disabled', {
get: function () { return false; },
set: function () { /* block all attempts to set disabled */ },
configurable: true
});
/* Set initial state */
originalSetAttribute('controls', '');
originalRemoveAttribute('disabled');
}
lockControls();
});
/* ============================================================
Training Video – Synced Transcript Sidebar
============================================================ */
$(function () {
if ($('.training-video-wrap').length === 0) return;
var $video = $('.training-video-wrap video');
var $entries = $('.ts-entry');
var $body = $('.ts-scroll-body');
var $markers = $('.ts-progress-marker');
var $fill = $('.ts-progress-fill');
var $timeDisp = $('.ts-time-display');
if ($video.length === 0) return;
var videoEl = $video[0];
function fmt(s) {
s = Math.floor(s);
return Math.floor(s / 60) + ':' + ('0' + (s % 60)).slice(-2);
}
var timestamps = [];
$entries.each(function () {
timestamps.push(parseInt($(this).data('time'), 10));
});
function highlightActive() {
var cur = videoEl.currentTime;
var activeIdx = 0;
for (var i = 0; i < timestamps.length; i++) {
if (cur >= timestamps[i]) activeIdx = i;
}
$entries.each(function (i) {
$(this).toggleClass('ts-active', i === activeIdx);
});
var $active = $entries.eq(activeIdx);
if ($active.length && $body.length) {
var entryTop = $active.position().top;
var bodyScrollTop = $body.scrollTop();
var bodyHeight = $body.height();
if (entryTop < 0 || entryTop > bodyHeight - 60) {
$body.animate({ scrollTop: bodyScrollTop + entryTop - 60 }, 200);
}
}
}
function updateProgress() {
if (!videoEl.duration) return;
var pct = (videoEl.currentTime / videoEl.duration) * 100;
$fill.css('width', pct + '%');
$timeDisp.text(fmt(videoEl.currentTime) + ' / ' + fmt(videoEl.duration));
}
$video.on('timeupdate', function () {
highlightActive();
updateProgress();
});
$video.on('loadedmetadata', function () {
$timeDisp.text('0:00 / ' + fmt(videoEl.duration));
$markers.each(function () {
var t = parseInt($(this).data('time'), 10);
$(this).css('left', ((t / videoEl.duration) * 100) + '%');
});
});
$entries.on('click', function () {
var t = parseInt($(this).data('time'), 10);
videoEl.currentTime = t;
if (videoEl.paused) videoEl.play();
highlightActive();
});
$('.ts-progress-track').on('click', function (e) {
var rect = this.getBoundingClientRect();
var ratio = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
videoEl.currentTime = ratio * videoEl.duration;
});
$markers.on('click', function (e) {
e.stopPropagation();
var t = parseInt($(this).data('time'), 10);
videoEl.currentTime = t;
if (videoEl.paused) videoEl.play();
});
});
/* ============================================================ */