<!DOCTYPE html>
<html>

  <head>
    <base href="." />
    <title>angular2 playground</title>
    <script src="https://unpkg.com/core-js@2.4.1/client/shim.min.js"></script>
    <script src="https://unpkg.com/zone.js/dist/zone.js"></script>
    <script src="https://unpkg.com/zone.js/dist/long-stack-trace-zone.js"></script>
    <script src="https://unpkg.com/reflect-metadata@0.1.3/Reflect.js"></script>
    <script src="https://unpkg.com/systemjs@0.19.31/dist/system.js"></script>
    <script src="config.js"></script>
    <script>
    System.import('app')
      .catch(console.error.bind(console));
  </script>

  </head>

  <body>
    <my-app>
    loading...
  </my-app>
  </body>

</html>
### Angular Starter Plunker - Typescript
var ngVer = '@2.3.1';

System.config({
  //use typescript for compilation
  transpiler: 'typescript',
  //typescript compiler options
  typescriptOptions: {
    emitDecoratorMetadata: true
  },
  paths: {
    'npm:': 'https://unpkg.com/'
  },
  //map tells the System loader where to look for thingsd
  map: {
    
    'app': './src',
    
    '@angular/core': 'npm:@angular/core' + ngVer + '/bundles/core.umd.js',
    '@angular/common': 'npm:@angular/common' + ngVer + '/bundles/common.umd.js',
    '@angular/compiler': 'npm:@angular/compiler' + ngVer + '/bundles/compiler.umd.js',
    '@angular/platform-browser': 'npm:@angular/platform-browser' + ngVer + '/bundles/platform-browser.umd.js',
    '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic' + ngVer + '/bundles/platform-browser-dynamic.umd.js',
    '@angular/http': 'npm:@angular/http' + ngVer + '/bundles/http.umd.js',
    '@angular/router': 'npm:@angular/router' + '@3.3.1' + '/bundles/router.umd.js',
    '@angular/forms': 'npm:@angular/forms' + ngVer + '/bundles/forms.umd.js',
    
    '@angular/core/testing': 'npm:@angular/core' + ngVer + '/bundles/core-testing.umd.js',
    '@angular/common/testing': 'npm:@angular/common' + ngVer + '/bundles/common-testing.umd.js',
    '@angular/compiler/testing': 'npm:@angular/compiler' + ngVer + '/bundles/compiler-testing.umd.js',
    '@angular/platform-browser/testing': 'npm:@angular/platform-browser' + ngVer + '/bundles/platform-browser-testing.umd.js',
    '@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic' + ngVer + '/bundles/platform-browser-dynamic-testing.umd.js',
    '@angular/http/testing': 'npm:@angular/http' + ngVer + '/bundles/http-testing.umd.js',
    '@angular/router/testing': 'npm:@angular/router' + ngVer + '/bundles/router-testing.umd.js',

    '@angular/flex-layout': 'npm:@angular/flex-layout@2.0.0-beta.4/bundles/flex-layout.umd.js',
    
    'rxjs': 'npm:rxjs',
    'typescript': 'npm:typescript@2.0.2/lib/typescript.js',

    'lodash': 'npm:lodash@4.17.4/lodash.min.js'
  },
  //packages defines our app package
  packages: {
    app: {
      main: './main.ts',
      defaultExtension: 'ts'
    },
    rxjs: {
      defaultExtension: 'js'
    }
  }
});
//main entry point
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {AppModule} from './app';

platformBrowserDynamic().bootstrapModule(AppModule);
//our root app component
import {HostBinding, AfterViewInit, CanDeactivate, Injectable, Component, ChangeDetectorRef, NgModule, animate, style, state, transition, trigger} from '@angular/core'
import {RoutesRecognized, RouterModule, ActivatedRoute, Routes, Router} from '@angular/router';
import {BrowserModule} from '@angular/platform-browser'
import { FlexLayoutModule } from "@angular/flex-layout";
import * as _ from 'lodash';
import {Subject} from 'rxjs/Subject';

// horizontal slide animation
const statesSlidedIn = [
  state('fromLeft' , style({})),
  state('fromRight' , style({}))
];
const styleSlidedLeft = style({transform: 'translateX(-100%)', display: 'none'});
const styleSlidedRight = style({transform: 'translateX(100%)', display: 'none'});
const stateSlidedLeft = state('left', styleSlidedLeft);
const stateSlidedRight = state('right', styleSlidedRight);
const transitionsSlideLeft = [
  transition('fromLeft => void', animate('.3s ease-out', styleSlidedRight)),
  transition('void => fromLeft', [styleSlidedLeft, animate('.3s ease-out')])
];
const transitionsSlideRight = [
  transition('fromRight => void', animate('.3s ease-out', styleSlidedLeft)),
  transition('void => fromRight', [styleSlidedRight, animate('.3s ease-out')])
];
const slideHorizontal = trigger('slideHorizontal', [
  ...statesSlidedIn,
  stateSlidedLeft,
  stateSlidedRight,
  ...transitionsSlideLeft,
  ...transitionsSlideRight
]);

// a shared service providing the current slide direction
@Injectable()
class RouteSlideDirectionService {

  direction: string;

  setDirection(direction: string) {
    this.direction = direction;
  }
  getDirection(): string {
    return this.direction;
  }
}

declare abstract class WaitForChangeDetection {
  abstract waitForChangeDetection(): Promise<boolean>;
}

@Injectable()
class CanDeactivateAfterChangeDetectionGuard implements CanDeactivate<WaitForChangeDetection> {
  canDeactivate(component: WaitForChangeDetection): Promise<boolean> {
    return component.waitForChangeDetection();
  }
}

@Component({})
class WaitForChangeDetectionImpl implements AfterViewChecked, WaitForChangeDetection {
  constructor(protected cdRef: ChangeDetectorRef){
    this.viewChecked$ = new Subject<void>();
  }

  viewChecked$: Subject<void>;
  waitForChangeDetection(): Promise<boolean>{
    this.cdRef.detectChanges();
    return new Promise((resolve) => this.viewChecked$.subscribe(() => resolve(true)));
  }

  ngAfterViewChecked(){
    this.viewChecked$.next();
  }
}

@Component({})
class Slidable extends WaitForChangeDetectionImpl {
  constructor(protected cdRef: ChangeDetectorRef, private routeSlideDirectionService: RouteSlideDirectionService){
    super(cdRef);
  }

  @HostBinding('@slideHorizontal')
  get slideHorizontal(){
    let slideDirection = this.routeSlideDirectionService.getDirection();
    if(slideDirection){
      return slideDirection === 'right' ? 'fromRight' : 'fromLeft';
    }
    return null;
  }
}

// 3 components extending Slidable component
@Component({
  template: `
    <div style="text-align: center;">A</div>
  `,
  animations: [slideHorizontal],
  styles: [`
    :host {
      position: absolute;
      display: block;
      top: 0; left: 0; right: 0;
      min-height: 100%;
      background: rgba(0,255,255,.2);
    }
  `]
})
class ComponentA  extends Slidable {}

@Component({
  template: `
    <div style="text-align: center;">B</div>
  `,
  animations: [slideHorizontal],
  styles: [`
    :host {
      position: absolute;
      display: block;
      top: 0; left: 0; right: 0;
      min-height: 100%;
      background: rgba(255,0,255,.2);
    }
  `]
})
class ComponentB extends Slidable {}

@Component({
  template: `
    <div style="text-align: center;">C</div>
  `,
  animations: [slideHorizontal],
  styles: [`
    :host {
      position: absolute;
      display: block;
      top: 0; left: 0; right: 0;
      min-height: 100%;
      background: rgba(255,255,0,.2);
    }
  `]
})
class ComponentC  extends Slidable {}

const AppRoutes: Routes = [{
  path: 'a',
  component: ComponentA,
  data: {
    slideIndex: 1
  },
  canDeactivate: [CanDeactivateAfterChangeDetectionGuard]
}, {
  path: 'b',
  component: ComponentB,
  data: {
    slideIndex: 2
  },
  canDeactivate: [CanDeactivateAfterChangeDetectionGuard]
}, {
  path: 'c',
  component: ComponentC,
  data: {
    slideIndex: 3
  },
  canDeactivate: [CanDeactivateAfterChangeDetectionGuard]
}, {
  path: '**',
  redirectTo: 'a'
}];


@Component({
  selector: 'menu-bar',
  template: `
    <div fxLayout="row" style="position: relative; height: 60px; background: #5e35b1; color: #fff;">
        <div fxFlex fxLayout="column" fxLayoutAlign="center center" routerLink="/a" [routerLinkActive]="['is-active']" class="link">
            <div>A</div>
        </div>
        <div fxFlex fxLayout="column" fxLayoutAlign="center center" routerLink="/b" [routerLinkActive]="['is-active']" class="link">
            <div>B</div>
        </div>
        <div fxFlex fxLayout="column" fxLayoutAlign="center center" routerLink="/c" [routerLinkActive]="['is-active']" class="link">
            <div>C</div>
        </div>
    </div>
  `,
  styles: [`
    .link {
        cursor: pointer;
        border-radius: 4px;
    }
    .is-active {
        background-color: rgba(255,255,255,.1);
        font-weight: bold;
    }
  `]
})
class MenuBarComponent {}


@Component({
  selector: 'my-app',
  template: `
    <menu-bar></menu-bar>
    
    <div style="position: absolute; top: 60px; left: 0; right: 0; bottom: 0;">
        <router-outlet></router-outlet>
    </div>
  `,
  styles: [`
    :host {
      display: block;
      position: fixed;
      top: 0; bottom: 0; left: 0; right: 0;
    }
    
  `]
})
class App {
  constructor(private router: Router, private route: ActivatedRoute, private routeSlideDirectionService: RouteSlideDirectionService){
    this.router.events.subscribe((event) => {
      if (event instanceof RoutesRecognized) {
        let leavingSlideIndex = _.get(event, 'state.root.firstChild.data.slideIndex');
        let enteringSlideIndex = _.get(this.route, 'snapshot.firstChild.data.slideIndex');
        if(leavingSlideIndex && enteringSlideIndex){
          this.routeSlideDirectionService.setDirection(leavingSlideIndex > enteringSlideIndex ? 'right' : 'left')
        } else {
          this.routeSlideDirectionService.setDirection(null);
        }
      }
    });
  }
}

@NgModule({
  imports: [ BrowserModule, FlexLayoutModule.forRoot(), RouterModule.forRoot(AppRoutes) ],
  declarations: [ App, ComponentA, ComponentB, ComponentC, MenuBarComponent],
  bootstrap: [ App ],
  providers: [RouteSlideDirectionService, CanDeactivateAfterChangeDetectionGuard]
})
export class AppModule {}