diff options
-rw-r--r-- | resources/media-controls.css | 9 | ||||
-rw-r--r-- | resources/media-controls.js | 75 |
2 files changed, 63 insertions, 21 deletions
diff --git a/resources/media-controls.css b/resources/media-controls.css index cbea462157c..62c39164747 100644 --- a/resources/media-controls.css +++ b/resources/media-controls.css @@ -16,7 +16,7 @@ button { display: block; position: relative; min-height: 40px; - min-width: 200px; + min-width: 230px; } .controls { @@ -52,3 +52,10 @@ button { background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAQAAAD8x0bcAAAAy0lEQVR4AayRtUICUBRA7a7Jye5RF7u+wM4vsF2s0e7O37BzkZqZ+AF2WGk4XLqZeO/dPq/TUtuoZSki7mIgFloFlv3+icgOoKE8GlsH1sVOgOgy9jGjpzIuRjqv/rgNI3/hQCuD0t+8WK7Eywx6NfSGIAX+5t90CwOF5ODgNj4EO2TzxbDkdXwlgmCdLOolr+UlEbTn37QAK0fRBz8BAImvEYwMnmiJfdRzHxTAYoAQJjqPWWxJMf+3vHEAzCf+8A4avXYR9xSpm5QAaOvUDhZuhCsAAAAASUVORK5CYII=") no-repeat; } +.fullscreen { + background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAa1JREFUSA3Vlj1Ow0AQhW1OAFUaKmoqOEAklCIlJQ3nyBFyBahc0IAEEnUUunQWVLiloqEKNzDfmJ2V7Z1JlpKRRrt+82bezq7/yrZtH4qiOMUt2wJelWX5aQXJnYDf4UdWHOyjgFTjlm0Bz53ECAsHF65lzUFkDiffXM5Y+esQTq8CZ0ZEchLzBF5gviVsBwgiz2aYvrwtuiFWmkkjEN7S2h+wpn8Gso/ViLhXBP64+COYnkkUiAdqJLgiHhdcD74TWAnQ79pL/AsniGxki+ReTswQmSuJ2BTvm9klhGPNMUcIur+LMYHYIiiYxcd895oiUy9IbI5n3WlejX+OS/uyDV4bxNzt83IiHorfMortOuRlTLImJCe3EpisXIsz7Syulis53L6ZIhAm8hxs8PigMbeKDwpkcuRpXolAg3evipxE3YVdXGL6qqhVAKwTeZJJzwYr1+I6wrO6rcD1ZTcQ6NXtpjuL7xHRWq5ApQVyRqpJJ+PuRaT2vmiXBOPBZ4icwbmweJ7AIeR1jkjgrOFLTmLSmvy2nCSRX0B+W6755n5ZcXLlGbrHvd+W9x/rQiZUwmH0RgAAAABJRU5ErkJggg==') no-repeat; +} + +.fullscreen.fullscreen-active { + background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAbtJREFUSA3tlj0vBFEUhnd8hFhrE4mPbSREovIfdFQ6hcJ/EJVColP6GRoqhZbEFqKmET2xDSpWYjxncu915u6ZzWS20DjJ6577no/3zL1mqNWwNE1HwKb4g5rrdcjayHo54oxVbG8QAepl0FNphLVBQ6bflZ2ySiLU6+a+3bY/Hq/qA0EEog4m4ieDmwOzwrNazUOPwgQKpfkVWDAEVuHvQAsUDhjqSLKmeIQXKxKQ2Jv8UPY7eejuHJIsEantJ6B6l/glIXsKvOgq/DICT+Rld6IHH9IbEursz8GM5kv68+RdxiKJLibYYr+sOeffJknyqXlyZbhJzTn/g9yuwf9Tf3QC8SUPM8eoMUuXi/vWPJcsuSuac36H3I7BZ98UeQduQGwbcQEJ03ES+3vQ8y7kakkQkQegrYzAKwXyLuQs96K5yDrrYi6r3KZJ2jEicnS2EdwCX8Dbu3P6PcGzT3brCWuvCGTc/BpOPsVyJ0UC2ZkT3wfa8iJErObZZ4BYEyzFzww3DsKF4tsiBNaAPhaZ3PrGxBo9e0PkQP6ajYELIFa5uVejh3+SNn74z0JEjkClyX1zv9Jnxzf/AaCJ561FockLAAAAAElFTkSuQmCC') no-repeat; +} diff --git a/resources/media-controls.js b/resources/media-controls.js index 383b025ee0e..b8f100c6f05 100644 --- a/resources/media-controls.js +++ b/resources/media-controls.js @@ -5,19 +5,6 @@ (() => { "use strict"; - const MARKUP = ` - <div class="controls"> - <button id="play-pause-button"></button> - <input id="progress" type="range" value="0" min="0" max="100" step="1"></input> - <span id="position-duration-box" class="hidden"> - <span id="position-text">#1</span> - <span id="duration"> / #2</span> - </span> - <button id="volume-switch"></button> - <input id="volume-level" type="range" value="100" min="0" max="100" step="1"></input> - </div> - `; - // States. const BUFFERING = "buffering"; const ENDED = "ended"; @@ -50,6 +37,22 @@ } }; + function generateMarkup(isAudioOnly) { + return ` + <div class="controls"> + <button id="play-pause-button"></button> + <input id="progress" type="range" value="0" min="0" max="100" step="1"></input> + <span id="position-duration-box" class="hidden"> + <span id="position-text">#1</span> + <span id="duration"> / #2</span> + </span> + <button id="volume-switch"></button> + <input id="volume-level" type="range" value="100" min="0" max="100" step="1"></input> + ${isAudioOnly ? "" : '<button id="fullscreen-switch" class="fullscreen"></button>'} + </div> + `; + } + function camelCase(str) { const rdashes = /-(.)/g; return str.replace(rdashes, (str, p1) => { @@ -89,15 +92,16 @@ attributeFilter: ["controls"] }); + this.isAudioOnly = this.media.localName == "audio"; + // Create root element and load markup. this.root = document.createElement("div"); this.root.classList.add("root"); - this.root.innerHTML = MARKUP; + this.root.innerHTML = generateMarkup(this.isAudioOnly); this.controls.appendChild(this.root); - // Import elements. - this.elements = {}; - [ + + const elementNames = [ "duration", "play-pause-button", "position-duration-box", @@ -105,7 +109,15 @@ "progress", "volume-switch", "volume-level" - ].forEach(id => { + ]; + + if (!this.isAudioOnly) { + elementNames.push("fullscreen-switch"); + } + + // Import elements. + this.elements = {}; + elementNames.forEach(id => { this.elements[camelCase(id)] = this.controls.getElementById(id); }); @@ -182,6 +194,11 @@ { el: this.elements.volumeSwitch, type: "click" }, { el: this.elements.volumeLevel, type: "input" } ]; + + if (!this.isAudioOnly) { + this.controlEvents.push({ el: this.elements.fullscreenSwitch, type: "click" }); + } + this.controlEvents.forEach(({ el, type }) => { el.addEventListener(type, this); }); @@ -238,8 +255,7 @@ } render(from = this.state) { - const isAudioOnly = this.media.localName == "audio"; - if (!isAudioOnly) { + if (!this.isAudioOnly) { // XXX This should ideally use clientHeight/clientWidth, // but for some reason I couldn't figure out yet, // using it breaks layout. @@ -312,6 +328,9 @@ case this.elements.volumeSwitch: this.toggleMuted(); break; + case this.elements.fullscreenSwitch: + this.toggleFullscreen(); + break; } break; case "input": @@ -368,6 +387,22 @@ this.media.muted = !this.media.muted; } + toggleFullscreen() { + const { fullscreenEnabled, fullscreenElement } = document; + + const isElementFullscreen = fullscreenElement && fullscreenElement === this.media; + + if (fullscreenEnabled && isElementFullscreen) { + document.exitFullscreen().then(() => { + this.elements.fullscreenSwitch.classList.remove("fullscreen-active"); + }); + } else { + this.media.requestFullscreen().then(() => { + this.elements.fullscreenSwitch.classList.add("fullscreen-active"); + }); + } + } + changeVolume() { const volume = parseInt(this.elements.volumeLevel.value); if (!isNaN(volume)) { |