diff --git a/fonts/FontAwesome.otf b/fonts/FontAwesome.otf deleted file mode 100644 index 8b0f54e..0000000 Binary files a/fonts/FontAwesome.otf and /dev/null differ diff --git a/fonts/fontawesome-webfont.eot b/fonts/fontawesome-webfont.eot deleted file mode 100644 index 7c79c6a..0000000 Binary files a/fonts/fontawesome-webfont.eot and /dev/null differ diff --git a/fonts/fontawesome-webfont.svg b/fonts/fontawesome-webfont.svg deleted file mode 100644 index 45fdf33..0000000 --- a/fonts/fontawesome-webfont.svg +++ /dev/null @@ -1,414 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/fonts/fontawesome-webfont.ttf b/fonts/fontawesome-webfont.ttf deleted file mode 100644 index e89738d..0000000 Binary files a/fonts/fontawesome-webfont.ttf and /dev/null differ diff --git a/fonts/fontawesome-webfont.woff b/fonts/fontawesome-webfont.woff deleted file mode 100644 index 8c1748a..0000000 Binary files a/fonts/fontawesome-webfont.woff and /dev/null differ diff --git a/fonts/h5p.eot b/fonts/h5p.eot index d74adc1..9680263 100644 Binary files a/fonts/h5p.eot and b/fonts/h5p.eot differ diff --git a/fonts/h5p.svg b/fonts/h5p.svg index 3364cd8..e4c9ef5 100644 --- a/fonts/h5p.svg +++ b/fonts/h5p.svg @@ -1,12 +1,42 @@ -Generated by IcoMoon + + +{ + "fontFamily": "h5p-core-fonts", + "majorVersion": 1, + "minorVersion": 0, + "fontURL": "http://h5p.org", + "license": "MIT license", + "licenseURL": "http://opensource.org/licenses/MIT", + "designer": "Magnus Vik Magnussen", + "designerURL": "", + "version": "Version 1.0", + "fontId": "h5p-core-fonts", + "psName": "h5p-core-fonts", + "subFamily": "Regular", + "fullName": "h5p-core-fonts", + "description": "Generated by IcoMoon" +} + + - + - - + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/h5p.ttf b/fonts/h5p.ttf index 904d19b..c0ffaf5 100644 Binary files a/fonts/h5p.ttf and b/fonts/h5p.ttf differ diff --git a/fonts/h5p.woff b/fonts/h5p.woff deleted file mode 100644 index de26840..0000000 Binary files a/fonts/h5p.woff and /dev/null differ diff --git a/h5p.classes.php b/h5p.classes.php index f5fa9ea..bf1b48d 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -55,6 +55,13 @@ interface H5PFrameworkInterface { * @return string Path to the last uploaded h5p */ public function getUploadedH5pPath(); + + /** + * Get the list of the current installed libraries + * + * @return array Associative array containg one item per machine name. This item contains an array of libraries. + */ + public function loadLibraries(); /** * Get id to an excisting library @@ -119,23 +126,23 @@ interface H5PFrameworkInterface { * Object holding the information that is to be stored */ public function saveLibraryData(&$libraryData, $new = TRUE); - + /** - * Stores contentData - * - * @param string $contentJson - * The content data that is to be stored - * @param array $mainJsonData - * The data extracted from the h5p.json file - * @param array $mainLibraryId - * Main library identifier + * Insert new content. + * + * @param object $content * @param int $contentMainId - * Any contentMainId defined by the framework, for instance to support revisioning - * @param int $contentId - * Framework specific id identifying the content */ - public function saveContentData($content, $contentMainId = NULL); - + public function insertContent($content, $contentMainId = NULL); + + /** + * Update old content. + * + * @param object $content + * @param int $contentMainId + */ + public function updateContent($content, $contentMainId = NULL); + /** * Save what libraries a library is dependending on * @@ -262,6 +269,51 @@ interface H5PFrameworkInterface { * @return array */ public function loadContentDependencies($id, $type = NULL); + + /** + * Get data from cache. + * + * @param string $group + * @param string $key + */ + public function cacheGet($group, $key); + + /** + * Store data in cache. + * + * @param string $group + * @param string $key + * @param mixed $data + */ + public function cacheSet($group, $key, $data); + + /** + * Delete data from cache. + * + * @param string $group + * @param string $key + */ + public function cacheDel($group, $key = NULL); + + /** + * Will invalidate the cache for the content that uses the specified library. + * This means that the content dependencies has to be rebuilt, and the parameters refiltered. + * + * @param int $library_id + */ + public function invalidateContentCache($library_id); + + /** + * Get content without cache. + */ + public function getNotCached(); + + /** + * Get number of contents using library as main library. + * + * @param int $library_id + */ + public function getNumContent($library_id); } /** @@ -998,6 +1050,9 @@ class H5PStorage { if (isset($library['editorDependencies'])) { $this->h5pF->saveLibraryDependencies($library['libraryId'], $library['editorDependencies'], 'editor'); } + + // Make sure libraries dependencies, parameter filtering and export files gets regenerated for all content who uses this library. + $this->h5pF->invalidateContentCache($library['libraryId']); } } @@ -1017,7 +1072,7 @@ class H5PStorage { } $content['library'] = $librariesInUse['preloaded-' . $this->h5pC->mainJsonData['mainLibrary']]['library']; $content['params'] = file_get_contents($current_path . DIRECTORY_SEPARATOR . 'content.json'); - $contentId = $this->h5pF->saveContentData($content, $contentMainId); + $contentId = $this->h5pC->saveContent($content, $contentMainId); $this->contentId = $contentId; $contents_path = $this->h5pF->getH5pPath() . DIRECTORY_SEPARATOR . 'content'; @@ -1105,96 +1160,86 @@ Class H5PExport { $this->h5pF = $H5PFramework; $this->h5pC = $H5PCore; } - + /** * Return path to h5p package. * * Creates package if not already created * - * @param int/string $contentId - * Identifier for this H5P - * @param String $title - * Title of H5P - * @param string $language - * Language code for H5P + * @param array $content * @return string - * Path to .h5p file */ - public function getExportPath($content) { + public function createExportFile($content) { $h5pDir = $this->h5pF->getH5pPath() . DIRECTORY_SEPARATOR; $tempPath = $h5pDir . 'temp' . DIRECTORY_SEPARATOR . $content['id']; $zipPath = $h5pDir . 'exports' . DIRECTORY_SEPARATOR . $content['id'] . '.h5p'; - // Check if h5p-package already exists. - if (!file_exists($zipPath)) { - // Temp dir to put the h5p files in - @mkdir($tempPath, 0777, TRUE); - @mkdir($h5pDir . 'exports', 0777, TRUE); - - // Create content folder - if ($this->h5pC->copyFileTree($h5pDir . 'content' . DIRECTORY_SEPARATOR . $content['id'], $tempPath . DIRECTORY_SEPARATOR . 'content') === FALSE) { - return FALSE; - } - file_put_contents($tempPath . DIRECTORY_SEPARATOR . 'content' . DIRECTORY_SEPARATOR . 'content.json', $content['params']); - - // Make embedTypes into an array - $embedTypes = explode(', ', $content['embedType']); // Won't content always be embedded in one way? + // Temp dir to put the h5p files in + @mkdir($tempPath, 0777, TRUE); + @mkdir($h5pDir . 'exports', 0777, TRUE); - // Build h5p.json - $h5pJson = array ( - 'title' => $content['title'], - 'language' => $content['language'], - 'mainLibrary' => $content['library']['name'], - 'embedTypes' => $embedTypes, + // Create content folder + if ($this->h5pC->copyFileTree($h5pDir . 'content' . DIRECTORY_SEPARATOR . $content['id'], $tempPath . DIRECTORY_SEPARATOR . 'content') === FALSE) { + return FALSE; + } + file_put_contents($tempPath . DIRECTORY_SEPARATOR . 'content' . DIRECTORY_SEPARATOR . 'content.json', $content['params']); + + // Make embedTypes into an array + $embedTypes = explode(', ', $content['embedType']); // Won't content always be embedded in one way? + + // Build h5p.json + $h5pJson = array ( + 'title' => $content['title'], + 'language' => $content['language'], + 'mainLibrary' => $content['library']['name'], + 'embedTypes' => $embedTypes, + ); + + // Add dependencies to h5p + foreach ($content['dependencies'] as $dependency) { + $library = $dependency['library']; + + // Copy library to h5p + $source = isset($library['path']) ? $library['path'] : $h5pDir . 'libraries' . DIRECTORY_SEPARATOR . H5PCore::libraryToString($library, TRUE); + $destination = $tempPath . DIRECTORY_SEPARATOR . $library['machineName']; + $this->h5pC->copyFileTree($source, $destination); + + // Do not add editor dependencies to h5p json. + if ($dependency['type'] === 'editor') { + continue; + } + + $h5pJson[$dependency['type'] . 'Dependencies'][] = array( + 'machineName' => $library['machineName'], + 'majorVersion' => $library['majorVersion'], + 'minorVersion' => $library['minorVersion'] ); - - // Add dependencies to h5p - $dependencies = $this->h5pC->loadContentDependencies($content['id']); - foreach ($dependencies as $dependency) { - // Copy library to h5p - $source = isset($dependency['path']) ? $dependency['path'] : $h5pDir . 'libraries' . DIRECTORY_SEPARATOR . H5PCore::libraryToString($dependency, TRUE); - $destination = $tempPath . DIRECTORY_SEPARATOR . $dependency['machineName']; - $this->h5pC->copyFileTree($source, $destination); - - // Do not add editor dependencies to h5p json. - if ($dependency['dependencyType'] === 'editor') { - continue; - } - - $h5pJson[$dependency['dependencyType'] . 'Dependencies'][] = array( - 'machineName' => $dependency['machineName'], - 'majorVersion' => $dependency['majorVersion'], - 'minorVersion' => $dependency['minorVersion'] - ); - } - - // Save h5p.json - $results = print_r(json_encode($h5pJson), true); - file_put_contents($tempPath . DIRECTORY_SEPARATOR . 'h5p.json', $results); - - // Create new zip instance. - $zip = new ZipArchive(); - $zip->open($zipPath, ZIPARCHIVE::CREATE); - - // Get all files and folders in $tempPath - $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($tempPath . DIRECTORY_SEPARATOR)); - // Add files to zip - foreach ($iterator as $key=>$value) { - $test = '.'; - // Do not add the folders '.' and '..' to the zip. This will make zip invalid. - if (substr_compare($key, $test, -strlen($test), strlen($test)) !== 0) { - // Get files path in $tempPath - $filePath = explode($tempPath . DIRECTORY_SEPARATOR, $key); - // Add files to the zip with the intended file-structure - $zip->addFile($key, $filePath[1]); - } - } - // Close zip and remove temp dir - $zip->close(); - H5PCore::deleteFileTree($tempPath); } - return str_replace(DIRECTORY_SEPARATOR, '/', $zipPath); + // Save h5p.json + $results = print_r(json_encode($h5pJson), true); + file_put_contents($tempPath . DIRECTORY_SEPARATOR . 'h5p.json', $results); + + // Create new zip instance. + $zip = new ZipArchive(); + $zip->open($zipPath, ZIPARCHIVE::CREATE); + + // Get all files and folders in $tempPath + $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($tempPath . DIRECTORY_SEPARATOR)); + // Add files to zip + foreach ($iterator as $key => $value) { + $test = '.'; + // Do not add the folders '.' and '..' to the zip. This will make zip invalid. + if (substr_compare($key, $test, -strlen($test), strlen($test)) !== 0) { + // Get files path in $tempPath + $filePath = explode($tempPath . DIRECTORY_SEPARATOR, $key); + // Add files to the zip with the intended file-structure + $zip->addFile($key, $filePath[1]); + } + } + // Close zip and remove temp dir + $zip->close(); + H5PCore::deleteFileTree($tempPath); } /** @@ -1238,7 +1283,7 @@ class H5PCore { public static $coreApi = array( 'majorVersion' => 1, - 'minorVersion' => 2 + 'minorVersion' => 3 ); public static $styles = array( 'styles/h5p.css', @@ -1256,6 +1301,7 @@ class H5PCore { public static $defaultLibraryWhitelistExtras = 'js css'; public $librariesJsonData, $contentJsonData, $mainJsonData, $h5pF, $path, $development_mode, $h5pD; + private $exportEnabled; /** * Constructor for the H5PCore @@ -1264,13 +1310,15 @@ class H5PCore { * The frameworks implementation of the H5PFrameworkInterface * @param string $path H5P file storage directory. * @param string $language code. Defaults to english. + * @param boolean $export enabled? * @param int $development_mode mode. */ - public function __construct($H5PFramework, $path, $language = 'en', $development_mode = H5PDevelopment::MODE_NONE) { + public function __construct($H5PFramework, $path, $language = 'en', $export = FALSE, $development_mode = H5PDevelopment::MODE_NONE) { $this->h5pF = $H5PFramework; $this->h5pF = $H5PFramework; $this->path = $path; + $this->exportEnabled = $export; $this->development_mode = $development_mode; if ($development_mode & H5PDevelopment::MODE_LIBRARY) { @@ -1317,20 +1365,61 @@ class H5PCore { ); 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; - } - } +// // TODO: Move to filterParameters? +// 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; + } + + /** + * Filter content run parameters, rebuild content dependecy cache and export file. + * + * @param Object $content + * @return Object NULL on failure. + */ + public function filterParameters($content) { + $params = $this->h5pF->cacheGet('parameters', $content['id']); + if ($params !== NULL) { + return $params; + } + + // Validate and filter against main library semantics. + $validator = new H5PContentValidator($this->h5pF, $this); + $params = (object) array( + 'library' => H5PCore::libraryToString($content['library']), + 'params' => json_decode($content['params']) + ); + $validator->validateLibrary($params, (object) array('options' => array($params->library))); + + $params = json_encode($params->params); + + // Update content dependencies. + $content['dependencies'] = $validator->getDependencies(); + $this->h5pF->deleteLibraryUsage($content['id']); + $this->h5pF->saveLibraryUsage($content['id'], $content['dependencies']); + + if ($this->exportEnabled) { + // Recreate export file + $exporter = new H5PExport($this->h5pF, $this); + $exporter->createExportFile($content); + + // TODO: Should we rather create the file once first accessed, like imagecache? } - return $content; + // Cache. + $this->h5pF->cacheSet('parameters', $content['id'], $params); + return $params; } /** @@ -1427,7 +1516,7 @@ class H5PCore { /** * Load library semantics. * - * @return string 'div' or 'iframe'. + * @return string */ public function loadLibrarySemantics($name, $majorVersion, $minorVersion) { $semantics = NULL; @@ -1610,7 +1699,7 @@ class H5PCore { * Returns FALSE only if string is not parsable in the normal library * string formats "Lib.Name-x.y" or "Lib.Name x.y" */ - public function libraryFromString($libraryString) { + public static function libraryFromString($libraryString) { $re = '/^([\w0-9\-\.]{1,255})[\-\ ]([0-9]{1,5})\.([0-9]{1,5})$/i'; $matches = array(); $res = preg_match($re, $libraryString, $matches); @@ -1626,7 +1715,6 @@ class H5PCore { /** * Determine the correct embed type to use. - * TODO: Use constants. * * @return string 'div' or 'iframe'. */ @@ -1645,6 +1733,60 @@ class H5PCore { return $embedType; } + + /** + * Get the absolute version for the library as a human readable string. + * + * @param object $library + * @return string + */ + public static function libraryVersion($library) { + return $library->major_version . '.' . $library->minor_version . '.' . $library->patch_version; + } + + /** + * Detemine which versions content with the given library can be upgraded to. + * + * @param object $library + * @param array $versions + * @return array + */ + public function getUpgrades($library, $versions) { + $upgrades = array(); + + foreach ($versions as $upgrade) { + if ($upgrade->major_version > $library->major_version || $upgrade->major_version === $library->major_version && $upgrade->minor_version > $library->minor_version) { + $upgrades[$upgrade->id] = H5PCore::libraryVersion($upgrade); + } + } + + return $upgrades; + } + + /** + * Converts all the properties of the given object or array from + * snake_case to camelCase. Useful after fetching data from the database. + * + * Note that some databases does not support camelCase. + * + * @param mixed $arr input + * @param boolean $obj return object + * @return mixed object or array + */ + public static function snakeToCamel($arr, $obj = false) { + $newArr = array(); + + foreach ($arr as $key => $val) { + $next = -1; + while (($next = strpos($key, '_', $next + 1)) !== FALSE) { + $key = substr_replace($key, strtoupper($key{$next + 1}), $next, 2); + } + + $newArr[$key] = $val; + } + + return $obj ? (object) $newArr : $newArr; + } } /** @@ -1654,7 +1796,7 @@ class H5PContentValidator { public $h5pF; public $h5pC; private $typeMap; - private $semanticsCache; + private $libraries, $dependencies; /** * Constructor for the H5PContentValidator @@ -1680,33 +1822,24 @@ class H5PContentValidator { 'select' => 'validateSelect', 'library' => 'validateLibrary', ); - // Cache for semantics used within this validation to avoid unneccessary - // json_decodes if a library is used multiple times. - $this->semanticsCache = array(); + + // Keep track of the libraries we load to avoid loading it multiple times. + $this->libraries = array(); + // TODO: Should this possible be done in core's loadLibrary? This might be done multiple places. + + // Keep track of all dependencies for the given content. + $this->dependencies = array(); } /** - * Validate the given value from content with the matching semantics - * object from semantics - * - * Function will recurse via external functions for container objects like - * 'list', 'group' and 'library'. - * - * @param object $value - * Object to be verified. May be a string or an array. (normal or keyed) - * @param object $semantics - * Semantics object from semantics.json for main library. Further - * semantics will be loaded from H5PFramework if any libraries are - * found within the value data. + * Get the flat dependecy tree. + * + * @return array */ - public function validateBySemantics(&$value, $semantics) { - $fakebaseobject = (object) array( - 'type' => 'group', - 'fields' => $semantics, - ); - $this->validateGroup($value, $fakebaseobject, FALSE); + public function getDependencies() { + return $this->dependencies; } - + /** * Validate given text value against text semantics. */ @@ -2047,31 +2180,47 @@ class H5PContentValidator { /** * Validate given library value against library semantics. + * Check if provided library is within allowed options. * * Will recurse into validating the library's semantics too. */ public function validateLibrary(&$value, $semantics) { - // Check if provided library is within allowed options - if (isset($value->library) && in_array($value->library, $semantics->options)) { - if (isset($this->semanticsCache[$value->library])) { - $librarySemantics = $this->semanticsCache[$value->library]; - } - else { - $libspec = $this->h5pC->libraryFromString($value->library); - $librarySemantics = $this->h5pC->loadLibrarySemantics($libspec['machineName'], $libspec['majorVersion'], $libspec['minorVersion']); - $this->semanticsCache[$value->library] = $librarySemantics; - } - $this->validateBySemantics($value->params, $librarySemantics); - $validkeys = array('library', 'params'); - if (isset($semantics->extraAttributes)) { - $validkeys = array_merge($validkeys, $semantics->extraAttributes); - } - $this->filterParams($value, $validkeys); - } - else { + if (!isset($value->library) || !in_array($value->library, $semantics->options)) { $this->h5pF->setErrorMessage($this->h5pF->t('Library used in content is not a valid library according to semantics')); $value = new stdClass(); + return; } + + if (!isset($this->libraries[$value->library])) { + $libspec = H5PCore::libraryFromString($value->library); + $library = $this->h5pC->loadLibrary($libspec['machineName'], $libspec['majorVersion'], $libspec['minorVersion']); + $library['semantics'] = json_decode($library['semantics']); + $this->libraries[$value->library] = $library; + + // Find all dependencies for this library + $depkey = 'preloaded-' . $libspec['machineName']; + if (!isset($this->dependencies[$depkey])) { + $this->dependencies[$depkey] = array( + 'library' => $library, + 'type' => 'preloaded' + ); + + $this->h5pC->findLibraryDependencies($this->dependencies, $library); + } + } + else { + $library = $this->libraries[$value->library]; + } + + $this->validateGroup($value->params, (object) array( + 'type' => 'group', + 'fields' => $library['semantics'], + ), FALSE); + $validkeys = array('library', 'params'); + if (isset($semantics->extraAttributes)) { + $validkeys = array_merge($validkeys, $semantics->extraAttributes); + } + $this->filterParams($value, $validkeys); } /** diff --git a/images/throbber.gif b/images/throbber.gif new file mode 100644 index 0000000..1560b64 Binary files /dev/null and b/images/throbber.gif differ diff --git a/js/flowplayer-3.2.12.min.js b/js/flowplayer-3.2.12.min.js deleted file mode 100644 index ff7b48f..0000000 --- a/js/flowplayer-3.2.12.min.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * flowplayer.js 3.2.12. The Flowplayer API - * - * Copyright 2009-2011 Flowplayer Oy - * - * This file is part of Flowplayer. - * - * Flowplayer is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Flowplayer is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Flowplayer. If not, see . - * - * Date: ${date} - * Revision: ${revision} - */ -!function(){function h(p){console.log("$f.fireEvent",[].slice.call(p))}function l(r){if(!r||typeof r!="object"){return r}var p=new r.constructor();for(var q in r){if(r.hasOwnProperty(q)){p[q]=l(r[q])}}return p}function n(u,r){if(!u){return}var p,q=0,s=u.length;if(s===undefined){for(p in u){if(r.call(u[p],p,u[p])===false){break}}}else{for(var t=u[0];q1){var u=arguments[1],r=(arguments.length==3)?arguments[2]:{};if(typeof u=="string"){u={src:u}}u=j({bgcolor:"#000000",version:[10,1],expressInstall:"http://releases.flowplayer.org/swf/expressinstall.swf",cachebusting:false},u);if(typeof p=="string"){if(p.indexOf(".")!=-1){var t=[];n(o(p),function(){t.push(new b(this,l(u),l(r)))});return new d(t)}else{var s=c(p);return new b(s!==null?s:l(p),l(u),l(r))}}else{if(p){return new b(p,l(u),l(r))}}}return null};j(window.$f,{fireEvent:function(){var q=[].slice.call(arguments);var r=$f(q[0]);return r?r._fireEvent(q.slice(1)):null},addPlugin:function(p,q){b.prototype[p]=q;return $f},each:n,extend:j});if(typeof jQuery=="function"){jQuery.fn.flowplayer=function(r,q){if(!arguments.length||typeof arguments[0]=="number"){var p=[];this.each(function(){var s=$f(this);if(s){p.push(s)}});return arguments.length?p[arguments[0]]:new d(p)}return this.each(function(){$f(this,l(r),q?l(q):{})})}}}();!function(){var h=document.all,j="http://get.adobe.com/flashplayer",c=typeof jQuery=="function",e=/(\d+)[^\d]+(\d+)[^\d]*(\d*)/,b={width:"100%",height:"100%",id:"_"+(""+Math.random()).slice(9),allowfullscreen:true,allowscriptaccess:"always",quality:"high",version:[3,0],onFail:null,expressInstall:null,w3c:false,cachebusting:false};if(window.attachEvent){window.attachEvent("onbeforeunload",function(){__flash_unloadHandler=function(){};__flash_savedUnloadHandler=function(){}})}function i(m,l){if(l){for(var f in l){if(l.hasOwnProperty(f)){m[f]=l[f]}}}return m}function a(f,n){var m=[];for(var l in f){if(f.hasOwnProperty(l)){m[l]=n(f[l])}}return m}window.flashembed=function(f,m,l){if(typeof f=="string"){f=document.getElementById(f.replace("#",""))}if(!f){return}if(typeof m=="string"){m={src:m}}return new d(f,i(i({},b),m),l)};var g=i(window.flashembed,{conf:b,getVersion:function(){var m,f;try{f=navigator.plugins["Shockwave Flash"].description.slice(16)}catch(o){try{m=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");f=m&&m.GetVariable("$version")}catch(n){try{m=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");f=m&&m.GetVariable("$version")}catch(l){}}}f=e.exec(f);return f?[1*f[1],1*f[(f[1]*1>9?2:3)]*1]:[0,0]},asString:function(l){if(l===null||l===undefined){return null}var f=typeof l;if(f=="object"&&l.push){f="array"}switch(f){case"string":l=l.replace(new RegExp('(["\\\\])',"g"),"\\$1");l=l.replace(/^\s?(\d+\.?\d*)%/,"$1pct");return'"'+l+'"';case"array":return"["+a(l,function(o){return g.asString(o)}).join(",")+"]";case"function":return'"function()"';case"object":var m=[];for(var n in l){if(l.hasOwnProperty(n)){m.push('"'+n+'":'+g.asString(l[n]))}}return"{"+m.join(",")+"}"}return String(l).replace(/\s/g," ").replace(/\'/g,'"')},getHTML:function(o,l){o=i({},o);var n=''}o.width=o.height=o.id=o.w3c=o.src=null;o.onFail=o.version=o.expressInstall=null;for(var m in o){if(o[m]){n+=''}}var p="";if(l){for(var f in l){if(l[f]){var q=l[f];p+=f+"="+(/function|object/.test(typeof q)?g.asString(q):q)+"&"}}p=p.slice(0,-1);n+='"}n+="";return n},isSupported:function(f){return k[0]>f[0]||k[0]==f[0]&&k[1]>=f[1]}});var k=g.getVersion();function d(f,n,m){if(g.isSupported(n.version)){f.innerHTML=g.getHTML(n,m)}else{if(n.expressInstall&&g.isSupported([6,65])){f.innerHTML=g.getHTML(i(n,{src:n.expressInstall}),{MMredirectURL:encodeURIComponent(location.href),MMplayerType:"PlugIn",MMdoctitle:document.title})}else{if(!f.innerHTML.replace(/\s/g,"")){f.innerHTML="

Flash version "+n.version+" or greater is required

"+(k[0]>0?"Your version is "+k:"You have no flash plugin installed")+"

"+(f.tagName=="A"?"

Click here to download latest version

":"

Download latest version from here

");if(f.tagName=="A"||f.tagName=="DIV"){f.onclick=function(){location.href=j}}}if(n.onFail){var l=n.onFail.call(this);if(typeof l=="string"){f.innerHTML=l}}}}if(h){window[n.id]=document.getElementById(n.id)}i(this,{getRoot:function(){return f},getOptions:function(){return n},getConf:function(){return m},getApi:function(){return f.firstChild}})}if(c){jQuery.tools=jQuery.tools||{version:"3.2.12"};jQuery.tools.flashembed={conf:b};jQuery.fn.flashembed=function(l,f){return this.each(function(){$(this).data("flashembed",flashembed(this,l,f))})}}}(); \ No newline at end of file diff --git a/js/h5p-content-upgrade.js b/js/h5p-content-upgrade.js new file mode 100644 index 0000000..5542bdd --- /dev/null +++ b/js/h5p-content-upgrade.js @@ -0,0 +1,474 @@ +var H5PUpgrades = H5PUpgrades || {}; + +(function ($) { + var info, $container; + + // Initialize + $(document).ready(function () { + // Get library info + info = H5PIntegration.getLibraryInfo(); + + // Get and reset container + $container = $('#h5p-admin-container').html('

' + info.message + '

'); + + // Make it possible to select version + var $version = $(getVersionSelect(info.versions)).appendTo($container); + + // Add "go" button + $('' + - '' + library.title, + library.numContent, + library.numContentDependencies === -1 ? t.NA : library.numContentDependencies, + library.numLibraryDependencies, + '
\ + \ + \ + \ +
' ]); + if (library.upgradeUrl !== null && library.numContent !== 0) { + $('.h5p-admin-upgrade-library', $libraryRow).attr('title', t.upgradeLibrary).click(function () { + window.location.href = library.upgradeUrl; + }); + } + else { + $('.h5p-admin-upgrade-library', $libraryRow).attr('disabled', true); + } + // 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) { + if (library.numContent !== 0 || library.numContentDependencies !== 0 || library.numLibraryDependencies !== 0) { // Disabled delete if content. - $deleteButton.attr('disabled', true); //.addClass('disabled'); + $deleteButton.attr('disabled', true).attr('title', t.deleteLibrary); } else { // Go to delete page om click. diff --git a/js/h5p-utils.js b/js/h5p-utils.js index dae71e2..d8faf68 100644 --- a/js/h5p-utils.js +++ b/js/h5p-utils.js @@ -17,7 +17,7 @@ var H5PUtils = H5PUtils || {}; $tr.append('' + value + ''); }); - $table.append($thead.append($tr)) + $table.append($thead.append($tr)); } return $table; @@ -51,7 +51,7 @@ var H5PUtils = H5PUtils || {}; $field.append('
' + value + '
'); return $field; - } + }; /** * Replaces placeholder fields in translation strings @@ -61,9 +61,60 @@ var H5PUtils = H5PUtils || {}; */ H5PUtils.translateReplace = function (template, replacors) { $.each(replacors, function (key, value) { - template = template.replace(new RegExp('\\'+key, 'g'), value) + template = template.replace(new RegExp('\\'+key, 'g'), value); }); return template; - } + }; + + /** + * Get throbber with given text. + * + * @param {String} text + * @returns {$} + */ + H5PUtils.throbber = function (text) { + return $('
', { + class: 'h5p-throbber', + text: text + }); + }; + + /** + * Makes it possbile to rebuild all content caches from admin UI. + * @param {Object} notCached + * @returns {$} + */ + H5PUtils.getRebuildCache = function (notCached) { + var $container = $('

' + notCached.message + '

'); + var $button = $('').appendTo($container).click(function () { + var $spinner = $('
', {class: 'h5p-spinner'}).replaceAll($button); + var parts = ['|', '/', '-', '\\']; + var current = 0; + var spinning = setInterval(function () { + $spinner.text(parts[current]); + current++; + if (current === parts.length) current = 0; + }, 100); + + var $counter = $container.find('.placeholder'); + var build = function () { + $.post(notCached.url, function (left) { + if (left === '0') { + clearInterval(spinning); + $container.remove(); + location.reload(); + } + else { + var counter = $counter.text().split(' ', 2); + $counter.text(left + ' ' + counter[1]); + build(); + } + }); + }; + build(); + }); + + return $container; + }; })(H5P.jQuery); \ No newline at end of file diff --git a/js/h5p.js b/js/h5p.js index fc65d9b..3b5db69 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -1,3 +1,4 @@ +// TODO: Should we split up the generic parts needed by the editor(and others), and the parts needed to "run" H5Ps? var H5P = H5P || {}; // Determine if we're inside an iframe. @@ -12,9 +13,14 @@ if (document.documentElement.requestFullScreen) { } else if (document.documentElement.webkitRequestFullScreen && navigator.userAgent.indexOf('Android') === -1 // Skip Android - && navigator.userAgent.indexOf('Version/') === -1 // Skip Safari ) { - H5P.fullScreenBrowserPrefix = 'webkit'; + H5P.safariBrowser = navigator.userAgent.match(/Version\/(\d)/); + H5P.safariBrowser = (H5P.safariBrowser === null ? 0 : parseInt(H5P.safariBrowser[1])); + + // Do not allow fullscreen for safari < 7. + if (H5P.safariBrowser === 0 || H5P.safariBrowser > 6) { + H5P.fullScreenBrowserPrefix = 'webkit'; + } } else if (document.documentElement.mozRequestFullScreen) { H5P.fullScreenBrowserPrefix = 'moz'; @@ -29,13 +35,13 @@ else if (document.documentElement.msRequestFullscreen) { */ H5P.init = function () { // Useful jQuery object. - H5P.$body = H5P.jQuery('body'); + H5P.$body = H5P.jQuery(document.body); // Prepare internal resizer for content. - var $window = H5P.jQuery(window.top); + var $window = H5P.jQuery(window.parent); // H5Ps added in normal DIV. - var $containers = H5P.jQuery(".h5p-content").each(function () { + var $containers = H5P.jQuery(".h5p-content").each(function () { var $element = H5P.jQuery(this); var $container = H5P.jQuery('
').appendTo($element); var contentId = $element.data('content-id'); @@ -45,7 +51,7 @@ H5P.init = function () { } var library = { library: contentData.library, - params: H5P.jQuery.parseJSON(contentData.jsonContent) + params: JSON.parse(contentData.jsonContent) }; // Create new instance. @@ -59,10 +65,10 @@ H5P.init = function () { }; var $actions = H5P.jQuery('
    '); - if (contentData.export !== '') { + if (contentData.exportUrl !== '') { // Display export button H5P.jQuery('
  • ' + H5P.t('download') + '
  • ').appendTo($actions).click(function () { - window.location.href = contentData.export; + window.location.href = contentData.exportUrl; }); } if (instance.getCopyrights !== undefined) { @@ -86,7 +92,7 @@ H5P.init = function () { // Make it possible to resize the iframe when the content changes size. This way we get no scrollbars. var iframe = window.parent.document.getElementById('h5p-iframe-' + contentId); var resizeIframe = function () { - if (window.top.H5P.isFullscreen) { + if (window.parent.H5P.isFullscreen) { return; // Skip if full screen. } @@ -104,9 +110,11 @@ H5P.init = function () { iframe.parentElement.style.height = parentHeight; }; + var resizeDelay; instance.$.on('resize', function () { - // Use timeout to make sure iframe is resized to the correct size. - setTimeout(function () { + // Use a delay to make sure iframe is resized to the correct size. + clearTimeout(resizeDelay); + resizeDelay = setTimeout(function () { resizeIframe(); }, 1); }); @@ -114,11 +122,9 @@ H5P.init = function () { // Resize everything when window is resized. $window.resize(function () { - if (window.top.H5P.isFullscreen) { + if (window.parent.H5P.isFullscreen) { // Use timeout to avoid bug in certain browsers when exiting fullscreen. Some browser will trigger resize before the fullscreenchange event. - setTimeout(function () { instance.$.trigger('resize'); - }, 1); } else { instance.$.trigger('resize'); @@ -170,19 +176,43 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) { $classes = $element.add(H5P.$body).add($classes); - var done = function (c) { - H5P.isFullscreen = false; - $classes.removeClass(c); + /** + * Prepare for resize by setting the correct styles. + * + * @param {String} classes CSS + */ + var before = function (classes) { + $classes.addClass(classes); - if (H5P.fullScreenBrowserPrefix === undefined) { - // Resize content. - if (instance.$ !== undefined) { - instance.$.trigger('resize'); - } - else if (instance.resize !== undefined) { - instance.resize(); - } + if ($iframe !== undefined) { + // Set iframe to its default size(100%). + $iframe.css('height', ''); } + }; + + /** + * Gets called when fullscreen mode has been entered. + * Resizes and sets focus on content. + */ + var entered = function () { + // Do not rely on window resize events. + instance.$.trigger('resize'); + instance.$.trigger('focus'); + }; + + /** + * Gets called when fullscreen mode has been exited. + * Resizes and sets focus on content. + * + * @param {String} classes CSS + */ + var done = function (classes) { + H5P.isFullscreen = false; + $classes.removeClass(classes); + + // Do not rely on window resize events. + instance.$.trigger('resize'); + instance.$.trigger('focus'); if (exitCallback !== undefined) { exitCallback(); @@ -193,13 +223,12 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) { if (H5P.fullScreenBrowserPrefix === undefined) { // Create semi fullscreen. - $classes.addClass('h5p-semi-fullscreen'); - var $disable = $container.prepend('').children(':first'); + before('h5p-semi-fullscreen'); + var $disable = H5P.jQuery('
    ').appendTo($container.find('.h5p-content-controls')); var keyup, disableSemiFullscreen = function () { $disable.remove(); $body.unbind('keyup', keyup); done('h5p-semi-fullscreen'); - return false; }; keyup = function (event) { if (event.keyCode === 27) { @@ -208,16 +237,22 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) { }; $disable.click(disableSemiFullscreen); $body.keyup(keyup); + entered(); } else { // Create real fullscreen. + before('h5p-fullscreen'); var first, eventName = (H5P.fullScreenBrowserPrefix === 'ms' ? 'MSFullscreenChange' : H5P.fullScreenBrowserPrefix + 'fullscreenchange'); document.addEventListener(eventName, function () { if (first === undefined) { + // We are entering fullscreen mode first = false; + entered(); return; } + + // We are exiting fullscreen done('h5p-fullscreen'); document.removeEventListener(eventName, arguments.callee, false); }); @@ -227,31 +262,9 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) { } else { var method = (H5P.fullScreenBrowserPrefix === 'ms' ? 'msRequestFullscreen' : H5P.fullScreenBrowserPrefix + 'RequestFullScreen'); - var params = (H5P.fullScreenBrowserPrefix === 'webkit' ? Element.ALLOW_KEYBOARD_INPUT : undefined); + var params = (H5P.fullScreenBrowserPrefix === 'webkit' && H5P.safariBrowser === 0 ? Element.ALLOW_KEYBOARD_INPUT : undefined); $element[0][method](params); } - - $classes.addClass('h5p-fullscreen'); - } - - 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(); } }; @@ -266,11 +279,29 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) { * Id of the content requesting a path */ H5P.getPath = function (path, contentId) { - if (path.substr(0, 7) === 'http://' || path.substr(0, 8) === 'https://') { + var hasProtocol = function (path) { + return path.match(/^[a-z0-9]+:\/\//i); + }; + + if (hasProtocol(path)) { return path; } - return H5PIntegration.getContentPath(contentId) + path; + if (contentId !== undefined) { + prefix = H5PIntegration.getContentPath(contentId); + } + else if (window['H5PEditor'] !== undefined) { + prefix = H5PEditor.filesPath; + } + else { + return; + } + + if (!hasProtocol(prefix)) { + prefix = window.parent.location.protocol + "//" + window.parent.location.host + prefix; + } + + return prefix + '/' + path; }; /** @@ -874,6 +905,8 @@ H5P.cloneObject = function (object, recursive) { /** * Remove all empty spaces before and after the value. * TODO: Only include this or String.trim(). What is best? + * I'm leaning towards implementing the missing ones: http://kangax.github.io/compat-table/es5/ + * So should we make this function deprecated? * * @param {String} value * @returns {@exp;value@call;replace} @@ -889,13 +922,8 @@ H5P.trim = function (value) { * @returns {Boolean} */ H5P.jsLoaded = function (path) { - for (var i = 0; i < H5P.loadedJs.length; i++) { - if (H5P.loadedJs[i] === path) { - return true; - } - } - - return false; + H5P.loadedJs = H5P.loadedJs || []; + return H5P.jQuery.inArray(path, H5P.loadedJs) !== -1; }; /** @@ -905,13 +933,8 @@ H5P.jsLoaded = function (path) { * @returns {Boolean} */ H5P.cssLoaded = function (path) { - for (var i = 0; i < H5P.loadedCss.length; i++) { - if (H5P.loadedCss[i] === path) { - return true; - } - } - - return false; + H5P.loadedCss = H5P.loadedCss || []; + return H5P.jQuery.inArray(path, H5P.loadedCss) !== -1; }; /** @@ -972,8 +995,8 @@ if (String.prototype.trim === undefined) { }; } -// Finally, we want to run init when document is ready. But not if we're -// in an iFrame. Then we wait for parent to start init(). +// Finally, we want to run init when document is ready. +// TODO: Move to integration. Systems like Moodle using YUI cannot get its translations set before this starts! if (H5P.jQuery) { H5P.jQuery(document).ready(function () { if (!H5P.initialized) { diff --git a/styles/h5p-admin.css b/styles/h5p-admin.css index 599afe0..44310b0 100644 --- a/styles/h5p-admin.css +++ b/styles/h5p-admin.css @@ -1,11 +1,4 @@ -/* 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; -} +/* Administration interface styling */ .h5p-content { border: 1px solid #DDD; @@ -36,35 +29,56 @@ text-align: right; } +.h5p-admin-buttons-wrapper { + white-space: nowrap; +} + .h5p-admin-table.libraries button { - font-family: H5PFontAwesome4; - font-size: 1.6em; - font-style: normal; - font-weight: normal; - margin-right: 10px; + font-size: 2em; 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; + padding: 0; + line-height: 1em; + width: 1.125em; + height: 1.05em; + text-indent: -0.125em; + margin: 0.125em 0.125em 0 0.125em; } -.h5p-admin-table.libraries button:hover, -.h5p-admin-table.libraries .h5p-admin-delete-library:hover { +.h5p-admin-upgrade-library:before { + font-family: 'H5P'; + content: "\e888"; +} +.h5p-admin-view-library:before { + font-family: 'H5P'; + content: "\e889"; +} +.h5p-admin-delete-library:before { + font-family: 'H5P'; + content: "\e890"; +} + +.h5p-admin-table.libraries button:hover { background-color: #d0d0d0; } - -.h5p-admin-table.libraries .h5p-admin-delete-library:disabled:hover { +.h5p-admin-table.libraries button:disabled:hover { background-color: #e0e0e0; + cursor: default; } -.h5p-admin-table.libraries .h5p-admin-delete-library { - color: #f9010f; +.h5p-admin-upgrade-library { + color: #339900; } -.h5p-admin-table.libraries .h5p-admin-delete-library:disabled { +.h5p-admin-view-library { + color: #0066cc; +} +.h5p-admin-delete-library { + color: #990000; +} +.h5p-admin-delete-library:disabled, +.h5p-admin-upgrade-library:disabled { cursor: default; color: #c0c0c0; } @@ -114,10 +128,11 @@ box-shadow: 2px 2px 5px #888888; } .h5p-content-search:before { - font-family: H5PFontAwesome4; - margin: 0 10px; - content: "\f002"; - font-size: 1.4em; + font-family: 'H5P'; + vertical-align: bottom; + content: "\e88a"; + font-size: 2em; + line-height: 1.25em; } .h5p-content-search input { font-size: 120%; @@ -187,7 +202,7 @@ button.h5p-admin.disabled:hover { line-height: 130%; border: none; background: none; - font-family: H5PFontAwesome4; + font-family: 'H5P'; /* TODO: Find content */ font-size: 1.4em; } .h5p-content-pager > button:focus { @@ -225,3 +240,8 @@ button.h5p-admin.disabled:hover { top: 0; right: 0; } +.h5p-spinner { + padding: 0 0.5em; + font-size: 1.5em; + font-weight: bold; +} \ No newline at end of file diff --git a/styles/h5p.css b/styles/h5p.css index 673354c..c9099c6 100644 --- a/styles/h5p.css +++ b/styles/h5p.css @@ -1,25 +1,19 @@ -/* 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; -} +/* General CSS for H5P. Licensed under the MIT License.*/ +/* Custom H5P font to use for icons. */ @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'); + src: url('../fonts/h5p.eot?ver=1.2.1'); + src: url('../fonts/h5p.eot?#iefix&ver=1.2.1') format('embedded-opentype'), + url('data:application/font-woff;base64,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') format('woff'), + url('../fonts/h5p.ttf?ver=1.2.1') format('truetype'), + url('../fonts/h5p.svg?ver=1.2.1#h5pregular') format('svg'); font-weight: normal; font-style: normal; } html.h5p-iframe, html.h5p-iframe > body { - font-family: Arial, Helvetica, sans-serif; + font-family: Sans-Serif; /* Use the browser's default sans-serif font. (Since Heletica doesn't look nice on Windows, and Arial on OS X.) */ width: 100%; height: 100%; margin: 0; @@ -71,45 +65,35 @@ html.h5p-iframe .h5p-content { color: #e5eef6; } -.h5p-enable-fullscreen:before, .h5p-disable-fullscreen:before { - font-family: H5PFontAwesome4; - - font-size: 25px; -} .h5p-enable-fullscreen:before { - content: "\f065"; + font-family: 'H5P'; + content: "\e88c"; } .h5p-disable-fullscreen:before { - content: "\f066"; + font-family: 'H5P'; + content: "\e891"; } - .h5p-enable-fullscreen, .h5p-disable-fullscreen { - display: inline-block; - line-height: 25px; - width: 25px; - height: 25px; - padding: 6px 6px; cursor: pointer; color: #EEE; background: rgb(0,0,0); background: rgba(0,0,0,0.3); - text-decoration: none; - text-align: center; + line-height: 0.975em; + font-size: 2em; + width: 1.125em; + height: 0.925em; + text-indent: -0.0875em; + outline: none; } .h5p-disable-fullscreen { - position: absolute; - right: 10px; - top: 10px; - z-index: 201; - background: rgb(0,0,0); - background: rgba(0,0,0,0.7); + line-height: 0.925em; + width: 1.1em; + height: 0.9em; } .h5p-enable-fullscreen:hover, .h5p-disable-fullscreen:hover { - text-decoration: none; background: rgba(0,0,0,0.5); } - -.h5p-semi-fullscreen .h5p-content-controls { +.h5p-semi-fullscreen .h5p-enable-fullscreen { display: none; } @@ -151,35 +135,6 @@ div.h5p-fullscreen { border: 0; display: block; } -.h5p-iframe-wrapper.h5p-semi-fullscreen .buttons button:before { - content: 'Exit '; -} -.h5p-iframe-wrapper button.fullscreen { - color: #e5eef6; - text-decoration: none; - padding: 6px 12px; - background: #539ad7; - background-image: -webkit-linear-gradient(top,#4584ba,#539ad7); - background-image: -moz-linear-gradient(top,#4584ba,#539ad7); - background-image: -o-linear-gradient(top,#4584ba,#539ad7); - background-image: -ms-linear-gradient(top,#4584ba,#539ad7); - background-image: linear-gradient(to bottom,#4584ba,#539ad7); - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4584ba',endColorstr='#539ad7',GradientType=0); - box-shadow: 0 0 8px #bebebe; - border: none; - opacity: 0.8; -} -.h5p-iframe-wrapper button.fullscreen:hover { - opacity: 1; - background: #539ad7; - background-image: -webkit-linear-gradient(top,#539ad7,#4584ba); - background-image: -moz-linear-gradient(top,#539ad7,#4584ba); - background-image: -o-linear-gradient(top,#539ad7,#4584ba); - background-image: -ms-linear-gradient(top,#539ad7,#4584ba); - background-image: linear-gradient(to bottom,#539ad7,#4584ba); - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#539ad7',endColorstr='#4584ba',GradientType=0); - cursor: pointer; -} .h5p-content ul.h5p-actions { box-sizing: border-box; @@ -194,7 +149,7 @@ div.h5p-fullscreen { border-top: 1px solid #EEE; border-bottom: 1px solid #EEE; clear: both; - font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; + font-family: Sans-Serif; } .h5p-fullscreen .h5p-actions, .h5p-semi-fullscreen .h5p-actions { display: none; @@ -214,35 +169,38 @@ div.h5p-fullscreen { color: #666; } .h5p-actions > .h5p-button:before { - font-family: H5PFontAwesome4; + font-family: 'H5P'; font-size: 1em; - padding-right: 0.3em; + padding-right: 0; + font-size: 1.7em; + line-height: 1.125em; + vertical-align: middle; } .h5p-actions > .h5p-button.h5p-export:before { - content: "\f019"; + content: "\e893"; } .h5p-actions > .h5p-button.h5p-copyrights:before { - font-family: H5P; - content: "\e668"; - font-size: 1.7em; - padding-right: 0; - vertical-align: bottom; + content: "\e88f"; } .h5p-actions > .h5p-button.h5p-embed:before { - content: "\f121"; + content: "\e892"; } .h5p-actions .h5p-link { float: right; margin-right: 0; font-size: 2.0em; + line-height: 1; overflow: hidden; color: #999; text-decoration: none; outline: none; } .h5p-actions .h5p-link:before { - font-family: H5P; - content: "\e667"; + font-family: 'H5P'; + content: "\e88e"; +} +.h5p-actions > li { + margin: 0; } .h5p-popup-dialog { position: absolute; @@ -254,7 +212,6 @@ div.h5p-fullscreen { 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; @@ -315,8 +272,8 @@ div.h5p-fullscreen { outline:none } .h5p-popup-dialog .h5p-close:after { - font-family: H5PFontAwesome4; - content: "\f00d"; + font-family: 'H5P'; + content: "\e894"; font-size: 2em; position: absolute; right: 0.5em; @@ -360,3 +317,9 @@ div.h5p-fullscreen { margin-left: 0.25em; padding-left: 0.25em; } +.h5p-throbber { + background: url('../images/throbber.gif?ver=1.2.1') 10px center no-repeat; + padding-left: 38px; + min-height: 30px; + line-height: 30px; +} \ No newline at end of file