2014-10-01 10:05:04 +02:00
|
|
|
|
/*jshint multistr: true */
|
2014-06-19 16:09:23 +02:00
|
|
|
|
// TODO: Should we split up the generic parts needed by the editor(and others), and the parts needed to "run" H5Ps?
|
2015-05-05 13:13:57 +02:00
|
|
|
|
|
|
|
|
|
/** @namespace */
|
2013-04-02 17:05:16 +02:00
|
|
|
|
var H5P = H5P || {};
|
2013-01-17 09:01:43 +01:00
|
|
|
|
|
2015-05-05 13:13:57 +02:00
|
|
|
|
/**
|
|
|
|
|
* Tells us if we're inside of an iframe.
|
|
|
|
|
* @member {boolean}
|
|
|
|
|
*/
|
2015-09-10 14:27:12 +02:00
|
|
|
|
H5P.isFramed = (window.self !== window.parent);
|
2014-03-26 08:43:29 +01:00
|
|
|
|
|
2015-05-05 13:13:57 +02:00
|
|
|
|
/**
|
|
|
|
|
* jQuery instance of current window.
|
|
|
|
|
* @member {H5P.jQuery}
|
|
|
|
|
*/
|
2014-03-26 08:43:29 +01:00
|
|
|
|
H5P.$window = H5P.jQuery(window);
|
|
|
|
|
|
2015-05-05 13:13:57 +02:00
|
|
|
|
/**
|
|
|
|
|
* List over H5P instances on the current page.
|
|
|
|
|
* @member {Array}
|
|
|
|
|
*/
|
2014-10-13 22:19:59 +02:00
|
|
|
|
H5P.instances = [];
|
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
// Detect if we support fullscreen, and what prefix to use.
|
|
|
|
|
if (document.documentElement.requestFullScreen) {
|
2015-05-05 13:13:57 +02:00
|
|
|
|
/**
|
|
|
|
|
* Browser prefix to use when entering fullscreen mode.
|
|
|
|
|
* undefined means no fullscreen support.
|
|
|
|
|
* @member {string}
|
|
|
|
|
*/
|
2014-03-26 08:43:29 +01:00
|
|
|
|
H5P.fullScreenBrowserPrefix = '';
|
|
|
|
|
}
|
2014-10-23 15:00:37 +02:00
|
|
|
|
else if (document.documentElement.webkitRequestFullScreen) {
|
2016-12-12 11:22:34 +01:00
|
|
|
|
H5P.safariBrowser = navigator.userAgent.match(/version\/([.\d]+)/i);
|
2014-05-22 11:17:30 +02:00
|
|
|
|
H5P.safariBrowser = (H5P.safariBrowser === null ? 0 : parseInt(H5P.safariBrowser[1]));
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-05-22 11:17:30 +02:00
|
|
|
|
// Do not allow fullscreen for safari < 7.
|
|
|
|
|
if (H5P.safariBrowser === 0 || H5P.safariBrowser > 6) {
|
|
|
|
|
H5P.fullScreenBrowserPrefix = 'webkit';
|
|
|
|
|
}
|
2014-03-26 08:43:29 +01:00
|
|
|
|
}
|
|
|
|
|
else if (document.documentElement.mozRequestFullScreen) {
|
|
|
|
|
H5P.fullScreenBrowserPrefix = 'moz';
|
|
|
|
|
}
|
|
|
|
|
else if (document.documentElement.msRequestFullscreen) {
|
|
|
|
|
H5P.fullScreenBrowserPrefix = 'ms';
|
|
|
|
|
}
|
2013-11-08 18:16:41 +01:00
|
|
|
|
|
2014-10-23 14:02:34 +02:00
|
|
|
|
/**
|
|
|
|
|
* Keep track of when the H5Ps where started.
|
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @type {Object[]}
|
2014-10-23 14:02:34 +02:00
|
|
|
|
*/
|
2014-10-15 11:38:29 +02:00
|
|
|
|
H5P.opened = {};
|
|
|
|
|
|
2013-11-26 14:54:29 +01:00
|
|
|
|
/**
|
|
|
|
|
* Initialize H5P content.
|
|
|
|
|
* Scans for ".h5p-content" in the document and initializes H5P instances where found.
|
2015-05-05 13:13:57 +02:00
|
|
|
|
*
|
|
|
|
|
* @param {Object} target DOM Element
|
2013-11-26 14:54:29 +01:00
|
|
|
|
*/
|
2015-03-10 10:09:31 +01:00
|
|
|
|
H5P.init = function (target) {
|
2014-03-26 08:43:29 +01:00
|
|
|
|
// Useful jQuery object.
|
2015-03-10 10:09:31 +01:00
|
|
|
|
if (H5P.$body === undefined) {
|
|
|
|
|
H5P.$body = H5P.jQuery(document.body);
|
|
|
|
|
}
|
2013-04-26 17:27:35 +02:00
|
|
|
|
|
2015-03-13 12:51:31 +01:00
|
|
|
|
// Determine if we can use full screen
|
2016-11-11 11:57:54 +01:00
|
|
|
|
if (H5P.fullscreenSupported === undefined) {
|
2015-05-05 13:13:57 +02:00
|
|
|
|
/**
|
|
|
|
|
* Use this variable to check if fullscreen is supported. Fullscreen can be
|
|
|
|
|
* restricted when embedding since not all browsers support the native
|
|
|
|
|
* fullscreen, and the semi-fullscreen solution doesn't work when embedded.
|
|
|
|
|
* @type {boolean}
|
|
|
|
|
*/
|
2016-12-14 16:21:42 +01:00
|
|
|
|
H5P.fullscreenSupported = !(H5P.isFramed && H5P.externalEmbed !== false) || !!(document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled);
|
2015-04-22 12:55:18 +02:00
|
|
|
|
// We should consider document.msFullscreenEnabled when they get their
|
|
|
|
|
// element sizing corrected. Ref. https://connect.microsoft.com/IE/feedback/details/838286/ie-11-incorrectly-reports-dom-element-sizes-in-fullscreen-mode-when-fullscreened-element-is-within-an-iframe
|
2015-03-13 12:51:31 +01:00
|
|
|
|
}
|
2013-11-08 18:16:41 +01:00
|
|
|
|
|
2016-12-21 11:34:51 +01:00
|
|
|
|
// Deprecated variable, kept to maintain backwards compatability
|
2016-12-12 11:28:23 +01:00
|
|
|
|
if (H5P.canHasFullScreen === undefined) {
|
|
|
|
|
/**
|
|
|
|
|
* @deprecated since version 1.11
|
|
|
|
|
* @type {boolean}
|
|
|
|
|
*/
|
2016-12-14 16:21:42 +01:00
|
|
|
|
H5P.canHasFullScreen = H5P.fullscreenSupported;
|
2016-12-12 11:28:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
// H5Ps added in normal DIV.
|
2015-03-10 10:09:31 +01:00
|
|
|
|
var $containers = H5P.jQuery('.h5p-content:not(.h5p-initialized)', target).each(function () {
|
|
|
|
|
var $element = H5P.jQuery(this).addClass('h5p-initialized');
|
2014-03-26 08:43:29 +01:00
|
|
|
|
var $container = H5P.jQuery('<div class="h5p-container"></div>').appendTo($element);
|
|
|
|
|
var contentId = $element.data('content-id');
|
2015-02-27 13:59:42 +01:00
|
|
|
|
var contentData = H5PIntegration.contents['cid-' + contentId];
|
2014-04-14 10:50:37 +02:00
|
|
|
|
if (contentData === undefined) {
|
|
|
|
|
return H5P.error('No data for content id ' + contentId + '. Perhaps the library is gone?');
|
|
|
|
|
}
|
2014-03-26 08:43:29 +01:00
|
|
|
|
var library = {
|
|
|
|
|
library: contentData.library,
|
2014-05-22 11:15:16 +02:00
|
|
|
|
params: JSON.parse(contentData.jsonContent)
|
2014-03-26 08:43:29 +01:00
|
|
|
|
};
|
|
|
|
|
|
2015-08-26 15:58:49 +02:00
|
|
|
|
H5P.getUserData(contentId, 'state', function (err, previousState) {
|
2015-04-07 19:32:44 +02:00
|
|
|
|
if (previousState) {
|
|
|
|
|
library.userDatas = {
|
|
|
|
|
state: previousState
|
|
|
|
|
};
|
|
|
|
|
}
|
2015-04-13 20:30:40 +02:00
|
|
|
|
else if (previousState === null && H5PIntegration.saveFreq) {
|
2015-04-07 19:32:44 +02:00
|
|
|
|
// Content has been reset. Display dialog.
|
|
|
|
|
delete contentData.contentUserData;
|
|
|
|
|
var dialog = new H5P.Dialog('content-user-data-reset', 'Data Reset', '<p>' + H5P.t('contentChanged') + '</p><p>' + H5P.t('startingOver') + '</p><div class="h5p-dialog-ok-button" tabIndex="0" role="button">OK</div>', $container);
|
|
|
|
|
H5P.jQuery(dialog).on('dialog-opened', function (event, $dialog) {
|
2016-06-07 11:09:42 +02:00
|
|
|
|
|
|
|
|
|
var closeDialog = function (event) {
|
|
|
|
|
if (event.type === 'click' || event.which === 32) {
|
2015-04-07 19:32:44 +02:00
|
|
|
|
dialog.close();
|
2016-06-07 11:09:42 +02:00
|
|
|
|
H5P.deleteUserData(contentId, 'state', 0);
|
2015-04-07 19:32:44 +02:00
|
|
|
|
}
|
2016-06-07 11:09:42 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$dialog.find('.h5p-dialog-ok-button').click(closeDialog).keypress(closeDialog);
|
2015-04-07 19:32:44 +02:00
|
|
|
|
});
|
|
|
|
|
dialog.open();
|
|
|
|
|
}
|
2016-07-15 16:14:32 +02:00
|
|
|
|
// If previousState is false we don't have a previous state
|
2015-04-07 19:32:44 +02:00
|
|
|
|
});
|
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
// Create new instance.
|
2015-07-14 14:37:39 +02:00
|
|
|
|
var instance = H5P.newRunnable(library, contentId, $container, true, {standalone: true});
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
// Check if we should add and display a fullscreen button for this H5P.
|
2016-11-11 11:57:54 +01:00
|
|
|
|
if (contentData.fullScreen == 1 && H5P.fullscreenSupported) {
|
2017-06-22 10:56:15 +02:00
|
|
|
|
H5P.jQuery(
|
|
|
|
|
'<div class="h5p-content-controls">' +
|
|
|
|
|
'<div role="button" ' +
|
|
|
|
|
'tabindex="0" ' +
|
|
|
|
|
'class="h5p-enable-fullscreen" ' +
|
|
|
|
|
'title="' + H5P.t('fullscreen') + '">' +
|
|
|
|
|
'</div>' +
|
|
|
|
|
'</div>')
|
|
|
|
|
.prependTo($container)
|
|
|
|
|
.children()
|
|
|
|
|
.click(function () {
|
|
|
|
|
H5P.fullScreen($container, instance);
|
|
|
|
|
})
|
|
|
|
|
.keydown(function (e) {
|
|
|
|
|
if (e.which === 32 || e.which === 13) {
|
|
|
|
|
H5P.fullScreen($container, instance);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
;
|
2014-10-01 10:05:04 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-04-08 13:18:35 +02:00
|
|
|
|
/**
|
2016-12-13 10:29:33 +01:00
|
|
|
|
* Create action bar
|
2016-04-08 13:18:35 +02:00
|
|
|
|
*/
|
2016-12-13 10:29:33 +01:00
|
|
|
|
var displayOptions = contentData.displayOptions;
|
2016-12-20 14:37:31 +01:00
|
|
|
|
var displayFrame = false;
|
2016-12-16 14:22:03 +01:00
|
|
|
|
if (displayOptions.frame) {
|
2016-12-13 10:29:33 +01:00
|
|
|
|
// Special handling of copyrights
|
2016-12-16 14:22:03 +01:00
|
|
|
|
if (displayOptions.copyright) {
|
2016-12-13 10:29:33 +01:00
|
|
|
|
var copyrights = H5P.getCopyrights(instance, library.params, contentId);
|
|
|
|
|
if (!copyrights) {
|
2016-12-16 14:22:03 +01:00
|
|
|
|
displayOptions.copyright = false;
|
2016-12-13 10:29:33 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-04-08 13:18:35 +02:00
|
|
|
|
|
2016-12-13 10:29:33 +01:00
|
|
|
|
// Create action bar
|
|
|
|
|
var actionBar = new H5P.ActionBar(displayOptions);
|
|
|
|
|
var $actions = actionBar.getDOMElement();
|
2015-05-11 16:00:55 +02:00
|
|
|
|
|
2016-12-13 10:29:33 +01:00
|
|
|
|
actionBar.on('download', function () {
|
2015-02-12 11:52:55 +01:00
|
|
|
|
window.location.href = contentData.exportUrl;
|
2017-01-31 13:35:23 +01:00
|
|
|
|
instance.triggerXAPI('downloaded');
|
2016-12-13 10:29:33 +01:00
|
|
|
|
});
|
|
|
|
|
actionBar.on('copyrights', function () {
|
|
|
|
|
var dialog = new H5P.Dialog('copyrights', H5P.t('copyrightInformation'), copyrights, $container);
|
|
|
|
|
dialog.open();
|
2017-01-31 13:35:23 +01:00
|
|
|
|
instance.triggerXAPI('accessed-copyright');
|
2016-12-13 10:29:33 +01:00
|
|
|
|
});
|
|
|
|
|
actionBar.on('embed', function () {
|
2015-02-25 12:10:07 +01:00
|
|
|
|
H5P.openEmbedDialog($actions, contentData.embedCode, contentData.resizeCode, {
|
2015-05-07 15:16:35 +02:00
|
|
|
|
width: $element.width(),
|
|
|
|
|
height: $element.height()
|
2015-02-25 12:10:07 +01:00
|
|
|
|
});
|
2017-01-31 13:35:23 +01:00
|
|
|
|
instance.triggerXAPI('accessed-embed');
|
2015-02-12 11:52:55 +01:00
|
|
|
|
});
|
2015-04-13 18:41:53 +02:00
|
|
|
|
|
2016-12-20 14:37:31 +01:00
|
|
|
|
if (actionBar.hasActions()) {
|
|
|
|
|
displayFrame = true;
|
|
|
|
|
$actions.insertAfter($container);
|
|
|
|
|
}
|
2015-02-12 11:52:55 +01:00
|
|
|
|
}
|
|
|
|
|
|
2016-12-20 14:37:31 +01:00
|
|
|
|
$element.addClass(displayFrame ? 'h5p-frame' : 'h5p-no-frame');
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-10-15 11:38:29 +02:00
|
|
|
|
// Keep track of when we started
|
|
|
|
|
H5P.opened[contentId] = new Date();
|
|
|
|
|
|
|
|
|
|
// Handle events when the user finishes the content. Useful for logging exercise results.
|
2015-02-20 10:15:36 +01:00
|
|
|
|
H5P.on(instance, 'finish', function (event) {
|
2014-10-15 11:38:29 +02:00
|
|
|
|
if (event.data !== undefined) {
|
|
|
|
|
H5P.setFinished(contentId, event.data.score, event.data.maxScore, event.data.time);
|
|
|
|
|
}
|
|
|
|
|
});
|
2014-04-30 15:59:19 +02:00
|
|
|
|
|
2015-02-20 10:15:36 +01:00
|
|
|
|
// Listen for xAPI events.
|
2015-02-16 16:47:04 +01:00
|
|
|
|
H5P.on(instance, 'xAPI', H5P.xAPICompletedListener);
|
2014-04-30 15:59:19 +02:00
|
|
|
|
|
2015-04-07 19:32:44 +02:00
|
|
|
|
// Auto save current state if supported
|
|
|
|
|
if (H5PIntegration.saveFreq !== false && (
|
|
|
|
|
instance.getCurrentState instanceof Function ||
|
|
|
|
|
typeof instance.getCurrentState === 'function')) {
|
2014-04-30 15:59:19 +02:00
|
|
|
|
|
2015-08-26 15:58:49 +02:00
|
|
|
|
var saveTimer, save = function () {
|
2015-04-07 19:32:44 +02:00
|
|
|
|
var state = instance.getCurrentState();
|
|
|
|
|
if (state !== undefined) {
|
2015-04-09 14:00:00 +02:00
|
|
|
|
H5P.setUserData(contentId, 'state', state, {deleteOnChange: true});
|
2015-04-07 19:32:44 +02:00
|
|
|
|
}
|
|
|
|
|
if (H5PIntegration.saveFreq) {
|
|
|
|
|
// Continue autosave
|
|
|
|
|
saveTimer = setTimeout(save, H5PIntegration.saveFreq * 1000);
|
|
|
|
|
}
|
2014-03-26 08:43:29 +01:00
|
|
|
|
};
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2015-04-07 19:32:44 +02:00
|
|
|
|
if (H5PIntegration.saveFreq) {
|
|
|
|
|
// Start autosave
|
|
|
|
|
saveTimer = setTimeout(save, H5PIntegration.saveFreq * 1000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// xAPI events will schedule a save in three seconds.
|
|
|
|
|
H5P.on(instance, 'xAPI', function (event) {
|
|
|
|
|
var verb = event.getVerb();
|
|
|
|
|
if (verb === 'completed' || verb === 'progressed') {
|
|
|
|
|
clearTimeout(saveTimer);
|
|
|
|
|
saveTimer = setTimeout(save, 3000);
|
|
|
|
|
}
|
2014-04-30 15:59:19 +02:00
|
|
|
|
});
|
2013-04-11 14:29:29 +02:00
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2015-03-03 11:03:10 +01:00
|
|
|
|
if (H5P.isFramed) {
|
|
|
|
|
var resizeDelay;
|
2015-02-09 10:22:14 +01:00
|
|
|
|
if (H5P.externalEmbed === false) {
|
|
|
|
|
// Internal embed
|
|
|
|
|
// Make it possible to resize the iframe when the content changes size. This way we get no scrollbars.
|
|
|
|
|
var iframe = window.parent.document.getElementById('h5p-iframe-' + contentId);
|
|
|
|
|
var resizeIframe = function () {
|
|
|
|
|
if (window.parent.H5P.isFullscreen) {
|
|
|
|
|
return; // Skip if full screen.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Retain parent size to avoid jumping/scrolling
|
|
|
|
|
var parentHeight = iframe.parentElement.style.height;
|
|
|
|
|
iframe.parentElement.style.height = iframe.parentElement.clientHeight + 'px';
|
|
|
|
|
|
|
|
|
|
// Reset iframe height, in case content has shrinked.
|
|
|
|
|
iframe.style.height = '1px';
|
|
|
|
|
|
|
|
|
|
// Resize iframe so all content is visible.
|
|
|
|
|
iframe.style.height = (iframe.contentDocument.body.scrollHeight) + 'px';
|
|
|
|
|
|
|
|
|
|
// Free parent
|
|
|
|
|
iframe.parentElement.style.height = parentHeight;
|
|
|
|
|
};
|
|
|
|
|
|
2015-02-20 10:15:36 +01:00
|
|
|
|
H5P.on(instance, 'resize', function () {
|
2015-02-09 10:22:14 +01:00
|
|
|
|
// Use a delay to make sure iframe is resized to the correct size.
|
|
|
|
|
clearTimeout(resizeDelay);
|
|
|
|
|
resizeDelay = setTimeout(function () {
|
|
|
|
|
resizeIframe();
|
|
|
|
|
}, 1);
|
|
|
|
|
});
|
2014-04-30 15:59:19 +02:00
|
|
|
|
}
|
2015-02-09 10:22:14 +01:00
|
|
|
|
else if (H5P.communicator) {
|
|
|
|
|
// External embed
|
|
|
|
|
var parentIsFriendly = false;
|
|
|
|
|
|
2015-03-02 15:53:29 +01:00
|
|
|
|
// Handle that the resizer is loaded after the iframe
|
|
|
|
|
H5P.communicator.on('ready', function () {
|
|
|
|
|
H5P.communicator.send('hello');
|
|
|
|
|
});
|
|
|
|
|
|
2015-02-09 10:22:14 +01:00
|
|
|
|
// Handle hello message from our parent window
|
|
|
|
|
H5P.communicator.on('hello', function () {
|
|
|
|
|
// Initial setup/handshake is done
|
|
|
|
|
parentIsFriendly = true;
|
|
|
|
|
|
2016-01-05 13:57:16 +01:00
|
|
|
|
// Make iframe responsive
|
|
|
|
|
document.body.style.height = 'auto';
|
|
|
|
|
|
2015-02-09 10:22:14 +01:00
|
|
|
|
// Hide scrollbars for correct size
|
|
|
|
|
document.body.style.overflow = 'hidden';
|
|
|
|
|
|
2015-03-02 15:53:29 +01:00
|
|
|
|
// Content need to be resized to fit the new iframe size
|
|
|
|
|
H5P.trigger(instance, 'resize');
|
2015-02-09 10:22:14 +01:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// When resize has been prepared tell parent window to resize
|
|
|
|
|
H5P.communicator.on('resizePrepared', function (data) {
|
|
|
|
|
H5P.communicator.send('resize', {
|
2016-01-05 13:57:16 +01:00
|
|
|
|
scrollHeight: document.body.scrollHeight
|
2015-02-09 10:22:14 +01:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
H5P.communicator.on('resize', function () {
|
2015-02-20 10:15:36 +01:00
|
|
|
|
H5P.trigger(instance, 'resize');
|
2015-02-09 10:22:14 +01:00
|
|
|
|
});
|
|
|
|
|
|
2015-02-20 10:15:36 +01:00
|
|
|
|
H5P.on(instance, 'resize', function () {
|
2015-02-09 10:22:14 +01:00
|
|
|
|
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) {
|
2016-01-05 13:57:16 +01:00
|
|
|
|
H5P.communicator.send('prepareResize', {
|
|
|
|
|
scrollHeight: document.body.scrollHeight,
|
|
|
|
|
clientHeight: document.body.clientHeight
|
|
|
|
|
});
|
2015-02-09 10:22:14 +01:00
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
H5P.communicator.send('hello');
|
|
|
|
|
}
|
|
|
|
|
}, 0);
|
|
|
|
|
});
|
2014-03-26 08:43:29 +01:00
|
|
|
|
}
|
2015-02-09 10:22:14 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!H5P.isFramed || H5P.externalEmbed === false) {
|
|
|
|
|
// Resize everything when window is resized.
|
2015-09-10 14:27:12 +02:00
|
|
|
|
H5P.jQuery(window.parent).resize(function () {
|
2014-06-24 15:31:02 +02:00
|
|
|
|
if (window.parent.H5P.isFullscreen) {
|
2015-02-09 10:22:14 +01:00
|
|
|
|
// Use timeout to avoid bug in certain browsers when exiting fullscreen. Some browser will trigger resize before the fullscreenchange event.
|
2015-03-02 15:53:29 +01:00
|
|
|
|
H5P.trigger(instance, 'resize');
|
2015-02-09 10:22:14 +01:00
|
|
|
|
}
|
|
|
|
|
else {
|
2015-02-20 10:15:36 +01:00
|
|
|
|
H5P.trigger(instance, 'resize');
|
2014-04-30 15:59:19 +02:00
|
|
|
|
}
|
|
|
|
|
});
|
2013-04-11 14:29:29 +02:00
|
|
|
|
}
|
2015-03-02 15:53:29 +01:00
|
|
|
|
|
2015-03-02 15:01:05 +01:00
|
|
|
|
H5P.instances.push(instance);
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-04-30 15:59:19 +02:00
|
|
|
|
// Resize content.
|
2015-02-04 18:43:06 +01:00
|
|
|
|
H5P.trigger(instance, 'resize');
|
2013-01-17 09:01:43 +01:00
|
|
|
|
});
|
2013-11-08 18:16:41 +01:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
// Insert H5Ps that should be in iframes.
|
2015-03-10 10:09:31 +01:00
|
|
|
|
H5P.jQuery('iframe.h5p-iframe:not(.h5p-initialized)', target).each(function () {
|
|
|
|
|
var contentId = H5P.jQuery(this).addClass('h5p-initialized').data('content-id');
|
2014-03-26 08:43:29 +01:00
|
|
|
|
this.contentDocument.open();
|
2015-02-09 10:22:14 +01:00
|
|
|
|
this.contentDocument.write('<!doctype html><html class="h5p-iframe"><head>' + H5P.getHeadTags(contentId) + '</head><body><div class="h5p-content" data-content-id="' + contentId + '"/></body></html>');
|
2014-03-26 08:43:29 +01:00
|
|
|
|
this.contentDocument.close();
|
|
|
|
|
});
|
2013-01-17 09:01:43 +01:00
|
|
|
|
};
|
|
|
|
|
|
2015-02-27 13:59:42 +01:00
|
|
|
|
/**
|
|
|
|
|
* Loop through assets for iframe content and create a set of tags for head.
|
|
|
|
|
*
|
|
|
|
|
* @private
|
|
|
|
|
* @param {number} contentId
|
|
|
|
|
* @returns {string} HTML
|
|
|
|
|
*/
|
|
|
|
|
H5P.getHeadTags = function (contentId) {
|
|
|
|
|
var createStyleTags = function (styles) {
|
|
|
|
|
var tags = '';
|
|
|
|
|
for (var i = 0; i < styles.length; i++) {
|
2015-03-23 09:45:02 +01:00
|
|
|
|
tags += '<link rel="stylesheet" href="' + styles[i] + '">';
|
2015-02-27 13:59:42 +01:00
|
|
|
|
}
|
|
|
|
|
return tags;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var createScriptTags = function (scripts) {
|
|
|
|
|
var tags = '';
|
|
|
|
|
for (var i = 0; i < scripts.length; i++) {
|
2015-03-23 09:45:02 +01:00
|
|
|
|
tags += '<script src="' + scripts[i] + '"></script>';
|
2015-02-27 13:59:42 +01:00
|
|
|
|
}
|
|
|
|
|
return tags;
|
|
|
|
|
};
|
|
|
|
|
|
2016-01-27 12:42:55 +01:00
|
|
|
|
return '<base target="_parent">' +
|
|
|
|
|
createStyleTags(H5PIntegration.core.styles) +
|
2015-02-27 13:59:42 +01:00
|
|
|
|
createStyleTags(H5PIntegration.contents['cid-' + contentId].styles) +
|
|
|
|
|
createScriptTags(H5PIntegration.core.scripts) +
|
|
|
|
|
createScriptTags(H5PIntegration.contents['cid-' + contentId].scripts) +
|
2015-09-10 14:27:12 +02:00
|
|
|
|
'<script>H5PIntegration = window.parent.H5PIntegration; var H5P = H5P || {}; H5P.externalEmbed = false;</script>';
|
2015-02-27 13:59:42 +01:00
|
|
|
|
};
|
|
|
|
|
|
2015-05-05 13:13:57 +02:00
|
|
|
|
/**
|
|
|
|
|
* When embedded the communicator helps talk to the parent page.
|
|
|
|
|
*
|
|
|
|
|
* @type {Communicator}
|
|
|
|
|
*/
|
2015-08-26 15:58:49 +02:00
|
|
|
|
H5P.communicator = (function () {
|
2015-02-09 10:22:14 +01:00
|
|
|
|
/**
|
|
|
|
|
* @class
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @private
|
2015-02-09 10:22:14 +01:00
|
|
|
|
*/
|
|
|
|
|
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.
|
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @param {string} action What you are waiting for
|
|
|
|
|
* @param {function} handler What you want done
|
2015-02-09 10:22:14 +01:00
|
|
|
|
*/
|
|
|
|
|
self.on = function (action, handler) {
|
|
|
|
|
actionHandlers[action] = handler;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Send a message to the all mighty father.
|
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @param {string} action
|
2015-02-09 10:22:14 +01:00
|
|
|
|
* @param {Object} [data] payload
|
|
|
|
|
*/
|
2015-08-26 15:58:49 +02:00
|
|
|
|
self.send = function (action, data) {
|
2015-02-09 10:22:14 +01:00
|
|
|
|
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);
|
|
|
|
|
})();
|
|
|
|
|
|
2016-11-11 11:45:59 +01:00
|
|
|
|
/**
|
|
|
|
|
* Enter semi fullscreen for the given H5P instance
|
|
|
|
|
*
|
|
|
|
|
* @param {H5P.jQuery} $element Content container.
|
|
|
|
|
* @param {Object} instance
|
|
|
|
|
* @param {function} exitCallback Callback function called when user exits fullscreen.
|
|
|
|
|
* @param {H5P.jQuery} $body For internal use. Gives the body of the iframe.
|
|
|
|
|
*/
|
|
|
|
|
H5P.semiFullScreen = function ($element, instance, exitCallback, body) {
|
|
|
|
|
H5P.fullScreen($element, instance, exitCallback, body, true);
|
|
|
|
|
};
|
|
|
|
|
|
2013-04-11 14:29:29 +02:00
|
|
|
|
/**
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* Enter fullscreen for the given H5P instance.
|
2013-04-26 17:27:35 +02:00
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @param {H5P.jQuery} $element Content container.
|
|
|
|
|
* @param {Object} instance
|
2013-11-26 14:54:29 +01:00
|
|
|
|
* @param {function} exitCallback Callback function called when user exits fullscreen.
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @param {H5P.jQuery} $body For internal use. Gives the body of the iframe.
|
2016-12-12 11:22:34 +01:00
|
|
|
|
* @param {Boolean} forceSemiFullScreen
|
2013-04-11 14:29:29 +02:00
|
|
|
|
*/
|
2016-11-11 11:45:59 +01:00
|
|
|
|
H5P.fullScreen = function ($element, instance, exitCallback, body, forceSemiFullScreen) {
|
2015-02-09 10:22:14 +01:00
|
|
|
|
if (H5P.exitFullScreen !== undefined) {
|
|
|
|
|
return; // Cannot enter new fullscreen until previous is over
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (H5P.isFramed && H5P.externalEmbed === false) {
|
2014-03-26 08:43:29 +01:00
|
|
|
|
// Trigger resize on wrapper in parent window.
|
2016-11-11 11:45:59 +01:00
|
|
|
|
window.parent.H5P.fullScreen($element, instance, exitCallback, H5P.$body.get(), forceSemiFullScreen);
|
2015-02-09 10:22:14 +01:00
|
|
|
|
H5P.isFullscreen = true;
|
|
|
|
|
H5P.exitFullScreen = function () {
|
2015-09-10 14:27:12 +02:00
|
|
|
|
window.parent.H5P.exitFullScreen();
|
2015-02-24 16:02:14 +01:00
|
|
|
|
};
|
|
|
|
|
H5P.on(instance, 'exitFullScreen', function () {
|
2015-02-09 10:22:14 +01:00
|
|
|
|
H5P.isFullscreen = false;
|
|
|
|
|
H5P.exitFullScreen = undefined;
|
2015-02-24 16:02:14 +01:00
|
|
|
|
});
|
2014-03-26 08:43:29 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
var $container = $element;
|
|
|
|
|
var $classes, $iframe;
|
|
|
|
|
if (body === undefined) {
|
2013-12-04 15:21:34 +01:00
|
|
|
|
$body = H5P.$body;
|
|
|
|
|
}
|
2014-03-26 08:43:29 +01:00
|
|
|
|
else {
|
|
|
|
|
// We're called from an iframe.
|
|
|
|
|
$body = H5P.jQuery(body);
|
|
|
|
|
$classes = $body.add($element.get());
|
|
|
|
|
var iframeSelector = '#h5p-iframe-' + $element.parent().data('content-id');
|
|
|
|
|
$iframe = H5P.jQuery(iframeSelector);
|
|
|
|
|
$element = $iframe.parent(); // Put iframe wrapper in fullscreen, not container.
|
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
$classes = $element.add(H5P.$body).add($classes);
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-05-22 11:17:30 +02:00
|
|
|
|
/**
|
|
|
|
|
* Prepare for resize by setting the correct styles.
|
2014-10-01 10:05:04 +02:00
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @private
|
|
|
|
|
* @param {string} classes CSS
|
2014-05-22 11:17:30 +02:00
|
|
|
|
*/
|
|
|
|
|
var before = function (classes) {
|
|
|
|
|
$classes.addClass(classes);
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-05-22 11:17:30 +02:00
|
|
|
|
if ($iframe !== undefined) {
|
|
|
|
|
// Set iframe to its default size(100%).
|
|
|
|
|
$iframe.css('height', '');
|
2014-03-26 08:43:29 +01:00
|
|
|
|
}
|
2014-05-22 11:17:30 +02:00
|
|
|
|
};
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-05-22 11:17:30 +02:00
|
|
|
|
/**
|
|
|
|
|
* Gets called when fullscreen mode has been entered.
|
|
|
|
|
* Resizes and sets focus on content.
|
2015-05-05 13:13:57 +02:00
|
|
|
|
*
|
|
|
|
|
* @private
|
2014-05-22 11:17:30 +02:00
|
|
|
|
*/
|
|
|
|
|
var entered = function () {
|
|
|
|
|
// Do not rely on window resize events.
|
2015-02-04 18:43:06 +01:00
|
|
|
|
H5P.trigger(instance, 'resize');
|
|
|
|
|
H5P.trigger(instance, 'focus');
|
2015-02-20 10:15:36 +01:00
|
|
|
|
H5P.trigger(instance, 'enterFullScreen');
|
2014-05-22 11:17:30 +02:00
|
|
|
|
};
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-05-22 11:17:30 +02:00
|
|
|
|
/**
|
|
|
|
|
* Gets called when fullscreen mode has been exited.
|
|
|
|
|
* Resizes and sets focus on content.
|
2014-10-01 10:05:04 +02:00
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @private
|
|
|
|
|
* @param {string} classes CSS
|
2014-05-22 11:17:30 +02:00
|
|
|
|
*/
|
|
|
|
|
var done = function (classes) {
|
|
|
|
|
H5P.isFullscreen = false;
|
|
|
|
|
$classes.removeClass(classes);
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-05-22 11:17:30 +02:00
|
|
|
|
// Do not rely on window resize events.
|
2015-02-04 18:43:06 +01:00
|
|
|
|
H5P.trigger(instance, 'resize');
|
|
|
|
|
H5P.trigger(instance, 'focus');
|
2013-11-08 18:16:41 +01:00
|
|
|
|
|
2015-02-09 10:22:14 +01:00
|
|
|
|
H5P.exitFullScreen = undefined;
|
2014-03-26 08:43:29 +01:00
|
|
|
|
if (exitCallback !== undefined) {
|
|
|
|
|
exitCallback();
|
|
|
|
|
}
|
2015-02-09 11:47:33 +01:00
|
|
|
|
|
2015-02-20 10:15:36 +01:00
|
|
|
|
H5P.trigger(instance, 'exitFullScreen');
|
2014-03-26 08:43:29 +01:00
|
|
|
|
};
|
2013-11-08 18:16:41 +01:00
|
|
|
|
|
2014-04-30 15:59:19 +02:00
|
|
|
|
H5P.isFullscreen = true;
|
2016-11-11 11:45:59 +01:00
|
|
|
|
if (H5P.fullScreenBrowserPrefix === undefined || forceSemiFullScreen === true) {
|
2013-04-11 14:29:29 +02:00
|
|
|
|
// Create semi fullscreen.
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2015-02-09 10:22:14 +01:00
|
|
|
|
if (H5P.isFramed) {
|
2015-02-09 11:51:39 +01:00
|
|
|
|
return; // TODO: Should we support semi-fullscreen for IE9 & 10 ?
|
2015-02-09 10:22:14 +01:00
|
|
|
|
}
|
|
|
|
|
|
2014-05-22 11:17:30 +02:00
|
|
|
|
before('h5p-semi-fullscreen');
|
2016-03-17 14:22:23 +01:00
|
|
|
|
var $disable = H5P.jQuery('<div role="button" tabindex="0" class="h5p-disable-fullscreen" title="' + H5P.t('disableFullscreen') + '"></div>').appendTo($container.find('.h5p-content-controls'));
|
2015-10-27 16:57:28 +01:00
|
|
|
|
var keyup, disableSemiFullscreen = H5P.exitFullScreen = function () {
|
2015-11-13 11:18:53 +01:00
|
|
|
|
if (prevViewportContent) {
|
|
|
|
|
// Use content from the previous viewport tag
|
|
|
|
|
h5pViewport.content = prevViewportContent;
|
2015-10-27 16:57:28 +01:00
|
|
|
|
}
|
|
|
|
|
else {
|
2015-11-13 11:18:53 +01:00
|
|
|
|
// Remove viewport tag
|
|
|
|
|
head.removeChild(h5pViewport);
|
2015-10-27 16:57:28 +01:00
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
$disable.remove();
|
2013-12-04 15:21:34 +01:00
|
|
|
|
$body.unbind('keyup', keyup);
|
2014-03-26 08:43:29 +01:00
|
|
|
|
done('h5p-semi-fullscreen');
|
2013-04-11 14:29:29 +02:00
|
|
|
|
};
|
|
|
|
|
keyup = function (event) {
|
|
|
|
|
if (event.keyCode === 27) {
|
|
|
|
|
disableSemiFullscreen();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
$disable.click(disableSemiFullscreen);
|
2013-12-04 15:21:34 +01:00
|
|
|
|
$body.keyup(keyup);
|
2015-10-27 16:57:28 +01:00
|
|
|
|
|
|
|
|
|
// Disable zoom
|
2015-11-13 11:18:53 +01:00
|
|
|
|
var prevViewportContent, h5pViewport;
|
2015-10-27 16:57:28 +01:00
|
|
|
|
var metaTags = document.getElementsByTagName('meta');
|
|
|
|
|
for (var i = 0; i < metaTags.length; i++) {
|
|
|
|
|
if (metaTags[i].name === 'viewport') {
|
2015-11-13 11:18:53 +01:00
|
|
|
|
// Use the existing viewport tag
|
|
|
|
|
h5pViewport = metaTags[i];
|
|
|
|
|
prevViewportContent = h5pViewport.content;
|
2015-10-27 16:57:28 +01:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-11-13 11:18:53 +01:00
|
|
|
|
if (!prevViewportContent) {
|
|
|
|
|
// Create a new viewport tag
|
|
|
|
|
h5pViewport = document.createElement('meta');
|
|
|
|
|
h5pViewport.name = 'viewport';
|
2015-10-27 16:57:28 +01:00
|
|
|
|
}
|
2015-11-13 11:18:53 +01:00
|
|
|
|
h5pViewport.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0';
|
|
|
|
|
if (!prevViewportContent) {
|
|
|
|
|
// Insert the new viewport tag
|
2015-10-27 16:57:28 +01:00
|
|
|
|
var head = document.getElementsByTagName('head')[0];
|
2015-11-13 11:18:53 +01:00
|
|
|
|
head.appendChild(h5pViewport);
|
2015-10-27 16:57:28 +01:00
|
|
|
|
}
|
|
|
|
|
|
2014-05-22 11:17:30 +02:00
|
|
|
|
entered();
|
2013-04-10 17:08:57 +02:00
|
|
|
|
}
|
2013-04-11 14:29:29 +02:00
|
|
|
|
else {
|
2014-03-26 08:43:29 +01:00
|
|
|
|
// Create real fullscreen.
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-05-22 11:17:30 +02:00
|
|
|
|
before('h5p-fullscreen');
|
2014-01-29 16:03:51 +01:00
|
|
|
|
var first, eventName = (H5P.fullScreenBrowserPrefix === 'ms' ? 'MSFullscreenChange' : H5P.fullScreenBrowserPrefix + 'fullscreenchange');
|
2013-04-11 14:29:29 +02:00
|
|
|
|
document.addEventListener(eventName, function () {
|
|
|
|
|
if (first === undefined) {
|
2014-05-22 11:17:30 +02:00
|
|
|
|
// We are entering fullscreen mode
|
2013-04-11 14:29:29 +02:00
|
|
|
|
first = false;
|
2014-05-22 11:17:30 +02:00
|
|
|
|
entered();
|
2013-04-11 14:29:29 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-05-22 11:17:30 +02:00
|
|
|
|
// We are exiting fullscreen
|
2014-03-26 08:43:29 +01:00
|
|
|
|
done('h5p-fullscreen');
|
2013-04-11 14:29:29 +02:00
|
|
|
|
document.removeEventListener(eventName, arguments.callee, false);
|
|
|
|
|
});
|
2013-04-26 17:27:35 +02:00
|
|
|
|
|
2013-04-11 14:29:29 +02:00
|
|
|
|
if (H5P.fullScreenBrowserPrefix === '') {
|
2014-03-26 08:43:29 +01:00
|
|
|
|
$element[0].requestFullScreen();
|
2013-04-11 14:29:29 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
2014-01-29 16:03:51 +01:00
|
|
|
|
var method = (H5P.fullScreenBrowserPrefix === 'ms' ? 'msRequestFullscreen' : H5P.fullScreenBrowserPrefix + 'RequestFullScreen');
|
2014-05-22 11:17:30 +02:00
|
|
|
|
var params = (H5P.fullScreenBrowserPrefix === 'webkit' && H5P.safariBrowser === 0 ? Element.ALLOW_KEYBOARD_INPUT : undefined);
|
2014-03-26 08:43:29 +01:00
|
|
|
|
$element[0][method](params);
|
2013-04-11 14:29:29 +02:00
|
|
|
|
}
|
2015-02-09 10:22:14 +01:00
|
|
|
|
|
|
|
|
|
// Allows everone to exit
|
|
|
|
|
H5P.exitFullScreen = function () {
|
|
|
|
|
if (H5P.fullScreenBrowserPrefix === '') {
|
|
|
|
|
document.exitFullscreen();
|
|
|
|
|
}
|
|
|
|
|
else if (H5P.fullScreenBrowserPrefix === 'moz') {
|
|
|
|
|
document.mozCancelFullScreen();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
document[H5P.fullScreenBrowserPrefix + 'ExitFullscreen']();
|
|
|
|
|
}
|
|
|
|
|
};
|
2013-04-10 17:08:57 +02:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2013-06-20 14:49:56 +02:00
|
|
|
|
/**
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* Find the path to the content files based on the id of the content.
|
|
|
|
|
* Also identifies and returns absolute paths.
|
2013-07-03 14:22:00 +02:00
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @param {string} path
|
|
|
|
|
* Relative to content folder or absolute.
|
|
|
|
|
* @param {number} contentId
|
|
|
|
|
* ID of the content requesting the path.
|
|
|
|
|
* @returns {string}
|
|
|
|
|
* Complete URL to path.
|
2013-06-20 14:49:56 +02:00
|
|
|
|
*/
|
2013-06-27 14:29:56 +02:00
|
|
|
|
H5P.getPath = function (path, contentId) {
|
2014-06-24 14:41:35 +02:00
|
|
|
|
var hasProtocol = function (path) {
|
|
|
|
|
return path.match(/^[a-z0-9]+:\/\//i);
|
|
|
|
|
};
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-06-24 14:41:35 +02:00
|
|
|
|
if (hasProtocol(path)) {
|
2013-06-27 14:29:56 +02:00
|
|
|
|
return path;
|
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2015-06-23 14:24:19 +02:00
|
|
|
|
var prefix;
|
2017-07-07 10:51:57 +02:00
|
|
|
|
var isTmpFile = (path.substr(-4,4) === '#tmp');
|
|
|
|
|
if (contentId !== undefined && !isTmpFile) {
|
2016-09-08 16:34:53 +02:00
|
|
|
|
// Check for custom override URL
|
2017-02-06 14:39:59 +01:00
|
|
|
|
if (H5PIntegration.contents !== undefined &&
|
|
|
|
|
H5PIntegration.contents['cid-' + contentId]) {
|
2016-09-09 09:19:55 +02:00
|
|
|
|
prefix = H5PIntegration.contents['cid-' + contentId].contentUrl;
|
|
|
|
|
}
|
2016-09-08 16:34:53 +02:00
|
|
|
|
if (!prefix) {
|
|
|
|
|
prefix = H5PIntegration.url + '/content/' + contentId;
|
|
|
|
|
}
|
2014-06-19 16:09:23 +02:00
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
else if (window.H5PEditor !== undefined) {
|
2014-06-19 16:09:23 +02:00
|
|
|
|
prefix = H5PEditor.filesPath;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-06-24 14:41:35 +02:00
|
|
|
|
if (!hasProtocol(prefix)) {
|
2015-02-27 10:55:47 +01:00
|
|
|
|
// Use absolute urls
|
|
|
|
|
prefix = window.location.protocol + "//" + window.location.host + prefix;
|
2014-06-24 14:41:35 +02:00
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
|
|
|
|
return prefix + '/' + path;
|
2013-02-07 17:50:17 +01:00
|
|
|
|
};
|
2013-01-17 22:20:41 +01:00
|
|
|
|
|
2013-07-03 14:22:00 +02:00
|
|
|
|
/**
|
2013-07-03 14:24:09 +02:00
|
|
|
|
* THIS FUNCTION IS DEPRECATED, USE getPath INSTEAD
|
2015-03-23 10:53:21 +01:00
|
|
|
|
* Will be remove march 2016.
|
2013-07-03 14:24:09 +02:00
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* Find the path to the content files folder based on the id of the content
|
2013-07-03 14:22:00 +02:00
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @deprecated
|
|
|
|
|
* Will be removed march 2016.
|
|
|
|
|
* @param contentId
|
|
|
|
|
* Id of the content requesting the path
|
|
|
|
|
* @returns {string}
|
|
|
|
|
* URL
|
2013-07-03 14:22:00 +02:00
|
|
|
|
*/
|
|
|
|
|
H5P.getContentPath = function (contentId) {
|
2015-03-23 10:53:21 +01:00
|
|
|
|
return H5PIntegration.url + '/content/' + contentId;
|
2013-07-03 14:22:00 +02:00
|
|
|
|
};
|
|
|
|
|
|
2013-11-26 14:54:29 +01:00
|
|
|
|
/**
|
|
|
|
|
* Get library class constructor from H5P by classname.
|
2014-10-01 10:05:04 +02:00
|
|
|
|
* Note that this class will only work for resolve "H5P.NameWithoutDot".
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* Also check out {@link H5P.newRunnable}
|
2013-11-26 14:54:29 +01:00
|
|
|
|
*
|
|
|
|
|
* Used from libraries to construct instances of other libraries' objects by name.
|
|
|
|
|
*
|
|
|
|
|
* @param {string} name Name of library
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @returns {Object} Class constructor
|
2013-11-26 14:54:29 +01:00
|
|
|
|
*/
|
|
|
|
|
H5P.classFromName = function (name) {
|
2013-01-17 09:01:43 +01:00
|
|
|
|
var arr = name.split(".");
|
2015-05-05 13:13:57 +02:00
|
|
|
|
return this[arr[arr.length - 1]];
|
2013-01-17 09:01:43 +01:00
|
|
|
|
};
|
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
/**
|
|
|
|
|
* A safe way of creating a new instance of a runnable H5P.
|
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @param {Object} library
|
|
|
|
|
* Library/action object form params.
|
|
|
|
|
* @param {number} contentId
|
|
|
|
|
* Identifies the content.
|
|
|
|
|
* @param {H5P.jQuery} [$attachTo]
|
|
|
|
|
* Element to attach the instance to.
|
|
|
|
|
* @param {boolean} [skipResize]
|
|
|
|
|
* Skip triggering of the resize event after attaching.
|
|
|
|
|
* @param {Object} [extras]
|
|
|
|
|
* Extra parameters for the H5P content constructor
|
|
|
|
|
* @returns {Object}
|
|
|
|
|
* Instance.
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*/
|
2015-03-22 20:39:16 +01:00
|
|
|
|
H5P.newRunnable = function (library, contentId, $attachTo, skipResize, extras) {
|
2015-04-07 19:32:44 +02:00
|
|
|
|
var nameSplit, versionSplit, machineName;
|
2014-03-26 08:43:29 +01:00
|
|
|
|
try {
|
2014-10-01 10:05:04 +02:00
|
|
|
|
nameSplit = library.library.split(' ', 2);
|
2015-04-07 19:32:44 +02:00
|
|
|
|
machineName = nameSplit[0];
|
2014-10-01 10:05:04 +02:00
|
|
|
|
versionSplit = nameSplit[1].split('.', 2);
|
2014-03-26 08:43:29 +01:00
|
|
|
|
}
|
|
|
|
|
catch (err) {
|
|
|
|
|
return H5P.error('Invalid library string: ' + library.library);
|
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
if ((library.params instanceof Object) !== true || (library.params instanceof Array) === true) {
|
|
|
|
|
H5P.error('Invalid library params for: ' + library.library);
|
|
|
|
|
return H5P.error(library.params);
|
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
// Find constructor function
|
2014-10-01 10:05:04 +02:00
|
|
|
|
var constructor;
|
2014-03-26 08:43:29 +01:00
|
|
|
|
try {
|
|
|
|
|
nameSplit = nameSplit[0].split('.');
|
2014-10-01 10:05:04 +02:00
|
|
|
|
constructor = window;
|
|
|
|
|
for (var i = 0; i < nameSplit.length; i++) {
|
2014-03-26 08:43:29 +01:00
|
|
|
|
constructor = constructor[nameSplit[i]];
|
2014-10-01 10:05:04 +02:00
|
|
|
|
}
|
2014-03-26 08:43:29 +01:00
|
|
|
|
if (typeof constructor !== 'function') {
|
|
|
|
|
throw null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (err) {
|
|
|
|
|
return H5P.error('Unable to find constructor for: ' + library.library);
|
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2015-03-22 20:39:16 +01:00
|
|
|
|
if (extras === undefined) {
|
|
|
|
|
extras = {};
|
|
|
|
|
}
|
2015-04-07 19:32:44 +02:00
|
|
|
|
if (library.subContentId) {
|
|
|
|
|
extras.subContentId = library.subContentId;
|
2015-03-21 16:45:38 +01:00
|
|
|
|
}
|
2015-04-07 19:32:44 +02:00
|
|
|
|
|
2015-04-13 20:31:21 +02:00
|
|
|
|
if (library.userDatas && library.userDatas.state && H5PIntegration.saveFreq) {
|
2015-04-07 19:32:44 +02:00
|
|
|
|
extras.previousState = library.userDatas.state;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-17 13:32:35 +02:00
|
|
|
|
// Makes all H5P libraries extend H5P.ContentType:
|
2015-07-14 14:37:39 +02:00
|
|
|
|
var standalone = extras.standalone || false;
|
2015-07-17 13:32:35 +02:00
|
|
|
|
// This order makes it possible for an H5P library to override H5P.ContentType functions!
|
2015-07-20 13:40:59 +02:00
|
|
|
|
constructor.prototype = H5P.jQuery.extend({}, H5P.ContentType(standalone).prototype, constructor.prototype);
|
2015-07-14 14:37:39 +02:00
|
|
|
|
|
2015-04-07 19:32:44 +02:00
|
|
|
|
var instance;
|
|
|
|
|
// Some old library versions have their own custom third parameter.
|
|
|
|
|
// Make sure we don't send them the extras.
|
|
|
|
|
// (they will interpret it as something else)
|
2015-03-22 20:39:16 +01:00
|
|
|
|
if (H5P.jQuery.inArray(library.library, ['H5P.CoursePresentation 1.0', 'H5P.CoursePresentation 1.1', 'H5P.CoursePresentation 1.2', 'H5P.CoursePresentation 1.3']) > -1) {
|
2015-04-07 19:32:44 +02:00
|
|
|
|
instance = new constructor(library.params, contentId);
|
2015-03-22 20:39:16 +01:00
|
|
|
|
}
|
|
|
|
|
else {
|
2015-04-07 19:32:44 +02:00
|
|
|
|
instance = new constructor(library.params, contentId, extras);
|
2015-03-21 16:45:38 +01:00
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-04-30 15:59:19 +02:00
|
|
|
|
if (instance.$ === undefined) {
|
|
|
|
|
instance.$ = H5P.jQuery(instance);
|
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2015-02-18 09:07:57 +01:00
|
|
|
|
if (instance.contentId === undefined) {
|
2015-02-18 15:20:20 +01:00
|
|
|
|
instance.contentId = contentId;
|
2015-02-18 09:07:57 +01:00
|
|
|
|
}
|
2015-04-07 19:32:44 +02:00
|
|
|
|
if (instance.subContentId === undefined && library.subContentId) {
|
|
|
|
|
instance.subContentId = library.subContentId;
|
2015-03-21 16:45:38 +01:00
|
|
|
|
}
|
2015-03-22 20:39:16 +01:00
|
|
|
|
if (instance.parent === undefined && extras && extras.parent) {
|
|
|
|
|
instance.parent = extras.parent;
|
2015-03-21 16:45:38 +01:00
|
|
|
|
}
|
2015-07-10 12:58:02 +02:00
|
|
|
|
if (instance.libraryInfo === undefined) {
|
|
|
|
|
instance.libraryInfo = {
|
|
|
|
|
versionedName: library.library,
|
|
|
|
|
versionedNameNoSpaces: machineName + '-' + versionSplit[0] + '.' + versionSplit[1],
|
|
|
|
|
machineName: machineName,
|
|
|
|
|
majorVersion: versionSplit[0],
|
|
|
|
|
minorVersion: versionSplit[1]
|
|
|
|
|
};
|
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
if ($attachTo !== undefined) {
|
2015-07-14 14:37:39 +02:00
|
|
|
|
$attachTo.toggleClass('h5p-standalone', standalone);
|
2014-03-26 08:43:29 +01:00
|
|
|
|
instance.attach($attachTo);
|
2015-04-07 19:32:44 +02:00
|
|
|
|
H5P.trigger(instance, 'domChanged', {
|
|
|
|
|
'$target': $attachTo,
|
|
|
|
|
'library': machineName,
|
|
|
|
|
'key': 'newLibrary'
|
|
|
|
|
}, {'bubbles': true, 'external': true});
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-04-30 15:59:19 +02:00
|
|
|
|
if (skipResize === undefined || !skipResize) {
|
2014-03-26 08:43:29 +01:00
|
|
|
|
// Resize content.
|
2015-02-04 18:43:06 +01:00
|
|
|
|
H5P.trigger(instance, 'resize');
|
2014-03-26 08:43:29 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return instance;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* Used to print useful error messages. (to JavaScript error console)
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @param {*} err Error to print.
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*/
|
|
|
|
|
H5P.error = function (err) {
|
2015-02-20 10:26:33 +01:00
|
|
|
|
if (window.console !== undefined && console.error !== undefined) {
|
2015-05-15 12:21:23 +02:00
|
|
|
|
console.error(err.stack ? err.stack : err);
|
2014-03-26 08:43:29 +01:00
|
|
|
|
}
|
2014-05-02 15:45:45 +02:00
|
|
|
|
};
|
2014-03-26 08:43:29 +01:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Translate text strings.
|
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @param {string} key
|
|
|
|
|
* Translation identifier, may only contain a-zA-Z0-9. No spaces or special chars.
|
2016-04-07 14:51:56 +02:00
|
|
|
|
* @param {Object} [vars]
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* Data for placeholders.
|
2016-04-07 14:51:56 +02:00
|
|
|
|
* @param {string} [ns]
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* Translation namespace. Defaults to H5P.
|
|
|
|
|
* @returns {string}
|
|
|
|
|
* Translated text
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*/
|
|
|
|
|
H5P.t = function (key, vars, ns) {
|
|
|
|
|
if (ns === undefined) {
|
|
|
|
|
ns = 'H5P';
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-27 13:59:42 +01:00
|
|
|
|
if (H5PIntegration.l10n[ns] === undefined) {
|
2014-03-26 08:43:29 +01:00
|
|
|
|
return '[Missing translation namespace "' + ns + '"]';
|
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2015-02-27 13:59:42 +01:00
|
|
|
|
if (H5PIntegration.l10n[ns][key] === undefined) {
|
2014-03-26 08:43:29 +01:00
|
|
|
|
return '[Missing translation "' + key + '" in "' + ns + '"]';
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-27 13:59:42 +01:00
|
|
|
|
var translation = H5PIntegration.l10n[ns][key];
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
if (vars !== undefined) {
|
|
|
|
|
// Replace placeholder with variables.
|
|
|
|
|
for (var placeholder in vars) {
|
|
|
|
|
translation = translation.replace(placeholder, vars[placeholder]);
|
2014-10-01 10:05:04 +02:00
|
|
|
|
}
|
2014-03-26 08:43:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return translation;
|
|
|
|
|
};
|
|
|
|
|
|
2015-05-05 13:13:57 +02:00
|
|
|
|
/**
|
|
|
|
|
* Creates a new popup dialog over the H5P content.
|
|
|
|
|
*
|
|
|
|
|
* @class
|
|
|
|
|
* @param {string} name
|
|
|
|
|
* Used for html class.
|
|
|
|
|
* @param {string} title
|
|
|
|
|
* Used for header.
|
|
|
|
|
* @param {string} content
|
|
|
|
|
* Displayed inside the dialog.
|
|
|
|
|
* @param {H5P.jQuery} $element
|
|
|
|
|
* Which DOM element the dialog should be inserted after.
|
|
|
|
|
*/
|
2014-03-26 08:43:29 +01:00
|
|
|
|
H5P.Dialog = function (name, title, content, $element) {
|
2017-02-07 13:48:42 +01:00
|
|
|
|
/** @alias H5P.Dialog# */
|
2014-03-26 08:43:29 +01:00
|
|
|
|
var self = this;
|
|
|
|
|
var $dialog = H5P.jQuery('<div class="h5p-popup-dialog h5p-' + name + '-dialog">\
|
|
|
|
|
<div class="h5p-inner">\
|
|
|
|
|
<h2>' + title + '</h2>\
|
|
|
|
|
<div class="h5p-scroll-content">' + content + '</div>\
|
2016-03-17 14:22:23 +01:00
|
|
|
|
<div class="h5p-close" role="button" tabindex="0" title="' + H5P.t('close') + '">\
|
2014-03-26 08:43:29 +01:00
|
|
|
|
</div>\
|
|
|
|
|
</div>')
|
|
|
|
|
.insertAfter($element)
|
|
|
|
|
.click(function () {
|
|
|
|
|
self.close();
|
|
|
|
|
})
|
|
|
|
|
.children('.h5p-inner')
|
|
|
|
|
.click(function () {
|
|
|
|
|
return false;
|
|
|
|
|
})
|
|
|
|
|
.find('.h5p-close')
|
|
|
|
|
.click(function () {
|
|
|
|
|
self.close();
|
|
|
|
|
})
|
|
|
|
|
.end()
|
2016-03-31 16:07:45 +02:00
|
|
|
|
.find('a')
|
|
|
|
|
.click(function (e) {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
})
|
|
|
|
|
.end()
|
|
|
|
|
.end();
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2017-02-07 13:48:42 +01:00
|
|
|
|
/**
|
|
|
|
|
* Opens the dialog.
|
|
|
|
|
*/
|
|
|
|
|
self.open = function () {
|
2014-03-26 08:43:29 +01:00
|
|
|
|
setTimeout(function () {
|
|
|
|
|
$dialog.addClass('h5p-open'); // Fade in
|
|
|
|
|
// Triggering an event, in case something has to be done after dialog has been opened.
|
|
|
|
|
H5P.jQuery(self).trigger('dialog-opened', [$dialog]);
|
|
|
|
|
}, 1);
|
|
|
|
|
};
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2017-02-07 13:48:42 +01:00
|
|
|
|
/**
|
|
|
|
|
* Closes the dialog.
|
|
|
|
|
*/
|
|
|
|
|
self.close = function () {
|
2014-03-26 08:43:29 +01:00
|
|
|
|
$dialog.removeClass('h5p-open'); // Fade out
|
|
|
|
|
setTimeout(function () {
|
|
|
|
|
$dialog.remove();
|
|
|
|
|
}, 200);
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
2015-05-11 16:00:55 +02:00
|
|
|
|
* Gather copyright information for the given content.
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @param {Object} instance
|
|
|
|
|
* H5P instance to get copyright information for.
|
|
|
|
|
* @param {Object} parameters
|
|
|
|
|
* Parameters of the content instance.
|
|
|
|
|
* @param {number} contentId
|
|
|
|
|
* Identifies the H5P content
|
2015-05-11 16:00:55 +02:00
|
|
|
|
* @returns {string} Copyright information.
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*/
|
2015-08-26 15:58:49 +02:00
|
|
|
|
H5P.getCopyrights = function (instance, parameters, contentId) {
|
2015-05-12 10:04:21 +02:00
|
|
|
|
var copyrights;
|
|
|
|
|
|
2014-10-30 15:51:20 +01:00
|
|
|
|
if (instance.getCopyrights !== undefined) {
|
2015-05-15 12:21:23 +02:00
|
|
|
|
try {
|
|
|
|
|
// Use the instance's own copyright generator
|
|
|
|
|
copyrights = instance.getCopyrights();
|
|
|
|
|
}
|
|
|
|
|
catch (err) {
|
|
|
|
|
// Failed, prevent crashing page.
|
|
|
|
|
}
|
2014-10-30 15:51:20 +01:00
|
|
|
|
}
|
2015-05-15 12:21:23 +02:00
|
|
|
|
|
|
|
|
|
if (copyrights === undefined) {
|
2014-10-30 15:51:20 +01:00
|
|
|
|
// Create a generic flat copyright list
|
|
|
|
|
copyrights = new H5P.ContentCopyrights();
|
|
|
|
|
H5P.findCopyrights(copyrights, parameters, contentId);
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
if (copyrights !== undefined) {
|
2014-10-30 15:51:20 +01:00
|
|
|
|
// Convert to string
|
2014-03-26 08:43:29 +01:00
|
|
|
|
copyrights = copyrights.toString();
|
|
|
|
|
}
|
2015-05-11 16:00:55 +02:00
|
|
|
|
return copyrights;
|
2014-03-26 08:43:29 +01:00
|
|
|
|
};
|
|
|
|
|
|
2014-10-30 15:51:20 +01:00
|
|
|
|
/**
|
|
|
|
|
* Gather a flat list of copyright information from the given parameters.
|
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @param {H5P.ContentCopyrights} info
|
|
|
|
|
* Used to collect all information in.
|
|
|
|
|
* @param {(Object|Array)} parameters
|
|
|
|
|
* To search for file objects in.
|
|
|
|
|
* @param {number} contentId
|
|
|
|
|
* Used to insert thumbnails for images.
|
2014-10-30 15:51:20 +01:00
|
|
|
|
*/
|
|
|
|
|
H5P.findCopyrights = function (info, parameters, contentId) {
|
|
|
|
|
// Cycle through parameters
|
|
|
|
|
for (var field in parameters) {
|
|
|
|
|
if (!parameters.hasOwnProperty(field)) {
|
|
|
|
|
continue; // Do not check
|
|
|
|
|
}
|
2016-12-21 11:34:51 +01:00
|
|
|
|
|
2017-05-08 14:17:29 +02:00
|
|
|
|
/**
|
|
|
|
|
* @deprecated This hack should be removed after 2017-11-01
|
|
|
|
|
* The code that was using this was removed by HFP-574
|
2016-12-21 11:34:51 +01:00
|
|
|
|
*/
|
|
|
|
|
if (field === 'overrideSettings') {
|
2017-05-08 14:17:29 +02:00
|
|
|
|
console.warn("The semantics field 'overrideSettings' is DEPRECATED and should not be used.");
|
|
|
|
|
console.warn(parameters);
|
2016-12-21 11:34:51 +01:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-30 15:51:20 +01:00
|
|
|
|
var value = parameters[field];
|
|
|
|
|
|
|
|
|
|
if (value instanceof Array) {
|
|
|
|
|
// Cycle through array
|
|
|
|
|
H5P.findCopyrights(info, value, contentId);
|
|
|
|
|
}
|
|
|
|
|
else if (value instanceof Object) {
|
|
|
|
|
// Check if object is a file with copyrights
|
|
|
|
|
if (value.copyright === undefined ||
|
|
|
|
|
value.copyright.license === undefined ||
|
|
|
|
|
value.path === undefined ||
|
|
|
|
|
value.mime === undefined) {
|
|
|
|
|
|
|
|
|
|
// Nope, cycle throught object
|
|
|
|
|
H5P.findCopyrights(info, value, contentId);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// Found file, add copyrights
|
|
|
|
|
var copyrights = new H5P.MediaCopyright(value.copyright);
|
|
|
|
|
if (value.width !== undefined && value.height !== undefined) {
|
|
|
|
|
copyrights.setThumbnail(new H5P.Thumbnail(H5P.getPath(value.path, contentId), value.width, value.height));
|
|
|
|
|
}
|
|
|
|
|
info.addMedia(copyrights);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
/**
|
|
|
|
|
* Display a dialog containing the embed code.
|
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @param {H5P.jQuery} $element
|
|
|
|
|
* Element to insert dialog after.
|
|
|
|
|
* @param {string} embedCode
|
|
|
|
|
* The embed code.
|
|
|
|
|
* @param {string} resizeCode
|
|
|
|
|
* The advanced resize code
|
|
|
|
|
* @param {Object} size
|
|
|
|
|
* The content's size.
|
|
|
|
|
* @param {number} size.width
|
|
|
|
|
* @param {number} size.height
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*/
|
2015-02-25 12:10:07 +01:00
|
|
|
|
H5P.openEmbedDialog = function ($element, embedCode, resizeCode, size) {
|
2015-03-02 15:01:05 +01:00
|
|
|
|
var fullEmbedCode = embedCode + resizeCode;
|
2015-06-25 15:14:55 +02:00
|
|
|
|
var dialog = new H5P.Dialog('embed', H5P.t('embed'), '<textarea class="h5p-embed-code-container" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>' + H5P.t('size') + ': <input type="text" value="' + Math.ceil(size.width) + '" class="h5p-embed-size"/> × <input type="text" value="' + Math.ceil(size.height) + '" class="h5p-embed-size"/> px<br/><div role="button" tabindex="0" class="h5p-expander">' + H5P.t('showAdvanced') + '</div><div class="h5p-expander-content"><p>' + H5P.t('advancedHelp') + '</p><textarea class="h5p-embed-code-container" autocorrect="off" autocapitalize="off" spellcheck="false">' + resizeCode + '</textarea></div>', $element);
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
// Selecting embed code when dialog is opened
|
|
|
|
|
H5P.jQuery(dialog).on('dialog-opened', function (event, $dialog) {
|
2015-02-25 13:05:45 +01:00
|
|
|
|
var $inner = $dialog.find('.h5p-inner');
|
2015-02-27 10:26:57 +01:00
|
|
|
|
var $scroll = $inner.find('.h5p-scroll-content');
|
|
|
|
|
var diff = $scroll.outerHeight() - $scroll.innerHeight();
|
2015-02-25 13:05:45 +01:00
|
|
|
|
var positionInner = function () {
|
2015-02-27 10:26:57 +01:00
|
|
|
|
var height = $inner.height();
|
|
|
|
|
if ($scroll[0].scrollHeight + diff > height) {
|
|
|
|
|
$inner.css('height', ''); // 100%
|
2015-02-25 13:05:45 +01:00
|
|
|
|
}
|
2015-02-27 10:26:57 +01:00
|
|
|
|
else {
|
|
|
|
|
$inner.css('height', 'auto');
|
|
|
|
|
height = $inner.height();
|
|
|
|
|
}
|
|
|
|
|
$inner.css('marginTop', '-' + (height / 2) + 'px');
|
2015-02-25 13:05:45 +01:00
|
|
|
|
};
|
|
|
|
|
|
2015-02-25 12:10:07 +01:00
|
|
|
|
// Handle changing of width/height
|
|
|
|
|
var $w = $dialog.find('.h5p-embed-size:eq(0)');
|
|
|
|
|
var $h = $dialog.find('.h5p-embed-size:eq(1)');
|
|
|
|
|
var getNum = function ($e, d) {
|
|
|
|
|
var num = parseFloat($e.val());
|
|
|
|
|
if (isNaN(num)) {
|
|
|
|
|
return d;
|
|
|
|
|
}
|
|
|
|
|
return Math.ceil(num);
|
|
|
|
|
};
|
2015-08-26 15:58:49 +02:00
|
|
|
|
var updateEmbed = function () {
|
2015-03-02 15:01:05 +01:00
|
|
|
|
$dialog.find('.h5p-embed-code-container:first').val(fullEmbedCode.replace(':w', getNum($w, size.width)).replace(':h', getNum($h, size.height)));
|
2015-02-25 12:10:07 +01:00
|
|
|
|
};
|
|
|
|
|
|
2015-03-02 15:01:05 +01:00
|
|
|
|
$w.change(updateEmbed);
|
2015-02-25 12:10:07 +01:00
|
|
|
|
$h.change(updateEmbed);
|
|
|
|
|
updateEmbed();
|
|
|
|
|
|
|
|
|
|
// Select text and expand textareas
|
2015-03-02 15:01:05 +01:00
|
|
|
|
$dialog.find('.h5p-embed-code-container').each(function(index, value) {
|
|
|
|
|
H5P.jQuery(this).css('height', this.scrollHeight + 'px').focus(function() {
|
|
|
|
|
H5P.jQuery(this).select();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
$dialog.find('.h5p-embed-code-container').eq(0).select();
|
|
|
|
|
positionInner();
|
2015-02-25 12:10:07 +01:00
|
|
|
|
|
|
|
|
|
// Expand advanced embed
|
2015-08-26 15:58:49 +02:00
|
|
|
|
var expand = function () {
|
2015-02-25 12:10:07 +01:00
|
|
|
|
var $expander = H5P.jQuery(this);
|
|
|
|
|
var $content = $expander.next();
|
|
|
|
|
if ($content.is(':visible')) {
|
|
|
|
|
$expander.removeClass('h5p-open').text(H5P.t('showAdvanced'));
|
|
|
|
|
$content.hide();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
$expander.addClass('h5p-open').text(H5P.t('hideAdvanced'));
|
|
|
|
|
$content.show();
|
|
|
|
|
}
|
2015-03-02 15:01:05 +01:00
|
|
|
|
$dialog.find('.h5p-embed-code-container').each(function(index, value) {
|
|
|
|
|
H5P.jQuery(this).css('height', this.scrollHeight + 'px');
|
|
|
|
|
});
|
2015-02-25 13:05:45 +01:00
|
|
|
|
positionInner();
|
2015-02-27 08:57:02 +01:00
|
|
|
|
};
|
|
|
|
|
$dialog.find('.h5p-expander').click(expand).keypress(function (event) {
|
|
|
|
|
if (event.keyCode === 32) {
|
2015-02-27 10:26:57 +01:00
|
|
|
|
expand.apply(this);
|
2015-02-27 08:57:02 +01:00
|
|
|
|
}
|
2015-02-25 12:10:07 +01:00
|
|
|
|
});
|
2014-03-26 08:43:29 +01:00
|
|
|
|
});
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
dialog.open();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Copyrights for a H5P Content Library.
|
2015-05-05 13:13:57 +02:00
|
|
|
|
*
|
|
|
|
|
* @class
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*/
|
|
|
|
|
H5P.ContentCopyrights = function () {
|
|
|
|
|
var label;
|
|
|
|
|
var media = [];
|
|
|
|
|
var content = [];
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
/**
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* Set label.
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @param {string} newLabel
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*/
|
|
|
|
|
this.setLabel = function (newLabel) {
|
|
|
|
|
label = newLabel;
|
|
|
|
|
};
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
/**
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* Add sub content.
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*
|
|
|
|
|
* @param {H5P.MediaCopyright} newMedia
|
|
|
|
|
*/
|
|
|
|
|
this.addMedia = function (newMedia) {
|
|
|
|
|
if (newMedia !== undefined) {
|
|
|
|
|
media.push(newMedia);
|
|
|
|
|
}
|
|
|
|
|
};
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
/**
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* Add sub content.
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*
|
|
|
|
|
* @param {H5P.ContentCopyrights} newContent
|
|
|
|
|
*/
|
|
|
|
|
this.addContent = function (newContent) {
|
|
|
|
|
if (newContent !== undefined) {
|
|
|
|
|
content.push(newContent);
|
|
|
|
|
}
|
|
|
|
|
};
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
/**
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* Print content copyright.
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @returns {string} HTML.
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*/
|
|
|
|
|
this.toString = function () {
|
|
|
|
|
var html = '';
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
// Add media rights
|
|
|
|
|
for (var i = 0; i < media.length; i++) {
|
|
|
|
|
html += media[i];
|
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
// Add sub content rights
|
2014-10-01 10:05:04 +02:00
|
|
|
|
for (i = 0; i < content.length; i++) {
|
2014-03-26 08:43:29 +01:00
|
|
|
|
html += content[i];
|
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
if (html !== '') {
|
|
|
|
|
// Add a label to this info
|
|
|
|
|
if (label !== undefined) {
|
|
|
|
|
html = '<h3>' + label + '</h3>' + html;
|
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
// Add wrapper
|
|
|
|
|
html = '<div class="h5p-content-copyrights">' + html + '</div>';
|
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
return html;
|
|
|
|
|
};
|
2014-05-02 15:45:45 +02:00
|
|
|
|
};
|
2014-03-26 08:43:29 +01:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A ordered list of copyright fields for media.
|
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @class
|
|
|
|
|
* @param {Object} copyright
|
|
|
|
|
* Copyright information fields.
|
|
|
|
|
* @param {Object} [labels]
|
|
|
|
|
* Translation of labels.
|
|
|
|
|
* @param {Array} [order]
|
|
|
|
|
* Order of the fields.
|
|
|
|
|
* @param {Object} [extraFields]
|
|
|
|
|
* Add extra copyright fields.
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*/
|
|
|
|
|
H5P.MediaCopyright = function (copyright, labels, order, extraFields) {
|
|
|
|
|
var thumbnail;
|
|
|
|
|
var list = new H5P.DefinitionList();
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
/**
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* Get translated label for field.
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @private
|
|
|
|
|
* @param {string} fieldName
|
|
|
|
|
* @returns {string}
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*/
|
|
|
|
|
var getLabel = function (fieldName) {
|
|
|
|
|
if (labels === undefined || labels[fieldName] === undefined) {
|
|
|
|
|
return H5P.t(fieldName);
|
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
return labels[fieldName];
|
|
|
|
|
};
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
/**
|
2017-05-30 14:12:22 +02:00
|
|
|
|
* Get humanized value for the license field.
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @private
|
2017-05-30 14:12:22 +02:00
|
|
|
|
* @param {string} license
|
|
|
|
|
* @param {string} [version]
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @returns {string}
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*/
|
2017-05-30 14:12:22 +02:00
|
|
|
|
var humanizeLicense = function (license, version) {
|
|
|
|
|
var copyrightLicense = H5P.copyrightLicenses[license];
|
|
|
|
|
|
|
|
|
|
// Build license string
|
|
|
|
|
var value = '';
|
|
|
|
|
if (!(license === 'PD' && version)) {
|
|
|
|
|
// Add license label
|
2017-05-31 09:33:10 +02:00
|
|
|
|
value += (copyrightLicense.hasOwnProperty('label') ? copyrightLicense.label : copyrightLicense);
|
2017-05-30 14:12:22 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check for version info
|
|
|
|
|
var versionInfo;
|
2017-06-06 13:51:51 +02:00
|
|
|
|
if (copyrightLicense.versions) {
|
2017-06-06 14:21:18 +02:00
|
|
|
|
if (copyrightLicense.versions.default && (!version || !copyrightLicense.versions[version])) {
|
2017-06-06 13:51:51 +02:00
|
|
|
|
version = copyrightLicense.versions.default;
|
|
|
|
|
}
|
|
|
|
|
if (version && copyrightLicense.versions[version]) {
|
|
|
|
|
versionInfo = copyrightLicense.versions[version];
|
|
|
|
|
}
|
2017-05-30 14:12:22 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (versionInfo) {
|
|
|
|
|
// Add license version
|
|
|
|
|
if (value) {
|
|
|
|
|
value += ' ';
|
|
|
|
|
}
|
2017-05-31 09:33:10 +02:00
|
|
|
|
value += (versionInfo.hasOwnProperty('label') ? versionInfo.label : versionInfo);
|
2017-05-30 14:12:22 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add link if specified
|
|
|
|
|
var link;
|
2017-05-31 09:33:10 +02:00
|
|
|
|
if (copyrightLicense.hasOwnProperty('link')) {
|
2017-05-30 14:12:22 +02:00
|
|
|
|
link = copyrightLicense.link.replace(':version', copyrightLicense.linkVersions ? copyrightLicense.linkVersions[version] : version);
|
|
|
|
|
}
|
2017-05-31 09:33:10 +02:00
|
|
|
|
else if (versionInfo && copyrightLicense.hasOwnProperty('link')) {
|
2017-05-30 14:12:22 +02:00
|
|
|
|
link = versionInfo.link
|
|
|
|
|
}
|
|
|
|
|
if (link) {
|
|
|
|
|
value = '<a href="' + link + '" target="_blank">' + value + '</a>';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Generate parenthesis
|
|
|
|
|
var parenthesis = '';
|
|
|
|
|
if (license !== 'PD' && license !== 'C') {
|
|
|
|
|
parenthesis += license;
|
|
|
|
|
}
|
|
|
|
|
if (version && version !== 'CC0 1.0') {
|
|
|
|
|
if (parenthesis && license !== 'GNU GPL') {
|
|
|
|
|
parenthesis += ' ';
|
|
|
|
|
}
|
|
|
|
|
parenthesis += version;
|
|
|
|
|
}
|
|
|
|
|
if (parenthesis) {
|
|
|
|
|
value += ' (' + parenthesis + ')';
|
|
|
|
|
}
|
|
|
|
|
if (license === 'C') {
|
|
|
|
|
value += ' ©';
|
2014-03-26 08:43:29 +01:00
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
return value;
|
|
|
|
|
};
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
if (copyright !== undefined) {
|
|
|
|
|
// Add the extra fields
|
|
|
|
|
for (var field in extraFields) {
|
|
|
|
|
if (extraFields.hasOwnProperty(field)) {
|
|
|
|
|
copyright[field] = extraFields[field];
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
if (order === undefined) {
|
|
|
|
|
// Set default order
|
|
|
|
|
order = ['title', 'author', 'year', 'source', 'license'];
|
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
for (var i = 0; i < order.length; i++) {
|
|
|
|
|
var fieldName = order[i];
|
|
|
|
|
if (copyright[fieldName] !== undefined) {
|
2017-05-30 14:12:22 +02:00
|
|
|
|
var humanValue = copyright[fieldName];
|
|
|
|
|
if (fieldName === 'license') {
|
|
|
|
|
humanValue = humanizeLicense(copyright.license, copyright.version);
|
|
|
|
|
}
|
|
|
|
|
list.add(new H5P.Field(getLabel(fieldName), humanValue));
|
2014-03-26 08:43:29 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
/**
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* Set thumbnail.
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*
|
|
|
|
|
* @param {H5P.Thumbnail} newThumbnail
|
|
|
|
|
*/
|
|
|
|
|
this.setThumbnail = function (newThumbnail) {
|
|
|
|
|
thumbnail = newThumbnail;
|
|
|
|
|
};
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
/**
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* Checks if this copyright is undisclosed.
|
2014-03-26 08:43:29 +01:00
|
|
|
|
* I.e. only has the license attribute set, and it's undisclosed.
|
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @returns {boolean}
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*/
|
|
|
|
|
this.undisclosed = function () {
|
|
|
|
|
if (list.size() === 1) {
|
|
|
|
|
var field = list.get(0);
|
2017-05-30 14:12:22 +02:00
|
|
|
|
if (field.getLabel() === getLabel('license') && field.getValue() === humanizeLicense('U')) {
|
2014-03-26 08:43:29 +01:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
};
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
/**
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* Print media copyright.
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @returns {string} HTML.
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*/
|
|
|
|
|
this.toString = function () {
|
|
|
|
|
var html = '';
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
if (this.undisclosed()) {
|
|
|
|
|
return html; // No need to print a copyright with a single undisclosed license.
|
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
if (thumbnail !== undefined) {
|
|
|
|
|
html += thumbnail;
|
|
|
|
|
}
|
|
|
|
|
html += list;
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
if (html !== '') {
|
|
|
|
|
html = '<div class="h5p-media-copyright">' + html + '</div>';
|
|
|
|
|
}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
return html;
|
|
|
|
|
};
|
2014-05-02 15:45:45 +02:00
|
|
|
|
};
|
2014-03-26 08:43:29 +01:00
|
|
|
|
|
|
|
|
|
/**
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* A simple and elegant class for creating thumbnails of images.
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @class
|
|
|
|
|
* @param {string} source
|
|
|
|
|
* @param {number} width
|
|
|
|
|
* @param {number} height
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*/
|
|
|
|
|
H5P.Thumbnail = function (source, width, height) {
|
|
|
|
|
var thumbWidth, thumbHeight = 100;
|
|
|
|
|
if (width !== undefined) {
|
|
|
|
|
thumbWidth = Math.round(thumbHeight * (width / height));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* Print thumbnail.
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @returns {string} HTML.
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*/
|
|
|
|
|
this.toString = function () {
|
|
|
|
|
return '<img src="' + source + '" alt="' + H5P.t('thumbnail') + '" class="h5p-thumbnail" height="' + thumbHeight + '"' + (thumbWidth === undefined ? '' : ' width="' + thumbWidth + '"') + '/>';
|
|
|
|
|
};
|
2014-05-02 15:45:45 +02:00
|
|
|
|
};
|
2014-03-26 08:43:29 +01:00
|
|
|
|
|
|
|
|
|
/**
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* Simple data structure class for storing a single field.
|
|
|
|
|
*
|
|
|
|
|
* @class
|
|
|
|
|
* @param {string} label
|
|
|
|
|
* @param {string} value
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*/
|
|
|
|
|
H5P.Field = function (label, value) {
|
|
|
|
|
/**
|
|
|
|
|
* Public. Get field label.
|
|
|
|
|
*
|
|
|
|
|
* @returns {String}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
*/
|
2014-03-26 08:43:29 +01:00
|
|
|
|
this.getLabel = function () {
|
|
|
|
|
return label;
|
|
|
|
|
};
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
/**
|
|
|
|
|
* Public. Get field value.
|
|
|
|
|
*
|
|
|
|
|
* @returns {String}
|
2014-10-01 10:05:04 +02:00
|
|
|
|
*/
|
2014-03-26 08:43:29 +01:00
|
|
|
|
this.getValue = function () {
|
|
|
|
|
return value;
|
|
|
|
|
};
|
2014-05-02 15:45:45 +02:00
|
|
|
|
};
|
2014-03-26 08:43:29 +01:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Simple class for creating a definition list.
|
2015-05-05 13:13:57 +02:00
|
|
|
|
*
|
|
|
|
|
* @class
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*/
|
|
|
|
|
H5P.DefinitionList = function () {
|
|
|
|
|
var fields = [];
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
/**
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* Add field to list.
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*
|
|
|
|
|
* @param {H5P.Field} field
|
|
|
|
|
*/
|
|
|
|
|
this.add = function (field) {
|
|
|
|
|
fields.push(field);
|
|
|
|
|
};
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
/**
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* Get Number of fields.
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @returns {number}
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*/
|
|
|
|
|
this.size = function () {
|
|
|
|
|
return fields.length;
|
|
|
|
|
};
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
/**
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* Get field at given index.
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @param {number} index
|
|
|
|
|
* @returns {H5P.Field}
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*/
|
|
|
|
|
this.get = function (index) {
|
|
|
|
|
return fields[index];
|
|
|
|
|
};
|
2014-10-01 10:05:04 +02:00
|
|
|
|
|
2014-03-26 08:43:29 +01:00
|
|
|
|
/**
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* Print definition list.
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @returns {string} HTML.
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*/
|
|
|
|
|
this.toString = function () {
|
|
|
|
|
var html = '';
|
|
|
|
|
for (var i = 0; i < fields.length; i++) {
|
|
|
|
|
var field = fields[i];
|
|
|
|
|
html += '<dt>' + field.getLabel() + '</dt><dd>' + field.getValue() + '</dd>';
|
|
|
|
|
}
|
|
|
|
|
return (html === '' ? html : '<dl class="h5p-definition-list">' + html + '</dl>');
|
|
|
|
|
};
|
2014-05-02 15:45:45 +02:00
|
|
|
|
};
|
2014-03-26 08:43:29 +01:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* THIS FUNCTION/CLASS IS DEPRECATED AND WILL BE REMOVED.
|
|
|
|
|
*
|
|
|
|
|
* Helper object for keeping coordinates in the same format all over.
|
2015-05-05 13:13:57 +02:00
|
|
|
|
*
|
|
|
|
|
* @deprecated
|
|
|
|
|
* Will be removed march 2016.
|
|
|
|
|
* @class
|
|
|
|
|
* @param {number} x
|
|
|
|
|
* @param {number} y
|
|
|
|
|
* @param {number} w
|
|
|
|
|
* @param {number} h
|
2014-03-26 08:43:29 +01:00
|
|
|
|
*/
|
2013-11-26 14:54:29 +01:00
|
|
|
|
H5P.Coords = function (x, y, w, h) {
|
2013-01-17 09:01:43 +01:00
|
|
|
|
if ( !(this instanceof H5P.Coords) )
|
|
|
|
|
return new H5P.Coords(x, y, w, h);
|
|
|
|
|
|
2015-05-05 13:13:57 +02:00
|
|
|
|
/** @member {number} */
|
2013-01-17 09:01:43 +01:00
|
|
|
|
this.x = 0;
|
2015-05-05 13:13:57 +02:00
|
|
|
|
/** @member {number} */
|
2013-01-17 09:01:43 +01:00
|
|
|
|
this.y = 0;
|
2015-05-05 13:13:57 +02:00
|
|
|
|
/** @member {number} */
|
2013-01-17 09:01:43 +01:00
|
|
|
|
this.w = 1;
|
2015-05-05 13:13:57 +02:00
|
|
|
|
/** @member {number} */
|
2013-01-17 09:01:43 +01:00
|
|
|
|
this.h = 1;
|
|
|
|
|
|
2013-04-02 17:05:16 +02:00
|
|
|
|
if (typeof(x) === 'object') {
|
2013-01-17 09:01:43 +01:00
|
|
|
|
this.x = x.x;
|
|
|
|
|
this.y = x.y;
|
|
|
|
|
this.w = x.w;
|
|
|
|
|
this.h = x.h;
|
|
|
|
|
} else {
|
|
|
|
|
if (x !== undefined) {
|
|
|
|
|
this.x = x;
|
|
|
|
|
}
|
|
|
|
|
if (y !== undefined) {
|
|
|
|
|
this.y = y;
|
|
|
|
|
}
|
|
|
|
|
if (w !== undefined) {
|
|
|
|
|
this.w = w;
|
|
|
|
|
}
|
|
|
|
|
if (h !== undefined) {
|
|
|
|
|
this.h = h;
|
2013-01-25 14:38:12 +01:00
|
|
|
|
}
|
2013-01-17 09:01:43 +01:00
|
|
|
|
}
|
|
|
|
|
return this;
|
|
|
|
|
};
|
|
|
|
|
|
2013-03-06 15:59:02 +01:00
|
|
|
|
/**
|
2013-11-26 14:54:29 +01:00
|
|
|
|
* Parse library string into values.
|
|
|
|
|
*
|
|
|
|
|
* @param {string} library
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* library in the format "machineName majorVersion.minorVersion"
|
|
|
|
|
* @returns {Object}
|
|
|
|
|
* library as an object with machineName, majorVersion and minorVersion properties
|
|
|
|
|
* return false if the library parameter is invalid
|
2013-03-06 15:59:02 +01:00
|
|
|
|
*/
|
|
|
|
|
H5P.libraryFromString = function (library) {
|
2016-04-21 13:22:01 +02:00
|
|
|
|
var regExp = /(.+)\s(\d+)\.(\d+)$/g;
|
2013-03-06 15:59:02 +01:00
|
|
|
|
var res = regExp.exec(library);
|
|
|
|
|
if (res !== null) {
|
|
|
|
|
return {
|
|
|
|
|
'machineName': res[1],
|
|
|
|
|
'majorVersion': res[2],
|
|
|
|
|
'minorVersion': res[3]
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
};
|
2013-02-07 17:50:17 +01:00
|
|
|
|
|
2013-07-08 08:59:14 +02:00
|
|
|
|
/**
|
|
|
|
|
* Get the path to the library
|
2013-07-18 15:58:52 +02:00
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @param {string} library
|
2014-03-26 08:43:29 +01:00
|
|
|
|
* The library identifier in the format "machineName-majorVersion.minorVersion".
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @returns {string}
|
|
|
|
|
* The full path to the library.
|
2013-07-08 08:59:14 +02:00
|
|
|
|
*/
|
2013-11-26 14:54:29 +01:00
|
|
|
|
H5P.getLibraryPath = function (library) {
|
2016-01-13 08:45:00 +01:00
|
|
|
|
return (H5PIntegration.libraryUrl !== undefined ? H5PIntegration.libraryUrl + '/' : H5PIntegration.url + '/libraries/') + library;
|
2013-07-08 08:59:14 +02:00
|
|
|
|
};
|
|
|
|
|
|
2013-04-04 14:58:17 +02:00
|
|
|
|
/**
|
|
|
|
|
* Recursivly clone the given object.
|
2013-04-26 17:27:35 +02:00
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @param {Object|Array} object
|
|
|
|
|
* Object to clone.
|
|
|
|
|
* @param {boolean} [recursive]
|
|
|
|
|
* @returns {Object|Array}
|
|
|
|
|
* A clone of object.
|
2013-04-04 14:58:17 +02:00
|
|
|
|
*/
|
2013-05-23 13:00:58 +02:00
|
|
|
|
H5P.cloneObject = function (object, recursive) {
|
2015-05-05 13:13:57 +02:00
|
|
|
|
// TODO: Consider if this needs to be in core. Doesn't $.extend do the same?
|
2013-05-23 13:00:58 +02:00
|
|
|
|
var clone = object instanceof Array ? [] : {};
|
2013-04-26 17:27:35 +02:00
|
|
|
|
|
2013-04-04 14:58:17 +02:00
|
|
|
|
for (var i in object) {
|
|
|
|
|
if (object.hasOwnProperty(i)) {
|
|
|
|
|
if (recursive !== undefined && recursive && typeof object[i] === 'object') {
|
2013-05-23 13:00:58 +02:00
|
|
|
|
clone[i] = H5P.cloneObject(object[i], recursive);
|
2013-04-04 14:58:17 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
clone[i] = object[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-04-18 09:51:58 +02:00
|
|
|
|
}
|
2013-04-26 17:27:35 +02:00
|
|
|
|
|
2013-04-18 09:51:58 +02:00
|
|
|
|
return clone;
|
2013-04-04 14:58:17 +02:00
|
|
|
|
};
|
|
|
|
|
|
2013-04-30 17:07:14 +02:00
|
|
|
|
/**
|
|
|
|
|
* Remove all empty spaces before and after the value.
|
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @param {string} value
|
|
|
|
|
* @returns {string}
|
2013-04-30 17:07:14 +02:00
|
|
|
|
*/
|
|
|
|
|
H5P.trim = function (value) {
|
|
|
|
|
return value.replace(/^\s+|\s+$/g, '');
|
2015-05-05 13:13:57 +02:00
|
|
|
|
|
|
|
|
|
// TODO: Only include this or String.trim(). What is best?
|
|
|
|
|
// I'm leaning towards implementing the missing ones: http://kangax.github.io/compat-table/es5/
|
|
|
|
|
// So should we make this function deprecated?
|
2013-04-30 17:07:14 +02:00
|
|
|
|
};
|
|
|
|
|
|
2013-05-24 13:24:01 +02:00
|
|
|
|
/**
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* Check if JavaScript path/key is loaded.
|
2013-05-24 13:24:01 +02:00
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @param {string} path
|
|
|
|
|
* @returns {boolean}
|
2013-05-24 13:24:01 +02:00
|
|
|
|
*/
|
|
|
|
|
H5P.jsLoaded = function (path) {
|
2015-02-27 13:59:42 +01:00
|
|
|
|
H5PIntegration.loadedJs = H5PIntegration.loadedJs || [];
|
|
|
|
|
return H5P.jQuery.inArray(path, H5PIntegration.loadedJs) !== -1;
|
2013-05-24 13:24:01 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Check if styles path/key is loaded.
|
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @param {string} path
|
|
|
|
|
* @returns {boolean}
|
2013-05-24 13:24:01 +02:00
|
|
|
|
*/
|
|
|
|
|
H5P.cssLoaded = function (path) {
|
2015-02-27 13:59:42 +01:00
|
|
|
|
H5PIntegration.loadedCss = H5PIntegration.loadedCss || [];
|
|
|
|
|
return H5P.jQuery.inArray(path, H5PIntegration.loadedCss) !== -1;
|
2013-05-24 13:24:01 +02:00
|
|
|
|
};
|
|
|
|
|
|
2013-11-26 14:54:29 +01:00
|
|
|
|
/**
|
|
|
|
|
* Shuffle an array in place.
|
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @param {Array} array
|
|
|
|
|
* Array to shuffle
|
|
|
|
|
* @returns {Array}
|
|
|
|
|
* The passed array is returned for chaining.
|
2013-11-26 14:54:29 +01:00
|
|
|
|
*/
|
|
|
|
|
H5P.shuffleArray = function (array) {
|
2015-05-05 13:13:57 +02:00
|
|
|
|
// TODO: Consider if this should be a part of core. I'm guessing very few libraries are going to use it.
|
2015-04-22 09:51:36 +02:00
|
|
|
|
if (!(array instanceof Array)) {
|
2013-11-26 14:54:29 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-25 01:50:16 +02:00
|
|
|
|
var i = array.length, j, tempi, tempj;
|
2013-01-17 09:01:43 +01:00
|
|
|
|
if ( i === 0 ) return false;
|
|
|
|
|
while ( --i ) {
|
2013-01-25 14:38:12 +01:00
|
|
|
|
j = Math.floor( Math.random() * ( i + 1 ) );
|
2013-07-25 01:50:16 +02:00
|
|
|
|
tempi = array[i];
|
|
|
|
|
tempj = array[j];
|
|
|
|
|
array[i] = tempj;
|
|
|
|
|
array[j] = tempi;
|
2013-01-17 09:01:43 +01:00
|
|
|
|
}
|
2013-07-25 01:50:16 +02:00
|
|
|
|
return array;
|
2013-01-17 09:01:43 +01:00
|
|
|
|
};
|
|
|
|
|
|
2013-10-21 13:22:13 +02:00
|
|
|
|
/**
|
|
|
|
|
* Post finished results for user.
|
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @deprecated
|
|
|
|
|
* Do not use this function directly, trigger the finish event instead.
|
|
|
|
|
* Will be removed march 2016
|
|
|
|
|
* @param {number} contentId
|
|
|
|
|
* Identifies the content
|
|
|
|
|
* @param {number} score
|
|
|
|
|
* Achieved score/points
|
|
|
|
|
* @param {number} maxScore
|
|
|
|
|
* The maximum score/points that can be achieved
|
|
|
|
|
* @param {number} [time]
|
|
|
|
|
* Reported time consumption/usage
|
2013-10-21 13:22:13 +02:00
|
|
|
|
*/
|
2014-10-15 11:38:29 +02:00
|
|
|
|
H5P.setFinished = function (contentId, score, maxScore, time) {
|
2016-11-19 12:29:38 +01:00
|
|
|
|
var validScore = typeof score === 'number' || score instanceof Number;
|
|
|
|
|
if (validScore && H5PIntegration.postUserStatistics === true) {
|
2014-10-15 11:38:29 +02:00
|
|
|
|
/**
|
|
|
|
|
* Return unix timestamp for the given JS Date.
|
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @private
|
2014-10-15 11:38:29 +02:00
|
|
|
|
* @param {Date} date
|
|
|
|
|
* @returns {Number}
|
|
|
|
|
*/
|
|
|
|
|
var toUnix = function (date) {
|
|
|
|
|
return Math.round(date.getTime() / 1000);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Post the results
|
2016-02-22 10:38:33 +01:00
|
|
|
|
H5P.jQuery.post(H5PIntegration.ajax.setFinished, {
|
2014-10-15 11:38:29 +02:00
|
|
|
|
contentId: contentId,
|
|
|
|
|
score: score,
|
|
|
|
|
maxScore: maxScore,
|
|
|
|
|
opened: toUnix(H5P.opened[contentId]),
|
|
|
|
|
finished: toUnix(new Date()),
|
2016-06-13 10:31:09 +02:00
|
|
|
|
time: time
|
2014-10-15 11:38:29 +02:00
|
|
|
|
});
|
2013-10-21 13:22:13 +02:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2013-01-17 09:01:43 +01:00
|
|
|
|
// Add indexOf to browsers that lack them. (IEs)
|
2013-11-08 18:16:41 +01:00
|
|
|
|
if (!Array.prototype.indexOf) {
|
|
|
|
|
Array.prototype.indexOf = function (needle) {
|
|
|
|
|
for (var i = 0; i < this.length; i++) {
|
|
|
|
|
if (this[i] === needle) {
|
2013-01-25 14:38:12 +01:00
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
};
|
2013-01-17 09:01:43 +01:00
|
|
|
|
}
|
|
|
|
|
|
2013-07-09 10:36:27 +02:00
|
|
|
|
// Need to define trim() since this is not available on older IEs,
|
|
|
|
|
// and trim is used in several libs
|
2013-11-08 18:16:41 +01:00
|
|
|
|
if (String.prototype.trim === undefined) {
|
2013-07-09 10:36:27 +02:00
|
|
|
|
String.prototype.trim = function () {
|
|
|
|
|
return H5P.trim(this);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-04 18:43:06 +01:00
|
|
|
|
/**
|
|
|
|
|
* Trigger an event on an instance
|
2015-02-20 10:15:36 +01:00
|
|
|
|
*
|
2015-02-04 18:43:06 +01:00
|
|
|
|
* Helper function that triggers an event if the instance supports event handling
|
2015-02-20 10:15:36 +01:00
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @param {Object} instance
|
|
|
|
|
* Instance of H5P content
|
2015-02-04 18:43:06 +01:00
|
|
|
|
* @param {string} eventType
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* Type of event to trigger
|
|
|
|
|
* @param {*} data
|
|
|
|
|
* @param {Object} extras
|
2015-02-04 18:43:06 +01:00
|
|
|
|
*/
|
2015-05-05 13:13:57 +02:00
|
|
|
|
H5P.trigger = function (instance, eventType, data, extras) {
|
2015-02-04 18:43:06 +01:00
|
|
|
|
// Try new event system first
|
2015-01-14 18:08:36 +01:00
|
|
|
|
if (instance.trigger !== undefined) {
|
2015-04-07 19:32:44 +02:00
|
|
|
|
instance.trigger(eventType, data, extras);
|
2015-01-14 18:08:36 +01:00
|
|
|
|
}
|
2015-02-04 18:43:06 +01:00
|
|
|
|
// Try deprecated event system
|
|
|
|
|
else if (instance.$ !== undefined && instance.$.trigger !== undefined) {
|
2015-02-20 10:26:33 +01:00
|
|
|
|
instance.$.trigger(eventType);
|
2015-01-14 18:08:36 +01:00
|
|
|
|
}
|
2015-02-04 18:43:06 +01:00
|
|
|
|
};
|
2015-01-14 18:08:36 +01:00
|
|
|
|
|
2015-02-04 18:43:06 +01:00
|
|
|
|
/**
|
|
|
|
|
* Register an event handler
|
2015-02-20 10:15:36 +01:00
|
|
|
|
*
|
2015-02-04 18:43:06 +01:00
|
|
|
|
* Helper function that registers an event handler for an event type if
|
|
|
|
|
* the instance supports event handling
|
2015-02-20 10:15:36 +01:00
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @param {Object} instance
|
|
|
|
|
* Instance of H5P content
|
2015-02-04 18:43:06 +01:00
|
|
|
|
* @param {string} eventType
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* Type of event to listen for
|
|
|
|
|
* @param {H5P.EventCallback} handler
|
|
|
|
|
* Callback that gets triggered for events of the specified type
|
2015-02-04 18:43:06 +01:00
|
|
|
|
*/
|
2015-05-05 13:13:57 +02:00
|
|
|
|
H5P.on = function (instance, eventType, handler) {
|
2015-02-04 18:43:06 +01:00
|
|
|
|
// Try new event system first
|
2015-01-14 18:08:36 +01:00
|
|
|
|
if (instance.on !== undefined) {
|
2015-02-04 18:43:06 +01:00
|
|
|
|
instance.on(eventType, handler);
|
2015-01-14 18:08:36 +01:00
|
|
|
|
}
|
2015-02-04 18:43:06 +01:00
|
|
|
|
// Try deprecated event system
|
|
|
|
|
else if (instance.$ !== undefined && instance.$.on !== undefined) {
|
2015-02-20 10:26:33 +01:00
|
|
|
|
instance.$.on(eventType, handler);
|
2015-01-14 18:08:36 +01:00
|
|
|
|
}
|
2015-02-19 15:50:04 +01:00
|
|
|
|
};
|
2015-03-10 09:53:33 +01:00
|
|
|
|
|
2015-03-21 14:16:31 +01:00
|
|
|
|
/**
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* Generate random UUID
|
2015-04-07 19:32:44 +02:00
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @returns {string} UUID
|
2015-03-21 14:16:31 +01:00
|
|
|
|
*/
|
2015-05-05 13:13:57 +02:00
|
|
|
|
H5P.createUUID = function () {
|
2015-03-21 14:16:31 +01:00
|
|
|
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(char) {
|
|
|
|
|
var random = Math.random()*16|0, newChar = char === 'x' ? random : (random&0x3|0x8);
|
|
|
|
|
return newChar.toString(16);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2015-05-05 13:13:57 +02:00
|
|
|
|
/**
|
|
|
|
|
* Create title
|
|
|
|
|
*
|
|
|
|
|
* @param {string} rawTitle
|
|
|
|
|
* @param {number} maxLength
|
|
|
|
|
* @returns {string}
|
|
|
|
|
*/
|
|
|
|
|
H5P.createTitle = function (rawTitle, maxLength) {
|
2015-04-15 13:54:46 +02:00
|
|
|
|
if (!rawTitle) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
2015-03-22 12:45:03 +01:00
|
|
|
|
if (maxLength === undefined) {
|
|
|
|
|
maxLength = 60;
|
|
|
|
|
}
|
|
|
|
|
var title = H5P.jQuery('<div></div>')
|
|
|
|
|
.text(
|
|
|
|
|
// Strip tags
|
|
|
|
|
rawTitle.replace(/(<([^>]+)>)/ig,"")
|
|
|
|
|
// Escape
|
|
|
|
|
).text();
|
|
|
|
|
if (title.length > maxLength) {
|
|
|
|
|
title = title.substr(0, maxLength - 3) + '...';
|
|
|
|
|
}
|
|
|
|
|
return title;
|
2015-03-21 16:45:38 +01:00
|
|
|
|
};
|
2015-03-10 09:53:33 +01:00
|
|
|
|
|
2015-04-07 19:32:44 +02:00
|
|
|
|
// Wrap in privates
|
|
|
|
|
(function ($) {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates ajax requests for inserting, updateing and deleteing
|
|
|
|
|
* content user data.
|
|
|
|
|
*
|
|
|
|
|
* @private
|
|
|
|
|
* @param {number} contentId What content to store the data for.
|
|
|
|
|
* @param {string} dataType Identifies the set of data for this content.
|
|
|
|
|
* @param {string} subContentId Identifies sub content
|
|
|
|
|
* @param {function} [done] Callback when ajax is done.
|
|
|
|
|
* @param {object} [data] To be stored for future use.
|
|
|
|
|
* @param {boolean} [preload=false] Data is loaded when content is loaded.
|
|
|
|
|
* @param {boolean} [invalidate=false] Data is invalidated when content changes.
|
|
|
|
|
* @param {boolean} [async=true]
|
|
|
|
|
*/
|
|
|
|
|
function contentUserDataAjax(contentId, dataType, subContentId, done, data, preload, invalidate, async) {
|
2015-06-23 11:12:29 +02:00
|
|
|
|
if (H5PIntegration.user === undefined) {
|
|
|
|
|
// Not logged in, no use in saving.
|
|
|
|
|
done('Not signed in.');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-07 19:32:44 +02:00
|
|
|
|
var options = {
|
2015-04-20 10:30:52 +02:00
|
|
|
|
url: H5PIntegration.ajax.contentUserData.replace(':contentId', contentId).replace(':dataType', dataType).replace(':subContentId', subContentId ? subContentId : 0),
|
2015-04-07 19:32:44 +02:00
|
|
|
|
dataType: 'json',
|
|
|
|
|
async: async === undefined ? true : async
|
|
|
|
|
};
|
|
|
|
|
if (data !== undefined) {
|
|
|
|
|
options.type = 'POST';
|
|
|
|
|
options.data = {
|
|
|
|
|
data: (data === null ? 0 : data),
|
|
|
|
|
preload: (preload ? 1 : 0),
|
2016-06-13 10:31:09 +02:00
|
|
|
|
invalidate: (invalidate ? 1 : 0)
|
2015-04-07 19:32:44 +02:00
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
options.type = 'GET';
|
|
|
|
|
}
|
|
|
|
|
if (done !== undefined) {
|
2015-08-26 15:58:49 +02:00
|
|
|
|
options.error = function (xhr, error) {
|
2015-04-07 19:32:44 +02:00
|
|
|
|
done(error);
|
|
|
|
|
};
|
|
|
|
|
options.success = function (response) {
|
|
|
|
|
if (!response.success) {
|
2016-02-22 12:01:18 +01:00
|
|
|
|
done(response.message);
|
2015-04-07 19:32:44 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (response.data === false || response.data === undefined) {
|
|
|
|
|
done();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
done(undefined, response.data);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$.ajax(options);
|
2015-03-10 09:53:33 +01:00
|
|
|
|
}
|
2015-04-07 19:32:44 +02:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get user data for given content.
|
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @param {number} contentId
|
|
|
|
|
* What content to get data for.
|
|
|
|
|
* @param {string} dataId
|
|
|
|
|
* Identifies the set of data for this content.
|
|
|
|
|
* @param {function} done
|
|
|
|
|
* Callback with error and data parameters.
|
|
|
|
|
* @param {string} [subContentId]
|
|
|
|
|
* Identifies which data belongs to sub content.
|
2015-04-07 19:32:44 +02:00
|
|
|
|
*/
|
|
|
|
|
H5P.getUserData = function (contentId, dataId, done, subContentId) {
|
|
|
|
|
if (!subContentId) {
|
|
|
|
|
subContentId = 0; // Default
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-04 17:30:58 +01:00
|
|
|
|
H5PIntegration.contents = H5PIntegration.contents || {};
|
|
|
|
|
var content = H5PIntegration.contents['cid-' + contentId] || {};
|
2015-04-07 19:32:44 +02:00
|
|
|
|
var preloadedData = content.contentUserData;
|
2016-07-15 16:14:32 +02:00
|
|
|
|
if (preloadedData && preloadedData[subContentId] && preloadedData[subContentId][dataId] !== undefined) {
|
2015-04-07 19:32:44 +02:00
|
|
|
|
if (preloadedData[subContentId][dataId] === 'RESET') {
|
|
|
|
|
done(undefined, null);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
done(undefined, JSON.parse(preloadedData[subContentId][dataId]));
|
|
|
|
|
}
|
|
|
|
|
catch (err) {
|
|
|
|
|
done(err);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2015-08-26 15:58:49 +02:00
|
|
|
|
contentUserDataAjax(contentId, dataId, subContentId, function (err, data) {
|
2015-04-07 19:32:44 +02:00
|
|
|
|
if (err || data === undefined) {
|
|
|
|
|
done(err, data);
|
|
|
|
|
return; // Error or no data
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cache in preloaded
|
|
|
|
|
if (content.contentUserData === undefined) {
|
2015-04-22 10:58:59 +02:00
|
|
|
|
content.contentUserData = preloadedData = {};
|
2015-04-07 19:32:44 +02:00
|
|
|
|
}
|
|
|
|
|
if (preloadedData[subContentId] === undefined) {
|
|
|
|
|
preloadedData[subContentId] = {};
|
|
|
|
|
}
|
|
|
|
|
preloadedData[subContentId][dataId] = data;
|
|
|
|
|
|
|
|
|
|
// Done. Try to decode JSON
|
|
|
|
|
try {
|
|
|
|
|
done(undefined, JSON.parse(data));
|
|
|
|
|
}
|
|
|
|
|
catch (e) {
|
|
|
|
|
done(e);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2015-05-05 13:13:57 +02:00
|
|
|
|
/**
|
|
|
|
|
* Async error handling.
|
|
|
|
|
*
|
|
|
|
|
* @callback H5P.ErrorCallback
|
|
|
|
|
* @param {*} error
|
|
|
|
|
*/
|
|
|
|
|
|
2015-04-07 19:32:44 +02:00
|
|
|
|
/**
|
|
|
|
|
* Set user data for given content.
|
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @param {number} contentId
|
|
|
|
|
* What content to get data for.
|
|
|
|
|
* @param {string} dataId
|
|
|
|
|
* Identifies the set of data for this content.
|
|
|
|
|
* @param {Object} data
|
|
|
|
|
* The data that is to be stored.
|
|
|
|
|
* @param {Object} [extras]
|
|
|
|
|
* Extra properties
|
|
|
|
|
* @param {string} [extras.subContentId]
|
|
|
|
|
* Identifies which data belongs to sub content.
|
|
|
|
|
* @param {boolean} [extras.preloaded=true]
|
|
|
|
|
* If the data should be loaded when content is loaded.
|
|
|
|
|
* @param {boolean} [extras.deleteOnChange=false]
|
|
|
|
|
* If the data should be invalidated when the content changes.
|
|
|
|
|
* @param {H5P.ErrorCallback} [extras.errorCallback]
|
|
|
|
|
* Callback with error as parameters.
|
|
|
|
|
* @param {boolean} [extras.async=true]
|
2015-04-07 19:32:44 +02:00
|
|
|
|
*/
|
2015-04-09 14:00:00 +02:00
|
|
|
|
H5P.setUserData = function (contentId, dataId, data, extras) {
|
|
|
|
|
var options = H5P.jQuery.extend(true, {}, {
|
|
|
|
|
subContentId: 0,
|
|
|
|
|
preloaded: true,
|
|
|
|
|
deleteOnChange: false,
|
|
|
|
|
async: true
|
|
|
|
|
}, extras);
|
2015-04-07 19:32:44 +02:00
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
data = JSON.stringify(data);
|
|
|
|
|
}
|
|
|
|
|
catch (err) {
|
2015-04-20 10:30:52 +02:00
|
|
|
|
if (options.errorCallback) {
|
2015-04-09 14:00:00 +02:00
|
|
|
|
options.errorCallback(err);
|
|
|
|
|
}
|
2015-04-07 19:32:44 +02:00
|
|
|
|
return; // Failed to serialize.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var content = H5PIntegration.contents['cid-' + contentId];
|
2015-11-04 17:30:58 +01:00
|
|
|
|
if (content === undefined) {
|
|
|
|
|
content = H5PIntegration.contents['cid-' + contentId] = {};
|
|
|
|
|
}
|
2015-04-07 19:32:44 +02:00
|
|
|
|
if (!content.contentUserData) {
|
|
|
|
|
content.contentUserData = {};
|
|
|
|
|
}
|
|
|
|
|
var preloadedData = content.contentUserData;
|
2015-04-09 14:00:00 +02:00
|
|
|
|
if (preloadedData[options.subContentId] === undefined) {
|
|
|
|
|
preloadedData[options.subContentId] = {};
|
2015-04-07 19:32:44 +02:00
|
|
|
|
}
|
2015-04-09 14:00:00 +02:00
|
|
|
|
if (data === preloadedData[options.subContentId][dataId]) {
|
2015-04-07 19:32:44 +02:00
|
|
|
|
return; // No need to save this twice.
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-09 14:00:00 +02:00
|
|
|
|
preloadedData[options.subContentId][dataId] = data;
|
|
|
|
|
contentUserDataAjax(contentId, dataId, options.subContentId, function (error, data) {
|
|
|
|
|
if (options.errorCallback && error) {
|
|
|
|
|
options.errorCallback(error);
|
2015-04-07 19:32:44 +02:00
|
|
|
|
}
|
2015-04-09 14:00:00 +02:00
|
|
|
|
}, data, options.preloaded, options.deleteOnChange, options.async);
|
2015-04-07 19:32:44 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Delete user data for given content.
|
|
|
|
|
*
|
2015-05-05 13:13:57 +02:00
|
|
|
|
* @param {number} contentId
|
|
|
|
|
* What content to remove data for.
|
|
|
|
|
* @param {string} dataId
|
|
|
|
|
* Identifies the set of data for this content.
|
|
|
|
|
* @param {string} [subContentId]
|
|
|
|
|
* Identifies which data belongs to sub content.
|
2015-04-07 19:32:44 +02:00
|
|
|
|
*/
|
|
|
|
|
H5P.deleteUserData = function (contentId, dataId, subContentId) {
|
|
|
|
|
if (!subContentId) {
|
|
|
|
|
subContentId = 0; // Default
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove from preloaded/cache
|
|
|
|
|
var preloadedData = H5PIntegration.contents['cid-' + contentId].contentUserData;
|
|
|
|
|
if (preloadedData && preloadedData[subContentId] && preloadedData[subContentId][dataId]) {
|
|
|
|
|
delete preloadedData[subContentId][dataId];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
contentUserDataAjax(contentId, dataId, subContentId, undefined, null);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Init H5P when page is fully loadded
|
|
|
|
|
$(document).ready(function () {
|
2016-11-17 12:56:15 +01:00
|
|
|
|
|
2017-05-30 15:43:26 +02:00
|
|
|
|
var ccVersions = {
|
2017-06-06 13:51:51 +02:00
|
|
|
|
'default': '4.0',
|
2017-05-30 15:43:26 +02:00
|
|
|
|
'4.0': H5P.t('licenseCC40'),
|
|
|
|
|
'3.0': H5P.t('licenseCC30'),
|
|
|
|
|
'2.5': H5P.t('licenseCC25'),
|
|
|
|
|
'2.0': H5P.t('licenseCC20'),
|
|
|
|
|
'1.0': H5P.t('licenseCC10'),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Maps copyright license codes to their human readable counterpart.
|
|
|
|
|
*
|
|
|
|
|
* @type {Object}
|
|
|
|
|
*/
|
|
|
|
|
H5P.copyrightLicenses = {
|
|
|
|
|
'U': H5P.t('licenseU'),
|
|
|
|
|
'CC BY': {
|
|
|
|
|
label: H5P.t('licenseCCBY'),
|
|
|
|
|
link: 'http://creativecommons.org/licenses/by/:version/legalcode',
|
|
|
|
|
versions: ccVersions
|
|
|
|
|
},
|
|
|
|
|
'CC BY-SA': {
|
|
|
|
|
label: H5P.t('licenseCCBYSA'),
|
|
|
|
|
link: 'http://creativecommons.org/licenses/by-sa/:version/legalcode',
|
|
|
|
|
versions: ccVersions
|
|
|
|
|
},
|
|
|
|
|
'CC BY-ND': {
|
|
|
|
|
label: H5P.t('licenseCCBYND'),
|
|
|
|
|
link: 'http://creativecommons.org/licenses/by-nd/:version/legalcode',
|
|
|
|
|
versions: ccVersions
|
|
|
|
|
},
|
|
|
|
|
'CC BY-NC': {
|
|
|
|
|
label: H5P.t('licenseCCBYNC'),
|
|
|
|
|
link: 'http://creativecommons.org/licenses/by-nc/:version/legalcode',
|
|
|
|
|
versions: ccVersions
|
|
|
|
|
},
|
|
|
|
|
'CC BY-NC-SA': {
|
|
|
|
|
label: H5P.t('licenseCCBYNCSA'),
|
|
|
|
|
link: 'http://creativecommons.org/licenses/by-nc-sa/:version/legalcode',
|
|
|
|
|
versions: ccVersions
|
|
|
|
|
},
|
|
|
|
|
'CC BY-NC-ND': {
|
|
|
|
|
label: H5P.t('licenseCCBYNCND'),
|
|
|
|
|
link: 'http://creativecommons.org/licenses/by-nc-nd/:version/legalcode',
|
|
|
|
|
versions: ccVersions
|
|
|
|
|
},
|
|
|
|
|
'GNU GPL': {
|
|
|
|
|
label: H5P.t('licenseGPL'),
|
|
|
|
|
link: 'http://www.gnu.org/licenses/gpl-:version-standalone.html',
|
|
|
|
|
linkVersions: {
|
|
|
|
|
'v3': '3.0',
|
|
|
|
|
'v2': '2.0',
|
|
|
|
|
'v1': '1.0'
|
|
|
|
|
},
|
|
|
|
|
versions: {
|
2017-06-06 13:51:51 +02:00
|
|
|
|
'default': 'v3',
|
2017-05-30 15:43:26 +02:00
|
|
|
|
'v3': H5P.t('licenseV3'),
|
|
|
|
|
'v2': H5P.t('licenseV2'),
|
|
|
|
|
'v1': H5P.t('licenseV1')
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
'PD': {
|
|
|
|
|
label: H5P.t('licensePD'),
|
|
|
|
|
versions: {
|
|
|
|
|
'CC0 1.0': {
|
|
|
|
|
label: H5P.t('licenseCC010'),
|
|
|
|
|
link: 'https://creativecommons.org/publicdomain/zero/1.0/'
|
|
|
|
|
},
|
|
|
|
|
'CC PDM': {
|
|
|
|
|
label: H5P.t('licensePDM'),
|
|
|
|
|
link: 'https://creativecommons.org/publicdomain/mark/1.0/'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
'ODC PDDL': '<a href="http://opendatacommons.org/licenses/pddl/1.0/" target="_blank">Public Domain Dedication and Licence</a>',
|
|
|
|
|
'CC PDM': H5P.t('licensePDM'),
|
|
|
|
|
'C': H5P.t('licenseC'),
|
|
|
|
|
};
|
|
|
|
|
|
2016-11-17 12:56:15 +01:00
|
|
|
|
/**
|
|
|
|
|
* Indicates if H5P is embedded on an external page using iframe.
|
|
|
|
|
* @member {boolean} H5P.externalEmbed
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// Relay events to top window. This must be done before H5P.init
|
|
|
|
|
// since events may be fired on initialization.
|
|
|
|
|
if (H5P.isFramed && H5P.externalEmbed === false) {
|
|
|
|
|
H5P.externalDispatcher.on('*', function (event) {
|
|
|
|
|
window.parent.H5P.externalDispatcher.trigger.call(this, event);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-05 13:13:57 +02:00
|
|
|
|
/**
|
|
|
|
|
* Prevent H5P Core from initializing. Must be overriden before document ready.
|
|
|
|
|
* @member {boolean} H5P.preventInit
|
|
|
|
|
*/
|
2015-04-07 19:32:44 +02:00
|
|
|
|
if (!H5P.preventInit) {
|
|
|
|
|
// Note that this start script has to be an external resource for it to
|
|
|
|
|
// load in correct order in IE9.
|
|
|
|
|
H5P.init(document.body);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (H5PIntegration.saveFreq !== false) {
|
2015-12-18 10:06:33 +01:00
|
|
|
|
// When was the last state stored
|
|
|
|
|
var lastStoredOn = 0;
|
2015-04-07 19:32:44 +02:00
|
|
|
|
// Store the current state of the H5P when leaving the page.
|
2015-12-16 09:50:58 +01:00
|
|
|
|
var storeCurrentState = function () {
|
2015-12-18 10:06:33 +01:00
|
|
|
|
// Make sure at least 250 ms has passed since last save
|
|
|
|
|
var currentTime = new Date().getTime();
|
|
|
|
|
if (currentTime - lastStoredOn > 250) {
|
|
|
|
|
lastStoredOn = currentTime;
|
|
|
|
|
for (var i = 0; i < H5P.instances.length; i++) {
|
|
|
|
|
var instance = H5P.instances[i];
|
|
|
|
|
if (instance.getCurrentState instanceof Function ||
|
|
|
|
|
typeof instance.getCurrentState === 'function') {
|
|
|
|
|
var state = instance.getCurrentState();
|
|
|
|
|
if (state !== undefined) {
|
|
|
|
|
// Async is not used to prevent the request from being cancelled.
|
|
|
|
|
H5P.setUserData(instance.contentId, 'state', state, {deleteOnChange: true, async: false});
|
|
|
|
|
}
|
2015-04-07 19:32:44 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-12-16 09:50:58 +01:00
|
|
|
|
};
|
2015-12-18 10:06:33 +01:00
|
|
|
|
// iPad does not support beforeunload, therefore using unload
|
|
|
|
|
H5P.$window.one('beforeunload unload', function () {
|
|
|
|
|
// Only want to do this once
|
|
|
|
|
H5P.$window.off('pagehide beforeunload unload');
|
|
|
|
|
storeCurrentState();
|
2015-04-07 19:32:44 +02:00
|
|
|
|
});
|
2015-12-18 10:06:33 +01:00
|
|
|
|
// pagehide is used on iPad when tabs are switched
|
2015-12-16 09:50:58 +01:00
|
|
|
|
H5P.$window.on('pagehide', storeCurrentState);
|
2015-04-07 19:32:44 +02:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
})(H5P.jQuery);
|