keycloak-aplcache
Changes
themes/pom.xml 56(+56 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/account-service/account.service.ts 110(+110 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/assets/img/favicon.ico 0(+0 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/assets/img/keycloak-logo.png 0(+0 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/account-page/account-page.component.css 0(+0 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/account-page/account-page.component.html 67(+67 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/account-page/account-page.component.ts 67(+67 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/app-card.ts 61(+61 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/application.ts 77(+77 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/applications-page.component.css 17(+17 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/applications-page.component.html 54(+54 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/applications-page.component.ts 95(+95 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/large-app-card.component.css 0(+0 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/large-app-card.component.html 42(+42 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/large-app-card.component.ts 36(+36 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/row-app-card.component.css 0(+0 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/row-app-card.component.html 52(+52 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/row-app-card.component.ts 36(+36 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/small-app-card.component.css 0(+0 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/small-app-card.component.html 41(+41 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/small-app-card.component.ts 36(+36 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/authenticator-page/authenticator-page.component.css 0(+0 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/authenticator-page/authenticator-page.component.html 46(+46 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/authenticator-page/authenticator-page.component.ts 32(+32 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/page-not-found/page-not-found.component.css 0(+0 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/page-not-found/page-not-found.component.html 3(+3 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/page-not-found/page-not-found.component.ts 32(+32 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/password-page/password-page.component.css 0(+0 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/password-page/password-page.component.html 55(+55 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/password-page/password-page.component.ts 48(+48 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/large-session-card.component.css 0(+0 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/large-session-card.component.html 27(+27 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/large-session-card.component.ts 34(+34 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/row-session-card.component.css 0(+0 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/row-session-card.component.html 32(+32 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/row-session-card.component.ts 35(+35 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/session.ts 47(+47 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/sessions-page.component.css 0(+0 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/sessions-page.component.html 60(+60 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/sessions-page.component.ts 115(+115 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/small-session-card.component.css 0(+0 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/small-session-card.component.html 22(+22 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/small-session-card.component.ts 34(+34 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/action.button.ts 32(+32 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/property.label.ts 28(+28 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/refresh.button.ts 49(+49 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/toolbar.component.ts 100(+100 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/toolbar.css 0(+0 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/toolbar.html 137(+137 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/.gitignore 1(+1 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.d.ts 478(+478 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.http.ts 65(+65 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.js 1277(+1277 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.service.ts 96(+96 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/ngx-translate/declared.var.translate.loader.ts 34(+34 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/ngx-translate/translate.util.ts 40(+40 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/page/side-nav-item.ts 33(+33 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/pipes/filterby.pipe.ts 52(+52 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/pipes/orderby.pipe.ts 42(+42 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/responsiveness-service/media.ts 47(+47 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/responsiveness-service/responsiveness.service.ts 105(+105 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/side-nav/side-nav.component.css 0(+0 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/side-nav/side-nav.component.html 71(+71 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/side-nav/side-nav.component.ts 109(+109 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/notification.component.css 0(+0 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/notification.component.html 12(+12 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/notification.component.ts 43(+43 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/toast.notifier.ts 75(+75 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/top-nav.component.css 0(+0 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/top-nav.component.html 47(+47 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/top-nav.component.ts 53(+53 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/environments/environment.prod.ts 3(+3 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/environments/environment.ts 8(+8 -0)
themes/src/main/resources/theme/keycloak-preview/account/resources/systemjs.config.extras.js 11(+11 -0)
Details
themes/pom.xml 56(+56 -0)
diff --git a/themes/pom.xml b/themes/pom.xml
index a0a5670..b7dfbfd 100755
--- a/themes/pom.xml
+++ b/themes/pom.xml
@@ -49,6 +49,62 @@
</resources>
</build>
</profile>
+
+ <profile>
+ <id>account2</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>com.github.eirslett</groupId>
+ <artifactId>frontend-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>yarn install keycloak preview</id>
+ <goals>
+ <goal>yarn</goal>
+ </goals>
+ <configuration>
+ <arguments>install --production=false --frozen-lockfile</arguments>
+ <workingDirectory>src/main/resources/theme/keycloak-preview/account/resources</workingDirectory>
+ </configuration>
+ </execution>
+ <execution>
+ <id>compile typescript</id>
+ <goals>
+ <goal>yarn</goal>
+ </goals>
+ <phase>compile</phase>
+ <configuration>
+ <arguments>run build</arguments>
+ <workingDirectory>src/main/resources/theme/keycloak-preview/account/resources</workingDirectory>
+ </configuration>
+ </execution>
+ <execution>
+ <id>yarn remove dev dependencies production=true</id>
+ <goals>
+ <goal>yarn</goal>
+ </goals>
+ <phase>process-classes</phase>
+ <configuration>
+ <arguments>install --production=true --frozen-lockfile</arguments>
+ <workingDirectory>src/main/resources/theme/keycloak-preview/account/resources</workingDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-clean-plugin</artifactId>
+ <configuration>
+ <filesets>
+ <fileset>
+ <directory>src/main/resources/theme/keycloak-preview/account/resources/node_modules</directory>
+ </fileset>
+ </filesets>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
</profiles>
<build>
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/index.ftl b/themes/src/main/resources/theme/keycloak-preview/account/index.ftl
new file mode 100644
index 0000000..e000b2f
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/index.ftl
@@ -0,0 +1,120 @@
+<!DOCTYPE html>
+<html class="layout-pf-alt layout-pf-alt-fixed">
+ <head>
+ <title>Keycloak Account</title>
+
+ <script>
+ var authUrl = '${authUrl}';
+ var baseUrl = '${baseUrl}';
+ var realm = '${realm}';
+ var resourceUrl = '${resourceUrl}';
+
+ <#if referrer??>
+ var referrer = '${referrer}';
+ var referrer_uri = '${referrer_uri?html}';
+ </#if>
+
+ <#if msg??>
+ var locale = '${locale}';
+ var l18n_msg = JSON.parse('${msg}');
+ </#if>
+ </script>
+
+ <base href="${baseUrl}/">
+
+ <meta charset="UTF-8">
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <meta name="robots" content="noindex, nofollow">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+
+ <link rel="icon" href="${resourceUrl}/app/assets/img/favicon.ico" type="image/x-icon"/>
+
+ <#if properties.styles?has_content>
+ <#list properties.styles?split(' ') as style>
+ <link href="${resourceUrl}/${style}" rel="stylesheet"/>
+ </#list>
+ </#if>
+
+ <link rel="stylesheet" href="${resourceUrl}/styles.css">
+
+ <!--<script src="${authUrl}/js/${resourceVersion}/keycloak.js" type="text/javascript"></script>-->
+
+ <!-- PatternFly -->
+ <!-- iPad retina icon -->
+ <link rel="apple-touch-icon-precomposed" sizes="152x152"
+ href="${resourceUrl}/node_modules/patternfly/dist/img/apple-touch-icon-precomposed-152.png">
+ <!-- iPad retina icon (iOS < 7) -->
+ <link rel="apple-touch-icon-precomposed" sizes="144x144"
+ href="${resourceUrl}/node_modules/patternfly/dist/img/apple-touch-icon-precomposed-144.png">
+ <!-- iPad non-retina icon -->
+ <link rel="apple-touch-icon-precomposed" sizes="76x76"
+ href="${resourceUrl}/node_modules/patternfly/dist/img/apple-touch-icon-precomposed-76.png">
+ <!-- iPad non-retina icon (iOS < 7) -->
+ <link rel="apple-touch-icon-precomposed" sizes="72x72"
+ href="${resourceUrl}/node_modules/patternfly/dist/img/apple-touch-icon-precomposed-72.png">
+ <!-- iPhone 6 Plus icon -->
+ <link rel="apple-touch-icon-precomposed" sizes="120x120"
+ href="${resourceUrl}/node_modules/patternfly/dist/img/apple-touch-icon-precomposed-180.png">
+ <!-- iPhone retina icon (iOS < 7) -->
+ <link rel="apple-touch-icon-precomposed" sizes="114x114"
+ href="${resourceUrl}/node_modules/patternfly/dist/img/apple-touch-icon-precomposed-114.png">
+ <!-- iPhone non-retina icon (iOS < 7) -->
+ <link rel="apple-touch-icon-precomposed" sizes="57x57"
+ href="${resourceUrl}/node_modules/patternfly/dist/img/apple-touch-icon-precomposed-57.png">
+ <link href="${resourceUrl}/node_modules/patternfly/dist/css/patternfly.min.css" rel="stylesheet"
+ media="screen, print">
+ <link href="${resourceUrl}/node_modules/patternfly/dist/css/patternfly-additions.min.css" rel="stylesheet"
+ media="screen, print">
+ <script src="${resourceUrl}/node_modules/jquery/dist/jquery.min.js"></script>
+ <script src="${resourceUrl}/node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
+ <script src="${resourceUrl}/node_modules/jquery-match-height/dist/jquery.matchHeight-min.js"></script>
+ <script src="${resourceUrl}/node_modules/patternfly/dist/js/patternfly.min.js"></script>
+
+ <!-- Polyfill(s) for older browsers -->
+ <script src="${resourceUrl}/node_modules/core-js/client/shim.min.js"></script>
+
+ <#if properties.scripts?has_content>
+ <#list properties.scripts?split(' ') as script>
+ <script type="text/javascript" src="${resourceUrl}/${script}"></script>
+ </#list>
+ </#if>
+
+ <script src="${resourceUrl}/node_modules/zone.js/dist/zone.js"></script>
+ <script src="${resourceUrl}/node_modules/systemjs/dist/system.src.js"></script>
+
+ <script src="${resourceUrl}/systemjs.config.js"></script>
+ <script>
+ System.import('${resourceUrl}/main.js').catch(function (err) {
+ console.error(err);
+ });
+ </script>
+ </head>
+
+ <app-root>
+ <style>
+ .kc-background {
+ background: url('${resourceUrl}/img/keycloak-bg.png') top left no-repeat;
+ background-size: cover;
+ }
+
+ .logo-centered {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ }
+
+ .kc-logo-text {
+ background-image: url("${resourceUrl}/img/keycloak-logo-text.png");
+ background-repeat: no-repeat;
+ width: 250px;
+ height: 38px;
+ }
+ </style>
+
+ <body class="cards-pf kc-background">
+ <div class='logo-centered kc-logo-text'/>
+ </body>
+ </app-root>
+
+</html>
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/.gitignore b/themes/src/main/resources/theme/keycloak-preview/account/resources/.gitignore
new file mode 100644
index 0000000..53b5bc9
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/.gitignore
@@ -0,0 +1,19 @@
+# ignore typescript-generated files
+*.js
+*.js.map
+
+# ignore keycloak.json - we won't need this much longer
+keycloak.json
+
+# ignore log files
+*.log
+
+# ignore libraries (for now?)
+node_modules
+node
+
+# Don't ignore these
+!keycloak.js
+!systemjs-angular-loader.js
+!systemjs.config.extras.js
+!systemjs.config.js
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/account-service/account.service.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/account-service/account.service.ts
new file mode 100644
index 0000000..860408d
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/account-service/account.service.ts
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2017 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+import {Injectable} from '@angular/core';
+import {Http, Response, RequestOptionsArgs} from '@angular/http';
+
+import {ToastNotifier, ToastNotification} from '../top-nav/toast.notifier';
+import {KeycloakService} from '../keycloak-service/keycloak.service';
+
+ /**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2017 Red Hat Inc.
+ */
+@Injectable()
+export class AccountServiceClient {
+
+ private accountUrl: string;
+
+ constructor(protected http: Http,
+ protected kcSvc: KeycloakService,
+ protected notifier: ToastNotifier) {
+ this.accountUrl = kcSvc.authServerUrl() + 'realms/' + kcSvc.realm() + '/account';
+ }
+
+ public doGetRequest(endpoint: string,
+ responseHandler: Function,
+ options?: RequestOptionsArgs) {
+ this.http.get(this.accountUrl + endpoint, options)
+ .subscribe((res: Response) => responseHandler(res),
+ (error: Response) => this.handleServiceError(error));
+ }
+
+ public doPostRequest(endpoint: string,
+ responseHandler: Function,
+ options?: RequestOptionsArgs,
+ successMessage?: string) {
+ this.http.post(this.accountUrl + endpoint, options)
+ .subscribe((res: Response) => this.handleAccountUpdated(responseHandler, res, successMessage),
+ (error: Response) => this.handleServiceError(error));
+ }
+
+ private handleAccountUpdated(responseHandler: Function, res: Response, successMessage?: string) {
+ let message: string = "Your account has been updated.";
+ if (successMessage) message = successMessage;
+ this.notifier.emit(new ToastNotification(message, "success"));
+ responseHandler(res);
+ }
+
+ public doDelete(endpoint: string,
+ responseHandler: Function,
+ options?: RequestOptionsArgs,
+ successMessage?: string) {
+ this.http.delete(this.accountUrl + endpoint, options)
+ .subscribe((res: Response) => this.handleAccountUpdated(responseHandler, res, successMessage),
+ (error: Response) => this.handleServiceError(error));
+ }
+
+ private handleServiceError(response: Response): void {
+ console.log('**** ERROR!!!! ***');
+ console.log(JSON.stringify(response));
+ console.log("response.status=" + response.status);
+ console.log('***************************************')
+
+ if ((response.status === undefined) || (response.status === 401)) {
+ this.kcSvc.logout();
+ return;
+ }
+
+ if (response.status === 403) {
+ // TODO: go to a forbidden page?
+ }
+
+ if (response.status === 404) {
+ // TODO: route to PageNotFoundComponent
+ }
+
+ let message: string = response.status + " " + response.statusText;
+
+ const not500Error: boolean = response.status !== 500;
+ console.log('not500Error=' + not500Error);
+
+ // Unfortunately, errors can be sent back in the response body as
+ // 'errorMessage' or 'error_description'
+ if (not500Error && response.json().hasOwnProperty('errorMessage')) {
+ message = response.json().errorMessage;
+ }
+
+ if (not500Error && response.json().hasOwnProperty('error_description')) {
+ message = response.json().error_description;
+ }
+
+ this.notifier.emit(new ToastNotification(message, "error"));
+ }
+}
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.component.css b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.component.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.component.css
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.component.html b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.component.html
new file mode 100644
index 0000000..2a65d8b
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.component.html
@@ -0,0 +1,9 @@
+<app-top-nav></app-top-nav>
+<app-side-nav></app-side-nav>
+
+
+<div class="container-fluid container-pf-alt-nav-pf-vertical-alt {{this.contentWidthClass}}"> <!-- collapsed-nav hidden-nav -->
+
+ <router-outlet></router-outlet>
+
+</div>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.component.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.component.ts
new file mode 100644
index 0000000..04b2c2e
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.component.ts
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {Component, HostListener} from '@angular/core';
+
+import {TranslateService} from '@ngx-translate/core';
+
+import {ResponsivenessService, ContentWidthClass, MenuClickListener} from "./responsiveness-service/responsiveness.service";
+
+declare const locale: string;
+
+@Component({
+ selector: 'app-root',
+ templateUrl: './app.component.html',
+ styleUrls: ['./app.component.css']
+})
+export class AppComponent implements MenuClickListener {
+
+ private contentWidthClass: ContentWidthClass = this.respSvc.calcSideContentWidthClass();
+
+ constructor(translate: TranslateService, private respSvc: ResponsivenessService) {
+ // this language will be used as a fallback when a translation isn't found in the current language
+ translate.setDefaultLang('en');
+
+ // the lang to use, if the lang isn't available, it will use the current loader to get them
+ translate.use(locale);
+
+ this.respSvc.addMenuClickListener(this);
+ }
+
+ public menuClicked() : void {
+ this.contentWidthClass = this.respSvc.calcSideContentWidthClass();
+ }
+
+ @HostListener('window:resize', ['$event'])
+ private onResize(event: any) {
+ this.contentWidthClass = this.respSvc.calcSideContentWidthClass();
+ }
+
+}
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.module.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.module.ts
new file mode 100644
index 0000000..6fdd6db
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.module.ts
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { BrowserModule } from '@angular/platform-browser';
+import { NgModule } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { HttpModule } from '@angular/http';
+import { RouterModule, Routes } from '@angular/router';
+import { LocationStrategy, HashLocationStrategy } from '@angular/common';
+
+import { TranslateLoader } from '@ngx-translate/core';
+import { TranslateModule } from '@ngx-translate/core';
+
+import { KeycloakService } from './keycloak-service/keycloak.service';
+import { KEYCLOAK_HTTP_PROVIDER } from './keycloak-service/keycloak.http';
+
+import {ResponsivenessService} from './responsiveness-service/responsiveness.service'
+
+import { AccountServiceClient } from './account-service/account.service';
+import {TranslateUtil} from './ngx-translate/translate.util';
+
+import { DeclaredVarTranslateLoader } from './ngx-translate/declared.var.translate.loader';
+import { AppComponent } from './app.component';
+import { TopNavComponent } from './top-nav/top-nav.component';
+import { NotificationComponent } from './top-nav/notification.component';
+import { ToastNotifier } from './top-nav/toast.notifier';
+import { SideNavComponent } from './side-nav/side-nav.component';
+import { AccountPageComponent } from './content/account-page/account-page.component';
+import { PasswordPageComponent } from './content/password-page/password-page.component';
+import { PageNotFoundComponent } from './content/page-not-found/page-not-found.component';
+
+import { AuthenticatorPageComponent } from './content/authenticator-page/authenticator-page.component';
+
+import { SessionsPageComponent } from './content/sessions-page/sessions-page.component';
+import { LargeSessionCardComponent } from './content/sessions-page/large-session-card.component';
+import { SmallSessionCardComponent } from './content/sessions-page/small-session-card.component';
+
+import { ApplicationsPageComponent } from './content/applications-page/applications-page.component';
+import { LargeAppCardComponent } from './content/applications-page/large-app-card.component';
+import { SmallAppCardComponent } from './content/applications-page/small-app-card.component';
+import { RowAppCardComponent } from './content/applications-page/row-app-card.component';
+
+import { ToolbarComponent } from './content/widgets/toolbar.component';
+
+import {OrderbyPipe} from './pipes/orderby.pipe';
+import {FilterbyPipe} from './pipes/filterby.pipe';
+
+const routes: Routes = [
+ { path: 'account', component: AccountPageComponent },
+ { path: 'password', component: PasswordPageComponent },
+ { path: 'authenticator', component: AuthenticatorPageComponent },
+ { path: 'sessions', component: SessionsPageComponent },
+ { path: 'applications', component: ApplicationsPageComponent },
+ { path: '', redirectTo: '/account', pathMatch: 'full' },
+ { path: '**', component: PageNotFoundComponent}
+];
+
+const decs = [
+ AppComponent,
+ TopNavComponent,
+ NotificationComponent,
+ SideNavComponent,
+ AccountPageComponent,
+ PasswordPageComponent,
+ PageNotFoundComponent,
+ AuthenticatorPageComponent,
+ SessionsPageComponent,
+ LargeSessionCardComponent,
+ SmallSessionCardComponent,
+ ApplicationsPageComponent,
+ LargeAppCardComponent,
+ SmallAppCardComponent,
+ RowAppCardComponent,
+ ToolbarComponent,
+ OrderbyPipe,
+ FilterbyPipe
+];
+
+export const ORIGINAL_INCOMING_URL: Location = window.location;
+
+@NgModule({
+ declarations: decs,
+ imports: [
+ BrowserModule,
+ FormsModule,
+ HttpModule,
+ TranslateModule.forRoot({
+ loader: {provide: TranslateLoader, useClass: DeclaredVarTranslateLoader}
+ }),
+ RouterModule.forRoot(routes)
+ ],
+ providers: [
+ KeycloakService,
+ KEYCLOAK_HTTP_PROVIDER,
+ ResponsivenessService,
+ AccountServiceClient,
+ TranslateUtil,
+ ToastNotifier,
+ { provide: LocationStrategy, useClass: HashLocationStrategy }
+ ],
+ bootstrap: [AppComponent]
+})
+export class AppModule { }
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/assets/img/favicon.ico b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/assets/img/favicon.ico
new file mode 100644
index 0000000..48188de
Binary files /dev/null and b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/assets/img/favicon.ico differ
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/assets/img/keycloak-logo.png b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/assets/img/keycloak-logo.png
new file mode 100644
index 0000000..ca53f0a
Binary files /dev/null and b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/assets/img/keycloak-logo.png differ
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/account-page/account-page.component.css b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/account-page/account-page.component.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/account-page/account-page.component.css
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/account-page/account-page.component.html b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/account-page/account-page.component.html
new file mode 100644
index 0000000..7d2ffb1
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/account-page/account-page.component.html
@@ -0,0 +1,67 @@
+<div class="page-header">
+ <h1>{{'editAccountHtmlTitle' | translate}}</h1>
+</div>
+
+<div class="col-sm-9 content-area">
+
+ <div class="row">
+ <div class="col-md-10">
+ </div>
+ <div class="col-md-2 subtitle">
+ <span class="subtitle"><span class="required">*</span> {{'requiredFields' | translate}}</span>
+ </div>
+ <hr/>
+ </div>
+
+ <form #formGroup="ngForm" (ngSubmit)="saveAccount()" class="form-horizontal">
+
+ <div class="form-group ">
+ <div class="col-sm-2 col-md-2">
+ <label for="username" class="control-label">{{'username' | translate}}</label>
+ </div>
+
+ <div class="col-sm-10 col-md-10">
+ <input type="text" class="form-control" id="username" name="username" disabled value="{{username}}" >
+ </div>
+ </div>
+
+ <div class="form-group ">
+ <div class="col-sm-2 col-md-2">
+ <label for="email" class="control-label">{{'email' | translate }}</label> <span class="required">*</span>
+ </div>
+
+ <div class="col-sm-10 col-md-10">
+ <input type="email" class="form-control" id="email" name="email" required autofocus="" ngModel type="text">
+ </div>
+ </div>
+
+ <div class="form-group ">
+ <div class="col-sm-2 col-md-2">
+ <label for="firstName" class="control-label">{{'firstName' | translate}}</label> <span class="required">*</span>
+ </div>
+
+ <div class="col-sm-10 col-md-10">
+ <input class="form-control" id="firstName" required name="firstName" ngModel type="text">
+ </div>
+ </div>
+
+ <div class="form-group ">
+ <div class="col-sm-2 col-md-2">
+ <label for="lastName" class="control-label">{{'lastName' | translate}}</label> <span class="required">*</span>
+ </div>
+
+ <div class="col-sm-10 col-md-10">
+ <input class="form-control" id="lastName" required name="lastName" ngModel type="text">
+ </div>
+ </div>
+
+ <div class="form-group">
+ <div id="kc-form-buttons" class="col-md-offset-2 col-md-10 submit">
+ <div class="">
+ <button type="submit" [disabled]="!formGroup.valid || !formGroup.dirty" class="btn btn-primary btn-lg" name="submitAction">{{'doSave' | translate}}</button>
+ </div>
+ </div>
+ </div>
+ </form>
+
+</div>
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/account-page/account-page.component.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/account-page/account-page.component.ts
new file mode 100644
index 0000000..eeae139
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/account-page/account-page.component.ts
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {Component, OnInit, ViewChild} from '@angular/core';
+import {Response} from '@angular/http';
+import {FormGroup} from '@angular/forms';
+
+import {AccountServiceClient} from '../../account-service/account.service';
+
+@Component({
+ selector: 'app-account-page',
+ templateUrl: './account-page.component.html',
+ styleUrls: ['./account-page.component.css']
+})
+export class AccountPageComponent implements OnInit {
+
+ @ViewChild('formGroup') private formGroup: FormGroup;
+
+ // using ordinary variable here
+ // disabled fields not working properly with FormGroup
+ // FormGroup.getRawValue() causes page refresh. Not sure why?
+ private username: string;
+
+ constructor(private accountSvc: AccountServiceClient ) {
+ accountSvc.doGetRequest("/", (res: Response) => this.handleGetResponse(res));
+ }
+
+ public saveAccount() {
+ console.log("posting: " + JSON.stringify(this.formGroup.value));
+ this.accountSvc.doPostRequest("/", (res: Response) => this.handlePostResponse(res), this.formGroup.value);
+ this.formGroup.reset(this.formGroup.value);
+ }
+
+ protected handleGetResponse(res: Response) {
+ const response: any = res.json();
+ this.username = response.username;
+ this.formGroup.reset(response);
+ console.log('**** response from account REST API ***');
+ console.log(JSON.stringify(res));
+ console.log('*** formGroup ***');
+ console.log(JSON.stringify(this.formGroup.value));
+ console.log('***************************************');
+ }
+
+ protected handlePostResponse(res: Response) {
+ console.log('**** response from account POST ***');
+ console.log(JSON.stringify(res));
+ console.log('***************************************');
+ }
+
+ ngOnInit() {
+ }
+
+}
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/app-card.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/app-card.ts
new file mode 100644
index 0000000..9442342
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/app-card.ts
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2017 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+import {Application} from './application';
+
+ /**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2017 Red Hat Inc.
+ */
+export abstract class AppCard {
+
+ abstract app: Application;
+ abstract sessions: any[];
+
+/*
+ protected getName(): string {
+ if (this.app.hasOwnProperty('name')) {
+ return this.translateUtil.translate(this.app.name);
+ }
+
+ return this.app.clientId;
+ }
+
+ protected getDescription (): string {
+ if (!this.app.hasOwnProperty('description')) return null;
+
+ let desc: string = this.app.description;
+
+ if (desc.indexOf("//icon") > -1) {
+ desc = desc.substring(0, desc.indexOf("//icon"));
+ }
+
+ return desc;
+ }*/
+
+ protected isSessionActive() : boolean {
+ for (let session of this.sessions) {
+ for (let client of session.clients) {
+ if (this.app.clientId === client.clientId) return true;
+ }
+ }
+ return false;
+ }
+
+}
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/application.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/application.ts
new file mode 100644
index 0000000..84817e8
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/application.ts
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2017 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+ import {TranslateUtil} from '../../ngx-translate/translate.util';
+
+ /**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2017 Red Hat Inc.
+ */
+export class Application {
+
+ constructor(private app: any, private translateUtil: TranslateUtil ) {
+ this.setIcon();
+ }
+
+ private setIcon(): void {
+ this.app.icon = "pficon-key";
+
+ if (!this.app.hasOwnProperty('description')) {
+ return;
+ }
+
+ let desc: string = this.app.description;
+ const iconIndex: number = desc.indexOf("//icon=");
+ if (iconIndex > -1) {
+ this.app.icon = desc.substring(iconIndex + 7, desc.length);
+ }
+ }
+
+ public get clientId():string {
+ return this.app.name;
+ }
+
+ public get name():string {
+ if (this.app.hasOwnProperty('name')) {
+ return this.translateUtil.translate(this.app.name);
+ }
+
+ return this.app.clientId;
+ }
+
+ public get description(): string {
+ if (!this.app.hasOwnProperty('description')) return null;
+
+ let desc: string = this.app.description;
+
+ if (desc.indexOf("//icon") > -1) {
+ desc = desc.substring(0, desc.indexOf("//icon"));
+ }
+
+ return desc;
+ }
+
+ public get icon(): string {
+ return this.app.icon;
+ }
+
+ public get effectiveUrl(): string {
+ return this.app.effectiveUrl;
+ }
+}
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/applications-page.component.css b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/applications-page.component.css
new file mode 100644
index 0000000..090ab1b
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/applications-page.component.css
@@ -0,0 +1,17 @@
+/*.card-pf-view .card-pf-top-element .app-icon {
+ display: block;
+ font-size: 46px;
+ height: 40px;
+ line-height: 40px;
+ margin: 0 auto;
+ text-align: center;
+ width: 40px;
+}*/
+
+/*
+.col-lg-2 .card-pf-view .card-pf-top-element .card-pf-small-icon-circle {
+ font-size: 23px;
+ height: 54px;
+ line-height: 50px;
+ width: 54px;
+}*/
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/applications-page.component.html b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/applications-page.component.html
new file mode 100644
index 0000000..075d64a
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/applications-page.component.html
@@ -0,0 +1,54 @@
+
+
+<div class="cards-pf">
+ <toolbar #toolbar [filterProps]="propLabels" [sortProps]="propLabels" [actionButtons]="actionButtons"></toolbar>
+ <div *ngIf="toolbar.activeView === 'LargeCards'" class="container-fluid container-cards-pf">
+ <div class="row row-cards-pf">
+ <large-app-card *ngFor="let app of (applications | filterby:toolbar.filterBy.prop:toolbar.filterText | orderby:toolbar.sortBy.prop:toolbar.isSortAscending )"
+ class="col-xs-12 col-sm-6 col-md-4 col-lg-3"
+ [app]="app"
+ [sessions]="sessions">
+ </large-app-card>
+ </div>
+ </div>
+
+ <div *ngIf="toolbar.activeView === 'SmallCards'" class="container-fluid container-cards-pf">
+ <div class="row row-cards-pf">
+ <small-app-card *ngFor="let app of (applications | filterby:toolbar.filterBy.prop:toolbar.filterText | orderby:toolbar.sortBy.prop:toolbar.isSortAscending )"
+ class="col-xs-12 col-sm-3 col-md-2"
+ [app]="app"
+ [sessions]="sessions">
+ </small-app-card>
+ </div>
+ </div>
+
+ <div *ngIf="toolbar.activeView === 'List'" class="container-fluid">
+ <div class="list-group list-view-pf list-view-pf-view">
+ <row-app-card *ngFor="let app of (applications | filterby:toolbar.filterBy.prop:toolbar.filterText | orderby:toolbar.sortBy.prop:toolbar.isSortAscending )"
+ class="list-group-item"
+ [app]="app"
+ [sessions]="sessions">
+ </row-app-card>
+ </div>
+ </div>
+</div>
+
+ <script>
+ $(function () {
+ // matchHeight the contents of each .card-pf and then the .card-pf itself
+ $(".row-cards-pf > [class*='col'] > .card-pf > .card-pf-body").matchHeight();
+ });
+ $(document).ready(function () {
+ // Card Single Select
+ $('.card-pf-view-single-select').click(function () {
+ if ($(this).hasClass('active'))
+ {
+ $(this).removeClass('active');
+ } else
+ {
+ $('.card-pf-view-single-select').removeClass('active');
+ $(this).addClass('active');
+ }
+ });
+ });
+ </script>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/applications-page.component.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/applications-page.component.ts
new file mode 100644
index 0000000..90d528a
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/applications-page.component.ts
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {Component, OnInit} from '@angular/core';
+import {Response} from '@angular/http';
+
+import {TranslateUtil} from '../../ngx-translate/translate.util';
+import {AccountServiceClient} from '../../account-service/account.service';
+
+import {Application} from './application';
+
+import {View} from '../widgets/toolbar.component';
+import {PropertyLabel} from '../widgets/property.label';
+import {ActionButton} from '../widgets/action.button';
+import {RefreshButton, Refreshable} from '../widgets/refresh.button';
+
+declare const resourceUrl: string;
+
+type SelectableProperty = "name" | "description";
+
+@Component({
+ moduleId: module.id, // need this for styleUrls path to work properly with Systemjs
+ selector: 'app-applications-page',
+ templateUrl: 'applications-page.component.html',
+ styleUrls: ['applications-page.component.css']
+})
+export class ApplicationsPageComponent implements Refreshable, OnInit {
+ private activeView: View = "LargeCards";
+
+ private resourceUrl: string = resourceUrl;
+ private applications: Application[] = [];
+ private isSortAscending: boolean = true;
+ private sortBy: SelectableProperty = "name";
+ private filterBy: SelectableProperty = "name";
+ private filterText: string = "";
+
+ private propLabels: PropertyLabel[] = [];
+ private actionButtons: ActionButton[] = [];
+
+ private sessions: any[] = [];
+
+ constructor(accountSvc: AccountServiceClient, private translateUtil: TranslateUtil) {
+ this.initPropLabels();
+ this.actionButtons.push(new RefreshButton(accountSvc,"/applications", this));
+ accountSvc.doGetRequest("/applications", (res: Response) => this.refresh(res));
+ accountSvc.doGetRequest("/sessions", (res: Response) => this.handleGetSessionsResponse(res));
+ }
+
+ private initPropLabels(): void {
+ this.propLabels.push({prop: "name", label: "Name"});
+ this.propLabels.push({prop: "description", label: "Description"});
+ }
+
+ public refresh(res: Response) {
+ console.log('**** response from apps REST API ***');
+ console.log(JSON.stringify(res));
+ console.log('*** apps res.json() ***');
+ console.log(JSON.stringify(res.json().applications));
+ console.log('*************************************');
+
+ const newApps: Application[] = [];
+ for (let app of res.json().applications) {
+ newApps.push(new Application(app, this.translateUtil));
+ }
+
+ // reference must change to trigger pipes
+ this.applications = newApps;
+ }
+
+ private handleGetSessionsResponse(res: Response) {
+ console.log('**** response from sessions REST API ***');
+ console.log(JSON.stringify(res));
+ console.log('*** sessions res.json() ***');
+ console.log(JSON.stringify(res.json()));
+ console.log('***************************************');
+ this.sessions = res.json();
+ }
+
+ ngOnInit() {
+ }
+
+}
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/large-app-card.component.css b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/large-app-card.component.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/large-app-card.component.css
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/large-app-card.component.html b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/large-app-card.component.html
new file mode 100644
index 0000000..862ecb2
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/large-app-card.component.html
@@ -0,0 +1,42 @@
+<div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
+ <div class="card-pf-body">
+ <div class="card-pf-top-element">
+
+ <div class="card-pf-heading-kebab">
+ <div class="dropup pull-right dropdown-kebab-pf">
+ <button class="btn btn-link dropdown-toggle" type="button" id="dropupKebabRight3" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
+ <span class="fa fa-ellipsis-v"></span>
+ </button>
+ <ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropupKebabRight3">
+ <li *ngIf="app.effectiveUrl"><a href="{{app.effectiveUrl}}">Go to Application</a></li>
+ <li><a href="#">Revoke Grant</a></li>
+ <li><a href="#">Logout Session(s)</a></li>
+ <li><a href="#">Something else here</a></li>
+ <li role="separator" class="divider"></li>
+ <li><a href="#">Separated link</a></li>
+ </ul>
+ </div>
+ </div>
+ <div class="text-center">
+ <span *ngIf="app.icon" class="fa {{app.icon}} card-pf-icon-circle"></span>
+ </div>
+ </div>
+ <h2 class="card-pf-title text-center">
+ {{app.name}}
+ </h2>
+ <h4 *ngIf="app.description" class="text-center">
+ {{app.description}}
+ </h4>
+ <div *ngIf="isSessionActive()" class="card-pf-items text-center">
+ <div class="card-pf-item">
+ <span class="pficon pficon-screen"></span>
+ <span class="card-pf-item-text">Session Active</span>
+ <span class="fa fa-check"></span>
+ </div>
+ </div>
+ <p class="card-pf-info text-center"><strong>Created On</strong> 2015-03-01 02:00 AM <br/> Never Expires</p>
+ </div>
+ <div class="card-pf-view-checkbox">
+ <input type="checkbox">
+ </div>
+</div>
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/large-app-card.component.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/large-app-card.component.ts
new file mode 100644
index 0000000..fe06500
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/large-app-card.component.ts
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2017 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+ import {Component, Input} from '@angular/core';
+ import {AppCard} from './app-card';
+ import {Application} from './application';
+
+ /**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2017 Red Hat Inc.
+ */
+ @Component({
+ moduleId: module.id, // need this for styleUrls path to work properly with Systemjs
+ selector: 'large-app-card',
+ templateUrl: 'large-app-card.component.html',
+ styleUrls: ['large-app-card.component.css']
+})
+export class LargeAppCardComponent extends AppCard {
+ @Input() app: Application;
+ @Input() sessions: any[];
+}
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/row-app-card.component.css b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/row-app-card.component.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/row-app-card.component.css
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/row-app-card.component.html b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/row-app-card.component.html
new file mode 100644
index 0000000..0604737
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/row-app-card.component.html
@@ -0,0 +1,52 @@
+<div class="list-view-pf-actions">
+ <button class="btn btn-default">Action</button>
+ <div class="dropdown pull-right dropdown-kebab-pf">
+ <button class="btn btn-link dropdown-toggle" type="button" id="dropdownKebabRight9" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
+ <span class="fa fa-ellipsis-v"></span>
+ </button>
+ <ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownKebabRight9">
+ <li *ngIf="app.effectiveUrl"><a href="{{app.effectiveUrl}}">Go to Application</a></li>
+ <li><a href="#">Revoke Grant</a></li>
+ <li><a href="#">Logout Session(s)</a></li>
+ <li><a href="#">Something else here</a></li>
+ <li role="separator" class="divider"></li>
+ <li><a href="#">Separated link</a></li>
+ </ul>
+ </div>
+
+</div>
+<div class="list-view-pf-main-info">
+ <div class="list-view-pf-left">
+ <span class="fa {{app.icon}} list-view-pf-icon-sm"></span>
+ <!--<img *ngIf="app.iconSrc" type="image/svg+xml" src="{{app.iconSrc}}" alt="" width="auto" height="40px"/>
+ <span *ngIf="app.icon" class="fa {{app.icon}} list-view-pf-icon-sm"></span>-->
+ </div>
+ <div class="list-view-pf-body">
+ <div class="list-view-pf-description">
+ <div class="list-group-item-heading">
+ {{app.name}}
+ </div>
+ <div class="list-group-item-text">
+ {{app.description}}
+ </div>
+ </div>
+ <div class="list-view-pf-additional-info">
+ <div class="list-view-pf-additional-info-item">
+ <span class="pficon pficon-screen"></span>
+ <strong>8</strong> Hosts
+ </div>
+ <div class="list-view-pf-additional-info-item">
+ <span class="pficon pficon-cluster"></span>
+ <strong>6</strong> Clusters
+ </div>
+ <div class="list-view-pf-additional-info-item">
+ <span class="pficon pficon-container-node"></span>
+ <strong>10</strong> Nodes
+ </div>
+ <div class="list-view-pf-additional-info-item">
+ <span class="pficon pficon-image"></span>
+ <strong>8</strong> Images
+ </div>
+ </div>
+ </div>
+</div>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/row-app-card.component.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/row-app-card.component.ts
new file mode 100644
index 0000000..b3cdffe
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/row-app-card.component.ts
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2017 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+ import {Component, Input} from '@angular/core';
+ import {AppCard} from './app-card';
+ import {Application} from './application';
+
+ /**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2017 Red Hat Inc.
+ */
+ @Component({
+ moduleId: module.id, // need this for styleUrls path to work properly with Systemjs
+ selector: 'row-app-card',
+ templateUrl: 'row-app-card.component.html',
+ styleUrls: ['row-app-card.component.css']
+})
+export class RowAppCardComponent extends AppCard {
+ @Input() app: Application;
+ @Input() sessions: any[];
+}
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/small-app-card.component.css b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/small-app-card.component.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/small-app-card.component.css
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/small-app-card.component.html b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/small-app-card.component.html
new file mode 100644
index 0000000..b702cec
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/small-app-card.component.html
@@ -0,0 +1,41 @@
+<div class="card-pf card-pf-view card-pf-view-xs">
+ <div class="card-pf-body">
+ <div class="card-pf-heading-kebab">
+ <div class="dropdown pull-right dropdown-kebab-pf">
+ <button class="btn btn-link dropdown-toggle" type="button" id="dropupKebabRight2" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
+ <span class="fa fa-ellipsis-v"></span>
+ </button>
+ <ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropupKebabRight2">
+ <li *ngIf="app.effectiveUrl"><a href="{{app.effectiveUrl}}">Go to Application</a></li>
+ <li><a href="#">Revoke Grant</a></li>
+ <li><a href="#">Logout Session(s)</a></li>
+ <li><a href="#">Something else here</a></li>
+ <li role="separator" class="divider"></li>
+ <li><a href="#">Separated link</a></li>
+ </ul>
+ </div>
+
+ <h2 class="card-pf-title">
+ <span class="pficon {{app.icon}}"></span> {{app.name}}
+ </h2>
+ </div>
+ <div class="progress-pf-legend">
+ <div class="progress">
+ <div class="progress-bar" role="progressbar" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100" style="width: 25%;" data-toggle="tooltip" title="25% Used">
+ <span class="sr-only">25% Used</span>
+ </div>
+ <div class="progress-bar progress-bar-remaining" role="progressbar" aria-valuenow="75" aria-valuemin="0" aria-valuemax="100" style="width: 75%;" data-toggle="tooltip" title="75% Available">
+ <span class="sr-only">75% Available</span>
+ </div>
+ </div>
+ <script>
+ // Initialize Tooltip
+ jQuery(document).ready(function () {
+ jQuery('[data-toggle="tooltip"]').tooltip();
+ });
+ </script>
+
+ <p><span class="pficon pficon-warning-triangle-o"></span> <strong>10%</strong> in use</p>
+ </div>
+ </div>
+</div>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/small-app-card.component.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/small-app-card.component.ts
new file mode 100644
index 0000000..73d5f65
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/applications-page/small-app-card.component.ts
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2017 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+ import {Component, Input} from '@angular/core';
+ import {AppCard} from './app-card';
+ import {Application} from './application';
+
+ /**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2017 Red Hat Inc.
+ */
+ @Component({
+ moduleId: module.id, // need this for styleUrls path to work properly with Systemjs
+ selector: 'small-app-card',
+ templateUrl: 'small-app-card.component.html',
+ styleUrls: ['small-app-card.component.css']
+})
+export class SmallAppCardComponent extends AppCard {
+ @Input() app: Application;
+ @Input() sessions: any[];
+}
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/authenticator-page/authenticator-page.component.css b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/authenticator-page/authenticator-page.component.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/authenticator-page/authenticator-page.component.css
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/authenticator-page/authenticator-page.component.html b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/authenticator-page/authenticator-page.component.html
new file mode 100644
index 0000000..f490c06
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/authenticator-page/authenticator-page.component.html
@@ -0,0 +1,46 @@
+<div class="page-header">
+ <h1>Authenticator</h1>
+</div>
+
+<div class="col-sm-9 content-area">
+
+ <ol>
+ <li>
+ <p>Install <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> or Google Authenticator on your device. Both applications are available in <a href="https://play.google.com">Google Play</a> and Apple App Store.</p>
+ </li>
+ <li>
+ <p>Open the application and scan the barcode or enter the key.</p>
+ <p><img src="data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAPYAAAD2AQAAAADNaUdlAAACiElEQVR42u2ZTa6jMBCEG7FgyRF8k+RilkDiYuEmHIGlF4ieqm7+3sxodqP2IiwQ+Eskx+4qlx3Rf16bfPmX/1++ikijYyovGbSb0dp9JKMRr1XwQXVesxTpVUuj09LpmpOBKngW9HoUeeO18Kazbkmamvg6LBhVEcUtyUtyZRz9L69eF3n1Y+L8V8Rt/nH7eH1ipJs/6yOQizdgkB+33/QVyA+nwaja+Mqw/M2f4jj6jwlv1eoTv6RVOFG7sByq4KqL7uSzTtrxl+BjqNn9Gt9Yjg7Dv/G0e33CHyn3e3yDORp8/imiwk/iScSWmyr4lmx96fZ+dH+EieM789n/YO59bc1/3B9Zp0net//E8izef1hjMn+0mu2u+gjn8GprxVN50x8//fTQfzAn+vRMDVCNCIoA84/rqo9gvrYL5lo8H9J6fP4f/hPMj/WvHFWZbc3+4e+xPLvXCEfV/YdKv/wnnLdsHZNaPrQkhuiwFKmEA8F1JpUjHyrXZ3E7r4LDa6CfZK22P7EiMBFVwuVwHexPjpU63/4dzkczRM/XZkcmHZ0r4e7akqzXDLFqcpf3vb6Ecgqa/Yeqt+SvpqlqONY6zj93xdf8D/rMZ7Ec14e7TguJth3YoXQ7bqiBr9ywgxemfjxtrARcl39HcztaQHSgicuRrwfuCbQS7hs6UxKPPjj/limGOriVpnrgZ4gQyl26+ZFfI7ldzF+CTYCwKn18X30d/MxfQBNRy+iQU/fjfDWS8/zIzgfPXae1PfQTzO18MHv+x61VPxO566MGPqilVtM3pb081ucaOHq9n/mLi8xz/Q7mx/mlh8Tm1Pytn2hu+uH5KqO12PkClFQarYN//7/68mr5LwnkYjOKw3BlAAAAAElFTkSuQmCC" alt="Figure: Barcode"></p>
+ <p><span class="code">MVFU 4UKB IYZU 2RBZ NJSW 2MTK NQZT QVBZ</span></p>
+ </li>
+ <li>
+ <p>Enter the one-time code provided by the application and click Save to finish the setup.</p>
+ </li>
+ </ol>
+
+ <hr>
+
+ <form action="http://localhost:8180/auth/realms/master/account/totp" class="form-horizontal" method="post">
+ <input id="stateChecker" name="stateChecker" value="7L6A5K0Mghuc4cm2DBF78rIMI5140AnKc01_q3Pj-4o" type="hidden">
+ <div class="form-group">
+ <div class="col-sm-2 col-md-2">
+ <label for="totp" class="control-label">One-time code</label>
+ </div>
+
+ <div class="col-sm-10 col-md-10">
+ <input class="form-control" id="totp" name="totp" autocomplete="off" autofocus="" type="text">
+ <input id="totpSecret" name="totpSecret" value="eKNQAF3MD9jem2jl38T9" type="hidden">
+ </div>
+ </div>
+
+ <div class="form-group">
+ <div id="kc-form-buttons" class="col-md-offset-2 col-md-10 submit">
+ <div class="">
+ <button type="submit" class="btn btn-primary btn-lg" name="submitAction" value="Save">Save</button>
+ <button type="submit" class="btn btn-default btn-lg" name="submitAction" value="Cancel">Cancel</button>
+ </div>
+ </div>
+ </div>
+ </form>
+
+</div>
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/authenticator-page/authenticator-page.component.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/authenticator-page/authenticator-page.component.ts
new file mode 100644
index 0000000..ea74642
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/authenticator-page/authenticator-page.component.ts
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {Component, OnInit} from '@angular/core';
+
+@Component({
+ selector: 'app-authenticator-page',
+ templateUrl: './authenticator-page.component.html',
+ styleUrls: ['./authenticator-page.component.css']
+})
+export class AuthenticatorPageComponent implements OnInit {
+
+ constructor() {
+ }
+
+ ngOnInit() {
+ }
+
+}
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/page-not-found/page-not-found.component.css b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/page-not-found/page-not-found.component.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/page-not-found/page-not-found.component.css
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/page-not-found/page-not-found.component.html b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/page-not-found/page-not-found.component.html
new file mode 100644
index 0000000..192c87e
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/page-not-found/page-not-found.component.html
@@ -0,0 +1,3 @@
+<p>
+ Page Not Found
+</p>
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/page-not-found/page-not-found.component.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/page-not-found/page-not-found.component.ts
new file mode 100644
index 0000000..2df131a
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/page-not-found/page-not-found.component.ts
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {Component, OnInit} from '@angular/core';
+
+@Component({
+ selector: 'app-page-not-found',
+ templateUrl: './page-not-found.component.html',
+ styleUrls: ['./page-not-found.component.css']
+})
+export class PageNotFoundComponent implements OnInit {
+
+ constructor() {
+ }
+
+ ngOnInit() {
+ }
+
+}
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/password-page/password-page.component.css b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/password-page/password-page.component.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/password-page/password-page.component.css
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/password-page/password-page.component.html b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/password-page/password-page.component.html
new file mode 100644
index 0000000..94b6c33
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/password-page/password-page.component.html
@@ -0,0 +1,55 @@
+<div class="page-header">
+ <h1>{{'changePasswordHtmlTitle' | translate}}</h1>
+</div>
+
+<div class="row">
+ <div class="col-md-10">
+ </div>
+ <div class="col-md-2 subtitle">
+ <span class="subtitle">{{'allFieldsRequired' | translate}}</span>
+ </div>
+ <hr/>
+</div>
+
+<form #formGroup="ngForm" (ngSubmit)="changePassword()" class="form-horizontal">
+ <input readonly="" value="this is not a login form" style="display: none;" type="text">
+ <input readonly="" value="this is not a login form" style="display: none;" type="password">
+
+ <div class="form-group">
+ <div class="col-sm-2 col-md-2">
+ <label for="password" class="control-label">{{'password' | translate}}</label>
+ </div>
+
+ <div class="col-sm-10 col-md-10">
+ <input ngModel class="form-control" id="password" name="password" autofocus="" autocomplete="off" type="password">
+ </div>
+ </div>
+
+ <div class="form-group">
+ <div class="col-sm-2 col-md-2">
+ <label for="password-new" class="control-label">{{'passwordNew' | translate}}</label>
+ </div>
+
+ <div class="col-sm-10 col-md-10">
+ <input ngModel class="form-control" id="newPassword" name="newPassword" autocomplete="off" type="password">
+ </div>
+ </div>
+
+ <div class="form-group">
+ <div class="col-sm-2 col-md-2">
+ <label for="password-confirm" class="control-label">{{'passwordConfirm' | translate}}</label>
+ </div>
+
+ <div class="col-sm-10 col-md-10">
+ <input ngModel class="form-control" id="confirmation" name="confirmation" autocomplete="off" type="password">
+ </div>
+ </div>
+
+ <div class="form-group">
+ <div id="kc-form-buttons" class="col-md-offset-2 col-md-10 submit">
+ <div class="">
+ <button type="submit" class="btn btn-primary btn-lg" name="submitAction">{{'doSave' | translate}}</button>
+ </div>
+ </div>
+ </div>
+</form>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/password-page/password-page.component.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/password-page/password-page.component.ts
new file mode 100644
index 0000000..dc9e62c
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/password-page/password-page.component.ts
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {Component, OnInit, ViewChild} from '@angular/core';
+import {Response} from '@angular/http';
+import {FormGroup} from '@angular/forms';
+
+import {AccountServiceClient} from '../../account-service/account.service';
+
+@Component({
+ selector: 'app-password-page',
+ templateUrl: './password-page.component.html',
+ styleUrls: ['./password-page.component.css']
+})
+export class PasswordPageComponent implements OnInit {
+
+ @ViewChild('formGroup') private formGroup: FormGroup;
+
+ constructor(private accountSvc: AccountServiceClient) {
+ }
+
+ public changePassword() {
+ console.log("posting: " + JSON.stringify(this.formGroup.value));
+ this.accountSvc.doPostRequest("/credentials", (res: Response) => this.handlePostResponse(res), this.formGroup.value);
+ }
+
+ protected handlePostResponse(res: Response) {
+ console.log('**** response from account POST ***');
+ console.log(JSON.stringify(res));
+ console.log('***************************************');
+ }
+
+ ngOnInit() {
+ }
+}
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/large-session-card.component.css b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/large-session-card.component.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/large-session-card.component.css
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/large-session-card.component.html b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/large-session-card.component.html
new file mode 100644
index 0000000..496d629
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/large-session-card.component.html
@@ -0,0 +1,27 @@
+<div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
+ <div class="card-pf-body">
+ <div class="card-pf-top-element">
+
+ <div class="card-pf-heading-kebab">
+ <div class="dropup pull-right dropdown-kebab-pf">
+ <button class="btn btn-link dropdown-toggle" type="button" id="dropupKebabRight3" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
+ <span class="fa fa-ellipsis-v"></span>
+ </button>
+ <ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropupKebabRight3">
+ <li><a href="#">Logout Session</a></li>
+ <li><a href="#">View Clients</a></li>
+ </ul>
+ </div>
+ </div>
+ <div class="text-center">
+ <span class="fa fa-clock-o card-pf-icon-circle"></span>
+ </div>
+ </div>
+ <h2 class="card-pf-title text-center">
+ <strong>{{'ip' | translate}}</strong> {{session.ipAddress}}
+ </h2>
+ <p class="card-pf-info text-center"><strong>{{'started' | translate}}</strong> {{session.started * 1000 | date:'medium'}}</p>
+ <p class="card-pf-info text-center"><strong>{{'lastAccess' | translate}}</strong> {{session.lastAccess * 1000 | date:'medium'}}</p>
+ <p class="card-pf-info text-center"><strong>{{'expires' | translate}}</strong> {{session.expires * 1000 | date:'medium'}}</p>
+ </div>
+</div>
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/large-session-card.component.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/large-session-card.component.ts
new file mode 100644
index 0000000..e85c9f3
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/large-session-card.component.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2017 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+ import {Component, Input} from '@angular/core';
+ import {Session} from './session';
+
+ /**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2017 Red Hat Inc.
+ */
+ @Component({
+ moduleId: module.id, // need this for styleUrls path to work properly with Systemjs
+ selector: 'large-session-card',
+ templateUrl: 'large-session-card.component.html',
+ styleUrls: ['large-session-card.component.css']
+})
+export class LargeSessionCardComponent {
+ @Input() session: Session;
+}
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/row-session-card.component.css b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/row-session-card.component.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/row-session-card.component.css
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/row-session-card.component.html b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/row-session-card.component.html
new file mode 100644
index 0000000..b99df3e
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/row-session-card.component.html
@@ -0,0 +1,32 @@
+<div class="col-sm-9 content-area">
+
+ <table class="table table-striped table-bordered">
+ <thead>
+ <tr>
+ <td>{{'ip' | translate}}</td>
+ <td>{{'started' | translate}}</td>
+ <td>{{'lastAccess' | translate}}</td>
+ <td>{{'expires' | translate}}</td>
+ <td>{{'clients' | translate}}</td>
+ </tr>
+ </thead>
+
+ <tbody *ngFor="let session of sessions">
+ <tr>
+ <td>{{session.ipAddress}}</td>
+ <td>{{session.started * 1000 | date:'medium'}}</td>
+ <td>{{session.lastAccess * 1000 | date: 'medium'}}</td>
+ <td>{{session.expires * 1000 | date: 'medium'}}</td>
+ <td>
+ <span *ngFor="let client of session.clients">
+ {{client.clientId}}<br>
+ </span>
+ </td>
+ </tr>
+ </tbody>
+
+ </table>
+
+ <a (click)="logoutAllSessions()" id="logout-all-sessions" href="#/sessions">{{'doLogOutAllSessions' | translate}}</a>
+
+</div>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/row-session-card.component.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/row-session-card.component.ts
new file mode 100644
index 0000000..a2b8caf
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/row-session-card.component.ts
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2017 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+ import {Component, Input} from '@angular/core';
+
+ import {Session} from './session';
+
+ /**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2017 Red Hat Inc.
+ */
+ @Component({
+ moduleId: module.id, // need this for styleUrls path to work properly with Systemjs
+ selector: 'row-session-card',
+ templateUrl: 'row-session-card.component.html',
+ styleUrls: ['row-session-card.component.css']
+})
+export class RowSessionCardComponent {
+ @Input() sessions: Session[];
+}
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/session.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/session.ts
new file mode 100644
index 0000000..22f1263
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/session.ts
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2017 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+ /**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2017 Red Hat Inc.
+ */
+export class Session {
+
+ constructor(private session: any) {}
+
+ get ipAddress(): string {
+ return this.session.ipAddress;
+ }
+
+ get started(): number {
+ return this.session.started;
+ }
+
+ get lastAccess(): number {
+ return this.session.lastAccess;
+ }
+
+ get expires(): number {
+ return this.session.expires;
+ }
+
+ get clients(): string[] {
+ return this.session.clients;
+ }
+}
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/sessions-page.component.css b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/sessions-page.component.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/sessions-page.component.css
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/sessions-page.component.html b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/sessions-page.component.html
new file mode 100644
index 0000000..7860b5c
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/sessions-page.component.html
@@ -0,0 +1,60 @@
+<div class="cards-pf">
+ <toolbar #toolbar [filterProps]="filterLabels" [sortProps]="sortLabels" [actionButtons]="actionButtons"></toolbar>
+
+ <div *ngIf="toolbar.activeView === 'LargeCards'" class="container-fluid container-cards-pf">
+ <div class="row row-cards-pf">
+ <large-session-card *ngFor="let session of (sessions | filterby:toolbar.filterBy.prop:toolbar.filterText | orderby:toolbar.sortBy.prop:toolbar.isSortAscending )"
+ class="col-xs-12 col-sm-6 col-md-4 col-lg-3"
+ [session]="session">
+ </large-session-card>
+ </div>
+ </div>
+
+ <div *ngIf="toolbar.activeView === 'SmallCards'" class="container-fluid container-cards-pf">
+ <div class="row row-cards-pf">
+ <small-session-card *ngFor="let session of (sessions | filterby:toolbar.filterBy.prop:toolbar.filterText | orderby:toolbar.sortBy.prop:toolbar.isSortAscending )"
+ class="col-xs-12 col-sm-3 col-md-2"
+ [session]="session">
+ </small-session-card>
+ </div>
+ </div>
+
+ <div *ngIf="toolbar.activeView === 'List'" class="container-fluid">
+ <div class="page-header">
+ <h1>{{'sessionsHtmlTitle' | translate}}</h1>
+ </div>
+
+ <div class="col-sm-9 content-area">
+
+ <table class="table table-striped table-bordered">
+ <thead>
+ <tr>
+ <td>{{'ip' | translate}}</td>
+ <td>{{'started' | translate}}</td>
+ <td>{{'lastAccess' | translate}}</td>
+ <td>{{'expires' | translate}}</td>
+ <td>{{'clients' | translate}}</td>
+ </tr>
+ </thead>
+
+ <tbody *ngFor="let item of response">
+ <tr>
+ <td>{{item.ipAddress}}</td>
+ <td>{{item.started * 1000 | date:'medium'}}</td>
+ <td>{{item.lastAccess * 1000 | date: 'medium'}}</td>
+ <td>{{item.expires * 1000 | date: 'medium'}}</td>
+ <td>
+ <span *ngFor="let client of item.clients">
+ {{client.clientId}}<br>
+ </span>
+ </td>
+ </tr>
+ </tbody>
+
+ </table>
+
+ <a (click)="logoutAllSessions()" id="logout-all-sessions" href="#/sessions">{{'doLogOutAllSessions' | translate}}</a>
+ </div>
+ </div>
+
+</div>
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/sessions-page.component.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/sessions-page.component.ts
new file mode 100644
index 0000000..95a519a
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/sessions-page.component.ts
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {Component, OnInit} from '@angular/core';
+import {Response} from '@angular/http';
+
+import {AccountServiceClient} from '../../account-service/account.service';
+ import {TranslateUtil} from '../../ngx-translate/translate.util';
+
+import {View} from '../widgets/toolbar.component';
+import {PropertyLabel} from '../widgets/property.label';
+import {ActionButton} from '../widgets/action.button';
+import {RefreshButton, Refreshable} from '../widgets/refresh.button';
+
+import {Session} from './session';
+
+@Component({
+ selector: 'app-sessions-page',
+ templateUrl: './sessions-page.component.html',
+ styleUrls: ['./sessions-page.component.css']
+})
+export class SessionsPageComponent implements Refreshable, OnInit {
+ private filterLabels: PropertyLabel[] = [];
+ private sortLabels: PropertyLabel[] = [];
+
+ private response: any[] = [];
+ private sessions: Session[] = [];
+
+ private actionButtons: ActionButton[] = [];
+
+ constructor(private accountSvc: AccountServiceClient, private translateUtil: TranslateUtil ) {
+ this.initPropLabels();
+ this.actionButtons.push(new LogoutAllButton(accountSvc, translateUtil));
+ this.actionButtons.push(new RefreshButton(accountSvc,"/sessions", this));
+ accountSvc.doGetRequest("/sessions", (res: Response) => this.refresh(res));
+ }
+
+ private initPropLabels(): void {
+ this.filterLabels.push({prop: "ipAddress", label: "IP"});
+
+ this.sortLabels.push({prop: "ipAddress", label: "IP"});
+ this.sortLabels.push({prop: "started", label: "Started"});
+ this.sortLabels.push({prop: "lastAccess", label: "Last Access"});
+ this.sortLabels.push({prop: "expires", label: "Expires"});
+ }
+
+ public refresh(res: Response) {
+ console.log('**** response from account REST API ***');
+ console.log(JSON.stringify(res));
+ console.log('*** res.json() ***');
+ console.log(JSON.stringify(res.json()));
+ console.log('***************************************');
+ this.response = res.json();
+
+ const newSessions: Session[] = [];
+ for (let session of res.json()) {
+ newSessions.push(new Session(session));
+ }
+
+ // reference must change to trigger pipes
+ this.sessions = newSessions;
+ }
+
+ private logoutAllSessions() {
+ this.accountSvc.doDelete("/sessions",
+ (res: Response) => this.handleLogoutResponse(res),
+ {params: {current: true}},
+ "Logging out all sessions.");
+ }
+
+ private handleLogoutResponse(res: Response) {
+ console.log('**** response from account DELETE ***');
+ console.log(JSON.stringify(res));
+ console.log('***************************************');
+ }
+
+ ngOnInit() {
+ }
+
+}
+
+class LogoutAllButton implements ActionButton {
+ public readonly label: string = "Logout All"; //TODO: localize in constructor
+ public readonly tooltip: string;
+
+ constructor(private accountSvc: AccountServiceClient, translateUtil: TranslateUtil ) {
+ this.tooltip = translateUtil.translate('doLogOutAllSessions');
+ }
+
+ performAction(): void {
+ this.accountSvc.doDelete("/sessions",
+ (res: Response) => this.handleLogoutResponse(res),
+ {params: {current: true}},
+ "Logging out all sessions.");
+ }
+
+ private handleLogoutResponse(res: Response) {
+ console.log('**** response from account DELETE ***');
+ console.log(JSON.stringify(res));
+ console.log('***************************************');
+ }
+}
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/small-session-card.component.css b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/small-session-card.component.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/small-session-card.component.css
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/small-session-card.component.html b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/small-session-card.component.html
new file mode 100644
index 0000000..72abfb4
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/small-session-card.component.html
@@ -0,0 +1,22 @@
+<div class="card-pf card-pf-view card-pf-view-xs">
+ <div class="card-pf-body">
+ <div class="card-pf-heading-kebab">
+ <div class="dropdown pull-right dropdown-kebab-pf">
+ <button class="btn btn-link dropdown-toggle" type="button" id="dropupKebabRight2" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
+ <span class="fa fa-ellipsis-v"></span>
+ </button>
+ <ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropupKebabRight2">
+ <li><a href="#">Logout Session</a></li>
+ <li><a href="#">View Clients</a></li>
+ </ul>
+ </div>
+
+ <h2 class="card-pf-title">
+ <span class="fa fa-clock-o"></span> {{session.ipAddress}}
+ </h2>
+ <p class="card-pf-info text-center"><strong>{{'started' | translate}}</strong> {{session.started * 1000 | date:'shortTime'}}</p>
+ <p class="card-pf-info text-center"><strong>{{'lastAccess' | translate}}</strong> {{session.lastAccess * 1000 | date:'shortTime'}}</p>
+ <p class="card-pf-info text-center"><strong>{{'expires' | translate}}</strong> {{session.expires * 1000 | date:'shortTime'}}</p>
+ </div>
+ </div>
+</div>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/small-session-card.component.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/small-session-card.component.ts
new file mode 100644
index 0000000..e58c1d7
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/sessions-page/small-session-card.component.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2017 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+ import {Component, Input} from '@angular/core';
+ import {Session} from './session';
+
+ /**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2017 Red Hat Inc.
+ */
+ @Component({
+ moduleId: module.id, // need this for styleUrls path to work properly with Systemjs
+ selector: 'small-session-card',
+ templateUrl: 'small-session-card.component.html',
+ styleUrls: ['small-session-card.component.css']
+})
+export class SmallSessionCardComponent {
+ @Input() session: Session;
+}
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/action.button.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/action.button.ts
new file mode 100644
index 0000000..d1e1c59
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/action.button.ts
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+import {Icon} from '../../page/icon';
+
+ /**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2017 Red Hat Inc.
+ */
+export interface ActionButton {
+ readonly label: string | Icon;
+ readonly tooptip?: string;
+
+ performAction(): void;
+
+}
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/property.label.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/property.label.ts
new file mode 100644
index 0000000..37d45b6
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/property.label.ts
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2017 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+ /**
+ * Property name and its human-readable, localized display text.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2017 Red Hat Inc.
+ */
+export interface PropertyLabel {
+ readonly prop: string;
+ readonly label: string;
+}
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/refresh.button.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/refresh.button.ts
new file mode 100644
index 0000000..e3c07a3
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/refresh.button.ts
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2017 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+import {Response} from '@angular/http';
+
+import {ActionButton} from './action.button';
+
+import {Icon} from '../../page/icon';
+
+import {AccountServiceClient} from '../../account-service/account.service';
+
+ /**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2017 Red Hat Inc.
+ */
+export class RefreshButton implements ActionButton {
+
+ readonly label:Icon = new Icon('fa', 'refresh');
+ readonly tooltip:string = 'Refresh'; //TODO: localize in constructor
+
+ constructor(private accountSvc: AccountServiceClient,
+ private request: string,
+ private refreshable:Refreshable) {}
+
+ public performAction(): void {
+ this.accountSvc.doGetRequest(this.request, (res: Response) => {
+ this.refreshable.refresh(res);
+ });
+ }
+}
+
+export interface Refreshable {
+ refresh(response:Response): void;
+}
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/toolbar.component.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/toolbar.component.ts
new file mode 100644
index 0000000..b8204ca
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/toolbar.component.ts
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2017 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+ import {Component, Input, OnInit} from '@angular/core';
+
+ import {PropertyLabel} from './property.label';
+ import {ActionButton} from './action.button';
+ import {Icon} from '../../page/icon';
+
+ export type View = "LargeCards" | "SmallCards" | "List";
+
+ /**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2017 Red Hat Inc.
+ */
+ @Component({
+ moduleId: module.id, // need this for styleUrls path to work properly with Systemjs
+ selector: 'toolbar',
+ templateUrl: 'toolbar.html',
+ styleUrls: ['toolbar.css']
+})
+export class ToolbarComponent implements OnInit {
+ @Input() filterProps: PropertyLabel[];
+ @Input() sortProps: PropertyLabel[];
+ @Input() actionButtons: ActionButton[];
+
+ // TODO: localize in constructor
+ readonly sortByTooltip: string = "Sort by...";
+ readonly sortAscendingTooltip: string = "Sort Ascending";
+ readonly sortDescendingTooltip: string = "Sort Descending";
+
+ private isSortAscending: boolean = true;
+ private sortBy: PropertyLabel;
+ private filterBy: PropertyLabel;
+ private filterText: string = "";
+
+ public activeView: View = "LargeCards";
+
+ ngOnInit() {
+ if (this.filterProps && this.filterProps.length > 0) {
+ this.filterBy = this.filterProps[0];
+ }
+
+ if (this.sortProps && this.sortProps.length > 0) {
+ this.sortBy = this.sortProps[0];
+ }
+ }
+
+ private changeView(activeView: View) {
+ this.activeView = activeView;
+ }
+
+ private toggleSort() {
+ this.isSortAscending = !this.isSortAscending;
+ }
+
+ private changeSortByProp(prop: PropertyLabel) {
+ this.sortBy = prop;
+ }
+
+ private changeFilterByProp(prop: PropertyLabel) {
+ this.filterBy = prop;
+ this.filterText = "";
+ }
+
+ private selectedFilterClass(prop: PropertyLabel): string {
+ if (this.filterBy === prop) {
+ return "selected";
+ } else {
+ return "";
+ }
+ }
+
+ private selectedSortByClass(prop: PropertyLabel): string {
+ if (this.sortBy === prop) {
+ return "selected";
+ } else {
+ return "";
+ }
+ }
+
+ isIconButton(button: ActionButton): boolean {
+ return button.label instanceof Icon;
+ }
+}
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/toolbar.css b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/toolbar.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/toolbar.css
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/toolbar.html b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/toolbar.html
new file mode 100644
index 0000000..81c6af0
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/content/widgets/toolbar.html
@@ -0,0 +1,137 @@
+<div class="container-fluid">
+ <div class="row toolbar-pf">
+ <div class="col-sm-12">
+ <div class="toolbar-pf-actions">
+ <div *ngIf="filterProps" class="form-group toolbar-pf-filter">
+ <label class="sr-only" for="filter">{{filterBy.label}}</label>
+ <div class="input-group">
+ <div class="input-group-btn">
+ <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{filterBy.label}} <span class="caret"></span></button>
+ <ul class="dropdown-menu">
+ <li *ngFor="let prop of filterProps" (click)="changeFilterByProp(prop)" class="{{selectedFilterClass(prop)}}"><a>{{prop.label}}</a></li>
+ </ul>
+ </div><!-- /btn-group -->
+ <form #filterForm="ngForm">
+ <input [(ngModel)]="filterText" name="filterText" type="text" class="form-control" id="filter" placeholder="Filter By {{filterBy.label}}...">
+ </form>
+ </div><!-- /input-group -->
+ </div>
+ <div *ngIf="sortProps" class="form-group">
+ <div class="dropdown btn-group">
+ <button type="button"
+ class="btn btn-default dropdown-toggle"
+ data-toggle="tooltip"
+ data-toggle="dropdown"
+ data-placement="top"
+ title="{{sortByTooltip}}"
+ aria-haspopup="true"
+ aria-expanded="false">{{sortBy.label}} <span class="caret"></span></button>
+ <ul class="dropdown-menu">
+ <li *ngFor="let prop of sortProps" (click)="changeSortByProp(prop)" class="{{selectedSortByClass(prop)}}"><a>{{prop.label}}</a></li>
+ </ul>
+ </div>
+ <button (click)="toggleSort()" class="btn btn-link" type="button">
+ <span *ngIf="!isSortAscending"
+ class="fa fa-sort-alpha-asc"
+ data-toggle="toltip"
+ data-placement="top"
+ title="{{sortAscendingTooltip}}"></span>
+ <span *ngIf="isSortAscending"
+ class="fa fa-sort-alpha-desc"
+ data-toggle="toltip"
+ data-placement="top"
+ title="{{sortDescendingTooltip}}"></span>
+ </button>
+ </div>
+ <div *ngIf="actionButtons">
+ <span class="form-group" *ngFor="let action of actionButtons">
+ <button *ngIf="!isIconButton(action)"
+ (click)="action.performAction()"
+ data-toggle="tooltip"
+ data-placement="top"
+ title="{{action.tooltip}}"
+ class="btn btn-default"
+ type="button">{{action.label}}</button>
+ <button *ngIf="isIconButton(action)"
+ (click)="action.performAction()"
+ data-toggle="tooltip"
+ data-placement="top"
+ title="{{action.tooltip}}"
+ class="btn btn-link"
+ type="button">
+ <span class="{{action.label.getClasses()}}"></span>
+ </button>
+ </span>
+ <!--<button class="btn btn-default" type="button">Action</button>
+ <div class="dropdown btn-group dropdown-kebab-pf">
+ <button class="btn btn-link dropdown-toggle" type="button" id="dropdownKebab" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
+ <span class="fa fa-ellipsis-v"></span>
+ </button>
+ <ul class="dropdown-menu " aria-labelledby="dropdownKebab">
+ <li><a href="#">Action</a></li>
+ <li><a href="#">Another action</a></li>
+ <li><a href="#">Something else here</a></li>
+ <li role="separator" class="divider"></li>
+ <li><a href="#">Separated link</a></li>
+ </ul>
+ </div>-->
+
+ </div>
+ <div class="toolbar-pf-action-right">
+ <!--<div class="form-group toolbar-pf-find">
+ <button class="btn btn-link btn-find" type="button">
+ <span class="fa fa-search"></span>
+ </button>
+ <div class="find-pf-dropdown-container">
+ <input type="text" class="form-control" id="find" placeholder="Find By Keyword...">
+ <div class="find-pf-buttons">
+ <span class="find-pf-nums">1 of 3</span>
+ <button class="btn btn-link" type="button">
+ <span class="fa fa-angle-up"></span>
+ </button>
+ <button class="btn btn-link" type="button">
+ <span class="fa fa-angle-down"></span>
+ </button>
+ <button class="btn btn-link btn-find-close" type="button">
+ <span class="pficon pficon-close"></span>
+ </button>
+ </div>
+ </div>
+ </div>-->
+ <div class="form-group toolbar-pf-view-selector">
+ <button (click)="changeView('LargeCards')" class="btn btn-link "><i class="fa fa-th-large"></i></button>
+ <button (click)="changeView('SmallCards')" class="btn btn-link "><i class="fa fa-th"></i></button>
+ <button (click)="changeView('List')" class="btn btn-link "><i class="fa fa-th-list"></i></button>
+ </div>
+ </div>
+ </div>
+ <!--<div class="row toolbar-pf-results">
+ <div class="col-sm-12">
+ <h5>40 Results</h5>
+ <p>Active filters:</p>
+ <ul class="list-inline">
+ <li>
+ <span class="label label-info">
+ Name: nameofthething
+ <a href="#"><span class="pficon pficon-close"></span></a>
+ </span>
+ </li>
+ <li>
+ <span class="label label-info">
+ Name: nameofthething
+ <a href="#"><span class="pficon pficon-close"></span></a>
+ </span>
+ </li>
+ <li>
+ <span class="label label-info">
+ Name: nameofthething
+ <a href="#"><span class="pficon pficon-close"></span></a>
+ </span>
+ </li>
+ </ul>
+ <p><a href="#">Clear All Filters</a></p>
+ </div><!-- /col --
+ </div>--><!-- /row -->
+ </div><!-- /col -->
+ </div><!-- /row -->
+</div><!-- /container -->
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/.gitignore b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/.gitignore
new file mode 100644
index 0000000..8de818a
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/.gitignore
@@ -0,0 +1 @@
+!keycloak.js
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.d.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.d.ts
new file mode 100644
index 0000000..e7e6e38
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.d.ts
@@ -0,0 +1,478 @@
+/*
+ * MIT License
+ *
+ * Copyright 2017 Andy Hanson
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+ * associated documentation files (the "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
+ * following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+ * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+export as namespace Keycloak;
+
+export = Keycloak;
+
+/**
+ * Creates a new Keycloak client instance.
+ * @param config Path to a JSON config file or a plain config object.
+ */
+declare function Keycloak(config?: string|{}): Keycloak.KeycloakInstance;
+
+declare namespace Keycloak {
+ type KeycloakAdapterName = 'cordova'|'default';
+ type KeycloakOnLoad = 'login-required'|'check-sso';
+ type KeycloakResponseMode = 'query'|'fragment';
+ type KeycloakResponseType = 'code'|'id_token token'|'code id_token token';
+ type KeycloakFlow = 'standard'|'implicit'|'hybrid';
+
+ interface KeycloakInitOptions {
+ /**
+ * @private Undocumented.
+ */
+ adapter?: KeycloakAdapterName;
+
+ /**
+ * Specifies an action to do on load.
+ */
+ onLoad?: KeycloakOnLoad;
+
+ /**
+ * Set an initial value for the token.
+ */
+ token?: string;
+
+ /**
+ * Set an initial value for the refresh token.
+ */
+ refreshToken?: string;
+
+ /**
+ * Set an initial value for the id token (only together with `token` or
+ * `refreshToken`).
+ */
+ idToken?: string;
+
+ /**
+ * Set an initial value for skew between local time and Keycloak server in
+ * seconds (only together with `token` or `refreshToken`).
+ */
+ timeSkew?: number;
+
+ /**
+ * Set to enable/disable monitoring login state.
+ * @default true
+ */
+ checkLoginIframe?: boolean;
+
+ /**
+ * Set the interval to check login state (in seconds).
+ * @default 5
+ */
+ checkLoginIframeInterval?: boolean;
+
+ /**
+ * Set the OpenID Connect response mode to send to Keycloak upon login.
+ * @default fragment After successful authentication Keycloak will redirect
+ * to JavaScript application with OpenID Connect parameters
+ * added in URL fragment. This is generally safer and
+ * recommended over query.
+ */
+ responseMode?: KeycloakResponseMode;
+
+ /**
+ * Set the OpenID Connect flow.
+ * @default standard
+ */
+ flow?: KeycloakFlow;
+ }
+
+ interface KeycloakLoginOptions {
+ /**
+ * @private Undocumented.
+ */
+ scope?: string;
+
+ /**
+ * Specifies the uri to redirect to after login.
+ */
+ redirectUri?: string;
+
+ /**
+ * By default the login screen is displayed if the user is not logged into
+ * Keycloak. To only authenticate to the application if the user is already
+ * logged in and not display the login page if the user is not logged in, set
+ * this option to `'none'`. To always require re-authentication and ignore
+ * SSO, set this option to `'login'`.
+ */
+ prompt?: 'none'|'login';
+
+ /**
+ * If value is `'register'` then user is redirected to registration page,
+ * otherwise to login page.
+ */
+ action?: 'register';
+
+ /**
+ * Used just if user is already authenticated. Specifies maximum time since
+ * the authentication of user happened. If user is already authenticated for
+ * longer time than `'maxAge'`, the SSO is ignored and he will need to
+ * authenticate again.
+ */
+ maxAge?: number;
+
+ /**
+ * Used to pre-fill the username/email field on the login form.
+ */
+ loginHint?: string;
+
+ /**
+ * Used to tell Keycloak which IDP the user wants to authenticate with.
+ */
+ idpHint?: string;
+
+ /**
+ * Specifies the desired locale for the UI.
+ */
+ locale?: string;
+ }
+
+ type KeycloakPromiseCallback<T> = (result: T) => void;
+
+ interface KeycloakPromise<TSuccess, TError> {
+ /**
+ * Function to call if the promised action succeeds.
+ */
+ success(callback: KeycloakPromiseCallback<TSuccess>): KeycloakPromise<TSuccess, TError>;
+
+ /**
+ * Function to call if the promised action throws an error.
+ */
+ error(callback: KeycloakPromiseCallback<TError>): KeycloakPromise<TSuccess, TError>;
+ }
+
+ interface KeycloakError {
+ error: string;
+ error_description: string;
+ }
+
+ interface KeycloakAdapter {
+ login(options?: KeycloakLoginOptions): KeycloakPromise<void, void>;
+ logout(options?: any): KeycloakPromise<void, void>;
+ register(options?: KeycloakLoginOptions): KeycloakPromise<void, void>;
+ accountManagement(): KeycloakPromise<void, void>;
+ redirectUri(options: { redirectUri: string; }, encodeHash: boolean): string;
+ }
+
+ interface KeycloakProfile {
+ id?: string;
+ username?: string;
+ email?: string;
+ firstName?: string;
+ lastName?: string;
+ enabled?: boolean;
+ emailVerified?: boolean;
+ totp?: boolean;
+ createdTimestamp?: number;
+ }
+
+ // export interface KeycloakUserInfo {}
+
+ /**
+ * A client for the Keycloak authentication server.
+ * @see {@link https://keycloak.gitbooks.io/securing-client-applications-guide/content/topics/oidc/javascript-adapter.html|Keycloak JS adapter documentation}
+ */
+ interface KeycloakInstance {
+ /**
+ * Is true if the user is authenticated, false otherwise.
+ */
+ authenticated?: boolean;
+
+ /**
+ * The user id.
+ */
+ subject?: string;
+
+ /**
+ * Response mode passed in init (default value is `'fragment'`).
+ */
+ responseMode?: KeycloakResponseMode;
+
+ /**
+ * Response type sent to Keycloak with login requests. This is determined
+ * based on the flow value used during initialization, but can be overridden
+ * by setting this value.
+ */
+ responseType?: KeycloakResponseType;
+
+ /**
+ * Flow passed in init.
+ */
+ flow?: KeycloakFlow;
+
+ /**
+ * The realm roles associated with the token.
+ */
+ realmAccess?: { roles: string[] };
+
+ /**
+ * The resource roles associated with the token.
+ */
+ resourceAccess?: string[];
+
+ /**
+ * The base64 encoded token that can be sent in the Authorization header in
+ * requests to services.
+ */
+ token?: string;
+
+ /**
+ * The parsed token as a JavaScript object.
+ */
+ tokenParsed?: {
+ exp?: number;
+ iat?: number;
+ nonce?: string;
+ sub?: string;
+ session_state?: string;
+ realm_access?: { roles: string[] };
+ resource_access?: string[];
+ };
+
+ /**
+ * The base64 encoded refresh token that can be used to retrieve a new token.
+ */
+ refreshToken?: string;
+
+ /**
+ * The parsed refresh token as a JavaScript object.
+ */
+ refreshTokenParsed?: { nonce?: string };
+
+ /**
+ * The base64 encoded ID token.
+ */
+ idToken?: string;
+
+ /**
+ * The parsed id token as a JavaScript object.
+ */
+ idTokenParsed?: { nonce?: string };
+
+ /**
+ * The estimated time difference between the browser time and the Keycloak
+ * server in seconds. This value is just an estimation, but is accurate
+ * enough when determining if a token is expired or not.
+ */
+ timeSkew?: number;
+
+ /**
+ * @private Undocumented.
+ */
+ loginRequired?: boolean;
+
+ /**
+ * @private Undocumented.
+ */
+ authServerUrl?: string;
+
+ /**
+ * @private Undocumented.
+ */
+ realm?: string;
+
+ /**
+ * @private Undocumented.
+ */
+ clientId?: string;
+
+ /**
+ * @private Undocumented.
+ */
+ clientSecret?: string;
+
+ /**
+ * @private Undocumented.
+ */
+ redirectUri?: string;
+
+ /**
+ * @private Undocumented.
+ */
+ sessionId?: string;
+
+ /**
+ * @private Undocumented.
+ */
+ profile?: KeycloakProfile;
+
+ /**
+ * @private Undocumented.
+ */
+ userInfo?: {}; // KeycloakUserInfo;
+
+ /**
+ * Called when the adapter is initialized.
+ */
+ onReady?(authenticated?: boolean): void;
+
+ /**
+ * Called when a user is successfully authenticated.
+ */
+ onAuthSuccess?(): void;
+
+ /**
+ * Called if there was an error during authentication.
+ */
+ onAuthError?(errorData: KeycloakError): void;
+
+ /**
+ * Called when the token is refreshed.
+ */
+ onAuthRefreshSuccess?(): void;
+
+ /**
+ * Called if there was an error while trying to refresh the token.
+ */
+ onAuthRefreshError?(): void;
+
+ /**
+ * Called if the user is logged out (will only be called if the session
+ * status iframe is enabled, or in Cordova mode).
+ */
+ onAuthLogout?(): void;
+
+ /**
+ * Called when the access token is expired. If a refresh token is available
+ * the token can be refreshed with Keycloak#updateToken, or in cases where
+ * it's not (ie. with implicit flow) you can redirect to login screen to
+ * obtain a new access token.
+ */
+ onTokenExpired?(): void;
+
+ /**
+ * Called to initialize the adapter.
+ * @param initOptions Initialization options.
+ * @returns A promise to set functions to be invoked on success or error.
+ */
+ init(initOptions: KeycloakInitOptions): KeycloakPromise<boolean, KeycloakError>;
+
+ /**
+ * Redirects to login form.
+ * @param options Login options.
+ */
+ login(options?: KeycloakLoginOptions): KeycloakPromise<void, void>;
+
+ /**
+ * Redirects to logout.
+ * @param options Logout options.
+ * @param options.redirectUri Specifies the uri to redirect to after logout.
+ */
+ logout(options?: any): KeycloakPromise<void, void>;
+
+ /**
+ * Redirects to registration form.
+ * @param options Supports same options as Keycloak#login but `action` is
+ * set to `'register'`.
+ */
+ register(options?: any): KeycloakPromise<void, void>;
+
+ /**
+ * Redirects to the Account Management Console.
+ */
+ accountManagement(): KeycloakPromise<void, void>;
+
+ /**
+ * Returns the URL to login form.
+ * @param options Supports same options as Keycloak#login.
+ */
+ createLoginUrl(options?: KeycloakLoginOptions): string;
+
+ /**
+ * Returns the URL to logout the user.
+ * @param options Logout options.
+ * @param options.redirectUri Specifies the uri to redirect to after logout.
+ */
+ createLogoutUrl(options?: any): string;
+
+ /**
+ * Returns the URL to registration page.
+ * @param options Supports same options as Keycloak#createLoginUrl but
+ * `action` is set to `'register'`.
+ */
+ createRegisterUrl(options?: KeycloakLoginOptions): string;
+
+ /**
+ * Returns the URL to the Account Management Console.
+ */
+ createAccountUrl(): string;
+
+ /**
+ * Returns true if the token has less than `minValidity` seconds left before
+ * it expires.
+ * @param minValidity If not specified, `0` is used.
+ */
+ isTokenExpired(minValidity?: number): boolean;
+
+ /**
+ * If the token expires within `minValidity` seconds, the token is refreshed.
+ * If the session status iframe is enabled, the session status is also
+ * checked.
+ * @returns A promise to set functions that can be invoked if the token is
+ * still valid, or if the token is no longer valid.
+ * @example
+ * ```js
+ * keycloak.updateToken(5).success(function(refreshed) {
+ * if (refreshed) {
+ * alert('Token was successfully refreshed');
+ * } else {
+ * alert('Token is still valid');
+ * }
+ * }).error(function() {
+ * alert('Failed to refresh the token, or the session has expired');
+ * });
+ */
+ updateToken(minValidity: number): KeycloakPromise<boolean, boolean>;
+
+ /**
+ * Clears authentication state, including tokens. This can be useful if
+ * the application has detected the session was expired, for example if
+ * updating token fails. Invoking this results in Keycloak#onAuthLogout
+ * callback listener being invoked.
+ */
+ clearToken(): void;
+
+ /**
+ * Returns true if the token has the given realm role.
+ * @param role A realm role name.
+ */
+ hasRealmRole(role: string): boolean;
+
+ /**
+ * Returns true if the token has the given role for the resource.
+ * @param role A role name.
+ * @param resource If not specified, `clientId` is used.
+ */
+ hasResourceRole(role: string, resource?: string): boolean;
+
+ /**
+ * Loads the user's profile.
+ * @returns A promise to set functions to be invoked on success or error.
+ */
+ loadUserProfile(): KeycloakPromise<KeycloakProfile, void>;
+
+ /**
+ * @private Undocumented.
+ */
+ loadUserInfo(): KeycloakPromise<{}, void>;
+ }
+}
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.http.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.http.ts
new file mode 100644
index 0000000..889e8ff
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.http.ts
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {Injectable} from '@angular/core';
+import {Http, Request, XHRBackend, ConnectionBackend, RequestOptions, RequestOptionsArgs, Response, Headers} from '@angular/http';
+
+import {KeycloakService} from './keycloak.service';
+import {Observable} from 'rxjs/Observable';
+import 'rxjs/add/observable/fromPromise';
+import 'rxjs/add/operator/concatMap';
+import 'rxjs/add/operator/map';
+
+
+/**
+ * This provides a wrapper over the ng2 Http class that insures tokens are refreshed on each request.
+ */
+@Injectable()
+export class KeycloakHttp extends Http {
+ constructor(_backend: ConnectionBackend, _defaultOptions: RequestOptions, private _keycloakService: KeycloakService) {
+ super(_backend, _defaultOptions);
+ }
+
+ request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
+ if (!this._keycloakService.authenticated()) return super.request(url, options);
+
+ const tokenPromise: Promise<string> = this._keycloakService.getToken();
+ const tokenObservable: Observable<string> = Observable.fromPromise(tokenPromise);
+
+ if (typeof url === 'string') {
+ return tokenObservable.map(token => {
+ const authOptions = new RequestOptions({headers: new Headers({'Authorization': 'Bearer ' + token})});
+ return new RequestOptions().merge(options).merge(authOptions);
+ }).concatMap(opts => super.request(url, opts));
+ } else if (url instanceof Request) {
+ return tokenObservable.map(token => {
+ url.headers.set('Authorization', 'Bearer ' + token);
+ return url;
+ }).concatMap(request => super.request(request));
+ }
+ }
+}
+
+export function keycloakHttpFactory(backend: XHRBackend, defaultOptions: RequestOptions, keycloakService: KeycloakService) {
+ return new KeycloakHttp(backend, defaultOptions, keycloakService);
+}
+
+export const KEYCLOAK_HTTP_PROVIDER = {
+ provide: Http,
+ useFactory: keycloakHttpFactory,
+ deps: [XHRBackend, RequestOptions, KeycloakService]
+};
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.js b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.js
new file mode 100644
index 0000000..a784936
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.js
@@ -0,0 +1,1277 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+(function( window, undefined ) {
+
+ var Keycloak = function (config) {
+ if (!(this instanceof Keycloak)) {
+ return new Keycloak(config);
+ }
+
+ var kc = this;
+ var adapter;
+ var refreshQueue = [];
+ var callbackStorage;
+
+ var loginIframe = {
+ enable: true,
+ callbackList: [],
+ interval: 5
+ };
+
+ var scripts = document.getElementsByTagName('script');
+ for (var i = 0; i < scripts.length; i++) {
+ if ((scripts[i].src.indexOf('keycloak.js') !== -1 || scripts[i].src.indexOf('keycloak.min.js') !== -1) && scripts[i].src.indexOf('version=') !== -1) {
+ kc.iframeVersion = scripts[i].src.substring(scripts[i].src.indexOf('version=') + 8).split('&')[0];
+ }
+ }
+
+ kc.init = function (initOptions) {
+ kc.authenticated = false;
+
+ callbackStorage = createCallbackStorage();
+
+ if (initOptions && initOptions.adapter === 'cordova') {
+ adapter = loadAdapter('cordova');
+ } else if (initOptions && initOptions.adapter === 'default') {
+ adapter = loadAdapter();
+ } else {
+ if (window.Cordova) {
+ adapter = loadAdapter('cordova');
+ } else {
+ adapter = loadAdapter();
+ }
+ }
+
+ if (initOptions) {
+ if (typeof initOptions.checkLoginIframe !== 'undefined') {
+ loginIframe.enable = initOptions.checkLoginIframe;
+ }
+
+ if (initOptions.checkLoginIframeInterval) {
+ loginIframe.interval = initOptions.checkLoginIframeInterval;
+ }
+
+ if (initOptions.onLoad === 'login-required') {
+ kc.loginRequired = true;
+ }
+
+ if (initOptions.responseMode) {
+ if (initOptions.responseMode === 'query' || initOptions.responseMode === 'fragment') {
+ kc.responseMode = initOptions.responseMode;
+ } else {
+ throw 'Invalid value for responseMode';
+ }
+ }
+
+ if (initOptions.flow) {
+ switch (initOptions.flow) {
+ case 'standard':
+ kc.responseType = 'code';
+ break;
+ case 'implicit':
+ kc.responseType = 'id_token token';
+ break;
+ case 'hybrid':
+ kc.responseType = 'code id_token token';
+ break;
+ default:
+ throw 'Invalid value for flow';
+ }
+ kc.flow = initOptions.flow;
+ }
+
+ if (initOptions.timeSkew != null) {
+ kc.timeSkew = initOptions.timeSkew;
+ }
+ }
+
+ if (!kc.responseMode) {
+ kc.responseMode = 'fragment';
+ }
+ if (!kc.responseType) {
+ kc.responseType = 'code';
+ kc.flow = 'standard';
+ }
+
+ var promise = createPromise();
+
+ var initPromise = createPromise();
+ initPromise.promise.success(function() {
+ kc.onReady && kc.onReady(kc.authenticated);
+ promise.setSuccess(kc.authenticated);
+ }).error(function(errorData) {
+ promise.setError(errorData);
+ });
+
+ var configPromise = loadConfig(config);
+
+ function onLoad() {
+ var doLogin = function(prompt) {
+ if (!prompt) {
+ options.prompt = 'none';
+ }
+ kc.login(options).success(function () {
+ initPromise.setSuccess();
+ }).error(function () {
+ initPromise.setError();
+ });
+ }
+
+ var options = {};
+ switch (initOptions.onLoad) {
+ case 'check-sso':
+ if (loginIframe.enable) {
+ setupCheckLoginIframe().success(function() {
+ checkLoginIframe().success(function () {
+ doLogin(false);
+ }).error(function () {
+ initPromise.setSuccess();
+ });
+ });
+ } else {
+ doLogin(false);
+ }
+ break;
+ case 'login-required':
+ doLogin(true);
+ break;
+ default:
+ throw 'Invalid value for onLoad';
+ }
+ }
+
+ function processInit() {
+ var callback = parseCallback(window.location.href);
+
+ if (callback) {
+ setupCheckLoginIframe();
+ window.history.replaceState({}, null, callback.newUrl);
+ processCallback(callback, initPromise);
+ return;
+ } else if (initOptions) {
+ if (initOptions.token && initOptions.refreshToken) {
+ setToken(initOptions.token, initOptions.refreshToken, initOptions.idToken);
+
+ if (loginIframe.enable) {
+ setupCheckLoginIframe().success(function() {
+ checkLoginIframe().success(function () {
+ kc.onAuthSuccess && kc.onAuthSuccess();
+ initPromise.setSuccess();
+ }).error(function () {
+ setToken(null, null, null);
+ initPromise.setSuccess();
+ });
+ });
+ } else {
+ kc.updateToken(-1).success(function() {
+ kc.onAuthSuccess && kc.onAuthSuccess();
+ initPromise.setSuccess();
+ }).error(function() {
+ kc.onAuthError && kc.onAuthError();
+ if (initOptions.onLoad) {
+ onLoad();
+ } else {
+ initPromise.setError();
+ }
+ });
+ }
+ } else if (initOptions.onLoad) {
+ onLoad();
+ } else {
+ initPromise.setSuccess();
+ }
+ } else {
+ initPromise.setSuccess();
+ }
+ }
+
+ configPromise.success(processInit);
+ configPromise.error(function() {
+ promise.setError();
+ });
+
+ return promise.promise;
+ }
+
+ kc.login = function (options) {
+ return adapter.login(options);
+ }
+
+ kc.createLoginUrl = function(options) {
+ var state = createUUID();
+ var nonce = createUUID();
+
+ var redirectUri = adapter.redirectUri(options);
+
+ var callbackState = {
+ state: state,
+ nonce: nonce,
+ redirectUri: encodeURIComponent(redirectUri),
+ }
+
+ if (options && options.prompt) {
+ callbackState.prompt = options.prompt;
+ }
+
+ callbackStorage.add(callbackState);
+
+ var action = 'auth';
+ if (options && options.action == 'register') {
+ action = 'registrations';
+ }
+
+ var scope = (options && options.scope) ? "openid " + options.scope : "openid";
+
+ var url = getRealmUrl()
+ + '/protocol/openid-connect/' + action
+ + '?client_id=' + encodeURIComponent(kc.clientId)
+ + '&redirect_uri=' + encodeURIComponent(redirectUri)
+ + '&state=' + encodeURIComponent(state)
+ + '&nonce=' + encodeURIComponent(nonce)
+ + '&response_mode=' + encodeURIComponent(kc.responseMode)
+ + '&response_type=' + encodeURIComponent(kc.responseType)
+ + '&scope=' + encodeURIComponent(scope);
+
+ if (options && options.prompt) {
+ url += '&prompt=' + encodeURIComponent(options.prompt);
+ }
+
+ if (options && options.maxAge) {
+ url += '&max_age=' + encodeURIComponent(options.maxAge);
+ }
+
+ if (options && options.loginHint) {
+ url += '&login_hint=' + encodeURIComponent(options.loginHint);
+ }
+
+ if (options && options.idpHint) {
+ url += '&kc_idp_hint=' + encodeURIComponent(options.idpHint);
+ }
+
+ if (options && options.locale) {
+ url += '&ui_locales=' + encodeURIComponent(options.locale);
+ }
+
+ return url;
+ }
+
+ kc.logout = function(options) {
+ return adapter.logout(options);
+ }
+
+ kc.createLogoutUrl = function(options) {
+ var url = getRealmUrl()
+ + '/protocol/openid-connect/logout'
+ + '?redirect_uri=' + encodeURIComponent(adapter.redirectUri(options, false));
+
+ return url;
+ }
+
+ kc.register = function (options) {
+ return adapter.register(options);
+ }
+
+ kc.createRegisterUrl = function(options) {
+ if (!options) {
+ options = {};
+ }
+ options.action = 'register';
+ return kc.createLoginUrl(options);
+ }
+
+ kc.createAccountUrl = function(options) {
+ var url = getRealmUrl()
+ + '/account'
+ + '?referrer=' + encodeURIComponent(kc.clientId)
+ + '&referrer_uri=' + encodeURIComponent(adapter.redirectUri(options));
+
+ return url;
+ }
+
+ kc.accountManagement = function() {
+ return adapter.accountManagement();
+ }
+
+ kc.hasRealmRole = function (role) {
+ var access = kc.realmAccess;
+ return !!access && access.roles.indexOf(role) >= 0;
+ }
+
+ kc.hasResourceRole = function(role, resource) {
+ if (!kc.resourceAccess) {
+ return false;
+ }
+
+ var access = kc.resourceAccess[resource || kc.clientId];
+ return !!access && access.roles.indexOf(role) >= 0;
+ }
+
+ kc.loadUserProfile = function() {
+ var url = getRealmUrl() + '/account';
+ var req = new XMLHttpRequest();
+ req.open('GET', url, true);
+ req.setRequestHeader('Accept', 'application/json');
+ req.setRequestHeader('Authorization', 'bearer ' + kc.token);
+
+ var promise = createPromise();
+
+ req.onreadystatechange = function () {
+ if (req.readyState == 4) {
+ if (req.status == 200) {
+ kc.profile = JSON.parse(req.responseText);
+ promise.setSuccess(kc.profile);
+ } else {
+ promise.setError();
+ }
+ }
+ }
+
+ req.send();
+
+ return promise.promise;
+ }
+
+ kc.loadUserInfo = function() {
+ var url = getRealmUrl() + '/protocol/openid-connect/userinfo';
+ var req = new XMLHttpRequest();
+ req.open('GET', url, true);
+ req.setRequestHeader('Accept', 'application/json');
+ req.setRequestHeader('Authorization', 'bearer ' + kc.token);
+
+ var promise = createPromise();
+
+ req.onreadystatechange = function () {
+ if (req.readyState == 4) {
+ if (req.status == 200) {
+ kc.userInfo = JSON.parse(req.responseText);
+ promise.setSuccess(kc.userInfo);
+ } else {
+ promise.setError();
+ }
+ }
+ }
+
+ req.send();
+
+ return promise.promise;
+ }
+
+ kc.isTokenExpired = function(minValidity) {
+ if (!kc.tokenParsed || (!kc.refreshToken && kc.flow != 'implicit' )) {
+ throw 'Not authenticated';
+ }
+
+ if (kc.timeSkew == null) {
+ console.info('[KEYCLOAK] Unable to determine if token is expired as timeskew is not set');
+ return true;
+ }
+
+ var expiresIn = kc.tokenParsed['exp'] - Math.ceil(new Date().getTime() / 1000) + kc.timeSkew;
+ if (minValidity) {
+ expiresIn -= minValidity;
+ }
+ return expiresIn < 0;
+ }
+
+ kc.updateToken = function(minValidity) {
+ var promise = createPromise();
+
+ if (!kc.refreshToken) {
+ promise.setError();
+ return promise.promise;
+ }
+
+ minValidity = minValidity || 5;
+
+ var exec = function() {
+ var refreshToken = false;
+ if (minValidity == -1) {
+ refreshToken = true;
+ console.info('[KEYCLOAK] Refreshing token: forced refresh');
+ } else if (!kc.tokenParsed || kc.isTokenExpired(minValidity)) {
+ refreshToken = true;
+ console.info('[KEYCLOAK] Refreshing token: token expired');
+ }
+
+ if (!refreshToken) {
+ promise.setSuccess(false);
+ } else {
+ var params = 'grant_type=refresh_token&' + 'refresh_token=' + kc.refreshToken;
+ var url = getRealmUrl() + '/protocol/openid-connect/token';
+
+ refreshQueue.push(promise);
+
+ if (refreshQueue.length == 1) {
+ var req = new XMLHttpRequest();
+ req.open('POST', url, true);
+ req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
+ req.withCredentials = true;
+
+ if (kc.clientId && kc.clientSecret) {
+ req.setRequestHeader('Authorization', 'Basic ' + btoa(kc.clientId + ':' + kc.clientSecret));
+ } else {
+ params += '&client_id=' + encodeURIComponent(kc.clientId);
+ }
+
+ var timeLocal = new Date().getTime();
+
+ req.onreadystatechange = function () {
+ if (req.readyState == 4) {
+ if (req.status == 200) {
+ console.info('[KEYCLOAK] Token refreshed');
+
+ timeLocal = (timeLocal + new Date().getTime()) / 2;
+
+ var tokenResponse = JSON.parse(req.responseText);
+
+ setToken(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token'], timeLocal);
+
+ kc.onAuthRefreshSuccess && kc.onAuthRefreshSuccess();
+ for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) {
+ p.setSuccess(true);
+ }
+ } else {
+ console.warn('[KEYCLOAK] Failed to refresh token');
+
+ kc.onAuthRefreshError && kc.onAuthRefreshError();
+ for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) {
+ p.setError(true);
+ }
+ }
+ }
+ };
+
+ req.send(params);
+ }
+ }
+ }
+
+ if (loginIframe.enable) {
+ var iframePromise = checkLoginIframe();
+ iframePromise.success(function() {
+ exec();
+ }).error(function() {
+ promise.setError();
+ });
+ } else {
+ exec();
+ }
+
+ return promise.promise;
+ }
+
+ kc.clearToken = function() {
+ if (kc.token) {
+ setToken(null, null, null);
+ kc.onAuthLogout && kc.onAuthLogout();
+ if (kc.loginRequired) {
+ kc.login();
+ }
+ }
+ }
+
+ function getRealmUrl() {
+ if (kc.authServerUrl.charAt(kc.authServerUrl.length - 1) == '/') {
+ return kc.authServerUrl + 'realms/' + encodeURIComponent(kc.realm);
+ } else {
+ return kc.authServerUrl + '/realms/' + encodeURIComponent(kc.realm);
+ }
+ }
+
+ function getOrigin() {
+ if (!window.location.origin) {
+ return window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: '');
+ } else {
+ return window.location.origin;
+ }
+ }
+
+ function processCallback(oauth, promise) {
+ var code = oauth.code;
+ var error = oauth.error;
+ var prompt = oauth.prompt;
+
+ var timeLocal = new Date().getTime();
+
+ if (error) {
+ if (prompt != 'none') {
+ var errorData = { error: error, error_description: oauth.error_description };
+ kc.onAuthError && kc.onAuthError(errorData);
+ promise && promise.setError(errorData);
+ } else {
+ promise && promise.setSuccess();
+ }
+ return;
+ } else if ((kc.flow != 'standard') && (oauth.access_token || oauth.id_token)) {
+ authSuccess(oauth.access_token, null, oauth.id_token, true);
+ }
+
+ if ((kc.flow != 'implicit') && code) {
+ var params = 'code=' + code + '&grant_type=authorization_code';
+ var url = getRealmUrl() + '/protocol/openid-connect/token';
+
+ var req = new XMLHttpRequest();
+ req.open('POST', url, true);
+ req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
+
+ if (kc.clientId && kc.clientSecret) {
+ req.setRequestHeader('Authorization', 'Basic ' + btoa(kc.clientId + ':' + kc.clientSecret));
+ } else {
+ params += '&client_id=' + encodeURIComponent(kc.clientId);
+ }
+
+ params += '&redirect_uri=' + oauth.redirectUri;
+
+ req.withCredentials = true;
+
+ req.onreadystatechange = function() {
+ if (req.readyState == 4) {
+ if (req.status == 200) {
+
+ var tokenResponse = JSON.parse(req.responseText);
+ authSuccess(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token'], kc.flow === 'standard');
+ } else {
+ kc.onAuthError && kc.onAuthError();
+ promise && promise.setError();
+ }
+ }
+ };
+
+ req.send(params);
+ }
+
+ function authSuccess(accessToken, refreshToken, idToken, fulfillPromise) {
+ timeLocal = (timeLocal + new Date().getTime()) / 2;
+
+ setToken(accessToken, refreshToken, idToken, timeLocal);
+
+ if ((kc.tokenParsed && kc.tokenParsed.nonce != oauth.storedNonce) ||
+ (kc.refreshTokenParsed && kc.refreshTokenParsed.nonce != oauth.storedNonce) ||
+ (kc.idTokenParsed && kc.idTokenParsed.nonce != oauth.storedNonce)) {
+
+ console.info('[KEYCLOAK] Invalid nonce, clearing token');
+ kc.clearToken();
+ promise && promise.setError();
+ } else {
+ if (fulfillPromise) {
+ kc.onAuthSuccess && kc.onAuthSuccess();
+ promise && promise.setSuccess();
+ }
+ }
+ }
+
+ }
+
+ function loadConfig(url) {
+ var promise = createPromise();
+ var configUrl;
+
+ if (!config) {
+ configUrl = 'keycloak.json';
+ } else if (typeof config === 'string') {
+ configUrl = config;
+ }
+
+ if (configUrl) {
+ var req = new XMLHttpRequest();
+ req.open('GET', configUrl, true);
+ req.setRequestHeader('Accept', 'application/json');
+
+ req.onreadystatechange = function () {
+ if (req.readyState == 4) {
+ if (req.status == 200 || fileLoaded(req)) {
+ var config = JSON.parse(req.responseText);
+
+ kc.authServerUrl = config['auth-server-url'];
+ kc.realm = config['realm'];
+ kc.clientId = config['resource'];
+ kc.clientSecret = (config['credentials'] || {})['secret'];
+
+ promise.setSuccess();
+ } else {
+ promise.setError();
+ }
+ }
+ };
+
+ req.send();
+ } else {
+ if (!config['url']) {
+ var scripts = document.getElementsByTagName('script');
+ for (var i = 0; i < scripts.length; i++) {
+ if (scripts[i].src.match(/.*keycloak\.js/)) {
+ config.url = scripts[i].src.substr(0, scripts[i].src.indexOf('/js/keycloak.js'));
+ break;
+ }
+ }
+ }
+
+ if (!config.realm) {
+ throw 'realm missing';
+ }
+
+ if (!config.clientId) {
+ throw 'clientId missing';
+ }
+
+ kc.authServerUrl = config.url;
+ kc.realm = config.realm;
+ kc.clientId = config.clientId;
+ kc.clientSecret = (config.credentials || {}).secret;
+
+ promise.setSuccess();
+ }
+
+ return promise.promise;
+ }
+
+ function fileLoaded(xhr) {
+ return xhr.status == 0 && xhr.responseText && xhr.responseURL.startsWith('file:');
+ }
+
+ function setToken(token, refreshToken, idToken, timeLocal) {
+ if (kc.tokenTimeoutHandle) {
+ clearTimeout(kc.tokenTimeoutHandle);
+ kc.tokenTimeoutHandle = null;
+ }
+
+ if (refreshToken) {
+ kc.refreshToken = refreshToken;
+ kc.refreshTokenParsed = decodeToken(refreshToken);
+ } else {
+ delete kc.refreshToken;
+ delete kc.refreshTokenParsed;
+ }
+
+ if (idToken) {
+ kc.idToken = idToken;
+ kc.idTokenParsed = decodeToken(idToken);
+ } else {
+ delete kc.idToken;
+ delete kc.idTokenParsed;
+ }
+
+ if (token) {
+ kc.token = token;
+ kc.tokenParsed = decodeToken(token);
+ kc.sessionId = kc.tokenParsed.session_state;
+ kc.authenticated = true;
+ kc.subject = kc.tokenParsed.sub;
+ kc.realmAccess = kc.tokenParsed.realm_access;
+ kc.resourceAccess = kc.tokenParsed.resource_access;
+
+ if (timeLocal) {
+ kc.timeSkew = Math.floor(timeLocal / 1000) - kc.tokenParsed.iat;
+ }
+
+ if (kc.timeSkew != null) {
+ console.info('[KEYCLOAK] Estimated time difference between browser and server is ' + kc.timeSkew + ' seconds');
+
+ if (kc.onTokenExpired) {
+ var expiresIn = (kc.tokenParsed['exp'] - (new Date().getTime() / 1000) + kc.timeSkew) * 1000;
+ console.info('[KEYCLOAK] Token expires in ' + Math.round(expiresIn / 1000) + ' s');
+ if (expiresIn <= 0) {
+ kc.onTokenExpired();
+ } else {
+ kc.tokenTimeoutHandle = setTimeout(kc.onTokenExpired, expiresIn);
+ }
+ }
+ }
+ } else {
+ delete kc.token;
+ delete kc.tokenParsed;
+ delete kc.subject;
+ delete kc.realmAccess;
+ delete kc.resourceAccess;
+
+ kc.authenticated = false;
+ }
+ }
+
+ function decodeToken(str) {
+ str = str.split('.')[1];
+
+ str = str.replace('/-/g', '+');
+ str = str.replace('/_/g', '/');
+ switch (str.length % 4)
+ {
+ case 0:
+ break;
+ case 2:
+ str += '==';
+ break;
+ case 3:
+ str += '=';
+ break;
+ default:
+ throw 'Invalid token';
+ }
+
+ str = (str + '===').slice(0, str.length + (str.length % 4));
+ str = str.replace(/-/g, '+').replace(/_/g, '/');
+
+ str = decodeURIComponent(escape(atob(str)));
+
+ str = JSON.parse(str);
+ return str;
+ }
+
+ function createUUID() {
+ var s = [];
+ var hexDigits = '0123456789abcdef';
+ for (var i = 0; i < 36; i++) {
+ s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
+ }
+ s[14] = '4';
+ s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
+ s[8] = s[13] = s[18] = s[23] = '-';
+ var uuid = s.join('');
+ return uuid;
+ }
+
+ kc.callback_id = 0;
+
+ function createCallbackId() {
+ var id = '<id: ' + (kc.callback_id++) + (Math.random()) + '>';
+ return id;
+
+ }
+
+ function parseCallback(url) {
+ var oauth = new CallbackParser(url, kc.responseMode).parseUri();
+ var oauthState = callbackStorage.get(oauth.state);
+
+ if (oauthState && (oauth.code || oauth.error || oauth.access_token || oauth.id_token)) {
+ oauth.redirectUri = oauthState.redirectUri;
+ oauth.storedNonce = oauthState.nonce;
+ oauth.prompt = oauthState.prompt;
+
+ if (oauth.fragment) {
+ oauth.newUrl += '#' + oauth.fragment;
+ }
+
+ return oauth;
+ }
+ }
+
+ function createPromise() {
+ var p = {
+ setSuccess: function(result) {
+ p.success = true;
+ p.result = result;
+ if (p.successCallback) {
+ p.successCallback(result);
+ }
+ },
+
+ setError: function(result) {
+ p.error = true;
+ p.result = result;
+ if (p.errorCallback) {
+ p.errorCallback(result);
+ }
+ },
+
+ promise: {
+ success: function(callback) {
+ if (p.success) {
+ callback(p.result);
+ } else if (!p.error) {
+ p.successCallback = callback;
+ }
+ return p.promise;
+ },
+ error: function(callback) {
+ if (p.error) {
+ callback(p.result);
+ } else if (!p.success) {
+ p.errorCallback = callback;
+ }
+ return p.promise;
+ }
+ }
+ }
+ return p;
+ }
+
+ function setupCheckLoginIframe() {
+ var promise = createPromise();
+
+ if (!loginIframe.enable) {
+ promise.setSuccess();
+ return promise.promise;
+ }
+
+ if (loginIframe.iframe) {
+ promise.setSuccess();
+ return promise.promise;
+ }
+
+ var iframe = document.createElement('iframe');
+ loginIframe.iframe = iframe;
+
+ iframe.onload = function() {
+ var realmUrl = getRealmUrl();
+ if (realmUrl.charAt(0) === '/') {
+ loginIframe.iframeOrigin = getOrigin();
+ } else {
+ loginIframe.iframeOrigin = realmUrl.substring(0, realmUrl.indexOf('/', 8));
+ }
+ promise.setSuccess();
+
+ setTimeout(check, loginIframe.interval * 1000);
+ }
+
+ var src = getRealmUrl() + '/protocol/openid-connect/login-status-iframe.html';
+ if (kc.iframeVersion) {
+ src = src + '?version=' + kc.iframeVersion;
+ }
+
+ iframe.setAttribute('src', src );
+ iframe.style.display = 'none';
+ document.body.appendChild(iframe);
+
+ var messageCallback = function(event) {
+ if ((event.origin !== loginIframe.iframeOrigin) || (loginIframe.iframe.contentWindow !== event.source)) {
+ return;
+ }
+
+ if (!(event.data == 'unchanged' || event.data == 'changed' || event.data == 'error')) {
+ return;
+ }
+
+
+ if (event.data != 'unchanged') {
+ kc.clearToken();
+ }
+
+ var callbacks = loginIframe.callbackList.splice(0, loginIframe.callbackList.length);
+
+ for (var i = callbacks.length - 1; i >= 0; --i) {
+ var promise = callbacks[i];
+ if (event.data == 'unchanged') {
+ promise.setSuccess();
+ } else {
+ promise.setError();
+ }
+ }
+ };
+
+ window.addEventListener('message', messageCallback, false);
+
+ var check = function() {
+ checkLoginIframe();
+ if (kc.token) {
+ setTimeout(check, loginIframe.interval * 1000);
+ }
+ };
+
+ return promise.promise;
+ }
+
+ function checkLoginIframe() {
+ var promise = createPromise();
+
+ if (loginIframe.iframe && loginIframe.iframeOrigin ) {
+ var msg = kc.clientId + ' ' + kc.sessionId;
+ loginIframe.callbackList.push(promise);
+ var origin = loginIframe.iframeOrigin;
+ if (loginIframe.callbackList.length == 1) {
+ loginIframe.iframe.contentWindow.postMessage(msg, origin);
+ }
+ } else {
+ promise.setSuccess();
+ }
+
+ return promise.promise;
+ }
+
+ function loadAdapter(type) {
+ if (!type || type == 'default') {
+ return {
+ login: function(options) {
+ window.location.href = kc.createLoginUrl(options);
+ return createPromise().promise;
+ },
+
+ logout: function(options) {
+ window.location.href = kc.createLogoutUrl(options);
+ return createPromise().promise;
+ },
+
+ register: function(options) {
+ window.location.href = kc.createRegisterUrl(options);
+ return createPromise().promise;
+ },
+
+ accountManagement : function() {
+ window.location.href = kc.createAccountUrl();
+ return createPromise().promise;
+ },
+
+ redirectUri: function(options, encodeHash) {
+ if (arguments.length == 1) {
+ encodeHash = true;
+ }
+
+ if (options && options.redirectUri) {
+ return options.redirectUri;
+ } else if (kc.redirectUri) {
+ return kc.redirectUri;
+ } else {
+ var redirectUri = location.href;
+ if (location.hash && encodeHash) {
+ redirectUri = redirectUri.substring(0, location.href.indexOf('#'));
+ redirectUri += (redirectUri.indexOf('?') == -1 ? '?' : '&') + 'redirect_fragment=' + encodeURIComponent(location.hash.substring(1));
+ }
+ return redirectUri;
+ }
+ }
+ };
+ }
+
+ if (type == 'cordova') {
+ loginIframe.enable = false;
+
+ return {
+ login: function(options) {
+ var promise = createPromise();
+
+ var o = 'location=no';
+ if (options && options.prompt == 'none') {
+ o += ',hidden=yes';
+ }
+
+ var loginUrl = kc.createLoginUrl(options);
+ var ref = window.open(loginUrl, '_blank', o);
+
+ var completed = false;
+
+ ref.addEventListener('loadstart', function(event) {
+ if (event.url.indexOf('http://localhost') == 0) {
+ var callback = parseCallback(event.url);
+ processCallback(callback, promise);
+ ref.close();
+ completed = true;
+ }
+ });
+
+ ref.addEventListener('loaderror', function(event) {
+ if (!completed) {
+ if (event.url.indexOf('http://localhost') == 0) {
+ var callback = parseCallback(event.url);
+ processCallback(callback, promise);
+ ref.close();
+ completed = true;
+ } else {
+ promise.setError();
+ ref.close();
+ }
+ }
+ });
+
+ return promise.promise;
+ },
+
+ logout: function(options) {
+ var promise = createPromise();
+
+ var logoutUrl = kc.createLogoutUrl(options);
+ var ref = window.open(logoutUrl, '_blank', 'location=no,hidden=yes');
+
+ var error;
+
+ ref.addEventListener('loadstart', function(event) {
+ if (event.url.indexOf('http://localhost') == 0) {
+ ref.close();
+ }
+ });
+
+ ref.addEventListener('loaderror', function(event) {
+ if (event.url.indexOf('http://localhost') == 0) {
+ ref.close();
+ } else {
+ error = true;
+ ref.close();
+ }
+ });
+
+ ref.addEventListener('exit', function(event) {
+ if (error) {
+ promise.setError();
+ } else {
+ kc.clearToken();
+ promise.setSuccess();
+ }
+ });
+
+ return promise.promise;
+ },
+
+ register : function() {
+ var registerUrl = kc.createRegisterUrl();
+ var ref = window.open(registerUrl, '_blank', 'location=no');
+ ref.addEventListener('loadstart', function(event) {
+ if (event.url.indexOf('http://localhost') == 0) {
+ ref.close();
+ }
+ });
+ },
+
+ accountManagement : function() {
+ var accountUrl = kc.createAccountUrl();
+ var ref = window.open(accountUrl, '_blank', 'location=no');
+ ref.addEventListener('loadstart', function(event) {
+ if (event.url.indexOf('http://localhost') == 0) {
+ ref.close();
+ }
+ });
+ },
+
+ redirectUri: function(options) {
+ return 'http://localhost';
+ }
+ }
+ }
+
+ throw 'invalid adapter type: ' + type;
+ }
+
+ var LocalStorage = function() {
+ if (!(this instanceof LocalStorage)) {
+ return new LocalStorage();
+ }
+
+ localStorage.setItem('kc-test', 'test');
+ localStorage.removeItem('kc-test');
+
+ var cs = this;
+
+ function clearExpired() {
+ var time = new Date().getTime();
+ for (var i = 0; i < localStorage.length; i++) {
+ var key = localStorage.key(i);
+ if (key && key.indexOf('kc-callback-') == 0) {
+ var value = localStorage.getItem(key);
+ if (value) {
+ try {
+ var expires = JSON.parse(value).expires;
+ if (!expires || expires < time) {
+ localStorage.removeItem(key);
+ }
+ } catch (err) {
+ localStorage.removeItem(key);
+ }
+ }
+ }
+ }
+ }
+
+ cs.get = function(state) {
+ if (!state) {
+ return;
+ }
+
+ var key = 'kc-callback-' + state;
+ var value = localStorage.getItem(key);
+ if (value) {
+ localStorage.removeItem(key);
+ value = JSON.parse(value);
+ }
+
+ clearExpired();
+ return value;
+ };
+
+ cs.add = function(state) {
+ clearExpired();
+
+ var key = 'kc-callback-' + state.state;
+ state.expires = new Date().getTime() + (60 * 60 * 1000);
+ localStorage.setItem(key, JSON.stringify(state));
+ };
+ };
+
+ var CookieStorage = function() {
+ if (!(this instanceof CookieStorage)) {
+ return new CookieStorage();
+ }
+
+ var cs = this;
+
+ cs.get = function(state) {
+ if (!state) {
+ return;
+ }
+
+ var value = getCookie('kc-callback-' + state);
+ setCookie('kc-callback-' + state, '', cookieExpiration(-100));
+ if (value) {
+ return JSON.parse(value);
+ }
+ };
+
+ cs.add = function(state) {
+ setCookie('kc-callback-' + state.state, JSON.stringify(state), cookieExpiration(60));
+ };
+
+ cs.removeItem = function(key) {
+ setCookie(key, '', cookieExpiration(-100));
+ };
+
+ var cookieExpiration = function (minutes) {
+ var exp = new Date();
+ exp.setTime(exp.getTime() + (minutes*60*1000));
+ return exp;
+ };
+
+ var getCookie = function (key) {
+ var name = key + '=';
+ var ca = document.cookie.split(';');
+ for (var i = 0; i < ca.length; i++) {
+ var c = ca[i];
+ while (c.charAt(0) == ' ') {
+ c = c.substring(1);
+ }
+ if (c.indexOf(name) == 0) {
+ return c.substring(name.length, c.length);
+ }
+ }
+ return '';
+ };
+
+ var setCookie = function (key, value, expirationDate) {
+ var cookie = key + '=' + value + '; '
+ + 'expires=' + expirationDate.toUTCString() + '; ';
+ document.cookie = cookie;
+ }
+ };
+
+ function createCallbackStorage() {
+ try {
+ return new LocalStorage();
+ } catch (err) {
+ }
+
+ return new CookieStorage();
+ }
+
+ var CallbackParser = function(uriToParse, responseMode) {
+ if (!(this instanceof CallbackParser)) {
+ return new CallbackParser(uriToParse, responseMode);
+ }
+ var parser = this;
+
+ var initialParse = function() {
+ var baseUri = null;
+ var queryString = null;
+ var fragmentString = null;
+
+ var questionMarkIndex = uriToParse.indexOf("?");
+ var fragmentIndex = uriToParse.indexOf("#", questionMarkIndex + 1);
+ if (questionMarkIndex == -1 && fragmentIndex == -1) {
+ baseUri = uriToParse;
+ } else if (questionMarkIndex != -1) {
+ baseUri = uriToParse.substring(0, questionMarkIndex);
+ queryString = uriToParse.substring(questionMarkIndex + 1);
+ if (fragmentIndex != -1) {
+ fragmentIndex = queryString.indexOf("#");
+ fragmentString = queryString.substring(fragmentIndex + 1);
+ queryString = queryString.substring(0, fragmentIndex);
+ }
+ } else {
+ baseUri = uriToParse.substring(0, fragmentIndex);
+ fragmentString = uriToParse.substring(fragmentIndex + 1);
+ }
+
+ return { baseUri: baseUri, queryString: queryString, fragmentString: fragmentString };
+ }
+
+ var parseParams = function(paramString) {
+ var result = {};
+ var params = paramString.split('&');
+ for (var i = 0; i < params.length; i++) {
+ var p = params[i].split('=');
+ var paramName = decodeURIComponent(p[0]);
+ var paramValue = decodeURIComponent(p[1]);
+ result[paramName] = paramValue;
+ }
+ return result;
+ }
+
+ var handleQueryParam = function(paramName, paramValue, oauth) {
+ var supportedOAuthParams = [ 'code', 'state', 'error', 'error_description' ];
+
+ for (var i = 0 ; i< supportedOAuthParams.length ; i++) {
+ if (paramName === supportedOAuthParams[i]) {
+ oauth[paramName] = paramValue;
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ parser.parseUri = function() {
+ var parsedUri = initialParse();
+
+ var queryParams = {};
+ if (parsedUri.queryString) {
+ queryParams = parseParams(parsedUri.queryString);
+ }
+
+ var oauth = { newUrl: parsedUri.baseUri };
+ for (var param in queryParams) {
+ switch (param) {
+ case 'redirect_fragment':
+ oauth.fragment = queryParams[param];
+ break;
+ default:
+ if (responseMode != 'query' || !handleQueryParam(param, queryParams[param], oauth)) {
+ oauth.newUrl += (oauth.newUrl.indexOf('?') == -1 ? '?' : '&') + param + '=' + encodeURIComponent(queryParams[param]);
+ }
+ break;
+ }
+ }
+
+ if (responseMode === 'fragment') {
+ var fragmentParams = {};
+ if (parsedUri.fragmentString) {
+ fragmentParams = parseParams(parsedUri.fragmentString);
+ }
+ for (var param in fragmentParams) {
+ oauth[param] = fragmentParams[param];
+ }
+ }
+
+ return oauth;
+ }
+ }
+
+ }
+
+ if ( typeof module === "object" && module && typeof module.exports === "object" ) {
+ module.exports = Keycloak;
+ } else {
+ window.Keycloak = Keycloak;
+
+ if ( typeof define === "function" && define.amd ) {
+ define( "keycloak", [], function () { return Keycloak; } );
+ }
+ }
+})( window );
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.service.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.service.ts
new file mode 100644
index 0000000..54f2eaa
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.service.ts
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {Injectable} from '@angular/core';
+
+// If using a local keycloak.js, uncomment this import. With keycloak.js fetched
+// from the server, you get a compile-time warning on use of the Keycloak()
+// method below. I'm not sure how to fix this, but it's certainly cleaner
+// to get keycloak.js from the server.
+//
+import * as Keycloak from './keycloak';
+
+type KeycloakClient = Keycloak.KeycloakInstance;
+type InitOptions = Keycloak.KeycloakInitOptions;
+
+@Injectable()
+export class KeycloakService {
+ static keycloakAuth: KeycloakClient;
+
+ /**
+ * Configure and initialize the Keycloak adapter.
+ *
+ * @param configOptions Optionally, a path to keycloak.json, or an object containing
+ * url, realm, and clientId.
+ * @param adapterOptions Optional initiaization options. See javascript adapter docs
+ * for details.
+ * @returns {Promise<T>}
+ */
+ static init(configOptions?: string|{}, initOptions?: InitOptions): Promise<any> {
+ KeycloakService.keycloakAuth = Keycloak(configOptions);
+
+ return new Promise((resolve, reject) => {
+ KeycloakService.keycloakAuth.init(initOptions)
+ .success(() => {
+ resolve();
+ })
+ .error((errorData: any) => {
+ reject(errorData);
+ });
+ });
+ }
+
+ authenticated(): boolean {
+ return KeycloakService.keycloakAuth.authenticated;
+ }
+
+ login() {
+ KeycloakService.keycloakAuth.login();
+ }
+
+ logout() {
+ KeycloakService.keycloakAuth.logout();
+ }
+
+ account() {
+ KeycloakService.keycloakAuth.accountManagement();
+ }
+
+ authServerUrl(): string {
+ return KeycloakService.keycloakAuth.authServerUrl;
+ }
+
+ realm(): string {
+ return KeycloakService.keycloakAuth.realm;
+ }
+
+ getToken(): Promise<string> {
+ return new Promise<string>((resolve, reject) => {
+ if (KeycloakService.keycloakAuth.token) {
+ KeycloakService.keycloakAuth
+ .updateToken(5)
+ .success(() => {
+ resolve(<string>KeycloakService.keycloakAuth.token);
+ })
+ .error(() => {
+ reject('Failed to refresh token');
+ });
+ } else {
+ reject('Not loggen in');
+ }
+ });
+ }
+}
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/ngx-translate/declared.var.translate.loader.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/ngx-translate/declared.var.translate.loader.ts
new file mode 100644
index 0000000..b508043
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/ngx-translate/declared.var.translate.loader.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2017 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+import {Observable} from 'rxjs/Observable';
+import {TranslateLoader} from '@ngx-translate/core';
+
+declare const l18n_msg: any;
+
+ /**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2017 Red Hat Inc.
+ */
+export class DeclaredVarTranslateLoader implements TranslateLoader {
+
+ getTranslation(lang: string): Observable<any> {
+ return Observable.of(l18n_msg);
+ }
+}
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/ngx-translate/translate.util.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/ngx-translate/translate.util.ts
new file mode 100644
index 0000000..947c2a3
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/ngx-translate/translate.util.ts
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2017 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+ import {Injectable} from '@angular/core';
+ import {TranslateService} from '@ngx-translate/core';
+
+ /**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2017 Red Hat Inc.
+ */
+@Injectable()
+export class TranslateUtil {
+ constructor(private translator: TranslateService) {
+ }
+
+ public translate(key: string) : string {
+ // remove Freemarker syntax
+ if (key.startsWith('${') && key.endsWith('}')) {
+ key = key.substring(2, key.length - 1);
+ }
+
+ return this.translator.instant(key);
+ }
+}
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/page/icon.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/page/icon.ts
new file mode 100644
index 0000000..e765cad
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/page/icon.ts
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+export type Vendor = "fa" | "pficon";
+
+export class Icon {
+ constructor(public vendor: Vendor, public name: string) {
+ }
+
+ getClasses(): string {
+ return `${this.vendor} ${this.vendor}-${this.name}`;
+ }
+}
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/page/referrer.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/page/referrer.ts
new file mode 100644
index 0000000..aa808ec
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/page/referrer.ts
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2017 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+import {TranslateUtil} from '../ngx-translate/translate.util';
+
+declare const referrer: string;
+declare const referrer_uri: string;
+
+ /**
+ * Encapsulate referrer logic.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2017 Red Hat Inc.
+ */
+export class Referrer {
+
+ constructor(private translateUtil: TranslateUtil) {}
+
+ public exists(): boolean {
+ return typeof referrer !== "undefined";
+ }
+
+ // return a value suitable for parameterized use with ngx-translate
+ // example {{'backTo' | translate:referrer.getName()}}
+ public getName(): { param_0: string } {
+ return {param_0: this.translateUtil.translate(referrer) };
+ }
+
+ public getUri(): string {
+ return referrer_uri;
+ }
+}
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/page/side-nav-item.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/page/side-nav-item.ts
new file mode 100644
index 0000000..3cfdb3b
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/page/side-nav-item.ts
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {Icon} from "./icon";
+
+export type Active = "active" | "";
+
+export class SideNavItem {
+
+ constructor(public displayName: string,
+ public link: string,
+ public tooltip?: string,
+ public icon?: Icon,
+ public active?: Active) {
+ }
+
+ setActive(active: Active) {
+ this.active = active;
+ }
+}
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/pipes/filterby.pipe.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/pipes/filterby.pipe.ts
new file mode 100644
index 0000000..f691142
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/pipes/filterby.pipe.ts
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2017 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+import { Pipe, PipeTransform } from '@angular/core';
+
+ /**
+ * Case insensitive filtering.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2017 Red Hat Inc.
+ */
+@Pipe({name: 'filterby'})
+export class FilterbyPipe implements PipeTransform {
+ transform(objects: any[], property: string, text: string) {
+ if (!property) return objects;
+ if (!text) return objects;
+
+ const transformed: any[] = [];
+ for (let obj of objects) {
+ let propVal:any = obj[property];
+ if (!this.isString(propVal)) {
+ console.error("Can't filter property " + property + ". Its value is not a string.");
+ break;
+ }
+
+ let strPropVal:string = propVal as string;
+ if (strPropVal.toLowerCase().indexOf(text.toLowerCase()) != -1) {
+ transformed.push(obj);
+ }
+ }
+
+ return transformed;
+ }
+
+ private isString(value: any): boolean {
+ return (typeof value == 'string') || (value instanceof String);
+ }
+}
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/pipes/orderby.pipe.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/pipes/orderby.pipe.ts
new file mode 100644
index 0000000..19e1216
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/pipes/orderby.pipe.ts
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+import { Pipe, PipeTransform } from '@angular/core';
+
+ /**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2017 Red Hat Inc.
+ */
+@Pipe({name: 'orderby'})
+export class OrderbyPipe implements PipeTransform {
+ transform(objects: any[], property: string, descending: boolean = true) {
+ if (!property) return objects;
+
+ let sorted: any[] = objects.sort((obj1, obj2) => {
+ if (obj1[property] > obj2[property]) return 1;
+ if (obj1[property] < obj2[property]) return -1;
+ return 0;
+ })
+
+ if (descending) {
+ return sorted;
+ } else {
+ return sorted.reverse();
+ }
+ }
+}
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/responsiveness-service/media.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/responsiveness-service/media.ts
new file mode 100644
index 0000000..e46ac52
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/responsiveness-service/media.ts
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2017 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+/**
+ * For this application, we are responsive to three sizes: large, medium, and
+ * small. Note that these do not perfectly correspond to bootstrap device
+ * sizes but are more in line with patternfly.
+ *
+ * When making decisions based on screen size, you should create a single
+ * instance of this class and then test for each size. Do not use several
+ * instances because the screen size could change at any time and you may
+ * get inconsistent results.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2017 Red Hat Inc.
+ */
+export class Media {
+
+ private screenWidth: number = window.innerWidth;
+
+ public isLarge(): boolean {
+ return this.screenWidth > 1023;
+ }
+
+ public isMedium(): boolean {
+ return (this.screenWidth < 1023) && (this.screenWidth > 768);
+ }
+
+ public isSmall(): boolean {
+ return this.screenWidth < 769;
+ }
+}
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/responsiveness-service/responsiveness.service.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/responsiveness-service/responsiveness.service.ts
new file mode 100644
index 0000000..66bd492
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/responsiveness-service/responsiveness.service.ts
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2017 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+ import { Injectable } from "@angular/core";
+
+ import { Media } from "./media";
+
+ export type SideNavClasses = "" | "collapsed" | "hidden" | "hidden show-mobile-nav";
+ export type ContentWidthClass = "" | "collapsed-nav" | "hidden-nav";
+
+ export interface MenuClickListener {
+ menuClicked(): void;
+ }
+
+ /**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2017 Red Hat Inc.
+ */
+@Injectable()
+export class ResponsivenessService {
+
+ private menuOn: boolean = false;
+ private menuListeners: MenuClickListener[] = Array<MenuClickListener>();
+
+ public addMenuClickListener(listener: MenuClickListener) {
+ this.menuListeners.push(listener);
+ }
+
+ public menuClicked() : void {
+ this.menuOn = !this.menuOn;
+
+ for (let listener of this.menuListeners) {
+ listener.menuClicked();
+ }
+ }
+
+ public calcSideNavWidthClasses() : SideNavClasses {
+ const media: Media = new Media();
+
+ if (media.isLarge() && !this.menuOn) {
+ return "";
+ }
+
+ if (media.isLarge() && this.menuOn) {
+ return "collapsed";
+ }
+
+ if (media.isMedium() && !this.menuOn) {
+ return "collapsed";
+ }
+
+ if (media.isMedium() && this.menuOn) {
+ return "";
+ }
+
+ // media must be small
+ if (!this.menuOn) {
+ return "hidden"
+ }
+
+ return "hidden show-mobile-nav";
+ }
+
+ public calcSideContentWidthClass() : ContentWidthClass {
+ const media: Media = new Media();
+
+ if (media.isLarge() && !this.menuOn) {
+ return "";
+ }
+
+ if (media.isLarge() && this.menuOn) {
+ return "collapsed-nav";
+ }
+
+ if (media.isMedium() && !this.menuOn) {
+ return "collapsed-nav";
+ }
+
+ if (media.isMedium() && this.menuOn) {
+ return "";
+ }
+
+ // media must be small
+ if (!this.menuOn) {
+ return "hidden-nav"
+ }
+
+ return "hidden-nav";
+ }
+}
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/side-nav/side-nav.component.css b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/side-nav/side-nav.component.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/side-nav/side-nav.component.css
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/side-nav/side-nav.component.html b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/side-nav/side-nav.component.html
new file mode 100644
index 0000000..616817e
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/side-nav/side-nav.component.html
@@ -0,0 +1,71 @@
+<nav class="nav-pf-vertical {{this.sideNavClasses}}"> <!-- {{this.sideNavClasses}} collapsed hidden show-mobile-nav -->
+ <ul class="list-group">
+ <li *ngFor="let item of navItems" class="list-group-item {{item.active}}">
+ <a [routerLink]="[item.link]">
+ <span class="{{item.icon.getClasses()}}" title="{{item.tooltip}}" data-toggle="tooltip" data-placement="right"></span>
+ <span class="list-group-item-value">{{item.displayName}}</span>
+ </a>
+ </li>
+ <li *ngIf="referrer.exists()" class="list-group-item hidden-sm hidden-md hidden-lg">
+ <a href="{{referrer.getUri()}}">
+ <span class="pficon-arrow" title="{{'backTo' | translate:referrer.getName()}}"></span>
+ <span class="list-group-item-value">{{'backTo' | translate:referrer.getName()}}</span>
+ </a>
+ </li>
+ <li class="list-group-item hidden-sm hidden-md hidden-lg">
+ <a href="#" (click)="logout()">
+ <span class="fa fa-sign-out" title="{{'doSignOut' | translate}}"></span>
+ <span class="list-group-item-value">{{'doSignOut' | translate}}</span>
+ </a>
+ </li>
+ </ul>
+</nav>
+<!-- <li class="list-group-item active">
+ <a href="#">
+ <span class="pficon pficon-user" title="Dashboard" data-toggle="tooltip" data-placement="right"></span>
+ <span class="list-group-item-value">Personal information</span>
+ </a>
+ </li>
+ <li class="list-group-item">
+ <a href="#">
+ <span class="fa fa-link" title="My Services" data-toggle="tooltip" data-placement="right"></span>
+ <span class="list-group-item-value">Connected Devices</span>
+ </a>
+ </li>
+ <li class="list-group-item">
+ <a href="#">
+ <span class="fa fa-cubes" title="My Requests" data-toggle="tooltip" data-placement="right"></span>
+ <span class="list-group-item-value">Applications</span>
+ </a>
+ </li>
+ <li class="list-group-item">
+ <a href="#">
+ <span class="pficon pficon-history" title="My Items" data-toggle="tooltip" data-placement="right"></span>
+ <span class="list-group-item-value">History</span>
+ </a>
+ </li>
+ <li class="list-group-item hidden-sm hidden-md hidden">
+ <a href="#">
+ <span class="pficon pficon-help" title="Help"></span>
+ <span class="list-group-item-value">Help</span>
+ </a>
+ </li>
+ <li class="list-group-item hidden-sm hidden-md hidden-lg">
+ <a href="#">
+ <span class="fa fa-info-circle" title="About"></span>
+ <span class="list-group-item-value">About</span>
+ </a>
+ </li>
+ <li class="list-group-item hidden-sm hidden-md hidden-lg">
+ <a href="#">
+ <span class="pficon pficon-user" title="Preferences"></span>
+ <span class="list-group-item-value">Preferences</span>
+ </a>
+ </li>
+ <li class="list-group-item hidden-sm hidden-md hidden-lg">
+ <a href="#">
+ <span class="fa fa-sign-out" title="Log Out"></span>
+ <span class="list-group-item-value">Log Out</span>
+ </a>
+ </li>-->
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/side-nav/side-nav.component.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/side-nav/side-nav.component.ts
new file mode 100644
index 0000000..bfeeeb8
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/side-nav/side-nav.component.ts
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {Component, OnInit, HostListener} from '@angular/core';
+import {Router, NavigationEnd} from '@angular/router';
+import {KeycloakService} from '../keycloak-service/keycloak.service';
+import {TranslateUtil} from '../ngx-translate/translate.util';
+import {SideNavItem, Active} from '../page/side-nav-item';
+import {Icon} from '../page/icon';
+import {ResponsivenessService, SideNavClasses, MenuClickListener} from "../responsiveness-service/responsiveness.service";
+import {Media} from "../responsiveness-service/media";
+import {Referrer} from "../page/referrer";
+
+@Component({
+ selector: 'app-side-nav',
+ templateUrl: './side-nav.component.html',
+ styleUrls: ['./side-nav.component.css']
+})
+export class SideNavComponent implements OnInit, MenuClickListener {
+
+ private referrer: Referrer;
+ private sideNavClasses: SideNavClasses = this.respSvc.calcSideNavWidthClasses();
+ private isFirstRouterEvent: boolean = true;
+
+ public navItems: SideNavItem[];
+
+ constructor(private router: Router,
+ private translateUtil: TranslateUtil,
+ private respSvc: ResponsivenessService,
+ private keycloakService: KeycloakService) {
+ this.referrer = new Referrer(translateUtil);
+ this.navItems = [
+ this.makeSideNavItem("account", new Icon("pficon", "user"), "active"),
+ this.makeSideNavItem("password", new Icon("pficon", "key")),
+ this.makeSideNavItem("authenticator", new Icon("pficon", "cloud-security")),
+ this.makeSideNavItem("sessions", new Icon("fa", "clock-o")),
+ this.makeSideNavItem("applications", new Icon("fa", "th"))
+ ];
+
+ this.router.events.subscribe(value => {
+ if (value instanceof NavigationEnd) {
+ const navEnd = value as NavigationEnd;
+ this.setActive(navEnd.url);
+
+ const media: Media = new Media();
+ if (media.isSmall() && !this.isFirstRouterEvent) {
+ this.respSvc.menuClicked();
+ }
+
+ this.isFirstRouterEvent = false;
+ }
+ });
+
+ this.respSvc.addMenuClickListener(this);
+ }
+
+ // use itemName for translate key, link, and tooltip
+ private makeSideNavItem(itemName: string, icon: Icon, active?: Active): SideNavItem {
+ const localizedName: string = this.translateUtil.translate(itemName);
+
+ return new SideNavItem(localizedName, itemName, localizedName, icon, active);
+ }
+
+ private logout() {
+ this.keycloakService.logout();
+ }
+
+ public menuClicked(): void {
+ this.sideNavClasses = this.respSvc.calcSideNavWidthClasses();
+ }
+
+ @HostListener('window:resize', ['$event'])
+ private onResize(event: any) {
+ this.sideNavClasses = this.respSvc.calcSideNavWidthClasses();
+ }
+
+ setActive(url: string) {
+ for (let navItem of this.navItems) {
+ if (("/" + navItem.link) === url) {
+ navItem.setActive("active");
+ } else {
+ navItem.setActive("");
+ }
+ }
+
+ if ("/" === url) {
+ this.navItems[0].setActive("active");
+ }
+ }
+
+ ngOnInit() {
+
+ }
+
+}
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/notification.component.css b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/notification.component.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/notification.component.css
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/notification.component.html b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/notification.component.html
new file mode 100644
index 0000000..7b71d11
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/notification.component.html
@@ -0,0 +1,12 @@
+<div *ngIf="isVisible" class="toast-pf
+ toast-pf-max-width
+ toast-pf-top-right
+ alert
+ {{notification.alertType}}
+ alert-dismissable">
+ <button type="button" class="close" data-dismiss="alert" aria-hidden="true">
+ <span class="pficon pficon-close"></span>
+ </button>
+ <span class="pficon {{notification.icon}}"></span>
+ {{notification.message}}
+</div>
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/notification.component.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/notification.component.ts
new file mode 100644
index 0000000..be57d83
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/notification.component.ts
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {Component} from '@angular/core';
+
+import {ToastNotifier, ToastNotification} from './toast.notifier';
+
+@Component({
+ selector: 'notification',
+ templateUrl: './notification.component.html',
+ styleUrls: ['./notification.component.css']
+})
+export class NotificationComponent {
+ private isVisible: boolean = false;
+ private notification: ToastNotification = new ToastNotification("");
+
+ constructor(toastNotifier: ToastNotifier) {
+
+ toastNotifier.subscribe((notification: ToastNotification) => {
+ this.notification = notification;
+ this.isVisible = true;
+ setTimeout(() => {
+ this.isVisible = false;
+ }, 8000);
+ })
+
+ }
+}
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/toast.notifier.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/toast.notifier.ts
new file mode 100644
index 0000000..ea275ec
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/toast.notifier.ts
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2017 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+ import {Injectable, EventEmitter} from '@angular/core';
+
+ /**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2017 Red Hat Inc.
+ */
+@Injectable()
+export class ToastNotifier extends EventEmitter<ToastNotification> {
+ constructor() {
+ super();
+ }
+}
+
+type ToastIcon = "pficon-ok" |
+ "pficon-info" |
+ "pficon-warning-triangle-o" |
+ "pficon-error-circle-o";
+
+type ToastAlertType = "alert-success" |
+ "alert-info" |
+ "alert-warning" |
+ "alert-danger";
+
+export type MessageType = "success" |
+ "info" |
+ "warning" |
+ "error";
+
+export class ToastNotification {
+ public alertType: ToastAlertType = "alert-success";
+ public icon: ToastIcon = "pficon-ok";
+
+ constructor(public message: string, messageType?: MessageType) {
+ switch (messageType) {
+ case "info": {
+ this.alertType = "alert-info";
+ this.icon = "pficon-info";
+ break;
+ }
+ case "warning": {
+ this.alertType = "alert-warning";
+ this.icon = "pficon-warning-triangle-o";
+ break;
+ }
+ case "error": {
+ this.alertType = "alert-danger";
+ this.icon = "pficon-error-circle-o";
+ break;
+ }
+ default: {
+ this.alertType = "alert-success";
+ this.icon = "pficon-ok";
+ }
+ }
+ }
+}
+
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/top-nav.component.css b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/top-nav.component.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/top-nav.component.css
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/top-nav.component.html b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/top-nav.component.html
new file mode 100644
index 0000000..4b30962
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/top-nav.component.html
@@ -0,0 +1,47 @@
+
+
+<nav class="navbar navbar-pf-alt">
+ <notification></notification>
+
+ <div class="navbar-header">
+ <button (click)="menuClicked()" type="button" class="navbar-toggle">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ <a href="http://www.keycloak.org" class="navbar-brand">
+ <img class="navbar-brand-icon" type="image/svg+xml" src="{{resourceUrl}}/app/assets/img/keycloak-logo.png" alt="" width="auto" height="30px"/>
+ </a>
+ </div>
+ <nav class="collapse navbar-collapse">
+ <ul class="nav navbar-nav">
+ </ul>
+ <ul class="nav navbar-nav navbar-right navbar-iconic">
+ <li *ngIf="referrer.exists()">
+ <a class="nav-item-iconic" href="{{referrer.getUri()}}"><span class="pficon-arrow"></span> {{'backTo' | translate:referrer.getName()}}</a>
+ </li>
+ <li class="dropdown">
+ <a class="dropdown-toggle nav-item-iconic" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
+ <span title="Help" class="fa pficon-help"></span>
+ <span class="caret"></span>
+ </a>
+ <ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
+ <li><a href="#">Help</a></li>
+ <li><a href="#">About</a></li>
+ </ul>
+ </li>
+ <li class="dropdown">
+ <a class="dropdown-toggle nav-item-iconic" id="dropdownMenu2" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
+ <span title="Username" class="fa pficon-user"></span>
+ <span class="caret"></span>
+ </a>
+ <ul class="dropdown-menu" aria-labelledby="dropdownMenu2">
+ <li><a href="#">Preferences</a></li>
+ <li><a href="#" (click)="logout()">{{'doSignOut' | translate}}</a></li>
+ </ul>
+ </li>
+ </ul>
+ </nav>
+
+</nav> <!--/.navbar-->
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/top-nav.component.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/top-nav.component.ts
new file mode 100644
index 0000000..88a0c14
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/top-nav/top-nav.component.ts
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {Component, OnInit} from '@angular/core';
+import {TranslateUtil} from '../ngx-translate/translate.util';
+import {KeycloakService} from '../keycloak-service/keycloak.service';
+import {ResponsivenessService} from "../responsiveness-service/responsiveness.service";
+import {Referrer} from "../page/referrer";
+
+declare const resourceUrl: string;
+declare const referrer: string;
+declare const referrer_uri: string;
+
+@Component({
+ selector: 'app-top-nav',
+ templateUrl: './top-nav.component.html',
+ styleUrls: ['./top-nav.component.css']
+})
+export class TopNavComponent implements OnInit {
+
+ public resourceUrl: string = resourceUrl;
+
+ private referrer: Referrer;
+
+ constructor(private keycloakService: KeycloakService, translateUtil: TranslateUtil, private respSvc: ResponsivenessService) {
+ this.referrer = new Referrer(translateUtil);
+ }
+
+ private menuClicked(): void {
+ this.respSvc.menuClicked();
+ }
+
+ ngOnInit() {
+ }
+
+ private logout() {
+ this.keycloakService.logout();
+ }
+
+}
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/environments/environment.prod.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/environments/environment.prod.ts
new file mode 100644
index 0000000..3612073
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/environments/environment.prod.ts
@@ -0,0 +1,3 @@
+export const environment = {
+ production: true
+};
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/environments/environment.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/environments/environment.ts
new file mode 100644
index 0000000..b7f639a
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/environments/environment.ts
@@ -0,0 +1,8 @@
+// The file contents for the current environment will overwrite these during build.
+// The build system defaults to the dev environment which uses `environment.ts`, but if you do
+// `ng build --env=prod` then `environment.prod.ts` will be used instead.
+// The list of which env maps to which file can be found in `.angular-cli.json`.
+
+export const environment = {
+ production: false
+};
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/main.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/main.ts
new file mode 100644
index 0000000..b5db7b0
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/main.ts
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { enableProdMode } from '@angular/core';
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { platformBrowser } from '@angular/platform-browser';
+
+import { AppModule } from './app/app.module';
+//import { environment } from './environments/environment';
+
+import { KeycloakService } from './app/keycloak-service/keycloak.service';
+
+//if (environment.production) {
+// enableProdMode();
+//}
+
+declare const authUrl: string;
+declare const resourceUrl: string;
+declare const realm: string;
+
+const noLogin: boolean = false; // convenient for development
+if (noLogin) {
+ platformBrowserDynamic().bootstrapModule(AppModule);
+} else {
+ KeycloakService.init(authUrl + '/realms/' + realm + '/account/keycloak.json',
+ {onLoad: 'login-required'})
+ .then(() => {
+ platformBrowserDynamic().bootstrapModule(AppModule);
+ })
+ .catch((e: any) => {
+ console.log('Error in bootstrap: ' + JSON.stringify(e));
+ });
+}
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/package.json b/themes/src/main/resources/theme/keycloak-preview/account/resources/package.json
new file mode 100644
index 0000000..1ea8c3c
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/package.json
@@ -0,0 +1,61 @@
+{
+ "name": "keycloak-preview",
+ "version": "1.0.0",
+ "description": "keycloak-preview account management written in Angular 2",
+ "scripts": {
+ "build": "tsc -p ./",
+ "build:watch": "tsc -p ./ -w",
+ "build:e2e": "tsc -p e2e/",
+ "serve": "lite-server -c=bs-config.json",
+ "serve:e2e": "lite-server -c=bs-config.e2e.json",
+ "prestart": "npm run build",
+ "start": "concurrently \"npm run build:watch\" \"npm run serve\"",
+ "pree2e": "npm run build:e2e",
+ "e2e": "concurrently \"npm run serve:e2e\" \"npm run protractor\" --kill-others --success first",
+ "preprotractor": "webdriver-manager update",
+ "protractor": "protractor protractor.config.js",
+ "pretest": "npm run build",
+ "test": "concurrently \"npm run build:watch\" \"karma start karma.conf.js\"",
+ "pretest:once": "npm run build",
+ "test:once": "karma start karma.conf.js --single-run",
+ "lint": "tslint ./**/*.ts --exclude ./node_modules/** -t verbose"
+ },
+ "keywords": [],
+ "author": "Stan Silvert",
+ "license": "Apache 2.0",
+ "dependencies": {
+ "@angular/common": "~4.0.0",
+ "@angular/compiler": "~4.0.0",
+ "@angular/core": "~4.0.0",
+ "@angular/forms": "~4.0.0",
+ "@angular/http": "~4.0.0",
+ "@angular/platform-browser": "~4.0.0",
+ "@angular/platform-browser-dynamic": "~4.0.0",
+ "@angular/router": "~4.0.0",
+ "@ngx-translate/core": "^7.1.0",
+ "core-js": "^2.4.1",
+ "patternfly": "^3.23.2",
+ "rxjs": "^5.4.2",
+ "systemjs": "^0.20.17",
+ "zone.js": "^0.8.4"
+ },
+ "devDependencies": {
+ "@types/jasmine": "2.5.36",
+ "@types/node": "^6.0.46",
+ "canonical-path": "0.0.2",
+ "concurrently": "^3.2.0",
+ "jasmine-core": "~2.4.1",
+ "karma": "^1.3.0",
+ "karma-chrome-launcher": "^2.0.0",
+ "karma-cli": "^1.0.1",
+ "karma-jasmine": "^1.0.2",
+ "karma-jasmine-html-reporter": "^0.2.2",
+ "lite-server": "^2.2.2",
+ "lodash": "^4.16.4",
+ "protractor": "~4.0.14",
+ "rimraf": "^2.5.4",
+ "tslint": "^3.15.1",
+ "typescript": "^2.4.2"
+ },
+ "repository": {}
+}
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/styles.css b/themes/src/main/resources/theme/keycloak-preview/account/resources/styles.css
new file mode 100644
index 0000000..58e1a7d
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/styles.css
@@ -0,0 +1,5 @@
+h1 {
+ color: #369;
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 250%;
+}
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/systemjs.config.extras.js b/themes/src/main/resources/theme/keycloak-preview/account/resources/systemjs.config.extras.js
new file mode 100644
index 0000000..027dfe5
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/systemjs.config.extras.js
@@ -0,0 +1,11 @@
+/**
+ * Add barrels and stuff
+ * Adjust as necessary for your application needs.
+ */
+// (function (global) {
+// System.config({
+// packages: {
+// // add packages here
+// }
+// });
+// })(this);
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/systemjs.config.js b/themes/src/main/resources/theme/keycloak-preview/account/resources/systemjs.config.js
new file mode 100644
index 0000000..7814857
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/systemjs.config.js
@@ -0,0 +1,49 @@
+/**
+ * System configuration for Angular samples
+ * Adjust as necessary for your application needs.
+ */
+(function (global) {
+ System.config({
+ paths: {
+ // paths serve as alias
+ 'npm:': resourceUrl + '/node_modules/'
+ },
+ // map tells the System loader where to look for things
+ map: {
+ // our app is within the app folder
+ 'app': resourceUrl + '/app',
+
+ // angular bundles
+ '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
+ '@angular/common': 'npm:@angular/common/bundles/common.umd.js',
+ '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
+ '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
+ '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
+ '@angular/http': 'npm:@angular/http/bundles/http.umd.js',
+ '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
+ '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
+
+ // other libraries
+ '@ngx-translate/core': 'npm:@ngx-translate/core/bundles/core.umd.js',
+ 'rxjs': 'npm:rxjs',
+ 'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js'
+ },
+ // packages tells the System loader how to load when no filename and/or no extension
+ packages: {
+ app: {
+ defaultExtension: 'js',
+ meta: {
+ './*.js': {
+ loader: resourceUrl + '/systemjs-angular-loader.js'
+ }
+ }
+ },
+ '@ngx-translate/core': {
+ defaultExtension: 'js'
+ },
+ rxjs: {
+ defaultExtension: 'js'
+ }
+ }
+ });
+})(this);
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/systemjs-angular-loader.js b/themes/src/main/resources/theme/keycloak-preview/account/resources/systemjs-angular-loader.js
new file mode 100644
index 0000000..4d3e127
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/systemjs-angular-loader.js
@@ -0,0 +1,45 @@
+var templateUrlRegex = /templateUrl\s*:(\s*['"`](.*?)['"`]\s*)/gm;
+var stylesRegex = /styleUrls *:(\s*\[[^\]]*?\])/g;
+var stringRegex = /(['`"])((?:[^\\]\\\1|.)*?)\1/g;
+
+module.exports.translate = function(load){
+ var url = document.createElement('a');
+ url.href = load.address;
+
+ var basePathParts = url.pathname.split('/');
+
+ basePathParts.pop();
+ var basePath = basePathParts.join('/');
+
+ var baseHref = document.createElement('a');
+ baseHref.href = this.baseURL;
+ baseHref = baseHref.pathname;
+
+ basePath = basePath.replace(baseHref, '');
+
+ load.source = load.source
+ .replace(templateUrlRegex, function(match, quote, url){
+ var resolvedUrl = url;
+
+ if (url.startsWith('.')) {
+ resolvedUrl = basePath + url.substr(1);
+ }
+
+ return 'templateUrl: "' + resolvedUrl + '"';
+ })
+ .replace(stylesRegex, function(match, relativeUrls) {
+ var urls = [];
+
+ while ((match = stringRegex.exec(relativeUrls)) !== null) {
+ if (match[2].startsWith('.')) {
+ urls.push('"' + basePath + match[2].substr(1) + '"');
+ } else {
+ urls.push('"' + match[2] + '"');
+ }
+ }
+
+ return "styleUrls: [" + urls.join(', ') + "]";
+ });
+
+ return load;
+};
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/tsconfig.json b/themes/src/main/resources/theme/keycloak-preview/account/resources/tsconfig.json
new file mode 100644
index 0000000..2c7260d
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "compilerOptions": {
+ "target": "es5",
+ "module": "commonjs",
+ "moduleResolution": "node",
+ "sourceMap": true,
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "lib": [ "es2015", "dom" ],
+ "noImplicitAny": true,
+ "suppressImplicitAnyIndexErrors": true
+ }
+}
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/tslint.json b/themes/src/main/resources/theme/keycloak-preview/account/resources/tslint.json
new file mode 100644
index 0000000..276453f
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/tslint.json
@@ -0,0 +1,93 @@
+{
+ "rules": {
+ "class-name": true,
+ "comment-format": [
+ true,
+ "check-space"
+ ],
+ "curly": true,
+ "eofline": true,
+ "forin": true,
+ "indent": [
+ true,
+ "spaces"
+ ],
+ "label-position": true,
+ "label-undefined": true,
+ "max-line-length": [
+ true,
+ 140
+ ],
+ "member-access": false,
+ "member-ordering": [
+ true,
+ "static-before-instance",
+ "variables-before-functions"
+ ],
+ "no-arg": true,
+ "no-bitwise": true,
+ "no-console": [
+ true,
+ "debug",
+ "info",
+ "time",
+ "timeEnd",
+ "trace"
+ ],
+ "no-construct": true,
+ "no-debugger": true,
+ "no-duplicate-key": true,
+ "no-duplicate-variable": true,
+ "no-empty": false,
+ "no-eval": true,
+ "no-inferrable-types": true,
+ "no-shadowed-variable": true,
+ "no-string-literal": false,
+ "no-switch-case-fall-through": true,
+ "no-trailing-whitespace": true,
+ "no-unused-expression": true,
+ "no-unused-variable": true,
+ "no-unreachable": true,
+ "no-use-before-declare": true,
+ "no-var-keyword": true,
+ "object-literal-sort-keys": false,
+ "one-line": [
+ true,
+ "check-open-brace",
+ "check-catch",
+ "check-else",
+ "check-whitespace"
+ ],
+ "quotemark": [
+ true,
+ "single"
+ ],
+ "radix": true,
+ "semicolon": [
+ "always"
+ ],
+ "triple-equals": [
+ true,
+ "allow-null-check"
+ ],
+ "typedef-whitespace": [
+ true,
+ {
+ "call-signature": "nospace",
+ "index-signature": "nospace",
+ "parameter": "nospace",
+ "property-declaration": "nospace",
+ "variable-declaration": "nospace"
+ }
+ ],
+ "variable-name": false,
+ "whitespace": [
+ true,
+ "check-branch",
+ "check-decl",
+ "check-operator",
+ "check-separator",
+ "check-type"
+ ]
+ }
+}
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/yarn.lock b/themes/src/main/resources/theme/keycloak-preview/account/resources/yarn.lock
new file mode 100644
index 0000000..c335da6
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/yarn.lock
@@ -0,0 +1,2936 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@angular/common@~4.0.0":
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/@angular/common/-/common-4.0.3.tgz#17472895eb425f2812b3a79162b5b494d2506a5b"
+
+"@angular/compiler@~4.0.0":
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-4.0.3.tgz#8b3cad338ac539328e10a6a4bfaa057094f7bc89"
+
+"@angular/core@~4.0.0":
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/@angular/core/-/core-4.0.3.tgz#61be21db6aa5778e33159ffd38cbbebaf16120d9"
+
+"@angular/forms@~4.0.0":
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-4.0.3.tgz#fb8e6e0aede782bf58730a31d1179b323271816e"
+
+"@angular/http@~4.0.0":
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/@angular/http/-/http-4.0.3.tgz#efbb701a215ec7704c021676484b85ed47392f4b"
+
+"@angular/platform-browser-dynamic@~4.0.0":
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-4.0.3.tgz#5fa3b98f725999b631d7d7174e6e43dcbf6aa9ac"
+
+"@angular/platform-browser@~4.0.0":
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-4.0.3.tgz#170b18d5af4ee02b248aa6a1f1e0584ac841681e"
+
+"@angular/router@~4.0.0":
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/@angular/router/-/router-4.0.3.tgz#24184e9b1266c4ad017b2be81573464b1e4c5dfa"
+
+"@ngx-translate/core@^7.1.0":
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/@ngx-translate/core/-/core-7.1.0.tgz#5087a65c8ff312e4244ca0646ed45cde83b170cd"
+
+"@types/jasmine@2.5.36", "@types/jasmine@^2.5.36":
+ version "2.5.36"
+ resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.5.36.tgz#6d804b79a57e9380d766a2680e9362c17d9556d2"
+
+"@types/node@^6.0.46":
+ version "6.0.73"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.73.tgz#85dc4bb6f125377c75ddd2519a1eeb63f0a4ed70"
+
+"@types/q@^0.0.32":
+ version "0.0.32"
+ resolved "https://registry.yarnpkg.com/@types/q/-/q-0.0.32.tgz#bd284e57c84f1325da702babfc82a5328190c0c5"
+
+"@types/selenium-webdriver@2.53.37":
+ version "2.53.37"
+ resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-2.53.37.tgz#34f743c20e53ae7100ede90870fde554df2447f8"
+
+abbrev@1:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f"
+
+accepts@1.3.3, accepts@~1.3.3:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca"
+ dependencies:
+ mime-types "~2.1.11"
+ negotiator "0.6.1"
+
+acorn-jsx@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b"
+ dependencies:
+ acorn "^3.0.4"
+
+acorn-object-spread@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/acorn-object-spread/-/acorn-object-spread-1.0.0.tgz#48ead0f4a8eb16995a17a0db9ffc6acaada4ba68"
+ dependencies:
+ acorn "^3.1.0"
+
+acorn@^3.0.4, acorn@^3.1.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
+
+adm-zip@0.4.4:
+ version "0.4.4"
+ resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.4.tgz#a61ed5ae6905c3aea58b3a657d25033091052736"
+
+adm-zip@0.4.7, adm-zip@^0.4.7:
+ version "0.4.7"
+ resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.7.tgz#8606c2cbf1c426ce8c8ec00174447fd49b6eafc1"
+
+after@0.8.1:
+ version "0.8.1"
+ resolved "https://registry.yarnpkg.com/after/-/after-0.8.1.tgz#ab5d4fb883f596816d3515f8f791c0af486dd627"
+
+after@0.8.2:
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f"
+
+agent-base@2:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-2.0.1.tgz#bd8f9e86a8eb221fffa07bd14befd55df142815e"
+ dependencies:
+ extend "~3.0.0"
+ semver "~5.0.1"
+
+ajv@^4.9.1:
+ version "4.11.8"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536"
+ dependencies:
+ co "^4.6.0"
+ json-stable-stringify "^1.0.1"
+
+ansi-regex@^0.2.0, ansi-regex@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-0.2.1.tgz#0d8e946967a3d8143f93e24e298525fc1b2235f9"
+
+ansi-regex@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
+
+ansi-styles@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.1.0.tgz#eaecbf66cd706882760b2f4691582b8f55d7a7de"
+
+ansi-styles@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
+
+anymatch@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507"
+ dependencies:
+ arrify "^1.0.0"
+ micromatch "^2.1.5"
+
+aproba@^1.0.3:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.1.tgz#95d3600f07710aa0e9298c726ad5ecf2eacbabab"
+
+are-we-there-yet@~1.1.2:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d"
+ dependencies:
+ delegates "^1.0.0"
+ readable-stream "^2.0.6"
+
+arr-diff@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf"
+ dependencies:
+ arr-flatten "^1.0.1"
+
+arr-flatten@^1.0.1:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.3.tgz#a274ed85ac08849b6bd7847c4580745dc51adfb1"
+
+array-slice@^0.2.3:
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-0.2.3.tgz#dd3cfb80ed7973a75117cdac69b0b99ec86186f5"
+
+array-union@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
+ dependencies:
+ array-uniq "^1.0.1"
+
+array-uniq@^1.0.1:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
+
+array-unique@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53"
+
+arraybuffer.slice@0.0.6:
+ version "0.0.6"
+ resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz#f33b2159f0532a3f3107a272c0ccfbd1ad2979ca"
+
+arrify@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
+
+asn1@~0.2.3:
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86"
+
+assert-plus@1.0.0, assert-plus@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
+
+assert-plus@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234"
+
+async-each-series@0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/async-each-series/-/async-each-series-0.1.1.tgz#7617c1917401fd8ca4a28aadce3dbae98afeb432"
+
+async-each@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
+
+async@1.5.2:
+ version "1.5.2"
+ resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
+
+asynckit@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+
+aws-sign2@~0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f"
+
+aws4@^1.2.1:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
+
+backo2@1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
+
+balanced-match@^0.4.1:
+ version "0.4.2"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
+
+base64-arraybuffer@0.1.5:
+ version "0.1.5"
+ resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
+
+base64id@0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/base64id/-/base64id-0.1.0.tgz#02ce0fdeee0cef4f40080e1e73e834f0b1bfce3f"
+
+base64id@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/base64id/-/base64id-1.0.0.tgz#47688cb99bb6804f0e06d3e763b1c32e57d8e6b6"
+
+batch@0.5.3:
+ version "0.5.3"
+ resolved "https://registry.yarnpkg.com/batch/-/batch-0.5.3.tgz#3f3414f380321743bfc1042f9a83ff1d5824d464"
+
+bcrypt-pbkdf@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d"
+ dependencies:
+ tweetnacl "^0.14.3"
+
+better-assert@~1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/better-assert/-/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522"
+ dependencies:
+ callsite "1.0.0"
+
+binary-extensions@^1.0.0:
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.8.0.tgz#48ec8d16df4377eae5fa5884682480af4d95c774"
+
+blob@0.0.4:
+ version "0.0.4"
+ resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.4.tgz#bcf13052ca54463f30f9fc7e95b9a47630a94921"
+
+block-stream@*:
+ version "0.0.9"
+ resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a"
+ dependencies:
+ inherits "~2.0.0"
+
+bluebird@^3.3.0:
+ version "3.5.0"
+ resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c"
+
+body-parser@^1.16.1:
+ version "1.17.1"
+ resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.17.1.tgz#75b3bc98ddd6e7e0d8ffe750dfaca5c66993fa47"
+ dependencies:
+ bytes "2.4.0"
+ content-type "~1.0.2"
+ debug "2.6.1"
+ depd "~1.1.0"
+ http-errors "~1.6.1"
+ iconv-lite "0.4.15"
+ on-finished "~2.3.0"
+ qs "6.4.0"
+ raw-body "~2.2.0"
+ type-is "~1.6.14"
+
+boom@2.x.x:
+ version "2.10.1"
+ resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f"
+ dependencies:
+ hoek "2.x.x"
+
+bootstrap-datepicker@~1.6.4:
+ version "1.6.4"
+ resolved "https://registry.yarnpkg.com/bootstrap-datepicker/-/bootstrap-datepicker-1.6.4.tgz#889ebeced8eaa2ff15ec1f273e4b07531cc43da0"
+ dependencies:
+ jquery ">=1.7.1"
+
+bootstrap-select@~1.10.0:
+ version "1.10.0"
+ resolved "https://registry.yarnpkg.com/bootstrap-select/-/bootstrap-select-1.10.0.tgz#9499679e70004dbc00872522322d3b93a0f132e4"
+ dependencies:
+ jquery ">=1.8"
+
+bootstrap-switch@~3.3.4:
+ version "3.3.4"
+ resolved "https://registry.yarnpkg.com/bootstrap-switch/-/bootstrap-switch-3.3.4.tgz#70e0aeb2a877c0dc766991de108e2170fc29a2ff"
+
+bootstrap-touchspin@~3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/bootstrap-touchspin/-/bootstrap-touchspin-3.1.1.tgz#9779deac72aaf577e5e762b8512c747c871d9597"
+
+bootstrap@3.3.x, bootstrap@^3.0, bootstrap@~3.3.7:
+ version "3.3.7"
+ resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-3.3.7.tgz#5a389394549f23330875a3b150656574f8a9eb71"
+
+brace-expansion@^1.1.7:
+ version "1.1.7"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.7.tgz#3effc3c50e000531fb720eaff80f0ae8ef23cf59"
+ dependencies:
+ balanced-match "^0.4.1"
+ concat-map "0.0.1"
+
+braces@^0.1.2:
+ version "0.1.5"
+ resolved "https://registry.yarnpkg.com/braces/-/braces-0.1.5.tgz#c085711085291d8b75fdd74eab0f8597280711e6"
+ dependencies:
+ expand-range "^0.1.0"
+
+braces@^1.8.2:
+ version "1.8.5"
+ resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7"
+ dependencies:
+ expand-range "^1.8.1"
+ preserve "^0.2.0"
+ repeat-element "^1.1.2"
+
+browser-sync-client@2.5.0:
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/browser-sync-client/-/browser-sync-client-2.5.0.tgz#29eb4b22a53bd528ee12d97448e0f52576022230"
+ dependencies:
+ etag "^1.7.0"
+ fresh "^0.3.0"
+
+browser-sync-ui@0.6.3:
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/browser-sync-ui/-/browser-sync-ui-0.6.3.tgz#640a537c180689303d5be92bc476b9ebc441c0bc"
+ dependencies:
+ async-each-series "0.1.1"
+ connect-history-api-fallback "^1.1.0"
+ immutable "^3.7.6"
+ server-destroy "1.0.1"
+ stream-throttle "^0.1.3"
+ weinre "^2.0.0-pre-I0Z7U9OV"
+
+browser-sync@^2.18.5:
+ version "2.18.11"
+ resolved "https://registry.yarnpkg.com/browser-sync/-/browser-sync-2.18.11.tgz#333600f2efa256ecd22ced2561dfc77dcad11ea5"
+ dependencies:
+ browser-sync-client "2.5.0"
+ browser-sync-ui "0.6.3"
+ bs-recipes "1.3.4"
+ chokidar "1.7.0"
+ connect "3.5.0"
+ dev-ip "^1.0.1"
+ easy-extender "2.3.2"
+ eazy-logger "3.0.2"
+ emitter-steward "^1.0.0"
+ fs-extra "3.0.1"
+ http-proxy "1.15.2"
+ immutable "3.8.1"
+ localtunnel "1.8.2"
+ micromatch "2.3.11"
+ opn "4.0.2"
+ portscanner "2.1.1"
+ qs "6.2.1"
+ resp-modifier "6.0.2"
+ rx "4.1.0"
+ serve-index "1.8.0"
+ serve-static "1.12.2"
+ server-destroy "1.0.1"
+ socket.io "1.6.0"
+ socket.io-client "1.6.0"
+ ua-parser-js "0.7.12"
+ yargs "6.4.0"
+
+bs-recipes@1.3.4:
+ version "1.3.4"
+ resolved "https://registry.yarnpkg.com/bs-recipes/-/bs-recipes-1.3.4.tgz#0d2d4d48a718c8c044769fdc4f89592dc8b69585"
+
+buble@^0.12.0:
+ version "0.12.5"
+ resolved "https://registry.yarnpkg.com/buble/-/buble-0.12.5.tgz#c66ffe92f9f4a3c65d3256079b711e2bd0bc5013"
+ dependencies:
+ acorn "^3.1.0"
+ acorn-jsx "^3.0.1"
+ acorn-object-spread "^1.0.0"
+ chalk "^1.1.3"
+ magic-string "^0.14.0"
+ minimist "^1.2.0"
+ os-homedir "^1.0.1"
+
+bubleify@^0.5.1:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/bubleify/-/bubleify-0.5.1.tgz#f65c47cee31b80cad8b9e747bbe187d7fe51e927"
+ dependencies:
+ buble "^0.12.0"
+ object-assign "^4.0.1"
+
+buffer-shims@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51"
+
+builtin-modules@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
+
+bytes@2.4.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.4.0.tgz#7d97196f9d5baf7f6935e25985549edd2a6c2339"
+
+c3@~0.4.11:
+ version "0.4.11"
+ resolved "https://registry.yarnpkg.com/c3/-/c3-0.4.11.tgz#3f2b6c7bd33577a3473dd61176735565b4d0f3ce"
+ dependencies:
+ d3 "~3.5.0"
+
+callsite@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20"
+
+camelcase@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39"
+
+camelcase@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
+
+canonical-path@0.0.2:
+ version "0.0.2"
+ resolved "https://registry.yarnpkg.com/canonical-path/-/canonical-path-0.0.2.tgz#e31eb937a8c93ee2a01df1839794721902874574"
+
+caseless@~0.11.0:
+ version "0.11.0"
+ resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7"
+
+caseless@~0.12.0:
+ version "0.12.0"
+ resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
+
+chalk@0.5.1:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.5.1.tgz#663b3a648b68b55d04690d49167aa837858f2174"
+ dependencies:
+ ansi-styles "^1.1.0"
+ escape-string-regexp "^1.0.0"
+ has-ansi "^0.1.0"
+ strip-ansi "^0.3.0"
+ supports-color "^0.2.0"
+
+chalk@^1.1.1, chalk@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
+ dependencies:
+ ansi-styles "^2.2.1"
+ escape-string-regexp "^1.0.2"
+ has-ansi "^2.0.0"
+ strip-ansi "^3.0.0"
+ supports-color "^2.0.0"
+
+chokidar@1.7.0, chokidar@^1.4.1:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468"
+ dependencies:
+ anymatch "^1.3.0"
+ async-each "^1.0.0"
+ glob-parent "^2.0.0"
+ inherits "^2.0.1"
+ is-binary-path "^1.0.0"
+ is-glob "^2.0.0"
+ path-is-absolute "^1.0.0"
+ readdirp "^2.0.0"
+ optionalDependencies:
+ fsevents "^1.0.0"
+
+cliui@^3.0.3, cliui@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d"
+ dependencies:
+ string-width "^1.0.1"
+ strip-ansi "^3.0.1"
+ wrap-ansi "^2.0.0"
+
+co@^4.6.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
+
+code-point-at@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
+
+colors@^1.1.0, colors@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
+
+combine-lists@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/combine-lists/-/combine-lists-1.0.1.tgz#458c07e09e0d900fc28b70a3fec2dacd1d2cb7f6"
+ dependencies:
+ lodash "^4.5.0"
+
+combined-stream@^1.0.5, combined-stream@~1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009"
+ dependencies:
+ delayed-stream "~1.0.0"
+
+commander@2.6.0, commander@^2.2.0:
+ version "2.6.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.6.0.tgz#9df7e52fb2a0cb0fb89058ee80c3104225f37e1d"
+
+commander@^2.9.0:
+ version "2.9.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4"
+ dependencies:
+ graceful-readlink ">= 1.0.0"
+
+component-bind@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1"
+
+component-emitter@1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.1.2.tgz#296594f2753daa63996d2af08d15a95116c9aec3"
+
+component-emitter@1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
+
+component-inherit@0.0.3:
+ version "0.0.3"
+ resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143"
+
+concat-map@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+
+concurrently@^3.2.0:
+ version "3.4.0"
+ resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-3.4.0.tgz#60662b3defde07375bae19aac0ab780ec748ba79"
+ dependencies:
+ chalk "0.5.1"
+ commander "2.6.0"
+ date-fns "^1.23.0"
+ lodash "^4.5.1"
+ rx "2.3.24"
+ spawn-command "^0.0.2-1"
+ supports-color "^3.2.3"
+ tree-kill "^1.1.0"
+
+connect-history-api-fallback@^1.1.0, connect-history-api-fallback@^1.2.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.3.0.tgz#e51d17f8f0ef0db90a64fdb47de3051556e9f169"
+
+connect-logger@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/connect-logger/-/connect-logger-0.0.1.tgz#4d999978a1d20bb4608e7cd434d741652255174b"
+ dependencies:
+ moment "*"
+
+connect@1.x:
+ version "1.9.2"
+ resolved "https://registry.yarnpkg.com/connect/-/connect-1.9.2.tgz#42880a22e9438ae59a8add74e437f58ae8e52807"
+ dependencies:
+ formidable "1.0.x"
+ mime ">= 0.0.1"
+ qs ">= 0.4.0"
+
+connect@3.5.0:
+ version "3.5.0"
+ resolved "https://registry.yarnpkg.com/connect/-/connect-3.5.0.tgz#b357525a0b4c1f50599cd983e1d9efeea9677198"
+ dependencies:
+ debug "~2.2.0"
+ finalhandler "0.5.0"
+ parseurl "~1.3.1"
+ utils-merge "1.0.0"
+
+connect@^3.6.0:
+ version "3.6.1"
+ resolved "https://registry.yarnpkg.com/connect/-/connect-3.6.1.tgz#b7760693a74f0454face1d9378edb3f885b43227"
+ dependencies:
+ debug "2.6.3"
+ finalhandler "1.0.1"
+ parseurl "~1.3.1"
+ utils-merge "1.0.0"
+
+console-control-strings@^1.0.0, console-control-strings@~1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
+
+content-type@~1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed"
+
+cookie@0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb"
+
+core-js@^2.2.0, core-js@^2.4.1:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e"
+
+core-util-is@~1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+
+cryptiles@2.x.x:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8"
+ dependencies:
+ boom "2.x.x"
+
+custom-event@~1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425"
+
+d3@~3.5.0, d3@~3.5.17:
+ version "3.5.17"
+ resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz#bc46748004378b21a360c9fc7cf5231790762fb8"
+
+dashdash@^1.12.0:
+ version "1.14.1"
+ resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
+ dependencies:
+ assert-plus "^1.0.0"
+
+datatables.net-bs@>=1.10.9:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/datatables.net-bs/-/datatables.net-bs-2.1.1.tgz#704108972891949d094bf44f53d0654d9fee7bce"
+ dependencies:
+ datatables.net ">=1.10.9"
+ jquery ">=1.7"
+
+datatables.net-colreorder-bs@~1.3.2:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/datatables.net-colreorder-bs/-/datatables.net-colreorder-bs-1.3.3.tgz#3a9dcb08deebeb5d854079591e06e493ad793a53"
+ dependencies:
+ datatables.net-bs ">=1.10.9"
+ datatables.net-colreorder ">=1.2.0"
+ jquery ">=1.7"
+
+datatables.net-colreorder@>=1.2.0, datatables.net-colreorder@~1.3.2:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/datatables.net-colreorder/-/datatables.net-colreorder-1.3.3.tgz#fc762e350f94224cb2cd45c2b962261a0fffef72"
+ dependencies:
+ datatables.net ">=1.10.9"
+ jquery ">=1.7"
+
+datatables.net-select@~1.2.0:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/datatables.net-select/-/datatables.net-select-1.2.2.tgz#9651793f52899f4e5745c0720821e8b8286fbf45"
+ dependencies:
+ datatables.net ">=1.10.9"
+ jquery ">=1.7"
+
+datatables.net@>=1.10.9:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/datatables.net/-/datatables.net-2.1.1.tgz#6f1103ef08054642aecf85e7e25efd592822ee78"
+ dependencies:
+ jquery ">=1.7"
+
+datatables@~1.10.12:
+ version "1.10.13"
+ resolved "https://registry.yarnpkg.com/datatables/-/datatables-1.10.13.tgz#9bb2dec6f7dcf02049a00e4f0e7d3fe009c39346"
+ dependencies:
+ jquery ">=1.7"
+
+date-fns@^1.23.0:
+ version "1.28.4"
+ resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.28.4.tgz#7938aec34ba31fc8bd134d2344bc2e0bbfd95165"
+
+debug@2, debug@2.6.3, debug@^2.2.0:
+ version "2.6.3"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.3.tgz#0f7eb8c30965ec08c72accfa0130c8b79984141d"
+ dependencies:
+ ms "0.7.2"
+
+debug@2.2.0, debug@~2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da"
+ dependencies:
+ ms "0.7.1"
+
+debug@2.3.3:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.3.3.tgz#40c453e67e6e13c901ddec317af8986cda9eff8c"
+ dependencies:
+ ms "0.7.2"
+
+debug@2.6.1:
+ version "2.6.1"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.1.tgz#79855090ba2c4e3115cc7d8769491d58f0491351"
+ dependencies:
+ ms "0.7.2"
+
+debug@2.6.4:
+ version "2.6.4"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.4.tgz#7586a9b3c39741c0282ae33445c4e8ac74734fe0"
+ dependencies:
+ ms "0.7.3"
+
+decamelize@^1.0.0, decamelize@^1.1.1:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
+
+deep-extend@~0.4.0:
+ version "0.4.2"
+ resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f"
+
+del@^2.2.0:
+ version "2.2.2"
+ resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8"
+ dependencies:
+ globby "^5.0.0"
+ is-path-cwd "^1.0.0"
+ is-path-in-cwd "^1.0.0"
+ object-assign "^4.0.1"
+ pify "^2.0.0"
+ pinkie-promise "^2.0.0"
+ rimraf "^2.2.8"
+
+delayed-stream@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+
+delegates@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
+
+depd@1.1.0, depd@~1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3"
+
+destroy@~1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
+
+dev-ip@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/dev-ip/-/dev-ip-1.0.1.tgz#a76a3ed1855be7a012bb8ac16cb80f3c00dc28f0"
+
+di@^0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c"
+
+diff@^2.2.1:
+ version "2.2.3"
+ resolved "https://registry.yarnpkg.com/diff/-/diff-2.2.3.tgz#60eafd0d28ee906e4e8ff0a52c1229521033bf99"
+
+dom-serialize@^2.2.0:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b"
+ dependencies:
+ custom-event "~1.0.0"
+ ent "~2.2.0"
+ extend "^3.0.0"
+ void-elements "^2.0.0"
+
+drmonty-datatables-colvis@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/drmonty-datatables-colvis/-/drmonty-datatables-colvis-1.1.2.tgz#96ab9edfb48643cc2edda3f87b88933cdee8127c"
+ dependencies:
+ jquery ">=1.7.0"
+
+easy-extender@2.3.2:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/easy-extender/-/easy-extender-2.3.2.tgz#3d3248febe2b159607316d8f9cf491c16648221d"
+ dependencies:
+ lodash "^3.10.1"
+
+eazy-logger@3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/eazy-logger/-/eazy-logger-3.0.2.tgz#a325aa5e53d13a2225889b2ac4113b2b9636f4fc"
+ dependencies:
+ tfunk "^3.0.1"
+
+ecc-jsbn@~0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505"
+ dependencies:
+ jsbn "~0.1.0"
+
+ee-first@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
+
+emitter-steward@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/emitter-steward/-/emitter-steward-1.0.0.tgz#f3411ade9758a7565df848b2da0cbbd1b46cbd64"
+
+encodeurl@~1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20"
+
+engine.io-client@1.8.0:
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-1.8.0.tgz#7b730e4127414087596d9be3c88d2bc5fdb6cf5c"
+ dependencies:
+ component-emitter "1.2.1"
+ component-inherit "0.0.3"
+ debug "2.3.3"
+ engine.io-parser "1.3.1"
+ has-cors "1.1.0"
+ indexof "0.0.1"
+ parsejson "0.0.3"
+ parseqs "0.0.5"
+ parseuri "0.0.5"
+ ws "1.1.1"
+ xmlhttprequest-ssl "1.5.3"
+ yeast "0.1.2"
+
+engine.io-client@1.8.3:
+ version "1.8.3"
+ resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-1.8.3.tgz#1798ed93451246453d4c6f635d7a201fe940d5ab"
+ dependencies:
+ component-emitter "1.2.1"
+ component-inherit "0.0.3"
+ debug "2.3.3"
+ engine.io-parser "1.3.2"
+ has-cors "1.1.0"
+ indexof "0.0.1"
+ parsejson "0.0.3"
+ parseqs "0.0.5"
+ parseuri "0.0.5"
+ ws "1.1.2"
+ xmlhttprequest-ssl "1.5.3"
+ yeast "0.1.2"
+
+engine.io-parser@1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-1.3.1.tgz#9554f1ae33107d6fbd170ca5466d2f833f6a07cf"
+ dependencies:
+ after "0.8.1"
+ arraybuffer.slice "0.0.6"
+ base64-arraybuffer "0.1.5"
+ blob "0.0.4"
+ has-binary "0.1.6"
+ wtf-8 "1.0.0"
+
+engine.io-parser@1.3.2:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-1.3.2.tgz#937b079f0007d0893ec56d46cb220b8cb435220a"
+ dependencies:
+ after "0.8.2"
+ arraybuffer.slice "0.0.6"
+ base64-arraybuffer "0.1.5"
+ blob "0.0.4"
+ has-binary "0.1.7"
+ wtf-8 "1.0.0"
+
+engine.io@1.8.0:
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-1.8.0.tgz#3eeb5f264cb75dbbec1baaea26d61f5a4eace2aa"
+ dependencies:
+ accepts "1.3.3"
+ base64id "0.1.0"
+ cookie "0.3.1"
+ debug "2.3.3"
+ engine.io-parser "1.3.1"
+ ws "1.1.1"
+
+engine.io@1.8.3:
+ version "1.8.3"
+ resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-1.8.3.tgz#8de7f97895d20d39b85f88eeee777b2bd42b13d4"
+ dependencies:
+ accepts "1.3.3"
+ base64id "1.0.0"
+ cookie "0.3.1"
+ debug "2.3.3"
+ engine.io-parser "1.3.2"
+ ws "1.1.2"
+
+ent@~2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d"
+
+eonasdan-bootstrap-datetimepicker@~4.15.35:
+ version "4.15.35"
+ resolved "https://registry.yarnpkg.com/eonasdan-bootstrap-datetimepicker/-/eonasdan-bootstrap-datetimepicker-4.15.35.tgz#aec96923e95b6b51e1c04a999ce017929d7396f8"
+ dependencies:
+ bootstrap "^3.0"
+ jquery ">=1.8.3 <2.2.0"
+ moment "~2.8"
+
+error-ex@^1.2.0:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc"
+ dependencies:
+ is-arrayish "^0.2.1"
+
+escape-html@~1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
+
+escape-string-regexp@^1.0.0, escape-string-regexp@^1.0.2:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+
+etag@^1.7.0, etag@~1.8.0:
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.0.tgz#6f631aef336d6c46362b51764044ce216be3c051"
+
+eventemitter3@1.x.x:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508"
+
+exit@^0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
+
+expand-braces@^0.1.1:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/expand-braces/-/expand-braces-0.1.2.tgz#488b1d1d2451cb3d3a6b192cfc030f44c5855fea"
+ dependencies:
+ array-slice "^0.2.3"
+ array-unique "^0.2.1"
+ braces "^0.1.2"
+
+expand-brackets@^0.1.4:
+ version "0.1.5"
+ resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b"
+ dependencies:
+ is-posix-bracket "^0.1.0"
+
+expand-range@^0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-0.1.1.tgz#4cb8eda0993ca56fa4f41fc42f3cbb4ccadff044"
+ dependencies:
+ is-number "^0.1.1"
+ repeat-string "^0.2.2"
+
+expand-range@^1.8.1:
+ version "1.8.2"
+ resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337"
+ dependencies:
+ fill-range "^2.1.0"
+
+express@2.5.x:
+ version "2.5.11"
+ resolved "https://registry.yarnpkg.com/express/-/express-2.5.11.tgz#4ce8ea1f3635e69e49f0ebb497b6a4b0a51ce6f0"
+ dependencies:
+ connect "1.x"
+ mime "1.2.4"
+ mkdirp "0.3.0"
+ qs "0.4.x"
+
+extend@3, extend@^3.0.0, extend@~3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
+
+extglob@^0.3.1:
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1"
+ dependencies:
+ is-extglob "^1.0.0"
+
+extsprintf@1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550"
+
+filename-regex@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26"
+
+fill-range@^2.1.0:
+ version "2.2.3"
+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723"
+ dependencies:
+ is-number "^2.1.0"
+ isobject "^2.0.0"
+ randomatic "^1.1.3"
+ repeat-element "^1.1.2"
+ repeat-string "^1.5.2"
+
+finalhandler@0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-0.5.0.tgz#e9508abece9b6dba871a6942a1d7911b91911ac7"
+ dependencies:
+ debug "~2.2.0"
+ escape-html "~1.0.3"
+ on-finished "~2.3.0"
+ statuses "~1.3.0"
+ unpipe "~1.0.0"
+
+finalhandler@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.0.1.tgz#bcd15d1689c0e5ed729b6f7f541a6df984117db8"
+ dependencies:
+ debug "2.6.3"
+ encodeurl "~1.0.1"
+ escape-html "~1.0.3"
+ on-finished "~2.3.0"
+ parseurl "~1.3.1"
+ statuses "~1.3.1"
+ unpipe "~1.0.0"
+
+find-up@^1.0.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
+ dependencies:
+ path-exists "^2.0.0"
+ pinkie-promise "^2.0.0"
+
+findup-sync@~0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.3.0.tgz#37930aa5d816b777c03445e1966cc6790a4c0b16"
+ dependencies:
+ glob "~5.0.0"
+
+font-awesome@^4.7.0:
+ version "4.7.0"
+ resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133"
+
+for-in@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
+
+for-own@^0.1.4:
+ version "0.1.5"
+ resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce"
+ dependencies:
+ for-in "^1.0.1"
+
+forever-agent@~0.6.1:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
+
+form-data@~2.1.1:
+ version "2.1.4"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1"
+ dependencies:
+ asynckit "^0.4.0"
+ combined-stream "^1.0.5"
+ mime-types "^2.1.12"
+
+formidable@1.0.x:
+ version "1.0.17"
+ resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.0.17.tgz#ef5491490f9433b705faa77249c99029ae348559"
+
+fresh@0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.0.tgz#f474ca5e6a9246d6fd8e0953cfa9b9c805afa78e"
+
+fresh@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.3.0.tgz#651f838e22424e7566de161d8358caa199f83d4f"
+
+fs-access@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/fs-access/-/fs-access-1.0.1.tgz#d6a87f262271cefebec30c553407fb995da8777a"
+ dependencies:
+ null-check "^1.0.0"
+
+fs-extra@3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-3.0.1.tgz#3794f378c58b342ea7dbbb23095109c4b3b62291"
+ dependencies:
+ graceful-fs "^4.1.2"
+ jsonfile "^3.0.0"
+ universalify "^0.1.0"
+
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+
+fsevents@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.1.tgz#f19fd28f43eeaf761680e519a203c4d0b3d31aff"
+ dependencies:
+ nan "^2.3.0"
+ node-pre-gyp "^0.6.29"
+
+fstream-ignore@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105"
+ dependencies:
+ fstream "^1.0.0"
+ inherits "2"
+ minimatch "^3.0.0"
+
+fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2:
+ version "1.0.11"
+ resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171"
+ dependencies:
+ graceful-fs "^4.1.2"
+ inherits "~2.0.0"
+ mkdirp ">=0.5 0"
+ rimraf "2"
+
+gauge@~2.7.3:
+ version "2.7.4"
+ resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
+ dependencies:
+ aproba "^1.0.3"
+ console-control-strings "^1.0.0"
+ has-unicode "^2.0.0"
+ object-assign "^4.1.0"
+ signal-exit "^3.0.0"
+ string-width "^1.0.1"
+ strip-ansi "^3.0.1"
+ wide-align "^1.1.0"
+
+generate-function@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74"
+
+generate-object-property@^1.1.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0"
+ dependencies:
+ is-property "^1.0.0"
+
+get-caller-file@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5"
+
+getpass@^0.1.1:
+ version "0.1.7"
+ resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
+ dependencies:
+ assert-plus "^1.0.0"
+
+glob-base@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4"
+ dependencies:
+ glob-parent "^2.0.0"
+ is-glob "^2.0.0"
+
+glob-parent@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28"
+ dependencies:
+ is-glob "^2.0.0"
+
+glob@^3.2.11:
+ version "3.2.11"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-3.2.11.tgz#4a973f635b9190f715d10987d5c00fd2815ebe3d"
+ dependencies:
+ inherits "2"
+ minimatch "0.3"
+
+glob@^7.0.3, glob@^7.0.5, glob@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.2"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+glob@~5.0.0:
+ version "5.0.15"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1"
+ dependencies:
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "2 || 3"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+globby@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d"
+ dependencies:
+ array-union "^1.0.1"
+ arrify "^1.0.0"
+ glob "^7.0.3"
+ object-assign "^4.0.1"
+ pify "^2.0.0"
+ pinkie-promise "^2.0.0"
+
+google-code-prettify@~1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/google-code-prettify/-/google-code-prettify-1.0.5.tgz#9f477f224dbfa62372e5ef803a7e157410400084"
+
+graceful-fs@^4.1.2, graceful-fs@^4.1.6:
+ version "4.1.11"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
+
+"graceful-readlink@>= 1.0.0":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
+
+har-schema@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e"
+
+har-validator@~2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d"
+ dependencies:
+ chalk "^1.1.1"
+ commander "^2.9.0"
+ is-my-json-valid "^2.12.4"
+ pinkie-promise "^2.0.0"
+
+har-validator@~4.2.1:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a"
+ dependencies:
+ ajv "^4.9.1"
+ har-schema "^1.0.5"
+
+has-ansi@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-0.1.0.tgz#84f265aae8c0e6a88a12d7022894b7568894c62e"
+ dependencies:
+ ansi-regex "^0.2.0"
+
+has-ansi@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
+ dependencies:
+ ansi-regex "^2.0.0"
+
+has-binary@0.1.6:
+ version "0.1.6"
+ resolved "https://registry.yarnpkg.com/has-binary/-/has-binary-0.1.6.tgz#25326f39cfa4f616ad8787894e3af2cfbc7b6e10"
+ dependencies:
+ isarray "0.0.1"
+
+has-binary@0.1.7:
+ version "0.1.7"
+ resolved "https://registry.yarnpkg.com/has-binary/-/has-binary-0.1.7.tgz#68e61eb16210c9545a0a5cce06a873912fe1e68c"
+ dependencies:
+ isarray "0.0.1"
+
+has-cors@1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39"
+
+has-flag@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
+
+has-unicode@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
+
+hawk@~3.1.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4"
+ dependencies:
+ boom "2.x.x"
+ cryptiles "2.x.x"
+ hoek "2.x.x"
+ sntp "1.x.x"
+
+hoek@2.x.x:
+ version "2.16.3"
+ resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
+
+hosted-git-info@^2.1.4:
+ version "2.4.2"
+ resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.4.2.tgz#0076b9f46a270506ddbaaea56496897460612a67"
+
+http-errors@~1.5.0:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.5.1.tgz#788c0d2c1de2c81b9e6e8c01843b6b97eb920750"
+ dependencies:
+ inherits "2.0.3"
+ setprototypeof "1.0.2"
+ statuses ">= 1.3.1 < 2"
+
+http-errors@~1.6.1:
+ version "1.6.1"
+ resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.1.tgz#5f8b8ed98aca545656bf572997387f904a722257"
+ dependencies:
+ depd "1.1.0"
+ inherits "2.0.3"
+ setprototypeof "1.0.3"
+ statuses ">= 1.3.1 < 2"
+
+http-proxy@1.15.2:
+ version "1.15.2"
+ resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.15.2.tgz#642fdcaffe52d3448d2bda3b0079e9409064da31"
+ dependencies:
+ eventemitter3 "1.x.x"
+ requires-port "1.x.x"
+
+http-proxy@^1.13.0:
+ version "1.16.2"
+ resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.16.2.tgz#06dff292952bf64dbe8471fa9df73066d4f37742"
+ dependencies:
+ eventemitter3 "1.x.x"
+ requires-port "1.x.x"
+
+http-signature@~1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf"
+ dependencies:
+ assert-plus "^0.2.0"
+ jsprim "^1.2.2"
+ sshpk "^1.7.0"
+
+https-proxy-agent@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz#35f7da6c48ce4ddbfa264891ac593ee5ff8671e6"
+ dependencies:
+ agent-base "2"
+ debug "2"
+ extend "3"
+
+iconv-lite@0.4.15:
+ version "0.4.15"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb"
+
+immutable@3.8.1, immutable@^3.7.6:
+ version "3.8.1"
+ resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.1.tgz#200807f11ab0f72710ea485542de088075f68cd2"
+
+indexof@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d"
+
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
+
+ini@^1.3.4, ini@~1.3.0:
+ version "1.3.4"
+ resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
+
+invert-kv@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
+
+is-arrayish@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+
+is-binary-path@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898"
+ dependencies:
+ binary-extensions "^1.0.0"
+
+is-buffer@^1.1.5:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc"
+
+is-builtin-module@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe"
+ dependencies:
+ builtin-modules "^1.0.0"
+
+is-dotfile@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.2.tgz#2c132383f39199f8edc268ca01b9b007d205cc4d"
+
+is-equal-shallow@^0.1.3:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534"
+ dependencies:
+ is-primitive "^2.0.0"
+
+is-extendable@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
+
+is-extglob@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0"
+
+is-fullwidth-code-point@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
+ dependencies:
+ number-is-nan "^1.0.0"
+
+is-glob@^2.0.0, is-glob@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863"
+ dependencies:
+ is-extglob "^1.0.0"
+
+is-my-json-valid@^2.12.4:
+ version "2.16.0"
+ resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz#f079dd9bfdae65ee2038aae8acbc86ab109e3693"
+ dependencies:
+ generate-function "^2.0.0"
+ generate-object-property "^1.1.0"
+ jsonpointer "^4.0.0"
+ xtend "^4.0.0"
+
+is-number-like@^1.0.3:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/is-number-like/-/is-number-like-1.0.7.tgz#a38d6b0fd2cd4282449128859eed86c03fd23552"
+ dependencies:
+ bubleify "^0.5.1"
+ lodash.isfinite "^3.3.2"
+
+is-number@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-0.1.1.tgz#69a7af116963d47206ec9bd9b48a14216f1e3806"
+
+is-number@^2.0.2, is-number@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f"
+ dependencies:
+ kind-of "^3.0.2"
+
+is-path-cwd@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d"
+
+is-path-in-cwd@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc"
+ dependencies:
+ is-path-inside "^1.0.0"
+
+is-path-inside@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f"
+ dependencies:
+ path-is-inside "^1.0.1"
+
+is-posix-bracket@^0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4"
+
+is-primitive@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575"
+
+is-property@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84"
+
+is-typedarray@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
+
+is-utf8@^0.2.0:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
+
+isarray@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
+
+isarray@1.0.0, isarray@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+
+isbinaryfile@^3.0.0:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.2.tgz#4a3e974ec0cba9004d3fc6cde7209ea69368a621"
+
+isexe@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+
+isobject@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
+ dependencies:
+ isarray "1.0.0"
+
+isstream@~0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
+
+jasmine-core@~2.4.0, jasmine-core@~2.4.1:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.4.1.tgz#6f83ab3a0f16951722ce07d206c773d57cc838be"
+
+jasmine@2.4.1:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-2.4.1.tgz#9016dda453213d27ac6d43dc4ea97315a189085e"
+ dependencies:
+ exit "^0.1.2"
+ glob "^3.2.11"
+ jasmine-core "~2.4.0"
+
+jasminewd2@0.0.10:
+ version "0.0.10"
+ resolved "https://registry.yarnpkg.com/jasminewd2/-/jasminewd2-0.0.10.tgz#94f48ae2bc946cad643035467b4bb7ea9c1075ef"
+
+jodid25519@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967"
+ dependencies:
+ jsbn "~0.1.0"
+
+jquery-match-height@~0.7.0:
+ version "0.7.2"
+ resolved "https://registry.yarnpkg.com/jquery-match-height/-/jquery-match-height-0.7.2.tgz#f8d9f3ba5314daab109cf07408674be204be5f0e"
+
+"jquery@>= 2.1.x", jquery@>=1.7, jquery@>=1.7.0, jquery@>=1.7.1, jquery@>=1.8, "jquery@>=1.8.3 <2.2.0", jquery@~2.1.4:
+ version "2.1.4"
+ resolved "https://registry.yarnpkg.com/jquery/-/jquery-2.1.4.tgz#228bde698a0c61431dc2630a6a154f15890d2317"
+
+jsbn@~0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
+
+json-schema@0.2.3:
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
+
+json-stable-stringify@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af"
+ dependencies:
+ jsonify "~0.0.0"
+
+json-stringify-safe@~5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
+
+json3@3.3.2:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1"
+
+jsonfile@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-3.0.0.tgz#92e7c7444e5ffd5fa32e6a9ae8b85034df8347d0"
+ optionalDependencies:
+ graceful-fs "^4.1.6"
+
+jsonify@~0.0.0:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
+
+jsonpointer@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9"
+
+jsprim@^1.2.2:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.0.tgz#a3b87e40298d8c380552d8cc7628a0bb95a22918"
+ dependencies:
+ assert-plus "1.0.0"
+ extsprintf "1.0.2"
+ json-schema "0.2.3"
+ verror "1.3.6"
+
+karma-chrome-launcher@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-2.1.1.tgz#216879c68ac04d8d5140e99619ba04b59afd46cf"
+ dependencies:
+ fs-access "^1.0.0"
+ which "^1.2.1"
+
+karma-cli@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/karma-cli/-/karma-cli-1.0.1.tgz#ae6c3c58a313a1d00b45164c455b9b86ce17f960"
+ dependencies:
+ resolve "^1.1.6"
+
+karma-jasmine-html-reporter@^0.2.2:
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-0.2.2.tgz#48a8e5ef18807617ee2b5e33c1194c35b439524c"
+ dependencies:
+ karma-jasmine "^1.0.2"
+
+karma-jasmine@^1.0.2:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-1.1.0.tgz#22e4c06bf9a182e5294d1f705e3733811b810acf"
+
+karma@^1.3.0:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/karma/-/karma-1.7.0.tgz#6f7a1a406446fa2e187ec95398698f4cee476269"
+ dependencies:
+ bluebird "^3.3.0"
+ body-parser "^1.16.1"
+ chokidar "^1.4.1"
+ colors "^1.1.0"
+ combine-lists "^1.0.0"
+ connect "^3.6.0"
+ core-js "^2.2.0"
+ di "^0.0.1"
+ dom-serialize "^2.2.0"
+ expand-braces "^0.1.1"
+ glob "^7.1.1"
+ graceful-fs "^4.1.2"
+ http-proxy "^1.13.0"
+ isbinaryfile "^3.0.0"
+ lodash "^3.8.0"
+ log4js "^0.6.31"
+ mime "^1.3.4"
+ minimatch "^3.0.2"
+ optimist "^0.6.1"
+ qjobs "^1.1.4"
+ range-parser "^1.2.0"
+ rimraf "^2.6.0"
+ safe-buffer "^5.0.1"
+ socket.io "1.7.3"
+ source-map "^0.5.3"
+ tmp "0.0.31"
+ useragent "^2.1.12"
+
+kind-of@^3.0.2:
+ version "3.2.2"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
+ dependencies:
+ is-buffer "^1.1.5"
+
+lcid@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
+ dependencies:
+ invert-kv "^1.0.0"
+
+limiter@^1.0.5:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/limiter/-/limiter-1.1.0.tgz#6e2bd12ca3fcdaa11f224e2e53c896df3f08d913"
+
+lite-server@^2.2.2:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/lite-server/-/lite-server-2.3.0.tgz#5b4cc8f5d5fd4836105480ab2ac48a3a0de2b0c8"
+ dependencies:
+ browser-sync "^2.18.5"
+ connect-history-api-fallback "^1.2.0"
+ connect-logger "0.0.1"
+ lodash "^4.11.1"
+ minimist "1.2.0"
+
+load-json-file@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
+ dependencies:
+ graceful-fs "^4.1.2"
+ parse-json "^2.2.0"
+ pify "^2.0.0"
+ pinkie-promise "^2.0.0"
+ strip-bom "^2.0.0"
+
+localtunnel@1.8.2:
+ version "1.8.2"
+ resolved "https://registry.yarnpkg.com/localtunnel/-/localtunnel-1.8.2.tgz#913051e8328b51f75ad8a22ad1f5c5b8c599a359"
+ dependencies:
+ debug "2.2.0"
+ openurl "1.1.0"
+ request "2.78.0"
+ yargs "3.29.0"
+
+lodash.isfinite@^3.3.2:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz#fb89b65a9a80281833f0b7478b3a5104f898ebb3"
+
+lodash@^3.10.1, lodash@^3.8.0:
+ version "3.10.1"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
+
+lodash@^4.11.1, lodash@^4.16.4, lodash@^4.5.0, lodash@^4.5.1:
+ version "4.17.4"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
+
+log4js@^0.6.31:
+ version "0.6.38"
+ resolved "https://registry.yarnpkg.com/log4js/-/log4js-0.6.38.tgz#2c494116695d6fb25480943d3fc872e662a522fd"
+ dependencies:
+ readable-stream "~1.0.2"
+ semver "~4.3.3"
+
+lru-cache@2, lru-cache@2.2.x:
+ version "2.2.4"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.2.4.tgz#6c658619becf14031d0d0b594b16042ce4dc063d"
+
+magic-string@^0.14.0:
+ version "0.14.0"
+ resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.14.0.tgz#57224aef1701caeed273b17a39a956e72b172462"
+ dependencies:
+ vlq "^0.2.1"
+
+media-typer@0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
+
+micromatch@2.3.11, micromatch@^2.1.5:
+ version "2.3.11"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565"
+ dependencies:
+ arr-diff "^2.0.0"
+ array-unique "^0.2.1"
+ braces "^1.8.2"
+ expand-brackets "^0.1.4"
+ extglob "^0.3.1"
+ filename-regex "^2.0.0"
+ is-extglob "^1.0.0"
+ is-glob "^2.0.1"
+ kind-of "^3.0.2"
+ normalize-path "^2.0.1"
+ object.omit "^2.0.0"
+ parse-glob "^3.0.4"
+ regex-cache "^0.4.2"
+
+mime-db@~1.27.0:
+ version "1.27.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1"
+
+mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.7:
+ version "2.1.15"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc92aed"
+ dependencies:
+ mime-db "~1.27.0"
+
+mime@1.2.4:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/mime/-/mime-1.2.4.tgz#11b5fdaf29c2509255176b80ad520294f5de92b7"
+
+mime@1.3.4:
+ version "1.3.4"
+ resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53"
+
+"mime@>= 0.0.1", mime@^1.3.4:
+ version "1.3.6"
+ resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.6.tgz#591d84d3653a6b0b4a3b9df8de5aa8108e72e5e0"
+
+minimatch@0.3:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.3.0.tgz#275d8edaac4f1bb3326472089e7949c8394699dd"
+ dependencies:
+ lru-cache "2"
+ sigmund "~1.0.0"
+
+"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+ dependencies:
+ brace-expansion "^1.1.7"
+
+minimist@0.0.8:
+ version "0.0.8"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
+
+minimist@1.2.0, minimist@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
+
+minimist@~0.0.1:
+ version "0.0.10"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
+
+mkdirp@0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e"
+
+"mkdirp@>=0.5 0", mkdirp@^0.5.1:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
+ dependencies:
+ minimist "0.0.8"
+
+moment@*, moment@~2.14.1:
+ version "2.14.1"
+ resolved "https://registry.yarnpkg.com/moment/-/moment-2.14.1.tgz#b35b27c47e57ed2ddc70053d6b07becdb291741c"
+
+moment@~2.8:
+ version "2.8.4"
+ resolved "https://registry.yarnpkg.com/moment/-/moment-2.8.4.tgz#cc174aabb19223efff5699a9467805a2789838bf"
+
+ms@0.7.1:
+ version "0.7.1"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
+
+ms@0.7.2:
+ version "0.7.2"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765"
+
+ms@0.7.3:
+ version "0.7.3"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.3.tgz#708155a5e44e33f5fd0fc53e81d0d40a91be1fff"
+
+ms@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-1.0.0.tgz#59adcd22edc543f7b5381862d31387b1f4bc9473"
+
+nan@^2.3.0:
+ version "2.6.2"
+ resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45"
+
+negotiator@0.6.1:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
+
+node-pre-gyp@^0.6.29:
+ version "0.6.34"
+ resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.34.tgz#94ad1c798a11d7fc67381b50d47f8cc18d9799f7"
+ dependencies:
+ mkdirp "^0.5.1"
+ nopt "^4.0.1"
+ npmlog "^4.0.2"
+ rc "^1.1.7"
+ request "^2.81.0"
+ rimraf "^2.6.1"
+ semver "^5.3.0"
+ tar "^2.2.1"
+ tar-pack "^3.4.0"
+
+node-uuid@~1.4.7:
+ version "1.4.8"
+ resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.8.tgz#b040eb0923968afabf8d32fb1f17f1167fdab907"
+
+nopt@3.0.x:
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
+ dependencies:
+ abbrev "1"
+
+nopt@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d"
+ dependencies:
+ abbrev "1"
+ osenv "^0.1.4"
+
+normalize-package-data@^2.3.2:
+ version "2.3.8"
+ resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.3.8.tgz#d819eda2a9dedbd1ffa563ea4071d936782295bb"
+ dependencies:
+ hosted-git-info "^2.1.4"
+ is-builtin-module "^1.0.0"
+ semver "2 || 3 || 4 || 5"
+ validate-npm-package-license "^3.0.1"
+
+normalize-path@^2.0.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
+ dependencies:
+ remove-trailing-separator "^1.0.1"
+
+npmlog@^4.0.2:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.0.tgz#dc59bee85f64f00ed424efb2af0783df25d1c0b5"
+ dependencies:
+ are-we-there-yet "~1.1.2"
+ console-control-strings "~1.1.0"
+ gauge "~2.7.3"
+ set-blocking "~2.0.0"
+
+null-check@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/null-check/-/null-check-1.0.0.tgz#977dffd7176012b9ec30d2a39db5cf72a0439edd"
+
+number-is-nan@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
+
+oauth-sign@~0.8.1:
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
+
+object-assign@4.1.0, object-assign@^4.0.1, object-assign@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0"
+
+object-component@0.0.3:
+ version "0.0.3"
+ resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291"
+
+object-path@^0.9.0:
+ version "0.9.2"
+ resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.9.2.tgz#0fd9a74fc5fad1ae3968b586bda5c632bd6c05a5"
+
+object.omit@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa"
+ dependencies:
+ for-own "^0.1.4"
+ is-extendable "^0.1.1"
+
+on-finished@~2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
+ dependencies:
+ ee-first "1.1.1"
+
+once@^1.3.0, once@^1.3.3:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ dependencies:
+ wrappy "1"
+
+openurl@1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/openurl/-/openurl-1.1.0.tgz#e2f2189d999c04823201f083f0f1a7cd8903187a"
+
+opn@4.0.2:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/opn/-/opn-4.0.2.tgz#7abc22e644dff63b0a96d5ab7f2790c0f01abc95"
+ dependencies:
+ object-assign "^4.0.1"
+ pinkie-promise "^2.0.0"
+
+optimist@^0.6.1, optimist@~0.6.0:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
+ dependencies:
+ minimist "~0.0.1"
+ wordwrap "~0.0.2"
+
+options@>=0.0.5:
+ version "0.0.6"
+ resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f"
+
+os-homedir@^1.0.0, os-homedir@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
+
+os-locale@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9"
+ dependencies:
+ lcid "^1.0.0"
+
+os-tmpdir@^1.0.0, os-tmpdir@~1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
+
+osenv@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644"
+ dependencies:
+ os-homedir "^1.0.0"
+ os-tmpdir "^1.0.0"
+
+parse-glob@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c"
+ dependencies:
+ glob-base "^0.3.0"
+ is-dotfile "^1.0.0"
+ is-extglob "^1.0.0"
+ is-glob "^2.0.0"
+
+parse-json@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
+ dependencies:
+ error-ex "^1.2.0"
+
+parsejson@0.0.3:
+ version "0.0.3"
+ resolved "https://registry.yarnpkg.com/parsejson/-/parsejson-0.0.3.tgz#ab7e3759f209ece99437973f7d0f1f64ae0e64ab"
+ dependencies:
+ better-assert "~1.0.0"
+
+parseqs@0.0.5:
+ version "0.0.5"
+ resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d"
+ dependencies:
+ better-assert "~1.0.0"
+
+parseuri@0.0.5:
+ version "0.0.5"
+ resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a"
+ dependencies:
+ better-assert "~1.0.0"
+
+parseurl@~1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56"
+
+path-exists@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
+ dependencies:
+ pinkie-promise "^2.0.0"
+
+path-is-absolute@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+
+path-is-inside@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
+
+path-parse@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1"
+
+path-type@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
+ dependencies:
+ graceful-fs "^4.1.2"
+ pify "^2.0.0"
+ pinkie-promise "^2.0.0"
+
+patternfly-bootstrap-combobox@~1.1.7:
+ version "1.1.7"
+ resolved "https://registry.yarnpkg.com/patternfly-bootstrap-combobox/-/patternfly-bootstrap-combobox-1.1.7.tgz#6a5e3ccd1170c21b3c4b4aa168a7413e1ddbb6e1"
+
+patternfly-bootstrap-treeview@~2.1.0:
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/patternfly-bootstrap-treeview/-/patternfly-bootstrap-treeview-2.1.3.tgz#6a7c5f37073a40ea1f6eed95630f29e08e0f30b0"
+ dependencies:
+ bootstrap "3.3.x"
+ jquery ">= 2.1.x"
+
+patternfly@^3.23.2:
+ version "3.24.0"
+ resolved "https://registry.yarnpkg.com/patternfly/-/patternfly-3.24.0.tgz#75551ce20667b025a534f29f30aa20d42afc708b"
+ dependencies:
+ bootstrap "~3.3.7"
+ font-awesome "^4.7.0"
+ jquery "~2.1.4"
+ optionalDependencies:
+ bootstrap-datepicker "~1.6.4"
+ bootstrap-select "~1.10.0"
+ bootstrap-switch "~3.3.4"
+ bootstrap-touchspin "~3.1.1"
+ c3 "~0.4.11"
+ d3 "~3.5.17"
+ datatables "~1.10.12"
+ datatables.net-colreorder "~1.3.2"
+ datatables.net-colreorder-bs "~1.3.2"
+ datatables.net-select "~1.2.0"
+ drmonty-datatables-colvis "~1.1.2"
+ eonasdan-bootstrap-datetimepicker "~4.15.35"
+ google-code-prettify "~1.0.5"
+ jquery-match-height "~0.7.0"
+ moment "~2.14.1"
+ patternfly-bootstrap-combobox "~1.1.7"
+ patternfly-bootstrap-treeview "~2.1.0"
+
+performance-now@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5"
+
+pify@^2.0.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
+
+pinkie-promise@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
+ dependencies:
+ pinkie "^2.0.0"
+
+pinkie@^2.0.0:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
+
+portscanner@2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/portscanner/-/portscanner-2.1.1.tgz#eabb409e4de24950f5a2a516d35ae769343fbb96"
+ dependencies:
+ async "1.5.2"
+ is-number-like "^1.0.3"
+
+preserve@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
+
+process-nextick-args@~1.0.6:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3"
+
+protractor@~4.0.14:
+ version "4.0.14"
+ resolved "https://registry.yarnpkg.com/protractor/-/protractor-4.0.14.tgz#efc4a877fac3a182a9dded26cd5869f4762fd172"
+ dependencies:
+ "@types/jasmine" "^2.5.36"
+ "@types/node" "^6.0.46"
+ "@types/q" "^0.0.32"
+ "@types/selenium-webdriver" "2.53.37"
+ adm-zip "0.4.7"
+ chalk "^1.1.3"
+ glob "^7.0.3"
+ jasmine "2.4.1"
+ jasminewd2 "0.0.10"
+ optimist "~0.6.0"
+ q "1.4.1"
+ saucelabs "~1.3.0"
+ selenium-webdriver "2.53.3"
+ source-map-support "~0.4.0"
+ webdriver-manager "^10.3.0"
+
+punycode@^1.4.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
+
+q@1.4.1, q@^1.4.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e"
+
+qjobs@^1.1.4:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.1.5.tgz#659de9f2cf8dcc27a1481276f205377272382e73"
+
+qs@0.4.x:
+ version "0.4.2"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-0.4.2.tgz#3cac4c861e371a8c9c4770ac23cda8de639b8e5f"
+
+qs@6.2.1:
+ version "6.2.1"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.1.tgz#ce03c5ff0935bc1d9d69a9f14cbd18e568d67625"
+
+qs@6.4.0, "qs@>= 0.4.0", qs@~6.4.0:
+ version "6.4.0"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
+
+qs@~6.3.0:
+ version "6.3.2"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.2.tgz#e75bd5f6e268122a2a0e0bda630b2550c166502c"
+
+randomatic@^1.1.3:
+ version "1.1.6"
+ resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.6.tgz#110dcabff397e9dcff7c0789ccc0a49adf1ec5bb"
+ dependencies:
+ is-number "^2.0.2"
+ kind-of "^3.0.2"
+
+range-parser@^1.2.0, range-parser@~1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e"
+
+raw-body@~2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.2.0.tgz#994976cf6a5096a41162840492f0bdc5d6e7fb96"
+ dependencies:
+ bytes "2.4.0"
+ iconv-lite "0.4.15"
+ unpipe "1.0.0"
+
+rc@^1.1.7:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95"
+ dependencies:
+ deep-extend "~0.4.0"
+ ini "~1.3.0"
+ minimist "^1.2.0"
+ strip-json-comments "~2.0.1"
+
+read-pkg-up@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
+ dependencies:
+ find-up "^1.0.0"
+ read-pkg "^1.0.0"
+
+read-pkg@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28"
+ dependencies:
+ load-json-file "^1.0.0"
+ normalize-package-data "^2.3.2"
+ path-type "^1.0.0"
+
+readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.4:
+ version "2.2.9"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.9.tgz#cf78ec6f4a6d1eb43d26488cac97f042e74b7fc8"
+ dependencies:
+ buffer-shims "~1.0.0"
+ core-util-is "~1.0.0"
+ inherits "~2.0.1"
+ isarray "~1.0.0"
+ process-nextick-args "~1.0.6"
+ string_decoder "~1.0.0"
+ util-deprecate "~1.0.1"
+
+readable-stream@~1.0.2:
+ version "1.0.34"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c"
+ dependencies:
+ core-util-is "~1.0.0"
+ inherits "~2.0.1"
+ isarray "0.0.1"
+ string_decoder "~0.10.x"
+
+readdirp@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78"
+ dependencies:
+ graceful-fs "^4.1.2"
+ minimatch "^3.0.2"
+ readable-stream "^2.0.2"
+ set-immediate-shim "^1.0.1"
+
+regex-cache@^0.4.2:
+ version "0.4.3"
+ resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145"
+ dependencies:
+ is-equal-shallow "^0.1.3"
+ is-primitive "^2.0.0"
+
+remove-trailing-separator@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.0.1.tgz#615ebb96af559552d4bf4057c8436d486ab63cc4"
+
+repeat-element@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a"
+
+repeat-string@^0.2.2:
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-0.2.2.tgz#c7a8d3236068362059a7e4651fc6884e8b1fb4ae"
+
+repeat-string@^1.5.2:
+ version "1.6.1"
+ resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
+
+request@2.78.0:
+ version "2.78.0"
+ resolved "https://registry.yarnpkg.com/request/-/request-2.78.0.tgz#e1c8dec346e1c81923b24acdb337f11decabe9cc"
+ dependencies:
+ aws-sign2 "~0.6.0"
+ aws4 "^1.2.1"
+ caseless "~0.11.0"
+ combined-stream "~1.0.5"
+ extend "~3.0.0"
+ forever-agent "~0.6.1"
+ form-data "~2.1.1"
+ har-validator "~2.0.6"
+ hawk "~3.1.3"
+ http-signature "~1.1.0"
+ is-typedarray "~1.0.0"
+ isstream "~0.1.2"
+ json-stringify-safe "~5.0.1"
+ mime-types "~2.1.7"
+ node-uuid "~1.4.7"
+ oauth-sign "~0.8.1"
+ qs "~6.3.0"
+ stringstream "~0.0.4"
+ tough-cookie "~2.3.0"
+ tunnel-agent "~0.4.1"
+
+request@^2.78.0, request@^2.81.0:
+ version "2.81.0"
+ resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
+ dependencies:
+ aws-sign2 "~0.6.0"
+ aws4 "^1.2.1"
+ caseless "~0.12.0"
+ combined-stream "~1.0.5"
+ extend "~3.0.0"
+ forever-agent "~0.6.1"
+ form-data "~2.1.1"
+ har-validator "~4.2.1"
+ hawk "~3.1.3"
+ http-signature "~1.1.0"
+ is-typedarray "~1.0.0"
+ isstream "~0.1.2"
+ json-stringify-safe "~5.0.1"
+ mime-types "~2.1.7"
+ oauth-sign "~0.8.1"
+ performance-now "^0.2.0"
+ qs "~6.4.0"
+ safe-buffer "^5.0.1"
+ stringstream "~0.0.4"
+ tough-cookie "~2.3.0"
+ tunnel-agent "^0.6.0"
+ uuid "^3.0.0"
+
+require-directory@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+
+require-main-filename@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
+
+requires-port@1.x.x:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
+
+resolve@^1.1.6, resolve@^1.1.7:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.3.tgz#655907c3469a8680dc2de3a275a8fdd69691f0e5"
+ dependencies:
+ path-parse "^1.0.5"
+
+resp-modifier@6.0.2:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/resp-modifier/-/resp-modifier-6.0.2.tgz#b124de5c4fbafcba541f48ffa73970f4aa456b4f"
+ dependencies:
+ debug "^2.2.0"
+ minimatch "^3.0.2"
+
+rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.1:
+ version "2.6.1"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d"
+ dependencies:
+ glob "^7.0.5"
+
+rx@2.3.24:
+ version "2.3.24"
+ resolved "https://registry.yarnpkg.com/rx/-/rx-2.3.24.tgz#14f950a4217d7e35daa71bbcbe58eff68ea4b2b7"
+
+rx@4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782"
+
+rxjs@^5.4.2:
+ version "5.4.2"
+ resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.4.2.tgz#2a3236fcbf03df57bae06fd6972fd99e5c08fcf7"
+ dependencies:
+ symbol-observable "^1.0.1"
+
+safe-buffer@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7"
+
+saucelabs@~1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/saucelabs/-/saucelabs-1.3.0.tgz#d240e8009df7fa87306ec4578a69ba3b5c424fee"
+ dependencies:
+ https-proxy-agent "^1.0.0"
+
+sax@0.6.x:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/sax/-/sax-0.6.1.tgz#563b19c7c1de892e09bfc4f2fc30e3c27f0952b9"
+
+selenium-webdriver@2.53.3:
+ version "2.53.3"
+ resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-2.53.3.tgz#d29ff5a957dff1a1b49dc457756e4e4bfbdce085"
+ dependencies:
+ adm-zip "0.4.4"
+ rimraf "^2.2.8"
+ tmp "0.0.24"
+ ws "^1.0.1"
+ xml2js "0.4.4"
+
+"semver@2 || 3 || 4 || 5", semver@^5.3.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
+
+semver@~4.3.3:
+ version "4.3.6"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da"
+
+semver@~5.0.1:
+ version "5.0.3"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a"
+
+send@0.15.2:
+ version "0.15.2"
+ resolved "https://registry.yarnpkg.com/send/-/send-0.15.2.tgz#f91fab4403bcf87e716f70ceb5db2f578bdc17d6"
+ dependencies:
+ debug "2.6.4"
+ depd "~1.1.0"
+ destroy "~1.0.4"
+ encodeurl "~1.0.1"
+ escape-html "~1.0.3"
+ etag "~1.8.0"
+ fresh "0.5.0"
+ http-errors "~1.6.1"
+ mime "1.3.4"
+ ms "1.0.0"
+ on-finished "~2.3.0"
+ range-parser "~1.2.0"
+ statuses "~1.3.1"
+
+serve-index@1.8.0:
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.8.0.tgz#7c5d96c13fb131101f93c1c5774f8516a1e78d3b"
+ dependencies:
+ accepts "~1.3.3"
+ batch "0.5.3"
+ debug "~2.2.0"
+ escape-html "~1.0.3"
+ http-errors "~1.5.0"
+ mime-types "~2.1.11"
+ parseurl "~1.3.1"
+
+serve-static@1.12.2:
+ version "1.12.2"
+ resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.12.2.tgz#e546e2726081b81b4bcec8e90808ebcdd323afba"
+ dependencies:
+ encodeurl "~1.0.1"
+ escape-html "~1.0.3"
+ parseurl "~1.3.1"
+ send "0.15.2"
+
+server-destroy@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/server-destroy/-/server-destroy-1.0.1.tgz#f13bf928e42b9c3e79383e61cc3998b5d14e6cdd"
+
+set-blocking@^2.0.0, set-blocking@~2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
+
+set-immediate-shim@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61"
+
+setprototypeof@1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.2.tgz#81a552141ec104b88e89ce383103ad5c66564d08"
+
+setprototypeof@1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04"
+
+sigmund@~1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590"
+
+signal-exit@^3.0.0:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
+
+sntp@1.x.x:
+ version "1.0.9"
+ resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198"
+ dependencies:
+ hoek "2.x.x"
+
+socket.io-adapter@0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz#cb6d4bb8bec81e1078b99677f9ced0046066bb8b"
+ dependencies:
+ debug "2.3.3"
+ socket.io-parser "2.3.1"
+
+socket.io-client@1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-1.6.0.tgz#5b668f4f771304dfeed179064708386fa6717853"
+ dependencies:
+ backo2 "1.0.2"
+ component-bind "1.0.0"
+ component-emitter "1.2.1"
+ debug "2.3.3"
+ engine.io-client "1.8.0"
+ has-binary "0.1.7"
+ indexof "0.0.1"
+ object-component "0.0.3"
+ parseuri "0.0.5"
+ socket.io-parser "2.3.1"
+ to-array "0.1.4"
+
+socket.io-client@1.7.3:
+ version "1.7.3"
+ resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-1.7.3.tgz#b30e86aa10d5ef3546601c09cde4765e381da377"
+ dependencies:
+ backo2 "1.0.2"
+ component-bind "1.0.0"
+ component-emitter "1.2.1"
+ debug "2.3.3"
+ engine.io-client "1.8.3"
+ has-binary "0.1.7"
+ indexof "0.0.1"
+ object-component "0.0.3"
+ parseuri "0.0.5"
+ socket.io-parser "2.3.1"
+ to-array "0.1.4"
+
+socket.io-parser@2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-2.3.1.tgz#dd532025103ce429697326befd64005fcfe5b4a0"
+ dependencies:
+ component-emitter "1.1.2"
+ debug "2.2.0"
+ isarray "0.0.1"
+ json3 "3.3.2"
+
+socket.io@1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-1.6.0.tgz#3e40d932637e6bd923981b25caf7c53e83b6e2e1"
+ dependencies:
+ debug "2.3.3"
+ engine.io "1.8.0"
+ has-binary "0.1.7"
+ object-assign "4.1.0"
+ socket.io-adapter "0.5.0"
+ socket.io-client "1.6.0"
+ socket.io-parser "2.3.1"
+
+socket.io@1.7.3:
+ version "1.7.3"
+ resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-1.7.3.tgz#b8af9caba00949e568e369f1327ea9be9ea2461b"
+ dependencies:
+ debug "2.3.3"
+ engine.io "1.8.3"
+ has-binary "0.1.7"
+ object-assign "4.1.0"
+ socket.io-adapter "0.5.0"
+ socket.io-client "1.7.3"
+ socket.io-parser "2.3.1"
+
+source-map-support@~0.4.0:
+ version "0.4.15"
+ resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.15.tgz#03202df65c06d2bd8c7ec2362a193056fef8d3b1"
+ dependencies:
+ source-map "^0.5.6"
+
+source-map@^0.5.3, source-map@^0.5.6:
+ version "0.5.6"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
+
+spawn-command@^0.0.2-1:
+ version "0.0.2"
+ resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2.tgz#9544e1a43ca045f8531aac1a48cb29bdae62338e"
+
+spdx-correct@~1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40"
+ dependencies:
+ spdx-license-ids "^1.0.2"
+
+spdx-expression-parse@~1.0.0:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c"
+
+spdx-license-ids@^1.0.2:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57"
+
+sprintf-js@^1.0.3:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.0.tgz#cffcaf702daf65ea39bb4e0fa2b299cec1a1be46"
+
+sshpk@^1.7.0:
+ version "1.13.0"
+ resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.0.tgz#ff2a3e4fd04497555fed97b39a0fd82fafb3a33c"
+ dependencies:
+ asn1 "~0.2.3"
+ assert-plus "^1.0.0"
+ dashdash "^1.12.0"
+ getpass "^0.1.1"
+ optionalDependencies:
+ bcrypt-pbkdf "^1.0.0"
+ ecc-jsbn "~0.1.1"
+ jodid25519 "^1.0.0"
+ jsbn "~0.1.0"
+ tweetnacl "~0.14.0"
+
+"statuses@>= 1.3.1 < 2", statuses@~1.3.0, statuses@~1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e"
+
+stream-throttle@^0.1.3:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/stream-throttle/-/stream-throttle-0.1.3.tgz#add57c8d7cc73a81630d31cd55d3961cfafba9c3"
+ dependencies:
+ commander "^2.2.0"
+ limiter "^1.0.5"
+
+string-width@^1.0.1, string-width@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
+ dependencies:
+ code-point-at "^1.0.0"
+ is-fullwidth-code-point "^1.0.0"
+ strip-ansi "^3.0.0"
+
+string_decoder@~0.10.x:
+ version "0.10.31"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
+
+string_decoder@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.0.tgz#f06f41157b664d86069f84bdbdc9b0d8ab281667"
+ dependencies:
+ buffer-shims "~1.0.0"
+
+stringstream@~0.0.4:
+ version "0.0.5"
+ resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878"
+
+strip-ansi@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.3.0.tgz#25f48ea22ca79187f3174a4db8759347bb126220"
+ dependencies:
+ ansi-regex "^0.2.1"
+
+strip-ansi@^3.0.0, strip-ansi@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
+ dependencies:
+ ansi-regex "^2.0.0"
+
+strip-bom@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
+ dependencies:
+ is-utf8 "^0.2.0"
+
+strip-json-comments@~2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
+
+supports-color@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-0.2.0.tgz#d92de2694eb3f67323973d7ae3d8b55b4c22190a"
+
+supports-color@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
+
+supports-color@^3.2.3:
+ version "3.2.3"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
+ dependencies:
+ has-flag "^1.0.0"
+
+symbol-observable@^1.0.1:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d"
+
+systemjs@^0.20.17:
+ version "0.20.17"
+ resolved "https://registry.yarnpkg.com/systemjs/-/systemjs-0.20.17.tgz#b3143bb7e02d2f41b9a640351a06024b7b63ae59"
+
+tar-pack@^3.4.0:
+ version "3.4.0"
+ resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.0.tgz#23be2d7f671a8339376cbdb0b8fe3fdebf317984"
+ dependencies:
+ debug "^2.2.0"
+ fstream "^1.0.10"
+ fstream-ignore "^1.0.5"
+ once "^1.3.3"
+ readable-stream "^2.1.4"
+ rimraf "^2.5.1"
+ tar "^2.2.1"
+ uid-number "^0.0.6"
+
+tar@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1"
+ dependencies:
+ block-stream "*"
+ fstream "^1.0.2"
+ inherits "2"
+
+tfunk@^3.0.1:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/tfunk/-/tfunk-3.1.0.tgz#38e4414fc64977d87afdaa72facb6d29f82f7b5b"
+ dependencies:
+ chalk "^1.1.1"
+ object-path "^0.9.0"
+
+tmp@0.0.24:
+ version "0.0.24"
+ resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.24.tgz#d6a5e198d14a9835cc6f2d7c3d9e302428c8cf12"
+
+tmp@0.0.31, tmp@0.0.x:
+ version "0.0.31"
+ resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7"
+ dependencies:
+ os-tmpdir "~1.0.1"
+
+to-array@0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890"
+
+tough-cookie@~2.3.0:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a"
+ dependencies:
+ punycode "^1.4.1"
+
+tree-kill@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.1.0.tgz#c963dcf03722892ec59cba569e940b71954d1729"
+
+tslint@^3.15.1:
+ version "3.15.1"
+ resolved "https://registry.yarnpkg.com/tslint/-/tslint-3.15.1.tgz#da165ca93d8fdc2c086b51165ee1bacb48c98ea5"
+ dependencies:
+ colors "^1.1.2"
+ diff "^2.2.1"
+ findup-sync "~0.3.0"
+ glob "^7.0.3"
+ optimist "~0.6.0"
+ resolve "^1.1.7"
+ underscore.string "^3.3.4"
+
+tunnel-agent@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
+ dependencies:
+ safe-buffer "^5.0.1"
+
+tunnel-agent@~0.4.1:
+ version "0.4.3"
+ resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb"
+
+tweetnacl@^0.14.3, tweetnacl@~0.14.0:
+ version "0.14.5"
+ resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
+
+type-is@~1.6.14:
+ version "1.6.15"
+ resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410"
+ dependencies:
+ media-typer "0.3.0"
+ mime-types "~2.1.15"
+
+typescript@^2.4.2:
+ version "2.4.2"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.2.tgz#f8395f85d459276067c988aa41837a8f82870844"
+
+ua-parser-js@0.7.12:
+ version "0.7.12"
+ resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.12.tgz#04c81a99bdd5dc52263ea29d24c6bf8d4818a4bb"
+
+uid-number@^0.0.6:
+ version "0.0.6"
+ resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"
+
+ultron@1.0.x:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa"
+
+underscore.string@^3.3.4:
+ version "3.3.4"
+ resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-3.3.4.tgz#2c2a3f9f83e64762fdc45e6ceac65142864213db"
+ dependencies:
+ sprintf-js "^1.0.3"
+ util-deprecate "^1.0.2"
+
+underscore@1.7.x:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209"
+
+universalify@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.0.tgz#9eb1c4651debcc670cc94f1a75762332bb967778"
+
+unpipe@1.0.0, unpipe@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
+
+useragent@^2.1.12:
+ version "2.1.13"
+ resolved "https://registry.yarnpkg.com/useragent/-/useragent-2.1.13.tgz#bba43e8aa24d5ceb83c2937473e102e21df74c10"
+ dependencies:
+ lru-cache "2.2.x"
+ tmp "0.0.x"
+
+util-deprecate@^1.0.2, util-deprecate@~1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+
+utils-merge@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8"
+
+uuid@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1"
+
+validate-npm-package-license@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc"
+ dependencies:
+ spdx-correct "~1.0.0"
+ spdx-expression-parse "~1.0.0"
+
+verror@1.3.6:
+ version "1.3.6"
+ resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c"
+ dependencies:
+ extsprintf "1.0.2"
+
+vlq@^0.2.1:
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.2.tgz#e316d5257b40b86bb43cb8d5fea5d7f54d6b0ca1"
+
+void-elements@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
+
+webdriver-manager@^10.3.0:
+ version "10.3.0"
+ resolved "https://registry.yarnpkg.com/webdriver-manager/-/webdriver-manager-10.3.0.tgz#99314588a0b1dbe688c441d74288c6cb1875fa8b"
+ dependencies:
+ adm-zip "^0.4.7"
+ chalk "^1.1.1"
+ del "^2.2.0"
+ glob "^7.0.3"
+ ini "^1.3.4"
+ minimist "^1.2.0"
+ q "^1.4.1"
+ request "^2.78.0"
+ rimraf "^2.5.2"
+ semver "^5.3.0"
+
+weinre@^2.0.0-pre-I0Z7U9OV:
+ version "2.0.0-pre-I0Z7U9OV"
+ resolved "https://registry.yarnpkg.com/weinre/-/weinre-2.0.0-pre-I0Z7U9OV.tgz#fef8aa223921f7b40bbbbd4c3ed4302f6fd0a813"
+ dependencies:
+ express "2.5.x"
+ nopt "3.0.x"
+ underscore "1.7.x"
+
+which-module@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"
+
+which@^1.2.1:
+ version "1.2.14"
+ resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5"
+ dependencies:
+ isexe "^2.0.0"
+
+wide-align@^1.1.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710"
+ dependencies:
+ string-width "^1.0.2"
+
+window-size@^0.1.2:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876"
+
+window-size@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075"
+
+wordwrap@~0.0.2:
+ version "0.0.3"
+ resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
+
+wrap-ansi@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
+ dependencies:
+ string-width "^1.0.1"
+ strip-ansi "^3.0.1"
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+
+ws@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.1.tgz#082ddb6c641e85d4bb451f03d52f06eabdb1f018"
+ dependencies:
+ options ">=0.0.5"
+ ultron "1.0.x"
+
+ws@1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.2.tgz#8a244fa052401e08c9886cf44a85189e1fd4067f"
+ dependencies:
+ options ">=0.0.5"
+ ultron "1.0.x"
+
+ws@^1.0.1:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.4.tgz#57f40d036832e5f5055662a397c4de76ed66bf61"
+ dependencies:
+ options ">=0.0.5"
+ ultron "1.0.x"
+
+wtf-8@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/wtf-8/-/wtf-8-1.0.0.tgz#392d8ba2d0f1c34d1ee2d630f15d0efb68e1048a"
+
+xml2js@0.4.4:
+ version "0.4.4"
+ resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.4.tgz#3111010003008ae19240eba17497b57c729c555d"
+ dependencies:
+ sax "0.6.x"
+ xmlbuilder ">=1.0.0"
+
+xmlbuilder@>=1.0.0:
+ version "9.0.0"
+ resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.0.tgz#a9311b3f8509345700c49a8f79be06bcc5988d18"
+
+xmlhttprequest-ssl@1.5.3:
+ version "1.5.3"
+ resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz#185a888c04eca46c3e4070d99f7b49de3528992d"
+
+xtend@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
+
+y18n@^3.2.0, y18n@^3.2.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
+
+yargs-parser@^4.1.0:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-4.2.1.tgz#29cceac0dc4f03c6c87b4a9f217dd18c9f74871c"
+ dependencies:
+ camelcase "^3.0.0"
+
+yargs@3.29.0:
+ version "3.29.0"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.29.0.tgz#1aab9660eae79d8b8f675bcaeeab6ee34c2cf69c"
+ dependencies:
+ camelcase "^1.2.1"
+ cliui "^3.0.3"
+ decamelize "^1.0.0"
+ os-locale "^1.4.0"
+ window-size "^0.1.2"
+ y18n "^3.2.0"
+
+yargs@6.4.0:
+ version "6.4.0"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.4.0.tgz#816e1a866d5598ccf34e5596ddce22d92da490d4"
+ dependencies:
+ camelcase "^3.0.0"
+ cliui "^3.2.0"
+ decamelize "^1.1.1"
+ get-caller-file "^1.0.1"
+ os-locale "^1.4.0"
+ read-pkg-up "^1.0.1"
+ require-directory "^2.1.1"
+ require-main-filename "^1.0.1"
+ set-blocking "^2.0.0"
+ string-width "^1.0.2"
+ which-module "^1.0.0"
+ window-size "^0.2.0"
+ y18n "^3.2.1"
+ yargs-parser "^4.1.0"
+
+yeast@0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"
+
+zone.js@^0.8.4:
+ version "0.8.10"
+ resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.8.10.tgz#6d1b696492c029cdbe808e59e87bbd9491b98aa8"
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/theme.properties b/themes/src/main/resources/theme/keycloak-preview/account/theme.properties
new file mode 100644
index 0000000..4cd0e08
--- /dev/null
+++ b/themes/src/main/resources/theme/keycloak-preview/account/theme.properties
@@ -0,0 +1,3 @@
+parent=base
+import=login/keycloak
+deprecatedMode=false
\ No newline at end of file