diff --git a/.drone.yml b/.drone.yml index 48882fe0d8..bd775ad91b 100644 --- a/.drone.yml +++ b/.drone.yml @@ -594,6 +594,13 @@ pipeline: when: matrix: TESTS-ACCEPTANCE: login + acceptance-users: + image: nextcloudci/acceptance-php7.1:acceptance-php7.1-2 + commands: + - tests/acceptance/run-local.sh --timeout-multiplier 10 --nextcloud-server-domain acceptance-users --selenium-server selenium:4444 allow-git-repository-modifications features/users.feature + when: + matrix: + TESTS-ACCEPTANCE: users nodb-codecov: image: nextcloudci/php7.0:php7.0-19 commands: @@ -761,6 +768,8 @@ matrix: TESTS-ACCEPTANCE: header - TESTS: acceptance TESTS-ACCEPTANCE: login + - TESTS: acceptance + TESTS-ACCEPTANCE: users - TESTS: jsunit - TESTS: syntax-php7.0 - TESTS: syntax-php7.1 diff --git a/core/css/inputs.scss b/core/css/inputs.scss index 7509575f9e..665deca228 100644 --- a/core/css/inputs.scss +++ b/core/css/inputs.scss @@ -67,6 +67,13 @@ div[contenteditable=true], cursor: default; opacity: 0.5; } + &:required { + box-shadow: none; + } + &:invalid { + box-shadow: none !important; + border-color: $color-error; + } /* Primary action button, use sparingly */ &.primary { background-color: $color-primary-element; @@ -216,7 +223,8 @@ input { margin-left: -8px !important; border-left-color: transparent !important; border-radius: 0 $border-radius $border-radius 0 !important; - background-clip: padding-box; /* Avoid background under border */ + background-clip: padding-box; + /* Avoid background under border */ background-color: $color-main-background !important; opacity: 1; width: 34px; @@ -227,6 +235,7 @@ input { background-image: url('../img/actions/confirm-fade.svg?v=2') !important; } } + /* only show confirm borders if input is not focused */ &:not(:active):not(:hover):not(:focus){ + .icon-confirm { @@ -244,14 +253,19 @@ input { &:active, &:hover, &:focus { + &:invalid { + + .icon-confirm { + border-color: $color-error; + } + } + .icon-confirm { border-color: $color-primary-element !important; border-left-color: transparent !important; - z-index: 2; /* above previous input */ + /* above previous input */ + z-index: 2; } } } - } @@ -606,6 +620,206 @@ input { } } + +/* Vue multiselect */ +.multiselect.multiselect-vue { + margin: 1px 2px; + padding: 0 !important; + display: inline-block; + /* min-width: 160px; */ + /* width: 160px; */ + position: relative; + background-color: $color-main-background; + &.multiselect--active { + /* Opened: force display the input */ + input.multiselect__input { + opacity: 1 !important; + cursor: text !important; + } + } + &.multiselect--disabled, + &.multiselect--disabled .multiselect__single { + background-color: nc-darken($color-main-background, 8%) !important; + } + .multiselect__tags { + /* space between tags and limit tag */ + $space-between: 5px; + + display: flex; + flex-wrap: nowrap; + overflow: hidden; + border: 1px solid nc-darken($color-main-background, 14%); + cursor: pointer; + position: relative; + border-radius: 3px; + height: 34px; + /* tag wrapper */ + .multiselect__tags-wrap { + align-items: center; + display: inline-flex; + overflow: hidden; + max-width: 100%; + position: relative; + padding: 3px $space-between; + flex-grow: 1; + /* no tags or simple select? Show input directly + input is used to display single value */ + &:empty ~ input.multiselect__input { + opacity: 1 !important; + /* hide default empty text, show input instead */ + + span:not(.multiselect__single) { + display: none; + } + } + /* selected tag */ + .multiselect__tag { + flex: 1 0 0; + line-height: 20px; + padding: 1px 5px; + background-image: none; + color: nc-lighten($color-main-text, 33%); + border: 1px solid nc-darken($color-main-background, 14%); + display: inline-flex; + align-items: center; + border-radius: 3px; + /* require to override the default width + and force the tag to shring properly */ + min-width: 0; + max-width: 50%; + max-width: fit-content; + max-width: -moz-fit-content; + /* css hack, detect if more than two tags + if so, flex-basis is set to half */ + &:only-child { + flex: 0 1 auto; + } + &:not(:last-child) { + margin-right: $space-between; + } + /* ellipsis the groups to be sure + we display at least two of them */ + > span { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } + } + } + /* Single select default value */ + .multiselect__single { + padding: 8px 10px; + flex: 0 0 100%; + z-index: 1; /* above input */ + background-color: $color-main-background; + cursor: pointer; + } + /* displayed text if tag limit reached */ + .multiselect__strong, + .multiselect__limit { + flex: 0 0 auto; + line-height: 20px; + color: nc-lighten($color-main-text, 33%); + display: inline-flex; + align-items: center; + opacity: .7; + margin-right: $space-between; + /* above the input */ + z-index: 5; + } + /* default multiselect input for search and placeholder */ + input.multiselect__input { + width: 100% !important; + position: absolute !important; + margin: 0; + opacity: 0; + /* let's leave it on top of tags but hide it */ + height: 100%; + border: none; + /* override hide to force show the placeholder */ + display: block !important; + /* only when not active */ + cursor: pointer; + } + } + /* results wrapper */ + .multiselect__content-wrapper { + position: absolute; + width: 100%; + margin-top: -1px; + border: 1px solid nc-darken($color-main-background, 14%); + background: $color-main-background; + z-index: 50; + max-height: 250px; + overflow-y: auto; + .multiselect__content { + width: 100%; + padding: 5px 0; + } + li { + padding: 5px; + position: relative; + display: flex; + align-items: center; + background-color: transparent; + &, + span { + cursor: pointer; + } + > span { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + height: 20px; + margin: 0; + min-height: 1em; + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + display: inline-flex; + align-items: center; + background-color: transparent !important; + color: nc-lighten($color-main-text, 33%); + width: 100%; + /* selected checkmark icon */ + &:not(.multiselect__option--disabled)::before { + content: ' '; + background-image: url('../img/actions/checkmark.svg?v=1'); + background-repeat: no-repeat; + background-position: center; + min-width: 16px; + min-height: 16px; + display: block; + opacity: 0.5; + margin-right: 5px; + visibility: hidden; + } + &.multiselect__option--disabled { + background-color: nc-darken($color-main-background, 8%); + } + /* add the prop tag-placeholder="create" to add the + + * icon on top of an unknown-and-ready-to-be-created entry + */ + &[data-select='create'] { + &::before { + background-image: url('../img/actions/add.svg?v=1'); + visibility: visible; + } + } + &.multiselect__option--highlight { + color: $color-main-text; + } + &.multiselect__option--selected { + &::before { + visibility: visible; + } + } + } + } + } +} + /* Progressbar */ progress { display: block; diff --git a/core/css/multiselect.scss b/core/css/multiselect.scss index da6cbde372..6c8bd11806 100644 --- a/core/css/multiselect.scss +++ b/core/css/multiselect.scss @@ -75,8 +75,9 @@ ul.multiselectoptions { } } -div.multiselect, -select.multiselect { +/* TODO drop old legacy multiselect! */ +div.multiselect:not(.multiselect-vue), +select.multiselect:not(.multiselect-vue) { display: inline-block; max-width: 200px; min-width: 150px !important; diff --git a/core/css/tooltip.scss b/core/css/tooltip.scss index a974e05e1a..fe9a96cf31 100644 --- a/core/css/tooltip.scss +++ b/core/css/tooltip.scss @@ -11,119 +11,128 @@ */ .tooltip { - position: absolute; - display: block; - font-family: 'Open Sans', Frutiger, Calibri, 'Myriad Pro', Myriad, sans-serif; - font-style: normal; - font-weight: 400; - letter-spacing: normal; - line-break: auto; - line-height: 1.6; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - white-space: normal; - word-break: normal; - word-spacing: normal; - word-wrap: normal; - font-size: 12px; - opacity: 0; - z-index: 100000; - filter: drop-shadow(0 1px 10px $color-box-shadow); - &.in { - opacity: 1; - } - - &.top { - margin-top: -3px; - padding: 10px 0; - } - &.bottom { - margin-top: 3px; - padding: 10px 0; - } - - &.right { - margin-left: 3px; - padding: 0 10px; - .tooltip-arrow { - top: 50%; - left: 0; - margin-top: -10px; - border-width: 10px 10px 10px 0; - border-right-color: $color-main-background; - } - } - &.left { - margin-left: -3px; - padding: 0 5px; - .tooltip-arrow { - top: 50%; - right: 0; - margin-top: -10px; - border-width: 10px 0 10px 10px; - border-left-color: $color-main-background; - } - } - - /* TOP */ - &.top .tooltip-arrow, - &.top-left .tooltip-arrow, - &.top-right .tooltip-arrow { - bottom: 0; - border-width: 10px 10px 0; - border-top-color: $color-main-background; - } - &.top .tooltip-arrow { - left: 50%; - margin-left: -10px; - } - &.top-left .tooltip-arrow { - right: 10px; - margin-bottom: -10px; - } - &.top-right .tooltip-arrow { - left: 10px; - margin-bottom: -10px; - } - - /* BOTTOM */ - &.bottom .tooltip-arrow, - &.bottom-left .tooltip-arrow, - &.bottom-right .tooltip-arrow { - top: 0; - border-width: 0 10px 10px; - border-bottom-color: $color-main-background; - } - &.bottom .tooltip-arrow { - left: 50%; - margin-left: -10px; - } - &.bottom-left .tooltip-arrow { - right: 10px; - margin-top: -10px; - } - &.bottom-right .tooltip-arrow { - left: 10px; - margin-top: -10px; - } + position: absolute; + display: block; + font-family: 'Open Sans', Frutiger, Calibri, 'Myriad Pro', Myriad, sans-serif; + font-style: normal; + font-weight: 400; + letter-spacing: normal; + line-break: auto; + line-height: 1.6; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + white-space: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + font-size: 12px; + opacity: 0; + z-index: 100000; + /* default to top */ + margin-top: -3px; + padding: 10px 0; + filter: drop-shadow(0 1px 10px $color-box-shadow); + &.in, + &.tooltip[aria-hidden='false'] { + visibility: visible; + opacity: 1; + transition: opacity .15s; + } + &.top .tooltip-arrow, + &[x-placement^='top'] { + left: 50%; + margin-left: -10px; + } + &.bottom, + &[x-placement^='bottom'] { + margin-top: 3px; + padding: 10px 0; + } + &.right, + &[x-placement^='right'] { + margin-left: 3px; + padding: 0 10px; + .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -10px; + border-width: 10px 10px 10px 0; + border-right-color: $color-main-background; + } + } + &.left, + &[x-placement^='left'] { + margin-left: -3px; + padding: 0 5px; + .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -10px; + border-width: 10px 0 10px 10px; + border-left-color: $color-main-background; + } + } + /* TOP */ + &.top, + &.top-left, + &[x-placement^='top'], + &.top-right { + .tooltip-arrow { + bottom: 0; + border-width: 10px 10px 0; + border-top-color: $color-main-background; + } + } + &.top-left .tooltip-arrow { + right: 10px; + margin-bottom: -10px; + } + &.top-right .tooltip-arrow { + left: 10px; + margin-bottom: -10px; + } + /* BOTTOM */ + &.bottom, + &[x-placement^='bottom'], + &.bottom-left, + &.bottom-right { + .tooltip-arrow { + top: 0; + border-width: 0 10px 10px; + border-bottom-color: $color-main-background; + } + } + &[x-placement^='bottom'] .tooltip-arrow, + &.bottom .tooltip-arrow { + left: 50%; + margin-left: -10px; + } + &.bottom-left .tooltip-arrow { + right: 10px; + margin-top: -10px; + } + &.bottom-right .tooltip-arrow { + left: 10px; + margin-top: -10px; + } } .tooltip-inner { - max-width: 350px; - padding: 5px 8px; - background-color: $color-main-background; - color: $color-main-text; - text-align: center; - border-radius: $border-radius; + max-width: 350px; + padding: 5px 8px; + background-color: $color-main-background; + color: $color-main-text; + text-align: center; + border-radius: $border-radius; } .tooltip-arrow { - position: absolute; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} \ No newline at end of file diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index ebf5197d4d..c6475f8c88 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -913,7 +913,6 @@ return array( 'OC\\Settings\\Controller\\ChangePasswordController' => $baseDir . '/settings/Controller/ChangePasswordController.php', 'OC\\Settings\\Controller\\CheckSetupController' => $baseDir . '/settings/Controller/CheckSetupController.php', 'OC\\Settings\\Controller\\CommonSettingsTrait' => $baseDir . '/settings/Controller/CommonSettingsTrait.php', - 'OC\\Settings\\Controller\\GroupsController' => $baseDir . '/settings/Controller/GroupsController.php', 'OC\\Settings\\Controller\\LogSettingsController' => $baseDir . '/settings/Controller/LogSettingsController.php', 'OC\\Settings\\Controller\\MailSettingsController' => $baseDir . '/settings/Controller/MailSettingsController.php', 'OC\\Settings\\Controller\\PersonalSettingsController' => $baseDir . '/settings/Controller/PersonalSettingsController.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 9a1dfebf52..f334733a2c 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -943,7 +943,6 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Settings\\Controller\\ChangePasswordController' => __DIR__ . '/../../..' . '/settings/Controller/ChangePasswordController.php', 'OC\\Settings\\Controller\\CheckSetupController' => __DIR__ . '/../../..' . '/settings/Controller/CheckSetupController.php', 'OC\\Settings\\Controller\\CommonSettingsTrait' => __DIR__ . '/../../..' . '/settings/Controller/CommonSettingsTrait.php', - 'OC\\Settings\\Controller\\GroupsController' => __DIR__ . '/../../..' . '/settings/Controller/GroupsController.php', 'OC\\Settings\\Controller\\LogSettingsController' => __DIR__ . '/../../..' . '/settings/Controller/LogSettingsController.php', 'OC\\Settings\\Controller\\MailSettingsController' => __DIR__ . '/../../..' . '/settings/Controller/MailSettingsController.php', 'OC\\Settings\\Controller\\PersonalSettingsController' => __DIR__ . '/../../..' . '/settings/Controller/PersonalSettingsController.php', diff --git a/lib/private/L10N/Factory.php b/lib/private/L10N/Factory.php index a7ffb401b7..759340a63c 100644 --- a/lib/private/L10N/Factory.php +++ b/lib/private/L10N/Factory.php @@ -59,6 +59,11 @@ class Factory implements IFactory { */ protected $pluralFunctions = []; + const COMMON_LANGUAGE_CODES = [ + 'en', 'es', 'fr', 'de', 'de_DE', 'ja', 'ar', 'ru', 'nl', 'it', + 'pt_BR', 'pt_PT', 'da', 'fi_FI', 'nb_NO', 'sv', 'tr', 'zh_CN', 'ko' + ]; + /** @var IConfig */ protected $config; @@ -137,9 +142,9 @@ class Factory implements IFactory { * * @link https://github.com/owncloud/core/issues/21955 */ - if($this->config->getSystemValue('installed', false)) { + if ($this->config->getSystemValue('installed', false)) { $userId = !is_null($this->userSession->getUser()) ? $this->userSession->getUser()->getUID() : null; - if(!is_null($userId)) { + if (!is_null($userId)) { $userLang = $this->config->getUserValue($userId, 'core', 'lang', null); } else { $userLang = null; @@ -310,7 +315,7 @@ class Factory implements IFactory { */ private function isSubDirectory($sub, $parent) { // Check whether $sub contains no ".." - if(strpos($sub, '..') !== false) { + if (strpos($sub, '..') !== false) { return false; } @@ -441,4 +446,74 @@ class Factory implements IFactory { return $function; } } + + /** + * returns the common language and other languages in an + * associative array + * + * @return array + */ + public function getLanguages() { + $forceLanguage = $this->config->getSystemValue('force_language', false); + if ($forceLanguage !== false) { + return []; + } + + $languageCodes = $this->findAvailableLanguages(); + + $commonLanguages = []; + $languages = []; + + foreach($languageCodes as $lang) { + $l = $this->get('lib', $lang); + // TRANSLATORS this is the language name for the language switcher in the personal settings and should be the localized version + $potentialName = (string) $l->t('__language_name__'); + if ($l->getLanguageCode() === $lang && $potentialName[0] !== '_') {//first check if the language name is in the translation file + $ln = array( + 'code' => $lang, + 'name' => $potentialName + ); + } else if ($lang === 'en') { + $ln = array( + 'code' => $lang, + 'name' => 'English (US)' + ); + } else {//fallback to language code + $ln = array( + 'code' => $lang, + 'name' => $lang + ); + } + + // put appropriate languages into appropriate arrays, to print them sorted + // common languages -> divider -> other languages + if (in_array($lang, self::COMMON_LANGUAGE_CODES)) { + $commonLanguages[array_search($lang, self::COMMON_LANGUAGE_CODES)] = $ln; + } else { + $languages[] = $ln; + } + } + + ksort($commonLanguages); + + // sort now by displayed language not the iso-code + usort( $languages, function ($a, $b) { + if ($a['code'] === $a['name'] && $b['code'] !== $b['name']) { + // If a doesn't have a name, but b does, list b before a + return 1; + } + if ($a['code'] !== $a['name'] && $b['code'] === $b['name']) { + // If a does have a name, but b doesn't, list a before b + return -1; + } + // Otherwise compare the names + return strcmp($a['name'], $b['name']); + }); + + return [ + // reset indexes + 'commonlanguages' => array_values($commonLanguages), + 'languages' => $languages + ]; + } } diff --git a/lib/private/NavigationManager.php b/lib/private/NavigationManager.php index 279c899c5f..af6613759b 100644 --- a/lib/private/NavigationManager.php +++ b/lib/private/NavigationManager.php @@ -247,7 +247,7 @@ class NavigationManager implements INavigationManager { 'type' => 'settings', 'id' => 'core_users', 'order' => 4, - 'href' => $this->urlGenerator->linkToRoute('settings_users'), + 'href' => $this->urlGenerator->linkToRoute('settings.Users.usersList'), 'name' => $l->t('Users'), 'icon' => $this->urlGenerator->imagePath('settings', 'users.svg'), ]); diff --git a/lib/private/Settings/Personal/PersonalInfo.php b/lib/private/Settings/Personal/PersonalInfo.php index 813d06195b..305e3fb0a4 100644 --- a/lib/private/Settings/Personal/PersonalInfo.php +++ b/lib/private/Settings/Personal/PersonalInfo.php @@ -39,6 +39,7 @@ use OCP\L10N\IFactory; use OCP\Settings\ISettings; class PersonalInfo implements ISettings { + /** @var IConfig */ private $config; /** @var IUserManager */ @@ -51,12 +52,6 @@ class PersonalInfo implements ISettings { private $appManager; /** @var IFactory */ private $l10nFactory; - - const COMMON_LANGUAGE_CODES = [ - 'en', 'es', 'fr', 'de', 'de_DE', 'ja', 'ar', 'ru', 'nl', 'it', - 'pt_BR', 'pt_PT', 'da', 'fi_FI', 'nb_NO', 'sv', 'tr', 'zh_CN', 'ko' - ]; - /** @var IL10N */ private $l; @@ -198,64 +193,29 @@ class PersonalInfo implements ISettings { $uid = $user->getUID(); - $userLang = $this->config->getUserValue($uid, 'core', 'lang', $this->l10nFactory->findLanguage()); - $languageCodes = $this->l10nFactory->findAvailableLanguages(); + $userConfLang = $this->config->getUserValue($uid, 'core', 'lang', $this->l10nFactory->findLanguage()); + $languages = $this->l10nFactory->getLanguages(); - $commonLanguages = []; - $languages = []; - - foreach($languageCodes as $lang) { - $l = \OC::$server->getL10N('lib', $lang); - // TRANSLATORS this is the language name for the language switcher in the personal settings and should be the localized version - $potentialName = (string) $l->t('__language_name__'); - if($l->getLanguageCode() === $lang && $potentialName[0] !== '_') {//first check if the language name is in the translation file - $ln = array('code' => $lang, 'name' => $potentialName); - } elseif ($lang === 'en') { - $ln = ['code' => $lang, 'name' => 'English (US)']; - }else{//fallback to language code - $ln=array('code'=>$lang, 'name'=>$lang); - } - - // put appropriate languages into appropriate arrays, to print them sorted - // used language -> common languages -> divider -> other languages - if ($lang === $userLang) { - $userLang = $ln; - } elseif (in_array($lang, self::COMMON_LANGUAGE_CODES)) { - $commonLanguages[array_search($lang, self::COMMON_LANGUAGE_CODES)]=$ln; - } else { - $languages[]=$ln; - } + // associate the user language with the proper array + $userLangIndex = array_search($userConfLang, array_column($languages['commonlanguages'], 'code')); + $userLang = $languages['commonlanguages'][$userLangIndex]; + // search in the other languages + if ($userLangIndex === false) { + $userLangIndex = array_search($userConfLang, array_column($languages['languages'], 'code')); + $userLang = $languages['languages'][$userLangIndex]; } - // if user language is not available but set somehow: show the actual code as name if (!is_array($userLang)) { $userLang = [ - 'code' => $userLang, - 'name' => $userLang, + 'code' => $userConfLang, + 'name' => $userConfLang, ]; } - ksort($commonLanguages); - - // sort now by displayed language not the iso-code - usort( $languages, function ($a, $b) { - if ($a['code'] === $a['name'] && $b['code'] !== $b['name']) { - // If a doesn't have a name, but b does, list b before a - return 1; - } - if ($a['code'] !== $a['name'] && $b['code'] === $b['name']) { - // If a does have a name, but b doesn't, list a before b - return -1; - } - // Otherwise compare the names - return strcmp($a['name'], $b['name']); - }); - - return [ - 'activelanguage' => $userLang, - 'commonlanguages' => $commonLanguages, - 'languages' => $languages - ]; + return array_merge( + array('activelanguage' => $userLang), + $languages + ); } /** diff --git a/settings/.babelrc b/settings/.babelrc new file mode 100644 index 0000000000..bcea13f8a2 --- /dev/null +++ b/settings/.babelrc @@ -0,0 +1,15 @@ +{ + "presets": [ + [ + "env", + { + "targets": { + "browsers": ["last 2 versions", "ie >= 11"] + }, + "modules": false, + "blacklist": ["useStrict"], + "useBuiltIns": true + } + ] + ] +} diff --git a/settings/.editorconfig b/settings/.editorconfig new file mode 100644 index 0000000000..442058331b --- /dev/null +++ b/settings/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +indent_style = tab +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[{package.json,.travis.yml,webpack.config.js}] +indent_style = space +indent_size = 2 \ No newline at end of file diff --git a/settings/.gitignore b/settings/.gitignore new file mode 100644 index 0000000000..a0446a5485 --- /dev/null +++ b/settings/.gitignore @@ -0,0 +1,12 @@ +.DS_Store +node_modules/ +dist/ +npm-debug.log +yarn-error.log + +# Editor directories and files +.idea +*.suo +*.ntvs* +*.njsproj +*.sln diff --git a/settings/Controller/GroupsController.php b/settings/Controller/GroupsController.php deleted file mode 100644 index 19b7c53f8b..0000000000 --- a/settings/Controller/GroupsController.php +++ /dev/null @@ -1,157 +0,0 @@ - - * @author Lukas Reschke - * @author Morris Jobke - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see - * - */ - -namespace OC\Settings\Controller; - -use OC\AppFramework\Http; -use OC\Group\MetaData; -use OCP\AppFramework\Controller; -use OCP\AppFramework\Http\DataResponse; -use OCP\IGroup; -use OCP\IGroupManager; -use OCP\IL10N; -use OCP\IRequest; -use OCP\IUserSession; - -/** - * @package OC\Settings\Controller - */ -class GroupsController extends Controller { - /** @var IGroupManager */ - private $groupManager; - /** @var IL10N */ - private $l10n; - /** @var IUserSession */ - private $userSession; - /** @var bool */ - private $isAdmin; - - /** - * @param string $appName - * @param IRequest $request - * @param IGroupManager $groupManager - * @param IUserSession $userSession - * @param bool $isAdmin - * @param IL10N $l10n - */ - public function __construct($appName, - IRequest $request, - IGroupManager $groupManager, - IUserSession $userSession, - $isAdmin, - IL10N $l10n) { - parent::__construct($appName, $request); - $this->groupManager = $groupManager; - $this->userSession = $userSession; - $this->isAdmin = $isAdmin; - $this->l10n = $l10n; - } - - /** - * @NoAdminRequired - * - * @param string $pattern - * @param bool $filterGroups - * @param int $sortGroups - * @return DataResponse - */ - public function index($pattern = '', $filterGroups = false, $sortGroups = MetaData::SORT_USERCOUNT) { - $groupPattern = $filterGroups ? $pattern : ''; - - $groupsInfo = new MetaData( - $this->userSession->getUser()->getUID(), - $this->isAdmin, - $this->groupManager, - $this->userSession - ); - $groupsInfo->setSorting($sortGroups); - list($adminGroups, $groups) = $groupsInfo->get($groupPattern, $pattern); - - return new DataResponse( - array( - 'data' => array('adminGroups' => $adminGroups, 'groups' => $groups) - ) - ); - } - - /** - * @PasswordConfirmationRequired - * @param string $id - * @return DataResponse - */ - public function create($id) { - if($this->groupManager->groupExists($id)) { - return new DataResponse( - array( - 'message' => (string)$this->l10n->t('Group already exists.') - ), - Http::STATUS_CONFLICT - ); - } - $group = $this->groupManager->createGroup($id); - if($group instanceof IGroup) { - return new DataResponse(['groupname' => $group->getDisplayName()], Http::STATUS_CREATED); - } - - return new DataResponse( - array( - 'status' => 'error', - 'data' => array( - 'message' => (string)$this->l10n->t('Unable to add group.') - ) - ), - Http::STATUS_FORBIDDEN - ); - } - - /** - * @PasswordConfirmationRequired - * @param string $id - * @return DataResponse - */ - public function destroy($id) { - $group = $this->groupManager->get($id); - if ($group) { - if ($group->delete()) { - return new DataResponse( - array( - 'status' => 'success', - 'data' => ['groupname' => $group->getDisplayName()] - ), - Http::STATUS_NO_CONTENT - ); - } - } - return new DataResponse( - array( - 'status' => 'error', - 'data' => array( - 'message' => (string)$this->l10n->t('Unable to delete group.') - ), - ), - Http::STATUS_FORBIDDEN - ); - } - -} diff --git a/settings/Controller/UsersController.php b/settings/Controller/UsersController.php index 3f2e63f1bb..dfc04a3397 100644 --- a/settings/Controller/UsersController.php +++ b/settings/Controller/UsersController.php @@ -42,76 +42,57 @@ namespace OC\Settings\Controller; use OC\Accounts\AccountManager; use OC\AppFramework\Http; use OC\ForbiddenException; -use OC\HintException; -use OC\Settings\Mailer\NewUserMailHelper; use OC\Security\IdentityProof\Manager; use OCP\App\IAppManager; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\Http\TemplateResponse; use OCP\BackgroundJob\IJobList; -use OCP\Files\Config\IUserMountCache; -use OCP\Encryption\IEncryptionModule; use OCP\Encryption\IManager; use OCP\IConfig; use OCP\IGroupManager; use OCP\IL10N; -use OCP\ILogger; use OCP\IRequest; use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserManager; use OCP\IUserSession; +use OCP\L10N\IFactory; use OCP\Mail\IMailer; -use OCP\IAvatarManager; -use OCP\Security\ISecureRandom; -use OCP\Util; use OC\Settings\BackgroundJobs\VerifyUserData; /** * @package OC\Settings\Controller */ class UsersController extends Controller { - /** @var IL10N */ - private $l10n; - /** @var IUserSession */ - private $userSession; - /** @var bool */ - private $isAdmin; /** @var IUserManager */ private $userManager; /** @var IGroupManager */ private $groupManager; + /** @var IUserSession */ + private $userSession; /** @var IConfig */ private $config; - /** @var ILogger */ - private $log; + /** @var bool */ + private $isAdmin; + /** @var IL10N */ + private $l10n; /** @var IMailer */ private $mailer; - /** @var bool contains the state of the encryption app */ - private $isEncryptionAppEnabled; - /** @var bool contains the state of the admin recovery setting */ - private $isRestoreEnabled = false; + /** @var IFactory */ + private $l10nFactory; /** @var IAppManager */ private $appManager; - /** @var IAvatarManager */ - private $avatarManager; /** @var AccountManager */ private $accountManager; - /** @var ISecureRandom */ - private $secureRandom; - /** @var NewUserMailHelper */ - private $newUserMailHelper; /** @var Manager */ private $keyManager; /** @var IJobList */ private $jobList; - - /** @var IUserMountCache */ - private $userMountCache; - /** @var IManager */ private $encryptionManager; + public function __construct(string $appName, IRequest $request, IUserManager $userManager, @@ -120,17 +101,12 @@ class UsersController extends Controller { IConfig $config, bool $isAdmin, IL10N $l10n, - ILogger $log, IMailer $mailer, - IURLGenerator $urlGenerator, + IFactory $l10nFactory, IAppManager $appManager, - IAvatarManager $avatarManager, AccountManager $accountManager, - ISecureRandom $secureRandom, - NewUserMailHelper $newUserMailHelper, Manager $keyManager, IJobList $jobList, - IUserMountCache $userMountCache, IManager $encryptionManager) { parent::__construct($appName, $request); $this->userManager = $userManager; @@ -139,467 +115,271 @@ class UsersController extends Controller { $this->config = $config; $this->isAdmin = $isAdmin; $this->l10n = $l10n; - $this->log = $log; $this->mailer = $mailer; + $this->l10nFactory = $l10nFactory; $this->appManager = $appManager; - $this->avatarManager = $avatarManager; $this->accountManager = $accountManager; - $this->secureRandom = $secureRandom; - $this->newUserMailHelper = $newUserMailHelper; $this->keyManager = $keyManager; $this->jobList = $jobList; - $this->userMountCache = $userMountCache; $this->encryptionManager = $encryptionManager; - - // check for encryption state - TODO see formatUserForIndex - $this->isEncryptionAppEnabled = $appManager->isEnabledForUser('encryption'); - if ($this->isEncryptionAppEnabled) { - // putting this directly in empty is possible in PHP 5.5+ - $result = $config->getAppValue('encryption', 'recoveryAdminEnabled', '0'); - $this->isRestoreEnabled = !empty($result); - } } - /** - * @param IUser $user - * @param array|null $userGroups - * @return array - */ - private function formatUserForIndex(IUser $user, array $userGroups = null): array { - - // TODO: eliminate this encryption specific code below and somehow - // hook in additional user info from other apps - - // recovery isn't possible if admin or user has it disabled and encryption - // is enabled - so we eliminate the else paths in the conditional tree - // below - $restorePossible = false; - - if ($this->isEncryptionAppEnabled) { - if ($this->isRestoreEnabled) { - // check for the users recovery setting - $recoveryMode = $this->config->getUserValue($user->getUID(), 'encryption', 'recoveryEnabled', '0'); - // method call inside empty is possible with PHP 5.5+ - $recoveryModeEnabled = !empty($recoveryMode); - if ($recoveryModeEnabled) { - // user also has recovery mode enabled - $restorePossible = true; - } - } else { - $modules = $this->encryptionManager->getEncryptionModules(); - $restorePossible = true; - foreach ($modules as $id => $module) { - /* @var IEncryptionModule $instance */ - $instance = call_user_func($module['callback']); - if ($instance->needDetailedAccessList()) { - $restorePossible = false; - break; - } - } - } - } else { - // recovery is possible if encryption is disabled (plain files are - // available) - $restorePossible = true; - } - - $subAdminGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroupsName($user); - - $displayName = $user->getEMailAddress(); - if (is_null($displayName)) { - $displayName = ''; - } - - $avatarAvailable = false; - try { - $avatarAvailable = $this->avatarManager->getAvatar($user->getUID())->exists(); - } catch (\Exception $e) { - //No avatar yet - } - - return [ - 'name' => $user->getUID(), - 'displayname' => $user->getDisplayName(), - 'groups' => empty($userGroups) ? $this->groupManager->getUserGroupNames($user) : $userGroups, - 'subadmin' => $subAdminGroups, - 'quota' => $user->getQuota(), - 'quota_bytes' => Util::computerFileSize($user->getQuota()), - 'storageLocation' => $user->getHome(), - 'lastLogin' => $user->getLastLogin() * 1000, - 'backend' => $user->getBackendClassName(), - 'email' => $displayName, - 'isRestoreDisabled' => !$restorePossible, - 'isAvatarAvailable' => $avatarAvailable, - 'isEnabled' => $user->isEnabled(), - ]; - } - - /** - * @param array $userIDs Array with schema [$uid => $displayName] - * @return IUser[] - */ - private function getUsersForUID(array $userIDs): array { - $users = []; - foreach ($userIDs as $uid => $displayName) { - $users[$uid] = $this->userManager->get($uid); - } - return $users; - } /** + * @NoCSRFRequired * @NoAdminRequired - * - * @param int $offset - * @param int $limit - * @param string $gid GID to filter for - * @param string $pattern Pattern to search for in the username - * @param string $backend Backend to filter for (class-name) - * @return DataResponse - * - * TODO: Tidy up and write unit tests - code is mainly static method calls + * + * Display users list template + * + * @return TemplateResponse */ - public function index(int $offset = 0, int $limit = 10, string $gid = '', string $pattern = '', string $backend = ''): DataResponse { - // Remove backends - if (!empty($backend)) { - $activeBackends = $this->userManager->getBackends(); - $this->userManager->clearBackends(); - foreach ($activeBackends as $singleActiveBackend) { - if ($backend === get_class($singleActiveBackend)) { - $this->userManager->registerBackend($singleActiveBackend); - break; + public function usersListByGroup() { + return $this->usersList(); + } + + /** + * @NoCSRFRequired + * @NoAdminRequired + * + * Display users list template + * + * @return TemplateResponse + */ + public function usersList() { + $user = $this->userSession->getUser(); + $uid = $user->getUID(); + + \OC::$server->getNavigationManager()->setActiveEntry('core_users'); + + /* SORT OPTION: SORT_USERCOUNT or SORT_GROUPNAME */ + $sortGroupsBy = \OC\Group\MetaData::SORT_USERCOUNT; + if ($this->config->getSystemValue('sort_groups_by_name', false)) { + $sortGroupsBy = \OC\Group\MetaData::SORT_GROUPNAME; + } else { + $isLDAPUsed = false; + if ($this->appManager->isEnabledForUser('user_ldap')) { + $isLDAPUsed = + $this->groupManager->isBackendUsed('\OCA\User_LDAP\Group_LDAP') + || $this->groupManager->isBackendUsed('\OCA\User_LDAP\Group_Proxy'); + if ($isLDAPUsed) { + // LDAP user count can be slow, so we sort by group name here + $sortGroupsBy = \OC\Group\MetaData::SORT_GROUPNAME; } } } - - $userObjects = []; - $users = []; + + /* ENCRYPTION CONFIG */ + $isEncryptionEnabled = $this->encryptionManager->isEnabled(); + $useMasterKey = $this->config->getAppValue('encryption', 'useMasterKey', true); + // If masterKey enabled, then you can change password. This is to avoid data loss! + $canChangePassword = ($isEncryptionEnabled && $useMasterKey) || $useMasterKey; + + + /* GROUPS */ + $groupsInfo = new \OC\Group\MetaData( + $uid, + $this->isAdmin, + $this->groupManager, + $this->userSession + ); + + $groupsInfo->setSorting($sortGroupsBy); + list($adminGroup, $groups) = $groupsInfo->get(); + if ($this->isAdmin) { - if ($gid !== '' && $gid !== '_disabledUsers' && $gid !== '_everyone') { - $batch = $this->getUsersForUID($this->groupManager->displayNamesInGroup($gid, $pattern, $limit, $offset)); - } else { - $batch = $this->userManager->search($pattern, $limit, $offset); + $subAdmins = $this->groupManager->getSubAdmin()->getAllSubAdmins(); + // New class returns IUser[] so convert back + $result = []; + foreach ($subAdmins as $subAdmin) { + $result[] = [ + 'gid' => $subAdmin['group']->getGID(), + 'uid' => $subAdmin['user']->getUID(), + ]; } - - foreach ($batch as $user) { - if (($gid !== '_disabledUsers' && $user->isEnabled()) || - ($gid === '_disabledUsers' && !$user->isEnabled()) - ) { - $userObjects[] = $user; - $users[] = $this->formatUserForIndex($user); - } - } - + $subAdmins = $result; } else { - $subAdminOfGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($this->userSession->getUser()); - // New class returns IGroup[] so convert back - $gids = []; - foreach ($subAdminOfGroups as $group) { - $gids[] = $group->getGID(); - } - $subAdminOfGroups = $gids; - - // Set the $gid parameter to an empty value if the subadmin has no rights to access a specific group - if ($gid !== '' && $gid !== '_disabledUsers' && !in_array($gid, $subAdminOfGroups)) { - $gid = ''; - } - - // Batch all groups the user is subadmin of when a group is specified - $batch = []; - if ($gid !== '' && $gid !== '_disabledUsers' && $gid !== '_everyone') { - $batch = $this->groupManager->displayNamesInGroup($gid, $pattern, $limit, $offset); - } else { - foreach ($subAdminOfGroups as $group) { - $groupUsers = $this->groupManager->displayNamesInGroup($group, $pattern, $limit, $offset); - - foreach ($groupUsers as $uid => $displayName) { - $batch[$uid] = $displayName; - } - } - } - $batch = $this->getUsersForUID($batch); - - foreach ($batch as $user) { - // Only add the groups, this user is a subadmin of - $userGroups = array_values(array_intersect( - $this->groupManager->getUserGroupIds($user), - $subAdminOfGroups - )); - if (($gid !== '_disabledUsers' && $user->isEnabled()) || - ($gid === '_disabledUsers' && !$user->isEnabled()) - ) { - $userObjects[] = $user; - $users[] = $this->formatUserForIndex($user, $userGroups); + /* Retrieve group IDs from $groups array, so we can pass that information into OC_Group::displayNamesInGroups() */ + $gids = array(); + foreach($groups as $group) { + if (isset($group['id'])) { + $gids[] = $group['id']; } } + $subAdmins = false; } - - $usedSpace = $this->userMountCache->getUsedSpaceForUsers($userObjects); - - foreach ($users as &$userData) { - $userData['size'] = isset($usedSpace[$userData['name']]) ? $usedSpace[$userData['name']] : 0; + + $disabledUsers = $isLDAPUsed ? 0 : $this->userManager->countDisabledUsers(); + $disabledUsersGroup = [ + 'id' => 'disabled', + 'name' => 'Disabled users', + 'usercount' => $disabledUsers + ]; + $allGroups = array_merge_recursive($adminGroup, $groups); + + /* QUOTAS PRESETS */ + $quotaPreset = $this->config->getAppValue('files', 'quota_preset', '1 GB, 5 GB, 10 GB'); + $quotaPreset = explode(',', $quotaPreset); + foreach ($quotaPreset as &$preset) { + $preset = trim($preset); } + $quotaPreset = array_diff($quotaPreset, array('default', 'none')); + $defaultQuota = $this->config->getAppValue('files', 'default_quota', 'none'); + + \OC::$server->getEventDispatcher()->dispatch('OC\Settings\Users::loadAdditionalScripts'); + + /* TOTAL USERS COUNT */ + $userCount = array_reduce($this->userManager->countUsers(), function($v, $w) { + return $v + (int)$w; + }, 0); + + /* LANGUAGES */ + $languages = $this->l10nFactory->getLanguages(); + + /* FINAL DATA */ + $serverData = array(); + // groups + $serverData['groups'] = array_merge_recursive($adminGroup, [$disabledUsersGroup], $groups); + $serverData['subadmingroups'] = $groups; + // Various data + $serverData['isAdmin'] = $this->isAdmin; + $serverData['subadmins'] = $subAdmins; + $serverData['sortGroups'] = $sortGroupsBy; + $serverData['quotaPreset'] = $quotaPreset; + $serverData['userCount'] = $userCount-$disabledUsers; + $serverData['languages'] = $languages; + $serverData['defaultLanguage'] = $this->config->getSystemValue('default_language', 'en'); + // Settings + $serverData['defaultQuota'] = $defaultQuota; + $serverData['canChangePassword'] = $canChangePassword; - return new DataResponse($users); + return new TemplateResponse('settings', 'settings', ['serverData' => $serverData]); } /** * @NoAdminRequired + * @NoSubadminRequired * @PasswordConfirmationRequired * - * @param string $username - * @param string $password - * @param array $groups + * @param string $avatarScope + * @param string $displayname + * @param string $displaynameScope + * @param string $phone + * @param string $phoneScope * @param string $email + * @param string $emailScope + * @param string $website + * @param string $websiteScope + * @param string $address + * @param string $addressScope + * @param string $twitter + * @param string $twitterScope * @return DataResponse */ - public function create(string $username, string $password, array $groups = [], $email = ''): DataResponse { - if ($email !== '' && !$this->mailer->validateMailAddress($email)) { + public function setUserSettings($avatarScope, + $displayname, + $displaynameScope, + $phone, + $phoneScope, + $email, + $emailScope, + $website, + $websiteScope, + $address, + $addressScope, + $twitter, + $twitterScope + ) { + if (!empty($email) && !$this->mailer->validateMailAddress($email)) { return new DataResponse( [ - 'message' => $this->l10n->t('Invalid mail address') + 'status' => 'error', + 'data' => [ + 'message' => $this->l10n->t('Invalid mail address') + ] ], Http::STATUS_UNPROCESSABLE_ENTITY ); } - - $currentUser = $this->userSession->getUser(); - - if (!$this->isAdmin) { - if (!empty($groups)) { - foreach ($groups as $key => $group) { - $groupObject = $this->groupManager->get($group); - if ($groupObject === null) { - unset($groups[$key]); - continue; - } - - if (!$this->groupManager->getSubAdmin()->isSubAdminOfGroup($currentUser, $groupObject)) { - unset($groups[$key]); - } - } - } - - if (empty($groups)) { - return new DataResponse( - [ - 'message' => $this->l10n->t('No valid group selected'), - ], - Http::STATUS_FORBIDDEN - ); + $user = $this->userSession->getUser(); + $data = $this->accountManager->getUser($user); + $data[AccountManager::PROPERTY_AVATAR] = ['scope' => $avatarScope]; + if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) { + $data[AccountManager::PROPERTY_DISPLAYNAME] = ['value' => $displayname, 'scope' => $displaynameScope]; + $data[AccountManager::PROPERTY_EMAIL] = ['value' => $email, 'scope' => $emailScope]; + } + if ($this->appManager->isEnabledForUser('federatedfilesharing')) { + $federatedFileSharing = new \OCA\FederatedFileSharing\AppInfo\Application(); + $shareProvider = $federatedFileSharing->getFederatedShareProvider(); + if ($shareProvider->isLookupServerUploadEnabled()) { + $data[AccountManager::PROPERTY_WEBSITE] = ['value' => $website, 'scope' => $websiteScope]; + $data[AccountManager::PROPERTY_ADDRESS] = ['value' => $address, 'scope' => $addressScope]; + $data[AccountManager::PROPERTY_PHONE] = ['value' => $phone, 'scope' => $phoneScope]; + $data[AccountManager::PROPERTY_TWITTER] = ['value' => $twitter, 'scope' => $twitterScope]; } } - - if ($this->userManager->userExists($username)) { - return new DataResponse( - [ - 'message' => $this->l10n->t('A user with that name already exists.') - ], - Http::STATUS_CONFLICT - ); - } - - $generatePasswordResetToken = false; - if ($password === '') { - if ($email === '') { - return new DataResponse( - [ - 'message' => $this->l10n->t('To send a password link to the user an email address is required.') - ], - Http::STATUS_UNPROCESSABLE_ENTITY - ); - } - - $password = $this->secureRandom->generate(30); - // Make sure we pass the password_policy - $password .= $this->secureRandom->generate(2, '$!.,;:-~+*[]{}()'); - $generatePasswordResetToken = true; - } - try { - $user = $this->userManager->createUser($username, $password); - } catch (\Exception $exception) { - $message = $exception->getMessage(); - if ($exception instanceof HintException && $exception->getHint()) { - $message = $exception->getHint(); - } - if (!$message) { - $message = $this->l10n->t('Unable to create user.'); - } - return new DataResponse( - [ - 'message' => (string)$message, - ], - Http::STATUS_FORBIDDEN - ); - } - - if ($user instanceof IUser) { - if ($groups !== null) { - foreach ($groups as $groupName) { - $group = $this->groupManager->get($groupName); - - if (empty($group)) { - $group = $this->groupManager->createGroup($groupName); - } - $group->addUser($user); - } - } - /** - * Send new user mail only if a mail is set - */ - if ($email !== '') { - $user->setEMailAddress($email); - try { - $emailTemplate = $this->newUserMailHelper->generateTemplate($user, $generatePasswordResetToken); - $this->newUserMailHelper->sendMail($user, $emailTemplate); - } catch (\Exception $e) { - $this->log->logException($e, [ - 'message' => "Can't send new user mail to $email", - 'level' => ILogger::ERROR, - 'app' => 'settings', - ]); - } - } - // fetch users groups - $userGroups = $this->groupManager->getUserGroupNames($user); - - return new DataResponse( - $this->formatUserForIndex($user, $userGroups), - Http::STATUS_CREATED - ); - } - - return new DataResponse( - [ - 'message' => $this->l10n->t('Unable to create user.') - ], - Http::STATUS_FORBIDDEN - ); - - } - - /** - * @NoAdminRequired - * @PasswordConfirmationRequired - * - * @param string $id - * @return DataResponse - */ - public function destroy(string $id): DataResponse { - $userId = $this->userSession->getUser()->getUID(); - $user = $this->userManager->get($id); - - if ($userId === $id) { - return new DataResponse( - [ - 'status' => 'error', - 'data' => [ - 'message' => $this->l10n->t('Unable to delete user.') - ] - ], - Http::STATUS_FORBIDDEN - ); - } - - if (!$this->isAdmin && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)) { - return new DataResponse( - [ - 'status' => 'error', - 'data' => [ - 'message' => $this->l10n->t('Authentication error') - ] - ], - Http::STATUS_FORBIDDEN - ); - } - - if ($user && $user->delete()) { + $this->saveUserSettings($user, $data); return new DataResponse( [ 'status' => 'success', 'data' => [ - 'username' => $id + 'userId' => $user->getUID(), + 'avatarScope' => $data[AccountManager::PROPERTY_AVATAR]['scope'], + 'displayname' => $data[AccountManager::PROPERTY_DISPLAYNAME]['value'], + 'displaynameScope' => $data[AccountManager::PROPERTY_DISPLAYNAME]['scope'], + 'email' => $data[AccountManager::PROPERTY_EMAIL]['value'], + 'emailScope' => $data[AccountManager::PROPERTY_EMAIL]['scope'], + 'website' => $data[AccountManager::PROPERTY_WEBSITE]['value'], + 'websiteScope' => $data[AccountManager::PROPERTY_WEBSITE]['scope'], + 'address' => $data[AccountManager::PROPERTY_ADDRESS]['value'], + 'addressScope' => $data[AccountManager::PROPERTY_ADDRESS]['scope'], + 'message' => $this->l10n->t('Settings saved') ] ], - Http::STATUS_NO_CONTENT + Http::STATUS_OK ); - } - - return new DataResponse( - [ + } catch (ForbiddenException $e) { + return new DataResponse([ 'status' => 'error', 'data' => [ - 'message' => $this->l10n->t('Unable to delete user.') - ] - ], - Http::STATUS_FORBIDDEN - ); - } - - /** - * @NoAdminRequired - * - * @param string $id - * @param int $enabled - * @return DataResponse - */ - public function setEnabled(string $id, int $enabled): DataResponse { - $enabled = (bool)$enabled; - if ($enabled) { - $errorMsgGeneral = $this->l10n->t('Error while enabling user.'); - } else { - $errorMsgGeneral = $this->l10n->t('Error while disabling user.'); - } - - $userId = $this->userSession->getUser()->getUID(); - $user = $this->userManager->get($id); - - if ($userId === $id) { - return new DataResponse( - [ - 'status' => 'error', - 'data' => [ - 'message' => $errorMsgGeneral - ] - ], Http::STATUS_FORBIDDEN - ); - } - - if ($user) { - if (!$this->isAdmin && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)) { - return new DataResponse( - [ - 'status' => 'error', - 'data' => [ - 'message' => $this->l10n->t('Authentication error') - ] - ], - Http::STATUS_FORBIDDEN - ); - } - - $user->setEnabled($enabled); - return new DataResponse( - [ - 'status' => 'success', - 'data' => [ - 'username' => $id, - 'enabled' => $enabled - ] - ] - ); - } else { - return new DataResponse( - [ - 'status' => 'error', - 'data' => [ - 'message' => $errorMsgGeneral - ] + 'message' => $e->getMessage() ], - Http::STATUS_FORBIDDEN - ); + ]); } - + } + /** + * update account manager with new user data + * + * @param IUser $user + * @param array $data + * @throws ForbiddenException + */ + protected function saveUserSettings(IUser $user, array $data) { + // keep the user back-end up-to-date with the latest display name and email + // address + $oldDisplayName = $user->getDisplayName(); + $oldDisplayName = is_null($oldDisplayName) ? '' : $oldDisplayName; + if (isset($data[AccountManager::PROPERTY_DISPLAYNAME]['value']) + && $oldDisplayName !== $data[AccountManager::PROPERTY_DISPLAYNAME]['value'] + ) { + $result = $user->setDisplayName($data[AccountManager::PROPERTY_DISPLAYNAME]['value']); + if ($result === false) { + throw new ForbiddenException($this->l10n->t('Unable to change full name')); + } + } + $oldEmailAddress = $user->getEMailAddress(); + $oldEmailAddress = is_null($oldEmailAddress) ? '' : $oldEmailAddress; + if (isset($data[AccountManager::PROPERTY_EMAIL]['value']) + && $oldEmailAddress !== $data[AccountManager::PROPERTY_EMAIL]['value'] + ) { + // this is the only permission a backend provides and is also used + // for the permission of setting a email address + if (!$user->canChangeDisplayName()) { + throw new ForbiddenException($this->l10n->t('Unable to change email address')); + } + $user->setEMailAddress($data[AccountManager::PROPERTY_EMAIL]['value']); + } + $this->accountManager->updateUser($user, $data); } /** @@ -689,330 +469,4 @@ class UsersController extends Controller { openssl_sign(json_encode($message), $signature, $privateKey, OPENSSL_ALGO_SHA512); return base64_encode($signature); } - - /** - * @NoAdminRequired - * @NoSubadminRequired - * @PasswordConfirmationRequired - * - * @param string $avatarScope - * @param string $displayname - * @param string $displaynameScope - * @param string $phone - * @param string $phoneScope - * @param string $email - * @param string $emailScope - * @param string $website - * @param string $websiteScope - * @param string $address - * @param string $addressScope - * @param string $twitter - * @param string $twitterScope - * @return DataResponse - */ - public function setUserSettings($avatarScope, - $displayname, - $displaynameScope, - $phone, - $phoneScope, - $email, - $emailScope, - $website, - $websiteScope, - $address, - $addressScope, - $twitter, - $twitterScope - ) { - - if (!empty($email) && !$this->mailer->validateMailAddress($email)) { - return new DataResponse( - [ - 'status' => 'error', - 'data' => [ - 'message' => $this->l10n->t('Invalid mail address') - ] - ], - Http::STATUS_UNPROCESSABLE_ENTITY - ); - } - - $user = $this->userSession->getUser(); - - $data = $this->accountManager->getUser($user); - - $data[AccountManager::PROPERTY_AVATAR] = ['scope' => $avatarScope]; - if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) { - $data[AccountManager::PROPERTY_DISPLAYNAME] = ['value' => $displayname, 'scope' => $displaynameScope]; - $data[AccountManager::PROPERTY_EMAIL] = ['value' => $email, 'scope' => $emailScope]; - } - - if ($this->appManager->isEnabledForUser('federatedfilesharing')) { - $federatedFileSharing = new \OCA\FederatedFileSharing\AppInfo\Application(); - $shareProvider = $federatedFileSharing->getFederatedShareProvider(); - if ($shareProvider->isLookupServerUploadEnabled()) { - $data[AccountManager::PROPERTY_WEBSITE] = ['value' => $website, 'scope' => $websiteScope]; - $data[AccountManager::PROPERTY_ADDRESS] = ['value' => $address, 'scope' => $addressScope]; - $data[AccountManager::PROPERTY_PHONE] = ['value' => $phone, 'scope' => $phoneScope]; - $data[AccountManager::PROPERTY_TWITTER] = ['value' => $twitter, 'scope' => $twitterScope]; - } - } - - try { - $this->saveUserSettings($user, $data); - return new DataResponse( - [ - 'status' => 'success', - 'data' => [ - 'userId' => $user->getUID(), - 'avatarScope' => $data[AccountManager::PROPERTY_AVATAR]['scope'], - 'displayname' => $data[AccountManager::PROPERTY_DISPLAYNAME]['value'], - 'displaynameScope' => $data[AccountManager::PROPERTY_DISPLAYNAME]['scope'], - 'email' => $data[AccountManager::PROPERTY_EMAIL]['value'], - 'emailScope' => $data[AccountManager::PROPERTY_EMAIL]['scope'], - 'website' => $data[AccountManager::PROPERTY_WEBSITE]['value'], - 'websiteScope' => $data[AccountManager::PROPERTY_WEBSITE]['scope'], - 'address' => $data[AccountManager::PROPERTY_ADDRESS]['value'], - 'addressScope' => $data[AccountManager::PROPERTY_ADDRESS]['scope'], - 'message' => $this->l10n->t('Settings saved') - ] - ], - Http::STATUS_OK - ); - } catch (ForbiddenException $e) { - return new DataResponse([ - 'status' => 'error', - 'data' => [ - 'message' => $e->getMessage() - ], - ]); - } - - } - - - /** - * update account manager with new user data - * - * @param IUser $user - * @param array $data - * @throws ForbiddenException - */ - protected function saveUserSettings(IUser $user, array $data) { - - // keep the user back-end up-to-date with the latest display name and email - // address - $oldDisplayName = $user->getDisplayName(); - $oldDisplayName = is_null($oldDisplayName) ? '' : $oldDisplayName; - if (isset($data[AccountManager::PROPERTY_DISPLAYNAME]['value']) - && $oldDisplayName !== $data[AccountManager::PROPERTY_DISPLAYNAME]['value'] - ) { - $result = $user->setDisplayName($data[AccountManager::PROPERTY_DISPLAYNAME]['value']); - if ($result === false) { - throw new ForbiddenException($this->l10n->t('Unable to change full name')); - } - } - - $oldEmailAddress = $user->getEMailAddress(); - $oldEmailAddress = is_null($oldEmailAddress) ? '' : $oldEmailAddress; - if (isset($data[AccountManager::PROPERTY_EMAIL]['value']) - && $oldEmailAddress !== $data[AccountManager::PROPERTY_EMAIL]['value'] - ) { - // this is the only permission a backend provides and is also used - // for the permission of setting a email address - if (!$user->canChangeDisplayName()) { - throw new ForbiddenException($this->l10n->t('Unable to change email address')); - } - $user->setEMailAddress($data[AccountManager::PROPERTY_EMAIL]['value']); - } - - $this->accountManager->updateUser($user, $data); - } - - /** - * Count all unique users visible for the current admin/subadmin. - * - * @NoAdminRequired - * - * @return DataResponse - */ - public function stats(): DataResponse { - $userCount = 0; - if ($this->isAdmin) { - $countByBackend = $this->userManager->countUsers(); - - if (!empty($countByBackend)) { - foreach ($countByBackend as $count) { - $userCount += $count; - } - } - } else { - $groups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($this->userSession->getUser()); - - $uniqueUsers = []; - foreach ($groups as $group) { - foreach ($group->getUsers() as $uid => $displayName) { - $uniqueUsers[$uid] = true; - } - } - - $userCount = count($uniqueUsers); - } - - return new DataResponse( - [ - 'totalUsers' => $userCount - ] - ); - } - - - /** - * Set the displayName of a user - * - * @NoAdminRequired - * @NoSubadminRequired - * @PasswordConfirmationRequired - * @todo merge into saveUserSettings - * - * @param string $username - * @param string $displayName - * @return DataResponse - */ - public function setDisplayName(string $username, string $displayName) { - $currentUser = $this->userSession->getUser(); - $user = $this->userManager->get($username); - - if ($user === null || - !$user->canChangeDisplayName() || - ( - !$this->groupManager->isAdmin($currentUser->getUID()) && - !$this->groupManager->getSubAdmin()->isUserAccessible($currentUser, $user) && - $currentUser->getUID() !== $username - - ) - ) { - return new DataResponse([ - 'status' => 'error', - 'data' => [ - 'message' => $this->l10n->t('Authentication error'), - ], - ]); - } - - $userData = $this->accountManager->getUser($user); - $userData[AccountManager::PROPERTY_DISPLAYNAME]['value'] = $displayName; - - - try { - $this->saveUserSettings($user, $userData); - return new DataResponse([ - 'status' => 'success', - 'data' => [ - 'message' => $this->l10n->t('Your full name has been changed.'), - 'username' => $username, - 'displayName' => $displayName, - ], - ]); - } catch (ForbiddenException $e) { - return new DataResponse([ - 'status' => 'error', - 'data' => [ - 'message' => $e->getMessage(), - 'displayName' => $user->getDisplayName(), - ], - ]); - } - } - - /** - * Set the mail address of a user - * - * @NoAdminRequired - * @NoSubadminRequired - * @PasswordConfirmationRequired - * - * @param string $id - * @param string $mailAddress - * @return DataResponse - */ - public function setEMailAddress(string $id, string $mailAddress) { - $user = $this->userManager->get($id); - if (!$this->isAdmin - && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user) - ) { - return new DataResponse( - [ - 'status' => 'error', - 'data' => [ - 'message' => $this->l10n->t('Forbidden') - ] - ], - Http::STATUS_FORBIDDEN - ); - } - - if ($mailAddress !== '' && !$this->mailer->validateMailAddress($mailAddress)) { - return new DataResponse( - [ - 'status' => 'error', - 'data' => [ - 'message' => $this->l10n->t('Invalid mail address') - ] - ], - Http::STATUS_UNPROCESSABLE_ENTITY - ); - } - - if (!$user) { - return new DataResponse( - [ - 'status' => 'error', - 'data' => [ - 'message' => $this->l10n->t('Invalid user') - ] - ], - Http::STATUS_UNPROCESSABLE_ENTITY - ); - } - // this is the only permission a backend provides and is also used - // for the permission of setting a email address - if (!$user->canChangeDisplayName()) { - return new DataResponse( - [ - 'status' => 'error', - 'data' => [ - 'message' => $this->l10n->t('Unable to change mail address') - ] - ], - Http::STATUS_FORBIDDEN - ); - } - - $userData = $this->accountManager->getUser($user); - $userData[AccountManager::PROPERTY_EMAIL]['value'] = $mailAddress; - - try { - $this->saveUserSettings($user, $userData); - return new DataResponse( - [ - 'status' => 'success', - 'data' => [ - 'username' => $id, - 'mailAddress' => $mailAddress, - 'message' => $this->l10n->t('Email saved') - ] - ], - Http::STATUS_OK - ); - } catch (ForbiddenException $e) { - return new DataResponse([ - 'status' => 'error', - 'data' => [ - 'message' => $e->getMessage() - ], - ]); - } - } - } diff --git a/settings/Makefile b/settings/Makefile new file mode 100644 index 0000000000..98e1f89b38 --- /dev/null +++ b/settings/Makefile @@ -0,0 +1,26 @@ +all: dev-setup build-js-production + +dev-setup: clean clean-dev npm-init + +npm-init: + npm install + +npm-update: + npm update + +build-js: + npm run dev + +build-js-production: + npm run build + +watch-js: + npm run watch + +clean: + rm -f js/main.js + rm -f js/main.js.map + +clean-dev: + rm -rf node_modules + diff --git a/settings/README.md b/settings/README.md new file mode 100644 index 0000000000..6774ec1886 --- /dev/null +++ b/settings/README.md @@ -0,0 +1,19 @@ +# Settings section + +> Nextcloud settings with Vue + +## Build Setup + +``` bash +# install dependencies +make dev-setup + +# build for development +make build-js + +# build for development and watch edits +make watch-js + +# build for production with minification +make build-js-production +``` \ No newline at end of file diff --git a/settings/ajax/setquota.php b/settings/ajax/setquota.php deleted file mode 100644 index b1155ee0f9..0000000000 --- a/settings/ajax/setquota.php +++ /dev/null @@ -1,77 +0,0 @@ - - * @author Bart Visscher - * @author Björn Schießle - * @author Christopher Schäpers - * @author Felix Moeller - * @author Georg Ehrke - * @author Joas Schilling - * @author Lukas Reschke - * @author Morris Jobke - * @author Robin Appelman - * @author Thomas Müller - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see - * - */ - -OC_JSON::checkSubAdminUser(); -\OC_JSON::callCheck(); - -$lastConfirm = (int) \OC::$server->getSession()->get('last-password-confirm'); -if ($lastConfirm < (time() - 30 * 60 + 15)) { // allow 15 seconds delay - $l = \OC::$server->getL10N('core'); - OC_JSON::error(array( 'data' => array( 'message' => $l->t('Password confirmation is required')))); - exit(); -} - -$username = isset($_POST["username"]) ? (string)$_POST["username"] : ''; - -$isUserAccessible = false; -$currentUserObject = \OC::$server->getUserSession()->getUser(); -$targetUserObject = \OC::$server->getUserManager()->get($username); -if($targetUserObject !== null && $currentUserObject !== null) { - $isUserAccessible = \OC::$server->getGroupManager()->getSubAdmin()->isUserAccessible($currentUserObject, $targetUserObject); -} - -if(($username === '' && !OC_User::isAdminUser(OC_User::getUser())) - || (!OC_User::isAdminUser(OC_User::getUser()) - && !$isUserAccessible)) { - $l = \OC::$server->getL10N('core'); - OC_JSON::error(array( 'data' => array( 'message' => $l->t('Authentication error') ))); - exit(); -} - -//make sure the quota is in the expected format -$quota= (string)$_POST["quota"]; -if($quota !== 'none' and $quota !== 'default') { - $quota= OC_Helper::computerFileSize($quota); - $quota=OC_Helper::humanFileSize($quota); -} - -// Return Success story -if($username) { - $targetUserObject->setQuota($quota); -}else{//set the default quota when no username is specified - if($quota === 'default') {//'default' as default quota makes no sense - $quota='none'; - } - \OC::$server->getConfig()->setAppValue('files', 'default_quota', $quota); -} -OC_JSON::success(array("data" => array( "username" => $username , 'quota' => $quota))); - diff --git a/settings/ajax/togglesubadmins.php b/settings/ajax/togglesubadmins.php deleted file mode 100644 index c115568bca..0000000000 --- a/settings/ajax/togglesubadmins.php +++ /dev/null @@ -1,55 +0,0 @@ - - * @author Georg Ehrke - * @author Joas Schilling - * @author Lukas Reschke - * @author Roeland Jago Douma - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see - * - */ -OC_JSON::checkAdminUser(); -\OC_JSON::callCheck(); - -$lastConfirm = (int) \OC::$server->getSession()->get('last-password-confirm'); -if ($lastConfirm < (time() - 30 * 60 + 15)) { // allow 15 seconds delay - $l = \OC::$server->getL10N('core'); - OC_JSON::error(array( 'data' => array( 'message' => $l->t('Password confirmation is required')))); - exit(); -} - -$username = (string)$_POST['username']; -$group = (string)$_POST['group']; - -$subAdminManager = \OC::$server->getGroupManager()->getSubAdmin(); -$targetUserObject = \OC::$server->getUserManager()->get($username); -$targetGroupObject = \OC::$server->getGroupManager()->get($group); - -$isSubAdminOfGroup = false; -if($targetUserObject !== null && $targetGroupObject !== null) { - $isSubAdminOfGroup = $subAdminManager->isSubAdminOfGroup($targetUserObject, $targetGroupObject); -} - -// Toggle group -if($isSubAdminOfGroup) { - $subAdminManager->deleteSubAdmin($targetUserObject, $targetGroupObject); -} else { - $subAdminManager->createSubAdmin($targetUserObject, $targetGroupObject); -} - -OC_JSON::success(); diff --git a/settings/css/settings.scss b/settings/css/settings.scss index 97767ce2f2..6e95d4bdc2 100644 --- a/settings/css/settings.scss +++ b/settings/css/settings.scss @@ -675,101 +675,6 @@ span.usersLastLoginTooltip { } } -tr:hover > td { - &.password > span, &.displayName > span, &.mailAddress > span { - margin: 0; - cursor: pointer; - } - &.password > img, &.displayName > img, &.mailAddress > img { - visibility: visible; - cursor: pointer; - } -} - -td.userActions { - .toggleUserActions { - width: 44px; - height: 44px; - position: relative; - .action { - display: block; - padding: 14px; - opacity: 0.5; - .icon-more { - display: inline-block; - } - &:hover, - &:focus { - opacity: 1; - } - } - } -} - -div.recoveryPassword { - left: 50em; - display: block; - position: absolute; - top: -1px; -} - -input#recoveryPassword { - width: 15em; -} - -#controls select.quota { - margin: 3px; - margin-right: 10px; - height: 37px; -} - -#userlist td.quota { - position: relative; - width: 10em; - progress.quota-user-progress { - position: absolute; - width: calc(10em + 0px); - margin-top: -7px; - z-index: 0; - margin-left: 1px; - height: 3px; - } -} - -select { - &.quota-user { - width: 10em; - height: 34px; - z-index: 50; - position: relative; - } - + progress.quota-user-progress { - position: absolute; - width: calc(10em + 0px); - margin-top: -7px; - z-index: 0; - margin-left: 1px; - height: 3px; - } -} - -input.userFilter { - width: 200px; -} - -#newusergroups + input[type='submit'] { - position: relative; - top: -1px; -} - -#headerGroups, #headerSubAdmins, #headerQuota { - padding-left: 18px; -} - -#headerAvatar { - width: 32px; -} - /* used to highlight a user row in red */ #userlist tr.row-warning { @@ -1350,3 +1255,178 @@ doesnotexist:-o-prefocus, .strengthify-wrapper { margin-top: 22px; } } + + +/* USERS LIST -------------------------------------------------------------- */ +#body-settings { + #app-navigation { + /* Hack to override the javascript orderBy */ + #usergrouplist > li { + order: 4; + &#everyone { + order:1; + } + &#admin { + order:2; + } + &#disabled { + order:3; + } + } + } + $grid-row-height: 46px; + #app-content.user-list-grid { + display: grid; + grid-auto-columns: 1fr; + grid-auto-rows: $grid-row-height; + grid-column-gap: 20px; + .row { + // TODO replace with css4 subgrid when available + display: grid; + grid-row-start: span 1; + grid-gap: 3px; + align-items: center; + /* let's define the column until storage path, + what follows will be manually defined */ + grid-template-columns: 44px; + grid-auto-columns: min-content; + border-top: $color-border 1px solid; + &.disabled { + opacity: .5; + } + .name, + .displayName, + .password { + width: 150px; + } + .mailAddress, + .groups, + .subadmins { + width: 200px; + } + .quota { + width: 150px; + } + .languages { + width: 200px; + } + .storageLocation { + width: 250px; + } + .userBackend, + .lastLogin, + .userActions { + width: 100px; + } + &#grid-header, + &#new-user { + position: sticky; + align-self: normal; + background-color: $color-main-background; + z-index: 55; /* above multiselect */ + top: 0; + &.sticky { + box-shadow: 0 -2px 10px 1px $color-box-shadow; + } + /* fake input for groups validation */ + input#newgroups { + position: absolute; + opacity: 0; + width: 80% !important; + margin: 0 10%; + z-index: 0; + } + } + // separate prop to set initial value to top:0 + &#new-user { + top: $grid-row-height; + } + &#grid-header { + color: nc-lighten($color-main-text, 60%); + z-index: 60; /* above new-user */ + } + &:hover { + input:not([type='submit']):not(:focus):not(:active) { + border-color: nc-darken($color-main-background, 14%) !important; + } + } + > div, + > form { + grid-row: 1; + display: inline-flex; + align-items: center; + color: nc-lighten($color-main-text, 33%); + position: relative; + > input:not(:focus):not(:active) { + border-color: transparent; + cursor: pointer; + } + > input:focus, >input:active { + + .icon-confirm { + display: block !important; + } + } + &:not(.userActions) > input:not([type='submit']) { + width: 100%; + min-width: 0; + } + &.quota { + .multiselect--active + progress { + display: none; + } + progress { + position: absolute; + width: calc(100% - 4px); /* minus left and right */ + left: 2px; + bottom: 2px; + height: 3px; + z-index: 5; /* above multiselect */ + } + } + .icon-confirm { + width: 32px; + height: 32px; + flex: 0 0 32px; + cursor: pointer; + &:not(:active) { + display: none; + } + } + &.avatar { + height: 32px; + width: 32px; + margin: 6px; + img { + display: block; + } + } + .toggleUserActions { + position: relative; + .icon-more { + width: 44px; + height: 44px; + opacity: .5; + cursor: pointer; + :hover { + opacity: .7; + } + } + } + /* Fill the grid cell */ + .multiselect.multiselect-vue { + width: 100%; + } + } + } + .infinite-loading-container { + display: flex; + align-items: center; + justify-content: center; + grid-row-start: span 4; + } + .users-list-end { + opacity: .5; + user-select: none; + } + } +} diff --git a/settings/js/main.js b/settings/js/main.js new file mode 100644 index 0000000000..a9797974ee --- /dev/null +++ b/settings/js/main.js @@ -0,0 +1,56 @@ +!function(t){var e={};function n(r){if(e[r])return e[r].exports;var i=e[r]={i:r,l:!1,exports:{}};return t[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{configurable:!1,enumerable:!0,get:r})},n.r=function(t){Object.defineProperty(t,"__esModule",{value:!0})},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="/dist/",n(n.s=109)}([function(t,e,n){var r=n(2),i=n(37),o=n(12),s=n(20),a=n(18),u=function(t,e,n){var c,l,f,p,d=t&u.F,h=t&u.G,v=t&u.S,m=t&u.P,g=t&u.B,y=h?r:v?r[e]||(r[e]={}):(r[e]||{}).prototype,b=h?i:i[e]||(i[e]={}),_=b.prototype||(b.prototype={});for(c in h&&(n=e),n)f=((l=!d&&y&&void 0!==y[c])?y:n)[c],p=g&&l?a(f,r):m&&"function"==typeof f?a(Function.call,f):f,y&&s(y,c,f,t&u.U),b[c]!=f&&o(b,c,p),m&&_[c]!=f&&(_[c]=f)};r.core=i,u.F=1,u.G=2,u.S=4,u.P=8,u.B=16,u.W=32,u.U=64,u.R=128,t.exports=u},function(t,e){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,e){var n=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},function(t,e,n){var r=n(1);t.exports=function(t){if(!r(t))throw TypeError(t+" is not an object!");return t}},function(t,e,n){var r=n(68)("wks"),i=n(25),o=n(2).Symbol,s="function"==typeof o;(t.exports=function(t){return r[t]||(r[t]=s&&o[t]||(s?o:i)("Symbol."+t))}).store=r},function(t,e,n){"use strict";(function(t,n){ +/*! + * Vue.js v2.5.16 + * (c) 2014-2018 Evan You + * Released under the MIT License. + */ +var r=Object.freeze({});function i(t){return void 0===t||null===t}function o(t){return void 0!==t&&null!==t}function s(t){return!0===t}function a(t){return"string"==typeof t||"number"==typeof t||"symbol"==typeof t||"boolean"==typeof t}function u(t){return null!==t&&"object"==typeof t}var c=Object.prototype.toString;function l(t){return"[object Object]"===c.call(t)}function f(t){return"[object RegExp]"===c.call(t)}function p(t){var e=parseFloat(String(t));return e>=0&&Math.floor(e)===e&&isFinite(t)}function d(t){return null==t?"":"object"==typeof t?JSON.stringify(t,null,2):String(t)}function h(t){var e=parseFloat(t);return isNaN(e)?t:e}function v(t,e){for(var n=Object.create(null),r=t.split(","),i=0;i-1)return t.splice(n,1)}}var b=Object.prototype.hasOwnProperty;function _(t,e){return b.call(t,e)}function w(t){var e=Object.create(null);return function(n){return e[n]||(e[n]=t(n))}}var x=/-(\w)/g,O=w(function(t){return t.replace(x,function(t,e){return e?e.toUpperCase():""})}),S=w(function(t){return t.charAt(0).toUpperCase()+t.slice(1)}),C=/\B([A-Z])/g,k=w(function(t){return t.replace(C,"-$1").toLowerCase()});var E=Function.prototype.bind?function(t,e){return t.bind(e)}:function(t,e){function n(n){var r=arguments.length;return r?r>1?t.apply(e,arguments):t.call(e,n):t.call(e)}return n._length=t.length,n};function A(t,e){e=e||0;for(var n=t.length-e,r=new Array(n);n--;)r[n]=t[n+e];return r}function $(t,e){for(var n in e)t[n]=e[n];return t}function L(t){for(var e={},n=0;n0,J=K&&K.indexOf("edge/")>0,X=(K&&K.indexOf("android"),K&&/iphone|ipad|ipod|ios/.test(K)||"ios"===W),Z=(K&&/chrome\/\d+/.test(K),{}.watch),tt=!1;if(q)try{var et={};Object.defineProperty(et,"passive",{get:function(){tt=!0}}),window.addEventListener("test-passive",null,et)}catch(t){}var nt=function(){return void 0===V&&(V=!q&&!z&&void 0!==t&&"server"===t.process.env.VUE_ENV),V},rt=q&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__;function it(t){return"function"==typeof t&&/native code/.test(t.toString())}var ot,st="undefined"!=typeof Symbol&&it(Symbol)&&"undefined"!=typeof Reflect&&it(Reflect.ownKeys);ot="undefined"!=typeof Set&&it(Set)?Set:function(){function t(){this.set=Object.create(null)}return t.prototype.has=function(t){return!0===this.set[t]},t.prototype.add=function(t){this.set[t]=!0},t.prototype.clear=function(){this.set=Object.create(null)},t}();var at=T,ut=0,ct=function(){this.id=ut++,this.subs=[]};ct.prototype.addSub=function(t){this.subs.push(t)},ct.prototype.removeSub=function(t){y(this.subs,t)},ct.prototype.depend=function(){ct.target&&ct.target.addDep(this)},ct.prototype.notify=function(){for(var t=this.subs.slice(),e=0,n=t.length;e-1)if(o&&!_(i,"default"))s=!1;else if(""===s||s===k(t)){var u=Bt(String,i.type);(u<0||a0&&(le((c=t(c,(n||"")+"_"+u))[0])&&le(f)&&(r[l]=mt(f.text+c[0].text),c.shift()),r.push.apply(r,c)):a(c)?le(f)?r[l]=mt(f.text+c):""!==c&&r.push(mt(c)):le(c)&&le(f)?r[l]=mt(f.text+c.text):(s(e._isVList)&&o(c.tag)&&i(c.key)&&o(n)&&(c.key="__vlist"+n+"_"+u+"__"),r.push(c)));return r}(t):void 0}function le(t){return o(t)&&o(t.text)&&!1===t.isComment}function fe(t,e){return(t.__esModule||st&&"Module"===t[Symbol.toStringTag])&&(t=t.default),u(t)?e.extend(t):t}function pe(t){return t.isComment&&t.asyncFactory}function de(t){if(Array.isArray(t))for(var e=0;e$e&&Se[n].id>t.id;)n--;Se.splice(n+1,0,t)}else Se.push(t);Ee||(Ee=!0,te(Le))}}(this)},Pe.prototype.run=function(){if(this.active){var t=this.get();if(t!==this.value||u(t)||this.deep){var e=this.value;if(this.value=t,this.user)try{this.cb.call(this.vm,t,e)}catch(t){Gt(t,this.vm,'callback for watcher "'+this.expression+'"')}else this.cb.call(this.vm,t,e)}}},Pe.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},Pe.prototype.depend=function(){for(var t=this.deps.length;t--;)this.deps[t].depend()},Pe.prototype.teardown=function(){if(this.active){this.vm._isBeingDestroyed||y(this.vm._watchers,this);for(var t=this.deps.length;t--;)this.deps[t].removeSub(this);this.active=!1}};var je={enumerable:!0,configurable:!0,get:T,set:T};function Ne(t,e,n){je.get=function(){return this[e][n]},je.set=function(t){this[e][n]=t},Object.defineProperty(t,n,je)}function Me(t){t._watchers=[];var e=t.$options;e.props&&function(t,e){var n=t.$options.propsData||{},r=t._props={},i=t.$options._propKeys=[];t.$parent&&xt(!1);var o=function(o){i.push(o);var s=Ft(o,e,n,t);Et(r,o,s),o in t||Ne(t,"_props",o)};for(var s in e)o(s);xt(!0)}(t,e.props),e.methods&&function(t,e){t.$options.props;for(var n in e)t[n]=null==e[n]?T:E(e[n],t)}(t,e.methods),e.data?function(t){var e=t.$options.data;l(e=t._data="function"==typeof e?function(t,e){ft();try{return t.call(e,e)}catch(t){return Gt(t,e,"data()"),{}}finally{pt()}}(e,t):e||{})||(e={});var n=Object.keys(e),r=t.$options.props,i=(t.$options.methods,n.length);for(;i--;){var o=n[i];0,r&&_(r,o)||(void 0,36!==(s=(o+"").charCodeAt(0))&&95!==s&&Ne(t,"_data",o))}var s;kt(e,!0)}(t):kt(t._data={},!0),e.computed&&function(t,e){var n=t._computedWatchers=Object.create(null),r=nt();for(var i in e){var o=e[i],s="function"==typeof o?o:o.get;0,r||(n[i]=new Pe(t,s||T,T,Ue)),i in t||Ie(t,i,o)}}(t,e.computed),e.watch&&e.watch!==Z&&function(t,e){for(var n in e){var r=e[n];if(Array.isArray(r))for(var i=0;i=0||n.indexOf(t[i])<0)&&r.push(t[i]);return r}return t}function pn(t){this._init(t)}function dn(t){t.cid=0;var e=1;t.extend=function(t){t=t||{};var n=this,r=n.cid,i=t._Ctor||(t._Ctor={});if(i[r])return i[r];var o=t.name||n.options.name;var s=function(t){this._init(t)};return(s.prototype=Object.create(n.prototype)).constructor=s,s.cid=e++,s.options=Ut(n.options,t),s.super=n,s.options.props&&function(t){var e=t.options.props;for(var n in e)Ne(t.prototype,"_props",n)}(s),s.options.computed&&function(t){var e=t.options.computed;for(var n in e)Ie(t.prototype,n,e[n])}(s),s.extend=n.extend,s.mixin=n.mixin,s.use=n.use,F.forEach(function(t){s[t]=n[t]}),o&&(s.options.components[o]=s),s.superOptions=n.options,s.extendOptions=t,s.sealedOptions=$({},s.options),i[r]=s,s}}function hn(t){return t&&(t.Ctor.options.name||t.tag)}function vn(t,e){return Array.isArray(t)?t.indexOf(e)>-1:"string"==typeof t?t.split(",").indexOf(e)>-1:!!f(t)&&t.test(e)}function mn(t,e){var n=t.cache,r=t.keys,i=t._vnode;for(var o in n){var s=n[o];if(s){var a=hn(s.componentOptions);a&&!e(a)&&gn(n,o,r,i)}}}function gn(t,e,n,r){var i=t[e];!i||r&&i.tag===r.tag||i.componentInstance.$destroy(),t[e]=null,y(n,e)}!function(t){t.prototype._init=function(t){var e=this;e._uid=cn++,e._isVue=!0,t&&t._isComponent?function(t,e){var n=t.$options=Object.create(t.constructor.options),r=e._parentVnode;n.parent=e.parent,n._parentVnode=r,n._parentElm=e._parentElm,n._refElm=e._refElm;var i=r.componentOptions;n.propsData=i.propsData,n._parentListeners=i.listeners,n._renderChildren=i.children,n._componentTag=i.tag,e.render&&(n.render=e.render,n.staticRenderFns=e.staticRenderFns)}(e,t):e.$options=Ut(ln(e.constructor),t||{},e),e._renderProxy=e,e._self=e,function(t){var e=t.$options,n=e.parent;if(n&&!e.abstract){for(;n.$options.abstract&&n.$parent;)n=n.$parent;n.$children.push(t)}t.$parent=n,t.$root=n?n.$root:t,t.$children=[],t.$refs={},t._watcher=null,t._inactive=null,t._directInactive=!1,t._isMounted=!1,t._isDestroyed=!1,t._isBeingDestroyed=!1}(e),function(t){t._events=Object.create(null),t._hasHookEvent=!1;var e=t.$options._parentListeners;e&&me(t,e)}(e),function(t){t._vnode=null,t._staticTrees=null;var e=t.$options,n=t.$vnode=e._parentVnode,i=n&&n.context;t.$slots=ge(e._renderChildren,i),t.$scopedSlots=r,t._c=function(e,n,r,i){return un(t,e,n,r,i,!1)},t.$createElement=function(e,n,r,i){return un(t,e,n,r,i,!0)};var o=n&&n.data;Et(t,"$attrs",o&&o.attrs||r,null,!0),Et(t,"$listeners",e._parentListeners||r,null,!0)}(e),Oe(e,"beforeCreate"),function(t){var e=De(t.$options.inject,t);e&&(xt(!1),Object.keys(e).forEach(function(n){Et(t,n,e[n])}),xt(!0))}(e),Me(e),function(t){var e=t.$options.provide;e&&(t._provided="function"==typeof e?e.call(t):e)}(e),Oe(e,"created"),e.$options.el&&e.$mount(e.$options.el)}}(pn),function(t){var e={get:function(){return this._data}},n={get:function(){return this._props}};Object.defineProperty(t.prototype,"$data",e),Object.defineProperty(t.prototype,"$props",n),t.prototype.$set=At,t.prototype.$delete=$t,t.prototype.$watch=function(t,e,n){if(l(e))return Re(this,t,e,n);(n=n||{}).user=!0;var r=new Pe(this,t,e,n);return n.immediate&&e.call(this,r.value),function(){r.teardown()}}}(pn),function(t){var e=/^hook:/;t.prototype.$on=function(t,n){if(Array.isArray(t))for(var r=0,i=t.length;r1?A(e):e;for(var n=A(arguments,1),r=0,i=e.length;rparseInt(this.max)&&gn(s,a[0],a,this._vnode)),e.data.keepAlive=!0}return e||t&&t[0]}}};!function(t){var e={get:function(){return D}};Object.defineProperty(t,"config",e),t.util={warn:at,extend:$,mergeOptions:Ut,defineReactive:Et},t.set=At,t.delete=$t,t.nextTick=te,t.options=Object.create(null),F.forEach(function(e){t.options[e+"s"]=Object.create(null)}),t.options._base=t,$(t.options.components,bn),function(t){t.use=function(t){var e=this._installedPlugins||(this._installedPlugins=[]);if(e.indexOf(t)>-1)return this;var n=A(arguments,1);return n.unshift(this),"function"==typeof t.install?t.install.apply(t,n):"function"==typeof t&&t.apply(null,n),e.push(t),this}}(t),function(t){t.mixin=function(t){return this.options=Ut(this.options,t),this}}(t),dn(t),function(t){F.forEach(function(e){t[e]=function(t,n){return n?("component"===e&&l(n)&&(n.name=n.name||t,n=this.options._base.extend(n)),"directive"===e&&"function"==typeof n&&(n={bind:n,update:n}),this.options[e+"s"][t]=n,n):this.options[e+"s"][t]}})}(t)}(pn),Object.defineProperty(pn.prototype,"$isServer",{get:nt}),Object.defineProperty(pn.prototype,"$ssrContext",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Object.defineProperty(pn,"FunctionalRenderContext",{value:Ze}),pn.version="2.5.16";var _n=v("style,class"),wn=v("input,textarea,option,select,progress"),xn=function(t,e,n){return"value"===n&&wn(t)&&"button"!==e||"selected"===n&&"option"===t||"checked"===n&&"input"===t||"muted"===n&&"video"===t},On=v("contenteditable,draggable,spellcheck"),Sn=v("allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,translate,truespeed,typemustmatch,visible"),Cn="http://www.w3.org/1999/xlink",kn=function(t){return":"===t.charAt(5)&&"xlink"===t.slice(0,5)},En=function(t){return kn(t)?t.slice(6,t.length):""},An=function(t){return null==t||!1===t};function $n(t){for(var e=t.data,n=t,r=t;o(r.componentInstance);)(r=r.componentInstance._vnode)&&r.data&&(e=Ln(r.data,e));for(;o(n=n.parent);)n&&n.data&&(e=Ln(e,n.data));return function(t,e){if(o(t)||o(e))return Tn(t,Pn(e));return""}(e.staticClass,e.class)}function Ln(t,e){return{staticClass:Tn(t.staticClass,e.staticClass),class:o(t.class)?[t.class,e.class]:e.class}}function Tn(t,e){return t?e?t+" "+e:t:e||""}function Pn(t){return Array.isArray(t)?function(t){for(var e,n="",r=0,i=t.length;r-1?rr(t,e,n):Sn(e)?An(n)?t.removeAttribute(e):(n="allowfullscreen"===e&&"EMBED"===t.tagName?"true":e,t.setAttribute(e,n)):On(e)?t.setAttribute(e,An(n)||"false"===n?"false":"true"):kn(e)?An(n)?t.removeAttributeNS(Cn,En(e)):t.setAttributeNS(Cn,e,n):rr(t,e,n)}function rr(t,e,n){if(An(n))t.removeAttribute(e);else{if(Y&&!Q&&"TEXTAREA"===t.tagName&&"placeholder"===e&&!t.__ieph){var r=function(e){e.stopImmediatePropagation(),t.removeEventListener("input",r)};t.addEventListener("input",r),t.__ieph=!0}t.setAttribute(e,n)}}var ir={create:er,update:er};function or(t,e){var n=e.elm,r=e.data,s=t.data;if(!(i(r.staticClass)&&i(r.class)&&(i(s)||i(s.staticClass)&&i(s.class)))){var a=$n(e),u=n._transitionClasses;o(u)&&(a=Tn(a,Pn(u))),a!==n._prevClass&&(n.setAttribute("class",a),n._prevClass=a)}}var sr,ar,ur,cr,lr,fr,pr={create:or,update:or},dr=/[\w).+\-_$\]]/;function hr(t){var e,n,r,i,o,s=!1,a=!1,u=!1,c=!1,l=0,f=0,p=0,d=0;for(r=0;r=0&&" "===(v=t.charAt(h));h--);v&&dr.test(v)||(c=!0)}}else void 0===i?(d=r+1,i=t.slice(0,r).trim()):m();function m(){(o||(o=[])).push(t.slice(d,r).trim()),d=r+1}if(void 0===i?i=t.slice(0,r).trim():0!==d&&m(),o)for(r=0;r-1?{exp:t.slice(0,cr),key:'"'+t.slice(cr+1)+'"'}:{exp:t,key:null};ar=t,cr=lr=fr=0;for(;!Ar();)$r(ur=Er())?Tr(ur):91===ur&&Lr(ur);return{exp:t.slice(0,lr),key:t.slice(lr+1,fr)}}(t);return null===n.key?t+"="+e:"$set("+n.exp+", "+n.key+", "+e+")"}function Er(){return ar.charCodeAt(++cr)}function Ar(){return cr>=sr}function $r(t){return 34===t||39===t}function Lr(t){var e=1;for(lr=cr;!Ar();)if($r(t=Er()))Tr(t);else if(91===t&&e++,93===t&&e--,0===e){fr=cr;break}}function Tr(t){for(var e=t;!Ar()&&(t=Er())!==e;);}var Pr,jr="__r",Nr="__c";function Mr(t,e,n,r,i){var o;e=(o=e)._withTask||(o._withTask=function(){Qt=!0;var t=o.apply(null,arguments);return Qt=!1,t}),n&&(e=function(t,e,n){var r=Pr;return function i(){null!==t.apply(null,arguments)&&Ur(e,i,n,r)}}(e,t,r)),Pr.addEventListener(t,e,tt?{capture:r,passive:i}:r)}function Ur(t,e,n,r){(r||Pr).removeEventListener(t,e._withTask||e,n)}function Ir(t,e){if(!i(t.data.on)||!i(e.data.on)){var n=e.data.on||{},r=t.data.on||{};Pr=e.elm,function(t){if(o(t[jr])){var e=Y?"change":"input";t[e]=[].concat(t[jr],t[e]||[]),delete t[jr]}o(t[Nr])&&(t.change=[].concat(t[Nr],t.change||[]),delete t[Nr])}(n),se(n,r,Mr,Ur,e.context),Pr=void 0}}var Fr={create:Ir,update:Ir};function Rr(t,e){if(!i(t.data.domProps)||!i(e.data.domProps)){var n,r,s=e.elm,a=t.data.domProps||{},u=e.data.domProps||{};for(n in o(u.__ob__)&&(u=e.data.domProps=$({},u)),a)i(u[n])&&(s[n]="");for(n in u){if(r=u[n],"textContent"===n||"innerHTML"===n){if(e.children&&(e.children.length=0),r===a[n])continue;1===s.childNodes.length&&s.removeChild(s.childNodes[0])}if("value"===n){s._value=r;var c=i(r)?"":String(r);Dr(s,c)&&(s.value=c)}else s[n]=r}}}function Dr(t,e){return!t.composing&&("OPTION"===t.tagName||function(t,e){var n=!0;try{n=document.activeElement!==t}catch(t){}return n&&t.value!==e}(t,e)||function(t,e){var n=t.value,r=t._vModifiers;if(o(r)){if(r.lazy)return!1;if(r.number)return h(n)!==h(e);if(r.trim)return n.trim()!==e.trim()}return n!==e}(t,e))}var Br={create:Rr,update:Rr},Gr=w(function(t){var e={},n=/:(.+)/;return t.split(/;(?![^(]*\))/g).forEach(function(t){if(t){var r=t.split(n);r.length>1&&(e[r[0].trim()]=r[1].trim())}}),e});function Vr(t){var e=Hr(t.style);return t.staticStyle?$(t.staticStyle,e):e}function Hr(t){return Array.isArray(t)?L(t):"string"==typeof t?Gr(t):t}var qr,zr=/^--/,Wr=/\s*!important$/,Kr=function(t,e,n){if(zr.test(e))t.style.setProperty(e,n);else if(Wr.test(n))t.style.setProperty(e,n.replace(Wr,""),"important");else{var r=Qr(e);if(Array.isArray(n))for(var i=0,o=n.length;i-1?e.split(/\s+/).forEach(function(e){return t.classList.add(e)}):t.classList.add(e);else{var n=" "+(t.getAttribute("class")||"")+" ";n.indexOf(" "+e+" ")<0&&t.setAttribute("class",(n+e).trim())}}function ti(t,e){if(e&&(e=e.trim()))if(t.classList)e.indexOf(" ")>-1?e.split(/\s+/).forEach(function(e){return t.classList.remove(e)}):t.classList.remove(e),t.classList.length||t.removeAttribute("class");else{for(var n=" "+(t.getAttribute("class")||"")+" ",r=" "+e+" ";n.indexOf(r)>=0;)n=n.replace(r," ");(n=n.trim())?t.setAttribute("class",n):t.removeAttribute("class")}}function ei(t){if(t){if("object"==typeof t){var e={};return!1!==t.css&&$(e,ni(t.name||"v")),$(e,t),e}return"string"==typeof t?ni(t):void 0}}var ni=w(function(t){return{enterClass:t+"-enter",enterToClass:t+"-enter-to",enterActiveClass:t+"-enter-active",leaveClass:t+"-leave",leaveToClass:t+"-leave-to",leaveActiveClass:t+"-leave-active"}}),ri=q&&!Q,ii="transition",oi="animation",si="transition",ai="transitionend",ui="animation",ci="animationend";ri&&(void 0===window.ontransitionend&&void 0!==window.onwebkittransitionend&&(si="WebkitTransition",ai="webkitTransitionEnd"),void 0===window.onanimationend&&void 0!==window.onwebkitanimationend&&(ui="WebkitAnimation",ci="webkitAnimationEnd"));var li=q?window.requestAnimationFrame?window.requestAnimationFrame.bind(window):setTimeout:function(t){return t()};function fi(t){li(function(){li(t)})}function pi(t,e){var n=t._transitionClasses||(t._transitionClasses=[]);n.indexOf(e)<0&&(n.push(e),Zr(t,e))}function di(t,e){t._transitionClasses&&y(t._transitionClasses,e),ti(t,e)}function hi(t,e,n){var r=mi(t,e),i=r.type,o=r.timeout,s=r.propCount;if(!i)return n();var a=i===ii?ai:ci,u=0,c=function(){t.removeEventListener(a,l),n()},l=function(e){e.target===t&&++u>=s&&c()};setTimeout(function(){u0&&(n=ii,l=s,f=o.length):e===oi?c>0&&(n=oi,l=c,f=u.length):f=(n=(l=Math.max(s,c))>0?s>c?ii:oi:null)?n===ii?o.length:u.length:0,{type:n,timeout:l,propCount:f,hasTransform:n===ii&&vi.test(r[si+"Property"])}}function gi(t,e){for(;t.length1}function Oi(t,e){!0!==e.data.show&&bi(e)}var Si=function(t){var e,n,r={},u=t.modules,c=t.nodeOps;for(e=0;eh?b(t,i(n[g+1])?null:n[g+1].elm,n,d,g,r):d>g&&w(0,e,p,h)}(u,d,h,n,a):o(h)?(o(t.text)&&c.setTextContent(u,""),b(u,null,h,0,h.length-1,n)):o(d)?w(0,d,0,d.length-1):o(t.text)&&c.setTextContent(u,""):t.text!==e.text&&c.setTextContent(u,e.text),o(p)&&o(l=p.hook)&&o(l=l.postpatch)&&l(t,e)}}}function C(t,e,n){if(s(n)&&o(t.parent))t.parent.data.pendingInsert=e;else for(var r=0;r-1,s.selected!==o&&(s.selected=o);else if(N($i(s),r))return void(t.selectedIndex!==a&&(t.selectedIndex=a));i||(t.selectedIndex=-1)}}function Ai(t,e){return e.every(function(e){return!N(e,t)})}function $i(t){return"_value"in t?t._value:t.value}function Li(t){t.target.composing=!0}function Ti(t){t.target.composing&&(t.target.composing=!1,Pi(t.target,"input"))}function Pi(t,e){var n=document.createEvent("HTMLEvents");n.initEvent(e,!0,!0),t.dispatchEvent(n)}function ji(t){return!t.componentInstance||t.data&&t.data.transition?t:ji(t.componentInstance._vnode)}var Ni={model:Ci,show:{bind:function(t,e,n){var r=e.value,i=(n=ji(n)).data&&n.data.transition,o=t.__vOriginalDisplay="none"===t.style.display?"":t.style.display;r&&i?(n.data.show=!0,bi(n,function(){t.style.display=o})):t.style.display=r?o:"none"},update:function(t,e,n){var r=e.value;!r!=!e.oldValue&&((n=ji(n)).data&&n.data.transition?(n.data.show=!0,r?bi(n,function(){t.style.display=t.__vOriginalDisplay}):_i(n,function(){t.style.display="none"})):t.style.display=r?t.__vOriginalDisplay:"none")},unbind:function(t,e,n,r,i){i||(t.style.display=t.__vOriginalDisplay)}}},Mi={name:String,appear:Boolean,css:Boolean,mode:String,type:String,enterClass:String,leaveClass:String,enterToClass:String,leaveToClass:String,enterActiveClass:String,leaveActiveClass:String,appearClass:String,appearActiveClass:String,appearToClass:String,duration:[Number,String,Object]};function Ui(t){var e=t&&t.componentOptions;return e&&e.Ctor.options.abstract?Ui(de(e.children)):t}function Ii(t){var e={},n=t.$options;for(var r in n.propsData)e[r]=t[r];var i=n._parentListeners;for(var o in i)e[O(o)]=i[o];return e}function Fi(t,e){if(/\d-keep-alive$/.test(e.tag))return t("keep-alive",{props:e.componentOptions.propsData})}var Ri={name:"transition",props:Mi,abstract:!0,render:function(t){var e=this,n=this.$slots.default;if(n&&(n=n.filter(function(t){return t.tag||pe(t)})).length){0;var r=this.mode;0;var i=n[0];if(function(t){for(;t=t.parent;)if(t.data.transition)return!0}(this.$vnode))return i;var o=Ui(i);if(!o)return i;if(this._leaving)return Fi(t,i);var s="__transition-"+this._uid+"-";o.key=null==o.key?o.isComment?s+"comment":s+o.tag:a(o.key)?0===String(o.key).indexOf(s)?o.key:s+o.key:o.key;var u=(o.data||(o.data={})).transition=Ii(this),c=this._vnode,l=Ui(c);if(o.data.directives&&o.data.directives.some(function(t){return"show"===t.name})&&(o.data.show=!0),l&&l.data&&!function(t,e){return e.key===t.key&&e.tag===t.tag}(o,l)&&!pe(l)&&(!l.componentInstance||!l.componentInstance._vnode.isComment)){var f=l.data.transition=$({},u);if("out-in"===r)return this._leaving=!0,ae(f,"afterLeave",function(){e._leaving=!1,e.$forceUpdate()}),Fi(t,i);if("in-out"===r){if(pe(o))return c;var p,d=function(){p()};ae(u,"afterEnter",d),ae(u,"enterCancelled",d),ae(f,"delayLeave",function(t){p=t})}}return i}}},Di=$({tag:String,moveClass:String},Mi);function Bi(t){t.elm._moveCb&&t.elm._moveCb(),t.elm._enterCb&&t.elm._enterCb()}function Gi(t){t.data.newPos=t.elm.getBoundingClientRect()}function Vi(t){var e=t.data.pos,n=t.data.newPos,r=e.left-n.left,i=e.top-n.top;if(r||i){t.data.moved=!0;var o=t.elm.style;o.transform=o.WebkitTransform="translate("+r+"px,"+i+"px)",o.transitionDuration="0s"}}delete Di.mode;var Hi={Transition:Ri,TransitionGroup:{props:Di,render:function(t){for(var e=this.tag||this.$vnode.data.tag||"span",n=Object.create(null),r=this.prevChildren=this.children,i=this.$slots.default||[],o=this.children=[],s=Ii(this),a=0;a-1?Fn[t]=e.constructor===window.HTMLUnknownElement||e.constructor===window.HTMLElement:Fn[t]=/HTMLUnknownElement/.test(e.toString())},$(pn.options.directives,Ni),$(pn.options.components,Hi),pn.prototype.__patch__=q?Si:T,pn.prototype.$mount=function(t,e){return function(t,e,n){return t.$el=e,t.$options.render||(t.$options.render=vt),Oe(t,"beforeMount"),new Pe(t,function(){t._update(t._render(),n)},T,null,!0),n=!1,null==t.$vnode&&(t._isMounted=!0,Oe(t,"mounted")),t}(this,t=t&&q?Dn(t):void 0,e)},q&&setTimeout(function(){D.devtools&&rt&&rt.emit("init",pn)},0);var qi=/\{\{((?:.|\n)+?)\}\}/g,zi=/[-.*+?^${}()|[\]\/\\]/g,Wi=w(function(t){var e=t[0].replace(zi,"\\$&"),n=t[1].replace(zi,"\\$&");return new RegExp(e+"((?:.|\\n)+?)"+n,"g")});var Ki={staticKeys:["staticClass"],transformNode:function(t,e){e.warn;var n=Sr(t,"class");n&&(t.staticClass=JSON.stringify(n));var r=Or(t,"class",!1);r&&(t.classBinding=r)},genData:function(t){var e="";return t.staticClass&&(e+="staticClass:"+t.staticClass+","),t.classBinding&&(e+="class:"+t.classBinding+","),e}};var Yi,Qi={staticKeys:["staticStyle"],transformNode:function(t,e){e.warn;var n=Sr(t,"style");n&&(t.staticStyle=JSON.stringify(Gr(n)));var r=Or(t,"style",!1);r&&(t.styleBinding=r)},genData:function(t){var e="";return t.staticStyle&&(e+="staticStyle:"+t.staticStyle+","),t.styleBinding&&(e+="style:("+t.styleBinding+"),"),e}},Ji=function(t){return(Yi=Yi||document.createElement("div")).innerHTML=t,Yi.textContent},Xi=v("area,base,br,col,embed,frame,hr,img,input,isindex,keygen,link,meta,param,source,track,wbr"),Zi=v("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source"),to=v("address,article,aside,base,blockquote,body,caption,col,colgroup,dd,details,dialog,div,dl,dt,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,legend,li,menuitem,meta,optgroup,option,param,rp,rt,source,style,summary,tbody,td,tfoot,th,thead,title,tr,track"),eo=/^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,no="[a-zA-Z_][\\w\\-\\.]*",ro="((?:"+no+"\\:)?"+no+")",io=new RegExp("^<"+ro),oo=/^\s*(\/?)>/,so=new RegExp("^<\\/"+ro+"[^>]*>"),ao=/^]+>/i,uo=/^",""":'"',"&":"&"," ":"\n"," ":"\t"},vo=/&(?:lt|gt|quot|amp);/g,mo=/&(?:lt|gt|quot|amp|#10|#9);/g,go=v("pre,textarea",!0),yo=function(t,e){return t&&go(t)&&"\n"===e[0]};function bo(t,e){var n=e?mo:vo;return t.replace(n,function(t){return ho[t]})}var _o,wo,xo,Oo,So,Co,ko,Eo,Ao=/^@|^v-on:/,$o=/^v-|^@|^:/,Lo=/([^]*?)\s+(?:in|of)\s+([^]*)/,To=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,Po=/^\(|\)$/g,jo=/:(.*)$/,No=/^:|^v-bind:/,Mo=/\.[^.]+/g,Uo=w(Ji);function Io(t,e,n){return{type:1,tag:t,attrsList:e,attrsMap:function(t){for(var e={},n=0,r=t.length;n]*>)","i")),p=t.replace(f,function(t,n,r){return c=r.length,fo(l)||"noscript"===l||(n=n.replace(//g,"$1").replace(//g,"$1")),yo(l,n)&&(n=n.slice(1)),e.chars&&e.chars(n),""});u+=t.length-p.length,t=p,k(l,u-c,u)}else{var d=t.indexOf("<");if(0===d){if(uo.test(t)){var h=t.indexOf("--\x3e");if(h>=0){e.shouldKeepComment&&e.comment(t.substring(4,h)),O(h+3);continue}}if(co.test(t)){var v=t.indexOf("]>");if(v>=0){O(v+2);continue}}var m=t.match(ao);if(m){O(m[0].length);continue}var g=t.match(so);if(g){var y=u;O(g[0].length),k(g[1],y,u);continue}var b=S();if(b){C(b),yo(r,t)&&O(1);continue}}var _=void 0,w=void 0,x=void 0;if(d>=0){for(w=t.slice(d);!(so.test(w)||io.test(w)||uo.test(w)||co.test(w)||(x=w.indexOf("<",1))<0);)d+=x,w=t.slice(d);_=t.substring(0,d),O(d)}d<0&&(_=t,t=""),e.chars&&_&&e.chars(_)}if(t===n){e.chars&&e.chars(t);break}}function O(e){u+=e,t=t.substring(e)}function S(){var e=t.match(io);if(e){var n,r,i={tagName:e[1],attrs:[],start:u};for(O(e[0].length);!(n=t.match(oo))&&(r=t.match(eo));)O(r[0].length),i.attrs.push(r);if(n)return i.unarySlash=n[1],O(n[0].length),i.end=u,i}}function C(t){var n=t.tagName,u=t.unarySlash;o&&("p"===r&&to(n)&&k(r),a(n)&&r===n&&k(n));for(var c=s(n)||!!u,l=t.attrs.length,f=new Array(l),p=0;p=0&&i[s].lowerCasedTag!==a;s--);else s=0;if(s>=0){for(var c=i.length-1;c>=s;c--)e.end&&e.end(i[c].tag,n,o);i.length=s,r=s&&i[s-1].tag}else"br"===a?e.start&&e.start(t,[],!0,n,o):"p"===a&&(e.start&&e.start(t,[],!1,n,o),e.end&&e.end(t,n,o))}k()}(t,{warn:_o,expectHTML:e.expectHTML,isUnaryTag:e.isUnaryTag,canBeLeftOpenTag:e.canBeLeftOpenTag,shouldDecodeNewlines:e.shouldDecodeNewlines,shouldDecodeNewlinesForHref:e.shouldDecodeNewlinesForHref,shouldKeepComment:e.comments,start:function(t,o,c){var l=r&&r.ns||Eo(t);Y&&"svg"===l&&(o=function(t){for(var e=[],n=0;nu&&(a.push(o=t.slice(u,i)),s.push(JSON.stringify(o)));var c=hr(r[1].trim());s.push("_s("+c+")"),a.push({"@binding":c}),u=i+r[0].length}return u-1"+("true"===o?":("+e+")":":_q("+e+","+o+")")),xr(t,"change","var $$a="+e+",$$el=$event.target,$$c=$$el.checked?("+o+"):("+s+");if(Array.isArray($$a)){var $$v="+(r?"_n("+i+")":i)+",$$i=_i($$a,$$v);if($$el.checked){$$i<0&&("+kr(e,"$$a.concat([$$v])")+")}else{$$i>-1&&("+kr(e,"$$a.slice(0,$$i).concat($$a.slice($$i+1))")+")}}else{"+kr(e,"$$c")+"}",null,!0)}(t,r,i);else if("input"===o&&"radio"===s)!function(t,e,n){var r=n&&n.number,i=Or(t,"value")||"null";yr(t,"checked","_q("+e+","+(i=r?"_n("+i+")":i)+")"),xr(t,"change",kr(e,i),null,!0)}(t,r,i);else if("input"===o||"textarea"===o)!function(t,e,n){var r=t.attrsMap.type,i=n||{},o=i.lazy,s=i.number,a=i.trim,u=!o&&"range"!==r,c=o?"change":"range"===r?jr:"input",l="$event.target.value";a&&(l="$event.target.value.trim()"),s&&(l="_n("+l+")");var f=kr(e,l);u&&(f="if($event.target.composing)return;"+f),yr(t,"value","("+e+")"),xr(t,c,f,null,!0),(a||s)&&xr(t,"blur","$forceUpdate()")}(t,r,i);else if(!D.isReservedTag(o))return Cr(t,r,i),!1;return!0},text:function(t,e){e.value&&yr(t,"textContent","_s("+e.value+")")},html:function(t,e){e.value&&yr(t,"innerHTML","_s("+e.value+")")}},isPreTag:function(t){return"pre"===t},isUnaryTag:Xi,mustUseProp:xn,canBeLeftOpenTag:Zi,isReservedTag:Un,getTagNamespace:In,staticKeys:function(t){return t.reduce(function(t,e){return t.concat(e.staticKeys||[])},[]).join(",")}(zo)},Qo=w(function(t){return v("type,tag,attrsList,attrsMap,plain,parent,children,attrs"+(t?","+t:""))});function Jo(t,e){t&&(Wo=Qo(e.staticKeys||""),Ko=e.isReservedTag||P,function t(e){e.static=function(t){if(2===t.type)return!1;if(3===t.type)return!0;return!(!t.pre&&(t.hasBindings||t.if||t.for||m(t.tag)||!Ko(t.tag)||function(t){for(;t.parent;){if("template"!==(t=t.parent).tag)return!1;if(t.for)return!0}return!1}(t)||!Object.keys(t).every(Wo)))}(e);if(1===e.type){if(!Ko(e.tag)&&"slot"!==e.tag&&null==e.attrsMap["inline-template"])return;for(var n=0,r=e.children.length;n|^function\s*\(/,Zo=/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/,ts={esc:27,tab:9,enter:13,space:32,up:38,left:37,right:39,down:40,delete:[8,46]},es={esc:"Escape",tab:"Tab",enter:"Enter",space:" ",up:["Up","ArrowUp"],left:["Left","ArrowLeft"],right:["Right","ArrowRight"],down:["Down","ArrowDown"],delete:["Backspace","Delete"]},ns=function(t){return"if("+t+")return null;"},rs={stop:"$event.stopPropagation();",prevent:"$event.preventDefault();",self:ns("$event.target !== $event.currentTarget"),ctrl:ns("!$event.ctrlKey"),shift:ns("!$event.shiftKey"),alt:ns("!$event.altKey"),meta:ns("!$event.metaKey"),left:ns("'button' in $event && $event.button !== 0"),middle:ns("'button' in $event && $event.button !== 1"),right:ns("'button' in $event && $event.button !== 2")};function is(t,e,n){var r=e?"nativeOn:{":"on:{";for(var i in t)r+='"'+i+'":'+os(i,t[i])+",";return r.slice(0,-1)+"}"}function os(t,e){if(!e)return"function(){}";if(Array.isArray(e))return"["+e.map(function(e){return os(t,e)}).join(",")+"]";var n=Zo.test(e.value),r=Xo.test(e.value);if(e.modifiers){var i="",o="",s=[];for(var a in e.modifiers)if(rs[a])o+=rs[a],ts[a]&&s.push(a);else if("exact"===a){var u=e.modifiers;o+=ns(["ctrl","shift","alt","meta"].filter(function(t){return!u[t]}).map(function(t){return"$event."+t+"Key"}).join("||"))}else s.push(a);return s.length&&(i+=function(t){return"if(!('button' in $event)&&"+t.map(ss).join("&&")+")return null;"}(s)),o&&(i+=o),"function($event){"+i+(n?"return "+e.value+"($event)":r?"return ("+e.value+")($event)":e.value)+"}"}return n||r?e.value:"function($event){"+e.value+"}"}function ss(t){var e=parseInt(t,10);if(e)return"$event.keyCode!=="+e;var n=ts[t],r=es[t];return"_k($event.keyCode,"+JSON.stringify(t)+","+JSON.stringify(n)+",$event.key,"+JSON.stringify(r)+")"}var as={on:function(t,e){t.wrapListeners=function(t){return"_g("+t+","+e.value+")"}},bind:function(t,e){t.wrapData=function(n){return"_b("+n+",'"+t.tag+"',"+e.value+","+(e.modifiers&&e.modifiers.prop?"true":"false")+(e.modifiers&&e.modifiers.sync?",true":"")+")"}},cloak:T},us=function(t){this.options=t,this.warn=t.warn||mr,this.transforms=gr(t.modules,"transformCode"),this.dataGenFns=gr(t.modules,"genData"),this.directives=$($({},as),t.directives);var e=t.isReservedTag||P;this.maybeComponent=function(t){return!e(t.tag)},this.onceId=0,this.staticRenderFns=[]};function cs(t,e){var n=new us(e);return{render:"with(this){return "+(t?ls(t,n):'_c("div")')+"}",staticRenderFns:n.staticRenderFns}}function ls(t,e){if(t.staticRoot&&!t.staticProcessed)return fs(t,e);if(t.once&&!t.onceProcessed)return ps(t,e);if(t.for&&!t.forProcessed)return function(t,e,n,r){var i=t.for,o=t.alias,s=t.iterator1?","+t.iterator1:"",a=t.iterator2?","+t.iterator2:"";0;return t.forProcessed=!0,(r||"_l")+"(("+i+"),function("+o+s+a+"){return "+(n||ls)(t,e)+"})"}(t,e);if(t.if&&!t.ifProcessed)return ds(t,e);if("template"!==t.tag||t.slotTarget){if("slot"===t.tag)return function(t,e){var n=t.slotName||'"default"',r=ms(t,e),i="_t("+n+(r?","+r:""),o=t.attrs&&"{"+t.attrs.map(function(t){return O(t.name)+":"+t.value}).join(",")+"}",s=t.attrsMap["v-bind"];!o&&!s||r||(i+=",null");o&&(i+=","+o);s&&(i+=(o?"":",null")+","+s);return i+")"}(t,e);var n;if(t.component)n=function(t,e,n){var r=e.inlineTemplate?null:ms(e,n,!0);return"_c("+t+","+hs(e,n)+(r?","+r:"")+")"}(t.component,t,e);else{var r=t.plain?void 0:hs(t,e),i=t.inlineTemplate?null:ms(t,e,!0);n="_c('"+t.tag+"'"+(r?","+r:"")+(i?","+i:"")+")"}for(var o=0;o':'
',Os.innerHTML.indexOf(" ")>0}var ks=!!q&&Cs(!1),Es=!!q&&Cs(!0),As=w(function(t){var e=Dn(t);return e&&e.innerHTML}),$s=pn.prototype.$mount;pn.prototype.$mount=function(t,e){if((t=t&&Dn(t))===document.body||t===document.documentElement)return this;var n=this.$options;if(!n.render){var r=n.template;if(r)if("string"==typeof r)"#"===r.charAt(0)&&(r=As(r));else{if(!r.nodeType)return this;r=r.innerHTML}else t&&(r=function(t){if(t.outerHTML)return t.outerHTML;var e=document.createElement("div");return e.appendChild(t.cloneNode(!0)),e.innerHTML}(t));if(r){0;var i=Ss(r,{shouldDecodeNewlines:ks,shouldDecodeNewlinesForHref:Es,delimiters:n.delimiters,comments:n.comments},this),o=i.render,s=i.staticRenderFns;n.render=o,n.staticRenderFns=s}}return $s.call(this,t,e)},pn.compile=Ss,e.a=pn}).call(this,n(51),n(239).setImmediate)},function(t,e){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,e,n){var r=n(3),i=n(100),o=n(44),s=Object.defineProperty;e.f=n(9)?Object.defineProperty:function(t,e,n){if(r(t),e=o(e,!0),r(n),i)try{return s(t,e,n)}catch(t){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(t[e]=n.value),t}},function(t,e,n){var r=n(23),i=Math.min;t.exports=function(t){return t>0?i(r(t),9007199254740991):0}},function(t,e,n){t.exports=!n(6)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(t,e,n){"use strict";var r=n(105),i=n(236),o=Object.prototype.toString;function s(t){return"[object Array]"===o.call(t)}function a(t){return null!==t&&"object"==typeof t}function u(t){return"[object Function]"===o.call(t)}function c(t,e){if(null!==t&&void 0!==t)if("object"!=typeof t&&(t=[t]),s(t))for(var n=0,r=t.length;nn;)i[n]=e[n++];return i},$t=function(t,e,n){B(t,e,{get:function(){return this._d[n]}})},Lt=function(t){var e,n,r,i,o,s,a=x(t),u=arguments.length,l=u>1?arguments[1]:void 0,f=void 0!==l,p=E(a);if(void 0!=p&&!O(p)){for(s=p.call(a),r=[],e=0;!(o=s.next()).done;e++)r.push(o.value);a=r}for(f&&u>2&&(l=c(l,arguments[2],2)),e=0,n=v(a.length),i=kt(this,n);n>e;e++)i[e]=f?l(a[e],e):a[e];return i},Tt=function(){for(var t=0,e=arguments.length,n=kt(this,e);e>t;)n[t]=arguments[t++];return n},Pt=!!q&&o(function(){dt.call(new q(1))}),jt=function(){return dt.apply(Pt?ft.call(Ct(this)):Ct(this),arguments)},Nt={copyWithin:function(t,e){return F.call(Ct(this),t,e,arguments.length>2?arguments[2]:void 0)},every:function(t){return X(Ct(this),t,arguments.length>1?arguments[1]:void 0)},fill:function(t){return I.apply(Ct(this),arguments)},filter:function(t){return Et(this,Q(Ct(this),t,arguments.length>1?arguments[1]:void 0))},find:function(t){return Z(Ct(this),t,arguments.length>1?arguments[1]:void 0)},findIndex:function(t){return tt(Ct(this),t,arguments.length>1?arguments[1]:void 0)},forEach:function(t){Y(Ct(this),t,arguments.length>1?arguments[1]:void 0)},indexOf:function(t){return nt(Ct(this),t,arguments.length>1?arguments[1]:void 0)},includes:function(t){return et(Ct(this),t,arguments.length>1?arguments[1]:void 0)},join:function(t){return ct.apply(Ct(this),arguments)},lastIndexOf:function(t){return st.apply(Ct(this),arguments)},map:function(t){return wt(Ct(this),t,arguments.length>1?arguments[1]:void 0)},reduce:function(t){return at.apply(Ct(this),arguments)},reduceRight:function(t){return ut.apply(Ct(this),arguments)},reverse:function(){for(var t,e=Ct(this).length,n=Math.floor(e/2),r=0;r1?arguments[1]:void 0)},sort:function(t){return lt.call(Ct(this),t)},subarray:function(t,e){var n=Ct(this),r=n.length,i=g(t,r);return new(P(n,n[gt]))(n.buffer,n.byteOffset+i*n.BYTES_PER_ELEMENT,v((void 0===e?r:g(e,r))-i))}},Mt=function(t,e){return Et(this,ft.call(Ct(this),t,e))},Ut=function(t){Ct(this);var e=St(arguments[1],1),n=this.length,r=x(t),i=v(r.length),o=0;if(i+e>n)throw V("Wrong length!");for(;o255?255:255&r),i.v[d](n*e+i.o,r,xt)}(this,n,t)},enumerable:!0})};b?(h=n(function(t,n,r,i){l(t,h,c,"_d");var o,s,a,u,f=0,d=0;if(w(n)){if(!(n instanceof W||"ArrayBuffer"==(u=_(n))||"SharedArrayBuffer"==u))return bt in n?At(h,n):Lt.call(h,n);o=n,d=St(r,e);var g=n.byteLength;if(void 0===i){if(g%e)throw V("Wrong length!");if((s=g-d)<0)throw V("Wrong length!")}else if((s=v(i)*e)+d>g)throw V("Wrong length!");a=s/e}else a=m(n),o=new W(s=a*e);for(p(t,"_d",{b:o,o:d,l:s,e:a,v:new K(o)});f0?r:n)(t)}},function(t,e){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},function(t,e){var n=0,r=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++n+r).toString(36))}},function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},function(t,e,n){var r=n(1);t.exports=function(t,e){if(!r(t)||t._t!==e)throw TypeError("Incompatible receiver, "+e+" required!");return t}},function(t,e,n){var r=n(4)("unscopables"),i=Array.prototype;void 0==i[r]&&n(12)(i,r,{}),t.exports=function(t){i[r][t]=!0}},function(t,e,n){var r=n(11),i=n(17),o=n(69)("IE_PROTO"),s=Object.prototype;t.exports=Object.getPrototypeOf||function(t){return t=i(t),r(t,o)?t[o]:"function"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?s:null}},function(t,e){t.exports={}},function(t,e,n){var r=n(7).f,i=n(11),o=n(4)("toStringTag");t.exports=function(t,e,n){t&&!i(t=n?t:t.prototype,o)&&r(t,o,{configurable:!0,value:e})}},function(t,e,n){var r=n(23),i=Math.max,o=Math.min;t.exports=function(t,e){return(t=r(t))<0?i(t+e,0):o(t,e)}},function(t,e){var n={}.toString;t.exports=function(t){return n.call(t).slice(8,-1)}},function(t,e){t.exports=function(t,e,n,r){if(!(t instanceof e)||void 0!==r&&r in t)throw TypeError(n+": incorrect invocation!");return t}},function(t,e,n){var r=n(20);t.exports=function(t,e,n){for(var i in e)r(t,i,e[i],n);return t}},function(t,e){t.exports=!1},function(t,e){var n=t.exports={version:"2.5.5"};"number"==typeof __e&&(__e=n)},function(t,e,n){t.exports=function(t){function e(r){if(n[r])return n[r].exports;var i=n[r]={i:r,l:!1,exports:{}};return t[r].call(i.exports,i,i.exports,e),i.l=!0,i.exports}var n={};return e.m=t,e.c=n,e.i=function(t){return t},e.d=function(t,n,r){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:r})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="/",e(e.s=66)}([function(t,e){var n=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},function(t,e,n){t.exports=!n(12)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(t,e){var n={}.hasOwnProperty;t.exports=function(t,e){return n.call(t,e)}},function(t,e,n){var r=n(10),i=n(43),o=n(31),s=Object.defineProperty;e.f=n(1)?Object.defineProperty:function(t,e,n){if(r(t),e=o(e,!0),r(n),i)try{return s(t,e,n)}catch(t){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(t[e]=n.value),t}},function(t,e,n){var r=n(77),i=n(21);t.exports=function(t){return r(i(t))}},function(t,e,n){var r=n(9),i=n(52),o=n(18),s=n(55),a=n(53),u=function(t,e,n){var c,l,f,p,d=t&u.F,h=t&u.G,v=t&u.S,m=t&u.P,g=t&u.B,y=h?r:v?r[e]||(r[e]={}):(r[e]||{}).prototype,b=h?i:i[e]||(i[e]={}),_=b.prototype||(b.prototype={});for(c in h&&(n=e),n)l=!d&&y&&void 0!==y[c],f=(l?y:n)[c],p=g&&l?a(f,r):m&&"function"==typeof f?a(Function.call,f):f,y&&s(y,c,f,t&u.U),b[c]!=f&&o(b,c,p),m&&_[c]!=f&&(_[c]=f)};r.core=i,u.F=1,u.G=2,u.S=4,u.P=8,u.B=16,u.W=32,u.U=64,u.R=128,t.exports=u},function(t,e,n){var r=n(3),i=n(15);t.exports=n(1)?function(t,e,n){return r.f(t,e,i(1,n))}:function(t,e,n){return t[e]=n,t}},function(t,e,n){var r=n(29)("wks"),i=n(16),o=n(0).Symbol,s="function"==typeof o;(t.exports=function(t){return r[t]||(r[t]=s&&o[t]||(s?o:i)("Symbol."+t))}).store=r},function(t,e){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,e){var n=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},function(t,e,n){var r=n(13);t.exports=function(t){if(!r(t))throw TypeError(t+" is not an object!");return t}},function(t,e){var n=t.exports={version:"2.4.0"};"number"==typeof __e&&(__e=n)},function(t,e){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,e){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,e,n){var r=n(48),i=n(22);t.exports=Object.keys||function(t){return r(t,i)}},function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},function(t,e){var n=0,r=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++n+r).toString(36))}},function(t,e){t.exports=function(t){if(void 0==t)throw TypeError("Can't call method on "+t);return t}},function(t,e,n){var r=n(109),i=n(110);t.exports=n(35)?function(t,e,n){return r.f(t,e,i(1,n))}:function(t,e,n){return t[e]=n,t}},function(t,e){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,e,n){var r=n(8);t.exports=function(t,e){return!!t&&r(function(){e?t.call(null,function(){},1):t.call(null)})}},function(t,e){t.exports=function(t){if(void 0==t)throw TypeError("Can't call method on "+t);return t}},function(t,e){t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(t,e,n){var r=n(0),i=n(11),o=n(74),s=n(6),a=function(t,e,n){var u,c,l,f=t&a.F,p=t&a.G,d=t&a.S,h=t&a.P,v=t&a.B,m=t&a.W,g=p?i:i[e]||(i[e]={}),y=g.prototype,b=p?r:d?r[e]:(r[e]||{}).prototype;for(u in p&&(n=e),n)(c=!f&&b&&void 0!==b[u])&&u in g||(l=c?b[u]:n[u],g[u]=p&&"function"!=typeof b[u]?n[u]:v&&c?o(l,r):m&&b[u]==l?function(t){var e=function(e,n,r){if(this instanceof t){switch(arguments.length){case 0:return new t;case 1:return new t(e);case 2:return new t(e,n)}return new t(e,n,r)}return t.apply(this,arguments)};return e.prototype=t.prototype,e}(l):h&&"function"==typeof l?o(Function.call,l):l,h&&((g.virtual||(g.virtual={}))[u]=l,t&a.R&&y&&!y[u]&&s(y,u,l)))};a.F=1,a.G=2,a.S=4,a.P=8,a.B=16,a.W=32,a.U=64,a.R=128,t.exports=a},function(t,e){t.exports={}},function(t,e){t.exports=!0},function(t,e){e.f={}.propertyIsEnumerable},function(t,e,n){var r=n(3).f,i=n(2),o=n(7)("toStringTag");t.exports=function(t,e,n){t&&!i(t=n?t:t.prototype,o)&&r(t,o,{configurable:!0,value:e})}},function(t,e,n){var r=n(29)("keys"),i=n(16);t.exports=function(t){return r[t]||(r[t]=i(t))}},function(t,e,n){var r=n(0),i=r["__core-js_shared__"]||(r["__core-js_shared__"]={});t.exports=function(t){return i[t]||(i[t]={})}},function(t,e){var n=Math.ceil,r=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?r:n)(t)}},function(t,e,n){var r=n(13);t.exports=function(t,e){if(!r(t))return t;var n,i;if(e&&"function"==typeof(n=t.toString)&&!r(i=n.call(t)))return i;if("function"==typeof(n=t.valueOf)&&!r(i=n.call(t)))return i;if(!e&&"function"==typeof(n=t.toString)&&!r(i=n.call(t)))return i;throw TypeError("Can't convert object to primitive value")}},function(t,e,n){var r=n(0),i=n(11),o=n(25),s=n(33),a=n(3).f;t.exports=function(t){var e=i.Symbol||(i.Symbol=o?{}:r.Symbol||{});"_"==t.charAt(0)||t in e||a(e,t,{value:s.f(t)})}},function(t,e,n){e.f=n(7)},function(t,e,n){var r=n(53),i=n(36),o=n(57),s=n(37),a=n(104);t.exports=function(t,e){var n=1==t,u=2==t,c=3==t,l=4==t,f=6==t,p=5==t||f,d=e||a;return function(e,a,h){for(var v,m,g=o(e),y=i(g),b=r(a,h,3),_=s(y.length),w=0,x=n?d(e,_):u?d(e,0):void 0;_>w;w++)if((p||w in y)&&(v=y[w],m=b(v,w,g),t))if(n)x[w]=m;else if(m)switch(t){case 3:return!0;case 5:return v;case 6:return w;case 2:x.push(v)}else if(l)return!1;return f?-1:c||l?l:x}}},function(t,e,n){t.exports=!n(8)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(t,e,n){var r=n(51);t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==r(t)?t.split(""):Object(t)}},function(t,e,n){var r=n(56),i=Math.min;t.exports=function(t){return t>0?i(r(t),9007199254740991):0}},function(t,e,n){var r=n(111)("wks"),i=n(58),o=n(9).Symbol,s="function"==typeof o;(t.exports=function(t){return r[t]||(r[t]=s&&o[t]||(s?o:i)("Symbol."+t))}).store=r},function(t,e,n){"use strict";function r(t){return!(0===t||(!Array.isArray(t)||0!==t.length)&&t)}function i(t){return function(){return!t.apply(void 0,arguments)}}function o(t,e,n,r){return t.filter(function(t){return function(t,e){return void 0===t&&(t="undefined"),null===t&&(t="null"),!1===t&&(t="false"),-1!==t.toString().toLowerCase().indexOf(e.trim())}(r(t,n),e)})}function s(t){return t.filter(function(t){return!t.$isLabel})}function a(t,e){return function(n){return n.reduce(function(n,r){return r[t]&&r[t].length?(n.push({$groupLabel:r[e],$isLabel:!0}),n.concat(r[t])):n},[])}}var u=n(65),c=n.n(u),l=n(59),f=(n.n(l),n(122)),p=(n.n(f),n(64)),d=n.n(p),h=n(120),v=(n.n(h),n(121)),m=(n.n(v),n(117)),g=(n.n(m),n(123)),y=(n.n(g),n(118)),b=(n.n(y),n(119)),_=(n.n(b),function(){for(var t=arguments.length,e=new Array(t),n=0;n-1},isSelected:function(t){var e=this.trackBy?t[this.trackBy]:t;return this.valueKeys.indexOf(e)>-1},getOptionLabel:function(t){if(r(t))return"";if(t.isTag)return t.label;if(t.$isLabel)return t.$groupLabel;var e=this.customLabel(t,this.label);return r(e)?"":e},select:function(t,e){if(t.$isLabel&&this.groupSelect)this.selectGroup(t);else if(!(-1!==this.blockKeys.indexOf(e)||this.disabled||t.$isDisabled||t.$isLabel)&&(!this.max||!this.multiple||this.internalValue.length!==this.max)&&("Tab"!==e||this.pointerDirty)){if(t.isTag)this.$emit("tag",t.label,this.id),this.search="",this.closeOnSelect&&!this.multiple&&this.deactivate();else{if(this.isSelected(t))return void("Tab"!==e&&this.removeElement(t));this.$emit("select",t,this.id),this.multiple?this.$emit("input",this.internalValue.concat([t]),this.id):this.$emit("input",t,this.id),this.clearOnSelect&&(this.search="")}this.closeOnSelect&&this.deactivate()}},selectGroup:function(t){var e=this,n=this.options.find(function(n){return n[e.groupLabel]===t.$groupLabel});if(n)if(this.wholeGroupSelected(n)){this.$emit("remove",n[this.groupValues],this.id);var r=this.internalValue.filter(function(t){return-1===n[e.groupValues].indexOf(t)});this.$emit("input",r,this.id)}else{var o=n[this.groupValues].filter(i(this.isSelected));this.$emit("select",o,this.id),this.$emit("input",this.internalValue.concat(o),this.id)}},wholeGroupSelected:function(t){return t[this.groupValues].every(this.isSelected)},removeElement:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];if(!this.disabled){if(!this.allowEmpty&&this.internalValue.length<=1)return void this.deactivate();var n="object"===c()(t)?this.valueKeys.indexOf(t[this.trackBy]):this.valueKeys.indexOf(t);if(this.$emit("remove",t,this.id),this.multiple){var r=this.internalValue.slice(0,n).concat(this.internalValue.slice(n+1));this.$emit("input",r,this.id)}else this.$emit("input",null,this.id);this.closeOnSelect&&e&&this.deactivate()}},removeLastElement:function(){-1===this.blockKeys.indexOf("Delete")&&0===this.search.length&&Array.isArray(this.internalValue)&&this.removeElement(this.internalValue[this.internalValue.length-1],!1)},activate:function(){var t=this;this.isOpen||this.disabled||(this.adjustPosition(),this.groupValues&&0===this.pointer&&this.filteredOptions.length&&(this.pointer=1),this.isOpen=!0,this.searchable?(this.preserveSearch||(this.search=""),this.$nextTick(function(){return t.$refs.search.focus()})):this.$el.focus(),this.$emit("open",this.id))},deactivate:function(){this.isOpen&&(this.isOpen=!1,this.searchable?this.$refs.search.blur():this.$el.blur(),this.preserveSearch||(this.search=""),this.$emit("close",this.getValue(),this.id))},toggle:function(){this.isOpen?this.deactivate():this.activate()},adjustPosition:function(){if("undefined"!=typeof window){var t=this.$el.getBoundingClientRect().top,e=window.innerHeight-this.$el.getBoundingClientRect().bottom;e>this.maxHeight||e>t||"below"===this.openDirection||"bottom"===this.openDirection?(this.prefferedOpenDirection="below",this.optimizedHeight=Math.min(e-40,this.maxHeight)):(this.prefferedOpenDirection="above",this.optimizedHeight=Math.min(t-40,this.maxHeight))}}}}},function(t,e,n){"use strict";var r=n(59);n.n(r),e.a={data:function(){return{pointer:0,pointerDirty:!1}},props:{showPointer:{type:Boolean,default:!0},optionHeight:{type:Number,default:40}},computed:{pointerPosition:function(){return this.pointer*this.optionHeight},visibleElements:function(){return this.optimizedHeight/this.optionHeight}},watch:{filteredOptions:function(){this.pointerAdjust()},isOpen:function(){this.pointerDirty=!1}},methods:{optionHighlight:function(t,e){return{"multiselect__option--highlight":t===this.pointer&&this.showPointer,"multiselect__option--selected":this.isSelected(e)}},groupHighlight:function(t,e){var n=this;if(!this.groupSelect)return["multiselect__option--disabled"];var r=this.options.find(function(t){return t[n.groupLabel]===e.$groupLabel});return[this.groupSelect?"multiselect__option--group":"multiselect__option--disabled",{"multiselect__option--highlight":t===this.pointer&&this.showPointer},{"multiselect__option--group-selected":this.wholeGroupSelected(r)}]},addPointerElement:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"Enter",e=t.key;this.filteredOptions.length>0&&this.select(this.filteredOptions[this.pointer],e),this.pointerReset()},pointerForward:function(){this.pointer0?(this.pointer--,this.$refs.list.scrollTop>=this.pointerPosition&&(this.$refs.list.scrollTop=this.pointerPosition),this.filteredOptions[this.pointer]&&this.filteredOptions[this.pointer].$isLabel&&!this.groupSelect&&this.pointerBackward()):this.filteredOptions[this.pointer]&&this.filteredOptions[0].$isLabel&&!this.groupSelect&&this.pointerForward(),this.pointerDirty=!0},pointerReset:function(){this.closeOnSelect&&(this.pointer=0,this.$refs.list&&(this.$refs.list.scrollTop=0))},pointerAdjust:function(){this.pointer>=this.filteredOptions.length-1&&(this.pointer=this.filteredOptions.length?this.filteredOptions.length-1:0),this.filteredOptions.length>0&&this.filteredOptions[this.pointer].$isLabel&&!this.groupSelect&&this.pointerForward()},pointerSet:function(t){this.pointer=t,this.pointerDirty=!0}}}},function(t,e){var n={}.toString;t.exports=function(t){return n.call(t).slice(8,-1)}},function(t,e,n){var r=n(13),i=n(0).document,o=r(i)&&r(i.createElement);t.exports=function(t){return o?i.createElement(t):{}}},function(t,e,n){t.exports=!n(1)&&!n(12)(function(){return 7!=Object.defineProperty(n(42)("div"),"a",{get:function(){return 7}}).a})},function(t,e,n){"use strict";var r=n(25),i=n(23),o=n(49),s=n(6),a=n(2),u=n(24),c=n(79),l=n(27),f=n(86),p=n(7)("iterator"),d=!([].keys&&"next"in[].keys()),h=function(){return this};t.exports=function(t,e,n,v,m,g,y){c(n,e,v);var b,_,w,x=function(t){if(!d&&t in k)return k[t];switch(t){case"keys":case"values":return function(){return new n(this,t)}}return function(){return new n(this,t)}},O=e+" Iterator",S="values"==m,C=!1,k=t.prototype,E=k[p]||k["@@iterator"]||m&&k[m],A=E||x(m),$=m?S?x("entries"):A:void 0,L="Array"==e&&k.entries||E;if(L&&(w=f(L.call(new t)))!==Object.prototype&&(l(w,O,!0),r||a(w,p)||s(w,p,h)),S&&E&&"values"!==E.name&&(C=!0,A=function(){return E.call(this)}),r&&!y||!d&&!C&&k[p]||s(k,p,A),u[e]=A,u[O]=h,m)if(b={values:S?A:x("values"),keys:g?A:x("keys"),entries:$},y)for(_ in b)_ in k||o(k,_,b[_]);else i(i.P+i.F*(d||C),e,b);return b}},function(t,e,n){var r=n(10),i=n(83),o=n(22),s=n(28)("IE_PROTO"),a=function(){},u=function(){var t,e=n(42)("iframe"),r=o.length;for(e.style.display="none",n(76).appendChild(e),e.src="javascript:",(t=e.contentWindow.document).open(),t.write("\n","var render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"router-view\")\n}\nvar staticRenderFns = []\nrender._withStripped = true\nexport { render, staticRenderFns }\nif (module.hot) {\n module.hot.accept()\n if (module.hot.data) {\n require(\"vue-hot-reload-api\") .rerender(\"data-v-7ba5bd90\", { render: render, staticRenderFns: staticRenderFns })\n }\n}","/* globals __VUE_SSR_CONTEXT__ */\n\n// IMPORTANT: Do NOT use ES2015 features in this file (except for modules).\n// This module is a runtime utility for cleaner component module output and will\n// be included in the final webpack user bundle.\n\nexport default function normalizeComponent (\n scriptExports,\n render,\n staticRenderFns,\n functionalTemplate,\n injectStyles,\n scopeId,\n moduleIdentifier, /* server only */\n shadowMode /* vue-cli only */\n) {\n scriptExports = scriptExports || {}\n\n // ES6 modules interop\n var type = typeof scriptExports.default\n if (type === 'object' || type === 'function') {\n scriptExports = scriptExports.default\n }\n\n // Vue.extend constructor export interop\n var options = typeof scriptExports === 'function'\n ? scriptExports.options\n : scriptExports\n\n // render functions\n if (render) {\n options.render = render\n options.staticRenderFns = staticRenderFns\n options._compiled = true\n }\n\n // functional template\n if (functionalTemplate) {\n options.functional = true\n }\n\n // scopedId\n if (scopeId) {\n options._scopeId = scopeId\n }\n\n var hook\n if (moduleIdentifier) { // server build\n hook = function (context) {\n // 2.3 injection\n context =\n context || // cached call\n (this.$vnode && this.$vnode.ssrContext) || // stateful\n (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional\n // 2.2 with runInNewContext: true\n if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {\n context = __VUE_SSR_CONTEXT__\n }\n // inject component styles\n if (injectStyles) {\n injectStyles.call(this, context)\n }\n // register component module identifier for async chunk inferrence\n if (context && context._registeredComponents) {\n context._registeredComponents.add(moduleIdentifier)\n }\n }\n // used by ssr in case component is cached and beforeCreate\n // never gets called\n options._ssrRegister = hook\n } else if (injectStyles) {\n hook = shadowMode\n ? function () { injectStyles.call(this, this.$root.$options.shadowRoot) }\n : injectStyles\n }\n\n if (hook) {\n if (options.functional) {\n // for template-only hot-reload because in that case the render fn doesn't\n // go through the normalizer\n options._injectStyles = hook\n // register for functioal component in vue file\n var originalRender = options.render\n options.render = function renderWithStyleInjection (h, context) {\n hook.call(context)\n return originalRender(h, context)\n }\n } else {\n // inject component registration as beforeCreate hook\n var existing = options.beforeCreate\n options.beforeCreate = existing\n ? [].concat(existing, hook)\n : [hook]\n }\n }\n\n return {\n exports: scriptExports,\n options: options\n }\n}\n","var disposed = false\n/* script */\nexport * from \"!!babel-loader!../node_modules/vue-loader/lib/selector?type=script&index=0!./App.vue\"\nimport __vue_script__ from \"!!babel-loader!../node_modules/vue-loader/lib/selector?type=script&index=0!./App.vue\"\n/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-7ba5bd90\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../node_modules/vue-loader/lib/selector?type=template&index=0!./App.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = null\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\nComponent.options.__file = \"src/App.vue\"\n\n/* hot reload */\nif (module.hot) {(function () {\n var hotAPI = require(\"vue-hot-reload-api\")\n hotAPI.install(require(\"vue\"), false)\n if (!hotAPI.compatible) return\n module.hot.accept()\n if (!module.hot.data) {\n hotAPI.createRecord(\"data-v-7ba5bd90\", Component.options)\n } else {\n hotAPI.reload(\"data-v-7ba5bd90\", Component.options)\n }\n module.hot.dispose(function (data) {\n disposed = true\n })\n})()}\n\nexport default Component.exports\n","/**\n * vue-router v3.0.1\n * (c) 2017 Evan You\n * @license MIT\n */\n/* */\n\nfunction assert (condition, message) {\n if (!condition) {\n throw new Error((\"[vue-router] \" + message))\n }\n}\n\nfunction warn (condition, message) {\n if (process.env.NODE_ENV !== 'production' && !condition) {\n typeof console !== 'undefined' && console.warn((\"[vue-router] \" + message));\n }\n}\n\nfunction isError (err) {\n return Object.prototype.toString.call(err).indexOf('Error') > -1\n}\n\nvar View = {\n name: 'router-view',\n functional: true,\n props: {\n name: {\n type: String,\n default: 'default'\n }\n },\n render: function render (_, ref) {\n var props = ref.props;\n var children = ref.children;\n var parent = ref.parent;\n var data = ref.data;\n\n data.routerView = true;\n\n // directly use parent context's createElement() function\n // so that components rendered by router-view can resolve named slots\n var h = parent.$createElement;\n var name = props.name;\n var route = parent.$route;\n var cache = parent._routerViewCache || (parent._routerViewCache = {});\n\n // determine current view depth, also check to see if the tree\n // has been toggled inactive but kept-alive.\n var depth = 0;\n var inactive = false;\n while (parent && parent._routerRoot !== parent) {\n if (parent.$vnode && parent.$vnode.data.routerView) {\n depth++;\n }\n if (parent._inactive) {\n inactive = true;\n }\n parent = parent.$parent;\n }\n data.routerViewDepth = depth;\n\n // render previous view if the tree is inactive and kept-alive\n if (inactive) {\n return h(cache[name], data, children)\n }\n\n var matched = route.matched[depth];\n // render empty node if no matched route\n if (!matched) {\n cache[name] = null;\n return h()\n }\n\n var component = cache[name] = matched.components[name];\n\n // attach instance registration hook\n // this will be called in the instance's injected lifecycle hooks\n data.registerRouteInstance = function (vm, val) {\n // val could be undefined for unregistration\n var current = matched.instances[name];\n if (\n (val && current !== vm) ||\n (!val && current === vm)\n ) {\n matched.instances[name] = val;\n }\n }\n\n // also register instance in prepatch hook\n // in case the same component instance is reused across different routes\n ;(data.hook || (data.hook = {})).prepatch = function (_, vnode) {\n matched.instances[name] = vnode.componentInstance;\n };\n\n // resolve props\n var propsToPass = data.props = resolveProps(route, matched.props && matched.props[name]);\n if (propsToPass) {\n // clone to prevent mutation\n propsToPass = data.props = extend({}, propsToPass);\n // pass non-declared props as attrs\n var attrs = data.attrs = data.attrs || {};\n for (var key in propsToPass) {\n if (!component.props || !(key in component.props)) {\n attrs[key] = propsToPass[key];\n delete propsToPass[key];\n }\n }\n }\n\n return h(component, data, children)\n }\n};\n\nfunction resolveProps (route, config) {\n switch (typeof config) {\n case 'undefined':\n return\n case 'object':\n return config\n case 'function':\n return config(route)\n case 'boolean':\n return config ? route.params : undefined\n default:\n if (process.env.NODE_ENV !== 'production') {\n warn(\n false,\n \"props in \\\"\" + (route.path) + \"\\\" is a \" + (typeof config) + \", \" +\n \"expecting an object, function or boolean.\"\n );\n }\n }\n}\n\nfunction extend (to, from) {\n for (var key in from) {\n to[key] = from[key];\n }\n return to\n}\n\n/* */\n\nvar encodeReserveRE = /[!'()*]/g;\nvar encodeReserveReplacer = function (c) { return '%' + c.charCodeAt(0).toString(16); };\nvar commaRE = /%2C/g;\n\n// fixed encodeURIComponent which is more conformant to RFC3986:\n// - escapes [!'()*]\n// - preserve commas\nvar encode = function (str) { return encodeURIComponent(str)\n .replace(encodeReserveRE, encodeReserveReplacer)\n .replace(commaRE, ','); };\n\nvar decode = decodeURIComponent;\n\nfunction resolveQuery (\n query,\n extraQuery,\n _parseQuery\n) {\n if ( extraQuery === void 0 ) extraQuery = {};\n\n var parse = _parseQuery || parseQuery;\n var parsedQuery;\n try {\n parsedQuery = parse(query || '');\n } catch (e) {\n process.env.NODE_ENV !== 'production' && warn(false, e.message);\n parsedQuery = {};\n }\n for (var key in extraQuery) {\n parsedQuery[key] = extraQuery[key];\n }\n return parsedQuery\n}\n\nfunction parseQuery (query) {\n var res = {};\n\n query = query.trim().replace(/^(\\?|#|&)/, '');\n\n if (!query) {\n return res\n }\n\n query.split('&').forEach(function (param) {\n var parts = param.replace(/\\+/g, ' ').split('=');\n var key = decode(parts.shift());\n var val = parts.length > 0\n ? decode(parts.join('='))\n : null;\n\n if (res[key] === undefined) {\n res[key] = val;\n } else if (Array.isArray(res[key])) {\n res[key].push(val);\n } else {\n res[key] = [res[key], val];\n }\n });\n\n return res\n}\n\nfunction stringifyQuery (obj) {\n var res = obj ? Object.keys(obj).map(function (key) {\n var val = obj[key];\n\n if (val === undefined) {\n return ''\n }\n\n if (val === null) {\n return encode(key)\n }\n\n if (Array.isArray(val)) {\n var result = [];\n val.forEach(function (val2) {\n if (val2 === undefined) {\n return\n }\n if (val2 === null) {\n result.push(encode(key));\n } else {\n result.push(encode(key) + '=' + encode(val2));\n }\n });\n return result.join('&')\n }\n\n return encode(key) + '=' + encode(val)\n }).filter(function (x) { return x.length > 0; }).join('&') : null;\n return res ? (\"?\" + res) : ''\n}\n\n/* */\n\n\nvar trailingSlashRE = /\\/?$/;\n\nfunction createRoute (\n record,\n location,\n redirectedFrom,\n router\n) {\n var stringifyQuery$$1 = router && router.options.stringifyQuery;\n\n var query = location.query || {};\n try {\n query = clone(query);\n } catch (e) {}\n\n var route = {\n name: location.name || (record && record.name),\n meta: (record && record.meta) || {},\n path: location.path || '/',\n hash: location.hash || '',\n query: query,\n params: location.params || {},\n fullPath: getFullPath(location, stringifyQuery$$1),\n matched: record ? formatMatch(record) : []\n };\n if (redirectedFrom) {\n route.redirectedFrom = getFullPath(redirectedFrom, stringifyQuery$$1);\n }\n return Object.freeze(route)\n}\n\nfunction clone (value) {\n if (Array.isArray(value)) {\n return value.map(clone)\n } else if (value && typeof value === 'object') {\n var res = {};\n for (var key in value) {\n res[key] = clone(value[key]);\n }\n return res\n } else {\n return value\n }\n}\n\n// the starting route that represents the initial state\nvar START = createRoute(null, {\n path: '/'\n});\n\nfunction formatMatch (record) {\n var res = [];\n while (record) {\n res.unshift(record);\n record = record.parent;\n }\n return res\n}\n\nfunction getFullPath (\n ref,\n _stringifyQuery\n) {\n var path = ref.path;\n var query = ref.query; if ( query === void 0 ) query = {};\n var hash = ref.hash; if ( hash === void 0 ) hash = '';\n\n var stringify = _stringifyQuery || stringifyQuery;\n return (path || '/') + stringify(query) + hash\n}\n\nfunction isSameRoute (a, b) {\n if (b === START) {\n return a === b\n } else if (!b) {\n return false\n } else if (a.path && b.path) {\n return (\n a.path.replace(trailingSlashRE, '') === b.path.replace(trailingSlashRE, '') &&\n a.hash === b.hash &&\n isObjectEqual(a.query, b.query)\n )\n } else if (a.name && b.name) {\n return (\n a.name === b.name &&\n a.hash === b.hash &&\n isObjectEqual(a.query, b.query) &&\n isObjectEqual(a.params, b.params)\n )\n } else {\n return false\n }\n}\n\nfunction isObjectEqual (a, b) {\n if ( a === void 0 ) a = {};\n if ( b === void 0 ) b = {};\n\n // handle null value #1566\n if (!a || !b) { return a === b }\n var aKeys = Object.keys(a);\n var bKeys = Object.keys(b);\n if (aKeys.length !== bKeys.length) {\n return false\n }\n return aKeys.every(function (key) {\n var aVal = a[key];\n var bVal = b[key];\n // check nested equality\n if (typeof aVal === 'object' && typeof bVal === 'object') {\n return isObjectEqual(aVal, bVal)\n }\n return String(aVal) === String(bVal)\n })\n}\n\nfunction isIncludedRoute (current, target) {\n return (\n current.path.replace(trailingSlashRE, '/').indexOf(\n target.path.replace(trailingSlashRE, '/')\n ) === 0 &&\n (!target.hash || current.hash === target.hash) &&\n queryIncludes(current.query, target.query)\n )\n}\n\nfunction queryIncludes (current, target) {\n for (var key in target) {\n if (!(key in current)) {\n return false\n }\n }\n return true\n}\n\n/* */\n\n// work around weird flow bug\nvar toTypes = [String, Object];\nvar eventTypes = [String, Array];\n\nvar Link = {\n name: 'router-link',\n props: {\n to: {\n type: toTypes,\n required: true\n },\n tag: {\n type: String,\n default: 'a'\n },\n exact: Boolean,\n append: Boolean,\n replace: Boolean,\n activeClass: String,\n exactActiveClass: String,\n event: {\n type: eventTypes,\n default: 'click'\n }\n },\n render: function render (h) {\n var this$1 = this;\n\n var router = this.$router;\n var current = this.$route;\n var ref = router.resolve(this.to, current, this.append);\n var location = ref.location;\n var route = ref.route;\n var href = ref.href;\n\n var classes = {};\n var globalActiveClass = router.options.linkActiveClass;\n var globalExactActiveClass = router.options.linkExactActiveClass;\n // Support global empty active class\n var activeClassFallback = globalActiveClass == null\n ? 'router-link-active'\n : globalActiveClass;\n var exactActiveClassFallback = globalExactActiveClass == null\n ? 'router-link-exact-active'\n : globalExactActiveClass;\n var activeClass = this.activeClass == null\n ? activeClassFallback\n : this.activeClass;\n var exactActiveClass = this.exactActiveClass == null\n ? exactActiveClassFallback\n : this.exactActiveClass;\n var compareTarget = location.path\n ? createRoute(null, location, null, router)\n : route;\n\n classes[exactActiveClass] = isSameRoute(current, compareTarget);\n classes[activeClass] = this.exact\n ? classes[exactActiveClass]\n : isIncludedRoute(current, compareTarget);\n\n var handler = function (e) {\n if (guardEvent(e)) {\n if (this$1.replace) {\n router.replace(location);\n } else {\n router.push(location);\n }\n }\n };\n\n var on = { click: guardEvent };\n if (Array.isArray(this.event)) {\n this.event.forEach(function (e) { on[e] = handler; });\n } else {\n on[this.event] = handler;\n }\n\n var data = {\n class: classes\n };\n\n if (this.tag === 'a') {\n data.on = on;\n data.attrs = { href: href };\n } else {\n // find the first child and apply listener and href\n var a = findAnchor(this.$slots.default);\n if (a) {\n // in case the is a static node\n a.isStatic = false;\n var extend = _Vue.util.extend;\n var aData = a.data = extend({}, a.data);\n aData.on = on;\n var aAttrs = a.data.attrs = extend({}, a.data.attrs);\n aAttrs.href = href;\n } else {\n // doesn't have child, apply listener to self\n data.on = on;\n }\n }\n\n return h(this.tag, data, this.$slots.default)\n }\n};\n\nfunction guardEvent (e) {\n // don't redirect with control keys\n if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) { return }\n // don't redirect when preventDefault called\n if (e.defaultPrevented) { return }\n // don't redirect on right click\n if (e.button !== undefined && e.button !== 0) { return }\n // don't redirect if `target=\"_blank\"`\n if (e.currentTarget && e.currentTarget.getAttribute) {\n var target = e.currentTarget.getAttribute('target');\n if (/\\b_blank\\b/i.test(target)) { return }\n }\n // this may be a Weex event which doesn't have this method\n if (e.preventDefault) {\n e.preventDefault();\n }\n return true\n}\n\nfunction findAnchor (children) {\n if (children) {\n var child;\n for (var i = 0; i < children.length; i++) {\n child = children[i];\n if (child.tag === 'a') {\n return child\n }\n if (child.children && (child = findAnchor(child.children))) {\n return child\n }\n }\n }\n}\n\nvar _Vue;\n\nfunction install (Vue) {\n if (install.installed && _Vue === Vue) { return }\n install.installed = true;\n\n _Vue = Vue;\n\n var isDef = function (v) { return v !== undefined; };\n\n var registerInstance = function (vm, callVal) {\n var i = vm.$options._parentVnode;\n if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) {\n i(vm, callVal);\n }\n };\n\n Vue.mixin({\n beforeCreate: function beforeCreate () {\n if (isDef(this.$options.router)) {\n this._routerRoot = this;\n this._router = this.$options.router;\n this._router.init(this);\n Vue.util.defineReactive(this, '_route', this._router.history.current);\n } else {\n this._routerRoot = (this.$parent && this.$parent._routerRoot) || this;\n }\n registerInstance(this, this);\n },\n destroyed: function destroyed () {\n registerInstance(this);\n }\n });\n\n Object.defineProperty(Vue.prototype, '$router', {\n get: function get () { return this._routerRoot._router }\n });\n\n Object.defineProperty(Vue.prototype, '$route', {\n get: function get () { return this._routerRoot._route }\n });\n\n Vue.component('router-view', View);\n Vue.component('router-link', Link);\n\n var strats = Vue.config.optionMergeStrategies;\n // use the same hook merging strategy for route hooks\n strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate = strats.created;\n}\n\n/* */\n\nvar inBrowser = typeof window !== 'undefined';\n\n/* */\n\nfunction resolvePath (\n relative,\n base,\n append\n) {\n var firstChar = relative.charAt(0);\n if (firstChar === '/') {\n return relative\n }\n\n if (firstChar === '?' || firstChar === '#') {\n return base + relative\n }\n\n var stack = base.split('/');\n\n // remove trailing segment if:\n // - not appending\n // - appending to trailing slash (last segment is empty)\n if (!append || !stack[stack.length - 1]) {\n stack.pop();\n }\n\n // resolve relative path\n var segments = relative.replace(/^\\//, '').split('/');\n for (var i = 0; i < segments.length; i++) {\n var segment = segments[i];\n if (segment === '..') {\n stack.pop();\n } else if (segment !== '.') {\n stack.push(segment);\n }\n }\n\n // ensure leading slash\n if (stack[0] !== '') {\n stack.unshift('');\n }\n\n return stack.join('/')\n}\n\nfunction parsePath (path) {\n var hash = '';\n var query = '';\n\n var hashIndex = path.indexOf('#');\n if (hashIndex >= 0) {\n hash = path.slice(hashIndex);\n path = path.slice(0, hashIndex);\n }\n\n var queryIndex = path.indexOf('?');\n if (queryIndex >= 0) {\n query = path.slice(queryIndex + 1);\n path = path.slice(0, queryIndex);\n }\n\n return {\n path: path,\n query: query,\n hash: hash\n }\n}\n\nfunction cleanPath (path) {\n return path.replace(/\\/\\//g, '/')\n}\n\nvar isarray = Array.isArray || function (arr) {\n return Object.prototype.toString.call(arr) == '[object Array]';\n};\n\n/**\n * Expose `pathToRegexp`.\n */\nvar pathToRegexp_1 = pathToRegexp;\nvar parse_1 = parse;\nvar compile_1 = compile;\nvar tokensToFunction_1 = tokensToFunction;\nvar tokensToRegExp_1 = tokensToRegExp;\n\n/**\n * The main path matching regexp utility.\n *\n * @type {RegExp}\n */\nvar PATH_REGEXP = new RegExp([\n // Match escaped characters that would otherwise appear in future matches.\n // This allows the user to escape special characters that won't transform.\n '(\\\\\\\\.)',\n // Match Express-style parameters and un-named parameters with a prefix\n // and optional suffixes. Matches appear as:\n //\n // \"/:test(\\\\d+)?\" => [\"/\", \"test\", \"\\d+\", undefined, \"?\", undefined]\n // \"/route(\\\\d+)\" => [undefined, undefined, undefined, \"\\d+\", undefined, undefined]\n // \"/*\" => [\"/\", undefined, undefined, undefined, undefined, \"*\"]\n '([\\\\/.])?(?:(?:\\\\:(\\\\w+)(?:\\\\(((?:\\\\\\\\.|[^\\\\\\\\()])+)\\\\))?|\\\\(((?:\\\\\\\\.|[^\\\\\\\\()])+)\\\\))([+*?])?|(\\\\*))'\n].join('|'), 'g');\n\n/**\n * Parse a string for the raw tokens.\n *\n * @param {string} str\n * @param {Object=} options\n * @return {!Array}\n */\nfunction parse (str, options) {\n var tokens = [];\n var key = 0;\n var index = 0;\n var path = '';\n var defaultDelimiter = options && options.delimiter || '/';\n var res;\n\n while ((res = PATH_REGEXP.exec(str)) != null) {\n var m = res[0];\n var escaped = res[1];\n var offset = res.index;\n path += str.slice(index, offset);\n index = offset + m.length;\n\n // Ignore already escaped sequences.\n if (escaped) {\n path += escaped[1];\n continue\n }\n\n var next = str[index];\n var prefix = res[2];\n var name = res[3];\n var capture = res[4];\n var group = res[5];\n var modifier = res[6];\n var asterisk = res[7];\n\n // Push the current path onto the tokens.\n if (path) {\n tokens.push(path);\n path = '';\n }\n\n var partial = prefix != null && next != null && next !== prefix;\n var repeat = modifier === '+' || modifier === '*';\n var optional = modifier === '?' || modifier === '*';\n var delimiter = res[2] || defaultDelimiter;\n var pattern = capture || group;\n\n tokens.push({\n name: name || key++,\n prefix: prefix || '',\n delimiter: delimiter,\n optional: optional,\n repeat: repeat,\n partial: partial,\n asterisk: !!asterisk,\n pattern: pattern ? escapeGroup(pattern) : (asterisk ? '.*' : '[^' + escapeString(delimiter) + ']+?')\n });\n }\n\n // Match any characters still remaining.\n if (index < str.length) {\n path += str.substr(index);\n }\n\n // If the path exists, push it onto the end.\n if (path) {\n tokens.push(path);\n }\n\n return tokens\n}\n\n/**\n * Compile a string to a template function for the path.\n *\n * @param {string} str\n * @param {Object=} options\n * @return {!function(Object=, Object=)}\n */\nfunction compile (str, options) {\n return tokensToFunction(parse(str, options))\n}\n\n/**\n * Prettier encoding of URI path segments.\n *\n * @param {string}\n * @return {string}\n */\nfunction encodeURIComponentPretty (str) {\n return encodeURI(str).replace(/[\\/?#]/g, function (c) {\n return '%' + c.charCodeAt(0).toString(16).toUpperCase()\n })\n}\n\n/**\n * Encode the asterisk parameter. Similar to `pretty`, but allows slashes.\n *\n * @param {string}\n * @return {string}\n */\nfunction encodeAsterisk (str) {\n return encodeURI(str).replace(/[?#]/g, function (c) {\n return '%' + c.charCodeAt(0).toString(16).toUpperCase()\n })\n}\n\n/**\n * Expose a method for transforming tokens into the path function.\n */\nfunction tokensToFunction (tokens) {\n // Compile all the tokens into regexps.\n var matches = new Array(tokens.length);\n\n // Compile all the patterns before compilation.\n for (var i = 0; i < tokens.length; i++) {\n if (typeof tokens[i] === 'object') {\n matches[i] = new RegExp('^(?:' + tokens[i].pattern + ')$');\n }\n }\n\n return function (obj, opts) {\n var path = '';\n var data = obj || {};\n var options = opts || {};\n var encode = options.pretty ? encodeURIComponentPretty : encodeURIComponent;\n\n for (var i = 0; i < tokens.length; i++) {\n var token = tokens[i];\n\n if (typeof token === 'string') {\n path += token;\n\n continue\n }\n\n var value = data[token.name];\n var segment;\n\n if (value == null) {\n if (token.optional) {\n // Prepend partial segment prefixes.\n if (token.partial) {\n path += token.prefix;\n }\n\n continue\n } else {\n throw new TypeError('Expected \"' + token.name + '\" to be defined')\n }\n }\n\n if (isarray(value)) {\n if (!token.repeat) {\n throw new TypeError('Expected \"' + token.name + '\" to not repeat, but received `' + JSON.stringify(value) + '`')\n }\n\n if (value.length === 0) {\n if (token.optional) {\n continue\n } else {\n throw new TypeError('Expected \"' + token.name + '\" to not be empty')\n }\n }\n\n for (var j = 0; j < value.length; j++) {\n segment = encode(value[j]);\n\n if (!matches[i].test(segment)) {\n throw new TypeError('Expected all \"' + token.name + '\" to match \"' + token.pattern + '\", but received `' + JSON.stringify(segment) + '`')\n }\n\n path += (j === 0 ? token.prefix : token.delimiter) + segment;\n }\n\n continue\n }\n\n segment = token.asterisk ? encodeAsterisk(value) : encode(value);\n\n if (!matches[i].test(segment)) {\n throw new TypeError('Expected \"' + token.name + '\" to match \"' + token.pattern + '\", but received \"' + segment + '\"')\n }\n\n path += token.prefix + segment;\n }\n\n return path\n }\n}\n\n/**\n * Escape a regular expression string.\n *\n * @param {string} str\n * @return {string}\n */\nfunction escapeString (str) {\n return str.replace(/([.+*?=^!:${}()[\\]|\\/\\\\])/g, '\\\\$1')\n}\n\n/**\n * Escape the capturing group by escaping special characters and meaning.\n *\n * @param {string} group\n * @return {string}\n */\nfunction escapeGroup (group) {\n return group.replace(/([=!:$\\/()])/g, '\\\\$1')\n}\n\n/**\n * Attach the keys as a property of the regexp.\n *\n * @param {!RegExp} re\n * @param {Array} keys\n * @return {!RegExp}\n */\nfunction attachKeys (re, keys) {\n re.keys = keys;\n return re\n}\n\n/**\n * Get the flags for a regexp from the options.\n *\n * @param {Object} options\n * @return {string}\n */\nfunction flags (options) {\n return options.sensitive ? '' : 'i'\n}\n\n/**\n * Pull out keys from a regexp.\n *\n * @param {!RegExp} path\n * @param {!Array} keys\n * @return {!RegExp}\n */\nfunction regexpToRegexp (path, keys) {\n // Use a negative lookahead to match only capturing groups.\n var groups = path.source.match(/\\((?!\\?)/g);\n\n if (groups) {\n for (var i = 0; i < groups.length; i++) {\n keys.push({\n name: i,\n prefix: null,\n delimiter: null,\n optional: false,\n repeat: false,\n partial: false,\n asterisk: false,\n pattern: null\n });\n }\n }\n\n return attachKeys(path, keys)\n}\n\n/**\n * Transform an array into a regexp.\n *\n * @param {!Array} path\n * @param {Array} keys\n * @param {!Object} options\n * @return {!RegExp}\n */\nfunction arrayToRegexp (path, keys, options) {\n var parts = [];\n\n for (var i = 0; i < path.length; i++) {\n parts.push(pathToRegexp(path[i], keys, options).source);\n }\n\n var regexp = new RegExp('(?:' + parts.join('|') + ')', flags(options));\n\n return attachKeys(regexp, keys)\n}\n\n/**\n * Create a path regexp from string input.\n *\n * @param {string} path\n * @param {!Array} keys\n * @param {!Object} options\n * @return {!RegExp}\n */\nfunction stringToRegexp (path, keys, options) {\n return tokensToRegExp(parse(path, options), keys, options)\n}\n\n/**\n * Expose a function for taking tokens and returning a RegExp.\n *\n * @param {!Array} tokens\n * @param {(Array|Object)=} keys\n * @param {Object=} options\n * @return {!RegExp}\n */\nfunction tokensToRegExp (tokens, keys, options) {\n if (!isarray(keys)) {\n options = /** @type {!Object} */ (keys || options);\n keys = [];\n }\n\n options = options || {};\n\n var strict = options.strict;\n var end = options.end !== false;\n var route = '';\n\n // Iterate over the tokens and create our regexp string.\n for (var i = 0; i < tokens.length; i++) {\n var token = tokens[i];\n\n if (typeof token === 'string') {\n route += escapeString(token);\n } else {\n var prefix = escapeString(token.prefix);\n var capture = '(?:' + token.pattern + ')';\n\n keys.push(token);\n\n if (token.repeat) {\n capture += '(?:' + prefix + capture + ')*';\n }\n\n if (token.optional) {\n if (!token.partial) {\n capture = '(?:' + prefix + '(' + capture + '))?';\n } else {\n capture = prefix + '(' + capture + ')?';\n }\n } else {\n capture = prefix + '(' + capture + ')';\n }\n\n route += capture;\n }\n }\n\n var delimiter = escapeString(options.delimiter || '/');\n var endsWithDelimiter = route.slice(-delimiter.length) === delimiter;\n\n // In non-strict mode we allow a slash at the end of match. If the path to\n // match already ends with a slash, we remove it for consistency. The slash\n // is valid at the end of a path match, not in the middle. This is important\n // in non-ending mode, where \"/test/\" shouldn't match \"/test//route\".\n if (!strict) {\n route = (endsWithDelimiter ? route.slice(0, -delimiter.length) : route) + '(?:' + delimiter + '(?=$))?';\n }\n\n if (end) {\n route += '$';\n } else {\n // In non-ending mode, we need the capturing groups to match as much as\n // possible by using a positive lookahead to the end or next path segment.\n route += strict && endsWithDelimiter ? '' : '(?=' + delimiter + '|$)';\n }\n\n return attachKeys(new RegExp('^' + route, flags(options)), keys)\n}\n\n/**\n * Normalize the given path string, returning a regular expression.\n *\n * An empty array can be passed in for the keys, which will hold the\n * placeholder key descriptions. For example, using `/user/:id`, `keys` will\n * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.\n *\n * @param {(string|RegExp|Array)} path\n * @param {(Array|Object)=} keys\n * @param {Object=} options\n * @return {!RegExp}\n */\nfunction pathToRegexp (path, keys, options) {\n if (!isarray(keys)) {\n options = /** @type {!Object} */ (keys || options);\n keys = [];\n }\n\n options = options || {};\n\n if (path instanceof RegExp) {\n return regexpToRegexp(path, /** @type {!Array} */ (keys))\n }\n\n if (isarray(path)) {\n return arrayToRegexp(/** @type {!Array} */ (path), /** @type {!Array} */ (keys), options)\n }\n\n return stringToRegexp(/** @type {string} */ (path), /** @type {!Array} */ (keys), options)\n}\n\npathToRegexp_1.parse = parse_1;\npathToRegexp_1.compile = compile_1;\npathToRegexp_1.tokensToFunction = tokensToFunction_1;\npathToRegexp_1.tokensToRegExp = tokensToRegExp_1;\n\n/* */\n\n// $flow-disable-line\nvar regexpCompileCache = Object.create(null);\n\nfunction fillParams (\n path,\n params,\n routeMsg\n) {\n try {\n var filler =\n regexpCompileCache[path] ||\n (regexpCompileCache[path] = pathToRegexp_1.compile(path));\n return filler(params || {}, { pretty: true })\n } catch (e) {\n if (process.env.NODE_ENV !== 'production') {\n warn(false, (\"missing param for \" + routeMsg + \": \" + (e.message)));\n }\n return ''\n }\n}\n\n/* */\n\nfunction createRouteMap (\n routes,\n oldPathList,\n oldPathMap,\n oldNameMap\n) {\n // the path list is used to control path matching priority\n var pathList = oldPathList || [];\n // $flow-disable-line\n var pathMap = oldPathMap || Object.create(null);\n // $flow-disable-line\n var nameMap = oldNameMap || Object.create(null);\n\n routes.forEach(function (route) {\n addRouteRecord(pathList, pathMap, nameMap, route);\n });\n\n // ensure wildcard routes are always at the end\n for (var i = 0, l = pathList.length; i < l; i++) {\n if (pathList[i] === '*') {\n pathList.push(pathList.splice(i, 1)[0]);\n l--;\n i--;\n }\n }\n\n return {\n pathList: pathList,\n pathMap: pathMap,\n nameMap: nameMap\n }\n}\n\nfunction addRouteRecord (\n pathList,\n pathMap,\n nameMap,\n route,\n parent,\n matchAs\n) {\n var path = route.path;\n var name = route.name;\n if (process.env.NODE_ENV !== 'production') {\n assert(path != null, \"\\\"path\\\" is required in a route configuration.\");\n assert(\n typeof route.component !== 'string',\n \"route config \\\"component\\\" for path: \" + (String(path || name)) + \" cannot be a \" +\n \"string id. Use an actual component instead.\"\n );\n }\n\n var pathToRegexpOptions = route.pathToRegexpOptions || {};\n var normalizedPath = normalizePath(\n path,\n parent,\n pathToRegexpOptions.strict\n );\n\n if (typeof route.caseSensitive === 'boolean') {\n pathToRegexpOptions.sensitive = route.caseSensitive;\n }\n\n var record = {\n path: normalizedPath,\n regex: compileRouteRegex(normalizedPath, pathToRegexpOptions),\n components: route.components || { default: route.component },\n instances: {},\n name: name,\n parent: parent,\n matchAs: matchAs,\n redirect: route.redirect,\n beforeEnter: route.beforeEnter,\n meta: route.meta || {},\n props: route.props == null\n ? {}\n : route.components\n ? route.props\n : { default: route.props }\n };\n\n if (route.children) {\n // Warn if route is named, does not redirect and has a default child route.\n // If users navigate to this route by name, the default child will\n // not be rendered (GH Issue #629)\n if (process.env.NODE_ENV !== 'production') {\n if (route.name && !route.redirect && route.children.some(function (child) { return /^\\/?$/.test(child.path); })) {\n warn(\n false,\n \"Named Route '\" + (route.name) + \"' has a default child route. \" +\n \"When navigating to this named route (:to=\\\"{name: '\" + (route.name) + \"'\\\"), \" +\n \"the default child route will not be rendered. Remove the name from \" +\n \"this route and use the name of the default child route for named \" +\n \"links instead.\"\n );\n }\n }\n route.children.forEach(function (child) {\n var childMatchAs = matchAs\n ? cleanPath((matchAs + \"/\" + (child.path)))\n : undefined;\n addRouteRecord(pathList, pathMap, nameMap, child, record, childMatchAs);\n });\n }\n\n if (route.alias !== undefined) {\n var aliases = Array.isArray(route.alias)\n ? route.alias\n : [route.alias];\n\n aliases.forEach(function (alias) {\n var aliasRoute = {\n path: alias,\n children: route.children\n };\n addRouteRecord(\n pathList,\n pathMap,\n nameMap,\n aliasRoute,\n parent,\n record.path || '/' // matchAs\n );\n });\n }\n\n if (!pathMap[record.path]) {\n pathList.push(record.path);\n pathMap[record.path] = record;\n }\n\n if (name) {\n if (!nameMap[name]) {\n nameMap[name] = record;\n } else if (process.env.NODE_ENV !== 'production' && !matchAs) {\n warn(\n false,\n \"Duplicate named routes definition: \" +\n \"{ name: \\\"\" + name + \"\\\", path: \\\"\" + (record.path) + \"\\\" }\"\n );\n }\n }\n}\n\nfunction compileRouteRegex (path, pathToRegexpOptions) {\n var regex = pathToRegexp_1(path, [], pathToRegexpOptions);\n if (process.env.NODE_ENV !== 'production') {\n var keys = Object.create(null);\n regex.keys.forEach(function (key) {\n warn(!keys[key.name], (\"Duplicate param keys in route with path: \\\"\" + path + \"\\\"\"));\n keys[key.name] = true;\n });\n }\n return regex\n}\n\nfunction normalizePath (path, parent, strict) {\n if (!strict) { path = path.replace(/\\/$/, ''); }\n if (path[0] === '/') { return path }\n if (parent == null) { return path }\n return cleanPath(((parent.path) + \"/\" + path))\n}\n\n/* */\n\n\nfunction normalizeLocation (\n raw,\n current,\n append,\n router\n) {\n var next = typeof raw === 'string' ? { path: raw } : raw;\n // named target\n if (next.name || next._normalized) {\n return next\n }\n\n // relative params\n if (!next.path && next.params && current) {\n next = assign({}, next);\n next._normalized = true;\n var params = assign(assign({}, current.params), next.params);\n if (current.name) {\n next.name = current.name;\n next.params = params;\n } else if (current.matched.length) {\n var rawPath = current.matched[current.matched.length - 1].path;\n next.path = fillParams(rawPath, params, (\"path \" + (current.path)));\n } else if (process.env.NODE_ENV !== 'production') {\n warn(false, \"relative params navigation requires a current route.\");\n }\n return next\n }\n\n var parsedPath = parsePath(next.path || '');\n var basePath = (current && current.path) || '/';\n var path = parsedPath.path\n ? resolvePath(parsedPath.path, basePath, append || next.append)\n : basePath;\n\n var query = resolveQuery(\n parsedPath.query,\n next.query,\n router && router.options.parseQuery\n );\n\n var hash = next.hash || parsedPath.hash;\n if (hash && hash.charAt(0) !== '#') {\n hash = \"#\" + hash;\n }\n\n return {\n _normalized: true,\n path: path,\n query: query,\n hash: hash\n }\n}\n\nfunction assign (a, b) {\n for (var key in b) {\n a[key] = b[key];\n }\n return a\n}\n\n/* */\n\n\nfunction createMatcher (\n routes,\n router\n) {\n var ref = createRouteMap(routes);\n var pathList = ref.pathList;\n var pathMap = ref.pathMap;\n var nameMap = ref.nameMap;\n\n function addRoutes (routes) {\n createRouteMap(routes, pathList, pathMap, nameMap);\n }\n\n function match (\n raw,\n currentRoute,\n redirectedFrom\n ) {\n var location = normalizeLocation(raw, currentRoute, false, router);\n var name = location.name;\n\n if (name) {\n var record = nameMap[name];\n if (process.env.NODE_ENV !== 'production') {\n warn(record, (\"Route with name '\" + name + \"' does not exist\"));\n }\n if (!record) { return _createRoute(null, location) }\n var paramNames = record.regex.keys\n .filter(function (key) { return !key.optional; })\n .map(function (key) { return key.name; });\n\n if (typeof location.params !== 'object') {\n location.params = {};\n }\n\n if (currentRoute && typeof currentRoute.params === 'object') {\n for (var key in currentRoute.params) {\n if (!(key in location.params) && paramNames.indexOf(key) > -1) {\n location.params[key] = currentRoute.params[key];\n }\n }\n }\n\n if (record) {\n location.path = fillParams(record.path, location.params, (\"named route \\\"\" + name + \"\\\"\"));\n return _createRoute(record, location, redirectedFrom)\n }\n } else if (location.path) {\n location.params = {};\n for (var i = 0; i < pathList.length; i++) {\n var path = pathList[i];\n var record$1 = pathMap[path];\n if (matchRoute(record$1.regex, location.path, location.params)) {\n return _createRoute(record$1, location, redirectedFrom)\n }\n }\n }\n // no match\n return _createRoute(null, location)\n }\n\n function redirect (\n record,\n location\n ) {\n var originalRedirect = record.redirect;\n var redirect = typeof originalRedirect === 'function'\n ? originalRedirect(createRoute(record, location, null, router))\n : originalRedirect;\n\n if (typeof redirect === 'string') {\n redirect = { path: redirect };\n }\n\n if (!redirect || typeof redirect !== 'object') {\n if (process.env.NODE_ENV !== 'production') {\n warn(\n false, (\"invalid redirect option: \" + (JSON.stringify(redirect)))\n );\n }\n return _createRoute(null, location)\n }\n\n var re = redirect;\n var name = re.name;\n var path = re.path;\n var query = location.query;\n var hash = location.hash;\n var params = location.params;\n query = re.hasOwnProperty('query') ? re.query : query;\n hash = re.hasOwnProperty('hash') ? re.hash : hash;\n params = re.hasOwnProperty('params') ? re.params : params;\n\n if (name) {\n // resolved named direct\n var targetRecord = nameMap[name];\n if (process.env.NODE_ENV !== 'production') {\n assert(targetRecord, (\"redirect failed: named route \\\"\" + name + \"\\\" not found.\"));\n }\n return match({\n _normalized: true,\n name: name,\n query: query,\n hash: hash,\n params: params\n }, undefined, location)\n } else if (path) {\n // 1. resolve relative redirect\n var rawPath = resolveRecordPath(path, record);\n // 2. resolve params\n var resolvedPath = fillParams(rawPath, params, (\"redirect route with path \\\"\" + rawPath + \"\\\"\"));\n // 3. rematch with existing query and hash\n return match({\n _normalized: true,\n path: resolvedPath,\n query: query,\n hash: hash\n }, undefined, location)\n } else {\n if (process.env.NODE_ENV !== 'production') {\n warn(false, (\"invalid redirect option: \" + (JSON.stringify(redirect))));\n }\n return _createRoute(null, location)\n }\n }\n\n function alias (\n record,\n location,\n matchAs\n ) {\n var aliasedPath = fillParams(matchAs, location.params, (\"aliased route with path \\\"\" + matchAs + \"\\\"\"));\n var aliasedMatch = match({\n _normalized: true,\n path: aliasedPath\n });\n if (aliasedMatch) {\n var matched = aliasedMatch.matched;\n var aliasedRecord = matched[matched.length - 1];\n location.params = aliasedMatch.params;\n return _createRoute(aliasedRecord, location)\n }\n return _createRoute(null, location)\n }\n\n function _createRoute (\n record,\n location,\n redirectedFrom\n ) {\n if (record && record.redirect) {\n return redirect(record, redirectedFrom || location)\n }\n if (record && record.matchAs) {\n return alias(record, location, record.matchAs)\n }\n return createRoute(record, location, redirectedFrom, router)\n }\n\n return {\n match: match,\n addRoutes: addRoutes\n }\n}\n\nfunction matchRoute (\n regex,\n path,\n params\n) {\n var m = path.match(regex);\n\n if (!m) {\n return false\n } else if (!params) {\n return true\n }\n\n for (var i = 1, len = m.length; i < len; ++i) {\n var key = regex.keys[i - 1];\n var val = typeof m[i] === 'string' ? decodeURIComponent(m[i]) : m[i];\n if (key) {\n params[key.name] = val;\n }\n }\n\n return true\n}\n\nfunction resolveRecordPath (path, record) {\n return resolvePath(path, record.parent ? record.parent.path : '/', true)\n}\n\n/* */\n\n\nvar positionStore = Object.create(null);\n\nfunction setupScroll () {\n // Fix for #1585 for Firefox\n window.history.replaceState({ key: getStateKey() }, '');\n window.addEventListener('popstate', function (e) {\n saveScrollPosition();\n if (e.state && e.state.key) {\n setStateKey(e.state.key);\n }\n });\n}\n\nfunction handleScroll (\n router,\n to,\n from,\n isPop\n) {\n if (!router.app) {\n return\n }\n\n var behavior = router.options.scrollBehavior;\n if (!behavior) {\n return\n }\n\n if (process.env.NODE_ENV !== 'production') {\n assert(typeof behavior === 'function', \"scrollBehavior must be a function\");\n }\n\n // wait until re-render finishes before scrolling\n router.app.$nextTick(function () {\n var position = getScrollPosition();\n var shouldScroll = behavior(to, from, isPop ? position : null);\n\n if (!shouldScroll) {\n return\n }\n\n if (typeof shouldScroll.then === 'function') {\n shouldScroll.then(function (shouldScroll) {\n scrollToPosition((shouldScroll), position);\n }).catch(function (err) {\n if (process.env.NODE_ENV !== 'production') {\n assert(false, err.toString());\n }\n });\n } else {\n scrollToPosition(shouldScroll, position);\n }\n });\n}\n\nfunction saveScrollPosition () {\n var key = getStateKey();\n if (key) {\n positionStore[key] = {\n x: window.pageXOffset,\n y: window.pageYOffset\n };\n }\n}\n\nfunction getScrollPosition () {\n var key = getStateKey();\n if (key) {\n return positionStore[key]\n }\n}\n\nfunction getElementPosition (el, offset) {\n var docEl = document.documentElement;\n var docRect = docEl.getBoundingClientRect();\n var elRect = el.getBoundingClientRect();\n return {\n x: elRect.left - docRect.left - offset.x,\n y: elRect.top - docRect.top - offset.y\n }\n}\n\nfunction isValidPosition (obj) {\n return isNumber(obj.x) || isNumber(obj.y)\n}\n\nfunction normalizePosition (obj) {\n return {\n x: isNumber(obj.x) ? obj.x : window.pageXOffset,\n y: isNumber(obj.y) ? obj.y : window.pageYOffset\n }\n}\n\nfunction normalizeOffset (obj) {\n return {\n x: isNumber(obj.x) ? obj.x : 0,\n y: isNumber(obj.y) ? obj.y : 0\n }\n}\n\nfunction isNumber (v) {\n return typeof v === 'number'\n}\n\nfunction scrollToPosition (shouldScroll, position) {\n var isObject = typeof shouldScroll === 'object';\n if (isObject && typeof shouldScroll.selector === 'string') {\n var el = document.querySelector(shouldScroll.selector);\n if (el) {\n var offset = shouldScroll.offset && typeof shouldScroll.offset === 'object' ? shouldScroll.offset : {};\n offset = normalizeOffset(offset);\n position = getElementPosition(el, offset);\n } else if (isValidPosition(shouldScroll)) {\n position = normalizePosition(shouldScroll);\n }\n } else if (isObject && isValidPosition(shouldScroll)) {\n position = normalizePosition(shouldScroll);\n }\n\n if (position) {\n window.scrollTo(position.x, position.y);\n }\n}\n\n/* */\n\nvar supportsPushState = inBrowser && (function () {\n var ua = window.navigator.userAgent;\n\n if (\n (ua.indexOf('Android 2.') !== -1 || ua.indexOf('Android 4.0') !== -1) &&\n ua.indexOf('Mobile Safari') !== -1 &&\n ua.indexOf('Chrome') === -1 &&\n ua.indexOf('Windows Phone') === -1\n ) {\n return false\n }\n\n return window.history && 'pushState' in window.history\n})();\n\n// use User Timing api (if present) for more accurate key precision\nvar Time = inBrowser && window.performance && window.performance.now\n ? window.performance\n : Date;\n\nvar _key = genKey();\n\nfunction genKey () {\n return Time.now().toFixed(3)\n}\n\nfunction getStateKey () {\n return _key\n}\n\nfunction setStateKey (key) {\n _key = key;\n}\n\nfunction pushState (url, replace) {\n saveScrollPosition();\n // try...catch the pushState call to get around Safari\n // DOM Exception 18 where it limits to 100 pushState calls\n var history = window.history;\n try {\n if (replace) {\n history.replaceState({ key: _key }, '', url);\n } else {\n _key = genKey();\n history.pushState({ key: _key }, '', url);\n }\n } catch (e) {\n window.location[replace ? 'replace' : 'assign'](url);\n }\n}\n\nfunction replaceState (url) {\n pushState(url, true);\n}\n\n/* */\n\nfunction runQueue (queue, fn, cb) {\n var step = function (index) {\n if (index >= queue.length) {\n cb();\n } else {\n if (queue[index]) {\n fn(queue[index], function () {\n step(index + 1);\n });\n } else {\n step(index + 1);\n }\n }\n };\n step(0);\n}\n\n/* */\n\nfunction resolveAsyncComponents (matched) {\n return function (to, from, next) {\n var hasAsync = false;\n var pending = 0;\n var error = null;\n\n flatMapComponents(matched, function (def, _, match, key) {\n // if it's a function and doesn't have cid attached,\n // assume it's an async component resolve function.\n // we are not using Vue's default async resolving mechanism because\n // we want to halt the navigation until the incoming component has been\n // resolved.\n if (typeof def === 'function' && def.cid === undefined) {\n hasAsync = true;\n pending++;\n\n var resolve = once(function (resolvedDef) {\n if (isESModule(resolvedDef)) {\n resolvedDef = resolvedDef.default;\n }\n // save resolved on async factory in case it's used elsewhere\n def.resolved = typeof resolvedDef === 'function'\n ? resolvedDef\n : _Vue.extend(resolvedDef);\n match.components[key] = resolvedDef;\n pending--;\n if (pending <= 0) {\n next();\n }\n });\n\n var reject = once(function (reason) {\n var msg = \"Failed to resolve async component \" + key + \": \" + reason;\n process.env.NODE_ENV !== 'production' && warn(false, msg);\n if (!error) {\n error = isError(reason)\n ? reason\n : new Error(msg);\n next(error);\n }\n });\n\n var res;\n try {\n res = def(resolve, reject);\n } catch (e) {\n reject(e);\n }\n if (res) {\n if (typeof res.then === 'function') {\n res.then(resolve, reject);\n } else {\n // new syntax in Vue 2.3\n var comp = res.component;\n if (comp && typeof comp.then === 'function') {\n comp.then(resolve, reject);\n }\n }\n }\n }\n });\n\n if (!hasAsync) { next(); }\n }\n}\n\nfunction flatMapComponents (\n matched,\n fn\n) {\n return flatten(matched.map(function (m) {\n return Object.keys(m.components).map(function (key) { return fn(\n m.components[key],\n m.instances[key],\n m, key\n ); })\n }))\n}\n\nfunction flatten (arr) {\n return Array.prototype.concat.apply([], arr)\n}\n\nvar hasSymbol =\n typeof Symbol === 'function' &&\n typeof Symbol.toStringTag === 'symbol';\n\nfunction isESModule (obj) {\n return obj.__esModule || (hasSymbol && obj[Symbol.toStringTag] === 'Module')\n}\n\n// in Webpack 2, require.ensure now also returns a Promise\n// so the resolve/reject functions may get called an extra time\n// if the user uses an arrow function shorthand that happens to\n// return that Promise.\nfunction once (fn) {\n var called = false;\n return function () {\n var args = [], len = arguments.length;\n while ( len-- ) args[ len ] = arguments[ len ];\n\n if (called) { return }\n called = true;\n return fn.apply(this, args)\n }\n}\n\n/* */\n\nvar History = function History (router, base) {\n this.router = router;\n this.base = normalizeBase(base);\n // start with a route object that stands for \"nowhere\"\n this.current = START;\n this.pending = null;\n this.ready = false;\n this.readyCbs = [];\n this.readyErrorCbs = [];\n this.errorCbs = [];\n};\n\nHistory.prototype.listen = function listen (cb) {\n this.cb = cb;\n};\n\nHistory.prototype.onReady = function onReady (cb, errorCb) {\n if (this.ready) {\n cb();\n } else {\n this.readyCbs.push(cb);\n if (errorCb) {\n this.readyErrorCbs.push(errorCb);\n }\n }\n};\n\nHistory.prototype.onError = function onError (errorCb) {\n this.errorCbs.push(errorCb);\n};\n\nHistory.prototype.transitionTo = function transitionTo (location, onComplete, onAbort) {\n var this$1 = this;\n\n var route = this.router.match(location, this.current);\n this.confirmTransition(route, function () {\n this$1.updateRoute(route);\n onComplete && onComplete(route);\n this$1.ensureURL();\n\n // fire ready cbs once\n if (!this$1.ready) {\n this$1.ready = true;\n this$1.readyCbs.forEach(function (cb) { cb(route); });\n }\n }, function (err) {\n if (onAbort) {\n onAbort(err);\n }\n if (err && !this$1.ready) {\n this$1.ready = true;\n this$1.readyErrorCbs.forEach(function (cb) { cb(err); });\n }\n });\n};\n\nHistory.prototype.confirmTransition = function confirmTransition (route, onComplete, onAbort) {\n var this$1 = this;\n\n var current = this.current;\n var abort = function (err) {\n if (isError(err)) {\n if (this$1.errorCbs.length) {\n this$1.errorCbs.forEach(function (cb) { cb(err); });\n } else {\n warn(false, 'uncaught error during route navigation:');\n console.error(err);\n }\n }\n onAbort && onAbort(err);\n };\n if (\n isSameRoute(route, current) &&\n // in the case the route map has been dynamically appended to\n route.matched.length === current.matched.length\n ) {\n this.ensureURL();\n return abort()\n }\n\n var ref = resolveQueue(this.current.matched, route.matched);\n var updated = ref.updated;\n var deactivated = ref.deactivated;\n var activated = ref.activated;\n\n var queue = [].concat(\n // in-component leave guards\n extractLeaveGuards(deactivated),\n // global before hooks\n this.router.beforeHooks,\n // in-component update hooks\n extractUpdateHooks(updated),\n // in-config enter guards\n activated.map(function (m) { return m.beforeEnter; }),\n // async components\n resolveAsyncComponents(activated)\n );\n\n this.pending = route;\n var iterator = function (hook, next) {\n if (this$1.pending !== route) {\n return abort()\n }\n try {\n hook(route, current, function (to) {\n if (to === false || isError(to)) {\n // next(false) -> abort navigation, ensure current URL\n this$1.ensureURL(true);\n abort(to);\n } else if (\n typeof to === 'string' ||\n (typeof to === 'object' && (\n typeof to.path === 'string' ||\n typeof to.name === 'string'\n ))\n ) {\n // next('/') or next({ path: '/' }) -> redirect\n abort();\n if (typeof to === 'object' && to.replace) {\n this$1.replace(to);\n } else {\n this$1.push(to);\n }\n } else {\n // confirm transition and pass on the value\n next(to);\n }\n });\n } catch (e) {\n abort(e);\n }\n };\n\n runQueue(queue, iterator, function () {\n var postEnterCbs = [];\n var isValid = function () { return this$1.current === route; };\n // wait until async components are resolved before\n // extracting in-component enter guards\n var enterGuards = extractEnterGuards(activated, postEnterCbs, isValid);\n var queue = enterGuards.concat(this$1.router.resolveHooks);\n runQueue(queue, iterator, function () {\n if (this$1.pending !== route) {\n return abort()\n }\n this$1.pending = null;\n onComplete(route);\n if (this$1.router.app) {\n this$1.router.app.$nextTick(function () {\n postEnterCbs.forEach(function (cb) { cb(); });\n });\n }\n });\n });\n};\n\nHistory.prototype.updateRoute = function updateRoute (route) {\n var prev = this.current;\n this.current = route;\n this.cb && this.cb(route);\n this.router.afterHooks.forEach(function (hook) {\n hook && hook(route, prev);\n });\n};\n\nfunction normalizeBase (base) {\n if (!base) {\n if (inBrowser) {\n // respect tag\n var baseEl = document.querySelector('base');\n base = (baseEl && baseEl.getAttribute('href')) || '/';\n // strip full URL origin\n base = base.replace(/^https?:\\/\\/[^\\/]+/, '');\n } else {\n base = '/';\n }\n }\n // make sure there's the starting slash\n if (base.charAt(0) !== '/') {\n base = '/' + base;\n }\n // remove trailing slash\n return base.replace(/\\/$/, '')\n}\n\nfunction resolveQueue (\n current,\n next\n) {\n var i;\n var max = Math.max(current.length, next.length);\n for (i = 0; i < max; i++) {\n if (current[i] !== next[i]) {\n break\n }\n }\n return {\n updated: next.slice(0, i),\n activated: next.slice(i),\n deactivated: current.slice(i)\n }\n}\n\nfunction extractGuards (\n records,\n name,\n bind,\n reverse\n) {\n var guards = flatMapComponents(records, function (def, instance, match, key) {\n var guard = extractGuard(def, name);\n if (guard) {\n return Array.isArray(guard)\n ? guard.map(function (guard) { return bind(guard, instance, match, key); })\n : bind(guard, instance, match, key)\n }\n });\n return flatten(reverse ? guards.reverse() : guards)\n}\n\nfunction extractGuard (\n def,\n key\n) {\n if (typeof def !== 'function') {\n // extend now so that global mixins are applied.\n def = _Vue.extend(def);\n }\n return def.options[key]\n}\n\nfunction extractLeaveGuards (deactivated) {\n return extractGuards(deactivated, 'beforeRouteLeave', bindGuard, true)\n}\n\nfunction extractUpdateHooks (updated) {\n return extractGuards(updated, 'beforeRouteUpdate', bindGuard)\n}\n\nfunction bindGuard (guard, instance) {\n if (instance) {\n return function boundRouteGuard () {\n return guard.apply(instance, arguments)\n }\n }\n}\n\nfunction extractEnterGuards (\n activated,\n cbs,\n isValid\n) {\n return extractGuards(activated, 'beforeRouteEnter', function (guard, _, match, key) {\n return bindEnterGuard(guard, match, key, cbs, isValid)\n })\n}\n\nfunction bindEnterGuard (\n guard,\n match,\n key,\n cbs,\n isValid\n) {\n return function routeEnterGuard (to, from, next) {\n return guard(to, from, function (cb) {\n next(cb);\n if (typeof cb === 'function') {\n cbs.push(function () {\n // #750\n // if a router-view is wrapped with an out-in transition,\n // the instance may not have been registered at this time.\n // we will need to poll for registration until current route\n // is no longer valid.\n poll(cb, match.instances, key, isValid);\n });\n }\n })\n }\n}\n\nfunction poll (\n cb, // somehow flow cannot infer this is a function\n instances,\n key,\n isValid\n) {\n if (instances[key]) {\n cb(instances[key]);\n } else if (isValid()) {\n setTimeout(function () {\n poll(cb, instances, key, isValid);\n }, 16);\n }\n}\n\n/* */\n\n\nvar HTML5History = (function (History$$1) {\n function HTML5History (router, base) {\n var this$1 = this;\n\n History$$1.call(this, router, base);\n\n var expectScroll = router.options.scrollBehavior;\n\n if (expectScroll) {\n setupScroll();\n }\n\n var initLocation = getLocation(this.base);\n window.addEventListener('popstate', function (e) {\n var current = this$1.current;\n\n // Avoiding first `popstate` event dispatched in some browsers but first\n // history route not updated since async guard at the same time.\n var location = getLocation(this$1.base);\n if (this$1.current === START && location === initLocation) {\n return\n }\n\n this$1.transitionTo(location, function (route) {\n if (expectScroll) {\n handleScroll(router, route, current, true);\n }\n });\n });\n }\n\n if ( History$$1 ) HTML5History.__proto__ = History$$1;\n HTML5History.prototype = Object.create( History$$1 && History$$1.prototype );\n HTML5History.prototype.constructor = HTML5History;\n\n HTML5History.prototype.go = function go (n) {\n window.history.go(n);\n };\n\n HTML5History.prototype.push = function push (location, onComplete, onAbort) {\n var this$1 = this;\n\n var ref = this;\n var fromRoute = ref.current;\n this.transitionTo(location, function (route) {\n pushState(cleanPath(this$1.base + route.fullPath));\n handleScroll(this$1.router, route, fromRoute, false);\n onComplete && onComplete(route);\n }, onAbort);\n };\n\n HTML5History.prototype.replace = function replace (location, onComplete, onAbort) {\n var this$1 = this;\n\n var ref = this;\n var fromRoute = ref.current;\n this.transitionTo(location, function (route) {\n replaceState(cleanPath(this$1.base + route.fullPath));\n handleScroll(this$1.router, route, fromRoute, false);\n onComplete && onComplete(route);\n }, onAbort);\n };\n\n HTML5History.prototype.ensureURL = function ensureURL (push) {\n if (getLocation(this.base) !== this.current.fullPath) {\n var current = cleanPath(this.base + this.current.fullPath);\n push ? pushState(current) : replaceState(current);\n }\n };\n\n HTML5History.prototype.getCurrentLocation = function getCurrentLocation () {\n return getLocation(this.base)\n };\n\n return HTML5History;\n}(History));\n\nfunction getLocation (base) {\n var path = window.location.pathname;\n if (base && path.indexOf(base) === 0) {\n path = path.slice(base.length);\n }\n return (path || '/') + window.location.search + window.location.hash\n}\n\n/* */\n\n\nvar HashHistory = (function (History$$1) {\n function HashHistory (router, base, fallback) {\n History$$1.call(this, router, base);\n // check history fallback deeplinking\n if (fallback && checkFallback(this.base)) {\n return\n }\n ensureSlash();\n }\n\n if ( History$$1 ) HashHistory.__proto__ = History$$1;\n HashHistory.prototype = Object.create( History$$1 && History$$1.prototype );\n HashHistory.prototype.constructor = HashHistory;\n\n // this is delayed until the app mounts\n // to avoid the hashchange listener being fired too early\n HashHistory.prototype.setupListeners = function setupListeners () {\n var this$1 = this;\n\n var router = this.router;\n var expectScroll = router.options.scrollBehavior;\n var supportsScroll = supportsPushState && expectScroll;\n\n if (supportsScroll) {\n setupScroll();\n }\n\n window.addEventListener(supportsPushState ? 'popstate' : 'hashchange', function () {\n var current = this$1.current;\n if (!ensureSlash()) {\n return\n }\n this$1.transitionTo(getHash(), function (route) {\n if (supportsScroll) {\n handleScroll(this$1.router, route, current, true);\n }\n if (!supportsPushState) {\n replaceHash(route.fullPath);\n }\n });\n });\n };\n\n HashHistory.prototype.push = function push (location, onComplete, onAbort) {\n var this$1 = this;\n\n var ref = this;\n var fromRoute = ref.current;\n this.transitionTo(location, function (route) {\n pushHash(route.fullPath);\n handleScroll(this$1.router, route, fromRoute, false);\n onComplete && onComplete(route);\n }, onAbort);\n };\n\n HashHistory.prototype.replace = function replace (location, onComplete, onAbort) {\n var this$1 = this;\n\n var ref = this;\n var fromRoute = ref.current;\n this.transitionTo(location, function (route) {\n replaceHash(route.fullPath);\n handleScroll(this$1.router, route, fromRoute, false);\n onComplete && onComplete(route);\n }, onAbort);\n };\n\n HashHistory.prototype.go = function go (n) {\n window.history.go(n);\n };\n\n HashHistory.prototype.ensureURL = function ensureURL (push) {\n var current = this.current.fullPath;\n if (getHash() !== current) {\n push ? pushHash(current) : replaceHash(current);\n }\n };\n\n HashHistory.prototype.getCurrentLocation = function getCurrentLocation () {\n return getHash()\n };\n\n return HashHistory;\n}(History));\n\nfunction checkFallback (base) {\n var location = getLocation(base);\n if (!/^\\/#/.test(location)) {\n window.location.replace(\n cleanPath(base + '/#' + location)\n );\n return true\n }\n}\n\nfunction ensureSlash () {\n var path = getHash();\n if (path.charAt(0) === '/') {\n return true\n }\n replaceHash('/' + path);\n return false\n}\n\nfunction getHash () {\n // We can't use window.location.hash here because it's not\n // consistent across browsers - Firefox will pre-decode it!\n var href = window.location.href;\n var index = href.indexOf('#');\n return index === -1 ? '' : href.slice(index + 1)\n}\n\nfunction getUrl (path) {\n var href = window.location.href;\n var i = href.indexOf('#');\n var base = i >= 0 ? href.slice(0, i) : href;\n return (base + \"#\" + path)\n}\n\nfunction pushHash (path) {\n if (supportsPushState) {\n pushState(getUrl(path));\n } else {\n window.location.hash = path;\n }\n}\n\nfunction replaceHash (path) {\n if (supportsPushState) {\n replaceState(getUrl(path));\n } else {\n window.location.replace(getUrl(path));\n }\n}\n\n/* */\n\n\nvar AbstractHistory = (function (History$$1) {\n function AbstractHistory (router, base) {\n History$$1.call(this, router, base);\n this.stack = [];\n this.index = -1;\n }\n\n if ( History$$1 ) AbstractHistory.__proto__ = History$$1;\n AbstractHistory.prototype = Object.create( History$$1 && History$$1.prototype );\n AbstractHistory.prototype.constructor = AbstractHistory;\n\n AbstractHistory.prototype.push = function push (location, onComplete, onAbort) {\n var this$1 = this;\n\n this.transitionTo(location, function (route) {\n this$1.stack = this$1.stack.slice(0, this$1.index + 1).concat(route);\n this$1.index++;\n onComplete && onComplete(route);\n }, onAbort);\n };\n\n AbstractHistory.prototype.replace = function replace (location, onComplete, onAbort) {\n var this$1 = this;\n\n this.transitionTo(location, function (route) {\n this$1.stack = this$1.stack.slice(0, this$1.index).concat(route);\n onComplete && onComplete(route);\n }, onAbort);\n };\n\n AbstractHistory.prototype.go = function go (n) {\n var this$1 = this;\n\n var targetIndex = this.index + n;\n if (targetIndex < 0 || targetIndex >= this.stack.length) {\n return\n }\n var route = this.stack[targetIndex];\n this.confirmTransition(route, function () {\n this$1.index = targetIndex;\n this$1.updateRoute(route);\n });\n };\n\n AbstractHistory.prototype.getCurrentLocation = function getCurrentLocation () {\n var current = this.stack[this.stack.length - 1];\n return current ? current.fullPath : '/'\n };\n\n AbstractHistory.prototype.ensureURL = function ensureURL () {\n // noop\n };\n\n return AbstractHistory;\n}(History));\n\n/* */\n\nvar VueRouter = function VueRouter (options) {\n if ( options === void 0 ) options = {};\n\n this.app = null;\n this.apps = [];\n this.options = options;\n this.beforeHooks = [];\n this.resolveHooks = [];\n this.afterHooks = [];\n this.matcher = createMatcher(options.routes || [], this);\n\n var mode = options.mode || 'hash';\n this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false;\n if (this.fallback) {\n mode = 'hash';\n }\n if (!inBrowser) {\n mode = 'abstract';\n }\n this.mode = mode;\n\n switch (mode) {\n case 'history':\n this.history = new HTML5History(this, options.base);\n break\n case 'hash':\n this.history = new HashHistory(this, options.base, this.fallback);\n break\n case 'abstract':\n this.history = new AbstractHistory(this, options.base);\n break\n default:\n if (process.env.NODE_ENV !== 'production') {\n assert(false, (\"invalid mode: \" + mode));\n }\n }\n};\n\nvar prototypeAccessors = { currentRoute: { configurable: true } };\n\nVueRouter.prototype.match = function match (\n raw,\n current,\n redirectedFrom\n) {\n return this.matcher.match(raw, current, redirectedFrom)\n};\n\nprototypeAccessors.currentRoute.get = function () {\n return this.history && this.history.current\n};\n\nVueRouter.prototype.init = function init (app /* Vue component instance */) {\n var this$1 = this;\n\n process.env.NODE_ENV !== 'production' && assert(\n install.installed,\n \"not installed. Make sure to call `Vue.use(VueRouter)` \" +\n \"before creating root instance.\"\n );\n\n this.apps.push(app);\n\n // main app already initialized.\n if (this.app) {\n return\n }\n\n this.app = app;\n\n var history = this.history;\n\n if (history instanceof HTML5History) {\n history.transitionTo(history.getCurrentLocation());\n } else if (history instanceof HashHistory) {\n var setupHashListener = function () {\n history.setupListeners();\n };\n history.transitionTo(\n history.getCurrentLocation(),\n setupHashListener,\n setupHashListener\n );\n }\n\n history.listen(function (route) {\n this$1.apps.forEach(function (app) {\n app._route = route;\n });\n });\n};\n\nVueRouter.prototype.beforeEach = function beforeEach (fn) {\n return registerHook(this.beforeHooks, fn)\n};\n\nVueRouter.prototype.beforeResolve = function beforeResolve (fn) {\n return registerHook(this.resolveHooks, fn)\n};\n\nVueRouter.prototype.afterEach = function afterEach (fn) {\n return registerHook(this.afterHooks, fn)\n};\n\nVueRouter.prototype.onReady = function onReady (cb, errorCb) {\n this.history.onReady(cb, errorCb);\n};\n\nVueRouter.prototype.onError = function onError (errorCb) {\n this.history.onError(errorCb);\n};\n\nVueRouter.prototype.push = function push (location, onComplete, onAbort) {\n this.history.push(location, onComplete, onAbort);\n};\n\nVueRouter.prototype.replace = function replace (location, onComplete, onAbort) {\n this.history.replace(location, onComplete, onAbort);\n};\n\nVueRouter.prototype.go = function go (n) {\n this.history.go(n);\n};\n\nVueRouter.prototype.back = function back () {\n this.go(-1);\n};\n\nVueRouter.prototype.forward = function forward () {\n this.go(1);\n};\n\nVueRouter.prototype.getMatchedComponents = function getMatchedComponents (to) {\n var route = to\n ? to.matched\n ? to\n : this.resolve(to).route\n : this.currentRoute;\n if (!route) {\n return []\n }\n return [].concat.apply([], route.matched.map(function (m) {\n return Object.keys(m.components).map(function (key) {\n return m.components[key]\n })\n }))\n};\n\nVueRouter.prototype.resolve = function resolve (\n to,\n current,\n append\n) {\n var location = normalizeLocation(\n to,\n current || this.history.current,\n append,\n this\n );\n var route = this.match(location, current);\n var fullPath = route.redirectedFrom || route.fullPath;\n var base = this.history.base;\n var href = createHref(base, fullPath, this.mode);\n return {\n location: location,\n route: route,\n href: href,\n // for backwards compat\n normalizedTo: location,\n resolved: route\n }\n};\n\nVueRouter.prototype.addRoutes = function addRoutes (routes) {\n this.matcher.addRoutes(routes);\n if (this.history.current !== START) {\n this.history.transitionTo(this.history.getCurrentLocation());\n }\n};\n\nObject.defineProperties( VueRouter.prototype, prototypeAccessors );\n\nfunction registerHook (list, fn) {\n list.push(fn);\n return function () {\n var i = list.indexOf(fn);\n if (i > -1) { list.splice(i, 1); }\n }\n}\n\nfunction createHref (base, fullPath, mode) {\n var path = mode === 'hash' ? '#' + fullPath : fullPath;\n return base ? cleanPath(base + '/' + path) : path\n}\n\nVueRouter.install = install;\nVueRouter.version = '3.0.1';\n\nif (inBrowser && window.Vue) {\n window.Vue.use(VueRouter);\n}\n\nexport default VueRouter;\n","var render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"li\", [\n _vm.item.href\n ? _c(\n \"a\",\n {\n attrs: { href: _vm.item.href ? _vm.item.href : \"#\" },\n on: { click: _vm.item.action }\n },\n [\n _c(\"span\", { class: _vm.item.icon }),\n _vm._v(\" \"),\n _vm.item.text\n ? _c(\"span\", [_vm._v(_vm._s(_vm.item.text))])\n : _vm.item.longtext\n ? _c(\"p\", [_vm._v(_vm._s(_vm.item.longtext))])\n : _vm._e()\n ]\n )\n : _vm.item.action\n ? _c(\"button\", { on: { click: _vm.item.action } }, [\n _c(\"span\", { class: _vm.item.icon }),\n _vm._v(\" \"),\n _vm.item.text\n ? _c(\"span\", [_vm._v(_vm._s(_vm.item.text))])\n : _vm.item.longtext\n ? _c(\"p\", [_vm._v(_vm._s(_vm.item.longtext))])\n : _vm._e()\n ])\n : _c(\"span\", [\n _c(\"span\", { class: _vm.item.icon }),\n _vm._v(\" \"),\n _vm.item.text\n ? _c(\"span\", [_vm._v(_vm._s(_vm.item.text))])\n : _vm.item.longtext\n ? _c(\"p\", [_vm._v(_vm._s(_vm.item.longtext))])\n : _vm._e()\n ])\n ])\n}\nvar staticRenderFns = []\nrender._withStripped = true\nexport { render, staticRenderFns }\nif (module.hot) {\n module.hot.accept()\n if (module.hot.data) {\n require(\"vue-hot-reload-api\") .rerender(\"data-v-4c6af9e6\", { render: render, staticRenderFns: staticRenderFns })\n }\n}","var disposed = false\n/* script */\nexport * from \"!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./popoverItem.vue\"\nimport __vue_script__ from \"!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./popoverItem.vue\"\n/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-4c6af9e6\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./popoverItem.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = null\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\nComponent.options.__file = \"src/components/popoverMenu/popoverItem.vue\"\n\n/* hot reload */\nif (module.hot) {(function () {\n var hotAPI = require(\"vue-hot-reload-api\")\n hotAPI.install(require(\"vue\"), false)\n if (!hotAPI.compatible) return\n module.hot.accept()\n if (!module.hot.data) {\n hotAPI.createRecord(\"data-v-4c6af9e6\", Component.options)\n } else {\n hotAPI.reload(\"data-v-4c6af9e6\", Component.options)\n }\n module.hot.dispose(function (data) {\n disposed = true\n })\n})()}\n\nexport default Component.exports\n","\n\n\n","\n\n\n\n","var render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"ul\",\n _vm._l(_vm.menu, function(item, key) {\n return _c(\"popover-item\", { key: key, attrs: { item: item } })\n })\n )\n}\nvar staticRenderFns = []\nrender._withStripped = true\nexport { render, staticRenderFns }\nif (module.hot) {\n module.hot.accept()\n if (module.hot.data) {\n require(\"vue-hot-reload-api\") .rerender(\"data-v-04ea21c4\", { render: render, staticRenderFns: staticRenderFns })\n }\n}","var disposed = false\n/* script */\nexport * from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./popoverMenu.vue\"\nimport __vue_script__ from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./popoverMenu.vue\"\n/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-04ea21c4\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../node_modules/vue-loader/lib/selector?type=template&index=0!./popoverMenu.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = null\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\nComponent.options.__file = \"src/components/popoverMenu.vue\"\n\n/* hot reload */\nif (module.hot) {(function () {\n var hotAPI = require(\"vue-hot-reload-api\")\n hotAPI.install(require(\"vue\"), false)\n if (!hotAPI.compatible) return\n module.hot.accept()\n if (!module.hot.data) {\n hotAPI.createRecord(\"data-v-04ea21c4\", Component.options)\n } else {\n hotAPI.reload(\"data-v-04ea21c4\", Component.options)\n }\n module.hot.dispose(function (data) {\n disposed = true\n })\n})()}\n\nexport default Component.exports\n","\n\n\n","var render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"li\",\n {\n class: [\n {\n \"icon-loading-small\": _vm.item.loading,\n open: _vm.item.opened,\n collapsible:\n _vm.item.collapsible &&\n _vm.item.children &&\n _vm.item.children.length > 0\n },\n _vm.item.classes\n ],\n attrs: { id: _vm.item.id }\n },\n [\n _vm.item.bullet\n ? _c(\"div\", {\n staticClass: \"app-navigation-entry-bullet\",\n style: { backgroundColor: _vm.item.bullet }\n })\n : _vm._e(),\n _vm._v(\" \"),\n _vm.item.href\n ? _c(\n \"a\",\n {\n class: _vm.item.icon,\n attrs: { href: _vm.item.href ? _vm.item.href : \"#\" },\n on: { click: _vm.toggleCollapse }\n },\n [\n _vm.item.iconUrl\n ? _c(\"img\", {\n attrs: { alt: _vm.item.text, src: _vm.item.iconUrl }\n })\n : _vm._e(),\n _vm._v(\"\\n\\t\\t\" + _vm._s(_vm.item.text) + \"\\n\\t\")\n ]\n )\n : _vm.item.router\n ? _c(\n \"router-link\",\n { class: _vm.item.icon, attrs: { to: _vm.item.router } },\n [\n _vm.item.iconUrl\n ? _c(\"img\", {\n attrs: { alt: _vm.item.text, src: _vm.item.iconUrl }\n })\n : _vm._e(),\n _vm._v(\"\\n\\t\\t\" + _vm._s(_vm.item.text) + \"\\n\\t\")\n ]\n )\n : _vm._e(),\n _vm._v(\" \"),\n _vm.item.utils\n ? _c(\"div\", { staticClass: \"app-navigation-entry-utils\" }, [\n _c(\n \"ul\",\n [\n Number.isInteger(_vm.item.utils.counter)\n ? _c(\n \"li\",\n { staticClass: \"app-navigation-entry-utils-counter\" },\n [_vm._v(_vm._s(_vm.item.utils.counter))]\n )\n : _vm._e(),\n _vm._v(\" \"),\n _vm.item.utils.actions &&\n _vm.item.utils.actions.length === 1 &&\n Number.isInteger(_vm.item.utils.counter)\n ? _c(\n \"li\",\n { staticClass: \"app-navigation-entry-utils-menu-button\" },\n [\n _c(\"button\", {\n class: _vm.item.utils.actions[0].icon,\n attrs: { title: _vm.item.utils.actions[0].text },\n on: { click: _vm.item.utils.actions[0].action }\n })\n ]\n )\n : _vm.item.utils.actions &&\n _vm.item.utils.actions.length === 2 &&\n !Number.isInteger(_vm.item.utils.counter)\n ? _vm._l(_vm.item.utils.actions, function(action) {\n return _c(\n \"li\",\n {\n key: action.action,\n staticClass:\n \"app-navigation-entry-utils-menu-button\"\n },\n [\n _c(\"button\", {\n class: action.icon,\n attrs: { title: action.text },\n on: { click: action.action }\n })\n ]\n )\n })\n : _vm.item.utils.actions &&\n _vm.item.utils.actions.length > 1 &&\n (Number.isInteger(_vm.item.utils.counter) ||\n _vm.item.utils.actions.length > 2)\n ? _c(\n \"li\",\n {\n staticClass:\n \"app-navigation-entry-utils-menu-button\"\n },\n [\n _c(\"button\", {\n directives: [\n {\n name: \"click-outside\",\n rawName: \"v-click-outside\",\n value: _vm.hideMenu,\n expression: \"hideMenu\"\n }\n ],\n on: { click: _vm.showMenu }\n })\n ]\n )\n : _vm._e()\n ],\n 2\n )\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _vm.item.utils &&\n _vm.item.utils.actions &&\n _vm.item.utils.actions.length > 1 &&\n (Number.isInteger(_vm.item.utils.counter) ||\n _vm.item.utils.actions.length > 2)\n ? _c(\n \"div\",\n {\n staticClass: \"app-navigation-entry-menu\",\n class: { open: _vm.openedMenu }\n },\n [_c(\"popover-menu\", { attrs: { menu: _vm.item.utils.actions } })],\n 1\n )\n : _vm._e(),\n _vm._v(\" \"),\n _vm.item.undo\n ? _c(\"div\", { staticClass: \"app-navigation-entry-deleted\" }, [\n _c(\n \"div\",\n { staticClass: \"app-navigation-entry-deleted-description\" },\n [_vm._v(_vm._s(_vm.item.undo.text))]\n ),\n _vm._v(\" \"),\n _c(\"button\", {\n staticClass: \"app-navigation-entry-deleted-button icon-history\",\n attrs: { title: _vm.t(\"settings\", \"Undo\") }\n })\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _vm.item.edit\n ? _c(\"div\", { staticClass: \"app-navigation-entry-edit\" }, [\n _c(\"form\", [\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model\",\n value: _vm.item.text,\n expression: \"item.text\"\n }\n ],\n attrs: { type: \"text\" },\n domProps: { value: _vm.item.text },\n on: {\n input: function($event) {\n if ($event.target.composing) {\n return\n }\n _vm.$set(_vm.item, \"text\", $event.target.value)\n }\n }\n }),\n _vm._v(\" \"),\n _c(\"input\", {\n staticClass: \"icon-confirm\",\n attrs: { type: \"submit\", value: \"\" }\n }),\n _vm._v(\" \"),\n _c(\"input\", {\n staticClass: \"icon-close\",\n attrs: { type: \"submit\", value: \"\" },\n on: {\n click: function($event) {\n $event.stopPropagation()\n $event.preventDefault()\n return _vm.cancelEdit($event)\n }\n }\n })\n ])\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _vm.item.children\n ? _c(\n \"ul\",\n _vm._l(_vm.item.children, function(item, key) {\n return _c(\"navigation-item\", { key: key, attrs: { item: item } })\n })\n )\n : _vm._e()\n ],\n 1\n )\n}\nvar staticRenderFns = []\nrender._withStripped = true\nexport { render, staticRenderFns }\nif (module.hot) {\n module.hot.accept()\n if (module.hot.data) {\n require(\"vue-hot-reload-api\") .rerender(\"data-v-2cb61dde\", { render: render, staticRenderFns: staticRenderFns })\n }\n}","var disposed = false\n/* script */\nexport * from \"!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./navigationItem.vue\"\nimport __vue_script__ from \"!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./navigationItem.vue\"\n/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-2cb61dde\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./navigationItem.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = null\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\nComponent.options.__file = \"src/components/appNavigation/navigationItem.vue\"\n\n/* hot reload */\nif (module.hot) {(function () {\n var hotAPI = require(\"vue-hot-reload-api\")\n hotAPI.install(require(\"vue\"), false)\n if (!hotAPI.compatible) return\n module.hot.accept()\n if (!module.hot.data) {\n hotAPI.createRecord(\"data-v-2cb61dde\", Component.options)\n } else {\n hotAPI.reload(\"data-v-2cb61dde\", Component.options)\n }\n module.hot.dispose(function (data) {\n disposed = true\n })\n})()}\n\nexport default Component.exports\n","\n\n\n","var render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n {\n class: { \"icon-loading\": _vm.menu.loading },\n attrs: { id: \"app-navigation\" }\n },\n [\n _vm.menu.new\n ? _c(\"div\", { staticClass: \"app-navigation-new\" }, [\n _c(\n \"button\",\n {\n class: _vm.menu.new.icon,\n attrs: { type: \"button\", id: _vm.menu.new.id },\n on: { click: _vm.menu.new.action }\n },\n [_vm._v(_vm._s(_vm.menu.new.text))]\n )\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _c(\n \"ul\",\n { attrs: { id: _vm.menu.id } },\n _vm._l(_vm.menu.items, function(item, key) {\n return _c(\"navigation-item\", { key: key, attrs: { item: item } })\n })\n ),\n _vm._v(\" \"),\n _c(\"div\", { attrs: { id: \"app-settings\" } }, [\n _c(\"div\", { attrs: { id: \"app-settings-header\" } }, [\n _c(\n \"button\",\n {\n staticClass: \"settings-button\",\n attrs: { \"data-apps-slide-toggle\": \"#app-settings-content\" }\n },\n [_vm._v(_vm._s(_vm.t(\"settings\", \"Settings\")))]\n )\n ]),\n _vm._v(\" \"),\n _c(\n \"div\",\n { attrs: { id: \"app-settings-content\" } },\n [_vm._t(\"settings-content\")],\n 2\n )\n ])\n ]\n )\n}\nvar staticRenderFns = []\nrender._withStripped = true\nexport { render, staticRenderFns }\nif (module.hot) {\n module.hot.accept()\n if (module.hot.data) {\n require(\"vue-hot-reload-api\") .rerender(\"data-v-142c1cb5\", { render: render, staticRenderFns: staticRenderFns })\n }\n}","var disposed = false\n/* script */\nexport * from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./appNavigation.vue\"\nimport __vue_script__ from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./appNavigation.vue\"\n/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-142c1cb5\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../node_modules/vue-loader/lib/selector?type=template&index=0!./appNavigation.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = null\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\nComponent.options.__file = \"src/components/appNavigation.vue\"\n\n/* hot reload */\nif (module.hot) {(function () {\n var hotAPI = require(\"vue-hot-reload-api\")\n hotAPI.install(require(\"vue\"), false)\n if (!hotAPI.compatible) return\n module.hot.accept()\n if (!module.hot.data) {\n hotAPI.createRecord(\"data-v-142c1cb5\", Component.options)\n } else {\n hotAPI.reload(\"data-v-142c1cb5\", Component.options)\n }\n module.hot.dispose(function (data) {\n disposed = true\n })\n})()}\n\nexport default Component.exports\n","\n\n\n","var render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n {\n staticClass: \"row\",\n class: { disabled: _vm.loading.delete || _vm.loading.disable }\n },\n [\n _c(\n \"div\",\n {\n staticClass: \"avatar\",\n class: {\n \"icon-loading-small\": _vm.loading.delete || _vm.loading.disable\n }\n },\n [\n !_vm.loading.delete && !_vm.loading.disable\n ? _c(\"img\", {\n attrs: {\n alt: \"\",\n width: \"32\",\n height: \"32\",\n src: _vm.generateAvatar(_vm.user.id, 32),\n srcset:\n _vm.generateAvatar(_vm.user.id, 64) +\n \" 2x, \" +\n _vm.generateAvatar(_vm.user.id, 128) +\n \" 4x\"\n }\n })\n : _vm._e()\n ]\n ),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"name\" }, [_vm._v(_vm._s(_vm.user.id))]),\n _vm._v(\" \"),\n _c(\n \"form\",\n {\n staticClass: \"displayName\",\n class: { \"icon-loading-small\": _vm.loading.displayName },\n on: {\n submit: function($event) {\n $event.preventDefault()\n return _vm.updateDisplayName($event)\n }\n }\n },\n [\n _c(\"input\", {\n ref: \"displayName\",\n attrs: {\n id: \"displayName\" + _vm.user.id + _vm.rand,\n type: \"text\",\n disabled: _vm.loading.displayName || _vm.loading.all,\n autocomplete: \"new-password\",\n autocorrect: \"off\",\n autocapitalize: \"off\",\n spellcheck: \"false\"\n },\n domProps: { value: _vm.user.displayname }\n }),\n _vm._v(\" \"),\n _c(\"input\", {\n staticClass: \"icon-confirm\",\n attrs: { type: \"submit\", value: \"\" }\n })\n ]\n ),\n _vm._v(\" \"),\n _vm.settings.canChangePassword\n ? _c(\n \"form\",\n {\n staticClass: \"password\",\n class: { \"icon-loading-small\": _vm.loading.password },\n on: {\n submit: function($event) {\n $event.preventDefault()\n return _vm.updatePassword($event)\n }\n }\n },\n [\n _c(\"input\", {\n ref: \"password\",\n attrs: {\n id: \"password\" + _vm.user.id + _vm.rand,\n type: \"password\",\n required: \"\",\n disabled: _vm.loading.password || _vm.loading.all,\n minlength: _vm.minPasswordLength,\n value: \"\",\n placeholder: _vm.t(\"settings\", \"New password\"),\n autocomplete: \"new-password\",\n autocorrect: \"off\",\n autocapitalize: \"off\",\n spellcheck: \"false\"\n }\n }),\n _vm._v(\" \"),\n _c(\"input\", {\n staticClass: \"icon-confirm\",\n attrs: { type: \"submit\", value: \"\" }\n })\n ]\n )\n : _c(\"div\"),\n _vm._v(\" \"),\n _c(\n \"form\",\n {\n staticClass: \"mailAddress\",\n class: { \"icon-loading-small\": _vm.loading.mailAddress },\n on: {\n submit: function($event) {\n $event.preventDefault()\n return _vm.updateEmail($event)\n }\n }\n },\n [\n _c(\"input\", {\n ref: \"mailAddress\",\n attrs: {\n id: \"mailAddress\" + _vm.user.id + _vm.rand,\n type: \"email\",\n disabled: _vm.loading.mailAddress || _vm.loading.all,\n autocomplete: \"new-password\",\n autocorrect: \"off\",\n autocapitalize: \"off\",\n spellcheck: \"false\"\n },\n domProps: { value: _vm.user.email }\n }),\n _vm._v(\" \"),\n _c(\"input\", {\n staticClass: \"icon-confirm\",\n attrs: { type: \"submit\", value: \"\" }\n })\n ]\n ),\n _vm._v(\" \"),\n _c(\n \"div\",\n {\n staticClass: \"groups\",\n class: { \"icon-loading-small\": _vm.loading.groups }\n },\n [\n _c(\n \"multiselect\",\n {\n staticClass: \"multiselect-vue\",\n attrs: {\n value: _vm.userGroups,\n options: _vm.groups,\n disabled: _vm.loading.groups || _vm.loading.all,\n \"tag-placeholder\": \"create\",\n placeholder: _vm.t(\"settings\", \"Add user in group\"),\n label: \"name\",\n \"track-by\": \"id\",\n limit: 2,\n multiple: true,\n taggable: _vm.settings.isAdmin,\n closeOnSelect: false\n },\n on: {\n tag: _vm.createGroup,\n select: _vm.addUserGroup,\n remove: _vm.removeUserGroup\n }\n },\n [\n _c(\n \"span\",\n {\n directives: [\n {\n name: \"tooltip\",\n rawName: \"v-tooltip.auto\",\n value: _vm.formatGroupsTitle(_vm.userGroups),\n expression: \"formatGroupsTitle(userGroups)\",\n modifiers: { auto: true }\n }\n ],\n staticClass: \"multiselect__limit\",\n attrs: { slot: \"limit\" },\n slot: \"limit\"\n },\n [_vm._v(\"+\" + _vm._s(_vm.userGroups.length - 2))]\n ),\n _vm._v(\" \"),\n _c(\"span\", { attrs: { slot: \"noResult\" }, slot: \"noResult\" }, [\n _vm._v(_vm._s(_vm.t(\"settings\", \"No results\")))\n ])\n ]\n )\n ],\n 1\n ),\n _vm._v(\" \"),\n _vm.subAdminsGroups.length > 0 && _vm.settings.isAdmin\n ? _c(\n \"div\",\n {\n staticClass: \"subadmins\",\n class: { \"icon-loading-small\": _vm.loading.subadmins }\n },\n [\n _c(\n \"multiselect\",\n {\n staticClass: \"multiselect-vue\",\n attrs: {\n value: _vm.userSubAdminsGroups,\n options: _vm.subAdminsGroups,\n disabled: _vm.loading.subadmins || _vm.loading.all,\n placeholder: _vm.t(\"settings\", \"Set user as admin for\"),\n label: \"name\",\n \"track-by\": \"id\",\n limit: 2,\n multiple: true,\n closeOnSelect: false\n },\n on: {\n select: _vm.addUserSubAdmin,\n remove: _vm.removeUserSubAdmin\n }\n },\n [\n _c(\n \"span\",\n {\n directives: [\n {\n name: \"tooltip\",\n rawName: \"v-tooltip.auto\",\n value: _vm.formatGroupsTitle(_vm.userSubAdminsGroups),\n expression: \"formatGroupsTitle(userSubAdminsGroups)\",\n modifiers: { auto: true }\n }\n ],\n staticClass: \"multiselect__limit\",\n attrs: { slot: \"limit\" },\n slot: \"limit\"\n },\n [_vm._v(\"+\" + _vm._s(_vm.userSubAdminsGroups.length - 2))]\n ),\n _vm._v(\" \"),\n _c(\n \"span\",\n { attrs: { slot: \"noResult\" }, slot: \"noResult\" },\n [_vm._v(_vm._s(_vm.t(\"settings\", \"No results\")))]\n )\n ]\n )\n ],\n 1\n )\n : _vm._e(),\n _vm._v(\" \"),\n _c(\n \"div\",\n {\n staticClass: \"quota\",\n class: { \"icon-loading-small\": _vm.loading.quota }\n },\n [\n _c(\"multiselect\", {\n staticClass: \"multiselect-vue\",\n attrs: {\n value: _vm.userQuota,\n options: _vm.quotaOptions,\n disabled: _vm.loading.quota || _vm.loading.all,\n \"tag-placeholder\": \"create\",\n placeholder: _vm.t(\"settings\", \"Select user quota\"),\n label: \"label\",\n \"track-by\": \"id\",\n allowEmpty: false,\n taggable: true\n },\n on: { tag: _vm.validateQuota, input: _vm.setUserQuota }\n }),\n _vm._v(\" \"),\n _c(\"progress\", {\n staticClass: \"quota-user-progress\",\n class: { warn: _vm.usedQuota > 80 },\n attrs: { max: \"100\" },\n domProps: { value: _vm.usedQuota }\n })\n ],\n 1\n ),\n _vm._v(\" \"),\n _vm.showConfig.showLanguages\n ? _c(\n \"div\",\n {\n staticClass: \"languages\",\n class: { \"icon-loading-small\": _vm.loading.languages }\n },\n [\n _c(\"multiselect\", {\n staticClass: \"multiselect-vue\",\n attrs: {\n value: _vm.userLanguage,\n options: _vm.languages,\n disabled: _vm.loading.languages || _vm.loading.all,\n placeholder: _vm.t(\"settings\", \"No language set\"),\n label: \"name\",\n \"track-by\": \"code\",\n allowEmpty: false,\n \"group-values\": \"languages\",\n \"group-label\": \"label\"\n },\n on: { input: _vm.setUserLanguage }\n })\n ],\n 1\n )\n : _vm._e(),\n _vm._v(\" \"),\n _vm.showConfig.showStoragePath\n ? _c(\"div\", { staticClass: \"storageLocation\" }, [\n _vm._v(_vm._s(_vm.user.storageLocation))\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _vm.showConfig.showUserBackend\n ? _c(\"div\", { staticClass: \"userBackend\" }, [\n _vm._v(_vm._s(_vm.user.backend))\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _vm.showConfig.showLastLogin\n ? _c(\n \"div\",\n {\n directives: [\n {\n name: \"tooltip\",\n rawName: \"v-tooltip.auto\",\n value:\n _vm.user.lastLogin > 0\n ? _vm.OC.Util.formatDate(_vm.user.lastLogin)\n : \"\",\n expression:\n \"user.lastLogin>0 ? OC.Util.formatDate(user.lastLogin) : ''\",\n modifiers: { auto: true }\n }\n ],\n staticClass: \"lastLogin\"\n },\n [\n _vm._v(\n \"\\n\\t\\t\" +\n _vm._s(\n _vm.user.lastLogin > 0\n ? _vm.OC.Util.relativeModifiedDate(_vm.user.lastLogin)\n : _vm.t(\"settings\", \"Never\")\n ) +\n \"\\n\\t\"\n )\n ]\n )\n : _vm._e(),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"userActions\" }, [\n _vm.OC.currentUser !== _vm.user.id &&\n _vm.user.id !== \"admin\" &&\n !_vm.loading.all\n ? _c(\"div\", { staticClass: \"toggleUserActions\" }, [\n _c(\"div\", {\n directives: [\n {\n name: \"click-outside\",\n rawName: \"v-click-outside\",\n value: _vm.hideMenu,\n expression: \"hideMenu\"\n }\n ],\n staticClass: \"icon-more\",\n on: { click: _vm.toggleMenu }\n }),\n _vm._v(\" \"),\n _c(\n \"div\",\n { staticClass: \"popovermenu\", class: { open: _vm.openedMenu } },\n [_c(\"popover-menu\", { attrs: { menu: _vm.userActions } })],\n 1\n )\n ])\n : _vm._e()\n ])\n ]\n )\n}\nvar staticRenderFns = []\nrender._withStripped = true\nexport { render, staticRenderFns }\nif (module.hot) {\n module.hot.accept()\n if (module.hot.data) {\n require(\"vue-hot-reload-api\") .rerender(\"data-v-d19586ce\", { render: render, staticRenderFns: staticRenderFns })\n }\n}","var disposed = false\n/* script */\nexport * from \"!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./userRow.vue\"\nimport __vue_script__ from \"!!babel-loader!../../../node_modules/vue-loader/lib/selector?type=script&index=0!./userRow.vue\"\n/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-d19586ce\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../../node_modules/vue-loader/lib/selector?type=template&index=0!./userRow.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = null\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\nComponent.options.__file = \"src/components/userList/userRow.vue\"\n\n/* hot reload */\nif (module.hot) {(function () {\n var hotAPI = require(\"vue-hot-reload-api\")\n hotAPI.install(require(\"vue\"), false)\n if (!hotAPI.compatible) return\n module.hot.accept()\n if (!module.hot.data) {\n hotAPI.createRecord(\"data-v-d19586ce\", Component.options)\n } else {\n hotAPI.reload(\"data-v-d19586ce\", Component.options)\n }\n module.hot.dispose(function (data) {\n disposed = true\n })\n})()}\n\nexport default Component.exports\n","\n\n\n","var render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n {\n staticClass: \"user-list-grid\",\n attrs: { id: \"app-content\" },\n on: {\n \"&scroll\": function($event) {\n return _vm.onScroll($event)\n }\n }\n },\n [\n _c(\n \"div\",\n {\n staticClass: \"row\",\n class: { sticky: _vm.scrolled && !_vm.showConfig.showNewUserForm },\n attrs: { id: \"grid-header\" }\n },\n [\n _c(\"div\", { staticClass: \"avatar\", attrs: { id: \"headerAvatar\" } }),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"name\", attrs: { id: \"headerName\" } }, [\n _vm._v(_vm._s(_vm.t(\"settings\", \"Username\")))\n ]),\n _vm._v(\" \"),\n _c(\n \"div\",\n { staticClass: \"displayName\", attrs: { id: \"headerDisplayName\" } },\n [_vm._v(_vm._s(_vm.t(\"settings\", \"Full name\")))]\n ),\n _vm._v(\" \"),\n _c(\n \"div\",\n { staticClass: \"password\", attrs: { id: \"headerPassword\" } },\n [_vm._v(_vm._s(_vm.t(\"settings\", \"Password\")))]\n ),\n _vm._v(\" \"),\n _c(\n \"div\",\n { staticClass: \"mailAddress\", attrs: { id: \"headerAddress\" } },\n [_vm._v(_vm._s(_vm.t(\"settings\", \"Email\")))]\n ),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"groups\", attrs: { id: \"headerGroups\" } }, [\n _vm._v(_vm._s(_vm.t(\"settings\", \"Groups\")))\n ]),\n _vm._v(\" \"),\n _vm.subAdminsGroups.length > 0 && _vm.settings.isAdmin\n ? _c(\n \"div\",\n { staticClass: \"subadmins\", attrs: { id: \"headerSubAdmins\" } },\n [_vm._v(_vm._s(_vm.t(\"settings\", \"Group admin for\")))]\n )\n : _vm._e(),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"quota\", attrs: { id: \"headerQuota\" } }, [\n _vm._v(_vm._s(_vm.t(\"settings\", \"Quota\")))\n ]),\n _vm._v(\" \"),\n _vm.showConfig.showLanguages\n ? _c(\n \"div\",\n { staticClass: \"languages\", attrs: { id: \"headerLanguages\" } },\n [_vm._v(_vm._s(_vm.t(\"settings\", \"Languages\")))]\n )\n : _vm._e(),\n _vm._v(\" \"),\n _vm.showConfig.showStoragePath\n ? _c(\n \"div\",\n { staticClass: \"headerStorageLocation storageLocation\" },\n [_vm._v(_vm._s(_vm.t(\"settings\", \"Storage location\")))]\n )\n : _vm._e(),\n _vm._v(\" \"),\n _vm.showConfig.showUserBackend\n ? _c(\"div\", { staticClass: \"headerUserBackend userBackend\" }, [\n _vm._v(_vm._s(_vm.t(\"settings\", \"User backend\")))\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _vm.showConfig.showLastLogin\n ? _c(\"div\", { staticClass: \"headerLastLogin lastLogin\" }, [\n _vm._v(_vm._s(_vm.t(\"settings\", \"Last login\")))\n ])\n : _vm._e(),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"userActions\" })\n ]\n ),\n _vm._v(\" \"),\n _c(\n \"form\",\n {\n directives: [\n {\n name: \"show\",\n rawName: \"v-show\",\n value: _vm.showConfig.showNewUserForm,\n expression: \"showConfig.showNewUserForm\"\n }\n ],\n staticClass: \"row\",\n class: { sticky: _vm.scrolled && _vm.showConfig.showNewUserForm },\n attrs: { id: \"new-user\", disabled: _vm.loading },\n on: {\n submit: function($event) {\n $event.preventDefault()\n return _vm.createUser($event)\n }\n }\n },\n [\n _c(\"div\", { class: _vm.loading ? \"icon-loading-small\" : \"icon-add\" }),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"name\" }, [\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model\",\n value: _vm.newUser.id,\n expression: \"newUser.id\"\n }\n ],\n attrs: {\n id: \"newusername\",\n type: \"text\",\n required: \"\",\n placeholder: _vm.t(\"settings\", \"User name\"),\n name: \"username\",\n autocomplete: \"off\",\n autocapitalize: \"none\",\n autocorrect: \"off\",\n pattern: \"[a-zA-Z0-9 _\\\\.@\\\\-']+\"\n },\n domProps: { value: _vm.newUser.id },\n on: {\n input: function($event) {\n if ($event.target.composing) {\n return\n }\n _vm.$set(_vm.newUser, \"id\", $event.target.value)\n }\n }\n })\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"displayName\" }, [\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model\",\n value: _vm.newUser.displayName,\n expression: \"newUser.displayName\"\n }\n ],\n attrs: {\n id: \"newdisplayname\",\n type: \"text\",\n placeholder: _vm.t(\"settings\", \"Display name\"),\n name: \"displayname\",\n autocomplete: \"off\",\n autocapitalize: \"none\",\n autocorrect: \"off\"\n },\n domProps: { value: _vm.newUser.displayName },\n on: {\n input: function($event) {\n if ($event.target.composing) {\n return\n }\n _vm.$set(_vm.newUser, \"displayName\", $event.target.value)\n }\n }\n })\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"password\" }, [\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model\",\n value: _vm.newUser.password,\n expression: \"newUser.password\"\n }\n ],\n attrs: {\n id: \"newuserpassword\",\n type: \"password\",\n required: _vm.newUser.mailAddress === \"\",\n placeholder: _vm.t(\"settings\", \"Password\"),\n name: \"password\",\n autocomplete: \"new-password\",\n autocapitalize: \"none\",\n autocorrect: \"off\",\n minlength: _vm.minPasswordLength\n },\n domProps: { value: _vm.newUser.password },\n on: {\n input: function($event) {\n if ($event.target.composing) {\n return\n }\n _vm.$set(_vm.newUser, \"password\", $event.target.value)\n }\n }\n })\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"mailAddress\" }, [\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model\",\n value: _vm.newUser.mailAddress,\n expression: \"newUser.mailAddress\"\n }\n ],\n attrs: {\n id: \"newemail\",\n type: \"email\",\n required: _vm.newUser.password === \"\",\n placeholder: _vm.t(\"settings\", \"Mail address\"),\n name: \"email\",\n autocomplete: \"off\",\n autocapitalize: \"none\",\n autocorrect: \"off\"\n },\n domProps: { value: _vm.newUser.mailAddress },\n on: {\n input: function($event) {\n if ($event.target.composing) {\n return\n }\n _vm.$set(_vm.newUser, \"mailAddress\", $event.target.value)\n }\n }\n })\n ]),\n _vm._v(\" \"),\n _c(\n \"div\",\n { staticClass: \"groups\" },\n [\n !_vm.settings.isAdmin\n ? _c(\"input\", {\n attrs: {\n type: \"text\",\n tabindex: \"-1\",\n id: \"newgroups\",\n required: !_vm.settings.isAdmin\n },\n domProps: { value: _vm.newUser.groups }\n })\n : _vm._e(),\n _vm._v(\" \"),\n _c(\n \"multiselect\",\n {\n staticClass: \"multiselect-vue\",\n attrs: {\n options: _vm.groups,\n placeholder: _vm.t(\"settings\", \"Add user in group\"),\n label: \"name\",\n \"track-by\": \"id\",\n multiple: true,\n \"close-on-select\": false,\n allowEmpty: _vm.settings.isAdmin\n },\n model: {\n value: _vm.newUser.groups,\n callback: function($$v) {\n _vm.$set(_vm.newUser, \"groups\", $$v)\n },\n expression: \"newUser.groups\"\n }\n },\n [\n _c(\n \"span\",\n { attrs: { slot: \"noResult\" }, slot: \"noResult\" },\n [_vm._v(_vm._s(_vm.t(\"settings\", \"No results\")))]\n )\n ]\n )\n ],\n 1\n ),\n _vm._v(\" \"),\n _vm.subAdminsGroups.length > 0 && _vm.settings.isAdmin\n ? _c(\n \"div\",\n { staticClass: \"subadmins\" },\n [\n _c(\n \"multiselect\",\n {\n staticClass: \"multiselect-vue\",\n attrs: {\n options: _vm.subAdminsGroups,\n placeholder: _vm.t(\"settings\", \"Set user as admin for\"),\n label: \"name\",\n \"track-by\": \"id\",\n multiple: true,\n \"close-on-select\": false\n },\n model: {\n value: _vm.newUser.subAdminsGroups,\n callback: function($$v) {\n _vm.$set(_vm.newUser, \"subAdminsGroups\", $$v)\n },\n expression: \"newUser.subAdminsGroups\"\n }\n },\n [\n _c(\n \"span\",\n { attrs: { slot: \"noResult\" }, slot: \"noResult\" },\n [_vm._v(_vm._s(_vm.t(\"settings\", \"No results\")))]\n )\n ]\n )\n ],\n 1\n )\n : _vm._e(),\n _vm._v(\" \"),\n _c(\n \"div\",\n { staticClass: \"quota\" },\n [\n _c(\"multiselect\", {\n staticClass: \"multiselect-vue\",\n attrs: {\n options: _vm.quotaOptions,\n placeholder: _vm.t(\"settings\", \"Select user quota\"),\n label: \"label\",\n \"track-by\": \"id\",\n allowEmpty: false,\n taggable: true\n },\n on: { tag: _vm.validateQuota },\n model: {\n value: _vm.newUser.quota,\n callback: function($$v) {\n _vm.$set(_vm.newUser, \"quota\", $$v)\n },\n expression: \"newUser.quota\"\n }\n })\n ],\n 1\n ),\n _vm._v(\" \"),\n _vm.showConfig.showLanguages\n ? _c(\n \"div\",\n { staticClass: \"languages\" },\n [\n _c(\"multiselect\", {\n staticClass: \"multiselect-vue\",\n attrs: {\n options: _vm.languages,\n placeholder: _vm.t(\"settings\", \"Default language\"),\n label: \"name\",\n \"track-by\": \"code\",\n allowEmpty: false,\n \"group-values\": \"languages\",\n \"group-label\": \"label\"\n },\n model: {\n value: _vm.newUser.language,\n callback: function($$v) {\n _vm.$set(_vm.newUser, \"language\", $$v)\n },\n expression: \"newUser.language\"\n }\n })\n ],\n 1\n )\n : _vm._e(),\n _vm._v(\" \"),\n _vm.showConfig.showStoragePath\n ? _c(\"div\", { staticClass: \"storageLocation\" })\n : _vm._e(),\n _vm._v(\" \"),\n _vm.showConfig.showUserBackend\n ? _c(\"div\", { staticClass: \"userBackend\" })\n : _vm._e(),\n _vm._v(\" \"),\n _vm.showConfig.showLastLogin\n ? _c(\"div\", { staticClass: \"lastLogin\" })\n : _vm._e(),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"userActions\" }, [\n _c(\"input\", {\n staticClass: \"button primary icon-checkmark-white has-tooltip\",\n attrs: {\n type: \"submit\",\n id: \"newsubmit\",\n value: \"\",\n title: _vm.t(\"settings\", \"Add a new user\")\n }\n }),\n _vm._v(\" \"),\n _c(\"input\", {\n staticClass: \"button icon-close has-tooltip\",\n attrs: {\n type: \"reset\",\n id: \"newreset\",\n value: \"\",\n title: _vm.t(\"settings\", \"Cancel and reset the form\")\n },\n on: { click: _vm.resetForm }\n })\n ])\n ]\n ),\n _vm._v(\" \"),\n _vm._l(_vm.filteredUsers, function(user, key) {\n return _c(\"user-row\", {\n key: key,\n attrs: {\n user: user,\n settings: _vm.settings,\n showConfig: _vm.showConfig,\n groups: _vm.groups,\n subAdminsGroups: _vm.subAdminsGroups,\n quotaOptions: _vm.quotaOptions,\n languages: _vm.languages\n }\n })\n }),\n _vm._v(\" \"),\n _c(\n \"infinite-loading\",\n { ref: \"infiniteLoading\", on: { infinite: _vm.infiniteHandler } },\n [\n _c(\"div\", { attrs: { slot: \"spinner\" }, slot: \"spinner\" }, [\n _c(\"div\", { staticClass: \"users-icon-loading icon-loading\" })\n ]),\n _vm._v(\" \"),\n _c(\"div\", { attrs: { slot: \"no-more\" }, slot: \"no-more\" }, [\n _c(\"div\", { staticClass: \"users-list-end\" }, [\n _vm._v(\"— \" + _vm._s(_vm.t(\"settings\", \"no more results\")) + \" —\")\n ])\n ]),\n _vm._v(\" \"),\n _c(\"div\", { attrs: { slot: \"no-results\" }, slot: \"no-results\" }, [\n _c(\"div\", { attrs: { id: \"emptycontent\" } }, [\n _c(\"div\", { staticClass: \"icon-contacts-dark\" }),\n _vm._v(\" \"),\n _c(\"h2\", [_vm._v(_vm._s(_vm.t(\"settings\", \"No users in here\")))])\n ])\n ])\n ]\n )\n ],\n 2\n )\n}\nvar staticRenderFns = []\nrender._withStripped = true\nexport { render, staticRenderFns }\nif (module.hot) {\n module.hot.accept()\n if (module.hot.data) {\n require(\"vue-hot-reload-api\") .rerender(\"data-v-40745299\", { render: render, staticRenderFns: staticRenderFns })\n }\n}","var disposed = false\n/* script */\nexport * from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./userList.vue\"\nimport __vue_script__ from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./userList.vue\"\n/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-40745299\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../node_modules/vue-loader/lib/selector?type=template&index=0!./userList.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = null\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\nComponent.options.__file = \"src/components/userList.vue\"\n\n/* hot reload */\nif (module.hot) {(function () {\n var hotAPI = require(\"vue-hot-reload-api\")\n hotAPI.install(require(\"vue\"), false)\n if (!hotAPI.compatible) return\n module.hot.accept()\n if (!module.hot.data) {\n hotAPI.createRecord(\"data-v-40745299\", Component.options)\n } else {\n hotAPI.reload(\"data-v-40745299\", Component.options)\n }\n module.hot.dispose(function (data) {\n disposed = true\n })\n})()}\n\nexport default Component.exports\n","import axios from 'axios';\n\nconst requestToken = document.getElementsByTagName('head')[0].getAttribute('data-requesttoken');\nconst tokenHeaders = { headers: { requesttoken: requestToken } };\n\nconst sanitize = function(url) {\n\treturn url.replace(/\\/$/, ''); // Remove last url slash\n};\n\nexport default {\n\n\t/**\n\t * This Promise is used to chain a request that require an admin password confirmation\n\t * Since chaining Promise have a very precise behavior concerning catch and then,\n\t * you'll need to be careful when using it.\n\t * e.g\n\t * // store\n\t * \taction(context) {\n\t *\t\treturn api.requireAdmin().then((response) => {\n\t *\t\t\treturn api.get('url')\n\t *\t\t\t\t.then((response) => {API success})\n\t *\t\t\t\t.catch((error) => {API failure});\n\t *\t\t}).catch((error) => {requireAdmin failure});\n\t *\t}\n\t * // vue\n\t *\tthis.$store.dispatch('action').then(() => {always executed})\n\t *\n\t * Since Promise.then().catch().then() will always execute the last then\n\t * this.$store.dispatch('action').then will always be executed\n\t * \n\t * If you want requireAdmin failure to also catch the API request failure\n\t * you will need to throw a new error in the api.get.catch()\n\t * \n\t * e.g\n\t *\tapi.requireAdmin().then((response) => {\n\t *\t\tapi.get('url')\n\t *\t\t\t.then((response) => {API success})\n\t *\t\t\t.catch((error) => {throw error;});\n\t *\t}).catch((error) => {requireAdmin OR API failure});\n\t * \n\t * @returns {Promise}\n\t */\n\trequireAdmin() {\n\t\treturn new Promise(function(resolve, reject) {\n\t\t\t// TODO: migrate the OC.dialog to Vue and avoid this mess\n\t\t\t// wait for password confirmation\n\t\t\tlet passwordTimeout;\n\t\t\tlet waitForpassword = function() {\n\t\t\t\tif (OC.PasswordConfirmation.requiresPasswordConfirmation()) {\n\t\t\t\t\tpasswordTimeout = setTimeout(waitForpassword, 500);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tclearTimeout(passwordTimeout);\n\t\t\t\tclearTimeout(promiseTimeout);\n\t\t\t\tresolve();\n\t\t\t};\n\n\t\t\t// automatically reject after 5s if not resolved\n\t\t\tlet promiseTimeout = setTimeout(() => {\n\t\t\t\tclearTimeout(passwordTimeout);\n\t\t\t\t// close dialog\n\t\t\t\tif (document.getElementsByClassName('oc-dialog-close').length>0) {\n\t\t\t\t\tdocument.getElementsByClassName('oc-dialog-close')[0].click();\n\t\t\t\t}\n\t\t\t\tOC.Notification.showTemporary(t('settings', 'You did not enter the password in time'));\n\t\t\t\treject('Password request cancelled');\n\t\t\t}, 7000); \n\n\t\t\t// request password\n\t\t\tOC.PasswordConfirmation.requirePasswordConfirmation();\n\t\t\twaitForpassword();\n\t\t});\n\t},\n\tget(url) {\n\t\treturn axios.get(sanitize(url), tokenHeaders)\n\t\t\t.then((response) => Promise.resolve(response))\n\t\t\t.catch((error) => Promise.reject(error));\n\t},\n\tpost(url, data) {\n\t\treturn axios.post(sanitize(url), data, tokenHeaders)\n\t\t\t.then((response) => Promise.resolve(response))\n\t\t\t.catch((error) => Promise.reject(error));\n\t},\n\tpatch(url, data) {\n\t\treturn axios.patch(sanitize(url), data, tokenHeaders)\n\t\t\t.then((response) => Promise.resolve(response))\n\t\t\t.catch((error) => Promise.reject(error));\n\t},\n\tput(url, data) {\n\t\treturn axios.put(sanitize(url), data, tokenHeaders)\n\t\t\t.then((response) => Promise.resolve(response))\n\t\t\t.catch((error) => Promise.reject(error));\n\t},\n\tdelete(url, data) {\n\t\treturn axios.delete(sanitize(url), { data: data, headers: tokenHeaders.headers })\n\t\t\t.then((response) => Promise.resolve(response))\n\t\t\t.catch((error) => Promise.reject(error));\n\t}\n};","\n\n\n","var render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n { attrs: { id: \"app\" } },\n [\n _c(\n \"app-navigation\",\n { attrs: { menu: _vm.menu } },\n [\n _c(\"template\", { slot: \"settings-content\" }, [\n _c(\n \"div\",\n [\n _c(\"p\", [_vm._v(_vm._s(_vm.t(\"settings\", \"Default quota :\")))]),\n _vm._v(\" \"),\n _c(\"multiselect\", {\n staticClass: \"multiselect-vue\",\n attrs: {\n value: _vm.defaultQuota,\n options: _vm.quotaOptions,\n \"tag-placeholder\": \"create\",\n placeholder: _vm.t(\"settings\", \"Select default quota\"),\n label: \"label\",\n \"track-by\": \"id\",\n allowEmpty: false,\n taggable: true\n },\n on: { tag: _vm.validateQuota, input: _vm.setDefaultQuota }\n })\n ],\n 1\n ),\n _vm._v(\" \"),\n _c(\"div\", [\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model\",\n value: _vm.showLanguages,\n expression: \"showLanguages\"\n }\n ],\n staticClass: \"checkbox\",\n attrs: { type: \"checkbox\", id: \"showLanguages\" },\n domProps: {\n checked: Array.isArray(_vm.showLanguages)\n ? _vm._i(_vm.showLanguages, null) > -1\n : _vm.showLanguages\n },\n on: {\n change: function($event) {\n var $$a = _vm.showLanguages,\n $$el = $event.target,\n $$c = $$el.checked ? true : false\n if (Array.isArray($$a)) {\n var $$v = null,\n $$i = _vm._i($$a, $$v)\n if ($$el.checked) {\n $$i < 0 && (_vm.showLanguages = $$a.concat([$$v]))\n } else {\n $$i > -1 &&\n (_vm.showLanguages = $$a\n .slice(0, $$i)\n .concat($$a.slice($$i + 1)))\n }\n } else {\n _vm.showLanguages = $$c\n }\n }\n }\n }),\n _vm._v(\" \"),\n _c(\"label\", { attrs: { for: \"showLanguages\" } }, [\n _vm._v(_vm._s(_vm.t(\"settings\", \"Show Languages\")))\n ])\n ]),\n _vm._v(\" \"),\n _c(\"div\", [\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model\",\n value: _vm.showLastLogin,\n expression: \"showLastLogin\"\n }\n ],\n staticClass: \"checkbox\",\n attrs: { type: \"checkbox\", id: \"showLastLogin\" },\n domProps: {\n checked: Array.isArray(_vm.showLastLogin)\n ? _vm._i(_vm.showLastLogin, null) > -1\n : _vm.showLastLogin\n },\n on: {\n change: function($event) {\n var $$a = _vm.showLastLogin,\n $$el = $event.target,\n $$c = $$el.checked ? true : false\n if (Array.isArray($$a)) {\n var $$v = null,\n $$i = _vm._i($$a, $$v)\n if ($$el.checked) {\n $$i < 0 && (_vm.showLastLogin = $$a.concat([$$v]))\n } else {\n $$i > -1 &&\n (_vm.showLastLogin = $$a\n .slice(0, $$i)\n .concat($$a.slice($$i + 1)))\n }\n } else {\n _vm.showLastLogin = $$c\n }\n }\n }\n }),\n _vm._v(\" \"),\n _c(\"label\", { attrs: { for: \"showLastLogin\" } }, [\n _vm._v(_vm._s(_vm.t(\"settings\", \"Show last login\")))\n ])\n ]),\n _vm._v(\" \"),\n _c(\"div\", [\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model\",\n value: _vm.showUserBackend,\n expression: \"showUserBackend\"\n }\n ],\n staticClass: \"checkbox\",\n attrs: { type: \"checkbox\", id: \"showUserBackend\" },\n domProps: {\n checked: Array.isArray(_vm.showUserBackend)\n ? _vm._i(_vm.showUserBackend, null) > -1\n : _vm.showUserBackend\n },\n on: {\n change: function($event) {\n var $$a = _vm.showUserBackend,\n $$el = $event.target,\n $$c = $$el.checked ? true : false\n if (Array.isArray($$a)) {\n var $$v = null,\n $$i = _vm._i($$a, $$v)\n if ($$el.checked) {\n $$i < 0 && (_vm.showUserBackend = $$a.concat([$$v]))\n } else {\n $$i > -1 &&\n (_vm.showUserBackend = $$a\n .slice(0, $$i)\n .concat($$a.slice($$i + 1)))\n }\n } else {\n _vm.showUserBackend = $$c\n }\n }\n }\n }),\n _vm._v(\" \"),\n _c(\"label\", { attrs: { for: \"showUserBackend\" } }, [\n _vm._v(_vm._s(_vm.t(\"settings\", \"Show user backend\")))\n ])\n ]),\n _vm._v(\" \"),\n _c(\"div\", [\n _c(\"input\", {\n directives: [\n {\n name: \"model\",\n rawName: \"v-model\",\n value: _vm.showStoragePath,\n expression: \"showStoragePath\"\n }\n ],\n staticClass: \"checkbox\",\n attrs: { type: \"checkbox\", id: \"showStoragePath\" },\n domProps: {\n checked: Array.isArray(_vm.showStoragePath)\n ? _vm._i(_vm.showStoragePath, null) > -1\n : _vm.showStoragePath\n },\n on: {\n change: function($event) {\n var $$a = _vm.showStoragePath,\n $$el = $event.target,\n $$c = $$el.checked ? true : false\n if (Array.isArray($$a)) {\n var $$v = null,\n $$i = _vm._i($$a, $$v)\n if ($$el.checked) {\n $$i < 0 && (_vm.showStoragePath = $$a.concat([$$v]))\n } else {\n $$i > -1 &&\n (_vm.showStoragePath = $$a\n .slice(0, $$i)\n .concat($$a.slice($$i + 1)))\n }\n } else {\n _vm.showStoragePath = $$c\n }\n }\n }\n }),\n _vm._v(\" \"),\n _c(\"label\", { attrs: { for: \"showStoragePath\" } }, [\n _vm._v(_vm._s(_vm.t(\"settings\", \"Show storage path\")))\n ])\n ])\n ])\n ],\n 2\n ),\n _vm._v(\" \"),\n _c(\"user-list\", {\n attrs: {\n users: _vm.users,\n showConfig: _vm.showConfig,\n selectedGroup: _vm.selectedGroup\n }\n })\n ],\n 1\n )\n}\nvar staticRenderFns = []\nrender._withStripped = true\nexport { render, staticRenderFns }\nif (module.hot) {\n module.hot.accept()\n if (module.hot.data) {\n require(\"vue-hot-reload-api\") .rerender(\"data-v-68be103e\", { render: render, staticRenderFns: staticRenderFns })\n }\n}","var disposed = false\n/* script */\nexport * from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./Users.vue\"\nimport __vue_script__ from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./Users.vue\"\n/* template */\nimport {render as __vue_render__, staticRenderFns as __vue_static_render_fns__} from \"!!../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-68be103e\\\",\\\"hasScoped\\\":false,\\\"optionsId\\\":\\\"0\\\",\\\"buble\\\":{\\\"transforms\\\":{}}}!../../node_modules/vue-loader/lib/selector?type=template&index=0!./Users.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = null\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nimport normalizeComponent from \"!../../node_modules/vue-loader/lib/runtime/component-normalizer\"\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_render__,\n __vue_static_render_fns__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\nComponent.options.__file = \"src/views/Users.vue\"\n\n/* hot reload */\nif (module.hot) {(function () {\n var hotAPI = require(\"vue-hot-reload-api\")\n hotAPI.install(require(\"vue\"), false)\n if (!hotAPI.compatible) return\n module.hot.accept()\n if (!module.hot.data) {\n hotAPI.createRecord(\"data-v-68be103e\", Component.options)\n } else {\n hotAPI.reload(\"data-v-68be103e\", Component.options)\n }\n module.hot.dispose(function (data) {\n disposed = true\n })\n})()}\n\nexport default Component.exports\n","import Vue from 'vue';\nimport Router from 'vue-router';\nimport Users from './views/Users';\n\nVue.use(Router);\n\n/*\n * This is the list of routes where the vuejs app will\n * take over php to provide data\n * You need to forward the php routing (routes.php) to\n * /settings/main.php, where the vue-router will ensure\n * the proper route.\n * ⚠️ Routes needs to match the php routes.\n */\n\nexport default new Router({\n\tmode: 'history',\n\t// if index.php is in the url AND we got this far, then it's working:\n\t// let's keep using index.php in the url\n\tbase: OC.generateUrl(''),\n\troutes: [\n\t\t{\n\t\t\tpath: '/:index(index.php/)?settings/users',\n\t\t\tcomponent: Users,\n\t\t\tprops: true,\n\t\t\tname: 'users',\n\t\t\tchildren: [\n\t\t\t\t{\n\t\t\t\t\tpath: ':selectedGroup',\n\t\t\t\t\tname: 'group',\n\t\t\t\t\tcomponent: Users\n\t\t\t\t}\n\t\t\t]\n\t\t}\n\t]\n});\n","/**\n * vuex v3.0.1\n * (c) 2017 Evan You\n * @license MIT\n */\nvar applyMixin = function (Vue) {\n var version = Number(Vue.version.split('.')[0]);\n\n if (version >= 2) {\n Vue.mixin({ beforeCreate: vuexInit });\n } else {\n // override init and inject vuex init procedure\n // for 1.x backwards compatibility.\n var _init = Vue.prototype._init;\n Vue.prototype._init = function (options) {\n if ( options === void 0 ) options = {};\n\n options.init = options.init\n ? [vuexInit].concat(options.init)\n : vuexInit;\n _init.call(this, options);\n };\n }\n\n /**\n * Vuex init hook, injected into each instances init hooks list.\n */\n\n function vuexInit () {\n var options = this.$options;\n // store injection\n if (options.store) {\n this.$store = typeof options.store === 'function'\n ? options.store()\n : options.store;\n } else if (options.parent && options.parent.$store) {\n this.$store = options.parent.$store;\n }\n }\n};\n\nvar devtoolHook =\n typeof window !== 'undefined' &&\n window.__VUE_DEVTOOLS_GLOBAL_HOOK__;\n\nfunction devtoolPlugin (store) {\n if (!devtoolHook) { return }\n\n store._devtoolHook = devtoolHook;\n\n devtoolHook.emit('vuex:init', store);\n\n devtoolHook.on('vuex:travel-to-state', function (targetState) {\n store.replaceState(targetState);\n });\n\n store.subscribe(function (mutation, state) {\n devtoolHook.emit('vuex:mutation', mutation, state);\n });\n}\n\n/**\n * Get the first item that pass the test\n * by second argument function\n *\n * @param {Array} list\n * @param {Function} f\n * @return {*}\n */\n/**\n * Deep copy the given object considering circular structure.\n * This function caches all nested objects and its copies.\n * If it detects circular structure, use cached copy to avoid infinite loop.\n *\n * @param {*} obj\n * @param {Array} cache\n * @return {*}\n */\n\n\n/**\n * forEach for object\n */\nfunction forEachValue (obj, fn) {\n Object.keys(obj).forEach(function (key) { return fn(obj[key], key); });\n}\n\nfunction isObject (obj) {\n return obj !== null && typeof obj === 'object'\n}\n\nfunction isPromise (val) {\n return val && typeof val.then === 'function'\n}\n\nfunction assert (condition, msg) {\n if (!condition) { throw new Error((\"[vuex] \" + msg)) }\n}\n\nvar Module = function Module (rawModule, runtime) {\n this.runtime = runtime;\n this._children = Object.create(null);\n this._rawModule = rawModule;\n var rawState = rawModule.state;\n this.state = (typeof rawState === 'function' ? rawState() : rawState) || {};\n};\n\nvar prototypeAccessors$1 = { namespaced: { configurable: true } };\n\nprototypeAccessors$1.namespaced.get = function () {\n return !!this._rawModule.namespaced\n};\n\nModule.prototype.addChild = function addChild (key, module) {\n this._children[key] = module;\n};\n\nModule.prototype.removeChild = function removeChild (key) {\n delete this._children[key];\n};\n\nModule.prototype.getChild = function getChild (key) {\n return this._children[key]\n};\n\nModule.prototype.update = function update (rawModule) {\n this._rawModule.namespaced = rawModule.namespaced;\n if (rawModule.actions) {\n this._rawModule.actions = rawModule.actions;\n }\n if (rawModule.mutations) {\n this._rawModule.mutations = rawModule.mutations;\n }\n if (rawModule.getters) {\n this._rawModule.getters = rawModule.getters;\n }\n};\n\nModule.prototype.forEachChild = function forEachChild (fn) {\n forEachValue(this._children, fn);\n};\n\nModule.prototype.forEachGetter = function forEachGetter (fn) {\n if (this._rawModule.getters) {\n forEachValue(this._rawModule.getters, fn);\n }\n};\n\nModule.prototype.forEachAction = function forEachAction (fn) {\n if (this._rawModule.actions) {\n forEachValue(this._rawModule.actions, fn);\n }\n};\n\nModule.prototype.forEachMutation = function forEachMutation (fn) {\n if (this._rawModule.mutations) {\n forEachValue(this._rawModule.mutations, fn);\n }\n};\n\nObject.defineProperties( Module.prototype, prototypeAccessors$1 );\n\nvar ModuleCollection = function ModuleCollection (rawRootModule) {\n // register root module (Vuex.Store options)\n this.register([], rawRootModule, false);\n};\n\nModuleCollection.prototype.get = function get (path) {\n return path.reduce(function (module, key) {\n return module.getChild(key)\n }, this.root)\n};\n\nModuleCollection.prototype.getNamespace = function getNamespace (path) {\n var module = this.root;\n return path.reduce(function (namespace, key) {\n module = module.getChild(key);\n return namespace + (module.namespaced ? key + '/' : '')\n }, '')\n};\n\nModuleCollection.prototype.update = function update$1 (rawRootModule) {\n update([], this.root, rawRootModule);\n};\n\nModuleCollection.prototype.register = function register (path, rawModule, runtime) {\n var this$1 = this;\n if ( runtime === void 0 ) runtime = true;\n\n if (process.env.NODE_ENV !== 'production') {\n assertRawModule(path, rawModule);\n }\n\n var newModule = new Module(rawModule, runtime);\n if (path.length === 0) {\n this.root = newModule;\n } else {\n var parent = this.get(path.slice(0, -1));\n parent.addChild(path[path.length - 1], newModule);\n }\n\n // register nested modules\n if (rawModule.modules) {\n forEachValue(rawModule.modules, function (rawChildModule, key) {\n this$1.register(path.concat(key), rawChildModule, runtime);\n });\n }\n};\n\nModuleCollection.prototype.unregister = function unregister (path) {\n var parent = this.get(path.slice(0, -1));\n var key = path[path.length - 1];\n if (!parent.getChild(key).runtime) { return }\n\n parent.removeChild(key);\n};\n\nfunction update (path, targetModule, newModule) {\n if (process.env.NODE_ENV !== 'production') {\n assertRawModule(path, newModule);\n }\n\n // update target module\n targetModule.update(newModule);\n\n // update nested modules\n if (newModule.modules) {\n for (var key in newModule.modules) {\n if (!targetModule.getChild(key)) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n \"[vuex] trying to add a new module '\" + key + \"' on hot reloading, \" +\n 'manual reload is needed'\n );\n }\n return\n }\n update(\n path.concat(key),\n targetModule.getChild(key),\n newModule.modules[key]\n );\n }\n }\n}\n\nvar functionAssert = {\n assert: function (value) { return typeof value === 'function'; },\n expected: 'function'\n};\n\nvar objectAssert = {\n assert: function (value) { return typeof value === 'function' ||\n (typeof value === 'object' && typeof value.handler === 'function'); },\n expected: 'function or object with \"handler\" function'\n};\n\nvar assertTypes = {\n getters: functionAssert,\n mutations: functionAssert,\n actions: objectAssert\n};\n\nfunction assertRawModule (path, rawModule) {\n Object.keys(assertTypes).forEach(function (key) {\n if (!rawModule[key]) { return }\n\n var assertOptions = assertTypes[key];\n\n forEachValue(rawModule[key], function (value, type) {\n assert(\n assertOptions.assert(value),\n makeAssertionMessage(path, key, type, value, assertOptions.expected)\n );\n });\n });\n}\n\nfunction makeAssertionMessage (path, key, type, value, expected) {\n var buf = key + \" should be \" + expected + \" but \\\"\" + key + \".\" + type + \"\\\"\";\n if (path.length > 0) {\n buf += \" in module \\\"\" + (path.join('.')) + \"\\\"\";\n }\n buf += \" is \" + (JSON.stringify(value)) + \".\";\n return buf\n}\n\nvar Vue; // bind on install\n\nvar Store = function Store (options) {\n var this$1 = this;\n if ( options === void 0 ) options = {};\n\n // Auto install if it is not done yet and `window` has `Vue`.\n // To allow users to avoid auto-installation in some cases,\n // this code should be placed here. See #731\n if (!Vue && typeof window !== 'undefined' && window.Vue) {\n install(window.Vue);\n }\n\n if (process.env.NODE_ENV !== 'production') {\n assert(Vue, \"must call Vue.use(Vuex) before creating a store instance.\");\n assert(typeof Promise !== 'undefined', \"vuex requires a Promise polyfill in this browser.\");\n assert(this instanceof Store, \"Store must be called with the new operator.\");\n }\n\n var plugins = options.plugins; if ( plugins === void 0 ) plugins = [];\n var strict = options.strict; if ( strict === void 0 ) strict = false;\n\n var state = options.state; if ( state === void 0 ) state = {};\n if (typeof state === 'function') {\n state = state() || {};\n }\n\n // store internal state\n this._committing = false;\n this._actions = Object.create(null);\n this._actionSubscribers = [];\n this._mutations = Object.create(null);\n this._wrappedGetters = Object.create(null);\n this._modules = new ModuleCollection(options);\n this._modulesNamespaceMap = Object.create(null);\n this._subscribers = [];\n this._watcherVM = new Vue();\n\n // bind commit and dispatch to self\n var store = this;\n var ref = this;\n var dispatch = ref.dispatch;\n var commit = ref.commit;\n this.dispatch = function boundDispatch (type, payload) {\n return dispatch.call(store, type, payload)\n };\n this.commit = function boundCommit (type, payload, options) {\n return commit.call(store, type, payload, options)\n };\n\n // strict mode\n this.strict = strict;\n\n // init root module.\n // this also recursively registers all sub-modules\n // and collects all module getters inside this._wrappedGetters\n installModule(this, state, [], this._modules.root);\n\n // initialize the store vm, which is responsible for the reactivity\n // (also registers _wrappedGetters as computed properties)\n resetStoreVM(this, state);\n\n // apply plugins\n plugins.forEach(function (plugin) { return plugin(this$1); });\n\n if (Vue.config.devtools) {\n devtoolPlugin(this);\n }\n};\n\nvar prototypeAccessors = { state: { configurable: true } };\n\nprototypeAccessors.state.get = function () {\n return this._vm._data.$$state\n};\n\nprototypeAccessors.state.set = function (v) {\n if (process.env.NODE_ENV !== 'production') {\n assert(false, \"Use store.replaceState() to explicit replace store state.\");\n }\n};\n\nStore.prototype.commit = function commit (_type, _payload, _options) {\n var this$1 = this;\n\n // check object-style commit\n var ref = unifyObjectStyle(_type, _payload, _options);\n var type = ref.type;\n var payload = ref.payload;\n var options = ref.options;\n\n var mutation = { type: type, payload: payload };\n var entry = this._mutations[type];\n if (!entry) {\n if (process.env.NODE_ENV !== 'production') {\n console.error((\"[vuex] unknown mutation type: \" + type));\n }\n return\n }\n this._withCommit(function () {\n entry.forEach(function commitIterator (handler) {\n handler(payload);\n });\n });\n this._subscribers.forEach(function (sub) { return sub(mutation, this$1.state); });\n\n if (\n process.env.NODE_ENV !== 'production' &&\n options && options.silent\n ) {\n console.warn(\n \"[vuex] mutation type: \" + type + \". Silent option has been removed. \" +\n 'Use the filter functionality in the vue-devtools'\n );\n }\n};\n\nStore.prototype.dispatch = function dispatch (_type, _payload) {\n var this$1 = this;\n\n // check object-style dispatch\n var ref = unifyObjectStyle(_type, _payload);\n var type = ref.type;\n var payload = ref.payload;\n\n var action = { type: type, payload: payload };\n var entry = this._actions[type];\n if (!entry) {\n if (process.env.NODE_ENV !== 'production') {\n console.error((\"[vuex] unknown action type: \" + type));\n }\n return\n }\n\n this._actionSubscribers.forEach(function (sub) { return sub(action, this$1.state); });\n\n return entry.length > 1\n ? Promise.all(entry.map(function (handler) { return handler(payload); }))\n : entry[0](payload)\n};\n\nStore.prototype.subscribe = function subscribe (fn) {\n return genericSubscribe(fn, this._subscribers)\n};\n\nStore.prototype.subscribeAction = function subscribeAction (fn) {\n return genericSubscribe(fn, this._actionSubscribers)\n};\n\nStore.prototype.watch = function watch (getter, cb, options) {\n var this$1 = this;\n\n if (process.env.NODE_ENV !== 'production') {\n assert(typeof getter === 'function', \"store.watch only accepts a function.\");\n }\n return this._watcherVM.$watch(function () { return getter(this$1.state, this$1.getters); }, cb, options)\n};\n\nStore.prototype.replaceState = function replaceState (state) {\n var this$1 = this;\n\n this._withCommit(function () {\n this$1._vm._data.$$state = state;\n });\n};\n\nStore.prototype.registerModule = function registerModule (path, rawModule, options) {\n if ( options === void 0 ) options = {};\n\n if (typeof path === 'string') { path = [path]; }\n\n if (process.env.NODE_ENV !== 'production') {\n assert(Array.isArray(path), \"module path must be a string or an Array.\");\n assert(path.length > 0, 'cannot register the root module by using registerModule.');\n }\n\n this._modules.register(path, rawModule);\n installModule(this, this.state, path, this._modules.get(path), options.preserveState);\n // reset store to update getters...\n resetStoreVM(this, this.state);\n};\n\nStore.prototype.unregisterModule = function unregisterModule (path) {\n var this$1 = this;\n\n if (typeof path === 'string') { path = [path]; }\n\n if (process.env.NODE_ENV !== 'production') {\n assert(Array.isArray(path), \"module path must be a string or an Array.\");\n }\n\n this._modules.unregister(path);\n this._withCommit(function () {\n var parentState = getNestedState(this$1.state, path.slice(0, -1));\n Vue.delete(parentState, path[path.length - 1]);\n });\n resetStore(this);\n};\n\nStore.prototype.hotUpdate = function hotUpdate (newOptions) {\n this._modules.update(newOptions);\n resetStore(this, true);\n};\n\nStore.prototype._withCommit = function _withCommit (fn) {\n var committing = this._committing;\n this._committing = true;\n fn();\n this._committing = committing;\n};\n\nObject.defineProperties( Store.prototype, prototypeAccessors );\n\nfunction genericSubscribe (fn, subs) {\n if (subs.indexOf(fn) < 0) {\n subs.push(fn);\n }\n return function () {\n var i = subs.indexOf(fn);\n if (i > -1) {\n subs.splice(i, 1);\n }\n }\n}\n\nfunction resetStore (store, hot) {\n store._actions = Object.create(null);\n store._mutations = Object.create(null);\n store._wrappedGetters = Object.create(null);\n store._modulesNamespaceMap = Object.create(null);\n var state = store.state;\n // init all modules\n installModule(store, state, [], store._modules.root, true);\n // reset vm\n resetStoreVM(store, state, hot);\n}\n\nfunction resetStoreVM (store, state, hot) {\n var oldVm = store._vm;\n\n // bind store public getters\n store.getters = {};\n var wrappedGetters = store._wrappedGetters;\n var computed = {};\n forEachValue(wrappedGetters, function (fn, key) {\n // use computed to leverage its lazy-caching mechanism\n computed[key] = function () { return fn(store); };\n Object.defineProperty(store.getters, key, {\n get: function () { return store._vm[key]; },\n enumerable: true // for local getters\n });\n });\n\n // use a Vue instance to store the state tree\n // suppress warnings just in case the user has added\n // some funky global mixins\n var silent = Vue.config.silent;\n Vue.config.silent = true;\n store._vm = new Vue({\n data: {\n $$state: state\n },\n computed: computed\n });\n Vue.config.silent = silent;\n\n // enable strict mode for new vm\n if (store.strict) {\n enableStrictMode(store);\n }\n\n if (oldVm) {\n if (hot) {\n // dispatch changes in all subscribed watchers\n // to force getter re-evaluation for hot reloading.\n store._withCommit(function () {\n oldVm._data.$$state = null;\n });\n }\n Vue.nextTick(function () { return oldVm.$destroy(); });\n }\n}\n\nfunction installModule (store, rootState, path, module, hot) {\n var isRoot = !path.length;\n var namespace = store._modules.getNamespace(path);\n\n // register in namespace map\n if (module.namespaced) {\n store._modulesNamespaceMap[namespace] = module;\n }\n\n // set state\n if (!isRoot && !hot) {\n var parentState = getNestedState(rootState, path.slice(0, -1));\n var moduleName = path[path.length - 1];\n store._withCommit(function () {\n Vue.set(parentState, moduleName, module.state);\n });\n }\n\n var local = module.context = makeLocalContext(store, namespace, path);\n\n module.forEachMutation(function (mutation, key) {\n var namespacedType = namespace + key;\n registerMutation(store, namespacedType, mutation, local);\n });\n\n module.forEachAction(function (action, key) {\n var type = action.root ? key : namespace + key;\n var handler = action.handler || action;\n registerAction(store, type, handler, local);\n });\n\n module.forEachGetter(function (getter, key) {\n var namespacedType = namespace + key;\n registerGetter(store, namespacedType, getter, local);\n });\n\n module.forEachChild(function (child, key) {\n installModule(store, rootState, path.concat(key), child, hot);\n });\n}\n\n/**\n * make localized dispatch, commit, getters and state\n * if there is no namespace, just use root ones\n */\nfunction makeLocalContext (store, namespace, path) {\n var noNamespace = namespace === '';\n\n var local = {\n dispatch: noNamespace ? store.dispatch : function (_type, _payload, _options) {\n var args = unifyObjectStyle(_type, _payload, _options);\n var payload = args.payload;\n var options = args.options;\n var type = args.type;\n\n if (!options || !options.root) {\n type = namespace + type;\n if (process.env.NODE_ENV !== 'production' && !store._actions[type]) {\n console.error((\"[vuex] unknown local action type: \" + (args.type) + \", global type: \" + type));\n return\n }\n }\n\n return store.dispatch(type, payload)\n },\n\n commit: noNamespace ? store.commit : function (_type, _payload, _options) {\n var args = unifyObjectStyle(_type, _payload, _options);\n var payload = args.payload;\n var options = args.options;\n var type = args.type;\n\n if (!options || !options.root) {\n type = namespace + type;\n if (process.env.NODE_ENV !== 'production' && !store._mutations[type]) {\n console.error((\"[vuex] unknown local mutation type: \" + (args.type) + \", global type: \" + type));\n return\n }\n }\n\n store.commit(type, payload, options);\n }\n };\n\n // getters and state object must be gotten lazily\n // because they will be changed by vm update\n Object.defineProperties(local, {\n getters: {\n get: noNamespace\n ? function () { return store.getters; }\n : function () { return makeLocalGetters(store, namespace); }\n },\n state: {\n get: function () { return getNestedState(store.state, path); }\n }\n });\n\n return local\n}\n\nfunction makeLocalGetters (store, namespace) {\n var gettersProxy = {};\n\n var splitPos = namespace.length;\n Object.keys(store.getters).forEach(function (type) {\n // skip if the target getter is not match this namespace\n if (type.slice(0, splitPos) !== namespace) { return }\n\n // extract local getter type\n var localType = type.slice(splitPos);\n\n // Add a port to the getters proxy.\n // Define as getter property because\n // we do not want to evaluate the getters in this time.\n Object.defineProperty(gettersProxy, localType, {\n get: function () { return store.getters[type]; },\n enumerable: true\n });\n });\n\n return gettersProxy\n}\n\nfunction registerMutation (store, type, handler, local) {\n var entry = store._mutations[type] || (store._mutations[type] = []);\n entry.push(function wrappedMutationHandler (payload) {\n handler.call(store, local.state, payload);\n });\n}\n\nfunction registerAction (store, type, handler, local) {\n var entry = store._actions[type] || (store._actions[type] = []);\n entry.push(function wrappedActionHandler (payload, cb) {\n var res = handler.call(store, {\n dispatch: local.dispatch,\n commit: local.commit,\n getters: local.getters,\n state: local.state,\n rootGetters: store.getters,\n rootState: store.state\n }, payload, cb);\n if (!isPromise(res)) {\n res = Promise.resolve(res);\n }\n if (store._devtoolHook) {\n return res.catch(function (err) {\n store._devtoolHook.emit('vuex:error', err);\n throw err\n })\n } else {\n return res\n }\n });\n}\n\nfunction registerGetter (store, type, rawGetter, local) {\n if (store._wrappedGetters[type]) {\n if (process.env.NODE_ENV !== 'production') {\n console.error((\"[vuex] duplicate getter key: \" + type));\n }\n return\n }\n store._wrappedGetters[type] = function wrappedGetter (store) {\n return rawGetter(\n local.state, // local state\n local.getters, // local getters\n store.state, // root state\n store.getters // root getters\n )\n };\n}\n\nfunction enableStrictMode (store) {\n store._vm.$watch(function () { return this._data.$$state }, function () {\n if (process.env.NODE_ENV !== 'production') {\n assert(store._committing, \"Do not mutate vuex store state outside mutation handlers.\");\n }\n }, { deep: true, sync: true });\n}\n\nfunction getNestedState (state, path) {\n return path.length\n ? path.reduce(function (state, key) { return state[key]; }, state)\n : state\n}\n\nfunction unifyObjectStyle (type, payload, options) {\n if (isObject(type) && type.type) {\n options = payload;\n payload = type;\n type = type.type;\n }\n\n if (process.env.NODE_ENV !== 'production') {\n assert(typeof type === 'string', (\"Expects string as the type, but found \" + (typeof type) + \".\"));\n }\n\n return { type: type, payload: payload, options: options }\n}\n\nfunction install (_Vue) {\n if (Vue && _Vue === Vue) {\n if (process.env.NODE_ENV !== 'production') {\n console.error(\n '[vuex] already installed. Vue.use(Vuex) should be called only once.'\n );\n }\n return\n }\n Vue = _Vue;\n applyMixin(Vue);\n}\n\nvar mapState = normalizeNamespace(function (namespace, states) {\n var res = {};\n normalizeMap(states).forEach(function (ref) {\n var key = ref.key;\n var val = ref.val;\n\n res[key] = function mappedState () {\n var state = this.$store.state;\n var getters = this.$store.getters;\n if (namespace) {\n var module = getModuleByNamespace(this.$store, 'mapState', namespace);\n if (!module) {\n return\n }\n state = module.context.state;\n getters = module.context.getters;\n }\n return typeof val === 'function'\n ? val.call(this, state, getters)\n : state[val]\n };\n // mark vuex getter for devtools\n res[key].vuex = true;\n });\n return res\n});\n\nvar mapMutations = normalizeNamespace(function (namespace, mutations) {\n var res = {};\n normalizeMap(mutations).forEach(function (ref) {\n var key = ref.key;\n var val = ref.val;\n\n res[key] = function mappedMutation () {\n var args = [], len = arguments.length;\n while ( len-- ) args[ len ] = arguments[ len ];\n\n var commit = this.$store.commit;\n if (namespace) {\n var module = getModuleByNamespace(this.$store, 'mapMutations', namespace);\n if (!module) {\n return\n }\n commit = module.context.commit;\n }\n return typeof val === 'function'\n ? val.apply(this, [commit].concat(args))\n : commit.apply(this.$store, [val].concat(args))\n };\n });\n return res\n});\n\nvar mapGetters = normalizeNamespace(function (namespace, getters) {\n var res = {};\n normalizeMap(getters).forEach(function (ref) {\n var key = ref.key;\n var val = ref.val;\n\n val = namespace + val;\n res[key] = function mappedGetter () {\n if (namespace && !getModuleByNamespace(this.$store, 'mapGetters', namespace)) {\n return\n }\n if (process.env.NODE_ENV !== 'production' && !(val in this.$store.getters)) {\n console.error((\"[vuex] unknown getter: \" + val));\n return\n }\n return this.$store.getters[val]\n };\n // mark vuex getter for devtools\n res[key].vuex = true;\n });\n return res\n});\n\nvar mapActions = normalizeNamespace(function (namespace, actions) {\n var res = {};\n normalizeMap(actions).forEach(function (ref) {\n var key = ref.key;\n var val = ref.val;\n\n res[key] = function mappedAction () {\n var args = [], len = arguments.length;\n while ( len-- ) args[ len ] = arguments[ len ];\n\n var dispatch = this.$store.dispatch;\n if (namespace) {\n var module = getModuleByNamespace(this.$store, 'mapActions', namespace);\n if (!module) {\n return\n }\n dispatch = module.context.dispatch;\n }\n return typeof val === 'function'\n ? val.apply(this, [dispatch].concat(args))\n : dispatch.apply(this.$store, [val].concat(args))\n };\n });\n return res\n});\n\nvar createNamespacedHelpers = function (namespace) { return ({\n mapState: mapState.bind(null, namespace),\n mapGetters: mapGetters.bind(null, namespace),\n mapMutations: mapMutations.bind(null, namespace),\n mapActions: mapActions.bind(null, namespace)\n}); };\n\nfunction normalizeMap (map) {\n return Array.isArray(map)\n ? map.map(function (key) { return ({ key: key, val: key }); })\n : Object.keys(map).map(function (key) { return ({ key: key, val: map[key] }); })\n}\n\nfunction normalizeNamespace (fn) {\n return function (namespace, map) {\n if (typeof namespace !== 'string') {\n map = namespace;\n namespace = '';\n } else if (namespace.charAt(namespace.length - 1) !== '/') {\n namespace += '/';\n }\n return fn(namespace, map)\n }\n}\n\nfunction getModuleByNamespace (store, helper, namespace) {\n var module = store._modulesNamespaceMap[namespace];\n if (process.env.NODE_ENV !== 'production' && !module) {\n console.error((\"[vuex] module namespace not found in \" + helper + \"(): \" + namespace));\n }\n return module\n}\n\nvar index_esm = {\n Store: Store,\n install: install,\n version: '3.0.1',\n mapState: mapState,\n mapMutations: mapMutations,\n mapGetters: mapGetters,\n mapActions: mapActions,\n createNamespacedHelpers: createNamespacedHelpers\n};\n\nexport { Store, install, mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers };\nexport default index_esm;\n","import api from './api';\n\nconst orderGroups = function(groups, orderBy) {\n\t/* const SORT_USERCOUNT = 1;\n\t * const SORT_GROUPNAME = 2;\n\t * https://github.com/nextcloud/server/blob/208e38e84e1a07a49699aa90dc5b7272d24489f0/lib/private/Group/MetaData.php#L34\n\t */\n\tif (orderBy === 1) {\n\t\treturn groups.sort((a, b) => a.usercount < b.usercount);\n\t} else {\n\t\treturn groups.sort((a, b) => a.name.localeCompare(b.name));\n\t}\n};\n\nconst state = {\n\tusers: [],\n\tgroups: [],\n\torderBy: 1,\n\tminPasswordLength: 0,\n\tusersOffset: 0,\n\tusersLimit: 25,\n\tuserCount: 0\n};\n\nconst mutations = {\n\tappendUsers(state, usersObj) {\n\t\t// convert obj to array\n\t\tlet users = state.users.concat(Object.keys(usersObj).map(userid => usersObj[userid]));\n\t\tstate.usersOffset += state.usersLimit;\n\t\tstate.users = users;\n\t},\n\tsetPasswordPolicyMinLength(state, length) {\n\t\tstate.minPasswordLength = length!=='' ? length : 0;\n\t},\n\tinitGroups(state, {groups, orderBy, userCount}) {\n\t\tstate.groups = groups;\n\t\tstate.orderBy = orderBy;\n\t\tstate.userCount = userCount;\n\t\tstate.groups = orderGroups(state.groups, state.orderBy);\n\t},\n\taddGroup(state, gid) {\n\t\ttry {\n\t\t\tstate.groups.push({\n\t\t\t\tid: gid,\n\t\t\t\tname: gid,\n\t\t\t\tusercount: 0 // user will be added after the creation\n\t\t\t});\n\t\t\tstate.groups = orderGroups(state.groups, state.orderBy);\n\t\t} catch (e) {\n\t\t\tconsole.log('Can\\'t create group', e);\n\t\t}\n\t},\n\tremoveGroup(state, gid) {\n\t\tlet groupIndex = state.groups.findIndex(groupSearch => groupSearch.id == gid);\n\t\tif (groupIndex >= 0) {\n\t\t\tstate.groups.splice(groupIndex, 1);\n\t\t}\n\t},\n\taddUserGroup(state, { userid, gid }) {\n\t\tlet group = state.groups.find(groupSearch => groupSearch.id == gid);\n\t\tif (group) {\n\t\t\tgroup.usercount++; // increase count\n\t\t}\n\t\tlet groups = state.users.find(user => user.id == userid).groups;\n\t\tgroups.push(gid);\n\t\tstate.groups = orderGroups(state.groups, state.orderBy);\n\t},\n\tremoveUserGroup(state, { userid, gid }) {\n\t\tlet group = state.groups.find(groupSearch => groupSearch.id == gid);\n\t\tif (group) {\n\t\t\tgroup.usercount--; // lower count\n\t\t}\n\t\tlet groups = state.users.find(user => user.id == userid).groups;\n\t\tgroups.splice(groups.indexOf(gid),1);\n\t\tstate.groups = orderGroups(state.groups, state.orderBy);\n\t},\n\taddUserSubAdmin(state, { userid, gid }) {\n\t\tlet groups = state.users.find(user => user.id == userid).subadmin;\n\t\tgroups.push(gid);\n\t},\n\tremoveUserSubAdmin(state, { userid, gid }) {\n\t\tlet groups = state.users.find(user => user.id == userid).subadmin;\n\t\tgroups.splice(groups.indexOf(gid),1);\n\t},\n\tdeleteUser(state, userid) {\n\t\tlet userIndex = state.users.findIndex(user => user.id == userid);\n\t\tstate.users.splice(userIndex, 1);\n\t},\n\taddUserData(state, response) {\n\t\tstate.users.push(response.data.ocs.data);\n\t},\n\tenableDisableUser(state, { userid, enabled }) {\n\t\tstate.users.find(user => user.id == userid).enabled = enabled;\n\t\t// increment or not\n\t\tstate.groups.find(group => group.id == 'disabled').usercount += enabled ? -1 : 1;\n\t\tstate.userCount += enabled ? 1 : -1;\n\t\tconsole.log(enabled);\n\t},\n\tsetUserData(state, { userid, key, value }) {\n\t\tif (key === 'quota') {\n\t\t\tlet humanValue = OC.Util.computerFileSize(value);\n\t\t\tstate.users.find(user => user.id == userid)[key][key] = humanValue?humanValue:value;\n\t\t} else {\n\t\t\tstate.users.find(user => user.id == userid)[key] = value;\n\t\t}\n\t},\n\n\t/**\n\t * Reset users list\n\t */\n\tresetUsers(state) {\n\t\tstate.users = [];\n\t\tstate.usersOffset = 0;\n\t}\n};\n\nconst getters = {\n\tgetUsers(state) {\n\t\treturn state.users;\n\t},\n\tgetGroups(state) {\n\t\treturn state.groups;\n\t},\n\tgetPasswordPolicyMinLength(state) {\n\t\treturn state.minPasswordLength;\n\t},\n\tgetUsersOffset(state) {\n\t\treturn state.usersOffset;\n\t},\n\tgetUsersLimit(state) {\n\t\treturn state.usersLimit;\n\t},\n\tgetUserCount(state) {\n\t\treturn state.userCount;\n\t}\n};\n\nconst actions = {\n\n\t/**\n\t * Get all users with full details\n\t * \n\t * @param {Object} context\n\t * @param {Object} options\n\t * @param {int} options.offset List offset to request\n\t * @param {int} options.limit List number to return from offset\n\t * @param {string} options.search Search amongst users\n\t * @param {string} options.group Get users from group\n\t * @returns {Promise}\n\t */\n\tgetUsers(context, { offset, limit, search, group }) {\n\t\tsearch = typeof search === 'string' ? search : '';\n\t\tgroup = typeof group === 'string' ? group : '';\n\t\tif (group !== '') {\n\t\t\treturn api.get(OC.linkToOCS(`cloud/groups/${group}/users/details?offset=${offset}&limit=${limit}&search=${search}`, 2))\n\t\t\t.then((response) => {\n\t\t\t\tif (Object.keys(response.data.ocs.data.users).length > 0) {\n\t\t\t\t\tcontext.commit('appendUsers', response.data.ocs.data.users);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t})\n\t\t\t.catch((error) => context.commit('API_FAILURE', error));\n\t\t}\n\n\t\treturn api.get(OC.linkToOCS(`cloud/users/details?offset=${offset}&limit=${limit}&search=${search}`, 2))\n\t\t\t.then((response) => {\n\t\t\t\tif (Object.keys(response.data.ocs.data.users).length > 0) {\n\t\t\t\t\tcontext.commit('appendUsers', response.data.ocs.data.users);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t})\n\t\t\t.catch((error) => context.commit('API_FAILURE', error));\n\t},\n\n\t/**\n\t * Get all users with full details\n\t * \n\t * @param {Object} context\n\t * @param {Object} options\n\t * @param {int} options.offset List offset to request\n\t * @param {int} options.limit List number to return from offset\n\t * @returns {Promise}\n\t */\n\tgetUsersFromList(context, { offset, limit, search }) {\n\t\tsearch = typeof search === 'string' ? search : '';\n\t\treturn api.get(OC.linkToOCS(`cloud/users/details?offset=${offset}&limit=${limit}&search=${search}`, 2))\n\t\t\t.then((response) => {\n\t\t\t\tif (Object.keys(response.data.ocs.data.users).length > 0) {\n\t\t\t\t\tcontext.commit('appendUsers', response.data.ocs.data.users);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t})\n\t\t\t.catch((error) => context.commit('API_FAILURE', error));\n\t},\n\n\t/**\n\t * Get all users with full details from a groupid\n\t * \n\t * @param {Object} context\n\t * @param {Object} options\n\t * @param {int} options.offset List offset to request\n\t * @param {int} options.limit List number to return from offset\n\t * @returns {Promise}\n\t */\n\tgetUsersFromGroup(context, { groupid, offset, limit }) {\n\t\treturn api.get(OC.linkToOCS(`cloud/users/${groupid}/details?offset=${offset}&limit=${limit}`, 2))\n\t\t\t.then((response) => context.commit('getUsersFromList', response.data.ocs.data.users))\n\t\t\t.catch((error) => context.commit('API_FAILURE', error));\n\t},\n\t\n\n\tgetPasswordPolicyMinLength(context) {\n\t\tif(oc_capabilities.password_policy && oc_capabilities.password_policy.minLength) {\n\t\t\tcontext.commit('setPasswordPolicyMinLength', oc_capabilities.password_policy.minLength);\n\t\t\treturn oc_capabilities.password_policy.minLength;\n\t\t}\n\t\treturn false;\n\t},\n\n\t/**\n\t * Add group\n\t * \n\t * @param {Object} context\n\t * @param {string} gid Group id\n\t * @returns {Promise}\n\t */\n\taddGroup(context, gid) {\n\t\treturn api.requireAdmin().then((response) => {\n\t\t\treturn api.post(OC.linkToOCS(`cloud/groups`, 2), {groupid: gid})\n\t\t\t\t.then((response) => context.commit('addGroup', gid))\n\t\t\t\t.catch((error) => {throw error;});\n\t\t}).catch((error) => context.commit('API_FAILURE', { userid, error }));\n\t},\n\n\t/**\n\t * Remove group\n\t * \n\t * @param {Object} context\n\t * @param {string} gid Group id\n\t * @returns {Promise}\n\t */\n\tremoveGroup(context, gid) {\n\t\treturn api.requireAdmin().then((response) => {\n\t\t\treturn api.delete(OC.linkToOCS(`cloud/groups/${gid}`, 2))\n\t\t\t\t.then((response) => context.commit('removeGroup', gid))\n\t\t\t\t.catch((error) => {throw error;});\n\t\t}).catch((error) => context.commit('API_FAILURE', { gid, error }));\n\t},\n\n\t/**\n\t * Add user to group\n\t * \n\t * @param {Object} context\n\t * @param {Object} options\n\t * @param {string} options.userid User id\n\t * @param {string} options.gid Group id\n\t * @returns {Promise}\n\t */\n\taddUserGroup(context, { userid, gid }) {\n\t\treturn api.requireAdmin().then((response) => {\n\t\t\treturn api.post(OC.linkToOCS(`cloud/users/${userid}/groups`, 2), { groupid: gid })\n\t\t\t\t.then((response) => context.commit('addUserGroup', { userid, gid }))\n\t\t\t\t.catch((error) => {throw error;});\n\t\t}).catch((error) => context.commit('API_FAILURE', { userid, error }));\n\t},\n\n\t/**\n\t * Remove user from group\n\t * \n\t * @param {Object} context\n\t * @param {Object} options\n\t * @param {string} options.userid User id\n\t * @param {string} options.gid Group id\n\t * @returns {Promise}\n\t */\n\tremoveUserGroup(context, { userid, gid }) {\n\t\treturn api.requireAdmin().then((response) => {\n\t\t\treturn api.delete(OC.linkToOCS(`cloud/users/${userid}/groups`, 2), { groupid: gid })\n\t\t\t\t.then((response) => context.commit('removeUserGroup', { userid, gid }))\n\t\t\t\t.catch((error) => {throw error;});\n\t\t}).catch((error) => context.commit('API_FAILURE', { userid, error }));\n\t},\n\n\t/**\n\t * Add user to group admin\n\t * \n\t * @param {Object} context\n\t * @param {Object} options\n\t * @param {string} options.userid User id\n\t * @param {string} options.gid Group id\n\t * @returns {Promise}\n\t */\n\taddUserSubAdmin(context, { userid, gid }) {\n\t\treturn api.requireAdmin().then((response) => {\n\t\t\treturn api.post(OC.linkToOCS(`cloud/users/${userid}/subadmins`, 2), { groupid: gid })\n\t\t\t\t.then((response) => context.commit('addUserSubAdmin', { userid, gid }))\n\t\t\t\t.catch((error) => {throw error;});\n\t\t}).catch((error) => context.commit('API_FAILURE', { userid, error }));\n\t},\n\n\t/**\n\t * Remove user from group admin\n\t * \n\t * @param {Object} context\n\t * @param {Object} options\n\t * @param {string} options.userid User id\n\t * @param {string} options.gid Group id\n\t * @returns {Promise}\n\t */\n\tremoveUserSubAdmin(context, { userid, gid }) {\n\t\treturn api.requireAdmin().then((response) => {\n\t\t\treturn api.delete(OC.linkToOCS(`cloud/users/${userid}/subadmins`, 2), { groupid: gid })\n\t\t\t\t.then((response) => context.commit('removeUserSubAdmin', { userid, gid }))\n\t\t\t\t.catch((error) => {throw error;});\n\t\t}).catch((error) => context.commit('API_FAILURE', { userid, error }));\n\t},\n\n\t/**\n\t * Delete a user\n\t * \n\t * @param {Object} context\n\t * @param {string} userid User id \n\t * @returns {Promise}\n\t */\n\tdeleteUser(context, { userid }) {\n\t\treturn api.requireAdmin().then((response) => {\n\t\t\treturn api.delete(OC.linkToOCS(`cloud/users/${userid}`, 2))\n\t\t\t\t.then((response) => context.commit('deleteUser', userid))\n\t\t\t\t.catch((error) => {throw error;});\n\t\t}).catch((error) => context.commit('API_FAILURE', { userid, error }));\n\t},\n\n\t/**\n\t * Add a user\n\t * \n\t * @param {Object} context\n\t * @param {Object} options\n\t * @param {string} options.userid User id\n\t * @param {string} options.password User password \n\t * @param {string} options.email User email\n\t * @param {string} options.groups User groups\n\t * @param {string} options.subadmin User subadmin groups\n\t * @param {string} options.quota User email\n\t * @returns {Promise}\n\t */\n\taddUser({commit, dispatch}, { userid, password, email, groups, subadmin, quota, language }) {\n\t\treturn api.requireAdmin().then((response) => {\n\t\t\treturn api.post(OC.linkToOCS(`cloud/users`, 2), { userid, password, email, groups, subadmin, quota, language })\n\t\t\t\t.then((response) => dispatch('addUserData', userid))\n\t\t\t\t.catch((error) => {throw error;});\n\t\t}).catch((error) => commit('API_FAILURE', { userid, error }));\n\t},\n\n\t/**\n\t * Get user data and commit addition\n\t * \n\t * @param {Object} context\n\t * @param {string} userid User id \n\t * @returns {Promise}\n\t */\n\taddUserData(context, userid) {\n\t\treturn api.requireAdmin().then((response) => {\n\t\t\treturn api.get(OC.linkToOCS(`cloud/users/${userid}`, 2))\n\t\t\t\t.then((response) => context.commit('addUserData', response))\n\t\t\t\t.catch((error) => {throw error;});\n\t\t}).catch((error) => context.commit('API_FAILURE', { userid, error }));\n\t},\n\n\t/** Enable or disable user \n\t * \n\t * @param {Object} context\n\t * @param {Object} options\n\t * @param {string} options.userid User id\n\t * @param {boolean} options.enabled User enablement status\n\t * @returns {Promise}\n\t */\n\tenableDisableUser(context, { userid, enabled = true }) {\n\t\tlet userStatus = enabled ? 'enable' : 'disable';\n\t\treturn api.requireAdmin().then((response) => {\n\t\t\treturn api.put(OC.linkToOCS(`cloud/users/${userid}/${userStatus}`, 2))\n\t\t\t\t.then((response) => context.commit('enableDisableUser', { userid, enabled }))\n\t\t\t\t.catch((error) => {throw error;});\n\t\t}).catch((error) => context.commit('API_FAILURE', { userid, error }));\n\t},\n\n\t/**\n\t * Edit user data\n\t * \n\t * @param {Object} context \n\t * @param {Object} options\n\t * @param {string} options.userid User id\n\t * @param {string} options.key User field to edit\n\t * @param {string} options.value Value of the change\n\t * @returns {Promise}\n\t */\n\tsetUserData(context, { userid, key, value }) {\n\t\tlet allowedEmpty = ['email', 'displayname'];\n\t\tif (['email', 'language', 'quota', 'displayname', 'password'].indexOf(key) !== -1) {\n\t\t\t// We allow empty email or displayname\n\t\t\tif (typeof value === 'string' &&\n\t\t\t\t(\n\t\t\t\t\t(allowedEmpty.indexOf(key) === -1 && value.length > 0) ||\n\t\t\t\t\tallowedEmpty.indexOf(key) !== -1\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\treturn api.requireAdmin().then((response) => {\n\t\t\t\t\treturn api.put(OC.linkToOCS(`cloud/users/${userid}`, 2), { key: key, value: value })\n\t\t\t\t\t\t.then((response) => context.commit('setUserData', { userid, key, value }))\n\t\t\t\t\t\t.catch((error) => {throw error;});\n\t\t\t\t}).catch((error) => context.commit('API_FAILURE', { userid, error }));\n\t\t\t}\n\t\t}\n\t\treturn Promise.reject(new Error('Invalid request data'));\n\t}\n};\n\nexport default { state, mutations, getters, actions };","import api from './api';\n\nconst state = {\n\tserverData: {}\n};\nconst mutations = {\n\tsetServerData(state, data) {\n\t\tstate.serverData = data;\n\t}\n};\nconst getters = {\n\tgetServerData(state) {\n\t\treturn state.serverData;\n\t}\n};\nconst actions = {};\n\nexport default {state, mutations, getters, actions};\n","import api from './api';\n\nconst state = {};\nconst mutations = {};\nconst getters = {};\nconst actions = {\n\t/**\n * Set application config in database\n * \n\t * @param {Object} context\n * @param {Object} options\n\t * @param {string} options.app Application name\n\t * @param {boolean} options.key Config key\n\t * @param {boolean} options.value Value to set\n\t * @returns{Promise}\n\t */\n\tsetAppConfig(context, {app, key, value}) {\n\t\treturn api.requireAdmin().then((response) => {\n\t\t\treturn api.post(OC.linkToOCS(`apps/provisioning_api/api/v1/config/apps/${app}/${key}`, 2), {value: value})\n\t\t\t\t.catch((error) => {throw error;});\n\t\t}).catch((error) => context.commit('API_FAILURE', { app, key, value, error }));;\n }\n};\n\nexport default {state, mutations, getters, actions};\n","import Vue from 'vue';\nimport Vuex from 'vuex';\nimport users from './users';\nimport settings from './settings';\nimport oc from './oc';\n\nVue.use(Vuex)\n\nconst debug = process.env.NODE_ENV !== 'production';\n\nconst mutations = {\n\tAPI_FAILURE(state, error) {\n\t\tlet message = error.error.response.data.ocs.meta.message;\n\t\tOC.Notification.showHtml(t('settings','An error occured during the request. Unable to proceed.')+'
'+message, {timeout: 7});\n\t\t// throw to raise exception of the promise and allow a `.then` in the Vue methods\n\t\tconsole.log(state, error);\n\t\tthrow new Error(error);\n\t}\n};\n\nexport default new Vuex.Store({\n\tmodules: {\n\t\tusers,\n\t\tsettings,\n\t\toc\n\t},\n\tstrict: debug,\n\n\tmutations\n});\n","import Vue from 'vue';\nimport { sync } from 'vuex-router-sync';\nimport App from './App.vue';\nimport router from './router';\nimport store from './store';\nrequire(\"babel-polyfill\");\n\n\nsync(store, router);\n\n// bind to window\nVue.prototype.t = t;\nVue.prototype.OC = OC;\nVue.prototype.oc_userconfig = oc_userconfig;\n\nconst app = new Vue({\n\trouter,\n\tstore,\n\trender: h => h(App)\n}).$mount('#content');\n\nexport { app, router, store };","/**\n * Copyright (c) 2014-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n!(function(global) {\n \"use strict\";\n\n var Op = Object.prototype;\n var hasOwn = Op.hasOwnProperty;\n var undefined; // More compressible than void 0.\n var $Symbol = typeof Symbol === \"function\" ? Symbol : {};\n var iteratorSymbol = $Symbol.iterator || \"@@iterator\";\n var asyncIteratorSymbol = $Symbol.asyncIterator || \"@@asyncIterator\";\n var toStringTagSymbol = $Symbol.toStringTag || \"@@toStringTag\";\n\n var inModule = typeof module === \"object\";\n var runtime = global.regeneratorRuntime;\n if (runtime) {\n if (inModule) {\n // If regeneratorRuntime is defined globally and we're in a module,\n // make the exports object identical to regeneratorRuntime.\n module.exports = runtime;\n }\n // Don't bother evaluating the rest of this file if the runtime was\n // already defined globally.\n return;\n }\n\n // Define the runtime globally (as expected by generated code) as either\n // module.exports (if we're in a module) or a new, empty object.\n runtime = global.regeneratorRuntime = inModule ? module.exports : {};\n\n function wrap(innerFn, outerFn, self, tryLocsList) {\n // If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator.\n var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator;\n var generator = Object.create(protoGenerator.prototype);\n var context = new Context(tryLocsList || []);\n\n // The ._invoke method unifies the implementations of the .next,\n // .throw, and .return methods.\n generator._invoke = makeInvokeMethod(innerFn, self, context);\n\n return generator;\n }\n runtime.wrap = wrap;\n\n // Try/catch helper to minimize deoptimizations. Returns a completion\n // record like context.tryEntries[i].completion. This interface could\n // have been (and was previously) designed to take a closure to be\n // invoked without arguments, but in all the cases we care about we\n // already have an existing method we want to call, so there's no need\n // to create a new function object. We can even get away with assuming\n // the method takes exactly one argument, since that happens to be true\n // in every case, so we don't have to touch the arguments object. The\n // only additional allocation required is the completion record, which\n // has a stable shape and so hopefully should be cheap to allocate.\n function tryCatch(fn, obj, arg) {\n try {\n return { type: \"normal\", arg: fn.call(obj, arg) };\n } catch (err) {\n return { type: \"throw\", arg: err };\n }\n }\n\n var GenStateSuspendedStart = \"suspendedStart\";\n var GenStateSuspendedYield = \"suspendedYield\";\n var GenStateExecuting = \"executing\";\n var GenStateCompleted = \"completed\";\n\n // Returning this object from the innerFn has the same effect as\n // breaking out of the dispatch switch statement.\n var ContinueSentinel = {};\n\n // Dummy constructor functions that we use as the .constructor and\n // .constructor.prototype properties for functions that return Generator\n // objects. For full spec compliance, you may wish to configure your\n // minifier not to mangle the names of these two functions.\n function Generator() {}\n function GeneratorFunction() {}\n function GeneratorFunctionPrototype() {}\n\n // This is a polyfill for %IteratorPrototype% for environments that\n // don't natively support it.\n var IteratorPrototype = {};\n IteratorPrototype[iteratorSymbol] = function () {\n return this;\n };\n\n var getProto = Object.getPrototypeOf;\n var NativeIteratorPrototype = getProto && getProto(getProto(values([])));\n if (NativeIteratorPrototype &&\n NativeIteratorPrototype !== Op &&\n hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) {\n // This environment has a native %IteratorPrototype%; use it instead\n // of the polyfill.\n IteratorPrototype = NativeIteratorPrototype;\n }\n\n var Gp = GeneratorFunctionPrototype.prototype =\n Generator.prototype = Object.create(IteratorPrototype);\n GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype;\n GeneratorFunctionPrototype.constructor = GeneratorFunction;\n GeneratorFunctionPrototype[toStringTagSymbol] =\n GeneratorFunction.displayName = \"GeneratorFunction\";\n\n // Helper for defining the .next, .throw, and .return methods of the\n // Iterator interface in terms of a single ._invoke method.\n function defineIteratorMethods(prototype) {\n [\"next\", \"throw\", \"return\"].forEach(function(method) {\n prototype[method] = function(arg) {\n return this._invoke(method, arg);\n };\n });\n }\n\n runtime.isGeneratorFunction = function(genFun) {\n var ctor = typeof genFun === \"function\" && genFun.constructor;\n return ctor\n ? ctor === GeneratorFunction ||\n // For the native GeneratorFunction constructor, the best we can\n // do is to check its .name property.\n (ctor.displayName || ctor.name) === \"GeneratorFunction\"\n : false;\n };\n\n runtime.mark = function(genFun) {\n if (Object.setPrototypeOf) {\n Object.setPrototypeOf(genFun, GeneratorFunctionPrototype);\n } else {\n genFun.__proto__ = GeneratorFunctionPrototype;\n if (!(toStringTagSymbol in genFun)) {\n genFun[toStringTagSymbol] = \"GeneratorFunction\";\n }\n }\n genFun.prototype = Object.create(Gp);\n return genFun;\n };\n\n // Within the body of any async function, `await x` is transformed to\n // `yield regeneratorRuntime.awrap(x)`, so that the runtime can test\n // `hasOwn.call(value, \"__await\")` to determine if the yielded value is\n // meant to be awaited.\n runtime.awrap = function(arg) {\n return { __await: arg };\n };\n\n function AsyncIterator(generator) {\n function invoke(method, arg, resolve, reject) {\n var record = tryCatch(generator[method], generator, arg);\n if (record.type === \"throw\") {\n reject(record.arg);\n } else {\n var result = record.arg;\n var value = result.value;\n if (value &&\n typeof value === \"object\" &&\n hasOwn.call(value, \"__await\")) {\n return Promise.resolve(value.__await).then(function(value) {\n invoke(\"next\", value, resolve, reject);\n }, function(err) {\n invoke(\"throw\", err, resolve, reject);\n });\n }\n\n return Promise.resolve(value).then(function(unwrapped) {\n // When a yielded Promise is resolved, its final value becomes\n // the .value of the Promise<{value,done}> result for the\n // current iteration. If the Promise is rejected, however, the\n // result for this iteration will be rejected with the same\n // reason. Note that rejections of yielded Promises are not\n // thrown back into the generator function, as is the case\n // when an awaited Promise is rejected. This difference in\n // behavior between yield and await is important, because it\n // allows the consumer to decide what to do with the yielded\n // rejection (swallow it and continue, manually .throw it back\n // into the generator, abandon iteration, whatever). With\n // await, by contrast, there is no opportunity to examine the\n // rejection reason outside the generator function, so the\n // only option is to throw it from the await expression, and\n // let the generator function handle the exception.\n result.value = unwrapped;\n resolve(result);\n }, reject);\n }\n }\n\n var previousPromise;\n\n function enqueue(method, arg) {\n function callInvokeWithMethodAndArg() {\n return new Promise(function(resolve, reject) {\n invoke(method, arg, resolve, reject);\n });\n }\n\n return previousPromise =\n // If enqueue has been called before, then we want to wait until\n // all previous Promises have been resolved before calling invoke,\n // so that results are always delivered in the correct order. If\n // enqueue has not been called before, then it is important to\n // call invoke immediately, without waiting on a callback to fire,\n // so that the async generator function has the opportunity to do\n // any necessary setup in a predictable way. This predictability\n // is why the Promise constructor synchronously invokes its\n // executor callback, and why async functions synchronously\n // execute code before the first await. Since we implement simple\n // async functions in terms of async generators, it is especially\n // important to get this right, even though it requires care.\n previousPromise ? previousPromise.then(\n callInvokeWithMethodAndArg,\n // Avoid propagating failures to Promises returned by later\n // invocations of the iterator.\n callInvokeWithMethodAndArg\n ) : callInvokeWithMethodAndArg();\n }\n\n // Define the unified helper method that is used to implement .next,\n // .throw, and .return (see defineIteratorMethods).\n this._invoke = enqueue;\n }\n\n defineIteratorMethods(AsyncIterator.prototype);\n AsyncIterator.prototype[asyncIteratorSymbol] = function () {\n return this;\n };\n runtime.AsyncIterator = AsyncIterator;\n\n // Note that simple async functions are implemented on top of\n // AsyncIterator objects; they just return a Promise for the value of\n // the final result produced by the iterator.\n runtime.async = function(innerFn, outerFn, self, tryLocsList) {\n var iter = new AsyncIterator(\n wrap(innerFn, outerFn, self, tryLocsList)\n );\n\n return runtime.isGeneratorFunction(outerFn)\n ? iter // If outerFn is a generator, return the full iterator.\n : iter.next().then(function(result) {\n return result.done ? result.value : iter.next();\n });\n };\n\n function makeInvokeMethod(innerFn, self, context) {\n var state = GenStateSuspendedStart;\n\n return function invoke(method, arg) {\n if (state === GenStateExecuting) {\n throw new Error(\"Generator is already running\");\n }\n\n if (state === GenStateCompleted) {\n if (method === \"throw\") {\n throw arg;\n }\n\n // Be forgiving, per 25.3.3.3.3 of the spec:\n // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume\n return doneResult();\n }\n\n context.method = method;\n context.arg = arg;\n\n while (true) {\n var delegate = context.delegate;\n if (delegate) {\n var delegateResult = maybeInvokeDelegate(delegate, context);\n if (delegateResult) {\n if (delegateResult === ContinueSentinel) continue;\n return delegateResult;\n }\n }\n\n if (context.method === \"next\") {\n // Setting context._sent for legacy support of Babel's\n // function.sent implementation.\n context.sent = context._sent = context.arg;\n\n } else if (context.method === \"throw\") {\n if (state === GenStateSuspendedStart) {\n state = GenStateCompleted;\n throw context.arg;\n }\n\n context.dispatchException(context.arg);\n\n } else if (context.method === \"return\") {\n context.abrupt(\"return\", context.arg);\n }\n\n state = GenStateExecuting;\n\n var record = tryCatch(innerFn, self, context);\n if (record.type === \"normal\") {\n // If an exception is thrown from innerFn, we leave state ===\n // GenStateExecuting and loop back for another invocation.\n state = context.done\n ? GenStateCompleted\n : GenStateSuspendedYield;\n\n if (record.arg === ContinueSentinel) {\n continue;\n }\n\n return {\n value: record.arg,\n done: context.done\n };\n\n } else if (record.type === \"throw\") {\n state = GenStateCompleted;\n // Dispatch the exception by looping back around to the\n // context.dispatchException(context.arg) call above.\n context.method = \"throw\";\n context.arg = record.arg;\n }\n }\n };\n }\n\n // Call delegate.iterator[context.method](context.arg) and handle the\n // result, either by returning a { value, done } result from the\n // delegate iterator, or by modifying context.method and context.arg,\n // setting context.delegate to null, and returning the ContinueSentinel.\n function maybeInvokeDelegate(delegate, context) {\n var method = delegate.iterator[context.method];\n if (method === undefined) {\n // A .throw or .return when the delegate iterator has no .throw\n // method always terminates the yield* loop.\n context.delegate = null;\n\n if (context.method === \"throw\") {\n if (delegate.iterator.return) {\n // If the delegate iterator has a return method, give it a\n // chance to clean up.\n context.method = \"return\";\n context.arg = undefined;\n maybeInvokeDelegate(delegate, context);\n\n if (context.method === \"throw\") {\n // If maybeInvokeDelegate(context) changed context.method from\n // \"return\" to \"throw\", let that override the TypeError below.\n return ContinueSentinel;\n }\n }\n\n context.method = \"throw\";\n context.arg = new TypeError(\n \"The iterator does not provide a 'throw' method\");\n }\n\n return ContinueSentinel;\n }\n\n var record = tryCatch(method, delegate.iterator, context.arg);\n\n if (record.type === \"throw\") {\n context.method = \"throw\";\n context.arg = record.arg;\n context.delegate = null;\n return ContinueSentinel;\n }\n\n var info = record.arg;\n\n if (! info) {\n context.method = \"throw\";\n context.arg = new TypeError(\"iterator result is not an object\");\n context.delegate = null;\n return ContinueSentinel;\n }\n\n if (info.done) {\n // Assign the result of the finished delegate to the temporary\n // variable specified by delegate.resultName (see delegateYield).\n context[delegate.resultName] = info.value;\n\n // Resume execution at the desired location (see delegateYield).\n context.next = delegate.nextLoc;\n\n // If context.method was \"throw\" but the delegate handled the\n // exception, let the outer generator proceed normally. If\n // context.method was \"next\", forget context.arg since it has been\n // \"consumed\" by the delegate iterator. If context.method was\n // \"return\", allow the original .return call to continue in the\n // outer generator.\n if (context.method !== \"return\") {\n context.method = \"next\";\n context.arg = undefined;\n }\n\n } else {\n // Re-yield the result returned by the delegate method.\n return info;\n }\n\n // The delegate iterator is finished, so forget it and continue with\n // the outer generator.\n context.delegate = null;\n return ContinueSentinel;\n }\n\n // Define Generator.prototype.{next,throw,return} in terms of the\n // unified ._invoke helper method.\n defineIteratorMethods(Gp);\n\n Gp[toStringTagSymbol] = \"Generator\";\n\n // A Generator should always return itself as the iterator object when the\n // @@iterator function is called on it. Some browsers' implementations of the\n // iterator prototype chain incorrectly implement this, causing the Generator\n // object to not be returned from this call. This ensures that doesn't happen.\n // See https://github.com/facebook/regenerator/issues/274 for more details.\n Gp[iteratorSymbol] = function() {\n return this;\n };\n\n Gp.toString = function() {\n return \"[object Generator]\";\n };\n\n function pushTryEntry(locs) {\n var entry = { tryLoc: locs[0] };\n\n if (1 in locs) {\n entry.catchLoc = locs[1];\n }\n\n if (2 in locs) {\n entry.finallyLoc = locs[2];\n entry.afterLoc = locs[3];\n }\n\n this.tryEntries.push(entry);\n }\n\n function resetTryEntry(entry) {\n var record = entry.completion || {};\n record.type = \"normal\";\n delete record.arg;\n entry.completion = record;\n }\n\n function Context(tryLocsList) {\n // The root entry object (effectively a try statement without a catch\n // or a finally block) gives us a place to store values thrown from\n // locations where there is no enclosing try statement.\n this.tryEntries = [{ tryLoc: \"root\" }];\n tryLocsList.forEach(pushTryEntry, this);\n this.reset(true);\n }\n\n runtime.keys = function(object) {\n var keys = [];\n for (var key in object) {\n keys.push(key);\n }\n keys.reverse();\n\n // Rather than returning an object with a next method, we keep\n // things simple and return the next function itself.\n return function next() {\n while (keys.length) {\n var key = keys.pop();\n if (key in object) {\n next.value = key;\n next.done = false;\n return next;\n }\n }\n\n // To avoid creating an additional object, we just hang the .value\n // and .done properties off the next function object itself. This\n // also ensures that the minifier will not anonymize the function.\n next.done = true;\n return next;\n };\n };\n\n function values(iterable) {\n if (iterable) {\n var iteratorMethod = iterable[iteratorSymbol];\n if (iteratorMethod) {\n return iteratorMethod.call(iterable);\n }\n\n if (typeof iterable.next === \"function\") {\n return iterable;\n }\n\n if (!isNaN(iterable.length)) {\n var i = -1, next = function next() {\n while (++i < iterable.length) {\n if (hasOwn.call(iterable, i)) {\n next.value = iterable[i];\n next.done = false;\n return next;\n }\n }\n\n next.value = undefined;\n next.done = true;\n\n return next;\n };\n\n return next.next = next;\n }\n }\n\n // Return an iterator with no values.\n return { next: doneResult };\n }\n runtime.values = values;\n\n function doneResult() {\n return { value: undefined, done: true };\n }\n\n Context.prototype = {\n constructor: Context,\n\n reset: function(skipTempReset) {\n this.prev = 0;\n this.next = 0;\n // Resetting context._sent for legacy support of Babel's\n // function.sent implementation.\n this.sent = this._sent = undefined;\n this.done = false;\n this.delegate = null;\n\n this.method = \"next\";\n this.arg = undefined;\n\n this.tryEntries.forEach(resetTryEntry);\n\n if (!skipTempReset) {\n for (var name in this) {\n // Not sure about the optimal order of these conditions:\n if (name.charAt(0) === \"t\" &&\n hasOwn.call(this, name) &&\n !isNaN(+name.slice(1))) {\n this[name] = undefined;\n }\n }\n }\n },\n\n stop: function() {\n this.done = true;\n\n var rootEntry = this.tryEntries[0];\n var rootRecord = rootEntry.completion;\n if (rootRecord.type === \"throw\") {\n throw rootRecord.arg;\n }\n\n return this.rval;\n },\n\n dispatchException: function(exception) {\n if (this.done) {\n throw exception;\n }\n\n var context = this;\n function handle(loc, caught) {\n record.type = \"throw\";\n record.arg = exception;\n context.next = loc;\n\n if (caught) {\n // If the dispatched exception was caught by a catch block,\n // then let that catch block handle the exception normally.\n context.method = \"next\";\n context.arg = undefined;\n }\n\n return !! caught;\n }\n\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n var record = entry.completion;\n\n if (entry.tryLoc === \"root\") {\n // Exception thrown outside of any try block that could handle\n // it, so set the completion value of the entire function to\n // throw the exception.\n return handle(\"end\");\n }\n\n if (entry.tryLoc <= this.prev) {\n var hasCatch = hasOwn.call(entry, \"catchLoc\");\n var hasFinally = hasOwn.call(entry, \"finallyLoc\");\n\n if (hasCatch && hasFinally) {\n if (this.prev < entry.catchLoc) {\n return handle(entry.catchLoc, true);\n } else if (this.prev < entry.finallyLoc) {\n return handle(entry.finallyLoc);\n }\n\n } else if (hasCatch) {\n if (this.prev < entry.catchLoc) {\n return handle(entry.catchLoc, true);\n }\n\n } else if (hasFinally) {\n if (this.prev < entry.finallyLoc) {\n return handle(entry.finallyLoc);\n }\n\n } else {\n throw new Error(\"try statement without catch or finally\");\n }\n }\n }\n },\n\n abrupt: function(type, arg) {\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n if (entry.tryLoc <= this.prev &&\n hasOwn.call(entry, \"finallyLoc\") &&\n this.prev < entry.finallyLoc) {\n var finallyEntry = entry;\n break;\n }\n }\n\n if (finallyEntry &&\n (type === \"break\" ||\n type === \"continue\") &&\n finallyEntry.tryLoc <= arg &&\n arg <= finallyEntry.finallyLoc) {\n // Ignore the finally entry if control is not jumping to a\n // location outside the try/catch block.\n finallyEntry = null;\n }\n\n var record = finallyEntry ? finallyEntry.completion : {};\n record.type = type;\n record.arg = arg;\n\n if (finallyEntry) {\n this.method = \"next\";\n this.next = finallyEntry.finallyLoc;\n return ContinueSentinel;\n }\n\n return this.complete(record);\n },\n\n complete: function(record, afterLoc) {\n if (record.type === \"throw\") {\n throw record.arg;\n }\n\n if (record.type === \"break\" ||\n record.type === \"continue\") {\n this.next = record.arg;\n } else if (record.type === \"return\") {\n this.rval = this.arg = record.arg;\n this.method = \"return\";\n this.next = \"end\";\n } else if (record.type === \"normal\" && afterLoc) {\n this.next = afterLoc;\n }\n\n return ContinueSentinel;\n },\n\n finish: function(finallyLoc) {\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n if (entry.finallyLoc === finallyLoc) {\n this.complete(entry.completion, entry.afterLoc);\n resetTryEntry(entry);\n return ContinueSentinel;\n }\n }\n },\n\n \"catch\": function(tryLoc) {\n for (var i = this.tryEntries.length - 1; i >= 0; --i) {\n var entry = this.tryEntries[i];\n if (entry.tryLoc === tryLoc) {\n var record = entry.completion;\n if (record.type === \"throw\") {\n var thrown = record.arg;\n resetTryEntry(entry);\n }\n return thrown;\n }\n }\n\n // The context.catch method must only be called with a location\n // argument that corresponds to a known catch block.\n throw new Error(\"illegal catch attempt\");\n },\n\n delegateYield: function(iterable, resultName, nextLoc) {\n this.delegate = {\n iterator: values(iterable),\n resultName: resultName,\n nextLoc: nextLoc\n };\n\n if (this.method === \"next\") {\n // Deliberately forget the last sent value so that we don't\n // accidentally pass it on to the delegate.\n this.arg = undefined;\n }\n\n return ContinueSentinel;\n }\n };\n})(\n // In sloppy mode, unbound `this` refers to the global object, fallback to\n // Function constructor if we're in global strict mode. That is sadly a form\n // of indirect eval which violates Content Security Policy.\n (function() { return this })() || Function(\"return this\")()\n);\n","var $iterators = require('./es6.array.iterator');\nvar getKeys = require('./_object-keys');\nvar redefine = require('./_redefine');\nvar global = require('./_global');\nvar hide = require('./_hide');\nvar Iterators = require('./_iterators');\nvar wks = require('./_wks');\nvar ITERATOR = wks('iterator');\nvar TO_STRING_TAG = wks('toStringTag');\nvar ArrayValues = Iterators.Array;\n\nvar DOMIterables = {\n CSSRuleList: true, // TODO: Not spec compliant, should be false.\n CSSStyleDeclaration: false,\n CSSValueList: false,\n ClientRectList: false,\n DOMRectList: false,\n DOMStringList: false,\n DOMTokenList: true,\n DataTransferItemList: false,\n FileList: false,\n HTMLAllCollection: false,\n HTMLCollection: false,\n HTMLFormElement: false,\n HTMLSelectElement: false,\n MediaList: true, // TODO: Not spec compliant, should be false.\n MimeTypeArray: false,\n NamedNodeMap: false,\n NodeList: true,\n PaintRequestList: false,\n Plugin: false,\n PluginArray: false,\n SVGLengthList: false,\n SVGNumberList: false,\n SVGPathSegList: false,\n SVGPointList: false,\n SVGStringList: false,\n SVGTransformList: false,\n SourceBufferList: false,\n StyleSheetList: true, // TODO: Not spec compliant, should be false.\n TextTrackCueList: false,\n TextTrackList: false,\n TouchList: false\n};\n\nfor (var collections = getKeys(DOMIterables), i = 0; i < collections.length; i++) {\n var NAME = collections[i];\n var explicit = DOMIterables[NAME];\n var Collection = global[NAME];\n var proto = Collection && Collection.prototype;\n var key;\n if (proto) {\n if (!proto[ITERATOR]) hide(proto, ITERATOR, ArrayValues);\n if (!proto[TO_STRING_TAG]) hide(proto, TO_STRING_TAG, NAME);\n Iterators[NAME] = ArrayValues;\n if (explicit) for (key in $iterators) if (!proto[key]) redefine(proto, key, $iterators[key], true);\n }\n}\n","var $export = require('./_export');\nvar $task = require('./_task');\n$export($export.G + $export.B, {\n setImmediate: $task.set,\n clearImmediate: $task.clear\n});\n","// ie9- setTimeout & setInterval additional parameters fix\nvar global = require('./_global');\nvar $export = require('./_export');\nvar userAgent = require('./_user-agent');\nvar slice = [].slice;\nvar MSIE = /MSIE .\\./.test(userAgent); // <- dirty ie9- check\nvar wrap = function (set) {\n return function (fn, time /* , ...args */) {\n var boundArgs = arguments.length > 2;\n var args = boundArgs ? slice.call(arguments, 2) : false;\n return set(boundArgs ? function () {\n // eslint-disable-next-line no-new-func\n (typeof fn == 'function' ? fn : Function(fn)).apply(this, args);\n } : fn, time);\n };\n};\n$export($export.G + $export.B + $export.F * MSIE, {\n setTimeout: wrap(global.setTimeout),\n setInterval: wrap(global.setInterval)\n});\n","'use strict';\n// https://github.com/tc39/proposal-string-pad-start-end\nvar $export = require('./_export');\nvar $pad = require('./_string-pad');\nvar userAgent = require('./_user-agent');\n\n// https://github.com/zloirock/core-js/issues/280\n$export($export.P + $export.F * /Version\\/10\\.\\d+(\\.\\d+)? Safari\\//.test(userAgent), 'String', {\n padEnd: function padEnd(maxLength /* , fillString = ' ' */) {\n return $pad(this, maxLength, arguments.length > 1 ? arguments[1] : undefined, false);\n }\n});\n","'use strict';\n// https://github.com/tc39/proposal-string-pad-start-end\nvar $export = require('./_export');\nvar $pad = require('./_string-pad');\nvar userAgent = require('./_user-agent');\n\n// https://github.com/zloirock/core-js/issues/280\n$export($export.P + $export.F * /Version\\/10\\.\\d+(\\.\\d+)? Safari\\//.test(userAgent), 'String', {\n padStart: function padStart(maxLength /* , fillString = ' ' */) {\n return $pad(this, maxLength, arguments.length > 1 ? arguments[1] : undefined, true);\n }\n});\n","// https://github.com/tc39/proposal-object-getownpropertydescriptors\nvar $export = require('./_export');\nvar ownKeys = require('./_own-keys');\nvar toIObject = require('./_to-iobject');\nvar gOPD = require('./_object-gopd');\nvar createProperty = require('./_create-property');\n\n$export($export.S, 'Object', {\n getOwnPropertyDescriptors: function getOwnPropertyDescriptors(object) {\n var O = toIObject(object);\n var getDesc = gOPD.f;\n var keys = ownKeys(O);\n var result = {};\n var i = 0;\n var key, desc;\n while (keys.length > i) {\n desc = getDesc(O, key = keys[i++]);\n if (desc !== undefined) createProperty(result, key, desc);\n }\n return result;\n }\n});\n","// https://github.com/tc39/proposal-object-values-entries\nvar $export = require('./_export');\nvar $entries = require('./_object-to-array')(true);\n\n$export($export.S, 'Object', {\n entries: function entries(it) {\n return $entries(it);\n }\n});\n","// https://github.com/tc39/proposal-object-values-entries\nvar $export = require('./_export');\nvar $values = require('./_object-to-array')(false);\n\n$export($export.S, 'Object', {\n values: function values(it) {\n return $values(it);\n }\n});\n","'use strict';\n// https://github.com/tc39/Array.prototype.includes\nvar $export = require('./_export');\nvar $includes = require('./_array-includes')(true);\n\n$export($export.P, 'Array', {\n includes: function includes(el /* , fromIndex = 0 */) {\n return $includes(this, el, arguments.length > 1 ? arguments[1] : undefined);\n }\n});\n\nrequire('./_add-to-unscopables')('includes');\n","// 20.2.2.34 Math.trunc(x)\nvar $export = require('./_export');\n\n$export($export.S, 'Math', {\n trunc: function trunc(it) {\n return (it > 0 ? Math.floor : Math.ceil)(it);\n }\n});\n","// 20.2.2.33 Math.tanh(x)\nvar $export = require('./_export');\nvar expm1 = require('./_math-expm1');\nvar exp = Math.exp;\n\n$export($export.S, 'Math', {\n tanh: function tanh(x) {\n var a = expm1(x = +x);\n var b = expm1(-x);\n return a == Infinity ? 1 : b == Infinity ? -1 : (a - b) / (exp(x) + exp(-x));\n }\n});\n","// 20.2.2.30 Math.sinh(x)\nvar $export = require('./_export');\nvar expm1 = require('./_math-expm1');\nvar exp = Math.exp;\n\n// V8 near Chromium 38 has a problem with very small numbers\n$export($export.S + $export.F * require('./_fails')(function () {\n return !Math.sinh(-2e-17) != -2e-17;\n}), 'Math', {\n sinh: function sinh(x) {\n return Math.abs(x = +x) < 1\n ? (expm1(x) - expm1(-x)) / 2\n : (exp(x - 1) - exp(-x - 1)) * (Math.E / 2);\n }\n});\n","// 20.2.2.28 Math.sign(x)\nvar $export = require('./_export');\n\n$export($export.S, 'Math', { sign: require('./_math-sign') });\n","// 20.2.2.22 Math.log2(x)\nvar $export = require('./_export');\n\n$export($export.S, 'Math', {\n log2: function log2(x) {\n return Math.log(x) / Math.LN2;\n }\n});\n","// 20.2.2.21 Math.log10(x)\nvar $export = require('./_export');\n\n$export($export.S, 'Math', {\n log10: function log10(x) {\n return Math.log(x) * Math.LOG10E;\n }\n});\n","// 20.2.2.20 Math.log1p(x)\nvar $export = require('./_export');\n\n$export($export.S, 'Math', { log1p: require('./_math-log1p') });\n","// 20.2.2.18 Math.imul(x, y)\nvar $export = require('./_export');\nvar $imul = Math.imul;\n\n// some WebKit versions fails with big numbers, some has wrong arity\n$export($export.S + $export.F * require('./_fails')(function () {\n return $imul(0xffffffff, 5) != -5 || $imul.length != 2;\n}), 'Math', {\n imul: function imul(x, y) {\n var UINT16 = 0xffff;\n var xn = +x;\n var yn = +y;\n var xl = UINT16 & xn;\n var yl = UINT16 & yn;\n return 0 | xl * yl + ((UINT16 & xn >>> 16) * yl + xl * (UINT16 & yn >>> 16) << 16 >>> 0);\n }\n});\n","// 20.2.2.17 Math.hypot([value1[, value2[, … ]]])\nvar $export = require('./_export');\nvar abs = Math.abs;\n\n$export($export.S, 'Math', {\n hypot: function hypot(value1, value2) { // eslint-disable-line no-unused-vars\n var sum = 0;\n var i = 0;\n var aLen = arguments.length;\n var larg = 0;\n var arg, div;\n while (i < aLen) {\n arg = abs(arguments[i++]);\n if (larg < arg) {\n div = larg / arg;\n sum = sum * div * div + 1;\n larg = arg;\n } else if (arg > 0) {\n div = arg / larg;\n sum += div * div;\n } else sum += arg;\n }\n return larg === Infinity ? Infinity : larg * Math.sqrt(sum);\n }\n});\n","// 20.2.2.16 Math.fround(x)\nvar sign = require('./_math-sign');\nvar pow = Math.pow;\nvar EPSILON = pow(2, -52);\nvar EPSILON32 = pow(2, -23);\nvar MAX32 = pow(2, 127) * (2 - EPSILON32);\nvar MIN32 = pow(2, -126);\n\nvar roundTiesToEven = function (n) {\n return n + 1 / EPSILON - 1 / EPSILON;\n};\n\nmodule.exports = Math.fround || function fround(x) {\n var $abs = Math.abs(x);\n var $sign = sign(x);\n var a, result;\n if ($abs < MIN32) return $sign * roundTiesToEven($abs / MIN32 / EPSILON32) * MIN32 * EPSILON32;\n a = (1 + EPSILON32 / EPSILON) * $abs;\n result = a - (a - $abs);\n // eslint-disable-next-line no-self-compare\n if (result > MAX32 || result != result) return $sign * Infinity;\n return $sign * result;\n};\n","// 20.2.2.16 Math.fround(x)\nvar $export = require('./_export');\n\n$export($export.S, 'Math', { fround: require('./_math-fround') });\n","// 20.2.2.14 Math.expm1(x)\nvar $export = require('./_export');\nvar $expm1 = require('./_math-expm1');\n\n$export($export.S + $export.F * ($expm1 != Math.expm1), 'Math', { expm1: $expm1 });\n","// 20.2.2.12 Math.cosh(x)\nvar $export = require('./_export');\nvar exp = Math.exp;\n\n$export($export.S, 'Math', {\n cosh: function cosh(x) {\n return (exp(x = +x) + exp(-x)) / 2;\n }\n});\n","// 20.2.2.11 Math.clz32(x)\nvar $export = require('./_export');\n\n$export($export.S, 'Math', {\n clz32: function clz32(x) {\n return (x >>>= 0) ? 31 - Math.floor(Math.log(x + 0.5) * Math.LOG2E) : 32;\n }\n});\n","// 20.2.2.9 Math.cbrt(x)\nvar $export = require('./_export');\nvar sign = require('./_math-sign');\n\n$export($export.S, 'Math', {\n cbrt: function cbrt(x) {\n return sign(x = +x) * Math.pow(Math.abs(x), 1 / 3);\n }\n});\n","// 20.2.2.7 Math.atanh(x)\nvar $export = require('./_export');\nvar $atanh = Math.atanh;\n\n// Tor Browser bug: Math.atanh(-0) -> 0\n$export($export.S + $export.F * !($atanh && 1 / $atanh(-0) < 0), 'Math', {\n atanh: function atanh(x) {\n return (x = +x) == 0 ? x : Math.log((1 + x) / (1 - x)) / 2;\n }\n});\n","// 20.2.2.5 Math.asinh(x)\nvar $export = require('./_export');\nvar $asinh = Math.asinh;\n\nfunction asinh(x) {\n return !isFinite(x = +x) || x == 0 ? x : x < 0 ? -asinh(-x) : Math.log(x + Math.sqrt(x * x + 1));\n}\n\n// Tor Browser bug: Math.asinh(0) -> -0\n$export($export.S + $export.F * !($asinh && 1 / $asinh(0) > 0), 'Math', { asinh: asinh });\n","// 20.2.2.3 Math.acosh(x)\nvar $export = require('./_export');\nvar log1p = require('./_math-log1p');\nvar sqrt = Math.sqrt;\nvar $acosh = Math.acosh;\n\n$export($export.S + $export.F * !($acosh\n // V8 bug: https://code.google.com/p/v8/issues/detail?id=3509\n && Math.floor($acosh(Number.MAX_VALUE)) == 710\n // Tor Browser bug: Math.acosh(Infinity) -> NaN\n && $acosh(Infinity) == Infinity\n), 'Math', {\n acosh: function acosh(x) {\n return (x = +x) < 1 ? NaN : x > 94906265.62425156\n ? Math.log(x) + Math.LN2\n : log1p(x - 1 + sqrt(x - 1) * sqrt(x + 1));\n }\n});\n","// 20.1.2.6 Number.MAX_SAFE_INTEGER\nvar $export = require('./_export');\n\n$export($export.S, 'Number', { MAX_SAFE_INTEGER: 0x1fffffffffffff });\n","// 20.1.2.10 Number.MIN_SAFE_INTEGER\nvar $export = require('./_export');\n\n$export($export.S, 'Number', { MIN_SAFE_INTEGER: -0x1fffffffffffff });\n","// 20.1.2.1 Number.EPSILON\nvar $export = require('./_export');\n\n$export($export.S, 'Number', { EPSILON: Math.pow(2, -52) });\n","// 20.1.2.4 Number.isNaN(number)\nvar $export = require('./_export');\n\n$export($export.S, 'Number', {\n isNaN: function isNaN(number) {\n // eslint-disable-next-line no-self-compare\n return number != number;\n }\n});\n","// 20.1.2.5 Number.isSafeInteger(number)\nvar $export = require('./_export');\nvar isInteger = require('./_is-integer');\nvar abs = Math.abs;\n\n$export($export.S, 'Number', {\n isSafeInteger: function isSafeInteger(number) {\n return isInteger(number) && abs(number) <= 0x1fffffffffffff;\n }\n});\n","// 20.1.2.3 Number.isInteger(number)\nvar $export = require('./_export');\n\n$export($export.S, 'Number', { isInteger: require('./_is-integer') });\n","// 20.1.2.2 Number.isFinite(number)\nvar $export = require('./_export');\nvar _isFinite = require('./_global').isFinite;\n\n$export($export.S, 'Number', {\n isFinite: function isFinite(it) {\n return typeof it == 'number' && _isFinite(it);\n }\n});\n","// 22.1.3.6 Array.prototype.fill(value, start = 0, end = this.length)\nvar $export = require('./_export');\n\n$export($export.P, 'Array', { fill: require('./_array-fill') });\n\nrequire('./_add-to-unscopables')('fill');\n","'use strict';\n// 22.1.3.9 Array.prototype.findIndex(predicate, thisArg = undefined)\nvar $export = require('./_export');\nvar $find = require('./_array-methods')(6);\nvar KEY = 'findIndex';\nvar forced = true;\n// Shouldn't skip holes\nif (KEY in []) Array(1)[KEY](function () { forced = false; });\n$export($export.P + $export.F * forced, 'Array', {\n findIndex: function findIndex(callbackfn /* , that = undefined */) {\n return $find(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);\n }\n});\nrequire('./_add-to-unscopables')(KEY);\n","'use strict';\n// 22.1.3.8 Array.prototype.find(predicate, thisArg = undefined)\nvar $export = require('./_export');\nvar $find = require('./_array-methods')(5);\nvar KEY = 'find';\nvar forced = true;\n// Shouldn't skip holes\nif (KEY in []) Array(1)[KEY](function () { forced = false; });\n$export($export.P + $export.F * forced, 'Array', {\n find: function find(callbackfn /* , that = undefined */) {\n return $find(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);\n }\n});\nrequire('./_add-to-unscopables')(KEY);\n","// 22.1.3.3 Array.prototype.copyWithin(target, start, end = this.length)\nvar $export = require('./_export');\n\n$export($export.P, 'Array', { copyWithin: require('./_array-copy-within') });\n\nrequire('./_add-to-unscopables')('copyWithin');\n","'use strict';\nvar $export = require('./_export');\nvar createProperty = require('./_create-property');\n\n// WebKit Array.of isn't generic\n$export($export.S + $export.F * require('./_fails')(function () {\n function F() { /* empty */ }\n return !(Array.of.call(F) instanceof F);\n}), 'Array', {\n // 22.1.2.3 Array.of( ...items)\n of: function of(/* ...args */) {\n var index = 0;\n var aLen = arguments.length;\n var result = new (typeof this == 'function' ? this : Array)(aLen);\n while (aLen > index) createProperty(result, index, arguments[index++]);\n result.length = aLen;\n return result;\n }\n});\n","'use strict';\nvar ctx = require('./_ctx');\nvar $export = require('./_export');\nvar toObject = require('./_to-object');\nvar call = require('./_iter-call');\nvar isArrayIter = require('./_is-array-iter');\nvar toLength = require('./_to-length');\nvar createProperty = require('./_create-property');\nvar getIterFn = require('./core.get-iterator-method');\n\n$export($export.S + $export.F * !require('./_iter-detect')(function (iter) { Array.from(iter); }), 'Array', {\n // 22.1.2.1 Array.from(arrayLike, mapfn = undefined, thisArg = undefined)\n from: function from(arrayLike /* , mapfn = undefined, thisArg = undefined */) {\n var O = toObject(arrayLike);\n var C = typeof this == 'function' ? this : Array;\n var aLen = arguments.length;\n var mapfn = aLen > 1 ? arguments[1] : undefined;\n var mapping = mapfn !== undefined;\n var index = 0;\n var iterFn = getIterFn(O);\n var length, result, step, iterator;\n if (mapping) mapfn = ctx(mapfn, aLen > 2 ? arguments[2] : undefined, 2);\n // if object isn't iterable or it's array with default iterator - use simple case\n if (iterFn != undefined && !(C == Array && isArrayIter(iterFn))) {\n for (iterator = iterFn.call(O), result = new C(); !(step = iterator.next()).done; index++) {\n createProperty(result, index, mapping ? call(iterator, mapfn, [step.value, index], true) : step.value);\n }\n } else {\n length = toLength(O.length);\n for (result = new C(length); length > index; index++) {\n createProperty(result, index, mapping ? mapfn(O[index], index) : O[index]);\n }\n }\n result.length = index;\n return result;\n }\n});\n","// @@search logic\nrequire('./_fix-re-wks')('search', 1, function (defined, SEARCH, $search) {\n // 21.1.3.15 String.prototype.search(regexp)\n return [function search(regexp) {\n 'use strict';\n var O = defined(this);\n var fn = regexp == undefined ? undefined : regexp[SEARCH];\n return fn !== undefined ? fn.call(regexp, O) : new RegExp(regexp)[SEARCH](String(O));\n }, $search];\n});\n","// @@split logic\nrequire('./_fix-re-wks')('split', 2, function (defined, SPLIT, $split) {\n 'use strict';\n var isRegExp = require('./_is-regexp');\n var _split = $split;\n var $push = [].push;\n var $SPLIT = 'split';\n var LENGTH = 'length';\n var LAST_INDEX = 'lastIndex';\n if (\n 'abbc'[$SPLIT](/(b)*/)[1] == 'c' ||\n 'test'[$SPLIT](/(?:)/, -1)[LENGTH] != 4 ||\n 'ab'[$SPLIT](/(?:ab)*/)[LENGTH] != 2 ||\n '.'[$SPLIT](/(.?)(.?)/)[LENGTH] != 4 ||\n '.'[$SPLIT](/()()/)[LENGTH] > 1 ||\n ''[$SPLIT](/.?/)[LENGTH]\n ) {\n var NPCG = /()??/.exec('')[1] === undefined; // nonparticipating capturing group\n // based on es5-shim implementation, need to rework it\n $split = function (separator, limit) {\n var string = String(this);\n if (separator === undefined && limit === 0) return [];\n // If `separator` is not a regex, use native split\n if (!isRegExp(separator)) return _split.call(string, separator, limit);\n var output = [];\n var flags = (separator.ignoreCase ? 'i' : '') +\n (separator.multiline ? 'm' : '') +\n (separator.unicode ? 'u' : '') +\n (separator.sticky ? 'y' : '');\n var lastLastIndex = 0;\n var splitLimit = limit === undefined ? 4294967295 : limit >>> 0;\n // Make `global` and avoid `lastIndex` issues by working with a copy\n var separatorCopy = new RegExp(separator.source, flags + 'g');\n var separator2, match, lastIndex, lastLength, i;\n // Doesn't need flags gy, but they don't hurt\n if (!NPCG) separator2 = new RegExp('^' + separatorCopy.source + '$(?!\\\\s)', flags);\n while (match = separatorCopy.exec(string)) {\n // `separatorCopy.lastIndex` is not reliable cross-browser\n lastIndex = match.index + match[0][LENGTH];\n if (lastIndex > lastLastIndex) {\n output.push(string.slice(lastLastIndex, match.index));\n // Fix browsers whose `exec` methods don't consistently return `undefined` for NPCG\n // eslint-disable-next-line no-loop-func\n if (!NPCG && match[LENGTH] > 1) match[0].replace(separator2, function () {\n for (i = 1; i < arguments[LENGTH] - 2; i++) if (arguments[i] === undefined) match[i] = undefined;\n });\n if (match[LENGTH] > 1 && match.index < string[LENGTH]) $push.apply(output, match.slice(1));\n lastLength = match[0][LENGTH];\n lastLastIndex = lastIndex;\n if (output[LENGTH] >= splitLimit) break;\n }\n if (separatorCopy[LAST_INDEX] === match.index) separatorCopy[LAST_INDEX]++; // Avoid an infinite loop\n }\n if (lastLastIndex === string[LENGTH]) {\n if (lastLength || !separatorCopy.test('')) output.push('');\n } else output.push(string.slice(lastLastIndex));\n return output[LENGTH] > splitLimit ? output.slice(0, splitLimit) : output;\n };\n // Chakra, V8\n } else if ('0'[$SPLIT](undefined, 0)[LENGTH]) {\n $split = function (separator, limit) {\n return separator === undefined && limit === 0 ? [] : _split.call(this, separator, limit);\n };\n }\n // 21.1.3.17 String.prototype.split(separator, limit)\n return [function split(separator, limit) {\n var O = defined(this);\n var fn = separator == undefined ? undefined : separator[SPLIT];\n return fn !== undefined ? fn.call(separator, O, limit) : $split.call(String(O), separator, limit);\n }, $split];\n});\n","// @@replace logic\nrequire('./_fix-re-wks')('replace', 2, function (defined, REPLACE, $replace) {\n // 21.1.3.14 String.prototype.replace(searchValue, replaceValue)\n return [function replace(searchValue, replaceValue) {\n 'use strict';\n var O = defined(this);\n var fn = searchValue == undefined ? undefined : searchValue[REPLACE];\n return fn !== undefined\n ? fn.call(searchValue, O, replaceValue)\n : $replace.call(String(O), searchValue, replaceValue);\n }, $replace];\n});\n","// @@match logic\nrequire('./_fix-re-wks')('match', 1, function (defined, MATCH, $match) {\n // 21.1.3.11 String.prototype.match(regexp)\n return [function match(regexp) {\n 'use strict';\n var O = defined(this);\n var fn = regexp == undefined ? undefined : regexp[MATCH];\n return fn !== undefined ? fn.call(regexp, O) : new RegExp(regexp)[MATCH](String(O));\n }, $match];\n});\n","'use strict';\n// 21.2.5.3 get RegExp.prototype.flags\nvar anObject = require('./_an-object');\nmodule.exports = function () {\n var that = anObject(this);\n var result = '';\n if (that.global) result += 'g';\n if (that.ignoreCase) result += 'i';\n if (that.multiline) result += 'm';\n if (that.unicode) result += 'u';\n if (that.sticky) result += 'y';\n return result;\n};\n","// 21.2.5.3 get RegExp.prototype.flags()\nif (require('./_descriptors') && /./g.flags != 'g') require('./_object-dp').f(RegExp.prototype, 'flags', {\n configurable: true,\n get: require('./_flags')\n});\n","// 21.1.3.7 String.prototype.includes(searchString, position = 0)\n'use strict';\nvar $export = require('./_export');\nvar context = require('./_string-context');\nvar INCLUDES = 'includes';\n\n$export($export.P + $export.F * require('./_fails-is-regexp')(INCLUDES), 'String', {\n includes: function includes(searchString /* , position = 0 */) {\n return !!~context(this, searchString, INCLUDES)\n .indexOf(searchString, arguments.length > 1 ? arguments[1] : undefined);\n }\n});\n","// 21.1.3.6 String.prototype.endsWith(searchString [, endPosition])\n'use strict';\nvar $export = require('./_export');\nvar toLength = require('./_to-length');\nvar context = require('./_string-context');\nvar ENDS_WITH = 'endsWith';\nvar $endsWith = ''[ENDS_WITH];\n\n$export($export.P + $export.F * require('./_fails-is-regexp')(ENDS_WITH), 'String', {\n endsWith: function endsWith(searchString /* , endPosition = @length */) {\n var that = context(this, searchString, ENDS_WITH);\n var endPosition = arguments.length > 1 ? arguments[1] : undefined;\n var len = toLength(that.length);\n var end = endPosition === undefined ? len : Math.min(toLength(endPosition), len);\n var search = String(searchString);\n return $endsWith\n ? $endsWith.call(that, search, end)\n : that.slice(end - search.length, end) === search;\n }\n});\n","// 21.1.3.18 String.prototype.startsWith(searchString [, position ])\n'use strict';\nvar $export = require('./_export');\nvar toLength = require('./_to-length');\nvar context = require('./_string-context');\nvar STARTS_WITH = 'startsWith';\nvar $startsWith = ''[STARTS_WITH];\n\n$export($export.P + $export.F * require('./_fails-is-regexp')(STARTS_WITH), 'String', {\n startsWith: function startsWith(searchString /* , position = 0 */) {\n var that = context(this, searchString, STARTS_WITH);\n var index = toLength(Math.min(arguments.length > 1 ? arguments[1] : undefined, that.length));\n var search = String(searchString);\n return $startsWith\n ? $startsWith.call(that, search, index)\n : that.slice(index, index + search.length) === search;\n }\n});\n","var $export = require('./_export');\n\n$export($export.P, 'String', {\n // 21.1.3.13 String.prototype.repeat(count)\n repeat: require('./_string-repeat')\n});\n","var toInteger = require('./_to-integer');\nvar defined = require('./_defined');\n// true -> String#at\n// false -> String#codePointAt\nmodule.exports = function (TO_STRING) {\n return function (that, pos) {\n var s = String(defined(that));\n var i = toInteger(pos);\n var l = s.length;\n var a, b;\n if (i < 0 || i >= l) return TO_STRING ? '' : undefined;\n a = s.charCodeAt(i);\n return a < 0xd800 || a > 0xdbff || i + 1 === l || (b = s.charCodeAt(i + 1)) < 0xdc00 || b > 0xdfff\n ? TO_STRING ? s.charAt(i) : a\n : TO_STRING ? s.slice(i, i + 2) : (a - 0xd800 << 10) + (b - 0xdc00) + 0x10000;\n };\n};\n","'use strict';\nvar $export = require('./_export');\nvar $at = require('./_string-at')(false);\n$export($export.P, 'String', {\n // 21.1.3.3 String.prototype.codePointAt(pos)\n codePointAt: function codePointAt(pos) {\n return $at(this, pos);\n }\n});\n","var $export = require('./_export');\nvar toAbsoluteIndex = require('./_to-absolute-index');\nvar fromCharCode = String.fromCharCode;\nvar $fromCodePoint = String.fromCodePoint;\n\n// length should be 1, old FF problem\n$export($export.S + $export.F * (!!$fromCodePoint && $fromCodePoint.length != 1), 'String', {\n // 21.1.2.2 String.fromCodePoint(...codePoints)\n fromCodePoint: function fromCodePoint(x) { // eslint-disable-line no-unused-vars\n var res = [];\n var aLen = arguments.length;\n var i = 0;\n var code;\n while (aLen > i) {\n code = +arguments[i++];\n if (toAbsoluteIndex(code, 0x10ffff) !== code) throw RangeError(code + ' is not a valid code point');\n res.push(code < 0x10000\n ? fromCharCode(code)\n : fromCharCode(((code -= 0x10000) >> 10) + 0xd800, code % 0x400 + 0xdc00)\n );\n } return res.join('');\n }\n});\n","var $export = require('./_export');\nvar toIObject = require('./_to-iobject');\nvar toLength = require('./_to-length');\n\n$export($export.S, 'String', {\n // 21.1.2.4 String.raw(callSite, ...substitutions)\n raw: function raw(callSite) {\n var tpl = toIObject(callSite.raw);\n var len = toLength(tpl.length);\n var aLen = arguments.length;\n var res = [];\n var i = 0;\n while (len > i) {\n res.push(String(tpl[i++]));\n if (i < aLen) res.push(String(arguments[i]));\n } return res.join('');\n }\n});\n","var dP = require('./_object-dp').f;\nvar FProto = Function.prototype;\nvar nameRE = /^\\s*function ([^ (]*)/;\nvar NAME = 'name';\n\n// 19.2.4.2 name\nNAME in FProto || require('./_descriptors') && dP(FProto, NAME, {\n configurable: true,\n get: function () {\n try {\n return ('' + this).match(nameRE)[1];\n } catch (e) {\n return '';\n }\n }\n});\n","// 19.1.3.19 Object.setPrototypeOf(O, proto)\nvar $export = require('./_export');\n$export($export.S, 'Object', { setPrototypeOf: require('./_set-proto').set });\n","// 7.2.9 SameValue(x, y)\nmodule.exports = Object.is || function is(x, y) {\n // eslint-disable-next-line no-self-compare\n return x === y ? x !== 0 || 1 / x === 1 / y : x != x && y != y;\n};\n","// 19.1.3.10 Object.is(value1, value2)\nvar $export = require('./_export');\n$export($export.S, 'Object', { is: require('./_same-value') });\n","// 19.1.3.1 Object.assign(target, source)\nvar $export = require('./_export');\n\n$export($export.S + $export.F, 'Object', { assign: require('./_object-assign') });\n","// 19.1.2.7 Object.getOwnPropertyNames(O)\nrequire('./_object-sap')('getOwnPropertyNames', function () {\n return require('./_object-gopn-ext').f;\n});\n","// 19.1.2.14 Object.keys(O)\nvar toObject = require('./_to-object');\nvar $keys = require('./_object-keys');\n\nrequire('./_object-sap')('keys', function () {\n return function keys(it) {\n return $keys(toObject(it));\n };\n});\n","// 19.1.2.9 Object.getPrototypeOf(O)\nvar toObject = require('./_to-object');\nvar $getPrototypeOf = require('./_object-gpo');\n\nrequire('./_object-sap')('getPrototypeOf', function () {\n return function getPrototypeOf(it) {\n return $getPrototypeOf(toObject(it));\n };\n});\n","// 19.1.2.6 Object.getOwnPropertyDescriptor(O, P)\nvar toIObject = require('./_to-iobject');\nvar $getOwnPropertyDescriptor = require('./_object-gopd').f;\n\nrequire('./_object-sap')('getOwnPropertyDescriptor', function () {\n return function getOwnPropertyDescriptor(it, key) {\n return $getOwnPropertyDescriptor(toIObject(it), key);\n };\n});\n","// 19.1.2.11 Object.isExtensible(O)\nvar isObject = require('./_is-object');\n\nrequire('./_object-sap')('isExtensible', function ($isExtensible) {\n return function isExtensible(it) {\n return isObject(it) ? $isExtensible ? $isExtensible(it) : true : false;\n };\n});\n","// 19.1.2.13 Object.isSealed(O)\nvar isObject = require('./_is-object');\n\nrequire('./_object-sap')('isSealed', function ($isSealed) {\n return function isSealed(it) {\n return isObject(it) ? $isSealed ? $isSealed(it) : false : true;\n };\n});\n","// 19.1.2.12 Object.isFrozen(O)\nvar isObject = require('./_is-object');\n\nrequire('./_object-sap')('isFrozen', function ($isFrozen) {\n return function isFrozen(it) {\n return isObject(it) ? $isFrozen ? $isFrozen(it) : false : true;\n };\n});\n","// 19.1.2.15 Object.preventExtensions(O)\nvar isObject = require('./_is-object');\nvar meta = require('./_meta').onFreeze;\n\nrequire('./_object-sap')('preventExtensions', function ($preventExtensions) {\n return function preventExtensions(it) {\n return $preventExtensions && isObject(it) ? $preventExtensions(meta(it)) : it;\n };\n});\n","// 19.1.2.17 Object.seal(O)\nvar isObject = require('./_is-object');\nvar meta = require('./_meta').onFreeze;\n\nrequire('./_object-sap')('seal', function ($seal) {\n return function seal(it) {\n return $seal && isObject(it) ? $seal(meta(it)) : it;\n };\n});\n","// 19.1.2.5 Object.freeze(O)\nvar isObject = require('./_is-object');\nvar meta = require('./_meta').onFreeze;\n\nrequire('./_object-sap')('freeze', function ($freeze) {\n return function freeze(it) {\n return $freeze && isObject(it) ? $freeze(meta(it)) : it;\n };\n});\n","// all enumerable object keys, includes symbols\nvar getKeys = require('./_object-keys');\nvar gOPS = require('./_object-gops');\nvar pIE = require('./_object-pie');\nmodule.exports = function (it) {\n var result = getKeys(it);\n var getSymbols = gOPS.f;\n if (getSymbols) {\n var symbols = getSymbols(it);\n var isEnum = pIE.f;\n var i = 0;\n var key;\n while (symbols.length > i) if (isEnum.call(it, key = symbols[i++])) result.push(key);\n } return result;\n};\n","var global = require('./_global');\nvar core = require('./_core');\nvar LIBRARY = require('./_library');\nvar wksExt = require('./_wks-ext');\nvar defineProperty = require('./_object-dp').f;\nmodule.exports = function (name) {\n var $Symbol = core.Symbol || (core.Symbol = LIBRARY ? {} : global.Symbol || {});\n if (name.charAt(0) != '_' && !(name in $Symbol)) defineProperty($Symbol, name, { value: wksExt.f(name) });\n};\n","'use strict';\n// ECMAScript 6 symbols shim\nvar global = require('./_global');\nvar has = require('./_has');\nvar DESCRIPTORS = require('./_descriptors');\nvar $export = require('./_export');\nvar redefine = require('./_redefine');\nvar META = require('./_meta').KEY;\nvar $fails = require('./_fails');\nvar shared = require('./_shared');\nvar setToStringTag = require('./_set-to-string-tag');\nvar uid = require('./_uid');\nvar wks = require('./_wks');\nvar wksExt = require('./_wks-ext');\nvar wksDefine = require('./_wks-define');\nvar enumKeys = require('./_enum-keys');\nvar isArray = require('./_is-array');\nvar anObject = require('./_an-object');\nvar isObject = require('./_is-object');\nvar toIObject = require('./_to-iobject');\nvar toPrimitive = require('./_to-primitive');\nvar createDesc = require('./_property-desc');\nvar _create = require('./_object-create');\nvar gOPNExt = require('./_object-gopn-ext');\nvar $GOPD = require('./_object-gopd');\nvar $DP = require('./_object-dp');\nvar $keys = require('./_object-keys');\nvar gOPD = $GOPD.f;\nvar dP = $DP.f;\nvar gOPN = gOPNExt.f;\nvar $Symbol = global.Symbol;\nvar $JSON = global.JSON;\nvar _stringify = $JSON && $JSON.stringify;\nvar PROTOTYPE = 'prototype';\nvar HIDDEN = wks('_hidden');\nvar TO_PRIMITIVE = wks('toPrimitive');\nvar isEnum = {}.propertyIsEnumerable;\nvar SymbolRegistry = shared('symbol-registry');\nvar AllSymbols = shared('symbols');\nvar OPSymbols = shared('op-symbols');\nvar ObjectProto = Object[PROTOTYPE];\nvar USE_NATIVE = typeof $Symbol == 'function';\nvar QObject = global.QObject;\n// Don't use setters in Qt Script, https://github.com/zloirock/core-js/issues/173\nvar setter = !QObject || !QObject[PROTOTYPE] || !QObject[PROTOTYPE].findChild;\n\n// fallback for old Android, https://code.google.com/p/v8/issues/detail?id=687\nvar setSymbolDesc = DESCRIPTORS && $fails(function () {\n return _create(dP({}, 'a', {\n get: function () { return dP(this, 'a', { value: 7 }).a; }\n })).a != 7;\n}) ? function (it, key, D) {\n var protoDesc = gOPD(ObjectProto, key);\n if (protoDesc) delete ObjectProto[key];\n dP(it, key, D);\n if (protoDesc && it !== ObjectProto) dP(ObjectProto, key, protoDesc);\n} : dP;\n\nvar wrap = function (tag) {\n var sym = AllSymbols[tag] = _create($Symbol[PROTOTYPE]);\n sym._k = tag;\n return sym;\n};\n\nvar isSymbol = USE_NATIVE && typeof $Symbol.iterator == 'symbol' ? function (it) {\n return typeof it == 'symbol';\n} : function (it) {\n return it instanceof $Symbol;\n};\n\nvar $defineProperty = function defineProperty(it, key, D) {\n if (it === ObjectProto) $defineProperty(OPSymbols, key, D);\n anObject(it);\n key = toPrimitive(key, true);\n anObject(D);\n if (has(AllSymbols, key)) {\n if (!D.enumerable) {\n if (!has(it, HIDDEN)) dP(it, HIDDEN, createDesc(1, {}));\n it[HIDDEN][key] = true;\n } else {\n if (has(it, HIDDEN) && it[HIDDEN][key]) it[HIDDEN][key] = false;\n D = _create(D, { enumerable: createDesc(0, false) });\n } return setSymbolDesc(it, key, D);\n } return dP(it, key, D);\n};\nvar $defineProperties = function defineProperties(it, P) {\n anObject(it);\n var keys = enumKeys(P = toIObject(P));\n var i = 0;\n var l = keys.length;\n var key;\n while (l > i) $defineProperty(it, key = keys[i++], P[key]);\n return it;\n};\nvar $create = function create(it, P) {\n return P === undefined ? _create(it) : $defineProperties(_create(it), P);\n};\nvar $propertyIsEnumerable = function propertyIsEnumerable(key) {\n var E = isEnum.call(this, key = toPrimitive(key, true));\n if (this === ObjectProto && has(AllSymbols, key) && !has(OPSymbols, key)) return false;\n return E || !has(this, key) || !has(AllSymbols, key) || has(this, HIDDEN) && this[HIDDEN][key] ? E : true;\n};\nvar $getOwnPropertyDescriptor = function getOwnPropertyDescriptor(it, key) {\n it = toIObject(it);\n key = toPrimitive(key, true);\n if (it === ObjectProto && has(AllSymbols, key) && !has(OPSymbols, key)) return;\n var D = gOPD(it, key);\n if (D && has(AllSymbols, key) && !(has(it, HIDDEN) && it[HIDDEN][key])) D.enumerable = true;\n return D;\n};\nvar $getOwnPropertyNames = function getOwnPropertyNames(it) {\n var names = gOPN(toIObject(it));\n var result = [];\n var i = 0;\n var key;\n while (names.length > i) {\n if (!has(AllSymbols, key = names[i++]) && key != HIDDEN && key != META) result.push(key);\n } return result;\n};\nvar $getOwnPropertySymbols = function getOwnPropertySymbols(it) {\n var IS_OP = it === ObjectProto;\n var names = gOPN(IS_OP ? OPSymbols : toIObject(it));\n var result = [];\n var i = 0;\n var key;\n while (names.length > i) {\n if (has(AllSymbols, key = names[i++]) && (IS_OP ? has(ObjectProto, key) : true)) result.push(AllSymbols[key]);\n } return result;\n};\n\n// 19.4.1.1 Symbol([description])\nif (!USE_NATIVE) {\n $Symbol = function Symbol() {\n if (this instanceof $Symbol) throw TypeError('Symbol is not a constructor!');\n var tag = uid(arguments.length > 0 ? arguments[0] : undefined);\n var $set = function (value) {\n if (this === ObjectProto) $set.call(OPSymbols, value);\n if (has(this, HIDDEN) && has(this[HIDDEN], tag)) this[HIDDEN][tag] = false;\n setSymbolDesc(this, tag, createDesc(1, value));\n };\n if (DESCRIPTORS && setter) setSymbolDesc(ObjectProto, tag, { configurable: true, set: $set });\n return wrap(tag);\n };\n redefine($Symbol[PROTOTYPE], 'toString', function toString() {\n return this._k;\n });\n\n $GOPD.f = $getOwnPropertyDescriptor;\n $DP.f = $defineProperty;\n require('./_object-gopn').f = gOPNExt.f = $getOwnPropertyNames;\n require('./_object-pie').f = $propertyIsEnumerable;\n require('./_object-gops').f = $getOwnPropertySymbols;\n\n if (DESCRIPTORS && !require('./_library')) {\n redefine(ObjectProto, 'propertyIsEnumerable', $propertyIsEnumerable, true);\n }\n\n wksExt.f = function (name) {\n return wrap(wks(name));\n };\n}\n\n$export($export.G + $export.W + $export.F * !USE_NATIVE, { Symbol: $Symbol });\n\nfor (var es6Symbols = (\n // 19.4.2.2, 19.4.2.3, 19.4.2.4, 19.4.2.6, 19.4.2.8, 19.4.2.9, 19.4.2.10, 19.4.2.11, 19.4.2.12, 19.4.2.13, 19.4.2.14\n 'hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables'\n).split(','), j = 0; es6Symbols.length > j;)wks(es6Symbols[j++]);\n\nfor (var wellKnownSymbols = $keys(wks.store), k = 0; wellKnownSymbols.length > k;) wksDefine(wellKnownSymbols[k++]);\n\n$export($export.S + $export.F * !USE_NATIVE, 'Symbol', {\n // 19.4.2.1 Symbol.for(key)\n 'for': function (key) {\n return has(SymbolRegistry, key += '')\n ? SymbolRegistry[key]\n : SymbolRegistry[key] = $Symbol(key);\n },\n // 19.4.2.5 Symbol.keyFor(sym)\n keyFor: function keyFor(sym) {\n if (!isSymbol(sym)) throw TypeError(sym + ' is not a symbol!');\n for (var key in SymbolRegistry) if (SymbolRegistry[key] === sym) return key;\n },\n useSetter: function () { setter = true; },\n useSimple: function () { setter = false; }\n});\n\n$export($export.S + $export.F * !USE_NATIVE, 'Object', {\n // 19.1.2.2 Object.create(O [, Properties])\n create: $create,\n // 19.1.2.4 Object.defineProperty(O, P, Attributes)\n defineProperty: $defineProperty,\n // 19.1.2.3 Object.defineProperties(O, Properties)\n defineProperties: $defineProperties,\n // 19.1.2.6 Object.getOwnPropertyDescriptor(O, P)\n getOwnPropertyDescriptor: $getOwnPropertyDescriptor,\n // 19.1.2.7 Object.getOwnPropertyNames(O)\n getOwnPropertyNames: $getOwnPropertyNames,\n // 19.1.2.8 Object.getOwnPropertySymbols(O)\n getOwnPropertySymbols: $getOwnPropertySymbols\n});\n\n// 24.3.2 JSON.stringify(value [, replacer [, space]])\n$JSON && $export($export.S + $export.F * (!USE_NATIVE || $fails(function () {\n var S = $Symbol();\n // MS Edge converts symbol values to JSON as {}\n // WebKit converts symbol values to JSON as null\n // V8 throws on boxed symbols\n return _stringify([S]) != '[null]' || _stringify({ a: S }) != '{}' || _stringify(Object(S)) != '{}';\n})), 'JSON', {\n stringify: function stringify(it) {\n var args = [it];\n var i = 1;\n var replacer, $replacer;\n while (arguments.length > i) args.push(arguments[i++]);\n $replacer = replacer = args[1];\n if (!isObject(replacer) && it === undefined || isSymbol(it)) return; // IE8 returns string on undefined\n if (!isArray(replacer)) replacer = function (key, value) {\n if (typeof $replacer == 'function') value = $replacer.call(this, key, value);\n if (!isSymbol(value)) return value;\n };\n args[1] = replacer;\n return _stringify.apply($JSON, args);\n }\n});\n\n// 19.4.3.4 Symbol.prototype[@@toPrimitive](hint)\n$Symbol[PROTOTYPE][TO_PRIMITIVE] || require('./_hide')($Symbol[PROTOTYPE], TO_PRIMITIVE, $Symbol[PROTOTYPE].valueOf);\n// 19.4.3.5 Symbol.prototype[@@toStringTag]\nsetToStringTag($Symbol, 'Symbol');\n// 20.2.1.9 Math[@@toStringTag]\nsetToStringTag(Math, 'Math', true);\n// 24.3.3 JSON[@@toStringTag]\nsetToStringTag(global.JSON, 'JSON', true);\n","var anObject = require('./_an-object');\nvar isObject = require('./_is-object');\nvar newPromiseCapability = require('./_new-promise-capability');\n\nmodule.exports = function (C, x) {\n anObject(C);\n if (isObject(x) && x.constructor === C) return x;\n var promiseCapability = newPromiseCapability.f(C);\n var resolve = promiseCapability.resolve;\n resolve(x);\n return promiseCapability.promise;\n};\n","module.exports = function (exec) {\n try {\n return { e: false, v: exec() };\n } catch (e) {\n return { e: true, v: e };\n }\n};\n","var global = require('./_global');\nvar macrotask = require('./_task').set;\nvar Observer = global.MutationObserver || global.WebKitMutationObserver;\nvar process = global.process;\nvar Promise = global.Promise;\nvar isNode = require('./_cof')(process) == 'process';\n\nmodule.exports = function () {\n var head, last, notify;\n\n var flush = function () {\n var parent, fn;\n if (isNode && (parent = process.domain)) parent.exit();\n while (head) {\n fn = head.fn;\n head = head.next;\n try {\n fn();\n } catch (e) {\n if (head) notify();\n else last = undefined;\n throw e;\n }\n } last = undefined;\n if (parent) parent.enter();\n };\n\n // Node.js\n if (isNode) {\n notify = function () {\n process.nextTick(flush);\n };\n // browsers with MutationObserver, except iOS Safari - https://github.com/zloirock/core-js/issues/339\n } else if (Observer && !(global.navigator && global.navigator.standalone)) {\n var toggle = true;\n var node = document.createTextNode('');\n new Observer(flush).observe(node, { characterData: true }); // eslint-disable-line no-new\n notify = function () {\n node.data = toggle = !toggle;\n };\n // environments with maybe non-completely correct, but existent Promise\n } else if (Promise && Promise.resolve) {\n var promise = Promise.resolve();\n notify = function () {\n promise.then(flush);\n };\n // for other environments - macrotask based on:\n // - setImmediate\n // - MessageChannel\n // - window.postMessag\n // - onreadystatechange\n // - setTimeout\n } else {\n notify = function () {\n // strange IE + webpack dev server bug - use .call(global)\n macrotask.call(global, flush);\n };\n }\n\n return function (fn) {\n var task = { fn: fn, next: undefined };\n if (last) last.next = task;\n if (!head) {\n head = task;\n notify();\n } last = task;\n };\n};\n","'use strict';\nvar LIBRARY = require('./_library');\nvar global = require('./_global');\nvar ctx = require('./_ctx');\nvar classof = require('./_classof');\nvar $export = require('./_export');\nvar isObject = require('./_is-object');\nvar aFunction = require('./_a-function');\nvar anInstance = require('./_an-instance');\nvar forOf = require('./_for-of');\nvar speciesConstructor = require('./_species-constructor');\nvar task = require('./_task').set;\nvar microtask = require('./_microtask')();\nvar newPromiseCapabilityModule = require('./_new-promise-capability');\nvar perform = require('./_perform');\nvar promiseResolve = require('./_promise-resolve');\nvar PROMISE = 'Promise';\nvar TypeError = global.TypeError;\nvar process = global.process;\nvar $Promise = global[PROMISE];\nvar isNode = classof(process) == 'process';\nvar empty = function () { /* empty */ };\nvar Internal, newGenericPromiseCapability, OwnPromiseCapability, Wrapper;\nvar newPromiseCapability = newGenericPromiseCapability = newPromiseCapabilityModule.f;\n\nvar USE_NATIVE = !!function () {\n try {\n // correct subclassing with @@species support\n var promise = $Promise.resolve(1);\n var FakePromise = (promise.constructor = {})[require('./_wks')('species')] = function (exec) {\n exec(empty, empty);\n };\n // unhandled rejections tracking support, NodeJS Promise without it fails @@species test\n return (isNode || typeof PromiseRejectionEvent == 'function') && promise.then(empty) instanceof FakePromise;\n } catch (e) { /* empty */ }\n}();\n\n// helpers\nvar isThenable = function (it) {\n var then;\n return isObject(it) && typeof (then = it.then) == 'function' ? then : false;\n};\nvar notify = function (promise, isReject) {\n if (promise._n) return;\n promise._n = true;\n var chain = promise._c;\n microtask(function () {\n var value = promise._v;\n var ok = promise._s == 1;\n var i = 0;\n var run = function (reaction) {\n var handler = ok ? reaction.ok : reaction.fail;\n var resolve = reaction.resolve;\n var reject = reaction.reject;\n var domain = reaction.domain;\n var result, then, exited;\n try {\n if (handler) {\n if (!ok) {\n if (promise._h == 2) onHandleUnhandled(promise);\n promise._h = 1;\n }\n if (handler === true) result = value;\n else {\n if (domain) domain.enter();\n result = handler(value); // may throw\n if (domain) {\n domain.exit();\n exited = true;\n }\n }\n if (result === reaction.promise) {\n reject(TypeError('Promise-chain cycle'));\n } else if (then = isThenable(result)) {\n then.call(result, resolve, reject);\n } else resolve(result);\n } else reject(value);\n } catch (e) {\n if (domain && !exited) domain.exit();\n reject(e);\n }\n };\n while (chain.length > i) run(chain[i++]); // variable length - can't use forEach\n promise._c = [];\n promise._n = false;\n if (isReject && !promise._h) onUnhandled(promise);\n });\n};\nvar onUnhandled = function (promise) {\n task.call(global, function () {\n var value = promise._v;\n var unhandled = isUnhandled(promise);\n var result, handler, console;\n if (unhandled) {\n result = perform(function () {\n if (isNode) {\n process.emit('unhandledRejection', value, promise);\n } else if (handler = global.onunhandledrejection) {\n handler({ promise: promise, reason: value });\n } else if ((console = global.console) && console.error) {\n console.error('Unhandled promise rejection', value);\n }\n });\n // Browsers should not trigger `rejectionHandled` event if it was handled here, NodeJS - should\n promise._h = isNode || isUnhandled(promise) ? 2 : 1;\n } promise._a = undefined;\n if (unhandled && result.e) throw result.v;\n });\n};\nvar isUnhandled = function (promise) {\n return promise._h !== 1 && (promise._a || promise._c).length === 0;\n};\nvar onHandleUnhandled = function (promise) {\n task.call(global, function () {\n var handler;\n if (isNode) {\n process.emit('rejectionHandled', promise);\n } else if (handler = global.onrejectionhandled) {\n handler({ promise: promise, reason: promise._v });\n }\n });\n};\nvar $reject = function (value) {\n var promise = this;\n if (promise._d) return;\n promise._d = true;\n promise = promise._w || promise; // unwrap\n promise._v = value;\n promise._s = 2;\n if (!promise._a) promise._a = promise._c.slice();\n notify(promise, true);\n};\nvar $resolve = function (value) {\n var promise = this;\n var then;\n if (promise._d) return;\n promise._d = true;\n promise = promise._w || promise; // unwrap\n try {\n if (promise === value) throw TypeError(\"Promise can't be resolved itself\");\n if (then = isThenable(value)) {\n microtask(function () {\n var wrapper = { _w: promise, _d: false }; // wrap\n try {\n then.call(value, ctx($resolve, wrapper, 1), ctx($reject, wrapper, 1));\n } catch (e) {\n $reject.call(wrapper, e);\n }\n });\n } else {\n promise._v = value;\n promise._s = 1;\n notify(promise, false);\n }\n } catch (e) {\n $reject.call({ _w: promise, _d: false }, e); // wrap\n }\n};\n\n// constructor polyfill\nif (!USE_NATIVE) {\n // 25.4.3.1 Promise(executor)\n $Promise = function Promise(executor) {\n anInstance(this, $Promise, PROMISE, '_h');\n aFunction(executor);\n Internal.call(this);\n try {\n executor(ctx($resolve, this, 1), ctx($reject, this, 1));\n } catch (err) {\n $reject.call(this, err);\n }\n };\n // eslint-disable-next-line no-unused-vars\n Internal = function Promise(executor) {\n this._c = []; // <- awaiting reactions\n this._a = undefined; // <- checked in isUnhandled reactions\n this._s = 0; // <- state\n this._d = false; // <- done\n this._v = undefined; // <- value\n this._h = 0; // <- rejection state, 0 - default, 1 - handled, 2 - unhandled\n this._n = false; // <- notify\n };\n Internal.prototype = require('./_redefine-all')($Promise.prototype, {\n // 25.4.5.3 Promise.prototype.then(onFulfilled, onRejected)\n then: function then(onFulfilled, onRejected) {\n var reaction = newPromiseCapability(speciesConstructor(this, $Promise));\n reaction.ok = typeof onFulfilled == 'function' ? onFulfilled : true;\n reaction.fail = typeof onRejected == 'function' && onRejected;\n reaction.domain = isNode ? process.domain : undefined;\n this._c.push(reaction);\n if (this._a) this._a.push(reaction);\n if (this._s) notify(this, false);\n return reaction.promise;\n },\n // 25.4.5.1 Promise.prototype.catch(onRejected)\n 'catch': function (onRejected) {\n return this.then(undefined, onRejected);\n }\n });\n OwnPromiseCapability = function () {\n var promise = new Internal();\n this.promise = promise;\n this.resolve = ctx($resolve, promise, 1);\n this.reject = ctx($reject, promise, 1);\n };\n newPromiseCapabilityModule.f = newPromiseCapability = function (C) {\n return C === $Promise || C === Wrapper\n ? new OwnPromiseCapability(C)\n : newGenericPromiseCapability(C);\n };\n}\n\n$export($export.G + $export.W + $export.F * !USE_NATIVE, { Promise: $Promise });\nrequire('./_set-to-string-tag')($Promise, PROMISE);\nrequire('./_set-species')(PROMISE);\nWrapper = require('./_core')[PROMISE];\n\n// statics\n$export($export.S + $export.F * !USE_NATIVE, PROMISE, {\n // 25.4.4.5 Promise.reject(r)\n reject: function reject(r) {\n var capability = newPromiseCapability(this);\n var $$reject = capability.reject;\n $$reject(r);\n return capability.promise;\n }\n});\n$export($export.S + $export.F * (LIBRARY || !USE_NATIVE), PROMISE, {\n // 25.4.4.6 Promise.resolve(x)\n resolve: function resolve(x) {\n return promiseResolve(LIBRARY && this === Wrapper ? $Promise : this, x);\n }\n});\n$export($export.S + $export.F * !(USE_NATIVE && require('./_iter-detect')(function (iter) {\n $Promise.all(iter)['catch'](empty);\n})), PROMISE, {\n // 25.4.4.1 Promise.all(iterable)\n all: function all(iterable) {\n var C = this;\n var capability = newPromiseCapability(C);\n var resolve = capability.resolve;\n var reject = capability.reject;\n var result = perform(function () {\n var values = [];\n var index = 0;\n var remaining = 1;\n forOf(iterable, false, function (promise) {\n var $index = index++;\n var alreadyCalled = false;\n values.push(undefined);\n remaining++;\n C.resolve(promise).then(function (value) {\n if (alreadyCalled) return;\n alreadyCalled = true;\n values[$index] = value;\n --remaining || resolve(values);\n }, reject);\n });\n --remaining || resolve(values);\n });\n if (result.e) reject(result.v);\n return capability.promise;\n },\n // 25.4.4.4 Promise.race(iterable)\n race: function race(iterable) {\n var C = this;\n var capability = newPromiseCapability(C);\n var reject = capability.reject;\n var result = perform(function () {\n forOf(iterable, false, function (promise) {\n C.resolve(promise).then(capability.resolve, reject);\n });\n });\n if (result.e) reject(result.v);\n return capability.promise;\n }\n});\n","// 26.1.14 Reflect.setPrototypeOf(target, proto)\nvar $export = require('./_export');\nvar setProto = require('./_set-proto');\n\nif (setProto) $export($export.S, 'Reflect', {\n setPrototypeOf: function setPrototypeOf(target, proto) {\n setProto.check(target, proto);\n try {\n setProto.set(target, proto);\n return true;\n } catch (e) {\n return false;\n }\n }\n});\n","// 26.1.13 Reflect.set(target, propertyKey, V [, receiver])\nvar dP = require('./_object-dp');\nvar gOPD = require('./_object-gopd');\nvar getPrototypeOf = require('./_object-gpo');\nvar has = require('./_has');\nvar $export = require('./_export');\nvar createDesc = require('./_property-desc');\nvar anObject = require('./_an-object');\nvar isObject = require('./_is-object');\n\nfunction set(target, propertyKey, V /* , receiver */) {\n var receiver = arguments.length < 4 ? target : arguments[3];\n var ownDesc = gOPD.f(anObject(target), propertyKey);\n var existingDescriptor, proto;\n if (!ownDesc) {\n if (isObject(proto = getPrototypeOf(target))) {\n return set(proto, propertyKey, V, receiver);\n }\n ownDesc = createDesc(0);\n }\n if (has(ownDesc, 'value')) {\n if (ownDesc.writable === false || !isObject(receiver)) return false;\n if (existingDescriptor = gOPD.f(receiver, propertyKey)) {\n if (existingDescriptor.get || existingDescriptor.set || existingDescriptor.writable === false) return false;\n existingDescriptor.value = V;\n dP.f(receiver, propertyKey, existingDescriptor);\n } else dP.f(receiver, propertyKey, createDesc(0, V));\n return true;\n }\n return ownDesc.set === undefined ? false : (ownDesc.set.call(receiver, V), true);\n}\n\n$export($export.S, 'Reflect', { set: set });\n","// 26.1.12 Reflect.preventExtensions(target)\nvar $export = require('./_export');\nvar anObject = require('./_an-object');\nvar $preventExtensions = Object.preventExtensions;\n\n$export($export.S, 'Reflect', {\n preventExtensions: function preventExtensions(target) {\n anObject(target);\n try {\n if ($preventExtensions) $preventExtensions(target);\n return true;\n } catch (e) {\n return false;\n }\n }\n});\n","// 26.1.11 Reflect.ownKeys(target)\nvar $export = require('./_export');\n\n$export($export.S, 'Reflect', { ownKeys: require('./_own-keys') });\n","// 26.1.10 Reflect.isExtensible(target)\nvar $export = require('./_export');\nvar anObject = require('./_an-object');\nvar $isExtensible = Object.isExtensible;\n\n$export($export.S, 'Reflect', {\n isExtensible: function isExtensible(target) {\n anObject(target);\n return $isExtensible ? $isExtensible(target) : true;\n }\n});\n","// 26.1.9 Reflect.has(target, propertyKey)\nvar $export = require('./_export');\n\n$export($export.S, 'Reflect', {\n has: function has(target, propertyKey) {\n return propertyKey in target;\n }\n});\n","// 26.1.8 Reflect.getPrototypeOf(target)\nvar $export = require('./_export');\nvar getProto = require('./_object-gpo');\nvar anObject = require('./_an-object');\n\n$export($export.S, 'Reflect', {\n getPrototypeOf: function getPrototypeOf(target) {\n return getProto(anObject(target));\n }\n});\n","// 26.1.7 Reflect.getOwnPropertyDescriptor(target, propertyKey)\nvar gOPD = require('./_object-gopd');\nvar $export = require('./_export');\nvar anObject = require('./_an-object');\n\n$export($export.S, 'Reflect', {\n getOwnPropertyDescriptor: function getOwnPropertyDescriptor(target, propertyKey) {\n return gOPD.f(anObject(target), propertyKey);\n }\n});\n","// 26.1.6 Reflect.get(target, propertyKey [, receiver])\nvar gOPD = require('./_object-gopd');\nvar getPrototypeOf = require('./_object-gpo');\nvar has = require('./_has');\nvar $export = require('./_export');\nvar isObject = require('./_is-object');\nvar anObject = require('./_an-object');\n\nfunction get(target, propertyKey /* , receiver */) {\n var receiver = arguments.length < 3 ? target : arguments[2];\n var desc, proto;\n if (anObject(target) === receiver) return target[propertyKey];\n if (desc = gOPD.f(target, propertyKey)) return has(desc, 'value')\n ? desc.value\n : desc.get !== undefined\n ? desc.get.call(receiver)\n : undefined;\n if (isObject(proto = getPrototypeOf(target))) return get(proto, propertyKey, receiver);\n}\n\n$export($export.S, 'Reflect', { get: get });\n","// 26.1.4 Reflect.deleteProperty(target, propertyKey)\nvar $export = require('./_export');\nvar gOPD = require('./_object-gopd').f;\nvar anObject = require('./_an-object');\n\n$export($export.S, 'Reflect', {\n deleteProperty: function deleteProperty(target, propertyKey) {\n var desc = gOPD(anObject(target), propertyKey);\n return desc && !desc.configurable ? false : delete target[propertyKey];\n }\n});\n","// 26.1.3 Reflect.defineProperty(target, propertyKey, attributes)\nvar dP = require('./_object-dp');\nvar $export = require('./_export');\nvar anObject = require('./_an-object');\nvar toPrimitive = require('./_to-primitive');\n\n// MS Edge has broken Reflect.defineProperty - throwing instead of returning false\n$export($export.S + $export.F * require('./_fails')(function () {\n // eslint-disable-next-line no-undef\n Reflect.defineProperty(dP.f({}, 1, { value: 1 }), 1, { value: 2 });\n}), 'Reflect', {\n defineProperty: function defineProperty(target, propertyKey, attributes) {\n anObject(target);\n propertyKey = toPrimitive(propertyKey, true);\n anObject(attributes);\n try {\n dP.f(target, propertyKey, attributes);\n return true;\n } catch (e) {\n return false;\n }\n }\n});\n","'use strict';\nvar aFunction = require('./_a-function');\nvar isObject = require('./_is-object');\nvar invoke = require('./_invoke');\nvar arraySlice = [].slice;\nvar factories = {};\n\nvar construct = function (F, len, args) {\n if (!(len in factories)) {\n for (var n = [], i = 0; i < len; i++) n[i] = 'a[' + i + ']';\n // eslint-disable-next-line no-new-func\n factories[len] = Function('F,a', 'return new F(' + n.join(',') + ')');\n } return factories[len](F, args);\n};\n\nmodule.exports = Function.bind || function bind(that /* , ...args */) {\n var fn = aFunction(this);\n var partArgs = arraySlice.call(arguments, 1);\n var bound = function (/* args... */) {\n var args = partArgs.concat(arraySlice.call(arguments));\n return this instanceof bound ? construct(fn, args.length, args) : invoke(fn, args, that);\n };\n if (isObject(fn.prototype)) bound.prototype = fn.prototype;\n return bound;\n};\n","// 26.1.2 Reflect.construct(target, argumentsList [, newTarget])\nvar $export = require('./_export');\nvar create = require('./_object-create');\nvar aFunction = require('./_a-function');\nvar anObject = require('./_an-object');\nvar isObject = require('./_is-object');\nvar fails = require('./_fails');\nvar bind = require('./_bind');\nvar rConstruct = (require('./_global').Reflect || {}).construct;\n\n// MS Edge supports only 2 arguments and argumentsList argument is optional\n// FF Nightly sets third argument as `new.target`, but does not create `this` from it\nvar NEW_TARGET_BUG = fails(function () {\n function F() { /* empty */ }\n return !(rConstruct(function () { /* empty */ }, [], F) instanceof F);\n});\nvar ARGS_BUG = !fails(function () {\n rConstruct(function () { /* empty */ });\n});\n\n$export($export.S + $export.F * (NEW_TARGET_BUG || ARGS_BUG), 'Reflect', {\n construct: function construct(Target, args /* , newTarget */) {\n aFunction(Target);\n anObject(args);\n var newTarget = arguments.length < 3 ? Target : aFunction(arguments[2]);\n if (ARGS_BUG && !NEW_TARGET_BUG) return rConstruct(Target, args, newTarget);\n if (Target == newTarget) {\n // w/o altered newTarget, optimization for 0-4 arguments\n switch (args.length) {\n case 0: return new Target();\n case 1: return new Target(args[0]);\n case 2: return new Target(args[0], args[1]);\n case 3: return new Target(args[0], args[1], args[2]);\n case 4: return new Target(args[0], args[1], args[2], args[3]);\n }\n // w/o altered newTarget, lot of arguments case\n var $args = [null];\n $args.push.apply($args, args);\n return new (bind.apply(Target, $args))();\n }\n // with altered newTarget, not support built-in constructors\n var proto = newTarget.prototype;\n var instance = create(isObject(proto) ? proto : Object.prototype);\n var result = Function.apply.call(Target, instance, args);\n return isObject(result) ? result : instance;\n }\n});\n","// 26.1.1 Reflect.apply(target, thisArgument, argumentsList)\nvar $export = require('./_export');\nvar aFunction = require('./_a-function');\nvar anObject = require('./_an-object');\nvar rApply = (require('./_global').Reflect || {}).apply;\nvar fApply = Function.apply;\n// MS Edge argumentsList argument is optional\n$export($export.S + $export.F * !require('./_fails')(function () {\n rApply(function () { /* empty */ });\n}), 'Reflect', {\n apply: function apply(target, thisArgument, argumentsList) {\n var T = aFunction(target);\n var L = anObject(argumentsList);\n return rApply ? rApply(T, thisArgument, L) : fApply.call(T, thisArgument, L);\n }\n});\n","'use strict';\nvar weak = require('./_collection-weak');\nvar validate = require('./_validate-collection');\nvar WEAK_SET = 'WeakSet';\n\n// 23.4 WeakSet Objects\nrequire('./_collection')(WEAK_SET, function (get) {\n return function WeakSet() { return get(this, arguments.length > 0 ? arguments[0] : undefined); };\n}, {\n // 23.4.3.1 WeakSet.prototype.add(value)\n add: function add(value) {\n return weak.def(validate(this, WEAK_SET), value, true);\n }\n}, weak, false, true);\n","'use strict';\nvar each = require('./_array-methods')(0);\nvar redefine = require('./_redefine');\nvar meta = require('./_meta');\nvar assign = require('./_object-assign');\nvar weak = require('./_collection-weak');\nvar isObject = require('./_is-object');\nvar fails = require('./_fails');\nvar validate = require('./_validate-collection');\nvar WEAK_MAP = 'WeakMap';\nvar getWeak = meta.getWeak;\nvar isExtensible = Object.isExtensible;\nvar uncaughtFrozenStore = weak.ufstore;\nvar tmp = {};\nvar InternalMap;\n\nvar wrapper = function (get) {\n return function WeakMap() {\n return get(this, arguments.length > 0 ? arguments[0] : undefined);\n };\n};\n\nvar methods = {\n // 23.3.3.3 WeakMap.prototype.get(key)\n get: function get(key) {\n if (isObject(key)) {\n var data = getWeak(key);\n if (data === true) return uncaughtFrozenStore(validate(this, WEAK_MAP)).get(key);\n return data ? data[this._i] : undefined;\n }\n },\n // 23.3.3.5 WeakMap.prototype.set(key, value)\n set: function set(key, value) {\n return weak.def(validate(this, WEAK_MAP), key, value);\n }\n};\n\n// 23.3 WeakMap Objects\nvar $WeakMap = module.exports = require('./_collection')(WEAK_MAP, wrapper, methods, weak, true, true);\n\n// IE11 WeakMap frozen keys fix\nif (fails(function () { return new $WeakMap().set((Object.freeze || Object)(tmp), 7).get(tmp) != 7; })) {\n InternalMap = weak.getConstructor(wrapper, WEAK_MAP);\n assign(InternalMap.prototype, methods);\n meta.NEED = true;\n each(['delete', 'has', 'get', 'set'], function (key) {\n var proto = $WeakMap.prototype;\n var method = proto[key];\n redefine(proto, key, function (a, b) {\n // store frozen objects on internal weakmap shim\n if (isObject(a) && !isExtensible(a)) {\n if (!this._f) this._f = new InternalMap();\n var result = this._f[key](a, b);\n return key == 'set' ? this : result;\n // store all the rest on native weakmap\n } return method.call(this, a, b);\n });\n });\n}\n","'use strict';\nvar strong = require('./_collection-strong');\nvar validate = require('./_validate-collection');\nvar SET = 'Set';\n\n// 23.2 Set Objects\nmodule.exports = require('./_collection')(SET, function (get) {\n return function Set() { return get(this, arguments.length > 0 ? arguments[0] : undefined); };\n}, {\n // 23.2.3.1 Set.prototype.add(value)\n add: function add(value) {\n return strong.def(validate(this, SET), value = value === 0 ? 0 : value, value);\n }\n}, strong);\n","var isObject = require('./_is-object');\nvar setPrototypeOf = require('./_set-proto').set;\nmodule.exports = function (that, target, C) {\n var S = target.constructor;\n var P;\n if (S !== C && typeof S == 'function' && (P = S.prototype) !== C.prototype && isObject(P) && setPrototypeOf) {\n setPrototypeOf(that, P);\n } return that;\n};\n","'use strict';\nvar strong = require('./_collection-strong');\nvar validate = require('./_validate-collection');\nvar MAP = 'Map';\n\n// 23.1 Map Objects\nmodule.exports = require('./_collection')(MAP, function (get) {\n return function Map() { return get(this, arguments.length > 0 ? arguments[0] : undefined); };\n}, {\n // 23.1.3.6 Map.prototype.get(key)\n get: function get(key) {\n var entry = strong.getEntry(validate(this, MAP), key);\n return entry && entry.v;\n },\n // 23.1.3.9 Map.prototype.set(key, value)\n set: function set(key, value) {\n return strong.def(validate(this, MAP), key === 0 ? 0 : key, value);\n }\n}, strong, true);\n","require('./_typed-array')('Float64', 8, function (init) {\n return function Float64Array(data, byteOffset, length) {\n return init(this, data, byteOffset, length);\n };\n});\n","require('./_typed-array')('Float32', 4, function (init) {\n return function Float32Array(data, byteOffset, length) {\n return init(this, data, byteOffset, length);\n };\n});\n","require('./_typed-array')('Uint32', 4, function (init) {\n return function Uint32Array(data, byteOffset, length) {\n return init(this, data, byteOffset, length);\n };\n});\n","require('./_typed-array')('Int32', 4, function (init) {\n return function Int32Array(data, byteOffset, length) {\n return init(this, data, byteOffset, length);\n };\n});\n","require('./_typed-array')('Uint16', 2, function (init) {\n return function Uint16Array(data, byteOffset, length) {\n return init(this, data, byteOffset, length);\n };\n});\n","require('./_typed-array')('Int16', 2, function (init) {\n return function Int16Array(data, byteOffset, length) {\n return init(this, data, byteOffset, length);\n };\n});\n","require('./_typed-array')('Uint8', 1, function (init) {\n return function Uint8ClampedArray(data, byteOffset, length) {\n return init(this, data, byteOffset, length);\n };\n}, true);\n","require('./_typed-array')('Uint8', 1, function (init) {\n return function Uint8Array(data, byteOffset, length) {\n return init(this, data, byteOffset, length);\n };\n});\n","'use strict';\nvar create = require('./_object-create');\nvar descriptor = require('./_property-desc');\nvar setToStringTag = require('./_set-to-string-tag');\nvar IteratorPrototype = {};\n\n// 25.1.2.1.1 %IteratorPrototype%[@@iterator]()\nrequire('./_hide')(IteratorPrototype, require('./_wks')('iterator'), function () { return this; });\n\nmodule.exports = function (Constructor, NAME, next) {\n Constructor.prototype = create(IteratorPrototype, { next: descriptor(1, next) });\n setToStringTag(Constructor, NAME + ' Iterator');\n};\n","var isObject = require('./_is-object');\nvar isArray = require('./_is-array');\nvar SPECIES = require('./_wks')('species');\n\nmodule.exports = function (original) {\n var C;\n if (isArray(original)) {\n C = original.constructor;\n // cross-realm fallback\n if (typeof C == 'function' && (C === Array || isArray(C.prototype))) C = undefined;\n if (isObject(C)) {\n C = C[SPECIES];\n if (C === null) C = undefined;\n }\n } return C === undefined ? Array : C;\n};\n","// 9.4.2.3 ArraySpeciesCreate(originalArray, length)\nvar speciesConstructor = require('./_array-species-constructor');\n\nmodule.exports = function (original, length) {\n return new (speciesConstructor(original))(length);\n};\n","var dP = require('./_object-dp');\nvar anObject = require('./_an-object');\nvar getKeys = require('./_object-keys');\n\nmodule.exports = require('./_descriptors') ? Object.defineProperties : function defineProperties(O, Properties) {\n anObject(O);\n var keys = getKeys(Properties);\n var length = keys.length;\n var i = 0;\n var P;\n while (length > i) dP.f(O, P = keys[i++], Properties[P]);\n return O;\n};\n","require('./_typed-array')('Int8', 1, function (init) {\n return function Int8Array(data, byteOffset, length) {\n return init(this, data, byteOffset, length);\n };\n});\n","'use strict';\nvar $export = require('./_export');\nvar $typed = require('./_typed');\nvar buffer = require('./_typed-buffer');\nvar anObject = require('./_an-object');\nvar toAbsoluteIndex = require('./_to-absolute-index');\nvar toLength = require('./_to-length');\nvar isObject = require('./_is-object');\nvar ArrayBuffer = require('./_global').ArrayBuffer;\nvar speciesConstructor = require('./_species-constructor');\nvar $ArrayBuffer = buffer.ArrayBuffer;\nvar $DataView = buffer.DataView;\nvar $isView = $typed.ABV && ArrayBuffer.isView;\nvar $slice = $ArrayBuffer.prototype.slice;\nvar VIEW = $typed.VIEW;\nvar ARRAY_BUFFER = 'ArrayBuffer';\n\n$export($export.G + $export.W + $export.F * (ArrayBuffer !== $ArrayBuffer), { ArrayBuffer: $ArrayBuffer });\n\n$export($export.S + $export.F * !$typed.CONSTR, ARRAY_BUFFER, {\n // 24.1.3.1 ArrayBuffer.isView(arg)\n isView: function isView(it) {\n return $isView && $isView(it) || isObject(it) && VIEW in it;\n }\n});\n\n$export($export.P + $export.U + $export.F * require('./_fails')(function () {\n return !new $ArrayBuffer(2).slice(1, undefined).byteLength;\n}), ARRAY_BUFFER, {\n // 24.1.4.3 ArrayBuffer.prototype.slice(start, end)\n slice: function slice(start, end) {\n if ($slice !== undefined && end === undefined) return $slice.call(anObject(this), start); // FF fix\n var len = anObject(this).byteLength;\n var first = toAbsoluteIndex(start, len);\n var final = toAbsoluteIndex(end === undefined ? len : end, len);\n var result = new (speciesConstructor(this, $ArrayBuffer))(toLength(final - first));\n var viewS = new $DataView(this);\n var viewT = new $DataView(result);\n var index = 0;\n while (first < final) {\n viewT.setUint8(index++, viewS.getUint8(first++));\n } return result;\n }\n});\n\nrequire('./_set-species')(ARRAY_BUFFER);\n","'use strict';\n\n/**\n * Syntactic sugar for invoking a function and expanding an array for arguments.\n *\n * Common use case would be to use `Function.prototype.apply`.\n *\n * ```js\n * function f(x, y, z) {}\n * var args = [1, 2, 3];\n * f.apply(null, args);\n * ```\n *\n * With `spread` this example can be re-written.\n *\n * ```js\n * spread(function(x, y, z) {})([1, 2, 3]);\n * ```\n *\n * @param {Function} callback\n * @returns {Function}\n */\nmodule.exports = function spread(callback) {\n return function wrap(arr) {\n return callback.apply(null, arr);\n };\n};\n","'use strict';\n\nvar Cancel = require('./Cancel');\n\n/**\n * A `CancelToken` is an object that can be used to request cancellation of an operation.\n *\n * @class\n * @param {Function} executor The executor function.\n */\nfunction CancelToken(executor) {\n if (typeof executor !== 'function') {\n throw new TypeError('executor must be a function.');\n }\n\n var resolvePromise;\n this.promise = new Promise(function promiseExecutor(resolve) {\n resolvePromise = resolve;\n });\n\n var token = this;\n executor(function cancel(message) {\n if (token.reason) {\n // Cancellation has already been requested\n return;\n }\n\n token.reason = new Cancel(message);\n resolvePromise(token.reason);\n });\n}\n\n/**\n * Throws a `Cancel` if cancellation has been requested.\n */\nCancelToken.prototype.throwIfRequested = function throwIfRequested() {\n if (this.reason) {\n throw this.reason;\n }\n};\n\n/**\n * Returns an object that contains a new `CancelToken` and a function that, when called,\n * cancels the `CancelToken`.\n */\nCancelToken.source = function source() {\n var cancel;\n var token = new CancelToken(function executor(c) {\n cancel = c;\n });\n return {\n token: token,\n cancel: cancel\n };\n};\n\nmodule.exports = CancelToken;\n","'use strict';\n\n/**\n * Creates a new URL by combining the specified URLs\n *\n * @param {string} baseURL The base URL\n * @param {string} relativeURL The relative URL\n * @returns {string} The combined URL\n */\nmodule.exports = function combineURLs(baseURL, relativeURL) {\n return relativeURL\n ? baseURL.replace(/\\/+$/, '') + '/' + relativeURL.replace(/^\\/+/, '')\n : baseURL;\n};\n","'use strict';\n\n/**\n * Determines whether the specified URL is absolute\n *\n * @param {string} url The URL to test\n * @returns {boolean} True if the specified URL is absolute, otherwise false\n */\nmodule.exports = function isAbsoluteURL(url) {\n // A URL is considered absolute if it begins with \"://\" or \"//\" (protocol-relative URL).\n // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed\n // by any combination of letters, digits, plus, period, or hyphen.\n return /^([a-z][a-z\\d\\+\\-\\.]*:)?\\/\\//i.test(url);\n};\n","'use strict';\n\nvar utils = require('./../utils');\n\n/**\n * Transform the data for a request or a response\n *\n * @param {Object|String} data The data to be transformed\n * @param {Array} headers The headers for the request or response\n * @param {Array|Function} fns A single function or Array of functions\n * @returns {*} The resulting transformed data\n */\nmodule.exports = function transformData(data, headers, fns) {\n /*eslint no-param-reassign:0*/\n utils.forEach(fns, function transform(fn) {\n data = fn(data, headers);\n });\n\n return data;\n};\n","'use strict';\n\nvar utils = require('./../utils');\nvar transformData = require('./transformData');\nvar isCancel = require('../cancel/isCancel');\nvar defaults = require('../defaults');\nvar isAbsoluteURL = require('./../helpers/isAbsoluteURL');\nvar combineURLs = require('./../helpers/combineURLs');\n\n/**\n * Throws a `Cancel` if cancellation has been requested.\n */\nfunction throwIfCancellationRequested(config) {\n if (config.cancelToken) {\n config.cancelToken.throwIfRequested();\n }\n}\n\n/**\n * Dispatch a request to the server using the configured adapter.\n *\n * @param {object} config The config that is to be used for the request\n * @returns {Promise} The Promise to be fulfilled\n */\nmodule.exports = function dispatchRequest(config) {\n throwIfCancellationRequested(config);\n\n // Support baseURL config\n if (config.baseURL && !isAbsoluteURL(config.url)) {\n config.url = combineURLs(config.baseURL, config.url);\n }\n\n // Ensure headers exist\n config.headers = config.headers || {};\n\n // Transform request data\n config.data = transformData(\n config.data,\n config.headers,\n config.transformRequest\n );\n\n // Flatten headers\n config.headers = utils.merge(\n config.headers.common || {},\n config.headers[config.method] || {},\n config.headers || {}\n );\n\n utils.forEach(\n ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],\n function cleanHeaderConfig(method) {\n delete config.headers[method];\n }\n );\n\n var adapter = config.adapter || defaults.adapter;\n\n return adapter(config).then(function onAdapterResolution(response) {\n throwIfCancellationRequested(config);\n\n // Transform response data\n response.data = transformData(\n response.data,\n response.headers,\n config.transformResponse\n );\n\n return response;\n }, function onAdapterRejection(reason) {\n if (!isCancel(reason)) {\n throwIfCancellationRequested(config);\n\n // Transform response data\n if (reason && reason.response) {\n reason.response.data = transformData(\n reason.response.data,\n reason.response.headers,\n config.transformResponse\n );\n }\n }\n\n return Promise.reject(reason);\n });\n};\n","'use strict';\n\nvar utils = require('./../utils');\n\nfunction InterceptorManager() {\n this.handlers = [];\n}\n\n/**\n * Add a new interceptor to the stack\n *\n * @param {Function} fulfilled The function to handle `then` for a `Promise`\n * @param {Function} rejected The function to handle `reject` for a `Promise`\n *\n * @return {Number} An ID used to remove interceptor later\n */\nInterceptorManager.prototype.use = function use(fulfilled, rejected) {\n this.handlers.push({\n fulfilled: fulfilled,\n rejected: rejected\n });\n return this.handlers.length - 1;\n};\n\n/**\n * Remove an interceptor from the stack\n *\n * @param {Number} id The ID that was returned by `use`\n */\nInterceptorManager.prototype.eject = function eject(id) {\n if (this.handlers[id]) {\n this.handlers[id] = null;\n }\n};\n\n/**\n * Iterate over all the registered interceptors\n *\n * This method is particularly useful for skipping over any\n * interceptors that may have become `null` calling `eject`.\n *\n * @param {Function} fn The function to call for each interceptor\n */\nInterceptorManager.prototype.forEach = function forEach(fn) {\n utils.forEach(this.handlers, function forEachHandler(h) {\n if (h !== null) {\n fn(h);\n }\n });\n};\n\nmodule.exports = InterceptorManager;\n","'use strict';\n\nvar utils = require('./../utils');\n\nmodule.exports = (\n utils.isStandardBrowserEnv() ?\n\n // Standard browser envs support document.cookie\n (function standardBrowserEnv() {\n return {\n write: function write(name, value, expires, path, domain, secure) {\n var cookie = [];\n cookie.push(name + '=' + encodeURIComponent(value));\n\n if (utils.isNumber(expires)) {\n cookie.push('expires=' + new Date(expires).toGMTString());\n }\n\n if (utils.isString(path)) {\n cookie.push('path=' + path);\n }\n\n if (utils.isString(domain)) {\n cookie.push('domain=' + domain);\n }\n\n if (secure === true) {\n cookie.push('secure');\n }\n\n document.cookie = cookie.join('; ');\n },\n\n read: function read(name) {\n var match = document.cookie.match(new RegExp('(^|;\\\\s*)(' + name + ')=([^;]*)'));\n return (match ? decodeURIComponent(match[3]) : null);\n },\n\n remove: function remove(name) {\n this.write(name, '', Date.now() - 86400000);\n }\n };\n })() :\n\n // Non standard browser env (web workers, react-native) lack needed support.\n (function nonStandardBrowserEnv() {\n return {\n write: function write() {},\n read: function read() { return null; },\n remove: function remove() {}\n };\n })()\n);\n","'use strict';\n\n// btoa polyfill for IE<10 courtesy https://github.com/davidchambers/Base64.js\n\nvar chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';\n\nfunction E() {\n this.message = 'String contains an invalid character';\n}\nE.prototype = new Error;\nE.prototype.code = 5;\nE.prototype.name = 'InvalidCharacterError';\n\nfunction btoa(input) {\n var str = String(input);\n var output = '';\n for (\n // initialize result and counter\n var block, charCode, idx = 0, map = chars;\n // if the next str index does not exist:\n // change the mapping table to \"=\"\n // check if d has no fractional digits\n str.charAt(idx | 0) || (map = '=', idx % 1);\n // \"8 - idx % 1 * 8\" generates the sequence 2, 4, 6, 8\n output += map.charAt(63 & block >> 8 - idx % 1 * 8)\n ) {\n charCode = str.charCodeAt(idx += 3 / 4);\n if (charCode > 0xFF) {\n throw new E();\n }\n block = block << 8 | charCode;\n }\n return output;\n}\n\nmodule.exports = btoa;\n","'use strict';\n\nvar utils = require('./../utils');\n\nmodule.exports = (\n utils.isStandardBrowserEnv() ?\n\n // Standard browser envs have full support of the APIs needed to test\n // whether the request URL is of the same origin as current location.\n (function standardBrowserEnv() {\n var msie = /(msie|trident)/i.test(navigator.userAgent);\n var urlParsingNode = document.createElement('a');\n var originURL;\n\n /**\n * Parse a URL to discover it's components\n *\n * @param {String} url The URL to be parsed\n * @returns {Object}\n */\n function resolveURL(url) {\n var href = url;\n\n if (msie) {\n // IE needs attribute set twice to normalize properties\n urlParsingNode.setAttribute('href', href);\n href = urlParsingNode.href;\n }\n\n urlParsingNode.setAttribute('href', href);\n\n // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils\n return {\n href: urlParsingNode.href,\n protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',\n host: urlParsingNode.host,\n search: urlParsingNode.search ? urlParsingNode.search.replace(/^\\?/, '') : '',\n hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',\n hostname: urlParsingNode.hostname,\n port: urlParsingNode.port,\n pathname: (urlParsingNode.pathname.charAt(0) === '/') ?\n urlParsingNode.pathname :\n '/' + urlParsingNode.pathname\n };\n }\n\n originURL = resolveURL(window.location.href);\n\n /**\n * Determine if a URL shares the same origin as the current location\n *\n * @param {String} requestURL The URL to test\n * @returns {boolean} True if URL shares the same origin, otherwise false\n */\n return function isURLSameOrigin(requestURL) {\n var parsed = (utils.isString(requestURL)) ? resolveURL(requestURL) : requestURL;\n return (parsed.protocol === originURL.protocol &&\n parsed.host === originURL.host);\n };\n })() :\n\n // Non standard browser envs (web workers, react-native) lack needed support.\n (function nonStandardBrowserEnv() {\n return function isURLSameOrigin() {\n return true;\n };\n })()\n);\n","'use strict';\n\nvar utils = require('./../utils');\n\n// Headers whose duplicates are ignored by node\n// c.f. https://nodejs.org/api/http.html#http_message_headers\nvar ignoreDuplicateOf = [\n 'age', 'authorization', 'content-length', 'content-type', 'etag',\n 'expires', 'from', 'host', 'if-modified-since', 'if-unmodified-since',\n 'last-modified', 'location', 'max-forwards', 'proxy-authorization',\n 'referer', 'retry-after', 'user-agent'\n];\n\n/**\n * Parse headers into an object\n *\n * ```\n * Date: Wed, 27 Aug 2014 08:58:49 GMT\n * Content-Type: application/json\n * Connection: keep-alive\n * Transfer-Encoding: chunked\n * ```\n *\n * @param {String} headers Headers needing to be parsed\n * @returns {Object} Headers parsed into an object\n */\nmodule.exports = function parseHeaders(headers) {\n var parsed = {};\n var key;\n var val;\n var i;\n\n if (!headers) { return parsed; }\n\n utils.forEach(headers.split('\\n'), function parser(line) {\n i = line.indexOf(':');\n key = utils.trim(line.substr(0, i)).toLowerCase();\n val = utils.trim(line.substr(i + 1));\n\n if (key) {\n if (parsed[key] && ignoreDuplicateOf.indexOf(key) >= 0) {\n return;\n }\n if (key === 'set-cookie') {\n parsed[key] = (parsed[key] ? parsed[key] : []).concat([val]);\n } else {\n parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;\n }\n }\n });\n\n return parsed;\n};\n","'use strict';\n\nvar utils = require('./../utils');\n\nfunction encode(val) {\n return encodeURIComponent(val).\n replace(/%40/gi, '@').\n replace(/%3A/gi, ':').\n replace(/%24/g, '$').\n replace(/%2C/gi, ',').\n replace(/%20/g, '+').\n replace(/%5B/gi, '[').\n replace(/%5D/gi, ']');\n}\n\n/**\n * Build a URL by appending params to the end\n *\n * @param {string} url The base of the url (e.g., http://www.google.com)\n * @param {object} [params] The params to be appended\n * @returns {string} The formatted url\n */\nmodule.exports = function buildURL(url, params, paramsSerializer) {\n /*eslint no-param-reassign:0*/\n if (!params) {\n return url;\n }\n\n var serializedParams;\n if (paramsSerializer) {\n serializedParams = paramsSerializer(params);\n } else if (utils.isURLSearchParams(params)) {\n serializedParams = params.toString();\n } else {\n var parts = [];\n\n utils.forEach(params, function serialize(val, key) {\n if (val === null || typeof val === 'undefined') {\n return;\n }\n\n if (utils.isArray(val)) {\n key = key + '[]';\n } else {\n val = [val];\n }\n\n utils.forEach(val, function parseValue(v) {\n if (utils.isDate(v)) {\n v = v.toISOString();\n } else if (utils.isObject(v)) {\n v = JSON.stringify(v);\n }\n parts.push(encode(key) + '=' + encode(v));\n });\n });\n\n serializedParams = parts.join('&');\n }\n\n if (serializedParams) {\n url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams;\n }\n\n return url;\n};\n","'use strict';\n\n/**\n * Update an Error with the specified config, error code, and response.\n *\n * @param {Error} error The error to update.\n * @param {Object} config The config.\n * @param {string} [code] The error code (for example, 'ECONNABORTED').\n * @param {Object} [request] The request.\n * @param {Object} [response] The response.\n * @returns {Error} The error.\n */\nmodule.exports = function enhanceError(error, config, code, request, response) {\n error.config = config;\n if (code) {\n error.code = code;\n }\n error.request = request;\n error.response = response;\n return error;\n};\n","'use strict';\n\nvar createError = require('./createError');\n\n/**\n * Resolve or reject a Promise based on response status.\n *\n * @param {Function} resolve A function that resolves the promise.\n * @param {Function} reject A function that rejects the promise.\n * @param {object} response The response.\n */\nmodule.exports = function settle(resolve, reject, response) {\n var validateStatus = response.config.validateStatus;\n // Note: status is not exposed by XDomainRequest\n if (!response.status || !validateStatus || validateStatus(response.status)) {\n resolve(response);\n } else {\n reject(createError(\n 'Request failed with status code ' + response.status,\n response.config,\n null,\n response.request,\n response\n ));\n }\n};\n","'use strict';\n\nvar utils = require('../utils');\n\nmodule.exports = function normalizeHeaderName(headers, normalizedName) {\n utils.forEach(headers, function processHeader(value, name) {\n if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) {\n headers[normalizedName] = value;\n delete headers[name];\n }\n });\n};\n","'use strict';\n\nvar defaults = require('./../defaults');\nvar utils = require('./../utils');\nvar InterceptorManager = require('./InterceptorManager');\nvar dispatchRequest = require('./dispatchRequest');\n\n/**\n * Create a new instance of Axios\n *\n * @param {Object} instanceConfig The default config for the instance\n */\nfunction Axios(instanceConfig) {\n this.defaults = instanceConfig;\n this.interceptors = {\n request: new InterceptorManager(),\n response: new InterceptorManager()\n };\n}\n\n/**\n * Dispatch a request\n *\n * @param {Object} config The config specific for this request (merged with this.defaults)\n */\nAxios.prototype.request = function request(config) {\n /*eslint no-param-reassign:0*/\n // Allow for axios('example/url'[, config]) a la fetch API\n if (typeof config === 'string') {\n config = utils.merge({\n url: arguments[0]\n }, arguments[1]);\n }\n\n config = utils.merge(defaults, {method: 'get'}, this.defaults, config);\n config.method = config.method.toLowerCase();\n\n // Hook up interceptors middleware\n var chain = [dispatchRequest, undefined];\n var promise = Promise.resolve(config);\n\n this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {\n chain.unshift(interceptor.fulfilled, interceptor.rejected);\n });\n\n this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {\n chain.push(interceptor.fulfilled, interceptor.rejected);\n });\n\n while (chain.length) {\n promise = promise.then(chain.shift(), chain.shift());\n }\n\n return promise;\n};\n\n// Provide aliases for supported request methods\nutils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {\n /*eslint func-names:0*/\n Axios.prototype[method] = function(url, config) {\n return this.request(utils.merge(config || {}, {\n method: method,\n url: url\n }));\n };\n});\n\nutils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {\n /*eslint func-names:0*/\n Axios.prototype[method] = function(url, data, config) {\n return this.request(utils.merge(config || {}, {\n method: method,\n url: url,\n data: data\n }));\n };\n});\n\nmodule.exports = Axios;\n","/*!\n * Determine if an object is a Buffer\n *\n * @author Feross Aboukhadijeh \n * @license MIT\n */\n\n// The _isBuffer check is for Safari 5-7 support, because it's missing\n// Object.prototype.constructor. Remove this eventually\nmodule.exports = function (obj) {\n return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer)\n}\n\nfunction isBuffer (obj) {\n return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj)\n}\n\n// For Node v0.10 support. Remove this eventually.\nfunction isSlowBuffer (obj) {\n return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0))\n}\n","'use strict';\n\nvar utils = require('./utils');\nvar bind = require('./helpers/bind');\nvar Axios = require('./core/Axios');\nvar defaults = require('./defaults');\n\n/**\n * Create an instance of Axios\n *\n * @param {Object} defaultConfig The default config for the instance\n * @return {Axios} A new instance of Axios\n */\nfunction createInstance(defaultConfig) {\n var context = new Axios(defaultConfig);\n var instance = bind(Axios.prototype.request, context);\n\n // Copy axios.prototype to instance\n utils.extend(instance, Axios.prototype, context);\n\n // Copy context to instance\n utils.extend(instance, context);\n\n return instance;\n}\n\n// Create the default instance to be exported\nvar axios = createInstance(defaults);\n\n// Expose Axios class to allow class inheritance\naxios.Axios = Axios;\n\n// Factory for creating new instances\naxios.create = function create(instanceConfig) {\n return createInstance(utils.merge(defaults, instanceConfig));\n};\n\n// Expose Cancel & CancelToken\naxios.Cancel = require('./cancel/Cancel');\naxios.CancelToken = require('./cancel/CancelToken');\naxios.isCancel = require('./cancel/isCancel');\n\n// Expose all/spread\naxios.all = function all(promises) {\n return Promise.all(promises);\n};\naxios.spread = require('./helpers/spread');\n\nmodule.exports = axios;\n\n// Allow use of default import syntax in TypeScript\nmodule.exports.default = axios;\n","(function (global, undefined) {\n \"use strict\";\n\n if (global.setImmediate) {\n return;\n }\n\n var nextHandle = 1; // Spec says greater than zero\n var tasksByHandle = {};\n var currentlyRunningATask = false;\n var doc = global.document;\n var registerImmediate;\n\n function setImmediate(callback) {\n // Callback can either be a function or a string\n if (typeof callback !== \"function\") {\n callback = new Function(\"\" + callback);\n }\n // Copy function arguments\n var args = new Array(arguments.length - 1);\n for (var i = 0; i < args.length; i++) {\n args[i] = arguments[i + 1];\n }\n // Store and register the task\n var task = { callback: callback, args: args };\n tasksByHandle[nextHandle] = task;\n registerImmediate(nextHandle);\n return nextHandle++;\n }\n\n function clearImmediate(handle) {\n delete tasksByHandle[handle];\n }\n\n function run(task) {\n var callback = task.callback;\n var args = task.args;\n switch (args.length) {\n case 0:\n callback();\n break;\n case 1:\n callback(args[0]);\n break;\n case 2:\n callback(args[0], args[1]);\n break;\n case 3:\n callback(args[0], args[1], args[2]);\n break;\n default:\n callback.apply(undefined, args);\n break;\n }\n }\n\n function runIfPresent(handle) {\n // From the spec: \"Wait until any invocations of this algorithm started before this one have completed.\"\n // So if we're currently running a task, we'll need to delay this invocation.\n if (currentlyRunningATask) {\n // Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a\n // \"too much recursion\" error.\n setTimeout(runIfPresent, 0, handle);\n } else {\n var task = tasksByHandle[handle];\n if (task) {\n currentlyRunningATask = true;\n try {\n run(task);\n } finally {\n clearImmediate(handle);\n currentlyRunningATask = false;\n }\n }\n }\n }\n\n function installNextTickImplementation() {\n registerImmediate = function(handle) {\n process.nextTick(function () { runIfPresent(handle); });\n };\n }\n\n function canUsePostMessage() {\n // The test against `importScripts` prevents this implementation from being installed inside a web worker,\n // where `global.postMessage` means something completely different and can't be used for this purpose.\n if (global.postMessage && !global.importScripts) {\n var postMessageIsAsynchronous = true;\n var oldOnMessage = global.onmessage;\n global.onmessage = function() {\n postMessageIsAsynchronous = false;\n };\n global.postMessage(\"\", \"*\");\n global.onmessage = oldOnMessage;\n return postMessageIsAsynchronous;\n }\n }\n\n function installPostMessageImplementation() {\n // Installs an event handler on `global` for the `message` event: see\n // * https://developer.mozilla.org/en/DOM/window.postMessage\n // * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages\n\n var messagePrefix = \"setImmediate$\" + Math.random() + \"$\";\n var onGlobalMessage = function(event) {\n if (event.source === global &&\n typeof event.data === \"string\" &&\n event.data.indexOf(messagePrefix) === 0) {\n runIfPresent(+event.data.slice(messagePrefix.length));\n }\n };\n\n if (global.addEventListener) {\n global.addEventListener(\"message\", onGlobalMessage, false);\n } else {\n global.attachEvent(\"onmessage\", onGlobalMessage);\n }\n\n registerImmediate = function(handle) {\n global.postMessage(messagePrefix + handle, \"*\");\n };\n }\n\n function installMessageChannelImplementation() {\n var channel = new MessageChannel();\n channel.port1.onmessage = function(event) {\n var handle = event.data;\n runIfPresent(handle);\n };\n\n registerImmediate = function(handle) {\n channel.port2.postMessage(handle);\n };\n }\n\n function installReadyStateChangeImplementation() {\n var html = doc.documentElement;\n registerImmediate = function(handle) {\n // Create a diff --git a/settings/src/components/appNavigation.vue b/settings/src/components/appNavigation.vue new file mode 100644 index 0000000000..02858b4bb5 --- /dev/null +++ b/settings/src/components/appNavigation.vue @@ -0,0 +1,32 @@ + + + diff --git a/settings/src/components/appNavigation/navigationItem.vue b/settings/src/components/appNavigation/navigationItem.vue new file mode 100644 index 0000000000..ee748ee826 --- /dev/null +++ b/settings/src/components/appNavigation/navigationItem.vue @@ -0,0 +1,117 @@ + + + diff --git a/settings/src/components/popoverMenu.vue b/settings/src/components/popoverMenu.vue new file mode 100644 index 0000000000..92f62c5090 --- /dev/null +++ b/settings/src/components/popoverMenu.vue @@ -0,0 +1,18 @@ + + + + diff --git a/settings/src/components/popoverMenu/popoverItem.vue b/settings/src/components/popoverMenu/popoverItem.vue new file mode 100644 index 0000000000..710aff80aa --- /dev/null +++ b/settings/src/components/popoverMenu/popoverItem.vue @@ -0,0 +1,28 @@ + + + diff --git a/settings/src/components/userList.vue b/settings/src/components/userList.vue new file mode 100644 index 0000000000..cb0a694482 --- /dev/null +++ b/settings/src/components/userList.vue @@ -0,0 +1,298 @@ + + + diff --git a/settings/src/components/userList/userRow.vue b/settings/src/components/userList/userRow.vue new file mode 100644 index 0000000000..da3f93ed2c --- /dev/null +++ b/settings/src/components/userList/userRow.vue @@ -0,0 +1,449 @@ + + + diff --git a/settings/src/main.js b/settings/src/main.js new file mode 100644 index 0000000000..e09925a95d --- /dev/null +++ b/settings/src/main.js @@ -0,0 +1,22 @@ +import Vue from 'vue'; +import { sync } from 'vuex-router-sync'; +import App from './App.vue'; +import router from './router'; +import store from './store'; +require("babel-polyfill"); + + +sync(store, router); + +// bind to window +Vue.prototype.t = t; +Vue.prototype.OC = OC; +Vue.prototype.oc_userconfig = oc_userconfig; + +const app = new Vue({ + router, + store, + render: h => h(App) +}).$mount('#content'); + +export { app, router, store }; \ No newline at end of file diff --git a/settings/src/router.js b/settings/src/router.js new file mode 100644 index 0000000000..7eedb359b6 --- /dev/null +++ b/settings/src/router.js @@ -0,0 +1,36 @@ +import Vue from 'vue'; +import Router from 'vue-router'; +import Users from './views/Users'; + +Vue.use(Router); + +/* + * This is the list of routes where the vuejs app will + * take over php to provide data + * You need to forward the php routing (routes.php) to + * /settings/main.php, where the vue-router will ensure + * the proper route. + * ⚠️ Routes needs to match the php routes. + */ + +export default new Router({ + mode: 'history', + // if index.php is in the url AND we got this far, then it's working: + // let's keep using index.php in the url + base: OC.generateUrl(''), + routes: [ + { + path: '/:index(index.php/)?settings/users', + component: Users, + props: true, + name: 'users', + children: [ + { + path: ':selectedGroup', + name: 'group', + component: Users + } + ] + } + ] +}); diff --git a/settings/src/store/api.js b/settings/src/store/api.js new file mode 100644 index 0000000000..7501a7bb4c --- /dev/null +++ b/settings/src/store/api.js @@ -0,0 +1,99 @@ +import axios from 'axios'; + +const requestToken = document.getElementsByTagName('head')[0].getAttribute('data-requesttoken'); +const tokenHeaders = { headers: { requesttoken: requestToken } }; + +const sanitize = function(url) { + return url.replace(/\/$/, ''); // Remove last url slash +}; + +export default { + + /** + * This Promise is used to chain a request that require an admin password confirmation + * Since chaining Promise have a very precise behavior concerning catch and then, + * you'll need to be careful when using it. + * e.g + * // store + * action(context) { + * return api.requireAdmin().then((response) => { + * return api.get('url') + * .then((response) => {API success}) + * .catch((error) => {API failure}); + * }).catch((error) => {requireAdmin failure}); + * } + * // vue + * this.$store.dispatch('action').then(() => {always executed}) + * + * Since Promise.then().catch().then() will always execute the last then + * this.$store.dispatch('action').then will always be executed + * + * If you want requireAdmin failure to also catch the API request failure + * you will need to throw a new error in the api.get.catch() + * + * e.g + * api.requireAdmin().then((response) => { + * api.get('url') + * .then((response) => {API success}) + * .catch((error) => {throw error;}); + * }).catch((error) => {requireAdmin OR API failure}); + * + * @returns {Promise} + */ + requireAdmin() { + return new Promise(function(resolve, reject) { + // TODO: migrate the OC.dialog to Vue and avoid this mess + // wait for password confirmation + let passwordTimeout; + let waitForpassword = function() { + if (OC.PasswordConfirmation.requiresPasswordConfirmation()) { + passwordTimeout = setTimeout(waitForpassword, 500); + return; + } + clearTimeout(passwordTimeout); + clearTimeout(promiseTimeout); + resolve(); + }; + + // automatically reject after 5s if not resolved + let promiseTimeout = setTimeout(() => { + clearTimeout(passwordTimeout); + // close dialog + if (document.getElementsByClassName('oc-dialog-close').length>0) { + document.getElementsByClassName('oc-dialog-close')[0].click(); + } + OC.Notification.showTemporary(t('settings', 'You did not enter the password in time')); + reject('Password request cancelled'); + }, 7000); + + // request password + OC.PasswordConfirmation.requirePasswordConfirmation(); + waitForpassword(); + }); + }, + get(url) { + return axios.get(sanitize(url), tokenHeaders) + .then((response) => Promise.resolve(response)) + .catch((error) => Promise.reject(error)); + }, + post(url, data) { + return axios.post(sanitize(url), data, tokenHeaders) + .then((response) => Promise.resolve(response)) + .catch((error) => Promise.reject(error)); + }, + patch(url, data) { + return axios.patch(sanitize(url), data, tokenHeaders) + .then((response) => Promise.resolve(response)) + .catch((error) => Promise.reject(error)); + }, + put(url, data) { + return axios.put(sanitize(url), data, tokenHeaders) + .then((response) => Promise.resolve(response)) + .catch((error) => Promise.reject(error)); + }, + delete(url, data) { + return axios.delete(sanitize(url), { data: data, headers: tokenHeaders.headers }) + .then((response) => Promise.resolve(response)) + .catch((error) => Promise.reject(error)); + } +}; \ No newline at end of file diff --git a/settings/src/store/index.js b/settings/src/store/index.js new file mode 100644 index 0000000000..4332ece33e --- /dev/null +++ b/settings/src/store/index.js @@ -0,0 +1,28 @@ +import Vue from 'vue'; +import Vuex from 'vuex'; +import users from './users'; +import settings from './settings'; +import oc from './oc'; + +Vue.use(Vuex) + +const debug = process.env.NODE_ENV !== 'production'; + +const mutations = { + API_FAILURE(state, error) { + let message = error.error.response.data.ocs.meta.message; + OC.Notification.showHtml(t('settings','An error occured during the request. Unable to proceed.')+'
'+message, {timeout: 7}); + console.log(state, error); + } +}; + +export default new Vuex.Store({ + modules: { + users, + settings, + oc + }, + strict: debug, + + mutations +}); diff --git a/settings/src/store/oc.js b/settings/src/store/oc.js new file mode 100644 index 0000000000..4bb82075e8 --- /dev/null +++ b/settings/src/store/oc.js @@ -0,0 +1,25 @@ +import api from './api'; + +const state = {}; +const mutations = {}; +const getters = {}; +const actions = { + /** + * Set application config in database + * + * @param {Object} context + * @param {Object} options + * @param {string} options.app Application name + * @param {boolean} options.key Config key + * @param {boolean} options.value Value to set + * @returns{Promise} + */ + setAppConfig(context, {app, key, value}) { + return api.requireAdmin().then((response) => { + return api.post(OC.linkToOCS(`apps/provisioning_api/api/v1/config/apps/${app}/${key}`, 2), {value: value}) + .catch((error) => {throw error;}); + }).catch((error) => context.commit('API_FAILURE', { app, key, value, error }));; + } +}; + +export default {state, mutations, getters, actions}; diff --git a/settings/src/store/settings.js b/settings/src/store/settings.js new file mode 100644 index 0000000000..0ba827467a --- /dev/null +++ b/settings/src/store/settings.js @@ -0,0 +1,18 @@ +import api from './api'; + +const state = { + serverData: {} +}; +const mutations = { + setServerData(state, data) { + state.serverData = data; + } +}; +const getters = { + getServerData(state) { + return state.serverData; + } +}; +const actions = {}; + +export default {state, mutations, getters, actions}; diff --git a/settings/src/store/users.js b/settings/src/store/users.js new file mode 100644 index 0000000000..f349c7b4a0 --- /dev/null +++ b/settings/src/store/users.js @@ -0,0 +1,420 @@ +import api from './api'; + +const orderGroups = function(groups, orderBy) { + /* const SORT_USERCOUNT = 1; + * const SORT_GROUPNAME = 2; + * https://github.com/nextcloud/server/blob/208e38e84e1a07a49699aa90dc5b7272d24489f0/lib/private/Group/MetaData.php#L34 + */ + if (orderBy === 1) { + return groups.sort((a, b) => a.usercount < b.usercount); + } else { + return groups.sort((a, b) => a.name.localeCompare(b.name)); + } +}; + +const state = { + users: [], + groups: [], + orderBy: 1, + minPasswordLength: 0, + usersOffset: 0, + usersLimit: 25, + userCount: 0 +}; + +const mutations = { + appendUsers(state, usersObj) { + // convert obj to array + let users = state.users.concat(Object.keys(usersObj).map(userid => usersObj[userid])); + state.usersOffset += state.usersLimit; + state.users = users; + }, + setPasswordPolicyMinLength(state, length) { + state.minPasswordLength = length!=='' ? length : 0; + }, + initGroups(state, {groups, orderBy, userCount}) { + state.groups = groups; + state.orderBy = orderBy; + state.userCount = userCount; + state.groups = orderGroups(state.groups, state.orderBy); + }, + addGroup(state, gid) { + try { + state.groups.push({ + id: gid, + name: gid, + usercount: 0 // user will be added after the creation + }); + state.groups = orderGroups(state.groups, state.orderBy); + } catch (e) { + console.log('Can\'t create group', e); + } + }, + removeGroup(state, gid) { + let groupIndex = state.groups.findIndex(groupSearch => groupSearch.id == gid); + if (groupIndex >= 0) { + state.groups.splice(groupIndex, 1); + } + }, + addUserGroup(state, { userid, gid }) { + let group = state.groups.find(groupSearch => groupSearch.id == gid); + if (group) { + group.usercount++; // increase count + } + let groups = state.users.find(user => user.id == userid).groups; + groups.push(gid); + state.groups = orderGroups(state.groups, state.orderBy); + }, + removeUserGroup(state, { userid, gid }) { + let group = state.groups.find(groupSearch => groupSearch.id == gid); + if (group) { + group.usercount--; // lower count + } + let groups = state.users.find(user => user.id == userid).groups; + groups.splice(groups.indexOf(gid),1); + state.groups = orderGroups(state.groups, state.orderBy); + }, + addUserSubAdmin(state, { userid, gid }) { + let groups = state.users.find(user => user.id == userid).subadmin; + groups.push(gid); + }, + removeUserSubAdmin(state, { userid, gid }) { + let groups = state.users.find(user => user.id == userid).subadmin; + groups.splice(groups.indexOf(gid),1); + }, + deleteUser(state, userid) { + let userIndex = state.users.findIndex(user => user.id == userid); + state.users.splice(userIndex, 1); + }, + addUserData(state, response) { + state.users.push(response.data.ocs.data); + }, + enableDisableUser(state, { userid, enabled }) { + state.users.find(user => user.id == userid).enabled = enabled; + // increment or not + state.groups.find(group => group.id == 'disabled').usercount += enabled ? -1 : 1; + state.userCount += enabled ? 1 : -1; + console.log(enabled); + }, + setUserData(state, { userid, key, value }) { + if (key === 'quota') { + let humanValue = OC.Util.computerFileSize(value); + state.users.find(user => user.id == userid)[key][key] = humanValue?humanValue:value; + } else { + state.users.find(user => user.id == userid)[key] = value; + } + }, + + /** + * Reset users list + */ + resetUsers(state) { + state.users = []; + state.usersOffset = 0; + } +}; + +const getters = { + getUsers(state) { + return state.users; + }, + getGroups(state) { + return state.groups; + }, + getPasswordPolicyMinLength(state) { + return state.minPasswordLength; + }, + getUsersOffset(state) { + return state.usersOffset; + }, + getUsersLimit(state) { + return state.usersLimit; + }, + getUserCount(state) { + return state.userCount; + } +}; + +const actions = { + + /** + * Get all users with full details + * + * @param {Object} context + * @param {Object} options + * @param {int} options.offset List offset to request + * @param {int} options.limit List number to return from offset + * @param {string} options.search Search amongst users + * @param {string} options.group Get users from group + * @returns {Promise} + */ + getUsers(context, { offset, limit, search, group }) { + search = typeof search === 'string' ? search : ''; + group = typeof group === 'string' ? group : ''; + if (group !== '') { + return api.get(OC.linkToOCS(`cloud/groups/${group}/users/details?offset=${offset}&limit=${limit}&search=${search}`, 2)) + .then((response) => { + if (Object.keys(response.data.ocs.data.users).length > 0) { + context.commit('appendUsers', response.data.ocs.data.users); + return true; + } + return false; + }) + .catch((error) => context.commit('API_FAILURE', error)); + } + + return api.get(OC.linkToOCS(`cloud/users/details?offset=${offset}&limit=${limit}&search=${search}`, 2)) + .then((response) => { + if (Object.keys(response.data.ocs.data.users).length > 0) { + context.commit('appendUsers', response.data.ocs.data.users); + return true; + } + return false; + }) + .catch((error) => context.commit('API_FAILURE', error)); + }, + + /** + * Get all users with full details + * + * @param {Object} context + * @param {Object} options + * @param {int} options.offset List offset to request + * @param {int} options.limit List number to return from offset + * @returns {Promise} + */ + getUsersFromList(context, { offset, limit, search }) { + search = typeof search === 'string' ? search : ''; + return api.get(OC.linkToOCS(`cloud/users/details?offset=${offset}&limit=${limit}&search=${search}`, 2)) + .then((response) => { + if (Object.keys(response.data.ocs.data.users).length > 0) { + context.commit('appendUsers', response.data.ocs.data.users); + return true; + } + return false; + }) + .catch((error) => context.commit('API_FAILURE', error)); + }, + + /** + * Get all users with full details from a groupid + * + * @param {Object} context + * @param {Object} options + * @param {int} options.offset List offset to request + * @param {int} options.limit List number to return from offset + * @returns {Promise} + */ + getUsersFromGroup(context, { groupid, offset, limit }) { + return api.get(OC.linkToOCS(`cloud/users/${groupid}/details?offset=${offset}&limit=${limit}`, 2)) + .then((response) => context.commit('getUsersFromList', response.data.ocs.data.users)) + .catch((error) => context.commit('API_FAILURE', error)); + }, + + + getPasswordPolicyMinLength(context) { + if(oc_capabilities.password_policy && oc_capabilities.password_policy.minLength) { + context.commit('setPasswordPolicyMinLength', oc_capabilities.password_policy.minLength); + return oc_capabilities.password_policy.minLength; + } + return false; + }, + + /** + * Add group + * + * @param {Object} context + * @param {string} gid Group id + * @returns {Promise} + */ + addGroup(context, gid) { + return api.requireAdmin().then((response) => { + return api.post(OC.linkToOCS(`cloud/groups`, 2), {groupid: gid}) + .then((response) => context.commit('addGroup', gid)) + .catch((error) => {throw error;}); + }).catch((error) => context.commit('API_FAILURE', { userid, error })); + }, + + /** + * Remove group + * + * @param {Object} context + * @param {string} gid Group id + * @returns {Promise} + */ + removeGroup(context, gid) { + return api.requireAdmin().then((response) => { + return api.delete(OC.linkToOCS(`cloud/groups/${gid}`, 2)) + .then((response) => context.commit('removeGroup', gid)) + .catch((error) => {throw error;}); + }).catch((error) => context.commit('API_FAILURE', { gid, error })); + }, + + /** + * Add user to group + * + * @param {Object} context + * @param {Object} options + * @param {string} options.userid User id + * @param {string} options.gid Group id + * @returns {Promise} + */ + addUserGroup(context, { userid, gid }) { + return api.requireAdmin().then((response) => { + return api.post(OC.linkToOCS(`cloud/users/${userid}/groups`, 2), { groupid: gid }) + .then((response) => context.commit('addUserGroup', { userid, gid })) + .catch((error) => {throw error;}); + }).catch((error) => context.commit('API_FAILURE', { userid, error })); + }, + + /** + * Remove user from group + * + * @param {Object} context + * @param {Object} options + * @param {string} options.userid User id + * @param {string} options.gid Group id + * @returns {Promise} + */ + removeUserGroup(context, { userid, gid }) { + return api.requireAdmin().then((response) => { + return api.delete(OC.linkToOCS(`cloud/users/${userid}/groups`, 2), { groupid: gid }) + .then((response) => context.commit('removeUserGroup', { userid, gid })) + .catch((error) => {throw error;}); + }).catch((error) => context.commit('API_FAILURE', { userid, error })); + }, + + /** + * Add user to group admin + * + * @param {Object} context + * @param {Object} options + * @param {string} options.userid User id + * @param {string} options.gid Group id + * @returns {Promise} + */ + addUserSubAdmin(context, { userid, gid }) { + return api.requireAdmin().then((response) => { + return api.post(OC.linkToOCS(`cloud/users/${userid}/subadmins`, 2), { groupid: gid }) + .then((response) => context.commit('addUserSubAdmin', { userid, gid })) + .catch((error) => {throw error;}); + }).catch((error) => context.commit('API_FAILURE', { userid, error })); + }, + + /** + * Remove user from group admin + * + * @param {Object} context + * @param {Object} options + * @param {string} options.userid User id + * @param {string} options.gid Group id + * @returns {Promise} + */ + removeUserSubAdmin(context, { userid, gid }) { + return api.requireAdmin().then((response) => { + return api.delete(OC.linkToOCS(`cloud/users/${userid}/subadmins`, 2), { groupid: gid }) + .then((response) => context.commit('removeUserSubAdmin', { userid, gid })) + .catch((error) => {throw error;}); + }).catch((error) => context.commit('API_FAILURE', { userid, error })); + }, + + /** + * Delete a user + * + * @param {Object} context + * @param {string} userid User id + * @returns {Promise} + */ + deleteUser(context, { userid }) { + return api.requireAdmin().then((response) => { + return api.delete(OC.linkToOCS(`cloud/users/${userid}`, 2)) + .then((response) => context.commit('deleteUser', userid)) + .catch((error) => {throw error;}); + }).catch((error) => context.commit('API_FAILURE', { userid, error })); + }, + + /** + * Add a user + * + * @param {Object} context + * @param {Object} options + * @param {string} options.userid User id + * @param {string} options.password User password + * @param {string} options.email User email + * @param {string} options.groups User groups + * @param {string} options.subadmin User subadmin groups + * @param {string} options.quota User email + * @returns {Promise} + */ + addUser({commit, dispatch}, { userid, password, email, groups, subadmin, quota, language }) { + return api.requireAdmin().then((response) => { + return api.post(OC.linkToOCS(`cloud/users`, 2), { userid, password, email, groups, subadmin, quota, language }) + .then((response) => dispatch('addUserData', userid)) + .catch((error) => {throw error;}); + }).catch((error) => commit('API_FAILURE', { userid, error })); + }, + + /** + * Get user data and commit addition + * + * @param {Object} context + * @param {string} userid User id + * @returns {Promise} + */ + addUserData(context, userid) { + return api.requireAdmin().then((response) => { + return api.get(OC.linkToOCS(`cloud/users/${userid}`, 2)) + .then((response) => context.commit('addUserData', response)) + .catch((error) => {throw error;}); + }).catch((error) => context.commit('API_FAILURE', { userid, error })); + }, + + /** Enable or disable user + * + * @param {Object} context + * @param {Object} options + * @param {string} options.userid User id + * @param {boolean} options.enabled User enablement status + * @returns {Promise} + */ + enableDisableUser(context, { userid, enabled = true }) { + let userStatus = enabled ? 'enable' : 'disable'; + return api.requireAdmin().then((response) => { + return api.put(OC.linkToOCS(`cloud/users/${userid}/${userStatus}`, 2)) + .then((response) => context.commit('enableDisableUser', { userid, enabled })) + .catch((error) => {throw error;}); + }).catch((error) => context.commit('API_FAILURE', { userid, error })); + }, + + /** + * Edit user data + * + * @param {Object} context + * @param {Object} options + * @param {string} options.userid User id + * @param {string} options.key User field to edit + * @param {string} options.value Value of the change + * @returns {Promise} + */ + setUserData(context, { userid, key, value }) { + let allowedEmpty = ['email', 'displayname']; + if (['email', 'language', 'quota', 'displayname', 'password'].indexOf(key) !== -1) { + // We allow empty email or displayname + if (typeof value === 'string' && + ( + (allowedEmpty.indexOf(key) === -1 && value.length > 0) || + allowedEmpty.indexOf(key) !== -1 + ) + ) { + return api.requireAdmin().then((response) => { + return api.put(OC.linkToOCS(`cloud/users/${userid}`, 2), { key: key, value: value }) + .then((response) => context.commit('setUserData', { userid, key, value })) + .catch((error) => {throw error;}); + }).catch((error) => context.commit('API_FAILURE', { userid, error })); + } + } + return Promise.reject(new Error('Invalid request data')); + } +}; + +export default { state, mutations, getters, actions }; \ No newline at end of file diff --git a/settings/src/views/Users.vue b/settings/src/views/Users.vue new file mode 100644 index 0000000000..c88487635a --- /dev/null +++ b/settings/src/views/Users.vue @@ -0,0 +1,301 @@ + + + diff --git a/settings/templates/settings.php b/settings/templates/settings.php index 44011af288..2314604111 100644 --- a/settings/templates/settings.php +++ b/settings/templates/settings.php @@ -1,9 +1,24 @@ - - * This file is licensed under the Affero General Public License version 3 or later. - * See the COPYING-README file. - */?> + + * @author John Molakvoæ (skjnldsv) + * + * @license GNU AGPL version 3 or any later version + * + * This is the default empty template to load Vue! + * Do your cbackend computations into a php files + * then serve this file as template and include your data into + * the $serverData template variable + * + * return new TemplateResponse('settings', 'settings', ['serverData' => $serverData]); + * + */ - + + diff --git a/settings/templates/users/main.php b/settings/templates/users/main.php deleted file mode 100644 index 2d40f0fbb6..0000000000 --- a/settings/templates/users/main.php +++ /dev/null @@ -1,80 +0,0 @@ - - * Copyright (c) 2017, John Molakvoæ - * This file is licensed under the Affero General Public License version 3 or later. - * See the COPYING-README file. - */ - -script('settings', [ - 'users/deleteHandler', - 'users/filter', - 'users/users', - 'users/groups' -]); -script('core', [ - 'multiselect', - 'singleselect' -]); -style('settings', 'settings'); - -$userlistParams = array(); -$allGroups=array(); -foreach($_["adminGroup"] as $group) { - $allGroups[$group['id']] = array('displayName' => $group['name']); -} -foreach($_["groups"] as $group) { - $allGroups[$group['id']] = array('displayName' => $group['name']); -} -$userlistParams['subadmingroups'] = $allGroups; -$userlistParams['allGroups'] = json_encode($allGroups); -$items = array_flip($userlistParams['subadmingroups']); -unset($items['admin']); -$userlistParams['subadmingroups'] = array_flip($items); - -translation('settings'); -?> - -
- inc('users/part.createuser')); ?> - inc('users/part.grouplist')); ?> -
-
- -
-
- inc('users/part.setquota')); ?> - -
-

- /> - -

-

- /> - -

-

- /> - -

-

- t('When the password of a new user is left empty, an activation email with a link to set the password is sent.')) ?> -

-
-
-
-
- -
- inc('users/part.userlist', $userlistParams)); ?> -
diff --git a/settings/templates/users/part.createuser.php b/settings/templates/users/part.createuser.php deleted file mode 100644 index 57fe8e8b63..0000000000 --- a/settings/templates/users/part.createuser.php +++ /dev/null @@ -1,3 +0,0 @@ -
- -
diff --git a/settings/templates/users/part.grouplist.php b/settings/templates/users/part.grouplist.php deleted file mode 100644 index 4128a6b76e..0000000000 --- a/settings/templates/users/part.grouplist.php +++ /dev/null @@ -1,69 +0,0 @@ - diff --git a/settings/templates/users/part.setquota.php b/settings/templates/users/part.setquota.php deleted file mode 100644 index a0231ddace..0000000000 --- a/settings/templates/users/part.setquota.php +++ /dev/null @@ -1,35 +0,0 @@ -
- - t('Default quota'));?> - - - - - : - - t('Unlimited'));?> - - - - -
diff --git a/settings/templates/users/part.userlist.php b/settings/templates/users/part.userlist.php deleted file mode 100644 index 5a37c968d1..0000000000 --- a/settings/templates/users/part.userlist.php +++ /dev/null @@ -1,149 +0,0 @@ -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
t('Username'))?>t( 'Full name' )); ?>t( 'Password' )); ?>t( 'Email' )); ?>t( 'Groups' )); ?>t('Group admin for')); ?>t('Recovery password')); ?>t('Quota')); ?>t('Storage location')); ?>t('User backend')); ?>t('Last login')); ?>
<?php p($l->t('change full name'))?> - ●●●●●●● <?php p($l->t('set new password'))?> - <?php p($l->t('change email address'))?> -
-
-
- - - -
- -
- -
-
-
-
- - diff --git a/settings/tests/js/users/deleteHandlerSpec.js b/settings/tests/js/users/deleteHandlerSpec.js deleted file mode 100644 index 98530c47ee..0000000000 --- a/settings/tests/js/users/deleteHandlerSpec.js +++ /dev/null @@ -1,220 +0,0 @@ -/** -* ownCloud -* -* @author Vincent Petry -* @copyright 2014 Vincent Petry -* -* This library is free software; you can redistribute it and/or -* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE -* License as published by the Free Software Foundation; either -* version 3 of the License, or any later version. -* -* This library is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU AFFERO GENERAL PUBLIC LICENSE for more details. -* -* You should have received a copy of the GNU Affero General Public -* License along with this library. If not, see . -* -*/ - -describe('DeleteHandler tests', function() { - var showNotificationSpy; - var hideNotificationSpy; - var clock; - var removeCallback; - var markCallback; - var undoCallback; - - function init(markCallback, removeCallback, undoCallback) { - var handler = new DeleteHandler('dummyendpoint.php', 'paramid', markCallback, removeCallback); - handler.setNotification(OC.Notification, 'dataid', 'removed %oid entry', undoCallback); - return handler; - } - - beforeEach(function() { - showNotificationSpy = sinon.spy(OC.Notification, 'showHtml'); - hideNotificationSpy = sinon.spy(OC.Notification, 'hide'); - clock = sinon.useFakeTimers(); - removeCallback = sinon.stub(); - markCallback = sinon.stub(); - undoCallback = sinon.stub(); - - $('#testArea').append('
'); - }); - afterEach(function() { - showNotificationSpy.restore(); - hideNotificationSpy.restore(); - clock.restore(); - }); - it('shows a notification when marking for delete', function() { - var handler = init(markCallback, removeCallback, undoCallback); - handler.mark('some_uid'); - - expect(showNotificationSpy.calledOnce).toEqual(true); - expect(showNotificationSpy.getCall(0).args[0]).toEqual('removed some_uid entry'); - - expect(markCallback.calledOnce).toEqual(true); - expect(markCallback.getCall(0).args[0]).toEqual('some_uid'); - expect(removeCallback.notCalled).toEqual(true); - expect(undoCallback.notCalled).toEqual(true); - - expect(fakeServer.requests.length).toEqual(0); - }); - it('deletes first entry and reshows notification on second delete', function() { - fakeServer.respondWith(/\/index\.php\/dummyendpoint.php\/some_uid/, [ - 204, - { 'Content-Type': 'application/json' }, - JSON.stringify({status: 'success'}) - ]); - fakeServer.respondWith(/\/index\.php\/dummyendpoint.php\/some_other_uid/, [ - 204, - { 'Content-Type': 'application/json' }, - JSON.stringify({status: 'success'}) - ]); - - var handler = init(markCallback, removeCallback, undoCallback); - handler.mark('some_uid'); - - expect(showNotificationSpy.calledOnce).toEqual(true); - expect(showNotificationSpy.getCall(0).args[0]).toEqual('removed some_uid entry'); - showNotificationSpy.resetHistory(); - - handler.mark('some_other_uid'); - - expect(hideNotificationSpy.calledOnce).toEqual(true); - expect(showNotificationSpy.calledOnce).toEqual(true); - expect(showNotificationSpy.getCall(0).args[0]).toEqual('removed some_other_uid entry'); - - expect(markCallback.calledTwice).toEqual(true); - expect(markCallback.getCall(0).args[0]).toEqual('some_uid'); - expect(markCallback.getCall(1).args[0]).toEqual('some_other_uid'); - // called only once, because it is called once the second user is deleted - expect(removeCallback.calledOnce).toEqual(true); - expect(undoCallback.notCalled).toEqual(true); - - // previous one was delete - expect(fakeServer.requests.length).toEqual(1); - var request = fakeServer.requests[0]; - expect(request.url).toEqual(OC.webroot + '/index.php/dummyendpoint.php/some_uid'); - }); - it('automatically deletes after timeout', function() { - fakeServer.respondWith(/\/index\.php\/dummyendpoint.php\/some_uid/, [ - 204, - { 'Content-Type': 'application/json' }, - JSON.stringify({status: 'success'}) - ]); - - var handler = init(markCallback, removeCallback, undoCallback); - handler.mark('some_uid'); - - clock.tick(5000); - // nothing happens yet - expect(fakeServer.requests.length).toEqual(0); - - clock.tick(3000); - expect(fakeServer.requests.length).toEqual(1); - var request = fakeServer.requests[0]; - expect(request.url).toEqual(OC.webroot + '/index.php/dummyendpoint.php/some_uid'); - }); - it('deletes when deleteEntry is called', function() { - fakeServer.respondWith(/\/index\.php\/dummyendpoint.php\/some_uid/, [ - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({status: 'success'}) - ]); - var handler = init(markCallback, removeCallback, undoCallback); - handler.mark('some_uid'); - - handler.deleteEntry(); - expect(fakeServer.requests.length).toEqual(1); - var request = fakeServer.requests[0]; - expect(request.url).toEqual(OC.webroot + '/index.php/dummyendpoint.php/some_uid'); - }); - it('deletes when deleteEntry is called and escapes', function() { - fakeServer.respondWith(/\/index\.php\/dummyendpoint.php\/some_uid/, [ - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({status: 'success'}) - ]); - var handler = init(markCallback, removeCallback, undoCallback); - handler.mark('some_uid<>/"..\\'); - - handler.deleteEntry(); - expect(fakeServer.requests.length).toEqual(1); - var request = fakeServer.requests[0]; - expect(request.url).toEqual(OC.webroot + '/index.php/dummyendpoint.php/some_uid%3C%3E%2F%22..%5C'); - }); - it('cancels deletion when undo is clicked', function() { - var handler = init(markCallback, removeCallback, undoCallback); - handler.setNotification(OC.Notification, 'dataid', 'removed %oid entry Undo', undoCallback); - handler.mark('some_uid'); - $('#notification .undo').click(); - - expect(undoCallback.calledOnce).toEqual(true); - - // timer was cancelled - clock.tick(10000); - expect(fakeServer.requests.length).toEqual(0); - }); - it('cancels deletion when cancel method is called', function() { - var handler = init(markCallback, removeCallback, undoCallback); - handler.setNotification(OC.Notification, 'dataid', 'removed %oid entry Undo', undoCallback); - handler.mark('some_uid'); - handler.cancel(); - - // not sure why, seems to be by design - expect(undoCallback.notCalled).toEqual(true); - - // timer was cancelled - clock.tick(10000); - expect(fakeServer.requests.length).toEqual(0); - }); - it('calls removeCallback after successful server side deletion', function() { - fakeServer.respondWith(/\/index\.php\/dummyendpoint.php\/some_uid/, [ - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({status: 'success'}) - ]); - - var handler = init(markCallback, removeCallback, undoCallback); - handler.mark('some_uid'); - handler.deleteEntry(); - - expect(fakeServer.requests.length).toEqual(1); - var request = fakeServer.requests[0]; - var query = OC.parseQueryString(request.requestBody); - - expect(removeCallback.calledOnce).toEqual(true); - expect(undoCallback.notCalled).toEqual(true); - expect(removeCallback.getCall(0).args[0]).toEqual('some_uid'); - }); - it('calls undoCallback and shows alert after failed server side deletion', function() { - // stub t to avoid extra calls - var tStub = sinon.stub(window, 't').returns('text'); - fakeServer.respondWith(/\/index\.php\/dummyendpoint.php\/some_uid/, [ - 403, - { 'Content-Type': 'application/json' }, - JSON.stringify({status: 'error', data: {message: 'test error'}}) - ]); - - var alertDialogStub = sinon.stub(OC.dialogs, 'alert'); - var handler = init(markCallback, removeCallback, undoCallback); - handler.mark('some_uid'); - handler.deleteEntry(); - - expect(fakeServer.requests.length).toEqual(1); - var request = fakeServer.requests[0]; - var query = OC.parseQueryString(request.requestBody); - - expect(removeCallback.notCalled).toEqual(true); - expect(undoCallback.calledOnce).toEqual(true); - expect(undoCallback.getCall(0).args[0]).toEqual('some_uid'); - - expect(alertDialogStub.calledOnce); - - alertDialogStub.restore(); - tStub.restore(); - }); -}); diff --git a/settings/users.php b/settings/users.php deleted file mode 100644 index 03523fc530..0000000000 --- a/settings/users.php +++ /dev/null @@ -1,149 +0,0 @@ - - * @author Bart Visscher - * @author Clark Tomlinson - * @author Daniel Molkentin - * @author Georg Ehrke - * @author Jakob Sack - * @author Joas Schilling - * @author Jörn Friedrich Dreyer - * @author Lukas Reschke - * @author Morris Jobke - * @author Robin Appelman - * @author Roeland Jago Douma - * @author Stephan Peijnik - * @author Thomas Müller - * @author Thomas Pulzer - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see - * - */ - -OC_Util::checkSubAdminUser(); - -\OC::$server->getNavigationManager()->setActiveEntry('core_users'); - -$userManager = \OC::$server->getUserManager(); -$groupManager = \OC::$server->getGroupManager(); -$appManager = \OC::$server->getAppManager(); - -// Set the sort option: SORT_USERCOUNT or SORT_GROUPNAME -$sortGroupsBy = \OC\Group\MetaData::SORT_USERCOUNT; - -$config = \OC::$server->getConfig(); - -if ($config->getSystemValue('sort_groups_by_name', false)) { - $sortGroupsBy = \OC\Group\MetaData::SORT_GROUPNAME; -} else { - $isLDAPUsed = false; - if ($appManager->isEnabledForUser('user_ldap')) { - $isLDAPUsed = - $groupManager->isBackendUsed('\OCA\User_LDAP\Group_LDAP') - || $groupManager->isBackendUsed('\OCA\User_LDAP\Group_Proxy'); - if ($isLDAPUsed) { - // LDAP user count can be slow, so we sort by group name here - $sortGroupsBy = \OC\Group\MetaData::SORT_GROUPNAME; - } - } -} - -$uid = \OC_User::getUser(); -$isAdmin = OC_User::isAdminUser($uid); - -$isDisabled = true; -$user = $userManager->get($uid); -if ($user) { - $isDisabled = !$user->isEnabled(); -} - -$groupsInfo = new \OC\Group\MetaData( - $uid, - $isAdmin, - $groupManager, - \OC::$server->getUserSession() -); - -$groupsInfo->setSorting($sortGroupsBy); -list($adminGroup, $groups) = $groupsInfo->get(); - -$recoveryAdminEnabled = $appManager->isEnabledForUser('encryption') && - $config->getAppValue( 'encryption', 'recoveryAdminEnabled', '0'); - -if($isAdmin) { - $subAdmins = \OC::$server->getGroupManager()->getSubAdmin()->getAllSubAdmins(); - // New class returns IUser[] so convert back - $result = []; - foreach ($subAdmins as $subAdmin) { - $result[] = [ - 'gid' => $subAdmin['group']->getGID(), - 'uid' => $subAdmin['user']->getUID(), - ]; - } - $subAdmins = $result; -}else{ - /* Retrieve group IDs from $groups array, so we can pass that information into OC_Group::displayNamesInGroups() */ - $gids = array(); - foreach($groups as $group) { - if (isset($group['id'])) { - $gids[] = $group['id']; - } - } - $subAdmins = false; -} - -$disabledUsers = $isLDAPUsed ? 0 : $userManager->countDisabledUsers(); -$disabledUsersGroup = [ - 'id' => '_disabledUsers', - 'name' => '_disabledUsers', - 'usercount' => $disabledUsers -]; - -// load preset quotas -$quotaPreset=$config->getAppValue('files', 'quota_preset', '1 GB, 5 GB, 10 GB'); -$quotaPreset=explode(',', $quotaPreset); -foreach($quotaPreset as &$preset) { - $preset=trim($preset); -} -$quotaPreset=array_diff($quotaPreset, array('default', 'none')); - -$defaultQuota=$config->getAppValue('files', 'default_quota', 'none'); -$defaultQuotaIsUserDefined=array_search($defaultQuota, $quotaPreset)===false - && array_search($defaultQuota, array('none', 'default'))===false; - -\OC::$server->getEventDispatcher()->dispatch('OC\Settings\Users::loadAdditionalScripts'); - -$tmpl = new OC_Template("settings", "users/main", "user"); -$tmpl->assign('groups', $groups); -$tmpl->assign('sortGroups', $sortGroupsBy); -$tmpl->assign('adminGroup', $adminGroup); -$tmpl->assign('disabledUsersGroup', $disabledUsersGroup); -$tmpl->assign('isAdmin', (int)$isAdmin); -$tmpl->assign('subadmins', $subAdmins); -$tmpl->assign('numofgroups', count($groups) + count($adminGroup)); -$tmpl->assign('quota_preset', $quotaPreset); -$tmpl->assign('default_quota', $defaultQuota); -$tmpl->assign('defaultQuotaIsUserDefined', $defaultQuotaIsUserDefined); -$tmpl->assign('recoveryAdminEnabled', $recoveryAdminEnabled); - -$tmpl->assign('show_storage_location', $config->getAppValue('core', 'umgmt_show_storage_location', 'false')); -$tmpl->assign('show_last_login', $config->getAppValue('core', 'umgmt_show_last_login', 'false')); -$tmpl->assign('show_email', $config->getAppValue('core', 'umgmt_show_email', 'false')); -$tmpl->assign('show_backend', $config->getAppValue('core', 'umgmt_show_backend', 'false')); -$tmpl->assign('send_email', $config->getAppValue('core', 'umgmt_send_email', 'false')); - -$tmpl->printPage(); diff --git a/settings/webpack.common.js b/settings/webpack.common.js new file mode 100644 index 0000000000..8810c30244 --- /dev/null +++ b/settings/webpack.common.js @@ -0,0 +1,77 @@ +const path = require('path') + +module.exports = { + entry: './src/main.js', + output: { + path: path.resolve(__dirname, './js'), + publicPath: '/dist/', + filename: 'main.js' + }, + module: { + rules: [ + { + test: /\.css$/, + use: [ + 'vue-style-loader', + 'css-loader' + ], + }, + { + test: /\.scss$/, + use: [ + 'vue-style-loader', + 'css-loader', + 'sass-loader' + ], + }, + { + test: /\.sass$/, + use: [ + 'vue-style-loader', + 'css-loader', + 'sass-loader?indentedSyntax' + ], + }, + { + test: /\.vue$/, + loader: 'vue-loader', + options: { + loaders: { + // Since sass-loader (weirdly) has SCSS as its default parse mode, we map + // the "scss" and "sass" values for the lang attribute to the right configs here. + // other preprocessors should work out of the box, no loader config like this necessary. + 'scss': [ + 'vue-style-loader', + 'css-loader', + 'sass-loader' + ], + 'sass': [ + 'vue-style-loader', + 'css-loader', + 'sass-loader?indentedSyntax' + ] + } + // other vue-loader options go here + } + }, + { + test: /\.js$/, + loader: 'babel-loader', + exclude: /node_modules/ + }, + { + test: /\.(png|jpg|gif|svg)$/, + loader: 'file-loader', + options: { + name: '[name].[ext]?[hash]' + } + } + ] + }, + resolve: { + alias: { + 'vue$': 'vue/dist/vue.esm.js' + }, + extensions: ['*', '.js', '.vue', '.json'] + } +} diff --git a/settings/webpack.dev.js b/settings/webpack.dev.js new file mode 100644 index 0000000000..88409bbb1d --- /dev/null +++ b/settings/webpack.dev.js @@ -0,0 +1,12 @@ +const merge = require('webpack-merge'); +const common = require('./webpack.common.js'); + +module.exports = merge(common, { + mode: 'development', + devServer: { + historyApiFallback: true, + noInfo: true, + overlay: true + }, + devtool: '#eval-source-map', +}) diff --git a/settings/webpack.prod.js b/settings/webpack.prod.js new file mode 100644 index 0000000000..f081567bd6 --- /dev/null +++ b/settings/webpack.prod.js @@ -0,0 +1,7 @@ +const merge = require('webpack-merge') +const common = require('./webpack.common.js') + +module.exports = merge(common, { + mode: 'production', + devtool: '#source-map' +}) diff --git a/tests/Settings/ApplicationTest.php b/tests/Settings/ApplicationTest.php index 31391a8fac..eab7d5bd8c 100644 --- a/tests/Settings/ApplicationTest.php +++ b/tests/Settings/ApplicationTest.php @@ -72,7 +72,6 @@ class ApplicationTest extends TestCase { [AuthSettingsController::class, Controller::class], // Needs session: [CertificateController::class, Controller::class], [CheckSetupController::class, Controller::class], - [GroupsController::class, Controller::class], [LogSettingsController::class, Controller::class], [MailSettingsController::class, Controller::class], [UsersController::class, Controller::class], diff --git a/tests/Settings/Controller/GroupsControllerTest.php b/tests/Settings/Controller/GroupsControllerTest.php deleted file mode 100644 index d43d4faf21..0000000000 --- a/tests/Settings/Controller/GroupsControllerTest.php +++ /dev/null @@ -1,381 +0,0 @@ -groupManager = $this->createMock(IGroupManager::class); - $this->userSession = $this->createMock(IUserSession::class); - $l = $this->createMock(IL10N::class); - $l->method('t') - ->will($this->returnCallback(function($text, $parameters = []) { - return vsprintf($text, $parameters); - })); - $this->groupsController = new GroupsController( - 'settings', - $this->createMock(IRequest::class), - $this->groupManager, - $this->userSession, - true, - $l - ); - - } - - /** - * TODO: Since GroupManager uses the static OC_Subadmin class it can't be mocked - * to test for subadmins. Thus the test always assumes you have admin permissions... - */ - public function testIndexSortByName() { - $firstGroup = $this->getMockBuilder(Group::class) - ->disableOriginalConstructor()->getMock(); - $firstGroup - ->method('getGID') - ->will($this->returnValue('firstGroup')); - $firstGroup - ->method('getDisplayName') - ->will($this->returnValue('First group')); - $firstGroup - ->method('count') - ->will($this->returnValue(12)); - $secondGroup = $this->getMockBuilder(Group::class) - ->disableOriginalConstructor()->getMock(); - $secondGroup - ->method('getGID') - ->will($this->returnValue('secondGroup')); - $secondGroup - ->method('getDisplayName') - ->will($this->returnValue('Second group')); - $secondGroup - ->method('count') - ->will($this->returnValue(25)); - $thirdGroup = $this->getMockBuilder(Group::class) - ->disableOriginalConstructor()->getMock(); - $thirdGroup - ->method('getGID') - ->will($this->returnValue('thirdGroup')); - $thirdGroup - ->method('getDisplayName') - ->will($this->returnValue('Third group')); - $thirdGroup - ->method('count') - ->will($this->returnValue(14)); - $fourthGroup = $this->getMockBuilder(Group::class) - ->disableOriginalConstructor()->getMock(); - $fourthGroup - ->method('getGID') - ->will($this->returnValue('admin')); - $fourthGroup - ->method('getDisplayName') - ->will($this->returnValue('Admin')); - $fourthGroup - ->method('count') - ->will($this->returnValue(18)); - /** @var \OC\Group\Group[] $groups */ - $groups = array(); - $groups[] = $firstGroup; - $groups[] = $secondGroup; - $groups[] = $thirdGroup; - $groups[] = $fourthGroup; - - $user = $this->getMockBuilder(User::class) - ->disableOriginalConstructor()->getMock(); - $this->userSession - ->expects($this->once()) - ->method('getUser') - ->will($this->returnValue($user)); - $user - ->expects($this->once()) - ->method('getUID') - ->will($this->returnValue('MyAdminUser')); - $this->groupManager->method('search') - ->will($this->returnValue($groups)); - - $expectedResponse = new DataResponse( - array( - 'data' => array( - 'adminGroups' => array( - 0 => array( - 'id' => 'admin', - 'name' => 'Admin', - 'usercount' => 0,//User count disabled 18, - ) - ), - 'groups' => - array( - 0 => array( - 'id' => 'firstGroup', - 'name' => 'First group', - 'usercount' => 0,//User count disabled 12, - ), - 1 => array( - 'id' => 'secondGroup', - 'name' => 'Second group', - 'usercount' => 0,//User count disabled 25, - ), - 2 => array( - 'id' => 'thirdGroup', - 'name' => 'Third group', - 'usercount' => 0,//User count disabled 14, - ), - ) - ) - ) - ); - $response = $this->groupsController->index('', false, MetaData::SORT_GROUPNAME); - $this->assertEquals($expectedResponse, $response); - } - - /** - * TODO: Since GroupManager uses the static OC_Subadmin class it can't be mocked - * to test for subadmins. Thus the test always assumes you have admin permissions... - */ - public function testIndexSortbyCount() { - $firstGroup = $this->getMockBuilder(Group::class) - ->disableOriginalConstructor()->getMock(); - $firstGroup - ->method('getGID') - ->will($this->returnValue('firstGroup')); - $firstGroup - ->method('getDisplayName') - ->will($this->returnValue('First group')); - $firstGroup - ->method('count') - ->will($this->returnValue(12)); - $secondGroup = $this->getMockBuilder(Group::class) - ->disableOriginalConstructor()->getMock(); - $secondGroup - ->method('getGID') - ->will($this->returnValue('secondGroup')); - $secondGroup - ->method('getDisplayName') - ->will($this->returnValue('Second group')); - $secondGroup - ->method('count') - ->will($this->returnValue(25)); - $thirdGroup = $this->getMockBuilder(Group::class) - ->disableOriginalConstructor()->getMock(); - $thirdGroup - ->method('getGID') - ->will($this->returnValue('thirdGroup')); - $thirdGroup - ->method('getDisplayName') - ->will($this->returnValue('Third group')); - $thirdGroup - ->method('count') - ->will($this->returnValue(14)); - $fourthGroup = $this->getMockBuilder(Group::class) - ->disableOriginalConstructor()->getMock(); - $fourthGroup - ->method('getGID') - ->will($this->returnValue('admin')); - $fourthGroup - ->method('getDisplayName') - ->will($this->returnValue('Admin')); - $fourthGroup - ->method('count') - ->will($this->returnValue(18)); - /** @var \OC\Group\Group[] $groups */ - $groups = array(); - $groups[] = $firstGroup; - $groups[] = $secondGroup; - $groups[] = $thirdGroup; - $groups[] = $fourthGroup; - - $user = $this->getMockBuilder(User::class) - ->disableOriginalConstructor()->getMock(); - $this->userSession - ->expects($this->once()) - ->method('getUser') - ->will($this->returnValue($user)); - $user - ->expects($this->once()) - ->method('getUID') - ->will($this->returnValue('MyAdminUser')); - $this->groupManager - ->method('search') - ->will($this->returnValue($groups)); - - $expectedResponse = new DataResponse( - array( - 'data' => array( - 'adminGroups' => array( - 0 => array( - 'id' => 'admin', - 'name' => 'Admin', - 'usercount' => 18, - ) - ), - 'groups' => - array( - 0 => array( - 'id' => 'secondGroup', - 'name' => 'Second group', - 'usercount' => 25, - ), - 1 => array( - 'id' => 'thirdGroup', - 'name' => 'Third group', - 'usercount' => 14, - ), - 2 => array( - 'id' => 'firstGroup', - 'name' => 'First group', - 'usercount' => 12, - ), - ) - ) - ) - ); - $response = $this->groupsController->index(); - $this->assertEquals($expectedResponse, $response); - } - - public function testCreateWithExistingGroup() { - $this->groupManager - ->expects($this->once()) - ->method('groupExists') - ->with('ExistingGroup') - ->will($this->returnValue(true)); - - $expectedResponse = new DataResponse( - array( - 'message' => 'Group already exists.' - ), - Http::STATUS_CONFLICT - ); - $response = $this->groupsController->create('ExistingGroup'); - $this->assertEquals($expectedResponse, $response); - } - - public function testCreateSuccessful() { - $group = $this->getMockBuilder(Group::class) - ->disableOriginalConstructor()->getMock(); - $this->groupManager - ->expects($this->once()) - ->method('groupExists') - ->with('NewGroup') - ->will($this->returnValue(false)); - $this->groupManager - ->expects($this->once()) - ->method('createGroup') - ->with('NewGroup') - ->will($this->returnValue($group)); - $group - ->expects($this->once()) - ->method('getDisplayName') - ->will($this->returnValue('NewGroup')); - - $expectedResponse = new DataResponse( - array( - 'groupname' => 'NewGroup' - ), - Http::STATUS_CREATED - ); - $response = $this->groupsController->create('NewGroup'); - $this->assertEquals($expectedResponse, $response); - } - - public function testCreateUnsuccessful() { - $this->groupManager - ->expects($this->once()) - ->method('groupExists') - ->with('NewGroup') - ->will($this->returnValue(false)); - $this->groupManager - ->expects($this->once()) - ->method('createGroup') - ->with('NewGroup') - ->will($this->returnValue(false)); - - $expectedResponse = new DataResponse( - array( - 'status' => 'error', - 'data' => array('message' => 'Unable to add group.') - ), - Http::STATUS_FORBIDDEN - ); - $response = $this->groupsController->create('NewGroup'); - $this->assertEquals($expectedResponse, $response); - } - - public function testDestroySuccessful() { - $group = $this->getMockBuilder(Group::class) - ->disableOriginalConstructor()->getMock(); - $this->groupManager - ->expects($this->once()) - ->method('get') - ->with('ExistingGroup') - ->will($this->returnValue($group)); - $group - ->expects($this->once()) - ->method('delete') - ->will($this->returnValue(true)); - $group - ->method('getDisplayName') - ->will($this->returnValue('ExistingGroup')); - - $expectedResponse = new DataResponse( - array( - 'status' => 'success', - 'data' => array('groupname' => 'ExistingGroup') - ), - Http::STATUS_NO_CONTENT - ); - $response = $this->groupsController->destroy('ExistingGroup'); - $this->assertEquals($expectedResponse, $response); - } - - public function testDestroyUnsuccessful() { - $this->groupManager - ->expects($this->once()) - ->method('get') - ->with('ExistingGroup') - ->will($this->returnValue(null)); - - $expectedResponse = new DataResponse( - array( - 'status' => 'error', - 'data' => array('message' => 'Unable to delete group.') - ), - Http::STATUS_FORBIDDEN - ); - $response = $this->groupsController->destroy('ExistingGroup'); - $this->assertEquals($expectedResponse, $response); - } - -} diff --git a/tests/Settings/Controller/UsersControllerTest.php b/tests/Settings/Controller/UsersControllerTest.php index 4cfec07f78..74ac990be9 100644 --- a/tests/Settings/Controller/UsersControllerTest.php +++ b/tests/Settings/Controller/UsersControllerTest.php @@ -31,10 +31,10 @@ use OCP\IGroupManager; use OCP\IL10N; use OCP\ILogger; use OCP\IRequest; -use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserManager; use OCP\IUserSession; +use OCP\L10N\IFactory; use OCP\Mail\IEMailTemplate; use OCP\Mail\IMailer; use OCP\Security\ISecureRandom; @@ -60,8 +60,8 @@ class UsersControllerTest extends \Test\TestCase { private $logger; /** @var IMailer|\PHPUnit_Framework_MockObject_MockObject */ private $mailer; - /** @var IURLGenerator|\PHPUnit_Framework_MockObject_MockObject */ - private $urlGenerator; + /** @var IFactory|\PHPUnit_Framework_MockObject_MockObject */ + private $l10nFactory; /** @var IAppManager|\PHPUnit_Framework_MockObject_MockObject */ private $appManager; /** @var IAvatarManager|\PHPUnit_Framework_MockObject_MockObject */ @@ -78,8 +78,6 @@ class UsersControllerTest extends \Test\TestCase { private $jobList; /** @var \OC\Security\IdentityProof\Manager |\PHPUnit_Framework_MockObject_MockObject */ private $securityManager; - /** @var IUserMountCache |\PHPUnit_Framework_MockObject_MockObject */ - private $userMountCache; /** @var IManager | \PHPUnit_Framework_MockObject_MockObject */ private $encryptionManager; /** @var IEncryptionModule | \PHPUnit_Framework_MockObject_MockObject */ @@ -88,46 +86,28 @@ class UsersControllerTest extends \Test\TestCase { protected function setUp() { parent::setUp(); - $this->groupManager = $this->createMock(Manager::class); $this->userManager = $this->createMock(IUserManager::class); + $this->groupManager = $this->createMock(Manager::class); $this->userSession = $this->createMock(IUserSession::class); $this->config = $this->createMock(IConfig::class); - $this->logger = $this->createMock(ILogger::class); + $this->l = $this->createMock(IL10N::class); $this->mailer = $this->createMock(IMailer::class); - $this->urlGenerator = $this->createMock(IURLGenerator::class); + $this->l10nFactory = $this->createMock(IFactory::class); $this->appManager = $this->createMock(IAppManager::class); - $this->avatarManager = $this->createMock(IAvatarManager::class); $this->accountManager = $this->createMock(AccountManager::class); - $this->secureRandom = $this->createMock(ISecureRandom::class); - $this->newUserMailHelper = $this->createMock(NewUserMailHelper::class); $this->securityManager = $this->getMockBuilder(\OC\Security\IdentityProof\Manager::class)->disableOriginalConstructor()->getMock(); $this->jobList = $this->createMock(IJobList::class); $this->encryptionManager = $this->createMock(IManager::class); - $this->l = $this->createMock(IL10N::class); + $this->l->method('t') ->will($this->returnCallback(function ($text, $parameters = []) { return vsprintf($text, $parameters); })); - $this->userMountCache = $this->createMock(IUserMountCache::class); $this->encryptionModule = $this->createMock(IEncryptionModule::class); $this->encryptionManager->expects($this->any())->method('getEncryptionModules') ->willReturn(['encryptionModule' => ['callback' => function() { return $this->encryptionModule;}]]); - /* - * Set default avatar behaviour for whole test suite - */ - - $avatarExists = $this->createMock(IAvatar::class); - $avatarExists->method('exists')->willReturn(true); - $avatarNotExists = $this->createMock(IAvatar::class); - $avatarNotExists->method('exists')->willReturn(false); - $this->avatarManager->method('getAvatar') - ->will($this->returnValueMap([ - ['foo', $avatarExists], - ['bar', $avatarExists], - ['admin', $avatarNotExists], - ])); } /** @@ -145,17 +125,12 @@ class UsersControllerTest extends \Test\TestCase { $this->config, $isAdmin, $this->l, - $this->logger, $this->mailer, - $this->urlGenerator, + $this->l10nFactory, $this->appManager, - $this->avatarManager, $this->accountManager, - $this->secureRandom, - $this->newUserMailHelper, $this->securityManager, $this->jobList, - $this->userMountCache, $this->encryptionManager ); } else { @@ -170,1926 +145,18 @@ class UsersControllerTest extends \Test\TestCase { $this->config, $isAdmin, $this->l, - $this->logger, $this->mailer, - $this->urlGenerator, + $this->l10nFactory, $this->appManager, - $this->avatarManager, $this->accountManager, - $this->secureRandom, - $this->newUserMailHelper, $this->securityManager, $this->jobList, - $this->userMountCache, $this->encryptionManager ] )->setMethods($mockedMethods)->getMock(); } } - public function testIndexAdmin() { - $controller = $this->getController(true); - - $foo = $this->createMock(User::class); - $foo - ->expects($this->exactly(2)) - ->method('getUID') - ->will($this->returnValue('foo')); - $foo - ->expects($this->once()) - ->method('getDisplayName') - ->will($this->returnValue('M. Foo')); - $foo - ->expects($this->once()) - ->method('getEMailAddress') - ->will($this->returnValue('foo@bar.com')); - $foo - ->expects($this->exactly(2)) - ->method('getQuota') - ->will($this->returnValue(1024)); - $foo - ->method('getLastLogin') - ->will($this->returnValue(500)); - $foo - ->method('getHome') - ->will($this->returnValue('/home/foo')); - $foo - ->expects($this->once()) - ->method('getBackendClassName') - ->will($this->returnValue('OC_User_Database')); - $foo->expects($this->any()) - ->method('isEnabled') - ->willReturn(true); - $admin = $this->createMock(User::class); - $admin - ->expects($this->exactly(2)) - ->method('getUID') - ->will($this->returnValue('admin')); - $admin - ->expects($this->once()) - ->method('getDisplayName') - ->will($this->returnValue('S. Admin')); - $admin - ->expects($this->once()) - ->method('getEMailAddress') - ->will($this->returnValue('admin@bar.com')); - $admin - ->expects($this->exactly(2)) - ->method('getQuota') - ->will($this->returnValue(404)); - $admin - ->expects($this->once()) - ->method('getLastLogin') - ->will($this->returnValue(12)); - $admin - ->expects($this->once()) - ->method('getHome') - ->will($this->returnValue('/home/admin')); - $admin - ->expects($this->once()) - ->method('getBackendClassName') - ->willReturn(Dummy::class); - $admin->expects($this->any()) - ->method('isEnabled') - ->willReturn(true); - $bar = $this->createMock(User::class); - $bar - ->expects($this->exactly(2)) - ->method('getUID') - ->will($this->returnValue('bar')); - $bar - ->expects($this->once()) - ->method('getDisplayName') - ->will($this->returnValue('B. Ar')); - $bar - ->expects($this->once()) - ->method('getEMailAddress') - ->will($this->returnValue('bar@dummy.com')); - $bar - ->expects($this->exactly(2)) - ->method('getQuota') - ->will($this->returnValue(2323)); - $bar - ->method('getLastLogin') - ->will($this->returnValue(3999)); - $bar - ->method('getHome') - ->will($this->returnValue('/home/bar')); - $bar - ->expects($this->once()) - ->method('getBackendClassName') - ->willReturn(Dummy::class); - $bar->expects($this->at(0)) - ->method('isEnabled') - ->willReturn(true); - $bar->expects($this->at(1)) - ->method('isEnabled') - ->willReturn(true); - $bar->expects($this->at(2)) - ->method('isEnabled') - ->willReturn(false); - - $this->groupManager - ->expects($this->once()) - ->method('displayNamesInGroup') - ->with('gid', 'pattern') - ->will($this->returnValue(array('foo' => 'M. Foo', 'admin' => 'S. Admin', 'bar' => 'B. Ar'))); - $this->groupManager - ->expects($this->exactly(3)) - ->method('getUserGroupNames') - ->will($this->onConsecutiveCalls( - array( - 'Users' => array('displayName' => 'Users'), - 'Support' => array('displayName' => 'Support') - ), - array( - 'admins' => array('displayName' => 'admins'), - 'Support' => array('displayName' => 'Support') - ), - array( - 'External Users' => array('displayName' => 'External Users') - ) - )); - $this->userManager - ->expects($this->at(0)) - ->method('get') - ->with('foo') - ->will($this->returnValue($foo)); - $this->userManager - ->expects($this->at(1)) - ->method('get') - ->with('admin') - ->will($this->returnValue($admin)); - $this->userManager - ->expects($this->at(2)) - ->method('get') - ->with('bar') - ->will($this->returnValue($bar)); - - $subadmin = $this->getMockBuilder(SubAdmin::class) - ->disableOriginalConstructor() - ->getMock(); - $subadmin - ->expects($this->any()) - ->method('getSubAdminsGroupsName') - ->with($foo) - ->will($this->returnValue([])); - $subadmin - ->expects($this->any()) - ->method('getSubAdminsGroupsName') - ->with($admin) - ->will($this->returnValue([])); - $subadmin - ->expects($this->any()) - ->method('getSubAdminsGroupsName') - ->with($bar) - ->will($this->returnValue([])); - $this->groupManager - ->expects($this->any()) - ->method('getSubAdmin') - ->will($this->returnValue($subadmin)); - - $this->userMountCache - ->expects($this->once()) - ->method('getUsedSpaceForUsers') - ->will($this->returnValue(['admin' => 200, 'bar' => 2000, 'foo' => 512])); - - $expectedResponse = new DataResponse( - array( - 0 => array( - 'name' => 'foo', - 'displayname' => 'M. Foo', - 'groups' => array( - 'Users' => array('displayName' => 'Users'), - 'Support' => array('displayName' => 'Support') - ), - 'subadmin' => array(), - 'quota' => 1024, - 'quota_bytes' => 1024.0, - 'storageLocation' => '/home/foo', - 'lastLogin' => 500000, - 'backend' => 'OC_User_Database', - 'email' => 'foo@bar.com', - 'isRestoreDisabled' => false, - 'isAvatarAvailable' => true, - 'isEnabled' => true, - 'size' => 512, - ), - 1 => array( - 'name' => 'admin', - 'displayname' => 'S. Admin', - 'groups' => array( - 'admins' => array('displayName' => 'admins'), - 'Support' => array('displayName' => 'Support') - ), - 'subadmin' => array(), - 'quota' => 404, - 'quota_bytes' => 404.0, - 'storageLocation' => '/home/admin', - 'lastLogin' => 12000, - 'backend' => Dummy::class, - 'email' => 'admin@bar.com', - 'isRestoreDisabled' => false, - 'isAvatarAvailable' => false, - 'isEnabled' => true, - 'size' => 200, - ), - 2 => array( - 'name' => 'bar', - 'displayname' => 'B. Ar', - 'groups' => array( - 'External Users' => array('displayName' => 'External Users') - ), - 'subadmin' => array(), - 'quota' => 2323, - 'quota_bytes' => 2323.0, - 'storageLocation' => '/home/bar', - 'lastLogin' => 3999000, - 'backend' => Dummy::class, - 'email' => 'bar@dummy.com', - 'isRestoreDisabled' => false, - 'isAvatarAvailable' => true, - 'isEnabled' => false, - 'size' => 2000, - ), - ) - ); - $response = $controller->index(0, 10, 'gid', 'pattern'); - $this->assertEquals($expectedResponse, $response); - } - - public function testIndexSubAdmin() { - $controller = $this->getController(false); - - $user = $this->createMock(User::class); - $this->userSession - ->expects($this->once()) - ->method('getUser') - ->will($this->returnValue($user)); - - $foo = $this->createMock(User::class); - $foo - ->expects($this->exactly(2)) - ->method('getUID') - ->will($this->returnValue('foo')); - $foo - ->expects($this->once()) - ->method('getDisplayName') - ->will($this->returnValue('M. Foo')); - $foo - ->expects($this->once()) - ->method('getEMailAddress') - ->will($this->returnValue('foo@bar.com')); - $foo - ->expects($this->exactly(2)) - ->method('getQuota') - ->will($this->returnValue('1024')); - $foo - ->method('getLastLogin') - ->will($this->returnValue(500)); - $foo - ->method('getHome') - ->will($this->returnValue('/home/foo')); - $foo - ->expects($this->once()) - ->method('getBackendClassName') - ->will($this->returnValue('OC_User_Database')); - $foo->expects($this->any()) - ->method('isEnabled') - ->willReturn(true); - $admin = $this->createMock(User::class); - $admin - ->expects($this->exactly(2)) - ->method('getUID') - ->will($this->returnValue('admin')); - $admin - ->expects($this->once()) - ->method('getDisplayName') - ->will($this->returnValue('S. Admin')); - $admin - ->expects($this->once()) - ->method('getEMailAddress') - ->will($this->returnValue('admin@bar.com')); - $admin - ->expects($this->exactly(2)) - ->method('getQuota') - ->will($this->returnValue('404')); - $admin - ->expects($this->once()) - ->method('getLastLogin') - ->will($this->returnValue(12)); - $admin - ->expects($this->once()) - ->method('getHome') - ->will($this->returnValue('/home/admin')); - $admin - ->expects($this->once()) - ->method('getBackendClassName') - ->willReturn(Dummy::class); - $admin->expects($this->any()) - ->method('isEnabled') - ->willReturn(true); - $bar = $this->createMock(User::class); - $bar - ->expects($this->exactly(2)) - ->method('getUID') - ->will($this->returnValue('bar')); - $bar - ->expects($this->once()) - ->method('getDisplayName') - ->will($this->returnValue('B. Ar')); - $bar - ->expects($this->once()) - ->method('getEMailAddress') - ->will($this->returnValue('bar@dummy.com')); - $bar - ->expects($this->exactly(2)) - ->method('getQuota') - ->will($this->returnValue('2323')); - $bar - ->method('getLastLogin') - ->will($this->returnValue(3999)); - $bar - ->method('getHome') - ->will($this->returnValue('/home/bar')); - $bar - ->expects($this->once()) - ->method('getBackendClassName') - ->willReturn(Dummy::class); - $bar->expects($this->any()) - ->method('isEnabled') - ->willReturn(true); - - $this->groupManager - ->expects($this->at(2)) - ->method('displayNamesInGroup') - ->with('SubGroup2', 'pattern') - ->will($this->returnValue(['foo' => 'M. Foo', 'admin' => 'S. Admin'])); - $this->groupManager - ->expects($this->at(1)) - ->method('displayNamesInGroup') - ->with('SubGroup1', 'pattern') - ->will($this->returnValue(['bar' => 'B. Ar'])); - $this->groupManager - ->expects($this->exactly(3)) - ->method('getUserGroupIds') - ->will($this->onConsecutiveCalls( - ['admin', 'SubGroup1', 'testGroup'], - ['SubGroup2', 'SubGroup1'], - ['SubGroup2', 'Foo'] - )); - $this->userManager - ->expects($this->at(0)) - ->method('get') - ->with('bar') - ->will($this->returnValue($bar)); - $this->userManager - ->expects($this->at(1)) - ->method('get') - ->with('foo') - ->will($this->returnValue($foo)); - $this->userManager - ->expects($this->at(2)) - ->method('get') - ->with('admin') - ->will($this->returnValue($admin)); - - $subgroup1 = $this->getMockBuilder(IGroup::class) - ->disableOriginalConstructor() - ->getMock(); - $subgroup1->expects($this->any()) - ->method('getGID') - ->will($this->returnValue('SubGroup1')); - $subgroup2 = $this->getMockBuilder(IGroup::class) - ->disableOriginalConstructor() - ->getMock(); - $subgroup2->expects($this->any()) - ->method('getGID') - ->will($this->returnValue('SubGroup2')); - $subadmin = $this->getMockBuilder(SubAdmin::class) - ->disableOriginalConstructor() - ->getMock(); - $subadmin - ->expects($this->at(0)) - ->method('getSubAdminsGroups') - ->will($this->returnValue([$subgroup1, $subgroup2])); - $subadmin - ->expects($this->any()) - ->method('getSubAdminsGroupsName') - ->will($this->returnValue([])); - $subadmin - ->expects($this->any()) - ->method('getSubAdminsGroups') - ->will($this->returnValue([])); - $this->groupManager - ->expects($this->any()) - ->method('getSubAdmin') - ->will($this->returnValue($subadmin)); - - $this->userMountCache - ->expects($this->once()) - ->method('getUsedSpaceForUsers') - ->will($this->returnValue(['admin' => 200, 'bar' => 2000, 'foo' => 512])); - - $expectedResponse = new DataResponse( - [ - 0 => [ - 'name' => 'bar', - 'displayname' => 'B. Ar', - 'groups' => ['SubGroup1'], - 'subadmin' => [], - 'quota' => '2323', - 'quota_bytes' => 2323.0, - 'storageLocation' => '/home/bar', - 'lastLogin' => 3999000, - 'backend' => Dummy::class, - 'email' => 'bar@dummy.com', - 'isRestoreDisabled' => false, - 'isAvatarAvailable' => true, - 'isEnabled' => true, - 'size' => 2000, - ], - 1=> [ - 'name' => 'foo', - 'displayname' => 'M. Foo', - 'groups' => ['SubGroup2', 'SubGroup1'], - 'subadmin' => [], - 'quota' => '1024', - 'quota_bytes' => 1024.0, - 'storageLocation' => '/home/foo', - 'lastLogin' => 500000, - 'backend' => 'OC_User_Database', - 'email' => 'foo@bar.com', - 'isRestoreDisabled' => false, - 'isAvatarAvailable' => true, - 'isEnabled' => true, - 'size' => 512, - ], - 2 => [ - 'name' => 'admin', - 'displayname' => 'S. Admin', - 'groups' => ['SubGroup2'], - 'subadmin' => [], - 'quota' => '404', - 'quota_bytes' => 404.0, - 'storageLocation' => '/home/admin', - 'lastLogin' => 12000, - 'backend' => Dummy::class, - 'email' => 'admin@bar.com', - 'isRestoreDisabled' => false, - 'isAvatarAvailable' => false, - 'isEnabled' => true, - 'size' => 200, - ], - ] - ); - - $response = $controller->index(0, 10, '', 'pattern'); - $this->assertEquals($expectedResponse, $response); - } - - /** - * TODO: Since the function uses the static OC_Subadmin class it can't be mocked - * to test for subadmins. Thus the test always assumes you have admin permissions... - */ - public function testIndexWithSearch() { - $controller = $this->getController(true); - - $foo = $this->createMock(User::class); - $foo - ->expects($this->exactly(2)) - ->method('getUID') - ->will($this->returnValue('foo')); - $foo - ->expects($this->once()) - ->method('getDisplayName') - ->will($this->returnValue('M. Foo')); - $foo - ->expects($this->once()) - ->method('getEMailAddress') - ->will($this->returnValue('foo@bar.com')); - $foo - ->expects($this->exactly(2)) - ->method('getQuota') - ->will($this->returnValue('1024')); - $foo - ->method('getLastLogin') - ->will($this->returnValue(500)); - $foo - ->method('getHome') - ->will($this->returnValue('/home/foo')); - $foo - ->expects($this->once()) - ->method('getBackendClassName') - ->will($this->returnValue('OC_User_Database')); - $foo->expects($this->any()) - ->method('isEnabled') - ->willReturn(true); - $admin = $this->createMock(User::class); - $admin - ->expects($this->exactly(2)) - ->method('getUID') - ->will($this->returnValue('admin')); - $admin - ->expects($this->once()) - ->method('getDisplayName') - ->will($this->returnValue('S. Admin')); - $admin - ->expects($this->once()) - ->method('getEMailAddress') - ->will($this->returnValue('admin@bar.com')); - $admin - ->expects($this->exactly(2)) - ->method('getQuota') - ->will($this->returnValue('404')); - $admin - ->expects($this->once()) - ->method('getLastLogin') - ->will($this->returnValue(12)); - $admin - ->expects($this->once()) - ->method('getHome') - ->will($this->returnValue('/home/admin')); - $admin - ->expects($this->once()) - ->method('getBackendClassName') - ->willReturn(Dummy::class); - $admin->expects($this->any()) - ->method('isEnabled') - ->willReturn(true); - $bar = $this->createMock(User::class); - $bar - ->expects($this->exactly(2)) - ->method('getUID') - ->will($this->returnValue('bar')); - $bar - ->expects($this->once()) - ->method('getDisplayName') - ->will($this->returnValue('B. Ar')); - $bar - ->expects($this->once()) - ->method('getEMailAddress') - ->will($this->returnValue('bar@dummy.com')); - $bar - ->expects($this->exactly(2)) - ->method('getQuota') - ->will($this->returnValue('2323')); - $bar - ->method('getLastLogin') - ->will($this->returnValue(3999)); - $bar - ->method('getHome') - ->will($this->returnValue('/home/bar')); - $bar - ->expects($this->once()) - ->method('getBackendClassName') - ->willReturn(Dummy::class); - $bar->expects($this->any()) - ->method('isEnabled') - ->willReturn(true); - - $this->userManager - ->expects($this->once()) - ->method('search') - ->with('pattern', 10, 0) - ->will($this->returnValue([$foo, $admin, $bar])); - $this->groupManager - ->expects($this->exactly(3)) - ->method('getUserGroupNames') - ->will($this->onConsecutiveCalls( - array( - 'Users' => array('displayName' => 'Users'), - 'Support' => array('displayName' => 'Support') - ), - array( - 'admins' => array('displayName' => 'admins'), - 'Support' => array('displayName' => 'Support') - ), - array( - 'External Users' => array('displayName' => 'External Users') - ) - )); - - $subadmin = $this->getMockBuilder(SubAdmin::class) - ->disableOriginalConstructor() - ->getMock(); - $subadmin->expects($this->any()) - ->method('getSubAdminsGroupsName') - ->will($this->returnValue([])); - $this->groupManager - ->expects($this->any()) - ->method('getSubAdmin') - ->will($this->returnValue($subadmin)); - - $this->userMountCache - ->expects($this->once()) - ->method('getUsedSpaceForUsers') - ->will($this->returnValue(['admin' => 200, 'bar' => 2000, 'foo' => 512])); - - $expectedResponse = new DataResponse( - array( - 0 => array( - 'name' => 'foo', - 'displayname' => 'M. Foo', - 'groups' => array( - 'Users' => array('displayName' => 'Users'), - 'Support' => array('displayName' => 'Support') - ), - 'subadmin' => array(), - 'quota' => 1024, - 'quota_bytes' => 1024, - 'storageLocation' => '/home/foo', - 'lastLogin' => 500000, - 'backend' => 'OC_User_Database', - 'email' => 'foo@bar.com', - 'isRestoreDisabled' => false, - 'isAvatarAvailable' => true, - 'isEnabled' => true, - 'size' => 512, - ), - 1 => array( - 'name' => 'admin', - 'displayname' => 'S. Admin', - 'groups' => array( - 'admins' => array('displayName' => 'admins'), - 'Support' => array('displayName' => 'Support') - ), - 'subadmin' => array(), - 'quota' => 404, - 'quota_bytes' => 404, - 'storageLocation' => '/home/admin', - 'lastLogin' => 12000, - 'backend' => Dummy::class, - 'email' => 'admin@bar.com', - 'isRestoreDisabled' => false, - 'isAvatarAvailable' => false, - 'isEnabled' => true, - 'size' => 200, - ), - 2 => array( - 'name' => 'bar', - 'displayname' => 'B. Ar', - 'groups' => array( - 'External Users' => array('displayName' => 'External Users') - ), - 'subadmin' => array(), - 'quota' => 2323, - 'quota_bytes' => 2323, - 'storageLocation' => '/home/bar', - 'lastLogin' => 3999000, - 'backend' => Dummy::class, - 'email' => 'bar@dummy.com', - 'isRestoreDisabled' => false, - 'isAvatarAvailable' => true, - 'isEnabled' => true, - 'size' => 2000, - ), - ) - ); - $response = $controller->index(0, 10, '', 'pattern'); - $this->assertEquals($expectedResponse, $response); - } - - public function testIndexWithBackend() { - $controller = $this->getController(true); - - $user = $this->createMock(User::class); - $user - ->expects($this->exactly(2)) - ->method('getUID') - ->will($this->returnValue('foo')); - $user - ->expects($this->once()) - ->method('getDisplayName') - ->will($this->returnValue('M. Foo')); - $user - ->expects($this->once()) - ->method('getEMailAddress') - ->will($this->returnValue(null)); - $user - ->expects($this->exactly(2)) - ->method('getQuota') - ->will($this->returnValue('none')); - $user - ->method('getLastLogin') - ->will($this->returnValue(500)); - $user - ->method('getHome') - ->will($this->returnValue('/home/foo')); - $user - ->expects($this->once()) - ->method('getBackendClassName') - ->will($this->returnValue('OC_User_Database')); - $user->expects($this->any()) - ->method('isEnabled') - ->willReturn(true); - - $this->userManager - ->expects($this->once()) - ->method('getBackends') - ->will($this->returnValue([new Dummy(), new \OC\User\Database()])); - $this->userManager - ->expects($this->once()) - ->method('clearBackends'); - $this->userManager - ->expects($this->once()) - ->method('search') - ->with('') - ->will($this->returnValue([$user])); - - $subadmin = $this->getMockBuilder(SubAdmin::class) - ->disableOriginalConstructor() - ->getMock(); - $subadmin->expects($this->once()) - ->method('getSubAdminsGroupsName') - ->will($this->returnValue([])); - $this->groupManager - ->expects($this->any()) - ->method('getSubAdmin') - ->will($this->returnValue($subadmin)); - - $this->userMountCache - ->expects($this->once()) - ->method('getUsedSpaceForUsers') - ->will($this->returnValue(['foo' => 512])); - - $expectedResponse = new DataResponse( - array( - 0 => array( - 'name' => 'foo', - 'displayname' => 'M. Foo', - 'groups' => null, - 'subadmin' => array(), - 'quota' => 'none', - 'quota_bytes' => 0, - 'storageLocation' => '/home/foo', - 'lastLogin' => 500000, - 'backend' => 'OC_User_Database', - 'email' => null, - 'isRestoreDisabled' => false, - 'isAvatarAvailable' => true, - 'isEnabled' => true, - 'size' => 512, - ) - ) - ); - $response = $controller->index(0, 10, '','', Dummy::class); - $this->assertEquals($expectedResponse, $response); - } - - public function testIndexWithBackendNoUser() { - $controller = $this->getController(true); - - $this->userManager - ->expects($this->once()) - ->method('getBackends') - ->will($this->returnValue([new Dummy(), new \OC\User\Database()])); - $this->userManager - ->expects($this->once()) - ->method('search') - ->with('') - ->will($this->returnValue([])); - - $this->userMountCache - ->expects($this->once()) - ->method('getUsedSpaceForUsers') - ->will($this->returnValue([])); - - $expectedResponse = new DataResponse([]); - $response = $controller->index(0, 10, '','', Dummy::class); - $this->assertEquals($expectedResponse, $response); - } - - public function testCreateSuccessfulWithoutGroupAdmin() { - $controller = $this->getController(true); - - $user = $this->createMock(User::class); - $user - ->method('getHome') - ->will($this->returnValue('/home/user')); - $user - ->method('getUID') - ->will($this->returnValue('foo')); - $user - ->expects($this->once()) - ->method('getBackendClassName') - ->will($this->returnValue('bar')); - $user->expects($this->any()) - ->method('isEnabled') - ->willReturn(true); - - $this->userManager - ->expects($this->once()) - ->method('createUser') - ->will($this->onConsecutiveCalls($user)); - - $subadmin = $this->getMockBuilder(SubAdmin::class) - ->disableOriginalConstructor() - ->getMock(); - $subadmin - ->expects($this->any()) - ->method('getSubAdminsGroupsName') - ->with($user) - ->will($this->returnValue([])); - $this->groupManager - ->expects($this->any()) - ->method('getSubAdmin') - ->will($this->returnValue($subadmin)); - - $expectedResponse = new DataResponse( - array( - 'name' => 'foo', - 'groups' => null, - 'storageLocation' => '/home/user', - 'backend' => 'bar', - 'lastLogin' => null, - 'displayname' => null, - 'quota' => null, - 'subadmin' => array(), - 'email' => null, - 'isRestoreDisabled' => false, - 'isAvatarAvailable' => true, - 'isEnabled' => true, - 'quota_bytes' => false, - ), - Http::STATUS_CREATED - ); - $response = $controller->create('foo', 'password', array()); - $this->assertEquals($expectedResponse, $response); - } - - public function testCreateSuccessfulWithGroupAdmin() { - $controller = $this->getController(true); - - $user = $this->createMock(User::class); - $user - ->method('getHome') - ->will($this->returnValue('/home/user')); - $user - ->method('getHome') - ->will($this->returnValue('/home/user')); - $user - ->method('getUID') - ->will($this->returnValue('foo')); - $user - ->expects($this->once()) - ->method('getBackendClassName') - ->will($this->returnValue('bar')); - $user->expects($this->any()) - ->method('isEnabled') - ->willReturn(true); - $existingGroup = $this->getMockBuilder(IGroup::class) - ->disableOriginalConstructor()->getMock(); - $existingGroup - ->expects($this->once()) - ->method('addUser') - ->with($user); - $newGroup = $this->getMockBuilder(IGroup::class) - ->disableOriginalConstructor()->getMock(); - $newGroup - ->expects($this->once()) - ->method('addUser') - ->with($user); - - $this->userManager - ->expects($this->once()) - ->method('createUser') - ->will($this->onConsecutiveCalls($user)); - $this->groupManager - ->expects($this->exactly(2)) - ->method('get') - ->will($this->onConsecutiveCalls(null, $existingGroup)); - $this->groupManager - ->expects($this->once()) - ->method('createGroup') - ->with('NewGroup') - ->will($this->onConsecutiveCalls($newGroup)); - $this->groupManager - ->expects($this->once()) - ->method('getUserGroupNames') - ->with($user) - ->will($this->onConsecutiveCalls( - array( - 'NewGroup' => array('displayName' => 'NewGroup'), - 'ExistingGroup' => array('displayName' => 'ExistingGroup') - ) - )); - - $subadmin = $this->getMockBuilder(SubAdmin::class) - ->disableOriginalConstructor() - ->getMock(); - $subadmin - ->expects($this->once()) - ->method('getSubAdminsGroupsName') - ->with($user) - ->will($this->returnValue([])); - $this->groupManager - ->expects($this->any()) - ->method('getSubAdmin') - ->will($this->returnValue($subadmin)); - - $expectedResponse = new DataResponse( - array( - 'name' => 'foo', - 'groups' => array( - 'NewGroup' => array('displayName' => 'NewGroup'), - 'ExistingGroup' => array('displayName' => 'ExistingGroup') - ), - 'storageLocation' => '/home/user', - 'backend' => 'bar', - 'lastLogin' => null, - 'displayname' => null, - 'quota' => null, - 'subadmin' => array(), - 'email' => null, - 'isRestoreDisabled' => false, - 'isAvatarAvailable' => true, - 'isEnabled' => true, - 'quota_bytes' => false, - ), - Http::STATUS_CREATED - ); - $response = $controller->create('foo', 'password', array('NewGroup', 'ExistingGroup')); - $this->assertEquals($expectedResponse, $response); - } - - public function testCreateSuccessfulWithGroupSubAdmin() { - $controller = $this->getController(false); - $user = $this->createMock(IUser::class); - $this->userSession - ->expects($this->once()) - ->method('getUser') - ->will($this->returnValue($user)); - $newUser = $this->createMock(IUser::class); - $newUser - ->method('getHome') - ->will($this->returnValue('/home/user')); - $newUser - ->method('getHome') - ->will($this->returnValue('/home/user')); - $newUser - ->method('getUID') - ->will($this->returnValue('foo')); - $newUser - ->expects($this->once()) - ->method('getBackendClassName') - ->will($this->returnValue('bar')); - $subGroup1 = $this->createMock(IGroup::class); - $newUser->expects($this->any()) - ->method('isEnabled') - ->willReturn(true); - $subGroup1 - ->expects($this->any()) - ->method('getGID') - ->will($this->returnValue('SubGroup1')); - $subGroup1 - ->expects($this->once()) - ->method('addUser') - ->with($user); - $this->userManager - ->expects($this->once()) - ->method('createUser') - ->will($this->returnValue($newUser)); - $this->groupManager - ->expects($this->once()) - ->method('getUserGroupNames') - ->with($user) - ->will($this->onConsecutiveCalls(array('SubGroup1' => - array('displayName' => 'SubGroup1') - ))); - $this->groupManager - ->expects($this->once()) - ->method('getUserGroupNames') - ->with($newUser) - ->will($this->onConsecutiveCalls(['SubGroup1'])); - - $subadmin = $this->createMock(\OC\SubAdmin::class); - $subadmin->expects($this->atLeastOnce()) - ->method('getSubAdminsGroupsName') - ->with($user) - ->willReturnMap([ - [$user, [$subGroup1]], - [$newUser, []], - ]); - $subadmin->expects($this->atLeastOnce()) - ->method('isSubAdminofGroup') - ->willReturnMap([ - [$user, $subGroup1, true], - ]); - $this->groupManager - ->expects($this->any()) - ->method('getSubAdmin') - ->will($this->returnValue($subadmin)); - $this->groupManager->expects($this->atLeastOnce()) - ->method('get') - ->willReturnMap([ - ['SubGroup1', $subGroup1], - ]); - - $expectedResponse = new DataResponse( - array( - 'name' => 'foo', - 'groups' => array('SubGroup1' => array('displayName' => 'SubGroup1')), - 'storageLocation' => '/home/user', - 'backend' => 'bar', - 'lastLogin' => 0, - 'displayname' => null, - 'quota' => null, - 'subadmin' => [], - 'email' => null, - 'isRestoreDisabled' => false, - 'isAvatarAvailable' => true, - 'isEnabled' => true, - 'quota_bytes' => false, - ), - Http::STATUS_CREATED - ); - $response = $controller->create('foo', 'password', ['SubGroup1', 'ExistingGroup']); - $this->assertEquals($expectedResponse, $response); - } - - public function testCreateUnsuccessfulAdmin() { - $controller = $this->getController(true); - - $this->userManager - ->method('createUser') - ->will($this->throwException(new \Exception())); - - $expectedResponse = new DataResponse( - array( - 'message' => 'Unable to create user.' - ), - Http::STATUS_FORBIDDEN - ); - $response = $controller->create('foo', 'password', array()); - $this->assertEquals($expectedResponse, $response); - } - - public function testCreateUnsuccessfulSubAdminNoGroup() { - $controller = $this->getController(false); - $user = $this->createMock(IUser::class); - $user->expects($this->any()) - ->method('getUID') - ->will($this->returnValue('username')); - $this->userSession->expects($this->once()) - ->method('getUser') - ->will($this->returnValue($user)); - - $this->userManager->expects($this->never()) - ->method('createUser'); - - $expectedResponse = new DataResponse( - [ - 'message' => 'No valid group selected' - ], - Http::STATUS_FORBIDDEN - ); - $response = $controller->create('foo', 'password', []); - $this->assertEquals($expectedResponse, $response); - } - - public function testCreateUnsuccessfulSubAdmin() { - $controller = $this->getController(false); - $user = $this->createMock(IUser::class); - $user->expects($this->any()) - ->method('getUID') - ->will($this->returnValue('username')); - $this->userSession->expects($this->once()) - ->method('getUser') - ->will($this->returnValue($user)); - - $this->userManager - ->method('createUser') - ->will($this->throwException(new \Exception())); - - $subgroup1 = $this->createMock(IGroup::class); - $subgroup2 = $this->createMock(IGroup::class); - $subadmin = $this->createMock(\OC\SubAdmin::class); - $subadmin->expects($this->atLeastOnce()) - ->method('isSubAdminofGroup') - ->willReturnMap([ - [$user, $subgroup1, true], - [$user, $subgroup2, true], - ]); - $this->groupManager->expects($this->any()) - ->method('getSubAdmin') - ->willReturn($subadmin); - $this->groupManager->expects($this->atLeastOnce()) - ->method('get') - ->willReturnMap([ - ['SubGroup1', $subgroup1], - ['SubGroup2', $subgroup2], - ]); - - $expectedResponse = new DataResponse( - [ - 'message' => 'Unable to create user.' - ], - Http::STATUS_FORBIDDEN - ); - $response = $controller->create('foo', 'password', array('SubGroup1', 'SubGroup2')); - $this->assertEquals($expectedResponse, $response); - } - - public function testDestroySelfAdmin() { - $controller = $this->getController(true); - - $user = $this->createMock(User::class); - $user - ->expects($this->once()) - ->method('getUID') - ->will($this->returnValue('myself')); - $this->userSession - ->method('getUser') - ->will($this->returnValue($user)); - - $expectedResponse = new DataResponse( - array( - 'status' => 'error', - 'data' => array( - 'message' => 'Unable to delete user.' - ) - ), - Http::STATUS_FORBIDDEN - ); - $response = $controller->destroy('myself'); - $this->assertEquals($expectedResponse, $response); - } - - public function testDestroySelfSubadmin() { - $controller = $this->getController(false); - - $user = $this->createMock(User::class); - $user - ->expects($this->once()) - ->method('getUID') - ->will($this->returnValue('myself')); - $this->userSession - ->method('getUser') - ->will($this->returnValue($user)); - - $expectedResponse = new DataResponse( - array( - 'status' => 'error', - 'data' => array( - 'message' => 'Unable to delete user.' - ) - ), - Http::STATUS_FORBIDDEN - ); - $response = $controller->destroy('myself'); - $this->assertEquals($expectedResponse, $response); - } - - public function testDestroyAdmin() { - $controller = $this->getController(true); - - $user = $this->createMock(User::class); - $user - ->expects($this->once()) - ->method('getUID') - ->will($this->returnValue('Admin')); - $toDeleteUser = $this->createMock(User::class); - $toDeleteUser - ->expects($this->once()) - ->method('delete') - ->will($this->returnValue(true)); - $this->userSession - ->method('getUser') - ->will($this->returnValue($user)); - $this->userManager - ->method('get') - ->with('UserToDelete') - ->will($this->returnValue($toDeleteUser)); - - $expectedResponse = new DataResponse( - array( - 'status' => 'success', - 'data' => array( - 'username' => 'UserToDelete' - ) - ), - Http::STATUS_NO_CONTENT - ); - $response = $controller->destroy('UserToDelete'); - $this->assertEquals($expectedResponse, $response); - } - - public function testDestroySubAdmin() { - $controller = $this->getController(false); - $user = $this->createMock(User::class); - $user - ->expects($this->once()) - ->method('getUID') - ->will($this->returnValue('myself')); - $this->userSession - ->method('getUser') - ->will($this->returnValue($user)); - - $user = $this->createMock(User::class); - $toDeleteUser = $this->createMock(User::class); - $toDeleteUser - ->expects($this->once()) - ->method('delete') - ->will($this->returnValue(true)); - $this->userSession - ->method('getUser') - ->will($this->returnValue($user)); - $this->userManager - ->method('get') - ->with('UserToDelete') - ->will($this->returnValue($toDeleteUser)); - - $subadmin = $this->getMockBuilder(SubAdmin::class) - ->disableOriginalConstructor() - ->getMock(); - $subadmin->expects($this->once()) - ->method('isUserAccessible') - ->with($user, $toDeleteUser) - ->will($this->returnValue(true)); - $this->groupManager - ->expects($this->any()) - ->method('getSubAdmin') - ->will($this->returnValue($subadmin)); - - $expectedResponse = new DataResponse( - [ - 'status' => 'success', - 'data' => [ - 'username' => 'UserToDelete' - ] - ], - Http::STATUS_NO_CONTENT - ); - $response = $controller->destroy('UserToDelete'); - $this->assertEquals($expectedResponse, $response); - } - - public function testDestroyUnsuccessfulAdmin() { - $controller = $this->getController(true); - - $user = $this->createMock(User::class); - $user - ->expects($this->once()) - ->method('getUID') - ->will($this->returnValue('Admin')); - $toDeleteUser = $this->createMock(User::class); - $toDeleteUser - ->expects($this->once()) - ->method('delete') - ->will($this->returnValue(false)); - $this->userSession - ->method('getUser') - ->will($this->returnValue($user)); - $this->userManager - ->method('get') - ->with('UserToDelete') - ->will($this->returnValue($toDeleteUser)); - - $expectedResponse = new DataResponse( - array( - 'status' => 'error', - 'data' => array( - 'message' => 'Unable to delete user.' - ) - ), - Http::STATUS_FORBIDDEN - ); - $response = $controller->destroy('UserToDelete'); - $this->assertEquals($expectedResponse, $response); - } - - public function testDestroyUnsuccessfulSubAdmin() { - $controller = $this->getController(false); - $user = $this->createMock(User::class); - $user - ->expects($this->once()) - ->method('getUID') - ->will($this->returnValue('myself')); - $this->userSession - ->method('getUser') - ->will($this->returnValue($user)); - - $toDeleteUser = $this->createMock(User::class); - $toDeleteUser - ->expects($this->once()) - ->method('delete') - ->will($this->returnValue(false)); - $this->userSession - ->method('getUser') - ->will($this->returnValue($user)); - $this->userManager - ->method('get') - ->with('UserToDelete') - ->will($this->returnValue($toDeleteUser)); - - $subadmin = $this->getMockBuilder(SubAdmin::class) - ->disableOriginalConstructor() - ->getMock(); - $subadmin->expects($this->once()) - ->method('isUserAccessible') - ->with($user, $toDeleteUser) - ->will($this->returnValue(true)); - $this->groupManager - ->expects($this->any()) - ->method('getSubAdmin') - ->will($this->returnValue($subadmin)); - - $expectedResponse = new DataResponse( - [ - 'status' => 'error', - 'data' => [ - 'message' => 'Unable to delete user.' - ] - ], - Http::STATUS_FORBIDDEN - ); - $response = $controller->destroy('UserToDelete'); - $this->assertEquals($expectedResponse, $response); - } - - public function testDestroyNotAccessibleToSubAdmin() { - $controller = $this->getController(false); - - $user = $this->createMock(User::class); - $user - ->expects($this->once()) - ->method('getUID') - ->will($this->returnValue('myself')); - $this->userSession - ->method('getUser') - ->will($this->returnValue($user)); - - $toDeleteUser = $this->createMock(User::class); - $this->userSession - ->method('getUser') - ->will($this->returnValue($user)); - $this->userManager - ->method('get') - ->with('UserToDelete') - ->will($this->returnValue($toDeleteUser)); - - $subadmin = $this->getMockBuilder(SubAdmin::class) - ->disableOriginalConstructor() - ->getMock(); - $subadmin->expects($this->once()) - ->method('isUserAccessible') - ->with($user, $toDeleteUser) - ->will($this->returnValue(false)); - $this->groupManager - ->expects($this->any()) - ->method('getSubAdmin') - ->will($this->returnValue($subadmin)); - - $expectedResponse = new DataResponse( - [ - 'status' => 'error', - 'data' => [ - 'message' => 'Authentication error' - ] - ], - Http::STATUS_FORBIDDEN - ); - $response = $controller->destroy('UserToDelete'); - $this->assertEquals($expectedResponse, $response); - } - - /** - * test if an invalid mail result in a failure response - */ - public function testCreateUnsuccessfulWithInvalidEmailAdmin() { - $controller = $this->getController(true); - - $expectedResponse = new DataResponse([ - 'message' => 'Invalid mail address', - ], - Http::STATUS_UNPROCESSABLE_ENTITY - ); - $response = $controller->create('foo', 'password', [], 'invalidMailAdress'); - $this->assertEquals($expectedResponse, $response); - } - - /** - * test if a valid mail result in a successful mail send - */ - public function testCreateSuccessfulWithValidEmailAdmin() { - $controller = $this->getController(true); - $this->mailer - ->expects($this->at(0)) - ->method('validateMailAddress') - ->with('validMail@Adre.ss') - ->will($this->returnValue(true)); - - $user = $this->createMock(User::class); - $user - ->method('getHome') - ->will($this->returnValue('/home/user')); - $user - ->method('getHome') - ->will($this->returnValue('/home/user')); - $user - ->method('getUID') - ->will($this->returnValue('foo')); - $user - ->method('getDisplayName') - ->will($this->returnValue('foo')); - $user - ->expects($this->once()) - ->method('getBackendClassName') - ->will($this->returnValue('bar')); - - $emailTemplate = $this->createMock(IEMailTemplate::class); - $this->newUserMailHelper - ->expects($this->at(0)) - ->method('generateTemplate') - ->with($user, false) - ->willReturn($emailTemplate); - $this->newUserMailHelper - ->expects($this->at(1)) - ->method('sendMail') - ->with($user, $emailTemplate); - - $this->userManager - ->expects($this->once()) - ->method('createUser') - ->will($this->onConsecutiveCalls($user)); - $subadmin = $this->getMockBuilder(SubAdmin::class) - ->disableOriginalConstructor() - ->getMock(); - $subadmin->expects($this->once()) - ->method('getSubAdminsGroupsName') - ->with($user) - ->will($this->returnValue([])); - $this->groupManager - ->expects($this->any()) - ->method('getSubAdmin') - ->will($this->returnValue($subadmin)); - - $response = $controller->create('foo', 'password', [], 'validMail@Adre.ss'); - $this->assertEquals(Http::STATUS_CREATED, $response->getStatus()); - } - - private function mockUser($userId = 'foo', $displayName = 'M. Foo', - $lastLogin = 500, $home = '/home/foo', - $backend = 'OC_User_Database', $enabled = true) { - $user = $this->createMock(User::class); - $user - ->expects($this->any()) - ->method('getUID') - ->will($this->returnValue($userId)); - $user - ->expects($this->once()) - ->method('getDisplayName') - ->will($this->returnValue($displayName)); - $user - ->method('getLastLogin') - ->will($this->returnValue($lastLogin)); - $user - ->method('getHome') - ->will($this->returnValue($home)); - $user - ->expects($this->once()) - ->method('getBackendClassName') - ->will($this->returnValue($backend)); - $user->expects($this->any()) - ->method('isEnabled') - ->willReturn($enabled); - - $result = [ - 'name' => $userId, - 'displayname' => $displayName, - 'groups' => null, - 'subadmin' => array(), - 'quota' => null, - 'storageLocation' => $home, - 'lastLogin' => $lastLogin * 1000, - 'backend' => $backend, - 'email' => null, - 'isRestoreDisabled' => false, - 'isAvatarAvailable' => true, - 'isEnabled' => $enabled, - 'quota_bytes' => false, - ]; - - return [$user, $result]; - } - - public function testRestorePossibleWithoutEncryption() { - $controller = $this->getController(true); - - list($user, $expectedResult) = $this->mockUser(); - - $subadmin = $this->getMockBuilder(SubAdmin::class) - ->disableOriginalConstructor() - ->getMock(); - $subadmin->expects($this->once()) - ->method('getSubAdminsGroupsName') - ->with($user) - ->will($this->returnValue([])); - $this->groupManager - ->expects($this->any()) - ->method('getSubAdmin') - ->will($this->returnValue($subadmin)); - - $result = self::invokePrivate($controller, 'formatUserForIndex', [$user]); - $this->assertEquals($expectedResult, $result); - } - - public function testRestorePossibleWithAdminAndUserRestore() { - list($user, $expectedResult) = $this->mockUser(); - - $this->appManager - ->expects($this->once()) - ->method('isEnabledForUser') - ->with( - $this->equalTo('encryption') - ) - ->will($this->returnValue(true)); - $this->config - ->expects($this->once()) - ->method('getAppValue') - ->with( - $this->equalTo('encryption'), - $this->equalTo('recoveryAdminEnabled'), - $this->anything() - ) - ->will($this->returnValue('1')); - - $this->config - ->expects($this->at(1)) - ->method('getUserValue') - ->with( - $this->anything(), - $this->equalTo('encryption'), - $this->equalTo('recoveryEnabled'), - $this->anything() - ) - ->will($this->returnValue('1')); - - $subadmin = $this->getMockBuilder(SubAdmin::class) - ->disableOriginalConstructor() - ->getMock(); - $subadmin->expects($this->once()) - ->method('getSubAdminsGroupsName') - ->with($user) - ->will($this->returnValue([])); - $this->groupManager - ->expects($this->any()) - ->method('getSubAdmin') - ->will($this->returnValue($subadmin)); - - $controller = $this->getController(true); - $result = self::invokePrivate($controller, 'formatUserForIndex', [$user]); - $this->assertEquals($expectedResult, $result); - } - - /** - * @dataProvider dataTestRestoreNotPossibleWithoutAdminRestore - * - * @param bool $masterKeyEnabled - */ - public function testRestoreNotPossibleWithoutAdminRestore($masterKeyEnabled) { - list($user, $expectedResult) = $this->mockUser(); - - // without the master key enabled we use per-user keys - $this->encryptionModule->expects($this->once())->method('needDetailedAccessList')->willReturn(!$masterKeyEnabled); - - $this->appManager - ->method('isEnabledForUser') - ->with( - $this->equalTo('encryption') - ) - ->will($this->returnValue(true)); - - // without the master key enabled we use per-user keys -> restore is disabled - $expectedResult['isRestoreDisabled'] = !$masterKeyEnabled; - - $subadmin = $this->getMockBuilder(SubAdmin::class) - ->disableOriginalConstructor() - ->getMock(); - $subadmin->expects($this->once()) - ->method('getSubAdminsGroupsName') - ->with($user) - ->will($this->returnValue([])); - $this->groupManager - ->expects($this->any()) - ->method('getSubAdmin') - ->will($this->returnValue($subadmin)); - - $controller = $this->getController(true); - $result = self::invokePrivate($controller, 'formatUserForIndex', [$user]); - $this->assertEquals($expectedResult, $result); - } - - public function dataTestRestoreNotPossibleWithoutAdminRestore() { - return [ - [true], - [false] - ]; - } - - public function testRestoreNotPossibleWithoutUserRestore() { - list($user, $expectedResult) = $this->mockUser(); - - $this->appManager - ->expects($this->once()) - ->method('isEnabledForUser') - ->with( - $this->equalTo('encryption') - ) - ->will($this->returnValue(true)); - $this->config - ->expects($this->once()) - ->method('getAppValue') - ->with( - $this->equalTo('encryption'), - $this->equalTo('recoveryAdminEnabled'), - $this->anything() - ) - ->will($this->returnValue('1')); - - $this->config - ->expects($this->at(1)) - ->method('getUserValue') - ->with( - $this->anything(), - $this->equalTo('encryption'), - $this->equalTo('recoveryEnabled'), - $this->anything() - ) - ->will($this->returnValue('0')); - - $expectedResult['isRestoreDisabled'] = true; - - $subadmin = $this->getMockBuilder(SubAdmin::class) - ->disableOriginalConstructor() - ->getMock(); - $subadmin->expects($this->once()) - ->method('getSubAdminsGroupsName') - ->with($user) - ->will($this->returnValue([])); - $this->groupManager - ->expects($this->any()) - ->method('getSubAdmin') - ->will($this->returnValue($subadmin)); - - $controller = $this->getController(true); - $result = self::invokePrivate($controller, 'formatUserForIndex', [$user]); - $this->assertEquals($expectedResult, $result); - } - - public function testNoAvatar() { - $controller = $this->getController(true); - - list($user, $expectedResult) = $this->mockUser(); - - $subadmin = $this->getMockBuilder(SubAdmin::class) - ->disableOriginalConstructor() - ->getMock(); - $subadmin->expects($this->once()) - ->method('getSubAdminsGroupsName') - ->with($user) - ->will($this->returnValue([])); - $this->groupManager - ->expects($this->any()) - ->method('getSubAdmin') - ->will($this->returnValue($subadmin)); - - $this->avatarManager - ->method('getAvatar') - ->will($this->throwException(new \OCP\Files\NotFoundException())); - $expectedResult['isAvatarAvailable'] = false; - - $result = self::invokePrivate($controller, 'formatUserForIndex', [$user]); - $this->assertEquals($expectedResult, $result); - } - - public function testStatsAdmin() { - $controller = $this->getController(true); - - $this->userManager - ->expects($this->at(0)) - ->method('countUsers') - ->will($this->returnValue([128, 44])); - - $expectedResponse = new DataResponse( - [ - 'totalUsers' => 172 - ] - ); - $response = $controller->stats(); - $this->assertEquals($expectedResponse, $response); - } - - /** - * Tests that the subadmin stats return unique users, even - * when a user appears in several groups. - */ - public function testStatsSubAdmin() { - $controller = $this->getController(false); - - $user = $this->createMock(User::class); - - $this->userSession - ->expects($this->once()) - ->method('getUser') - ->will($this->returnValue($user)); - - $group1 = $this->getMockBuilder(Group::class) - ->disableOriginalConstructor()->getMock(); - $group1 - ->expects($this->once()) - ->method('getUsers') - ->will($this->returnValue(['foo' => 'M. Foo', 'admin' => 'S. Admin'])); - - $group2 = $this->getMockBuilder(Group::class) - ->disableOriginalConstructor()->getMock(); - $group2 - ->expects($this->once()) - ->method('getUsers') - ->will($this->returnValue(['bar' => 'B. Ar'])); - - $subadmin = $this->getMockBuilder(SubAdmin::class) - ->disableOriginalConstructor() - ->getMock(); - $subadmin - ->expects($this->at(0)) - ->method('getSubAdminsGroupsName') - ->will($this->returnValue([$group1, $group2])); - $subadmin - ->expects($this->at(0)) - ->method('getSubAdminsGroups') - ->will($this->returnValue([$group1, $group2])); - - $this->groupManager - ->expects($this->any()) - ->method('getSubAdmin') - ->will($this->returnValue($subadmin)); - - $expectedResponse = new DataResponse( - [ - 'totalUsers' => 3 - ] - ); - - $response = $controller->stats(); - $this->assertEquals($expectedResponse, $response); - } - - public function dataSetDisplayName() { - $data = []; - - $user1 = $this->createMock(IUser::class); - $user1->method('getUID')->willReturn('user1'); - $user1->method('canChangeDisplayName')->willReturn(true); - $data[] = [$user1, $user1, false, false, true]; - - $user1 = $this->createMock(IUser::class); - $user1->method('getUID')->willReturn('user1'); - $user1->method('canChangeDisplayName')->willReturn(false); - $data[] = [$user1, $user1, false, false, false]; - - $user1 = $this->createMock(IUser::class); - $user1->method('getUID')->willReturn('user1'); - $user2 = $this->createMock(IUser::class); - $user2->method('getUID')->willReturn('user2'); - $user2->method('canChangeDisplayName')->willReturn(true); - $data[] = [$user1, $user2, false, false, false]; - - $user1 = $this->createMock(IUser::class); - $user1->method('getUID')->willReturn('user1'); - $user2 = $this->createMock(IUser::class); - $user2->method('getUID')->willReturn('user2'); - $user2->method('canChangeDisplayName')->willReturn(true); - $data[] = [$user1, $user2, true, false, true]; - - $user1 = $this->createMock(IUser::class); - $user1->method('getUID')->willReturn('user1'); - $user2 = $this->createMock(IUser::class); - $user2->method('getUID')->willReturn('user2'); - $user2->method('canChangeDisplayName')->willReturn(true); - $data[] = [$user1, $user2, false, true, true]; - - return $data; - } - - /** - * @dataProvider dataSetDisplayName - * - * @param IUser|\PHPUnit_Framework_MockObject_MockObject $currentUser - * @param IUser|\PHPUnit_Framework_MockObject_MockObject $editUser - * @param bool $isAdmin - * @param bool $isSubAdmin - * @param bool $valid - */ - public function testSetDisplayName($currentUser, $editUser, $isAdmin, $isSubAdmin, $valid) { - $this->userSession - ->expects($this->once()) - ->method('getUser') - ->willReturn($currentUser); - $this->userManager - ->expects($this->once()) - ->method('get') - ->with($editUser->getUID()) - ->willReturn($editUser); - $this->accountManager->expects($this->any())->method('getUser')->willReturn([]); - - $subadmin = $this->getMockBuilder(SubAdmin::class) - ->disableOriginalConstructor() - ->getMock(); - $subadmin - ->method('isUserAccessible') - ->with($currentUser, $editUser) - ->willReturn($isSubAdmin); - - $this->groupManager - ->method('getSubAdmin') - ->willReturn($subadmin); - $this->groupManager - ->method('isAdmin') - ->with($currentUser->getUID()) - ->willReturn($isAdmin); - - if ($valid === true) { - $expectedResponse = new DataResponse( - [ - 'status' => 'success', - 'data' => [ - 'message' => 'Your full name has been changed.', - 'username' => $editUser->getUID(), - 'displayName' => 'newDisplayName', - ], - ] - ); - } else { - $expectedResponse = new DataResponse( - [ - 'status' => 'error', - 'data' => [ - 'message' => 'Authentication error', - ], - ] - ); - } - - $controller = $this->getController(true); - $response = $controller->setDisplayName($editUser->getUID(), 'newDisplayName'); - $this->assertEquals($expectedResponse, $response); - } - - public function testSetDisplayNameFails() { - /** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */ - $user = $this->createMock(IUser::class); - $user->method('canChangeDisplayname')->willReturn(true); - $user->method('getUID')->willReturn('user'); - $user->expects($this->once()) - ->method('setDisplayName') - ->with('newDisplayName') - ->willReturn(false); - $user->method('getDisplayName')->willReturn('oldDisplayName'); - - $this->userSession - ->expects($this->once()) - ->method('getUser') - ->willReturn($user); - - $this->userManager - ->expects($this->once()) - ->method('get') - ->with($user->getUID()) - ->willReturn($user); - - $subadmin = $this->getMockBuilder(SubAdmin::class) - ->disableOriginalConstructor() - ->getMock(); - $subadmin - ->method('isUserAccessible') - ->with($user, $user) - ->willReturn(false); - - $this->groupManager - ->method('getSubAdmin') - ->willReturn($subadmin); - $this->groupManager - ->expects($this->once()) - ->method('isAdmin') - ->with($user->getUID()) - ->willReturn(false); - - $expectedResponse = new DataResponse( - [ - 'status' => 'error', - 'data' => [ - 'message' => 'Unable to change full name', - 'displayName' => 'oldDisplayName', - ], - ] - ); - $controller = $this->getController(true); - $response = $controller->setDisplayName($user->getUID(), 'newDisplayName'); - $this->assertEquals($expectedResponse, $response); - } - /** * @dataProvider dataTestSetUserSettings * @@ -2360,158 +427,6 @@ class UsersControllerTest extends \Test\TestCase { ]; } - /** - * @return array - */ - public function setEmailAddressData() { - return [ - /* mailAddress, isValid, expectsUpdate, canChangeDisplayName, responseCode */ - [ '', true, true, true, Http::STATUS_OK ], - [ 'foo@local', true, true, true, Http::STATUS_OK], - [ 'foo@bar@local', false, false, true, Http::STATUS_UNPROCESSABLE_ENTITY], - [ 'foo@local', true, false, false, Http::STATUS_FORBIDDEN], - ]; - } - /** - * @dataProvider setEmailAddressData - * - * @param string $mailAddress - * @param bool $isValid - * @param bool $expectsUpdate - * @param bool $canChangeDisplayName - * @param int $responseCode - */ - public function testSetEMailAddress($mailAddress, $isValid, $expectsUpdate, $canChangeDisplayName, $responseCode) { - $user = $this->createMock(User::class); - $user - ->expects($this->any()) - ->method('getUID') - ->will($this->returnValue('foo')); - $user - ->expects($this->any()) - ->method('canChangeDisplayName') - ->will($this->returnValue($canChangeDisplayName)); - $user - ->expects($expectsUpdate ? $this->once() : $this->never()) - ->method('setEMailAddress') - ->with( - $this->equalTo($mailAddress) - ); - $user->method('getEMailAddress')->willReturn('oldEmailAddress'); - $this->mailer - ->expects($this->any()) - ->method('validateMailAddress') - ->with($mailAddress) - ->willReturn($isValid); - if ($isValid) { - $user->expects($this->atLeastOnce()) - ->method('canChangeDisplayName') - ->willReturn(true); - $this->userManager - ->expects($this->atLeastOnce()) - ->method('get') - ->with('foo') - ->will($this->returnValue($user)); - } - $controller = $this->getController(true); - $response = $controller->setEMailAddress($user->getUID(), $mailAddress); - $this->assertSame($responseCode, $response->getStatus()); - } - - public function testCreateUnsuccessfulWithoutPasswordAndEmail() { - $controller = $this->getController(true); - - $expectedResponse = new DataResponse( - array( - 'message' => 'To send a password link to the user an email address is required.' - ), - Http::STATUS_UNPROCESSABLE_ENTITY - ); - $response = $controller->create('foo', '', array(), ''); - $this->assertEquals($expectedResponse, $response); - } - - - - public function testCreateSuccessfulWithoutPasswordAndWithEmail() { - $user = $this->createMock(User::class); - $user - ->method('getHome') - ->willReturn('/home/user'); - $user - ->method('getUID') - ->willReturn('foo'); - $user - ->method('getDisplayName') - ->willReturn('John Doe'); - $user - ->method('getEmailAddress') - ->willReturn('abc@example.org'); - $user - ->expects($this->once()) - ->method('getBackendClassName') - ->willReturn('bar'); - $user - ->method('isEnabled') - ->willReturn(true); - - $this->userManager - ->expects($this->once()) - ->method('createUser') - ->will($this->onConsecutiveCalls($user)); - - $subadmin = $this->getMockBuilder(SubAdmin::class) - ->disableOriginalConstructor() - ->getMock(); - $subadmin - ->expects($this->any()) - ->method('getSubAdminsGroupsName') - ->with($user) - ->will($this->returnValue([])); - $this->groupManager - ->expects($this->any()) - ->method('getSubAdmin') - ->will($this->returnValue($subadmin)); - - $controller = $this->getController(true); - $this->mailer - ->expects($this->at(0)) - ->method('validateMailAddress') - ->with('abc@example.org') - ->will($this->returnValue(true)); - $emailTemplate = $this->createMock(IEMailTemplate::class); - $this->newUserMailHelper - ->expects($this->at(0)) - ->method('generateTemplate') - ->with($user, true) - ->willReturn($emailTemplate); - $this->newUserMailHelper - ->expects($this->at(1)) - ->method('sendMail') - ->with($user, $emailTemplate); - - $expectedResponse = new DataResponse( - [ - 'name' => 'foo', - 'groups' => null, - 'storageLocation' => '/home/user', - 'backend' => 'bar', - 'lastLogin' => 0, - 'displayname' => 'John Doe', - 'quota' => null, - 'quota_bytes' => false, - 'subadmin' => array(), - 'email' => 'abc@example.org', - 'isRestoreDisabled' => false, - 'isAvatarAvailable' => true, - 'isEnabled' => true, - ], - Http::STATUS_CREATED - ); - $response = $controller->create('foo', '', array(), 'abc@example.org'); - $this->assertEquals($expectedResponse, $response); - } - /** * @param string $account * @param string $type @@ -2598,425 +513,4 @@ class UsersControllerTest extends \Test\TestCase { $this->assertSame(Http::STATUS_BAD_REQUEST, $result->getStatus()); } - public function testDisableUserFailsDueSameUser() { - $user = $this->getMockBuilder(User::class) - ->disableOriginalConstructor()->getMock(); - $user->expects($this->once()) - ->method('getUID') - ->will($this->returnValue('abc')); - $this->userSession - ->expects($this->once()) - ->method('getUser') - ->will($this->returnValue($user)); - $expectedResponse = new DataResponse( - [ - 'status' => 'error', - 'data' => [ - 'message' => 'Error while disabling user.', - ], - ], - Http::STATUS_FORBIDDEN - ); - $response = $this->getController(true)->setEnabled('abc', false); - $this->assertEquals($expectedResponse, $response); - } - - public function testDisableUserFailsDueNoAdminAndNoSubadmin() { - $user = $this->getMockBuilder(User::class) - ->disableOriginalConstructor()->getMock(); - $user->expects($this->once()) - ->method('getUID') - ->will($this->returnValue('def')); - $this->userSession - ->expects($this->exactly(2)) - ->method('getUser') - ->will($this->returnValue($user)); - $user2 = $this->getMockBuilder(User::class) - ->disableOriginalConstructor()->getMock(); - $user2->expects($this->never()) - ->method('setEnabled'); - $this->userManager - ->expects($this->once()) - ->method('get') - ->with('abc') - ->willReturn($user2); - - $subadmin = $this->createMock(SubAdmin::class); - $subadmin->expects($this->once()) - ->method('isUserAccessible') - ->will($this->returnValue(false)); - $this->groupManager - ->expects($this->once()) - ->method('getSubAdmin') - ->willReturn($subadmin); - - $expectedResponse = new DataResponse( - [ - 'status' => 'error', - 'data' => [ - 'message' => 'Authentication error', - ], - ], - Http::STATUS_FORBIDDEN - ); - $response = $this->getController(false)->setEnabled('abc', false); - $this->assertEquals($expectedResponse, $response); - } - - public function testDisableUserFailsDueNoUser() { - $user = $this->getMockBuilder(User::class) - ->disableOriginalConstructor()->getMock(); - $user->expects($this->once()) - ->method('getUID') - ->will($this->returnValue('def')); - $this->userSession - ->expects($this->exactly(1)) - ->method('getUser') - ->will($this->returnValue($user)); - $this->userManager - ->expects($this->once()) - ->method('get') - ->with('abc') - ->willReturn(null); - - $this->groupManager - ->expects($this->never()) - ->method('getSubAdmin'); - - $expectedResponse = new DataResponse( - [ - 'status' => 'error', - 'data' => [ - 'message' => 'Error while disabling user.', - ], - ], - Http::STATUS_FORBIDDEN - ); - $response = $this->getController(true)->setEnabled('abc', false); - $this->assertEquals($expectedResponse, $response); - } - - public function testDisableUserFailsDueNoUserForSubAdmin() { - $user = $this->getMockBuilder(User::class) - ->disableOriginalConstructor()->getMock(); - $user->expects($this->once()) - ->method('getUID') - ->will($this->returnValue('def')); - $this->userSession - ->expects($this->exactly(1)) - ->method('getUser') - ->will($this->returnValue($user)); - $this->userManager - ->expects($this->once()) - ->method('get') - ->with('abc') - ->willReturn(null); - - $this->groupManager - ->expects($this->never()) - ->method('getSubAdmin'); - - $expectedResponse = new DataResponse( - [ - 'status' => 'error', - 'data' => [ - 'message' => 'Error while disabling user.', - ], - ], - Http::STATUS_FORBIDDEN - ); - $response = $this->getController(false)->setEnabled('abc', false); - $this->assertEquals($expectedResponse, $response); - } - - public function testDisableUserSuccessForAdmin() { - $user = $this->getMockBuilder(User::class) - ->disableOriginalConstructor()->getMock(); - $user->expects($this->once()) - ->method('getUID') - ->will($this->returnValue('def')); - $this->userSession - ->expects($this->exactly(1)) - ->method('getUser') - ->will($this->returnValue($user)); - $user2 = $this->getMockBuilder(User::class) - ->disableOriginalConstructor()->getMock(); - $user2->expects($this->once()) - ->method('setEnabled') - ->with(false); - $this->userManager - ->expects($this->once()) - ->method('get') - ->with('abc') - ->willReturn($user2); - - $this->groupManager - ->expects($this->never()) - ->method('getSubAdmin'); - - $expectedResponse = new DataResponse( - [ - 'status' => 'success', - 'data' => [ - 'username' => 'abc', - 'enabled' => 0, - ], - ] - ); - $response = $this->getController(true)->setEnabled('abc', false); - $this->assertEquals($expectedResponse, $response); - } - - public function testDisableUserSuccessForSubAdmin() { - $user = $this->getMockBuilder(User::class) - ->disableOriginalConstructor()->getMock(); - $user->expects($this->once()) - ->method('getUID') - ->will($this->returnValue('def')); - $this->userSession - ->expects($this->exactly(2)) - ->method('getUser') - ->will($this->returnValue($user)); - $user2 = $this->getMockBuilder(User::class) - ->disableOriginalConstructor()->getMock(); - $user2->expects($this->once()) - ->method('setEnabled'); - $this->userManager - ->expects($this->once()) - ->method('get') - ->with('abc') - ->willReturn($user2); - - $subadmin = $this->createMock(SubAdmin::class); - $subadmin->expects($this->once()) - ->method('isUserAccessible') - ->will($this->returnValue(true)); - $this->groupManager - ->expects($this->once()) - ->method('getSubAdmin') - ->willReturn($subadmin); - - $expectedResponse = new DataResponse( - [ - 'status' => 'success', - 'data' => [ - 'username' => 'abc', - 'enabled' => 0, - ], - ] - ); - $response = $this->getController(false)->setEnabled('abc', false); - $this->assertEquals($expectedResponse, $response); - } - - public function testEnableUserFailsDueSameUser() { - $user = $this->getMockBuilder(User::class) - ->disableOriginalConstructor()->getMock(); - $user->expects($this->once()) - ->method('getUID') - ->will($this->returnValue('abc')); - $this->userSession - ->expects($this->once()) - ->method('getUser') - ->will($this->returnValue($user)); - $expectedResponse = new DataResponse( - [ - 'status' => 'error', - 'data' => [ - 'message' => 'Error while enabling user.', - ], - ], - Http::STATUS_FORBIDDEN - ); - $response = $this->getController(true)->setEnabled('abc', true); - $this->assertEquals($expectedResponse, $response); - } - - public function testEnableUserFailsDueNoAdminAndNoSubadmin() { - $user = $this->getMockBuilder(User::class) - ->disableOriginalConstructor()->getMock(); - $user->expects($this->once()) - ->method('getUID') - ->will($this->returnValue('def')); - $this->userSession - ->expects($this->exactly(2)) - ->method('getUser') - ->will($this->returnValue($user)); - $user2 = $this->getMockBuilder(User::class) - ->disableOriginalConstructor()->getMock(); - $user2->expects($this->never()) - ->method('setEnabled'); - $this->userManager - ->expects($this->once()) - ->method('get') - ->with('abc') - ->willReturn($user2); - - $subadmin = $this->createMock(SubAdmin::class); - $subadmin->expects($this->once()) - ->method('isUserAccessible') - ->will($this->returnValue(false)); - $this->groupManager - ->expects($this->once()) - ->method('getSubAdmin') - ->willReturn($subadmin); - - $expectedResponse = new DataResponse( - [ - 'status' => 'error', - 'data' => [ - 'message' => 'Authentication error', - ], - ], - Http::STATUS_FORBIDDEN - ); - $response = $this->getController(false)->setEnabled('abc', true); - $this->assertEquals($expectedResponse, $response); - } - - public function testEnableUserFailsDueNoUser() { - $user = $this->getMockBuilder(User::class) - ->disableOriginalConstructor()->getMock(); - $user->expects($this->once()) - ->method('getUID') - ->will($this->returnValue('def')); - $this->userSession - ->expects($this->exactly(1)) - ->method('getUser') - ->will($this->returnValue($user)); - $this->userManager - ->expects($this->once()) - ->method('get') - ->with('abc') - ->willReturn(null); - - $this->groupManager - ->expects($this->never()) - ->method('getSubAdmin'); - - $expectedResponse = new DataResponse( - [ - 'status' => 'error', - 'data' => [ - 'message' => 'Error while enabling user.', - ], - ], - Http::STATUS_FORBIDDEN - ); - $response = $this->getController(true)->setEnabled('abc', true); - $this->assertEquals($expectedResponse, $response); - } - - public function testEnableUserFailsDueNoUserForSubAdmin() { - $user = $this->getMockBuilder(User::class) - ->disableOriginalConstructor()->getMock(); - $user->expects($this->once()) - ->method('getUID') - ->will($this->returnValue('def')); - $this->userSession - ->expects($this->exactly(1)) - ->method('getUser') - ->will($this->returnValue($user)); - $this->userManager - ->expects($this->once()) - ->method('get') - ->with('abc') - ->willReturn(null); - - $this->groupManager - ->expects($this->never()) - ->method('getSubAdmin'); - - $expectedResponse = new DataResponse( - [ - 'status' => 'error', - 'data' => [ - 'message' => 'Error while enabling user.', - ], - ], - Http::STATUS_FORBIDDEN - ); - $response = $this->getController(false)->setEnabled('abc', true); - $this->assertEquals($expectedResponse, $response); - } - - public function testEnableUserSuccessForAdmin() { - $user = $this->getMockBuilder(User::class) - ->disableOriginalConstructor()->getMock(); - $user->expects($this->once()) - ->method('getUID') - ->will($this->returnValue('def')); - $this->userSession - ->expects($this->exactly(1)) - ->method('getUser') - ->will($this->returnValue($user)); - $user2 = $this->getMockBuilder(User::class) - ->disableOriginalConstructor()->getMock(); - $user2->expects($this->once()) - ->method('setEnabled'); - $this->userManager - ->expects($this->once()) - ->method('get') - ->with('abc') - ->willReturn($user2); - - $this->groupManager - ->expects($this->never()) - ->method('getSubAdmin'); - - $expectedResponse = new DataResponse( - [ - 'status' => 'success', - 'data' => [ - 'username' => 'abc', - 'enabled' => 1, - ], - ] - ); - $response = $this->getController(true)->setEnabled('abc', true); - $this->assertEquals($expectedResponse, $response); - } - - public function testEnableUserSuccessForSubAdmin() { - $user = $this->getMockBuilder(User::class) - ->disableOriginalConstructor()->getMock(); - $user->expects($this->once()) - ->method('getUID') - ->will($this->returnValue('def')); - $this->userSession - ->expects($this->exactly(2)) - ->method('getUser') - ->will($this->returnValue($user)); - $user2 = $this->getMockBuilder(User::class) - ->disableOriginalConstructor()->getMock(); - $user2->expects($this->once()) - ->method('setEnabled') - ->with(true); - $this->userManager - ->expects($this->once()) - ->method('get') - ->with('abc') - ->willReturn($user2); - - $subadmin = $this->createMock(SubAdmin::class); - $subadmin->expects($this->once()) - ->method('isUserAccessible') - ->will($this->returnValue(true)); - $this->groupManager - ->expects($this->once()) - ->method('getSubAdmin') - ->willReturn($subadmin); - - $expectedResponse = new DataResponse( - [ - 'status' => 'success', - 'data' => [ - 'username' => 'abc', - 'enabled' => 1, - ], - ] - ); - $response = $this->getController(false)->setEnabled('abc', true); - $this->assertEquals($expectedResponse, $response); - } } diff --git a/tests/acceptance/config/behat.yml b/tests/acceptance/config/behat.yml index 7eee0a4da9..5f1b27ffe2 100644 --- a/tests/acceptance/config/behat.yml +++ b/tests/acceptance/config/behat.yml @@ -8,8 +8,10 @@ default: - NextcloudTestServerContext - AppNavigationContext + - AppSettingsContext - CommentsAppContext - ContactsMenuContext + - DialogContext - FeatureContext - FileListContext - FilesAppContext diff --git a/tests/acceptance/features/bootstrap/AppNavigationContext.php b/tests/acceptance/features/bootstrap/AppNavigationContext.php index 9ef71a0606..e6e6bcc847 100644 --- a/tests/acceptance/features/bootstrap/AppNavigationContext.php +++ b/tests/acceptance/features/bootstrap/AppNavigationContext.php @@ -1,9 +1,10 @@ + * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify @@ -32,24 +33,43 @@ class AppNavigationContext implements Context, ActorAwareInterface { */ public static function appNavigation() { return Locator::forThe()->id("app-navigation")-> - describedAs("App navigation"); + describedAs("App navigation"); } /** * @return Locator */ public static function appNavigationSectionItemFor($sectionText) { - return Locator::forThe()->xpath("//li[normalize-space() = '$sectionText']")-> - descendantOf(self::appNavigation())-> - describedAs($sectionText . " section item in App Navigation"); + return Locator::forThe()->xpath("//li/a[normalize-space() = '$sectionText']/..")-> + descendantOf(self::appNavigation())-> + describedAs($sectionText . " section item in App Navigation"); } /** * @return Locator */ public static function appNavigationCurrentSectionItem() { - return Locator::forThe()->css(".active")->descendantOf(self::appNavigation())-> - describedAs("Current section item in App Navigation"); + return Locator::forThe()->css(".active")-> + descendantOf(self::appNavigation())-> + describedAs("Current section item in App Navigation"); + } + + /** + * @return Locator + */ + public static function buttonForTheSection($class, $section) { + return Locator::forThe()->css("." . $class)-> + descendantOf(self::appNavigationSectionItemFor($section))-> + describedAs("The $class button on the $section section in App Navigation"); + } + + /** + * @return Locator + */ + public static function counterForTheSection($section) { + return Locator::forThe()->css(".app-navigation-entry-utils-counter")-> + descendantOf(self::appNavigationSectionItemFor($section))-> + describedAs("The counter for the $section section in App Navigation"); } /** @@ -59,6 +79,13 @@ class AppNavigationContext implements Context, ActorAwareInterface { $this->actor->find(self::appNavigationSectionItemFor($section), 10)->click(); } + /** + * @Given I click the :class button on the :section section + */ + public function iClickTheButtonInTheSection($class, $section) { + $this->actor->find(self::buttonForTheSection($class, $section), 10)->click(); + } + /** * @Then I see that the current section is :section */ @@ -66,4 +93,25 @@ class AppNavigationContext implements Context, ActorAwareInterface { PHPUnit_Framework_Assert::assertEquals($this->actor->find(self::appNavigationCurrentSectionItem(), 10)->getText(), $section); } + /** + * @Then I see that the section :section is shown + */ + public function iSeeThatTheSectionIsShown($section) { + WaitFor::elementToBeEventuallyShown($this->actor, self::appNavigationSectionItemFor($section)); + } + + /** + * @Then I see that the section :section is not shown + */ + public function iSeeThatTheSectionIsNotShown($section) { + WaitFor::elementToBeEventuallyNotShown($this->actor, self::appNavigationSectionItemFor($section)); + } + + /** + * @Then I see that the section :section has a count of :count + */ + public function iSeeThatTheSectionHasACountOf($section, $count) { + PHPUnit_Framework_Assert::assertEquals($this->actor->find(self::counterForTheSection($section), 10)->getText(), $count); + } + } diff --git a/tests/acceptance/features/bootstrap/AppSettingsContext.php b/tests/acceptance/features/bootstrap/AppSettingsContext.php new file mode 100644 index 0000000000..5cf54cb528 --- /dev/null +++ b/tests/acceptance/features/bootstrap/AppSettingsContext.php @@ -0,0 +1,102 @@ + + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +use Behat\Behat\Context\Context; + +class AppSettingsContext implements Context, ActorAwareInterface { + + use ActorAware; + + /** + * @return Locator + */ + public static function appSettings() { + return Locator::forThe()->id("app-settings")-> + describedAs("App settings"); + } + /** + * @return Locator + */ + public static function appSettingsContent() { + return Locator::forThe()->id("app-settings-content")-> + descendantOf(self::appSettings())-> + describedAs("App settings"); + } + + /** + * @return Locator + */ + public static function appSettingsOpenButton() { + return Locator::forThe()->xpath("//div[@id = 'app-settings-header']/button")-> + descendantOf(self::appSettings())-> + describedAs("The button to open the app settings"); + } + + /** + * @return Locator + */ + public static function checkboxInTheSettings($id) { + return Locator::forThe()->xpath("//input[@id = '$id']")-> + descendantOf(self::appSettingsContent())-> + describedAs("The $id checkbox in the settings"); + } + + /** + * @return Locator + */ + public static function checkboxLabelInTheSettings($id) { + return Locator::forThe()->xpath("//label[@for = '$id']")-> + descendantOf(self::appSettingsContent())-> + describedAs("The label for the $id checkbox in the settings"); + } + + /** + * @Given I open the settings + */ + public function iOpenTheSettings() { + $this->actor->find(self::appSettingsOpenButton())->click(); + } + + /** + * @Given I toggle the :id checkbox in the settings + */ + public function iToggleTheCheckboxInTheSettingsTo($id) { + $locator = self::CheckboxInTheSettings($id); + + // If locator is not visible, fallback to label + if (!$this->actor->find(self::CheckboxInTheSettings($id))->isVisible()) { + $locator = self::checkboxLabelInTheSettings($id); + } + + $this->actor->find($locator)->click(); + } + + /** + * @Then I see that the settings are opened + */ + public function iSeeThatTheSettingsAreOpened() { + WaitFor::elementToBeEventuallyShown($this->actor, self::appSettingsContent()); + } + +} diff --git a/tests/acceptance/features/bootstrap/DialogContext.php b/tests/acceptance/features/bootstrap/DialogContext.php new file mode 100644 index 0000000000..8f387992b5 --- /dev/null +++ b/tests/acceptance/features/bootstrap/DialogContext.php @@ -0,0 +1,68 @@ + + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +use Behat\Behat\Context\Context; + +class DialogContext implements Context, ActorAwareInterface { + + use ActorAware; + + /** + * @return Locator + */ + public static function theDialog() { + return Locator::forThe()->css(".oc-dialog")-> + describedAs("The dialog"); + } + + /** + * @return Locator + */ + public static function theDialogButton($text) { + return Locator::forThe()->xpath("//button[normalize-space() = '$text']")-> + descendantOf(self::theDialog())-> + describedAs($text . " button of the dialog"); + } + + /** + * @Given I click the :text button of the confirmation dialog + */ + public function iClickTheDialogButton($text) { + $this->actor->find(self::theDialogButton($text), 10)->click(); + } + + /** + * @Then I see that the confirmation dialog is shown + */ + public function iSeeThatTheConfirmationDialogIsShown() { + WaitFor::elementToBeEventuallyShown($this->actor, self::theDialog()); + } + + /** + * @Then I see that the confirmation dialog is not shown + */ + public function iSeeThatTheConfirmationDialogIsNotShown() { + WaitFor::elementToBeEventuallyNotShown($this->actor, self::theDialog()); + } + +} diff --git a/tests/acceptance/features/bootstrap/UsersSettingsContext.php b/tests/acceptance/features/bootstrap/UsersSettingsContext.php index 9ddea57f41..995bbdd872 100644 --- a/tests/acceptance/features/bootstrap/UsersSettingsContext.php +++ b/tests/acceptance/features/bootstrap/UsersSettingsContext.php @@ -1,8 +1,9 @@ * * @license GNU AGPL version 3 or any later version * @@ -31,7 +32,7 @@ class UsersSettingsContext implements Context, ActorAwareInterface { * @return Locator */ public static function newUserForm() { - return Locator::forThe()->id("newuserHeader")-> + return Locator::forThe()->id("new-user")-> describedAs("New user form in Users Settings"); } @@ -63,7 +64,7 @@ class UsersSettingsContext implements Context, ActorAwareInterface { * @return Locator */ public static function createNewUserButton() { - return Locator::forThe()->xpath("//form[@id = 'newuser']//input[@type = 'submit']")-> + return Locator::forThe()->xpath("//form[@id = 'new-user']//input[@type = 'submit']")-> describedAs("Create user button in Users Settings"); } @@ -71,24 +72,72 @@ class UsersSettingsContext implements Context, ActorAwareInterface { * @return Locator */ public static function rowForUser($user) { - return Locator::forThe()->xpath("//table[@id = 'userlist']//td[normalize-space() = '$user']/..")-> + return Locator::forThe()->xpath("//div[@id='app-content']/div/div[normalize-space() = '$user']/..")-> describedAs("Row for user $user in Users Settings"); } /** + * Warning: you need to watch out for the proper classes order + * * @return Locator */ - public static function passwordCellForUser($user) { - return Locator::forThe()->css(".password")->descendantOf(self::rowForUser($user))-> - describedAs("Password cell for user $user in Users Settings"); + public static function classCellForUser($class, $user) { + return Locator::forThe()->xpath("//*[@class='$class']")-> + descendantOf(self::rowForUser($user))-> + describedAs("$class cell for user $user in Users Settings"); } /** * @return Locator */ - public static function passwordInputForUser($user) { - return Locator::forThe()->css("input")->descendantOf(self::passwordCellForUser($user))-> - describedAs("Password input for user $user in Users Settings"); + public static function inputForUserInCell($cell, $user) { + return Locator::forThe()->css("input")-> + descendantOf(self::classCellForUser($cell, $user))-> + describedAs("$cell input for user $user in Users Settings"); + } + + /** + * @return Locator + */ + public static function optionInInputForUser($cell, $user) { + return Locator::forThe()->css(".multiselect__option--highlight")-> + descendantOf(self::classCellForUser($cell, $user))-> + describedAs("Selected $cell option in $cell input for user $user in Users Settings"); + } + + /** + * @return Locator + */ + public static function actionsMenuOf($user) { + return Locator::forThe()->css(".icon-more")-> + descendantOf(self::rowForUser($user))-> + describedAs("Actions menu for user $user in Users Settings"); + } + + /** + * @return Locator + */ + public static function theAction($action, $user) { + return Locator::forThe()->xpath("//button[normalize-space() = '$action']")-> + descendantOf(self::rowForUser($user))-> + describedAs("$action action for the user $user row in Users Settings"); + } + + /** + * @return Locator + */ + public static function theColumn($column) { + return Locator::forThe()->xpath("//div[@class='user-list-grid']//div[normalize-space() = '$column']")-> + describedAs("The $column column in Users Settings"); + } + + /** + * @return Locator + */ + public static function selectedSelectOption($cell, $user) { + return Locator::forThe()->css(".multiselect__single")-> + descendantOf(self::classCellForUser($cell, $user))-> + describedAs("The selected option of the $cell select for the user $user in Users Settings"); } /** @@ -98,6 +147,20 @@ class UsersSettingsContext implements Context, ActorAwareInterface { $this->actor->find(self::newUserButton())->click(); } + /** + * @When I click the :action action in the :user actions menu + */ + public function iClickTheAction($action, $user) { + $this->actor->find(self::theAction($action, $user))->click(); + } + + /** + * @When I open the actions menu for the user :user + */ + public function iOpenTheActionsMenuOf($user) { + $this->actor->find(self::actionsMenuOf($user))->click(); + } + /** * @When I create user :user with password :password */ @@ -108,18 +171,40 @@ class UsersSettingsContext implements Context, ActorAwareInterface { } /** - * @When I set the password for :user to :password + * @When I set the :field for :user to :value */ - public function iSetThePasswordForUserTo($user, $password) { - $this->actor->find(self::passwordCellForUser($user), 10)->click(); - $this->actor->find(self::passwordInputForUser($user), 2)->setValue($password . "\r"); + public function iSetTheFieldForUserTo($field, $user, $value) { + $this->actor->find(self::inputForUserInCell($field, $user), 2)->setValue($value . "\r"); + } + + /** + * @When I assign the user :user to the group :group + */ + public function iAssignTheUserToTheGroup($user, $group) { + $this->actor->find(self::inputForUserInCell('groups', $user))->setValue($group); + $this->actor->find(self::optionInInputForUser('groups', $user))->click(); + } + + /** + * @When I set the user :user quota to :quota + */ + public function iSetTheUserQuotaTo($user, $quota) { + $this->actor->find(self::inputForUserInCell('quota', $user))->setValue($quota); + $this->actor->find(self::optionInInputForUser('quota', $user))->click(); } /** * @Then I see that the list of users contains the user :user */ public function iSeeThatTheListOfUsersContainsTheUser($user) { - PHPUnit_Framework_Assert::assertNotNull($this->actor->find(self::rowForUser($user), 10)); + WaitFor::elementToBeEventuallyShown($this->actor, self::rowForUser($user)); + } + + /** + * @Then I see that the list of users does not contains the user :user + */ + public function iSeeThatTheListOfUsersDoesNotContainsTheUser($user) { + WaitFor::elementToBeEventuallyNotShown($this->actor, self::rowForUser($user)); } /** @@ -130,4 +215,45 @@ class UsersSettingsContext implements Context, ActorAwareInterface { $this->actor->find(self::newUserForm(), 10)->isVisible()); } + /** + * @Then I see that the :action action in the :user actions menu is shown + */ + public function iSeeTheAction($action, $user) { + PHPUnit_Framework_Assert::assertTrue( + $this->actor->find(self::theAction($action, $user), 10)->isVisible()); + } + + /** + * @Then I see that the :column column is shown + */ + public function iSeeThatTheColumnIsShown($column) { + PHPUnit_Framework_Assert::assertTrue( + $this->actor->find(self::theColumn($column), 10)->isVisible()); + } + + /** + * @Then I see that the :field of :user is :value + */ + public function iSeeThatTheFieldOfUserIs($field, $user, $value) { + PHPUnit_Framework_Assert::assertEquals( + $this->actor->find(self::inputForUserInCell($field, $user), 10)->getValue(), $value); + } + + /** + * @Then I see that the :cell cell for user :user is done loading + */ + public function iSeeThatTheCellForUserIsDoneLoading($cell, $user) { + WaitFor::elementToBeEventuallyShown($this->actor, self::classCellForUser($cell.' icon-loading-small', $user)); + WaitFor::elementToBeEventuallyNotShown($this->actor, self::classCellForUser($cell.' icon-loading-small', $user)); + } + + /** + * @Then I see that the user quota of :user is :quota + */ + public function iSeeThatTheuserQuotaIs($user, $quota) { + PHPUnit_Framework_Assert::assertEquals( + $this->actor->find(self::selectedSelectOption('quota', $user), 2)->getText(), $quota); + } + + } diff --git a/tests/acceptance/features/core/ElementWrapper.php b/tests/acceptance/features/core/ElementWrapper.php index 7d7a86149e..c866ea699f 100644 --- a/tests/acceptance/features/core/ElementWrapper.php +++ b/tests/acceptance/features/core/ElementWrapper.php @@ -147,6 +147,18 @@ class ElementWrapper { return $this->executeCommand($commandCallback, "visibility could not be got"); } + /** + * Returns whether the wrapped element is checked or not. + * + * @return bool true if the wrapped element is checked, false otherwise. + */ + public function isChecked() { + $commandCallback = function() { + return $this->element->isChecked(); + }; + return $this->executeCommand($commandCallback, "check state could not be got"); + } + /** * Returns the text of the wrapped element. * @@ -205,6 +217,32 @@ class ElementWrapper { $this->executeCommandOnVisibleElement($commandCallback, "could not be clicked"); } + /** + * Check the wrapped element. + * + * If automatically waits for the wrapped element to be visible (up to the + * timeout set when finding it). + */ + public function check() { + $commandCallback = function() { + $this->element->check(); + }; + $this->executeCommand($commandCallback, "could not be checked"); + } + + /** + * uncheck the wrapped element. + * + * If automatically waits for the wrapped element to be visible (up to the + * timeout set when finding it). + */ + public function uncheck() { + $commandCallback = function() { + $this->element->uncheck(); + }; + $this->executeCommand($commandCallback, "could not be unchecked"); + } + /** * Executes the given command. * diff --git a/tests/acceptance/features/login.feature b/tests/acceptance/features/login.feature index 3db8c4945e..3a31d3f88b 100644 --- a/tests/acceptance/features/login.feature +++ b/tests/acceptance/features/login.feature @@ -18,7 +18,6 @@ Feature: login And I am logged in as the admin And I open the User settings And I set the password for user0 to 654321 - And I see that the "Password successfully changed" notification is shown And I act as John And I log in with user user0 and password 654321 Then I see that the current page is the Files app diff --git a/tests/acceptance/features/users.feature b/tests/acceptance/features/users.feature new file mode 100644 index 0000000000..9bce32a2da --- /dev/null +++ b/tests/acceptance/features/users.feature @@ -0,0 +1,115 @@ +Feature: users + + Scenario: create a new user + Given I act as Jane + And I am logged in as the admin + And I open the User settings + And I click the New user button + And I see that the new user form is shown + When I create user unknownUser with password 123456acb + Then I see that the list of users contains the user unknownUser + + Scenario: delete a user + Given I act as Jane + And I am logged in as the admin + And I open the User settings + And I see that the list of users contains the user user0 + And I open the actions menu for the user user0 + And I see that the "Delete user" action in the user0 actions menu is shown + When I click the "Delete user" action in the user0 actions menu + Then I see that the list of users does not contains the user user0 + + Scenario: disable a user + Given I act as Jane + And I am logged in as the admin + And I open the User settings + And I see that the list of users contains the user user0 + And I open the actions menu for the user user0 + And I see that the "Disable user" action in the user0 actions menu is shown + When I click the "Disable user" action in the user0 actions menu + Then I see that the list of users does not contains the user user0 + When I open the "Disabled users" section + Then I see that the list of users contains the user user0 + + Scenario: assign user to a group + Given I act as Jane + And I am logged in as the admin + And I open the User settings + And I see that the list of users contains the user user0 + # disabled because we need the TAB patch: + # https://github.com/minkphp/MinkSelenium2Driver/pull/244 + # When I assign the user user0 to the group admin + # Then I see that the section Admins is shown + # And I see that the section Admins has a count of 2 + + Scenario: create and delete a group + Given I act as Jane + And I am logged in as the admin + And I open the User settings + And I see that the list of users contains the user user0 + # disabled because we need the TAB patch: + # https://github.com/minkphp/MinkSelenium2Driver/pull/244 + # And I assign the user user0 to the group Group1 + # And I see that the section Group1 is shown + # And I click the "icon-delete" button on the Group1 section + # And I see that the confirmation dialog is shown + # When I click the "Yes" button of the confirmation dialog + # Then I see that the section Group1 is not shown + + Scenario: change columns visibility + Given I act as Jane + And I am logged in as the admin + And I open the User settings + And I open the settings + And I see that the settings are opened + When I toggle the showLanguages checkbox in the settings + Then I see that the "Languages" column is shown + When I toggle the showLastLogin checkbox in the settings + Then I see that the "Last login" column is shown + When I toggle the showStoragePath checkbox in the settings + Then I see that the "Storage location" column is shown + When I toggle the showUserBackend checkbox in the settings + Then I see that the "User backend" column is shown + + Scenario: change display name + Given I act as Jane + And I am logged in as the admin + And I open the User settings + And I see that the list of users contains the user user0 + And I see that the displayName of user0 is user0 + When I set the displayName for user0 to user1 + And I see that the displayName cell for user user0 is done loading + Then I see that the displayName of user0 is user1 + + Scenario: change password + Given I act as Jane + And I am logged in as the admin + And I open the User settings + And I see that the list of users contains the user user0 + And I see that the password of user0 is "" + When I set the password for user0 to 123456 + And I see that the password cell for user user0 is done loading + # password input is emptied on change + Then I see that the password of user0 is "" + + Scenario: change email + Given I act as Jane + And I am logged in as the admin + And I open the User settings + And I see that the list of users contains the user user0 + And I see that the mailAddress of user0 is "" + When I set the mailAddress for user0 to "test@nextcloud.com" + And I see that the mailAddress cell for user user0 is done loading + Then I see that the mailAddress of user0 is "test@nextcloud.com" + + Scenario: change user quota + Given I act as Jane + And I am logged in as the admin + And I open the User settings + And I see that the list of users contains the user user0 + And I see that the user quota of user0 is Unlimited + # disabled because we need the TAB patch: + # https://github.com/minkphp/MinkSelenium2Driver/pull/244 + # When I set the user user0 quota to 1GB + # And I see that the quota cell for user user0 is done loading + # Then I see that the user quota of user0 is "1 GB" \ No newline at end of file