From 98fc276740c56364d60792522cee978f9dc21122 Mon Sep 17 00:00:00 2001 From: skysilver Date: Sun, 9 Jun 2019 15:11:33 +0300 Subject: [PATCH] =?UTF-8?q?v.0.1-beta=20[=D0=92=D0=B5=D1=80=D1=81=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=B4=D0=BB=D1=8F=20=D1=82=D0=B5=D1=81=D1=82=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Базовые функции модуля. --- .gitignore | 1 + README.md | 5 +- img/modules/yandexhome.png | Bin 0 -> 5131 bytes modules/yandexhome/.htaccess | 1 + modules/yandexhome/authorize.php | 138 + modules/yandexhome/css/style.css | 197 ++ modules/yandexhome/favicon.ico | Bin 0 -> 40451 bytes modules/yandexhome/img/mlogo.png | Bin 0 -> 4511 bytes modules/yandexhome/img/ylogo.png | Bin 0 -> 100947 bytes modules/yandexhome/lib/.htaccess | 1 + modules/yandexhome/lib/OAuth2/.htaccess | 1 + modules/yandexhome/lib/OAuth2/Autoloader.php | 54 + .../ClientAssertionTypeInterface.php | 28 + .../OAuth2/ClientAssertionType/HttpBasic.php | 139 + .../OAuth2/Controller/AuthorizeController.php | 480 +++ .../AuthorizeControllerInterface.php | 58 + .../OAuth2/Controller/ResourceController.php | 156 + .../ResourceControllerInterface.php | 41 + .../lib/OAuth2/Controller/TokenController.php | 333 ++ .../Controller/TokenControllerInterface.php | 39 + .../OAuth2/Encryption/EncryptionInterface.php | 34 + .../lib/OAuth2/Encryption/FirebaseJwt.php | 47 + .../yandexhome/lib/OAuth2/Encryption/Jwt.php | 223 ++ .../OAuth2/GrantType/AuthorizationCode.php | 142 + .../OAuth2/GrantType/ClientCredentials.php | 98 + .../OAuth2/GrantType/GrantTypeInterface.php | 59 + .../lib/OAuth2/GrantType/JwtBearer.php | 247 ++ .../lib/OAuth2/GrantType/RefreshToken.php | 154 + .../lib/OAuth2/GrantType/UserCredentials.php | 123 + .../OpenID/Controller/AuthorizeController.php | 135 + .../AuthorizeControllerInterface.php | 12 + .../OpenID/Controller/UserInfoController.php | 62 + .../UserInfoControllerInterface.php | 30 + .../OpenID/GrantType/AuthorizationCode.php | 41 + .../OpenID/ResponseType/AuthorizationCode.php | 66 + .../AuthorizationCodeInterface.php | 27 + .../OpenID/ResponseType/CodeIdToken.php | 40 + .../ResponseType/CodeIdTokenInterface.php | 9 + .../OAuth2/OpenID/ResponseType/IdToken.php | 178 + .../OpenID/ResponseType/IdTokenInterface.php | 30 + .../OpenID/ResponseType/IdTokenToken.php | 45 + .../ResponseType/IdTokenTokenInterface.php | 9 + .../Storage/AuthorizationCodeInterface.php | 37 + .../OpenID/Storage/UserClaimsInterface.php | 35 + modules/yandexhome/lib/OAuth2/Request.php | 252 ++ .../lib/OAuth2/RequestInterface.php | 39 + modules/yandexhome/lib/OAuth2/Response.php | 487 +++ .../lib/OAuth2/ResponseInterface.php | 53 + .../lib/OAuth2/ResponseType/AccessToken.php | 218 ++ .../ResponseType/AccessTokenInterface.php | 33 + .../OAuth2/ResponseType/AuthorizationCode.php | 101 + .../AuthorizationCodeInterface.php | 30 + .../OAuth2/ResponseType/JwtAccessToken.php | 159 + .../ResponseType/ResponseTypeInterface.php | 13 + modules/yandexhome/lib/OAuth2/Scope.php | 109 + .../yandexhome/lib/OAuth2/ScopeInterface.php | 35 + modules/yandexhome/lib/OAuth2/Server.php | 1019 ++++++ .../OAuth2/Storage/AccessTokenInterface.php | 65 + .../Storage/AuthorizationCodeInterface.php | 86 + .../lib/OAuth2/Storage/Cassandra.php | 660 ++++ .../Storage/ClientCredentialsInterface.php | 49 + .../lib/OAuth2/Storage/ClientInterface.php | 66 + .../lib/OAuth2/Storage/CouchbaseDB.php | 331 ++ .../lib/OAuth2/Storage/DynamoDB.php | 540 +++ .../lib/OAuth2/Storage/JwtAccessToken.php | 87 + .../Storage/JwtAccessTokenInterface.php | 14 + .../lib/OAuth2/Storage/JwtBearerInterface.php | 74 + .../lib/OAuth2/Storage/MajordomoPdo.php | 338 ++ .../yandexhome/lib/OAuth2/Storage/Memory.php | 381 ++ .../yandexhome/lib/OAuth2/Storage/Mongo.php | 392 +++ .../yandexhome/lib/OAuth2/Storage/MongoDB.php | 380 ++ .../lib/OAuth2/Storage/PublicKeyInterface.php | 30 + .../yandexhome/lib/OAuth2/Storage/Redis.php | 321 ++ .../OAuth2/Storage/RefreshTokenInterface.php | 82 + .../lib/OAuth2/Storage/ScopeInterface.php | 46 + .../Storage/UserCredentialsInterface.php | 52 + .../lib/OAuth2/TokenType/Bearer.php | 130 + .../yandexhome/lib/OAuth2/TokenType/Mac.php | 22 + .../OAuth2/TokenType/TokenTypeInterface.php | 21 + modules/yandexhome/server.php | 40 + modules/yandexhome/smarthome.php | 128 + modules/yandexhome/structure.inc.php | 157 + modules/yandexhome/token.php | 14 + modules/yandexhome/yandexhome.class.php | 885 +++++ templates/yandexhome/about.html | 39 + templates/yandexhome/action_admin.html | 76 + templates/yandexhome/action_usual.html | 1 + templates/yandexhome/css/bootstrap-select.css | 429 +++ .../yandexhome/css/bootstrap-select.css.map | 1 + templates/yandexhome/css/bootstrap-select.js | 3061 +++++++++++++++++ .../yandexhome/css/bootstrap-select.js.map | 1 + .../yandexhome/css/bootstrap-select.min.css | 6 + .../yandexhome/css/bootstrap-select.min.js | 9 + .../css/bootstrap-select.min.js.map | 1 + templates/yandexhome/css/defaults-ru_RU.js | 40 + templates/yandexhome/css/usual.css | 175 + templates/yandexhome/devices_addnew.html | 64 + templates/yandexhome/devices_edit.html | 364 ++ templates/yandexhome/devices_list_panels.html | 86 + templates/yandexhome/devices_list_table.html | 60 + templates/yandexhome/img/cooking.kettle.svg | 6 + templates/yandexhome/img/cooking.svg | 7 + templates/yandexhome/img/dialog_logo.png | Bin 0 -> 3558 bytes templates/yandexhome/img/light.svg | 6 + templates/yandexhome/img/logo.png | Bin 0 -> 5131 bytes templates/yandexhome/img/media_device.svg | 9 + templates/yandexhome/img/media_device.tv.svg | 6 + templates/yandexhome/img/other.svg | 13 + templates/yandexhome/img/socket.svg | 8 + templates/yandexhome/img/switch.svg | 5 + templates/yandexhome/img/thermostat.ac.svg | 6 + templates/yandexhome/img/thermostat.svg | 6 + templates/yandexhome/settings.html | 141 + templates/yandexhome/yandexhome.html | 7 + 114 files changed, 16125 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 img/modules/yandexhome.png create mode 100644 modules/yandexhome/.htaccess create mode 100644 modules/yandexhome/authorize.php create mode 100644 modules/yandexhome/css/style.css create mode 100644 modules/yandexhome/favicon.ico create mode 100644 modules/yandexhome/img/mlogo.png create mode 100644 modules/yandexhome/img/ylogo.png create mode 100644 modules/yandexhome/lib/.htaccess create mode 100644 modules/yandexhome/lib/OAuth2/.htaccess create mode 100644 modules/yandexhome/lib/OAuth2/Autoloader.php create mode 100644 modules/yandexhome/lib/OAuth2/ClientAssertionType/ClientAssertionTypeInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/ClientAssertionType/HttpBasic.php create mode 100644 modules/yandexhome/lib/OAuth2/Controller/AuthorizeController.php create mode 100644 modules/yandexhome/lib/OAuth2/Controller/AuthorizeControllerInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/Controller/ResourceController.php create mode 100644 modules/yandexhome/lib/OAuth2/Controller/ResourceControllerInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/Controller/TokenController.php create mode 100644 modules/yandexhome/lib/OAuth2/Controller/TokenControllerInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/Encryption/EncryptionInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/Encryption/FirebaseJwt.php create mode 100644 modules/yandexhome/lib/OAuth2/Encryption/Jwt.php create mode 100644 modules/yandexhome/lib/OAuth2/GrantType/AuthorizationCode.php create mode 100644 modules/yandexhome/lib/OAuth2/GrantType/ClientCredentials.php create mode 100644 modules/yandexhome/lib/OAuth2/GrantType/GrantTypeInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/GrantType/JwtBearer.php create mode 100644 modules/yandexhome/lib/OAuth2/GrantType/RefreshToken.php create mode 100644 modules/yandexhome/lib/OAuth2/GrantType/UserCredentials.php create mode 100644 modules/yandexhome/lib/OAuth2/OpenID/Controller/AuthorizeController.php create mode 100644 modules/yandexhome/lib/OAuth2/OpenID/Controller/AuthorizeControllerInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/OpenID/Controller/UserInfoController.php create mode 100644 modules/yandexhome/lib/OAuth2/OpenID/Controller/UserInfoControllerInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/OpenID/GrantType/AuthorizationCode.php create mode 100644 modules/yandexhome/lib/OAuth2/OpenID/ResponseType/AuthorizationCode.php create mode 100644 modules/yandexhome/lib/OAuth2/OpenID/ResponseType/AuthorizationCodeInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/OpenID/ResponseType/CodeIdToken.php create mode 100644 modules/yandexhome/lib/OAuth2/OpenID/ResponseType/CodeIdTokenInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/OpenID/ResponseType/IdToken.php create mode 100644 modules/yandexhome/lib/OAuth2/OpenID/ResponseType/IdTokenInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/OpenID/ResponseType/IdTokenToken.php create mode 100644 modules/yandexhome/lib/OAuth2/OpenID/ResponseType/IdTokenTokenInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/OpenID/Storage/AuthorizationCodeInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/OpenID/Storage/UserClaimsInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/Request.php create mode 100644 modules/yandexhome/lib/OAuth2/RequestInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/Response.php create mode 100644 modules/yandexhome/lib/OAuth2/ResponseInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/ResponseType/AccessToken.php create mode 100644 modules/yandexhome/lib/OAuth2/ResponseType/AccessTokenInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/ResponseType/AuthorizationCode.php create mode 100644 modules/yandexhome/lib/OAuth2/ResponseType/AuthorizationCodeInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/ResponseType/JwtAccessToken.php create mode 100644 modules/yandexhome/lib/OAuth2/ResponseType/ResponseTypeInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/Scope.php create mode 100644 modules/yandexhome/lib/OAuth2/ScopeInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/Server.php create mode 100644 modules/yandexhome/lib/OAuth2/Storage/AccessTokenInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/Storage/AuthorizationCodeInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/Storage/Cassandra.php create mode 100644 modules/yandexhome/lib/OAuth2/Storage/ClientCredentialsInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/Storage/ClientInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/Storage/CouchbaseDB.php create mode 100644 modules/yandexhome/lib/OAuth2/Storage/DynamoDB.php create mode 100644 modules/yandexhome/lib/OAuth2/Storage/JwtAccessToken.php create mode 100644 modules/yandexhome/lib/OAuth2/Storage/JwtAccessTokenInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/Storage/JwtBearerInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/Storage/MajordomoPdo.php create mode 100644 modules/yandexhome/lib/OAuth2/Storage/Memory.php create mode 100644 modules/yandexhome/lib/OAuth2/Storage/Mongo.php create mode 100644 modules/yandexhome/lib/OAuth2/Storage/MongoDB.php create mode 100644 modules/yandexhome/lib/OAuth2/Storage/PublicKeyInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/Storage/Redis.php create mode 100644 modules/yandexhome/lib/OAuth2/Storage/RefreshTokenInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/Storage/ScopeInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/Storage/UserCredentialsInterface.php create mode 100644 modules/yandexhome/lib/OAuth2/TokenType/Bearer.php create mode 100644 modules/yandexhome/lib/OAuth2/TokenType/Mac.php create mode 100644 modules/yandexhome/lib/OAuth2/TokenType/TokenTypeInterface.php create mode 100644 modules/yandexhome/server.php create mode 100644 modules/yandexhome/smarthome.php create mode 100644 modules/yandexhome/structure.inc.php create mode 100644 modules/yandexhome/token.php create mode 100644 modules/yandexhome/yandexhome.class.php create mode 100644 templates/yandexhome/about.html create mode 100644 templates/yandexhome/action_admin.html create mode 100644 templates/yandexhome/action_usual.html create mode 100644 templates/yandexhome/css/bootstrap-select.css create mode 100644 templates/yandexhome/css/bootstrap-select.css.map create mode 100644 templates/yandexhome/css/bootstrap-select.js create mode 100644 templates/yandexhome/css/bootstrap-select.js.map create mode 100644 templates/yandexhome/css/bootstrap-select.min.css create mode 100644 templates/yandexhome/css/bootstrap-select.min.js create mode 100644 templates/yandexhome/css/bootstrap-select.min.js.map create mode 100644 templates/yandexhome/css/defaults-ru_RU.js create mode 100644 templates/yandexhome/css/usual.css create mode 100644 templates/yandexhome/devices_addnew.html create mode 100644 templates/yandexhome/devices_edit.html create mode 100644 templates/yandexhome/devices_list_panels.html create mode 100644 templates/yandexhome/devices_list_table.html create mode 100644 templates/yandexhome/img/cooking.kettle.svg create mode 100644 templates/yandexhome/img/cooking.svg create mode 100644 templates/yandexhome/img/dialog_logo.png create mode 100644 templates/yandexhome/img/light.svg create mode 100644 templates/yandexhome/img/logo.png create mode 100644 templates/yandexhome/img/media_device.svg create mode 100644 templates/yandexhome/img/media_device.tv.svg create mode 100644 templates/yandexhome/img/other.svg create mode 100644 templates/yandexhome/img/socket.svg create mode 100644 templates/yandexhome/img/switch.svg create mode 100644 templates/yandexhome/img/thermostat.ac.svg create mode 100644 templates/yandexhome/img/thermostat.svg create mode 100644 templates/yandexhome/settings.html create mode 100644 templates/yandexhome/yandexhome.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f94d6ed --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +installed diff --git a/README.md b/README.md index decace1..19ec666 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ -# majordomo-yandexhome -Yandex smart home platform integration +# Yandex Home + +Модуль поддержки **личных** (приватных) навыков для платформы умного дома **Яндекс**. \ No newline at end of file diff --git a/img/modules/yandexhome.png b/img/modules/yandexhome.png new file mode 100644 index 0000000000000000000000000000000000000000..d4369a29587e746d9a2f768f88d25d6694080b7b GIT binary patch literal 5131 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 zz+E87taX-1F^My2Zf=-gqvZWriteLog($yandexhome->IncomingRequestFormat($request)); + +$response = new OAuth2\Response(); + +// Проверяем валидность запроса на авторизацию. +if (!$server->validateAuthorizeRequest($request, $response)) { + // Если не валиден, то отправляем ответ с ошибкой и завершаем работу. + $response->send(); + $yandexhome->WriteLog('authorize.php >>> ' . $server->getResponse()); + die; +} + +$_SESSION['REQUEST_URI'] = $request->server['REQUEST_URI']; + +$user_name = $yandexhome->config['USER_NAME']; +$user_pass = $yandexhome->config['USER_PASS']; + +if (isset($_SESSION['user']) && $_SESSION['user'] == $user_name) { + $_SESSION['AUTH'] = true; +} else { + $_SESSION['AUTH'] = false; +} + +$user = $_SESSION['AUTH'] ? $user_name : null; + +$message = ''; + +if (!empty($_SESSION['message'])) { + $message = $_SESSION['message']; + unset($_SESSION['message']); +} + +if ($_SESSION['AUTH'] && isset($_POST['authorized'])) { + $is_authorized = ($_POST['authorized'] === 'yes'); // Если yes, то true, иначе false. + $log = $server->handleAuthorizeRequest($request, $response, $is_authorized); + $response->send(); + unset($_SESSION['user']); + session_destroy(); + $yandexhome->WriteLog('authorize.php >>> ' . $log); +} else if (!$_SESSION['AUTH'] && isset($_POST['login']) && isset($_POST['password'])) { + // Если не авторизованы. + if(!empty($_POST['login']) && !empty($_POST['password']) && $user_name == $_POST['login']) { + // Если переданы логин/пароль, и пользователь с таким логином имеется в списке пользователей, + // то проверим корректность введенного пароля. + if($user_pass == $_POST['password']) { + // Пароль совпадает. Инициируем сессию. + $_SESSION['user'] = $_POST['login']; + $_SESSION['AUTH'] = true; + $user = $_SESSION['AUTH'] ? $user_name : null; + } + } + if(!isset($_SESSION['user']) || $_SESSION['user'] != $_POST['login']) { + // Авторизация не прошла. Сохраним ошибку. + $message = 'Неверный логин или пароль. Повторите попытку.'; + } +} + +?> + + + + + + MajorDoMo | Connect + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + diff --git a/modules/yandexhome/css/style.css b/modules/yandexhome/css/style.css new file mode 100644 index 0000000..fef7843 --- /dev/null +++ b/modules/yandexhome/css/style.css @@ -0,0 +1,197 @@ +.login-page +{ + background-color:#e5e7ed +} +.login-page main +{ + width:100%; + max-width:460px; + margin:8% auto 5% +} +.login-block +{ + background-color:#fff; + padding-right:40px; + padding-left:40px; + padding-top:40px; + padding-bottom:30px; + -webkit-box-shadow:0 3px 50px 0 rgba(0,0,0,.1); + box-shadow:0 3px 50px 0 rgba(0,0,0,.1); + text-align:center; + border-radius:5px +} +.login-block h1,.login-block h6 +{ + font-family:Open Sans,sans-serif; + color:#96a2b2; + letter-spacing:1px +} +.login-block h1 +{ + font-size:22px; + margin-bottom:40px; + margin-top:40px +} +.login-block h6 +{ + font-size:11px; + text-transform:uppercase; + margin-top:0 +} +.login-block .form-group +{ + margin-top:15px; + margin-bottom:15px; +} +.login-block .form-control,.login-block .form-control:focus,.login-block .input-group-addon,.login-block .input-group-addon:focus +{ + background-color:transparent; + border:none; +} +.login-block .form-control +{ + font-size:17px; + border-radius:0px; +} +.login-block input:-webkit-autofill +{ + -webkit-box-shadow:0 0 0 1000px #fff inset; + -webkit-text-fill-color:#818a91; + -webkit-transition:none; + -o-transition:none; + transition:none; +} +.login-block .input-group-addon +{ + color:#29aafe; + font-size:19px; + opacity:.5 +} +.login-block .btn-block +{ + margin-top:30px; + padding:15px; + padding-top:10px; + padding-bottom:10px; + font-size:16px; + background:#29aafe; + border-color:#29aafe; +} +.login-block .hr-xs +{ + margin-top:5px; + margin-bottom:5px +} +.login-footer +{ + margin-top:60px; + opacity:.5; + -webkit-transition:opacity .3s ease-in-out; + -o-transition:opacity .3s ease-in-out; + transition:opacity .3s ease-in-out +} +.login-footer:hover +{ + opacity:1 +} +.login-links +{ + padding:15px 5px 0; + font-size:13px; + color:#96a2b2 +} +.login-links:after +{ + content:''; + display:table; + clear:both +} +.login-links a +{ + color:#96a2b2; + opacity:.9 +} +.login-links a:hover +{ + color:#29aafe; + opacity:1 +} +@media (max-width:767px) +{ + .login-page main + { + position:static; + top:auto; + left:auto; + -webkit-transform:none; + -o-transform:none; + transform:none; + padding:30px 15px + } + .login-block{padding:20px}} +.social-icons +{ + padding-left:0; + margin-bottom:0; + list-style:none +} +.social-icons li +{ + display:inline-block; + margin-bottom:4px +} +.social-icons li.title +{ + margin-right:15px; + text-transform:uppercase; + color:#96a2b2; + font-weight:700; + font-size:13px +} +.social-icons a{ + background-color:#eceeef; + color:#818a91; + font-size:16px; + display:inline-block; + line-height:44px; + width:44px; + height:44px; + text-align:center; + margin-right:8px; + border-radius:100%; + -webkit-transition:all .2s linear; + -o-transition:all .2s linear; + transition:all .2s linear +} +.social-icons a:active,.social-icons a:focus,.social-icons a:hover +{ + color:#fff; + background-color:#29aafe +} +.social-icons.size-sm a +{ + line-height:34px; + height:34px; + width:34px; + font-size:14px +} +.social-icons a.facebook:hover +{ + background-color:#3b5998 +} +.social-icons a.rss:hover +{ + background-color:#f26522 +} +.social-icons a.google-plus:hover +{ + background-color:#dd4b39 +} +.social-icons a.twitter:hover +{ + background-color:#00aced +} +.social-icons a.linkedin:hover +{ + background-color:#007bb6 +} diff --git a/modules/yandexhome/favicon.ico b/modules/yandexhome/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..5839676376edbd3557795630222bc054fbafbffc GIT binary patch literal 40451 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 Z#U37UOCoTYb1p~#ni@Llc~`6-{2LD{2#Ejy literal 0 HcmV?d00001 diff --git a/modules/yandexhome/img/ylogo.png b/modules/yandexhome/img/ylogo.png new file mode 100644 index 0000000000000000000000000000000000000000..6949ab85c7879a78055acdfa012402ed07d3d470 GIT binary patch literal 100947 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 literal 0 HcmV?d00001 diff --git a/modules/yandexhome/lib/.htaccess b/modules/yandexhome/lib/.htaccess new file mode 100644 index 0000000..3418e55 --- /dev/null +++ b/modules/yandexhome/lib/.htaccess @@ -0,0 +1 @@ +deny from all \ No newline at end of file diff --git a/modules/yandexhome/lib/OAuth2/.htaccess b/modules/yandexhome/lib/OAuth2/.htaccess new file mode 100644 index 0000000..3418e55 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/.htaccess @@ -0,0 +1 @@ +deny from all \ No newline at end of file diff --git a/modules/yandexhome/lib/OAuth2/Autoloader.php b/modules/yandexhome/lib/OAuth2/Autoloader.php new file mode 100644 index 0000000..4ec08cb --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Autoloader.php @@ -0,0 +1,54 @@ + + * @license MIT License + */ +class Autoloader +{ + /** + * @var string + */ + private $dir; + + /** + * @param string $dir + */ + public function __construct($dir = null) + { + if (is_null($dir)) { + $dir = dirname(__FILE__).'/..'; + } + $this->dir = $dir; + } + + /** + * Registers OAuth2\Autoloader as an SPL autoloader. + */ + public static function register($dir = null) + { + ini_set('unserialize_callback_func', 'spl_autoload_call'); + spl_autoload_register(array(new self($dir), 'autoload')); + } + + /** + * Handles autoloading of classes. + * + * @param string $class - A class name. + * @return boolean - Returns true if the class has been loaded + */ + public function autoload($class) + { + if (0 !== strpos($class, 'OAuth2')) { + return; + } + + if (file_exists($file = $this->dir.'/'.str_replace('\\', '/', $class).'.php')) { + require $file; + } + } +} diff --git a/modules/yandexhome/lib/OAuth2/ClientAssertionType/ClientAssertionTypeInterface.php b/modules/yandexhome/lib/OAuth2/ClientAssertionType/ClientAssertionTypeInterface.php new file mode 100644 index 0000000..6a167da --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/ClientAssertionType/ClientAssertionTypeInterface.php @@ -0,0 +1,28 @@ + + */ +class HttpBasic implements ClientAssertionTypeInterface +{ + private $clientData; + + protected $storage; + protected $config; + + /** + * Config array $config should look as follows: + * @code + * $config = array( + * 'allow_credentials_in_request_body' => true, // whether to look for credentials in the POST body in addition to the Authorize HTTP Header + * 'allow_public_clients' => true // if true, "public clients" (clients without a secret) may be authenticated + * ); + * @endcode + * + * @param ClientCredentialsInterface $storage Storage + * @param array $config Configuration options for the server + */ + public function __construct(ClientCredentialsInterface $storage, array $config = array()) + { + $this->storage = $storage; + $this->config = array_merge(array( + 'allow_credentials_in_request_body' => true, + 'allow_public_clients' => true, + ), $config); + } + + /** + * Validate the OAuth request + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool|mixed + * @throws LogicException + */ + public function validateRequest(RequestInterface $request, ResponseInterface $response) + { + if (!$clientData = $this->getClientCredentials($request, $response)) { + return false; + } + + if (!isset($clientData['client_id'])) { + throw new LogicException('the clientData array must have "client_id" set'); + } + + if (!isset($clientData['client_secret']) || $clientData['client_secret'] == '') { + if (!$this->config['allow_public_clients']) { + $response->setError(400, 'invalid_client', 'client credentials are required'); + + return false; + } + + if (!$this->storage->isPublicClient($clientData['client_id'])) { + $response->setError(400, 'invalid_client', 'This client is invalid or must authenticate using a client secret'); + + return false; + } + } elseif ($this->storage->checkClientCredentials($clientData['client_id'], $clientData['client_secret']) === false) { + $response->setError(400, 'invalid_client', 'The client credentials are invalid'); + + return false; + } + + $this->clientData = $clientData; + + return true; + } + + /** + * Get the client id + * + * @return mixed + */ + public function getClientId() + { + return $this->clientData['client_id']; + } + + /** + * Internal function used to get the client credentials from HTTP basic + * auth or POST data. + * + * According to the spec (draft 20), the client_id can be provided in + * the Basic Authorization header (recommended) or via GET/POST. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return array|null A list containing the client identifier and password, for example: + * @code + * return array( + * "client_id" => CLIENT_ID, // REQUIRED the client id + * "client_secret" => CLIENT_SECRET, // OPTIONAL the client secret (may be omitted for public clients) + * ); + * @endcode + * + * @see http://tools.ietf.org/html/rfc6749#section-2.3.1 + * + * @ingroup oauth2_section_2 + */ + public function getClientCredentials(RequestInterface $request, ResponseInterface $response = null) + { + if (!is_null($request->headers('PHP_AUTH_USER')) && !is_null($request->headers('PHP_AUTH_PW'))) { + return array('client_id' => $request->headers('PHP_AUTH_USER'), 'client_secret' => $request->headers('PHP_AUTH_PW')); + } + + if ($this->config['allow_credentials_in_request_body']) { + // Using POST for HttpBasic authorization is not recommended, but is supported by specification + if (!is_null($request->request('client_id'))) { + /** + * client_secret can be null if the client's password is an empty string + * @see http://tools.ietf.org/html/rfc6749#section-2.3.1 + */ + return array('client_id' => $request->request('client_id'), 'client_secret' => $request->request('client_secret')); + } + } + + if ($response) { + $message = $this->config['allow_credentials_in_request_body'] ? ' or body' : ''; + $response->setError(400, 'invalid_client', 'Client credentials were not found in the headers'.$message); + } + + return null; + } +} diff --git a/modules/yandexhome/lib/OAuth2/Controller/AuthorizeController.php b/modules/yandexhome/lib/OAuth2/Controller/AuthorizeController.php new file mode 100644 index 0000000..4bafb1d --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Controller/AuthorizeController.php @@ -0,0 +1,480 @@ + false, // if the controller should allow the "implicit" grant type + * 'enforce_state' => true // if the controller should require the "state" parameter + * 'require_exact_redirect_uri' => true, // if the controller should require an exact match on the "redirect_uri" parameter + * 'redirect_status_code' => 302, // HTTP status code to use for redirect responses + * ); + * @endcode + */ + public function __construct(ClientInterface $clientStorage, array $responseTypes = array(), array $config = array(), ScopeInterface $scopeUtil = null) + { + $this->clientStorage = $clientStorage; + $this->responseTypes = $responseTypes; + $this->config = array_merge(array( + 'allow_implicit' => false, + 'enforce_state' => true, + 'require_exact_redirect_uri' => true, + 'redirect_status_code' => 302, + ), $config); + + if (is_null($scopeUtil)) { + $scopeUtil = new Scope(); + } + $this->scopeUtil = $scopeUtil; + } + + /** + * Handle the authorization request + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @param boolean $is_authorized + * @param mixed $user_id + * @return mixed|void + * @throws InvalidArgumentException + */ + public function handleAuthorizeRequest(RequestInterface $request, ResponseInterface $response, $is_authorized, $user_id = null) + { + if (!is_bool($is_authorized)) { + throw new InvalidArgumentException('Argument "is_authorized" must be a boolean. This method must know if the user has granted access to the client.'); + } + + // We repeat this, because we need to re-validate. The request could be POSTed + // by a 3rd-party (because we are not internally enforcing NONCEs, etc) + if (!$this->validateAuthorizeRequest($request, $response)) { + return; + } + + // If no redirect_uri is passed in the request, use client's registered one + if (empty($this->redirect_uri)) { + $clientData = $this->clientStorage->getClientDetails($this->client_id); + $registered_redirect_uri = $clientData['redirect_uri']; + } + + // the user declined access to the client's application + if ($is_authorized === false) { + $redirect_uri = $this->redirect_uri ?: $registered_redirect_uri; + $this->setNotAuthorizedResponse($request, $response, $redirect_uri, $user_id); + + return; + } + + // build the parameters to set in the redirect URI + if (!$params = $this->buildAuthorizeParameters($request, $response, $user_id)) { + return; + } + + $authResult = $this->responseTypes[$this->response_type]->getAuthorizeResponse($params, $user_id); + + list($redirect_uri, $uri_params) = $authResult; + + if (empty($redirect_uri) && !empty($registered_redirect_uri)) { + $redirect_uri = $registered_redirect_uri; + } + + $uri = $this->buildUri($redirect_uri, $uri_params); + + // return redirect response + $response->setRedirect($this->config['redirect_status_code'], $uri); + } + + /** + * Set not authorized response + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @param string $redirect_uri + * @param mixed $user_id + */ + protected function setNotAuthorizedResponse(RequestInterface $request, ResponseInterface $response, $redirect_uri, $user_id = null) + { + $error = 'access_denied'; + $error_message = 'The user denied access to your application'; + $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $this->state, $error, $error_message); + } + + /** + * We have made this protected so this class can be extended to add/modify + * these parameters + * + * @TODO: add dependency injection for the parameters in this method + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @param mixed $user_id + * @return array + */ + protected function buildAuthorizeParameters($request, $response, $user_id) + { + // @TODO: we should be explicit with this in the future + $params = array( + 'scope' => $this->scope, + 'state' => $this->state, + 'client_id' => $this->client_id, + 'redirect_uri' => $this->redirect_uri, + 'response_type' => $this->response_type, + ); + + return $params; + } + + /** + * Validate the OAuth request + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + public function validateAuthorizeRequest(RequestInterface $request, ResponseInterface $response) + { + // Make sure a valid client id was supplied (we can not redirect because we were unable to verify the URI) + if (!$client_id = $request->query('client_id', $request->request('client_id'))) { + // We don't have a good URI to use + $response->setError(400, 'invalid_client', "No client id supplied"); + + return false; + } + + // Get client details + if (!$clientData = $this->clientStorage->getClientDetails($client_id)) { + $response->setError(400, 'invalid_client', 'The client id supplied is invalid'); + + return false; + } + + $registered_redirect_uri = isset($clientData['redirect_uri']) ? $clientData['redirect_uri'] : ''; + + // Make sure a valid redirect_uri was supplied. If specified, it must match the clientData URI. + // @see http://tools.ietf.org/html/rfc6749#section-3.1.2 + // @see http://tools.ietf.org/html/rfc6749#section-4.1.2.1 + // @see http://tools.ietf.org/html/rfc6749#section-4.2.2.1 + if ($supplied_redirect_uri = $request->query('redirect_uri', $request->request('redirect_uri'))) { + // validate there is no fragment supplied + $parts = parse_url($supplied_redirect_uri); + if (isset($parts['fragment']) && $parts['fragment']) { + $response->setError(400, 'invalid_uri', 'The redirect URI must not contain a fragment'); + + return false; + } + + // validate against the registered redirect uri(s) if available + if ($registered_redirect_uri && !$this->validateRedirectUri($supplied_redirect_uri, $registered_redirect_uri)) { + $response->setError(400, 'redirect_uri_mismatch', 'The redirect URI provided is missing or does not match', '#section-3.1.2'); + + return false; + } + $redirect_uri = $supplied_redirect_uri; + } else { + // use the registered redirect_uri if none has been supplied, if possible + if (!$registered_redirect_uri) { + $response->setError(400, 'invalid_uri', 'No redirect URI was supplied or stored'); + + return false; + } + + if (count(explode(' ', $registered_redirect_uri)) > 1) { + $response->setError(400, 'invalid_uri', 'A redirect URI must be supplied when multiple redirect URIs are registered', '#section-3.1.2.3'); + + return false; + } + $redirect_uri = $registered_redirect_uri; + } + + // Select the response type + $response_type = $request->query('response_type', $request->request('response_type')); + + // for multiple-valued response types - make them alphabetical + if (false !== strpos($response_type, ' ')) { + $types = explode(' ', $response_type); + sort($types); + $response_type = ltrim(implode(' ', $types)); + } + + $state = $request->query('state', $request->request('state')); + + // type and client_id are required + if (!$response_type || !in_array($response_type, $this->getValidResponseTypes())) { + $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'invalid_request', 'Invalid or missing response type', null); + + return false; + } + + if ($response_type == self::RESPONSE_TYPE_AUTHORIZATION_CODE) { + if (!isset($this->responseTypes['code'])) { + $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'unsupported_response_type', 'authorization code grant type not supported', null); + + return false; + } + if (!$this->clientStorage->checkRestrictedGrantType($client_id, 'authorization_code')) { + $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'unauthorized_client', 'The grant type is unauthorized for this client_id', null); + + return false; + } + if ($this->responseTypes['code']->enforceRedirect() && !$redirect_uri) { + $response->setError(400, 'redirect_uri_mismatch', 'The redirect URI is mandatory and was not supplied'); + + return false; + } + } else { + if (!$this->config['allow_implicit']) { + $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'unsupported_response_type', 'implicit grant type not supported', null); + + return false; + } + if (!$this->clientStorage->checkRestrictedGrantType($client_id, 'implicit')) { + $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'unauthorized_client', 'The grant type is unauthorized for this client_id', null); + + return false; + } + } + + // validate requested scope if it exists + $requestedScope = $this->scopeUtil->getScopeFromRequest($request); + + if ($requestedScope) { + // restrict scope by client specific scope if applicable, + // otherwise verify the scope exists + $clientScope = $this->clientStorage->getClientScope($client_id); + if ((empty($clientScope) && !$this->scopeUtil->scopeExists($requestedScope)) + || (!empty($clientScope) && !$this->scopeUtil->checkScope($requestedScope, $clientScope))) { + $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'invalid_scope', 'An unsupported scope was requested', null); + + return false; + } + } else { + // use a globally-defined default scope + $defaultScope = $this->scopeUtil->getDefaultScope($client_id); + + if (false === $defaultScope) { + $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'invalid_client', 'This application requires you specify a scope parameter', null); + + return false; + } + + $requestedScope = $defaultScope; + } + + // Validate state parameter exists (if configured to enforce this) + if ($this->config['enforce_state'] && !$state) { + $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, null, 'invalid_request', 'The state parameter is required'); + + return false; + } + + // save the input data and return true + $this->scope = $requestedScope; + $this->state = $state; + $this->client_id = $client_id; + // Only save the SUPPLIED redirect URI (@see http://tools.ietf.org/html/rfc6749#section-4.1.3) + $this->redirect_uri = $supplied_redirect_uri; + $this->response_type = $response_type; + + return true; + } + + /** + * Build the absolute URI based on supplied URI and parameters. + * + * @param string $uri An absolute URI. + * @param array $params Parameters to be append as GET. + * + * @return string + * An absolute URI with supplied parameters. + * + * @ingroup oauth2_section_4 + */ + private function buildUri($uri, $params) + { + $parse_url = parse_url($uri); + + // Add our params to the parsed uri + foreach ($params as $k => $v) { + if (isset($parse_url[$k])) { + $parse_url[$k] .= "&" . http_build_query($v, '', '&'); + } else { + $parse_url[$k] = http_build_query($v, '', '&'); + } + } + + // Put the uri back together + return + ((isset($parse_url["scheme"])) ? $parse_url["scheme"] . "://" : "") + . ((isset($parse_url["user"])) ? $parse_url["user"] + . ((isset($parse_url["pass"])) ? ":" . $parse_url["pass"] : "") . "@" : "") + . ((isset($parse_url["host"])) ? $parse_url["host"] : "") + . ((isset($parse_url["port"])) ? ":" . $parse_url["port"] : "") + . ((isset($parse_url["path"])) ? $parse_url["path"] : "") + . ((isset($parse_url["query"]) && !empty($parse_url['query'])) ? "?" . $parse_url["query"] : "") + . ((isset($parse_url["fragment"])) ? "#" . $parse_url["fragment"] : "") + ; + } + + protected function getValidResponseTypes() + { + return array( + self::RESPONSE_TYPE_ACCESS_TOKEN, + self::RESPONSE_TYPE_AUTHORIZATION_CODE, + ); + } + + /** + * Internal method for validating redirect URI supplied + * + * @param string $inputUri The submitted URI to be validated + * @param string $registeredUriString The allowed URI(s) to validate against. Can be a space-delimited string of URIs to + * allow for multiple URIs + * @return bool + * @see http://tools.ietf.org/html/rfc6749#section-3.1.2 + */ + protected function validateRedirectUri($inputUri, $registeredUriString) + { + if (!$inputUri || !$registeredUriString) { + return false; // if either one is missing, assume INVALID + } + + $registered_uris = preg_split('/\s+/', $registeredUriString); + foreach ($registered_uris as $registered_uri) { + if ($this->config['require_exact_redirect_uri']) { + // the input uri is validated against the registered uri using exact match + if (strcmp($inputUri, $registered_uri) === 0) { + return true; + } + } else { + $registered_uri_length = strlen($registered_uri); + if ($registered_uri_length === 0) { + return false; + } + + // the input uri is validated against the registered uri using case-insensitive match of the initial string + // i.e. additional query parameters may be applied + if (strcasecmp(substr($inputUri, 0, $registered_uri_length), $registered_uri) === 0) { + return true; + } + } + } + + return false; + } + + /** + * Convenience method to access the scope + * + * @return string + */ + public function getScope() + { + return $this->scope; + } + + /** + * Convenience method to access the state + * + * @return int + */ + public function getState() + { + return $this->state; + } + + /** + * Convenience method to access the client id + * + * @return mixed + */ + public function getClientId() + { + return $this->client_id; + } + + /** + * Convenience method to access the redirect url + * + * @return string + */ + public function getRedirectUri() + { + return $this->redirect_uri; + } + + /** + * Convenience method to access the response type + * + * @return string + */ + public function getResponseType() + { + return $this->response_type; + } +} diff --git a/modules/yandexhome/lib/OAuth2/Controller/AuthorizeControllerInterface.php b/modules/yandexhome/lib/OAuth2/Controller/AuthorizeControllerInterface.php new file mode 100644 index 0000000..f758f97 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Controller/AuthorizeControllerInterface.php @@ -0,0 +1,58 @@ +somehowDetermineUserId(); + * $is_authorized = $this->somehowDetermineUserAuthorization(); + * $response = new OAuth2\Response(); + * $authorizeController->handleAuthorizeRequest( + * OAuth2\Request::createFromGlobals(), + * $response, + * $is_authorized, + * $user_id + * ); + * $response->send(); + * @endcode + */ +interface AuthorizeControllerInterface +{ + /** + * List of possible authentication response types. + * The "authorization_code" mechanism exclusively supports 'code' + * and the "implicit" mechanism exclusively supports 'token'. + * + * @var string + * @see http://tools.ietf.org/html/rfc6749#section-4.1.1 + * @see http://tools.ietf.org/html/rfc6749#section-4.2.1 + */ + const RESPONSE_TYPE_AUTHORIZATION_CODE = 'code'; + const RESPONSE_TYPE_ACCESS_TOKEN = 'token'; + + /** + * Handle the OAuth request + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @param $is_authorized + * @param null $user_id + * @return mixed + */ + public function handleAuthorizeRequest(RequestInterface $request, ResponseInterface $response, $is_authorized, $user_id = null); + + /** + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + public function validateAuthorizeRequest(RequestInterface $request, ResponseInterface $response); +} diff --git a/modules/yandexhome/lib/OAuth2/Controller/ResourceController.php b/modules/yandexhome/lib/OAuth2/Controller/ResourceController.php new file mode 100644 index 0000000..926f90f --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Controller/ResourceController.php @@ -0,0 +1,156 @@ +tokenType = $tokenType; + $this->tokenStorage = $tokenStorage; + + $this->config = array_merge(array( + 'www_realm' => 'Service', + ), $config); + + if (is_null($scopeUtil)) { + $scopeUtil = new Scope(); + } + $this->scopeUtil = $scopeUtil; + } + + /** + * Verify the resource request + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @param null $scope + * @return bool + */ + public function verifyResourceRequest(RequestInterface $request, ResponseInterface $response, $scope = null) + { + $token = $this->getAccessTokenData($request, $response); + + // Check if we have token data + if (is_null($token)) { + return false; + } + + /** + * Check scope, if provided + * If token doesn't have a scope, it's null/empty, or it's insufficient, then throw 403 + * @see http://tools.ietf.org/html/rfc6750#section-3.1 + */ + if ($scope && (!isset($token["scope"]) || !$token["scope"] || !$this->scopeUtil->checkScope($scope, $token["scope"]))) { + $response->setError(403, 'insufficient_scope', 'The request requires higher privileges than provided by the access token'); + $response->addHttpHeaders(array( + 'WWW-Authenticate' => sprintf('%s realm="%s", scope="%s", error="%s", error_description="%s"', + $this->tokenType->getTokenType(), + $this->config['www_realm'], + $scope, + $response->getParameter('error'), + $response->getParameter('error_description') + ) + )); + + return false; + } + + // allow retrieval of the token + $this->token = $token; + + return (bool) $token; + } + + /** + * Get access token data. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return array|null + */ + public function getAccessTokenData(RequestInterface $request, ResponseInterface $response) + { + // Get the token parameter + if ($token_param = $this->tokenType->getAccessTokenParameter($request, $response)) { + // Get the stored token data (from the implementing subclass) + // Check we have a well formed token + // Check token expiration (expires is a mandatory paramter) + if (!$token = $this->tokenStorage->getAccessToken($token_param)) { + $response->setError(401, 'invalid_token', 'The access token provided is invalid'); + } elseif (!isset($token["expires"]) || !isset($token["client_id"])) { + $response->setError(401, 'malformed_token', 'Malformed token (missing "expires")'); + } elseif (time() > $token["expires"]) { + $response->setError(401, 'invalid_token', 'The access token provided has expired'); + } else { + return $token; + } + } + + $authHeader = sprintf('%s realm="%s"', $this->tokenType->getTokenType(), $this->config['www_realm']); + + if ($error = $response->getParameter('error')) { + $authHeader = sprintf('%s, error="%s"', $authHeader, $error); + if ($error_description = $response->getParameter('error_description')) { + $authHeader = sprintf('%s, error_description="%s"', $authHeader, $error_description); + } + } + + $response->addHttpHeaders(array('WWW-Authenticate' => $authHeader)); + + return null; + } + + /** + * convenience method to allow retrieval of the token. + * + * @return array + */ + public function getToken() + { + return $this->token; + } +} diff --git a/modules/yandexhome/lib/OAuth2/Controller/ResourceControllerInterface.php b/modules/yandexhome/lib/OAuth2/Controller/ResourceControllerInterface.php new file mode 100644 index 0000000..0e847ca --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Controller/ResourceControllerInterface.php @@ -0,0 +1,41 @@ +verifyResourceRequest(OAuth2\Request::createFromGlobals(), $response = new OAuth2\Response())) { + * $response->send(); // authorization failed + * die(); + * } + * return json_encode($resource); // valid token! Send the stuff! + * @endcode + */ +interface ResourceControllerInterface +{ + /** + * Verify the resource request + * + * @param RequestInterface $request - Request object + * @param ResponseInterface $response - Response object + * @param string $scope + * @return mixed + */ + public function verifyResourceRequest(RequestInterface $request, ResponseInterface $response, $scope = null); + + /** + * Get access token data. + * + * @param RequestInterface $request - Request object + * @param ResponseInterface $response - Response object + * @return mixed + */ + public function getAccessTokenData(RequestInterface $request, ResponseInterface $response); +} diff --git a/modules/yandexhome/lib/OAuth2/Controller/TokenController.php b/modules/yandexhome/lib/OAuth2/Controller/TokenController.php new file mode 100644 index 0000000..7fdaf85 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Controller/TokenController.php @@ -0,0 +1,333 @@ + + */ + protected $grantTypes; + + /** + * @var ClientAssertionTypeInterface + */ + protected $clientAssertionType; + + /** + * @var ScopeInterface + */ + protected $scopeUtil; + + /** + * @var ClientInterface + */ + protected $clientStorage; + + /** + * Constructor + * + * @param AccessTokenInterface $accessToken + * @param ClientInterface $clientStorage + * @param array $grantTypes + * @param ClientAssertionTypeInterface $clientAssertionType + * @param ScopeInterface $scopeUtil + * @throws InvalidArgumentException + */ + public function __construct(AccessTokenInterface $accessToken, ClientInterface $clientStorage, array $grantTypes = array(), ClientAssertionTypeInterface $clientAssertionType = null, ScopeInterface $scopeUtil = null) + { + if (is_null($clientAssertionType)) { + foreach ($grantTypes as $grantType) { + if (!$grantType instanceof ClientAssertionTypeInterface) { + throw new InvalidArgumentException('You must supply an instance of OAuth2\ClientAssertionType\ClientAssertionTypeInterface or only use grant types which implement OAuth2\ClientAssertionType\ClientAssertionTypeInterface'); + } + } + } + $this->clientAssertionType = $clientAssertionType; + $this->accessToken = $accessToken; + $this->clientStorage = $clientStorage; + foreach ($grantTypes as $grantType) { + $this->addGrantType($grantType); + } + + if (is_null($scopeUtil)) { + $scopeUtil = new Scope(); + } + $this->scopeUtil = $scopeUtil; + } + + /** + * Handle the token request. + * + * @param RequestInterface $request - Request object to grant access token + * @param ResponseInterface $response - Response object + */ + public function handleTokenRequest(RequestInterface $request, ResponseInterface $response) + { + if ($token = $this->grantAccessToken($request, $response)) { + // @see http://tools.ietf.org/html/rfc6749#section-5.1 + // server MUST disable caching in headers when tokens are involved + $response->setStatusCode(200); + $response->addParameters($token); + $response->addHttpHeaders(array( + 'Cache-Control' => 'no-store', + 'Pragma' => 'no-cache', + 'Content-Type' => 'application/json' + )); + } + } + + /** + * Grant or deny a requested access token. + * This would be called from the "/token" endpoint as defined in the spec. + * You can call your endpoint whatever you want. + * + * @param RequestInterface $request - Request object to grant access token + * @param ResponseInterface $response - Response object + * + * @return bool|null|array + * + * @throws \InvalidArgumentException + * @throws \LogicException + * + * @see http://tools.ietf.org/html/rfc6749#section-4 + * @see http://tools.ietf.org/html/rfc6749#section-10.6 + * @see http://tools.ietf.org/html/rfc6749#section-4.1.3 + * + * @ingroup oauth2_section_4 + */ + public function grantAccessToken(RequestInterface $request, ResponseInterface $response) + { + if (strtolower($request->server('REQUEST_METHOD')) === 'options') { + $response->addHttpHeaders(array('Allow' => 'POST, OPTIONS')); + + return null; + } + + if (strtolower($request->server('REQUEST_METHOD')) !== 'post') { + $response->setError(405, 'invalid_request', 'The request method must be POST when requesting an access token', '#section-3.2'); + $response->addHttpHeaders(array('Allow' => 'POST, OPTIONS')); + + return null; + } + + /** + * Determine grant type from request + * and validate the request for that grant type + */ + if (!$grantTypeIdentifier = $request->request('grant_type')) { + $response->setError(400, 'invalid_request', 'The grant type was not specified in the request'); + + return null; + } + + if (!isset($this->grantTypes[$grantTypeIdentifier])) { + /* TODO: If this is an OAuth2 supported grant type that we have chosen not to implement, throw a 501 Not Implemented instead */ + $response->setError(400, 'unsupported_grant_type', sprintf('Grant type "%s" not supported', $grantTypeIdentifier)); + + return null; + } + + /** @var GrantTypeInterface $grantType */ + $grantType = $this->grantTypes[$grantTypeIdentifier]; + + /** + * Retrieve the client information from the request + * ClientAssertionTypes allow for grant types which also assert the client data + * in which case ClientAssertion is handled in the validateRequest method + * + * @see \OAuth2\GrantType\JWTBearer + * @see \OAuth2\GrantType\ClientCredentials + */ + if (!$grantType instanceof ClientAssertionTypeInterface) { + if (!$this->clientAssertionType->validateRequest($request, $response)) { + return null; + } + $clientId = $this->clientAssertionType->getClientId(); + } + + /** + * Retrieve the grant type information from the request + * The GrantTypeInterface object handles all validation + * If the object is an instance of ClientAssertionTypeInterface, + * That logic is handled here as well + */ + if (!$grantType->validateRequest($request, $response)) { + return null; + } + + if ($grantType instanceof ClientAssertionTypeInterface) { + $clientId = $grantType->getClientId(); + } else { + // validate the Client ID (if applicable) + if (!is_null($storedClientId = $grantType->getClientId()) && $storedClientId != $clientId) { + $response->setError(400, 'invalid_grant', sprintf('%s doesn\'t exist or is invalid for the client', $grantTypeIdentifier)); + + return null; + } + } + + /** + * Validate the client can use the requested grant type + */ + if (!$this->clientStorage->checkRestrictedGrantType($clientId, $grantTypeIdentifier)) { + $response->setError(400, 'unauthorized_client', 'The grant type is unauthorized for this client_id'); + + return false; + } + + /** + * Validate the scope of the token + * + * requestedScope - the scope specified in the token request + * availableScope - the scope associated with the grant type + * ex: in the case of the "Authorization Code" grant type, + * the scope is specified in the authorize request + * + * @see http://tools.ietf.org/html/rfc6749#section-3.3 + */ + $requestedScope = $this->scopeUtil->getScopeFromRequest($request); + $availableScope = $grantType->getScope(); + + if ($requestedScope) { + // validate the requested scope + if ($availableScope) { + if (!$this->scopeUtil->checkScope($requestedScope, $availableScope)) { + $response->setError(400, 'invalid_scope', 'The scope requested is invalid for this request'); + + return null; + } + } else { + // validate the client has access to this scope + if ($clientScope = $this->clientStorage->getClientScope($clientId)) { + if (!$this->scopeUtil->checkScope($requestedScope, $clientScope)) { + $response->setError(400, 'invalid_scope', 'The scope requested is invalid for this client'); + + return false; + } + } elseif (!$this->scopeUtil->scopeExists($requestedScope)) { + $response->setError(400, 'invalid_scope', 'An unsupported scope was requested'); + + return null; + } + } + } elseif ($availableScope) { + // use the scope associated with this grant type + $requestedScope = $availableScope; + } else { + // use a globally-defined default scope + $defaultScope = $this->scopeUtil->getDefaultScope($clientId); + + // "false" means default scopes are not allowed + if (false === $defaultScope) { + $response->setError(400, 'invalid_scope', 'This application requires you specify a scope parameter'); + + return null; + } + + $requestedScope = $defaultScope; + } + + return $grantType->createAccessToken($this->accessToken, $clientId, $grantType->getUserId(), $requestedScope); + } + + /** + * Add grant type + * + * @param GrantTypeInterface $grantType - the grant type to add for the specified identifier + * @param string|null $identifier - a string passed in as "grant_type" in the response that will call this grantType + */ + public function addGrantType(GrantTypeInterface $grantType, $identifier = null) + { + if (is_null($identifier) || is_numeric($identifier)) { + $identifier = $grantType->getQueryStringIdentifier(); + } + + $this->grantTypes[$identifier] = $grantType; + } + + /** + * @param RequestInterface $request + * @param ResponseInterface $response + */ + public function handleRevokeRequest(RequestInterface $request, ResponseInterface $response) + { + if ($this->revokeToken($request, $response)) { + $response->setStatusCode(200); + $response->addParameters(array('revoked' => true)); + } + } + + /** + * Revoke a refresh or access token. Returns true on success and when tokens are invalid + * + * Note: invalid tokens do not cause an error response since the client + * cannot handle such an error in a reasonable way. Moreover, the + * purpose of the revocation request, invalidating the particular token, + * is already achieved. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @throws RuntimeException + * @return bool|null + */ + public function revokeToken(RequestInterface $request, ResponseInterface $response) + { + if (strtolower($request->server('REQUEST_METHOD')) === 'options') { + $response->addHttpHeaders(array('Allow' => 'POST, OPTIONS')); + + return null; + } + + if (strtolower($request->server('REQUEST_METHOD')) !== 'post') { + $response->setError(405, 'invalid_request', 'The request method must be POST when revoking an access token', '#section-3.2'); + $response->addHttpHeaders(array('Allow' => 'POST, OPTIONS')); + + return null; + } + + $token_type_hint = $request->request('token_type_hint'); + if (!in_array($token_type_hint, array(null, 'access_token', 'refresh_token'), true)) { + $response->setError(400, 'invalid_request', 'Token type hint must be either \'access_token\' or \'refresh_token\''); + + return null; + } + + $token = $request->request('token'); + if ($token === null) { + $response->setError(400, 'invalid_request', 'Missing token parameter to revoke'); + + return null; + } + + // @todo remove this check for v2.0 + if (!method_exists($this->accessToken, 'revokeToken')) { + $class = get_class($this->accessToken); + throw new RuntimeException("AccessToken {$class} does not implement required revokeToken method"); + } + + $this->accessToken->revokeToken($token, $token_type_hint); + + return true; + } +} diff --git a/modules/yandexhome/lib/OAuth2/Controller/TokenControllerInterface.php b/modules/yandexhome/lib/OAuth2/Controller/TokenControllerInterface.php new file mode 100644 index 0000000..2f83ce4 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Controller/TokenControllerInterface.php @@ -0,0 +1,39 @@ +handleTokenRequest(OAuth2\Request::createFromGlobals(), $response = new OAuth2\Response()); + * $response->send(); + * @endcode + */ +interface TokenControllerInterface +{ + /** + * Handle the token request + * + * @param RequestInterface $request - The current http request + * @param ResponseInterface $response - An instance of OAuth2\ResponseInterface to contain the response data + */ + public function handleTokenRequest(RequestInterface $request, ResponseInterface $response); + + /** + * Grant or deny a requested access token. + * This would be called from the "/token" endpoint as defined in the spec. + * You can call your endpoint whatever you want. + * + * @param RequestInterface $request - Request object to grant access token + * @param ResponseInterface $response - Response object + * + * @return mixed + */ + public function grantAccessToken(RequestInterface $request, ResponseInterface $response); +} diff --git a/modules/yandexhome/lib/OAuth2/Encryption/EncryptionInterface.php b/modules/yandexhome/lib/OAuth2/Encryption/EncryptionInterface.php new file mode 100644 index 0000000..8dc720a --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Encryption/EncryptionInterface.php @@ -0,0 +1,34 @@ + + */ +class FirebaseJwt implements EncryptionInterface +{ + public function __construct() + { + if (!class_exists('\JWT')) { + throw new \ErrorException('firebase/php-jwt must be installed to use this feature. You can do this by running "composer require firebase/php-jwt"'); + } + } + + public function encode($payload, $key, $alg = 'HS256', $keyId = null) + { + return \JWT::encode($payload, $key, $alg, $keyId); + } + + public function decode($jwt, $key = null, $allowedAlgorithms = null) + { + try { + + //Maintain BC: Do not verify if no algorithms are passed in. + if (!$allowedAlgorithms) { + $key = null; + } + + return (array)\JWT::decode($jwt, $key, $allowedAlgorithms); + } catch (\Exception $e) { + return false; + } + } + + public function urlSafeB64Encode($data) + { + return \JWT::urlsafeB64Encode($data); + } + + public function urlSafeB64Decode($b64) + { + return \JWT::urlsafeB64Decode($b64); + } +} diff --git a/modules/yandexhome/lib/OAuth2/Encryption/Jwt.php b/modules/yandexhome/lib/OAuth2/Encryption/Jwt.php new file mode 100644 index 0000000..c258b8f --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Encryption/Jwt.php @@ -0,0 +1,223 @@ +generateJwtHeader($payload, $algo); + + $segments = array( + $this->urlSafeB64Encode(json_encode($header)), + $this->urlSafeB64Encode(json_encode($payload)) + ); + + $signing_input = implode('.', $segments); + + $signature = $this->sign($signing_input, $key, $algo); + $segments[] = $this->urlsafeB64Encode($signature); + + return implode('.', $segments); + } + + /** + * @param string $jwt + * @param null $key + * @param array|bool $allowedAlgorithms + * @return bool|mixed + */ + public function decode($jwt, $key = null, $allowedAlgorithms = true) + { + if (!strpos($jwt, '.')) { + return false; + } + + $tks = explode('.', $jwt); + + if (count($tks) != 3) { + return false; + } + + list($headb64, $payloadb64, $cryptob64) = $tks; + + if (null === ($header = json_decode($this->urlSafeB64Decode($headb64), true))) { + return false; + } + + if (null === $payload = json_decode($this->urlSafeB64Decode($payloadb64), true)) { + return false; + } + + $sig = $this->urlSafeB64Decode($cryptob64); + + if ((bool) $allowedAlgorithms) { + if (!isset($header['alg'])) { + return false; + } + + // check if bool arg supplied here to maintain BC + if (is_array($allowedAlgorithms) && !in_array($header['alg'], $allowedAlgorithms)) { + return false; + } + + if (!$this->verifySignature($sig, "$headb64.$payloadb64", $key, $header['alg'])) { + return false; + } + } + + return $payload; + } + + /** + * @param $signature + * @param $input + * @param $key + * @param string $algo + * @return bool + * @throws InvalidArgumentException + */ + private function verifySignature($signature, $input, $key, $algo = 'HS256') + { + // use constants when possible, for HipHop support + switch ($algo) { + case'HS256': + case'HS384': + case'HS512': + return $this->hash_equals( + $this->sign($input, $key, $algo), + $signature + ); + + case 'RS256': + return openssl_verify($input, $signature, $key, defined('OPENSSL_ALGO_SHA256') ? OPENSSL_ALGO_SHA256 : 'sha256') === 1; + + case 'RS384': + return @openssl_verify($input, $signature, $key, defined('OPENSSL_ALGO_SHA384') ? OPENSSL_ALGO_SHA384 : 'sha384') === 1; + + case 'RS512': + return @openssl_verify($input, $signature, $key, defined('OPENSSL_ALGO_SHA512') ? OPENSSL_ALGO_SHA512 : 'sha512') === 1; + + default: + throw new InvalidArgumentException("Unsupported or invalid signing algorithm."); + } + } + + /** + * @param $input + * @param $key + * @param string $algo + * @return string + * @throws Exception + */ + private function sign($input, $key, $algo = 'HS256') + { + switch ($algo) { + case 'HS256': + return hash_hmac('sha256', $input, $key, true); + + case 'HS384': + return hash_hmac('sha384', $input, $key, true); + + case 'HS512': + return hash_hmac('sha512', $input, $key, true); + + case 'RS256': + return $this->generateRSASignature($input, $key, defined('OPENSSL_ALGO_SHA256') ? OPENSSL_ALGO_SHA256 : 'sha256'); + + case 'RS384': + return $this->generateRSASignature($input, $key, defined('OPENSSL_ALGO_SHA384') ? OPENSSL_ALGO_SHA384 : 'sha384'); + + case 'RS512': + return $this->generateRSASignature($input, $key, defined('OPENSSL_ALGO_SHA512') ? OPENSSL_ALGO_SHA512 : 'sha512'); + + default: + throw new Exception("Unsupported or invalid signing algorithm."); + } + } + + /** + * @param $input + * @param $key + * @param string $algo + * @return mixed + * @throws Exception + */ + private function generateRSASignature($input, $key, $algo) + { + if (!openssl_sign($input, $signature, $key, $algo)) { + throw new Exception("Unable to sign data."); + } + + return $signature; + } + + /** + * @param string $data + * @return string + */ + public function urlSafeB64Encode($data) + { + $b64 = base64_encode($data); + $b64 = str_replace(array('+', '/', "\r", "\n", '='), + array('-', '_'), + $b64); + + return $b64; + } + + /** + * @param string $b64 + * @return mixed|string + */ + public function urlSafeB64Decode($b64) + { + $b64 = str_replace(array('-', '_'), + array('+', '/'), + $b64); + + return base64_decode($b64); + } + + /** + * Override to create a custom header + */ + protected function generateJwtHeader($payload, $algorithm) + { + return array( + 'typ' => 'JWT', + 'alg' => $algorithm, + ); + } + + /** + * @param string $a + * @param string $b + * @return bool + */ + protected function hash_equals($a, $b) + { + if (function_exists('hash_equals')) { + return hash_equals($a, $b); + } + $diff = strlen($a) ^ strlen($b); + for ($i = 0; $i < strlen($a) && $i < strlen($b); $i++) { + $diff |= ord($a[$i]) ^ ord($b[$i]); + } + + return $diff === 0; + } +} \ No newline at end of file diff --git a/modules/yandexhome/lib/OAuth2/GrantType/AuthorizationCode.php b/modules/yandexhome/lib/OAuth2/GrantType/AuthorizationCode.php new file mode 100644 index 0000000..784f6b3 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/GrantType/AuthorizationCode.php @@ -0,0 +1,142 @@ + + */ +class AuthorizationCode implements GrantTypeInterface +{ + /** + * @var AuthorizationCodeInterface + */ + protected $storage; + + /** + * @var array + */ + protected $authCode; + + /** + * @param AuthorizationCodeInterface $storage - REQUIRED Storage class for retrieving authorization code information + */ + public function __construct(AuthorizationCodeInterface $storage) + { + $this->storage = $storage; + } + + /** + * @return string + */ + public function getQueryStringIdentifier() + { + return 'authorization_code'; + } + + /** + * Validate the OAuth request + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + * @throws Exception + */ + public function validateRequest(RequestInterface $request, ResponseInterface $response) + { + if (!$request->request('code')) { + $response->setError(400, 'invalid_request', 'Missing parameter: "code" is required'); + + return false; + } + + $code = $request->request('code'); + if (!$authCode = $this->storage->getAuthorizationCode($code)) { + $response->setError(400, 'invalid_grant', 'Authorization code doesn\'t exist or is invalid for the client'); + + return false; + } + + /* + * 4.1.3 - ensure that the "redirect_uri" parameter is present if the "redirect_uri" parameter was included in the initial authorization request + * @uri - http://tools.ietf.org/html/rfc6749#section-4.1.3 + */ + if (isset($authCode['redirect_uri']) && $authCode['redirect_uri']) { + if (!$request->request('redirect_uri') || urldecode($request->request('redirect_uri')) != urldecode($authCode['redirect_uri'])) { + $response->setError(400, 'redirect_uri_mismatch', "The redirect URI is missing or do not match", "#section-4.1.3"); + + return false; + } + } + + if (!isset($authCode['expires'])) { + throw new \Exception('Storage must return authcode with a value for "expires"'); + } + + if ($authCode["expires"] < time()) { + $response->setError(400, 'invalid_grant', "The authorization code has expired"); + + return false; + } + + if (!isset($authCode['code'])) { + $authCode['code'] = $code; // used to expire the code after the access token is granted + } + + $this->authCode = $authCode; + + return true; + } + + /** + * Get the client id + * + * @return mixed + */ + public function getClientId() + { + return $this->authCode['client_id']; + } + + /** + * Get the scope + * + * @return string + */ + public function getScope() + { + return isset($this->authCode['scope']) ? $this->authCode['scope'] : null; + } + + /** + * Get the user id + * + * @return mixed + */ + public function getUserId() + { + return isset($this->authCode['user_id']) ? $this->authCode['user_id'] : null; + } + + /** + * Create access token + * + * @param AccessTokenInterface $accessToken + * @param mixed $client_id - client identifier related to the access token. + * @param mixed $user_id - user id associated with the access token + * @param string $scope - scopes to be stored in space-separated string. + * @return array + */ + public function createAccessToken(AccessTokenInterface $accessToken, $client_id, $user_id, $scope) + { + $token = $accessToken->createAccessToken($client_id, $user_id, $scope); + $this->storage->expireAuthorizationCode($this->authCode['code']); + + return $token; + } +} diff --git a/modules/yandexhome/lib/OAuth2/GrantType/ClientCredentials.php b/modules/yandexhome/lib/OAuth2/GrantType/ClientCredentials.php new file mode 100644 index 0000000..e135c2d --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/GrantType/ClientCredentials.php @@ -0,0 +1,98 @@ + + * + * @see HttpBasic + */ +class ClientCredentials extends HttpBasic implements GrantTypeInterface +{ + /** + * @var array + */ + private $clientData; + + /** + * @param ClientCredentialsInterface $storage + * @param array $config + */ + public function __construct(ClientCredentialsInterface $storage, array $config = array()) + { + /** + * The client credentials grant type MUST only be used by confidential clients + * + * @see http://tools.ietf.org/html/rfc6749#section-4.4 + */ + $config['allow_public_clients'] = false; + + parent::__construct($storage, $config); + } + + /** + * Get query string identifier + * + * @return string + */ + public function getQueryStringIdentifier() + { + return 'client_credentials'; + } + + /** + * Get scope + * + * @return string|null + */ + public function getScope() + { + $this->loadClientData(); + + return isset($this->clientData['scope']) ? $this->clientData['scope'] : null; + } + + /** + * Get user id + * + * @return mixed + */ + public function getUserId() + { + $this->loadClientData(); + + return isset($this->clientData['user_id']) ? $this->clientData['user_id'] : null; + } + + /** + * Create access token + * + * @param AccessTokenInterface $accessToken + * @param mixed $client_id - client identifier related to the access token. + * @param mixed $user_id - user id associated with the access token + * @param string $scope - scopes to be stored in space-separated string. + * @return array + */ + public function createAccessToken(AccessTokenInterface $accessToken, $client_id, $user_id, $scope) + { + /** + * Client Credentials Grant does NOT include a refresh token + * + * @see http://tools.ietf.org/html/rfc6749#section-4.4.3 + */ + $includeRefreshToken = false; + + return $accessToken->createAccessToken($client_id, $user_id, $scope, $includeRefreshToken); + } + + private function loadClientData() + { + if (!$this->clientData) { + $this->clientData = $this->storage->getClientDetails($this->getClientId()); + } + } +} diff --git a/modules/yandexhome/lib/OAuth2/GrantType/GrantTypeInterface.php b/modules/yandexhome/lib/OAuth2/GrantType/GrantTypeInterface.php new file mode 100644 index 0000000..f45786f --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/GrantType/GrantTypeInterface.php @@ -0,0 +1,59 @@ + + */ +class JwtBearer implements GrantTypeInterface, ClientAssertionTypeInterface +{ + private $jwt; + + protected $storage; + protected $audience; + protected $jwtUtil; + protected $allowedAlgorithms; + + /** + * Creates an instance of the JWT bearer grant type. + * + * @param JwtBearerInterface $storage - A valid storage interface that implements storage hooks for the JWT + * bearer grant type. + * @param string $audience - The audience to validate the token against. This is usually the full + * URI of the OAuth token requests endpoint. + * @param EncryptionInterface|JWT $jwtUtil - OPTONAL The class used to decode, encode and verify JWTs. + * @param array $config + */ + public function __construct(JwtBearerInterface $storage, $audience, EncryptionInterface $jwtUtil = null, array $config = array()) + { + $this->storage = $storage; + $this->audience = $audience; + + if (is_null($jwtUtil)) { + $jwtUtil = new Jwt(); + } + + $this->config = array_merge(array( + 'allowed_algorithms' => array('RS256', 'RS384', 'RS512') + ), $config); + + $this->jwtUtil = $jwtUtil; + + $this->allowedAlgorithms = $this->config['allowed_algorithms']; + } + + /** + * Returns the grant_type get parameter to identify the grant type request as JWT bearer authorization grant. + * + * @return string - The string identifier for grant_type. + * + * @see GrantTypeInterface::getQueryStringIdentifier() + */ + public function getQueryStringIdentifier() + { + return 'urn:ietf:params:oauth:grant-type:jwt-bearer'; + } + + /** + * Validates the data from the decoded JWT. + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool|mixed|null TRUE if the JWT request is valid and can be decoded. Otherwise, FALSE is returned.@see GrantTypeInterface::getTokenData() + */ + public function validateRequest(RequestInterface $request, ResponseInterface $response) + { + if (!$request->request("assertion")) { + $response->setError(400, 'invalid_request', 'Missing parameters: "assertion" required'); + + return null; + } + + // Store the undecoded JWT for later use + $undecodedJWT = $request->request('assertion'); + + // Decode the JWT + $jwt = $this->jwtUtil->decode($request->request('assertion'), null, false); + + if (!$jwt) { + $response->setError(400, 'invalid_request', "JWT is malformed"); + + return null; + } + + // ensure these properties contain a value + // @todo: throw malformed error for missing properties + $jwt = array_merge(array( + 'scope' => null, + 'iss' => null, + 'sub' => null, + 'aud' => null, + 'exp' => null, + 'nbf' => null, + 'iat' => null, + 'jti' => null, + 'typ' => null, + ), $jwt); + + if (!isset($jwt['iss'])) { + $response->setError(400, 'invalid_grant', "Invalid issuer (iss) provided"); + + return null; + } + + if (!isset($jwt['sub'])) { + $response->setError(400, 'invalid_grant', "Invalid subject (sub) provided"); + + return null; + } + + if (!isset($jwt['exp'])) { + $response->setError(400, 'invalid_grant', "Expiration (exp) time must be present"); + + return null; + } + + // Check expiration + if (ctype_digit($jwt['exp'])) { + if ($jwt['exp'] <= time()) { + $response->setError(400, 'invalid_grant', "JWT has expired"); + + return null; + } + } else { + $response->setError(400, 'invalid_grant', "Expiration (exp) time must be a unix time stamp"); + + return null; + } + + // Check the not before time + if ($notBefore = $jwt['nbf']) { + if (ctype_digit($notBefore)) { + if ($notBefore > time()) { + $response->setError(400, 'invalid_grant', "JWT cannot be used before the Not Before (nbf) time"); + + return null; + } + } else { + $response->setError(400, 'invalid_grant', "Not Before (nbf) time must be a unix time stamp"); + + return null; + } + } + + // Check the audience if required to match + if (!isset($jwt['aud']) || ($jwt['aud'] != $this->audience)) { + $response->setError(400, 'invalid_grant', "Invalid audience (aud)"); + + return null; + } + + // Check the jti (nonce) + // @see http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-13#section-4.1.7 + if (isset($jwt['jti'])) { + $jti = $this->storage->getJti($jwt['iss'], $jwt['sub'], $jwt['aud'], $jwt['exp'], $jwt['jti']); + + //Reject if jti is used and jwt is still valid (exp parameter has not expired). + if ($jti && $jti['expires'] > time()) { + $response->setError(400, 'invalid_grant', "JSON Token Identifier (jti) has already been used"); + + return null; + } else { + $this->storage->setJti($jwt['iss'], $jwt['sub'], $jwt['aud'], $jwt['exp'], $jwt['jti']); + } + } + + // Get the iss's public key + // @see http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-06#section-4.1.1 + if (!$key = $this->storage->getClientKey($jwt['iss'], $jwt['sub'])) { + $response->setError(400, 'invalid_grant', "Invalid issuer (iss) or subject (sub) provided"); + + return null; + } + + // Verify the JWT + if (!$this->jwtUtil->decode($undecodedJWT, $key, $this->allowedAlgorithms)) { + $response->setError(400, 'invalid_grant', "JWT failed signature verification"); + + return null; + } + + $this->jwt = $jwt; + + return true; + } + + /** + * Get client id + * + * @return mixed + */ + public function getClientId() + { + return $this->jwt['iss']; + } + + /** + * Get user id + * + * @return mixed + */ + public function getUserId() + { + return $this->jwt['sub']; + } + + /** + * Get scope + * + * @return null + */ + public function getScope() + { + return null; + } + + /** + * Creates an access token that is NOT associated with a refresh token. + * If a subject (sub) the name of the user/account we are accessing data on behalf of. + * + * @see GrantTypeInterface::createAccessToken() + * + * @param AccessTokenInterface $accessToken + * @param mixed $client_id - client identifier related to the access token. + * @param mixed $user_id - user id associated with the access token + * @param string $scope - scopes to be stored in space-separated string. + * @return array + */ + public function createAccessToken(AccessTokenInterface $accessToken, $client_id, $user_id, $scope) + { + $includeRefreshToken = false; + + return $accessToken->createAccessToken($client_id, $user_id, $scope, $includeRefreshToken); + } +} diff --git a/modules/yandexhome/lib/OAuth2/GrantType/RefreshToken.php b/modules/yandexhome/lib/OAuth2/GrantType/RefreshToken.php new file mode 100644 index 0000000..75c611f --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/GrantType/RefreshToken.php @@ -0,0 +1,154 @@ + + */ +class RefreshToken implements GrantTypeInterface +{ + /** + * @var array + */ + private $refreshToken; + + /** + * @var RefreshTokenInterface + */ + protected $storage; + + /** + * @var array + */ + protected $config; + + /** + * @param RefreshTokenInterface $storage - REQUIRED Storage class for retrieving refresh token information + * @param array $config - OPTIONAL Configuration options for the server + * @code + * $config = array( + * 'always_issue_new_refresh_token' => true, // whether to issue a new refresh token upon successful token request + * 'unset_refresh_token_after_use' => true // whether to unset the refresh token after after using + * ); + * @endcode + */ + public function __construct(RefreshTokenInterface $storage, $config = array()) + { + $this->config = array_merge(array( + 'always_issue_new_refresh_token' => false, + 'unset_refresh_token_after_use' => true + ), $config); + + // to preserve B.C. with v1.6 + // @see https://github.com/bshaffer/oauth2-server-php/pull/580 + // @todo - remove in v2.0 + if (isset($config['always_issue_new_refresh_token']) && !isset($config['unset_refresh_token_after_use'])) { + $this->config['unset_refresh_token_after_use'] = $config['always_issue_new_refresh_token']; + } + + $this->storage = $storage; + } + + /** + * @return string + */ + public function getQueryStringIdentifier() + { + return 'refresh_token'; + } + + /** + * Validate the OAuth request + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool|mixed|null + */ + public function validateRequest(RequestInterface $request, ResponseInterface $response) + { + if (!$request->request("refresh_token")) { + $response->setError(400, 'invalid_request', 'Missing parameter: "refresh_token" is required'); + + return null; + } + + if (!$refreshToken = $this->storage->getRefreshToken($request->request("refresh_token"))) { + $response->setError(400, 'invalid_grant', 'Invalid refresh token'); + + return null; + } + + if ($refreshToken['expires'] > 0 && $refreshToken["expires"] < time()) { + $response->setError(400, 'invalid_grant', 'Refresh token has expired'); + + return null; + } + + // store the refresh token locally so we can delete it when a new refresh token is generated + $this->refreshToken = $refreshToken; + + return true; + } + + /** + * Get client id + * + * @return mixed + */ + public function getClientId() + { + return $this->refreshToken['client_id']; + } + + /** + * Get user id + * + * @return mixed|null + */ + public function getUserId() + { + return isset($this->refreshToken['user_id']) ? $this->refreshToken['user_id'] : null; + } + + /** + * Get scope + * + * @return null|string + */ + public function getScope() + { + return isset($this->refreshToken['scope']) ? $this->refreshToken['scope'] : null; + } + + /** + * Create access token + * + * @param AccessTokenInterface $accessToken + * @param mixed $client_id - client identifier related to the access token. + * @param mixed $user_id - user id associated with the access token + * @param string $scope - scopes to be stored in space-separated string. + * @return array + */ + public function createAccessToken(AccessTokenInterface $accessToken, $client_id, $user_id, $scope) + { + /* + * It is optional to force a new refresh token when a refresh token is used. + * However, if a new refresh token is issued, the old one MUST be expired + * @see http://tools.ietf.org/html/rfc6749#section-6 + */ + $issueNewRefreshToken = $this->config['always_issue_new_refresh_token']; + $unsetRefreshToken = $this->config['unset_refresh_token_after_use']; + $token = $accessToken->createAccessToken($client_id, $user_id, $scope, $issueNewRefreshToken); + + if ($unsetRefreshToken) { + $this->storage->unsetRefreshToken($this->refreshToken['refresh_token']); + } + + return $token; + } +} diff --git a/modules/yandexhome/lib/OAuth2/GrantType/UserCredentials.php b/modules/yandexhome/lib/OAuth2/GrantType/UserCredentials.php new file mode 100644 index 0000000..b10c2dd --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/GrantType/UserCredentials.php @@ -0,0 +1,123 @@ + + */ +class UserCredentials implements GrantTypeInterface +{ + /** + * @var array + */ + private $userInfo; + + /** + * @var UserCredentialsInterface + */ + protected $storage; + + /** + * @param UserCredentialsInterface $storage - REQUIRED Storage class for retrieving user credentials information + */ + public function __construct(UserCredentialsInterface $storage) + { + $this->storage = $storage; + } + + /** + * @return string + */ + public function getQueryStringIdentifier() + { + return 'password'; + } + + /** + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool|mixed|null + * + * @throws LogicException + */ + public function validateRequest(RequestInterface $request, ResponseInterface $response) + { + if (!$request->request("password") || !$request->request("username")) { + $response->setError(400, 'invalid_request', 'Missing parameters: "username" and "password" required'); + + return null; + } + + if (!$this->storage->checkUserCredentials($request->request("username"), $request->request("password"))) { + $response->setError(401, 'invalid_grant', 'Invalid username and password combination'); + + return null; + } + + $userInfo = $this->storage->getUserDetails($request->request("username")); + + if (empty($userInfo)) { + $response->setError(400, 'invalid_grant', 'Unable to retrieve user information'); + + return null; + } + + if (!isset($userInfo['user_id'])) { + throw new \LogicException("you must set the user_id on the array returned by getUserDetails"); + } + + $this->userInfo = $userInfo; + + return true; + } + + /** + * Get client id + * + * @return mixed|null + */ + public function getClientId() + { + return null; + } + + /** + * Get user id + * + * @return mixed + */ + public function getUserId() + { + return $this->userInfo['user_id']; + } + + /** + * Get scope + * + * @return null|string + */ + public function getScope() + { + return isset($this->userInfo['scope']) ? $this->userInfo['scope'] : null; + } + + /** + * Create access token + * + * @param AccessTokenInterface $accessToken + * @param mixed $client_id - client identifier related to the access token. + * @param mixed $user_id - user id associated with the access token + * @param string $scope - scopes to be stored in space-separated string. + * @return array + */ + public function createAccessToken(AccessTokenInterface $accessToken, $client_id, $user_id, $scope) + { + return $accessToken->createAccessToken($client_id, $user_id, $scope); + } +} diff --git a/modules/yandexhome/lib/OAuth2/OpenID/Controller/AuthorizeController.php b/modules/yandexhome/lib/OAuth2/OpenID/Controller/AuthorizeController.php new file mode 100644 index 0000000..54c5f9a --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/OpenID/Controller/AuthorizeController.php @@ -0,0 +1,135 @@ +query('prompt', 'consent'); + if ($prompt == 'none') { + if (is_null($user_id)) { + $error = 'login_required'; + $error_message = 'The user must log in'; + } else { + $error = 'interaction_required'; + $error_message = 'The user must grant access to your application'; + } + } else { + $error = 'consent_required'; + $error_message = 'The user denied access to your application'; + } + + $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $this->getState(), $error, $error_message); + } + + /** + * @TODO: add dependency injection for the parameters in this method + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @param mixed $user_id + * @return array + */ + protected function buildAuthorizeParameters($request, $response, $user_id) + { + if (!$params = parent::buildAuthorizeParameters($request, $response, $user_id)) { + return; + } + + // Generate an id token if needed. + if ($this->needsIdToken($this->getScope()) && $this->getResponseType() == self::RESPONSE_TYPE_AUTHORIZATION_CODE) { + $params['id_token'] = $this->responseTypes['id_token']->createIdToken($this->getClientId(), $user_id, $this->nonce); + } + + // add the nonce to return with the redirect URI + $params['nonce'] = $this->nonce; + + return $params; + } + + /** + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + public function validateAuthorizeRequest(RequestInterface $request, ResponseInterface $response) + { + if (!parent::validateAuthorizeRequest($request, $response)) { + return false; + } + + $nonce = $request->query('nonce'); + + // Validate required nonce for "id_token" and "id_token token" + if (!$nonce && in_array($this->getResponseType(), array(self::RESPONSE_TYPE_ID_TOKEN, self::RESPONSE_TYPE_ID_TOKEN_TOKEN))) { + $response->setError(400, 'invalid_nonce', 'This application requires you specify a nonce parameter'); + + return false; + } + + $this->nonce = $nonce; + + return true; + } + + /** + * Array of valid response types + * + * @return array + */ + protected function getValidResponseTypes() + { + return array( + self::RESPONSE_TYPE_ACCESS_TOKEN, + self::RESPONSE_TYPE_AUTHORIZATION_CODE, + self::RESPONSE_TYPE_ID_TOKEN, + self::RESPONSE_TYPE_ID_TOKEN_TOKEN, + self::RESPONSE_TYPE_CODE_ID_TOKEN, + ); + } + + /** + * Returns whether the current request needs to generate an id token. + * + * ID Tokens are a part of the OpenID Connect specification, so this + * method checks whether OpenID Connect is enabled in the server settings + * and whether the openid scope was requested. + * + * @param string $request_scope - A space-separated string of scopes. + * @return boolean - TRUE if an id token is needed, FALSE otherwise. + */ + public function needsIdToken($request_scope) + { + // see if the "openid" scope exists in the requested scope + return $this->scopeUtil->checkScope('openid', $request_scope); + } + + /** + * @return mixed + */ + public function getNonce() + { + return $this->nonce; + } +} diff --git a/modules/yandexhome/lib/OAuth2/OpenID/Controller/AuthorizeControllerInterface.php b/modules/yandexhome/lib/OAuth2/OpenID/Controller/AuthorizeControllerInterface.php new file mode 100644 index 0000000..b4967c3 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/OpenID/Controller/AuthorizeControllerInterface.php @@ -0,0 +1,12 @@ +userClaimsStorage = $userClaimsStorage; + } + + /** + * Handle the user info request + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return void + */ + public function handleUserInfoRequest(RequestInterface $request, ResponseInterface $response) + { + if (!$this->verifyResourceRequest($request, $response, 'openid')) { + return; + } + + $token = $this->getToken(); + $claims = $this->userClaimsStorage->getUserClaims($token['user_id'], $token['scope']); + // The sub Claim MUST always be returned in the UserInfo Response. + // http://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse + $claims += array( + 'sub' => $token['user_id'], + ); + $response->addParameters($claims); + } +} \ No newline at end of file diff --git a/modules/yandexhome/lib/OAuth2/OpenID/Controller/UserInfoControllerInterface.php b/modules/yandexhome/lib/OAuth2/OpenID/Controller/UserInfoControllerInterface.php new file mode 100644 index 0000000..88e9228 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/OpenID/Controller/UserInfoControllerInterface.php @@ -0,0 +1,30 @@ +handleUserInfoRequest( + * OAuth2\Request::createFromGlobals(), + * $response + * ); + * $response->send(); + * @endcode + */ +interface UserInfoControllerInterface +{ + /** + * Handle user info request + * + * @param RequestInterface $request + * @param ResponseInterface $response + */ + public function handleUserInfoRequest(RequestInterface $request, ResponseInterface $response); +} diff --git a/modules/yandexhome/lib/OAuth2/OpenID/GrantType/AuthorizationCode.php b/modules/yandexhome/lib/OAuth2/OpenID/GrantType/AuthorizationCode.php new file mode 100644 index 0000000..ee113a0 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/OpenID/GrantType/AuthorizationCode.php @@ -0,0 +1,41 @@ + + */ +class AuthorizationCode extends BaseAuthorizationCode +{ + /** + * Create access token + * + * @param AccessTokenInterface $accessToken + * @param mixed $client_id - client identifier related to the access token. + * @param mixed $user_id - user id associated with the access token + * @param string $scope - scopes to be stored in space-separated string. + * @return array + */ + public function createAccessToken(AccessTokenInterface $accessToken, $client_id, $user_id, $scope) + { + $includeRefreshToken = true; + if (isset($this->authCode['id_token'])) { + // OpenID Connect requests include the refresh token only if the + // offline_access scope has been requested and granted. + $scopes = explode(' ', trim($scope)); + $includeRefreshToken = in_array('offline_access', $scopes); + } + + $token = $accessToken->createAccessToken($client_id, $user_id, $scope, $includeRefreshToken); + if (isset($this->authCode['id_token'])) { + $token['id_token'] = $this->authCode['id_token']; + } + + $this->storage->expireAuthorizationCode($this->authCode['code']); + + return $token; + } +} diff --git a/modules/yandexhome/lib/OAuth2/OpenID/ResponseType/AuthorizationCode.php b/modules/yandexhome/lib/OAuth2/OpenID/ResponseType/AuthorizationCode.php new file mode 100644 index 0000000..b8ad41f --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/OpenID/ResponseType/AuthorizationCode.php @@ -0,0 +1,66 @@ + + */ +class AuthorizationCode extends BaseAuthorizationCode implements AuthorizationCodeInterface +{ + /** + * Constructor + * + * @param AuthorizationCodeStorageInterface $storage + * @param array $config + */ + public function __construct(AuthorizationCodeStorageInterface $storage, array $config = array()) + { + parent::__construct($storage, $config); + } + + /** + * @param $params + * @param null $user_id + * @return array + */ + public function getAuthorizeResponse($params, $user_id = null) + { + // build the URL to redirect to + $result = array('query' => array()); + + $params += array('scope' => null, 'state' => null, 'id_token' => null); + + $result['query']['code'] = $this->createAuthorizationCode($params['client_id'], $user_id, $params['redirect_uri'], $params['scope'], $params['id_token']); + + if (isset($params['state'])) { + $result['query']['state'] = $params['state']; + } + + return array($params['redirect_uri'], $result); + } + + /** + * Handle the creation of the authorization code. + * + * @param mixed $client_id - Client identifier related to the authorization code + * @param mixed $user_id - User ID associated with the authorization code + * @param string $redirect_uri - An absolute URI to which the authorization server will redirect the + * user-agent to when the end-user authorization step is completed. + * @param string $scope - OPTIONAL Scopes to be stored in space-separated string. + * @param string $id_token - OPTIONAL The OpenID Connect id_token. + * + * @return string + * @see http://tools.ietf.org/html/rfc6749#section-4 + * @ingroup oauth2_section_4 + */ + public function createAuthorizationCode($client_id, $user_id, $redirect_uri, $scope = null, $id_token = null) + { + $code = $this->generateAuthorizationCode(); + $this->storage->setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, time() + $this->config['auth_code_lifetime'], $scope, $id_token); + + return $code; + } +} diff --git a/modules/yandexhome/lib/OAuth2/OpenID/ResponseType/AuthorizationCodeInterface.php b/modules/yandexhome/lib/OAuth2/OpenID/ResponseType/AuthorizationCodeInterface.php new file mode 100644 index 0000000..eb94ef0 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/OpenID/ResponseType/AuthorizationCodeInterface.php @@ -0,0 +1,27 @@ + + */ +interface AuthorizationCodeInterface extends BaseAuthorizationCodeInterface +{ + /** + * Handle the creation of the authorization code. + * + * @param mixed $client_id - Client identifier related to the authorization code + * @param mixed $user_id - User ID associated with the authorization code + * @param string $redirect_uri - An absolute URI to which the authorization server will redirect the + * user-agent to when the end-user authorization step is completed. + * @param string $scope - OPTIONAL Scopes to be stored in space-separated string. + * @param string $id_token - OPTIONAL The OpenID Connect id_token. + * @return string + * + * @see http://tools.ietf.org/html/rfc6749#section-4 + * @ingroup oauth2_section_4 + */ + public function createAuthorizationCode($client_id, $user_id, $redirect_uri, $scope = null, $id_token = null); +} diff --git a/modules/yandexhome/lib/OAuth2/OpenID/ResponseType/CodeIdToken.php b/modules/yandexhome/lib/OAuth2/OpenID/ResponseType/CodeIdToken.php new file mode 100644 index 0000000..2696ada --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/OpenID/ResponseType/CodeIdToken.php @@ -0,0 +1,40 @@ +authCode = $authCode; + $this->idToken = $idToken; + } + + /** + * @param array $params + * @param mixed $user_id + * @return mixed + */ + public function getAuthorizeResponse($params, $user_id = null) + { + $result = $this->authCode->getAuthorizeResponse($params, $user_id); + $resultIdToken = $this->idToken->getAuthorizeResponse($params, $user_id); + $result[1]['query']['id_token'] = $resultIdToken[1]['fragment']['id_token']; + + return $result; + } +} diff --git a/modules/yandexhome/lib/OAuth2/OpenID/ResponseType/CodeIdTokenInterface.php b/modules/yandexhome/lib/OAuth2/OpenID/ResponseType/CodeIdTokenInterface.php new file mode 100644 index 0000000..629adcc --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/OpenID/ResponseType/CodeIdTokenInterface.php @@ -0,0 +1,9 @@ +userClaimsStorage = $userClaimsStorage; + $this->publicKeyStorage = $publicKeyStorage; + if (is_null($encryptionUtil)) { + $encryptionUtil = new Jwt(); + } + $this->encryptionUtil = $encryptionUtil; + + if (!isset($config['issuer'])) { + throw new LogicException('config parameter "issuer" must be set'); + } + $this->config = array_merge(array( + 'id_lifetime' => 3600, + ), $config); + } + + /** + * @param array $params + * @param null $userInfo + * @return array|mixed + */ + public function getAuthorizeResponse($params, $userInfo = null) + { + // build the URL to redirect to + $result = array('query' => array()); + $params += array('scope' => null, 'state' => null, 'nonce' => null); + + // create the id token. + list($user_id, $auth_time) = $this->getUserIdAndAuthTime($userInfo); + $userClaims = $this->userClaimsStorage->getUserClaims($user_id, $params['scope']); + + $id_token = $this->createIdToken($params['client_id'], $userInfo, $params['nonce'], $userClaims, null); + $result["fragment"] = array('id_token' => $id_token); + if (isset($params['state'])) { + $result["fragment"]["state"] = $params['state']; + } + + return array($params['redirect_uri'], $result); + } + + /** + * Create id token + * + * @param string $client_id + * @param mixed $userInfo + * @param mixed $nonce + * @param mixed $userClaims + * @param mixed $access_token + * @return mixed|string + */ + public function createIdToken($client_id, $userInfo, $nonce = null, $userClaims = null, $access_token = null) + { + // pull auth_time from user info if supplied + list($user_id, $auth_time) = $this->getUserIdAndAuthTime($userInfo); + + $token = array( + 'iss' => $this->config['issuer'], + 'sub' => $user_id, + 'aud' => $client_id, + 'iat' => time(), + 'exp' => time() + $this->config['id_lifetime'], + 'auth_time' => $auth_time, + ); + + if ($nonce) { + $token['nonce'] = $nonce; + } + + if ($userClaims) { + $token += $userClaims; + } + + if ($access_token) { + $token['at_hash'] = $this->createAtHash($access_token, $client_id); + } + + return $this->encodeToken($token, $client_id); + } + + /** + * @param $access_token + * @param null $client_id + * @return mixed|string + */ + protected function createAtHash($access_token, $client_id = null) + { + // maps HS256 and RS256 to sha256, etc. + $algorithm = $this->publicKeyStorage->getEncryptionAlgorithm($client_id); + $hash_algorithm = 'sha' . substr($algorithm, 2); + $hash = hash($hash_algorithm, $access_token, true); + $at_hash = substr($hash, 0, strlen($hash) / 2); + + return $this->encryptionUtil->urlSafeB64Encode($at_hash); + } + + /** + * @param array $token + * @param null $client_id + * @return mixed|string + */ + protected function encodeToken(array $token, $client_id = null) + { + $private_key = $this->publicKeyStorage->getPrivateKey($client_id); + $algorithm = $this->publicKeyStorage->getEncryptionAlgorithm($client_id); + + return $this->encryptionUtil->encode($token, $private_key, $algorithm); + } + + /** + * @param $userInfo + * @return array + * @throws LogicException + */ + private function getUserIdAndAuthTime($userInfo) + { + $auth_time = null; + + // support an array for user_id / auth_time + if (is_array($userInfo)) { + if (!isset($userInfo['user_id'])) { + throw new LogicException('if $user_id argument is an array, user_id index must be set'); + } + + $auth_time = isset($userInfo['auth_time']) ? $userInfo['auth_time'] : null; + $user_id = $userInfo['user_id']; + } else { + $user_id = $userInfo; + } + + if (is_null($auth_time)) { + $auth_time = time(); + } + + // userInfo is a scalar, and so this is the $user_id. Auth Time is null + return array($user_id, $auth_time); + } +} diff --git a/modules/yandexhome/lib/OAuth2/OpenID/ResponseType/IdTokenInterface.php b/modules/yandexhome/lib/OAuth2/OpenID/ResponseType/IdTokenInterface.php new file mode 100644 index 0000000..226a3bc --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/OpenID/ResponseType/IdTokenInterface.php @@ -0,0 +1,30 @@ +accessToken = $accessToken; + $this->idToken = $idToken; + } + + /** + * @param array $params + * @param mixed $user_id + * @return mixed + */ + public function getAuthorizeResponse($params, $user_id = null) + { + $result = $this->accessToken->getAuthorizeResponse($params, $user_id); + $access_token = $result[1]['fragment']['access_token']; + $id_token = $this->idToken->createIdToken($params['client_id'], $user_id, $params['nonce'], null, $access_token); + $result[1]['fragment']['id_token'] = $id_token; + + return $result; + } +} diff --git a/modules/yandexhome/lib/OAuth2/OpenID/ResponseType/IdTokenTokenInterface.php b/modules/yandexhome/lib/OAuth2/OpenID/ResponseType/IdTokenTokenInterface.php new file mode 100644 index 0000000..ac13e20 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/OpenID/ResponseType/IdTokenTokenInterface.php @@ -0,0 +1,9 @@ + + */ +interface AuthorizationCodeInterface extends BaseAuthorizationCodeInterface +{ + /** + * Take the provided authorization code values and store them somewhere. + * + * This function should be the storage counterpart to getAuthCode(). + * + * If storage fails for some reason, we're not currently checking for + * any sort of success/failure, so you should bail out of the script + * and provide a descriptive fail message. + * + * Required for OAuth2::GRANT_TYPE_AUTH_CODE. + * + * @param string $code - authorization code to be stored. + * @param mixed $client_id - client identifier to be stored. + * @param mixed $user_id - user identifier to be stored. + * @param string $redirect_uri - redirect URI(s) to be stored in a space-separated string. + * @param int $expires - expiration to be stored as a Unix timestamp. + * @param string $scope - OPTIONAL scopes to be stored in space-separated string. + * @param string $id_token - OPTIONAL the OpenID Connect id_token. + * + * @ingroup oauth2_section_4 + */ + public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null); +} diff --git a/modules/yandexhome/lib/OAuth2/OpenID/Storage/UserClaimsInterface.php b/modules/yandexhome/lib/OAuth2/OpenID/Storage/UserClaimsInterface.php new file mode 100644 index 0000000..9c5e7c8 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/OpenID/Storage/UserClaimsInterface.php @@ -0,0 +1,35 @@ + value format. + * + * @see http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims + */ + public function getUserClaims($user_id, $scope); +} diff --git a/modules/yandexhome/lib/OAuth2/Request.php b/modules/yandexhome/lib/OAuth2/Request.php new file mode 100644 index 0000000..f547bf6 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Request.php @@ -0,0 +1,252 @@ +initialize($query, $request, $attributes, $cookies, $files, $server, $content, $headers); + } + + /** + * Sets the parameters for this request. + * + * This method also re-initializes all properties. + * + * @param array $query - The GET parameters + * @param array $request - The POST parameters + * @param array $attributes - The request attributes (parameters parsed from the PATH_INFO, ...) + * @param array $cookies - The COOKIE parameters + * @param array $files - The FILES parameters + * @param array $server - The SERVER parameters + * @param string $content - The raw body data + * @param array $headers - The headers + * + * @api + */ + public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null, array $headers = null) + { + $this->request = $request; + $this->query = $query; + $this->attributes = $attributes; + $this->cookies = $cookies; + $this->files = $files; + $this->server = $server; + $this->content = $content; + + if ($headers === null) { + $headers = array(); + } + + $this->headers = $headers + $this->getHeadersFromServer($this->server); + } + + /** + * @param string $name + * @param mixed $default + * @return mixed + */ + public function query($name, $default = null) + { + return isset($this->query[$name]) ? $this->query[$name] : $default; + } + + /** + * @param string $name + * @param mixed $default + * @return mixed + */ + public function request($name, $default = null) + { + return isset($this->request[$name]) ? $this->request[$name] : $default; + } + + /** + * @param string $name + * @param mixed $default + * @return mixed + */ + public function server($name, $default = null) + { + return isset($this->server[$name]) ? $this->server[$name] : $default; + } + + /** + * @param string $name + * @param mixed $default + * @return mixed + */ + public function headers($name, $default = null) + { + $headers = array_change_key_case($this->headers); + $name = strtolower($name); + + return isset($headers[$name]) ? $headers[$name] : $default; + } + + /** + * @return array + */ + public function getAllQueryParameters() + { + return $this->query; + } + + /** + * Returns the request body content. + * + * @param boolean $asResource - If true, a resource will be returned + * @return string|resource - The request body content or a resource to read the body stream. + * + * @throws LogicException + */ + public function getContent($asResource = false) + { + if (false === $this->content || (true === $asResource && null !== $this->content)) { + throw new LogicException('getContent() can only be called once when using the resource return type.'); + } + + if (true === $asResource) { + $this->content = false; + + return fopen('php://input', 'rb'); + } + + if (null === $this->content) { + $this->content = file_get_contents('php://input'); + } + + return $this->content; + } + + /** + * @param array $server + * @return array + */ + private function getHeadersFromServer($server) + { + $headers = array(); + foreach ($server as $key => $value) { + if (0 === strpos($key, 'HTTP_')) { + $headers[substr($key, 5)] = $value; + } + // CONTENT_* are not prefixed with HTTP_ + elseif (in_array($key, array('CONTENT_LENGTH', 'CONTENT_MD5', 'CONTENT_TYPE'))) { + $headers[$key] = $value; + } + } + + if (isset($server['PHP_AUTH_USER'])) { + $headers['PHP_AUTH_USER'] = $server['PHP_AUTH_USER']; + $headers['PHP_AUTH_PW'] = isset($server['PHP_AUTH_PW']) ? $server['PHP_AUTH_PW'] : ''; + } else { + /* + * php-cgi under Apache does not pass HTTP Basic user/pass to PHP by default + * For this workaround to work, add this line to your .htaccess file: + * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + * + * A sample .htaccess file: + * RewriteEngine On + * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + * RewriteCond %{REQUEST_FILENAME} !-f + * RewriteRule ^(.*)$ app.php [QSA,L] + */ + + $authorizationHeader = null; + if (isset($server['HTTP_AUTHORIZATION'])) { + $authorizationHeader = $server['HTTP_AUTHORIZATION']; + } elseif (isset($server['REDIRECT_HTTP_AUTHORIZATION'])) { + $authorizationHeader = $server['REDIRECT_HTTP_AUTHORIZATION']; + } elseif (function_exists('apache_request_headers')) { + $requestHeaders = (array) apache_request_headers(); + + // Server-side fix for bug in old Android versions (a nice side-effect of this fix means we don't care about capitalization for Authorization) + $requestHeaders = array_combine(array_map('ucwords', array_keys($requestHeaders)), array_values($requestHeaders)); + + if (isset($requestHeaders['Authorization'])) { + $authorizationHeader = trim($requestHeaders['Authorization']); + } + } + + if (null !== $authorizationHeader) { + $headers['AUTHORIZATION'] = $authorizationHeader; + // Decode AUTHORIZATION header into PHP_AUTH_USER and PHP_AUTH_PW when authorization header is basic + if (0 === stripos($authorizationHeader, 'basic')) { + $exploded = explode(':', base64_decode(substr($authorizationHeader, 6))); + if (count($exploded) == 2) { + list($headers['PHP_AUTH_USER'], $headers['PHP_AUTH_PW']) = $exploded; + } + } + } + } + + // PHP_AUTH_USER/PHP_AUTH_PW + if (isset($headers['PHP_AUTH_USER'])) { + $headers['AUTHORIZATION'] = 'Basic '.base64_encode($headers['PHP_AUTH_USER'].':'.$headers['PHP_AUTH_PW']); + } + + return $headers; + } + + /** + * Creates a new request with values from PHP's super globals. + * + * @return Request - A new request + * + * @api + */ + public static function createFromGlobals() + { + $class = get_called_class(); + + /** @var Request $request */ + $request = new $class($_GET, $_POST, array(), $_COOKIE, $_FILES, $_SERVER); + + $contentType = $request->server('CONTENT_TYPE', ''); + $requestMethod = $request->server('REQUEST_METHOD', 'GET'); + if (0 === strpos($contentType, 'application/x-www-form-urlencoded') + && in_array(strtoupper($requestMethod), array('PUT', 'DELETE')) + ) { + parse_str($request->getContent(), $data); + $request->request = $data; + } elseif (0 === strpos($contentType, 'application/json') + && in_array(strtoupper($requestMethod), array('POST', 'PUT', 'DELETE')) + ) { + $data = json_decode($request->getContent(), true); + $request->request = $data; + } + + return $request; + } +} diff --git a/modules/yandexhome/lib/OAuth2/RequestInterface.php b/modules/yandexhome/lib/OAuth2/RequestInterface.php new file mode 100644 index 0000000..1d036b7 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/RequestInterface.php @@ -0,0 +1,39 @@ + 'Continue', + 101 => 'Switching Protocols', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 418 => 'I\'m a teapot', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported', + ); + + /** + * @param array $parameters + * @param int $statusCode + * @param array $headers + */ + public function __construct($parameters = array(), $statusCode = 200, $headers = array()) + { + $this->setParameters($parameters); + $this->setStatusCode($statusCode); + $this->setHttpHeaders($headers); + $this->version = '1.1'; + } + + /** + * Converts the response object to string containing all headers and the response content. + * + * @return string The response with headers and content + */ + public function __toString() + { + $headers = array(); + foreach ($this->httpHeaders as $name => $value) { + $headers[$name] = (array) $value; + } + + return + sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)."\r\n". + $this->getHttpHeadersAsString($headers)."\r\n". + $this->getResponseBody(); + } + + /** + * Returns the build header line. + * + * @param string $name The header name + * @param string $value The header value + * + * @return string The built header line + */ + protected function buildHeader($name, $value) + { + return sprintf("%s: %s\n", $name, $value); + } + + /** + * @return int + */ + public function getStatusCode() + { + return $this->statusCode; + } + + /** + * @param int $statusCode + * @param string $text + * @throws InvalidArgumentException + */ + public function setStatusCode($statusCode, $text = null) + { + $this->statusCode = (int) $statusCode; + if ($this->isInvalid()) { + throw new InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $statusCode)); + } + + $this->statusText = false === $text ? '' : (null === $text ? self::$statusTexts[$this->statusCode] : $text); + } + + /** + * @return string + */ + public function getStatusText() + { + return $this->statusText; + } + + /** + * @return array + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * @param array $parameters + */ + public function setParameters(array $parameters) + { + $this->parameters = $parameters; + } + + /** + * @param array $parameters + */ + public function addParameters(array $parameters) + { + $this->parameters = array_merge($this->parameters, $parameters); + } + + /** + * @param string $name + * @param mixed $default + * @return mixed + */ + public function getParameter($name, $default = null) + { + return isset($this->parameters[$name]) ? $this->parameters[$name] : $default; + } + + /** + * @param string $name + * @param mixed $value + */ + public function setParameter($name, $value) + { + $this->parameters[$name] = $value; + } + + /** + * @param array $httpHeaders + */ + public function setHttpHeaders(array $httpHeaders) + { + $this->httpHeaders = $httpHeaders; + } + + /** + * @param string $name + * @param mixed $value + */ + public function setHttpHeader($name, $value) + { + $this->httpHeaders[$name] = $value; + } + + /** + * @param array $httpHeaders + */ + public function addHttpHeaders(array $httpHeaders) + { + $this->httpHeaders = array_merge($this->httpHeaders, $httpHeaders); + } + + /** + * @return array + */ + public function getHttpHeaders() + { + return $this->httpHeaders; + } + + /** + * @param string $name + * @param mixed $default + * @return mixed + */ + public function getHttpHeader($name, $default = null) + { + return isset($this->httpHeaders[$name]) ? $this->httpHeaders[$name] : $default; + } + + /** + * @param string $format + * @return mixed + * @throws InvalidArgumentException + */ + public function getResponseBody($format = 'json') + { + switch ($format) { + case 'json': + return $this->parameters ? json_encode($this->parameters) : ''; + case 'xml': + // this only works for single-level arrays + $xml = new \SimpleXMLElement(''); + foreach ($this->parameters as $key => $param) { + $xml->addChild($key, $param); + } + + return $xml->asXML(); + } + + throw new InvalidArgumentException(sprintf('The format %s is not supported', $format)); + + } + + /** + * @param string $format + */ + public function send($format = 'json') + { + // headers have already been sent by the developer + if (headers_sent()) { + return; + } + + switch ($format) { + case 'json': + $this->setHttpHeader('Content-Type', 'application/json'); + break; + case 'xml': + $this->setHttpHeader('Content-Type', 'text/xml'); + break; + } + // status + header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)); + + foreach ($this->getHttpHeaders() as $name => $header) { + header(sprintf('%s: %s', $name, $header)); + } + echo $this->getResponseBody($format); + } + + /** + * @param int $statusCode + * @param string $error + * @param string $errorDescription + * @param string $errorUri + * @return mixed + * @throws InvalidArgumentException + */ + public function setError($statusCode, $error, $errorDescription = null, $errorUri = null) + { + $parameters = array( + 'error' => $error, + 'error_description' => $errorDescription, + ); + + if (!is_null($errorUri)) { + if (strlen($errorUri) > 0 && $errorUri[0] == '#') { + // we are referencing an oauth bookmark (for brevity) + $errorUri = 'http://tools.ietf.org/html/rfc6749' . $errorUri; + } + $parameters['error_uri'] = $errorUri; + } + + $httpHeaders = array( + 'Cache-Control' => 'no-store' + ); + + $this->setStatusCode($statusCode); + $this->addParameters($parameters); + $this->addHttpHeaders($httpHeaders); + + if (!$this->isClientError() && !$this->isServerError()) { + throw new InvalidArgumentException(sprintf('The HTTP status code is not an error ("%s" given).', $statusCode)); + } + } + + /** + * @param int $statusCode + * @param string $url + * @param string $state + * @param string $error + * @param string $errorDescription + * @param string $errorUri + * @return mixed + * @throws InvalidArgumentException + */ + public function setRedirect($statusCode, $url, $state = null, $error = null, $errorDescription = null, $errorUri = null) + { + if (empty($url)) { + throw new InvalidArgumentException('Cannot redirect to an empty URL.'); + } + + $parameters = array(); + + if (!is_null($state)) { + $parameters['state'] = $state; + } + + if (!is_null($error)) { + $this->setError(400, $error, $errorDescription, $errorUri); + } + $this->setStatusCode($statusCode); + $this->addParameters($parameters); + + if (count($this->parameters) > 0) { + // add parameters to URL redirection + $parts = parse_url($url); + $sep = isset($parts['query']) && !empty($parts['query']) ? '&' : '?'; + $url .= $sep . http_build_query($this->parameters); + } + + $this->addHttpHeaders(array('Location' => $url)); + + if (!$this->isRedirection()) { + throw new InvalidArgumentException(sprintf('The HTTP status code is not a redirect ("%s" given).', $statusCode)); + } + } + + /** + * @return Boolean + * + * @api + * + * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html + */ + public function isInvalid() + { + return $this->statusCode < 100 || $this->statusCode >= 600; + } + + /** + * @return Boolean + * + * @api + */ + public function isInformational() + { + return $this->statusCode >= 100 && $this->statusCode < 200; + } + + /** + * @return Boolean + * + * @api + */ + public function isSuccessful() + { + return $this->statusCode >= 200 && $this->statusCode < 300; + } + + /** + * @return Boolean + * + * @api + */ + public function isRedirection() + { + return $this->statusCode >= 300 && $this->statusCode < 400; + } + + /** + * @return Boolean + * + * @api + */ + public function isClientError() + { + return $this->statusCode >= 400 && $this->statusCode < 500; + } + + /** + * @return Boolean + * + * @api + */ + public function isServerError() + { + return $this->statusCode >= 500 && $this->statusCode < 600; + } + + /** + * Function from Symfony2 HttpFoundation - output pretty header + * + * @param array $headers + * @return string + */ + private function getHttpHeadersAsString($headers) + { + if (count($headers) == 0) { + return ''; + } + + $max = max(array_map('strlen', array_keys($headers))) + 1; + $content = ''; + ksort($headers); + foreach ($headers as $name => $values) { + foreach ($values as $value) { + $content .= sprintf("%-{$max}s %s\r\n", $this->beautifyHeaderName($name).':', $value); + } + } + + return $content; + } + + /** + * Function from Symfony2 HttpFoundation - output pretty header + * + * @param string $name + * @return mixed + */ + private function beautifyHeaderName($name) + { + return preg_replace_callback('/\-(.)/', array($this, 'beautifyCallback'), ucfirst($name)); + } + + /** + * Function from Symfony2 HttpFoundation - output pretty header + * + * @param array $match + * @return string + */ + private function beautifyCallback($match) + { + return '-'.strtoupper($match[1]); + } +} diff --git a/modules/yandexhome/lib/OAuth2/ResponseInterface.php b/modules/yandexhome/lib/OAuth2/ResponseInterface.php new file mode 100644 index 0000000..fe92086 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/ResponseInterface.php @@ -0,0 +1,53 @@ + + */ +class AccessToken implements AccessTokenInterface +{ + /** + * @var AccessTokenInterface + */ + protected $tokenStorage; + + /** + * @var RefreshTokenInterface + */ + protected $refreshStorage; + + /** + * @var array + */ + protected $config; + + /** + * @param AccessTokenStorageInterface $tokenStorage - REQUIRED Storage class for saving access token information + * @param RefreshTokenInterface $refreshStorage - OPTIONAL Storage class for saving refresh token information + * @param array $config - OPTIONAL Configuration options for the server + * @code + * $config = array( + * 'token_type' => 'bearer', // token type identifier + * 'access_lifetime' => 3600, // time before access token expires + * 'refresh_token_lifetime' => 1209600, // time before refresh token expires + * ); + * @endcode + */ + public function __construct(AccessTokenStorageInterface $tokenStorage, RefreshTokenInterface $refreshStorage = null, array $config = array()) + { + $this->tokenStorage = $tokenStorage; + $this->refreshStorage = $refreshStorage; + + $this->config = array_merge(array( + 'token_type' => 'bearer', + 'access_lifetime' => 3600, + 'refresh_token_lifetime' => 1209600, + ), $config); + } + + /** + * Get authorize response + * + * @param array $params + * @param mixed $user_id + * @return array + */ + public function getAuthorizeResponse($params, $user_id = null) + { + // build the URL to redirect to + $result = array('query' => array()); + + $params += array('scope' => null, 'state' => null); + + /* + * a refresh token MUST NOT be included in the fragment + * + * @see http://tools.ietf.org/html/rfc6749#section-4.2.2 + */ + $includeRefreshToken = false; + $result["fragment"] = $this->createAccessToken($params['client_id'], $user_id, $params['scope'], $includeRefreshToken); + + if (isset($params['state'])) { + $result["fragment"]["state"] = $params['state']; + } + + return array($params['redirect_uri'], $result); + } + + /** + * Handle the creation of access token, also issue refresh token if supported / desirable. + * + * @param mixed $client_id - client identifier related to the access token. + * @param mixed $user_id - user ID associated with the access token + * @param string $scope - OPTIONAL scopes to be stored in space-separated string. + * @param bool $includeRefreshToken - if true, a new refresh_token will be added to the response + * @return array + * + * @see http://tools.ietf.org/html/rfc6749#section-5 + * @ingroup oauth2_section_5 + */ + public function createAccessToken($client_id, $user_id, $scope = null, $includeRefreshToken = true) + { + $token = array( + "access_token" => $this->generateAccessToken(), + "expires_in" => $this->config['access_lifetime'], + "token_type" => $this->config['token_type'], + "scope" => $scope + ); + + $this->tokenStorage->setAccessToken($token["access_token"], $client_id, $user_id, $this->config['access_lifetime'] ? time() + $this->config['access_lifetime'] : null, $scope); + + /* + * Issue a refresh token also, if we support them + * + * Refresh Tokens are considered supported if an instance of OAuth2\Storage\RefreshTokenInterface + * is supplied in the constructor + */ + if ($includeRefreshToken && $this->refreshStorage) { + $token["refresh_token"] = $this->generateRefreshToken(); + $expires = 0; + if ($this->config['refresh_token_lifetime'] > 0) { + $expires = time() + $this->config['refresh_token_lifetime']; + } + $this->refreshStorage->setRefreshToken($token['refresh_token'], $client_id, $user_id, $expires, $scope); + } + + return $token; + } + + /** + * Generates an unique access token. + * + * Implementing classes may want to override this function to implement + * other access token generation schemes. + * + * @return string - A unique access token. + * + * @ingroup oauth2_section_4 + */ + protected function generateAccessToken() + { + if (function_exists('random_bytes')) { + $randomData = random_bytes(20); + if ($randomData !== false && strlen($randomData) === 20) { + return bin2hex($randomData); + } + } + if (function_exists('openssl_random_pseudo_bytes')) { + $randomData = openssl_random_pseudo_bytes(20); + if ($randomData !== false && strlen($randomData) === 20) { + return bin2hex($randomData); + } + } + if (function_exists('mcrypt_create_iv')) { + $randomData = mcrypt_create_iv(20, MCRYPT_DEV_URANDOM); + if ($randomData !== false && strlen($randomData) === 20) { + return bin2hex($randomData); + } + } + if (@file_exists('/dev/urandom')) { // Get 100 bytes of random data + $randomData = file_get_contents('/dev/urandom', false, null, 0, 20); + if ($randomData !== false && strlen($randomData) === 20) { + return bin2hex($randomData); + } + } + // Last resort which you probably should just get rid of: + $randomData = mt_rand() . mt_rand() . mt_rand() . mt_rand() . microtime(true) . uniqid(mt_rand(), true); + + return substr(hash('sha512', $randomData), 0, 40); + } + + /** + * Generates an unique refresh token + * + * Implementing classes may want to override this function to implement + * other refresh token generation schemes. + * + * @return string - A unique refresh token. + * + * @ingroup oauth2_section_4 + * @see OAuth2::generateAccessToken() + */ + protected function generateRefreshToken() + { + return $this->generateAccessToken(); // let's reuse the same scheme for token generation + } + + /** + * Handle the revoking of refresh tokens, and access tokens if supported / desirable + * RFC7009 specifies that "If the server is unable to locate the token using + * the given hint, it MUST extend its search across all of its supported token types" + * + * @param $token + * @param null $tokenTypeHint + * @throws RuntimeException + * @return boolean + */ + public function revokeToken($token, $tokenTypeHint = null) + { + if ($tokenTypeHint == 'refresh_token') { + if ($this->refreshStorage && $revoked = $this->refreshStorage->unsetRefreshToken($token)) { + return true; + } + } + + /** @TODO remove in v2 */ + if (!method_exists($this->tokenStorage, 'unsetAccessToken')) { + throw new RuntimeException( + sprintf('Token storage %s must implement unsetAccessToken method', get_class($this->tokenStorage) + )); + } + + $revoked = $this->tokenStorage->unsetAccessToken($token); + + // if a typehint is supplied and fails, try other storages + // @see https://tools.ietf.org/html/rfc7009#section-2.1 + if (!$revoked && $tokenTypeHint != 'refresh_token') { + if ($this->refreshStorage) { + $revoked = $this->refreshStorage->unsetRefreshToken($token); + } + } + + return $revoked; + } +} diff --git a/modules/yandexhome/lib/OAuth2/ResponseType/AccessTokenInterface.php b/modules/yandexhome/lib/OAuth2/ResponseType/AccessTokenInterface.php new file mode 100644 index 0000000..0e576df --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/ResponseType/AccessTokenInterface.php @@ -0,0 +1,33 @@ + + */ +interface AccessTokenInterface extends ResponseTypeInterface +{ + /** + * Handle the creation of access token, also issue refresh token if supported / desirable. + * + * @param mixed $client_id - client identifier related to the access token. + * @param mixed $user_id - user ID associated with the access token + * @param string $scope - OPTONAL scopes to be stored in space-separated string. + * @param bool $includeRefreshToken - if true, a new refresh_token will be added to the response + * + * @see http://tools.ietf.org/html/rfc6749#section-5 + * @ingroup oauth2_section_5 + */ + public function createAccessToken($client_id, $user_id, $scope = null, $includeRefreshToken = true); + + /** + * Handle the revoking of refresh tokens, and access tokens if supported / desirable + * + * @param $token + * @param $tokenTypeHint + * @return mixed + * + * @todo v2.0 include this method in interface. Omitted to maintain BC in v1.x + */ + //public function revokeToken($token, $tokenTypeHint); +} \ No newline at end of file diff --git a/modules/yandexhome/lib/OAuth2/ResponseType/AuthorizationCode.php b/modules/yandexhome/lib/OAuth2/ResponseType/AuthorizationCode.php new file mode 100644 index 0000000..b92c73c --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/ResponseType/AuthorizationCode.php @@ -0,0 +1,101 @@ + + */ +class AuthorizationCode implements AuthorizationCodeInterface +{ + protected $storage; + protected $config; + + public function __construct(AuthorizationCodeStorageInterface $storage, array $config = array()) + { + $this->storage = $storage; + $this->config = array_merge(array( + 'enforce_redirect' => false, + 'auth_code_lifetime' => 30, + ), $config); + } + + public function getAuthorizeResponse($params, $user_id = null) + { + // build the URL to redirect to + $result = array('query' => array()); + + $params += array('scope' => null, 'state' => null); + + $result['query']['code'] = $this->createAuthorizationCode($params['client_id'], $user_id, $params['redirect_uri'], $params['scope']); + + if (isset($params['state'])) { + $result['query']['state'] = $params['state']; + } + + return array($params['redirect_uri'], $result); + } + + /** + * Handle the creation of the authorization code. + * + * @param $client_id + * Client identifier related to the authorization code + * @param $user_id + * User ID associated with the authorization code + * @param $redirect_uri + * An absolute URI to which the authorization server will redirect the + * user-agent to when the end-user authorization step is completed. + * @param $scope + * (optional) Scopes to be stored in space-separated string. + * + * @see http://tools.ietf.org/html/rfc6749#section-4 + * @ingroup oauth2_section_4 + */ + public function createAuthorizationCode($client_id, $user_id, $redirect_uri, $scope = null) + { + $code = $this->generateAuthorizationCode(); + $this->storage->setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, time() + $this->config['auth_code_lifetime'], $scope); + + return $code; + } + + /** + * @return + * TRUE if the grant type requires a redirect_uri, FALSE if not + */ + public function enforceRedirect() + { + return $this->config['enforce_redirect']; + } + + /** + * Generates an unique auth code. + * + * Implementing classes may want to override this function to implement + * other auth code generation schemes. + * + * @return + * An unique auth code. + * + * @ingroup oauth2_section_4 + */ + protected function generateAuthorizationCode() + { + $tokenLen = 40; + if (function_exists('random_bytes')) { + $randomData = random_bytes(100); + } elseif (function_exists('openssl_random_pseudo_bytes')) { + $randomData = openssl_random_pseudo_bytes(100); + } elseif (function_exists('mcrypt_create_iv')) { + $randomData = mcrypt_create_iv(100, MCRYPT_DEV_URANDOM); + } elseif (@file_exists('/dev/urandom')) { // Get 100 bytes of random data + $randomData = file_get_contents('/dev/urandom', false, null, 0, 100) . uniqid(mt_rand(), true); + } else { + $randomData = mt_rand() . mt_rand() . mt_rand() . mt_rand() . microtime(true) . uniqid(mt_rand(), true); + } + + return substr(hash('sha512', $randomData), 0, $tokenLen); + } +} diff --git a/modules/yandexhome/lib/OAuth2/ResponseType/AuthorizationCodeInterface.php b/modules/yandexhome/lib/OAuth2/ResponseType/AuthorizationCodeInterface.php new file mode 100644 index 0000000..4f0a29d --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/ResponseType/AuthorizationCodeInterface.php @@ -0,0 +1,30 @@ + + */ +interface AuthorizationCodeInterface extends ResponseTypeInterface +{ + /** + * @return + * TRUE if the grant type requires a redirect_uri, FALSE if not + */ + public function enforceRedirect(); + + /** + * Handle the creation of the authorization code. + * + * @param mixed $client_id - Client identifier related to the authorization code + * @param mixed $user_id - User ID associated with the authorization code + * @param string $redirect_uri - An absolute URI to which the authorization server will redirect the + * user-agent to when the end-user authorization step is completed. + * @param string $scope - OPTIONAL Scopes to be stored in space-separated string. + * @return string + * + * @see http://tools.ietf.org/html/rfc6749#section-4 + * @ingroup oauth2_section_4 + */ + public function createAuthorizationCode($client_id, $user_id, $redirect_uri, $scope = null); +} diff --git a/modules/yandexhome/lib/OAuth2/ResponseType/JwtAccessToken.php b/modules/yandexhome/lib/OAuth2/ResponseType/JwtAccessToken.php new file mode 100644 index 0000000..0ee3708 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/ResponseType/JwtAccessToken.php @@ -0,0 +1,159 @@ + + */ +class JwtAccessToken extends AccessToken +{ + protected $publicKeyStorage; + protected $encryptionUtil; + + /** + * @param PublicKeyInterface $publicKeyStorage - + * @param AccessTokenStorageInterface $tokenStorage - + * @param RefreshTokenInterface $refreshStorage - + * @param array $config - array with key store_encrypted_token_string (bool true) + * whether the entire encrypted string is stored, + * or just the token ID is stored + * @param EncryptionInterface $encryptionUtil - + */ + public function __construct(PublicKeyInterface $publicKeyStorage = null, AccessTokenStorageInterface $tokenStorage = null, RefreshTokenInterface $refreshStorage = null, array $config = array(), EncryptionInterface $encryptionUtil = null) + { + $this->publicKeyStorage = $publicKeyStorage; + $config = array_merge(array( + 'store_encrypted_token_string' => true, + 'issuer' => '' + ), $config); + if (is_null($tokenStorage)) { + // a pass-thru, so we can call the parent constructor + $tokenStorage = new Memory(); + } + if (is_null($encryptionUtil)) { + $encryptionUtil = new Jwt(); + } + $this->encryptionUtil = $encryptionUtil; + parent::__construct($tokenStorage, $refreshStorage, $config); + } + + /** + * Handle the creation of access token, also issue refresh token if supported / desirable. + * + * @param mixed $client_id - Client identifier related to the access token. + * @param mixed $user_id - User ID associated with the access token + * @param string $scope - (optional) Scopes to be stored in space-separated string. + * @param bool $includeRefreshToken - If true, a new refresh_token will be added to the response + * @return array - The access token + * + * @see http://tools.ietf.org/html/rfc6749#section-5 + * @ingroup oauth2_section_5 + */ + public function createAccessToken($client_id, $user_id, $scope = null, $includeRefreshToken = true) + { + // payload to encrypt + $payload = $this->createPayload($client_id, $user_id, $scope); + + /* + * Encode the payload data into a single JWT access_token string + */ + $access_token = $this->encodeToken($payload, $client_id); + + /* + * Save the token to a secondary storage. This is implemented on the + * OAuth2\Storage\JwtAccessToken side, and will not actually store anything, + * if no secondary storage has been supplied + */ + $token_to_store = $this->config['store_encrypted_token_string'] ? $access_token : $payload['id']; + $this->tokenStorage->setAccessToken($token_to_store, $client_id, $user_id, $this->config['access_lifetime'] ? time() + $this->config['access_lifetime'] : null, $scope); + + // token to return to the client + $token = array( + 'access_token' => $access_token, + 'expires_in' => $this->config['access_lifetime'], + 'token_type' => $this->config['token_type'], + 'scope' => $scope + ); + + /* + * Issue a refresh token also, if we support them + * + * Refresh Tokens are considered supported if an instance of OAuth2\Storage\RefreshTokenInterface + * is supplied in the constructor + */ + if ($includeRefreshToken && $this->refreshStorage) { + $refresh_token = $this->generateRefreshToken(); + $expires = 0; + if ($this->config['refresh_token_lifetime'] > 0) { + $expires = time() + $this->config['refresh_token_lifetime']; + } + $this->refreshStorage->setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope); + $token['refresh_token'] = $refresh_token; + } + + return $token; + } + + /** + * @param array $token + * @param mixed $client_id + * @return mixed + */ + protected function encodeToken(array $token, $client_id = null) + { + $private_key = $this->publicKeyStorage->getPrivateKey($client_id); + $algorithm = $this->publicKeyStorage->getEncryptionAlgorithm($client_id); + + return $this->encryptionUtil->encode($token, $private_key, $algorithm); + } + + /** + * This function can be used to create custom JWT payloads + * + * @param mixed $client_id - Client identifier related to the access token. + * @param mixed $user_id - User ID associated with the access token + * @param string $scope - (optional) Scopes to be stored in space-separated string. + * @return array - The access token + */ + protected function createPayload($client_id, $user_id, $scope = null) + { + // token to encrypt + $expires = time() + $this->config['access_lifetime']; + $id = $this->generateAccessToken(); + + $payload = array( + 'id' => $id, // for BC (see #591) + 'jti' => $id, + 'iss' => $this->config['issuer'], + 'aud' => $client_id, + 'sub' => $user_id, + 'exp' => $expires, + 'iat' => time(), + 'token_type' => $this->config['token_type'], + 'scope' => $scope + ); + + if (isset($this->config['jwt_extra_payload_callable'])) { + if (!is_callable($this->config['jwt_extra_payload_callable'])) { + throw new \InvalidArgumentException('jwt_extra_payload_callable is not callable'); + } + + $extra = call_user_func($this->config['jwt_extra_payload_callable'], $client_id, $user_id, $scope); + + if (!is_array($extra)) { + throw new \InvalidArgumentException('jwt_extra_payload_callable must return array'); + } + + $payload = array_merge($extra, $payload); + } + + return $payload; + } +} diff --git a/modules/yandexhome/lib/OAuth2/ResponseType/ResponseTypeInterface.php b/modules/yandexhome/lib/OAuth2/ResponseType/ResponseTypeInterface.php new file mode 100644 index 0000000..a271565 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/ResponseType/ResponseTypeInterface.php @@ -0,0 +1,13 @@ +storage = $storage; + } + + /** + * Check if everything in required scope is contained in available scope. + * + * @param string $required_scope - A space-separated string of scopes. + * @param string $available_scope - A space-separated string of scopes. + * @return bool - TRUE if everything in required scope is contained in available scope and FALSE + * if it isn't. + * + * @see http://tools.ietf.org/html/rfc6749#section-7 + * + * @ingroup oauth2_section_7 + */ + public function checkScope($required_scope, $available_scope) + { + $required_scope = explode(' ', trim($required_scope)); + $available_scope = explode(' ', trim($available_scope)); + + return (count(array_diff($required_scope, $available_scope)) == 0); + } + + /** + * Check if the provided scope exists in storage. + * + * @param string $scope - A space-separated string of scopes. + * @return bool - TRUE if it exists, FALSE otherwise. + */ + public function scopeExists($scope) + { + // Check reserved scopes first. + $scope = explode(' ', trim($scope)); + $reservedScope = $this->getReservedScopes(); + $nonReservedScopes = array_diff($scope, $reservedScope); + if (count($nonReservedScopes) == 0) { + return true; + } else { + // Check the storage for non-reserved scopes. + $nonReservedScopes = implode(' ', $nonReservedScopes); + + return $this->storage->scopeExists($nonReservedScopes); + } + } + + /** + * @param RequestInterface $request + * @return string + */ + public function getScopeFromRequest(RequestInterface $request) + { + // "scope" is valid if passed in either POST or QUERY + return $request->request('scope', $request->query('scope')); + } + + /** + * @param null $client_id + * @return mixed + */ + public function getDefaultScope($client_id = null) + { + return $this->storage->getDefaultScope($client_id); + } + + /** + * Get reserved scopes needed by the server. + * + * In case OpenID Connect is used, these scopes must include: + * 'openid', offline_access'. + * + * @return array - An array of reserved scopes. + */ + public function getReservedScopes() + { + return array('openid', 'offline_access'); + } +} diff --git a/modules/yandexhome/lib/OAuth2/ScopeInterface.php b/modules/yandexhome/lib/OAuth2/ScopeInterface.php new file mode 100644 index 0000000..f65cfa7 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/ScopeInterface.php @@ -0,0 +1,35 @@ + 'OAuth2\Storage\AccessTokenInterface', + 'authorization_code' => 'OAuth2\Storage\AuthorizationCodeInterface', + 'client_credentials' => 'OAuth2\Storage\ClientCredentialsInterface', + 'client' => 'OAuth2\Storage\ClientInterface', + 'refresh_token' => 'OAuth2\Storage\RefreshTokenInterface', + 'user_credentials' => 'OAuth2\Storage\UserCredentialsInterface', + 'user_claims' => 'OAuth2\OpenID\Storage\UserClaimsInterface', + 'public_key' => 'OAuth2\Storage\PublicKeyInterface', + 'jwt_bearer' => 'OAuth2\Storage\JWTBearerInterface', + 'scope' => 'OAuth2\Storage\ScopeInterface', + ); + + /** + * @var array + */ + protected $responseTypeMap = array( + 'token' => 'OAuth2\ResponseType\AccessTokenInterface', + 'code' => 'OAuth2\ResponseType\AuthorizationCodeInterface', + 'id_token' => 'OAuth2\OpenID\ResponseType\IdTokenInterface', + 'id_token token' => 'OAuth2\OpenID\ResponseType\IdTokenTokenInterface', + 'code id_token' => 'OAuth2\OpenID\ResponseType\CodeIdTokenInterface', + ); + + /** + * @param mixed $storage (array or OAuth2\Storage) - single object or array of objects implementing the + * required storage types (ClientCredentialsInterface and AccessTokenInterface as a minimum) + * @param array $config specify a different token lifetime, token header name, etc + * @param array $grantTypes An array of OAuth2\GrantType\GrantTypeInterface to use for granting access tokens + * @param array $responseTypes Response types to use. array keys should be "code" and "token" for + * Access Token and Authorization Code response types + * @param TokenTypeInterface $tokenType The token type object to use. Valid token types are "bearer" and "mac" + * @param ScopeInterface $scopeUtil The scope utility class to use to validate scope + * @param ClientAssertionTypeInterface $clientAssertionType The method in which to verify the client identity. Default is HttpBasic + * + * @ingroup oauth2_section_7 + */ + public function __construct($storage = array(), array $config = array(), array $grantTypes = array(), array $responseTypes = array(), TokenTypeInterface $tokenType = null, ScopeInterface $scopeUtil = null, ClientAssertionTypeInterface $clientAssertionType = null) + { + $storage = is_array($storage) ? $storage : array($storage); + $this->storages = array(); + foreach ($storage as $key => $service) { + $this->addStorage($service, $key); + } + + // merge all config values. These get passed to our controller objects + $this->config = array_merge(array( + 'use_jwt_access_tokens' => false, + 'jwt_extra_payload_callable' => null, + 'store_encrypted_token_string' => true, + 'use_openid_connect' => false, + 'id_lifetime' => 3600, + 'access_lifetime' => 3600, + 'www_realm' => 'Service', + 'token_param_name' => 'access_token', + 'token_bearer_header_name' => 'Bearer', + 'enforce_state' => true, + 'require_exact_redirect_uri' => true, + 'allow_implicit' => false, + 'allow_credentials_in_request_body' => true, + 'allow_public_clients' => true, + 'always_issue_new_refresh_token' => false, + 'unset_refresh_token_after_use' => true, + ), $config); + + foreach ($grantTypes as $key => $grantType) { + $this->addGrantType($grantType, $key); + } + + foreach ($responseTypes as $key => $responseType) { + $this->addResponseType($responseType, $key); + } + + $this->tokenType = $tokenType; + $this->scopeUtil = $scopeUtil; + $this->clientAssertionType = $clientAssertionType; + + if ($this->config['use_openid_connect']) { + $this->validateOpenIdConnect(); + } + } + + /** + * @return AuthorizeControllerInterface + */ + public function getAuthorizeController() + { + if (is_null($this->authorizeController)) { + $this->authorizeController = $this->createDefaultAuthorizeController(); + } + + return $this->authorizeController; + } + + /** + * @return TokenController + */ + public function getTokenController() + { + if (is_null($this->tokenController)) { + $this->tokenController = $this->createDefaultTokenController(); + } + + return $this->tokenController; + } + + /** + * @return ResourceControllerInterface + */ + public function getResourceController() + { + if (is_null($this->resourceController)) { + $this->resourceController = $this->createDefaultResourceController(); + } + + return $this->resourceController; + } + + /** + * @return UserInfoControllerInterface + */ + public function getUserInfoController() + { + if (is_null($this->userInfoController)) { + $this->userInfoController = $this->createDefaultUserInfoController(); + } + + return $this->userInfoController; + } + + /** + * @param AuthorizeControllerInterface $authorizeController + */ + public function setAuthorizeController(AuthorizeControllerInterface $authorizeController) + { + $this->authorizeController = $authorizeController; + } + + /** + * @param TokenControllerInterface $tokenController + */ + public function setTokenController(TokenControllerInterface $tokenController) + { + $this->tokenController = $tokenController; + } + + /** + * @param ResourceControllerInterface $resourceController + */ + public function setResourceController(ResourceControllerInterface $resourceController) + { + $this->resourceController = $resourceController; + } + + /** + * @param UserInfoControllerInterface $userInfoController + */ + public function setUserInfoController(UserInfoControllerInterface $userInfoController) + { + $this->userInfoController = $userInfoController; + } + + /** + * Return claims about the authenticated end-user. + * This would be called from the "/UserInfo" endpoint as defined in the spec. + * + * @param RequestInterface $request - Request object to grant access token + * @param ResponseInterface $response - Response object containing error messages (failure) or user claims (success) + * @return ResponseInterface + * + * @throws \InvalidArgumentException + * @throws \LogicException + * + * @see http://openid.net/specs/openid-connect-core-1_0.html#UserInfo + */ + public function handleUserInfoRequest(RequestInterface $request, ResponseInterface $response = null) + { + $this->response = is_null($response) ? new Response() : $response; + $this->getUserInfoController()->handleUserInfoRequest($request, $this->response); + + return $this->response; + } + + /** + * Grant or deny a requested access token. + * This would be called from the "/token" endpoint as defined in the spec. + * Obviously, you can call your endpoint whatever you want. + * + * @param RequestInterface $request - Request object to grant access token + * @param ResponseInterface $response - Response object containing error messages (failure) or access token (success) + * @return ResponseInterface + * + * @throws \InvalidArgumentException + * @throws \LogicException + * + * @see http://tools.ietf.org/html/rfc6749#section-4 + * @see http://tools.ietf.org/html/rfc6749#section-10.6 + * @see http://tools.ietf.org/html/rfc6749#section-4.1.3 + * + * @ingroup oauth2_section_4 + */ + public function handleTokenRequest(RequestInterface $request, ResponseInterface $response = null) + { + $this->response = is_null($response) ? new Response() : $response; + $this->getTokenController()->handleTokenRequest($request, $this->response); + + return $this->response; + } + + /** + * @param RequestInterface $request - Request object to grant access token + * @param ResponseInterface $response - Response object + * @return mixed + */ + public function grantAccessToken(RequestInterface $request, ResponseInterface $response = null) + { + $this->response = is_null($response) ? new Response() : $response; + $value = $this->getTokenController()->grantAccessToken($request, $this->response); + + return $value; + } + + /** + * Handle a revoke token request + * This would be called from the "/revoke" endpoint as defined in the draft Token Revocation spec + * + * @see https://tools.ietf.org/html/rfc7009#section-2 + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return Response|ResponseInterface + */ + public function handleRevokeRequest(RequestInterface $request, ResponseInterface $response = null) + { + $this->response = is_null($response) ? new Response() : $response; + $this->getTokenController()->handleRevokeRequest($request, $this->response); + + return $this->response; + } + + /** + * Redirect the user appropriately after approval. + * + * After the user has approved or denied the resource request the + * authorization server should call this function to redirect the user + * appropriately. + * + * @param RequestInterface $request - The request should have the follow parameters set in the querystring: + * - response_type: The requested response: an access token, an authorization code, or both. + * - client_id: The client identifier as described in Section 2. + * - redirect_uri: An absolute URI to which the authorization server will redirect the user-agent to when the + * end-user authorization step is completed. + * - scope: (optional) The scope of the resource request expressed as a list of space-delimited strings. + * - state: (optional) An opaque value used by the client to maintain state between the request and callback. + * + * @param ResponseInterface $response - Response object + * @param bool $is_authorized - TRUE or FALSE depending on whether the user authorized the access. + * @param mixed $user_id - Identifier of user who authorized the client + * @return ResponseInterface + * + * @see http://tools.ietf.org/html/rfc6749#section-4 + * + * @ingroup oauth2_section_4 + */ + public function handleAuthorizeRequest(RequestInterface $request, ResponseInterface $response, $is_authorized, $user_id = null) + { + $this->response = $response; + $this->getAuthorizeController()->handleAuthorizeRequest($request, $this->response, $is_authorized, $user_id); + + return $this->response; + } + + /** + * Pull the authorization request data out of the HTTP request. + * - The redirect_uri is OPTIONAL as per draft 20. But your implementation can enforce it + * by setting $config['enforce_redirect'] to true. + * - The state is OPTIONAL but recommended to enforce CSRF. Draft 21 states, however, that + * CSRF protection is MANDATORY. You can enforce this by setting the $config['enforce_state'] to true. + * + * The draft specifies that the parameters should be retrieved from GET, override the Response + * object to change this + * + * @param RequestInterface $request - Request object + * @param ResponseInterface $response - Response object + * @return bool + * + * The authorization parameters so the authorization server can prompt + * the user for approval if valid. + * + * @see http://tools.ietf.org/html/rfc6749#section-4.1.1 + * @see http://tools.ietf.org/html/rfc6749#section-10.12 + * + * @ingroup oauth2_section_3 + */ + public function validateAuthorizeRequest(RequestInterface $request, ResponseInterface $response = null) + { + $this->response = is_null($response) ? new Response() : $response; + $value = $this->getAuthorizeController()->validateAuthorizeRequest($request, $this->response); + + return $value; + } + + /** + * @param RequestInterface $request - Request object + * @param ResponseInterface $response - Response object + * @param string $scope - Scope + * @return mixed + */ + public function verifyResourceRequest(RequestInterface $request, ResponseInterface $response = null, $scope = null) + { + $this->response = is_null($response) ? new Response() : $response; + $value = $this->getResourceController()->verifyResourceRequest($request, $this->response, $scope); + + return $value; + } + + /** + * @param RequestInterface $request - Request object + * @param ResponseInterface $response - Response object + * @return mixed + */ + public function getAccessTokenData(RequestInterface $request, ResponseInterface $response = null) + { + $this->response = is_null($response) ? new Response() : $response; + $value = $this->getResourceController()->getAccessTokenData($request, $this->response); + + return $value; + } + + /** + * @param GrantTypeInterface $grantType + * @param mixed $identifier + */ + public function addGrantType(GrantTypeInterface $grantType, $identifier = null) + { + if (!is_string($identifier)) { + $identifier = $grantType->getQueryStringIdentifier(); + } + + $this->grantTypes[$identifier] = $grantType; + + // persist added grant type down to TokenController + if (!is_null($this->tokenController)) { + $this->getTokenController()->addGrantType($grantType, $identifier); + } + } + + /** + * Set a storage object for the server + * + * @param object $storage - An object implementing one of the Storage interfaces + * @param mixed $key - If null, the storage is set to the key of each storage interface it implements + * + * @throws InvalidArgumentException + * @see storageMap + */ + public function addStorage($storage, $key = null) + { + // if explicitly set to a valid key, do not "magically" set below + if (isset($this->storageMap[$key])) { + if (!is_null($storage) && !$storage instanceof $this->storageMap[$key]) { + throw new \InvalidArgumentException(sprintf('storage of type "%s" must implement interface "%s"', $key, $this->storageMap[$key])); + } + $this->storages[$key] = $storage; + + // special logic to handle "client" and "client_credentials" strangeness + if ($key === 'client' && !isset($this->storages['client_credentials'])) { + if ($storage instanceof ClientCredentialsInterface) { + $this->storages['client_credentials'] = $storage; + } + } elseif ($key === 'client_credentials' && !isset($this->storages['client'])) { + if ($storage instanceof ClientInterface) { + $this->storages['client'] = $storage; + } + } + } elseif (!is_null($key) && !is_numeric($key)) { + throw new \InvalidArgumentException(sprintf('unknown storage key "%s", must be one of [%s]', $key, implode(', ', array_keys($this->storageMap)))); + } else { + $set = false; + foreach ($this->storageMap as $type => $interface) { + if ($storage instanceof $interface) { + $this->storages[$type] = $storage; + $set = true; + } + } + + if (!$set) { + throw new \InvalidArgumentException(sprintf('storage of class "%s" must implement one of [%s]', get_class($storage), implode(', ', $this->storageMap))); + } + } + } + + /** + * @param ResponseTypeInterface $responseType + * @param mixed $key + * + * @throws InvalidArgumentException + */ + public function addResponseType(ResponseTypeInterface $responseType, $key = null) + { + $key = $this->normalizeResponseType($key); + + if (isset($this->responseTypeMap[$key])) { + if (!$responseType instanceof $this->responseTypeMap[$key]) { + throw new \InvalidArgumentException(sprintf('responseType of type "%s" must implement interface "%s"', $key, $this->responseTypeMap[$key])); + } + $this->responseTypes[$key] = $responseType; + } elseif (!is_null($key) && !is_numeric($key)) { + throw new \InvalidArgumentException(sprintf('unknown responseType key "%s", must be one of [%s]', $key, implode(', ', array_keys($this->responseTypeMap)))); + } else { + $set = false; + foreach ($this->responseTypeMap as $type => $interface) { + if ($responseType instanceof $interface) { + $this->responseTypes[$type] = $responseType; + $set = true; + } + } + + if (!$set) { + throw new \InvalidArgumentException(sprintf('Unknown response type %s. Please implement one of [%s]', get_class($responseType), implode(', ', $this->responseTypeMap))); + } + } + } + + /** + * @return ScopeInterface + */ + public function getScopeUtil() + { + if (!$this->scopeUtil) { + $storage = isset($this->storages['scope']) ? $this->storages['scope'] : null; + $this->scopeUtil = new Scope($storage); + } + + return $this->scopeUtil; + } + + /** + * @param ScopeInterface $scopeUtil + */ + public function setScopeUtil($scopeUtil) + { + $this->scopeUtil = $scopeUtil; + } + + /** + * @return AuthorizeControllerInterface + * @throws LogicException + */ + protected function createDefaultAuthorizeController() + { + if (!isset($this->storages['client'])) { + throw new \LogicException('You must supply a storage object implementing \OAuth2\Storage\ClientInterface to use the authorize server'); + } + if (0 == count($this->responseTypes)) { + $this->responseTypes = $this->getDefaultResponseTypes(); + } + if ($this->config['use_openid_connect'] && !isset($this->responseTypes['id_token'])) { + $this->responseTypes['id_token'] = $this->createDefaultIdTokenResponseType(); + if ($this->config['allow_implicit']) { + $this->responseTypes['id_token token'] = $this->createDefaultIdTokenTokenResponseType(); + } + } + + $config = array_intersect_key($this->config, array_flip(explode(' ', 'allow_implicit enforce_state require_exact_redirect_uri'))); + + if ($this->config['use_openid_connect']) { + return new OpenIDAuthorizeController($this->storages['client'], $this->responseTypes, $config, $this->getScopeUtil()); + } + + return new AuthorizeController($this->storages['client'], $this->responseTypes, $config, $this->getScopeUtil()); + } + + /** + * @return TokenControllerInterface + * @throws LogicException + */ + protected function createDefaultTokenController() + { + if (0 == count($this->grantTypes)) { + $this->grantTypes = $this->getDefaultGrantTypes(); + } + + if (is_null($this->clientAssertionType)) { + // see if HttpBasic assertion type is requred. If so, then create it from storage classes. + foreach ($this->grantTypes as $grantType) { + if (!$grantType instanceof ClientAssertionTypeInterface) { + if (!isset($this->storages['client_credentials'])) { + throw new \LogicException('You must supply a storage object implementing OAuth2\Storage\ClientCredentialsInterface to use the token server'); + } + $config = array_intersect_key($this->config, array_flip(explode(' ', 'allow_credentials_in_request_body allow_public_clients'))); + $this->clientAssertionType = new HttpBasic($this->storages['client_credentials'], $config); + break; + } + } + } + + if (!isset($this->storages['client'])) { + throw new LogicException("You must supply a storage object implementing OAuth2\Storage\ClientInterface to use the token server"); + } + + $accessTokenResponseType = $this->getAccessTokenResponseType(); + + return new TokenController($accessTokenResponseType, $this->storages['client'], $this->grantTypes, $this->clientAssertionType, $this->getScopeUtil()); + } + + /** + * @return ResourceControllerInterface + * @throws LogicException + */ + protected function createDefaultResourceController() + { + if ($this->config['use_jwt_access_tokens']) { + // overwrites access token storage with crypto token storage if "use_jwt_access_tokens" is set + if (!isset($this->storages['access_token']) || !$this->storages['access_token'] instanceof JwtAccessTokenInterface) { + $this->storages['access_token'] = $this->createDefaultJwtAccessTokenStorage(); + } + } elseif (!isset($this->storages['access_token'])) { + throw new \LogicException('You must supply a storage object implementing OAuth2\Storage\AccessTokenInterface or use JwtAccessTokens to use the resource server'); + } + + if (!$this->tokenType) { + $this->tokenType = $this->getDefaultTokenType(); + } + + $config = array_intersect_key($this->config, array('www_realm' => '')); + + return new ResourceController($this->tokenType, $this->storages['access_token'], $config, $this->getScopeUtil()); + } + + /** + * @return UserInfoControllerInterface + * @throws LogicException + */ + protected function createDefaultUserInfoController() + { + if ($this->config['use_jwt_access_tokens']) { + // overwrites access token storage with crypto token storage if "use_jwt_access_tokens" is set + if (!isset($this->storages['access_token']) || !$this->storages['access_token'] instanceof JwtAccessTokenInterface) { + $this->storages['access_token'] = $this->createDefaultJwtAccessTokenStorage(); + } + } elseif (!isset($this->storages['access_token'])) { + throw new \LogicException('You must supply a storage object implementing OAuth2\Storage\AccessTokenInterface or use JwtAccessTokens to use the UserInfo server'); + } + + if (!isset($this->storages['user_claims'])) { + throw new \LogicException('You must supply a storage object implementing OAuth2\OpenID\Storage\UserClaimsInterface to use the UserInfo server'); + } + + if (!$this->tokenType) { + $this->tokenType = $this->getDefaultTokenType(); + } + + $config = array_intersect_key($this->config, array('www_realm' => '')); + + return new UserInfoController($this->tokenType, $this->storages['access_token'], $this->storages['user_claims'], $config, $this->getScopeUtil()); + } + + /** + * @return Bearer + */ + protected function getDefaultTokenType() + { + $config = array_intersect_key($this->config, array_flip(explode(' ', 'token_param_name token_bearer_header_name'))); + + return new Bearer($config); + } + + /** + * @return array + * @throws LogicException + */ + protected function getDefaultResponseTypes() + { + $responseTypes = array(); + + if ($this->config['allow_implicit']) { + $responseTypes['token'] = $this->getAccessTokenResponseType(); + } + + if ($this->config['use_openid_connect']) { + $responseTypes['id_token'] = $this->getIdTokenResponseType(); + if ($this->config['allow_implicit']) { + $responseTypes['id_token token'] = $this->getIdTokenTokenResponseType(); + } + } + + if (isset($this->storages['authorization_code'])) { + $config = array_intersect_key($this->config, array_flip(explode(' ', 'enforce_redirect auth_code_lifetime'))); + if ($this->config['use_openid_connect']) { + if (!$this->storages['authorization_code'] instanceof OpenIDAuthorizationCodeInterface) { + throw new \LogicException('Your authorization_code storage must implement OAuth2\OpenID\Storage\AuthorizationCodeInterface to work when "use_openid_connect" is true'); + } + $responseTypes['code'] = new OpenIDAuthorizationCodeResponseType($this->storages['authorization_code'], $config); + $responseTypes['code id_token'] = new CodeIdToken($responseTypes['code'], $responseTypes['id_token']); + } else { + $responseTypes['code'] = new AuthorizationCodeResponseType($this->storages['authorization_code'], $config); + } + } + + if (count($responseTypes) == 0) { + throw new \LogicException('You must supply an array of response_types in the constructor or implement a OAuth2\Storage\AuthorizationCodeInterface storage object or set "allow_implicit" to true and implement a OAuth2\Storage\AccessTokenInterface storage object'); + } + + return $responseTypes; + } + + /** + * @return array + * @throws LogicException + */ + protected function getDefaultGrantTypes() + { + $grantTypes = array(); + + if (isset($this->storages['user_credentials'])) { + $grantTypes['password'] = new UserCredentials($this->storages['user_credentials']); + } + + if (isset($this->storages['client_credentials'])) { + $config = array_intersect_key($this->config, array('allow_credentials_in_request_body' => '')); + $grantTypes['client_credentials'] = new ClientCredentials($this->storages['client_credentials'], $config); + } + + if (isset($this->storages['refresh_token'])) { + $config = array_intersect_key($this->config, array_flip(explode(' ', 'always_issue_new_refresh_token unset_refresh_token_after_use'))); + $grantTypes['refresh_token'] = new RefreshToken($this->storages['refresh_token'], $config); + } + + if (isset($this->storages['authorization_code'])) { + if ($this->config['use_openid_connect']) { + if (!$this->storages['authorization_code'] instanceof OpenIDAuthorizationCodeInterface) { + throw new \LogicException('Your authorization_code storage must implement OAuth2\OpenID\Storage\AuthorizationCodeInterface to work when "use_openid_connect" is true'); + } + $grantTypes['authorization_code'] = new OpenIDAuthorizationCodeGrantType($this->storages['authorization_code']); + } else { + $grantTypes['authorization_code'] = new AuthorizationCode($this->storages['authorization_code']); + } + } + + if (count($grantTypes) == 0) { + throw new \LogicException('Unable to build default grant types - You must supply an array of grant_types in the constructor'); + } + + return $grantTypes; + } + + /** + * @return AccessToken + */ + protected function getAccessTokenResponseType() + { + if (isset($this->responseTypes['token'])) { + return $this->responseTypes['token']; + } + + if ($this->config['use_jwt_access_tokens']) { + return $this->createDefaultJwtAccessTokenResponseType(); + } + + return $this->createDefaultAccessTokenResponseType(); + } + + /** + * @return IdToken + */ + protected function getIdTokenResponseType() + { + if (isset($this->responseTypes['id_token'])) { + return $this->responseTypes['id_token']; + } + + return $this->createDefaultIdTokenResponseType(); + } + + /** + * @return IdTokenToken + */ + protected function getIdTokenTokenResponseType() + { + if (isset($this->responseTypes['id_token token'])) { + return $this->responseTypes['id_token token']; + } + + return $this->createDefaultIdTokenTokenResponseType(); + } + + /** + * For Resource Controller + * + * @return JwtAccessTokenStorage + * @throws LogicException + */ + protected function createDefaultJwtAccessTokenStorage() + { + if (!isset($this->storages['public_key'])) { + throw new \LogicException('You must supply a storage object implementing OAuth2\Storage\PublicKeyInterface to use crypto tokens'); + } + $tokenStorage = null; + if (!empty($this->config['store_encrypted_token_string']) && isset($this->storages['access_token'])) { + $tokenStorage = $this->storages['access_token']; + } + // wrap the access token storage as required. + return new JwtAccessTokenStorage($this->storages['public_key'], $tokenStorage); + } + + /** + * For Authorize and Token Controllers + * + * @return JwtAccessToken + * @throws LogicException + */ + protected function createDefaultJwtAccessTokenResponseType() + { + if (!isset($this->storages['public_key'])) { + throw new \LogicException('You must supply a storage object implementing OAuth2\Storage\PublicKeyInterface to use crypto tokens'); + } + + $tokenStorage = null; + if (isset($this->storages['access_token'])) { + $tokenStorage = $this->storages['access_token']; + } + + $refreshStorage = null; + if (isset($this->storages['refresh_token'])) { + $refreshStorage = $this->storages['refresh_token']; + } + + $config = array_intersect_key($this->config, array_flip(explode(' ', 'store_encrypted_token_string issuer access_lifetime refresh_token_lifetime jwt_extra_payload_callable'))); + + return new JwtAccessToken($this->storages['public_key'], $tokenStorage, $refreshStorage, $config); + } + + /** + * @return AccessToken + * @throws LogicException + */ + protected function createDefaultAccessTokenResponseType() + { + if (!isset($this->storages['access_token'])) { + throw new LogicException("You must supply a response type implementing OAuth2\ResponseType\AccessTokenInterface, or a storage object implementing OAuth2\Storage\AccessTokenInterface to use the token server"); + } + + $refreshStorage = null; + if (isset($this->storages['refresh_token'])) { + $refreshStorage = $this->storages['refresh_token']; + } + + $config = array_intersect_key($this->config, array_flip(explode(' ', 'access_lifetime refresh_token_lifetime'))); + $config['token_type'] = $this->tokenType ? $this->tokenType->getTokenType() : $this->getDefaultTokenType()->getTokenType(); + + return new AccessToken($this->storages['access_token'], $refreshStorage, $config); + } + + /** + * @return IdToken + * @throws LogicException + */ + protected function createDefaultIdTokenResponseType() + { + if (!isset($this->storages['user_claims'])) { + throw new LogicException("You must supply a storage object implementing OAuth2\OpenID\Storage\UserClaimsInterface to use openid connect"); + } + if (!isset($this->storages['public_key'])) { + throw new LogicException("You must supply a storage object implementing OAuth2\Storage\PublicKeyInterface to use openid connect"); + } + + $config = array_intersect_key($this->config, array_flip(explode(' ', 'issuer id_lifetime'))); + + return new IdToken($this->storages['user_claims'], $this->storages['public_key'], $config); + } + + /** + * @return IdTokenToken + */ + protected function createDefaultIdTokenTokenResponseType() + { + return new IdTokenToken($this->getAccessTokenResponseType(), $this->getIdTokenResponseType()); + } + + /** + * @throws InvalidArgumentException + */ + protected function validateOpenIdConnect() + { + $authCodeGrant = $this->getGrantType('authorization_code'); + if (!empty($authCodeGrant) && !$authCodeGrant instanceof OpenIDAuthorizationCodeGrantType) { + throw new \InvalidArgumentException('You have enabled OpenID Connect, but supplied a grant type that does not support it.'); + } + } + + /** + * @param string $name + * @return string + */ + protected function normalizeResponseType($name) + { + // for multiple-valued response types - make them alphabetical + if (!empty($name) && false !== strpos($name, ' ')) { + $types = explode(' ', $name); + sort($types); + $name = implode(' ', $types); + } + + return $name; + } + + /** + * @return mixed + */ + public function getResponse() + { + return $this->response; + } + + /** + * @return array + */ + public function getStorages() + { + return $this->storages; + } + + /** + * @param string $name + * @return object|null + */ + public function getStorage($name) + { + return isset($this->storages[$name]) ? $this->storages[$name] : null; + } + + /** + * @return array + */ + public function getGrantTypes() + { + return $this->grantTypes; + } + + /** + * @param string $name + * @return object|null + */ + public function getGrantType($name) + { + return isset($this->grantTypes[$name]) ? $this->grantTypes[$name] : null; + } + + /** + * @return array + */ + public function getResponseTypes() + { + return $this->responseTypes; + } + + /** + * @param string $name + * @return object|null + */ + public function getResponseType($name) + { + // for multiple-valued response types - make them alphabetical + $name = $this->normalizeResponseType($name); + + return isset($this->responseTypes[$name]) ? $this->responseTypes[$name] : null; + } + + /** + * @return TokenTypeInterface + */ + public function getTokenType() + { + return $this->tokenType; + } + + /** + * @return ClientAssertionTypeInterface + */ + public function getClientAssertionType() + { + return $this->clientAssertionType; + } + + /** + * @param string $name + * @param mixed $value + */ + public function setConfig($name, $value) + { + $this->config[$name] = $value; + } + + /** + * @param string $name + * @param mixed $default + * @return mixed + */ + public function getConfig($name, $default = null) + { + return isset($this->config[$name]) ? $this->config[$name] : $default; + } +} diff --git a/modules/yandexhome/lib/OAuth2/Storage/AccessTokenInterface.php b/modules/yandexhome/lib/OAuth2/Storage/AccessTokenInterface.php new file mode 100644 index 0000000..22428f2 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Storage/AccessTokenInterface.php @@ -0,0 +1,65 @@ + + */ +interface AccessTokenInterface +{ + /** + * Look up the supplied oauth_token from storage. + * + * We need to retrieve access token data as we create and verify tokens. + * + * @param string $oauth_token - oauth_token to be check with. + * + * @return array|null - An associative array as below, and return NULL if the supplied oauth_token is invalid: + * @code + * array( + * 'expires' => $expires, // Stored expiration in unix timestamp. + * 'client_id' => $client_id, // (optional) Stored client identifier. + * 'user_id' => $user_id, // (optional) Stored user identifier. + * 'scope' => $scope, // (optional) Stored scope values in space-separated string. + * 'id_token' => $id_token // (optional) Stored id_token (if "use_openid_connect" is true). + * ); + * @endcode + * + * @ingroup oauth2_section_7 + */ + public function getAccessToken($oauth_token); + + /** + * Store the supplied access token values to storage. + * + * We need to store access token data as we create and verify tokens. + * + * @param string $oauth_token - oauth_token to be stored. + * @param mixed $client_id - client identifier to be stored. + * @param mixed $user_id - user identifier to be stored. + * @param int $expires - expiration to be stored as a Unix timestamp. + * @param string $scope - OPTIONAL Scopes to be stored in space-separated string. + * + * @ingroup oauth2_section_4 + */ + public function setAccessToken($oauth_token, $client_id, $user_id, $expires, $scope = null); + + /** + * Expire an access token. + * + * This is not explicitly required in the spec, but if defined in a draft RFC for token + * revoking (RFC 7009) https://tools.ietf.org/html/rfc7009 + * + * @param $access_token + * Access token to be expired. + * + * @return BOOL true if an access token was unset, false if not + * @ingroup oauth2_section_6 + * + * @todo v2.0 include this method in interface. Omitted to maintain BC in v1.x + */ + //public function unsetAccessToken($access_token); +} \ No newline at end of file diff --git a/modules/yandexhome/lib/OAuth2/Storage/AuthorizationCodeInterface.php b/modules/yandexhome/lib/OAuth2/Storage/AuthorizationCodeInterface.php new file mode 100644 index 0000000..2dbc138 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Storage/AuthorizationCodeInterface.php @@ -0,0 +1,86 @@ + + */ +interface AuthorizationCodeInterface +{ + /** + * The Authorization Code grant type supports a response type of "code". + * + * @var string + * @see http://tools.ietf.org/html/rfc6749#section-1.4.1 + * @see http://tools.ietf.org/html/rfc6749#section-4.2 + */ + const RESPONSE_TYPE_CODE = "code"; + + /** + * Fetch authorization code data (probably the most common grant type). + * + * Retrieve the stored data for the given authorization code. + * + * Required for OAuth2::GRANT_TYPE_AUTH_CODE. + * + * @param $code + * Authorization code to be check with. + * + * @return + * An associative array as below, and NULL if the code is invalid + * @code + * return array( + * "client_id" => CLIENT_ID, // REQUIRED Stored client identifier + * "user_id" => USER_ID, // REQUIRED Stored user identifier + * "expires" => EXPIRES, // REQUIRED Stored expiration in unix timestamp + * "redirect_uri" => REDIRECT_URI, // REQUIRED Stored redirect URI + * "scope" => SCOPE, // OPTIONAL Stored scope values in space-separated string + * ); + * @endcode + * + * @see http://tools.ietf.org/html/rfc6749#section-4.1 + * + * @ingroup oauth2_section_4 + */ + public function getAuthorizationCode($code); + + /** + * Take the provided authorization code values and store them somewhere. + * + * This function should be the storage counterpart to getAuthCode(). + * + * If storage fails for some reason, we're not currently checking for + * any sort of success/failure, so you should bail out of the script + * and provide a descriptive fail message. + * + * Required for OAuth2::GRANT_TYPE_AUTH_CODE. + * + * @param string $code - Authorization code to be stored. + * @param mixed $client_id - Client identifier to be stored. + * @param mixed $user_id - User identifier to be stored. + * @param string $redirect_uri - Redirect URI(s) to be stored in a space-separated string. + * @param int $expires - Expiration to be stored as a Unix timestamp. + * @param string $scope - OPTIONAL Scopes to be stored in space-separated string. + * + * @ingroup oauth2_section_4 + */ + public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null); + + /** + * once an Authorization Code is used, it must be expired + * + * @see http://tools.ietf.org/html/rfc6749#section-4.1.2 + * + * The client MUST NOT use the authorization code + * more than once. If an authorization code is used more than + * once, the authorization server MUST deny the request and SHOULD + * revoke (when possible) all tokens previously issued based on + * that authorization code + * + */ + public function expireAuthorizationCode($code); +} diff --git a/modules/yandexhome/lib/OAuth2/Storage/Cassandra.php b/modules/yandexhome/lib/OAuth2/Storage/Cassandra.php new file mode 100644 index 0000000..e60e9d3 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Storage/Cassandra.php @@ -0,0 +1,660 @@ + + * composer require thobbs/phpcassa:dev-master + * + * + * Once this is done, instantiate the connection: + * + * $cassandra = new \phpcassa\Connection\ConnectionPool('oauth2_server', array('127.0.0.1:9160')); + * + * + * Then, register the storage client: + * + * $storage = new OAuth2\Storage\Cassandra($cassandra); + * $storage->setClientDetails($client_id, $client_secret, $redirect_uri); + * + * + * @see test/lib/OAuth2/Storage/Bootstrap::getCassandraStorage + */ +class Cassandra implements AuthorizationCodeInterface, + AccessTokenInterface, + ClientCredentialsInterface, + UserCredentialsInterface, + RefreshTokenInterface, + JwtBearerInterface, + ScopeInterface, + PublicKeyInterface, + UserClaimsInterface, + OpenIDAuthorizationCodeInterface +{ + + private $cache; + + /** + * @var ConnectionPool + */ + protected $cassandra; + + /** + * @var array + */ + protected $config; + + /** + * Cassandra Storage! uses phpCassa + * + * @param ConnectionPool|array $connection + * @param array $config + * + * @throws InvalidArgumentException + */ + public function __construct($connection = array(), array $config = array()) + { + if ($connection instanceof ConnectionPool) { + $this->cassandra = $connection; + } else { + if (!is_array($connection)) { + throw new InvalidArgumentException('First argument to OAuth2\Storage\Cassandra must be an instance of phpcassa\Connection\ConnectionPool or a configuration array'); + } + $connection = array_merge(array( + 'keyspace' => 'oauth2', + 'servers' => null, + ), $connection); + + $this->cassandra = new ConnectionPool($connection['keyspace'], $connection['servers']); + } + + $this->config = array_merge(array( + // cassandra config + 'column_family' => 'auth', + + // key names + 'client_key' => 'oauth_clients:', + 'access_token_key' => 'oauth_access_tokens:', + 'refresh_token_key' => 'oauth_refresh_tokens:', + 'code_key' => 'oauth_authorization_codes:', + 'user_key' => 'oauth_users:', + 'jwt_key' => 'oauth_jwt:', + 'scope_key' => 'oauth_scopes:', + 'public_key_key' => 'oauth_public_keys:', + ), $config); + } + + /** + * @param $key + * @return bool|mixed + */ + protected function getValue($key) + { + if (isset($this->cache[$key])) { + return $this->cache[$key]; + } + $cf = new ColumnFamily($this->cassandra, $this->config['column_family']); + + try { + $value = $cf->get($key, new ColumnSlice("", "")); + $value = array_shift($value); + } catch (\cassandra\NotFoundException $e) { + return false; + } + + return json_decode($value, true); + } + + /** + * @param $key + * @param $value + * @param int $expire + * @return bool + */ + protected function setValue($key, $value, $expire = 0) + { + $this->cache[$key] = $value; + + $cf = new ColumnFamily($this->cassandra, $this->config['column_family']); + + $str = json_encode($value); + if ($expire > 0) { + try { + $seconds = $expire - time(); + // __data key set as C* requires a field, note: max TTL can only be 630720000 seconds + $cf->insert($key, array('__data' => $str), null, $seconds); + } catch (\Exception $e) { + return false; + } + } else { + try { + // __data key set as C* requires a field + $cf->insert($key, array('__data' => $str)); + } catch (\Exception $e) { + return false; + } + } + + return true; + } + + /** + * @param $key + * @return bool + */ + protected function expireValue($key) + { + unset($this->cache[$key]); + + $cf = new ColumnFamily($this->cassandra, $this->config['column_family']); + + if ($cf->get_count($key) > 0) { + try { + // __data key set as C* requires a field + $cf->remove($key, array('__data')); + } catch (\Exception $e) { + return false; + } + + return true; + } + + return false; + } + + /** + * @param string $code + * @return bool|mixed + */ + public function getAuthorizationCode($code) + { + return $this->getValue($this->config['code_key'] . $code); + } + + /** + * @param string $authorization_code + * @param mixed $client_id + * @param mixed $user_id + * @param string $redirect_uri + * @param int $expires + * @param string $scope + * @param string $id_token + * @return bool + */ + public function setAuthorizationCode($authorization_code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) + { + return $this->setValue( + $this->config['code_key'] . $authorization_code, + compact('authorization_code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token'), + $expires + ); + } + + /** + * @param string $code + * @return bool + */ + public function expireAuthorizationCode($code) + { + $key = $this->config['code_key'] . $code; + unset($this->cache[$key]); + + return $this->expireValue($key); + } + + /** + * @param string $username + * @param string $password + * @return bool + */ + public function checkUserCredentials($username, $password) + { + if ($user = $this->getUser($username)) { + return $this->checkPassword($user, $password); + } + + return false; + } + + /** + * plaintext passwords are bad! Override this for your application + * + * @param array $user + * @param string $password + * @return bool + */ + protected function checkPassword($user, $password) + { + return $user['password'] == $this->hashPassword($password); + } + + // use a secure hashing algorithm when storing passwords. Override this for your application + protected function hashPassword($password) + { + return sha1($password); + } + + /** + * @param string $username + * @return array|bool|false + */ + public function getUserDetails($username) + { + return $this->getUser($username); + } + + /** + * @param string $username + * @return array|bool + */ + public function getUser($username) + { + if (!$userInfo = $this->getValue($this->config['user_key'] . $username)) { + return false; + } + + // the default behavior is to use "username" as the user_id + return array_merge(array( + 'user_id' => $username, + ), $userInfo); + } + + /** + * @param string $username + * @param string $password + * @param string $first_name + * @param string $last_name + * @return bool + */ + public function setUser($username, $password, $first_name = null, $last_name = null) + { + $password = $this->hashPassword($password); + + return $this->setValue( + $this->config['user_key'] . $username, + compact('username', 'password', 'first_name', 'last_name') + ); + } + + /** + * @param mixed $client_id + * @param string $client_secret + * @return bool + */ + public function checkClientCredentials($client_id, $client_secret = null) + { + if (!$client = $this->getClientDetails($client_id)) { + return false; + } + + return isset($client['client_secret']) + && $client['client_secret'] == $client_secret; + } + + /** + * @param $client_id + * @return bool + */ + public function isPublicClient($client_id) + { + if (!$client = $this->getClientDetails($client_id)) { + return false; + } + + return empty($client['client_secret']); + } + + /** + * @param $client_id + * @return array|bool|mixed + */ + public function getClientDetails($client_id) + { + return $this->getValue($this->config['client_key'] . $client_id); + } + + /** + * @param $client_id + * @param null $client_secret + * @param null $redirect_uri + * @param null $grant_types + * @param null $scope + * @param null $user_id + * @return bool + */ + public function setClientDetails($client_id, $client_secret = null, $redirect_uri = null, $grant_types = null, $scope = null, $user_id = null) + { + return $this->setValue( + $this->config['client_key'] . $client_id, + compact('client_id', 'client_secret', 'redirect_uri', 'grant_types', 'scope', 'user_id') + ); + } + + /** + * @param $client_id + * @param $grant_type + * @return bool + */ + public function checkRestrictedGrantType($client_id, $grant_type) + { + $details = $this->getClientDetails($client_id); + if (isset($details['grant_types'])) { + $grant_types = explode(' ', $details['grant_types']); + + return in_array($grant_type, (array) $grant_types); + } + + // if grant_types are not defined, then none are restricted + return true; + } + + /** + * @param $refresh_token + * @return bool|mixed + */ + public function getRefreshToken($refresh_token) + { + return $this->getValue($this->config['refresh_token_key'] . $refresh_token); + } + + /** + * @param $refresh_token + * @param $client_id + * @param $user_id + * @param $expires + * @param null $scope + * @return bool + */ + public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null) + { + return $this->setValue( + $this->config['refresh_token_key'] . $refresh_token, + compact('refresh_token', 'client_id', 'user_id', 'expires', 'scope'), + $expires + ); + } + + /** + * @param $refresh_token + * @return bool + */ + public function unsetRefreshToken($refresh_token) + { + return $this->expireValue($this->config['refresh_token_key'] . $refresh_token); + } + + /** + * @param string $access_token + * @return array|bool|mixed|null + */ + public function getAccessToken($access_token) + { + return $this->getValue($this->config['access_token_key'].$access_token); + } + + /** + * @param string $access_token + * @param mixed $client_id + * @param mixed $user_id + * @param int $expires + * @param null $scope + * @return bool + */ + public function setAccessToken($access_token, $client_id, $user_id, $expires, $scope = null) + { + return $this->setValue( + $this->config['access_token_key'].$access_token, + compact('access_token', 'client_id', 'user_id', 'expires', 'scope'), + $expires + ); + } + + /** + * @param $access_token + * @return bool + */ + public function unsetAccessToken($access_token) + { + return $this->expireValue($this->config['access_token_key'] . $access_token); + } + + /** + * @param $scope + * @return bool + */ + public function scopeExists($scope) + { + $scope = explode(' ', $scope); + + $result = $this->getValue($this->config['scope_key'].'supported:global'); + + $supportedScope = explode(' ', (string) $result); + + return (count(array_diff($scope, $supportedScope)) == 0); + } + + /** + * @param null $client_id + * @return bool|mixed + */ + public function getDefaultScope($client_id = null) + { + if (is_null($client_id) || !$result = $this->getValue($this->config['scope_key'].'default:'.$client_id)) { + $result = $this->getValue($this->config['scope_key'].'default:global'); + } + + return $result; + } + + /** + * @param $scope + * @param null $client_id + * @param string $type + * @return bool + * @throws \InvalidArgumentException + */ + public function setScope($scope, $client_id = null, $type = 'supported') + { + if (!in_array($type, array('default', 'supported'))) { + throw new \InvalidArgumentException('"$type" must be one of "default", "supported"'); + } + + if (is_null($client_id)) { + $key = $this->config['scope_key'].$type.':global'; + } else { + $key = $this->config['scope_key'].$type.':'.$client_id; + } + + return $this->setValue($key, $scope); + } + + /** + * @param $client_id + * @param $subject + * @return bool|null + */ + public function getClientKey($client_id, $subject) + { + if (!$jwt = $this->getValue($this->config['jwt_key'] . $client_id)) { + return false; + } + + if (isset($jwt['subject']) && $jwt['subject'] == $subject ) { + return $jwt['key']; + } + + return null; + } + + /** + * @param $client_id + * @param $key + * @param null $subject + * @return bool + */ + public function setClientKey($client_id, $key, $subject = null) + { + return $this->setValue($this->config['jwt_key'] . $client_id, array( + 'key' => $key, + 'subject' => $subject + )); + } + + /** + * @param $client_id + * @return bool|null + */ + public function getClientScope($client_id) + { + if (!$clientDetails = $this->getClientDetails($client_id)) { + return false; + } + + if (isset($clientDetails['scope'])) { + return $clientDetails['scope']; + } + + return null; + } + + /** + * @param $client_id + * @param $subject + * @param $audience + * @param $expiration + * @param $jti + * @throws \Exception + */ + public function getJti($client_id, $subject, $audience, $expiration, $jti) + { + //TODO: Needs cassandra implementation. + throw new \Exception('getJti() for the Cassandra driver is currently unimplemented.'); + } + + /** + * @param $client_id + * @param $subject + * @param $audience + * @param $expiration + * @param $jti + * @throws \Exception + */ + public function setJti($client_id, $subject, $audience, $expiration, $jti) + { + //TODO: Needs cassandra implementation. + throw new \Exception('setJti() for the Cassandra driver is currently unimplemented.'); + } + + /** + * @param string $client_id + * @return mixed + */ + public function getPublicKey($client_id = '') + { + $public_key = $this->getValue($this->config['public_key_key'] . $client_id); + if (is_array($public_key)) { + return $public_key['public_key']; + } + $public_key = $this->getValue($this->config['public_key_key']); + if (is_array($public_key)) { + return $public_key['public_key']; + } + } + + /** + * @param string $client_id + * @return mixed + */ + public function getPrivateKey($client_id = '') + { + $public_key = $this->getValue($this->config['public_key_key'] . $client_id); + if (is_array($public_key)) { + return $public_key['private_key']; + } + $public_key = $this->getValue($this->config['public_key_key']); + if (is_array($public_key)) { + return $public_key['private_key']; + } + } + + /** + * @param null $client_id + * @return mixed|string + */ + public function getEncryptionAlgorithm($client_id = null) + { + $public_key = $this->getValue($this->config['public_key_key'] . $client_id); + if (is_array($public_key)) { + return $public_key['encryption_algorithm']; + } + $public_key = $this->getValue($this->config['public_key_key']); + if (is_array($public_key)) { + return $public_key['encryption_algorithm']; + } + + return 'RS256'; + } + + /** + * @param mixed $user_id + * @param string $claims + * @return array|bool + */ + public function getUserClaims($user_id, $claims) + { + $userDetails = $this->getUserDetails($user_id); + if (!is_array($userDetails)) { + return false; + } + + $claims = explode(' ', trim($claims)); + $userClaims = array(); + + // for each requested claim, if the user has the claim, set it in the response + $validClaims = explode(' ', self::VALID_CLAIMS); + foreach ($validClaims as $validClaim) { + if (in_array($validClaim, $claims)) { + if ($validClaim == 'address') { + // address is an object with subfields + $userClaims['address'] = $this->getUserClaim($validClaim, $userDetails['address'] ?: $userDetails); + } else { + $userClaims = array_merge($userClaims, $this->getUserClaim($validClaim, $userDetails)); + } + } + } + + return $userClaims; + } + + /** + * @param $claim + * @param $userDetails + * @return array + */ + protected function getUserClaim($claim, $userDetails) + { + $userClaims = array(); + $claimValuesString = constant(sprintf('self::%s_CLAIM_VALUES', strtoupper($claim))); + $claimValues = explode(' ', $claimValuesString); + + foreach ($claimValues as $value) { + if ($value == 'email_verified') { + $userClaims[$value] = $userDetails[$value]=='true' ? true : false; + } else { + $userClaims[$value] = isset($userDetails[$value]) ? $userDetails[$value] : null; + } + } + + return $userClaims; + } +} \ No newline at end of file diff --git a/modules/yandexhome/lib/OAuth2/Storage/ClientCredentialsInterface.php b/modules/yandexhome/lib/OAuth2/Storage/ClientCredentialsInterface.php new file mode 100644 index 0000000..3318c69 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Storage/ClientCredentialsInterface.php @@ -0,0 +1,49 @@ + + */ +interface ClientCredentialsInterface extends ClientInterface +{ + + /** + * Make sure that the client credentials is valid. + * + * @param $client_id + * Client identifier to be check with. + * @param $client_secret + * (optional) If a secret is required, check that they've given the right one. + * + * @return + * TRUE if the client credentials are valid, and MUST return FALSE if it isn't. + * @endcode + * + * @see http://tools.ietf.org/html/rfc6749#section-3.1 + * + * @ingroup oauth2_section_3 + */ + public function checkClientCredentials($client_id, $client_secret = null); + + /** + * Determine if the client is a "public" client, and therefore + * does not require passing credentials for certain grant types + * + * @param $client_id + * Client identifier to be check with. + * + * @return + * TRUE if the client is public, and FALSE if it isn't. + * @endcode + * + * @see http://tools.ietf.org/html/rfc6749#section-2.3 + * @see https://github.com/bshaffer/oauth2-server-php/issues/257 + * + * @ingroup oauth2_section_2 + */ + public function isPublicClient($client_id); +} diff --git a/modules/yandexhome/lib/OAuth2/Storage/ClientInterface.php b/modules/yandexhome/lib/OAuth2/Storage/ClientInterface.php new file mode 100644 index 0000000..09a5bff --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Storage/ClientInterface.php @@ -0,0 +1,66 @@ + + */ +interface ClientInterface +{ + /** + * Get client details corresponding client_id. + * + * OAuth says we should store request URIs for each registered client. + * Implement this function to grab the stored URI for a given client id. + * + * @param $client_id + * Client identifier to be check with. + * + * @return array + * Client details. The only mandatory key in the array is "redirect_uri". + * This function MUST return FALSE if the given client does not exist or is + * invalid. "redirect_uri" can be space-delimited to allow for multiple valid uris. + * + * return array( + * "redirect_uri" => REDIRECT_URI, // REQUIRED redirect_uri registered for the client + * "client_id" => CLIENT_ID, // OPTIONAL the client id + * "grant_types" => GRANT_TYPES, // OPTIONAL an array of restricted grant types + * "user_id" => USER_ID, // OPTIONAL the user identifier associated with this client + * "scope" => SCOPE, // OPTIONAL the scopes allowed for this client + * ); + * + * + * @ingroup oauth2_section_4 + */ + public function getClientDetails($client_id); + + /** + * Get the scope associated with this client + * + * @return + * STRING the space-delineated scope list for the specified client_id + */ + public function getClientScope($client_id); + + /** + * Check restricted grant types of corresponding client identifier. + * + * If you want to restrict clients to certain grant types, override this + * function. + * + * @param $client_id + * Client identifier to be check with. + * @param $grant_type + * Grant type to be check with + * + * @return + * TRUE if the grant type is supported by this client identifier, and + * FALSE if it isn't. + * + * @ingroup oauth2_section_4 + */ + public function checkRestrictedGrantType($client_id, $grant_type); +} diff --git a/modules/yandexhome/lib/OAuth2/Storage/CouchbaseDB.php b/modules/yandexhome/lib/OAuth2/Storage/CouchbaseDB.php new file mode 100644 index 0000000..9e8148b --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Storage/CouchbaseDB.php @@ -0,0 +1,331 @@ + + */ +class CouchbaseDB implements AuthorizationCodeInterface, + AccessTokenInterface, + ClientCredentialsInterface, + UserCredentialsInterface, + RefreshTokenInterface, + JwtBearerInterface, + OpenIDAuthorizationCodeInterface +{ + protected $db; + protected $config; + + public function __construct($connection, $config = array()) + { + if ($connection instanceof \Couchbase) { + $this->db = $connection; + } else { + if (!is_array($connection) || !is_array($connection['servers'])) { + throw new \InvalidArgumentException('First argument to OAuth2\Storage\CouchbaseDB must be an instance of Couchbase or a configuration array containing a server array'); + } + + $this->db = new \Couchbase($connection['servers'], (!isset($connection['username'])) ? '' : $connection['username'], (!isset($connection['password'])) ? '' : $connection['password'], $connection['bucket'], false); + } + + $this->config = array_merge(array( + 'client_table' => 'oauth_clients', + 'access_token_table' => 'oauth_access_tokens', + 'refresh_token_table' => 'oauth_refresh_tokens', + 'code_table' => 'oauth_authorization_codes', + 'user_table' => 'oauth_users', + 'jwt_table' => 'oauth_jwt', + ), $config); + } + + // Helper function to access couchbase item by type: + protected function getObjectByType($name,$id) + { + return json_decode($this->db->get($this->config[$name].'-'.$id),true); + } + + // Helper function to set couchbase item by type: + protected function setObjectByType($name,$id,$array) + { + $array['type'] = $name; + + return $this->db->set($this->config[$name].'-'.$id,json_encode($array)); + } + + // Helper function to delete couchbase item by type, wait for persist to at least 1 node + protected function deleteObjectByType($name,$id) + { + $this->db->delete($this->config[$name].'-'.$id,"",1); + } + + /* ClientCredentialsInterface */ + public function checkClientCredentials($client_id, $client_secret = null) + { + if ($result = $this->getObjectByType('client_table',$client_id)) { + return $result['client_secret'] == $client_secret; + } + + return false; + } + + public function isPublicClient($client_id) + { + if (!$result = $this->getObjectByType('client_table',$client_id)) { + return false; + } + + return empty($result['client_secret']); + } + + /* ClientInterface */ + public function getClientDetails($client_id) + { + $result = $this->getObjectByType('client_table',$client_id); + + return is_null($result) ? false : $result; + } + + public function setClientDetails($client_id, $client_secret = null, $redirect_uri = null, $grant_types = null, $scope = null, $user_id = null) + { + if ($this->getClientDetails($client_id)) { + + $this->setObjectByType('client_table',$client_id, array( + 'client_id' => $client_id, + 'client_secret' => $client_secret, + 'redirect_uri' => $redirect_uri, + 'grant_types' => $grant_types, + 'scope' => $scope, + 'user_id' => $user_id, + )); + } else { + $this->setObjectByType('client_table',$client_id, array( + 'client_id' => $client_id, + 'client_secret' => $client_secret, + 'redirect_uri' => $redirect_uri, + 'grant_types' => $grant_types, + 'scope' => $scope, + 'user_id' => $user_id, + )); + } + + return true; + } + + public function checkRestrictedGrantType($client_id, $grant_type) + { + $details = $this->getClientDetails($client_id); + if (isset($details['grant_types'])) { + $grant_types = explode(' ', $details['grant_types']); + + return in_array($grant_type, $grant_types); + } + + // if grant_types are not defined, then none are restricted + return true; + } + + /* AccessTokenInterface */ + public function getAccessToken($access_token) + { + $token = $this->getObjectByType('access_token_table',$access_token); + + return is_null($token) ? false : $token; + } + + public function setAccessToken($access_token, $client_id, $user_id, $expires, $scope = null) + { + // if it exists, update it. + if ($this->getAccessToken($access_token)) { + $this->setObjectByType('access_token_table',$access_token, array( + 'access_token' => $access_token, + 'client_id' => $client_id, + 'expires' => $expires, + 'user_id' => $user_id, + 'scope' => $scope + )); + } else { + $this->setObjectByType('access_token_table',$access_token, array( + 'access_token' => $access_token, + 'client_id' => $client_id, + 'expires' => $expires, + 'user_id' => $user_id, + 'scope' => $scope + )); + } + + return true; + } + + /* AuthorizationCodeInterface */ + public function getAuthorizationCode($code) + { + $code = $this->getObjectByType('code_table',$code); + + return is_null($code) ? false : $code; + } + + public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) + { + // if it exists, update it. + if ($this->getAuthorizationCode($code)) { + $this->setObjectByType('code_table',$code, array( + 'authorization_code' => $code, + 'client_id' => $client_id, + 'user_id' => $user_id, + 'redirect_uri' => $redirect_uri, + 'expires' => $expires, + 'scope' => $scope, + 'id_token' => $id_token, + )); + } else { + $this->setObjectByType('code_table',$code,array( + 'authorization_code' => $code, + 'client_id' => $client_id, + 'user_id' => $user_id, + 'redirect_uri' => $redirect_uri, + 'expires' => $expires, + 'scope' => $scope, + 'id_token' => $id_token, + )); + } + + return true; + } + + public function expireAuthorizationCode($code) + { + $this->deleteObjectByType('code_table',$code); + + return true; + } + + /* UserCredentialsInterface */ + public function checkUserCredentials($username, $password) + { + if ($user = $this->getUser($username)) { + return $this->checkPassword($user, $password); + } + + return false; + } + + public function getUserDetails($username) + { + if ($user = $this->getUser($username)) { + $user['user_id'] = $user['username']; + } + + return $user; + } + + /* RefreshTokenInterface */ + public function getRefreshToken($refresh_token) + { + $token = $this->getObjectByType('refresh_token_table',$refresh_token); + + return is_null($token) ? false : $token; + } + + public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null) + { + $this->setObjectByType('refresh_token_table',$refresh_token, array( + 'refresh_token' => $refresh_token, + 'client_id' => $client_id, + 'user_id' => $user_id, + 'expires' => $expires, + 'scope' => $scope + )); + + return true; + } + + public function unsetRefreshToken($refresh_token) + { + $this->deleteObjectByType('refresh_token_table',$refresh_token); + + return true; + } + + // plaintext passwords are bad! Override this for your application + protected function checkPassword($user, $password) + { + return $user['password'] == $password; + } + + public function getUser($username) + { + $result = $this->getObjectByType('user_table',$username); + + return is_null($result) ? false : $result; + } + + public function setUser($username, $password, $firstName = null, $lastName = null) + { + if ($this->getUser($username)) { + $this->setObjectByType('user_table',$username, array( + 'username' => $username, + 'password' => $password, + 'first_name' => $firstName, + 'last_name' => $lastName + )); + + } else { + $this->setObjectByType('user_table',$username, array( + 'username' => $username, + 'password' => $password, + 'first_name' => $firstName, + 'last_name' => $lastName + )); + + } + + return true; + } + + public function getClientKey($client_id, $subject) + { + if (!$jwt = $this->getObjectByType('jwt_table',$client_id)) { + return false; + } + + if (isset($jwt['subject']) && $jwt['subject'] == $subject) { + return $jwt['key']; + } + + return false; + } + + public function getClientScope($client_id) + { + if (!$clientDetails = $this->getClientDetails($client_id)) { + return false; + } + + if (isset($clientDetails['scope'])) { + return $clientDetails['scope']; + } + + return null; + } + + public function getJti($client_id, $subject, $audience, $expiration, $jti) + { + //TODO: Needs couchbase implementation. + throw new \Exception('getJti() for the Couchbase driver is currently unimplemented.'); + } + + public function setJti($client_id, $subject, $audience, $expiration, $jti) + { + //TODO: Needs couchbase implementation. + throw new \Exception('setJti() for the Couchbase driver is currently unimplemented.'); + } +} \ No newline at end of file diff --git a/modules/yandexhome/lib/OAuth2/Storage/DynamoDB.php b/modules/yandexhome/lib/OAuth2/Storage/DynamoDB.php new file mode 100644 index 0000000..a54cb37 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Storage/DynamoDB.php @@ -0,0 +1,540 @@ + + * composer require aws/aws-sdk-php:dev-master + * + * + * Once this is done, instantiate the DynamoDB client + * + * $storage = new OAuth2\Storage\Dynamodb(array("key" => "YOURKEY", "secret" => "YOURSECRET", "region" => "YOURREGION")); + * + * + * Table : + * - oauth_access_tokens (primary hash key : access_token) + * - oauth_authorization_codes (primary hash key : authorization_code) + * - oauth_clients (primary hash key : client_id) + * - oauth_jwt (primary hash key : client_id, primary range key : subject) + * - oauth_public_keys (primary hash key : client_id) + * - oauth_refresh_tokens (primary hash key : refresh_token) + * - oauth_scopes (primary hash key : scope, secondary index : is_default-index hash key is_default) + * - oauth_users (primary hash key : username) + * + * @author Frederic AUGUSTE + */ +class DynamoDB implements + AuthorizationCodeInterface, + AccessTokenInterface, + ClientCredentialsInterface, + UserCredentialsInterface, + RefreshTokenInterface, + JwtBearerInterface, + ScopeInterface, + PublicKeyInterface, + UserClaimsInterface, + OpenIDAuthorizationCodeInterface +{ + protected $client; + protected $config; + + public function __construct($connection, $config = array()) + { + if (!($connection instanceof DynamoDbClient)) { + if (!is_array($connection)) { + throw new \InvalidArgumentException('First argument to OAuth2\Storage\Dynamodb must be an instance a configuration array containt key, secret, region'); + } + if (!array_key_exists("key",$connection) || !array_key_exists("secret",$connection) || !array_key_exists("region",$connection) ) { + throw new \InvalidArgumentException('First argument to OAuth2\Storage\Dynamodb must be an instance a configuration array containt key, secret, region'); + } + $this->client = DynamoDbClient::factory(array( + 'key' => $connection["key"], + 'secret' => $connection["secret"], + 'region' =>$connection["region"] + )); + } else { + $this->client = $connection; + } + + $this->config = array_merge(array( + 'client_table' => 'oauth_clients', + 'access_token_table' => 'oauth_access_tokens', + 'refresh_token_table' => 'oauth_refresh_tokens', + 'code_table' => 'oauth_authorization_codes', + 'user_table' => 'oauth_users', + 'jwt_table' => 'oauth_jwt', + 'scope_table' => 'oauth_scopes', + 'public_key_table' => 'oauth_public_keys', + ), $config); + } + + /* OAuth2\Storage\ClientCredentialsInterface */ + public function checkClientCredentials($client_id, $client_secret = null) + { + $result = $this->client->getItem(array( + "TableName"=> $this->config['client_table'], + "Key" => array('client_id' => array('S' => $client_id)) + )); + + return $result->count()==1 && $result["Item"]["client_secret"]["S"] == $client_secret; + } + + public function isPublicClient($client_id) + { + $result = $this->client->getItem(array( + "TableName"=> $this->config['client_table'], + "Key" => array('client_id' => array('S' => $client_id)) + )); + + if ($result->count()==0) { + return false ; + } + + return empty($result["Item"]["client_secret"]); + } + + /* OAuth2\Storage\ClientInterface */ + public function getClientDetails($client_id) + { + $result = $this->client->getItem(array( + "TableName"=> $this->config['client_table'], + "Key" => array('client_id' => array('S' => $client_id)) + )); + if ($result->count()==0) { + return false ; + } + $result = $this->dynamo2array($result); + foreach (array('client_id', 'client_secret', 'redirect_uri', 'grant_types', 'scope', 'user_id') as $key => $val) { + if (!array_key_exists ($val, $result)) { + $result[$val] = null; + } + } + + return $result; + } + + public function setClientDetails($client_id, $client_secret = null, $redirect_uri = null, $grant_types = null, $scope = null, $user_id = null) + { + $clientData = compact('client_id', 'client_secret', 'redirect_uri', 'grant_types', 'scope', 'user_id'); + $clientData = array_filter($clientData, 'self::isNotEmpty'); + + $result = $this->client->putItem(array( + 'TableName' => $this->config['client_table'], + 'Item' => $this->client->formatAttributes($clientData) + )); + + return true; + } + + public function checkRestrictedGrantType($client_id, $grant_type) + { + $details = $this->getClientDetails($client_id); + if (isset($details['grant_types'])) { + $grant_types = explode(' ', $details['grant_types']); + + return in_array($grant_type, (array) $grant_types); + } + + // if grant_types are not defined, then none are restricted + return true; + } + + /* OAuth2\Storage\AccessTokenInterface */ + public function getAccessToken($access_token) + { + $result = $this->client->getItem(array( + "TableName"=> $this->config['access_token_table'], + "Key" => array('access_token' => array('S' => $access_token)) + )); + if ($result->count()==0) { + return false ; + } + $token = $this->dynamo2array($result); + if (array_key_exists ('expires', $token)) { + $token['expires'] = strtotime($token['expires']); + } + + return $token; + } + + public function setAccessToken($access_token, $client_id, $user_id, $expires, $scope = null) + { + // convert expires to datestring + $expires = date('Y-m-d H:i:s', $expires); + + $clientData = compact('access_token', 'client_id', 'user_id', 'expires', 'scope'); + $clientData = array_filter($clientData, 'self::isNotEmpty'); + + $result = $this->client->putItem(array( + 'TableName' => $this->config['access_token_table'], + 'Item' => $this->client->formatAttributes($clientData) + )); + + return true; + + } + + public function unsetAccessToken($access_token) + { + $result = $this->client->deleteItem(array( + 'TableName' => $this->config['access_token_table'], + 'Key' => $this->client->formatAttributes(array("access_token" => $access_token)), + 'ReturnValues' => 'ALL_OLD', + )); + + return null !== $result->get('Attributes'); + } + + /* OAuth2\Storage\AuthorizationCodeInterface */ + public function getAuthorizationCode($code) + { + $result = $this->client->getItem(array( + "TableName"=> $this->config['code_table'], + "Key" => array('authorization_code' => array('S' => $code)) + )); + if ($result->count()==0) { + return false ; + } + $token = $this->dynamo2array($result); + if (!array_key_exists("id_token", $token )) { + $token['id_token'] = null; + } + $token['expires'] = strtotime($token['expires']); + + return $token; + + } + + public function setAuthorizationCode($authorization_code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) + { + // convert expires to datestring + $expires = date('Y-m-d H:i:s', $expires); + + $clientData = compact('authorization_code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'id_token', 'scope'); + $clientData = array_filter($clientData, 'self::isNotEmpty'); + + $result = $this->client->putItem(array( + 'TableName' => $this->config['code_table'], + 'Item' => $this->client->formatAttributes($clientData) + )); + + return true; + } + + public function expireAuthorizationCode($code) + { + + $result = $this->client->deleteItem(array( + 'TableName' => $this->config['code_table'], + 'Key' => $this->client->formatAttributes(array("authorization_code" => $code)) + )); + + return true; + } + + /* OAuth2\Storage\UserCredentialsInterface */ + public function checkUserCredentials($username, $password) + { + if ($user = $this->getUser($username)) { + return $this->checkPassword($user, $password); + } + + return false; + } + + public function getUserDetails($username) + { + return $this->getUser($username); + } + + /* UserClaimsInterface */ + public function getUserClaims($user_id, $claims) + { + if (!$userDetails = $this->getUserDetails($user_id)) { + return false; + } + + $claims = explode(' ', trim($claims)); + $userClaims = array(); + + // for each requested claim, if the user has the claim, set it in the response + $validClaims = explode(' ', self::VALID_CLAIMS); + foreach ($validClaims as $validClaim) { + if (in_array($validClaim, $claims)) { + if ($validClaim == 'address') { + // address is an object with subfields + $userClaims['address'] = $this->getUserClaim($validClaim, $userDetails['address'] ?: $userDetails); + } else { + $userClaims = array_merge($userClaims, $this->getUserClaim($validClaim, $userDetails)); + } + } + } + + return $userClaims; + } + + protected function getUserClaim($claim, $userDetails) + { + $userClaims = array(); + $claimValuesString = constant(sprintf('self::%s_CLAIM_VALUES', strtoupper($claim))); + $claimValues = explode(' ', $claimValuesString); + + foreach ($claimValues as $value) { + if ($value == 'email_verified') { + $userClaims[$value] = $userDetails[$value]=='true' ? true : false; + } else { + $userClaims[$value] = isset($userDetails[$value]) ? $userDetails[$value] : null; + } + } + + return $userClaims; + } + + /* OAuth2\Storage\RefreshTokenInterface */ + public function getRefreshToken($refresh_token) + { + $result = $this->client->getItem(array( + "TableName"=> $this->config['refresh_token_table'], + "Key" => array('refresh_token' => array('S' => $refresh_token)) + )); + if ($result->count()==0) { + return false ; + } + $token = $this->dynamo2array($result); + $token['expires'] = strtotime($token['expires']); + + return $token; + } + + public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null) + { + // convert expires to datestring + $expires = date('Y-m-d H:i:s', $expires); + + $clientData = compact('refresh_token', 'client_id', 'user_id', 'expires', 'scope'); + $clientData = array_filter($clientData, 'self::isNotEmpty'); + + $result = $this->client->putItem(array( + 'TableName' => $this->config['refresh_token_table'], + 'Item' => $this->client->formatAttributes($clientData) + )); + + return true; + } + + public function unsetRefreshToken($refresh_token) + { + $result = $this->client->deleteItem(array( + 'TableName' => $this->config['refresh_token_table'], + 'Key' => $this->client->formatAttributes(array("refresh_token" => $refresh_token)) + )); + + return true; + } + + // plaintext passwords are bad! Override this for your application + protected function checkPassword($user, $password) + { + return $user['password'] == $this->hashPassword($password); + } + + // use a secure hashing algorithm when storing passwords. Override this for your application + protected function hashPassword($password) + { + return sha1($password); + } + + public function getUser($username) + { + $result = $this->client->getItem(array( + "TableName"=> $this->config['user_table'], + "Key" => array('username' => array('S' => $username)) + )); + if ($result->count()==0) { + return false ; + } + $token = $this->dynamo2array($result); + $token['user_id'] = $username; + + return $token; + } + + public function setUser($username, $password, $first_name = null, $last_name = null) + { + // do not store in plaintext + $password = $this->hashPassword($password); + + $clientData = compact('username', 'password', 'first_name', 'last_name'); + $clientData = array_filter($clientData, 'self::isNotEmpty'); + + $result = $this->client->putItem(array( + 'TableName' => $this->config['user_table'], + 'Item' => $this->client->formatAttributes($clientData) + )); + + return true; + + } + + /* ScopeInterface */ + public function scopeExists($scope) + { + $scope = explode(' ', $scope); + $scope_query = array(); + $count = 0; + foreach ($scope as $key => $val) { + $result = $this->client->query(array( + 'TableName' => $this->config['scope_table'], + 'Select' => 'COUNT', + 'KeyConditions' => array( + 'scope' => array( + 'AttributeValueList' => array(array('S' => $val)), + 'ComparisonOperator' => 'EQ' + ) + ) + )); + $count += $result['Count']; + } + + return $count == count($scope); + } + + public function getDefaultScope($client_id = null) + { + + $result = $this->client->query(array( + 'TableName' => $this->config['scope_table'], + 'IndexName' => 'is_default-index', + 'Select' => 'ALL_ATTRIBUTES', + 'KeyConditions' => array( + 'is_default' => array( + 'AttributeValueList' => array(array('S' => 'true')), + 'ComparisonOperator' => 'EQ', + ), + ) + )); + $defaultScope = array(); + if ($result->count() > 0) { + $array = $result->toArray(); + foreach ($array["Items"] as $item) { + $defaultScope[] = $item['scope']['S']; + } + + return empty($defaultScope) ? null : implode(' ', $defaultScope); + } + + return null; + } + + /* JWTBearerInterface */ + public function getClientKey($client_id, $subject) + { + $result = $this->client->getItem(array( + "TableName"=> $this->config['jwt_table'], + "Key" => array('client_id' => array('S' => $client_id), 'subject' => array('S' => $subject)) + )); + if ($result->count()==0) { + return false ; + } + $token = $this->dynamo2array($result); + + return $token['public_key']; + } + + public function getClientScope($client_id) + { + if (!$clientDetails = $this->getClientDetails($client_id)) { + return false; + } + + if (isset($clientDetails['scope'])) { + return $clientDetails['scope']; + } + + return null; + } + + public function getJti($client_id, $subject, $audience, $expires, $jti) + { + //TODO not use. + } + + public function setJti($client_id, $subject, $audience, $expires, $jti) + { + //TODO not use. + } + + /* PublicKeyInterface */ + public function getPublicKey($client_id = '0') + { + + $result = $this->client->getItem(array( + "TableName"=> $this->config['public_key_table'], + "Key" => array('client_id' => array('S' => $client_id)) + )); + if ($result->count()==0) { + return false ; + } + $token = $this->dynamo2array($result); + + return $token['public_key']; + + } + + public function getPrivateKey($client_id = '0') + { + $result = $this->client->getItem(array( + "TableName"=> $this->config['public_key_table'], + "Key" => array('client_id' => array('S' => $client_id)) + )); + if ($result->count()==0) { + return false ; + } + $token = $this->dynamo2array($result); + + return $token['private_key']; + } + + public function getEncryptionAlgorithm($client_id = null) + { + $result = $this->client->getItem(array( + "TableName"=> $this->config['public_key_table'], + "Key" => array('client_id' => array('S' => $client_id)) + )); + if ($result->count()==0) { + return 'RS256' ; + } + $token = $this->dynamo2array($result); + + return $token['encryption_algorithm']; + } + + /** + * Transform dynamodb resultset to an array. + * @param $dynamodbResult + * @return $array + */ + private function dynamo2array($dynamodbResult) + { + $result = array(); + foreach ($dynamodbResult["Item"] as $key => $val) { + $result[$key] = $val["S"]; + $result[] = $val["S"]; + } + + return $result; + } + + private static function isNotEmpty($value) + { + return null !== $value && '' !== $value; + } +} \ No newline at end of file diff --git a/modules/yandexhome/lib/OAuth2/Storage/JwtAccessToken.php b/modules/yandexhome/lib/OAuth2/Storage/JwtAccessToken.php new file mode 100644 index 0000000..6ccacd6 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Storage/JwtAccessToken.php @@ -0,0 +1,87 @@ + + */ +class JwtAccessToken implements JwtAccessTokenInterface +{ + protected $publicKeyStorage; + protected $tokenStorage; + protected $encryptionUtil; + + /** + * @param OAuth2\Encryption\PublicKeyInterface $publicKeyStorage the public key encryption to use + * @param OAuth2\Storage\AccessTokenInterface $tokenStorage OPTIONAL persist the access token to another storage. This is useful if + * you want to retain access token grant information somewhere, but + * is not necessary when using this grant type. + * @param OAuth2\Encryption\EncryptionInterface $encryptionUtil OPTIONAL class to use for "encode" and "decode" functions. + */ + public function __construct(PublicKeyInterface $publicKeyStorage, AccessTokenInterface $tokenStorage = null, EncryptionInterface $encryptionUtil = null) + { + $this->publicKeyStorage = $publicKeyStorage; + $this->tokenStorage = $tokenStorage; + if (is_null($encryptionUtil)) { + $encryptionUtil = new Jwt; + } + $this->encryptionUtil = $encryptionUtil; + } + + public function getAccessToken($oauth_token) + { + // just decode the token, don't verify + if (!$tokenData = $this->encryptionUtil->decode($oauth_token, null, false)) { + return false; + } + + $client_id = isset($tokenData['aud']) ? $tokenData['aud'] : null; + $public_key = $this->publicKeyStorage->getPublicKey($client_id); + $algorithm = $this->publicKeyStorage->getEncryptionAlgorithm($client_id); + + // now that we have the client_id, verify the token + if (false === $this->encryptionUtil->decode($oauth_token, $public_key, array($algorithm))) { + return false; + } + + // normalize the JWT claims to the format expected by other components in this library + return $this->convertJwtToOAuth2($tokenData); + } + + public function setAccessToken($oauth_token, $client_id, $user_id, $expires, $scope = null) + { + if ($this->tokenStorage) { + return $this->tokenStorage->setAccessToken($oauth_token, $client_id, $user_id, $expires, $scope); + } + } + + public function unsetAccessToken($access_token) + { + if ($this->tokenStorage) { + return $this->tokenStorage->unsetAccessToken($access_token); + } + } + + + // converts a JWT access token into an OAuth2-friendly format + protected function convertJwtToOAuth2($tokenData) + { + $keyMapping = array( + 'aud' => 'client_id', + 'exp' => 'expires', + 'sub' => 'user_id' + ); + + foreach ($keyMapping as $jwtKey => $oauth2Key) { + if (isset($tokenData[$jwtKey])) { + $tokenData[$oauth2Key] = $tokenData[$jwtKey]; + unset($tokenData[$jwtKey]); + } + } + + return $tokenData; + } +} \ No newline at end of file diff --git a/modules/yandexhome/lib/OAuth2/Storage/JwtAccessTokenInterface.php b/modules/yandexhome/lib/OAuth2/Storage/JwtAccessTokenInterface.php new file mode 100644 index 0000000..3abb2aa --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Storage/JwtAccessTokenInterface.php @@ -0,0 +1,14 @@ + + */ +interface JwtAccessTokenInterface extends AccessTokenInterface +{ + +} diff --git a/modules/yandexhome/lib/OAuth2/Storage/JwtBearerInterface.php b/modules/yandexhome/lib/OAuth2/Storage/JwtBearerInterface.php new file mode 100644 index 0000000..c83aa72 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Storage/JwtBearerInterface.php @@ -0,0 +1,74 @@ + + */ +interface JwtBearerInterface +{ + /** + * Get the public key associated with a client_id + * + * @param $client_id + * Client identifier to be checked with. + * + * @return + * STRING Return the public key for the client_id if it exists, and MUST return FALSE if it doesn't. + */ + public function getClientKey($client_id, $subject); + + /** + * Get a jti (JSON token identifier) by matching against the client_id, subject, audience and expiration. + * + * @param $client_id + * Client identifier to match. + * + * @param $subject + * The subject to match. + * + * @param $audience + * The audience to match. + * + * @param $expiration + * The expiration of the jti. + * + * @param $jti + * The jti to match. + * + * @return + * An associative array as below, and return NULL if the jti does not exist. + * - issuer: Stored client identifier. + * - subject: Stored subject. + * - audience: Stored audience. + * - expires: Stored expiration in unix timestamp. + * - jti: The stored jti. + */ + public function getJti($client_id, $subject, $audience, $expiration, $jti); + + /** + * Store a used jti so that we can check against it to prevent replay attacks. + * @param $client_id + * Client identifier to insert. + * + * @param $subject + * The subject to insert. + * + * @param $audience + * The audience to insert. + * + * @param $expiration + * The expiration of the jti. + * + * @param $jti + * The jti to insert. + */ + public function setJti($client_id, $subject, $audience, $expiration, $jti); +} diff --git a/modules/yandexhome/lib/OAuth2/Storage/MajordomoPdo.php b/modules/yandexhome/lib/OAuth2/Storage/MajordomoPdo.php new file mode 100644 index 0000000..2940f0f --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Storage/MajordomoPdo.php @@ -0,0 +1,338 @@ + $connection); + } + if (!is_array($connection)) { + throw new \InvalidArgumentException('First argument to OAuth2\Storage\Pdo must be an instance of PDO, a DSN string, or a configuration array'); + } + if (!isset($connection['dsn'])) { + throw new \InvalidArgumentException('configuration array must contain "dsn"'); + } + // merge optional parameters + $connection = array_merge(array( + 'username' => null, + 'password' => null, + 'options' => array(), + ), $connection); + $connection = new \PDO($connection['dsn'], $connection['username'], $connection['password'], $connection['options']); + } + + $this->db = $connection; + + // debugging + $connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + + $this->config = array_merge(array( + 'client_table' => 'yandexhome_oauth', + 'access_token_table' => 'yandexhome_oauth', + 'refresh_token_table' => 'yandexhome_oauth', + 'code_table' => 'yandexhome_oauth', + 'user_table' => 'oauth_users', + 'jwt_table' => 'oauth_jwt', + 'jti_table' => 'oauth_jti', + 'scope_table' => 'oauth_scopes', + 'public_key_table' => 'oauth_public_keys', + ), $config); + } + + /** + * @param string $client_id + * @param null|string $client_secret + * @return bool + */ + public function checkClientCredentials($client_id, $client_secret = null) + { + //writeLog(date('H:i:s') . " PDO Storage === checkClientCredentials ($client_id, $client_secret)" . PHP_EOL); + + $stmt = $this->db->prepare(sprintf('SELECT * from %s where client_id = :client_id', $this->config['client_table'])); + $stmt->execute(compact('client_id')); + $result = $stmt->fetch(\PDO::FETCH_ASSOC); + + //writeLog(date('H:i:s') . " PDO Storage === checkClientCredentials () CLIENT_SECRET=" . $result['CLIENT_SECRET'] . PHP_EOL); + ////writeLog(date('H:i:s') . " PDO Storage === checkClientCredentials () return=" . json_encode($result && $result['CLIENT_SECRET'] == $client_secret) . PHP_EOL); + + return $result && $result['CLIENT_SECRET'] == $client_secret; + } + + /** + * @param string $client_id + * @return array|mixed + */ + public function getClientDetails($client_id) + { + //writeLog(date('H:i:s') . " PDO Storage === getClientDetails ($client_id) " . PHP_EOL); + + $stmt = $this->db->prepare(sprintf('SELECT client_id,client_secret from %s where client_id = :client_id', $this->config['client_table'])); + $stmt->execute(compact('client_id')); + $res = $stmt->fetch(\PDO::FETCH_ASSOC); + + //writeLog(date('H:i:s') . " PDO Storage === getClientDetails () return " . json_encode($res) . PHP_EOL); + + return $res; + } + + /** + * @param $client_id + * @param $grant_type + * @return bool + */ + public function checkRestrictedGrantType($client_id, $grant_type) + { + //writeLog(date('H:i:s') . " PDO Storage === checkRestrictedGrantType ($client_id, $grant_type)" . PHP_EOL); + + $res = $grant_type == 'refresh_token' || $grant_type == 'authorization_code'; + + return $grant_type == 'refresh_token' || $grant_type == 'authorization_code'; + } + + /** + * @param string $access_token + * @return array|bool|mixed|null + */ + public function getAccessToken($access_token) + { + //writeLog(date('H:i:s') . " PDO Storage === getAccessToken ($access_token)" . PHP_EOL); + + $stmt = $this->db->prepare(sprintf('SELECT client_id,access_token,access_token_expires from %s where access_token = :access_token', $this->config['access_token_table'])); + + $token = $stmt->execute(compact('access_token')); + + + if ($token = $stmt->fetch(\PDO::FETCH_ASSOC)) { + // convert date string back to timestamp + $token['expires'] = strtotime($token['access_token_expires']); + } + ////writeLog(date('H:i:s') . " PDO Storage === getAccessToken () ACCESS_TOKEN_EXPIRES=".$token['ACCESS_TOKEN_EXPIRES'] . PHP_EOL); + //writeLog(date('H:i:s') . " PDO Storage === getAccessToken () access_token_expires=".$token['access_token_expires'] . PHP_EOL); + //writeLog(date('H:i:s') . " PDO Storage === getAccessToken () return=".json_encode($token) . PHP_EOL); + + return $token; + } + + /** + * @param string $access_token + * @param mixed $client_id + * @param mixed $user_id + * @param int $expires + * @param string $scope + * @return bool + */ + public function setAccessToken($access_token, $client_id, $user_id, $expires, $scope = null) + { + //writeLog(date('H:i:s') . " PDO Storage === setAccessToken ($access_token, $client_id, $user_id, $expires, $scope) " . PHP_EOL); + + // convert expires to datestring + $expires = date('Y-m-d H:i:s', $expires); + + $stmt = $this->db->prepare(sprintf('SELECT * from %s where client_id = :client_id', $this->config['access_token_table'])); + + $res = $stmt->execute(compact('client_id')); + + if ($res = $stmt->fetch(\PDO::FETCH_ASSOC)) { + $stmt = $this->db->prepare(sprintf('UPDATE %s SET access_token=:access_token, access_token_expires=:expires where client_id = :client_id', $this->config['access_token_table'])); + } else { + $stmt = $this->db->prepare(sprintf('INSERT INTO %s (access_token, client_id, access_token_expires) VALUES (:access_token, :client_id, :expires)', $this->config['access_token_table'])); + } + + $res = $stmt->execute(compact('access_token', 'client_id', 'expires')); + + //writeLog(date('H:i:s') . " PDO Storage === setAccessToken () return=" . json_encode($res) . PHP_EOL); + + return $res; + } + + /* OAuth2\Storage\AuthorizationCodeInterface */ + /** + * @param string $code + * @return mixed + */ + public function getAuthorizationCode($code) + { + //writeLog(date('H:i:s') . " PDO Storage === getAuthorizationCode ($code) " . PHP_EOL); + + $stmt = $this->db->prepare(sprintf('SELECT authorization_code,authorization_code_expires,redirect_uri,client_id from %s where authorization_code = :code', $this->config['code_table'])); + + $code = $stmt->execute(compact('code')); + + if ($code = $stmt->fetch(\PDO::FETCH_ASSOC)) { + // convert date string back to timestamp + $code['expires'] = strtotime($code['authorization_code_expires']); + } + + //writeLog(date('H:i:s') . " PDO Storage === getAuthorizationCode () return=" . json_encode($code) . PHP_EOL); + + return $code; + } + + /** + * @param string $code + * @param mixed $client_id + * @param mixed $user_id + * @param string $redirect_uri + * @param int $expires + * @param string $scope + * @param string $id_token + * @return bool|mixed + */ + public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) + { + //writeLog(date('H:i:s') . " PDO Storage === setAuthorizationCode ($code, $client_id, $user_id, $redirect_uri, $expires, $scope, $id_token) " . PHP_EOL); + + $expires = date('Y-m-d H:i:s', $expires); + + $stmt = $this->db->prepare(sprintf('SELECT * from %s where client_id = :client_id', $this->config['code_table'])); + + $res = $stmt->execute(compact('client_id')); + + if ($res = $stmt->fetch(\PDO::FETCH_ASSOC)) { + $stmt = $this->db->prepare($sql = sprintf('UPDATE %s SET authorization_code=:code,redirect_uri=:redirect_uri, authorization_code_expires=:expires where client_id=:client_id', $this->config['code_table'])); + } else { + $stmt = $this->db->prepare(sprintf('INSERT INTO %s (authorization_code, client_id, redirect_uri, authorization_code_expires) VALUES (:code, :client_id, :redirect_uri, :expires)', $this->config['code_table'])); + } + + $res = $stmt->execute(compact('code', 'client_id', 'redirect_uri', 'expires')); + + //writeLog(date('H:i:s') . " PDO Storage === setAuthorizationCode () return=" . json_encode($res) . PHP_EOL); + + return $res; + } + + /** + * @param string $code + * @return bool + */ + public function expireAuthorizationCode($code) + { + //writeLog(date('H:i:s') . " PDO Storage === expireAuthorizationCode ($code) " . PHP_EOL); + + $stmt = $this->db->prepare(sprintf('SELECT authorization_code,authorization_code_expires,redirect_uri from %s where authorization_code = :code', $this->config['code_table'])); + + $res = $stmt->execute(compact('code')); + + if ($res = $stmt->fetch(\PDO::FETCH_ASSOC)) { + $stmt = $this->db->prepare($sql = sprintf('UPDATE %s SET authorization_code=:code_new,redirect_uri=:redirect_uri, authorization_code_expires=:expires where authorization_code = :code', $this->config['code_table'])); + $code_new = null; + $expires = null; + $redirect_uri = null; + $res = $stmt->execute(compact('code', 'code_new', 'redirect_uri', 'expires')); + } + + //writeLog(date('H:i:s') . " PDO Storage === expireAuthorizationCode () return=" . json_encode($res) . PHP_EOL); + + return $res; + } + + /** + * @param string $refresh_token + * @return bool|mixed + */ + public function getRefreshToken($refresh_token) + { + //writeLog(date('H:i:s') . " PDO Storage === getRefreshToken ($refresh_token)" . PHP_EOL); + + $stmt = $this->db->prepare(sprintf('SELECT refresh_token,refresh_token_expires,client_id FROM %s WHERE refresh_token = :refresh_token', $this->config['refresh_token_table'])); + + $token = $stmt->execute(compact('refresh_token')); + + if ($token = $stmt->fetch(\PDO::FETCH_ASSOC)) { + // convert expires to epoch time + $token['expires'] = strtotime($token['refresh_token_expires']); + } + + //writeLog(date('H:i:s') . " PDO Storage === getRefreshToken () return=" . json_encode($token) . PHP_EOL); + + return $token; + } + + /** + * @param string $refresh_token + * @param mixed $client_id + * @param mixed $user_id + * @param string $expires + * @param string $scope + * @return bool + */ + public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null) + { + //writeLog(date('H:i:s') . " PDO Storage === setRefreshToken ($refresh_token, $client_id, $user_id, $expires, $scope)" . PHP_EOL); + + // convert expires to datestring + $expires = date('Y-m-d H:i:s', $expires); + + $stmt = $this->db->prepare(sprintf('SELECT * from %s where client_id = :client_id', $this->config['refresh_token_table'])); + + $res = $stmt->execute(compact('client_id')); + + if ($res = $stmt->fetch(\PDO::FETCH_ASSOC)) { + $stmt = $this->db->prepare(sprintf('UPDATE %s SET refresh_token=:refresh_token, refresh_token_expires=:expires where client_id = :client_id', $this->config['refresh_token_table'])); + } else { + $stmt = $this->db->prepare(sprintf('INSERT INTO %s (refresh_token, client_id, refresh_token_expires) VALUES (:refresh_token, :client_id, :expires)', $this->config['refresh_token_table'])); + } + + $res = $stmt->execute(compact('refresh_token', 'client_id', 'expires')); + + //writeLog(date('H:i:s') . " PDO Storage === setRefreshToken () return=" . json_encode($res) . PHP_EOL); + + return $res; + } + + /** + * @param string $refresh_token + * @return bool + */ + public function unsetRefreshToken($refresh_token) + { + // + return true; + } + + /** + * @param string $client_id + * @return bool + */ + public function isPublicClient($client_id) + { + // + return true; + } + + /** + * @param mixed $client_id + * @return bool|null + */ + public function getClientScope($client_id) + { + // + return null; + } +} \ No newline at end of file diff --git a/modules/yandexhome/lib/OAuth2/Storage/Memory.php b/modules/yandexhome/lib/OAuth2/Storage/Memory.php new file mode 100644 index 0000000..2c60b71 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Storage/Memory.php @@ -0,0 +1,381 @@ + + */ +class Memory implements AuthorizationCodeInterface, + UserCredentialsInterface, + UserClaimsInterface, + AccessTokenInterface, + ClientCredentialsInterface, + RefreshTokenInterface, + JwtBearerInterface, + ScopeInterface, + PublicKeyInterface, + OpenIDAuthorizationCodeInterface +{ + public $authorizationCodes; + public $userCredentials; + public $clientCredentials; + public $refreshTokens; + public $accessTokens; + public $jwt; + public $jti; + public $supportedScopes; + public $defaultScope; + public $keys; + + public function __construct($params = array()) + { + $params = array_merge(array( + 'authorization_codes' => array(), + 'user_credentials' => array(), + 'client_credentials' => array(), + 'refresh_tokens' => array(), + 'access_tokens' => array(), + 'jwt' => array(), + 'jti' => array(), + 'default_scope' => null, + 'supported_scopes' => array(), + 'keys' => array(), + ), $params); + + $this->authorizationCodes = $params['authorization_codes']; + $this->userCredentials = $params['user_credentials']; + $this->clientCredentials = $params['client_credentials']; + $this->refreshTokens = $params['refresh_tokens']; + $this->accessTokens = $params['access_tokens']; + $this->jwt = $params['jwt']; + $this->jti = $params['jti']; + $this->supportedScopes = $params['supported_scopes']; + $this->defaultScope = $params['default_scope']; + $this->keys = $params['keys']; + } + + /* AuthorizationCodeInterface */ + public function getAuthorizationCode($code) + { + if (!isset($this->authorizationCodes[$code])) { + return false; + } + + return array_merge(array( + 'authorization_code' => $code, + ), $this->authorizationCodes[$code]); + } + + public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) + { + $this->authorizationCodes[$code] = compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token'); + + return true; + } + + public function setAuthorizationCodes($authorization_codes) + { + $this->authorizationCodes = $authorization_codes; + } + + public function expireAuthorizationCode($code) + { + unset($this->authorizationCodes[$code]); + } + + /* UserCredentialsInterface */ + public function checkUserCredentials($username, $password) + { + $userDetails = $this->getUserDetails($username); + + return $userDetails && $userDetails['password'] && $userDetails['password'] === $password; + } + + public function setUser($username, $password, $firstName = null, $lastName = null) + { + $this->userCredentials[$username] = array( + 'password' => $password, + 'first_name' => $firstName, + 'last_name' => $lastName, + ); + + return true; + } + + public function getUserDetails($username) + { + if (!isset($this->userCredentials[$username])) { + return false; + } + + return array_merge(array( + 'user_id' => $username, + 'password' => null, + 'first_name' => null, + 'last_name' => null, + ), $this->userCredentials[$username]); + } + + /* UserClaimsInterface */ + public function getUserClaims($user_id, $claims) + { + if (!$userDetails = $this->getUserDetails($user_id)) { + return false; + } + + $claims = explode(' ', trim($claims)); + $userClaims = array(); + + // for each requested claim, if the user has the claim, set it in the response + $validClaims = explode(' ', self::VALID_CLAIMS); + foreach ($validClaims as $validClaim) { + if (in_array($validClaim, $claims)) { + if ($validClaim == 'address') { + // address is an object with subfields + $userClaims['address'] = $this->getUserClaim($validClaim, $userDetails['address'] ?: $userDetails); + } else { + $userClaims = array_merge($userClaims, $this->getUserClaim($validClaim, $userDetails)); + } + } + } + + return $userClaims; + } + + protected function getUserClaim($claim, $userDetails) + { + $userClaims = array(); + $claimValuesString = constant(sprintf('self::%s_CLAIM_VALUES', strtoupper($claim))); + $claimValues = explode(' ', $claimValuesString); + + foreach ($claimValues as $value) { + $userClaims[$value] = isset($userDetails[$value]) ? $userDetails[$value] : null; + } + + return $userClaims; + } + + /* ClientCredentialsInterface */ + public function checkClientCredentials($client_id, $client_secret = null) + { + return isset($this->clientCredentials[$client_id]['client_secret']) && $this->clientCredentials[$client_id]['client_secret'] === $client_secret; + } + + public function isPublicClient($client_id) + { + if (!isset($this->clientCredentials[$client_id])) { + return false; + } + + return empty($this->clientCredentials[$client_id]['client_secret']); + } + + /* ClientInterface */ + public function getClientDetails($client_id) + { + if (!isset($this->clientCredentials[$client_id])) { + return false; + } + + $clientDetails = array_merge(array( + 'client_id' => $client_id, + 'client_secret' => null, + 'redirect_uri' => null, + 'scope' => null, + ), $this->clientCredentials[$client_id]); + + return $clientDetails; + } + + public function checkRestrictedGrantType($client_id, $grant_type) + { + if (isset($this->clientCredentials[$client_id]['grant_types'])) { + $grant_types = explode(' ', $this->clientCredentials[$client_id]['grant_types']); + + return in_array($grant_type, $grant_types); + } + + // if grant_types are not defined, then none are restricted + return true; + } + + public function setClientDetails($client_id, $client_secret = null, $redirect_uri = null, $grant_types = null, $scope = null, $user_id = null) + { + $this->clientCredentials[$client_id] = array( + 'client_id' => $client_id, + 'client_secret' => $client_secret, + 'redirect_uri' => $redirect_uri, + 'grant_types' => $grant_types, + 'scope' => $scope, + 'user_id' => $user_id, + ); + + return true; + } + + /* RefreshTokenInterface */ + public function getRefreshToken($refresh_token) + { + return isset($this->refreshTokens[$refresh_token]) ? $this->refreshTokens[$refresh_token] : false; + } + + public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null) + { + $this->refreshTokens[$refresh_token] = compact('refresh_token', 'client_id', 'user_id', 'expires', 'scope'); + + return true; + } + + public function unsetRefreshToken($refresh_token) + { + if (isset($this->refreshTokens[$refresh_token])) { + unset($this->refreshTokens[$refresh_token]); + + return true; + } + + return false; + } + + public function setRefreshTokens($refresh_tokens) + { + $this->refreshTokens = $refresh_tokens; + } + + /* AccessTokenInterface */ + public function getAccessToken($access_token) + { + return isset($this->accessTokens[$access_token]) ? $this->accessTokens[$access_token] : false; + } + + public function setAccessToken($access_token, $client_id, $user_id, $expires, $scope = null, $id_token = null) + { + $this->accessTokens[$access_token] = compact('access_token', 'client_id', 'user_id', 'expires', 'scope', 'id_token'); + + return true; + } + + public function unsetAccessToken($access_token) + { + if (isset($this->accessTokens[$access_token])) { + unset($this->accessTokens[$access_token]); + + return true; + } + + return false; + } + + public function scopeExists($scope) + { + $scope = explode(' ', trim($scope)); + + return (count(array_diff($scope, $this->supportedScopes)) == 0); + } + + public function getDefaultScope($client_id = null) + { + return $this->defaultScope; + } + + /*JWTBearerInterface */ + public function getClientKey($client_id, $subject) + { + if (isset($this->jwt[$client_id])) { + $jwt = $this->jwt[$client_id]; + if ($jwt) { + if ($jwt["subject"] == $subject) { + return $jwt["key"]; + } + } + } + + return false; + } + + public function getClientScope($client_id) + { + if (!$clientDetails = $this->getClientDetails($client_id)) { + return false; + } + + if (isset($clientDetails['scope'])) { + return $clientDetails['scope']; + } + + return null; + } + + public function getJti($client_id, $subject, $audience, $expires, $jti) + { + foreach ($this->jti as $storedJti) { + if ($storedJti['issuer'] == $client_id && $storedJti['subject'] == $subject && $storedJti['audience'] == $audience && $storedJti['expires'] == $expires && $storedJti['jti'] == $jti) { + return array( + 'issuer' => $storedJti['issuer'], + 'subject' => $storedJti['subject'], + 'audience' => $storedJti['audience'], + 'expires' => $storedJti['expires'], + 'jti' => $storedJti['jti'] + ); + } + } + + return null; + } + + public function setJti($client_id, $subject, $audience, $expires, $jti) + { + $this->jti[] = array('issuer' => $client_id, 'subject' => $subject, 'audience' => $audience, 'expires' => $expires, 'jti' => $jti); + } + + /*PublicKeyInterface */ + public function getPublicKey($client_id = null) + { + if (isset($this->keys[$client_id])) { + return $this->keys[$client_id]['public_key']; + } + + // use a global encryption pair + if (isset($this->keys['public_key'])) { + return $this->keys['public_key']; + } + + return false; + } + + public function getPrivateKey($client_id = null) + { + if (isset($this->keys[$client_id])) { + return $this->keys[$client_id]['private_key']; + } + + // use a global encryption pair + if (isset($this->keys['private_key'])) { + return $this->keys['private_key']; + } + + return false; + } + + public function getEncryptionAlgorithm($client_id = null) + { + if (isset($this->keys[$client_id]['encryption_algorithm'])) { + return $this->keys[$client_id]['encryption_algorithm']; + } + + // use a global encryption algorithm + if (isset($this->keys['encryption_algorithm'])) { + return $this->keys['encryption_algorithm']; + } + + return 'RS256'; + } +} \ No newline at end of file diff --git a/modules/yandexhome/lib/OAuth2/Storage/Mongo.php b/modules/yandexhome/lib/OAuth2/Storage/Mongo.php new file mode 100644 index 0000000..eea06e3 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Storage/Mongo.php @@ -0,0 +1,392 @@ + + */ +class Mongo implements AuthorizationCodeInterface, + AccessTokenInterface, + ClientCredentialsInterface, + UserCredentialsInterface, + RefreshTokenInterface, + JwtBearerInterface, + PublicKeyInterface, + OpenIDAuthorizationCodeInterface +{ + protected $db; + protected $config; + + public function __construct($connection, $config = array()) + { + if ($connection instanceof \MongoDB) { + $this->db = $connection; + } else { + if (!is_array($connection)) { + throw new \InvalidArgumentException('First argument to OAuth2\Storage\Mongo must be an instance of MongoDB or a configuration array'); + } + $server = sprintf('mongodb://%s:%d', $connection['host'], $connection['port']); + $m = new \MongoClient($server); + $this->db = $m->{$connection['database']}; + } + + $this->config = array_merge(array( + 'client_table' => 'oauth_clients', + 'access_token_table' => 'oauth_access_tokens', + 'refresh_token_table' => 'oauth_refresh_tokens', + 'code_table' => 'oauth_authorization_codes', + 'user_table' => 'oauth_users', + 'key_table' => 'oauth_keys', + 'jwt_table' => 'oauth_jwt', + ), $config); + } + + // Helper function to access a MongoDB collection by `type`: + protected function collection($name) + { + return $this->db->{$this->config[$name]}; + } + + /* ClientCredentialsInterface */ + public function checkClientCredentials($client_id, $client_secret = null) + { + if ($result = $this->collection('client_table')->findOne(array('client_id' => $client_id))) { + return $result['client_secret'] == $client_secret; + } + + return false; + } + + public function isPublicClient($client_id) + { + if (!$result = $this->collection('client_table')->findOne(array('client_id' => $client_id))) { + return false; + } + + return empty($result['client_secret']); + } + + /* ClientInterface */ + public function getClientDetails($client_id) + { + $result = $this->collection('client_table')->findOne(array('client_id' => $client_id)); + + return is_null($result) ? false : $result; + } + + public function setClientDetails($client_id, $client_secret = null, $redirect_uri = null, $grant_types = null, $scope = null, $user_id = null) + { + if ($this->getClientDetails($client_id)) { + $this->collection('client_table')->update( + array('client_id' => $client_id), + array('$set' => array( + 'client_secret' => $client_secret, + 'redirect_uri' => $redirect_uri, + 'grant_types' => $grant_types, + 'scope' => $scope, + 'user_id' => $user_id, + )) + ); + } else { + $client = array( + 'client_id' => $client_id, + 'client_secret' => $client_secret, + 'redirect_uri' => $redirect_uri, + 'grant_types' => $grant_types, + 'scope' => $scope, + 'user_id' => $user_id, + ); + $this->collection('client_table')->insert($client); + } + + return true; + } + + public function checkRestrictedGrantType($client_id, $grant_type) + { + $details = $this->getClientDetails($client_id); + if (isset($details['grant_types'])) { + $grant_types = explode(' ', $details['grant_types']); + + return in_array($grant_type, $grant_types); + } + + // if grant_types are not defined, then none are restricted + return true; + } + + /* AccessTokenInterface */ + public function getAccessToken($access_token) + { + $token = $this->collection('access_token_table')->findOne(array('access_token' => $access_token)); + + return is_null($token) ? false : $token; + } + + public function setAccessToken($access_token, $client_id, $user_id, $expires, $scope = null) + { + // if it exists, update it. + if ($this->getAccessToken($access_token)) { + $this->collection('access_token_table')->update( + array('access_token' => $access_token), + array('$set' => array( + 'client_id' => $client_id, + 'expires' => $expires, + 'user_id' => $user_id, + 'scope' => $scope + )) + ); + } else { + $token = array( + 'access_token' => $access_token, + 'client_id' => $client_id, + 'expires' => $expires, + 'user_id' => $user_id, + 'scope' => $scope + ); + $this->collection('access_token_table')->insert($token); + } + + return true; + } + + public function unsetAccessToken($access_token) + { + $result = $this->collection('access_token_table')->remove(array( + 'access_token' => $access_token + ), array('w' => 1)); + + return $result['n'] > 0; + } + + + /* AuthorizationCodeInterface */ + public function getAuthorizationCode($code) + { + $code = $this->collection('code_table')->findOne(array('authorization_code' => $code)); + + return is_null($code) ? false : $code; + } + + public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) + { + // if it exists, update it. + if ($this->getAuthorizationCode($code)) { + $this->collection('code_table')->update( + array('authorization_code' => $code), + array('$set' => array( + 'client_id' => $client_id, + 'user_id' => $user_id, + 'redirect_uri' => $redirect_uri, + 'expires' => $expires, + 'scope' => $scope, + 'id_token' => $id_token, + )) + ); + } else { + $token = array( + 'authorization_code' => $code, + 'client_id' => $client_id, + 'user_id' => $user_id, + 'redirect_uri' => $redirect_uri, + 'expires' => $expires, + 'scope' => $scope, + 'id_token' => $id_token, + ); + $this->collection('code_table')->insert($token); + } + + return true; + } + + public function expireAuthorizationCode($code) + { + $this->collection('code_table')->remove(array('authorization_code' => $code)); + + return true; + } + + /* UserCredentialsInterface */ + public function checkUserCredentials($username, $password) + { + if ($user = $this->getUser($username)) { + return $this->checkPassword($user, $password); + } + + return false; + } + + public function getUserDetails($username) + { + if ($user = $this->getUser($username)) { + $user['user_id'] = $user['username']; + } + + return $user; + } + + /* RefreshTokenInterface */ + public function getRefreshToken($refresh_token) + { + $token = $this->collection('refresh_token_table')->findOne(array('refresh_token' => $refresh_token)); + + return is_null($token) ? false : $token; + } + + public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null) + { + $token = array( + 'refresh_token' => $refresh_token, + 'client_id' => $client_id, + 'user_id' => $user_id, + 'expires' => $expires, + 'scope' => $scope + ); + $this->collection('refresh_token_table')->insert($token); + + return true; + } + + public function unsetRefreshToken($refresh_token) + { + $result = $this->collection('refresh_token_table')->remove(array( + 'refresh_token' => $refresh_token + ), array('w' => 1)); + + return $result['n'] > 0; + } + + // plaintext passwords are bad! Override this for your application + protected function checkPassword($user, $password) + { + return $user['password'] == $password; + } + + public function getUser($username) + { + $result = $this->collection('user_table')->findOne(array('username' => $username)); + + return is_null($result) ? false : $result; + } + + public function setUser($username, $password, $firstName = null, $lastName = null) + { + if ($this->getUser($username)) { + $this->collection('user_table')->update( + array('username' => $username), + array('$set' => array( + 'password' => $password, + 'first_name' => $firstName, + 'last_name' => $lastName + )) + ); + } else { + $user = array( + 'username' => $username, + 'password' => $password, + 'first_name' => $firstName, + 'last_name' => $lastName + ); + $this->collection('user_table')->insert($user); + } + + return true; + } + + public function getClientKey($client_id, $subject) + { + $result = $this->collection('jwt_table')->findOne(array( + 'client_id' => $client_id, + 'subject' => $subject + )); + + return is_null($result) ? false : $result['key']; + } + + public function getClientScope($client_id) + { + if (!$clientDetails = $this->getClientDetails($client_id)) { + return false; + } + + if (isset($clientDetails['scope'])) { + return $clientDetails['scope']; + } + + return null; + } + + public function getJti($client_id, $subject, $audience, $expiration, $jti) + { + //TODO: Needs mongodb implementation. + throw new \Exception('getJti() for the MongoDB driver is currently unimplemented.'); + } + + public function setJti($client_id, $subject, $audience, $expiration, $jti) + { + //TODO: Needs mongodb implementation. + throw new \Exception('setJti() for the MongoDB driver is currently unimplemented.'); + } + + public function getPublicKey($client_id = null) + { + if ($client_id) { + $result = $this->collection('key_table')->findOne(array( + 'client_id' => $client_id + )); + if ($result) { + return $result['public_key']; + } + } + + $result = $this->collection('key_table')->findOne(array( + 'client_id' => null + )); + return is_null($result) ? false : $result['public_key']; + } + + public function getPrivateKey($client_id = null) + { + if ($client_id) { + $result = $this->collection('key_table')->findOne(array( + 'client_id' => $client_id + )); + if ($result) { + return $result['private_key']; + } + } + + $result = $this->collection('key_table')->findOne(array( + 'client_id' => null + )); + return is_null($result) ? false : $result['private_key']; + } + + public function getEncryptionAlgorithm($client_id = null) + { + if ($client_id) { + $result = $this->collection('key_table')->findOne(array( + 'client_id' => $client_id + )); + if ($result) { + return $result['encryption_algorithm']; + } + } + + $result = $this->collection('key_table')->findOne(array( + 'client_id' => null + )); + return is_null($result) ? 'RS256' : $result['encryption_algorithm']; + } +} diff --git a/modules/yandexhome/lib/OAuth2/Storage/MongoDB.php b/modules/yandexhome/lib/OAuth2/Storage/MongoDB.php new file mode 100644 index 0000000..64f740f --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Storage/MongoDB.php @@ -0,0 +1,380 @@ + + */ +class MongoDB implements AuthorizationCodeInterface, + UserCredentialsInterface, + AccessTokenInterface, + ClientCredentialsInterface, + RefreshTokenInterface, + JwtBearerInterface, + PublicKeyInterface, + OpenIDAuthorizationCodeInterface +{ + protected $db; + protected $config; + + public function __construct($connection, $config = array()) + { + if ($connection instanceof Database) { + $this->db = $connection; + } else { + if (!is_array($connection)) { + throw new \InvalidArgumentException('First argument to OAuth2\Storage\Mongo must be an instance of MongoDB\Database or a configuration array'); + } + $server = sprintf('mongodb://%s:%d', $connection['host'], $connection['port']); + $m = new Client($server); + $this->db = $m->selectDatabase($connection['database']); + } + $this->config = array_merge(array( + 'client_table' => 'oauth_clients', + 'access_token_table' => 'oauth_access_tokens', + 'refresh_token_table' => 'oauth_refresh_tokens', + 'code_table' => 'oauth_authorization_codes', + 'user_table' => 'oauth_users', + 'jwt_table' => 'oauth_jwt', + 'jti_table' => 'oauth_jti', + 'scope_table' => 'oauth_scopes', + 'key_table' => 'oauth_keys', + ), $config); + } + + /* ClientCredentialsInterface */ + public function checkClientCredentials($client_id, $client_secret = null) + { + if ($result = $this->collection('client_table')->findOne(array('client_id' => $client_id))) { + return $result['client_secret'] == $client_secret; + } + return false; + } + + public function isPublicClient($client_id) + { + if (!$result = $this->collection('client_table')->findOne(array('client_id' => $client_id))) { + return false; + } + return empty($result['client_secret']); + } + + /* ClientInterface */ + public function getClientDetails($client_id) + { + $result = $this->collection('client_table')->findOne(array('client_id' => $client_id)); + return is_null($result) ? false : $result; + } + + public function setClientDetails($client_id, $client_secret = null, $redirect_uri = null, $grant_types = null, $scope = null, $user_id = null) + { + if ($this->getClientDetails($client_id)) { + $result = $this->collection('client_table')->updateOne( + array('client_id' => $client_id), + array('$set' => array( + 'client_secret' => $client_secret, + 'redirect_uri' => $redirect_uri, + 'grant_types' => $grant_types, + 'scope' => $scope, + 'user_id' => $user_id, + )) + ); + return $result->getMatchedCount() > 0; + } + $client = array( + 'client_id' => $client_id, + 'client_secret' => $client_secret, + 'redirect_uri' => $redirect_uri, + 'grant_types' => $grant_types, + 'scope' => $scope, + 'user_id' => $user_id, + ); + $result = $this->collection('client_table')->insertOne($client); + return $result->getInsertedCount() > 0; + } + + public function checkRestrictedGrantType($client_id, $grant_type) + { + $details = $this->getClientDetails($client_id); + if (isset($details['grant_types'])) { + $grant_types = explode(' ', $details['grant_types']); + return in_array($grant_type, $grant_types); + } + // if grant_types are not defined, then none are restricted + return true; + } + + /* AccessTokenInterface */ + public function getAccessToken($access_token) + { + $token = $this->collection('access_token_table')->findOne(array('access_token' => $access_token)); + return is_null($token) ? false : $token; + } + + public function setAccessToken($access_token, $client_id, $user_id, $expires, $scope = null) + { + // if it exists, update it. + if ($this->getAccessToken($access_token)) { + $result = $this->collection('access_token_table')->updateOne( + array('access_token' => $access_token), + array('$set' => array( + 'client_id' => $client_id, + 'expires' => $expires, + 'user_id' => $user_id, + 'scope' => $scope + )) + ); + return $result->getMatchedCount() > 0; + } + $token = array( + 'access_token' => $access_token, + 'client_id' => $client_id, + 'expires' => $expires, + 'user_id' => $user_id, + 'scope' => $scope + ); + $result = $this->collection('access_token_table')->insertOne($token); + return $result->getInsertedCount() > 0; + } + + public function unsetAccessToken($access_token) + { + $result = $this->collection('access_token_table')->deleteOne(array( + 'access_token' => $access_token + )); + return $result->getDeletedCount() > 0; + } + + /* AuthorizationCodeInterface */ + public function getAuthorizationCode($code) + { + $code = $this->collection('code_table')->findOne(array( + 'authorization_code' => $code + )); + return is_null($code) ? false : $code; + } + + public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) + { + // if it exists, update it. + if ($this->getAuthorizationCode($code)) { + $result = $this->collection('code_table')->updateOne( + array('authorization_code' => $code), + array('$set' => array( + 'client_id' => $client_id, + 'user_id' => $user_id, + 'redirect_uri' => $redirect_uri, + 'expires' => $expires, + 'scope' => $scope, + 'id_token' => $id_token, + )) + ); + return $result->getMatchedCount() > 0; + } + $token = array( + 'authorization_code' => $code, + 'client_id' => $client_id, + 'user_id' => $user_id, + 'redirect_uri' => $redirect_uri, + 'expires' => $expires, + 'scope' => $scope, + 'id_token' => $id_token, + ); + $result = $this->collection('code_table')->insertOne($token); + return $result->getInsertedCount() > 0; + } + + public function expireAuthorizationCode($code) + { + $result = $this->collection('code_table')->deleteOne(array( + 'authorization_code' => $code + )); + return $result->getDeletedCount() > 0; + } + + /* UserCredentialsInterface */ + public function checkUserCredentials($username, $password) + { + if ($user = $this->getUser($username)) { + return $this->checkPassword($user, $password); + } + return false; + } + + public function getUserDetails($username) + { + if ($user = $this->getUser($username)) { + $user['user_id'] = $user['username']; + } + return $user; + } + + /* RefreshTokenInterface */ + public function getRefreshToken($refresh_token) + { + $token = $this->collection('refresh_token_table')->findOne(array( + 'refresh_token' => $refresh_token + )); + return is_null($token) ? false : $token; + } + + public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null) + { + $token = array( + 'refresh_token' => $refresh_token, + 'client_id' => $client_id, + 'user_id' => $user_id, + 'expires' => $expires, + 'scope' => $scope + ); + $result = $this->collection('refresh_token_table')->insertOne($token); + return $result->getInsertedCount() > 0; + } + + public function unsetRefreshToken($refresh_token) + { + $result = $this->collection('refresh_token_table')->deleteOne(array( + 'refresh_token' => $refresh_token + )); + return $result->getDeletedCount() > 0; + } + + // plaintext passwords are bad! Override this for your application + protected function checkPassword($user, $password) + { + return $user['password'] == $password; + } + + public function getUser($username) + { + $result = $this->collection('user_table')->findOne(array('username' => $username)); + return is_null($result) ? false : $result; + } + + public function setUser($username, $password, $firstName = null, $lastName = null) + { + if ($this->getUser($username)) { + $result = $this->collection('user_table')->updateOne( + array('username' => $username), + array('$set' => array( + 'password' => $password, + 'first_name' => $firstName, + 'last_name' => $lastName + )) + ); + + return $result->getMatchedCount() > 0; + } + + $user = array( + 'username' => $username, + 'password' => $password, + 'first_name' => $firstName, + 'last_name' => $lastName + ); + $result = $this->collection('user_table')->insertOne($user); + return $result->getInsertedCount() > 0; + } + + public function getClientKey($client_id, $subject) + { + $result = $this->collection('jwt_table')->findOne(array( + 'client_id' => $client_id, + 'subject' => $subject + )); + return is_null($result) ? false : $result['key']; + } + + public function getClientScope($client_id) + { + if (!$clientDetails = $this->getClientDetails($client_id)) { + return false; + } + if (isset($clientDetails['scope'])) { + return $clientDetails['scope']; + } + return null; + } + + public function getJti($client_id, $subject, $audience, $expires, $jti) + { + //TODO: Needs mongodb implementation. + throw new \Exception('getJti() for the MongoDB driver is currently unimplemented.'); + } + + public function setJti($client_id, $subject, $audience, $expires, $jti) + { + //TODO: Needs mongodb implementation. + throw new \Exception('setJti() for the MongoDB driver is currently unimplemented.'); + } + + public function getPublicKey($client_id = null) + { + if ($client_id) { + $result = $this->collection('key_table')->findOne(array( + 'client_id' => $client_id + )); + if ($result) { + return $result['public_key']; + } + } + + $result = $this->collection('key_table')->findOne(array( + 'client_id' => null + )); + return is_null($result) ? false : $result['public_key']; + } + + public function getPrivateKey($client_id = null) + { + if ($client_id) { + $result = $this->collection('key_table')->findOne(array( + 'client_id' => $client_id + )); + if ($result) { + return $result['private_key']; + } + } + + $result = $this->collection('key_table')->findOne(array( + 'client_id' => null + )); + return is_null($result) ? false : $result['private_key']; + } + + public function getEncryptionAlgorithm($client_id = null) + { + if ($client_id) { + $result = $this->collection('key_table')->findOne(array( + 'client_id' => $client_id + )); + if ($result) { + return $result['encryption_algorithm']; + } + } + + $result = $this->collection('key_table')->findOne(array( + 'client_id' => null + )); + return is_null($result) ? 'RS256' : $result['encryption_algorithm']; + } + + // Helper function to access a MongoDB collection by `type`: + protected function collection($name) + { + return $this->db->{$this->config[$name]}; + } +} \ No newline at end of file diff --git a/modules/yandexhome/lib/OAuth2/Storage/PublicKeyInterface.php b/modules/yandexhome/lib/OAuth2/Storage/PublicKeyInterface.php new file mode 100644 index 0000000..a6dc49f --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Storage/PublicKeyInterface.php @@ -0,0 +1,30 @@ + + */ +interface PublicKeyInterface +{ + /** + * @param mixed $client_id + * @return mixed + */ + public function getPublicKey($client_id = null); + + /** + * @param mixed $client_id + * @return mixed + */ + public function getPrivateKey($client_id = null); + + /** + * @param mixed $client_id + * @return mixed + */ + public function getEncryptionAlgorithm($client_id = null); +} \ No newline at end of file diff --git a/modules/yandexhome/lib/OAuth2/Storage/Redis.php b/modules/yandexhome/lib/OAuth2/Storage/Redis.php new file mode 100644 index 0000000..e6294e2 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Storage/Redis.php @@ -0,0 +1,321 @@ + + * $storage = new OAuth2\Storage\Redis($redis); + * $storage->setClientDetails($client_id, $client_secret, $redirect_uri); + * + */ +class Redis implements AuthorizationCodeInterface, + AccessTokenInterface, + ClientCredentialsInterface, + UserCredentialsInterface, + RefreshTokenInterface, + JwtBearerInterface, + ScopeInterface, + OpenIDAuthorizationCodeInterface +{ + + private $cache; + + /* The redis client */ + protected $redis; + + /* Configuration array */ + protected $config; + + /** + * Redis Storage! + * + * @param \Predis\Client $redis + * @param array $config + */ + public function __construct($redis, $config=array()) + { + $this->redis = $redis; + $this->config = array_merge(array( + 'client_key' => 'oauth_clients:', + 'access_token_key' => 'oauth_access_tokens:', + 'refresh_token_key' => 'oauth_refresh_tokens:', + 'code_key' => 'oauth_authorization_codes:', + 'user_key' => 'oauth_users:', + 'jwt_key' => 'oauth_jwt:', + 'scope_key' => 'oauth_scopes:', + ), $config); + } + + protected function getValue($key) + { + if ( isset($this->cache[$key]) ) { + return $this->cache[$key]; + } + $value = $this->redis->get($key); + if ( isset($value) ) { + return json_decode($value, true); + } else { + return false; + } + } + + protected function setValue($key, $value, $expire=0) + { + $this->cache[$key] = $value; + $str = json_encode($value); + if ($expire > 0) { + $seconds = $expire - time(); + $ret = $this->redis->setex($key, $seconds, $str); + } else { + $ret = $this->redis->set($key, $str); + } + + // check that the key was set properly + // if this fails, an exception will usually thrown, so this step isn't strictly necessary + return is_bool($ret) ? $ret : $ret->getPayload() == 'OK'; + } + + protected function expireValue($key) + { + unset($this->cache[$key]); + + return $this->redis->del($key); + } + + /* AuthorizationCodeInterface */ + public function getAuthorizationCode($code) + { + return $this->getValue($this->config['code_key'] . $code); + } + + public function setAuthorizationCode($authorization_code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) + { + return $this->setValue( + $this->config['code_key'] . $authorization_code, + compact('authorization_code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token'), + $expires + ); + } + + public function expireAuthorizationCode($code) + { + $key = $this->config['code_key'] . $code; + unset($this->cache[$key]); + + return $this->expireValue($key); + } + + /* UserCredentialsInterface */ + public function checkUserCredentials($username, $password) + { + $user = $this->getUserDetails($username); + + return $user && $user['password'] === $password; + } + + public function getUserDetails($username) + { + return $this->getUser($username); + } + + public function getUser($username) + { + if (!$userInfo = $this->getValue($this->config['user_key'] . $username)) { + return false; + } + + // the default behavior is to use "username" as the user_id + return array_merge(array( + 'user_id' => $username, + ), $userInfo); + } + + public function setUser($username, $password, $first_name = null, $last_name = null) + { + return $this->setValue( + $this->config['user_key'] . $username, + compact('username', 'password', 'first_name', 'last_name') + ); + } + + /* ClientCredentialsInterface */ + public function checkClientCredentials($client_id, $client_secret = null) + { + if (!$client = $this->getClientDetails($client_id)) { + return false; + } + + return isset($client['client_secret']) + && $client['client_secret'] == $client_secret; + } + + public function isPublicClient($client_id) + { + if (!$client = $this->getClientDetails($client_id)) { + return false; + } + + return empty($client['client_secret']); + } + + /* ClientInterface */ + public function getClientDetails($client_id) + { + return $this->getValue($this->config['client_key'] . $client_id); + } + + public function setClientDetails($client_id, $client_secret = null, $redirect_uri = null, $grant_types = null, $scope = null, $user_id = null) + { + return $this->setValue( + $this->config['client_key'] . $client_id, + compact('client_id', 'client_secret', 'redirect_uri', 'grant_types', 'scope', 'user_id') + ); + } + + public function checkRestrictedGrantType($client_id, $grant_type) + { + $details = $this->getClientDetails($client_id); + if (isset($details['grant_types'])) { + $grant_types = explode(' ', $details['grant_types']); + + return in_array($grant_type, (array) $grant_types); + } + + // if grant_types are not defined, then none are restricted + return true; + } + + /* RefreshTokenInterface */ + public function getRefreshToken($refresh_token) + { + return $this->getValue($this->config['refresh_token_key'] . $refresh_token); + } + + public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null) + { + return $this->setValue( + $this->config['refresh_token_key'] . $refresh_token, + compact('refresh_token', 'client_id', 'user_id', 'expires', 'scope'), + $expires + ); + } + + public function unsetRefreshToken($refresh_token) + { + $result = $this->expireValue($this->config['refresh_token_key'] . $refresh_token); + + return $result > 0; + } + + /* AccessTokenInterface */ + public function getAccessToken($access_token) + { + return $this->getValue($this->config['access_token_key'].$access_token); + } + + public function setAccessToken($access_token, $client_id, $user_id, $expires, $scope = null) + { + return $this->setValue( + $this->config['access_token_key'].$access_token, + compact('access_token', 'client_id', 'user_id', 'expires', 'scope'), + $expires + ); + } + + public function unsetAccessToken($access_token) + { + $result = $this->expireValue($this->config['access_token_key'] . $access_token); + + return $result > 0; + } + + /* ScopeInterface */ + public function scopeExists($scope) + { + $scope = explode(' ', $scope); + + $result = $this->getValue($this->config['scope_key'].'supported:global'); + + $supportedScope = explode(' ', (string) $result); + + return (count(array_diff($scope, $supportedScope)) == 0); + } + + public function getDefaultScope($client_id = null) + { + if (is_null($client_id) || !$result = $this->getValue($this->config['scope_key'].'default:'.$client_id)) { + $result = $this->getValue($this->config['scope_key'].'default:global'); + } + + return $result; + } + + public function setScope($scope, $client_id = null, $type = 'supported') + { + if (!in_array($type, array('default', 'supported'))) { + throw new \InvalidArgumentException('"$type" must be one of "default", "supported"'); + } + + if (is_null($client_id)) { + $key = $this->config['scope_key'].$type.':global'; + } else { + $key = $this->config['scope_key'].$type.':'.$client_id; + } + + return $this->setValue($key, $scope); + } + + /*JWTBearerInterface */ + public function getClientKey($client_id, $subject) + { + if (!$jwt = $this->getValue($this->config['jwt_key'] . $client_id)) { + return false; + } + + if (isset($jwt['subject']) && $jwt['subject'] == $subject) { + return $jwt['key']; + } + + return null; + } + + public function setClientKey($client_id, $key, $subject = null) + { + return $this->setValue($this->config['jwt_key'] . $client_id, array( + 'key' => $key, + 'subject' => $subject + )); + } + + public function getClientScope($client_id) + { + if (!$clientDetails = $this->getClientDetails($client_id)) { + return false; + } + + if (isset($clientDetails['scope'])) { + return $clientDetails['scope']; + } + + return null; + } + + public function getJti($client_id, $subject, $audience, $expiration, $jti) + { + //TODO: Needs redis implementation. + throw new \Exception('getJti() for the Redis driver is currently unimplemented.'); + } + + public function setJti($client_id, $subject, $audience, $expiration, $jti) + { + //TODO: Needs redis implementation. + throw new \Exception('setJti() for the Redis driver is currently unimplemented.'); + } +} diff --git a/modules/yandexhome/lib/OAuth2/Storage/RefreshTokenInterface.php b/modules/yandexhome/lib/OAuth2/Storage/RefreshTokenInterface.php new file mode 100644 index 0000000..e6407e4 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Storage/RefreshTokenInterface.php @@ -0,0 +1,82 @@ + + */ +interface RefreshTokenInterface +{ + /** + * Grant refresh access tokens. + * + * Retrieve the stored data for the given refresh token. + * + * Required for OAuth2::GRANT_TYPE_REFRESH_TOKEN. + * + * @param $refresh_token + * Refresh token to be check with. + * + * @return + * An associative array as below, and NULL if the refresh_token is + * invalid: + * - refresh_token: Refresh token identifier. + * - client_id: Client identifier. + * - user_id: User identifier. + * - expires: Expiration unix timestamp, or 0 if the token doesn't expire. + * - scope: (optional) Scope values in space-separated string. + * + * @see http://tools.ietf.org/html/rfc6749#section-6 + * + * @ingroup oauth2_section_6 + */ + public function getRefreshToken($refresh_token); + + /** + * Take the provided refresh token values and store them somewhere. + * + * This function should be the storage counterpart to getRefreshToken(). + * + * If storage fails for some reason, we're not currently checking for + * any sort of success/failure, so you should bail out of the script + * and provide a descriptive fail message. + * + * Required for OAuth2::GRANT_TYPE_REFRESH_TOKEN. + * + * @param $refresh_token + * Refresh token to be stored. + * @param $client_id + * Client identifier to be stored. + * @param $user_id + * User identifier to be stored. + * @param $expires + * Expiration timestamp to be stored. 0 if the token doesn't expire. + * @param $scope + * (optional) Scopes to be stored in space-separated string. + * + * @ingroup oauth2_section_6 + */ + public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null); + + /** + * Expire a used refresh token. + * + * This is not explicitly required in the spec, but is almost implied. + * After granting a new refresh token, the old one is no longer useful and + * so should be forcibly expired in the data store so it can't be used again. + * + * If storage fails for some reason, we're not currently checking for + * any sort of success/failure, so you should bail out of the script + * and provide a descriptive fail message. + * + * @param $refresh_token + * Refresh token to be expired. + * + * @ingroup oauth2_section_6 + */ + public function unsetRefreshToken($refresh_token); +} diff --git a/modules/yandexhome/lib/OAuth2/Storage/ScopeInterface.php b/modules/yandexhome/lib/OAuth2/Storage/ScopeInterface.php new file mode 100644 index 0000000..a829226 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Storage/ScopeInterface.php @@ -0,0 +1,46 @@ + + */ +interface ScopeInterface +{ + /** + * Check if the provided scope exists. + * + * @param $scope + * A space-separated string of scopes. + * + * @return + * TRUE if it exists, FALSE otherwise. + */ + public function scopeExists($scope); + + /** + * The default scope to use in the event the client + * does not request one. By returning "false", a + * request_error is returned by the server to force a + * scope request by the client. By returning "null", + * opt out of requiring scopes + * + * @param $client_id + * An optional client id that can be used to return customized default scopes. + * + * @return + * string representation of default scope, null if + * scopes are not defined, or false to force scope + * request by the client + * + * ex: + * 'default' + * ex: + * null + */ + public function getDefaultScope($client_id = null); +} diff --git a/modules/yandexhome/lib/OAuth2/Storage/UserCredentialsInterface.php b/modules/yandexhome/lib/OAuth2/Storage/UserCredentialsInterface.php new file mode 100644 index 0000000..f550579 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/Storage/UserCredentialsInterface.php @@ -0,0 +1,52 @@ + + */ +interface UserCredentialsInterface +{ + /** + * Grant access tokens for basic user credentials. + * + * Check the supplied username and password for validity. + * + * You can also use the $client_id param to do any checks required based + * on a client, if you need that. + * + * Required for OAuth2::GRANT_TYPE_USER_CREDENTIALS. + * + * @param $username + * Username to be check with. + * @param $password + * Password to be check with. + * + * @return + * TRUE if the username and password are valid, and FALSE if it isn't. + * Moreover, if the username and password are valid, and you want to + * + * @see http://tools.ietf.org/html/rfc6749#section-4.3 + * + * @ingroup oauth2_section_4 + */ + public function checkUserCredentials($username, $password); + + /** + * @param string $username - username to get details for + * @return array|false - the associated "user_id" and optional "scope" values + * This function MUST return FALSE if the requested user does not exist or is + * invalid. "scope" is a space-separated list of restricted scopes. + * @code + * return array( + * "user_id" => USER_ID, // REQUIRED user_id to be stored with the authorization code or access token + * "scope" => SCOPE // OPTIONAL space-separated list of restricted scopes + * ); + * @endcode + */ + public function getUserDetails($username); +} diff --git a/modules/yandexhome/lib/OAuth2/TokenType/Bearer.php b/modules/yandexhome/lib/OAuth2/TokenType/Bearer.php new file mode 100644 index 0000000..8ac8596 --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/TokenType/Bearer.php @@ -0,0 +1,130 @@ +config = array_merge(array( + 'token_param_name' => 'access_token', + 'token_bearer_header_name' => 'Bearer', + ), $config); + } + + public function getTokenType() + { + return 'Bearer'; + } + + /** + * Check if the request has supplied token + * + * @see https://github.com/bshaffer/oauth2-server-php/issues/349#issuecomment-37993588 + */ + public function requestHasToken(RequestInterface $request) + { + $headers = $request->headers('AUTHORIZATION'); + + // check the header, then the querystring, then the request body + return !empty($headers) || (bool) ($request->request($this->config['token_param_name'])) || (bool) ($request->query($this->config['token_param_name'])); + } + + /** + * This is a convenience function that can be used to get the token, which can then + * be passed to getAccessTokenData(). The constraints specified by the draft are + * attempted to be adheared to in this method. + * + * As per the Bearer spec (draft 8, section 2) - there are three ways for a client + * to specify the bearer token, in order of preference: Authorization Header, + * POST and GET. + * + * NB: Resource servers MUST accept tokens via the Authorization scheme + * (http://tools.ietf.org/html/rfc6750#section-2). + * + * @todo Should we enforce TLS/SSL in this function? + * + * @see http://tools.ietf.org/html/rfc6750#section-2.1 + * @see http://tools.ietf.org/html/rfc6750#section-2.2 + * @see http://tools.ietf.org/html/rfc6750#section-2.3 + * + * Old Android version bug (at least with version 2.2) + * @see http://code.google.com/p/android/issues/detail?id=6684 + * + */ + public function getAccessTokenParameter(RequestInterface $request, ResponseInterface $response) + { + $headers = $request->headers('AUTHORIZATION'); + + /** + * Ensure more than one method is not used for including an + * access token + * + * @see http://tools.ietf.org/html/rfc6750#section-3.1 + */ + $methodsUsed = !empty($headers) + (bool) ($request->query($this->config['token_param_name'])) + (bool) ($request->request($this->config['token_param_name'])); + if ($methodsUsed > 1) { + $response->setError(400, 'invalid_request', 'Only one method may be used to authenticate at a time (Auth header, GET or POST)'); + + return null; + } + + /** + * If no authentication is provided, set the status code + * to 401 and return no other error information + * + * @see http://tools.ietf.org/html/rfc6750#section-3.1 + */ + if ($methodsUsed == 0) { + $response->setStatusCode(401); + + return null; + } + + // HEADER: Get the access token from the header + if (!empty($headers)) { + if (!preg_match('/' . $this->config['token_bearer_header_name'] . '\s(\S+)/i', $headers, $matches)) { + $response->setError(400, 'invalid_request', 'Malformed auth header'); + + return null; + } + + return $matches[1]; + } + + if ($request->request($this->config['token_param_name'])) { + // // POST: Get the token from POST data + if (!in_array(strtolower($request->server('REQUEST_METHOD')), array('post', 'put'))) { + $response->setError(400, 'invalid_request', 'When putting the token in the body, the method must be POST or PUT', '#section-2.2'); + + return null; + } + + $contentType = $request->server('CONTENT_TYPE'); + if (false !== $pos = strpos($contentType, ';')) { + $contentType = substr($contentType, 0, $pos); + } + + if ($contentType !== null && $contentType != 'application/x-www-form-urlencoded') { + // IETF specifies content-type. NB: Not all webservers populate this _SERVER variable + // @see http://tools.ietf.org/html/rfc6750#section-2.2 + $response->setError(400, 'invalid_request', 'The content type for POST requests must be "application/x-www-form-urlencoded"'); + + return null; + } + + return $request->request($this->config['token_param_name']); + } + + // GET method + return $request->query($this->config['token_param_name']); + } +} diff --git a/modules/yandexhome/lib/OAuth2/TokenType/Mac.php b/modules/yandexhome/lib/OAuth2/TokenType/Mac.php new file mode 100644 index 0000000..fe6a86a --- /dev/null +++ b/modules/yandexhome/lib/OAuth2/TokenType/Mac.php @@ -0,0 +1,22 @@ + $dsn, 'username' => DB_USER, 'password' => DB_PASSWORD)); + +$server = new OAuth2\Server($storage, array('access_lifetime' => 12*3600)); + +$server->addGrantType(new OAuth2\GrantType\ClientCredentials($storage)); + +$server->addGrantType(new OAuth2\GrantType\AuthorizationCode($storage)); + +$server->addGrantType(new OAuth2\GrantType\RefreshToken($storage, array('always_issue_new_refresh_token' => true))); diff --git a/modules/yandexhome/smarthome.php b/modules/yandexhome/smarthome.php new file mode 100644 index 0000000..8e9557d --- /dev/null +++ b/modules/yandexhome/smarthome.php @@ -0,0 +1,128 @@ +server['PATH_INFO'])) { + + // Периодическая проверка доступности Endpoint URL нашего провайдера (PING). + + if ($yandexhome->debug_ping) { + $yandexhome->WriteLog($yandexhome->IncomingRequestFormat($request)); + $yandexhome->WriteLog('smarthome.php <<< PING'); + } + + if (!empty($request->request)) { + + $content = $request->request; + + // Если действительно команда PING. + if ($content['request']['command'] == 'ping' || $content['request']['original_utterance'] == 'ping') { + // Отвечаем PONG (HTTP/1.1 200 OK). + $response = json_encode([ + 'version' => API_VERSION, + 'session' => [ + 'session_id' => $content['session']['session_id'], + 'message_id' => $content['session']['message_id'], + 'user_id' => $content['session']['user_id'] + ], + 'response' => [ + 'text' => 'pong' + ] + ]); + + header('HTTP/1.1 200 OK'); + header('Content-Type: application/json'); + echo $response; + + if ($yandexhome->debug_ping) { + $yandexhome->WriteLog('smarthome.php >>> PONG'); + $yandexhome->WriteLog('smarthome.php >>> ' . $response); + } + } + } + header('HTTP/1.1 400 Bad Request'); + die; +} else if (isset($request->server['PATH_INFO']) && $request->server['PATH_INFO'] != '') { + + // Входящий API-запрос на Endpoint URL нашего провайдера. + + $yandexhome->WriteLog($yandexhome->IncomingRequestFormat($request)); + + // Обработка запроса к нашему API и аутентификация токена доступа. + if (!$server->verifyResourceRequest($request)) { + + // Если запрос не аутентифицирован, отвечаем 401 Unauthorized и завершаем работу. + + $server->getResponse()->send(); + + $yandexhome->WriteLog('smarthome.php >>> ' . $server->getResponse()); + + echo json_encode(array('success' => false, 'message' => 'Access is denied.')); + + $yandexhome->WriteLog('smarthome.php >>> ' . json_encode(array('success' => false, 'message' => 'Access is denied.'))); + + die; + + } else { + + // Если аутентификация успешна, то обрабатываем запрос. + + $content = $request->request; + $content['request_id'] = $request->headers['X_REQUEST_ID']; + $path = $request->server['PATH_INFO']; + $response = ''; + + $api_query = explode('/', trim($path, '/')); + + // Если верные версия API и формат запроса. + if (array_shift($api_query) == 'v'.API_VERSION && array_shift($api_query) == 'user') { + if ($api_query[0] == 'devices') { + if (isset($api_query[1])) { + if ($api_query[1] == 'query') { + // Обработка запроса информации о состоянии устройств. + $response = $yandexhome->HandleQueryRequest($content); + } else if ($api_query[1] == 'action') { + // Обработка запроса на изменение состояния устройств. + $response = $yandexhome->HandleExecuteRequest($content); + } + } else { + // Обработка запроса информации о перечне устройств. + $response = $yandexhome->HandleSyncRequest($content); + } + } else if ($api_query[0] == 'unlink') { + // Обработка запроса на разъединение аккаунтов. + $response = $yandexhome->HandleUnlinkRequest($content); + } else { + $yandexhome->WriteLog('smarthome.php === Unsupported API version!'); + header('HTTP/1.1 400 Bad Request'); + } + } else { + $yandexhome->WriteLog('smarthome.php === Unsupported API version!'); + header('HTTP/1.1 400 Bad Request'); + } + + header('HTTP/1.1 200 OK'); + header('Content-Type: application/json'); + echo $response; + + $yandexhome->WriteLog('smarthome.php >>> ' . $response); + + die; + } +} else { + header('HTTP/1.1 400 Bad Request'); +} diff --git a/modules/yandexhome/structure.inc.php b/modules/yandexhome/structure.inc.php new file mode 100644 index 0000000..f6a413d --- /dev/null +++ b/modules/yandexhome/structure.inc.php @@ -0,0 +1,157 @@ +devices_type = [ + 'media_device' => [ + 'device_name' => 'media_device', + 'description' => 'Аудио и видеотехника' + ], + 'thermostat.ac' => [ + 'device_name' => 'thermostat.ac', + 'description' => 'Кондиционер' + ], + 'cooking' => [ + 'device_name' => 'cooking', + 'description' => 'Кухонная техника' + ], + 'other' => [ + 'device_name' => 'other', + 'description' => 'Остальные устройства' + ], + 'switch' => [ + 'device_name' => 'switch', + 'description' => 'Переключатель' + ], + 'socket' => [ + 'device_name' => 'socket', + 'description' => 'Розетка' + ], + 'light' => [ + 'device_name' => 'light', + 'description' => 'Свет' + ], + 'media_device.tv' => [ + 'device_name' => 'media_device.tv', + 'description' => 'Телевизор' + ], + 'thermostat' => [ + 'device_name' => 'thermostat', + 'description' => 'Термостат' + ], + 'cooking.kettle' => [ + 'device_name' => 'cooking.kettle', + 'description' => 'Чайник' + ], + +]; + +$this->devices_instance = [ + 'on' => [ + 'instance_name' => 'on', + 'description' => 'Включить/выключить', + 'capabilitie' => 'on_off', + 'default_value' => 0 + ],/* + 'volume' => [ + 'instance_name' => 'volume', + 'description' => 'Громкость', + 'capabilitie' => 'range', + 'default_value' => 10, + 'parameters' => [ + 'unit' => 'unit.percent', + 'range' => [ + 'min' => 0, + 'max' => 100, + 'precision' => 1 + ] + ] + ], + 'channel' => [ + 'instance_name' => 'channel', + 'description' => 'Канал', + 'capabilitie' => 'range' + ], + 'temperature' => [ + 'instance_name' => 'temperature', + 'description' => 'Температура', + 'capabilitie' => 'range' + ],*/ + 'temperature_k' => [ + 'instance_name' => 'temperature_k', + 'description' => 'Цветовая температура', + 'capabilitie' => 'color_setting', + 'default_value' => 4500, + 'parameters' => [ + 'temperature_k' => [ + 'min' => 2700, + 'max' => 9000, + 'precision' => 1 + ] + ] + ],/* + 'thermostat' => [ + 'instance_name' => 'thermostat', + 'description' => 'Режим работы', + 'capabilitie' => 'mode' + ], + 'mute' => [ + 'instance_name' => 'mute', + 'description' => 'Режим без звука', + 'capabilitie' => 'toggle' + ], + 'fan_speed' => [ + 'instance_name' => 'fan_speed', + 'description' => 'Скорость вентиляции', + 'capabilitie' => 'mode' + ], + 'hsv' => [ + 'instance_name' => 'hsv', + 'description' => 'Цвет в формате HSV', + 'capabilitie' => 'color_setting' + ],*/ + 'rgb' => [ + 'instance_name' => 'rgb', + 'description' => 'Цвет в формате RGB', + 'capabilitie' => 'color_setting', + 'default_value' => 16777215, + 'parameters' => [ + 'color_model' => 'rgb' + ] + ], + 'brightness' => [ + 'instance_name' => 'brightness', + 'description' => 'Яркость', + 'capabilitie' => 'range', + 'default_value' => 50, + 'parameters' => [ + 'unit' => 'unit.percent', + 'range' => [ + 'min' => 1, + 'max' => 100, + 'precision' => 1 + ] + ] + ] +]; \ No newline at end of file diff --git a/modules/yandexhome/token.php b/modules/yandexhome/token.php new file mode 100644 index 0000000..3eb76cf --- /dev/null +++ b/modules/yandexhome/token.php @@ -0,0 +1,14 @@ +WriteLog($yandexhome->IncomingRequestFormat($request)); + +// Обработка запроса на токен доступа OAuth2.0 и отправка ответа клиенту. +$server->handleTokenRequest($request)->send(); + +// Пишем в лог ответ. +$yandexhome->WriteLog('token.php >>> ' . $server->getResponse()); diff --git a/modules/yandexhome/yandexhome.class.php b/modules/yandexhome/yandexhome.class.php new file mode 100644 index 0000000..aca5773 --- /dev/null +++ b/modules/yandexhome/yandexhome.class.php @@ -0,0 +1,885 @@ + +* @copyright 2019 Agaphonov Dmitri aka skysilver (c) +* @version 0.1b 2019/06/09 +*/ + +const PREFIX_CAPABILITIES = 'devices.capabilities.'; +const PREFIX_TYPES = 'devices.types.'; +const API_VERSION = '1.0'; + +class yandexhome extends module +{ + /** + * + * Конструктор класса модуля. + * + */ + function __construct() + { + $this->name = 'yandexhome'; + $this->title = 'Yandex Home'; + $this->module_category = '<#LANG_SECTION_DEVICES#>'; + $this->checkInstalled(); + + $this->getConfig(); + $this->debug = ($this->config['LOG_DEBMES'] == 1) ? true : false; + $this->debug_ping = ($this->config['LOG_PING'] == 1) ? true : false; + + require ('structure.inc.php'); + } + + /** + * + * Сохранение параметров модуля. + * + */ + function saveParams($data = 1) + { + $p = array(); + + if (isset($this->id)) { + $p['id'] = $this->id; + } + + if (isset($this->view_mode)) { + $p['view_mode'] = $this->view_mode; + } + + if (isset($this->edit_mode)) { + $p['edit_mode'] = $this->edit_mode; + } + + if (isset($this->tab)) { + $p['tab'] = $this->tab; + } + + return parent::saveParams($p); + } + + /** + * + * Получение параметров модуля. + * + */ + function getParams() + { + global $id; + global $mode; + global $view_mode; + global $edit_mode; + global $tab; + + if (isset($id)) { + $this->id = $id; + } + + if (isset($mode)) { + $this->mode = $mode; + } + + if (isset($view_mode)) { + $this->view_mode = $view_mode; + } + + if (isset($edit_mode)) { + $this->edit_mode = $edit_mode; + } + + if (isset($tab)) { + $this->tab = $tab; + } + } + + /** + * + * Запуск модуля. + * + */ + function run() + { + global $session; + + $out = array(); + + if ($this->action == 'admin') { + $this->admin($out); + } else { + $this->usual($out); + } + + if (isset($this->owner->action)) { + $out['PARENT_ACTION'] = $this->owner->action; + } + + if (isset($this->owner->name)) { + $out['PARENT_NAME'] = $this->owner->name; + } + + $out['VIEW_MODE'] = $this->view_mode; + $out['EDIT_MODE'] = $this->edit_mode; + $out['ACTION'] = $this->action; + $out['MODE'] = $this->mode; + $out['TAB'] = $this->tab; + + $this->data = $out; + + $p = new parser(DIR_TEMPLATES . $this->name . '/' . $this->name . '.html', $this->data, $this); + $this->result = $p->result; + } + + /** + * + * Админка модуля. + * + */ + function admin(&$out) + { + $this->getConfig(); + + $out['USER_NAME'] = $this->config['USER_NAME']; + $out['USER_PASS'] = $this->config['USER_PASS']; + $out['CLIENT_ID'] = $this->config['CLIENT_ID']; + $out['CLIENT_KEY'] = $this->config['CLIENT_KEY']; + $out['LOG_DEBMES'] = $this->config['LOG_DEBMES']; + $out['LOG_PING'] = $this->config['LOG_PING']; + $out['VIEW_STYLE'] = $this->config['VIEW_STYLE']; + $out['USER_ID'] = md5($this->config['USER_NAME']); + + if ($this->view_mode == 'update_settings') { + $this->config['USER_NAME'] = gr('user_name'); + $this->config['USER_PASS'] = gr('user_pass'); + $this->config['CLIENT_ID'] = gr('client_id'); + $this->config['CLIENT_KEY'] = gr('client_key'); + $this->config['LOG_DEBMES'] = gr('log_debmes'); + $this->config['LOG_PING'] = gr('log_ping'); + $this->config['VIEW_STYLE'] = gr('view_style'); + + $this->saveConfig(); + + $res = SQLSelectOne('SELECT * FROM yandexhome_oauth'); + + if ($res['CLIENT_ID']) { + $res['CLIENT_ID'] = $this->config['CLIENT_ID']; + $res['CLIENT_SECRET'] = $this->config['CLIENT_KEY']; + SQLUpdate('yandexhome_oauth', $res); + } else { + $res = array(); + $res['CLIENT_ID'] = $this->config['CLIENT_ID']; + $res['CLIENT_SECRET'] = $this->config['CLIENT_KEY']; + SQLInsert('yandexhome_oauth', $res); + } + + $this->redirect('?'); + } + + if ($this->view_mode == '' || $this->view_mode == 'search_yandexhome_devices') { + $oauth = SQLSelectOne('SELECT * FROM yandexhome_oauth'); + if ($oauth['CLIENT_ID']) { + $out['ACCESS_TOKEN'] = $oauth['ACCESS_TOKEN']; + $out['ACCESS_TOKEN_EXPIRES'] = $oauth['ACCESS_TOKEN_EXPIRES']; + $out['REFRESH_TOKEN'] = $oauth['REFRESH_TOKEN']; + $out['REFRESH_TOKEN_EXPIRES'] = $oauth['REFRESH_TOKEN_EXPIRES']; + $out['AUTHORIZATION_CODE'] = $oauth['AUTHORIZATION_CODE']; + $out['AUTHORIZATION_CODE_EXPIRES'] = $oauth['AUTHORIZATION_CODE_EXPIRES']; + $out['REDIRECT_URI'] = $oauth['REDIRECT_URI']; + } + $this->search_yandexhome_devices($out); + } + + if ($this->view_mode == 'addnew_yandexhome_devices') { + $this->addnew_yandexhome_devices($out); + } + + if ($this->view_mode == 'edit_yandexhome_devices') { + $this->edit_yandexhome_devices($out, $this->id); + } + + if ($this->view_mode == 'delete_yandexhome_devices') { + $this->delete_yandexhome_devices($this->id); + $this->redirect('?'); + } + } + + /** + * + * Фронтенд модуля (http api). + * + */ + function usual(&$out) + { + if ($this->ajax) { + + $op = gr('op'); + + if ($op == 'generateClientId') { + $client_id = sprintf('%04X%04X%04X-%04X%04X', mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(16384, 20479), mt_rand(32768, 49151)); + $this->WriteLog("Generate new Client ID {$client_id}"); + exit (strtolower($client_id)); + } else if ($op == 'generateClientKey') { + $client_key = sprintf('%04X%04X-%04X-%04X-%04X-%04X%04X%04X', mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(16384, 20479), mt_rand(32768, 49151), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535)); + $this->WriteLog("Generate new Client KEY {$client_key}"); + exit (strtolower($client_key)); + } else if ($op == 'sendSyncRequest') { + // TODO + exit ('OK'); + } + + echo 'OK'; + } + } + + /** + * + * Список устройств на главной странице модуля. + * + */ + function search_yandexhome_devices(&$out) + { + $res = SQLSelect("SELECT * FROM yandexhome_devices ORDER BY ROOM,TITLE"); + $loc_title = ''; + if ($res[0]['ID']) { + $total = count($res); + for($i = 0; $i < $total; $i++) { + $res[$i]['ICON'] = strtolower($res[$i]['TYPE']); + $res[$i]['TYPE_TITLE'] = $this->devices_type[$res[$i]['TYPE']]['description']; + $res[$i]['VIEW_STYLE'] = $this->config['VIEW_STYLE']; + if ($res[$i]['ROOM'] != $loc_title) { + $res[$i]['NEW_ROOM'] = 1; + $loc_title = $res[$i]['ROOM']; + } + $res[$i]['LAST_DEV'] = 0; + if (isset($res[$i]['NEW_ROOM']) || $i == $total-1) { + if ($i == $total-1) { + $res[$i-1]['LAST_DEV'] = 1; + $res[$i]['LAST_DEV'] = 1; + } else if ($i > 0) { + $res[$i-1]['LAST_DEV'] = 1; + } + } + $traits = json_decode($res[$i]['TRAITS'], true); + if (is_array($traits) && count($traits) > 0) { + foreach ($traits as $trait) { + $res[$i]['TRAITS_LIST'] .= $trait['type'] . '
'; + } + } + } + $out['RESULT'] = $res; + } + } + + /** + * + * Интерфейс добавления устройства. + * + */ + function addnew_yandexhome_devices(&$out) + { + $out['DEVICES_TYPE'] = array_values($this->devices_type); + $out['LOCATIONS'] = SQLSelect('SELECT ID, TITLE FROM locations ORDER BY TITLE'); + + if ($this->mode == 'addnew') { + $ok = 1; + + $rec['TITLE'] = gr('title'); + if ($rec['TITLE'] == '') { + $out['ERR_TITLE'] = 1; + $ok = 0; + } + + $rec['TYPE'] = gr('type'); + if ($rec['TYPE'] == '') { + $out['ERR_TYPE'] = 1; + $ok = 0; + } + + $rec['ROOM'] = gr('location'); + + if ($ok) { + $new_rec = 1; + $rec['ID'] = SQLInsert('yandexhome_devices', $rec); + $out['OK'] = 1; + } else { + $out['ERR'] = 1; + } + + if (is_array($rec)) { + foreach($rec as $k=>$v) { + if (!is_array($v)) { + $rec[$k] = htmlspecialchars($v); + } + } + } + + if ($ok) { + $this->redirect('?'); + } + } + outHash($rec, $out); + } + + /** + * + * Интерфейс редактирования настроек устройства и просмотра его данных. + * + */ + function edit_yandexhome_devices(&$out, $id) + { + // ID, TITLE, TYPE, ROOM, TRAITS (json), CONFIG (json) + $rec = SQLSelectOne("SELECT * FROM yandexhome_devices WHERE ID='{$id}'"); + + // Поддерживаемые типы устройств. + $out['DEVICES_TYPE'] = array_values($this->devices_type); + + // Поддерживаемые метрики (возможности) устройств. + $out['DEVICES_INSTANCE'] = array_values($this->devices_instance); + + // Список местоположений (комнат) в системе. + $out['LOCATIONS'] = SQLSelect('SELECT ID, TITLE FROM locations ORDER BY TITLE'); + + // Список объектов в системе. + $objs = SQLSelect('SELECT TITLE, DESCRIPTION FROM objects ORDER BY TITLE'); // CLASS_ID + $out['OBJECTS'] = json_encode($objs, JSON_UNESCAPED_UNICODE); + + // Сохранение конфигурации устройства. + if ($this->mode == 'update') { + + $ok = 1; + + // Название устройства (обязательное поле). + $rec['TITLE'] = gr('title'); + if ($rec['TITLE'] == '') { + $out['ERR_TITLE'] = 1; + $ok = 0; + } + + // Тип устройства (обязательное поле). + $rec['TYPE'] = gr('type'); + if ($rec['TYPE'] == '') { + $out['ERR_TYPE'] = 1; + $ok = 0; + } + + // Местоположение устройства (опционально). + $rec['ROOM'] = gr('location'); + + // Описание устройства (опционально). + $rec['DESCRIPTION'] = gr('description'); + + // Производитель устройства (опционально). + $rec['MANUFACTURER'] = gr('manufacturer'); + + // Модель устройства (опционально). + $rec['MODEL'] = gr('model'); + + // Версия ПО устройства (опционально). + $rec['SW_VERSION'] = gr('sw_version'); + + // Версия АО устройства (опционально). + $rec['HW_VERSION'] = gr('hw_version'); + + // Метрики (в т.ч. привязанные к ним объекты и свойства) устройства (обязательное поле). + // Старые (в формате массива). + $old_dev_traits = json_decode($rec['TRAITS'], true); + // Новые (в формате json). + $rec['TRAITS'] = gr('traits_json'); + // Новые (в формате json). + $new_dev_traits = json_decode($rec['TRAITS'], true); + if ($rec['TRAITS'] == '' || count($new_dev_traits) == 0) { + $out['ERR_TRAITS'] = 1; + $ok = 0; + } + + // Если обязательные поля заполнены, то сохраняем конфигурацию устройства. + if ($ok) { + if ($rec['ID']) { + // Собираем JSON-конфиг устройства согласно формату API Yandex Home. + $traits = []; + if (is_array($new_dev_traits)) { + foreach ($new_dev_traits as $trait) { + $parameters = []; + $trait_type = PREFIX_CAPABILITIES . $this->devices_instance[$trait['type']]['capabilitie']; + if (isset($this->devices_instance[$trait['type']]['parameters'])) { + $parameters = $this->devices_instance[$trait['type']]['parameters']; + if ($trait['type'] != 'rgb' && $trait['type'] != 'temperature_k') { + $parameters['instance'] = $trait['type']; + } + } else { + $parameters['instance'] = $trait['type']; + } + $check = false; + foreach ($traits as $key => $item) { + if ($item['type'] == $trait_type) { + $check = $key; + break; + } + } + if ($check) { + $traits[$check]['parameters'] = array_merge ($traits[$check]['parameters'], $parameters); + } else { + $traits[] = [ + 'type' => $trait_type, + 'parameters' => $parameters + ]; + } + } + } + $rec['CONFIG'] = json_encode([ + 'id' => $rec['ID'], + 'name' => $rec['TITLE'], + 'type' => PREFIX_TYPES . $rec['TYPE'], + 'room' => $rec['ROOM'], + 'description' => $rec['DESCRIPTION'], + 'capabilities' => $traits, + 'device_info' => [ + 'manufacturer' => $rec['MANUFACTURER'], + 'model' => $rec['MODEL'] . ' via MajorDoMo', + 'hw_version' => $rec['HW_VERSION'], + 'sw_version' => $rec['SW_VERSION'] + ] + ], JSON_UNESCAPED_UNICODE); + + // Обрабатываем набор метрик и привязанные к ним объекты и свойства. + if (is_array($new_dev_traits)) { + if (is_array($old_dev_traits)) { + // Если удалили метрику, у которой были привязанные объект и свойство, то удаляем линк. + $del_dev_traits = array_diff_assoc($old_dev_traits, $new_dev_traits); + if (!empty($del_dev_traits)) { + foreach ($del_dev_traits as $trait) { + $linked_object = $trait['linked_object']; + $linked_property = $trait['linked_property']; + if ($linked_object != '' && $linked_property != '') { + removeLinkedProperty($linked_object, $linked_property, $this->name); + $this->WriteLog("removeLinkedProperty for $linked_object and $linked_property"); + } + } + } + } + + foreach ($new_dev_traits as $trait) { + // Новые объект и свойство метрики. + $linked_object = $trait['linked_object']; + $linked_property = $trait['linked_property']; + + // Предыдущие объект и свойство метрики. + if (isset($old_dev_traits[$trait['type']])) { + $old_linked_object = $old_dev_traits[$trait['type']]['linked_object']; + $old_linked_property = $old_dev_traits[$trait['type']]['linked_property']; + } else { + $old_linked_object = ''; + $old_linked_property = ''; + } + + // Если юзер удалил привязанное свойство, но забыл про объект, то очищаем его. + if ($linked_object != '' && $linked_property == '') { + $linked_object = ''; + $new_dev_traits[$trait['type']]['linked_object'] = ''; + } + + // Если юзер удалил только привязанный объект, то свойство тоже очищаем. + if ($linked_object == '' && $linked_property != '') { + $linked_property = ''; + $new_dev_traits[$trait['type']]['linked_property'] = ''; + } + + // Если предыдущие привязанные объект и свойство не пустые и не совпадают с новыми, то удаляем линк. + if ($old_linked_object !='' && $old_linked_property != '' && ($linked_object.$linked_property != $old_linked_object.$old_linked_property)) { + removeLinkedProperty($old_linked_object, $old_linked_property, $this->name); + $this->WriteLog("removeLinkedProperty for $old_linked_object and $old_linked_property"); + } + + // Если поля привязанного объекта и свойства не пустые и не совпадают с предыдущими, то проставляем линк. + if ($linked_object != '' && $linked_property != '' && ($linked_object.$linked_property != $old_linked_object.$old_linked_property)) { + addLinkedProperty($linked_object, $linked_property, $this->name); + $this->WriteLog("addLinkedProperty for $linked_object and $linked_property"); + } + } + $rec['TRAITS'] = json_encode($new_dev_traits, JSON_UNESCAPED_UNICODE); + } + // Обновляем запись об устройстве в БД. + SQLUpdate('yandexhome_devices', $rec); + } + $out['OK'] = 1; + } else { + $out['ERR'] = 1; + } + } + outHash($rec, $out); + } + + /** + * + * Удаление устройства из модуля. + * + */ + function delete_yandexhome_devices($id) + { + $this->DeleteLinkedProperties($id); + + SQLExec("DELETE FROM yandexhome_devices WHERE ID='{$id}'"); + } + + /** + * + * Обработка событий смены значений привязанных к метрикам свойств объектов. + * + */ + function PropertySetHandle($object, $property, $value) + { + // TODO: отправка статусов в Yandex по факту изменения свойства объекта (если появится поддержка в API). + + $this->WriteLog("PropertySetHandle for object '$object' and property '$property' and value=$value"); + } + + /** + * + * Обработка запроса информации о перечне устройств. + * + */ + function HandleSyncRequest($content) + { + $this->WriteLog('Incoming sync request'); + + $res = SQLSelect("SELECT CONFIG FROM yandexhome_devices WHERE CONFIG!='' ORDER BY ID"); + + foreach ($res as $device) { + $devices[] = json_decode($device['CONFIG']); + } + + $response = json_encode([ + 'request_id' => $content['request_id'], + 'payload' => [ + 'user_id' => md5($this->config['USER_NAME']), + 'devices' => $devices + ] + ], JSON_UNESCAPED_UNICODE); + + return $response; + } + + /** + * + * Обработка запроса информации о состоянии устройств. + * + */ + function HandleQueryRequest($content) + { + $devices = []; + + foreach ($content['devices'] as $device) { + $device_id = $device['id']; + + $this->WriteLog("Incoming query request for device ID$device_id"); + + $rec = SQLSelectOne("SELECT * FROM yandexhome_devices WHERE ID='{$device_id}'"); + + if (is_array($rec) && !empty($rec['TRAITS'])) { + $capabilities = []; + $traits = json_decode($rec['TRAITS'], true); + if (is_array($traits)) { + foreach ($traits as $trait) { + $state['instance'] = $trait['type']; + if ($trait['linked_object'] != '' && $trait['linked_property'] != '') { + $linked_object = $trait['linked_object']; + $linked_property = $trait['linked_property']; + $value = getGlobal("$linked_object.$linked_property"); + $this->WriteLog("Object '$linked_object', property '$linked_property', get value=$value"); + } else { + $value = $this->devices_instance[$trait['type']]['default_value']; + $this->WriteLog('Linked object and property not defined'); + } + switch ($trait['type']) { + case 'on': + $state['value'] = $value ? true : false; + break; + case 'brightness': + $state['value'] = (int)$value; + break; + case 'rgb': + $value = preg_replace('/^#/', '', $value); + $state['value'] = hexdec($value); + break; + case 'temperature_k': + $state['value'] = (int)$value; + break; + case 'channel': + $state['value'] = (int)$value; + break; + case 'temperature': + $state['value'] = (int)$value; + break; + default: + $state['value'] = $value; + break; + } + $capabilities[] = [ + 'type' => PREFIX_CAPABILITIES . $this->devices_instance[$trait['type']]['capabilitie'], + 'state' => $state + ]; + } + $devices[] = [ + 'id' => $device_id, + 'capabilities' => $capabilities + ]; + } + } else { + $devices[] = [ + 'id' => $device_id, + 'error_code' => 'DEVICE_NOT_FOUND', + 'error_message' => 'DEVICE_NOT_FOUND_MSG', + ]; + } + } + + $response = json_encode([ + 'request_id' => $content['request_id'], + 'payload' => [ + 'devices' => $devices + ] + ], JSON_UNESCAPED_UNICODE); + + return $response; + } + + /** + * + * Обработка запросов на управление устройствами. + * + */ + function HandleExecuteRequest($content) + { + $devices = []; + + foreach ($content['payload']['devices'] as $device) { + $device_id = $device['id']; + + $this->WriteLog("Incoming action request for device ID$device_id"); + + $rec = SQLSelectOne("SELECT * FROM yandexhome_devices WHERE ID='{$device_id}'"); + + $capabilities = []; + $state = []; + + foreach ($device['capabilities'] as $capabilitie) { + + $type = str_replace(PREFIX_CAPABILITIES, '', $capabilitie['type']); + $value = $capabilitie['state']['value']; + $instance = $capabilitie['state']['instance']; + + $this->WriteLog("Capabilities type '$type', instance '$instance', value=" . json_encode($value)); + + $linked_object = ''; + $linked_property = ''; + + $error_code = false; + + if (!empty($rec['TRAITS'])) { + $traits = json_decode($rec['TRAITS'], true); + if (is_array($traits) && isset($traits[$instance]) && $traits[$instance]['linked_object'] != '' && $traits[$instance]['linked_property'] != '') { + $linked_object = $traits[$instance]['linked_object']; + $linked_property = $traits[$instance]['linked_property']; + switch ($instance) { + case 'on': + // Конвертируем true/false в 1/0. + $value = ($value === true) ? 1 : 0; + break; + case 'brightness': + // TODO + break; + case 'rgb': + $value = str_pad(dechex($value), 6, '0', STR_PAD_LEFT); + break; + } + if (!$error_code) { + setGlobal("$linked_object.$linked_property", $value, array($this->name => '0')); + $this->WriteLog("Object '$linked_object', property '$linked_property', set value=$value"); + } + } else { + $error_code = 'INVALID_ACTION'; + $error_message = 'INVALID_ACTION_MSG'; + } + } else { + $error_code = 'INVALID_ACTION'; + $error_message = 'INVALID_ACTION_MSG'; + } + + $state['instance'] = $instance; + + if (!$error_code) { + $state['action_result'] = ['status' => 'DONE']; + } else { + $state['action_result'] = [ + 'status' => 'ERROR', + 'error_code' => $error_code, + 'error_message' => $error_message + ]; + } + + $capabilities[] = [ + 'type' => $type, + 'state' => $state + ]; + } + + $devices[] = [ + 'id' => $device_id, + 'capabilities' => $capabilities + ]; + } + + $response = json_encode([ + 'request_id' => $content['request_id'], + 'payload' => [ + 'devices' => $devices + ] + ], JSON_UNESCAPED_UNICODE); + + return $response; + } + + /** + * + * Обработка запроса на разъединение аккаунтов. + * + */ + function HandleUnlinkRequest($content) + { + $this->WriteLog('Incoming unlink request'); + } + + /** + * + * Удаление всех линков на привязанные к метрикам устройства свойства. + * + */ + function DeleteLinkedProperties($id, $properties = false) + { + if (!$properties) { + $properties = SQLSelectOne("SELECT TRAITS FROM yandexhome_devices WHERE ID='{$id}'"); + $properties = json_decode($properties['TRAITS'], true); + } + + if (is_array($properties) && !empty($properties)) { + foreach ($properties as $prop) { + $linked_object = $prop['linked_object']; + $linked_property = $prop['linked_property']; + if ($linked_object != '' && $linked_property != '') { + removeLinkedProperty($linked_object, $linked_property, $this->name); + $this->WriteLog("removeLinkedProperty for $linked_object and $linked_property"); + } + } + } + } + + /** + * + * Запись отладочной информации в DebMes-лог модуля. + * + */ + function WriteLog($msg) + { + if ($this->debug) { + DebMes($msg, $this->name); + } + } + + /** + * + * Форматирование отладочной информации для лога. + * + */ + function IncomingRequestFormat($request) + { + $method = $request->server['REQUEST_METHOD']; + + if (isset($request->server['HTTP_X_FORWARDED_FOR'])) { + $remoteip = $request->server['HTTP_X_FORWARDED_FOR']; + } else { + $remoteip = $request->server['REMOTE_ADDR']; + } + + $script = $request->server['REQUEST_URI']; + + $content = json_encode($request->request); + + $message = "{$method} {$script} {$remoteip} <<< {$content}"; + + return $message; + } + + /** + * + * Процедура установки модуля. + * + */ + function install($data = '') + { + parent::install(); + } + + /** + * + * Процедура удаления модуля. + * + */ + function uninstall() + { + echo '
' . date('H:i:s') . " Uninstall module {$this->name}.
"; + + // Удалим слинкованные свойства объектов у метрик каждого устройства. + echo '
' . date('H:i:s') . ' Delete linked properties.
'; + $devices = SQLSelect("SELECT ID,TRAITS FROM yandexhome_devices"); + if (!empty($devices)) { + foreach ($devices as $device) { + $this->DeleteLinkedProperties($device['ID'], json_decode($device['TRAITS'], true)); + } + } + + // Удаляем таблицы модуля из БД. + echo date('H:i:s') . ' Delete DB tables.
'; + SQLExec('DROP TABLE IF EXISTS yandexhome_devices'); + SQLExec('DROP TABLE IF EXISTS yandexhome_oauth'); + + // Удаляем модуль с помощью "родительской" функции ядра. + echo date('H:i:s') . ' Delete files and remove frome system.
'; + parent::uninstall(); + } + + /** + * + * Процедура создания таблиц модуля в базе данных. + * + */ + function dbInstall($data = '') + { + $data = << + diff --git a/templates/yandexhome/action_admin.html b/templates/yandexhome/action_admin.html new file mode 100644 index 0000000..20685a0 --- /dev/null +++ b/templates/yandexhome/action_admin.html @@ -0,0 +1,76 @@ + +[#if VIEW_MODE=""#] + +
+ + +     + + Настройки +     + + +     + + + + +     + + + +
    
+ + +
   
+ +
+ + + [#inc settings.html#] + + + [#if VIEW_STYLE="table" OR VIEW_STYLE="table_group"#] + [#inc devices_list_table.html#] + [#else#] + [#inc devices_list_panels.html#] + [#endif#] + + + [#inc about.html#] +[#endif#] + + +[#if VIEW_MODE="addnew_yandexhome_devices"#] + [#inc devices_addnew.html#] +[#endif#] + + +[#if VIEW_MODE="edit_yandexhome_devices"#] + [#if ID!=""#] + [#TITLE#][#if ROOM!=""#] ([#ROOM#])[#endif#] + [#inc devices_edit.html#] + [#endif#] +[#endif#] + + +[#if VIEW_MODE="show_help"#] + [#inc ./help/help_ru.html#] +[#endif#] + + diff --git a/templates/yandexhome/action_usual.html b/templates/yandexhome/action_usual.html new file mode 100644 index 0000000..0c7e313 --- /dev/null +++ b/templates/yandexhome/action_usual.html @@ -0,0 +1 @@ + diff --git a/templates/yandexhome/css/bootstrap-select.css b/templates/yandexhome/css/bootstrap-select.css new file mode 100644 index 0000000..bc8c9cc --- /dev/null +++ b/templates/yandexhome/css/bootstrap-select.css @@ -0,0 +1,429 @@ +/*! + * Bootstrap-select v1.13.9 (https://developer.snapappointments.com/bootstrap-select) + * + * Copyright 2012-2019 SnapAppointments, LLC + * Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE) + */ + +select.bs-select-hidden, +.bootstrap-select > select.bs-select-hidden, +select.selectpicker { + display: none !important; +} +.bootstrap-select { + width: 220px \0; + /*IE9 and below*/ + vertical-align: middle; +} +.bootstrap-select > .dropdown-toggle { + position: relative; + width: 100%; + text-align: right; + white-space: nowrap; + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-box-align: center; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; +} +.bootstrap-select > .dropdown-toggle:after { + margin-top: -1px; +} +.bootstrap-select > .dropdown-toggle.bs-placeholder, +.bootstrap-select > .dropdown-toggle.bs-placeholder:hover, +.bootstrap-select > .dropdown-toggle.bs-placeholder:focus, +.bootstrap-select > .dropdown-toggle.bs-placeholder:active { + color: #999; +} +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-primary, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-secondary, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-success, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-danger, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-info, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-dark, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-primary:hover, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-secondary:hover, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-success:hover, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-danger:hover, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-info:hover, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-dark:hover, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-primary:focus, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-secondary:focus, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-success:focus, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-danger:focus, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-info:focus, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-dark:focus, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-primary:active, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-secondary:active, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-success:active, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-danger:active, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-info:active, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-dark:active { + color: rgba(255, 255, 255, 0.5); +} +.bootstrap-select > select { + position: absolute !important; + bottom: 0; + left: 50%; + display: block !important; + width: 0.5px !important; + height: 100% !important; + padding: 0 !important; + opacity: 0 !important; + border: none; + z-index: 0 !important; +} +.bootstrap-select > select.mobile-device { + top: 0; + left: 0; + display: block !important; + width: 100% !important; + z-index: 2 !important; +} +.has-error .bootstrap-select .dropdown-toggle, +.error .bootstrap-select .dropdown-toggle, +.bootstrap-select.is-invalid .dropdown-toggle, +.was-validated .bootstrap-select .selectpicker:invalid + .dropdown-toggle { + border-color: #b94a48; +} +.bootstrap-select.is-valid .dropdown-toggle, +.was-validated .bootstrap-select .selectpicker:valid + .dropdown-toggle { + border-color: #28a745; +} +.bootstrap-select.fit-width { + width: auto !important; +} +.bootstrap-select:not([class*="col-"]):not([class*="form-control"]):not(.input-group-btn) { + width: 220px; +} +.bootstrap-select > select.mobile-device:focus + .dropdown-toggle, +.bootstrap-select .dropdown-toggle:focus { + outline: thin dotted #333333 !important; + outline: 5px auto -webkit-focus-ring-color !important; + outline-offset: -2px; +} +.bootstrap-select.form-control { + margin-bottom: 0; + padding: 0; + border: none; +} +:not(.input-group) > .bootstrap-select.form-control:not([class*="col-"]) { + width: 100%; +} +.bootstrap-select.form-control.input-group-btn { + float: none; + z-index: auto; +} +.form-inline .bootstrap-select, +.form-inline .bootstrap-select.form-control:not([class*="col-"]) { + width: auto; +} +.bootstrap-select:not(.input-group-btn), +.bootstrap-select[class*="col-"] { + float: none; + display: inline-block; + margin-left: 0; +} +.bootstrap-select.dropdown-menu-right, +.bootstrap-select[class*="col-"].dropdown-menu-right, +.row .bootstrap-select[class*="col-"].dropdown-menu-right { + float: right; +} +.form-inline .bootstrap-select, +.form-horizontal .bootstrap-select, +.form-group .bootstrap-select { + margin-bottom: 0; +} +.form-group-lg .bootstrap-select.form-control, +.form-group-sm .bootstrap-select.form-control { + padding: 0; +} +.form-group-lg .bootstrap-select.form-control .dropdown-toggle, +.form-group-sm .bootstrap-select.form-control .dropdown-toggle { + height: 100%; + font-size: inherit; + line-height: inherit; + border-radius: inherit; +} +.bootstrap-select.form-control-sm .dropdown-toggle, +.bootstrap-select.form-control-lg .dropdown-toggle { + font-size: inherit; + line-height: inherit; + border-radius: inherit; +} +.bootstrap-select.form-control-sm .dropdown-toggle { + padding: 0.25rem 0.5rem; +} +.bootstrap-select.form-control-lg .dropdown-toggle { + padding: 0.5rem 1rem; +} +.form-inline .bootstrap-select .form-control { + width: 100%; +} +.bootstrap-select.disabled, +.bootstrap-select > .disabled { + cursor: not-allowed; +} +.bootstrap-select.disabled:focus, +.bootstrap-select > .disabled:focus { + outline: none !important; +} +.bootstrap-select.bs-container { + position: absolute; + top: 0; + left: 0; + height: 0 !important; + padding: 0 !important; +} +.bootstrap-select.bs-container .dropdown-menu { + z-index: 1060; +} +.bootstrap-select .dropdown-toggle .filter-option { + position: static; + top: 0; + left: 0; + float: left; + height: 100%; + width: 100%; + text-align: left; + overflow: hidden; + -webkit-box-flex: 0; + -webkit-flex: 0 1 auto; + -ms-flex: 0 1 auto; + flex: 0 1 auto; +} +.bs3.bootstrap-select .dropdown-toggle .filter-option { + padding-right: inherit; +} +.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option { + position: absolute; + padding-top: inherit; + padding-bottom: inherit; + padding-left: inherit; + float: none; +} +.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option .filter-option-inner { + padding-right: inherit; +} +.bootstrap-select .dropdown-toggle .filter-option-inner-inner { + overflow: hidden; +} +.bootstrap-select .dropdown-toggle .filter-expand { + width: 0 !important; + float: left; + opacity: 0 !important; + overflow: hidden; +} +.bootstrap-select .dropdown-toggle .caret { + position: absolute; + top: 50%; + right: 12px; + margin-top: -2px; + vertical-align: middle; +} +.input-group .bootstrap-select.form-control .dropdown-toggle { + border-radius: inherit; +} +.bootstrap-select[class*="col-"] .dropdown-toggle { + width: 100%; +} +.bootstrap-select .dropdown-menu { + min-width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.bootstrap-select .dropdown-menu > .inner:focus { + outline: none !important; +} +.bootstrap-select .dropdown-menu.inner { + position: static; + float: none; + border: 0; + padding: 0; + margin: 0; + border-radius: 0; + -webkit-box-shadow: none; + box-shadow: none; +} +.bootstrap-select .dropdown-menu li { + position: relative; +} +.bootstrap-select .dropdown-menu li.active small { + color: rgba(255, 255, 255, 0.5) !important; +} +.bootstrap-select .dropdown-menu li.disabled a { + cursor: not-allowed; +} +.bootstrap-select .dropdown-menu li a { + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.bootstrap-select .dropdown-menu li a.opt { + position: relative; + padding-left: 2.25em; +} +.bootstrap-select .dropdown-menu li a span.check-mark { + display: none; +} +.bootstrap-select .dropdown-menu li a span.text { + display: inline-block; +} +.bootstrap-select .dropdown-menu li small { + padding-left: 0.5em; +} +.bootstrap-select .dropdown-menu .notify { + position: absolute; + bottom: 5px; + width: 96%; + margin: 0 2%; + min-height: 26px; + padding: 3px 5px; + background: #f5f5f5; + border: 1px solid #e3e3e3; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + pointer-events: none; + opacity: 0.9; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.bootstrap-select .no-results { + padding: 3px; + background: #f5f5f5; + margin: 0 5px; + white-space: nowrap; +} +.bootstrap-select.fit-width .dropdown-toggle .filter-option { + position: static; + display: inline; + padding: 0; + width: auto; +} +.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner, +.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner-inner { + display: inline; +} +.bootstrap-select.fit-width .dropdown-toggle .bs-caret:before { + content: '\00a0'; +} +.bootstrap-select.fit-width .dropdown-toggle .caret { + position: static; + top: auto; + margin-top: -1px; +} +.bootstrap-select.show-tick .dropdown-menu .selected span.check-mark { + position: absolute; + display: inline-block; + right: 15px; + top: 5px; +} +.bootstrap-select.show-tick .dropdown-menu li a span.text { + margin-right: 34px; +} +.bootstrap-select .bs-ok-default:after { + content: ''; + display: block; + width: 0.5em; + height: 1em; + border-style: solid; + border-width: 0 0.26em 0.26em 0; + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); + -o-transform: rotate(45deg); + transform: rotate(45deg); +} +.bootstrap-select.show-menu-arrow.open > .dropdown-toggle, +.bootstrap-select.show-menu-arrow.show > .dropdown-toggle { + z-index: 1061; +} +.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:before { + content: ''; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid rgba(204, 204, 204, 0.2); + position: absolute; + bottom: -4px; + left: 9px; + display: none; +} +.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:after { + content: ''; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid white; + position: absolute; + bottom: -4px; + left: 10px; + display: none; +} +.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:before { + bottom: auto; + top: -4px; + border-top: 7px solid rgba(204, 204, 204, 0.2); + border-bottom: 0; +} +.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:after { + bottom: auto; + top: -4px; + border-top: 6px solid white; + border-bottom: 0; +} +.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:before { + right: 12px; + left: auto; +} +.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:after { + right: 13px; + left: auto; +} +.bootstrap-select.show-menu-arrow.open > .dropdown-toggle .filter-option:before, +.bootstrap-select.show-menu-arrow.show > .dropdown-toggle .filter-option:before, +.bootstrap-select.show-menu-arrow.open > .dropdown-toggle .filter-option:after, +.bootstrap-select.show-menu-arrow.show > .dropdown-toggle .filter-option:after { + display: block; +} +.bs-searchbox, +.bs-actionsbox, +.bs-donebutton { + padding: 4px 8px; +} +.bs-actionsbox { + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.bs-actionsbox .btn-group button { + width: 50%; +} +.bs-donebutton { + float: left; + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.bs-donebutton .btn-group button { + width: 100%; +} +.bs-searchbox + .bs-actionsbox { + padding: 0 8px 4px; +} +.bs-searchbox .form-control { + margin-bottom: 0; + width: 100%; + float: none; +} +/*# sourceMappingURL=bootstrap-select.css.map */ \ No newline at end of file diff --git a/templates/yandexhome/css/bootstrap-select.css.map b/templates/yandexhome/css/bootstrap-select.css.map new file mode 100644 index 0000000..e3a7ca8 --- /dev/null +++ b/templates/yandexhome/css/bootstrap-select.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["less/bootstrap-select.less","bootstrap-select.css"],"names":[],"mappings":"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","file":"bootstrap-select.css","sourcesContent":["@import \"variables\";\n\n// Mixins\n.cursor-disabled() {\n cursor: not-allowed;\n}\n\n// Rules\nselect.bs-select-hidden,\n.bootstrap-select > select.bs-select-hidden,\nselect.selectpicker {\n display: none !important;\n}\n\n.bootstrap-select {\n width: 220px \\0; /*IE9 and below*/\n vertical-align: middle;\n\n // The selectpicker button\n > .dropdown-toggle {\n position: relative;\n width: 100%;\n // necessary for proper positioning of caret in Bootstrap 4 (pushes caret to the right)\n text-align: right;\n white-space: nowrap;\n // force caret to be vertically centered for Bootstrap 4 multi-line buttons\n display: inline-flex;\n align-items: center;\n justify-content: space-between;\n\n &:after {\n margin-top: -1px;\n }\n\n &.bs-placeholder {\n &,\n &:hover,\n &:focus,\n &:active {\n color: @input-color-placeholder;\n }\n\n &.btn-primary,\n &.btn-secondary,\n &.btn-success,\n &.btn-danger,\n &.btn-info,\n &.btn-dark {\n &,\n &:hover,\n &:focus,\n &:active {\n color: @input-alt-color-placeholder;\n }\n }\n }\n }\n\n > select {\n position: absolute !important;\n bottom: 0;\n left: 50%;\n display: block !important;\n width: 0.5px !important;\n height: 100% !important;\n padding: 0 !important;\n opacity: 0 !important;\n border: none;\n z-index: 0 !important;\n\n &.mobile-device {\n top: 0;\n left: 0;\n display: block !important;\n width: 100% !important;\n z-index: 2 !important;\n }\n }\n\n // Error display\n .has-error & .dropdown-toggle,\n .error & .dropdown-toggle,\n &.is-invalid .dropdown-toggle,\n .was-validated & .selectpicker:invalid + .dropdown-toggle {\n border-color: @color-red-error;\n }\n\n &.is-valid .dropdown-toggle,\n .was-validated & .selectpicker:valid + .dropdown-toggle {\n border-color: @color-green-success;\n }\n\n &.fit-width {\n width: auto !important;\n }\n\n &:not([class*=\"col-\"]):not([class*=\"form-control\"]):not(.input-group-btn) {\n width: @width-default;\n }\n\n > select.mobile-device:focus + .dropdown-toggle,\n .dropdown-toggle:focus {\n outline: thin dotted #333333 !important;\n outline: 5px auto -webkit-focus-ring-color !important;\n outline-offset: -2px;\n }\n}\n\n// The selectpicker components\n.bootstrap-select {\n &.form-control {\n margin-bottom: 0;\n padding: 0;\n border: none;\n\n :not(.input-group) > &:not([class*=\"col-\"]) {\n width: 100%;\n }\n\n &.input-group-btn {\n float: none;\n z-index: auto;\n }\n }\n\n .form-inline &,\n .form-inline &.form-control:not([class*=\"col-\"]) {\n width: auto;\n }\n\n &:not(.input-group-btn),\n &[class*=\"col-\"] {\n float: none;\n display: inline-block;\n margin-left: 0;\n }\n\n // Forces the pull to the right, if necessary\n &,\n &[class*=\"col-\"],\n .row &[class*=\"col-\"] {\n &.dropdown-menu-right {\n float: right;\n }\n }\n\n .form-inline &,\n .form-horizontal &,\n .form-group & {\n margin-bottom: 0;\n }\n\n .form-group-lg &.form-control,\n .form-group-sm &.form-control {\n padding: 0;\n\n .dropdown-toggle {\n height: 100%;\n font-size: inherit;\n line-height: inherit;\n border-radius: inherit;\n }\n }\n\n &.form-control-sm .dropdown-toggle,\n &.form-control-lg .dropdown-toggle {\n font-size: inherit;\n line-height: inherit;\n border-radius: inherit;\n }\n\n &.form-control-sm .dropdown-toggle {\n padding: @input-padding-y-sm @input-padding-x-sm;\n }\n\n &.form-control-lg .dropdown-toggle {\n padding: @input-padding-y-lg @input-padding-x-lg;\n }\n\n // Set the width of the live search (and any other form control within an inline form)\n // see https://github.com/silviomoreto/bootstrap-select/issues/685\n .form-inline & .form-control {\n width: 100%;\n }\n\n &.disabled,\n > .disabled {\n .cursor-disabled();\n\n &:focus {\n outline: none !important;\n }\n }\n\n &.bs-container {\n position: absolute;\n top: 0;\n left: 0;\n height: 0 !important;\n padding: 0 !important;\n \n .dropdown-menu {\n z-index: @zindex-select-dropdown;\n }\n }\n\n // The selectpicker button\n .dropdown-toggle {\n .filter-option {\n position: static;\n top: 0;\n left: 0;\n float: left;\n height: 100%;\n width: 100%;\n text-align: left;\n overflow: hidden;\n flex: 0 1 auto; // for IE10\n\n .bs3& {\n padding-right: inherit;\n }\n\n .input-group .bs3-has-addon& {\n position: absolute;\n padding-top: inherit;\n padding-bottom: inherit;\n padding-left: inherit;\n float: none;\n\n .filter-option-inner {\n padding-right: inherit;\n }\n }\n }\n\n .filter-option-inner-inner {\n overflow: hidden;\n }\n\n // used to expand the height of the button when inside an input group\n .filter-expand {\n width: 0 !important;\n float: left;\n opacity: 0 !important;\n overflow: hidden;\n }\n\n .caret {\n position: absolute;\n top: 50%;\n right: 12px;\n margin-top: -2px;\n vertical-align: middle;\n }\n }\n\n .input-group &.form-control .dropdown-toggle {\n border-radius: inherit;\n }\n\n &[class*=\"col-\"] .dropdown-toggle {\n width: 100%;\n }\n\n // The selectpicker dropdown\n .dropdown-menu {\n min-width: 100%;\n box-sizing: border-box;\n\n > .inner:focus {\n outline: none !important;\n }\n\n &.inner {\n position: static;\n float: none;\n border: 0;\n padding: 0;\n margin: 0;\n border-radius: 0;\n box-shadow: none;\n }\n\n li {\n position: relative;\n\n &.active small {\n color: @input-alt-color-placeholder !important;\n }\n\n &.disabled a {\n .cursor-disabled();\n }\n\n a {\n cursor: pointer;\n user-select: none;\n\n &.opt {\n position: relative;\n padding-left: 2.25em;\n }\n\n span.check-mark {\n display: none;\n }\n\n span.text {\n display: inline-block;\n }\n }\n\n small {\n padding-left: 0.5em;\n }\n }\n\n .notify {\n position: absolute;\n bottom: 5px;\n width: 96%;\n margin: 0 2%;\n min-height: 26px;\n padding: 3px 5px;\n background: rgb(245, 245, 245);\n border: 1px solid rgb(227, 227, 227);\n box-shadow: inset 0 1px 1px fade(rgb(0, 0, 0), 5%);\n pointer-events: none;\n opacity: 0.9;\n box-sizing: border-box;\n }\n }\n\n .no-results {\n padding: 3px;\n background: #f5f5f5;\n margin: 0 5px;\n white-space: nowrap;\n }\n\n &.fit-width .dropdown-toggle {\n .filter-option {\n position: static;\n display: inline;\n padding: 0;\n width: auto;\n }\n\n .filter-option-inner,\n .filter-option-inner-inner {\n display: inline;\n }\n\n .bs-caret:before {\n content: '\\00a0';\n }\n\n .caret {\n position: static;\n top: auto;\n margin-top: -1px;\n }\n }\n\n &.show-tick .dropdown-menu {\n .selected span.check-mark {\n position: absolute;\n display: inline-block;\n right: 15px;\n top: 5px;\n }\n\n li a span.text {\n margin-right: 34px;\n }\n }\n\n // default check mark for use without an icon font\n .bs-ok-default:after {\n content: '';\n display: block;\n width: 0.5em;\n height: 1em;\n border-style: solid;\n border-width: 0 0.26em 0.26em 0;\n transform: rotate(45deg);\n }\n}\n\n.bootstrap-select.show-menu-arrow {\n &.open > .dropdown-toggle,\n &.show > .dropdown-toggle {\n z-index: (@zindex-select-dropdown + 1);\n }\n\n .dropdown-toggle .filter-option {\n &:before {\n content: '';\n border-left: 7px solid transparent;\n border-right: 7px solid transparent;\n border-bottom: 7px solid @color-grey-arrow;\n position: absolute;\n bottom: -4px;\n left: 9px;\n display: none;\n }\n\n &:after {\n content: '';\n border-left: 6px solid transparent;\n border-right: 6px solid transparent;\n border-bottom: 6px solid white;\n position: absolute;\n bottom: -4px;\n left: 10px;\n display: none;\n }\n }\n\n &.dropup .dropdown-toggle .filter-option {\n &:before {\n bottom: auto;\n top: -4px;\n border-top: 7px solid @color-grey-arrow;\n border-bottom: 0;\n }\n\n &:after {\n bottom: auto;\n top: -4px;\n border-top: 6px solid white;\n border-bottom: 0;\n }\n }\n\n &.pull-right .dropdown-toggle .filter-option {\n &:before {\n right: 12px;\n left: auto;\n }\n\n &:after {\n right: 13px;\n left: auto;\n }\n }\n\n &.open > .dropdown-toggle .filter-option,\n &.show > .dropdown-toggle .filter-option {\n &:before,\n &:after {\n display: block;\n }\n }\n}\n\n.bs-searchbox,\n.bs-actionsbox,\n.bs-donebutton {\n padding: 4px 8px;\n}\n\n.bs-actionsbox {\n width: 100%;\n box-sizing: border-box;\n\n & .btn-group button {\n width: 50%;\n }\n}\n\n.bs-donebutton {\n float: left;\n width: 100%;\n box-sizing: border-box;\n\n & .btn-group button {\n width: 100%;\n }\n}\n\n.bs-searchbox {\n & + .bs-actionsbox {\n padding: 0 8px 4px;\n }\n\n & .form-control {\n margin-bottom: 0;\n width: 100%;\n float: none;\n }\n}\n","select.bs-select-hidden,\n.bootstrap-select > select.bs-select-hidden,\nselect.selectpicker {\n display: none !important;\n}\n.bootstrap-select {\n width: 220px \\0;\n /*IE9 and below*/\n vertical-align: middle;\n}\n.bootstrap-select > .dropdown-toggle {\n position: relative;\n width: 100%;\n text-align: right;\n white-space: nowrap;\n display: inline-flex;\n align-items: center;\n justify-content: space-between;\n}\n.bootstrap-select > .dropdown-toggle:after {\n margin-top: -1px;\n}\n.bootstrap-select > .dropdown-toggle.bs-placeholder,\n.bootstrap-select > .dropdown-toggle.bs-placeholder:hover,\n.bootstrap-select > .dropdown-toggle.bs-placeholder:focus,\n.bootstrap-select > .dropdown-toggle.bs-placeholder:active {\n color: #999;\n}\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-primary,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-secondary,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-success,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-danger,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-info,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-dark,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-primary:hover,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-secondary:hover,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-success:hover,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-danger:hover,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-info:hover,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-dark:hover,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-primary:focus,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-secondary:focus,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-success:focus,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-danger:focus,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-info:focus,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-dark:focus,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-primary:active,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-secondary:active,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-success:active,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-danger:active,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-info:active,\n.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-dark:active {\n color: rgba(255, 255, 255, 0.5);\n}\n.bootstrap-select > select {\n position: absolute !important;\n bottom: 0;\n left: 50%;\n display: block !important;\n width: 0.5px !important;\n height: 100% !important;\n padding: 0 !important;\n opacity: 0 !important;\n border: none;\n z-index: 0 !important;\n}\n.bootstrap-select > select.mobile-device {\n top: 0;\n left: 0;\n display: block !important;\n width: 100% !important;\n z-index: 2 !important;\n}\n.has-error .bootstrap-select .dropdown-toggle,\n.error .bootstrap-select .dropdown-toggle,\n.bootstrap-select.is-invalid .dropdown-toggle,\n.was-validated .bootstrap-select .selectpicker:invalid + .dropdown-toggle {\n border-color: #b94a48;\n}\n.bootstrap-select.is-valid .dropdown-toggle,\n.was-validated .bootstrap-select .selectpicker:valid + .dropdown-toggle {\n border-color: #28a745;\n}\n.bootstrap-select.fit-width {\n width: auto !important;\n}\n.bootstrap-select:not([class*=\"col-\"]):not([class*=\"form-control\"]):not(.input-group-btn) {\n width: 220px;\n}\n.bootstrap-select > select.mobile-device:focus + .dropdown-toggle,\n.bootstrap-select .dropdown-toggle:focus {\n outline: thin dotted #333333 !important;\n outline: 5px auto -webkit-focus-ring-color !important;\n outline-offset: -2px;\n}\n.bootstrap-select.form-control {\n margin-bottom: 0;\n padding: 0;\n border: none;\n}\n:not(.input-group) > .bootstrap-select.form-control:not([class*=\"col-\"]) {\n width: 100%;\n}\n.bootstrap-select.form-control.input-group-btn {\n float: none;\n z-index: auto;\n}\n.form-inline .bootstrap-select,\n.form-inline .bootstrap-select.form-control:not([class*=\"col-\"]) {\n width: auto;\n}\n.bootstrap-select:not(.input-group-btn),\n.bootstrap-select[class*=\"col-\"] {\n float: none;\n display: inline-block;\n margin-left: 0;\n}\n.bootstrap-select.dropdown-menu-right,\n.bootstrap-select[class*=\"col-\"].dropdown-menu-right,\n.row .bootstrap-select[class*=\"col-\"].dropdown-menu-right {\n float: right;\n}\n.form-inline .bootstrap-select,\n.form-horizontal .bootstrap-select,\n.form-group .bootstrap-select {\n margin-bottom: 0;\n}\n.form-group-lg .bootstrap-select.form-control,\n.form-group-sm .bootstrap-select.form-control {\n padding: 0;\n}\n.form-group-lg .bootstrap-select.form-control .dropdown-toggle,\n.form-group-sm .bootstrap-select.form-control .dropdown-toggle {\n height: 100%;\n font-size: inherit;\n line-height: inherit;\n border-radius: inherit;\n}\n.bootstrap-select.form-control-sm .dropdown-toggle,\n.bootstrap-select.form-control-lg .dropdown-toggle {\n font-size: inherit;\n line-height: inherit;\n border-radius: inherit;\n}\n.bootstrap-select.form-control-sm .dropdown-toggle {\n padding: 0.25rem 0.5rem;\n}\n.bootstrap-select.form-control-lg .dropdown-toggle {\n padding: 0.5rem 1rem;\n}\n.form-inline .bootstrap-select .form-control {\n width: 100%;\n}\n.bootstrap-select.disabled,\n.bootstrap-select > .disabled {\n cursor: not-allowed;\n}\n.bootstrap-select.disabled:focus,\n.bootstrap-select > .disabled:focus {\n outline: none !important;\n}\n.bootstrap-select.bs-container {\n position: absolute;\n top: 0;\n left: 0;\n height: 0 !important;\n padding: 0 !important;\n}\n.bootstrap-select.bs-container .dropdown-menu {\n z-index: 1060;\n}\n.bootstrap-select .dropdown-toggle .filter-option {\n position: static;\n top: 0;\n left: 0;\n float: left;\n height: 100%;\n width: 100%;\n text-align: left;\n overflow: hidden;\n flex: 0 1 auto;\n}\n.bs3.bootstrap-select .dropdown-toggle .filter-option {\n padding-right: inherit;\n}\n.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option {\n position: absolute;\n padding-top: inherit;\n padding-bottom: inherit;\n padding-left: inherit;\n float: none;\n}\n.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option .filter-option-inner {\n padding-right: inherit;\n}\n.bootstrap-select .dropdown-toggle .filter-option-inner-inner {\n overflow: hidden;\n}\n.bootstrap-select .dropdown-toggle .filter-expand {\n width: 0 !important;\n float: left;\n opacity: 0 !important;\n overflow: hidden;\n}\n.bootstrap-select .dropdown-toggle .caret {\n position: absolute;\n top: 50%;\n right: 12px;\n margin-top: -2px;\n vertical-align: middle;\n}\n.input-group .bootstrap-select.form-control .dropdown-toggle {\n border-radius: inherit;\n}\n.bootstrap-select[class*=\"col-\"] .dropdown-toggle {\n width: 100%;\n}\n.bootstrap-select .dropdown-menu {\n min-width: 100%;\n box-sizing: border-box;\n}\n.bootstrap-select .dropdown-menu > .inner:focus {\n outline: none !important;\n}\n.bootstrap-select .dropdown-menu.inner {\n position: static;\n float: none;\n border: 0;\n padding: 0;\n margin: 0;\n border-radius: 0;\n box-shadow: none;\n}\n.bootstrap-select .dropdown-menu li {\n position: relative;\n}\n.bootstrap-select .dropdown-menu li.active small {\n color: rgba(255, 255, 255, 0.5) !important;\n}\n.bootstrap-select .dropdown-menu li.disabled a {\n cursor: not-allowed;\n}\n.bootstrap-select .dropdown-menu li a {\n cursor: pointer;\n user-select: none;\n}\n.bootstrap-select .dropdown-menu li a.opt {\n position: relative;\n padding-left: 2.25em;\n}\n.bootstrap-select .dropdown-menu li a span.check-mark {\n display: none;\n}\n.bootstrap-select .dropdown-menu li a span.text {\n display: inline-block;\n}\n.bootstrap-select .dropdown-menu li small {\n padding-left: 0.5em;\n}\n.bootstrap-select .dropdown-menu .notify {\n position: absolute;\n bottom: 5px;\n width: 96%;\n margin: 0 2%;\n min-height: 26px;\n padding: 3px 5px;\n background: #f5f5f5;\n border: 1px solid #e3e3e3;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n pointer-events: none;\n opacity: 0.9;\n box-sizing: border-box;\n}\n.bootstrap-select .no-results {\n padding: 3px;\n background: #f5f5f5;\n margin: 0 5px;\n white-space: nowrap;\n}\n.bootstrap-select.fit-width .dropdown-toggle .filter-option {\n position: static;\n display: inline;\n padding: 0;\n width: auto;\n}\n.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner,\n.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner-inner {\n display: inline;\n}\n.bootstrap-select.fit-width .dropdown-toggle .bs-caret:before {\n content: '\\00a0';\n}\n.bootstrap-select.fit-width .dropdown-toggle .caret {\n position: static;\n top: auto;\n margin-top: -1px;\n}\n.bootstrap-select.show-tick .dropdown-menu .selected span.check-mark {\n position: absolute;\n display: inline-block;\n right: 15px;\n top: 5px;\n}\n.bootstrap-select.show-tick .dropdown-menu li a span.text {\n margin-right: 34px;\n}\n.bootstrap-select .bs-ok-default:after {\n content: '';\n display: block;\n width: 0.5em;\n height: 1em;\n border-style: solid;\n border-width: 0 0.26em 0.26em 0;\n transform: rotate(45deg);\n}\n.bootstrap-select.show-menu-arrow.open > .dropdown-toggle,\n.bootstrap-select.show-menu-arrow.show > .dropdown-toggle {\n z-index: 1061;\n}\n.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:before {\n content: '';\n border-left: 7px solid transparent;\n border-right: 7px solid transparent;\n border-bottom: 7px solid rgba(204, 204, 204, 0.2);\n position: absolute;\n bottom: -4px;\n left: 9px;\n display: none;\n}\n.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:after {\n content: '';\n border-left: 6px solid transparent;\n border-right: 6px solid transparent;\n border-bottom: 6px solid white;\n position: absolute;\n bottom: -4px;\n left: 10px;\n display: none;\n}\n.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:before {\n bottom: auto;\n top: -4px;\n border-top: 7px solid rgba(204, 204, 204, 0.2);\n border-bottom: 0;\n}\n.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:after {\n bottom: auto;\n top: -4px;\n border-top: 6px solid white;\n border-bottom: 0;\n}\n.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:before {\n right: 12px;\n left: auto;\n}\n.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:after {\n right: 13px;\n left: auto;\n}\n.bootstrap-select.show-menu-arrow.open > .dropdown-toggle .filter-option:before,\n.bootstrap-select.show-menu-arrow.show > .dropdown-toggle .filter-option:before,\n.bootstrap-select.show-menu-arrow.open > .dropdown-toggle .filter-option:after,\n.bootstrap-select.show-menu-arrow.show > .dropdown-toggle .filter-option:after {\n display: block;\n}\n.bs-searchbox,\n.bs-actionsbox,\n.bs-donebutton {\n padding: 4px 8px;\n}\n.bs-actionsbox {\n width: 100%;\n box-sizing: border-box;\n}\n.bs-actionsbox .btn-group button {\n width: 50%;\n}\n.bs-donebutton {\n float: left;\n width: 100%;\n box-sizing: border-box;\n}\n.bs-donebutton .btn-group button {\n width: 100%;\n}\n.bs-searchbox + .bs-actionsbox {\n padding: 0 8px 4px;\n}\n.bs-searchbox .form-control {\n margin-bottom: 0;\n width: 100%;\n float: none;\n}\n/*# sourceMappingURL=bootstrap-select.css.map */"]} \ No newline at end of file diff --git a/templates/yandexhome/css/bootstrap-select.js b/templates/yandexhome/css/bootstrap-select.js new file mode 100644 index 0000000..403f26b --- /dev/null +++ b/templates/yandexhome/css/bootstrap-select.js @@ -0,0 +1,3061 @@ +/*! + * Bootstrap-select v1.13.9 (https://developer.snapappointments.com/bootstrap-select) + * + * Copyright 2012-2019 SnapAppointments, LLC + * Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE) + */ + +(function (root, factory) { + if (root === undefined && window !== undefined) root = window; + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module unless amdModuleId is set + define(["jquery"], function (a0) { + return (factory(a0)); + }); + } else if (typeof module === 'object' && module.exports) { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(require("jquery")); + } else { + factory(root["jQuery"]); + } +}(this, function (jQuery) { + +(function ($) { + 'use strict'; + + var DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn']; + + var uriAttrs = [ + 'background', + 'cite', + 'href', + 'itemtype', + 'longdesc', + 'poster', + 'src', + 'xlink:href' + ]; + + var ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i; + + var DefaultWhitelist = { + // Global attributes allowed on any supplied element below. + '*': ['class', 'dir', 'id', 'lang', 'role', 'tabindex', 'style', ARIA_ATTRIBUTE_PATTERN], + a: ['target', 'href', 'title', 'rel'], + area: [], + b: [], + br: [], + col: [], + code: [], + div: [], + em: [], + hr: [], + h1: [], + h2: [], + h3: [], + h4: [], + h5: [], + h6: [], + i: [], + img: ['src', 'alt', 'title', 'width', 'height'], + li: [], + ol: [], + p: [], + pre: [], + s: [], + small: [], + span: [], + sub: [], + sup: [], + strong: [], + u: [], + ul: [] + } + + /** + * A pattern that recognizes a commonly useful subset of URLs that are safe. + * + * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts + */ + var SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi; + + /** + * A pattern that matches safe data URLs. Only matches image, video and audio types. + * + * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts + */ + var DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i; + + function allowedAttribute (attr, allowedAttributeList) { + var attrName = attr.nodeName.toLowerCase() + + if ($.inArray(attrName, allowedAttributeList) !== -1) { + if ($.inArray(attrName, uriAttrs) !== -1) { + return Boolean(attr.nodeValue.match(SAFE_URL_PATTERN) || attr.nodeValue.match(DATA_URL_PATTERN)) + } + + return true + } + + var regExp = $(allowedAttributeList).filter(function (index, value) { + return value instanceof RegExp + }) + + // Check if a regular expression validates the attribute. + for (var i = 0, l = regExp.length; i < l; i++) { + if (attrName.match(regExp[i])) { + return true + } + } + + return false + } + + function sanitizeHtml (unsafeElements, whiteList, sanitizeFn) { + if (sanitizeFn && typeof sanitizeFn === 'function') { + return sanitizeFn(unsafeElements); + } + + var whitelistKeys = Object.keys(whiteList); + + for (var i = 0, len = unsafeElements.length; i < len; i++) { + var elements = unsafeElements[i].querySelectorAll('*'); + + for (var j = 0, len2 = elements.length; j < len2; j++) { + var el = elements[j]; + var elName = el.nodeName.toLowerCase(); + + if (whitelistKeys.indexOf(elName) === -1) { + el.parentNode.removeChild(el); + + continue; + } + + var attributeList = [].slice.call(el.attributes); + var whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || []); + + for (var k = 0, len3 = attributeList.length; k < len3; k++) { + var attr = attributeList[k]; + + if (!allowedAttribute(attr, whitelistedAttributes)) { + el.removeAttribute(attr.nodeName); + } + } + } + } + } + + // Polyfill for browsers with no classList support + // Remove in v2 + if (!('classList' in document.createElement('_'))) { + (function (view) { + if (!('Element' in view)) return; + + var classListProp = 'classList', + protoProp = 'prototype', + elemCtrProto = view.Element[protoProp], + objCtr = Object, + classListGetter = function () { + var $elem = $(this); + + return { + add: function (classes) { + classes = Array.prototype.slice.call(arguments).join(' '); + return $elem.addClass(classes); + }, + remove: function (classes) { + classes = Array.prototype.slice.call(arguments).join(' '); + return $elem.removeClass(classes); + }, + toggle: function (classes, force) { + return $elem.toggleClass(classes, force); + }, + contains: function (classes) { + return $elem.hasClass(classes); + } + } + }; + + if (objCtr.defineProperty) { + var classListPropDesc = { + get: classListGetter, + enumerable: true, + configurable: true + }; + try { + objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); + } catch (ex) { // IE 8 doesn't support enumerable:true + // adding undefined to fight this issue https://github.com/eligrey/classList.js/issues/36 + // modernie IE8-MSW7 machine has IE8 8.0.6001.18702 and is affected + if (ex.number === undefined || ex.number === -0x7FF5EC54) { + classListPropDesc.enumerable = false; + objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); + } + } + } else if (objCtr[protoProp].__defineGetter__) { + elemCtrProto.__defineGetter__(classListProp, classListGetter); + } + }(window)); + } + + var testElement = document.createElement('_'); + + testElement.classList.add('c1', 'c2'); + + if (!testElement.classList.contains('c2')) { + var _add = DOMTokenList.prototype.add, + _remove = DOMTokenList.prototype.remove; + + DOMTokenList.prototype.add = function () { + Array.prototype.forEach.call(arguments, _add.bind(this)); + } + + DOMTokenList.prototype.remove = function () { + Array.prototype.forEach.call(arguments, _remove.bind(this)); + } + } + + testElement.classList.toggle('c3', false); + + // Polyfill for IE 10 and Firefox <24, where classList.toggle does not + // support the second argument. + if (testElement.classList.contains('c3')) { + var _toggle = DOMTokenList.prototype.toggle; + + DOMTokenList.prototype.toggle = function (token, force) { + if (1 in arguments && !this.contains(token) === !force) { + return force; + } else { + return _toggle.call(this, token); + } + }; + } + + testElement = null; + + // shallow array comparison + function isEqual (array1, array2) { + return array1.length === array2.length && array1.every(function (element, index) { + return element === array2[index]; + }); + }; + + // + if (!String.prototype.startsWith) { + (function () { + 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` + var defineProperty = (function () { + // IE 8 only supports `Object.defineProperty` on DOM elements + try { + var object = {}; + var $defineProperty = Object.defineProperty; + var result = $defineProperty(object, object, object) && $defineProperty; + } catch (error) { + } + return result; + }()); + var toString = {}.toString; + var startsWith = function (search) { + if (this == null) { + throw new TypeError(); + } + var string = String(this); + if (search && toString.call(search) == '[object RegExp]') { + throw new TypeError(); + } + var stringLength = string.length; + var searchString = String(search); + var searchLength = searchString.length; + var position = arguments.length > 1 ? arguments[1] : undefined; + // `ToInteger` + var pos = position ? Number(position) : 0; + if (pos != pos) { // better `isNaN` + pos = 0; + } + var start = Math.min(Math.max(pos, 0), stringLength); + // Avoid the `indexOf` call if no match is possible + if (searchLength + start > stringLength) { + return false; + } + var index = -1; + while (++index < searchLength) { + if (string.charCodeAt(start + index) != searchString.charCodeAt(index)) { + return false; + } + } + return true; + }; + if (defineProperty) { + defineProperty(String.prototype, 'startsWith', { + 'value': startsWith, + 'configurable': true, + 'writable': true + }); + } else { + String.prototype.startsWith = startsWith; + } + }()); + } + + if (!Object.keys) { + Object.keys = function ( + o, // object + k, // key + r // result array + ) { + // initialize object and result + r = []; + // iterate over object keys + for (k in o) { + // fill result array with non-prototypical keys + r.hasOwnProperty.call(o, k) && r.push(k); + } + // return result + return r; + }; + } + + if (HTMLSelectElement && !HTMLSelectElement.prototype.hasOwnProperty('selectedOptions')) { + Object.defineProperty(HTMLSelectElement.prototype, 'selectedOptions', { + get: function () { + return this.querySelectorAll(':checked'); + } + }); + } + + // much faster than $.val() + function getSelectValues (select) { + var result = []; + var options = select.selectedOptions; + var opt; + + if (select.multiple) { + for (var i = 0, len = options.length; i < len; i++) { + opt = options[i]; + + result.push(opt.value || opt.text); + } + } else { + result = select.value; + } + + return result; + } + + // set data-selected on select element if the value has been programmatically selected + // prior to initialization of bootstrap-select + // * consider removing or replacing an alternative method * + var valHooks = { + useDefault: false, + _set: $.valHooks.select.set + }; + + $.valHooks.select.set = function (elem, value) { + if (value && !valHooks.useDefault) $(elem).data('selected', true); + + return valHooks._set.apply(this, arguments); + }; + + var changedArguments = null; + + var EventIsSupported = (function () { + try { + new Event('change'); + return true; + } catch (e) { + return false; + } + })(); + + $.fn.triggerNative = function (eventName) { + var el = this[0], + event; + + if (el.dispatchEvent) { // for modern browsers & IE9+ + if (EventIsSupported) { + // For modern browsers + event = new Event(eventName, { + bubbles: true + }); + } else { + // For IE since it doesn't support Event constructor + event = document.createEvent('Event'); + event.initEvent(eventName, true, false); + } + + el.dispatchEvent(event); + } else if (el.fireEvent) { // for IE8 + event = document.createEventObject(); + event.eventType = eventName; + el.fireEvent('on' + eventName, event); + } else { + // fall back to jQuery.trigger + this.trigger(eventName); + } + }; + // + + function stringSearch (li, searchString, method, normalize) { + var stringTypes = [ + 'display', + 'subtext', + 'tokens' + ], + searchSuccess = false; + + for (var i = 0; i < stringTypes.length; i++) { + var stringType = stringTypes[i], + string = li[stringType]; + + if (string) { + string = string.toString(); + + // Strip HTML tags. This isn't perfect, but it's much faster than any other method + if (stringType === 'display') { + string = string.replace(/<[^>]+>/g, ''); + } + + if (normalize) string = normalizeToBase(string); + string = string.toUpperCase(); + + if (method === 'contains') { + searchSuccess = string.indexOf(searchString) >= 0; + } else { + searchSuccess = string.startsWith(searchString); + } + + if (searchSuccess) break; + } + } + + return searchSuccess; + } + + function toInteger (value) { + return parseInt(value, 10) || 0; + } + + // Borrowed from Lodash (_.deburr) + /** Used to map Latin Unicode letters to basic Latin letters. */ + var deburredLetters = { + // Latin-1 Supplement block. + '\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A', + '\xe0': 'a', '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a', + '\xc7': 'C', '\xe7': 'c', + '\xd0': 'D', '\xf0': 'd', + '\xc8': 'E', '\xc9': 'E', '\xca': 'E', '\xcb': 'E', + '\xe8': 'e', '\xe9': 'e', '\xea': 'e', '\xeb': 'e', + '\xcc': 'I', '\xcd': 'I', '\xce': 'I', '\xcf': 'I', + '\xec': 'i', '\xed': 'i', '\xee': 'i', '\xef': 'i', + '\xd1': 'N', '\xf1': 'n', + '\xd2': 'O', '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O', + '\xf2': 'o', '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o', + '\xd9': 'U', '\xda': 'U', '\xdb': 'U', '\xdc': 'U', + '\xf9': 'u', '\xfa': 'u', '\xfb': 'u', '\xfc': 'u', + '\xdd': 'Y', '\xfd': 'y', '\xff': 'y', + '\xc6': 'Ae', '\xe6': 'ae', + '\xde': 'Th', '\xfe': 'th', + '\xdf': 'ss', + // Latin Extended-A block. + '\u0100': 'A', '\u0102': 'A', '\u0104': 'A', + '\u0101': 'a', '\u0103': 'a', '\u0105': 'a', + '\u0106': 'C', '\u0108': 'C', '\u010a': 'C', '\u010c': 'C', + '\u0107': 'c', '\u0109': 'c', '\u010b': 'c', '\u010d': 'c', + '\u010e': 'D', '\u0110': 'D', '\u010f': 'd', '\u0111': 'd', + '\u0112': 'E', '\u0114': 'E', '\u0116': 'E', '\u0118': 'E', '\u011a': 'E', + '\u0113': 'e', '\u0115': 'e', '\u0117': 'e', '\u0119': 'e', '\u011b': 'e', + '\u011c': 'G', '\u011e': 'G', '\u0120': 'G', '\u0122': 'G', + '\u011d': 'g', '\u011f': 'g', '\u0121': 'g', '\u0123': 'g', + '\u0124': 'H', '\u0126': 'H', '\u0125': 'h', '\u0127': 'h', + '\u0128': 'I', '\u012a': 'I', '\u012c': 'I', '\u012e': 'I', '\u0130': 'I', + '\u0129': 'i', '\u012b': 'i', '\u012d': 'i', '\u012f': 'i', '\u0131': 'i', + '\u0134': 'J', '\u0135': 'j', + '\u0136': 'K', '\u0137': 'k', '\u0138': 'k', + '\u0139': 'L', '\u013b': 'L', '\u013d': 'L', '\u013f': 'L', '\u0141': 'L', + '\u013a': 'l', '\u013c': 'l', '\u013e': 'l', '\u0140': 'l', '\u0142': 'l', + '\u0143': 'N', '\u0145': 'N', '\u0147': 'N', '\u014a': 'N', + '\u0144': 'n', '\u0146': 'n', '\u0148': 'n', '\u014b': 'n', + '\u014c': 'O', '\u014e': 'O', '\u0150': 'O', + '\u014d': 'o', '\u014f': 'o', '\u0151': 'o', + '\u0154': 'R', '\u0156': 'R', '\u0158': 'R', + '\u0155': 'r', '\u0157': 'r', '\u0159': 'r', + '\u015a': 'S', '\u015c': 'S', '\u015e': 'S', '\u0160': 'S', + '\u015b': 's', '\u015d': 's', '\u015f': 's', '\u0161': 's', + '\u0162': 'T', '\u0164': 'T', '\u0166': 'T', + '\u0163': 't', '\u0165': 't', '\u0167': 't', + '\u0168': 'U', '\u016a': 'U', '\u016c': 'U', '\u016e': 'U', '\u0170': 'U', '\u0172': 'U', + '\u0169': 'u', '\u016b': 'u', '\u016d': 'u', '\u016f': 'u', '\u0171': 'u', '\u0173': 'u', + '\u0174': 'W', '\u0175': 'w', + '\u0176': 'Y', '\u0177': 'y', '\u0178': 'Y', + '\u0179': 'Z', '\u017b': 'Z', '\u017d': 'Z', + '\u017a': 'z', '\u017c': 'z', '\u017e': 'z', + '\u0132': 'IJ', '\u0133': 'ij', + '\u0152': 'Oe', '\u0153': 'oe', + '\u0149': "'n", '\u017f': 's' + }; + + /** Used to match Latin Unicode letters (excluding mathematical operators). */ + var reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g; + + /** Used to compose unicode character classes. */ + var rsComboMarksRange = '\\u0300-\\u036f', + reComboHalfMarksRange = '\\ufe20-\\ufe2f', + rsComboSymbolsRange = '\\u20d0-\\u20ff', + rsComboMarksExtendedRange = '\\u1ab0-\\u1aff', + rsComboMarksSupplementRange = '\\u1dc0-\\u1dff', + rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange + rsComboMarksExtendedRange + rsComboMarksSupplementRange; + + /** Used to compose unicode capture groups. */ + var rsCombo = '[' + rsComboRange + ']'; + + /** + * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and + * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols). + */ + var reComboMark = RegExp(rsCombo, 'g'); + + function deburrLetter (key) { + return deburredLetters[key]; + }; + + function normalizeToBase (string) { + string = string.toString(); + return string && string.replace(reLatin, deburrLetter).replace(reComboMark, ''); + } + + // List of HTML entities for escaping. + var escapeMap = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '`': '`' + }; + + // Functions for escaping and unescaping strings to/from HTML interpolation. + var createEscaper = function (map) { + var escaper = function (match) { + return map[match]; + }; + // Regexes for identifying a key that needs to be escaped. + var source = '(?:' + Object.keys(map).join('|') + ')'; + var testRegexp = RegExp(source); + var replaceRegexp = RegExp(source, 'g'); + return function (string) { + string = string == null ? '' : '' + string; + return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string; + }; + }; + + var htmlEscape = createEscaper(escapeMap); + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var keyCodeMap = { + 32: ' ', + 48: '0', + 49: '1', + 50: '2', + 51: '3', + 52: '4', + 53: '5', + 54: '6', + 55: '7', + 56: '8', + 57: '9', + 59: ';', + 65: 'A', + 66: 'B', + 67: 'C', + 68: 'D', + 69: 'E', + 70: 'F', + 71: 'G', + 72: 'H', + 73: 'I', + 74: 'J', + 75: 'K', + 76: 'L', + 77: 'M', + 78: 'N', + 79: 'O', + 80: 'P', + 81: 'Q', + 82: 'R', + 83: 'S', + 84: 'T', + 85: 'U', + 86: 'V', + 87: 'W', + 88: 'X', + 89: 'Y', + 90: 'Z', + 96: '0', + 97: '1', + 98: '2', + 99: '3', + 100: '4', + 101: '5', + 102: '6', + 103: '7', + 104: '8', + 105: '9' + }; + + var keyCodes = { + ESCAPE: 27, // KeyboardEvent.which value for Escape (Esc) key + ENTER: 13, // KeyboardEvent.which value for Enter key + SPACE: 32, // KeyboardEvent.which value for space key + TAB: 9, // KeyboardEvent.which value for tab key + ARROW_UP: 38, // KeyboardEvent.which value for up arrow key + ARROW_DOWN: 40 // KeyboardEvent.which value for down arrow key + } + + var version = { + success: false, + major: '3' + }; + + try { + version.full = ($.fn.dropdown.Constructor.VERSION || '').split(' ')[0].split('.'); + version.major = version.full[0]; + version.success = true; + } catch (err) { + // do nothing + } + + var selectId = 0; + + var EVENT_KEY = '.bs.select'; + + var classNames = { + DISABLED: 'disabled', + DIVIDER: 'divider', + SHOW: 'open', + DROPUP: 'dropup', + MENU: 'dropdown-menu', + MENURIGHT: 'dropdown-menu-right', + MENULEFT: 'dropdown-menu-left', + // to-do: replace with more advanced template/customization options + BUTTONCLASS: 'btn-default', + POPOVERHEADER: 'popover-title', + ICONBASE: 'glyphicon', + TICKICON: 'glyphicon-ok' + } + + var Selector = { + MENU: '.' + classNames.MENU + } + + var elementTemplates = { + span: document.createElement('span'), + i: document.createElement('i'), + subtext: document.createElement('small'), + a: document.createElement('a'), + li: document.createElement('li'), + whitespace: document.createTextNode('\u00A0'), + fragment: document.createDocumentFragment() + } + + elementTemplates.a.setAttribute('role', 'option'); + elementTemplates.subtext.className = 'text-muted'; + + elementTemplates.text = elementTemplates.span.cloneNode(false); + elementTemplates.text.className = 'text'; + + elementTemplates.checkMark = elementTemplates.span.cloneNode(false); + + var REGEXP_ARROW = new RegExp(keyCodes.ARROW_UP + '|' + keyCodes.ARROW_DOWN); + var REGEXP_TAB_OR_ESCAPE = new RegExp('^' + keyCodes.TAB + '$|' + keyCodes.ESCAPE); + + var generateOption = { + li: function (content, classes, optgroup) { + var li = elementTemplates.li.cloneNode(false); + + if (content) { + if (content.nodeType === 1 || content.nodeType === 11) { + li.appendChild(content); + } else { + li.innerHTML = content; + } + } + + if (typeof classes !== 'undefined' && classes !== '') li.className = classes; + if (typeof optgroup !== 'undefined' && optgroup !== null) li.classList.add('optgroup-' + optgroup); + + return li; + }, + + a: function (text, classes, inline) { + var a = elementTemplates.a.cloneNode(true); + + if (text) { + if (text.nodeType === 11) { + a.appendChild(text); + } else { + a.insertAdjacentHTML('beforeend', text); + } + } + + if (typeof classes !== 'undefined' && classes !== '') a.className = classes; + if (version.major === '4') a.classList.add('dropdown-item'); + if (inline) a.setAttribute('style', inline); + + return a; + }, + + text: function (options, useFragment) { + var textElement = elementTemplates.text.cloneNode(false), + subtextElement, + iconElement; + + if (options.content) { + textElement.innerHTML = options.content; + } else { + textElement.textContent = options.text; + + if (options.icon) { + var whitespace = elementTemplates.whitespace.cloneNode(false); + + // need to use for icons in the button to prevent a breaking change + // note: switch to span in next major release + iconElement = (useFragment === true ? elementTemplates.i : elementTemplates.span).cloneNode(false); + iconElement.className = options.iconBase + ' ' + options.icon; + + elementTemplates.fragment.appendChild(iconElement); + elementTemplates.fragment.appendChild(whitespace); + } + + if (options.subtext) { + subtextElement = elementTemplates.subtext.cloneNode(false); + subtextElement.textContent = options.subtext; + textElement.appendChild(subtextElement); + } + } + + if (useFragment === true) { + while (textElement.childNodes.length > 0) { + elementTemplates.fragment.appendChild(textElement.childNodes[0]); + } + } else { + elementTemplates.fragment.appendChild(textElement); + } + + return elementTemplates.fragment; + }, + + label: function (options) { + var textElement = elementTemplates.text.cloneNode(false), + subtextElement, + iconElement; + + textElement.innerHTML = options.label; + + if (options.icon) { + var whitespace = elementTemplates.whitespace.cloneNode(false); + + iconElement = elementTemplates.span.cloneNode(false); + iconElement.className = options.iconBase + ' ' + options.icon; + + elementTemplates.fragment.appendChild(iconElement); + elementTemplates.fragment.appendChild(whitespace); + } + + if (options.subtext) { + subtextElement = elementTemplates.subtext.cloneNode(false); + subtextElement.textContent = options.subtext; + textElement.appendChild(subtextElement); + } + + elementTemplates.fragment.appendChild(textElement); + + return elementTemplates.fragment; + } + } + + var Selectpicker = function (element, options) { + var that = this; + + // bootstrap-select has been initialized - revert valHooks.select.set back to its original function + if (!valHooks.useDefault) { + $.valHooks.select.set = valHooks._set; + valHooks.useDefault = true; + } + + this.$element = $(element); + this.$newElement = null; + this.$button = null; + this.$menu = null; + this.options = options; + this.selectpicker = { + main: {}, + current: {}, // current changes if a search is in progress + search: {}, + view: {}, + keydown: { + keyHistory: '', + resetKeyHistory: { + start: function () { + return setTimeout(function () { + that.selectpicker.keydown.keyHistory = ''; + }, 800); + } + } + } + }; + // If we have no title yet, try to pull it from the html title attribute (jQuery doesnt' pick it up as it's not a + // data-attribute) + if (this.options.title === null) { + this.options.title = this.$element.attr('title'); + } + + // Format window padding + var winPad = this.options.windowPadding; + if (typeof winPad === 'number') { + this.options.windowPadding = [winPad, winPad, winPad, winPad]; + } + + // Expose public methods + this.val = Selectpicker.prototype.val; + this.render = Selectpicker.prototype.render; + this.refresh = Selectpicker.prototype.refresh; + this.setStyle = Selectpicker.prototype.setStyle; + this.selectAll = Selectpicker.prototype.selectAll; + this.deselectAll = Selectpicker.prototype.deselectAll; + this.destroy = Selectpicker.prototype.destroy; + this.remove = Selectpicker.prototype.remove; + this.show = Selectpicker.prototype.show; + this.hide = Selectpicker.prototype.hide; + + this.init(); + }; + + Selectpicker.VERSION = '1.13.9'; + + // part of this is duplicated in i18n/defaults-en_US.js. Make sure to update both. + Selectpicker.DEFAULTS = { + noneSelectedText: 'Nothing selected', + noneResultsText: 'No results matched {0}', + countSelectedText: function (numSelected, numTotal) { + return (numSelected == 1) ? '{0} item selected' : '{0} items selected'; + }, + maxOptionsText: function (numAll, numGroup) { + return [ + (numAll == 1) ? 'Limit reached ({n} item max)' : 'Limit reached ({n} items max)', + (numGroup == 1) ? 'Group limit reached ({n} item max)' : 'Group limit reached ({n} items max)' + ]; + }, + selectAllText: 'Select All', + deselectAllText: 'Deselect All', + doneButton: false, + doneButtonText: 'Close', + multipleSeparator: ', ', + styleBase: 'btn', + style: classNames.BUTTONCLASS, + size: 'auto', + title: null, + selectedTextFormat: 'values', + width: false, + container: false, + hideDisabled: false, + showSubtext: false, + showIcon: true, + showContent: true, + dropupAuto: true, + header: false, + liveSearch: false, + liveSearchPlaceholder: null, + liveSearchNormalize: false, + liveSearchStyle: 'contains', + actionsBox: false, + iconBase: classNames.ICONBASE, + tickIcon: classNames.TICKICON, + showTick: false, + template: { + caret: '' + }, + maxOptions: false, + mobile: false, + selectOnTab: false, + dropdownAlignRight: false, + windowPadding: 0, + virtualScroll: 600, + display: false, + sanitize: true, + sanitizeFn: null, + whiteList: DefaultWhitelist + }; + + Selectpicker.prototype = { + + constructor: Selectpicker, + + init: function () { + var that = this, + id = this.$element.attr('id'); + + this.selectId = selectId++; + + this.$element[0].classList.add('bs-select-hidden'); + + this.multiple = this.$element.prop('multiple'); + this.autofocus = this.$element.prop('autofocus'); + this.options.showTick = this.$element[0].classList.contains('show-tick'); + + this.$newElement = this.createDropdown(); + this.$element + .after(this.$newElement) + .prependTo(this.$newElement); + + this.$button = this.$newElement.children('button'); + this.$menu = this.$newElement.children(Selector.MENU); + this.$menuInner = this.$menu.children('.inner'); + this.$searchbox = this.$menu.find('input'); + + this.$element[0].classList.remove('bs-select-hidden'); + + if (this.options.dropdownAlignRight === true) this.$menu[0].classList.add(classNames.MENURIGHT); + + if (typeof id !== 'undefined') { + this.$button.attr('data-id', id); + } + + this.checkDisabled(); + this.clickListener(); + if (this.options.liveSearch) this.liveSearchListener(); + this.setStyle(); + this.render(); + this.setWidth(); + if (this.options.container) { + this.selectPosition(); + } else { + this.$element.on('hide' + EVENT_KEY, function () { + if (that.isVirtual()) { + // empty menu on close + var menuInner = that.$menuInner[0], + emptyMenu = menuInner.firstChild.cloneNode(false); + + // replace the existing UL with an empty one - this is faster than $.empty() or innerHTML = '' + menuInner.replaceChild(emptyMenu, menuInner.firstChild); + menuInner.scrollTop = 0; + } + }); + } + this.$menu.data('this', this); + this.$newElement.data('this', this); + if (this.options.mobile) this.mobile(); + + this.$newElement.on({ + 'hide.bs.dropdown': function (e) { + that.$menuInner.attr('aria-expanded', false); + that.$element.trigger('hide' + EVENT_KEY, e); + }, + 'hidden.bs.dropdown': function (e) { + that.$element.trigger('hidden' + EVENT_KEY, e); + }, + 'show.bs.dropdown': function (e) { + that.$menuInner.attr('aria-expanded', true); + that.$element.trigger('show' + EVENT_KEY, e); + }, + 'shown.bs.dropdown': function (e) { + that.$element.trigger('shown' + EVENT_KEY, e); + } + }); + + if (that.$element[0].hasAttribute('required')) { + this.$element.on('invalid' + EVENT_KEY, function () { + that.$button[0].classList.add('bs-invalid'); + + that.$element + .on('shown' + EVENT_KEY + '.invalid', function () { + that.$element + .val(that.$element.val()) // set the value to hide the validation message in Chrome when menu is opened + .off('shown' + EVENT_KEY + '.invalid'); + }) + .on('rendered' + EVENT_KEY, function () { + // if select is no longer invalid, remove the bs-invalid class + if (this.validity.valid) that.$button[0].classList.remove('bs-invalid'); + that.$element.off('rendered' + EVENT_KEY); + }); + + that.$button.on('blur' + EVENT_KEY, function () { + that.$element.trigger('focus').trigger('blur'); + that.$button.off('blur' + EVENT_KEY); + }); + }); + } + + setTimeout(function () { + that.createLi(); + that.$element.trigger('loaded' + EVENT_KEY); + }); + }, + + createDropdown: function () { + // Options + // If we are multiple or showTick option is set, then add the show-tick class + var showTick = (this.multiple || this.options.showTick) ? ' show-tick' : '', + inputGroup = '', + autofocus = this.autofocus ? ' autofocus' : ''; + + if (version.major < 4 && this.$element.parent().hasClass('input-group')) { + inputGroup = ' input-group-btn'; + } + + // Elements + var drop, + header = '', + searchbox = '', + actionsbox = '', + donebutton = ''; + + if (this.options.header) { + header = + '
' + + '' + + this.options.header + + '
'; + } + + if (this.options.liveSearch) { + searchbox = + ''; + } + + if (this.multiple && this.options.actionsBox) { + actionsbox = + '
' + + '
' + + '' + + '' + + '
' + + '
'; + } + + if (this.multiple && this.options.doneButton) { + donebutton = + '
' + + '
' + + '' + + '
' + + '
'; + } + + drop = + ''; + + return $(drop); + }, + + setPositionData: function () { + this.selectpicker.view.canHighlight = []; + + for (var i = 0; i < this.selectpicker.current.data.length; i++) { + var li = this.selectpicker.current.data[i], + canHighlight = true; + + if (li.type === 'divider') { + canHighlight = false; + li.height = this.sizeInfo.dividerHeight; + } else if (li.type === 'optgroup-label') { + canHighlight = false; + li.height = this.sizeInfo.dropdownHeaderHeight; + } else { + li.height = this.sizeInfo.liHeight; + } + + if (li.disabled) canHighlight = false; + + this.selectpicker.view.canHighlight.push(canHighlight); + + li.position = (i === 0 ? 0 : this.selectpicker.current.data[i - 1].position) + li.height; + } + }, + + isVirtual: function () { + return (this.options.virtualScroll !== false) && (this.selectpicker.main.elements.length >= this.options.virtualScroll) || this.options.virtualScroll === true; + }, + + createView: function (isSearching, scrollTop) { + scrollTop = scrollTop || 0; + + var that = this; + + this.selectpicker.current = isSearching ? this.selectpicker.search : this.selectpicker.main; + + var active = []; + var selected; + var prevActive; + + this.setPositionData(); + + scroll(scrollTop, true); + + this.$menuInner.off('scroll.createView').on('scroll.createView', function (e, updateValue) { + if (!that.noScroll) scroll(this.scrollTop, updateValue); + that.noScroll = false; + }); + + function scroll (scrollTop, init) { + var size = that.selectpicker.current.elements.length, + chunks = [], + chunkSize, + chunkCount, + firstChunk, + lastChunk, + currentChunk, + prevPositions, + positionIsDifferent, + previousElements, + menuIsDifferent = true, + isVirtual = that.isVirtual(); + + that.selectpicker.view.scrollTop = scrollTop; + + if (isVirtual === true) { + // if an option that is encountered that is wider than the current menu width, update the menu width accordingly + if (that.sizeInfo.hasScrollBar && that.$menu[0].offsetWidth > that.sizeInfo.totalMenuWidth) { + that.sizeInfo.menuWidth = that.$menu[0].offsetWidth; + that.sizeInfo.totalMenuWidth = that.sizeInfo.menuWidth + that.sizeInfo.scrollBarWidth; + that.$menu.css('min-width', that.sizeInfo.menuWidth); + } + } + + chunkSize = Math.ceil(that.sizeInfo.menuInnerHeight / that.sizeInfo.liHeight * 1.5); // number of options in a chunk + chunkCount = Math.round(size / chunkSize) || 1; // number of chunks + + for (var i = 0; i < chunkCount; i++) { + var endOfChunk = (i + 1) * chunkSize; + + if (i === chunkCount - 1) { + endOfChunk = size; + } + + chunks[i] = [ + (i) * chunkSize + (!i ? 0 : 1), + endOfChunk + ]; + + if (!size) break; + + if (currentChunk === undefined && scrollTop <= that.selectpicker.current.data[endOfChunk - 1].position - that.sizeInfo.menuInnerHeight) { + currentChunk = i; + } + } + + if (currentChunk === undefined) currentChunk = 0; + + prevPositions = [that.selectpicker.view.position0, that.selectpicker.view.position1]; + + // always display previous, current, and next chunks + firstChunk = Math.max(0, currentChunk - 1); + lastChunk = Math.min(chunkCount - 1, currentChunk + 1); + + that.selectpicker.view.position0 = isVirtual === false ? 0 : (Math.max(0, chunks[firstChunk][0]) || 0); + that.selectpicker.view.position1 = isVirtual === false ? size : (Math.min(size, chunks[lastChunk][1]) || 0); + + positionIsDifferent = prevPositions[0] !== that.selectpicker.view.position0 || prevPositions[1] !== that.selectpicker.view.position1; + + if (that.activeIndex !== undefined) { + prevActive = that.selectpicker.main.elements[that.prevActiveIndex]; + active = that.selectpicker.main.elements[that.activeIndex]; + selected = that.selectpicker.main.elements[that.selectedIndex]; + + if (init) { + if (that.activeIndex !== that.selectedIndex && active && active.length) { + active.classList.remove('active'); + if (active.firstChild) active.firstChild.classList.remove('active'); + } + that.activeIndex = undefined; + } + + if (that.activeIndex && that.activeIndex !== that.selectedIndex && selected && selected.length) { + selected.classList.remove('active'); + if (selected.firstChild) selected.firstChild.classList.remove('active'); + } + } + + if (that.prevActiveIndex !== undefined && that.prevActiveIndex !== that.activeIndex && that.prevActiveIndex !== that.selectedIndex && prevActive && prevActive.length) { + prevActive.classList.remove('active'); + if (prevActive.firstChild) prevActive.firstChild.classList.remove('active'); + } + + if (init || positionIsDifferent) { + previousElements = that.selectpicker.view.visibleElements ? that.selectpicker.view.visibleElements.slice() : []; + + if (isVirtual === false) { + that.selectpicker.view.visibleElements = that.selectpicker.current.elements; + } else { + that.selectpicker.view.visibleElements = that.selectpicker.current.elements.slice(that.selectpicker.view.position0, that.selectpicker.view.position1); + } + + that.setOptionStatus(); + + // if searching, check to make sure the list has actually been updated before updating DOM + // this prevents unnecessary repaints + if (isSearching || (isVirtual === false && init)) menuIsDifferent = !isEqual(previousElements, that.selectpicker.view.visibleElements); + + // if virtual scroll is disabled and not searching, + // menu should never need to be updated more than once + if ((init || isVirtual === true) && menuIsDifferent) { + var menuInner = that.$menuInner[0], + menuFragment = document.createDocumentFragment(), + emptyMenu = menuInner.firstChild.cloneNode(false), + marginTop, + marginBottom, + elements = that.selectpicker.view.visibleElements, + toSanitize = []; + + // replace the existing UL with an empty one - this is faster than $.empty() + menuInner.replaceChild(emptyMenu, menuInner.firstChild); + + for (var i = 0, visibleElementsLen = elements.length; i < visibleElementsLen; i++) { + var element = elements[i], + elText, + elementData; + + if (that.options.sanitize) { + elText = element.lastChild; + + if (elText) { + elementData = that.selectpicker.current.data[i + that.selectpicker.view.position0]; + + if (elementData && elementData.content && !elementData.sanitized) { + toSanitize.push(elText); + elementData.sanitized = true; + } + } + } + + menuFragment.appendChild(element); + } + + if (that.options.sanitize && toSanitize.length) { + sanitizeHtml(toSanitize, that.options.whiteList, that.options.sanitizeFn); + } + + if (isVirtual === true) { + marginTop = (that.selectpicker.view.position0 === 0 ? 0 : that.selectpicker.current.data[that.selectpicker.view.position0 - 1].position); + marginBottom = (that.selectpicker.view.position1 > size - 1 ? 0 : that.selectpicker.current.data[size - 1].position - that.selectpicker.current.data[that.selectpicker.view.position1 - 1].position); + + menuInner.firstChild.style.marginTop = marginTop + 'px'; + menuInner.firstChild.style.marginBottom = marginBottom + 'px'; + } + + menuInner.firstChild.appendChild(menuFragment); + } + } + + that.prevActiveIndex = that.activeIndex; + + if (!that.options.liveSearch) { + that.$menuInner.trigger('focus'); + } else if (isSearching && init) { + var index = 0, + newActive; + + if (!that.selectpicker.view.canHighlight[index]) { + index = 1 + that.selectpicker.view.canHighlight.slice(1).indexOf(true); + } + + newActive = that.selectpicker.view.visibleElements[index]; + + if (that.selectpicker.view.currentActive) { + that.selectpicker.view.currentActive.classList.remove('active'); + if (that.selectpicker.view.currentActive.firstChild) that.selectpicker.view.currentActive.firstChild.classList.remove('active'); + } + + if (newActive) { + newActive.classList.add('active'); + if (newActive.firstChild) newActive.firstChild.classList.add('active'); + } + + that.activeIndex = (that.selectpicker.current.data[index] || {}).index; + } + } + + $(window) + .off('resize' + EVENT_KEY + '.' + this.selectId + '.createView') + .on('resize' + EVENT_KEY + '.' + this.selectId + '.createView', function () { + var isActive = that.$newElement.hasClass(classNames.SHOW); + + if (isActive) scroll(that.$menuInner[0].scrollTop); + }); + }, + + setPlaceholder: function () { + var updateIndex = false; + + if (this.options.title && !this.multiple) { + if (!this.selectpicker.view.titleOption) this.selectpicker.view.titleOption = document.createElement('option'); + + // this option doesn't create a new
  • element, but does add a new option at the start, + // so startIndex should increase to prevent having to check every option for the bs-title-option class + updateIndex = true; + + var element = this.$element[0], + isSelected = false, + titleNotAppended = !this.selectpicker.view.titleOption.parentNode; + + if (titleNotAppended) { + // Use native JS to prepend option (faster) + this.selectpicker.view.titleOption.className = 'bs-title-option'; + this.selectpicker.view.titleOption.value = ''; + + // Check if selected or data-selected attribute is already set on an option. If not, select the titleOption option. + // the selected item may have been changed by user or programmatically before the bootstrap select plugin runs, + // if so, the select will have the data-selected attribute + var $opt = $(element.options[element.selectedIndex]); + isSelected = $opt.attr('selected') === undefined && this.$element.data('selected') === undefined; + } + + if (titleNotAppended || this.selectpicker.view.titleOption.index !== 0) { + element.insertBefore(this.selectpicker.view.titleOption, element.firstChild); + } + + // Set selected *after* appending to select, + // otherwise the option doesn't get selected in IE + // set using selectedIndex, as setting the selected attr to true here doesn't work in IE11 + if (isSelected) element.selectedIndex = 0; + } + + return updateIndex; + }, + + createLi: function () { + var that = this, + iconBase = this.options.iconBase, + optionSelector = ':not([hidden]):not([data-hidden="true"])', + mainElements = [], + mainData = [], + widestOptionLength = 0, + optID = 0, + startIndex = this.setPlaceholder() ? 1 : 0; // append the titleOption if necessary and skip the first option in the loop + + if (this.options.hideDisabled) optionSelector += ':not(:disabled)'; + + if ((that.options.showTick || that.multiple) && !elementTemplates.checkMark.parentNode) { + elementTemplates.checkMark.className = iconBase + ' ' + that.options.tickIcon + ' check-mark'; + elementTemplates.a.appendChild(elementTemplates.checkMark); + } + + var selectOptions = this.$element[0].querySelectorAll('select > *' + optionSelector); + + function addDivider (config) { + var previousData = mainData[mainData.length - 1]; + + // ensure optgroup doesn't create back-to-back dividers + if ( + previousData && + previousData.type === 'divider' && + (previousData.optID || config.optID) + ) { + return; + } + + config = config || {}; + config.type = 'divider'; + + mainElements.push( + generateOption.li( + false, + classNames.DIVIDER, + (config.optID ? config.optID + 'div' : undefined) + ) + ); + + mainData.push(config); + } + + function addOption (option, config) { + config = config || {}; + + config.divider = option.getAttribute('data-divider') === 'true'; + + if (config.divider) { + addDivider({ + optID: config.optID + }); + } else { + var liIndex = mainData.length, + cssText = option.style.cssText, + inlineStyle = cssText ? htmlEscape(cssText) : '', + optionClass = (option.className || '') + (config.optgroupClass || ''); + + if (config.optID) optionClass = 'opt ' + optionClass; + + config.text = option.textContent; + + config.content = option.getAttribute('data-content'); + config.tokens = option.getAttribute('data-tokens'); + config.subtext = option.getAttribute('data-subtext'); + config.icon = option.getAttribute('data-icon'); + config.iconBase = iconBase; + + var textElement = generateOption.text(config); + + mainElements.push( + generateOption.li( + generateOption.a( + textElement, + optionClass, + inlineStyle + ), + '', + config.optID + ) + ); + + option.liIndex = liIndex; + + config.display = config.content || config.text; + config.type = 'option'; + config.index = liIndex; + config.option = option; + config.disabled = config.disabled || option.disabled; + + mainData.push(config); + + var combinedLength = 0; + + // count the number of characters in the option - not perfect, but should work in most cases + if (config.display) combinedLength += config.display.length; + if (config.subtext) combinedLength += config.subtext.length; + // if there is an icon, ensure this option's width is checked + if (config.icon) combinedLength += 1; + + if (combinedLength > widestOptionLength) { + widestOptionLength = combinedLength; + + // guess which option is the widest + // use this when calculating menu width + // not perfect, but it's fast, and the width will be updating accordingly when scrolling + that.selectpicker.view.widestOption = mainElements[mainElements.length - 1]; + } + } + } + + function addOptgroup (index, selectOptions) { + var optgroup = selectOptions[index], + previous = selectOptions[index - 1], + next = selectOptions[index + 1], + options = optgroup.querySelectorAll('option' + optionSelector); + + if (!options.length) return; + + var config = { + label: htmlEscape(optgroup.label), + subtext: optgroup.getAttribute('data-subtext'), + icon: optgroup.getAttribute('data-icon'), + iconBase: iconBase + }, + optgroupClass = ' ' + (optgroup.className || ''), + headerIndex, + lastIndex; + + optID++; + + if (previous) { + addDivider({ optID: optID }); + } + + var labelElement = generateOption.label(config); + + mainElements.push( + generateOption.li(labelElement, 'dropdown-header' + optgroupClass, optID) + ); + + mainData.push({ + display: config.label, + subtext: config.subtext, + type: 'optgroup-label', + optID: optID + }); + + for (var j = 0, len = options.length; j < len; j++) { + var option = options[j]; + + if (j === 0) { + headerIndex = mainData.length - 1; + lastIndex = headerIndex + len; + } + + addOption(option, { + headerIndex: headerIndex, + lastIndex: lastIndex, + optID: optID, + optgroupClass: optgroupClass, + disabled: optgroup.disabled + }); + } + + if (next) { + addDivider({ optID: optID }); + } + } + + for (var len = selectOptions.length; startIndex < len; startIndex++) { + var item = selectOptions[startIndex]; + + if (item.tagName !== 'OPTGROUP') { + addOption(item, {}); + } else { + addOptgroup(startIndex, selectOptions); + } + } + + this.selectpicker.main.elements = mainElements; + this.selectpicker.main.data = mainData; + + this.selectpicker.current = this.selectpicker.main; + }, + + findLis: function () { + return this.$menuInner.find('.inner > li'); + }, + + render: function () { + // ensure titleOption is appended and selected (if necessary) before getting selectedOptions + this.setPlaceholder(); + + var that = this, + selectedOptions = this.$element[0].selectedOptions, + selectedCount = selectedOptions.length, + button = this.$button[0], + buttonInner = button.querySelector('.filter-option-inner-inner'), + multipleSeparator = document.createTextNode(this.options.multipleSeparator), + titleFragment = elementTemplates.fragment.cloneNode(false), + showCount, + countMax, + hasContent = false; + + this.togglePlaceholder(); + + this.tabIndex(); + + if (this.options.selectedTextFormat === 'static') { + titleFragment = generateOption.text({ text: this.options.title }, true); + } else { + showCount = this.multiple && this.options.selectedTextFormat.indexOf('count') !== -1 && selectedCount > 1; + + // determine if the number of selected options will be shown (showCount === true) + if (showCount) { + countMax = this.options.selectedTextFormat.split('>'); + showCount = (countMax.length > 1 && selectedCount > countMax[1]) || (countMax.length === 1 && selectedCount >= 2); + } + + // only loop through all selected options if the count won't be shown + if (showCount === false) { + for (var selectedIndex = 0; selectedIndex < selectedCount; selectedIndex++) { + if (selectedIndex < 50) { + var option = selectedOptions[selectedIndex], + titleOptions = {}, + thisData = { + content: option.getAttribute('data-content'), + subtext: option.getAttribute('data-subtext'), + icon: option.getAttribute('data-icon') + }; + + if (this.multiple && selectedIndex > 0) { + titleFragment.appendChild(multipleSeparator.cloneNode(false)); + } + + if (option.title) { + titleOptions.text = option.title; + } else if (thisData.content && that.options.showContent) { + titleOptions.content = thisData.content.toString(); + hasContent = true; + } else { + if (that.options.showIcon) { + titleOptions.icon = thisData.icon; + titleOptions.iconBase = this.options.iconBase; + } + if (that.options.showSubtext && !that.multiple && thisData.subtext) titleOptions.subtext = ' ' + thisData.subtext; + titleOptions.text = option.textContent.trim(); + } + + titleFragment.appendChild(generateOption.text(titleOptions, true)); + } else { + break; + } + } + + // add ellipsis + if (selectedCount > 49) { + titleFragment.appendChild(document.createTextNode('...')); + } + } else { + var optionSelector = ':not([hidden]):not([data-hidden="true"]):not([data-divider="true"])'; + if (this.options.hideDisabled) optionSelector += ':not(:disabled)'; + + // If this is a multiselect, and selectedTextFormat is count, then show 1 of 2 selected, etc. + var totalCount = this.$element[0].querySelectorAll('select > option' + optionSelector + ', optgroup' + optionSelector + ' option' + optionSelector).length, + tr8nText = (typeof this.options.countSelectedText === 'function') ? this.options.countSelectedText(selectedCount, totalCount) : this.options.countSelectedText; + + titleFragment = generateOption.text({ + text: tr8nText.replace('{0}', selectedCount.toString()).replace('{1}', totalCount.toString()) + }, true); + } + } + + if (this.options.title == undefined) { + // use .attr to ensure undefined is returned if title attribute is not set + this.options.title = this.$element.attr('title'); + } + + // If the select doesn't have a title, then use the default, or if nothing is set at all, use noneSelectedText + if (!titleFragment.childNodes.length) { + titleFragment = generateOption.text({ + text: typeof this.options.title !== 'undefined' ? this.options.title : this.options.noneSelectedText + }, true); + } + + // strip all HTML tags and trim the result, then unescape any escaped tags + button.title = titleFragment.textContent.replace(/<[^>]*>?/g, '').trim(); + + if (this.options.sanitize && hasContent) { + sanitizeHtml([titleFragment], that.options.whiteList, that.options.sanitizeFn); + } + + buttonInner.innerHTML = ''; + buttonInner.appendChild(titleFragment); + + if (version.major < 4 && this.$newElement[0].classList.contains('bs3-has-addon')) { + var filterExpand = button.querySelector('.filter-expand'), + clone = buttonInner.cloneNode(true); + + clone.className = 'filter-expand'; + + if (filterExpand) { + button.replaceChild(clone, filterExpand); + } else { + button.appendChild(clone); + } + } + + this.$element.trigger('rendered' + EVENT_KEY); + }, + + /** + * @param [style] + * @param [status] + */ + setStyle: function (newStyle, status) { + var button = this.$button[0], + newElement = this.$newElement[0], + style = this.options.style.trim(), + buttonClass; + + if (this.$element.attr('class')) { + this.$newElement.addClass(this.$element.attr('class').replace(/selectpicker|mobile-device|bs-select-hidden|validate\[.*\]/gi, '')); + } + + if (version.major < 4) { + newElement.classList.add('bs3'); + + if (newElement.parentNode.classList.contains('input-group') && + (newElement.previousElementSibling || newElement.nextElementSibling) && + (newElement.previousElementSibling || newElement.nextElementSibling).classList.contains('input-group-addon') + ) { + newElement.classList.add('bs3-has-addon'); + } + } + + if (newStyle) { + buttonClass = newStyle.trim(); + } else { + buttonClass = style; + } + + if (status == 'add') { + if (buttonClass) button.classList.add.apply(button.classList, buttonClass.split(' ')); + } else if (status == 'remove') { + if (buttonClass) button.classList.remove.apply(button.classList, buttonClass.split(' ')); + } else { + if (style) button.classList.remove.apply(button.classList, style.split(' ')); + if (buttonClass) button.classList.add.apply(button.classList, buttonClass.split(' ')); + } + }, + + liHeight: function (refresh) { + if (!refresh && (this.options.size === false || this.sizeInfo)) return; + + if (!this.sizeInfo) this.sizeInfo = {}; + + var newElement = document.createElement('div'), + menu = document.createElement('div'), + menuInner = document.createElement('div'), + menuInnerInner = document.createElement('ul'), + divider = document.createElement('li'), + dropdownHeader = document.createElement('li'), + li = document.createElement('li'), + a = document.createElement('a'), + text = document.createElement('span'), + header = this.options.header && this.$menu.find('.' + classNames.POPOVERHEADER).length > 0 ? this.$menu.find('.' + classNames.POPOVERHEADER)[0].cloneNode(true) : null, + search = this.options.liveSearch ? document.createElement('div') : null, + actions = this.options.actionsBox && this.multiple && this.$menu.find('.bs-actionsbox').length > 0 ? this.$menu.find('.bs-actionsbox')[0].cloneNode(true) : null, + doneButton = this.options.doneButton && this.multiple && this.$menu.find('.bs-donebutton').length > 0 ? this.$menu.find('.bs-donebutton')[0].cloneNode(true) : null, + firstOption = this.$element.find('option')[0]; + + this.sizeInfo.selectWidth = this.$newElement[0].offsetWidth; + + text.className = 'text'; + a.className = 'dropdown-item ' + (firstOption ? firstOption.className : ''); + newElement.className = this.$menu[0].parentNode.className + ' ' + classNames.SHOW; + newElement.style.width = this.sizeInfo.selectWidth + 'px'; + if (this.options.width === 'auto') menu.style.minWidth = 0; + menu.className = classNames.MENU + ' ' + classNames.SHOW; + menuInner.className = 'inner ' + classNames.SHOW; + menuInnerInner.className = classNames.MENU + ' inner ' + (version.major === '4' ? classNames.SHOW : ''); + divider.className = classNames.DIVIDER; + dropdownHeader.className = 'dropdown-header'; + + text.appendChild(document.createTextNode('\u200b')); + a.appendChild(text); + li.appendChild(a); + dropdownHeader.appendChild(text.cloneNode(true)); + + if (this.selectpicker.view.widestOption) { + menuInnerInner.appendChild(this.selectpicker.view.widestOption.cloneNode(true)); + } + + menuInnerInner.appendChild(li); + menuInnerInner.appendChild(divider); + menuInnerInner.appendChild(dropdownHeader); + if (header) menu.appendChild(header); + if (search) { + var input = document.createElement('input'); + search.className = 'bs-searchbox'; + input.className = 'form-control'; + search.appendChild(input); + menu.appendChild(search); + } + if (actions) menu.appendChild(actions); + menuInner.appendChild(menuInnerInner); + menu.appendChild(menuInner); + if (doneButton) menu.appendChild(doneButton); + newElement.appendChild(menu); + + document.body.appendChild(newElement); + + var liHeight = li.offsetHeight, + dropdownHeaderHeight = dropdownHeader ? dropdownHeader.offsetHeight : 0, + headerHeight = header ? header.offsetHeight : 0, + searchHeight = search ? search.offsetHeight : 0, + actionsHeight = actions ? actions.offsetHeight : 0, + doneButtonHeight = doneButton ? doneButton.offsetHeight : 0, + dividerHeight = $(divider).outerHeight(true), + // fall back to jQuery if getComputedStyle is not supported + menuStyle = window.getComputedStyle ? window.getComputedStyle(menu) : false, + menuWidth = menu.offsetWidth, + $menu = menuStyle ? null : $(menu), + menuPadding = { + vert: toInteger(menuStyle ? menuStyle.paddingTop : $menu.css('paddingTop')) + + toInteger(menuStyle ? menuStyle.paddingBottom : $menu.css('paddingBottom')) + + toInteger(menuStyle ? menuStyle.borderTopWidth : $menu.css('borderTopWidth')) + + toInteger(menuStyle ? menuStyle.borderBottomWidth : $menu.css('borderBottomWidth')), + horiz: toInteger(menuStyle ? menuStyle.paddingLeft : $menu.css('paddingLeft')) + + toInteger(menuStyle ? menuStyle.paddingRight : $menu.css('paddingRight')) + + toInteger(menuStyle ? menuStyle.borderLeftWidth : $menu.css('borderLeftWidth')) + + toInteger(menuStyle ? menuStyle.borderRightWidth : $menu.css('borderRightWidth')) + }, + menuExtras = { + vert: menuPadding.vert + + toInteger(menuStyle ? menuStyle.marginTop : $menu.css('marginTop')) + + toInteger(menuStyle ? menuStyle.marginBottom : $menu.css('marginBottom')) + 2, + horiz: menuPadding.horiz + + toInteger(menuStyle ? menuStyle.marginLeft : $menu.css('marginLeft')) + + toInteger(menuStyle ? menuStyle.marginRight : $menu.css('marginRight')) + 2 + }, + scrollBarWidth; + + menuInner.style.overflowY = 'scroll'; + + scrollBarWidth = menu.offsetWidth - menuWidth; + + document.body.removeChild(newElement); + + this.sizeInfo.liHeight = liHeight; + this.sizeInfo.dropdownHeaderHeight = dropdownHeaderHeight; + this.sizeInfo.headerHeight = headerHeight; + this.sizeInfo.searchHeight = searchHeight; + this.sizeInfo.actionsHeight = actionsHeight; + this.sizeInfo.doneButtonHeight = doneButtonHeight; + this.sizeInfo.dividerHeight = dividerHeight; + this.sizeInfo.menuPadding = menuPadding; + this.sizeInfo.menuExtras = menuExtras; + this.sizeInfo.menuWidth = menuWidth; + this.sizeInfo.totalMenuWidth = this.sizeInfo.menuWidth; + this.sizeInfo.scrollBarWidth = scrollBarWidth; + this.sizeInfo.selectHeight = this.$newElement[0].offsetHeight; + + this.setPositionData(); + }, + + getSelectPosition: function () { + var that = this, + $window = $(window), + pos = that.$newElement.offset(), + $container = $(that.options.container), + containerPos; + + if (that.options.container && $container.length && !$container.is('body')) { + containerPos = $container.offset(); + containerPos.top += parseInt($container.css('borderTopWidth')); + containerPos.left += parseInt($container.css('borderLeftWidth')); + } else { + containerPos = { top: 0, left: 0 }; + } + + var winPad = that.options.windowPadding; + + this.sizeInfo.selectOffsetTop = pos.top - containerPos.top - $window.scrollTop(); + this.sizeInfo.selectOffsetBot = $window.height() - this.sizeInfo.selectOffsetTop - this.sizeInfo.selectHeight - containerPos.top - winPad[2]; + this.sizeInfo.selectOffsetLeft = pos.left - containerPos.left - $window.scrollLeft(); + this.sizeInfo.selectOffsetRight = $window.width() - this.sizeInfo.selectOffsetLeft - this.sizeInfo.selectWidth - containerPos.left - winPad[1]; + this.sizeInfo.selectOffsetTop -= winPad[0]; + this.sizeInfo.selectOffsetLeft -= winPad[3]; + }, + + setMenuSize: function (isAuto) { + this.getSelectPosition(); + + var selectWidth = this.sizeInfo.selectWidth, + liHeight = this.sizeInfo.liHeight, + headerHeight = this.sizeInfo.headerHeight, + searchHeight = this.sizeInfo.searchHeight, + actionsHeight = this.sizeInfo.actionsHeight, + doneButtonHeight = this.sizeInfo.doneButtonHeight, + divHeight = this.sizeInfo.dividerHeight, + menuPadding = this.sizeInfo.menuPadding, + menuInnerHeight, + menuHeight, + divLength = 0, + minHeight, + _minHeight, + maxHeight, + menuInnerMinHeight, + estimate; + + if (this.options.dropupAuto) { + // Get the estimated height of the menu without scrollbars. + // This is useful for smaller menus, where there might be plenty of room + // below the button without setting dropup, but we can't know + // the exact height of the menu until createView is called later + estimate = liHeight * this.selectpicker.current.elements.length + menuPadding.vert; + this.$newElement.toggleClass(classNames.DROPUP, this.sizeInfo.selectOffsetTop - this.sizeInfo.selectOffsetBot > this.sizeInfo.menuExtras.vert && estimate + this.sizeInfo.menuExtras.vert + 50 > this.sizeInfo.selectOffsetBot); + } + + if (this.options.size === 'auto') { + _minHeight = this.selectpicker.current.elements.length > 3 ? this.sizeInfo.liHeight * 3 + this.sizeInfo.menuExtras.vert - 2 : 0; + menuHeight = this.sizeInfo.selectOffsetBot - this.sizeInfo.menuExtras.vert; + minHeight = _minHeight + headerHeight + searchHeight + actionsHeight + doneButtonHeight; + menuInnerMinHeight = Math.max(_minHeight - menuPadding.vert, 0); + + if (this.$newElement.hasClass(classNames.DROPUP)) { + menuHeight = this.sizeInfo.selectOffsetTop - this.sizeInfo.menuExtras.vert; + } + + maxHeight = menuHeight; + menuInnerHeight = menuHeight - headerHeight - searchHeight - actionsHeight - doneButtonHeight - menuPadding.vert; + } else if (this.options.size && this.options.size != 'auto' && this.selectpicker.current.elements.length > this.options.size) { + for (var i = 0; i < this.options.size; i++) { + if (this.selectpicker.current.data[i].type === 'divider') divLength++; + } + + menuHeight = liHeight * this.options.size + divLength * divHeight + menuPadding.vert; + menuInnerHeight = menuHeight - menuPadding.vert; + maxHeight = menuHeight + headerHeight + searchHeight + actionsHeight + doneButtonHeight; + minHeight = menuInnerMinHeight = ''; + } + + if (this.options.dropdownAlignRight === 'auto') { + this.$menu.toggleClass(classNames.MENURIGHT, this.sizeInfo.selectOffsetLeft > this.sizeInfo.selectOffsetRight && this.sizeInfo.selectOffsetRight < (this.sizeInfo.totalMenuWidth - selectWidth)); + } + + this.$menu.css({ + 'max-height': maxHeight + 'px', + 'overflow': 'hidden', + 'min-height': minHeight + 'px' + }); + + this.$menuInner.css({ + 'max-height': menuInnerHeight + 'px', + 'overflow-y': 'auto', + 'min-height': menuInnerMinHeight + 'px' + }); + + // ensure menuInnerHeight is always a positive number to prevent issues calculating chunkSize in createView + this.sizeInfo.menuInnerHeight = Math.max(menuInnerHeight, 1); + + if (this.selectpicker.current.data.length && this.selectpicker.current.data[this.selectpicker.current.data.length - 1].position > this.sizeInfo.menuInnerHeight) { + this.sizeInfo.hasScrollBar = true; + this.sizeInfo.totalMenuWidth = this.sizeInfo.menuWidth + this.sizeInfo.scrollBarWidth; + + this.$menu.css('min-width', this.sizeInfo.totalMenuWidth); + } + + if (this.dropdown && this.dropdown._popper) this.dropdown._popper.update(); + }, + + setSize: function (refresh) { + this.liHeight(refresh); + + if (this.options.header) this.$menu.css('padding-top', 0); + if (this.options.size === false) return; + + var that = this, + $window = $(window), + selectedIndex, + offset = 0; + + this.setMenuSize(); + + if (this.options.liveSearch) { + this.$searchbox + .off('input.setMenuSize propertychange.setMenuSize') + .on('input.setMenuSize propertychange.setMenuSize', function () { + return that.setMenuSize(); + }); + } + + if (this.options.size === 'auto') { + $window + .off('resize' + EVENT_KEY + '.' + this.selectId + '.setMenuSize' + ' scroll' + EVENT_KEY + '.' + this.selectId + '.setMenuSize') + .on('resize' + EVENT_KEY + '.' + this.selectId + '.setMenuSize' + ' scroll' + EVENT_KEY + '.' + this.selectId + '.setMenuSize', function () { + return that.setMenuSize(); + }); + } else if (this.options.size && this.options.size != 'auto' && this.selectpicker.current.elements.length > this.options.size) { + $window.off('resize' + EVENT_KEY + '.' + this.selectId + '.setMenuSize' + ' scroll' + EVENT_KEY + '.' + this.selectId + '.setMenuSize'); + } + + if (refresh) { + offset = this.$menuInner[0].scrollTop; + } else if (!that.multiple) { + var element = that.$element[0]; + selectedIndex = (element.options[element.selectedIndex] || {}).liIndex; + + if (typeof selectedIndex === 'number' && that.options.size !== false) { + offset = that.sizeInfo.liHeight * selectedIndex; + offset = offset - (that.sizeInfo.menuInnerHeight / 2) + (that.sizeInfo.liHeight / 2); + } + } + + that.createView(false, offset); + }, + + setWidth: function () { + var that = this; + + if (this.options.width === 'auto') { + requestAnimationFrame(function () { + that.$menu.css('min-width', '0'); + + that.$element.on('loaded' + EVENT_KEY, function () { + that.liHeight(); + that.setMenuSize(); + + // Get correct width if element is hidden + var $selectClone = that.$newElement.clone().appendTo('body'), + btnWidth = $selectClone.css('width', 'auto').children('button').outerWidth(); + + $selectClone.remove(); + + // Set width to whatever's larger, button title or longest option + that.sizeInfo.selectWidth = Math.max(that.sizeInfo.totalMenuWidth, btnWidth); + that.$newElement.css('width', that.sizeInfo.selectWidth + 'px'); + }); + }); + } else if (this.options.width === 'fit') { + // Remove inline min-width so width can be changed from 'auto' + this.$menu.css('min-width', ''); + this.$newElement.css('width', '').addClass('fit-width'); + } else if (this.options.width) { + // Remove inline min-width so width can be changed from 'auto' + this.$menu.css('min-width', ''); + this.$newElement.css('width', this.options.width); + } else { + // Remove inline min-width/width so width can be changed + this.$menu.css('min-width', ''); + this.$newElement.css('width', ''); + } + // Remove fit-width class if width is changed programmatically + if (this.$newElement.hasClass('fit-width') && this.options.width !== 'fit') { + this.$newElement[0].classList.remove('fit-width'); + } + }, + + selectPosition: function () { + this.$bsContainer = $('
    '); + + var that = this, + $container = $(this.options.container), + pos, + containerPos, + actualHeight, + getPlacement = function ($element) { + var containerPosition = {}, + // fall back to dropdown's default display setting if display is not manually set + display = that.options.display || ( + // Bootstrap 3 doesn't have $.fn.dropdown.Constructor.Default + $.fn.dropdown.Constructor.Default ? $.fn.dropdown.Constructor.Default.display + : false + ); + + that.$bsContainer.addClass($element.attr('class').replace(/form-control|fit-width/gi, '')).toggleClass(classNames.DROPUP, $element.hasClass(classNames.DROPUP)); + pos = $element.offset(); + + if (!$container.is('body')) { + containerPos = $container.offset(); + containerPos.top += parseInt($container.css('borderTopWidth')) - $container.scrollTop(); + containerPos.left += parseInt($container.css('borderLeftWidth')) - $container.scrollLeft(); + } else { + containerPos = { top: 0, left: 0 }; + } + + actualHeight = $element.hasClass(classNames.DROPUP) ? 0 : $element[0].offsetHeight; + + // Bootstrap 4+ uses Popper for menu positioning + if (version.major < 4 || display === 'static') { + containerPosition.top = pos.top - containerPos.top + actualHeight; + containerPosition.left = pos.left - containerPos.left; + } + + containerPosition.width = $element[0].offsetWidth; + + that.$bsContainer.css(containerPosition); + }; + + this.$button.on('click.bs.dropdown.data-api', function () { + if (that.isDisabled()) { + return; + } + + getPlacement(that.$newElement); + + that.$bsContainer + .appendTo(that.options.container) + .toggleClass(classNames.SHOW, !that.$button.hasClass(classNames.SHOW)) + .append(that.$menu); + }); + + $(window) + .off('resize' + EVENT_KEY + '.' + this.selectId + ' scroll' + EVENT_KEY + '.' + this.selectId) + .on('resize' + EVENT_KEY + '.' + this.selectId + ' scroll' + EVENT_KEY + '.' + this.selectId, function () { + var isActive = that.$newElement.hasClass(classNames.SHOW); + + if (isActive) getPlacement(that.$newElement); + }); + + this.$element.on('hide' + EVENT_KEY, function () { + that.$menu.data('height', that.$menu.height()); + that.$bsContainer.detach(); + }); + }, + + setOptionStatus: function () { + var that = this; + + that.noScroll = false; + + if (that.selectpicker.view.visibleElements && that.selectpicker.view.visibleElements.length) { + for (var i = 0; i < that.selectpicker.view.visibleElements.length; i++) { + var liData = that.selectpicker.current.data[i + that.selectpicker.view.position0], + option = liData.option; + + if (option) { + that.setDisabled( + liData.index, + liData.disabled + ); + + that.setSelected( + liData.index, + option.selected + ); + } + } + } + }, + + /** + * @param {number} index - the index of the option that is being changed + * @param {boolean} selected - true if the option is being selected, false if being deselected + */ + setSelected: function (index, selected) { + var li = this.selectpicker.main.elements[index], + liData = this.selectpicker.main.data[index], + activeIndexIsSet = this.activeIndex !== undefined, + thisIsActive = this.activeIndex === index, + prevActive, + a, + // if current option is already active + // OR + // if the current option is being selected, it's NOT multiple, and + // activeIndex is undefined: + // - when the menu is first being opened, OR + // - after a search has been performed, OR + // - when retainActive is false when selecting a new option (i.e. index of the newly selected option is not the same as the current activeIndex) + keepActive = thisIsActive || (selected && !this.multiple && !activeIndexIsSet); + + liData.selected = selected; + + a = li.firstChild; + + if (selected) { + this.selectedIndex = index; + } + + li.classList.toggle('selected', selected); + li.classList.toggle('active', keepActive); + + if (keepActive) { + this.selectpicker.view.currentActive = li; + this.activeIndex = index; + } + + if (a) { + a.classList.toggle('selected', selected); + a.classList.toggle('active', keepActive); + a.setAttribute('aria-selected', selected); + } + + if (!keepActive) { + if (!activeIndexIsSet && selected && this.prevActiveIndex !== undefined) { + prevActive = this.selectpicker.main.elements[this.prevActiveIndex]; + + prevActive.classList.remove('active'); + if (prevActive.firstChild) { + prevActive.firstChild.classList.remove('active'); + } + } + } + }, + + /** + * @param {number} index - the index of the option that is being disabled + * @param {boolean} disabled - true if the option is being disabled, false if being enabled + */ + setDisabled: function (index, disabled) { + var li = this.selectpicker.main.elements[index], + a; + + this.selectpicker.main.data[index].disabled = disabled; + + a = li.firstChild; + + li.classList.toggle(classNames.DISABLED, disabled); + + if (a) { + if (version.major === '4') a.classList.toggle(classNames.DISABLED, disabled); + + a.setAttribute('aria-disabled', disabled); + + if (disabled) { + a.setAttribute('tabindex', -1); + } else { + a.setAttribute('tabindex', 0); + } + } + }, + + isDisabled: function () { + return this.$element[0].disabled; + }, + + checkDisabled: function () { + var that = this; + + if (this.isDisabled()) { + this.$newElement[0].classList.add(classNames.DISABLED); + this.$button.addClass(classNames.DISABLED).attr('tabindex', -1).attr('aria-disabled', true); + } else { + if (this.$button[0].classList.contains(classNames.DISABLED)) { + this.$newElement[0].classList.remove(classNames.DISABLED); + this.$button.removeClass(classNames.DISABLED).attr('aria-disabled', false); + } + + if (this.$button.attr('tabindex') == -1 && !this.$element.data('tabindex')) { + this.$button.removeAttr('tabindex'); + } + } + + this.$button.on('click', function () { + return !that.isDisabled(); + }); + }, + + togglePlaceholder: function () { + // much faster than calling $.val() + var element = this.$element[0], + selectedIndex = element.selectedIndex, + nothingSelected = selectedIndex === -1; + + if (!nothingSelected && !element.options[selectedIndex].value) nothingSelected = true; + + this.$button.toggleClass('bs-placeholder', nothingSelected); + }, + + tabIndex: function () { + if (this.$element.data('tabindex') !== this.$element.attr('tabindex') && + (this.$element.attr('tabindex') !== -98 && this.$element.attr('tabindex') !== '-98')) { + this.$element.data('tabindex', this.$element.attr('tabindex')); + this.$button.attr('tabindex', this.$element.data('tabindex')); + } + + this.$element.attr('tabindex', -98); + }, + + clickListener: function () { + var that = this, + $document = $(document); + + $document.data('spaceSelect', false); + + this.$button.on('keyup', function (e) { + if (/(32)/.test(e.keyCode.toString(10)) && $document.data('spaceSelect')) { + e.preventDefault(); + $document.data('spaceSelect', false); + } + }); + + this.$newElement.on('show.bs.dropdown', function () { + if (version.major > 3 && !that.dropdown) { + that.dropdown = that.$button.data('bs.dropdown'); + that.dropdown._menu = that.$menu[0]; + } + }); + + this.$button.on('click.bs.dropdown.data-api', function () { + if (!that.$newElement.hasClass(classNames.SHOW)) { + that.setSize(); + } + }); + + function setFocus () { + if (that.options.liveSearch) { + that.$searchbox.trigger('focus'); + } else { + that.$menuInner.trigger('focus'); + } + } + + function checkPopperExists () { + if (that.dropdown && that.dropdown._popper && that.dropdown._popper.state.isCreated) { + setFocus(); + } else { + requestAnimationFrame(checkPopperExists); + } + } + + this.$element.on('shown' + EVENT_KEY, function () { + if (that.$menuInner[0].scrollTop !== that.selectpicker.view.scrollTop) { + that.$menuInner[0].scrollTop = that.selectpicker.view.scrollTop; + } + + if (version.major > 3) { + requestAnimationFrame(checkPopperExists); + } else { + setFocus(); + } + }); + + this.$menuInner.on('click', 'li a', function (e, retainActive) { + var $this = $(this), + position0 = that.isVirtual() ? that.selectpicker.view.position0 : 0, + clickedData = that.selectpicker.current.data[$this.parent().index() + position0], + clickedIndex = clickedData.index, + prevValue = getSelectValues(that.$element[0]), + prevIndex = that.$element.prop('selectedIndex'), + triggerChange = true; + + // Don't close on multi choice menu + if (that.multiple && that.options.maxOptions !== 1) { + e.stopPropagation(); + } + + e.preventDefault(); + + // Don't run if the select is disabled + if (!that.isDisabled() && !$this.parent().hasClass(classNames.DISABLED)) { + var $options = that.$element.find('option'), + option = clickedData.option, + $option = $(option), + state = option.selected, + $optgroup = $option.parent('optgroup'), + $optgroupOptions = $optgroup.find('option'), + maxOptions = that.options.maxOptions, + maxOptionsGrp = $optgroup.data('maxOptions') || false; + + if (clickedIndex === that.activeIndex) retainActive = true; + + if (!retainActive) { + that.prevActiveIndex = that.activeIndex; + that.activeIndex = undefined; + } + + if (!that.multiple) { // Deselect all others if not multi select box + $options.prop('selected', false); + option.selected = true; + that.setSelected(clickedIndex, true); + } else { // Toggle the one we have chosen if we are multi select. + option.selected = !state; + + that.setSelected(clickedIndex, !state); + $this.trigger('blur'); + + if (maxOptions !== false || maxOptionsGrp !== false) { + var maxReached = maxOptions < $options.filter(':selected').length, + maxReachedGrp = maxOptionsGrp < $optgroup.find('option:selected').length; + + if ((maxOptions && maxReached) || (maxOptionsGrp && maxReachedGrp)) { + if (maxOptions && maxOptions == 1) { + $options.prop('selected', false); + $option.prop('selected', true); + + for (var i = 0; i < $options.length; i++) { + that.setSelected(i, false); + } + + that.setSelected(clickedIndex, true); + } else if (maxOptionsGrp && maxOptionsGrp == 1) { + $optgroup.find('option:selected').prop('selected', false); + $option.prop('selected', true); + + for (var i = 0; i < $optgroupOptions.length; i++) { + var option = $optgroupOptions[i]; + that.setSelected($options.index(option), false); + } + + that.setSelected(clickedIndex, true); + } else { + var maxOptionsText = typeof that.options.maxOptionsText === 'string' ? [that.options.maxOptionsText, that.options.maxOptionsText] : that.options.maxOptionsText, + maxOptionsArr = typeof maxOptionsText === 'function' ? maxOptionsText(maxOptions, maxOptionsGrp) : maxOptionsText, + maxTxt = maxOptionsArr[0].replace('{n}', maxOptions), + maxTxtGrp = maxOptionsArr[1].replace('{n}', maxOptionsGrp), + $notify = $('
    '); + // If {var} is set in array, replace it + /** @deprecated */ + if (maxOptionsArr[2]) { + maxTxt = maxTxt.replace('{var}', maxOptionsArr[2][maxOptions > 1 ? 0 : 1]); + maxTxtGrp = maxTxtGrp.replace('{var}', maxOptionsArr[2][maxOptionsGrp > 1 ? 0 : 1]); + } + + $option.prop('selected', false); + + that.$menu.append($notify); + + if (maxOptions && maxReached) { + $notify.append($('
    ' + maxTxt + '
    ')); + triggerChange = false; + that.$element.trigger('maxReached' + EVENT_KEY); + } + + if (maxOptionsGrp && maxReachedGrp) { + $notify.append($('
    ' + maxTxtGrp + '
    ')); + triggerChange = false; + that.$element.trigger('maxReachedGrp' + EVENT_KEY); + } + + setTimeout(function () { + that.setSelected(clickedIndex, false); + }, 10); + + $notify.delay(750).fadeOut(300, function () { + $(this).remove(); + }); + } + } + } + } + + if (!that.multiple || (that.multiple && that.options.maxOptions === 1)) { + that.$button.trigger('focus'); + } else if (that.options.liveSearch) { + that.$searchbox.trigger('focus'); + } + + // Trigger select 'change' + if (triggerChange) { + if ((prevValue != getSelectValues(that.$element[0]) && that.multiple) || (prevIndex != that.$element.prop('selectedIndex') && !that.multiple)) { + // $option.prop('selected') is current option state (selected/unselected). prevValue is the value of the select prior to being changed. + changedArguments = [option.index, $option.prop('selected'), prevValue]; + that.$element + .triggerNative('change'); + } + } + } + }); + + this.$menu.on('click', 'li.' + classNames.DISABLED + ' a, .' + classNames.POPOVERHEADER + ', .' + classNames.POPOVERHEADER + ' :not(.close)', function (e) { + if (e.currentTarget == this) { + e.preventDefault(); + e.stopPropagation(); + if (that.options.liveSearch && !$(e.target).hasClass('close')) { + that.$searchbox.trigger('focus'); + } else { + that.$button.trigger('focus'); + } + } + }); + + this.$menuInner.on('click', '.divider, .dropdown-header', function (e) { + e.preventDefault(); + e.stopPropagation(); + if (that.options.liveSearch) { + that.$searchbox.trigger('focus'); + } else { + that.$button.trigger('focus'); + } + }); + + this.$menu.on('click', '.' + classNames.POPOVERHEADER + ' .close', function () { + that.$button.trigger('click'); + }); + + this.$searchbox.on('click', function (e) { + e.stopPropagation(); + }); + + this.$menu.on('click', '.actions-btn', function (e) { + if (that.options.liveSearch) { + that.$searchbox.trigger('focus'); + } else { + that.$button.trigger('focus'); + } + + e.preventDefault(); + e.stopPropagation(); + + if ($(this).hasClass('bs-select-all')) { + that.selectAll(); + } else { + that.deselectAll(); + } + }); + + this.$element + .on('change' + EVENT_KEY, function () { + that.render(); + that.$element.trigger('changed' + EVENT_KEY, changedArguments); + changedArguments = null; + }) + .on('focus' + EVENT_KEY, function () { + if (!that.options.mobile) that.$button.trigger('focus'); + }); + }, + + liveSearchListener: function () { + var that = this, + noResults = document.createElement('li'); + + this.$button.on('click.bs.dropdown.data-api', function () { + if (!!that.$searchbox.val()) { + that.$searchbox.val(''); + } + }); + + this.$searchbox.on('click.bs.dropdown.data-api focus.bs.dropdown.data-api touchend.bs.dropdown.data-api', function (e) { + e.stopPropagation(); + }); + + this.$searchbox.on('input propertychange', function () { + var searchValue = that.$searchbox.val(); + + that.selectpicker.search.elements = []; + that.selectpicker.search.data = []; + + if (searchValue) { + var i, + searchMatch = [], + q = searchValue.toUpperCase(), + cache = {}, + cacheArr = [], + searchStyle = that._searchStyle(), + normalizeSearch = that.options.liveSearchNormalize; + + if (normalizeSearch) q = normalizeToBase(q); + + that._$lisSelected = that.$menuInner.find('.selected'); + + for (var i = 0; i < that.selectpicker.main.data.length; i++) { + var li = that.selectpicker.main.data[i]; + + if (!cache[i]) { + cache[i] = stringSearch(li, q, searchStyle, normalizeSearch); + } + + if (cache[i] && li.headerIndex !== undefined && cacheArr.indexOf(li.headerIndex) === -1) { + if (li.headerIndex > 0) { + cache[li.headerIndex - 1] = true; + cacheArr.push(li.headerIndex - 1); + } + + cache[li.headerIndex] = true; + cacheArr.push(li.headerIndex); + + cache[li.lastIndex + 1] = true; + } + + if (cache[i] && li.type !== 'optgroup-label') cacheArr.push(i); + } + + for (var i = 0, cacheLen = cacheArr.length; i < cacheLen; i++) { + var index = cacheArr[i], + prevIndex = cacheArr[i - 1], + li = that.selectpicker.main.data[index], + liPrev = that.selectpicker.main.data[prevIndex]; + + if (li.type !== 'divider' || (li.type === 'divider' && liPrev && liPrev.type !== 'divider' && cacheLen - 1 !== i)) { + that.selectpicker.search.data.push(li); + searchMatch.push(that.selectpicker.main.elements[index]); + } + } + + that.activeIndex = undefined; + that.noScroll = true; + that.$menuInner.scrollTop(0); + that.selectpicker.search.elements = searchMatch; + that.createView(true); + + if (!searchMatch.length) { + noResults.className = 'no-results'; + noResults.innerHTML = that.options.noneResultsText.replace('{0}', '"' + htmlEscape(searchValue) + '"'); + that.$menuInner[0].firstChild.appendChild(noResults); + } + } else { + that.$menuInner.scrollTop(0); + that.createView(false); + } + }); + }, + + _searchStyle: function () { + return this.options.liveSearchStyle || 'contains'; + }, + + val: function (value) { + if (typeof value !== 'undefined') { + var prevValue = getSelectValues(this.$element[0]); + + changedArguments = [null, null, prevValue]; + + this.$element + .val(value) + .trigger('changed' + EVENT_KEY, changedArguments); + + this.render(); + + changedArguments = null; + + return this.$element; + } else { + return this.$element.val(); + } + }, + + changeAll: function (status) { + if (!this.multiple) return; + if (typeof status === 'undefined') status = true; + + var element = this.$element[0], + previousSelected = 0, + currentSelected = 0, + prevValue = getSelectValues(element); + + element.classList.add('bs-select-hidden'); + + for (var i = 0, len = this.selectpicker.current.elements.length; i < len; i++) { + var liData = this.selectpicker.current.data[i], + option = liData.option; + + if (option && !liData.disabled && liData.type !== 'divider') { + if (liData.selected) previousSelected++; + option.selected = status; + if (status) currentSelected++; + } + } + + element.classList.remove('bs-select-hidden'); + + if (previousSelected === currentSelected) return; + + this.setOptionStatus(); + + this.togglePlaceholder(); + + changedArguments = [null, null, prevValue]; + + this.$element + .triggerNative('change'); + }, + + selectAll: function () { + return this.changeAll(true); + }, + + deselectAll: function () { + return this.changeAll(false); + }, + + toggle: function (e) { + e = e || window.event; + + if (e) e.stopPropagation(); + + this.$button.trigger('click.bs.dropdown.data-api'); + }, + + keydown: function (e) { + var $this = $(this), + isToggle = $this.hasClass('dropdown-toggle'), + $parent = isToggle ? $this.closest('.dropdown') : $this.closest(Selector.MENU), + that = $parent.data('this'), + $items = that.findLis(), + index, + isActive, + liActive, + activeLi, + offset, + updateScroll = false, + downOnTab = e.which === keyCodes.TAB && !isToggle && !that.options.selectOnTab, + isArrowKey = REGEXP_ARROW.test(e.which) || downOnTab, + scrollTop = that.$menuInner[0].scrollTop, + isVirtual = that.isVirtual(), + position0 = isVirtual === true ? that.selectpicker.view.position0 : 0; + + isActive = that.$newElement.hasClass(classNames.SHOW); + + if ( + !isActive && + ( + isArrowKey || + (e.which >= 48 && e.which <= 57) || + (e.which >= 96 && e.which <= 105) || + (e.which >= 65 && e.which <= 90) + ) + ) { + that.$button.trigger('click.bs.dropdown.data-api'); + + if (that.options.liveSearch) { + that.$searchbox.trigger('focus'); + return; + } + } + + if (e.which === keyCodes.ESCAPE && isActive) { + e.preventDefault(); + that.$button.trigger('click.bs.dropdown.data-api').trigger('focus'); + } + + if (isArrowKey) { // if up or down + if (!$items.length) return; + + // $items.index/.filter is too slow with a large list and no virtual scroll + index = isVirtual === true ? $items.index($items.filter('.active')) : that.activeIndex; + + if (index === undefined) index = -1; + + if (index !== -1) { + liActive = that.selectpicker.current.elements[index + position0]; + liActive.classList.remove('active'); + if (liActive.firstChild) liActive.firstChild.classList.remove('active'); + } + + if (e.which === keyCodes.ARROW_UP) { // up + if (index !== -1) index--; + if (index + position0 < 0) index += $items.length; + + if (!that.selectpicker.view.canHighlight[index + position0]) { + index = that.selectpicker.view.canHighlight.slice(0, index + position0).lastIndexOf(true) - position0; + if (index === -1) index = $items.length - 1; + } + } else if (e.which === keyCodes.ARROW_DOWN || downOnTab) { // down + index++; + if (index + position0 >= that.selectpicker.view.canHighlight.length) index = 0; + + if (!that.selectpicker.view.canHighlight[index + position0]) { + index = index + 1 + that.selectpicker.view.canHighlight.slice(index + position0 + 1).indexOf(true); + } + } + + e.preventDefault(); + + var liActiveIndex = position0 + index; + + if (e.which === keyCodes.ARROW_UP) { // up + // scroll to bottom and highlight last option + if (position0 === 0 && index === $items.length - 1) { + that.$menuInner[0].scrollTop = that.$menuInner[0].scrollHeight; + + liActiveIndex = that.selectpicker.current.elements.length - 1; + } else { + activeLi = that.selectpicker.current.data[liActiveIndex]; + offset = activeLi.position - activeLi.height; + + updateScroll = offset < scrollTop; + } + } else if (e.which === keyCodes.ARROW_DOWN || downOnTab) { // down + // scroll to top and highlight first option + if (index === 0) { + that.$menuInner[0].scrollTop = 0; + + liActiveIndex = 0; + } else { + activeLi = that.selectpicker.current.data[liActiveIndex]; + offset = activeLi.position - that.sizeInfo.menuInnerHeight; + + updateScroll = offset > scrollTop; + } + } + + liActive = that.selectpicker.current.elements[liActiveIndex]; + + if (liActive) { + liActive.classList.add('active'); + if (liActive.firstChild) liActive.firstChild.classList.add('active'); + } + + that.activeIndex = that.selectpicker.current.data[liActiveIndex].index; + + that.selectpicker.view.currentActive = liActive; + + if (updateScroll) that.$menuInner[0].scrollTop = offset; + + if (that.options.liveSearch) { + that.$searchbox.trigger('focus'); + } else { + $this.trigger('focus'); + } + } else if ( + (!$this.is('input') && !REGEXP_TAB_OR_ESCAPE.test(e.which)) || + (e.which === keyCodes.SPACE && that.selectpicker.keydown.keyHistory) + ) { + var searchMatch, + matches = [], + keyHistory; + + e.preventDefault(); + + that.selectpicker.keydown.keyHistory += keyCodeMap[e.which]; + + if (that.selectpicker.keydown.resetKeyHistory.cancel) clearTimeout(that.selectpicker.keydown.resetKeyHistory.cancel); + that.selectpicker.keydown.resetKeyHistory.cancel = that.selectpicker.keydown.resetKeyHistory.start(); + + keyHistory = that.selectpicker.keydown.keyHistory; + + // if all letters are the same, set keyHistory to just the first character when searching + if (/^(.)\1+$/.test(keyHistory)) { + keyHistory = keyHistory.charAt(0); + } + + // find matches + for (var i = 0; i < that.selectpicker.current.data.length; i++) { + var li = that.selectpicker.current.data[i], + hasMatch; + + hasMatch = stringSearch(li, keyHistory, 'startsWith', true); + + if (hasMatch && that.selectpicker.view.canHighlight[i]) { + matches.push(li.index); + } + } + + if (matches.length) { + var matchIndex = 0; + + $items.removeClass('active').find('a').removeClass('active'); + + // either only one key has been pressed or they are all the same key + if (keyHistory.length === 1) { + matchIndex = matches.indexOf(that.activeIndex); + + if (matchIndex === -1 || matchIndex === matches.length - 1) { + matchIndex = 0; + } else { + matchIndex++; + } + } + + searchMatch = matches[matchIndex]; + + activeLi = that.selectpicker.main.data[searchMatch]; + + if (scrollTop - activeLi.position > 0) { + offset = activeLi.position - activeLi.height; + updateScroll = true; + } else { + offset = activeLi.position - that.sizeInfo.menuInnerHeight; + // if the option is already visible at the current scroll position, just keep it the same + updateScroll = activeLi.position > scrollTop + that.sizeInfo.menuInnerHeight; + } + + liActive = that.selectpicker.main.elements[searchMatch]; + liActive.classList.add('active'); + if (liActive.firstChild) liActive.firstChild.classList.add('active'); + that.activeIndex = matches[matchIndex]; + + liActive.firstChild.focus(); + + if (updateScroll) that.$menuInner[0].scrollTop = offset; + + $this.trigger('focus'); + } + } + + // Select focused option if "Enter", "Spacebar" or "Tab" (when selectOnTab is true) are pressed inside the menu. + if ( + isActive && + ( + (e.which === keyCodes.SPACE && !that.selectpicker.keydown.keyHistory) || + e.which === keyCodes.ENTER || + (e.which === keyCodes.TAB && that.options.selectOnTab) + ) + ) { + if (e.which !== keyCodes.SPACE) e.preventDefault(); + + if (!that.options.liveSearch || e.which !== keyCodes.SPACE) { + that.$menuInner.find('.active a').trigger('click', true); // retain active class + $this.trigger('focus'); + + if (!that.options.liveSearch) { + // Prevent screen from scrolling if the user hits the spacebar + e.preventDefault(); + // Fixes spacebar selection of dropdown items in FF & IE + $(document).data('spaceSelect', true); + } + } + } + }, + + mobile: function () { + this.$element[0].classList.add('mobile-device'); + }, + + refresh: function () { + // update options if data attributes have been changed + var config = $.extend({}, this.options, this.$element.data()); + this.options = config; + + this.checkDisabled(); + this.setStyle(); + this.render(); + this.createLi(); + this.setWidth(); + + this.setSize(true); + + this.$element.trigger('refreshed' + EVENT_KEY); + }, + + hide: function () { + this.$newElement.hide(); + }, + + show: function () { + this.$newElement.show(); + }, + + remove: function () { + this.$newElement.remove(); + this.$element.remove(); + }, + + destroy: function () { + this.$newElement.before(this.$element).remove(); + + if (this.$bsContainer) { + this.$bsContainer.remove(); + } else { + this.$menu.remove(); + } + + this.$element + .off(EVENT_KEY) + .removeData('selectpicker') + .removeClass('bs-select-hidden selectpicker'); + + $(window).off(EVENT_KEY + '.' + this.selectId); + } + }; + + // SELECTPICKER PLUGIN DEFINITION + // ============================== + function Plugin (option) { + // get the args of the outer function.. + var args = arguments; + // The arguments of the function are explicitly re-defined from the argument list, because the shift causes them + // to get lost/corrupted in android 2.3 and IE9 #715 #775 + var _option = option; + + [].shift.apply(args); + + // if the version was not set successfully + if (!version.success) { + // try to retreive it again + try { + version.full = ($.fn.dropdown.Constructor.VERSION || '').split(' ')[0].split('.'); + } catch (err) { + // fall back to use BootstrapVersion if set + if (Selectpicker.BootstrapVersion) { + version.full = Selectpicker.BootstrapVersion.split(' ')[0].split('.'); + } else { + version.full = [version.major, '0', '0']; + + console.warn( + 'There was an issue retrieving Bootstrap\'s version. ' + + 'Ensure Bootstrap is being loaded before bootstrap-select and there is no namespace collision. ' + + 'If loading Bootstrap asynchronously, the version may need to be manually specified via $.fn.selectpicker.Constructor.BootstrapVersion.', + err + ); + } + } + + version.major = version.full[0]; + version.success = true; + } + + if (version.major === '4') { + // some defaults need to be changed if using Bootstrap 4 + // check to see if they have already been manually changed before forcing them to update + var toUpdate = []; + + if (Selectpicker.DEFAULTS.style === classNames.BUTTONCLASS) toUpdate.push({ name: 'style', className: 'BUTTONCLASS' }); + if (Selectpicker.DEFAULTS.iconBase === classNames.ICONBASE) toUpdate.push({ name: 'iconBase', className: 'ICONBASE' }); + if (Selectpicker.DEFAULTS.tickIcon === classNames.TICKICON) toUpdate.push({ name: 'tickIcon', className: 'TICKICON' }); + + classNames.DIVIDER = 'dropdown-divider'; + classNames.SHOW = 'show'; + classNames.BUTTONCLASS = 'btn-light'; + classNames.POPOVERHEADER = 'popover-header'; + classNames.ICONBASE = ''; + classNames.TICKICON = 'bs-ok-default'; + + for (var i = 0; i < toUpdate.length; i++) { + var option = toUpdate[i]; + Selectpicker.DEFAULTS[option.name] = classNames[option.className]; + } + } + + var value; + var chain = this.each(function () { + var $this = $(this); + if ($this.is('select')) { + var data = $this.data('selectpicker'), + options = typeof _option == 'object' && _option; + + if (!data) { + var dataAttributes = $this.data(); + + for (var dataAttr in dataAttributes) { + if (dataAttributes.hasOwnProperty(dataAttr) && $.inArray(dataAttr, DISALLOWED_ATTRIBUTES) !== -1) { + delete dataAttributes[dataAttr]; + } + } + + var config = $.extend({}, Selectpicker.DEFAULTS, $.fn.selectpicker.defaults || {}, dataAttributes, options); + config.template = $.extend({}, Selectpicker.DEFAULTS.template, ($.fn.selectpicker.defaults ? $.fn.selectpicker.defaults.template : {}), dataAttributes.template, options.template); + $this.data('selectpicker', (data = new Selectpicker(this, config))); + } else if (options) { + for (var i in options) { + if (options.hasOwnProperty(i)) { + data.options[i] = options[i]; + } + } + } + + if (typeof _option == 'string') { + if (data[_option] instanceof Function) { + value = data[_option].apply(data, args); + } else { + value = data.options[_option]; + } + } + } + }); + + if (typeof value !== 'undefined') { + // noinspection JSUnusedAssignment + return value; + } else { + return chain; + } + } + + var old = $.fn.selectpicker; + $.fn.selectpicker = Plugin; + $.fn.selectpicker.Constructor = Selectpicker; + + // SELECTPICKER NO CONFLICT + // ======================== + $.fn.selectpicker.noConflict = function () { + $.fn.selectpicker = old; + return this; + }; + + $(document) + .off('keydown.bs.dropdown.data-api') + .on('keydown' + EVENT_KEY, '.bootstrap-select [data-toggle="dropdown"], .bootstrap-select [role="listbox"], .bootstrap-select .bs-searchbox input', Selectpicker.prototype.keydown) + .on('focusin.modal', '.bootstrap-select [data-toggle="dropdown"], .bootstrap-select [role="listbox"], .bootstrap-select .bs-searchbox input', function (e) { + e.stopPropagation(); + }); + + // SELECTPICKER DATA-API + // ===================== + $(window).on('load' + EVENT_KEY + '.data-api', function () { + $('.selectpicker').each(function () { + var $selectpicker = $(this); + Plugin.call($selectpicker, $selectpicker.data()); + }) + }); +})(jQuery); + + +})); +//# sourceMappingURL=bootstrap-select.js.map \ No newline at end of file diff --git a/templates/yandexhome/css/bootstrap-select.js.map b/templates/yandexhome/css/bootstrap-select.js.map new file mode 100644 index 0000000..0c36f84 --- /dev/null +++ b/templates/yandexhome/css/bootstrap-select.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../js/bootstrap-select.js"],"names":[],"mappings":"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AAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACnF,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACnF,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAC/B,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAC/B,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACzD,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACzD,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACzD,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACzD,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAC/B,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACnF,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACnF,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACzD,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACzD,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAC5C,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC;AAChC,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC;AAChC,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC;AAClB,IAAI,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AAC/B,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAClD,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAClD,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACjE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACjE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACjE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAChF,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAChF,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACjE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACjE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACjE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAChF,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAChF,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACnC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAClD,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAChF,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAChF,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACjE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACjE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAClD,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAClD,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAClD,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAClD,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACjE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACjE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAClD,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAClD,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAC/F,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,E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b,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACb,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAA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","file":"bootstrap-select.js","sourcesContent":["(function ($) {\r\n 'use strict';\r\n\r\n var DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn'];\r\n\r\n var uriAttrs = [\r\n 'background',\r\n 'cite',\r\n 'href',\r\n 'itemtype',\r\n 'longdesc',\r\n 'poster',\r\n 'src',\r\n 'xlink:href'\r\n ];\r\n\r\n var ARIA_ATTRIBUTE_PATTERN = /^aria-[\\w-]*$/i;\r\n\r\n var DefaultWhitelist = {\r\n // Global attributes allowed on any supplied element below.\r\n '*': ['class', 'dir', 'id', 'lang', 'role', 'tabindex', 'style', ARIA_ATTRIBUTE_PATTERN],\r\n a: ['target', 'href', 'title', 'rel'],\r\n area: [],\r\n b: [],\r\n br: [],\r\n col: [],\r\n code: [],\r\n div: [],\r\n em: [],\r\n hr: [],\r\n h1: [],\r\n h2: [],\r\n h3: [],\r\n h4: [],\r\n h5: [],\r\n h6: [],\r\n i: [],\r\n img: ['src', 'alt', 'title', 'width', 'height'],\r\n li: [],\r\n ol: [],\r\n p: [],\r\n pre: [],\r\n s: [],\r\n small: [],\r\n span: [],\r\n sub: [],\r\n sup: [],\r\n strong: [],\r\n u: [],\r\n ul: []\r\n }\r\n\r\n /**\r\n * A pattern that recognizes a commonly useful subset of URLs that are safe.\r\n *\r\n * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts\r\n */\r\n var SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi;\r\n\r\n /**\r\n * A pattern that matches safe data URLs. Only matches image, video and audio types.\r\n *\r\n * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts\r\n */\r\n var DATA_URL_PATTERN = /^data:(?:image\\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\\/(?:mpeg|mp4|ogg|webm)|audio\\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i;\r\n\r\n function allowedAttribute (attr, allowedAttributeList) {\r\n var attrName = attr.nodeName.toLowerCase()\r\n\r\n if ($.inArray(attrName, allowedAttributeList) !== -1) {\r\n if ($.inArray(attrName, uriAttrs) !== -1) {\r\n return Boolean(attr.nodeValue.match(SAFE_URL_PATTERN) || attr.nodeValue.match(DATA_URL_PATTERN))\r\n }\r\n\r\n return true\r\n }\r\n\r\n var regExp = $(allowedAttributeList).filter(function (index, value) {\r\n return value instanceof RegExp\r\n })\r\n\r\n // Check if a regular expression validates the attribute.\r\n for (var i = 0, l = regExp.length; i < l; i++) {\r\n if (attrName.match(regExp[i])) {\r\n return true\r\n }\r\n }\r\n\r\n return false\r\n }\r\n\r\n function sanitizeHtml (unsafeElements, whiteList, sanitizeFn) {\r\n if (sanitizeFn && typeof sanitizeFn === 'function') {\r\n return sanitizeFn(unsafeElements);\r\n }\r\n\r\n var whitelistKeys = Object.keys(whiteList);\r\n\r\n for (var i = 0, len = unsafeElements.length; i < len; i++) {\r\n var elements = unsafeElements[i].querySelectorAll('*');\r\n\r\n for (var j = 0, len2 = elements.length; j < len2; j++) {\r\n var el = elements[j];\r\n var elName = el.nodeName.toLowerCase();\r\n\r\n if (whitelistKeys.indexOf(elName) === -1) {\r\n el.parentNode.removeChild(el);\r\n\r\n continue;\r\n }\r\n\r\n var attributeList = [].slice.call(el.attributes);\r\n var whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || []);\r\n\r\n for (var k = 0, len3 = attributeList.length; k < len3; k++) {\r\n var attr = attributeList[k];\r\n\r\n if (!allowedAttribute(attr, whitelistedAttributes)) {\r\n el.removeAttribute(attr.nodeName);\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n // Polyfill for browsers with no classList support\r\n // Remove in v2\r\n if (!('classList' in document.createElement('_'))) {\r\n (function (view) {\r\n if (!('Element' in view)) return;\r\n\r\n var classListProp = 'classList',\r\n protoProp = 'prototype',\r\n elemCtrProto = view.Element[protoProp],\r\n objCtr = Object,\r\n classListGetter = function () {\r\n var $elem = $(this);\r\n\r\n return {\r\n add: function (classes) {\r\n classes = Array.prototype.slice.call(arguments).join(' ');\r\n return $elem.addClass(classes);\r\n },\r\n remove: function (classes) {\r\n classes = Array.prototype.slice.call(arguments).join(' ');\r\n return $elem.removeClass(classes);\r\n },\r\n toggle: function (classes, force) {\r\n return $elem.toggleClass(classes, force);\r\n },\r\n contains: function (classes) {\r\n return $elem.hasClass(classes);\r\n }\r\n }\r\n };\r\n\r\n if (objCtr.defineProperty) {\r\n var classListPropDesc = {\r\n get: classListGetter,\r\n enumerable: true,\r\n configurable: true\r\n };\r\n try {\r\n objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);\r\n } catch (ex) { // IE 8 doesn't support enumerable:true\r\n // adding undefined to fight this issue https://github.com/eligrey/classList.js/issues/36\r\n // modernie IE8-MSW7 machine has IE8 8.0.6001.18702 and is affected\r\n if (ex.number === undefined || ex.number === -0x7FF5EC54) {\r\n classListPropDesc.enumerable = false;\r\n objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);\r\n }\r\n }\r\n } else if (objCtr[protoProp].__defineGetter__) {\r\n elemCtrProto.__defineGetter__(classListProp, classListGetter);\r\n }\r\n }(window));\r\n }\r\n\r\n var testElement = document.createElement('_');\r\n\r\n testElement.classList.add('c1', 'c2');\r\n\r\n if (!testElement.classList.contains('c2')) {\r\n var _add = DOMTokenList.prototype.add,\r\n _remove = DOMTokenList.prototype.remove;\r\n\r\n DOMTokenList.prototype.add = function () {\r\n Array.prototype.forEach.call(arguments, _add.bind(this));\r\n }\r\n\r\n DOMTokenList.prototype.remove = function () {\r\n Array.prototype.forEach.call(arguments, _remove.bind(this));\r\n }\r\n }\r\n\r\n testElement.classList.toggle('c3', false);\r\n\r\n // Polyfill for IE 10 and Firefox <24, where classList.toggle does not\r\n // support the second argument.\r\n if (testElement.classList.contains('c3')) {\r\n var _toggle = DOMTokenList.prototype.toggle;\r\n\r\n DOMTokenList.prototype.toggle = function (token, force) {\r\n if (1 in arguments && !this.contains(token) === !force) {\r\n return force;\r\n } else {\r\n return _toggle.call(this, token);\r\n }\r\n };\r\n }\r\n\r\n testElement = null;\r\n\r\n // shallow array comparison\r\n function isEqual (array1, array2) {\r\n return array1.length === array2.length && array1.every(function (element, index) {\r\n return element === array2[index];\r\n });\r\n };\r\n\r\n // \r\n if (!String.prototype.startsWith) {\r\n (function () {\r\n 'use strict'; // needed to support `apply`/`call` with `undefined`/`null`\r\n var defineProperty = (function () {\r\n // IE 8 only supports `Object.defineProperty` on DOM elements\r\n try {\r\n var object = {};\r\n var $defineProperty = Object.defineProperty;\r\n var result = $defineProperty(object, object, object) && $defineProperty;\r\n } catch (error) {\r\n }\r\n return result;\r\n }());\r\n var toString = {}.toString;\r\n var startsWith = function (search) {\r\n if (this == null) {\r\n throw new TypeError();\r\n }\r\n var string = String(this);\r\n if (search && toString.call(search) == '[object RegExp]') {\r\n throw new TypeError();\r\n }\r\n var stringLength = string.length;\r\n var searchString = String(search);\r\n var searchLength = searchString.length;\r\n var position = arguments.length > 1 ? arguments[1] : undefined;\r\n // `ToInteger`\r\n var pos = position ? Number(position) : 0;\r\n if (pos != pos) { // better `isNaN`\r\n pos = 0;\r\n }\r\n var start = Math.min(Math.max(pos, 0), stringLength);\r\n // Avoid the `indexOf` call if no match is possible\r\n if (searchLength + start > stringLength) {\r\n return false;\r\n }\r\n var index = -1;\r\n while (++index < searchLength) {\r\n if (string.charCodeAt(start + index) != searchString.charCodeAt(index)) {\r\n return false;\r\n }\r\n }\r\n return true;\r\n };\r\n if (defineProperty) {\r\n defineProperty(String.prototype, 'startsWith', {\r\n 'value': startsWith,\r\n 'configurable': true,\r\n 'writable': true\r\n });\r\n } else {\r\n String.prototype.startsWith = startsWith;\r\n }\r\n }());\r\n }\r\n\r\n if (!Object.keys) {\r\n Object.keys = function (\r\n o, // object\r\n k, // key\r\n r // result array\r\n ) {\r\n // initialize object and result\r\n r = [];\r\n // iterate over object keys\r\n for (k in o) {\r\n // fill result array with non-prototypical keys\r\n r.hasOwnProperty.call(o, k) && r.push(k);\r\n }\r\n // return result\r\n return r;\r\n };\r\n }\r\n\r\n if (HTMLSelectElement && !HTMLSelectElement.prototype.hasOwnProperty('selectedOptions')) {\r\n Object.defineProperty(HTMLSelectElement.prototype, 'selectedOptions', {\r\n get: function () {\r\n return this.querySelectorAll(':checked');\r\n }\r\n });\r\n }\r\n\r\n // much faster than $.val()\r\n function getSelectValues (select) {\r\n var result = [];\r\n var options = select.selectedOptions;\r\n var opt;\r\n\r\n if (select.multiple) {\r\n for (var i = 0, len = options.length; i < len; i++) {\r\n opt = options[i];\r\n\r\n result.push(opt.value || opt.text);\r\n }\r\n } else {\r\n result = select.value;\r\n }\r\n\r\n return result;\r\n }\r\n\r\n // set data-selected on select element if the value has been programmatically selected\r\n // prior to initialization of bootstrap-select\r\n // * consider removing or replacing an alternative method *\r\n var valHooks = {\r\n useDefault: false,\r\n _set: $.valHooks.select.set\r\n };\r\n\r\n $.valHooks.select.set = function (elem, value) {\r\n if (value && !valHooks.useDefault) $(elem).data('selected', true);\r\n\r\n return valHooks._set.apply(this, arguments);\r\n };\r\n\r\n var changedArguments = null;\r\n\r\n var EventIsSupported = (function () {\r\n try {\r\n new Event('change');\r\n return true;\r\n } catch (e) {\r\n return false;\r\n }\r\n })();\r\n\r\n $.fn.triggerNative = function (eventName) {\r\n var el = this[0],\r\n event;\r\n\r\n if (el.dispatchEvent) { // for modern browsers & IE9+\r\n if (EventIsSupported) {\r\n // For modern browsers\r\n event = new Event(eventName, {\r\n bubbles: true\r\n });\r\n } else {\r\n // For IE since it doesn't support Event constructor\r\n event = document.createEvent('Event');\r\n event.initEvent(eventName, true, false);\r\n }\r\n\r\n el.dispatchEvent(event);\r\n } else if (el.fireEvent) { // for IE8\r\n event = document.createEventObject();\r\n event.eventType = eventName;\r\n el.fireEvent('on' + eventName, event);\r\n } else {\r\n // fall back to jQuery.trigger\r\n this.trigger(eventName);\r\n }\r\n };\r\n // \r\n\r\n function stringSearch (li, searchString, method, normalize) {\r\n var stringTypes = [\r\n 'display',\r\n 'subtext',\r\n 'tokens'\r\n ],\r\n searchSuccess = false;\r\n\r\n for (var i = 0; i < stringTypes.length; i++) {\r\n var stringType = stringTypes[i],\r\n string = li[stringType];\r\n\r\n if (string) {\r\n string = string.toString();\r\n\r\n // Strip HTML tags. This isn't perfect, but it's much faster than any other method\r\n if (stringType === 'display') {\r\n string = string.replace(/<[^>]+>/g, '');\r\n }\r\n\r\n if (normalize) string = normalizeToBase(string);\r\n string = string.toUpperCase();\r\n\r\n if (method === 'contains') {\r\n searchSuccess = string.indexOf(searchString) >= 0;\r\n } else {\r\n searchSuccess = string.startsWith(searchString);\r\n }\r\n\r\n if (searchSuccess) break;\r\n }\r\n }\r\n\r\n return searchSuccess;\r\n }\r\n\r\n function toInteger (value) {\r\n return parseInt(value, 10) || 0;\r\n }\r\n\r\n // Borrowed from Lodash (_.deburr)\r\n /** Used to map Latin Unicode letters to basic Latin letters. */\r\n var deburredLetters = {\r\n // Latin-1 Supplement block.\r\n '\\xc0': 'A', '\\xc1': 'A', '\\xc2': 'A', '\\xc3': 'A', '\\xc4': 'A', '\\xc5': 'A',\r\n '\\xe0': 'a', '\\xe1': 'a', '\\xe2': 'a', '\\xe3': 'a', '\\xe4': 'a', '\\xe5': 'a',\r\n '\\xc7': 'C', '\\xe7': 'c',\r\n '\\xd0': 'D', '\\xf0': 'd',\r\n '\\xc8': 'E', '\\xc9': 'E', '\\xca': 'E', '\\xcb': 'E',\r\n '\\xe8': 'e', '\\xe9': 'e', '\\xea': 'e', '\\xeb': 'e',\r\n '\\xcc': 'I', '\\xcd': 'I', '\\xce': 'I', '\\xcf': 'I',\r\n '\\xec': 'i', '\\xed': 'i', '\\xee': 'i', '\\xef': 'i',\r\n '\\xd1': 'N', '\\xf1': 'n',\r\n '\\xd2': 'O', '\\xd3': 'O', '\\xd4': 'O', '\\xd5': 'O', '\\xd6': 'O', '\\xd8': 'O',\r\n '\\xf2': 'o', '\\xf3': 'o', '\\xf4': 'o', '\\xf5': 'o', '\\xf6': 'o', '\\xf8': 'o',\r\n '\\xd9': 'U', '\\xda': 'U', '\\xdb': 'U', '\\xdc': 'U',\r\n '\\xf9': 'u', '\\xfa': 'u', '\\xfb': 'u', '\\xfc': 'u',\r\n '\\xdd': 'Y', '\\xfd': 'y', '\\xff': 'y',\r\n '\\xc6': 'Ae', '\\xe6': 'ae',\r\n '\\xde': 'Th', '\\xfe': 'th',\r\n '\\xdf': 'ss',\r\n // Latin Extended-A block.\r\n '\\u0100': 'A', '\\u0102': 'A', '\\u0104': 'A',\r\n '\\u0101': 'a', '\\u0103': 'a', '\\u0105': 'a',\r\n '\\u0106': 'C', '\\u0108': 'C', '\\u010a': 'C', '\\u010c': 'C',\r\n '\\u0107': 'c', '\\u0109': 'c', '\\u010b': 'c', '\\u010d': 'c',\r\n '\\u010e': 'D', '\\u0110': 'D', '\\u010f': 'd', '\\u0111': 'd',\r\n '\\u0112': 'E', '\\u0114': 'E', '\\u0116': 'E', '\\u0118': 'E', '\\u011a': 'E',\r\n '\\u0113': 'e', '\\u0115': 'e', '\\u0117': 'e', '\\u0119': 'e', '\\u011b': 'e',\r\n '\\u011c': 'G', '\\u011e': 'G', '\\u0120': 'G', '\\u0122': 'G',\r\n '\\u011d': 'g', '\\u011f': 'g', '\\u0121': 'g', '\\u0123': 'g',\r\n '\\u0124': 'H', '\\u0126': 'H', '\\u0125': 'h', '\\u0127': 'h',\r\n '\\u0128': 'I', '\\u012a': 'I', '\\u012c': 'I', '\\u012e': 'I', '\\u0130': 'I',\r\n '\\u0129': 'i', '\\u012b': 'i', '\\u012d': 'i', '\\u012f': 'i', '\\u0131': 'i',\r\n '\\u0134': 'J', '\\u0135': 'j',\r\n '\\u0136': 'K', '\\u0137': 'k', '\\u0138': 'k',\r\n '\\u0139': 'L', '\\u013b': 'L', '\\u013d': 'L', '\\u013f': 'L', '\\u0141': 'L',\r\n '\\u013a': 'l', '\\u013c': 'l', '\\u013e': 'l', '\\u0140': 'l', '\\u0142': 'l',\r\n '\\u0143': 'N', '\\u0145': 'N', '\\u0147': 'N', '\\u014a': 'N',\r\n '\\u0144': 'n', '\\u0146': 'n', '\\u0148': 'n', '\\u014b': 'n',\r\n '\\u014c': 'O', '\\u014e': 'O', '\\u0150': 'O',\r\n '\\u014d': 'o', '\\u014f': 'o', '\\u0151': 'o',\r\n '\\u0154': 'R', '\\u0156': 'R', '\\u0158': 'R',\r\n '\\u0155': 'r', '\\u0157': 'r', '\\u0159': 'r',\r\n '\\u015a': 'S', '\\u015c': 'S', '\\u015e': 'S', '\\u0160': 'S',\r\n '\\u015b': 's', '\\u015d': 's', '\\u015f': 's', '\\u0161': 's',\r\n '\\u0162': 'T', '\\u0164': 'T', '\\u0166': 'T',\r\n '\\u0163': 't', '\\u0165': 't', '\\u0167': 't',\r\n '\\u0168': 'U', '\\u016a': 'U', '\\u016c': 'U', '\\u016e': 'U', '\\u0170': 'U', '\\u0172': 'U',\r\n '\\u0169': 'u', '\\u016b': 'u', '\\u016d': 'u', '\\u016f': 'u', '\\u0171': 'u', '\\u0173': 'u',\r\n '\\u0174': 'W', '\\u0175': 'w',\r\n '\\u0176': 'Y', '\\u0177': 'y', '\\u0178': 'Y',\r\n '\\u0179': 'Z', '\\u017b': 'Z', '\\u017d': 'Z',\r\n '\\u017a': 'z', '\\u017c': 'z', '\\u017e': 'z',\r\n '\\u0132': 'IJ', '\\u0133': 'ij',\r\n '\\u0152': 'Oe', '\\u0153': 'oe',\r\n '\\u0149': \"'n\", '\\u017f': 's'\r\n };\r\n\r\n /** Used to match Latin Unicode letters (excluding mathematical operators). */\r\n var reLatin = /[\\xc0-\\xd6\\xd8-\\xf6\\xf8-\\xff\\u0100-\\u017f]/g;\r\n\r\n /** Used to compose unicode character classes. */\r\n var rsComboMarksRange = '\\\\u0300-\\\\u036f',\r\n reComboHalfMarksRange = '\\\\ufe20-\\\\ufe2f',\r\n rsComboSymbolsRange = '\\\\u20d0-\\\\u20ff',\r\n rsComboMarksExtendedRange = '\\\\u1ab0-\\\\u1aff',\r\n rsComboMarksSupplementRange = '\\\\u1dc0-\\\\u1dff',\r\n rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange + rsComboMarksExtendedRange + rsComboMarksSupplementRange;\r\n\r\n /** Used to compose unicode capture groups. */\r\n var rsCombo = '[' + rsComboRange + ']';\r\n\r\n /**\r\n * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and\r\n * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols).\r\n */\r\n var reComboMark = RegExp(rsCombo, 'g');\r\n\r\n function deburrLetter (key) {\r\n return deburredLetters[key];\r\n };\r\n\r\n function normalizeToBase (string) {\r\n string = string.toString();\r\n return string && string.replace(reLatin, deburrLetter).replace(reComboMark, '');\r\n }\r\n\r\n // List of HTML entities for escaping.\r\n var escapeMap = {\r\n '&': '&',\r\n '<': '<',\r\n '>': '>',\r\n '\"': '"',\r\n \"'\": ''',\r\n '`': '`'\r\n };\r\n\r\n // Functions for escaping and unescaping strings to/from HTML interpolation.\r\n var createEscaper = function (map) {\r\n var escaper = function (match) {\r\n return map[match];\r\n };\r\n // Regexes for identifying a key that needs to be escaped.\r\n var source = '(?:' + Object.keys(map).join('|') + ')';\r\n var testRegexp = RegExp(source);\r\n var replaceRegexp = RegExp(source, 'g');\r\n return function (string) {\r\n string = string == null ? '' : '' + string;\r\n return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;\r\n };\r\n };\r\n\r\n var htmlEscape = createEscaper(escapeMap);\r\n\r\n /**\r\n * ------------------------------------------------------------------------\r\n * Constants\r\n * ------------------------------------------------------------------------\r\n */\r\n\r\n var keyCodeMap = {\r\n 32: ' ',\r\n 48: '0',\r\n 49: '1',\r\n 50: '2',\r\n 51: '3',\r\n 52: '4',\r\n 53: '5',\r\n 54: '6',\r\n 55: '7',\r\n 56: '8',\r\n 57: '9',\r\n 59: ';',\r\n 65: 'A',\r\n 66: 'B',\r\n 67: 'C',\r\n 68: 'D',\r\n 69: 'E',\r\n 70: 'F',\r\n 71: 'G',\r\n 72: 'H',\r\n 73: 'I',\r\n 74: 'J',\r\n 75: 'K',\r\n 76: 'L',\r\n 77: 'M',\r\n 78: 'N',\r\n 79: 'O',\r\n 80: 'P',\r\n 81: 'Q',\r\n 82: 'R',\r\n 83: 'S',\r\n 84: 'T',\r\n 85: 'U',\r\n 86: 'V',\r\n 87: 'W',\r\n 88: 'X',\r\n 89: 'Y',\r\n 90: 'Z',\r\n 96: '0',\r\n 97: '1',\r\n 98: '2',\r\n 99: '3',\r\n 100: '4',\r\n 101: '5',\r\n 102: '6',\r\n 103: '7',\r\n 104: '8',\r\n 105: '9'\r\n };\r\n\r\n var keyCodes = {\r\n ESCAPE: 27, // KeyboardEvent.which value for Escape (Esc) key\r\n ENTER: 13, // KeyboardEvent.which value for Enter key\r\n SPACE: 32, // KeyboardEvent.which value for space key\r\n TAB: 9, // KeyboardEvent.which value for tab key\r\n ARROW_UP: 38, // KeyboardEvent.which value for up arrow key\r\n ARROW_DOWN: 40 // KeyboardEvent.which value for down arrow key\r\n }\r\n\r\n var version = {\r\n success: false,\r\n major: '3'\r\n };\r\n\r\n try {\r\n version.full = ($.fn.dropdown.Constructor.VERSION || '').split(' ')[0].split('.');\r\n version.major = version.full[0];\r\n version.success = true;\r\n } catch (err) {\r\n // do nothing\r\n }\r\n\r\n var selectId = 0;\r\n\r\n var EVENT_KEY = '.bs.select';\r\n\r\n var classNames = {\r\n DISABLED: 'disabled',\r\n DIVIDER: 'divider',\r\n SHOW: 'open',\r\n DROPUP: 'dropup',\r\n MENU: 'dropdown-menu',\r\n MENURIGHT: 'dropdown-menu-right',\r\n MENULEFT: 'dropdown-menu-left',\r\n // to-do: replace with more advanced template/customization options\r\n BUTTONCLASS: 'btn-default',\r\n POPOVERHEADER: 'popover-title',\r\n ICONBASE: 'glyphicon',\r\n TICKICON: 'glyphicon-ok'\r\n }\r\n\r\n var Selector = {\r\n MENU: '.' + classNames.MENU\r\n }\r\n\r\n var elementTemplates = {\r\n span: document.createElement('span'),\r\n i: document.createElement('i'),\r\n subtext: document.createElement('small'),\r\n a: document.createElement('a'),\r\n li: document.createElement('li'),\r\n whitespace: document.createTextNode('\\u00A0'),\r\n fragment: document.createDocumentFragment()\r\n }\r\n\r\n elementTemplates.a.setAttribute('role', 'option');\r\n elementTemplates.subtext.className = 'text-muted';\r\n\r\n elementTemplates.text = elementTemplates.span.cloneNode(false);\r\n elementTemplates.text.className = 'text';\r\n\r\n elementTemplates.checkMark = elementTemplates.span.cloneNode(false);\r\n\r\n var REGEXP_ARROW = new RegExp(keyCodes.ARROW_UP + '|' + keyCodes.ARROW_DOWN);\r\n var REGEXP_TAB_OR_ESCAPE = new RegExp('^' + keyCodes.TAB + '$|' + keyCodes.ESCAPE);\r\n\r\n var generateOption = {\r\n li: function (content, classes, optgroup) {\r\n var li = elementTemplates.li.cloneNode(false);\r\n\r\n if (content) {\r\n if (content.nodeType === 1 || content.nodeType === 11) {\r\n li.appendChild(content);\r\n } else {\r\n li.innerHTML = content;\r\n }\r\n }\r\n\r\n if (typeof classes !== 'undefined' && classes !== '') li.className = classes;\r\n if (typeof optgroup !== 'undefined' && optgroup !== null) li.classList.add('optgroup-' + optgroup);\r\n\r\n return li;\r\n },\r\n\r\n a: function (text, classes, inline) {\r\n var a = elementTemplates.a.cloneNode(true);\r\n\r\n if (text) {\r\n if (text.nodeType === 11) {\r\n a.appendChild(text);\r\n } else {\r\n a.insertAdjacentHTML('beforeend', text);\r\n }\r\n }\r\n\r\n if (typeof classes !== 'undefined' && classes !== '') a.className = classes;\r\n if (version.major === '4') a.classList.add('dropdown-item');\r\n if (inline) a.setAttribute('style', inline);\r\n\r\n return a;\r\n },\r\n\r\n text: function (options, useFragment) {\r\n var textElement = elementTemplates.text.cloneNode(false),\r\n subtextElement,\r\n iconElement;\r\n\r\n if (options.content) {\r\n textElement.innerHTML = options.content;\r\n } else {\r\n textElement.textContent = options.text;\r\n\r\n if (options.icon) {\r\n var whitespace = elementTemplates.whitespace.cloneNode(false);\r\n\r\n // need to use for icons in the button to prevent a breaking change\r\n // note: switch to span in next major release\r\n iconElement = (useFragment === true ? elementTemplates.i : elementTemplates.span).cloneNode(false);\r\n iconElement.className = options.iconBase + ' ' + options.icon;\r\n\r\n elementTemplates.fragment.appendChild(iconElement);\r\n elementTemplates.fragment.appendChild(whitespace);\r\n }\r\n\r\n if (options.subtext) {\r\n subtextElement = elementTemplates.subtext.cloneNode(false);\r\n subtextElement.textContent = options.subtext;\r\n textElement.appendChild(subtextElement);\r\n }\r\n }\r\n\r\n if (useFragment === true) {\r\n while (textElement.childNodes.length > 0) {\r\n elementTemplates.fragment.appendChild(textElement.childNodes[0]);\r\n }\r\n } else {\r\n elementTemplates.fragment.appendChild(textElement);\r\n }\r\n\r\n return elementTemplates.fragment;\r\n },\r\n\r\n label: function (options) {\r\n var textElement = elementTemplates.text.cloneNode(false),\r\n subtextElement,\r\n iconElement;\r\n\r\n textElement.innerHTML = options.label;\r\n\r\n if (options.icon) {\r\n var whitespace = elementTemplates.whitespace.cloneNode(false);\r\n\r\n iconElement = elementTemplates.span.cloneNode(false);\r\n iconElement.className = options.iconBase + ' ' + options.icon;\r\n\r\n elementTemplates.fragment.appendChild(iconElement);\r\n elementTemplates.fragment.appendChild(whitespace);\r\n }\r\n\r\n if (options.subtext) {\r\n subtextElement = elementTemplates.subtext.cloneNode(false);\r\n subtextElement.textContent = options.subtext;\r\n textElement.appendChild(subtextElement);\r\n }\r\n\r\n elementTemplates.fragment.appendChild(textElement);\r\n\r\n return elementTemplates.fragment;\r\n }\r\n }\r\n\r\n var Selectpicker = function (element, options) {\r\n var that = this;\r\n\r\n // bootstrap-select has been initialized - revert valHooks.select.set back to its original function\r\n if (!valHooks.useDefault) {\r\n $.valHooks.select.set = valHooks._set;\r\n valHooks.useDefault = true;\r\n }\r\n\r\n this.$element = $(element);\r\n this.$newElement = null;\r\n this.$button = null;\r\n this.$menu = null;\r\n this.options = options;\r\n this.selectpicker = {\r\n main: {},\r\n current: {}, // current changes if a search is in progress\r\n search: {},\r\n view: {},\r\n keydown: {\r\n keyHistory: '',\r\n resetKeyHistory: {\r\n start: function () {\r\n return setTimeout(function () {\r\n that.selectpicker.keydown.keyHistory = '';\r\n }, 800);\r\n }\r\n }\r\n }\r\n };\r\n // If we have no title yet, try to pull it from the html title attribute (jQuery doesnt' pick it up as it's not a\r\n // data-attribute)\r\n if (this.options.title === null) {\r\n this.options.title = this.$element.attr('title');\r\n }\r\n\r\n // Format window padding\r\n var winPad = this.options.windowPadding;\r\n if (typeof winPad === 'number') {\r\n this.options.windowPadding = [winPad, winPad, winPad, winPad];\r\n }\r\n\r\n // Expose public methods\r\n this.val = Selectpicker.prototype.val;\r\n this.render = Selectpicker.prototype.render;\r\n this.refresh = Selectpicker.prototype.refresh;\r\n this.setStyle = Selectpicker.prototype.setStyle;\r\n this.selectAll = Selectpicker.prototype.selectAll;\r\n this.deselectAll = Selectpicker.prototype.deselectAll;\r\n this.destroy = Selectpicker.prototype.destroy;\r\n this.remove = Selectpicker.prototype.remove;\r\n this.show = Selectpicker.prototype.show;\r\n this.hide = Selectpicker.prototype.hide;\r\n\r\n this.init();\r\n };\r\n\r\n Selectpicker.VERSION = '1.13.9';\r\n\r\n // part of this is duplicated in i18n/defaults-en_US.js. Make sure to update both.\r\n Selectpicker.DEFAULTS = {\r\n noneSelectedText: 'Nothing selected',\r\n noneResultsText: 'No results matched {0}',\r\n countSelectedText: function (numSelected, numTotal) {\r\n return (numSelected == 1) ? '{0} item selected' : '{0} items selected';\r\n },\r\n maxOptionsText: function (numAll, numGroup) {\r\n return [\r\n (numAll == 1) ? 'Limit reached ({n} item max)' : 'Limit reached ({n} items max)',\r\n (numGroup == 1) ? 'Group limit reached ({n} item max)' : 'Group limit reached ({n} items max)'\r\n ];\r\n },\r\n selectAllText: 'Select All',\r\n deselectAllText: 'Deselect All',\r\n doneButton: false,\r\n doneButtonText: 'Close',\r\n multipleSeparator: ', ',\r\n styleBase: 'btn',\r\n style: classNames.BUTTONCLASS,\r\n size: 'auto',\r\n title: null,\r\n selectedTextFormat: 'values',\r\n width: false,\r\n container: false,\r\n hideDisabled: false,\r\n showSubtext: false,\r\n showIcon: true,\r\n showContent: true,\r\n dropupAuto: true,\r\n header: false,\r\n liveSearch: false,\r\n liveSearchPlaceholder: null,\r\n liveSearchNormalize: false,\r\n liveSearchStyle: 'contains',\r\n actionsBox: false,\r\n iconBase: classNames.ICONBASE,\r\n tickIcon: classNames.TICKICON,\r\n showTick: false,\r\n template: {\r\n caret: ''\r\n },\r\n maxOptions: false,\r\n mobile: false,\r\n selectOnTab: false,\r\n dropdownAlignRight: false,\r\n windowPadding: 0,\r\n virtualScroll: 600,\r\n display: false,\r\n sanitize: true,\r\n sanitizeFn: null,\r\n whiteList: DefaultWhitelist\r\n };\r\n\r\n Selectpicker.prototype = {\r\n\r\n constructor: Selectpicker,\r\n\r\n init: function () {\r\n var that = this,\r\n id = this.$element.attr('id');\r\n\r\n this.selectId = selectId++;\r\n\r\n this.$element[0].classList.add('bs-select-hidden');\r\n\r\n this.multiple = this.$element.prop('multiple');\r\n this.autofocus = this.$element.prop('autofocus');\r\n this.options.showTick = this.$element[0].classList.contains('show-tick');\r\n\r\n this.$newElement = this.createDropdown();\r\n this.$element\r\n .after(this.$newElement)\r\n .prependTo(this.$newElement);\r\n\r\n this.$button = this.$newElement.children('button');\r\n this.$menu = this.$newElement.children(Selector.MENU);\r\n this.$menuInner = this.$menu.children('.inner');\r\n this.$searchbox = this.$menu.find('input');\r\n\r\n this.$element[0].classList.remove('bs-select-hidden');\r\n\r\n if (this.options.dropdownAlignRight === true) this.$menu[0].classList.add(classNames.MENURIGHT);\r\n\r\n if (typeof id !== 'undefined') {\r\n this.$button.attr('data-id', id);\r\n }\r\n\r\n this.checkDisabled();\r\n this.clickListener();\r\n if (this.options.liveSearch) this.liveSearchListener();\r\n this.setStyle();\r\n this.render();\r\n this.setWidth();\r\n if (this.options.container) {\r\n this.selectPosition();\r\n } else {\r\n this.$element.on('hide' + EVENT_KEY, function () {\r\n if (that.isVirtual()) {\r\n // empty menu on close\r\n var menuInner = that.$menuInner[0],\r\n emptyMenu = menuInner.firstChild.cloneNode(false);\r\n\r\n // replace the existing UL with an empty one - this is faster than $.empty() or innerHTML = ''\r\n menuInner.replaceChild(emptyMenu, menuInner.firstChild);\r\n menuInner.scrollTop = 0;\r\n }\r\n });\r\n }\r\n this.$menu.data('this', this);\r\n this.$newElement.data('this', this);\r\n if (this.options.mobile) this.mobile();\r\n\r\n this.$newElement.on({\r\n 'hide.bs.dropdown': function (e) {\r\n that.$menuInner.attr('aria-expanded', false);\r\n that.$element.trigger('hide' + EVENT_KEY, e);\r\n },\r\n 'hidden.bs.dropdown': function (e) {\r\n that.$element.trigger('hidden' + EVENT_KEY, e);\r\n },\r\n 'show.bs.dropdown': function (e) {\r\n that.$menuInner.attr('aria-expanded', true);\r\n that.$element.trigger('show' + EVENT_KEY, e);\r\n },\r\n 'shown.bs.dropdown': function (e) {\r\n that.$element.trigger('shown' + EVENT_KEY, e);\r\n }\r\n });\r\n\r\n if (that.$element[0].hasAttribute('required')) {\r\n this.$element.on('invalid' + EVENT_KEY, function () {\r\n that.$button[0].classList.add('bs-invalid');\r\n\r\n that.$element\r\n .on('shown' + EVENT_KEY + '.invalid', function () {\r\n that.$element\r\n .val(that.$element.val()) // set the value to hide the validation message in Chrome when menu is opened\r\n .off('shown' + EVENT_KEY + '.invalid');\r\n })\r\n .on('rendered' + EVENT_KEY, function () {\r\n // if select is no longer invalid, remove the bs-invalid class\r\n if (this.validity.valid) that.$button[0].classList.remove('bs-invalid');\r\n that.$element.off('rendered' + EVENT_KEY);\r\n });\r\n\r\n that.$button.on('blur' + EVENT_KEY, function () {\r\n that.$element.trigger('focus').trigger('blur');\r\n that.$button.off('blur' + EVENT_KEY);\r\n });\r\n });\r\n }\r\n\r\n setTimeout(function () {\r\n that.createLi();\r\n that.$element.trigger('loaded' + EVENT_KEY);\r\n });\r\n },\r\n\r\n createDropdown: function () {\r\n // Options\r\n // If we are multiple or showTick option is set, then add the show-tick class\r\n var showTick = (this.multiple || this.options.showTick) ? ' show-tick' : '',\r\n inputGroup = '',\r\n autofocus = this.autofocus ? ' autofocus' : '';\r\n\r\n if (version.major < 4 && this.$element.parent().hasClass('input-group')) {\r\n inputGroup = ' input-group-btn';\r\n }\r\n\r\n // Elements\r\n var drop,\r\n header = '',\r\n searchbox = '',\r\n actionsbox = '',\r\n donebutton = '';\r\n\r\n if (this.options.header) {\r\n header =\r\n '
    ' +\r\n '' +\r\n this.options.header +\r\n '
    ';\r\n }\r\n\r\n if (this.options.liveSearch) {\r\n searchbox =\r\n '
    ' +\r\n '' +\r\n '
    ';\r\n }\r\n\r\n if (this.multiple && this.options.actionsBox) {\r\n actionsbox =\r\n '
    ' +\r\n '
    ' +\r\n '' +\r\n '' +\r\n '
    ' +\r\n '
    ';\r\n }\r\n\r\n if (this.multiple && this.options.doneButton) {\r\n donebutton =\r\n '
    ' +\r\n '
    ' +\r\n '' +\r\n '
    ' +\r\n '
    ';\r\n }\r\n\r\n drop =\r\n '
    ' +\r\n '' +\r\n '
    ' +\r\n header +\r\n searchbox +\r\n actionsbox +\r\n '
    ' +\r\n '
      ' +\r\n '
    ' +\r\n '
    ' +\r\n donebutton +\r\n '
    ' +\r\n '
    ';\r\n\r\n return $(drop);\r\n },\r\n\r\n setPositionData: function () {\r\n this.selectpicker.view.canHighlight = [];\r\n\r\n for (var i = 0; i < this.selectpicker.current.data.length; i++) {\r\n var li = this.selectpicker.current.data[i],\r\n canHighlight = true;\r\n\r\n if (li.type === 'divider') {\r\n canHighlight = false;\r\n li.height = this.sizeInfo.dividerHeight;\r\n } else if (li.type === 'optgroup-label') {\r\n canHighlight = false;\r\n li.height = this.sizeInfo.dropdownHeaderHeight;\r\n } else {\r\n li.height = this.sizeInfo.liHeight;\r\n }\r\n\r\n if (li.disabled) canHighlight = false;\r\n\r\n this.selectpicker.view.canHighlight.push(canHighlight);\r\n\r\n li.position = (i === 0 ? 0 : this.selectpicker.current.data[i - 1].position) + li.height;\r\n }\r\n },\r\n\r\n isVirtual: function () {\r\n return (this.options.virtualScroll !== false) && (this.selectpicker.main.elements.length >= this.options.virtualScroll) || this.options.virtualScroll === true;\r\n },\r\n\r\n createView: function (isSearching, scrollTop) {\r\n scrollTop = scrollTop || 0;\r\n\r\n var that = this;\r\n\r\n this.selectpicker.current = isSearching ? this.selectpicker.search : this.selectpicker.main;\r\n\r\n var active = [];\r\n var selected;\r\n var prevActive;\r\n\r\n this.setPositionData();\r\n\r\n scroll(scrollTop, true);\r\n\r\n this.$menuInner.off('scroll.createView').on('scroll.createView', function (e, updateValue) {\r\n if (!that.noScroll) scroll(this.scrollTop, updateValue);\r\n that.noScroll = false;\r\n });\r\n\r\n function scroll (scrollTop, init) {\r\n var size = that.selectpicker.current.elements.length,\r\n chunks = [],\r\n chunkSize,\r\n chunkCount,\r\n firstChunk,\r\n lastChunk,\r\n currentChunk,\r\n prevPositions,\r\n positionIsDifferent,\r\n previousElements,\r\n menuIsDifferent = true,\r\n isVirtual = that.isVirtual();\r\n\r\n that.selectpicker.view.scrollTop = scrollTop;\r\n\r\n if (isVirtual === true) {\r\n // if an option that is encountered that is wider than the current menu width, update the menu width accordingly\r\n if (that.sizeInfo.hasScrollBar && that.$menu[0].offsetWidth > that.sizeInfo.totalMenuWidth) {\r\n that.sizeInfo.menuWidth = that.$menu[0].offsetWidth;\r\n that.sizeInfo.totalMenuWidth = that.sizeInfo.menuWidth + that.sizeInfo.scrollBarWidth;\r\n that.$menu.css('min-width', that.sizeInfo.menuWidth);\r\n }\r\n }\r\n\r\n chunkSize = Math.ceil(that.sizeInfo.menuInnerHeight / that.sizeInfo.liHeight * 1.5); // number of options in a chunk\r\n chunkCount = Math.round(size / chunkSize) || 1; // number of chunks\r\n\r\n for (var i = 0; i < chunkCount; i++) {\r\n var endOfChunk = (i + 1) * chunkSize;\r\n\r\n if (i === chunkCount - 1) {\r\n endOfChunk = size;\r\n }\r\n\r\n chunks[i] = [\r\n (i) * chunkSize + (!i ? 0 : 1),\r\n endOfChunk\r\n ];\r\n\r\n if (!size) break;\r\n\r\n if (currentChunk === undefined && scrollTop <= that.selectpicker.current.data[endOfChunk - 1].position - that.sizeInfo.menuInnerHeight) {\r\n currentChunk = i;\r\n }\r\n }\r\n\r\n if (currentChunk === undefined) currentChunk = 0;\r\n\r\n prevPositions = [that.selectpicker.view.position0, that.selectpicker.view.position1];\r\n\r\n // always display previous, current, and next chunks\r\n firstChunk = Math.max(0, currentChunk - 1);\r\n lastChunk = Math.min(chunkCount - 1, currentChunk + 1);\r\n\r\n that.selectpicker.view.position0 = isVirtual === false ? 0 : (Math.max(0, chunks[firstChunk][0]) || 0);\r\n that.selectpicker.view.position1 = isVirtual === false ? size : (Math.min(size, chunks[lastChunk][1]) || 0);\r\n\r\n positionIsDifferent = prevPositions[0] !== that.selectpicker.view.position0 || prevPositions[1] !== that.selectpicker.view.position1;\r\n\r\n if (that.activeIndex !== undefined) {\r\n prevActive = that.selectpicker.main.elements[that.prevActiveIndex];\r\n active = that.selectpicker.main.elements[that.activeIndex];\r\n selected = that.selectpicker.main.elements[that.selectedIndex];\r\n\r\n if (init) {\r\n if (that.activeIndex !== that.selectedIndex && active && active.length) {\r\n active.classList.remove('active');\r\n if (active.firstChild) active.firstChild.classList.remove('active');\r\n }\r\n that.activeIndex = undefined;\r\n }\r\n\r\n if (that.activeIndex && that.activeIndex !== that.selectedIndex && selected && selected.length) {\r\n selected.classList.remove('active');\r\n if (selected.firstChild) selected.firstChild.classList.remove('active');\r\n }\r\n }\r\n\r\n if (that.prevActiveIndex !== undefined && that.prevActiveIndex !== that.activeIndex && that.prevActiveIndex !== that.selectedIndex && prevActive && prevActive.length) {\r\n prevActive.classList.remove('active');\r\n if (prevActive.firstChild) prevActive.firstChild.classList.remove('active');\r\n }\r\n\r\n if (init || positionIsDifferent) {\r\n previousElements = that.selectpicker.view.visibleElements ? that.selectpicker.view.visibleElements.slice() : [];\r\n\r\n if (isVirtual === false) {\r\n that.selectpicker.view.visibleElements = that.selectpicker.current.elements;\r\n } else {\r\n that.selectpicker.view.visibleElements = that.selectpicker.current.elements.slice(that.selectpicker.view.position0, that.selectpicker.view.position1);\r\n }\r\n\r\n that.setOptionStatus();\r\n\r\n // if searching, check to make sure the list has actually been updated before updating DOM\r\n // this prevents unnecessary repaints\r\n if (isSearching || (isVirtual === false && init)) menuIsDifferent = !isEqual(previousElements, that.selectpicker.view.visibleElements);\r\n\r\n // if virtual scroll is disabled and not searching,\r\n // menu should never need to be updated more than once\r\n if ((init || isVirtual === true) && menuIsDifferent) {\r\n var menuInner = that.$menuInner[0],\r\n menuFragment = document.createDocumentFragment(),\r\n emptyMenu = menuInner.firstChild.cloneNode(false),\r\n marginTop,\r\n marginBottom,\r\n elements = that.selectpicker.view.visibleElements,\r\n toSanitize = [];\r\n\r\n // replace the existing UL with an empty one - this is faster than $.empty()\r\n menuInner.replaceChild(emptyMenu, menuInner.firstChild);\r\n\r\n for (var i = 0, visibleElementsLen = elements.length; i < visibleElementsLen; i++) {\r\n var element = elements[i],\r\n elText,\r\n elementData;\r\n\r\n if (that.options.sanitize) {\r\n elText = element.lastChild;\r\n\r\n if (elText) {\r\n elementData = that.selectpicker.current.data[i + that.selectpicker.view.position0];\r\n\r\n if (elementData && elementData.content && !elementData.sanitized) {\r\n toSanitize.push(elText);\r\n elementData.sanitized = true;\r\n }\r\n }\r\n }\r\n\r\n menuFragment.appendChild(element);\r\n }\r\n\r\n if (that.options.sanitize && toSanitize.length) {\r\n sanitizeHtml(toSanitize, that.options.whiteList, that.options.sanitizeFn);\r\n }\r\n\r\n if (isVirtual === true) {\r\n marginTop = (that.selectpicker.view.position0 === 0 ? 0 : that.selectpicker.current.data[that.selectpicker.view.position0 - 1].position);\r\n marginBottom = (that.selectpicker.view.position1 > size - 1 ? 0 : that.selectpicker.current.data[size - 1].position - that.selectpicker.current.data[that.selectpicker.view.position1 - 1].position);\r\n\r\n menuInner.firstChild.style.marginTop = marginTop + 'px';\r\n menuInner.firstChild.style.marginBottom = marginBottom + 'px';\r\n }\r\n\r\n menuInner.firstChild.appendChild(menuFragment);\r\n }\r\n }\r\n\r\n that.prevActiveIndex = that.activeIndex;\r\n\r\n if (!that.options.liveSearch) {\r\n that.$menuInner.trigger('focus');\r\n } else if (isSearching && init) {\r\n var index = 0,\r\n newActive;\r\n\r\n if (!that.selectpicker.view.canHighlight[index]) {\r\n index = 1 + that.selectpicker.view.canHighlight.slice(1).indexOf(true);\r\n }\r\n\r\n newActive = that.selectpicker.view.visibleElements[index];\r\n\r\n if (that.selectpicker.view.currentActive) {\r\n that.selectpicker.view.currentActive.classList.remove('active');\r\n if (that.selectpicker.view.currentActive.firstChild) that.selectpicker.view.currentActive.firstChild.classList.remove('active');\r\n }\r\n\r\n if (newActive) {\r\n newActive.classList.add('active');\r\n if (newActive.firstChild) newActive.firstChild.classList.add('active');\r\n }\r\n\r\n that.activeIndex = (that.selectpicker.current.data[index] || {}).index;\r\n }\r\n }\r\n\r\n $(window)\r\n .off('resize' + EVENT_KEY + '.' + this.selectId + '.createView')\r\n .on('resize' + EVENT_KEY + '.' + this.selectId + '.createView', function () {\r\n var isActive = that.$newElement.hasClass(classNames.SHOW);\r\n\r\n if (isActive) scroll(that.$menuInner[0].scrollTop);\r\n });\r\n },\r\n\r\n setPlaceholder: function () {\r\n var updateIndex = false;\r\n\r\n if (this.options.title && !this.multiple) {\r\n if (!this.selectpicker.view.titleOption) this.selectpicker.view.titleOption = document.createElement('option');\r\n\r\n // this option doesn't create a new
  • element, but does add a new option at the start,\r\n // so startIndex should increase to prevent having to check every option for the bs-title-option class\r\n updateIndex = true;\r\n\r\n var element = this.$element[0],\r\n isSelected = false,\r\n titleNotAppended = !this.selectpicker.view.titleOption.parentNode;\r\n\r\n if (titleNotAppended) {\r\n // Use native JS to prepend option (faster)\r\n this.selectpicker.view.titleOption.className = 'bs-title-option';\r\n this.selectpicker.view.titleOption.value = '';\r\n\r\n // Check if selected or data-selected attribute is already set on an option. If not, select the titleOption option.\r\n // the selected item may have been changed by user or programmatically before the bootstrap select plugin runs,\r\n // if so, the select will have the data-selected attribute\r\n var $opt = $(element.options[element.selectedIndex]);\r\n isSelected = $opt.attr('selected') === undefined && this.$element.data('selected') === undefined;\r\n }\r\n\r\n if (titleNotAppended || this.selectpicker.view.titleOption.index !== 0) {\r\n element.insertBefore(this.selectpicker.view.titleOption, element.firstChild);\r\n }\r\n\r\n // Set selected *after* appending to select,\r\n // otherwise the option doesn't get selected in IE\r\n // set using selectedIndex, as setting the selected attr to true here doesn't work in IE11\r\n if (isSelected) element.selectedIndex = 0;\r\n }\r\n\r\n return updateIndex;\r\n },\r\n\r\n createLi: function () {\r\n var that = this,\r\n iconBase = this.options.iconBase,\r\n optionSelector = ':not([hidden]):not([data-hidden=\"true\"])',\r\n mainElements = [],\r\n mainData = [],\r\n widestOptionLength = 0,\r\n optID = 0,\r\n startIndex = this.setPlaceholder() ? 1 : 0; // append the titleOption if necessary and skip the first option in the loop\r\n\r\n if (this.options.hideDisabled) optionSelector += ':not(:disabled)';\r\n\r\n if ((that.options.showTick || that.multiple) && !elementTemplates.checkMark.parentNode) {\r\n elementTemplates.checkMark.className = iconBase + ' ' + that.options.tickIcon + ' check-mark';\r\n elementTemplates.a.appendChild(elementTemplates.checkMark);\r\n }\r\n\r\n var selectOptions = this.$element[0].querySelectorAll('select > *' + optionSelector);\r\n\r\n function addDivider (config) {\r\n var previousData = mainData[mainData.length - 1];\r\n\r\n // ensure optgroup doesn't create back-to-back dividers\r\n if (\r\n previousData &&\r\n previousData.type === 'divider' &&\r\n (previousData.optID || config.optID)\r\n ) {\r\n return;\r\n }\r\n\r\n config = config || {};\r\n config.type = 'divider';\r\n\r\n mainElements.push(\r\n generateOption.li(\r\n false,\r\n classNames.DIVIDER,\r\n (config.optID ? config.optID + 'div' : undefined)\r\n )\r\n );\r\n\r\n mainData.push(config);\r\n }\r\n\r\n function addOption (option, config) {\r\n config = config || {};\r\n\r\n config.divider = option.getAttribute('data-divider') === 'true';\r\n\r\n if (config.divider) {\r\n addDivider({\r\n optID: config.optID\r\n });\r\n } else {\r\n var liIndex = mainData.length,\r\n cssText = option.style.cssText,\r\n inlineStyle = cssText ? htmlEscape(cssText) : '',\r\n optionClass = (option.className || '') + (config.optgroupClass || '');\r\n\r\n if (config.optID) optionClass = 'opt ' + optionClass;\r\n\r\n config.text = option.textContent;\r\n\r\n config.content = option.getAttribute('data-content');\r\n config.tokens = option.getAttribute('data-tokens');\r\n config.subtext = option.getAttribute('data-subtext');\r\n config.icon = option.getAttribute('data-icon');\r\n config.iconBase = iconBase;\r\n\r\n var textElement = generateOption.text(config);\r\n\r\n mainElements.push(\r\n generateOption.li(\r\n generateOption.a(\r\n textElement,\r\n optionClass,\r\n inlineStyle\r\n ),\r\n '',\r\n config.optID\r\n )\r\n );\r\n\r\n option.liIndex = liIndex;\r\n\r\n config.display = config.content || config.text;\r\n config.type = 'option';\r\n config.index = liIndex;\r\n config.option = option;\r\n config.disabled = config.disabled || option.disabled;\r\n\r\n mainData.push(config);\r\n\r\n var combinedLength = 0;\r\n\r\n // count the number of characters in the option - not perfect, but should work in most cases\r\n if (config.display) combinedLength += config.display.length;\r\n if (config.subtext) combinedLength += config.subtext.length;\r\n // if there is an icon, ensure this option's width is checked\r\n if (config.icon) combinedLength += 1;\r\n\r\n if (combinedLength > widestOptionLength) {\r\n widestOptionLength = combinedLength;\r\n\r\n // guess which option is the widest\r\n // use this when calculating menu width\r\n // not perfect, but it's fast, and the width will be updating accordingly when scrolling\r\n that.selectpicker.view.widestOption = mainElements[mainElements.length - 1];\r\n }\r\n }\r\n }\r\n\r\n function addOptgroup (index, selectOptions) {\r\n var optgroup = selectOptions[index],\r\n previous = selectOptions[index - 1],\r\n next = selectOptions[index + 1],\r\n options = optgroup.querySelectorAll('option' + optionSelector);\r\n\r\n if (!options.length) return;\r\n\r\n var config = {\r\n label: htmlEscape(optgroup.label),\r\n subtext: optgroup.getAttribute('data-subtext'),\r\n icon: optgroup.getAttribute('data-icon'),\r\n iconBase: iconBase\r\n },\r\n optgroupClass = ' ' + (optgroup.className || ''),\r\n headerIndex,\r\n lastIndex;\r\n\r\n optID++;\r\n\r\n if (previous) {\r\n addDivider({ optID: optID });\r\n }\r\n\r\n var labelElement = generateOption.label(config);\r\n\r\n mainElements.push(\r\n generateOption.li(labelElement, 'dropdown-header' + optgroupClass, optID)\r\n );\r\n\r\n mainData.push({\r\n display: config.label,\r\n subtext: config.subtext,\r\n type: 'optgroup-label',\r\n optID: optID\r\n });\r\n\r\n for (var j = 0, len = options.length; j < len; j++) {\r\n var option = options[j];\r\n\r\n if (j === 0) {\r\n headerIndex = mainData.length - 1;\r\n lastIndex = headerIndex + len;\r\n }\r\n\r\n addOption(option, {\r\n headerIndex: headerIndex,\r\n lastIndex: lastIndex,\r\n optID: optID,\r\n optgroupClass: optgroupClass,\r\n disabled: optgroup.disabled\r\n });\r\n }\r\n\r\n if (next) {\r\n addDivider({ optID: optID });\r\n }\r\n }\r\n\r\n for (var len = selectOptions.length; startIndex < len; startIndex++) {\r\n var item = selectOptions[startIndex];\r\n\r\n if (item.tagName !== 'OPTGROUP') {\r\n addOption(item, {});\r\n } else {\r\n addOptgroup(startIndex, selectOptions);\r\n }\r\n }\r\n\r\n this.selectpicker.main.elements = mainElements;\r\n this.selectpicker.main.data = mainData;\r\n\r\n this.selectpicker.current = this.selectpicker.main;\r\n },\r\n\r\n findLis: function () {\r\n return this.$menuInner.find('.inner > li');\r\n },\r\n\r\n render: function () {\r\n // ensure titleOption is appended and selected (if necessary) before getting selectedOptions\r\n this.setPlaceholder();\r\n\r\n var that = this,\r\n selectedOptions = this.$element[0].selectedOptions,\r\n selectedCount = selectedOptions.length,\r\n button = this.$button[0],\r\n buttonInner = button.querySelector('.filter-option-inner-inner'),\r\n multipleSeparator = document.createTextNode(this.options.multipleSeparator),\r\n titleFragment = elementTemplates.fragment.cloneNode(false),\r\n showCount,\r\n countMax,\r\n hasContent = false;\r\n\r\n this.togglePlaceholder();\r\n\r\n this.tabIndex();\r\n\r\n if (this.options.selectedTextFormat === 'static') {\r\n titleFragment = generateOption.text({ text: this.options.title }, true);\r\n } else {\r\n showCount = this.multiple && this.options.selectedTextFormat.indexOf('count') !== -1 && selectedCount > 1;\r\n\r\n // determine if the number of selected options will be shown (showCount === true)\r\n if (showCount) {\r\n countMax = this.options.selectedTextFormat.split('>');\r\n showCount = (countMax.length > 1 && selectedCount > countMax[1]) || (countMax.length === 1 && selectedCount >= 2);\r\n }\r\n\r\n // only loop through all selected options if the count won't be shown\r\n if (showCount === false) {\r\n for (var selectedIndex = 0; selectedIndex < selectedCount; selectedIndex++) {\r\n if (selectedIndex < 50) {\r\n var option = selectedOptions[selectedIndex],\r\n titleOptions = {},\r\n thisData = {\r\n content: option.getAttribute('data-content'),\r\n subtext: option.getAttribute('data-subtext'),\r\n icon: option.getAttribute('data-icon')\r\n };\r\n\r\n if (this.multiple && selectedIndex > 0) {\r\n titleFragment.appendChild(multipleSeparator.cloneNode(false));\r\n }\r\n\r\n if (option.title) {\r\n titleOptions.text = option.title;\r\n } else if (thisData.content && that.options.showContent) {\r\n titleOptions.content = thisData.content.toString();\r\n hasContent = true;\r\n } else {\r\n if (that.options.showIcon) {\r\n titleOptions.icon = thisData.icon;\r\n titleOptions.iconBase = this.options.iconBase;\r\n }\r\n if (that.options.showSubtext && !that.multiple && thisData.subtext) titleOptions.subtext = ' ' + thisData.subtext;\r\n titleOptions.text = option.textContent.trim();\r\n }\r\n\r\n titleFragment.appendChild(generateOption.text(titleOptions, true));\r\n } else {\r\n break;\r\n }\r\n }\r\n\r\n // add ellipsis\r\n if (selectedCount > 49) {\r\n titleFragment.appendChild(document.createTextNode('...'));\r\n }\r\n } else {\r\n var optionSelector = ':not([hidden]):not([data-hidden=\"true\"]):not([data-divider=\"true\"])';\r\n if (this.options.hideDisabled) optionSelector += ':not(:disabled)';\r\n\r\n // If this is a multiselect, and selectedTextFormat is count, then show 1 of 2 selected, etc.\r\n var totalCount = this.$element[0].querySelectorAll('select > option' + optionSelector + ', optgroup' + optionSelector + ' option' + optionSelector).length,\r\n tr8nText = (typeof this.options.countSelectedText === 'function') ? this.options.countSelectedText(selectedCount, totalCount) : this.options.countSelectedText;\r\n\r\n titleFragment = generateOption.text({\r\n text: tr8nText.replace('{0}', selectedCount.toString()).replace('{1}', totalCount.toString())\r\n }, true);\r\n }\r\n }\r\n\r\n if (this.options.title == undefined) {\r\n // use .attr to ensure undefined is returned if title attribute is not set\r\n this.options.title = this.$element.attr('title');\r\n }\r\n\r\n // If the select doesn't have a title, then use the default, or if nothing is set at all, use noneSelectedText\r\n if (!titleFragment.childNodes.length) {\r\n titleFragment = generateOption.text({\r\n text: typeof this.options.title !== 'undefined' ? this.options.title : this.options.noneSelectedText\r\n }, true);\r\n }\r\n\r\n // strip all HTML tags and trim the result, then unescape any escaped tags\r\n button.title = titleFragment.textContent.replace(/<[^>]*>?/g, '').trim();\r\n\r\n if (this.options.sanitize && hasContent) {\r\n sanitizeHtml([titleFragment], that.options.whiteList, that.options.sanitizeFn);\r\n }\r\n\r\n buttonInner.innerHTML = '';\r\n buttonInner.appendChild(titleFragment);\r\n\r\n if (version.major < 4 && this.$newElement[0].classList.contains('bs3-has-addon')) {\r\n var filterExpand = button.querySelector('.filter-expand'),\r\n clone = buttonInner.cloneNode(true);\r\n\r\n clone.className = 'filter-expand';\r\n\r\n if (filterExpand) {\r\n button.replaceChild(clone, filterExpand);\r\n } else {\r\n button.appendChild(clone);\r\n }\r\n }\r\n\r\n this.$element.trigger('rendered' + EVENT_KEY);\r\n },\r\n\r\n /**\r\n * @param [style]\r\n * @param [status]\r\n */\r\n setStyle: function (newStyle, status) {\r\n var button = this.$button[0],\r\n newElement = this.$newElement[0],\r\n style = this.options.style.trim(),\r\n buttonClass;\r\n\r\n if (this.$element.attr('class')) {\r\n this.$newElement.addClass(this.$element.attr('class').replace(/selectpicker|mobile-device|bs-select-hidden|validate\\[.*\\]/gi, ''));\r\n }\r\n\r\n if (version.major < 4) {\r\n newElement.classList.add('bs3');\r\n\r\n if (newElement.parentNode.classList.contains('input-group') &&\r\n (newElement.previousElementSibling || newElement.nextElementSibling) &&\r\n (newElement.previousElementSibling || newElement.nextElementSibling).classList.contains('input-group-addon')\r\n ) {\r\n newElement.classList.add('bs3-has-addon');\r\n }\r\n }\r\n\r\n if (newStyle) {\r\n buttonClass = newStyle.trim();\r\n } else {\r\n buttonClass = style;\r\n }\r\n\r\n if (status == 'add') {\r\n if (buttonClass) button.classList.add.apply(button.classList, buttonClass.split(' '));\r\n } else if (status == 'remove') {\r\n if (buttonClass) button.classList.remove.apply(button.classList, buttonClass.split(' '));\r\n } else {\r\n if (style) button.classList.remove.apply(button.classList, style.split(' '));\r\n if (buttonClass) button.classList.add.apply(button.classList, buttonClass.split(' '));\r\n }\r\n },\r\n\r\n liHeight: function (refresh) {\r\n if (!refresh && (this.options.size === false || this.sizeInfo)) return;\r\n\r\n if (!this.sizeInfo) this.sizeInfo = {};\r\n\r\n var newElement = document.createElement('div'),\r\n menu = document.createElement('div'),\r\n menuInner = document.createElement('div'),\r\n menuInnerInner = document.createElement('ul'),\r\n divider = document.createElement('li'),\r\n dropdownHeader = document.createElement('li'),\r\n li = document.createElement('li'),\r\n a = document.createElement('a'),\r\n text = document.createElement('span'),\r\n header = this.options.header && this.$menu.find('.' + classNames.POPOVERHEADER).length > 0 ? this.$menu.find('.' + classNames.POPOVERHEADER)[0].cloneNode(true) : null,\r\n search = this.options.liveSearch ? document.createElement('div') : null,\r\n actions = this.options.actionsBox && this.multiple && this.$menu.find('.bs-actionsbox').length > 0 ? this.$menu.find('.bs-actionsbox')[0].cloneNode(true) : null,\r\n doneButton = this.options.doneButton && this.multiple && this.$menu.find('.bs-donebutton').length > 0 ? this.$menu.find('.bs-donebutton')[0].cloneNode(true) : null,\r\n firstOption = this.$element.find('option')[0];\r\n\r\n this.sizeInfo.selectWidth = this.$newElement[0].offsetWidth;\r\n\r\n text.className = 'text';\r\n a.className = 'dropdown-item ' + (firstOption ? firstOption.className : '');\r\n newElement.className = this.$menu[0].parentNode.className + ' ' + classNames.SHOW;\r\n newElement.style.width = this.sizeInfo.selectWidth + 'px';\r\n if (this.options.width === 'auto') menu.style.minWidth = 0;\r\n menu.className = classNames.MENU + ' ' + classNames.SHOW;\r\n menuInner.className = 'inner ' + classNames.SHOW;\r\n menuInnerInner.className = classNames.MENU + ' inner ' + (version.major === '4' ? classNames.SHOW : '');\r\n divider.className = classNames.DIVIDER;\r\n dropdownHeader.className = 'dropdown-header';\r\n\r\n text.appendChild(document.createTextNode('\\u200b'));\r\n a.appendChild(text);\r\n li.appendChild(a);\r\n dropdownHeader.appendChild(text.cloneNode(true));\r\n\r\n if (this.selectpicker.view.widestOption) {\r\n menuInnerInner.appendChild(this.selectpicker.view.widestOption.cloneNode(true));\r\n }\r\n\r\n menuInnerInner.appendChild(li);\r\n menuInnerInner.appendChild(divider);\r\n menuInnerInner.appendChild(dropdownHeader);\r\n if (header) menu.appendChild(header);\r\n if (search) {\r\n var input = document.createElement('input');\r\n search.className = 'bs-searchbox';\r\n input.className = 'form-control';\r\n search.appendChild(input);\r\n menu.appendChild(search);\r\n }\r\n if (actions) menu.appendChild(actions);\r\n menuInner.appendChild(menuInnerInner);\r\n menu.appendChild(menuInner);\r\n if (doneButton) menu.appendChild(doneButton);\r\n newElement.appendChild(menu);\r\n\r\n document.body.appendChild(newElement);\r\n\r\n var liHeight = li.offsetHeight,\r\n dropdownHeaderHeight = dropdownHeader ? dropdownHeader.offsetHeight : 0,\r\n headerHeight = header ? header.offsetHeight : 0,\r\n searchHeight = search ? search.offsetHeight : 0,\r\n actionsHeight = actions ? actions.offsetHeight : 0,\r\n doneButtonHeight = doneButton ? doneButton.offsetHeight : 0,\r\n dividerHeight = $(divider).outerHeight(true),\r\n // fall back to jQuery if getComputedStyle is not supported\r\n menuStyle = window.getComputedStyle ? window.getComputedStyle(menu) : false,\r\n menuWidth = menu.offsetWidth,\r\n $menu = menuStyle ? null : $(menu),\r\n menuPadding = {\r\n vert: toInteger(menuStyle ? menuStyle.paddingTop : $menu.css('paddingTop')) +\r\n toInteger(menuStyle ? menuStyle.paddingBottom : $menu.css('paddingBottom')) +\r\n toInteger(menuStyle ? menuStyle.borderTopWidth : $menu.css('borderTopWidth')) +\r\n toInteger(menuStyle ? menuStyle.borderBottomWidth : $menu.css('borderBottomWidth')),\r\n horiz: toInteger(menuStyle ? menuStyle.paddingLeft : $menu.css('paddingLeft')) +\r\n toInteger(menuStyle ? menuStyle.paddingRight : $menu.css('paddingRight')) +\r\n toInteger(menuStyle ? menuStyle.borderLeftWidth : $menu.css('borderLeftWidth')) +\r\n toInteger(menuStyle ? menuStyle.borderRightWidth : $menu.css('borderRightWidth'))\r\n },\r\n menuExtras = {\r\n vert: menuPadding.vert +\r\n toInteger(menuStyle ? menuStyle.marginTop : $menu.css('marginTop')) +\r\n toInteger(menuStyle ? menuStyle.marginBottom : $menu.css('marginBottom')) + 2,\r\n horiz: menuPadding.horiz +\r\n toInteger(menuStyle ? menuStyle.marginLeft : $menu.css('marginLeft')) +\r\n toInteger(menuStyle ? menuStyle.marginRight : $menu.css('marginRight')) + 2\r\n },\r\n scrollBarWidth;\r\n\r\n menuInner.style.overflowY = 'scroll';\r\n\r\n scrollBarWidth = menu.offsetWidth - menuWidth;\r\n\r\n document.body.removeChild(newElement);\r\n\r\n this.sizeInfo.liHeight = liHeight;\r\n this.sizeInfo.dropdownHeaderHeight = dropdownHeaderHeight;\r\n this.sizeInfo.headerHeight = headerHeight;\r\n this.sizeInfo.searchHeight = searchHeight;\r\n this.sizeInfo.actionsHeight = actionsHeight;\r\n this.sizeInfo.doneButtonHeight = doneButtonHeight;\r\n this.sizeInfo.dividerHeight = dividerHeight;\r\n this.sizeInfo.menuPadding = menuPadding;\r\n this.sizeInfo.menuExtras = menuExtras;\r\n this.sizeInfo.menuWidth = menuWidth;\r\n this.sizeInfo.totalMenuWidth = this.sizeInfo.menuWidth;\r\n this.sizeInfo.scrollBarWidth = scrollBarWidth;\r\n this.sizeInfo.selectHeight = this.$newElement[0].offsetHeight;\r\n\r\n this.setPositionData();\r\n },\r\n\r\n getSelectPosition: function () {\r\n var that = this,\r\n $window = $(window),\r\n pos = that.$newElement.offset(),\r\n $container = $(that.options.container),\r\n containerPos;\r\n\r\n if (that.options.container && $container.length && !$container.is('body')) {\r\n containerPos = $container.offset();\r\n containerPos.top += parseInt($container.css('borderTopWidth'));\r\n containerPos.left += parseInt($container.css('borderLeftWidth'));\r\n } else {\r\n containerPos = { top: 0, left: 0 };\r\n }\r\n\r\n var winPad = that.options.windowPadding;\r\n\r\n this.sizeInfo.selectOffsetTop = pos.top - containerPos.top - $window.scrollTop();\r\n this.sizeInfo.selectOffsetBot = $window.height() - this.sizeInfo.selectOffsetTop - this.sizeInfo.selectHeight - containerPos.top - winPad[2];\r\n this.sizeInfo.selectOffsetLeft = pos.left - containerPos.left - $window.scrollLeft();\r\n this.sizeInfo.selectOffsetRight = $window.width() - this.sizeInfo.selectOffsetLeft - this.sizeInfo.selectWidth - containerPos.left - winPad[1];\r\n this.sizeInfo.selectOffsetTop -= winPad[0];\r\n this.sizeInfo.selectOffsetLeft -= winPad[3];\r\n },\r\n\r\n setMenuSize: function (isAuto) {\r\n this.getSelectPosition();\r\n\r\n var selectWidth = this.sizeInfo.selectWidth,\r\n liHeight = this.sizeInfo.liHeight,\r\n headerHeight = this.sizeInfo.headerHeight,\r\n searchHeight = this.sizeInfo.searchHeight,\r\n actionsHeight = this.sizeInfo.actionsHeight,\r\n doneButtonHeight = this.sizeInfo.doneButtonHeight,\r\n divHeight = this.sizeInfo.dividerHeight,\r\n menuPadding = this.sizeInfo.menuPadding,\r\n menuInnerHeight,\r\n menuHeight,\r\n divLength = 0,\r\n minHeight,\r\n _minHeight,\r\n maxHeight,\r\n menuInnerMinHeight,\r\n estimate;\r\n\r\n if (this.options.dropupAuto) {\r\n // Get the estimated height of the menu without scrollbars.\r\n // This is useful for smaller menus, where there might be plenty of room\r\n // below the button without setting dropup, but we can't know\r\n // the exact height of the menu until createView is called later\r\n estimate = liHeight * this.selectpicker.current.elements.length + menuPadding.vert;\r\n this.$newElement.toggleClass(classNames.DROPUP, this.sizeInfo.selectOffsetTop - this.sizeInfo.selectOffsetBot > this.sizeInfo.menuExtras.vert && estimate + this.sizeInfo.menuExtras.vert + 50 > this.sizeInfo.selectOffsetBot);\r\n }\r\n\r\n if (this.options.size === 'auto') {\r\n _minHeight = this.selectpicker.current.elements.length > 3 ? this.sizeInfo.liHeight * 3 + this.sizeInfo.menuExtras.vert - 2 : 0;\r\n menuHeight = this.sizeInfo.selectOffsetBot - this.sizeInfo.menuExtras.vert;\r\n minHeight = _minHeight + headerHeight + searchHeight + actionsHeight + doneButtonHeight;\r\n menuInnerMinHeight = Math.max(_minHeight - menuPadding.vert, 0);\r\n\r\n if (this.$newElement.hasClass(classNames.DROPUP)) {\r\n menuHeight = this.sizeInfo.selectOffsetTop - this.sizeInfo.menuExtras.vert;\r\n }\r\n\r\n maxHeight = menuHeight;\r\n menuInnerHeight = menuHeight - headerHeight - searchHeight - actionsHeight - doneButtonHeight - menuPadding.vert;\r\n } else if (this.options.size && this.options.size != 'auto' && this.selectpicker.current.elements.length > this.options.size) {\r\n for (var i = 0; i < this.options.size; i++) {\r\n if (this.selectpicker.current.data[i].type === 'divider') divLength++;\r\n }\r\n\r\n menuHeight = liHeight * this.options.size + divLength * divHeight + menuPadding.vert;\r\n menuInnerHeight = menuHeight - menuPadding.vert;\r\n maxHeight = menuHeight + headerHeight + searchHeight + actionsHeight + doneButtonHeight;\r\n minHeight = menuInnerMinHeight = '';\r\n }\r\n\r\n if (this.options.dropdownAlignRight === 'auto') {\r\n this.$menu.toggleClass(classNames.MENURIGHT, this.sizeInfo.selectOffsetLeft > this.sizeInfo.selectOffsetRight && this.sizeInfo.selectOffsetRight < (this.sizeInfo.totalMenuWidth - selectWidth));\r\n }\r\n\r\n this.$menu.css({\r\n 'max-height': maxHeight + 'px',\r\n 'overflow': 'hidden',\r\n 'min-height': minHeight + 'px'\r\n });\r\n\r\n this.$menuInner.css({\r\n 'max-height': menuInnerHeight + 'px',\r\n 'overflow-y': 'auto',\r\n 'min-height': menuInnerMinHeight + 'px'\r\n });\r\n\r\n // ensure menuInnerHeight is always a positive number to prevent issues calculating chunkSize in createView\r\n this.sizeInfo.menuInnerHeight = Math.max(menuInnerHeight, 1);\r\n\r\n if (this.selectpicker.current.data.length && this.selectpicker.current.data[this.selectpicker.current.data.length - 1].position > this.sizeInfo.menuInnerHeight) {\r\n this.sizeInfo.hasScrollBar = true;\r\n this.sizeInfo.totalMenuWidth = this.sizeInfo.menuWidth + this.sizeInfo.scrollBarWidth;\r\n\r\n this.$menu.css('min-width', this.sizeInfo.totalMenuWidth);\r\n }\r\n\r\n if (this.dropdown && this.dropdown._popper) this.dropdown._popper.update();\r\n },\r\n\r\n setSize: function (refresh) {\r\n this.liHeight(refresh);\r\n\r\n if (this.options.header) this.$menu.css('padding-top', 0);\r\n if (this.options.size === false) return;\r\n\r\n var that = this,\r\n $window = $(window),\r\n selectedIndex,\r\n offset = 0;\r\n\r\n this.setMenuSize();\r\n\r\n if (this.options.liveSearch) {\r\n this.$searchbox\r\n .off('input.setMenuSize propertychange.setMenuSize')\r\n .on('input.setMenuSize propertychange.setMenuSize', function () {\r\n return that.setMenuSize();\r\n });\r\n }\r\n\r\n if (this.options.size === 'auto') {\r\n $window\r\n .off('resize' + EVENT_KEY + '.' + this.selectId + '.setMenuSize' + ' scroll' + EVENT_KEY + '.' + this.selectId + '.setMenuSize')\r\n .on('resize' + EVENT_KEY + '.' + this.selectId + '.setMenuSize' + ' scroll' + EVENT_KEY + '.' + this.selectId + '.setMenuSize', function () {\r\n return that.setMenuSize();\r\n });\r\n } else if (this.options.size && this.options.size != 'auto' && this.selectpicker.current.elements.length > this.options.size) {\r\n $window.off('resize' + EVENT_KEY + '.' + this.selectId + '.setMenuSize' + ' scroll' + EVENT_KEY + '.' + this.selectId + '.setMenuSize');\r\n }\r\n\r\n if (refresh) {\r\n offset = this.$menuInner[0].scrollTop;\r\n } else if (!that.multiple) {\r\n var element = that.$element[0];\r\n selectedIndex = (element.options[element.selectedIndex] || {}).liIndex;\r\n\r\n if (typeof selectedIndex === 'number' && that.options.size !== false) {\r\n offset = that.sizeInfo.liHeight * selectedIndex;\r\n offset = offset - (that.sizeInfo.menuInnerHeight / 2) + (that.sizeInfo.liHeight / 2);\r\n }\r\n }\r\n\r\n that.createView(false, offset);\r\n },\r\n\r\n setWidth: function () {\r\n var that = this;\r\n\r\n if (this.options.width === 'auto') {\r\n requestAnimationFrame(function () {\r\n that.$menu.css('min-width', '0');\r\n\r\n that.$element.on('loaded' + EVENT_KEY, function () {\r\n that.liHeight();\r\n that.setMenuSize();\r\n\r\n // Get correct width if element is hidden\r\n var $selectClone = that.$newElement.clone().appendTo('body'),\r\n btnWidth = $selectClone.css('width', 'auto').children('button').outerWidth();\r\n\r\n $selectClone.remove();\r\n\r\n // Set width to whatever's larger, button title or longest option\r\n that.sizeInfo.selectWidth = Math.max(that.sizeInfo.totalMenuWidth, btnWidth);\r\n that.$newElement.css('width', that.sizeInfo.selectWidth + 'px');\r\n });\r\n });\r\n } else if (this.options.width === 'fit') {\r\n // Remove inline min-width so width can be changed from 'auto'\r\n this.$menu.css('min-width', '');\r\n this.$newElement.css('width', '').addClass('fit-width');\r\n } else if (this.options.width) {\r\n // Remove inline min-width so width can be changed from 'auto'\r\n this.$menu.css('min-width', '');\r\n this.$newElement.css('width', this.options.width);\r\n } else {\r\n // Remove inline min-width/width so width can be changed\r\n this.$menu.css('min-width', '');\r\n this.$newElement.css('width', '');\r\n }\r\n // Remove fit-width class if width is changed programmatically\r\n if (this.$newElement.hasClass('fit-width') && this.options.width !== 'fit') {\r\n this.$newElement[0].classList.remove('fit-width');\r\n }\r\n },\r\n\r\n selectPosition: function () {\r\n this.$bsContainer = $('
    ');\r\n\r\n var that = this,\r\n $container = $(this.options.container),\r\n pos,\r\n containerPos,\r\n actualHeight,\r\n getPlacement = function ($element) {\r\n var containerPosition = {},\r\n // fall back to dropdown's default display setting if display is not manually set\r\n display = that.options.display || (\r\n // Bootstrap 3 doesn't have $.fn.dropdown.Constructor.Default\r\n $.fn.dropdown.Constructor.Default ? $.fn.dropdown.Constructor.Default.display\r\n : false\r\n );\r\n\r\n that.$bsContainer.addClass($element.attr('class').replace(/form-control|fit-width/gi, '')).toggleClass(classNames.DROPUP, $element.hasClass(classNames.DROPUP));\r\n pos = $element.offset();\r\n\r\n if (!$container.is('body')) {\r\n containerPos = $container.offset();\r\n containerPos.top += parseInt($container.css('borderTopWidth')) - $container.scrollTop();\r\n containerPos.left += parseInt($container.css('borderLeftWidth')) - $container.scrollLeft();\r\n } else {\r\n containerPos = { top: 0, left: 0 };\r\n }\r\n\r\n actualHeight = $element.hasClass(classNames.DROPUP) ? 0 : $element[0].offsetHeight;\r\n\r\n // Bootstrap 4+ uses Popper for menu positioning\r\n if (version.major < 4 || display === 'static') {\r\n containerPosition.top = pos.top - containerPos.top + actualHeight;\r\n containerPosition.left = pos.left - containerPos.left;\r\n }\r\n\r\n containerPosition.width = $element[0].offsetWidth;\r\n\r\n that.$bsContainer.css(containerPosition);\r\n };\r\n\r\n this.$button.on('click.bs.dropdown.data-api', function () {\r\n if (that.isDisabled()) {\r\n return;\r\n }\r\n\r\n getPlacement(that.$newElement);\r\n\r\n that.$bsContainer\r\n .appendTo(that.options.container)\r\n .toggleClass(classNames.SHOW, !that.$button.hasClass(classNames.SHOW))\r\n .append(that.$menu);\r\n });\r\n\r\n $(window)\r\n .off('resize' + EVENT_KEY + '.' + this.selectId + ' scroll' + EVENT_KEY + '.' + this.selectId)\r\n .on('resize' + EVENT_KEY + '.' + this.selectId + ' scroll' + EVENT_KEY + '.' + this.selectId, function () {\r\n var isActive = that.$newElement.hasClass(classNames.SHOW);\r\n\r\n if (isActive) getPlacement(that.$newElement);\r\n });\r\n\r\n this.$element.on('hide' + EVENT_KEY, function () {\r\n that.$menu.data('height', that.$menu.height());\r\n that.$bsContainer.detach();\r\n });\r\n },\r\n\r\n setOptionStatus: function () {\r\n var that = this;\r\n\r\n that.noScroll = false;\r\n\r\n if (that.selectpicker.view.visibleElements && that.selectpicker.view.visibleElements.length) {\r\n for (var i = 0; i < that.selectpicker.view.visibleElements.length; i++) {\r\n var liData = that.selectpicker.current.data[i + that.selectpicker.view.position0],\r\n option = liData.option;\r\n\r\n if (option) {\r\n that.setDisabled(\r\n liData.index,\r\n liData.disabled\r\n );\r\n\r\n that.setSelected(\r\n liData.index,\r\n option.selected\r\n );\r\n }\r\n }\r\n }\r\n },\r\n\r\n /**\r\n * @param {number} index - the index of the option that is being changed\r\n * @param {boolean} selected - true if the option is being selected, false if being deselected\r\n */\r\n setSelected: function (index, selected) {\r\n var li = this.selectpicker.main.elements[index],\r\n liData = this.selectpicker.main.data[index],\r\n activeIndexIsSet = this.activeIndex !== undefined,\r\n thisIsActive = this.activeIndex === index,\r\n prevActive,\r\n a,\r\n // if current option is already active\r\n // OR\r\n // if the current option is being selected, it's NOT multiple, and\r\n // activeIndex is undefined:\r\n // - when the menu is first being opened, OR\r\n // - after a search has been performed, OR\r\n // - when retainActive is false when selecting a new option (i.e. index of the newly selected option is not the same as the current activeIndex)\r\n keepActive = thisIsActive || (selected && !this.multiple && !activeIndexIsSet);\r\n\r\n liData.selected = selected;\r\n\r\n a = li.firstChild;\r\n\r\n if (selected) {\r\n this.selectedIndex = index;\r\n }\r\n\r\n li.classList.toggle('selected', selected);\r\n li.classList.toggle('active', keepActive);\r\n\r\n if (keepActive) {\r\n this.selectpicker.view.currentActive = li;\r\n this.activeIndex = index;\r\n }\r\n\r\n if (a) {\r\n a.classList.toggle('selected', selected);\r\n a.classList.toggle('active', keepActive);\r\n a.setAttribute('aria-selected', selected);\r\n }\r\n\r\n if (!keepActive) {\r\n if (!activeIndexIsSet && selected && this.prevActiveIndex !== undefined) {\r\n prevActive = this.selectpicker.main.elements[this.prevActiveIndex];\r\n\r\n prevActive.classList.remove('active');\r\n if (prevActive.firstChild) {\r\n prevActive.firstChild.classList.remove('active');\r\n }\r\n }\r\n }\r\n },\r\n\r\n /**\r\n * @param {number} index - the index of the option that is being disabled\r\n * @param {boolean} disabled - true if the option is being disabled, false if being enabled\r\n */\r\n setDisabled: function (index, disabled) {\r\n var li = this.selectpicker.main.elements[index],\r\n a;\r\n\r\n this.selectpicker.main.data[index].disabled = disabled;\r\n\r\n a = li.firstChild;\r\n\r\n li.classList.toggle(classNames.DISABLED, disabled);\r\n\r\n if (a) {\r\n if (version.major === '4') a.classList.toggle(classNames.DISABLED, disabled);\r\n\r\n a.setAttribute('aria-disabled', disabled);\r\n\r\n if (disabled) {\r\n a.setAttribute('tabindex', -1);\r\n } else {\r\n a.setAttribute('tabindex', 0);\r\n }\r\n }\r\n },\r\n\r\n isDisabled: function () {\r\n return this.$element[0].disabled;\r\n },\r\n\r\n checkDisabled: function () {\r\n var that = this;\r\n\r\n if (this.isDisabled()) {\r\n this.$newElement[0].classList.add(classNames.DISABLED);\r\n this.$button.addClass(classNames.DISABLED).attr('tabindex', -1).attr('aria-disabled', true);\r\n } else {\r\n if (this.$button[0].classList.contains(classNames.DISABLED)) {\r\n this.$newElement[0].classList.remove(classNames.DISABLED);\r\n this.$button.removeClass(classNames.DISABLED).attr('aria-disabled', false);\r\n }\r\n\r\n if (this.$button.attr('tabindex') == -1 && !this.$element.data('tabindex')) {\r\n this.$button.removeAttr('tabindex');\r\n }\r\n }\r\n\r\n this.$button.on('click', function () {\r\n return !that.isDisabled();\r\n });\r\n },\r\n\r\n togglePlaceholder: function () {\r\n // much faster than calling $.val()\r\n var element = this.$element[0],\r\n selectedIndex = element.selectedIndex,\r\n nothingSelected = selectedIndex === -1;\r\n\r\n if (!nothingSelected && !element.options[selectedIndex].value) nothingSelected = true;\r\n\r\n this.$button.toggleClass('bs-placeholder', nothingSelected);\r\n },\r\n\r\n tabIndex: function () {\r\n if (this.$element.data('tabindex') !== this.$element.attr('tabindex') &&\r\n (this.$element.attr('tabindex') !== -98 && this.$element.attr('tabindex') !== '-98')) {\r\n this.$element.data('tabindex', this.$element.attr('tabindex'));\r\n this.$button.attr('tabindex', this.$element.data('tabindex'));\r\n }\r\n\r\n this.$element.attr('tabindex', -98);\r\n },\r\n\r\n clickListener: function () {\r\n var that = this,\r\n $document = $(document);\r\n\r\n $document.data('spaceSelect', false);\r\n\r\n this.$button.on('keyup', function (e) {\r\n if (/(32)/.test(e.keyCode.toString(10)) && $document.data('spaceSelect')) {\r\n e.preventDefault();\r\n $document.data('spaceSelect', false);\r\n }\r\n });\r\n\r\n this.$newElement.on('show.bs.dropdown', function () {\r\n if (version.major > 3 && !that.dropdown) {\r\n that.dropdown = that.$button.data('bs.dropdown');\r\n that.dropdown._menu = that.$menu[0];\r\n }\r\n });\r\n\r\n this.$button.on('click.bs.dropdown.data-api', function () {\r\n if (!that.$newElement.hasClass(classNames.SHOW)) {\r\n that.setSize();\r\n }\r\n });\r\n\r\n function setFocus () {\r\n if (that.options.liveSearch) {\r\n that.$searchbox.trigger('focus');\r\n } else {\r\n that.$menuInner.trigger('focus');\r\n }\r\n }\r\n\r\n function checkPopperExists () {\r\n if (that.dropdown && that.dropdown._popper && that.dropdown._popper.state.isCreated) {\r\n setFocus();\r\n } else {\r\n requestAnimationFrame(checkPopperExists);\r\n }\r\n }\r\n\r\n this.$element.on('shown' + EVENT_KEY, function () {\r\n if (that.$menuInner[0].scrollTop !== that.selectpicker.view.scrollTop) {\r\n that.$menuInner[0].scrollTop = that.selectpicker.view.scrollTop;\r\n }\r\n\r\n if (version.major > 3) {\r\n requestAnimationFrame(checkPopperExists);\r\n } else {\r\n setFocus();\r\n }\r\n });\r\n\r\n this.$menuInner.on('click', 'li a', function (e, retainActive) {\r\n var $this = $(this),\r\n position0 = that.isVirtual() ? that.selectpicker.view.position0 : 0,\r\n clickedData = that.selectpicker.current.data[$this.parent().index() + position0],\r\n clickedIndex = clickedData.index,\r\n prevValue = getSelectValues(that.$element[0]),\r\n prevIndex = that.$element.prop('selectedIndex'),\r\n triggerChange = true;\r\n\r\n // Don't close on multi choice menu\r\n if (that.multiple && that.options.maxOptions !== 1) {\r\n e.stopPropagation();\r\n }\r\n\r\n e.preventDefault();\r\n\r\n // Don't run if the select is disabled\r\n if (!that.isDisabled() && !$this.parent().hasClass(classNames.DISABLED)) {\r\n var $options = that.$element.find('option'),\r\n option = clickedData.option,\r\n $option = $(option),\r\n state = option.selected,\r\n $optgroup = $option.parent('optgroup'),\r\n $optgroupOptions = $optgroup.find('option'),\r\n maxOptions = that.options.maxOptions,\r\n maxOptionsGrp = $optgroup.data('maxOptions') || false;\r\n\r\n if (clickedIndex === that.activeIndex) retainActive = true;\r\n\r\n if (!retainActive) {\r\n that.prevActiveIndex = that.activeIndex;\r\n that.activeIndex = undefined;\r\n }\r\n\r\n if (!that.multiple) { // Deselect all others if not multi select box\r\n $options.prop('selected', false);\r\n option.selected = true;\r\n that.setSelected(clickedIndex, true);\r\n } else { // Toggle the one we have chosen if we are multi select.\r\n option.selected = !state;\r\n\r\n that.setSelected(clickedIndex, !state);\r\n $this.trigger('blur');\r\n\r\n if (maxOptions !== false || maxOptionsGrp !== false) {\r\n var maxReached = maxOptions < $options.filter(':selected').length,\r\n maxReachedGrp = maxOptionsGrp < $optgroup.find('option:selected').length;\r\n\r\n if ((maxOptions && maxReached) || (maxOptionsGrp && maxReachedGrp)) {\r\n if (maxOptions && maxOptions == 1) {\r\n $options.prop('selected', false);\r\n $option.prop('selected', true);\r\n\r\n for (var i = 0; i < $options.length; i++) {\r\n that.setSelected(i, false);\r\n }\r\n\r\n that.setSelected(clickedIndex, true);\r\n } else if (maxOptionsGrp && maxOptionsGrp == 1) {\r\n $optgroup.find('option:selected').prop('selected', false);\r\n $option.prop('selected', true);\r\n\r\n for (var i = 0; i < $optgroupOptions.length; i++) {\r\n var option = $optgroupOptions[i];\r\n that.setSelected($options.index(option), false);\r\n }\r\n\r\n that.setSelected(clickedIndex, true);\r\n } else {\r\n var maxOptionsText = typeof that.options.maxOptionsText === 'string' ? [that.options.maxOptionsText, that.options.maxOptionsText] : that.options.maxOptionsText,\r\n maxOptionsArr = typeof maxOptionsText === 'function' ? maxOptionsText(maxOptions, maxOptionsGrp) : maxOptionsText,\r\n maxTxt = maxOptionsArr[0].replace('{n}', maxOptions),\r\n maxTxtGrp = maxOptionsArr[1].replace('{n}', maxOptionsGrp),\r\n $notify = $('
    ');\r\n // If {var} is set in array, replace it\r\n /** @deprecated */\r\n if (maxOptionsArr[2]) {\r\n maxTxt = maxTxt.replace('{var}', maxOptionsArr[2][maxOptions > 1 ? 0 : 1]);\r\n maxTxtGrp = maxTxtGrp.replace('{var}', maxOptionsArr[2][maxOptionsGrp > 1 ? 0 : 1]);\r\n }\r\n\r\n $option.prop('selected', false);\r\n\r\n that.$menu.append($notify);\r\n\r\n if (maxOptions && maxReached) {\r\n $notify.append($('
    ' + maxTxt + '
    '));\r\n triggerChange = false;\r\n that.$element.trigger('maxReached' + EVENT_KEY);\r\n }\r\n\r\n if (maxOptionsGrp && maxReachedGrp) {\r\n $notify.append($('
    ' + maxTxtGrp + '
    '));\r\n triggerChange = false;\r\n that.$element.trigger('maxReachedGrp' + EVENT_KEY);\r\n }\r\n\r\n setTimeout(function () {\r\n that.setSelected(clickedIndex, false);\r\n }, 10);\r\n\r\n $notify.delay(750).fadeOut(300, function () {\r\n $(this).remove();\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (!that.multiple || (that.multiple && that.options.maxOptions === 1)) {\r\n that.$button.trigger('focus');\r\n } else if (that.options.liveSearch) {\r\n that.$searchbox.trigger('focus');\r\n }\r\n\r\n // Trigger select 'change'\r\n if (triggerChange) {\r\n if ((prevValue != getSelectValues(that.$element[0]) && that.multiple) || (prevIndex != that.$element.prop('selectedIndex') && !that.multiple)) {\r\n // $option.prop('selected') is current option state (selected/unselected). prevValue is the value of the select prior to being changed.\r\n changedArguments = [option.index, $option.prop('selected'), prevValue];\r\n that.$element\r\n .triggerNative('change');\r\n }\r\n }\r\n }\r\n });\r\n\r\n this.$menu.on('click', 'li.' + classNames.DISABLED + ' a, .' + classNames.POPOVERHEADER + ', .' + classNames.POPOVERHEADER + ' :not(.close)', function (e) {\r\n if (e.currentTarget == this) {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n if (that.options.liveSearch && !$(e.target).hasClass('close')) {\r\n that.$searchbox.trigger('focus');\r\n } else {\r\n that.$button.trigger('focus');\r\n }\r\n }\r\n });\r\n\r\n this.$menuInner.on('click', '.divider, .dropdown-header', function (e) {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n if (that.options.liveSearch) {\r\n that.$searchbox.trigger('focus');\r\n } else {\r\n that.$button.trigger('focus');\r\n }\r\n });\r\n\r\n this.$menu.on('click', '.' + classNames.POPOVERHEADER + ' .close', function () {\r\n that.$button.trigger('click');\r\n });\r\n\r\n this.$searchbox.on('click', function (e) {\r\n e.stopPropagation();\r\n });\r\n\r\n this.$menu.on('click', '.actions-btn', function (e) {\r\n if (that.options.liveSearch) {\r\n that.$searchbox.trigger('focus');\r\n } else {\r\n that.$button.trigger('focus');\r\n }\r\n\r\n e.preventDefault();\r\n e.stopPropagation();\r\n\r\n if ($(this).hasClass('bs-select-all')) {\r\n that.selectAll();\r\n } else {\r\n that.deselectAll();\r\n }\r\n });\r\n\r\n this.$element\r\n .on('change' + EVENT_KEY, function () {\r\n that.render();\r\n that.$element.trigger('changed' + EVENT_KEY, changedArguments);\r\n changedArguments = null;\r\n })\r\n .on('focus' + EVENT_KEY, function () {\r\n if (!that.options.mobile) that.$button.trigger('focus');\r\n });\r\n },\r\n\r\n liveSearchListener: function () {\r\n var that = this,\r\n noResults = document.createElement('li');\r\n\r\n this.$button.on('click.bs.dropdown.data-api', function () {\r\n if (!!that.$searchbox.val()) {\r\n that.$searchbox.val('');\r\n }\r\n });\r\n\r\n this.$searchbox.on('click.bs.dropdown.data-api focus.bs.dropdown.data-api touchend.bs.dropdown.data-api', function (e) {\r\n e.stopPropagation();\r\n });\r\n\r\n this.$searchbox.on('input propertychange', function () {\r\n var searchValue = that.$searchbox.val();\r\n\r\n that.selectpicker.search.elements = [];\r\n that.selectpicker.search.data = [];\r\n\r\n if (searchValue) {\r\n var i,\r\n searchMatch = [],\r\n q = searchValue.toUpperCase(),\r\n cache = {},\r\n cacheArr = [],\r\n searchStyle = that._searchStyle(),\r\n normalizeSearch = that.options.liveSearchNormalize;\r\n\r\n if (normalizeSearch) q = normalizeToBase(q);\r\n\r\n that._$lisSelected = that.$menuInner.find('.selected');\r\n\r\n for (var i = 0; i < that.selectpicker.main.data.length; i++) {\r\n var li = that.selectpicker.main.data[i];\r\n\r\n if (!cache[i]) {\r\n cache[i] = stringSearch(li, q, searchStyle, normalizeSearch);\r\n }\r\n\r\n if (cache[i] && li.headerIndex !== undefined && cacheArr.indexOf(li.headerIndex) === -1) {\r\n if (li.headerIndex > 0) {\r\n cache[li.headerIndex - 1] = true;\r\n cacheArr.push(li.headerIndex - 1);\r\n }\r\n\r\n cache[li.headerIndex] = true;\r\n cacheArr.push(li.headerIndex);\r\n\r\n cache[li.lastIndex + 1] = true;\r\n }\r\n\r\n if (cache[i] && li.type !== 'optgroup-label') cacheArr.push(i);\r\n }\r\n\r\n for (var i = 0, cacheLen = cacheArr.length; i < cacheLen; i++) {\r\n var index = cacheArr[i],\r\n prevIndex = cacheArr[i - 1],\r\n li = that.selectpicker.main.data[index],\r\n liPrev = that.selectpicker.main.data[prevIndex];\r\n\r\n if (li.type !== 'divider' || (li.type === 'divider' && liPrev && liPrev.type !== 'divider' && cacheLen - 1 !== i)) {\r\n that.selectpicker.search.data.push(li);\r\n searchMatch.push(that.selectpicker.main.elements[index]);\r\n }\r\n }\r\n\r\n that.activeIndex = undefined;\r\n that.noScroll = true;\r\n that.$menuInner.scrollTop(0);\r\n that.selectpicker.search.elements = searchMatch;\r\n that.createView(true);\r\n\r\n if (!searchMatch.length) {\r\n noResults.className = 'no-results';\r\n noResults.innerHTML = that.options.noneResultsText.replace('{0}', '\"' + htmlEscape(searchValue) + '\"');\r\n that.$menuInner[0].firstChild.appendChild(noResults);\r\n }\r\n } else {\r\n that.$menuInner.scrollTop(0);\r\n that.createView(false);\r\n }\r\n });\r\n },\r\n\r\n _searchStyle: function () {\r\n return this.options.liveSearchStyle || 'contains';\r\n },\r\n\r\n val: function (value) {\r\n if (typeof value !== 'undefined') {\r\n var prevValue = getSelectValues(this.$element[0]);\r\n\r\n changedArguments = [null, null, prevValue];\r\n\r\n this.$element\r\n .val(value)\r\n .trigger('changed' + EVENT_KEY, changedArguments);\r\n\r\n this.render();\r\n\r\n changedArguments = null;\r\n\r\n return this.$element;\r\n } else {\r\n return this.$element.val();\r\n }\r\n },\r\n\r\n changeAll: function (status) {\r\n if (!this.multiple) return;\r\n if (typeof status === 'undefined') status = true;\r\n\r\n var element = this.$element[0],\r\n previousSelected = 0,\r\n currentSelected = 0,\r\n prevValue = getSelectValues(element);\r\n\r\n element.classList.add('bs-select-hidden');\r\n\r\n for (var i = 0, len = this.selectpicker.current.elements.length; i < len; i++) {\r\n var liData = this.selectpicker.current.data[i],\r\n option = liData.option;\r\n\r\n if (option && !liData.disabled && liData.type !== 'divider') {\r\n if (liData.selected) previousSelected++;\r\n option.selected = status;\r\n if (status) currentSelected++;\r\n }\r\n }\r\n\r\n element.classList.remove('bs-select-hidden');\r\n\r\n if (previousSelected === currentSelected) return;\r\n\r\n this.setOptionStatus();\r\n\r\n this.togglePlaceholder();\r\n\r\n changedArguments = [null, null, prevValue];\r\n\r\n this.$element\r\n .triggerNative('change');\r\n },\r\n\r\n selectAll: function () {\r\n return this.changeAll(true);\r\n },\r\n\r\n deselectAll: function () {\r\n return this.changeAll(false);\r\n },\r\n\r\n toggle: function (e) {\r\n e = e || window.event;\r\n\r\n if (e) e.stopPropagation();\r\n\r\n this.$button.trigger('click.bs.dropdown.data-api');\r\n },\r\n\r\n keydown: function (e) {\r\n var $this = $(this),\r\n isToggle = $this.hasClass('dropdown-toggle'),\r\n $parent = isToggle ? $this.closest('.dropdown') : $this.closest(Selector.MENU),\r\n that = $parent.data('this'),\r\n $items = that.findLis(),\r\n index,\r\n isActive,\r\n liActive,\r\n activeLi,\r\n offset,\r\n updateScroll = false,\r\n downOnTab = e.which === keyCodes.TAB && !isToggle && !that.options.selectOnTab,\r\n isArrowKey = REGEXP_ARROW.test(e.which) || downOnTab,\r\n scrollTop = that.$menuInner[0].scrollTop,\r\n isVirtual = that.isVirtual(),\r\n position0 = isVirtual === true ? that.selectpicker.view.position0 : 0;\r\n\r\n isActive = that.$newElement.hasClass(classNames.SHOW);\r\n\r\n if (\r\n !isActive &&\r\n (\r\n isArrowKey ||\r\n (e.which >= 48 && e.which <= 57) ||\r\n (e.which >= 96 && e.which <= 105) ||\r\n (e.which >= 65 && e.which <= 90)\r\n )\r\n ) {\r\n that.$button.trigger('click.bs.dropdown.data-api');\r\n\r\n if (that.options.liveSearch) {\r\n that.$searchbox.trigger('focus');\r\n return;\r\n }\r\n }\r\n\r\n if (e.which === keyCodes.ESCAPE && isActive) {\r\n e.preventDefault();\r\n that.$button.trigger('click.bs.dropdown.data-api').trigger('focus');\r\n }\r\n\r\n if (isArrowKey) { // if up or down\r\n if (!$items.length) return;\r\n\r\n // $items.index/.filter is too slow with a large list and no virtual scroll\r\n index = isVirtual === true ? $items.index($items.filter('.active')) : that.activeIndex;\r\n\r\n if (index === undefined) index = -1;\r\n\r\n if (index !== -1) {\r\n liActive = that.selectpicker.current.elements[index + position0];\r\n liActive.classList.remove('active');\r\n if (liActive.firstChild) liActive.firstChild.classList.remove('active');\r\n }\r\n\r\n if (e.which === keyCodes.ARROW_UP) { // up\r\n if (index !== -1) index--;\r\n if (index + position0 < 0) index += $items.length;\r\n\r\n if (!that.selectpicker.view.canHighlight[index + position0]) {\r\n index = that.selectpicker.view.canHighlight.slice(0, index + position0).lastIndexOf(true) - position0;\r\n if (index === -1) index = $items.length - 1;\r\n }\r\n } else if (e.which === keyCodes.ARROW_DOWN || downOnTab) { // down\r\n index++;\r\n if (index + position0 >= that.selectpicker.view.canHighlight.length) index = 0;\r\n\r\n if (!that.selectpicker.view.canHighlight[index + position0]) {\r\n index = index + 1 + that.selectpicker.view.canHighlight.slice(index + position0 + 1).indexOf(true);\r\n }\r\n }\r\n\r\n e.preventDefault();\r\n\r\n var liActiveIndex = position0 + index;\r\n\r\n if (e.which === keyCodes.ARROW_UP) { // up\r\n // scroll to bottom and highlight last option\r\n if (position0 === 0 && index === $items.length - 1) {\r\n that.$menuInner[0].scrollTop = that.$menuInner[0].scrollHeight;\r\n\r\n liActiveIndex = that.selectpicker.current.elements.length - 1;\r\n } else {\r\n activeLi = that.selectpicker.current.data[liActiveIndex];\r\n offset = activeLi.position - activeLi.height;\r\n\r\n updateScroll = offset < scrollTop;\r\n }\r\n } else if (e.which === keyCodes.ARROW_DOWN || downOnTab) { // down\r\n // scroll to top and highlight first option\r\n if (index === 0) {\r\n that.$menuInner[0].scrollTop = 0;\r\n\r\n liActiveIndex = 0;\r\n } else {\r\n activeLi = that.selectpicker.current.data[liActiveIndex];\r\n offset = activeLi.position - that.sizeInfo.menuInnerHeight;\r\n\r\n updateScroll = offset > scrollTop;\r\n }\r\n }\r\n\r\n liActive = that.selectpicker.current.elements[liActiveIndex];\r\n\r\n if (liActive) {\r\n liActive.classList.add('active');\r\n if (liActive.firstChild) liActive.firstChild.classList.add('active');\r\n }\r\n\r\n that.activeIndex = that.selectpicker.current.data[liActiveIndex].index;\r\n\r\n that.selectpicker.view.currentActive = liActive;\r\n\r\n if (updateScroll) that.$menuInner[0].scrollTop = offset;\r\n\r\n if (that.options.liveSearch) {\r\n that.$searchbox.trigger('focus');\r\n } else {\r\n $this.trigger('focus');\r\n }\r\n } else if (\r\n (!$this.is('input') && !REGEXP_TAB_OR_ESCAPE.test(e.which)) ||\r\n (e.which === keyCodes.SPACE && that.selectpicker.keydown.keyHistory)\r\n ) {\r\n var searchMatch,\r\n matches = [],\r\n keyHistory;\r\n\r\n e.preventDefault();\r\n\r\n that.selectpicker.keydown.keyHistory += keyCodeMap[e.which];\r\n\r\n if (that.selectpicker.keydown.resetKeyHistory.cancel) clearTimeout(that.selectpicker.keydown.resetKeyHistory.cancel);\r\n that.selectpicker.keydown.resetKeyHistory.cancel = that.selectpicker.keydown.resetKeyHistory.start();\r\n\r\n keyHistory = that.selectpicker.keydown.keyHistory;\r\n\r\n // if all letters are the same, set keyHistory to just the first character when searching\r\n if (/^(.)\\1+$/.test(keyHistory)) {\r\n keyHistory = keyHistory.charAt(0);\r\n }\r\n\r\n // find matches\r\n for (var i = 0; i < that.selectpicker.current.data.length; i++) {\r\n var li = that.selectpicker.current.data[i],\r\n hasMatch;\r\n\r\n hasMatch = stringSearch(li, keyHistory, 'startsWith', true);\r\n\r\n if (hasMatch && that.selectpicker.view.canHighlight[i]) {\r\n matches.push(li.index);\r\n }\r\n }\r\n\r\n if (matches.length) {\r\n var matchIndex = 0;\r\n\r\n $items.removeClass('active').find('a').removeClass('active');\r\n\r\n // either only one key has been pressed or they are all the same key\r\n if (keyHistory.length === 1) {\r\n matchIndex = matches.indexOf(that.activeIndex);\r\n\r\n if (matchIndex === -1 || matchIndex === matches.length - 1) {\r\n matchIndex = 0;\r\n } else {\r\n matchIndex++;\r\n }\r\n }\r\n\r\n searchMatch = matches[matchIndex];\r\n\r\n activeLi = that.selectpicker.main.data[searchMatch];\r\n\r\n if (scrollTop - activeLi.position > 0) {\r\n offset = activeLi.position - activeLi.height;\r\n updateScroll = true;\r\n } else {\r\n offset = activeLi.position - that.sizeInfo.menuInnerHeight;\r\n // if the option is already visible at the current scroll position, just keep it the same\r\n updateScroll = activeLi.position > scrollTop + that.sizeInfo.menuInnerHeight;\r\n }\r\n\r\n liActive = that.selectpicker.main.elements[searchMatch];\r\n liActive.classList.add('active');\r\n if (liActive.firstChild) liActive.firstChild.classList.add('active');\r\n that.activeIndex = matches[matchIndex];\r\n\r\n liActive.firstChild.focus();\r\n\r\n if (updateScroll) that.$menuInner[0].scrollTop = offset;\r\n\r\n $this.trigger('focus');\r\n }\r\n }\r\n\r\n // Select focused option if \"Enter\", \"Spacebar\" or \"Tab\" (when selectOnTab is true) are pressed inside the menu.\r\n if (\r\n isActive &&\r\n (\r\n (e.which === keyCodes.SPACE && !that.selectpicker.keydown.keyHistory) ||\r\n e.which === keyCodes.ENTER ||\r\n (e.which === keyCodes.TAB && that.options.selectOnTab)\r\n )\r\n ) {\r\n if (e.which !== keyCodes.SPACE) e.preventDefault();\r\n\r\n if (!that.options.liveSearch || e.which !== keyCodes.SPACE) {\r\n that.$menuInner.find('.active a').trigger('click', true); // retain active class\r\n $this.trigger('focus');\r\n\r\n if (!that.options.liveSearch) {\r\n // Prevent screen from scrolling if the user hits the spacebar\r\n e.preventDefault();\r\n // Fixes spacebar selection of dropdown items in FF & IE\r\n $(document).data('spaceSelect', true);\r\n }\r\n }\r\n }\r\n },\r\n\r\n mobile: function () {\r\n this.$element[0].classList.add('mobile-device');\r\n },\r\n\r\n refresh: function () {\r\n // update options if data attributes have been changed\r\n var config = $.extend({}, this.options, this.$element.data());\r\n this.options = config;\r\n\r\n this.checkDisabled();\r\n this.setStyle();\r\n this.render();\r\n this.createLi();\r\n this.setWidth();\r\n\r\n this.setSize(true);\r\n\r\n this.$element.trigger('refreshed' + EVENT_KEY);\r\n },\r\n\r\n hide: function () {\r\n this.$newElement.hide();\r\n },\r\n\r\n show: function () {\r\n this.$newElement.show();\r\n },\r\n\r\n remove: function () {\r\n this.$newElement.remove();\r\n this.$element.remove();\r\n },\r\n\r\n destroy: function () {\r\n this.$newElement.before(this.$element).remove();\r\n\r\n if (this.$bsContainer) {\r\n this.$bsContainer.remove();\r\n } else {\r\n this.$menu.remove();\r\n }\r\n\r\n this.$element\r\n .off(EVENT_KEY)\r\n .removeData('selectpicker')\r\n .removeClass('bs-select-hidden selectpicker');\r\n\r\n $(window).off(EVENT_KEY + '.' + this.selectId);\r\n }\r\n };\r\n\r\n // SELECTPICKER PLUGIN DEFINITION\r\n // ==============================\r\n function Plugin (option) {\r\n // get the args of the outer function..\r\n var args = arguments;\r\n // The arguments of the function are explicitly re-defined from the argument list, because the shift causes them\r\n // to get lost/corrupted in android 2.3 and IE9 #715 #775\r\n var _option = option;\r\n\r\n [].shift.apply(args);\r\n\r\n // if the version was not set successfully\r\n if (!version.success) {\r\n // try to retreive it again\r\n try {\r\n version.full = ($.fn.dropdown.Constructor.VERSION || '').split(' ')[0].split('.');\r\n } catch (err) {\r\n // fall back to use BootstrapVersion if set\r\n if (Selectpicker.BootstrapVersion) {\r\n version.full = Selectpicker.BootstrapVersion.split(' ')[0].split('.');\r\n } else {\r\n version.full = [version.major, '0', '0'];\r\n\r\n console.warn(\r\n 'There was an issue retrieving Bootstrap\\'s version. ' +\r\n 'Ensure Bootstrap is being loaded before bootstrap-select and there is no namespace collision. ' +\r\n 'If loading Bootstrap asynchronously, the version may need to be manually specified via $.fn.selectpicker.Constructor.BootstrapVersion.',\r\n err\r\n );\r\n }\r\n }\r\n\r\n version.major = version.full[0];\r\n version.success = true;\r\n }\r\n\r\n if (version.major === '4') {\r\n // some defaults need to be changed if using Bootstrap 4\r\n // check to see if they have already been manually changed before forcing them to update\r\n var toUpdate = [];\r\n\r\n if (Selectpicker.DEFAULTS.style === classNames.BUTTONCLASS) toUpdate.push({ name: 'style', className: 'BUTTONCLASS' });\r\n if (Selectpicker.DEFAULTS.iconBase === classNames.ICONBASE) toUpdate.push({ name: 'iconBase', className: 'ICONBASE' });\r\n if (Selectpicker.DEFAULTS.tickIcon === classNames.TICKICON) toUpdate.push({ name: 'tickIcon', className: 'TICKICON' });\r\n\r\n classNames.DIVIDER = 'dropdown-divider';\r\n classNames.SHOW = 'show';\r\n classNames.BUTTONCLASS = 'btn-light';\r\n classNames.POPOVERHEADER = 'popover-header';\r\n classNames.ICONBASE = '';\r\n classNames.TICKICON = 'bs-ok-default';\r\n\r\n for (var i = 0; i < toUpdate.length; i++) {\r\n var option = toUpdate[i];\r\n Selectpicker.DEFAULTS[option.name] = classNames[option.className];\r\n }\r\n }\r\n\r\n var value;\r\n var chain = this.each(function () {\r\n var $this = $(this);\r\n if ($this.is('select')) {\r\n var data = $this.data('selectpicker'),\r\n options = typeof _option == 'object' && _option;\r\n\r\n if (!data) {\r\n var dataAttributes = $this.data();\r\n\r\n for (var dataAttr in dataAttributes) {\r\n if (dataAttributes.hasOwnProperty(dataAttr) && $.inArray(dataAttr, DISALLOWED_ATTRIBUTES) !== -1) {\r\n delete dataAttributes[dataAttr];\r\n }\r\n }\r\n\r\n var config = $.extend({}, Selectpicker.DEFAULTS, $.fn.selectpicker.defaults || {}, dataAttributes, options);\r\n config.template = $.extend({}, Selectpicker.DEFAULTS.template, ($.fn.selectpicker.defaults ? $.fn.selectpicker.defaults.template : {}), dataAttributes.template, options.template);\r\n $this.data('selectpicker', (data = new Selectpicker(this, config)));\r\n } else if (options) {\r\n for (var i in options) {\r\n if (options.hasOwnProperty(i)) {\r\n data.options[i] = options[i];\r\n }\r\n }\r\n }\r\n\r\n if (typeof _option == 'string') {\r\n if (data[_option] instanceof Function) {\r\n value = data[_option].apply(data, args);\r\n } else {\r\n value = data.options[_option];\r\n }\r\n }\r\n }\r\n });\r\n\r\n if (typeof value !== 'undefined') {\r\n // noinspection JSUnusedAssignment\r\n return value;\r\n } else {\r\n return chain;\r\n }\r\n }\r\n\r\n var old = $.fn.selectpicker;\r\n $.fn.selectpicker = Plugin;\r\n $.fn.selectpicker.Constructor = Selectpicker;\r\n\r\n // SELECTPICKER NO CONFLICT\r\n // ========================\r\n $.fn.selectpicker.noConflict = function () {\r\n $.fn.selectpicker = old;\r\n return this;\r\n };\r\n\r\n $(document)\r\n .off('keydown.bs.dropdown.data-api')\r\n .on('keydown' + EVENT_KEY, '.bootstrap-select [data-toggle=\"dropdown\"], .bootstrap-select [role=\"listbox\"], .bootstrap-select .bs-searchbox input', Selectpicker.prototype.keydown)\r\n .on('focusin.modal', '.bootstrap-select [data-toggle=\"dropdown\"], .bootstrap-select [role=\"listbox\"], .bootstrap-select .bs-searchbox input', function (e) {\r\n e.stopPropagation();\r\n });\r\n\r\n // SELECTPICKER DATA-API\r\n // =====================\r\n $(window).on('load' + EVENT_KEY + '.data-api', function () {\r\n $('.selectpicker').each(function () {\r\n var $selectpicker = $(this);\r\n Plugin.call($selectpicker, $selectpicker.data());\r\n })\r\n });\r\n})(jQuery);\r\n"]} \ No newline at end of file diff --git a/templates/yandexhome/css/bootstrap-select.min.css b/templates/yandexhome/css/bootstrap-select.min.css new file mode 100644 index 0000000..43e66e1 --- /dev/null +++ b/templates/yandexhome/css/bootstrap-select.min.css @@ -0,0 +1,6 @@ +/*! + * Bootstrap-select v1.13.9 (https://developer.snapappointments.com/bootstrap-select) + * + * Copyright 2012-2019 SnapAppointments, LLC + * Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE) + */.bootstrap-select>select.bs-select-hidden,select.bs-select-hidden,select.selectpicker{display:none!important}.bootstrap-select{width:220px\0;vertical-align:middle}.bootstrap-select>.dropdown-toggle{position:relative;width:100%;text-align:right;white-space:nowrap;display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.bootstrap-select>.dropdown-toggle:after{margin-top:-1px}.bootstrap-select>.dropdown-toggle.bs-placeholder,.bootstrap-select>.dropdown-toggle.bs-placeholder:active,.bootstrap-select>.dropdown-toggle.bs-placeholder:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder:hover{color:#999}.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success:hover{color:rgba(255,255,255,.5)}.bootstrap-select>select{position:absolute!important;bottom:0;left:50%;display:block!important;width:.5px!important;height:100%!important;padding:0!important;opacity:0!important;border:none;z-index:0!important}.bootstrap-select>select.mobile-device{top:0;left:0;display:block!important;width:100%!important;z-index:2!important}.bootstrap-select.is-invalid .dropdown-toggle,.error .bootstrap-select .dropdown-toggle,.has-error .bootstrap-select .dropdown-toggle,.was-validated .bootstrap-select .selectpicker:invalid+.dropdown-toggle{border-color:#b94a48}.bootstrap-select.is-valid .dropdown-toggle,.was-validated .bootstrap-select .selectpicker:valid+.dropdown-toggle{border-color:#28a745}.bootstrap-select.fit-width{width:auto!important}.bootstrap-select:not([class*=col-]):not([class*=form-control]):not(.input-group-btn){width:220px}.bootstrap-select .dropdown-toggle:focus,.bootstrap-select>select.mobile-device:focus+.dropdown-toggle{outline:thin dotted #333!important;outline:5px auto -webkit-focus-ring-color!important;outline-offset:-2px}.bootstrap-select.form-control{margin-bottom:0;padding:0;border:none}:not(.input-group)>.bootstrap-select.form-control:not([class*=col-]){width:100%}.bootstrap-select.form-control.input-group-btn{float:none;z-index:auto}.form-inline .bootstrap-select,.form-inline .bootstrap-select.form-control:not([class*=col-]){width:auto}.bootstrap-select:not(.input-group-btn),.bootstrap-select[class*=col-]{float:none;display:inline-block;margin-left:0}.bootstrap-select.dropdown-menu-right,.bootstrap-select[class*=col-].dropdown-menu-right,.row .bootstrap-select[class*=col-].dropdown-menu-right{float:right}.form-group .bootstrap-select,.form-horizontal .bootstrap-select,.form-inline .bootstrap-select{margin-bottom:0}.form-group-lg .bootstrap-select.form-control,.form-group-sm .bootstrap-select.form-control{padding:0}.form-group-lg .bootstrap-select.form-control .dropdown-toggle,.form-group-sm .bootstrap-select.form-control .dropdown-toggle{height:100%;font-size:inherit;line-height:inherit;border-radius:inherit}.bootstrap-select.form-control-lg .dropdown-toggle,.bootstrap-select.form-control-sm .dropdown-toggle{font-size:inherit;line-height:inherit;border-radius:inherit}.bootstrap-select.form-control-sm .dropdown-toggle{padding:.25rem .5rem}.bootstrap-select.form-control-lg .dropdown-toggle{padding:.5rem 1rem}.form-inline .bootstrap-select .form-control{width:100%}.bootstrap-select.disabled,.bootstrap-select>.disabled{cursor:not-allowed}.bootstrap-select.disabled:focus,.bootstrap-select>.disabled:focus{outline:0!important}.bootstrap-select.bs-container{position:absolute;top:0;left:0;height:0!important;padding:0!important}.bootstrap-select.bs-container .dropdown-menu{z-index:1060}.bootstrap-select .dropdown-toggle .filter-option{position:static;top:0;left:0;float:left;height:100%;width:100%;text-align:left;overflow:hidden;-webkit-box-flex:0;-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto}.bs3.bootstrap-select .dropdown-toggle .filter-option{padding-right:inherit}.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option{position:absolute;padding-top:inherit;padding-bottom:inherit;padding-left:inherit;float:none}.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option .filter-option-inner{padding-right:inherit}.bootstrap-select .dropdown-toggle .filter-option-inner-inner{overflow:hidden}.bootstrap-select .dropdown-toggle .filter-expand{width:0!important;float:left;opacity:0!important;overflow:hidden}.bootstrap-select .dropdown-toggle .caret{position:absolute;top:50%;right:12px;margin-top:-2px;vertical-align:middle}.input-group .bootstrap-select.form-control .dropdown-toggle{border-radius:inherit}.bootstrap-select[class*=col-] .dropdown-toggle{width:100%}.bootstrap-select .dropdown-menu{min-width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select .dropdown-menu>.inner:focus{outline:0!important}.bootstrap-select .dropdown-menu.inner{position:static;float:none;border:0;padding:0;margin:0;border-radius:0;-webkit-box-shadow:none;box-shadow:none}.bootstrap-select .dropdown-menu li{position:relative}.bootstrap-select .dropdown-menu li.active small{color:rgba(255,255,255,.5)!important}.bootstrap-select .dropdown-menu li.disabled a{cursor:not-allowed}.bootstrap-select .dropdown-menu li a{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.bootstrap-select .dropdown-menu li a.opt{position:relative;padding-left:2.25em}.bootstrap-select .dropdown-menu li a span.check-mark{display:none}.bootstrap-select .dropdown-menu li a span.text{display:inline-block}.bootstrap-select .dropdown-menu li small{padding-left:.5em}.bootstrap-select .dropdown-menu .notify{position:absolute;bottom:5px;width:96%;margin:0 2%;min-height:26px;padding:3px 5px;background:#f5f5f5;border:1px solid #e3e3e3;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05);pointer-events:none;opacity:.9;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select .no-results{padding:3px;background:#f5f5f5;margin:0 5px;white-space:nowrap}.bootstrap-select.fit-width .dropdown-toggle .filter-option{position:static;display:inline;padding:0;width:auto}.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner,.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner-inner{display:inline}.bootstrap-select.fit-width .dropdown-toggle .bs-caret:before{content:'\00a0'}.bootstrap-select.fit-width .dropdown-toggle .caret{position:static;top:auto;margin-top:-1px}.bootstrap-select.show-tick .dropdown-menu .selected span.check-mark{position:absolute;display:inline-block;right:15px;top:5px}.bootstrap-select.show-tick .dropdown-menu li a span.text{margin-right:34px}.bootstrap-select .bs-ok-default:after{content:'';display:block;width:.5em;height:1em;border-style:solid;border-width:0 .26em .26em 0;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg)}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle,.bootstrap-select.show-menu-arrow.show>.dropdown-toggle{z-index:1061}.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:before{content:'';border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid rgba(204,204,204,.2);position:absolute;bottom:-4px;left:9px;display:none}.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:after{content:'';border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute;bottom:-4px;left:10px;display:none}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:before{bottom:auto;top:-4px;border-top:7px solid rgba(204,204,204,.2);border-bottom:0}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:after{bottom:auto;top:-4px;border-top:6px solid #fff;border-bottom:0}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:before{right:12px;left:auto}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:after{right:13px;left:auto}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle .filter-option:after,.bootstrap-select.show-menu-arrow.open>.dropdown-toggle .filter-option:before,.bootstrap-select.show-menu-arrow.show>.dropdown-toggle .filter-option:after,.bootstrap-select.show-menu-arrow.show>.dropdown-toggle .filter-option:before{display:block}.bs-actionsbox,.bs-donebutton,.bs-searchbox{padding:4px 8px}.bs-actionsbox{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bs-actionsbox .btn-group button{width:50%}.bs-donebutton{float:left;width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bs-donebutton .btn-group button{width:100%}.bs-searchbox+.bs-actionsbox{padding:0 8px 4px}.bs-searchbox .form-control{margin-bottom:0;width:100%;float:none} \ No newline at end of file diff --git a/templates/yandexhome/css/bootstrap-select.min.js b/templates/yandexhome/css/bootstrap-select.min.js new file mode 100644 index 0000000..20c818d --- /dev/null +++ b/templates/yandexhome/css/bootstrap-select.min.js @@ -0,0 +1,9 @@ +/*! + * Bootstrap-select v1.13.9 (https://developer.snapappointments.com/bootstrap-select) + * + * Copyright 2012-2019 SnapAppointments, LLC + * Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE) + */ + +!function(e,t){void 0===e&&void 0!==window&&(e=window),"function"==typeof define&&define.amd?define(["jquery"],function(e){return t(e)}):"object"==typeof module&&module.exports?module.exports=t(require("jquery")):t(e.jQuery)}(this,function(e){!function(z){"use strict";var d=["sanitize","whiteList","sanitizeFn"],l=["background","cite","href","itemtype","longdesc","poster","src","xlink:href"],e={"*":["class","dir","id","lang","role","tabindex","style",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},r=/^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi,a=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i;function v(e,t){var i=e.nodeName.toLowerCase();if(-1!==z.inArray(i,t))return-1===z.inArray(i,l)||Boolean(e.nodeValue.match(r)||e.nodeValue.match(a));for(var s=z(t).filter(function(e,t){return t instanceof RegExp}),n=0,o=s.length;n]+>/g,"")),s&&(a=w(a)),a=a.toUpperCase(),o="contains"===i?0<=a.indexOf(t):a.startsWith(t)))break}return o}function L(e){return parseInt(e,10)||0}z.fn.triggerNative=function(e){var t,i=this[0];i.dispatchEvent?(u?t=new Event(e,{bubbles:!0}):(t=document.createEvent("Event")).initEvent(e,!0,!1),i.dispatchEvent(t)):i.fireEvent?((t=document.createEventObject()).eventType=e,i.fireEvent("on"+e,t)):this.trigger(e)};var f={"\xc0":"A","\xc1":"A","\xc2":"A","\xc3":"A","\xc4":"A","\xc5":"A","\xe0":"a","\xe1":"a","\xe2":"a","\xe3":"a","\xe4":"a","\xe5":"a","\xc7":"C","\xe7":"c","\xd0":"D","\xf0":"d","\xc8":"E","\xc9":"E","\xca":"E","\xcb":"E","\xe8":"e","\xe9":"e","\xea":"e","\xeb":"e","\xcc":"I","\xcd":"I","\xce":"I","\xcf":"I","\xec":"i","\xed":"i","\xee":"i","\xef":"i","\xd1":"N","\xf1":"n","\xd2":"O","\xd3":"O","\xd4":"O","\xd5":"O","\xd6":"O","\xd8":"O","\xf2":"o","\xf3":"o","\xf4":"o","\xf5":"o","\xf6":"o","\xf8":"o","\xd9":"U","\xda":"U","\xdb":"U","\xdc":"U","\xf9":"u","\xfa":"u","\xfb":"u","\xfc":"u","\xdd":"Y","\xfd":"y","\xff":"y","\xc6":"Ae","\xe6":"ae","\xde":"Th","\xfe":"th","\xdf":"ss","\u0100":"A","\u0102":"A","\u0104":"A","\u0101":"a","\u0103":"a","\u0105":"a","\u0106":"C","\u0108":"C","\u010a":"C","\u010c":"C","\u0107":"c","\u0109":"c","\u010b":"c","\u010d":"c","\u010e":"D","\u0110":"D","\u010f":"d","\u0111":"d","\u0112":"E","\u0114":"E","\u0116":"E","\u0118":"E","\u011a":"E","\u0113":"e","\u0115":"e","\u0117":"e","\u0119":"e","\u011b":"e","\u011c":"G","\u011e":"G","\u0120":"G","\u0122":"G","\u011d":"g","\u011f":"g","\u0121":"g","\u0123":"g","\u0124":"H","\u0126":"H","\u0125":"h","\u0127":"h","\u0128":"I","\u012a":"I","\u012c":"I","\u012e":"I","\u0130":"I","\u0129":"i","\u012b":"i","\u012d":"i","\u012f":"i","\u0131":"i","\u0134":"J","\u0135":"j","\u0136":"K","\u0137":"k","\u0138":"k","\u0139":"L","\u013b":"L","\u013d":"L","\u013f":"L","\u0141":"L","\u013a":"l","\u013c":"l","\u013e":"l","\u0140":"l","\u0142":"l","\u0143":"N","\u0145":"N","\u0147":"N","\u014a":"N","\u0144":"n","\u0146":"n","\u0148":"n","\u014b":"n","\u014c":"O","\u014e":"O","\u0150":"O","\u014d":"o","\u014f":"o","\u0151":"o","\u0154":"R","\u0156":"R","\u0158":"R","\u0155":"r","\u0157":"r","\u0159":"r","\u015a":"S","\u015c":"S","\u015e":"S","\u0160":"S","\u015b":"s","\u015d":"s","\u015f":"s","\u0161":"s","\u0162":"T","\u0164":"T","\u0166":"T","\u0163":"t","\u0165":"t","\u0167":"t","\u0168":"U","\u016a":"U","\u016c":"U","\u016e":"U","\u0170":"U","\u0172":"U","\u0169":"u","\u016b":"u","\u016d":"u","\u016f":"u","\u0171":"u","\u0173":"u","\u0174":"W","\u0175":"w","\u0176":"Y","\u0177":"y","\u0178":"Y","\u0179":"Z","\u017b":"Z","\u017d":"Z","\u017a":"z","\u017c":"z","\u017e":"z","\u0132":"IJ","\u0133":"ij","\u0152":"Oe","\u0153":"oe","\u0149":"'n","\u017f":"s"},m=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,g=RegExp("[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff\\u1ab0-\\u1aff\\u1dc0-\\u1dff]","g");function b(e){return f[e]}function w(e){return(e=e.toString())&&e.replace(m,b).replace(g,"")}var x,I,k,y,S,O=(x={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},I=function(e){return x[e]},k="(?:"+Object.keys(x).join("|")+")",y=RegExp(k),S=RegExp(k,"g"),function(e){return e=null==e?"":""+e,y.test(e)?e.replace(S,I):e}),T={32:" ",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",59:";",65:"A",66:"B",67:"C",68:"D",69:"E",70:"F",71:"G",72:"H",73:"I",74:"J",75:"K",76:"L",77:"M",78:"N",79:"O",80:"P",81:"Q",82:"R",83:"S",84:"T",85:"U",86:"V",87:"W",88:"X",89:"Y",90:"Z",96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9"},A=27,N=13,D=32,H=9,P=38,W=40,M={success:!1,major:"3"};try{M.full=(z.fn.dropdown.Constructor.VERSION||"").split(" ")[0].split("."),M.major=M.full[0],M.success=!0}catch(e){}var R=0,U=".bs.select",j={DISABLED:"disabled",DIVIDER:"divider",SHOW:"open",DROPUP:"dropup",MENU:"dropdown-menu",MENURIGHT:"dropdown-menu-right",MENULEFT:"dropdown-menu-left",BUTTONCLASS:"btn-default",POPOVERHEADER:"popover-title",ICONBASE:"glyphicon",TICKICON:"glyphicon-ok"},V={MENU:"."+j.MENU},F={span:document.createElement("span"),i:document.createElement("i"),subtext:document.createElement("small"),a:document.createElement("a"),li:document.createElement("li"),whitespace:document.createTextNode("\xa0"),fragment:document.createDocumentFragment()};F.a.setAttribute("role","option"),F.subtext.className="text-muted",F.text=F.span.cloneNode(!1),F.text.className="text",F.checkMark=F.span.cloneNode(!1);var _=new RegExp(P+"|"+W),q=new RegExp("^"+H+"$|"+A),G=function(e,t,i){var s=F.li.cloneNode(!1);return e&&(1===e.nodeType||11===e.nodeType?s.appendChild(e):s.innerHTML=e),void 0!==t&&""!==t&&(s.className=t),null!=i&&s.classList.add("optgroup-"+i),s},K=function(e,t,i){var s=F.a.cloneNode(!0);return e&&(11===e.nodeType?s.appendChild(e):s.insertAdjacentHTML("beforeend",e)),void 0!==t&&""!==t&&(s.className=t),"4"===M.major&&s.classList.add("dropdown-item"),i&&s.setAttribute("style",i),s},Y=function(e,t){var i,s,n=F.text.cloneNode(!1);if(e.content)n.innerHTML=e.content;else{if(n.textContent=e.text,e.icon){var o=F.whitespace.cloneNode(!1);(s=(!0===t?F.i:F.span).cloneNode(!1)).className=e.iconBase+" "+e.icon,F.fragment.appendChild(s),F.fragment.appendChild(o)}e.subtext&&((i=F.subtext.cloneNode(!1)).textContent=e.subtext,n.appendChild(i))}if(!0===t)for(;0'},maxOptions:!1,mobile:!1,selectOnTab:!1,dropdownAlignRight:!1,windowPadding:0,virtualScroll:600,display:!1,sanitize:!0,sanitizeFn:null,whiteList:e},J.prototype={constructor:J,init:function(){var i=this,e=this.$element.attr("id");this.selectId=R++,this.$element[0].classList.add("bs-select-hidden"),this.multiple=this.$element.prop("multiple"),this.autofocus=this.$element.prop("autofocus"),this.options.showTick=this.$element[0].classList.contains("show-tick"),this.$newElement=this.createDropdown(),this.$element.after(this.$newElement).prependTo(this.$newElement),this.$button=this.$newElement.children("button"),this.$menu=this.$newElement.children(V.MENU),this.$menuInner=this.$menu.children(".inner"),this.$searchbox=this.$menu.find("input"),this.$element[0].classList.remove("bs-select-hidden"),!0===this.options.dropdownAlignRight&&this.$menu[0].classList.add(j.MENURIGHT),void 0!==e&&this.$button.attr("data-id",e),this.checkDisabled(),this.clickListener(),this.options.liveSearch&&this.liveSearchListener(),this.setStyle(),this.render(),this.setWidth(),this.options.container?this.selectPosition():this.$element.on("hide"+U,function(){if(i.isVirtual()){var e=i.$menuInner[0],t=e.firstChild.cloneNode(!1);e.replaceChild(t,e.firstChild),e.scrollTop=0}}),this.$menu.data("this",this),this.$newElement.data("this",this),this.options.mobile&&this.mobile(),this.$newElement.on({"hide.bs.dropdown":function(e){i.$menuInner.attr("aria-expanded",!1),i.$element.trigger("hide"+U,e)},"hidden.bs.dropdown":function(e){i.$element.trigger("hidden"+U,e)},"show.bs.dropdown":function(e){i.$menuInner.attr("aria-expanded",!0),i.$element.trigger("show"+U,e)},"shown.bs.dropdown":function(e){i.$element.trigger("shown"+U,e)}}),i.$element[0].hasAttribute("required")&&this.$element.on("invalid"+U,function(){i.$button[0].classList.add("bs-invalid"),i.$element.on("shown"+U+".invalid",function(){i.$element.val(i.$element.val()).off("shown"+U+".invalid")}).on("rendered"+U,function(){this.validity.valid&&i.$button[0].classList.remove("bs-invalid"),i.$element.off("rendered"+U)}),i.$button.on("blur"+U,function(){i.$element.trigger("focus").trigger("blur"),i.$button.off("blur"+U)})}),setTimeout(function(){i.createLi(),i.$element.trigger("loaded"+U)})},createDropdown:function(){var e=this.multiple||this.options.showTick?" show-tick":"",t="",i=this.autofocus?" autofocus":"";M.major<4&&this.$element.parent().hasClass("input-group")&&(t=" input-group-btn");var s,n="",o="",l="",r="";return this.options.header&&(n='
    '+this.options.header+"
    "),this.options.liveSearch&&(o=''),this.multiple&&this.options.actionsBox&&(l='
    "),this.multiple&&this.options.doneButton&&(r='
    "),s='",z(s)},setPositionData:function(){this.selectpicker.view.canHighlight=[];for(var e=0;e=this.options.virtualScroll||!0===this.options.virtualScroll},createView:function(T,e){e=e||0;var A=this;this.selectpicker.current=T?this.selectpicker.search:this.selectpicker.main;var N,D,H=[];function i(e,t){var i,s,n,o,l,r,a,c,d,h,p=A.selectpicker.current.elements.length,u=[],f=!0,m=A.isVirtual();A.selectpicker.view.scrollTop=e,!0===m&&A.sizeInfo.hasScrollBar&&A.$menu[0].offsetWidth>A.sizeInfo.totalMenuWidth&&(A.sizeInfo.menuWidth=A.$menu[0].offsetWidth,A.sizeInfo.totalMenuWidth=A.sizeInfo.menuWidth+A.sizeInfo.scrollBarWidth,A.$menu.css("min-width",A.sizeInfo.menuWidth)),i=Math.ceil(A.sizeInfo.menuInnerHeight/A.sizeInfo.liHeight*1.5),s=Math.round(p/i)||1;for(var v=0;vp-1?0:A.selectpicker.current.data[p-1].position-A.selectpicker.current.data[A.selectpicker.view.position1-1].position,x.firstChild.style.marginTop=b+"px",x.firstChild.style.marginBottom=w+"px"),x.firstChild.appendChild(I)}if(A.prevActiveIndex=A.activeIndex,A.options.liveSearch){if(T&&t){var z,L=0;A.selectpicker.view.canHighlight[L]||(L=1+A.selectpicker.view.canHighlight.slice(1).indexOf(!0)),z=A.selectpicker.view.visibleElements[L],A.selectpicker.view.currentActive&&(A.selectpicker.view.currentActive.classList.remove("active"),A.selectpicker.view.currentActive.firstChild&&A.selectpicker.view.currentActive.firstChild.classList.remove("active")),z&&(z.classList.add("active"),z.firstChild&&z.firstChild.classList.add("active")),A.activeIndex=(A.selectpicker.current.data[L]||{}).index}}else A.$menuInner.trigger("focus")}this.setPositionData(),i(e,!0),this.$menuInner.off("scroll.createView").on("scroll.createView",function(e,t){A.noScroll||i(this.scrollTop,t),A.noScroll=!1}),z(window).off("resize"+U+"."+this.selectId+".createView").on("resize"+U+"."+this.selectId+".createView",function(){A.$newElement.hasClass(j.SHOW)&&i(A.$menuInner[0].scrollTop)})},setPlaceholder:function(){var e=!1;if(this.options.title&&!this.multiple){this.selectpicker.view.titleOption||(this.selectpicker.view.titleOption=document.createElement("option")),e=!0;var t=this.$element[0],i=!1,s=!this.selectpicker.view.titleOption.parentNode;if(s)this.selectpicker.view.titleOption.className="bs-title-option",this.selectpicker.view.titleOption.value="",i=void 0===z(t.options[t.selectedIndex]).attr("selected")&&void 0===this.$element.data("selected");(s||0!==this.selectpicker.view.titleOption.index)&&t.insertBefore(this.selectpicker.view.titleOption,t.firstChild),i&&(t.selectedIndex=0)}return e},createLi:function(){var a=this,f=this.options.iconBase,m=':not([hidden]):not([data-hidden="true"])',v=[],g=[],c=0,b=0,e=this.setPlaceholder()?1:0;this.options.hideDisabled&&(m+=":not(:disabled)"),!a.options.showTick&&!a.multiple||F.checkMark.parentNode||(F.checkMark.className=f+" "+a.options.tickIcon+" check-mark",F.a.appendChild(F.checkMark));var t=this.$element[0].querySelectorAll("select > *"+m);function w(e){var t=g[g.length-1];t&&"divider"===t.type&&(t.optID||e.optID)||((e=e||{}).type="divider",v.push(G(!1,j.DIVIDER,e.optID?e.optID+"div":void 0)),g.push(e))}function x(e,t){if((t=t||{}).divider="true"===e.getAttribute("data-divider"),t.divider)w({optID:t.optID});else{var i=g.length,s=e.style.cssText,n=s?O(s):"",o=(e.className||"")+(t.optgroupClass||"");t.optID&&(o="opt "+o),t.text=e.textContent,t.content=e.getAttribute("data-content"),t.tokens=e.getAttribute("data-tokens"),t.subtext=e.getAttribute("data-subtext"),t.icon=e.getAttribute("data-icon"),t.iconBase=f;var l=Y(t);v.push(G(K(l,o,n),"",t.optID)),e.liIndex=i,t.display=t.content||t.text,t.type="option",t.index=i,t.option=e,t.disabled=t.disabled||e.disabled,g.push(t);var r=0;t.display&&(r+=t.display.length),t.subtext&&(r+=t.subtext.length),t.icon&&(r+=1),c li")},render:function(){this.setPlaceholder();var e,t,i=this,s=this.$element[0].selectedOptions,n=s.length,o=this.$button[0],l=o.querySelector(".filter-option-inner-inner"),r=document.createTextNode(this.options.multipleSeparator),a=F.fragment.cloneNode(!1),c=!1;if(this.togglePlaceholder(),this.tabIndex(),"static"===this.options.selectedTextFormat)a=Y({text:this.options.title},!0);else if((e=this.multiple&&-1!==this.options.selectedTextFormat.indexOf("count")&&1")).length&&n>t[1]||1===t.length&&2<=n),!1===e){for(var d=0;d option"+f+", optgroup"+f+" option"+f).length,v="function"==typeof this.options.countSelectedText?this.options.countSelectedText(n,m):this.options.countSelectedText;a=Y({text:v.replace("{0}",n.toString()).replace("{1}",m.toString())},!0)}if(null==this.options.title&&(this.options.title=this.$element.attr("title")),a.childNodes.length||(a=Y({text:void 0!==this.options.title?this.options.title:this.options.noneSelectedText},!0)),o.title=a.textContent.replace(/<[^>]*>?/g,"").trim(),this.options.sanitize&&c&&B([a],i.options.whiteList,i.options.sanitizeFn),l.innerHTML="",l.appendChild(a),M.major<4&&this.$newElement[0].classList.contains("bs3-has-addon")){var g=o.querySelector(".filter-expand"),b=l.cloneNode(!0);b.className="filter-expand",g?o.replaceChild(b,g):o.appendChild(b)}this.$element.trigger("rendered"+U)},setStyle:function(e,t){var i,s=this.$button[0],n=this.$newElement[0],o=this.options.style.trim();this.$element.attr("class")&&this.$newElement.addClass(this.$element.attr("class").replace(/selectpicker|mobile-device|bs-select-hidden|validate\[.*\]/gi,"")),M.major<4&&(n.classList.add("bs3"),n.parentNode.classList.contains("input-group")&&(n.previousElementSibling||n.nextElementSibling)&&(n.previousElementSibling||n.nextElementSibling).classList.contains("input-group-addon")&&n.classList.add("bs3-has-addon")),i=e?e.trim():o,"add"==t?i&&s.classList.add.apply(s.classList,i.split(" ")):"remove"==t?i&&s.classList.remove.apply(s.classList,i.split(" ")):(o&&s.classList.remove.apply(s.classList,o.split(" ")),i&&s.classList.add.apply(s.classList,i.split(" ")))},liHeight:function(e){if(e||!1!==this.options.size&&!this.sizeInfo){this.sizeInfo||(this.sizeInfo={});var t=document.createElement("div"),i=document.createElement("div"),s=document.createElement("div"),n=document.createElement("ul"),o=document.createElement("li"),l=document.createElement("li"),r=document.createElement("li"),a=document.createElement("a"),c=document.createElement("span"),d=this.options.header&&0this.sizeInfo.menuExtras.vert&&r+this.sizeInfo.menuExtras.vert+50>this.sizeInfo.selectOffsetBot)),"auto"===this.options.size)n=3this.options.size){for(var g=0;gthis.sizeInfo.selectOffsetRight&&this.sizeInfo.selectOffsetRightthis.sizeInfo.menuInnerHeight&&(this.sizeInfo.hasScrollBar=!0,this.sizeInfo.totalMenuWidth=this.sizeInfo.menuWidth+this.sizeInfo.scrollBarWidth,this.$menu.css("min-width",this.sizeInfo.totalMenuWidth)),this.dropdown&&this.dropdown._popper&&this.dropdown._popper.update()},setSize:function(e){if(this.liHeight(e),this.options.header&&this.$menu.css("padding-top",0),!1!==this.options.size){var t,i=this,s=z(window),n=0;if(this.setMenuSize(),this.options.liveSearch&&this.$searchbox.off("input.setMenuSize propertychange.setMenuSize").on("input.setMenuSize propertychange.setMenuSize",function(){return i.setMenuSize()}),"auto"===this.options.size?s.off("resize"+U+"."+this.selectId+".setMenuSize scroll"+U+"."+this.selectId+".setMenuSize").on("resize"+U+"."+this.selectId+".setMenuSize scroll"+U+"."+this.selectId+".setMenuSize",function(){return i.setMenuSize()}):this.options.size&&"auto"!=this.options.size&&this.selectpicker.current.elements.length>this.options.size&&s.off("resize"+U+"."+this.selectId+".setMenuSize scroll"+U+"."+this.selectId+".setMenuSize"),e)n=this.$menuInner[0].scrollTop;else if(!i.multiple){var o=i.$element[0];"number"==typeof(t=(o.options[o.selectedIndex]||{}).liIndex)&&!1!==i.options.size&&(n=(n=i.sizeInfo.liHeight*t)-i.sizeInfo.menuInnerHeight/2+i.sizeInfo.liHeight/2)}i.createView(!1,n)}},setWidth:function(){var i=this;"auto"===this.options.width?requestAnimationFrame(function(){i.$menu.css("min-width","0"),i.$element.on("loaded"+U,function(){i.liHeight(),i.setMenuSize();var e=i.$newElement.clone().appendTo("body"),t=e.css("width","auto").children("button").outerWidth();e.remove(),i.sizeInfo.selectWidth=Math.max(i.sizeInfo.totalMenuWidth,t),i.$newElement.css("width",i.sizeInfo.selectWidth+"px")})}):"fit"===this.options.width?(this.$menu.css("min-width",""),this.$newElement.css("width","").addClass("fit-width")):this.options.width?(this.$menu.css("min-width",""),this.$newElement.css("width",this.options.width)):(this.$menu.css("min-width",""),this.$newElement.css("width","")),this.$newElement.hasClass("fit-width")&&"fit"!==this.options.width&&this.$newElement[0].classList.remove("fit-width")},selectPosition:function(){this.$bsContainer=z('
    ');var s,n,o,l=this,r=z(this.options.container),e=function(e){var t={},i=l.options.display||!!z.fn.dropdown.Constructor.Default&&z.fn.dropdown.Constructor.Default.display;l.$bsContainer.addClass(e.attr("class").replace(/form-control|fit-width/gi,"")).toggleClass(j.DROPUP,e.hasClass(j.DROPUP)),s=e.offset(),r.is("body")?n={top:0,left:0}:((n=r.offset()).top+=parseInt(r.css("borderTopWidth"))-r.scrollTop(),n.left+=parseInt(r.css("borderLeftWidth"))-r.scrollLeft()),o=e.hasClass(j.DROPUP)?0:e[0].offsetHeight,(M.major<4||"static"===i)&&(t.top=s.top-n.top+o,t.left=s.left-n.left),t.width=e[0].offsetWidth,l.$bsContainer.css(t)};this.$button.on("click.bs.dropdown.data-api",function(){l.isDisabled()||(e(l.$newElement),l.$bsContainer.appendTo(l.options.container).toggleClass(j.SHOW,!l.$button.hasClass(j.SHOW)).append(l.$menu))}),z(window).off("resize"+U+"."+this.selectId+" scroll"+U+"."+this.selectId).on("resize"+U+"."+this.selectId+" scroll"+U+"."+this.selectId,function(){l.$newElement.hasClass(j.SHOW)&&e(l.$newElement)}),this.$element.on("hide"+U,function(){l.$menu.data("height",l.$menu.height()),l.$bsContainer.detach()})},setOptionStatus:function(){var e=this;if(e.noScroll=!1,e.selectpicker.view.visibleElements&&e.selectpicker.view.visibleElements.length)for(var t=0;t
    ');I[2]&&(k=k.replace("{var}",I[2][1"+k+"
    ")),a=!1,S.$element.trigger("maxReached"+U)),v&&b&&(y.append(z("
    "+$+"
    ")),a=!1,S.$element.trigger("maxReachedGrp"+U)),setTimeout(function(){S.setSelected(o,!1)},10),y.delay(750).fadeOut(300,function(){z(this).remove()})}}}else c.prop("selected",!1),d.selected=!0,S.setSelected(o,!0);!S.multiple||S.multiple&&1===S.options.maxOptions?S.$button.trigger("focus"):S.options.liveSearch&&S.$searchbox.trigger("focus"),a&&(l!=E(S.$element[0])&&S.multiple||r!=S.$element.prop("selectedIndex")&&!S.multiple)&&(C=[d.index,h.prop("selected"),l],S.$element.triggerNative("change"))}}),this.$menu.on("click","li."+j.DISABLED+" a, ."+j.POPOVERHEADER+", ."+j.POPOVERHEADER+" :not(.close)",function(e){e.currentTarget==this&&(e.preventDefault(),e.stopPropagation(),S.options.liveSearch&&!z(e.target).hasClass("close")?S.$searchbox.trigger("focus"):S.$button.trigger("focus"))}),this.$menuInner.on("click",".divider, .dropdown-header",function(e){e.preventDefault(),e.stopPropagation(),S.options.liveSearch?S.$searchbox.trigger("focus"):S.$button.trigger("focus")}),this.$menu.on("click","."+j.POPOVERHEADER+" .close",function(){S.$button.trigger("click")}),this.$searchbox.on("click",function(e){e.stopPropagation()}),this.$menu.on("click",".actions-btn",function(e){S.options.liveSearch?S.$searchbox.trigger("focus"):S.$button.trigger("focus"),e.preventDefault(),e.stopPropagation(),z(this).hasClass("bs-select-all")?S.selectAll():S.deselectAll()}),this.$element.on("change"+U,function(){S.render(),S.$element.trigger("changed"+U,C),C=null}).on("focus"+U,function(){S.options.mobile||S.$button.trigger("focus")})},liveSearchListener:function(){var u=this,f=document.createElement("li");this.$button.on("click.bs.dropdown.data-api",function(){u.$searchbox.val()&&u.$searchbox.val("")}),this.$searchbox.on("click.bs.dropdown.data-api focus.bs.dropdown.data-api touchend.bs.dropdown.data-api",function(e){e.stopPropagation()}),this.$searchbox.on("input propertychange",function(){var e=u.$searchbox.val();if(u.selectpicker.search.elements=[],u.selectpicker.search.data=[],e){var t=[],i=e.toUpperCase(),s={},n=[],o=u._searchStyle(),l=u.options.liveSearchNormalize;l&&(i=w(i)),u._$lisSelected=u.$menuInner.find(".selected");for(var r=0;r=a.selectpicker.view.canHighlight.length&&(t=0),a.selectpicker.view.canHighlight[t+m]||(t=t+1+a.selectpicker.view.canHighlight.slice(t+m+1).indexOf(!0))),e.preventDefault();var v=m+t;e.which===P?0===m&&t===c.length-1?(a.$menuInner[0].scrollTop=a.$menuInner[0].scrollHeight,v=a.selectpicker.current.elements.length-1):d=(o=(n=a.selectpicker.current.data[v]).position-n.height)u+a.sizeInfo.menuInnerHeight),(s=a.selectpicker.main.elements[g]).classList.add("active"),s.firstChild&&s.firstChild.classList.add("active"),a.activeIndex=w[k],s.firstChild.focus(),d&&(a.$menuInner[0].scrollTop=o),l.trigger("focus")}}i&&(e.which===D&&!a.selectpicker.keydown.keyHistory||e.which===N||e.which===H&&a.options.selectOnTab)&&(e.which!==D&&e.preventDefault(),a.options.liveSearch&&e.which===D||(a.$menuInner.find(".active a").trigger("click",!0),l.trigger("focus"),a.options.liveSearch||(e.preventDefault(),z(document).data("spaceSelect",!0))))}},mobile:function(){this.$element[0].classList.add("mobile-device")},refresh:function(){var e=z.extend({},this.options,this.$element.data());this.options=e,this.checkDisabled(),this.setStyle(),this.render(),this.createLi(),this.setWidth(),this.setSize(!0),this.$element.trigger("refreshed"+U)},hide:function(){this.$newElement.hide()},show:function(){this.$newElement.show()},remove:function(){this.$newElement.remove(),this.$element.remove()},destroy:function(){this.$newElement.before(this.$element).remove(),this.$bsContainer?this.$bsContainer.remove():this.$menu.remove(),this.$element.off(U).removeData("selectpicker").removeClass("bs-select-hidden selectpicker"),z(window).off(U+"."+this.selectId)}};var X=z.fn.selectpicker;z.fn.selectpicker=Q,z.fn.selectpicker.Constructor=J,z.fn.selectpicker.noConflict=function(){return z.fn.selectpicker=X,this},z(document).off("keydown.bs.dropdown.data-api").on("keydown"+U,'.bootstrap-select [data-toggle="dropdown"], .bootstrap-select [role="listbox"], .bootstrap-select .bs-searchbox input',J.prototype.keydown).on("focusin.modal",'.bootstrap-select [data-toggle="dropdown"], .bootstrap-select [role="listbox"], .bootstrap-select .bs-searchbox input',function(e){e.stopPropagation()}),z(window).on("load"+U+".data-api",function(){z(".selectpicker").each(function(){var e=z(this);Q.call(e,e.data())})})}(e)}); +//# sourceMappingURL=bootstrap-select.min.js.map \ No newline at end of file diff --git a/templates/yandexhome/css/bootstrap-select.min.js.map b/templates/yandexhome/css/bootstrap-select.min.js.map new file mode 100644 index 0000000..4b0e4c5 --- /dev/null +++ b/templates/yandexhome/css/bootstrap-select.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../js/bootstrap-select.js"],"names":["$","DISALLOWED_ATTRIBUTES","uriAttrs","DefaultWhitelist","*","a","area","b","br","col","code","div","em","hr","h1","h2","h3","h4","h5","h6","i","img","li","ol","p","pre","s","small","span","sub","sup","strong","u","ul","SAFE_URL_PATTERN","DATA_URL_PATTERN","allowedAttribute","attr","allowedAttributeList","attrName","nodeName","toLowerCase","inArray","Boolean","nodeValue","match","regExp","filter","index","value","RegExp","l","length","sanitizeHtml","unsafeElements","whiteList","sanitizeFn","whitelistKeys","Object","keys","len","elements","querySelectorAll","j","len2","el","elName","indexOf","attributeList","slice","call","attributes","whitelistedAttributes","concat","k","len3","removeAttribute","parentNode","removeChild","document","createElement","view","classListProp","protoProp","elemCtrProto","Element","objCtr","classListGetter","$elem","this","add","classes","Array","prototype","arguments","join","addClass","remove","removeClass","toggle","force","toggleClass","contains","hasClass","defineProperty","classListPropDesc","get","enumerable","configurable","ex","undefined","number","__defineGetter__","window","toString","startsWith","testElement","classList","_add","DOMTokenList","_remove","forEach","bind","_toggle","token","getSelectValues","select","opt","result","options","selectedOptions","multiple","push","text","String","object","$defineProperty","error","search","TypeError","string","stringLength","searchString","searchLength","position","pos","Number","start","Math","min","max","charCodeAt","writable","o","r","hasOwnProperty","HTMLSelectElement","valHooks","useDefault","_set","set","elem","data","apply","changedArguments","EventIsSupported","Event","e","stringSearch","method","normalize","stringTypes","searchSuccess","stringType","replace","normalizeToBase","toUpperCase","toInteger","parseInt","fn","triggerNative","eventName","event","dispatchEvent","bubbles","createEvent","initEvent","fireEvent","createEventObject","eventType","trigger","deburredLetters","À","Á","Â","Ã","Ä","Å","à","á","â","ã","ä","å","Ç","ç","Ð","ð","È","É","Ê","Ë","è","é","ê","ë","Ì","Í","Î","Ï","ì","í","î","ï","Ñ","ñ","Ò","Ó","Ô","Õ","Ö","Ø","ò","ó","ô","õ","ö","ø","Ù","Ú","Û","Ü","ù","ú","û","ü","Ý","ý","ÿ","Æ","æ","Þ","þ","ß","Ā","Ă","Ą","ā","ă","ą","Ć","Ĉ","Ċ","Č","ć","ĉ","ċ","č","Ď","Đ","ď","đ","Ē","Ĕ","Ė","Ę","Ě","ē","ĕ","ė","ę","ě","Ĝ","Ğ","Ġ","Ģ","ĝ","ğ","ġ","ģ","Ĥ","Ħ","ĥ","ħ","Ĩ","Ī","Ĭ","Į","İ","ĩ","ī","ĭ","į","ı","Ĵ","ĵ","Ķ","ķ","ĸ","Ĺ","Ļ","Ľ","Ŀ","Ł","ĺ","ļ","ľ","ŀ","ł","Ń","Ņ","Ň","Ŋ","ń","ņ","ň","ŋ","Ō","Ŏ","Ő","ō","ŏ","ő","Ŕ","Ŗ","Ř","ŕ","ŗ","ř","Ś","Ŝ","Ş","Š","ś","ŝ","ş","š","Ţ","Ť","Ŧ","ţ","ť","ŧ","Ũ","Ū","Ŭ","Ů","Ű","Ų","ũ","ū","ŭ","ů","ű","ų","Ŵ","ŵ","Ŷ","ŷ","Ÿ","Ź","Ż","Ž","ź","ż","ž","IJ","ij","Œ","œ","ʼn","ſ","reLatin","reComboMark","deburrLetter","key","map","escaper","source","testRegexp","replaceRegexp","htmlEscape","&","<",">","\"","'","`","test","keyCodeMap","32","48","49","50","51","52","53","54","55","56","57","59","65","66","67","68","69","70","71","72","73","74","75","76","77","78","79","80","81","82","83","84","85","86","87","88","89","90","96","97","98","99","100","101","102","103","104","105","keyCodes","version","success","major","full","dropdown","Constructor","VERSION","split","err","selectId","EVENT_KEY","classNames","DISABLED","DIVIDER","SHOW","DROPUP","MENU","MENURIGHT","MENULEFT","BUTTONCLASS","POPOVERHEADER","ICONBASE","TICKICON","Selector","elementTemplates","subtext","whitespace","createTextNode","fragment","createDocumentFragment","setAttribute","className","cloneNode","checkMark","REGEXP_ARROW","REGEXP_TAB_OR_ESCAPE","generateOption","content","optgroup","nodeType","appendChild","innerHTML","inline","insertAdjacentHTML","useFragment","subtextElement","iconElement","textElement","textContent","icon","iconBase","childNodes","label","Selectpicker","element","that","$element","$newElement","$button","$menu","selectpicker","main","current","keydown","keyHistory","resetKeyHistory","setTimeout","title","winPad","windowPadding","val","render","refresh","setStyle","selectAll","deselectAll","destroy","show","hide","init","Plugin","option","args","_option","shift","BootstrapVersion","console","warn","toUpdate","DEFAULTS","style","name","tickIcon","chain","each","$this","is","dataAttributes","dataAttr","config","extend","defaults","template","Function","noneSelectedText","noneResultsText","countSelectedText","numSelected","numTotal","maxOptionsText","numAll","numGroup","selectAllText","deselectAllText","doneButton","doneButtonText","multipleSeparator","styleBase","size","selectedTextFormat","width","container","hideDisabled","showSubtext","showIcon","showContent","dropupAuto","header","liveSearch","liveSearchPlaceholder","liveSearchNormalize","liveSearchStyle","actionsBox","showTick","caret","maxOptions","mobile","selectOnTab","dropdownAlignRight","virtualScroll","display","sanitize","constructor","id","prop","autofocus","createDropdown","after","prependTo","children","$menuInner","$searchbox","find","checkDisabled","clickListener","liveSearchListener","setWidth","selectPosition","on","isVirtual","menuInner","emptyMenu","firstChild","replaceChild","scrollTop","hide.bs.dropdown","hidden.bs.dropdown","show.bs.dropdown","shown.bs.dropdown","hasAttribute","off","validity","valid","createLi","inputGroup","parent","drop","searchbox","actionsbox","donebutton","setPositionData","canHighlight","type","height","sizeInfo","dividerHeight","dropdownHeaderHeight","liHeight","disabled","createView","isSearching","selected","prevActive","active","scroll","chunkSize","chunkCount","firstChunk","lastChunk","currentChunk","prevPositions","positionIsDifferent","previousElements","array1","array2","chunks","menuIsDifferent","hasScrollBar","offsetWidth","totalMenuWidth","menuWidth","scrollBarWidth","css","ceil","menuInnerHeight","round","endOfChunk","position0","position1","activeIndex","prevActiveIndex","selectedIndex","visibleElements","setOptionStatus","every","marginTop","marginBottom","menuFragment","toSanitize","visibleElementsLen","elText","elementData","lastChild","sanitized","newActive","currentActive","updateValue","noScroll","setPlaceholder","updateIndex","titleOption","isSelected","titleNotAppended","insertBefore","optionSelector","mainElements","mainData","widestOptionLength","optID","startIndex","selectOptions","addDivider","previousData","addOption","divider","getAttribute","liIndex","cssText","inlineStyle","optionClass","optgroupClass","tokens","combinedLength","widestOption","addOptgroup","previous","next","headerIndex","lastIndex","labelElement","item","tagName","findLis","showCount","countMax","selectedCount","button","buttonInner","querySelector","titleFragment","hasContent","togglePlaceholder","tabIndex","titleOptions","thisData","trim","totalCount","tr8nText","filterExpand","clone","newStyle","status","buttonClass","newElement","previousElementSibling","nextElementSibling","menu","menuInnerInner","dropdownHeader","actions","firstOption","selectWidth","minWidth","input","body","offsetHeight","headerHeight","searchHeight","actionsHeight","doneButtonHeight","outerHeight","menuStyle","getComputedStyle","menuPadding","vert","paddingTop","paddingBottom","borderTopWidth","borderBottomWidth","horiz","paddingLeft","paddingRight","borderLeftWidth","borderRightWidth","menuExtras","marginLeft","marginRight","overflowY","selectHeight","getSelectPosition","containerPos","$window","offset","$container","top","left","selectOffsetTop","selectOffsetBot","selectOffsetLeft","scrollLeft","selectOffsetRight","setMenuSize","isAuto","menuHeight","minHeight","_minHeight","maxHeight","menuInnerMinHeight","estimate","divHeight","divLength","max-height","overflow","min-height","overflow-y","_popper","update","setSize","requestAnimationFrame","$selectClone","appendTo","btnWidth","outerWidth","$bsContainer","actualHeight","getPlacement","containerPosition","Default","isDisabled","append","detach","liData","setDisabled","setSelected","activeIndexIsSet","keepActive","removeAttr","nothingSelected","$document","setFocus","checkPopperExists","state","isCreated","keyCode","preventDefault","_menu","retainActive","clickedData","clickedIndex","prevValue","prevIndex","triggerChange","stopPropagation","$options","$option","$optgroup","$optgroupOptions","maxOptionsGrp","maxReached","maxReachedGrp","maxOptionsArr","maxTxt","maxTxtGrp","$notify","delay","fadeOut","currentTarget","target","noResults","searchValue","searchMatch","q","cache","cacheArr","searchStyle","_searchStyle","normalizeSearch","_$lisSelected","cacheLen","liPrev","changeAll","previousSelected","currentSelected","isActive","liActive","activeLi","isToggle","closest","$items","updateScroll","downOnTab","which","isArrowKey","lastIndexOf","liActiveIndex","scrollHeight","matches","cancel","clearTimeout","charAt","matchIndex","focus","before","removeData","old","noConflict","$selectpicker","jQuery"],"mappings":"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SAAU,IACxDC,SAAU,IAAMC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IACxDC,SAAU,IAAMC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IACvEC,SAAU,IAAMC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IACvEC,SAAU,IAAMC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IACxDC,SAAU,IAAMC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IACxDC,SAAU,IAAMC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IACxDC,SAAU,IAAMC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IACvEC,SAAU,IAAMC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IACvEC,SAAU,IAAMC,SAAU,IAC1BC,SAAU,IAAMC,SAAU,IAAKC,SAAU,IACzCC,SAAU,IAAMC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IACvEC,SAAU,IAAMC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IACvEC,SAAU,IAAMC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IACxDC,SAAU,IAAMC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IACxDC,SAAU,IAAMC,SAAU,IAAKC,SAAU,IACzCC,SAAU,IAAMC,SAAU,IAAKC,SAAU,IACzCC,SAAU,IAAMC,SAAU,IAAKC,SAAU,IACzCC,SAAU,IAAMC,SAAU,IAAKC,SAAU,IACzCC,SAAU,IAAMC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IACxDC,SAAU,IAAMC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IACxDC,SAAU,IAAMC,SAAU,IAAKC,SAAU,IACzCC,SAAU,IAAMC,SAAU,IAAKC,SAAU,IACzCC,SAAU,IAAMC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IACtFC,SAAU,IAAMC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IAAKC,SAAU,IACtFC,SAAU,IAAMC,SAAU,IAC1BC,SAAU,IAAMC,SAAU,IAAKC,SAAU,IACzCC,SAAU,IAAMC,SAAU,IAAKC,SAAU,IACzCC,SAAU,I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","file":"bootstrap-select.min.js"} \ No newline at end of file diff --git a/templates/yandexhome/css/defaults-ru_RU.js b/templates/yandexhome/css/defaults-ru_RU.js new file mode 100644 index 0000000..4aac88a --- /dev/null +++ b/templates/yandexhome/css/defaults-ru_RU.js @@ -0,0 +1,40 @@ +/*! + * Bootstrap-select v1.13.9 (https://developer.snapappointments.com/bootstrap-select) + * + * Copyright 2012-2019 SnapAppointments, LLC + * Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE) + */ + +(function (root, factory) { + if (root === undefined && window !== undefined) root = window; + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module unless amdModuleId is set + define(["jquery"], function (a0) { + return (factory(a0)); + }); + } else if (typeof module === 'object' && module.exports) { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(require("jquery")); + } else { + factory(root["jQuery"]); + } +}(this, function (jQuery) { + +(function ($) { + $.fn.selectpicker.defaults = { + noneSelectedText: 'Ничего не выбрано', + noneResultsText: 'Совпадений не найдено {0}', + countSelectedText: 'Выбрано {0} из {1}', + maxOptionsText: ['Достигнут предел ({n} {var} максимум)', 'Достигнут предел в группе ({n} {var} максимум)', ['шт.', 'шт.']], + doneButtonText: 'Закрыть', + selectAllText: 'Выбрать все', + deselectAllText: 'Отменить все', + multipleSeparator: ', ' + }; +})(jQuery); + + +})); +//# sourceMappingURL=defaults-ru_RU.js.map \ No newline at end of file diff --git a/templates/yandexhome/css/usual.css b/templates/yandexhome/css/usual.css new file mode 100644 index 0000000..7dbc44f --- /dev/null +++ b/templates/yandexhome/css/usual.css @@ -0,0 +1,175 @@ +@charset "utf-8"; +/* CSS Document */ + +/*---------Оформление. Общие стили---------*/ +.support, +.support-two { + display: inline-block; + position: relative; + text-decoration: none; + cursor: pointer; +} + +.tip, +.tip-two, +.tip-three, +span.support::before, +span.support-hover::before { + display: none; + position: absolute; + z-index: 9998; + top: 0; + left: 100%; + background: #EDEDED; + border-radius: 3px; + border: 1px solid #ccc; + box-shadow: 5px 5px 0.5em -0.1em rgba(0,0,6,0.5); + text-align: left; + color: #000; + font: normal 500 14px Arial, sans-serif; + opacity: .9; + cursor: default; + padding: 5px; + margin: -15px 0 0 10px; + width: 400px; + min-height: 50px; + height: auto; +} +.tip-two, +.tip-three { + margin: -30px 0 0 0; +} +.tip:before, +.tip-two:before, +.tip-three:before { + border-width: 9px 8px 9px 0; + border-color: transparent #cccccc transparent transparent; + top: 13px; + left: -9px; +} +.tip:after, +.tip-two:after, +.tip-three:after { + border-width: 7px 7px 7px 0; + border-color: transparent #ededed transparent transparent; + top: 15px; + left: -7px; +} +.tip:before, +.tip-two:before, +.tip-three:before, +.tip:after, +.tip-two:after, +.tip-three:after { + content:""; + position:absolute; + width: 0px; + height: 0px; + border-style: solid; +} +a.support:focus em, +a.support-two:focus em, +#tip-checkbox:checked ~ .support em { + background: #6699FF; +} + +.tip-block { + display: inline-block; + position: relative; + background: transparent; + margin: 0; + padding: 0; + border: 0; + height: 0; + width: 1px; +} + +/*--------------Любой тег, атрибут data-title------------*/ + +span.support::before, +span.support-hover::before { + content: attr(data-title); +} +span.support-hover:hover::before, +span.support:focus::before { + display: block; +} +/*--------------Внутри тега 'A'------------*/ + +a.support:focus .tip { + display:block; +} +a.support[tabindex="1"]:focus { + outline: none; +} +/*---------------Непосредственно после тега 'A'----------------*/ + +a.support-two:focus + .tip-block .tip-two { + display:block; +} +a.support-two[tabindex="1"]:focus { + outline: none; +} + +/*----------Использование тега 'INPUT'--------------*/ + +.tip-checkbox { + display: none; +} + +#tip-checkbox:checked ~ * .tip-three { + display: block; +} + +.tip-close { + display: inline-block; + position: absolute; + background: #333; + border-radius: 20px; + border: 2px solid #FFF; + top: -8px; + right: -8px; + font: normal bold 14px Comic Sans MS; + text-align: center; + color: #fff; + cursor: pointer; + padding: 0 4px; +} + +/*---------При наведении--------------*/ + +.support-hover, +.support-hover-two { + display: inline-block; + position: relative; + color: #06C; + text-decoration: none; + cursor: pointer; +} +.support-hover em, +.support-hover-two em { + background: #FF6600; + border-radius: 20px; + border: 1px solid #999; + text-align: center; + color: #FFF; + font: normal bold 16px Arial; + padding: 0 4px; +} +span.support-hover:hover em, +span.support:hover em, +span.support:focus em, +a.support-hover:hover em, +a.support-hover-two:hover em { + background: #6699FF; +} +/*--------------Внутри тега 'A'------------*/ + +a.support-hover:hover .tip { + display:block; +} + +/*---------------Непосредственно после 'A'----------------*/ +a.support-hover-two:hover + .tip-block .tip-two { + display:block; +} diff --git a/templates/yandexhome/devices_addnew.html b/templates/yandexhome/devices_addnew.html new file mode 100644 index 0000000..af59c89 --- /dev/null +++ b/templates/yandexhome/devices_addnew.html @@ -0,0 +1,64 @@ + + +Новое устройство +
    +
    + [#if OK#] + + [#endif OK#] + [#if ERR#] + + [#endif ERR#] + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    +
    +
    + + <#LANG_STRING_BACK#> + + +
    +
    +
    +
    + + diff --git a/templates/yandexhome/devices_edit.html b/templates/yandexhome/devices_edit.html new file mode 100644 index 0000000..d2b45c5 --- /dev/null +++ b/templates/yandexhome/devices_edit.html @@ -0,0 +1,364 @@ + + + +
    +
    + [#if OK#] + + [#endif OK#] + [#if ERR#] + + [#endif ERR#] + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    +
    + + + + +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + + <#LANG_STRING_BACK#> + + + +
    +
    +
    +
    + +
    + +
    + + diff --git a/templates/yandexhome/devices_list_panels.html b/templates/yandexhome/devices_list_panels.html new file mode 100644 index 0000000..3f4fad0 --- /dev/null +++ b/templates/yandexhome/devices_list_panels.html @@ -0,0 +1,86 @@ + +[#if RESULT#] + + [#if VIEW_STYLE="panels"#] +
    +
    + [#endif#] + [#begin RESULT#] + + [#if VIEW_STYLE="panels_group"#] + [#if NEW_ROOM#] +
    +

    [#ROOM#]

    +
    +
    +
    + [#endif#] + [#endif#] +
    +
    + +
    + +

    [#TITLE#]

    +
    + +
    + Device Logo +
    +
    Описание
    +
    + [#if DESCRIPTION!=""#][#DESCRIPTION#][#else#] [#endif#] +
    +
    Тип
    +

    [#TYPE_TITLE#] ([#ICON#])

    +
    +
    +
    +
    + [#if LAST_DEV==1 AND VIEW_STYLE="panels_group"#] +
    +
    + [#endif#] + [#end RESULT#] + [#if VIEW_STYLE="panels"#] +
    +
    + [#endif#] +[#else RESULT#] + + +[#endif RESULT#] + + diff --git a/templates/yandexhome/devices_list_table.html b/templates/yandexhome/devices_list_table.html new file mode 100644 index 0000000..68ff8c3 --- /dev/null +++ b/templates/yandexhome/devices_list_table.html @@ -0,0 +1,60 @@ + +[#if RESULT#] + + + + + + + + + [#if VIEW_STYLE="table"#][#endif#] + + + + [#begin RESULT#] + + [#if VIEW_STYLE="table_group"#] + [#if NEW_ROOM#] + + [#endif#] + [#endif#] + + + + + + + [#if VIEW_STYLE="table"#] + + [#endif#] + + + [#end RESULT#] + +
     НазваниеТипОписаниеВозможностиМестоположение 
    [#ROOM#]
    + + + [#TITLE#] + + [#TYPE_TITLE#] ([#ICON#]) + + [#DESCRIPTION#] + + [#TRAITS_LIST#] + + [#if ROOM!=""#] + [#ROOM#]
    + [#else#] +   + [#endif#] +
    +
    + + +
    +
    +[#else RESULT#] + + +[#endif RESULT#] diff --git a/templates/yandexhome/img/cooking.kettle.svg b/templates/yandexhome/img/cooking.kettle.svg new file mode 100644 index 0000000..60911fa --- /dev/null +++ b/templates/yandexhome/img/cooking.kettle.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/templates/yandexhome/img/cooking.svg b/templates/yandexhome/img/cooking.svg new file mode 100644 index 0000000..57163f8 --- /dev/null +++ b/templates/yandexhome/img/cooking.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/templates/yandexhome/img/dialog_logo.png b/templates/yandexhome/img/dialog_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..514a6f4bb5c2db1bf7027899084b635a91ca166e GIT binary patch literal 3558 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 F{vRIlBtifH literal 0 HcmV?d00001 diff --git a/templates/yandexhome/img/light.svg b/templates/yandexhome/img/light.svg new file mode 100644 index 0000000..69a467a --- /dev/null +++ b/templates/yandexhome/img/light.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/templates/yandexhome/img/logo.png b/templates/yandexhome/img/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d4369a29587e746d9a2f768f88d25d6694080b7b GIT binary patch literal 5131 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 + + + + + + + + diff --git a/templates/yandexhome/img/media_device.tv.svg b/templates/yandexhome/img/media_device.tv.svg new file mode 100644 index 0000000..a1f3695 --- /dev/null +++ b/templates/yandexhome/img/media_device.tv.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/templates/yandexhome/img/other.svg b/templates/yandexhome/img/other.svg new file mode 100644 index 0000000..664cb08 --- /dev/null +++ b/templates/yandexhome/img/other.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/templates/yandexhome/img/socket.svg b/templates/yandexhome/img/socket.svg new file mode 100644 index 0000000..3e95a40 --- /dev/null +++ b/templates/yandexhome/img/socket.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/templates/yandexhome/img/switch.svg b/templates/yandexhome/img/switch.svg new file mode 100644 index 0000000..1c07335 --- /dev/null +++ b/templates/yandexhome/img/switch.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/templates/yandexhome/img/thermostat.ac.svg b/templates/yandexhome/img/thermostat.ac.svg new file mode 100644 index 0000000..ec4f920 --- /dev/null +++ b/templates/yandexhome/img/thermostat.ac.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/templates/yandexhome/img/thermostat.svg b/templates/yandexhome/img/thermostat.svg new file mode 100644 index 0000000..5d161f2 --- /dev/null +++ b/templates/yandexhome/img/thermostat.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/templates/yandexhome/settings.html b/templates/yandexhome/settings.html new file mode 100644 index 0000000..cf2e73b --- /dev/null +++ b/templates/yandexhome/settings.html @@ -0,0 +1,141 @@ + +
    +   +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    +
    +
    +
    + Отладка DebMes +
    +
    +
    +
    + Включать в лог ping-запросы +
    +
    +
    + + +
    +
    +
    +
    + + + + + diff --git a/templates/yandexhome/yandexhome.html b/templates/yandexhome/yandexhome.html new file mode 100644 index 0000000..d134cec --- /dev/null +++ b/templates/yandexhome/yandexhome.html @@ -0,0 +1,7 @@ + +[#if ACTION=""#] + [#inc action_usual.html#] +[#endif#] +[#if ACTION="admin"#] + [#inc action_admin.html#] +[#endif#]