master b162711f4e3e cached
70 files
70.5 KB
17.8k tokens
37 symbols
1 requests
Download .txt
Repository: ng-web-apis/intersection-observer
Branch: master
Commit: b162711f4e3e
Files: 70
Total size: 70.5 KB

Directory structure:
gitextract_w7p2l720/

├── .editorconfig
├── .eslintrc.js
├── .github/
│   ├── CODEOWNERS
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── .husky/
│   ├── commit-msg
│   └── pre-commit
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── angular.json
├── commitlint.config.js
├── package.json
├── prettier.config.js
├── projects/
│   ├── demo/
│   │   ├── .gitignore
│   │   ├── angular.json
│   │   ├── karma.conf.js
│   │   ├── package.json
│   │   ├── server.ts
│   │   ├── src/
│   │   │   ├── app/
│   │   │   │   ├── app.browser.module.ts
│   │   │   │   ├── app.component.html
│   │   │   │   ├── app.component.less
│   │   │   │   ├── app.component.ts
│   │   │   │   ├── app.routes.ts
│   │   │   │   └── app.server.module.ts
│   │   │   ├── assets/
│   │   │   │   ├── browserconfig.xml
│   │   │   │   └── site.webmanifest
│   │   │   ├── index.html
│   │   │   ├── main.browser.ts
│   │   │   ├── main.server.ts
│   │   │   ├── polyfills.ts
│   │   │   ├── styles.css
│   │   │   └── typings.d.ts
│   │   ├── tsconfig.demo.json
│   │   ├── tsconfig.json
│   │   ├── tsconfig.server.json
│   │   └── tsconfig.spec.json
│   └── intersection-observer/
│       ├── LICENSE
│       ├── karma.conf.js
│       ├── ng-package.json
│       ├── package.json
│       ├── src/
│       │   ├── directives/
│       │   │   ├── intersection-observee.directive.ts
│       │   │   ├── intersection-observer.directive.ts
│       │   │   ├── intersection-root.directive.ts
│       │   │   └── tests/
│       │   │       └── intersection-observee.spec.ts
│       │   ├── module.ts
│       │   ├── public-api.ts
│       │   ├── services/
│       │   │   ├── intersection-observee.service.ts
│       │   │   ├── intersection-observer.service.ts
│       │   │   └── tests/
│       │   │       └── intersection-observer.service.spec.ts
│       │   ├── test.ts
│       │   ├── tokens/
│       │   │   ├── intersection-root-margin.ts
│       │   │   ├── intersection-root.ts
│       │   │   ├── intersection-threshold.ts
│       │   │   ├── support.ts
│       │   │   └── tests/
│       │   │       └── support.spec.ts
│       │   └── utils/
│       │       ├── root-margin-factory.ts
│       │       └── threshold-factory.ts
│       ├── tsconfig.lib.json
│       └── tsconfig.spec.json
├── scripts/
│   ├── postbuild.js
│   └── syncVersions.js
├── tsconfig.eslint.json
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
# Editor configuration, see https://editorconfig.org
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true

[*.md]
max_line_length = off
trim_trailing_whitespace = false

================================================
FILE: .eslintrc.js
================================================
/**
 * @type {import('eslint').Linter.Config}
 */
module.exports = {
    root: true,
    extends: [
        // TODO: warning No cached ProjectGraph is available. The rule will be skipped. @nrwl/nx/enforce-module-boundaries
        // If you encounter this error as part of running standard `nx` commands then please open an issue on
        // https://github.com/nrwl/nx
        // './scripts/eslint/nx.js',
        '@tinkoff/eslint-config-angular',
        '@tinkoff/eslint-config-angular/html',
        '@tinkoff/eslint-config-angular/imports',
        '@tinkoff/eslint-config-angular/line-statements',
        '@tinkoff/eslint-config-angular/member-ordering',
    ],
    ignorePatterns: ['projects/**/test.ts', '*.js', '*.json', '*.less', '*.md'],
    parserOptions: {
        ecmaVersion: 'latest',
        sourceType: 'module',
        project: [require.resolve('./tsconfig.eslint.json')],
    },
    parser: '@typescript-eslint/parser',
    rules: {
        'dot-notation': 'off',
        '@typescript-eslint/dot-notation': [
            'error',
            {
                allowPrivateClassPropertyAccess: true,
                allowProtectedClassPropertyAccess: true,
                allowIndexSignaturePropertyAccess: true,
            },
        ],
        '@typescript-eslint/no-useless-constructor': 'off',
        'no-prototype-builtins': 'off',
        '@typescript-eslint/no-unnecessary-type-constraint': 'error',
        '@typescript-eslint/prefer-includes': 'error',
        'prefer-template': 'error',
        '@typescript-eslint/explicit-function-return-type': [
            'error',
            {
                allowExpressions: true,
                allowTypedFunctionExpressions: true,
                allowHigherOrderFunctions: true,
                allowDirectConstAssertionInArrowFunctions: true,
                allowConciseArrowFunctionExpressionsStartingWithVoid: true,
            },
        ],
        '@typescript-eslint/no-base-to-string': 'error',
        '@typescript-eslint/ban-types': 'error',
        '@typescript-eslint/no-for-in-array': 'error',
        '@typescript-eslint/prefer-nullish-coalescing': 'error',
        '@typescript-eslint/prefer-optional-chain': 'error',
    },
};


================================================
FILE: .github/CODEOWNERS
================================================

# ==================================================================================
# ==================================================================================
#                    @ng-web-apis/intersection-observer codeowners
# ==================================================================================
# ==================================================================================
#
#  Configuration of code ownership and review approvals for the @ng-web-apis/intersection-observer repo.
#
#  More info: https://help.github.com/articles/about-codeowners/
#

* @waterplea @MarsiBarsi
# will be requested for review when someone opens a pull request


================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

open_collective: ng-web-apis
issuehunt: ng-web-apis


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: 🐞 Bug report
about: Create a report to help us improve
title: '[BUG] '
labels: ''
assignee: waterplea
---

# 🐞 Bug report

### Description

<!-- A clear and concise description of what the bug is -->

### Reproduction

<!-- Steps to reproduce or, preferably, a demo on StackBlitz or similar service -->

http://www.stackblitz.com/...

### Expected behavior

<!-- A clear and concise description of what you expected to happen -->

### Versions

-   OS: [e.g. iOS]
-   Browser [e.g. chrome, safari]
-   Angular [e.g. 8]

### Additional context

<!-- Add any other context about the problem here -->


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: 🚀 Feature request
about: Suggest an idea for this project
title: '[FEATURE]'
labels: ''
assignee: waterplea
---

# 🚀 Feature request

### Is your feature request related to a problem?

<!-- A clear and concise description of what the problem is. Ex. -->
<!-- ✍️edit: --> I'm always frustrated when...

### Describe the solution you'd like

<!-- A clear and concise description of what you want to happen -->
<!-- ✍️edit: -->

### Describe alternatives you've considered

<!-- A clear and concise description of any alternative solutions or features you've considered  -->
<!-- ✍️edit: -->

### Additional context

<!-- Add any other context or screenshots about the feature request here -->
<!-- ✍️edit: -->


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
## PR Checklist

Please check if your PR fulfills the following requirements:

-   [ ] The commit message follows [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0-beta.4/)
-   [ ] Tests for the changes have been added (for bug fixes / features)
-   [ ] Docs have been added / updated (for bug fixes / features)

## PR Type

What kind of change does this PR introduce?

<!-- Please check the one that applies to this PR using "x". -->

-   [ ] Bugfix
-   [ ] Feature
-   [ ] Refactoring (no functional changes, no api changes)
-   [ ] Other... Please describe:

## What is the current behavior?

<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->

Issue Number: N/A

## What is the new behavior?

## Does this PR introduce a breaking change?

-   [ ] Yes
-   [ ] No

<!-- If this PR contains a breaking change, please describe the impact and migration path for existing applications below. -->

## Other information


================================================
FILE: .github/workflows/ci.yml
================================================
name: Web APIs CI

on: push

jobs:
  ci:
    # Setup part
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Use Node.js
      uses: actions/setup-node@v1
      with:
        node-version: '12.x'
    - name: Cache Node.js modules
      uses: actions/cache@v2
      with:
        path: ~/.npm 
        key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }}
        restore-keys: |
          ${{ runner.OS }}-node-
          ${{ runner.OS }}-
    - name: Install dependencies
      run: npm ci
    # End of setup
    - run: |
        npm run build
        npm run test
        npm run lint
    - name: Coveralls
      uses: coverallsapp/github-action@master
      with:
        github-token: ${{ secrets.GITHUB_TOKEN }}
        path-to-lcov: ./coverage/intersection-observer/lcov.info

================================================
FILE: .gitignore
================================================
# compiled schematics
schematics/library-starter/*.js
schematics/library-starter/*.js.map
schematics/library-starter/*.d.ts

# compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out

# dependencies
/node_modules

# profiling files
chrome-profiler-events.json
speed-measure-plugin.json

# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*

# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings

# System Files
.DS_Store
Thumbs.db


================================================
FILE: .husky/commit-msg
================================================
#!/bin/sh
# shellcheck disable=SC1090
. "$(dirname "$0")/_/husky.sh"

npx commitlint --edit $1


================================================
FILE: .husky/pre-commit
================================================
#!/bin/sh
# shellcheck disable=SC1090
. "$(dirname "$0")/_/husky.sh"

npx lint-staged
npm run typecheck


================================================
FILE: CHANGELOG.md
================================================
# Changelog

All notable changes to this project will be documented in this file. See
[standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

### [3.0.1](https://github.com/ng-web-apis/intersection-observer/compare/v3.0.0...v3.0.1) (2023-05-25)

- add missing README

## [3.0.0](https://github.com/ng-web-apis/intersection-observer/compare/v2.1.0...v3.0.0) (2022-07-15)

### ⚠ BREAKING CHANGES

- update to Angular 12 and Ivy distribution
  ([90e166b](https://github.com/ng-web-apis/intersection-observer/commit/90e166b7404f2e6edac8713dfbb56cd344e861f7))

## [2.1.0](https://github.com/ng-web-apis/intersection-observer/compare/v2.0.1...v2.1.0) (2020-09-19)

### Features

- **directive:** add `exportAs` ([8341df3](https://github.com/ng-web-apis/intersection-observer/commit/8341df3))

### [2.0.1](https://github.com/ng-web-apis/intersection-observer/compare/v2.0.0...v2.0.1) (2020-08-26)

### Bug Fixes

- **service:** fix exception in Internet Explorer
  ([5f28df4](https://github.com/ng-web-apis/intersection-observer/commit/5f28df4))

## [2.0.0](https://github.com/ng-web-apis/intersection-observer/compare/v1.1.3...v2.0.0) (2020-05-15)

### Features

- **observer:** Add new directives and service to create single observer and observe multiple elements, rename old
  directive ([c2de2ff](https://github.com/ng-web-apis/intersection-observer/commit/c2de2ff))

BREAKING CHANGE: New directives names and mechanics

### [1.1.3](https://github.com/ng-web-apis/intersection-observer/compare/v1.1.2...v1.1.3) (2020-04-24)

- add default values for tokens and remove @Optional

### [1.1.2](https://github.com/ng-web-apis/intersection-observer/compare/v1.1.1...v1.1.2) (2020-04-06)

### Bug Fixes

- **directive:** add attributes to constructor to make code completion work in IDE
  ([ec3c25a](https://github.com/ng-web-apis/intersection-observer/commit/ec3c25a))

### [1.1.1](https://github.com/ng-web-apis/intersection-observer/compare/v1.1.0...v1.1.1) (2020-03-30)

### Bug Fixes

- **service:** only observe when there are subscriptions
  ([2a8e267](https://github.com/ng-web-apis/intersection-observer/commit/2a8e267))

## [1.1.0](https://github.com/ng-web-apis/intersection-observer/compare/v1.0.0...v1.1.0) (2020-03-26)

### Features

- **service:** add `IntersectionObserverService`
  ([65e79aa](https://github.com/ng-web-apis/intersection-observer/commit/65e79aa))

## 1.0.0 (2020-03-25)


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment
include:

-   Using welcoming and inclusive language
-   Being respectful of differing viewpoints and experiences
-   Gracefully accepting constructive criticism
-   Focusing on what is best for the community
-   Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

-   The use of sexualized language or imagery and unwelcome sexual attention or
    advances
-   Trolling, insulting/derogatory comments, and personal or political attacks
-   Public or private harassment
-   Publishing others' private information, such as a physical or electronic
    address, without explicit permission
-   Other conduct which could reasonably be considered inappropriate in a
    professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project at ng.web.apis@gmail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html

[homepage]: https://www.contributor-covenant.org

For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing

> Thank you for considering contributing to our project. Your help if very welcome!

When contributing, it's better to first discuss the change you wish to make via issue,
email, or any other method with the owners of this repository before making a change.

All members of our community are expected to follow our [Code of Conduct](CODE_OF_CONDUCT.md).
Please make sure you are welcoming and friendly in all of our spaces.

## Getting started

In order to make your contribution please make a fork of the repository. After you've pulled
the code, follow these steps to kick start the development:

1. Run `npm ci` to install dependencies
2. Run `npm start` to launch demo project where you could test your changes
3. Use following commands to ensure code quality

```
npm run lint
npm run build
npm run test
```

## Pull Request Process

1. We follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0-beta.4/)
   in our commit messages, i.e. `feat(core): improve typing`
2. Update [README.md](README.md) to reflect changes related to public API and everything relevant
3. Make sure you cover all code changes with unit tests
4. When you are ready, create Pull Request of your fork into original repository


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2020 Alexander Inkin <alexander@inkin.ru>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
___
___
**Attention!** This repository is archived and the library has been moved to [tinkoff/ng-web-apis](https://github.com/Tinkoff/ng-web-apis) monorepository
___
___
# ![ng-web-apis logo](projects/demo/src/assets/logo.svg) Intersection Observer API for Angular

> Part of <img src="projects/demo/src/assets/web-api.svg" align="top"> [Web APIs for Angular](https://ng-web-apis.github.io/)

[![npm version](https://img.shields.io/npm/v/@ng-web-apis/intersection-observer.svg)](https://npmjs.com/package/@ng-web-apis/intersection-observer)
[![npm bundle size](https://img.shields.io/bundlephobia/minzip/@ng-web-apis/intersection-observer)](https://bundlephobia.com/result?p=@ng-web-apis/intersection-observer)
[![.github/workflows/ci.yml](https://github.com/ng-web-apis/intersection-observer/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/ng-web-apis/intersection-observer/actions/workflows/ci.yml)
[![Coveralls github](https://img.shields.io/coveralls/github/ng-web-apis/intersection-observer)](https://coveralls.io/github/ng-web-apis/intersection-observer?branch=master)
[![angular-open-source-starter](https://img.shields.io/badge/made%20with-angular--open--source--starter-d81676?logo=angular)](https://github.com/TinkoffCreditSystems/angular-open-source-starter)

This is a library for declarative use of
[Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)
with Angular.

## Install

If you do not have [@ng-web-apis/common](https://github.com/ng-web-apis/common):

```
npm i @ng-web-apis/common
```

Now install the package:

```
npm i @ng-web-apis/intersection-observer
```

## Usage

1. Import `IntersectionObserverModule` for directives to work
2. Create [IntersectionObserver](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver) with `waIntersectionObserver` directive
3. Observe elements with `waIntersectionObservee` directive
4. _Optional:_ provide root element with `waIntersectionRoot` directive and
   use `waIntersectionThreshold` and `waIntersectionRootMargin` attributes to configure
   [IntersectionObserver options](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/IntersectionObserver)

    > **NOTE:** Keep in mind these are used one time in constructor so you cannot use binding, only strings. Pass comma separated numbers to set an array of thresholds.

### Usage with Jest

DOM environment provided by Jest does not emulate IntersectionObserver API and need to be mocked. You can add the following line to your `setup.ts`:

```ts
// setup.ts
import '@ng-web-apis/universal/mocks';
```

to use mocks from [@ng-web-apis/universal](https://github.com/ng-web-apis/universal) package.

## Examples

Observing multiple elements intersecting with viewport using single observer

```html
<section waIntersectionObserver waIntersectionThreshold="0.5">
    <div (waIntersectionObservee)="onIntersection($event)">
        I'm being observed
    </div>
    <div (waIntersectionObservee)="onIntersection($event)">
        I'm being observed
    </div>
</section>
```

Observing elements intersecting with parent element,
each having different configuration therefore using individual observers:

```html
<section waIntersectionRoot>
    <div
        waIntersectionObserver
        waIntersectionThreshold="0.5"
        (waIntersectionObservee)="onIntersection($event)"
    >
        I'm being observed
    </div>
    <div
        waIntersectionObserver
        waIntersectionThreshold="1,0.5,0"
        (waIntersectionObservee)="onIntersection($event)"
    >
        I'm being observed
    </div>
</section>
```

## Services

Alternatively you can use `Observable`-based services:

1. `IntersectionObserveeService` can be used to observe elements under `waIntersectionObserver`
   directive in the DI tree

2. `IntersectionObserverService` can be used to observe single element independently.
   Provide tokens manually to configure it:

```typescript
@Component({
    selector: 'my-component',
    providers: [
        IntersectionObserverService,
        {
            provide: INTERSECTION_THRESHOLD,
            useValue: 0.5,
        },
        {
            provide: INTERSECTION_ROOT_MARGIN,
            useValue: '10px',
        },
    ],
})
export class MyComponent {
    constructor(
        @Inject(IntersectionObserverService) entries$: IntersectionObserverService,
    ) {
        entries$.subscribe(entries => {
            // Don't forget to unsubscribe
            console.log(entries);
        });
    }
}
```

> In this case provide `INTERSECTION_ROOT` up the DI tree if you
> want to observe intersection with a particular parent element

## Browser support

| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/) |
| :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
|                                                                                                15+                                                                                                |                                                                                                  55+                                                                                                  |                                                                                                51+                                                                                                 |                                                                                               12.2+                                                                                                |

> You can use [polyfill](https://www.npmjs.com/package/intersection-observer) to support older browsers

## Angular Universal

If you want to use this package with SSR, you need to mock `IntersectionObserver` class on the server.
You can use our Universal package for this, see [this example](https://github.com/ng-web-apis/universal#mocks).

## Demo

You can [try online demo here](https://ng-web-apis.github.io/intersection-observer)

## See also

Other [Web APIs for Angular](https://ng-web-apis.github.io/) by [@ng-web-apis](https://github.com/ng-web-apis)

## Open-source

Do you also want to open-source something, but hate the collateral work?
Check out this [Angular Open-source Library Starter](https://github.com/TinkoffCreditSystems/angular-open-source-starter)
we’ve created for our projects. It got you covered on continuous integration,
pre-commit checks, linting, versioning + changelog, code coverage and all that jazz.


================================================
FILE: angular.json
================================================
{
    "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
    "version": 1,
    "newProjectRoot": "projects",
    "projects": {
        "demo": {
            "projectType": "application",
            "schematics": {},
            "root": "projects/demo",
            "sourceRoot": "projects/demo/src",
            "prefix": "app",
            "architect": {
                "build": {
                    "builder": "@angular-devkit/build-angular:browser",
                    "options": {
                        "baseHref": "/intersection-observer/",
                        "deployUrl": "/intersection-observer/",
                        "outputPath": "dist/demo/browser",
                        "index": "projects/demo/src/index.html",
                        "main": "projects/demo/src/main.browser.ts",
                        "polyfills": "projects/demo/src/polyfills.ts",
                        "tsConfig": "tsconfig.json",
                        "assets": [
                            {
                                "glob": "**/*",
                                "input": "projects/demo/src/assets/",
                                "output": "./assets/"
                            },
                            "projects/demo/src/favicon.ico"
                        ],
                        "styles": ["projects/demo/src/styles.css"],
                        "showCircularDependencies": false,
                        "vendorChunk": true,
                        "extractLicenses": false,
                        "buildOptimizer": false,
                        "sourceMap": true,
                        "optimization": false,
                        "namedChunks": true,
                        "scripts": []
                    },
                    "configurations": {
                        "production": {
                            "baseHref": "/intersection-observer/",
                            "deployUrl": "/intersection-observer/",
                            "optimization": true,
                            "outputHashing": "all",
                            "sourceMap": false,
                            "namedChunks": false,
                            "buildOptimizer": true,
                            "statsJson": false,
                            "progress": false,
                            "budgets": [
                                {
                                    "type": "initial",
                                    "maximumWarning": "2mb",
                                    "maximumError": "5mb"
                                }
                            ]
                        },
                        "development": {
                            "baseHref": "/",
                            "deployUrl": "/"
                        }
                    },
                    "defaultConfiguration": "production"
                },
                "serve": {
                    "builder": "@angular-devkit/build-angular:dev-server",
                    "options": {
                        "browserTarget": "demo:build"
                    },
                    "configurations": {
                        "production": {
                            "browserTarget": "demo:build:production"
                        }
                    }
                },
                "server": {
                    "builder": "@angular-devkit/build-angular:server",
                    "options": {
                        "outputPath": "dist/demo/server",
                        "main": "projects/demo/server.ts",
                        "tsConfig": "projects/demo/tsconfig.server.json",
                        "inlineStyleLanguage": "less"
                    },
                    "configurations": {
                        "production": {
                            "outputHashing": "media",
                            "fileReplacements": [
                                {
                                    "replace": "projects/demo/src/environments/environment.ts",
                                    "with": "projects/demo/src/environments/environment.prod.ts"
                                }
                            ]
                        },
                        "development": {
                            "optimization": false,
                            "sourceMap": true,
                            "extractLicenses": false
                        }
                    },
                    "defaultConfiguration": "production"
                },
                "serve-ssr": {
                    "builder": "@nguniversal/builders:ssr-dev-server",
                    "configurations": {
                        "development": {
                            "browserTarget": "demo:build:development",
                            "serverTarget": "demo:server:development"
                        },
                        "production": {
                            "browserTarget": "demo:build:production",
                            "serverTarget": "demo:server:production"
                        }
                    },
                    "defaultConfiguration": "development"
                },
                "prerender": {
                    "builder": "@nguniversal/builders:prerender",
                    "options": {
                        "routes": ["/"]
                    },
                    "configurations": {
                        "production": {
                            "browserTarget": "demo:build:production",
                            "serverTarget": "demo:server:production"
                        },
                        "development": {
                            "browserTarget": "demo:build:development",
                            "serverTarget": "demo:server:development"
                        }
                    },
                    "defaultConfiguration": "production"
                }
            }
        },
        "intersection-observer": {
            "projectType": "library",
            "root": "projects/intersection-observer",
            "sourceRoot": "projects/intersection-observer/src",
            "architect": {
                "build": {
                    "builder": "@angular-devkit/build-angular:ng-packagr",
                    "options": {
                        "tsConfig": "projects/intersection-observer/tsconfig.lib.json",
                        "project": "projects/intersection-observer/ng-package.json"
                    }
                },
                "test": {
                    "builder": "@angular-devkit/build-angular:karma",
                    "options": {
                        "main": "projects/intersection-observer/src/test.ts",
                        "tsConfig": "projects/intersection-observer/tsconfig.spec.json",
                        "karmaConfig": "projects/intersection-observer/karma.conf.js",
                        "codeCoverage": true,
                        "browsers": "ChromeHeadless"
                    }
                }
            }
        }
    },
    "defaultProject": "intersection-observer"
}


================================================
FILE: commitlint.config.js
================================================
module.exports = {extends: ['@commitlint/config-conventional']};


================================================
FILE: package.json
================================================
{
    "name": "@ng-web-apis/intersection-observer",
    "version": "3.0.1",
    "description": "A library for declarative use of Intersection Observer API with Angular",
    "keywords": [
        "angular",
        "ng",
        "intersection",
        "observer"
    ],
    "homepage": "https://github.com/ng-web-apis/intersection-observer#README",
    "bugs": "https://github.com/ng-web-apis/intersection-observer/issues",
    "repository": "https://github.com/ng-web-apis/intersection-observer",
    "license": "MIT",
    "author": {
        "name": "Alexander Inkin",
        "email": "alexander@inkin.ru"
    },
    "contributors": [
        "Roman Sedov <79601794011@ya.ru>"
    ],
    "scripts": {
        "postinstall": "husky install",
        "ng": "ng",
        "start": "ng serve",
        "start:ssr": "ng run demo:serve-ssr",
        "serve:ssr": "node dist/demo/server/main.js",
        "build:ssr": "ng build && ng run demo:server",
        "prerender": "ng run demo:prerender",
        "build": "ng build",
        "postbuild": "node scripts/postbuild.js",
        "test": "ng test",
        "stylelint": "stylelint '**/*.{less,css}'",
        "lint": "eslint --cache --cache-location node_modules/.cache/eslint",
        "typecheck": "tsc --noEmit --skipLibCheck",
        "release": "standard-version",
        "release:patch": "npm run release -- --release-as patch",
        "release:minor": "npm run release -- --release-as minor",
        "release:major": "npm run release -- --release-as major",
        "publish": "npm run build && npm publish ./dist/intersection-observer"
    },
    "lint-staged": {
        "*.{js,ts,html,md,less,json}": [
            "npm run lint -- --fix",
            "prettier --write",
            "git add"
        ],
        "*.less": [
            "stylelint --fix",
            "git add"
        ]
    },
    "dependencies": {
        "@angular/common": "12.2.16",
        "@angular/compiler": "12.2.16",
        "@angular/core": "12.2.16",
        "@angular/forms": "12.2.16",
        "@angular/platform-browser": "12.2.16",
        "@angular/platform-browser-dynamic": "12.2.16",
        "@angular/platform-server": "12.2.16",
        "@angular/router": "12.2.16",
        "@ng-web-apis/common": "^2.0.0",
        "@ng-web-apis/universal": "^2.0.0",
        "@nguniversal/express-engine": "12.1.3",
        "core-js": "3.20.3",
        "intersection-observer": "^0.12.2",
        "rxjs": "7.5.2",
        "tslib": "2.3.1",
        "zone.js": "0.11.4"
    },
    "devDependencies": {
        "@angular-devkit/build-angular": "12.2.16",
        "@angular-devkit/core": "12.2.16",
        "@angular/cli": "12.2.16",
        "@angular/compiler-cli": "12.2.16",
        "@angular/language-service": "12.2.16",
        "@commitlint/cli": "^11.0.0",
        "@commitlint/config-conventional": "^11.0.0",
        "@nguniversal/builders": "12.1.3",
        "@tinkoff/eslint-config": "1.36.1",
        "@tinkoff/eslint-config-angular": "1.36.1",
        "@tinkoff/prettier-config": "1.32.1",
        "@types/estree": "1.0.0",
        "@types/express": "4.17.13",
        "@types/jasmine": "3.10.3",
        "@types/jasminewd2": "2.0.10",
        "@types/node": "18.0.4",
        "coveralls": "3.1.1",
        "husky": "7.0.4",
        "jasmine-core": "4.0.0",
        "jasmine-spec-reporter": "7.0.0",
        "karma": "6.3.11",
        "karma-chrome-launcher": "3.1.0",
        "karma-coverage-istanbul-reporter": "3.0.3",
        "karma-jasmine": "4.0.1",
        "karma-jasmine-html-reporter": "1.7.0",
        "lint-staged": "12.2.1",
        "ng-packagr": "12.2.6",
        "prettier": "2.5.1",
        "standard-version": "9.3.2",
        "ts-node": "9.0.0",
        "tslint": "6.1.3",
        "typescript": "4.3.5"
    },
    "engines": {
        "node": ">= 10",
        "npm": ">= 3"
    },
    "standard-version": {
        "scripts": {
            "postbump": "node scripts/syncVersions.js && git add **/package.json"
        }
    }
}


================================================
FILE: prettier.config.js
================================================
const base = require('@tinkoff/prettier-config/angular');

module.exports = {
    ...base,
    singleAttributePerLine: true,
    overrides: [
        ...base.overrides,
        {
            files: ['*.js', '*.ts'],
            options: {printWidth: 90, parser: 'typescript'},
        },
        {
            files: ['*.html'],
            options: {printWidth: 120, parser: 'angular'},
        },
    ],
};


================================================
FILE: projects/demo/.gitignore
================================================
# compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out

# dependencies
/node_modules

# profiling files
chrome-profiler-events.json
speed-measure-plugin.json

# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*

# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings

# System Files
.DS_Store
Thumbs.db


================================================
FILE: projects/demo/angular.json
================================================
{
    "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
    "version": 1,
    "newProjectRoot": "projects",
    "projects": {
        "demo": {
            "root": "",
            "sourceRoot": "src",
            "projectType": "application",
            "prefix": "app",
            "schematics": {},
            "architect": {
                "build": {
                    "builder": "@angular-devkit/build-angular:browser",
                    "options": {
                        "outputPath": "dist/demo",
                        "index": "src/index.html",
                        "main": "src/main.browser.ts",
                        "polyfills": "src/polyfills.ts",
                        "tsConfig": "tsconfig.demo.json",
                        "aot": false,
                        "assets": [
                            {
                                "glob": "**/*",
                                "input": "projects/demo/src/assets/",
                                "output": "./assets/"
                            },
                            "src/favicon.ico"
                        ],
                        "styles": ["src/styles.css"],
                        "scripts": []
                    },
                    "configurations": {
                        "production": {
                            "optimization": true,
                            "outputHashing": "all",
                            "sourceMap": false,
                            "extractCss": true,
                            "namedChunks": false,
                            "aot": true,
                            "extractLicenses": true,
                            "vendorChunk": false,
                            "buildOptimizer": true
                        }
                    }
                },
                "serve": {
                    "builder": "@angular-devkit/build-angular:dev-server",
                    "options": {
                        "browserTarget": "demo:build"
                    },
                    "configurations": {
                        "production": {
                            "browserTarget": "demo:build:production"
                        }
                    }
                }
            }
        }
    },
    "defaultProject": "demo"
}


================================================
FILE: projects/demo/karma.conf.js
================================================
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html

module.exports = function(config) {
    config.set({
        basePath: '',
        frameworks: ['jasmine', '@angular-devkit/build-angular'],
        plugins: [
            require('karma-jasmine'),
            require('karma-chrome-launcher'),
            require('karma-jasmine-html-reporter'),
            require('karma-coverage-istanbul-reporter'),
            require('@angular-devkit/build-angular/plugins/karma'),
        ],
        client: {
            clearContext: false, // leave Jasmine Spec Runner output visible in browser
        },
        coverageIstanbulReporter: {
            dir: require('path').join(__dirname, '../../coverage/demo'),
            reports: ['html', 'lcovonly'],
            fixWebpackSourcePaths: true,
        },
        reporters: ['progress', 'kjhtml'],
        port: 9876,
        colors: true,
        logLevel: config.LOG_INFO,
        autoWatch: true,
        browsers: ['ChromeHeadless'],
        singleRun: true,
        customLaunchers: {
            ChromeHeadless: {
                base: 'Chrome',
                flags: [
                    '--no-sandbox',
                    '--headless',
                    '--disable-gpu',
                    '--remote-debugging-port=9222',
                ],
            },
        },
    });
};


================================================
FILE: projects/demo/package.json
================================================
{
    "name": "demo",
    "version": "3.0.1",
    "private": true,
    "scripts": {
        "ng": "ng",
        "start": "ng serve",
        "build": "ng build"
    },
    "dependencies": {
        "@angular/common": "12.2.16",
        "@angular/compiler": "12.2.16",
        "@angular/core": "12.2.16",
        "@angular/forms": "12.2.16",
        "@angular/platform-browser": "12.2.16",
        "@angular/platform-browser-dynamic": "12.2.16",
        "@angular/router": "12.2.16",
        "@ng-web-apis/common": "latest",
        "@ng-web-apis/intersection-observer": "latest",
        "core-js": "3.20.3",
        "intersection-observer": "^0.12.2",
        "rxjs": "7.5.2",
        "zone.js": "0.11.4"
    },
    "devDependencies": {
        "@angular-devkit/build-angular": "12.2.16",
        "@angular-devkit/core": "12.2.16",
        "@angular/cli": "12.2.16",
        "@angular/compiler-cli": "12.2.16",
        "@angular/language-service": "12.2.16",
        "@types/node": "18.0.4",
        "ts-node": "9.0.0",
        "tslint": "6.1.3",
        "typescript": "4.3.5"
    }
}


================================================
FILE: projects/demo/server.ts
================================================
import '@ng-web-apis/universal/mocks';
import 'zone.js/node';

import {APP_BASE_HREF} from '@angular/common';
import {provideLocation, provideUserAgent} from '@ng-web-apis/universal';
import {ngExpressEngine} from '@nguniversal/express-engine';
import * as express from 'express';
import {existsSync} from 'fs';
import {join} from 'path';

import {AppServerModule} from './src/main.server';

// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
    const server = express();
    const distFolder = join(process.cwd(), 'dist/demo/browser');
    const indexHtml = existsSync(join(distFolder, 'index.original.html'))
        ? 'index.original.html'
        : 'index';

    // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
    server.engine(
        'html',
        ngExpressEngine({
            bootstrap: AppServerModule,
        }),
    );

    server.set('view engine', 'html');
    server.set('views', distFolder);

    // Example Express Rest API endpoints
    // server.get('/api/**', (req, res) => { });
    // Serve static files from /browser
    server.get(
        '*.*',
        express.static(distFolder, {
            maxAge: '1y',
        }),
    );

    // All regular routes use the Universal engine
    server.get('*', (req, res) => {
        res.render(indexHtml, {
            req,
            providers: [
                {provide: APP_BASE_HREF, useValue: req.baseUrl},
                provideLocation(req),
                provideUserAgent(req),
            ],
        });
    });

    return server;
}

function run(): void {
    const port = process.env.PORT || 4000;
    const server = app();

    server.listen(port, () => {
        console.info(`Node Express server listening on http://localhost:${port}`);
    });
}

// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule?.filename || '';

if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
    run();
}

export * from './src/main.server';


================================================
FILE: projects/demo/src/app/app.browser.module.ts
================================================
import {
    APP_BASE_HREF,
    CommonModule,
    LocationStrategy,
    PathLocationStrategy,
} from '@angular/common';
import {NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {BrowserModule} from '@angular/platform-browser';
import {IntersectionObserverModule} from '@ng-web-apis/intersection-observer';
import {AppComponent} from './app.component';
import {AppRoutingModule} from './app.routes';

@NgModule({
    bootstrap: [AppComponent],
    imports: [
        CommonModule,
        FormsModule,
        BrowserModule.withServerTransition({appId: 'demo'}),
        AppRoutingModule,
        IntersectionObserverModule,
    ],
    declarations: [AppComponent],
    providers: [
        {
            provide: LocationStrategy,
            useClass: PathLocationStrategy,
        },
        {
            provide: APP_BASE_HREF,
            useValue: '',
        },
    ],
})
export class AppBrowserModule {}


================================================
FILE: projects/demo/src/app/app.component.html
================================================
<section
    *ngIf="support; else not"
    class="wrapper"
    waIntersectionRoot
    waIntersectionObserver
    waIntersectionThreshold="0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9"
>
    <h1
        class="element"
        [attr.data-ratio]="ratio"
        (waIntersectionObservee)="onIntersection($event)"
    >
        I'm being observed
    </h1>
</section>
<ng-template #not>
    Your browser does not support Intersection Observer API
</ng-template>


================================================
FILE: projects/demo/src/app/app.component.less
================================================
:host {
    perspective: 150vw;
    user-select: none;
    flex-direction: column;
    align-items: center;
}

.wrapper {
    position: relative;
    height: 200px;
    width: 80vw;
    overflow: auto;
    box-shadow: 0 12px 36px rgba(0, 0, 0, 0.2);

    &:before {
        content: '';
        display: block;
        height: 900px;
    }
}

.element {
    position: absolute;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0;
    top: 300px;
    left: 10vw;
    width: 60vw;
    height: 200px;
    transition: background 0.1s;

    &[data-ratio='0'] {
        background: #8591eb;
    }

    &[data-ratio='1'] {
        background: #85a0eb;
    }

    &[data-ratio='2'] {
        background: #84aeeb;
    }

    &[data-ratio='3'] {
        background: #83beeb;
    }

    &[data-ratio='4'] {
        background: #86d2eb;
    }

    &[data-ratio='5'] {
        background: #87ddeb;
    }

    &[data-ratio='6'] {
        background: #8ae5eb;
    }

    &[data-ratio='7'] {
        background: #8bebdf;
    }

    &[data-ratio='8'] {
        background: #83ebc8;
    }

    &[data-ratio='9'] {
        background: #6beb99;
    }

    &[data-ratio='10'] {
        background: #4ceb60;
    }
}


================================================
FILE: projects/demo/src/app/app.component.ts
================================================
import {ChangeDetectionStrategy, Component, Inject} from '@angular/core';
import {INTERSECTION_OBSERVER_SUPPORT} from '@ng-web-apis/intersection-observer';

@Component({
    selector: 'main',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.less'],
    changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppComponent {
    ratio = 0;

    constructor(@Inject(INTERSECTION_OBSERVER_SUPPORT) readonly support: boolean) {}

    onIntersection(intersections: IntersectionObserverEntry[]) {
        this.ratio = Math.round(intersections[0].intersectionRatio * 10);
    }
}


================================================
FILE: projects/demo/src/app/app.routes.ts
================================================
import {NgModule} from '@angular/core';
import {RouterModule} from '@angular/router';
import {AppComponent} from './app.component';

export const appRoutes = [
    {
        path: '**',
        component: AppComponent,
    },
];

@NgModule({
    imports: [RouterModule.forRoot(appRoutes)],
    exports: [RouterModule],
})
export class AppRoutingModule {}


================================================
FILE: projects/demo/src/app/app.server.module.ts
================================================
import {NgModule} from '@angular/core';
import {ServerModule} from '@angular/platform-server';

import {AppBrowserModule} from './app.browser.module';
import {AppComponent} from './app.component';

@NgModule({
    imports: [AppBrowserModule, ServerModule],
    bootstrap: [AppComponent],
})
export class AppServerModule {}


================================================
FILE: projects/demo/src/assets/browserconfig.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
    <msapplication>
        <tile>
            <square150x150logo src="/intersection-observer/assets/mstile-150x150.png"/>
            <TileColor>#2d89ef</TileColor>
        </tile>
    </msapplication>
</browserconfig>


================================================
FILE: projects/demo/src/assets/site.webmanifest
================================================
{
    "name": "",
    "short_name": "",
    "icons": [
        {
            "src": "/intersection-observer/assets/android-chrome-192x192.png",
            "sizes": "192x192",
            "type": "image/png"
        },
        {
            "src": "/intersection-observer/assets/android-chrome-512x512.png",
            "sizes": "512x512",
            "type": "image/png"
        }
    ],
    "theme_color": "#ffffff",
    "background_color": "#ffffff",
    "display": "standalone"
}


================================================
FILE: projects/demo/src/index.html
================================================
<html>
    <head>
        <title>Intersection Observer API for Angular</title>
        <link
            rel="apple-touch-icon"
            sizes="180x180"
            href="/intersection-observer/assets/apple-touch-icon.png"
        />
        <link
            rel="icon"
            type="image/png"
            sizes="32x32"
            href="/intersection-observer/assets/favicon-32x32.png"
        />
        <link
            rel="icon"
            type="image/png"
            sizes="16x16"
            href="/intersection-observer/assets/favicon-16x16.png"
        />
        <link rel="manifest" href="/intersection-observer/assets/site.webmanifest" />
        <link
            rel="mask-icon"
            href="/intersection-observer/assets/safari-pinned-tab.svg"
            color="#5bbad5"
        />
        <link rel="shortcut icon" href="/intersection-observer/assets/favicon.ico" />
        <meta name="msapplication-TileColor" content="#2b5797" />
        <meta
            name="msapplication-config"
            content="/intersection-observer/assets/browserconfig.xml"
        />
        <meta name="theme-color" content="#ffffff" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
    </head>
    <body>
        <header>
            <img
                src="/intersection-observer/assets/logo.svg"
                onerror="this.src='/assets/logo.svg'"
                class="logo"
                width="128"
                height="128"
                alt="Intersection Observer API for Angular logo"
            />
            <div>
                <h1>Intersection Observer API for Angular</h1>
                Part of
                <a href="https://ng-web-apis.github.io/">
                    <img
                        src="/intersection-observer/assets/web-api.svg"
                        onerror="this.src='/assets/web-api.svg'"
                        width="32"
                        height="32"
                        align="center"
                        alt="Web APIs logo"
                    />
                    Web APIs for Angular
                </a>
            </div>
        </header>
        <main>loading</main>
        <footer>
            Get it here:
            <a href="https://github.com/ng-web-apis/intersection-observer">GitHub</a> |
            <a href="https://www.npmjs.com/package/@ng-web-apis/intersection-observer"
                >NPM</a
            >
        </footer>
    </body>
</html>


================================================
FILE: projects/demo/src/main.browser.ts
================================================
import './polyfills';

import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {AppBrowserModule} from './app/app.browser.module';

platformBrowserDynamic()
    .bootstrapModule(AppBrowserModule)
    .then(ref => {
        const windowRef: any = window;

        // Ensure Angular destroys itself on hot reloads for Stackblitz
        if (windowRef['ngRef']) {
            windowRef['ngRef'].destroy();
        }

        windowRef['ngRef'] = ref;
    })
    .catch(err => console.error(err));


================================================
FILE: projects/demo/src/main.server.ts
================================================
export {AppServerModule} from './app/app.server.module';


================================================
FILE: projects/demo/src/polyfills.ts
================================================
import 'intersection-observer';
import 'zone.js';


================================================
FILE: projects/demo/src/styles.css
================================================
/* Base demo styles */
body,
html {
    display: flex;
    flex-direction: column;
    margin: 0;
    height: 100%;
    font-family: Roboto, 'Helvetica Neue Light', 'Helvetica Neue', Helvetica, Arial,
        'Lucida Grande', sans-serif;
    color: #444;
}

header {
    display: flex;
    width: 100%;
    max-width: 800px;
    margin: 0 auto;
    padding: 40px 10px;
    box-sizing: border-box;
    border-bottom: 1px solid gainsboro;
}

main {
    flex: 1;
    display: flex;
    justify-content: center;
    padding: 40px 0;
}

footer {
    padding: 16px;
    font-size: 12px;
    border-top: 1px solid gainsboro;
    text-align: center;
}

a {
    color: #1976D2;
    text-decoration: none;
}

.logo {
    margin-right: 20px;
}


================================================
FILE: projects/demo/src/typings.d.ts
================================================
declare module '*';


================================================
FILE: projects/demo/tsconfig.demo.json
================================================
{
    "extends": "./tsconfig.json",
    "compilerOptions": {
        "outDir": "../out-tsc/app",
        "typeRoots": [],
        "paths": {}
    }
}


================================================
FILE: projects/demo/tsconfig.json
================================================
{
    "extends": "../../tsconfig.json"
}


================================================
FILE: projects/demo/tsconfig.server.json
================================================
{
    "extends": "./tsconfig.json",
    "compilerOptions": {
        "module": "commonjs"
    },
    "angularCompilerOptions": {
        "entryModule": "src/app/app.server.module#AppServerModule"
    }
}


================================================
FILE: projects/demo/tsconfig.spec.json
================================================
{
    "extends": "./tsconfig.json",
    "compilerOptions": {
        "outDir": "../../out-tsc/spec",
        "types": ["jasmine", "node"]
    },
    "files": ["src/test.ts"],
    "include": ["**/*.spec.ts", "**/*.d.ts"]
}


================================================
FILE: projects/intersection-observer/LICENSE
================================================
MIT License

Copyright (c) 2020 Alexander Inkin <alexander@inkin.ru>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: projects/intersection-observer/karma.conf.js
================================================
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html

module.exports = function(config) {
    config.set({
        basePath: '',
        frameworks: ['jasmine', '@angular-devkit/build-angular'],
        plugins: [
            require('karma-jasmine'),
            require('karma-chrome-launcher'),
            require('karma-jasmine-html-reporter'),
            require('karma-coverage-istanbul-reporter'),
            require('@angular-devkit/build-angular/plugins/karma'),
        ],
        client: {
            clearContext: false, // leave Jasmine Spec Runner output visible in browser
        },
        coverageIstanbulReporter: {
            dir: require('path').join(__dirname, '../../coverage/intersection-observer'),
            reports: ['html', 'lcovonly'],
            fixWebpackSourcePaths: true,
        },
        reporters: ['progress', 'kjhtml'],
        port: 9876,
        colors: true,
        logLevel: config.LOG_INFO,
        autoWatch: true,
        browsers: ['ChromeHeadless'],
        singleRun: true,
        customLaunchers: {
            ChromeHeadless: {
                base: 'Chrome',
                flags: [
                    '--no-sandbox',
                    '--headless',
                    '--disable-gpu',
                    '--disable-web-security',
                    '--remote-debugging-port=9222',
                ],
            },
        },
    });
};


================================================
FILE: projects/intersection-observer/ng-package.json
================================================
{
    "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
    "dest": "../../dist/intersection-observer",
    "lib": {
        "entryFile": "src/public-api.ts"
    }
}


================================================
FILE: projects/intersection-observer/package.json
================================================
{
    "name": "@ng-web-apis/intersection-observer",
    "version": "3.0.1",
    "description": "A library for declarative use of Intersection Observer API with Angular",
    "keywords": [
        "angular",
        "ng",
        "intersection",
        "observer"
    ],
    "homepage": "https://github.com/ng-web-apis/intersection-observer#README",
    "bugs": "https://github.com/ng-web-apis/intersection-observer/issues",
    "repository": "https://github.com/ng-web-apis/intersection-observer",
    "license": "MIT",
    "author": {
        "name": "Alexander Inkin",
        "email": "alexander@inkin.ru"
    },
    "contributors": [
        "Roman Sedov <79601794011@ya.ru>"
    ],
    "peerDependencies": {
        "@angular/core": ">=12.0.0",
        "@ng-web-apis/common": ">=2.0.0"
    }
}


================================================
FILE: projects/intersection-observer/src/directives/intersection-observee.directive.ts
================================================
import {Directive, Inject} from '@angular/core';
import {Observable} from 'rxjs';

import {IntersectionObserveeService} from '../services/intersection-observee.service';

@Directive({
    selector: '[waIntersectionObservee]',
    outputs: ['waIntersectionObservee'],
    providers: [IntersectionObserveeService],
})
export class IntersectionObserveeDirective {
    constructor(
        @Inject(IntersectionObserveeService)
        readonly waIntersectionObservee: Observable<IntersectionObserverEntry[]>,
    ) {}
}


================================================
FILE: projects/intersection-observer/src/directives/intersection-observer.directive.ts
================================================
import {
    Attribute,
    Directive,
    ElementRef,
    Inject,
    OnDestroy,
    Optional,
} from '@angular/core';
import {INTERSECTION_ROOT} from '../tokens/intersection-root';
import {rootMarginFactory} from '../utils/root-margin-factory';
import {thresholdFactory} from '../utils/threshold-factory';

@Directive({
    selector: '[waIntersectionObserver]',
    exportAs: 'IntersectionObserver',
})
export class IntersectionObserverDirective extends IntersectionObserver
    implements OnDestroy {
    private readonly callbacks = new Map<Element, IntersectionObserverCallback>();

    constructor(
        @Optional() @Inject(INTERSECTION_ROOT) root: ElementRef<Element> | null,
        @Attribute('waIntersectionRootMargin') rootMargin: string | null,
        @Attribute('waIntersectionThreshold') threshold: string | null,
    ) {
        super(
            entries => {
                this.callbacks.forEach((callback, element) => {
                    const filtered = entries.filter(({target}) => target === element);

                    return filtered.length && callback(filtered, this);
                });
            },
            {
                root: root && root.nativeElement,
                rootMargin: rootMarginFactory(rootMargin),
                threshold: thresholdFactory(threshold),
            },
        );
    }

    observe(target: Element, callback: IntersectionObserverCallback = () => {}) {
        super.observe(target);
        this.callbacks.set(target, callback);
    }

    unobserve(target: Element) {
        super.unobserve(target);
        this.callbacks.delete(target);
    }

    ngOnDestroy() {
        this.disconnect();
    }
}


================================================
FILE: projects/intersection-observer/src/directives/intersection-root.directive.ts
================================================
import {Directive, ElementRef} from '@angular/core';
import {INTERSECTION_ROOT} from '../tokens/intersection-root';

@Directive({
    selector: '[waIntersectionRoot]',
    providers: [
        {
            provide: INTERSECTION_ROOT,
            useExisting: ElementRef,
        },
    ],
})
export class IntersectionRootDirective {}


================================================
FILE: projects/intersection-observer/src/directives/tests/intersection-observee.spec.ts
================================================
import {Component, ViewChild} from '@angular/core';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {IntersectionObserverModule} from '../../module';
import {INTERSECTION_ROOT_MARGIN} from '../../tokens/intersection-root-margin';
import {INTERSECTION_THRESHOLD} from '../../tokens/intersection-threshold';
import {IntersectionObserverDirective} from '../intersection-observer.directive';

describe('IntersectionObserveeDirective', () => {
    @Component({
        template: `
            <div id="manual_observee">Hello</div>
            <section
                *ngIf="observe"
                #root
                id="observer_root"
                style="position: relative; height: 200px; overflow: auto;"
                waIntersectionThreshold="0.5"
                waIntersectionObserver
                waIntersectionRoot
            >
                <div style="height: 900px;">Height expander</div>
                <h1
                    style="position: absolute; top: 200px; height: 200px;"
                    (waIntersectionObservee)="onIntersection($event)"
                >
                    I'm being observed
                </h1>
                <h1
                    style="position: absolute; top: 200px; height: 200px;"
                    waIntersectionObserver
                    (waIntersectionObservee)="onIntersection($event)"
                >
                    Default values
                </h1>
            </section>
        `,
    })
    class TestComponent {
        @ViewChild('root', {read: IntersectionObserverDirective})
        observer!: IntersectionObserverDirective;

        onIntersection = jasmine.createSpy('onIntersection');
        observe = true;
    }

    let fixture: ComponentFixture<TestComponent>;
    let testComponent: TestComponent;

    beforeEach(() => {
        TestBed.configureTestingModule({
            imports: [IntersectionObserverModule],
            declarations: [TestComponent],
        });

        fixture = TestBed.createComponent(TestComponent);
        testComponent = fixture.componentInstance;
        fixture.detectChanges();
        testComponent.onIntersection.calls.reset();
    });

    it('Emits intersections', done => {
        document.querySelector('#observer_root')!.scrollTop = 350;
        fixture.detectChanges();

        setTimeout(() => {
            expect(testComponent.onIntersection).toHaveBeenCalled();
            document.querySelector('#observer_root')!.scrollTop = 0;
            fixture.detectChanges();
            testComponent.observe = false;
            fixture.detectChanges();
            done();
        }, 100);
    });

    it('Compatible with native method signature', () => {
        expect(() =>
            testComponent.observer.observe(document.querySelector('#manual_observee')!),
        ).not.toThrow();
    });

    it('Default options', () => {
        // https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/IntersectionObserver
        expect(TestBed.get(INTERSECTION_ROOT_MARGIN)).toBe('0px 0px 0px 0px');
        expect(TestBed.get(INTERSECTION_THRESHOLD)).toBe(0);
    });
});


================================================
FILE: projects/intersection-observer/src/module.ts
================================================
import {NgModule} from '@angular/core';
import {IntersectionObserveeDirective} from './directives/intersection-observee.directive';
import {IntersectionObserverDirective} from './directives/intersection-observer.directive';
import {IntersectionRootDirective} from './directives/intersection-root.directive';

@NgModule({
    declarations: [
        IntersectionObserverDirective,
        IntersectionObserveeDirective,
        IntersectionRootDirective,
    ],
    exports: [
        IntersectionObserverDirective,
        IntersectionObserveeDirective,
        IntersectionRootDirective,
    ],
})
export class IntersectionObserverModule {}


================================================
FILE: projects/intersection-observer/src/public-api.ts
================================================
/**
 * Public API Surface of @ng-web-apis/intersection-observer
 */

/* Directives */
export * from './directives/intersection-observee.directive';
export * from './directives/intersection-observer.directive';
export * from './directives/intersection-root.directive';

/* Modules */
export * from './module';

/* Services */
export * from './services/intersection-observee.service';
export * from './services/intersection-observer.service';

/* Tokens */
export * from './tokens/intersection-root';
export * from './tokens/intersection-root-margin';
export * from './tokens/intersection-threshold';
export * from './tokens/support';


================================================
FILE: projects/intersection-observer/src/services/intersection-observee.service.ts
================================================
import {ElementRef, Inject, Injectable} from '@angular/core';
import {Observable} from 'rxjs';
import {share} from 'rxjs/operators';
import {IntersectionObserverDirective} from '../directives/intersection-observer.directive';

@Injectable()
export class IntersectionObserveeService extends Observable<IntersectionObserverEntry[]> {
    constructor(
        @Inject(ElementRef) {nativeElement}: ElementRef<Element>,
        @Inject(IntersectionObserverDirective)
        observer: IntersectionObserverDirective,
    ) {
        super(subscriber => {
            observer.observe(nativeElement, entries => {
                subscriber.next(entries);
            });

            return () => {
                observer.unobserve(nativeElement);
            };
        });

        return this.pipe(share());
    }
}


================================================
FILE: projects/intersection-observer/src/services/intersection-observer.service.ts
================================================
import {ElementRef, Inject, Injectable, Optional} from '@angular/core';
import {Observable} from 'rxjs';
import {share} from 'rxjs/operators';
import {INTERSECTION_ROOT} from '../tokens/intersection-root';
import {INTERSECTION_ROOT_MARGIN} from '../tokens/intersection-root-margin';
import {INTERSECTION_THRESHOLD} from '../tokens/intersection-threshold';
import {INTERSECTION_OBSERVER_SUPPORT} from '../tokens/support';

@Injectable()
export class IntersectionObserverService extends Observable<IntersectionObserverEntry[]> {
    constructor(
        @Inject(ElementRef) {nativeElement}: ElementRef<Element>,
        @Inject(INTERSECTION_OBSERVER_SUPPORT) support: boolean,
        @Inject(INTERSECTION_ROOT_MARGIN) rootMargin: string,
        @Inject(INTERSECTION_THRESHOLD) threshold: number | number[],
        @Optional() @Inject(INTERSECTION_ROOT) root: ElementRef<Element> | null,
    ) {
        super(subscriber => {
            if (!support) {
                subscriber.error('IntersectionObserver is not supported in your browser');

                return;
            }

            const observer = new IntersectionObserver(
                entries => {
                    subscriber.next(entries);
                },
                {
                    root: root && root.nativeElement,
                    rootMargin,
                    threshold,
                },
            );

            observer.observe(nativeElement);

            return () => {
                observer.disconnect();
            };
        });

        return this.pipe(share());
    }
}


================================================
FILE: projects/intersection-observer/src/services/tests/intersection-observer.service.spec.ts
================================================
import {take} from 'rxjs/operators';
import {IntersectionObserverService} from '../intersection-observer.service';

describe('IntersectionObserverService', () => {
    it('works', done => {
        let called = false;

        const nativeElement = document.createElement('div');
        const service = new IntersectionObserverService(
            {
                nativeElement,
            },
            true,
            '0px 0px 0px 0px',
            0,
            {
                nativeElement: document.body,
            },
        );

        service.pipe(take(1)).subscribe({
            next: () => {
                called = true;
            },
        });

        document.body.appendChild(nativeElement);

        setTimeout(() => {
            expect(called).toBe(true);
            done();
        });
    });

    it('throws when not supported', () => {
        let error = false;
        const service = new IntersectionObserverService(
            {
                nativeElement: document.createElement('DIV'),
            },
            false,
            '0px 0px 0px 0px',
            0,
            null,
        );

        service.subscribe({
            error: () => {
                error = true;
            },
        });

        expect(error).toBe(true);
    });
});


================================================
FILE: projects/intersection-observer/src/test.ts
================================================
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js';
import 'zone.js/testing';

import {getTestBed} from '@angular/core/testing';
import {
    BrowserDynamicTestingModule,
    platformBrowserDynamicTesting,
} from '@angular/platform-browser-dynamic/testing';

declare const require: any;

// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
    BrowserDynamicTestingModule,
    platformBrowserDynamicTesting(),
);

// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);

// And load the modules.
context.keys().map(context);


================================================
FILE: projects/intersection-observer/src/tokens/intersection-root-margin.ts
================================================
import {InjectionToken} from '@angular/core';

export const INTERSECTION_ROOT_MARGIN_DEFAULT = '0px 0px 0px 0px';
export const INTERSECTION_ROOT_MARGIN = new InjectionToken<string>(
    'rootMargin for IntersectionObserver',
    {
        providedIn: 'root',
        factory: () => INTERSECTION_ROOT_MARGIN_DEFAULT,
    },
);


================================================
FILE: projects/intersection-observer/src/tokens/intersection-root.ts
================================================
import {ElementRef, InjectionToken} from '@angular/core';

export const INTERSECTION_ROOT = new InjectionToken<ElementRef<Element>>(
    'Root element for IntersectionObserver',
);


================================================
FILE: projects/intersection-observer/src/tokens/intersection-threshold.ts
================================================
import {InjectionToken} from '@angular/core';

export const INTERSECTION_THRESHOLD_DEFAULT = 0;
export const INTERSECTION_THRESHOLD = new InjectionToken<number | number[]>(
    'threshold for IntersectionObserver',
    {
        providedIn: 'root',
        factory: () => INTERSECTION_THRESHOLD_DEFAULT,
    },
);


================================================
FILE: projects/intersection-observer/src/tokens/support.ts
================================================
import {inject, InjectionToken} from '@angular/core';
import {WINDOW} from '@ng-web-apis/common';

export const INTERSECTION_OBSERVER_SUPPORT = new InjectionToken<boolean>(
    'Intersection Observer API support',
    {
        providedIn: 'root',
        factory: () => !!(inject(WINDOW) as any).IntersectionObserver,
    },
);


================================================
FILE: projects/intersection-observer/src/tokens/tests/support.spec.ts
================================================
import {TestBed} from '@angular/core/testing';
import {INTERSECTION_OBSERVER_SUPPORT} from '../support';

describe('INTERSECTION_OBSERVER_SUPPORT', () => {
    it('true in modern browsers', () => {
        TestBed.configureTestingModule({});

        expect(TestBed.get(INTERSECTION_OBSERVER_SUPPORT)).toBe(true);
    });
});


================================================
FILE: projects/intersection-observer/src/utils/root-margin-factory.ts
================================================
import {INTERSECTION_ROOT_MARGIN_DEFAULT} from '../tokens/intersection-root-margin';

export function rootMarginFactory(rootMargin: string | null): string {
    return rootMargin || INTERSECTION_ROOT_MARGIN_DEFAULT;
}


================================================
FILE: projects/intersection-observer/src/utils/threshold-factory.ts
================================================
import {INTERSECTION_THRESHOLD_DEFAULT} from '../tokens/intersection-threshold';

export function thresholdFactory(threshold: string | null): number | number[] {
    return threshold?.split(',').map(parseFloat) || INTERSECTION_THRESHOLD_DEFAULT;
}


================================================
FILE: projects/intersection-observer/tsconfig.lib.json
================================================
{
    "extends": "../../tsconfig.json",
    "compilerOptions": {
        "outDir": "../../out-tsc/lib",
        "target": "es2015",
        "declaration": true,
        "inlineSources": true,
        "lib": ["dom", "es2018"]
    },
    "angularCompilerOptions": {
        "annotateForClosureCompiler": true,
        "skipTemplateCodegen": true,
        "strictMetadataEmit": true,
        "fullTemplateTypeCheck": true,
        "strictInjectionParameters": true,
        "enableResourceInlining": true,
        "enableIvy": true,
        "compilationMode": "partial"
    },
    "exclude": ["src/test.ts", "**/*.spec.ts"]
}


================================================
FILE: projects/intersection-observer/tsconfig.spec.json
================================================
{
    "extends": "../../tsconfig.json",
    "compilerOptions": {
        "outDir": "../../out-tsc/spec",
        "types": ["jasmine", "node"]
    },
    "files": ["src/test.ts"],
    "include": ["**/*.spec.ts", "**/*.d.ts"]
}


================================================
FILE: scripts/postbuild.js
================================================
const fs = require('fs');

const DIST_LIB_PATH = 'dist/intersection-observer/';
const README_PATH = 'README.md';
const PATH_TO_README = DIST_LIB_PATH + README_PATH;

copyExtraFiles();

function copyExtraFiles() {
    if (!fs.existsSync(README_PATH)) {
        throw new Error('Requested files do not exit');
    } else {
        copyReadmeIntoDistFolder(README_PATH, PATH_TO_README);
    }
}

function copyReadmeIntoDistFolder(srcPath, toPath) {
    const fileBody = fs.readFileSync(srcPath).toString();
    const withoutLogos = fileBody
        .replace('![ng-web-apis logo](projects/demo/src/assets/logo.svg) ', '')
        .replace('<img src="projects/demo/src/assets/web-api.svg" align="top"> ', '');

    fs.writeFileSync(toPath, withoutLogos);
}


================================================
FILE: scripts/syncVersions.js
================================================
const fs = require('fs');
const glob = require('glob');
const JSON_INDENTATION_LEVEL = 4;
const {version} = require('../package.json');

// Sync libraries package.json versions with main package.json
syncVersions('projects');

function syncVersions(root) {
    glob(root + '/**/package.json', (_, files) => {
        files.forEach(file => {
            const packageJson = JSON.parse(fs.readFileSync(file));

            fs.writeFileSync(
                file,
                JSON.stringify(
                    {
                        ...packageJson,
                        version,
                    },
                    null,
                    JSON_INDENTATION_LEVEL,
                ),
            );
        });
    });
}


================================================
FILE: tsconfig.eslint.json
================================================
{
    "extends": "./tsconfig.json",
    "compilerOptions": {
        "rootDir": ".",
        "baseUrl": ".",
        "strict": false,
        "incremental": true
    },
    "include": ["projects", "scripts"],
    "exclude": ["**/node_modules", "**/schematics/**", "**/.*/", "*.js"]
}


================================================
FILE: tsconfig.json
================================================
{
    "compileOnSave": false,
    "compilerOptions": {
        "baseUrl": "./",
        "outDir": "./dist/out-tsc",
        "sourceMap": true,
        "declaration": false,
        "module": "esnext",
        "moduleResolution": "node",
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "importHelpers": true,
        "strict": true,
        "noFallthroughCasesInSwitch": true,
        "noImplicitReturns": true,
        "noUnusedParameters": true,
        "noUnusedLocals": true,
        "target": "es5",
        "typeRoots": ["node_modules/@types"],
        "lib": ["es2018", "dom"],
        "paths": {
            "@ng-web-apis/intersection-observer": [
                "projects/intersection-observer/src/public-api"
            ]
        }
    }
}
Download .txt
gitextract_w7p2l720/

├── .editorconfig
├── .eslintrc.js
├── .github/
│   ├── CODEOWNERS
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── .husky/
│   ├── commit-msg
│   └── pre-commit
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── angular.json
├── commitlint.config.js
├── package.json
├── prettier.config.js
├── projects/
│   ├── demo/
│   │   ├── .gitignore
│   │   ├── angular.json
│   │   ├── karma.conf.js
│   │   ├── package.json
│   │   ├── server.ts
│   │   ├── src/
│   │   │   ├── app/
│   │   │   │   ├── app.browser.module.ts
│   │   │   │   ├── app.component.html
│   │   │   │   ├── app.component.less
│   │   │   │   ├── app.component.ts
│   │   │   │   ├── app.routes.ts
│   │   │   │   └── app.server.module.ts
│   │   │   ├── assets/
│   │   │   │   ├── browserconfig.xml
│   │   │   │   └── site.webmanifest
│   │   │   ├── index.html
│   │   │   ├── main.browser.ts
│   │   │   ├── main.server.ts
│   │   │   ├── polyfills.ts
│   │   │   ├── styles.css
│   │   │   └── typings.d.ts
│   │   ├── tsconfig.demo.json
│   │   ├── tsconfig.json
│   │   ├── tsconfig.server.json
│   │   └── tsconfig.spec.json
│   └── intersection-observer/
│       ├── LICENSE
│       ├── karma.conf.js
│       ├── ng-package.json
│       ├── package.json
│       ├── src/
│       │   ├── directives/
│       │   │   ├── intersection-observee.directive.ts
│       │   │   ├── intersection-observer.directive.ts
│       │   │   ├── intersection-root.directive.ts
│       │   │   └── tests/
│       │   │       └── intersection-observee.spec.ts
│       │   ├── module.ts
│       │   ├── public-api.ts
│       │   ├── services/
│       │   │   ├── intersection-observee.service.ts
│       │   │   ├── intersection-observer.service.ts
│       │   │   └── tests/
│       │   │       └── intersection-observer.service.spec.ts
│       │   ├── test.ts
│       │   ├── tokens/
│       │   │   ├── intersection-root-margin.ts
│       │   │   ├── intersection-root.ts
│       │   │   ├── intersection-threshold.ts
│       │   │   ├── support.ts
│       │   │   └── tests/
│       │   │       └── support.spec.ts
│       │   └── utils/
│       │       ├── root-margin-factory.ts
│       │       └── threshold-factory.ts
│       ├── tsconfig.lib.json
│       └── tsconfig.spec.json
├── scripts/
│   ├── postbuild.js
│   └── syncVersions.js
├── tsconfig.eslint.json
└── tsconfig.json
Download .txt
SYMBOL INDEX (37 symbols across 20 files)

FILE: projects/demo/server.ts
  function app (line 14) | function app(): express.Express {
  function run (line 57) | function run(): void {

FILE: projects/demo/src/app/app.browser.module.ts
  class AppBrowserModule (line 35) | class AppBrowserModule {}

FILE: projects/demo/src/app/app.component.ts
  class AppComponent (line 10) | class AppComponent {
    method constructor (line 13) | constructor(@Inject(INTERSECTION_OBSERVER_SUPPORT) readonly support: b...
    method onIntersection (line 15) | onIntersection(intersections: IntersectionObserverEntry[]) {

FILE: projects/demo/src/app/app.routes.ts
  class AppRoutingModule (line 16) | class AppRoutingModule {}

FILE: projects/demo/src/app/app.server.module.ts
  class AppServerModule (line 11) | class AppServerModule {}

FILE: projects/intersection-observer/src/directives/intersection-observee.directive.ts
  class IntersectionObserveeDirective (line 11) | class IntersectionObserveeDirective {
    method constructor (line 12) | constructor(

FILE: projects/intersection-observer/src/directives/intersection-observer.directive.ts
  class IntersectionObserverDirective (line 17) | class IntersectionObserverDirective extends IntersectionObserver
    method constructor (line 21) | constructor(
    method observe (line 42) | observe(target: Element, callback: IntersectionObserverCallback = () =...
    method unobserve (line 47) | unobserve(target: Element) {
    method ngOnDestroy (line 52) | ngOnDestroy() {

FILE: projects/intersection-observer/src/directives/intersection-root.directive.ts
  class IntersectionRootDirective (line 13) | class IntersectionRootDirective {}

FILE: projects/intersection-observer/src/directives/tests/intersection-observee.spec.ts
  class TestComponent (line 9) | @Component({

FILE: projects/intersection-observer/src/module.ts
  class IntersectionObserverModule (line 18) | class IntersectionObserverModule {}

FILE: projects/intersection-observer/src/services/intersection-observee.service.ts
  class IntersectionObserveeService (line 7) | class IntersectionObserveeService extends Observable<IntersectionObserve...
    method constructor (line 8) | constructor(

FILE: projects/intersection-observer/src/services/intersection-observer.service.ts
  class IntersectionObserverService (line 10) | class IntersectionObserverService extends Observable<IntersectionObserve...
    method constructor (line 11) | constructor(

FILE: projects/intersection-observer/src/tokens/intersection-root-margin.ts
  constant INTERSECTION_ROOT_MARGIN_DEFAULT (line 3) | const INTERSECTION_ROOT_MARGIN_DEFAULT = '0px 0px 0px 0px';
  constant INTERSECTION_ROOT_MARGIN (line 4) | const INTERSECTION_ROOT_MARGIN = new InjectionToken<string>(

FILE: projects/intersection-observer/src/tokens/intersection-root.ts
  constant INTERSECTION_ROOT (line 3) | const INTERSECTION_ROOT = new InjectionToken<ElementRef<Element>>(

FILE: projects/intersection-observer/src/tokens/intersection-threshold.ts
  constant INTERSECTION_THRESHOLD_DEFAULT (line 3) | const INTERSECTION_THRESHOLD_DEFAULT = 0;
  constant INTERSECTION_THRESHOLD (line 4) | const INTERSECTION_THRESHOLD = new InjectionToken<number | number[]>(

FILE: projects/intersection-observer/src/tokens/support.ts
  constant INTERSECTION_OBSERVER_SUPPORT (line 4) | const INTERSECTION_OBSERVER_SUPPORT = new InjectionToken<boolean>(

FILE: projects/intersection-observer/src/utils/root-margin-factory.ts
  function rootMarginFactory (line 3) | function rootMarginFactory(rootMargin: string | null): string {

FILE: projects/intersection-observer/src/utils/threshold-factory.ts
  function thresholdFactory (line 3) | function thresholdFactory(threshold: string | null): number | number[] {

FILE: scripts/postbuild.js
  constant DIST_LIB_PATH (line 3) | const DIST_LIB_PATH = 'dist/intersection-observer/';
  constant README_PATH (line 4) | const README_PATH = 'README.md';
  constant PATH_TO_README (line 5) | const PATH_TO_README = DIST_LIB_PATH + README_PATH;
  function copyExtraFiles (line 9) | function copyExtraFiles() {
  function copyReadmeIntoDistFolder (line 17) | function copyReadmeIntoDistFolder(srcPath, toPath) {

FILE: scripts/syncVersions.js
  constant JSON_INDENTATION_LEVEL (line 3) | const JSON_INDENTATION_LEVEL = 4;
  function syncVersions (line 9) | function syncVersions(root) {
Condensed preview — 70 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (81K chars).
[
  {
    "path": ".editorconfig",
    "chars": 245,
    "preview": "# Editor configuration, see https://editorconfig.org\nroot = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size ="
  },
  {
    "path": ".eslintrc.js",
    "chars": 2226,
    "preview": "/**\n * @type {import('eslint').Linter.Config}\n */\nmodule.exports = {\n    root: true,\n    extends: [\n        // TODO: war"
  },
  {
    "path": ".github/CODEOWNERS",
    "chars": 675,
    "preview": "\n# ==================================================================================\n# ================================"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 99,
    "preview": "# These are supported funding model platforms\n\nopen_collective: ng-web-apis\nissuehunt: ng-web-apis\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 608,
    "preview": "---\nname: 🐞 Bug report\nabout: Create a report to help us improve\ntitle: '[BUG] '\nlabels: ''\nassignee: waterplea\n---\n\n# 🐞"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 718,
    "preview": "---\nname: 🚀 Feature request\nabout: Suggest an idea for this project\ntitle: '[FEATURE]'\nlabels: ''\nassignee: waterplea\n--"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 983,
    "preview": "## PR Checklist\n\nPlease check if your PR fulfills the following requirements:\n\n-   [ ] The commit message follows [Conve"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 826,
    "preview": "name: Web APIs CI\n\non: push\n\njobs:\n  ci:\n    # Setup part\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/chec"
  },
  {
    "path": ".gitignore",
    "chars": 679,
    "preview": "# compiled schematics\nschematics/library-starter/*.js\nschematics/library-starter/*.js.map\nschematics/library-starter/*.d"
  },
  {
    "path": ".husky/commit-msg",
    "chars": 95,
    "preview": "#!/bin/sh\n# shellcheck disable=SC1090\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nnpx commitlint --edit $1\n"
  },
  {
    "path": ".husky/pre-commit",
    "chars": 104,
    "preview": "#!/bin/sh\n# shellcheck disable=SC1090\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nnpx lint-staged\nnpm run typecheck\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 2442,
    "preview": "# Changelog\n\nAll notable changes to this project will be documented in this file. See\n[standard-version](https://github."
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 3377,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 1242,
    "preview": "# Contributing\n\n> Thank you for considering contributing to our project. Your help if very welcome!\n\nWhen contributing, "
  },
  {
    "path": "LICENSE",
    "chars": 1093,
    "preview": "MIT License\n\nCopyright (c) 2020 Alexander Inkin <alexander@inkin.ru>\n\nPermission is hereby granted, free of charge, to a"
  },
  {
    "path": "README.md",
    "chars": 8020,
    "preview": "___\n___\n**Attention!** This repository is archived and the library has been moved to [tinkoff/ng-web-apis](https://githu"
  },
  {
    "path": "angular.json",
    "chars": 7168,
    "preview": "{\n    \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n    \"version\": 1,\n    \"newProjectRoot\": \"projects"
  },
  {
    "path": "commitlint.config.js",
    "chars": 65,
    "preview": "module.exports = {extends: ['@commitlint/config-conventional']};\n"
  },
  {
    "path": "package.json",
    "chars": 3994,
    "preview": "{\n    \"name\": \"@ng-web-apis/intersection-observer\",\n    \"version\": \"3.0.1\",\n    \"description\": \"A library for declarativ"
  },
  {
    "path": "prettier.config.js",
    "chars": 409,
    "preview": "const base = require('@tinkoff/prettier-config/angular');\n\nmodule.exports = {\n    ...base,\n    singleAttributePerLine: t"
  },
  {
    "path": "projects/demo/.gitignore",
    "chars": 554,
    "preview": "# compiled output\n/dist\n/tmp\n/out-tsc\n# Only exists if Bazel was run\n/bazel-out\n\n# dependencies\n/node_modules\n\n# profili"
  },
  {
    "path": "projects/demo/angular.json",
    "chars": 2322,
    "preview": "{\n    \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n    \"version\": 1,\n    \"newProjectRoot\": \"projects"
  },
  {
    "path": "projects/demo/karma.conf.js",
    "chars": 1419,
    "preview": "// Karma configuration file, see link for more information\n// https://karma-runner.github.io/1.0/config/configuration-fi"
  },
  {
    "path": "projects/demo/package.json",
    "chars": 1086,
    "preview": "{\n    \"name\": \"demo\",\n    \"version\": \"3.0.1\",\n    \"private\": true,\n    \"scripts\": {\n        \"ng\": \"ng\",\n        \"start\":"
  },
  {
    "path": "projects/demo/server.ts",
    "chars": 2360,
    "preview": "import '@ng-web-apis/universal/mocks';\nimport 'zone.js/node';\n\nimport {APP_BASE_HREF} from '@angular/common';\nimport {pr"
  },
  {
    "path": "projects/demo/src/app/app.browser.module.ts",
    "chars": 946,
    "preview": "import {\n    APP_BASE_HREF,\n    CommonModule,\n    LocationStrategy,\n    PathLocationStrategy,\n} from '@angular/common';\n"
  },
  {
    "path": "projects/demo/src/app/app.component.html",
    "chars": 449,
    "preview": "<section\n    *ngIf=\"support; else not\"\n    class=\"wrapper\"\n    waIntersectionRoot\n    waIntersectionObserver\n    waInter"
  },
  {
    "path": "projects/demo/src/app/app.component.less",
    "chars": 1230,
    "preview": ":host {\n    perspective: 150vw;\n    user-select: none;\n    flex-direction: column;\n    align-items: center;\n}\n\n.wrapper "
  },
  {
    "path": "projects/demo/src/app/app.component.ts",
    "chars": 607,
    "preview": "import {ChangeDetectionStrategy, Component, Inject} from '@angular/core';\nimport {INTERSECTION_OBSERVER_SUPPORT} from '@"
  },
  {
    "path": "projects/demo/src/app/app.routes.ts",
    "chars": 355,
    "preview": "import {NgModule} from '@angular/core';\nimport {RouterModule} from '@angular/router';\nimport {AppComponent} from './app."
  },
  {
    "path": "projects/demo/src/app/app.server.module.ts",
    "chars": 323,
    "preview": "import {NgModule} from '@angular/core';\nimport {ServerModule} from '@angular/platform-server';\n\nimport {AppBrowserModule"
  },
  {
    "path": "projects/demo/src/assets/browserconfig.xml",
    "chars": 275,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<browserconfig>\n    <msapplication>\n        <tile>\n            <square150x150logo"
  },
  {
    "path": "projects/demo/src/assets/site.webmanifest",
    "chars": 484,
    "preview": "{\n    \"name\": \"\",\n    \"short_name\": \"\",\n    \"icons\": [\n        {\n            \"src\": \"/intersection-observer/assets/andro"
  },
  {
    "path": "projects/demo/src/index.html",
    "chars": 2496,
    "preview": "<html>\n    <head>\n        <title>Intersection Observer API for Angular</title>\n        <link\n            rel=\"apple-touc"
  },
  {
    "path": "projects/demo/src/main.browser.ts",
    "chars": 519,
    "preview": "import './polyfills';\n\nimport {platformBrowserDynamic} from '@angular/platform-browser-dynamic';\nimport {AppBrowserModul"
  },
  {
    "path": "projects/demo/src/main.server.ts",
    "chars": 57,
    "preview": "export {AppServerModule} from './app/app.server.module';\n"
  },
  {
    "path": "projects/demo/src/polyfills.ts",
    "chars": 50,
    "preview": "import 'intersection-observer';\nimport 'zone.js';\n"
  },
  {
    "path": "projects/demo/src/styles.css",
    "chars": 733,
    "preview": "/* Base demo styles */\nbody,\nhtml {\n    display: flex;\n    flex-direction: column;\n    margin: 0;\n    height: 100%;\n    "
  },
  {
    "path": "projects/demo/src/typings.d.ts",
    "chars": 20,
    "preview": "declare module '*';\n"
  },
  {
    "path": "projects/demo/tsconfig.demo.json",
    "chars": 150,
    "preview": "{\n    \"extends\": \"./tsconfig.json\",\n    \"compilerOptions\": {\n        \"outDir\": \"../out-tsc/app\",\n        \"typeRoots\": []"
  },
  {
    "path": "projects/demo/tsconfig.json",
    "chars": 41,
    "preview": "{\n    \"extends\": \"../../tsconfig.json\"\n}\n"
  },
  {
    "path": "projects/demo/tsconfig.server.json",
    "chars": 204,
    "preview": "{\n    \"extends\": \"./tsconfig.json\",\n    \"compilerOptions\": {\n        \"module\": \"commonjs\"\n    },\n    \"angularCompilerOpt"
  },
  {
    "path": "projects/demo/tsconfig.spec.json",
    "chars": 222,
    "preview": "{\n    \"extends\": \"./tsconfig.json\",\n    \"compilerOptions\": {\n        \"outDir\": \"../../out-tsc/spec\",\n        \"types\": [\""
  },
  {
    "path": "projects/intersection-observer/LICENSE",
    "chars": 1093,
    "preview": "MIT License\n\nCopyright (c) 2020 Alexander Inkin <alexander@inkin.ru>\n\nPermission is hereby granted, free of charge, to a"
  },
  {
    "path": "projects/intersection-observer/karma.conf.js",
    "chars": 1482,
    "preview": "// Karma configuration file, see link for more information\n// https://karma-runner.github.io/1.0/config/configuration-fi"
  },
  {
    "path": "projects/intersection-observer/ng-package.json",
    "chars": 183,
    "preview": "{\n    \"$schema\": \"../../node_modules/ng-packagr/ng-package.schema.json\",\n    \"dest\": \"../../dist/intersection-observer\","
  },
  {
    "path": "projects/intersection-observer/package.json",
    "chars": 800,
    "preview": "{\n    \"name\": \"@ng-web-apis/intersection-observer\",\n    \"version\": \"3.0.1\",\n    \"description\": \"A library for declarativ"
  },
  {
    "path": "projects/intersection-observer/src/directives/intersection-observee.directive.ts",
    "chars": 516,
    "preview": "import {Directive, Inject} from '@angular/core';\nimport {Observable} from 'rxjs';\n\nimport {IntersectionObserveeService} "
  },
  {
    "path": "projects/intersection-observer/src/directives/intersection-observer.directive.ts",
    "chars": 1684,
    "preview": "import {\n    Attribute,\n    Directive,\n    ElementRef,\n    Inject,\n    OnDestroy,\n    Optional,\n} from '@angular/core';\n"
  },
  {
    "path": "projects/intersection-observer/src/directives/intersection-root.directive.ts",
    "chars": 335,
    "preview": "import {Directive, ElementRef} from '@angular/core';\nimport {INTERSECTION_ROOT} from '../tokens/intersection-root';\n\n@Di"
  },
  {
    "path": "projects/intersection-observer/src/directives/tests/intersection-observee.spec.ts",
    "chars": 3161,
    "preview": "import {Component, ViewChild} from '@angular/core';\nimport {ComponentFixture, TestBed} from '@angular/core/testing';\nimp"
  },
  {
    "path": "projects/intersection-observer/src/module.ts",
    "chars": 642,
    "preview": "import {NgModule} from '@angular/core';\nimport {IntersectionObserveeDirective} from './directives/intersection-observee."
  },
  {
    "path": "projects/intersection-observer/src/public-api.ts",
    "chars": 633,
    "preview": "/**\n * Public API Surface of @ng-web-apis/intersection-observer\n */\n\n/* Directives */\nexport * from './directives/inters"
  },
  {
    "path": "projects/intersection-observer/src/services/intersection-observee.service.ts",
    "chars": 814,
    "preview": "import {ElementRef, Inject, Injectable} from '@angular/core';\nimport {Observable} from 'rxjs';\nimport {share} from 'rxjs"
  },
  {
    "path": "projects/intersection-observer/src/services/intersection-observer.service.ts",
    "chars": 1587,
    "preview": "import {ElementRef, Inject, Injectable, Optional} from '@angular/core';\nimport {Observable} from 'rxjs';\nimport {share} "
  },
  {
    "path": "projects/intersection-observer/src/services/tests/intersection-observer.service.spec.ts",
    "chars": 1306,
    "preview": "import {take} from 'rxjs/operators';\nimport {IntersectionObserverService} from '../intersection-observer.service';\n\ndesc"
  },
  {
    "path": "projects/intersection-observer/src/test.ts",
    "chars": 660,
    "preview": "// This file is required by karma.conf.js and loads recursively all the .spec and framework files\nimport 'zone.js';\nimpo"
  },
  {
    "path": "projects/intersection-observer/src/tokens/intersection-root-margin.ts",
    "chars": 326,
    "preview": "import {InjectionToken} from '@angular/core';\n\nexport const INTERSECTION_ROOT_MARGIN_DEFAULT = '0px 0px 0px 0px';\nexport"
  },
  {
    "path": "projects/intersection-observer/src/tokens/intersection-root.ts",
    "chars": 181,
    "preview": "import {ElementRef, InjectionToken} from '@angular/core';\n\nexport const INTERSECTION_ROOT = new InjectionToken<ElementRe"
  },
  {
    "path": "projects/intersection-observer/src/tokens/intersection-threshold.ts",
    "chars": 314,
    "preview": "import {InjectionToken} from '@angular/core';\n\nexport const INTERSECTION_THRESHOLD_DEFAULT = 0;\nexport const INTERSECTIO"
  },
  {
    "path": "projects/intersection-observer/src/tokens/support.ts",
    "chars": 329,
    "preview": "import {inject, InjectionToken} from '@angular/core';\nimport {WINDOW} from '@ng-web-apis/common';\n\nexport const INTERSEC"
  },
  {
    "path": "projects/intersection-observer/src/tokens/tests/support.spec.ts",
    "chars": 326,
    "preview": "import {TestBed} from '@angular/core/testing';\nimport {INTERSECTION_OBSERVER_SUPPORT} from '../support';\n\ndescribe('INTE"
  },
  {
    "path": "projects/intersection-observer/src/utils/root-margin-factory.ts",
    "chars": 218,
    "preview": "import {INTERSECTION_ROOT_MARGIN_DEFAULT} from '../tokens/intersection-root-margin';\n\nexport function rootMarginFactory("
  },
  {
    "path": "projects/intersection-observer/src/utils/threshold-factory.ts",
    "chars": 248,
    "preview": "import {INTERSECTION_THRESHOLD_DEFAULT} from '../tokens/intersection-threshold';\n\nexport function thresholdFactory(thres"
  },
  {
    "path": "projects/intersection-observer/tsconfig.lib.json",
    "chars": 623,
    "preview": "{\n    \"extends\": \"../../tsconfig.json\",\n    \"compilerOptions\": {\n        \"outDir\": \"../../out-tsc/lib\",\n        \"target\""
  },
  {
    "path": "projects/intersection-observer/tsconfig.spec.json",
    "chars": 226,
    "preview": "{\n    \"extends\": \"../../tsconfig.json\",\n    \"compilerOptions\": {\n        \"outDir\": \"../../out-tsc/spec\",\n        \"types\""
  },
  {
    "path": "scripts/postbuild.js",
    "chars": 752,
    "preview": "const fs = require('fs');\n\nconst DIST_LIB_PATH = 'dist/intersection-observer/';\nconst README_PATH = 'README.md';\nconst P"
  },
  {
    "path": "scripts/syncVersions.js",
    "chars": 737,
    "preview": "const fs = require('fs');\nconst glob = require('glob');\nconst JSON_INDENTATION_LEVEL = 4;\nconst {version} = require('../"
  },
  {
    "path": "tsconfig.eslint.json",
    "chars": 284,
    "preview": "{\n    \"extends\": \"./tsconfig.json\",\n    \"compilerOptions\": {\n        \"rootDir\": \".\",\n        \"baseUrl\": \".\",\n        \"st"
  },
  {
    "path": "tsconfig.json",
    "chars": 790,
    "preview": "{\n    \"compileOnSave\": false,\n    \"compilerOptions\": {\n        \"baseUrl\": \"./\",\n        \"outDir\": \"./dist/out-tsc\",\n    "
  }
]

About this extraction

This page contains the full source code of the ng-web-apis/intersection-observer GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 70 files (70.5 KB), approximately 17.8k tokens, and a symbol index with 37 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!