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.
/**
* This script interacts with [[Template:Video]]
*
* Most of the complexity of this script stems from the fact that it's designed
* to handle videos from YouTube, Vimeo, Commons or Appropedia
*
* [[Category:Template script pages]]
*/
window.TemplateVideo = {
init: function () {
var $videos = $( '.template-video' );
// Give each video an id and an index
$videos.each( function ( index ) {
$( this ).attr( {
'id': 'video-' + index,
'data-video-index': index
} );
} );
// Replace plain timestamps for links
$videos.find( '.template-video-annotations' ).html( function ( index, html ) {
if ( /(\d\d?:\d\d)/.test( html ) ) {
return html.replace( /(\d\d?:\d\d)/g, '<a class="timestamp" href="#$1">$1</a>' );
}
} );
// Play the relevant video when a timestamp is clicked
$videos.find( '.template-video-annotations .timestamp' ).on( 'click', function () {
var $link = $( this );
var $video = $link.closest( '.template-video' );
var index = $video.data( 'video-index' );
var hash = $link.attr( 'href' );
if ( $video.find( 'iframe, video' ).length ) {
TemplateVideo.playVideoAtTime( index, hash );
} else if ( $video.hasClass( 'youtube' ) ) {
TemplateVideo.loadYouTubeVideo.bind( this )();
} else if ( $video.hasClass( 'vimeo' ) ) {
TemplateVideo.loadVimeoVideo.bind( this )();
}
} );
// Add event listeners to highlight the relevant annotation
$videos.filter( '.appropedia, .commons' ).each( function () {
var $video = $( this );
var player = $video.find( 'video' )[0];
var previousTime = -1;
player.addEventListener( 'timeupdate', function () {
var currentTime = player.currentTime;
currentTime = Math.floor( currentTime );
if ( currentTime === previousTime ) {
return;
}
previousTime = currentTime;
TemplateVideo.highlightAnnotation( $video, currentTime );
} );
} );
// Load a YouTube video when a YouTube play button or thumbnail is clicked
$videos.filter( '.youtube' ).find( '.template-video-play, .template-video-thumb' ).on( 'click', TemplateVideo.loadYouTubeVideo );
// Load a Vimeo video when a Vimeo play button or thumbnail is clicked
$videos.filter( '.vimeo' ).find( '.template-video-play, .template-video-thumb' ).on( 'click', TemplateVideo.loadVimeoVideo );
},
isYouTubeLoaded: false,
loadYouTubeVideo: function () {
var $video = $( this ).closest( '.template-video' );
// Make the iframe
var params = new URLSearchParams( {
autoplay: 1,
enablejsapi: 1,
rel: 0,
origin: window.location.origin
} );
var id = $video.data( 'video-id' );
var url = 'https://www.youtube.com/embed/' + id + '?' + params.toString();
var $iframe = $( '<iframe src="' + url + '" width="640" height="360" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>' );
// Update the DOM
$video.find( '.template-video-content' ).html( $iframe );
// Load the YouTube IFrame Player API code only once
if ( TemplateVideo.isYouTubeLoaded === false ) {
$.getScript( 'https://www.youtube.com/iframe_api' );
window.onYouTubeIframeAPIReady = function () {
TemplateVideo.isYouTubeLoaded = true;
TemplateVideo.makeYouTubePlayer( $video );
};
} else {
TemplateVideo.makeYouTubePlayer( $video );
}
},
isVimeoLoaded: false,
loadVimeoVideo: function () {
var $video = $( this ).closest( '.template-video' );
// Make the iframe
var id = $video.data( 'video-id' );
var url = 'https://player.vimeo.com/video/' + id;
var $iframe = $( '<iframe src="' + url + '" width="640" height="360" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen></iframe>' );
// Update the DOM
$video.find( '.template-video-content' ).html( $iframe );
// Load the Vimeo Player SDK code only once
if ( TemplateVideo.isVimeoLoaded === false ) {
$.getScript( 'https://player.vimeo.com/api/player.js' ).done( function () {
TemplateVideo.isVimeoLoaded = true;
TemplateVideo.makeVimeoPlayer( $video );
} );
} else {
TemplateVideo.makeVimeoPlayer( $video );
}
},
// This will hold both YouTube and Vimeo players
players: [],
// Create a YouTube player
makeYouTubePlayer: function ( $video ) {
var $iframe = $video.find( 'iframe' );
var index = $video.data( 'video-index' );
var id = 'video-iframe-' + index;
$iframe.attr( 'id', id );
var player = new YT.Player( id, { origin: window.location.origin } );
TemplateVideo.players[ index ] = player;
player.addEventListener( 'onReady', function () {
TemplateVideo.playVideoAtTime( index );
// Highlight the relevant annotation
var previousTime = -1;
setInterval( function () {
var currentTime = player.getCurrentTime();
currentTime = Math.floor( currentTime );
if ( currentTime === previousTime ) {
return;
}
previousTime = currentTime;
TemplateVideo.highlightAnnotation( $video, currentTime );
}, 1000 );
} );
},
// Create a Vimeo player
makeVimeoPlayer: function ( $video ) {
var $iframe = $video.find( 'iframe' );
var index = $video.data( 'video-index' );
var iframe = $iframe[0];
var player = new Vimeo.Player( iframe );
TemplateVideo.players[ index ] = player;
player.on( 'loaded', function () {
TemplateVideo.playVideoAtTime( index );
// Highlight the relevant annotation
var previousTime = -1;
setInterval( function () {
player.getCurrentTime().then( function ( currentTime ) {
currentTime = Math.floor( currentTime );
if ( currentTime === previousTime ) {
return;
}
previousTime = currentTime;
TemplateVideo.highlightAnnotation( $video, currentTime );
} );
}, 1000 );
} );
},
// Play the specified video at the specified time
playVideoAtTime: function ( index, hash ) {
// Figure out the intended video and time
if ( !Number.isInteger( index ) ) {
index = 0;
}
if ( !hash ) {
hash = window.location.hash;
}
var time = hash.substr( 1 ).split( ':' );
var minutes = time[1] ? parseInt( time[0], 10 ) : 0;
var seconds = time[1] ? parseInt( time[1], 10 ) + minutes * 60 : parseInt( time[0], 10 );
if ( !Number.isInteger( seconds ) ) {
return;
}
var $video = $( '#video-' + index );
// Play the specified video at the specified time
var player;
if ( $video.hasClass( 'youtube' ) ) {
player = TemplateVideo.players[ index ];
player.seekTo( seconds );
player.playVideo();
} else if ( $video.hasClass( 'vimeo' ) ) {
player = TemplateVideo.players[ index ];
player.setCurrentTime( seconds );
player.play();
} else {
player = $video.find( 'video' )[0];
player.currentTime = seconds;
player.play();
}
// Center the video
$video.find( '.template-video-content' )[0].scrollIntoView( { behavior: 'smooth', block: 'center' } );
},
// Find the relevant annotation and highlight it
highlightAnnotation: function ( $video, currentTime ) {
$video.find( '.template-video-annotations a' ).each( function () {
var $link = $( this );
var href = $link.attr( 'href' );
var time = href.substr( 1 ).split( ':' );
var minutes = time[1] ? parseInt( time[0], 10 ) : 0;
var seconds = time[1] ? parseInt( time[1], 10 ) + minutes * 60 : parseInt( time[0], 10 );
var $item = $link.closest( 'li' );
if ( currentTime === seconds ) {
var $annotations = $item.closest( '.template-video-annotations' );
$annotations.find( '.highlight' ).removeClass( 'highlight' );
$item.addClass( 'highlight' );
$annotations.find( '.mw-collapsible' ).hide();
if ( $item.is( ':last-child' ) ) {
$item.closest( 'ul' ).next( '.mw-collapsible' ).show();
}
return false; // Break the loop
}
} );
}
};
$( TemplateVideo.init );