Difference between revisions of "MediaWiki:Common.js"
From Conceptual Reconstructionism Project
Thaumasnot (talk | contribs) |
Thaumasnot (talk | contribs) |
||
| (94 intermediate revisions by the same user not shown) | |||
| Line 2: | Line 2: | ||
function TimeSegment( | var isMobile = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent); | ||
function TimeSegment(trackIndex, startTime, endTime, element) { | |||
this.trackIndex = trackIndex; | |||
this.startTime = startTime; | |||
this.endTime = endTime; | |||
this.element = element; | |||
} | } | ||
TimeSegment.prototype.isDisabled = function() { | |||
return !this.element.hasClass('active'); | |||
}; | |||
TimeSegment.prototype.disable = function() { | |||
this.element.removeClass('active'); | |||
}; | |||
TimeSegment.prototype.update = function(trackIndex, currentTime) { | |||
var percent = 100.0 * Math.max(currentTime - this.startTime, 0) / (this.endTime - this.startTime) | |||
if (trackIndex === this.trackIndex && percent > 0.0 && percent < 100.0) { | |||
this.element.get(0).style.setProperty('--gauge-fill', percent + "%"); | |||
this.element.addClass('active'); | |||
} else { | |||
this.disable(); | |||
} | |||
}; | |||
function PlaybackData() { | function PlaybackData() { | ||
this.autostopTime = null; | |||
this.noAutoscroll = false; | |||
this.segments = []; | |||
this.htmlAudios = []; | |||
this.closestPlayableSegIndex = 0; | |||
this.playingAudioIndex = null; | |||
}; | |||
var playbackData = new PlaybackData(); | var playbackData = new PlaybackData(); | ||
function buildTimeSegmentFromSpec(spec, progressElement) { | |||
var m = /(?<track>\d+\/)?((?<minuteStart>\d+):)?(?<secondStart>\d+(.\d*)?)-((?<minuteEnd>\d+):)?(?<secondEnd>\d+(.\d*)?)/.exec(spec); | |||
if (m) { | |||
return new TimeSegment( | |||
m.groups.track? parseInt(m.groups.track) : 0, | |||
(m.groups.minuteStart? parseInt(m.groups.minuteStart) : 0) * 60 + parseFloat(m.groups.secondStart), | |||
(m.groups.minuteEnd? parseInt(m.groups.minuteEnd) : 0) * 60 + parseFloat(m.groups.secondEnd), | |||
progressElement | |||
); | |||
} | |||
return null; | |||
} | |||
// Helper functions for music reconstruction writers. | |||
// They can be called from the developer console in Chrome. | |||
function playSample(trackIndex, segmentSpec) { | |||
var segment = buildTimeSegmentFromSpec(segmentSpec, null); | |||
playSample2(trackIndex, segment.startTime, segment.endTime); | |||
if (!playbackData.noAutoscroll) { | |||
playbackData.noAutoscroll = true; | |||
console.log('Autoscroll during playback is now disabled'); | |||
} | |||
copyToClipboard(segmentSpec); | |||
} | |||
function copyToClipboard(text) { | |||
var tempInput = $('<input></input>').prop('value', text).appendTo($('body')); | |||
tempInput.get(0).select(); | |||
document.execCommand('copy'); | |||
tempInput.detach(); | |||
} | |||
function playSample2(trackIndex, startTime, endTime) { | |||
playbackData.autostopTime = endTime; | |||
playbackData.htmlAudios[trackIndex].currentTime = startTime; | |||
playbackData.htmlAudios[trackIndex].play(); | |||
} | |||
| Line 23: | Line 95: | ||
// Create one audio player per declared track. | // Create one audio player per declared track | ||
var audioPlayer = $('<div class="audio-player"></div>').insertAfter($('*[data-track]').last()); | |||
function selectAudioPlayer(trackNumber) { | |||
audioPlayer.find('figure.active').removeClass('active'); | |||
audioPlayer.find('figure').eq(trackNumber).addClass('active'); | |||
audioPlayer.find('.selected').removeClass('selected'); | |||
audioPlayer.find('.track-menu div').eq(trackNumber).addClass('selected'); | |||
} | |||
$('*[data-track]').each(function(trackNumber) { | $('*[data-track]').each(function(trackNumber) { | ||
$('<figure></figure>').append($('<figcaption></figcaption>').text($(this).attr('data-track-title'))).appendTo( | var audio = $('<audio></audio>') | ||
.prop('controls', true) | |||
.attr('controlsList', 'nodownload') | |||
.attr('preload', 'auto') | |||
.attr('src', $(this).attr('data-track')); | |||
$('<figure></figure>') | |||
.addClass(trackNumber === 0? 'active': '') | |||
.append($('<figcaption></figcaption>') | |||
.text($(this).attr('data-track-title'))) | |||
.appendTo(audioPlayer) | |||
.append(audio); | |||
function onPlay(event) { | |||
if (playbackData.playingAudioIndex == null) { | |||
selectAudioPlayer(trackNumber); | |||
} | |||
else if (playbackData.playingAudioIndex !== trackNumber) { | |||
playbackData.htmlAudios[playbackData.playingAudioIndex].pause(); | |||
selectAudioPlayer(trackNumber); | |||
} | |||
playbackData.playingAudioIndex = trackNumber; | |||
} | |||
function onPause(event) { | |||
if (playbackData.playingAudioIndex === trackNumber) { | |||
playbackData.playingAudioIndex = null; | |||
playbackData.autostopTime = null; | |||
} | |||
playbackData.closestPlayableSegIndex = 0; | |||
} | |||
function onSeeked(event) { | |||
playbackData.closestPlayableSegIndex = 0; | |||
playbackData.segments.forEach(function(s) { s.disable(); }); | |||
} | |||
function onEnded(event) { | |||
var nextPlayingAudioIndex = playbackData.playingAudioIndex + 1; | |||
onPause(event); | |||
onSeeked(event); | |||
if (nextPlayingAudioIndex < playbackData.htmlAudios.length) { | |||
var nextAudio = playbackData.htmlAudios[nextPlayingAudioIndex]; | |||
nextAudio.currentTime = 0; | |||
nextAudio.play(); | |||
} | |||
} | |||
audio.on('play', onPlay); | |||
audio.on('pause', onPause); | |||
audio.on('ended', onEnded); | |||
audio.on('seeked', onSeeked); | |||
playbackData.htmlAudios.push(audio.get(0)); | |||
}); | }); | ||
if ($('*[data-track]').length > 1) { | |||
var trackMenu = $('<div class="track-menu"></div>').appendTo(audioPlayer); | |||
$('*[data-track]').each(function(trackNumber) { | |||
$('<div></div>') | |||
.text(trackNumber + 1) | |||
.click(function() { selectAudioPlayer(trackNumber); } ) | |||
.addClass(trackNumber === 0? 'selected': '') | |||
.prop('title', audioPlayer.find('figcaption').eq(trackNumber).text()) | |||
.tooltip() | |||
.appendTo(trackMenu); | |||
}); | |||
} | |||
// Create hyperlinks out of reconstruction markup | // Create hyperlinks out of reconstruction markup | ||
$('*[data-def]'). | |||
$('*[data-def]').each(function() { | |||
var url = location.href; | var url = location.href; | ||
var anchor = "#" + encodeURIComponent($(this).attr('data-def').toLowerCase()) | |||
if ($(anchor).length === 0) { | |||
$(this).addClass('broken-ref-link'); | |||
} | |||
else { | |||
$(this).click(function(evt) { | |||
location.href = anchor; | |||
history.replaceState(null, null, url); | |||
evt.stopPropagation(); // Inner markup has priority over outer markup | |||
}); | |||
} | |||
}); | }); | ||
// Create context menu for music references | |||
function playSampleFromMenu(menuItem, segment) { | |||
var m = /\[(\-?\d+)s \+(\d+)s\]/.exec(menuItem.text()); | |||
if (m) { | |||
var lowerBound = parseInt(m[1]); | |||
var upperBound = parseInt(m[2]); | |||
var trackIndex = segment.trackIndex; | |||
playSample2(segment.trackIndex, segment.startTime + lowerBound, segment.endTime + upperBound); | |||
} | |||
} | |||
var soundContextMenu = $('<div class="sound-meta-menu meta-menu">') | |||
.append($('<div>\u25B6 [-0s +0s]</div>')) | |||
.append($('<div>\u25B6 [-0s +5s]</div>')) | |||
.append($('<div>\u25B6 [-5s +0s]</div>')) | |||
.append($('<div>\u25B6 [-2s +2s]</div>')) | |||
.append($('<div>\u25B6 [-5s +5s]</div>')) | |||
.append($('<div>\u25B6 [-10s +10s]</div>')); | |||
// | // Store timing data and attach context menu | ||
$('*[data-segment]').each(function() { | $('*[data-segment]').each(function() { | ||
var segment = buildTimeSegmentFromSpec($(this).attr('data-segment'), $(this).find('.meta-progress').eq(0)); | |||
if (segment) { | |||
if (playbackData.htmlAudios.length <= segment.trackIndex) { | |||
$(this).append('Track number does not refer to existing track (note that the index starts with 0)'); | |||
} | |||
else { | |||
function attachSoundContextMenu() { | |||
soundContextMenu.detach().appendTo($(this)); | |||
soundContextMenu.children('div').off('click').click(function () { playSampleFromMenu($(this), segment); }); | |||
} | |||
var contextMenuAnchor = $('<span class="context-menu-anchor">\u23F5</span>').insertBefore($(this)); | |||
if (!isMobile) { | |||
contextMenuAnchor.hover(attachSoundContextMenu); | |||
} else { | |||
contextMenuAnchor.click(attachSoundContextMenu); | |||
} | |||
playbackData.segments.push(segment); | |||
} | |||
} | |||
else { | |||
$(this).append('Invalid syntax for segment time'); | |||
} | |||
}); | |||
}); | playbackData.segments.sort(function(a, b) { return a.trackIndex - b.trackIndex || a.startTime - b.startTime; }); | ||
playbackData. | |||
// Playing tick | |||
window.setInterval(function() { | |||
if (playbackData.playingAudioIndex === null) { | |||
return; | |||
} | |||
var playingAudio = playbackData.htmlAudios[playbackData.playingAudioIndex]; | |||
var currentTime = playingAudio.currentTime; | |||
var previouSegmentActive = false; | |||
var newActiveSegment = null; | |||
for(var segIndex = playbackData.closestPlayableSegIndex; | |||
segIndex < playbackData.segments.length; | |||
segIndex++) { | |||
var segment = playbackData.segments[segIndex]; | |||
var wasDisabled = segment.isDisabled(); | |||
segment.update(playbackData.playingAudioIndex, currentTime); | |||
if (segment.isDisabled()) { | |||
if (segment.startTime > currentTime && segment.trackIndex === playbackData.playingAudioIndex) { | |||
break; | |||
} | |||
continue; | |||
} | |||
if (!previouSegmentActive) { | |||
playbackData.closestPlayableSegIndex = segIndex; | |||
} | |||
previouSegmentActive = true; | |||
if (wasDisabled && newActiveSegment == null) { | |||
newActiveSegment = segment; | |||
} | |||
} | |||
function ensureVisible(element) { | |||
var offset = element.offset().top - $(window).scrollTop(); | |||
var SCROLL_OFFSET = 150; | |||
window. | if (offset > window.innerHeight) { | ||
$('html,body').animate({ scrollTop: Math.max(0, offset - SCROLL_OFFSET) }, 500); | |||
} | |||
else if (offset < 0) { | |||
$('html,body').animate({ scrollTop: Math.max(0, element.offset().top - SCROLL_OFFSET) }, 500); | |||
} | |||
} | |||
if (newActiveSegment != null && !playbackData.noAutoscroll) { | |||
ensureVisible(newActiveSegment.element); | |||
} | |||
if (playbackData.autostopTime != null && currentTime >= playbackData.autostopTime) { | |||
playingAudio.pause(); | |||
return; | |||
} | |||
}, 50); | }, 50); | ||
}); | }); | ||
Latest revision as of 18:04, 16 January 2022
/* Any JavaScript here will be loaded for all users on every page load. */
var isMobile = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent);
function TimeSegment(trackIndex, startTime, endTime, element) {
this.trackIndex = trackIndex;
this.startTime = startTime;
this.endTime = endTime;
this.element = element;
}
TimeSegment.prototype.isDisabled = function() {
return !this.element.hasClass('active');
};
TimeSegment.prototype.disable = function() {
this.element.removeClass('active');
};
TimeSegment.prototype.update = function(trackIndex, currentTime) {
var percent = 100.0 * Math.max(currentTime - this.startTime, 0) / (this.endTime - this.startTime)
if (trackIndex === this.trackIndex && percent > 0.0 && percent < 100.0) {
this.element.get(0).style.setProperty('--gauge-fill', percent + "%");
this.element.addClass('active');
} else {
this.disable();
}
};
function PlaybackData() {
this.autostopTime = null;
this.noAutoscroll = false;
this.segments = [];
this.htmlAudios = [];
this.closestPlayableSegIndex = 0;
this.playingAudioIndex = null;
};
var playbackData = new PlaybackData();
function buildTimeSegmentFromSpec(spec, progressElement) {
var m = /(?<track>\d+\/)?((?<minuteStart>\d+):)?(?<secondStart>\d+(.\d*)?)-((?<minuteEnd>\d+):)?(?<secondEnd>\d+(.\d*)?)/.exec(spec);
if (m) {
return new TimeSegment(
m.groups.track? parseInt(m.groups.track) : 0,
(m.groups.minuteStart? parseInt(m.groups.minuteStart) : 0) * 60 + parseFloat(m.groups.secondStart),
(m.groups.minuteEnd? parseInt(m.groups.minuteEnd) : 0) * 60 + parseFloat(m.groups.secondEnd),
progressElement
);
}
return null;
}
// Helper functions for music reconstruction writers.
// They can be called from the developer console in Chrome.
function playSample(trackIndex, segmentSpec) {
var segment = buildTimeSegmentFromSpec(segmentSpec, null);
playSample2(trackIndex, segment.startTime, segment.endTime);
if (!playbackData.noAutoscroll) {
playbackData.noAutoscroll = true;
console.log('Autoscroll during playback is now disabled');
}
copyToClipboard(segmentSpec);
}
function copyToClipboard(text) {
var tempInput = $('<input></input>').prop('value', text).appendTo($('body'));
tempInput.get(0).select();
document.execCommand('copy');
tempInput.detach();
}
function playSample2(trackIndex, startTime, endTime) {
playbackData.autostopTime = endTime;
playbackData.htmlAudios[trackIndex].currentTime = startTime;
playbackData.htmlAudios[trackIndex].play();
}
$(document).ready(function() {
// Create one audio player per declared track
var audioPlayer = $('<div class="audio-player"></div>').insertAfter($('*[data-track]').last());
function selectAudioPlayer(trackNumber) {
audioPlayer.find('figure.active').removeClass('active');
audioPlayer.find('figure').eq(trackNumber).addClass('active');
audioPlayer.find('.selected').removeClass('selected');
audioPlayer.find('.track-menu div').eq(trackNumber).addClass('selected');
}
$('*[data-track]').each(function(trackNumber) {
var audio = $('<audio></audio>')
.prop('controls', true)
.attr('controlsList', 'nodownload')
.attr('preload', 'auto')
.attr('src', $(this).attr('data-track'));
$('<figure></figure>')
.addClass(trackNumber === 0? 'active': '')
.append($('<figcaption></figcaption>')
.text($(this).attr('data-track-title')))
.appendTo(audioPlayer)
.append(audio);
function onPlay(event) {
if (playbackData.playingAudioIndex == null) {
selectAudioPlayer(trackNumber);
}
else if (playbackData.playingAudioIndex !== trackNumber) {
playbackData.htmlAudios[playbackData.playingAudioIndex].pause();
selectAudioPlayer(trackNumber);
}
playbackData.playingAudioIndex = trackNumber;
}
function onPause(event) {
if (playbackData.playingAudioIndex === trackNumber) {
playbackData.playingAudioIndex = null;
playbackData.autostopTime = null;
}
playbackData.closestPlayableSegIndex = 0;
}
function onSeeked(event) {
playbackData.closestPlayableSegIndex = 0;
playbackData.segments.forEach(function(s) { s.disable(); });
}
function onEnded(event) {
var nextPlayingAudioIndex = playbackData.playingAudioIndex + 1;
onPause(event);
onSeeked(event);
if (nextPlayingAudioIndex < playbackData.htmlAudios.length) {
var nextAudio = playbackData.htmlAudios[nextPlayingAudioIndex];
nextAudio.currentTime = 0;
nextAudio.play();
}
}
audio.on('play', onPlay);
audio.on('pause', onPause);
audio.on('ended', onEnded);
audio.on('seeked', onSeeked);
playbackData.htmlAudios.push(audio.get(0));
});
if ($('*[data-track]').length > 1) {
var trackMenu = $('<div class="track-menu"></div>').appendTo(audioPlayer);
$('*[data-track]').each(function(trackNumber) {
$('<div></div>')
.text(trackNumber + 1)
.click(function() { selectAudioPlayer(trackNumber); } )
.addClass(trackNumber === 0? 'selected': '')
.prop('title', audioPlayer.find('figcaption').eq(trackNumber).text())
.tooltip()
.appendTo(trackMenu);
});
}
// Create hyperlinks out of reconstruction markup
$('*[data-def]').each(function() {
var url = location.href;
var anchor = "#" + encodeURIComponent($(this).attr('data-def').toLowerCase())
if ($(anchor).length === 0) {
$(this).addClass('broken-ref-link');
}
else {
$(this).click(function(evt) {
location.href = anchor;
history.replaceState(null, null, url);
evt.stopPropagation(); // Inner markup has priority over outer markup
});
}
});
// Create context menu for music references
function playSampleFromMenu(menuItem, segment) {
var m = /\[(\-?\d+)s \+(\d+)s\]/.exec(menuItem.text());
if (m) {
var lowerBound = parseInt(m[1]);
var upperBound = parseInt(m[2]);
var trackIndex = segment.trackIndex;
playSample2(segment.trackIndex, segment.startTime + lowerBound, segment.endTime + upperBound);
}
}
var soundContextMenu = $('<div class="sound-meta-menu meta-menu">')
.append($('<div>\u25B6 [-0s +0s]</div>'))
.append($('<div>\u25B6 [-0s +5s]</div>'))
.append($('<div>\u25B6 [-5s +0s]</div>'))
.append($('<div>\u25B6 [-2s +2s]</div>'))
.append($('<div>\u25B6 [-5s +5s]</div>'))
.append($('<div>\u25B6 [-10s +10s]</div>'));
// Store timing data and attach context menu
$('*[data-segment]').each(function() {
var segment = buildTimeSegmentFromSpec($(this).attr('data-segment'), $(this).find('.meta-progress').eq(0));
if (segment) {
if (playbackData.htmlAudios.length <= segment.trackIndex) {
$(this).append('Track number does not refer to existing track (note that the index starts with 0)');
}
else {
function attachSoundContextMenu() {
soundContextMenu.detach().appendTo($(this));
soundContextMenu.children('div').off('click').click(function () { playSampleFromMenu($(this), segment); });
}
var contextMenuAnchor = $('<span class="context-menu-anchor">\u23F5</span>').insertBefore($(this));
if (!isMobile) {
contextMenuAnchor.hover(attachSoundContextMenu);
} else {
contextMenuAnchor.click(attachSoundContextMenu);
}
playbackData.segments.push(segment);
}
}
else {
$(this).append('Invalid syntax for segment time');
}
});
playbackData.segments.sort(function(a, b) { return a.trackIndex - b.trackIndex || a.startTime - b.startTime; });
// Playing tick
window.setInterval(function() {
if (playbackData.playingAudioIndex === null) {
return;
}
var playingAudio = playbackData.htmlAudios[playbackData.playingAudioIndex];
var currentTime = playingAudio.currentTime;
var previouSegmentActive = false;
var newActiveSegment = null;
for(var segIndex = playbackData.closestPlayableSegIndex;
segIndex < playbackData.segments.length;
segIndex++) {
var segment = playbackData.segments[segIndex];
var wasDisabled = segment.isDisabled();
segment.update(playbackData.playingAudioIndex, currentTime);
if (segment.isDisabled()) {
if (segment.startTime > currentTime && segment.trackIndex === playbackData.playingAudioIndex) {
break;
}
continue;
}
if (!previouSegmentActive) {
playbackData.closestPlayableSegIndex = segIndex;
}
previouSegmentActive = true;
if (wasDisabled && newActiveSegment == null) {
newActiveSegment = segment;
}
}
function ensureVisible(element) {
var offset = element.offset().top - $(window).scrollTop();
var SCROLL_OFFSET = 150;
if (offset > window.innerHeight) {
$('html,body').animate({ scrollTop: Math.max(0, offset - SCROLL_OFFSET) }, 500);
}
else if (offset < 0) {
$('html,body').animate({ scrollTop: Math.max(0, element.offset().top - SCROLL_OFFSET) }, 500);
}
}
if (newActiveSegment != null && !playbackData.noAutoscroll) {
ensureVisible(newActiveSegment.element);
}
if (playbackData.autostopTime != null && currentTime >= playbackData.autostopTime) {
playingAudio.pause();
return;
}
}, 50);
});