MediaWiki:Common.js
From Conceptual Reconstructionism Project
Revision as of 23:38, 20 December 2021 by Thaumasnot (talk | contribs)
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)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
/* 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.segments = []; this.htmlAudios = []; this.closestPlayableSegIndex = 0; this.playingAudioIndex = null; }; var playbackData = new PlaybackData(); $(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('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 && 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) { playbackData.htmlAudios[nextPlayingAudioIndex].play(); } } audio.on('play', onPlay); audio.on('pause', onPause); audio.on('ended', onEnded); audio.on('seeked', onSeeked); playbackData.htmlAudios.push(audio.get(0)); }); 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': '') .appendTo(trackMenu); }); // Create hyperlinks out of reconstruction markup $('*[data-def]').click(function() { var url = location.href; location.href = "#" + $(this).attr('data-def'); history.replaceState(null, null, url); }); // Update timing data $('*[data-segment]').each(function() { var m = /(?<track>\d+\/)?((?<minuteStart>\d+):)?(?<secondStart>\d+(.\d*)?)-((?<minuteEnd>\d+):)?(?<secondEnd>\d+(.\d*)?)/.exec($(this).attr('data-segment')); if (m) { if (m.groups.track != null && playbackData.htmlAudios.length <= parseInt(m.groups.track)) { $(this).append('Track number does not refer to existing track (note that the index starts with 0)'); } else { $(this).prop('data-segment-id', playbackData.segments.length) playbackData.segments.push(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), $(this).find('.meta-progress') )); } } else { $(this).append('Invalid syntax for segment time'); } }); playbackData.segments.sort(function(a, b) { return a.startTime - b.startTime; }); // Attach context menu to music references function playSample(menuItem, lowerBound, upperBound) { var meta = menuItem.closest('.meta'); if (meta.length === 0) { meta = menuItem.closest('.context-menu-anchor').prev('.meta'); } var segmentId = parseInt(meta.prop('data-segment-id')); var segment = playbackData.segments[segmentId]; var trackIndex = segment.trackIndex; playbackData.autostopTime = segment.endTime + upperBound; playbackData.htmlAudios[trackIndex].currentTime = Math.max(0, segment.startTime + lowerBound); playbackData.htmlAudios[trackIndex].play(); } var soundContextMenu = $('<div class="sound-meta-menu meta-menu">') .append($('<div>\u25B6 [-0s +0s]</div>').click(function () { playSample($(this), 0, 0); })) .append($('<div>\u25B6 [-1s +1s]</div>').click(function () { playSample($(this), -1, 1); })) .append($('<div>\u25B6 [-2s +2s]</div>').click(function () { playSample($(this), -2, 2); })) .append($('<div>\u25B6 [-5s +5s]</div>').click(function () { playSample($(this), -5, 5); })) .append($('<div>\u25B6 [-10s +10s]</div>').click(function () { playSample($(this), -10, 10); })); $('*[data-segment]').each(function() { function attachSoundContextMenu() { soundContextMenu.detach().appendTo($(this)); } if (!isMobile) { $(this).hover(attachSoundContextMenu); } else { $(this).after($('<span class="context-menu-anchor">\u23F5</span>').click(attachSoundContextMenu)); } }); // 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) { 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) { ensureVisible(newActiveSegment.element); } if (playbackData.autostopTime != null && currentTime >= playbackData.autostopTime) { playingAudio.pause(); return; } }, 50); });