diff --git a/index.ts b/index.ts index 9bdf81e0..35e1de31 100644 --- a/index.ts +++ b/index.ts @@ -1,5 +1,15 @@ +import { NgFileDropDirective } from './src/directives/ng-file-drop'; +import { NgFileSelectDirective } from './src/directives/ng-file-select'; + export * from './src/directives/ng-file-drop'; export * from './src/directives/ng-file-select'; export * from './src/services/ng2-uploader'; -export { Ng2UploaderModule } from './src/module/ng2-uploader.module'; \ No newline at end of file +export { Ng2UploaderOptions, UploadedFile, UploadRejected } from './src/classes'; + +export { Ng2UploaderModule } from './src/module/ng2-uploader.module'; + +export const UPLOAD_DIRECTIVES: any[] = [ + NgFileSelectDirective, + NgFileDropDirective +]; diff --git a/package.json b/package.json index f0158e93..127edfa0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "ng2-uploader", "description": "Angular2 File Uploader", - "version": "1.6.2", + "version": "2.0.0", "license": "MIT", "main": "index.js", "typings": "index.d.ts", @@ -25,18 +25,18 @@ "uploader" ], "devDependencies": { - "@angular/common": "^2.2.3", - "@angular/compiler": "^2.2.3", - "@angular/compiler-cli": "^2.2.3", - "@angular/core": "^2.2.3", - "@angular/platform-browser": "^2.2.3", - "@angular/platform-browser-dynamic": "^2.2.3", - "@angular/platform-server": "^2.2.3", + "@angular/common": "^2.4.1", + "@angular/compiler": "^2.4.1", + "@angular/compiler-cli": "^2.4.1", + "@angular/core": "^2.4.1", + "@angular/platform-browser": "^2.4.1", + "@angular/platform-browser-dynamic": "^2.4.1", + "@angular/platform-server": "^2.4.1", "@types/core-js": "^0.9.35", - "@types/node": "^6.0.51", - "reflect-metadata": "^0.1.8", - "rxjs": "5.0.0-rc.4", - "typescript": "2.1.4", - "zone.js": "^0.7.2" + "@types/node": "^6.0.52", + "reflect-metadata": "^0.1.9", + "rxjs": "5.0.1", + "typescript": "2.0.10", + "zone.js": "^0.7.4" } } diff --git a/src/classes/index.ts b/src/classes/index.ts new file mode 100644 index 00000000..0dd20451 --- /dev/null +++ b/src/classes/index.ts @@ -0,0 +1,3 @@ +export * from './ng2-uploader-options.class'; +export * from './uploaded-file.class'; +export * from './upload-rejected.class'; diff --git a/src/classes/ng2-uploader-options.class.ts b/src/classes/ng2-uploader-options.class.ts new file mode 100644 index 00000000..2a69919b --- /dev/null +++ b/src/classes/ng2-uploader-options.class.ts @@ -0,0 +1,65 @@ +export interface INg2UploaderOptions { + url: string; + cors?: boolean; + withCredentials?: boolean; + multiple?: boolean; + maxUploads?: number; + data?: any; + autoUpload?: boolean; + multipart?: any; + method?: 'POST' | 'GET'; + customHeaders?: any; + encodeHeaders?: boolean; + authTokenPrefix?: string; + authToken?: string; + fieldName?: string; + fieldReset?: boolean; + previewUrl?: string; + calculateSpeed?: boolean; + filterExtensions?: boolean; + allowedExtensions?: string[]; +} + +export class Ng2UploaderOptions implements INg2UploaderOptions { + url: string; + cors?: boolean; + withCredentials?: boolean; + multiple?: boolean; + maxUploads?: number; + data?: any; + autoUpload?: boolean; + multipart?: any; + method?: 'POST' | 'GET'; + customHeaders?: any; + encodeHeaders?: boolean; + authTokenPrefix?: string; + authToken?: string; + fieldName?: string; + fieldReset?: boolean; + previewUrl?: string; + calculateSpeed?: boolean; + filterExtensions?: boolean; + allowedExtensions?: string[]; + + constructor(obj: INg2UploaderOptions) { + this.url = obj.url != null ? obj.url : ''; + this.cors = obj.cors != null ? obj.cors : true; + this.withCredentials = obj.withCredentials != null ? obj.withCredentials : this.withCredentials; + this.multiple = obj.multiple != null ? obj.multiple : true; + this.maxUploads = obj.maxUploads != null ? obj.maxUploads : 10; + this.data = obj.data != null ? obj.data : {}; + this.autoUpload = obj && obj.autoUpload ? obj.autoUpload : true; + this.multipart = obj.multipart != null ? obj.multipart : false; + this.method = obj.method != null ? obj.method : 'POST'; + this.customHeaders = obj.customHeaders != null ? obj.customHeaders : { }; + this.encodeHeaders = obj.encodeHeaders != null ? obj.encodeHeaders : false; + this.authTokenPrefix = obj.authTokenPrefix != null ? obj.authTokenPrefix : 'Bearer'; + this.authToken = obj.authToken != null ? obj.authToken : null; + this.fieldName = obj.fieldName != null ? obj.fieldName : 'file'; + this.fieldReset = obj.fieldReset != null ? obj.fieldReset : null; + this.previewUrl = obj.previewUrl != null ? obj.previewUrl : null; + this.calculateSpeed = obj.calculateSpeed != null ? obj.calculateSpeed : true; + this.filterExtensions = obj.filterExtensions != null ? obj.filterExtensions : false; + this.allowedExtensions = obj && obj.allowedExtensions ? obj.allowedExtensions : []; + } +} diff --git a/src/classes/upload-rejected.class.ts b/src/classes/upload-rejected.class.ts new file mode 100644 index 00000000..93fc2c34 --- /dev/null +++ b/src/classes/upload-rejected.class.ts @@ -0,0 +1,7 @@ +export class UploadRejected { + public static get EXTENSION_NOT_ALLOWED():string { return 'ExtensionNotAllowed'; } + public static get MAX_SIZE_EXCEEDED():string { return 'MaxSizeExceeded'; } + + file: any; + reason: string; +} diff --git a/src/classes/uploaded-file.class.ts b/src/classes/uploaded-file.class.ts new file mode 100644 index 00000000..b30014b3 --- /dev/null +++ b/src/classes/uploaded-file.class.ts @@ -0,0 +1,72 @@ +export class UploadedFile { + id: string; + status: number; + statusText: string; + progress: Object; + originalName: string; + size: number; + response: string; + done: boolean; + error: boolean; + abort: boolean; + startTime: number; + endTime: number; + speedAverage: number; + speedAverageHumanized: string; + + constructor(id: string, originalName: string, size: number) { + this.id = id; + this.originalName = originalName; + this.size = size; + this.progress = { + loaded: 0, + total: 0, + percent: 0, + speed: 0, + speedHumanized: null + }; + this.done = false; + this.error = false; + this.abort = false; + this.startTime = new Date().getTime(); + this.endTime = 0; + this.speedAverage = 0; + this.speedAverageHumanized = null; + } + + setProgres(progress: Object): void { + this.progress = progress; + } + + setError(): void { + this.error = true; + this.done = true; + } + + setAbort(): void { + this.abort = true; + this.done = true; + } + + onFinished(status: number, statusText: string, response: string): void { + this.endTime = new Date().getTime(); + this.speedAverage = this.size / (this.endTime - this.startTime) * 1000; + this.speedAverage = parseInt(this.speedAverage, 10); + this.speedAverageHumanized = this.humanizeBytes(this.speedAverage); + this.status = status; + this.statusText = statusText; + this.response = response; + this.done = true; + } + + humanizeBytes(bytes: number): string { + if (bytes === 0) { + return '0 Byte'; + } + let k = 1024; + const sizes: string[] = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB']; + let i: number = Math.floor(Math.log(bytes) / Math.log(k)); + + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i] + '/s'; + } +} diff --git a/src/directives/ng-file-drop.ts b/src/directives/ng-file-drop.ts index 47c3e702..42208d4c 100644 --- a/src/directives/ng-file-drop.ts +++ b/src/directives/ng-file-drop.ts @@ -4,15 +4,19 @@ import { EventEmitter, Input, Output, - HostListener + HostListener, + Inject, + OnChanges, + OnInit } from '@angular/core'; -import { Ng2Uploader, UploadRejected, UploadedFile } from '../services/ng2-uploader'; +import { Ng2UploaderService } from '../services/ng2-uploader'; +import { INg2UploaderOptions, Ng2UploaderOptions, UploadedFile, UploadRejected } from '../classes'; @Directive({ selector: '[ngFileDrop]' }) -export class NgFileDropDirective { - +export class NgFileDropDirective implements OnChanges, OnInit { + @Input() options: Ng2UploaderOptions; @Input() events: EventEmitter; @Output() onUpload: EventEmitter = new EventEmitter(); @Output() onPreviewData: EventEmitter = new EventEmitter(); @@ -20,27 +24,13 @@ export class NgFileDropDirective { @Output() onUploadRejected: EventEmitter = new EventEmitter(); @Output() beforeUpload: EventEmitter = new EventEmitter(); - _options:any; - - @Input('options') - set options(value: any) { - this._options = value; - this.uploader.setOptions(this.options); - } - - get options(): any { - return this._options; - } - files: any[] = []; - uploader: Ng2Uploader; - constructor(public el: ElementRef) { - this.uploader = new Ng2Uploader(); - setTimeout(() => { - this.uploader.setOptions(this.options); - }); + constructor( + @Inject(ElementRef) public el: ElementRef, + @Inject(Ng2UploaderService) public uploader: Ng2UploaderService) { } + ngOnInit() { this.uploader._emitter.subscribe((data: any) => { this.onUpload.emit(data); if (data.done) { @@ -69,6 +59,15 @@ export class NgFileDropDirective { this.initEvents(); } + ngOnChanges() { + if (!this.options) { + return; + } + + this.options = new Ng2UploaderOptions(this.options); + this.uploader.setOptions(this.options); + } + initEvents(): void { if (typeof this.el.nativeElement.addEventListener === 'undefined') { return; @@ -95,32 +94,28 @@ export class NgFileDropDirective { }, false); } - filterFilesByExtension(): void { - this.files = this.files.filter(f => { - if (this.options.allowedExtensions.indexOf(f.type) !== -1) { - return true; - } - - let ext: string = f.name.split('.').pop(); - if (this.options.allowedExtensions.indexOf(ext) !== -1 ) { - return true; - } - - this.onUploadRejected.emit({file: f, reason: UploadRejected.EXTENSION_NOT_ALLOWED}); - - return false; - }); - } - @HostListener('change') onChange(): void { - if (!this.el.nativeElement.files || !this.el.nativeElement.files.length) { + this.files = this.el.nativeElement.files; + if (!this.files) { + console.log('return'); return; } - this.files = Array.from(this.el.nativeElement.files); - if (this.options.filterExtensions && this.options.allowedExtensions) { - this.filterFilesByExtension(); + this.files = this.files.filter(f => { + if (this.options.allowedExtensions.indexOf(f.type) !== -1) { + return true; + } + + let ext: string = f.name.split('.').pop(); + if (this.options.allowedExtensions.indexOf(ext) !== -1 ) { + return true; + } + + this.onUploadRejected.emit({file: f, reason: UploadRejected.EXTENSION_NOT_ALLOWED}); + + return false; + }); } if (this.files.length) { diff --git a/src/directives/ng-file-select.ts b/src/directives/ng-file-select.ts index b1170d1d..2b006163 100644 --- a/src/directives/ng-file-select.ts +++ b/src/directives/ng-file-select.ts @@ -4,49 +4,41 @@ import { EventEmitter, Input, Output, - HostListener + HostListener, + Inject, + OnChanges } from '@angular/core'; -import { Ng2Uploader, UploadRejected, UploadedFile } from '../services/ng2-uploader'; +import { Ng2UploaderService } from '../services/ng2-uploader'; +import { INg2UploaderOptions, Ng2UploaderOptions, UploadedFile, UploadRejected } from '../classes'; @Directive({ selector: '[ngFileSelect]' }) -export class NgFileSelectDirective { - +export class NgFileSelectDirective implements OnChanges { + @Input() options: Ng2UploaderOptions; @Input() events: EventEmitter; @Output() onUpload: EventEmitter = new EventEmitter(); @Output() onPreviewData: EventEmitter = new EventEmitter(); @Output() onUploadRejected: EventEmitter = new EventEmitter(); @Output() beforeUpload: EventEmitter = new EventEmitter(); - _options: any; + files: any[] = []; - @Input('options') - set options(value: any) { - this._options = value; - this.uploader.setOptions(this.options); - } + constructor( + @Inject(ElementRef) public el: ElementRef, + @Inject(Ng2UploaderService) public uploader: Ng2UploaderService) { } - get options(): any { - return this._options; - } - - files: any[] = []; - uploader: Ng2Uploader; + ngOnChanges() { + if (!this.options) { + return; + } - constructor(public el: ElementRef) { - this.uploader = new Ng2Uploader(); - setTimeout(() => { - this.uploader.setOptions(this.options); - }); + this.uploader.setOptions(new Ng2UploaderOptions(this.options)); this.uploader._emitter.subscribe((data: any) => { this.onUpload.emit(data); if (data.done) { this.files = this.files.filter(f => f.name !== data.originalName); - if (this.uploader.fieldReset) { - this.el.nativeElement.value = ''; - } } }); @@ -58,42 +50,37 @@ export class NgFileSelectDirective { this.beforeUpload.emit(uploadingFile) }); - setTimeout(() => { - if (this.events) { - this.events.subscribe((data: string) => { - if (data === 'startUpload') { - this.uploader.uploadFilesInQueue(); - } - }); - } - }); - } - - filterFilesByExtension(): void { - this.files = this.files.filter(f => { - if (this.options.allowedExtensions.indexOf(f.type) !== -1) { - return true; - } - - let ext: string = f.name.split('.').pop(); - if (this.options.allowedExtensions.indexOf(ext) !== -1 ) { - return true; - } - - this.onUploadRejected.emit({file: f, reason: UploadRejected.EXTENSION_NOT_ALLOWED}); - - return false; - }); + if (this.events instanceof EventEmitter) { + this.events.subscribe((data: string) => { + if (data === 'startUpload') { + this.uploader.uploadFilesInQueue(); + } + }); + } } @HostListener('change') onChange(): void { - if (!this.el.nativeElement.files) { + this.files = this.el.nativeElement.files; + if (!this.files) { + console.log('return'); return; } - this.files = Array.from(this.el.nativeElement.files); if (this.options.filterExtensions && this.options.allowedExtensions) { - this.filterFilesByExtension(); + this.files = this.files.filter(f => { + if (this.options.allowedExtensions.indexOf(f.type) !== -1) { + return true; + } + + let ext: string = f.name.split('.').pop(); + if (this.options.allowedExtensions.indexOf(ext) !== -1 ) { + return true; + } + + this.onUploadRejected.emit({file: f, reason: UploadRejected.EXTENSION_NOT_ALLOWED}); + + return false; + }); } if (this.files.length) { diff --git a/src/module/ng2-uploader.module.ts b/src/module/ng2-uploader.module.ts index e6b89f59..0d786aef 100644 --- a/src/module/ng2-uploader.module.ts +++ b/src/module/ng2-uploader.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { NgFileDropDirective } from './../directives/ng-file-drop'; import { NgFileSelectDirective } from './../directives/ng-file-select'; -import { Ng2Uploader } from './../services/ng2-uploader'; +import { Ng2UploaderServiceProvider } from './../services/ng2-uploader'; @NgModule({ declarations: [ @@ -9,7 +9,7 @@ import { Ng2Uploader } from './../services/ng2-uploader'; NgFileSelectDirective ], providers: [ - Ng2Uploader + Ng2UploaderServiceProvider ], exports: [ NgFileDropDirective, diff --git a/src/services/ng2-uploader.ts b/src/services/ng2-uploader.ts index 78549c0e..94b23cbf 100644 --- a/src/services/ng2-uploader.ts +++ b/src/services/ng2-uploader.ts @@ -1,112 +1,25 @@ -import { EventEmitter } from '@angular/core'; - -export class UploadedFile { - id: string; - status: number; - statusText: string; - progress: Object; - originalName: string; - size: number; - response: string; - done: boolean; - error: boolean; - abort: boolean; - startTime: number; - endTime: number; - speedAverage: number; - speedAverageHumanized: string; - - constructor(id: string, originalName: string, size: number) { - this.id = id; - this.originalName = originalName; - this.size = size; - this.progress = { - loaded: 0, - total: 0, - percent: 0, - speed: 0, - speedHumanized: null - }; - this.done = false; - this.error = false; - this.abort = false; - this.startTime = new Date().getTime(); - this.endTime = 0; - this.speedAverage = 0; - this.speedAverageHumanized = null; - } - - setProgres(progress: Object): void { - this.progress = progress; - } - - setError(): void { - this.error = true; - this.done = true; - } - - setAbort(): void { - this.abort = true; - this.done = true; - } - - onFinished(status: number, statusText: string, response: string): void { - this.endTime = new Date().getTime(); - this.speedAverage = this.size / (this.endTime - this.startTime) * 1000; - this.speedAverage = parseInt(this.speedAverage, 10); - this.speedAverageHumanized = humanizeBytes(this.speedAverage); - this.status = status; - this.statusText = statusText; - this.response = response; - this.done = true; +import { EventEmitter, Injectable, OnChanges, Provider } from '@angular/core'; +import { Ng2UploaderOptions } from '../classes/ng2-uploader-options.class'; +import { UploadedFile } from '../classes/uploaded-file.class'; +import { UploadRejected } from '../classes/upload-rejected.class'; + +@Injectable() +export class Ng2UploaderService { + _queue: any[]; + _emitter: EventEmitter; + _previewEmitter: EventEmitter; + _beforeEmitter: EventEmitter; + opts: Ng2UploaderOptions; + + constructor() { + this._queue = []; + this._emitter = new EventEmitter(); + this._previewEmitter = new EventEmitter(); + this._beforeEmitter = new EventEmitter(); } -} -export class Ng2Uploader { - url: string; - cors: boolean = false; - withCredentials: boolean = false; - multiple: boolean = false; - maxUploads: number = 3; - data: { [index: string]: any } = {}; - autoUpload: boolean = true; - multipart: boolean = true; - method: string = 'POST'; - debug: boolean = false; - customHeaders: any = {}; - encodeHeaders: boolean = true; - authTokenPrefix: string = 'Bearer'; - authToken: string = undefined; - fieldName: string = 'file'; - fieldReset: boolean = true; - previewUrl: boolean = false; - calculateSpeed: boolean = false; - _queue: any[] = []; - _emitter: EventEmitter = new EventEmitter(); - _previewEmitter: EventEmitter = new EventEmitter(); - _beforeEmitter: EventEmitter = new EventEmitter(); - - setOptions(options: any): void { - this.url = options.url != null ? options.url : this.url; - this.cors = options.cors != null ? options.cors : this.cors; - this.withCredentials = options.withCredentials != null ? options.withCredentials : this.withCredentials; - this.multiple = options.multiple != null ? options.multiple : this.multiple; - this.maxUploads = options.maxUploads != null ? options.maxUploads : this.maxUploads; - this.data = options.data != null ? options.data : this.data; - this.autoUpload = options.autoUpload != null ? options.autoUpload : this.autoUpload; - this.multipart = options.multipart != null ? options.multipart : this.multipart; - this.method = options.method != null ? options.method : this.method; - this.customHeaders = options.customHeaders != null ? options.customHeaders : this.customHeaders; - this.encodeHeaders = options.encodeHeaders != null ? options.encodeHeaders : this.encodeHeaders; - this.authTokenPrefix = options.authTokenPrefix != null ? options.authTokenPrefix : this.authTokenPrefix; - this.authToken = options.authToken != null ? options.authToken : this.authToken; - this.fieldName = options.fieldName != null ? options.fieldName : this.fieldName; - this.fieldReset = options.fieldReset != null ? options.fieldReset : this.fieldReset; - this.previewUrl = options.previewUrl != null ? options.previewUrl : this.previewUrl; - this.calculateSpeed = options.calculateSpeed != null ? options.calculateSpeed : this.calculateSpeed; - if (!this.multiple) { - this.maxUploads = 1; - } + setOptions(opts: Ng2UploaderOptions) { + this.opts = opts; } uploadFilesInQueue(): void { @@ -119,10 +32,10 @@ export class Ng2Uploader { uploadFile(file: any): void { let xhr = new XMLHttpRequest(); let form = new FormData(); - form.append(this.fieldName, file, file.name); + form.append(this.opts.fieldName, file, file.name); - Object.keys(this.data).forEach(k => { - form.append(k, this.data[k]); + Object.keys(this.opts.data).forEach(k => { + form.append(k, this.opts.data[k]); }); let uploadingFile = new UploadedFile( @@ -140,12 +53,12 @@ export class Ng2Uploader { xhr.upload.onprogress = (e: ProgressEvent) => { if (e.lengthComputable) { - if (this.calculateSpeed) { + if (this.opts.calculateSpeed) { time = new Date().getTime() - time; load = e.loaded - load; speed = load / time * 1000; speed = parseInt(speed, 10); - speedHumanized = humanizeBytes(speed); + speedHumanized = this.humanizeBytes(speed); } let percent = Math.round(e.loaded / e.total * 100); @@ -191,17 +104,17 @@ export class Ng2Uploader { } }; - xhr.open(this.method, this.url, true); - xhr.withCredentials = this.withCredentials; + xhr.open(this.opts.method, this.opts.url, true); + xhr.withCredentials = this.opts.withCredentials; - if (this.customHeaders) { - Object.keys(this.customHeaders).forEach((key) => { - xhr.setRequestHeader(key, this.customHeaders[key]); + if (this.opts.customHeaders) { + Object.keys(this.opts.customHeaders).forEach((key) => { + xhr.setRequestHeader(key, this.opts.customHeaders[key]); }); } - if (this.authToken) { - xhr.setRequestHeader('Authorization', `${this.authTokenPrefix} ${this.authToken}`); + if (this.opts.authToken) { + xhr.setRequestHeader('Authorization', `${this.opts.authTokenPrefix} ${this.opts.authToken}`); } this._beforeEmitter.emit(uploadingFile); @@ -215,17 +128,17 @@ export class Ng2Uploader { addFilesToQueue(files: File[]): void { this.clearQueue(); - files.forEach((file: File, i: number) => { - if (this.isFile(file) && !this.inQueue(file)) { + [].forEach.call(files, (file: File, i: number) => { + if (!this.inQueue(file)) { this._queue.push(file); } }); - if (this.previewUrl) { - files.forEach(file => this.createFileUrl(file)); + if (this.opts.previewUrl) { + [].forEach.call(files, (file: File) => this.createFileUrl(file)); } - if (this.autoUpload) { + if (this.opts.autoUpload) { this.uploadFilesInQueue(); } } @@ -255,30 +168,22 @@ export class Ng2Uploader { return fileInQueue.length ? true : false; } - isFile(file: any): boolean { - return file !== null && (file instanceof Blob || (file.name && file.size)); - } - generateRandomIndex(): string { return Math.random().toString(36).substring(7); } -} -export class UploadRejected { - public static get EXTENSION_NOT_ALLOWED():string { return 'ExtensionNotAllowed'; } - public static get MAX_SIZE_EXCEEDED():string { return 'MaxSizeExceeded'; } - - file: any; - reason: string; -} + humanizeBytes(bytes: number): string { + if (bytes === 0) { + return '0 Byte'; + } + let k = 1024; + const sizes: string[] = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB']; + let i: number = Math.floor(Math.log(bytes) / Math.log(k)); -function humanizeBytes(bytes: number): string { - if (bytes === 0) { - return '0 Byte'; + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i] + '/s'; } - let k = 1024; - const sizes: string[] = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB']; - let i: number = Math.floor(Math.log(bytes) / Math.log(k)); - - return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i] + '/s'; } + +export const Ng2UploaderServiceProvider: Provider = { + provide: Ng2UploaderService, useClass: Ng2UploaderService +}; diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000..89ca262d --- /dev/null +++ b/yarn.lock @@ -0,0 +1,110 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@angular/common@^2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@angular/common/-/common-2.4.1.tgz#a70167430574959c3423ac96ebdec98032d3500d" + +"@angular/compiler-cli@^2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-2.4.1.tgz#9291504197a199fee2f48353d67431a1a9e40833" + dependencies: + "@angular/tsc-wrapped" "0.5.0" + minimist "^1.2.0" + reflect-metadata "^0.1.2" + +"@angular/compiler@^2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-2.4.1.tgz#62b4fbfc53c934bd5def78db594cbf245b3f446a" + +"@angular/core@^2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@angular/core/-/core-2.4.1.tgz#3a6d2dc7fd86fdebe4febae7eb28abad7d04c76a" + +"@angular/platform-browser-dynamic@^2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-2.4.1.tgz#5fb72038f76c1e3646cea95f5b4722d67a4419dd" + +"@angular/platform-browser@^2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-2.4.1.tgz#4eaa829b450be34f0029796b6c3b99e27d3d5740" + +"@angular/platform-server@^2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@angular/platform-server/-/platform-server-2.4.1.tgz#9e5e427b8226d1a073949effea00a4df352682de" + dependencies: + parse5 "^2.2.1" + +"@angular/tsc-wrapped@0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@angular/tsc-wrapped/-/tsc-wrapped-0.5.0.tgz#e50f81af02c6817dcaba22032e49ba8060d628b4" + dependencies: + tsickle "^0.2" + +"@types/core-js@^0.9.35": + version "0.9.35" + resolved "https://registry.yarnpkg.com/@types/core-js/-/core-js-0.9.35.tgz#444064e63711cdcc62ea844d27642f6efc2285f2" + +"@types/node@^6.0.52": + version "6.0.52" + resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.52.tgz#1ac3a99b42320f9e463482f25af4c2359473aaa6" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +mkdirp@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +parse5@^2.2.1: + version "2.2.3" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-2.2.3.tgz#0c4fc41c1000c5e6b93d48b03f8083837834e9f6" + +reflect-metadata@^0.1.2, reflect-metadata@^0.1.9: + version "0.1.9" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.9.tgz#987238dc87a516895fe457f130435ffbd763a4d4" + +rxjs@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.0.1.tgz#3a69bdf9f0ca0a986303370d4708f72bdfac8356" + dependencies: + symbol-observable "^1.0.1" + +source-map-support@^0.4.2: + version "0.4.7" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.7.tgz#7a7988e0e66241c778c78dd179199bb6bcd35bd6" + dependencies: + source-map "^0.5.3" + +source-map@^0.5.3, source-map@^0.5.6: + version "0.5.6" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + +symbol-observable@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d" + +tsickle@^0.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.2.3.tgz#3e7d3ff74bbecdadc011cfd747f991dcfd6503af" + dependencies: + minimist "^1.2.0" + mkdirp "^0.5.1" + source-map "^0.5.6" + source-map-support "^0.4.2" + +typescript@2.0.10: + version "2.0.10" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.0.10.tgz#ccdd4ed86fd5550a407101a0814012e1b3fac3dd" + +zone.js@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.4.tgz#0e624fe5b724450ee433495deff15c02b3908ee0"