├── .babelrc ├── .gitignore ├── .jshintrc ├── CONTRIBUTING.md ├── README.md ├── WebpackConfig.js ├── bower.json ├── dist ├── angular-file-upload.js ├── angular-file-upload.js.map ├── angular-file-upload.min.js └── angular-file-upload.min.js.map ├── examples ├── console-sham.js ├── console-sham.min.js ├── image-preview │ ├── controllers.js │ ├── directives.js │ ├── index.html │ ├── upload.php │ └── uploads │ │ └── gap.txt ├── issues │ ├── 862 │ │ ├── controllers.js │ │ └── index.html │ ├── 873 │ │ ├── controllers.js │ │ └── index.html │ ├── 874 │ │ └── index.html │ ├── pr876 │ │ ├── iframe.html │ │ ├── index.html │ │ └── uploadPage.html │ └── upload.php ├── simple │ ├── controllers.js │ ├── index.html │ ├── upload.php │ └── uploads │ │ └── gap.txt └── without-bootstrap │ ├── controllers.js │ ├── directives.js │ ├── index.html │ ├── style.css │ ├── upload.php │ └── uploads │ └── gap.txt ├── gulpfile.js ├── license.txt ├── package.json └── src ├── config.json ├── directives ├── FileDrop.js ├── FileOver.js └── FileSelect.js ├── index.js ├── services ├── FileDirective.js ├── FileDrop.js ├── FileItem.js ├── FileLikeObject.js ├── FileOver.js ├── FileSelect.js ├── FileUploader.js └── Pipeline.js └── values └── options.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015" 4 | ], 5 | "plugins": [ 6 | [ 7 | "transform-es2015-classes", 8 | { 9 | "loose": true 10 | } 11 | ] 12 | ] 13 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .temp 2 | .idea 3 | *.iml 4 | node_modules 5 | bower_components 6 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "camelcase" : true, 3 | "indent": 4, 4 | "strict": true, 5 | "undef": true, 6 | "unused": true, 7 | "quotmark": "single", 8 | "maxlen": 120, 9 | "trailing": true, 10 | "curly": true, 11 | 12 | "devel": true, 13 | 14 | "browser":true, 15 | "jquery":true, 16 | "predef": [ 17 | "angular", 18 | "define", 19 | "require", 20 | "app" 21 | ] 22 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Angular-File-Upload 2 | 3 | :+1::tada: Welcome in angular-file-upload community :tada::+1: 4 | 5 | The following is a set of guidelines for contributing to Angular-File-Upload. 6 | These are just guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request. 7 | 8 | Note : this guide is inspired by https://github.com/atom/atom/blob/master/CONTRIBUTING.md 9 | 10 | #### Table Of Contents 11 | 12 | [How Can I Contribute?](#how-can-i-contribute) 13 | * [Reporting Bugs](#reporting-bugs) 14 | * [Suggesting Enhancements](#suggesting-enhancements) 15 | * [Pull Requests](#pull-requests) 16 | 17 | [Additional Notes](#additional-notes) 18 | * [Issue and Pull Request Labels](#issue-and-pull-request-labels) 19 | * [Contributors Communication](#contributors-communication) 20 | 21 | ## How Can I Contribute? 22 | 23 | ### Reporting Bugs 24 | 25 | This section guides you through submitting a bug report for Angular-File-Upload. Following these guidelines helps maintainers and the community understand your report :pencil:, reproduce the behavior :computer: :computer:, and find related reports :mag_right:. 26 | 27 | Fill out [the required template](ISSUE_TEMPLATE.md), the information it asks for helps us resolve issues faster. 28 | * Before Submitting A Bug Report : **Perform a [quick search](https://github.com/nervgh/angular-file-upload/issues)** to see if the problem has already been reported. If it has, add a comment to the existing issue instead of opening a new one. 29 | * When you are creating a bug report, please include as many details as possible, explain the problem and include additional details to help maintainers reproduce the problem: 30 | 31 | * **Use a clear and descriptive title** for the issue to identify the problem. 32 | * **Describe the exact steps which reproduce the problem** in as many details as possible. For example, start by explaining how you are using Angulare-File-Upload. When listing steps, **don't just say what you did, but explain how you did it**. For example, if you upload a file, explain if you did it by clicking on a button or if you have started the upload programmatically? 33 | * **Provide specific examples to demonstrate the steps**. Include links to files or GitHub projects, or copy/pasteable snippets, which you use in those examples. If you're providing snippets in the issue, use [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines). 34 | * **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior. 35 | * **Explain which behavior you expected to see instead and why.** 36 | * **Include screenshots and animated GIFs** which show you following the described steps and clearly demonstrate the problem. If you use the keyboard while following the steps. You can use [this tool](http://www.cockos.com/licecap/) to record GIFs on macOS and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://github.com/GNOME/byzanz) on Linux. 37 | * **If you're reporting that Angular-File-Upload crashed**, include a crash report with a stack trace from the browser stack. On macOS, the crash report will be available and put it in a [gist](https://gist.github.com/) and provide link to that gist. 38 | 39 | Provide more context by answering these questions: 40 | 41 | * **Did the problem start happening recently** (e.g. after updating to a new version of Angular-File-Upload) or was this always a problem? 42 | * **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens. 43 | * **Does the problem happen for all files or only some?** Does the problem happen only when working with local or remote files (e.g. on network drives), with files of a specific type (e.g. only JavaScript or Python files), with large files or files with very long lines, or with files in a specific encoding? Is there anything else special about the files you are using? 44 | 45 | Include details about your configuration and environment: 46 | 47 | * **Which browser are you using?** ? 48 | * **What's the name and version of the OS you're using**? 49 | 50 | ### Suggesting Enhancements 51 | 52 | This section guides you through submitting an enhancement suggestion for Angular-File-Upload, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion :pencil: and find related suggestions :mag_right:. 53 | * Before Submitting An Enhancement : **Perform a [quick search](https://github.com/nervgh/angular-file-upload/issues)** to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. 54 | 55 | Enhancement suggestions are tracked as [GitHub issues](https://guides.github.com/features/issues/). Provide the following information: 56 | 57 | * **Use a clear and descriptive title** for the issue to identify the suggestion. 58 | * **Provide a step-by-step description of the suggested enhancement** in as many details as possible. 59 | * **Provide specific examples to demonstrate the steps**. Include copy/pasteable snippets which you use in those examples, as [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines). 60 | * **Describe the current behavior** and **explain which behavior you expected to see instead** and why. 61 | * **Include screenshots and animated GIFs** which help you demonstrate the steps or point out the part of Angular-File-Upload which the suggestion is related to. You can use [this tool](http://www.cockos.com/licecap/) to record GIFs on macOS and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://github.com/GNOME/byzanz) on Linux. 62 | * **Explain why this enhancement would be useful** to most Angular-File-Upload users. 63 | * **List some other uploader libs or usages where this enhancement exists.** 64 | 65 | ### Pull Requests 66 | 67 | * Follow [standardJS](https://github.com/feross/standard) styleguides. 68 | * Include thoughtfully-worded, [protractor](http://www.protractortest.org/#/) tests 69 | * Document new code based using [jsdoc](http://usejsdoc.org/) 70 | 71 | ## Additional Notes 72 | 73 | ### Issue and Pull Request Labels 74 | 75 | Feel free to grade issues and PRs by tags if it helps you work. 76 | You can create any labels what you need and you can manage project using these labels. 77 | 78 | ### Contributors Communication 79 | 80 | To participate to discussion, please ask access to [nervgh](https://github.com/nervgh) to the telegram group of contributors. 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular File Upload 2 | 3 | + compatible with AngularJS v1.x 4 | 5 | --- 6 | 7 | ## About 8 | 9 | **Angular File Upload** is a module for the [AngularJS](http://angularjs.org/) framework. Supports drag-n-drop upload, upload progress, validation filters and a file upload queue. It supports native HTML5 uploads, but degrades to a legacy iframe upload method for older browsers. Works with any server side platform which supports standard HTML form uploads. 10 | 11 | When files are selected or dropped into the component, one or more filters are applied. Files which pass all filters are added to the queue. When file is added to the queue, for him is created instance of `{FileItem}` and uploader options are copied into this object. After, items in the queue (FileItems) are ready for uploading. 12 | 13 | ## Package managers 14 | 15 | ### NPM [](https://www.npmjs.com/package/angular-file-upload) 16 | ``` 17 | npm install angular-file-upload 18 | ``` 19 | You could find this module in npm like [_angular file upload_](https://www.npmjs.com/package/angular-file-upload). 20 | 21 | ### Yarn [](https://www.npmjs.com/package/angular-file-upload) 22 | ``` 23 | yarn add --exact angular-file-upload 24 | ``` 25 | You could find this module in yarn like [_angular file upload_](https://yarnpkg.com/en/package/angular-file-upload). 26 | 27 | ### Module Dependency 28 | 29 | Add `'angularFileUpload'` to your module declaration: 30 | 31 | ``` 32 | var app = angular.module('my-app', [ 33 | 'angularFileUpload' 34 | ]); 35 | ``` 36 | 37 | ## Demos 38 | 1. [Simple example](http://nervgh.github.io/pages/angular-file-upload/examples/simple) 39 | 2. [Uploads only images (with canvas preview)](http://nervgh.github.io/pages/angular-file-upload/examples/image-preview) 40 | 3. [Without bootstrap example](http://nervgh.github.io/pages/angular-file-upload/examples/without-bootstrap) 41 | 42 | ## More Info 43 | 44 | 1. [Introduction](https://github.com/nervgh/angular-file-upload/wiki/Introduction) 45 | 2. [Module API](https://github.com/nervgh/angular-file-upload/wiki/Module-API) 46 | 3. [FAQ](https://github.com/nervgh/angular-file-upload/wiki/FAQ) 47 | 4. [Migrate from 0.x.x to 1.x.x](https://github.com/nervgh/angular-file-upload/wiki/Migrate-from-0.x.x-to-1.x.x) 48 | 5. [RubyGem](https://github.com/marthyn/angularjs-file-upload-rails) 49 | 50 | ## Browser compatibility 51 | This module uses the _feature detection_ pattern for adaptation its behaviour: [fd1](https://github.com/nervgh/angular-file-upload/blob/v2.3.1/src/services/FileUploader.js#L728), 52 | [fd2](https://github.com/nervgh/angular-file-upload/blob/v2.3.1/examples/image-preview/directives.js#L21). 53 | 54 | You could check out features of target browsers using http://caniuse.com/. For example, the [File API](http://caniuse.com/#feat=fileapi) feature. 55 | 56 | | Feature/Browser | IE 8-9 | IE10+ | Firefox 28+ | Chrome 38+ | Safari 6+ | 57 | |----------|:---:|:---:|:---:|:---:|:---:| 58 | | `` | + | + | + | + | + | 59 | | `` | - | + | + | + | + | 60 | | Drag-n-drop | - | + | + | + | + | 61 | | Iframe transport (only for old browsers) | + | + | + | + | + | 62 | | XHR transport (multipart,binary) | - | + | + | + | + | 63 | | An image preview via Canvas (not built-in) | - | + | + | + | + | 64 | | AJAX headers | - | + | + | + | + | 65 | 66 | 67 | ## How to ask a question 68 | 69 | ### A right way to ask a question 70 | If you have a question, please, follow next steps: 71 | - Try to find an answer to your question using [search](https://github.com/nervgh/angular-file-upload/issues?utf8=%E2%9C%93&q=) 72 | - If you have not found an answer, create [new issue](https://github.com/nervgh/angular-file-upload/issues/new) on issue-tracker 73 | 74 | ### Why email a question is a bad way? 75 | When you emal me a question: 76 | - You lose an opportunity to get an answer from other team members or users (devs) 77 | - It requires from me to answer on same questions again and again 78 | - It is not a rational way. For example, if everybody who use code of this project will have emailed me a question then I will be receiving ~700 emails each day =) 79 | - It is a very slow way. I have not time for it. 80 | -------------------------------------------------------------------------------- /WebpackConfig.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // http://webpack.github.io/docs/ 4 | let webpack = require('webpack'); 5 | 6 | 7 | class WebpackConfig { 8 | /** 9 | * @param {Object} descriptor 10 | * @param {String} descriptor.name 11 | * @param {String} descriptor.version 12 | * @param {String} descriptor.homepage 13 | * @param {Object} path 14 | * @param {String} path.src 15 | * @param {String} path.dist 16 | */ 17 | constructor(descriptor, path) { 18 | this.name = descriptor.name; 19 | this.version = descriptor.version; 20 | this.homepage = descriptor.homepage; 21 | this.path = path; 22 | } 23 | /** 24 | * @returns {Object} 25 | */ 26 | get() { 27 | let entry = {}; 28 | entry[this.name] = this.path.src; 29 | entry[this.name + '.min'] = this.path.src; 30 | 31 | return { 32 | entry, 33 | module: { 34 | loaders: [ 35 | // https://github.com/babel/babel-loader 36 | { 37 | test: /\.js$/, 38 | loader: 'babel' 39 | }, 40 | // https://github.com/webpack/json-loader 41 | {test: /\.json$/, loader: 'json'}, 42 | // https://github.com/webpack/html-loader 43 | {test: /\.html$/, loader: 'raw'} 44 | ] 45 | }, 46 | devtool: 'source-map', 47 | output: { 48 | libraryTarget: 'umd', 49 | library: this.name, 50 | filename: '[name].js' 51 | }, 52 | plugins: [ 53 | new webpack.optimize.UglifyJsPlugin({ 54 | // Minify only [name].min.js file 55 | // http://stackoverflow.com/a/34018909 56 | include: /\.min\.js$/ 57 | }), 58 | new webpack.BannerPlugin( 59 | `/*\n` + 60 | ` ${this.name} v${this.version}\n` + 61 | ` ${this.homepage}\n` + 62 | `*/\n` 63 | , { 64 | entryOnly: true, 65 | raw: true 66 | }) 67 | ] 68 | }; 69 | } 70 | } 71 | 72 | module.exports = WebpackConfig; -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-file-upload", 3 | "main": "dist/angular-file-upload.min.js", 4 | "homepage": "https://github.com/nervgh/angular-file-upload", 5 | "ignore": ["examples"], 6 | "dependencies": { 7 | "angular": "^1.1.5" 8 | }, 9 | "devDependencies": { 10 | "es5-shim": ">=3.4.0" 11 | }, 12 | "keywords": [ 13 | "angular", 14 | "file", 15 | "upload", 16 | "module" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /dist/angular-file-upload.js: -------------------------------------------------------------------------------- 1 | /* 2 | angular-file-upload v2.6.1 3 | https://github.com/nervgh/angular-file-upload 4 | */ 5 | 6 | (function webpackUniversalModuleDefinition(root, factory) { 7 | if(typeof exports === 'object' && typeof module === 'object') 8 | module.exports = factory(); 9 | else if(typeof define === 'function' && define.amd) 10 | define([], factory); 11 | else if(typeof exports === 'object') 12 | exports["angular-file-upload"] = factory(); 13 | else 14 | root["angular-file-upload"] = factory(); 15 | })(this, function() { 16 | return /******/ (function(modules) { // webpackBootstrap 17 | /******/ // The module cache 18 | /******/ var installedModules = {}; 19 | /******/ 20 | /******/ // The require function 21 | /******/ function __webpack_require__(moduleId) { 22 | /******/ 23 | /******/ // Check if module is in cache 24 | /******/ if(installedModules[moduleId]) 25 | /******/ return installedModules[moduleId].exports; 26 | /******/ 27 | /******/ // Create a new module (and put it into the cache) 28 | /******/ var module = installedModules[moduleId] = { 29 | /******/ exports: {}, 30 | /******/ id: moduleId, 31 | /******/ loaded: false 32 | /******/ }; 33 | /******/ 34 | /******/ // Execute the module function 35 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 36 | /******/ 37 | /******/ // Flag the module as loaded 38 | /******/ module.loaded = true; 39 | /******/ 40 | /******/ // Return the exports of the module 41 | /******/ return module.exports; 42 | /******/ } 43 | /******/ 44 | /******/ 45 | /******/ // expose the modules object (__webpack_modules__) 46 | /******/ __webpack_require__.m = modules; 47 | /******/ 48 | /******/ // expose the module cache 49 | /******/ __webpack_require__.c = installedModules; 50 | /******/ 51 | /******/ // __webpack_public_path__ 52 | /******/ __webpack_require__.p = ""; 53 | /******/ 54 | /******/ // Load entry module and return exports 55 | /******/ return __webpack_require__(0); 56 | /******/ }) 57 | /************************************************************************/ 58 | /******/ ([ 59 | /* 0 */ 60 | /***/ (function(module, exports, __webpack_require__) { 61 | 62 | 'use strict'; 63 | 64 | var _config = __webpack_require__(1); 65 | 66 | var _config2 = _interopRequireDefault(_config); 67 | 68 | var _options = __webpack_require__(2); 69 | 70 | var _options2 = _interopRequireDefault(_options); 71 | 72 | var _FileUploader = __webpack_require__(3); 73 | 74 | var _FileUploader2 = _interopRequireDefault(_FileUploader); 75 | 76 | var _FileLikeObject = __webpack_require__(4); 77 | 78 | var _FileLikeObject2 = _interopRequireDefault(_FileLikeObject); 79 | 80 | var _FileItem = __webpack_require__(5); 81 | 82 | var _FileItem2 = _interopRequireDefault(_FileItem); 83 | 84 | var _FileDirective = __webpack_require__(6); 85 | 86 | var _FileDirective2 = _interopRequireDefault(_FileDirective); 87 | 88 | var _FileSelect = __webpack_require__(7); 89 | 90 | var _FileSelect2 = _interopRequireDefault(_FileSelect); 91 | 92 | var _Pipeline = __webpack_require__(8); 93 | 94 | var _Pipeline2 = _interopRequireDefault(_Pipeline); 95 | 96 | var _FileDrop = __webpack_require__(9); 97 | 98 | var _FileDrop2 = _interopRequireDefault(_FileDrop); 99 | 100 | var _FileOver = __webpack_require__(10); 101 | 102 | var _FileOver2 = _interopRequireDefault(_FileOver); 103 | 104 | var _FileSelect3 = __webpack_require__(11); 105 | 106 | var _FileSelect4 = _interopRequireDefault(_FileSelect3); 107 | 108 | var _FileDrop3 = __webpack_require__(12); 109 | 110 | var _FileDrop4 = _interopRequireDefault(_FileDrop3); 111 | 112 | var _FileOver3 = __webpack_require__(13); 113 | 114 | var _FileOver4 = _interopRequireDefault(_FileOver3); 115 | 116 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 117 | 118 | angular.module(_config2.default.name, []).value('fileUploaderOptions', _options2.default).factory('FileUploader', _FileUploader2.default).factory('FileLikeObject', _FileLikeObject2.default).factory('FileItem', _FileItem2.default).factory('FileDirective', _FileDirective2.default).factory('FileSelect', _FileSelect2.default).factory('FileDrop', _FileDrop2.default).factory('FileOver', _FileOver2.default).factory('Pipeline', _Pipeline2.default).directive('nvFileSelect', _FileSelect4.default).directive('nvFileDrop', _FileDrop4.default).directive('nvFileOver', _FileOver4.default).run(['FileUploader', 'FileLikeObject', 'FileItem', 'FileDirective', 'FileSelect', 'FileDrop', 'FileOver', 'Pipeline', function (FileUploader, FileLikeObject, FileItem, FileDirective, FileSelect, FileDrop, FileOver, Pipeline) { 119 | // only for compatibility 120 | FileUploader.FileLikeObject = FileLikeObject; 121 | FileUploader.FileItem = FileItem; 122 | FileUploader.FileDirective = FileDirective; 123 | FileUploader.FileSelect = FileSelect; 124 | FileUploader.FileDrop = FileDrop; 125 | FileUploader.FileOver = FileOver; 126 | FileUploader.Pipeline = Pipeline; 127 | }]); 128 | 129 | /***/ }), 130 | /* 1 */ 131 | /***/ (function(module, exports) { 132 | 133 | module.exports = {"name":"angularFileUpload"} 134 | 135 | /***/ }), 136 | /* 2 */ 137 | /***/ (function(module, exports) { 138 | 139 | 'use strict'; 140 | 141 | Object.defineProperty(exports, "__esModule", { 142 | value: true 143 | }); 144 | exports.default = { 145 | url: '/', 146 | alias: 'file', 147 | headers: {}, 148 | queue: [], 149 | progress: 0, 150 | autoUpload: false, 151 | removeAfterUpload: false, 152 | method: 'POST', 153 | filters: [], 154 | formData: [], 155 | queueLimit: Number.MAX_VALUE, 156 | withCredentials: false, 157 | disableMultipart: false 158 | }; 159 | 160 | /***/ }), 161 | /* 3 */ 162 | /***/ (function(module, exports, __webpack_require__) { 163 | 164 | 'use strict'; 165 | 166 | Object.defineProperty(exports, "__esModule", { 167 | value: true 168 | }); 169 | 170 | var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); 171 | 172 | exports.default = __identity; 173 | 174 | var _config = __webpack_require__(1); 175 | 176 | var _config2 = _interopRequireDefault(_config); 177 | 178 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 179 | 180 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 181 | 182 | var _angular = angular, 183 | bind = _angular.bind, 184 | copy = _angular.copy, 185 | extend = _angular.extend, 186 | forEach = _angular.forEach, 187 | isObject = _angular.isObject, 188 | isNumber = _angular.isNumber, 189 | isDefined = _angular.isDefined, 190 | isArray = _angular.isArray, 191 | isUndefined = _angular.isUndefined, 192 | element = _angular.element; 193 | function __identity(fileUploaderOptions, $rootScope, $http, $window, $timeout, FileLikeObject, FileItem, Pipeline) { 194 | var File = $window.File, 195 | FormData = $window.FormData; 196 | 197 | var FileUploader = function () { 198 | /********************** 199 | * PUBLIC 200 | **********************/ 201 | /** 202 | * Creates an instance of FileUploader 203 | * @param {Object} [options] 204 | * @constructor 205 | */ 206 | function FileUploader(options) { 207 | _classCallCheck(this, FileUploader); 208 | 209 | var settings = copy(fileUploaderOptions); 210 | 211 | extend(this, settings, options, { 212 | isUploading: false, 213 | _nextIndex: 0, 214 | _directives: { select: [], drop: [], over: [] } 215 | }); 216 | 217 | // add default filters 218 | this.filters.unshift({ name: 'queueLimit', fn: this._queueLimitFilter }); 219 | this.filters.unshift({ name: 'folder', fn: this._folderFilter }); 220 | } 221 | /** 222 | * Adds items to the queue 223 | * @param {File|HTMLInputElement|Object|FileList|Array} files 224 | * @param {Object} [options] 225 | * @param {Array|String} filters 226 | */ 227 | 228 | 229 | FileUploader.prototype.addToQueue = function addToQueue(files, options, filters) { 230 | var _this = this; 231 | 232 | var incomingQueue = this.isArrayLikeObject(files) ? Array.prototype.slice.call(files) : [files]; 233 | var arrayOfFilters = this._getFilters(filters); 234 | var count = this.queue.length; 235 | var addedFileItems = []; 236 | 237 | var next = function next() { 238 | var something = incomingQueue.shift(); 239 | 240 | if (isUndefined(something)) { 241 | return done(); 242 | } 243 | 244 | var fileLikeObject = _this.isFile(something) ? something : new FileLikeObject(something); 245 | var pipes = _this._convertFiltersToPipes(arrayOfFilters); 246 | var pipeline = new Pipeline(pipes); 247 | var onThrown = function onThrown(err) { 248 | var originalFilter = err.pipe.originalFilter; 249 | 250 | var _err$args = _slicedToArray(err.args, 2), 251 | fileLikeObject = _err$args[0], 252 | options = _err$args[1]; 253 | 254 | _this._onWhenAddingFileFailed(fileLikeObject, originalFilter, options); 255 | next(); 256 | }; 257 | var onSuccessful = function onSuccessful(fileLikeObject, options) { 258 | var fileItem = new FileItem(_this, fileLikeObject, options); 259 | addedFileItems.push(fileItem); 260 | _this.queue.push(fileItem); 261 | _this._onAfterAddingFile(fileItem); 262 | next(); 263 | }; 264 | pipeline.onThrown = onThrown; 265 | pipeline.onSuccessful = onSuccessful; 266 | pipeline.exec(fileLikeObject, options); 267 | }; 268 | 269 | var done = function done() { 270 | if (_this.queue.length !== count) { 271 | _this._onAfterAddingAll(addedFileItems); 272 | _this.progress = _this._getTotalProgress(); 273 | } 274 | 275 | _this._render(); 276 | if (_this.autoUpload) _this.uploadAll(); 277 | }; 278 | 279 | next(); 280 | }; 281 | /** 282 | * Remove items from the queue. Remove last: index = -1 283 | * @param {FileItem|Number} value 284 | */ 285 | 286 | 287 | FileUploader.prototype.removeFromQueue = function removeFromQueue(value) { 288 | var index = this.getIndexOfItem(value); 289 | var item = this.queue[index]; 290 | if (item.isUploading) item.cancel(); 291 | this.queue.splice(index, 1); 292 | item._destroy(); 293 | this.progress = this._getTotalProgress(); 294 | }; 295 | /** 296 | * Clears the queue 297 | */ 298 | 299 | 300 | FileUploader.prototype.clearQueue = function clearQueue() { 301 | while (this.queue.length) { 302 | this.queue[0].remove(); 303 | } 304 | this.progress = 0; 305 | }; 306 | /** 307 | * Uploads a item from the queue 308 | * @param {FileItem|Number} value 309 | */ 310 | 311 | 312 | FileUploader.prototype.uploadItem = function uploadItem(value) { 313 | var index = this.getIndexOfItem(value); 314 | var item = this.queue[index]; 315 | var transport = this.isHTML5 ? '_xhrTransport' : '_iframeTransport'; 316 | 317 | item._prepareToUploading(); 318 | if (this.isUploading) return; 319 | 320 | this._onBeforeUploadItem(item); 321 | if (item.isCancel) return; 322 | 323 | item.isUploading = true; 324 | this.isUploading = true; 325 | this[transport](item); 326 | this._render(); 327 | }; 328 | /** 329 | * Cancels uploading of item from the queue 330 | * @param {FileItem|Number} value 331 | */ 332 | 333 | 334 | FileUploader.prototype.cancelItem = function cancelItem(value) { 335 | var _this2 = this; 336 | 337 | var index = this.getIndexOfItem(value); 338 | var item = this.queue[index]; 339 | var prop = this.isHTML5 ? '_xhr' : '_form'; 340 | if (!item) return; 341 | item.isCancel = true; 342 | if (item.isUploading) { 343 | // It will call this._onCancelItem() & this._onCompleteItem() asynchronously 344 | item[prop].abort(); 345 | } else { 346 | var dummy = [undefined, 0, {}]; 347 | var onNextTick = function onNextTick() { 348 | _this2._onCancelItem.apply(_this2, [item].concat(dummy)); 349 | _this2._onCompleteItem.apply(_this2, [item].concat(dummy)); 350 | }; 351 | $timeout(onNextTick); // Trigger callbacks asynchronously (setImmediate emulation) 352 | } 353 | }; 354 | /** 355 | * Uploads all not uploaded items of queue 356 | */ 357 | 358 | 359 | FileUploader.prototype.uploadAll = function uploadAll() { 360 | var items = this.getNotUploadedItems().filter(function (item) { 361 | return !item.isUploading; 362 | }); 363 | if (!items.length) return; 364 | 365 | forEach(items, function (item) { 366 | return item._prepareToUploading(); 367 | }); 368 | items[0].upload(); 369 | }; 370 | /** 371 | * Cancels all uploads 372 | */ 373 | 374 | 375 | FileUploader.prototype.cancelAll = function cancelAll() { 376 | var items = this.getNotUploadedItems(); 377 | forEach(items, function (item) { 378 | return item.cancel(); 379 | }); 380 | }; 381 | /** 382 | * Returns "true" if value an instance of File 383 | * @param {*} value 384 | * @returns {Boolean} 385 | * @private 386 | */ 387 | 388 | 389 | FileUploader.prototype.isFile = function isFile(value) { 390 | return this.constructor.isFile(value); 391 | }; 392 | /** 393 | * Returns "true" if value an instance of FileLikeObject 394 | * @param {*} value 395 | * @returns {Boolean} 396 | * @private 397 | */ 398 | 399 | 400 | FileUploader.prototype.isFileLikeObject = function isFileLikeObject(value) { 401 | return this.constructor.isFileLikeObject(value); 402 | }; 403 | /** 404 | * Returns "true" if value is array like object 405 | * @param {*} value 406 | * @returns {Boolean} 407 | */ 408 | 409 | 410 | FileUploader.prototype.isArrayLikeObject = function isArrayLikeObject(value) { 411 | return this.constructor.isArrayLikeObject(value); 412 | }; 413 | /** 414 | * Returns a index of item from the queue 415 | * @param {Item|Number} value 416 | * @returns {Number} 417 | */ 418 | 419 | 420 | FileUploader.prototype.getIndexOfItem = function getIndexOfItem(value) { 421 | return isNumber(value) ? value : this.queue.indexOf(value); 422 | }; 423 | /** 424 | * Returns not uploaded items 425 | * @returns {Array} 426 | */ 427 | 428 | 429 | FileUploader.prototype.getNotUploadedItems = function getNotUploadedItems() { 430 | return this.queue.filter(function (item) { 431 | return !item.isUploaded; 432 | }); 433 | }; 434 | /** 435 | * Returns items ready for upload 436 | * @returns {Array} 437 | */ 438 | 439 | 440 | FileUploader.prototype.getReadyItems = function getReadyItems() { 441 | return this.queue.filter(function (item) { 442 | return item.isReady && !item.isUploading; 443 | }).sort(function (item1, item2) { 444 | return item1.index - item2.index; 445 | }); 446 | }; 447 | /** 448 | * Destroys instance of FileUploader 449 | */ 450 | 451 | 452 | FileUploader.prototype.destroy = function destroy() { 453 | var _this3 = this; 454 | 455 | forEach(this._directives, function (key) { 456 | forEach(_this3._directives[key], function (object) { 457 | object.destroy(); 458 | }); 459 | }); 460 | }; 461 | /** 462 | * Callback 463 | * @param {Array} fileItems 464 | */ 465 | 466 | 467 | FileUploader.prototype.onAfterAddingAll = function onAfterAddingAll(fileItems) {}; 468 | /** 469 | * Callback 470 | * @param {FileItem} fileItem 471 | */ 472 | 473 | 474 | FileUploader.prototype.onAfterAddingFile = function onAfterAddingFile(fileItem) {}; 475 | /** 476 | * Callback 477 | * @param {File|Object} item 478 | * @param {Object} filter 479 | * @param {Object} options 480 | */ 481 | 482 | 483 | FileUploader.prototype.onWhenAddingFileFailed = function onWhenAddingFileFailed(item, filter, options) {}; 484 | /** 485 | * Callback 486 | * @param {FileItem} fileItem 487 | */ 488 | 489 | 490 | FileUploader.prototype.onBeforeUploadItem = function onBeforeUploadItem(fileItem) {}; 491 | /** 492 | * Callback 493 | * @param {FileItem} fileItem 494 | * @param {Number} progress 495 | */ 496 | 497 | 498 | FileUploader.prototype.onProgressItem = function onProgressItem(fileItem, progress) {}; 499 | /** 500 | * Callback 501 | * @param {Number} progress 502 | */ 503 | 504 | 505 | FileUploader.prototype.onProgressAll = function onProgressAll(progress) {}; 506 | /** 507 | * Callback 508 | * @param {FileItem} item 509 | * @param {*} response 510 | * @param {Number} status 511 | * @param {Object} headers 512 | */ 513 | 514 | 515 | FileUploader.prototype.onSuccessItem = function onSuccessItem(item, response, status, headers) {}; 516 | /** 517 | * Callback 518 | * @param {FileItem} item 519 | * @param {*} response 520 | * @param {Number} status 521 | * @param {Object} headers 522 | */ 523 | 524 | 525 | FileUploader.prototype.onErrorItem = function onErrorItem(item, response, status, headers) {}; 526 | /** 527 | * Callback 528 | * @param {FileItem} item 529 | * @param {*} response 530 | * @param {Number} status 531 | * @param {Object} headers 532 | */ 533 | 534 | 535 | FileUploader.prototype.onCancelItem = function onCancelItem(item, response, status, headers) {}; 536 | /** 537 | * Callback 538 | * @param {FileItem} item 539 | * @param {*} response 540 | * @param {Number} status 541 | * @param {Object} headers 542 | */ 543 | 544 | 545 | FileUploader.prototype.onCompleteItem = function onCompleteItem(item, response, status, headers) {}; 546 | /** 547 | * Callback 548 | * @param {FileItem} item 549 | */ 550 | 551 | 552 | FileUploader.prototype.onTimeoutItem = function onTimeoutItem(item) {}; 553 | /** 554 | * Callback 555 | */ 556 | 557 | 558 | FileUploader.prototype.onCompleteAll = function onCompleteAll() {}; 559 | /********************** 560 | * PRIVATE 561 | **********************/ 562 | /** 563 | * Returns the total progress 564 | * @param {Number} [value] 565 | * @returns {Number} 566 | * @private 567 | */ 568 | 569 | 570 | FileUploader.prototype._getTotalProgress = function _getTotalProgress(value) { 571 | if (this.removeAfterUpload) return value || 0; 572 | 573 | var notUploaded = this.getNotUploadedItems().length; 574 | var uploaded = notUploaded ? this.queue.length - notUploaded : this.queue.length; 575 | var ratio = 100 / this.queue.length; 576 | var current = (value || 0) * ratio / 100; 577 | 578 | return Math.round(uploaded * ratio + current); 579 | }; 580 | /** 581 | * Returns array of filters 582 | * @param {Array|String} filters 583 | * @returns {Array} 584 | * @private 585 | */ 586 | 587 | 588 | FileUploader.prototype._getFilters = function _getFilters(filters) { 589 | if (!filters) return this.filters; 590 | if (isArray(filters)) return filters; 591 | var names = filters.match(/[^\s,]+/g); 592 | return this.filters.filter(function (filter) { 593 | return names.indexOf(filter.name) !== -1; 594 | }); 595 | }; 596 | /** 597 | * @param {Array} filters 598 | * @returns {Array} 599 | * @private 600 | */ 601 | 602 | 603 | FileUploader.prototype._convertFiltersToPipes = function _convertFiltersToPipes(filters) { 604 | var _this4 = this; 605 | 606 | return filters.map(function (filter) { 607 | var fn = bind(_this4, filter.fn); 608 | fn.isAsync = filter.fn.length === 3; 609 | fn.originalFilter = filter; 610 | return fn; 611 | }); 612 | }; 613 | /** 614 | * Updates html 615 | * @private 616 | */ 617 | 618 | 619 | FileUploader.prototype._render = function _render() { 620 | if (!$rootScope.$$phase) $rootScope.$apply(); 621 | }; 622 | /** 623 | * Returns "true" if item is a file (not folder) 624 | * @param {File|FileLikeObject} item 625 | * @returns {Boolean} 626 | * @private 627 | */ 628 | 629 | 630 | FileUploader.prototype._folderFilter = function _folderFilter(item) { 631 | return !!(item.size || item.type); 632 | }; 633 | /** 634 | * Returns "true" if the limit has not been reached 635 | * @returns {Boolean} 636 | * @private 637 | */ 638 | 639 | 640 | FileUploader.prototype._queueLimitFilter = function _queueLimitFilter() { 641 | return this.queue.length < this.queueLimit; 642 | }; 643 | /** 644 | * Checks whether upload successful 645 | * @param {Number} status 646 | * @returns {Boolean} 647 | * @private 648 | */ 649 | 650 | 651 | FileUploader.prototype._isSuccessCode = function _isSuccessCode(status) { 652 | return status >= 200 && status < 300 || status === 304; 653 | }; 654 | /** 655 | * Transforms the server response 656 | * @param {*} response 657 | * @param {Object} headers 658 | * @returns {*} 659 | * @private 660 | */ 661 | 662 | 663 | FileUploader.prototype._transformResponse = function _transformResponse(response, headers) { 664 | var headersGetter = this._headersGetter(headers); 665 | forEach($http.defaults.transformResponse, function (transformFn) { 666 | response = transformFn(response, headersGetter); 667 | }); 668 | return response; 669 | }; 670 | /** 671 | * Parsed response headers 672 | * @param headers 673 | * @returns {Object} 674 | * @see https://github.com/angular/angular.js/blob/master/src/ng/http.js 675 | * @private 676 | */ 677 | 678 | 679 | FileUploader.prototype._parseHeaders = function _parseHeaders(headers) { 680 | var parsed = {}, 681 | key, 682 | val, 683 | i; 684 | 685 | if (!headers) return parsed; 686 | 687 | forEach(headers.split('\n'), function (line) { 688 | i = line.indexOf(':'); 689 | key = line.slice(0, i).trim().toLowerCase(); 690 | val = line.slice(i + 1).trim(); 691 | 692 | if (key) { 693 | parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; 694 | } 695 | }); 696 | 697 | return parsed; 698 | }; 699 | /** 700 | * Returns function that returns headers 701 | * @param {Object} parsedHeaders 702 | * @returns {Function} 703 | * @private 704 | */ 705 | 706 | 707 | FileUploader.prototype._headersGetter = function _headersGetter(parsedHeaders) { 708 | return function (name) { 709 | if (name) { 710 | return parsedHeaders[name.toLowerCase()] || null; 711 | } 712 | return parsedHeaders; 713 | }; 714 | }; 715 | /** 716 | * The XMLHttpRequest transport 717 | * @param {FileItem} item 718 | * @private 719 | */ 720 | 721 | 722 | FileUploader.prototype._xhrTransport = function _xhrTransport(item) { 723 | var _this5 = this; 724 | 725 | var xhr = item._xhr = new XMLHttpRequest(); 726 | var sendable; 727 | 728 | if (!item.disableMultipart) { 729 | sendable = new FormData(); 730 | forEach(item.formData, function (obj) { 731 | forEach(obj, function (value, key) { 732 | sendable.append(key, value); 733 | }); 734 | }); 735 | 736 | sendable.append(item.alias, item._file, item.file.name); 737 | } else { 738 | sendable = item._file; 739 | } 740 | 741 | if (typeof item._file.size != 'number') { 742 | throw new TypeError('The file specified is no longer valid'); 743 | } 744 | 745 | xhr.upload.onprogress = function (event) { 746 | var progress = Math.round(event.lengthComputable ? event.loaded * 100 / event.total : 0); 747 | _this5._onProgressItem(item, progress); 748 | }; 749 | 750 | xhr.onload = function () { 751 | var headers = _this5._parseHeaders(xhr.getAllResponseHeaders()); 752 | var response = _this5._transformResponse(xhr.response, headers); 753 | var gist = _this5._isSuccessCode(xhr.status) ? 'Success' : 'Error'; 754 | var method = '_on' + gist + 'Item'; 755 | _this5[method](item, response, xhr.status, headers); 756 | _this5._onCompleteItem(item, response, xhr.status, headers); 757 | }; 758 | 759 | xhr.onerror = function () { 760 | var headers = _this5._parseHeaders(xhr.getAllResponseHeaders()); 761 | var response = _this5._transformResponse(xhr.response, headers); 762 | _this5._onErrorItem(item, response, xhr.status, headers); 763 | _this5._onCompleteItem(item, response, xhr.status, headers); 764 | }; 765 | 766 | xhr.onabort = function () { 767 | var headers = _this5._parseHeaders(xhr.getAllResponseHeaders()); 768 | var response = _this5._transformResponse(xhr.response, headers); 769 | _this5._onCancelItem(item, response, xhr.status, headers); 770 | _this5._onCompleteItem(item, response, xhr.status, headers); 771 | }; 772 | 773 | xhr.ontimeout = function (e) { 774 | var headers = _this5._parseHeaders(xhr.getAllResponseHeaders()); 775 | var response = "Request Timeout."; 776 | _this5._onTimeoutItem(item); 777 | _this5._onCompleteItem(item, response, 408, headers); 778 | }; 779 | 780 | xhr.open(item.method, item.url, true); 781 | 782 | xhr.timeout = item.timeout || 0; 783 | xhr.withCredentials = item.withCredentials; 784 | 785 | forEach(item.headers, function (value, name) { 786 | xhr.setRequestHeader(name, value); 787 | }); 788 | 789 | xhr.send(sendable); 790 | }; 791 | /** 792 | * The IFrame transport 793 | * @param {FileItem} item 794 | * @private 795 | */ 796 | 797 | 798 | FileUploader.prototype._iframeTransport = function _iframeTransport(item) { 799 | var _this6 = this; 800 | 801 | var form = element(''); 802 | var iframe = element(''); 803 | var input = item._input; 804 | 805 | var timeout = 0; 806 | var timer = null; 807 | var isTimedOut = false; 808 | 809 | if (item._form) item._form.replaceWith(input); // remove old form 810 | item._form = form; // save link to new form 811 | 812 | input.prop('name', item.alias); 813 | 814 | forEach(item.formData, function (obj) { 815 | forEach(obj, function (value, key) { 816 | var element_ = element(''); 817 | element_.val(value); 818 | form.append(element_); 819 | }); 820 | }); 821 | 822 | form.prop({ 823 | action: item.url, 824 | method: 'POST', 825 | target: iframe.prop('name'), 826 | enctype: 'multipart/form-data', 827 | encoding: 'multipart/form-data' // old IE 828 | }); 829 | 830 | iframe.bind('load', function () { 831 | var html = ''; 832 | var status = 200; 833 | 834 | try { 835 | // Fix for legacy IE browsers that loads internal error page 836 | // when failed WS response received. In consequence iframe 837 | // content access denied error is thrown becouse trying to 838 | // access cross domain page. When such thing occurs notifying 839 | // with empty response object. See more info at: 840 | // http://stackoverflow.com/questions/151362/access-is-denied-error-on-accessing-iframe-document-object 841 | // Note that if non standard 4xx or 5xx error code returned 842 | // from WS then response content can be accessed without error 843 | // but 'XHR' status becomes 200. In order to avoid confusion 844 | // returning response via same 'success' event handler. 845 | 846 | // fixed angular.contents() for iframes 847 | html = iframe[0].contentDocument.body.innerHTML; 848 | } catch (e) { 849 | // in case we run into the access-is-denied error or we have another error on the server side 850 | // (intentional 500,40... errors), we at least say 'something went wrong' -> 500 851 | status = 500; 852 | } 853 | 854 | if (timer) { 855 | clearTimeout(timer); 856 | } 857 | timer = null; 858 | 859 | if (isTimedOut) { 860 | return false; //throw 'Request Timeout' 861 | } 862 | 863 | var xhr = { response: html, status: status, dummy: true }; 864 | var headers = {}; 865 | var response = _this6._transformResponse(xhr.response, headers); 866 | 867 | _this6._onSuccessItem(item, response, xhr.status, headers); 868 | _this6._onCompleteItem(item, response, xhr.status, headers); 869 | }); 870 | 871 | form.abort = function () { 872 | var xhr = { status: 0, dummy: true }; 873 | var headers = {}; 874 | var response; 875 | 876 | iframe.unbind('load').prop('src', 'javascript:false;'); 877 | form.replaceWith(input); 878 | 879 | _this6._onCancelItem(item, response, xhr.status, headers); 880 | _this6._onCompleteItem(item, response, xhr.status, headers); 881 | }; 882 | 883 | input.after(form); 884 | form.append(input).append(iframe); 885 | 886 | timeout = item.timeout || 0; 887 | timer = null; 888 | 889 | if (timeout) { 890 | timer = setTimeout(function () { 891 | isTimedOut = true; 892 | 893 | item.isCancel = true; 894 | if (item.isUploading) { 895 | iframe.unbind('load').prop('src', 'javascript:false;'); 896 | form.replaceWith(input); 897 | } 898 | 899 | var headers = {}; 900 | var response = "Request Timeout."; 901 | _this6._onTimeoutItem(item); 902 | _this6._onCompleteItem(item, response, 408, headers); 903 | }, timeout); 904 | } 905 | 906 | form[0].submit(); 907 | }; 908 | /** 909 | * Inner callback 910 | * @param {File|Object} item 911 | * @param {Object} filter 912 | * @param {Object} options 913 | * @private 914 | */ 915 | 916 | 917 | FileUploader.prototype._onWhenAddingFileFailed = function _onWhenAddingFileFailed(item, filter, options) { 918 | this.onWhenAddingFileFailed(item, filter, options); 919 | }; 920 | /** 921 | * Inner callback 922 | * @param {FileItem} item 923 | */ 924 | 925 | 926 | FileUploader.prototype._onAfterAddingFile = function _onAfterAddingFile(item) { 927 | this.onAfterAddingFile(item); 928 | }; 929 | /** 930 | * Inner callback 931 | * @param {Array} items 932 | */ 933 | 934 | 935 | FileUploader.prototype._onAfterAddingAll = function _onAfterAddingAll(items) { 936 | this.onAfterAddingAll(items); 937 | }; 938 | /** 939 | * Inner callback 940 | * @param {FileItem} item 941 | * @private 942 | */ 943 | 944 | 945 | FileUploader.prototype._onBeforeUploadItem = function _onBeforeUploadItem(item) { 946 | item._onBeforeUpload(); 947 | this.onBeforeUploadItem(item); 948 | }; 949 | /** 950 | * Inner callback 951 | * @param {FileItem} item 952 | * @param {Number} progress 953 | * @private 954 | */ 955 | 956 | 957 | FileUploader.prototype._onProgressItem = function _onProgressItem(item, progress) { 958 | var total = this._getTotalProgress(progress); 959 | this.progress = total; 960 | item._onProgress(progress); 961 | this.onProgressItem(item, progress); 962 | this.onProgressAll(total); 963 | this._render(); 964 | }; 965 | /** 966 | * Inner callback 967 | * @param {FileItem} item 968 | * @param {*} response 969 | * @param {Number} status 970 | * @param {Object} headers 971 | * @private 972 | */ 973 | 974 | 975 | FileUploader.prototype._onSuccessItem = function _onSuccessItem(item, response, status, headers) { 976 | item._onSuccess(response, status, headers); 977 | this.onSuccessItem(item, response, status, headers); 978 | }; 979 | /** 980 | * Inner callback 981 | * @param {FileItem} item 982 | * @param {*} response 983 | * @param {Number} status 984 | * @param {Object} headers 985 | * @private 986 | */ 987 | 988 | 989 | FileUploader.prototype._onErrorItem = function _onErrorItem(item, response, status, headers) { 990 | item._onError(response, status, headers); 991 | this.onErrorItem(item, response, status, headers); 992 | }; 993 | /** 994 | * Inner callback 995 | * @param {FileItem} item 996 | * @param {*} response 997 | * @param {Number} status 998 | * @param {Object} headers 999 | * @private 1000 | */ 1001 | 1002 | 1003 | FileUploader.prototype._onCancelItem = function _onCancelItem(item, response, status, headers) { 1004 | item._onCancel(response, status, headers); 1005 | this.onCancelItem(item, response, status, headers); 1006 | }; 1007 | /** 1008 | * Inner callback 1009 | * @param {FileItem} item 1010 | * @param {*} response 1011 | * @param {Number} status 1012 | * @param {Object} headers 1013 | * @private 1014 | */ 1015 | 1016 | 1017 | FileUploader.prototype._onCompleteItem = function _onCompleteItem(item, response, status, headers) { 1018 | item._onComplete(response, status, headers); 1019 | this.onCompleteItem(item, response, status, headers); 1020 | 1021 | var nextItem = this.getReadyItems()[0]; 1022 | this.isUploading = false; 1023 | 1024 | if (isDefined(nextItem)) { 1025 | nextItem.upload(); 1026 | return; 1027 | } 1028 | 1029 | this.onCompleteAll(); 1030 | this.progress = this._getTotalProgress(); 1031 | this._render(); 1032 | }; 1033 | /** 1034 | * Inner callback 1035 | * @param {FileItem} item 1036 | * @private 1037 | */ 1038 | 1039 | 1040 | FileUploader.prototype._onTimeoutItem = function _onTimeoutItem(item) { 1041 | item._onTimeout(); 1042 | this.onTimeoutItem(item); 1043 | }; 1044 | /********************** 1045 | * STATIC 1046 | **********************/ 1047 | /** 1048 | * Returns "true" if value an instance of File 1049 | * @param {*} value 1050 | * @returns {Boolean} 1051 | * @private 1052 | */ 1053 | 1054 | 1055 | FileUploader.isFile = function isFile(value) { 1056 | return File && value instanceof File; 1057 | }; 1058 | /** 1059 | * Returns "true" if value an instance of FileLikeObject 1060 | * @param {*} value 1061 | * @returns {Boolean} 1062 | * @private 1063 | */ 1064 | 1065 | 1066 | FileUploader.isFileLikeObject = function isFileLikeObject(value) { 1067 | return value instanceof FileLikeObject; 1068 | }; 1069 | /** 1070 | * Returns "true" if value is array like object 1071 | * @param {*} value 1072 | * @returns {Boolean} 1073 | */ 1074 | 1075 | 1076 | FileUploader.isArrayLikeObject = function isArrayLikeObject(value) { 1077 | return isObject(value) && 'length' in value; 1078 | }; 1079 | /** 1080 | * Inherits a target (Class_1) by a source (Class_2) 1081 | * @param {Function} target 1082 | * @param {Function} source 1083 | */ 1084 | 1085 | 1086 | FileUploader.inherit = function inherit(target, source) { 1087 | target.prototype = Object.create(source.prototype); 1088 | target.prototype.constructor = target; 1089 | target.super_ = source; 1090 | }; 1091 | 1092 | return FileUploader; 1093 | }(); 1094 | 1095 | /********************** 1096 | * PUBLIC 1097 | **********************/ 1098 | /** 1099 | * Checks a support the html5 uploader 1100 | * @returns {Boolean} 1101 | * @readonly 1102 | */ 1103 | 1104 | 1105 | FileUploader.prototype.isHTML5 = !!(File && FormData); 1106 | /********************** 1107 | * STATIC 1108 | **********************/ 1109 | /** 1110 | * @borrows FileUploader.prototype.isHTML5 1111 | */ 1112 | FileUploader.isHTML5 = FileUploader.prototype.isHTML5; 1113 | 1114 | return FileUploader; 1115 | } 1116 | 1117 | __identity.$inject = ['fileUploaderOptions', '$rootScope', '$http', '$window', '$timeout', 'FileLikeObject', 'FileItem', 'Pipeline']; 1118 | 1119 | /***/ }), 1120 | /* 4 */ 1121 | /***/ (function(module, exports, __webpack_require__) { 1122 | 1123 | 'use strict'; 1124 | 1125 | Object.defineProperty(exports, "__esModule", { 1126 | value: true 1127 | }); 1128 | exports.default = __identity; 1129 | 1130 | var _config = __webpack_require__(1); 1131 | 1132 | var _config2 = _interopRequireDefault(_config); 1133 | 1134 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 1135 | 1136 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 1137 | 1138 | var _angular = angular, 1139 | copy = _angular.copy, 1140 | isElement = _angular.isElement, 1141 | isString = _angular.isString; 1142 | function __identity() { 1143 | 1144 | return function () { 1145 | /** 1146 | * Creates an instance of FileLikeObject 1147 | * @param {File|HTMLInputElement|Object} fileOrInput 1148 | * @constructor 1149 | */ 1150 | function FileLikeObject(fileOrInput) { 1151 | _classCallCheck(this, FileLikeObject); 1152 | 1153 | var isInput = isElement(fileOrInput); 1154 | var fakePathOrObject = isInput ? fileOrInput.value : fileOrInput; 1155 | var postfix = isString(fakePathOrObject) ? 'FakePath' : 'Object'; 1156 | var method = '_createFrom' + postfix; 1157 | this[method](fakePathOrObject, fileOrInput); 1158 | } 1159 | /** 1160 | * Creates file like object from fake path string 1161 | * @param {String} path 1162 | * @private 1163 | */ 1164 | 1165 | 1166 | FileLikeObject.prototype._createFromFakePath = function _createFromFakePath(path, input) { 1167 | this.lastModifiedDate = null; 1168 | this.size = null; 1169 | this.type = 'like/' + path.slice(path.lastIndexOf('.') + 1).toLowerCase(); 1170 | this.name = path.slice(path.lastIndexOf('/') + path.lastIndexOf('\\') + 2); 1171 | this.input = input; 1172 | }; 1173 | /** 1174 | * Creates file like object from object 1175 | * @param {File|FileLikeObject} object 1176 | * @private 1177 | */ 1178 | 1179 | 1180 | FileLikeObject.prototype._createFromObject = function _createFromObject(object) { 1181 | this.lastModifiedDate = copy(object.lastModifiedDate); 1182 | this.size = object.size; 1183 | this.type = object.type; 1184 | this.name = object.name; 1185 | this.input = object.input; 1186 | }; 1187 | 1188 | return FileLikeObject; 1189 | }(); 1190 | } 1191 | 1192 | /***/ }), 1193 | /* 5 */ 1194 | /***/ (function(module, exports, __webpack_require__) { 1195 | 1196 | 'use strict'; 1197 | 1198 | Object.defineProperty(exports, "__esModule", { 1199 | value: true 1200 | }); 1201 | exports.default = __identity; 1202 | 1203 | var _config = __webpack_require__(1); 1204 | 1205 | var _config2 = _interopRequireDefault(_config); 1206 | 1207 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 1208 | 1209 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 1210 | 1211 | var _angular = angular, 1212 | copy = _angular.copy, 1213 | extend = _angular.extend, 1214 | element = _angular.element, 1215 | isElement = _angular.isElement; 1216 | function __identity($compile, FileLikeObject) { 1217 | 1218 | return function () { 1219 | /** 1220 | * Creates an instance of FileItem 1221 | * @param {FileUploader} uploader 1222 | * @param {File|HTMLInputElement|Object} some 1223 | * @param {Object} options 1224 | * @constructor 1225 | */ 1226 | function FileItem(uploader, some, options) { 1227 | _classCallCheck(this, FileItem); 1228 | 1229 | var isInput = !!some.input; 1230 | var input = isInput ? element(some.input) : null; 1231 | var file = !isInput ? some : null; 1232 | 1233 | extend(this, { 1234 | url: uploader.url, 1235 | alias: uploader.alias, 1236 | headers: copy(uploader.headers), 1237 | formData: copy(uploader.formData), 1238 | removeAfterUpload: uploader.removeAfterUpload, 1239 | withCredentials: uploader.withCredentials, 1240 | disableMultipart: uploader.disableMultipart, 1241 | method: uploader.method, 1242 | timeout: uploader.timeout 1243 | }, options, { 1244 | uploader: uploader, 1245 | file: new FileLikeObject(some), 1246 | isReady: false, 1247 | isUploading: false, 1248 | isUploaded: false, 1249 | isSuccess: false, 1250 | isCancel: false, 1251 | isError: false, 1252 | progress: 0, 1253 | index: null, 1254 | _file: file, 1255 | _input: input 1256 | }); 1257 | 1258 | if (input) this._replaceNode(input); 1259 | } 1260 | /********************** 1261 | * PUBLIC 1262 | **********************/ 1263 | /** 1264 | * Uploads a FileItem 1265 | */ 1266 | 1267 | 1268 | FileItem.prototype.upload = function upload() { 1269 | try { 1270 | this.uploader.uploadItem(this); 1271 | } catch (e) { 1272 | var message = e.name + ':' + e.message; 1273 | this.uploader._onCompleteItem(this, message, e.code, []); 1274 | this.uploader._onErrorItem(this, message, e.code, []); 1275 | } 1276 | }; 1277 | /** 1278 | * Cancels uploading of FileItem 1279 | */ 1280 | 1281 | 1282 | FileItem.prototype.cancel = function cancel() { 1283 | this.uploader.cancelItem(this); 1284 | }; 1285 | /** 1286 | * Removes a FileItem 1287 | */ 1288 | 1289 | 1290 | FileItem.prototype.remove = function remove() { 1291 | this.uploader.removeFromQueue(this); 1292 | }; 1293 | /** 1294 | * Callback 1295 | * @private 1296 | */ 1297 | 1298 | 1299 | FileItem.prototype.onBeforeUpload = function onBeforeUpload() {}; 1300 | /** 1301 | * Callback 1302 | * @param {Number} progress 1303 | * @private 1304 | */ 1305 | 1306 | 1307 | FileItem.prototype.onProgress = function onProgress(progress) {}; 1308 | /** 1309 | * Callback 1310 | * @param {*} response 1311 | * @param {Number} status 1312 | * @param {Object} headers 1313 | */ 1314 | 1315 | 1316 | FileItem.prototype.onSuccess = function onSuccess(response, status, headers) {}; 1317 | /** 1318 | * Callback 1319 | * @param {*} response 1320 | * @param {Number} status 1321 | * @param {Object} headers 1322 | */ 1323 | 1324 | 1325 | FileItem.prototype.onError = function onError(response, status, headers) {}; 1326 | /** 1327 | * Callback 1328 | * @param {*} response 1329 | * @param {Number} status 1330 | * @param {Object} headers 1331 | */ 1332 | 1333 | 1334 | FileItem.prototype.onCancel = function onCancel(response, status, headers) {}; 1335 | /** 1336 | * Callback 1337 | * @param {*} response 1338 | * @param {Number} status 1339 | * @param {Object} headers 1340 | */ 1341 | 1342 | 1343 | FileItem.prototype.onComplete = function onComplete(response, status, headers) {}; 1344 | /** 1345 | * Callback 1346 | */ 1347 | 1348 | 1349 | FileItem.prototype.onTimeout = function onTimeout() {}; 1350 | /********************** 1351 | * PRIVATE 1352 | **********************/ 1353 | /** 1354 | * Inner callback 1355 | */ 1356 | 1357 | 1358 | FileItem.prototype._onBeforeUpload = function _onBeforeUpload() { 1359 | this.isReady = true; 1360 | this.isUploading = false; 1361 | this.isUploaded = false; 1362 | this.isSuccess = false; 1363 | this.isCancel = false; 1364 | this.isError = false; 1365 | this.progress = 0; 1366 | this.onBeforeUpload(); 1367 | }; 1368 | /** 1369 | * Inner callback 1370 | * @param {Number} progress 1371 | * @private 1372 | */ 1373 | 1374 | 1375 | FileItem.prototype._onProgress = function _onProgress(progress) { 1376 | this.progress = progress; 1377 | this.onProgress(progress); 1378 | }; 1379 | /** 1380 | * Inner callback 1381 | * @param {*} response 1382 | * @param {Number} status 1383 | * @param {Object} headers 1384 | * @private 1385 | */ 1386 | 1387 | 1388 | FileItem.prototype._onSuccess = function _onSuccess(response, status, headers) { 1389 | this.isReady = false; 1390 | this.isUploading = false; 1391 | this.isUploaded = true; 1392 | this.isSuccess = true; 1393 | this.isCancel = false; 1394 | this.isError = false; 1395 | this.progress = 100; 1396 | this.index = null; 1397 | this.onSuccess(response, status, headers); 1398 | }; 1399 | /** 1400 | * Inner callback 1401 | * @param {*} response 1402 | * @param {Number} status 1403 | * @param {Object} headers 1404 | * @private 1405 | */ 1406 | 1407 | 1408 | FileItem.prototype._onError = function _onError(response, status, headers) { 1409 | this.isReady = false; 1410 | this.isUploading = false; 1411 | this.isUploaded = true; 1412 | this.isSuccess = false; 1413 | this.isCancel = false; 1414 | this.isError = true; 1415 | this.progress = 0; 1416 | this.index = null; 1417 | this.onError(response, status, headers); 1418 | }; 1419 | /** 1420 | * Inner callback 1421 | * @param {*} response 1422 | * @param {Number} status 1423 | * @param {Object} headers 1424 | * @private 1425 | */ 1426 | 1427 | 1428 | FileItem.prototype._onCancel = function _onCancel(response, status, headers) { 1429 | this.isReady = false; 1430 | this.isUploading = false; 1431 | this.isUploaded = false; 1432 | this.isSuccess = false; 1433 | this.isCancel = true; 1434 | this.isError = false; 1435 | this.progress = 0; 1436 | this.index = null; 1437 | this.onCancel(response, status, headers); 1438 | }; 1439 | /** 1440 | * Inner callback 1441 | * @param {*} response 1442 | * @param {Number} status 1443 | * @param {Object} headers 1444 | * @private 1445 | */ 1446 | 1447 | 1448 | FileItem.prototype._onComplete = function _onComplete(response, status, headers) { 1449 | this.onComplete(response, status, headers); 1450 | if (this.removeAfterUpload) this.remove(); 1451 | }; 1452 | /** 1453 | * Inner callback 1454 | * @private 1455 | */ 1456 | 1457 | 1458 | FileItem.prototype._onTimeout = function _onTimeout() { 1459 | this.isReady = false; 1460 | this.isUploading = false; 1461 | this.isUploaded = false; 1462 | this.isSuccess = false; 1463 | this.isCancel = false; 1464 | this.isError = true; 1465 | this.progress = 0; 1466 | this.index = null; 1467 | this.onTimeout(); 1468 | }; 1469 | /** 1470 | * Destroys a FileItem 1471 | */ 1472 | 1473 | 1474 | FileItem.prototype._destroy = function _destroy() { 1475 | if (this._input) this._input.remove(); 1476 | if (this._form) this._form.remove(); 1477 | delete this._form; 1478 | delete this._input; 1479 | }; 1480 | /** 1481 | * Prepares to uploading 1482 | * @private 1483 | */ 1484 | 1485 | 1486 | FileItem.prototype._prepareToUploading = function _prepareToUploading() { 1487 | this.index = this.index || ++this.uploader._nextIndex; 1488 | this.isReady = true; 1489 | }; 1490 | /** 1491 | * Replaces input element on his clone 1492 | * @param {JQLite|jQuery} input 1493 | * @private 1494 | */ 1495 | 1496 | 1497 | FileItem.prototype._replaceNode = function _replaceNode(input) { 1498 | var clone = $compile(input.clone())(input.scope()); 1499 | clone.prop('value', null); // FF fix 1500 | input.css('display', 'none'); 1501 | input.after(clone); // remove jquery dependency 1502 | }; 1503 | 1504 | return FileItem; 1505 | }(); 1506 | } 1507 | 1508 | __identity.$inject = ['$compile', 'FileLikeObject']; 1509 | 1510 | /***/ }), 1511 | /* 6 */ 1512 | /***/ (function(module, exports, __webpack_require__) { 1513 | 1514 | 'use strict'; 1515 | 1516 | Object.defineProperty(exports, "__esModule", { 1517 | value: true 1518 | }); 1519 | exports.default = __identity; 1520 | 1521 | var _config = __webpack_require__(1); 1522 | 1523 | var _config2 = _interopRequireDefault(_config); 1524 | 1525 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 1526 | 1527 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 1528 | 1529 | var _angular = angular, 1530 | extend = _angular.extend; 1531 | function __identity() { 1532 | var FileDirective = function () { 1533 | /** 1534 | * Creates instance of {FileDirective} object 1535 | * @param {Object} options 1536 | * @param {Object} options.uploader 1537 | * @param {HTMLElement} options.element 1538 | * @param {Object} options.events 1539 | * @param {String} options.prop 1540 | * @constructor 1541 | */ 1542 | function FileDirective(options) { 1543 | _classCallCheck(this, FileDirective); 1544 | 1545 | extend(this, options); 1546 | this.uploader._directives[this.prop].push(this); 1547 | this._saveLinks(); 1548 | this.bind(); 1549 | } 1550 | /** 1551 | * Binds events handles 1552 | */ 1553 | 1554 | 1555 | FileDirective.prototype.bind = function bind() { 1556 | for (var key in this.events) { 1557 | var prop = this.events[key]; 1558 | this.element.bind(key, this[prop]); 1559 | } 1560 | }; 1561 | /** 1562 | * Unbinds events handles 1563 | */ 1564 | 1565 | 1566 | FileDirective.prototype.unbind = function unbind() { 1567 | for (var key in this.events) { 1568 | this.element.unbind(key, this.events[key]); 1569 | } 1570 | }; 1571 | /** 1572 | * Destroys directive 1573 | */ 1574 | 1575 | 1576 | FileDirective.prototype.destroy = function destroy() { 1577 | var index = this.uploader._directives[this.prop].indexOf(this); 1578 | this.uploader._directives[this.prop].splice(index, 1); 1579 | this.unbind(); 1580 | // this.element = null; 1581 | }; 1582 | /** 1583 | * Saves links to functions 1584 | * @private 1585 | */ 1586 | 1587 | 1588 | FileDirective.prototype._saveLinks = function _saveLinks() { 1589 | for (var key in this.events) { 1590 | var prop = this.events[key]; 1591 | this[prop] = this[prop].bind(this); 1592 | } 1593 | }; 1594 | 1595 | return FileDirective; 1596 | }(); 1597 | 1598 | /** 1599 | * Map of events 1600 | * @type {Object} 1601 | */ 1602 | 1603 | 1604 | FileDirective.prototype.events = {}; 1605 | 1606 | return FileDirective; 1607 | } 1608 | 1609 | /***/ }), 1610 | /* 7 */ 1611 | /***/ (function(module, exports, __webpack_require__) { 1612 | 1613 | 'use strict'; 1614 | 1615 | Object.defineProperty(exports, "__esModule", { 1616 | value: true 1617 | }); 1618 | exports.default = __identity; 1619 | 1620 | var _config = __webpack_require__(1); 1621 | 1622 | var _config2 = _interopRequireDefault(_config); 1623 | 1624 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 1625 | 1626 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 1627 | 1628 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 1629 | 1630 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 1631 | 1632 | var _angular = angular, 1633 | extend = _angular.extend; 1634 | function __identity($compile, FileDirective) { 1635 | 1636 | return function (_FileDirective) { 1637 | _inherits(FileSelect, _FileDirective); 1638 | 1639 | /** 1640 | * Creates instance of {FileSelect} object 1641 | * @param {Object} options 1642 | * @constructor 1643 | */ 1644 | function FileSelect(options) { 1645 | _classCallCheck(this, FileSelect); 1646 | 1647 | var extendedOptions = extend(options, { 1648 | // Map of events 1649 | events: { 1650 | $destroy: 'destroy', 1651 | change: 'onChange' 1652 | }, 1653 | // Name of property inside uploader._directive object 1654 | prop: 'select' 1655 | }); 1656 | 1657 | var _this = _possibleConstructorReturn(this, _FileDirective.call(this, extendedOptions)); 1658 | 1659 | if (!_this.uploader.isHTML5) { 1660 | _this.element.removeAttr('multiple'); 1661 | } 1662 | _this.element.prop('value', null); // FF fix 1663 | return _this; 1664 | } 1665 | /** 1666 | * Returns options 1667 | * @return {Object|undefined} 1668 | */ 1669 | 1670 | 1671 | FileSelect.prototype.getOptions = function getOptions() {}; 1672 | /** 1673 | * Returns filters 1674 | * @return {Array|String|undefined} 1675 | */ 1676 | 1677 | 1678 | FileSelect.prototype.getFilters = function getFilters() {}; 1679 | /** 1680 | * If returns "true" then HTMLInputElement will be cleared 1681 | * @returns {Boolean} 1682 | */ 1683 | 1684 | 1685 | FileSelect.prototype.isEmptyAfterSelection = function isEmptyAfterSelection() { 1686 | return !!this.element.attr('multiple'); 1687 | }; 1688 | /** 1689 | * Event handler 1690 | */ 1691 | 1692 | 1693 | FileSelect.prototype.onChange = function onChange() { 1694 | var files = this.uploader.isHTML5 ? this.element[0].files : this.element[0]; 1695 | var options = this.getOptions(); 1696 | var filters = this.getFilters(); 1697 | 1698 | if (!this.uploader.isHTML5) this.destroy(); 1699 | this.uploader.addToQueue(files, options, filters); 1700 | if (this.isEmptyAfterSelection()) { 1701 | this.element.prop('value', null); 1702 | this.element.replaceWith($compile(this.element.clone())(this.scope)); // IE fix 1703 | } 1704 | }; 1705 | 1706 | return FileSelect; 1707 | }(FileDirective); 1708 | } 1709 | 1710 | __identity.$inject = ['$compile', 'FileDirective']; 1711 | 1712 | /***/ }), 1713 | /* 8 */ 1714 | /***/ (function(module, exports) { 1715 | 1716 | 'use strict'; 1717 | 1718 | Object.defineProperty(exports, "__esModule", { 1719 | value: true 1720 | }); 1721 | exports.default = __identity; 1722 | 1723 | function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } 1724 | 1725 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 1726 | 1727 | var _angular = angular, 1728 | bind = _angular.bind, 1729 | isUndefined = _angular.isUndefined; 1730 | function __identity($q) { 1731 | 1732 | return function () { 1733 | /** 1734 | * @param {Array} pipes 1735 | */ 1736 | function Pipeline() { 1737 | var pipes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; 1738 | 1739 | _classCallCheck(this, Pipeline); 1740 | 1741 | this.pipes = pipes; 1742 | } 1743 | 1744 | Pipeline.prototype.next = function next(args) { 1745 | var pipe = this.pipes.shift(); 1746 | if (isUndefined(pipe)) { 1747 | this.onSuccessful.apply(this, _toConsumableArray(args)); 1748 | return; 1749 | } 1750 | var err = new Error('The filter has not passed'); 1751 | err.pipe = pipe; 1752 | err.args = args; 1753 | if (pipe.isAsync) { 1754 | var deferred = $q.defer(); 1755 | var onFulfilled = bind(this, this.next, args); 1756 | var onRejected = bind(this, this.onThrown, err); 1757 | deferred.promise.then(onFulfilled, onRejected); 1758 | pipe.apply(undefined, _toConsumableArray(args).concat([deferred])); 1759 | } else { 1760 | var isDone = Boolean(pipe.apply(undefined, _toConsumableArray(args))); 1761 | if (isDone) { 1762 | this.next(args); 1763 | } else { 1764 | this.onThrown(err); 1765 | } 1766 | } 1767 | }; 1768 | 1769 | Pipeline.prototype.exec = function exec() { 1770 | for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { 1771 | args[_key] = arguments[_key]; 1772 | } 1773 | 1774 | this.next(args); 1775 | }; 1776 | 1777 | Pipeline.prototype.onThrown = function onThrown(err) {}; 1778 | 1779 | Pipeline.prototype.onSuccessful = function onSuccessful() {}; 1780 | 1781 | return Pipeline; 1782 | }(); 1783 | } 1784 | 1785 | __identity.$inject = ['$q']; 1786 | 1787 | /***/ }), 1788 | /* 9 */ 1789 | /***/ (function(module, exports, __webpack_require__) { 1790 | 1791 | 'use strict'; 1792 | 1793 | Object.defineProperty(exports, "__esModule", { 1794 | value: true 1795 | }); 1796 | exports.default = __identity; 1797 | 1798 | var _config = __webpack_require__(1); 1799 | 1800 | var _config2 = _interopRequireDefault(_config); 1801 | 1802 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 1803 | 1804 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 1805 | 1806 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 1807 | 1808 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 1809 | 1810 | var _angular = angular, 1811 | extend = _angular.extend, 1812 | forEach = _angular.forEach; 1813 | function __identity(FileDirective) { 1814 | 1815 | return function (_FileDirective) { 1816 | _inherits(FileDrop, _FileDirective); 1817 | 1818 | /** 1819 | * Creates instance of {FileDrop} object 1820 | * @param {Object} options 1821 | * @constructor 1822 | */ 1823 | function FileDrop(options) { 1824 | _classCallCheck(this, FileDrop); 1825 | 1826 | var extendedOptions = extend(options, { 1827 | // Map of events 1828 | events: { 1829 | $destroy: 'destroy', 1830 | drop: 'onDrop', 1831 | dragover: 'onDragOver', 1832 | dragleave: 'onDragLeave' 1833 | }, 1834 | // Name of property inside uploader._directive object 1835 | prop: 'drop' 1836 | }); 1837 | 1838 | return _possibleConstructorReturn(this, _FileDirective.call(this, extendedOptions)); 1839 | } 1840 | /** 1841 | * Returns options 1842 | * @return {Object|undefined} 1843 | */ 1844 | 1845 | 1846 | FileDrop.prototype.getOptions = function getOptions() {}; 1847 | /** 1848 | * Returns filters 1849 | * @return {Array|String|undefined} 1850 | */ 1851 | 1852 | 1853 | FileDrop.prototype.getFilters = function getFilters() {}; 1854 | /** 1855 | * Event handler 1856 | */ 1857 | 1858 | 1859 | FileDrop.prototype.onDrop = function onDrop(event) { 1860 | var transfer = this._getTransfer(event); 1861 | if (!transfer) return; 1862 | var options = this.getOptions(); 1863 | var filters = this.getFilters(); 1864 | this._preventAndStop(event); 1865 | forEach(this.uploader._directives.over, this._removeOverClass, this); 1866 | this.uploader.addToQueue(transfer.files, options, filters); 1867 | }; 1868 | /** 1869 | * Event handler 1870 | */ 1871 | 1872 | 1873 | FileDrop.prototype.onDragOver = function onDragOver(event) { 1874 | var transfer = this._getTransfer(event); 1875 | if (!this._haveFiles(transfer.types)) return; 1876 | transfer.dropEffect = 'copy'; 1877 | this._preventAndStop(event); 1878 | forEach(this.uploader._directives.over, this._addOverClass, this); 1879 | }; 1880 | /** 1881 | * Event handler 1882 | */ 1883 | 1884 | 1885 | FileDrop.prototype.onDragLeave = function onDragLeave(event) { 1886 | if (event.currentTarget === this.element[0]) return; 1887 | this._preventAndStop(event); 1888 | forEach(this.uploader._directives.over, this._removeOverClass, this); 1889 | }; 1890 | /** 1891 | * Helper 1892 | */ 1893 | 1894 | 1895 | FileDrop.prototype._getTransfer = function _getTransfer(event) { 1896 | return event.dataTransfer ? event.dataTransfer : event.originalEvent.dataTransfer; // jQuery fix; 1897 | }; 1898 | /** 1899 | * Helper 1900 | */ 1901 | 1902 | 1903 | FileDrop.prototype._preventAndStop = function _preventAndStop(event) { 1904 | event.preventDefault(); 1905 | event.stopPropagation(); 1906 | }; 1907 | /** 1908 | * Returns "true" if types contains files 1909 | * @param {Object} types 1910 | */ 1911 | 1912 | 1913 | FileDrop.prototype._haveFiles = function _haveFiles(types) { 1914 | if (!types) return false; 1915 | if (types.indexOf) { 1916 | return types.indexOf('Files') !== -1; 1917 | } else if (types.contains) { 1918 | return types.contains('Files'); 1919 | } else { 1920 | return false; 1921 | } 1922 | }; 1923 | /** 1924 | * Callback 1925 | */ 1926 | 1927 | 1928 | FileDrop.prototype._addOverClass = function _addOverClass(item) { 1929 | item.addOverClass(); 1930 | }; 1931 | /** 1932 | * Callback 1933 | */ 1934 | 1935 | 1936 | FileDrop.prototype._removeOverClass = function _removeOverClass(item) { 1937 | item.removeOverClass(); 1938 | }; 1939 | 1940 | return FileDrop; 1941 | }(FileDirective); 1942 | } 1943 | 1944 | __identity.$inject = ['FileDirective']; 1945 | 1946 | /***/ }), 1947 | /* 10 */ 1948 | /***/ (function(module, exports, __webpack_require__) { 1949 | 1950 | 'use strict'; 1951 | 1952 | Object.defineProperty(exports, "__esModule", { 1953 | value: true 1954 | }); 1955 | exports.default = __identity; 1956 | 1957 | var _config = __webpack_require__(1); 1958 | 1959 | var _config2 = _interopRequireDefault(_config); 1960 | 1961 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 1962 | 1963 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 1964 | 1965 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 1966 | 1967 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 1968 | 1969 | var _angular = angular, 1970 | extend = _angular.extend; 1971 | function __identity(FileDirective) { 1972 | 1973 | return function (_FileDirective) { 1974 | _inherits(FileOver, _FileDirective); 1975 | 1976 | /** 1977 | * Creates instance of {FileDrop} object 1978 | * @param {Object} options 1979 | * @constructor 1980 | */ 1981 | function FileOver(options) { 1982 | _classCallCheck(this, FileOver); 1983 | 1984 | var extendedOptions = extend(options, { 1985 | // Map of events 1986 | events: { 1987 | $destroy: 'destroy' 1988 | }, 1989 | // Name of property inside uploader._directive object 1990 | prop: 'over', 1991 | // Over class 1992 | overClass: 'nv-file-over' 1993 | }); 1994 | 1995 | return _possibleConstructorReturn(this, _FileDirective.call(this, extendedOptions)); 1996 | } 1997 | /** 1998 | * Adds over class 1999 | */ 2000 | 2001 | 2002 | FileOver.prototype.addOverClass = function addOverClass() { 2003 | this.element.addClass(this.getOverClass()); 2004 | }; 2005 | /** 2006 | * Removes over class 2007 | */ 2008 | 2009 | 2010 | FileOver.prototype.removeOverClass = function removeOverClass() { 2011 | this.element.removeClass(this.getOverClass()); 2012 | }; 2013 | /** 2014 | * Returns over class 2015 | * @returns {String} 2016 | */ 2017 | 2018 | 2019 | FileOver.prototype.getOverClass = function getOverClass() { 2020 | return this.overClass; 2021 | }; 2022 | 2023 | return FileOver; 2024 | }(FileDirective); 2025 | } 2026 | 2027 | __identity.$inject = ['FileDirective']; 2028 | 2029 | /***/ }), 2030 | /* 11 */ 2031 | /***/ (function(module, exports, __webpack_require__) { 2032 | 2033 | 'use strict'; 2034 | 2035 | Object.defineProperty(exports, "__esModule", { 2036 | value: true 2037 | }); 2038 | exports.default = __identity; 2039 | 2040 | var _config = __webpack_require__(1); 2041 | 2042 | var _config2 = _interopRequireDefault(_config); 2043 | 2044 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 2045 | 2046 | function __identity($parse, FileUploader, FileSelect) { 2047 | 2048 | return { 2049 | link: function link(scope, element, attributes) { 2050 | var uploader = scope.$eval(attributes.uploader); 2051 | 2052 | if (!(uploader instanceof FileUploader)) { 2053 | throw new TypeError('"Uploader" must be an instance of FileUploader'); 2054 | } 2055 | 2056 | var object = new FileSelect({ 2057 | uploader: uploader, 2058 | element: element, 2059 | scope: scope 2060 | }); 2061 | 2062 | object.getOptions = $parse(attributes.options).bind(object, scope); 2063 | object.getFilters = function () { 2064 | return attributes.filters; 2065 | }; 2066 | } 2067 | }; 2068 | } 2069 | 2070 | __identity.$inject = ['$parse', 'FileUploader', 'FileSelect']; 2071 | 2072 | /***/ }), 2073 | /* 12 */ 2074 | /***/ (function(module, exports, __webpack_require__) { 2075 | 2076 | 'use strict'; 2077 | 2078 | Object.defineProperty(exports, "__esModule", { 2079 | value: true 2080 | }); 2081 | exports.default = __identity; 2082 | 2083 | var _config = __webpack_require__(1); 2084 | 2085 | var _config2 = _interopRequireDefault(_config); 2086 | 2087 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 2088 | 2089 | function __identity($parse, FileUploader, FileDrop) { 2090 | 2091 | return { 2092 | link: function link(scope, element, attributes) { 2093 | var uploader = scope.$eval(attributes.uploader); 2094 | 2095 | if (!(uploader instanceof FileUploader)) { 2096 | throw new TypeError('"Uploader" must be an instance of FileUploader'); 2097 | } 2098 | 2099 | if (!uploader.isHTML5) return; 2100 | 2101 | var object = new FileDrop({ 2102 | uploader: uploader, 2103 | element: element 2104 | }); 2105 | 2106 | object.getOptions = $parse(attributes.options).bind(object, scope); 2107 | object.getFilters = function () { 2108 | return attributes.filters; 2109 | }; 2110 | } 2111 | }; 2112 | } 2113 | 2114 | __identity.$inject = ['$parse', 'FileUploader', 'FileDrop']; 2115 | 2116 | /***/ }), 2117 | /* 13 */ 2118 | /***/ (function(module, exports, __webpack_require__) { 2119 | 2120 | 'use strict'; 2121 | 2122 | Object.defineProperty(exports, "__esModule", { 2123 | value: true 2124 | }); 2125 | exports.default = __identity; 2126 | 2127 | var _config = __webpack_require__(1); 2128 | 2129 | var _config2 = _interopRequireDefault(_config); 2130 | 2131 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 2132 | 2133 | function __identity(FileUploader, FileOver) { 2134 | 2135 | return { 2136 | link: function link(scope, element, attributes) { 2137 | var uploader = scope.$eval(attributes.uploader); 2138 | 2139 | if (!(uploader instanceof FileUploader)) { 2140 | throw new TypeError('"Uploader" must be an instance of FileUploader'); 2141 | } 2142 | 2143 | var object = new FileOver({ 2144 | uploader: uploader, 2145 | element: element 2146 | }); 2147 | 2148 | object.getOverClass = function () { 2149 | return attributes.overClass || object.overClass; 2150 | }; 2151 | } 2152 | }; 2153 | } 2154 | 2155 | __identity.$inject = ['FileUploader', 'FileOver']; 2156 | 2157 | /***/ }) 2158 | /******/ ]) 2159 | }); 2160 | ; 2161 | //# sourceMappingURL=angular-file-upload.js.map -------------------------------------------------------------------------------- /dist/angular-file-upload.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | angular-file-upload v2.6.1 3 | https://github.com/nervgh/angular-file-upload 4 | */ 5 | 6 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports["angular-file-upload"]=t():e["angular-file-upload"]=t()}(this,function(){return function(e){function t(n){if(o[n])return o[n].exports;var r=o[n]={exports:{},id:n,loaded:!1};return e[n].call(r.exports,r,r.exports,t),r.loaded=!0,r.exports}var o={};return t.m=e,t.c=o,t.p="",t(0)}([function(e,t,o){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}var r=o(1),i=n(r),s=o(2),a=n(s),u=o(3),l=n(u),p=o(4),c=n(p),f=o(5),d=n(f),h=o(6),y=n(h),m=o(7),v=n(m),_=o(8),g=n(_),b=o(9),F=n(b),O=o(10),C=n(O),T=o(11),I=n(T),w=o(12),A=n(w),U=o(13),x=n(U);angular.module(i.default.name,[]).value("fileUploaderOptions",a.default).factory("FileUploader",l.default).factory("FileLikeObject",c.default).factory("FileItem",d.default).factory("FileDirective",y.default).factory("FileSelect",v.default).factory("FileDrop",F.default).factory("FileOver",C.default).factory("Pipeline",g.default).directive("nvFileSelect",I.default).directive("nvFileDrop",A.default).directive("nvFileOver",x.default).run(["FileUploader","FileLikeObject","FileItem","FileDirective","FileSelect","FileDrop","FileOver","Pipeline",function(e,t,o,n,r,i,s,a){e.FileLikeObject=t,e.FileItem=o,e.FileDirective=n,e.FileSelect=r,e.FileDrop=i,e.FileOver=s,e.Pipeline=a}])},function(e,t){e.exports={name:"angularFileUpload"}},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={url:"/",alias:"file",headers:{},queue:[],progress:0,autoUpload:!1,removeAfterUpload:!1,method:"POST",filters:[],formData:[],queueLimit:Number.MAX_VALUE,withCredentials:!1,disableMultipart:!1}},function(e,t,o){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t,o,n,i,a,u,g){var b=n.File,F=n.FormData,O=function(){function n(t){r(this,n);var o=p(e);c(this,o,t,{isUploading:!1,_nextIndex:0,_directives:{select:[],drop:[],over:[]}}),this.filters.unshift({name:"queueLimit",fn:this._queueLimitFilter}),this.filters.unshift({name:"folder",fn:this._folderFilter})}return n.prototype.addToQueue=function(e,t,o){var n=this,r=this.isArrayLikeObject(e)?Array.prototype.slice.call(e):[e],i=this._getFilters(o),l=this.queue.length,p=[],c=function e(){var o=r.shift();if(v(o))return f();var l=n.isFile(o)?o:new a(o),c=n._convertFiltersToPipes(i),d=new g(c),h=function(t){var o=t.pipe.originalFilter,r=s(t.args,2),i=r[0],a=r[1];n._onWhenAddingFileFailed(i,o,a),e()},y=function(t,o){var r=new u(n,t,o);p.push(r),n.queue.push(r),n._onAfterAddingFile(r),e()};d.onThrown=h,d.onSuccessful=y,d.exec(l,t)},f=function(){n.queue.length!==l&&(n._onAfterAddingAll(p),n.progress=n._getTotalProgress()),n._render(),n.autoUpload&&n.uploadAll()};c()},n.prototype.removeFromQueue=function(e){var t=this.getIndexOfItem(e),o=this.queue[t];o.isUploading&&o.cancel(),this.queue.splice(t,1),o._destroy(),this.progress=this._getTotalProgress()},n.prototype.clearQueue=function(){for(;this.queue.length;)this.queue[0].remove();this.progress=0},n.prototype.uploadItem=function(e){var t=this.getIndexOfItem(e),o=this.queue[t],n=this.isHTML5?"_xhrTransport":"_iframeTransport";o._prepareToUploading(),this.isUploading||(this._onBeforeUploadItem(o),o.isCancel||(o.isUploading=!0,this.isUploading=!0,this[n](o),this._render()))},n.prototype.cancelItem=function(e){var t=this,o=this.getIndexOfItem(e),n=this.queue[o],r=this.isHTML5?"_xhr":"_form";if(n)if(n.isCancel=!0,n.isUploading)n[r].abort();else{var s=[void 0,0,{}],a=function(){t._onCancelItem.apply(t,[n].concat(s)),t._onCompleteItem.apply(t,[n].concat(s))};i(a)}},n.prototype.uploadAll=function(){var e=this.getNotUploadedItems().filter(function(e){return!e.isUploading});e.length&&(f(e,function(e){return e._prepareToUploading()}),e[0].upload())},n.prototype.cancelAll=function(){var e=this.getNotUploadedItems();f(e,function(e){return e.cancel()})},n.prototype.isFile=function(e){return this.constructor.isFile(e)},n.prototype.isFileLikeObject=function(e){return this.constructor.isFileLikeObject(e)},n.prototype.isArrayLikeObject=function(e){return this.constructor.isArrayLikeObject(e)},n.prototype.getIndexOfItem=function(e){return h(e)?e:this.queue.indexOf(e)},n.prototype.getNotUploadedItems=function(){return this.queue.filter(function(e){return!e.isUploaded})},n.prototype.getReadyItems=function(){return this.queue.filter(function(e){return e.isReady&&!e.isUploading}).sort(function(e,t){return e.index-t.index})},n.prototype.destroy=function(){var e=this;f(this._directives,function(t){f(e._directives[t],function(e){e.destroy()})})},n.prototype.onAfterAddingAll=function(e){},n.prototype.onAfterAddingFile=function(e){},n.prototype.onWhenAddingFileFailed=function(e,t,o){},n.prototype.onBeforeUploadItem=function(e){},n.prototype.onProgressItem=function(e,t){},n.prototype.onProgressAll=function(e){},n.prototype.onSuccessItem=function(e,t,o,n){},n.prototype.onErrorItem=function(e,t,o,n){},n.prototype.onCancelItem=function(e,t,o,n){},n.prototype.onCompleteItem=function(e,t,o,n){},n.prototype.onTimeoutItem=function(e){},n.prototype.onCompleteAll=function(){},n.prototype._getTotalProgress=function(e){if(this.removeAfterUpload)return e||0;var t=this.getNotUploadedItems().length,o=t?this.queue.length-t:this.queue.length,n=100/this.queue.length,r=(e||0)*n/100;return Math.round(o*n+r)},n.prototype._getFilters=function(e){if(!e)return this.filters;if(m(e))return e;var t=e.match(/[^\s,]+/g);return this.filters.filter(function(e){return t.indexOf(e.name)!==-1})},n.prototype._convertFiltersToPipes=function(e){var t=this;return e.map(function(e){var o=l(t,e.fn);return o.isAsync=3===e.fn.length,o.originalFilter=e,o})},n.prototype._render=function(){t.$$phase||t.$apply()},n.prototype._folderFilter=function(e){return!(!e.size&&!e.type)},n.prototype._queueLimitFilter=function(){return this.queue.length=200&&e<300||304===e},n.prototype._transformResponse=function(e,t){var n=this._headersGetter(t);return f(o.defaults.transformResponse,function(t){e=t(e,n)}),e},n.prototype._parseHeaders=function(e){var t,o,n,r={};return e?(f(e.split("\n"),function(e){n=e.indexOf(":"),t=e.slice(0,n).trim().toLowerCase(),o=e.slice(n+1).trim(),t&&(r[t]=r[t]?r[t]+", "+o:o)}),r):r},n.prototype._headersGetter=function(e){return function(t){return t?e[t.toLowerCase()]||null:e}},n.prototype._xhrTransport=function(e){var t,o=this,n=e._xhr=new XMLHttpRequest;if(e.disableMultipart?t=e._file:(t=new F,f(e.formData,function(e){f(e,function(e,o){t.append(o,e)})}),t.append(e.alias,e._file,e.file.name)),"number"!=typeof e._file.size)throw new TypeError("The file specified is no longer valid");n.upload.onprogress=function(t){var n=Math.round(t.lengthComputable?100*t.loaded/t.total:0);o._onProgressItem(e,n)},n.onload=function(){var t=o._parseHeaders(n.getAllResponseHeaders()),r=o._transformResponse(n.response,t),i=o._isSuccessCode(n.status)?"Success":"Error",s="_on"+i+"Item";o[s](e,r,n.status,t),o._onCompleteItem(e,r,n.status,t)},n.onerror=function(){var t=o._parseHeaders(n.getAllResponseHeaders()),r=o._transformResponse(n.response,t);o._onErrorItem(e,r,n.status,t),o._onCompleteItem(e,r,n.status,t)},n.onabort=function(){var t=o._parseHeaders(n.getAllResponseHeaders()),r=o._transformResponse(n.response,t);o._onCancelItem(e,r,n.status,t),o._onCompleteItem(e,r,n.status,t)},n.ontimeout=function(t){var r=o._parseHeaders(n.getAllResponseHeaders()),i="Request Timeout.";o._onTimeoutItem(e),o._onCompleteItem(e,i,408,r)},n.open(e.method,e.url,!0),n.timeout=e.timeout||0,n.withCredentials=e.withCredentials,f(e.headers,function(e,t){n.setRequestHeader(t,e)}),n.send(t)},n.prototype._iframeTransport=function(e){var t=this,o=_(''),n=_(''),r=e._input,i=0,s=null,a=!1;e._form&&e._form.replaceWith(r),e._form=o,r.prop("name",e.alias),f(e.formData,function(e){f(e,function(e,t){var n=_('');n.val(e),o.append(n)})}),o.prop({action:e.url,method:"POST",target:n.prop("name"),enctype:"multipart/form-data",encoding:"multipart/form-data"}),n.bind("load",function(){var o="",r=200;try{o=n[0].contentDocument.body.innerHTML}catch(e){r=500}if(s&&clearTimeout(s),s=null,a)return!1;var i={response:o,status:r,dummy:!0},u={},l=t._transformResponse(i.response,u);t._onSuccessItem(e,l,i.status,u),t._onCompleteItem(e,l,i.status,u)}),o.abort=function(){var i,s={status:0,dummy:!0},a={};n.unbind("load").prop("src","javascript:false;"),o.replaceWith(r),t._onCancelItem(e,i,s.status,a),t._onCompleteItem(e,i,s.status,a)},r.after(o),o.append(r).append(n),i=e.timeout||0,s=null,i&&(s=setTimeout(function(){a=!0,e.isCancel=!0,e.isUploading&&(n.unbind("load").prop("src","javascript:false;"),o.replaceWith(r));var i={},s="Request Timeout.";t._onTimeoutItem(e),t._onCompleteItem(e,s,408,i)},i)),o[0].submit()},n.prototype._onWhenAddingFileFailed=function(e,t,o){this.onWhenAddingFileFailed(e,t,o)},n.prototype._onAfterAddingFile=function(e){this.onAfterAddingFile(e)},n.prototype._onAfterAddingAll=function(e){this.onAfterAddingAll(e)},n.prototype._onBeforeUploadItem=function(e){e._onBeforeUpload(),this.onBeforeUploadItem(e)},n.prototype._onProgressItem=function(e,t){var o=this._getTotalProgress(t);this.progress=o,e._onProgress(t),this.onProgressItem(e,t),this.onProgressAll(o),this._render()},n.prototype._onSuccessItem=function(e,t,o,n){e._onSuccess(t,o,n),this.onSuccessItem(e,t,o,n)},n.prototype._onErrorItem=function(e,t,o,n){e._onError(t,o,n),this.onErrorItem(e,t,o,n)},n.prototype._onCancelItem=function(e,t,o,n){e._onCancel(t,o,n),this.onCancelItem(e,t,o,n)},n.prototype._onCompleteItem=function(e,t,o,n){e._onComplete(t,o,n),this.onCompleteItem(e,t,o,n);var r=this.getReadyItems()[0];return this.isUploading=!1,y(r)?void r.upload():(this.onCompleteAll(),this.progress=this._getTotalProgress(),void this._render())},n.prototype._onTimeoutItem=function(e){e._onTimeout(),this.onTimeoutItem(e)},n.isFile=function(e){return b&&e instanceof b},n.isFileLikeObject=function(e){return e instanceof a},n.isArrayLikeObject=function(e){return d(e)&&"length"in e},n.inherit=function(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,e.super_=t},n}();return O.prototype.isHTML5=!(!b||!F),O.isHTML5=O.prototype.isHTML5,O}Object.defineProperty(t,"__esModule",{value:!0});var s=function(){function e(e,t){var o=[],n=!0,r=!1,i=void 0;try{for(var s,a=e[Symbol.iterator]();!(n=(s=a.next()).done)&&(o.push(s.value),!t||o.length!==t);n=!0);}catch(e){r=!0,i=e}finally{try{!n&&a.return&&a.return()}finally{if(r)throw i}}return o}return function(t,o){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,o);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}();t.default=i;var a=o(1),u=(n(a),angular),l=u.bind,p=u.copy,c=u.extend,f=u.forEach,d=u.isObject,h=u.isNumber,y=u.isDefined,m=u.isArray,v=u.isUndefined,_=u.element;i.$inject=["fileUploaderOptions","$rootScope","$http","$window","$timeout","FileLikeObject","FileItem","Pipeline"]},function(e,t,o){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(){return function(){function e(t){r(this,e);var o=l(t),n=o?t.value:t,i=p(n)?"FakePath":"Object",s="_createFrom"+i;this[s](n,t)}return e.prototype._createFromFakePath=function(e,t){this.lastModifiedDate=null,this.size=null,this.type="like/"+e.slice(e.lastIndexOf(".")+1).toLowerCase(),this.name=e.slice(e.lastIndexOf("/")+e.lastIndexOf("\\")+2),this.input=t},e.prototype._createFromObject=function(e){this.lastModifiedDate=u(e.lastModifiedDate),this.size=e.size,this.type=e.type,this.name=e.name,this.input=e.input},e}()}Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var s=o(1),a=(n(s),angular),u=a.copy,l=a.isElement,p=a.isString},function(e,t,o){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){return function(){function o(e,n,i){r(this,o);var s=!!n.input,a=s?p(n.input):null,c=s?null:n;l(this,{url:e.url,alias:e.alias,headers:u(e.headers),formData:u(e.formData),removeAfterUpload:e.removeAfterUpload,withCredentials:e.withCredentials,disableMultipart:e.disableMultipart,method:e.method,timeout:e.timeout},i,{uploader:e,file:new t(n),isReady:!1,isUploading:!1,isUploaded:!1,isSuccess:!1,isCancel:!1,isError:!1,progress:0,index:null,_file:c,_input:a}),a&&this._replaceNode(a)}return o.prototype.upload=function(){try{this.uploader.uploadItem(this)}catch(t){var e=t.name+":"+t.message;this.uploader._onCompleteItem(this,e,t.code,[]),this.uploader._onErrorItem(this,e,t.code,[])}},o.prototype.cancel=function(){this.uploader.cancelItem(this)},o.prototype.remove=function(){this.uploader.removeFromQueue(this)},o.prototype.onBeforeUpload=function(){},o.prototype.onProgress=function(e){},o.prototype.onSuccess=function(e,t,o){},o.prototype.onError=function(e,t,o){},o.prototype.onCancel=function(e,t,o){},o.prototype.onComplete=function(e,t,o){},o.prototype.onTimeout=function(){},o.prototype._onBeforeUpload=function(){this.isReady=!0,this.isUploading=!1,this.isUploaded=!1,this.isSuccess=!1,this.isCancel=!1,this.isError=!1,this.progress=0,this.onBeforeUpload()},o.prototype._onProgress=function(e){this.progress=e,this.onProgress(e)},o.prototype._onSuccess=function(e,t,o){this.isReady=!1,this.isUploading=!1,this.isUploaded=!0,this.isSuccess=!0,this.isCancel=!1,this.isError=!1,this.progress=100,this.index=null,this.onSuccess(e,t,o)},o.prototype._onError=function(e,t,o){this.isReady=!1,this.isUploading=!1,this.isUploaded=!0,this.isSuccess=!1,this.isCancel=!1,this.isError=!0,this.progress=0,this.index=null,this.onError(e,t,o)},o.prototype._onCancel=function(e,t,o){this.isReady=!1,this.isUploading=!1,this.isUploaded=!1,this.isSuccess=!1,this.isCancel=!0,this.isError=!1,this.progress=0,this.index=null,this.onCancel(e,t,o)},o.prototype._onComplete=function(e,t,o){this.onComplete(e,t,o),this.removeAfterUpload&&this.remove()},o.prototype._onTimeout=function(){this.isReady=!1,this.isUploading=!1,this.isUploaded=!1,this.isSuccess=!1,this.isCancel=!1,this.isError=!0,this.progress=0,this.index=null,this.onTimeout()},o.prototype._destroy=function(){this._input&&this._input.remove(),this._form&&this._form.remove(),delete this._form,delete this._input},o.prototype._prepareToUploading=function(){this.index=this.index||++this.uploader._nextIndex,this.isReady=!0},o.prototype._replaceNode=function(t){var o=e(t.clone())(t.scope());o.prop("value",null),t.css("display","none"),t.after(o)},o}()}Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var s=o(1),a=(n(s),angular),u=a.copy,l=a.extend,p=a.element;a.isElement;i.$inject=["$compile","FileLikeObject"]},function(e,t,o){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(){var e=function(){function e(t){r(this,e),u(this,t),this.uploader._directives[this.prop].push(this),this._saveLinks(),this.bind()}return e.prototype.bind=function(){for(var e in this.events){var t=this.events[e];this.element.bind(e,this[t])}},e.prototype.unbind=function(){for(var e in this.events)this.element.unbind(e,this.events[e])},e.prototype.destroy=function(){var e=this.uploader._directives[this.prop].indexOf(this);this.uploader._directives[this.prop].splice(e,1),this.unbind()},e.prototype._saveLinks=function(){for(var e in this.events){var t=this.events[e];this[t]=this[t].bind(this)}},e}();return e.prototype.events={},e}Object.defineProperty(t,"__esModule",{value:!0}),t.default=i;var s=o(1),a=(n(s),angular),u=a.extend},function(e,t,o){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function s(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function a(e,t){return function(t){function o(e){r(this,o);var n=p(e,{events:{$destroy:"destroy",change:"onChange"},prop:"select"}),s=i(this,t.call(this,n));return s.uploader.isHTML5||s.element.removeAttr("multiple"),s.element.prop("value",null),s}return s(o,t),o.prototype.getOptions=function(){},o.prototype.getFilters=function(){},o.prototype.isEmptyAfterSelection=function(){return!!this.element.attr("multiple")},o.prototype.onChange=function(){var t=this.uploader.isHTML5?this.element[0].files:this.element[0],o=this.getOptions(),n=this.getFilters();this.uploader.isHTML5||this.destroy(),this.uploader.addToQueue(t,o,n),this.isEmptyAfterSelection()&&(this.element.prop("value",null),this.element.replaceWith(e(this.element.clone())(this.scope)))},o}(t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var u=o(1),l=(n(u),angular),p=l.extend;a.$inject=["$compile","FileDirective"]},function(e,t){"use strict";function o(e){if(Array.isArray(e)){for(var t=0,o=Array(e.length);t0&&void 0!==arguments[0]?arguments[0]:[];n(this,t),this.pipes=e}return t.prototype.next=function(t){var n=this.pipes.shift();if(a(n))return void this.onSuccessful.apply(this,o(t));var r=new Error("The filter has not passed");if(r.pipe=n,r.args=t,n.isAsync){var i=e.defer(),u=s(this,this.next,t),l=s(this,this.onThrown,r);i.promise.then(u,l),n.apply(void 0,o(t).concat([i]))}else{var p=Boolean(n.apply(void 0,o(t)));p?this.next(t):this.onThrown(r)}},t.prototype.exec=function(){for(var e=arguments.length,t=Array(e),o=0;o', 34 | link: function(scope, element, attributes) { 35 | if (!helper.support) return; 36 | 37 | var params = scope.$eval(attributes.ngThumb); 38 | 39 | if (!helper.isFile(params.file)) return; 40 | if (!helper.isImage(params.file)) return; 41 | 42 | var canvas = element.find('canvas'); 43 | var reader = new FileReader(); 44 | 45 | reader.onload = onLoadFile; 46 | reader.readAsDataURL(params.file); 47 | 48 | function onLoadFile(event) { 49 | var img = new Image(); 50 | img.onload = onLoadImage; 51 | img.src = event.target.result; 52 | } 53 | 54 | function onLoadImage() { 55 | var width = params.width || this.width / this.height * params.height; 56 | var height = params.height || this.height / this.width * params.width; 57 | canvas.attr({ width: width, height: height }); 58 | canvas[0].getContext('2d').drawImage(this, 0, 0, width, height); 59 | } 60 | } 61 | }; 62 | }]); 63 | -------------------------------------------------------------------------------- /examples/image-preview/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Uploads only images (with canvas preview) 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | Angular File Upload 52 | 53 | 54 | 55 | 56 | Demos 57 | 58 | Simple example 59 | Uploads only images (with canvas preview) 60 | Without bootstrap example 61 | 62 | 63 | View on Github 64 | Download 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | Select files 74 | 75 | 76 | 77 | 78 | Base drop zone 79 | 80 | 81 | 82 | 83 | 84 | Another drop zone with its own settings 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | Uploads only images (with canvas preview) 96 | The queue 97 | Queue length: {{ uploader.queue.length }} 98 | 99 | 100 | 101 | 102 | Name 103 | Size 104 | Progress 105 | Status 106 | Actions 107 | 108 | 109 | 110 | 111 | 112 | {{ item.file.name }} 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | {{ item.file.size/1024/1024|number:2 }} MB 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | Upload 135 | 136 | 137 | Cancel 138 | 139 | 140 | Remove 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | Queue progress: 150 | 151 | 152 | 153 | 154 | 155 | Upload all 156 | 157 | 158 | Cancel all 159 | 160 | 161 | Remove all 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /examples/image-preview/upload.php: -------------------------------------------------------------------------------- 1 | 'File transfer completed' ); 11 | $json = json_encode( $answer ); 12 | 13 | echo $json; 14 | 15 | } else { 16 | 17 | echo 'No files'; 18 | 19 | } 20 | 21 | ?> -------------------------------------------------------------------------------- /examples/image-preview/uploads/gap.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nervgh/angular-file-upload/173520ae510949d77c558888da8087f581409c08/examples/image-preview/uploads/gap.txt -------------------------------------------------------------------------------- /examples/issues/862/controllers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | angular 5 | 6 | 7 | .module('app', ['angularFileUpload']) 8 | 9 | 10 | .controller('AppController', ['$scope', 'FileUploader', function($scope, FileUploader) { 11 | var uploader = $scope.uploader = new FileUploader({ 12 | url: '../upload.php' 13 | //,timeout: 2000 14 | }); 15 | 16 | $scope.testUploader = function(){ 17 | angular.element("#testUpload").click(); 18 | } 19 | 20 | uploader.onCompleteAll = function() { 21 | console.info('onCompleteAll'); 22 | }; 23 | 24 | console.info('uploader', uploader); 25 | }]); 26 | -------------------------------------------------------------------------------- /examples/issues/862/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Simple example 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | Select files 55 | 56 | Single 57 | 58 | 59 | 60 | Alternative upload buttoon 61 | File meta data:name,status 62 | {{ uploader.queue[uploader.queue.length-1].file.name }} 63 | Is also an drag-and-drop area 64 | 65 | 66 | 67 | 68 | 69 | 70 | Upload queue 71 | Queue length: {{ uploader.queue.length }} 72 | 73 | 74 | 75 | 76 | Name 77 | Size 78 | Progress 79 | Status 80 | Actions 81 | 82 | 83 | 84 | 85 | {{ item.file.name }} 86 | {{ item.file.size/1024/1024|number:2 }} MB 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | Upload 100 | 101 | 102 | Cancel 103 | 104 | 105 | Remove 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | Queue progress: 115 | 116 | 117 | 118 | 119 | 120 | Upload all 121 | 122 | 123 | Cancel all 124 | 125 | 126 | Remove all 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /examples/issues/873/controllers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | angular 3 | .module('app', ['angularFileUpload']) 4 | .controller('AppController', ['$scope', 'FileUploader', function ($scope, FileUploader) { 5 | var uploader = $scope.uploader = new FileUploader({ 6 | url: '../upload.php' 7 | }); 8 | 9 | $scope.upload = function () { 10 | var items = uploader.getNotUploadedItems(); 11 | if (items.length == 0) { 12 | console.log("No files to upload"); 13 | return false; 14 | } 15 | 16 | for (var i = 0; i < items.length; i++) { 17 | var fileItem = changeFileName(items[i], ""); 18 | fileItem.upload(); 19 | } 20 | }; 21 | 22 | function changeFileName(fileItem, newFileName) { 23 | newFileName = newFileName || "file" + new Date().getTime(); 24 | 25 | var fileName = fileItem.file.name.split('.'); 26 | if (fileName.length < 2) { 27 | alert("Uploaded file must have a valid extension! For more information see the Supported Formats") 28 | } 29 | var fileExtension = "." + fileName.pop(); 30 | fileItem.file.name = newFileName + fileExtension; 31 | return fileItem; 32 | } 33 | }]); 34 | -------------------------------------------------------------------------------- /examples/issues/873/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Simple example 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Upload 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /examples/issues/874/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Simple example 6 | 7 | 8 | 9 | hello world 10 | 11 | 24 | 25 | -------------------------------------------------------------------------------- /examples/issues/pr876/iframe.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | iframe page 6 | 12 | 13 | 14 | 15 | 16 | test 17 | 18 | 19 | -------------------------------------------------------------------------------- /examples/issues/pr876/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Simple example 6 | 7 | 8 | 9 | Open iframe 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/issues/pr876/uploadPage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Upload page 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/issues/upload.php: -------------------------------------------------------------------------------- 1 | 'File transfer completed' ); 11 | $json = json_encode( $answer ); 12 | 13 | echo $json; 14 | 15 | } else { 16 | 17 | echo 'No files'; 18 | 19 | } 20 | 21 | ?> -------------------------------------------------------------------------------- /examples/simple/controllers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | angular 5 | 6 | 7 | .module('app', ['angularFileUpload']) 8 | 9 | 10 | .controller('AppController', ['$scope', 'FileUploader', function($scope, FileUploader) { 11 | var uploader = $scope.uploader = new FileUploader({ 12 | url: 'upload.php' 13 | //,timeout: 2000 14 | }); 15 | 16 | // FILTERS 17 | 18 | // a sync filter 19 | uploader.filters.push({ 20 | name: 'syncFilter', 21 | fn: function(item /*{File|FileLikeObject}*/, options) { 22 | console.log('syncFilter'); 23 | return this.queue.length < 10; 24 | } 25 | }); 26 | 27 | // an async filter 28 | uploader.filters.push({ 29 | name: 'asyncFilter', 30 | fn: function(item /*{File|FileLikeObject}*/, options, deferred) { 31 | console.log('asyncFilter'); 32 | setTimeout(deferred.resolve, 1e3); 33 | } 34 | }); 35 | 36 | // CALLBACKS 37 | 38 | uploader.onWhenAddingFileFailed = function(item /*{File|FileLikeObject}*/, filter, options) { 39 | console.info('onWhenAddingFileFailed', item, filter, options); 40 | }; 41 | uploader.onAfterAddingFile = function(fileItem) { 42 | console.info('onAfterAddingFile', fileItem); 43 | }; 44 | uploader.onAfterAddingAll = function(addedFileItems) { 45 | console.info('onAfterAddingAll', addedFileItems); 46 | }; 47 | uploader.onBeforeUploadItem = function(item) { 48 | console.info('onBeforeUploadItem', item); 49 | }; 50 | uploader.onProgressItem = function(fileItem, progress) { 51 | console.info('onProgressItem', fileItem, progress); 52 | }; 53 | uploader.onProgressAll = function(progress) { 54 | console.info('onProgressAll', progress); 55 | }; 56 | uploader.onSuccessItem = function(fileItem, response, status, headers) { 57 | console.info('onSuccessItem', fileItem, response, status, headers); 58 | }; 59 | uploader.onErrorItem = function(fileItem, response, status, headers) { 60 | console.info('onErrorItem', fileItem, response, status, headers); 61 | }; 62 | uploader.onCancelItem = function(fileItem, response, status, headers) { 63 | console.info('onCancelItem', fileItem, response, status, headers); 64 | }; 65 | uploader.onCompleteItem = function(fileItem, response, status, headers) { 66 | console.info('onCompleteItem', fileItem, response, status, headers); 67 | }; 68 | 69 | uploader.onTimeoutItem = function(fileItem) { 70 | console.info('onTimeoutItem', fileItem); 71 | }; 72 | 73 | uploader.onCompleteAll = function() { 74 | console.info('onCompleteAll'); 75 | }; 76 | 77 | console.info('uploader', uploader); 78 | }]); 79 | -------------------------------------------------------------------------------- /examples/simple/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Simple example 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | Angular File Upload 39 | 40 | 41 | 42 | 43 | Demos 44 | 45 | Simple example 46 | Uploads only images (with canvas preview) 47 | Without bootstrap example 48 | 49 | 50 | View on Github 51 | Download 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | Select files 61 | 62 | 63 | 64 | 65 | Base drop zone 66 | 67 | 68 | 69 | 70 | 71 | Another drop zone with its own settings 72 | 73 | 74 | 75 | 76 | 77 | Multiple 78 | 79 | 80 | Single 81 | 82 | 83 | Set timeout 2s 84 | 85 | 86 | 87 | 88 | 89 | Upload queue 90 | Queue length: {{ uploader.queue.length }} 91 | 92 | 93 | 94 | 95 | Name 96 | Size 97 | Progress 98 | Status 99 | Actions 100 | 101 | 102 | 103 | 104 | {{ item.file.name }} 105 | {{ item.file.size/1024/1024|number:2 }} MB 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | Upload 119 | 120 | 121 | Cancel 122 | 123 | 124 | Remove 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | Queue progress: 134 | 135 | 136 | 137 | 138 | 139 | Upload all 140 | 141 | 142 | Cancel all 143 | 144 | 145 | Remove all 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /examples/simple/upload.php: -------------------------------------------------------------------------------- 1 | 'File transfer completed' ); 11 | $json = json_encode( $answer ); 12 | 13 | echo $json; 14 | 15 | } else { 16 | 17 | echo 'No files'; 18 | 19 | } 20 | 21 | ?> -------------------------------------------------------------------------------- /examples/simple/uploads/gap.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nervgh/angular-file-upload/173520ae510949d77c558888da8087f581409c08/examples/simple/uploads/gap.txt -------------------------------------------------------------------------------- /examples/without-bootstrap/controllers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | angular 5 | 6 | 7 | .module('app', ['angularFileUpload']) 8 | 9 | 10 | .controller('AppController', ['$scope', 'FileUploader', function($scope, FileUploader) { 11 | var uploader = $scope.uploader = new FileUploader({ 12 | url: 'upload.php' 13 | }); 14 | 15 | // FILTERS 16 | 17 | uploader.filters.push({ 18 | name: 'customFilter', 19 | fn: function(item /*{File|FileLikeObject}*/, options) { 20 | return this.queue.length < 10; 21 | } 22 | }); 23 | 24 | // CALLBACKS 25 | 26 | uploader.onWhenAddingFileFailed = function(item /*{File|FileLikeObject}*/, filter, options) { 27 | console.info('onWhenAddingFileFailed', item, filter, options); 28 | }; 29 | uploader.onAfterAddingFile = function(fileItem) { 30 | console.info('onAfterAddingFile', fileItem); 31 | }; 32 | uploader.onAfterAddingAll = function(addedFileItems) { 33 | console.info('onAfterAddingAll', addedFileItems); 34 | }; 35 | uploader.onBeforeUploadItem = function(item) { 36 | console.info('onBeforeUploadItem', item); 37 | }; 38 | uploader.onProgressItem = function(fileItem, progress) { 39 | console.info('onProgressItem', fileItem, progress); 40 | }; 41 | uploader.onProgressAll = function(progress) { 42 | console.info('onProgressAll', progress); 43 | }; 44 | uploader.onSuccessItem = function(fileItem, response, status, headers) { 45 | console.info('onSuccessItem', fileItem, response, status, headers); 46 | }; 47 | uploader.onErrorItem = function(fileItem, response, status, headers) { 48 | console.info('onErrorItem', fileItem, response, status, headers); 49 | }; 50 | uploader.onCancelItem = function(fileItem, response, status, headers) { 51 | console.info('onCancelItem', fileItem, response, status, headers); 52 | }; 53 | uploader.onCompleteItem = function(fileItem, response, status, headers) { 54 | console.info('onCompleteItem', fileItem, response, status, headers); 55 | }; 56 | uploader.onCompleteAll = function() { 57 | console.info('onCompleteAll'); 58 | }; 59 | 60 | console.info('uploader', uploader); 61 | 62 | 63 | // ------------------------------- 64 | 65 | 66 | var controller = $scope.controller = { 67 | isImage: function(item) { 68 | var type = '|' + item.type.slice(item.type.lastIndexOf('/') + 1) + '|'; 69 | return '|jpg|png|jpeg|bmp|gif|'.indexOf(type) !== -1; 70 | } 71 | }; 72 | }]); -------------------------------------------------------------------------------- /examples/without-bootstrap/directives.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | angular 5 | 6 | 7 | .module('app') 8 | 9 | 10 | // Angular File Upload module does not include this directive 11 | // Only for example 12 | 13 | 14 | /** 15 | * The ng-thumb directive 16 | * @author: nerv 17 | * @version: 0.1.2, 2014-01-09 18 | */ 19 | .directive('ngThumb', ['$window', function($window) { 20 | var helper = { 21 | support: !!($window.FileReader && $window.CanvasRenderingContext2D), 22 | isFile: function(item) { 23 | return angular.isObject(item) && item instanceof $window.File; 24 | }, 25 | isImage: function(file) { 26 | var type = '|' + file.type.slice(file.type.lastIndexOf('/') + 1) + '|'; 27 | return '|jpg|png|jpeg|bmp|gif|'.indexOf(type) !== -1; 28 | } 29 | }; 30 | 31 | return { 32 | restrict: 'A', 33 | template: '', 34 | link: function(scope, element, attributes) { 35 | if (!helper.support) return; 36 | 37 | var params = scope.$eval(attributes.ngThumb); 38 | 39 | if (!helper.isFile(params.file)) return; 40 | if (!helper.isImage(params.file)) return; 41 | 42 | var canvas = element.find('canvas'); 43 | var reader = new FileReader(); 44 | 45 | reader.onload = onLoadFile; 46 | reader.readAsDataURL(params.file); 47 | 48 | function onLoadFile(event) { 49 | var img = new Image(); 50 | img.onload = onLoadImage; 51 | img.src = event.target.result; 52 | } 53 | 54 | function onLoadImage() { 55 | var width = params.width || this.width / this.height * params.height; 56 | var height = params.height || this.height / this.width * params.width; 57 | canvas.attr({ width: width, height: height }); 58 | canvas[0].getContext('2d').drawImage(this, 0, 0, width, height); 59 | } 60 | } 61 | }; 62 | }]); 63 | -------------------------------------------------------------------------------- /examples/without-bootstrap/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Without bootstrap example 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | Without bootstrap example 25 | 26 | Examples 27 | 28 | Simple example 29 | Uploads only images (with canvas preview) 30 | Without bootstrap 31 | 32 | Download / Repository 33 | 34 | 35 | 36 | 37 | 38 | 39 | Base drop zone indication 40 | 41 | 42 | 43 | One more drop zone with its own settings (and indication) 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | The queue. Length: {{ uploader.queue.length }} 54 | 55 | 56 | Name: {{ item.file.name }} 57 | Size: {{ item.file.size/1024/1024|number:2 }} Mb 58 | 59 | Progress: {{ item.progress }} 60 | 61 | 62 | 63 | 64 | 65 | Thumbnail (only images): 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Upload 76 | Cancel 77 | Remove 78 | 79 | 80 | 81 | 82 | 83 | Total progress: {{ uploader.progress }} 84 | 85 | 86 | 87 | 88 | Upload all 89 | Cancel all 90 | Remove all 91 | 92 | 93 | -------------------------------------------------------------------------------- /examples/without-bootstrap/style.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | } 4 | 5 | h1, h2 { 6 | background-color: steelblue; 7 | font-family: Tahoma, serif; 8 | color: white; 9 | padding: 10px; 10 | letter-spacing: 1px; 11 | } 12 | 13 | h1 { 14 | font-size: 1.3em; 15 | } 16 | 17 | h2 { 18 | font-size: 1.2em; 19 | } 20 | 21 | canvas { 22 | background-color: #f3f3f3; 23 | -webkit-box-shadow: 3px 3px 3px 0 #e3e3e3; 24 | -moz-box-shadow: 3px 3px 3px 0 #e3e3e3; 25 | box-shadow: 3px 3px 3px 0 #e3e3e3; 26 | border: 1px solid #c3c3c3; 27 | height: 100px; 28 | margin: 6px 0 0 6px; 29 | } 30 | 31 | .nv-file-over { 32 | background-color: #FFBEA3; 33 | } 34 | 35 | .other-drop-zone { 36 | border: 2px dashed burlywood; 37 | padding: 4px; 38 | height: 100px; 39 | } 40 | 41 | .other-over-zone { 42 | background-color: moccasin; 43 | } 44 | 45 | .bg { 46 | background-color: lightgreen; 47 | } 48 | 49 | .over-zone { 50 | border: 2px dashed lavender; 51 | height: 100px; 52 | padding: 4px; 53 | } 54 | 55 | .item-progress-box { 56 | height: 20px; 57 | margin-top: -20px; 58 | margin-left: 60px; 59 | margin-right: 10px; 60 | } 61 | 62 | .item-progress { 63 | background-color: #90B8DA; 64 | height: 100%; 65 | width: 0; 66 | } 67 | 68 | .total-progress-box { 69 | height: 20px; 70 | margin-top: -20px; 71 | margin-left: 90px; 72 | margin-right: 10px; 73 | } 74 | 75 | .total-progress { 76 | background-color: #90B8DA; 77 | height: 100%; 78 | width: 0; 79 | } 80 | 81 | .box { 82 | margin: 20px; 83 | } 84 | 85 | .progress { 86 | background-color: mediumpurple; 87 | height: 20px; 88 | } 89 | 90 | .uploaded { 91 | background-color: lightgreen; 92 | height: 20px; 93 | width: 100px; 94 | } 95 | 96 | ul > li:nth-child(odd) { 97 | background-color: #f5f5f5; 98 | margin: 2px; 99 | } 100 | 101 | .zone { 102 | width: 49%; 103 | } -------------------------------------------------------------------------------- /examples/without-bootstrap/upload.php: -------------------------------------------------------------------------------- 1 | 'File transfer completed' ); 11 | $json = json_encode( $answer ); 12 | 13 | echo $json; 14 | 15 | } else { 16 | 17 | echo 'No files'; 18 | 19 | } 20 | 21 | ?> -------------------------------------------------------------------------------- /examples/without-bootstrap/uploads/gap.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nervgh/angular-file-upload/173520ae510949d77c558888da8087f581409c08/examples/without-bootstrap/uploads/gap.txt -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // https://github.com/gulpjs/gulp/blob/master/docs/README.md 4 | let gulp = require('gulp'); 5 | // https://github.com/shama/webpack-stream 6 | let webpackStream = require('webpack-stream'); 7 | 8 | let WebpackConfig = require('./WebpackConfig'); 9 | let descriptor = require('./package.json'); 10 | 11 | 12 | let config = new WebpackConfig(descriptor, { 13 | src: './src/', 14 | dist: './dist/' 15 | }); 16 | 17 | 18 | gulp.task( 19 | `${config.name}/build`, 20 | function () { 21 | return gulp 22 | .src(config.path.src) 23 | .pipe(webpackStream(config.get())) 24 | .pipe(gulp.dest(config.path.dist)); 25 | } 26 | ); 27 | 28 | gulp.task( 29 | `${config.name}/watch`, function () { 30 | return gulp 31 | .watch(`${config.path.src}**/*.*`, [ 32 | `${config.name}/build` 33 | ]); 34 | } 35 | ); 36 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2013 nerv. https://github.com/nervgh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-file-upload", 3 | "version": "2.6.1", 4 | "homepage": "https://github.com/nervgh/angular-file-upload", 5 | "description": "Angular File Upload is a module for the AngularJS framework", 6 | "license": "MIT", 7 | "author": { 8 | "name": "nerv", 9 | "url": "https://github.com/nervgh" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/nervgh/angular-file-upload.git" 14 | }, 15 | "main": "dist/angular-file-upload.js", 16 | "engines": { 17 | "node": ">=4.0.0" 18 | }, 19 | "devDependencies": { 20 | "babel-core": "~6.8.0", 21 | "babel-loader": "~6.2.4", 22 | "babel-preset-es2015": "~6.6.0", 23 | "gulp": "~3.9.1", 24 | "json-loader": "~0.5.4", 25 | "raw-loader": "~0.5.1", 26 | "serve": "^11.2.0", 27 | "webpack-stream": "~3.2.0" 28 | }, 29 | "scripts": { 30 | "serve": "serve ." 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angularFileUpload" 3 | } -------------------------------------------------------------------------------- /src/directives/FileDrop.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | import CONFIG from './../config.json'; 5 | 6 | 7 | export default function __identity($parse, FileUploader, FileDrop) { 8 | 9 | 10 | return { 11 | link: (scope, element, attributes) => { 12 | var uploader = scope.$eval(attributes.uploader); 13 | 14 | if (!(uploader instanceof FileUploader)) { 15 | throw new TypeError('"Uploader" must be an instance of FileUploader'); 16 | } 17 | 18 | if (!uploader.isHTML5) return; 19 | 20 | var object = new FileDrop({ 21 | uploader: uploader, 22 | element: element 23 | }); 24 | 25 | object.getOptions = $parse(attributes.options).bind(object, scope); 26 | object.getFilters = () => attributes.filters; 27 | } 28 | }; 29 | 30 | 31 | } 32 | 33 | 34 | __identity.$inject = [ 35 | '$parse', 36 | 'FileUploader', 37 | 'FileDrop' 38 | ]; -------------------------------------------------------------------------------- /src/directives/FileOver.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | import CONFIG from './../config.json'; 5 | 6 | 7 | export default function __identity(FileUploader, FileOver) { 8 | 9 | 10 | return { 11 | link: (scope, element, attributes) => { 12 | var uploader = scope.$eval(attributes.uploader); 13 | 14 | if (!(uploader instanceof FileUploader)) { 15 | throw new TypeError('"Uploader" must be an instance of FileUploader'); 16 | } 17 | 18 | var object = new FileOver({ 19 | uploader: uploader, 20 | element: element 21 | }); 22 | 23 | object.getOverClass = () => attributes.overClass || object.overClass; 24 | } 25 | }; 26 | 27 | 28 | } 29 | 30 | 31 | __identity.$inject = [ 32 | 'FileUploader', 33 | 'FileOver' 34 | ]; -------------------------------------------------------------------------------- /src/directives/FileSelect.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | import CONFIG from './../config.json'; 5 | 6 | 7 | export default function __identity($parse, FileUploader, FileSelect) { 8 | 9 | 10 | return { 11 | link: (scope, element, attributes) => { 12 | var uploader = scope.$eval(attributes.uploader); 13 | 14 | if (!(uploader instanceof FileUploader)) { 15 | throw new TypeError('"Uploader" must be an instance of FileUploader'); 16 | } 17 | 18 | var object = new FileSelect({ 19 | uploader: uploader, 20 | element: element, 21 | scope: scope 22 | }); 23 | 24 | object.getOptions = $parse(attributes.options).bind(object, scope); 25 | object.getFilters = () => attributes.filters; 26 | } 27 | }; 28 | 29 | 30 | } 31 | 32 | 33 | __identity.$inject = [ 34 | '$parse', 35 | 'FileUploader', 36 | 'FileSelect' 37 | ]; -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | import CONFIG from './config.json'; 5 | 6 | 7 | import options from './values/options' 8 | 9 | 10 | import serviceFileUploader from './services/FileUploader'; 11 | import serviceFileLikeObject from './services/FileLikeObject'; 12 | import serviceFileItem from './services/FileItem'; 13 | import serviceFileDirective from './services/FileDirective'; 14 | import serviceFileSelect from './services/FileSelect'; 15 | import servicePipeline from './services/Pipeline'; 16 | import serviceFileDrop from './services/FileDrop'; 17 | import serviceFileOver from './services/FileOver'; 18 | 19 | 20 | import directiveFileSelect from './directives/FileSelect'; 21 | import directiveFileDrop from './directives/FileDrop'; 22 | import directiveFileOver from './directives/FileOver'; 23 | 24 | 25 | angular 26 | .module(CONFIG.name, []) 27 | .value('fileUploaderOptions', options) 28 | .factory('FileUploader', serviceFileUploader) 29 | .factory('FileLikeObject', serviceFileLikeObject) 30 | .factory('FileItem', serviceFileItem) 31 | .factory('FileDirective', serviceFileDirective) 32 | .factory('FileSelect', serviceFileSelect) 33 | .factory('FileDrop', serviceFileDrop) 34 | .factory('FileOver', serviceFileOver) 35 | .factory('Pipeline', servicePipeline) 36 | .directive('nvFileSelect', directiveFileSelect) 37 | .directive('nvFileDrop', directiveFileDrop) 38 | .directive('nvFileOver', directiveFileOver) 39 | .run([ 40 | 'FileUploader', 41 | 'FileLikeObject', 42 | 'FileItem', 43 | 'FileDirective', 44 | 'FileSelect', 45 | 'FileDrop', 46 | 'FileOver', 47 | 'Pipeline', 48 | function(FileUploader, FileLikeObject, FileItem, FileDirective, FileSelect, FileDrop, FileOver, Pipeline) { 49 | // only for compatibility 50 | FileUploader.FileLikeObject = FileLikeObject; 51 | FileUploader.FileItem = FileItem; 52 | FileUploader.FileDirective = FileDirective; 53 | FileUploader.FileSelect = FileSelect; 54 | FileUploader.FileDrop = FileDrop; 55 | FileUploader.FileOver = FileOver; 56 | FileUploader.Pipeline = Pipeline; 57 | } 58 | ]); 59 | -------------------------------------------------------------------------------- /src/services/FileDirective.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | import CONFIG from './../config.json'; 5 | 6 | 7 | let { 8 | extend 9 | } = angular; 10 | 11 | 12 | export default function __identity() { 13 | 14 | 15 | class FileDirective { 16 | /** 17 | * Creates instance of {FileDirective} object 18 | * @param {Object} options 19 | * @param {Object} options.uploader 20 | * @param {HTMLElement} options.element 21 | * @param {Object} options.events 22 | * @param {String} options.prop 23 | * @constructor 24 | */ 25 | constructor(options) { 26 | extend(this, options); 27 | this.uploader._directives[this.prop].push(this); 28 | this._saveLinks(); 29 | this.bind(); 30 | } 31 | /** 32 | * Binds events handles 33 | */ 34 | bind() { 35 | for(var key in this.events) { 36 | var prop = this.events[key]; 37 | this.element.bind(key, this[prop]); 38 | } 39 | } 40 | /** 41 | * Unbinds events handles 42 | */ 43 | unbind() { 44 | for(var key in this.events) { 45 | this.element.unbind(key, this.events[key]); 46 | } 47 | } 48 | /** 49 | * Destroys directive 50 | */ 51 | destroy() { 52 | var index = this.uploader._directives[this.prop].indexOf(this); 53 | this.uploader._directives[this.prop].splice(index, 1); 54 | this.unbind(); 55 | // this.element = null; 56 | } 57 | /** 58 | * Saves links to functions 59 | * @private 60 | */ 61 | _saveLinks() { 62 | for(var key in this.events) { 63 | var prop = this.events[key]; 64 | this[prop] = this[prop].bind(this); 65 | } 66 | } 67 | } 68 | 69 | 70 | /** 71 | * Map of events 72 | * @type {Object} 73 | */ 74 | FileDirective.prototype.events = {}; 75 | 76 | 77 | return FileDirective; 78 | } -------------------------------------------------------------------------------- /src/services/FileDrop.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | import CONFIG from './../config.json'; 5 | 6 | 7 | let { 8 | extend, 9 | forEach 10 | } = angular; 11 | 12 | 13 | export default function __identity(FileDirective) { 14 | 15 | 16 | return class FileDrop extends FileDirective { 17 | /** 18 | * Creates instance of {FileDrop} object 19 | * @param {Object} options 20 | * @constructor 21 | */ 22 | constructor(options) { 23 | let extendedOptions = extend(options, { 24 | // Map of events 25 | events: { 26 | $destroy: 'destroy', 27 | drop: 'onDrop', 28 | dragover: 'onDragOver', 29 | dragleave: 'onDragLeave' 30 | }, 31 | // Name of property inside uploader._directive object 32 | prop: 'drop' 33 | }); 34 | 35 | super(extendedOptions); 36 | } 37 | /** 38 | * Returns options 39 | * @return {Object|undefined} 40 | */ 41 | getOptions() { 42 | } 43 | /** 44 | * Returns filters 45 | * @return {Array|String|undefined} 46 | */ 47 | getFilters() { 48 | } 49 | /** 50 | * Event handler 51 | */ 52 | onDrop(event) { 53 | var transfer = this._getTransfer(event); 54 | if(!transfer) return; 55 | var options = this.getOptions(); 56 | var filters = this.getFilters(); 57 | this._preventAndStop(event); 58 | forEach(this.uploader._directives.over, this._removeOverClass, this); 59 | this.uploader.addToQueue(transfer.files, options, filters); 60 | } 61 | /** 62 | * Event handler 63 | */ 64 | onDragOver(event) { 65 | var transfer = this._getTransfer(event); 66 | if(!this._haveFiles(transfer.types)) return; 67 | transfer.dropEffect = 'copy'; 68 | this._preventAndStop(event); 69 | forEach(this.uploader._directives.over, this._addOverClass, this); 70 | } 71 | /** 72 | * Event handler 73 | */ 74 | onDragLeave(event) { 75 | if(event.currentTarget === this.element[0]) return; 76 | this._preventAndStop(event); 77 | forEach(this.uploader._directives.over, this._removeOverClass, this); 78 | } 79 | /** 80 | * Helper 81 | */ 82 | _getTransfer(event) { 83 | return event.dataTransfer ? event.dataTransfer : event.originalEvent.dataTransfer; // jQuery fix; 84 | } 85 | /** 86 | * Helper 87 | */ 88 | _preventAndStop(event) { 89 | event.preventDefault(); 90 | event.stopPropagation(); 91 | } 92 | /** 93 | * Returns "true" if types contains files 94 | * @param {Object} types 95 | */ 96 | _haveFiles(types) { 97 | if(!types) return false; 98 | if(types.indexOf) { 99 | return types.indexOf('Files') !== -1; 100 | } else if(types.contains) { 101 | return types.contains('Files'); 102 | } else { 103 | return false; 104 | } 105 | } 106 | /** 107 | * Callback 108 | */ 109 | _addOverClass(item) { 110 | item.addOverClass(); 111 | } 112 | /** 113 | * Callback 114 | */ 115 | _removeOverClass(item) { 116 | item.removeOverClass(); 117 | } 118 | } 119 | } 120 | 121 | 122 | __identity.$inject = [ 123 | 'FileDirective' 124 | ]; -------------------------------------------------------------------------------- /src/services/FileItem.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | import CONFIG from './../config.json'; 5 | 6 | 7 | let { 8 | copy, 9 | extend, 10 | element, 11 | isElement 12 | } = angular; 13 | 14 | 15 | export default function __identity($compile, FileLikeObject) { 16 | 17 | 18 | return class FileItem { 19 | /** 20 | * Creates an instance of FileItem 21 | * @param {FileUploader} uploader 22 | * @param {File|HTMLInputElement|Object} some 23 | * @param {Object} options 24 | * @constructor 25 | */ 26 | constructor(uploader, some, options) { 27 | var isInput = !!some.input; 28 | var input = isInput ? element(some.input) : null; 29 | var file = !isInput ? some : null; 30 | 31 | extend(this, { 32 | url: uploader.url, 33 | alias: uploader.alias, 34 | headers: copy(uploader.headers), 35 | formData: copy(uploader.formData), 36 | removeAfterUpload: uploader.removeAfterUpload, 37 | withCredentials: uploader.withCredentials, 38 | disableMultipart: uploader.disableMultipart, 39 | method: uploader.method, 40 | timeout: uploader.timeout 41 | }, options, { 42 | uploader: uploader, 43 | file: new FileLikeObject(some), 44 | isReady: false, 45 | isUploading: false, 46 | isUploaded: false, 47 | isSuccess: false, 48 | isCancel: false, 49 | isError: false, 50 | progress: 0, 51 | index: null, 52 | _file: file, 53 | _input: input 54 | }); 55 | 56 | if (input) this._replaceNode(input); 57 | } 58 | /********************** 59 | * PUBLIC 60 | **********************/ 61 | /** 62 | * Uploads a FileItem 63 | */ 64 | upload() { 65 | try { 66 | this.uploader.uploadItem(this); 67 | } catch(e) { 68 | var message = e.name + ':' + e.message; 69 | this.uploader._onCompleteItem(this, message, e.code, []); 70 | this.uploader._onErrorItem(this, message, e.code, []); 71 | } 72 | } 73 | /** 74 | * Cancels uploading of FileItem 75 | */ 76 | cancel() { 77 | this.uploader.cancelItem(this); 78 | } 79 | /** 80 | * Removes a FileItem 81 | */ 82 | remove() { 83 | this.uploader.removeFromQueue(this); 84 | } 85 | /** 86 | * Callback 87 | * @private 88 | */ 89 | onBeforeUpload() { 90 | } 91 | /** 92 | * Callback 93 | * @param {Number} progress 94 | * @private 95 | */ 96 | onProgress(progress) { 97 | } 98 | /** 99 | * Callback 100 | * @param {*} response 101 | * @param {Number} status 102 | * @param {Object} headers 103 | */ 104 | onSuccess(response, status, headers) { 105 | } 106 | /** 107 | * Callback 108 | * @param {*} response 109 | * @param {Number} status 110 | * @param {Object} headers 111 | */ 112 | onError(response, status, headers) { 113 | } 114 | /** 115 | * Callback 116 | * @param {*} response 117 | * @param {Number} status 118 | * @param {Object} headers 119 | */ 120 | onCancel(response, status, headers) { 121 | } 122 | /** 123 | * Callback 124 | * @param {*} response 125 | * @param {Number} status 126 | * @param {Object} headers 127 | */ 128 | onComplete(response, status, headers) { 129 | } 130 | /** 131 | * Callback 132 | */ 133 | onTimeout() { 134 | } 135 | /********************** 136 | * PRIVATE 137 | **********************/ 138 | /** 139 | * Inner callback 140 | */ 141 | _onBeforeUpload() { 142 | this.isReady = true; 143 | this.isUploading = false; 144 | this.isUploaded = false; 145 | this.isSuccess = false; 146 | this.isCancel = false; 147 | this.isError = false; 148 | this.progress = 0; 149 | this.onBeforeUpload(); 150 | } 151 | /** 152 | * Inner callback 153 | * @param {Number} progress 154 | * @private 155 | */ 156 | _onProgress(progress) { 157 | this.progress = progress; 158 | this.onProgress(progress); 159 | } 160 | /** 161 | * Inner callback 162 | * @param {*} response 163 | * @param {Number} status 164 | * @param {Object} headers 165 | * @private 166 | */ 167 | _onSuccess(response, status, headers) { 168 | this.isReady = false; 169 | this.isUploading = false; 170 | this.isUploaded = true; 171 | this.isSuccess = true; 172 | this.isCancel = false; 173 | this.isError = false; 174 | this.progress = 100; 175 | this.index = null; 176 | this.onSuccess(response, status, headers); 177 | } 178 | /** 179 | * Inner callback 180 | * @param {*} response 181 | * @param {Number} status 182 | * @param {Object} headers 183 | * @private 184 | */ 185 | _onError(response, status, headers) { 186 | this.isReady = false; 187 | this.isUploading = false; 188 | this.isUploaded = true; 189 | this.isSuccess = false; 190 | this.isCancel = false; 191 | this.isError = true; 192 | this.progress = 0; 193 | this.index = null; 194 | this.onError(response, status, headers); 195 | } 196 | /** 197 | * Inner callback 198 | * @param {*} response 199 | * @param {Number} status 200 | * @param {Object} headers 201 | * @private 202 | */ 203 | _onCancel(response, status, headers) { 204 | this.isReady = false; 205 | this.isUploading = false; 206 | this.isUploaded = false; 207 | this.isSuccess = false; 208 | this.isCancel = true; 209 | this.isError = false; 210 | this.progress = 0; 211 | this.index = null; 212 | this.onCancel(response, status, headers); 213 | } 214 | /** 215 | * Inner callback 216 | * @param {*} response 217 | * @param {Number} status 218 | * @param {Object} headers 219 | * @private 220 | */ 221 | _onComplete(response, status, headers) { 222 | this.onComplete(response, status, headers); 223 | if(this.removeAfterUpload) this.remove(); 224 | } 225 | /** 226 | * Inner callback 227 | * @private 228 | */ 229 | _onTimeout() { 230 | this.isReady = false; 231 | this.isUploading = false; 232 | this.isUploaded = false; 233 | this.isSuccess = false; 234 | this.isCancel = false; 235 | this.isError = true; 236 | this.progress = 0; 237 | this.index = null; 238 | this.onTimeout(); 239 | } 240 | /** 241 | * Destroys a FileItem 242 | */ 243 | _destroy() { 244 | if(this._input) this._input.remove(); 245 | if(this._form) this._form.remove(); 246 | delete this._form; 247 | delete this._input; 248 | } 249 | /** 250 | * Prepares to uploading 251 | * @private 252 | */ 253 | _prepareToUploading() { 254 | this.index = this.index || ++this.uploader._nextIndex; 255 | this.isReady = true; 256 | } 257 | /** 258 | * Replaces input element on his clone 259 | * @param {JQLite|jQuery} input 260 | * @private 261 | */ 262 | _replaceNode(input) { 263 | var clone = $compile(input.clone())(input.scope()); 264 | clone.prop('value', null); // FF fix 265 | input.css('display', 'none'); 266 | input.after(clone); // remove jquery dependency 267 | } 268 | } 269 | } 270 | 271 | 272 | __identity.$inject = [ 273 | '$compile', 274 | 'FileLikeObject' 275 | ]; 276 | -------------------------------------------------------------------------------- /src/services/FileLikeObject.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | import CONFIG from './../config.json'; 5 | 6 | 7 | let { 8 | copy, 9 | isElement, 10 | isString 11 | } = angular; 12 | 13 | 14 | export default function __identity() { 15 | 16 | 17 | return class FileLikeObject { 18 | /** 19 | * Creates an instance of FileLikeObject 20 | * @param {File|HTMLInputElement|Object} fileOrInput 21 | * @constructor 22 | */ 23 | constructor(fileOrInput) { 24 | var isInput = isElement(fileOrInput); 25 | var fakePathOrObject = isInput ? fileOrInput.value : fileOrInput; 26 | var postfix = isString(fakePathOrObject) ? 'FakePath' : 'Object'; 27 | var method = '_createFrom' + postfix; 28 | this[method](fakePathOrObject, fileOrInput); 29 | } 30 | /** 31 | * Creates file like object from fake path string 32 | * @param {String} path 33 | * @private 34 | */ 35 | _createFromFakePath(path, input) { 36 | this.lastModifiedDate = null; 37 | this.size = null; 38 | this.type = 'like/' + path.slice(path.lastIndexOf('.') + 1).toLowerCase(); 39 | this.name = path.slice(path.lastIndexOf('/') + path.lastIndexOf('\\') + 2); 40 | this.input = input; 41 | } 42 | /** 43 | * Creates file like object from object 44 | * @param {File|FileLikeObject} object 45 | * @private 46 | */ 47 | _createFromObject(object) { 48 | this.lastModifiedDate = copy(object.lastModifiedDate); 49 | this.size = object.size; 50 | this.type = object.type; 51 | this.name = object.name; 52 | this.input = object.input; 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/services/FileOver.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | import CONFIG from './../config.json'; 5 | 6 | 7 | let { 8 | extend 9 | } = angular; 10 | 11 | 12 | export default function __identity(FileDirective) { 13 | 14 | 15 | return class FileOver extends FileDirective { 16 | /** 17 | * Creates instance of {FileDrop} object 18 | * @param {Object} options 19 | * @constructor 20 | */ 21 | constructor(options) { 22 | let extendedOptions = extend(options, { 23 | // Map of events 24 | events: { 25 | $destroy: 'destroy' 26 | }, 27 | // Name of property inside uploader._directive object 28 | prop: 'over', 29 | // Over class 30 | overClass: 'nv-file-over' 31 | }); 32 | 33 | super(extendedOptions); 34 | } 35 | /** 36 | * Adds over class 37 | */ 38 | addOverClass() { 39 | this.element.addClass(this.getOverClass()); 40 | } 41 | /** 42 | * Removes over class 43 | */ 44 | removeOverClass() { 45 | this.element.removeClass(this.getOverClass()); 46 | } 47 | /** 48 | * Returns over class 49 | * @returns {String} 50 | */ 51 | getOverClass() { 52 | return this.overClass; 53 | } 54 | } 55 | } 56 | 57 | 58 | __identity.$inject = [ 59 | 'FileDirective' 60 | ]; -------------------------------------------------------------------------------- /src/services/FileSelect.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | import CONFIG from './../config.json'; 5 | 6 | 7 | let { 8 | extend 9 | } = angular; 10 | 11 | 12 | export default function __identity($compile, FileDirective) { 13 | 14 | 15 | return class FileSelect extends FileDirective { 16 | /** 17 | * Creates instance of {FileSelect} object 18 | * @param {Object} options 19 | * @constructor 20 | */ 21 | constructor(options) { 22 | let extendedOptions = extend(options, { 23 | // Map of events 24 | events: { 25 | $destroy: 'destroy', 26 | change: 'onChange' 27 | }, 28 | // Name of property inside uploader._directive object 29 | prop: 'select' 30 | }); 31 | 32 | super(extendedOptions); 33 | 34 | if(!this.uploader.isHTML5) { 35 | this.element.removeAttr('multiple'); 36 | } 37 | this.element.prop('value', null); // FF fix 38 | } 39 | /** 40 | * Returns options 41 | * @return {Object|undefined} 42 | */ 43 | getOptions() { 44 | } 45 | /** 46 | * Returns filters 47 | * @return {Array|String|undefined} 48 | */ 49 | getFilters() { 50 | } 51 | /** 52 | * If returns "true" then HTMLInputElement will be cleared 53 | * @returns {Boolean} 54 | */ 55 | isEmptyAfterSelection() { 56 | return !!this.element.attr('multiple'); 57 | } 58 | /** 59 | * Event handler 60 | */ 61 | onChange() { 62 | var files = this.uploader.isHTML5 ? this.element[0].files : this.element[0]; 63 | var options = this.getOptions(); 64 | var filters = this.getFilters(); 65 | 66 | if(!this.uploader.isHTML5) this.destroy(); 67 | this.uploader.addToQueue(files, options, filters); 68 | if(this.isEmptyAfterSelection()) { 69 | this.element.prop('value', null); 70 | this.element.replaceWith($compile(this.element.clone())(this.scope)); // IE fix 71 | } 72 | } 73 | } 74 | } 75 | 76 | 77 | __identity.$inject = [ 78 | '$compile', 79 | 'FileDirective' 80 | ]; 81 | -------------------------------------------------------------------------------- /src/services/FileUploader.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | import CONFIG from './../config.json'; 5 | 6 | 7 | let { 8 | bind, 9 | copy, 10 | extend, 11 | forEach, 12 | isObject, 13 | isNumber, 14 | isDefined, 15 | isArray, 16 | isUndefined, 17 | element 18 | } = angular; 19 | 20 | 21 | export default function __identity(fileUploaderOptions, $rootScope, $http, $window, $timeout, FileLikeObject, FileItem, Pipeline) { 22 | 23 | 24 | let { 25 | File, 26 | FormData 27 | } = $window; 28 | 29 | 30 | class FileUploader { 31 | /********************** 32 | * PUBLIC 33 | **********************/ 34 | /** 35 | * Creates an instance of FileUploader 36 | * @param {Object} [options] 37 | * @constructor 38 | */ 39 | constructor(options) { 40 | var settings = copy(fileUploaderOptions); 41 | 42 | extend(this, settings, options, { 43 | isUploading: false, 44 | _nextIndex: 0, 45 | _directives: {select: [], drop: [], over: []} 46 | }); 47 | 48 | // add default filters 49 | this.filters.unshift({name: 'queueLimit', fn: this._queueLimitFilter}); 50 | this.filters.unshift({name: 'folder', fn: this._folderFilter}); 51 | } 52 | /** 53 | * Adds items to the queue 54 | * @param {File|HTMLInputElement|Object|FileList|Array} files 55 | * @param {Object} [options] 56 | * @param {Array|String} filters 57 | */ 58 | addToQueue(files, options, filters) { 59 | let incomingQueue = this.isArrayLikeObject(files) ? Array.prototype.slice.call(files): [files]; 60 | var arrayOfFilters = this._getFilters(filters); 61 | var count = this.queue.length; 62 | var addedFileItems = []; 63 | 64 | let next = () => { 65 | let something = incomingQueue.shift(); 66 | 67 | if (isUndefined(something)) { 68 | return done(); 69 | } 70 | 71 | let fileLikeObject = this.isFile(something) ? something : new FileLikeObject(something); 72 | let pipes = this._convertFiltersToPipes(arrayOfFilters); 73 | let pipeline = new Pipeline(pipes); 74 | let onThrown = (err) => { 75 | let {originalFilter} = err.pipe; 76 | let [fileLikeObject, options] = err.args; 77 | this._onWhenAddingFileFailed(fileLikeObject, originalFilter, options); 78 | next(); 79 | }; 80 | let onSuccessful = (fileLikeObject, options) => { 81 | let fileItem = new FileItem(this, fileLikeObject, options); 82 | addedFileItems.push(fileItem); 83 | this.queue.push(fileItem); 84 | this._onAfterAddingFile(fileItem); 85 | next(); 86 | }; 87 | pipeline.onThrown = onThrown; 88 | pipeline.onSuccessful = onSuccessful; 89 | pipeline.exec(fileLikeObject, options); 90 | }; 91 | 92 | let done = () => { 93 | if(this.queue.length !== count) { 94 | this._onAfterAddingAll(addedFileItems); 95 | this.progress = this._getTotalProgress(); 96 | } 97 | 98 | this._render(); 99 | if (this.autoUpload) this.uploadAll(); 100 | }; 101 | 102 | next(); 103 | } 104 | /** 105 | * Remove items from the queue. Remove last: index = -1 106 | * @param {FileItem|Number} value 107 | */ 108 | removeFromQueue(value) { 109 | var index = this.getIndexOfItem(value); 110 | var item = this.queue[index]; 111 | if(item.isUploading) item.cancel(); 112 | this.queue.splice(index, 1); 113 | item._destroy(); 114 | this.progress = this._getTotalProgress(); 115 | } 116 | /** 117 | * Clears the queue 118 | */ 119 | clearQueue() { 120 | while(this.queue.length) { 121 | this.queue[0].remove(); 122 | } 123 | this.progress = 0; 124 | } 125 | /** 126 | * Uploads a item from the queue 127 | * @param {FileItem|Number} value 128 | */ 129 | uploadItem(value) { 130 | var index = this.getIndexOfItem(value); 131 | var item = this.queue[index]; 132 | var transport = this.isHTML5 ? '_xhrTransport' : '_iframeTransport'; 133 | 134 | item._prepareToUploading(); 135 | if(this.isUploading) return; 136 | 137 | this._onBeforeUploadItem(item); 138 | if (item.isCancel) return; 139 | 140 | item.isUploading = true; 141 | this.isUploading = true; 142 | this[transport](item); 143 | this._render(); 144 | } 145 | /** 146 | * Cancels uploading of item from the queue 147 | * @param {FileItem|Number} value 148 | */ 149 | cancelItem(value) { 150 | var index = this.getIndexOfItem(value); 151 | var item = this.queue[index]; 152 | var prop = this.isHTML5 ? '_xhr' : '_form'; 153 | if (!item) return; 154 | item.isCancel = true; 155 | if(item.isUploading) { 156 | // It will call this._onCancelItem() & this._onCompleteItem() asynchronously 157 | item[prop].abort(); 158 | } else { 159 | let dummy = [undefined, 0, {}]; 160 | let onNextTick = () => { 161 | this._onCancelItem(item, ...dummy); 162 | this._onCompleteItem(item, ...dummy); 163 | }; 164 | $timeout(onNextTick); // Trigger callbacks asynchronously (setImmediate emulation) 165 | } 166 | } 167 | /** 168 | * Uploads all not uploaded items of queue 169 | */ 170 | uploadAll() { 171 | var items = this.getNotUploadedItems().filter(item => !item.isUploading); 172 | if(!items.length) return; 173 | 174 | forEach(items, item => item._prepareToUploading()); 175 | items[0].upload(); 176 | } 177 | /** 178 | * Cancels all uploads 179 | */ 180 | cancelAll() { 181 | var items = this.getNotUploadedItems(); 182 | forEach(items, item => item.cancel()); 183 | } 184 | /** 185 | * Returns "true" if value an instance of File 186 | * @param {*} value 187 | * @returns {Boolean} 188 | * @private 189 | */ 190 | isFile(value) { 191 | return this.constructor.isFile(value); 192 | } 193 | /** 194 | * Returns "true" if value an instance of FileLikeObject 195 | * @param {*} value 196 | * @returns {Boolean} 197 | * @private 198 | */ 199 | isFileLikeObject(value) { 200 | return this.constructor.isFileLikeObject(value); 201 | } 202 | /** 203 | * Returns "true" if value is array like object 204 | * @param {*} value 205 | * @returns {Boolean} 206 | */ 207 | isArrayLikeObject(value) { 208 | return this.constructor.isArrayLikeObject(value); 209 | } 210 | /** 211 | * Returns a index of item from the queue 212 | * @param {Item|Number} value 213 | * @returns {Number} 214 | */ 215 | getIndexOfItem(value) { 216 | return isNumber(value) ? value : this.queue.indexOf(value); 217 | } 218 | /** 219 | * Returns not uploaded items 220 | * @returns {Array} 221 | */ 222 | getNotUploadedItems() { 223 | return this.queue.filter(item => !item.isUploaded); 224 | } 225 | /** 226 | * Returns items ready for upload 227 | * @returns {Array} 228 | */ 229 | getReadyItems() { 230 | return this.queue 231 | .filter(item => (item.isReady && !item.isUploading)) 232 | .sort((item1, item2) => item1.index - item2.index); 233 | } 234 | /** 235 | * Destroys instance of FileUploader 236 | */ 237 | destroy() { 238 | forEach(this._directives, (key) => { 239 | forEach(this._directives[key], (object) => { 240 | object.destroy(); 241 | }); 242 | }); 243 | } 244 | /** 245 | * Callback 246 | * @param {Array} fileItems 247 | */ 248 | onAfterAddingAll(fileItems) { 249 | } 250 | /** 251 | * Callback 252 | * @param {FileItem} fileItem 253 | */ 254 | onAfterAddingFile(fileItem) { 255 | } 256 | /** 257 | * Callback 258 | * @param {File|Object} item 259 | * @param {Object} filter 260 | * @param {Object} options 261 | */ 262 | onWhenAddingFileFailed(item, filter, options) { 263 | } 264 | /** 265 | * Callback 266 | * @param {FileItem} fileItem 267 | */ 268 | onBeforeUploadItem(fileItem) { 269 | } 270 | /** 271 | * Callback 272 | * @param {FileItem} fileItem 273 | * @param {Number} progress 274 | */ 275 | onProgressItem(fileItem, progress) { 276 | } 277 | /** 278 | * Callback 279 | * @param {Number} progress 280 | */ 281 | onProgressAll(progress) { 282 | } 283 | /** 284 | * Callback 285 | * @param {FileItem} item 286 | * @param {*} response 287 | * @param {Number} status 288 | * @param {Object} headers 289 | */ 290 | onSuccessItem(item, response, status, headers) { 291 | } 292 | /** 293 | * Callback 294 | * @param {FileItem} item 295 | * @param {*} response 296 | * @param {Number} status 297 | * @param {Object} headers 298 | */ 299 | onErrorItem(item, response, status, headers) { 300 | } 301 | /** 302 | * Callback 303 | * @param {FileItem} item 304 | * @param {*} response 305 | * @param {Number} status 306 | * @param {Object} headers 307 | */ 308 | onCancelItem(item, response, status, headers) { 309 | } 310 | /** 311 | * Callback 312 | * @param {FileItem} item 313 | * @param {*} response 314 | * @param {Number} status 315 | * @param {Object} headers 316 | */ 317 | onCompleteItem(item, response, status, headers) { 318 | } 319 | /** 320 | * Callback 321 | * @param {FileItem} item 322 | */ 323 | onTimeoutItem(item) { 324 | } 325 | /** 326 | * Callback 327 | */ 328 | onCompleteAll() { 329 | } 330 | /********************** 331 | * PRIVATE 332 | **********************/ 333 | /** 334 | * Returns the total progress 335 | * @param {Number} [value] 336 | * @returns {Number} 337 | * @private 338 | */ 339 | _getTotalProgress(value) { 340 | if(this.removeAfterUpload) return value || 0; 341 | 342 | var notUploaded = this.getNotUploadedItems().length; 343 | var uploaded = notUploaded ? this.queue.length - notUploaded : this.queue.length; 344 | var ratio = 100 / this.queue.length; 345 | var current = (value || 0) * ratio / 100; 346 | 347 | return Math.round(uploaded * ratio + current); 348 | } 349 | /** 350 | * Returns array of filters 351 | * @param {Array|String} filters 352 | * @returns {Array} 353 | * @private 354 | */ 355 | _getFilters(filters) { 356 | if(!filters) return this.filters; 357 | if(isArray(filters)) return filters; 358 | var names = filters.match(/[^\s,]+/g); 359 | return this.filters 360 | .filter(filter => names.indexOf(filter.name) !== -1); 361 | } 362 | /** 363 | * @param {Array} filters 364 | * @returns {Array} 365 | * @private 366 | */ 367 | _convertFiltersToPipes(filters) { 368 | return filters 369 | .map(filter => { 370 | let fn = bind(this, filter.fn); 371 | fn.isAsync = filter.fn.length === 3; 372 | fn.originalFilter = filter; 373 | return fn; 374 | }); 375 | } 376 | /** 377 | * Updates html 378 | * @private 379 | */ 380 | _render() { 381 | if(!$rootScope.$$phase) $rootScope.$apply(); 382 | } 383 | /** 384 | * Returns "true" if item is a file (not folder) 385 | * @param {File|FileLikeObject} item 386 | * @returns {Boolean} 387 | * @private 388 | */ 389 | _folderFilter(item) { 390 | return !!(item.size || item.type); 391 | } 392 | /** 393 | * Returns "true" if the limit has not been reached 394 | * @returns {Boolean} 395 | * @private 396 | */ 397 | _queueLimitFilter() { 398 | return this.queue.length < this.queueLimit; 399 | } 400 | /** 401 | * Checks whether upload successful 402 | * @param {Number} status 403 | * @returns {Boolean} 404 | * @private 405 | */ 406 | _isSuccessCode(status) { 407 | return (status >= 200 && status < 300) || status === 304; 408 | } 409 | /** 410 | * Transforms the server response 411 | * @param {*} response 412 | * @param {Object} headers 413 | * @returns {*} 414 | * @private 415 | */ 416 | _transformResponse(response, headers) { 417 | var headersGetter = this._headersGetter(headers); 418 | forEach($http.defaults.transformResponse, (transformFn) => { 419 | response = transformFn(response, headersGetter); 420 | }); 421 | return response; 422 | } 423 | /** 424 | * Parsed response headers 425 | * @param headers 426 | * @returns {Object} 427 | * @see https://github.com/angular/angular.js/blob/master/src/ng/http.js 428 | * @private 429 | */ 430 | _parseHeaders(headers) { 431 | var parsed = {}, key, val, i; 432 | 433 | if(!headers) return parsed; 434 | 435 | forEach(headers.split('\n'), (line) => { 436 | i = line.indexOf(':'); 437 | key = line.slice(0, i).trim().toLowerCase(); 438 | val = line.slice(i + 1).trim(); 439 | 440 | if(key) { 441 | parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; 442 | } 443 | }); 444 | 445 | return parsed; 446 | } 447 | /** 448 | * Returns function that returns headers 449 | * @param {Object} parsedHeaders 450 | * @returns {Function} 451 | * @private 452 | */ 453 | _headersGetter(parsedHeaders) { 454 | return (name) => { 455 | if(name) { 456 | return parsedHeaders[name.toLowerCase()] || null; 457 | } 458 | return parsedHeaders; 459 | }; 460 | } 461 | /** 462 | * The XMLHttpRequest transport 463 | * @param {FileItem} item 464 | * @private 465 | */ 466 | _xhrTransport(item) { 467 | var xhr = item._xhr = new XMLHttpRequest(); 468 | var sendable; 469 | 470 | if (!item.disableMultipart) { 471 | sendable = new FormData(); 472 | forEach(item.formData, (obj) => { 473 | forEach(obj, (value, key) => { 474 | sendable.append(key, value); 475 | }); 476 | }); 477 | 478 | sendable.append(item.alias, item._file, item.file.name); 479 | } 480 | else { 481 | sendable = item._file; 482 | } 483 | 484 | if(typeof(item._file.size) != 'number') { 485 | throw new TypeError('The file specified is no longer valid'); 486 | } 487 | 488 | xhr.upload.onprogress = (event) => { 489 | var progress = Math.round(event.lengthComputable ? event.loaded * 100 / event.total : 0); 490 | this._onProgressItem(item, progress); 491 | }; 492 | 493 | xhr.onload = () => { 494 | var headers = this._parseHeaders(xhr.getAllResponseHeaders()); 495 | var response = this._transformResponse(xhr.response, headers); 496 | var gist = this._isSuccessCode(xhr.status) ? 'Success' : 'Error'; 497 | var method = '_on' + gist + 'Item'; 498 | this[method](item, response, xhr.status, headers); 499 | this._onCompleteItem(item, response, xhr.status, headers); 500 | }; 501 | 502 | xhr.onerror = () => { 503 | var headers = this._parseHeaders(xhr.getAllResponseHeaders()); 504 | var response = this._transformResponse(xhr.response, headers); 505 | this._onErrorItem(item, response, xhr.status, headers); 506 | this._onCompleteItem(item, response, xhr.status, headers); 507 | }; 508 | 509 | xhr.onabort = () => { 510 | var headers = this._parseHeaders(xhr.getAllResponseHeaders()); 511 | var response = this._transformResponse(xhr.response, headers); 512 | this._onCancelItem(item, response, xhr.status, headers); 513 | this._onCompleteItem(item, response, xhr.status, headers); 514 | }; 515 | 516 | xhr.ontimeout = (e) => { 517 | var headers = this._parseHeaders(xhr.getAllResponseHeaders()); 518 | var response = "Request Timeout."; 519 | this._onTimeoutItem(item); 520 | this._onCompleteItem(item, response, 408, headers); 521 | }; 522 | 523 | xhr.open(item.method, item.url, true); 524 | 525 | xhr.timeout = item.timeout || 0; 526 | xhr.withCredentials = item.withCredentials; 527 | 528 | forEach(item.headers, (value, name) => { 529 | xhr.setRequestHeader(name, value); 530 | }); 531 | 532 | xhr.send(sendable); 533 | } 534 | /** 535 | * The IFrame transport 536 | * @param {FileItem} item 537 | * @private 538 | */ 539 | _iframeTransport(item) { 540 | var form = element(''); 541 | var iframe = element(''); 542 | var input = item._input; 543 | 544 | var timeout = 0; 545 | var timer = null; 546 | var isTimedOut = false; 547 | 548 | if(item._form) item._form.replaceWith(input); // remove old form 549 | item._form = form; // save link to new form 550 | 551 | input.prop('name', item.alias); 552 | 553 | forEach(item.formData, (obj) => { 554 | forEach(obj, (value, key) => { 555 | var element_ = element(''); 556 | element_.val(value); 557 | form.append(element_); 558 | }); 559 | }); 560 | 561 | form.prop({ 562 | action: item.url, 563 | method: 'POST', 564 | target: iframe.prop('name'), 565 | enctype: 'multipart/form-data', 566 | encoding: 'multipart/form-data' // old IE 567 | }); 568 | 569 | iframe.bind('load', () => { 570 | var html = ''; 571 | var status = 200; 572 | 573 | try { 574 | // Fix for legacy IE browsers that loads internal error page 575 | // when failed WS response received. In consequence iframe 576 | // content access denied error is thrown becouse trying to 577 | // access cross domain page. When such thing occurs notifying 578 | // with empty response object. See more info at: 579 | // http://stackoverflow.com/questions/151362/access-is-denied-error-on-accessing-iframe-document-object 580 | // Note that if non standard 4xx or 5xx error code returned 581 | // from WS then response content can be accessed without error 582 | // but 'XHR' status becomes 200. In order to avoid confusion 583 | // returning response via same 'success' event handler. 584 | 585 | // fixed angular.contents() for iframes 586 | html = iframe[0].contentDocument.body.innerHTML; 587 | } catch(e) { 588 | // in case we run into the access-is-denied error or we have another error on the server side 589 | // (intentional 500,40... errors), we at least say 'something went wrong' -> 500 590 | status = 500; 591 | } 592 | 593 | if (timer) { 594 | clearTimeout(timer); 595 | } 596 | timer = null; 597 | 598 | if (isTimedOut) { 599 | return false; //throw 'Request Timeout' 600 | } 601 | 602 | var xhr = {response: html, status: status, dummy: true}; 603 | var headers = {}; 604 | var response = this._transformResponse(xhr.response, headers); 605 | 606 | this._onSuccessItem(item, response, xhr.status, headers); 607 | this._onCompleteItem(item, response, xhr.status, headers); 608 | }); 609 | 610 | form.abort = () => { 611 | var xhr = {status: 0, dummy: true}; 612 | var headers = {}; 613 | var response; 614 | 615 | iframe.unbind('load').prop('src', 'javascript:false;'); 616 | form.replaceWith(input); 617 | 618 | this._onCancelItem(item, response, xhr.status, headers); 619 | this._onCompleteItem(item, response, xhr.status, headers); 620 | }; 621 | 622 | input.after(form); 623 | form.append(input).append(iframe); 624 | 625 | timeout = item.timeout || 0; 626 | timer = null; 627 | 628 | if (timeout) { 629 | timer = setTimeout(() => { 630 | isTimedOut = true; 631 | 632 | item.isCancel = true; 633 | if (item.isUploading) { 634 | iframe.unbind('load').prop('src', 'javascript:false;'); 635 | form.replaceWith(input); 636 | } 637 | 638 | var headers = {}; 639 | var response = "Request Timeout."; 640 | this._onTimeoutItem(item); 641 | this._onCompleteItem(item, response, 408, headers); 642 | }, timeout); 643 | } 644 | 645 | form[0].submit(); 646 | } 647 | /** 648 | * Inner callback 649 | * @param {File|Object} item 650 | * @param {Object} filter 651 | * @param {Object} options 652 | * @private 653 | */ 654 | _onWhenAddingFileFailed(item, filter, options) { 655 | this.onWhenAddingFileFailed(item, filter, options); 656 | } 657 | /** 658 | * Inner callback 659 | * @param {FileItem} item 660 | */ 661 | _onAfterAddingFile(item) { 662 | this.onAfterAddingFile(item); 663 | } 664 | /** 665 | * Inner callback 666 | * @param {Array} items 667 | */ 668 | _onAfterAddingAll(items) { 669 | this.onAfterAddingAll(items); 670 | } 671 | /** 672 | * Inner callback 673 | * @param {FileItem} item 674 | * @private 675 | */ 676 | _onBeforeUploadItem(item) { 677 | item._onBeforeUpload(); 678 | this.onBeforeUploadItem(item); 679 | } 680 | /** 681 | * Inner callback 682 | * @param {FileItem} item 683 | * @param {Number} progress 684 | * @private 685 | */ 686 | _onProgressItem(item, progress) { 687 | var total = this._getTotalProgress(progress); 688 | this.progress = total; 689 | item._onProgress(progress); 690 | this.onProgressItem(item, progress); 691 | this.onProgressAll(total); 692 | this._render(); 693 | } 694 | /** 695 | * Inner callback 696 | * @param {FileItem} item 697 | * @param {*} response 698 | * @param {Number} status 699 | * @param {Object} headers 700 | * @private 701 | */ 702 | _onSuccessItem(item, response, status, headers) { 703 | item._onSuccess(response, status, headers); 704 | this.onSuccessItem(item, response, status, headers); 705 | } 706 | /** 707 | * Inner callback 708 | * @param {FileItem} item 709 | * @param {*} response 710 | * @param {Number} status 711 | * @param {Object} headers 712 | * @private 713 | */ 714 | _onErrorItem(item, response, status, headers) { 715 | item._onError(response, status, headers); 716 | this.onErrorItem(item, response, status, headers); 717 | } 718 | /** 719 | * Inner callback 720 | * @param {FileItem} item 721 | * @param {*} response 722 | * @param {Number} status 723 | * @param {Object} headers 724 | * @private 725 | */ 726 | _onCancelItem(item, response, status, headers) { 727 | item._onCancel(response, status, headers); 728 | this.onCancelItem(item, response, status, headers); 729 | } 730 | /** 731 | * Inner callback 732 | * @param {FileItem} item 733 | * @param {*} response 734 | * @param {Number} status 735 | * @param {Object} headers 736 | * @private 737 | */ 738 | _onCompleteItem(item, response, status, headers) { 739 | item._onComplete(response, status, headers); 740 | this.onCompleteItem(item, response, status, headers); 741 | 742 | var nextItem = this.getReadyItems()[0]; 743 | this.isUploading = false; 744 | 745 | if(isDefined(nextItem)) { 746 | nextItem.upload(); 747 | return; 748 | } 749 | 750 | this.onCompleteAll(); 751 | this.progress = this._getTotalProgress(); 752 | this._render(); 753 | } 754 | /** 755 | * Inner callback 756 | * @param {FileItem} item 757 | * @private 758 | */ 759 | _onTimeoutItem(item) { 760 | item._onTimeout(); 761 | this.onTimeoutItem(item); 762 | } 763 | /********************** 764 | * STATIC 765 | **********************/ 766 | /** 767 | * Returns "true" if value an instance of File 768 | * @param {*} value 769 | * @returns {Boolean} 770 | * @private 771 | */ 772 | static isFile(value) { 773 | return (File && value instanceof File); 774 | } 775 | /** 776 | * Returns "true" if value an instance of FileLikeObject 777 | * @param {*} value 778 | * @returns {Boolean} 779 | * @private 780 | */ 781 | static isFileLikeObject(value) { 782 | return value instanceof FileLikeObject; 783 | } 784 | /** 785 | * Returns "true" if value is array like object 786 | * @param {*} value 787 | * @returns {Boolean} 788 | */ 789 | static isArrayLikeObject(value) { 790 | return (isObject(value) && 'length' in value); 791 | } 792 | /** 793 | * Inherits a target (Class_1) by a source (Class_2) 794 | * @param {Function} target 795 | * @param {Function} source 796 | */ 797 | static inherit(target, source) { 798 | target.prototype = Object.create(source.prototype); 799 | target.prototype.constructor = target; 800 | target.super_ = source; 801 | } 802 | } 803 | 804 | 805 | /********************** 806 | * PUBLIC 807 | **********************/ 808 | /** 809 | * Checks a support the html5 uploader 810 | * @returns {Boolean} 811 | * @readonly 812 | */ 813 | FileUploader.prototype.isHTML5 = !!(File && FormData); 814 | /********************** 815 | * STATIC 816 | **********************/ 817 | /** 818 | * @borrows FileUploader.prototype.isHTML5 819 | */ 820 | FileUploader.isHTML5 = FileUploader.prototype.isHTML5; 821 | 822 | 823 | return FileUploader; 824 | } 825 | 826 | 827 | __identity.$inject = [ 828 | 'fileUploaderOptions', 829 | '$rootScope', 830 | '$http', 831 | '$window', 832 | '$timeout', 833 | 'FileLikeObject', 834 | 'FileItem', 835 | 'Pipeline' 836 | ]; -------------------------------------------------------------------------------- /src/services/Pipeline.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | const { 5 | bind, 6 | isUndefined 7 | } = angular; 8 | 9 | 10 | export default function __identity($q) { 11 | 12 | 13 | return class Pipeline { 14 | /** 15 | * @param {Array} pipes 16 | */ 17 | constructor(pipes = []) { 18 | this.pipes = pipes; 19 | } 20 | next(args) { 21 | let pipe = this.pipes.shift(); 22 | if (isUndefined(pipe)) { 23 | this.onSuccessful(...args); 24 | return; 25 | } 26 | let err = new Error('The filter has not passed'); 27 | err.pipe = pipe; 28 | err.args = args; 29 | if (pipe.isAsync) { 30 | let deferred = $q.defer(); 31 | let onFulfilled = bind(this, this.next, args); 32 | let onRejected = bind(this, this.onThrown, err); 33 | deferred.promise.then(onFulfilled, onRejected); 34 | pipe(...args, deferred); 35 | } else { 36 | let isDone = Boolean(pipe(...args)); 37 | if (isDone) { 38 | this.next(args); 39 | } else { 40 | this.onThrown(err); 41 | } 42 | } 43 | } 44 | exec(...args) { 45 | this.next(args); 46 | } 47 | onThrown(err) { 48 | 49 | } 50 | onSuccessful(...args) { 51 | 52 | } 53 | } 54 | 55 | } 56 | 57 | __identity.$inject = [ 58 | '$q' 59 | ]; -------------------------------------------------------------------------------- /src/values/options.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | export default { 5 | url: '/', 6 | alias: 'file', 7 | headers: {}, 8 | queue: [], 9 | progress: 0, 10 | autoUpload: false, 11 | removeAfterUpload: false, 12 | method: 'POST', 13 | filters: [], 14 | formData: [], 15 | queueLimit: Number.MAX_VALUE, 16 | withCredentials: false, 17 | disableMultipart: false 18 | }; --------------------------------------------------------------------------------
Queue length: {{ uploader.queue.length }}