From ccc03782dffafc2fc08b1293026e650844e7c71e Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Tue, 5 May 2015 13:13:57 +0200 Subject: [PATCH] Cleaned up docs to make JSdoc generation more meaningful. --- js/h5p-event-dispatcher.js | 112 +++++---- js/h5p-x-api-event.js | 75 +++--- js/h5p-x-api.js | 56 +++-- js/h5p.js | 485 ++++++++++++++++++++++++------------- js/jquery.js | 6 + 5 files changed, 475 insertions(+), 259 deletions(-) diff --git a/js/h5p-event-dispatcher.js b/js/h5p-event-dispatcher.js index 1d52ef7..b6f59a6 100644 --- a/js/h5p-event-dispatcher.js +++ b/js/h5p-event-dispatcher.js @@ -1,21 +1,26 @@ -/** @namespace H5P */ var H5P = H5P || {}; /** - * The Event class for the EventDispatcher + * The Event class for the EventDispatcher. + * * @class + * @param {string} type + * @param {*} data + * @param {Object} [extras] + * @param {boolean} [extras.bubbles]  + * @param {boolean} [extras.external] */ H5P.Event = function(type, data, extras) { this.type = type; this.data = data; var bubbles = false; - + // Is this an external event? var external = false; - + // Is this event scheduled to be sent externally? var scheduledForExternal = false; - + if (extras === undefined) { extras = {}; } @@ -25,31 +30,29 @@ H5P.Event = function(type, data, extras) { if (extras.external === true) { external = true; } - + /** * Prevent this event from bubbling up to parent - * - * @returns {undefined} */ this.preventBubbling = function() { bubbles = false; }; - + /** * Get bubbling status - * - * @returns {Boolean} - true if bubbling false otherwise + * + * @returns {boolean} + * true if bubbling false otherwise */ this.getBubbles = function() { return bubbles; }; - + /** * Try to schedule an event for externalDispatcher - * - * @returns {Boolean} - * - true if external and not already scheduled - * - false otherwise + * + * @returns {boolean} + * true if external and not already scheduled, otherwise false */ this.scheduleForExternal = function() { if (external && !scheduledForExternal) { @@ -60,18 +63,28 @@ H5P.Event = function(type, data, extras) { }; }; +/** + * Callback type for event listeners. + * + * @callback H5P.EventCallback + * @param {H5P.Event} event + */ + H5P.EventDispatcher = (function () { /** * The base of the event system. * Inherit this class if you want your H5P to dispatch events. + * * @class + * @memberof H5P */ function EventDispatcher() { var self = this; /** * Keep track of listeners for each event. + * * @private * @type {Object} */ @@ -80,11 +93,14 @@ H5P.EventDispatcher = (function () { /** * Add new event listener. * - * @public - * @throws {TypeError} listener - Must be a function - * @param {String} type - Event type - * @param {Function} listener - Event listener - * @param {Function} thisArg - Optionally specify the this value when calling listener. + * @throws {TypeError} + * listener must be a function + * @param {string} type + * Event type + * @param {H5P.EventCallback} listener + * Event listener + * @param {function} thisArg + * Optionally specify the this value when calling listener. */ this.on = function (type, listener, thisArg) { if (thisArg === undefined) { @@ -110,11 +126,14 @@ H5P.EventDispatcher = (function () { /** * Add new event listener that will be fired only once. * - * @public - * @throws {TypeError} listener - must be a function - * @param {String} type - Event type - * @param {Function} listener - Event listener - * @param {Function} thisArg - Optionally specify the this value when calling listener. + * @throws {TypeError} + * listener must be a function + * @param {string} type + * Event type + * @param {H5P.EventCallback} listener + * Event listener + * @param {function} thisArg + * Optionally specify the this value when calling listener. */ this.once = function (type, listener, thisArg) { if (thisArg === undefined) { @@ -136,10 +155,12 @@ H5P.EventDispatcher = (function () { * Remove event listener. * If no listener is specified, all listeners will be removed. * - * @public - * @throws {TypeError} listener - must be a function - * @param {String} type - Event type - * @param {Function} listener - Event listener + * @throws {TypeError} + * listener must be a function + * @param {string} type + * Event type + * @param {H5P.EventCallback} listener + * Event listener */ this.off = function (type, listener) { if (listener !== undefined && !(listener instanceof Function)) { @@ -175,45 +196,48 @@ H5P.EventDispatcher = (function () { /** * Dispatch event. * - * @public - * @param {String|Function} event - Event object or event type as string - * @param {mixed} eventData - * Custom event data(used when event type as string is used as first - * argument + * @param {string|H5P.Event} event + * Event object or event type as string + * @param {*} [eventData] + * Custom event data(used when event type as string is used as first + * argument). + * @param {Object} [extras] + * @param {boolean} [extras.bubbles]  + * @param {boolean} [extras.external] */ this.trigger = function (event, eventData, extras) { if (event === undefined) { return; } - if (typeof event === 'string') { + if (typeof event === 'string') { // TODO: Check instanceof String as well? event = new H5P.Event(event, eventData, extras); } else if (eventData !== undefined) { event.data = eventData; } - + // Check to see if this event should go externally after all triggering and bubbling is done var scheduledForExternal = event.scheduleForExternal(); - + if (triggers[event.type] !== undefined) { // Call all listeners for (var i = 0; i < triggers[event.type].length; i++) { triggers[event.type][i].listener.call(triggers[event.type][i].thisArg, event); } } - + if (triggers['*'] !== undefined) { // Call all * listeners - for (var i = 0; i < triggers['*'].length; i++) { - triggers['*'][i].listener.call(triggers['*'][i].thisArg, event); + for (var j = 0; j < triggers['*'].length; j++) { + triggers['*'][j].listener.call(triggers['*'][j].thisArg, event); } } - + // Bubble - if (event.getBubbles() && self.parent instanceof H5P.EventDispatcher && typeof self.parent.trigger === 'function') { + if (event.getBubbles() && self.parent instanceof H5P.EventDispatcher && typeof self.parent.trigger === 'function') { // TODO: Check instanceof Function as well? self.parent.trigger(event); } - + if (scheduledForExternal) { H5P.externalDispatcher.trigger(event); } diff --git a/js/h5p-x-api-event.js b/js/h5p-x-api-event.js index d26a3b7..c28b4d3 100644 --- a/js/h5p-x-api-event.js +++ b/js/h5p-x-api-event.js @@ -1,11 +1,12 @@ var H5P = H5P || {}; /** - * Constructor for xAPI events + * Used for xAPI events. * * @class + * @extends H5P.Event */ -H5P.XAPIEvent = function() { +H5P.XAPIEvent = function () { H5P.Event.call(this, 'xAPI', {'statement': {}}, {bubbles: true, external: true}); }; @@ -13,12 +14,12 @@ H5P.XAPIEvent.prototype = Object.create(H5P.Event.prototype); H5P.XAPIEvent.prototype.constructor = H5P.XAPIEvent; /** - * Helperfunction to set scored result statements + * Set scored result statements. * - * @param {int} score - * @param {int} maxScore + * @param {number} score + * @param {number} maxScore */ -H5P.XAPIEvent.prototype.setScoredResult = function(score, maxScore) { +H5P.XAPIEvent.prototype.setScoredResult = function (score, maxScore) { this.data.statement.result = { 'score': { 'min': 0, @@ -29,13 +30,14 @@ H5P.XAPIEvent.prototype.setScoredResult = function(score, maxScore) { }; /** - * Helperfunction to set a verb. + * Set a verb. * * @param {string} verb - * Verb in short form, one of the verbs defined at - * http://adlnet.gov/expapi/verbs/ + * Verb in short form, one of the verbs defined at + * {@link http://adlnet.gov/expapi/verbs/|ADL xAPI Vocabulary} + * */ -H5P.XAPIEvent.prototype.setVerb = function(verb) { +H5P.XAPIEvent.prototype.setVerb = function (verb) { if (H5P.jQuery.inArray(verb, H5P.XAPIEvent.allowedXAPIVerbs) !== -1) { this.data.statement.verb = { 'id': 'http://adlnet.gov/expapi/verbs/' + verb, @@ -50,13 +52,15 @@ H5P.XAPIEvent.prototype.setVerb = function(verb) { }; /** - * Helperfunction to get the statements verb id + * Get the statements verb id. * * @param {boolean} full - * if true the full verb id prefixed by http://adlnet.gov/expapi/verbs/ will be returned - * @returns {string} - Verb or null if no verb with an id has been defined + * if true the full verb id prefixed by http://adlnet.gov/expapi/verbs/ + * will be returned + * @returns {string} + * Verb or null if no verb with an id has been defined */ -H5P.XAPIEvent.prototype.getVerb = function(full) { +H5P.XAPIEvent.prototype.getVerb = function (full) { var statement = this.data.statement; if ('verb' in statement) { if (full === true) { @@ -70,13 +74,14 @@ H5P.XAPIEvent.prototype.getVerb = function(full) { }; /** - * Helperfunction to set the object part of the statement. + * Set the object part of the statement. * * The id is found automatically (the url to the content) * - * @param {object} instance - the H5P instance + * @param {Object} instance + * The H5P instance */ -H5P.XAPIEvent.prototype.setObject = function(instance) { +H5P.XAPIEvent.prototype.setObject = function (instance) { if (instance.contentId) { this.data.statement.object = { 'id': this.getContentXAPIId(instance), @@ -107,11 +112,12 @@ H5P.XAPIEvent.prototype.setObject = function(instance) { }; /** - * Helperfunction to set the context part of the statement. + * Set the context part of the statement. * - * @param {object} instance - the H5P instance + * @param {Object} instance + * The H5P instance */ -H5P.XAPIEvent.prototype.setContext = function(instance) { +H5P.XAPIEvent.prototype.setContext = function (instance) { if (instance.parent && (instance.parent.contentId || instance.parent.subContentId)) { var parentId = instance.parent.subContentId === undefined ? instance.parent.contentId : instance.parent.subContentId; this.data.statement.context = { @@ -128,9 +134,9 @@ H5P.XAPIEvent.prototype.setContext = function(instance) { }; /** - * Helper function to set the actor, email and name will be added automatically + * Set the actor. Email and name will be added automatically. */ -H5P.XAPIEvent.prototype.setActor = function() { +H5P.XAPIEvent.prototype.setActor = function () { if (H5PIntegration.user !== undefined) { this.data.statement.actor = { 'name': H5PIntegration.user.name, @@ -160,7 +166,8 @@ H5P.XAPIEvent.prototype.setActor = function() { /** * Get the max value of the result - score part of the statement * - * @returns {int} the max score, or null if not defined + * @returns {number} + * The max score, or null if not defined */ H5P.XAPIEvent.prototype.getMaxScore = function() { return this.getVerifiedStatementValue(['result', 'score', 'max']); @@ -169,12 +176,19 @@ H5P.XAPIEvent.prototype.getMaxScore = function() { /** * Get the raw value of the result - score part of the statement * - * @returns {int} the max score, or null if not defined + * @returns {number} + * The score, or null if not defined */ H5P.XAPIEvent.prototype.getScore = function() { return this.getVerifiedStatementValue(['result', 'score', 'raw']); }; +/** + * Get content xAPI ID. + * + * @param {Object} instance + * The H5P instance + */ H5P.XAPIEvent.prototype.getContentXAPIId = function (instance) { var xAPIId; if (instance.contentId && H5PIntegration && H5PIntegration.contents) { @@ -184,15 +198,16 @@ H5P.XAPIEvent.prototype.getContentXAPIId = function (instance) { } } return xAPIId; -} +}; /** * Figure out if a property exists in the statement and return it * - * @param {array} keys - * List describing the property we're looking for. For instance - * ['result', 'score', 'raw'] for result.score.raw - * @returns the value of the property if it is set, null otherwise + * @param {string[]} keys + * List describing the property we're looking for. For instance + * ['result', 'score', 'raw'] for result.score.raw + * @returns {*} + * The value of the property if it is set, null otherwise. */ H5P.XAPIEvent.prototype.getVerifiedStatementValue = function(keys) { var val = this.data.statement; @@ -206,7 +221,7 @@ H5P.XAPIEvent.prototype.getVerifiedStatementValue = function(keys) { }; /** - * List of verbs defined at http://adlnet.gov/expapi/verbs/ + * List of verbs defined at {@link http://adlnet.gov/expapi/verbs/|ADL xAPI Vocabulary} * * @type Array */ diff --git a/js/h5p-x-api.js b/js/h5p-x-api.js index 87a6267..ea5ba79 100644 --- a/js/h5p-x-api.js +++ b/js/h5p-x-api.js @@ -1,31 +1,41 @@ var H5P = H5P || {}; -// Create object where external code may register and listen for H5P Events +/** + * The external event dispatcher. Others, outside of H5P may register and + * listen for H5P Events here. + * + * @type {H5P.EventDispatcher} + */ H5P.externalDispatcher = new H5P.EventDispatcher(); // EventDispatcher extensions /** - * Helper function for triggering xAPI added to the EventDispatcher + * Helper function for triggering xAPI added to the EventDispatcher. * - * @param {string} verb - the short id of the verb we want to trigger - * @param {oject} extra - extra properties for the xAPI statement + * @param {string} verb + * The short id of the verb we want to trigger + * @param {Oject} [extra] + * Extra properties for the xAPI statement */ -H5P.EventDispatcher.prototype.triggerXAPI = function(verb, extra) { +H5P.EventDispatcher.prototype.triggerXAPI = function (verb, extra) { this.trigger(this.createXAPIEventTemplate(verb, extra)); }; /** - * Helper function to create event templates added to the EventDispatcher + * Helper function to create event templates added to the EventDispatcher. * * Will in the future be used to add representations of the questions to the * statements. * - * @param {string} verb - verb id in short form - * @param {object} extra - Extra values to be added to the statement - * @returns {Function} - XAPIEvent object + * @param {string} verb + * Verb id in short form + * @param {Object} [extra] + * Extra values to be added to the statement + * @returns {H5P.XAPIEvent} + * Instance */ -H5P.EventDispatcher.prototype.createXAPIEventTemplate = function(verb, extra) { +H5P.EventDispatcher.prototype.createXAPIEventTemplate = function (verb, extra) { var event = new H5P.XAPIEvent(); event.setActor(); @@ -49,22 +59,28 @@ H5P.EventDispatcher.prototype.createXAPIEventTemplate = function(verb, extra) { * * DEPRECATED - USE triggerXAPIScored instead * - * @param {int} score - will be set as the 'raw' value of the score object - * @param {int} maxScore - will be set as the "max" value of the score object + * @deprecated + * since 1.5, use triggerXAPIScored instead. + * @param {number} score + * Will be set as the 'raw' value of the score object + * @param {number} maxScore + * will be set as the "max" value of the score object */ -H5P.EventDispatcher.prototype.triggerXAPICompleted = function(score, maxScore) { +H5P.EventDispatcher.prototype.triggerXAPICompleted = function (score, maxScore) { this.triggerXAPIScored(score, maxScore, 'completed'); }; /** * Helper function to create scored xAPI events * - * - * @param {int} score - will be set as the 'raw' value of the score object - * @param {int} maxScore - will be set as the "max" value of the score object - * @param {string} verb - short form of adl verb + * @param {number} score + * Will be set as the 'raw' value of the score object + * @param {number} maxScore + * Will be set as the "max" value of the score object + * @param {string} verb + * Short form of adl verb */ -H5P.EventDispatcher.prototype.triggerXAPIScored = function(score, maxScore, verb) { +H5P.EventDispatcher.prototype.triggerXAPIScored = function (score, maxScore, verb) { var event = this.createXAPIEventTemplate(verb); event.setScoredResult(score, maxScore); this.trigger(event); @@ -73,9 +89,9 @@ H5P.EventDispatcher.prototype.triggerXAPIScored = function(score, maxScore, verb /** * Internal H5P function listening for xAPI completed events and stores scores * - * @param {function} event - xAPI event + * @param {H5P.XAPIEvent} event */ -H5P.xAPICompletedListener = function(event) { +H5P.xAPICompletedListener = function (event) { if (event.getVerb() === 'completed' && !event.getVerifiedStatementValue(['context', 'contextActivities', 'parent'])) { var score = event.getScore(); var maxScore = event.getMaxScore(); diff --git a/js/h5p.js b/js/h5p.js index b8a09e0..9a9554a 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -1,17 +1,34 @@ /*jshint multistr: true */ // TODO: Should we split up the generic parts needed by the editor(and others), and the parts needed to "run" H5Ps? + +/** @namespace */ var H5P = H5P || {}; -// Determine if we're inside an iframe. +/** + * Tells us if we're inside of an iframe. + * @member {boolean} + */ H5P.isFramed = (window.self !== window.top); -// Useful jQuery object. +/** + * jQuery instance of current window. + * @member {H5P.jQuery} + */ H5P.$window = H5P.jQuery(window); +/** + * List over H5P instances on the current page. + * @member {Array} + */ H5P.instances = []; // Detect if we support fullscreen, and what prefix to use. if (document.documentElement.requestFullScreen) { + /** + * Browser prefix to use when entering fullscreen mode. + * undefined means no fullscreen support. + * @member {string} + */ H5P.fullScreenBrowserPrefix = ''; } else if (document.documentElement.webkitRequestFullScreen) { @@ -30,34 +47,36 @@ else if (document.documentElement.msRequestFullscreen) { H5P.fullScreenBrowserPrefix = 'ms'; } -/** @const {Number} */ +/** @const {number} */ H5P.DISABLE_NONE = 0; -/** @const {Number} */ +/** @const {number} */ H5P.DISABLE_FRAME = 1; -/** @const {Number} */ +/** @const {number} */ H5P.DISABLE_DOWNLOAD = 2; -/** @const {Number} */ +/** @const {number} */ H5P.DISABLE_EMBED = 4; -/** @const {Number} */ +/** @const {number} */ H5P.DISABLE_COPYRIGHT = 8; -/** @const {Number} */ +/** @const {number} */ H5P.DISABLE_ABOUT = 16; /** * Keep track of when the H5Ps where started. * - * @type {Array} + * @type {Object[]} */ H5P.opened = {}; /** * Initialize H5P content. * Scans for ".h5p-content" in the document and initializes H5P instances where found. + * + * @param {Object} target DOM Element */ H5P.init = function (target) { // Useful jQuery object. @@ -67,8 +86,12 @@ H5P.init = function (target) { // Determine if we can use full screen if (H5P.canHasFullScreen === undefined) { - // Restricts fullscreen when embedded. - // (embedded doesn't support semi-fullscreen solution) + /** + * 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} + */ H5P.canHasFullScreen = (H5P.isFramed && H5P.externalEmbed !== false) ? ((document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled) ? true : false) : true; // 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 @@ -348,9 +371,15 @@ H5P.getHeadTags = function (contentId) { ''; }; +/** + * When embedded the communicator helps talk to the parent page. + * + * @type {Communicator} + */ H5P.communicator = (function () { /** * @class + * @private */ function Communicator() { var self = this; @@ -373,9 +402,8 @@ H5P.communicator = (function () { /** * Register action listener. * - * @public - * @param {String} action What you are waiting for - * @param {Function} handler What you want done + * @param {string} action What you are waiting for + * @param {function} handler What you want done */ self.on = function (action, handler) { actionHandlers[action] = handler; @@ -384,8 +412,7 @@ H5P.communicator = (function () { /** * Send a message to the all mighty father. * - * @public - * @param {String} action + * @param {string} action * @param {Object} [data] payload */ self.send = function (action, data) { @@ -404,13 +431,12 @@ H5P.communicator = (function () { })(); /** - * Enable full screen for the given h5p. + * Enter fullscreen for the given H5P instance. * - * @param {jQuery} $element Content container. - * @param {object} instance + * @param {H5P.jQuery} $element Content container. + * @param {Object} instance * @param {function} exitCallback Callback function called when user exits fullscreen. - * @param {jQuery} $body For internal use. Gives the body of the iframe. - * @returns {undefined} + * @param {H5P.jQuery} $body For internal use. Gives the body of the iframe. */ H5P.fullScreen = function ($element, instance, exitCallback, body) { if (H5P.exitFullScreen !== undefined) { @@ -450,7 +476,8 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) { /** * Prepare for resize by setting the correct styles. * - * @param {String} classes CSS + * @private + * @param {string} classes CSS */ var before = function (classes) { $classes.addClass(classes); @@ -464,6 +491,8 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) { /** * Gets called when fullscreen mode has been entered. * Resizes and sets focus on content. + * + * @private */ var entered = function () { // Do not rely on window resize events. @@ -476,7 +505,8 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) { * Gets called when fullscreen mode has been exited. * Resizes and sets focus on content. * - * @param {String} classes CSS + * @private + * @param {string} classes CSS */ var done = function (classes) { H5P.isFullscreen = false; @@ -561,14 +591,15 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) { }; /** - * Find the path to the content files based on the id of the content + * Find the path to the content files based on the id of the content. + * Also identifies and returns absolute paths. * - * Also identifies and returns absolute paths - * - * @param string path - * Absolute path to a file, or relative path to a file in the content folder - * @param contentId - * Id of the content requesting a path + * @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. */ H5P.getPath = function (path, contentId) { var hasProtocol = function (path) { @@ -601,10 +632,14 @@ H5P.getPath = function (path, contentId) { * THIS FUNCTION IS DEPRECATED, USE getPath INSTEAD * Will be remove march 2016. * - * Find the path to the content files folder based on the id of the content + * Find the path to the content files folder based on the id of the content * - * @param contentId - * Id of the content requesting a path + * @deprecated + * Will be removed march 2016. + * @param contentId + * Id of the content requesting the path + * @returns {string} + * URL */ H5P.getContentPath = function (contentId) { return H5PIntegration.url + '/content/' + contentId; @@ -613,32 +648,38 @@ H5P.getContentPath = function (contentId) { /** * Get library class constructor from H5P by classname. * Note that this class will only work for resolve "H5P.NameWithoutDot". - * Also check out: H5P.newRunnable + * Also check out {@link H5P.newRunnable} * * Used from libraries to construct instances of other libraries' objects by name. * * @param {string} name Name of library - * @returns Class constructor + * @returns {Object} Class constructor */ H5P.classFromName = function (name) { var arr = name.split("."); - return this[arr[arr.length-1]]; + return this[arr[arr.length - 1]]; }; /** * A safe way of creating a new instance of a runnable H5P. * - * TODO: Should we check if version matches the library? - * TODO: Dynamically try to load libraries currently not loaded? That will require a callback. - * - * @param {Object} library Library/action object form params. - * @param {Number} contentId - * @param {jQuery} $attachTo An optional element to attach the instance to. - * @param {Boolean} skipResize Optionally skip triggering of the resize event after attaching. - * @param {Object} extras - extra params for the H5P content constructor - * @return {Object} Instance. + * @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. */ H5P.newRunnable = function (library, contentId, $attachTo, skipResize, extras) { + // TODO: Should we check if version matches the library? + // TODO: Dynamically try to load libraries currently not loaded? That will require a callback. + var nameSplit, versionSplit, machineName; try { nameSplit = library.library.split(' ', 2); @@ -723,10 +764,9 @@ H5P.newRunnable = function (library, contentId, $attachTo, skipResize, extras) { }; /** - * Used to print useful error messages. + * Used to print useful error messages. (to JavaScript error console) * - * @param {mixed} err Error to print. - * @returns {undefined} + * @param {*} err Error to print. */ H5P.error = function (err) { if (window.console !== undefined && console.error !== undefined) { @@ -737,10 +777,14 @@ H5P.error = function (err) { /** * Translate text strings. * - * @param {String} key Translation identifier, may only contain a-zA-Z0-9. No spaces or special chars. - * @param {Object} vars Data for placeholders. - * @param {String} ns Translation namespace. Defaults to H5P. - * @returns {String} Text + * @param {string} key + * Translation identifier, may only contain a-zA-Z0-9. No spaces or special chars. + * @param {Object} vars + * Data for placeholders. + * @param {string} ns + * Translation namespace. Defaults to H5P. + * @returns {string} + * Translated text */ H5P.t = function (key, vars, ns) { if (ns === undefined) { @@ -767,6 +811,19 @@ H5P.t = function (key, vars, ns) { return translation; }; +/** + * 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. + */ H5P.Dialog = function (name, title, content, $element) { var self = this; var $dialog = H5P.jQuery('
\ @@ -810,9 +867,14 @@ H5P.Dialog = function (name, title, content, $element) { /** * Gather copyright information and display in a dialog over the content. * - * @param {jQuery} $element to insert dialog after. - * @param {object} instance to get copyright information from. - * @returns {undefined} + * @param {H5P.jQuery} $element + * DOM Element to insert dialog after. + * @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 */ H5P.openCopyrightsDialog = function ($element, instance, parameters, contentId) { var copyrights; @@ -843,10 +905,12 @@ H5P.openCopyrightsDialog = function ($element, instance, parameters, contentId) /** * Gather a flat list of copyright information from the given parameters. * - * @param {H5P.ContentCopyrights} info Used to collect all information in. - * @param {(Object|Arrray)} parameters To search for file objects in. - * @param {Number} contentId Used to insert thumbnails for images. - * @returns {undefined} + * @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. */ H5P.findCopyrights = function (info, parameters, contentId) { // Cycle through parameters @@ -887,9 +951,12 @@ H5P.findCopyrights = function (info, parameters, contentId) { /** * Recursive function for checking if content has copyrights * - * @param {(Object|Arrray)} parameters To search for file objects in. - * @param {Number} contentId Used to insert thumbnails for images. - * @returns {boolean} Returns true if complete copyright information was found. + * @param {(Object|Array)} parameters + * To search for file objects in. + * @param {number} contentId + * Used to insert thumbnails for images. + * @returns {boolean} + * Returns true if complete copyright information was found. */ H5P.hasCopyrights = function (parameters, contentId) { // Cycle through parameters @@ -929,9 +996,16 @@ H5P.hasCopyrights = function (parameters, contentId) { /** * Display a dialog containing the embed code. * - * @param {jQuery} $element to insert dialog after. - * @param {string} embed code. - * @returns {undefined} + * @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 */ H5P.openEmbedDialog = function ($element, embedCode, resizeCode, size) { var fullEmbedCode = embedCode + resizeCode; @@ -1010,6 +1084,8 @@ H5P.openEmbedDialog = function ($element, embedCode, resizeCode, size) { /** * Copyrights for a H5P Content Library. + * + * @class */ H5P.ContentCopyrights = function () { var label; @@ -1017,16 +1093,16 @@ H5P.ContentCopyrights = function () { var content = []; /** - * Public. Set label. + * Set label. * - * @param {String} newLabel + * @param {string} newLabel */ this.setLabel = function (newLabel) { label = newLabel; }; /** - * Public. Add sub content. + * Add sub content. * * @param {H5P.MediaCopyright} newMedia */ @@ -1037,7 +1113,7 @@ H5P.ContentCopyrights = function () { }; /** - * Public. Add sub content. + * Add sub content. * * @param {H5P.ContentCopyrights} newContent */ @@ -1048,9 +1124,9 @@ H5P.ContentCopyrights = function () { }; /** - * Public. Print content copyright. + * Print content copyright. * - * @returns {String} HTML. + * @returns {string} HTML. */ this.toString = function () { var html = ''; @@ -1083,20 +1159,26 @@ H5P.ContentCopyrights = function () { /** * A ordered list of copyright fields for media. * - * @param {Object} copyright information fields. - * @param {Object} labels translation. Optional. - * @param {Array} order of fields. Optional. - * @param {Object} extraFields for copyright. Optional. + * @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. */ H5P.MediaCopyright = function (copyright, labels, order, extraFields) { var thumbnail; var list = new H5P.DefinitionList(); /** - * Private. Get translated label for field. + * Get translated label for field. * - * @param {String} fieldName - * @return {String} + * @private + * @param {string} fieldName + * @returns {string} */ var getLabel = function (fieldName) { if (labels === undefined || labels[fieldName] === undefined) { @@ -1107,10 +1189,12 @@ H5P.MediaCopyright = function (copyright, labels, order, extraFields) { }; /** - * Private. Get humanized value for field. + * Get humanized value for field. * - * @param {String} fieldName - * @return {String} + * @private + * @param {string} fieldName + * @param {string} value + * @returns {string} */ var humanizeValue = function (fieldName, value) { if (fieldName === 'license') { @@ -1142,7 +1226,7 @@ H5P.MediaCopyright = function (copyright, labels, order, extraFields) { } /** - * Public. Set thumbnail. + * Set thumbnail. * * @param {H5P.Thumbnail} newThumbnail */ @@ -1151,10 +1235,10 @@ H5P.MediaCopyright = function (copyright, labels, order, extraFields) { }; /** - * Public. Checks if this copyright is undisclosed. + * Checks if this copyright is undisclosed. * I.e. only has the license attribute set, and it's undisclosed. * - * @returns {Boolean} + * @returns {boolean} */ this.undisclosed = function () { if (list.size() === 1) { @@ -1167,9 +1251,9 @@ H5P.MediaCopyright = function (copyright, labels, order, extraFields) { }; /** - * Public. Print media copyright. + * Print media copyright. * - * @returns {String} HTML. + * @returns {string} HTML. */ this.toString = function () { var html = ''; @@ -1191,7 +1275,11 @@ H5P.MediaCopyright = function (copyright, labels, order, extraFields) { }; }; -// Translate table for copyright license codes. +/** + * Maps copyright license codes to their human readable counterpart. + * + * @type {Object} + */ H5P.copyrightLicenses = { 'U': 'Undisclosed', 'CC BY': 'Attribution', @@ -1208,11 +1296,12 @@ H5P.copyrightLicenses = { }; /** - * Simple class for creating thumbnails for images. + * A simple and elegant class for creating thumbnails of images. * - * @param {String} source - * @param {Number} width - * @param {Number} height + * @class + * @param {string} source + * @param {number} width + * @param {number} height */ H5P.Thumbnail = function (source, width, height) { var thumbWidth, thumbHeight = 100; @@ -1221,9 +1310,9 @@ H5P.Thumbnail = function (source, width, height) { } /** - * Public. Print thumbnail. + * Print thumbnail. * - * @returns {String} HTML. + * @returns {string} HTML. */ this.toString = function () { return '' + H5P.t('thumbnail') + ''; @@ -1231,7 +1320,11 @@ H5P.Thumbnail = function (source, width, height) { }; /** - * Simple data class for storing a single field. + * Simple data structure class for storing a single field. + * + * @class + * @param {string} label + * @param {string} value */ H5P.Field = function (label, value) { /** @@ -1255,12 +1348,14 @@ H5P.Field = function (label, value) { /** * Simple class for creating a definition list. + * + * @class */ H5P.DefinitionList = function () { var fields = []; /** - * Public. Add field to list. + * Add field to list. * * @param {H5P.Field} field */ @@ -1269,28 +1364,28 @@ H5P.DefinitionList = function () { }; /** - * Public. Get Number of fields. + * Get Number of fields. * - * @returns {Number} + * @returns {number} */ this.size = function () { return fields.length; }; /** - * Public. Get field at given index. + * Get field at given index. * - * @param {Number} index - * @returns {Object} + * @param {number} index + * @returns {H5P.Field} */ this.get = function (index) { return fields[index]; }; /** - * Public. Print definition list. + * Print definition list. * - * @returns {String} HTML. + * @returns {string} HTML. */ this.toString = function () { var html = ''; @@ -1306,14 +1401,26 @@ H5P.DefinitionList = function () { * THIS FUNCTION/CLASS IS DEPRECATED AND WILL BE REMOVED. * * Helper object for keeping coordinates in the same format all over. + * + * @deprecated + * Will be removed march 2016. + * @class + * @param {number} x + * @param {number} y + * @param {number} w + * @param {number} h */ H5P.Coords = function (x, y, w, h) { if ( !(this instanceof H5P.Coords) ) return new H5P.Coords(x, y, w, h); + /** @member {number} */ this.x = 0; + /** @member {number} */ this.y = 0; + /** @member {number} */ this.w = 1; + /** @member {number} */ this.h = 1; if (typeof(x) === 'object') { @@ -1342,10 +1449,10 @@ H5P.Coords = function (x, y, w, h) { * Parse library string into values. * * @param {string} library - * library in the format "machineName majorVersion.minorVersion" - * @returns - * library as an object with machineName, majorVersion and minorVersion properties - * return false if the library parameter is invalid + * 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 */ H5P.libraryFromString = function (library) { var regExp = /(.+)\s(\d)+\.(\d)$/g; @@ -1365,9 +1472,10 @@ H5P.libraryFromString = function (library) { /** * Get the path to the library * - * @param {String} library + * @param {string} library * The library identifier in the format "machineName-majorVersion.minorVersion". - * @returns {String} The full path to the library. + * @returns {string} + * The full path to the library. */ H5P.getLibraryPath = function (library) { return H5PIntegration.url + '/libraries/' + library; @@ -1375,13 +1483,15 @@ H5P.getLibraryPath = function (library) { /** * Recursivly clone the given object. - * TODO: Consider if this needs to be in core. Doesn't $.extend do the same? * - * @param {object} object Object to clone. - * @param {type} recursive - * @returns {object} A clone of object. + * @param {Object|Array} object + * Object to clone. + * @param {boolean} [recursive] + * @returns {Object|Array} + * A clone of object. */ H5P.cloneObject = function (object, recursive) { + // TODO: Consider if this needs to be in core. Doesn't $.extend do the same? var clone = object instanceof Array ? [] : {}; for (var i in object) { @@ -1400,22 +1510,23 @@ H5P.cloneObject = function (object, recursive) { /** * Remove all empty spaces before and after the value. - * 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? * - * @param {String} value - * @returns {@exp;value@call;replace} + * @param {string} value + * @returns {string} */ H5P.trim = function (value) { return value.replace(/^\s+|\s+$/g, ''); + + // 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? }; /** - * Check if javascript path/key is loaded. + * Check if JavaScript path/key is loaded. * - * @param {String} path - * @returns {Boolean} + * @param {string} path + * @returns {boolean} */ H5P.jsLoaded = function (path) { H5PIntegration.loadedJs = H5PIntegration.loadedJs || []; @@ -1425,8 +1536,8 @@ H5P.jsLoaded = function (path) { /** * Check if styles path/key is loaded. * - * @param {String} path - * @returns {Boolean} + * @param {string} path + * @returns {boolean} */ H5P.cssLoaded = function (path) { H5PIntegration.loadedCss = H5PIntegration.loadedCss || []; @@ -1435,12 +1546,14 @@ H5P.cssLoaded = function (path) { /** * Shuffle an array in place. - * TODO: Consider if this should be a part of core. I'm guessing very few libraries are going to use it. * - * @param {array} array Array to shuffle - * @returns {array} The passed array is returned for chaining. + * @param {Array} array + * Array to shuffle + * @returns {Array} + * The passed array is returned for chaining. */ H5P.shuffleArray = function (array) { + // TODO: Consider if this should be a part of core. I'm guessing very few libraries are going to use it. if (!(array instanceof Array)) { return; } @@ -1458,21 +1571,26 @@ H5P.shuffleArray = function (array) { }; /** - * DEPRECATED! Do not use this function directly, trigger the finish event - * instead. - * * Post finished results for user. * - * @param {Number} contentId - * @param {Number} score achieved - * @param {Number} maxScore that can be achieved - * @param {Number} time optional reported time usage + * @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 */ H5P.setFinished = function (contentId, score, maxScore, time) { if (H5PIntegration.postUserStatistics === true) { /** * Return unix timestamp for the given JS Date. * + * @private * @param {Date} date * @returns {Number} */ @@ -1518,12 +1636,14 @@ if (String.prototype.trim === undefined) { * * Helper function that triggers an event if the instance supports event handling * - * @param {function} instance - * An H5P instance + * @param {Object} instance + * Instance of H5P content * @param {string} eventType - * The event type + * Type of event to trigger + * @param {*} data + * @param {Object} extras */ -H5P.trigger = function(instance, eventType, data, extras) { +H5P.trigger = function (instance, eventType, data, extras) { // Try new event system first if (instance.trigger !== undefined) { instance.trigger(eventType, data, extras); @@ -1540,14 +1660,14 @@ H5P.trigger = function(instance, eventType, data, extras) { * Helper function that registers an event handler for an event type if * the instance supports event handling * - * @param {function} instance - * An h5p instance + * @param {Object} instance + * Instance of H5P content * @param {string} eventType - * The event type - * @param {function} handler - * Callback that gets triggered for events of the specified type + * Type of event to listen for + * @param {H5P.EventCallback} handler + * Callback that gets triggered for events of the specified type */ -H5P.on = function(instance, eventType, handler) { +H5P.on = function (instance, eventType, handler) { // Try new event system first if (instance.on !== undefined) { instance.on(eventType, handler); @@ -1559,18 +1679,25 @@ H5P.on = function(instance, eventType, handler) { }; /** - * Create UUID + * Generate random UUID * - * @returns {String} UUID + * @returns {string} UUID */ -H5P.createUUID = function() { +H5P.createUUID = function () { 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); }); }; -H5P.createTitle = function(rawTitle, maxLength) { +/** + * Create title + * + * @param {string} rawTitle + * @param {number} maxLength + * @returns {string} + */ +H5P.createTitle = function (rawTitle, maxLength) { if (!rawTitle) { return ''; } @@ -1648,11 +1775,14 @@ H5P.createTitle = function(rawTitle, maxLength) { /** * Get user data for given content. * - * @public - * @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. + * @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. */ H5P.getUserData = function (contentId, dataId, done, subContentId) { if (!subContentId) { @@ -1700,19 +1830,33 @@ H5P.createTitle = function(rawTitle, maxLength) { } }; + /** + * Async error handling. + * + * @callback H5P.ErrorCallback + * @param {*} error + */ + /** * Set user data for given content. * - * @public - * @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 - object holding the following properties: - * - {string} [subContentId] Identifies which data belongs to sub content. - * - {boolean} [preloaded=true] If the data should be loaded when content is loaded. - * - {boolean} [deleteOnChange=false] If the data should be invalidated when the content changes. - * - {function} [errorCallback] Callback with error as parameters. - * - {boolean} [async=true] + * @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] */ H5P.setUserData = function (contentId, dataId, data, extras) { var options = H5P.jQuery.extend(true, {}, { @@ -1755,10 +1899,12 @@ H5P.createTitle = function(rawTitle, maxLength) { /** * Delete user data for given content. * - * @public - * @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. + * @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. */ H5P.deleteUserData = function (contentId, dataId, subContentId) { if (!subContentId) { @@ -1776,6 +1922,10 @@ H5P.createTitle = function(rawTitle, maxLength) { // Init H5P when page is fully loadded $(document).ready(function () { + /** + * Prevent H5P Core from initializing. Must be overriden before document ready. + * @member {boolean} H5P.preventInit + */ if (!H5P.preventInit) { // Note that this start script has to be an external resource for it to // load in correct order in IE9. @@ -1799,6 +1949,11 @@ H5P.createTitle = function(rawTitle, maxLength) { }); } + /** + * Indicates if H5P is embedded on an external page using iframe. + * @member {boolean} H5P.externalEmbed + */ + // Relay events to top window. if (H5P.isFramed && H5P.externalEmbed === false) { H5P.externalDispatcher.on('*', window.top.H5P.externalDispatcher.trigger); diff --git a/js/jquery.js b/js/jquery.js index 14eeb18..d821060 100644 --- a/js/jquery.js +++ b/js/jquery.js @@ -6,4 +6,10 @@ return(!i||i!==r&&!b.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,a // Snap this specific version of jQuery into H5P. jQuery.noConflict will // revert the globals to what they were before this file was loaded. var H5P = H5P || {}; + +/** + * jQuery v1.9.1 + * + * @member + */ H5P.jQuery = jQuery.noConflict(true);