keycloak-aplcache

Merge pull request #4486 from ssilvert/kc1250-big-commit KEYCLOAK-1250:

9/18/2017 5:51:18 PM

Changes

themes/pom.xml 56(+56 -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