Compare commits

...

68 Commits

Author SHA1 Message Date
dependabot[bot]
7911b51c60 chore(deps): bump loader-utils, @angular-devkit/build-angular and @nrwl/angular
Bumps [loader-utils](https://github.com/webpack/loader-utils) to 3.2.1 and updates ancestor dependencies [loader-utils](https://github.com/webpack/loader-utils), [loader-utils](https://github.com/webpack/loader-utils), [@angular-devkit/build-angular](https://github.com/angular/angular-cli) and [@nrwl/angular](https://github.com/nrwl/nx/tree/HEAD/packages/angular). These dependencies need to be updated together.


Updates `loader-utils` from 2.0.2 to 3.2.1
- [Release notes](https://github.com/webpack/loader-utils/releases)
- [Changelog](https://github.com/webpack/loader-utils/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack/loader-utils/compare/v2.0.2...v3.2.1)

Updates `loader-utils` from 1.4.0 to 3.2.1
- [Release notes](https://github.com/webpack/loader-utils/releases)
- [Changelog](https://github.com/webpack/loader-utils/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack/loader-utils/compare/v2.0.2...v3.2.1)

Updates `@angular-devkit/build-angular` from 14.2.3 to 14.2.10
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular-cli/compare/14.2.3...14.2.10)

Updates `@nrwl/angular` from 14.7.5 to 14.8.6
- [Release notes](https://github.com/nrwl/nx/releases)
- [Commits](https://github.com/nrwl/nx/commits/14.8.6/packages/angular)

---
updated-dependencies:
- dependency-name: loader-utils
  dependency-type: indirect
- dependency-name: loader-utils
  dependency-type: indirect
- dependency-name: "@angular-devkit/build-angular"
  dependency-type: direct:development
- dependency-name: "@nrwl/angular"
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-17 23:29:24 +00:00
SvetlanaMuravlova
ad01e31cf8 feat(core): updated version up to angular 14 and nx 14 (#1205)
* feat(chore): updated version up to angular 13 and nx 13

* version fixies

* chore(bump): updated version up to angular, nx 14

* check key

* fix(ci): fixed yml files

* fix(ci): fix build commands

* fix(ci): fix build commands

* fix(ci): fix build commands

* fix(ci): fix build commands

* fix(chore): fixed peerdependencies

* fix(test): deleted ts-ignore from .spec files

* fix(test): little cleaning

* fix(chore): deleted unnecessery command
2022-09-28 02:05:37 -07:00
Rick van Lieshout
6608960cec feat(package): Added support for Angular 13. fixes #1193 (#1201)
* updated versioning, added changelog docs

* update to angular 12

* update of eslint

* update of angular 13

* updated nx to 13
2022-07-19 23:53:57 +03:00
Dmitriy Shekhovtsov
19ec08cc02 2.0.0-3 2021-09-07 11:35:42 +03:00
Dmitriy Shekhovtsov
0cf7db94f2 chore(changelog): update [skip ci] 2021-09-07 11:35:42 +03:00
SvetlanaMuravlova
02a1b2a53c feat(demo): updated up to bootstrap 5 (#1186)
* feat(demo): updated up to bootstrap 5

* feat(demo): deleted commented and extra code

* fix(readme): deleted slack link

* fix(readme): deleted slack link
2021-09-07 11:35:02 +03:00
Dmitriy Shekhovtsov
2d8947e07c 2.0.0-2 2021-09-03 16:42:27 +03:00
Dmitriy Shekhovtsov
020d55182d chore(changelog): update [skip ci] 2021-09-03 16:42:27 +03:00
SvetlanaMuravlova
8f3b7cf534 refactor(readme): updated readme and package.json (#1184) 2021-09-03 16:41:58 +03:00
Dmitriy Shekhovtsov
2cc794a04b 2.0.0-1 2021-09-03 15:12:40 +03:00
Dmitriy Shekhovtsov
6cc1909aae chore(changelog): update [skip ci] 2021-09-03 15:12:40 +03:00
SvetlanaMuravlova
326e3ac1a8 feat(version): added npm versions (#1183)
* feat(version): added npm versions

* feat(version): added npm versions
2021-09-03 15:12:02 +03:00
Dmitriy Shekhovtsov
b4cf6c53e3 2.0.0-0 2021-09-03 13:47:33 +03:00
Dmitriy Shekhovtsov
f2271a6139 chore(changelog): update [skip ci] 2021-09-03 13:47:33 +03:00
SvetlanaMuravlova
be27edbe13 chore(bump): updated versions (#1177)
* feat(upgrade): updated up to angular 11 tests are failed

* chore(bump): updated versions

* chore(bump): updated package

* fix(style): delete extra rule

* disabled ivy build, added prod config, changed demo serve script

* feat(bump): added strict mode, doesn't build in dist, should be resolved

* feat(core): added nx

* feat(core): updated dependencies list

* feat(github actions): check gh actions

* feat(gh actions): try gh actions

* feat(gh actions): try gh actions

* feat(gh actions): try gh actions

* feat(gh actions): try gh actions

* feat(gh actions): try gh actions

* feat(github actions): delete codecov

* feat(firebase): try firebase actions

* feat(firebase): try firebase actions

* feat(firebase): try firebase actions

* feat(firebase): try firebase actions

* feat(firebase): try firebase actions

* feat(strict): added strict mode

* feat(github actions): updated yml file

* fix(lint): fixed linting errors

* fix(lint): fixed linting errors

* fix(lint): fixed lint errors

* Delete hosting.ZGlzdC9hcHBzL2RlbW8.cache

* feat(github actions): added publish action

* fix(firebase): test extra folder https

Co-authored-by: Mishchenko Dmitriy <ripatrip@gmail.com>
Co-authored-by: Dmitriy Shekhovtsov <valorkin@gmail.com>
2021-09-03 13:44:45 +03:00
PARAM MITTAL
8171bc831b fix(typo): fix grammatical mistake in readme (#1119) 2019-10-30 17:37:02 +02:00
Vitaliy Makogon
d63c8ce9e2 Merge pull request #1107 from earshinov/update-to-angular8
feta(build): update to Angular 8. Use Angular CLI instead of ngm for building. Ensure compatibility with Ivy on application side.
2019-10-23 12:49:47 +03:00
Vitaliy Makogon
33ac156208 fix(ci): fix xvfb service issue 2019-10-22 16:04:21 +03:00
Eugene Arshinov
2888f4cb11 Provide angularCompilerOptions to ensure compatibility with Ivy (on the application side)
Without this configuration, the following error occurs:

```
error TS-996002: Appears in the NgModule.imports of ...Module, but could not be resolved to an NgModule class
```

Configuruation taken from https://github.com/froala/angular-froala-wysiwyg/pull/329/files#diff-bb630a56aac9aa4924037d7db039769d
2019-08-27 12:39:13 +03:00
Eugene Arshinov
7a342268bb Use Angular CLI instead of ngm for building
Some commands:

npm run build      - to build the library.  Output is in dist/ng2-file-upload/.
npm run test       - to run tests.  Coverage report is in coverage/ as before.
npm run demo.serve - to build demo application and serve it through browsersync
npm run start      - to run demo application using `ng serve`.  Requires the library to be built and linked first.
2019-08-27 12:39:13 +03:00
Eugene Arshinov
6cb3f87e57 Remove unused i18n and e2e stuff from angular.json 2019-08-27 12:39:13 +03:00
Eugene Arshinov
49a3334942 Update tslint and tslint-config-valorsoft 2019-08-27 12:39:13 +03:00
Eugene Arshinov
9fae840b69 Remove unused dependency ng2-page-scroll 2019-08-27 12:39:12 +03:00
Eugene Arshinov
7b4f871200 Run ng update to update to latest Angular 8.3.0 2019-08-27 12:39:12 +03:00
Eugene Arshinov
268534b3e1 Repair npm run test 2019-08-27 12:39:12 +03:00
Eugene Arshinov
77f2466f17 Use ng update to convert .angular-cli.json into angular.json 2019-08-27 12:39:12 +03:00
Eugene Arshinov
01c2d173f3 Update to @angular/core@6.0.0, @angular/cli@6.2.8, rxjs@6.0.0
Aux changes:
- Added package-lock.json to lock dependency versions
- Removed unused @angular/http from dependencies
2019-08-27 12:39:12 +03:00
Adrian Fâciu
fb48f4411d Merge pull request #927 from Amos47/development
Expand compression mime types
2017-12-09 23:07:49 +02:00
Adrian Fâciu
e423a3089c Merge pull request #939 from mattmcsparran/patch-1
Updated Readme for easier implementation
2017-12-09 23:06:14 +02:00
Matthew McSparran
502789dad6 Updated Readme
Updated to show what needs to be imported for a project to work like the demo.
2017-12-09 12:42:15 -05:00
Adrian Fâciu
d4bed8a045 Merge pull request #930 from adrianfaciu/release-1.3
Releasing 1.3.0
2017-11-25 19:09:39 +02:00
Adrian Fâciu
954240cfdc Releasing 1.3.0 2017-11-25 19:07:56 +02:00
Jordan Amos
7d25b57e1b expand compression mime types 2017-11-17 16:50:11 -08:00
Adrian Fâciu
0d97b76228 Merge pull request #904 from guanbo/options
Update: setOptions
2017-10-22 13:03:45 +03:00
Adrian Fâciu
9d0408e595 Merge pull request #911 from WisdomFusion/development
add file type .zip
2017-10-22 12:59:19 +03:00
WisdomFusion
35c5131c47 add file type .zip 2017-10-16 08:17:34 +08:00
Guan Bo
ccee135949 Update: setOptions
prevent from overwrite options which set before, such as authToken
2017-10-09 15:42:00 +08:00
Adrian Fâciu
aa1448392b Merge pull request #903 from adrianfaciu/chore/docs
Updating docs and formatting files
2017-10-07 13:00:53 +03:00
Adrian Fâciu
e1e36bbab4 Updating docs and formating files 2017-10-07 12:48:05 +03:00
Adrian Fâciu
bcf40f4407 Merge pull request #649 from infinum/feature/file-like-object-with-raw-file
Add rawFile in the file like object
2017-10-07 12:07:51 +03:00
Adrian Fâciu
e1bc9ff086 Merge pull request #658 from kopertop/development
Adds some Fixes for AWS Support.
2017-10-07 12:06:23 +03:00
Adrian Fâciu
631b4aa607 Merge pull request #786 from albpara/feature/emit-event-when-file-is-selected
Emit event when file is selected
2017-10-07 12:04:31 +03:00
Adrian Fâciu
57b2d0711d Merge branch 'development' into feature/emit-event-when-file-is-selected 2017-10-07 11:53:53 +03:00
Adrian Fâciu
898f297967 Merge pull request #901 from davidmds/development
Add response and function to modify the request body
2017-10-07 11:48:47 +03:00
David Martins
be7f164d98 Updating example 2017-10-06 17:22:20 -03:00
David Martins
4c0c4c70bc Fixing bug in response 2017-10-06 17:20:26 -03:00
David Martins
c84046e86e Update README 2017-10-04 17:03:16 -03:00
David Martins
14812e0cf7 Added option to send request body modifier function 2017-10-04 16:52:28 -03:00
David Martins
5357a243f6 Added event with return of request 2017-10-04 16:36:48 -03:00
Adrian Fâciu
5df3c5f076 Merge pull request #844 from spike31/patch-1
Fix correct path for isHTML5 option
2017-10-02 17:57:14 +03:00
Adrian Fâciu
7fc4945ff0 Merge pull request #857 from joshterrill/development
Added onFileDrop() event to documentation
2017-10-02 17:50:35 +03:00
Adrian Fâciu
0370d334d7 Merge pull request #898 from adrianfaciu/chore/tests
Fixing tests execution and adding a few more tests
2017-10-02 17:27:52 +03:00
Adrian Fâciu
048f534aa2 Adding tests for file select directive 2017-10-01 17:33:38 +03:00
Adrian Fâciu
0df30f996f Fixing test execution on CI 2017-10-01 17:07:54 +03:00
Adrian Fâciu
6bdf60fac4 Adding tests for file drop directive 2017-09-26 23:04:21 +03:00
Adrian
eca11298ee Fixed karma config 2017-09-26 21:40:55 +03:00
Joshua Terrill
28d98bc8af Added onFileDrop() event to documentation
In case anyone wants to use the onFileDrop event to handle the files in their own way, I've added it to the documentation.
2017-08-01 15:08:21 -07:00
Gilles Gauthier
74306fc210 Update simple demo
Fix correct path for isHTML5 option
2017-07-12 11:35:55 +02:00
Alberto Para
1f0ca3072b Emit event when file is selected 2017-05-08 23:55:06 +02:00
Dmitriy Shekhovtsov
41759be974 chore(package): dependencies upgrade 2017-04-10 13:57:32 +03:00
Dmitriy Shekhovtsov
e8d5ced3f6 chore(package): update to angular cli v1.0.0 2017-04-10 13:48:58 +03:00
smartm0use
7704e0e970 feat(package): relaxed peer dependencies to allow ng v4 (#713)
Add Angular 4 peer dependencies support
2017-04-10 13:31:27 +03:00
Chris Moyer
a333c59f82 Make sure it's a string before trying to do indexOf 2017-03-07 09:59:55 -05:00
Chris Moyer
8b36c892d5 Fixes {{file_name}} template 2017-03-06 17:41:47 -05:00
Chris Moyer
0e17d55633 Adds some Fixes for AWS Support.
AWS Requires any additional parameters to be specified BEFORE Files.
This also adds the ability to specify a {{file_name}} template in any
additional parameters to allow adding the filename to additional
parameters.

For example, you can now specify:

additionalParameter: {
	key: "path/to/uploads/{{file_name}}"
}
2017-03-06 17:30:18 -05:00
Charles Mark Maynard
99c83035d1 [On behalf of Lexmark]: Add export of FileLikeObject to allow callback to function (#619) 2017-03-01 23:28:57 +02:00
Gabrijel Škoro
e08f80ee74 Add rawFile in the file like object 2017-02-28 10:09:26 +01:00
Dmitriy Shekhovtsov
cf12851e1d chore(readme): badges updates 2017-01-18 15:19:20 +02:00
98 changed files with 21923 additions and 9429 deletions

41
.eslintrc.json Normal file
View File

@@ -0,0 +1,41 @@
{
"root": true,
"ignorePatterns": [
"**/*"
],
"plugins": [
"@nrwl/nx"
],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {
"@nrwl/nx/enforce-module-boundaries": [
"error",
{
"enforceBuildableLibDependency": true,
"allow": [],
"depConstraints": [
{ "sourceTag": "*", "onlyDependOnLibsWithTags": ["*"] }
]
}
]
}
},
{
"files": ["*.ts", "*.tsx"],
"extends": ["plugin:@nrwl/nx/typescript"],
"parserOptions": { "project": "./tsconfig.*?.json" },
"rules": {
"semi": "off",
"@typescript-eslint/semi": ["error"],
"@typescript-eslint/ban-ts-comment": "off"
}
},
{
"files": ["*.js", "*.jsx"],
"extends": ["plugin:@nrwl/nx/javascript"],
"rules": {}
}
]
}

5
.firebaserc Normal file
View File

@@ -0,0 +1,5 @@
{
"projects": {
"default": "ngx-file-upload"
}
}

121
.github/workflows/on-publish.yml vendored Normal file
View File

@@ -0,0 +1,121 @@
name: on-release
on:
push:
tags:
- v*
env:
NX_BRANCH: ${{ github.event.number }}
NX_RUN_GROUP: ${{ github.run_id }}
NX_CLOUD_AUTH_TOKEN: ${{ secrets.NX_CLOUD_AUTH_TOKEN }}
MOZ_HEADLESS: 1
CONVENTIONAL_GITHUB_RELEASER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
jobs:
# one run
one_run:
runs-on: ubuntu-latest
steps:
- name: Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.9.0
with:
access_token: ${{ secrets.GITHUB_TOKEN }}
# install dependencies
install:
runs-on: ubuntu-latest
needs: one_run
steps:
- uses: actions/checkout@v2.3.4
- uses: actions/cache@v2.1.4
id: cache
with:
path: node_modules
key: node_modules-${{ hashFiles('**/package-lock.json') }}
- run: npm ci
if: steps.cache.outputs.cache-hit != 'true'
# build ng2-file-upload
build:
needs: install
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.3.4
- uses: actions/cache@v2.1.4
with:
path: node_modules
key: node_modules-${{ hashFiles('**/package-lock.json') }}
- uses: actions/cache@v2.1.4
with:
path: |
dist
key: dist-${{ github.run_id }}
- run: npx nx run ng2-file-upload-demo:build
# update release notes in github
update_release_draft:
needs: install
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.3.4
- uses: actions/cache@v2.1.4
with:
path: node_modules
key: node_modules-${{ hashFiles('**/package-lock.json') }}
- run: npx conventional-github-releaser -p angular
# update gh_pages
gh_pages_deploy:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v2.3.4
- uses: actions/checkout@v2.3.4
with:
ref: 'gh-pages'
path: 'gh-pages'
- uses: actions/cache@v2.1.4
with:
path: node_modules
key: node_modules-${{ hashFiles('**/package-lock.json') }}
- uses: actions/cache@v2.1.4
with:
path: |
dist
key: dist-${{ github.run_id }}
- run: |
cd gh-pages
git config user.email gh-actions-${GITHUB_ACTOR}@github.com
git config user.name $GITHUB_ACTOR
git add -A
git commit -am "ci: gh-pages update"
continue-on-error: true
- name: push to gh-pages
uses: ad-m/github-push-action@v0.6.0
continue-on-error: true
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
branch: 'gh-pages'
directory: 'gh-pages'
# publish to npm
npm_publish:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v2.3.4
- uses: actions/cache@v2.1.4
with:
path: node_modules
key: node_modules-${{ hashFiles('**/package-lock.json') }}
- uses: actions/cache@v2.1.4
with:
path: |
dist
key: dist-${{ github.run_id }}
- uses: JS-DevTools/npm-publish@v1
with:
package: "dist/libs/ng2-file-upload/package.json"
token: ${{ secrets.NPM_TOKEN }}

113
.github/workflows/on-push-or-pull.yml vendored Normal file
View File

@@ -0,0 +1,113 @@
name: on-pull-request-or-push
on:
pull_request:
push:
branches:
- development
env:
NX_BRANCH: ${{ github.event.number }}
NX_RUN_GROUP: ${{ github.run_id }}
NX_CLOUD_AUTH_TOKEN: ${{ secrets.NX_CLOUD_AUTH_TOKEN }}
MOZ_HEALESS: 1
SAUCE_USERNAME_PR: valorkinpr
FIREBASE_CHANNEL: ${{ fromJSON('["", "live"]')[!github.base_ref] }}
CACHE_NODE_MODULES_PATH: |
~/.npm
node_modules
CACHE_DIST_PATH: |
dist
jobs:
# one run
one_run:
runs-on: ubuntu-latest
steps:
- name: Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.9.0
with:
access_token: ${{ secrets.GITHUB_TOKEN }}
# install dependencies
install:
runs-on: ubuntu-latest
needs: one_run
steps:
- uses: actions/checkout@v2.3.4
- uses: actions/cache@v2.1.4
id: cache
with:
path: ${{ env.CACHE_NODE_MODULES_PATH }}
key: node_modules-${{ hashFiles('**/package-lock.json') }}
- run: npm ci
if: steps.cache.outputs.cache-hit != 'true'
# build ng2-file-upload
build:
needs: install
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.3.4
- uses: actions/cache@v2.1.4
with:
path: ${{ env.CACHE_NODE_MODULES_PATH }}
key: node_modules-${{ hashFiles('**/package-lock.json') }}
- uses: actions/cache@v2.1.4
with:
path: ${{ env.CACHE_DIST_PATH }}
key: dist-${{ github.run_id }}
- run: npx nx run ng2-file-upload-demo:build
# run unit tests
unit_tests_with_coverage:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: ${{ env.CACHE_NODE_MODULES_PATH }}
key: node_modules-${{ hashFiles('**/package-lock.json') }}
- uses: actions/cache@v2
with:
path: ${{ env.CACHE_DIST_PATH }}
key: dist-${{ github.run_id }}
- run: npm run test-coverage
continue-on-error: true
# run linting
linting:
runs-on: ubuntu-latest
needs: install
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: ${{ env.CACHE_NODE_MODULES_PATH }}
key: node_modules-${{ hashFiles('**/package-lock.json') }}
- run: npm run lint
# firebase preview
build_and_preview:
runs-on: ubuntu-latest
needs: build
outputs:
output_url: ${{ steps.firebase_hosting_preview.outputs.details_url }}
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: ${{ env.CACHE_DIST_PATH }}
key: dist-${{ github.run_id }}
- uses: FirebaseExtended/action-hosting-deploy@v0
continue-on-error: true
id: firebase_hosting_preview
with:
repoToken: '${{ secrets.GITHUB_TOKEN }}'
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_NGX_FILE_UPLOAD }}'
projectId: ngx-file-upload
channelId: ${{ env.FIREBASE_CHANNEL }}
expires: 7d

1
.gitignore vendored
View File

@@ -12,6 +12,7 @@
.settings/
# misc
/.angular/cache
/.sass-cache
/connect.lock
/coverage

View File

@@ -1,11 +1,13 @@
language: node_js
node_js:
- "6"
- "10"
services:
- xvfb
before_install:
- export CHROME_BIN=chromium-browser
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
script:
- npm run pretest

View File

@@ -1,108 +1,168 @@
# [3.0.0]()
Updated the library and test project to Angular 13
# [2.0.0-3](https://github.com/valor-software/ng2-file-upload/compare/v2.0.0-2...v2.0.0-3) (2021-09-07)
### Features
- **demo:** updated up to bootstrap 5 ([#1186](https://github.com/valor-software/ng2-file-upload/issues/1186)) ([02a1b2a](https://github.com/valor-software/ng2-file-upload/commit/02a1b2a53c331e2088ea05d2202be6eb2b1d051f))
# [2.0.0-2](https://github.com/valor-software/ng2-file-upload/compare/v2.0.0-1...v2.0.0-2) (2021-09-03)
# [2.0.0-1](https://github.com/valor-software/ng2-file-upload/compare/v2.0.0-0...v2.0.0-1) (2021-09-03)
### Features
- **version:** added npm versions ([#1183](https://github.com/valor-software/ng2-file-upload/issues/1183)) ([326e3ac](https://github.com/valor-software/ng2-file-upload/commit/326e3ac1a884f000f57b3fb6e30cd32f5ec4622c))
# [2.0.0-0](https://github.com/valor-software/ng2-file-upload/compare/v1.2.0...v2.0.0-0) (2021-09-03)
### Bug Fixes
- **ci:** fix xvfb service issue ([33ac156](https://github.com/valor-software/ng2-file-upload/commit/33ac156208bfcf57851210f037719107e1ca9eb9))
- **typo:** fix grammatical mistake in readme ([#1119](https://github.com/valor-software/ng2-file-upload/issues/1119)) ([8171bc8](https://github.com/valor-software/ng2-file-upload/commit/8171bc831b69692d04b650be19ff82f04ff56662))
### Features
- **package:** relaxed peer dependencies to allow ng v4 ([#713](https://github.com/valor-software/ng2-file-upload/issues/713)) ([7704e0e](https://github.com/valor-software/ng2-file-upload/commit/7704e0e970276ebcd8bfefe34bf153f82108a11e))
# [1.3.0](https://github.com/valor-software/ng2-file-upload/compare/v1.2.0...v1.3.0) (2021-08-31)
### Bug Fixes
- **ci:** fix xvfb service issue ([33ac156](https://github.com/valor-software/ng2-file-upload/commit/33ac156208bfcf57851210f037719107e1ca9eb9))
- **style:** delete extra rule ([b5917b9](https://github.com/valor-software/ng2-file-upload/commit/b5917b9fa77e63c4c1b06598abc817b8033730c3))
- **typo:** fix grammatical mistake in readme ([#1119](https://github.com/valor-software/ng2-file-upload/issues/1119)) ([8171bc8](https://github.com/valor-software/ng2-file-upload/commit/8171bc831b69692d04b650be19ff82f04ff56662))
### Features
- **bump:** added strict mode, doesn't build in dist, should be resolved ([69cd64d](https://github.com/valor-software/ng2-file-upload/commit/69cd64dc287c9bdd1c35af1062e27ce32a47e977))
- **core:** added nx ([de738f7](https://github.com/valor-software/ng2-file-upload/commit/de738f7c63d7f37e07019bc596c02e9f4320f563))
- **package:** relaxed peer dependencies to allow ng v4 ([#713](https://github.com/valor-software/ng2-file-upload/issues/713)) ([7704e0e](https://github.com/valor-software/ng2-file-upload/commit/7704e0e970276ebcd8bfefe34bf153f82108a11e))
- **upgrade:** updated up to angular 11 tests are failed ([ce9dc20](https://github.com/valor-software/ng2-file-upload/commit/ce9dc20056cc6c7cd58e502af05d7d97043c8f3a))
# [1.3.0](https://github.com/valor-software/ng2-file-upload/compare/v1.2.0...v1.3.0) (2021-08-31)
### Bug Fixes
- **ci:** fix xvfb service issue ([33ac156](https://github.com/valor-software/ng2-file-upload/commit/33ac156208bfcf57851210f037719107e1ca9eb9))
- **style:** delete extra rule ([b5917b9](https://github.com/valor-software/ng2-file-upload/commit/b5917b9fa77e63c4c1b06598abc817b8033730c3))
- **typo:** fix grammatical mistake in readme ([#1119](https://github.com/valor-software/ng2-file-upload/issues/1119)) ([8171bc8](https://github.com/valor-software/ng2-file-upload/commit/8171bc831b69692d04b650be19ff82f04ff56662))
### Features
- **bump:** added strict mode, doesn't build in dist, should be resolved ([69cd64d](https://github.com/valor-software/ng2-file-upload/commit/69cd64dc287c9bdd1c35af1062e27ce32a47e977))
- **package:** relaxed peer dependencies to allow ng v4 ([#713](https://github.com/valor-software/ng2-file-upload/issues/713)) ([7704e0e](https://github.com/valor-software/ng2-file-upload/commit/7704e0e970276ebcd8bfefe34bf153f82108a11e))
- **upgrade:** updated up to angular 11 tests are failed ([ce9dc20](https://github.com/valor-software/ng2-file-upload/commit/ce9dc20056cc6c7cd58e502af05d7d97043c8f3a))
<a name="1.3.0"></a>
# [1.3.0](https://github.com/valor-software/ng2-file-upload/compare/v1.2.0...v1.3.0) (2017-11-25)
### Features
- **file-upload:** Add response and function to modify the request body ([#901](https://github.com/valor-software/ng2-file-upload/pull/901))
- **file-upload** add file type .zip ([#911](https://github.com/valor-software/ng2-file-upload/pull/911))
### Bug Fixes
- **file-uploader** Update: setOptions ([#904](https://github.com/valor-software/ng2-file-upload/pull/904))
- **docs** Fix correct path for isHTML5 option ([#844](https://github.com/valor-software/ng2-file-upload/pull/844))
- **docs** Added onFileDrop() event to documentation ([#857](https://github.com/valor-software/ng2-file-upload/pull/857))
<a name="1.2.1"></a>
## [1.2.1](https://github.com/valor-software/ng2-file-upload/compare/v1.2.0...v1.2.1) (2017-04-10)
### Features
- **package:** relaxed peer dependencies to allow ng v4 ([#713](https://github.com/valor-software/ng2-file-upload/issues/713)) ([7704e0e](https://github.com/valor-software/ng2-file-upload/commit/7704e0e))
<a name="1.2.0"></a>
# [1.2.0](https://github.com/valor-software/ng2-file-upload/compare/v1.1.3-0...v1.2.0) (2017-01-17)
### Bug Fixes
* **headers:** Add FileItem headers to XHR ([#553](https://github.com/valor-software/ng2-file-upload/issues/553)) ([e4a7099](https://github.com/valor-software/ng2-file-upload/commit/e4a7099)), closes [#552](https://github.com/valor-software/ng2-file-upload/issues/552)
- **headers:** Add FileItem headers to XHR ([#553](https://github.com/valor-software/ng2-file-upload/issues/553)) ([e4a7099](https://github.com/valor-software/ng2-file-upload/commit/e4a7099)), closes [#552](https://github.com/valor-software/ng2-file-upload/issues/552)
### Features
* **file-select:** Clear file select automatically ([#524](https://github.com/valor-software/ng2-file-upload/issues/524)) ([410efda](https://github.com/valor-software/ng2-file-upload/commit/410efda))
* **fileUpload:** added additionalParameter ([#565](https://github.com/valor-software/ng2-file-upload/issues/565)) ([397de09](https://github.com/valor-software/ng2-file-upload/commit/397de09))
* **package:** upgrade to ng v2.3+ ([#574](https://github.com/valor-software/ng2-file-upload/issues/574)) ([3cc6a99](https://github.com/valor-software/ng2-file-upload/commit/3cc6a99))
- **file-select:** Clear file select automatically ([#524](https://github.com/valor-software/ng2-file-upload/issues/524)) ([410efda](https://github.com/valor-software/ng2-file-upload/commit/410efda))
- **fileUpload:** added additionalParameter ([#565](https://github.com/valor-software/ng2-file-upload/issues/565)) ([397de09](https://github.com/valor-software/ng2-file-upload/commit/397de09))
- **package:** upgrade to ng v2.3+ ([#574](https://github.com/valor-software/ng2-file-upload/issues/574)) ([3cc6a99](https://github.com/valor-software/ng2-file-upload/commit/3cc6a99))
<a name="1.1.3-0"></a>
## [1.1.3-0](https://github.com/valor-software/ng2-file-upload/compare/v1.1.2...v1.1.3-0) (2016-10-19)
### Bug Fixes
* **typing:** added authTokenHeader property to options and file upload class ([b55c852](https://github.com/valor-software/ng2-file-upload/commit/b55c852))
- **typing:** added authTokenHeader property to options and file upload class ([b55c852](https://github.com/valor-software/ng2-file-upload/commit/b55c852))
### Features
* **build:** added support for AoT and ng-cli ([f0b2879](https://github.com/valor-software/ng2-file-upload/commit/f0b2879)), closes [#436](https://github.com/valor-software/ng2-file-upload/issues/436)
* **file-upload:** Add the possibility of set the token header ([#213](https://github.com/valor-software/ng2-file-upload/issues/213)) ([282295c](https://github.com/valor-software/ng2-file-upload/commit/282295c))
- **build:** added support for AoT and ng-cli ([f0b2879](https://github.com/valor-software/ng2-file-upload/commit/f0b2879)), closes [#436](https://github.com/valor-software/ng2-file-upload/issues/436)
- **file-upload:** Add the possibility of set the token header ([#213](https://github.com/valor-software/ng2-file-upload/issues/213)) ([282295c](https://github.com/valor-software/ng2-file-upload/commit/282295c))
<a name="1.1.2"></a>
## [1.1.2](https://github.com/valor-software/ng2-file-upload/compare/v1.1.1...v1.1.2) (2016-10-17)
### Features
* **package:** allow of ng2 v2.0.* and v2.*.* ([87395e6](https://github.com/valor-software/ng2-file-upload/commit/87395e6))
- **package:** allow of ng2 v2.0._ and v2._.\* ([87395e6](https://github.com/valor-software/ng2-file-upload/commit/87395e6))
<a name="1.1.1"></a>
## [1.1.1](https://github.com/valor-software/ng2-file-upload/compare/v1.0.3...v1.1.1) (2016-10-17)
### Bug Fixes
* **uploader:** Add the ability to upload files via PUT instead of POST ([#239](https://github.com/valor-software/ng2-file-upload/issues/239)) ([e068511](https://github.com/valor-software/ng2-file-upload/commit/e068511))
* **zone.js:** error in Safari, Added Typings ([#221](https://github.com/valor-software/ng2-file-upload/issues/221)) ([db77e89](https://github.com/valor-software/ng2-file-upload/commit/db77e89))
- **uploader:** Add the ability to upload files via PUT instead of POST ([#239](https://github.com/valor-software/ng2-file-upload/issues/239)) ([e068511](https://github.com/valor-software/ng2-file-upload/commit/e068511))
- **zone.js:** error in Safari, Added Typings ([#221](https://github.com/valor-software/ng2-file-upload/issues/221)) ([db77e89](https://github.com/valor-software/ng2-file-upload/commit/db77e89))
### Features
* **multipart:** Create disableMultipart option in FileUploader ([#224](https://github.com/valor-software/ng2-file-upload/issues/224)) ([22307d2](https://github.com/valor-software/ng2-file-upload/commit/22307d2))
* **package:** angular ~2.0.1 stable release ([#425](https://github.com/valor-software/ng2-file-upload/issues/425)) ([3fec385](https://github.com/valor-software/ng2-file-upload/commit/3fec385))
* **package:** updated to typescript 2 ([4fef496](https://github.com/valor-software/ng2-file-upload/commit/4fef496))
- **multipart:** Create disableMultipart option in FileUploader ([#224](https://github.com/valor-software/ng2-file-upload/issues/224)) ([22307d2](https://github.com/valor-software/ng2-file-upload/commit/22307d2))
- **package:** angular ~2.0.1 stable release ([#425](https://github.com/valor-software/ng2-file-upload/issues/425)) ([3fec385](https://github.com/valor-software/ng2-file-upload/commit/3fec385))
- **package:** updated to typescript 2 ([4fef496](https://github.com/valor-software/ng2-file-upload/commit/4fef496))
<a name="1.1.0"></a>
# [1.1.0](https://github.com/valor-software/ng2-file-upload/compare/v1.0.3...v1.1.0) (2016-09-21)
# [1.1.0](https://github.com/valor-software/ng2-file-upload/compare/v1.0.3...v1.1.0) (2016-09-21)
### Bug Fixes
* **uploader:** Add the ability to upload files via PUT instead of POST ([#239](https://github.com/valor-software/ng2-file-upload/issues/239)) ([e068511](https://github.com/valor-software/ng2-file-upload/commit/e068511))
* **zone.js:** error in Safari, Added Typings ([#221](https://github.com/valor-software/ng2-file-upload/issues/221)) ([db77e89](https://github.com/valor-software/ng2-file-upload/commit/db77e89))
- **uploader:** Add the ability to upload files via PUT instead of POST ([#239](https://github.com/valor-software/ng2-file-upload/issues/239)) ([e068511](https://github.com/valor-software/ng2-file-upload/commit/e068511))
- **zone.js:** error in Safari, Added Typings ([#221](https://github.com/valor-software/ng2-file-upload/issues/221)) ([db77e89](https://github.com/valor-software/ng2-file-upload/commit/db77e89))
### Features
* **multipart:** Create disableMultipart option in FileUploader ([#224](https://github.com/valor-software/ng2-file-upload/issues/224)) ([22307d2](https://github.com/valor-software/ng2-file-upload/commit/22307d2))
* **package:** updated to typescript 2 ([4fef496](https://github.com/valor-software/ng2-file-upload/commit/4fef496))
- **multipart:** Create disableMultipart option in FileUploader ([#224](https://github.com/valor-software/ng2-file-upload/issues/224)) ([22307d2](https://github.com/valor-software/ng2-file-upload/commit/22307d2))
- **package:** updated to typescript 2 ([4fef496](https://github.com/valor-software/ng2-file-upload/commit/4fef496))
<a name="1.0.3"></a>
## [1.0.3](https://github.com/valor-software/ng2-file-upload/compare/v1.0.2...v1.0.3) (2016-05-12)
<a name="1.0.2"></a>
## 1.0.2 (2016-05-12)
## 1.0.2 (2016-05-12)
### Bug Fixes
* **upload:** merge fix and get filters fix ([ef6091c](https://github.com/valor-software/ng2-file-upload/commit/ef6091c))
- **upload:** merge fix and get filters fix ([ef6091c](https://github.com/valor-software/ng2-file-upload/commit/ef6091c))
### Chores
* **build:** ng2 style guide applied ([aee69d8](https://github.com/valor-software/ng2-file-upload/commit/aee69d8))
- **build:** ng2 style guide applied ([aee69d8](https://github.com/valor-software/ng2-file-upload/commit/aee69d8))
### Features
* **package:** upgrade to angular 2.0.0-rc.1 ([#176](https://github.com/valor-software/ng2-file-upload/issues/176)) ([13c5c35](https://github.com/valor-software/ng2-file-upload/commit/13c5c35)), closes [#180](https://github.com/valor-software/ng2-file-upload/issues/180)
- **package:** upgrade to angular 2.0.0-rc.1 ([#176](https://github.com/valor-software/ng2-file-upload/issues/176)) ([13c5c35](https://github.com/valor-software/ng2-file-upload/commit/13c5c35)), closes [#180](https://github.com/valor-software/ng2-file-upload/issues/180)
### BREAKING CHANGES
- directives and selectors renamed to ng2FileSelect and ng2FileDrop

View File

@@ -1,15 +1,9 @@
# ng2-file-upload [![npm version](https://badge.fury.io/js/ng2-file-upload.svg)](http://badge.fury.io/js/ng2-file-upload)
# ng2-file-upload [![npm version](https://badge.fury.io/js/ng2-file-upload.svg)](http://badge.fury.io/js/ng2-file-upload) [![npm downloads](https://img.shields.io/npm/dm/ng2-file-upload.svg)](https://npmjs.org/ng2-file-upload)
Easy to use Angular2 directives for files upload ([demo](http://valor-software.github.io/ng2-file-upload/))
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.
[![Angular 2 Style Guide](https://mgechev.github.io/angular2-style-guide/images/badge.svg)](https://github.com/mgechev/angular2-style-guide)
[![Build Status](https://travis-ci.org/valor-software/ng2-file-upload.svg?branch=master)](https://travis-ci.org/valor-software/ng2-file-upload)
[![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)
[![Build Status](https://travis-ci.org/valor-software/ng2-file-upload.svg?branch=development)](https://travis-ci.org/valor-software/ng2-file-upload)
[![Dependency Status](https://david-dm.org/valor-software/ng2-file-upload.svg)](https://david-dm.org/valor-software/ng2-file-upload)
[![devDependency Status](https://david-dm.org/valor-software/ng2-file-upload/dev-status.svg)](https://david-dm.org/valor-software/ng2-file-upload#info=devDependencies)
[![Throughput Graph](https://graphs.waffle.io/valor-software/ng2-file-upload/throughput.svg)](https://waffle.io/valor-software/ng2-file-upload/metrics)
## Quick start
@@ -24,6 +18,30 @@ Follow me [![twitter](https://img.shields.io/twitter/follow/valorkin.svg?style=s
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).
## Using ***ng2-file-upload*** in a project
1. Install as shown in the above section.
2. Import `FileUploadModule` into the module that declares the component using ***ng2-file-upload***:
```import { FileUploadModule } from 'ng2-file-upload';```
3. Add it to `[imports]` under `@NgModule`:
```imports: [ ... FileUploadModule, ... ]```
4. Import `FileUploader` into the component:
```import { FileUploader } from 'ng2-file-upload';```
5. Create a variable for the API url:
```const URL = 'path_to_api';```
6. Initialize it:
```public uploader:FileUploader = new FileUploader({url: URL}); ```
## API for `ng2FileSelect`
@@ -31,6 +49,9 @@ Follow me [![twitter](https://img.shields.io/twitter/follow/valorkin.svg?style=s
- `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)
### Events
- `onFileSelected` - fires when files are selected and added to the uploader queue
## API for `ng2FileDrop`
### Properties
@@ -42,17 +63,21 @@ Follow me [![twitter](https://img.shields.io/twitter/follow/valorkin.svg?style=s
1. `url` - URL of File Uploader's route
2. `authToken` - Auth token that will be applied as 'Authorization' header during file send.
3. `disableMultipart` - If 'true', disable using a multipart form for file upload and instead stream the file. Some APIs (e.g. Amazon S3) may expect the file to be streamed rather than sent via a form. Defaults to false.
4. `itemAlias` - item alias (form name redefenition)
4. `itemAlias` - item alias (form name redefinition)
5. `formatDataFunction` - Function to modify the request body. 'DisableMultipart' must be 'true' for this function to be called.
6. `formatDataFunctionIsAsync` - Informs if the function sent in 'formatDataFunction' is asynchronous. Defaults to false.
7. `parametersBeforeFiles` - States if additional parameters should be appended before or after the file. Defaults to false.
### Events
- `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)
- `onFileDrop` - it fires after a file has been dropped on a Drop Area; you can pass in `$event` to get the list of files that were dropped. i.e. `(onFileDrop)="dropped($event)"`
# Troubleshooting
Please follow this guidelines when reporting bugs and feature requests:
Please follow these guidelines when reporting bugs and feature requests:
1. Use [GitHub Issues](https://github.com/valor-software/ng2-file-upload/issues) board to report bugs and feature requests (not our email address)
2. Please **always** write steps to reproduce the error. That way we can focus on fixing the bug, not scratching our heads trying to reproduce it.

View File

@@ -1,57 +0,0 @@
{
"project": {
"name": "ng2-file-upload"
},
"apps": [
{
"root": "demo/src",
"outDir": "demo/dist",
"assets": [
"assets"
],
"index": "index.html",
"main": "main.ts",
"test": "../../scripts/test.ts",
"tsconfig": "tsconfig.json",
"prefix": "",
"mobile": false,
"styles": [
],
"scripts": [
],
"environments": {
"source": "environments/environment.ts",
"dev": "environments/environment.ts",
"prod": "environments/environment.prod.ts"
}
}
],
"addons": [],
"packages": [],
"e2e": {
"protractor": {
"config": "protractor.conf.js"
}
},
"test": {
"karma": {
"config": "karma.conf.js"
}
},
"defaults": {
"styleExt": "css",
"prefixInterfaces": false,
"inline": {
"style": false,
"template": false
},
"spec": {
"class": false,
"component": true,
"directive": true,
"module": false,
"pipe": true,
"service": true
}
}
}

148
angular.json Normal file
View File

@@ -0,0 +1,148 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"ng2-file-upload": {
"root": "libs/ng2-file-upload",
"sourceRoot": "libs/ng2-file-upload/",
"projectType": "library",
"architect": {
"build": {
"builder": "@nrwl/angular:package",
"outputs": [
"dist/libs/ng2-file-upload"
],
"options": {
"tsConfig": "libs/ng2-file-upload/tsconfig.lib.json",
"project": "libs/ng2-file-upload/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "libs/ng2-file-upload/tsconfig.lib.prod.json"
}
}
},
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": [
"libs/ng2-file-upload/**/*.ts"
]
},
"outputs": [
"{options.outputFile}"
]
},
"test": {
"builder": "@nrwl/jest:jest",
"outputs": [
"coverage/libs/ng2-file-upload"
],
"options": {
"jestConfig": "libs/ng2-file-upload/jest.config.ts",
"passWithNoTests": true
}
},
"version": {
"builder": "@nrwl/workspace:run-commands",
"outputs": [],
"options": {
"commands": [
"ts-node ./scripts/set-version.ts",
"conventional-changelog -i CHANGELOG.md -s -p angular",
"git add -A",
"git commit -am \"chore(changelog): update [skip ci] \""
],
"parallel": false
},
"configurations": {
"production": {}
}
}
},
"tags": [
"lib"
]
},
"ng2-file-upload-demo": {
"root": "apps/demo",
"sourceRoot": "apps/demo/src",
"projectType": "application",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/apps/demo",
"index": "apps/demo/src/index.html",
"main": "apps/demo/src/main.ts",
"tsConfig": "apps/demo/tsconfig.json",
"assets": [
"apps/demo/src/assets"
],
"styles": [],
"scripts": [],
"aot": false,
"vendorChunk": true,
"extractLicenses": false,
"buildOptimizer": false,
"sourceMap": true,
"optimization": false,
"namedChunks": true
},
"dependsOn": [
{
"target": "build",
"projects": "dependencies"
}
],
"configurations": {
"production": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"fileReplacements": [
{
"replace": "apps/demo/src/environments/environment.ts",
"with": "apps/demo/src/environments/environment.prod.ts"
}
]
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "ng2-file-upload-demo:build:production"
},
"configurations": {
"production": {
"browserTarget": "ng2-file-upload-demo:build"
}
}
},
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": [
"apps/demo/**/*.ts"
]
},
"outputs": [
"{options.outputFile}"
]
}
},
"tags": [],
"implicitDependencies": [
"ng2-file-upload"
]
}
}
}

29
apps/demo/.eslintrc.json Normal file
View File

@@ -0,0 +1,29 @@
{
"extends": [
"../../.eslintrc.json"
],
"ignorePatterns": [
"!**/*"
],
"overrides": [
{
"files": [
"*.ts"
],
"extends": [
"plugin:@nrwl/nx/angular",
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {}
},
{
"files": [
"*.html"
],
"extends": [
"plugin:@nrwl/nx/angular-template"
],
"rules": {}
}
]
}

12
apps/demo/browserslist Normal file
View File

@@ -0,0 +1,12 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# You can see what browsers were selected by your queries by running:
# npx browserslist
> 0.5%
last 2 versions
Firefox ESR
not dead
not IE 9-11 # For IE 9-11 support, remove 'not'.

4
apps/demo/bs-config.json Normal file
View File

@@ -0,0 +1,4 @@
{
"port": 4200,
"server": { "baseDir": "./dist/apps/demo" }
}

View File

@@ -0,0 +1,55 @@
import { Component } from '@angular/core';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const gettingStarted = require('html-loader!markdown-loader!../getting-started.md');
@Component({
selector: 'app',
template: `
<main class="bd-pageheader">
<div class="container">
<div class="row">
<h1>ng2-file-upload</h1>
<p>The Angular2 File Upload directives</p>
<p>
<a class="btn btn-light w-auto" href="https://github.com/valor-software/ng2-file-upload">View on GitHub</a>
</p>
</div>
<div class="row" style="padding-top: 15px">
<div class="d-flex flex-wrap">
<a href="https://npmjs.org/ng2-file-upload" class="me-2 d-flex mb-2">
<img src="https://img.shields.io/npm/v/ng2-file-upload/latest.svg" alt="npm latest version" >
</a>
<a href="https://npmjs.org/ng2-file-upload" class="me-2 d-flex mb-2">
<img src="https://img.shields.io/npm/v/ng2-file-upload/next.svg" alt="npm next version" >
</a>
<iframe src="https://ghbtns.com/github-btn.html?user=valor-software&repo=ng2-file-upload&type=star&count=true" frameborder="0" scrolling="0" width="100px" height="20px" class="me-2 mb-2"></iframe>
<iframe src="https://ghbtns.com/github-btn.html?user=valor-software&repo=ng2-file-upload&type=fork&count=true" frameborder="0" scrolling="0" width="100px" height="20px" class="mb-2"></iframe>
</div>
</div>
</div>
</main>
<div class="container">
<section id="getting-started" [innerHtml]="gettingStarted"></section>
<file-upload-section class="col-md-12"></file-upload-section>
</div>
<footer class="footer">
<div class="container">
<p class="text-muted text-center"><a href="https://github.com/valor-software/ng2-file-upload">ng2-file-upload</a> is maintained by <a href="https://github.com/valor-software">valor-software</a>.</p>
</div>
</footer>
`
})
export class AppComponent {
public gettingStarted:string = gettingStarted;
public ngAfterContentInit(): any {
setTimeout(()=>{
if (typeof PR !== 'undefined') {
// google code-prettify
PR.prettyPrint();
}
}, 150);
}
}

View File

@@ -3,7 +3,7 @@ import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { TabsModule } from 'ng2-bootstrap';
import { TabsModule } from 'ngx-bootstrap/tabs';
import { FileUploadModule } from 'ng2-file-upload';
import { AppComponent } from './app.component';

View File

@@ -0,0 +1,41 @@
<section [attr.id]="name.toLowerCase()">
<div class="row">
<tabset>
<tab *ngFor="let desc of tabs" heading="{{desc.heading}}" (select)="select($event)">
<div class="card card-block p-3 border-top-0">
<simple-demo></simple-demo>
<br>
<div class="container p-0">
<div class="row" style="margin: 0px;">
<tabset class="p-0">
<tab heading="Markup">
<div class="card card-block p-3 border-top-0">
<pre class="prettyprint linenums lang-html">{{desc.html}}</pre>
</div>
</tab>
<tab heading="TypeScript">
<div class="card card-block p-3 border-top-0">
<pre class="prettyprint linenums lang-js">{{desc.ts}}</pre>
<pre class="language-typescript"><code class="language-typescript" ng-non-bindable [innerHTML]="desc.ts"></code></pre>
</div>
</tab>
<tab heading="Backend Demo">
<div class="card card-block p-3 border-top-0">
<pre class="prettyprint linenums lang-js">{{desc.js}}</pre>
</div>
</tab>
</tabset>
</div>
</div>
</div>
</tab>
</tabset>
</div>
<div class="container">
<div class="row mt-3">
<h2>API</h2>
<div class="card card-block p-3" [innerHTML]="doc"></div>
</div>
</div>
</section>

View File

@@ -0,0 +1,32 @@
import { Component } from '@angular/core';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const doc = require('html-loader!markdown-loader!../../doc.md');
const tabDesc: Array<any> = [
{
heading: 'Simple',
// eslint-disable-next-line @typescript-eslint/no-var-requires
ts: require('!!raw-loader!./file-upload/simple-demo.ts').default,
// eslint-disable-next-line @typescript-eslint/no-var-requires
html: require('!!raw-loader!./file-upload/simple-demo.html').default,
// eslint-disable-next-line @typescript-eslint/no-var-requires
js: require('!!raw-loader!./file-upload/file-catcher.js').default
}
];
@Component({
selector: 'file-upload-section',
templateUrl: './file-upload-section.html'
})
export class FileUploadSectionComponent {
name = 'File Upload';
currentHeading = 'Simple';
doc = doc;
tabs: any = tabDesc;
select(e: any): void {
if (e.heading) {
this.currentHeading = e.heading;
}
}
}

View File

@@ -2,13 +2,12 @@
.my-drop-zone { border: dotted 3px lightgray; }
.nv-file-over { border: dotted 3px red; } /* Default class applied to drop zones on over */
.another-file-over-class { border: dotted 3px green; }
html, body { height: 100%; }
</style>
<div class="container">
<div class="container p-0">
<div class="navbar navbar-default">
<div class="navbar">
<div class="navbar-header">
<a class="navbar-brand" href>Angular2 File Upload</a>
</div>
@@ -24,7 +23,7 @@
[ngClass]="{'nv-file-over': hasBaseDropZoneOver}"
(fileOver)="fileOverBase($event)"
[uploader]="uploader"
class="well my-drop-zone">
class="well my-drop-zone p-2 mb-3">
Base drop zone
</div>
@@ -32,21 +31,21 @@
[ngClass]="{'another-file-over-class': hasAnotherDropZoneOver}"
(fileOver)="fileOverAnother($event)"
[uploader]="uploader"
class="well my-drop-zone">
class="well my-drop-zone p-2 mb-3">
Another drop zone
</div>
Multiple
<input type="file" ng2FileSelect [uploader]="uploader" multiple /><br/>
Multiple<br>
<input class="mt-2 mb-3" type="file" ng2FileSelect [uploader]="uploader" multiple /><br/>
Single
<input type="file" ng2FileSelect [uploader]="uploader" />
Single<br>
<input class="mt-2 mb-3" type="file" ng2FileSelect [uploader]="uploader" />
</div>
<div class="col-md-9" style="margin-bottom: 40px">
<h3>Upload queue</h3>
<p>Queue length: {{ uploader?.queue?.length }}</p>
<p>Queue length: {{ uploader?.queue?.length ? uploader?.queue?.length : 'Empty'}}</p>
<table class="table">
<thead>
@@ -58,21 +57,21 @@
<th>Actions</th>
</tr>
</thead>
<tbody>
<tbody *ngIf="uploader?.queue?.length">
<tr *ngFor="let item of uploader.queue">
<td><strong>{{ item?.file?.name }}</strong></td>
<td *ngIf="uploader.isHTML5" nowrap>{{ item?.file?.size/1024/1024 | number:'.2' }} MB</td>
<td *ngIf="uploader.isHTML5">
<td data-label="Name"><strong>{{ item?.file?.name }}</strong></td>
<td data-label="Size" *ngIf="uploader.options.isHTML5" nowrap>{{ item?.file?.size/1024/1024 | number:'.2' }} MB</td>
<td data-label="Progress" *ngIf="uploader.options.isHTML5">
<div class="progress" style="margin-bottom: 0;">
<div class="progress-bar" role="progressbar" [ngStyle]="{ 'width': item.progress + '%' }"></div>
</div>
</td>
<td class="text-center">
<td data-label="Status" class="text-center">
<span *ngIf="item.isSuccess"><i class="glyphicon glyphicon-ok"></i></span>
<span *ngIf="item.isCancel"><i class="glyphicon glyphicon-ban-circle"></i></span>
<span *ngIf="item.isError"><i class="glyphicon glyphicon-remove"></i></span>
</td>
<td nowrap>
<td data-label="Actions" nowrap>
<button type="button" class="btn btn-success btn-xs"
(click)="item.upload()" [disabled]="item.isReady || item.isUploading || item.isSuccess">
<span class="glyphicon glyphicon-upload"></span> Upload
@@ -93,15 +92,15 @@
<div>
<div>
Queue progress:
<div class="progress" style="">
<div class="progress mt-2 mb-3" style="">
<div class="progress-bar" role="progressbar" [ngStyle]="{ 'width': uploader.progress + '%' }"></div>
</div>
</div>
<button type="button" class="btn btn-success btn-s"
<button type="button" class="btn btn-success btn-s me-2"
(click)="uploader.uploadAll()" [disabled]="!uploader.getNotUploadedItems().length">
<span class="glyphicon glyphicon-upload"></span> Upload all
</button>
<button type="button" class="btn btn-warning btn-s"
<button type="button" class="btn btn-warning btn-s me-2"
(click)="uploader.cancelAll()" [disabled]="!uploader.isUploading">
<span class="glyphicon glyphicon-ban-circle"></span> Cancel all
</button>
@@ -115,4 +114,16 @@
</div>
<br><br>
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-header">Response</div>
<div class="card-body">
{{ response }}
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,50 @@
import { Component } from '@angular/core';
import { FileUploader } from 'ng2-file-upload';
// const URL = '/api/';
const URL = 'https://evening-anchorage-3159.herokuapp.com/api/';
@Component({
selector: 'simple-demo',
templateUrl: './simple-demo.html'
})
export class SimpleDemoComponent {
uploader: FileUploader;
hasBaseDropZoneOver: boolean;
hasAnotherDropZoneOver: boolean;
response: string;
constructor() {
this.uploader = new FileUploader({
url: URL,
disableMultipart: true, // 'DisableMultipart' must be 'true' for formatDataFunction to be called.
formatDataFunctionIsAsync: true,
formatDataFunction: async item => {
return new Promise((resolve, reject) => {
resolve({
name: item._file.name,
length: item._file.size,
contentType: item._file.type,
date: new Date()
});
});
}
});
this.hasBaseDropZoneOver = false;
this.hasAnotherDropZoneOver = false;
this.response = '';
this.uploader.response.subscribe(res => this.response = res );
}
fileOverBase(e: any): void {
this.hasBaseDropZoneOver = e;
}
fileOverAnother(e: any): void {
this.hasAnotherDropZoneOver = e;
}
}

View File

@@ -14,6 +14,15 @@
font-size: 36px;
}
td {
vertical-align: middle;
}
a, .nav-tabs a.nav-link {
text-decoration: none;
color: #6f5499;
}
.btn-group-lg > .btn, .btn-lg {
font-size: 18px;
}
@@ -22,10 +31,6 @@ section {
padding-top: 30px;
}
.bd-pageheader {
margin-top: 51px;
}
.page-header {
padding-bottom: 9px;
margin: 40px 0 20px;
@@ -37,43 +42,24 @@ section {
}
.navbar {
padding: 0;
padding: 15px;
background-color: #f8f8f8;
border-color: #e7e7e7;
}
.navbar-nav .nav-item {
margin-left: 0 !important;
}
.nav > li > a {
position: relative;
display: block;
padding: 10px 15px;
}
.nav .navbar-brand {
float: left;
height: 50px;
padding: 15px 15px;
font-size: 18px;
line-height: 20px;
margin-right: 0 !important;
}
.navbar-brand {
color: #777;
float: left;
height: 50px;
padding: 15px 15px;
font-size: 18px;
line-height: 20px;
}
.navbar-toggler {
margin-top: 8px;
margin-right: 15px;
}
.navbar-default .navbar-nav > li > a:focus, .navbar-default .navbar-nav > li > a:hover {
.navbar-nav > li > a:focus, .navbar-default .navbar-nav > li > a:hover {
color: #333;
background-color: transparent;
}
@@ -113,14 +99,8 @@ section {
margin: 10px 0;
}
.scrollable-menu .nav-link {
color: #337ab7;
font-size: 14px;
}
.scrollable-menu .nav-link:hover {
color: #23527c;
background-color: #eee;
.card {
border-radius: 0 0 .25rem .25rem;
}
@media (min-width: 992px) {
@@ -167,6 +147,53 @@ section {
padding: 0;
margin: 0;
}
table {
border: 0;
}
table caption {
font-size: 1.3em;
}
table thead {
border: none;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
table tr {
border-bottom: 3px solid #ddd;
display: block;
margin-bottom: .625em;
}
table td {
border-bottom: 1px solid #ddd;
font-size: .8em;
display: flex;
justify-content: space-between;
align-items: center;
}
table td::before {
content: attr(data-label);
font-weight: bold;
text-transform: uppercase;
}
table td:last-child {
border-bottom: 0;
}
td[data-label="Progress"] .progress {
width: 75%;
}
}
@media (max-width: 400px) {
@@ -185,13 +212,6 @@ section {
* iOS Resolution Quick Reference: http://www.iosres.com/
*/
.scrollable-menu {
height: 90vh !important;
width: 100vw;
overflow-x: hidden;
padding: 0 0 20px;
}
/**
* iPad with portrait orientation.
*/
@@ -220,29 +240,22 @@ section {
}
}
.navbar-default .navbar-toggle .icon-bar {
background-color: #888;
}
.navbar-toggle:focus {
outline: 0
}
.navbar-toggle .icon-bar {
display: block;
width: 22px;
height: 2px;
border-radius: 1px
}
.navbar-toggle .icon-bar + .icon-bar {
margin-top: 4px
}
pre {
white-space: pre-wrap; /* CSS 3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
line-height: 1.42857143;
color: #333;
background-color: #f5f5f5;
border: 1px solid #ccc;
border-radius: 4px;
padding: 15px;
}
pre.prettyprint {
margin-bottom: 0;
}

View File

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 106 KiB

View File

@@ -25,6 +25,13 @@ import { FileSelectDirective, FileDropDirective, FileUploader } from 'ng2-file-u
1. `url` - URL of File Uploader's route
2. `authToken` - auth token that will be applied as 'Authorization' header during file send.
3. `disableMultipart` - If 'true', disable using a multipart form for file upload and instead stream the file. Some APIs (e.g. Amazon S3) may expect the file to be streamed rather than sent via a form. Defaults to false.
4. `itemAlias` - item alias (form name redefenition)
5. `formatDataFunction` - Function to modify the request body. 'DisableMultipart' must be 'true' for this function to be called.
6. `formatDataFunctionIsAsync` - Informs if the function sent in 'formatDataFunction' is asynchronous. Defaults to false.
7. `parametersBeforeFiles` - States if additional parameters should be appended before or after the file. Defaults to false.
### Events
- `onFileSelected` - fires when files are selected and added to the uploader queue
## FileDrop API
@@ -37,3 +44,4 @@ import { FileSelectDirective, FileDropDirective, FileUploader } from 'ng2-file-u
- `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)
- `onFileDrop` - it fires after a file has been dropped on a Drop Area; you can pass in `$event` to get the list of files that were dropped. i.e. `(onFileDrop)="dropped($event)"`

View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -12,7 +12,7 @@
<link rel="author" href="https://github.com/valor-software/ng2-file-upload/graphs/contributors">
<!--link to bootstrap.css-->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
<link rel="stylesheet" href="assets/css/style.css">
<link rel="stylesheet" href="assets/css/prettify-angulario.css">
<style media="screen">

View File

@@ -1,4 +1,4 @@
import './polyfills.ts';
import '../../../scripts/polyfills.ts';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';

View File

@@ -8,10 +8,10 @@ declare const ENV:string;
// google code-prettify
declare const PR:any;
declare const global:any;
// declare const global:any;
// eslint-disable-next-line @typescript-eslint/prefer-namespace-keyword
declare module jasmine {
interface Matchers {
interface Matchers<T> {
toHaveCssClass(expected: any): boolean;
}
}

View File

@@ -0,0 +1,17 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": ["node"],
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
"angularCompilerOptions": {
"strictInjectionParameters": true,
"strictTemplates": true,
"fullTemplateTypeCheck": true
},
"files": ["src/main.ts"]
}

View File

@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"include": ["**/*.ts"],
"compilerOptions": {
"types": ["jest", "node"]
}
}

27
apps/demo/tsconfig.json Normal file
View File

@@ -0,0 +1,27 @@
{
"extends": "../../tsconfig.base.json",
"files": [
"./src/main.ts",
"../../scripts/polyfills.ts"
],
"include": [
"./src/typings.d.ts"
],
"references": [
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.spec.json"
},
{
"path": "./tsconfig.editor.json"
}
],
"exclude": [
"**/*.spec.ts"
],
"compilerOptions": {
"target": "es2020"
}
}

69
decorate-angular-cli.js Normal file
View File

@@ -0,0 +1,69 @@
/**
* This file decorates the Angular CLI with the Nx CLI to enable features such as computation caching
* and faster execution of tasks.
*
* It does this by:
*
* - Patching the Angular CLI to warn you in case you accidentally use the undecorated ng command.
* - Symlinking the ng to nx command, so all commands run through the Nx CLI
* - Updating the package.json postinstall script to give you control over this script
*
* The Nx CLI decorates the Angular CLI, so the Nx CLI is fully compatible with it.
* Every command you run should work the same when using the Nx CLI, except faster.
*
* Because of symlinking you can still type `ng build/test/lint` in the terminal. The ng command, in this case,
* will point to nx, which will perform optimizations before invoking ng. So the Angular CLI is always invoked.
* The Nx CLI simply does some optimizations before invoking the Angular CLI.
*
* To opt out of this patch:
* - Replace occurrences of nx with ng in your package.json
* - Remove the script from your postinstall script in your package.json
* - Delete and reinstall your node_modules
*/
const fs = require('fs');
const os = require('os');
const cp = require('child_process');
const isWindows = os.platform() === 'win32';
let output;
try {
output = require('@nrwl/workspace').output;
} catch (e) {
console.warn('Angular CLI could not be decorated to enable computation caching. Please ensure @nrwl/workspace is installed.');
process.exit(0);
}
/**
* Symlink of ng to nx, so you can keep using `ng build/test/lint` and still
* invoke the Nx CLI and get the benefits of computation caching.
*/
function symlinkNgCLItoNxCLI() {
try {
const ngPath = './node_modules/.bin/ng';
const nxPath = './node_modules/.bin/nx';
if (isWindows) {
/**
* This is the most reliable way to create symlink-like behavior on Windows.
* Such that it works in all shells and works with npx.
*/
['', '.cmd', '.ps1'].forEach(ext => {
if (fs.existsSync(nxPath + ext)) fs.writeFileSync(ngPath + ext, fs.readFileSync(nxPath + ext));
});
} else {
// If unix-based, symlink
cp.execSync(`ln -sf ./nx ${ngPath}`);
}
}
catch(e) {
output.error({ title: 'Unable to create a symlink from the Angular CLI to the Nx CLI:' + e.message });
throw e;
}
}
try {
symlinkNgCLItoNxCLI();
require('@nrwl/cli/lib/decorate-cli').decorateCli();
output.log({ title: 'Angular CLI has been decorated to enable computation caching.' });
} catch(e) {
output.error({ title: 'Decoration of the Angular CLI did not complete successfully' });
}

View File

@@ -1,4 +0,0 @@
{
"port": 4200,
"server": { "baseDir": "./demo/dist" }
}

View File

@@ -1,43 +0,0 @@
import { Component } from '@angular/core';
let gettingStarted = require('html-loader!markdown-loader!../getting-started.md');
@Component({
selector: 'app',
template: `
<main class="bd-pageheader">
<div class="container">
<h1>ng2-file-upload</h1>
<p>The Angular2 File Upload directives</p>
<a class="btn btn-primary" href="https://github.com/valor-software/ng2-file-upload">View on GitHub</a>
<div class="row">
<div class="col-lg-1"><iframe src="https://ghbtns.com/github-btn.html?user=valor-software&repo=ng2-file-upload&type=star&count=true" frameborder="0" scrolling="0" width="170px" height="20px"></iframe></div>
<div class="col-lg-1"><iframe src="https://ghbtns.com/github-btn.html?user=valor-software&repo=ng2-file-upload&type=fork&count=true" frameborder="0" scrolling="0" width="170px" height="20px"></iframe></div>
</div>
</div>
</main>
<div class="container">
<section id="getting-started" [innerHtml]="gettingStarted"></section>
<file-upload-section class="col-md-12"></file-upload-section>
</div>
<footer class="footer">
<div class="container">
<p class="text-muted text-center"><a href="https://github.com/valor-software/ng2-file-upload">ng2-file-upload</a> is maintained by <a href="https://github.com/valor-software">valor-software</a>.</p>
</div>
</footer>
`
})
export class AppComponent {
public gettingStarted:string = gettingStarted;
public ngAfterContentInit(): any {
setTimeout(()=>{
if (typeof PR !== 'undefined') {
// google code-prettify
PR.prettyPrint();
}
}, 150);
}
}

View File

@@ -1,40 +0,0 @@
<section [attr.id]="name.toLowerCase()">
<div class="row">
<tabset>
<tab *ngFor="let desc of tabs" heading="{{desc.heading}}" (select)="select($event)">
<div class="card card-block panel panel-default panel-body">
<simple-demo></simple-demo>
<br>
<div class="row" style="margin: 0px;">
<tabset>
<tab heading="Markup">
<div class="card card-block panel panel-default panel-body">
<pre class="prettyprint linenums lang-html">{{desc.html}}</pre>
</div>
</tab>
<tab heading="TypeScript">
<div class="card card-block panel panel-default panel-body">
<pre class="prettyprint linenums lang-js">{{desc.ts}}</pre>
<pre class="language-typescript"><code class="language-typescript" ng-non-bindable [innerHTML]="desc.ts"></code></pre>
</div>
</tab>
<tab heading="Backend Demo">
<div class="card card-block panel panel-default panel-body">
<pre class="prettyprint linenums lang-js">{{desc.js}}</pre>
</div>
</tab>
</tabset>
</div>
</div>
</tab>
</tabset>
</div>
<div class="row">
<h2>API</h2>
<div class="card card-block panel panel-default panel-body" [innerHTML]="doc"></div>
</div>
</section>

View File

@@ -1,29 +0,0 @@
import { Component } from '@angular/core';
let doc = require('html-loader!markdown-loader!../../doc.md');
let tabDesc:Array<any> = [
{
heading: 'Simple',
ts: require('!!raw-loader?lang=typescript!./file-upload/simple-demo.ts'),
html: require('!!raw-loader?lang=markup!./file-upload/simple-demo.html'),
js: require('!!raw-loader?lang=javascript!./file-upload/file-catcher.js')
}
];
@Component({
selector: 'file-upload-section',
templateUrl: './file-upload-section.html'
})
export class FileUploadSectionComponent {
public name:string = 'File Upload';
public currentHeading:string = 'Simple';
public doc:string = doc;
public tabs:any = tabDesc;
public select(e:any):void {
if (e.heading) {
this.currentHeading = e.heading;
}
}
}

View File

@@ -1,23 +0,0 @@
import { Component } from '@angular/core';
import { FileUploader } from 'ng2-file-upload';
// const URL = '/api/';
const URL = 'https://evening-anchorage-3159.herokuapp.com/api/';
@Component({
selector: 'simple-demo',
templateUrl: './simple-demo.html'
})
export class SimpleDemoComponent {
public uploader:FileUploader = new FileUploader({url: URL});
public hasBaseDropZoneOver:boolean = false;
public hasAnotherDropZoneOver:boolean = false;
public fileOverBase(e:any):void {
this.hasBaseDropZoneOver = e;
}
public fileOverAnother(e:any):void {
this.hasAnotherDropZoneOver = e;
}
}

View File

@@ -1,21 +0,0 @@
// This file includes polyfills needed by Angular 2 and is loaded before
// the app. You can add your own extra polyfills to this file.
import 'ts-helpers';
import 'core-js/es6/symbol';
import 'core-js/es6/object';
import 'core-js/es6/function';
import 'core-js/es6/parse-int';
import 'core-js/es6/parse-float';
import 'core-js/es6/number';
import 'core-js/es6/math';
import 'core-js/es6/string';
import 'core-js/es6/date';
import 'core-js/es6/array';
import 'core-js/es6/regexp';
import 'core-js/es6/map';
import 'core-js/es6/set';
import 'core-js/es6/reflect';
import 'core-js/es7/reflect';
import 'zone.js/dist/zone';
import 'classlist-polyfill';

View File

@@ -1,22 +0,0 @@
{
"compilerOptions": {
"declaration": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"noEmitHelpers" :true,
"lib": ["es6", "dom"],
"types": [
"jasmine",
"webpack"
],
"mapRoot": "./",
"module": "es6",
"moduleResolution": "node",
"outDir": "../temp/out-tsc",
"sourceMap": true,
"target": "es5",
"typeRoots": [
"../node_modules/@types"
]
}
}

16
demo/src/typings.d.ts vendored
View File

@@ -1,16 +0,0 @@
// Typings reference file, you can add your own global typings here
// https://www.typescriptlang.org/docs/handbook/writing-declaration-files.html
// tslint:disable
declare const System: any;
declare const ENV:string;
// google code-prettify
declare const PR:any;
declare const require:any;
declare const global:any;
declare module jasmine {
interface Matchers {
toHaveCssClass(expected: any): boolean;
}
}

16
firebase.json Normal file
View File

@@ -0,0 +1,16 @@
{
"hosting": {
"public": "./dist/apps/demo/",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"destination": "index.html"
}
]
}
}

3
jest.config.ts Normal file
View File

@@ -0,0 +1,3 @@
const { getJestProjects } = require('@nrwl/jest');
export default {"projects": getJestProjects()};

7
jest.preset.js Normal file
View File

@@ -0,0 +1,7 @@
const nxPreset = require('@nrwl/jest/preset').default;
module.exports = {
...nxPreset,
...{
coverageReporters: ['text-summary', 'json', 'lcov']
}
}

View File

@@ -1,85 +0,0 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/0.13/config/configuration-file.html
const customLaunchers = require('./scripts/sauce-browsers').customLaunchers;
module.exports = function (config) {
const configuration = {
basePath: '',
frameworks: ['jasmine', 'angular-cli'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-remap-istanbul'),
require('angular-cli/plugins/karma')
],
files: [
{pattern: './scripts/test.ts', watched: false}
],
preprocessors: {
'./scripts/test.ts': ['angular-cli']
},
remapIstanbulReporter: {
reports: {
html: 'coverage',
lcovonly: './coverage/coverage.lcov'
}
},
angularCli: {
config: './angular-cli.json',
environment: 'dev'
},
reporters: config.angularCli && config.angularCli.codeCoverage
? ['dots', 'karma-remap-istanbul']
: ['dots'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
customLaunchers: {
Chrome_travis_ci: {
base: 'Chrome',
flags: ['--no-sandbox']
}
},
mime: { 'text/x-typescript': ['ts','tsx'] },
client: { captureConsole: true }
};
if (process.env.TRAVIS) {
configuration.browsers = ['Chrome_travis_ci'];
}
if (process.env.SAUCE) {
if (!process.env.SAUCE_USERNAME || !process.env.SAUCE_ACCESS_KEY) {
console.log('Make sure the SAUCE_USERNAME and SAUCE_ACCESS_KEY environment variables are set.');
process.exit(1);
}
configuration.plugins.push(require('karma-sauce-launcher'));
configuration.reporters.push('saucelabs');
configuration.sauceLabs = {
verbose: true,
testName: 'ng2-bootstrap unit tests',
recordScreenshots: false,
username: process.env.SAUCE_USERNAME,
accessKey: process.env.SAUCE_ACCESS_KEY,
connectOptions: {
port: 5757,
logfile: 'sauce_connect.log'
},
public: 'public'
};
configuration.captureTimeout = 0;
configuration.customLaunchers = customLaunchers();
configuration.browsers = Object.keys(configuration.customLaunchers);
configuration.concurrency = 3;
configuration.browserDisconnectTolerance = 2;
configuration.browserNoActivityTimeout = 20000;
configuration.browserDisconnectTimeout = 5000;
}
config.set(configuration);
};

View File

@@ -0,0 +1,29 @@
{
"extends": [
"../../.eslintrc.json"
],
"ignorePatterns": [
"!**/*"
],
"overrides": [
{
"files": [
"*.ts"
],
"extends": [
"plugin:@nrwl/nx/angular",
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {}
},
{
"files": [
"*.html"
],
"extends": [
"plugin:@nrwl/nx/angular-template"
],
"rules": {}
}
]
}

View File

@@ -0,0 +1,89 @@
# ng2-file-upload [![npm version](https://badge.fury.io/js/ng2-file-upload.svg)](http://badge.fury.io/js/ng2-file-upload) [![npm downloads](https://img.shields.io/npm/dm/ng2-file-upload.svg)](https://npmjs.org/ng2-file-upload)
Easy to use Angular2 directives for files upload ([demo](http://valor-software.github.io/ng2-file-upload/))
[![Angular 2 Style Guide](https://mgechev.github.io/angular2-style-guide/images/badge.svg)](https://github.com/mgechev/angular2-style-guide)
[![Build Status](https://travis-ci.org/valor-software/ng2-file-upload.svg?branch=development)](https://travis-ci.org/valor-software/ng2-file-upload)
[![Dependency Status](https://david-dm.org/valor-software/ng2-file-upload.svg)](https://david-dm.org/valor-software/ng2-file-upload)
## Quick start
1. A recommended way to install ***ng2-file-upload*** is through [npm](https://www.npmjs.com/search?q=ng2-file-upload) package manager using the following command:
`npm i ng2-file-upload --save`
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
`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).
## Using ***ng2-file-upload*** in a project
1. Install as shown in the above section.
2. Import `FileUploadModule` into the module that declares the component using ***ng2-file-upload***:
```import { FileUploadModule } from 'ng2-file-upload';```
3. Add it to `[imports]` under `@NgModule`:
```imports: [ ... FileUploadModule, ... ]```
4. Import `FileUploader` into the component:
```import { FileUploader } from 'ng2-file-upload';```
5. Create a variable for the API url:
```const URL = 'path_to_api';```
6. Initialize it:
```public uploader:FileUploader = new FileUploader({url: URL}); ```
## API for `ng2FileSelect`
### 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)
### Events
- `onFileSelected` - fires when files are selected and added to the uploader queue
## API for `ng2FileDrop`
### 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)
Parameters supported by this object:
1. `url` - URL of File Uploader's route
2. `authToken` - Auth token that will be applied as 'Authorization' header during file send.
3. `disableMultipart` - If 'true', disable using a multipart form for file upload and instead stream the file. Some APIs (e.g. Amazon S3) may expect the file to be streamed rather than sent via a form. Defaults to false.
4. `itemAlias` - item alias (form name redefinition)
5. `formatDataFunction` - Function to modify the request body. 'DisableMultipart' must be 'true' for this function to be called.
6. `formatDataFunctionIsAsync` - Informs if the function sent in 'formatDataFunction' is asynchronous. Defaults to false.
7. `parametersBeforeFiles` - States if additional parameters should be appended before or after the file. Defaults to false.
### Events
- `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)
- `onFileDrop` - it fires after a file has been dropped on a Drop Area; you can pass in `$event` to get the list of files that were dropped. i.e. `(onFileDrop)="dropped($event)"`
# Troubleshooting
Please follow these guidelines when reporting bugs and feature requests:
1. Use [GitHub Issues](https://github.com/valor-software/ng2-file-upload/issues) board to report bugs and feature requests (not our email address)
2. Please **always** write steps to reproduce the error. That way we can focus on fixing the bug, not scratching our heads trying to reproduce it.
Thanks for understanding!
### License
The MIT License (see the [LICENSE](https://github.com/valor-software/ng2-file-upload/blob/master/LICENSE) file for the full text)

View File

@@ -0,0 +1,89 @@
import { Directive, EventEmitter, ElementRef, HostListener, Input, Output } from '@angular/core';
import { FileUploader, FileUploaderOptions } from './file-uploader.class';
@Directive({ selector: '[ng2FileDrop]' })
export class FileDropDirective {
@Input() uploader?: FileUploader;
@Output() fileOver: EventEmitter<any> = new EventEmitter();
// eslint-disable-next-line @angular-eslint/no-output-on-prefix
@Output() onFileDrop: EventEmitter<File[]> = new EventEmitter<File[]>();
protected element: ElementRef;
constructor(element: ElementRef) {
this.element = element;
}
getOptions(): FileUploaderOptions | void {
return this.uploader?.options;
}
getFilters(): string {
return '';
}
@HostListener('drop', [ '$event' ])
onDrop(event: MouseEvent): void {
const transfer = this._getTransfer(event);
if (!transfer) {
return;
}
const options = this.getOptions();
const filters = this.getFilters();
this._preventAndStop(event);
if (options) {
this.uploader?.addToQueue(transfer.files, options, filters);
}
this.fileOver.emit(false);
this.onFileDrop.emit(transfer.files);
}
@HostListener('dragover', [ '$event' ])
onDragOver(event: MouseEvent): void {
const transfer = this._getTransfer(event);
if (!this._haveFiles(transfer.types)) {
return;
}
transfer.dropEffect = 'copy';
this._preventAndStop(event);
this.fileOver.emit(true);
}
@HostListener('dragleave', [ '$event' ])
onDragLeave(event: MouseEvent): void {
if ((this as any).element) {
if (event.currentTarget === (this as any).element[ 0 ]) {
return;
}
}
this._preventAndStop(event);
this.fileOver.emit(false);
}
protected _getTransfer(event: any): any {
return event.dataTransfer ? event.dataTransfer : event.originalEvent.dataTransfer; // jQuery fix;
}
protected _preventAndStop(event: MouseEvent): void {
event.preventDefault();
event.stopPropagation();
}
protected _haveFiles(types: any): boolean {
if (!types) {
return false;
}
if (types.indexOf) {
return types.indexOf('Files') !== -1;
} else if (types.contains) {
return types.contains('Files');
} else {
return false;
}
}
}

View File

@@ -2,30 +2,30 @@ import { FileLikeObject } from './file-like-object.class';
import { FileUploader, ParsedResponseHeaders, FileUploaderOptions } from './file-uploader.class';
export class FileItem {
public file:FileLikeObject;
public _file:File;
public alias:string;
public url:string = '/';
public method:string;
public headers:any = [];
public withCredentials:boolean = true;
public formData:any = [];
public isReady:boolean = false;
public isUploading:boolean = false;
public isUploaded:boolean = false;
public isSuccess:boolean = false;
public isCancel:boolean = false;
public isError:boolean = false;
public progress:number = 0;
public index:number = void 0;
public _xhr:XMLHttpRequest;
public _form:any;
file: FileLikeObject;
_file: File;
alias?: string;
url = '/';
method?: string;
headers: any = [];
withCredentials = true;
formData: any = [];
isReady = false;
isUploading = false;
isUploaded = false;
isSuccess = false;
isCancel = false;
isError = false;
progress = 0;
index?: number;
_xhr?: XMLHttpRequest;
_form: any;
protected uploader:FileUploader;
protected some:File;
protected options:FileUploaderOptions;
protected uploader: FileUploader;
protected some: File;
protected options: FileUploaderOptions;
public constructor(uploader:FileUploader, some:File, options:FileUploaderOptions) {
constructor(uploader: FileUploader, some: File, options: FileUploaderOptions) {
this.uploader = uploader;
this.some = some;
this.options = options;
@@ -38,7 +38,7 @@ export class FileItem {
this.url = uploader.options.url;
}
public upload():void {
upload(): void {
try {
this.uploader.uploadItem(this);
} catch (e) {
@@ -47,43 +47,43 @@ export class FileItem {
}
}
public cancel():void {
cancel(): void {
this.uploader.cancelItem(this);
}
public remove():void {
remove(): void {
this.uploader.removeFromQueue(this);
}
public onBeforeUpload():void {
onBeforeUpload(): void {
return void 0;
}
public onBuildForm(form:any):any {
return {form};
onBuildForm(form: any): any {
return { form };
}
public onProgress(progress:number):any {
return {progress};
onProgress(progress: number): any {
return { progress };
}
public onSuccess(response:string, status:number, headers:ParsedResponseHeaders):any {
return {response, status, headers};
onSuccess(response: string, status: number, headers: ParsedResponseHeaders): any {
return { response, status, headers };
}
public onError(response:string, status:number, headers:ParsedResponseHeaders):any {
return {response, status, headers};
onError(response: string, status: number, headers: ParsedResponseHeaders): any {
return { response, status, headers };
}
public onCancel(response:string, status:number, headers:ParsedResponseHeaders):any {
return {response, status, headers};
onCancel(response: string, status: number, headers: ParsedResponseHeaders): any {
return { response, status, headers };
}
public onComplete(response:string, status:number, headers:ParsedResponseHeaders):any {
return {response, status, headers};
onComplete(response: string, status: number, headers: ParsedResponseHeaders): any {
return { response, status, headers };
}
public _onBeforeUpload():void {
_onBeforeUpload(): void {
this.isReady = true;
this.isUploading = true;
this.isUploaded = false;
@@ -94,16 +94,16 @@ export class FileItem {
this.onBeforeUpload();
}
public _onBuildForm(form:any):void {
_onBuildForm(form: any): void {
this.onBuildForm(form);
}
public _onProgress(progress:number):void {
_onProgress(progress: number): void {
this.progress = progress;
this.onProgress(progress);
}
public _onSuccess(response:string, status:number, headers:ParsedResponseHeaders):void {
_onSuccess(response: string, status: number, headers: ParsedResponseHeaders): void {
this.isReady = false;
this.isUploading = false;
this.isUploaded = true;
@@ -111,11 +111,11 @@ export class FileItem {
this.isCancel = false;
this.isError = false;
this.progress = 100;
this.index = void 0;
this.index = undefined;
this.onSuccess(response, status, headers);
}
public _onError(response:string, status:number, headers:ParsedResponseHeaders):void {
_onError(response: string, status: number, headers: ParsedResponseHeaders): void {
this.isReady = false;
this.isUploading = false;
this.isUploaded = true;
@@ -123,11 +123,11 @@ export class FileItem {
this.isCancel = false;
this.isError = true;
this.progress = 0;
this.index = void 0;
this.index = undefined;
this.onError(response, status, headers);
}
public _onCancel(response:string, status:number, headers:ParsedResponseHeaders):void {
_onCancel(response: string, status: number, headers: ParsedResponseHeaders): void {
this.isReady = false;
this.isUploading = false;
this.isUploaded = false;
@@ -135,11 +135,11 @@ export class FileItem {
this.isCancel = true;
this.isError = false;
this.progress = 0;
this.index = void 0;
this.index = undefined;
this.onCancel(response, status, headers);
}
public _onComplete(response:string, status:number, headers:ParsedResponseHeaders):void {
_onComplete(response: string, status: number, headers: ParsedResponseHeaders): void {
this.onComplete(response, status, headers);
if (this.uploader.options.removeAfterUpload) {
@@ -147,7 +147,7 @@ export class FileItem {
}
}
public _prepareToUploading():void {
_prepareToUploading(): void {
this.index = this.index || ++this.uploader._nextIndex;
this.isReady = true;
}

View File

@@ -0,0 +1,28 @@
export class FileLikeObject {
lastModifiedDate: any;
size: any;
type?: string;
name?: string;
rawFile: HTMLInputElement | File;
constructor(fileOrInput: HTMLInputElement | File) {
this.rawFile = fileOrInput;
const fakePathOrObject = fileOrInput instanceof HTMLInputElement ? fileOrInput.value : fileOrInput;
const postfix = typeof fakePathOrObject === 'string' ? 'FakePath' : 'Object';
const method = `_createFrom${postfix}`;
(this as any)[ method ](fakePathOrObject);
}
_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);
}
_createFromObject(object: { size: number, type: string, name: string }): void {
this.size = object.size;
this.type = object.type;
this.name = object.name;
}
}

View File

@@ -0,0 +1,41 @@
import { Directive, EventEmitter, ElementRef, Input, HostListener, Output } from '@angular/core';
import { FileUploader, FileUploaderOptions } from './file-uploader.class';
@Directive({ selector: '[ng2FileSelect]' })
export class FileSelectDirective {
@Input() uploader?: FileUploader;
// eslint-disable-next-line @angular-eslint/no-output-on-prefix
@Output() onFileSelected: EventEmitter<File[]> = new EventEmitter<File[]>();
protected element: ElementRef;
constructor(element: ElementRef) {
this.element = element;
}
getOptions(): FileUploaderOptions | undefined {
return this.uploader?.options;
}
getFilters(): string {
return '';
}
isEmptyAfterSelection(): boolean {
return !!this.element.nativeElement.attributes.multiple;
}
@HostListener('change')
onChange(): void {
const files = this.element.nativeElement.files;
const options = this.getOptions();
const filters = this.getFilters();
this.uploader?.addToQueue(files, options, filters);
this.onFileSelected.emit(files);
if (this.isEmptyAfterSelection()) {
this.element.nativeElement.value = '';
}
}
}

View File

@@ -0,0 +1,169 @@
import { FileLikeObject } from '../index';
export class FileType {
/* MS office */
// tslint:disable-next-line:variable-name
static mime_doc: string[] = [
'application/msword',
'application/msword',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
'application/vnd.ms-word.document.macroEnabled.12',
'application/vnd.ms-word.template.macroEnabled.12'
];
// tslint:disable-next-line:variable-name
static mime_xsl: string[] = [
'application/vnd.ms-excel',
'application/vnd.ms-excel',
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
'application/vnd.ms-excel.sheet.macroEnabled.12',
'application/vnd.ms-excel.template.macroEnabled.12',
'application/vnd.ms-excel.addin.macroEnabled.12',
'application/vnd.ms-excel.sheet.binary.macroEnabled.12'
];
// tslint:disable-next-line:variable-name
static mime_ppt: string[] = [
'application/vnd.ms-powerpoint',
'application/vnd.ms-powerpoint',
'application/vnd.ms-powerpoint',
'application/vnd.ms-powerpoint',
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'application/vnd.openxmlformats-officedocument.presentationml.template',
'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
'application/vnd.ms-powerpoint.addin.macroEnabled.12',
'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
'application/vnd.ms-powerpoint.slideshow.macroEnabled.12'
];
/* PSD */
// tslint:disable-next-line:variable-name
static mime_psd: string[] = [
'image/photoshop',
'image/x-photoshop',
'image/psd',
'application/photoshop',
'application/psd',
'zz-application/zz-winassoc-psd'
];
/* Compressed files */
// tslint:disable-next-line:variable-name
static mime_compress: string[] = [
'application/x-gtar',
'application/x-gcompress',
'application/compress',
'application/x-tar',
'application/x-rar-compressed',
'application/octet-stream',
'application/x-zip-compressed',
'application/zip-compressed',
'application/x-7z-compressed',
'application/gzip',
'application/x-bzip2'
];
static getMimeClass(file: FileLikeObject): string {
let mimeClass = 'application';
if (file?.type && this.mime_psd.indexOf(file.type) !== -1) {
mimeClass = 'image';
} else if (file?.type?.match('image.*')) {
mimeClass = 'image';
} else if (file?.type?.match('video.*')) {
mimeClass = 'video';
} else if (file?.type?.match('audio.*')) {
mimeClass = 'audio';
} else if (file?.type === 'application/pdf') {
mimeClass = 'pdf';
} else if (file?.type && this.mime_compress.indexOf(file.type) !== -1) {
mimeClass = 'compress';
} else if (file?.type && this.mime_doc.indexOf(file.type) !== -1) {
mimeClass = 'doc';
} else if (file?.type && this.mime_xsl.indexOf(file.type) !== -1) {
mimeClass = 'xls';
} else if (file?.type && this.mime_ppt.indexOf(file.type) !== -1) {
mimeClass = 'ppt';
}
if (mimeClass === 'application' && file?.name) {
mimeClass = this.fileTypeDetection(file.name);
}
return mimeClass;
}
static fileTypeDetection(inputFilename: string): string {
const types: { [ key: string ]: string } = {
jpg: 'image',
jpeg: 'image',
tif: 'image',
psd: 'image',
bmp: 'image',
png: 'image',
nef: 'image',
tiff: 'image',
cr2: 'image',
dwg: 'image',
cdr: 'image',
ai: 'image',
indd: 'image',
pin: 'image',
cdp: 'image',
skp: 'image',
stp: 'image',
'3dm': 'image',
mp3: 'audio',
wav: 'audio',
wma: 'audio',
mod: 'audio',
m4a: 'audio',
compress: 'compress',
zip: 'compress',
rar: 'compress',
'7z': 'compress',
lz: 'compress',
z01: 'compress',
bz2: 'compress',
gz: 'compress',
pdf: 'pdf',
xls: 'xls',
xlsx: 'xls',
ods: 'xls',
mp4: 'video',
avi: 'video',
wmv: 'video',
mpg: 'video',
mts: 'video',
flv: 'video',
'3gp': 'video',
vob: 'video',
m4v: 'video',
mpeg: 'video',
m2ts: 'video',
mov: 'video',
doc: 'doc',
docx: 'doc',
eps: 'doc',
txt: 'doc',
odt: 'doc',
rtf: 'doc',
ppt: 'ppt',
pptx: 'ppt',
pps: 'ppt',
ppsx: 'ppt',
odp: 'ppt'
};
const chunks = inputFilename.split('.');
if (chunks.length < 2) {
return 'application';
}
const extension = chunks[ chunks.length - 1 ].toLowerCase();
if (types[ extension ] === undefined) {
return 'application';
} else {
return types[ extension ];
}
}
}

View File

@@ -0,0 +1,15 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FileDropDirective } from './file-drop.directive';
import { FileSelectDirective } from './file-select.directive';
export { FileDropDirective } from './file-drop.directive';
export { FileSelectDirective } from './file-select.directive';
@NgModule({
imports: [ CommonModule ],
declarations: [ FileDropDirective, FileSelectDirective ],
exports: [ FileDropDirective, FileSelectDirective ]
})
export class FileUploadModule {
}

View File

@@ -0,0 +1,513 @@
import { EventEmitter } from '@angular/core';
import { FileLikeObject } from './file-like-object.class';
import { FileItem } from './file-item.class';
import { FileType } from './file-type.class';
function isFile(value: any): boolean {
return (File && value instanceof File);
}
export interface Headers {
name: string;
value: string;
}
export interface ParsedResponseHeaders { [ headerFieldName: string ]: string }
export interface FilterFunction {
name: string;
fn(item: FileLikeObject, options?: FileUploaderOptions): boolean;
}
export interface FileUploaderOptions {
allowedMimeType?: string[];
allowedFileType?: string[];
autoUpload?: boolean;
isHTML5?: boolean;
filters?: FilterFunction[];
headers?: Headers[];
method?: string;
authToken?: string;
maxFileSize?: number;
queueLimit?: number;
removeAfterUpload?: boolean;
url: string;
disableMultipart?: boolean;
itemAlias?: string;
authTokenHeader?: string;
additionalParameter?: { [ key: string ]: any };
parametersBeforeFiles?: boolean;
// eslint-disable-next-line @typescript-eslint/ban-types
formatDataFunction?: Function;
formatDataFunctionIsAsync?: boolean;
}
export class FileUploader {
authToken?: string;
isUploading = false;
queue: FileItem[] = [];
progress = 0;
_nextIndex = 0;
autoUpload: any;
authTokenHeader?: string;
response: EventEmitter<any>;
options: FileUploaderOptions = {
autoUpload: false,
isHTML5: true,
filters: [],
removeAfterUpload: false,
disableMultipart: false,
formatDataFunction: (item: FileItem) => item._file,
formatDataFunctionIsAsync: false,
url: ''
};
protected _failFilterIndex?: number;
constructor(options: FileUploaderOptions) {
this.setOptions(options);
this.response = new EventEmitter<string>();
}
setOptions(options: FileUploaderOptions): void {
this.options = Object.assign(this.options, options);
this.authToken = this.options.authToken;
this.authTokenHeader = this.options.authTokenHeader || 'Authorization';
this.autoUpload = this.options.autoUpload;
this.options.filters?.unshift({ name: 'queueLimit', fn: this._queueLimitFilter });
if (this.options.maxFileSize) {
this.options.filters?.unshift({ name: 'fileSize', fn: this._fileSizeFilter });
}
if (this.options.allowedFileType) {
this.options.filters?.unshift({ name: 'fileType', fn: this._fileTypeFilter });
}
if (this.options.allowedMimeType) {
this.options.filters?.unshift({ name: 'mimeType', fn: this._mimeTypeFilter });
}
for (let i = 0; i < this.queue.length; i++) {
this.queue[ i ].url = this.options.url;
}
}
addToQueue(files: File[], _options?: FileUploaderOptions, filters?: [] | string): void {
let options = _options;
const list: File[] = [];
for (const file of files) {
list.push(file);
}
const arrayOfFilters = this._getFilters(filters);
const count = this.queue.length;
const addedFileItems: FileItem[] = [];
list.map((some: File) => {
if (!options) {
options = this.options;
}
const temp = new FileLikeObject(some);
if (this._isValidFile(temp, arrayOfFilters, options)) {
const fileItem = new FileItem(this, some, options);
addedFileItems.push(fileItem);
this.queue.push(fileItem);
this._onAfterAddingFile(fileItem);
} else {
if (this._failFilterIndex) {
const filter = arrayOfFilters[ this._failFilterIndex ];
this._onWhenAddingFileFailed(temp, filter, options);
}
}
});
if (this.queue.length !== count) {
this._onAfterAddingAll(addedFileItems);
this.progress = this._getTotalProgress();
}
this._render();
if (this.options.autoUpload) {
this.uploadAll();
}
}
removeFromQueue(value: FileItem): void {
const index = this.getIndexOfItem(value);
const item = this.queue[ index ];
if (item.isUploading) {
item.cancel();
}
this.queue.splice(index, 1);
this.progress = this._getTotalProgress();
}
clearQueue(): void {
while (this.queue.length) {
this.queue[ 0 ].remove();
}
this.progress = 0;
}
uploadItem(value: FileItem): void {
const index = this.getIndexOfItem(value);
const item = this.queue[ index ];
const transport = this.options.isHTML5 ? '_xhrTransport' : '_iframeTransport';
item._prepareToUploading();
if (this.isUploading) {
return;
}
this.isUploading = true;
(this as any)[ transport ](item);
}
cancelItem(value: FileItem): void {
const index = this.getIndexOfItem(value);
const item = this.queue[ index ];
const prop = this.options.isHTML5 ? item._xhr : item._form;
if (item && item.isUploading) {
prop.abort();
}
}
uploadAll(): void {
const items = this.getNotUploadedItems().filter((item: FileItem) => !item.isUploading);
if (!items.length) {
return;
}
items.map((item: FileItem) => item._prepareToUploading());
items[ 0 ].upload();
}
cancelAll(): void {
const items = this.getNotUploadedItems();
items.map((item: FileItem) => item.cancel());
}
isFile(value: any): boolean {
return isFile(value);
}
isFileLikeObject(value: any): boolean {
return value instanceof FileLikeObject;
}
getIndexOfItem(value: any): number {
return typeof value === 'number' ? value : this.queue.indexOf(value);
}
getNotUploadedItems(): any[] {
return this.queue.filter((item: FileItem) => !item.isUploaded);
}
getReadyItems(): any[] {
return this.queue
.filter((item: FileItem) => (item.isReady && !item.isUploading))
.sort((item1: any, item2: any) => item1.index - item2.index);
}
onAfterAddingAll(fileItems: any): any {
return { fileItems };
}
onBuildItemForm(fileItem: FileItem, form: any): any {
return { fileItem, form };
}
onAfterAddingFile(fileItem: FileItem): any {
return { fileItem };
}
onWhenAddingFileFailed(item: FileLikeObject, filter: any, options: any): any {
return { item, filter, options };
}
onBeforeUploadItem(fileItem: FileItem): any {
return { fileItem };
}
onProgressItem(fileItem: FileItem, progress: any): any {
return { fileItem, progress };
}
onProgressAll(progress: any): any {
return { progress };
}
onSuccessItem(item: FileItem, response: string, status: number, headers: ParsedResponseHeaders): any {
return { item, response, status, headers };
}
onErrorItem(item: FileItem, response: string, status: number, headers: ParsedResponseHeaders): any {
return { item, response, status, headers };
}
onCancelItem(item: FileItem, response: string, status: number, headers: ParsedResponseHeaders): any {
return { item, response, status, headers };
}
onCompleteItem(item: FileItem, response: string, status: number, headers: ParsedResponseHeaders): any {
return { item, response, status, headers };
}
onCompleteAll(): any {
return void 0;
}
_mimeTypeFilter(item: FileLikeObject): boolean {
return !(item?.type && this.options.allowedMimeType && this.options.allowedMimeType?.indexOf(item.type) === -1);
}
_fileSizeFilter(item: FileLikeObject): boolean {
return !(this.options.maxFileSize && item.size > this.options.maxFileSize);
}
_fileTypeFilter(item: FileLikeObject): boolean {
return !(this.options.allowedFileType &&
this.options.allowedFileType.indexOf(FileType.getMimeClass(item)) === -1);
}
_onErrorItem(item: FileItem, response: string, status: number, headers: ParsedResponseHeaders): void {
item._onError(response, status, headers);
this.onErrorItem(item, response, status, headers);
}
_onCompleteItem(item: FileItem, response: string, status: number, headers: ParsedResponseHeaders): void {
item._onComplete(response, status, headers);
this.onCompleteItem(item, response, status, headers);
const nextItem = this.getReadyItems()[ 0 ];
this.isUploading = false;
if (nextItem) {
nextItem.upload();
return;
}
this.onCompleteAll();
this.progress = this._getTotalProgress();
this._render();
}
protected _headersGetter(parsedHeaders: ParsedResponseHeaders): any {
return (name: any): any => {
if (name) {
return parsedHeaders[ name.toLowerCase() ] || undefined;
}
return parsedHeaders;
};
}
protected _xhrTransport(item: FileItem): any {
// tslint:disable-next-line:no-this-assignment
// eslint-disable-next-line @typescript-eslint/no-this-alias
const that = this;
const xhr = item._xhr = new XMLHttpRequest();
let sendable: any;
this._onBeforeUploadItem(item);
if (typeof item._file.size !== 'number') {
throw new TypeError('The file specified is no longer valid');
}
if (!this.options.disableMultipart) {
sendable = new FormData();
this._onBuildItemForm(item, sendable);
const appendFile = () => sendable.append(item.alias, item._file, item.file.name);
if (!this.options.parametersBeforeFiles) {
appendFile();
}
// For AWS, Additional Parameters must come BEFORE Files
if (this.options.additionalParameter !== undefined) {
Object.keys(this.options.additionalParameter).forEach((key: string) => {
let paramVal = this.options.additionalParameter?.[ key ];
// Allow an additional parameter to include the filename
if (typeof paramVal === 'string' && paramVal.indexOf('{{file_name}}') >= 0 && item.file?.name) {
paramVal = paramVal.replace('{{file_name}}', item.file.name);
}
sendable.append(key, paramVal);
});
}
if (appendFile && this.options.parametersBeforeFiles) {
appendFile();
}
} else {
if (this.options.formatDataFunction) {
sendable = this.options.formatDataFunction(item);
}
}
xhr.upload.onprogress = (event: any) => {
const progress = Math.round(event.lengthComputable ? event.loaded * 100 / event.total : 0);
this._onProgressItem(item, progress);
};
xhr.onload = () => {
const headers = this._parseHeaders(xhr.getAllResponseHeaders());
const response = this._transformResponse(xhr.response, headers);
const gist = this._isSuccessCode(xhr.status) ? 'Success' : 'Error';
const method = `_on${gist}Item`;
(this as any)[ method ](item, response, xhr.status, headers);
this._onCompleteItem(item, response, xhr.status, headers);
};
xhr.onerror = () => {
const headers = this._parseHeaders(xhr.getAllResponseHeaders());
const response = this._transformResponse(xhr.response, headers);
this._onErrorItem(item, response, xhr.status, headers);
this._onCompleteItem(item, response, xhr.status, headers);
};
xhr.onabort = () => {
const headers = this._parseHeaders(xhr.getAllResponseHeaders());
const response = this._transformResponse(xhr.response, headers);
this._onCancelItem(item, response, xhr.status, headers);
this._onCompleteItem(item, response, xhr.status, headers);
};
if (item.method && item.url) {
xhr.open(item.method, item.url, true);
}
xhr.withCredentials = item.withCredentials;
if (this.options.headers) {
for (const header of this.options.headers) {
xhr.setRequestHeader(header.name, header.value);
}
}
if (item.headers.length) {
for (const header of item.headers) {
xhr.setRequestHeader(header.name, header.value);
}
}
if (this.authToken && this.authTokenHeader) {
xhr.setRequestHeader(this.authTokenHeader, this.authToken);
}
xhr.onreadystatechange = function () {
if (xhr.readyState == XMLHttpRequest.DONE) {
that.response.emit(xhr.responseText);
}
};
if (this.options.formatDataFunctionIsAsync) {
sendable.then(
(result: any) => xhr.send(JSON.stringify(result))
);
} else {
xhr.send(sendable);
}
this._render();
}
protected _getTotalProgress(value = 0): number {
if (this.options.removeAfterUpload) {
return value;
}
const notUploaded = this.getNotUploadedItems().length;
const uploaded = notUploaded ? this.queue.length - notUploaded : this.queue.length;
const ratio = 100 / this.queue.length;
const current = value * ratio / 100;
return Math.round(uploaded * ratio + current);
}
protected _getFilters(filters?: FilterFunction[] | string): FilterFunction[] | [] {
if (!filters) {
return this.options?.filters || [];
}
if (Array.isArray(filters)) {
return filters;
}
if (typeof filters === 'string') {
const names = filters.match(/[^\s,]+/g);
return this.options?.filters || []
.filter((filter: any) => names?.indexOf(filter.name) !== -1);
}
return this.options?.filters || [];
}
protected _render(): any {
return void 0;
}
protected _queueLimitFilter(): boolean {
return this.options.queueLimit === undefined || this.queue.length < this.options.queueLimit;
}
protected _isValidFile(file: FileLikeObject, filters: FilterFunction[], options: FileUploaderOptions): boolean {
this._failFilterIndex = -1;
return !filters.length ? true : filters.every((filter: FilterFunction) => {
if (this._failFilterIndex) {
this._failFilterIndex++;
}
return filter.fn.call(this, file, options);
});
}
protected _isSuccessCode(status: number): boolean {
return (status >= 200 && status < 300) || status === 304;
}
protected _transformResponse(response: string, headers: ParsedResponseHeaders): string {
return response;
}
protected _parseHeaders(headers: string): ParsedResponseHeaders {
const 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;
}
protected _onWhenAddingFileFailed(item: FileLikeObject, filter: any, options: any): void {
this.onWhenAddingFileFailed(item, filter, options);
}
protected _onAfterAddingFile(item: FileItem): void {
this.onAfterAddingFile(item);
}
protected _onAfterAddingAll(items: any): void {
this.onAfterAddingAll(items);
}
protected _onBeforeUploadItem(item: FileItem): void {
item._onBeforeUpload();
this.onBeforeUploadItem(item);
}
protected _onBuildItemForm(item: FileItem, form: any): void {
item._onBuildForm(form);
this.onBuildItemForm(item, form);
}
protected _onProgressItem(item: FileItem, progress: any): void {
const total = this._getTotalProgress(progress);
this.progress = total;
item._onProgress(progress);
this.onProgressItem(item, progress);
this.onProgressAll(total);
this._render();
}
protected _onSuccessItem(item: FileItem, response: string, status: number, headers: ParsedResponseHeaders): void {
item._onSuccess(response, status, headers);
this.onSuccessItem(item, response, status, headers);
}
protected _onCancelItem(item: FileItem, response: string, status: number, headers: ParsedResponseHeaders): void {
item._onCancel(response, status, headers);
this.onCancelItem(item, response, status, headers);
}
}

View File

@@ -1,6 +1,8 @@
export * from './file-upload/file-select.directive';
export * from './file-upload/file-drop.directive';
export * from './file-upload/file-uploader.class';
export * from './file-upload/file-item.class';
export * from './file-upload/file-like-object.class';
export * from './file-upload/file-like-object.class';
export { FileUploadModule } from './file-upload/file-upload.module';
export { FileSelectDirective } from './file-upload/file-select.directive';

View File

@@ -0,0 +1,21 @@
/* eslint-disable */
export default {
displayName: 'ng2-file-upload',
preset: '../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/testing/test-setup.ts'],
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json',
}
},
coverageDirectory: '../../coverage/libs/ng2-file-upload',
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
'jest-preset-angular/build/serializers/html-comment'
],
transform: { '^.+.(ts|mjs|js|html)$': 'jest-preset-angular' },
transformIgnorePatterns: ['node_modules/(?!.*.mjs$)'],
moduleFileExtensions: ['mjs', 'ts', 'js', 'html']
};

View File

@@ -0,0 +1,7 @@
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/libs/ng2-file-upload/",
"lib": {
"entryFile": "index.ts"
}
}

View File

@@ -0,0 +1,31 @@
{
"name": "ng2-file-upload",
"version": "3.0.0",
"description": "Angular file uploader",
"author": "Dmitriy Shekhovtsov <valorkin@gmail.com>",
"license": "MIT",
"peerDependencies": {
"@angular/core": "^14.0.0",
"@angular/common": "^14.0.0"
},
"sideEffects": false,
"publishConfig": {
"registry": "https://registry.npmjs.org/",
"tag": "next"
},
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/valor-software/ng2-file-upload.git"
},
"bugs": {
"url": "https://github.com/valor-software/ng2-file-upload/issues"
},
"homepage": "https://github.com/valor-software/ng2-file-upload#readme",
"keywords": [
"angular",
"upload file",
"ng",
"ng2",
"angular2"
]
}

View File

@@ -0,0 +1,150 @@
import { Component, DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FileUploader } from '../../file-upload/file-uploader.class';
import { FileUploadModule } from '../../file-upload/file-upload.module';
import { FileDropDirective } from '../../file-upload/file-drop.directive';
@Component({
selector: 'container',
template: `<div type="file"
ng2FileDrop
[uploader]="uploader"
></div>`
})
export class ContainerComponent {
public get url(): string { return 'localhost:3000'; }
public uploader: FileUploader = new FileUploader({ url: this.url });
}
describe('Directive: FileDropDirective', () => {
let fixture: ComponentFixture<ContainerComponent>;
let hostComponent: ContainerComponent;
let directiveElement: DebugElement;
let fileDropDirective: FileDropDirective;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ FileUploadModule ],
declarations: [ ContainerComponent, FileDropDirective ],
providers: [ ContainerComponent ]
});
});
beforeEach(() => {
fixture = TestBed.createComponent(ContainerComponent);
hostComponent = fixture.componentInstance;
fixture.detectChanges();
directiveElement = fixture.debugElement.query(By.directive(FileDropDirective));
fileDropDirective = directiveElement.injector.get(FileDropDirective) as FileDropDirective;
});
it('can be initialized', () => {
expect(fixture).toBeDefined();
expect(hostComponent).toBeDefined();
expect(fileDropDirective).toBeDefined();
});
it('can set file uploader', () => {
expect(fileDropDirective.uploader).toBe(hostComponent.uploader);
});
it('can get uploader options', () => {
const options = fileDropDirective.getOptions();
// Check url set through binding
// Check default options
expect(options).toBeTruthy();
if (options) {
expect(options.url).toBe(hostComponent.url);
expect(options.autoUpload).toBeFalsy();
expect(options.isHTML5).toBeTruthy();
expect(options.removeAfterUpload).toBeFalsy();
expect(options.disableMultipart).toBeFalsy();
}
});
it('can get filters', () => {
const filters = fileDropDirective.getFilters();
// TODO: Update test once implemented
expect(filters).toBeFalsy();
});
it('handles drop event', () => {
const drop = jest.spyOn(fileDropDirective, 'onDrop');
directiveElement.triggerEventHandler('drop', getFakeEventData());
expect(drop).toHaveBeenCalled();
});
it('adds file to upload', () => {
let addToQueue;
if (fileDropDirective.uploader?.addToQueue) {
addToQueue = jest.spyOn(fileDropDirective.uploader, 'addToQueue');
}
let fileOverData;
fileDropDirective.fileOver.subscribe((data: any) => fileOverData = data);
let fileDropData;
fileDropDirective.onFileDrop.subscribe((data: File[]) => fileDropData = data);
fileDropDirective.onDrop(getFakeEventData());
const uploadedFiles = getFakeEventData().dataTransfer.files;
expect(addToQueue).toHaveBeenCalledWith(uploadedFiles, fileDropDirective.getOptions(), fileDropDirective.getFilters());
expect(fileOverData).toBeFalsy();
expect(fileDropData).toEqual(uploadedFiles);
});
it('handles dragover event', () => {
jest.spyOn(fileDropDirective, 'onDragOver');
directiveElement.triggerEventHandler('dragover', getFakeEventData());
expect(fileDropDirective.onDragOver).toHaveBeenCalled();
});
it('handles file over', () => {
let fileOverData;
fileDropDirective.fileOver.subscribe((data: any) => fileOverData = data);
fileDropDirective.onDragOver(getFakeEventData());
expect(fileOverData).toBeTruthy();
});
it('handles dragleave event', () => {
jest.spyOn(fileDropDirective, 'onDragLeave');
directiveElement.triggerEventHandler('dragleave', getFakeEventData());
expect(fileDropDirective.onDragLeave).toHaveBeenCalled();
});
it('handles file over leave', () => {
let fileOverData;
fileDropDirective.fileOver.subscribe((data: any) => fileOverData = data);
fileDropDirective.onDragLeave(getFakeEventData());
expect(fileOverData).toBeFalsy();
});
});
function getFakeEventData(): any {
return {
dataTransfer: {
files: [ 'foo.bar' ],
types: [ 'Files' ]
},
preventDefault: () => undefined,
stopPropagation: () => undefined
};
}

View File

@@ -0,0 +1,101 @@
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { Component, DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';
import { FileUploadModule } from '../../file-upload/file-upload.module';
import { FileSelectDirective } from '../../file-upload/file-select.directive';
import { FileUploader } from '../../file-upload/file-uploader.class';
@Component({
selector: 'container',
template: `<input type="file"
ng2FileSelect
[uploader]="uploader"
/>`
})
export class ContainerComponent {
public get url(): string { return 'localhost:3000'; }
public uploader: FileUploader = new FileUploader({ url: this.url });
}
describe('Directive: FileSelectDirective', () => {
let fixture: ComponentFixture<ContainerComponent>;
let hostComponent: ContainerComponent;
let directiveElement: DebugElement;
let fileSelectDirective: FileSelectDirective;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ FileUploadModule ],
declarations: [ ContainerComponent ],
providers: [ ContainerComponent ]
});
});
beforeEach(() => {
fixture = TestBed.createComponent(ContainerComponent);
hostComponent = fixture.componentInstance;
fixture.detectChanges();
directiveElement = fixture.debugElement.query(By.directive(FileSelectDirective));
fileSelectDirective = directiveElement.injector.get(FileSelectDirective) as FileSelectDirective;
});
it('can be initialized', () => {
expect(fixture).toBeDefined();
expect(hostComponent).toBeDefined();
expect(fileSelectDirective).toBeDefined();
});
it('can set file uploader', () => {
expect(fileSelectDirective.uploader).toBe(hostComponent.uploader);
});
it('can get uploader options', () => {
const options = fileSelectDirective.getOptions();
expect(options).toBeTruthy();
if (options) {
// Check url set through binding
expect(options.url).toBe(hostComponent.url);
// Check default options
expect(options.autoUpload).toBeFalsy();
expect(options.isHTML5).toBeTruthy();
expect(options.removeAfterUpload).toBeFalsy();
expect(options.disableMultipart).toBeFalsy();
}
});
it('can get filters', () => {
const filters = fileSelectDirective.getFilters();
// TODO: Update test once implemented
expect(filters).toEqual('');
});
it('can check if element is empty', () => {
const isElementEmpty = fileSelectDirective.isEmptyAfterSelection();
expect(isElementEmpty).toBeFalsy();
});
it('can listed on change event', () => {
const change = jest.spyOn(fileSelectDirective, 'onChange');
directiveElement.triggerEventHandler('change', {});
expect(change).toHaveBeenCalled();
});
it('handles change event', () => {
let addToQueue;
if (fileSelectDirective.uploader?.addToQueue) {
addToQueue = jest.spyOn(fileSelectDirective.uploader, 'addToQueue');
}
fileSelectDirective.onChange();
expect(addToQueue).toHaveBeenCalledWith(directiveElement.nativeElement.files, fileSelectDirective.getOptions(), fileSelectDirective.getFilters());
});
});

View File

@@ -0,0 +1 @@
import 'jest-preset-angular/setup-jest';

View File

@@ -0,0 +1,28 @@
{
"extends": "../../tsconfig.base.json",
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.lib.prod.json"
},
{
"path": "./tsconfig.spec.json"
}
],
"compilerOptions": {
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"target": "es2020"
},
"angularCompilerOptions": {
"strictInjectionParameters": true,
"strictTemplates": true,
"fullTemplateTypeCheck": true
}
}

View File

@@ -0,0 +1,28 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"target": "es2020",
"declaration": true,
"declarationMap": true,
"inlineSources": true,
"types": [],
"lib": [
"dom",
"es2018"
]
},
"angularCompilerOptions": {
"skipTemplateCodegen": true,
"strictMetadataEmit": true,
"enableResourceInlining": true
},
"include": [
"**/*.ts"
],
"exclude": [
"**/*.spec.ts",
"**/*.test.ts",
"jest.config.ts"
]
}

View File

@@ -0,0 +1,12 @@
{
"extends": "./tsconfig.lib.json",
"compilerOptions": {
"declarationMap": false
},
"angularCompilerOptions": {
"compilationMode": "partial"
},
"exclude": [
"jest.config.ts"
]
}

View File

@@ -0,0 +1,20 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": [
"jest",
"node"
]
},
"files": [
"testing/test-setup.ts"
],
"include": [
"**/*.spec.ts",
"**/*.test.ts",
"**/*.d.ts",
"jest.config.ts"
]
}

39
nx.json Normal file
View File

@@ -0,0 +1,39 @@
{
"npmScope": "ng2-file-upload-demo",
"implicitDependencies": {
"angular.json": "*",
"package.json": "*",
"tsconfig.json": "*",
"tslint.json": "*",
".eslintrc.json": "*",
"nx.json": "*"
},
"tasksRunnerOptions": {
"default": {
"runner": "nx/tasks-runners/default",
"options": {
"cacheableOperations": [
"build",
"lint",
"test",
"e2e"
],
"parallel": 1
}
}
},
"affected": {
"defaultBase": "master"
},
"defaultProject": "ng2-file-upload-demo",
"generators": {
"@schematics/angular:component": {
"prefix": "",
"styleext": "css"
},
"@schematics/angular:directive": {
"prefix": ""
}
},
"$schema": "./node_modules/nx/schemas/nx-schema.json"
}

19273
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,39 +1,33 @@
{
"name": "ng2-file-upload-base",
"version": "1.2.0",
"version": "2.0.0-3",
"private": true,
"description": "Angular file upload directives",
"scripts": {
"lite-server": "lite-server -c demo/bs-config.json",
"demo.serve": "run-s build link demo.build lite-server",
"ng": "nx",
"start": "ng serve",
"demo.build": "ng build",
"demo.build-prod": "ng build",
"build": "nx run ng2-file-upload:build",
"build-prod": "nx run ng2-file-upload:build",
"lint": "nx run-many --target=lint --all",
"test": "nx run-many --target=test --all",
"version": "nx run ng2-file-upload:version",
"pretest": "run-s lint build",
"test-coverage": "nx run-many --all --target=test --codeCoverage",
"demo.gh-pages": "run-s build demo.build demo.deploy",
"demo.build": "ng build -prod --aot",
"demo.deploy": "gh-pages -d demo/dist",
"link": "ngm link -p src --here",
"lint": "exit 0",
"disable-lint": "tslint \"**/*.ts\" -c tslint.json --fix --type-check -t prose -e \"node_modules/**\" -e \"dist/**\" -e \"temp/**\" -e \"scripts/docs/**\"",
"flow.changelog": "conventional-changelog -i CHANGELOG.md -s -p angular",
"flow.github-release": "conventional-github-releaser -p angular",
"build": "ngm build -p src --clean",
"build.watch": "ngm build -p src --watch --skip-bundles",
"start": "ng serve --aot",
"pretest": "run-s lint build link",
"test": "ng test -sr",
"test-coverage": "ng test -sr -cc",
"version": "npm run flow.changelog && git add -A"
"demo.deploy": "gh-pages -d dist/apps/demo",
"postinstall": "node ./decorate-angular-cli.js",
"flow.github-release": "conventional-github-releaser -p angular"
},
"main": "bundles/ng2-file-upload.umd.js",
"module": "index.js",
"typings": "index.d.ts",
"keywords": [
"angular2",
"angular",
"bootstrap",
"angularjs",
"twitter-bootstrap",
"file-upload",
"angular-file-upload"
],
"author": "Vyacheslav Chub <vyacheslav.chub@valor-software.com>",
"author": "Dmitriy Shekhovtsov <valorkin@gmail.com>",
"license": "MIT",
"repository": {
"type": "git",
@@ -43,77 +37,65 @@
"url": "https://github.com/valor-software/ng2-file-upload/issues"
},
"homepage": "https://github.com/valor-software/ng2-file-upload#readme",
"dependencies": {},
"peerDependencies": {
"@angular/common": "^2.3.0",
"@angular/core": "^2.3.0"
},
"peerDependencies": {},
"devDependencies": {
"@angular/common": "2.4.3",
"@angular/compiler": "2.4.3",
"@angular/compiler-cli": "2.4.3",
"@angular/core": "2.4.3",
"@angular/forms": "2.4.3",
"@angular/http": "2.4.3",
"@angular/language-service": "2.4.3",
"@angular/platform-browser": "2.4.3",
"@angular/platform-browser-dynamic": "2.4.3",
"@angular/router": "3.4.3",
"@angular/tsc-wrapped": "0.5.1",
"@ngtools/webpack": "1.2.3",
"@types/jasmine": "2.5.40",
"@types/marked": "0.0.28",
"@types/node": "7.0.0",
"@types/webpack": "^2.2.1",
"angular-cli": "1.0.0-beta.25.5",
"@angular-devkit/build-angular": "14.2.10",
"@angular-eslint/eslint-plugin": "14.0.4",
"@angular-eslint/eslint-plugin-template": "14.0.4",
"@angular-eslint/template-parser": "14.0.4",
"@angular/animations": "14.2.2",
"@angular/cli": "~14.2.0",
"@angular/common": "14.2.2",
"@angular/compiler": "14.2.2",
"@angular/compiler-cli": "14.2.2",
"@angular/core": "14.2.2",
"@angular/forms": "14.2.2",
"@angular/platform-browser": "14.2.2",
"@angular/platform-browser-dynamic": "14.2.2",
"@nrwl/angular": "14.8.6",
"@nrwl/cli": "14.7.5",
"@nrwl/eslint-plugin-nx": "14.7.5",
"@nrwl/jest": "14.7.5",
"@nrwl/linter": "14.7.5",
"@nrwl/node": "14.7.5",
"@types/fs-extra": "9.0.7",
"@types/jest": "28.1.8",
"@types/node": "18.7.1",
"@types/webpack": "^5.0.0",
"@typescript-eslint/eslint-plugin": "5.33.1",
"@typescript-eslint/parser": "5.33.1",
"bootstrap": "3.3.7",
"chokidar-cli": "1.2.0",
"classlist-polyfill": "1.0.3",
"codecov": "1.0.1",
"codelyzer": "~2.0.0-beta.4",
"conventional-changelog-cli": "1.2.0",
"conventional-github-releaser": "1.1.3",
"core-js": "^2.4.1",
"cpy": "5.0.0",
"cpy-cli": "1.0.1",
"del-cli": "0.2.1",
"gh-pages": "0.12.0",
"gitignore-to-glob": "0.3.0",
"classlist-polyfill": "1.2.0",
"codecov": "3.8.2",
"conventional-changelog-cli": "2.1.1",
"conventional-github-releaser": "3.1.5",
"core-js": "^3.14.0",
"eslint": "8.15.0",
"eslint-config-prettier": "8.1.0",
"fs-extra": "9.1.0",
"gh-pages": "3.2.2",
"google-code-prettify": "1.0.5",
"html-loader": "0.4.4",
"jasmine": "2.5.3",
"jasmine-core": "2.5.2",
"jasmine-data-provider": "2.2.0",
"jasmine-spec-reporter": "3.2.0",
"karma": "1.4.0",
"karma-chrome-launcher": "^2.0.0",
"karma-cli": "^1.0.1",
"karma-jasmine": "^1.0.2",
"karma-remap-istanbul": "0.4.0",
"karma-sauce-launcher": "1.1.0",
"lite-server": "2.2.2",
"lodash": "4.17.4",
"markdown-loader": "^0.1.7",
"marked": "0.3.6",
"ng2-bootstrap": "1.2.2",
"ng2-page-scroll": "4.0.0-beta.2",
"ngm-cli": "0.4.0",
"npm-run-all": "^4.0.1",
"pre-commit": "1.2.2",
"protractor": "5.0.0",
"reflect-metadata": "0.1.9",
"require-dir": "0.3.1",
"rxjs": "5.0.3",
"systemjs-builder": "0.15.34",
"ts-helpers": "^1.1.1",
"ts-node": "2.0.0",
"tslint": "4.3.1",
"tslint-config-valorsoft": "1.2.0",
"typedoc": "0.5.5",
"typescript": "2.1.5",
"wallaby-webpack": "0.0.30",
"webdriver-manager": "11.1.1",
"zone.js": "0.7.5"
"html-loader": "^1.3.2",
"jest": "28.1.3",
"jest-createspyobj": "2.0.0",
"jest-environment-jsdom": "28.1.1",
"jest-preset-angular": "12.2.2",
"markdown-loader": "^6.0.0",
"ng-packagr": "14.2.1",
"ngx-bootstrap": "8.0.0",
"npm-run-all": "^4.1.5",
"postcss": "^8.3.9",
"postcss-import": "14.1.0",
"postcss-preset-env": "7.5.0",
"postcss-url": "10.1.3",
"protractor": "~7.0.0",
"raw-loader": "^4.0.2",
"rxjs": "^6.6.0",
"ts-helpers": "^1.1.2",
"ts-jest": "28.0.8",
"ts-node": "10.9.1",
"typescript": "4.7.4",
"zone.js": "0.11.5"
},
"contributors": [
{
@@ -136,5 +118,10 @@
"email": "otelnov@gmail.com",
"url": "https://github.com/otelnov"
}
]
],
"dependencies": {
"@nrwl/workspace": "14.7.5",
"nx": "14.7.5",
"tslib": "^2.0.0"
}
}

1
scripts/.gitignore vendored
View File

@@ -1 +0,0 @@
!**/*.js

View File

@@ -1,21 +0,0 @@
// tslint:disable
/**
* @copyright Angular ng-bootstrap team
*/
beforeEach(() => {
jasmine.addMatchers({
toHaveCssClass(/*util, customEqualityTests*/) {
return {compare: buildError(false), negativeCompare: buildError(true)};
function buildError(isNot) {
return function (actual, className) {
const orNot = isNot ? 'not ' : '';
return {
pass: actual.classList.contains(className) === !isNot,
message: `Expected ${actual.outerHTML} ${orNot} to contain the CSS class "${className}"`
};
};
}
}
});
});

5
scripts/polyfills.ts Normal file
View File

@@ -0,0 +1,5 @@
// This file includes polyfills needed by Angular 2 and is loaded before
// the app. You can add your own extra polyfills to this file.
import 'ts-helpers';
import 'zone.js';
import 'classlist-polyfill';

View File

@@ -1,18 +0,0 @@
module.exports.customLaunchers = function customLaunchers() {
return {
sl_chrome: {base: 'SauceLabs', browserName: 'chrome'},
sl_chrome_1: {base: 'SauceLabs', browserName: 'chrome', version: 'latest-1'},
sl_firefox: {base: 'SauceLabs', browserName: 'firefox'},
sl_firefox_1: {base: 'SauceLabs', browserName: 'firefox', version: 'latest-1'},
sl_ie9: {base: 'SauceLabs', browserName: 'internet explorer', platform: 'Windows 2008', version: '9'},
'SL_IE10': {base: 'SauceLabs', browserName: 'internet explorer', platform: 'Windows 2012', version: '10'},
'SL_IE11': {base: 'SauceLabs', browserName: 'internet explorer', platform: 'Windows 8.1', version: '11'},
'SL_EDGE': {base: 'SauceLabs', browserName: 'MicrosoftEdge', platform: 'Windows 10', version: '13.10586'},
'SL_IOS9': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '9.3'},
'SL_IOS10': {base: 'SauceLabs', browserName: 'iphone', platform: 'OS X 10.10', version: '10.0'},
'SL_ANDROID4.4': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '4.4'},
'SL_ANDROID5': {base: 'SauceLabs', browserName: 'android', platform: 'Linux', version: '5.1'},
'SL_SAFARI9': {base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.11', version: '9.0'}
};
};

13
scripts/set-version.ts Normal file
View File

@@ -0,0 +1,13 @@
import { readJson, writeJson } from 'fs-extra';
const libPackage = './libs/ng2-file-upload/package.json';
const mainPackage = './package.json';
(async () => {
const version = await readJson(mainPackage).then(json => json.version);
const packageJson = await readJson(libPackage);
if (packageJson.version) {
packageJson.version = version;
}
await writeJson(libPackage, packageJson, { spaces: 2 });
})();

View File

@@ -1,37 +0,0 @@
import '../demo/src/polyfills.ts';
import 'zone.js/dist/long-stack-trace-zone';
import 'zone.js/dist/proxy.js';
import 'zone.js/dist/sync-test';
import 'zone.js/dist/jasmine-patch';
import 'zone.js/dist/async-test';
import 'zone.js/dist/fake-async-test';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
import './matchers';
// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
declare var __karma__: any;
declare var require: any;
// Prevent Karma from running prematurely.
__karma__.loaded = Function.prototype;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
let context = require.context('../demo/src', true, /\.spec\.ts/);
// And load the modules.
context.keys().map(context);
let context2 = require.context('../src/spec', true, /\.spec\.ts/);
context2.keys().map(context2);
// Finally, start Karma to run the tests.
__karma__.start();

View File

@@ -1,95 +0,0 @@
import { Directive, EventEmitter, ElementRef, HostListener, Input, Output } from '@angular/core';
import { FileUploader } from './file-uploader.class';
@Directive({selector: '[ng2FileDrop]'})
export class FileDropDirective {
@Input() public uploader:FileUploader;
@Output() public fileOver:EventEmitter<any> = new EventEmitter();
@Output() public onFileDrop:EventEmitter<File[]> = new EventEmitter<File[]>();
protected element:ElementRef;
public constructor(element:ElementRef) {
this.element = element;
}
public getOptions():any {
return this.uploader.options;
}
public getFilters():any {
return {};
}
@HostListener('drop', ['$event'])
public onDrop(event:any):void {
let transfer = this._getTransfer(event);
if (!transfer) {
return;
}
let options = this.getOptions();
let filters = this.getFilters();
this._preventAndStop(event);
this.uploader.addToQueue(transfer.files, options, filters);
this.fileOver.emit(false);
this.onFileDrop.emit(transfer.files);
}
@HostListener('dragover', ['$event'])
public onDragOver(event:any):void {
let transfer = this._getTransfer(event);
if (!this._haveFiles(transfer.types)) {
return;
}
transfer.dropEffect = 'copy';
this._preventAndStop(event);
this.fileOver.emit(true);
}
@HostListener('dragleave', ['$event'])
public onDragLeave(event:any):any {
if ((this as any).element) {
if (event.currentTarget === (this as any).element[0]) {
return;
}
}
this._preventAndStop(event);
this.fileOver.emit(false);
}
protected _getTransfer(event:any):any {
return event.dataTransfer ? event.dataTransfer : event.originalEvent.dataTransfer; // jQuery fix;
}
protected _preventAndStop(event:any):any {
event.preventDefault();
event.stopPropagation();
}
protected _haveFiles(types:any):any {
if (!types) {
return false;
}
if (types.indexOf) {
return types.indexOf('Files') !== -1;
} else if (types.contains) {
return types.contains('Files');
} else {
return false;
}
}
/*
_addOverClass(item:any):any {
item.addOverClass();
}
_removeOverClass(item:any):any {
item.removeOverClass();
}*/
}

View File

@@ -1,32 +0,0 @@
function isElement(node:any):boolean {
return !!(node && (node.nodeName || node.prop && node.attr && node.find));
}
export class FileLikeObject {
public lastModifiedDate:any;
public size:any;
public type:string;
public name:string;
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 as any)[method](fakePathOrObject);
}
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}):void {
// this.lastModifiedDate = copy(object.lastModifiedDate);
this.size = object.size;
this.type = object.type;
this.name = object.name;
}
}

View File

@@ -1,46 +0,0 @@
import { Directive, ElementRef, Input, HostListener } from '@angular/core';
import { FileUploader } from './file-uploader.class';
// todo: filters
@Directive({selector: '[ng2FileSelect]'})
export class FileSelectDirective {
@Input() public uploader:FileUploader;
protected element:ElementRef;
public constructor(element:ElementRef) {
this.element = element;
}
public getOptions():any {
return this.uploader.options;
}
public getFilters():any {
return void 0;
}
public isEmptyAfterSelection():boolean {
return !!this.element.nativeElement.attributes.multiple;
}
@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();
let filters = this.getFilters();
// if(!this.uploader.isHTML5) this.destroy();
this.uploader.addToQueue(files, options, filters);
if (this.isEmptyAfterSelection()) {
// todo
this.element.nativeElement.value = '';
/*this.element.nativeElement
.replaceWith(this.element = this.element.nativeElement.clone(true)); // IE fix*/
}
}
}

View File

@@ -1,154 +0,0 @@
export class FileType {
/* MS office */
public static mime_doc:string[] = [
'application/msword',
'application/msword',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
'application/vnd.ms-word.document.macroEnabled.12',
'application/vnd.ms-word.template.macroEnabled.12'
];
public static mime_xsl:string[] = [
'application/vnd.ms-excel',
'application/vnd.ms-excel',
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
'application/vnd.ms-excel.sheet.macroEnabled.12',
'application/vnd.ms-excel.template.macroEnabled.12',
'application/vnd.ms-excel.addin.macroEnabled.12',
'application/vnd.ms-excel.sheet.binary.macroEnabled.12'
];
public static mime_ppt:string[] = [
'application/vnd.ms-powerpoint',
'application/vnd.ms-powerpoint',
'application/vnd.ms-powerpoint',
'application/vnd.ms-powerpoint',
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'application/vnd.openxmlformats-officedocument.presentationml.template',
'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
'application/vnd.ms-powerpoint.addin.macroEnabled.12',
'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
'application/vnd.ms-powerpoint.slideshow.macroEnabled.12'
];
/* PSD */
public static mime_psd:string[] = [
'image/photoshop',
'image/x-photoshop',
'image/psd',
'application/photoshop',
'application/psd',
'zz-application/zz-winassoc-psd'
];
/* Compressed files */
public static mime_compress:string[] = [
'application/x-gtar',
'application/x-gcompress',
'application/compress',
'application/x-tar',
'application/x-rar-compressed',
'application/octet-stream'
];
public static getMimeClass(file:any):string {
let mimeClass = 'application';
if (this.mime_psd.indexOf(file.type) !== -1) {
mimeClass = 'image';
} else if (file.type.match('image.*')) {
mimeClass = 'image';
} else if (file.type.match('video.*')) {
mimeClass = 'video';
} else if (file.type.match('audio.*')) {
mimeClass = 'audio';
} else if (file.type === 'application/pdf') {
mimeClass = 'pdf';
} else if (this.mime_compress.indexOf(file.type) !== -1) {
mimeClass = 'compress';
} else if (this.mime_doc.indexOf(file.type) !== -1) {
mimeClass = 'doc';
} else if (this.mime_xsl.indexOf(file.type) !== -1) {
mimeClass = 'xls';
} else if (this.mime_ppt.indexOf(file.type) !== -1) {
mimeClass = 'ppt';
}
if (mimeClass === 'application') {
mimeClass = this.fileTypeDetection(file.name);
}
return mimeClass;
}
public static fileTypeDetection(inputFilename:string):string {
let types:{[key:string]:string} = {
'jpg': 'image',
'jpeg': 'image',
'tif': 'image',
'psd': 'image',
'bmp': 'image',
'png': 'image',
'nef': 'image',
'tiff': 'image',
'cr2': 'image',
'dwg': 'image',
'cdr': 'image',
'ai': 'image',
'indd': 'image',
'pin': 'image',
'cdp': 'image',
'skp': 'image',
'stp': 'image',
'3dm': 'image',
'mp3': 'audio',
'wav': 'audio',
'wma': 'audio',
'mod': 'audio',
'm4a': 'audio',
'compress': 'compress',
'rar': 'compress',
'7z': 'compress',
'lz': 'compress',
'z01': 'compress',
'pdf': 'pdf',
'xls': 'xls',
'xlsx': 'xls',
'ods': 'xls',
'mp4': 'video',
'avi': 'video',
'wmv': 'video',
'mpg': 'video',
'mts': 'video',
'flv': 'video',
'3gp': 'video',
'vob': 'video',
'm4v': 'video',
'mpeg': 'video',
'm2ts': 'video',
'mov': 'video',
'doc': 'doc',
'docx': 'doc',
'eps': 'doc',
'txt': 'doc',
'odt': 'doc',
'rtf': 'doc',
'ppt': 'ppt',
'pptx': 'ppt',
'pps': 'ppt',
'ppsx': 'ppt',
'odp': 'ppt'
};
let chunks = inputFilename.split('.');
if (chunks.length < 2) {
return 'application';
}
let extension = chunks[chunks.length - 1].toLowerCase();
if (types[extension] === undefined) {
return 'application';
} else {
return types[extension];
}
}
}

View File

@@ -1,13 +0,0 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FileDropDirective } from './file-drop.directive';
import { FileSelectDirective } from './file-select.directive';
@NgModule({
imports: [CommonModule],
declarations: [FileDropDirective, FileSelectDirective],
exports: [FileDropDirective, FileSelectDirective]
})
export class FileUploadModule {
}

View File

@@ -1,491 +0,0 @@
import { FileLikeObject } from './file-like-object.class';
import { FileItem } from './file-item.class';
import { FileType } from './file-type.class';
function isFile(value:any):boolean {
return (File && value instanceof File);
}
// function isFileLikeObject(value:any) {
export interface Headers {
name:string;
value:string;
}
export type ParsedResponseHeaders = {[headerFieldName:string]:string};
export type FilterFunction = {name:string, fn:(item?:FileLikeObject, options?:FileUploaderOptions)=>boolean};
export interface FileUploaderOptions {
allowedMimeType?:Array<string>;
allowedFileType?:Array<string>;
autoUpload?:boolean;
isHTML5?:boolean;
filters?:Array<FilterFunction>;
headers?:Array<Headers>;
method?:string;
authToken?:string;
maxFileSize?:number;
queueLimit?:number;
removeAfterUpload?:boolean;
url?:string;
disableMultipart?:boolean;
itemAlias?: string;
authTokenHeader?: string;
additionalParameter?:{[key: string]: any};
}
export class FileUploader {
public authToken:string;
public isUploading:boolean = false;
public queue:Array<FileItem> = [];
public progress:number = 0;
public _nextIndex:number = 0;
public autoUpload:any;
public authTokenHeader: string;
public options:FileUploaderOptions = {
autoUpload: false,
isHTML5: true,
filters: [],
removeAfterUpload: false,
disableMultipart: false
};
protected _failFilterIndex:number;
public constructor(options:FileUploaderOptions) {
this.setOptions(options);
}
public setOptions(options:FileUploaderOptions):void {
this.options = Object.assign(this.options, options);
this.authToken = options.authToken;
this.authTokenHeader = options.authTokenHeader || 'Authorization';
this.autoUpload = options.autoUpload;
this.options.filters.unshift({name: 'queueLimit', fn: this._queueLimitFilter});
if (this.options.maxFileSize) {
this.options.filters.unshift({name: 'fileSize', fn: this._fileSizeFilter});
}
if (this.options.allowedFileType) {
this.options.filters.unshift({name: 'fileType', fn: this._fileTypeFilter});
}
if (this.options.allowedMimeType) {
this.options.filters.unshift({name: 'mimeType', fn: this._mimeTypeFilter});
}
for(let i = 0; i < this.queue.length; i++) {
this.queue[i].url = this.options.url;
}
// this.options.filters.unshift({name: 'folder', fn: this._folderFilter});
}
public addToQueue(files:File[], options?:FileUploaderOptions, filters?:FilterFunction[]|string):void {
let list:File[] = [];
for (let file of files) {
list.push(file);
}
let arrayOfFilters = this._getFilters(filters);
let count = this.queue.length;
let addedFileItems:FileItem[] = [];
list.map((some:File) => {
if (!options) {
options = this.options;
}
let temp = new FileLikeObject(some);
if (this._isValidFile(temp, arrayOfFilters, options)) {
let fileItem = new FileItem(this, some, options);
addedFileItems.push(fileItem);
this.queue.push(fileItem);
this._onAfterAddingFile(fileItem);
} else {
let filter = arrayOfFilters[this._failFilterIndex];
this._onWhenAddingFileFailed(temp, filter, options);
}
});
if (this.queue.length !== count) {
this._onAfterAddingAll(addedFileItems);
this.progress = this._getTotalProgress();
}
this._render();
if (this.options.autoUpload) {
this.uploadAll();
}
}
public removeFromQueue(value:FileItem):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():void {
while (this.queue.length) {
this.queue[0].remove();
}
this.progress = 0;
}
public uploadItem(value:FileItem):void {
let index = this.getIndexOfItem(value);
let item = this.queue[index];
let transport = this.options.isHTML5 ? '_xhrTransport' : '_iframeTransport';
item._prepareToUploading();
if (this.isUploading) {
return;
}
this.isUploading = true;
(this as any)[transport](item);
}
public cancelItem(value:FileItem):void {
let index = this.getIndexOfItem(value);
let item = this.queue[index];
let prop = this.options.isHTML5 ? item._xhr : item._form;
if (item && item.isUploading) {
prop.abort();
}
}
public uploadAll():void {
let items = this.getNotUploadedItems().filter((item:FileItem) => !item.isUploading);
if (!items.length) {
return;
}
items.map((item:FileItem) => item._prepareToUploading());
items[0].upload();
}
public cancelAll():void {
let items = this.getNotUploadedItems();
items.map((item:FileItem) => item.cancel());
}
public isFile(value:any):boolean {
return isFile(value);
}
public isFileLikeObject(value:any):boolean {
return value instanceof FileLikeObject;
}
public getIndexOfItem(value:any):number {
return typeof value === 'number' ? value : this.queue.indexOf(value);
}
public getNotUploadedItems():Array<any> {
return this.queue.filter((item:FileItem) => !item.isUploaded);
}
public getReadyItems():Array<any> {
return this.queue
.filter((item:FileItem) => (item.isReady && !item.isUploading))
.sort((item1:any, item2:any) => item1.index - item2.index);
}
public destroy():void {
return void 0;
/*forEach(this._directives, (key) => {
forEach(this._directives[key], (object) => {
object.destroy();
});
});*/
}
public onAfterAddingAll(fileItems:any):any {
return {fileItems};
}
public onBuildItemForm(fileItem:FileItem, form:any):any {
return {fileItem, form};
}
public onAfterAddingFile(fileItem:FileItem):any {
return {fileItem};
}
public onWhenAddingFileFailed(item:FileLikeObject, filter:any, options:any):any {
return {item, filter, options};
}
public onBeforeUploadItem(fileItem:FileItem):any {
return {fileItem};
}
public onProgressItem(fileItem:FileItem, progress:any):any {
return {fileItem, progress};
}
public onProgressAll(progress:any):any {
return {progress};
}
public onSuccessItem(item:FileItem, response:string, status:number, headers:ParsedResponseHeaders):any {
return {item, response, status, headers};
}
public onErrorItem(item:FileItem, response:string, status:number, headers:ParsedResponseHeaders):any {
return {item, response, status, headers};
}
public onCancelItem(item:FileItem, response:string, status:number, headers:ParsedResponseHeaders):any {
return {item, response, status, headers};
}
public onCompleteItem(item:FileItem, response:string, status:number, headers:ParsedResponseHeaders):any {
return {item, response, status, headers};
}
public onCompleteAll():any {
return void 0;
}
public _mimeTypeFilter(item:FileLikeObject):boolean {
return !(this.options.allowedMimeType && this.options.allowedMimeType.indexOf(item.type) === -1);
}
public _fileSizeFilter(item:FileLikeObject):boolean {
return !(this.options.maxFileSize && item.size > this.options.maxFileSize);
}
public _fileTypeFilter(item:FileLikeObject):boolean {
return !(this.options.allowedFileType &&
this.options.allowedFileType.indexOf(FileType.getMimeClass(item)) === -1);
}
public _onErrorItem(item:FileItem, response:string, status:number, headers:ParsedResponseHeaders):void {
item._onError(response, status, headers);
this.onErrorItem(item, response, status, headers);
}
public _onCompleteItem(item:FileItem, response:string, status:number, headers:ParsedResponseHeaders):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:ParsedResponseHeaders):any {
return (name:any):any => {
if (name) {
return parsedHeaders[name.toLowerCase()] || void 0;
}
return parsedHeaders;
};
}
protected _xhrTransport(item:FileItem):any {
let xhr = item._xhr = new XMLHttpRequest();
let sendable:any;
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');
}
if (!this.options.disableMultipart) {
sendable = new FormData();
this._onBuildItemForm(item, sendable);
sendable.append(item.alias, item._file, item.file.name);
if (this.options.additionalParameter !== undefined) {
Object.keys(this.options.additionalParameter).forEach((key:string) => {
sendable.append(key, this.options.additionalParameter[key]);
});
}
} else {
sendable = item._file;
}
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;
if (this.options.headers) {
for (let header of this.options.headers) {
xhr.setRequestHeader(header.name, header.value);
}
}
if (item.headers.length) {
for (let header of item.headers) {
xhr.setRequestHeader(header.name, header.value);
}
}
if (this.authToken) {
xhr.setRequestHeader(this.authTokenHeader, this.authToken);
}
xhr.send(sendable);
this._render();
}
protected _getTotalProgress(value:number = 0):number {
if (this.options.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);
}
protected _getFilters(filters:FilterFunction[]|string):FilterFunction[] {
if (!filters) {
return this.options.filters;
}
if (Array.isArray(filters)) {
return filters;
}
if (typeof filters === 'string') {
let names = filters.match(/[^\s,]+/g);
return this.options.filters
.filter((filter:any) => names.indexOf(filter.name) !== -1);
}
return this.options.filters;
}
protected _render():any {
return void 0;
// todo: ?
}
// protected _folderFilter(item:FileItem):boolean {
// return !!(item.size || item.type);
// }
protected _queueLimitFilter():boolean {
return this.options.queueLimit === undefined || this.queue.length < this.options.queueLimit;
}
protected _isValidFile(file:FileLikeObject, filters:FilterFunction[], options:FileUploaderOptions):boolean {
this._failFilterIndex = -1;
return !filters.length ? true : filters.every((filter:FilterFunction) => {
this._failFilterIndex++;
return filter.fn.call(this, file, options);
});
}
protected _isSuccessCode(status:number):boolean {
return (status >= 200 && status < 300) || status === 304;
}
/* tslint:disable */
protected _transformResponse(response:string, headers:ParsedResponseHeaders):string {
// todo: ?
/*var headersGetter = this._headersGetter(headers);
forEach($http.defaults.transformResponse, (transformFn) => {
response = transformFn(response, headersGetter);
});*/
return response;
}
/* tslint:enable */
protected _parseHeaders(headers:string):ParsedResponseHeaders {
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;
}
/*protected _iframeTransport(item:FileItem) {
// todo: implement it later
}*/
protected _onWhenAddingFileFailed(item:FileLikeObject, filter:any, options:any):void {
this.onWhenAddingFileFailed(item, filter, options);
}
protected _onAfterAddingFile(item:FileItem):void {
this.onAfterAddingFile(item);
}
protected _onAfterAddingAll(items:any):void {
this.onAfterAddingAll(items);
}
protected _onBeforeUploadItem(item:FileItem):void {
item._onBeforeUpload();
this.onBeforeUploadItem(item);
}
protected _onBuildItemForm(item:FileItem, form:any):void {
item._onBuildForm(form);
this.onBuildItemForm(item, form);
}
protected _onProgressItem(item:FileItem, progress:any):void {
let total = this._getTotalProgress(progress);
this.progress = total;
item._onProgress(progress);
this.onProgressItem(item, progress);
this.onProgressAll(total);
this._render();
}
/* tslint:disable */
protected _onSuccessItem(item:FileItem, response:string, status:number, headers:ParsedResponseHeaders):void {
item._onSuccess(response, status, headers);
this.onSuccessItem(item, response, status, headers);
}
/* tslint:enable */
protected _onCancelItem(item:FileItem, response:string, status:number, headers:ParsedResponseHeaders):void {
item._onCancel(response, status, headers);
this.onCancelItem(item, response, status, headers);
}
}

View File

@@ -1 +0,0 @@
export * from './index';

View File

@@ -1,8 +0,0 @@
{
"name": "ng2-file-upload",
"version": "1.2.0",
"peerDependencies": {
"@angular/common": "*",
"@angular/core": "*"
}
}

View File

@@ -1,28 +0,0 @@
import { Component } from '@angular/core';
import { inject, ComponentFixture, TestBed } from '@angular/core/testing';
import { FileUploader } from '../file-upload/file-uploader.class';
import { FileUploadModule } from '../file-upload/file-upload.module';
@Component({
selector: 'container',
template: `<input type="file" ng2FileSelect [uploader]="uploader" />`
})
export class ContainerComponent {
public uploader:FileUploader = new FileUploader({url: 'localhost:3000'});
}
describe('Directive: FileSelectDirective', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [FileUploadModule],
declarations: [ContainerComponent],
providers: [ContainerComponent]
});
});
it('should be fine', inject([ContainerComponent], (fixture:ComponentFixture<ContainerComponent>) => {
expect(fixture).not.toBeNull();
}));
});

View File

@@ -1,37 +0,0 @@
{
"compilerOptions": {
"outDir": "../dist",
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"sourceMap": false,
"noEmitHelpers": false,
"noImplicitAny": true,
"declaration": true,
"skipLibCheck": true,
"stripInternal": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"lib": ["dom", "es6"],
"types": [
"jasmine",
"webpack"
],
"typeRoots": [
"../node_modules/@types"
]
},
"exclude": [
"node_modules"
],
"files": [
"../scripts/typings.d.ts",
"./ng2-file-upload.ts",
"./index.ts"
],
"angularCompilerOptions": {
"genDir": "../temp/factories"
}
}

27
tsconfig.base.json Normal file
View File

@@ -0,0 +1,27 @@
{
"compileOnSave": false,
"compilerOptions": {
"rootDir": ".",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": true,
"target": "es2015",
"module": "esnext",
"lib": [
"es2017",
"dom"
],
"skipLibCheck": true,
"skipDefaultLibCheck": true,
"baseUrl": ".",
"paths": {
"ng2-file-upload": [
"libs/ng2-file-upload/index.ts"
]
}
},
"exclude": []
}

View File

@@ -1,12 +0,0 @@
{
"extends": "tslint-config-valorsoft",
"rulesDirectory": "./node_modules/codelyzer",
"rules": {
"no-forward-ref": false,
"no-null-keyword": false,
"only-arrow-functions": false,
"no-access-missing-member": false,
"directive-selector": false,
"component-selector": false
}
}

7807
yarn.lock

File diff suppressed because it is too large Load Diff