├── .npmignore ├── .travis.yml ├── .gitignore ├── spec ├── setup.js └── mocha.opts ├── typings.json ├── tsconfig.dist.es2015.json ├── CHANGELOG.md ├── docs └── viewengine.md ├── tsconfig.json ├── package.json ├── tslint.json ├── src ├── index.ts └── test.ts ├── README.md └── LICENSE /.npmignore: -------------------------------------------------------------------------------- 1 | .idea 2 | dev 3 | src 4 | typings 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /dist/ 3 | /typings/ 4 | /node_modules/ 5 | /.idea/ 6 | -------------------------------------------------------------------------------- /spec/setup.js: -------------------------------------------------------------------------------- 1 | // const chai = require("chai"); 2 | // global.assert = chai.assert; 3 | -------------------------------------------------------------------------------- /typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "globalDependencies": { 3 | "es6-shim": "registry:dt/es6-shim#0.31.2+20160602141504" 4 | }, 5 | "dependencies": {} 6 | } 7 | -------------------------------------------------------------------------------- /tsconfig.dist.es2015.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "es2015", 5 | "outDir": "dist/es2015", 6 | "rootDirs": ["src"] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /spec/mocha.opts: -------------------------------------------------------------------------------- 1 | --require source-map-support/register 2 | --require ts-node/register 3 | --require spec/setup.js 4 | 5 | --ui bdd 6 | --reporter spec 7 | 8 | --check-leaks 9 | 10 | --recursive 11 | --timeout 1000 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # Version 4.1.4 3 | 4 | - undeprecated untilComponentDestroyed 5 | 6 | 7 | # Version 4.1.3 8 | 9 | - deprecated untilComponentDestroyed 10 | 11 | 12 | # Version 4.0.0 13 | 14 | - This package was renamed to '@w11k/ngx-componentdestroyed' 15 | 16 | 17 | # Version 3.0.1 18 | 19 | - bundle ES2015 modules to enable tree-shaking 20 | 21 | 22 | # Version 3.0.0 23 | 24 | - Requires RxJS 6.0.0 25 | 26 | 27 | # Version 2.0.0 28 | 29 | - Requires RxJS 5.5.6 30 | - added `untilComponentDestroyed` method which can be used in the `.pipe()` operator 31 | -------------------------------------------------------------------------------- /docs/viewengine.md: -------------------------------------------------------------------------------- 1 | 2 | # Angular < 9 / ViewEngine 3 | 4 | If you are using ... 5 | - Angular 8 or older 6 | - ViewEngine (instead of Ivy in Angular >= 9) 7 | 8 | ... you have to use a previous version of this library. Please follow the instructions below. 9 | 10 | ## Installation 11 | 12 | ``` 13 | npm i --save @w11k/ngx-componentdestroyed@4.x.x 14 | ``` 15 | 16 | ``` 17 | @Component({ 18 | selector: 'foo', 19 | templateUrl: './foo.component.html' 20 | }) 21 | export class FooComponent implements OnInit, OnDestroy { 22 | 23 | ngOnInit() { 24 | interval(1000) 25 | .pipe( 26 | untilComponentDestroyed(this) // <--- 2. use the pipe operator 27 | ) 28 | .subscribe(); 29 | } 30 | 31 | ngOnDestroy() { // <--- 1. add empty stub 32 | } 33 | 34 | } 35 | ``` 36 | 37 | The TypeScript compiler will ensure that you implemented **1.** if you try to use **2.**. 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "allowJs": false, 5 | "declaration": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "module": "commonjs", 9 | "moduleResolution": "node", 10 | "noEmitOnError": false, 11 | "noImplicitAny": true, 12 | "strictNullChecks": true, 13 | "strictPropertyInitialization": true, 14 | "noImplicitThis": true, 15 | "noImplicitReturns": true, 16 | "strictFunctionTypes": true, 17 | "skipLibCheck": true, 18 | "strict": true, 19 | "target": "es5", 20 | "outDir": "dist", 21 | "lib": [ 22 | // "ES2015.Core", 23 | // "ES2015.Collection", 24 | // "ES2015.Reflect", 25 | // "ES2015.Generator", 26 | // "ES2015.Iterable", 27 | // "ES2015.Promise", 28 | "ES2015.Symbol", 29 | // "ES2015.Symbol.WellKnown", 30 | "ES2015", 31 | "DOM" 32 | ], 33 | "typeRoots": [ 34 | "node_modules/@types" 35 | ] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@w11k/ngx-componentdestroyed", 3 | "version": "5.0.2", 4 | "license": "Apache-2.0", 5 | "main": "./dist/index.js", 6 | "module": "./dist/es2015/index.js", 7 | "typings": "dist/index.d.ts", 8 | "repository": "https://github.com/w11k/ngx-componentdestroyed", 9 | "keywords": [ 10 | "Angular", 11 | "RxJS", 12 | "unsubscribe", 13 | "easy unsubscribe", 14 | "memory leak" 15 | ], 16 | "files": [ 17 | "dist", 18 | "LICENSE", 19 | "README.md" 20 | ], 21 | "dependencies": { 22 | "rxjs": ">=6.5.0" 23 | }, 24 | "devDependencies": { 25 | "@types/chai": "4.1.2", 26 | "@types/mocha": "2.2.48", 27 | "chai": "4.1.2", 28 | "del-cli": "1.1.0", 29 | "mocha": "5.0.0", 30 | "np": "6.2.0", 31 | "source-map-support": "0.5.3", 32 | "ts-node": "4.1.0", 33 | "tslint": "^6.0.0", 34 | "typescript": "^3.1.6", 35 | "watch": "1.0.2" 36 | }, 37 | "peerDependencies": { 38 | "rxjs": ">=6.0.0", 39 | "@angular/core": ">=9.x.x" 40 | }, 41 | "scripts": { 42 | "np": "np", 43 | "clean": "del-cli dist", 44 | "build": "npm run clean && npm run dist", 45 | "dist": "tsc -p tsconfig.json && tsc -p tsconfig.dist.es2015.json", 46 | "test": "mocha --opts spec/mocha.opts src/**/*test.ts" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "member-access": [ 4 | true, 5 | "no-public" 6 | ], 7 | "strict-boolean-expressions": true, 8 | "class-name": true, 9 | "curly": true, 10 | "eofline": false, 11 | "no-duplicate-super": false, 12 | "forin": true, 13 | "indent": [ 14 | true, 15 | 4 16 | ], 17 | "label-position": true, 18 | "max-line-length": [ 19 | false, 20 | 140 21 | ], 22 | "no-arg": true, 23 | "no-bitwise": true, 24 | "no-console": [ 25 | true, 26 | "debug", 27 | "info", 28 | "time", 29 | "timeEnd", 30 | "trace" 31 | ], 32 | "no-construct": true, 33 | "no-debugger": true, 34 | "no-duplicate-variable": true, 35 | "no-empty": false, 36 | "no-eval": true, 37 | "no-require-imports": true, 38 | "no-string-literal": false, 39 | "trailing-comma": true, 40 | "no-trailing-whitespace": false, 41 | "no-unused-variable": false, 42 | "one-line": [ 43 | true, 44 | "check-open-brace", 45 | "check-catch", 46 | "check-else", 47 | "check-whitespace" 48 | ], 49 | "quotemark": [ 50 | true, 51 | "double" 52 | ], 53 | "radix": true, 54 | "semicolon": true, 55 | "triple-equals": [ 56 | true, 57 | "allow-null-check" 58 | ], 59 | "variable-name": false, 60 | "whitespace": [ 61 | true, 62 | "check-branch", 63 | "check-decl", 64 | "check-operator", 65 | "check-separator" 66 | ] 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import {Observable, ReplaySubject, Subject} from "rxjs"; 2 | import {takeUntil} from "rxjs/operators"; 3 | 4 | /* 5 | function getInternalAngularComponent(type: any): any { 6 | noinspection JSNonASCIINames 7 | // return type.ɵdir || type.ɵcmp; 8 | } 9 | 10 | export function ObserveOnDestroy() { 11 | return (target: any) => { 12 | const componentDefinition = getInternalAngularComponent(target); 13 | if (componentDefinition) { 14 | const old = componentDefinition.onDestroy; 15 | componentDefinition.onDestroy = function (this: any) { 16 | const onDestroySubject = componentDestroyed(this) as Subject; 17 | onDestroySubject.next(); 18 | 19 | if (old !== undefined && old !== null) { 20 | old(); 21 | } 22 | }; 23 | } else { 24 | throw new Error("Ivy and AoT must be enabled for @ObserveOnDestroy()."); 25 | } 26 | 27 | function decorated(this: any) { 28 | const instance = Reflect.construct(target, arguments); 29 | instance[ON_DESTROY_SUBJECT_KEY] = new ReplaySubject(1); 30 | return instance; 31 | } 32 | 33 | Object.setPrototypeOf(decorated, target); 34 | return decorated as any; 35 | }; 36 | } 37 | */ 38 | 39 | const ON_DESTROY_SUBJECT_KEY = Symbol("ON_DESTROY_SUBJECT_KEY"); 40 | 41 | export type ComponentWithOnDestroyObservable = { 42 | observeOnDestroy(): Observable; 43 | }; 44 | 45 | export class OnDestroyMixin { 46 | 47 | constructor() { 48 | (this as any)[ON_DESTROY_SUBJECT_KEY] = new ReplaySubject(); 49 | } 50 | 51 | observeOnDestroy() { 52 | return (this as any)[ON_DESTROY_SUBJECT_KEY]; 53 | } 54 | 55 | ngOnDestroy() { 56 | (this.observeOnDestroy() as Subject).next(); 57 | } 58 | 59 | } 60 | 61 | export function componentDestroyed(target: ComponentWithOnDestroyObservable): Observable { 62 | const onDestroySubject = (target as any)[ON_DESTROY_SUBJECT_KEY]; 63 | if (onDestroySubject === undefined) { 64 | const proto = Object.getPrototypeOf(target); 65 | const compInfo = proto !== undefined && proto.constructor !== undefined !== proto.constructor.name !== undefined 66 | ? ` (component: ${proto.constructor.name})` 67 | : ""; 68 | 69 | throw new Error(`You are almost there! Please extends the base class 'OnDestroyMixin'${compInfo}.`); 70 | } 71 | 72 | return onDestroySubject; 73 | } 74 | 75 | export function untilComponentDestroyed(component: ComponentWithOnDestroyObservable): (source: Observable) => Observable { 76 | return (source: Observable) => source.pipe(takeUntil(componentDestroyed(component))); 77 | } 78 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | import {assert} from "chai"; 2 | import {of, Subject} from "rxjs"; 3 | import {switchMap, takeUntil} from "rxjs/operators"; 4 | import {componentDestroyed, OnDestroyMixin, untilComponentDestroyed} from "./index"; 5 | 6 | function FakeAngularIvyAot() { 7 | return (target: any) => { 8 | // noinspection JSNonASCIINames 9 | target.ɵcmp = target; 10 | }; 11 | } 12 | 13 | function destroyComponent(component: any) { 14 | // const constructor = Object.getPrototypeOf(component).constructor; 15 | // const onDestroy = constructor.onDestroy; 16 | // onDestroy.apply(component); 17 | component.ngOnDestroy(); 18 | } 19 | 20 | // @ObserveOnDestroy() 21 | // @FakeAngularIvyAot() 22 | class FakeComp extends OnDestroyMixin { 23 | } 24 | 25 | const NOOP = () => { 26 | }; 27 | 28 | describe("componentDestroyed", function () { 29 | 30 | it("emits a value when ngOnDestroy() gets called", function () { 31 | const fakeComp = new FakeComp(); 32 | let observable = componentDestroyed(fakeComp); 33 | let called = false; 34 | observable.subscribe(() => called = true); 35 | destroyComponent(fakeComp); 36 | assert.isTrue(called); 37 | }); 38 | 39 | it("gets reinitialized on component creation", function () { 40 | function testRun() { 41 | const fakeComp = new FakeComp(); 42 | let called = false; 43 | let closed = false; 44 | const source = new Subject(); 45 | source.pipe(takeUntil(componentDestroyed(fakeComp))).subscribe( 46 | () => called = true, 47 | NOOP, 48 | () => closed = true); 49 | source.next(1); 50 | destroyComponent(fakeComp); 51 | assert.isTrue(called); 52 | assert.isTrue(closed); 53 | } 54 | 55 | testRun(); 56 | testRun(); 57 | testRun(); 58 | }); 59 | 60 | it("each component instance has it's own destroyed observable", function () { 61 | const fakeComp1 = new FakeComp(); 62 | const fakeComp2 = new FakeComp(); 63 | 64 | let called1 = false; 65 | let called2 = false; 66 | let closed1 = false; 67 | let closed2 = false; 68 | const source1 = new Subject(); 69 | const source2 = new Subject(); 70 | 71 | source1.pipe(takeUntil(componentDestroyed(fakeComp1))).subscribe(() => called1 = true, NOOP, () => closed1 = true); 72 | source2.pipe(takeUntil(componentDestroyed(fakeComp2))).subscribe(() => called2 = true, NOOP, () => closed2 = true); 73 | 74 | assert.isFalse(called1); 75 | assert.isFalse(closed1); 76 | assert.isFalse(called2); 77 | assert.isFalse(closed2); 78 | source1.next(); 79 | assert.isTrue(called1); 80 | assert.isFalse(closed1); 81 | assert.isFalse(called2); 82 | assert.isFalse(closed2); 83 | destroyComponent(fakeComp1); 84 | assert.isTrue(called1); 85 | assert.isTrue(closed1); 86 | assert.isFalse(called2); 87 | assert.isFalse(closed2); 88 | source2.next(); 89 | assert.isTrue(called1); 90 | assert.isTrue(closed1); 91 | assert.isTrue(called2); 92 | assert.isFalse(closed2); 93 | destroyComponent(fakeComp2); 94 | assert.isTrue(called1); 95 | assert.isTrue(closed1); 96 | assert.isTrue(called2); 97 | assert.isTrue(closed2); 98 | }); 99 | 100 | it("can be used with the pipe and takeUntil operators", function () { 101 | const fakeComp = new FakeComp(); 102 | 103 | let closed = false; 104 | const source = new Subject(); 105 | source.pipe(takeUntil(componentDestroyed(fakeComp))) 106 | .subscribe(NOOP, NOOP, () => closed = true); 107 | 108 | destroyComponent(fakeComp); 109 | assert.isTrue(closed); 110 | }); 111 | 112 | 113 | }); 114 | 115 | describe("untilComponentDestroyed", function () { 116 | 117 | it("can be used as a pipe operator", function () { 118 | const fakeComp = new FakeComp(); 119 | 120 | let closed = false; 121 | const source = new Subject(); 122 | source.pipe(untilComponentDestroyed(fakeComp)) 123 | .subscribe(NOOP, NOOP, () => closed = true); 124 | 125 | destroyComponent(fakeComp); 126 | assert.isTrue(closed); 127 | }); 128 | 129 | it("can be used with other pipe operators", function () { 130 | const fakeComp = new FakeComp(); 131 | 132 | let closed = false; 133 | const vals: number[] = []; 134 | const source = new Subject(); 135 | source 136 | .pipe( 137 | switchMap/**/(val => of(val + 100)), 138 | untilComponentDestroyed(fakeComp), 139 | ) 140 | .subscribe(val => vals.push(val), NOOP, () => closed = true); 141 | 142 | source.next(1); 143 | source.next(2); 144 | source.next(3); 145 | destroyComponent(fakeComp); 146 | 147 | assert.deepEqual(vals, [101, 102, 103]); 148 | assert.isTrue(closed); 149 | }); 150 | 151 | }); 152 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Build Status](https://travis-ci.org/w11k/ngx-componentdestroyed.svg?branch=master)](https://travis-ci.org/w11k/ngx-componentdestroyed) 3 | [![npm version](https://badge.fury.io/js/%40w11k%2Fngx-componentdestroyed.svg)](https://badge.fury.io/js/%40w11k%2Fngx-componentdestroyed) 4 | 5 | # Unsubscribe from Observables in Angular 6 | 7 | This library provides utility methods which help to unsubscribe from ReactiveX's Observables in Angular applications. 8 | 9 | **If you already use this library and want to use it with Angular 9:** Please check the [Migration Guide](https://github.com/w11k/ngx-componentdestroyed#migration-guide-4xx---5xx). 10 | 11 | ## Why? 12 | 13 | Failing to unsubscribe from observables will lead to unwanted memory leaks as the observable stream is left open, potentially even after a component has been destroyed or the user has navigated to another page. 14 | 15 | *Important*: If services are used in [Hierarchical Dependency Injectors](https://angular.io/guide/hierarchical-dependency-injection#hierarchical-dependency-injectors) they are affected by the same memory-leak issue! 16 | 17 | This blog post provides additional information: 18 | 19 | https://medium.com/thecodecampus-knowledge/the-easiest-way-to-unsubscribe-from-observables-in-angular-5abde80a5ae3 20 | 21 | ## Patrons 22 | 23 | ❤️ [W11K - The Web Engineers](https://www.w11k.de/) 24 | 25 | ❤️ [theCodeCampus - Trainings for Angular and TypeScript](https://www.thecodecampus.de/) 26 | 27 | ## First: Check your Angular version! 28 | 29 | If you are using Angular <= 8 or Angular 9 with ViewEngine instead of Ivy, you have to use a previous version of this library. Please see [ViewEngine usage](https://github.com/w11k/ngx-componentdestroyed/blob/master/docs/viewengine.md) for further instructions. If you are using the latest Angular version and if you have no idea what ViewEngine or Ivy is, just continue with the instructions below. 30 | 31 | ## Demo 32 | 33 | ``` 34 | @Component({ 35 | selector: 'foo', 36 | templateUrl: './foo.component.html' 37 | }) 38 | export class FooComponent 39 | extends OnDestroyMixin // <--- 1. extend OnDestroyMixin 40 | implements OnInit { 41 | 42 | ngOnInit() { 43 | interval(1000) 44 | .pipe( 45 | untilComponentDestroyed(this) // <--- 2. use the pipe operator 46 | ) 47 | .subscribe(); 48 | } 49 | 50 | } 51 | ``` 52 | 53 | The TypeScript compiler will check that your component extends `OnDestroyMixin` when you try to use `untilComponentDestroyed`. 54 | 55 | ## Installation 56 | 57 | **Download the NPM package** 58 | 59 | ``` 60 | npm i --save @w11k/ngx-componentdestroyed 61 | ``` 62 | ## Usage 63 | 64 | ### Prepare the class 65 | 66 | Your component class must extend `OnDestroyMixin`: 67 | 68 | ``` 69 | import {OnDestroyMixin} from "@w11k/ngx-componentdestroyed"; 70 | 71 | @Component({ 72 | selector: 'foo', 73 | templateUrl: './foo.component.html' 74 | }) 75 | export class FooComponent extends OnDestroyMixin { // <--- HERE 76 | ... 77 | } 78 | ``` 79 | 80 | ### Use the pipe operator 81 | 82 | Either use 83 | 84 | - `untilComponentDestroyed(this)` 85 | - `takeUntil(componentDestroyed(this))` 86 | 87 | as the last Observable pipe operator. 88 | 89 | ``` 90 | import {interval} from "rxjs"; 91 | import {takeUntil} from "rxjs/operators"; 92 | import {untilComponentDestroyed} from "@w11k/ngx-componentdestroyed"; 93 | 94 | 95 | interval(1000) 96 | .pipe( 97 | untilComponentDestroyed(this) // <--- HERE 98 | ) 99 | .subscribe(); 100 | ``` 101 | 102 | ### Be careful with implementing `ngOnDestroy()` 103 | 104 | If the component implements `ngOnDestroy()`, it must call `super.ngOnDestroy()` within the method body. 105 | 106 | 107 | ## Migration guide 4.x.x -> 5.x.x 108 | 109 | 1. The component class has to extend `OnDestroyMixin` (import from `@w11k/ngx-componentdestroyed`). 110 | 2. If the component class has a constructor (very likely), you have to call `super()` at the beginning. The TypeScript compiler will complain if you don't. 111 | 3. You **must** either remove the existing `ngOnDestroy()` method (if empty, recommended) or call `super.ngOnDestroy()` within. 112 | 113 | ## TSLint rule 114 | 115 | Our sister project [@w11k/rx-ninja](https://github.com/w11k/rx-ninja) provides a TSLint rule to enforce the use a terminator operator. If you want to use `untilComponentDestroyed(this)` instead of `takeUntil(componentDestroyed(this))` please add this configuration to your tslint.json file: 116 | 117 | ``` 118 | { 119 | "rulesDirectory": [ 120 | "node_modules/@w11k/rx-ninja/dist/tslint_rules" 121 | ], 122 | "rules": { 123 | "rx-ninja-subscribe-takeuntil": [true, "takeUntil", "untilComponentDestroyed"] 124 | } 125 | } 126 | ``` 127 | 128 | ## A note on Ivy, ViewEngine, AoT on/off, Karma, Jest, ... 129 | 130 | We tried everything but the current state of Angular's Ivy compilation is a f@#!ing nightmare: 131 | 132 | - Base classes do not work with ViewEngine 133 | - Ivy doesn't work with patching at runtime (this library version <= 4) 134 | - Decorator tricks rely on Angular internals and **will** break in the future ... 135 | - ... they don't work with Karma or Jest 136 | - ... but even if the don't break, they don't work with AoT compilation turned off 137 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "{}" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright {yyyy} {name of copyright owner} 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | --------------------------------------------------------------------------------