diff --git a/config.xml b/config.xml index 3260607d7..ae353eb0d 100644 --- a/config.xml +++ b/config.xml @@ -47,7 +47,7 @@ - + diff --git a/src/core/components/iframe/iframe.ts b/src/core/components/iframe/iframe.ts index 522d46867..116b672d6 100644 --- a/src/core/components/iframe/iframe.ts +++ b/src/core/components/iframe/iframe.ts @@ -57,9 +57,13 @@ export class CoreIframeComponent implements OnChanges, OnDestroy { protected style?: HTMLStyleElement; protected orientationObs?: CoreEventObserver; protected navSubscription?: Subscription; + protected messageListenerFunction: (event: MessageEvent) => Promise; constructor(protected elementRef: ElementRef) { this.loaded = new EventEmitter(); + + // Listen for messages from the iframe. + window.addEventListener('message', this.messageListenerFunction = (event) => this.onIframeMessage(event)); } /** @@ -177,12 +181,13 @@ export class CoreIframeComponent implements OnChanges, OnDestroy { ngOnDestroy(): void { this.orientationObs?.off(); this.navSubscription?.unsubscribe(); + window.removeEventListener('message', this.messageListenerFunction); } /** * Toggle fullscreen mode. */ - toggleFullscreen(enable?: boolean): void { + toggleFullscreen(enable?: boolean, notifyIframe = true): void { if (enable !== undefined) { this.fullscreen = enable; } else { @@ -200,6 +205,27 @@ export class CoreIframeComponent implements OnChanges, OnDestroy { } document.body.classList.toggle('core-iframe-fullscreen', this.fullscreen); + + if (notifyIframe && this.iframe?.nativeElement) { + ( this.iframe.nativeElement).contentWindow?.postMessage( + this.fullscreen ? 'enterFullScreen' : 'exitFullScreen', + '*', + ); + } + } + + /** + * Treat an iframe message event. + * + * @param event Event. + * @return Promise resolved when done. + */ + protected async onIframeMessage(event: MessageEvent): Promise { + if (event.data == 'enterFullScreen' && this.showFullscreenOnToolbar && !this.fullscreen) { + this.toggleFullscreen(true, false); + } else if (event.data == 'exitFullScreen' && this.fullscreen) { + this.toggleFullscreen(false, false); + } } } diff --git a/src/core/features/h5p/assets/js/h5p.js b/src/core/features/h5p/assets/js/h5p.js index 424ac842f..7cb831751 100644 --- a/src/core/features/h5p/assets/js/h5p.js +++ b/src/core/features/h5p/assets/js/h5p.js @@ -22,6 +22,11 @@ H5P.$window = H5P.jQuery(window); */ H5P.instances = []; +function isIOS() { + return ['iPad Simulator', 'iPhone Simulator', 'iPod Simulator', 'iPad', 'iPhone', 'iPod'].includes(navigator.platform) + || (navigator.userAgent.includes("Mac") && "ontouchend" in document); +} + // Detect if we support fullscreen, and what prefix to use. if (document.documentElement.requestFullScreen) { /** @@ -32,23 +37,23 @@ if (document.documentElement.requestFullScreen) { H5P.fullScreenBrowserPrefix = ''; } else if (document.documentElement.webkitRequestFullScreen) { - // This code has been changed to allow full screen in Moodle app. - H5P.fullScreenBrowserPrefix = 'webkit'; - H5P.safariBrowser = 0; + H5P.safariBrowser = navigator.userAgent.match(/version\/([.\d]+)/i); + H5P.safariBrowser = (H5P.safariBrowser === null ? 0 : parseInt(H5P.safariBrowser[1])); - // H5P.safariBrowser = navigator.userAgent.match(/version\/([.\d]+)/i); - // H5P.safariBrowser = (H5P.safariBrowser === null ? 0 : parseInt(H5P.safariBrowser[1])); - - // // Do not allow fullscreen for safari < 7. - // if (H5P.safariBrowser === 0 || H5P.safariBrowser > 6) { - // H5P.fullScreenBrowserPrefix = 'webkit'; - // } + // Do not allow fullscreen for safari < 7. + if (H5P.safariBrowser === 0 || H5P.safariBrowser > 6) { + H5P.fullScreenBrowserPrefix = 'webkit'; + } } else if (document.documentElement.mozRequestFullScreen) { H5P.fullScreenBrowserPrefix = 'moz'; } else if (document.documentElement.msRequestFullscreen) { H5P.fullScreenBrowserPrefix = 'ms'; +} else if (isIOS()) { + // This code has been added to allow a "fake" full screen in Moodle app. + H5P.fullScreenBrowserPrefix = 'webkit'; + H5P.safariBrowser = 0; } /** @@ -165,6 +170,17 @@ H5P.init = function (target) { } }) ; + + if (isIOS()) { + // Register message listener to enter fullscreen. + window.addEventListener('message', function receiveMessage(event) { + if (event.data == 'enterFullScreen') { + H5P.fullScreen($container, instance); + } else if (event.data == 'exitFullScreen') { + H5P.exitFullScreen(); + } + }, false); + } } /** @@ -587,7 +603,7 @@ H5P.fullScreen = function ($element, instance, exitCallback, body, forceSemiFull }; H5P.isFullscreen = true; - if (H5P.fullScreenBrowserPrefix === undefined || forceSemiFullScreen === true) { + if (forceSemiFullScreen === true) { // Create semi fullscreen. if (H5P.isFramed) { @@ -666,7 +682,13 @@ H5P.fullScreen = function ($element, instance, exitCallback, body, forceSemiFull else { var method = (H5P.fullScreenBrowserPrefix === 'ms' ? 'msRequestFullscreen' : H5P.fullScreenBrowserPrefix + 'RequestFullScreen'); var params = (H5P.fullScreenBrowserPrefix === 'webkit' && H5P.safariBrowser === 0 ? Element.ALLOW_KEYBOARD_INPUT : undefined); - $element[0][method](params); + + if (isIOS()) { + before('h5p-fullscreen-ios'); + window.parent.postMessage('enterFullScreen', '*'); + } else { + $element[0][method](params); + } } // Allows everone to exit @@ -678,12 +700,28 @@ H5P.fullScreen = function ($element, instance, exitCallback, body, forceSemiFull document.mozCancelFullScreen(); } else { - document[H5P.fullScreenBrowserPrefix + 'ExitFullscreen'](); + done('h5p-fullscreen'); + document[H5P.fullScreenBrowserPrefix + 'ExitFullscreen'] && document[H5P.fullScreenBrowserPrefix + 'ExitFullscreen'](); + if (isIOS()) { + done('h5p-fullscreen-ios'); + window.parent.postMessage('exitFullScreen', '*'); + } } }; } }; +if (isIOS()) { + // Pass fullscreen messages to child iframes. + window.addEventListener('message', function receiveMessage(event) { + if (event.data === 'enterFullScreen' || event.data === 'exitFullScreen') { + Array.from(document.querySelectorAll('iframe')).forEach(function (iframe) { + iframe.contentWindow && iframe.contentWindow.postMessage(event.data, '*'); + }); + } + }, false); +} + (function () { /** * Helper for adding a query parameter to an existing path that may already