From 3a29297e886c8f4cc247e065df9a60d6177514a2 Mon Sep 17 00:00:00 2001
From: rsanchez <rsanchez@curisit.net>
Date: Thu, 02 Mar 2017 15:52:51 +0000
Subject: [PATCH] #3527 feature - Added common components like i18n and local storage

---
 securis/src/main/webapp/src/environments/environment.prod.ts       |    3 
 securis/src/main/webapp/src/app/app.component.ts                   |   28 ++
 securis/src/main/webapp/src/environments/environment.ts            |    3 
 securis/src/main/webapp/src/lang/messages_fr.json                  |    4 
 securis/src/main/webapp/src/environments/environment.dev.ts        |    3 
 securis/src/main/webapp/src/main.ts                                |    6 
 securis/src/main/webapp/src/app/app.routes.ts                      |    4 
 securis/src/main/webapp/package.json                               |   20 +-
 securis/src/main/webapp/src/app/common/i18n.ts                     |  133 ++++++++++++++++
 securis/src/main/webapp/src/app/login.form.component.ts            |   38 ++++
 securis/src/main/webapp/src/app/login.form.html                    |   25 +++
 securis/src/main/webapp/.vscode/settings.json                      |    4 
 securis/src/main/webapp/src/app/common/default.requests.options.ts |   37 ++++
 securis/src/main/webapp/src/app/app.module.ts                      |   18 ++
 securis/src/main/webapp/src/app/user.service.ts                    |   76 +++++++++
 securis/src/main/webapp/src/lang/messages_es.json                  |    4 
 securis/src/main/webapp/src/app/pack.list.component.ts             |    1 
 securis/src/main/webapp/systemjs.config.js                         |   10 +
 securis/src/main/webapp/src/lang/messages_en.json                  |    4 
 19 files changed, 403 insertions(+), 18 deletions(-)

diff --git a/securis/src/main/webapp/.vscode/settings.json b/securis/src/main/webapp/.vscode/settings.json
new file mode 100644
index 0000000..0c807a4
--- /dev/null
+++ b/securis/src/main/webapp/.vscode/settings.json
@@ -0,0 +1,4 @@
+// Place your settings in this file to overwrite default and user settings.
+{
+   "typescript.tsdk": "./node_modules/typescript/lib"
+}
\ No newline at end of file
diff --git a/securis/src/main/webapp/package.json b/securis/src/main/webapp/package.json
index 3d12b5f..509fc75 100644
--- a/securis/src/main/webapp/package.json
+++ b/securis/src/main/webapp/package.json
@@ -1,6 +1,6 @@
 {
-  "name": "angular-quickstart",
-  "version": "1.0.0",
+  "name": "securis",
+  "version": "2.0.0",
   "description": "QuickStart package.json from the documentation, supplemented with testing support",
   "scripts": {
     "build": "tsc -p src/",
@@ -36,30 +36,32 @@
     "@covalent/core": "^1.0.0-beta.2",
     "@covalent/dynamic-forms": "^1.0.0-beta.2",
     "@covalent/http": "^1.0.0-beta.2",
+    "angular-2-local-storage": "^1.0.1",
     "angular-in-memory-web-api": "~0.2.4",
     "core-js": "^2.4.1",
     "hammerjs": "^2.0.8",
+    "ng2-toastr": "^1.5.1",
     "rxjs": "^5.0.1",
     "systemjs": "0.19.40",
     "zone.js": "^0.7.4"
   },
   "devDependencies": {
-    "concurrently": "^3.2.0",
-    "lite-server": "^2.2.2",
-    "typescript": "~2.0.10",
+    "@types/jasmine": "2.5.36",
+    "@types/node": "^6.0.46",
     "canonical-path": "0.0.2",
-    "tslint": "^3.15.1",
-    "lodash": "^4.16.4",
+    "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",
-    "@types/node": "^6.0.46",
-    "@types/jasmine": "2.5.36"
+    "tslint": "^3.15.1",
+    "typescript": "^2.2.1"
   },
   "repository": {}
 }
diff --git a/securis/src/main/webapp/src/app/app.component.ts b/securis/src/main/webapp/src/app/app.component.ts
index 74afbfe..786b8d0 100644
--- a/securis/src/main/webapp/src/app/app.component.ts
+++ b/securis/src/main/webapp/src/app/app.component.ts
@@ -1,6 +1,10 @@
-import { Component } from '@angular/core';
+import { Component, AfterViewInit } from '@angular/core';
 import { DomSanitizer } from '@angular/platform-browser';
 import { MdIconRegistry } from '@angular/material';
+import { UserService } from './user.service';
+import { LocalStorageService } from 'angular-2-local-storage';
+import { TdMediaService } from '@covalent/core';
+import { Router } from '@angular/router';
 
 // https://github.com/Teradata/covalent-quickstart/tree/develop/src/app
 // https://teradata.github.io/covalent-quickstart/#/
@@ -11,11 +15,18 @@
 })
 
 
-export class AppComponent {
+export class AppComponent implements AfterViewInit {
 
-  constructor(private _iconRegistry: MdIconRegistry,
-              private _domSanitizer: DomSanitizer) {
-                
+  constructor(private userService: UserService,
+              private router: Router,
+              private media: TdMediaService,
+              private _iconRegistry: MdIconRegistry,
+              private _domSanitizer: DomSanitizer,
+              private store: LocalStorageService) {
+      this.registerIcons();
+  }
+
+  private registerIcons() : void {
       this._iconRegistry.addSvgIconInNamespace('assets', 'covalent',
         this._domSanitizer.bypassSecurityTrustResourceUrl('https://raw.githubusercontent.com/Teradata/covalent-quickstart/develop/src/assets/icons/covalent.svg'));
     this._iconRegistry.addSvgIconInNamespace('assets', 'teradata',
@@ -36,4 +47,11 @@
       this._domSanitizer.bypassSecurityTrustResourceUrl('assets/icons/querygrid.svg'));
   }
 
+  ngAfterViewInit(): void {
+      this.media.broadcast();
+      this.userService.isLoggedIn().subscribe(authOk => {
+        this.router.navigateByUrl(authOk ? 'packs' : 'login');
+      }, 
+      err => /* Show message */ this.router.navigateByUrl('login'));
+  }
 }
diff --git a/securis/src/main/webapp/src/app/app.module.ts b/securis/src/main/webapp/src/app/app.module.ts
index 38fc432..9dc09d5 100644
--- a/securis/src/main/webapp/src/app/app.module.ts
+++ b/securis/src/main/webapp/src/app/app.module.ts
@@ -7,30 +7,46 @@
 import {CovalentHttpModule} from '@covalent/http';
 import {CovalentDynamicFormsModule} from '@covalent/dynamic-forms'; 
 
+import { LocalStorageModule } from 'angular-2-local-storage';
+import { ToastModule } from 'ng2-toastr/ng2-toastr';
+
 import { AppComponent }  from './app.component';
+import { UserService }  from './user.service';
 import { PackListComponent }  from './pack.list.component';
 import { HeroDetailComponent } from './detail.component';
+import { LoginFormComponent } from './login.form.component';
 
 import { appRoutes, appRoutingProviders } from './app.routes';
+import { requestOptionsProvider } from './common/default.requests.options';
+import { LocaleServiceModule } from './common/i18n';
 
 @NgModule({
   imports: [
+    LocalStorageModule.withConfig({
+            prefix: 'securis',
+            storageType: 'localStorage'
+        }),
     BrowserModule,
     FormsModule,
-    MaterialModule.forRoot(),
+    MaterialModule,
     CovalentCoreModule.forRoot(),
     CovalentHttpModule.forRoot(),
     CovalentDynamicFormsModule.forRoot(),
+    ToastModule.forRoot(),
+    LocaleServiceModule,
     appRoutes,
   ],
   declarations: [
     HeroDetailComponent,
     PackListComponent,
+    LoginFormComponent,
     AppComponent
   ],
   bootstrap: [ AppComponent ],
   providers: [
+    UserService,
     appRoutingProviders,
+    requestOptionsProvider,
   ]
 })
 
diff --git a/securis/src/main/webapp/src/app/app.routes.ts b/securis/src/main/webapp/src/app/app.routes.ts
index 4078919..a8c5fac 100644
--- a/securis/src/main/webapp/src/app/app.routes.ts
+++ b/securis/src/main/webapp/src/app/app.routes.ts
@@ -2,10 +2,12 @@
 
 import { AppComponent } from './app.component';
 import { PackListComponent } from './pack.list.component';
+import { LoginFormComponent } from './login.form.component';
 
 const routes: Routes = [
-  {path: '', redirectTo: '/packs', pathMatch: 'full'}, 
+ // {path: '', redirectTo: '/packs', pathMatch: 'full'}, 
   {path: 'packs', component: PackListComponent },
+  {path: 'login', component: LoginFormComponent }
 ];
 /*    {path: 'product', component: DashboardProductComponent, children: [
       {path: '', component: ProductOverviewComponent},
diff --git a/securis/src/main/webapp/src/app/common/default.requests.options.ts b/securis/src/main/webapp/src/app/common/default.requests.options.ts
new file mode 100644
index 0000000..62dbe13
--- /dev/null
+++ b/securis/src/main/webapp/src/app/common/default.requests.options.ts
@@ -0,0 +1,37 @@
+import { LocalStorageService } from 'angular-2-local-storage';
+import { Injectable } from '@angular/core';
+import { BaseRequestOptions, RequestOptions, Request, XHRBackend, XHRConnection} from '@angular/http';
+
+
+// TODO: Chnage this to use Covalent Http helper service
+// https://www.npmjs.com/package/@covalent/http 
+
+@Injectable()
+export class DefaultRequestOptions extends BaseRequestOptions {
+
+  constructor(private store: LocalStorageService) {
+    super();
+
+    // Set the default 'Content-Type' header
+    this.headers.set('Content-Type', 'application/json');
+    let token = this.store.get<string>('token');
+    if (token) {
+        this.headers.set('X-SECURIS-TOKEN', token);
+    }    
+    
+  }
+}
+
+@Injectable()
+export class ApiXHRBackend extends XHRBackend {
+    createConnection(request: Request): XHRConnection {
+        if (request.url.startsWith('/')){
+           // request.url = '/securis' + request.url;     // prefix base url
+        }
+        return super.createConnection(request);
+    }
+}
+
+export const requestOptionsProvider = { provide: RequestOptions, useClass: DefaultRequestOptions };
+
+export const requestBackendProvider = { provide: XHRBackend, useClass: ApiXHRBackend };
diff --git a/securis/src/main/webapp/src/app/common/i18n.ts b/securis/src/main/webapp/src/app/common/i18n.ts
new file mode 100644
index 0000000..f7ca62c
--- /dev/null
+++ b/securis/src/main/webapp/src/app/common/i18n.ts
@@ -0,0 +1,133 @@
+import { Http } from '@angular/http';
+import { ModuleWithProviders, NgModule, Inject, Injectable, Directive, ElementRef, HostListener, Input, Renderer } from '@angular/core';
+declare var navigator:any;
+
+// Use as reference: https://github.com/ngx-translate/core/tree/master/src
+@Injectable()
+export class LocaleService {
+    get URL_TPL() {return  'src/lang/messages-{0}.json'};
+    
+	private _currentLang : string = null;
+    private _messages : any = null;
+    private _elements = new Array<ElementRef>();
+    private constructor(private http: Http, @Inject('INITIAL_LANG') initLang: string) {
+		this._currentLang = initLang || this.getBrowserLang();
+    }
+    
+    get lang() : string {
+        return this._currentLang;
+    }
+
+    set lang(newLang: string) {
+        this._currentLang = newLang;
+        this.http.get(this._format(this.URL_TPL, newLang)).subscribe((data) => {
+            this._messages = data;
+            this.reloadTexts();
+        }); 
+    }
+    
+    isLoaded() : boolean {
+        return !!this._messages
+    }
+    
+    getBrowserLang() : string {
+        var l;
+        if (!navigator) return 'en';
+        
+        if (navigator.languages && navigator.languages.length) {
+            l = navigator.languages[0];
+        } else {
+            l = navigator.language || 'en';
+        }
+        l = l.replace(/-.*/gi, '');
+        if (l !== 'en' && l !== 'es')
+            return 'en';        
+        return l;
+    }
+
+    /**
+     * It works similar to MessageFormat in Java
+     */
+    _format(str: string, ...params: Array<string>) {
+        
+        return str.replace(/\{(\d+)\}/g, function(match, index) {
+            return params[index];
+        });
+    };
+
+    reloadTexts() : void {
+        this._elements.forEach((ele: ElementRef) => {
+			
+            var txt = ele.nativeElement.getAttribute('i18n');
+            if (!!txt) {
+				 ele.nativeElement.textContent = this.get(txt);
+			}
+        })
+    }
+    
+    add(ele: ElementRef) {
+        this._elements.push(ele);
+    }
+    
+    /**
+     * It accepts direct messages and templates:
+     * {
+     *  "hello": "hola",
+     *  "Hello {0}!!: "Hola {0}!!"
+     *  }
+     * $L.get('hello'); // This returns "hola"
+     * $L.get('Hello {0}!!', 'John'); // This returns: "Hola John!!" if language is spanish
+     */
+    get(msg: string) : string {
+        msg = msg.trim();
+        var trans_msg = msg;
+        
+        if (this.isLoaded()) {
+            if (this._messages[msg]) {
+                trans_msg = this._messages[msg];
+            } else if (this._messages[msg.toLowerCase()]) {
+                trans_msg = this._messages[msg.toLowerCase()];
+            } else {
+                this._currentLang !== 'es' && console.error("Missing i18 key: " + msg);
+                trans_msg = msg;
+            }
+        }
+        // Enviar evento cuando el idioma cambia al $rootScope
+        
+        if (arguments.length === 1) return trans_msg;
+        var params = Array.prototype.slice.call(arguments, 1);
+        return this._format.apply(trans_msg, params);
+    }
+    
+}
+
+
+@Directive({ selector: '[i18n]' })
+export class I18nDirective {
+    constructor(private el: ElementRef, private renderer: Renderer, private $L: LocaleService) {
+    }
+
+	ngAfterViewChecked() {
+        var txt = this.el.nativeElement.getAttribute('i18n') || this.el.nativeElement.textContent;
+        this.renderer.setElementProperty(this.el.nativeElement, 'i18n', txt.trim());
+        this.$L.add(this.el);
+
+        this.renderer.setElementProperty(this.el.nativeElement, 'innerHTML', this.$L.get(txt));
+    }
+}
+
+@NgModule({
+    providers: [ <any>LocaleService ]
+})
+export class LocaleServiceModule {
+    static withConfig(initLang?: string): ModuleWithProviders {
+        return {
+            ngModule: LocaleServiceModule,
+            providers: [
+                { provide: 'INITIAL_LANG', useValue: initLang }
+            ]
+        }
+    }
+}
+
+
diff --git a/securis/src/main/webapp/src/app/login.form.component.ts b/securis/src/main/webapp/src/app/login.form.component.ts
new file mode 100644
index 0000000..7de13b0
--- /dev/null
+++ b/securis/src/main/webapp/src/app/login.form.component.ts
@@ -0,0 +1,38 @@
+import { Http } from '@angular/http';
+import { Router } from '@angular/router';
+import { AfterViewInit, Component, Input } from '@angular/core';
+import { UserService } from './user.service';
+import { ToastsManager } from 'ng2-toastr/ng2-toastr';
+
+class LoginData {
+	username: string;
+	password: string;
+}
+
+@Component({
+  selector: 'login-form',
+  templateUrl: "src/app/login.form.html"
+})
+export class LoginFormComponent implements AfterViewInit {
+  data = new LoginData();
+
+  public constructor(private toaster: ToastsManager, 
+                      private router: Router, 
+                      private userService: UserService) {
+  }
+
+  public login() {
+    this.userService.login(this.data.username, this.data.password).subscribe(
+      token => this.toaster.info('Hola'),
+      err => this.toaster.error('Hubo un error: ' + err));
+  }
+
+  public clear() {
+    this.data.username = '';
+    this.data.password = '';
+  }
+  
+  ngAfterViewInit() {
+
+  }
+}
diff --git a/securis/src/main/webapp/src/app/login.form.html b/securis/src/main/webapp/src/app/login.form.html
new file mode 100644
index 0000000..3d9d09e
--- /dev/null
+++ b/securis/src/main/webapp/src/app/login.form.html
@@ -0,0 +1,25 @@
+<md-card>
+	<md-card-title layout="row" layout-align="start end"><md-icon class="md-icon-logo" svgSrc="assets/icons/logo.svg"></md-icon> <span class="md-app-title" i18n>
+		SeCuris Login</span></md-card-title>
+	<md-card-subtitle>Sign in via your current SeCuris account</md-card-subtitle>
+	<md-divider></md-divider>
+	<md-card-content>
+		<form #loginForm="ngForm" class="inset">
+			<div layout="row">
+				<md-input-container>
+					<input mdInput flex placeholder="Username" type="text" [(ngModel)]="data.username" name="username" required />
+				</md-input-container>
+			</div>
+			<div layout="row">
+				<md-input-container>
+				<input mdInput (keyup.enter)="loginForm.form.valid && login()" flex placeholder="Password" type="password" [(ngModel)]="data.password" name="password" required/>
+				</md-input-container>
+			</div>
+		</form>
+		<div id="invalid-auth-msg" *ngIf="invalidError" class="tc-red-600 text-center" flex i18n>The username or password is incorrect. Please try again.</div>
+	</md-card-content>
+	<md-divider></md-divider>
+	<md-card-actions>
+		<button flex [disabled]="!loginForm.form.valid" md-raised-button color="primary" (click)="login()">Sign In</button>
+	</md-card-actions>
+</md-card>
\ No newline at end of file
diff --git a/securis/src/main/webapp/src/app/pack.list.component.ts b/securis/src/main/webapp/src/app/pack.list.component.ts
index 4d51722..2355901 100644
--- a/securis/src/main/webapp/src/app/pack.list.component.ts
+++ b/securis/src/main/webapp/src/app/pack.list.component.ts
@@ -1,3 +1,4 @@
+import { Hero } from './hero';
 import { Component, AfterViewInit } from '@angular/core';
 import { TdMediaService } from '@covalent/core';
 
diff --git a/securis/src/main/webapp/src/app/user.service.ts b/securis/src/main/webapp/src/app/user.service.ts
new file mode 100644
index 0000000..38d842d
--- /dev/null
+++ b/securis/src/main/webapp/src/app/user.service.ts
@@ -0,0 +1,76 @@
+import { Injectable } from '@angular/core';
+import { Router } from '@angular/router';
+import { Location } from '@angular/common';
+import { Http, RequestOptions, Response, Headers } from '@angular/http';
+import { Observable } from 'rxjs/Observable';
+
+import { LocalStorageService } from 'angular-2-local-storage';
+
+const SECURIS_TOKEN = "X-SECURIS-TOKEN";
+
+@Injectable()
+export class UserService {
+
+  constructor(private router: Router,
+              private store: LocalStorageService, 
+              private http: Http) {
+    
+  }
+
+  public login(username: string, password: string) : Observable<string> {
+    let params = new URLSearchParams();
+    params.append('username', username);
+    params.append('password', password);
+    let options = new RequestOptions({ headers: new Headers({ "Content-Type": "application/x-www-form-urlencoded" })});
+    return this.http.post('user/login', params, options)
+                    .map((res: Response) => {
+                        let data = res.json();
+                        this.store.set('username', username);
+				                this.store.set('token', data.token);
+                        return <string>data.token;
+                      })
+                    .catch(this.handleError);
+  }
+
+  isLoggedIn() : Observable<Boolean> {
+    if (!this.existsToken()) {
+      return Observable.of(false);
+    }
+    var token = this.store.get(SECURIS_TOKEN);
+    return this.http.get('check', new RequestOptions({ headers: new Headers({ 'X-SECURIS-TOKEN': token }) }))
+                    .map((res: Response) => {
+                        let body = res.json();
+                        if (body.valid) {
+                          this.store.set('user', body.user);
+                        }
+                        return body.valid;
+                      })
+                    .catch(this.handleError)
+                    .catch(() => Observable.of(false));
+  }
+
+  existsToken() : Boolean {
+    return this.store.get(SECURIS_TOKEN) !== null;
+  }
+
+  logout() : void {
+    this.store.remove('user', 'token');
+    this.router.navigate(['Login']);
+  }
+
+  private handleError (error: Response | any) {
+    // In a real world app, we might use a remote logging infrastructure
+    let errMsg: string;
+    if (error instanceof Response) {
+      const body = error.json() || '';
+      const err = body.error || JSON.stringify(body);
+      errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
+    } else {
+      errMsg = error.message ? error.message : error.toString();
+    }
+    console.error(errMsg);
+    return Observable.throw(errMsg);
+  }
+
+}
+
diff --git a/securis/src/main/webapp/src/environments/environment.dev.ts b/securis/src/main/webapp/src/environments/environment.dev.ts
new file mode 100644
index 0000000..ffe8aed
--- /dev/null
+++ b/securis/src/main/webapp/src/environments/environment.dev.ts
@@ -0,0 +1,3 @@
+export const environment = {
+  production: false
+};
diff --git a/securis/src/main/webapp/src/environments/environment.prod.ts b/securis/src/main/webapp/src/environments/environment.prod.ts
new file mode 100644
index 0000000..3612073
--- /dev/null
+++ b/securis/src/main/webapp/src/environments/environment.prod.ts
@@ -0,0 +1,3 @@
+export const environment = {
+  production: true
+};
diff --git a/securis/src/main/webapp/src/environments/environment.ts b/securis/src/main/webapp/src/environments/environment.ts
index e69de29..ffe8aed 100644
--- a/securis/src/main/webapp/src/environments/environment.ts
+++ b/securis/src/main/webapp/src/environments/environment.ts
@@ -0,0 +1,3 @@
+export const environment = {
+  production: false
+};
diff --git a/securis/src/main/webapp/src/lang/messages_en.json b/securis/src/main/webapp/src/lang/messages_en.json
new file mode 100644
index 0000000..413715e
--- /dev/null
+++ b/securis/src/main/webapp/src/lang/messages_en.json
@@ -0,0 +1,4 @@
+{
+"": "",
+"": ""
+}
\ No newline at end of file
diff --git a/securis/src/main/webapp/src/lang/messages_es.json b/securis/src/main/webapp/src/lang/messages_es.json
new file mode 100644
index 0000000..413715e
--- /dev/null
+++ b/securis/src/main/webapp/src/lang/messages_es.json
@@ -0,0 +1,4 @@
+{
+"": "",
+"": ""
+}
\ No newline at end of file
diff --git a/securis/src/main/webapp/src/lang/messages_fr.json b/securis/src/main/webapp/src/lang/messages_fr.json
new file mode 100644
index 0000000..413715e
--- /dev/null
+++ b/securis/src/main/webapp/src/lang/messages_fr.json
@@ -0,0 +1,4 @@
+{
+"": "",
+"": ""
+}
\ No newline at end of file
diff --git a/securis/src/main/webapp/src/main.ts b/securis/src/main/webapp/src/main.ts
index 923ac5b..a75fbfe 100644
--- a/securis/src/main/webapp/src/main.ts
+++ b/securis/src/main/webapp/src/main.ts
@@ -4,6 +4,10 @@
 import { AppModule } from './app/app.module';
 
 import { enableProdMode } from '@angular/core';
-enableProdMode();
+import { environment } from './environments/environment';
+
+if (environment.production) {
+  enableProdMode();
+}
 
 platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/securis/src/main/webapp/systemjs.config.js b/securis/src/main/webapp/systemjs.config.js
index 3612986..4567190 100644
--- a/securis/src/main/webapp/systemjs.config.js
+++ b/securis/src/main/webapp/systemjs.config.js
@@ -9,11 +9,14 @@
   var COVALENT_LIBS = ['core', 'http', 'dynamic-forms'];
   var mapping = {
     // our app is within the app folder
-    main: 'src/main.js',
+     main: 'src/main.js',
     'app': 'src/app',
+    'environments': 'src/environments',
 
     // other libraries
     'rxjs': 'npm:rxjs',
+    'ng2-toastr': 'npm:ng2-toastr',
+    'angular-2-local-storage': 'npm:angular-2-local-storage/dist',
     'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js'
   }
 
@@ -32,10 +35,15 @@
     },
     map: mapping,
     packages: {
+      'environments': {
+        defaultExtension: 'js'
+      },
       'app': {
         defaultExtension: 'js'
       },
       'rxjs': { main: 'index' },
+      'angular-2-local-storage': { main: 'index', defaultExtension: 'js' },
+      'ng2-toastr': { defaultExtension: 'js' }
     }
   });
 

--
Gitblit v1.3.2