Merge branch 'master' of github.com:h5p/h5p-php-library

namespaces
Pål Jørgensen 2014-11-01 09:28:23 +01:00
commit 1752d4eccf
10 changed files with 757 additions and 579 deletions

View File

@ -30,7 +30,7 @@ class H5PDevelopment {
$this->findLibraries($filesPath . '/development');
}
}
/**
* Get contents of file.
*
@ -41,15 +41,15 @@ class H5PDevelopment {
if (file_exists($file) === FALSE) {
return NULL;
}
$contents = file_get_contents($file);
if ($contents === FALSE) {
return NULL;
}
return $contents;
}
/**
* Scans development directory and find all libraries.
*
@ -57,39 +57,39 @@ class H5PDevelopment {
*/
private function findLibraries($path) {
$this->libraries = array();
if (is_dir($path) === FALSE) {
return;
return;
}
$contents = scandir($path);
for ($i = 0, $s = count($contents); $i < $s; $i++) {
if ($contents[$i]{0} === '.') {
continue; // Skip hidden stuff.
}
$libraryPath = $path . '/' . $contents[$i];
$libraryJSON = $this->getFileContents($libraryPath . '/library.json');
if ($libraryJSON === NULL) {
continue; // No JSON file, skip.
}
$library = json_decode($libraryJSON, TRUE);
if ($library === FALSE) {
continue; // Invalid JSON.
}
// TODO: Validate props? Not really needed, is it? this is a dev site.
// Save/update library.
$library['libraryId'] = $this->h5pF->getLibraryId($library['machineName'], $library['majorVersion'], $library['minorVersion']);
$this->h5pF->saveLibraryData($library, $library['libraryId'] === FALSE);
$library['path'] = $libraryPath;
$this->libraries[H5PDevelopment::libraryToString($library['machineName'], $library['majorVersion'], $library['minorVersion'])] = $library;
}
// TODO: Should we remove libraries without files? Not really needed, but must be cleaned up some time, right?
// Go trough libraries and insert dependencies. Missing deps. will just be ignored and not available. (I guess?!)
@ -106,17 +106,17 @@ class H5PDevelopment {
}
// TODO: Deps must be inserted into h5p_nodes_libraries as well... ? But only if they are used?!
}
/**
* @return array Libraris in development folder.
*/
public function getLibraries() {
return $this->libraries;
}
/**
* Get library
*
*
* @param string $name of the library.
* @param int $majorVersion of the library.
* @param int $minorVersion of the library.
@ -126,10 +126,10 @@ class H5PDevelopment {
$library = H5PDevelopment::libraryToString($name, $majorVersion, $minorVersion);
return isset($this->libraries[$library]) === TRUE ? $this->libraries[$library] : NULL;
}
/**
* Get semantics for the given library.
*
*
* @param string $name of the library.
* @param int $majorVersion of the library.
* @param int $minorVersion of the library.
@ -137,32 +137,32 @@ class H5PDevelopment {
*/
public function getSemantics($name, $majorVersion, $minorVersion) {
$library = H5PDevelopment::libraryToString($name, $majorVersion, $minorVersion);
if (isset($this->libraries[$library]) === FALSE) {
return NULL;
}
return $this->getFileContents($this->libraries[$library]['path'] . '/semantics.json');
}
/**
* Get translations for the given library.
*
*
* @param string $name of the library.
* @param int $majorVersion of the library.
* @param int $minorVersion of the library.
* @return string Translation
*/
public function getLanguage($name, $majorVersion, $minorVersion) {
public function getLanguage($name, $majorVersion, $minorVersion, $language) {
$library = H5PDevelopment::libraryToString($name, $majorVersion, $minorVersion);
if (isset($this->libraries[$library]) === FALSE) {
return NULL;
}
return $this->getFileContents($this->libraries[$library]['path'] . '/language/' . $this->language . '.json');
return $this->getFileContents($this->libraries[$library]['path'] . '/language/' . $language . '.json');
}
/**
* Writes library as string on the form "name majorVersion.minorVersion"
*
@ -175,4 +175,3 @@ class H5PDevelopment {
return $name . ' ' . $majorVersion . '.' . $minorVersion;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +1,20 @@
/*jshint -W083 */
var H5PUpgrades = H5PUpgrades || {};
(function ($) {
var info, $container, librariesCache = {};
// Initialize
$(document).ready(function () {
// Get library info
info = H5PIntegration.getLibraryInfo();
// Get and reset container
$container = $('#h5p-admin-container').html('<p>' + info.message + '</p>');
// Make it possible to select version
var $version = $(getVersionSelect(info.versions)).appendTo($container);
// Add "go" button
$('<button/>', {
class: 'h5p-admin-upgrade-button',
@ -23,11 +24,11 @@ var H5PUpgrades = H5PUpgrades || {};
new ContentUpgrade($version.val());
}
}).appendTo($container);
});
});
/**
* Generate html for version select.
*
*
* @param {Object} versions
* @returns {String}
*/
@ -41,18 +42,18 @@ var H5PUpgrades = H5PUpgrades || {};
return html;
}
};
/**
* Private. Helps process each property on the given object asynchronously in serial order.
*
*
* @param {Object} obj
* @param {Function} process
* @param {Function} finished
*/
var asyncSerial = function (obj, process, finished) {
var id, isArray = obj instanceof Array;
// Keep track of each property that belongs to this object.
// Keep track of each property that belongs to this object.
if (!isArray) {
var ids = [];
for (id in obj) {
@ -61,9 +62,9 @@ var H5PUpgrades = H5PUpgrades || {};
}
}
}
var i = -1; // Keeps track of the current property
/**
* Private. Process the next property
*/
@ -71,10 +72,10 @@ var H5PUpgrades = H5PUpgrades || {};
id = isArray ? i : ids[i];
process(id, obj[id], check);
};
/**
* Private. Check if we're done or have an error.
*
*
* @param {String} err
*/
var check = function (err) {
@ -89,13 +90,13 @@ var H5PUpgrades = H5PUpgrades || {};
}
}, 0);
};
check(); // Start
};
/**
/**
* Make it easy to keep track of version details.
*
*
* @param {String} version
* @param {Number} libraryId
* @returns {_L1.Version}
@ -103,19 +104,19 @@ var H5PUpgrades = H5PUpgrades || {};
function Version(version, libraryId) {
if (libraryId !== undefined) {
version = info.versions[libraryId];
// Public
this.libraryId = libraryId;
}
var versionSplit = version.split('.', 3);
// Public
this.major = versionSplit[0];
this.minor = versionSplit[1];
/**
* Public. Custom string for this object.
*
*
* @returns {String}
*/
this.toString = function () {
@ -125,17 +126,17 @@ var H5PUpgrades = H5PUpgrades || {};
/**
* Displays a throbber in the status field.
*
*
* @param {String} msg
* @returns {_L1.Throbber}
*/
function Throbber(msg) {
var $throbber = H5PUtils.throbber(msg);
$container.html('').append($throbber);
/**
* Makes it possible to set the progress.
*
*
* @param {String} progress
*/
this.setProgress = function (progress) {
@ -145,16 +146,16 @@ var H5PUpgrades = H5PUpgrades || {};
/**
* Start a new content upgrade.
*
*
* @param {Number} libraryId
* @returns {_L1.ContentUpgrade}
*/
function ContentUpgrade(libraryId) {
var self = this;
// Get selected version
self.version = new Version(null, libraryId);
// Create throbber with loading text and progress
self.throbber = new Throbber(info.inProgress.replace('%ver', self.version));
@ -164,36 +165,36 @@ var H5PUpgrades = H5PUpgrades || {};
token: info.token
});
}
/**
* Get the next batch and start processing it.
*
*
* @param {Object} outData
*/
ContentUpgrade.prototype.nextBatch = function (outData) {
var self = this;
$.post(info.infoUrl, outData, function (inData) {
if (!(inData instanceof Object)) {
// Print errors from backend
return self.setStatus(inData);
}
}
if (inData.left === 0) {
// Nothing left to process
return self.setStatus(info.done);
}
self.left = inData.left;
self.token = inData.token;
// Start processing
self.processBatch(inData.params);
});
};
/**
* Set current status message.
*
*
* @param {String} msg
*/
ContentUpgrade.prototype.setStatus = function (msg) {
@ -202,28 +203,38 @@ var H5PUpgrades = H5PUpgrades || {};
/**
* Process the given parameters.
*
*
* @param {Object} parameters
*/
ContentUpgrade.prototype.processBatch = function (parameters) {
var self = this;
var upgraded = {}; // Track upgraded params
var current = 0; // Track progress
asyncSerial(parameters, function (id, params, next) {
// Make params possible to work with
params = JSON.parse(params);
try {
// Make params possible to work with
params = JSON.parse(params);
if (!(params instanceof Object)) {
throw true;
}
}
catch (event) {
return next(info.errorContent.replace('%id', id) + ' ' + info.errorParamsBroken);
}
// Upgrade this content.
self.upgrade(info.library.name, new Version(info.library.version), self.version, params, function (err, params) {
if (!err) {
upgraded[id] = JSON.stringify(params);
current++;
self.throbber.setProgress(Math.round((info.total - self.left + current) / (info.total / 100)) + ' %');
if (err) {
return next(info.errorContent.replace('%id', id) + ' ' + err);
}
next(err);
upgraded[id] = JSON.stringify(params);
current++;
self.throbber.setProgress(Math.round((info.total - self.left + current) / (info.total / 100)) + ' %');
next();
});
}, function (err) {
@ -240,10 +251,10 @@ var H5PUpgrades = H5PUpgrades || {};
});
});
};
/**
* Upgade the given content.
*
*
* @param {String} name
* @param {Version} oldVersion
* @param {Version} newVersion
@ -253,19 +264,19 @@ var H5PUpgrades = H5PUpgrades || {};
*/
ContentUpgrade.prototype.upgrade = function (name, oldVersion, newVersion, params, next) {
var self = this;
// Load library details and upgrade routines
self.loadLibrary(name, newVersion, function (err, library) {
if (err) {
return next(err);
}
// Run upgrade routines on params
self.processParams(library, oldVersion, newVersion, params, function (err, params) {
if (err) {
return next(err);
}
// Check if any of the sub-libraries need upgrading
asyncSerial(library.semantics, function (index, field, next) {
self.processField(field, params[field.name], function (err, upgradedParams) {
@ -280,24 +291,24 @@ var H5PUpgrades = H5PUpgrades || {};
});
});
};
/**
* Load library data needed for content upgrade.
*
*
* @param {String} name
* @param {Version} version
* @param {Function} next
*/
ContentUpgrade.prototype.loadLibrary = function (name, version, next) {
var self = this;
var key = name + '/' + version.major + '/' + version.minor;
if (librariesCache[key] !== undefined) {
// Library has been loaded before. Return cache.
next(null, librariesCache[key]);
return;
}
$.ajax({
dataType: 'json',
cache: true,
@ -306,7 +317,7 @@ var H5PUpgrades = H5PUpgrades || {};
next(info.errorData.replace('%lib', name + ' ' + version));
}).done(function (library) {
librariesCache[key] = library;
if (library.upgradesScript) {
self.loadScript(library.upgradesScript, function (err) {
if (err) {
@ -320,10 +331,10 @@ var H5PUpgrades = H5PUpgrades || {};
}
});
};
/**
* Load script with upgrade hooks.
*
*
* @param {String} url
* @param {Function} next
*/
@ -338,10 +349,10 @@ var H5PUpgrades = H5PUpgrades || {};
next();
});
};
/**
* Run upgrade hooks on params.
*
*
* @param {Object} library
* @param {Version} oldVersion
* @param {Version} newVersion
@ -354,7 +365,7 @@ var H5PUpgrades = H5PUpgrades || {};
// Upgrades script should be loaded so the upgrades should be here.
return next(info.errorScript.replace('%lib', library.name + ' ' + newVersion));
}
// No upgrades script. Move on
return next(null, params);
}
@ -362,7 +373,7 @@ var H5PUpgrades = H5PUpgrades || {};
// Run upgrade hooks. Start by going through major versions
asyncSerial(H5PUpgrades[library.name], function (major, minors, nextMajor) {
if (major < oldVersion.major || major > newVersion.major) {
// Older than the current version or newer than the selected
// Older than the current version or newer than the selected
nextMajor();
}
else {
@ -374,14 +385,16 @@ var H5PUpgrades = H5PUpgrades || {};
}
else {
// We found an upgrade hook, run it
if (upgrade.contentUpgrade !== undefined && typeof upgrade.contentUpgrade === 'function') {
upgrade.contentUpgrade(params, function (err, upgradedParams) {
var unnecessaryWrapper = (upgrade.contentUpgrade !== undefined ? upgrade.contentUpgrade : upgrade);
try {
unnecessaryWrapper(params, function (err, upgradedParams) {
params = upgradedParams;
nextMinor(err);
});
}
else {
nextMinor(info.errorScript.replace('%lib', library.name + ' ' + newVersion));
catch (err) {
next(err);
}
}
}, nextMajor);
@ -390,27 +403,27 @@ var H5PUpgrades = H5PUpgrades || {};
next(err, params);
});
};
/**
* Process parameter fields to find and upgrade sub-libraries.
*
*
* @param {Object} field
* @param {Object} params
* @param {Function} next
*/
ContentUpgrade.prototype.processField = function (field, params, next) {
var self = this;
if (params === undefined) {
return next();
}
switch (field.type) {
case 'library':
if (params.library === undefined || params.params === undefined) {
return next();
}
// Look for available upgrades
var usedLib = params.library.split(' ', 2);
for (var i = 0; i < field.options.length; i++) {
@ -419,14 +432,14 @@ var H5PUpgrades = H5PUpgrades || {};
if (availableLib[1] === usedLib[1]) {
return next(); // Same version
}
// We have different versions
var usedVer = new Version(usedLib[1]);
var availableVer = new Version(availableLib[1]);
if (usedVer.major > availableVer.major || (usedVer.major === availableVer.major && usedVer.minor >= availableVer.minor)) {
return next(); // Larger or same version that's available
}
// A newer version is available, upgrade params
return self.upgrade(availableLib[0], usedVer, availableVer, params.params, function (err, upgraded) {
if (!err) {
@ -484,4 +497,4 @@ var H5PUpgrades = H5PUpgrades || {};
}
};
})(H5P.jQuery);
})(H5P.jQuery);

View File

@ -1,3 +1,5 @@
/*jshint multistr: true */
/**
*
*/
@ -5,7 +7,7 @@ var H5P = H5P || (function () {
var head = document.getElementsByTagName('head')[0];
var contentId = 0;
var contents = {};
/**
* Wraps multiple content between a prefix and a suffix.
*/
@ -16,25 +18,25 @@ var H5P = H5P || (function () {
}
return result;
};
/**
*
*
*/
var loadContent = function (id, script) {
var url = script.getAttribute('data-h5p');
var data, callback = 'H5P' + id;
// Prevent duplicate loading.
script.removeAttribute('data-h5p');
// Callback for when content data is loaded.
window[callback] = function (content) {
contents[id] = content;
var iframe = document.createElement('iframe');
var parent = script.parentNode;
parent.insertBefore(iframe, script);
iframe.id = 'h5p-iframe-' + id;
iframe.style.display = 'block';
iframe.style.width = '100%';
@ -59,19 +61,19 @@ var H5P = H5P || (function () {
</body></html>');
iframe.contentDocument.close();
iframe.contentDocument.documentElement.style.overflow = 'hidden';
// Clean up
parent.removeChild(script);
head.removeChild(data);
delete window[callback];
};
// Create data script
data = document.createElement('script');
data.src = url + (url.indexOf('?') === -1 ? '?' : '&') + 'callback=' + callback;
head.appendChild(data);
};
/**
* Go throught all script tags with the data-h5p attribute and load content.
*/
@ -89,7 +91,7 @@ var H5P = H5P || (function () {
contentId++;
}
};
/**
* Return integration object
*/
@ -124,14 +126,14 @@ var H5P = H5P || (function () {
}
};
};
// Detect if we support fullscreen, and what prefix to use.
var fullScreenBrowserPrefix, safariBrowser;
if (document.documentElement.requestFullScreen) {
fullScreenBrowserPrefix = '';
}
else if (document.documentElement.webkitRequestFullScreen
&& navigator.userAgent.indexOf('Android') === -1 // Skip Android
else if (document.documentElement.webkitRequestFullScreen &&
navigator.userAgent.indexOf('Android') === -1 // Skip Android
) {
safariBrowser = navigator.userAgent.match(/Version\/(\d)/);
safariBrowser = (safariBrowser === null ? 0 : parseInt(safariBrowser[1]));
@ -155,10 +157,10 @@ var H5P = H5P || (function () {
var iframe = document.getElementById('h5p-iframe-' + $element.parent().data('content-id'));
var $classes = $element.add(body);
var $body = $classes.eq(1);
/**
* Prepare for resize by setting the correct styles.
*
*
* @param {String} classes CSS
*/
var before = function (classes) {
@ -179,7 +181,7 @@ var H5P = H5P || (function () {
/**
* Gets called when fullscreen mode has been exited.
* Resizes and sets focus on content.
*
*
* @param {String} classes CSS
*/
var done = function (classes) {
@ -201,10 +203,10 @@ var H5P = H5P || (function () {
before('h5p-semi-fullscreen');
iframe.style.position = 'fixed';
var $disable = $element.prepend('<a href="#" class="h5p-disable-fullscreen" title="Disable fullscreen"></a>').children(':first');
var keyup, disableSemiFullscreen = function () {
$disable.remove();
$disable.remove();
$body.unbind('keyup', keyup);
iframe.style.position = 'static';
done('h5p-semi-fullscreen');
@ -247,7 +249,7 @@ var H5P = H5P || (function () {
}
}
};
return H5P;
})();

View File

@ -9,47 +9,47 @@ var H5PLibraryDetails= H5PLibraryDetails || {};
H5PLibraryDetails.init = function () {
H5PLibraryDetails.$adminContainer = H5PIntegration.getAdminContainer();
H5PLibraryDetails.library = H5PIntegration.getLibraryInfo();
// currentContent holds the current list if data (relevant for filtering)
H5PLibraryDetails.currentContent = H5PLibraryDetails.library.content;
// The current page index (for pager)
H5PLibraryDetails.currentPage = 0;
// The current filter
H5PLibraryDetails.currentFilter = '';
// We cache the filtered results, so we don't have to do unneccessary searches
H5PLibraryDetails.filterCache = [];
// Append library info
H5PLibraryDetails.$adminContainer.append(H5PLibraryDetails.createLibraryInfo());
// Append node list
H5PLibraryDetails.$adminContainer.append(H5PLibraryDetails.createContentElement());
};
/**
* Create the library details view
* Create the library details view
*/
H5PLibraryDetails.createLibraryInfo = function () {
var $libraryInfo = $('<div class="h5p-library-info"></div>');
$.each(H5PLibraryDetails.library.info, function (title, value) {
$libraryInfo.append(H5PUtils.createLabeledField(title, value));
});
return $libraryInfo;
};
/**
* Create the content list with searching and paging
* Create the content list with searching and paging
*/
H5PLibraryDetails.createContentElement = function () {
if (H5PLibraryDetails.library.notCached !== undefined) {
return H5PUtils.getRebuildCache(H5PLibraryDetails.library.notCached);
}
if (H5PLibraryDetails.currentContent === undefined) {
H5PLibraryDetails.$content = $('<div class="h5p-content empty">' + H5PLibraryDetails.library.translations.noContent + '</div>');
}
@ -62,7 +62,7 @@ var H5PLibraryDetails= H5PLibraryDetails || {};
return H5PLibraryDetails.$content;
}
};
/**
* Creates the content list
*/
@ -71,12 +71,12 @@ var H5PLibraryDetails= H5PLibraryDetails || {};
if(H5PLibraryDetails.$contentTable) {
H5PLibraryDetails.$contentTable.remove();
}
H5PLibraryDetails.$contentTable = H5PUtils.createTable();
var i = (H5PLibraryDetails.currentPage*H5PLibraryDetails.PAGER_SIZE);
var lastIndex = (i+H5PLibraryDetails.PAGER_SIZE);
if(lastIndex > H5PLibraryDetails.currentContent.length) {
lastIndex = H5PLibraryDetails.currentContent.length;
}
@ -84,48 +84,48 @@ var H5PLibraryDetails= H5PLibraryDetails || {};
var content = H5PLibraryDetails.currentContent[i];
H5PLibraryDetails.$contentTable.append(H5PUtils.createTableRow(['<a href="' + content.url + '">' + content.title + '</a>']));
}
// Appends it to the browser DOM
// Appends it to the browser DOM
H5PLibraryDetails.$contentTable.insertAfter(H5PLibraryDetails.$search);
};
/**
* Creates the pager element on the bottom of the list
*/
H5PLibraryDetails.createPagerElement = function () {
// Only create pager if needed:
if(H5PLibraryDetails.currentContent.length > H5PLibraryDetails.PAGER_SIZE) {
H5PLibraryDetails.$previous = $('<button type="button" class="previous h5p-admin"><</button>');
H5PLibraryDetails.$next = $('<button type="button" class="next h5p-admin">></button>');
H5PLibraryDetails.$previous.on('click', function () {
if(H5PLibraryDetails.$previous.hasClass('disabled')) {
return;
}
H5PLibraryDetails.currentPage--;
H5PLibraryDetails.updatePager();
H5PLibraryDetails.createContentTable();
});
H5PLibraryDetails.$next.on('click', function () {
if(H5PLibraryDetails.$next.hasClass('disabled')) {
return;
}
H5PLibraryDetails.currentPage++;
H5PLibraryDetails.updatePager();
H5PLibraryDetails.createContentTable();
});
// This is the Page x of y widget:
H5PLibraryDetails.$pagerInfo = $('<span class="pager-info"></span>');
H5PLibraryDetails.$pager = $('<div class="h5p-content-pager"></div>').append(H5PLibraryDetails.$previous, H5PLibraryDetails.$pagerInfo, H5PLibraryDetails.$next);
H5PLibraryDetails.$content.append(H5PLibraryDetails.$pager);
H5PLibraryDetails.$pagerInfo.on('click', function () {
var width = H5PLibraryDetails.$pagerInfo.innerWidth();
H5PLibraryDetails.$pagerInfo.hide();
@ -134,24 +134,24 @@ var H5PLibraryDetails= H5PLibraryDetails || {};
var pageNumerUpdated = function() {
var newPageNum = $gotoInput.val()-1;
var intRegex = /^\d+$/;
$goto.remove();
H5PLibraryDetails.$pagerInfo.css({display: 'inline-block'});
// Check if input value is valid, and that it has actually changed
if(!(intRegex.test(newPageNum) && newPageNum >= 0 && newPageNum < H5PLibraryDetails.getNumPages() && newPageNum != H5PLibraryDetails.currentPage)) {
return;
}
H5PLibraryDetails.currentPage = newPageNum;
H5PLibraryDetails.updatePager();
H5PLibraryDetails.createContentTable();
};
// We create an input box where the user may type in the page number
// he wants to be displayed.
// Reson for doing this is when user has ten-thousands of elements in list,
// this is the easiest way of getting to a specified page
// this is the easiest way of getting to a specified page
var $gotoInput = $('<input/>', {
type: 'number',
min : 1,
@ -166,30 +166,30 @@ var H5PLibraryDetails= H5PLibraryDetails || {};
}
}
}).css({width: width});
var $goto = $('<span/>', {
var $goto = $('<span/>', {
'class': 'h5p-pager-goto'
}).css({width: width}).append($gotoInput).insertAfter(H5PLibraryDetails.$pagerInfo);
$gotoInput.focus();
});
H5PLibraryDetails.updatePager();
}
};
/**
* Calculates number of pages
*/
H5PLibraryDetails.getNumPages = function () {
return Math.ceil(H5PLibraryDetails.currentContent.length / H5PLibraryDetails.PAGER_SIZE);
};
/**
* Update the pager text, and enables/disables the next and previous buttons as needed
*/
H5PLibraryDetails.updatePager = function () {
H5PLibraryDetails.$pagerInfo.css({display: 'inline-block'});
if(H5PLibraryDetails.getNumPages() > 0) {
var message = H5PUtils.translateReplace(H5PLibraryDetails.library.translations.pageXOfY, {
'$x': (H5PLibraryDetails.currentPage+1),
@ -200,26 +200,26 @@ var H5PLibraryDetails= H5PLibraryDetails || {};
else {
H5PLibraryDetails.$pagerInfo.html('');
}
H5PLibraryDetails.$previous.toggleClass('disabled', H5PLibraryDetails.currentPage <= 0);
H5PLibraryDetails.$next.toggleClass('disabled', H5PLibraryDetails.currentContent.length < (H5PLibraryDetails.currentPage+1)*H5PLibraryDetails.PAGER_SIZE);
};
/**
* Creates the search element
*/
H5PLibraryDetails.createSearchElement = function () {
H5PLibraryDetails.$search = $('<div class="h5p-content-search"><input placeholder="' + H5PLibraryDetails.library.translations.filterPlaceholder + '" type="search"></div>');
var performSeach = function () {
var searchString = $('.h5p-content-search > input').val();
// If search string same as previous, just do nothing
if(H5PLibraryDetails.currentFilter === searchString) {
return;
}
if (searchString.trim().length === 0) {
// If empty search, use the complete list
H5PLibraryDetails.currentContent = H5PLibraryDetails.library.content;
@ -230,7 +230,7 @@ var H5PLibraryDetails= H5PLibraryDetails || {};
}
else {
var listToFilter = H5PLibraryDetails.library.content;
// Check if we can filter the already filtered results (for performance)
if(searchString.length > 1 && H5PLibraryDetails.currentFilter === searchString.substr(0, H5PLibraryDetails.currentFilter.length)) {
listToFilter = H5PLibraryDetails.currentContent;
@ -239,47 +239,47 @@ var H5PLibraryDetails= H5PLibraryDetails || {};
return content.title && content.title.match(new RegExp(searchString, 'i'));
});
}
H5PLibraryDetails.currentFilter = searchString;
// Cache the current result
H5PLibraryDetails.filterCache[searchString] = H5PLibraryDetails.currentContent;
H5PLibraryDetails.currentPage = 0;
H5PLibraryDetails.createContentTable();
// Display search results:
if (H5PLibraryDetails.$searchResults) {
H5PLibraryDetails.$searchResults.remove();
}
if (searchString.trim().length > 0) {
H5PLibraryDetails.$searchResults = $('<span class="h5p-admin-search-results">' + H5PLibraryDetails.currentContent.length + ' hits on ' + H5PLibraryDetails.currentFilter + '</span>');
H5PLibraryDetails.$searchResults = $('<span class="h5p-admin-search-results">' + H5PLibraryDetails.currentContent.length + ' hits on ' + H5PLibraryDetails.currentFilter + '</span>');
H5PLibraryDetails.$search.append(H5PLibraryDetails.$searchResults);
}
H5PLibraryDetails.updatePager();
};
var inputTimer = undefined;
var inputTimer;
$('input', H5PLibraryDetails.$search).on('change keypress paste input', function () {
// Here we start the filtering
// We wait at least 500 ms after last input to perform search
if(inputTimer) {
clearTimeout(inputTimer);
}
inputTimer = setTimeout( function () {
performSeach();
}, 500);
});
H5PLibraryDetails.$content.append(H5PLibraryDetails.$search);
};
/**
* Creates the page size selector
*/
H5PLibraryDetails.createPageSizeSelector = function () {
H5PLibraryDetails.$search.append('<div class="h5p-admin-pager-size-selector">' + H5PLibraryDetails.library.translations.pageSizeSelectorLabel + ':<span data-page-size="10">10</span><span class="selected" data-page-size="20">20</span><span data-page-size="50">50</span><span data-page-size="100">100</span><span data-page-size="200">200</span></div>');
// Listen to clicks on the page size selector:
// Listen to clicks on the page size selector:
$('.h5p-admin-pager-size-selector > span', H5PLibraryDetails.$search).on('click', function () {
H5PLibraryDetails.PAGER_SIZE = $(this).data('page-size');
$('.h5p-admin-pager-size-selector > span', H5PLibraryDetails.$search).removeClass('selected');
@ -289,7 +289,7 @@ var H5PLibraryDetails= H5PLibraryDetails || {};
H5PLibraryDetails.updatePager();
});
};
// Initialize me:
$(document).ready(function () {
if (!H5PLibraryDetails.initialized) {
@ -297,5 +297,5 @@ var H5PLibraryDetails= H5PLibraryDetails || {};
H5PLibraryDetails.init();
}
});
})(H5P.jQuery);
})(H5P.jQuery);

View File

@ -1,4 +1,5 @@
var H5PLibraryList= H5PLibraryList || {};
/*jshint multistr: true */
var H5PLibraryList = H5PLibraryList || {};
(function ($) {
@ -7,53 +8,62 @@ var H5PLibraryList= H5PLibraryList || {};
*/
H5PLibraryList.init = function () {
var $adminContainer = H5PIntegration.getAdminContainer();
var libraryList = H5PIntegration.getLibraryList();
if (libraryList.notCached) {
$adminContainer.append(H5PUtils.getRebuildCache(libraryList.notCached));
}
// Create library list
$adminContainer.append(H5PLibraryList.createLibraryList(H5PIntegration.getLibraryList()));
};
/**
* Create the library list
*
*
* @param {object} libraries List of libraries and headers
*/
H5PLibraryList.createLibraryList = function (libraries) {
var t = H5PIntegration.i18n.H5P;
if(libraries.listData === undefined || libraries.listData.length === 0) {
return;
return $('<div>' + t.NA + '</div>');
}
// Create table
var $table = H5PUtils.createTable(libraries.listHeaders);
$table.addClass('libraries');
// Add libraries
var t = H5PIntegration.i18n.H5P;
$.each (libraries.listData, function (index, library) {
var $libraryRow = H5PUtils.createTableRow([
library.title,
'<input class="h5p-admin-restricted" type="checkbox"/>',
library.numContent,
library.numContentDependencies,
library.numLibraryDependencies,
{
text: library.numContent,
class: 'h5p-admin-center'
},
{
text: library.numContentDependencies,
class: 'h5p-admin-center'
},
{
text: library.numLibraryDependencies,
class: 'h5p-admin-center'
},
'<div class="h5p-admin-buttons-wrapper">\
<button class="h5p-admin-upgrade-library"></button>\
<button class="h5p-admin-view-library" title="' + t.viewLibrary + '"></button>\
<button class="h5p-admin-delete-library"></button>\
</div>'
]);
H5PLibraryList.addRestricted($('.h5p-admin-restricted', $libraryRow), library.restrictedUrl, library.restricted);
var hasContent = !(library.numContent === '' || library.numContent === 0);
if (library.upgradeUrl === null) {
$('.h5p-admin-upgrade-library', $libraryRow).remove();
}
else if (library.upgradeUrl === false || library.numContent === 0) {
else if (library.upgradeUrl === false || !hasContent) {
$('.h5p-admin-upgrade-library', $libraryRow).attr('disabled', true);
}
else {
@ -61,14 +71,14 @@ var H5PLibraryList= H5PLibraryList || {};
window.location.href = library.upgradeUrl;
});
}
// Open details view when clicked
$('.h5p-admin-view-library', $libraryRow).on('click', function () {
window.location.href = library.detailsUrl;
});
var $deleteButton = $('.h5p-admin-delete-library', $libraryRow);
if (libraries.notCached !== undefined || library.numContent !== 0 || (library.numContentDependencies !== '' && library.numContentDependencies !== 0) || (library.numLibraryDependencies !== '' && library.numLibraryDependencies !== 0)) {
if (libraries.notCached !== undefined || hasContent || (library.numContentDependencies !== '' && library.numContentDependencies !== 0) || (library.numLibraryDependencies !== '' && library.numLibraryDependencies !== 0)) {
// Disabled delete if content.
$deleteButton.attr('disabled', true);
}
@ -81,10 +91,10 @@ var H5PLibraryList= H5PLibraryList || {};
$table.append($libraryRow);
});
return $table;
};
H5PLibraryList.addRestricted = function ($checkbox, url, selected) {
if (selected === null) {
$checkbox.remove();
@ -121,5 +131,5 @@ var H5PLibraryList= H5PLibraryList || {};
H5PLibraryList.init();
}
});
})(H5P.jQuery);

View File

@ -3,7 +3,7 @@ var H5PUtils = H5PUtils || {};
(function ($) {
/**
* Generic function for creating a table including the headers
*
*
* @param {array} headers List of headers
*/
H5PUtils.createTable = function (headers) {
@ -12,56 +12,62 @@ var H5PUtils = H5PUtils || {};
if(headers) {
var $thead = $('<thead></thead>');
var $tr = $('<tr></tr>');
$.each(headers, function (index, value) {
if (!(value instanceof Object)) {
value = {
text: value
html: value
};
}
$('<th/>', value).appendTo($tr);
});
$table.append($thead.append($tr));
}
return $table;
};
/**
* Generic function for creating a table row
*
*
* @param {array} rows Value list. Object name is used as class name in <TD>
*/
H5PUtils.createTableRow = function (rows) {
var $tr = $('<tr></tr>');
$.each(rows, function (index, value) {
$tr.append('<td>' + value + '</td>');
if (!(value instanceof Object)) {
value = {
html: value
};
}
$('<td/>', value).appendTo($tr);
});
return $tr;
};
/**
* Generic function for creating a field containing label and value
*
* @param {string} label The label displayed in front of the value
*
* @param {string} label The label displayed in front of the value
* @param {string} value The value
*/
H5PUtils.createLabeledField = function (label, value) {
var $field = $('<div class="h5p-labeled-field"></div>');
$field.append('<div class="h5p-label">' + label + '</div>');
$field.append('<div class="h5p-value">' + value + '</div>');
return $field;
};
/**
* Replaces placeholder fields in translation strings
*
*
* @param {string} template The translation template string in the following format: "$name is a $sex"
* @param {array} replacors An js object with key and values. Eg: {'$name': 'Frode', '$sex': 'male'}
*/
@ -71,10 +77,10 @@ var H5PUtils = H5PUtils || {};
});
return template;
};
/**
* Get throbber with given text.
*
*
* @param {String} text
* @returns {$}
*/
@ -84,7 +90,7 @@ var H5PUtils = H5PUtils || {};
text: text
});
};
/**
* Makes it possbile to rebuild all content caches from admin UI.
* @param {Object} notCached
@ -101,7 +107,7 @@ var H5PUtils = H5PUtils || {};
current++;
if (current === parts.length) current = 0;
}, 100);
var $counter = $container.find('.progress');
var build = function () {
$.post(notCached.url, function (left) {
@ -120,8 +126,8 @@ var H5PUtils = H5PUtils || {};
};
build();
});
return $container;
};
})(H5P.jQuery);
})(H5P.jQuery);

179
js/h5p.js
View File

@ -1,3 +1,4 @@
/*jshint multistr: true */
// TODO: Should we split up the generic parts needed by the editor(and others), and the parts needed to "run" H5Ps?
var H5P = H5P || {};
@ -11,12 +12,10 @@ H5P.$window = H5P.jQuery(window);
if (document.documentElement.requestFullScreen) {
H5P.fullScreenBrowserPrefix = '';
}
else if (document.documentElement.webkitRequestFullScreen
&& navigator.userAgent.indexOf('Android') === -1 // Skip Android
) {
else if (document.documentElement.webkitRequestFullScreen) {
H5P.safariBrowser = navigator.userAgent.match(/Version\/(\d)/);
H5P.safariBrowser = (H5P.safariBrowser === null ? 0 : parseInt(H5P.safariBrowser[1]));
// Do not allow fullscreen for safari < 7.
if (H5P.safariBrowser === 0 || H5P.safariBrowser > 6) {
H5P.fullScreenBrowserPrefix = 'webkit';
@ -56,14 +55,14 @@ H5P.init = function () {
// Create new instance.
var instance = H5P.newRunnable(library, contentId, $container, true);
// Check if we should add and display a fullscreen button for this H5P.
if (contentData.fullScreen == 1) {
H5P.jQuery('<div class="h5p-content-controls"><div role="button" tabindex="1" class="h5p-enable-fullscreen" title="' + H5P.t('fullscreen') + '"></div></div>').prependTo($container).children().click(function () {
H5P.fullScreen($container, instance);
});
};
}
var $actions = H5P.jQuery('<ul class="h5p-actions"></ul>');
if (contentData.exportUrl !== '') {
// Display export button
@ -87,7 +86,7 @@ H5P.init = function () {
H5P.jQuery('<li><a class="h5p-link" href="http://h5p.org" target="_blank" title="' + H5P.t('h5pDescription') + '"></a></li>').appendTo($actions);
}
$actions.insertAfter($container);
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);
@ -95,10 +94,10 @@ H5P.init = 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';
iframe.parentElement.style.height = iframe.parentElement.clientHeight + 'px';
// Reset iframe height, in case content has shrinked.
iframe.style.height = '1px';
@ -109,7 +108,7 @@ H5P.init = function () {
// 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.
@ -119,7 +118,7 @@ H5P.init = function () {
}, 1);
});
}
// Resize everything when window is resized.
$window.resize(function () {
if (window.parent.H5P.isFullscreen) {
@ -130,7 +129,7 @@ H5P.init = function () {
instance.$.trigger('resize');
}
});
// Resize content.
instance.$.trigger('resize');
});
@ -159,7 +158,7 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) {
window.parent.H5P.fullScreen($element, instance, exitCallback, H5P.$body.get());
return;
}
var $container = $element;
var $classes, $iframe;
if (body === undefined) {
@ -173,23 +172,23 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) {
$iframe = H5P.jQuery(iframeSelector);
$element = $iframe.parent(); // Put iframe wrapper in fullscreen, not container.
}
$classes = $element.add(H5P.$body).add($classes);
/**
* Prepare for resize by setting the correct styles.
*
*
* @param {String} classes CSS
*/
var before = function (classes) {
$classes.addClass(classes);
if ($iframe !== undefined) {
// Set iframe to its default size(100%).
$iframe.css('height', '');
}
};
/**
* Gets called when fullscreen mode has been entered.
* Resizes and sets focus on content.
@ -199,17 +198,17 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) {
instance.$.trigger('resize');
instance.$.trigger('focus');
};
/**
* Gets called when fullscreen mode has been exited.
* Resizes and sets focus on content.
*
*
* @param {String} classes CSS
*/
var done = function (classes) {
H5P.isFullscreen = false;
$classes.removeClass(classes);
// Do not rely on window resize events.
instance.$.trigger('resize');
instance.$.trigger('focus');
@ -222,11 +221,11 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) {
H5P.isFullscreen = true;
if (H5P.fullScreenBrowserPrefix === undefined) {
// Create semi fullscreen.
before('h5p-semi-fullscreen');
var $disable = H5P.jQuery('<div role="button" tabindex="1" class="h5p-disable-fullscreen" title="' + H5P.t('disableFullscreen') + '"></div>').appendTo($container.find('.h5p-content-controls'));
var keyup, disableSemiFullscreen = function () {
$disable.remove();
$disable.remove();
$body.unbind('keyup', keyup);
done('h5p-semi-fullscreen');
};
@ -241,7 +240,7 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) {
}
else {
// Create real fullscreen.
before('h5p-fullscreen');
var first, eventName = (H5P.fullScreenBrowserPrefix === 'ms' ? 'MSFullscreenChange' : H5P.fullScreenBrowserPrefix + 'fullscreenchange');
document.addEventListener(eventName, function () {
@ -251,7 +250,7 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) {
entered();
return;
}
// We are exiting fullscreen
done('h5p-fullscreen');
document.removeEventListener(eventName, arguments.callee, false);
@ -282,26 +281,26 @@ H5P.getPath = function (path, contentId) {
var hasProtocol = function (path) {
return path.match(/^[a-z0-9]+:\/\//i);
};
if (hasProtocol(path)) {
return path;
}
if (contentId !== undefined) {
prefix = H5PIntegration.getContentPath(contentId);
}
else if (window['H5PEditor'] !== undefined) {
else if (window.H5PEditor !== undefined) {
prefix = H5PEditor.filesPath;
}
else {
return;
}
if (!hasProtocol(prefix)) {
prefix = window.parent.location.protocol + "//" + window.parent.location.host + prefix;
}
return prefix + '/' + path;
return prefix + '/' + path;
};
/**
@ -318,7 +317,7 @@ H5P.getContentPath = function (contentId) {
/**
* Get library class constructor from H5P by classname.
* Note that this class will only work for resolve "H5P.NameWithoutDot".
* Note that this class will only work for resolve "H5P.NameWithoutDot".
* Also check out: H5P.newRunnable
*
* Used from libraries to construct instances of other libraries' objects by name.
@ -336,34 +335,36 @@ H5P.classFromName = function (name) {
*
* 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 {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.
* @return {Object} Instance.
*/
H5P.newRunnable = function (library, contentId, $attachTo, skipResize) {
var nameSplit, versionSplit;
try {
var nameSplit = library.library.split(' ', 2);
var versionSplit = nameSplit[1].split('.', 2);
nameSplit = library.library.split(' ', 2);
versionSplit = nameSplit[1].split('.', 2);
}
catch (err) {
return H5P.error('Invalid library string: ' + library.library);
}
if ((library.params instanceof Object) !== true || (library.params instanceof Array) === true) {
H5P.error('Invalid library params for: ' + library.library);
return H5P.error(library.params);
}
// Find constructor function
var constructor;
try {
nameSplit = nameSplit[0].split('.');
var constructor = window;
for (var i = 0; i < nameSplit.length; i++) {
constructor = window;
for (var i = 0; i < nameSplit.length; i++) {
constructor = constructor[nameSplit[i]];
};
}
if (typeof constructor !== 'function') {
throw null;
}
@ -371,16 +372,16 @@ H5P.newRunnable = function (library, contentId, $attachTo, skipResize) {
catch (err) {
return H5P.error('Unable to find constructor for: ' + library.library);
}
var instance = new constructor(library.params, contentId);
if (instance.$ === undefined) {
instance.$ = H5P.jQuery(instance);
}
if ($attachTo !== undefined) {
instance.attach($attachTo);
if (skipResize === undefined || !skipResize) {
// Resize content.
instance.$.trigger('resize');
@ -417,18 +418,18 @@ H5P.t = function (key, vars, ns) {
if (H5PIntegration.i18n[ns] === undefined) {
return '[Missing translation namespace "' + ns + '"]';
}
if (H5PIntegration.i18n[ns][key] === undefined) {
return '[Missing translation "' + key + '" in "' + ns + '"]';
}
var translation = H5PIntegration.i18n[ns][key];
if (vars !== undefined) {
// Replace placeholder with variables.
for (var placeholder in vars) {
translation = translation.replace(placeholder, vars[placeholder]);
}
}
}
return translation;
@ -457,7 +458,7 @@ H5P.Dialog = function (name, title, content, $element) {
})
.end()
.end();
this.open = function () {
setTimeout(function () {
$dialog.addClass('h5p-open'); // Fade in
@ -465,7 +466,7 @@ H5P.Dialog = function (name, title, content, $element) {
H5P.jQuery(self).trigger('dialog-opened', [$dialog]);
}, 1);
};
this.close = function () {
$dialog.removeClass('h5p-open'); // Fade out
setTimeout(function () {
@ -489,7 +490,7 @@ H5P.openCopyrightsDialog = function ($element, instance) {
if (copyrights === undefined || copyrights === '') {
copyrights = H5P.t('noCopyrights');
}
var dialog = new H5P.Dialog('copyrights', H5P.t('copyrightInformation'), copyrights, $element);
dialog.open();
};
@ -503,12 +504,12 @@ H5P.openCopyrightsDialog = function ($element, instance) {
*/
H5P.openEmbedDialog = function ($element, embedCode) {
var dialog = new H5P.Dialog('embed', H5P.t('embed'), '<textarea class="h5p-embed-code-container">' + embedCode + '</textarea>', $element);
// Selecting embed code when dialog is opened
H5P.jQuery(dialog).on('dialog-opened', function (event, $dialog) {
$dialog.find('.h5p-embed-code-container').select();
});
dialog.open();
};
@ -519,7 +520,7 @@ H5P.ContentCopyrights = function () {
var label;
var media = [];
var content = [];
/**
* Public. Set label.
*
@ -528,7 +529,7 @@ H5P.ContentCopyrights = function () {
this.setLabel = function (newLabel) {
label = newLabel;
};
/**
* Public. Add sub content.
*
@ -539,7 +540,7 @@ H5P.ContentCopyrights = function () {
media.push(newMedia);
}
};
/**
* Public. Add sub content.
*
@ -550,7 +551,7 @@ H5P.ContentCopyrights = function () {
content.push(newContent);
}
};
/**
* Public. Print content copyright.
*
@ -558,28 +559,28 @@ H5P.ContentCopyrights = function () {
*/
this.toString = function () {
var html = '';
// Add media rights
for (var i = 0; i < media.length; i++) {
html += media[i];
}
// Add sub content rights
for (var i = 0; i < content.length; i++) {
for (i = 0; i < content.length; i++) {
html += content[i];
}
if (html !== '') {
// Add a label to this info
if (label !== undefined) {
html = '<h3>' + label + '</h3>' + html;
}
// Add wrapper
html = '<div class="h5p-content-copyrights">' + html + '</div>';
}
return html;
};
};
@ -595,35 +596,35 @@ H5P.ContentCopyrights = function () {
H5P.MediaCopyright = function (copyright, labels, order, extraFields) {
var thumbnail;
var list = new H5P.DefinitionList();
/**
* Private. Get translated label for field.
*
* @param {String} fieldName
* @return {String}
* @return {String}
*/
var getLabel = function (fieldName) {
if (labels === undefined || labels[fieldName] === undefined) {
return H5P.t(fieldName);
}
return labels[fieldName];
};
/**
* Private. Get humanized value for field.
*
* @param {String} fieldName
* @return {String}
* @return {String}
*/
var humanizeValue = function (fieldName, value) {
if (fieldName === 'license') {
return H5P.copyrightLicenses[value];
}
return value;
};
if (copyright !== undefined) {
// Add the extra fields
for (var field in extraFields) {
@ -631,12 +632,12 @@ H5P.MediaCopyright = function (copyright, labels, order, extraFields) {
copyright[field] = extraFields[field];
}
}
if (order === undefined) {
// Set default order
order = ['title', 'author', 'year', 'source', 'license'];
}
for (var i = 0; i < order.length; i++) {
var fieldName = order[i];
if (copyright[fieldName] !== undefined) {
@ -644,7 +645,7 @@ H5P.MediaCopyright = function (copyright, labels, order, extraFields) {
}
}
}
/**
* Public. Set thumbnail.
*
@ -653,7 +654,7 @@ H5P.MediaCopyright = function (copyright, labels, order, extraFields) {
this.setThumbnail = function (newThumbnail) {
thumbnail = newThumbnail;
};
/**
* Public. Checks if this copyright is undisclosed.
* I.e. only has the license attribute set, and it's undisclosed.
@ -669,7 +670,7 @@ H5P.MediaCopyright = function (copyright, labels, order, extraFields) {
}
return false;
};
/**
* Public. Print media copyright.
*
@ -677,20 +678,20 @@ H5P.MediaCopyright = function (copyright, labels, order, extraFields) {
*/
this.toString = function () {
var html = '';
if (this.undisclosed()) {
return html; // No need to print a copyright with a single undisclosed license.
}
if (thumbnail !== undefined) {
html += thumbnail;
}
html += list;
if (html !== '') {
html = '<div class="h5p-media-copyright">' + html + '</div>';
}
return html;
};
};
@ -742,16 +743,16 @@ H5P.Field = function (label, value) {
* Public. Get field label.
*
* @returns {String}
*/
*/
this.getLabel = function () {
return label;
};
/**
* Public. Get field value.
*
* @returns {String}
*/
*/
this.getValue = function () {
return value;
};
@ -762,7 +763,7 @@ H5P.Field = function (label, value) {
*/
H5P.DefinitionList = function () {
var fields = [];
/**
* Public. Add field to list.
*
@ -771,7 +772,7 @@ H5P.DefinitionList = function () {
this.add = function (field) {
fields.push(field);
};
/**
* Public. Get Number of fields.
*
@ -780,7 +781,7 @@ H5P.DefinitionList = function () {
this.size = function () {
return fields.length;
};
/**
* Public. Get field at given index.
*
@ -790,7 +791,7 @@ H5P.DefinitionList = function () {
this.get = function (index) {
return fields[index];
};
/**
* Public. Print definition list.
*
@ -879,7 +880,7 @@ H5P.getLibraryPath = function (library) {
/**
* Recursivly clone the given object.
* TODO: Consider if this needs to be in core. Doesn't $.extend do the same?
* TODO: Consider if this needs to be in core. Doesn't $.extend do the same?
*
* @param {object} object Object to clone.
* @param {type} recursive

1
js/jquery.js vendored

File diff suppressed because one or more lines are too long

View File

@ -244,4 +244,7 @@ button.h5p-admin.disabled:hover {
padding: 0 0.5em;
font-size: 1.5em;
font-weight: bold;
}
}
#h5p-admin-container .h5p-admin-center {
text-align: center;
}