Difference between revisions of "MediaWiki:Common.js"
From Conceptual Reconstructionism Project
Thaumasnot (talk | contribs) |
Thaumasnot (talk | contribs) |
||
(24 intermediate revisions by the same user not shown) | |||
Line 36: | Line 36: | ||
function PlaybackData() { | function PlaybackData() { | ||
this.autostopTime = null; | this.autostopTime = null; | ||
this.noAutoscroll = false; | |||
this.segments = []; | this.segments = []; | ||
this.htmlAudios = []; | this.htmlAudios = []; | ||
Line 46: | Line 47: | ||
function buildTimeSegmentFromSpec(spec) { | function buildTimeSegmentFromSpec(spec, progressElement) { | ||
var m = /(?<track>\d+\/)?((?<minuteStart>\d+):)?(?<secondStart>\d+(.\d*)?)-((?<minuteEnd>\d+):)?(?<secondEnd>\d+(.\d*)?)/.exec(spec); | var m = /(?<track>\d+\/)?((?<minuteStart>\d+):)?(?<secondStart>\d+(.\d*)?)-((?<minuteEnd>\d+):)?(?<secondEnd>\d+(.\d*)?)/.exec(spec); | ||
Line 54: | Line 55: | ||
(m.groups.minuteStart? parseInt(m.groups.minuteStart) : 0) * 60 + parseFloat(m.groups.secondStart), | (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), | (m.groups.minuteEnd? parseInt(m.groups.minuteEnd) : 0) * 60 + parseFloat(m.groups.secondEnd), | ||
progressElement | |||
); | ); | ||
} | } | ||
Line 61: | Line 62: | ||
// Helper | // Helper functions for music reconstruction writers. | ||
// | // They can be called from the developer console in Chrome. | ||
function playSample(trackIndex, segmentSpec) { | function playSample(trackIndex, segmentSpec) { | ||
var segment = buildTimeSegmentFromSpec(segmentSpec); | var segment = buildTimeSegmentFromSpec(segmentSpec, null); | ||
playbackData.autostopTime = | playSample2(trackIndex, segment.startTime, segment.endTime); | ||
playbackData.htmlAudios[trackIndex].currentTime = | 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(); | playbackData.htmlAudios[trackIndex].play(); | ||
} | } | ||
Line 94: | Line 113: | ||
var audio = $('<audio></audio>') | var audio = $('<audio></audio>') | ||
.prop('controls', true) | .prop('controls', true) | ||
.attr('controlsList', 'nodownload') | |||
.attr('preload', 'auto') | .attr('preload', 'auto') | ||
.attr('src', $(this).attr('data-track')); | .attr('src', $(this).attr('data-track')); | ||
Line 160: | Line 180: | ||
}); | }); | ||
} | } | ||
// 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 | // Create context menu for music references | ||
Line 176: | Line 205: | ||
function playSampleFromMenu(menuItem, segment) { | function playSampleFromMenu(menuItem, segment) { | ||
var m = /\[\-? | var m = /\[(\-?\d+)s \+(\d+)s\]/.exec(menuItem.text()); | ||
if (m) { | if (m) { | ||
var lowerBound = parseInt(m[1]); | var lowerBound = parseInt(m[1]); | ||
var upperBound = parseInt(m[2]); | var upperBound = parseInt(m[2]); | ||
var trackIndex = segment.trackIndex; | var trackIndex = segment.trackIndex; | ||
playSample2(segment.trackIndex, segment.startTime + lowerBound, segment.endTime + upperBound); | |||
} | } | ||
} | } | ||
Line 194: | Line 223: | ||
// | // Store timing data and attach context menu | ||
$('*[data-segment]').each(function() { | $('*[data-segment]').each(function() { | ||
var segment = buildTimeSegmentFromSpec($(this).attr('data-segment')); | var segment = buildTimeSegmentFromSpec($(this).attr('data-segment'), $(this).find('.meta-progress').eq(0)); | ||
if (segment) { | if (segment) { | ||
Line 210: | Line 239: | ||
soundContextMenu.detach().appendTo($(this)); | soundContextMenu.detach().appendTo($(this)); | ||
soundContextMenu.children('div').off('click').click(function () { playSampleFromMenu($(this), segment); }); | soundContextMenu.children('div').off('click').click(function () { playSampleFromMenu($(this), segment); }); | ||
} | } | ||
var contextMenuAnchor = $('<span class="context-menu-anchor">\u23F5</span>').insertBefore($(this)); | |||
if (!isMobile) { | if (!isMobile) { | ||
contextMenuAnchor.hover(attachSoundContextMenu); | |||
} else { | } else { | ||
contextMenuAnchor.click(attachSoundContextMenu); | |||
} | } | ||
Line 281: | Line 310: | ||
} | } | ||
if (newActiveSegment != null) { | if (newActiveSegment != null && !playbackData.noAutoscroll) { | ||
ensureVisible(newActiveSegment.element); | ensureVisible(newActiveSegment.element); | ||
} | } |
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); });