Synced library with module 6.x-1.x.

namespaces
Frode Petterson 2014-03-26 08:43:29 +01:00
parent c06123a21e
commit 81feec677d
13 changed files with 2430 additions and 305 deletions

BIN
fonts/h5p.eot Normal file

Binary file not shown.

12
fonts/h5p.svg Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Generated by IcoMoon</metadata>
<defs>
<font id="h5p_toolbar" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="960" descent="-64" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" d="" horiz-adv-x="512" />
<glyph unicode="&#xe667;" d="M918.466 608.215c-21.625 20.696-53.948 30.461-97.198 30.461h-146.495v-81.386h-173.934l-14.184-58.599c11.86 5.581 28.834 11.161 40.461 13.951 11.626 2.79 23.254 2.325 34.647 2.325 38.601 0 69.993-11.86 94.176-35.113 24.183-23.486 36.042-53.017 36.042-88.361 0-24.882-6.278-49.064-18.603-72.085-12.323-23.020-29.996-41.158-52.786-53.25-8.138-4.419-17.208-7.673-26.974-11.161h148.588v141.845h65.806c48.135 0 83.944 10.697 107.43 32.789 23.486 22.091 35.113 52.088 35.113 91.154 0.233 37.902-10.464 66.969-32.089 87.432zM820.572 486.134c-9.302-7.906-25.578-12.556-48.599-12.556h-29.764v86.037h34.414c22.324 0 37.67-3.488 45.577-12.093 8.138-8.372 12.093-18.603 12.093-30.694 0.233-12.556-4.419-22.787-13.719-30.694zM523.161 445.442c-20.929 0-39.066-12.093-47.669-29.532l-104.64 14.184 47.203 208.581h-94.408v-162.774h-132.544v162.774h-118.591v-383.678h118.591v139.52h132.544v-139.52h143.24c-17.906 4.884-33.020 12.323-45.577 21.858-12.789 9.535-23.020 20.696-30.926 32.789-7.906 12.093-14.184 25.811-19.532 43.715l104.873 15.114c8.835-17.439 26.742-29.299 47.436-29.299 29.299 0 53.25 23.719 53.25 53.25s-23.949 53.017-53.25 53.017z" />
<glyph unicode="&#xe668;" d="M766.4 377.6c-11.2 1.6-19.2 1.6-27.2 1.6-1.6 0-3.2 1.6-4.8 3.2-67.2 60.8-136 121.6-203.2 182.4-1.6 1.6-4.8 1.6-6.4 1.6-24-6.4-48-12.8-73.6-19.2-25.6-4.8-51.2-1.6-75.2 12.8-12.8 8-22.4 19.2-28.8 33.6-4.8 9.6 1.6 22.4 12.8 25.6 43.2 12.8 86.4 28.8 128 44.8 12.8 4.8 27.2 8 41.6 6.4 4.8 0 9.6-3.2 14.4-4.8 41.6-16 83.2-30.4 124.8-46.4 1.6-1.6 4.8-1.6 8 0 30.4 8 62.4 17.6 92.8 25.6 3.2 1.6 4.8 0 6.4-1.6 14.4-17.6 24-38.4 35.2-57.6 25.6-51.2 48-104 70.4-156.8 1.6-3.2 0-3.2-1.6-6.4-20.8-22.4-46.4-35.2-76.8-41.6-12.8-1.6-25.6-3.2-36.8-3.2zM272 436.8c19.2 9.6 36.8 6.4 51.2-9.6 12.8-12.8 12.8-28.8 3.2-49.6 19.2 3.2 33.6-3.2 43.2-19.2 11.2-17.6 8-33.6-6.4-49.6 4.8 0 11.2 0 16-1.6 14.4-3.2 25.6-12.8 30.4-27.2 4.8-14.4 1.6-27.2-8-36.8-4.8-6.4-11.2-11.2-16-17.6-4.8-6.4-11.2-11.2-16-17.6-14.4-14.4-38.4-16-52.8-1.6-30.4 30.4-56 64-83.2 97.6-17.6 22.4-33.6 43.2-49.6 65.6-8 9.6-12.8 19.2-14.4 32 0 8 1.6 16 8 22.4 9.6 9.6 17.6 19.2 27.2 28.8 17.6 17.6 48 12.8 62.4-8 1.6-1.6 3.2-4.8 4.8-8zM726.4 353.6c11.2-1.6 24-1.6 35.2-3.2 1.6-8 1.6-17.6 0-25.6-6.4-30.4-24-49.6-52.8-59.2-1.6 0-3.2-1.6-3.2-3.2-9.6-32-33.6-52.8-67.2-56-3.2 0-3.2-1.6-4.8-3.2-17.6-33.6-57.6-49.6-91.2-36.8-4.8 1.6-9.6 4.8-14.4 6.4-6.4-6.4-14.4-12.8-22.4-16-27.2-12.8-57.6-6.4-78.4 14.4-9.6 9.6-19.2 19.2-30.4 28.8 8 8 14.4 16 24 25.6 1.6-1.6 3.2-3.2 3.2-4.8 8-9.6 17.6-17.6 25.6-25.6 17.6-16 46.4-12.8 57.6 8 0 0 0 0 0 1.6-1.6 0-1.6 1.6-3.2 1.6-22.4 22.4-46.4 46.4-68.8 68.8-3.2 3.2-4.8 8-3.2 12.8 1.6 4.8 4.8 8 9.6 9.6 4.8 1.6 9.6 0 12.8-4.8 14.4-14.4 30.4-30.4 44.8-44.8 14.4-14.4 30.4-28.8 44.8-44.8 8-9.6 19.2-11.2 30.4-9.6 14.4 3.2 24 11.2 30.4 25.6 1.6 3.2 0 4.8-1.6 6.4-43.2 43.2-86.4 84.8-128 128-3.2 3.2-6.4 8-4.8 14.4 1.6 9.6 12.8 14.4 22.4 8 1.6-1.6 3.2-1.6 3.2-3.2 43.2-43.2 88-88 131.2-131.2 3.2-3.2 4.8-3.2 8-3.2 17.6 1.6 33.6 16 36.8 33.6 0 3.2 0 4.8-1.6 6.4-49.6 49.6-99.2 99.2-148.8 148.8-3.2 3.2-4.8 6.4-4.8 11.2 0 4.8 3.2 11.2 8 12.8 4.8 1.6 9.6 1.6 14.4-3.2 3.2-3.2 8-8 11.2-11.2 35.2-35.2 70.4-70.4 105.6-105.6 11.2-11.2 22.4-20.8 32-32 1.6-1.6 4.8-3.2 6.4-1.6 24 4.8 38.4 30.4 28.8 54.4 3.2 0 3.2 0 3.2 1.6zM452.8 680c-17.6-6.4-33.6-11.2-49.6-17.6-1.6 0-3.2 0-4.8 0-44.8 14.4-91.2 28.8-136 43.2-3.2 1.6-4.8 0-8-1.6-43.2-36.8-70.4-81.6-78.4-137.6-1.6-16-3.2-32-1.6-48 0-9.6 4.8-17.6 11.2-25.6 3.2-4.8 6.4-8 8-9.6-8-8-14.4-16-22.4-25.6-16 19.2-28.8 38.4-30.4 64-1.6 44.8 4.8 86.4 25.6 126.4 17.6 33.6 40 62.4 72 86.4 3.2 3.2 8 4.8 11.2 8 1.6 1.6 3.2 1.6 6.4 1.6 32-9.6 62.4-20.8 94.4-30.4 33.6-11.2 65.6-20.8 99.2-32 1.6 0 1.6 0 3.2-1.6z" />
</font></defs></svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
fonts/h5p.ttf Normal file

Binary file not shown.

BIN
fonts/h5p.woff Normal file

Binary file not shown.

177
h5p-development.class.php Normal file
View File

@ -0,0 +1,177 @@
<?php
/**
* This is a data layer which uses the file system so it isn't specific to any framework.
*/
class H5PDevelopment {
const MODE_NONE = 0;
const MODE_CONTENT = 1;
const MODE_LIBRARY = 2;
private $h5pF, $libraries, $language;
/**
* Constructor.
*
* @param object $H5PFramework
* The frameworks implementation of the H5PFrameworkInterface
* @param string $filesPath
* Path to where H5P should store its files
* @param array $libraries Optional cache input.
*/
public function __construct($H5PFramework, $filesPath, $language, $libraries = NULL) {
$this->h5pF = $H5PFramework;
$this->language = $language;
if ($libraries !== NULL) {
$this->libraries = $libraries;
}
else {
$this->findLibraries($filesPath . '/development');
}
}
/**
* Get contents of file.
*
* @param string File path.
* @return mixed String on success or NULL on failure.
*/
private function getFileContents($file) {
if (file_exists($file) === FALSE) {
return NULL;
}
$contents = file_get_contents($file);
if ($contents === FALSE) {
return NULL;
}
return $contents;
}
/**
* Scans development directory and find all libraries.
*
* @param string $path Libraries development folder
*/
private function findLibraries($path) {
$this->libraries = array();
if (is_dir($path) === FALSE) {
return;
}
$contents = scandir($path);
for ($i = 0, $s = count($contents); $i < $s; $i++) {
if ($contents[$i]{0} === '.') {
continue; // Skip hidden stuff.
}
$libraryPath = $path . '/' . $contents[$i];
$libraryJSON = $this->getFileContents($libraryPath . '/library.json');
if ($libraryJSON === NULL) {
continue; // No JSON file, skip.
}
$library = json_decode($libraryJSON, TRUE);
if ($library === FALSE) {
continue; // Invalid JSON.
}
// TODO: Validate props? Not really needed, is it? this is a dev site.
// Save/update library.
$library['libraryId'] = $this->h5pF->getLibraryId($library['machineName'], $library['majorVersion'], $library['minorVersion']);
$this->h5pF->saveLibraryData($library, $library['libraryId'] === FALSE);
$library['path'] = $libraryPath;
$this->libraries[H5PDevelopment::libraryToString($library['machineName'], $library['majorVersion'], $library['minorVersion'])] = $library;
}
// TODO: Should we remove libraries without files? Not really needed, but must be cleaned up some time, right?
// Go trough libraries and insert dependencies. Missing deps. will just be ignored and not available. (I guess?!)
foreach ($this->libraries as $library) {
$this->h5pF->deleteLibraryDependencies($library['libraryId']);
// This isn't optimal, but without it we would get duplicate warnings.
$types = array('preloaded', 'dynamic', 'editor');
foreach ($types as $type) {
if (isset($library[$type . 'Dependencies'])) {
$this->h5pF->saveLibraryDependencies($library['libraryId'], $library[$type . 'Dependencies'], $type);
}
}
}
// TODO: Deps must be inserted into h5p_nodes_libraries as well... ? But only if they are used?!
}
/**
* @return array Libraris in development folder.
*/
public function getLibraries() {
return $this->libraries;
}
/**
* Get library
*
* @param string $name of the library.
* @param int $majorVersion of the library.
* @param int $minorVersion of the library.
* @return array library.
*/
public function getLibrary($name, $majorVersion, $minorVersion) {
$library = H5PDevelopment::libraryToString($name, $majorVersion, $minorVersion);
return isset($this->libraries[$library]) === TRUE ? $this->libraries[$library] : NULL;
}
/**
* Get semantics for the given library.
*
* @param string $name of the library.
* @param int $majorVersion of the library.
* @param int $minorVersion of the library.
* @return string Semantics
*/
public function getSemantics($name, $majorVersion, $minorVersion) {
$library = H5PDevelopment::libraryToString($name, $majorVersion, $minorVersion);
if (isset($this->libraries[$library]) === FALSE) {
return NULL;
}
return $this->getFileContents($this->libraries[$library]['path'] . '/semantics.json');
}
/**
* Get translations for the given library.
*
* @param string $name of the library.
* @param int $majorVersion of the library.
* @param int $minorVersion of the library.
* @return string Translation
*/
public function getLanguage($name, $majorVersion, $minorVersion) {
$library = H5PDevelopment::libraryToString($name, $majorVersion, $minorVersion);
if (isset($this->libraries[$library]) === FALSE) {
return NULL;
}
return $this->getFileContents($this->libraries[$library]['path'] . '/language/' . $this->language . '.json');
}
/**
* Writes library as string on the form "name majorVersion.minorVersion"
*
* @param string $name Machine readable library name
* @param integer $majorVersion
* @param integer $majorVersion
* @return string Library identifier.
*/
public static function libraryToString($name, $majorVersion, $minorVersion) {
return $name . ' ' . $majorVersion . '.' . $minorVersion;
}
}

View File

@ -3,6 +3,7 @@
* Interface defining functions the h5p library needs the framework to implement
*/
interface H5PFrameworkInterface {
/**
* Show the user an error message
*
@ -79,7 +80,7 @@ interface H5PFrameworkInterface {
* @param string $defaultLibraryWhitelist
*/
public function getWhitelist($isLibrary, $defaultContentWhitelist, $defaultLibraryWhitelist);
/**
* Is the library a patched version of an existing library?
*
@ -187,6 +188,15 @@ interface H5PFrameworkInterface {
*/
public function saveLibraryUsage($contentId, $librariesInUse);
/**
* Get number of content/nodes using a library, and the number of
* dependencies to other libraries
*
* @param int $library_id
* @return array The array contains two elements, keyed by 'content' and 'libraries'.
* Each element contains a number
*/
public function getLibraryUsage($libraryId);
/**
* Loads a library
@ -201,16 +211,24 @@ interface H5PFrameworkInterface {
public function loadLibrary($machineName, $majorVersion, $minorVersion);
/**
* Loads and decodes library semantics.
* Loads library semantics.
*
* @param string $machineName
* @param int $majorVersion
* @param int $minorVersion
* @return array|FALSE
* Array representing the library with dependency descriptions
* FALSE if the library doesn't exist
* @param string $name library identifier.
* @param int $majorVersion library identifier.
* @param int $minorVersion library identifier.
* @return string semantics.
*/
public function getLibrarySemantics($machineName, $majorVersion, $minorVersion);
public function loadLibrarySemantics($name, $majorVersion, $minorVersion);
/**
* Makes it possible to alter the semantics, adding custom fields, etc.
*
* @param array $semantics
* @param string $name library identifier.
* @param int $majorVersion library identifier.
* @param int $minorVersion library identifier.
*/
public function alterLibrarySemantics(&$semantics, $name, $majorVersion, $minorVersion);
/**
* Delete all dependencies belonging to given library
@ -219,6 +237,13 @@ interface H5PFrameworkInterface {
* Library Id
*/
public function deleteLibraryDependencies($libraryId);
/**
* Delete a library from database and file system
*
* @param int $libraryId Library Id
*/
public function deleteLibrary($libraryId);
/**
* Get all the data we need to export H5P
@ -246,6 +271,22 @@ interface H5PFrameworkInterface {
* Check if export is enabled.
*/
public function isExportEnabled();
/**
* Load content.
*
* @return object Content, null if not found.
*/
public function loadContent($id);
/**
* Load dependencies for the given content of the given type.
*
* @param int $id content.
* @param int $type dependency.
* @return array
*/
public function loadContentDependencies($id, $type = NULL);
}
/**
@ -349,25 +390,25 @@ class H5PValidator {
* @return boolean
* TRUE if the .h5p file is valid
*/
public function isValidPackage() {
public function isValidPackage($skipContent = FALSE) {
// Create a temporary dir to extract package in.
$tmpDir = $this->h5pF->getUploadedH5pFolderPath();
$tmp_path = $this->h5pF->getUploadedH5pPath();
$tmpPath = $this->h5pF->getUploadedH5pPath();
$valid = TRUE;
// Extract and then remove the package file.
$zip = new ZipArchive;
if ($zip->open($tmp_path) === true) {
if ($zip->open($tmpPath) === true) {
$zip->extractTo($tmpDir);
$zip->close();
}
else {
$this->h5pF->setErrorMessage($this->h5pF->t('The file you uploaded is not a valid HTML5 Package.'));
$this->h5pC->delTree($tmpDir);
H5PCore::deleteFileTree($tmpDir);
return;
}
unlink($tmp_path);
unlink($tmpPath);
// Process content and libraries
$libraries = array();
@ -382,6 +423,10 @@ class H5PValidator {
$filePath = $tmpDir . DIRECTORY_SEPARATOR . $file;
// Check for h5p.json file.
if (strtolower($file) == 'h5p.json') {
if ($skipContent === TRUE) {
continue;
}
$mainH5pData = $this->getJsonData($filePath);
if ($mainH5pData === FALSE) {
$valid = FALSE;
@ -404,6 +449,10 @@ class H5PValidator {
}
// Content directory holds content.
elseif ($file == 'content') {
// We do a separate skipContent check to avoid having the content folder being treated as a library
if ($skipContent) {
continue;
}
if (!is_dir($filePath)) {
$this->h5pF->setErrorMessage($this->h5pF->t('Invalid content folder'));
$valid = FALSE;
@ -421,6 +470,7 @@ class H5PValidator {
}
if (!$this->h5pCV->validateContentFiles($filePath)) {
// validateContentfiles prints error messages itself
$valid = FALSE;
continue;
}
@ -443,20 +493,25 @@ class H5PValidator {
}
}
}
if (!$contentExists) {
$this->h5pF->setErrorMessage($this->h5pF->t('A valid content folder is missing'));
$valid = FALSE;
}
if (!$mainH5pExists) {
$this->h5pF->setErrorMessage($this->h5pF->t('A valid main h5p.json file is missing'));
$valid = FALSE;
if ($skipContent === FALSE) {
if (!$contentExists) {
$this->h5pF->setErrorMessage($this->h5pF->t('A valid content folder is missing'));
$valid = FALSE;
}
if (!$mainH5pExists) {
$this->h5pF->setErrorMessage($this->h5pF->t('A valid main h5p.json file is missing'));
$valid = FALSE;
}
}
if ($valid) {
$this->h5pC->librariesJsonData = $libraries;
$this->h5pC->mainJsonData = $mainH5pData;
$this->h5pC->contentJsonData = $contentJsonData;
$libraries['mainH5pData'] = $mainH5pData; // Check for the dependencies in h5p.json as well as in the libraries
if ($skipContent === FALSE) {
$this->h5pC->mainJsonData = $mainH5pData;
$this->h5pC->contentJsonData = $contentJsonData;
$libraries['mainH5pData'] = $mainH5pData; // Check for the dependencies in h5p.json as well as in the libraries
}
$missingLibraries = $this->getMissingLibraries($libraries);
foreach ($missingLibraries as $missing) {
if ($this->h5pF->getLibraryId($missing['machineName'], $missing['majorVersion'], $missing['minorVersion'])) {
@ -465,7 +520,7 @@ class H5PValidator {
}
if (!empty($missingLibraries)) {
foreach ($missingLibraries as $library) {
$this->h5pF->setErrorMessage($this->h5pF->t('Missing required library @library', array('@library' => $this->h5pC->libraryToString($library))));
$this->h5pF->setErrorMessage($this->h5pF->t('Missing required library @library', array('@library' => H5PCore::libraryToString($library))));
}
if (!$this->h5pF->mayUpdateLibraries()) {
$this->h5pF->setInfoMessage($this->h5pF->t("Note that the libraries may exist in the file you uploaded, but you're not allowed to upload new libraries. Contact the site administrator about this."));
@ -474,7 +529,7 @@ class H5PValidator {
$valid = empty($missingLibraries) && $valid;
}
if (!$valid) {
$this->h5pC->delTree($tmpDir);
H5PCore::deleteFileTree($tmpDir);
}
return $valid;
}
@ -900,7 +955,7 @@ class H5PStorage {
* TRUE if one or more libraries were updated
* FALSE otherwise
*/
public function savePackage($contentId, $contentMainId = NULL) {
public function savePackage($contentId = NULL, $contentMainId = NULL, $skipContent = FALSE) {
// Save the libraries we processed during validation
$library_saved = FALSE;
$mayUpdateLibraries = $this->h5pF->mayUpdateLibraries();
@ -929,8 +984,8 @@ class H5PStorage {
$this->h5pF->saveLibraryData($library, $new);
$current_path = $this->h5pF->getUploadedH5pFolderPath() . DIRECTORY_SEPARATOR . $key;
$destination_path = $this->h5pF->getH5pPath() . DIRECTORY_SEPARATOR . 'libraries' . DIRECTORY_SEPARATOR . $this->h5pC->libraryToString($library, TRUE);
$this->h5pC->delTree($destination_path);
$destination_path = $this->h5pF->getH5pPath() . DIRECTORY_SEPARATOR . 'libraries' . DIRECTORY_SEPARATOR . H5PCore::libraryToString($library, TRUE);
H5PCore::deleteFileTree($destination_path);
rename($current_path, $destination_path);
$library_saved = TRUE;
@ -950,25 +1005,29 @@ class H5PStorage {
}
}
}
// Move the content folder
$current_path = $this->h5pF->getUploadedH5pFolderPath() . DIRECTORY_SEPARATOR . 'content';
$destination_path = $this->h5pF->getH5pPath() . DIRECTORY_SEPARATOR . 'content' . DIRECTORY_SEPARATOR . $contentId;
rename($current_path, $destination_path);
if (!$skipContent) {
// Move the content folder
$current_path = $this->h5pF->getUploadedH5pFolderPath() . DIRECTORY_SEPARATOR . 'content';
$destination_path = $this->h5pF->getH5pPath() . DIRECTORY_SEPARATOR . 'content' . DIRECTORY_SEPARATOR . $contentId;
rename($current_path, $destination_path);
// Save what libraries is beeing used by this package/content
$librariesInUse = array();
$this->getLibraryUsage($librariesInUse, $this->h5pC->mainJsonData);
$this->h5pF->saveLibraryUsage($contentId, $librariesInUse);
$this->h5pC->delTree($this->h5pF->getUploadedH5pFolderPath());
// Save what libraries is beeing used by this package/content
$librariesInUse = array();
$this->h5pC->findLibraryDependencies($librariesInUse, $this->h5pC->mainJsonData);
$this->h5pF->saveLibraryUsage($contentId, $librariesInUse);
H5PCore::deleteFileTree($this->h5pF->getUploadedH5pFolderPath());
// Save the data in content.json
$contentJson = file_get_contents($destination_path . DIRECTORY_SEPARATOR . 'content.json');
$mainLibraryId = $librariesInUse[$this->h5pC->mainJsonData['mainLibrary']]['library']['libraryId'];
$this->h5pF->saveContentData($contentId, $contentJson, $this->h5pC->mainJsonData, $mainLibraryId, $contentMainId);
// Save the data in content.json
$contentJson = file_get_contents($destination_path . DIRECTORY_SEPARATOR . 'content.json');
$mainLibraryId = $librariesInUse['preloaded-' . $this->h5pC->mainJsonData['mainLibrary']]['library']['libraryId'];
$this->h5pF->saveContentData($contentId, $contentJson, $this->h5pC->mainJsonData, $mainLibraryId, $contentMainId);
}
return $library_saved;
}
/**
* Delete an H5P package
*
@ -976,7 +1035,7 @@ class H5PStorage {
* The content id
*/
public function deletePackage($contentId) {
$this->h5pC->delTree($this->h5pF->getH5pPath() . DIRECTORY_SEPARATOR . 'content' . DIRECTORY_SEPARATOR . $contentId);
H5PCore::deleteFileTree($this->h5pF->getH5pPath() . DIRECTORY_SEPARATOR . 'content' . DIRECTORY_SEPARATOR . $contentId);
$this->h5pF->deleteContentData($contentId);
}
@ -1012,57 +1071,10 @@ class H5PStorage {
public function copyPackage($contentId, $copyFromId, $contentMainId = NULL) {
$source_path = $this->h5pF->getH5pPath() . DIRECTORY_SEPARATOR . 'content' . DIRECTORY_SEPARATOR . $copyFromId;
$destination_path = $this->h5pF->getH5pPath() . DIRECTORY_SEPARATOR . 'content' . DIRECTORY_SEPARATOR . $contentId;
$this->h5pC->copyTree($source_path, $destination_path);
$this->h5pC->copyFileTree($source_path, $destination_path);
$this->h5pF->copyLibraryUsage($contentId, $copyFromId, $contentMainId);
}
/**
* Identify what libraries are beeing used taking all dependencies into account
*
* @param array $librariesInUse
* List of libraries in use, indexed by machineName
* @param array $jsonData
* library.json og h5p.json data holding dependency information
* @param boolean $dynamic
* Whether or not the current library is a dynamic dependency
*/
public function getLibraryUsage(&$librariesInUse, $jsonData, $dynamic = FALSE, $editor = FALSE) {
if (isset($jsonData['preloadedDependencies'])) {
foreach ($jsonData['preloadedDependencies'] as $preloadedDependency) {
$library = $this->h5pF->loadLibrary($preloadedDependency['machineName'], $preloadedDependency['majorVersion'], $preloadedDependency['minorVersion']);
$librariesInUse[$preloadedDependency['machineName']] = array(
'library' => $library,
'preloaded' => $dynamic ? 0 : 1,
);
$this->getLibraryUsage($librariesInUse, $library, $dynamic, $editor);
}
}
if (isset($jsonData['dynamicDependencies'])) {
foreach ($jsonData['dynamicDependencies'] as $dynamicDependency) {
if (!isset($librariesInUse[$dynamicDependency['machineName']])) {
$library = $this->h5pF->loadLibrary($dynamicDependency['machineName'], $dynamicDependency['majorVersion'], $dynamicDependency['minorVersion']);
$librariesInUse[$dynamicDependency['machineName']] = array(
'library' => $library,
'preloaded' => 0,
);
}
$this->getLibraryUsage($librariesInUse, $library, TRUE, $editor);
}
}
if (isset($jsonData['editorDependencies']) && $editor) {
foreach ($jsonData['editorDependencies'] as $editorDependency) {
if (!isset($librariesInUse[$editorDependency['machineName']])) {
$library = $this->h5pF->loadLibrary($editorDependency['machineName'], $editorDependency['majorVersion'], $editorDependency['minorVersion']);
$librariesInUse[$editorDependency['machineName']] = array(
'library' => $library,
'preloaded' => $dynamic ? 0 : 1,
);
}
$this->getLibraryUsage($librariesInUse, $library, $dynamic, TRUE);
}
}
}
}
/**
@ -1109,7 +1121,7 @@ Class H5PExport {
@mkdir($tempPath);
$exportData = $this->h5pF->getExportData($contentId);
// Create content folder
if ($this->h5pC->copyTree($h5pDir . 'content' . DIRECTORY_SEPARATOR . $contentId, $tempPath . DIRECTORY_SEPARATOR . 'content') === FALSE) {
if ($this->h5pC->copyFileTree($h5pDir . 'content' . DIRECTORY_SEPARATOR . $contentId, $tempPath . DIRECTORY_SEPARATOR . 'content') === FALSE) {
return FALSE;
}
file_put_contents($tempPath . DIRECTORY_SEPARATOR . 'content' . DIRECTORY_SEPARATOR . 'content.json', $exportData['jsonContent']);
@ -1121,7 +1133,7 @@ Class H5PExport {
// Build h5p.json
$h5pJson = array (
'title' => $title,
'language' => $exportData['language'] ? $exportData['language'] : 'und',
'language' => $language ? $language : 'und',
'mainLibrary' => $exportData['mainLibrary'],
'embedTypes' => $embedTypes,
);
@ -1129,13 +1141,13 @@ Class H5PExport {
// Copies libraries to temp dir and create mention in h5p.json
foreach($exportData['libraries'] as $library) {
// Set preloaded and dynamic dependencies
if ($library['preloaded']) {
if ($library['dependencyType'] == 'preloaded') {
$preloadedDependencies[] = array(
'machineName' => $library['machineName'],
'majorVersion' => $library['majorVersion'],
'minorVersion' => $library['minorVersion'],
);
} else {
} elseif ($library['dependencyType'] == 'dynamic') {
$dynamicDependencies[] = array(
'machineName' => $library['machineName'],
'majorVersion' => $library['majorVersion'],
@ -1145,22 +1157,28 @@ Class H5PExport {
}
// Add preloaded and dynamic dependencies if they exist
if ($preloadedDependencies) { $h5pJson['preloadedDependencies'] = $preloadedDependencies; }
if ($dynamicDependencies) { $h5pJson['dynamicDependencies'] = $dynamicDependencies; }
if (isset($preloadedDependencies)) { $h5pJson['preloadedDependencies'] = $preloadedDependencies; }
if (isset($dynamicDependencies)) { $h5pJson['dynamicDependencies'] = $dynamicDependencies; }
// Save h5p.json
$results = print_r(json_encode($h5pJson), true);
file_put_contents($tempPath . DIRECTORY_SEPARATOR . 'h5p.json', $results);
// Add the editor libraries to the list of libraries
// TODO: Add support for dependencies or editor libraries
$exportData['libraries'] = $this->addEditorLibraries($exportData['libraries'], $exportData['editorLibraries']);
// Copies libraries to temp dir and create mention in h5p.json
foreach($exportData['libraries'] as $library) {
$source = $h5pDir . 'libraries' . DIRECTORY_SEPARATOR . $library['machineName'] . '-' . $library['majorVersion'] . '.' . $library['minorVersion'];
$source = NULL;
if ($this->h5pC->development_mode & H5PDevelopment::MODE_LIBRARY) {
$devlib = $this->h5pC->h5pD->getLibrary($library['machineName'], $library['majorVersion'], $library['minorVersion']);
if ($devlib !== NULL) {
$source = $devlib['path'];
}
}
if ($source === NULL) {
$source = $h5pDir . 'libraries' . DIRECTORY_SEPARATOR . $library['machineName'] . '-' . $library['majorVersion'] . '.' . $library['minorVersion'];
}
$destination = $tempPath . DIRECTORY_SEPARATOR . $library['machineName'];
$this->h5pC->copyTree($source, $destination);
$this->h5pC->copyFileTree($source, $destination);
}
// Create new zip instance.
@ -1182,7 +1200,7 @@ Class H5PExport {
}
// Close zip and remove temp dir
$zip->close();
$this->h5pC->delTree($tempPath);
H5PCore::deleteFileTree($tempPath);
}
return str_replace(DIRECTORY_SEPARATOR, '/', $zipPath);
@ -1198,7 +1216,7 @@ Class H5PExport {
$h5pDir = $this->h5pF->getH5pPath() . DIRECTORY_SEPARATOR;
$zipPath = $h5pDir . 'exports' . DIRECTORY_SEPARATOR . $contentId . '.h5p';
if (file_exists($zipPath)) {
file_delete($zipPath);
unlink($zipPath);
}
}
@ -1226,9 +1244,10 @@ Class H5PExport {
* Functions and storage shared by the other H5P classes
*/
class H5PCore {
public static $coreApi = array(
'majorVersion' => 1,
'minorVersion' => 0
'minorVersion' => 2
);
public static $styles = array(
'styles/h5p.css',
@ -1237,23 +1256,205 @@ class H5PCore {
'js/jquery.js',
'js/h5p.js',
);
public static $adminScripts = array(
'js/jquery.js',
'js/h5p-utils.js',
);
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';
public static $defaultLibraryWhitelistExtras = 'js css';
public $h5pF;
public $librariesJsonData;
public $contentJsonData;
public $mainJsonData;
public $librariesJsonData, $contentJsonData, $mainJsonData, $h5pF, $path, $development_mode, $h5pD;
/**
* Constructor for the H5PCore
*
* @param object $H5PFramework
* The frameworks implementation of the H5PFrameworkInterface
* @param string $path H5P file storage directory.
* @param string $language code. Defaults to english.
* @param int $development_mode mode.
*/
public function __construct($H5PFramework) {
public function __construct($H5PFramework, $path, $language = 'en', $development_mode = H5PDevelopment::MODE_NONE) {
$this->h5pF = $H5PFramework;
$this->h5pF = $H5PFramework;
$this->path = $path;
$this->development_mode = $development_mode;
if ($development_mode & H5PDevelopment::MODE_LIBRARY) {
$this->h5pD = new H5PDevelopment($this->h5pF, $path, $language);
}
}
/**
* Load content.
*
* @param int $id for content.
* @return object
*/
public function loadContent($id) {
$content = $this->h5pF->loadContent($id);
if ($content !== NULL) {
$content['library'] = array(
'id' => $content['libraryId'],
'name' => $content['libraryName'],
'majorVersion' => $content['libraryMajorVersion'],
'minorVersion' => $content['libraryMinorVersion'],
'embedTypes' => $content['libraryEmbedTypes'],
'fullscreen' => $content['libraryFullscreen'],
);
unset($content['libraryId'], $content['libraryName'], $content['libraryEmbedTypes'], $content['libraryFullscreen']);
if ($this->development_mode & H5PDevelopment::MODE_CONTENT) {
// TODO: Remove Drupal specific stuff
$json_content_path = file_create_path(file_directory_path() . '/' . variable_get('h5p_default_path', 'h5p') . '/content/' . $id . '/content.json');
if (file_exists($json_content_path) === TRUE) {
$json_content = file_get_contents($json_content_path);
if (json_decode($json_content, TRUE) !== FALSE) {
drupal_set_message(t('Invalid json in json content'), 'warning');
}
$content['params'] = $json_content;
}
}
}
return $content;
}
/**
* Find the files required for this content to work.
*
* @param int $id for content.
* @return array
*/
public function loadContentDependencies($id, $type = NULL) {
$dependencies = $this->h5pF->loadContentDependencies($id, $type);
if ($this->development_mode & H5PDevelopment::MODE_LIBRARY) {
$developmentLibraries = $this->h5pD->getLibraries();
foreach ($dependencies as $key => $dependency) {
$libraryString = H5PCore::libraryToString($dependency);
if (isset($developmentLibraries[$libraryString])) {
$dependencies[$key] = $developmentLibraries[$libraryString];
}
}
}
return $this->getDependenciesFiles($dependencies);
}
/**
* Return file paths for all dependecies files.
*
* @param array $dependencies
* @return array files.
*/
public function getDependenciesFiles($dependencies) {
$files = array(
'scripts' => array(),
'styles' => array(),
);
foreach ($dependencies as $dependency) {
if (isset($dependency['path']) === FALSE) {
$dependency['path'] = $this->path . '/libraries/' . H5PCore::libraryToString($dependency, TRUE);
$dependency['preloadedJs'] = explode(',', $dependency['preloadedJs']);
$dependency['preloadedCss'] = explode(',', $dependency['preloadedCss']);
}
if (!empty($dependency['preloadedJs']) && $dependency['preloadedJs'][0] !== '') {
foreach ($dependency['preloadedJs'] as $file) {
$files['scripts'][] = $dependency['path'] . '/' . trim(is_array($file) ? $file['path'] : $file);
}
}
if ($dependency['dropCss'] !== '1' && !empty($dependency['preloadedCss']) && $dependency['preloadedCss'][0] !== '') {
foreach ($dependency['preloadedCss'] as $file) {
$files['styles'][] = $dependency['path'] . '/' . trim(is_array($file) ? $file['path'] : $file);
}
}
}
return $files;
}
/**
* Load library semantics.
*
* @return string 'div' or 'iframe'.
*/
public function loadLibrarySemantics($name, $majorVersion, $minorVersion) {
if ($this->development_mode & H5PDevelopment::MODE_LIBRARY) {
// Try to load from dev lib
$semantics = $this->h5pD->getSemantics($name, $majorVersion, $minorVersion);
}
if ($semantics === NULL) {
// Try to load from DB.
$semantics = $this->h5pF->loadLibrarySemantics($name, $majorVersion, $minorVersion);
}
if ($semantics !== NULL) {
$semantics = json_decode($semantics);
$this->h5pF->alterLibrarySemantics($semantics, $name, $majorVersion, $minorVersion);
}
return $semantics;
}
/**
* Load library.
*
* @return array or null.
*/
public function loadLibrary($name, $majorVersion, $minorVersion) {
if ($this->development_mode & H5PDevelopment::MODE_LIBRARY) {
// Try to load from dev
$library = $this->h5pD->getLibrary($name, $majorVersion, $minorVersion);
}
if ($library === NULL) {
// Try to load from DB.
$library = $this->h5pF->loadLibrary($name, $majorVersion, $minorVersion);
}
return $library;
}
/**
* Recusive. Goes through the dependency tree for the given library and
* adds all the dependencies to the given array in a flat format.
*
* @param array $librariesUsed Flat list of all dependencies.
* @param array $library To find all dependencies for.
* @param bool $editor Used interally to force all preloaded sub dependencies of an editor dependecy to be editor dependencies.
*/
public function findLibraryDependencies(&$dependencies, $library, $editor = FALSE) {
foreach (array('dynamic', 'preloaded', 'editor') as $type) {
$property = $type . 'Dependencies';
if ($library[$property] === NULL) {
continue; // Skip, no such dependencies.
}
if ($type === 'preloaded' && $editor === TRUE) {
// All preloaded dependencies of an editor library is set to editor.
$type = 'editor';
}
foreach ($library[$property] as $dependency) {
$dependencyKey = $type . '-' . $dependency['machineName'];
if (isset($dependencies[$dependencyKey]) === TRUE) {
continue; // Skip, already have this.
}
$dependencyLibrary = $this->loadLibrary($dependency['machineName'], $dependency['majorVersion'], $dependency['minorVersion']);
$dependencies[$dependencyKey] = array(
'library' => $dependencyLibrary,
'type' => $type
);
$this->findLibraryDependencies($dependencies, $dependencyLibrary, $type === 'editor');
}
}
}
/**
@ -1287,13 +1488,13 @@ class H5PCore {
* @return boolean
* Indicates if the directory existed.
*/
public function delTree($dir) {
public static function deleteFileTree($dir) {
if (!is_dir($dir)) {
return;
}
$files = array_diff(scandir($dir), array('.','..'));
foreach ($files as $file) {
(is_dir("$dir/$file")) ? $this->delTree("$dir/$file") : unlink("$dir/$file");
(is_dir("$dir/$file")) ? self::deleteFileTree("$dir/$file") : unlink("$dir/$file");
}
return rmdir($dir);
}
@ -1306,7 +1507,7 @@ class H5PCore {
* @return boolean
* Indicates if the directory existed.
*/
public function copyTree($source, $destination) {
public function copyFileTree($source, $destination) {
$dir = opendir($source);
if ($dir === FALSE) {
@ -1318,7 +1519,7 @@ class H5PCore {
while (false !== ($file = readdir($dir))) {
if (($file != '.') && ($file != '..')) {
if (is_dir($source . DIRECTORY_SEPARATOR . $file)) {
$this->copyTree($source . DIRECTORY_SEPARATOR . $file, $destination . DIRECTORY_SEPARATOR . $file);
$this->copyFileTree($source . DIRECTORY_SEPARATOR . $file, $destination . DIRECTORY_SEPARATOR . $file);
}
else {
copy($source . DIRECTORY_SEPARATOR . $file,$destination . DIRECTORY_SEPARATOR . $file);
@ -1338,8 +1539,8 @@ class H5PCore {
* @return string
* On the form {machineName} {majorVersion}.{minorVersion}
*/
public function libraryToString($library, $folderName = FALSE) {
return $library['machineName'] . ($folderName ? '-' : ' ') . $library['majorVersion'] . '.' . $library['minorVersion'];
public static function libraryToString($library, $folderName = FALSE) {
return (isset($library['machineName']) ? $library['machineName'] : $library['name']) . ($folderName ? '-' : ' ') . $library['majorVersion'] . '.' . $library['minorVersion'];
}
/**
@ -1365,6 +1566,28 @@ class H5PCore {
}
return FALSE;
}
/**
* Determine the correct embed type to use.
* TODO: Use constants.
*
* @return string 'div' or 'iframe'.
*/
public static function determineEmbedType($contentEmbedType, $libraryEmbedTypes) {
// Detect content embed type
$embedType = strpos(strtolower($contentEmbedType), 'div') !== FALSE ? 'div' : 'iframe';
if ($libraryEmbedTypes !== NULL && $libraryEmbedTypes !== '') {
// Check that embed type is available for library
$embedTypes = strtolower($libraryEmbedTypes);
if (strpos($embedTypes, $embedType) === FALSE) {
// Not available, pick default.
$embedType = strpos($embedTypes, 'div') !== FALSE ? 'div' : 'iframe';
}
}
return $embedType;
}
}
/**
@ -1469,14 +1692,14 @@ class H5PContentValidator {
}
// Check if string is according to optional regexp in semantics
if (isset($semantics->regexp)) {
if (!($text === '' && $semantics->optional) && isset($semantics->regexp)) {
// Escaping '/' found in patterns, so that it does not break regexp fencing.
$pattern = '/' . str_replace('/', '\\/', $semantics->regexp->pattern) . '/';
$pattern .= isset($semantics->regexp->modifiers) ? $semantics->regexp->modifiers : '';
if (preg_match($pattern, $text) === 0) {
// Note: explicitly ignore return value FALSE, to avoid removing text
// if regexp is invalid...
$this->h5pF->setErrorMessage($this->h5pF->t('Provided string is not valid according to regexp in semantics.'));
$this->h5pF->setErrorMessage($this->h5pF->t('Provided string is not valid according to regexp in semantics. (value: "%value", regexp: "%regexp")', array('%value' => $text, '%regexp' => $pattern)));
$text = '';
}
}
@ -1646,7 +1869,7 @@ class H5PContentValidator {
// Remove attributes that should not exist, they may contain JSON escape
// code.
$validkeys = array_merge(array('path', 'mime'), $typevalidkeys);
$validkeys = array_merge(array('path', 'mime', 'copyright'), $typevalidkeys);
if (isset($semantics->extraAttributes)) {
$validkeys = array_merge($validkeys, $semantics->extraAttributes); // TODO: Validate extraAttributes
}
@ -1674,6 +1897,10 @@ class H5PContentValidator {
$file->quality->label = htmlspecialchars($file->quality->label, ENT_QUOTES, 'UTF-8', FALSE);
}
}
if (isset($file->copyright)) {
$this->validateGroup($file->copyright, H5PContentValidator::getCopyrightSemantics());
}
}
/**
@ -1774,7 +2001,7 @@ class H5PContentValidator {
}
else {
$libspec = $this->h5pC->libraryFromString($value->library);
$librarySemantics = $this->h5pF->getLibrarySemantics($libspec['machineName'], $libspec['majorVersion'], $libspec['minorVersion']);
$librarySemantics = $this->h5pC->loadLibrarySemantics($libspec['machineName'], $libspec['majorVersion'], $libspec['minorVersion']);
$this->semanticsCache[$value->library] = $librarySemantics;
}
$this->validateBySemantics($value->params, $librarySemantics);
@ -2047,9 +2274,11 @@ class H5PContentValidator {
return $attrarr;
}
// TODO: Remove Drupal related stuff in docs.
/**
* Processes an HTML attribute value and strips dangerous protocols from URLs.
*
*
* @param $string
* The string with the attribute value.
* @param $decode
@ -2122,6 +2351,108 @@ class H5PContentValidator {
return $uri;
}
public static function getCopyrightSemantics() {
static $semantics;
if ($semantics === NULL) {
$semantics = (object) array(
'name' => 'copyright',
'type' => 'group',
'label' => 'Copyright information',
'fields' => array(
(object) array(
'name' => 'title',
'type' => 'text',
'label' => 'Title',
'placeholder' => 'La Gioconda',
'optional' => TRUE
),
(object) array(
'name' => 'author',
'type' => 'text',
'label' => 'Author',
'placeholder' => 'Leonardo da Vinci',
'optional' => TRUE
),
(object) array(
'name' => 'year',
'type' => 'text',
'label' => 'Year(s)',
'placeholder' => '1503 - 1517',
'optional' => TRUE
),
(object) array(
'name' => 'source',
'type' => 'text',
'label' => 'Source',
'placeholder' => 'http://en.wikipedia.org/wiki/Mona_Lisa',
'optional' => true,
'regexp' => (object) array(
'pattern' => '^http[s]?://.+',
'modifiers' => 'i'
)
),
(object) array(
'name' => 'license',
'type' => 'select',
'label' => 'License',
'default' => 'U',
'options' => array(
(object) array(
'value' => 'U',
'label' => 'Undisclosed'
),
(object) array(
'value' => 'CC BY',
'label' => 'Attribution'
),
(object) array(
'value' => 'CC BY-SA',
'label' => 'Attribution-ShareAlike'
),
(object) array(
'value' => 'CC BY-ND',
'label' => 'Attribution-NoDerivs'
),
(object) array(
'value' => 'CC BY-NC',
'label' => 'Attribution-NonCommercial'
),
(object) array(
'value' => 'CC BY-NC-SA',
'label' => 'Attribution-NonCommercial-ShareAlike'
),
(object) array(
'value' => 'CC BY-NC-ND',
'label' => 'Attribution-NonCommercial-NoDerivs'
),
(object) array(
'value' => 'GNU GPL',
'label' => 'General Public License'
),
(object) array(
'value' => 'PD',
'label' => 'Public Domain'
),
(object) array(
'value' => 'ODC PDDL',
'label' => 'Public Domain Dedication and Licence'
),
(object) array(
'value' => 'CC PDM',
'label' => 'Public Domain Mark'
),
(object) array(
'value' => 'C',
'label' => 'Copyright'
)
)
)
)
);
}
return $semantics;
}
}
?>

230
js/h5p-embed.js Normal file
View File

@ -0,0 +1,230 @@
/**
*
*/
var H5P = H5P || (function () {
var head = document.getElementsByTagName('head')[0];
var contentId = 0;
var contents = {};
/**
* Wraps multiple content between a prefix and a suffix.
*/
var wrap = function (prefix, content, suffix) {
var result = '';
for (var i = 0; i < content.length; i++) {
result += prefix + content[i] + suffix;
}
return result;
}
/**
*
*/
var loadContent = function (id, script) {
var url = script.getAttribute('data-h5p');
var data, callback = 'H5P' + id;
// Prevent duplicate loading.
script.removeAttribute('data-h5p')
// Callback for when content data is loaded.
window[callback] = function (content) {
contents[id] = content;
var iframe = document.createElement('iframe');
var parent = script.parentNode;
parent.insertBefore(iframe, script);
iframe.id = 'h5p-iframe-' + id;
iframe.style.display = 'block';
iframe.style.width = '100%';
iframe.style.height = '1px';
iframe.style.border = 'none';
iframe.style.zIndex = 101;
iframe.style.top = 0;
iframe.style.left = 0;
iframe.className = 'h5p-iframe';
iframe.setAttribute('frameBorder', '0');
iframe.contentDocument.open();
iframe.contentDocument.write('\
<!doctype html><html class="h5p-iframe">\
<head>\
<script>\
var H5PIntegration = window.parent.H5P.getIntegration(' + id + ');\
</script>\
' + wrap('<link rel="stylesheet" href="', content.styles, '">') + '\
' + wrap('<script src="', content.scripts, '"></script>') + '\
</head><body>\
<div class="h5p-content" data-class="' + content.library + '" data-content-id="' + id + '"/>\
</body></html>');
iframe.contentDocument.close();
iframe.contentDocument.documentElement.style.overflow = 'hidden';
// Clean up
parent.removeChild(script);
head.removeChild(data);
delete window[callback];
}
// Create data script
data = document.createElement('script');
data.src = url + (url.indexOf('?') === -1 ? '?' : '&') + 'callback=' + callback;
head.appendChild(data);
}
/**
* Go throught all script tags with the data-h5p attribute and load content.
*/
function H5P() {
var scripts = document.getElementsByTagName('script');
var h5ps = []; // Use seperate array since scripts grow in size.
for (var i = 0; i < scripts.length; i++) {
var script = scripts[i];
if (script.hasAttribute('data-h5p')) {
h5ps.push(script);
}
}
for (i = 0; i < h5ps.length; i++) {
loadContent(contentId, h5ps[i]);
contentId++;
}
};
/**
* Return integration object
*/
H5P.getIntegration = function (id) {
var content = contents[id];
return {
getJsonContent: function () {
return content.params;
},
getContentPath: function () {
return content.path + 'content/' + content.id + '/';
},
getFullscreen: function () {
return content.fullscreen;
},
getLibraryPath: function (library) {
return content.path + 'libraries/' + library;
},
getContentData: function () {
return {
library: content.library,
jsonContent: content.params,
fullScreen: content.fullscreen,
export: content.export,
embedCode: content.embedCode
};
},
i18n: content.i18n
};
};
// Detect if we support fullscreen, and what prefix to use.
var fullScreenBrowserPrefix;
if (document.documentElement.requestFullScreen) {
fullScreenBrowserPrefix = '';
}
else if (document.documentElement.webkitRequestFullScreen
&& navigator.userAgent.indexOf('Android') === -1 // Skip Android
&& navigator.userAgent.indexOf('Version/') === -1 // Skip Safari
) {
fullScreenBrowserPrefix = 'webkit';
}
else if (document.documentElement.mozRequestFullScreen) {
fullScreenBrowserPrefix = 'moz';
}
else if (document.documentElement.msRequestFullscreen) {
fullScreenBrowserPrefix = 'ms';
}
/**
* Enter fullscreen mode.
*/
H5P.fullScreen = function ($element, instance, exitCallback, body) {
var iframe = document.getElementById('h5p-iframe-' + $element.parent().data('content-id'));
var $classes = $element.add(body);
var $body = $classes.eq(1);
var done = function (c) {
$classes.removeClass(c);
if (H5P.fullScreenBrowserPrefix === undefined) {
// Resize content.
if (instance.$ !== undefined) {
instance.$.trigger('resize');
}
}
if (exitCallback !== undefined) {
exitCallback();
}
};
if (fullScreenBrowserPrefix === undefined) {
// Create semi fullscreen.
$classes.addClass('h5p-semi-fullscreen');
iframe.style.position = 'fixed';
var $disable = $element.prepend('<a href="#" class="h5p-disable-fullscreen">Disable fullscreen</a>').children(':first');
var keyup, disableSemiFullscreen = function () {
$disable.remove();
$body.unbind('keyup', keyup);
iframe.style.position = 'static';
done('h5p-semi-fullscreen');
return false;
};
keyup = function (event) {
if (event.keyCode === 27) {
disableSemiFullscreen();
}
};
$disable.click(disableSemiFullscreen);
$body.keyup(keyup); // TODO: Does not work with iframe's $!
}
else {
// Create real fullscreen.
var first, eventName = (fullScreenBrowserPrefix === 'ms' ? 'MSFullscreenChange' : fullScreenBrowserPrefix + 'fullscreenchange');
document.addEventListener(eventName, function () {
if (first === undefined) {
first = false;
return;
}
done('h5p-fullscreen');
document.removeEventListener(eventName, arguments.callee, false);
});
if (fullScreenBrowserPrefix === '') {
iframe.requestFullScreen();
}
else {
var method = (fullScreenBrowserPrefix === 'ms' ? 'msRequestFullscreen' : fullScreenBrowserPrefix + 'RequestFullScreen');
var params = (fullScreenBrowserPrefix === 'webkit' ? Element.ALLOW_KEYBOARD_INPUT : undefined);
iframe[method](params);
}
$classes.addClass('h5p-fullscreen');
}
iframe.style.height = '100%';
if (H5P.fullScreenBrowserPrefix === undefined) {
// Resize content.
if (instance.$ !== undefined) {
instance.$.trigger('resize', [true]);
}
}
// Allow H5P to set focus when entering fullscreen mode
if (instance.focus !== undefined) {
instance.focus();
}
};
return H5P;
})();
new H5P();

300
js/h5p-library-details.js Normal file
View File

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

73
js/h5p-library-list.js Normal file
View File

@ -0,0 +1,73 @@
var H5PLibraryList= H5PLibraryList || {};
(function ($) {
/**
* Initializing
*/
H5PLibraryList.init = function () {
var $adminContainer = H5PIntegration.getAdminContainer();
// Create library list
$adminContainer.append(H5PLibraryList.createLibraryList(H5PIntegration.getLibraryList()));
};
/**
* Create the library list
*
* @param {object} libraries List of libraries and headers
*/
H5PLibraryList.createLibraryList = function (libraries) {
if(libraries.listData === undefined || libraries.listData.length === 0) {
return;
}
// Create table
var $table = H5PUtils.createTable(libraries.listHeaders);
$table.addClass('libraries');
// Add libraries
$.each (libraries.listData, function (index, library) {
var $libraryRow = H5PUtils.createTableRow([
library.name,
library.machineName,
library.contentCount,
library.libraryDependencyCount,
'<button class="h5p-admin-view-library">&#xf002;</button>' +
'<button class="h5p-admin-delete-library">&#xf057;</button>'
]);
// Open details view when clicked
$('.h5p-admin-view-library', $libraryRow).on('click', function () {
window.location.href = library.detailsUrl;
});
var $deleteButton = $('.h5p-admin-delete-library', $libraryRow);
if (library.contentCount !== 0 || library.libraryDependencyCount !== 0) {
// Disabled delete if content.
$deleteButton.attr('disabled', true); //.addClass('disabled');
}
else {
// Go to delete page om click.
$deleteButton.on('click', function () {
window.location.href = library.deleteUrl;
});
}
$table.append($libraryRow);
});
return $table;
};
// Initialize me:
$(document).ready(function () {
if (!H5PLibraryList.initialized) {
H5PLibraryList.initialized = true;
H5PLibraryList.init();
}
});
})(H5P.jQuery);

69
js/h5p-utils.js Normal file
View File

@ -0,0 +1,69 @@
var H5PUtils = H5PUtils || {};
(function ($) {
/**
* Generic function for creating a table including the headers
*
* @param {array} headers List of headers
*/
H5PUtils.createTable = function (headers) {
var $table = $('<table class="h5p-admin-table"></table>');
if(headers) {
var $thead = $('<thead></thead>');
var $tr = $('<tr></tr>');
$.each(headers, function (index, value) {
$tr.append('<th>' + value + '</th>');
});
$table.append($thead.append($tr))
}
return $table;
};
/**
* Generic function for creating a table row
*
* @param {array} rows Value list. Object name is used as class name in <TD>
*/
H5PUtils.createTableRow = function (rows) {
var $tr = $('<tr></tr>');
$.each(rows, function (index, value) {
$tr.append('<td>' + value + '</td>');
});
return $tr;
};
/**
* Generic function for creating a field containing label and value
*
* @param {string} label The label displayed in front of the value
* @param {string} value The value
*/
H5PUtils.createLabeledField = function (label, value) {
var $field = $('<div class="h5p-labeled-field"></div>');
$field.append('<div class="h5p-label">' + label + '</div>');
$field.append('<div class="h5p-value">' + value + '</div>');
return $field;
}
/**
* Replaces placeholder fields in translation strings
*
* @param {string} template The translation template string in the following format: "$name is a $sex"
* @param {array} replacors An js object with key and values. Eg: {'$name': 'Frode', '$sex': 'male'}
*/
H5PUtils.translateReplace = function (template, replacors) {
$.each(replacors, function (key, value) {
template = template.replace(new RegExp('\\'+key, 'g'), value)
});
return template;
}
})(H5P.jQuery);

845
js/h5p.js
View File

@ -1,175 +1,217 @@
var H5P = H5P || {};
// This needs to be determined before init is run.
H5P.isFramed = (window.self !== window.top); // (window.parent !== window);
// Determine if we're inside an iframe.
H5P.isFramed = (window.self !== window.top);
// Useful jQuery object.
H5P.$window = H5P.jQuery(window);
// Detect if we support fullscreen, and what prefix to use.
if (document.documentElement.requestFullScreen) {
H5P.fullScreenBrowserPrefix = '';
}
else if (document.documentElement.webkitRequestFullScreen
&& navigator.userAgent.indexOf('Android') === -1 // Skip Android
&& navigator.userAgent.indexOf('Version/') === -1 // Skip Safari
) {
H5P.fullScreenBrowserPrefix = 'webkit';
}
else if (document.documentElement.mozRequestFullScreen) {
H5P.fullScreenBrowserPrefix = 'moz';
}
else if (document.documentElement.msRequestFullscreen) {
H5P.fullScreenBrowserPrefix = 'ms';
}
/**
* Initialize H5P content.
* Scans for ".h5p-content" in the document and initializes H5P instances where found.
*/
H5P.init = function () {
if (H5P.$window === undefined) {
H5P.$window = H5P.jQuery(window);
}
if (H5P.$body === undefined) {
H5P.$body = H5P.jQuery('body');
}
// Useful jQuery object.
H5P.$body = H5P.jQuery('body');
// Is this H5P being run in a frame?
if (H5P.isFramed) {
H5P.$body.addClass('h5p-iframe-content');
}
if (H5P.fullScreenBrowserPrefix === undefined) {
if (document.documentElement.requestFullScreen) {
H5P.fullScreenBrowserPrefix = '';
}
else if (document.documentElement.webkitRequestFullScreen
&& navigator.userAgent.indexOf('Android') === -1 // Skip Android
&& navigator.userAgent.indexOf('Version/') === -1 // Skip Safari
) {
H5P.fullScreenBrowserPrefix = 'webkit';
}
else if (document.documentElement.mozRequestFullScreen) {
H5P.fullScreenBrowserPrefix = 'moz';
}
else if (document.documentElement.msRequestFullscreen) {
H5P.fullScreenBrowserPrefix = 'ms';
}
}
// Prepare internal resizer for content.
var $window = H5P.jQuery(window.top);
// H5Ps added in normal DIV.
H5P.jQuery(".h5p-content").each(function (idx, el) {
var $el = H5P.jQuery(el),
contentId = $el.data('content-id'),
mainLibrary = $el.data('class'),
obj = new (H5P.classFromName(mainLibrary))(H5P.jQuery.parseJSON(H5PIntegration.getJsonContent(contentId)), contentId);
var $containers = H5P.jQuery(".h5p-content").each(function () {
var $element = H5P.jQuery(this);
var $container = H5P.jQuery('<div class="h5p-container"></div>').appendTo($element);
var contentId = $element.data('content-id');
var contentData = H5PIntegration.getContentData(contentId);
var library = {
library: contentData.library,
params: H5P.jQuery.parseJSON(contentData.jsonContent)
};
// Render H5P in container.
obj.attach($el);
// Add Fullscreen button if relevant.
if (H5PIntegration.getFullscreen(contentId)) {
H5P.jQuery('<div class="h5p-content-controls"><div role="button" class="h5p-enable-fullscreen">' + H5PIntegration.fullscreenText + '</div></div>').insertBefore($el).children().click(function () {
H5P.fullScreen($el, obj);
return false;
// Create new instance.
var instance = H5P.newRunnable(library, contentId);
instance.attach($container); // Not sent to newRunnable to avoid resize.
// Check if we should add and display a fullscreen button for this H5P.
if (contentData.fullScreen == 1) {
H5P.jQuery('<div class="h5p-content-controls"><div role="button" tabindex="1" class="h5p-enable-fullscreen">' + H5P.t('fullscreen') + '</div></div>').insertBefore($container).children().click(function () {
H5P.fullScreen($container, instance);
});
};
var $actions = H5P.jQuery('<ul class="h5p-actions"></ul>');
if (contentData.export !== '') {
// Display export button
H5P.jQuery('<li class="h5p-button h5p-export" role="button" tabindex="1" title="' + H5P.t('downloadDescription') + '">' + H5P.t('download') + '</li>').appendTo($actions).click(function () {
window.location.href = contentData.export;
});
}
if (instance.getCopyrights !== undefined) {
// 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.openCopyrightsDialog($actions, instance);
});
}
if (contentData.embedCode !== undefined) {
// 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.openEmbedDialog($actions, contentData.embedCode);
});
}
H5P.jQuery('<li><a class="h5p-link" href="http://h5p.org" target="_blank" title="' + H5P.t('h5pDescription') + '"></a></li>').appendTo($actions);
$actions.insertAfter($container);
if (H5P.isFramed) {
// Make it possible to resize the iframe when the content changes size. This way we get no scrollbars.
var iframe = window.parent.document.getElementById('h5p-iframe-' + contentId);
var resizeIframe = function () {
// Use timeout to make sure the iframe is resized
setTimeout(function () {
var fullscreen = $container.hasClass('h5p-fullscreen') || $container.hasClass('h5p-semi-fullscreen');
if (!fullscreen) {
// Retain parent size to avoid jumping/scrolling
var parentHeight = iframe.parentElement.style.height;
iframe.parentElement.style.height = iframe.parentElement.clientHeight + 'px';
// Reset iframe height, incase content has shrinked.
iframe.style.height = '1px';
// Resize iframe so all content is visible.
iframe.style.height = (iframe.contentDocument.body.scrollHeight) + 'px';
// Free parent
iframe.parentElement.style.height = parentHeight;
}
}, 1);
};
if (instance.$ !== undefined) {
instance.$.on('resize', resizeIframe);
}
}
var resize = function () {
if (instance.$ !== undefined) {
// Resize content.
instance.$.trigger('resize');
}
};
resize();
// Resize everything when window is resized.
$window.resize(resize);
});
// H5Ps living in iframes. Note: Fullscreen button will be added
// inside iFrame if relevant
var $h5pIframes = H5P.jQuery(".h5p-iframe");
if ($h5pIframes.length !== 0) {
$h5pIframes.each(function (idx, iframe) {
var $iframe = H5P.jQuery(iframe),
contentId = $iframe.data('content-id'),
mainLibrary = $iframe.data('class');
// Insert H5Ps that should be in iframes.
H5P.jQuery("iframe.h5p-iframe").each(function () {
var $iframe = H5P.jQuery(this);
var contentId = $iframe.data('content-id');
// DEPRECATED AND WILL BE REMOVED. MAKE SURE YOUR H5Ps EXPOSES A $ AND A resize FUNCTION.
$iframe.ready(function () {
resizeIframeInterval = setInterval(function () {
if (H5P.isFullscreen) {
return;
}
$iframe.ready(function () {
// This is a bit hacky but necessary until libraries runs callbacks or similar when "done" or resizing or something.
resizeIframeInterval = setInterval(function () {
var $doc = $iframe.contents();
var contentHeight = $doc.height();
var frameHeight = $iframe.innerHeight();
if (frameHeight !== contentHeight) {
H5P.resizeIframe(contentId, contentHeight);
$doc[0].documentElement.style.margin = '0 0 1px 0';
}
else {
// Small trick to make scrollbars go away in ie.
$doc[0].documentElement.style.margin = '0 0 0 0';
}
}, 300);
});
var $doc = $iframe.contents();
var contentHeight = $doc.height();
var frameHeight = $iframe.innerHeight();
iframe.contentDocument.open();
iframe.contentDocument.write('<!doctype html><html><head>' + H5PIntegration.getHeadTags(contentId) + '</head><body><div class="h5p-content" data-class="' + mainLibrary + '" data-content-id="' + contentId + '"/></body></html>');
iframe.contentDocument.close();
if (frameHeight !== contentHeight) {
$iframe.css('height', contentHeight + 'px');
$doc[0].documentElement.style.overflow = 'hidden';
}
}, 500);
});
}
};
// END DEPRECATION
/**
* Fullscreen iframe container
*
* @param {string} contentId Content id of H5P in iframe
* @param {object} obj H5P object
* @param {function} exitCallback Callback function called when user exits fullscreen.
* @returns {undefined}
*/
H5P.fullScreenIframe = function (contentId, obj, exitCallback, $body) {
H5P.fullScreen(H5P.jQuery('#h5p-iframe-' + contentId + '-wrapper'), obj, exitCallback, $body);
};
/**
* Resize iframe height.
*
* @param {string} contentId Content id of H5P in iframe
* @param {integer} height New height in pixels.
* @returns {undefined}
*/
H5P.resizeIframe = function (contentId, height) {
var iframe = document.getElementById('h5p-iframe-' + contentId);
iframe.style.height = (H5P.isFullscreen) ? '100%' : height + 'px';
this.contentDocument.open();
this.contentDocument.write('<!doctype html><html class="h5p-iframe"><head>' + H5PIntegration.getHeadTags(contentId) + '</head><body><div class="h5p-content" data-content-id="' + contentId + '"/></body></html>');
this.contentDocument.close();
});
};
/**
* Enable full screen for the given h5p.
*
* @param {jQuery} $el Container
* @param {object} obj H5P
* @param {jQuery} $element Content container.
* @param {object} instance
* @param {function} exitCallback Callback function called when user exits fullscreen.
* @param {jQuery} $body For internal use. Gives the body of the iframe.
* @returns {undefined}
*/
H5P.fullScreen = function ($el, obj, exitCallback, $body) {
if ($body === undefined) {
$body = H5P.$body;
}
H5P.fullScreen = function ($element, instance, exitCallback, body) {
if (H5P.isFramed) {
var $classes = H5P.jQuery('html').add(H5P.$body).add($el);
$classes.addClass('h5p-fullscreen');
window.parent.H5P.fullScreenIframe($el.data('content-id'), obj, function () {
$classes.removeClass('h5p-fullscreen');
}, $body);
// Trigger resize on wrapper in parent window.
window.parent.H5P.fullScreen($element, instance, exitCallback, H5P.$body.get());
return;
}
var $container = $element;
var $classes, $iframe;
if (body === undefined) {
$body = H5P.$body;
}
else {
// We're called from an iframe.
$body = H5P.jQuery(body);
$classes = $body.add($element.get());
var iframeSelector = '#h5p-iframe-' + $element.parent().data('content-id');
$iframe = H5P.jQuery(iframeSelector);
$element = $iframe.parent(); // Put iframe wrapper in fullscreen, not container.
}
$classes = $element.add(H5P.$body).add($classes);
var done = function (c) {
H5P.isFullscreen = false;
$classes.removeClass(c);
if (H5P.fullScreenBrowserPrefix === undefined) {
// Resize content.
if (instance.$ !== undefined) {
instance.$.trigger('resize');
}
else if (instance.resize !== undefined) {
instance.resize();
}
}
if (exitCallback !== undefined) {
exitCallback();
}
};
if (H5P.fullScreenBrowserPrefix === undefined) {
// Create semi fullscreen.
$el.add(H5P.$body).addClass('h5p-semi-fullscreen');
// Move H5P content to top of body to make sure it is above other page
// content. Insert placeholder in original position to be able to move it
// back.
// THIS DOES NOT WORK WITH IFRAMED CONTENT, iframe will reload/fail.
// $el.after('<div id="h5pfullscreenreplacementplaceholder"></div>').prependTo(H5P.$body);
$classes.addClass('h5p-semi-fullscreen');
H5P.isFullscreen = true;
var $disable = H5P.jQuery('<a href="#" class="h5p-disable-fullscreen">Disable fullscreen</a>').appendTo($el);
var $disable = $container.prepend('<a href="#" class="h5p-disable-fullscreen">Disable fullscreen</a>').children(':first');
var keyup, disableSemiFullscreen = function () {
$el.add(H5P.$body).removeClass('h5p-semi-fullscreen');
// H5P.jQuery('#h5pfullscreenreplacementplaceholder').before($el).remove();
$disable.remove();
H5P.isFullscreen = false;
$disable.remove();
$body.unbind('keyup', keyup);
H5P.jQuery(".h5p-iframe").each(function (idx, el) {
H5P.resizeIframe(H5P.jQuery(el).data('content-id'), 0);
});
if (exitCallback) {
exitCallback();
}
if (obj.resize !== undefined) {
obj.resize(false);
}
done('h5p-semi-fullscreen');
return false;
};
keyup = function (event) {
@ -181,6 +223,8 @@ H5P.fullScreen = function ($el, obj, exitCallback, $body) {
$body.keyup(keyup);
}
else {
// Create real fullscreen.
var first, eventName = (H5P.fullScreenBrowserPrefix === 'ms' ? 'MSFullscreenChange' : H5P.fullScreenBrowserPrefix + 'fullscreenchange');
H5P.isFullscreen = true;
document.addEventListener(eventName, function () {
@ -188,39 +232,40 @@ H5P.fullScreen = function ($el, obj, exitCallback, $body) {
first = false;
return;
}
H5P.isFullscreen = false;
$el.add(H5P.$body).removeClass('h5p-fullscreen');
H5P.jQuery(".h5p-iframe").each(function (idx, el) {
H5P.resizeIframe(H5P.jQuery(el).data('content-id'), 0);
});
if (exitCallback) {
exitCallback();
}
if (obj.resize !== undefined) {
obj.resize(false);
}
done('h5p-fullscreen');
document.removeEventListener(eventName, arguments.callee, false);
});
if (H5P.fullScreenBrowserPrefix === '') {
$el[0].requestFullScreen();
$element[0].requestFullScreen();
}
else {
var method = (H5P.fullScreenBrowserPrefix === 'ms' ? 'msRequestFullscreen' : H5P.fullScreenBrowserPrefix + 'RequestFullScreen');
var params = (H5P.fullScreenBrowserPrefix === 'webkit' ? Element.ALLOW_KEYBOARD_INPUT : undefined);
$el[0][method](params);
$element[0][method](params);
}
$el.add(H5P.$body).addClass('h5p-fullscreen');
$classes.addClass('h5p-fullscreen');
}
H5P.jQuery(".h5p-iframe").each(function (idx, el) {
H5P.resizeIframe(H5P.jQuery(el).data('content-id'), 0);
});
if (obj.resize !== undefined) {
obj.resize(true);
if ($iframe !== undefined) {
// Set iframe to its default size(100%).
$iframe.css('height', '');
}
if (H5P.fullScreenBrowserPrefix === undefined) {
// Resize content.
if (instance.$ !== undefined) {
instance.$.trigger('resize');
}
else if (instance.resize !== undefined) {
instance.resize();
}
}
// Allow H5P to set focus when entering fullscreen mode
if (instance.focus !== undefined) {
instance.focus();
}
};
@ -238,7 +283,7 @@ H5P.getPath = function (path, contentId) {
if (path.substr(0, 7) === 'http://' || path.substr(0, 8) === 'https://') {
return path;
}
return H5PIntegration.getContentPath(contentId) + path;
};
@ -256,6 +301,8 @@ H5P.getContentPath = function (contentId) {
/**
* Get library class constructor from H5P by classname.
* Note that this class will only work for resolve "H5P.NameWithoutDot".
* Also check out: H5P.newRunnable
*
* Used from libraries to construct instances of other libraries' objects by name.
*
@ -267,7 +314,479 @@ H5P.classFromName = function (name) {
return this[arr[arr.length-1]];
};
// Helper object for keeping coordinates in the same format all over.
/**
* A safe way of creating a new instance of a runnable H5P.
*
* TODO: Should we check if version matches the library?
* TODO: Dynamically try to load libraries currently not loaded? That will require a callback.
*
* @param {Object} library Library/action object form params.
* @param {Number} contentId
* @param {jQuery} $attachTo The element to attach the new instance to.
* @return {Object} Instance.
*/
H5P.newRunnable = function (library, contentId, $attachTo) {
try {
var nameSplit = library.library.split(' ', 2);
var versionSplit = nameSplit[1].split('.', 2);
}
catch (err) {
return H5P.error('Invalid library string: ' + library.library);
}
if ((library.params instanceof Object) !== true || (library.params instanceof Array) === true) {
H5P.error('Invalid library params for: ' + library.library);
return H5P.error(library.params);
}
// Find constructor function
try {
nameSplit = nameSplit[0].split('.');
var constructor = window;
for (var i = 0; i < nameSplit.length; i++) {
constructor = constructor[nameSplit[i]];
};
if (typeof constructor !== 'function') {
throw null;
}
}
catch (err) {
return H5P.error('Unable to find constructor for: ' + library.library);
}
var instance = new constructor(library.params, contentId);
if ($attachTo !== undefined) {
instance.attach($attachTo);
if (instance.$ !== undefined) {
// Resize content.
instance.$.trigger('resize');
}
}
return instance;
};
/**
* Used to print useful error messages.
*
* @param {mixed} err Error to print.
* @returns {undefined}
*/
H5P.error = function (err) {
if (window['console'] !== undefined && console.error !== undefined) {
console.error(err);
}
}
/**
* Translate text strings.
*
* @param {String} key Translation identifier, may only contain a-zA-Z0-9. No spaces or special chars.
* @param {Object} vars Data for placeholders.
* @param {String} ns Translation namespace. Defaults to H5P.
* @returns {String} Text
*/
H5P.t = function (key, vars, ns) {
if (ns === undefined) {
ns = 'H5P';
}
if (H5PIntegration.i18n[ns] === undefined) {
return '[Missing translation namespace "' + ns + '"]';
}
if (H5PIntegration.i18n[ns][key] === undefined) {
return '[Missing translation "' + key + '" in "' + ns + '"]';
}
var translation = H5PIntegration.i18n[ns][key];
if (vars !== undefined) {
// Replace placeholder with variables.
for (var placeholder in vars) {
translation = translation.replace(placeholder, vars[placeholder]);
}
}
return translation;
};
H5P.Dialog = function (name, title, content, $element) {
var self = this;
var $dialog = H5P.jQuery('<div class="h5p-popup-dialog h5p-' + name + '-dialog">\
<div class="h5p-inner">\
<h2>' + title + '</h2>\
<div class="h5p-scroll-content">' + content + '</div>\
<div class="h5p-close" role="button" tabindex="1" title="' + H5P.t('close') + '">\
</div>\
</div>')
.insertAfter($element)
.click(function () {
self.close();
})
.children('.h5p-inner')
.click(function () {
return false;
})
.find('.h5p-close')
.click(function () {
self.close();
})
.end()
.end();
this.open = function () {
setTimeout(function () {
$dialog.addClass('h5p-open'); // Fade in
// Triggering an event, in case something has to be done after dialog has been opened.
H5P.jQuery(self).trigger('dialog-opened', [$dialog]);
}, 1);
};
this.close = function () {
$dialog.removeClass('h5p-open'); // Fade out
setTimeout(function () {
$dialog.remove();
}, 200);
};
};
/**
* Gather copyright information and display in a dialog over the content.
*
* @param {jQuery} $element to insert dialog after.
* @param {object} instance to get copyright information from.
* @returns {undefined}
*/
H5P.openCopyrightsDialog = function ($element, instance) {
var copyrights = instance.getCopyrights();
if (copyrights !== undefined) {
copyrights = copyrights.toString();
}
if (copyrights === undefined || copyrights === '') {
copyrights = H5P.t('noCopyrights');
}
var dialog = new H5P.Dialog('copyrights', H5P.t('copyrightInformation'), copyrights, $element);
dialog.open();
};
/**
* Display a dialog containing the embed code.
*
* @param {jQuery} $element to insert dialog after.
* @param {string} embed code.
* @returns {undefined}
*/
H5P.openEmbedDialog = function ($element, embedCode) {
var dialog = new H5P.Dialog('embed', H5P.t('embed'), '<textarea class="h5p-embed-code-container">' + embedCode + '</textarea>', $element);
// Selecting embed code when dialog is opened
H5P.jQuery(dialog).on('dialog-opened', function (event, $dialog) {
$dialog.find('.h5p-embed-code-container').select();
});
dialog.open();
};
/**
* Copyrights for a H5P Content Library.
*/
H5P.ContentCopyrights = function () {
var label;
var media = [];
var content = [];
/**
* Public. Set label.
*
* @param {String} newLabel
*/
this.setLabel = function (newLabel) {
label = newLabel;
};
/**
* Public. Add sub content.
*
* @param {H5P.MediaCopyright} newMedia
*/
this.addMedia = function (newMedia) {
if (newMedia !== undefined) {
media.push(newMedia);
}
};
/**
* Public. Add sub content.
*
* @param {H5P.ContentCopyrights} newContent
*/
this.addContent = function (newContent) {
if (newContent !== undefined) {
content.push(newContent);
}
};
/**
* Public. Print content copyright.
*
* @returns {String} HTML.
*/
this.toString = function () {
var html = '';
// Add media rights
for (var i = 0; i < media.length; i++) {
html += media[i];
}
// Add sub content rights
for (var i = 0; i < content.length; i++) {
html += content[i];
}
if (html !== '') {
// Add a label to this info
if (label !== undefined) {
html = '<h3>' + label + '</h3>' + html;
}
// Add wrapper
html = '<div class="h5p-content-copyrights">' + html + '</div>';
}
return html;
};
}
/**
* A ordered list of copyright fields for media.
*
* @param {Object} copyright information fields.
* @param {Object} labels translation. Optional.
* @param {Array} order of fields. Optional.
* @param {Object} extraFields for copyright. Optional.
*/
H5P.MediaCopyright = function (copyright, labels, order, extraFields) {
var thumbnail;
var list = new H5P.DefinitionList();
/**
* Private. Get translated label for field.
*
* @param {String} fieldName
* @return {String}
*/
var getLabel = function (fieldName) {
if (labels === undefined || labels[fieldName] === undefined) {
return H5P.t(fieldName);
}
return labels[fieldName];
};
/**
* Private. Get humanized value for field.
*
* @param {String} fieldName
* @return {String}
*/
var humanizeValue = function (fieldName, value) {
if (fieldName === 'license') {
return H5P.copyrightLicenses[value];
}
return value;
};
if (copyright !== undefined) {
// Add the extra fields
for (var field in extraFields) {
if (extraFields.hasOwnProperty(field)) {
copyright[field] = extraFields[field];
}
}
if (order === undefined) {
// Set default order
order = ['title', 'author', 'year', 'source', 'license'];
}
for (var i = 0; i < order.length; i++) {
var fieldName = order[i];
if (copyright[fieldName] !== undefined) {
list.add(new H5P.Field(getLabel(fieldName), humanizeValue(fieldName, copyright[fieldName])));
}
}
}
/**
* Public. Set thumbnail.
*
* @param {H5P.Thumbnail} newThumbnail
*/
this.setThumbnail = function (newThumbnail) {
thumbnail = newThumbnail;
};
/**
* Public. Checks if this copyright is undisclosed.
* I.e. only has the license attribute set, and it's undisclosed.
*
* @returns {Boolean}
*/
this.undisclosed = function () {
if (list.size() === 1) {
var field = list.get(0);
if (field.getLabel() === getLabel('license') && field.getValue() === humanizeValue('license', 'U')) {
return true;
}
}
return false;
};
/**
* Public. Print media copyright.
*
* @returns {String} HTML.
*/
this.toString = function () {
var html = '';
if (this.undisclosed()) {
return html; // No need to print a copyright with a single undisclosed license.
}
if (thumbnail !== undefined) {
html += thumbnail;
}
html += list;
if (html !== '') {
html = '<div class="h5p-media-copyright">' + html + '</div>';
}
return html;
};
}
// Translate table for copyright license codes.
H5P.copyrightLicenses = {
'U': 'Undisclosed',
'CC BY': 'Attribution',
'CC BY-SA': 'Attribution-ShareAlike',
'CC BY-ND': 'Attribution-NoDerivs',
'CC BY-NC': 'Attribution-NonCommercial',
'CC BY-NC-SA': 'Attribution-NonCommercial-ShareAlike',
'CC BY-NC-ND': 'Attribution-NonCommercial-NoDerivs',
'GNU GPL': 'General Public License',
'PD': 'Public Domain',
'ODC PDDL': 'Public Domain Dedication and Licence',
'CC PDM': 'Public Domain Mark',
'C': 'Copyright'
};
/**
* Simple class for creating thumbnails for images.
*
* @param {String} source
* @param {Number} width
* @param {Number} height
*/
H5P.Thumbnail = function (source, width, height) {
var thumbWidth, thumbHeight = 100;
if (width !== undefined) {
thumbWidth = Math.round(thumbHeight * (width / height));
}
/**
* Public. Print thumbnail.
*
* @returns {String} HTML.
*/
this.toString = function () {
return '<img src="' + source + '" alt="' + H5P.t('thumbnail') + '" class="h5p-thumbnail" height="' + thumbHeight + '"' + (thumbWidth === undefined ? '' : ' width="' + thumbWidth + '"') + '/>';
};
}
/**
* Simple data class for storing a single field.
*/
H5P.Field = function (label, value) {
/**
* Public. Get field label.
*
* @returns {String}
*/
this.getLabel = function () {
return label;
};
/**
* Public. Get field value.
*
* @returns {String}
*/
this.getValue = function () {
return value;
};
}
/**
* Simple class for creating a definition list.
*/
H5P.DefinitionList = function () {
var fields = [];
/**
* Public. Add field to list.
*
* @param {H5P.Field} field
*/
this.add = function (field) {
fields.push(field);
};
/**
* Public. Get Number of fields.
*
* @returns {Number}
*/
this.size = function () {
return fields.length;
};
/**
* Public. Get field at given index.
*
* @param {Number} index
* @returns {Object}
*/
this.get = function (index) {
return fields[index];
};
/**
* Public. Print definition list.
*
* @returns {String} HTML.
*/
this.toString = function () {
var html = '';
for (var i = 0; i < fields.length; i++) {
var field = fields[i];
html += '<dt>' + field.getLabel() + '</dt><dd>' + field.getValue() + '</dd>';
}
return (html === '' ? html : '<dl class="h5p-definition-list">' + html + '</dl>');
};
}
/**
* THIS FUNCTION/CLASS IS DEPRECATED AND WILL BE REMOVED.
*
* Helper object for keeping coordinates in the same format all over.
*/
H5P.Coords = function (x, y, w, h) {
if ( !(this instanceof H5P.Coords) )
return new H5P.Coords(x, y, w, h);
@ -326,9 +845,9 @@ H5P.libraryFromString = function (library) {
/**
* Get the path to the library
*
* @param {string} library
* The library identifier in the format "machineName-majorVersion.minorVersion"
* @returns {string} The full path to the library
* @param {String} library
* The library identifier in the format "machineName-majorVersion.minorVersion".
* @returns {String} The full path to the library.
*/
H5P.getLibraryPath = function (library) {
return H5PIntegration.getLibraryPath(library);
@ -336,6 +855,7 @@ H5P.getLibraryPath = function (library) {
/**
* Recursivly clone the given object.
* TODO: Consider if this needs to be in core. Doesn't $.extend do the same?
*
* @param {object} object Object to clone.
* @param {type} recursive
@ -360,6 +880,7 @@ H5P.cloneObject = function (object, recursive) {
/**
* Remove all empty spaces before and after the value.
* TODO: Only include this or String.trim(). What is best?
*
* @param {String} value
* @returns {@exp;value@call;replace}
@ -402,6 +923,7 @@ H5P.cssLoaded = function (path) {
/**
* Shuffle an array in place.
* TODO: Consider if this should be a part of core. I'm guessing very few libraries are going to use it.
*
* @param {array} array Array to shuffle
* @returns {array} The passed array is returned for chaining.
@ -425,6 +947,7 @@ H5P.shuffleArray = function (array) {
/**
* 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} points

227
styles/h5p-admin.css Normal file
View File

@ -0,0 +1,227 @@
/* Font Awesome font licensed under SIL OFL 1.1 · Code licensed under MIT License */
@font-face {
font-family: 'H5PFontAwesome4'; /* Named so to avoid collisions and preserve backwards compatibility! */
src: url('../fonts/fontawesome-webfont.eot?v=4.0.3');
src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.0.3') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff?v=4.0.3') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.0.3') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.0.3#fontawesomeregular') format('svg');
font-weight: normal;
font-style: normal;
}
.h5p-content {
border: 1px solid #DDD;
border-radius: 3px;
padding: 10px;
}
.h5p-admin-table,
.h5p-admin-table > tbody {
border: none;
}
.h5p-admin-table tr:nth-child(odd) {
background-color: #F9F9F9;
}
.h5p-admin-table tbody tr:hover {
background-color: #EEE;
}
.h5p-admin-table.empty {
padding: 1em;
background-color: #EEE;
font-size: 1.2em;
font-weight: bold;
}
.h5p-admin-table.libraries th:last-child,
.h5p-admin-table.libraries td:last-child {
text-align: right;
}
.h5p-admin-table.libraries button {
font-family: H5PFontAwesome4;
font-size: 1.6em;
font-style: normal;
font-weight: normal;
margin-right: 10px;
cursor: pointer;
border: 1px solid #AAA;
border-radius: .2em;
line-height: 1.6em;
width: 1.6em;
background-color: #e0e0e0;
text-shadow: 0 0 0.5em #fff;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.h5p-admin-table.libraries button:hover,
.h5p-admin-table.libraries .h5p-admin-delete-library:hover {
background-color: #d0d0d0;
}
.h5p-admin-table.libraries .h5p-admin-delete-library:disabled:hover {
background-color: #e0e0e0;
}
.h5p-admin-table.libraries .h5p-admin-delete-library {
color: #f9010f;
}
.h5p-admin-table.libraries .h5p-admin-delete-library:disabled {
cursor: default;
color: #c0c0c0;
}
.h5p-library-info {
padding: 1em 1em;
margin: 1em 0;
width: 350px;
border: 1px solid #DDD;
border-radius: 3px;
}
/* Labeled field (label + value) */
.h5p-labeled-field {
border-bottom: 1px solid #ccc;
}
.h5p-labeled-field:last-child {
border-bottom: none;
}
.h5p-labeled-field .h5p-label {
display: inline-block;
min-width: 150px;
font-size: 1.2em;
font-weight: bold;
padding: 0.2em;
}
.h5p-labeled-field .h5p-value {
display: inline-block;
padding: 0.2em;
}
/* Search element */
.h5p-content-search {
display: inline-block;
position: relative;
width: 100%;
padding: 5px 0;
margin-top: 10px;
border: 1px solid #CCC;
border-radius: 3px;
box-shadow: 2px 2px 5px #888888;
}
.h5p-content-search:before {
font-family: H5PFontAwesome4;
margin: 0 10px;
content: "\f002";
font-size: 1.4em;
}
.h5p-content-search input {
font-size: 120%;
line-height: 120%;
}
.h5p-admin-search-results {
margin-left: 10px;
color: #888;
}
.h5p-admin-pager-size-selector {
position: absolute;
right: 10px;
top: .75em;
display: inline-block;
}
.h5p-admin-pager-size-selector > span {
padding: 5px;
margin-left: 10px;
cursor: pointer;
border: 1px solid #CCC;
border-radius: 3px;
}
.h5p-admin-pager-size-selector > span.selected {
background-color: #edf5fa;
}
.h5p-admin-pager-size-selector > span:hover {
background-color: #555;
color: #FFF;
}
/* Generic "javascript"-action button */
button.h5p-admin {
border: 1px solid #AAA;
border-radius: 5px;
padding: 3px 10px;
background-color: #EEE;
cursor: pointer;
display: inline-block;
text-align: center;
color: #222;
}
button.h5p-admin:hover {
background-color: #555;
color: #FFF;
}
button.h5p-admin.disabled,
button.h5p-admin.disabled:hover {
cursor: auto;
color: #CCC;
background-color: #FFF;
}
/* Pager element */
.h5p-content-pager {
display: inline-block;
border: 1px solid #CCC;
border-radius: 3px;
box-shadow: 2px 2px 5px #888888;
width: 100%;
text-align: center;
padding: 3px 0;
}
.h5p-content-pager > button {
min-width: 80px;
font-size: 130%;
line-height: 130%;
border: none;
background: none;
font-family: H5PFontAwesome4;
font-size: 1.4em;
}
.h5p-content-pager > button:focus {
outline: 0;
}
.h5p-content-pager > button:last-child {
margin-left: 10px;
}
.h5p-content-pager > .pager-info {
cursor: pointer;
padding: 5px;
border-radius: 3px;
}
.h5p-content-pager > .pager-info:hover {
background-color: #555;
color: #FFF;
}
.h5p-content-pager > .pager-info,
.h5p-content-pager > .h5p-pager-goto {
margin: 0 10px;
line-height: 130%;
display: inline-block;
}
.h5p-admin-header {
margin-top: 1.5em;
}
#h5p-library-upload-form.h5p-admin-upload-libraries-form {
position: relative;
margin: 0;
}
.h5p-admin-upload-libraries-form .form-submit {
position: absolute;
top: 0;
right: 0;
}

View File

@ -7,22 +7,42 @@
font-style: normal;
}
body.h5p-iframe-content {
font-family: Arial, Helvetica, sans-serif;
margin: 0;
@font-face {
font-family: 'H5P';
src:url('../fonts/h5p.eot?-r12eb7');
src:url('../fonts/h5p.eot?#iefix-r12eb7') format('embedded-opentype'),
url('../fonts/h5p.woff?-r12eb7') format('woff'),
url('../fonts/h5p.ttf?-r12eb7') format('truetype'),
url('../fonts/h5p.svg?-r12eb7#h5p_toolbar') format('svg');
font-weight: normal;
font-style: normal;
}
body.h5p-semi-fullscreen {
html.h5p-iframe, html.h5p-iframe > body {
font-family: Arial, Helvetica, sans-serif;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
.h5p-semi-fullscreen, .h5p-fullscreen, html.h5p-iframe .h5p-container {
overflow: hidden;
}
.h5p-content {
display: block;
width: auto;
height: auto;
position: relative;
background: #fefefe;
font-size: 16px;
line-height: 1.5em;
}
div.h5p-semi-fullscreen {
html.h5p-iframe .h5p-content {
width: 100%;
height: 100%;
}
.h5p-container {
position: relative;
z-index: 1;
}
.h5p-container.h5p-semi-fullscreen {
position: fixed;
top: 0;
left: 0;
@ -34,7 +54,11 @@ div.h5p-semi-fullscreen {
.h5p-content-controls {
margin: 0;
text-align: right;
margin: 0;
position: relative;
z-index: 3;
}
.h5p-iframe > .h5p-fullscreen .h5p-content-controls {
display: none;
}
.h5p-content-controls > a:link, .h5p-content-controls > a:visited, a.h5p-disable-fullscreen:link, a.h5p-disable-fullscreen:visited {
@ -50,6 +74,7 @@ div.h5p-semi-fullscreen {
.h5p-enable-fullscreen, .h5p-disable-fullscreen {
display: inline-block;
line-height: 1.25em;
padding: 6px 12px;
border: 0.2em solid #fff;
border-radius: 0.4em;
@ -86,6 +111,7 @@ div.h5p-semi-fullscreen {
background: -ms-linear-gradient(top, rgba(4,104,206,1) 0%,rgba(100,152,254,1) 100%); /* IE10+ */
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#0468ce', endColorstr='#6498fe',GradientType=0 ); /* IE6-9 */
}
div.h5p-fullscreen {
width: 100%;
height: 100%;
@ -110,16 +136,13 @@ div.h5p-fullscreen {
left: 0;
z-index: 100;
}
.h5p-iframe-wrapper .buttons {
text-align: right;
}
.h5p-iframe-wrapper.h5p-semi-fullscreen .buttons {
position: absolute;
top: 0;
right: 0;
z-index: 20;
}
.h5p-iframe-wrapper .h5p-iframe {
.h5p-iframe-wrapper iframe.h5p-iframe {
width: 100%;
height: 100%;
z-index: 10;
@ -156,16 +179,176 @@ div.h5p-fullscreen {
cursor: pointer;
}
html.h5p-fullscreen, body.h5p-fullscreen {
height: 100%;
.h5p-actions {
overflow: hidden;
list-style: none;
padding: 4px;
margin: 0.25em 0;
position: relative;
z-index: 2;
font-size: 12px;
}
.h5p-fullscreen .h5p-content-controls {
.h5p-fullscreen .h5p-actions, .h5p-semi-fullscreen .h5p-actions {
display: none;
}
body.h5p-iframe-content div.h5p-content {
.h5p-actions > .h5p-button {
float: left;
cursor: pointer;
margin: 0 1.5em 0 0;
background: none;
padding: 0;
vertical-align: top;
color: #999;
text-decoration: none;
outline: none;
}
.h5p-actions > .h5p-button:hover {
color: #666;
}
.h5p-actions > .h5p-button:before {
font-family: H5PFontAwesome4;
font-size: 1em;
padding-right: 0.3em;
}
.h5p-actions > .h5p-button.h5p-export:before {
content: "\f019";
}
.h5p-actions > .h5p-button.h5p-copyrights:before {
font-family: H5P;
content: "\e668";
font-size: 1.7em;
padding-right: 0;
vertical-align: bottom;
}
.h5p-actions > .h5p-button.h5p-embed:before {
content: "\f121";
}
.h5p-actions .h5p-link {
float: right;
margin-right: 0;
font-size: 2.0em;
overflow: hidden;
color: #999;
text-decoration: none;
outline: none;
}
body.h5p-iframe-content.h5p-fullscreen div.h5p-content {
.h5p-actions .h5p-link:before {
font-family: H5P;
content: "\e667";
}
.h5p-popup-dialog {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 100;
padding: 2em;
box-sizing: border-box;
-moz-box-sizing: border-box;
/* background: rgba(50,50,50,0.5);*/
opacity: 0;
-webkit-transition: opacity 0.2s;
-moz-transition: opacity 0.2s;
-o-transition: opacity 0.2s;
transition: opacity 0.2s;
}
.h5p-popup-dialog.h5p-open {
opacity: 1;
}
.h5p-popup-dialog .h5p-inner {
box-sizing: border-box;
box-shadow: 0 0 2em #000;
background: #fff;
height: 90%;
max-height: 100%;
padding: 0.75em;
position: relative;
}
.h5p-popup-dialog .h5p-inner > h2 {
font-size: 1.5em;
margin: 0.25em 0;
position: absolute;
}
.h5p-embed-dialog .h5p-inner {
width: 50%;
left: 25%;
top: 25%;
height: auto;
}
.h5p-embed-dialog .h5p-embed-code-container {
width: 90%;
padding: .3em;
min-height: 10em;
resize: none;
outline: none;
}
.h5p-popup-dialog .h5p-scroll-content {
border-top: 2.75em solid transparent;
box-sizing: border-box;
-moz-box-sizing: border-box;
height: 100%;
overflow: auto;
overflow-x: hidden;
overflow-y: auto;
}
.h5p-popup-dialog .h5p-scroll-content::-webkit-scrollbar {
width: 8px;
}
.h5p-popup-dialog .h5p-scroll-content::-webkit-scrollbar-track {
background: #e0e0e0;
}
.h5p-popup-dialog .h5p-scroll-content::-webkit-scrollbar-thumb {
box-shadow: 0 0 10px #000 inset;
border-radius: 4px;
}
.h5p-popup-dialog .h5p-close {
cursor: pointer;
outline:none
}
.h5p-popup-dialog .h5p-close:after {
font-family: H5PFontAwesome4;
content: "\f00d";
font-size: 2em;
position: absolute;
right: 0.5em;
top: 0.5em;
cursor: pointer;
-webkit-transition: -webkit-transform 0.2s;
-moz-transition: -moz-transform 0.2s;
-o-transition: -o-transform 0.2s;
transition: transform 0.2s;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
.h5p-popup-dialog .h5p-close:hover:after {
-webkit-transform: scale(1.1, 1.1);
-moz-transform: scale(1.1, 1.1);
-ms-transform: scale(1.1, 1.1);
-o-transform: scale(1.1, 1.1);
transform: scale(1.1, 1.1);
}
.h5p-poopup-dialog h2 {
margin: 0.25em 0 0.5em;
}
.h5p-popup-dialog h3 {
margin: 0.75em 0 0.25em;
}
.h5p-popup-dialog dl {
margin: 0.25em 0 0.75em;
}
.h5p-popup-dialog dt {
float: left;
margin: 0 0.75em 0 0;
}
.h5p-popup-dialog dt:after {
content: ':';
}
.h5p-popup-dialog dd {
margin: 0;
}
.h5p-content-copyrights {
border-left: 0.25em solid #d0d0d0;
margin-left: 0.25em;
padding-left: 0.25em;
}