master eee119fe3a69 cached
31 files
56.4 KB
16.4k tokens
41 symbols
1 requests
Download .txt
Repository: NathanWalker/ng2-image-lazy-load
Branch: master
Commit: eee119fe3a69
Files: 31
Total size: 56.4 KB

Directory structure:
gitextract_n04mij4e/

├── .editorconfig
├── .gitignore
├── .npmignore
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── index.d.ts
├── karma-shim.js
├── karma.conf.js
├── make.js
├── ng2-image-lazy-load.ts
├── package.json
├── protractor.conf.js
├── src/
│   ├── app/
│   │   ├── app.component.html
│   │   ├── app.component.scss
│   │   ├── app.component.ts
│   │   ├── app.module.ts
│   │   ├── directives/
│   │   │   └── image-lazy-load.directive.ts
│   │   └── services/
│   │       ├── image-lazy-load.service.ts
│   │       ├── web-worker.service.stub.ts
│   │       └── web-worker.service.ts
│   ├── main.ts
│   ├── polyfills.ts
│   ├── public/
│   │   ├── index.html
│   │   └── xhrWorker.js
│   ├── style/
│   │   └── app.scss
│   └── vendor.ts
├── tsconfig.json
├── tslint.json
├── typedoc.json
└── webpack.config.js

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

================================================
FILE: .editorconfig
================================================
# EditorConfig is awesome: http://EditorConfig.org

# top-most EditorConfig file
root = true

# Unix-style newlines with a newline ending every file
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

================================================
FILE: .gitignore
================================================
# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Users Environment Variables
.lock-wscript

# OS generated files #
.DS_Store
ehthumbs.db
Icon?
Thumbs.db

# Node Files #
/node_modules
/bower_components

# Coverage #
/coverage/

# Typing #
/src/typings/tsd/
/typings/
/tsd_typings/

# Dist #
/dist
/public/__build__/
/src/*/__build__/
__build__/**
.webpack.json

# Doc #
/doc/

# IDE #
.idea/
*.swp

*.js
*.js.map
*.d.ts
!src/public/xhrWorker.js
!index.d.ts
!*.e2e.js
!make.js
!karma*.js
!protractor.conf.js
!webpack.config.js


================================================
FILE: .npmignore
================================================
# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directory
# Commenting this out is preferred by some people, see
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
node_modules

# Users Environment Variables
.lock-wscript
.tsdrc

#IntelliJ configuration files
.idea

dist
dev
docs
lib
test

Thumbs.db
.DS_Store
*.yml
*.ts
!*.d.ts
src/app/app.*
src/public
src/shims
src/app.scss
src/main.ts
src/polyfills.ts
src/vendor.ts
src/*.d.ts
src/*.js
src/*.map
src/style
*.spec.*
*.e2e.*
CONTRIBUTING.md
karma-shim.js
karma.conf.js
make.js
protractor.conf.js
tsconfig.json
tslint.json
typedoc.json
typings.json
typings
webpack.config.js
.travis.yml
.jshintrc
.editorconfig


================================================
FILE: CONTRIBUTING.md
================================================
## Submitting Pull Requests

**Please follow these basic steps to simplify pull request reviews - if you don't you'll probably just be asked to anyway.**

* Please rebase your branch against the current master
* Please ensure that the test suite passes **and** that code is lint free before submitting a PR by running:
 * ```npm test```
* If you've added new functionality, **please** include tests which validate its behaviour
* Make reference to possible [issues](https://github.com/preboot/angular2-library-seed/issues) on PR comment

## Submitting bug reports

* Please detail the affected browser(s) and operating system(s)
* Please be sure to state which version of node **and** npm you're using

## How to get setup and run the code as well as test

**Note** To run the demo, you must have node v4.x.x or higher and npm 3.x.x.

```bash
git clone https://github.com/preboot/angular2-library-seed.git
cd angular2-library-seed
npm install   # or `npm run reinstall` if you get an error
npm start     # start with --env dev
```

# Running tests

```bash
npm test
```


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2016 Preboot team

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
================================================
[![Dependency Status](https://david-dm.org/NathanWalker/ng2-image-lazy-load.svg)](https://david-dm.org/NathanWalker/ng2-image-lazy-load)
[![devDependency Status](https://david-dm.org/NathanWalker/ng2-image-lazy-load/dev-status.svg)](https://david-dm.org/NathanWalker/ng2-image-lazy-load#info=devDependencies)

# Not currently maintained

You might try using [this great lazy load lib for now](https://github.com/tjoskar/ng-lazyload-image).
The only thing the above lib doesn't have is the Web Worker support - I hope to circle back and update this lib at some point in future or contribute worker support to tjoskar's lib.

# ng2-image-lazy-load

Demo: https://ng2-image-lazy-load-demo.herokuapp.com

## Installation
```sh
npm i ng2-image-lazy-load --save
```

## Example implementation

This library utilizes `WebWorkers` (https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) for background loading of images.

By default, the location of the worker file is `assets/js/xhrWorker.js`. You can copy this [xhrWorker.js](https://github.com/NathanWalker/ng2-image-lazy-load/blob/master/src/public/xhrWorker.js) file for your own use from this repo or you can create your own.

To set a custom path to load your worker file relative to your web server root:
```
WebWorkerService.workerUrl = 'path/to/your/custom_worker.js'
```
The example below will help illustrate this.

Also, ensure you've loaded the angular/http bundle as well as this library falls back to using `Http` wherever `Worker` is not supported.

```ts
import {BrowserModule} from "@angular/platform-browser";
import {NgModule, Component} from '@angular/core';
import {HttpModule} from '@angular/http';
import {ImageLazyLoadModule, WebWorkerService} from 'ng2-image-lazy-load';

// default: 'assets/js/xhrWorker.js'
WebWorkerService.workerUrl = 'path/to/your/xhrWorker.js';

// default: true
// set to false if you want to force Http instead of WebWorker
WebWorkerService.enabled = true;

@Component({
    selector: 'app',
    template: `
      <div imageLazyLoadArea>
        <div *ngFor="let image of images">
          <img [imageLazyLoadItem]="image.url"/>
        </div>
      </div>
    `
})
export class AppComponent {
    public images: Array<any> = [
      {
        name:`image 1`,
        url:`image.jpg`
      },
      {
        name:`image 2`,
        url:`image_2.jpg`
      }
    ];
}

@NgModule({
    imports: [
        BrowserModule,
        HttpModule,
        ImageLazyLoadModule
    ],
    declarations: [AppComponent],
    bootstrap: [AppComponent]
})
export class AppModule {
}
```

### Configuration

You can configure custom headers as well as custom loading, loaded and error classes by using the `imageLazyLoadConfig` directive:

```
// view template
<div imageLazyLoadArea [imageLazyLoadConfig]="lazyLoadConfig">
  <div *ngFor="let image of images">
    <img [imageLazyLoadItem]="image.url"/>
  </div>
</div>

// Component
public lazyLoadConfig: IImageLazyLoadConfig = {
  headers: {
    'Authorization': 'Bearer auth-token'
  },
  loadingClass: 'custom-loading',
  loadedClass: 'custom-loaded',
  errorClass: 'custom-error'
};
```

## API
### ImageLazyLoaderService
#### Properties:
- `imageCache:any`: Object where the key is the url of the image the library has already loaded and doesn't need to be loaded again. i.e., {'http://domain.com/image.png':true}

#### Methods:
- `load(url: string, headers?: any): Promise<any>`: Load url with optional custom headers
- `loadViaWorker(url: string, headers?: any): Promise<any>`: Use a webworker directly to load url with optional custom headers
- `loadViaHttp(url: string, headers?: any): Promise<any>`: Use the `Http` service directly to load url with optional custom headers

### WebWorkerService
##### This is a helper service used by the library that wraps the usage of the browser's `Worker` api, however you can use it directly if you'd like to interact with it.
#### Properties:
- `static supported: boolean`: Determine if workers are supported
- `static workerUrl: string`: Used to set the path to a worker file. Defaults to 'assets/js/xhrWorker.js'
- `activeWorkers: Array<any>`: At any given moment, this can be checked to see how many workers are currently activated

#### Methods:
- `load(config: any, msgFn: any, errorFn?: any):number`: Load a configuration with your worker and wire it to a `message` function and/or an `error` function. Returns an `id` which can be used to terminate the worker.
- `terminate(id: number)`: Terminate the worker


# How to contribute

See [CONTRIBUTING](https://github.com/NathanWalker/ng2-image-lazy-load/blob/master/CONTRIBUTING.md)

# Big Thank You

This library was made possible with help from this article by [Olivier Combe](https://github.com/ocombe):
https://medium.com/@OCombe/how-to-publish-a-library-for-angular-2-on-npm-5f48cdabf435

Also, this project setup is based on the excellent [angular2-seed](https://github.com/mgechev/angular2-seed) by [Minko Gechev](https://github.com/mgechev).

# License

MIT


================================================
FILE: index.d.ts
================================================
import { ModuleWithProviders } from "@angular/core";
export * from './src/app/services/image-lazy-load.service';
export * from './src/app/services/web-worker.service';
export * from './src/app/directives/image-lazy-load.directive';
export declare class ImageLazyLoadModule {
    static forRoot(configuredProviders: Array<any>): ModuleWithProviders;
}


================================================
FILE: karma-shim.js
================================================
Error.stackTraceLimit = Infinity;

require('core-js/client/shim');
require('reflect-metadata');

require('ts-helpers');

require('zone.js/dist/zone');
require('zone.js/dist/long-stack-trace-zone');
require('zone.js/dist/proxy');
require('zone.js/dist/sync-test');
require('zone.js/dist/jasmine-patch');
require('zone.js/dist/async-test');
require('zone.js/dist/fake-async-test');

/*
 Ok, this is kinda crazy. We can use the the context method on
 require that webpack created in order to tell webpack
 what files we actually want to require or import.
 Below, context will be a function/object with file names as keys.
 using that regex we are saying look in client/app and find
 any file that ends with '.spec.ts' and get its path. By passing in true
 we say do this recursively
 */
var appContext = require.context('./src', true, /\.spec\.ts/);

// get all the files, for each file, call the context function
// that will require the file and load it up here. Context will
// loop and require those spec files here
appContext.keys().forEach(appContext);

// Select BrowserDomAdapter.
// see https://github.com/AngularClass/angular2-webpack-starter/issues/124
// Somewhere in the test setup
var testing = require('@angular/core/testing');
var browser = require('@angular/platform-browser-dynamic/testing');

testing.TestBed.initTestEnvironment(browser.BrowserDynamicTestingModule, browser.platformBrowserDynamicTesting());


================================================
FILE: karma.conf.js
================================================
var path = require('path');

var webpackConfig = require('./webpack.config');

var ENV = process.env.npm_lifecycle_event;
var isTestWatch = ENV === 'test-watch';

module.exports = function (config) {
  var _config = {

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
      { pattern: './karma-shim.js', watched: false }
    ],

    // list of files to exclude
    exclude: [],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
      './karma-shim.js': ['webpack', 'sourcemap']
    },

    webpack: webpackConfig,

    webpackMiddleware: {
      // webpack-dev-middleware configuration
      // i. e.
      stats: 'errors-only'
    },

    webpackServer: {
      noInfo: true // please don't spam the console when running in karma!
    },

    // test results reporter to use
    // possible values: 'dots', 'progress', 'mocha'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ["mocha"],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: false,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: isTestWatch ? ['Chrome'] : ['PhantomJS'], 

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: true
  };

  if (!isTestWatch) {
    _config.reporters.push("coverage");

    _config.coverageReporter = {
      dir: 'coverage/',
      reporters: [{
        type: 'json',
        dir: 'coverage',
        subdir: 'json',
        file: 'coverage-final.json'
      }]
    };
  }

  config.set(_config);

};


================================================
FILE: make.js
================================================
var pkg     = require('./package.json');
var path    = require('path');
var Builder = require('systemjs-builder');
var name    = pkg.name;

var builder = new Builder();
var config = {
  baseURL: '.',
  transpiler: 'typescript',
  typescriptOptions: {
    module: 'cjs'
  },
  map: {
    typescript: './node_modules/typescript/lib/typescript.js',
    '@angular': './node_modules/@angular',
    rxjs: './node_modules/rxjs'
  },
  paths: {
    '*': '*.js'
  },
  meta: {
    './node_modules/@angular/*': { build: false },
    './node_modules/rxjs/*': { build: false }
  }
};

builder.config(config);

builder
.bundle(name, path.resolve(__dirname, 'bundles/', name + '.js'))
.then(function() {
  console.log('Build complete.');
})
.catch(function(err) {
  console.log('Error', err);
});


================================================
FILE: ng2-image-lazy-load.ts
================================================
import {NgModule, ModuleWithProviders} from "@angular/core";
import {IMAGELAZYLOAD_DIRECTIVES} from './src/app/directives/image-lazy-load.directive';
import {ImageLazyLoaderService} from './src/app/services/image-lazy-load.service';
import {WebWorkerService} from './src/app/services/web-worker.service';

// for manual imports
export * from './src/app/services/image-lazy-load.service';
export * from './src/app/services/web-worker.service';
export * from './src/app/directives/image-lazy-load.directive';

@NgModule({
  declarations: [
    IMAGELAZYLOAD_DIRECTIVES
  ],
  providers: [
    ImageLazyLoaderService,
    WebWorkerService
  ],
  exports: [
    IMAGELAZYLOAD_DIRECTIVES
  ]
})
export class ImageLazyLoadModule {

  static forRoot(configuredProviders: Array<any>): ModuleWithProviders {
    return {
      ngModule: ImageLazyLoadModule,
      providers: configuredProviders
    };
  }
}


================================================
FILE: package.json
================================================
{
  "name": "ng2-image-lazy-load",
  "version": "2.0.9",
  "description": "Angular2 image lazy loader library",
  "repository": {
    "url": "https://github.com/NathanWalker/ng2-image-lazy-load"
  },
  "main": "ng2-image-lazy-load.js",
  "typings": "./index.d.ts",
  "author": "Nathan Walker <NathanWalker>",
  "license": "MIT",
  "scripts": {
    "clean": "rimraf node_modules doc dist && npm cache clean",
    "clean-install": "npm run clean && npm install",
    "clean-start": "npm run clean-install && npm start",
    "watch": "webpack --watch --progress --profile",
    "build": "rimraf dist && webpack --progress --profile --bail",
    "server": "webpack-dashboard -- webpack-dev-server --inline --port 8080",
    "webdriver-update": "webdriver-manager update",
    "webdriver-start": "webdriver-manager start",
    "lint": "tslint --force \"src/**/*.ts\" --exclude \"src/**/*.d.ts\"",
    "e2e": "protractor",
    "e2e-live": "protractor --elementExplorer",
    "pretest": "npm run lint",
    "test": "karma start",
    "posttest": "remap-istanbul -i coverage/json/coverage-final.json -o coverage/html -t html",
    "test-watch": "karma start --no-single-run --auto-watch",
    "ci": "npm run e2e && npm run test",
    "docs": "typedoc --options typedoc.json src/app/app.component.ts",
    "start": "npm run server",
    "start:hmr": "npm run server -- --hot",
    "postinstall": "npm run webdriver-update"
  },
  "dependencies": {
    "@angular/common": "2.0.1",
    "@angular/compiler": "2.0.1",
    "@angular/core": "2.0.1",
    "@angular/forms": "2.0.1",
    "@angular/http": "2.0.1",
    "@angular/platform-browser": "2.0.1",
    "@angular/platform-browser-dynamic": "2.0.1",
    "@angular/router": "3.0.1",
    "core-js": "^2.4.1",
    "reflect-metadata": "^0.1.3",
    "rxjs": "5.0.0-beta.12",
    "zone.js": "^0.6.21"
  },
  "devDependencies": {
    "@angularclass/hmr": "^1.0.1",
    "@angularclass/hmr-loader": "^3.0.2",
    "@types/core-js": "^0.9.0",
    "@types/jasmine": "^2.2.29",
    "@types/node": "^6.0.38",
    "@types/protractor": "^1.5.16",
    "@types/selenium-webdriver": "2.44.26",
    "angular2-template-loader": "^0.4.0",
    "autoprefixer": "^6.3.2",
    "awesome-typescript-loader": "^2.2.4",
    "codelyzer": "0.0.26",
    "copy-webpack-plugin": "^3.0.0",
    "css-loader": "^0.25.0",
    "extract-text-webpack-plugin": "^2.0.0-beta.4",
    "file-loader": "^0.9.0",
    "html-loader": "^0.4.0",
    "html-webpack-plugin": "^2.8.1",
    "istanbul-instrumenter-loader": "^0.2.0",
    "jasmine-core": "^2.3.4",
    "jasmine-spec-reporter": "^2.4.0",
    "json-loader": "^0.5.3",
    "karma": "1.3.0",
    "karma-chrome-launcher": "^2.0.0",
    "karma-coverage": "^1.0.0",
    "karma-jasmine": "^1.0.2",
    "karma-mocha-reporter": "^2.0.3",
    "karma-phantomjs-launcher": "^1.0.0",
    "karma-remap-istanbul": "0.2.1",
    "karma-sourcemap-loader": "^0.3.7",
    "karma-webpack": "2.0.1",
    "node-sass": "^3.4.2",
    "null-loader": "0.1.1",
    "phantomjs-prebuilt": "^2.1.4",
    "postcss-loader": "^0.13.0",
    "protractor": "^3.1.1",
    "raw-loader": "0.5.1",
    "remap-istanbul": "^0.6.4",
    "rimraf": "^2.5.1",
    "sass-loader": "^4.0.0",
    "shelljs": "^0.7.0",
    "style-loader": "^0.13.0",
    "ts-helpers": "^1.1.1",
    "tslint": "^3.4.0",
    "tslint-loader": "^2.1.0",
    "typedoc": "^0.4.4",
    "typescript": "2.0.2",
    "url-loader": "^0.5.6",
    "webpack": "^2.1.0-beta.26",
    "webpack-dashboard": "^0.1.8",
    "webpack-dev-server": "2.1.0-beta.5"
  }
}


================================================
FILE: protractor.conf.js
================================================
exports.config = {
  baseUrl: 'http://localhost:8080/',

  specs: [
    'src/**/*.e2e-spec.js'
  ],
  exclude: [],

  framework: 'jasmine2',

  allScriptsTimeout: 110000,

  jasmineNodeOpts: {
    showTiming: true,
    showColors: true,
    isVerbose: false,
    includeStackTrace: false,
    defaultTimeoutInterval: 400000
  },
  directConnect: true,

  capabilities: {
    'browserName': 'chrome'
  },

  onPrepare: function () {
    var SpecReporter = require('jasmine-spec-reporter');
    // add jasmine spec reporter
    jasmine.getEnv().addReporter(new SpecReporter({displayStacktrace: true}));

    browser.ignoreSynchronization = true;
  },


  /**
   * Angular 2 configuration
   *
   * useAllAngular2AppRoots: tells Protractor to wait for any angular2 apps on the page instead of just the one matching
   * `rootEl`
   *
   */
  useAllAngular2AppRoots: true
};


================================================
FILE: src/app/app.component.html
================================================
<section class="demo" [ngClass]="{'at-top':atTop}" imageLazyLoadArea>
  <!-- Above: You can optionally pass threshold, imageLazyLoadArea="50", the default is 100 -->
  <h3 id="top">Angular2 Image Lazy Loader Demo</h3>
  <ul>
    <li><a href="#ex1">Using img tag</a></li>
    <li><a href="#ex2">Using background-image</a></li>
    <li><a href="#ex3">Using img tag with custom loading container</a></li>
    <li><a href="#" (click)="addImage()">Add an image dynamically and scroll down to see it load in.</a></li>
  </ul>
  <h3 id="ex1">Using img tag</h3>
  <h5>As you scroll, the img's lazily load as they scroll into view.</h5>
  <div *ngFor="let image of images">
    <div class="spinner"></div>
    <img [imageLazyLoadItem]="image.url"/>
  </div>
  <h3 id="ex2">Using background-image</h3>
  <h5>As you scroll, images lazily load into the background-image of the containers as they scroll into view.</h5>
  <div *ngFor="let image of images" [imageLazyLoadItem]="image.url">
    <div class="spinner"></div>
  </div>
  <h3 id="ex3">Using img tag with custom loading container</h3>
  <h5>By default, when using img, the immediate container surrounding it will be used for the 'loading' and 'loaded' class. However you can set [imageLazyLoadingContainer] to a String which represents the selector of the parent to use for the classes. As you scroll, img's lazily load into the background-image of the containers as they scroll into view and notice that the 'loading-container' gets the classes.</h5>
  <div *ngFor="let image of images" class="loading-container" [id]="image.id">
    <div class="spinner"></div>
    <div>
      <img [imageLazyLoadItem]="image.url" [imageLazyLoadingContainer]="'.loading-container'"/>
    </div>
  </div>
  <a class="top" href="#top">Top</a>
</section>


================================================
FILE: src/app/app.component.scss
================================================
// styles applied on :host are applied on the current component, "app" in this case
:host {
  display: block;
}

header {
  background-color: #fff;
  padding:0;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.5);
}

h1 {
  color: #000;
  text-align: center;
  font-family: sans-serif;
}

img {
  width:50%;
}

main {
  padding: 1em;
  font-family: Arial, Helvetica, sans-serif;
  text-align: center;
  margin-top: 50px;
  display: block;
}

footer {
  text-align: center;
  font-size: 0.8em;
  width: 100%;
  position: absolute;
  bottom: 20px;

  a {
    color: #fff;
    text-decoration: none;
    font-family: sans-serif;

    &:hover {
      text-decoration: underline;
    }
  }
}


================================================
FILE: src/app/app.component.ts
================================================
import { Component, ViewEncapsulation } from '@angular/core';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/operator/debounceTime';

import '../style/app.scss';

@Component({
  selector: 'my-app', // <my-app></my-app>
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  encapsulation: ViewEncapsulation.None
})
export class AppComponent {
  public atTop: boolean = true;
  public images: Array<any> = [
    {
      id: 1,
      name: `Oneonta Falls 1`,
      url: `img/oneonta.jpg`
    },
    {
      id: 2,
      name: `Oneonta Falls 2`,
      url: `img/DSC01920-L.jpg`
    },
    {
      id: 3,
      name: `Oneonta Falls 3`,
      url: `img/oneonta-gorge-800x600.jpg`
    },
    {
      id: 4,
      name: `Oneonta Falls 4`,
      url: `img/oneonta-gorge-11.jpg`
    },
    {
      id: 5,
      name: `Oneonta Falls 5`,
      url: `img/IMG_2039.jpg`
    },
    {
      id: 6,
      name: `Oneonta Falls 6`,
      url: `img/horsetail_falls_9-b.jpg`
    },
    {
      id: 7,
      name: `Oneonta Falls 7`,
      url: `img/P1000662-L.jpg`
    },
    {
      id: 8,
      name: `Oneonta Falls 8`,
      url: `img/DSC01920-L.jpg`
    },
    {
      id: 9,
      name: `Oneonta Falls 9`,
      url: `img/IMG_8356_Medium.JPG`
    },
    {
      id: 10,
      name: `Oneonta Falls 10`,
      url: `img/IMG_2036.jpg`
    }
  ];

  constructor() {
    let scrollStream = Observable.fromEvent(window, 'scroll').debounceTime(500);

    scrollStream.subscribe(() => {
      this.atTop = window.pageYOffset < 25;
    });
  }

  public addImage(): void {
    this.images.push({
      id: 11,
      name: `Simpsons as the ${this.images.length + 1}th image!`,
      url: `img/The-Simpsons-post2.jpg`,
      added: true
    });
    if (window.confirm(`You added an image dynamically, click "Ok" to scroll down to see it load in when you get there!`)) {
      setTimeout(function(){
        location.hash = '#11';
      }, 100);

    }
  }
}


================================================
FILE: src/app/app.module.ts
================================================
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpModule } from '@angular/http';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { IMAGELAZYLOAD_DIRECTIVES } from './directives/image-lazy-load.directive';
import { ImageLazyLoaderService } from './services/image-lazy-load.service';
import { WebWorkerService } from './services/web-worker.service';

WebWorkerService.workerUrl = 'xhrWorker.js';

@NgModule({
  imports: [
    BrowserModule,
    HttpModule,
    FormsModule
  ],
  declarations: [
    AppComponent,
    IMAGELAZYLOAD_DIRECTIVES
  ],
  providers: [
    ImageLazyLoaderService,
    WebWorkerService
  ],
  exports: [
    IMAGELAZYLOAD_DIRECTIVES
  ],
  bootstrap: [AppComponent]
})
export class AppModule {

}


================================================
FILE: src/app/directives/image-lazy-load.directive.ts
================================================
import {
    Directive, ContentChildren, QueryList, Input, ElementRef, Renderer, OnInit, AfterContentInit
} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import {Subscription} from 'rxjs/Subscription';
import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/operator/debounceTime';
import {ImageLazyLoaderService, IImageLazyLoadConfig} from '../services/image-lazy-load.service';

@Directive({
  selector: '[imageLazyLoadItem]'
})
export class ImageLazyLoadItemDirective {
  @Input('imageLazyLoadItem') imageLazyLoadItem: string;
  @Input() imageLazyLoadingContainer: string;
  public loading: boolean = false;
  public loaded: boolean = false;
  public error: boolean = false;
  private tagName: string;

  constructor(private el: ElementRef, private renderer: Renderer, private lazyLoader: ImageLazyLoaderService) {
    this.tagName = el.nativeElement.tagName;
  }
  /*
  * @returns return position/dimension info as an Object `{top, left, bottom}`.
  */
  getPosition() {
    let box = this.el.nativeElement.getBoundingClientRect();
    let top = box.top + (window.pageYOffset - document.documentElement.clientTop);
    return {
      top: top,
      left: box.left + (window.pageXOffset - document.documentElement.clientLeft),
      bottom: top + this.el.nativeElement.clientHeight
    };
  }
  /*
  * @returns container target to place `loading`/`loaded` classes onto.
  */
  getLoadingContainer() {
    if (this.imageLazyLoadingContainer) {
      // find parent node with specified selector
      let collectionHas = (a: any, b: any) => {
        for (let i in a) {
          if (a[i] === b) {
              return true;
          }
        }
        return false;
      };
      let all = document.querySelectorAll(this.imageLazyLoadingContainer);
      let cur = this.el.nativeElement.parentNode;
      while (cur && !collectionHas(all, cur)) {
        cur = cur.parentNode;
      }
      if (cur) {
        return cur;
      } else {
        // fallback to direct parentNode if not found
        return this.el.nativeElement.parentNode;
      }
    } else {
      // default is direct parentNode for IMG and the node itself for background-image
      if (this.tagName === 'IMG') {
        return this.el.nativeElement.parentNode;
      } else {
        return this.el.nativeElement;
      }
    }
  }
  hasClassName(name: string) {
    return new RegExp('(?:^|\\s+)' + name + '(?:\\s+|$)').test(this.getLoadingContainer().className);
  }
  addClassName(name: string) {
    if (!this.hasClassName(name)) {
      let container = this.getLoadingContainer();
      container.className = container.className ? [container.className, name].join(' ') : name;
    }
  }
  removeClassName(name: string) {
    if (this.hasClassName(name)) {
      let container = this.getLoadingContainer();
      let c = container.className;
      container.className = c.replace(new RegExp('(?:^|\\s+)' + name + '(?:\\s+|$)', 'g'), '');
    }
  }
  toggleLoaded(enable: boolean) {
    this.loaded = enable;
    if (enable) {
      this.removeClassName(this.lazyLoader.config.loadingClass);
      this.addClassName(this.lazyLoader.config.loadedClass);
    } else {
      this.removeClassName(this.lazyLoader.config.loadedClass);
    }
  }
  /*
  * starts loading the image in the background.
  */
  loadImage() {
    if (!this.loaded && !this.loading) {
      this.loading = true;
      this.addClassName(this.lazyLoader.config.loadingClass);

      let customHeaders: any = this.lazyLoader.config.headers ? this.lazyLoader.config.headers : null;
      this.lazyLoader.load(this.imageLazyLoadItem, customHeaders).then(() => {
        this.setImage();
      }, (err) => {
        this.error = true;
        this.loading = false;
        this.removeClassName(this.lazyLoader.config.loadingClass);
        this.addClassName(this.lazyLoader.config.errorClass);
      });
    }
  }
  /*
  * sets the image to `imageLazyLoadItem`.
  */
  setImage() {
    if (!this.loaded) {
      if (this.tagName === 'IMG') {
        this.renderer.setElementAttribute(this.el.nativeElement, 'src', this.imageLazyLoadItem);
      } else {
        this.renderer.setElementAttribute(this.el.nativeElement, 'style', `background-image:url('${this.imageLazyLoadItem}')`);
      }
      this.loading = false;
      this.toggleLoaded(true);
    }
  }
}

@Directive({
  selector: '[imageLazyLoadArea]'
})
export class ImageLazyLoadAreaDirective implements OnInit, AfterContentInit {
  @Input('imageLazyLoadArea') threshold: number;
  /**
   * Object that implements IImageLazyLoadConfig:
   * headers?: any = custom headers
   * loadingClass?: string = 'custom-loading-class'
   * loadedClass?: string = 'custom-loaded-class'
   * errorClass?: string = 'custom-error-class'
   */
  @Input() imageLazyLoadConfig: IImageLazyLoadConfig;
  @ContentChildren(ImageLazyLoadItemDirective) private items: QueryList<ImageLazyLoadItemDirective>;
  private itemsToLoad: Array<any>;
  private _sub: Subscription;

  constructor(private lazyLoader: ImageLazyLoaderService) {}

  private loadInView(list?: Array<ImageLazyLoadItemDirective>): void {
    this.itemsToLoad = (list || this.itemsToLoad).filter((item) => !item.loaded && !item.loading);
    for (let item of this.itemsToLoad) {
      let ePos = item.getPosition();
      if (ePos.bottom > 0 && (ePos.bottom >= (window.pageYOffset - this.threshold))
          && (ePos.top <= ((window.pageYOffset + window.innerHeight) + this.threshold))) {
        item.loadImage();
      }
    }
    if (this.itemsToLoad.length === 0 && this._sub) {
      // subscription is no longer needed
      this._sub.unsubscribe();
      this._sub = undefined;
    }
  }
  private scrollSubscribe() {
    let scrollStream = Observable.fromEvent(window, 'scroll').debounceTime(250);

    this._sub = scrollStream.subscribe(() => {
      this.loadInView();
    });
  }
  private init() {

    let subScroll = () => {
      if (!this._sub) {
        this.scrollSubscribe();
      }
    };

    // load the initial children in view
    if (this.items.length) {
      // using setTimeout to ensure styles have applied before triggering load
      setTimeout(() => {
        this.loadInView(this.items.toArray());
      });
    }

    // listen to scroll event
    subScroll();

    // fired with subsequent changes
    // ideally this would fire on subscribe but it doesn't
    // therefore the above ensures it's handled on ngAfterContentInit
    this.items.changes.subscribe((list) => {
      this.loadInView(list.toArray());
      // since scroll subscription is unsuscribed when all items have loaded
      // ensure it is re-subscribed when changes occur
      subScroll();
    });
  }
  ngOnInit() {
    this.threshold = +this.threshold || 100;

    if (typeof (this.imageLazyLoadConfig) === 'object') {
      this.lazyLoader.config = this.imageLazyLoadConfig;
    }
  }
  ngAfterContentInit() {
    this.init();
  }
}

export const IMAGELAZYLOAD_DIRECTIVES: any[] = [ImageLazyLoadAreaDirective, ImageLazyLoadItemDirective];


================================================
FILE: src/app/services/image-lazy-load.service.ts
================================================
import {Injectable} from '@angular/core';
import {Http, Headers, RequestOptions} from '@angular/http';
import {WebWorkerService} from './web-worker.service';

export interface IImageLazyLoadConfig {
  headers?: any;
  loadingClass?: string;
  loadedClass?: string;
  errorClass?: string;
}

@Injectable()
export class ImageLazyLoaderService {
  public imageCache: any = {};
  // default config
  private _config: IImageLazyLoadConfig = {
    loadingClass: 'loading',
    loadedClass: 'loaded',
    errorClass: 'error'
  };

  constructor(private http: Http, private worker: WebWorkerService) {}
  /*
  * Loads the url via `WebWorker` where supported and gracefully degrades to using `Http` if needed.
  * @param url  url of image to load.
  * @param headers  **(optional)** any custom headers (as an `Object`) that may be required to load the image.
  * @returns return `Promise`
  */
  public load(url: string, headers?: any): Promise<any> {
    if (this.imageCache[url]) {
      // image has been previously loaded, rely on browser cache
      return Promise.resolve(true);
    } else if (WebWorkerService.supported && WebWorkerService.enabled) {
      return this.loadViaWorker(url, headers);
    } else {
      return this.loadViaHttp(url, headers);
    }
  }
  /*
  * Loads the url via `WebWorker` directly.
  * @param url  url of image to load.
  * @param headers  **(optional)** any custom headers (as an `Object`) that may be required to load the image.
  * @returns return `Promise`
  */
  public loadViaWorker(url: string, headers?: any): Promise<any> {
    return new Promise((resolve, reject) => {
      let id: any, completeHandler: any, msgFn: any, errorFn: any;

      completeHandler = (success: boolean, err?: any) => {
        this.worker.terminate(id);

        if (success) {
          this.imageCache[url] = true;
          resolve(true);
        } else {
          reject(err);
        }
      };

      msgFn = (e: any) => {
        if (e && e.data !== 'ERROR') {
          completeHandler(true);
        } else {
          completeHandler(false, e);
        }
      };

      errorFn = (e: any) => {
        completeHandler(false, e);
      };

      let config: any = {
        method: 'GET',
        url: url
      };

      // optionally set headers
      if (headers) {
        config.headers = headers;
      }
      id = this.worker.load(config, msgFn, errorFn);
    });
  }
  /*
  * Loads the url via `Http` directly.
  * @param url  url of image to load.
  * @param headers  **(optional)** any custom headers (as an `Object`) that may be required to load the image.
  * @returns return `Promise`
  */
  public loadViaHttp(url: string, headers?: any): Promise<any> {
    let ro: RequestOptions = null;
    if (headers) {
      ro = new RequestOptions({
        headers: new Headers(headers)
      });
    }
    return new Promise((resolve, reject) => {
      this.http.get(url, ro)
        .subscribe(res => {
          this.imageCache[url] = true;
          resolve(true);
        });
    });
  }

  /**
   * Custom config
   **/
  public set config(value: IImageLazyLoadConfig) {
    if (value) {
      if (value.headers) {
          this._config.headers = value.headers;
      }
      if (value.loadingClass) {
          this._config.loadingClass = value.loadingClass;
      }
      if (value.loadedClass) {
          this._config.loadedClass = value.loadedClass;
      }
      if (value.errorClass) {
          this._config.errorClass = value.errorClass;
      }
    }
  }

  public get config(): IImageLazyLoadConfig {
    return this._config;
  }
}


================================================
FILE: src/app/services/web-worker.service.stub.ts
================================================
import {Injectable} from '@angular/core';

@Injectable()
export class WebWorkerStub {
  static supported: boolean = true;
  static workerUrl: string = 'assets/js/xhrWorker.js';
  public activeWorkers: Array<any> = [];
  // public workersCalled: number = 0;

  load(config: any, msgFn: any, errorFn?: any): number {
    let id: number = Math.floor(Math.random() * 1000000000000);
    this.activeWorkers.push({
      id: id,
      config: config
    });
    setTimeout(() => {
     if (msgFn) {
       msgFn({data: 'success'});
     } else if (errorFn) {
       errorFn({data: 'ERROR'});
     }
    });
    // this.workersCalled++;
    return id;
  }
  terminate(id: number): void {
    let index = this.activeWorkers.findIndex(item => item.id === id);
    if (index > -1) {
      this.activeWorkers.splice(index, 1);
    }
  }
}


================================================
FILE: src/app/services/web-worker.service.ts
================================================
import {Injectable} from '@angular/core';

@Injectable()
export class WebWorkerService {
  static supported: boolean = typeof (Worker) !== 'undefined';
  static enabled: boolean = true; // enabled by default, however can be manually turned off
  static workerUrl: string = 'assets/js/xhrWorker.js';
  public activeWorkers: Array<any> = [];

  load(config: any, msgFn: any, errorFn?: any): number {
    if (typeof(config) !== 'object') {
      throw(`config must be an Object with method and url defined.`);
    } else if (!config.url) {
      throw(`config.url must be defined.`);
    }
    let id: number = Math.floor(Math.random() * 1000000000000);
    let w = new Worker(WebWorkerService.workerUrl);
    if (msgFn) {
      w.addEventListener('message', msgFn, false);
    }
    if (errorFn) {
      w.addEventListener('error', errorFn, false);
    }

    this.activeWorkers.push({
      id: id,
      worker: w,
      msgFn: msgFn,
      errorFn: errorFn
    });
    w.postMessage(config);
    return id;
  }
  terminate(id: number): void {
    let index = this.activeWorkers.findIndex(item => item.id === id);
    if (index > -1) {
      let activeWorker = this.activeWorkers[index];
      if (activeWorker && activeWorker.worker) {
        if (activeWorker.worker.msgFn) {
          activeWorker.worker.removeEventListener('message', activeWorker.worker.msgFn);
        }
        if (activeWorker.worker.errorFn) {
          activeWorker.worker.removeEventListener('error', activeWorker.worker.errorFn);
        }
        activeWorker.worker.terminate();
      }
      this.activeWorkers.splice(index, 1);
    }
  }
}


================================================
FILE: src/main.ts
================================================
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';

// depending on the env mode, enable prod mode or add debugging modules
if (process.env.ENV === 'build') {
  enableProdMode();
}

export function main() {
  return platformBrowserDynamic().bootstrapModule(AppModule);
}

if (document.readyState === 'complete') {
  main();
} else {
  document.addEventListener('DOMContentLoaded', main);
}


================================================
FILE: src/polyfills.ts
================================================
import 'core-js/client/shim';
import 'reflect-metadata';
require('zone.js/dist/zone');

import 'ts-helpers';

if (process.env.ENV === 'build') {
  // Production

} else {
  // Development

  Error['stackTraceLimit'] = Infinity;

  require('zone.js/dist/long-stack-trace-zone');
}


================================================
FILE: src/public/index.html
================================================
<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Angular2 Image Lazy Loader</title>
    <link rel="icon" type="image/x-icon" href="/img/favicon.ico">

    <base href="/">
  </head>
  <body>
    <my-app><div class="spinner"></div></my-app>
  </body>
</html>


================================================
FILE: src/public/xhrWorker.js
================================================
self.addEventListener('message', function(e) {
  var data, header, xhr;
  data = e.data;
  xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) {
      if (xhr.status === 200) {
        postMessage(xhr.responseText);
      } else {
        postMessage('ERROR');
      }
      return self.close();
    }
  };
  xhr.onerror = function() {
    postMessage('ERROR');
    return self.close();
  };
  xhr.open(data.method, data.url);
  if (data.headers) {
    for (header in data.headers) {
      if (data.headers[header]) {
        xhr.setRequestHeader(header, data.headers[header]);
      }
    }
  }
  return xhr.send();
});


================================================
FILE: src/style/app.scss
================================================
/* Reset */
html,
body,
div {
  border: 0;
  margin: 0;
  padding: 0;
}

/* Box-sizing border-box */
* {
  box-sizing: border-box;
}

/* Set up a default font and some padding to provide breathing room */
body {
  font-family: Roboto, "Helvetica Neue", sans-serif;
  font-size: 16px;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

p {
  font-weight: 400;
  letter-spacing: 0.01em;
  line-height: 20px;
  margin-bottom: 1em;
  margin-top: 1em;
}

ul {
  margin: 10px 0 0 0;
  padding: 0 0 0 20px;
}

li {
  font-weight: 400;
  margin-top: 4px;
}

input {
  border: 1px solid #106cc8;
  font-size: 14px;
  height: 40px;
  outline: none;
  padding: 8px;
}

button {
  background-color: #106cc8;
  border-style: none;
  color: rgba(255, 255, 255, 0.87);
  cursor: pointer;
  display: inline-block;
  font-size: 14px;
  height: 40px;
  padding: 8px 18px;
  text-decoration: none;
}

button:hover {
  background-color: #28739e;
}

body {
  background-color:rgba(0,0,0,.4);
}
h1, h2, h3, h5 {
  text-align: center;
  width: 90%;
  margin: 10px auto;
}
.spinner:before {
  transform: translate3d(0, 0, 0);
  -ms-transform: translate3d(0, 0, 0);
  -moz-transform: translate3d(0, 0, 0);
  -webkit-transform: translate3d(0, 0, 0);
  -o-transform: translate3d(0, 0, 0);
  -webkit-transition: opacity 0.5s ease-out;
  -moz-transition: opacity 0.5s ease-out;
  -o-transition: opacity 0.5s ease-out;
  transition: opacity 0.5s ease-out;
  -webkit-animation: rotating 1s linear infinite;
  -moz-animation: rotating 1s linear infinite;
  -ms-animation: rotating 1s linear infinite;
  -o-animation: rotating 1s linear infinite;
  animation: rotating 1s linear infinite;
  opacity:1;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  position: absolute;
  margin: auto;
  color: rgba(0, 0, 0, 0.3);
  content: "";
  color: #fff;
  background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxOC4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KICAgdmlld0JveD0iMCAwIDI5MCAyOTAiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDI5MCAyOTAiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPHBhdGggZmlsbD0iI2ZmZmZmZiIgZD0iTTE0NSwyNDEuNmMtNTMuMywwLTk2LjYtNDMuMi05Ni42LTk2LjZjMC01My4zLDQzLjItOTYuNiw5Ni42LTk2LjZjNTMuMywwLDk2LjYsNDMuMiw5Ni42LDk2LjYNCiAgYzAsMjYuNy0xMC44LDUwLjktMjguMyw2OC4zbDcuNiw3LjZjMTkuNC0xOS40LDMxLjUtNDYuMywzMS41LTc1LjljMC01OS4zLTQ4LTEwNy4zLTEwNy4zLTEwNy4zUzM3LjcsODUuNywzNy43LDE0NQ0KICBjMCw1OS4zLDQ4LDEwNy4zLDEwNy4zLDEwNy4zVjI0MS42eiIvPg0KPC9zdmc+DQo=");
  background-size: 50px 50px;
  height: 50px;
  width: 50px;
  background-repeat: no-repeat;
}
.demo {
  font-family: Verdana;
}
.demo a {
  display:block;
  text-align: center;
  cursor:pointer;
}
.demo a.top {
  position: fixed;
  bottom:10px;
  right:15px;
  font-size:14px;
  z-index: 10;
  background-color: rgba(0,0,0,.5);
  color:#fff;
  padding:8px 12px;
  border-radius: 4px;
  opacity:1;
  -webkit-transition: opacity 0.5s ease-out;
  -moz-transition: opacity 0.5s ease-out;
  -o-transition: opacity 0.5s ease-out;
  transition: opacity 0.5s ease-out;
}
.demo.at-top a.top {
  opacity: 0; 
}
.demo ul {
  width: 88%;
  margin: 0 auto;
  background-color: rgba(255,255,255,.5);
  border-radius: 4px;
  padding-top: 10px;
  padding-bottom: 10px;
  max-width: 420px;
}
.demo ul li a {
  text-align: left;
}
.demo > div {
  margin:10px auto;
  min-height: 280px;
  max-height:500px;
  background-repeat: no-repeat;
  background-position: center center;
  background-size: cover;
  position: relative;
}
.demo > div[style] {
  width:95%;
  -webkit-box-shadow: 0 0 4px rgba(0,0,0,.6);
  -moz-box-shadow: 0 0 4px rgba(0,0,0,.6);
  box-shadow: 0 0 4px rgba(0,0,0,.6);
}
.demo > div img {
  display: block;
  margin:0 auto;
  max-width: 100%;
  max-height: 500px;
  -webkit-box-shadow: 0 0 4px rgba(0,0,0,.6);
  -moz-box-shadow: 0 0 4px rgba(0,0,0,.6);
  box-shadow: 0 0 4px rgba(0,0,0,.6);
  -webkit-transition: opacity 0.5s ease-out;
  -moz-transition: opacity 0.5s ease-out;
  -o-transition: opacity 0.5s ease-out;
  transition: opacity 0.5s ease-out;
  opacity:0;
}
.demo > div.loaded img {
  opacity:1;
}
.demo > div.loaded .spinner:before {
  opacity:0;
}
@media (min-width:670px) {
  .demo > div {
    min-height: 500px;
  }
}


@keyframes rotating {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
@-moz-keyframes rotating {
  from {
    -moz-transform: rotate(0deg);
  }
  to {
    -moz-transform: rotate(360deg);
  }
}
@-ms-keyframes rotating {
  from {
    -ms-transform: rotate(0deg);
  }
  to {
    -ms-transform: rotate(360deg);
  }
}
@-o-keyframes rotating {
  from {
    -o-transform: rotate(0deg);
  }
  to {
    -o-transform: rotate(360deg);
  }
}
@-webkit-keyframes rotating {
  from {
    -webkit-transform: rotate(0deg);
  }
  to {
    -webkit-transform: rotate(360deg);
  }
}


================================================
FILE: src/vendor.ts
================================================
// Angular 2
import '@angular/platform-browser';
import '@angular/platform-browser-dynamic';
import '@angular/core';
import '@angular/common';
import '@angular/http';
import '@angular/router';

import 'rxjs';
import '@angularclass/hmr';

// Other vendors for example jQuery, Lodash or Bootstrap
// You can import js, ts, css, sass, ...


================================================
FILE: tsconfig.json
================================================
{
  "compilerOptions": {
    "target": "ES5",
    "module": "commonjs",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "sourceMap": true,
    "noEmitHelpers": true
  },
  "compileOnSave": false,
  "buildOnSave": false,
  "awesomeTypescriptLoaderOptions": {
    "forkChecker": true,
    "useWebpackText": true
  }
}


================================================
FILE: tslint.json
================================================
{
  "rulesDirectory": [
    "node_modules/codelyzer"
  ],
  "rules": {
    "class-name": true,
    "comment-format": [
      true,
      "check-space"
    ],
    "curly": true,
    "eofline": true,
    "forin": true,
    "indent": [
      true,
      "spaces"
    ],
    "label-position": true,
    "label-undefined": true,
    "max-line-length": [
      true,
      140
    ],
    "member-access": false,
    "member-ordering": [
      true,
      "static-before-instance",
      "variables-before-functions"
    ],
    "no-arg": true,
    "no-bitwise": true,
    "no-console": [
      true,
      "debug",
      "info",
      "time",
      "timeEnd",
      "trace"
    ],
    "no-construct": true,
    "no-debugger": true,
    "no-duplicate-key": true,
    "no-duplicate-variable": true,
    "no-empty": false,
    "no-eval": true,
    "no-inferrable-types": true,
    "no-shadowed-variable": true,
    "no-string-literal": false,
    "no-switch-case-fall-through": true,
    "no-trailing-whitespace": false,
    "no-unused-expression": true,
    "no-unused-variable": true,
    "no-unreachable": true,
    "no-use-before-declare": true,
    "no-var-keyword": true,
    "object-literal-sort-keys": false,
    "one-line": [
      true,
      "check-catch",
      "check-else",
      "check-whitespace"
    ],
    "quotemark": [
      true,
      "single"
    ],
    "radix": true,
    "semicolon": [
      "always"
    ],
    "triple-equals": [
      true,
      "allow-null-check"
    ],
    "typedef-whitespace": [
      true,
      {
        "call-signature": "nospace",
        "index-signature": "nospace",
        "parameter": "nospace",
        "property-declaration": "nospace",
        "variable-declaration": "nospace"
      }
    ],
    "variable-name": false,
    "whitespace": [
      true,
      "check-branch",
      "check-decl",
      "check-operator",
      "check-separator",
      "check-type"
    ],

    "directive-selector-name": [true, "camelCase"],
    "component-selector-name": [true, "kebab-case"],
    "directive-selector-type": [true, "attribute"],
    "component-selector-type": [true, "element"],
    "use-input-property-decorator": true,
    "use-output-property-decorator": true,
    "use-host-property-decorator": true,
    "no-input-rename": true,
    "no-output-rename": true,
    "use-life-cycle-interface": true,
    "use-pipe-transform-interface": true,
    "component-class-suffix": true,
    "directive-class-suffix": true,
    "directive-selector-prefix": [true, "my"],
    "component-selector-prefix": [true, "my"],
    "pipe-naming": [true, "camelCase", "my"]
  }
}


================================================
FILE: typedoc.json
================================================
{
  "mode": "modules",
  "out": "doc",
  "theme": "default",
  "ignoreCompilerErrors": "true",
  "experimentalDecorators": "true",
  "emitDecoratorMetadata": "true",
  "target": "ES5",
  "moduleResolution": "node",
  "preserveConstEnums": "true",
  "stripInternal": "true",
  "suppressExcessPropertyErrors": "true",
  "suppressImplicitAnyIndexErrors": "true",
  "module": "commonjs"
}

================================================
FILE: webpack.config.js
================================================
// Helper: root() is defined at the bottom
var path = require('path');
var webpack = require('webpack');

// Webpack Plugins
var CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin;
var autoprefixer = require('autoprefixer');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var CopyWebpackPlugin = require('copy-webpack-plugin');
var DashboardPlugin = require('webpack-dashboard/plugin');
var ForkCheckerPlugin = require('awesome-typescript-loader').ForkCheckerPlugin;

/**
 * Env
 * Get npm lifecycle event to identify the environment
 */
var ENV = process.env.npm_lifecycle_event;
var isTestWatch = ENV === 'test-watch';
var isTest = ENV === 'test' || isTestWatch;
var isProd = ENV === 'build';

module.exports = function makeWebpackConfig() {
  /**
   * Config
   * Reference: http://webpack.github.io/docs/configuration.html
   * This is the object where all configuration gets set
   */
  var config = {};

  /**
   * Devtool
   * Reference: http://webpack.github.io/docs/configuration.html#devtool
   * Type of sourcemap to use per build type
   */
  if (isProd) {
    config.devtool = 'source-map';
  }
  else if (isTest) {
    config.devtool = 'inline-source-map';
  }
  else {
    config.devtool = 'eval-source-map';
  }

  /**
   * Entry
   * Reference: http://webpack.github.io/docs/configuration.html#entry
   */
  config.entry = isTest ? function(){return {}} : {
    'polyfills': './src/polyfills.ts',
    'vendor': './src/vendor.ts',
    'app': './src/main.ts' // our angular app
  };

  /**
   * Output
   * Reference: http://webpack.github.io/docs/configuration.html#output
   */
  config.output = isTest ? {} : {
    path: root('dist'),
    publicPath: isProd ? '/' : 'http://localhost:8080/',
    filename: isProd ? 'js/[name].[hash].js' : 'js/[name].js',
    chunkFilename: isProd ? '[id].[hash].chunk.js' : '[id].chunk.js'
  };

  /**
   * Resolve
   * Reference: http://webpack.github.io/docs/configuration.html#resolve
   */
  config.resolve = {
    // only discover files that have those extensions
    extensions: ['.ts', '.js', '.json', '.css', '.scss', '.html'],
  };

  var atlOptions = '';
  if (isTest && !isTestWatch) {
    // awesome-typescript-loader needs to output inlineSourceMap for code coverage to work with source maps.
    atlOptions = 'inlineSourceMap=true&sourceMap=false';
  }

  /**
   * Loaders
   * Reference: http://webpack.github.io/docs/configuration.html#module-loaders
   * List: http://webpack.github.io/docs/list-of-loaders.html
   * This handles most of the magic responsible for converting modules
   */
  config.module = {
    rules: [
      // Support for .ts files.
      {
        test: /\.ts$/,
        loaders: ['awesome-typescript-loader?' + atlOptions, 'angular2-template-loader', '@angularclass/hmr-loader'],
        exclude: [isTest ? /\.(e2e)\.ts$/ : /\.(spec|e2e)\.ts$/, /node_modules\/(?!(ng2-.+))/]
      },

      // copy those assets to output
      {
        test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        loader: 'file-loader?name=fonts/[name].[hash].[ext]?'
      },

      // Support for *.json files.
      {test: /\.json$/, loader: 'json-loader'},

      // Support for CSS as raw text
      // use 'null' loader in test mode (https://github.com/webpack/null-loader)
      // all css in src/style will be bundled in an external css file
      {
        test: /\.css$/,
        exclude: root('src', 'app'),
          loader: isTest ? 'null-loader' : ExtractTextPlugin.extract({
              fallbackLoader: 'style-loader',
              loader: ['css-loader', 'postcss-loader']
          })
      },
      // all css required in src/app files will be merged in js files
      {test: /\.css$/, include: root('src', 'app'), loader: 'raw-loader!postcss-loader'},

      // support for .scss files
      // use 'null' loader in test mode (https://github.com/webpack/null-loader)
      // all css in src/style will be bundled in an external css file
      {
        test: /\.scss$/,
        exclude: root('src', 'app'),
          loader: isTest ? 'null-loader' : ExtractTextPlugin.extract({
              fallbackLoader: 'style-loader',
              loader: ['css-loader', 'postcss-loader', 'sass-loader']
          })
      },
      // all css required in src/app files will be merged in js files
      {test: /\.scss$/, exclude: root('src', 'style'), loader: 'raw-loader!postcss-loader!sass-loader'},

      // support for .html as raw text
      // todo: change the loader to something that adds a hash to images
      {test: /\.html$/, loader: 'raw-loader',  exclude: root('src', 'public')}
    ]
  };

  if (isTest && !isTestWatch) {
    // instrument only testing sources with Istanbul, covers ts files
    config.module.rules.push({
      test: /\.ts$/,
      enforce: 'post',
      include: path.resolve('src'),
      loader: 'istanbul-instrumenter-loader',
      exclude: [/\.spec\.ts$/, /\.e2e\.ts$/, /node_modules/]
    });
  }

  if (!isTest || !isTestWatch) {
    // tslint support
    config.module.rules.push({
      test: /\.ts$/,
      enforce: 'pre',
      loader: 'tslint-loader'
    });
  }

  /**
   * Plugins
   * Reference: http://webpack.github.io/docs/configuration.html#plugins
   * List: http://webpack.github.io/docs/list-of-plugins.html
   */
  config.plugins = [
    // Define env variables to help with builds
    // Reference: https://webpack.github.io/docs/list-of-plugins.html#defineplugin
    new webpack.DefinePlugin({
      // Environment helpers
      'process.env': {
        ENV: JSON.stringify(ENV)
      }
    }),

    // Workaround needed for angular 2 angular/angular#11580
      new webpack.ContextReplacementPlugin(
        // The (\\|\/) piece accounts for path separators in *nix and Windows
        /angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/,
        root('./src') // location of your src
      ),

    // Tslint configuration for webpack 2
    new webpack.LoaderOptionsPlugin({
      options: {
        /**
         * Apply the tslint loader as pre/postLoader
         * Reference: https://github.com/wbuchwalter/tslint-loader
         */
        tslint: {
          emitErrors: false,
          failOnHint: false
        },
        /**
         * Sass
         * Reference: https://github.com/jtangelder/sass-loader
         * Transforms .scss files to .css
         */
        sassLoader: {
          //includePaths: [path.resolve(__dirname, "node_modules/foundation-sites/scss")]
        },
        /**
         * PostCSS
         * Reference: https://github.com/postcss/autoprefixer-core
         * Add vendor prefixes to your css
         */
        postcss: [
          autoprefixer({
            browsers: ['last 2 version']
          })
        ]
      }
    })
  ];

  if (!isTest && !isProd) {
      config.plugins.push(new DashboardPlugin());
  }

  if (!isTest && !isTestWatch) {
    config.plugins.push(
      new ForkCheckerPlugin(),

      // Generate common chunks if necessary
      // Reference: https://webpack.github.io/docs/code-splitting.html
      // Reference: https://webpack.github.io/docs/list-of-plugins.html#commonschunkplugin
      new CommonsChunkPlugin({
        name: ['vendor', 'polyfills']
      }),

      // Inject script and link tags into html files
      // Reference: https://github.com/ampedandwired/html-webpack-plugin
      new HtmlWebpackPlugin({
        template: './src/public/index.html',
        chunksSortMode: 'dependency'
      }),

      // Extract css files
      // Reference: https://github.com/webpack/extract-text-webpack-plugin
      // Disabled when in test mode or not in build mode
      new ExtractTextPlugin({filename: 'css/[name].[hash].css', disable: !isProd})
    );
  }

  // Add build specific plugins
  if (isProd) {
    config.plugins.push(
      // Reference: http://webpack.github.io/docs/list-of-plugins.html#noerrorsplugin
      // Only emit files when there are no errors
      new webpack.NoErrorsPlugin(),

      // // Reference: http://webpack.github.io/docs/list-of-plugins.html#dedupeplugin
      // // Dedupe modules in the output
      // new webpack.optimize.DedupePlugin(),

      // Reference: http://webpack.github.io/docs/list-of-plugins.html#uglifyjsplugin
      // Minify all javascript, switch loaders to minimizing mode
      new webpack.optimize.UglifyJsPlugin({sourceMap: true, mangle: { keep_fnames: true }}),

      // Copy assets from the public folder
      // Reference: https://github.com/kevlened/copy-webpack-plugin
      new CopyWebpackPlugin([{
        from: root('src/public')
      }])
    );
  }

  /**
   * Dev server configuration
   * Reference: http://webpack.github.io/docs/configuration.html#devserver
   * Reference: http://webpack.github.io/docs/webpack-dev-server.html
   */
  config.devServer = {
    contentBase: './src/public',
    historyApiFallback: true,
    quiet: true,
    stats: 'minimal' // none (or false), errors-only, minimal, normal (or true) and verbose
  };

  return config;
}();

// Helper functions
function root(args) {
  args = Array.prototype.slice.call(arguments, 0);
  return path.join.apply(path, [__dirname].concat(args));
}
Download .txt
gitextract_n04mij4e/

├── .editorconfig
├── .gitignore
├── .npmignore
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── index.d.ts
├── karma-shim.js
├── karma.conf.js
├── make.js
├── ng2-image-lazy-load.ts
├── package.json
├── protractor.conf.js
├── src/
│   ├── app/
│   │   ├── app.component.html
│   │   ├── app.component.scss
│   │   ├── app.component.ts
│   │   ├── app.module.ts
│   │   ├── directives/
│   │   │   └── image-lazy-load.directive.ts
│   │   └── services/
│   │       ├── image-lazy-load.service.ts
│   │       ├── web-worker.service.stub.ts
│   │       └── web-worker.service.ts
│   ├── main.ts
│   ├── polyfills.ts
│   ├── public/
│   │   ├── index.html
│   │   └── xhrWorker.js
│   ├── style/
│   │   └── app.scss
│   └── vendor.ts
├── tsconfig.json
├── tslint.json
├── typedoc.json
└── webpack.config.js
Download .txt
SYMBOL INDEX (41 symbols across 10 files)

FILE: index.d.ts
  class ImageLazyLoadModule (line 5) | class ImageLazyLoadModule {

FILE: ng2-image-lazy-load.ts
  class ImageLazyLoadModule (line 23) | class ImageLazyLoadModule {
    method forRoot (line 25) | static forRoot(configuredProviders: Array<any>): ModuleWithProviders {

FILE: src/app/app.component.ts
  class AppComponent (line 14) | class AppComponent {
    method constructor (line 69) | constructor() {
    method addImage (line 77) | public addImage(): void {

FILE: src/app/app.module.ts
  class AppModule (line 32) | class AppModule {

FILE: src/app/directives/image-lazy-load.directive.ts
  class ImageLazyLoadItemDirective (line 13) | class ImageLazyLoadItemDirective {
    method constructor (line 21) | constructor(private el: ElementRef, private renderer: Renderer, privat...
    method getPosition (line 27) | getPosition() {
    method getLoadingContainer (line 39) | getLoadingContainer() {
    method hasClassName (line 70) | hasClassName(name: string) {
    method addClassName (line 73) | addClassName(name: string) {
    method removeClassName (line 79) | removeClassName(name: string) {
    method toggleLoaded (line 86) | toggleLoaded(enable: boolean) {
    method loadImage (line 98) | loadImage() {
    method setImage (line 117) | setImage() {
  class ImageLazyLoadAreaDirective (line 133) | class ImageLazyLoadAreaDirective implements OnInit, AfterContentInit {
    method constructor (line 147) | constructor(private lazyLoader: ImageLazyLoaderService) {}
    method loadInView (line 149) | private loadInView(list?: Array<ImageLazyLoadItemDirective>): void {
    method scrollSubscribe (line 164) | private scrollSubscribe() {
    method init (line 171) | private init() {
    method ngOnInit (line 200) | ngOnInit() {
    method ngAfterContentInit (line 207) | ngAfterContentInit() {
  constant IMAGELAZYLOAD_DIRECTIVES (line 212) | const IMAGELAZYLOAD_DIRECTIVES: any[] = [ImageLazyLoadAreaDirective, Ima...

FILE: src/app/services/image-lazy-load.service.ts
  type IImageLazyLoadConfig (line 5) | interface IImageLazyLoadConfig {
  class ImageLazyLoaderService (line 13) | class ImageLazyLoaderService {
    method constructor (line 22) | constructor(private http: Http, private worker: WebWorkerService) {}
    method load (line 29) | public load(url: string, headers?: any): Promise<any> {
    method loadViaWorker (line 45) | public loadViaWorker(url: string, headers?: any): Promise<any> {
    method loadViaHttp (line 90) | public loadViaHttp(url: string, headers?: any): Promise<any> {
    method config (line 109) | public set config(value: IImageLazyLoadConfig) {
    method config (line 126) | public get config(): IImageLazyLoadConfig {

FILE: src/app/services/web-worker.service.stub.ts
  class WebWorkerStub (line 4) | class WebWorkerStub {
    method load (line 10) | load(config: any, msgFn: any, errorFn?: any): number {
    method terminate (line 26) | terminate(id: number): void {

FILE: src/app/services/web-worker.service.ts
  class WebWorkerService (line 4) | class WebWorkerService {
    method load (line 10) | load(config: any, msgFn: any, errorFn?: any): number {
    method terminate (line 34) | terminate(id: number): void {

FILE: src/main.ts
  function main (line 10) | function main() {

FILE: webpack.config.js
  function root (line 283) | function root(args) {
Condensed preview — 31 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (62K chars).
[
  {
    "path": ".editorconfig",
    "chars": 282,
    "preview": "# EditorConfig is awesome: http://EditorConfig.org\n\n# top-most EditorConfig file\nroot = true\n\n# Unix-style newlines with"
  },
  {
    "path": ".gitignore",
    "chars": 835,
    "preview": "# Logs\nlogs\n*.log\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nl"
  },
  {
    "path": ".npmignore",
    "chars": 1028,
    "preview": "# Logs\nlogs\n*.log\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nl"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 1070,
    "preview": "## Submitting Pull Requests\n\n**Please follow these basic steps to simplify pull request reviews - if you don't you'll pr"
  },
  {
    "path": "LICENSE",
    "chars": 1079,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2016 Preboot team\n\nPermission is hereby granted, free of charge, to any person obta"
  },
  {
    "path": "README.md",
    "chars": 5012,
    "preview": "[![Dependency Status](https://david-dm.org/NathanWalker/ng2-image-lazy-load.svg)](https://david-dm.org/NathanWalker/ng2-"
  },
  {
    "path": "index.d.ts",
    "chars": 351,
    "preview": "import { ModuleWithProviders } from \"@angular/core\";\nexport * from './src/app/services/image-lazy-load.service';\nexport "
  },
  {
    "path": "karma-shim.js",
    "chars": 1425,
    "preview": "Error.stackTraceLimit = Infinity;\n\nrequire('core-js/client/shim');\nrequire('reflect-metadata');\n\nrequire('ts-helpers');\n"
  },
  {
    "path": "karma.conf.js",
    "chars": 2305,
    "preview": "var path = require('path');\n\nvar webpackConfig = require('./webpack.config');\n\nvar ENV = process.env.npm_lifecycle_event"
  },
  {
    "path": "make.js",
    "chars": 783,
    "preview": "var pkg     = require('./package.json');\nvar path    = require('path');\nvar Builder = require('systemjs-builder');\nvar n"
  },
  {
    "path": "ng2-image-lazy-load.ts",
    "chars": 899,
    "preview": "import {NgModule, ModuleWithProviders} from \"@angular/core\";\nimport {IMAGELAZYLOAD_DIRECTIVES} from './src/app/directive"
  },
  {
    "path": "package.json",
    "chars": 3521,
    "preview": "{\n  \"name\": \"ng2-image-lazy-load\",\n  \"version\": \"2.0.9\",\n  \"description\": \"Angular2 image lazy loader library\",\n  \"repos"
  },
  {
    "path": "protractor.conf.js",
    "chars": 871,
    "preview": "exports.config = {\n  baseUrl: 'http://localhost:8080/',\n\n  specs: [\n    'src/**/*.e2e-spec.js'\n  ],\n  exclude: [],\n\n  fr"
  },
  {
    "path": "src/app/app.component.html",
    "chars": 1783,
    "preview": "<section class=\"demo\" [ngClass]=\"{'at-top':atTop}\" imageLazyLoadArea>\n  <!-- Above: You can optionally pass threshold, i"
  },
  {
    "path": "src/app/app.component.scss",
    "chars": 735,
    "preview": "// styles applied on :host are applied on the current component, \"app\" in this case\n:host {\n  display: block;\n}\n\nheader "
  },
  {
    "path": "src/app/app.component.ts",
    "chars": 2010,
    "preview": "import { Component, ViewEncapsulation } from '@angular/core';\nimport {Observable} from 'rxjs/Observable';\nimport 'rxjs/a"
  },
  {
    "path": "src/app/app.module.ts",
    "chars": 839,
    "preview": "import { NgModule } from '@angular/core';\nimport { BrowserModule } from '@angular/platform-browser';\nimport { HttpModule"
  },
  {
    "path": "src/app/directives/image-lazy-load.directive.ts",
    "chars": 7020,
    "preview": "import {\n    Directive, ContentChildren, QueryList, Input, ElementRef, Renderer, OnInit, AfterContentInit\n} from '@angul"
  },
  {
    "path": "src/app/services/image-lazy-load.service.ts",
    "chars": 3586,
    "preview": "import {Injectable} from '@angular/core';\nimport {Http, Headers, RequestOptions} from '@angular/http';\nimport {WebWorker"
  },
  {
    "path": "src/app/services/web-worker.service.stub.ts",
    "chars": 828,
    "preview": "import {Injectable} from '@angular/core';\n\n@Injectable()\nexport class WebWorkerStub {\n  static supported: boolean = true"
  },
  {
    "path": "src/app/services/web-worker.service.ts",
    "chars": 1623,
    "preview": "import {Injectable} from '@angular/core';\n\n@Injectable()\nexport class WebWorkerService {\n  static supported: boolean = t"
  },
  {
    "path": "src/main.ts",
    "chars": 509,
    "preview": "import { enableProdMode } from '@angular/core';\nimport { platformBrowserDynamic } from '@angular/platform-browser-dynami"
  },
  {
    "path": "src/polyfills.ts",
    "chars": 280,
    "preview": "import 'core-js/client/shim';\nimport 'reflect-metadata';\nrequire('zone.js/dist/zone');\n\nimport 'ts-helpers';\n\nif (proces"
  },
  {
    "path": "src/public/index.html",
    "chars": 278,
    "preview": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Angular2 Image Lazy Loader</title>\n    <link rel=\""
  },
  {
    "path": "src/public/xhrWorker.js",
    "chars": 667,
    "preview": "self.addEventListener('message', function(e) {\n  var data, header, xhr;\n  data = e.data;\n  xhr = new XMLHttpRequest();\n "
  },
  {
    "path": "src/style/app.scss",
    "chars": 5209,
    "preview": "/* Reset */\nhtml,\nbody,\ndiv {\n  border: 0;\n  margin: 0;\n  padding: 0;\n}\n\n/* Box-sizing border-box */\n* {\n  box-sizing: b"
  },
  {
    "path": "src/vendor.ts",
    "chars": 336,
    "preview": "// Angular 2\nimport '@angular/platform-browser';\nimport '@angular/platform-browser-dynamic';\nimport '@angular/core';\nimp"
  },
  {
    "path": "tsconfig.json",
    "chars": 343,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"ES5\",\n    \"module\": \"commonjs\",\n    \"emitDecoratorMetadata\": true,\n    \"experime"
  },
  {
    "path": "tslint.json",
    "chars": 2612,
    "preview": "{\n  \"rulesDirectory\": [\n    \"node_modules/codelyzer\"\n  ],\n  \"rules\": {\n    \"class-name\": true,\n    \"comment-format\": [\n "
  },
  {
    "path": "typedoc.json",
    "chars": 384,
    "preview": "{\n  \"mode\": \"modules\",\n  \"out\": \"doc\",\n  \"theme\": \"default\",\n  \"ignoreCompilerErrors\": \"true\",\n  \"experimentalDecorators"
  },
  {
    "path": "webpack.config.js",
    "chars": 9228,
    "preview": "// Helper: root() is defined at the bottom\nvar path = require('path');\nvar webpack = require('webpack');\n\n// Webpack Plu"
  }
]

About this extraction

This page contains the full source code of the NathanWalker/ng2-image-lazy-load GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 31 files (56.4 KB), approximately 16.4k tokens, and a symbol index with 41 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!