Resolving conflicts

namespaces
Svein-Tore Griff With 2014-11-01 20:42:34 +01:00
commit b3da2c5b32
12 changed files with 1131 additions and 386 deletions

View File

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

View File

@ -394,10 +394,10 @@ interface H5PFrameworkInterface {
/** /**
* Delete a library from database and file system * Delete a library from database and file system
* *
* @param int $libraryId * @param stdClass $library
* Library identifier * Library object with id, name, major version and minor version.
*/ */
public function deleteLibrary($libraryId); public function deleteLibrary($library);
/** /**
* Load content. * Load content.
@ -444,57 +444,58 @@ interface H5PFrameworkInterface {
public function loadContentDependencies($id, $type = NULL); public function loadContentDependencies($id, $type = NULL);
/** /**
* Get data from cache. * Get stored setting.
* *
* @param string $group * @param string $name
* Identifier for the cache group * Identifier for the setting
* @param string $key * @param string $default
* Unique identifier within the group * Optional default value if settings is not set
* @return mixed * @return mixed
* Whatever has been stored in the cache. NULL if the entry doesn't exist * Whatever has been stored as the setting
*/ */
public function cacheGet($group, $key); public function getOption($name, $default = NULL);
/** /**
* Store data in cache. * Stores the given setting.
* For example when did we last check h5p.org for updates to our libraries.
* *
* @param string $group * @param string $name
* The cache group where the data should be stored * Identifier for the setting
* @param string $key * @param mixed $value Data
* A unique key identifying where the data should be stored * Whatever we want to store as the setting
* @param mixed $data
* The data you want to cache
*/ */
public function cacheSet($group, $key, $data); public function setOption($name, $value);
/** /**
* Delete data from cache. * This will set the filtered parameters for the given content.
* *
* @param string $group * @param int $content_id
* Identifier for the cache group * @param string $parameters filtered
* @param string $key
* Unique identifier within the group
*/ */
public function cacheDel($group, $key = NULL); public function setFilteredParameters($content_id, $parameters = '');
/** /**
* Will invalidate the cache for the content that uses the specified library. * Will clear filtered params for all the content that uses the specified
* This means that the content dependencies has to be rebuilt, and the parameters refiltered. * library. This means that the content dependencies will have to be rebuilt,
* and the parameters refiltered.
* *
* @param int $libraryId * @param int $library_id
*/ */
public function invalidateContentCache($libraryId); public function clearFilteredParameters($library_id);
/** /**
* Get number of content that hasn't been cached * Get number of contents that has to get their content dependencies rebuilt
* and parameters refiltered.
*
* @return int
*/ */
public function getNotCached(); public function getNumNotFiltered();
/** /**
* Get number of contents using library as main library. * Get number of contents using library as main library.
* *
* @param int $libraryId * @param int $libraryId
* Identifier for a library * @return int
*/ */
public function getNumContent($libraryId); public function getNumContent($libraryId);
} }
@ -1265,7 +1266,7 @@ class H5PStorage {
} }
// Make sure libraries dependencies, parameter filtering and export files gets regenerated for all content who uses this library. // Make sure libraries dependencies, parameter filtering and export files gets regenerated for all content who uses this library.
$this->h5pF->invalidateContentCache($library['libraryId']); $this->h5pF->clearFilteredParameters($library['libraryId']);
$upgradedLibsCount++; $upgradedLibsCount++;
} }
@ -1525,9 +1526,9 @@ class H5PCore {
public static $defaultContentWhitelist = 'json png jpg jpeg gif bmp tif tiff svg eot ttf woff otf webm mp4 ogg mp3 txt pdf rtf doc docx xls xlsx ppt pptx odt ods odp xml csv diff patch swf md textile'; public static $defaultContentWhitelist = 'json png jpg jpeg gif bmp tif tiff svg eot ttf woff otf webm mp4 ogg mp3 txt pdf rtf doc docx xls xlsx ppt pptx odt ods odp xml csv diff patch swf md textile';
public static $defaultLibraryWhitelistExtras = 'js css'; public static $defaultLibraryWhitelistExtras = 'js css';
public $librariesJsonData, $contentJsonData, $mainJsonData, $h5pF, $path, $development_mode, $h5pD, $disableFileCheck;
const SECONDS_IN_WEEK = 604800; const SECONDS_IN_WEEK = 604800;
public $librariesJsonData, $contentJsonData, $mainJsonData, $h5pF, $path, $development_mode, $h5pD;
private $exportEnabled; private $exportEnabled;
/** /**
@ -1567,11 +1568,6 @@ class H5PCore {
$content['id'] = $this->h5pF->insertContent($content, $contentMainId); $content['id'] = $this->h5pF->insertContent($content, $contentMainId);
} }
if (!isset($content['filtered'])) {
// TODO: Add filtered to all impl. and remove
$this->h5pF->cacheDel('parameters', $content['id']);
}
return $content['id']; return $content['id'];
} }
@ -1619,16 +1615,8 @@ class H5PCore {
* @return Object NULL on failure. * @return Object NULL on failure.
*/ */
public function filterParameters($content) { public function filterParameters($content) {
if (isset($content['filtered'])) { if (isset($content['filtered']) && $content['filtered'] !== '') {
$params = ($content['filtered'] === '' ? NULL : $content['filtered']); return $content['filtered'];
}
else {
// TODO: Add filtered to all impl. and remove
$params = $this->h5pF->cacheGet('parameters', $content['id']);
}
if ($params !== NULL) {
return $params;
} }
// Validate and filter against main library semantics. // Validate and filter against main library semantics.
@ -1655,7 +1643,7 @@ class H5PCore {
} }
// Cache. // Cache.
$this->h5pF->cacheSet('parameters', $content['id'], $params); $this->h5pF->setFilteredParameters($content['id'], $params);
return $params; return $params;
} }
@ -2309,6 +2297,10 @@ class H5PContentValidator {
* FALSE if one or more files fail validation. Error message should be set accordingly by validator. * FALSE if one or more files fail validation. Error message should be set accordingly by validator.
*/ */
public function validateContentFiles($contentPath, $isLibrary = FALSE) { public function validateContentFiles($contentPath, $isLibrary = FALSE) {
if ($this->h5pC->disableFileCheck === TRUE) {
return TRUE;
}
// Scan content directory for files, recurse into sub directories. // Scan content directory for files, recurse into sub directories.
$files = array_diff(scandir($contentPath), array('.','..')); $files = array_diff(scandir($contentPath), array('.','..'));
$valid = TRUE; $valid = TRUE;
@ -2598,7 +2590,7 @@ class H5PContentValidator {
if (!isset($this->libraries[$value->library])) { if (!isset($this->libraries[$value->library])) {
$libspec = H5PCore::libraryFromString($value->library); $libspec = H5PCore::libraryFromString($value->library);
$library = $this->h5pC->loadLibrary($libspec['machineName'], $libspec['majorVersion'], $libspec['minorVersion']); $library = $this->h5pC->loadLibrary($libspec['machineName'], $libspec['majorVersion'], $libspec['minorVersion']);
$library['semantics'] = json_decode($library['semantics']); $library['semantics'] = $this->h5pC->loadLibrarySemantics($libspec['machineName'], $libspec['majorVersion'], $libspec['minorVersion']);
$this->libraries[$value->library] = $library; $this->libraries[$value->library] = $library;
// Find all dependencies for this library // Find all dependencies for this library

View File

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

255
js/h5p-data-view.js Normal file
View File

@ -0,0 +1,255 @@
var H5PDataView = (function ($) {
/**
* Initialize a new H5P data view.
*
* @class
* @param {Object} container
* Element to clear out and append to.
* @param {String} source
* URL to get data from. Data format: {num: 123, rows:[[1,2,3],[2,4,6]]}
* @param {Array} headers
* List with column headers. Can be strings or objects with options like
* "text" and "sortable". E.g.
* [{text: 'Col 1', sortable: true}, 'Col 2', 'Col 3']
* @param {Object} l10n
* Localization / translations. e.g.
* {
* loading: 'Loading data.',
* ajaxFailed: 'Failed to load data.',
* noData: "There's no data available that matches your criteria.",
* currentPage: 'Page $current of $total',
* nextPage: 'Next page',
* previousPage: 'Previous page',
* search: 'Search'
* }
* @param {Object} classes
* Custom html classes to use on elements.
* e.g. {tableClass: 'fixed'}.
* @param {Array} filters
* Make it possible to filter/search in the given column.
* e.g. [null, true, null, null] will make it possible to do a text
* search in column 2.
* @param {Function} loaded
* Callback for when data has been loaded.
*/
function H5PDataView(container, source, headers, l10n, classes, filters, loaded) {
var self = this;
self.$container = $(container).addClass('h5p-data-view').html('');
self.source = source;
self.headers = headers;
self.l10n = l10n;
self.classes = (classes === undefined ? {} : classes);
self.filters = (filters === undefined ? [] : filters);
self.loaded = loaded;
self.limit = 20;
self.offset = 0;
self.filterOn = [];
self.loadData();
}
/**
* Load data from source URL.
*
* @public
*/
H5PDataView.prototype.loadData = function () {
var self = this;
// Throbb
self.setMessage(H5PUtils.throbber(self.l10n.loading));
// Create URL
var url = self.source;
url += (url.indexOf('?') === -1 ? '?' : '&') + 'offset=' + self.offset + '&limit=' + self.limit;
// Add sorting
if (self.sortBy !== undefined && self.sortDir !== undefined) {
url += '&sortBy=' + self.sortBy + '&sortDir=' + self.sortDir;
}
// Add filters
var filtering;
for (var i = 0; i < self.filterOn.length; i++) {
if (self.filterOn[i] === undefined) {
continue;
}
filtering = true;
url += '&filters[' + i + ']=' + encodeURIComponent(self.filterOn[i]);
}
// Fire ajax request
$.ajax({
dataType: 'json',
cache: true,
url: url
}).fail(function () {
// Error handling
self.setMessage($('<p/>', {text: self.l10n.ajaxFailed}));
}).done(function (data) {
if (!data.rows.length) {
self.setMessage($('<p/>', {text: filtering ? self.l10n.noData : self.l10n.empty}));
}
else {
// Update table data
self.updateTable(data.rows);
}
// Update pagination widget
self.updatePagination(data.num);
if (self.loaded !== undefined) {
self.loaded();
}
});
};
/**
* Display the given message to the user.
*
* @public
* @param {jQuery} $message wrapper with message
*/
H5PDataView.prototype.setMessage = function ($message) {
var self = this;
if (self.table === undefined) {
self.$container.html('').append($message);
}
else {
self.table.setBody($message);
}
};
/**
* Update table data.
*
* @public
* @param {Array} rows
*/
H5PDataView.prototype.updateTable = function (rows) {
var self = this;
if (self.table === undefined) {
// Clear out container
self.$container.html('');
// Add filters
self.addFilters();
// Create new table
self.table = new H5PUtils.Table(self.classes, self.headers);
self.table.setHeaders(self.headers, function (col, dir) {
// Sorting column or direction has changed callback.
self.sortBy = col;
self.sortDir = dir;
self.loadData();
});
self.table.appendTo(self.$container);
}
// Add/update rows
self.table.setRows(rows);
};
/**
* Update pagination widget.
*
* @public
* @param {Number} num size of data collection
*/
H5PDataView.prototype.updatePagination = function (num) {
var self = this;
if (self.pagination === undefined) {
// Create new widget
var $pagerContainer = $('<div/>', {'class': 'h5p-pagination'});
self.pagination = new H5PUtils.Pagination(num, self.limit, function (offset) {
// Handle page changes in pagination widget
self.offset = offset;
self.loadData();
}, self.l10n);
self.pagination.appendTo($pagerContainer);
self.table.setFoot($pagerContainer);
}
else {
// Update existing widget
self.pagination.update(num, self.limit);
}
};
/**
* Add filters.
*
* @public
*/
H5PDataView.prototype.addFilters = function () {
var self = this;
for (var i = 0; i < self.filters.length; i++) {
if (self.filters[i] === true) {
// Add text input filter for col i
self.addTextFilter(i);
}
}
};
/**
* Add text filter for given col num.
* @public
* @param {Number} col
*/
H5PDataView.prototype.addTextFilter = function (col) {
var self = this;
/**
* Find input value and filter on it.
* @private
*/
var search = function () {
var filterOn = $input.val().replace(/^\s+|\s+$/g, '');
if (filterOn === '') {
filterOn = undefined;
}
if (filterOn !== self.filterOn[col]) {
self.filterOn[col] = filterOn;
self.loadData();
}
};
// Add text field for filtering
var typing;
var $input = $('<input/>', {
type: 'text',
placeholder: self.l10n.search,
on: {
'blur': function () {
clearTimeout(typing);
search();
},
'keyup': function (event) {
if (event.keyCode === 13) {
clearTimeout(typing);
search();
return false;
}
else {
clearTimeout(typing);
typing = setTimeout(function () {
search();
}, 500);
}
}
}
}).appendTo(self.$container);
};
return H5PDataView;
})(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 head = document.getElementsByTagName('head')[0];
var contentId = 0; var contentId = 0;
var contents = {}; var contents = {};
/** /**
* Wraps multiple content between a prefix and a suffix. * Wraps multiple content between a prefix and a suffix.
*/ */
@ -16,25 +18,25 @@ var H5P = H5P || (function () {
} }
return result; return result;
}; };
/** /**
* *
*/ */
var loadContent = function (id, script) { var loadContent = function (id, script) {
var url = script.getAttribute('data-h5p'); var url = script.getAttribute('data-h5p');
var data, callback = 'H5P' + id; var data, callback = 'H5P' + id;
// Prevent duplicate loading. // Prevent duplicate loading.
script.removeAttribute('data-h5p'); script.removeAttribute('data-h5p');
// Callback for when content data is loaded. // Callback for when content data is loaded.
window[callback] = function (content) { window[callback] = function (content) {
contents[id] = content; contents[id] = content;
var iframe = document.createElement('iframe'); var iframe = document.createElement('iframe');
var parent = script.parentNode; var parent = script.parentNode;
parent.insertBefore(iframe, script); parent.insertBefore(iframe, script);
iframe.id = 'h5p-iframe-' + id; iframe.id = 'h5p-iframe-' + id;
iframe.style.display = 'block'; iframe.style.display = 'block';
iframe.style.width = '100%'; iframe.style.width = '100%';
@ -59,19 +61,19 @@ var H5P = H5P || (function () {
</body></html>'); </body></html>');
iframe.contentDocument.close(); iframe.contentDocument.close();
iframe.contentDocument.documentElement.style.overflow = 'hidden'; iframe.contentDocument.documentElement.style.overflow = 'hidden';
// Clean up // Clean up
parent.removeChild(script); parent.removeChild(script);
head.removeChild(data); head.removeChild(data);
delete window[callback]; delete window[callback];
}; };
// Create data script // Create data script
data = document.createElement('script'); data = document.createElement('script');
data.src = url + (url.indexOf('?') === -1 ? '?' : '&') + 'callback=' + callback; data.src = url + (url.indexOf('?') === -1 ? '?' : '&') + 'callback=' + callback;
head.appendChild(data); head.appendChild(data);
}; };
/** /**
* Go throught all script tags with the data-h5p attribute and load content. * Go throught all script tags with the data-h5p attribute and load content.
*/ */
@ -89,7 +91,7 @@ var H5P = H5P || (function () {
contentId++; contentId++;
} }
}; };
/** /**
* Return integration object * Return integration object
*/ */
@ -124,14 +126,14 @@ var H5P = H5P || (function () {
} }
}; };
}; };
// Detect if we support fullscreen, and what prefix to use. // Detect if we support fullscreen, and what prefix to use.
var fullScreenBrowserPrefix, safariBrowser; var fullScreenBrowserPrefix, safariBrowser;
if (document.documentElement.requestFullScreen) { if (document.documentElement.requestFullScreen) {
fullScreenBrowserPrefix = ''; fullScreenBrowserPrefix = '';
} }
else if (document.documentElement.webkitRequestFullScreen else if (document.documentElement.webkitRequestFullScreen &&
&& navigator.userAgent.indexOf('Android') === -1 // Skip Android navigator.userAgent.indexOf('Android') === -1 // Skip Android
) { ) {
safariBrowser = navigator.userAgent.match(/Version\/(\d)/); safariBrowser = navigator.userAgent.match(/Version\/(\d)/);
safariBrowser = (safariBrowser === null ? 0 : parseInt(safariBrowser[1])); 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 iframe = document.getElementById('h5p-iframe-' + $element.parent().data('content-id'));
var $classes = $element.add(body); var $classes = $element.add(body);
var $body = $classes.eq(1); var $body = $classes.eq(1);
/** /**
* Prepare for resize by setting the correct styles. * Prepare for resize by setting the correct styles.
* *
* @param {String} classes CSS * @param {String} classes CSS
*/ */
var before = function (classes) { var before = function (classes) {
@ -179,7 +181,7 @@ var H5P = H5P || (function () {
/** /**
* Gets called when fullscreen mode has been exited. * Gets called when fullscreen mode has been exited.
* Resizes and sets focus on content. * Resizes and sets focus on content.
* *
* @param {String} classes CSS * @param {String} classes CSS
*/ */
var done = function (classes) { var done = function (classes) {
@ -201,10 +203,10 @@ var H5P = H5P || (function () {
before('h5p-semi-fullscreen'); before('h5p-semi-fullscreen');
iframe.style.position = 'fixed'; iframe.style.position = 'fixed';
var $disable = $element.prepend('<a href="#" class="h5p-disable-fullscreen" title="Disable fullscreen"></a>').children(':first'); var $disable = $element.prepend('<a href="#" class="h5p-disable-fullscreen" title="Disable fullscreen"></a>').children(':first');
var keyup, disableSemiFullscreen = function () { var keyup, disableSemiFullscreen = function () {
$disable.remove(); $disable.remove();
$body.unbind('keyup', keyup); $body.unbind('keyup', keyup);
iframe.style.position = 'static'; iframe.style.position = 'static';
done('h5p-semi-fullscreen'); done('h5p-semi-fullscreen');
@ -247,7 +249,7 @@ var H5P = H5P || (function () {
} }
} }
}; };
return H5P; return H5P;
})(); })();

View File

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

View File

@ -1,4 +1,5 @@
var H5PLibraryList= H5PLibraryList || {}; /*jshint multistr: true */
var H5PLibraryList = H5PLibraryList || {};
(function ($) { (function ($) {
@ -7,19 +8,19 @@ var H5PLibraryList= H5PLibraryList || {};
*/ */
H5PLibraryList.init = function () { H5PLibraryList.init = function () {
var $adminContainer = H5PIntegration.getAdminContainer(); var $adminContainer = H5PIntegration.getAdminContainer();
var libraryList = H5PIntegration.getLibraryList(); var libraryList = H5PIntegration.getLibraryList();
if (libraryList.notCached) { if (libraryList.notCached) {
$adminContainer.append(H5PUtils.getRebuildCache(libraryList.notCached)); $adminContainer.append(H5PUtils.getRebuildCache(libraryList.notCached));
} }
// Create library list // Create library list
$adminContainer.append(H5PLibraryList.createLibraryList(H5PIntegration.getLibraryList())); $adminContainer.append(H5PLibraryList.createLibraryList(H5PIntegration.getLibraryList()));
}; };
/** /**
* Create the library list * Create the library list
* *
* @param {object} libraries List of libraries and headers * @param {object} libraries List of libraries and headers
*/ */
H5PLibraryList.createLibraryList = function (libraries) { H5PLibraryList.createLibraryList = function (libraries) {
@ -27,11 +28,11 @@ var H5PLibraryList= H5PLibraryList || {};
if(libraries.listData === undefined || libraries.listData.length === 0) { if(libraries.listData === undefined || libraries.listData.length === 0) {
return $('<div>' + t.NA + '</div>'); return $('<div>' + t.NA + '</div>');
} }
// Create table // Create table
var $table = H5PUtils.createTable(libraries.listHeaders); var $table = H5PUtils.createTable(libraries.listHeaders);
$table.addClass('libraries'); $table.addClass('libraries');
// Add libraries // Add libraries
$.each (libraries.listData, function (index, library) { $.each (libraries.listData, function (index, library) {
var $libraryRow = H5PUtils.createTableRow([ var $libraryRow = H5PUtils.createTableRow([
@ -55,7 +56,7 @@ var H5PLibraryList= H5PLibraryList || {};
<button class="h5p-admin-delete-library"></button>\ <button class="h5p-admin-delete-library"></button>\
</div>' </div>'
]); ]);
H5PLibraryList.addRestricted($('.h5p-admin-restricted', $libraryRow), library.restrictedUrl, library.restricted); H5PLibraryList.addRestricted($('.h5p-admin-restricted', $libraryRow), library.restrictedUrl, library.restricted);
var hasContent = !(library.numContent === '' || library.numContent === 0); var hasContent = !(library.numContent === '' || library.numContent === 0);
@ -70,12 +71,12 @@ var H5PLibraryList= H5PLibraryList || {};
window.location.href = library.upgradeUrl; window.location.href = library.upgradeUrl;
}); });
} }
// Open details view when clicked // Open details view when clicked
$('.h5p-admin-view-library', $libraryRow).on('click', function () { $('.h5p-admin-view-library', $libraryRow).on('click', function () {
window.location.href = library.detailsUrl; window.location.href = library.detailsUrl;
}); });
var $deleteButton = $('.h5p-admin-delete-library', $libraryRow); var $deleteButton = $('.h5p-admin-delete-library', $libraryRow);
if (libraries.notCached !== undefined || hasContent || (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. // Disabled delete if content.
@ -90,10 +91,10 @@ var H5PLibraryList= H5PLibraryList || {};
$table.append($libraryRow); $table.append($libraryRow);
}); });
return $table; return $table;
}; };
H5PLibraryList.addRestricted = function ($checkbox, url, selected) { H5PLibraryList.addRestricted = function ($checkbox, url, selected) {
if (selected === null) { if (selected === null) {
$checkbox.remove(); $checkbox.remove();
@ -130,5 +131,5 @@ var H5PLibraryList= H5PLibraryList || {};
H5PLibraryList.init(); H5PLibraryList.init();
} }
}); });
})(H5P.jQuery); })(H5P.jQuery);

View File

@ -3,7 +3,7 @@ var H5PUtils = H5PUtils || {};
(function ($) { (function ($) {
/** /**
* Generic function for creating a table including the headers * Generic function for creating a table including the headers
* *
* @param {array} headers List of headers * @param {array} headers List of headers
*/ */
H5PUtils.createTable = function (headers) { H5PUtils.createTable = function (headers) {
@ -12,62 +12,62 @@ var H5PUtils = H5PUtils || {};
if(headers) { if(headers) {
var $thead = $('<thead></thead>'); var $thead = $('<thead></thead>');
var $tr = $('<tr></tr>'); var $tr = $('<tr></tr>');
$.each(headers, function (index, value) { $.each(headers, function (index, value) {
if (!(value instanceof Object)) { if (!(value instanceof Object)) {
value = { value = {
html: value html: value
}; };
} }
$('<th/>', value).appendTo($tr); $('<th/>', value).appendTo($tr);
}); });
$table.append($thead.append($tr)); $table.append($thead.append($tr));
} }
return $table; return $table;
}; };
/** /**
* Generic function for creating a table row * Generic function for creating a table row
* *
* @param {array} rows Value list. Object name is used as class name in <TD> * @param {array} rows Value list. Object name is used as class name in <TD>
*/ */
H5PUtils.createTableRow = function (rows) { H5PUtils.createTableRow = function (rows) {
var $tr = $('<tr></tr>'); var $tr = $('<tr></tr>');
$.each(rows, function (index, value) { $.each(rows, function (index, value) {
if (!(value instanceof Object)) { if (!(value instanceof Object)) {
value = { value = {
html: value html: value
}; };
} }
$('<td/>', value).appendTo($tr); $('<td/>', value).appendTo($tr);
}); });
return $tr; return $tr;
}; };
/** /**
* Generic function for creating a field containing label and value * 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 * @param {string} value The value
*/ */
H5PUtils.createLabeledField = function (label, value) { H5PUtils.createLabeledField = function (label, value) {
var $field = $('<div class="h5p-labeled-field"></div>'); var $field = $('<div class="h5p-labeled-field"></div>');
$field.append('<div class="h5p-label">' + label + '</div>'); $field.append('<div class="h5p-label">' + label + '</div>');
$field.append('<div class="h5p-value">' + value + '</div>'); $field.append('<div class="h5p-value">' + value + '</div>');
return $field; return $field;
}; };
/** /**
* Replaces placeholder fields in translation strings * Replaces placeholder fields in translation strings
* *
* @param {string} template The translation template string in the following format: "$name is a $sex" * @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'} * @param {array} replacors An js object with key and values. Eg: {'$name': 'Frode', '$sex': 'male'}
*/ */
@ -77,10 +77,10 @@ var H5PUtils = H5PUtils || {};
}); });
return template; return template;
}; };
/** /**
* Get throbber with given text. * Get throbber with given text.
* *
* @param {String} text * @param {String} text
* @returns {$} * @returns {$}
*/ */
@ -90,7 +90,7 @@ var H5PUtils = H5PUtils || {};
text: text text: text
}); });
}; };
/** /**
* Makes it possbile to rebuild all content caches from admin UI. * Makes it possbile to rebuild all content caches from admin UI.
* @param {Object} notCached * @param {Object} notCached
@ -107,7 +107,7 @@ var H5PUtils = H5PUtils || {};
current++; current++;
if (current === parts.length) current = 0; if (current === parts.length) current = 0;
}, 100); }, 100);
var $counter = $container.find('.progress'); var $counter = $container.find('.progress');
var build = function () { var build = function () {
$.post(notCached.url, function (left) { $.post(notCached.url, function (left) {
@ -126,8 +126,357 @@ var H5PUtils = H5PUtils || {};
}; };
build(); build();
}); });
return $container; return $container;
}; };
})(H5P.jQuery); /**
* Generic table class with useful helpers.
*
* @class
* @param {Object} classes
* Custom html classes to use on elements.
* e.g. {tableClass: 'fixed'}.
*/
H5PUtils.Table = function (classes) {
var numCols;
var sortByCol;
var $sortCol;
var sortCol;
var sortDir;
// Create basic table
var tableOptions = {};
if (classes.table !== undefined) {
tableOptions['class'] = classes.table;
}
var $table = $('<table/>', tableOptions);
var $thead = $('<thead/>').appendTo($table);
var $tfoot = $('<tfoot/>').appendTo($table);
var $tbody = $('<tbody/>').appendTo($table);
/**
* Add columns to given table row.
*
* @private
* @param {jQuery} $tr Table row
* @param {(String|Object)} col Column properties
* @param {Number} id Used to seperate the columns
*/
var addCol = function ($tr, col, id) {
var options = {
on: {}
};
if (!(col instanceof Object)) {
options.text = col;
}
else {
if (col.text !== undefined) {
options.text = col.text;
}
if (col.class !== undefined) {
options.class = col.class;
}
if (sortByCol !== undefined && col.sortable === true) {
// Make sortable
options.role = 'button';
options.tabIndex = 1;
// This is the first sortable column, use as default sort
if (sortCol === undefined) {
sortCol = id;
sortDir = 0;
options['class'] = 'h5p-sort';
}
options.on.click = function () {
sort($th, id);
};
}
}
// Append
var $th = $('<th>', options).appendTo($tr);
if (sortCol === id) {
$sortCol = $th; // Default sort column
}
};
/**
* Updates the UI when a column header has been clicked.
* Triggers sorting callback.
*
* @private
* @param {jQuery} $th Table header
* @param {Number} id Used to seperate the columns
*/
var sort = function ($th, id) {
if (id === sortCol) {
// Change sorting direction
if (sortDir === 0) {
sortDir = 1;
$th.addClass('h5p-reverse');
}
else {
sortDir = 0;
$th.removeClass('h5p-reverse');
}
}
else {
// Change sorting column
$sortCol.removeClass('h5p-sort').removeClass('h5p-reverse');
$sortCol = $th.addClass('h5p-sort');
sortCol = id;
sortDir = 0;
}
sortByCol(sortCol, sortDir);
};
/**
* Set table headers.
*
* @public
* @param {Array} cols
* Table header data. Can be strings or objects with options like
* "text" and "sortable". E.g.
* [{text: 'Col 1', sortable: true}, 'Col 2', 'Col 3']
* @param {Function} sort Callback which is runned when sorting changes
*/
this.setHeaders = function (cols, sort) {
numCols = cols.length;
sortByCol = sort;
// Create new head
var $newThead = $('<thead/>');
var $tr = $('<tr/>').appendTo($newThead);
for (var i = 0; i < cols.length; i++) {
addCol($tr, cols[i], i);
}
// Update DOM
$thead.replaceWith($newThead);
$thead = $newThead;
};
/**
* Set table rows.
*
* @public
* @param {Array} rows Table rows with cols: [[1,'hello',3],[2,'asd',6]]
*/
this.setRows = function (rows) {
var $newTbody = $('<tbody/>');
for (var i = 0; i < rows.length; i++) {
var $tr = $('<tr/>').appendTo($newTbody);
for (var j = 0; j < rows[i].length; j++) {
$('<td>', {
html: rows[i][j]
}).appendTo($tr);
}
}
$tbody.replaceWith($newTbody);
$tbody = $newTbody;
};
/**
* Set custom table body content. This can be a message or a throbber.
* Will cover all table columns.
*
* @public
* @param {jQuery} $content Custom content
*/
this.setBody = function ($content) {
var $newTbody = $('<tbody/>');
var $tr = $('<tr/>').appendTo($newTbody);
$('<td>', {
colspan: numCols
}).append($content).appendTo($tr);
$tbody.replaceWith($newTbody);
$tbody = $newTbody;
};
/**
* Set custom table foot content. This can be a pagination widget.
* Will cover all table columns.
*
* @public
* @param {jQuery} $content Custom content
*/
this.setFoot = function ($content) {
var $newTfoot = $('<tfoot/>');
var $tr = $('<tr/>').appendTo($newTfoot);
$('<td>', {
colspan: numCols
}).append($content).appendTo($tr);
$tfoot.replaceWith($newTfoot);
};
/**
* Appends the table to the given container.
*
* @public
* @param {jQuery} $container
*/
this.appendTo = function ($container) {
$table.appendTo($container);
};
};
/**
* Generic pagination class. Creates a useful pagination widget.
*
* @class
* @param {Number} num Total number of items to pagiate.
* @param {Number} limit Number of items to dispaly per page.
* @param {Function} goneTo
* Callback which is fired when the user wants to go to another page.
* @param {Object} l10n
* Localization / translations. e.g.
* {
* currentPage: 'Page $current of $total',
* nextPage: 'Next page',
* previousPage: 'Previous page'
* }
*/
H5PUtils.Pagination = function (num, limit, goneTo, l10n) {
var current = 0;
var pages = Math.ceil(num / limit);
// Create components
// Previous button
var $left = $('<button/>', {
html: '&lt;',
'class': 'button',
title: l10n.previousPage
}).click(function () {
goTo(current - 1);
});
// Current page text
var $text = $('<span/>').click(function () {
$input.width($text.width()).show().val(current + 1).focus();
$text.hide();
});
// Jump to page input
var $input = $('<input/>', {
type: 'number',
min : 1,
max: pages,
on: {
'blur': function () {
gotInput();
},
'keyup': function (event) {
if (event.keyCode === 13) {
gotInput();
return false;
}
}
}
}).hide();
// Next button
var $right = $('<button/>', {
html: '&gt;',
'class': 'button',
title: l10n.nextPage
}).click(function () {
goTo(current + 1);
});
/**
* Check what page the user has typed in and jump to it.
*
* @private
*/
var gotInput = function () {
var page = parseInt($input.hide().val());
if (!isNaN(page)) {
goTo(page - 1);
}
$text.show();
};
/**
* Update UI elements.
*
* @private
*/
var updateUI = function () {
var next = current + 1;
// Disable or enable buttons
$left.attr('disabled', current === 0);
$right.attr('disabled', next === pages);
// Update counter
$text.html(l10n.currentPage.replace('$current', next).replace('$total', pages));
};
/**
* Try to go to the requested page.
*
* @private
* @param {Number} page
*/
var goTo = function (page) {
if (page === current || page < 0 || page >= pages) {
return; // Invalid page number
}
current = page;
updateUI();
// Fire callback
goneTo(page * limit);
};
/**
* Update number of items and limit.
*
* @public
* @param {Number} newNum Total number of items to pagiate.
* @param {Number} newLimit Number of items to dispaly per page.
*/
this.update = function (newNum, newLimit) {
if (newNum !== num || newLimit !== limit) {
// Update num and limit
num = newNum;
limit = newLimit;
pages = Math.ceil(num / limit);
$input.attr('max', pages);
if (current >= pages) {
// Content is gone, move to last page.
goTo(pages - 1);
return;
}
updateUI();
}
};
/**
* Append the pagination widget to the given container.
*
* @public
* @param {jQuery} $container
*/
this.appendTo = function ($container) {
$left.add($text).add($input).add($right).appendTo($container);
};
// Update UI
updateUI();
};
})(H5P.jQuery);

297
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? // TODO: Should we split up the generic parts needed by the editor(and others), and the parts needed to "run" H5Ps?
var H5P = H5P || {}; var H5P = H5P || {};
@ -13,12 +14,10 @@ H5P.instances = [];
if (document.documentElement.requestFullScreen) { if (document.documentElement.requestFullScreen) {
H5P.fullScreenBrowserPrefix = ''; H5P.fullScreenBrowserPrefix = '';
} }
else if (document.documentElement.webkitRequestFullScreen else if (document.documentElement.webkitRequestFullScreen) {
&& navigator.userAgent.indexOf('Android') === -1 // Skip Android
) {
H5P.safariBrowser = navigator.userAgent.match(/Version\/(\d)/); H5P.safariBrowser = navigator.userAgent.match(/Version\/(\d)/);
H5P.safariBrowser = (H5P.safariBrowser === null ? 0 : parseInt(H5P.safariBrowser[1])); H5P.safariBrowser = (H5P.safariBrowser === null ? 0 : parseInt(H5P.safariBrowser[1]));
// Do not allow fullscreen for safari < 7. // Do not allow fullscreen for safari < 7.
if (H5P.safariBrowser === 0 || H5P.safariBrowser > 6) { if (H5P.safariBrowser === 0 || H5P.safariBrowser > 6) {
H5P.fullScreenBrowserPrefix = 'webkit'; H5P.fullScreenBrowserPrefix = 'webkit';
@ -31,6 +30,9 @@ else if (document.documentElement.msRequestFullscreen) {
H5P.fullScreenBrowserPrefix = 'ms'; H5P.fullScreenBrowserPrefix = 'ms';
} }
// Keep track of when the H5Ps where started
H5P.opened = {};
/** /**
* Initialize H5P content. * Initialize H5P content.
* Scans for ".h5p-content" in the document and initializes H5P instances where found. * Scans for ".h5p-content" in the document and initializes H5P instances where found.
@ -58,14 +60,14 @@ H5P.init = function () {
// Create new instance. // Create new instance.
var instance = H5P.newRunnable(library, contentId, $container, true); var instance = H5P.newRunnable(library, contentId, $container, true);
// Check if we should add and display a fullscreen button for this H5P. // Check if we should add and display a fullscreen button for this H5P.
if (contentData.fullScreen == 1) { 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.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); H5P.fullScreen($container, instance);
}); });
}; }
var $actions = H5P.jQuery('<ul class="h5p-actions"></ul>'); var $actions = H5P.jQuery('<ul class="h5p-actions"></ul>');
if (contentData.exportUrl !== '') { if (contentData.exportUrl !== '') {
// Display export button // Display export button
@ -73,12 +75,12 @@ H5P.init = function () {
window.location.href = contentData.exportUrl; window.location.href = contentData.exportUrl;
}); });
} }
if (instance.getCopyrights !== undefined) {
// Display copyrights button // Display copyrights button
H5P.jQuery('<li class="h5p-button h5p-copyrights" role="button" tabindex="1" title="' + H5P.t('copyrightsDescription') + '">' + H5P.t('copyrights') + '</li>').appendTo($actions).click(function () { H5P.jQuery('<li class="h5p-button h5p-copyrights" role="button" tabindex="1" title="' + H5P.t('copyrightsDescription') + '">' + H5P.t('copyrights') + '</li>').appendTo($actions).click(function () {
H5P.openCopyrightsDialog($actions, instance); H5P.openCopyrightsDialog($actions, instance, library.params, contentId);
}); });
}
if (contentData.embedCode !== undefined) { if (contentData.embedCode !== undefined) {
// Display embed button // Display embed button
H5P.jQuery('<li class="h5p-button h5p-embed" role="button" tabindex="1" title="' + H5P.t('embedDescription') + '">' + H5P.t('embed') + '</li>').appendTo($actions).click(function () { H5P.jQuery('<li class="h5p-button h5p-embed" role="button" tabindex="1" title="' + H5P.t('embedDescription') + '">' + H5P.t('embed') + '</li>').appendTo($actions).click(function () {
@ -89,7 +91,17 @@ H5P.init = function () {
H5P.jQuery('<li><a class="h5p-link" href="http://h5p.org" target="_blank" title="' + H5P.t('h5pDescription') + '"></a></li>').appendTo($actions); H5P.jQuery('<li><a class="h5p-link" href="http://h5p.org" target="_blank" title="' + H5P.t('h5pDescription') + '"></a></li>').appendTo($actions);
} }
$actions.insertAfter($container); $actions.insertAfter($container);
// Keep track of when we started
H5P.opened[contentId] = new Date();
// Handle events when the user finishes the content. Useful for logging exercise results.
instance.$.on('finish', function (event) {
if (event.data !== undefined) {
H5P.setFinished(contentId, event.data.score, event.data.maxScore, event.data.time);
}
});
if (H5P.isFramed) { if (H5P.isFramed) {
// Make it possible to resize the iframe when the content changes size. This way we get no scrollbars. // 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 iframe = window.parent.document.getElementById('h5p-iframe-' + contentId);
@ -97,10 +109,10 @@ H5P.init = function () {
if (window.parent.H5P.isFullscreen) { if (window.parent.H5P.isFullscreen) {
return; // Skip if full screen. return; // Skip if full screen.
} }
// Retain parent size to avoid jumping/scrolling // Retain parent size to avoid jumping/scrolling
var parentHeight = iframe.parentElement.style.height; 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. // Reset iframe height, in case content has shrinked.
iframe.style.height = '1px'; iframe.style.height = '1px';
@ -111,7 +123,7 @@ H5P.init = function () {
// Free parent // Free parent
iframe.parentElement.style.height = parentHeight; iframe.parentElement.style.height = parentHeight;
}; };
var resizeDelay; var resizeDelay;
instance.on('resize', function () { instance.on('resize', function () {
// Use a delay to make sure iframe is resized to the correct size. // Use a delay to make sure iframe is resized to the correct size.
@ -122,8 +134,9 @@ H5P.init = function () {
}); });
H5P.instances.push(instance); H5P.instances.push(instance);
} }
instance.on('xAPI', H5P.xAPIListener); instance.on('xAPI', H5P.xAPIListener);
// Resize everything when window is resized. // Resize everything when window is resized.
$window.resize(function () { $window.resize(function () {
if (window.parent.H5P.isFullscreen) { if (window.parent.H5P.isFullscreen) {
@ -134,7 +147,7 @@ H5P.init = function () {
instance.trigger('resize'); instance.trigger('resize');
} }
}); });
// Resize content. // Resize content.
instance.trigger('resize'); instance.trigger('resize');
}); });
@ -172,7 +185,7 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) {
window.parent.H5P.fullScreen($element, instance, exitCallback, H5P.$body.get()); window.parent.H5P.fullScreen($element, instance, exitCallback, H5P.$body.get());
return; return;
} }
var $container = $element; var $container = $element;
var $classes, $iframe; var $classes, $iframe;
if (body === undefined) { if (body === undefined) {
@ -186,23 +199,23 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) {
$iframe = H5P.jQuery(iframeSelector); $iframe = H5P.jQuery(iframeSelector);
$element = $iframe.parent(); // Put iframe wrapper in fullscreen, not container. $element = $iframe.parent(); // Put iframe wrapper in fullscreen, not container.
} }
$classes = $element.add(H5P.$body).add($classes); $classes = $element.add(H5P.$body).add($classes);
/** /**
* Prepare for resize by setting the correct styles. * Prepare for resize by setting the correct styles.
* *
* @param {String} classes CSS * @param {String} classes CSS
*/ */
var before = function (classes) { var before = function (classes) {
$classes.addClass(classes); $classes.addClass(classes);
if ($iframe !== undefined) { if ($iframe !== undefined) {
// Set iframe to its default size(100%). // Set iframe to its default size(100%).
$iframe.css('height', ''); $iframe.css('height', '');
} }
}; };
/** /**
* Gets called when fullscreen mode has been entered. * Gets called when fullscreen mode has been entered.
* Resizes and sets focus on content. * Resizes and sets focus on content.
@ -212,17 +225,17 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) {
instance.trigger('resize'); instance.trigger('resize');
instance.trigger('focus'); instance.trigger('focus');
}; };
/** /**
* Gets called when fullscreen mode has been exited. * Gets called when fullscreen mode has been exited.
* Resizes and sets focus on content. * Resizes and sets focus on content.
* *
* @param {String} classes CSS * @param {String} classes CSS
*/ */
var done = function (classes) { var done = function (classes) {
H5P.isFullscreen = false; H5P.isFullscreen = false;
$classes.removeClass(classes); $classes.removeClass(classes);
// Do not rely on window resize events. // Do not rely on window resize events.
instance.trigger('resize'); instance.trigger('resize');
instance.trigger('focus'); instance.trigger('focus');
@ -235,11 +248,11 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) {
H5P.isFullscreen = true; H5P.isFullscreen = true;
if (H5P.fullScreenBrowserPrefix === undefined) { if (H5P.fullScreenBrowserPrefix === undefined) {
// Create semi fullscreen. // Create semi fullscreen.
before('h5p-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 $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 () { var keyup, disableSemiFullscreen = function () {
$disable.remove(); $disable.remove();
$body.unbind('keyup', keyup); $body.unbind('keyup', keyup);
done('h5p-semi-fullscreen'); done('h5p-semi-fullscreen');
}; };
@ -254,7 +267,7 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) {
} }
else { else {
// Create real fullscreen. // Create real fullscreen.
before('h5p-fullscreen'); before('h5p-fullscreen');
var first, eventName = (H5P.fullScreenBrowserPrefix === 'ms' ? 'MSFullscreenChange' : H5P.fullScreenBrowserPrefix + 'fullscreenchange'); var first, eventName = (H5P.fullScreenBrowserPrefix === 'ms' ? 'MSFullscreenChange' : H5P.fullScreenBrowserPrefix + 'fullscreenchange');
document.addEventListener(eventName, function () { document.addEventListener(eventName, function () {
@ -264,7 +277,7 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) {
entered(); entered();
return; return;
} }
// We are exiting fullscreen // We are exiting fullscreen
done('h5p-fullscreen'); done('h5p-fullscreen');
document.removeEventListener(eventName, arguments.callee, false); document.removeEventListener(eventName, arguments.callee, false);
@ -295,26 +308,26 @@ H5P.getPath = function (path, contentId) {
var hasProtocol = function (path) { var hasProtocol = function (path) {
return path.match(/^[a-z0-9]+:\/\//i); return path.match(/^[a-z0-9]+:\/\//i);
}; };
if (hasProtocol(path)) { if (hasProtocol(path)) {
return path; return path;
} }
if (contentId !== undefined) { if (contentId !== undefined) {
prefix = H5PIntegration.getContentPath(contentId); prefix = H5PIntegration.getContentPath(contentId);
} }
else if (window['H5PEditor'] !== undefined) { else if (window.H5PEditor !== undefined) {
prefix = H5PEditor.filesPath; prefix = H5PEditor.filesPath;
} }
else { else {
return; return;
} }
if (!hasProtocol(prefix)) { if (!hasProtocol(prefix)) {
prefix = window.parent.location.protocol + "//" + window.parent.location.host + prefix; prefix = window.parent.location.protocol + "//" + window.parent.location.host + prefix;
} }
return prefix + '/' + path; return prefix + '/' + path;
}; };
/** /**
@ -331,7 +344,7 @@ H5P.getContentPath = function (contentId) {
/** /**
* Get library class constructor from H5P by classname. * 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 * Also check out: H5P.newRunnable
* *
* Used from libraries to construct instances of other libraries' objects by name. * Used from libraries to construct instances of other libraries' objects by name.
@ -349,35 +362,37 @@ H5P.classFromName = function (name) {
* *
* TODO: Should we check if version matches the library? * TODO: Should we check if version matches the library?
* TODO: Dynamically try to load libraries currently not loaded? That will require a callback. * TODO: Dynamically try to load libraries currently not loaded? That will require a callback.
* *
* @param {Object} library Library/action object form params. * @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 {jQuery} $attachTo An optional element to attach the instance to.
* @param {Boolean} skipResize Optionally skip triggering of the resize event after attaching. * @param {Boolean} skipResize Optionally skip triggering of the resize event after attaching.
* @param {Object} The parent of this H5P * @param {Object} The parent of this H5P
* @return {Object} Instance. * @return {Object} Instance.
*/ */
H5P.newRunnable = function (library, contentId, $attachTo, skipResize, parent) { H5P.newRunnable = function (library, contentId, $attachTo, skipResize) {
var nameSplit, versionSplit;
try { try {
var nameSplit = library.library.split(' ', 2); nameSplit = library.library.split(' ', 2);
var versionSplit = nameSplit[1].split('.', 2); versionSplit = nameSplit[1].split('.', 2);
} }
catch (err) { catch (err) {
return H5P.error('Invalid library string: ' + library.library); return H5P.error('Invalid library string: ' + library.library);
} }
if ((library.params instanceof Object) !== true || (library.params instanceof Array) === true) { if ((library.params instanceof Object) !== true || (library.params instanceof Array) === true) {
H5P.error('Invalid library params for: ' + library.library); H5P.error('Invalid library params for: ' + library.library);
return H5P.error(library.params); return H5P.error(library.params);
} }
// Find constructor function // Find constructor function
var constructor;
try { try {
nameSplit = nameSplit[0].split('.'); nameSplit = nameSplit[0].split('.');
var constructor = window; constructor = window;
for (var i = 0; i < nameSplit.length; i++) { for (var i = 0; i < nameSplit.length; i++) {
constructor = constructor[nameSplit[i]]; constructor = constructor[nameSplit[i]];
}; }
if (typeof constructor !== 'function') { if (typeof constructor !== 'function') {
throw null; throw null;
} }
@ -385,7 +400,7 @@ H5P.newRunnable = function (library, contentId, $attachTo, skipResize, parent) {
catch (err) { catch (err) {
return H5P.error('Unable to find constructor for: ' + library.library); return H5P.error('Unable to find constructor for: ' + library.library);
} }
var instance = new constructor(library.params, contentId); var instance = new constructor(library.params, contentId);
H5P.addContentTypeFeatures(instance); H5P.addContentTypeFeatures(instance);
@ -402,7 +417,7 @@ H5P.newRunnable = function (library, contentId, $attachTo, skipResize, parent) {
if ($attachTo !== undefined) { if ($attachTo !== undefined) {
instance.attach($attachTo); instance.attach($attachTo);
if (skipResize === undefined || !skipResize) { if (skipResize === undefined || !skipResize) {
// Resize content. // Resize content.
instance.trigger('resize'); instance.trigger('resize');
@ -568,18 +583,18 @@ H5P.t = function (key, vars, ns) {
if (H5PIntegration.i18n[ns] === undefined) { if (H5PIntegration.i18n[ns] === undefined) {
return '[Missing translation namespace "' + ns + '"]'; return '[Missing translation namespace "' + ns + '"]';
} }
if (H5PIntegration.i18n[ns][key] === undefined) { if (H5PIntegration.i18n[ns][key] === undefined) {
return '[Missing translation "' + key + '" in "' + ns + '"]'; return '[Missing translation "' + key + '" in "' + ns + '"]';
} }
var translation = H5PIntegration.i18n[ns][key]; var translation = H5PIntegration.i18n[ns][key];
if (vars !== undefined) { if (vars !== undefined) {
// Replace placeholder with variables. // Replace placeholder with variables.
for (var placeholder in vars) { for (var placeholder in vars) {
translation = translation.replace(placeholder, vars[placeholder]); translation = translation.replace(placeholder, vars[placeholder]);
} }
} }
return translation; return translation;
@ -608,7 +623,7 @@ H5P.Dialog = function (name, title, content, $element) {
}) })
.end() .end()
.end(); .end();
this.open = function () { this.open = function () {
setTimeout(function () { setTimeout(function () {
$dialog.addClass('h5p-open'); // Fade in $dialog.addClass('h5p-open'); // Fade in
@ -616,7 +631,7 @@ H5P.Dialog = function (name, title, content, $element) {
H5P.jQuery(self).trigger('dialog-opened', [$dialog]); H5P.jQuery(self).trigger('dialog-opened', [$dialog]);
}, 1); }, 1);
}; };
this.close = function () { this.close = function () {
$dialog.removeClass('h5p-open'); // Fade out $dialog.removeClass('h5p-open'); // Fade out
setTimeout(function () { setTimeout(function () {
@ -632,19 +647,77 @@ H5P.Dialog = function (name, title, content, $element) {
* @param {object} instance to get copyright information from. * @param {object} instance to get copyright information from.
* @returns {undefined} * @returns {undefined}
*/ */
H5P.openCopyrightsDialog = function ($element, instance) { H5P.openCopyrightsDialog = function ($element, instance, parameters, contentId) {
var copyrights = instance.getCopyrights(); var copyrights;
if (instance.getCopyrights !== undefined) {
// Use the instance's own copyright generator
copyrights = instance.getCopyrights();
}
else {
// Create a generic flat copyright list
copyrights = new H5P.ContentCopyrights();
H5P.findCopyrights(copyrights, parameters, contentId);
}
if (copyrights !== undefined) { if (copyrights !== undefined) {
// Convert to string
copyrights = copyrights.toString(); copyrights = copyrights.toString();
} }
if (copyrights === undefined || copyrights === '') { if (copyrights === undefined || copyrights === '') {
// Use no copyrights default text
copyrights = H5P.t('noCopyrights'); copyrights = H5P.t('noCopyrights');
} }
// Open dialog with copyright information
var dialog = new H5P.Dialog('copyrights', H5P.t('copyrightInformation'), copyrights, $element); var dialog = new H5P.Dialog('copyrights', H5P.t('copyrightInformation'), copyrights, $element);
dialog.open(); dialog.open();
}; };
/**
* 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}
*/
H5P.findCopyrights = function (info, parameters, contentId) {
// Cycle through parameters
for (var field in parameters) {
if (!parameters.hasOwnProperty(field)) {
continue; // Do not check
}
var value = parameters[field];
if (value instanceof Array) {
// Cycle through array
H5P.findCopyrights(info, value, contentId);
}
else if (value instanceof Object) {
// Check if object is a file with copyrights
if (value.copyright === undefined ||
value.copyright.license === undefined ||
value.path === undefined ||
value.mime === undefined) {
// Nope, cycle throught object
H5P.findCopyrights(info, value, contentId);
}
else {
// Found file, add copyrights
var copyrights = new H5P.MediaCopyright(value.copyright);
if (value.width !== undefined && value.height !== undefined) {
copyrights.setThumbnail(new H5P.Thumbnail(H5P.getPath(value.path, contentId), value.width, value.height));
}
info.addMedia(copyrights);
}
}
else {
}
}
};
/** /**
* Display a dialog containing the embed code. * Display a dialog containing the embed code.
* *
@ -654,12 +727,12 @@ H5P.openCopyrightsDialog = function ($element, instance) {
*/ */
H5P.openEmbedDialog = function ($element, embedCode) { H5P.openEmbedDialog = function ($element, embedCode) {
var dialog = new H5P.Dialog('embed', H5P.t('embed'), '<textarea class="h5p-embed-code-container">' + embedCode + '</textarea>', $element); 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 // Selecting embed code when dialog is opened
H5P.jQuery(dialog).on('dialog-opened', function (event, $dialog) { H5P.jQuery(dialog).on('dialog-opened', function (event, $dialog) {
$dialog.find('.h5p-embed-code-container').select(); $dialog.find('.h5p-embed-code-container').select();
}); });
dialog.open(); dialog.open();
}; };
@ -670,7 +743,7 @@ H5P.ContentCopyrights = function () {
var label; var label;
var media = []; var media = [];
var content = []; var content = [];
/** /**
* Public. Set label. * Public. Set label.
* *
@ -679,7 +752,7 @@ H5P.ContentCopyrights = function () {
this.setLabel = function (newLabel) { this.setLabel = function (newLabel) {
label = newLabel; label = newLabel;
}; };
/** /**
* Public. Add sub content. * Public. Add sub content.
* *
@ -690,7 +763,7 @@ H5P.ContentCopyrights = function () {
media.push(newMedia); media.push(newMedia);
} }
}; };
/** /**
* Public. Add sub content. * Public. Add sub content.
* *
@ -701,7 +774,7 @@ H5P.ContentCopyrights = function () {
content.push(newContent); content.push(newContent);
} }
}; };
/** /**
* Public. Print content copyright. * Public. Print content copyright.
* *
@ -709,28 +782,28 @@ H5P.ContentCopyrights = function () {
*/ */
this.toString = function () { this.toString = function () {
var html = ''; var html = '';
// Add media rights // Add media rights
for (var i = 0; i < media.length; i++) { for (var i = 0; i < media.length; i++) {
html += media[i]; html += media[i];
} }
// Add sub content rights // Add sub content rights
for (var i = 0; i < content.length; i++) { for (i = 0; i < content.length; i++) {
html += content[i]; html += content[i];
} }
if (html !== '') { if (html !== '') {
// Add a label to this info // Add a label to this info
if (label !== undefined) { if (label !== undefined) {
html = '<h3>' + label + '</h3>' + html; html = '<h3>' + label + '</h3>' + html;
} }
// Add wrapper // Add wrapper
html = '<div class="h5p-content-copyrights">' + html + '</div>'; html = '<div class="h5p-content-copyrights">' + html + '</div>';
} }
return html; return html;
}; };
}; };
@ -746,35 +819,35 @@ H5P.ContentCopyrights = function () {
H5P.MediaCopyright = function (copyright, labels, order, extraFields) { H5P.MediaCopyright = function (copyright, labels, order, extraFields) {
var thumbnail; var thumbnail;
var list = new H5P.DefinitionList(); var list = new H5P.DefinitionList();
/** /**
* Private. Get translated label for field. * Private. Get translated label for field.
* *
* @param {String} fieldName * @param {String} fieldName
* @return {String} * @return {String}
*/ */
var getLabel = function (fieldName) { var getLabel = function (fieldName) {
if (labels === undefined || labels[fieldName] === undefined) { if (labels === undefined || labels[fieldName] === undefined) {
return H5P.t(fieldName); return H5P.t(fieldName);
} }
return labels[fieldName]; return labels[fieldName];
}; };
/** /**
* Private. Get humanized value for field. * Private. Get humanized value for field.
* *
* @param {String} fieldName * @param {String} fieldName
* @return {String} * @return {String}
*/ */
var humanizeValue = function (fieldName, value) { var humanizeValue = function (fieldName, value) {
if (fieldName === 'license') { if (fieldName === 'license') {
return H5P.copyrightLicenses[value]; return H5P.copyrightLicenses[value];
} }
return value; return value;
}; };
if (copyright !== undefined) { if (copyright !== undefined) {
// Add the extra fields // Add the extra fields
for (var field in extraFields) { for (var field in extraFields) {
@ -782,12 +855,12 @@ H5P.MediaCopyright = function (copyright, labels, order, extraFields) {
copyright[field] = extraFields[field]; copyright[field] = extraFields[field];
} }
} }
if (order === undefined) { if (order === undefined) {
// Set default order // Set default order
order = ['title', 'author', 'year', 'source', 'license']; order = ['title', 'author', 'year', 'source', 'license'];
} }
for (var i = 0; i < order.length; i++) { for (var i = 0; i < order.length; i++) {
var fieldName = order[i]; var fieldName = order[i];
if (copyright[fieldName] !== undefined) { if (copyright[fieldName] !== undefined) {
@ -795,7 +868,7 @@ H5P.MediaCopyright = function (copyright, labels, order, extraFields) {
} }
} }
} }
/** /**
* Public. Set thumbnail. * Public. Set thumbnail.
* *
@ -804,7 +877,7 @@ H5P.MediaCopyright = function (copyright, labels, order, extraFields) {
this.setThumbnail = function (newThumbnail) { this.setThumbnail = function (newThumbnail) {
thumbnail = newThumbnail; thumbnail = newThumbnail;
}; };
/** /**
* Public. Checks if this copyright is undisclosed. * Public. Checks if this copyright is undisclosed.
* I.e. only has the license attribute set, and it's undisclosed. * I.e. only has the license attribute set, and it's undisclosed.
@ -820,7 +893,7 @@ H5P.MediaCopyright = function (copyright, labels, order, extraFields) {
} }
return false; return false;
}; };
/** /**
* Public. Print media copyright. * Public. Print media copyright.
* *
@ -828,20 +901,20 @@ H5P.MediaCopyright = function (copyright, labels, order, extraFields) {
*/ */
this.toString = function () { this.toString = function () {
var html = ''; var html = '';
if (this.undisclosed()) { if (this.undisclosed()) {
return html; // No need to print a copyright with a single undisclosed license. return html; // No need to print a copyright with a single undisclosed license.
} }
if (thumbnail !== undefined) { if (thumbnail !== undefined) {
html += thumbnail; html += thumbnail;
} }
html += list; html += list;
if (html !== '') { if (html !== '') {
html = '<div class="h5p-media-copyright">' + html + '</div>'; html = '<div class="h5p-media-copyright">' + html + '</div>';
} }
return html; return html;
}; };
}; };
@ -893,16 +966,16 @@ H5P.Field = function (label, value) {
* Public. Get field label. * Public. Get field label.
* *
* @returns {String} * @returns {String}
*/ */
this.getLabel = function () { this.getLabel = function () {
return label; return label;
}; };
/** /**
* Public. Get field value. * Public. Get field value.
* *
* @returns {String} * @returns {String}
*/ */
this.getValue = function () { this.getValue = function () {
return value; return value;
}; };
@ -913,7 +986,7 @@ H5P.Field = function (label, value) {
*/ */
H5P.DefinitionList = function () { H5P.DefinitionList = function () {
var fields = []; var fields = [];
/** /**
* Public. Add field to list. * Public. Add field to list.
* *
@ -922,7 +995,7 @@ H5P.DefinitionList = function () {
this.add = function (field) { this.add = function (field) {
fields.push(field); fields.push(field);
}; };
/** /**
* Public. Get Number of fields. * Public. Get Number of fields.
* *
@ -931,7 +1004,7 @@ H5P.DefinitionList = function () {
this.size = function () { this.size = function () {
return fields.length; return fields.length;
}; };
/** /**
* Public. Get field at given index. * Public. Get field at given index.
* *
@ -941,7 +1014,7 @@ H5P.DefinitionList = function () {
this.get = function (index) { this.get = function (index) {
return fields[index]; return fields[index];
}; };
/** /**
* Public. Print definition list. * Public. Print definition list.
* *
@ -1030,7 +1103,7 @@ H5P.getLibraryPath = function (library) {
/** /**
* Recursivly clone the given object. * 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 {object} object Object to clone.
* @param {type} recursive * @param {type} recursive
@ -1113,16 +1186,38 @@ H5P.shuffleArray = function (array) {
}; };
/** /**
* DEPRECATED! Do not use this function directly, trigger the finish event
* instead.
*
* Post finished results for user. * Post finished results for user.
* TODO: Should we use events instead? That way the parent can handle the results of the child.
* *
* @param {Number} contentId * @param {Number} contentId
* @param {Number} points * @param {Number} score achieved
* @param {Number} maxPoints * @param {Number} maxScore that can be achieved
* @param {Number} time optional reported time usage
*/ */
H5P.setFinished = function (contentId, points, maxPoints) { H5P.setFinished = function (contentId, score, maxScore, time) {
if (H5P.postUserStatistics === true) { if (H5P.postUserStatistics === true) {
H5P.jQuery.post(H5P.ajaxPath + 'setFinished', {contentId: contentId, points: points, maxPoints: maxPoints}); /**
* Return unix timestamp for the given JS Date.
*
* @param {Date} date
* @returns {Number}
*/
var toUnix = function (date) {
return Math.round(date.getTime() / 1000);
};
// Post the results
// TODO: Should we use a variable with the complete path?
H5P.jQuery.post(H5P.ajaxPath + 'setFinished', {
contentId: contentId,
score: score,
maxScore: maxScore,
opened: toUnix(H5P.opened[contentId]),
finished: toUnix(new Date()),
time: time
});
} }
}; };

1
js/jquery.js vendored

File diff suppressed because one or more lines are too long

View File

@ -11,7 +11,8 @@
border: none; border: none;
} }
.h5p-admin-table tr:nth-child(odd) { .h5p-admin-table tr:nth-child(odd),
.h5p-data-view tr:nth-child(odd) {
background-color: #F9F9F9; background-color: #F9F9F9;
} }
.h5p-admin-table tbody tr:hover { .h5p-admin-table tbody tr:hover {
@ -30,7 +31,7 @@
} }
.h5p-admin-buttons-wrapper { .h5p-admin-buttons-wrapper {
white-space: nowrap; white-space: nowrap;
} }
.h5p-admin-table.libraries button { .h5p-admin-table.libraries button {
@ -69,13 +70,13 @@
} }
.h5p-admin-upgrade-library { .h5p-admin-upgrade-library {
color: #339900; color: #339900;
} }
.h5p-admin-view-library { .h5p-admin-view-library {
color: #0066cc; color: #0066cc;
} }
.h5p-admin-delete-library { .h5p-admin-delete-library {
color: #990000; color: #990000;
} }
.h5p-admin-delete-library:disabled, .h5p-admin-delete-library:disabled,
.h5p-admin-upgrade-library:disabled { .h5p-admin-upgrade-library:disabled {
@ -86,9 +87,9 @@
.h5p-library-info { .h5p-library-info {
padding: 1em 1em; padding: 1em 1em;
margin: 1em 0; margin: 1em 0;
width: 350px; width: 350px;
border: 1px solid #DDD; border: 1px solid #DDD;
border-radius: 3px; border-radius: 3px;
} }
@ -118,14 +119,14 @@
.h5p-content-search { .h5p-content-search {
display: inline-block; display: inline-block;
position: relative; position: relative;
width: 100%; width: 100%;
padding: 5px 0; padding: 5px 0;
margin-top: 10px; margin-top: 10px;
border: 1px solid #CCC; border: 1px solid #CCC;
border-radius: 3px; border-radius: 3px;
box-shadow: 2px 2px 5px #888888; box-shadow: 2px 2px 5px #888888;
} }
.h5p-content-search:before { .h5p-content-search:before {
font-family: 'H5P'; font-family: 'H5P';
@ -219,7 +220,7 @@ button.h5p-admin.disabled:hover {
.h5p-content-pager > .pager-info:hover { .h5p-content-pager > .pager-info:hover {
background-color: #555; background-color: #555;
color: #FFF; color: #FFF;
} }
.h5p-content-pager > .pager-info, .h5p-content-pager > .pager-info,
.h5p-content-pager > .h5p-pager-goto { .h5p-content-pager > .h5p-pager-goto {
margin: 0 10px; margin: 0 10px;
@ -244,4 +245,41 @@ button.h5p-admin.disabled:hover {
padding: 0 0.5em; padding: 0 0.5em;
font-size: 1.5em; font-size: 1.5em;
font-weight: bold; font-weight: bold;
} }
#h5p-admin-container .h5p-admin-center {
text-align: center;
}
.h5p-pagination {
text-align: center;
}
.h5p-pagination > span, .h5p-pagination > input {
margin: 0 1em;
}
.h5p-data-view input[type="text"] {
margin-bottom: 0.5em;
}
.h5p-data-view input[type="text"]::-ms-clear {
display: none;
}
.h5p-data-view th[role="button"] {
cursor: pointer;
}
.h5p-data-view th[role="button"].h5p-sort:after,
.h5p-data-view th[role="button"]:hover:after,
.h5p-data-view th[role="button"].h5p-sort.h5p-reverse:hover:after {
content: "\25BE";
position: relative;
left: 0.5em;
top: -1px;
}
.h5p-data-view th[role="button"].h5p-sort.h5p-reverse:after,
.h5p-data-view th[role="button"].h5p-sort:hover:after {
content: "\25B4";
top: -2px;
}
.h5p-data-view th[role="button"]:hover:after,
.h5p-data-view th[role="button"].h5p-sort.h5p-reverse:hover:after,
.h5p-data-view th[role="button"].h5p-sort:hover:after {
color: #999;
}

View File

@ -234,6 +234,8 @@ div.h5p-fullscreen {
font-size: 1.5em; font-size: 1.5em;
margin: 0.25em 0; margin: 0.25em 0;
position: absolute; position: absolute;
line-height: 1.25em;
padding: 0;
} }
.h5p-embed-dialog .h5p-inner { .h5p-embed-dialog .h5p-inner {
width: 50%; width: 50%;
@ -322,4 +324,4 @@ div.h5p-fullscreen {
padding-left: 38px; padding-left: 38px;
min-height: 30px; min-height: 30px;
line-height: 30px; line-height: 30px;
} }