├── .editorconfig
├── .eslintrc.json
├── .gitignore
├── .prettierrc
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── angular.json
├── cypress.config.ts
├── cypress
├── e2e
│ ├── counter-helpers.cy.ts
│ └── counter.cy.ts
├── support
│ ├── commands.ts
│ └── e2e.ts
└── tsconfig.json
├── docs
├── 3rdpartylicenses.txt
├── assets
│ └── counter.json
├── favicon.ico
├── index.html
├── main-es2015.34bbdc20fd140520aee0.js
├── main-es5.34bbdc20fd140520aee0.js
├── polyfills-es2015.f2c5ab749249a66bdf26.js
├── polyfills-es5.049f620af8c864cf4d88.js
├── runtime-es2015.1eba213af0b233498d9d.js
├── runtime-es5.1eba213af0b233498d9d.js
└── styles.7639ff453898f4e0c1be.css
├── karma.conf.js
├── package-lock.json
├── package.json
├── src
├── app
│ ├── actions
│ │ └── counter.actions.ts
│ ├── app.component.html
│ ├── app.component.spec.ts
│ ├── app.component.spectator.spec.ts
│ ├── app.component.ts
│ ├── app.module.spec.ts
│ ├── app.module.ts
│ ├── app.routing.module.spec.ts
│ ├── app.routing.module.ts
│ ├── components
│ │ ├── counter
│ │ │ ├── counter.component.css
│ │ │ ├── counter.component.html
│ │ │ ├── counter.component.spec.ts
│ │ │ ├── counter.component.spectator.spec.ts
│ │ │ └── counter.component.ts
│ │ ├── home
│ │ │ ├── home-component.ng-mocks.spec.ts
│ │ │ ├── home-component.spectator.spec.ts
│ │ │ ├── home.component.css
│ │ │ ├── home.component.fake-child.spec.ts
│ │ │ ├── home.component.html
│ │ │ ├── home.component.spec.ts
│ │ │ └── home.component.ts
│ │ ├── ngrx-counter
│ │ │ ├── ngrx-counter.component.css
│ │ │ ├── ngrx-counter.component.html
│ │ │ ├── ngrx-counter.component.spec.ts
│ │ │ ├── ngrx-counter.component.spectator.spec.ts
│ │ │ └── ngrx-counter.component.ts
│ │ ├── service-counter
│ │ │ ├── service-counter.component.css
│ │ │ ├── service-counter.component.html
│ │ │ ├── service-counter.component.spec.ts
│ │ │ ├── service-counter.component.spectator.spec.ts
│ │ │ └── service-counter.component.ts
│ │ └── standalone-service-counter
│ │ │ ├── service-counter.component.spectator.spec.ts
│ │ │ ├── standalone-service-counter.component.css
│ │ │ ├── standalone-service-counter.component.html
│ │ │ ├── standalone-service-counter.component.spec.ts
│ │ │ └── standalone-service-counter.component.ts
│ ├── effects
│ │ ├── counter.effects.spec.ts
│ │ └── counter.effects.ts
│ ├── reducers
│ │ ├── counter.reducer.spec.ts
│ │ ├── counter.reducer.ts
│ │ └── index.ts
│ ├── services
│ │ ├── counter-api.service.spec.ts
│ │ ├── counter-api.service.spectator.spec.ts
│ │ ├── counter-api.service.ts
│ │ ├── counter.service.spec.ts
│ │ ├── counter.service.ts
│ │ └── todos-service.spec.ts
│ ├── shared
│ │ ├── app-state.ts
│ │ └── selectors.ts
│ └── spec-helpers
│ │ └── element.spec-helper.ts
├── assets
│ ├── .gitkeep
│ └── counter.json
├── environments
│ ├── environment.prod.ts
│ └── environment.ts
├── favicon.ico
├── index.html
├── main.ts
└── styles.css
├── tsconfig.app.json
├── tsconfig.json
└── tsconfig.spec.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.{js,ts}]
12 | quote_type = single
13 |
14 | [*.md]
15 | max_line_length = off
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "ignorePatterns": ["projects/**/*"],
4 | "overrides": [
5 | {
6 | "files": ["*.ts"],
7 | "parserOptions": {
8 | "project": ["tsconfig.json", "e2e/tsconfig.json"],
9 | "createDefaultProgram": true
10 | },
11 | "extends": [
12 | "plugin:@angular-eslint/recommended",
13 | "plugin:@angular-eslint/template/process-inline-templates"
14 | ],
15 | "rules": {
16 | "@angular-eslint/directive-selector": [
17 | "error",
18 | {
19 | "type": "attribute",
20 | "prefix": "app",
21 | "style": "camelCase"
22 | }
23 | ],
24 | "@angular-eslint/component-selector": [
25 | "error",
26 | {
27 | "type": "element",
28 | "prefix": "app",
29 | "style": "kebab-case"
30 | }
31 | ]
32 | }
33 | },
34 | {
35 | "files": ["*.html"],
36 | "extends": ["plugin:@angular-eslint/template/recommended"],
37 | "rules": {}
38 | }
39 | ]
40 | }
41 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # Compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 | /bazel-out
8 |
9 | # Node
10 | /node_modules
11 | npm-debug.log
12 | yarn-error.log
13 |
14 | # IDEs and editors
15 | .idea/
16 | .project
17 | .classpath
18 | .c9/
19 | *.launch
20 | .settings/
21 | *.sublime-workspace
22 |
23 | # Visual Studio Code
24 | .vscode/*
25 | !.vscode/settings.json
26 | !.vscode/tasks.json
27 | !.vscode/launch.json
28 | !.vscode/extensions.json
29 | .history/*
30 |
31 | # Miscellaneous
32 | /.angular/cache
33 | .sass-cache/
34 | /connect.lock
35 | /coverage
36 | /libpeerconnection.log
37 | testem.log
38 | /typings
39 |
40 | # System files
41 | .DS_Store
42 | Thumbs.db
43 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "all",
3 | "printWidth": 90,
4 | "tabWidth": 2,
5 | "singleQuote": true,
6 | "arrowParens": "always"
7 | }
8 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.defaultFormatter": "esbenp.prettier-vscode",
3 | "editor.formatOnSave": true
4 | }
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Angular Counter Component
2 |
3 | 📖 This example is part of the **[free online book: Testing Angular – A Guide to Robust Angular Applications
4 | ](https://testing-angular.com/)**. 📖
5 |
6 | ## Overview
7 |
8 | This repository builds a simple counter with Angular in three ways:
9 |
10 | - [CounterComponent](src/app/components/counter/): Counter that manages its own state. Has an Input and an Output.
11 | - [ServiceCounterComponent](src/app/components/service-counter): Counter that stores the state in shared service.
12 | - [NgRxCounterComponent](src/app/components/ngrx-counter): Counter that uses NgRx to manage the count and NgRx effects to persist them on the server.
13 |
14 | ## Related projects
15 |
16 | - [Angular Flickr Search](https://github.com/9elements/angular-flickr-search) – a more complex example app
17 | - [Angular testing workshop](https://9elements.github.io/angular-testing-workshop/)
18 |
19 | ## Development server
20 |
21 | - Clone the repository, change into the `angular-workshop` directory
22 | - `npm install`
23 | - `npm install -g @angular/cli`
24 | - `ng serve`
25 | - Navigate to http://localhost:4200/
26 |
27 | ## Running unit & integration tests
28 |
29 | Run `ng test` to execute the unit & integration tests with Karma and Jasmine.
30 |
31 | ## Running end-to-end tests with Cypress
32 |
33 | Run `ng run angular-workshop:cypress-run` to execute the Cypress end-to-end tests. (This starts the development server automatically.)
34 |
35 | Run `ng run angular-workshop:cypress-open` to start the interactive Cypress test runner.
36 |
37 | ## Deployment
38 |
39 | Run `npm run deploy` to the deploy the code to [https://9elements.github.io/angular-workshop/].
40 |
--------------------------------------------------------------------------------
/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "angular-workshop": {
7 | "projectType": "application",
8 | "schematics": {
9 | "@schematics/angular:application": {
10 | "strict": true
11 | }
12 | },
13 | "root": "",
14 | "sourceRoot": "src",
15 | "prefix": "app",
16 | "architect": {
17 | "build": {
18 | "builder": "@angular-devkit/build-angular:browser",
19 | "options": {
20 | "outputPath": "dist/angular-workshop",
21 | "index": "src/index.html",
22 | "main": "src/main.ts",
23 | "polyfills": ["zone.js"],
24 | "tsConfig": "tsconfig.app.json",
25 | "assets": ["src/favicon.ico", "src/assets"],
26 | "styles": ["src/styles.css"],
27 | "scripts": []
28 | },
29 | "configurations": {
30 | "production": {
31 | "budgets": [
32 | {
33 | "type": "initial",
34 | "maximumWarning": "500kb",
35 | "maximumError": "1mb"
36 | },
37 | {
38 | "type": "anyComponentStyle",
39 | "maximumWarning": "2kb",
40 | "maximumError": "4kb"
41 | }
42 | ],
43 | "fileReplacements": [
44 | {
45 | "replace": "src/environments/environment.ts",
46 | "with": "src/environments/environment.prod.ts"
47 | }
48 | ],
49 | "outputHashing": "all"
50 | },
51 | "development": {
52 | "buildOptimizer": false,
53 | "optimization": false,
54 | "vendorChunk": true,
55 | "extractLicenses": false,
56 | "sourceMap": true,
57 | "namedChunks": true
58 | }
59 | },
60 | "defaultConfiguration": "production"
61 | },
62 | "serve": {
63 | "builder": "@angular-devkit/build-angular:dev-server",
64 | "configurations": {
65 | "production": {
66 | "browserTarget": "angular-workshop:build:production"
67 | },
68 | "development": {
69 | "browserTarget": "angular-workshop:build:development"
70 | }
71 | },
72 | "defaultConfiguration": "development"
73 | },
74 | "extract-i18n": {
75 | "builder": "@angular-devkit/build-angular:extract-i18n",
76 | "options": {
77 | "browserTarget": "angular-workshop:build"
78 | }
79 | },
80 | "test": {
81 | "builder": "@angular-devkit/build-angular:karma",
82 | "options": {
83 | "polyfills": ["zone.js", "zone.js/testing"],
84 | "tsConfig": "tsconfig.spec.json",
85 | "karmaConfig": "karma.conf.js",
86 | "assets": ["src/favicon.ico", "src/assets"],
87 | "styles": ["src/styles.css"],
88 | "scripts": []
89 | }
90 | },
91 | "lint": {
92 | "builder": "@angular-eslint/builder:lint",
93 | "options": {
94 | "lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
95 | }
96 | },
97 | "deploy": {
98 | "builder": "angular-cli-ghpages:deploy",
99 | "options": {}
100 | },
101 | "cypress-run": {
102 | "builder": "@cypress/schematic:cypress",
103 | "options": {
104 | "devServerTarget": "angular-workshop:serve"
105 | },
106 | "configurations": {
107 | "production": {
108 | "devServerTarget": "angular-workshop:serve:production"
109 | }
110 | }
111 | },
112 | "cypress-open": {
113 | "builder": "@cypress/schematic:cypress",
114 | "options": {
115 | "watch": true,
116 | "headless": false
117 | }
118 | },
119 | "e2e": {
120 | "builder": "@cypress/schematic:cypress",
121 | "options": {
122 | "devServerTarget": "angular-workshop:serve",
123 | "watch": true,
124 | "headless": false
125 | },
126 | "configurations": {
127 | "production": {
128 | "devServerTarget": "angular-workshop:serve:production"
129 | }
130 | }
131 | }
132 | }
133 | }
134 | },
135 | "cli": {
136 | "analytics": false,
137 | "schematicCollections": ["@cypress/schematic", "@schematics/angular"]
138 | },
139 | "schematics": {
140 | "@angular-eslint/schematics:application": {
141 | "setParserOptionsProject": true
142 | },
143 | "@angular-eslint/schematics:library": {
144 | "setParserOptionsProject": true
145 | }
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/cypress.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'cypress';
2 |
3 | export default defineConfig({
4 | e2e: {
5 | baseUrl: 'http://localhost:4200',
6 | video: false,
7 | experimentalRunAllSpecs: true,
8 | },
9 | });
10 |
--------------------------------------------------------------------------------
/cypress/e2e/counter-helpers.cy.ts:
--------------------------------------------------------------------------------
1 | describe('Counter (with helpers)', () => {
2 | beforeEach(() => {
3 | cy.visit('/');
4 | });
5 |
6 | it('has the correct title', () => {
7 | cy.title().should('equal', 'Angular Workshop: Counters');
8 | });
9 |
10 | it('increments the count', () => {
11 | cy.byTestId('count').first().should('have.text', '5');
12 | cy.byTestId('increment-button').first().click();
13 | cy.byTestId('count').first().should('have.text', '6');
14 | });
15 |
16 | it('decrements the count', () => {
17 | cy.byTestId('decrement-button').first().click();
18 | cy.byTestId('count').first().should('have.text', '4');
19 | });
20 |
21 | it('resets the count', () => {
22 | cy.byTestId('reset-input').first().type('123');
23 | cy.byTestId('reset-button').first().click();
24 | cy.byTestId('count').first().should('have.text', '123');
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/cypress/e2e/counter.cy.ts:
--------------------------------------------------------------------------------
1 | describe('Counter', () => {
2 | beforeEach(() => {
3 | cy.visit('/');
4 | });
5 |
6 | it('has the correct title', () => {
7 | cy.title().should('equal', 'Angular Workshop: Counters');
8 | });
9 |
10 | it('increments the count', () => {
11 | cy.get('[data-testid="count"]').first().should('have.text', '5');
12 | cy.get('[data-testid="increment-button"]').first().click();
13 | cy.get('[data-testid="count"]').first().should('have.text', '6');
14 | });
15 |
16 | it('decrements the count', () => {
17 | cy.get('[data-testid="decrement-button"]').first().click();
18 | cy.get('[data-testid="count"]').first().should('have.text', '4');
19 | });
20 |
21 | it('resets the count', () => {
22 | cy.get('[data-testid="reset-input"]').first().type('123');
23 | cy.get('[data-testid="reset-button"]').first().click();
24 | cy.get('[data-testid="count"]').first().should('have.text', '123');
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/cypress/support/commands.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example namespace declaration will help
3 | // with Intellisense and code completion in your
4 | // IDE or Text Editor.
5 | // ***********************************************
6 | // declare namespace Cypress {
7 | // interface Chainable {
8 | // customCommand(param: any): typeof customCommand;
9 | // }
10 | // }
11 | //
12 | // function customCommand(param: any): void {
13 | // console.warn(param);
14 | // }
15 | //
16 | // NOTE: You can use it like so:
17 | // Cypress.Commands.add('customCommand', customCommand);
18 | //
19 | // ***********************************************
20 | // This example commands.js shows you how to
21 | // create various custom commands and overwrite
22 | // existing commands.
23 | //
24 | // For more comprehensive examples of custom
25 | // commands please read more here:
26 | // https://on.cypress.io/custom-commands
27 | // ***********************************************
28 | //
29 | //
30 | // -- This is a parent command --
31 | // Cypress.Commands.add("login", (email, password) => { ... })
32 | //
33 | //
34 | // -- This is a child command --
35 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
36 | //
37 | //
38 | // -- This is a dual command --
39 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
40 | //
41 | //
42 | // -- This will overwrite an existing command --
43 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
44 |
45 | declare namespace Cypress {
46 | interface Chainable {
47 | /**
48 | * Get one or more DOM elements by test id.
49 | *
50 | * @param id The test id
51 | * @param options The same options as cy.get
52 | */
53 | byTestId(
54 | id: string,
55 | options?: Partial<
56 | Cypress.Loggable & Cypress.Timeoutable & Cypress.Withinable & Cypress.Shadow
57 | >,
58 | ): Cypress.Chainable>;
59 | }
60 | }
61 |
62 | Cypress.Commands.add(
63 | 'byTestId',
64 | // Borrow the signature from cy.get
65 | (
66 | id: string,
67 | options?: Partial<
68 | Cypress.Loggable & Cypress.Timeoutable & Cypress.Withinable & Cypress.Shadow
69 | >,
70 | ): Cypress.Chainable> => cy.get(`[data-testid="${id}"]`, options),
71 | );
72 |
--------------------------------------------------------------------------------
/cypress/support/e2e.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // When a command from ./commands is ready to use, import with `import './commands'` syntax
17 | import './commands';
18 |
--------------------------------------------------------------------------------
/cypress/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "include": ["**/*.ts"],
4 | "compilerOptions": {
5 | "sourceMap": false,
6 | "types": ["cypress"]
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/docs/3rdpartylicenses.txt:
--------------------------------------------------------------------------------
1 | @angular-devkit/build-angular
2 | MIT
3 | The MIT License
4 |
5 | Copyright (c) 2017 Google, Inc.
6 |
7 | Permission is hereby granted, free of charge, to any person obtaining a copy
8 | of this software and associated documentation files (the "Software"), to deal
9 | in the Software without restriction, including without limitation the rights
10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | copies of the Software, and to permit persons to whom the Software is
12 | furnished to do so, subject to the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be included in all
15 | copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 | SOFTWARE.
24 |
25 |
26 | @angular/common
27 | MIT
28 |
29 | @angular/core
30 | MIT
31 |
32 | @angular/platform-browser
33 | MIT
34 |
35 | @ngrx/effects
36 | MIT
37 |
38 | @ngrx/store
39 | MIT
40 |
41 | @ngrx/store-devtools
42 | MIT
43 |
44 | core-js
45 | MIT
46 | Copyright (c) 2014-2020 Denis Pushkarev
47 |
48 | Permission is hereby granted, free of charge, to any person obtaining a copy
49 | of this software and associated documentation files (the "Software"), to deal
50 | in the Software without restriction, including without limitation the rights
51 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
52 | copies of the Software, and to permit persons to whom the Software is
53 | furnished to do so, subject to the following conditions:
54 |
55 | The above copyright notice and this permission notice shall be included in
56 | all copies or substantial portions of the Software.
57 |
58 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
59 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
60 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
61 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
62 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
63 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
64 | THE SOFTWARE.
65 |
66 |
67 | regenerator-runtime
68 | MIT
69 | MIT License
70 |
71 | Copyright (c) 2014-present, Facebook, Inc.
72 |
73 | Permission is hereby granted, free of charge, to any person obtaining a copy
74 | of this software and associated documentation files (the "Software"), to deal
75 | in the Software without restriction, including without limitation the rights
76 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
77 | copies of the Software, and to permit persons to whom the Software is
78 | furnished to do so, subject to the following conditions:
79 |
80 | The above copyright notice and this permission notice shall be included in all
81 | copies or substantial portions of the Software.
82 |
83 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
84 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
85 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
86 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
87 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
88 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
89 | SOFTWARE.
90 |
91 |
92 | rxjs
93 | Apache-2.0
94 | Apache License
95 | Version 2.0, January 2004
96 | http://www.apache.org/licenses/
97 |
98 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
99 |
100 | 1. Definitions.
101 |
102 | "License" shall mean the terms and conditions for use, reproduction,
103 | and distribution as defined by Sections 1 through 9 of this document.
104 |
105 | "Licensor" shall mean the copyright owner or entity authorized by
106 | the copyright owner that is granting the License.
107 |
108 | "Legal Entity" shall mean the union of the acting entity and all
109 | other entities that control, are controlled by, or are under common
110 | control with that entity. For the purposes of this definition,
111 | "control" means (i) the power, direct or indirect, to cause the
112 | direction or management of such entity, whether by contract or
113 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
114 | outstanding shares, or (iii) beneficial ownership of such entity.
115 |
116 | "You" (or "Your") shall mean an individual or Legal Entity
117 | exercising permissions granted by this License.
118 |
119 | "Source" form shall mean the preferred form for making modifications,
120 | including but not limited to software source code, documentation
121 | source, and configuration files.
122 |
123 | "Object" form shall mean any form resulting from mechanical
124 | transformation or translation of a Source form, including but
125 | not limited to compiled object code, generated documentation,
126 | and conversions to other media types.
127 |
128 | "Work" shall mean the work of authorship, whether in Source or
129 | Object form, made available under the License, as indicated by a
130 | copyright notice that is included in or attached to the work
131 | (an example is provided in the Appendix below).
132 |
133 | "Derivative Works" shall mean any work, whether in Source or Object
134 | form, that is based on (or derived from) the Work and for which the
135 | editorial revisions, annotations, elaborations, or other modifications
136 | represent, as a whole, an original work of authorship. For the purposes
137 | of this License, Derivative Works shall not include works that remain
138 | separable from, or merely link (or bind by name) to the interfaces of,
139 | the Work and Derivative Works thereof.
140 |
141 | "Contribution" shall mean any work of authorship, including
142 | the original version of the Work and any modifications or additions
143 | to that Work or Derivative Works thereof, that is intentionally
144 | submitted to Licensor for inclusion in the Work by the copyright owner
145 | or by an individual or Legal Entity authorized to submit on behalf of
146 | the copyright owner. For the purposes of this definition, "submitted"
147 | means any form of electronic, verbal, or written communication sent
148 | to the Licensor or its representatives, including but not limited to
149 | communication on electronic mailing lists, source code control systems,
150 | and issue tracking systems that are managed by, or on behalf of, the
151 | Licensor for the purpose of discussing and improving the Work, but
152 | excluding communication that is conspicuously marked or otherwise
153 | designated in writing by the copyright owner as "Not a Contribution."
154 |
155 | "Contributor" shall mean Licensor and any individual or Legal Entity
156 | on behalf of whom a Contribution has been received by Licensor and
157 | subsequently incorporated within the Work.
158 |
159 | 2. Grant of Copyright License. Subject to the terms and conditions of
160 | this License, each Contributor hereby grants to You a perpetual,
161 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
162 | copyright license to reproduce, prepare Derivative Works of,
163 | publicly display, publicly perform, sublicense, and distribute the
164 | Work and such Derivative Works in Source or Object form.
165 |
166 | 3. Grant of Patent License. Subject to the terms and conditions of
167 | this License, each Contributor hereby grants to You a perpetual,
168 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
169 | (except as stated in this section) patent license to make, have made,
170 | use, offer to sell, sell, import, and otherwise transfer the Work,
171 | where such license applies only to those patent claims licensable
172 | by such Contributor that are necessarily infringed by their
173 | Contribution(s) alone or by combination of their Contribution(s)
174 | with the Work to which such Contribution(s) was submitted. If You
175 | institute patent litigation against any entity (including a
176 | cross-claim or counterclaim in a lawsuit) alleging that the Work
177 | or a Contribution incorporated within the Work constitutes direct
178 | or contributory patent infringement, then any patent licenses
179 | granted to You under this License for that Work shall terminate
180 | as of the date such litigation is filed.
181 |
182 | 4. Redistribution. You may reproduce and distribute copies of the
183 | Work or Derivative Works thereof in any medium, with or without
184 | modifications, and in Source or Object form, provided that You
185 | meet the following conditions:
186 |
187 | (a) You must give any other recipients of the Work or
188 | Derivative Works a copy of this License; and
189 |
190 | (b) You must cause any modified files to carry prominent notices
191 | stating that You changed the files; and
192 |
193 | (c) You must retain, in the Source form of any Derivative Works
194 | that You distribute, all copyright, patent, trademark, and
195 | attribution notices from the Source form of the Work,
196 | excluding those notices that do not pertain to any part of
197 | the Derivative Works; and
198 |
199 | (d) If the Work includes a "NOTICE" text file as part of its
200 | distribution, then any Derivative Works that You distribute must
201 | include a readable copy of the attribution notices contained
202 | within such NOTICE file, excluding those notices that do not
203 | pertain to any part of the Derivative Works, in at least one
204 | of the following places: within a NOTICE text file distributed
205 | as part of the Derivative Works; within the Source form or
206 | documentation, if provided along with the Derivative Works; or,
207 | within a display generated by the Derivative Works, if and
208 | wherever such third-party notices normally appear. The contents
209 | of the NOTICE file are for informational purposes only and
210 | do not modify the License. You may add Your own attribution
211 | notices within Derivative Works that You distribute, alongside
212 | or as an addendum to the NOTICE text from the Work, provided
213 | that such additional attribution notices cannot be construed
214 | as modifying the License.
215 |
216 | You may add Your own copyright statement to Your modifications and
217 | may provide additional or different license terms and conditions
218 | for use, reproduction, or distribution of Your modifications, or
219 | for any such Derivative Works as a whole, provided Your use,
220 | reproduction, and distribution of the Work otherwise complies with
221 | the conditions stated in this License.
222 |
223 | 5. Submission of Contributions. Unless You explicitly state otherwise,
224 | any Contribution intentionally submitted for inclusion in the Work
225 | by You to the Licensor shall be under the terms and conditions of
226 | this License, without any additional terms or conditions.
227 | Notwithstanding the above, nothing herein shall supersede or modify
228 | the terms of any separate license agreement you may have executed
229 | with Licensor regarding such Contributions.
230 |
231 | 6. Trademarks. This License does not grant permission to use the trade
232 | names, trademarks, service marks, or product names of the Licensor,
233 | except as required for reasonable and customary use in describing the
234 | origin of the Work and reproducing the content of the NOTICE file.
235 |
236 | 7. Disclaimer of Warranty. Unless required by applicable law or
237 | agreed to in writing, Licensor provides the Work (and each
238 | Contributor provides its Contributions) on an "AS IS" BASIS,
239 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
240 | implied, including, without limitation, any warranties or conditions
241 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
242 | PARTICULAR PURPOSE. You are solely responsible for determining the
243 | appropriateness of using or redistributing the Work and assume any
244 | risks associated with Your exercise of permissions under this License.
245 |
246 | 8. Limitation of Liability. In no event and under no legal theory,
247 | whether in tort (including negligence), contract, or otherwise,
248 | unless required by applicable law (such as deliberate and grossly
249 | negligent acts) or agreed to in writing, shall any Contributor be
250 | liable to You for damages, including any direct, indirect, special,
251 | incidental, or consequential damages of any character arising as a
252 | result of this License or out of the use or inability to use the
253 | Work (including but not limited to damages for loss of goodwill,
254 | work stoppage, computer failure or malfunction, or any and all
255 | other commercial damages or losses), even if such Contributor
256 | has been advised of the possibility of such damages.
257 |
258 | 9. Accepting Warranty or Additional Liability. While redistributing
259 | the Work or Derivative Works thereof, You may choose to offer,
260 | and charge a fee for, acceptance of support, warranty, indemnity,
261 | or other liability obligations and/or rights consistent with this
262 | License. However, in accepting such obligations, You may act only
263 | on Your own behalf and on Your sole responsibility, not on behalf
264 | of any other Contributor, and only if You agree to indemnify,
265 | defend, and hold each Contributor harmless for any liability
266 | incurred by, or claims asserted against, such Contributor by reason
267 | of your accepting any such warranty or additional liability.
268 |
269 | END OF TERMS AND CONDITIONS
270 |
271 | APPENDIX: How to apply the Apache License to your work.
272 |
273 | To apply the Apache License to your work, attach the following
274 | boilerplate notice, with the fields enclosed by brackets "[]"
275 | replaced with your own identifying information. (Don't include
276 | the brackets!) The text should be enclosed in the appropriate
277 | comment syntax for the file format. We also recommend that a
278 | file or class name and description of purpose be included on the
279 | same "printed page" as the copyright notice for easier
280 | identification within third-party archives.
281 |
282 | Copyright (c) 2015-2018 Google, Inc., Netflix, Inc., Microsoft Corp. and contributors
283 |
284 | Licensed under the Apache License, Version 2.0 (the "License");
285 | you may not use this file except in compliance with the License.
286 | You may obtain a copy of the License at
287 |
288 | http://www.apache.org/licenses/LICENSE-2.0
289 |
290 | Unless required by applicable law or agreed to in writing, software
291 | distributed under the License is distributed on an "AS IS" BASIS,
292 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
293 | See the License for the specific language governing permissions and
294 | limitations under the License.
295 |
296 |
297 |
298 | zone.js
299 | MIT
300 | The MIT License
301 |
302 | Copyright (c) 2010-2020 Google LLC. http://angular.io/license
303 |
304 | Permission is hereby granted, free of charge, to any person obtaining a copy
305 | of this software and associated documentation files (the "Software"), to deal
306 | in the Software without restriction, including without limitation the rights
307 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
308 | copies of the Software, and to permit persons to whom the Software is
309 | furnished to do so, subject to the following conditions:
310 |
311 | The above copyright notice and this permission notice shall be included in
312 | all copies or substantial portions of the Software.
313 |
314 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
315 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
316 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
317 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
318 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
319 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
320 | THE SOFTWARE.
321 |
--------------------------------------------------------------------------------
/docs/assets/counter.json:
--------------------------------------------------------------------------------
1 | { "description": "placeholder file for testing counter effects" }
2 |
--------------------------------------------------------------------------------
/docs/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/9elements/angular-workshop/a4cac55e0be4796879b7030a689add058c8b150b/docs/favicon.ico
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Angular Workshop: Counters
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/docs/polyfills-es2015.f2c5ab749249a66bdf26.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[2],{2:function(e,t,n){e.exports=n("hN/g")},"hN/g":function(e,t,n){"use strict";n.r(t),n("pDpN")},pDpN:function(e,t,n){var o,r;void 0===(r="function"==typeof(o=function(){"use strict";!function(e){const t=e.performance;function n(e){t&&t.mark&&t.mark(e)}function o(e,n){t&&t.measure&&t.measure(e,n)}n("Zone");const r=e.__Zone_symbol_prefix||"__zone_symbol__";function s(e){return r+e}const a=!0===e[s("forceDuplicateZoneCheck")];if(e.Zone){if(a||"function"!=typeof e.Zone.__symbol__)throw new Error("Zone already loaded.");return e.Zone}class i{constructor(e,t){this._parent=e,this._name=t?t.name||"unnamed":"",this._properties=t&&t.properties||{},this._zoneDelegate=new l(this,this._parent&&this._parent._zoneDelegate,t)}static assertZonePatched(){if(e.Promise!==C.ZoneAwarePromise)throw new Error("Zone.js has detected that ZoneAwarePromise `(window|global).Promise` has been overwritten.\nMost likely cause is that a Promise polyfill has been loaded after Zone.js (Polyfilling Promise api is not necessary when zone.js is loaded. If you must load one, do so before loading zone.js.)")}static get root(){let e=i.current;for(;e.parent;)e=e.parent;return e}static get current(){return z.zone}static get currentTask(){return j}static __load_patch(t,r){if(C.hasOwnProperty(t)){if(a)throw Error("Already loaded patch: "+t)}else if(!e["__Zone_disable_"+t]){const s="Zone:"+t;n(s),C[t]=r(e,i,O),o(s,s)}}get parent(){return this._parent}get name(){return this._name}get(e){const t=this.getZoneWith(e);if(t)return t._properties[e]}getZoneWith(e){let t=this;for(;t;){if(t._properties.hasOwnProperty(e))return t;t=t._parent}return null}fork(e){if(!e)throw new Error("ZoneSpec required!");return this._zoneDelegate.fork(this,e)}wrap(e,t){if("function"!=typeof e)throw new Error("Expecting function got: "+e);const n=this._zoneDelegate.intercept(this,e,t),o=this;return function(){return o.runGuarded(n,this,arguments,t)}}run(e,t,n,o){z={parent:z,zone:this};try{return this._zoneDelegate.invoke(this,e,t,n,o)}finally{z=z.parent}}runGuarded(e,t=null,n,o){z={parent:z,zone:this};try{try{return this._zoneDelegate.invoke(this,e,t,n,o)}catch(r){if(this._zoneDelegate.handleError(this,r))throw r}}finally{z=z.parent}}runTask(e,t,n){if(e.zone!=this)throw new Error("A task can only be run in the zone of creation! (Creation: "+(e.zone||y).name+"; Execution: "+this.name+")");if(e.state===v&&(e.type===P||e.type===D))return;const o=e.state!=E;o&&e._transitionTo(E,T),e.runCount++;const r=j;j=e,z={parent:z,zone:this};try{e.type==D&&e.data&&!e.data.isPeriodic&&(e.cancelFn=void 0);try{return this._zoneDelegate.invokeTask(this,e,t,n)}catch(s){if(this._zoneDelegate.handleError(this,s))throw s}}finally{e.state!==v&&e.state!==Z&&(e.type==P||e.data&&e.data.isPeriodic?o&&e._transitionTo(T,E):(e.runCount=0,this._updateTaskCount(e,-1),o&&e._transitionTo(v,E,v))),z=z.parent,j=r}}scheduleTask(e){if(e.zone&&e.zone!==this){let t=this;for(;t;){if(t===e.zone)throw Error(`can not reschedule task to ${this.name} which is descendants of the original zone ${e.zone.name}`);t=t.parent}}e._transitionTo(b,v);const t=[];e._zoneDelegates=t,e._zone=this;try{e=this._zoneDelegate.scheduleTask(this,e)}catch(n){throw e._transitionTo(Z,b,v),this._zoneDelegate.handleError(this,n),n}return e._zoneDelegates===t&&this._updateTaskCount(e,1),e.state==b&&e._transitionTo(T,b),e}scheduleMicroTask(e,t,n,o){return this.scheduleTask(new u(S,e,t,n,o,void 0))}scheduleMacroTask(e,t,n,o,r){return this.scheduleTask(new u(D,e,t,n,o,r))}scheduleEventTask(e,t,n,o,r){return this.scheduleTask(new u(P,e,t,n,o,r))}cancelTask(e){if(e.zone!=this)throw new Error("A task can only be cancelled in the zone of creation! (Creation: "+(e.zone||y).name+"; Execution: "+this.name+")");e._transitionTo(w,T,E);try{this._zoneDelegate.cancelTask(this,e)}catch(t){throw e._transitionTo(Z,w),this._zoneDelegate.handleError(this,t),t}return this._updateTaskCount(e,-1),e._transitionTo(v,w),e.runCount=0,e}_updateTaskCount(e,t){const n=e._zoneDelegates;-1==t&&(e._zoneDelegates=null);for(let o=0;oe.hasTask(n,o),onScheduleTask:(e,t,n,o)=>e.scheduleTask(n,o),onInvokeTask:(e,t,n,o,r,s)=>e.invokeTask(n,o,r,s),onCancelTask:(e,t,n,o)=>e.cancelTask(n,o)};class l{constructor(e,t,n){this._taskCounts={microTask:0,macroTask:0,eventTask:0},this.zone=e,this._parentDelegate=t,this._forkZS=n&&(n&&n.onFork?n:t._forkZS),this._forkDlgt=n&&(n.onFork?t:t._forkDlgt),this._forkCurrZone=n&&(n.onFork?this.zone:t._forkCurrZone),this._interceptZS=n&&(n.onIntercept?n:t._interceptZS),this._interceptDlgt=n&&(n.onIntercept?t:t._interceptDlgt),this._interceptCurrZone=n&&(n.onIntercept?this.zone:t._interceptCurrZone),this._invokeZS=n&&(n.onInvoke?n:t._invokeZS),this._invokeDlgt=n&&(n.onInvoke?t:t._invokeDlgt),this._invokeCurrZone=n&&(n.onInvoke?this.zone:t._invokeCurrZone),this._handleErrorZS=n&&(n.onHandleError?n:t._handleErrorZS),this._handleErrorDlgt=n&&(n.onHandleError?t:t._handleErrorDlgt),this._handleErrorCurrZone=n&&(n.onHandleError?this.zone:t._handleErrorCurrZone),this._scheduleTaskZS=n&&(n.onScheduleTask?n:t._scheduleTaskZS),this._scheduleTaskDlgt=n&&(n.onScheduleTask?t:t._scheduleTaskDlgt),this._scheduleTaskCurrZone=n&&(n.onScheduleTask?this.zone:t._scheduleTaskCurrZone),this._invokeTaskZS=n&&(n.onInvokeTask?n:t._invokeTaskZS),this._invokeTaskDlgt=n&&(n.onInvokeTask?t:t._invokeTaskDlgt),this._invokeTaskCurrZone=n&&(n.onInvokeTask?this.zone:t._invokeTaskCurrZone),this._cancelTaskZS=n&&(n.onCancelTask?n:t._cancelTaskZS),this._cancelTaskDlgt=n&&(n.onCancelTask?t:t._cancelTaskDlgt),this._cancelTaskCurrZone=n&&(n.onCancelTask?this.zone:t._cancelTaskCurrZone),this._hasTaskZS=null,this._hasTaskDlgt=null,this._hasTaskDlgtOwner=null,this._hasTaskCurrZone=null;const o=n&&n.onHasTask;(o||t&&t._hasTaskZS)&&(this._hasTaskZS=o?n:c,this._hasTaskDlgt=t,this._hasTaskDlgtOwner=this,this._hasTaskCurrZone=e,n.onScheduleTask||(this._scheduleTaskZS=c,this._scheduleTaskDlgt=t,this._scheduleTaskCurrZone=this.zone),n.onInvokeTask||(this._invokeTaskZS=c,this._invokeTaskDlgt=t,this._invokeTaskCurrZone=this.zone),n.onCancelTask||(this._cancelTaskZS=c,this._cancelTaskDlgt=t,this._cancelTaskCurrZone=this.zone))}fork(e,t){return this._forkZS?this._forkZS.onFork(this._forkDlgt,this.zone,e,t):new i(e,t)}intercept(e,t,n){return this._interceptZS?this._interceptZS.onIntercept(this._interceptDlgt,this._interceptCurrZone,e,t,n):t}invoke(e,t,n,o,r){return this._invokeZS?this._invokeZS.onInvoke(this._invokeDlgt,this._invokeCurrZone,e,t,n,o,r):t.apply(n,o)}handleError(e,t){return!this._handleErrorZS||this._handleErrorZS.onHandleError(this._handleErrorDlgt,this._handleErrorCurrZone,e,t)}scheduleTask(e,t){let n=t;if(this._scheduleTaskZS)this._hasTaskZS&&n._zoneDelegates.push(this._hasTaskDlgtOwner),n=this._scheduleTaskZS.onScheduleTask(this._scheduleTaskDlgt,this._scheduleTaskCurrZone,e,t),n||(n=t);else if(t.scheduleFn)t.scheduleFn(t);else{if(t.type!=S)throw new Error("Task is missing scheduleFn.");k(t)}return n}invokeTask(e,t,n,o){return this._invokeTaskZS?this._invokeTaskZS.onInvokeTask(this._invokeTaskDlgt,this._invokeTaskCurrZone,e,t,n,o):t.callback.apply(n,o)}cancelTask(e,t){let n;if(this._cancelTaskZS)n=this._cancelTaskZS.onCancelTask(this._cancelTaskDlgt,this._cancelTaskCurrZone,e,t);else{if(!t.cancelFn)throw Error("Task is not cancelable");n=t.cancelFn(t)}return n}hasTask(e,t){try{this._hasTaskZS&&this._hasTaskZS.onHasTask(this._hasTaskDlgt,this._hasTaskCurrZone,e,t)}catch(n){this.handleError(e,n)}}_updateTaskCount(e,t){const n=this._taskCounts,o=n[e],r=n[e]=o+t;if(r<0)throw new Error("More tasks executed then were scheduled.");0!=o&&0!=r||this.hasTask(this.zone,{microTask:n.microTask>0,macroTask:n.macroTask>0,eventTask:n.eventTask>0,change:e})}}class u{constructor(t,n,o,r,s,a){if(this._zone=null,this.runCount=0,this._zoneDelegates=null,this._state="notScheduled",this.type=t,this.source=n,this.data=r,this.scheduleFn=s,this.cancelFn=a,!o)throw new Error("callback is not defined");this.callback=o;const i=this;this.invoke=t===P&&r&&r.useG?u.invokeTask:function(){return u.invokeTask.call(e,i,this,arguments)}}static invokeTask(e,t,n){e||(e=this),I++;try{return e.runCount++,e.zone.runTask(e,t,n)}finally{1==I&&m(),I--}}get zone(){return this._zone}get state(){return this._state}cancelScheduleRequest(){this._transitionTo(v,b)}_transitionTo(e,t,n){if(this._state!==t&&this._state!==n)throw new Error(`${this.type} '${this.source}': can not transition to '${e}', expecting state '${t}'${n?" or '"+n+"'":""}, was '${this._state}'.`);this._state=e,e==v&&(this._zoneDelegates=null)}toString(){return this.data&&void 0!==this.data.handleId?this.data.handleId.toString():Object.prototype.toString.call(this)}toJSON(){return{type:this.type,state:this.state,source:this.source,zone:this.zone.name,runCount:this.runCount}}}const h=s("setTimeout"),p=s("Promise"),f=s("then");let d,g=[],_=!1;function k(t){if(0===I&&0===g.length)if(d||e[p]&&(d=e[p].resolve(0)),d){let e=d[f];e||(e=d.then),e.call(d,m)}else e[h](m,0);t&&g.push(t)}function m(){if(!_){for(_=!0;g.length;){const t=g;g=[];for(let n=0;nz,onUnhandledError:N,microtaskDrainDone:N,scheduleMicroTask:k,showUncaughtError:()=>!i[s("ignoreConsoleErrorUncaughtError")],patchEventTarget:()=>[],patchOnProperties:N,patchMethod:()=>N,bindArguments:()=>[],patchThen:()=>N,patchMacroTask:()=>N,setNativePromise:e=>{e&&"function"==typeof e.resolve&&(d=e.resolve(0))},patchEventPrototype:()=>N,isIEOrEdge:()=>!1,getGlobalObjects:()=>{},ObjectDefineProperty:()=>N,ObjectGetOwnPropertyDescriptor:()=>{},ObjectCreate:()=>{},ArraySlice:()=>[],patchClass:()=>N,wrapWithCurrentZone:()=>N,filterProperties:()=>[],attachOriginToPatched:()=>N,_redefineProperty:()=>N,patchCallbacks:()=>N};let z={parent:null,zone:new i(null,null)},j=null,I=0;function N(){}o("Zone","Zone"),e.Zone=i}("undefined"!=typeof window&&window||"undefined"!=typeof self&&self||global),Zone.__load_patch("ZoneAwarePromise",(e,t,n)=>{const o=Object.getOwnPropertyDescriptor,r=Object.defineProperty,s=n.symbol,a=[],i=!0===e[s("DISABLE_WRAPPING_UNCAUGHT_PROMISE_REJECTION")],c=s("Promise"),l=s("then");n.onUnhandledError=e=>{if(n.showUncaughtError()){const t=e&&e.rejection;t?console.error("Unhandled Promise rejection:",t instanceof Error?t.message:t,"; Zone:",e.zone.name,"; Task:",e.task&&e.task.source,"; Value:",t,t instanceof Error?t.stack:void 0):console.error(e)}},n.microtaskDrainDone=()=>{for(;a.length;){const t=a.shift();try{t.zone.runGuarded(()=>{throw t})}catch(e){h(e)}}};const u=s("unhandledPromiseRejectionHandler");function h(e){n.onUnhandledError(e);try{const n=t[u];"function"==typeof n&&n.call(this,e)}catch(o){}}function p(e){return e&&e.then}function f(e){return e}function d(e){return D.reject(e)}const g=s("state"),_=s("value"),k=s("finally"),m=s("parentPromiseValue"),y=s("parentPromiseState");function v(e,t){return n=>{try{T(e,t,n)}catch(o){T(e,!1,o)}}}const b=s("currentTaskTrace");function T(e,o,s){const c=function(){let e=!1;return function(t){return function(){e||(e=!0,t.apply(null,arguments))}}}();if(e===s)throw new TypeError("Promise resolved with itself");if(null===e[g]){let h=null;try{"object"!=typeof s&&"function"!=typeof s||(h=s&&s.then)}catch(u){return c(()=>{T(e,!1,u)})(),e}if(!1!==o&&s instanceof D&&s.hasOwnProperty(g)&&s.hasOwnProperty(_)&&null!==s[g])w(s),T(e,s[g],s[_]);else if(!1!==o&&"function"==typeof h)try{h.call(s,c(v(e,o)),c(v(e,!1)))}catch(u){c(()=>{T(e,!1,u)})()}else{e[g]=o;const c=e[_];if(e[_]=s,e[k]===k&&!0===o&&(e[g]=e[y],e[_]=e[m]),!1===o&&s instanceof Error){const e=t.currentTask&&t.currentTask.data&&t.currentTask.data.__creationTrace__;e&&r(s,b,{configurable:!0,enumerable:!1,writable:!0,value:e})}for(let t=0;t{try{const o=e[_],r=!!n&&k===n[k];r&&(n[m]=o,n[y]=s);const i=t.run(a,void 0,r&&a!==d&&a!==f?[]:[o]);T(n,!0,i)}catch(o){T(n,!1,o)}},n)}const S=function(){};class D{static toString(){return"function ZoneAwarePromise() { [native code] }"}static resolve(e){return T(new this(null),!0,e)}static reject(e){return T(new this(null),!1,e)}static race(e){let t,n,o=new this((e,o)=>{t=e,n=o});function r(e){t(e)}function s(e){n(e)}for(let a of e)p(a)||(a=this.resolve(a)),a.then(r,s);return o}static all(e){return D.allWithCallback(e)}static allSettled(e){return(this&&this.prototype instanceof D?this:D).allWithCallback(e,{thenCallback:e=>({status:"fulfilled",value:e}),errorCallback:e=>({status:"rejected",reason:e})})}static allWithCallback(e,t){let n,o,r=new this((e,t)=>{n=e,o=t}),s=2,a=0;const i=[];for(let l of e){p(l)||(l=this.resolve(l));const e=a;try{l.then(o=>{i[e]=t?t.thenCallback(o):o,s--,0===s&&n(i)},r=>{t?(i[e]=t.errorCallback(r),s--,0===s&&n(i)):o(r)})}catch(c){o(c)}s++,a++}return s-=2,0===s&&n(i),r}constructor(e){const t=this;if(!(t instanceof D))throw new Error("Must be an instanceof Promise.");t[g]=null,t[_]=[];try{e&&e(v(t,!0),v(t,!1))}catch(n){T(t,!1,n)}}get[Symbol.toStringTag](){return"Promise"}get[Symbol.species](){return D}then(e,n){let o=this.constructor[Symbol.species];o&&"function"==typeof o||(o=this.constructor||D);const r=new o(S),s=t.current;return null==this[g]?this[_].push(s,r,e,n):Z(this,s,r,e,n),r}catch(e){return this.then(null,e)}finally(e){let n=this.constructor[Symbol.species];n&&"function"==typeof n||(n=D);const o=new n(S);o[k]=k;const r=t.current;return null==this[g]?this[_].push(r,o,e,e):Z(this,r,o,e,e),o}}D.resolve=D.resolve,D.reject=D.reject,D.race=D.race,D.all=D.all;const P=e[c]=e.Promise,C=t.__symbol__("ZoneAwarePromise");let O=o(e,"Promise");O&&!O.configurable||(O&&delete O.writable,O&&delete O.value,O||(O={configurable:!0,enumerable:!0}),O.get=function(){return e[C]?e[C]:e[c]},O.set=function(t){t===D?e[C]=t:(e[c]=t,t.prototype[l]||j(t),n.setNativePromise(t))},r(e,"Promise",O)),e.Promise=D;const z=s("thenPatched");function j(e){const t=e.prototype,n=o(t,"then");if(n&&(!1===n.writable||!n.configurable))return;const r=t.then;t[l]=r,e.prototype.then=function(e,t){return new D((e,t)=>{r.call(this,e,t)}).then(e,t)},e[z]=!0}if(n.patchThen=j,P){j(P);const t=e.fetch;"function"==typeof t&&(e[n.symbol("fetch")]=t,e.fetch=(I=t,function(){let e=I.apply(this,arguments);if(e instanceof D)return e;let t=e.constructor;return t[z]||j(t),e}))}var I;return Promise[t.__symbol__("uncaughtPromiseErrors")]=a,D});const e=Object.getOwnPropertyDescriptor,t=Object.defineProperty,n=Object.getPrototypeOf,o=Object.create,r=Array.prototype.slice,s=Zone.__symbol__("addEventListener"),a=Zone.__symbol__("removeEventListener"),i=Zone.__symbol__("");function c(e,t){return Zone.current.wrap(e,t)}function l(e,t,n,o,r){return Zone.current.scheduleMacroTask(e,t,n,o,r)}const u=Zone.__symbol__,h="undefined"!=typeof window,p=h?window:void 0,f=h&&p||"object"==typeof self&&self||global,d=[null];function g(e,t){for(let n=e.length-1;n>=0;n--)"function"==typeof e[n]&&(e[n]=c(e[n],t+"_"+n));return e}function _(e){return!e||!1!==e.writable&&!("function"==typeof e.get&&void 0===e.set)}const k="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope,m=!("nw"in f)&&void 0!==f.process&&"[object process]"==={}.toString.call(f.process),y=!m&&!k&&!(!h||!p.HTMLElement),v=void 0!==f.process&&"[object process]"==={}.toString.call(f.process)&&!k&&!(!h||!p.HTMLElement),b={},T=function(e){if(!(e=e||f.event))return;let t=b[e.type];t||(t=b[e.type]=u("ON_PROPERTY"+e.type));const n=this||e.target||f,o=n[t];let r;if(y&&n===p&&"error"===e.type){const t=e;r=o&&o.call(this,t.message,t.filename,t.lineno,t.colno,t.error),!0===r&&e.preventDefault()}else r=o&&o.apply(this,arguments),null==r||r||e.preventDefault();return r};function E(n,o,r){let s=e(n,o);if(!s&&r&&e(r,o)&&(s={enumerable:!0,configurable:!0}),!s||!s.configurable)return;const a=u("on"+o+"patched");if(n.hasOwnProperty(a)&&n[a])return;delete s.writable,delete s.value;const i=s.get,c=s.set,l=o.substr(2);let h=b[l];h||(h=b[l]=u("ON_PROPERTY"+l)),s.set=function(e){let t=this;t||n!==f||(t=f),t&&(t[h]&&t.removeEventListener(l,T),c&&c.apply(t,d),"function"==typeof e?(t[h]=e,t.addEventListener(l,T,!1)):t[h]=null)},s.get=function(){let e=this;if(e||n!==f||(e=f),!e)return null;const t=e[h];if(t)return t;if(i){let t=i&&i.call(this);if(t)return s.set.call(this,t),"function"==typeof e.removeAttribute&&e.removeAttribute(o),t}return null},t(n,o,s),n[a]=!0}function w(e,t,n){if(t)for(let o=0;ofunction(t,o){const s=n(t,o);return s.cbIdx>=0&&"function"==typeof o[s.cbIdx]?l(s.name,o[s.cbIdx],s,r):e.apply(t,o)})}function C(e,t){e[u("OriginalDelegate")]=t}let O=!1,z=!1;function j(){try{const e=p.navigator.userAgent;if(-1!==e.indexOf("MSIE ")||-1!==e.indexOf("Trident/"))return!0}catch(e){}return!1}function I(){if(O)return z;O=!0;try{const e=p.navigator.userAgent;-1===e.indexOf("MSIE ")&&-1===e.indexOf("Trident/")&&-1===e.indexOf("Edge/")||(z=!0)}catch(e){}return z}Zone.__load_patch("toString",e=>{const t=Function.prototype.toString,n=u("OriginalDelegate"),o=u("Promise"),r=u("Error"),s=function(){if("function"==typeof this){const s=this[n];if(s)return"function"==typeof s?t.call(s):Object.prototype.toString.call(s);if(this===Promise){const n=e[o];if(n)return t.call(n)}if(this===Error){const n=e[r];if(n)return t.call(n)}}return t.call(this)};s[n]=t,Function.prototype.toString=s;const a=Object.prototype.toString;Object.prototype.toString=function(){return this instanceof Promise?"[object Promise]":a.call(this)}});let N=!1;if("undefined"!=typeof window)try{const e=Object.defineProperty({},"passive",{get:function(){N=!0}});window.addEventListener("test",e,e),window.removeEventListener("test",e,e)}catch(ie){N=!1}const R={useG:!0},x={},L={},M=new RegExp("^"+i+"(\\w+)(true|false)$"),A=u("propagationStopped");function H(e,t){const n=(t?t(e):e)+"false",o=(t?t(e):e)+"true",r=i+n,s=i+o;x[e]={},x[e].false=r,x[e].true=s}function F(e,t,o){const r=o&&o.add||"addEventListener",s=o&&o.rm||"removeEventListener",a=o&&o.listeners||"eventListeners",c=o&&o.rmAll||"removeAllListeners",l=u(r),h="."+r+":",p=function(e,t,n){if(e.isRemoved)return;const o=e.callback;"object"==typeof o&&o.handleEvent&&(e.callback=e=>o.handleEvent(e),e.originalDelegate=o),e.invoke(e,t,[n]);const r=e.options;r&&"object"==typeof r&&r.once&&t[s].call(t,n.type,e.originalDelegate?e.originalDelegate:e.callback,r)},f=function(t){if(!(t=t||e.event))return;const n=this||t.target||e,o=n[x[t.type].false];if(o)if(1===o.length)p(o[0],n,t);else{const e=o.slice();for(let o=0;ofunction(t,n){t[A]=!0,e&&e.apply(t,n)})}function q(e,t,n,o,r){const s=Zone.__symbol__(o);if(t[s])return;const a=t[s]=t[o];t[o]=function(s,i,c){return i&&i.prototype&&r.forEach((function(t){const r=`${n}.${o}::`+t,s=i.prototype;if(s.hasOwnProperty(t)){const n=e.ObjectGetOwnPropertyDescriptor(s,t);n&&n.value?(n.value=e.wrapWithCurrentZone(n.value,r),e._redefineProperty(i.prototype,t,n)):s[t]&&(s[t]=e.wrapWithCurrentZone(s[t],r))}else s[t]&&(s[t]=e.wrapWithCurrentZone(s[t],r))})),a.call(t,s,i,c)},e.attachOriginToPatched(t[o],a)}const W=["absolutedeviceorientation","afterinput","afterprint","appinstalled","beforeinstallprompt","beforeprint","beforeunload","devicelight","devicemotion","deviceorientation","deviceorientationabsolute","deviceproximity","hashchange","languagechange","message","mozbeforepaint","offline","online","paint","pageshow","pagehide","popstate","rejectionhandled","storage","unhandledrejection","unload","userproximity","vrdisplayconnected","vrdisplaydisconnected","vrdisplaypresentchange"],U=["encrypted","waitingforkey","msneedkey","mozinterruptbegin","mozinterruptend"],V=["load"],$=["blur","error","focus","load","resize","scroll","messageerror"],X=["bounce","finish","start"],J=["loadstart","progress","abort","error","load","progress","timeout","loadend","readystatechange"],Y=["upgradeneeded","complete","abort","success","error","blocked","versionchange","close"],K=["close","error","open","message"],Q=["error","message"],ee=["abort","animationcancel","animationend","animationiteration","auxclick","beforeinput","blur","cancel","canplay","canplaythrough","change","compositionstart","compositionupdate","compositionend","cuechange","click","close","contextmenu","curechange","dblclick","drag","dragend","dragenter","dragexit","dragleave","dragover","drop","durationchange","emptied","ended","error","focus","focusin","focusout","gotpointercapture","input","invalid","keydown","keypress","keyup","load","loadstart","loadeddata","loadedmetadata","lostpointercapture","mousedown","mouseenter","mouseleave","mousemove","mouseout","mouseover","mouseup","mousewheel","orientationchange","pause","play","playing","pointercancel","pointerdown","pointerenter","pointerleave","pointerlockchange","mozpointerlockchange","webkitpointerlockerchange","pointerlockerror","mozpointerlockerror","webkitpointerlockerror","pointermove","pointout","pointerover","pointerup","progress","ratechange","reset","resize","scroll","seeked","seeking","select","selectionchange","selectstart","show","sort","stalled","submit","suspend","timeupdate","volumechange","touchcancel","touchmove","touchstart","touchend","transitioncancel","transitionend","waiting","wheel"].concat(["webglcontextrestored","webglcontextlost","webglcontextcreationerror"],["autocomplete","autocompleteerror"],["toggle"],["afterscriptexecute","beforescriptexecute","DOMContentLoaded","freeze","fullscreenchange","mozfullscreenchange","webkitfullscreenchange","msfullscreenchange","fullscreenerror","mozfullscreenerror","webkitfullscreenerror","msfullscreenerror","readystatechange","visibilitychange","resume"],W,["beforecopy","beforecut","beforepaste","copy","cut","paste","dragstart","loadend","animationstart","search","transitionrun","transitionstart","webkitanimationend","webkitanimationiteration","webkitanimationstart","webkittransitionend"],["activate","afterupdate","ariarequest","beforeactivate","beforedeactivate","beforeeditfocus","beforeupdate","cellchange","controlselect","dataavailable","datasetchanged","datasetcomplete","errorupdate","filterchange","layoutcomplete","losecapture","move","moveend","movestart","propertychange","resizeend","resizestart","rowenter","rowexit","rowsdelete","rowsinserted","command","compassneedscalibration","deactivate","help","mscontentzoom","msmanipulationstatechanged","msgesturechange","msgesturedoubletap","msgestureend","msgesturehold","msgesturestart","msgesturetap","msgotpointercapture","msinertiastart","mslostpointercapture","mspointercancel","mspointerdown","mspointerenter","mspointerhover","mspointerleave","mspointermove","mspointerout","mspointerover","mspointerup","pointerout","mssitemodejumplistitemremoved","msthumbnailclick","stop","storagecommit"]);function te(e,t,n){if(!n||0===n.length)return t;const o=n.filter(t=>t.target===e);if(!o||0===o.length)return t;const r=o[0].ignoreProperties;return t.filter(e=>-1===r.indexOf(e))}function ne(e,t,n,o){e&&w(e,te(e,t,n),o)}function oe(e,t){if(m&&!v)return;if(Zone[e.symbol("patchEvents")])return;const o="undefined"!=typeof WebSocket,r=t.__Zone_ignore_on_properties;if(y){const e=window,t=j?[{target:e,ignoreProperties:["error"]}]:[];ne(e,ee.concat(["messageerror"]),r?r.concat(t):r,n(e)),ne(Document.prototype,ee,r),void 0!==e.SVGElement&&ne(e.SVGElement.prototype,ee,r),ne(Element.prototype,ee,r),ne(HTMLElement.prototype,ee,r),ne(HTMLMediaElement.prototype,U,r),ne(HTMLFrameSetElement.prototype,W.concat($),r),ne(HTMLBodyElement.prototype,W.concat($),r),ne(HTMLFrameElement.prototype,V,r),ne(HTMLIFrameElement.prototype,V,r);const o=e.HTMLMarqueeElement;o&&ne(o.prototype,X,r);const s=e.Worker;s&&ne(s.prototype,Q,r)}const s=t.XMLHttpRequest;s&&ne(s.prototype,J,r);const a=t.XMLHttpRequestEventTarget;a&&ne(a&&a.prototype,J,r),"undefined"!=typeof IDBIndex&&(ne(IDBIndex.prototype,Y,r),ne(IDBRequest.prototype,Y,r),ne(IDBOpenDBRequest.prototype,Y,r),ne(IDBDatabase.prototype,Y,r),ne(IDBTransaction.prototype,Y,r),ne(IDBCursor.prototype,Y,r)),o&&ne(WebSocket.prototype,K,r)}Zone.__load_patch("util",(n,s,a)=>{a.patchOnProperties=w,a.patchMethod=D,a.bindArguments=g,a.patchMacroTask=P;const l=s.__symbol__("BLACK_LISTED_EVENTS"),u=s.__symbol__("UNPATCHED_EVENTS");n[u]&&(n[l]=n[u]),n[l]&&(s[l]=s[u]=n[l]),a.patchEventPrototype=B,a.patchEventTarget=F,a.isIEOrEdge=I,a.ObjectDefineProperty=t,a.ObjectGetOwnPropertyDescriptor=e,a.ObjectCreate=o,a.ArraySlice=r,a.patchClass=S,a.wrapWithCurrentZone=c,a.filterProperties=te,a.attachOriginToPatched=C,a._redefineProperty=Object.defineProperty,a.patchCallbacks=q,a.getGlobalObjects=()=>({globalSources:L,zoneSymbolEventNames:x,eventNames:ee,isBrowser:y,isMix:v,isNode:m,TRUE_STR:"true",FALSE_STR:"false",ZONE_SYMBOL_PREFIX:i,ADD_EVENT_LISTENER_STR:"addEventListener",REMOVE_EVENT_LISTENER_STR:"removeEventListener"})});const re=u("zoneTask");function se(e,t,n,o){let r=null,s=null;n+=o;const a={};function i(t){const n=t.data;return n.args[0]=function(){try{t.invoke.apply(this,arguments)}finally{t.data&&t.data.isPeriodic||("number"==typeof n.handleId?delete a[n.handleId]:n.handleId&&(n.handleId[re]=null))}},n.handleId=r.apply(e,n.args),t}function c(e){return s(e.data.handleId)}r=D(e,t+=o,n=>function(r,s){if("function"==typeof s[0]){const e=l(t,s[0],{isPeriodic:"Interval"===o,delay:"Timeout"===o||"Interval"===o?s[1]||0:void 0,args:s},i,c);if(!e)return e;const n=e.data.handleId;return"number"==typeof n?a[n]=e:n&&(n[re]=e),n&&n.ref&&n.unref&&"function"==typeof n.ref&&"function"==typeof n.unref&&(e.ref=n.ref.bind(n),e.unref=n.unref.bind(n)),"number"==typeof n||n?n:e}return n.apply(e,s)}),s=D(e,n,t=>function(n,o){const r=o[0];let s;"number"==typeof r?s=a[r]:(s=r&&r[re],s||(s=r)),s&&"string"==typeof s.type?"notScheduled"!==s.state&&(s.cancelFn&&s.data.isPeriodic||0===s.runCount)&&("number"==typeof r?delete a[r]:r&&(r[re]=null),s.zone.cancelTask(s)):t.apply(e,o)})}function ae(e,t){if(Zone[t.symbol("patchEventTarget")])return;const{eventNames:n,zoneSymbolEventNames:o,TRUE_STR:r,FALSE_STR:s,ZONE_SYMBOL_PREFIX:a}=t.getGlobalObjects();for(let c=0;c{const t=e[Zone.__symbol__("legacyPatch")];t&&t()}),Zone.__load_patch("timers",e=>{se(e,"set","clear","Timeout"),se(e,"set","clear","Interval"),se(e,"set","clear","Immediate")}),Zone.__load_patch("requestAnimationFrame",e=>{se(e,"request","cancel","AnimationFrame"),se(e,"mozRequest","mozCancel","AnimationFrame"),se(e,"webkitRequest","webkitCancel","AnimationFrame")}),Zone.__load_patch("blocking",(e,t)=>{const n=["alert","prompt","confirm"];for(let o=0;ofunction(o,s){return t.current.run(n,e,s,r)})}),Zone.__load_patch("EventTarget",(e,t,n)=>{!function(e,t){t.patchEventPrototype(e,t)}(e,n),ae(e,n);const o=e.XMLHttpRequestEventTarget;o&&o.prototype&&n.patchEventTarget(e,[o.prototype]),S("MutationObserver"),S("WebKitMutationObserver"),S("IntersectionObserver"),S("FileReader")}),Zone.__load_patch("on_property",(e,t,n)=>{oe(n,e)}),Zone.__load_patch("customElements",(e,t,n)=>{!function(e,t){const{isBrowser:n,isMix:o}=t.getGlobalObjects();(n||o)&&e.customElements&&"customElements"in e&&t.patchCallbacks(t,e.customElements,"customElements","define",["connectedCallback","disconnectedCallback","adoptedCallback","attributeChangedCallback"])}(e,n)}),Zone.__load_patch("XHR",(e,t)=>{!function(e){const p=e.XMLHttpRequest;if(!p)return;const f=p.prototype;let d=f[s],g=f[a];if(!d){const t=e.XMLHttpRequestEventTarget;if(t){const e=t.prototype;d=e[s],g=e[a]}}function _(e){const o=e.data,c=o.target;c[i]=!1,c[h]=!1;const l=c[r];d||(d=c[s],g=c[a]),l&&g.call(c,"readystatechange",l);const u=c[r]=()=>{if(c.readyState===c.DONE)if(!o.aborted&&c[i]&&"scheduled"===e.state){const n=c[t.__symbol__("loadfalse")];if(n&&n.length>0){const r=e.invoke;e.invoke=function(){const n=c[t.__symbol__("loadfalse")];for(let t=0;tfunction(e,t){return e[o]=0==t[2],e[c]=t[1],y.apply(e,t)}),v=u("fetchTaskAborting"),b=u("fetchTaskScheduling"),T=D(f,"send",()=>function(e,n){if(!0===t.current[b])return T.apply(e,n);if(e[o])return T.apply(e,n);{const t={target:e,url:e[c],isPeriodic:!1,args:n,aborted:!1},o=l("XMLHttpRequest.send",k,t,_,m);e&&!0===e[h]&&!t.aborted&&"scheduled"===o.state&&o.invoke()}}),E=D(f,"abort",()=>function(e,o){const r=e[n];if(r&&"string"==typeof r.type){if(null==r.cancelFn||r.data&&r.data.aborted)return;r.zone.cancelTask(r)}else if(!0===t.current[v])return E.apply(e,o)})}(e);const n=u("xhrTask"),o=u("xhrSync"),r=u("xhrListener"),i=u("xhrScheduled"),c=u("xhrURL"),h=u("xhrErrorBeforeScheduled")}),Zone.__load_patch("geolocation",t=>{t.navigator&&t.navigator.geolocation&&function(t,n){const o=t.constructor.name;for(let r=0;r{const t=function(){return e.apply(this,g(arguments,o+"."+s))};return C(t,e),t})(a)}}}(t.navigator.geolocation,["getCurrentPosition","watchPosition"])}),Zone.__load_patch("PromiseRejectionEvent",(e,t)=>{function n(t){return function(n){G(e,t).forEach(o=>{const r=e.PromiseRejectionEvent;if(r){const e=new r(t,{promise:n.promise,reason:n.rejection});o.invoke(e)}})}}e.PromiseRejectionEvent&&(t[u("unhandledPromiseRejectionHandler")]=n("unhandledrejection"),t[u("rejectionHandledHandler")]=n("rejectionhandled"))})})?o.call(t,n,t,e):o)||(e.exports=r)}},[[2,0]]]);
--------------------------------------------------------------------------------
/docs/runtime-es2015.1eba213af0b233498d9d.js:
--------------------------------------------------------------------------------
1 | !function(e){function r(r){for(var n,l,f=r[0],i=r[1],p=r[2],c=0,s=[];c (https://molily.de)",
18 | "dependencies": {
19 | "@angular/animations": "^15.1.2",
20 | "@angular/common": "^15.1.2",
21 | "@angular/compiler": "^15.1.2",
22 | "@angular/core": "^15.1.2",
23 | "@angular/forms": "^15.1.2",
24 | "@angular/platform-browser": "^15.1.2",
25 | "@angular/platform-browser-dynamic": "^15.1.2",
26 | "@angular/router": "^15.1.2",
27 | "@ngrx/effects": "^15.2.1",
28 | "@ngrx/store": "^15.2.1",
29 | "@ngrx/store-devtools": "^15.2.1",
30 | "rxjs": "~7.8.0",
31 | "tslib": "^2.5.0",
32 | "zone.js": "~0.12.0"
33 | },
34 | "devDependencies": {
35 | "@angular-devkit/build-angular": "^15.1.3",
36 | "@angular-eslint/builder": "^15.2.0",
37 | "@angular-eslint/eslint-plugin": "^15.2.0",
38 | "@angular-eslint/eslint-plugin-template": "^15.2.0",
39 | "@angular-eslint/schematics": "^15.2.0",
40 | "@angular-eslint/template-parser": "^15.2.0",
41 | "@angular/cli": "~15.1.3",
42 | "@angular/compiler-cli": "^15.1.2",
43 | "@cypress/schematic": "^2.5.0",
44 | "@ngneat/spectator": "^14.0.0",
45 | "@types/jasmine": "~4.3.1",
46 | "@typescript-eslint/eslint-plugin": "^5.49.0",
47 | "@typescript-eslint/parser": "^5.49.0",
48 | "angular-cli-ghpages": "^1.0.5",
49 | "cypress": "^12.4.1",
50 | "eslint": "^8.32.0",
51 | "jasmine-core": "~4.5.0",
52 | "jasmine-spec-reporter": "~7.0.0",
53 | "karma": "~6.4.1",
54 | "karma-chrome-launcher": "~3.1.1",
55 | "karma-coverage": "~2.2.0",
56 | "karma-firefox-launcher": "^2.1.2",
57 | "karma-jasmine": "~5.1.0",
58 | "karma-jasmine-html-reporter": "^2.0.0",
59 | "ng-mocks": "^14.6.0",
60 | "typescript": "~4.9.4"
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/app/actions/counter.actions.ts:
--------------------------------------------------------------------------------
1 | import { createAction, props } from '@ngrx/store';
2 |
3 | import { CounterState } from '../reducers/counter.reducer';
4 |
5 | export const increment = createAction('[counter] Increment');
6 | export const decrement = createAction('[counter] Decrement');
7 | export const reset = createAction('[counter] Reset', props<{ count: CounterState }>());
8 |
9 | export const saveSuccess = createAction('[counter] Save success');
10 | export const saveError = createAction('[counter] Save error', props<{ error: Error }>());
11 |
12 | export type CounterActions = ReturnType<
13 | | typeof increment
14 | | typeof decrement
15 | | typeof reset
16 | | typeof saveSuccess
17 | | typeof saveError
18 | >;
19 |
--------------------------------------------------------------------------------
/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { NO_ERRORS_SCHEMA } from '@angular/core';
2 | import { ComponentFixture, TestBed } from '@angular/core/testing';
3 |
4 | import { AppComponent } from './app.component';
5 | import { findComponent } from './spec-helpers/element.spec-helper';
6 |
7 | describe('AppComponent', () => {
8 | let fixture: ComponentFixture;
9 | let component: AppComponent;
10 |
11 | beforeEach(async () => {
12 | await TestBed.configureTestingModule({
13 | declarations: [AppComponent],
14 | schemas: [NO_ERRORS_SCHEMA],
15 | }).compileComponents();
16 | });
17 |
18 | beforeEach(() => {
19 | fixture = TestBed.createComponent(AppComponent);
20 | component = fixture.componentInstance;
21 | fixture.detectChanges();
22 | });
23 |
24 | it('contains a router outlet', () => {
25 | const el = findComponent(fixture, 'router-outlet');
26 | expect(el).toBeTruthy();
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/src/app/app.component.spectator.spec.ts:
--------------------------------------------------------------------------------
1 | import { createComponentFactory, Spectator } from '@ngneat/spectator';
2 |
3 | import { AppComponent } from './app.component';
4 |
5 | describe('AppComponent with spectator', () => {
6 | let spectator: Spectator;
7 |
8 | const createComponent = createComponentFactory({
9 | component: AppComponent,
10 | shallow: true,
11 | });
12 |
13 | beforeEach(() => {
14 | spectator = createComponent();
15 | });
16 |
17 | it('contains a router outlet', () => {
18 | const el = spectator.query('router-outlet');
19 | expect(el).toBeTruthy();
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-root',
5 | templateUrl: './app.component.html',
6 | })
7 | export class AppComponent {}
8 |
--------------------------------------------------------------------------------
/src/app/app.module.spec.ts:
--------------------------------------------------------------------------------
1 | import { APP_BASE_HREF } from '@angular/common';
2 | import { TestBed } from '@angular/core/testing';
3 |
4 | import { AppModule } from './app.module';
5 |
6 | describe('AppModule', () => {
7 | beforeEach(() => {
8 | TestBed.configureTestingModule({
9 | imports: [AppModule],
10 | providers: [{ provide: APP_BASE_HREF, useValue: '/' }],
11 | });
12 | });
13 |
14 | it('initializes', () => {
15 | const appModule = TestBed.inject(AppModule);
16 | expect(appModule).toBeTruthy();
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { HttpClientModule } from '@angular/common/http';
2 | import { NgModule } from '@angular/core';
3 | import { BrowserModule } from '@angular/platform-browser';
4 |
5 | import { environment } from 'src/environments/environment';
6 |
7 | import { EffectsModule } from '@ngrx/effects';
8 | import { StoreModule } from '@ngrx/store';
9 | import { StoreDevtoolsModule } from '@ngrx/store-devtools';
10 |
11 | import { AppComponent } from './app.component';
12 | import { AppRoutingModule } from './app.routing.module';
13 | import { CounterComponent } from './components/counter/counter.component';
14 | import { HomeComponent } from './components/home/home.component';
15 | import {
16 | NgRxCounterComponent,
17 | } from './components/ngrx-counter/ngrx-counter.component';
18 | import {
19 | ServiceCounterComponent,
20 | } from './components/service-counter/service-counter.component';
21 | import {
22 | StandaloneServiceCounterComponent,
23 | } from './components/standalone-service-counter/standalone-service-counter.component';
24 | import { CounterEffects } from './effects/counter.effects';
25 | import { reducers } from './reducers';
26 | import { CounterApiService } from './services/counter-api.service';
27 | import { CounterService } from './services/counter.service';
28 |
29 | @NgModule({
30 | declarations: [
31 | AppComponent,
32 | HomeComponent,
33 | CounterComponent,
34 | ServiceCounterComponent,
35 | NgRxCounterComponent,
36 | ],
37 | imports: [
38 | BrowserModule,
39 | HttpClientModule,
40 |
41 | AppRoutingModule,
42 | StandaloneServiceCounterComponent,
43 |
44 | // NgRx Store
45 | StoreModule.forRoot(reducers, {
46 | runtimeChecks: {
47 | strictStateImmutability: true,
48 | strictActionImmutability: true,
49 | },
50 | }),
51 |
52 | // NgRx Effects
53 | EffectsModule.forRoot([CounterEffects]),
54 |
55 | // NgRx Store Dev Tools
56 | StoreDevtoolsModule.instrument({
57 | maxAge: 25, // Retains last n states
58 | logOnly: environment.production,
59 | }),
60 | ],
61 | providers: [CounterService, CounterApiService],
62 | bootstrap: [AppComponent],
63 | })
64 | export class AppModule {}
65 |
--------------------------------------------------------------------------------
/src/app/app.routing.module.spec.ts:
--------------------------------------------------------------------------------
1 | import {
2 | APP_BASE_HREF,
3 | Location,
4 | } from '@angular/common';
5 | import { Type } from '@angular/core';
6 | import {
7 | ComponentFixture,
8 | TestBed,
9 | } from '@angular/core/testing';
10 | import { By } from '@angular/platform-browser';
11 | import { Router } from '@angular/router';
12 | import { RouterTestingModule } from '@angular/router/testing';
13 |
14 | import { provideMockStore } from '@ngrx/store/testing';
15 |
16 | import { AppComponent } from './app.component';
17 | import {
18 | AppRoutingModule,
19 | routes,
20 | } from './app.routing.module';
21 | import { CounterComponent } from './components/counter/counter.component';
22 | import { HomeComponent } from './components/home/home.component';
23 | import {
24 | NgRxCounterComponent,
25 | } from './components/ngrx-counter/ngrx-counter.component';
26 | import {
27 | ServiceCounterComponent,
28 | } from './components/service-counter/service-counter.component';
29 | import { CounterService } from './services/counter.service';
30 |
31 | describe('AppRoutingModule', () => {
32 | beforeEach(() => {
33 | TestBed.configureTestingModule({
34 | imports: [AppRoutingModule],
35 | providers: [{ provide: APP_BASE_HREF, useValue: '/' }],
36 | });
37 | });
38 |
39 | it('initializes', () => {
40 | const appRoutingModule = TestBed.inject(AppRoutingModule);
41 | expect(appRoutingModule).toBeTruthy();
42 | });
43 | });
44 |
45 | describe('AppRoutingModule: routes', () => {
46 | let fixture: ComponentFixture;
47 | let router: Router;
48 | let location: Location;
49 |
50 | function expectComponent(component: Type): void {
51 | expect(fixture.debugElement.query(By.directive(component))).toBeTruthy();
52 | }
53 |
54 | beforeEach(async () => {
55 | await TestBed.configureTestingModule({
56 | imports: [RouterTestingModule.withRoutes(routes)],
57 | declarations: [AppComponent],
58 | providers: [CounterService, provideMockStore({ initialState: { counter: 5 } })],
59 | }).compileComponents();
60 | });
61 |
62 | beforeEach(() => {
63 | router = TestBed.inject(Router);
64 | location = TestBed.inject(Location);
65 |
66 | fixture = TestBed.createComponent(AppComponent);
67 |
68 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
69 | fixture.ngZone!.run(() => {
70 | router.initialNavigation();
71 | });
72 | });
73 |
74 | it('routes / to the HomeComponent', async () => {
75 | await router.navigate(['']);
76 | fixture.detectChanges();
77 | expectComponent(HomeComponent);
78 | });
79 |
80 | it('routes /counter-component', async () => {
81 | await router.navigate(['counter-component']);
82 | fixture.detectChanges();
83 | expect(location.path()).toBe('/counter-component');
84 | expectComponent(CounterComponent);
85 | });
86 |
87 | it('routes /service-counter-component', async () => {
88 | await router.navigate(['service-counter-component']);
89 | fixture.detectChanges();
90 | expect(location.path()).toBe('/service-counter-component');
91 | expectComponent(ServiceCounterComponent);
92 | });
93 |
94 | it('routes /ngrx-counter-component', async () => {
95 | await router.navigate(['ngrx-counter-component']);
96 | fixture.detectChanges();
97 | expect(location.path()).toBe('/ngrx-counter-component');
98 | expectComponent(NgRxCounterComponent);
99 | });
100 | });
101 |
--------------------------------------------------------------------------------
/src/app/app.routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import {
3 | RouterModule,
4 | Routes,
5 | } from '@angular/router';
6 |
7 | import { CounterComponent } from './components/counter/counter.component';
8 | import { HomeComponent } from './components/home/home.component';
9 | import {
10 | NgRxCounterComponent,
11 | } from './components/ngrx-counter/ngrx-counter.component';
12 | import {
13 | ServiceCounterComponent,
14 | } from './components/service-counter/service-counter.component';
15 | import {
16 | StandaloneServiceCounterComponent,
17 | } from './components/standalone-service-counter/standalone-service-counter.component';
18 |
19 | export const routes: Routes = [
20 | { path: '', pathMatch: 'full', component: HomeComponent },
21 | { path: 'counter-component', component: CounterComponent },
22 | { path: 'service-counter-component', component: ServiceCounterComponent },
23 | { path: 'standalone-service-counter-component', component: StandaloneServiceCounterComponent },
24 | { path: 'ngrx-counter-component', component: NgRxCounterComponent },
25 | ];
26 |
27 | @NgModule({
28 | imports: [RouterModule.forRoot(routes)],
29 | exports: [RouterModule],
30 | })
31 | export class AppRoutingModule {}
32 |
--------------------------------------------------------------------------------
/src/app/components/counter/counter.component.css:
--------------------------------------------------------------------------------
1 | :host {
2 | display: block;
3 | margin-bottom: 1rem;
4 | border: 4px solid navy;
5 | border-radius: 2px;
6 | padding: 0 1rem;
7 | }
8 |
--------------------------------------------------------------------------------
/src/app/components/counter/counter.component.html:
--------------------------------------------------------------------------------
1 |