diff --git a/dashboard-ui/bower_components/emby-webcomponents/.bower.json b/dashboard-ui/bower_components/emby-webcomponents/.bower.json index 925b3aef7..6c8950c1c 100644 --- a/dashboard-ui/bower_components/emby-webcomponents/.bower.json +++ b/dashboard-ui/bower_components/emby-webcomponents/.bower.json @@ -14,12 +14,12 @@ }, "devDependencies": {}, "ignore": [], - "version": "1.4.422", - "_release": "1.4.422", + "version": "1.4.423", + "_release": "1.4.423", "_resolution": { "type": "version", - "tag": "1.4.422", - "commit": "308a34c32d4476d82f2d29684132a9806ab8c34e" + "tag": "1.4.423", + "commit": "81758c06344944e4d2abfe72daf02c1809c28db8" }, "_source": "https://github.com/MediaBrowser/emby-webcomponents.git", "_target": "^1.2.1", diff --git a/dashboard-ui/bower_components/emby-webcomponents/htmlvideoplayer/plugin.js b/dashboard-ui/bower_components/emby-webcomponents/htmlvideoplayer/plugin.js new file mode 100644 index 000000000..b339b1c1c --- /dev/null +++ b/dashboard-ui/bower_components/emby-webcomponents/htmlvideoplayer/plugin.js @@ -0,0 +1,1106 @@ +define(['browser', 'pluginManager', 'events', 'apphost', 'loading', 'playbackManager'], function (browser, pluginManager, events, appHost, loading, playbackManager) { + "use strict"; + + return function () { + + var self = this; + + self.name = 'Html Video Player'; + self.type = 'mediaplayer'; + self.id = 'htmlvideoplayer'; + + // Let any players created by plugins take priority + self.priority = 1; + + var mediaElement; + var videoDialog; + var currentSrc; + var started = false; + var hlsPlayer; + + var winJsPlaybackItem; + var currentPlayOptions; + + var subtitleTrackIndexToSetOnPlaying; + + var currentClock; + var currentAssRenderer; + + self.canPlayMediaType = function (mediaType) { + + return (mediaType || '').toLowerCase() === 'video'; + }; + + function getBaseProfileOptions(item) { + + var disableHlsVideoAudioCodecs = []; + if (!canPlayNativeHls() || (browser.edge && !item.RunTimeTicks)) { + // hls.js does not support these + disableHlsVideoAudioCodecs.push('mp3'); + disableHlsVideoAudioCodecs.push('ac3'); + } + + var enableMkvProgressive = (item.RunTimeTicks && browser.edgeUwp) ? true : false; + + return { + supportsCustomSeeking: appHost.supports('htmlvideoautoplay'), + enableMkvProgressive: enableMkvProgressive, + disableHlsVideoAudioCodecs: disableHlsVideoAudioCodecs + }; + } + + function getDeviceProfileForWindowsUwp(item) { + + return new Promise(function (resolve, reject) { + + require(['browserdeviceprofile', 'environments/windows-uwp/mediacaps'], function (profileBuilder, uwpMediaCaps) { + + var profileOptions = getBaseProfileOptions(item); + profileOptions.supportsCustomSeeking = true; + profileOptions.supportsDts = uwpMediaCaps.supportsDTS(); + profileOptions.supportsTrueHd = uwpMediaCaps.supportsDolby(); + profileOptions.audioChannels = uwpMediaCaps.getAudioChannels(); + + resolve(profileBuilder(profileOptions)); + }); + }); + } + + self.getDeviceProfile = function (item) { + + if (window.Windows) { + return getDeviceProfileForWindowsUwp(item); + } + + return new Promise(function (resolve, reject) { + + require(['browserdeviceprofile'], function (profileBuilder) { + + var profile = profileBuilder(getBaseProfileOptions(item)); + + if (!browser.edge && !browser.msie) { + // libjass not working here + profile.SubtitleProfiles.push({ + Format: 'ass', + Method: 'External' + }); + profile.SubtitleProfiles.push({ + Format: 'ssa', + Method: 'External' + }); + } + + resolve(profile); + }); + }); + }; + + self.currentSrc = function () { + return currentSrc; + }; + + self.play = function (options) { + + started = false; + _currentTime = null; + + return createMediaElement(options).then(function (elem) { + + return setCurrentSrc(elem, options); + }); + }; + + function getCrossOriginValue(mediaSource) { + + return 'anonymous'; + } + + function requireHlsPlayer(callback) { + require(['hlsjs'], function (hls) { + window.Hls = hls; + callback(); + }); + } + + function setCurrentSrc(elem, options) { + + return new Promise(function (resolve, reject) { + + //if (!elem) { + // currentSrc = null; + // resolve(); + // return; + //} + + //if (!options) { + // currentSrc = null; + // elem.src = null; + // elem.src = ""; + + // // When the browser regains focus it may start auto-playing the last video + // //if ($.browser.safari) { + // // elem.src = 'files/dummy.mp4'; + // // elem.play(); + // //} + + // resolve(); + // return; + //} + + var val = options.url; + + console.log('playing url: ' + val); + + //if (AppInfo.isNativeApp && $.browser.safari) { + // val = val.replace('file://', ''); + //} + + // Convert to seconds + var seconds = (options.playerStartPositionTicks || 0) / 10000000; + if (seconds) { + val += '#t=' + seconds; + } + + var playNow = false; + + destroyHlsPlayer(); + + var tracks = getMediaStreamTextTracks(options.mediaSource); + + var currentTrackIndex = -1; + for (var i = 0, length = tracks.length; i < length; i++) { + if (tracks[i].Index === options.mediaSource.DefaultSubtitleStreamIndex) { + currentTrackIndex = tracks[i].Index; + break; + } + } + subtitleTrackIndexToSetOnPlaying = currentTrackIndex; + + currentPlayOptions = options; + + elem.crossOrigin = getCrossOriginValue(options.mediaSource); + + if (enableHlsPlayer(val, options.item, options.mediaSource)) { + + setTracks(elem, tracks, options.mediaSource, options.item.ServerId); + + requireHlsPlayer(function () { + var hls = new Hls({ + manifestLoadingTimeOut: 20000 + }); + hls.loadSource(val); + hls.attachMedia(elem); + hls.on(Hls.Events.MANIFEST_PARSED, function () { + playAndResolve(elem, resolve, reject); + }); + + hls.on(Hls.Events.ERROR, function (event, data) { + if (data.fatal) { + switch (data.type) { + case Hls.ErrorTypes.NETWORK_ERROR: + // try to recover network error + console.log("fatal network error encountered, try to recover"); + hls.startLoad(); + break; + case Hls.ErrorTypes.MEDIA_ERROR: + console.log("fatal media error encountered, try to recover"); + hls.recoverMediaError(); + break; + default: + // cannot recover + hls.destroy(); + break; + } + } + }); + + hlsPlayer = hls; + }); + + } else { + + elem.autoplay = true; + var mimeType = options.mimeType; + + // Opera TV guidelines suggest using source elements, so let's do that if we have a valid mimeType + if (mimeType && browser.operaTv) { + + if (browser.chrome && mimeType === 'video/x-matroska') { + mimeType = 'video/webm'; + } + + // Need to do this or we won't be able to restart a new stream + if (elem.currentSrc) { + elem.src = ''; + elem.removeAttribute('src'); + } + + elem.innerHTML = '' + getTracksHtml(tracks, options.mediaSource, options.item.ServerId); + } else { + applySrc(elem, val); + setTracks(elem, tracks, options.mediaSource, options.item.ServerId); + } + + elem.addEventListener('loadedmetadata', onLoadedMetadata); + playNow = true; + } + + // This is needed in setCurrentTrackElement + currentSrc = val; + + setCurrentTrackElement(currentTrackIndex); + + if (playNow) { + playAndResolve(elem, resolve, reject); + } + }); + } + + function applySrc(elem, src) { + + if (window.Windows) { + + var playlist = new Windows.Media.Playback.MediaPlaybackList(); + var source1 = Windows.Media.Core.MediaSource.createFromUri(new Windows.Foundation.Uri(src)); + + winJsPlaybackItem = new Windows.Media.Playback.MediaPlaybackItem(source1); + playlist.items.append(winJsPlaybackItem); + + elem.src = URL.createObjectURL(playlist, { oneTimeOnly: true }); + + } else { + + elem.src = src; + } + } + + function playAndResolve(elem, resolve, reject) { + var promise = elem.play(); + if (promise && promise.then) { + // Chrome now returns a promise + promise.then(resolve, reject); + } else { + resolve(); + } + } + + self.setSubtitleStreamIndex = function (index) { + + setCurrentTrackElement(index); + }; + + self.canSetAudioStreamIndex = function () { + + if (winJsPlaybackItem) { + return true; + } + + return false; + }; + + self.setAudioStreamIndex = function (index) { + + var audioTracks = getMediaStreamAudioTracks(currentPlayOptions.mediaSource); + + var track = audioTracks.filter(function (t) { + return t.Index === index; + })[0]; + + if (!track) { + return; + } + + if (winJsPlaybackItem) { + var audioIndex = audioTracks.indexOf(track); + winJsPlaybackItem.audioTracks.selectedIndex = audioIndex; + } + }; + + // Save this for when playback stops, because querying the time at that point might return 0 + var _currentTime; + self.currentTime = function (val) { + + if (mediaElement) { + if (val != null) { + mediaElement.currentTime = val / 1000; + return; + } + + if (_currentTime) { + return _currentTime * 1000; + } + + return (mediaElement.currentTime || 0) * 1000; + } + }; + + self.duration = function (val) { + + if (mediaElement) { + return mediaElement.duration * 1000; + } + + return null; + }; + + self.stop = function (destroyPlayer, reportEnded) { + + var elem = mediaElement; + var src = currentSrc; + + if (elem && src) { + + elem.pause(); + + elem.src = ''; + elem.innerHTML = ''; + elem.removeAttribute("src"); + + destroyHlsPlayer(); + + onEndedInternal(reportEnded); + + if (destroyPlayer) { + self.destroy(); + } + } + + destroyCustomTrack(elem); + + return Promise.resolve(); + }; + + self.destroy = function () { + + destroyHlsPlayer(); + Emby.Page.setTransparency('none'); + + var videoElement = mediaElement; + + if (videoElement) { + + mediaElement = null; + + destroyCustomTrack(videoElement); + + videoElement.removeEventListener('timeupdate', onTimeUpdate); + videoElement.removeEventListener('ended', onEnded); + videoElement.removeEventListener('volumechange', onVolumeChange); + videoElement.removeEventListener('pause', onPause); + videoElement.removeEventListener('playing', onPlaying); + videoElement.removeEventListener('error', onError); + videoElement.removeEventListener('loadedmetadata', onLoadedMetadata); + videoElement.removeEventListener('click', onClick); + videoElement.removeEventListener('dblclick', onDblClick); + + videoElement.parentNode.removeChild(videoElement); + } + + var dlg = videoDialog; + if (dlg) { + + videoDialog = null; + + dlg.parentNode.removeChild(dlg); + } + }; + + function destroyHlsPlayer() { + var player = hlsPlayer; + if (player) { + try { + player.destroy(); + } catch (err) { + console.log(err); + } + + hlsPlayer = null; + } + } + + self.pause = function () { + if (mediaElement) { + mediaElement.pause(); + } + }; + + // This is a retry after error + self.resume = function () { + if (mediaElement) { + mediaElement.play(); + } + }; + + self.unpause = function () { + if (mediaElement) { + mediaElement.play(); + } + }; + + self.paused = function () { + + if (mediaElement) { + return mediaElement.paused; + } + + return false; + }; + + self.volume = function (val) { + if (mediaElement) { + if (val != null) { + mediaElement.volume = val / 100; + return; + } + + return mediaElement.volume * 100; + } + }; + + self.volumeUp = function () { + self.volume(Math.min(self.volume() + 2, 100)); + }; + + self.volumeDown = function () { + self.volume(Math.max(self.volume() - 2, 0)); + }; + + self.setMute = function (mute) { + + if (mute) { + self.volume(0); + } else { + + if (self.isMuted()) { + self.volume(50); + } + } + }; + + self.isMuted = function () { + return self.volume() === 0; + }; + + function onEnded() { + + destroyCustomTrack(this); + onEndedInternal(true); + } + + function onEndedInternal(triggerEnded) { + + if (self.originalDocumentTitle) { + document.title = self.originalDocumentTitle; + self.originalDocumentTitle = null; + } + + if (triggerEnded) { + + var stopInfo = { + src: currentSrc + }; + + events.trigger(self, 'stopped', [stopInfo]); + + _currentTime = null; + } + + currentSrc = null; + winJsPlaybackItem = null; + } + + function onTimeUpdate(e) { + + // Get the player position + the transcoding offset + var time = this.currentTime; + _currentTime = time; + var timeMs = time * 1000; + timeMs += ((currentPlayOptions.transcodingOffsetTicks || 0) / 10000); + updateSubtitleText(timeMs); + + events.trigger(self, 'timeupdate'); + } + + function onVolumeChange() { + + events.trigger(self, 'volumechange'); + } + + function onNavigatedToOsd() { + + videoDialog.classList.remove('videoPlayerContainer-withBackdrop'); + videoDialog.classList.remove('videoPlayerContainer-onTop'); + } + + function onPlaying(e) { + + if (!started) { + started = true; + + if (currentPlayOptions.title) { + self.originalDocumentTitle = document.title; + document.title = currentPlayOptions.title; + } else { + self.originalDocumentTitle = null; + } + + setCurrentTrackElement(subtitleTrackIndexToSetOnPlaying); + + //var requiresNativeControls = !self.enableCustomVideoControls(); + + //if (requiresNativeControls) { + // $(element).attr('controls', 'controls'); + //} + seekOnPlaybackStart(e.target); + + if (currentPlayOptions.fullscreen) { + + Emby.Page.showVideoOsd().then(onNavigatedToOsd); + + } else { + Emby.Page.setTransparency('backdrop'); + videoDialog.classList.remove('videoPlayerContainer-withBackdrop'); + videoDialog.classList.remove('videoPlayerContainer-onTop'); + } + + loading.hide(); + } + events.trigger(self, 'playing'); + } + + function seekOnPlaybackStart(element) { + + var seconds = (currentPlayOptions.playerStartPositionTicks || 0) / 10000000; + + if (seconds) { + var src = (self.currentSrc() || '').toLowerCase(); + + // Appending #t=xxx to the query string doesn't seem to work with HLS + // For plain video files, not all browsers support it either + if (!browser.chrome || src.indexOf('.m3u8') !== -1) { + + var delay = browser.safari ? 2500 : 0; + if (delay) { + setTimeout(function () { + element.currentTime = seconds; + }, delay); + } else { + element.currentTime = seconds; + } + } + } + } + + function onClick() { + events.trigger(self, 'click'); + } + + function onDblClick() { + events.trigger(self, 'dblclick'); + } + + function onPause() { + events.trigger(self, 'pause'); + } + + function onError() { + + destroyCustomTrack(this); + var errorCode = this.error ? this.error.code : ''; + errorCode = (errorCode || '').toString(); + console.log('Media element error code: ' + errorCode); + + var type; + + switch (errorCode) { + case 1: + // MEDIA_ERR_ABORTED + // This will trigger when changing media while something is playing + return; + case 2: + // MEDIA_ERR_NETWORK + type = 'network'; + break; + case 3: + // MEDIA_ERR_DECODE + break; + case 4: + // MEDIA_ERR_SRC_NOT_SUPPORTED + break; + } + + //events.trigger(self, 'error', [ + //{ + // type: type + //}]); + } + + function onLoadedMetadata(e) { + + var mediaElem = e.target; + mediaElem.removeEventListener('loadedmetadata', onLoadedMetadata); + + if (!hlsPlayer) { + mediaElem.play(); + } + } + + function enableHlsPlayer(src, item, mediaSource) { + + if (src) { + if (src.indexOf('.m3u8') === -1) { + return false; + } + } + + if (window.MediaSource == null) { + return false; + } + + if (canPlayNativeHls()) { + + // simple playback should use the native support + if (mediaSource.RunTimeTicks) { + return false; + } + + //return false; + } + + // hls.js is only in beta. needs more testing. + if (browser.safari && !browser.osx) { + return false; + } + + return true; + } + + function canPlayNativeHls() { + var media = document.createElement('video'); + + if (media.canPlayType('application/x-mpegURL').replace(/no/, '') || + media.canPlayType('application/vnd.apple.mpegURL').replace(/no/, '')) { + return true; + } + + return false; + } + + function enableCustomVideoControls() { + + //if (AppInfo.isNativeApp && browser.safari) { + + // if (browser.ipad) { + // // Need to disable it in order to support picture in picture + // return false; + // } + + // return true; + //} + + //return self.canAutoPlayVideo(); + + return true; + } + + function setTracks(elem, tracks, mediaSource, serverId) { + + elem.innerHTML = getTracksHtml(tracks, mediaSource, serverId); + } + + function getTextTrackUrl(track, serverId) { + return playbackManager.getSubtitleUrl(track, serverId); + } + + function getTracksHtml(tracks, mediaSource, serverId) { + return tracks.map(function (t) { + + var defaultAttribute = mediaSource.DefaultSubtitleStreamIndex === t.Index ? ' default' : ''; + + var language = t.Language || 'und'; + var label = t.Language || 'und'; + return ''; + + }).join(''); + } + + var _supportsTextTracks; + + function supportsTextTracks() { + + if (_supportsTextTracks == null) { + _supportsTextTracks = document.createElement('video').textTracks != null; + } + + // For now, until ready + return _supportsTextTracks; + } + + function enableNativeTrackSupport(track) { + + // TODO: + // Custom display for firefox & ps4 + + if (track) { + var format = (track.Codec || '').toLowerCase(); + if (format === 'ssa' || format === 'ass') { + // libjass is needed here + return false; + } + } + + return true; + } + + function destroyCustomTrack(videoElement, isPlaying) { + + window.removeEventListener('resize', onVideoResize); + window.removeEventListener('orientationchange', onVideoResize); + + var videoSubtitlesElem = document.querySelector('.videoSubtitles'); + if (videoSubtitlesElem) { + videoSubtitlesElem.parentNode.removeChild(videoSubtitlesElem); + } + + if (isPlaying) { + + var allTracks = mediaElement.textTracks; // get list of tracks + for (var i = 0; i < allTracks.length; i++) { + + var currentTrack = allTracks[i]; + + if (currentTrack.label.indexOf('manualTrack') !== -1) { + currentTrack.mode = 'disabled'; + } + } + } + + customTrackIndex = -1; + currentClock = null; + + var renderer = currentAssRenderer; + if (renderer) { + renderer.setEnabled(false); + } + currentAssRenderer = null; + } + + function fetchSubtitles(track, serverId) { + + return new Promise(function (resolve, reject) { + + require(['fetchHelper'], function (fetchHelper) { + fetchHelper.ajax({ + url: getTextTrackUrl(track, serverId).replace('.vtt', '.js'), + type: 'GET', + dataType: 'json' + }).then(resolve, reject); + }); + }); + } + + var customTrackIndex = -1; + + function setTrackForCustomDisplay(videoElement, track) { + + if (!track) { + destroyCustomTrack(videoElement, true); + return; + } + + // if already playing thids track, skip + if (customTrackIndex === track.Index) { + return; + } + + var serverId = currentPlayOptions.item.ServerId; + + destroyCustomTrack(videoElement, true); + customTrackIndex = track.Index; + renderTracksEvents(videoElement, track, serverId); + } + + function renderWithLibjass(videoElement, track, serverId) { + + var rendererSettings = {}; + + require(['libjass'], function (libjass) { + + libjass.ASS.fromUrl(getTextTrackUrl(track, serverId)).then(function (ass) { + + var clock = new libjass.renderers.ManualClock(); + currentClock = clock; + + // Create a DefaultRenderer using the video element and the ASS object + var renderer = new libjass.renderers.WebRenderer(ass, clock, videoElement.parentNode, rendererSettings); + + currentAssRenderer = renderer; + + renderer.addEventListener("ready", function () { + try { + renderer.resize(videoElement.offsetWidth, videoElement.offsetHeight, 0, 0); + window.removeEventListener('resize', onVideoResize); + window.addEventListener('resize', onVideoResize); + window.removeEventListener('orientationchange', onVideoResize); + window.addEventListener('orientationchange', onVideoResize); + //clock.pause(); + } catch (ex) { + } + }); + }); + }); + } + + function onVideoResize() { + var renderer = currentAssRenderer; + if (renderer) { + var videoElement = mediaElement; + var width = videoElement.offsetWidth; + var height = videoElement.offsetHeight; + console.log('videoElement resized: ' + width + 'x' + height); + renderer.resize(width, height, 0, 0); + } + } + + function renderTracksEvents(videoElement, track, serverId) { + + var format = (track.Codec || '').toLowerCase(); + if (format === 'ssa' || format === 'ass') { + // libjass is needed here + renderWithLibjass(videoElement, track, serverId); + return; + } + } + + function updateSubtitleText(timeMs) { + + var clock = currentClock; + if (clock) { + clock.seek(timeMs / 1000); + } + } + + function getMediaStreamAudioTracks(mediaSource) { + + return mediaSource.MediaStreams.filter(function (s) { + return s.Type === 'Audio'; + }); + } + + function getMediaStreamTextTracks(mediaSource) { + + return mediaSource.MediaStreams.filter(function (s) { + return s.Type === 'Subtitle' && s.DeliveryMethod === 'External'; + }); + } + + function setCurrentTrackElement(streamIndex) { + + console.log('Setting new text track index to: ' + streamIndex); + + var mediaStreamTextTracks = getMediaStreamTextTracks(currentPlayOptions.mediaSource); + + var track = streamIndex === -1 ? null : mediaStreamTextTracks.filter(function (t) { + return t.Index === streamIndex; + })[0]; + + if (enableNativeTrackSupport(track)) { + + setTrackForCustomDisplay(mediaElement, null); + } else { + setTrackForCustomDisplay(mediaElement, track); + + // null these out to disable the player's native display (handled below) + streamIndex = -1; + track = null; + } + + var expectedId = 'textTrack' + streamIndex; + var trackIndex = streamIndex === -1 || !track ? -1 : mediaStreamTextTracks.indexOf(track); + var modes = ['disabled', 'showing', 'hidden']; + + var allTracks = mediaElement.textTracks; // get list of tracks + for (var i = 0; i < allTracks.length; i++) { + + var currentTrack = allTracks[i]; + + console.log('currentTrack id: ' + currentTrack.id); + + var mode; + + console.log('expectedId: ' + expectedId + '--currentTrack.Id:' + currentTrack.id); + + // IE doesn't support track id + if (browser.msie || browser.edge) { + if (trackIndex === i) { + mode = 1; // show this track + } else { + mode = 0; // hide all other tracks + } + } else { + + if (currentTrack.label.indexOf('manualTrack') !== -1) { + continue; + } + if (currentTrack.id === expectedId) { + mode = 1; // show this track + } else { + mode = 0; // hide all other tracks + } + } + + console.log('Setting track ' + i + ' mode to: ' + mode); + + // Safari uses integers for the mode property + // http://www.jwplayer.com/html5/scripting/ + // edit: not anymore + var useNumericMode = false; + + if (!isNaN(currentTrack.mode)) { + //useNumericMode = true; + } + + if (useNumericMode) { + currentTrack.mode = mode; + } else { + currentTrack.mode = modes[mode]; + } + } + } + + function updateTextStreamUrls(startPositionTicks) { + + if (!supportsTextTracks()) { + return; + } + + var allTracks = mediaElement.textTracks; // get list of tracks + var i; + var track; + + for (i = 0; i < allTracks.length; i++) { + + track = allTracks[i]; + + // This throws an error in IE, but is fine in chrome + // In IE it's not necessary anyway because changing the src seems to be enough + try { + while (track.cues.length) { + track.removeCue(track.cues[0]); + } + } catch (e) { + console.log('Error removing cue from textTrack'); + } + } + + var tracks = mediaElement.querySelectorAll('track'); + for (i = 0; i < tracks.length; i++) { + + track = tracks[i]; + + track.src = replaceQueryString(track.src, 'startPositionTicks', startPositionTicks); + } + } + + function zoomIn(elem, iterations) { + var keyframes = [ + { transform: 'scale3d(.2, .2, .2) ', opacity: '.6', offset: 0 }, + { transform: 'none', opacity: '1', offset: 1 } + ]; + + var timing = { duration: 240, iterations: iterations }; + return elem.animate(keyframes, timing); + } + + function createMediaElement(options) { + + if (browser.operaTv || browser.web0s) { + // too slow + options.backdropUrl = null; + } + return new Promise(function (resolve, reject) { + + var dlg = document.querySelector('.videoPlayerContainer'); + + if (!dlg) { + + require(['css!' + pluginManager.mapPath(self, 'style.css')], function () { + + loading.show(); + + var requiresNativeControls = !enableCustomVideoControls(); + + // Safari often displays the poster under the video and it doesn't look good + var poster = /*!$.browser.safari &&*/ options.poster ? (' poster="' + options.poster + '"') : ''; + poster = ''; + + var dlg = document.createElement('div'); + + dlg.classList.add('videoPlayerContainer'); + + if (options.backdropUrl) { + + dlg.classList.add('videoPlayerContainer-withBackdrop'); + dlg.style.backgroundImage = "url('" + options.backdropUrl + "')"; + } + + if (options.fullscreen) { + dlg.classList.add('videoPlayerContainer-onTop'); + } + + // playsinline new for iOS 10 + // https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_10_0.html + + var html = ''; + // Can't autoplay in these browsers so we need to use the full controls + if (requiresNativeControls) { + html += '