From ea2aa48d77202d5171f9cbb3742cf18ecc53e246 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Mon, 25 Nov 2019 09:19:26 +0100 Subject: [PATCH] MOBILE-2235 h5p: Include resizer scripts --- src/core/filter/providers/delegate.ts | 1 + src/core/h5p/assets/moodle/js/embed.js | 155 ++++++++++++++++++ .../h5p-player/core-h5p-player.html | 3 +- .../h5p/components/h5p-player/h5p-player.ts | 13 ++ src/core/h5p/providers/h5p.ts | 22 ++- src/providers/utils/dom.ts | 2 +- 6 files changed, 188 insertions(+), 8 deletions(-) create mode 100644 src/core/h5p/assets/moodle/js/embed.js diff --git a/src/core/filter/providers/delegate.ts b/src/core/filter/providers/delegate.ts index d712ffb31..642a454ca 100644 --- a/src/core/filter/providers/delegate.ts +++ b/src/core/filter/providers/delegate.ts @@ -188,6 +188,7 @@ export class CoreFilterDelegate extends CoreDelegate { } promise = promise.then(() => { + return Promise.resolve(this.executeFunctionOnEnabled(filter.filter, 'handleHtml', [container, filter, options, viewContainerRef, component, componentId, siteId])).catch((error) => { this.logger.error('Error handling HTML' + filter.filter, error); diff --git a/src/core/h5p/assets/moodle/js/embed.js b/src/core/h5p/assets/moodle/js/embed.js new file mode 100644 index 000000000..6135db2e5 --- /dev/null +++ b/src/core/h5p/assets/moodle/js/embed.js @@ -0,0 +1,155 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/* global H5PEmbedCommunicator:true */ +/** + * When embedded the communicator helps talk to the parent page. + * This is a copy of the H5P.communicator, which we need to communicate in this context + * + * @type {H5PEmbedCommunicator} + * @module core_h5p + * @copyright 2019 Joubel AS + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +H5PEmbedCommunicator = (function() { + /** + * @class + * @private + */ + function Communicator() { + var self = this; + + // Maps actions to functions. + var actionHandlers = {}; + + // Register message listener. + window.addEventListener('message', function receiveMessage(event) { + if (window.parent !== event.source || event.data.context !== 'h5p') { + return; // Only handle messages from parent and in the correct context. + } + + if (actionHandlers[event.data.action] !== undefined) { + actionHandlers[event.data.action](event.data); + } + }, false); + + /** + * Register action listener. + * + * @param {string} action What you are waiting for + * @param {function} handler What you want done + */ + self.on = function(action, handler) { + actionHandlers[action] = handler; + }; + + /** + * Send a message to the all mighty father. + * + * @param {string} action + * @param {Object} [data] payload + */ + self.send = function(action, data) { + if (data === undefined) { + data = {}; + } + data.context = 'h5p'; + data.action = action; + + // Parent origin can be anything. + window.parent.postMessage(data, '*'); + }; + } + + return (window.postMessage && window.addEventListener ? new Communicator() : undefined); +})(); + +document.onreadystatechange = function() { + // Wait for instances to be initialize. + if (document.readyState !== 'complete') { + return; + } + + // Check for H5P iFrame. + var iFrame = document.querySelector('.h5p-iframe'); + if (!iFrame || !iFrame.contentWindow) { + return; + } + var H5P = iFrame.contentWindow.H5P; + + // Check for H5P instances. + if (!H5P || !H5P.instances || !H5P.instances[0]) { + return; + } + + var resizeDelay; + var instance = H5P.instances[0]; + var parentIsFriendly = false; + + // Handle that the resizer is loaded after the iframe. + H5PEmbedCommunicator.on('ready', function() { + H5PEmbedCommunicator.send('hello'); + }); + + // Handle hello message from our parent window. + H5PEmbedCommunicator.on('hello', function() { + // Initial setup/handshake is done. + parentIsFriendly = true; + + // Hide scrollbars for correct size. + iFrame.contentDocument.body.style.overflow = 'hidden'; + + document.body.classList.add('h5p-resizing'); + + // Content need to be resized to fit the new iframe size. + H5P.trigger(instance, 'resize'); + }); + + // When resize has been prepared tell parent window to resize. + H5PEmbedCommunicator.on('resizePrepared', function() { + H5PEmbedCommunicator.send('resize', { + scrollHeight: iFrame.contentDocument.body.scrollHeight + }); + }); + + H5PEmbedCommunicator.on('resize', function() { + H5P.trigger(instance, 'resize'); + }); + + H5P.on(instance, 'resize', function() { + if (H5P.isFullscreen) { + return; // Skip iframe resize. + } + + // Use a delay to make sure iframe is resized to the correct size. + clearTimeout(resizeDelay); + resizeDelay = setTimeout(function() { + // Only resize if the iframe can be resized. + if (parentIsFriendly) { + H5PEmbedCommunicator.send('prepareResize', + { + scrollHeight: iFrame.contentDocument.body.scrollHeight, + clientHeight: iFrame.contentDocument.body.clientHeight + } + ); + } else { + H5PEmbedCommunicator.send('hello'); + } + }, 0); + }); + + // Trigger initial resize for instance. + H5P.trigger(instance, 'resize'); +}; diff --git a/src/core/h5p/components/h5p-player/core-h5p-player.html b/src/core/h5p/components/h5p-player/core-h5p-player.html index 2fb1e9989..3cfd58ccb 100644 --- a/src/core/h5p/components/h5p-player/core-h5p-player.html +++ b/src/core/h5p/components/h5p-player/core-h5p-player.html @@ -9,4 +9,5 @@ - + + diff --git a/src/core/h5p/components/h5p-player/h5p-player.ts b/src/core/h5p/components/h5p-player/h5p-player.ts index 0627e89d6..ccc5b5325 100644 --- a/src/core/h5p/components/h5p-player/h5p-player.ts +++ b/src/core/h5p/components/h5p-player/h5p-player.ts @@ -99,6 +99,8 @@ export class CoreH5PPlayerComponent implements OnInit, OnChanges, OnDestroy { let promise; + this.addResizerScript(); + if (this.canDownload && (this.state == CoreConstants.DOWNLOADED || this.state == CoreConstants.OUTDATED)) { // Package is downloaded, use the local URL. promise = this.h5pProvider.getContentIndexFileUrl(this.urlParams.url).catch((error) => { @@ -154,6 +156,17 @@ export class CoreH5PPlayerComponent implements OnInit, OnChanges, OnDestroy { }); } + /** + * Add the resizer script if it hasn't been added already. + */ + protected addResizerScript(): void { + const script = document.createElement('script'); + script.id = 'core-h5p-resizer-script'; + script.type = 'text/javascript'; + script.src = this.h5pProvider.getResizerScriptUrl(); + document.head.appendChild(script); + } + /** * Check if the package can be downloaded. */ diff --git a/src/core/h5p/providers/h5p.ts b/src/core/h5p/providers/h5p.ts index 57d674897..d92f398a7 100644 --- a/src/core/h5p/providers/h5p.ts +++ b/src/core/h5p/providers/h5p.ts @@ -522,20 +522,22 @@ export class CoreH5PProvider { let html = '' + content.title + '' + ''; - // @todo: Load the embed.js to allow communication with the parent window. - // $PAGE->requires->js(new moodle_url('/h5p/js/embed.js')); - // Include the required CSS. result.cssRequires.forEach((cssUrl) => { html += ''; }); // Add the settings. - html += ''; + html += ''; html += ''; // Include the required JS at the beginning of the body, like Moodle web does. + // Load the embed.js to allow communication with the parent window. + html += ''; + result.jsRequires.forEach((jsUrl) => { html += ''; }); @@ -1664,8 +1666,16 @@ export class CoreH5PProvider { * @return The HTML code with the resize script. */ protected getResizeCode(): string { - // @todo return ''; - return ''; + return ''; + } + + /** + * Get the URL to the resizer script. + * + * @return URL. + */ + getResizerScriptUrl(): string { + return this.textUtils.concatenatePaths(this.getCoreH5PPath(), 'js/h5p-resizer.js'); } /** diff --git a/src/providers/utils/dom.ts b/src/providers/utils/dom.ts index 37bb85907..18dec5369 100644 --- a/src/providers/utils/dom.ts +++ b/src/providers/utils/dom.ts @@ -385,7 +385,7 @@ export class CoreDomUtilsProvider { * @return Formatted size. If size is not valid, returns an empty string. */ formatPixelsSize(size: any): string { - if (typeof size == 'string' && (size.indexOf('px') > -1 || size.indexOf('%') > -1)) { + if (typeof size == 'string' && (size.indexOf('px') > -1 || size.indexOf('%') > -1 || size == 'auto' || size == 'initial')) { // It seems to be a valid size. return size; }