UNPKG

mediaelement

Version:
344 lines (295 loc) 10.2 kB
'use strict'; import document from 'global/document'; import i18n from '../core/i18n'; import {config} from '../player'; import MediaElementPlayer from '../player'; import * as Features from '../utils/constants'; import {isString, createEvent} from '../utils/general'; import {addClass, removeClass} from '../utils/dom'; import {getTypeFromFile} from '../utils/media'; import {generateControlButton} from '../utils/generate'; /** * Fullscreen button * * This feature creates a button to toggle fullscreen on video; it considers a letiety of possibilities when dealing with it * since it is not consistent across browsers. */ // Feature configuration Object.assign(config, { /** * @type {Boolean} */ usePluginFullScreen: true, /** * @type {?String} */ fullscreenText: null, /** * @type {Boolean} */ useFakeFullscreen: false }); Object.assign(MediaElementPlayer.prototype, { /** * @type {Boolean} */ isFullScreen: false, /** * @type {Boolean} */ isNativeFullScreen: false, /** * @type {Boolean} */ isPluginClickThroughCreated: false, /** * Possible modes * (1) 'native-native' HTML5 video + browser fullscreen (IE10+, etc.) * (2) 'plugin-native' plugin video + browser fullscreen (fails in some versions of Firefox) * (3) 'fullwindow' Full window (retains all UI) * * @type {String} */ fullscreenMode: '', /** * */ containerSizeTimeout: null, /** * Feature constructor. * * Always has to be prefixed with `build` and the name that will be used in MepDefaults.features list * @param {MediaElementPlayer} player */ buildfullscreen (player) { if (!player.isVideo) { return; } player.detectFullscreenMode(); const t = this, fullscreenTitle = isString(t.options.fullscreenText) ? t.options.fullscreenText : i18n.t('mejs.fullscreen'), fullscreenBtn = document.createElement('div') ; fullscreenBtn.className = `${t.options.classPrefix}button ${t.options.classPrefix}fullscreen-button`; fullscreenBtn.innerHTML = generateControlButton(t.id, fullscreenTitle, fullscreenTitle, `${t.media.options.iconSprite}`, ['icon-fullscreen', 'icon-unfullscreen'], `${t.options.classPrefix}`); t.addControlElement(fullscreenBtn, 'fullscreen'); fullscreenBtn.addEventListener('click', () => { // toggle fullscreen const isFullScreen = (Features.HAS_TRUE_NATIVE_FULLSCREEN && Features.IS_FULLSCREEN) || player.isFullScreen; if (isFullScreen) { player.exitFullScreen(); } else { player.enterFullScreen(); } }); player.fullscreenBtn = fullscreenBtn; t.options.keyActions.push({ keys: [70], // F action: (player, media, key, event) => { if (!event.ctrlKey) { if (typeof player.enterFullScreen !== 'undefined') { if (player.isFullScreen) { player.exitFullScreen(); } else { player.enterFullScreen(); } } } } }); t.exitFullscreenCallback = (e) => { const key = e.which || e.keyCode || 0; if (t.options.enableKeyboard && key === 27 && ((Features.HAS_TRUE_NATIVE_FULLSCREEN && Features.IS_FULLSCREEN) || t.isFullScreen)) { player.exitFullScreen(); } }; t.globalBind('keydown', t.exitFullscreenCallback); t.normalHeight = 0; t.normalWidth = 0; // setup native fullscreen event if (Features.HAS_TRUE_NATIVE_FULLSCREEN) { /** * Detect any changes on fullscreen * * Chrome doesn't always fire this in an `<iframe>` * @private */ const fullscreenChanged = () => { if (player.isFullScreen) { if (Features.isFullScreen()) { player.isNativeFullScreen = true; // reset the controls once we are fully in full screen player.setControlsSize(); } else { player.isNativeFullScreen = false; // when a user presses ESC // make sure to put the player back into place player.exitFullScreen(); } } }; player.globalBind(Features.FULLSCREEN_EVENT_NAME, fullscreenChanged); } }, cleanfullscreen (player) { player.exitFullScreen(); player.globalUnbind('keydown', player.exitFullscreenCallback); }, /** * Detect the type of fullscreen based on browser's capabilities * * @return {String} */ detectFullscreenMode () { const t = this, isNative = t.media.rendererName !== null && /(native|html5)/i.test(t.media.rendererName) ; let mode = ''; if (Features.HAS_TRUE_NATIVE_FULLSCREEN && isNative) { mode = 'native-native'; } else if (Features.HAS_TRUE_NATIVE_FULLSCREEN && !isNative) { mode = 'plugin-native'; } else if (t.usePluginFullScreen && Features.SUPPORT_POINTER_EVENTS) { mode = 'plugin-click'; } t.fullscreenMode = mode; return mode; }, /** * */ enterFullScreen () { const t = this, isNative = t.media.rendererName !== null && /(html5|native)/i.test(t.media.rendererName), containerStyles = getComputedStyle(t.getElement(t.container)) ; if (!t.isVideo) { return; } // iOS allows playing fullscreen ONLY on `video` tag, so check if the source can go fullscreen on iOS // and if the player can play the current source if (t.options.useFakeFullscreen === false && (Features.IS_IOS || Features.IS_SAFARI) && Features.HAS_IOS_FULLSCREEN && typeof t.node.webkitEnterFullscreen === 'function' && t.node.canPlayType(getTypeFromFile(t.media.getSrc()))) { t.node.webkitEnterFullscreen(); return; } // set it to not show scroll bars so 100% will work addClass(document.documentElement, `${t.options.classPrefix}fullscreen`); addClass(t.getElement(t.container), `${t.options.classPrefix}container-fullscreen`); // store sizing t.normalHeight = parseFloat(containerStyles.height); t.normalWidth = parseFloat(containerStyles.width); // attempt to do true fullscreen if (t.fullscreenMode === 'native-native' || t.fullscreenMode === 'plugin-native') { Features.requestFullScreen(t.getElement(t.container)); } // make full size t.getElement(t.container).style.width = '100%'; t.getElement(t.container).style.height = '100%'; if (isNative) { // For native HTML5 video, let CSS handle the sizing with object-fit // Don't set explicit dimensions - CSS will handle it } else { const elements = t.getElement(t.container).querySelectorAll('embed, object, video'), total = elements.length; for (let i = 0; i < total; i++) { elements[i].style.width = '100%'; elements[i].style.height = '100%'; } } if (t.options.setDimensions && typeof t.media.setSize === 'function' && !isNative) { t.media.setSize(screen.width, screen.height); } const layers = t.getElement(t.layers).children, total = layers.length; for (let i = 0; i < total; i++) { layers[i].style.width = '100%'; layers[i].style.height = '100%'; } if (t.fullscreenBtn) { removeClass(t.fullscreenBtn, `${t.options.classPrefix}fullscreen`); addClass(t.fullscreenBtn, `${t.options.classPrefix}unfullscreen`); } t.isFullScreen = true; t.setControlsSize(); // Recalculate player dimensions for fullscreen after browser updates layout (only for non-native) if (!isNative) { requestAnimationFrame(() => { t.setPlayerSize(screen.width, screen.height); }); } const zoomFactor = Math.min(screen.width / t.width, screen.height / t.height), captionText = t.getElement(t.container).querySelector(`.${t.options.classPrefix}captions-text`) ; if (captionText) { captionText.style.fontSize = `${(zoomFactor * 100)}%`; captionText.style.lineHeight = 'normal'; t.getElement(t.container).querySelector(`.${t.options.classPrefix}captions-position`).style.bottom = `${(((screen.height - t.normalHeight) / 2) - (t.getElement(t.controls).offsetHeight / 2) + zoomFactor + 15)}px`; } const event = createEvent('enteredfullscreen', t.getElement(t.container)); t.getElement(t.container).dispatchEvent(event); }, /** * */ exitFullScreen () { const t = this, isNative = t.media.rendererName !== null && /(native|html5)/i.test(t.media.rendererName) ; if (!t.isVideo) { return; } // Prevent container from attempting to stretch a second time clearTimeout(t.containerSizeTimeout); // come out of native fullscreen if (Features.HAS_TRUE_NATIVE_FULLSCREEN && (Features.IS_FULLSCREEN || t.isFullScreen)) { Features.cancelFullScreen(); } // restore scroll bars to document removeClass(document.documentElement, `${t.options.classPrefix}fullscreen`); removeClass(t.getElement(t.container), `${t.options.classPrefix}container-fullscreen`); if (t.options.setDimensions) { t.getElement(t.container).style.width = `${t.normalWidth}px`; t.getElement(t.container).style.height = `${t.normalHeight}px`; if (isNative) { t.node.style.width = `${t.normalWidth}px`; t.node.style.height = `${t.normalHeight}px`; } else { const elements = t.getElement(t.container).querySelectorAll('embed, object, video'), total = elements.length; for (let i = 0; i < total; i++) { elements[i].style.width = `${t.normalWidth}px`; elements[i].style.height = `${t.normalHeight}px`; } } if (typeof t.media.setSize === 'function') { t.media.setSize(t.normalWidth, t.normalHeight); } const layers = t.getElement(t.layers).children, total = layers.length; for (let i = 0; i < total; i++) { layers[i].style.width = `${t.normalWidth}px`; layers[i].style.height = `${t.normalHeight}px`; } } if (t.fullscreenBtn) { removeClass(t.fullscreenBtn, `${t.options.classPrefix}unfullscreen`); addClass(t.fullscreenBtn, `${t.options.classPrefix}fullscreen`); } t.setControlsSize(); t.isFullScreen = false; // Recalculate player dimensions after exiting fullscreen t.setPlayerSize(t.normalWidth, t.normalHeight); const captionText = t.getElement(t.container).querySelector(`.${t.options.classPrefix}captions-text`); if (captionText) { captionText.style.fontSize = ''; captionText.style.lineHeight = ''; t.getElement(t.container).querySelector(`.${t.options.classPrefix}captions-position`).style.bottom = ''; } const event = createEvent('exitedfullscreen', t.getElement(t.container)); t.getElement(t.container).dispatchEvent(event); } });