diff --git a/js/h5p-api.js b/js/h5p-api.js
new file mode 100644
index 0000000..e69de29
diff --git a/js/h5p-resizer.js b/js/h5p-resizer.js
new file mode 100644
index 0000000..86cb280
--- /dev/null
+++ b/js/h5p-resizer.js
@@ -0,0 +1,155 @@
+// H5P iframe Resizer
+(function () {
+ if (!window.postMessage || !window.addEventListener) {
+ return; // Not supported
+ }
+
+ // Map actions to handlers
+ var actionHandlers = {};
+
+ /**
+ * Prepare iframe resize.
+ *
+ * @private
+ * @param {Object} iframe Element
+ * @param {Object} data Payload
+ * @param {Function} respond Send a response to the iframe
+ */
+ actionHandlers.hello = function (iframe, data, respond) {
+ // Make iframe responsive
+ iframe.style.width = '100%';
+
+ // Tell iframe that it needs to resize when our window resizes
+ var resize = function (event) {
+ if (iframe.contentWindow) {
+ // Limit resize calls to avoid flickering
+ respond('resize');
+ }
+ else {
+ // Frame is gone, unregister.
+ window.removeEventListener('resize', resize);
+ }
+ };
+ window.addEventListener('resize', resize, false);
+
+ // Respond to let the iframe know we can resize it
+ respond('hello');
+ };
+
+ /**
+ * Prepare iframe resize.
+ *
+ * @private
+ * @param {Object} iframe Element
+ * @param {Object} data Payload
+ * @param {Function} respond Send a response to the iframe
+ */
+ actionHandlers.prepareResize = function (iframe, data, respond) {
+ responseData = {};
+
+ // Retain parent size to avoid jumping/scrolling
+ responseData.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';
+
+ respond('resizePrepared', responseData);
+ };
+
+ /**
+ * Resize parent and iframe to desired height.
+ *
+ * @private
+ * @param {Object} iframe Element
+ * @param {Object} data Payload
+ * @param {Function} respond Send a response to the iframe
+ */
+ actionHandlers.resize = function (iframe, data, respond) {
+ // Resize iframe so all content is visible.
+ iframe.style.height = data.height + 'px';
+
+ // Free parent
+ //iframe.parentElement.style.height = data.parentHeight;
+ };
+
+ /**
+ * Keyup event handler. Exits full screen on escape.
+ *
+ * @param {Event} event
+ */
+ var escape = function (event) {
+ if (event.keyCode === 27) {
+ exitFullScreen();
+ }
+ };
+
+ /**
+ * Enter semi full screen.
+ * Expands the iframe so that it covers the whole page.
+ *
+ * @private
+ * @param {Object} iframe Element
+ * @param {Object} data Payload
+ * @param {Function} respond Send a response to the iframe
+ */
+ actionHandlers.fullScreen = function (iframe, data, respond) {
+ iframe.style.position = 'fixed';
+ iframe.style.top = iframe.style.left = 0;
+ iframe.style.zIndex = 101;
+ iframe.style.width = iframe.style.height = '100%';
+ document.body.addEventListener('keyup', escape, false);
+ respond('fullScreen');
+ };
+
+ /**
+ * Exit semi full screen.
+ *
+ * @private
+ * @param {Object} iframe Element
+ * @param {Object} data Payload
+ * @param {Function} respond Send a response to the iframe
+ */
+ actionHandlers.exitFullScreen = function (iframe, data, respond) {
+ iframe.style.position = '';
+ iframe.style.top = iframe.style.left = '';
+ iframe.style.zIndex = '';
+ iframe.style.width = '100%';
+ iframe.style.height = '';
+ document.body.removeEventListener('keyup', escape, false);
+ respond('exitFullScreen');
+ };
+
+
+ // Listen for messages from iframes
+ window.addEventListener('message', function receiveMessage(event) {
+ if (event.data.context !== 'h5p') {
+ return; // Only handle h5p requests.
+ }
+
+ // Find out who sent the message
+ var iframe, iframes = document.getElementsByTagName('iframe');
+ for (var i = 0; i < iframes.length; i++) {
+ if (iframes[i].contentWindow === event.source) {
+ iframe = iframes[i];
+ break;
+ }
+ }
+
+ if (!iframe) {
+ return; // Cannot find sender
+ }
+
+ // Find action handler handler
+ if (actionHandlers[event.data.action]) {
+ actionHandlers[event.data.action](iframe, event.data, function (action, data) {
+ if (data === undefined) {
+ data = {};
+ }
+ data.action = action;
+ data.context = 'h5p';
+ event.source.postMessage(data, event.origin);
+ });
+ }
+ }, false);
+})();
diff --git a/js/h5p.js b/js/h5p.js
index d53f8e2..8ff166d 100644
--- a/js/h5p.js
+++ b/js/h5p.js
@@ -31,6 +31,8 @@ else if (document.documentElement.msRequestFullscreen) {
// Keep track of when the H5Ps where started
H5P.opened = {};
+H5P.canHasFullScreen = (H5P.isFramed && H5P.externalEmbed !== false) ? (document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled) : true;
+
/**
* Initialize H5P content.
* Scans for ".h5p-content" in the document and initializes H5P instances where found.
@@ -39,15 +41,12 @@ H5P.init = function () {
// Useful jQuery object.
H5P.$body = H5P.jQuery(document.body);
- // Prepare internal resizer for content.
- var $window = H5P.jQuery(window.parent);
-
// H5Ps added in normal DIV.
var $containers = H5P.jQuery(".h5p-content").each(function () {
var $element = H5P.jQuery(this);
var $container = H5P.jQuery('
').appendTo($element);
var contentId = $element.data('content-id');
- var contentData = H5PIntegration.getContentData(contentId);
+ var contentData = H5P.contentDatas['cid-' + contentId];
if (contentData === undefined) {
return H5P.error('No data for content id ' + contentId + '. Perhaps the library is gone?');
}
@@ -85,7 +84,7 @@ H5P.init = function () {
H5P.openEmbedDialog($actions, contentData.embedCode);
});
}
- if (H5PIntegration.showH5PIconInActionBar()) {
+ if (contentData.showH5PIconInActionBar) {
H5P.jQuery('').appendTo($actions);
}
$actions.insertAfter($container);
@@ -100,48 +99,98 @@ H5P.init = function () {
}
});
- if (H5P.isFramed) {
- // 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.
- }
+ if (H5P.isFramed)
+ var resizeDelay;{
+ 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';
+ // 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';
+ // 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';
+ // Resize iframe so all content is visible.
+ iframe.style.height = (iframe.contentDocument.body.scrollHeight) + 'px';
- // Free parent
- iframe.parentElement.style.height = parentHeight;
- };
+ // Free parent
+ iframe.parentElement.style.height = parentHeight;
+ };
- var resizeDelay;
- instance.$.on('resize', function () {
- // Use a delay to make sure iframe is resized to the correct size.
- clearTimeout(resizeDelay);
- resizeDelay = setTimeout(function () {
- resizeIframe();
- }, 1);
- });
+ instance.$.on('resize', function () {
+ // Use a delay to make sure iframe is resized to the correct size.
+ clearTimeout(resizeDelay);
+ resizeDelay = setTimeout(function () {
+ resizeIframe();
+ }, 1);
+ });
+ }
+ else if (H5P.communicator) {
+ // External embed
+ var parentIsFriendly = false;
+
+ // Handle hello message from our parent window
+ H5P.communicator.on('hello', function () {
+ // Initial setup/handshake is done
+ parentIsFriendly = true;
+
+ // Hide scrollbars for correct size
+ document.body.style.overflow = 'hidden';
+
+ H5P.communicator.send('prepareResize');
+ });
+
+ // When resize has been prepared tell parent window to resize
+ H5P.communicator.on('resizePrepared', function (data) {
+ H5P.communicator.send('resize', {
+ height: document.body.scrollHeight,
+ parentHeight: data.parentHeight
+ });
+ });
+
+ H5P.communicator.on('resize', function () {
+ instance.$.trigger('resize');
+ });
+
+ instance.$.on('resize', function () {
+ 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) {
+ H5P.communicator.send('prepareResize');
+ }
+ else {
+ H5P.communicator.send('hello');
+ }
+ }, 0);
+ });
+ }
}
- // Resize everything when window is resized.
- $window.resize(function () {
- if (window.parent.H5P.isFullscreen) {
- // Use timeout to avoid bug in certain browsers when exiting fullscreen. Some browser will trigger resize before the fullscreenchange event.
+ if (!H5P.isFramed || H5P.externalEmbed === false) {
+ // Resize everything when window is resized.
+ H5P.jQuery(window.top).resize(function () {
+ if (window.parent.H5P.isFullscreen) {
+ // Use timeout to avoid bug in certain browsers when exiting fullscreen. Some browser will trigger resize before the fullscreenchange event.
+ instance.$.trigger('resize');
+ }
+ else {
instance.$.trigger('resize');
- }
- else {
- instance.$.trigger('resize');
- }
- });
+ }
+ });
+ }
// Resize content.
instance.$.trigger('resize');
@@ -151,11 +200,69 @@ H5P.init = function () {
H5P.jQuery("iframe.h5p-iframe").each(function () {
var contentId = H5P.jQuery(this).data('content-id');
this.contentDocument.open();
- this.contentDocument.write('' + H5PIntegration.getHeadTags(contentId) + '');
+ this.contentDocument.write('' + H5P.getHeadTags(contentId) + '');
this.contentDocument.close();
+ this.contentWindow.H5P = {
+ externalEmbed: false
+ };
});
};
+H5P.communicator = (function () {
+ /**
+ * @class
+ */
+ 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.
+ *
+ * @public
+ * @param {String} action What you are waiting for
+ * @param {Function} handler What you want done
+ */
+ self.on = function (action, handler) {
+ actionHandlers[action] = handler;
+ };
+
+ /**
+ * Send a message to the all mighty father.
+ *
+ * @public
+ * @param {String} action
+ * @param {Object} [data] payload
+ */
+ self.send = function (action, data) {
+ 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);
+})();
+
/**
* Enable full screen for the given h5p.
*
@@ -166,9 +273,19 @@ H5P.init = function () {
* @returns {undefined}
*/
H5P.fullScreen = function ($element, instance, exitCallback, body) {
- if (H5P.isFramed) {
+ if (H5P.exitFullScreen !== undefined) {
+ return; // Cannot enter new fullscreen until previous is over
+ }
+
+ if (H5P.isFramed && H5P.externalEmbed === false) {
// Trigger resize on wrapper in parent window.
- window.parent.H5P.fullScreen($element, instance, exitCallback, H5P.$body.get());
+ window.top.H5P.fullScreen($element, instance, exitCallback, H5P.$body.get());
+ H5P.isFullscreen = true;
+ H5P.exitFullScreen = function () {
+ window.top.H5P.exitFullScreen();
+ H5P.isFullscreen = false;
+ H5P.exitFullScreen = undefined;
+ };
return;
}
@@ -226,6 +343,7 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) {
instance.$.trigger('resize');
instance.$.trigger('focus');
+ H5P.exitFullScreen = undefined;
if (exitCallback !== undefined) {
exitCallback();
}
@@ -235,6 +353,10 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) {
if (H5P.fullScreenBrowserPrefix === undefined) {
// Create semi fullscreen.
+ if (H5P.isFramed) {
+ return; // TODO: Ask parent for iframe
+ }
+
before('h5p-semi-fullscreen');
var $disable = H5P.jQuery('').appendTo($container.find('.h5p-content-controls'));
var keyup, disableSemiFullscreen = function () {
@@ -277,6 +399,19 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) {
var params = (H5P.fullScreenBrowserPrefix === 'webkit' && H5P.safariBrowser === 0 ? Element.ALLOW_KEYBOARD_INPUT : undefined);
$element[0][method](params);
}
+
+ // Allows everone to exit
+ H5P.exitFullScreen = function () {
+ if (H5P.fullScreenBrowserPrefix === '') {
+ document.exitFullscreen();
+ }
+ else if (H5P.fullScreenBrowserPrefix === 'moz') {
+ document.mozCancelFullScreen();
+ }
+ else {
+ document[H5P.fullScreenBrowserPrefix + 'ExitFullscreen']();
+ }
+ };
}
};
@@ -300,7 +435,7 @@ H5P.getPath = function (path, contentId) {
}
if (contentId !== undefined) {
- prefix = H5PIntegration.getContentPath(contentId);
+ prefix = H5P.url + '/content/' + contentId;
}
else if (window.H5PEditor !== undefined) {
prefix = H5PEditor.filesPath;
@@ -316,18 +451,6 @@ H5P.getPath = function (path, contentId) {
return prefix + '/' + path;
};
-/**
- * THIS FUNCTION IS DEPRECATED, USE getPath INSTEAD
- *
- * Find the path to the content files folder based on the id of the content
- *
- * @param contentId
- * Id of the content requesting a path
- */
-H5P.getContentPath = function (contentId) {
- return H5PIntegration.getContentPath(contentId);
-};
-
/**
* Get library class constructor from H5P by classname.
* Note that this class will only work for resolve "H5P.NameWithoutDot".
@@ -428,15 +551,15 @@ H5P.t = function (key, vars, ns) {
ns = 'H5P';
}
- if (H5PIntegration.i18n[ns] === undefined) {
+ if (H5P.l10n[ns] === undefined) {
return '[Missing translation namespace "' + ns + '"]';
}
- if (H5PIntegration.i18n[ns][key] === undefined) {
+ if (H5P.l10n[ns][key] === undefined) {
return '[Missing translation "' + key + '" in "' + ns + '"]';
}
- var translation = H5PIntegration.i18n[ns][key];
+ var translation = H5P.l10n[ns][key];
if (vars !== undefined) {
// Replace placeholder with variables.
@@ -946,7 +1069,7 @@ H5P.libraryFromString = function (library) {
* @returns {String} The full path to the library.
*/
H5P.getLibraryPath = function (library) {
- return H5PIntegration.getLibraryPath(library);
+ return H5P.url + '/libraries/' + library;
};
/**
@@ -1088,14 +1211,3 @@ if (String.prototype.trim === undefined) {
return H5P.trim(this);
};
}
-
-// Finally, we want to run init when document is ready.
-// TODO: Move to integration. Systems like Moodle using YUI cannot get its translations set before this starts!
-if (H5P.jQuery) {
- H5P.jQuery(document).ready(function () {
- if (!H5P.initialized) {
- H5P.initialized = true;
- H5P.init();
- }
- });
-}