forked from daronspence/media-playback-speed
-
Notifications
You must be signed in to change notification settings - Fork 2
/
playback-speed.js
110 lines (94 loc) · 5.01 KB
/
playback-speed.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// contain all JS effects of this plugin so we don't break sites
(function() {
// window.load is unfortunately the best handler to attach to for allowing media-element-js to initialize
window.addEventListener("load", function() {
var buttons = document.createRange().createContextualFragment(
document.querySelector("#playback-buttons-template").innerHTML
);
var els = [].slice.call( document.querySelectorAll( '.mejs-container' ) );
els.forEach(function(elem, i) {
var mediaTag = elem.querySelector('audio,video');
// buttons for me-js element
[].slice.call(
buttons.querySelectorAll('.playback-rate-button')
).forEach(function(elem) {
elem.setAttribute('aria-controls', mediaTag.id );
});
// parent for controls
var controls = elem.querySelector('.mejs-controls');
if(controls) {
var container = controls.querySelector('.mejs-duration-container'),
hasSpeedControls = controls.querySelector('.playback-rate-button');
// Guard to ensure that this only affects as-yet unaffected elements
if (!hasSpeedControls) {
// insertAfter container
container.parentNode.insertBefore(buttons.cloneNode(true), container.nextSibling);
}
}
});
});
[].slice.call( document.querySelectorAll( 'audio,video' ) ).forEach( function(elem) {
// when media is loaded persist the playback speed currently selected
elem.addEventListener('loadedmetadata', function(e) {
var wpPlayer = e.target.closest('.mejs-container');
var activeSpeed, rate;
if(wpPlayer) {
// WordPress Playlist state restore selected speed
activeSpeed = wpPlayer.querySelector('.playback-rate-button.mejs-active');
rate = activeSpeed.dataset.value;
} else {
// Any media-element, getting the first
activeSpeed = document.querySelector('.playback-rate-button.mejs-active, .playback-rate-button.active-playback-rate');
if(activeSpeed) {
rate = activeSpeed.dataset.value;
}
}
// Guard against failing matchers. The DOM must be fulfilled, but this also means this part maybe doesn't need media-element-js
if(!rate) { return; }
// This is actually the magic. It's basically a more complex document.querySelector('video, audio').playbackRate
e.target.playbackRate = rate;
});
});
// AJAX / SPA supporting click bind handler
// Uses data attribute and aria attribute as well as class-names from media-element-js & this plugin
// because this binds to body it should always be available in a valid HTML page
document.body.addEventListener('click', function(e) {
// Because we're bound to body, we need to guard and only act on HTML elements with the right class
if(!e.target || !e.target.classList.contains('playback-rate-button')) { return; }
// We set aria attributes informing which DOMElement to control
var targetId = e.target.getAttribute('aria-controls')
mediaTag = document.getElementById(targetId);
// Guard against failing matchers. The DOM must be fulfilled, but this also means this part maybe doesn't need media-element-js
var rate = e.target.dataset.value;
// Guard against failing matchers. The DOM must be fulfilled, but this also means this part maybe doesn't need media-element-js
if(!rate) { return; }
// This is actually the magic. It's basically a more complex document.querySelector('video, audio').playbackRate
if(mediaTag) {
mediaTag.playbackRate = rate;
} else {
[].slice.call(
document.querySelectorAll('audio, video')
).forEach(function(elem){
elem.playbackRate = rate;
});
}
var mediaPlaybackContainer;
if(mediaTag) { mediaPlaybackContainer = mediaTag.closest('.mejs-container'); }
// This allows use outside of WordPress for this
if(!mediaTag || !mediaPlaybackContainer) { mediaPlaybackContainer = mediaTag || document.body; }
// Clear all active playback rate buttons for this element of the active class
[].slice.call(
mediaPlaybackContainer.querySelectorAll('.playback-rate-button')
).map(function(elem) {
elem.classList.remove('mejs-active', 'active-playback-rate');
});
// Set the clicked element, or the matching to active rate to be active
[].slice.call(
mediaPlaybackContainer.querySelectorAll('.playback-rate-button')
).forEach(function(elem) {
if(rate && elem.dataset.value == rate) {
elem.classList.add('mejs-active', 'active-playback-rate');
}
});
});
})();