diff --git a/h5p.classes.php b/h5p.classes.php index 16ab951..8ff35f8 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1578,16 +1578,19 @@ Class H5PExport { */ private static function populateFileList($dir, &$files, $relative = '') { $strip = strlen($dir) + 1; - foreach (glob($dir . DIRECTORY_SEPARATOR . '*') as $file) { - $rel = $relative . substr($file, $strip); - if (is_dir($file)) { - self::populateFileList($file, $files, $rel . '/'); - } - else { - $files[] = (object) array( - 'absolutePath' => $file, - 'relativePath' => $rel - ); + $contents = glob($dir . DIRECTORY_SEPARATOR . '*'); + if (!empty($contents)) { + foreach ($contents as $file) { + $rel = $relative . substr($file, $strip); + if (is_dir($file)) { + self::populateFileList($file, $files, $rel . '/'); + } + else { + $files[] = (object) array( + 'absolutePath' => $file, + 'relativePath' => $rel + ); + } } } } diff --git a/js/h5p-resizer.js b/js/h5p-resizer.js index ab35e19..228cc72 100644 --- a/js/h5p-resizer.js +++ b/js/h5p-resizer.js @@ -19,6 +19,7 @@ actionHandlers.hello = function (iframe, data, respond) { // Make iframe responsive iframe.style.width = '100%'; + iframe.contentDocument.body.style.height = 'auto'; // Tell iframe that it needs to resize when our window resizes var resize = function (event) { @@ -46,16 +47,6 @@ * @param {Function} respond Send a response to the iframe */ actionHandlers.prepareResize = function (iframe, data, respond) { - responseData = {}; - - // Create spaceholder and insert after iframe. - var spaceholder = document.createElement('div'); - spaceholder.style.height = (iframe.clientHeight - 1) + 'px'; - iframe.parentNode.insertBefore(spaceholder, iframe.nextSibling); - - // Reset iframe height, in case content has shrinked. - iframe.style.height = '1px'; - respond('resizePrepared'); }; @@ -68,14 +59,21 @@ * @param {Function} respond Send a response to the iframe */ actionHandlers.resize = function (iframe, data, respond) { - // Resize iframe so all content is visible. - iframe.style.height = data.height + 'px'; - iframe.parentNode.removeChild(iframe.nextSibling); + if (iframe.clientHeight === iframe.contentDocument.body.scrollHeight && + iframe.contentDocument.body.scrollHeight === iframe.contentWindow.document.body.clientHeight) { + return; // Do not resize unless page and scrolling differs + } + + // Reset iframe height, in case content has shrinked. + iframe.style.height = iframe.contentWindow.document.body.clientHeight + 'px'; + + // Resize iframe so all content is visible. Use scrollHeight to make sure we get everything + iframe.style.height = iframe.contentDocument.body.scrollHeight + 'px'; }; /** * Keyup event handler. Exits full screen on escape. - * + * * @param {Event} event */ var escape = function (event) { diff --git a/js/h5p-x-api-event.js b/js/h5p-x-api-event.js index 98b287a..c5ce2e3 100644 --- a/js/h5p-x-api-event.js +++ b/js/h5p-x-api-event.js @@ -18,18 +18,40 @@ H5P.XAPIEvent.prototype.constructor = H5P.XAPIEvent; * * @param {number} score * @param {number} maxScore + * @param {object} instance + * @param {boolean} completion + * @param {boolean} success */ -H5P.XAPIEvent.prototype.setScoredResult = function (score, maxScore, instance) { - this.data.statement.result = { - 'score': { - 'min': 0, - 'max': maxScore, - 'raw': score +H5P.XAPIEvent.prototype.setScoredResult = function (score, maxScore, instance, completion, success) { + this.data.statement.result = {}; + + if (typeof score !== 'undefined') { + if (typeof maxScore === 'undefined') { + this.data.statement.result.score = {'raw': score}; + } + else { + this.data.statement.result.score = { + 'min': 0, + 'max': maxScore, + 'raw': score + }; + if (maxScore > 0) { + this.data.statement.result.score.scaled = Math.round(score / maxScore * 10000) / 10000; + } } - }; - if (maxScore > 0) { - this.data.statement.result.score.scaled = Math.round(score / maxScore * 10000) / 10000; } + + if (typeof completion === 'undefined') { + this.data.statement.result.completion = (this.getVerb() === 'completed' || this.getVerb() === 'answered'); + } + else { + this.data.statement.result.completion = completion; + } + + if (typeof success !== 'undefined') { + this.data.statement.result.success = success; + } + if (instance && instance.activityStartTime) { var duration = Math.round((Date.now() - instance.activityStartTime ) / 10) / 100; // xAPI spec allows a precision of 0.01 seconds diff --git a/js/h5p-x-api.js b/js/h5p-x-api.js index e287c1a..08908bf 100644 --- a/js/h5p-x-api.js +++ b/js/h5p-x-api.js @@ -65,9 +65,11 @@ H5P.EventDispatcher.prototype.createXAPIEventTemplate = function (verb, extra) { * Will be set as the 'raw' value of the score object * @param {number} maxScore * will be set as the "max" value of the score object + * @param {boolean} success + * will be set as the "success" value of the result object */ -H5P.EventDispatcher.prototype.triggerXAPICompleted = function (score, maxScore) { - this.triggerXAPIScored(score, maxScore, 'completed'); +H5P.EventDispatcher.prototype.triggerXAPICompleted = function (score, maxScore, success) { + this.triggerXAPIScored(score, maxScore, 'completed', true, success); }; /** @@ -79,10 +81,14 @@ H5P.EventDispatcher.prototype.triggerXAPICompleted = function (score, maxScore) * Will be set as the "max" value of the score object * @param {string} verb * Short form of adl verb + * @param {boolean} completion + * Is this a statement from a completed activity? + * @param {boolean} success + * Is this a statement from an activity that was done successfully? */ -H5P.EventDispatcher.prototype.triggerXAPIScored = function (score, maxScore, verb) { +H5P.EventDispatcher.prototype.triggerXAPIScored = function (score, maxScore, verb, completion, success) { var event = this.createXAPIEventTemplate(verb); - event.setScoredResult(score, maxScore, this); + event.setScoredResult(score, maxScore, this, completion, success); this.trigger(event); }; @@ -96,7 +102,7 @@ H5P.EventDispatcher.prototype.setActivityStarted = function() { * @param {H5P.XAPIEvent} event */ H5P.xAPICompletedListener = function (event) { - if (event.getVerb() === 'completed' && !event.getVerifiedStatementValue(['context', 'contextActivities', 'parent'])) { + if ((event.getVerb() === 'completed' || event.getVerb() === 'answered') && !event.getVerifiedStatementValue(['context', 'contextActivities', 'parent'])) { var score = event.getScore(); var maxScore = event.getMaxScore(); var contentId = event.getVerifiedStatementValue(['object', 'definition', 'extensions', 'http://h5p.org/x-api/h5p-local-content-id']); diff --git a/js/h5p.js b/js/h5p.js index a55a87c..504d8d0 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -539,7 +539,13 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) { before('h5p-semi-fullscreen'); var $disable = H5P.jQuery('
').appendTo($container.find('.h5p-content-controls')); - var keyup, disableSemiFullscreen = function () { + var keyup, disableSemiFullscreen = H5P.exitFullScreen = function () { + if (lastViewport) { + metaTags[i].content = lastViewport; + } + else { + head.removeChild(metaTag); + } $disable.remove(); $body.unbind('keyup', keyup); done('h5p-semi-fullscreen'); @@ -551,6 +557,27 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) { }; $disable.click(disableSemiFullscreen); $body.keyup(keyup); + + // Disable zoom + var lastViewport; + var metaTags = document.getElementsByTagName('meta'); + for (var i = 0; i < metaTags.length; i++) { + if (metaTags[i].name === 'viewport') { + lastViewport = metaTags[i].content; + break; + } + } + if (!lastViewport) { + // Create tag + metaTags[i] = document.createElement('meta'); + metaTags[i].name = 'viewport'; + } + metaTags[i].content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0'; + if (!lastViewport) { + var head = document.getElementsByTagName('head')[0]; + head.appendChild(metaTag); + } + entered(); } else { diff --git a/styles/h5p.css b/styles/h5p.css index 3fca24e..4586952 100644 --- a/styles/h5p.css +++ b/styles/h5p.css @@ -130,13 +130,15 @@ div.h5p-fullscreen { } .h5p-iframe-wrapper.h5p-semi-fullscreen { - width: 100%; - height: 100%; + width: auto; + height: auto; background: black; position: fixed; top: 0; left: 0; - z-index: 1000; + right: 0; + bottom: 0; + z-index: 100001; } .h5p-iframe-wrapper.h5p-semi-fullscreen .buttons { position: absolute;