Merge branch 'master' into copy-and-paste
commit
4bca7055ab
|
@ -1578,16 +1578,19 @@ Class H5PExport {
|
||||||
*/
|
*/
|
||||||
private static function populateFileList($dir, &$files, $relative = '') {
|
private static function populateFileList($dir, &$files, $relative = '') {
|
||||||
$strip = strlen($dir) + 1;
|
$strip = strlen($dir) + 1;
|
||||||
foreach (glob($dir . DIRECTORY_SEPARATOR . '*') as $file) {
|
$contents = glob($dir . DIRECTORY_SEPARATOR . '*');
|
||||||
$rel = $relative . substr($file, $strip);
|
if (!empty($contents)) {
|
||||||
if (is_dir($file)) {
|
foreach ($contents as $file) {
|
||||||
self::populateFileList($file, $files, $rel . '/');
|
$rel = $relative . substr($file, $strip);
|
||||||
}
|
if (is_dir($file)) {
|
||||||
else {
|
self::populateFileList($file, $files, $rel . '/');
|
||||||
$files[] = (object) array(
|
}
|
||||||
'absolutePath' => $file,
|
else {
|
||||||
'relativePath' => $rel
|
$files[] = (object) array(
|
||||||
);
|
'absolutePath' => $file,
|
||||||
|
'relativePath' => $rel
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
actionHandlers.hello = function (iframe, data, respond) {
|
actionHandlers.hello = function (iframe, data, respond) {
|
||||||
// Make iframe responsive
|
// Make iframe responsive
|
||||||
iframe.style.width = '100%';
|
iframe.style.width = '100%';
|
||||||
|
iframe.contentDocument.body.style.height = 'auto';
|
||||||
|
|
||||||
// Tell iframe that it needs to resize when our window resizes
|
// Tell iframe that it needs to resize when our window resizes
|
||||||
var resize = function (event) {
|
var resize = function (event) {
|
||||||
|
@ -46,16 +47,6 @@
|
||||||
* @param {Function} respond Send a response to the iframe
|
* @param {Function} respond Send a response to the iframe
|
||||||
*/
|
*/
|
||||||
actionHandlers.prepareResize = function (iframe, data, respond) {
|
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');
|
respond('resizePrepared');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -68,9 +59,16 @@
|
||||||
* @param {Function} respond Send a response to the iframe
|
* @param {Function} respond Send a response to the iframe
|
||||||
*/
|
*/
|
||||||
actionHandlers.resize = function (iframe, data, respond) {
|
actionHandlers.resize = function (iframe, data, respond) {
|
||||||
// Resize iframe so all content is visible.
|
if (iframe.clientHeight === iframe.contentDocument.body.scrollHeight &&
|
||||||
iframe.style.height = data.height + 'px';
|
iframe.contentDocument.body.scrollHeight === iframe.contentWindow.document.body.clientHeight) {
|
||||||
iframe.parentNode.removeChild(iframe.nextSibling);
|
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';
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -18,18 +18,40 @@ H5P.XAPIEvent.prototype.constructor = H5P.XAPIEvent;
|
||||||
*
|
*
|
||||||
* @param {number} score
|
* @param {number} score
|
||||||
* @param {number} maxScore
|
* @param {number} maxScore
|
||||||
|
* @param {object} instance
|
||||||
|
* @param {boolean} completion
|
||||||
|
* @param {boolean} success
|
||||||
*/
|
*/
|
||||||
H5P.XAPIEvent.prototype.setScoredResult = function (score, maxScore, instance) {
|
H5P.XAPIEvent.prototype.setScoredResult = function (score, maxScore, instance, completion, success) {
|
||||||
this.data.statement.result = {
|
this.data.statement.result = {};
|
||||||
'score': {
|
|
||||||
'min': 0,
|
if (typeof score !== 'undefined') {
|
||||||
'max': maxScore,
|
if (typeof maxScore === 'undefined') {
|
||||||
'raw': score
|
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) {
|
if (instance && instance.activityStartTime) {
|
||||||
var duration = Math.round((Date.now() - instance.activityStartTime ) / 10) / 100;
|
var duration = Math.round((Date.now() - instance.activityStartTime ) / 10) / 100;
|
||||||
// xAPI spec allows a precision of 0.01 seconds
|
// xAPI spec allows a precision of 0.01 seconds
|
||||||
|
|
|
@ -65,9 +65,11 @@ H5P.EventDispatcher.prototype.createXAPIEventTemplate = function (verb, extra) {
|
||||||
* Will be set as the 'raw' value of the score object
|
* Will be set as the 'raw' value of the score object
|
||||||
* @param {number} maxScore
|
* @param {number} maxScore
|
||||||
* will be set as the "max" value of the score object
|
* 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) {
|
H5P.EventDispatcher.prototype.triggerXAPICompleted = function (score, maxScore, success) {
|
||||||
this.triggerXAPIScored(score, maxScore, 'completed');
|
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
|
* Will be set as the "max" value of the score object
|
||||||
* @param {string} verb
|
* @param {string} verb
|
||||||
* Short form of adl 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);
|
var event = this.createXAPIEventTemplate(verb);
|
||||||
event.setScoredResult(score, maxScore, this);
|
event.setScoredResult(score, maxScore, this, completion, success);
|
||||||
this.trigger(event);
|
this.trigger(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -96,7 +102,7 @@ H5P.EventDispatcher.prototype.setActivityStarted = function() {
|
||||||
* @param {H5P.XAPIEvent} event
|
* @param {H5P.XAPIEvent} event
|
||||||
*/
|
*/
|
||||||
H5P.xAPICompletedListener = function (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 score = event.getScore();
|
||||||
var maxScore = event.getMaxScore();
|
var maxScore = event.getMaxScore();
|
||||||
var contentId = event.getVerifiedStatementValue(['object', 'definition', 'extensions', 'http://h5p.org/x-api/h5p-local-content-id']);
|
var contentId = event.getVerifiedStatementValue(['object', 'definition', 'extensions', 'http://h5p.org/x-api/h5p-local-content-id']);
|
||||||
|
|
29
js/h5p.js
29
js/h5p.js
|
@ -539,7 +539,13 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) {
|
||||||
|
|
||||||
before('h5p-semi-fullscreen');
|
before('h5p-semi-fullscreen');
|
||||||
var $disable = H5P.jQuery('<div role="button" tabindex="1" class="h5p-disable-fullscreen" title="' + H5P.t('disableFullscreen') + '"></div>').appendTo($container.find('.h5p-content-controls'));
|
var $disable = H5P.jQuery('<div role="button" tabindex="1" class="h5p-disable-fullscreen" title="' + H5P.t('disableFullscreen') + '"></div>').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();
|
$disable.remove();
|
||||||
$body.unbind('keyup', keyup);
|
$body.unbind('keyup', keyup);
|
||||||
done('h5p-semi-fullscreen');
|
done('h5p-semi-fullscreen');
|
||||||
|
@ -551,6 +557,27 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) {
|
||||||
};
|
};
|
||||||
$disable.click(disableSemiFullscreen);
|
$disable.click(disableSemiFullscreen);
|
||||||
$body.keyup(keyup);
|
$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();
|
entered();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -130,13 +130,15 @@ div.h5p-fullscreen {
|
||||||
}
|
}
|
||||||
|
|
||||||
.h5p-iframe-wrapper.h5p-semi-fullscreen {
|
.h5p-iframe-wrapper.h5p-semi-fullscreen {
|
||||||
width: 100%;
|
width: auto;
|
||||||
height: 100%;
|
height: auto;
|
||||||
background: black;
|
background: black;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
z-index: 1000;
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 100001;
|
||||||
}
|
}
|
||||||
.h5p-iframe-wrapper.h5p-semi-fullscreen .buttons {
|
.h5p-iframe-wrapper.h5p-semi-fullscreen .buttons {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
Loading…
Reference in New Issue