diff --git a/angular-oauth2-oidc/src/base64-helper.ts b/angular-oauth2-oidc/src/base64-helper.ts
index d2a5b823..19c7029b 100644
--- a/angular-oauth2-oidc/src/base64-helper.ts
+++ b/angular-oauth2-oidc/src/base64-helper.ts
@@ -1,6 +1,9 @@
// see: https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_.22Unicode_Problem.22
export function b64DecodeUnicode(str) {
- return decodeURIComponent(atob(str).split('').map(function(c) {
+
+ let base64 = str.replace(/\-/g, '+').replace(/\_/g, '/');
+
+ return decodeURIComponent(atob(base64).split('').map(function(c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
}
diff --git a/angular-oauth2-oidc/src/oauth-service.ts b/angular-oauth2-oidc/src/oauth-service.ts
index b3b41024..d4b5def7 100644
--- a/angular-oauth2-oidc/src/oauth-service.ts
+++ b/angular-oauth2-oidc/src/oauth-service.ts
@@ -92,11 +92,7 @@ export class OAuthService
this.setupRefreshTimer();
- if (this.sessionChecksEnabled) {
- this.restartSessionChecksIfStillLoggedIn();
- }
-
- this.restartRefreshTimerIfStillLoggedIn();
+
}
/**
@@ -113,9 +109,14 @@ export class OAuthService
if (this.sessionChecksEnabled) {
this.setupSessionCheck();
}
+
+ this.configChanged();
}
- private restartSessionChecksIfStillLoggedIn(): void {
+ private configChanged(): void {
+ }
+
+ public restartSessionChecksIfStillLoggedIn(): void {
if (this.hasValidIdToken()) {
this.initSessionCheck();
}
@@ -145,8 +146,12 @@ export class OAuthService
.events
.filter(e => e.type === 'token_expires')
.subscribe(e => {
- this.silentRefresh();
+ this.silentRefresh().catch(_ => {
+ this.debug('automatic silent refresh did not work');
+ })
});
+
+ this.restartRefreshTimerIfStillLoggedIn();
}
public loadDiscoveryDocumentAndTryLogin() {
@@ -227,7 +232,8 @@ export class OAuthService
private setupAccessTokenTimer(): void {
let expiration = this.getAccessTokenExpiration();
- let timeout = this.calcTimeout(expiration);
+ let storedAt = this.getAccessTokenStoredAt();
+ let timeout = this.calcTimeout(storedAt, expiration);
this.accessTokenTimeoutSubscription =
Observable
@@ -239,7 +245,8 @@ export class OAuthService
private setupIdTokenTimer(): void {
let expiration = this.getIdTokenExpiration();
- let timeout = this.calcTimeout(expiration);
+ let storedAt = this.getIdTokenStoredAt();
+ let timeout = this.calcTimeout(storedAt, expiration);
this.idTokenTimeoutSubscription =
Observable
@@ -260,10 +267,8 @@ export class OAuthService
}
}
- private calcTimeout(expiration: number): number {
- let now = Date.now();
- let delta = (expiration - now) * this.timeoutFactor;
- // let timeout = now + delta;
+ private calcTimeout(storedAt: number, expiration: number): number {
+ let delta = (expiration - storedAt) * this.timeoutFactor;
return delta;
}
@@ -276,6 +281,7 @@ export class OAuthService
*/
public setStorage(storage: OAuthStorage): void {
this._storage = storage;
+ this.configChanged();
}
/**
@@ -292,7 +298,11 @@ export class OAuthService
return new Promise((resolve, reject) => {
if (!fullUrl) {
- fullUrl = this.issuer + '/.well-known/openid-configuration';
+ fullUrl = this.issuer || '';
+ if (!fullUrl.endsWith('/')) {
+ fullUrl += '/';
+ }
+ fullUrl += '.well-known/openid-configuration';
}
if (!this.validateUrlForHttps(fullUrl)) {
@@ -321,6 +331,10 @@ export class OAuthService
this.discoveryDocumentLoaded = true;
this.discoveryDocumentLoadedSubject.next(doc);
+ if (this.sessionChecksEnabled) {
+ this.restartSessionChecksIfStillLoggedIn();
+ }
+
this.loadJwks().then(jwks => {
let result: object = {
discoveryDocument: doc,
@@ -965,7 +979,7 @@ export class OAuthService
private storeAccessTokenResponse(accessToken: string, refreshToken: string, expiresIn: number): void {
this._storage.setItem('access_token', accessToken);
-
+ this._storage.setItem('access_token_stored_at', '' + Date.now());
if (expiresIn) {
let expiresInMilliSeconds = expiresIn * 1000;
let now = new Date();
@@ -1092,9 +1106,10 @@ export class OAuthService
this._storage.setItem('id_token', idToken.idToken);
this._storage.setItem('id_token_claims_obj', idToken.idTokenClaimsJson);
this._storage.setItem('id_token_expires_at', '' + idToken.idTokenExpiresAt);
+ this._storage.setItem('id_token_stored_at', '' + Date.now());
}
- protected storeSessionState(sessionState: string) {
+ protected storeSessionState(sessionState: string): void {
this._storage.setItem('session_state', sessionState);
}
@@ -1273,6 +1288,15 @@ export class OAuthService
return parseInt(this._storage.getItem('expires_at'), 10);
}
+
+ private getAccessTokenStoredAt(): number {
+ return parseInt(this._storage.getItem('access_token_stored_at'), 10);
+ }
+
+ private getIdTokenStoredAt(): number {
+ return parseInt(this._storage.getItem('id_token_stored_at'), 10);
+ }
+
/**
* Returns the expiration date of the id_token
* as milliseconds since 1970.
@@ -1340,7 +1364,9 @@ export class OAuthService
this._storage.removeItem('expires_at');
this._storage.removeItem('id_token_claims_obj');
this._storage.removeItem('id_token_expires_at');
-
+ this._storage.removeItem('id_token_stored_at');
+ this._storage.removeItem('access_token_stored_at');
+
this.silentRefreshSubject = null;
if (!this.logoutUrl) return;
@@ -1350,7 +1376,7 @@ export class OAuthService
let logoutUrl: string;
if (!this.validateUrlForHttps(this.logoutUrl)) throw new Error('logoutUrl must use Http. Also check property requireHttps.');
-
+
// For backward compatibility
if (this.logoutUrl.indexOf('{{') > -1) {
logoutUrl = this.logoutUrl.replace(/\{\{id_token\}\}/, id_token);
diff --git a/angular-oauth2-oidc/src/package.json b/angular-oauth2-oidc/src/package.json
index 08e5a586..6aa4022c 100644
--- a/angular-oauth2-oidc/src/package.json
+++ b/angular-oauth2-oidc/src/package.json
@@ -1,6 +1,6 @@
{
"name": "angular-oauth2-oidc",
- "version": "2.1.1",
+ "version": "2.1.2",
"repository": {
"type": "git",
"url": "https://github.com/manfredsteyer/angular-oauth2-oidc"
diff --git a/sample - Kopie/.angular-cli.json b/sample - Kopie/.angular-cli.json
new file mode 100644
index 00000000..cd7c68ac
--- /dev/null
+++ b/sample - Kopie/.angular-cli.json
@@ -0,0 +1,62 @@
+{
+ "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
+ "project": {
+ "name": "sample2"
+ },
+ "apps": [
+ {
+ "root": "src",
+ "outDir": "dist",
+ "assets": [
+ "assets",
+ "favicon.ico",
+ "silent-refresh.html"
+ ],
+ "index": "index.html",
+ "main": "main.ts",
+ "polyfills": "polyfills.ts",
+ "test": "test.ts",
+ "tsconfig": "tsconfig.app.json",
+ "testTsconfig": "tsconfig.spec.json",
+ "prefix": "app",
+ "styles": [
+ "styles.css",
+ "../node_modules/bootstrap/dist/css/bootstrap.css"
+ ],
+ "scripts": [],
+ "environmentSource": "environments/environment.ts",
+ "environments": {
+ "dev": "environments/environment.ts",
+ "prod": "environments/environment.prod.ts"
+ }
+ }
+ ],
+ "e2e": {
+ "protractor": {
+ "config": "./protractor.conf.js"
+ }
+ },
+ "lint": [
+ {
+ "project": "src/tsconfig.app.json",
+ "exclude": "**/node_modules/**"
+ },
+ {
+ "project": "src/tsconfig.spec.json",
+ "exclude": "**/node_modules/**"
+ },
+ {
+ "project": "e2e/tsconfig.e2e.json",
+ "exclude": "**/node_modules/**"
+ }
+ ],
+ "test": {
+ "karma": {
+ "config": "./karma.conf.js"
+ }
+ },
+ "defaults": {
+ "styleExt": "css",
+ "component": {}
+ }
+}
diff --git a/sample - Kopie/.editorconfig b/sample - Kopie/.editorconfig
new file mode 100644
index 00000000..6e87a003
--- /dev/null
+++ b/sample - Kopie/.editorconfig
@@ -0,0 +1,13 @@
+# Editor configuration, see http://editorconfig.org
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+max_line_length = off
+trim_trailing_whitespace = false
diff --git a/sample - Kopie/.gitignore b/sample - Kopie/.gitignore
new file mode 100644
index 00000000..6b668143
--- /dev/null
+++ b/sample - Kopie/.gitignore
@@ -0,0 +1,43 @@
+# See http://help.github.com/ignore-files/ for more about ignoring files.
+
+# compiled output
+/dist
+/tmp
+/out-tsc
+
+# dependencies
+/node_modules
+
+# IDEs and editors
+/.idea
+.project
+.classpath
+.c9/
+*.launch
+.settings/
+*.sublime-workspace
+
+# IDE - VSCode
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+
+# misc
+/.sass-cache
+/connect.lock
+/coverage
+/libpeerconnection.log
+npm-debug.log
+testem.log
+/typings
+yarn-error.log
+
+# e2e
+/e2e/*.js
+/e2e/*.map
+
+# System Files
+.DS_Store
+Thumbs.db
diff --git a/sample - Kopie/README.md b/sample - Kopie/README.md
new file mode 100644
index 00000000..b4082acf
--- /dev/null
+++ b/sample - Kopie/README.md
@@ -0,0 +1,28 @@
+# Sample2
+
+This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.3.1.
+
+## Development server
+
+Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
+
+## Code scaffolding
+
+Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
+
+## Build
+
+Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
+
+## Running unit tests
+
+Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
+
+## Running end-to-end tests
+
+Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
+Before running the tests make sure you are serving the app via `ng serve`.
+
+## Further help
+
+To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
diff --git a/sample - Kopie/e2e/app.e2e-spec.ts b/sample - Kopie/e2e/app.e2e-spec.ts
new file mode 100644
index 00000000..4ef49c9e
--- /dev/null
+++ b/sample - Kopie/e2e/app.e2e-spec.ts
@@ -0,0 +1,14 @@
+import { AppPage } from './app.po';
+
+describe('sample2 App', () => {
+ let page: AppPage;
+
+ beforeEach(() => {
+ page = new AppPage();
+ });
+
+ it('should display welcome message', () => {
+ page.navigateTo();
+ expect(page.getParagraphText()).toEqual('Welcome to app!');
+ });
+});
diff --git a/sample - Kopie/e2e/app.po.ts b/sample - Kopie/e2e/app.po.ts
new file mode 100644
index 00000000..82ea75ba
--- /dev/null
+++ b/sample - Kopie/e2e/app.po.ts
@@ -0,0 +1,11 @@
+import { browser, by, element } from 'protractor';
+
+export class AppPage {
+ navigateTo() {
+ return browser.get('/');
+ }
+
+ getParagraphText() {
+ return element(by.css('app-root h1')).getText();
+ }
+}
diff --git a/sample - Kopie/e2e/tsconfig.e2e.json b/sample - Kopie/e2e/tsconfig.e2e.json
new file mode 100644
index 00000000..1d9e5edf
--- /dev/null
+++ b/sample - Kopie/e2e/tsconfig.e2e.json
@@ -0,0 +1,14 @@
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../out-tsc/e2e",
+ "baseUrl": "./",
+ "module": "commonjs",
+ "target": "es5",
+ "types": [
+ "jasmine",
+ "jasminewd2",
+ "node"
+ ]
+ }
+}
diff --git a/sample - Kopie/info.txt b/sample - Kopie/info.txt
new file mode 100644
index 00000000..7962b131
--- /dev/null
+++ b/sample - Kopie/info.txt
@@ -0,0 +1,154 @@
+sample2@0.0.0 C:\Users\Manfred\Documents\bücher\Angular 2\src\oauth-lib4\angular-oauth2-oidc\sample2
++-- @angular/animations@4.3.5
+| `-- tslib@1.7.1
++-- @angular/cli@1.3.1
+| +-- @angular-devkit/build-optimizer@0.0.13
+| +-- @ngtools/json-schema@1.1.0
+| +-- @ngtools/webpack@1.6.1
+| +-- autoprefixer@6.7.7
+| +-- chalk@2.1.0
+| +-- circular-dependency-plugin@3.0.0
+| +-- common-tags@1.4.0
+| +-- core-object@3.1.4
+| +-- css-loader@0.28.5
+| +-- cssnano@3.10.0
+| +-- denodeify@1.2.1
+| +-- diff@3.3.0
+| +-- ember-cli-normalize-entity-name@1.0.0
+| +-- ember-cli-string-utils@1.1.0
+| +-- exports-loader@0.6.4
+| +-- extract-text-webpack-plugin@3.0.0
+| +-- file-loader@0.10.1
+| +-- fs-extra@4.0.1
+| +-- get-caller-file@1.0.2
+| +-- glob@7.1.2
+| +-- heimdalljs@0.2.5
+| +-- heimdalljs-logger@0.1.9
+| +-- html-webpack-plugin@2.30.1
+| +-- inflection@1.12.0
+| +-- inquirer@3.2.2
+| +-- isbinaryfile@3.0.2
+| +-- istanbul-instrumenter-loader@2.0.0
+| +-- karma-source-map-support@1.2.0
+| +-- less@2.7.2
+| +-- less-loader@4.0.5
+| +-- license-webpack-plugin@0.5.1
+| +-- lodash@4.17.4
+| +-- memory-fs@0.4.1
+| +-- minimatch@3.0.4
+| +-- node-modules-path@1.0.1
+| +-- node-sass@4.5.3
+| +-- nopt@4.0.1
+| +-- opn@5.1.0
+| +-- portfinder@1.0.13
+| +-- postcss-loader@1.3.3
+| +-- postcss-url@5.1.2
+| +-- raw-loader@0.5.1
+| +-- resolve@1.4.0
+| +-- rsvp@3.6.2
+| +-- sass-loader@6.0.6
+| +-- script-loader@0.7.0
+| +-- semver@5.4.1
+| +-- silent-error@1.1.0
+| +-- source-map-loader@0.2.1
+| +-- source-map-support@0.4.16
+| +-- style-loader@0.13.2
+| +-- stylus@0.54.5
+| +-- stylus-loader@3.0.1
+| +-- temp@0.8.3
+| +-- url-loader@0.5.9
+| +-- walk-sync@0.3.2
+| +-- webpack@3.4.1
+| +-- webpack-dev-middleware@1.12.0
+| +-- webpack-dev-server@2.5.1
+| `-- webpack-merge@4.1.0
++-- @angular/common@4.3.5
++-- @angular/compiler@4.3.5
++-- @angular/compiler-cli@4.3.5
+| +-- @angular/tsc-wrapped@4.3.5
+| +-- minimist@1.2.0
+| `-- reflect-metadata@0.1.10
++-- @angular/core@4.3.5
++-- @angular/forms@4.3.5
++-- @angular/http@4.3.5
++-- @angular/language-service@4.3.5
++-- @angular/platform-browser@4.3.5
++-- @angular/platform-browser-dynamic@4.3.5
++-- @angular/router@4.3.5
++-- @types/jasmine@2.5.53
++-- @types/jasminewd2@2.0.2
++-- @types/node@6.0.87
++-- angular-oauth2-oidc@2.0.6
+| `-- jsrsasign@8.0.3
++-- bootstrap@3.3.7
++-- codelyzer@3.1.2
+| +-- app-root-path@2.0.1
+| +-- css-selector-tokenizer@0.7.0
+| +-- cssauron@1.4.0
+| +-- semver-dsl@1.0.1
+| +-- source-map@0.5.6
+| `-- sprintf-js@1.0.3
++-- core-js@2.5.0
++-- jasmine-core@2.6.4
++-- jasmine-spec-reporter@4.1.1
+| `-- colors@1.1.2
++-- karma@1.7.0
+| +-- bluebird@3.5.0
+| +-- body-parser@1.17.2
+| +-- chokidar@1.7.0
+| +-- combine-lists@1.0.1
+| +-- connect@3.6.3
+| +-- di@0.0.1
+| +-- dom-serialize@2.2.1
+| +-- expand-braces@0.1.2
+| +-- graceful-fs@4.1.11
+| +-- http-proxy@1.16.2
+| +-- lodash@3.10.1
+| +-- log4js@0.6.38
+| +-- mime@1.3.6
+| +-- optimist@0.6.1
+| +-- qjobs@1.1.5
+| +-- range-parser@1.2.0
+| +-- rimraf@2.6.1
+| +-- safe-buffer@5.1.1
+| +-- socket.io@1.7.3
+| +-- tmp@0.0.31
+| `-- useragent@2.2.1
++-- karma-chrome-launcher@2.1.1
+| +-- fs-access@1.0.1
+| `-- which@1.3.0
++-- karma-cli@1.0.1
++-- karma-coverage-istanbul-reporter@1.3.0
+| `-- istanbul-api@1.1.11
++-- karma-jasmine@1.1.0
++-- karma-jasmine-html-reporter@0.2.2
++-- protractor@5.1.2
+| +-- @types/q@0.0.32
+| +-- @types/selenium-webdriver@2.53.42
+| +-- blocking-proxy@0.0.5
+| +-- chalk@1.1.3
+| +-- jasmine@2.7.0
+| +-- jasminewd2@2.1.0
+| +-- optimist@0.6.1
+| +-- q@1.4.1
+| +-- saucelabs@1.3.0
+| +-- selenium-webdriver@3.0.1
+| +-- webdriver-js-extender@1.0.0
+| `-- webdriver-manager@12.0.6
++-- rxjs@5.4.3
+| `-- symbol-observable@1.0.4
++-- ts-node@3.2.2
+| +-- arrify@1.0.1
+| +-- make-error@1.3.0
+| +-- minimist@1.2.0
+| +-- mkdirp@0.5.1
+| +-- tsconfig@6.0.0
+| +-- v8flags@3.0.0
+| `-- yn@2.0.0
++-- tslint@5.3.2
+| +-- babel-code-frame@6.26.0
+| +-- optimist@0.6.1
+| `-- tsutils@2.8.0
++-- typescript@2.3.4
+`-- zone.js@0.8.16
+
diff --git a/sample - Kopie/karma.conf.js b/sample - Kopie/karma.conf.js
new file mode 100644
index 00000000..af139fad
--- /dev/null
+++ b/sample - Kopie/karma.conf.js
@@ -0,0 +1,33 @@
+// Karma configuration file, see link for more information
+// https://karma-runner.github.io/1.0/config/configuration-file.html
+
+module.exports = function (config) {
+ config.set({
+ basePath: '',
+ frameworks: ['jasmine', '@angular/cli'],
+ plugins: [
+ require('karma-jasmine'),
+ require('karma-chrome-launcher'),
+ require('karma-jasmine-html-reporter'),
+ require('karma-coverage-istanbul-reporter'),
+ require('@angular/cli/plugins/karma')
+ ],
+ client:{
+ clearContext: false // leave Jasmine Spec Runner output visible in browser
+ },
+ coverageIstanbulReporter: {
+ reports: [ 'html', 'lcovonly' ],
+ fixWebpackSourcePaths: true
+ },
+ angularCli: {
+ environment: 'dev'
+ },
+ reporters: ['progress', 'kjhtml'],
+ port: 9876,
+ colors: true,
+ logLevel: config.LOG_INFO,
+ autoWatch: true,
+ browsers: ['Chrome'],
+ singleRun: false
+ });
+};
diff --git a/sample - Kopie/package.json b/sample - Kopie/package.json
new file mode 100644
index 00000000..d230d7ca
--- /dev/null
+++ b/sample - Kopie/package.json
@@ -0,0 +1,51 @@
+{
+ "name": "sample2",
+ "version": "0.0.0",
+ "license": "MIT",
+ "scripts": {
+ "ng": "ng",
+ "start": "ng serve --preserve-symlinks",
+ "build": "ng build",
+ "test": "ng test",
+ "lint": "ng lint",
+ "e2e": "ng e2e"
+ },
+ "private": true,
+ "dependencies": {
+ "@angular/animations": "^4.2.4",
+ "@angular/common": "^4.2.4",
+ "@angular/compiler": "^4.2.4",
+ "@angular/core": "^4.2.4",
+ "@angular/forms": "^4.2.4",
+ "@angular/http": "^4.2.4",
+ "@angular/platform-browser": "^4.2.4",
+ "@angular/platform-browser-dynamic": "^4.2.4",
+ "@angular/router": "^4.2.4",
+ "angular-oauth2-oidc": "^2.1.1",
+ "bootstrap": "^3.3.7",
+ "core-js": "^2.4.1",
+ "rxjs": "^5.4.2",
+ "zone.js": "^0.8.14"
+ },
+ "devDependencies": {
+ "@angular/cli": "1.3.1",
+ "@angular/compiler-cli": "^4.2.4",
+ "@angular/language-service": "^4.2.4",
+ "@types/jasmine": "~2.5.53",
+ "@types/jasminewd2": "~2.0.2",
+ "@types/node": "~6.0.60",
+ "codelyzer": "~3.1.1",
+ "jasmine-core": "~2.6.2",
+ "jasmine-spec-reporter": "~4.1.0",
+ "karma": "~1.7.0",
+ "karma-chrome-launcher": "~2.1.1",
+ "karma-cli": "~1.0.1",
+ "karma-coverage-istanbul-reporter": "^1.2.1",
+ "karma-jasmine": "~1.1.0",
+ "karma-jasmine-html-reporter": "^0.2.2",
+ "protractor": "~5.1.2",
+ "ts-node": "~3.2.0",
+ "tslint": "~5.3.2",
+ "typescript": "~2.3.3"
+ }
+}
diff --git a/sample - Kopie/protractor.conf.js b/sample - Kopie/protractor.conf.js
new file mode 100644
index 00000000..7ee3b5ee
--- /dev/null
+++ b/sample - Kopie/protractor.conf.js
@@ -0,0 +1,28 @@
+// Protractor configuration file, see link for more information
+// https://github.com/angular/protractor/blob/master/lib/config.ts
+
+const { SpecReporter } = require('jasmine-spec-reporter');
+
+exports.config = {
+ allScriptsTimeout: 11000,
+ specs: [
+ './e2e/**/*.e2e-spec.ts'
+ ],
+ capabilities: {
+ 'browserName': 'chrome'
+ },
+ directConnect: true,
+ baseUrl: 'http://localhost:4200/',
+ framework: 'jasmine',
+ jasmineNodeOpts: {
+ showColors: true,
+ defaultTimeoutInterval: 30000,
+ print: function() {}
+ },
+ onPrepare() {
+ require('ts-node').register({
+ project: 'e2e/tsconfig.e2e.json'
+ });
+ jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
+ }
+};
diff --git a/sample - Kopie/src/app/app.component.css b/sample - Kopie/src/app/app.component.css
new file mode 100644
index 00000000..e69de29b
diff --git a/sample - Kopie/src/app/app.component.html b/sample - Kopie/src/app/app.component.html
new file mode 100644
index 00000000..d2228e59
--- /dev/null
+++ b/sample - Kopie/src/app/app.component.html
@@ -0,0 +1,22 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sample - Kopie/src/app/app.component.spec.ts b/sample - Kopie/src/app/app.component.spec.ts
new file mode 100644
index 00000000..9510495a
--- /dev/null
+++ b/sample - Kopie/src/app/app.component.spec.ts
@@ -0,0 +1,32 @@
+import { TestBed, async } from '@angular/core/testing';
+
+import { AppComponent } from './app.component';
+
+describe('AppComponent', () => {
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [
+ AppComponent
+ ],
+ }).compileComponents();
+ }));
+
+ it('should create the app', async(() => {
+ const fixture = TestBed.createComponent(AppComponent);
+ const app = fixture.debugElement.componentInstance;
+ expect(app).toBeTruthy();
+ }));
+
+ it(`should have as title 'app'`, async(() => {
+ const fixture = TestBed.createComponent(AppComponent);
+ const app = fixture.debugElement.componentInstance;
+ expect(app.title).toEqual('app');
+ }));
+
+ it('should render title in a h1 tag', async(() => {
+ const fixture = TestBed.createComponent(AppComponent);
+ fixture.detectChanges();
+ const compiled = fixture.debugElement.nativeElement;
+ expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!');
+ }));
+});
diff --git a/sample - Kopie/src/app/app.component.ts b/sample - Kopie/src/app/app.component.ts
new file mode 100644
index 00000000..0913a5aa
--- /dev/null
+++ b/sample - Kopie/src/app/app.component.ts
@@ -0,0 +1,95 @@
+import { authConfig } from './auth.config';
+import { FlightHistoryComponent } from './flight-history/flight-history.component';
+import { Component } from '@angular/core';
+import { OAuthService } from 'angular-oauth2-oidc';
+import { JwksValidationHandler } from 'angular-oauth2-oidc';
+import { Router } from "@angular/router";
+
+@Component({
+ selector: 'flight-app',
+ templateUrl: './app.component.html'
+})
+export class AppComponent {
+
+ constructor(
+ private router: Router,
+ private oauthService: OAuthService) {
+
+ this.configureWithNewConfigApi();
+ // this.configureAuth();
+ // this.configurePasswordFlow();
+
+ }
+
+
+ // This api will come in the next version
+ private configureWithNewConfigApi() {
+
+ this.oauthService.configure(authConfig);
+ this.oauthService.tokenValidationHandler = new JwksValidationHandler();
+ this.oauthService.loadDiscoveryDocumentAndTryLogin();
+
+ // Optional
+ this.oauthService.setupAutomaticSilentRefresh();
+ this.oauthService.events.subscribe(e => {
+ console.debug('oauth/oidc event', e);
+ });
+
+ this.oauthService.events.filter(e => e.type === 'session_terminated').subscribe(e => {
+ console.debug('Your session has been terminated!');
+ });
+ }
+
+ private configureAuth() {
+ // URL of the SPA to redirect the user to after login
+ this.oauthService.redirectUri = window.location.origin + "/index.html";
+
+ // URL of the SPA to redirect the user after silent refresh
+ this.oauthService.silentRefreshRedirectUri = window.location.origin + "/silent-refresh.html";
+
+ // The SPA's id. The SPA is registerd with this id at the auth-server
+ this.oauthService.clientId = "spa-demo";
+
+ // set the scope for the permissions the client should request
+ // The first three are defined by OIDC. The 4th is a usecase-specific one
+ this.oauthService.scope = "openid profile email voucher";
+
+ // Url of the Identity Provider
+ this.oauthService.issuer = 'https://steyer-identity-server.azurewebsites.net/identity';
+
+ this.oauthService.tokenValidationHandler = new JwksValidationHandler();
+
+ this.oauthService.events.subscribe(e => {
+ console.debug('oauth/oidc event', e);
+ });
+
+ // Load Discovery Document and then try to login the user
+ this.oauthService.loadDiscoveryDocument().then((doc) => {
+ this.oauthService.tryLogin();
+ });
+
+ this
+ .oauthService
+ .events
+ .filter(e => e.type == 'token_expires')
+ .subscribe(e => {
+ console.debug('received token_expires event', e);
+ this.oauthService.silentRefresh();
+ });
+ }
+
+ private configurePasswordFlow() {
+
+ // Set a dummy secret
+ // Please note that the auth-server used here demand the client to transmit a client secret, although
+ // the standard explicitly cites that the password flow can also be used without it. Using a client secret
+ // does not make sense for a SPA that runs in the browser. That's why the property is called dummyClientSecret
+ // Using such a dummy secreat is as safe as using no secret.
+ this.oauthService.dummyClientSecret = "geheim";
+
+ }
+
+}
+
+
+
diff --git a/sample - Kopie/src/app/app.module.ts b/sample - Kopie/src/app/app.module.ts
new file mode 100644
index 00000000..14bb0c08
--- /dev/null
+++ b/sample - Kopie/src/app/app.module.ts
@@ -0,0 +1,46 @@
+import { authConfig } from './auth.config';
+import {NgModule} from '@angular/core';
+import {FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {HttpModule} from '@angular/http';
+import {BrowserModule} from '@angular/platform-browser';
+import {AuthConfig, JwksValidationHandler, OAuthModule, ValidationHandler} from 'angular-oauth2-oidc';
+
+import {AppComponent} from './app.component';
+import {AppRouterModule} from './app.routes';
+import {BASE_URL} from './app.tokens';
+import {FlightHistoryComponent} from './flight-history/flight-history.component';
+import {HomeComponent} from './home/home.component';
+import {PasswordFlowLoginComponent} from './password-flow-login/password-flow-login.component';
+import {SharedModule} from './shared/shared.module';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ HttpModule,
+ FormsModule,
+ ReactiveFormsModule,
+ SharedModule.forRoot(),
+ AppRouterModule,
+
+ OAuthModule.forRoot()
+ ],
+ declarations: [
+ AppComponent,
+ HomeComponent,
+ FlightHistoryComponent
+,
+ PasswordFlowLoginComponent
+],
+ providers: [
+ // {provide: AuthConfig, useValue: authConfig },
+ // { provide: OAuthStorage, useClass: DemoStorage },
+ // { provide: ValidationHandler, useClass: JwksValidationHandler },
+ { provide: BASE_URL, useValue: "http://www.angular.at" }
+ ],
+ bootstrap: [
+ AppComponent
+
+ ]
+})
+export class AppModule {
+}
diff --git a/sample - Kopie/src/app/app.routes.ts b/sample - Kopie/src/app/app.routes.ts
new file mode 100644
index 00000000..32c7265d
--- /dev/null
+++ b/sample - Kopie/src/app/app.routes.ts
@@ -0,0 +1,40 @@
+import { PasswordFlowLoginComponent } from './password-flow-login/password-flow-login.component';
+import {Routes, RouterModule, PreloadAllModules} from "@angular/router";
+import {HomeComponent} from "./home/home.component";
+import {FlightHistoryComponent} from "./flight-history/flight-history.component";
+import {CustomPreloadingStrategy} from "./shared/preload/custom-preloading.strategy";
+
+let APP_ROUTES: Routes = [
+ {
+ path: '',
+ redirectTo: 'home',
+ pathMatch: 'full'
+ },
+ {
+ path: 'home',
+ component: HomeComponent
+ },
+ {
+ path: 'password-flow-login',
+ component: PasswordFlowLoginComponent
+ },
+ {
+ path: 'flight-booking',
+ loadChildren: './flight-booking/flight-booking.module#FlightBookingModule'
+ },
+ {
+ path: 'history',
+ component: FlightHistoryComponent,
+ outlet: 'aux'
+ },
+ {
+ path: '**',
+ redirectTo: 'home'
+ }
+];
+
+export let AppRouterModule = RouterModule.forRoot(APP_ROUTES, {
+ preloadingStrategy: CustomPreloadingStrategy,
+ // useHash: true,
+ // initialNavigation: false
+} );
\ No newline at end of file
diff --git a/sample - Kopie/src/app/app.tokens.ts b/sample - Kopie/src/app/app.tokens.ts
new file mode 100644
index 00000000..c9579e06
--- /dev/null
+++ b/sample - Kopie/src/app/app.tokens.ts
@@ -0,0 +1,3 @@
+import {OpaqueToken} from "@angular/core";
+
+export const BASE_URL = new OpaqueToken("BASE_URL");
diff --git a/sample - Kopie/src/app/auth-password-flow.config.ts b/sample - Kopie/src/app/auth-password-flow.config.ts
new file mode 100644
index 00000000..c9104f87
--- /dev/null
+++ b/sample - Kopie/src/app/auth-password-flow.config.ts
@@ -0,0 +1,29 @@
+// This api will come in the next version
+
+import { AuthConfig } from 'angular-oauth2-oidc';
+
+export const authPasswordFlowConfig: AuthConfig = {
+
+ // Url of the Identity Provider
+ issuer: 'https://steyer-identity-server.azurewebsites.net/identity',
+
+ // URL of the SPA to redirect the user to after login
+ redirectUri: window.location.origin + '/index.html',
+
+ // URL of the SPA to redirect the user after silent refresh
+ silentRefreshRedirectUri: window.location.origin + '/silent-refresh.html',
+
+ // The SPA's id. The SPA is registerd with this id at the auth-server
+ clientId: 'demo-resource-owner',
+
+ dummyClientSecret: 'geheim',
+
+ // set the scope for the permissions the client should request
+ // The first three are defined by OIDC. The 4th is a usecase-specific one
+ scope: 'openid profile email voucher',
+
+ showDebugInformation: true,
+
+ oidc: false
+
+}
diff --git a/sample - Kopie/src/app/auth.config.ts b/sample - Kopie/src/app/auth.config.ts
new file mode 100644
index 00000000..ed0b4223
--- /dev/null
+++ b/sample - Kopie/src/app/auth.config.ts
@@ -0,0 +1,26 @@
+// This api will come in the next version
+
+import { AuthConfig } from 'angular-oauth2-oidc';
+
+export const authConfig: AuthConfig = {
+
+ // Url of the Identity Provider
+ issuer: 'https://steyer-identity-server.azurewebsites.net/identity',
+
+ // URL of the SPA to redirect the user to after login
+ redirectUri: window.location.origin + '/index.html',
+
+ // URL of the SPA to redirect the user after silent refresh
+ silentRefreshRedirectUri: window.location.origin + '/silent-refresh.html',
+
+ // The SPA's id. The SPA is registerd with this id at the auth-server
+ clientId: 'spa-demo',
+
+ // set the scope for the permissions the client should request
+ // The first three are defined by OIDC. The 4th is a usecase-specific one
+ scope: 'openid profile email voucher',
+
+ showDebugInformation: true,
+
+ sessionChecksEnabled: true
+}
diff --git a/sample - Kopie/src/app/entities/flight.ts b/sample - Kopie/src/app/entities/flight.ts
new file mode 100644
index 00000000..98a7adb0
--- /dev/null
+++ b/sample - Kopie/src/app/entities/flight.ts
@@ -0,0 +1,8 @@
+
+export interface Flight {
+ id: number; // int + double
+ from: string;
+ to: string;
+ date: string;
+ // JSON: ISO-String 2016-12-24T17:00:00.000+01:00
+}
diff --git a/sample - Kopie/src/app/flight-booking/alt-flight-card/alt-flight-card.component.html b/sample - Kopie/src/app/flight-booking/alt-flight-card/alt-flight-card.component.html
new file mode 100644
index 00000000..2a3702c3
--- /dev/null
+++ b/sample - Kopie/src/app/flight-booking/alt-flight-card/alt-flight-card.component.html
@@ -0,0 +1,16 @@
+
+
+
{{item.from}} - {{item.to}}
+
Flugnr. #{{item.id}}
+
Datum: {{item.date | date:'dd.MM.yyyy HH:mm'}}
+
+
+
+
+
+
+
diff --git a/sample - Kopie/src/app/flight-booking/alt-flight-card/alt-flight.card.component.ts b/sample - Kopie/src/app/flight-booking/alt-flight-card/alt-flight.card.component.ts
new file mode 100644
index 00000000..fc838a0e
--- /dev/null
+++ b/sample - Kopie/src/app/flight-booking/alt-flight-card/alt-flight.card.component.ts
@@ -0,0 +1,17 @@
+import {Component, EventEmitter, Input, Output} from "@angular/core";
+import {Flight} from "../../entities/flight";
+
+@Component({
+ selector: 'alt-flight-card',
+ templateUrl: 'alt-flight-card.component.html'
+})
+export class AltFlightCardComponent {
+
+ @Input() item: Flight;
+ @Input() selected: boolean;
+ @Output() selectedChange = new EventEmitter();
+
+ select() {
+ this.selectedChange.emit(true);
+ }
+}
\ No newline at end of file
diff --git a/sample - Kopie/src/app/flight-booking/alt-flight-card/flight-list.ts b/sample - Kopie/src/app/flight-booking/alt-flight-card/flight-list.ts
new file mode 100644
index 00000000..e6506701
--- /dev/null
+++ b/sample - Kopie/src/app/flight-booking/alt-flight-card/flight-list.ts
@@ -0,0 +1,28 @@
+
+import {Component, Input, Output, EventEmitter} from "@angular/core";
+import {Flight} from "../../entities/flight";
+@Component({
+ selector: 'flight-list',
+ template: `
+
+ `
+})
+export class FlightListComponent {
+
+ @Input() flights: Flight[] = [];
+ @Input() selectedFlight: Flight;
+ @Output() selectedFlightChange = new EventEmitter();
+
+ change(f: Flight) {
+ this.selectedFlightChange.emit(f);
+ }
+
+}
diff --git a/sample - Kopie/src/app/flight-booking/flight-booking.component.html b/sample - Kopie/src/app/flight-booking/flight-booking.component.html
new file mode 100644
index 00000000..2d531679
--- /dev/null
+++ b/sample - Kopie/src/app/flight-booking/flight-booking.component.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sample - Kopie/src/app/flight-booking/flight-booking.component.ts b/sample - Kopie/src/app/flight-booking/flight-booking.component.ts
new file mode 100644
index 00000000..1b81e960
--- /dev/null
+++ b/sample - Kopie/src/app/flight-booking/flight-booking.component.ts
@@ -0,0 +1,9 @@
+import { Component } from '@angular/core';
+import {FlightService} from "./services/flight.service";
+
+@Component({
+ selector: 'flight-booking',
+ templateUrl: './flight-booking.component.html'
+})
+export class FlightBookingComponent {
+}
\ No newline at end of file
diff --git a/sample - Kopie/src/app/flight-booking/flight-booking.module.ts b/sample - Kopie/src/app/flight-booking/flight-booking.module.ts
new file mode 100644
index 00000000..52b25ee1
--- /dev/null
+++ b/sample - Kopie/src/app/flight-booking/flight-booking.module.ts
@@ -0,0 +1,42 @@
+import {NgModule} from "@angular/core";
+import {FlightSearchComponent} from "./flight-search/flight-search.component";
+import {FlightCardComponent} from "./flight-card/flight.card.component";
+import {AltFlightCardComponent} from "./alt-flight-card/alt-flight.card.component";
+import {FlightListComponent} from "./alt-flight-card/flight-list";
+import {PassengerSearchComponent} from "./passenger-search/passenger-search.component";
+import {FlightEditComponent} from "./flight-edit/flight-edit.component";
+import {FlightSearchReactiveComponent} from "./flight-search-reactive/flight-search-reactive.component";
+import {CommonModule} from "@angular/common";
+import {FormsModule, ReactiveFormsModule} from "@angular/forms";
+import {SharedModule} from "../shared/shared.module";
+import {FlightBookingRouterModule} from "./flight-booking.routes";
+import {FlightBookingComponent} from "./flight-booking.component";
+import {FlightService} from "./services/flight.service";
+
+@NgModule({
+ imports: [
+ CommonModule, // ngFor
+ FormsModule,
+ ReactiveFormsModule,
+ SharedModule,
+ FlightBookingRouterModule
+ ],
+ declarations: [
+ FlightSearchComponent,
+ FlightCardComponent,
+ AltFlightCardComponent,
+ FlightListComponent,
+ FlightSearchReactiveComponent,
+ PassengerSearchComponent,
+ FlightEditComponent,
+ FlightBookingComponent
+ ],
+ providers: [
+ FlightService
+ ],
+ exports: [
+ ]
+})
+export class FlightBookingModule {
+
+}
\ No newline at end of file
diff --git a/sample - Kopie/src/app/flight-booking/flight-booking.routes.ts b/sample - Kopie/src/app/flight-booking/flight-booking.routes.ts
new file mode 100644
index 00000000..c3802bd0
--- /dev/null
+++ b/sample - Kopie/src/app/flight-booking/flight-booking.routes.ts
@@ -0,0 +1,33 @@
+import {Routes, RouterModule} from "@angular/router";
+import {FlightSearchComponent} from "./flight-search/flight-search.component";
+import {PassengerSearchComponent} from "./passenger-search/passenger-search.component";
+import {FlightEditComponent} from "./flight-edit/flight-edit.component";
+import {FlightBookingComponent} from "./flight-booking.component";
+import {AuthGuard} from "../shared/auth/auth.guard";
+import {LeaveComponentGuard} from "../shared/deactivation/LeaveComponentGuard";
+
+let FLIGHT_BOOKING_ROUTES: Routes = [
+ {
+ path: '',
+ component: FlightBookingComponent,
+ canActivate: [AuthGuard],
+ children: [
+ {
+ path: 'flight-search',
+ component: FlightSearchComponent
+ },
+ {
+ path: 'passenger-search',
+ component: PassengerSearchComponent
+ },
+ {
+ path: 'flight-edit/:id',
+ component: FlightEditComponent,
+ canDeactivate: [LeaveComponentGuard]
+ }
+
+ ]
+ }
+];
+
+export let FlightBookingRouterModule = RouterModule.forChild(FLIGHT_BOOKING_ROUTES);
\ No newline at end of file
diff --git a/sample - Kopie/src/app/flight-booking/flight-card/flight-card.component.html b/sample - Kopie/src/app/flight-booking/flight-card/flight-card.component.html
new file mode 100644
index 00000000..c372547c
--- /dev/null
+++ b/sample - Kopie/src/app/flight-booking/flight-card/flight-card.component.html
@@ -0,0 +1,17 @@
+
+
+
{{item.from}} - {{item.to}}
+
Flugnr. #{{item.id}}
+
Datum: {{item.date | date:'dd.MM.yyyy HH:mm'}}
+
+
+
+
+
+
+
+
diff --git a/sample - Kopie/src/app/flight-booking/flight-card/flight.card.component.ts b/sample - Kopie/src/app/flight-booking/flight-card/flight.card.component.ts
new file mode 100644
index 00000000..6326e190
--- /dev/null
+++ b/sample - Kopie/src/app/flight-booking/flight-card/flight.card.component.ts
@@ -0,0 +1,17 @@
+import {Component, EventEmitter, Input, Output} from "@angular/core";
+import {Flight} from "../../entities/flight";
+
+@Component({
+ selector: 'flight-card',
+ templateUrl: './flight-card.component.html'
+})
+export class FlightCardComponent {
+
+ @Input() item: Flight;
+ @Input() selectedItem: Flight;
+ @Output() selectedItemChange = new EventEmitter();
+
+ select() {
+ this.selectedItemChange.emit(this.item);
+ }
+}
\ No newline at end of file
diff --git a/sample - Kopie/src/app/flight-booking/flight-edit/flight-edit.component.ts b/sample - Kopie/src/app/flight-booking/flight-edit/flight-edit.component.ts
new file mode 100644
index 00000000..aedbe55f
--- /dev/null
+++ b/sample - Kopie/src/app/flight-booking/flight-edit/flight-edit.component.ts
@@ -0,0 +1,54 @@
+import { Component, OnInit } from '@angular/core';
+import {ActivatedRoute} from "@angular/router";
+
+@Component({
+ template: `
+ Flight Edit!
+ Hier könnte auch der Datensatz mit der Id {{id}} stehen!
+
+
+
+ Daten wurden nicht gespeichert! Trotzdem Maske verlassen?
+
+
+
+
+
+
+
+ `
+})
+export class FlightEditComponent implements OnInit {
+
+ public id: string;
+
+ constructor(private route: ActivatedRoute) {
+
+ route.params.subscribe(p => {
+ this.id = p['id'];
+ });
+ }
+
+ ngOnInit() { }
+
+ exitWarning = {
+ show: false,
+ resolve: null
+ }
+
+ decide(decision: boolean) {
+ this.exitWarning.show = false;
+ this.exitWarning.resolve(decision);
+ }
+
+ canDeactivate() {
+ this.exitWarning.show = true;
+ return new Promise((resolve) => {
+ this.exitWarning.resolve = resolve;
+ });
+ }
+
+}
\ No newline at end of file
diff --git a/sample - Kopie/src/app/flight-booking/flight-search-reactive/flight-search-reactive.component.css b/sample - Kopie/src/app/flight-booking/flight-search-reactive/flight-search-reactive.component.css
new file mode 100644
index 00000000..58633484
--- /dev/null
+++ b/sample - Kopie/src/app/flight-booking/flight-search-reactive/flight-search-reactive.component.css
@@ -0,0 +1,13 @@
+
+
+input.ng-valid {
+ border-left-style: solid;
+ border-left-color: forestgreen;
+ border-left-width: 5px;
+}
+
+input.ng-invalid {
+ border-left-style: solid;
+ border-left-color: hotpink;
+ border-left-width: 5px;
+}
\ No newline at end of file
diff --git a/sample - Kopie/src/app/flight-booking/flight-search-reactive/flight-search-reactive.component.html b/sample - Kopie/src/app/flight-booking/flight-search-reactive/flight-search-reactive.component.html
new file mode 100644
index 00000000..b07bd6db
--- /dev/null
+++ b/sample - Kopie/src/app/flight-booking/flight-search-reactive/flight-search-reactive.component.html
@@ -0,0 +1,95 @@
+Flight Search (Reactive) !
+
+
+
+
+
+
+
+
+
+
+
+
+
Warenkorb
+----------------------
+{{selectedFlight | json}}
+
+
\ No newline at end of file
diff --git a/sample - Kopie/src/app/flight-booking/flight-search-reactive/flight-search-reactive.component.ts b/sample - Kopie/src/app/flight-booking/flight-search-reactive/flight-search-reactive.component.ts
new file mode 100644
index 00000000..97d554bc
--- /dev/null
+++ b/sample - Kopie/src/app/flight-booking/flight-search-reactive/flight-search-reactive.component.ts
@@ -0,0 +1,83 @@
+import { Component } from '@angular/core';
+import {Flight} from "../../entities/flight";
+import {Http, URLSearchParams, Headers } from '@angular/http';
+import {FlightService} from "../services/flight.service";
+import {FormGroup, FormBuilder, Validators, AbstractControl} from "@angular/forms";
+import {CityValidatorDirective} from "../../shared/validation/city.validator";
+
+@Component({
+ selector: 'flight-search-reactive',
+ templateUrl: 'flight-search-reactive.component.html',
+ providers: [FlightService],
+ styleUrls: ['flight-search-reactive.component.css']
+})
+export class FlightSearchReactiveComponent {
+
+ public flights: Array = [];
+ public selectedFlight: Flight;
+
+ public filter: FormGroup;
+
+ public formDesc = [];
+
+ constructor(
+ private flightService: FlightService,
+ private fb: FormBuilder) {
+
+ this.formDesc.push({
+ label: 'Von',
+ name: 'from'
+ });
+
+ this.formDesc.push({
+ label: 'Nach',
+ name: 'to'
+ });
+
+ this.filter= fb.group({
+ 'from': [
+ 'Graz',
+ [
+ Validators.required,
+ Validators.minLength(3),
+ (c: AbstractControl): any => {
+ if (c.value != 'Graz' && c.value != 'Hamburg') {
+ return {
+ city: true
+ };
+ }
+ return {}
+ }
+ ]
+ ],
+ 'to': ['Hamburg']
+ });
+
+ this.filter.valueChanges.subscribe((e) => {
+ console.debug('formular geändert', e)
+ });
+
+ this.filter.controls['from'].valueChanges.subscribe((e) => {
+ console.debug('from geändert', e)
+ });
+
+
+ }
+
+ public select(f: Flight): void {
+ this.selectedFlight = f;
+ }
+
+ public search(): void {
+
+ var value = this.filter.value;
+
+ this.flightService
+ .find(value.from, value.to);
+
+
+
+ // .map(function(resp) { return resp.json() })
+
+ }
+}
\ No newline at end of file
diff --git a/sample - Kopie/src/app/flight-booking/flight-search/flight-search.component.css b/sample - Kopie/src/app/flight-booking/flight-search/flight-search.component.css
new file mode 100644
index 00000000..58633484
--- /dev/null
+++ b/sample - Kopie/src/app/flight-booking/flight-search/flight-search.component.css
@@ -0,0 +1,13 @@
+
+
+input.ng-valid {
+ border-left-style: solid;
+ border-left-color: forestgreen;
+ border-left-width: 5px;
+}
+
+input.ng-invalid {
+ border-left-style: solid;
+ border-left-color: hotpink;
+ border-left-width: 5px;
+}
\ No newline at end of file
diff --git a/sample - Kopie/src/app/flight-booking/flight-search/flight-search.component.html b/sample - Kopie/src/app/flight-booking/flight-search/flight-search.component.html
new file mode 100644
index 00000000..d36dabdf
--- /dev/null
+++ b/sample - Kopie/src/app/flight-booking/flight-search/flight-search.component.html
@@ -0,0 +1,124 @@
+Flight Search!
+
+
+
+
+
+
+
+
+
+
+
+
+
Warenkorb
+----------------------
+{{selectedFlight | json}}
+
+
\ No newline at end of file
diff --git a/sample - Kopie/src/app/flight-booking/flight-search/flight-search.component.ts b/sample - Kopie/src/app/flight-booking/flight-search/flight-search.component.ts
new file mode 100644
index 00000000..77ffdcdb
--- /dev/null
+++ b/sample - Kopie/src/app/flight-booking/flight-search/flight-search.component.ts
@@ -0,0 +1,43 @@
+import {Component} from '@angular/core';
+import {Headers, Http, URLSearchParams} from '@angular/http';
+
+import {OAuthService} from 'angular-oauth2-oidc';
+import {Flight} from '../../entities/flight';
+import {FlightService} from '../services/flight.service';
+
+@Component({
+ selector: 'flight-search',
+ templateUrl: './flight-search.component.html',
+ styleUrls: ['./flight-search.component.css']
+})
+export class FlightSearchComponent {
+
+ public from: string = "Graz";
+ public to: string = "";
+ public selectedFlight: Flight;
+
+ constructor(
+ private flightService: FlightService,
+ private oauthService: OAuthService
+ ) {
+ console.debug('access-token', this.oauthService.getAccessToken());
+ }
+
+ // cmp.flights
+ public get flights() {
+ return this.flightService.flights;
+ }
+
+ public select(f: Flight): void {
+ this.selectedFlight = f;
+ }
+
+ public search(): void {
+
+ this.flightService
+ .find(this.from, this.to);
+
+ // .map(function(resp) { return resp.json() })
+
+ }
+}
\ No newline at end of file
diff --git a/sample - Kopie/src/app/flight-booking/passenger-search/passenger-search.component.ts b/sample - Kopie/src/app/flight-booking/passenger-search/passenger-search.component.ts
new file mode 100644
index 00000000..992ad7c8
--- /dev/null
+++ b/sample - Kopie/src/app/flight-booking/passenger-search/passenger-search.component.ts
@@ -0,0 +1,19 @@
+import { Component, OnInit } from '@angular/core';
+import { OAuthService } from "angular-oauth2-oidc";
+
+@Component({
+ template: `
+ PassengerSearch
+ Platzhalter-Seite. Hier könnte auch Ihre Werbung stehen ;-)
+
+
+ `
+})
+export class PassengerSearchComponent implements OnInit {
+ constructor(private oauthService: OAuthService) { }
+ ngOnInit() { }
+
+ refresh() {
+ this.oauthService.silentRefresh();
+ }
+}
diff --git a/sample - Kopie/src/app/flight-booking/services/flight.service.ts b/sample - Kopie/src/app/flight-booking/services/flight.service.ts
new file mode 100644
index 00000000..4cf0c8d0
--- /dev/null
+++ b/sample - Kopie/src/app/flight-booking/services/flight.service.ts
@@ -0,0 +1,44 @@
+import { Injectable, Inject} from '@angular/core';
+import {Http, Headers, URLSearchParams} from '@angular/http';
+import {BASE_URL} from "../../app.tokens";
+import {Observable} from "rxjs";
+import {Flight} from "../../entities/flight";
+import {OAuthService} from "angular-oauth2-oidc";
+
+@Injectable()
+export class FlightService {
+
+ constructor(
+ private oauthService: OAuthService,
+ private http: Http,
+ @Inject(BASE_URL) private baseUrl: string
+ ) {
+ }
+
+ public flights: Array = [];
+
+ find(from: string, to: string): void {
+ let url = this.baseUrl + "/api/flight";
+ let headers = new Headers();
+ headers.set('Accept', 'application/json');
+ headers.set('Authorization', 'Bearer ' + this.oauthService.getAccessToken());
+
+ let search = new URLSearchParams();
+ search.set('from', from);
+ search.set('to', to);
+
+ this
+ .http
+ .get(url, {headers, search})
+ .map(resp => resp.json())
+ .subscribe(
+ (flights) => {
+ this.flights = flights;
+ },
+ (err) => {
+ console.warn('status', err.status);
+ }
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/sample - Kopie/src/app/flight-history/flight-history.component.ts b/sample - Kopie/src/app/flight-history/flight-history.component.ts
new file mode 100644
index 00000000..1a5eef12
--- /dev/null
+++ b/sample - Kopie/src/app/flight-history/flight-history.component.ts
@@ -0,0 +1,16 @@
+import {Component} from "@angular/core";
+
+@Component({
+ template: `
+ Flight History
+
+ - Graz - Hamburg
+ - Hamburg - Frankfurt
+ - Frankfurt - Graz
+
+ `
+})
+export class FlightHistoryComponent {
+
+}
+
diff --git a/sample - Kopie/src/app/home/home.component.html b/sample - Kopie/src/app/home/home.component.html
new file mode 100644
index 00000000..f0f82c08
--- /dev/null
+++ b/sample - Kopie/src/app/home/home.component.html
@@ -0,0 +1,51 @@
+Status: {{ givenName ? 'logged in' : 'logged out' }}
+Welcome!
+Welcome, {{givenName}} {{familyName}}!
+
+
+
+
Login with Authorization Server
+
+
+
+
+
+
+
+
+
+
+
+
+ Username/Password: max/geheim
+
+
+
+
+
+
+ access_token_expiration: {{access_token_expiration}}
+
+
+ id_token_expiration: {{id_token_expiration}}
+
+
+
+
+
+
+
+ access_token: {{access_token}}
+
+
+ id_token: {{id_token}}
+
+
+
user profile:
+
{{userProfile | json}}
+
+
+
+
+
+
diff --git a/sample - Kopie/src/app/home/home.component.ts b/sample - Kopie/src/app/home/home.component.ts
new file mode 100644
index 00000000..01b522b0
--- /dev/null
+++ b/sample - Kopie/src/app/home/home.component.ts
@@ -0,0 +1,92 @@
+import { authConfig } from '../auth.config';
+import { Component, OnInit } from '@angular/core';
+import {OAuthService} from "angular-oauth2-oidc";
+
+@Component({
+ templateUrl: './home.component.html'
+})
+export class HomeComponent implements OnInit {
+
+ loginFailed: boolean = false;
+ userProfile: object;
+
+ constructor(private oauthService: OAuthService) {
+ // Tweak config for implicit flow.
+ // This is just needed b/c this demo uses both,
+ // implicit flow as well as password flow
+ this.oauthService.configure(authConfig)
+ }
+
+ ngOnInit() {
+ }
+
+
+ login() {
+ this.oauthService.initImplicitFlow('http://www.myurl.com/x/y/z');
+ }
+
+ logout() {
+ this.oauthService.logOut();
+ }
+
+ loadUserProfile(): void {
+ this
+ .oauthService
+ .loadUserProfile()
+ .then(up => this.userProfile = up);
+
+ }
+
+ get givenName() {
+ var claims = this.oauthService.getIdentityClaims();
+ if (!claims) return null;
+ return claims['given_name'];
+ }
+
+ get familyName() {
+ var claims = this.oauthService.getIdentityClaims();
+ if (!claims) return null;
+ return claims['family_name'];
+ }
+
+ testSilentRefresh() {
+ /*
+ * Tweak config for implicit flow.
+ * This is needed b/c this sample uses both flows
+ */
+ //this.oauthService.clientId = "spa-demo";
+ this.oauthService.oidc = true;
+
+ this
+ .oauthService
+ .silentRefresh()
+ .then(info => console.debug('refresh ok', info))
+ .catch(err => console.error('refresh error', err));
+ }
+
+ set requestAccessToken(value: boolean) {
+ this.oauthService.requestAccessToken = value;
+ localStorage.setItem('requestAccessToken', '' + value);
+ }
+
+ get requestAccessToken() {
+ return this.oauthService.requestAccessToken;
+ }
+
+ get id_token() {
+ return this.oauthService.getIdToken();
+ }
+
+ get access_token() {
+ return this.oauthService.getAccessToken();
+ }
+
+ get id_token_expiration() {
+ return this.oauthService.getIdTokenExpiration();
+ }
+
+ get access_token_expiration() {
+ return this.oauthService.getAccessTokenExpiration();
+ }
+
+}
\ No newline at end of file
diff --git a/sample - Kopie/src/app/password-flow-login/password-flow-login.component.html b/sample - Kopie/src/app/password-flow-login/password-flow-login.component.html
new file mode 100644
index 00000000..a37e787c
--- /dev/null
+++ b/sample - Kopie/src/app/password-flow-login/password-flow-login.component.html
@@ -0,0 +1,53 @@
+Welcome!
+Welcome, {{givenName}} {{familyName}}!
+
+
+
+
+
Login with Username/Password
+
+
+ Login wasn't successfull.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Username/Password: max/geheim
+
+
+
+
+
+
+ access_token_expiration: {{access_token_expiration}}
+
+
+
+
+
+
+
+ access_token: {{access_token}}
+
+
+
user profile:
+
{{userProfile | json}}
+
+
+
+
diff --git a/sample - Kopie/src/app/password-flow-login/password-flow-login.component.ts b/sample - Kopie/src/app/password-flow-login/password-flow-login.component.ts
new file mode 100644
index 00000000..91e21b3f
--- /dev/null
+++ b/sample - Kopie/src/app/password-flow-login/password-flow-login.component.ts
@@ -0,0 +1,78 @@
+import { authPasswordFlowConfig } from '../auth-password-flow.config';
+import { OAuthService } from 'angular-oauth2-oidc';
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'app-password-flow-login',
+ templateUrl: './password-flow-login.component.html'
+})
+export class PasswordFlowLoginComponent implements OnInit {
+
+ userName: string;
+ password: string;
+ loginFailed: boolean = false;
+ userProfile: object;
+
+ constructor(private oauthService: OAuthService) {
+
+ // Tweak config for password flow
+ // This is just needed b/c this demo uses both,
+ // implicit flow as well as password flow
+
+ this.oauthService.configure(authPasswordFlowConfig)
+
+ }
+
+ ngOnInit() {
+ }
+
+ loadUserProfile(): void {
+ this
+ .oauthService
+ .loadUserProfile()
+ .then(up => this.userProfile = up);
+
+ }
+
+ get access_token() {
+ return this.oauthService.getAccessToken();
+ }
+
+ get access_token_expiration() {
+ return this.oauthService.getAccessTokenExpiration();
+}
+
+ get givenName() {
+ var claims = this.oauthService.getIdentityClaims();
+ if (!claims) return null;
+ return claims['given_name'];
+ }
+
+ get familyName() {
+ var claims = this.oauthService.getIdentityClaims();
+ if (!claims) return null;
+ return claims['family_name'];
+ }
+
+ loginWithPassword() {
+
+ this
+ .oauthService
+ .fetchTokenUsingPasswordFlowAndLoadUserProfile(this.userName, this.password)
+ .then(() => {
+ console.debug('successfully logged in');
+ this.loginFailed = false;
+ })
+ .catch((err) => {
+ console.error('error logging in', err);
+ this.loginFailed = true;
+ });
+ }
+
+ logout() {
+ this.oauthService.logOut(true);
+ }
+
+
+
+}
diff --git a/sample - Kopie/src/app/shared/auth/auth.guard.ts b/sample - Kopie/src/app/shared/auth/auth.guard.ts
new file mode 100644
index 00000000..dc76ba90
--- /dev/null
+++ b/sample - Kopie/src/app/shared/auth/auth.guard.ts
@@ -0,0 +1,11 @@
+import {Injectable} from "@angular/core";
+import {CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot} from "@angular/router";
+
+@Injectable()
+export class AuthGuard implements CanActivate {
+
+ canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/sample - Kopie/src/app/shared/date/date.component.ts b/sample - Kopie/src/app/shared/date/date.component.ts
new file mode 100644
index 00000000..955439d8
--- /dev/null
+++ b/sample - Kopie/src/app/shared/date/date.component.ts
@@ -0,0 +1,50 @@
+import {Component, Input, OnInit, OnChanges} from '@angular/core';
+
+@Component({
+ selector: 'date-component',
+ template: `
+
+ {{day}}.{{month}}.{{year}} {{hour}}:{{minute}}
+
+ `
+
+})
+export class DateComponent implements OnInit, OnChanges {
+
+ @Input() date: string;
+
+ day;
+ month;
+ year;
+ hour;
+ minute;
+
+ constructor() {
+ console.debug('ctrl')
+ }
+
+ ngOnInit() {
+
+ }
+
+ ngOnChanges(change) {
+
+ // if(change.date) { ... }
+
+ console.debug('change', change);
+
+ let date = new Date(this.date);
+
+
+
+ this.day = date.getDate();
+ this.month = date.getMonth() + 1;
+ this.year = date.getFullYear();
+
+ this.hour = date.getHours();
+ this.minute = date.getMinutes();
+ }
+
+
+
+}
diff --git a/sample - Kopie/src/app/shared/deactivation/LeaveComponentGuard.ts b/sample - Kopie/src/app/shared/deactivation/LeaveComponentGuard.ts
new file mode 100644
index 00000000..19b36582
--- /dev/null
+++ b/sample - Kopie/src/app/shared/deactivation/LeaveComponentGuard.ts
@@ -0,0 +1,11 @@
+import {CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot} from "@angular/router";
+
+export class LeaveComponentGuard implements CanDeactivate {
+
+ canDeactivate(component: any, route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise {
+
+ return component.canDeactivate();
+
+ }
+
+}
\ No newline at end of file
diff --git a/sample - Kopie/src/app/shared/pipes/city.pipe.ts b/sample - Kopie/src/app/shared/pipes/city.pipe.ts
new file mode 100644
index 00000000..76d0ee78
--- /dev/null
+++ b/sample - Kopie/src/app/shared/pipes/city.pipe.ts
@@ -0,0 +1,34 @@
+import {Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+ name: 'city',
+ pure: true
+})
+export class CityPipe implements PipeTransform {
+
+ transform(value: any, ...args: any[]): any {
+
+ let fmt = args[0]; // short, long
+ let short, long;
+
+ switch(value) {
+ case "Graz":
+ long = "Flughafen Graz Thalerhof";
+ short = "GRZ";
+ break;
+ case "Hamburg":
+ long = "Airport Hamburg Fuhlsbüttl Helmut Schmidt";
+ short = "HAM";
+ break;
+ default:
+ long = short = "ROM";
+ }
+
+ if (fmt == 'short') return short;
+ return long;
+
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/sample - Kopie/src/app/shared/preload/custom-preloading.strategy.ts b/sample - Kopie/src/app/shared/preload/custom-preloading.strategy.ts
new file mode 100644
index 00000000..f0e7f83c
--- /dev/null
+++ b/sample - Kopie/src/app/shared/preload/custom-preloading.strategy.ts
@@ -0,0 +1,15 @@
+import {PreloadingStrategy, Route} from "@angular/router";
+import {Observable} from 'rxjs';
+
+export class CustomPreloadingStrategy implements PreloadingStrategy {
+
+ preload(route: Route, fn: () => Observable): Observable {
+ //return Observable.of(true).delay(7000).flatMap(_ => fn());
+
+ if (true) {
+ return fn();
+ }
+
+ }
+
+}
diff --git a/sample - Kopie/src/app/shared/shared.module.ts b/sample - Kopie/src/app/shared/shared.module.ts
new file mode 100644
index 00000000..884f4880
--- /dev/null
+++ b/sample - Kopie/src/app/shared/shared.module.ts
@@ -0,0 +1,46 @@
+import {NgModule, ModuleWithProviders} from "@angular/core";
+import {FormsModule} from "@angular/forms";
+import {CommonModule} from "@angular/common";
+import {CityPipe} from "./pipes/city.pipe";
+import {CityValidatorDirective} from "./validation/city.validator";
+import {RoundTrip} from "./validation/roundtrip.validator";
+import {AsyncCityValidatorDirective} from "./validation/async-city.validator";
+import {DateComponent} from "./date/date.component";
+import {AuthGuard} from "./auth/auth.guard";
+import {LeaveComponentGuard} from "./deactivation/LeaveComponentGuard";
+import {CustomPreloadingStrategy} from "./preload/custom-preloading.strategy";
+
+@NgModule({
+ imports: [
+ FormsModule, // [(ngModel)]
+ CommonModule // ngFor, ngIf, ngStyle, ngClass, date, json
+ ],
+ providers: [
+ ],
+ declarations: [
+ CityPipe,
+ CityValidatorDirective,
+ AsyncCityValidatorDirective,
+ RoundTrip,
+ DateComponent
+ ],
+ exports:[
+ CityPipe,
+ CityValidatorDirective,
+ AsyncCityValidatorDirective,
+ RoundTrip,
+ DateComponent
+ ]
+})
+export class SharedModule {
+ static forRoot(): ModuleWithProviders {
+ return {
+ providers: [
+ AuthGuard,
+ LeaveComponentGuard,
+ CustomPreloadingStrategy
+ ],
+ ngModule: SharedModule
+ };
+ }
+}
\ No newline at end of file
diff --git a/sample - Kopie/src/app/shared/validation/async-city.validator.ts b/sample - Kopie/src/app/shared/validation/async-city.validator.ts
new file mode 100644
index 00000000..b3c814d5
--- /dev/null
+++ b/sample - Kopie/src/app/shared/validation/async-city.validator.ts
@@ -0,0 +1,33 @@
+
+import {Directive} from "@angular/core";
+import {NG_ASYNC_VALIDATORS, AbstractControl} from "@angular/forms";
+
+@Directive({
+ selector: 'input[async-city]',
+ providers: [
+ {
+ provide: NG_ASYNC_VALIDATORS,
+ useExisting: AsyncCityValidatorDirective,
+ multi: true
+ }]
+})
+export class AsyncCityValidatorDirective {
+
+ validate(ctrl: AbstractControl): Promise {
+
+ return new Promise((resolve: Function) => {
+ setTimeout(() => {
+
+ if (ctrl.value == "Graz" || ctrl.value == "Hamburg") {
+ resolve({});
+ return;
+ }
+
+ resolve({ 'async-city': false});
+
+ }, 100);
+ })
+
+ }
+
+}
\ No newline at end of file
diff --git a/sample - Kopie/src/app/shared/validation/city.validator.ts b/sample - Kopie/src/app/shared/validation/city.validator.ts
new file mode 100644
index 00000000..24ce4840
--- /dev/null
+++ b/sample - Kopie/src/app/shared/validation/city.validator.ts
@@ -0,0 +1,51 @@
+
+import {Directive, Input, Attribute} from "@angular/core";
+import {NG_VALIDATORS, Validator, AbstractControl, FormGroup} from "@angular/forms";
+
+@Directive({
+ selector: 'input[city]', //
+ providers: [
+ {
+ provide: NG_VALIDATORS,
+ useExisting: CityValidatorDirective,
+ multi: true
+ }
+ ]
+})
+export class CityValidatorDirective implements Validator {
+
+ // @Input() city: string;
+
+ constructor(@Attribute('city') private city: string) {
+ }
+
+ validate(c: AbstractControl): any {
+
+
+ let formGroup = c.root;
+ let otherValueCtrl = formGroup.controls['to'];
+
+ if (!otherValueCtrl) return { };
+
+ let otherValue = otherValueCtrl.value;
+
+ if(otherValue == c.value) {
+ return {
+ city: 'rundflug'
+ }
+ }
+
+ if (!this.city) return { }
+
+ let allowed = this.city.split(','); //['Graz', 'Hamburg', 'Wien', 'Frankfurt'];
+
+ if (allowed.indexOf(c.value) == -1) {
+ return {
+ city: true
+ }
+ }
+
+ return {};
+
+ }
+}
\ No newline at end of file
diff --git a/sample - Kopie/src/app/shared/validation/roundtrip.validator.ts b/sample - Kopie/src/app/shared/validation/roundtrip.validator.ts
new file mode 100644
index 00000000..f1a0103a
--- /dev/null
+++ b/sample - Kopie/src/app/shared/validation/roundtrip.validator.ts
@@ -0,0 +1,32 @@
+import {Directive} from "@angular/core";
+import {FormGroup, Validator, AbstractControl, NG_VALIDATORS, FormGroupDirective} from "@angular/forms";
+
+@Directive({
+ selector: 'form[round-trip]',
+ providers: [{ provide: NG_VALIDATORS, useExisting: RoundTrip, multi: true }]
+})
+export class RoundTrip implements Validator{
+
+ validate(control: AbstractControl): any {
+
+ let formGroup = control;
+ let fromCtrl = formGroup.controls['from'];
+ let toCtrl = formGroup.controls['to'];
+
+ if (!fromCtrl || !toCtrl) return { };
+
+ let from = fromCtrl.value;
+ let to = toCtrl.value;
+
+ if (from == to) {
+ return {
+ 'round-trip': {
+ city: from
+ }
+ }
+ }
+ return { };
+
+ }
+
+}
\ No newline at end of file
diff --git a/sample - Kopie/src/assets/.gitkeep b/sample - Kopie/src/assets/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/sample - Kopie/src/environments/environment.prod.ts b/sample - Kopie/src/environments/environment.prod.ts
new file mode 100644
index 00000000..3612073b
--- /dev/null
+++ b/sample - Kopie/src/environments/environment.prod.ts
@@ -0,0 +1,3 @@
+export const environment = {
+ production: true
+};
diff --git a/sample - Kopie/src/environments/environment.ts b/sample - Kopie/src/environments/environment.ts
new file mode 100644
index 00000000..b7f639ae
--- /dev/null
+++ b/sample - Kopie/src/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/sample - Kopie/src/favicon.ico b/sample - Kopie/src/favicon.ico
new file mode 100644
index 00000000..8081c7ce
Binary files /dev/null and b/sample - Kopie/src/favicon.ico differ
diff --git a/sample - Kopie/src/index.html b/sample - Kopie/src/index.html
new file mode 100644
index 00000000..a1c710cf
--- /dev/null
+++ b/sample - Kopie/src/index.html
@@ -0,0 +1,53 @@
+
+
+
+
+ Sample
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sample - Kopie/src/main.ts b/sample - Kopie/src/main.ts
new file mode 100644
index 00000000..a2d2fa81
--- /dev/null
+++ b/sample - Kopie/src/main.ts
@@ -0,0 +1,13 @@
+import { enableProdMode } from '@angular/core';
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { environment } from './environments/environment';
+
+import 'rxjs/add/operator/map';
+import { AppModule } from "./app/app.module";
+
+if (environment.production) {
+ enableProdMode();
+}
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/sample - Kopie/src/polyfills.ts b/sample - Kopie/src/polyfills.ts
new file mode 100644
index 00000000..8f52dca7
--- /dev/null
+++ b/sample - Kopie/src/polyfills.ts
@@ -0,0 +1,73 @@
+/**
+ * This file includes polyfills needed by Angular and is loaded before the app.
+ * You can add your own extra polyfills to this file.
+ *
+ * This file is divided into 2 sections:
+ * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
+ * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
+ * file.
+ *
+ * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
+ * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
+ * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
+ *
+ * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
+ */
+
+/***************************************************************************************************
+ * BROWSER POLYFILLS
+ */
+
+/** IE9, IE10 and IE11 requires all of the following polyfills. **/
+import 'core-js/es6/symbol';
+import 'core-js/es6/object';
+import 'core-js/es6/function';
+import 'core-js/es6/parse-int';
+import 'core-js/es6/parse-float';
+import 'core-js/es6/number';
+import 'core-js/es6/math';
+import 'core-js/es6/string';
+import 'core-js/es6/date';
+import 'core-js/es6/array';
+import 'core-js/es6/regexp';
+import 'core-js/es6/map';
+import 'core-js/es6/weak-map';
+import 'core-js/es6/set';
+
+/** IE10 and IE11 requires the following for NgClass support on SVG elements */
+// import 'classlist.js'; // Run `npm install --save classlist.js`.
+
+/** IE10 and IE11 requires the following to support `@angular/animation`. */
+// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
+
+
+/** Evergreen browsers require these. **/
+import 'core-js/es6/reflect';
+import 'core-js/es7/reflect';
+
+
+/** ALL Firefox browsers require the following to support `@angular/animation`. **/
+// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
+
+
+
+/***************************************************************************************************
+ * Zone JS is required by Angular itself.
+ */
+import 'zone.js/dist/zone'; // Included with Angular CLI.
+
+
+
+/***************************************************************************************************
+ * APPLICATION IMPORTS
+ */
+
+/**
+ * Date, currency, decimal and percent pipes.
+ * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10
+ */
+// import 'intl'; // Run `npm install --save intl`.
+/**
+ * Need to import at least one locale-data with intl.
+ */
+// import 'intl/locale-data/jsonp/en';
diff --git a/sample - Kopie/src/silent-refresh.html b/sample - Kopie/src/silent-refresh.html
new file mode 100644
index 00000000..7e3423c4
--- /dev/null
+++ b/sample - Kopie/src/silent-refresh.html
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/sample - Kopie/src/styles.css b/sample - Kopie/src/styles.css
new file mode 100644
index 00000000..90d4ee00
--- /dev/null
+++ b/sample - Kopie/src/styles.css
@@ -0,0 +1 @@
+/* You can add global styles to this file, and also import other style files */
diff --git a/sample - Kopie/src/test.ts b/sample - Kopie/src/test.ts
new file mode 100644
index 00000000..cd612eeb
--- /dev/null
+++ b/sample - Kopie/src/test.ts
@@ -0,0 +1,32 @@
+// This file is required by karma.conf.js and loads recursively all the .spec and framework files
+
+import 'zone.js/dist/long-stack-trace-zone';
+import 'zone.js/dist/proxy.js';
+import 'zone.js/dist/sync-test';
+import 'zone.js/dist/jasmine-patch';
+import 'zone.js/dist/async-test';
+import 'zone.js/dist/fake-async-test';
+import { getTestBed } from '@angular/core/testing';
+import {
+ BrowserDynamicTestingModule,
+ platformBrowserDynamicTesting
+} from '@angular/platform-browser-dynamic/testing';
+
+// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
+declare const __karma__: any;
+declare const require: any;
+
+// Prevent Karma from running prematurely.
+__karma__.loaded = function () {};
+
+// First, initialize the Angular testing environment.
+getTestBed().initTestEnvironment(
+ BrowserDynamicTestingModule,
+ platformBrowserDynamicTesting()
+);
+// Then we find all the tests.
+const context = require.context('./', true, /\.spec\.ts$/);
+// And load the modules.
+context.keys().map(context);
+// Finally, start Karma to run the tests.
+__karma__.start();
diff --git a/sample - Kopie/src/tsconfig.app.json b/sample - Kopie/src/tsconfig.app.json
new file mode 100644
index 00000000..b364c032
--- /dev/null
+++ b/sample - Kopie/src/tsconfig.app.json
@@ -0,0 +1,14 @@
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../out-tsc/app",
+ "module": "es2015",
+ "baseUrl": "",
+ "types": []
+ },
+
+ "exclude": [
+ "test.ts",
+ "**/*.spec.ts"
+ ]
+}
diff --git a/sample - Kopie/src/tsconfig.spec.json b/sample - Kopie/src/tsconfig.spec.json
new file mode 100644
index 00000000..510e3f1f
--- /dev/null
+++ b/sample - Kopie/src/tsconfig.spec.json
@@ -0,0 +1,20 @@
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../out-tsc/spec",
+ "module": "commonjs",
+ "target": "es5",
+ "baseUrl": "",
+ "types": [
+ "jasmine",
+ "node"
+ ]
+ },
+ "files": [
+ "test.ts"
+ ],
+ "include": [
+ "**/*.spec.ts",
+ "**/*.d.ts"
+ ]
+}
diff --git a/sample - Kopie/src/typings.d.ts b/sample - Kopie/src/typings.d.ts
new file mode 100644
index 00000000..ef5c7bd6
--- /dev/null
+++ b/sample - Kopie/src/typings.d.ts
@@ -0,0 +1,5 @@
+/* SystemJS module definition */
+declare var module: NodeModule;
+interface NodeModule {
+ id: string;
+}
diff --git a/sample - Kopie/tsconfig.json b/sample - Kopie/tsconfig.json
new file mode 100644
index 00000000..a6c016bf
--- /dev/null
+++ b/sample - Kopie/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "compileOnSave": false,
+ "compilerOptions": {
+ "outDir": "./dist/out-tsc",
+ "sourceMap": true,
+ "declaration": false,
+ "moduleResolution": "node",
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "target": "es5",
+ "typeRoots": [
+ "node_modules/@types"
+ ],
+ "lib": [
+ "es2017",
+ "dom"
+ ]
+ }
+}
diff --git a/sample - Kopie/tslint.json b/sample - Kopie/tslint.json
new file mode 100644
index 00000000..0db5751c
--- /dev/null
+++ b/sample - Kopie/tslint.json
@@ -0,0 +1,142 @@
+{
+ "rulesDirectory": [
+ "node_modules/codelyzer"
+ ],
+ "rules": {
+ "arrow-return-shorthand": true,
+ "callable-types": true,
+ "class-name": true,
+ "comment-format": [
+ true,
+ "check-space"
+ ],
+ "curly": true,
+ "eofline": true,
+ "forin": true,
+ "import-blacklist": [
+ true,
+ "rxjs"
+ ],
+ "import-spacing": true,
+ "indent": [
+ true,
+ "spaces"
+ ],
+ "interface-over-type-literal": true,
+ "label-position": true,
+ "max-line-length": [
+ true,
+ 140
+ ],
+ "member-access": false,
+ "member-ordering": [
+ true,
+ {
+ "order": [
+ "static-field",
+ "instance-field",
+ "static-method",
+ "instance-method"
+ ]
+ }
+ ],
+ "no-arg": true,
+ "no-bitwise": true,
+ "no-console": [
+ true,
+ "debug",
+ "info",
+ "time",
+ "timeEnd",
+ "trace"
+ ],
+ "no-construct": true,
+ "no-debugger": true,
+ "no-duplicate-super": true,
+ "no-empty": false,
+ "no-empty-interface": true,
+ "no-eval": true,
+ "no-inferrable-types": [
+ true,
+ "ignore-params"
+ ],
+ "no-misused-new": true,
+ "no-non-null-assertion": true,
+ "no-shadowed-variable": true,
+ "no-string-literal": false,
+ "no-string-throw": true,
+ "no-switch-case-fall-through": true,
+ "no-trailing-whitespace": true,
+ "no-unnecessary-initializer": true,
+ "no-unused-expression": 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"
+ ],
+ "prefer-const": true,
+ "quotemark": [
+ true,
+ "single"
+ ],
+ "radix": true,
+ "semicolon": [
+ true,
+ "always"
+ ],
+ "triple-equals": [
+ true,
+ "allow-null-check"
+ ],
+ "typedef-whitespace": [
+ true,
+ {
+ "call-signature": "nospace",
+ "index-signature": "nospace",
+ "parameter": "nospace",
+ "property-declaration": "nospace",
+ "variable-declaration": "nospace"
+ }
+ ],
+ "typeof-compare": true,
+ "unified-signatures": true,
+ "variable-name": false,
+ "whitespace": [
+ true,
+ "check-branch",
+ "check-decl",
+ "check-operator",
+ "check-separator",
+ "check-type"
+ ],
+ "directive-selector": [
+ true,
+ "attribute",
+ "app",
+ "camelCase"
+ ],
+ "component-selector": [
+ true,
+ "element",
+ "app",
+ "kebab-case"
+ ],
+ "use-input-property-decorator": true,
+ "use-output-property-decorator": true,
+ "use-host-property-decorator": true,
+ "no-input-rename": true,
+ "no-output-rename": true,
+ "use-life-cycle-interface": true,
+ "use-pipe-transform-interface": true,
+ "component-class-suffix": true,
+ "directive-class-suffix": true,
+ "no-access-missing-member": true,
+ "templates-use-public": true,
+ "invoke-injectable": true
+ }
+}