From aee69d8b80f9e88b22af6da5841352307bc2b42e Mon Sep 17 00:00:00 2001 From: Dmitriy Shekhovtsov Date: Wed, 11 May 2016 18:40:47 +0300 Subject: [PATCH] chore(build): ng2 style guide applied, - build process updated - systemjs bundler added BREAKING CHANGES: - directives and selectors renamed to ng2FileSelect and ng2FileDrop --- .config/bundle-system.js | 107 ++++++ .eslintrc | 200 ----------- .eslintrc.json | 6 + .gitignore | 6 +- .npmignore | 19 +- README.md | 8 +- .../file-upload/file-drop.directive.spec.ts | 21 ++ .../{file-drop.ts => file-drop.directive.ts} | 51 ++- .../{file-item.ts => file-item.class.ts} | 59 +-- ...ke-object.ts => file-like-object.class.ts} | 14 +- ...ile-select.ts => file-select.directive.ts} | 30 +- ...ile-uploader.ts => file-uploader.class.ts} | 339 ++++++++---------- components/file-upload/readme.md | 27 +- demo/components/file-upload-section.ts | 14 +- demo/components/file-upload/file-catcher.js | 1 + demo/components/file-upload/simple-demo.html | 8 +- demo/components/file-upload/simple-demo.ts | 14 +- demo/index.html | 6 - demo/index.ts | 9 +- es6-object.d.ts | 26 -- gulp-tasks/lint.js | 35 +- gulpfile.js | 25 +- karma.conf.js | 101 ++++++ ng2-file-upload.ts | 18 +- package.json | 80 +++-- test.bundle.js | 47 +++ tsd.d.ts | 1 - tslint.json | 56 +-- typings.json | 7 +- webpack.config.js | 148 ++++---- 30 files changed, 743 insertions(+), 740 deletions(-) create mode 100755 .config/bundle-system.js delete mode 100644 .eslintrc create mode 100644 .eslintrc.json create mode 100644 components/file-upload/file-drop.directive.spec.ts rename components/file-upload/{file-drop.ts => file-drop.directive.ts} (57%) rename components/file-upload/{file-item.ts => file-item.class.ts} (59%) rename components/file-upload/{file-like-object.ts => file-like-object.class.ts} (76%) rename components/file-upload/{file-select.ts => file-select.directive.ts} (61%) rename components/file-upload/{file-uploader.ts => file-uploader.class.ts} (71%) delete mode 100644 es6-object.d.ts create mode 100644 karma.conf.js create mode 100644 test.bundle.js delete mode 100644 tsd.d.ts diff --git a/.config/bundle-system.js b/.config/bundle-system.js new file mode 100755 index 0000000..9c8cc53 --- /dev/null +++ b/.config/bundle-system.js @@ -0,0 +1,107 @@ +#!/usr/bin/env node +'use strict'; + +/*eslint no-console: 0, no-sync: 0*/ + +// System.js bundler +// simple and yet reusable system.js bundler +// bundles, minifies and gzips + +const fs = require('fs'); +const del = require('del'); +const path = require('path'); +const zlib = require('zlib'); +const async = require('async'); +const Builder = require('systemjs-builder'); + +const pkg = require('../package.json'); +const name = pkg.name; +const targetFolder = path.resolve('./bundles'); + +async.waterfall([ + cleanBundlesFolder, + getSystemJsBundleConfig, + buildSystemJs({minify: false, sourceMaps: true, mangle: false}), + getSystemJsBundleConfig, + buildSystemJs({minify: true, sourceMaps: true, mangle: false}), + gzipSystemJsBundle +], err => { + if (err) { + throw err; + } +}); + +function getSystemJsBundleConfig(cb) { + const config = { + baseURL: '..', + transpiler: 'typescript', + typescriptOptions: { + module: 'cjs' + }, + map: { + typescript: path.resolve('node_modules/typescript/lib/typescript.js'), + '@angular/core': path.resolve('node_modules/@angular/core/index.js'), + '@angular/common': path.resolve('node_modules/@angular/common/index.js'), + '@angular/compiler': path.resolve('node_modules/@angular/compiler/index.js'), + '@angular/platform-browser': path.resolve('node_modules/@angular/platform-browser/index.js'), + '@angular/platform-browser-dynamic': path.resolve('node_modules/@angular/platform-browser-dynamic/'), + rxjs: path.resolve('node_modules/rxjs') + }, + paths: { + '*': '*.js' + } + }; + + config.meta = ['@angular/common','@angular/compiler','@angular/core', + '@angular/platform-browser','@angular/platform-browser-dynamic', 'rxjs'].reduce((memo, currentValue) => { + memo[path.resolve(`node_modules/${currentValue}/*`)] = {build: false}; + return memo; + }, {}); + config.meta.moment = {build: false}; + return cb(null, config); +} + +function cleanBundlesFolder(cb) { + return del(targetFolder) + .then(paths => { + console.log('Deleted files and folders:\n', paths.join('\n')); + cb(); + }); +} + +function buildSystemJs(options) { + return (config, cb) => { + const minPostFix = options && options.minify ? '.min' : ''; + const fileName = `${name}${minPostFix}.js`; + const dest = path.resolve(__dirname, targetFolder, fileName); + const builder = new Builder(); + + console.log('Bundling system.js file:', fileName, options); + builder.config(config); + return builder + .bundle([name, name].join('/'), dest, options) + .then(() => cb()) + .catch(cb); + }; +} + +function gzipSystemJsBundle(cb) { + const files = fs + .readdirSync(path.resolve(targetFolder)) + .map(file => path.resolve(targetFolder, file)) + .filter(file => fs.statSync(file).isFile()) + .filter(file => path.extname(file) !== 'gz'); + + return async.eachSeries(files, (file, gzipcb) => { + process.nextTick(() => { + console.log('Gzipping ', file); + const gzip = zlib.createGzip({level: 9}); + const inp = fs.createReadStream(file); + const out = fs.createWriteStream(`${file}.gz`); + + inp.on('end', () => gzipcb()); + inp.on('error', err => gzipcb(err)); + return inp.pipe(gzip).pipe(out); + }); + }, cb); +} diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 1025ddc..0000000 --- a/.eslintrc +++ /dev/null @@ -1,200 +0,0 @@ -{ - "env": { - "browser": 2, - "node": 2, - "mocha": 2, - "es6": 2 - }, - "ecmaFeatures": { - "modules": true - }, - "globals": { - "_": 2, - "$": 2, - "angular": 2, - "jQuery": 2 - }, - "rules": { - // Possible Errors - "comma-dangle": 2, - // no-comma-dangle - (deprecated) - "no-cond-assign": [2, "always"], - "no-console": 2, - "no-constant-condition": 2, - "no-control-regex": 2, - "no-debugger": 2, - "no-dupe-keys": 2, - "no-dupe-args": 2, - "no-duplicate-case": 2, - "no-empty-character-class": 2, - // no-empty-class - (deprecated) - "no-empty": 2, - "no-ex-assign": 2, - "no-extra-boolean-cast": 2, - "no-extra-parens": 2, - "no-extra-semi": 2, - "no-func-assign": 2, - "no-inner-declarations": 2, - "no-invalid-regexp": 2, - "no-irregular-whitespace": 2, - "no-negated-in-lhs": 2, - "no-obj-calls": 2, - "no-regex-spaces": 2, - "no-sparse-arrays": 2, - "no-unreachable": 2, - "use-isnan": 2, - "valid-jsdoc": 2, - "valid-typeof": 2, - "no-unexpected-multiline": 2, - - // Best Practices - // not sure - "accessor-pairs": [2, {"getWithoutSet": false, "setWithoutGet": true}], - "block-scoped-var": 2, - "complexity": [2, 6], - "consistent-return": 2, - "curly": 2, - "default-case": 2, - "dot-notation": 2, - // not good for chain calls, but not for properties - "dot-location": [2, "property"], - "eqeqeq": 2, - "guard-for-in": 2, - "no-alert": 2, - "no-caller": 2, - "no-div-regex": 2, - "no-else-return": 2, - "no-empty-label": 2, - "no-eq-null": 2, - "no-eval": 2, - "no-extend-native": 2, - "no-extra-bind": 2, - "no-fallthrough": 2, - "no-floating-decimal": 2, - "no-implied-eval": 2, - "no-iterator": 2, - "no-labels": 2, - "no-lone-blocks": 2, - "no-loop-func": 2, - "no-multi-spaces": 2, - "no-multi-str": 2, - "no-native-reassign": 2, - "no-new": 2, - "no-new-func": 2, - "no-new-wrappers": 2, - "no-octal": 2, - "no-octal-escape": 2, - "no-param-reassign": 2, - "no-process-env": 2, - "no-proto": 2, - "no-redeclare": 2, - "no-return-assign": 2, - "no-script-url": 2, - "no-self-compare": 2, - "no-sequences": 2, - "no-throw-literal": 2, - "no-unused-expressions": 2, - "no-void": 2, - "no-warning-comments": [1, { "terms": ["todo", "fixme"], "location": "anywhere" }], - "no-with": 2, - "radix": 2, - "vars-on-top": 0, - "wrap-iife": [2, "inside"], - "yoda": [2, "never"], - - // Strict Mode - "strict": [2, "global"], - - // Variables - "no-catch-shadow": 2, - "no-delete-var": 2, - "no-label-var": 2, - "no-shadow": 2, - "no-shadow-restricted-names": 2, - "no-undef": 2, - "no-undef-init": 2, - "no-undefined": 2, - "no-unused-vars": 2, - "no-use-before-define": [2, "nofunc"], - - // Node.js - "handle-callback-err": [2, "^.*(e|E)rr" ], - "no-mixed-requires": [2, true], - "no-new-require": 2, - "no-path-concat": 2, - "no-process-exit": 2, - "no-sync": 2, - - // Stylistic Issues - "array-bracket-spacing": [2, "never"], - "brace-style": [2, "1tbs", { "allowSingleLine": true }], - "camelcase": 2, - "comma-spacing": [2, {"before": false, "after": true}], - "comma-style": [2, "last"], - "computed-property-spacing": [2, "never"], - "consistent-this": [2, "self"], - // not sure - "eol-last": 0, - "func-names": 0, - "func-style": [2, "declaration"], - "indent": [2, 2], - "key-spacing": [2, { "beforeColon": false, "afterColon": true}], - "max-nested-callbacks": [2, 3], - "new-cap": [2, {"newIsCap": true, "capIsNew": true, "capIsNewExceptions":[ - "ObjectId", - "Object", - "Function", - "Number", - "String", - "Boolean", - "Date", - "Array", - "Symbol", - "RegExp" - ]}], - "new-parens": 2, - "newline-after-var": 0, - "no-array-constructor": 2, - "no-inline-comments": 2, - "no-lonely-if": 2, - "no-mixed-spaces-and-tabs": 2, - "no-multiple-empty-lines": [2, {"max": 1}], - "no-nested-ternary": 2, - "no-new-object": 2, - "no-spaced-func": 2, - "no-ternary": 0, - "no-trailing-spaces": 2, - "no-underscore-dangle": 2, - "no-unneeded-ternary": 2, - "object-curly-spacing": [2, "never"], - "one-var": [2, { - "var": "never", // Exactly one var declaration per function - "let": "never", // Exactly one let declaration per block - "const": "never" // Exactly one declarator per const declaration per block - }], - "operator-assignment": [1, "always"], - "operator-linebreak": [2, "after"], - "padded-blocks": [2, "never"], - "quote-props": [2, "as-needed"], - "quotes": [2, "single", "avoid-escape"], - "semi": [2, "always"], - "semi-spacing": 2, - // "sort-vars": [1, { "ignoreCase": true }], - "space-after-keywords": [2, "always" ], - "space-before-blocks": [2, "always"], - "space-in-parens": [2, "never"], - "space-infix-ops": 2, - "space-return-throw-case": 2, - "space-unary-ops": [2, { "words": true, "nonwords": false }], - "spaced-comment": 0, - "wrap-regexp": 0, - - // Legacy - "max-len": [1, 120, 4], - // todo: apply max-params - "max-params": [0, 3], - // todo: apply max-statements - "max-statements": [2, 30], - "no-bitwise": 2 - } -} diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..f8afe99 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,6 @@ +{ + "extends": "./node_modules/eslint-config-valorsoft/.eslintrc.json", + "env": { + "node": true + } +} diff --git a/.gitignore b/.gitignore index 3fb66f9..9a947e2 100644 --- a/.gitignore +++ b/.gitignore @@ -10,9 +10,13 @@ npm-debug.log .idea # ignore build and dist for now -/build +/bundles +/demo-build /dist +/coverage +/ts +# ignore incline compiling /demo/**/*.js /demo/**/*.d.ts /demo/**/*.js.map diff --git a/.npmignore b/.npmignore index bd753f7..8f189f2 100644 --- a/.npmignore +++ b/.npmignore @@ -1,13 +1,24 @@ .idea -demo -build gulp-tasks logs -tsd.d.ts +# typings typings -/node_modules +# testing +karma.conf.js +test.bundle.js +coverage +# demo build +demo +demo-build webpack.config.js +#typescript sources +*.ts +*.js.map +!*.d.ts +/components/**/*.ts +!/components/**/*.d.ts + diff --git a/README.md b/README.md index 8373a11..74da75e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # ng2-file-upload [![npm version](https://badge.fury.io/js/ng2-file-upload.svg)](http://badge.fury.io/js/ng2-file-upload) Easy to use Angular2 directives for files upload ([demo](http://valor-software.github.io/ng2-file-upload/)) -Follow me at [twitter](https://twitter.com/valorkin) to be notified about new releases. +Follow me [![twitter](https://img.shields.io/twitter/follow/valorkin.svg?style=social&label=%20valorkin)](https://twitter.com/valorkin) to be notified about new releases. [![Code Climate](https://codeclimate.com/github/valor-software/ng2-file-upload/badges/gpa.svg)](https://codeclimate.com/github/valor-software/ng2-file-upload) [![Join the chat at https://gitter.im/valor-software/ng2-bootstrap](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/valor-software/ng2-bootstrap?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) @@ -17,9 +17,9 @@ Follow me at [twitter](https://twitter.com/valorkin) to be notified about new re Alternatively, you can [download it in a ZIP file](https://github.com/valor-software/ng2-file-upload/archive/master.zip). -2. Currently `ng2-file-upload` contains two directives: `ng2-file-select` and `ng2-file-drop`. `ng2-file-select` is used for 'file-input' field of form and +2. Currently `ng2-file-upload` contains two directives: `ng2-file-select` and `ng2-file-drop`. `ng2-file-select` is used for 'file-input' field of form and `ng2-file-drop` is used for area that will be used for dropping of file or files. - + 3. More information regarding using of ***ng2-file-upload*** is located in [demo](http://valor-software.github.io/ng2-file-upload/) and [demo sources](https://github.com/valor-software/ng2-file-upload/tree/master/demo). @@ -28,7 +28,7 @@ Follow me at [twitter](https://twitter.com/valorkin) to be notified about new re ### Properties - `uploader` - (`FileUploader`) - uploader object. See using in [demo](https://github.com/valor-software/ng2-file-upload/blob/master/demo/components/file-upload/simple-demo.ts) - + ## API for `ng2-file-drop` ### Properties diff --git a/components/file-upload/file-drop.directive.spec.ts b/components/file-upload/file-drop.directive.spec.ts new file mode 100644 index 0000000..264e409 --- /dev/null +++ b/components/file-upload/file-drop.directive.spec.ts @@ -0,0 +1,21 @@ +import {Component} from '@angular/core'; +import {it, inject, beforeEachProviders} from '@angular/core/testing'; +import {ComponentFixture} from '@angular/compiler/testing'; +import {FileUploader} from './file-uploader.class'; +import {FileSelectDirective} from './file-select.directive'; +@Component({ + selector: 'container', + template: ``, + directives: [FileSelectDirective] +}) +export class ContainerComponent { + public uploader:FileUploader = new FileUploader({url: 'localhost:3000'}); +} +describe('Directive: FileSelectDirective', () => { + beforeEachProviders(() => [ + ContainerComponent + ]); + it('should be fine', inject([ContainerComponent], (fixture:ComponentFixture) => { + expect(fixture).not.toBeNull(); + })); +}); diff --git a/components/file-upload/file-drop.ts b/components/file-upload/file-drop.directive.ts similarity index 57% rename from components/file-upload/file-drop.ts rename to components/file-upload/file-drop.directive.ts index 2de4812..14ed93f 100644 --- a/components/file-upload/file-drop.ts +++ b/components/file-upload/file-drop.directive.ts @@ -1,32 +1,27 @@ -import { Directive, EventEmitter, ElementRef } from '@angular/core'; +import { Directive, EventEmitter, ElementRef, HostListener, Input, Output } from '@angular/core'; -import { FileUploader } from './file-uploader'; +import { FileUploader } from './file-uploader.class'; -@Directive({ - selector: '[ng2-file-drop]', - properties: ['uploader'], - events: ['fileOver'], - host: { - '(drop)': 'onDrop($event)', - '(dragover)': 'onDragOver($event)', - '(dragleave)': 'onDragLeave($event)' - } -}) -export class FileDrop { - public uploader:FileUploader; - private fileOver:EventEmitter = new EventEmitter(); +@Directive({selector: '[ng2FileDrop]'}) +export class FileDropDirective { + @Input() public uploader:FileUploader; + @Output() public fileOver:EventEmitter = new EventEmitter(); - constructor(private element:ElementRef) { + private element:ElementRef; + public constructor(element:ElementRef) { + this.element = element; } - getOptions() { + public getOptions():any { return this.uploader.options; } - getFilters() { + public getFilters():any { + return {}; } - onDrop(event:any) { + @HostListener('drop', ['$event']) + public onDrop(event:any):void { let transfer = this._getTransfer(event); if (!transfer) { return; @@ -36,10 +31,11 @@ export class FileDrop { let filters = this.getFilters(); this._preventAndStop(event); this.uploader.addToQueue(transfer.files, options, filters); - this.fileOver.next(false); + this.fileOver.emit(false); } - onDragOver(event:any) { + @HostListener('dragover', ['$event']) + public onDragOver(event:any):void { let transfer = this._getTransfer(event); if (!this._haveFiles(transfer.types)) { return; @@ -47,16 +43,17 @@ export class FileDrop { transfer.dropEffect = 'copy'; this._preventAndStop(event); - this.fileOver.next(true); + this.fileOver.emit(true); } - onDragLeave(event:any):any { - if (event.currentTarget === (this).element[0]) { + @HostListener('dragleave', ['$event']) + public onDragLeave(event:any):any { + if (event.currentTarget === (this as any).element[0]) { return; } this._preventAndStop(event); - this.fileOver.next(false); + this.fileOver.emit(false); } private _getTransfer(event:any):any { @@ -81,12 +78,12 @@ export class FileDrop { return false; } } - +/* _addOverClass(item:any):any { item.addOverClass(); } _removeOverClass(item:any):any { item.removeOverClass(); - } + }*/ } diff --git a/components/file-upload/file-item.ts b/components/file-upload/file-item.class.ts similarity index 59% rename from components/file-upload/file-item.ts rename to components/file-upload/file-item.class.ts index 2fe79d8..97cef1d 100644 --- a/components/file-upload/file-item.ts +++ b/components/file-upload/file-item.class.ts @@ -1,5 +1,5 @@ -import {FileLikeObject} from './file-like-object'; -import {FileUploader} from './file-uploader'; +import {FileLikeObject} from './file-like-object.class'; +import {FileUploader} from './file-uploader.class'; export class FileItem { public file:FileLikeObject; @@ -17,15 +17,22 @@ export class FileItem { public isCancel:boolean = false; public isError:boolean = false; public progress:number = 0; - public index:number = null; + public index:number = void 0; - constructor(private uploader:FileUploader, private some:any, private options:any) { + private uploader:FileUploader; + private some:any; + private options:any; + + public constructor(uploader:FileUploader, some:any, options:any) { + this.uploader = uploader; + this.some = some; + this.options = options; this.file = new FileLikeObject(some); this._file = some; this.url = uploader.url; } - public upload() { + public upload():void { try { this.uploader.uploadItem(this); } catch (e) { @@ -34,33 +41,39 @@ export class FileItem { } } - public cancel() { + public cancel():void { this.uploader.cancelItem(this); } - public remove() { + public remove():void { this.uploader.removeFromQueue(this); } - public onBeforeUpload() { + public onBeforeUpload():void { + return void 0; } - public onProgress(progress:number) { + public onProgress(progress:number):any { + return {progress}; } - public onSuccess(response:any, status:any, headers:any) { + public onSuccess(response:any, status:any, headers:any):any { + return {response,status,headers}; } - public onError(response:any, status:any, headers:any) { + public onError(response:any, status:any, headers:any):any { + return {response,status,headers}; } - public onCancel(response:any, status:any, headers:any) { + public onCancel(response:any, status:any, headers:any):any { + return {response,status,headers}; } - public onComplete(response:any, status:any, headers:any) { + public onComplete(response:any, status:any, headers:any):any { + return {response,status,headers}; } - private _onBeforeUpload() { + public _onBeforeUpload():void { this.isReady = true; this.isUploading = true; this.isUploaded = false; @@ -71,12 +84,12 @@ export class FileItem { this.onBeforeUpload(); } - private _onProgress(progress:number) { + public _onProgress(progress:number):void { this.progress = progress; this.onProgress(progress); } - private _onSuccess(response:any, status:any, headers:any) { + public _onSuccess(response:any, status:any, headers:any):void { this.isReady = false; this.isUploading = false; this.isUploaded = true; @@ -84,11 +97,11 @@ export class FileItem { this.isCancel = false; this.isError = false; this.progress = 100; - this.index = null; + this.index = void 0; this.onSuccess(response, status, headers); } - private _onError(response:any, status:any, headers:any) { + public _onError(response:any, status:any, headers:any):void { this.isReady = false; this.isUploading = false; this.isUploaded = true; @@ -96,11 +109,11 @@ export class FileItem { this.isCancel = false; this.isError = true; this.progress = 0; - this.index = null; + this.index = void 0; this.onError(response, status, headers); } - private _onCancel(response:any, status:any, headers:any) { + public _onCancel(response:any, status:any, headers:any):void { this.isReady = false; this.isUploading = false; this.isUploaded = false; @@ -108,11 +121,11 @@ export class FileItem { this.isCancel = true; this.isError = false; this.progress = 0; - this.index = null; + this.index = void 0; this.onCancel(response, status, headers); } - private _onComplete(response:any, status:any, headers:any) { + public _onComplete(response:any, status:any, headers:any):void { this.onComplete(response, status, headers); if (this.uploader.removeAfterUpload) { @@ -120,7 +133,7 @@ export class FileItem { } } - private _prepareToUploading() { + public _prepareToUploading():void { this.index = this.index || ++this.uploader._nextIndex; this.isReady = true; } diff --git a/components/file-upload/file-like-object.ts b/components/file-upload/file-like-object.class.ts similarity index 76% rename from components/file-upload/file-like-object.ts rename to components/file-upload/file-like-object.class.ts index 9e824ab..2d0ccf7 100644 --- a/components/file-upload/file-like-object.ts +++ b/components/file-upload/file-like-object.class.ts @@ -1,4 +1,4 @@ -function isElement(node:any) { +function isElement(node:any):boolean { return !!(node && (node.nodeName || node.prop && node.attr && node.find)); } @@ -8,22 +8,22 @@ export class FileLikeObject { public type:string; public name:string; - constructor(fileOrInput:any) { + public constructor(fileOrInput:any) { let isInput = isElement(fileOrInput); let fakePathOrObject = isInput ? fileOrInput.value : fileOrInput; let postfix = typeof fakePathOrObject === 'string' ? 'FakePath' : 'Object'; let method = '_createFrom' + postfix; - (this)[method](fakePathOrObject); + (this as any)[method](fakePathOrObject); } - public _createFromFakePath(path:string) { - this.lastModifiedDate = null; - this.size = null; + public _createFromFakePath(path:string):void { + this.lastModifiedDate = void 0; + this.size = void 0; this.type = 'like/' + path.slice(path.lastIndexOf('.') + 1).toLowerCase(); this.name = path.slice(path.lastIndexOf('/') + path.lastIndexOf('\\') + 2); } - public _createFromObject(object:{size: number, type: string, name: string}) { + public _createFromObject(object:{size: number, type: string, name: string}):void { // this.lastModifiedDate = copy(object.lastModifiedDate); this.size = object.size; this.type = object.type; diff --git a/components/file-upload/file-select.ts b/components/file-upload/file-select.directive.ts similarity index 61% rename from components/file-upload/file-select.ts rename to components/file-upload/file-select.directive.ts index 0fb75de..250bf2c 100644 --- a/components/file-upload/file-select.ts +++ b/components/file-upload/file-select.directive.ts @@ -1,34 +1,32 @@ -import { Directive, ElementRef } from '@angular/core'; +import { Directive, ElementRef, Input, HostListener } from '@angular/core'; -import {FileUploader} from './file-uploader'; +import {FileUploader} from './file-uploader.class'; // todo: filters -@Directive({ - selector: '[ng2-file-select]', - properties: ['uploader'], - host: { - '(change)': 'onChange()' - } -}) -export class FileSelect { - public uploader:FileUploader; +@Directive({selector: '[ng2FileSelect]'}) +export class FileSelectDirective { + @Input() public uploader:FileUploader; - constructor(private element:ElementRef) { + private element:ElementRef; + public constructor(element:ElementRef) { + this.element = element; } - public getOptions() { + public getOptions():any { return this.uploader.options; } - public getFilters() { + public getFilters():any { + return void 0; } public isEmptyAfterSelection():boolean { return !!this.element.nativeElement.attributes.multiple; } - onChange() { + @HostListener('change') + public onChange():any { // let files = this.uploader.isHTML5 ? this.element.nativeElement[0].files : this.element.nativeElement[0]; let files = this.element.nativeElement.files; let options = this.getOptions(); @@ -45,5 +43,3 @@ export class FileSelect { } } } - -export const fileUpload:Array = [FileSelect]; diff --git a/components/file-upload/file-uploader.ts b/components/file-upload/file-uploader.class.ts similarity index 71% rename from components/file-upload/file-uploader.ts rename to components/file-upload/file-uploader.class.ts index e0ce563..45cf2cd 100644 --- a/components/file-upload/file-uploader.ts +++ b/components/file-upload/file-uploader.class.ts @@ -1,14 +1,11 @@ -import {FileLikeObject} from './file-like-object'; -import {FileItem} from './file-item'; - -function isFile(value:any) { +import {FileLikeObject} from './file-like-object.class'; +import {FileItem} from './file-item.class'; +function isFile(value:any):boolean { return (File && value instanceof File); } - -function isFileLikeObject(value:any) { - return value instanceof FileLikeObject; -} - +// function isFileLikeObject(value:any) { +// return value instanceof FileLikeObject; +// } export class FileUploader { public url:string; public authToken:string; @@ -19,11 +16,12 @@ export class FileUploader { public isHTML5:boolean = true; public removeAfterUpload:boolean = false; public queueLimit:number; - public _nextIndex = 0; + public _nextIndex:number = 0; public filters:Array = []; + public options:any; private _failFilterIndex:number; - constructor(public options:any) { + public constructor(options:any) { // Object.assign(this, options); this.url = options.url; this.authToken = options.authToken; @@ -31,19 +29,16 @@ export class FileUploader { this.filters.unshift({name: 'folder', fn: this._folderFilter}); } - public addToQueue(files:any[], options:any, filters:any) { + public addToQueue(files:any[], options:any, filters:any):void { let list:any[] = []; for (let file of files) { list.push(file); } - let arrayOfFilters = this._getFilters(filters); let count = this.queue.length; let addedFileItems:any[] = []; - - list.map(some => { + list.map((some:any) => { let temp = new FileLikeObject(some); - if (this._isValidFile(temp, arrayOfFilters, options)) { let fileItem = new FileItem(this, some, options); addedFileItems.push(fileItem); @@ -54,101 +49,92 @@ export class FileUploader { this._onWhenAddingFileFailed(temp, filter, options); } }); - if (this.queue.length !== count) { this._onAfterAddingAll(addedFileItems); this.progress = this._getTotalProgress(); } - this._render(); - if (this.autoUpload) { this.uploadAll(); } } - public removeFromQueue(value:any) { + public removeFromQueue(value:any):void { let index = this.getIndexOfItem(value); let item = this.queue[index]; if (item.isUploading) { item.cancel(); } - this.queue.splice(index, 1); this.progress = this._getTotalProgress(); } - public clearQueue() { + public clearQueue():void { while (this.queue.length) { this.queue[0].remove(); } - this.progress = 0; } - public uploadItem(value:FileItem) { + public uploadItem(value:FileItem):void { let index = this.getIndexOfItem(value); let item = this.queue[index]; let transport = this.isHTML5 ? '_xhrTransport' : '_iframeTransport'; - item._prepareToUploading(); if (this.isUploading) { return; } - this.isUploading = true; - (this)[transport](item); + (this as any)[transport](item); } - public cancelItem(value:any) { + public cancelItem(value:any):void { let index = this.getIndexOfItem(value); let item = this.queue[index]; let prop = this.isHTML5 ? '_xhr' : '_form'; - if (item && item.isUploading) { item[prop].abort(); } } - public uploadAll() { - let items = this.getNotUploadedItems().filter(item => !item.isUploading); + public uploadAll():void { + let items = this.getNotUploadedItems().filter((item:any) => !item.isUploading); if (!items.length) { return; } - - items.map(item => item._prepareToUploading()); + items.map((item:any) => item._prepareToUploading()); items[0].upload(); } - public cancelAll() { + public cancelAll():void { let items = this.getNotUploadedItems(); - items.map(item => item.cancel()); + items.map((item:any) => item.cancel()); } - - public isFile(value:any) { + public isFile(value:any):boolean { return isFile(value); } - public isFileLikeObject(value:any) { + public isFileLikeObject(value:any):boolean { return value instanceof FileLikeObject; } - public getIndexOfItem(value:any) { + public getIndexOfItem(value:any):number { return typeof value === 'number' ? value : this.queue.indexOf(value); } - public getNotUploadedItems() { - return this.queue.filter(item => !item.isUploaded); + public getNotUploadedItems():Array { + return this.queue.filter((item:any) => !item.isUploaded); } - public getReadyItems() { + public getReadyItems():Array { return this.queue - .filter(item => (item.isReady && !item.isUploading)) - .sort((item1, item2) => item1.index - item2.index); + .filter((item:any) => (item.isReady && !item.isUploading)) + .sort((item1:any, item2:any) => item1.index - item2.index); } - public destroy() { + public destroy():void { + return void 0; /*forEach(this._directives, (key) => { forEach(this._directives[key], (object) => { object.destroy(); @@ -156,79 +142,166 @@ export class FileUploader { });*/ } - public onAfterAddingAll(fileItems:any) { + public onAfterAddingAll(fileItems:any):any { + return {fileItems}; } - public onAfterAddingFile(fileItem:any) { + public onAfterAddingFile(fileItem:any):any { + return {fileItem}; } - public onWhenAddingFileFailed(item:any, filter:any, options:any) { + public onWhenAddingFileFailed(item:any, filter:any, options:any):any { + return {item, filter, options}; } - public onBeforeUploadItem(fileItem:any) { + public onBeforeUploadItem(fileItem:any):any { + return {fileItem}; } - public onProgressItem(fileItem:any, progress:any) { + public onProgressItem(fileItem:any, progress:any):any { + return {fileItem, progress}; } - public onProgressAll(progress:any) { + public onProgressAll(progress:any):any { + return {progress}; } - public onSuccessItem(item:any, response:any, status:any, headers:any) { + public onSuccessItem(item:any, response:any, status:any, headers:any):any { + return {item, response, status, headers}; } - public onErrorItem(item:any, response:any, status:any, headers:any) { + public onErrorItem(item:any, response:any, status:any, headers:any):any { + return {item, response, status, headers}; } - public onCancelItem(item:any, response:any, status:any, headers:any) { + public onCancelItem(item:any, response:any, status:any, headers:any):any { + return {item, response, status, headers}; } - public onCompleteItem(item:any, response:any, status:any, headers:any) { + public onCompleteItem(item:any, response:any, status:any, headers:any):any { + return {item, response, status, headers}; } - public onCompleteAll() { + public onCompleteAll():any { + return void 0; } - private _getTotalProgress(value = 0) { + public _onErrorItem(item:any, response:any, status:any, headers:any):void { + item._onError(response, status, headers); + this.onErrorItem(item, response, status, headers); + } + + public _onCompleteItem(item:any, response:any, status:any, headers:any):void { + item._onComplete(response, status, headers); + this.onCompleteItem(item, response, status, headers); + let nextItem = this.getReadyItems()[0]; + this.isUploading = false; + if (nextItem) { + nextItem.upload(); + return; + } + this.onCompleteAll(); + this.progress = this._getTotalProgress(); + this._render(); + } + + protected _headersGetter(parsedHeaders:any):any { + return (name:any) => { + if (name) { + return parsedHeaders[name.toLowerCase()] || void 0; + } + return parsedHeaders; + }; + } + + protected _xhrTransport(item:any):any { + let xhr = item._xhr = new XMLHttpRequest(); + let form = new FormData(); + this._onBeforeUploadItem(item); + // todo + /*item.formData.map(obj => { + obj.map((value, key) => { + form.append(key, value); + }); + });*/ + if (typeof item._file.size !== 'number') { + throw new TypeError('The file specified is no longer valid'); + } + form.append(item.alias, item._file, item.file.name); + xhr.upload.onprogress = (event:any) => { + let progress = Math.round(event.lengthComputable ? event.loaded * 100 / event.total : 0); + this._onProgressItem(item, progress); + }; + xhr.onload = () => { + let headers = this._parseHeaders(xhr.getAllResponseHeaders()); + let response = this._transformResponse(xhr.response, headers); + let gist = this._isSuccessCode(xhr.status) ? 'Success' : 'Error'; + let method = '_on' + gist + 'Item'; + (this as any)[method](item, response, xhr.status, headers); + this._onCompleteItem(item, response, xhr.status, headers); + }; + xhr.onerror = () => { + let headers = this._parseHeaders(xhr.getAllResponseHeaders()); + let response = this._transformResponse(xhr.response, headers); + this._onErrorItem(item, response, xhr.status, headers); + this._onCompleteItem(item, response, xhr.status, headers); + }; + xhr.onabort = () => { + let headers = this._parseHeaders(xhr.getAllResponseHeaders()); + let response = this._transformResponse(xhr.response, headers); + this._onCancelItem(item, response, xhr.status, headers); + this._onCompleteItem(item, response, xhr.status, headers); + }; + xhr.open(item.method, item.url, true); + xhr.withCredentials = item.withCredentials; + // todo + /*item.headers.map((value, name) => { + xhr.setRequestHeader(name, value); + });*/ + if (this.authToken) { + xhr.setRequestHeader('Authorization', this.authToken); + } + xhr.send(form); + this._render(); + } + + private _getTotalProgress(value:number = 0):number { if (this.removeAfterUpload) { return value; } - let notUploaded = this.getNotUploadedItems().length; let uploaded = notUploaded ? this.queue.length - notUploaded : this.queue.length; let ratio = 100 / this.queue.length; let current = value * ratio / 100; - return Math.round(uploaded * ratio + current); } - private _getFilters(filters:any) { + private _getFilters(filters:any):any { if (!filters) { return this.filters; } - if (Array.isArray(filters)) { return filters; } - let names = filters.match(/[^\s,]+/g); return this.filters - .filter(filter => names.indexOf(filter.name) !== -1); + .filter((filter:any) => names.indexOf(filter.name) !== -1); } - private _render() { + private _render():any { + return void 0; // todo: ? } - private _folderFilter(item:any) { + private _folderFilter(item:any):boolean { return !!(item.size || item.type); } - private _queueLimitFilter() { + private _queueLimitFilter():boolean { return this.queue.length < this.queueLimit; } - private _isValidFile(file:any, filters:any, options:any) { + private _isValidFile(file:any, filters:any, options:any):boolean { this._failFilterIndex = -1; return !filters.length ? true : filters.every((filter:any) => { this._failFilterIndex++; @@ -236,10 +309,11 @@ export class FileUploader { }); } - private _isSuccessCode(status:any) { + private _isSuccessCode(status:any):boolean { return (status >= 200 && status < 300) || status === 304; } + /* tslint:disable */ private _transformResponse(response:any, headers:any):any { // todo: ? /*var headersGetter = this._headersGetter(headers); @@ -248,121 +322,48 @@ export class FileUploader { });*/ return response; } - - private _parseHeaders(headers:any) { - let parsed:any = {}, key:any, val:any, i:any; - + /* tslint:enable */ + private _parseHeaders(headers:any):any { + let parsed:any = {}; + let key:any; + let val:any; + let i:any; if (!headers) { return parsed; } - headers.split('\n').map((line:any) => { i = line.indexOf(':'); key = line.slice(0, i).trim().toLowerCase(); val = line.slice(i + 1).trim(); - if (key) { parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; } }); - return parsed; } - private _headersGetter(parsedHeaders:any) { - return (name:any) => { - if (name) { - return parsedHeaders[name.toLowerCase()] || null; - } - return parsedHeaders; - }; - } - - _xhrTransport(item:any) { - let xhr = item._xhr = new XMLHttpRequest(); - let form = new FormData(); - - this._onBeforeUploadItem(item); - - // todo - /*item.formData.map(obj => { - obj.map((value, key) => { - form.append(key, value); - }); - });*/ - - if (typeof item._file.size !== 'number') { - throw new TypeError('The file specified is no longer valid'); - } - - form.append(item.alias, item._file, item.file.name); - - xhr.upload.onprogress = (event) => { - let progress = Math.round(event.lengthComputable ? event.loaded * 100 / event.total : 0); - this._onProgressItem(item, progress); - }; - - xhr.onload = () => { - let headers = this._parseHeaders(xhr.getAllResponseHeaders()); - let response = this._transformResponse(xhr.response, headers); - let gist = this._isSuccessCode(xhr.status) ? 'Success' : 'Error'; - let method = '_on' + gist + 'Item'; - (this)[method](item, response, xhr.status, headers); - this._onCompleteItem(item, response, xhr.status, headers); - }; - - xhr.onerror = () => { - let headers = this._parseHeaders(xhr.getAllResponseHeaders()); - let response = this._transformResponse(xhr.response, headers); - this._onErrorItem(item, response, xhr.status, headers); - this._onCompleteItem(item, response, xhr.status, headers); - }; - - xhr.onabort = () => { - let headers = this._parseHeaders(xhr.getAllResponseHeaders()); - let response = this._transformResponse(xhr.response, headers); - this._onCancelItem(item, response, xhr.status, headers); - this._onCompleteItem(item, response, xhr.status, headers); - }; - - xhr.open(item.method, item.url, true); - xhr.withCredentials = item.withCredentials; - - // todo - /*item.headers.map((value, name) => { - xhr.setRequestHeader(name, value); - });*/ - - if (this.authToken) { - xhr.setRequestHeader('Authorization', this.authToken); - } - - xhr.send(form); - this._render(); - } - - private _iframeTransport(item:any) { + /*private _iframeTransport(item:any) { // todo: implement it later - } + }*/ - private _onWhenAddingFileFailed(item:any, filter:any, options:any) { + private _onWhenAddingFileFailed(item:any, filter:any, options:any):void { this.onWhenAddingFileFailed(item, filter, options); } - private _onAfterAddingFile(item:any) { + private _onAfterAddingFile(item:any):void { this.onAfterAddingFile(item); } - private _onAfterAddingAll(items:any) { + private _onAfterAddingAll(items:any):void { this.onAfterAddingAll(items); } - private _onBeforeUploadItem(item:any) { + private _onBeforeUploadItem(item:any):void { item._onBeforeUpload(); this.onBeforeUploadItem(item); } - private _onProgressItem(item:any, progress:any) { + private _onProgressItem(item:any, progress:any):void { let total = this._getTotalProgress(progress); this.progress = total; item._onProgress(progress); @@ -370,36 +371,14 @@ export class FileUploader { this.onProgressAll(total); this._render(); } - - private _onSuccessItem(item:any, response:any, status:any, headers:any) { + /* tslint:disable */ + private _onSuccessItem(item:any, response:any, status:any, headers:any):void { item._onSuccess(response, status, headers); this.onSuccessItem(item, response, status, headers); } - - public _onErrorItem(item:any, response:any, status:any, headers:any) { - item._onError(response, status, headers); - this.onErrorItem(item, response, status, headers); - } - - private _onCancelItem(item:any, response:any, status:any, headers:any) { + /* tslint:enable */ + private _onCancelItem(item:any, response:any, status:any, headers:any):void { item._onCancel(response, status, headers); this.onCancelItem(item, response, status, headers); } - - public _onCompleteItem(item:any, response:any, status:any, headers:any) { - item._onComplete(response, status, headers); - this.onCompleteItem(item, response, status, headers); - - let nextItem = this.getReadyItems()[0]; - this.isUploading = false; - - if (nextItem) { - nextItem.upload(); - return; - } - - this.onCompleteAll(); - this.progress = this._getTotalProgress(); - this._render(); - } } diff --git a/components/file-upload/readme.md b/components/file-upload/readme.md index 37f84be..62b9b18 100644 --- a/components/file-upload/readme.md +++ b/components/file-upload/readme.md @@ -1,32 +1,17 @@ ### Usage ```typescript -import {FileSelect, FileDrop, FileUploader} from 'ng2-file-upload'; +import {FileSelectDirective, FileDropDirective, FileUploader} from 'ng2-file-upload/ng2-file-upload'; ``` ### Annotations ```typescript -// class FileSelect -@Directive({ - selector: '[ng2-file-select]', - properties: ['uploader'], - host: { - '(change)': 'onChange()' - } -}) +// class FileSelectDirective +@Directive({ selector: '[ng2FileSelect]' }) ``` ```typescript -// class FileDrop -@Directive({ - selector: '[ng2-file-drop]', - properties: ['uploader'], - events: ['fileOver'], - host: { - '(drop)': 'onDrop($event)', - '(dragover)': 'onDragOver($event)', - '(dragleave)': 'onDragLeave($event)' - } -}) +// class FileDropDirective +@Directive({ selector: '[ng2FileDrop]' }) ``` ## FileSelect API @@ -48,6 +33,6 @@ import {FileSelect, FileDrop, FileUploader} from 'ng2-file-upload'; ### Events - - `file-over` - it fires during 'over' and 'out' events for Drop Area; returns `boolean`: `true` if file is over Drop Area, `false` in case of out. + - `fileOver` - it fires during 'over' and 'out' events for Drop Area; returns `boolean`: `true` if file is over Drop Area, `false` in case of out. See using in [ts demo](https://github.com/valor-software/ng2-file-upload/blob/master/demo/components/file-upload/simple-demo.ts) and [html demo](https://github.com/valor-software/ng2-file-upload/blob/master/demo/components/file-upload/simple-demo.html) diff --git a/demo/components/file-upload-section.ts b/demo/components/file-upload-section.ts index 5c3fe76..eb4d586 100644 --- a/demo/components/file-upload-section.ts +++ b/demo/components/file-upload-section.ts @@ -1,10 +1,8 @@ -/// - import {Component} from '@angular/core'; import {CORE_DIRECTIVES} from '@angular/common'; import {TAB_DIRECTIVES} from 'ng2-bootstrap/ng2-bootstrap'; -import {SimpleDemo} from './file-upload/simple-demo'; +import {SimpleDemoComponent} from './file-upload/simple-demo'; let name = 'File Upload'; let doc = require('../../components/file-upload/readme.md'); @@ -19,7 +17,7 @@ let tabDesc:Array = [ ]; let tabsContent:string = ``; -tabDesc.forEach(desc => { +tabDesc.forEach((desc:any) => { tabsContent += `
@@ -70,12 +68,12 @@ tabDesc.forEach(desc => {
`, - directives: [SimpleDemo, TAB_DIRECTIVES, CORE_DIRECTIVES] + directives: [SimpleDemoComponent, TAB_DIRECTIVES, CORE_DIRECTIVES] }) -export class FileUploadSection { - private currentHeading:string = 'Simple'; +export class FileUploadSectionComponent { + public currentHeading:string = 'Simple'; - private select(e:any) { + public select(e:any):void { if (e.heading) { this.currentHeading = e.heading; } diff --git a/demo/components/file-upload/file-catcher.js b/demo/components/file-upload/file-catcher.js index ff858e8..960ce2d 100644 --- a/demo/components/file-upload/file-catcher.js +++ b/demo/components/file-upload/file-catcher.js @@ -1,3 +1,4 @@ +/*eslint-disable*/ var express = require('express'); var multer = require('multer'); var fs = require('fs'); diff --git a/demo/components/file-upload/simple-demo.html b/demo/components/file-upload/simple-demo.html index f59cb7a..d0e4c81 100644 --- a/demo/components/file-upload/simple-demo.html +++ b/demo/components/file-upload/simple-demo.html @@ -20,7 +20,7 @@

Select files

-
-
Multiple -
+
Single - +
diff --git a/demo/components/file-upload/simple-demo.ts b/demo/components/file-upload/simple-demo.ts index 3d031c5..cd0bf52 100644 --- a/demo/components/file-upload/simple-demo.ts +++ b/demo/components/file-upload/simple-demo.ts @@ -1,5 +1,3 @@ -/// - import {Component} from '@angular/core'; import {CORE_DIRECTIVES, FORM_DIRECTIVES, NgClass, NgStyle} from '@angular/common'; import {FILE_UPLOAD_DIRECTIVES, FileUploader} from '../../../ng2-file-upload'; @@ -15,16 +13,16 @@ const URL = 'https://evening-anchorage-3159.herokuapp.com/api/'; template: template, directives: [FILE_UPLOAD_DIRECTIVES, NgClass, NgStyle, CORE_DIRECTIVES, FORM_DIRECTIVES] }) -export class SimpleDemo { - private uploader:FileUploader = new FileUploader({url: URL}); - private hasBaseDropZoneOver:boolean = false; - private hasAnotherDropZoneOver:boolean = false; +export class SimpleDemoComponent { + public uploader:FileUploader = new FileUploader({url: URL}); + public hasBaseDropZoneOver:boolean = false; + public hasAnotherDropZoneOver:boolean = false; - private fileOverBase(e:any) { + public fileOverBase(e:any):void { this.hasBaseDropZoneOver = e; } - private fileOverAnother(e:any) { + public fileOverAnother(e:any):void { this.hasAnotherDropZoneOver = e; } } diff --git a/demo/index.html b/demo/index.html index beae0ac..e110fd9 100644 --- a/demo/index.html +++ b/demo/index.html @@ -34,11 +34,5 @@ Loading... - - - - - - diff --git a/demo/index.ts b/demo/index.ts index 6de7dee..2ee024c 100644 --- a/demo/index.ts +++ b/demo/index.ts @@ -1,9 +1,8 @@ -/// import {bootstrap} from '@angular/platform-browser-dynamic'; import {Component} from '@angular/core'; import {NgClass} from '@angular/common'; -import {FileUploadSection} from './components/file-upload-section'; +import {FileUploadSectionComponent} from './components/file-upload-section'; let gettingStarted = require('./getting-started.md'); @@ -36,10 +35,10 @@ let gettingStarted = require('./getting-started.md'); `, directives: [ NgClass, - FileUploadSection + FileUploadSectionComponent ] }) -export class Demo { +export class DemoComponent { } -bootstrap(Demo); +bootstrap(DemoComponent); diff --git a/es6-object.d.ts b/es6-object.d.ts deleted file mode 100644 index bede479..0000000 --- a/es6-object.d.ts +++ /dev/null @@ -1,26 +0,0 @@ -interface ObjectConstructor { - /** - * Copy the values of all of the enumerable own properties from one or more source objects to a - * target object. Returns the target object. - * @param target The target object to copy to. - * @param sources One or more source objects to copy properties from. - */ - assign(target: any, ...sources: any[]): any; - - /** - * Returns true if the values are the same value, false otherwise. - * @param value1 The first value. - * @param value2 The second value. - */ - is(value1: any, value2: any): boolean; - - /** - * Sets the prototype of a specified object o to object proto or null. Returns the object o. - * @param o The object to change its prototype. - * @param proto The value of the new prototype or null. - * @remarks Requires `__proto__` support. - */ - setPrototypeOf(o: any, proto: any): any; -} - -declare function require(path:string): any; diff --git a/gulp-tasks/lint.js b/gulp-tasks/lint.js index 1371ed5..de91156 100644 --- a/gulp-tasks/lint.js +++ b/gulp-tasks/lint.js @@ -1,23 +1,18 @@ -var gulp = require('gulp'); -var esLint = require('gulp-eslint'); -var tslint = require('gulp-tslint'); +'use strict'; -var paths = gulp.paths; +const gulp = require('gulp'); +const tslint = require('gulp-tslint'); +const paths = gulp.paths; -gulp.task('eslint', function() { - return gulp.src(paths.jssrc) - .pipe(esLint({useEslintrc: true})) - .pipe(esLint.format()) - .pipe(esLint.failOnError()); -}); +gulp.task('tslint', () => +gulp + .src(paths.tssrc) + .pipe(tslint()) + .pipe(tslint.report('prose', { + emitError: true, + summarizeFailureOutput: true, + reportLimit: 50 + })) +); -gulp.task('tslint', function() { - return gulp.src(paths.tssrc) - .pipe(tslint()) - .pipe(tslint.report('verbose', { - emitError: true, - reportLimit: 0 - })); -}); - -gulp.task('lint', ['tslint', 'eslint']); +gulp.task('lint', ['tslint']); diff --git a/gulpfile.js b/gulpfile.js index 2eb78a8..a902fca 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,29 +1,18 @@ -var gulp = require('gulp'); +'use strict'; + +const gulp = require('gulp'); gulp.paths = { tssrc: [ '**/*.ts', + '!**/*.d.ts', '!node_modules/**/*', - '!dist/**/*', - '!typings/**/*', - '!**/*.{ts,coffee}.js'], - jssrc: [ - '*.js', - 'gulp-tasks/*.js', - '!ng2-file-upload.js', - '!angular2-file-upload.js', - '!node_modules', - '!**/*.{ts,coffee}.js'] + '!bundles/**/*', + '!typings/**/*'] }; require('require-dir')('./gulp-tasks'); -var clean = require('gulp-clean'); -gulp.task('clean', function () { - return gulp.src('dist', {read: false}) - .pipe(clean()); -}); - -gulp.task('default', function () { +gulp.task('default', () => { gulp.start('lint'); }); diff --git a/karma.conf.js b/karma.conf.js new file mode 100644 index 0000000..31fec66 --- /dev/null +++ b/karma.conf.js @@ -0,0 +1,101 @@ +'use strict'; + +const path = require('path'); +const cwd = process.cwd(); + +module.exports = config => { + config.set({ + // 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: 'test.bundle.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: { + 'test.bundle.js': ['coverage', 'webpack', 'sourcemap'] + }, + + webpack: { + resolve: { + root: [path.resolve(cwd)], + modulesDirectories: ['node_modules', 'demo', 'components', 'test', '.'], + extensions: ['', '.ts', '.js', '.css'] + }, + module: { + loaders: [ + {test: /\.ts$/, loader: 'ts-loader', exclude: [/node_modules/]} + ], + postLoaders: [ + // instrument only testing sources with Istanbul + { + test: /\.(js|ts)$/, + include: root('components'), + loader: 'istanbul-instrumenter-loader', + exclude: [ + /\.e2e\.ts$/, + /node_modules/ + ] + } + ] + }, + stats: { + colors: true, + reasons: true + }, + watch: true, + debug: true + }, + + coverageReporter: { + dir: 'coverage/', + reporters: [ + {type: 'text'}, + {type: 'json'}, + {type: 'html'} + ] + }, + webpackServer: {noInfo: true}, + + // test results reporter to use + // possible values: 'dots', 'progress' + // available reporters: https://npmjs.org/browse/keyword/karma-reporter + reporters: ['spec', 'coverage'], + + // 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: ['PhantomJS'], + + // Continuous Integration mode + // if true, Karma captures browsers, runs the tests and exits + singleRun: true + }); +}; + +function root(partialPath) { + return path.join(__dirname, partialPath); +} diff --git a/ng2-file-upload.ts b/ng2-file-upload.ts index 74d20a4..353f03c 100644 --- a/ng2-file-upload.ts +++ b/ng2-file-upload.ts @@ -1,8 +1,14 @@ -export * from './components/file-upload/file-select'; -export * from './components/file-upload/file-drop'; -export * from './components/file-upload/file-uploader'; +export * from './components/file-upload/file-select.directive'; +export * from './components/file-upload/file-drop.directive'; +export * from './components/file-upload/file-uploader.class'; -import {FileSelect} from './components/file-upload/file-select'; -import {FileDrop} from './components/file-upload/file-drop'; +import {FileSelectDirective} from './components/file-upload/file-select.directive'; +import {FileDropDirective} from './components/file-upload/file-drop.directive'; -export const FILE_UPLOAD_DIRECTIVES:[any] = [FileSelect, FileDrop]; +export const FILE_UPLOAD_DIRECTIVES:[any] = [FileSelectDirective, FileDropDirective]; + +export default { + directives: [ + FILE_UPLOAD_DIRECTIVES + ] +}; diff --git a/package.json b/package.json index 5fcfa7a..1510712 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,32 @@ { "name": "ng2-file-upload", - "version": "1.0.0-beta.1", + "version": "1.0.1", "description": "angular2 file upload directives", "scripts": { - "deploy": "NODE_ENV=production webpack -p --progress --color --optimize-minimize --optimize-dedupe --optimize-occurence-order", - "prepublish": "gulp clean && ./node_modules/.bin/typings install && tsc", - "server": "webpack-dev-server --hot --inline --colors --display-error-details --display-cached", - "start": "npm run server", - "test": "gulp lint" + "flow.install:typings": "./node_modules/.bin/typings install", + "flow.compile": "npm run flow.install:typings && npm run flow.compile:common && npm run flow.compile:system ", + "flow.compile:common": "./node_modules/.bin/tsc", + "flow.compile:system": "./.config/bundle-system.js", + "flow.copy:src": "./node_modules/.bin/cpy ng2-file-upload.ts \"components/*.ts\" ts --parents", + "flow.clean": "./node_modules/.bin/del bundles coverage demo-build typings \"components/**/*.+(js|d.ts|js.map)\" dist \"ng2-file-upload.+(js|d.ts|js.map)\"", + "flow.deploy:gh-pages": "npm run flow.build:prod && ./node_modules/.bin/gh-pages -d demo-build", + "flow.eslint": "./node_modules/.bin/eslint --ignore-path .gitignore --ext js --fix . .config", + "flow.tslint": "./node_modules/.bin/gulp lint", + "flow.lint": "npm run flow.eslint && npm run flow.tslint", + "flow.changelog": "./node_modules/.bin/conventional-changelog -i CHANGELOG.md -s -p angular -v", + "flow.github-release": "conventional-github-releaser -p angular", + "flow.build:prod": "NODE_ENV=production ./node_modules/.bin/webpack --progress --color", + "flow.build:dev": "./node_modules/.bin/webpack --progress --color", + "flow.serve:dev": "./node_modules/.bin/webpack-dev-server --hot --inline --colors --display-error-details --display-cached", + "flow.serve:prod": "NODE_ENV=production ./node_modules/.bin/webpack-dev-server --hot --inline --colors --display-error-details --display-cached", + "prepublish": "npm run flow.clean && npm run flow.compile", + "postpublish": "npm run flow.deploy:gh-pages", + "start": "npm run flow.serve:dev", + "pretest": "npm run flow.lint", + "test": "NODE_ENV=test ./node_modules/.bin/karma start", + "preversion": "npm test", + "version": "npm run flow.changelog && git add -A", + "postversion": "git push origin development && git push --tags" }, "main": "ng2-file-upload.js", "typings": "ng2-file-upload.d.ts", @@ -27,8 +46,7 @@ "url": "https://github.com/valor-software/ng2-file-upload/issues" }, "homepage": "https://github.com/valor-software/ng2-file-upload#readme", - "dependencies": { - }, + "dependencies": {}, "peerDependencies": { "@angular/common": "^2.0.0-rc.1", "@angular/core": "^2.0.0-rc.1" @@ -39,39 +57,59 @@ "@angular/core": "^2.0.0-rc.1", "@angular/platform-browser": "^2.0.0-rc.1", "@angular/platform-browser-dynamic": "^2.0.0-rc.1", + "async": "1.5.2", "bootstrap": "3.3.6", - "clean-webpack-plugin": "0.1.9", + "codecov": "1.0.1", "compression-webpack-plugin": "0.3.1", - "es6-promise": "^3.1.2", - "es6-shim": "^0.35.0", - "eslint": "1.10.3", + "conventional-changelog-cli": "1.2.0", + "conventional-github-releaser": "1.1.2", + "copy-webpack-plugin": "2.1.3", + "cpy-cli": "1.0.0", + "del-cli": "0.2.0", + "es6-promise": "3.1.2", + "es6-shim": "0.35.0", + "es7-reflect-metadata": "1.6.0", + "eslint-config-valorsoft": "0.0.10", "exports-loader": "0.6.3", "file-loader": "0.8.5", + "gh-pages": "0.11.0", "gulp": "3.9.1", - "gulp-clean": "0.3.2", - "gulp-eslint": "1.1.1", "gulp-size": "2.1.0", - "gulp-tsc": "1.1.5", "gulp-tslint": "5.0.0", "html-loader": "0.4.3", + "html-webpack-plugin": "2.16.1", + "istanbul-instrumenter-loader": "0.2.0", + "jasmine": "2.4.1", + "karma": "0.13.22", + "karma-chrome-launcher": "1.0.1", + "karma-coverage": "1.0.0", + "karma-jasmine": "1.0.2", + "karma-phantomjs-launcher": "1.0.0", + "karma-sourcemap-loader": "0.3.7", + "karma-spec-reporter": "0.0.26", + "karma-webpack": "1.7.0", + "lite-server": "2.2.0", "markdown-loader": "0.1.7", "marked": "0.3.5", - "moment": "2.13.0", "ng2-bootstrap": "1.0.16", - "pre-commit": "1.1.2", - "prismjs": "valorkin/prism", + "phantomjs-polyfill": "0.0.2", + "phantomjs-prebuilt": "2.1.7", + "pre-commit": "1.1.3", + "prismjs": "1.4.1", "prismjs-loader": "0.0.2", "raw-loader": "0.5.1", "reflect-metadata": "0.1.2", "require-dir": "0.3.0", "rxjs": "5.0.0-beta.6", + "source-map-loader": "0.1.5", + "systemjs-builder": "0.15.16", "ts-loader": "0.8.2", - "tslint": "3.9.0", + "tslint-config-valorsoft": "1.0.3", "typescript": "1.8.10", - "typings": "^0.8.1", + "typings": "0.8.1", "webpack": "1.13.0", "webpack-dev-server": "1.14.1", - "zone.js": "^0.6.12" + "zone.js": "0.6.12" }, "contributors": [ { diff --git a/test.bundle.js b/test.bundle.js new file mode 100644 index 0000000..9ade857 --- /dev/null +++ b/test.bundle.js @@ -0,0 +1,47 @@ +'use strict'; + +/* eslint vars-on-top:0 no-var:0 */ +// @AngularClass +/* + * When testing with webpack and ES6, we have to do some extra + * things get testing to work right. Because we are gonna write test + * in ES6 to, we have to compile those as well. That's handled in + * karma.conf.js with the karma-webpack plugin. This is the entry + * file for webpack test. Just like webpack will create a bundle.js + * file for our client, when we run test, it well compile and bundle them + * all here! Crazy huh. So we need to do some setup + */ +Error.stackTraceLimit = Infinity; +require('phantomjs-polyfill'); +require('es6-promise'); +require('es6-shim'); +require('es7-reflect-metadata/dist/browser'); + +// require('zone.js'); +require('zone.js/dist/zone.js'); +require('zone.js/dist/long-stack-trace-zone.js'); +require('zone.js/dist/jasmine-patch.js'); +require('zone.js/dist/async-test.js'); + +var testing = require('@angular/core/testing'); +var browser = require('@angular/platform-browser-dynamic/testing'); + +testing.setBaseTestProviders( + browser.TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS, + browser.TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS); + +/* + 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 an function/object with file names as keys. + using that regex we are saying look in ./src/app and ./test then find + any file that ends with spec.js and get its path. By passing in true + we say do this recursively + */ +var testContext = require.context('./components', 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 +testContext.keys().forEach(testContext); diff --git a/tsd.d.ts b/tsd.d.ts deleted file mode 100644 index 8c3121c..0000000 --- a/tsd.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/tslint.json b/tslint.json index 262192e..ea3f776 100644 --- a/tslint.json +++ b/tslint.json @@ -1,56 +1,4 @@ { - "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": [false, 140], - "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-shadowed-variable": true, - "no-string-literal": true, - "no-switch-case-fall-through": true, - "trailing-comma": false, - "no-trailing-whitespace": true, - "no-unused-expression": true, - "no-unused-variable": false, - "no-unreachable": true, - "no-use-before-declare": true, - "no-var-keyword": true, - "one-line": [true, - "check-open-brace", - "check-catch", - "check-else", - "check-whitespace" - ], - "quotemark": [true, "single"], - "radix": true, - "semicolon": true, - "object-literal-sort-keys": false, - "triple-equals": [true, "allow-null-check"], - "variable-name": false, - "whitespace": [true, - "check-branch", - "check-decl", - "check-operator", - "check-separator" - ] - } + "extends": "tslint-config-valorsoft", + "rulesDirectory": "./node_modules/codelyzer" } diff --git a/typings.json b/typings.json index 8279f82..592b524 100644 --- a/typings.json +++ b/typings.json @@ -1,9 +1,12 @@ { "dependencies": { + "moment": "registry:npm/moment#2.10.5+20160211003958", "webpack": "registry:npm/webpack#1.12.9+20160219013405" }, "devDependencies": {}, "ambientDependencies": { - "es6-shim": "registry:dt/es6-shim#0.31.2+20160317120654" + "es6-shim": "registry:dt/es6-shim#0.31.2+20160317120654", + "jasmine": "registry:dt/jasmine#2.2.0+20160317120654", + "require": "registry:dt/require#2.1.20+20160316155526" } -} \ No newline at end of file +} diff --git a/webpack.config.js b/webpack.config.js index 8ee6d46..2bb2843 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,35 +1,35 @@ -var path = require('path'); -var marked = require('marked'); -var webpack = require('webpack'); +/* eslint global-require: 0 */ +'use strict'; -var Clean = require('clean-webpack-plugin'); -var CompressionPlugin = require('compression-webpack-plugin'); +const path = require('path'); +const marked = require('marked'); +const webpack = require('webpack'); +const reqPrism = require('prismjs'); +const CompressionPlugin = require('compression-webpack-plugin'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); // marked renderer hack -marked.Renderer.prototype.code = function (code, lang) { - var out = this.options.highlight(code, lang); +marked.Renderer.prototype.code = function renderCode(code, lang) { + const out = this.options.highlight(code, lang); + const classMap = this.options.langPrefix + lang; if (!lang) { - return '
' + out + '\n
'; + return `
${out}\n
`; } - - var classMap = this.options.langPrefix + lang; - return '
' + out + '\n
\n'; + return `
${out}\n
\n`; }; /*eslint no-process-env:0, camelcase:0*/ -var isProduction = (process.env.NODE_ENV || 'development') === 'production'; +const isProduction = (process.env.NODE_ENV || 'development') === 'production'; +const devtool = process.env.NODE_ENV === 'test' ? 'inline-source-map' : 'source-map'; +const dest = 'demo-build'; +const absDest = root(dest); -var src = 'demo'; -//var absSrc = path.join(__dirname, src); -var dest = '/build'; -var absDest = path.join(__dirname, dest); - -var config = { +const config = { // isProduction ? 'source-map' : 'evale', - devtool: 'source-map', - debug: true, - cache: true, + devtool, + debug: false, verbose: true, displayErrorDetails: true, @@ -40,9 +40,9 @@ var config = { }, resolve: { + cache: false, root: __dirname, - extensions: ['', '.ts', '.js', '.json'], - alias: {} + extensions: ['', '.ts', '.js', '.json'] }, entry: { @@ -55,8 +55,8 @@ var config = { '@angular/common', '@angular/core' ], - 'angular2-file-upload': ['ng2-file-upload'], - 'angular2-file-upload-demo': 'demo' + 'angular2-bootstrap': ['ng2-file-upload'], + 'angular2-bootstrap-demo': 'demo' }, output: { @@ -71,23 +71,20 @@ var config = { inline: true, colors: true, historyApiFallback: true, - proxy: { - '*/api/*': 'http://localhost:3000/' - }, - contentBase: src, - publicPath: dest + contentBase: dest, + //publicPath: dest, + outputPath: dest, + watchOptions: {aggregateTimeout: 300, poll: 1000} }, markdownLoader: { langPrefix: 'language-', - highlight: function (code, lang) { - var language = !lang || lang === 'html' ? 'markup' : lang; - if (!global.Prism) { - global.Prism = require('prismjs'); - } - var Prism = global.Prism; + highlight(code, lang) { + const language = !lang || lang === 'html' ? 'markup' : lang; + const Prism = global.Prism || reqPrism; + if (!Prism.languages[language]) { - require('prismjs/components/prism-' + language + '.js'); + require(`prismjs/components/prism-${language}.js`); } return Prism.highlight(code, Prism.languages[language]); } @@ -96,77 +93,65 @@ var config = { loaders: [ // support markdown {test: /\.md$/, loader: 'html?minimize=false!markdown'}, - // Support for *.json files. {test: /\.json$/, loader: 'json'}, - // Support for CSS as raw text {test: /\.css$/, loader: 'raw'}, - // support for .html as raw text {test: /\.html$/, loader: 'raw'}, - // Support for .ts files. { test: /\.ts$/, loader: 'ts', query: { - ignoreDiagnostics: [ - 6053, - // TS2305 -> Module 'ng' has no exported member - 2305, - // TS2307 -> Cannot find external module - 2307, - // TS2300 -> Duplicate identifier - 2300, - // TS2309 -> An export assignment cannot be used in a module with other exported elements. - 2309 - ] + compilerOptions: { + removeComments: true, + noEmitHelpers: false + } }, - exclude: [ - /\.min\.js$/, - /\.spec\.ts$/, - /\.e2e\.ts$/, - /web_modules/, - /test/ - ] + exclude: [/\.(spec|e2e)\.ts$/] } ], noParse: [ /rtts_assert\/src\/rtts_assert/, - /reflect-metadata/ + /reflect-metadata/, + /zone\.js\/dist\/zone-microtask/ ] }, plugins: [ - new Clean(['build']), + //new Clean([dest]), + new webpack.optimize.DedupePlugin(), + new webpack.optimize.OccurenceOrderPlugin(true), new webpack.optimize.CommonsChunkPlugin({ name: 'angular2', minChunks: Infinity, filename: 'angular2.js' }), - new webpack.optimize.DedupePlugin({ - __isProduction: isProduction - }), - new webpack.optimize.OccurenceOrderPlugin(), - new webpack.optimize.DedupePlugin() + // static assets + new CopyWebpackPlugin([{from: 'demo/favicon.ico', to: 'favicon.ico'}]), + new CopyWebpackPlugin([{from: 'demo/assets', to: 'assets'}]), + // generating html + new HtmlWebpackPlugin({template: 'demo/index.html'}) ], - pushPlugins: function () { + pushPlugins() { if (!isProduction) { return; } - - this.plugins.push.apply(this.plugins, [ + const plugins = [ //production only new webpack.optimize.UglifyJsPlugin({ + beautify: false, + mangle: false, + comments: false, compress: { - warnings: false, - drop_debugger: false - }, - output: { - comments: false - }, - beautify: false + screw_ie8: true + //warnings: false, + //drop_debugger: false + } + //verbose: true, + //beautify: false, + //quote_style: 3 }), new CompressionPlugin({ asset: '{file}.gz', @@ -175,10 +160,19 @@ var config = { threshold: 10240, minRatio: 0.8 }) - ]); + ]; + + this + .plugins + .push + .apply(plugins); } }; config.pushPlugins(); module.exports = config; + +function root(partialPath) { + return path.join(__dirname, partialPath); +}