diff --git a/h5p.classes.php b/h5p.classes.php index b6df5af..c4c703d 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -2731,11 +2731,14 @@ class H5PContentValidator { 'type' => 'group', 'fields' => $library['semantics'], ), FALSE); - $validkeys = array('library', 'params'); + $validkeys = array('library', 'params', 'uuid'); if (isset($semantics->extraAttributes)) { $validkeys = array_merge($validkeys, $semantics->extraAttributes); } $this->filterParams($value, $validkeys); + if (isset($value->uuid) && ! preg_match('/^\{?[A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12}\}?$/', $value->uuid)) { + unset($value->uuid); + } // Find all dependencies for this library $depkey = 'preloaded-' . $library['machineName']; diff --git a/js/h5p-event-dispatcher.js b/js/h5p-event-dispatcher.js index cb40cd6..e164564 100644 --- a/js/h5p-event-dispatcher.js +++ b/js/h5p-event-dispatcher.js @@ -8,6 +8,13 @@ var H5P = H5P || {}; H5P.Event = function(type, data) { this.type = type; this.data = data; + var bubbles = true; + this.preventBubbling = function() { + bubbles = false; + }; + this.getBubbles = function() { + return bubbles; + }; }; H5P.EventDispatcher = (function () { @@ -132,6 +139,7 @@ H5P.EventDispatcher = (function () { * argument */ this.trigger = function (event, eventData) { + console.log(event); if (event === undefined) { return; } @@ -148,6 +156,9 @@ H5P.EventDispatcher = (function () { for (var i = 0; i < triggers[event.type].length; i++) { triggers[event.type][i].listener.call(triggers[event.type][i].thisArg, event); } + if (event.getBubbles() && typeof self.parent === 'function' && typeof self.parent.trigger === 'function') { + self.parent.trigger(event); + } }; } diff --git a/js/h5p-x-api-event.js b/js/h5p-x-api-event.js index abc7cca..0d18d84 100644 --- a/js/h5p-x-api-event.js +++ b/js/h5p-x-api-event.js @@ -79,12 +79,20 @@ H5P.XAPIEvent.prototype.getVerb = function(full) { H5P.XAPIEvent.prototype.setObject = function(instance) { if (instance.contentId) { this.data.statement.object = { - 'id': H5PIntegration.contents['cid-' + instance.contentId].url, + 'id': this.getContentXAPIId(instance), 'objectType': 'Activity', 'extensions': { 'http://h5p.org/x-api/h5p-local-content-id': instance.contentId } }; + if (instance.h5pUUID) { + this.data.statement.object.extensions['http://h5p.org/x-api/h5p-uuid'] = instance.h5pUUID; + } + if (typeof instance.getH5PTitle === 'function') { + this.data.statement.object.description = { + "en-US": instance.getH5PTitle() + }; + } } else { // Not triggered by an H5P content type... @@ -94,6 +102,25 @@ H5P.XAPIEvent.prototype.setObject = function(instance) { } }; +/** + * Helperfunction to set the context part of the statement. + * + * @param {object} instance - the H5P instance + */ +H5P.XAPIEvent.prototype.setContext = function(instance) { + if (instance.parent && instance.parent.contentId || instance.parent.uuid) { + var parentId = instance.parent.uuid === undefined ? instance.parent.contentId : instance.parent.uuid; + this.data.statement.context = { + "parent": [ + { + "id": getContentXAPIId(instance.parent), + "objectType": "Activity" + } + ] + }; + } +}; + /** * Helper function to set the actor, email and name will be added automatically */ @@ -142,6 +169,17 @@ H5P.XAPIEvent.prototype.getScore = function() { return this.getVerifiedStatementValue(['result', 'score', 'raw']); }; +H5P.XAPIEvent.prototype.getContentXAPIId = function (instance) { + var xAPIId; + if (instance.contentId) { + xAPIId = H5PIntegration.contents['cid-' + instance.contentId].url; + if (instance.h5pUUID) { + xAPIId += '?uuid=' + instance.h5pUUID; + } + } + return xAPIId; +} + /** * Figure out if a property exists in the statement and return it * diff --git a/js/h5p.js b/js/h5p.js index c80d2a5..0370476 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -544,7 +544,7 @@ H5P.classFromName = function (name) { * @param {Object} The parent of this H5P * @return {Object} Instance. */ -H5P.newRunnable = function (library, contentId, $attachTo, skipResize) { +H5P.newRunnable = function (library, contentId, $attachTo, skipResize, parent) { var nameSplit, versionSplit; try { nameSplit = library.library.split(' ', 2); @@ -575,7 +575,16 @@ H5P.newRunnable = function (library, contentId, $attachTo, skipResize) { return H5P.error('Unable to find constructor for: ' + library.library); } - var instance = new constructor(library.params, contentId); + var extras = {}; + + if (library.uuid) { + extras.uuid = library.uuid; + } + if (parent) { + extras.parent = parent; + } + + var instance = new constructor(library.params, contentId, extras); if (instance.$ === undefined) { instance.$ = H5P.jQuery(instance); @@ -584,6 +593,12 @@ H5P.newRunnable = function (library, contentId, $attachTo, skipResize) { if (instance.contentId === undefined) { instance.contentId = contentId; } + if (instance.uuid === undefined && library.uuid) { + instance.uuid = library.uuid; + } + if (instance.parent === undefined && parent) { + instance.parent = parent; + } if ($attachTo !== undefined) { instance.attach($attachTo); @@ -1403,6 +1418,9 @@ H5P.createUUID = function() { }); }; +H5P.createH5PTitle = function(rawTitle) { + return H5P.jQuery('
').text(rawTitle).text().substr(0, 60); +}; H5P.jQuery(document).ready(function () { if (!H5P.preventInit) {