[
  {
    "path": ".editorconfig",
    "content": "# EditorConfig is awesome: http://EditorConfig.org\n\n# top-most EditorConfig file\nroot = true\n\n# Unix-style newlines with a newline ending every file\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = 2\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true"
  },
  {
    "path": ".gitignore",
    "content": "# Logs\nlogs\n*.log\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Users Environment Variables\n.lock-wscript\n\n# OS generated files #\n.DS_Store\nehthumbs.db\nIcon?\nThumbs.db\n\n# Node Files #\n/node_modules\n/bower_components\n\n# Coverage #\n/coverage/\n\n# Typing #\n/src/typings/tsd/\n/typings/\n/tsd_typings/\n\n# Dist #\n/dist\n/public/__build__/\n/src/*/__build__/\n__build__/**\n.webpack.json\n\n# Doc #\n/doc/\n\n# IDE #\n.idea/\n*.swp\n\n*.js\n*.js.map\n*.d.ts\n!src/public/xhrWorker.js\n!index.d.ts\n!*.e2e.js\n!make.js\n!karma*.js\n!protractor.conf.js\n!webpack.config.js\n"
  },
  {
    "path": ".npmignore",
    "content": "# Logs\nlogs\n*.log\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directory\n# Commenting this out is preferred by some people, see\n# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-\nnode_modules\n\n# Users Environment Variables\n.lock-wscript\n.tsdrc\n\n#IntelliJ configuration files\n.idea\n\ndist\ndev\ndocs\nlib\ntest\n\nThumbs.db\n.DS_Store\n*.yml\n*.ts\n!*.d.ts\nsrc/app/app.*\nsrc/public\nsrc/shims\nsrc/app.scss\nsrc/main.ts\nsrc/polyfills.ts\nsrc/vendor.ts\nsrc/*.d.ts\nsrc/*.js\nsrc/*.map\nsrc/style\n*.spec.*\n*.e2e.*\nCONTRIBUTING.md\nkarma-shim.js\nkarma.conf.js\nmake.js\nprotractor.conf.js\ntsconfig.json\ntslint.json\ntypedoc.json\ntypings.json\ntypings\nwebpack.config.js\n.travis.yml\n.jshintrc\n.editorconfig\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "## Submitting Pull Requests\n\n**Please follow these basic steps to simplify pull request reviews - if you don't you'll probably just be asked to anyway.**\n\n* Please rebase your branch against the current master\n* Please ensure that the test suite passes **and** that code is lint free before submitting a PR by running:\n * ```npm test```\n* If you've added new functionality, **please** include tests which validate its behaviour\n* Make reference to possible [issues](https://github.com/preboot/angular2-library-seed/issues) on PR comment\n\n## Submitting bug reports\n\n* Please detail the affected browser(s) and operating system(s)\n* Please be sure to state which version of node **and** npm you're using\n\n## How to get setup and run the code as well as test\n\n**Note** To run the demo, you must have node v4.x.x or higher and npm 3.x.x.\n\n```bash\ngit clone https://github.com/preboot/angular2-library-seed.git\ncd angular2-library-seed\nnpm install   # or `npm run reinstall` if you get an error\nnpm start     # start with --env dev\n```\n\n# Running tests\n\n```bash\nnpm test\n```\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016 Preboot team\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "[![Dependency Status](https://david-dm.org/NathanWalker/ng2-image-lazy-load.svg)](https://david-dm.org/NathanWalker/ng2-image-lazy-load)\n[![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)\n\n# Not currently maintained\n\nYou might try using [this great lazy load lib for now](https://github.com/tjoskar/ng-lazyload-image).\nThe 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.\n\n# ng2-image-lazy-load\n\nDemo: https://ng2-image-lazy-load-demo.herokuapp.com\n\n## Installation\n```sh\nnpm i ng2-image-lazy-load --save\n```\n\n## Example implementation\n\nThis library utilizes `WebWorkers` (https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) for background loading of images.\n\nBy 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.\n\nTo set a custom path to load your worker file relative to your web server root:\n```\nWebWorkerService.workerUrl = 'path/to/your/custom_worker.js'\n```\nThe example below will help illustrate this.\n\nAlso, ensure you've loaded the angular/http bundle as well as this library falls back to using `Http` wherever `Worker` is not supported.\n\n```ts\nimport {BrowserModule} from \"@angular/platform-browser\";\nimport {NgModule, Component} from '@angular/core';\nimport {HttpModule} from '@angular/http';\nimport {ImageLazyLoadModule, WebWorkerService} from 'ng2-image-lazy-load';\n\n// default: 'assets/js/xhrWorker.js'\nWebWorkerService.workerUrl = 'path/to/your/xhrWorker.js';\n\n// default: true\n// set to false if you want to force Http instead of WebWorker\nWebWorkerService.enabled = true;\n\n@Component({\n    selector: 'app',\n    template: `\n      <div imageLazyLoadArea>\n        <div *ngFor=\"let image of images\">\n          <img [imageLazyLoadItem]=\"image.url\"/>\n        </div>\n      </div>\n    `\n})\nexport class AppComponent {\n    public images: Array<any> = [\n      {\n        name:`image 1`,\n        url:`image.jpg`\n      },\n      {\n        name:`image 2`,\n        url:`image_2.jpg`\n      }\n    ];\n}\n\n@NgModule({\n    imports: [\n        BrowserModule,\n        HttpModule,\n        ImageLazyLoadModule\n    ],\n    declarations: [AppComponent],\n    bootstrap: [AppComponent]\n})\nexport class AppModule {\n}\n```\n\n### Configuration\n\nYou can configure custom headers as well as custom loading, loaded and error classes by using the `imageLazyLoadConfig` directive:\n\n```\n// view template\n<div imageLazyLoadArea [imageLazyLoadConfig]=\"lazyLoadConfig\">\n  <div *ngFor=\"let image of images\">\n    <img [imageLazyLoadItem]=\"image.url\"/>\n  </div>\n</div>\n\n// Component\npublic lazyLoadConfig: IImageLazyLoadConfig = {\n  headers: {\n    'Authorization': 'Bearer auth-token'\n  },\n  loadingClass: 'custom-loading',\n  loadedClass: 'custom-loaded',\n  errorClass: 'custom-error'\n};\n```\n\n## API\n### ImageLazyLoaderService\n#### Properties:\n- `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}\n\n#### Methods:\n- `load(url: string, headers?: any): Promise<any>`: Load url with optional custom headers\n- `loadViaWorker(url: string, headers?: any): Promise<any>`: Use a webworker directly to load url with optional custom headers\n- `loadViaHttp(url: string, headers?: any): Promise<any>`: Use the `Http` service directly to load url with optional custom headers\n\n### WebWorkerService\n##### 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.\n#### Properties:\n- `static supported: boolean`: Determine if workers are supported\n- `static workerUrl: string`: Used to set the path to a worker file. Defaults to 'assets/js/xhrWorker.js'\n- `activeWorkers: Array<any>`: At any given moment, this can be checked to see how many workers are currently activated\n\n#### Methods:\n- `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.\n- `terminate(id: number)`: Terminate the worker\n\n\n# How to contribute\n\nSee [CONTRIBUTING](https://github.com/NathanWalker/ng2-image-lazy-load/blob/master/CONTRIBUTING.md)\n\n# Big Thank You\n\nThis library was made possible with help from this article by [Olivier Combe](https://github.com/ocombe):\nhttps://medium.com/@OCombe/how-to-publish-a-library-for-angular-2-on-npm-5f48cdabf435\n\nAlso, this project setup is based on the excellent [angular2-seed](https://github.com/mgechev/angular2-seed) by [Minko Gechev](https://github.com/mgechev).\n\n# License\n\nMIT\n"
  },
  {
    "path": "index.d.ts",
    "content": "import { ModuleWithProviders } from \"@angular/core\";\nexport * from './src/app/services/image-lazy-load.service';\nexport * from './src/app/services/web-worker.service';\nexport * from './src/app/directives/image-lazy-load.directive';\nexport declare class ImageLazyLoadModule {\n    static forRoot(configuredProviders: Array<any>): ModuleWithProviders;\n}\n"
  },
  {
    "path": "karma-shim.js",
    "content": "Error.stackTraceLimit = Infinity;\n\nrequire('core-js/client/shim');\nrequire('reflect-metadata');\n\nrequire('ts-helpers');\n\nrequire('zone.js/dist/zone');\nrequire('zone.js/dist/long-stack-trace-zone');\nrequire('zone.js/dist/proxy');\nrequire('zone.js/dist/sync-test');\nrequire('zone.js/dist/jasmine-patch');\nrequire('zone.js/dist/async-test');\nrequire('zone.js/dist/fake-async-test');\n\n/*\n Ok, this is kinda crazy. We can use the the context method on\n require that webpack created in order to tell webpack\n what files we actually want to require or import.\n Below, context will be a function/object with file names as keys.\n using that regex we are saying look in client/app and find\n any file that ends with '.spec.ts' and get its path. By passing in true\n we say do this recursively\n */\nvar appContext = require.context('./src', true, /\\.spec\\.ts/);\n\n// get all the files, for each file, call the context function\n// that will require the file and load it up here. Context will\n// loop and require those spec files here\nappContext.keys().forEach(appContext);\n\n// Select BrowserDomAdapter.\n// see https://github.com/AngularClass/angular2-webpack-starter/issues/124\n// Somewhere in the test setup\nvar testing = require('@angular/core/testing');\nvar browser = require('@angular/platform-browser-dynamic/testing');\n\ntesting.TestBed.initTestEnvironment(browser.BrowserDynamicTestingModule, browser.platformBrowserDynamicTesting());\n"
  },
  {
    "path": "karma.conf.js",
    "content": "var path = require('path');\n\nvar webpackConfig = require('./webpack.config');\n\nvar ENV = process.env.npm_lifecycle_event;\nvar isTestWatch = ENV === 'test-watch';\n\nmodule.exports = function (config) {\n  var _config = {\n\n    // base path that will be used to resolve all patterns (eg. files, exclude)\n    basePath: '',\n\n    // frameworks to use\n    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter\n    frameworks: ['jasmine'],\n\n    // list of files / patterns to load in the browser\n    files: [\n      { pattern: './karma-shim.js', watched: false }\n    ],\n\n    // list of files to exclude\n    exclude: [],\n\n    // preprocess matching files before serving them to the browser\n    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor\n    preprocessors: {\n      './karma-shim.js': ['webpack', 'sourcemap']\n    },\n\n    webpack: webpackConfig,\n\n    webpackMiddleware: {\n      // webpack-dev-middleware configuration\n      // i. e.\n      stats: 'errors-only'\n    },\n\n    webpackServer: {\n      noInfo: true // please don't spam the console when running in karma!\n    },\n\n    // test results reporter to use\n    // possible values: 'dots', 'progress', 'mocha'\n    // available reporters: https://npmjs.org/browse/keyword/karma-reporter\n    reporters: [\"mocha\"],\n\n    // web server port\n    port: 9876,\n\n    // enable / disable colors in the output (reporters and logs)\n    colors: true,\n\n    // level of logging\n    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG\n    logLevel: config.LOG_INFO,\n\n    // enable / disable watching file and executing tests whenever any file changes\n    autoWatch: false,\n\n    // start these browsers\n    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher\n    browsers: isTestWatch ? ['Chrome'] : ['PhantomJS'], \n\n    // Continuous Integration mode\n    // if true, Karma captures browsers, runs the tests and exits\n    singleRun: true\n  };\n\n  if (!isTestWatch) {\n    _config.reporters.push(\"coverage\");\n\n    _config.coverageReporter = {\n      dir: 'coverage/',\n      reporters: [{\n        type: 'json',\n        dir: 'coverage',\n        subdir: 'json',\n        file: 'coverage-final.json'\n      }]\n    };\n  }\n\n  config.set(_config);\n\n};\n"
  },
  {
    "path": "make.js",
    "content": "var pkg     = require('./package.json');\nvar path    = require('path');\nvar Builder = require('systemjs-builder');\nvar name    = pkg.name;\n\nvar builder = new Builder();\nvar config = {\n  baseURL: '.',\n  transpiler: 'typescript',\n  typescriptOptions: {\n    module: 'cjs'\n  },\n  map: {\n    typescript: './node_modules/typescript/lib/typescript.js',\n    '@angular': './node_modules/@angular',\n    rxjs: './node_modules/rxjs'\n  },\n  paths: {\n    '*': '*.js'\n  },\n  meta: {\n    './node_modules/@angular/*': { build: false },\n    './node_modules/rxjs/*': { build: false }\n  }\n};\n\nbuilder.config(config);\n\nbuilder\n.bundle(name, path.resolve(__dirname, 'bundles/', name + '.js'))\n.then(function() {\n  console.log('Build complete.');\n})\n.catch(function(err) {\n  console.log('Error', err);\n});\n"
  },
  {
    "path": "ng2-image-lazy-load.ts",
    "content": "import {NgModule, ModuleWithProviders} from \"@angular/core\";\nimport {IMAGELAZYLOAD_DIRECTIVES} from './src/app/directives/image-lazy-load.directive';\nimport {ImageLazyLoaderService} from './src/app/services/image-lazy-load.service';\nimport {WebWorkerService} from './src/app/services/web-worker.service';\n\n// for manual imports\nexport * from './src/app/services/image-lazy-load.service';\nexport * from './src/app/services/web-worker.service';\nexport * from './src/app/directives/image-lazy-load.directive';\n\n@NgModule({\n  declarations: [\n    IMAGELAZYLOAD_DIRECTIVES\n  ],\n  providers: [\n    ImageLazyLoaderService,\n    WebWorkerService\n  ],\n  exports: [\n    IMAGELAZYLOAD_DIRECTIVES\n  ]\n})\nexport class ImageLazyLoadModule {\n\n  static forRoot(configuredProviders: Array<any>): ModuleWithProviders {\n    return {\n      ngModule: ImageLazyLoadModule,\n      providers: configuredProviders\n    };\n  }\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"ng2-image-lazy-load\",\n  \"version\": \"2.0.9\",\n  \"description\": \"Angular2 image lazy loader library\",\n  \"repository\": {\n    \"url\": \"https://github.com/NathanWalker/ng2-image-lazy-load\"\n  },\n  \"main\": \"ng2-image-lazy-load.js\",\n  \"typings\": \"./index.d.ts\",\n  \"author\": \"Nathan Walker <NathanWalker>\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"clean\": \"rimraf node_modules doc dist && npm cache clean\",\n    \"clean-install\": \"npm run clean && npm install\",\n    \"clean-start\": \"npm run clean-install && npm start\",\n    \"watch\": \"webpack --watch --progress --profile\",\n    \"build\": \"rimraf dist && webpack --progress --profile --bail\",\n    \"server\": \"webpack-dashboard -- webpack-dev-server --inline --port 8080\",\n    \"webdriver-update\": \"webdriver-manager update\",\n    \"webdriver-start\": \"webdriver-manager start\",\n    \"lint\": \"tslint --force \\\"src/**/*.ts\\\" --exclude \\\"src/**/*.d.ts\\\"\",\n    \"e2e\": \"protractor\",\n    \"e2e-live\": \"protractor --elementExplorer\",\n    \"pretest\": \"npm run lint\",\n    \"test\": \"karma start\",\n    \"posttest\": \"remap-istanbul -i coverage/json/coverage-final.json -o coverage/html -t html\",\n    \"test-watch\": \"karma start --no-single-run --auto-watch\",\n    \"ci\": \"npm run e2e && npm run test\",\n    \"docs\": \"typedoc --options typedoc.json src/app/app.component.ts\",\n    \"start\": \"npm run server\",\n    \"start:hmr\": \"npm run server -- --hot\",\n    \"postinstall\": \"npm run webdriver-update\"\n  },\n  \"dependencies\": {\n    \"@angular/common\": \"2.0.1\",\n    \"@angular/compiler\": \"2.0.1\",\n    \"@angular/core\": \"2.0.1\",\n    \"@angular/forms\": \"2.0.1\",\n    \"@angular/http\": \"2.0.1\",\n    \"@angular/platform-browser\": \"2.0.1\",\n    \"@angular/platform-browser-dynamic\": \"2.0.1\",\n    \"@angular/router\": \"3.0.1\",\n    \"core-js\": \"^2.4.1\",\n    \"reflect-metadata\": \"^0.1.3\",\n    \"rxjs\": \"5.0.0-beta.12\",\n    \"zone.js\": \"^0.6.21\"\n  },\n  \"devDependencies\": {\n    \"@angularclass/hmr\": \"^1.0.1\",\n    \"@angularclass/hmr-loader\": \"^3.0.2\",\n    \"@types/core-js\": \"^0.9.0\",\n    \"@types/jasmine\": \"^2.2.29\",\n    \"@types/node\": \"^6.0.38\",\n    \"@types/protractor\": \"^1.5.16\",\n    \"@types/selenium-webdriver\": \"2.44.26\",\n    \"angular2-template-loader\": \"^0.4.0\",\n    \"autoprefixer\": \"^6.3.2\",\n    \"awesome-typescript-loader\": \"^2.2.4\",\n    \"codelyzer\": \"0.0.26\",\n    \"copy-webpack-plugin\": \"^3.0.0\",\n    \"css-loader\": \"^0.25.0\",\n    \"extract-text-webpack-plugin\": \"^2.0.0-beta.4\",\n    \"file-loader\": \"^0.9.0\",\n    \"html-loader\": \"^0.4.0\",\n    \"html-webpack-plugin\": \"^2.8.1\",\n    \"istanbul-instrumenter-loader\": \"^0.2.0\",\n    \"jasmine-core\": \"^2.3.4\",\n    \"jasmine-spec-reporter\": \"^2.4.0\",\n    \"json-loader\": \"^0.5.3\",\n    \"karma\": \"1.3.0\",\n    \"karma-chrome-launcher\": \"^2.0.0\",\n    \"karma-coverage\": \"^1.0.0\",\n    \"karma-jasmine\": \"^1.0.2\",\n    \"karma-mocha-reporter\": \"^2.0.3\",\n    \"karma-phantomjs-launcher\": \"^1.0.0\",\n    \"karma-remap-istanbul\": \"0.2.1\",\n    \"karma-sourcemap-loader\": \"^0.3.7\",\n    \"karma-webpack\": \"2.0.1\",\n    \"node-sass\": \"^3.4.2\",\n    \"null-loader\": \"0.1.1\",\n    \"phantomjs-prebuilt\": \"^2.1.4\",\n    \"postcss-loader\": \"^0.13.0\",\n    \"protractor\": \"^3.1.1\",\n    \"raw-loader\": \"0.5.1\",\n    \"remap-istanbul\": \"^0.6.4\",\n    \"rimraf\": \"^2.5.1\",\n    \"sass-loader\": \"^4.0.0\",\n    \"shelljs\": \"^0.7.0\",\n    \"style-loader\": \"^0.13.0\",\n    \"ts-helpers\": \"^1.1.1\",\n    \"tslint\": \"^3.4.0\",\n    \"tslint-loader\": \"^2.1.0\",\n    \"typedoc\": \"^0.4.4\",\n    \"typescript\": \"2.0.2\",\n    \"url-loader\": \"^0.5.6\",\n    \"webpack\": \"^2.1.0-beta.26\",\n    \"webpack-dashboard\": \"^0.1.8\",\n    \"webpack-dev-server\": \"2.1.0-beta.5\"\n  }\n}\n"
  },
  {
    "path": "protractor.conf.js",
    "content": "exports.config = {\n  baseUrl: 'http://localhost:8080/',\n\n  specs: [\n    'src/**/*.e2e-spec.js'\n  ],\n  exclude: [],\n\n  framework: 'jasmine2',\n\n  allScriptsTimeout: 110000,\n\n  jasmineNodeOpts: {\n    showTiming: true,\n    showColors: true,\n    isVerbose: false,\n    includeStackTrace: false,\n    defaultTimeoutInterval: 400000\n  },\n  directConnect: true,\n\n  capabilities: {\n    'browserName': 'chrome'\n  },\n\n  onPrepare: function () {\n    var SpecReporter = require('jasmine-spec-reporter');\n    // add jasmine spec reporter\n    jasmine.getEnv().addReporter(new SpecReporter({displayStacktrace: true}));\n\n    browser.ignoreSynchronization = true;\n  },\n\n\n  /**\n   * Angular 2 configuration\n   *\n   * useAllAngular2AppRoots: tells Protractor to wait for any angular2 apps on the page instead of just the one matching\n   * `rootEl`\n   *\n   */\n  useAllAngular2AppRoots: true\n};\n"
  },
  {
    "path": "src/app/app.component.html",
    "content": "<section class=\"demo\" [ngClass]=\"{'at-top':atTop}\" imageLazyLoadArea>\n  <!-- Above: You can optionally pass threshold, imageLazyLoadArea=\"50\", the default is 100 -->\n  <h3 id=\"top\">Angular2 Image Lazy Loader Demo</h3>\n  <ul>\n    <li><a href=\"#ex1\">Using img tag</a></li>\n    <li><a href=\"#ex2\">Using background-image</a></li>\n    <li><a href=\"#ex3\">Using img tag with custom loading container</a></li>\n    <li><a href=\"#\" (click)=\"addImage()\">Add an image dynamically and scroll down to see it load in.</a></li>\n  </ul>\n  <h3 id=\"ex1\">Using img tag</h3>\n  <h5>As you scroll, the img's lazily load as they scroll into view.</h5>\n  <div *ngFor=\"let image of images\">\n    <div class=\"spinner\"></div>\n    <img [imageLazyLoadItem]=\"image.url\"/>\n  </div>\n  <h3 id=\"ex2\">Using background-image</h3>\n  <h5>As you scroll, images lazily load into the background-image of the containers as they scroll into view.</h5>\n  <div *ngFor=\"let image of images\" [imageLazyLoadItem]=\"image.url\">\n    <div class=\"spinner\"></div>\n  </div>\n  <h3 id=\"ex3\">Using img tag with custom loading container</h3>\n  <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>\n  <div *ngFor=\"let image of images\" class=\"loading-container\" [id]=\"image.id\">\n    <div class=\"spinner\"></div>\n    <div>\n      <img [imageLazyLoadItem]=\"image.url\" [imageLazyLoadingContainer]=\"'.loading-container'\"/>\n    </div>\n  </div>\n  <a class=\"top\" href=\"#top\">Top</a>\n</section>\n"
  },
  {
    "path": "src/app/app.component.scss",
    "content": "// styles applied on :host are applied on the current component, \"app\" in this case\n:host {\n  display: block;\n}\n\nheader {\n  background-color: #fff;\n  padding:0;\n  position: fixed;\n  top: 0;\n  left: 0;\n  width: 100%;\n  box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.5);\n}\n\nh1 {\n  color: #000;\n  text-align: center;\n  font-family: sans-serif;\n}\n\nimg {\n  width:50%;\n}\n\nmain {\n  padding: 1em;\n  font-family: Arial, Helvetica, sans-serif;\n  text-align: center;\n  margin-top: 50px;\n  display: block;\n}\n\nfooter {\n  text-align: center;\n  font-size: 0.8em;\n  width: 100%;\n  position: absolute;\n  bottom: 20px;\n\n  a {\n    color: #fff;\n    text-decoration: none;\n    font-family: sans-serif;\n\n    &:hover {\n      text-decoration: underline;\n    }\n  }\n}\n"
  },
  {
    "path": "src/app/app.component.ts",
    "content": "import { Component, ViewEncapsulation } from '@angular/core';\nimport {Observable} from 'rxjs/Observable';\nimport 'rxjs/add/observable/fromEvent';\nimport 'rxjs/add/operator/debounceTime';\n\nimport '../style/app.scss';\n\n@Component({\n  selector: 'my-app', // <my-app></my-app>\n  templateUrl: './app.component.html',\n  styleUrls: ['./app.component.scss'],\n  encapsulation: ViewEncapsulation.None\n})\nexport class AppComponent {\n  public atTop: boolean = true;\n  public images: Array<any> = [\n    {\n      id: 1,\n      name: `Oneonta Falls 1`,\n      url: `img/oneonta.jpg`\n    },\n    {\n      id: 2,\n      name: `Oneonta Falls 2`,\n      url: `img/DSC01920-L.jpg`\n    },\n    {\n      id: 3,\n      name: `Oneonta Falls 3`,\n      url: `img/oneonta-gorge-800x600.jpg`\n    },\n    {\n      id: 4,\n      name: `Oneonta Falls 4`,\n      url: `img/oneonta-gorge-11.jpg`\n    },\n    {\n      id: 5,\n      name: `Oneonta Falls 5`,\n      url: `img/IMG_2039.jpg`\n    },\n    {\n      id: 6,\n      name: `Oneonta Falls 6`,\n      url: `img/horsetail_falls_9-b.jpg`\n    },\n    {\n      id: 7,\n      name: `Oneonta Falls 7`,\n      url: `img/P1000662-L.jpg`\n    },\n    {\n      id: 8,\n      name: `Oneonta Falls 8`,\n      url: `img/DSC01920-L.jpg`\n    },\n    {\n      id: 9,\n      name: `Oneonta Falls 9`,\n      url: `img/IMG_8356_Medium.JPG`\n    },\n    {\n      id: 10,\n      name: `Oneonta Falls 10`,\n      url: `img/IMG_2036.jpg`\n    }\n  ];\n\n  constructor() {\n    let scrollStream = Observable.fromEvent(window, 'scroll').debounceTime(500);\n\n    scrollStream.subscribe(() => {\n      this.atTop = window.pageYOffset < 25;\n    });\n  }\n\n  public addImage(): void {\n    this.images.push({\n      id: 11,\n      name: `Simpsons as the ${this.images.length + 1}th image!`,\n      url: `img/The-Simpsons-post2.jpg`,\n      added: true\n    });\n    if (window.confirm(`You added an image dynamically, click \"Ok\" to scroll down to see it load in when you get there!`)) {\n      setTimeout(function(){\n        location.hash = '#11';\n      }, 100);\n\n    }\n  }\n}\n"
  },
  {
    "path": "src/app/app.module.ts",
    "content": "import { NgModule } from '@angular/core';\nimport { BrowserModule } from '@angular/platform-browser';\nimport { HttpModule } from '@angular/http';\nimport { FormsModule } from '@angular/forms';\n\nimport { AppComponent } from './app.component';\nimport { IMAGELAZYLOAD_DIRECTIVES } from './directives/image-lazy-load.directive';\nimport { ImageLazyLoaderService } from './services/image-lazy-load.service';\nimport { WebWorkerService } from './services/web-worker.service';\n\nWebWorkerService.workerUrl = 'xhrWorker.js';\n\n@NgModule({\n  imports: [\n    BrowserModule,\n    HttpModule,\n    FormsModule\n  ],\n  declarations: [\n    AppComponent,\n    IMAGELAZYLOAD_DIRECTIVES\n  ],\n  providers: [\n    ImageLazyLoaderService,\n    WebWorkerService\n  ],\n  exports: [\n    IMAGELAZYLOAD_DIRECTIVES\n  ],\n  bootstrap: [AppComponent]\n})\nexport class AppModule {\n\n}\n"
  },
  {
    "path": "src/app/directives/image-lazy-load.directive.ts",
    "content": "import {\n    Directive, ContentChildren, QueryList, Input, ElementRef, Renderer, OnInit, AfterContentInit\n} from '@angular/core';\nimport {Observable} from 'rxjs/Observable';\nimport {Subscription} from 'rxjs/Subscription';\nimport 'rxjs/add/observable/fromEvent';\nimport 'rxjs/add/operator/debounceTime';\nimport {ImageLazyLoaderService, IImageLazyLoadConfig} from '../services/image-lazy-load.service';\n\n@Directive({\n  selector: '[imageLazyLoadItem]'\n})\nexport class ImageLazyLoadItemDirective {\n  @Input('imageLazyLoadItem') imageLazyLoadItem: string;\n  @Input() imageLazyLoadingContainer: string;\n  public loading: boolean = false;\n  public loaded: boolean = false;\n  public error: boolean = false;\n  private tagName: string;\n\n  constructor(private el: ElementRef, private renderer: Renderer, private lazyLoader: ImageLazyLoaderService) {\n    this.tagName = el.nativeElement.tagName;\n  }\n  /*\n  * @returns return position/dimension info as an Object `{top, left, bottom}`.\n  */\n  getPosition() {\n    let box = this.el.nativeElement.getBoundingClientRect();\n    let top = box.top + (window.pageYOffset - document.documentElement.clientTop);\n    return {\n      top: top,\n      left: box.left + (window.pageXOffset - document.documentElement.clientLeft),\n      bottom: top + this.el.nativeElement.clientHeight\n    };\n  }\n  /*\n  * @returns container target to place `loading`/`loaded` classes onto.\n  */\n  getLoadingContainer() {\n    if (this.imageLazyLoadingContainer) {\n      // find parent node with specified selector\n      let collectionHas = (a: any, b: any) => {\n        for (let i in a) {\n          if (a[i] === b) {\n              return true;\n          }\n        }\n        return false;\n      };\n      let all = document.querySelectorAll(this.imageLazyLoadingContainer);\n      let cur = this.el.nativeElement.parentNode;\n      while (cur && !collectionHas(all, cur)) {\n        cur = cur.parentNode;\n      }\n      if (cur) {\n        return cur;\n      } else {\n        // fallback to direct parentNode if not found\n        return this.el.nativeElement.parentNode;\n      }\n    } else {\n      // default is direct parentNode for IMG and the node itself for background-image\n      if (this.tagName === 'IMG') {\n        return this.el.nativeElement.parentNode;\n      } else {\n        return this.el.nativeElement;\n      }\n    }\n  }\n  hasClassName(name: string) {\n    return new RegExp('(?:^|\\\\s+)' + name + '(?:\\\\s+|$)').test(this.getLoadingContainer().className);\n  }\n  addClassName(name: string) {\n    if (!this.hasClassName(name)) {\n      let container = this.getLoadingContainer();\n      container.className = container.className ? [container.className, name].join(' ') : name;\n    }\n  }\n  removeClassName(name: string) {\n    if (this.hasClassName(name)) {\n      let container = this.getLoadingContainer();\n      let c = container.className;\n      container.className = c.replace(new RegExp('(?:^|\\\\s+)' + name + '(?:\\\\s+|$)', 'g'), '');\n    }\n  }\n  toggleLoaded(enable: boolean) {\n    this.loaded = enable;\n    if (enable) {\n      this.removeClassName(this.lazyLoader.config.loadingClass);\n      this.addClassName(this.lazyLoader.config.loadedClass);\n    } else {\n      this.removeClassName(this.lazyLoader.config.loadedClass);\n    }\n  }\n  /*\n  * starts loading the image in the background.\n  */\n  loadImage() {\n    if (!this.loaded && !this.loading) {\n      this.loading = true;\n      this.addClassName(this.lazyLoader.config.loadingClass);\n\n      let customHeaders: any = this.lazyLoader.config.headers ? this.lazyLoader.config.headers : null;\n      this.lazyLoader.load(this.imageLazyLoadItem, customHeaders).then(() => {\n        this.setImage();\n      }, (err) => {\n        this.error = true;\n        this.loading = false;\n        this.removeClassName(this.lazyLoader.config.loadingClass);\n        this.addClassName(this.lazyLoader.config.errorClass);\n      });\n    }\n  }\n  /*\n  * sets the image to `imageLazyLoadItem`.\n  */\n  setImage() {\n    if (!this.loaded) {\n      if (this.tagName === 'IMG') {\n        this.renderer.setElementAttribute(this.el.nativeElement, 'src', this.imageLazyLoadItem);\n      } else {\n        this.renderer.setElementAttribute(this.el.nativeElement, 'style', `background-image:url('${this.imageLazyLoadItem}')`);\n      }\n      this.loading = false;\n      this.toggleLoaded(true);\n    }\n  }\n}\n\n@Directive({\n  selector: '[imageLazyLoadArea]'\n})\nexport class ImageLazyLoadAreaDirective implements OnInit, AfterContentInit {\n  @Input('imageLazyLoadArea') threshold: number;\n  /**\n   * Object that implements IImageLazyLoadConfig:\n   * headers?: any = custom headers\n   * loadingClass?: string = 'custom-loading-class'\n   * loadedClass?: string = 'custom-loaded-class'\n   * errorClass?: string = 'custom-error-class'\n   */\n  @Input() imageLazyLoadConfig: IImageLazyLoadConfig;\n  @ContentChildren(ImageLazyLoadItemDirective) private items: QueryList<ImageLazyLoadItemDirective>;\n  private itemsToLoad: Array<any>;\n  private _sub: Subscription;\n\n  constructor(private lazyLoader: ImageLazyLoaderService) {}\n\n  private loadInView(list?: Array<ImageLazyLoadItemDirective>): void {\n    this.itemsToLoad = (list || this.itemsToLoad).filter((item) => !item.loaded && !item.loading);\n    for (let item of this.itemsToLoad) {\n      let ePos = item.getPosition();\n      if (ePos.bottom > 0 && (ePos.bottom >= (window.pageYOffset - this.threshold))\n          && (ePos.top <= ((window.pageYOffset + window.innerHeight) + this.threshold))) {\n        item.loadImage();\n      }\n    }\n    if (this.itemsToLoad.length === 0 && this._sub) {\n      // subscription is no longer needed\n      this._sub.unsubscribe();\n      this._sub = undefined;\n    }\n  }\n  private scrollSubscribe() {\n    let scrollStream = Observable.fromEvent(window, 'scroll').debounceTime(250);\n\n    this._sub = scrollStream.subscribe(() => {\n      this.loadInView();\n    });\n  }\n  private init() {\n\n    let subScroll = () => {\n      if (!this._sub) {\n        this.scrollSubscribe();\n      }\n    };\n\n    // load the initial children in view\n    if (this.items.length) {\n      // using setTimeout to ensure styles have applied before triggering load\n      setTimeout(() => {\n        this.loadInView(this.items.toArray());\n      });\n    }\n\n    // listen to scroll event\n    subScroll();\n\n    // fired with subsequent changes\n    // ideally this would fire on subscribe but it doesn't\n    // therefore the above ensures it's handled on ngAfterContentInit\n    this.items.changes.subscribe((list) => {\n      this.loadInView(list.toArray());\n      // since scroll subscription is unsuscribed when all items have loaded\n      // ensure it is re-subscribed when changes occur\n      subScroll();\n    });\n  }\n  ngOnInit() {\n    this.threshold = +this.threshold || 100;\n\n    if (typeof (this.imageLazyLoadConfig) === 'object') {\n      this.lazyLoader.config = this.imageLazyLoadConfig;\n    }\n  }\n  ngAfterContentInit() {\n    this.init();\n  }\n}\n\nexport const IMAGELAZYLOAD_DIRECTIVES: any[] = [ImageLazyLoadAreaDirective, ImageLazyLoadItemDirective];\n"
  },
  {
    "path": "src/app/services/image-lazy-load.service.ts",
    "content": "import {Injectable} from '@angular/core';\nimport {Http, Headers, RequestOptions} from '@angular/http';\nimport {WebWorkerService} from './web-worker.service';\n\nexport interface IImageLazyLoadConfig {\n  headers?: any;\n  loadingClass?: string;\n  loadedClass?: string;\n  errorClass?: string;\n}\n\n@Injectable()\nexport class ImageLazyLoaderService {\n  public imageCache: any = {};\n  // default config\n  private _config: IImageLazyLoadConfig = {\n    loadingClass: 'loading',\n    loadedClass: 'loaded',\n    errorClass: 'error'\n  };\n\n  constructor(private http: Http, private worker: WebWorkerService) {}\n  /*\n  * Loads the url via `WebWorker` where supported and gracefully degrades to using `Http` if needed.\n  * @param url  url of image to load.\n  * @param headers  **(optional)** any custom headers (as an `Object`) that may be required to load the image.\n  * @returns return `Promise`\n  */\n  public load(url: string, headers?: any): Promise<any> {\n    if (this.imageCache[url]) {\n      // image has been previously loaded, rely on browser cache\n      return Promise.resolve(true);\n    } else if (WebWorkerService.supported && WebWorkerService.enabled) {\n      return this.loadViaWorker(url, headers);\n    } else {\n      return this.loadViaHttp(url, headers);\n    }\n  }\n  /*\n  * Loads the url via `WebWorker` directly.\n  * @param url  url of image to load.\n  * @param headers  **(optional)** any custom headers (as an `Object`) that may be required to load the image.\n  * @returns return `Promise`\n  */\n  public loadViaWorker(url: string, headers?: any): Promise<any> {\n    return new Promise((resolve, reject) => {\n      let id: any, completeHandler: any, msgFn: any, errorFn: any;\n\n      completeHandler = (success: boolean, err?: any) => {\n        this.worker.terminate(id);\n\n        if (success) {\n          this.imageCache[url] = true;\n          resolve(true);\n        } else {\n          reject(err);\n        }\n      };\n\n      msgFn = (e: any) => {\n        if (e && e.data !== 'ERROR') {\n          completeHandler(true);\n        } else {\n          completeHandler(false, e);\n        }\n      };\n\n      errorFn = (e: any) => {\n        completeHandler(false, e);\n      };\n\n      let config: any = {\n        method: 'GET',\n        url: url\n      };\n\n      // optionally set headers\n      if (headers) {\n        config.headers = headers;\n      }\n      id = this.worker.load(config, msgFn, errorFn);\n    });\n  }\n  /*\n  * Loads the url via `Http` directly.\n  * @param url  url of image to load.\n  * @param headers  **(optional)** any custom headers (as an `Object`) that may be required to load the image.\n  * @returns return `Promise`\n  */\n  public loadViaHttp(url: string, headers?: any): Promise<any> {\n    let ro: RequestOptions = null;\n    if (headers) {\n      ro = new RequestOptions({\n        headers: new Headers(headers)\n      });\n    }\n    return new Promise((resolve, reject) => {\n      this.http.get(url, ro)\n        .subscribe(res => {\n          this.imageCache[url] = true;\n          resolve(true);\n        });\n    });\n  }\n\n  /**\n   * Custom config\n   **/\n  public set config(value: IImageLazyLoadConfig) {\n    if (value) {\n      if (value.headers) {\n          this._config.headers = value.headers;\n      }\n      if (value.loadingClass) {\n          this._config.loadingClass = value.loadingClass;\n      }\n      if (value.loadedClass) {\n          this._config.loadedClass = value.loadedClass;\n      }\n      if (value.errorClass) {\n          this._config.errorClass = value.errorClass;\n      }\n    }\n  }\n\n  public get config(): IImageLazyLoadConfig {\n    return this._config;\n  }\n}\n"
  },
  {
    "path": "src/app/services/web-worker.service.stub.ts",
    "content": "import {Injectable} from '@angular/core';\n\n@Injectable()\nexport class WebWorkerStub {\n  static supported: boolean = true;\n  static workerUrl: string = 'assets/js/xhrWorker.js';\n  public activeWorkers: Array<any> = [];\n  // public workersCalled: number = 0;\n\n  load(config: any, msgFn: any, errorFn?: any): number {\n    let id: number = Math.floor(Math.random() * 1000000000000);\n    this.activeWorkers.push({\n      id: id,\n      config: config\n    });\n    setTimeout(() => {\n     if (msgFn) {\n       msgFn({data: 'success'});\n     } else if (errorFn) {\n       errorFn({data: 'ERROR'});\n     }\n    });\n    // this.workersCalled++;\n    return id;\n  }\n  terminate(id: number): void {\n    let index = this.activeWorkers.findIndex(item => item.id === id);\n    if (index > -1) {\n      this.activeWorkers.splice(index, 1);\n    }\n  }\n}\n"
  },
  {
    "path": "src/app/services/web-worker.service.ts",
    "content": "import {Injectable} from '@angular/core';\n\n@Injectable()\nexport class WebWorkerService {\n  static supported: boolean = typeof (Worker) !== 'undefined';\n  static enabled: boolean = true; // enabled by default, however can be manually turned off\n  static workerUrl: string = 'assets/js/xhrWorker.js';\n  public activeWorkers: Array<any> = [];\n\n  load(config: any, msgFn: any, errorFn?: any): number {\n    if (typeof(config) !== 'object') {\n      throw(`config must be an Object with method and url defined.`);\n    } else if (!config.url) {\n      throw(`config.url must be defined.`);\n    }\n    let id: number = Math.floor(Math.random() * 1000000000000);\n    let w = new Worker(WebWorkerService.workerUrl);\n    if (msgFn) {\n      w.addEventListener('message', msgFn, false);\n    }\n    if (errorFn) {\n      w.addEventListener('error', errorFn, false);\n    }\n\n    this.activeWorkers.push({\n      id: id,\n      worker: w,\n      msgFn: msgFn,\n      errorFn: errorFn\n    });\n    w.postMessage(config);\n    return id;\n  }\n  terminate(id: number): void {\n    let index = this.activeWorkers.findIndex(item => item.id === id);\n    if (index > -1) {\n      let activeWorker = this.activeWorkers[index];\n      if (activeWorker && activeWorker.worker) {\n        if (activeWorker.worker.msgFn) {\n          activeWorker.worker.removeEventListener('message', activeWorker.worker.msgFn);\n        }\n        if (activeWorker.worker.errorFn) {\n          activeWorker.worker.removeEventListener('error', activeWorker.worker.errorFn);\n        }\n        activeWorker.worker.terminate();\n      }\n      this.activeWorkers.splice(index, 1);\n    }\n  }\n}\n"
  },
  {
    "path": "src/main.ts",
    "content": "import { enableProdMode } from '@angular/core';\nimport { platformBrowserDynamic } from '@angular/platform-browser-dynamic';\nimport { AppModule } from './app/app.module';\n\n// depending on the env mode, enable prod mode or add debugging modules\nif (process.env.ENV === 'build') {\n  enableProdMode();\n}\n\nexport function main() {\n  return platformBrowserDynamic().bootstrapModule(AppModule);\n}\n\nif (document.readyState === 'complete') {\n  main();\n} else {\n  document.addEventListener('DOMContentLoaded', main);\n}\n"
  },
  {
    "path": "src/polyfills.ts",
    "content": "import 'core-js/client/shim';\nimport 'reflect-metadata';\nrequire('zone.js/dist/zone');\n\nimport 'ts-helpers';\n\nif (process.env.ENV === 'build') {\n  // Production\n\n} else {\n  // Development\n\n  Error['stackTraceLimit'] = Infinity;\n\n  require('zone.js/dist/long-stack-trace-zone');\n}\n"
  },
  {
    "path": "src/public/index.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Angular2 Image Lazy Loader</title>\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"/img/favicon.ico\">\n\n    <base href=\"/\">\n  </head>\n  <body>\n    <my-app><div class=\"spinner\"></div></my-app>\n  </body>\n</html>\n"
  },
  {
    "path": "src/public/xhrWorker.js",
    "content": "self.addEventListener('message', function(e) {\n  var data, header, xhr;\n  data = e.data;\n  xhr = new XMLHttpRequest();\n  xhr.onreadystatechange = function() {\n    if (xhr.readyState === 4) {\n      if (xhr.status === 200) {\n        postMessage(xhr.responseText);\n      } else {\n        postMessage('ERROR');\n      }\n      return self.close();\n    }\n  };\n  xhr.onerror = function() {\n    postMessage('ERROR');\n    return self.close();\n  };\n  xhr.open(data.method, data.url);\n  if (data.headers) {\n    for (header in data.headers) {\n      if (data.headers[header]) {\n        xhr.setRequestHeader(header, data.headers[header]);\n      }\n    }\n  }\n  return xhr.send();\n});\n"
  },
  {
    "path": "src/style/app.scss",
    "content": "/* Reset */\nhtml,\nbody,\ndiv {\n  border: 0;\n  margin: 0;\n  padding: 0;\n}\n\n/* Box-sizing border-box */\n* {\n  box-sizing: border-box;\n}\n\n/* Set up a default font and some padding to provide breathing room */\nbody {\n  font-family: Roboto, \"Helvetica Neue\", sans-serif;\n  font-size: 16px;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\np {\n  font-weight: 400;\n  letter-spacing: 0.01em;\n  line-height: 20px;\n  margin-bottom: 1em;\n  margin-top: 1em;\n}\n\nul {\n  margin: 10px 0 0 0;\n  padding: 0 0 0 20px;\n}\n\nli {\n  font-weight: 400;\n  margin-top: 4px;\n}\n\ninput {\n  border: 1px solid #106cc8;\n  font-size: 14px;\n  height: 40px;\n  outline: none;\n  padding: 8px;\n}\n\nbutton {\n  background-color: #106cc8;\n  border-style: none;\n  color: rgba(255, 255, 255, 0.87);\n  cursor: pointer;\n  display: inline-block;\n  font-size: 14px;\n  height: 40px;\n  padding: 8px 18px;\n  text-decoration: none;\n}\n\nbutton:hover {\n  background-color: #28739e;\n}\n\nbody {\n  background-color:rgba(0,0,0,.4);\n}\nh1, h2, h3, h5 {\n  text-align: center;\n  width: 90%;\n  margin: 10px auto;\n}\n.spinner:before {\n  transform: translate3d(0, 0, 0);\n  -ms-transform: translate3d(0, 0, 0);\n  -moz-transform: translate3d(0, 0, 0);\n  -webkit-transform: translate3d(0, 0, 0);\n  -o-transform: translate3d(0, 0, 0);\n  -webkit-transition: opacity 0.5s ease-out;\n  -moz-transition: opacity 0.5s ease-out;\n  -o-transition: opacity 0.5s ease-out;\n  transition: opacity 0.5s ease-out;\n  -webkit-animation: rotating 1s linear infinite;\n  -moz-animation: rotating 1s linear infinite;\n  -ms-animation: rotating 1s linear infinite;\n  -o-animation: rotating 1s linear infinite;\n  animation: rotating 1s linear infinite;\n  opacity:1;\n  top: 0;\n  left: 0;\n  bottom: 0;\n  right: 0;\n  position: absolute;\n  margin: auto;\n  color: rgba(0, 0, 0, 0.3);\n  content: \"\";\n  color: #fff;\n  background-image: url(\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxOC4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KICAgdmlld0JveD0iMCAwIDI5MCAyOTAiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDI5MCAyOTAiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPHBhdGggZmlsbD0iI2ZmZmZmZiIgZD0iTTE0NSwyNDEuNmMtNTMuMywwLTk2LjYtNDMuMi05Ni42LTk2LjZjMC01My4zLDQzLjItOTYuNiw5Ni42LTk2LjZjNTMuMywwLDk2LjYsNDMuMiw5Ni42LDk2LjYNCiAgYzAsMjYuNy0xMC44LDUwLjktMjguMyw2OC4zbDcuNiw3LjZjMTkuNC0xOS40LDMxLjUtNDYuMywzMS41LTc1LjljMC01OS4zLTQ4LTEwNy4zLTEwNy4zLTEwNy4zUzM3LjcsODUuNywzNy43LDE0NQ0KICBjMCw1OS4zLDQ4LDEwNy4zLDEwNy4zLDEwNy4zVjI0MS42eiIvPg0KPC9zdmc+DQo=\");\n  background-size: 50px 50px;\n  height: 50px;\n  width: 50px;\n  background-repeat: no-repeat;\n}\n.demo {\n  font-family: Verdana;\n}\n.demo a {\n  display:block;\n  text-align: center;\n  cursor:pointer;\n}\n.demo a.top {\n  position: fixed;\n  bottom:10px;\n  right:15px;\n  font-size:14px;\n  z-index: 10;\n  background-color: rgba(0,0,0,.5);\n  color:#fff;\n  padding:8px 12px;\n  border-radius: 4px;\n  opacity:1;\n  -webkit-transition: opacity 0.5s ease-out;\n  -moz-transition: opacity 0.5s ease-out;\n  -o-transition: opacity 0.5s ease-out;\n  transition: opacity 0.5s ease-out;\n}\n.demo.at-top a.top {\n  opacity: 0; \n}\n.demo ul {\n  width: 88%;\n  margin: 0 auto;\n  background-color: rgba(255,255,255,.5);\n  border-radius: 4px;\n  padding-top: 10px;\n  padding-bottom: 10px;\n  max-width: 420px;\n}\n.demo ul li a {\n  text-align: left;\n}\n.demo > div {\n  margin:10px auto;\n  min-height: 280px;\n  max-height:500px;\n  background-repeat: no-repeat;\n  background-position: center center;\n  background-size: cover;\n  position: relative;\n}\n.demo > div[style] {\n  width:95%;\n  -webkit-box-shadow: 0 0 4px rgba(0,0,0,.6);\n  -moz-box-shadow: 0 0 4px rgba(0,0,0,.6);\n  box-shadow: 0 0 4px rgba(0,0,0,.6);\n}\n.demo > div img {\n  display: block;\n  margin:0 auto;\n  max-width: 100%;\n  max-height: 500px;\n  -webkit-box-shadow: 0 0 4px rgba(0,0,0,.6);\n  -moz-box-shadow: 0 0 4px rgba(0,0,0,.6);\n  box-shadow: 0 0 4px rgba(0,0,0,.6);\n  -webkit-transition: opacity 0.5s ease-out;\n  -moz-transition: opacity 0.5s ease-out;\n  -o-transition: opacity 0.5s ease-out;\n  transition: opacity 0.5s ease-out;\n  opacity:0;\n}\n.demo > div.loaded img {\n  opacity:1;\n}\n.demo > div.loaded .spinner:before {\n  opacity:0;\n}\n@media (min-width:670px) {\n  .demo > div {\n    min-height: 500px;\n  }\n}\n\n\n@keyframes rotating {\n  from {\n    transform: rotate(0deg);\n  }\n  to {\n    transform: rotate(360deg);\n  }\n}\n@-moz-keyframes rotating {\n  from {\n    -moz-transform: rotate(0deg);\n  }\n  to {\n    -moz-transform: rotate(360deg);\n  }\n}\n@-ms-keyframes rotating {\n  from {\n    -ms-transform: rotate(0deg);\n  }\n  to {\n    -ms-transform: rotate(360deg);\n  }\n}\n@-o-keyframes rotating {\n  from {\n    -o-transform: rotate(0deg);\n  }\n  to {\n    -o-transform: rotate(360deg);\n  }\n}\n@-webkit-keyframes rotating {\n  from {\n    -webkit-transform: rotate(0deg);\n  }\n  to {\n    -webkit-transform: rotate(360deg);\n  }\n}\n"
  },
  {
    "path": "src/vendor.ts",
    "content": "// Angular 2\nimport '@angular/platform-browser';\nimport '@angular/platform-browser-dynamic';\nimport '@angular/core';\nimport '@angular/common';\nimport '@angular/http';\nimport '@angular/router';\n\nimport 'rxjs';\nimport '@angularclass/hmr';\n\n// Other vendors for example jQuery, Lodash or Bootstrap\n// You can import js, ts, css, sass, ...\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES5\",\n    \"module\": \"commonjs\",\n    \"emitDecoratorMetadata\": true,\n    \"experimentalDecorators\": true,\n    \"sourceMap\": true,\n    \"noEmitHelpers\": true\n  },\n  \"compileOnSave\": false,\n  \"buildOnSave\": false,\n  \"awesomeTypescriptLoaderOptions\": {\n    \"forkChecker\": true,\n    \"useWebpackText\": true\n  }\n}\n"
  },
  {
    "path": "tslint.json",
    "content": "{\n  \"rulesDirectory\": [\n    \"node_modules/codelyzer\"\n  ],\n  \"rules\": {\n    \"class-name\": true,\n    \"comment-format\": [\n      true,\n      \"check-space\"\n    ],\n    \"curly\": true,\n    \"eofline\": true,\n    \"forin\": true,\n    \"indent\": [\n      true,\n      \"spaces\"\n    ],\n    \"label-position\": true,\n    \"label-undefined\": true,\n    \"max-line-length\": [\n      true,\n      140\n    ],\n    \"member-access\": false,\n    \"member-ordering\": [\n      true,\n      \"static-before-instance\",\n      \"variables-before-functions\"\n    ],\n    \"no-arg\": true,\n    \"no-bitwise\": true,\n    \"no-console\": [\n      true,\n      \"debug\",\n      \"info\",\n      \"time\",\n      \"timeEnd\",\n      \"trace\"\n    ],\n    \"no-construct\": true,\n    \"no-debugger\": true,\n    \"no-duplicate-key\": true,\n    \"no-duplicate-variable\": true,\n    \"no-empty\": false,\n    \"no-eval\": true,\n    \"no-inferrable-types\": true,\n    \"no-shadowed-variable\": true,\n    \"no-string-literal\": false,\n    \"no-switch-case-fall-through\": true,\n    \"no-trailing-whitespace\": false,\n    \"no-unused-expression\": true,\n    \"no-unused-variable\": true,\n    \"no-unreachable\": true,\n    \"no-use-before-declare\": true,\n    \"no-var-keyword\": true,\n    \"object-literal-sort-keys\": false,\n    \"one-line\": [\n      true,\n      \"check-catch\",\n      \"check-else\",\n      \"check-whitespace\"\n    ],\n    \"quotemark\": [\n      true,\n      \"single\"\n    ],\n    \"radix\": true,\n    \"semicolon\": [\n      \"always\"\n    ],\n    \"triple-equals\": [\n      true,\n      \"allow-null-check\"\n    ],\n    \"typedef-whitespace\": [\n      true,\n      {\n        \"call-signature\": \"nospace\",\n        \"index-signature\": \"nospace\",\n        \"parameter\": \"nospace\",\n        \"property-declaration\": \"nospace\",\n        \"variable-declaration\": \"nospace\"\n      }\n    ],\n    \"variable-name\": false,\n    \"whitespace\": [\n      true,\n      \"check-branch\",\n      \"check-decl\",\n      \"check-operator\",\n      \"check-separator\",\n      \"check-type\"\n    ],\n\n    \"directive-selector-name\": [true, \"camelCase\"],\n    \"component-selector-name\": [true, \"kebab-case\"],\n    \"directive-selector-type\": [true, \"attribute\"],\n    \"component-selector-type\": [true, \"element\"],\n    \"use-input-property-decorator\": true,\n    \"use-output-property-decorator\": true,\n    \"use-host-property-decorator\": true,\n    \"no-input-rename\": true,\n    \"no-output-rename\": true,\n    \"use-life-cycle-interface\": true,\n    \"use-pipe-transform-interface\": true,\n    \"component-class-suffix\": true,\n    \"directive-class-suffix\": true,\n    \"directive-selector-prefix\": [true, \"my\"],\n    \"component-selector-prefix\": [true, \"my\"],\n    \"pipe-naming\": [true, \"camelCase\", \"my\"]\n  }\n}\n"
  },
  {
    "path": "typedoc.json",
    "content": "{\n  \"mode\": \"modules\",\n  \"out\": \"doc\",\n  \"theme\": \"default\",\n  \"ignoreCompilerErrors\": \"true\",\n  \"experimentalDecorators\": \"true\",\n  \"emitDecoratorMetadata\": \"true\",\n  \"target\": \"ES5\",\n  \"moduleResolution\": \"node\",\n  \"preserveConstEnums\": \"true\",\n  \"stripInternal\": \"true\",\n  \"suppressExcessPropertyErrors\": \"true\",\n  \"suppressImplicitAnyIndexErrors\": \"true\",\n  \"module\": \"commonjs\"\n}"
  },
  {
    "path": "webpack.config.js",
    "content": "// Helper: root() is defined at the bottom\nvar path = require('path');\nvar webpack = require('webpack');\n\n// Webpack Plugins\nvar CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin;\nvar autoprefixer = require('autoprefixer');\nvar HtmlWebpackPlugin = require('html-webpack-plugin');\nvar ExtractTextPlugin = require('extract-text-webpack-plugin');\nvar CopyWebpackPlugin = require('copy-webpack-plugin');\nvar DashboardPlugin = require('webpack-dashboard/plugin');\nvar ForkCheckerPlugin = require('awesome-typescript-loader').ForkCheckerPlugin;\n\n/**\n * Env\n * Get npm lifecycle event to identify the environment\n */\nvar ENV = process.env.npm_lifecycle_event;\nvar isTestWatch = ENV === 'test-watch';\nvar isTest = ENV === 'test' || isTestWatch;\nvar isProd = ENV === 'build';\n\nmodule.exports = function makeWebpackConfig() {\n  /**\n   * Config\n   * Reference: http://webpack.github.io/docs/configuration.html\n   * This is the object where all configuration gets set\n   */\n  var config = {};\n\n  /**\n   * Devtool\n   * Reference: http://webpack.github.io/docs/configuration.html#devtool\n   * Type of sourcemap to use per build type\n   */\n  if (isProd) {\n    config.devtool = 'source-map';\n  }\n  else if (isTest) {\n    config.devtool = 'inline-source-map';\n  }\n  else {\n    config.devtool = 'eval-source-map';\n  }\n\n  /**\n   * Entry\n   * Reference: http://webpack.github.io/docs/configuration.html#entry\n   */\n  config.entry = isTest ? function(){return {}} : {\n    'polyfills': './src/polyfills.ts',\n    'vendor': './src/vendor.ts',\n    'app': './src/main.ts' // our angular app\n  };\n\n  /**\n   * Output\n   * Reference: http://webpack.github.io/docs/configuration.html#output\n   */\n  config.output = isTest ? {} : {\n    path: root('dist'),\n    publicPath: isProd ? '/' : 'http://localhost:8080/',\n    filename: isProd ? 'js/[name].[hash].js' : 'js/[name].js',\n    chunkFilename: isProd ? '[id].[hash].chunk.js' : '[id].chunk.js'\n  };\n\n  /**\n   * Resolve\n   * Reference: http://webpack.github.io/docs/configuration.html#resolve\n   */\n  config.resolve = {\n    // only discover files that have those extensions\n    extensions: ['.ts', '.js', '.json', '.css', '.scss', '.html'],\n  };\n\n  var atlOptions = '';\n  if (isTest && !isTestWatch) {\n    // awesome-typescript-loader needs to output inlineSourceMap for code coverage to work with source maps.\n    atlOptions = 'inlineSourceMap=true&sourceMap=false';\n  }\n\n  /**\n   * Loaders\n   * Reference: http://webpack.github.io/docs/configuration.html#module-loaders\n   * List: http://webpack.github.io/docs/list-of-loaders.html\n   * This handles most of the magic responsible for converting modules\n   */\n  config.module = {\n    rules: [\n      // Support for .ts files.\n      {\n        test: /\\.ts$/,\n        loaders: ['awesome-typescript-loader?' + atlOptions, 'angular2-template-loader', '@angularclass/hmr-loader'],\n        exclude: [isTest ? /\\.(e2e)\\.ts$/ : /\\.(spec|e2e)\\.ts$/, /node_modules\\/(?!(ng2-.+))/]\n      },\n\n      // copy those assets to output\n      {\n        test: /\\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)(\\?v=[0-9]\\.[0-9]\\.[0-9])?$/,\n        loader: 'file-loader?name=fonts/[name].[hash].[ext]?'\n      },\n\n      // Support for *.json files.\n      {test: /\\.json$/, loader: 'json-loader'},\n\n      // Support for CSS as raw text\n      // use 'null' loader in test mode (https://github.com/webpack/null-loader)\n      // all css in src/style will be bundled in an external css file\n      {\n        test: /\\.css$/,\n        exclude: root('src', 'app'),\n          loader: isTest ? 'null-loader' : ExtractTextPlugin.extract({\n              fallbackLoader: 'style-loader',\n              loader: ['css-loader', 'postcss-loader']\n          })\n      },\n      // all css required in src/app files will be merged in js files\n      {test: /\\.css$/, include: root('src', 'app'), loader: 'raw-loader!postcss-loader'},\n\n      // support for .scss files\n      // use 'null' loader in test mode (https://github.com/webpack/null-loader)\n      // all css in src/style will be bundled in an external css file\n      {\n        test: /\\.scss$/,\n        exclude: root('src', 'app'),\n          loader: isTest ? 'null-loader' : ExtractTextPlugin.extract({\n              fallbackLoader: 'style-loader',\n              loader: ['css-loader', 'postcss-loader', 'sass-loader']\n          })\n      },\n      // all css required in src/app files will be merged in js files\n      {test: /\\.scss$/, exclude: root('src', 'style'), loader: 'raw-loader!postcss-loader!sass-loader'},\n\n      // support for .html as raw text\n      // todo: change the loader to something that adds a hash to images\n      {test: /\\.html$/, loader: 'raw-loader',  exclude: root('src', 'public')}\n    ]\n  };\n\n  if (isTest && !isTestWatch) {\n    // instrument only testing sources with Istanbul, covers ts files\n    config.module.rules.push({\n      test: /\\.ts$/,\n      enforce: 'post',\n      include: path.resolve('src'),\n      loader: 'istanbul-instrumenter-loader',\n      exclude: [/\\.spec\\.ts$/, /\\.e2e\\.ts$/, /node_modules/]\n    });\n  }\n\n  if (!isTest || !isTestWatch) {\n    // tslint support\n    config.module.rules.push({\n      test: /\\.ts$/,\n      enforce: 'pre',\n      loader: 'tslint-loader'\n    });\n  }\n\n  /**\n   * Plugins\n   * Reference: http://webpack.github.io/docs/configuration.html#plugins\n   * List: http://webpack.github.io/docs/list-of-plugins.html\n   */\n  config.plugins = [\n    // Define env variables to help with builds\n    // Reference: https://webpack.github.io/docs/list-of-plugins.html#defineplugin\n    new webpack.DefinePlugin({\n      // Environment helpers\n      'process.env': {\n        ENV: JSON.stringify(ENV)\n      }\n    }),\n\n    // Workaround needed for angular 2 angular/angular#11580\n      new webpack.ContextReplacementPlugin(\n        // The (\\\\|\\/) piece accounts for path separators in *nix and Windows\n        /angular(\\\\|\\/)core(\\\\|\\/)(esm(\\\\|\\/)src|src)(\\\\|\\/)linker/,\n        root('./src') // location of your src\n      ),\n\n    // Tslint configuration for webpack 2\n    new webpack.LoaderOptionsPlugin({\n      options: {\n        /**\n         * Apply the tslint loader as pre/postLoader\n         * Reference: https://github.com/wbuchwalter/tslint-loader\n         */\n        tslint: {\n          emitErrors: false,\n          failOnHint: false\n        },\n        /**\n         * Sass\n         * Reference: https://github.com/jtangelder/sass-loader\n         * Transforms .scss files to .css\n         */\n        sassLoader: {\n          //includePaths: [path.resolve(__dirname, \"node_modules/foundation-sites/scss\")]\n        },\n        /**\n         * PostCSS\n         * Reference: https://github.com/postcss/autoprefixer-core\n         * Add vendor prefixes to your css\n         */\n        postcss: [\n          autoprefixer({\n            browsers: ['last 2 version']\n          })\n        ]\n      }\n    })\n  ];\n\n  if (!isTest && !isProd) {\n      config.plugins.push(new DashboardPlugin());\n  }\n\n  if (!isTest && !isTestWatch) {\n    config.plugins.push(\n      new ForkCheckerPlugin(),\n\n      // Generate common chunks if necessary\n      // Reference: https://webpack.github.io/docs/code-splitting.html\n      // Reference: https://webpack.github.io/docs/list-of-plugins.html#commonschunkplugin\n      new CommonsChunkPlugin({\n        name: ['vendor', 'polyfills']\n      }),\n\n      // Inject script and link tags into html files\n      // Reference: https://github.com/ampedandwired/html-webpack-plugin\n      new HtmlWebpackPlugin({\n        template: './src/public/index.html',\n        chunksSortMode: 'dependency'\n      }),\n\n      // Extract css files\n      // Reference: https://github.com/webpack/extract-text-webpack-plugin\n      // Disabled when in test mode or not in build mode\n      new ExtractTextPlugin({filename: 'css/[name].[hash].css', disable: !isProd})\n    );\n  }\n\n  // Add build specific plugins\n  if (isProd) {\n    config.plugins.push(\n      // Reference: http://webpack.github.io/docs/list-of-plugins.html#noerrorsplugin\n      // Only emit files when there are no errors\n      new webpack.NoErrorsPlugin(),\n\n      // // Reference: http://webpack.github.io/docs/list-of-plugins.html#dedupeplugin\n      // // Dedupe modules in the output\n      // new webpack.optimize.DedupePlugin(),\n\n      // Reference: http://webpack.github.io/docs/list-of-plugins.html#uglifyjsplugin\n      // Minify all javascript, switch loaders to minimizing mode\n      new webpack.optimize.UglifyJsPlugin({sourceMap: true, mangle: { keep_fnames: true }}),\n\n      // Copy assets from the public folder\n      // Reference: https://github.com/kevlened/copy-webpack-plugin\n      new CopyWebpackPlugin([{\n        from: root('src/public')\n      }])\n    );\n  }\n\n  /**\n   * Dev server configuration\n   * Reference: http://webpack.github.io/docs/configuration.html#devserver\n   * Reference: http://webpack.github.io/docs/webpack-dev-server.html\n   */\n  config.devServer = {\n    contentBase: './src/public',\n    historyApiFallback: true,\n    quiet: true,\n    stats: 'minimal' // none (or false), errors-only, minimal, normal (or true) and verbose\n  };\n\n  return config;\n}();\n\n// Helper functions\nfunction root(args) {\n  args = Array.prototype.slice.call(arguments, 0);\n  return path.join.apply(path, [__dirname].concat(args));\n}\n"
  }
]