diff --git a/fonts/h5p-core-26.eot b/fonts/h5p-core-26.eot new file mode 100644 index 0000000..b6fdc50 Binary files /dev/null and b/fonts/h5p-core-26.eot differ diff --git a/fonts/h5p-core-26.svg b/fonts/h5p-core-26.svg new file mode 100644 index 0000000..ba2be6f --- /dev/null +++ b/fonts/h5p-core-26.svg @@ -0,0 +1,112 @@ + + + + + + +{ + "fontFamily": "h5p-core-26", + "description": "Font generated by IcoMoon.", + "majorVersion": 1, + "minorVersion": 1, + "copyright": "H5P", + "fontURL": "", + "version": "Version 1.1", + "fontId": "h5p-core-26", + "psName": "h5p-core-26", + "subFamily": "Regular", + "fullName": "h5p-core-26" +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/h5p-core-26.ttf b/fonts/h5p-core-26.ttf new file mode 100644 index 0000000..3c08638 Binary files /dev/null and b/fonts/h5p-core-26.ttf differ diff --git a/fonts/h5p-core-26.woff b/fonts/h5p-core-26.woff new file mode 100644 index 0000000..eb6df51 Binary files /dev/null and b/fonts/h5p-core-26.woff differ diff --git a/fonts/h5p-hub-publish.eot b/fonts/h5p-hub-publish.eot new file mode 100644 index 0000000..6afdd28 Binary files /dev/null and b/fonts/h5p-hub-publish.eot differ diff --git a/fonts/h5p-hub-publish.svg b/fonts/h5p-hub-publish.svg new file mode 100644 index 0000000..e32badd --- /dev/null +++ b/fonts/h5p-hub-publish.svg @@ -0,0 +1,38 @@ + + + + + + +{ + "fontFamily": "h5p-hub", + "description": "Font generated by IcoMoon.", + "majorVersion": 1, + "minorVersion": 3, + "version": "Version 1.3", + "fontId": "h5p-hub", + "psName": "h5p-hub", + "subFamily": "Regular", + "fullName": "h5p-hub" +} + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/h5p-hub-publish.ttf b/fonts/h5p-hub-publish.ttf new file mode 100644 index 0000000..d1d40dd Binary files /dev/null and b/fonts/h5p-hub-publish.ttf differ diff --git a/fonts/h5p-hub-publish.woff b/fonts/h5p-hub-publish.woff new file mode 100644 index 0000000..c053534 Binary files /dev/null and b/fonts/h5p-hub-publish.woff differ diff --git a/h5p-metadata.class.php b/h5p-metadata.class.php index 572cfd0..5b045aa 100644 --- a/h5p-metadata.class.php +++ b/h5p-metadata.class.php @@ -9,6 +9,10 @@ abstract class H5PMetadata { 'type' => 'text', 'maxLength' => 255 ), + 'a11yTitle' => array( + 'type' => 'text', + 'maxLength' => 255, + ), 'authors' => array( 'type' => 'json' ), @@ -57,6 +61,7 @@ abstract class H5PMetadata { // Note: deliberatly creating JSON string "manually" to improve performance return '{"title":' . (isset($content->title) ? json_encode($content->title) : 'null') . + ',"a11yTitle":' . (isset($content->a11y_title) ? $content->a11y_title : 'null') . ',"authors":' . (isset($content->authors) ? $content->authors : 'null') . ',"source":' . (isset($content->source) ? '"' . $content->source . '"' : 'null') . ',"license":' . (isset($content->license) ? '"' . $content->license . '"' : 'null') . diff --git a/h5p.classes.php b/h5p.classes.php index a610431..96252fa 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -623,6 +623,44 @@ interface H5PFrameworkInterface { * @return boolean */ public function libraryHasUpgrade($library); + + /** + * Replace content hub metadata cache + * + * @param JsonSerializable $metadata Metadata as received from content hub + * @param string $lang Language in ISO 639-1 + * + * @return mixed + */ + public function replaceContentHubMetadataCache($metadata, $lang); + + /** + * Get content hub metadata cache from db + * + * @param string $lang Language code in ISO 639-1 + * + * @return JsonSerializable Json string + */ + public function getContentHubMetadataCache($lang = 'en'); + + /** + * Get time of last content hub metadata check + * + * @param string $lang Language code iin ISO 639-1 format + * + * @return string|null Time in RFC7231 format + */ + public function getContentHubMetadataChecked($lang = 'en'); + + /** + * Set time of last content hub metadata check + * + * @param int|null $time Time in RFC7231 format + * @param string $lang Language code iin ISO 639-1 format + * + * @return bool True if successful + */ + public function setContentHubMetadataChecked($time, $lang = 'en'); } /** @@ -3297,6 +3335,72 @@ class H5PCore { return $data; } + /** + * Update content hub metadata cache + */ + public function updateContentHubMetadataCache($lang = 'en') { + $url = H5PHubEndpoints::createURL(H5PHubEndpoints::METADATA); + $lastModified = $this->h5pF->getContentHubMetadataChecked($lang); + + $headers = array(); + if (!empty($lastModified)) { + $headers['If-Modified-Since'] = $lastModified; + } + $data = $this->h5pF->fetchExternalData("{$url}?lang={$lang}", NULL, TRUE, NULL, TRUE, $headers, NULL, 'GET'); + $lastChecked = new DateTime('now', new DateTimeZone('GMT')); + + if ($data['status'] !== 200 && $data['status'] !== 304) { + // If this was not a success, set the error message and return + $this->h5pF->setErrorMessage( + $this->h5pF->t('No metadata was received from the H5P Hub. Please try again later.') + ); + return null; + } + + // Update timestamp + $this->h5pF->setContentHubMetadataChecked($lastChecked->getTimestamp(), $lang); + + // Not modified + if ($data['status'] === 304) { + return null; + } + $this->h5pF->replaceContentHubMetadataCache($data['data'], $lang); + // TODO: If 200 should we have checked if it decodes? Or 'success'? Not sure if necessary though + return $data['data']; + } + + /** + * Get updated content hub metadata cache + * + * @param string $lang Language as ISO 639-1 code + * + * @return JsonSerializable|string + */ + public function getUpdatedContentHubMetadataCache($lang = 'en') { + $lastUpdate = $this->h5pF->getContentHubMetadataChecked($lang); + if (!$lastUpdate) { + return $this->updateContentHubMetadataCache($lang); + } + + $lastUpdate = new DateTime($lastUpdate); + $expirationTime = $lastUpdate->getTimestamp() + (60 * 60 * 24); // Check once per day + if (time() > $expirationTime) { + $update = $this->updateContentHubMetadataCache($lang); + if (!empty($update)) { + return $update; + } + } + + $storedCache = $this->h5pF->getContentHubMetadataCache($lang); + if (!$storedCache) { + // We don't have the value stored for some reason, reset last update and re-fetch + $this->h5pF->setContentHubMetadataChecked(null, $lang); + return $this->updateContentHubMetadataCache($lang); + } + + return $storedCache; + } + /** * Check if the current server setup is valid and set error messages * @@ -3505,8 +3609,241 @@ class H5PCore { 'offlineDialogRetryMessage' => $this->h5pF->t('Retrying in :num....'), 'offlineDialogRetryButtonLabel' => $this->h5pF->t('Retry now'), 'offlineSuccessfulSubmit' => $this->h5pF->t('Successfully submitted results.'), + 'mainTitle' => $this->h5pF->t('Sharing :title'), + 'cancel' => $this->h5pF->t('Cancel'), + 'back' => $this->h5pF->t('Back'), + 'next' => $this->h5pF->t('Next'), + 'reviewInfo' => $this->h5pF->t('Review info'), + 'share' => $this->h5pF->t('Share'), + 'close' => $this->h5pF->t('Close'), + 'registerOnHub' => $this->h5pF->t('Register on the Hub'), + 'requiredInfo' => $this->h5pF->t('Required Info'), + 'optionalInfo' => $this->h5pF->t('Optional Info'), + 'reviewAndShare' => $this->h5pF->t('Review & Share'), + 'shared' => $this->h5pF->t('Shared'), + 'currentStep' => $this->h5pF->t('Step :step of :total'), + 'sharingNote' => $this->h5pF->t('All content details can be edited after sharing'), + 'licenseDescription' => $this->h5pF->t('Select a license for your content'), + 'licenseVersion' => $this->h5pF->t('License Version'), + 'licenseVersionDescription' => $this->h5pF->t('Select a license version'), + 'disciplineLabel' => $this->h5pF->t('Disciplines'), + 'disciplineDescription' => $this->h5pF->t('You can select multiple disciplines'), + 'discipline' => array( + 'searchPlaceholder' => $this->h5pF->t('Select one or more disciplines'), + 'in' => $this->h5pF->t('in'), + 'dropdownButton' => $this->h5pF->t('Dropdown button'), + ), + 'removeChip' => $this->h5pF->t('Remove :chip from the list'), + 'keywordsPlaceholder' => $this->h5pF->t('Add keywords'), + 'keywords' => $this->h5pF->t('Keywords'), + 'keywordsDescription' => $this->h5pF->t('You can add multiple keywords. Press "Enter" to confirm each keyword'), + 'altText' => $this->h5pF->t('Alt text'), + 'reviewMessage' => $this->h5pF->t('Please review the info below before you share'), + 'subContentWarning' => $this->h5pF->t('Sub-content (images, questions etc.) will be shared under :license unless otherwise specified in the authoring tool'), + 'title' => $this->h5pF->t('Title'), + 'license' => $this->h5pF->t('License'), + 'disciplines' => $this->h5pF->t('Disciplines'), + 'shortDescription' => $this->h5pF->t('Short description'), + 'longDescription' => $this->h5pF->t('Long description'), + 'icon' => $this->h5pF->t('Icon'), + 'screenshots' => $this->h5pF->t('Screenshots'), + 'helpChoosingLicense' => $this->h5pF->t('Help me choose a license'), + 'shareFailed' => $this->h5pF->t('Share failed.'), + 'shareTryAgain' => $this->h5pF->t('Something went wrong, please try to share agian.'), + 'pleaseWait' => $this->h5pF->t('Please wait...'), + 'language' => $this->h5pF->t('Language'), + 'level' => $this->h5pF->t('Level'), + 'shortDescriptionPlaceholder' => $this->h5pF->t('Short description of your content'), + 'longDescriptionPlaceholder' => $this->h5pF->t('Long description of your content'), + 'description' => $this->h5pF->t('Description'), + 'iconDescription' => $this->h5pF->t('640x480px. If not selected content will use category icon'), + 'screenshotsDescription' => $this->h5pF->t('Add up to five screenshots of your content'), + 'submitted' => $this->h5pF->t('Submitted!'), + 'isNowSubmitted' => $this->h5pF->t('Is now submitted to H5P Hub'), + 'contentAvailable' => $this->h5pF->t('Your content will soon be available for download'), + 'contentLicenseTitle' => $this->h5pF->t('Content License Info'), + 'licenseDialogDescription' => $this->h5pF->t('Click on a specific license to get info about proper usage'), + 'publisherFieldTitle' => $this->h5pF->t('Publisher'), + 'publisherFieldDescription' => $this->h5pF->t('This will display as the "Publisher name" on shared content'), + 'emailAddress' => $this->h5pF->t('Email Address'), + 'publisherDescription' => $this->h5pF->t('Publisher description'), + 'publisherDescriptionText' => $this->h5pF->t('This will be displayed under "Publisher info" on shared content'), + 'contactPerson' => $this->h5pF->t('Contact Person'), + 'phone' => $this->h5pF->t('Phone'), + 'address' => $this->h5pF->t('Address'), + 'city' => $this->h5pF->t('City'), + 'zip' => $this->h5pF->t('Zip'), + 'country' => $this->h5pF->t('Country'), + 'logoUploadText' => $this->h5pF->t('Organization logo or avatar'), + 'acceptTerms' => $this->h5pF->t('I accept the terms of the license agreement'), + 'successfullyRegistred' => $this->h5pF->t('You have successfully registered an account on the H5P Hub'), + 'successfullyRegistredDescription' => $this->h5pF->t('You account details can be changed'), + 'accountDetailsLinkText' => $this->h5pF->t('here'), + 'registrationTitle' => $this->h5pF->t('Hub Registration and End User License Agreement (EULA)'), + 'registrationFailed' => $this->h5pF->t('An error occured'), + 'registrationFailedDescription' => $this->h5pF->t('We were not able to create an account at this point. Something went wrong. Try again later.'), + 'maxLength' => $this->h5pF->t(':length is the maximum number of characters'), + 'keywordExists' => $this->h5pF->t('Keyword already exists!'), + 'licenseDetails' => $this->h5pF->t('License details'), ); } + + /** + * Publish content on the H5P Hub. + * + * @param array $data Data from content publishing process + * @param array $files Files to upload with the content publish + * @return stdClass + */ + public function hubPublishContent($data, $files) { + $headers = array( + 'Authorization' => $this->hubGetAuthorizationHeader(), + 'Accept' => 'application/json', + ); + + $response = $this->h5pF->fetchExternalData( + H5PHubEndpoints::createURL(H5PHubEndpoints::CONTENT), + $data, TRUE, NULL, TRUE, $headers, $files + ); + + if (empty($response['data'])) { + throw new Exception($this->h5pF->t('Unable to authorize with the H5P Hub. Please check your Hub registration and connection.')); + } + + $result = json_decode($response['data']); + if (isset($result->success) && $result->success === TRUE) { + return $result; + } + elseif (!empty($result->errors)) { + // Relay any error messages + $e = new Exception($this->h5pF->t('Validation failed.')); + $e->errors = $result->errors; + throw $e; + } + } + + /** + * Creates the authorization header needed to access the private parts of + * the H5P Hub. + * + * @return string + */ + public function hubGetAuthorizationHeader() { + $site_uuid = $this->h5pF->getOption('site_uuid', ''); + $hub_secret = $this->h5pF->getOption('hub_secret', ''); + if (empty($site_uuid)) { + throw new Exception($this->h5pF->t('Missing Site UUID. Please check your Hub registration.')); + } + if (empty($hub_secret)) { + throw new Exception($this->h5pF->t('Missing Hub Secret. Please check your Hub registration.')); + } + return 'Basic ' . base64_encode("$site_uuid:$hub_secret"); + } + + /** + * Unpublish content from content hub + * + * @param integer $hubId Content hub id + * @param string $token CSRF token + * + * @return bool True if successful + */ + public function hubUnpublishContent($hubId, $token) { + if (!self::validToken('content_hub_token', $token)) { + $msg = $this->h5pF->t('Could not unpublish content because token was invalid. Please try again.'); + $this->h5pF->setErrorMessage($msg); + + return false; + } + + $headers = array( + 'Authorization' => $this->hubGetAuthorizationHeader(), + 'Accept' => 'application/json', + ); + + $url = H5PHubEndpoints::createURL(H5PHubEndpoints::CONTENT); + $response = $this->h5pF->fetchExternalData("{$url}/{$hubId}", array( + 'published' => '0', + ), true, null, true, $headers, array(), 'PUT'); + + // Remove shared status if successful + if (!empty($response) && $response['status'] === 200) { + $msg = $this->h5pF->t('Content successfully unpublished'); + $this->h5pF->setInfoMessage($msg); + + return true; + } + $msg = $this->h5pF->t('Content unpublish failed'); + $this->h5pF->setErrorMessage($msg); + + return false; + } + + /** + * Sync content with content hub + * + * @param integer $hubId Content hub id + * @param string $token CSRF token + * @param string $exportPath Export path where .h5p for content can be found + * + * @return bool + */ + public function hubSyncContent($hubId, $token, $exportPath) { + if (!self::validToken('content_hub_token', $token)) { + $msg = $this->h5pF->t('Could not sync content because token was invalid. Please try again.'); + $this->h5pF->setErrorMessage($msg); + + return false; + } + + $headers = array( + 'Authorization' => $this->hubGetAuthorizationHeader(), + 'Accept' => 'application/json', + ); + + $url = H5PHubEndpoints::createURL(H5PHubEndpoints::CONTENT); + $response = $this->h5pF->fetchExternalData("{$url}/{$hubId}", array( + 'download_url' => $exportPath, + ), true, null, true, $headers, array(), 'PUT'); + + if (!empty($response) && $response['status'] === 200) { + $msg = $this->h5pF->t('Content sync queued'); + $this->h5pF->setInfoMessage($msg); + return true; + } + + $msg = $this->h5pF->t('Content sync failed'); + $this->h5pF->setErrorMessage($msg); + return false; + } + + /** + * Fetch account info for our site from the content hub + * + * @return array|bool|string False if account is not setup, otherwise data + */ + public function hubAccountInfo() { + $siteUuid = $this->h5pF->getOption('site_uuid', null); + $secret = $this->h5pF->getOption('hub_secret', null); + if (empty($siteUuid) || empty($secret)) { + return false; + } + + $headers = array( + 'Authorization' => $this->hubGetAuthorizationHeader(), + 'Accept' => 'application/json', + ); + + $url = H5PHubEndpoints::createURL(H5PHubEndpoints::REGISTER); + $accountInfo = $this->h5pF->fetchExternalData("{$url}/{$siteUuid}", + null, true, null, true, $headers, array(), 'GET'); + + if ($accountInfo['status'] !== 200) { + return false; + } + + return json_decode($accountInfo['data'])->data; + } } /** @@ -4519,6 +4856,12 @@ class H5PContentValidator { 'label' => $this->h5pF->t('Title'), 'placeholder' => 'La Gioconda' ), + (object) array( + 'name' => 'a11yTitle', + 'type' => 'text', + 'label' => $this->h5pF->t('Assistive Technologies label'), + 'optional' => TRUE, + ), (object) array( 'name' => 'license', 'type' => 'select', diff --git a/js/h5p-hub-registration.js b/js/h5p-hub-registration.js new file mode 100644 index 0000000..c77a78e --- /dev/null +++ b/js/h5p-hub-registration.js @@ -0,0 +1,37 @@ +!function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)n.d(r,i,function(t){return e[t]}.bind(null,i));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=56)}([function(e,t,n){"use strict";e.exports=n(19)},function(e,t,n){e.exports=n(28)()},function(e,t,n){"use strict";var r=n(8),i=Object.prototype.toString;function o(e){return"[object Array]"===i.call(e)}function a(e){return void 0===e}function l(e){return null!==e&&"object"==typeof e}function u(e){return"[object Function]"===i.call(e)}function c(e,t){if(null!=e)if("object"!=typeof e&&(e=[e]),o(e))for(var n=0,r=e.length;n1)for(var n=1;n=200&&e<300}};u.headers={common:{Accept:"application/json, text/plain, */*"}},r.forEach(["delete","get","head"],(function(e){u.headers[e]={}})),r.forEach(["post","put","patch"],(function(e){u.headers[e]=r.merge(o)})),e.exports=u}).call(this,n(6))},function(e,t,n){"use strict";var r=n(2),i=n(38),o=n(9),a=n(40),l=n(43),u=n(44),c=n(13);e.exports=function(e){return new Promise((function(t,s){var f=e.data,d=e.headers;r.isFormData(f)&&delete d["Content-Type"];var p=new XMLHttpRequest;if(e.auth){var m=e.auth.username||"",h=e.auth.password||"";d.Authorization="Basic "+btoa(m+":"+h)}var v=a(e.baseURL,e.url);if(p.open(e.method.toUpperCase(),o(v,e.params,e.paramsSerializer),!0),p.timeout=e.timeout,p.onreadystatechange=function(){if(p&&4===p.readyState&&(0!==p.status||p.responseURL&&0===p.responseURL.indexOf("file:"))){var n="getAllResponseHeaders"in p?l(p.getAllResponseHeaders()):null,r={data:e.responseType&&"text"!==e.responseType?p.response:p.responseText,status:p.status,statusText:p.statusText,headers:n,config:e,request:p};i(t,s,r),p=null}},p.onabort=function(){p&&(s(c("Request aborted",e,"ECONNABORTED",p)),p=null)},p.onerror=function(){s(c("Network Error",e,null,p)),p=null},p.ontimeout=function(){var t="timeout of "+e.timeout+"ms exceeded";e.timeoutErrorMessage&&(t=e.timeoutErrorMessage),s(c(t,e,"ECONNABORTED",p)),p=null},r.isStandardBrowserEnv()){var g=n(45),y=(e.withCredentials||u(v))&&e.xsrfCookieName?g.read(e.xsrfCookieName):void 0;y&&(d[e.xsrfHeaderName]=y)}if("setRequestHeader"in p&&r.forEach(d,(function(e,t){void 0===f&&"content-type"===t.toLowerCase()?delete d[t]:p.setRequestHeader(t,e)})),r.isUndefined(e.withCredentials)||(p.withCredentials=!!e.withCredentials),e.responseType)try{p.responseType=e.responseType}catch(t){if("json"!==e.responseType)throw t}"function"==typeof e.onDownloadProgress&&p.addEventListener("progress",e.onDownloadProgress),"function"==typeof e.onUploadProgress&&p.upload&&p.upload.addEventListener("progress",e.onUploadProgress),e.cancelToken&&e.cancelToken.promise.then((function(e){p&&(p.abort(),s(e),p=null)})),void 0===f&&(f=null),p.send(f)}))}},function(e,t,n){"use strict";var r=n(39);e.exports=function(e,t,n,i,o){var a=new Error(e);return r(a,t,n,i,o)}},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t){t=t||{};var n={},i=["url","method","params","data"],o=["headers","auth","proxy"],a=["baseURL","url","transformRequest","transformResponse","paramsSerializer","timeout","withCredentials","adapter","responseType","xsrfCookieName","xsrfHeaderName","onUploadProgress","onDownloadProgress","maxContentLength","validateStatus","maxRedirects","httpAgent","httpsAgent","cancelToken","socketPath"];r.forEach(i,(function(e){void 0!==t[e]&&(n[e]=t[e])})),r.forEach(o,(function(i){r.isObject(t[i])?n[i]=r.deepMerge(e[i],t[i]):void 0!==t[i]?n[i]=t[i]:r.isObject(e[i])?n[i]=r.deepMerge(e[i]):void 0!==e[i]&&(n[i]=e[i])})),r.forEach(a,(function(r){void 0!==t[r]?n[r]=t[r]:void 0!==e[r]&&(n[r]=e[r])}));var l=i.concat(o).concat(a),u=Object.keys(t).filter((function(e){return-1===l.indexOf(e)}));return r.forEach(u,(function(r){void 0!==t[r]?n[r]=t[r]:void 0!==e[r]&&(n[r]=e[r])})),n}},function(e,t,n){"use strict";function r(e){this.message=e}r.prototype.toString=function(){return"Cancel"+(this.message?": "+this.message:"")},r.prototype.__CANCEL__=!0,e.exports=r},function(e,t,n){"use strict";!function e(){if("undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE){0;try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(e){console.error(e)}}}(),e.exports=n(20)},function(e,t,n){(function(t,n){ +/*! + * @overview es6-promise - a tiny implementation of Promises/A+. + * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) + * @license Licensed under MIT license + * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE + * @version v4.2.8+1e68dce6 + */var r;r=function(){"use strict";function e(e){return"function"==typeof e}var r=Array.isArray?Array.isArray:function(e){return"[object Array]"===Object.prototype.toString.call(e)},i=0,o=void 0,a=void 0,l=function(e,t){m[i]=e,m[i+1]=t,2===(i+=2)&&(a?a(h):w())},u="undefined"!=typeof window?window:void 0,c=u||{},s=c.MutationObserver||c.WebKitMutationObserver,f="undefined"==typeof self&&void 0!==t&&"[object process]"==={}.toString.call(t),d="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel;function p(){var e=setTimeout;return function(){return e(h,1)}}var m=new Array(1e3);function h(){for(var e=0;eO.length&&O.push(e)}function I(e,t,n){return null==e?0:function e(t,n,r,i){var l=typeof t;"undefined"!==l&&"boolean"!==l||(t=null);var u=!1;if(null===t)u=!0;else switch(l){case"string":case"number":u=!0;break;case"object":switch(t.$$typeof){case o:case a:u=!0}}if(u)return r(i,t,""===n?"."+D(t,0):n),1;if(u=0,n=""===n?".":n+":",Array.isArray(t))for(var c=0;c