From 7cb2ebe17cf17831fd6cb386918dd091f765d2f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A5l=20J=C3=B8rgensen?= Date: Fri, 5 Sep 2014 14:12:46 +0200 Subject: [PATCH 01/24] Fixed bug in validation, which made library-upload on library admin page fail --- h5p.classes.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index 15ef52f..4a386a7 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -609,9 +609,10 @@ class H5PValidator { $missingLibraries = $this->getMissingLibraries($libraries); foreach ($missingLibraries as $missing) { if ($this->h5pF->getLibraryId($missing['machineName'], $missing['majorVersion'], $missing['minorVersion'])) { - unset($missingLibraries[$missing['machineName']]); + unset($missingLibraries[H5PCore::libraryToString($missing)]); } } + if (!empty($missingLibraries)) { foreach ($missingLibraries as $library) { $this->h5pF->setErrorMessage($this->h5pF->t('Missing required library @library', array('@library' => H5PCore::libraryToString($library)))); From f207c3be9b037ad770147a587341e1d122b1f466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A5l=20J=C3=B8rgensen?= Date: Tue, 23 Sep 2014 10:44:22 +0200 Subject: [PATCH 02/24] Setting language on export to 'und' if not defined --- h5p.classes.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index 4a386a7..51ffcec 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1251,7 +1251,9 @@ Class H5PExport { // Build h5p.json $h5pJson = array ( 'title' => $content['title'], - 'language' => $content['language'], + // TODO - stop using 'und', this is not the preferred way. + // Either remove language from the json if not existing, or use "language": null + 'language' => isset($content['language']) ? $content['language'] : 'und', 'mainLibrary' => $content['library']['name'], 'embedTypes' => $embedTypes, ); From 38c98202f48ac371d1c7ca5c81a810bd2221c44f Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Tue, 23 Sep 2014 11:35:21 +0200 Subject: [PATCH 03/24] Added info message when there's no installed libraries. --- js/h5p-library-list.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/js/h5p-library-list.js b/js/h5p-library-list.js index a18eb93..0332216 100644 --- a/js/h5p-library-list.js +++ b/js/h5p-library-list.js @@ -23,9 +23,9 @@ var H5PLibraryList= H5PLibraryList || {}; * @param {object} libraries List of libraries and headers */ H5PLibraryList.createLibraryList = function (libraries) { - + var t = H5PIntegration.i18n.H5P; if(libraries.listData === undefined || libraries.listData.length === 0) { - return; + return $('
' + t.NA + '
'); } // Create table @@ -33,7 +33,6 @@ var H5PLibraryList= H5PLibraryList || {}; $table.addClass('libraries'); // Add libraries - var t = H5PIntegration.i18n.H5P; $.each (libraries.listData, function (index, library) { var $libraryRow = H5PUtils.createTableRow([ library.title, From 212f8c6f0fa62f4ee99fcfa755cfa588391c4c31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A5l=20J=C3=B8rgensen?= Date: Tue, 23 Sep 2014 12:00:21 +0200 Subject: [PATCH 04/24] Added H5P.htmlspecialchars() --- js/h5p.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/js/h5p.js b/js/h5p.js index 3b5db69..0500cc9 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -975,6 +975,13 @@ H5P.setFinished = function (contentId, points, maxPoints) { } }; +/** + * Mimics how php's htmlspecialchars works (the way we use it) + */ +H5P.htmlspecialchars = function(string) { + return string.toString().replace(//g, '>').replace(/'/g, ''').replace(/"/g, '"'); +}; + // Add indexOf to browsers that lack them. (IEs) if (!Array.prototype.indexOf) { Array.prototype.indexOf = function (needle) { From 24be95a78ef29bf8c7adee3fb3669e28aa3561fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A5l=20J=C3=B8rgensen?= Date: Tue, 23 Sep 2014 12:59:08 +0200 Subject: [PATCH 05/24] Bumped core version because of the new function added --- h5p.classes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index 4a386a7..ae8116f 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1344,7 +1344,7 @@ class H5PCore { public static $coreApi = array( 'majorVersion' => 1, - 'minorVersion' => 3 + 'minorVersion' => 4 ); public static $styles = array( 'styles/h5p.css', From 7e669884cee4e295b84e99941111ab498b7c12e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A5l=20J=C3=B8rgensen?= Date: Tue, 23 Sep 2014 13:01:39 +0200 Subject: [PATCH 06/24] Correct capitalization of H5P.htmlspecialchars function --- js/h5p.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/h5p.js b/js/h5p.js index 0500cc9..73f833a 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -978,7 +978,7 @@ H5P.setFinished = function (contentId, points, maxPoints) { /** * Mimics how php's htmlspecialchars works (the way we use it) */ -H5P.htmlspecialchars = function(string) { +H5P.htmlSpecialChars = function(string) { return string.toString().replace(//g, '>').replace(/'/g, ''').replace(/"/g, '"'); }; From 7fd333088b6bc44f8e158799aed512d6ea22f471 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Tue, 23 Sep 2014 15:10:23 +0200 Subject: [PATCH 07/24] Added missing translation. --- h5p.classes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index 4a386a7..f05382e 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1147,7 +1147,7 @@ class H5PStorage { if ($upgradeOnly) { // TODO - support translation - $this->h5pF->setInfoMessage($upgradedLibsCount . ' libraries were upgraded!'); + $this->h5pF->setInfoMessage($this->h5pF->t('@num libraries were upgraded!', array('@num' => $upgradedLibsCount))); } return $library_saved; From 1236eb5fd11f344d15fd6aa0c80bc97ab314e1f1 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Wed, 24 Sep 2014 15:34:34 +0200 Subject: [PATCH 08/24] Revert "Merge branch 'adding-htmlspecialchars-to-h5p' into without-pal" This reverts commit 687fd8ddbc256c1e8c5f3714f561c06b3c31fee1, reversing changes made to ab1b691a27aadfc4b6a9798ca09bd2ff661f2f2f. --- js/h5p.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/h5p.js b/js/h5p.js index 73f833a..0500cc9 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -978,7 +978,7 @@ H5P.setFinished = function (contentId, points, maxPoints) { /** * Mimics how php's htmlspecialchars works (the way we use it) */ -H5P.htmlSpecialChars = function(string) { +H5P.htmlspecialchars = function(string) { return string.toString().replace(//g, '>').replace(/'/g, ''').replace(/"/g, '"'); }; From 4b8804988eef20ade122a2b3b97ffc423c790198 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Wed, 24 Sep 2014 15:34:55 +0200 Subject: [PATCH 09/24] Revert "Merge branch 'adding-htmlspecialchars-to-h5p' into without-pal" This reverts commit ab1b691a27aadfc4b6a9798ca09bd2ff661f2f2f, reversing changes made to 2b12bf4441b92f6bb3f7985c7fcb36be9875d177. --- h5p.classes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index 83874f8..3cc31ac 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1346,7 +1346,7 @@ class H5PCore { public static $coreApi = array( 'majorVersion' => 1, - 'minorVersion' => 4 + 'minorVersion' => 3 ); public static $styles = array( 'styles/h5p.css', From 3442954971184ae51e3a26026f6289d41d268720 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Wed, 24 Sep 2014 15:35:47 +0200 Subject: [PATCH 10/24] Revert "Merge branch 'adding-htmlspecialchars-to-h5p' into without-pal" This reverts commit 9f19f64baba068aee25ec2894017eb5f5e2fd733, reversing changes made to 38c98202f48ac371d1c7ca5c81a810bd2221c44f. --- js/h5p.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/js/h5p.js b/js/h5p.js index 0500cc9..3b5db69 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -975,13 +975,6 @@ H5P.setFinished = function (contentId, points, maxPoints) { } }; -/** - * Mimics how php's htmlspecialchars works (the way we use it) - */ -H5P.htmlspecialchars = function(string) { - return string.toString().replace(//g, '>').replace(/'/g, ''').replace(/"/g, '"'); -}; - // Add indexOf to browsers that lack them. (IEs) if (!Array.prototype.indexOf) { Array.prototype.indexOf = function (needle) { From d359ce52ca0640596440c6121d47b5678d403108 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Wed, 24 Sep 2014 16:24:56 +0200 Subject: [PATCH 11/24] Help make libraries admin easier. --- js/h5p-library-list.js | 15 ++++++++++++--- js/h5p-utils.js | 10 ++++++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/js/h5p-library-list.js b/js/h5p-library-list.js index 0332216..8327744 100644 --- a/js/h5p-library-list.js +++ b/js/h5p-library-list.js @@ -36,9 +36,18 @@ var H5PLibraryList= H5PLibraryList || {}; $.each (libraries.listData, function (index, library) { var $libraryRow = H5PUtils.createTableRow([ library.title, - library.numContent, - library.numContentDependencies, - library.numLibraryDependencies, + { + text: library.numContent, + class: 'h5p-admin-center' + }, + { + text: library.numContentDependencies, + class: 'h5p-admin-center' + }, + { + text: library.numLibraryDependencies, + class: 'h5p-admin-center' + }, '
\ \ \ diff --git a/js/h5p-utils.js b/js/h5p-utils.js index 3004c95..26ffa1d 100644 --- a/js/h5p-utils.js +++ b/js/h5p-utils.js @@ -16,7 +16,7 @@ var H5PUtils = H5PUtils || {}; $.each(headers, function (index, value) { if (!(value instanceof Object)) { value = { - text: value + html: value }; } @@ -38,7 +38,13 @@ var H5PUtils = H5PUtils || {}; var $tr = $(''); $.each(rows, function (index, value) { - $tr.append('' + value + ''); + if (!(value instanceof Object)) { + value = { + html: value + }; + } + + $('', value).appendTo($tr); }); return $tr; From 53ceb6156b3a8c6df421903352f4fd9223e22167 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Thu, 25 Sep 2014 14:38:05 +0200 Subject: [PATCH 12/24] Improved content check, supporting blank fields. --- js/h5p-library-list.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/js/h5p-library-list.js b/js/h5p-library-list.js index 8327744..a1e517f 100644 --- a/js/h5p-library-list.js +++ b/js/h5p-library-list.js @@ -55,10 +55,11 @@ var H5PLibraryList= H5PLibraryList || {};
' ]); + var hasContent = !(library.numContent === '' || library.numContent === 0); if (library.upgradeUrl === null) { $('.h5p-admin-upgrade-library', $libraryRow).remove(); } - else if (library.upgradeUrl === false || library.numContent === 0) { + else if (library.upgradeUrl === false || !hasContent) { $('.h5p-admin-upgrade-library', $libraryRow).attr('disabled', true); } else { @@ -73,7 +74,7 @@ var H5PLibraryList= H5PLibraryList || {}; }); var $deleteButton = $('.h5p-admin-delete-library', $libraryRow); - if (libraries.notCached !== undefined || library.numContent !== 0 || (library.numContentDependencies !== '' && library.numContentDependencies !== 0) || (library.numLibraryDependencies !== '' && library.numLibraryDependencies !== 0)) { + if (libraries.notCached !== undefined || hasContent || (library.numContentDependencies !== '' && library.numContentDependencies !== 0) || (library.numLibraryDependencies !== '' && library.numLibraryDependencies !== 0)) { // Disabled delete if content. $deleteButton.attr('disabled', true); } From 39ea23b8616e7d38ed972d4e7ad9c5cefc689b63 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Fri, 26 Sep 2014 13:29:07 +0200 Subject: [PATCH 13/24] Text align --- styles/h5p-admin.css | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/styles/h5p-admin.css b/styles/h5p-admin.css index 44310b0..219309a 100644 --- a/styles/h5p-admin.css +++ b/styles/h5p-admin.css @@ -244,4 +244,7 @@ button.h5p-admin.disabled:hover { padding: 0 0.5em; font-size: 1.5em; font-weight: bold; -} \ No newline at end of file +} +#h5p-admin-container .h5p-admin-center { + text-align: center; +} From a38a0f1253e646a05bfe3835ebbf36ebecc8bf67 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Fri, 26 Sep 2014 15:56:42 +0200 Subject: [PATCH 14/24] Removed old caching system. Use content variable for filtered parameters. Added getOption/setOption. --- h5p.classes.php | 373 ++++++++++++++++++++++++------------------------ 1 file changed, 183 insertions(+), 190 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 3cc31ac..b246771 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -55,31 +55,31 @@ 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(); - + /** * Saving the unsupported library list - * + * * @param array A list of unsupported libraries */ public function setUnsupportedLibraries($libraries); - + /** * Returns unsupported libraries - * + * * @return array A list of the unsupported libraries */ public function getUnsupportedLibraries(); - + /** * Returns the URL to the library admin page - * + * * @return string URL to admin page */ public function getAdminUrl(); @@ -108,7 +108,7 @@ interface H5PFrameworkInterface { * @param string $defaultLibraryWhitelist */ public function getWhitelist($isLibrary, $defaultContentWhitelist, $defaultLibraryWhitelist); - + /** * Is the library a patched version of an existing library? * @@ -147,23 +147,23 @@ interface H5PFrameworkInterface { * Object holding the information that is to be stored */ public function saveLibraryData(&$libraryData, $new = TRUE); - + /** * Insert new content. - * + * * @param object $content * @param int $contentMainId */ 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 * @@ -219,11 +219,11 @@ interface H5PFrameworkInterface { public function saveLibraryUsage($contentId, $librariesInUse); /** - * Get number of content/nodes using a library, and the number of + * 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'. + * @return array The array contains two elements, keyed by 'content' and 'libraries'. * Each element contains a number */ public function getLibraryUsage($libraryId); @@ -249,7 +249,7 @@ interface H5PFrameworkInterface { * @return string semantics. */ public function loadLibrarySemantics($name, $majorVersion, $minorVersion); - + /** * Makes it possible to alter the semantics, adding custom fields, etc. * @@ -267,21 +267,21 @@ interface H5PFrameworkInterface { * Library Id */ public function deleteLibraryDependencies($libraryId); - + /** * Delete a library from database and file system - * + * * @param mixed $library Library */ public function deleteLibrary($library); - + /** * Load content. * * @return object Content, null if not found. */ public function loadContent($id); - + /** * Load dependencies for the given content of the given type. * @@ -290,49 +290,55 @@ interface H5PFrameworkInterface { * @return array */ public function loadContentDependencies($id, $type = NULL); - + /** - * Get data from cache. - * - * @param string $group - * @param string $key + * Get stored setting. + * + * @param string $name Identifier + * @param string $default Optional + * @return mixed data */ - public function cacheGet($group, $key); - + public function getOption($name, $default = NULL); + /** - * Store data in cache. - * - * @param string $group - * @param string $key - * @param mixed $data + * Stores the given setting. + * For example when did we last check h5p.org for updates to our libraries. + * + * @param string $name Identifier + * @param mixed $value Data */ - public function cacheSet($group, $key, $data); - + public function setOption($name, $value); + /** - * Delete data from cache. - * - * @param string $group - * @param string $key + * This will set the filtered parameters for the given content. + * + * @param int $content_id + * @param string $parameters filtered */ - public function cacheDel($group, $key = NULL); - + public function setFilteredParameters($content_id, $parameters = ''); + /** - * 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. - * + * Will clear filtered params for all the content that uses the specified + * library. This means that the content dependencies will have to be rebuilt, + * and the parameters refiltered. + * * @param int $library_id */ - public function invalidateContentCache($library_id); - + public function clearFilteredParameters($library_id); + /** - * Get content without cache. + * Get number of contents that has to get their content dependencies rebuilt + * and parameters refiltered. + * + * @return int */ - public function getNotCached(); - + public function getNumNotFiltered(); + /** * Get number of contents using library as main library. - * + * * @param int $library_id + * @return int */ public function getNumContent($library_id); } @@ -447,14 +453,14 @@ class H5PValidator { // Extract and then remove the package file. $zip = new ZipArchive; - + // Only allow files with the .h5p extension: if (strtolower(substr($tmpPath, -3)) !== 'h5p') { $this->h5pF->setErrorMessage($this->h5pF->t('The file you uploaded is not a valid HTML5 Package (It does not have the .h5p file extension)')); H5PCore::deleteFileTree($tmpDir); return; } - + if ($zip->open($tmpPath) === true) { $zip->extractTo($tmpDir); $zip->close(); @@ -482,7 +488,7 @@ class H5PValidator { if ($skipContent === TRUE) { continue; } - + $mainH5pData = $this->getJsonData($filePath); if ($mainH5pData === FALSE) { $valid = FALSE; @@ -540,7 +546,7 @@ class H5PValidator { } $libraryH5PData = $this->getLibraryData($file, $filePath, $tmpDir); - + if ($libraryH5PData !== FALSE) { // Library's directory name must be: // - @@ -576,7 +582,7 @@ class H5PValidator { } if ($valid) { if ($upgradeOnly) { - // When upgrading, we opnly add allready installed libraries, + // When upgrading, we opnly add allready installed libraries, // and new dependent libraries $upgrades = array(); foreach ($libraries as &$library) { @@ -594,25 +600,25 @@ class H5PValidator { } } } - + $libraries = $upgrades; } - + $this->h5pC->librariesJsonData = $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'])) { unset($missingLibraries[H5PCore::libraryToString($missing)]); } } - + if (!empty($missingLibraries)) { foreach ($missingLibraries as $library) { $this->h5pF->setErrorMessage($this->h5pF->t('Missing required library @library', array('@library' => H5PCore::libraryToString($library)))); @@ -1021,7 +1027,7 @@ class H5PStorage { public $h5pF; public $h5pC; - + public $contentId = NULL; // Quick fix so WP can get ID of new content. /** @@ -1052,11 +1058,11 @@ class H5PStorage { $library_saved = FALSE; $upgradedLibsCount = 0; $mayUpdateLibraries = $this->h5pF->mayUpdateLibraries(); - + foreach ($this->h5pC->librariesJsonData as &$library) { $libraryId = $this->h5pF->getLibraryId($library['machineName'], $library['majorVersion'], $library['minorVersion']); $library['saveDependencies'] = TRUE; - + if (!$libraryId) { $new = TRUE; } @@ -1101,21 +1107,21 @@ 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']); - + $this->h5pF->clearFilteredParameters($library['libraryId']); + $upgradedLibsCount++; } } - + if (!$skipContent) { $current_path = $this->h5pF->getUploadedH5pFolderPath() . DIRECTORY_SEPARATOR . 'content'; - + // Find out which libraries are used by this package/content $librariesInUse = array(); $this->h5pC->findLibraryDependencies($librariesInUse, $this->h5pC->mainJsonData); - + // Save content if ($content === NULL) { $content = array(); @@ -1132,19 +1138,19 @@ class H5PStorage { if (!is_dir($contents_path)) { mkdir($contents_path, 0777, true); } - + // Move the content folder $destination_path = $contents_path . DIRECTORY_SEPARATOR . $contentId; @rename($current_path, $destination_path); - + // Save the content library dependencies $this->h5pF->saveLibraryUsage($contentId, $librariesInUse); H5PCore::deleteFileTree($this->h5pF->getUploadedH5pFolderPath()); } - + // Update supported library list if neccessary: $this->h5pC->validateLibrarySupport(TRUE); - + if ($upgradeOnly) { // TODO - support translation $this->h5pF->setInfoMessage($this->h5pF->t('@num libraries were upgraded!', array('@num' => $upgradedLibsCount))); @@ -1152,7 +1158,7 @@ class H5PStorage { return $library_saved; } - + /** * Delete an H5P package * @@ -1221,7 +1227,7 @@ Class H5PExport { $this->h5pF = $H5PFramework; $this->h5pC = $H5PCore; } - + /** * Return path to h5p package. * @@ -1234,7 +1240,7 @@ Class H5PExport { $h5pDir = $this->h5pF->getH5pPath() . DIRECTORY_SEPARATOR; $tempPath = $h5pDir . 'temp' . DIRECTORY_SEPARATOR . $content['id']; $zipPath = $h5pDir . 'exports' . DIRECTORY_SEPARATOR . $content['id'] . '.h5p'; - + // Temp dir to put the h5p files in @mkdir($tempPath, 0777, TRUE); @mkdir($h5pDir . 'exports', 0777, TRUE); @@ -1251,8 +1257,8 @@ Class H5PExport { // Build h5p.json $h5pJson = array ( 'title' => $content['title'], - // TODO - stop using 'und', this is not the preferred way. - // Either remove language from the json if not existing, or use "language": null + // TODO - stop using 'und', this is not the preferred way. + // Either remove language from the json if not existing, or use "language": null 'language' => isset($content['language']) ? $content['language'] : 'und', 'mainLibrary' => $content['library']['name'], 'embedTypes' => $embedTypes, @@ -1261,7 +1267,7 @@ Class H5PExport { // 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']; @@ -1269,7 +1275,7 @@ Class H5PExport { // Do not add editor dependencies to h5p json. if ($dependency['type'] === 'editor') { - continue; + continue; } $h5pJson[$dependency['type'] . 'Dependencies'][] = array( @@ -1333,7 +1339,7 @@ Class H5PExport { */ private function addEditorLibraries($libraries, $editorLibraries) { foreach ($editorLibraries as $editorLibrary) { - $libraries[$editorLibrary['machineName']] = $editorLibrary; + $libraries[$editorLibrary['machineName']] = $editorLibrary; } return $libraries; } @@ -1378,12 +1384,12 @@ class H5PCore { */ 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) { $this->h5pD = new H5PDevelopment($this->h5pF, $path, $language); } @@ -1391,7 +1397,7 @@ class H5PCore { /** * Save content and clear cache. - * + * * @param array $content * @return int Content ID */ @@ -1400,17 +1406,12 @@ class H5PCore { $this->h5pF->updateContent($content, $contentMainId); } else { - $content['id'] = $this->h5pF->insertContent($content, $contentMainId); + $content['id'] = $this->h5pF->insertContent($content, $contentMainId); } - - if (!isset($content['filtered'])) { - // TODO: Add filtered to all impl. and remove - $this->h5pF->cacheDel('parameters', $content['id']); - } - + return $content['id']; } - + /** * Load content. * @@ -1419,7 +1420,7 @@ class H5PCore { */ public function loadContent($id) { $content = $this->h5pF->loadContent($id); - + if ($content !== NULL) { $content['library'] = array( 'id' => $content['libraryId'], @@ -1430,7 +1431,7 @@ class H5PCore { 'fullscreen' => $content['libraryFullscreen'], ); unset($content['libraryId'], $content['libraryName'], $content['libraryEmbedTypes'], $content['libraryFullscreen']); - + // // TODO: Move to filterParameters? // if ($this->development_mode & H5PDevelopment::MODE_CONTENT) { // // TODO: Remove Drupal specific stuff @@ -1444,29 +1445,21 @@ class H5PCore { // } // } } - + 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) { - if (isset($content['filtered'])) { - $params = ($content['filtered'] === '' ? NULL : $content['filtered']); - } - else { - // TODO: Add filtered to all impl. and remove - $params = $this->h5pF->cacheGet('parameters', $content['id']); + if (isset($content['filtered']) && $content['filtered'] !== '') { + return $content['filtered']; } - if ($params !== NULL) { - return $params; - } - // Validate and filter against main library semantics. $validator = new H5PContentValidator($this->h5pF, $this); $params = (object) array( @@ -1476,25 +1469,25 @@ class H5PCore { $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? } // Cache. - $this->h5pF->cacheSet('parameters', $content['id'], $params); + $this->h5pF->setFilteredParameters($content['id'], $params); return $params; } - + /** * Find the files required for this content to work. * @@ -1503,10 +1496,10 @@ class H5PCore { */ 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])) { @@ -1515,13 +1508,13 @@ class H5PCore { } } } - + return $dependencies; } - + /** * Get all dependency assets of the given type - * + * * @param array $dependency * @param string $type * @param array $assets @@ -1531,12 +1524,12 @@ class H5PCore { if (empty($dependency[$type]) || $dependency[$type][0] === '') { return; } - + // Check if we should skip CSS. if ($type === 'preloadedCss' && (isset($dependency['dropCss']) && $dependency['dropCss'] === '1')) { return; } - + foreach ($dependency[$type] as $file) { $assets[] = (object) array( 'path' => $dependency['path'] . '/' . trim(is_array($file) ? $file['path'] : $file), @@ -1544,23 +1537,23 @@ class H5PCore { ); } } - + /** * Combines path with cache buster / version. - * + * * @param array $assets * @return array */ public function getAssetsUrls($assets) { $urls = array(); - + foreach ($assets as $asset) { $urls[] = $asset->path . $asset->version; } - + return $urls; } - + /** * Return file paths for all dependecies files. * @@ -1578,14 +1571,14 @@ class H5PCore { $dependency['preloadedJs'] = explode(',', $dependency['preloadedJs']); $dependency['preloadedCss'] = explode(',', $dependency['preloadedCss']); } - + $dependency['version'] = "?ver={$dependency['majorVersion']}.{$dependency['minorVersion']}.{$dependency['patchVersion']}"; $this->getDependencyAssets($dependency, 'preloadedJs', $files['scripts']); $this->getDependencyAssets($dependency, 'preloadedCss', $files['styles']); } return $files; } - + /** * Load library semantics. * @@ -1597,20 +1590,20 @@ class H5PCore { // 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. * @@ -1625,31 +1618,31 @@ class H5PCore { $library['semantics'] = $this->h5pD->getSemantics($name, $majorVersion, $minorVersion); } } - + if ($library === NULL) { // Try to load from DB. $library = $this->h5pF->loadLibrary($name, $majorVersion, $minorVersion); } - + return $library; } - + /** * Deletes a library - * + * * @param unknown $libraryId */ public function deleteLibrary($libraryId) { $this->h5pF->deleteLibrary($libraryId); - + // Force update of unsupported libraries list: $this->validateLibrarySupport(TRUE); } - + /** - * Recursive. Goes through the dependency tree for the given library and + * Recursive. 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. @@ -1660,18 +1653,18 @@ class H5PCore { if (!isset($library[$property])) { 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']); if ($dependencyLibrary) { $dependencies[$dependencyKey] = array( @@ -1800,7 +1793,7 @@ class H5PCore { } return FALSE; } - + /** * Determine the correct embed type to use. * @@ -1809,32 +1802,32 @@ class H5PCore { 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; } - + /** * 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 @@ -1850,13 +1843,13 @@ class H5PCore { 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 @@ -1875,36 +1868,36 @@ class H5PCore { return $obj ? (object) $newArr : $newArr; } - + /** * Check if currently installed H5P libraries are supported by * the current versjon of core. Which versions of which libraries are supported is * defined in the library-support.json file. * - * @param boolean If TRUE, unsupported libraries list are rebuilt. If FALSE, list is + * @param boolean If TRUE, unsupported libraries list are rebuilt. If FALSE, list is * rebuilt only if non-existing */ public function validateLibrarySupport($force = false) { if (!($this->h5pF->getUnsupportedLibraries() === NULL || $force)) { return; } - + $minVersions = $this->getMinimumVersionsSupported(realpath(dirname(__FILE__)) . '/library-support.json'); if ($minVersions === NULL) { return; } - + // Get all libraries installed, check if any of them is not supported: $libraries = $this->h5pF->loadLibraries(); $unsupportedLibraries = array(); - + // Iterate over all installed libraries foreach ($libraries as $library_name => $versions) { if (!isset($minVersions[$library_name])) { continue; } $min = $minVersions[$library_name]; - + // For each version of this library, check if it is supported foreach ($versions as $library) { if (!$this->isLibraryVersionSupported($library, $min->versions)) { @@ -1920,23 +1913,23 @@ class H5PCore { ); } } - + $this->h5pF->setUnsupportedLibraries(empty($unsupportedLibraries) ? NULL : $unsupportedLibraries); } } - + /** * Returns a list of the minimum version of libraries that are supported. * This is needed because some old libraries are no longer supported by core. - * + * * TODO: Make it possible for the systems to cache this list between requests. - * + * * @param string $path to json file * @return array indexed using library names */ public function getMinimumVersionsSupported($path) { $minSupported = array(); - + // Get list of minimum version for libraries. Some old libraries are no longer supported. $libraries = file_get_contents($path); if ($libraries !== FALSE) { @@ -1950,13 +1943,13 @@ class H5PCore { } } } - + return empty($minSupported) ? NULL : $minSupported; } - + /** * Check if a specific version of a library is supported - * + * * @param object library * @param array An array containing versions * @return boolean TRUE if supported, otherwise FALSE @@ -1969,37 +1962,37 @@ class H5PCore { // --- minor is higher than any minimumversion for a given major // --- major and minor equals and patch is >= supported $major_supported |= ($library->major_version > $minimumVersion->major); - + if ($library->major_version == $minimumVersion->major) { $minor_supported |= ($library->minor_version > $minimumVersion->minor); } - + if ($library->major_version == $minimumVersion->major && $library->minor_version == $minimumVersion->minor) { $patch_supported |= ($library->patch_version >= $minimumVersion->patch); } } - + return ($patch_supported || $minor_supported || $major_supported); } - + /** - * Helper function for creating markup for the unsupported libraries list - * + * Helper function for creating markup for the unsupported libraries list + * * TODO: Make help text translatable - * + * * @return string Html * */ public function createMarkupForUnsupportedLibraryList($libraries) { $html = '
The following versions of H5P libraries are not supported anymore:
    '; - + foreach ($libraries as $library) { $downloadUrl = $library['downloadUrl']; $libraryName = $library['name']; $currentVersion = $library['currentVersion']['major'] . '.' . $library['currentVersion']['minor'] .'.' . $library['currentVersion']['patch']; $html .= "
  • $libraryName ($currentVersion)
  • "; } - + $html .= '

These libraries may cause problems on this site. See here for more info
'; return $html; } @@ -2038,24 +2031,24 @@ class H5PContentValidator { 'select' => 'validateSelect', 'library' => 'validateLibrary', ); - + // 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(); } /** * Get the flat dependecy tree. - * + * * @return array */ public function getDependencies() { return $this->dependencies; } - + /** * Validate given text value against text semantics. */ @@ -2280,19 +2273,19 @@ class H5PContentValidator { $validkeys = array_merge($validkeys, $semantics->extraAttributes); // TODO: Validate extraAttributes } $this->filterParams($file, $validkeys); - + if (isset($file->width)) { $file->width = intval($file->width); } - + if (isset($file->height)) { $file->height = intval($file->height); } - + if (isset($file->codecs)) { $file->codecs = htmlspecialchars($file->codecs, ENT_QUOTES, 'UTF-8', FALSE); } - + if (isset($file->quality)) { if (!is_object($file->quality) || !isset($file->quality->level) || !isset($file->quality->label)) { unset($file->quality); @@ -2303,7 +2296,7 @@ class H5PContentValidator { $file->quality->label = htmlspecialchars($file->quality->label, ENT_QUOTES, 'UTF-8', FALSE); } } - + if (isset($file->copyright)) { $this->validateGroup($file->copyright, H5PContentValidator::getCopyrightSemantics()); } @@ -2406,7 +2399,7 @@ class H5PContentValidator { $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']); @@ -2700,7 +2693,7 @@ class H5PContentValidator { /** * Processes an HTML attribute value and strips dangerous protocols from URLs. - * + * * @param $string * The string with the attribute value. * @param $decode @@ -2773,10 +2766,10 @@ class H5PContentValidator { return $uri; } - + public static function getCopyrightSemantics() { static $semantics; - + if ($semantics === NULL) { $semantics = (object) array( 'name' => 'copyright', @@ -2874,7 +2867,7 @@ class H5PContentValidator { ) ); } - + return $semantics; } } From 2011e856c04101073dfb9f3ab46cf8659012cd35 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Mon, 29 Sep 2014 17:13:47 +0200 Subject: [PATCH 15/24] Improved error handling when upgrading content. --- js/h5p-content-upgrade.js | 161 ++++++++++++++++++++------------------ 1 file changed, 86 insertions(+), 75 deletions(-) diff --git a/js/h5p-content-upgrade.js b/js/h5p-content-upgrade.js index bbea037..5be3428 100644 --- a/js/h5p-content-upgrade.js +++ b/js/h5p-content-upgrade.js @@ -2,18 +2,18 @@ var H5PUpgrades = H5PUpgrades || {}; (function ($) { var info, $container, librariesCache = {}; - + // Initialize $(document).ready(function () { // Get library info info = H5PIntegration.getLibraryInfo(); - + // Get and reset container $container = $('#h5p-admin-container').html('

' + info.message + '

'); - + // Make it possible to select version var $version = $(getVersionSelect(info.versions)).appendTo($container); - + // Add "go" button $(''); H5PLibraryDetails.$next = $(''); - + 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 = $(''); - + H5PLibraryDetails.$pager = $('
').append(H5PLibraryDetails.$previous, H5PLibraryDetails.$pagerInfo, H5PLibraryDetails.$next); H5PLibraryDetails.$content.append(H5PLibraryDetails.$pager); - + H5PLibraryDetails.$pagerInfo.on('click', function () { var width = H5PLibraryDetails.$pagerInfo.innerWidth(); H5PLibraryDetails.$pagerInfo.hide(); @@ -134,24 +134,24 @@ var H5PLibraryDetails= H5PLibraryDetails || {}; var pageNumerUpdated = function() { var newPageNum = $gotoInput.val()-1; var intRegex = /^\d+$/; - + $goto.remove(); H5PLibraryDetails.$pagerInfo.css({display: 'inline-block'}); - + // Check if input value is valid, and that it has actually changed if(!(intRegex.test(newPageNum) && newPageNum >= 0 && newPageNum < H5PLibraryDetails.getNumPages() && newPageNum != H5PLibraryDetails.currentPage)) { return; } - + H5PLibraryDetails.currentPage = newPageNum; H5PLibraryDetails.updatePager(); H5PLibraryDetails.createContentTable(); }; - + // We create an input box where the user may type in the page number // he wants to be displayed. // Reson for doing this is when user has ten-thousands of elements in list, - // this is the easiest way of getting to a specified page + // this is the easiest way of getting to a specified page var $gotoInput = $('', { type: 'number', min : 1, @@ -166,30 +166,30 @@ var H5PLibraryDetails= H5PLibraryDetails || {}; } } }).css({width: width}); - var $goto = $('', { + var $goto = $('', { 'class': 'h5p-pager-goto' }).css({width: width}).append($gotoInput).insertAfter(H5PLibraryDetails.$pagerInfo); - + $gotoInput.focus(); }); - + H5PLibraryDetails.updatePager(); } }; - + /** * Calculates number of pages */ H5PLibraryDetails.getNumPages = function () { return Math.ceil(H5PLibraryDetails.currentContent.length / H5PLibraryDetails.PAGER_SIZE); }; - + /** * Update the pager text, and enables/disables the next and previous buttons as needed */ H5PLibraryDetails.updatePager = function () { H5PLibraryDetails.$pagerInfo.css({display: 'inline-block'}); - + if(H5PLibraryDetails.getNumPages() > 0) { var message = H5PUtils.translateReplace(H5PLibraryDetails.library.translations.pageXOfY, { '$x': (H5PLibraryDetails.currentPage+1), @@ -200,26 +200,26 @@ var H5PLibraryDetails= H5PLibraryDetails || {}; else { H5PLibraryDetails.$pagerInfo.html(''); } - + H5PLibraryDetails.$previous.toggleClass('disabled', H5PLibraryDetails.currentPage <= 0); H5PLibraryDetails.$next.toggleClass('disabled', H5PLibraryDetails.currentContent.length < (H5PLibraryDetails.currentPage+1)*H5PLibraryDetails.PAGER_SIZE); }; - + /** * Creates the search element */ H5PLibraryDetails.createSearchElement = function () { - + H5PLibraryDetails.$search = $(''); - + var performSeach = function () { var searchString = $('.h5p-content-search > input').val(); - + // If search string same as previous, just do nothing if(H5PLibraryDetails.currentFilter === searchString) { return; } - + if (searchString.trim().length === 0) { // If empty search, use the complete list H5PLibraryDetails.currentContent = H5PLibraryDetails.library.content; @@ -230,7 +230,7 @@ var H5PLibraryDetails= H5PLibraryDetails || {}; } else { var listToFilter = H5PLibraryDetails.library.content; - + // Check if we can filter the already filtered results (for performance) if(searchString.length > 1 && H5PLibraryDetails.currentFilter === searchString.substr(0, H5PLibraryDetails.currentFilter.length)) { listToFilter = H5PLibraryDetails.currentContent; @@ -239,47 +239,47 @@ var H5PLibraryDetails= H5PLibraryDetails || {}; return content.title && content.title.match(new RegExp(searchString, 'i')); }); } - + H5PLibraryDetails.currentFilter = searchString; // Cache the current result H5PLibraryDetails.filterCache[searchString] = H5PLibraryDetails.currentContent; H5PLibraryDetails.currentPage = 0; H5PLibraryDetails.createContentTable(); - + // Display search results: if (H5PLibraryDetails.$searchResults) { H5PLibraryDetails.$searchResults.remove(); } if (searchString.trim().length > 0) { - H5PLibraryDetails.$searchResults = $('' + H5PLibraryDetails.currentContent.length + ' hits on ' + H5PLibraryDetails.currentFilter + ''); + H5PLibraryDetails.$searchResults = $('' + H5PLibraryDetails.currentContent.length + ' hits on ' + H5PLibraryDetails.currentFilter + ''); H5PLibraryDetails.$search.append(H5PLibraryDetails.$searchResults); } H5PLibraryDetails.updatePager(); }; - - var inputTimer = undefined; + + var inputTimer; $('input', H5PLibraryDetails.$search).on('change keypress paste input', function () { // Here we start the filtering // We wait at least 500 ms after last input to perform search if(inputTimer) { clearTimeout(inputTimer); } - + inputTimer = setTimeout( function () { performSeach(); }, 500); }); - + H5PLibraryDetails.$content.append(H5PLibraryDetails.$search); }; - + /** * Creates the page size selector */ H5PLibraryDetails.createPageSizeSelector = function () { H5PLibraryDetails.$search.append('
' + H5PLibraryDetails.library.translations.pageSizeSelectorLabel + ':102050100200
'); - - // Listen to clicks on the page size selector: + + // Listen to clicks on the page size selector: $('.h5p-admin-pager-size-selector > span', H5PLibraryDetails.$search).on('click', function () { H5PLibraryDetails.PAGER_SIZE = $(this).data('page-size'); $('.h5p-admin-pager-size-selector > span', H5PLibraryDetails.$search).removeClass('selected'); @@ -289,7 +289,7 @@ var H5PLibraryDetails= H5PLibraryDetails || {}; H5PLibraryDetails.updatePager(); }); }; - + // Initialize me: $(document).ready(function () { if (!H5PLibraryDetails.initialized) { @@ -297,5 +297,5 @@ var H5PLibraryDetails= H5PLibraryDetails || {}; H5PLibraryDetails.init(); } }); - -})(H5P.jQuery); \ No newline at end of file + +})(H5P.jQuery); diff --git a/js/h5p-library-list.js b/js/h5p-library-list.js index a1e517f..fa37d3a 100644 --- a/js/h5p-library-list.js +++ b/js/h5p-library-list.js @@ -1,3 +1,4 @@ +/*jshint multistr: true */ var H5PLibraryList= H5PLibraryList || {}; (function ($) { @@ -7,19 +8,19 @@ var H5PLibraryList= H5PLibraryList || {}; */ H5PLibraryList.init = function () { var $adminContainer = H5PIntegration.getAdminContainer(); - + var libraryList = H5PIntegration.getLibraryList(); if (libraryList.notCached) { $adminContainer.append(H5PUtils.getRebuildCache(libraryList.notCached)); } - + // Create library list $adminContainer.append(H5PLibraryList.createLibraryList(H5PIntegration.getLibraryList())); }; - + /** * Create the library list - * + * * @param {object} libraries List of libraries and headers */ H5PLibraryList.createLibraryList = function (libraries) { @@ -27,11 +28,11 @@ var H5PLibraryList= H5PLibraryList || {}; if(libraries.listData === undefined || libraries.listData.length === 0) { return $('
' + t.NA + '
'); } - + // Create table var $table = H5PUtils.createTable(libraries.listHeaders); $table.addClass('libraries'); - + // Add libraries $.each (libraries.listData, function (index, library) { var $libraryRow = H5PUtils.createTableRow([ @@ -54,7 +55,7 @@ var H5PLibraryList= H5PLibraryList || {}; \ ' ]); - + var hasContent = !(library.numContent === '' || library.numContent === 0); if (library.upgradeUrl === null) { $('.h5p-admin-upgrade-library', $libraryRow).remove(); @@ -67,12 +68,12 @@ var H5PLibraryList= H5PLibraryList || {}; window.location.href = library.upgradeUrl; }); } - + // Open details view when clicked $('.h5p-admin-view-library', $libraryRow).on('click', function () { window.location.href = library.detailsUrl; }); - + var $deleteButton = $('.h5p-admin-delete-library', $libraryRow); if (libraries.notCached !== undefined || hasContent || (library.numContentDependencies !== '' && library.numContentDependencies !== 0) || (library.numLibraryDependencies !== '' && library.numLibraryDependencies !== 0)) { // Disabled delete if content. @@ -87,11 +88,11 @@ var H5PLibraryList= H5PLibraryList || {}; $table.append($libraryRow); }); - + return $table; }; - - + + // Initialize me: $(document).ready(function () { if (!H5PLibraryList.initialized) { @@ -99,5 +100,5 @@ var H5PLibraryList= H5PLibraryList || {}; H5PLibraryList.init(); } }); - + })(H5P.jQuery); diff --git a/js/h5p-utils.js b/js/h5p-utils.js index 26ffa1d..0db73a7 100644 --- a/js/h5p-utils.js +++ b/js/h5p-utils.js @@ -3,7 +3,7 @@ var H5PUtils = H5PUtils || {}; (function ($) { /** * Generic function for creating a table including the headers - * + * * @param {array} headers List of headers */ H5PUtils.createTable = function (headers) { @@ -12,62 +12,62 @@ var H5PUtils = H5PUtils || {}; if(headers) { var $thead = $(''); var $tr = $(''); - + $.each(headers, function (index, value) { if (!(value instanceof Object)) { value = { html: value }; } - + $('', value).appendTo($tr); }); - + $table.append($thead.append($tr)); } - + return $table; }; - + /** * Generic function for creating a table row - * + * * @param {array} rows Value list. Object name is used as class name in */ H5PUtils.createTableRow = function (rows) { var $tr = $(''); - + $.each(rows, function (index, value) { if (!(value instanceof Object)) { value = { html: value }; } - + $('', value).appendTo($tr); }); - + return $tr; }; - + /** * Generic function for creating a field containing label and value - * - * @param {string} label The label displayed in front of the value + * + * @param {string} label The label displayed in front of the value * @param {string} value The value */ H5PUtils.createLabeledField = function (label, value) { var $field = $('
'); - + $field.append('
' + label + '
'); $field.append('
' + value + '
'); - + 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'} */ @@ -77,10 +77,10 @@ var H5PUtils = H5PUtils || {}; }); return template; }; - + /** * Get throbber with given text. - * + * * @param {String} text * @returns {$} */ @@ -90,7 +90,7 @@ var H5PUtils = H5PUtils || {}; text: text }); }; - + /** * Makes it possbile to rebuild all content caches from admin UI. * @param {Object} notCached @@ -107,7 +107,7 @@ var H5PUtils = H5PUtils || {}; current++; if (current === parts.length) current = 0; }, 100); - + var $counter = $container.find('.progress'); var build = function () { $.post(notCached.url, function (left) { @@ -126,8 +126,8 @@ var H5PUtils = H5PUtils || {}; }; build(); }); - + return $container; }; - -})(H5P.jQuery); \ No newline at end of file + +})(H5P.jQuery); diff --git a/js/h5p.js b/js/h5p.js index 3b5db69..c858652 100644 --- a/js/h5p.js +++ b/js/h5p.js @@ -1,3 +1,4 @@ +/*jshint multistr: true */ // TODO: Should we split up the generic parts needed by the editor(and others), and the parts needed to "run" H5Ps? var H5P = H5P || {}; @@ -11,12 +12,12 @@ H5P.$window = H5P.jQuery(window); if (document.documentElement.requestFullScreen) { H5P.fullScreenBrowserPrefix = ''; } -else if (document.documentElement.webkitRequestFullScreen - && navigator.userAgent.indexOf('Android') === -1 // Skip Android +else if (document.documentElement.webkitRequestFullScreen && + navigator.userAgent.indexOf('Android') === -1 // Skip Android ) { H5P.safariBrowser = navigator.userAgent.match(/Version\/(\d)/); H5P.safariBrowser = (H5P.safariBrowser === null ? 0 : parseInt(H5P.safariBrowser[1])); - + // Do not allow fullscreen for safari < 7. if (H5P.safariBrowser === 0 || H5P.safariBrowser > 6) { H5P.fullScreenBrowserPrefix = 'webkit'; @@ -56,14 +57,14 @@ H5P.init = function () { // Create new instance. var instance = H5P.newRunnable(library, contentId, $container, true); - + // Check if we should add and display a fullscreen button for this H5P. if (contentData.fullScreen == 1) { H5P.jQuery('
').prependTo($container).children().click(function () { H5P.fullScreen($container, instance); }); - }; - + } + var $actions = H5P.jQuery('
    '); if (contentData.exportUrl !== '') { // Display export button @@ -87,7 +88,7 @@ H5P.init = function () { H5P.jQuery('
  • ').appendTo($actions); } $actions.insertAfter($container); - + if (H5P.isFramed) { // Make it possible to resize the iframe when the content changes size. This way we get no scrollbars. var iframe = window.parent.document.getElementById('h5p-iframe-' + contentId); @@ -95,10 +96,10 @@ H5P.init = function () { if (window.parent.H5P.isFullscreen) { return; // Skip if full screen. } - + // Retain parent size to avoid jumping/scrolling var parentHeight = iframe.parentElement.style.height; - iframe.parentElement.style.height = iframe.parentElement.clientHeight + 'px'; + iframe.parentElement.style.height = iframe.parentElement.clientHeight + 'px'; // Reset iframe height, in case content has shrinked. iframe.style.height = '1px'; @@ -109,7 +110,7 @@ H5P.init = function () { // Free parent iframe.parentElement.style.height = parentHeight; }; - + var resizeDelay; instance.$.on('resize', function () { // Use a delay to make sure iframe is resized to the correct size. @@ -119,7 +120,7 @@ H5P.init = function () { }, 1); }); } - + // Resize everything when window is resized. $window.resize(function () { if (window.parent.H5P.isFullscreen) { @@ -130,7 +131,7 @@ H5P.init = function () { instance.$.trigger('resize'); } }); - + // Resize content. instance.$.trigger('resize'); }); @@ -159,7 +160,7 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) { window.parent.H5P.fullScreen($element, instance, exitCallback, H5P.$body.get()); return; } - + var $container = $element; var $classes, $iframe; if (body === undefined) { @@ -173,23 +174,23 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) { $iframe = H5P.jQuery(iframeSelector); $element = $iframe.parent(); // Put iframe wrapper in fullscreen, not container. } - + $classes = $element.add(H5P.$body).add($classes); - + /** * Prepare for resize by setting the correct styles. - * + * * @param {String} classes CSS */ var before = function (classes) { $classes.addClass(classes); - + if ($iframe !== undefined) { // Set iframe to its default size(100%). $iframe.css('height', ''); } }; - + /** * Gets called when fullscreen mode has been entered. * Resizes and sets focus on content. @@ -199,17 +200,17 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) { instance.$.trigger('resize'); instance.$.trigger('focus'); }; - + /** * Gets called when fullscreen mode has been exited. * Resizes and sets focus on content. - * + * * @param {String} classes CSS */ var done = function (classes) { H5P.isFullscreen = false; $classes.removeClass(classes); - + // Do not rely on window resize events. instance.$.trigger('resize'); instance.$.trigger('focus'); @@ -222,11 +223,11 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) { H5P.isFullscreen = true; if (H5P.fullScreenBrowserPrefix === undefined) { // Create semi fullscreen. - + before('h5p-semi-fullscreen'); var $disable = H5P.jQuery('
    ').appendTo($container.find('.h5p-content-controls')); var keyup, disableSemiFullscreen = function () { - $disable.remove(); + $disable.remove(); $body.unbind('keyup', keyup); done('h5p-semi-fullscreen'); }; @@ -241,7 +242,7 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) { } else { // Create real fullscreen. - + before('h5p-fullscreen'); var first, eventName = (H5P.fullScreenBrowserPrefix === 'ms' ? 'MSFullscreenChange' : H5P.fullScreenBrowserPrefix + 'fullscreenchange'); document.addEventListener(eventName, function () { @@ -251,7 +252,7 @@ H5P.fullScreen = function ($element, instance, exitCallback, body) { entered(); return; } - + // We are exiting fullscreen done('h5p-fullscreen'); document.removeEventListener(eventName, arguments.callee, false); @@ -282,26 +283,26 @@ H5P.getPath = function (path, contentId) { var hasProtocol = function (path) { return path.match(/^[a-z0-9]+:\/\//i); }; - + if (hasProtocol(path)) { return path; } - + if (contentId !== undefined) { prefix = H5PIntegration.getContentPath(contentId); } - else if (window['H5PEditor'] !== undefined) { + else if (window.H5PEditor !== undefined) { prefix = H5PEditor.filesPath; } else { return; } - + if (!hasProtocol(prefix)) { prefix = window.parent.location.protocol + "//" + window.parent.location.host + prefix; } - - return prefix + '/' + path; + + return prefix + '/' + path; }; /** @@ -318,7 +319,7 @@ H5P.getContentPath = function (contentId) { /** * Get library class constructor from H5P by classname. - * Note that this class will only work for resolve "H5P.NameWithoutDot". + * Note that this class will only work for resolve "H5P.NameWithoutDot". * Also check out: H5P.newRunnable * * Used from libraries to construct instances of other libraries' objects by name. @@ -336,34 +337,36 @@ H5P.classFromName = function (name) { * * TODO: Should we check if version matches the library? * TODO: Dynamically try to load libraries currently not loaded? That will require a callback. - * + * * @param {Object} library Library/action object form params. - * @param {Number} contentId + * @param {Number} contentId * @param {jQuery} $attachTo An optional element to attach the instance to. * @param {Boolean} skipResize Optionally skip triggering of the resize event after attaching. * @return {Object} Instance. */ H5P.newRunnable = function (library, contentId, $attachTo, skipResize) { + var nameSplit, versionSplit; try { - var nameSplit = library.library.split(' ', 2); - var versionSplit = nameSplit[1].split('.', 2); + nameSplit = library.library.split(' ', 2); + versionSplit = nameSplit[1].split('.', 2); } catch (err) { return H5P.error('Invalid library string: ' + library.library); } - + if ((library.params instanceof Object) !== true || (library.params instanceof Array) === true) { H5P.error('Invalid library params for: ' + library.library); return H5P.error(library.params); } - + // Find constructor function + var constructor; try { nameSplit = nameSplit[0].split('.'); - var constructor = window; - for (var i = 0; i < nameSplit.length; i++) { + constructor = window; + for (var i = 0; i < nameSplit.length; i++) { constructor = constructor[nameSplit[i]]; - }; + } if (typeof constructor !== 'function') { throw null; } @@ -371,16 +374,16 @@ H5P.newRunnable = function (library, contentId, $attachTo, skipResize) { catch (err) { return H5P.error('Unable to find constructor for: ' + library.library); } - + var instance = new constructor(library.params, contentId); - + if (instance.$ === undefined) { instance.$ = H5P.jQuery(instance); } - + if ($attachTo !== undefined) { instance.attach($attachTo); - + if (skipResize === undefined || !skipResize) { // Resize content. instance.$.trigger('resize'); @@ -417,18 +420,18 @@ H5P.t = function (key, vars, ns) { if (H5PIntegration.i18n[ns] === undefined) { return '[Missing translation namespace "' + ns + '"]'; } - + if (H5PIntegration.i18n[ns][key] === undefined) { return '[Missing translation "' + key + '" in "' + ns + '"]'; } var translation = H5PIntegration.i18n[ns][key]; - + if (vars !== undefined) { // Replace placeholder with variables. for (var placeholder in vars) { translation = translation.replace(placeholder, vars[placeholder]); - } + } } return translation; @@ -457,7 +460,7 @@ H5P.Dialog = function (name, title, content, $element) { }) .end() .end(); - + this.open = function () { setTimeout(function () { $dialog.addClass('h5p-open'); // Fade in @@ -465,7 +468,7 @@ H5P.Dialog = function (name, title, content, $element) { H5P.jQuery(self).trigger('dialog-opened', [$dialog]); }, 1); }; - + this.close = function () { $dialog.removeClass('h5p-open'); // Fade out setTimeout(function () { @@ -489,7 +492,7 @@ H5P.openCopyrightsDialog = function ($element, instance) { if (copyrights === undefined || copyrights === '') { copyrights = H5P.t('noCopyrights'); } - + var dialog = new H5P.Dialog('copyrights', H5P.t('copyrightInformation'), copyrights, $element); dialog.open(); }; @@ -503,12 +506,12 @@ H5P.openCopyrightsDialog = function ($element, instance) { */ H5P.openEmbedDialog = function ($element, embedCode) { var dialog = new H5P.Dialog('embed', H5P.t('embed'), '', $element); - + // Selecting embed code when dialog is opened H5P.jQuery(dialog).on('dialog-opened', function (event, $dialog) { $dialog.find('.h5p-embed-code-container').select(); }); - + dialog.open(); }; @@ -519,7 +522,7 @@ H5P.ContentCopyrights = function () { var label; var media = []; var content = []; - + /** * Public. Set label. * @@ -528,7 +531,7 @@ H5P.ContentCopyrights = function () { this.setLabel = function (newLabel) { label = newLabel; }; - + /** * Public. Add sub content. * @@ -539,7 +542,7 @@ H5P.ContentCopyrights = function () { media.push(newMedia); } }; - + /** * Public. Add sub content. * @@ -550,7 +553,7 @@ H5P.ContentCopyrights = function () { content.push(newContent); } }; - + /** * Public. Print content copyright. * @@ -558,28 +561,28 @@ H5P.ContentCopyrights = function () { */ this.toString = function () { var html = ''; - + // Add media rights for (var i = 0; i < media.length; i++) { html += media[i]; } - + // Add sub content rights - for (var i = 0; i < content.length; i++) { + for (i = 0; i < content.length; i++) { html += content[i]; } - - + + if (html !== '') { // Add a label to this info if (label !== undefined) { html = '

    ' + label + '

    ' + html; } - + // Add wrapper html = '
    ' + html + '
    '; } - + return html; }; }; @@ -595,35 +598,35 @@ H5P.ContentCopyrights = function () { H5P.MediaCopyright = function (copyright, labels, order, extraFields) { var thumbnail; var list = new H5P.DefinitionList(); - + /** * Private. Get translated label for field. * * @param {String} fieldName - * @return {String} + * @return {String} */ var getLabel = function (fieldName) { if (labels === undefined || labels[fieldName] === undefined) { return H5P.t(fieldName); } - + return labels[fieldName]; }; - + /** * Private. Get humanized value for field. * * @param {String} fieldName - * @return {String} + * @return {String} */ var humanizeValue = function (fieldName, value) { if (fieldName === 'license') { return H5P.copyrightLicenses[value]; } - + return value; }; - + if (copyright !== undefined) { // Add the extra fields for (var field in extraFields) { @@ -631,12 +634,12 @@ H5P.MediaCopyright = function (copyright, labels, order, extraFields) { copyright[field] = extraFields[field]; } } - + if (order === undefined) { // Set default order order = ['title', 'author', 'year', 'source', 'license']; } - + for (var i = 0; i < order.length; i++) { var fieldName = order[i]; if (copyright[fieldName] !== undefined) { @@ -644,7 +647,7 @@ H5P.MediaCopyright = function (copyright, labels, order, extraFields) { } } } - + /** * Public. Set thumbnail. * @@ -653,7 +656,7 @@ H5P.MediaCopyright = function (copyright, labels, order, extraFields) { this.setThumbnail = function (newThumbnail) { thumbnail = newThumbnail; }; - + /** * Public. Checks if this copyright is undisclosed. * I.e. only has the license attribute set, and it's undisclosed. @@ -669,7 +672,7 @@ H5P.MediaCopyright = function (copyright, labels, order, extraFields) { } return false; }; - + /** * Public. Print media copyright. * @@ -677,20 +680,20 @@ H5P.MediaCopyright = function (copyright, labels, order, extraFields) { */ this.toString = function () { var html = ''; - + if (this.undisclosed()) { return html; // No need to print a copyright with a single undisclosed license. } - + if (thumbnail !== undefined) { html += thumbnail; } html += list; - + if (html !== '') { html = ''; } - + return html; }; }; @@ -742,16 +745,16 @@ H5P.Field = function (label, value) { * Public. Get field label. * * @returns {String} - */ + */ this.getLabel = function () { return label; }; - + /** * Public. Get field value. * * @returns {String} - */ + */ this.getValue = function () { return value; }; @@ -762,7 +765,7 @@ H5P.Field = function (label, value) { */ H5P.DefinitionList = function () { var fields = []; - + /** * Public. Add field to list. * @@ -771,7 +774,7 @@ H5P.DefinitionList = function () { this.add = function (field) { fields.push(field); }; - + /** * Public. Get Number of fields. * @@ -780,7 +783,7 @@ H5P.DefinitionList = function () { this.size = function () { return fields.length; }; - + /** * Public. Get field at given index. * @@ -790,7 +793,7 @@ H5P.DefinitionList = function () { this.get = function (index) { return fields[index]; }; - + /** * Public. Print definition list. * @@ -879,7 +882,7 @@ H5P.getLibraryPath = function (library) { /** * Recursivly clone the given object. - * TODO: Consider if this needs to be in core. Doesn't $.extend do the same? + * TODO: Consider if this needs to be in core. Doesn't $.extend do the same? * * @param {object} object Object to clone. * @param {type} recursive From 43188bb7e0b3fe6ded3a3cd67ff9f45435d96bc7 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Tue, 7 Oct 2014 14:46:35 +0200 Subject: [PATCH 17/24] Specifying language code is now required when getting library translations. --- h5p-development.class.php | 57 +++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/h5p-development.class.php b/h5p-development.class.php index d599ca1..75e9daf 100644 --- a/h5p-development.class.php +++ b/h5p-development.class.php @@ -30,7 +30,7 @@ class H5PDevelopment { $this->findLibraries($filesPath . '/development'); } } - + /** * Get contents of file. * @@ -41,15 +41,15 @@ class H5PDevelopment { if (file_exists($file) === FALSE) { return NULL; } - + $contents = file_get_contents($file); if ($contents === FALSE) { return NULL; } - + return $contents; } - + /** * Scans development directory and find all libraries. * @@ -57,39 +57,39 @@ class H5PDevelopment { */ private function findLibraries($path) { $this->libraries = array(); - + if (is_dir($path) === FALSE) { - return; + return; } - + $contents = scandir($path); - + for ($i = 0, $s = count($contents); $i < $s; $i++) { if ($contents[$i]{0} === '.') { continue; // Skip hidden stuff. } - + $libraryPath = $path . '/' . $contents[$i]; $libraryJSON = $this->getFileContents($libraryPath . '/library.json'); if ($libraryJSON === NULL) { continue; // No JSON file, skip. } - + $library = json_decode($libraryJSON, TRUE); if ($library === FALSE) { continue; // Invalid JSON. } - + // TODO: Validate props? Not really needed, is it? this is a dev site. - + // Save/update library. $library['libraryId'] = $this->h5pF->getLibraryId($library['machineName'], $library['majorVersion'], $library['minorVersion']); $this->h5pF->saveLibraryData($library, $library['libraryId'] === FALSE); - + $library['path'] = $libraryPath; $this->libraries[H5PDevelopment::libraryToString($library['machineName'], $library['majorVersion'], $library['minorVersion'])] = $library; } - + // TODO: Should we remove libraries without files? Not really needed, but must be cleaned up some time, right? // Go trough libraries and insert dependencies. Missing deps. will just be ignored and not available. (I guess?!) @@ -106,17 +106,17 @@ class H5PDevelopment { } // TODO: Deps must be inserted into h5p_nodes_libraries as well... ? But only if they are used?! } - + /** * @return array Libraris in development folder. */ public function getLibraries() { return $this->libraries; } - + /** * Get library - * + * * @param string $name of the library. * @param int $majorVersion of the library. * @param int $minorVersion of the library. @@ -126,10 +126,10 @@ class H5PDevelopment { $library = H5PDevelopment::libraryToString($name, $majorVersion, $minorVersion); return isset($this->libraries[$library]) === TRUE ? $this->libraries[$library] : NULL; } - + /** * Get semantics for the given library. - * + * * @param string $name of the library. * @param int $majorVersion of the library. * @param int $minorVersion of the library. @@ -137,32 +137,32 @@ class H5PDevelopment { */ public function getSemantics($name, $majorVersion, $minorVersion) { $library = H5PDevelopment::libraryToString($name, $majorVersion, $minorVersion); - + if (isset($this->libraries[$library]) === FALSE) { return NULL; } - + return $this->getFileContents($this->libraries[$library]['path'] . '/semantics.json'); } - + /** * Get translations for the given library. - * + * * @param string $name of the library. * @param int $majorVersion of the library. * @param int $minorVersion of the library. * @return string Translation */ - public function getLanguage($name, $majorVersion, $minorVersion) { + public function getLanguage($name, $majorVersion, $minorVersion, $language) { $library = H5PDevelopment::libraryToString($name, $majorVersion, $minorVersion); - + if (isset($this->libraries[$library]) === FALSE) { return NULL; } - - return $this->getFileContents($this->libraries[$library]['path'] . '/language/' . $this->language . '.json'); + + return $this->getFileContents($this->libraries[$library]['path'] . '/language/' . $language . '.json'); } - + /** * Writes library as string on the form "name majorVersion.minorVersion" * @@ -175,4 +175,3 @@ class H5PDevelopment { return $name . ' ' . $majorVersion . '.' . $minorVersion; } } - From a7939bf5ad0a3493bd0a0086808cc69e9853badf Mon Sep 17 00:00:00 2001 From: falcon Date: Wed, 8 Oct 2014 10:16:16 +0200 Subject: [PATCH 18/24] Improve comments --- h5p.classes.php | 145 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 109 insertions(+), 36 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index e9ef67c..7ee6d62 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -7,7 +7,10 @@ interface H5PFrameworkInterface { /** * Returns info for the current platform * - * @return array An array containing info + * @return array + * An associative array containing: + * - name: The name of the plattform, for instance "Wordpress" + * - version: The version of the pattform, for instance "4.0" */ public function getPlatformInfo(); @@ -15,7 +18,7 @@ interface H5PFrameworkInterface { * Show the user an error message * * @param string $message - * The error message + * The error message */ public function setErrorMessage($message); @@ -40,54 +43,77 @@ interface H5PFrameworkInterface { * - @variable: escape plain text to HTML * - %variable: escape text and theme as a placeholder for user-submitted * content - * @return string Translated string + * @return string + * Translated string */ public function t($message, $replacements = array()); /** * Get the Path to the last uploaded h5p * - * @return string Path to the folder where the last uploaded h5p for this session is located. + * @return string + * Path to the folder where the last uploaded h5p for this session is located. */ public function getUploadedH5pFolderPath(); /** - * @return string Path to the folder where all h5p files are stored + * @return string + * Path to the folder where all h5p files are stored */ public function getH5pPath(); /** * Get the path to the last uploaded h5p file * - * @return string Path to the last uploaded h5p + * @return string + * Path to the last uploaded h5p */ public function getUploadedH5pPath(); /** - * Get the list of the current installed libraries + * Get a list of the current installed libraries * - * @return array Associative array containg one item per machine name. This item contains an array of libraries. + * @return array + * Associative array containg one entry per machine name. + * For each machineName there is a list of libraries(with different versions) */ public function loadLibraries(); /** * Saving the unsupported library list * - * @param array A list of unsupported libraries + * @param array + * A list of unsupported libraries. Each list entry contains: + * - name: MachineName for the library + * - downloadUrl: URL to a location a new version of the library may be downloaded from + * - currentVersion: The unsupported version of the library installed on the system. + * This is an associative array containing: + * - major: The major version of the library + * - minor: The minor version of the library + * - patch: The patch version of the library */ public function setUnsupportedLibraries($libraries); /** * Returns unsupported libraries * - * @return array A list of the unsupported libraries + * @return array + * A list of unsupported libraries. Each entry contains an associative array with: + * - name: MachineName for the library + * - downloadUrl: URL to a location a new version of the library may be downloaded from + * - currentVersion: The unsupported version of the library installed on the system. + * This is an associative array containing: + * - major: The major version of the library + * - minor: The minor version of the library + * - patch: The patch version of the library */ public function getUnsupportedLibraries(); /** * Returns the URL to the library admin page * - * @return string URL to admin page + * @return string + * URL to admin page */ public function getAdminUrl(); @@ -95,13 +121,13 @@ interface H5PFrameworkInterface { * Get id to an excisting library * * @param string $machineName - * The librarys machine name + * The librarys machine name * @param int $majorVersion - * The librarys major version + * The librarys major version * @param int $minorVersion - * The librarys minor version + * The librarys minor version * @return int - * The id of the specified library or FALSE + * The id of the specified library or FALSE */ public function getLibraryId($machineName, $majorVersion, $minorVersion); @@ -111,8 +137,12 @@ interface H5PFrameworkInterface { * The default extension list is part of h5p, but admins should be allowed to modify it * * @param boolean $isLibrary + * TRUE if this is the whitelist for a library. FALSE if it is the whitelist + * for the content folder we are getting * @param string $defaultContentWhitelist + * A string of file extensions separated by whitespace * @param string $defaultLibraryWhitelist + * A string of file extensions separated by whitespace */ public function getWhitelist($isLibrary, $defaultContentWhitelist, $defaultLibraryWhitelist); @@ -120,10 +150,14 @@ interface H5PFrameworkInterface { * Is the library a patched version of an existing library? * * @param object $library - * The library data for a library we are checking + * An associateve array containing: + * - machineName: The library machineName + * - majorVersion: The librarys majorVersion + * - minorVersion: The librarys minorVersion + * - patchVersion: The librarys patchVersion * @return boolean - * TRUE if the library is a patched version of an excisting library - * FALSE otherwise + * TRUE if the library is a patched version of an excisting library + * FALSE otherwise */ public function isPatchedLibrary($library); @@ -151,23 +185,53 @@ interface H5PFrameworkInterface { * Also fills in the libraryId in the libraryData object if the object is new * * @param object $libraryData - * Object holding the information that is to be stored + * Associative array containing: + * - libraryId: The id of the library if it is an existing library. + * - title: The library's name + * - machineName: The library machineName + * - majorVersion: The library's majorVersion + * - minorVersion: The library's minorVersion + * - patchVersion: The library's patchVersion + * - runnable: 1 if the library is a content type, 0 otherwise + * - fullscreen(optional): 1 if the library supports fullscreen, 0 otherwise + * - embedTypes(optional): list of supported embed types + * - preloadedJs(optional): list of associative arrays containing: + * - path: path to a js file relative to the library root folder + * - preloadedCss(optional): list of associative arrays containing: + * - path: path to css file relative to the library root folder + * - dropLibraryCss(optional): list of associative arrays containing: + * - machineName: machine name for the librarys that are to drop their css + * - semantics(optional): Json describing the content structure for the library + * - language(optional): associative array containing: + * - languageCode: Translation in json format */ public function saveLibraryData(&$libraryData, $new = TRUE); /** * Insert new content. * - * @param object $content + * @param array $content + * An associative array containing: + * - id: The content id + * - params: The content in json format + * - library: An associative array containing: + * - libraryId: The id of the main library for this content * @param int $contentMainId + * Main id for the content if this is a system that supports versioning */ public function insertContent($content, $contentMainId = NULL); /** * Update old content. * - * @param object $content + * @param array $content + * An associative array containing: + * - id: The content id + * - params: The content in json format + * - library: An associative array containing: + * - libraryId: The id of the main library for this content * @param int $contentMainId + * Main id for the content if this is a system that supports versioning */ public function updateContent($content, $contentMainId = NULL); @@ -175,25 +239,31 @@ interface H5PFrameworkInterface { * Save what libraries a library is dependending on * * @param int $libraryId - * Library Id for the library we're saving dependencies for + * Library Id for the library we're saving dependencies for * @param array $dependencies - * List of dependencies in the format used in library.json + * List of dependencies as associative arrays containing: + * - machineName: The library machineName + * - majorVersion: The library's majorVersion + * - minorVersion: The library's minorVersion * @param string $dependency_type - * What type of dependency this is, for instance it might be an editor dependency + * What type of dependency this is, the following values are allowed: + * - editor + * - preloaded + * - dynamic */ public function saveLibraryDependencies($libraryId, $dependencies, $dependency_type); /** - * Copies library usage + * Give an H5P the same library dependencies as a given H5P * * @param int $contentId - * Framework specific id identifying the content + * Id identifying the content * @param int $copyFromId - * Framework specific id identifying the content to be copied + * Id identifying the content to be copied * @param int $contentMainId - * Framework specific main id for the content, typically used in frameworks - * That supports versioning. (In this case the content id will typically be - * the version id, and the contentMainId will be the frameworks content id + * Main id for the content, typically used in frameworks + * That supports versioning. (In this case the content id will typically be + * the version id, and the contentMainId will be the frameworks content id */ public function copyLibraryUsage($contentId, $copyFromId, $contentMainId = NULL); @@ -201,7 +271,7 @@ interface H5PFrameworkInterface { * Deletes content data * * @param int $contentId - * Framework specific id identifying the content + * Id identifying the content */ public function deleteContentData($contentId); @@ -209,7 +279,7 @@ interface H5PFrameworkInterface { * Delete what libraries a content item is using * * @param int $contentId - * Content Id of the content we'll be deleting library usage for + * Content Id of the content we'll be deleting library usage for */ public function deleteLibraryUsage($contentId); @@ -217,11 +287,14 @@ interface H5PFrameworkInterface { * Saves what libraries the content uses * * @param int $contentId - * Framework specific id identifying the content + * Id identifying the content * @param array $librariesInUse - * List of libraries the content uses. Libraries consist of arrays with: - * - libraryId stored in $librariesInUse[]['library']['libraryId'] - * - libraryId stored in $librariesInUse[]['preloaded'] + * List of libraries the content uses. Libraries consist of associative arrays with: + * - library: Associative array containing: + * - dropLibraryCss(optional): commasepareted list of machineNames + * - machineName: Machine name for the library + * - libraryId: Id of the library + * - type: The dependency type (editor, preloaded or dynamic) */ public function saveLibraryUsage($contentId, $librariesInUse); From c131a339a158d6047f478e27e9f7fdb5be8f51e3 Mon Sep 17 00:00:00 2001 From: falcon Date: Thu, 9 Oct 2014 19:29:20 +0200 Subject: [PATCH 19/24] Finish up commenting the interface functions --- h5p.classes.php | 130 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 106 insertions(+), 24 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 7ee6d62..75bf60e 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -294,7 +294,10 @@ interface H5PFrameworkInterface { * - dropLibraryCss(optional): commasepareted list of machineNames * - machineName: Machine name for the library * - libraryId: Id of the library - * - type: The dependency type (editor, preloaded or dynamic) + * - type: The dependency type. Allowed values: + * - editor + * - dynamic + * - preloaded */ public function saveLibraryUsage($contentId, $librariesInUse); @@ -303,8 +306,11 @@ interface H5PFrameworkInterface { * 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 + * Library identifier + * @return array + * Associative array containing: + * - content: Number of content using the library + * - libraries: Number of libraries depending on the library */ public function getLibraryUsage($libraryId); @@ -312,62 +318,128 @@ interface H5PFrameworkInterface { * Loads a library * * @param string $machineName + * The library's machine name * @param int $majorVersion + * The library's major version * @param int $minorVersion + * The library's minor version * @return array|FALSE - * Array representing the library with dependency descriptions - * FALSE if the library doesn't exist + * FALSE if the library doesn't exist. + * Otherwise an associative array containing: + * - libraryId: The id of the library if it is an existing library. + * - title: The library's name + * - machineName: The library machineName + * - majorVersion: The library's majorVersion + * - minorVersion: The library's minorVersion + * - patchVersion: The library's patchVersion + * - runnable: 1 if the library is a content type, 0 otherwise + * - fullscreen(optional): 1 if the library supports fullscreen, 0 otherwise + * - embedTypes(optional): list of supported embed types + * - preloadedJs(optional): comma separated string with js file paths + * - preloadedCss(optional): comma separated sting with css file paths + * - dropLibraryCss(optional): list of associative arrays containing: + * - machineName: machine name for the librarys that are to drop their css + * - semantics(optional): Json describing the content structure for the library + * - preloadedDependencies(optional): list of associative arrays containing: + * - machineName: Machine name for a library this library is depending on + * - majorVersion: Major version for a library this library is depending on + * - minorVersion: Minor for a library this library is depending on + * - dynamicDependencies(optional): list of associative arrays containing: + * - machineName: Machine name for a library this library is depending on + * - majorVersion: Major version for a library this library is depending on + * - minorVersion: Minor for a library this library is depending on + * - editorDependencies(optional): list of associative arrays containing: + * - machineName: Machine name for a library this library is depending on + * - majorVersion: Major version for a library this library is depending on + * - minorVersion: Minor for a library this library is depending on */ public function loadLibrary($machineName, $majorVersion, $minorVersion); /** * Loads library semantics. * - * @param string $name library identifier. - * @param int $majorVersion library identifier. - * @param int $minorVersion library identifier. - * @return string semantics. + * @param string $machineName + * Machine name for the library + * @param int $majorVersion + * The library's major version + * @param int $minorVersion + * The library's minor version + * @return string + * The library's semantics as json */ - public function loadLibrarySemantics($name, $majorVersion, $minorVersion); + public function loadLibrarySemantics($machineName, $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. + * Associative array representing the semantics + * @param string $machineName + * The library's machine name + * @param int $majorVersion + * The library's major version + * @param int $minorVersion + * The library's minor version */ - public function alterLibrarySemantics(&$semantics, $name, $majorVersion, $minorVersion); + public function alterLibrarySemantics(&$semantics, $machineName, $majorVersion, $minorVersion); /** * Delete all dependencies belonging to given library * * @param int $libraryId - * Library Id + * Library identifier */ public function deleteLibraryDependencies($libraryId); /** * Delete a library from database and file system * - * @param mixed $library Library + * @param int $libraryId + * Library identifier */ - public function deleteLibrary($library); + public function deleteLibrary($libraryId); /** * Load content. * - * @return object Content, null if not found. + * @param int $id + * Content identifier + * @return array + * Associative array containing: + * - contentId: Identifier for the content + * - params: json content as string + * - embedType: csv of embed types + * - title: The contents title + * - language: Language code for the content + * - libraryId: Id for the main library + * - libraryName: The library machine name + * - libraryMajorVersion: The library's majorVersion + * - libraryMinorVersion: The library's minorVersion + * - libraryEmbedTypes: CSV of the main library's embed types + * - libraryFullscreen: 1 if fullscreen is supported. 0 otherwise. */ public function loadContent($id); /** * Load dependencies for the given content of the given type. * - * @param int $id content. - * @param int $type dependency. + * @param int $id + * Content identifier + * @param int $type + * Dependency types. Allowed values: + * - editor + * - preloaded + * - dynamic * @return array + * List of associative arrays containing: + * - libraryId: The id of the library if it is an existing library. + * - machineName: The library machineName + * - majorVersion: The library's majorVersion + * - minorVersion: The library's minorVersion + * - patchVersion: The library's patchVersion + * - preloadedJs(optional): comma separated string with js file paths + * - preloadedCss(optional): comma separated sting with css file paths + * - dropCss(optional): csv of machine names */ public function loadContentDependencies($id, $type = NULL); @@ -375,7 +447,11 @@ interface H5PFrameworkInterface { * Get data from cache. * * @param string $group + * Identifier for the cache group * @param string $key + * Unique identifier within the group + * @return mixed + * Whatever has been stored in the cache. NULL if the entry doesn't exist */ public function cacheGet($group, $key); @@ -383,8 +459,11 @@ interface H5PFrameworkInterface { * Store data in cache. * * @param string $group + * The cache group where the data should be stored * @param string $key + * A unique key identifying where the data should be stored * @param mixed $data + * The data you want to cache */ public function cacheSet($group, $key, $data); @@ -392,20 +471,22 @@ interface H5PFrameworkInterface { * Delete data from cache. * * @param string $group + * Identifier for the cache group * @param string $key + * Unique identifier within the group */ 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 + * + * @param int $libraryId */ - public function invalidateContentCache($library_id); + public function invalidateContentCache($libraryId); /** - * Get content without cache. + * Get number of content that hasn't been cached */ public function getNotCached(); @@ -413,6 +494,7 @@ interface H5PFrameworkInterface { * Get number of contents using library as main library. * * @param int $library_id + * Identifier for a library */ public function getNumContent($library_id); } From c9914966f334c4e3f23270e11b0f24acd643f0fe Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Fri, 10 Oct 2014 13:56:48 +0200 Subject: [PATCH 20/24] Space fix. --- h5p.classes.php | 340 ++++++++++++++++++++++++------------------------ 1 file changed, 170 insertions(+), 170 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 75bf60e..72264ec 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -6,14 +6,14 @@ interface H5PFrameworkInterface { /** * Returns info for the current platform - * - * @return array + * + * @return array * An associative array containing: * - name: The name of the plattform, for instance "Wordpress" * - version: The version of the pattform, for instance "4.0" */ public function getPlatformInfo(); - + /** * Show the user an error message * @@ -69,19 +69,19 @@ interface H5PFrameworkInterface { * Path to the last uploaded h5p */ public function getUploadedH5pPath(); - + /** * Get a list of the current installed libraries - * - * @return array + * + * @return array * Associative array containg one entry per machine name. * For each machineName there is a list of libraries(with different versions) */ public function loadLibraries(); - + /** * Saving the unsupported library list - * + * * @param array * A list of unsupported libraries. Each list entry contains: * - name: MachineName for the library @@ -93,10 +93,10 @@ interface H5PFrameworkInterface { * - patch: The patch version of the library */ public function setUnsupportedLibraries($libraries); - + /** * Returns unsupported libraries - * + * * @return array * A list of unsupported libraries. Each entry contains an associative array with: * - name: MachineName for the library @@ -108,10 +108,10 @@ interface H5PFrameworkInterface { * - patch: The patch version of the library */ public function getUnsupportedLibraries(); - + /** * Returns the URL to the library admin page - * + * * @return string * URL to admin page */ @@ -145,7 +145,7 @@ interface H5PFrameworkInterface { * A string of file extensions separated by whitespace */ public function getWhitelist($isLibrary, $defaultContentWhitelist, $defaultLibraryWhitelist); - + /** * Is the library a patched version of an existing library? * @@ -203,13 +203,13 @@ interface H5PFrameworkInterface { * - machineName: machine name for the librarys that are to drop their css * - semantics(optional): Json describing the content structure for the library * - language(optional): associative array containing: - * - languageCode: Translation in json format + * - languageCode: Translation in json format */ public function saveLibraryData(&$libraryData, $new = TRUE); - + /** * Insert new content. - * + * * @param array $content * An associative array containing: * - id: The content id @@ -220,10 +220,10 @@ interface H5PFrameworkInterface { * Main id for the content if this is a system that supports versioning */ public function insertContent($content, $contentMainId = NULL); - + /** * Update old content. - * + * * @param array $content * An associative array containing: * - id: The content id @@ -234,7 +234,7 @@ interface H5PFrameworkInterface { * Main id for the content if this is a system that supports versioning */ public function updateContent($content, $contentMainId = NULL); - + /** * Save what libraries a library is dependending on * @@ -302,9 +302,9 @@ interface H5PFrameworkInterface { public function saveLibraryUsage($contentId, $librariesInUse); /** - * Get number of content/nodes using a library, and the number of + * Get number of content/nodes using a library, and the number of * dependencies to other libraries - * + * * @param int $library_id * Library identifier * @return array @@ -368,7 +368,7 @@ interface H5PFrameworkInterface { * The library's semantics as json */ public function loadLibrarySemantics($machineName, $majorVersion, $minorVersion); - + /** * Makes it possible to alter the semantics, adding custom fields, etc. * @@ -378,7 +378,7 @@ interface H5PFrameworkInterface { * The library's machine name * @param int $majorVersion * The library's major version - * @param int $minorVersion + * @param int $minorVersion * The library's minor version */ public function alterLibrarySemantics(&$semantics, $machineName, $majorVersion, $minorVersion); @@ -390,15 +390,15 @@ interface H5PFrameworkInterface { * Library identifier */ public function deleteLibraryDependencies($libraryId); - + /** * Delete a library from database and file system - * + * * @param int $libraryId * Library identifier */ public function deleteLibrary($libraryId); - + /** * Load content. * @@ -419,7 +419,7 @@ interface H5PFrameworkInterface { * - libraryFullscreen: 1 if fullscreen is supported. 0 otherwise. */ public function loadContent($id); - + /** * Load dependencies for the given content of the given type. * @@ -442,10 +442,10 @@ interface H5PFrameworkInterface { * - dropCss(optional): csv of machine names */ public function loadContentDependencies($id, $type = NULL); - + /** * Get data from cache. - * + * * @param string $group * Identifier for the cache group * @param string $key @@ -454,10 +454,10 @@ interface H5PFrameworkInterface { * Whatever has been stored in the cache. NULL if the entry doesn't exist */ public function cacheGet($group, $key); - + /** * Store data in cache. - * + * * @param string $group * The cache group where the data should be stored * @param string $key @@ -466,17 +466,17 @@ interface H5PFrameworkInterface { * The data you want to cache */ public function cacheSet($group, $key, $data); - + /** * Delete data from cache. - * + * * @param string $group * Identifier for the cache group * @param string $key * Unique identifier within the group */ 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. @@ -484,15 +484,15 @@ interface H5PFrameworkInterface { * @param int $libraryId */ public function invalidateContentCache($libraryId); - + /** * Get number of content that hasn't been cached */ public function getNotCached(); - + /** * Get number of contents using library as main library. - * + * * @param int $library_id * Identifier for a library */ @@ -609,14 +609,14 @@ class H5PValidator { // Extract and then remove the package file. $zip = new ZipArchive; - + // Only allow files with the .h5p extension: if (strtolower(substr($tmpPath, -3)) !== 'h5p') { $this->h5pF->setErrorMessage($this->h5pF->t('The file you uploaded is not a valid HTML5 Package (It does not have the .h5p file extension)')); H5PCore::deleteFileTree($tmpDir); return; } - + if ($zip->open($tmpPath) === true) { $zip->extractTo($tmpDir); $zip->close(); @@ -644,7 +644,7 @@ class H5PValidator { if ($skipContent === TRUE) { continue; } - + $mainH5pData = $this->getJsonData($filePath); if ($mainH5pData === FALSE) { $valid = FALSE; @@ -702,7 +702,7 @@ class H5PValidator { } $libraryH5PData = $this->getLibraryData($file, $filePath, $tmpDir); - + if ($libraryH5PData !== FALSE) { // Library's directory name must be: // - @@ -738,7 +738,7 @@ class H5PValidator { } if ($valid) { if ($upgradeOnly) { - // When upgrading, we opnly add allready installed libraries, + // When upgrading, we opnly add allready installed libraries, // and new dependent libraries $upgrades = array(); foreach ($libraries as &$library) { @@ -756,25 +756,25 @@ class H5PValidator { } } } - + $libraries = $upgrades; } - + $this->h5pC->librariesJsonData = $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'])) { unset($missingLibraries[H5PCore::libraryToString($missing)]); } } - + if (!empty($missingLibraries)) { foreach ($missingLibraries as $library) { $this->h5pF->setErrorMessage($this->h5pF->t('Missing required library @library', array('@library' => H5PCore::libraryToString($library)))); @@ -1183,7 +1183,7 @@ class H5PStorage { public $h5pF; public $h5pC; - + public $contentId = NULL; // Quick fix so WP can get ID of new content. /** @@ -1214,11 +1214,11 @@ class H5PStorage { $library_saved = FALSE; $upgradedLibsCount = 0; $mayUpdateLibraries = $this->h5pF->mayUpdateLibraries(); - + foreach ($this->h5pC->librariesJsonData as &$library) { $libraryId = $this->h5pF->getLibraryId($library['machineName'], $library['majorVersion'], $library['minorVersion']); $library['saveDependencies'] = TRUE; - + if (!$libraryId) { $new = TRUE; } @@ -1263,21 +1263,21 @@ 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']); - + $upgradedLibsCount++; } } - + if (!$skipContent) { $current_path = $this->h5pF->getUploadedH5pFolderPath() . DIRECTORY_SEPARATOR . 'content'; - + // Find out which libraries are used by this package/content $librariesInUse = array(); $this->h5pC->findLibraryDependencies($librariesInUse, $this->h5pC->mainJsonData); - + // Save content if ($content === NULL) { $content = array(); @@ -1294,19 +1294,19 @@ class H5PStorage { if (!is_dir($contents_path)) { mkdir($contents_path, 0777, true); } - + // Move the content folder $destination_path = $contents_path . DIRECTORY_SEPARATOR . $contentId; @rename($current_path, $destination_path); - + // Save the content library dependencies $this->h5pF->saveLibraryUsage($contentId, $librariesInUse); H5PCore::deleteFileTree($this->h5pF->getUploadedH5pFolderPath()); } - + // Update supported library list if neccessary: $this->h5pC->validateLibrarySupport(TRUE); - + if ($upgradeOnly) { // TODO - support translation $this->h5pF->setInfoMessage($this->h5pF->t('@num libraries were upgraded!', array('@num' => $upgradedLibsCount))); @@ -1314,7 +1314,7 @@ class H5PStorage { return $library_saved; } - + /** * Delete an H5P package * @@ -1383,7 +1383,7 @@ Class H5PExport { $this->h5pF = $H5PFramework; $this->h5pC = $H5PCore; } - + /** * Return path to h5p package. * @@ -1396,7 +1396,7 @@ Class H5PExport { $h5pDir = $this->h5pF->getH5pPath() . DIRECTORY_SEPARATOR; $tempPath = $h5pDir . 'temp' . DIRECTORY_SEPARATOR . $content['id']; $zipPath = $h5pDir . 'exports' . DIRECTORY_SEPARATOR . $content['id'] . '.h5p'; - + // Temp dir to put the h5p files in @mkdir($tempPath, 0777, TRUE); @mkdir($h5pDir . 'exports', 0777, TRUE); @@ -1413,8 +1413,8 @@ Class H5PExport { // Build h5p.json $h5pJson = array ( 'title' => $content['title'], - // TODO - stop using 'und', this is not the preferred way. - // Either remove language from the json if not existing, or use "language": null + // TODO - stop using 'und', this is not the preferred way. + // Either remove language from the json if not existing, or use "language": null 'language' => isset($content['language']) ? $content['language'] : 'und', 'mainLibrary' => $content['library']['name'], 'embedTypes' => $embedTypes, @@ -1423,7 +1423,7 @@ Class H5PExport { // 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']; @@ -1431,7 +1431,7 @@ Class H5PExport { // Do not add editor dependencies to h5p json. if ($dependency['type'] === 'editor') { - continue; + continue; } $h5pJson[$dependency['type'] . 'Dependencies'][] = array( @@ -1495,7 +1495,7 @@ Class H5PExport { */ private function addEditorLibraries($libraries, $editorLibraries) { foreach ($editorLibraries as $editorLibrary) { - $libraries[$editorLibrary['machineName']] = $editorLibrary; + $libraries[$editorLibrary['machineName']] = $editorLibrary; } return $libraries; } @@ -1524,9 +1524,9 @@ class H5PCore { public static $defaultContentWhitelist = 'json png jpg jpeg gif bmp tif tiff svg eot ttf woff otf webm mp4 ogg mp3 txt pdf rtf doc docx xls xlsx ppt pptx odt ods odp xml csv diff patch swf md textile'; public static $defaultLibraryWhitelistExtras = 'js css'; - - const SECONDS_IN_WEEK = 604800; - + + const SECONDS_IN_WEEK = 604800; + public $librariesJsonData, $contentJsonData, $mainJsonData, $h5pF, $path, $development_mode, $h5pD; private $exportEnabled; @@ -1542,12 +1542,12 @@ class H5PCore { */ 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) { $this->h5pD = new H5PDevelopment($this->h5pF, $path, $language); } @@ -1555,7 +1555,7 @@ class H5PCore { /** * Save content and clear cache. - * + * * @param array $content * @return int Content ID */ @@ -1564,17 +1564,17 @@ class H5PCore { $this->h5pF->updateContent($content, $contentMainId); } else { - $content['id'] = $this->h5pF->insertContent($content, $contentMainId); + $content['id'] = $this->h5pF->insertContent($content, $contentMainId); } - + if (!isset($content['filtered'])) { // TODO: Add filtered to all impl. and remove $this->h5pF->cacheDel('parameters', $content['id']); } - + return $content['id']; } - + /** * Load content. * @@ -1583,7 +1583,7 @@ class H5PCore { */ public function loadContent($id) { $content = $this->h5pF->loadContent($id); - + if ($content !== NULL) { $content['library'] = array( 'id' => $content['libraryId'], @@ -1594,7 +1594,7 @@ class H5PCore { 'fullscreen' => $content['libraryFullscreen'], ); unset($content['libraryId'], $content['libraryName'], $content['libraryEmbedTypes'], $content['libraryFullscreen']); - + // // TODO: Move to filterParameters? // if ($this->development_mode & H5PDevelopment::MODE_CONTENT) { // // TODO: Remove Drupal specific stuff @@ -1608,13 +1608,13 @@ class H5PCore { // } // } } - + return $content; } - + /** * Filter content run parameters, rebuild content dependecy cache and export file. - * + * * @param Object $content * @return Object NULL on failure. */ @@ -1630,7 +1630,7 @@ class H5PCore { if ($params !== NULL) { return $params; } - + // Validate and filter against main library semantics. $validator = new H5PContentValidator($this->h5pF, $this); $params = (object) array( @@ -1640,17 +1640,17 @@ class H5PCore { $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? } @@ -1658,7 +1658,7 @@ class H5PCore { $this->h5pF->cacheSet('parameters', $content['id'], $params); return $params; } - + /** * Find the files required for this content to work. * @@ -1667,10 +1667,10 @@ class H5PCore { */ 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])) { @@ -1679,13 +1679,13 @@ class H5PCore { } } } - + return $dependencies; } - + /** * Get all dependency assets of the given type - * + * * @param array $dependency * @param string $type * @param array $assets @@ -1695,12 +1695,12 @@ class H5PCore { if (empty($dependency[$type]) || $dependency[$type][0] === '') { return; } - + // Check if we should skip CSS. if ($type === 'preloadedCss' && (isset($dependency['dropCss']) && $dependency['dropCss'] === '1')) { return; } - + foreach ($dependency[$type] as $file) { $assets[] = (object) array( 'path' => $dependency['path'] . '/' . trim(is_array($file) ? $file['path'] : $file), @@ -1708,23 +1708,23 @@ class H5PCore { ); } } - + /** * Combines path with cache buster / version. - * + * * @param array $assets * @return array */ public function getAssetsUrls($assets) { $urls = array(); - + foreach ($assets as $asset) { $urls[] = $asset->path . $asset->version; } - + return $urls; } - + /** * Return file paths for all dependecies files. * @@ -1742,14 +1742,14 @@ class H5PCore { $dependency['preloadedJs'] = explode(',', $dependency['preloadedJs']); $dependency['preloadedCss'] = explode(',', $dependency['preloadedCss']); } - + $dependency['version'] = "?ver={$dependency['majorVersion']}.{$dependency['minorVersion']}.{$dependency['patchVersion']}"; $this->getDependencyAssets($dependency, 'preloadedJs', $files['scripts']); $this->getDependencyAssets($dependency, 'preloadedCss', $files['styles']); } return $files; } - + /** * Load library semantics. * @@ -1761,20 +1761,20 @@ class H5PCore { // 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. * @@ -1789,31 +1789,31 @@ class H5PCore { $library['semantics'] = $this->h5pD->getSemantics($name, $majorVersion, $minorVersion); } } - + if ($library === NULL) { // Try to load from DB. $library = $this->h5pF->loadLibrary($name, $majorVersion, $minorVersion); } - + return $library; } - + /** * Deletes a library - * + * * @param unknown $libraryId */ public function deleteLibrary($libraryId) { $this->h5pF->deleteLibrary($libraryId); - + // Force update of unsupported libraries list: $this->validateLibrarySupport(TRUE); } - + /** - * Recursive. Goes through the dependency tree for the given library and + * Recursive. 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. @@ -1824,18 +1824,18 @@ class H5PCore { if (!isset($library[$property])) { 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']); if ($dependencyLibrary) { $dependencies[$dependencyKey] = array( @@ -1964,7 +1964,7 @@ class H5PCore { } return FALSE; } - + /** * Determine the correct embed type to use. * @@ -1973,32 +1973,32 @@ class H5PCore { 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; } - + /** * 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 @@ -2014,13 +2014,13 @@ class H5PCore { 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 @@ -2039,36 +2039,36 @@ class H5PCore { return $obj ? (object) $newArr : $newArr; } - + /** * Check if currently installed H5P libraries are supported by * the current versjon of core. Which versions of which libraries are supported is * defined in the library-support.json file. * - * @param boolean If TRUE, unsupported libraries list are rebuilt. If FALSE, list is + * @param boolean If TRUE, unsupported libraries list are rebuilt. If FALSE, list is * rebuilt only if non-existing */ public function validateLibrarySupport($force = false) { if (!($this->h5pF->getUnsupportedLibraries() === NULL || $force)) { return; } - + $minVersions = $this->getMinimumVersionsSupported(realpath(dirname(__FILE__)) . '/library-support.json'); if ($minVersions === NULL) { return; } - + // Get all libraries installed, check if any of them is not supported: $libraries = $this->h5pF->loadLibraries(); $unsupportedLibraries = array(); - + // Iterate over all installed libraries foreach ($libraries as $library_name => $versions) { if (!isset($minVersions[$library_name])) { continue; } $min = $minVersions[$library_name]; - + // For each version of this library, check if it is supported foreach ($versions as $library) { if (!$this->isLibraryVersionSupported($library, $min->versions)) { @@ -2084,23 +2084,23 @@ class H5PCore { ); } } - + $this->h5pF->setUnsupportedLibraries(empty($unsupportedLibraries) ? NULL : $unsupportedLibraries); } } - + /** * Returns a list of the minimum version of libraries that are supported. * This is needed because some old libraries are no longer supported by core. - * + * * TODO: Make it possible for the systems to cache this list between requests. - * + * * @param string $path to json file * @return array indexed using library names */ public function getMinimumVersionsSupported($path) { $minSupported = array(); - + // Get list of minimum version for libraries. Some old libraries are no longer supported. $libraries = file_get_contents($path); if ($libraries !== FALSE) { @@ -2114,13 +2114,13 @@ class H5PCore { } } } - + return empty($minSupported) ? NULL : $minSupported; } - + /** * Check if a specific version of a library is supported - * + * * @param object library * @param array An array containing versions * @return boolean TRUE if supported, otherwise FALSE @@ -2133,62 +2133,62 @@ class H5PCore { // --- minor is higher than any minimumversion for a given major // --- major and minor equals and patch is >= supported $major_supported |= ($library->major_version > $minimumVersion->major); - + if ($library->major_version == $minimumVersion->major) { $minor_supported |= ($library->minor_version > $minimumVersion->minor); } - + if ($library->major_version == $minimumVersion->major && $library->minor_version == $minimumVersion->minor) { $patch_supported |= ($library->patch_version >= $minimumVersion->patch); } } - + return ($patch_supported || $minor_supported || $major_supported); } - + /** - * Helper function for creating markup for the unsupported libraries list - * + * Helper function for creating markup for the unsupported libraries list + * * TODO: Make help text translatable - * + * * @return string Html * */ public function createMarkupForUnsupportedLibraryList($libraries) { $html = '
    The following versions of H5P libraries are not supported anymore:
      '; - + foreach ($libraries as $library) { $downloadUrl = $library['downloadUrl']; $libraryName = $library['name']; $currentVersion = $library['currentVersion']['major'] . '.' . $library['currentVersion']['minor'] .'.' . $library['currentVersion']['patch']; $html .= "
    • $libraryName ($currentVersion)
    • "; } - + $html .= '

    These libraries may cause problems on this site. See here for more info
    '; return $html; } - + /** * Get a list of libraries' metadata from h5p.org. Cache it, and refetch once a week. - * + * * @return mixed An object of objects keyed by machineName */ public function getLibrariesMetadata() { // Fetch from cache: $metadata = $this->h5pF->cacheGet('libraries','metadata'); - + // If not available in cache, or older than a week => refetch! if ($metadata === NULL || $metadata->lastTimeFetched < (time() - self::SECONDS_IN_WEEK)) { $platformInfo = $this->h5pF->getPlatformInfo(); $json = file_get_contents('http://h5p.org/libraries-metadata.json?platform=' . json_encode($platformInfo)); - + $metadata = new stdClass(); $metadata->json = ($json === FALSE ? NULL : json_decode($json)); $metadata->lastTimeFetched = time(); - + $this->h5pF->cacheSet('libraries','metadata', $metadata); } - + return $metadata->json; } } @@ -2226,24 +2226,24 @@ class H5PContentValidator { 'select' => 'validateSelect', 'library' => 'validateLibrary', ); - + // 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(); } /** * Get the flat dependecy tree. - * + * * @return array */ public function getDependencies() { return $this->dependencies; } - + /** * Validate given text value against text semantics. */ @@ -2468,19 +2468,19 @@ class H5PContentValidator { $validkeys = array_merge($validkeys, $semantics->extraAttributes); // TODO: Validate extraAttributes } $this->filterParams($file, $validkeys); - + if (isset($file->width)) { $file->width = intval($file->width); } - + if (isset($file->height)) { $file->height = intval($file->height); } - + if (isset($file->codecs)) { $file->codecs = htmlspecialchars($file->codecs, ENT_QUOTES, 'UTF-8', FALSE); } - + if (isset($file->quality)) { if (!is_object($file->quality) || !isset($file->quality->level) || !isset($file->quality->label)) { unset($file->quality); @@ -2491,7 +2491,7 @@ class H5PContentValidator { $file->quality->label = htmlspecialchars($file->quality->label, ENT_QUOTES, 'UTF-8', FALSE); } } - + if (isset($file->copyright)) { $this->validateGroup($file->copyright, H5PContentValidator::getCopyrightSemantics()); } @@ -2594,7 +2594,7 @@ class H5PContentValidator { $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']); @@ -2888,7 +2888,7 @@ class H5PContentValidator { /** * Processes an HTML attribute value and strips dangerous protocols from URLs. - * + * * @param $string * The string with the attribute value. * @param $decode @@ -2961,10 +2961,10 @@ class H5PContentValidator { return $uri; } - + public static function getCopyrightSemantics() { static $semantics; - + if ($semantics === NULL) { $semantics = (object) array( 'name' => 'copyright', @@ -3062,7 +3062,7 @@ class H5PContentValidator { ) ); } - + return $semantics; } } From 0a8bd6a5b5edeff495bd31de0208ba342775dddb Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Fri, 10 Oct 2014 13:57:43 +0200 Subject: [PATCH 21/24] Fixed typos. --- h5p.classes.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/h5p.classes.php b/h5p.classes.php index 72264ec..f6f9855 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -118,7 +118,7 @@ interface H5PFrameworkInterface { public function getAdminUrl(); /** - * Get id to an excisting library + * Get id to an existing library * * @param string $machineName * The librarys machine name @@ -156,7 +156,7 @@ interface H5PFrameworkInterface { * - minorVersion: The librarys minorVersion * - patchVersion: The librarys patchVersion * @return boolean - * TRUE if the library is a patched version of an excisting library + * TRUE if the library is a patched version of an existing library * FALSE otherwise */ public function isPatchedLibrary($library); @@ -305,7 +305,7 @@ interface H5PFrameworkInterface { * Get number of content/nodes using a library, and the number of * dependencies to other libraries * - * @param int $library_id + * @param int $libraryId * Library identifier * @return array * Associative array containing: @@ -493,10 +493,10 @@ interface H5PFrameworkInterface { /** * Get number of contents using library as main library. * - * @param int $library_id + * @param int $libraryId * Identifier for a library */ - public function getNumContent($library_id); + public function getNumContent($libraryId); } /** From a8e6854324f57fdc5afda94b1f55f049656f2f03 Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Tue, 14 Oct 2014 14:06:49 +0200 Subject: [PATCH 22/24] Use correct semantics. --- h5p.classes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h5p.classes.php b/h5p.classes.php index b246771..a6de0d4 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -2403,7 +2403,7 @@ class H5PContentValidator { 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']); + $library['semantics'] = $this->h5pC->loadLibrarySemantics($libspec['machineName'], $libspec['majorVersion'], $libspec['minorVersion']); $this->libraries[$value->library] = $library; // Find all dependencies for this library From 68f1e89ca580e7a8535237befb442ecc42b3c37f Mon Sep 17 00:00:00 2001 From: Frode Petterson Date: Tue, 21 Oct 2014 11:34:14 +0200 Subject: [PATCH 23/24] Removed source map ref. --- js/jquery.js | 1 - 1 file changed, 1 deletion(-) diff --git a/js/jquery.js b/js/jquery.js index c126ed7..14eeb18 100644 --- a/js/jquery.js +++ b/js/jquery.js @@ -1,5 +1,4 @@ /*! jQuery v1.9.1 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license -//@ sourceMappingURL=jquery.min.map */(function(e,t){var n,r,i=typeof t,o=e.document,a=e.location,s=e.jQuery,u=e.$,l={},c=[],p="1.9.1",f=c.concat,d=c.push,h=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=p.trim,b=function(e,t){return new b.fn.init(e,t,r)},x=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,w=/\S+/g,T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,k=/^[\],:{}\s]*$/,E=/(?:^|:|,)(?:\s*\[)+/g,S=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,A=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,j=/^-ms-/,D=/-([\da-z])/gi,L=function(e,t){return t.toUpperCase()},H=function(e){(o.addEventListener||"load"===e.type||"complete"===o.readyState)&&(q(),b.ready())},q=function(){o.addEventListener?(o.removeEventListener("DOMContentLoaded",H,!1),e.removeEventListener("load",H,!1)):(o.detachEvent("onreadystatechange",H),e.detachEvent("onload",H))};b.fn=b.prototype={jquery:p,constructor:b,init:function(e,n,r){var i,a;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof b?n[0]:n,b.merge(this,b.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:o,!0)),C.test(i[1])&&b.isPlainObject(n))for(i in n)b.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(a=o.getElementById(i[2]),a&&a.parentNode){if(a.id!==i[2])return r.find(e);this.length=1,this[0]=a}return this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):b.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),b.makeArray(e,this))},selector:"",length:0,size:function(){return this.length},toArray:function(){return h.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=b.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return b.each(this,e,t)},ready:function(e){return b.ready.promise().done(e),this},slice:function(){return this.pushStack(h.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(b.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:d,sort:[].sort,splice:[].splice},b.fn.init.prototype=b.fn,b.extend=b.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},u=1,l=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},u=2),"object"==typeof s||b.isFunction(s)||(s={}),l===u&&(s=this,--u);l>u;u++)if(null!=(o=arguments[u]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(b.isPlainObject(r)||(n=b.isArray(r)))?(n?(n=!1,a=e&&b.isArray(e)?e:[]):a=e&&b.isPlainObject(e)?e:{},s[i]=b.extend(c,a,r)):r!==t&&(s[i]=r));return s},b.extend({noConflict:function(t){return e.$===b&&(e.$=u),t&&e.jQuery===b&&(e.jQuery=s),b},isReady:!1,readyWait:1,holdReady:function(e){e?b.readyWait++:b.ready(!0)},ready:function(e){if(e===!0?!--b.readyWait:!b.isReady){if(!o.body)return setTimeout(b.ready);b.isReady=!0,e!==!0&&--b.readyWait>0||(n.resolveWith(o,[b]),b.fn.trigger&&b(o).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===b.type(e)},isArray:Array.isArray||function(e){return"array"===b.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[m.call(e)]||"object":typeof e},isPlainObject:function(e){if(!e||"object"!==b.type(e)||e.nodeType||b.isWindow(e))return!1;try{if(e.constructor&&!y.call(e,"constructor")&&!y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||y.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=b.buildFragment([e],t,i),i&&b(i).remove(),b.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=b.trim(n),n&&k.test(n.replace(S,"@").replace(A,"]").replace(E,"")))?Function("return "+n)():(b.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||b.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&b.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(j,"ms-").replace(D,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:v&&!v.call("\ufeff\u00a0")?function(e){return null==e?"":v.call(e)}:function(e){return null==e?"":(e+"").replace(T,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?b.merge(n,"string"==typeof e?[e]:e):d.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(g)return g.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return f.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),b.isFunction(e)?(r=h.call(arguments,2),i=function(){return e.apply(n||this,r.concat(h.call(arguments)))},i.guid=e.guid=e.guid||b.guid++,i):t},access:function(e,n,r,i,o,a,s){var u=0,l=e.length,c=null==r;if("object"===b.type(r)){o=!0;for(u in r)b.access(e,n,u,r[u],!0,a,s)}else if(i!==t&&(o=!0,b.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(b(e),n)})),n))for(;l>u;u++)n(e[u],r,s?i:i.call(e[u],u,n(e[u],r)));return o?e:c?n.call(e):l?n(e[0],r):a},now:function(){return(new Date).getTime()}}),b.ready.promise=function(t){if(!n)if(n=b.Deferred(),"complete"===o.readyState)setTimeout(b.ready);else if(o.addEventListener)o.addEventListener("DOMContentLoaded",H,!1),e.addEventListener("load",H,!1);else{o.attachEvent("onreadystatechange",H),e.attachEvent("onload",H);var r=!1;try{r=null==e.frameElement&&o.documentElement}catch(i){}r&&r.doScroll&&function a(){if(!b.isReady){try{r.doScroll("left")}catch(e){return setTimeout(a,50)}q(),b.ready()}}()}return n.promise(t)},b.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=b.type(e);return b.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=b(o);var _={};function F(e){var t=_[e]={};return b.each(e.match(w)||[],function(e,n){t[n]=!0}),t}b.Callbacks=function(e){e="string"==typeof e?_[e]||F(e):b.extend({},e);var n,r,i,o,a,s,u=[],l=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=u.length,n=!0;u&&o>a;a++)if(u[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,u&&(l?l.length&&c(l.shift()):r?u=[]:p.disable())},p={add:function(){if(u){var t=u.length;(function i(t){b.each(t,function(t,n){var r=b.type(n);"function"===r?e.unique&&p.has(n)||u.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=u.length:r&&(s=t,c(r))}return this},remove:function(){return u&&b.each(arguments,function(e,t){var r;while((r=b.inArray(t,u,r))>-1)u.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?b.inArray(e,u)>-1:!(!u||!u.length)},empty:function(){return u=[],this},disable:function(){return u=l=r=t,this},disabled:function(){return!u},lock:function(){return l=t,r||p.disable(),this},locked:function(){return!l},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!u||i&&!l||(n?l.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},b.extend({Deferred:function(e){var t=[["resolve","done",b.Callbacks("once memory"),"resolved"],["reject","fail",b.Callbacks("once memory"),"rejected"],["notify","progress",b.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return b.Deferred(function(n){b.each(t,function(t,o){var a=o[0],s=b.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&b.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?b.extend(e,r):r}},i={};return r.pipe=r.then,b.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=h.call(arguments),r=n.length,i=1!==r||e&&b.isFunction(e.promise)?r:0,o=1===i?e:b.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?h.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,u,l;if(r>1)for(s=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&b.isFunction(n[t].promise)?n[t].promise().done(a(t,l,n)).fail(o.reject).progress(a(t,u,s)):--i;return i||o.resolveWith(l,n),o.promise()}}),b.support=function(){var t,n,r,a,s,u,l,c,p,f,d=o.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
    a",n=d.getElementsByTagName("*"),r=d.getElementsByTagName("a")[0],!n||!r||!n.length)return{};s=o.createElement("select"),l=s.appendChild(o.createElement("option")),a=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={getSetAttribute:"t"!==d.className,leadingWhitespace:3===d.firstChild.nodeType,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:"/a"===r.getAttribute("href"),opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:!!a.value,optSelected:l.selected,enctype:!!o.createElement("form").enctype,html5Clone:"<:nav>"!==o.createElement("nav").cloneNode(!0).outerHTML,boxModel:"CSS1Compat"===o.compatMode,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},a.checked=!0,t.noCloneChecked=a.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!l.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}a=o.createElement("input"),a.setAttribute("value",""),t.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),t.radioValue="t"===a.value,a.setAttribute("checked","t"),a.setAttribute("name","t"),u=o.createDocumentFragment(),u.appendChild(a),t.appendChecked=a.checked,t.checkClone=u.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;return d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip,b(function(){var n,r,a,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",u=o.getElementsByTagName("body")[0];u&&(n=o.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",u.appendChild(n).appendChild(d),d.innerHTML="
    t
    ",a=d.getElementsByTagName("td"),a[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===a[0].offsetHeight,a[0].style.display="",a[1].style.display="none",t.reliableHiddenOffsets=p&&0===a[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=4===d.offsetWidth,t.doesNotIncludeMarginInBodyOffset=1!==u.offsetTop,e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(o.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
    ",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(u.style.zoom=1)),u.removeChild(n),n=d=a=r=null)}),n=s=u=l=r=a=null,t}();var O=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,B=/([A-Z])/g;function P(e,n,r,i){if(b.acceptData(e)){var o,a,s=b.expando,u="string"==typeof n,l=e.nodeType,p=l?b.cache:e,f=l?e[s]:e[s]&&s;if(f&&p[f]&&(i||p[f].data)||!u||r!==t)return f||(l?e[s]=f=c.pop()||b.guid++:f=s),p[f]||(p[f]={},l||(p[f].toJSON=b.noop)),("object"==typeof n||"function"==typeof n)&&(i?p[f]=b.extend(p[f],n):p[f].data=b.extend(p[f].data,n)),o=p[f],i||(o.data||(o.data={}),o=o.data),r!==t&&(o[b.camelCase(n)]=r),u?(a=o[n],null==a&&(a=o[b.camelCase(n)])):a=o,a}}function R(e,t,n){if(b.acceptData(e)){var r,i,o,a=e.nodeType,s=a?b.cache:e,u=a?e[b.expando]:b.expando;if(s[u]){if(t&&(o=n?s[u]:s[u].data)){b.isArray(t)?t=t.concat(b.map(t,b.camelCase)):t in o?t=[t]:(t=b.camelCase(t),t=t in o?[t]:t.split(" "));for(r=0,i=t.length;i>r;r++)delete o[t[r]];if(!(n?$:b.isEmptyObject)(o))return}(n||(delete s[u].data,$(s[u])))&&(a?b.cleanData([e],!0):b.support.deleteExpando||s!=s.window?delete s[u]:s[u]=null)}}}b.extend({cache:{},expando:"jQuery"+(p+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?b.cache[e[b.expando]]:e[b.expando],!!e&&!$(e)},data:function(e,t,n){return P(e,t,n)},removeData:function(e,t){return R(e,t)},_data:function(e,t,n){return P(e,t,n,!0)},_removeData:function(e,t){return R(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&b.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),b.fn.extend({data:function(e,n){var r,i,o=this[0],a=0,s=null;if(e===t){if(this.length&&(s=b.data(o),1===o.nodeType&&!b._data(o,"parsedAttrs"))){for(r=o.attributes;r.length>a;a++)i=r[a].name,i.indexOf("data-")||(i=b.camelCase(i.slice(5)),W(o,i,s[i]));b._data(o,"parsedAttrs",!0)}return s}return"object"==typeof e?this.each(function(){b.data(this,e)}):b.access(this,function(n){return n===t?o?W(o,e,b.data(o,e)):null:(this.each(function(){b.data(this,e,n)}),t)},null,n,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){b.removeData(this,e)})}});function W(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(B,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:O.test(r)?b.parseJSON(r):r}catch(o){}b.data(e,n,r)}else r=t}return r}function $(e){var t;for(t in e)if(("data"!==t||!b.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}b.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=b._data(e,n),r&&(!i||b.isArray(r)?i=b._data(e,n,b.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=b.queue(e,t),r=n.length,i=n.shift(),o=b._queueHooks(e,t),a=function(){b.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),o.cur=i,i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return b._data(e,n)||b._data(e,n,{empty:b.Callbacks("once memory").add(function(){b._removeData(e,t+"queue"),b._removeData(e,n)})})}}),b.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?b.queue(this[0],e):n===t?this:this.each(function(){var t=b.queue(this,e,n);b._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&b.dequeue(this,e)})},dequeue:function(e){return this.each(function(){b.dequeue(this,e)})},delay:function(e,t){return e=b.fx?b.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=b.Deferred(),a=this,s=this.length,u=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=b._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(u));return u(),o.promise(n)}});var I,z,X=/[\t\r\n]/g,U=/\r/g,V=/^(?:input|select|textarea|button|object)$/i,Y=/^(?:a|area)$/i,J=/^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,G=/^(?:checked|selected)$/i,Q=b.support.getSetAttribute,K=b.support.input;b.fn.extend({attr:function(e,t){return b.access(this,b.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){b.removeAttr(this,e)})},prop:function(e,t){return b.access(this,b.prop,e,t,arguments.length>1)},removeProp:function(e){return e=b.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,u="string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=b.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,u=0===arguments.length||"string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?b.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return b.isFunction(e)?this.each(function(n){b(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,a=0,s=b(this),u=t,l=e.match(w)||[];while(o=l[a++])u=r?u:!s.hasClass(o),s[u?"addClass":"removeClass"](o)}else(n===i||"boolean"===n)&&(this.className&&b._data(this,"__className__",this.className),this.className=this.className||e===!1?"":b._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(X," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=b.isFunction(e),this.each(function(n){var o,a=b(this);1===this.nodeType&&(o=i?e.call(this,n,a.val()):e,null==o?o="":"number"==typeof o?o+="":b.isArray(o)&&(o=b.map(o,function(e){return null==e?"":e+""})),r=b.valHooks[this.type]||b.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=b.valHooks[o.type]||b.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(U,""):null==n?"":n)}}}),b.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,u=0>i?s:o?i:0;for(;s>u;u++)if(n=r[u],!(!n.selected&&u!==i||(b.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&b.nodeName(n.parentNode,"optgroup"))){if(t=b(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n=b.makeArray(t);return b(e).find("option").each(function(){this.selected=b.inArray(b(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attr:function(e,n,r){var o,a,s,u=e.nodeType;if(e&&3!==u&&8!==u&&2!==u)return typeof e.getAttribute===i?b.prop(e,n,r):(a=1!==u||!b.isXMLDoc(e),a&&(n=n.toLowerCase(),o=b.attrHooks[n]||(J.test(n)?z:I)),r===t?o&&a&&"get"in o&&null!==(s=o.get(e,n))?s:(typeof e.getAttribute!==i&&(s=e.getAttribute(n)),null==s?t:s):null!==r?o&&a&&"set"in o&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r):(b.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i++])r=b.propFix[n]||n,J.test(n)?!Q&&G.test(n)?e[b.camelCase("default-"+n)]=e[r]=!1:e[r]=!1:b.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!b.support.radioValue&&"radio"===t&&b.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!b.isXMLDoc(e),a&&(n=b.propFix[n]||n,o=b.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode("tabindex");return n&&n.specified?parseInt(n.value,10):V.test(e.nodeName)||Y.test(e.nodeName)&&e.href?0:t}}}}),z={get:function(e,n){var r=b.prop(e,n),i="boolean"==typeof r&&e.getAttribute(n),o="boolean"==typeof r?K&&Q?null!=i:G.test(n)?e[b.camelCase("default-"+n)]:!!i:e.getAttributeNode(n);return o&&o.value!==!1?n.toLowerCase():t},set:function(e,t,n){return t===!1?b.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&b.propFix[n]||n,n):e[b.camelCase("default-"+n)]=e[n]=!0,n}},K&&Q||(b.attrHooks.value={get:function(e,n){var r=e.getAttributeNode(n);return b.nodeName(e,"input")?e.defaultValue:r&&r.specified?r.value:t},set:function(e,n,r){return b.nodeName(e,"input")?(e.defaultValue=n,t):I&&I.set(e,n,r)}}),Q||(I=b.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&("id"===n||"name"===n||"coords"===n?""!==r.value:r.specified)?r.value:t},set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},b.attrHooks.contenteditable={get:I.get,set:function(e,t,n){I.set(e,""===t?!1:t,n)}},b.each(["width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}})})),b.support.hrefNormalized||(b.each(["href","src","width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return null==r?t:r}})}),b.each(["href","src"],function(e,t){b.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}})),b.support.style||(b.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),b.support.optSelected||(b.propHooks.selected=b.extend(b.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),b.support.enctype||(b.propFix.enctype="encoding"),b.support.checkOn||b.each(["radio","checkbox"],function(){b.valHooks[this]={get:function(e){return null===e.getAttribute("value")?"on":e.value}}}),b.each(["radio","checkbox"],function(){b.valHooks[this]=b.extend(b.valHooks[this],{set:function(e,n){return b.isArray(n)?e.checked=b.inArray(b(e).val(),n)>=0:t}})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}b.event={global:{},add:function(e,n,r,o,a){var s,u,l,c,p,f,d,h,g,m,y,v=b._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=b.guid++),(u=v.events)||(u=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof b===i||e&&b.event.triggered===e.type?t:b.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(w)||[""],l=n.length;while(l--)s=rt.exec(n[l])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),p=b.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=b.event.special[g]||{},d=b.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&b.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=u[g])||(h=u[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),b.event.global[g]=!0;e=null}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,p,f,d,h,g,m=b.hasData(e)&&b._data(e);if(m&&(c=m.events)){t=(t||"").match(w)||[""],l=t.length;while(l--)if(s=rt.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=b.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),u=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));u&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||b.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)b.event.remove(e,d+t[l],n,r,!0);b.isEmptyObject(c)&&(delete m.handle,b._removeData(e,"events"))}},trigger:function(n,r,i,a){var s,u,l,c,p,f,d,h=[i||o],g=y.call(n,"type")?n.type:n,m=y.call(n,"namespace")?n.namespace.split("."):[];if(l=f=i=i||o,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+b.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),u=0>g.indexOf(":")&&"on"+g,n=n[b.expando]?n:new b.Event(g,"object"==typeof n&&n),n.isTrigger=!0,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:b.makeArray(r,[n]),p=b.event.special[g]||{},a||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!a&&!p.noBubble&&!b.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(l=l.parentNode);l;l=l.parentNode)h.push(l),f=l;f===(i.ownerDocument||o)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((l=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(b._data(l,"events")||{})[n.type]&&b._data(l,"handle"),s&&s.apply(l,r),s=u&&l[u],s&&b.acceptData(l)&&s.apply&&s.apply(l,r)===!1&&n.preventDefault();if(n.type=g,!(a||n.isDefaultPrevented()||p._default&&p._default.apply(i.ownerDocument,r)!==!1||"click"===g&&b.nodeName(i,"a")||!b.acceptData(i)||!u||!i[g]||b.isWindow(i))){f=i[u],f&&(i[u]=null),b.event.triggered=g;try{i[g]()}catch(v){}b.event.triggered=t,f&&(i[u]=f)}return n.result}},dispatch:function(e){e=b.event.fix(e);var n,r,i,o,a,s=[],u=h.call(arguments),l=(b._data(this,"events")||{})[e.type]||[],c=b.event.special[e.type]||{};if(u[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=b.event.handlers.call(this,e,l),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((b.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,u),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],u=n.delegateCount,l=e.target;if(u&&l.nodeType&&(!e.button||"click"!==e.type))for(;l!=this;l=l.parentNode||this)if(1===l.nodeType&&(l.disabled!==!0||"click"!==e.type)){for(o=[],a=0;u>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?b(r,this).index(l)>=0:b.find(r,this,null,[l]).length),o[r]&&o.push(i);o.length&&s.push({elem:l,handlers:o})}return n.length>u&&s.push({elem:this,handlers:n.slice(u)}),s},fix:function(e){if(e[b.expando])return e;var t,n,r,i=e.type,a=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new b.Event(a),t=r.length;while(t--)n=r[t],e[n]=a[n];return e.target||(e.target=a.srcElement||o),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,a):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,a,s=n.button,u=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||o,a=i.documentElement,r=i.body,e.pageX=n.clientX+(a&&a.scrollLeft||r&&r.scrollLeft||0)-(a&&a.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(a&&a.scrollTop||r&&r.scrollTop||0)-(a&&a.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&u&&(e.relatedTarget=u===e.target?n.toElement:u),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},click:{trigger:function(){return b.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t}},focus:{trigger:function(){if(this!==o.activeElement&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===o.activeElement&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=b.extend(new b.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?b.event.trigger(i,null,t):b.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},b.removeEvent=o.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},b.Event=function(e,n){return this instanceof b.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&b.extend(this,n),this.timeStamp=e&&e.timeStamp||b.now(),this[b.expando]=!0,t):new b.Event(e,n)},b.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},b.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){b.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj; return(!i||i!==r&&!b.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),b.support.submitBubbles||(b.event.special.submit={setup:function(){return b.nodeName(this,"form")?!1:(b.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=b.nodeName(n,"input")||b.nodeName(n,"button")?n.form:t;r&&!b._data(r,"submitBubbles")&&(b.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),b._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&b.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return b.nodeName(this,"form")?!1:(b.event.remove(this,"._submit"),t)}}),b.support.changeBubbles||(b.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(b.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),b.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),b.event.simulate("change",this,e,!0)})),!1):(b.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!b._data(t,"changeBubbles")&&(b.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||b.event.simulate("change",this.parentNode,e,!0)}),b._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return b.event.remove(this,"._change"),!Z.test(this.nodeName)}}),b.support.focusinBubbles||b.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){b.event.simulate(t,e.target,b.event.fix(e),!0)};b.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),b.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return b().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=b.guid++)),this.each(function(){b.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,b(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){b.event.remove(this,e,r,n)})},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},trigger:function(e,t){return this.each(function(){b.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?b.event.trigger(e,n,r,!0):t}}),function(e,t){var n,r,i,o,a,s,u,l,c,p,f,d,h,g,m,y,v,x="sizzle"+-new Date,w=e.document,T={},N=0,C=0,k=it(),E=it(),S=it(),A=typeof t,j=1<<31,D=[],L=D.pop,H=D.push,q=D.slice,M=D.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},_="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=F.replace("w","w#"),B="([*^$|!~]?=)",P="\\["+_+"*("+F+")"+_+"*(?:"+B+_+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+O+")|)|)"+_+"*\\]",R=":("+F+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+P.replace(3,8)+")*)|.*)\\)|)",W=RegExp("^"+_+"+|((?:^|[^\\\\])(?:\\\\.)*)"+_+"+$","g"),$=RegExp("^"+_+"*,"+_+"*"),I=RegExp("^"+_+"*([\\x20\\t\\r\\n\\f>+~])"+_+"*"),z=RegExp(R),X=RegExp("^"+O+"$"),U={ID:RegExp("^#("+F+")"),CLASS:RegExp("^\\.("+F+")"),NAME:RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:RegExp("^("+F.replace("w","w*")+")"),ATTR:RegExp("^"+P),PSEUDO:RegExp("^"+R),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+_+"*(even|odd|(([+-]|)(\\d*)n|)"+_+"*(?:([+-]|)"+_+"*(\\d+)|))"+_+"*\\)|)","i"),needsContext:RegExp("^"+_+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+_+"*((?:-\\d)?\\d*)"+_+"*\\)|)(?=[^-]|$)","i")},V=/[\x20\t\r\n\f]*[+~]/,Y=/^[^{]+\{\s*\[native code/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,G=/^(?:input|select|textarea|button)$/i,Q=/^h\d$/i,K=/'|\\/g,Z=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,et=/\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,tt=function(e,t){var n="0x"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{q.call(w.documentElement.childNodes,0)[0].nodeType}catch(nt){q=function(e){var t,n=[];while(t=this[e++])n.push(t);return n}}function rt(e){return Y.test(e+"")}function it(){var e,t=[];return e=function(n,r){return t.push(n+=" ")>i.cacheLength&&delete e[t.shift()],e[n]=r}}function ot(e){return e[x]=!0,e}function at(e){var t=p.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}}function st(e,t,n,r){var i,o,a,s,u,l,f,g,m,v;if((t?t.ownerDocument||t:w)!==p&&c(t),t=t||p,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(!d&&!r){if(i=J.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&y(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return H.apply(n,q.call(t.getElementsByTagName(e),0)),n;if((a=i[3])&&T.getByClassName&&t.getElementsByClassName)return H.apply(n,q.call(t.getElementsByClassName(a),0)),n}if(T.qsa&&!h.test(e)){if(f=!0,g=x,m=t,v=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){l=ft(e),(f=t.getAttribute("id"))?g=f.replace(K,"\\$&"):t.setAttribute("id",g),g="[id='"+g+"'] ",u=l.length;while(u--)l[u]=g+dt(l[u]);m=V.test(e)&&t.parentNode||t,v=l.join(",")}if(v)try{return H.apply(n,q.call(m.querySelectorAll(v),0)),n}catch(b){}finally{f||t.removeAttribute("id")}}}return wt(e.replace(W,"$1"),t,n,r)}a=st.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},c=st.setDocument=function(e){var n=e?e.ownerDocument||e:w;return n!==p&&9===n.nodeType&&n.documentElement?(p=n,f=n.documentElement,d=a(n),T.tagNameNoComments=at(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),T.attributes=at(function(e){e.innerHTML="";var t=typeof e.lastChild.getAttribute("multiple");return"boolean"!==t&&"string"!==t}),T.getByClassName=at(function(e){return e.innerHTML="",e.getElementsByClassName&&e.getElementsByClassName("e").length?(e.lastChild.className="e",2===e.getElementsByClassName("e").length):!1}),T.getByName=at(function(e){e.id=x+0,e.innerHTML="
    ",f.insertBefore(e,f.firstChild);var t=n.getElementsByName&&n.getElementsByName(x).length===2+n.getElementsByName(x+0).length;return T.getIdNotName=!n.getElementById(x),f.removeChild(e),t}),i.attrHandle=at(function(e){return e.innerHTML="",e.firstChild&&typeof e.firstChild.getAttribute!==A&&"#"===e.firstChild.getAttribute("href")})?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},T.getIdNotName?(i.find.ID=function(e,t){if(typeof t.getElementById!==A&&!d){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){return e.getAttribute("id")===t}}):(i.find.ID=function(e,n){if(typeof n.getElementById!==A&&!d){var r=n.getElementById(e);return r?r.id===e||typeof r.getAttributeNode!==A&&r.getAttributeNode("id").value===e?[r]:t:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){var n=typeof e.getAttributeNode!==A&&e.getAttributeNode("id");return n&&n.value===t}}),i.find.TAG=T.tagNameNoComments?function(e,n){return typeof n.getElementsByTagName!==A?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},i.find.NAME=T.getByName&&function(e,n){return typeof n.getElementsByName!==A?n.getElementsByName(name):t},i.find.CLASS=T.getByClassName&&function(e,n){return typeof n.getElementsByClassName===A||d?t:n.getElementsByClassName(e)},g=[],h=[":focus"],(T.qsa=rt(n.querySelectorAll))&&(at(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||h.push("\\["+_+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||h.push(":checked")}),at(function(e){e.innerHTML="",e.querySelectorAll("[i^='']").length&&h.push("[*^$]="+_+"*(?:\"\"|'')"),e.querySelectorAll(":enabled").length||h.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),h.push(",.*:")})),(T.matchesSelector=rt(m=f.matchesSelector||f.mozMatchesSelector||f.webkitMatchesSelector||f.oMatchesSelector||f.msMatchesSelector))&&at(function(e){T.disconnectedMatch=m.call(e,"div"),m.call(e,"[s!='']:x"),g.push("!=",R)}),h=RegExp(h.join("|")),g=RegExp(g.join("|")),y=rt(f.contains)||f.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},v=f.compareDocumentPosition?function(e,t){var r;return e===t?(u=!0,0):(r=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t))?1&r||e.parentNode&&11===e.parentNode.nodeType?e===n||y(w,e)?-1:t===n||y(w,t)?1:0:4&r?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return u=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:0;if(o===a)return ut(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?ut(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},u=!1,[0,0].sort(v),T.detectDuplicates=u,p):p},st.matches=function(e,t){return st(e,null,null,t)},st.matchesSelector=function(e,t){if((e.ownerDocument||e)!==p&&c(e),t=t.replace(Z,"='$1']"),!(!T.matchesSelector||d||g&&g.test(t)||h.test(t)))try{var n=m.call(e,t);if(n||T.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return st(t,p,null,[e]).length>0},st.contains=function(e,t){return(e.ownerDocument||e)!==p&&c(e),y(e,t)},st.attr=function(e,t){var n;return(e.ownerDocument||e)!==p&&c(e),d||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):d||T.attributes?e.getAttribute(t):((n=e.getAttributeNode(t))||e.getAttribute(t))&&e[t]===!0?t:n&&n.specified?n.value:null},st.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},st.uniqueSort=function(e){var t,n=[],r=1,i=0;if(u=!T.detectDuplicates,e.sort(v),u){for(;t=e[r];r++)t===e[r-1]&&(i=n.push(r));while(i--)e.splice(n[i],1)}return e};function ut(e,t){var n=t&&e,r=n&&(~t.sourceIndex||j)-(~e.sourceIndex||j);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function lt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function ct(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function pt(e){return ot(function(t){return t=+t,ot(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}o=st.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=o(t);return n},i=st.selectors={cacheLength:50,createPseudo:ot,match:U,find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(et,tt),e[3]=(e[4]||e[5]||"").replace(et,tt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||st.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&st.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return U.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&z.test(n)&&(t=ft(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){return"*"===e?function(){return!0}:(e=e.replace(et,tt).toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[e+" "];return t||(t=RegExp("(^|"+_+")"+e+"("+_+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==A&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=st.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[x]||(m[x]={}),l=c[e]||[],d=l[0]===N&&l[1],f=l[0]===N&&l[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[N,d,f];break}}else if(v&&(l=(t[x]||(t[x]={}))[e])&&l[0]===N)f=l[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[x]||(p[x]={}))[e]=[N,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||st.error("unsupported pseudo: "+e);return r[x]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?ot(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=M.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ot(function(e){var t=[],n=[],r=s(e.replace(W,"$1"));return r[x]?ot(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ot(function(e){return function(t){return st(e,t).length>0}}),contains:ot(function(e){return function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:ot(function(e){return X.test(e||"")||st.error("unsupported lang: "+e),e=e.replace(et,tt).toLowerCase(),function(t){var n;do if(n=d?t.getAttribute("xml:lang")||t.getAttribute("lang"):t.lang)return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===f},focus:function(e){return e===p.activeElement&&(!p.hasFocus||p.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!i.pseudos.empty(e)},header:function(e){return Q.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:pt(function(){return[0]}),last:pt(function(e,t){return[t-1]}),eq:pt(function(e,t,n){return[0>n?n+t:n]}),even:pt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:pt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:pt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:pt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})i.pseudos[n]=lt(n);for(n in{submit:!0,reset:!0})i.pseudos[n]=ct(n);function ft(e,t){var n,r,o,a,s,u,l,c=E[e+" "];if(c)return t?0:c.slice(0);s=e,u=[],l=i.preFilter;while(s){(!n||(r=$.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),u.push(o=[])),n=!1,(r=I.exec(s))&&(n=r.shift(),o.push({value:n,type:r[0].replace(W," ")}),s=s.slice(n.length));for(a in i.filter)!(r=U[a].exec(s))||l[a]&&!(r=l[a](r))||(n=r.shift(),o.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?st.error(e):E(e,u).slice(0)}function dt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function ht(e,t,n){var i=t.dir,o=n&&"parentNode"===i,a=C++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,n,s){var u,l,c,p=N+" "+a;if(s){while(t=t[i])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[x]||(t[x]={}),(l=c[i])&&l[0]===p){if((u=l[1])===!0||u===r)return u===!0}else if(l=c[i]=[p],l[1]=e(t,n,s)||r,l[1]===!0)return!0}}function gt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function mt(e,t,n,r,i){var o,a=[],s=0,u=e.length,l=null!=t;for(;u>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),l&&t.push(s));return a}function yt(e,t,n,r,i,o){return r&&!r[x]&&(r=yt(r)),i&&!i[x]&&(i=yt(i,o)),ot(function(o,a,s,u){var l,c,p,f=[],d=[],h=a.length,g=o||xt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:mt(g,f,e,s,u),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,u),r){l=mt(y,d),r(l,[],s,u),c=l.length;while(c--)(p=l[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(p=y[c])&&l.push(m[c]=p);i(null,y=[],l,u)}c=y.length;while(c--)(p=y[c])&&(l=i?M.call(o,p):f[c])>-1&&(o[l]=!(a[l]=p))}}else y=mt(y===a?y.splice(h,y.length):y),i?i(null,a,y,u):H.apply(a,y)})}function vt(e){var t,n,r,o=e.length,a=i.relative[e[0].type],s=a||i.relative[" "],u=a?1:0,c=ht(function(e){return e===t},s,!0),p=ht(function(e){return M.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;o>u;u++)if(n=i.relative[e[u].type])f=[ht(gt(f),n)];else{if(n=i.filter[e[u].type].apply(null,e[u].matches),n[x]){for(r=++u;o>r;r++)if(i.relative[e[r].type])break;return yt(u>1&>(f),u>1&&dt(e.slice(0,u-1)).replace(W,"$1"),n,r>u&&vt(e.slice(u,r)),o>r&&vt(e=e.slice(r)),o>r&&dt(e))}f.push(n)}return gt(f)}function bt(e,t){var n=0,o=t.length>0,a=e.length>0,s=function(s,u,c,f,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,T=l,C=s||a&&i.find.TAG("*",d&&u.parentNode||u),k=N+=null==T?1:Math.random()||.1;for(w&&(l=u!==p&&u,r=n);null!=(h=C[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,u,c)){f.push(h);break}w&&(N=k,r=++n)}o&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,o&&b!==v){g=0;while(m=t[g++])m(x,y,u,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=L.call(f));y=mt(y)}H.apply(f,y),w&&!s&&y.length>0&&v+t.length>1&&st.uniqueSort(f)}return w&&(N=k,l=T),x};return o?ot(s):s}s=st.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=ft(e)),n=t.length;while(n--)o=vt(t[n]),o[x]?r.push(o):i.push(o);o=S(e,bt(i,r))}return o};function xt(e,t,n){var r=0,i=t.length;for(;i>r;r++)st(e,t[r],n);return n}function wt(e,t,n,r){var o,a,u,l,c,p=ft(e);if(!r&&1===p.length){if(a=p[0]=p[0].slice(0),a.length>2&&"ID"===(u=a[0]).type&&9===t.nodeType&&!d&&i.relative[a[1].type]){if(t=i.find.ID(u.matches[0].replace(et,tt),t)[0],!t)return n;e=e.slice(a.shift().value.length)}o=U.needsContext.test(e)?0:a.length;while(o--){if(u=a[o],i.relative[l=u.type])break;if((c=i.find[l])&&(r=c(u.matches[0].replace(et,tt),V.test(a[0].type)&&t.parentNode||t))){if(a.splice(o,1),e=r.length&&dt(a),!e)return H.apply(n,q.call(r,0)),n;break}}}return s(e,p)(r,t,d,n,V.test(e)),n}i.pseudos.nth=i.pseudos.eq;function Tt(){}i.filters=Tt.prototype=i.pseudos,i.setFilters=new Tt,c(),st.attr=b.attr,b.find=st,b.expr=st.selectors,b.expr[":"]=b.expr.pseudos,b.unique=st.uniqueSort,b.text=st.getText,b.isXMLDoc=st.isXML,b.contains=st.contains}(e);var at=/Until$/,st=/^(?:parents|prev(?:Until|All))/,ut=/^.[^:#\[\.,]*$/,lt=b.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};b.fn.extend({find:function(e){var t,n,r,i=this.length;if("string"!=typeof e)return r=this,this.pushStack(b(e).filter(function(){for(t=0;i>t;t++)if(b.contains(r[t],this))return!0}));for(n=[],t=0;i>t;t++)b.find(e,this[t],n);return n=this.pushStack(i>1?b.unique(n):n),n.selector=(this.selector?this.selector+" ":"")+e,n},has:function(e){var t,n=b(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(b.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e,!1))},filter:function(e){return this.pushStack(ft(this,e,!0))},is:function(e){return!!e&&("string"==typeof e?lt.test(e)?b(e,this.context).index(this[0])>=0:b.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,o=[],a=lt.test(e)||"string"!=typeof e?b(e,t||this.context):0;for(;i>r;r++){n=this[r];while(n&&n.ownerDocument&&n!==t&&11!==n.nodeType){if(a?a.index(n)>-1:b.find.matchesSelector(n,e)){o.push(n);break}n=n.parentNode}}return this.pushStack(o.length>1?b.unique(o):o)},index:function(e){return e?"string"==typeof e?b.inArray(this[0],b(e)):b.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?b(e,t):b.makeArray(e&&e.nodeType?[e]:e),r=b.merge(this.get(),n);return this.pushStack(b.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),b.fn.andSelf=b.fn.addBack;function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}b.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return b.dir(e,"parentNode")},parentsUntil:function(e,t,n){return b.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return b.dir(e,"nextSibling")},prevAll:function(e){return b.dir(e,"previousSibling")},nextUntil:function(e,t,n){return b.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return b.dir(e,"previousSibling",n)},siblings:function(e){return b.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return b.sibling(e.firstChild)},contents:function(e){return b.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:b.merge([],e.childNodes)}},function(e,t){b.fn[e]=function(n,r){var i=b.map(this,t,n);return at.test(e)||(r=n),r&&"string"==typeof r&&(i=b.filter(r,i)),i=this.length>1&&!ct[e]?b.unique(i):i,this.length>1&&st.test(e)&&(i=i.reverse()),this.pushStack(i)}}),b.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),1===t.length?b.find.matchesSelector(t[0],e)?[t[0]]:[]:b.find.matches(e,t)},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!b(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(t=t||0,b.isFunction(t))return b.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return b.grep(e,function(e){return e===t===n});if("string"==typeof t){var r=b.grep(e,function(e){return 1===e.nodeType});if(ut.test(t))return b.filter(t,r,!n);t=b.filter(t,r)}return b.grep(e,function(e){return b.inArray(e,t)>=0===n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
    ","
    "],area:[1,"",""],param:[1,"",""],thead:[1,"","
    "],tr:[2,"","
    "],col:[2,"","
    "],td:[3,"","
    "],_default:b.support.htmlSerialize?[0,"",""]:[1,"X
    ","
    "]},jt=dt(o),Dt=jt.appendChild(o.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,b.fn.extend({text:function(e){return b.access(this,function(e){return e===t?b.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(b.isFunction(e))return this.each(function(t){b(this).wrapAll(e.call(this,t))});if(this[0]){var t=b(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return b.isFunction(e)?this.each(function(t){b(this).wrapInner(e.call(this,t))}):this.each(function(){var t=b(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=b.isFunction(e);return this.each(function(n){b(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){b.nodeName(this,"body")||b(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.insertBefore(e,this.firstChild)})},before:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=0;for(;null!=(n=this[r]);r++)(!e||b.filter(e,[n]).length>0)&&(t||1!==n.nodeType||b.cleanData(Ot(n)),n.parentNode&&(t&&b.contains(n.ownerDocument,n)&&Mt(Ot(n,"script")),n.parentNode.removeChild(n)));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&b.cleanData(Ot(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&b.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return b.clone(this,e,t)})},html:function(e){return b.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!b.support.htmlSerialize&&mt.test(e)||!b.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(b.cleanData(Ot(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){var t=b.isFunction(e);return t||"string"==typeof e||(e=b(e).not(this).detach()),this.domManip([e],!0,function(e){var t=this.nextSibling,n=this.parentNode;n&&(b(this).remove(),n.insertBefore(e,t))})},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=f.apply([],e);var i,o,a,s,u,l,c=0,p=this.length,d=this,h=p-1,g=e[0],m=b.isFunction(g);if(m||!(1>=p||"string"!=typeof g||b.support.checkClone)&&Ct.test(g))return this.each(function(i){var o=d.eq(i);m&&(e[0]=g.call(this,i,n?o.html():t)),o.domManip(e,n,r)});if(p&&(l=b.buildFragment(e,this[0].ownerDocument,!1,this),i=l.firstChild,1===l.childNodes.length&&(l=i),i)){for(n=n&&b.nodeName(i,"tr"),s=b.map(Ot(l,"script"),Ht),a=s.length;p>c;c++)o=l,c!==h&&(o=b.clone(o,!0,!0),a&&b.merge(s,Ot(o,"script"))),r.call(n&&b.nodeName(this[c],"table")?Lt(this[c],"tbody"):this[c],o,c);if(a)for(u=s[s.length-1].ownerDocument,b.map(s,qt),c=0;a>c;c++)o=s[c],kt.test(o.type||"")&&!b._data(o,"globalEval")&&b.contains(u,o)&&(o.src?b.ajax({url:o.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):b.globalEval((o.text||o.textContent||o.innerHTML||"").replace(St,"")));l=i=null}return this}});function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function Ht(e){var t=e.getAttributeNode("type");return e.type=(t&&t.specified)+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function Mt(e,t){var n,r=0;for(;null!=(n=e[r]);r++)b._data(n,"globalEval",!t||b._data(t[r],"globalEval"))}function _t(e,t){if(1===t.nodeType&&b.hasData(e)){var n,r,i,o=b._data(e),a=b._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)b.event.add(t,n,s[n][r])}a.data&&(a.data=b.extend({},a.data))}}function Ft(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!b.support.noCloneEvent&&t[b.expando]){i=b._data(t);for(r in i.events)b.removeEvent(t,r,i.handle);t.removeAttribute(b.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),b.support.html5Clone&&e.innerHTML&&!b.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Nt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}b.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){b.fn[e]=function(e){var n,r=0,i=[],o=b(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),b(o[r])[t](n),d.apply(i,n.get());return this.pushStack(i)}});function Ot(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||b.nodeName(o,n)?s.push(o):b.merge(s,Ot(o,n));return n===t||n&&b.nodeName(e,n)?b.merge([e],s):s}function Bt(e){Nt.test(e.type)&&(e.defaultChecked=e.checked)}b.extend({clone:function(e,t,n){var r,i,o,a,s,u=b.contains(e.ownerDocument,e);if(b.support.html5Clone||b.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(b.support.noCloneEvent&&b.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||b.isXMLDoc(e)))for(r=Ot(o),s=Ot(e),a=0;null!=(i=s[a]);++a)r[a]&&Ft(i,r[a]);if(t)if(n)for(s=s||Ot(e),r=r||Ot(o),a=0;null!=(i=s[a]);a++)_t(i,r[a]);else _t(e,o);return r=Ot(o,"script"),r.length>0&&Mt(r,!u&&Ot(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,u,l,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===b.type(o))b.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),u=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[u]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!b.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!b.support.tbody){o="table"!==u||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)b.nodeName(l=o.childNodes[i],"tbody")&&!l.childNodes.length&&o.removeChild(l) }b.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),b.support.appendChecked||b.grep(Ot(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===b.inArray(o,r))&&(a=b.contains(o.ownerDocument,o),s=Ot(f.appendChild(o),"script"),a&&Mt(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,u=b.expando,l=b.cache,p=b.support.deleteExpando,f=b.event.special;for(;null!=(n=e[s]);s++)if((t||b.acceptData(n))&&(o=n[u],a=o&&l[o])){if(a.events)for(r in a.events)f[r]?b.event.remove(n,r):b.removeEvent(n,r,a.handle);l[o]&&(delete l[o],p?delete n[u]:typeof n.removeAttribute!==i?n.removeAttribute(u):n[u]=null,c.push(o))}}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+x+")(.*)$","i"),Yt=RegExp("^("+x+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+x+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===b.css(e,"display")||!b.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=b._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=b._data(r,"olddisplay",un(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&b._data(r,"olddisplay",i?n:b.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}b.fn.extend({css:function(e,n){return b.access(this,function(e,n,r){var i,o,a={},s=0;if(b.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=b.css(e,n[s],!1,o);return a}return r!==t?b.style(e,n,r):b.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:nn(this))?b(this).show():b(this).hide()})}}),b.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":b.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,u=b.camelCase(n),l=e.style;if(n=b.cssProps[u]||(b.cssProps[u]=tn(l,u)),s=b.cssHooks[n]||b.cssHooks[u],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:l[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(b.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||b.cssNumber[u]||(r+="px"),b.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(l[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{l[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,u=b.camelCase(n);return n=b.cssProps[u]||(b.cssProps[u]=tn(e.style,u)),s=b.cssHooks[n]||b.cssHooks[u],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||b.isNumeric(o)?o||0:a):a},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s.getPropertyValue(n)||s[n]:t,l=e.style;return s&&(""!==u||b.contains(e.ownerDocument,e)||(u=b.style(e,n)),Yt.test(u)&&Ut.test(n)&&(i=l.width,o=l.minWidth,a=l.maxWidth,l.minWidth=l.maxWidth=l.width=u,u=s.width,l.width=i,l.minWidth=o,l.maxWidth=a)),u}):o.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s[n]:t,l=e.style;return null==u&&l&&l[n]&&(u=l[n]),Yt.test(u)&&!zt.test(n)&&(i=l.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),l.left="fontSize"===n?"1em":u,u=l.pixelLeft+"px",l.left=i,a&&(o.left=a)),""===u?"auto":u});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=b.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=b.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=b.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=b.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=b.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=b.support.boxSizing&&"border-box"===b.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(b.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function un(e){var t=o,n=Gt[e];return n||(n=ln(e,t),"none"!==n&&n||(Pt=(Pt||b("