├── .gitignore ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── angularjs-file-upload.gemspec ├── app └── assets │ └── javascripts │ └── angularjs-file-upload.js └── lib ├── angularjs-file-upload-rails.rb └── angularjs-file-upload ├── rails.rb └── version.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in angularjs-file-upload.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Marthyn Olthof 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Gem Version](https://badge.fury.io/rb/angularjs-file-upload-rails.svg)](http://badge.fury.io/rb/angularjs-file-upload-rails) 2 | 3 | # AngularJS File Upload 4 | 5 | A gem that uses [angular-file-upload](https://github.com/nervgh/angular-file-upload) as an asset in the Rails Asset Pipeline. 6 | 7 | ## Installation 8 | 9 | Add this line to your application's Gemfile: 10 | 11 | ```ruby 12 | gem 'angularjs-file-upload-rails', '~> 2.4.1' 13 | ``` 14 | 15 | And then execute: 16 | 17 | ``` 18 | $ bundle install 19 | ``` 20 | 21 | Or install it yourself as: 22 | 23 | ``` 24 | $ gem install angularjs-file-upload 25 | ``` 26 | 27 | ## Usage 28 | 29 | Include it in your JavaScript manifest (e.g. `application.js`) 30 | 31 | ```javascript 32 | //= require angularjs-file-upload 33 | ``` 34 | \* *be sure that angular is required before angularjs-file-upload* 35 | 36 | ## Read more 37 | 38 | read more about the options in [angular-file-upload-wiki](https://github.com/nervgh/angular-file-upload/wiki/Introduction) 39 | 40 | ## Basic example 41 | 42 | \* *assuming that you have setup an ```angularjs``` correctly in your rails app 43 | 44 | ```ruby 45 | gem 'angularjs-file-upload-rails', '~> 2.4.1' 46 | gem 'carrierwave' 47 | gem 'rails', '4.2.5.1' 48 | ``` 49 | 50 | Setup your carrierwave gem as discribed in the [carrierwave-readme](https://github.com/carrierwaveuploader/carrierwave) 51 | 52 | add ```angularjs-file-upload-rails``` to your gem file 53 | add 54 | 55 | ```javascript 56 | //= require angularjs-file-upload 57 | ``` 58 | 59 | to ```application.js``` 60 | 61 | in the angular file 62 | 63 | ```javascript 64 | angular 65 | .module('app', ['angularFileUpload']) 66 | .controller('AppController', function($scope, FileUploader) { 67 | $scope.uploader = new FileUploader({url: ''}); 68 | }); 69 | ``` 70 | 71 | in your view 72 | 73 | ```html 74 |
75 |
76 |
77 |
78 |
79 | ``` 80 | 81 | in your controller 82 | 83 | ```ruby 84 | class UsersController < ApplicationController 85 | 86 | def create 87 | user = User.new() 88 | //other params 89 | user.picture = params[:file] 90 | user.save 91 | end 92 | 93 | end 94 | ``` 95 | 96 | ## Contributing 97 | 98 | 1. Fork it 99 | 2. Create your feature branch (`git checkout -b my-new-feature`) 100 | 3. Commit your changes (`git commit -m 'Add some feature'`) 101 | 4. Push to the branch (`git push origin my-new-feature`) 102 | 5. Create new Pull Request 103 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | -------------------------------------------------------------------------------- /angularjs-file-upload.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | require File.expand_path('../lib/angularjs-file-upload/version', __FILE__) 3 | 4 | Gem::Specification.new do |spec| 5 | spec.name = "angularjs-file-upload-rails" 6 | spec.version = AngularjsFileUpload::VERSION 7 | spec.authors = ["Marthyn Olthof"] 8 | spec.email = ["info@voxapex.com"] 9 | spec.description = %q{Angular File Upload is a module for the AngularJS framework.} 10 | spec.summary = %q{It supports drag-n-drop upload, upload progress, validation filters and a file upload queue. 11 | It supports native HTML5 uploads, but degrades to a legacy iframe upload method for older browsers. 12 | Works with any server side platform which supports standard HTML form uploads.} 13 | spec.homepage = "https://github.com/marthyn/angularjs-file-upload-rails" 14 | spec.license = "MIT" 15 | 16 | spec.files = `git ls-files`.split($/) 17 | spec.require_paths = ["lib", "app"] 18 | 19 | spec.add_development_dependency "bundler", "~> 1.3" 20 | spec.add_development_dependency "rails", ">= 3.1" 21 | spec.add_development_dependency "rake" 22 | end 23 | -------------------------------------------------------------------------------- /app/assets/javascripts/angularjs-file-upload.js: -------------------------------------------------------------------------------- 1 | /* 2 | angular-file-upload v2.4.0 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 = { 134 | "name": "angularFileUpload" 135 | }; 136 | 137 | /***/ }, 138 | /* 2 */ 139 | /***/ function(module, exports) { 140 | 141 | 'use strict'; 142 | 143 | Object.defineProperty(exports, "__esModule", { 144 | value: true 145 | }); 146 | exports.default = { 147 | url: '/', 148 | alias: 'file', 149 | headers: {}, 150 | queue: [], 151 | progress: 0, 152 | autoUpload: false, 153 | removeAfterUpload: false, 154 | method: 'POST', 155 | filters: [], 156 | formData: [], 157 | queueLimit: Number.MAX_VALUE, 158 | withCredentials: false, 159 | disableMultipart: false 160 | }; 161 | 162 | /***/ }, 163 | /* 3 */ 164 | /***/ function(module, exports, __webpack_require__) { 165 | 166 | 'use strict'; 167 | 168 | Object.defineProperty(exports, "__esModule", { 169 | value: true 170 | }); 171 | 172 | 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"); } }; }(); 173 | 174 | exports.default = __identity; 175 | 176 | var _config = __webpack_require__(1); 177 | 178 | var _config2 = _interopRequireDefault(_config); 179 | 180 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 181 | 182 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 183 | 184 | var _angular = angular; 185 | var bind = _angular.bind; 186 | var copy = _angular.copy; 187 | var extend = _angular.extend; 188 | var forEach = _angular.forEach; 189 | var isObject = _angular.isObject; 190 | var isNumber = _angular.isNumber; 191 | var isDefined = _angular.isDefined; 192 | var isArray = _angular.isArray; 193 | var isUndefined = _angular.isUndefined; 194 | var element = _angular.element; 195 | function __identity(fileUploaderOptions, $rootScope, $http, $window, $timeout, FileLikeObject, FileItem, Pipeline) { 196 | var File = $window.File; 197 | var FormData = $window.FormData; 198 | 199 | var FileUploader = function () { 200 | /********************** 201 | * PUBLIC 202 | **********************/ 203 | /** 204 | * Creates an instance of FileUploader 205 | * @param {Object} [options] 206 | * @constructor 207 | */ 208 | 209 | function FileUploader(options) { 210 | _classCallCheck(this, FileUploader); 211 | 212 | var settings = copy(fileUploaderOptions); 213 | 214 | extend(this, settings, options, { 215 | isUploading: false, 216 | _nextIndex: 0, 217 | _directives: { select: [], drop: [], over: [] } 218 | }); 219 | 220 | // add default filters 221 | this.filters.unshift({ name: 'queueLimit', fn: this._queueLimitFilter }); 222 | this.filters.unshift({ name: 'folder', fn: this._folderFilter }); 223 | } 224 | /** 225 | * Adds items to the queue 226 | * @param {File|HTMLInputElement|Object|FileList|Array} files 227 | * @param {Object} [options] 228 | * @param {Array|String} filters 229 | */ 230 | 231 | 232 | FileUploader.prototype.addToQueue = function addToQueue(files, options, filters) { 233 | var _this = this; 234 | 235 | var incomingQueue = this.isArrayLikeObject(files) ? Array.prototype.slice.call(files) : [files]; 236 | var arrayOfFilters = this._getFilters(filters); 237 | var count = this.queue.length; 238 | var addedFileItems = []; 239 | 240 | var next = function next() { 241 | var something = incomingQueue.shift(); 242 | 243 | if (isUndefined(something)) { 244 | return done(); 245 | } 246 | 247 | var fileLikeObject = _this.isFile(something) ? something : new FileLikeObject(something); 248 | var pipes = _this._convertFiltersToPipes(arrayOfFilters); 249 | var pipeline = new Pipeline(pipes); 250 | var onThrown = function onThrown(err) { 251 | var originalFilter = err.pipe.originalFilter; 252 | 253 | var _err$args = _slicedToArray(err.args, 2); 254 | 255 | var fileLikeObject = _err$args[0]; 256 | var options = _err$args[1]; 257 | 258 | _this._onWhenAddingFileFailed(fileLikeObject, originalFilter, options); 259 | next(); 260 | }; 261 | var onSuccessful = function onSuccessful(fileLikeObject, options) { 262 | var fileItem = new FileItem(_this, fileLikeObject, options); 263 | addedFileItems.push(fileItem); 264 | _this.queue.push(fileItem); 265 | _this._onAfterAddingFile(fileItem); 266 | next(); 267 | }; 268 | pipeline.onThrown = onThrown; 269 | pipeline.onSuccessful = onSuccessful; 270 | pipeline.exec(fileLikeObject, options); 271 | }; 272 | 273 | var done = function done() { 274 | if (_this.queue.length !== count) { 275 | _this._onAfterAddingAll(addedFileItems); 276 | _this.progress = _this._getTotalProgress(); 277 | } 278 | 279 | _this._render(); 280 | if (_this.autoUpload) _this.uploadAll(); 281 | }; 282 | 283 | next(); 284 | }; 285 | /** 286 | * Remove items from the queue. Remove last: index = -1 287 | * @param {FileItem|Number} value 288 | */ 289 | 290 | 291 | FileUploader.prototype.removeFromQueue = function removeFromQueue(value) { 292 | var index = this.getIndexOfItem(value); 293 | var item = this.queue[index]; 294 | if (item.isUploading) item.cancel(); 295 | this.queue.splice(index, 1); 296 | item._destroy(); 297 | this.progress = this._getTotalProgress(); 298 | }; 299 | /** 300 | * Clears the queue 301 | */ 302 | 303 | 304 | FileUploader.prototype.clearQueue = function clearQueue() { 305 | while (this.queue.length) { 306 | this.queue[0].remove(); 307 | } 308 | this.progress = 0; 309 | }; 310 | /** 311 | * Uploads a item from the queue 312 | * @param {FileItem|Number} value 313 | */ 314 | 315 | 316 | FileUploader.prototype.uploadItem = function uploadItem(value) { 317 | var index = this.getIndexOfItem(value); 318 | var item = this.queue[index]; 319 | var transport = this.isHTML5 ? '_xhrTransport' : '_iframeTransport'; 320 | 321 | item._prepareToUploading(); 322 | if (this.isUploading) return; 323 | 324 | this._onBeforeUploadItem(item); 325 | if (item.isCancel) return; 326 | 327 | item.isUploading = true; 328 | this.isUploading = true; 329 | this[transport](item); 330 | this._render(); 331 | }; 332 | /** 333 | * Cancels uploading of item from the queue 334 | * @param {FileItem|Number} value 335 | */ 336 | 337 | 338 | FileUploader.prototype.cancelItem = function cancelItem(value) { 339 | var _this2 = this; 340 | 341 | var index = this.getIndexOfItem(value); 342 | var item = this.queue[index]; 343 | var prop = this.isHTML5 ? '_xhr' : '_form'; 344 | if (!item) return; 345 | item.isCancel = true; 346 | if (item.isUploading) { 347 | // It will call this._onCancelItem() & this._onCompleteItem() asynchronously 348 | item[prop].abort(); 349 | } else { 350 | (function () { 351 | var dummy = [undefined, 0, {}]; 352 | var onNextTick = function onNextTick() { 353 | _this2._onCancelItem.apply(_this2, [item].concat(dummy)); 354 | _this2._onCompleteItem.apply(_this2, [item].concat(dummy)); 355 | }; 356 | $timeout(onNextTick); // Trigger callbacks asynchronously (setImmediate emulation) 357 | })(); 358 | } 359 | }; 360 | /** 361 | * Uploads all not uploaded items of queue 362 | */ 363 | 364 | 365 | FileUploader.prototype.uploadAll = function uploadAll() { 366 | var items = this.getNotUploadedItems().filter(function (item) { 367 | return !item.isUploading; 368 | }); 369 | if (!items.length) return; 370 | 371 | forEach(items, function (item) { 372 | return item._prepareToUploading(); 373 | }); 374 | items[0].upload(); 375 | }; 376 | /** 377 | * Cancels all uploads 378 | */ 379 | 380 | 381 | FileUploader.prototype.cancelAll = function cancelAll() { 382 | var items = this.getNotUploadedItems(); 383 | forEach(items, function (item) { 384 | return item.cancel(); 385 | }); 386 | }; 387 | /** 388 | * Returns "true" if value an instance of File 389 | * @param {*} value 390 | * @returns {Boolean} 391 | * @private 392 | */ 393 | 394 | 395 | FileUploader.prototype.isFile = function isFile(value) { 396 | return this.constructor.isFile(value); 397 | }; 398 | /** 399 | * Returns "true" if value an instance of FileLikeObject 400 | * @param {*} value 401 | * @returns {Boolean} 402 | * @private 403 | */ 404 | 405 | 406 | FileUploader.prototype.isFileLikeObject = function isFileLikeObject(value) { 407 | return this.constructor.isFileLikeObject(value); 408 | }; 409 | /** 410 | * Returns "true" if value is array like object 411 | * @param {*} value 412 | * @returns {Boolean} 413 | */ 414 | 415 | 416 | FileUploader.prototype.isArrayLikeObject = function isArrayLikeObject(value) { 417 | return this.constructor.isArrayLikeObject(value); 418 | }; 419 | /** 420 | * Returns a index of item from the queue 421 | * @param {Item|Number} value 422 | * @returns {Number} 423 | */ 424 | 425 | 426 | FileUploader.prototype.getIndexOfItem = function getIndexOfItem(value) { 427 | return isNumber(value) ? value : this.queue.indexOf(value); 428 | }; 429 | /** 430 | * Returns not uploaded items 431 | * @returns {Array} 432 | */ 433 | 434 | 435 | FileUploader.prototype.getNotUploadedItems = function getNotUploadedItems() { 436 | return this.queue.filter(function (item) { 437 | return !item.isUploaded; 438 | }); 439 | }; 440 | /** 441 | * Returns items ready for upload 442 | * @returns {Array} 443 | */ 444 | 445 | 446 | FileUploader.prototype.getReadyItems = function getReadyItems() { 447 | return this.queue.filter(function (item) { 448 | return item.isReady && !item.isUploading; 449 | }).sort(function (item1, item2) { 450 | return item1.index - item2.index; 451 | }); 452 | }; 453 | /** 454 | * Destroys instance of FileUploader 455 | */ 456 | 457 | 458 | FileUploader.prototype.destroy = function destroy() { 459 | var _this3 = this; 460 | 461 | forEach(this._directives, function (key) { 462 | forEach(_this3._directives[key], function (object) { 463 | object.destroy(); 464 | }); 465 | }); 466 | }; 467 | /** 468 | * Callback 469 | * @param {Array} fileItems 470 | */ 471 | 472 | 473 | FileUploader.prototype.onAfterAddingAll = function onAfterAddingAll(fileItems) {}; 474 | /** 475 | * Callback 476 | * @param {FileItem} fileItem 477 | */ 478 | 479 | 480 | FileUploader.prototype.onAfterAddingFile = function onAfterAddingFile(fileItem) {}; 481 | /** 482 | * Callback 483 | * @param {File|Object} item 484 | * @param {Object} filter 485 | * @param {Object} options 486 | */ 487 | 488 | 489 | FileUploader.prototype.onWhenAddingFileFailed = function onWhenAddingFileFailed(item, filter, options) {}; 490 | /** 491 | * Callback 492 | * @param {FileItem} fileItem 493 | */ 494 | 495 | 496 | FileUploader.prototype.onBeforeUploadItem = function onBeforeUploadItem(fileItem) {}; 497 | /** 498 | * Callback 499 | * @param {FileItem} fileItem 500 | * @param {Number} progress 501 | */ 502 | 503 | 504 | FileUploader.prototype.onProgressItem = function onProgressItem(fileItem, progress) {}; 505 | /** 506 | * Callback 507 | * @param {Number} progress 508 | */ 509 | 510 | 511 | FileUploader.prototype.onProgressAll = function onProgressAll(progress) {}; 512 | /** 513 | * Callback 514 | * @param {FileItem} item 515 | * @param {*} response 516 | * @param {Number} status 517 | * @param {Object} headers 518 | */ 519 | 520 | 521 | FileUploader.prototype.onSuccessItem = function onSuccessItem(item, response, status, headers) {}; 522 | /** 523 | * Callback 524 | * @param {FileItem} item 525 | * @param {*} response 526 | * @param {Number} status 527 | * @param {Object} headers 528 | */ 529 | 530 | 531 | FileUploader.prototype.onErrorItem = function onErrorItem(item, response, status, headers) {}; 532 | /** 533 | * Callback 534 | * @param {FileItem} item 535 | * @param {*} response 536 | * @param {Number} status 537 | * @param {Object} headers 538 | */ 539 | 540 | 541 | FileUploader.prototype.onCancelItem = function onCancelItem(item, response, status, headers) {}; 542 | /** 543 | * Callback 544 | * @param {FileItem} item 545 | * @param {*} response 546 | * @param {Number} status 547 | * @param {Object} headers 548 | */ 549 | 550 | 551 | FileUploader.prototype.onCompleteItem = function onCompleteItem(item, response, status, headers) {}; 552 | /** 553 | * Callback 554 | */ 555 | 556 | 557 | FileUploader.prototype.onCompleteAll = function onCompleteAll() {}; 558 | /********************** 559 | * PRIVATE 560 | **********************/ 561 | /** 562 | * Returns the total progress 563 | * @param {Number} [value] 564 | * @returns {Number} 565 | * @private 566 | */ 567 | 568 | 569 | FileUploader.prototype._getTotalProgress = function _getTotalProgress(value) { 570 | if (this.removeAfterUpload) return value || 0; 571 | 572 | var notUploaded = this.getNotUploadedItems().length; 573 | var uploaded = notUploaded ? this.queue.length - notUploaded : this.queue.length; 574 | var ratio = 100 / this.queue.length; 575 | var current = (value || 0) * ratio / 100; 576 | 577 | return Math.round(uploaded * ratio + current); 578 | }; 579 | /** 580 | * Returns array of filters 581 | * @param {Array|String} filters 582 | * @returns {Array} 583 | * @private 584 | */ 585 | 586 | 587 | FileUploader.prototype._getFilters = function _getFilters(filters) { 588 | if (!filters) return this.filters; 589 | if (isArray(filters)) return filters; 590 | var names = filters.match(/[^\s,]+/g); 591 | return this.filters.filter(function (filter) { 592 | return names.indexOf(filter.name) !== -1; 593 | }); 594 | }; 595 | /** 596 | * @param {Array} filters 597 | * @returns {Array} 598 | * @private 599 | */ 600 | 601 | 602 | FileUploader.prototype._convertFiltersToPipes = function _convertFiltersToPipes(filters) { 603 | var _this4 = this; 604 | 605 | return filters.map(function (filter) { 606 | var fn = bind(_this4, filter.fn); 607 | fn.isAsync = filter.fn.length === 3; 608 | fn.originalFilter = filter; 609 | return fn; 610 | }); 611 | }; 612 | /** 613 | * Updates html 614 | * @private 615 | */ 616 | 617 | 618 | FileUploader.prototype._render = function _render() { 619 | if (!$rootScope.$$phase) $rootScope.$apply(); 620 | }; 621 | /** 622 | * Returns "true" if item is a file (not folder) 623 | * @param {File|FileLikeObject} item 624 | * @returns {Boolean} 625 | * @private 626 | */ 627 | 628 | 629 | FileUploader.prototype._folderFilter = function _folderFilter(item) { 630 | return !!(item.size || item.type); 631 | }; 632 | /** 633 | * Returns "true" if the limit has not been reached 634 | * @returns {Boolean} 635 | * @private 636 | */ 637 | 638 | 639 | FileUploader.prototype._queueLimitFilter = function _queueLimitFilter() { 640 | return this.queue.length < this.queueLimit; 641 | }; 642 | /** 643 | * Checks whether upload successful 644 | * @param {Number} status 645 | * @returns {Boolean} 646 | * @private 647 | */ 648 | 649 | 650 | FileUploader.prototype._isSuccessCode = function _isSuccessCode(status) { 651 | return status >= 200 && status < 300 || status === 304; 652 | }; 653 | /** 654 | * Transforms the server response 655 | * @param {*} response 656 | * @param {Object} headers 657 | * @returns {*} 658 | * @private 659 | */ 660 | 661 | 662 | FileUploader.prototype._transformResponse = function _transformResponse(response, headers) { 663 | var headersGetter = this._headersGetter(headers); 664 | forEach($http.defaults.transformResponse, function (transformFn) { 665 | response = transformFn(response, headersGetter); 666 | }); 667 | return response; 668 | }; 669 | /** 670 | * Parsed response headers 671 | * @param headers 672 | * @returns {Object} 673 | * @see https://github.com/angular/angular.js/blob/master/src/ng/http.js 674 | * @private 675 | */ 676 | 677 | 678 | FileUploader.prototype._parseHeaders = function _parseHeaders(headers) { 679 | var parsed = {}, 680 | key, 681 | val, 682 | i; 683 | 684 | if (!headers) return parsed; 685 | 686 | forEach(headers.split('\n'), function (line) { 687 | i = line.indexOf(':'); 688 | key = line.slice(0, i).trim().toLowerCase(); 689 | val = line.slice(i + 1).trim(); 690 | 691 | if (key) { 692 | parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; 693 | } 694 | }); 695 | 696 | return parsed; 697 | }; 698 | /** 699 | * Returns function that returns headers 700 | * @param {Object} parsedHeaders 701 | * @returns {Function} 702 | * @private 703 | */ 704 | 705 | 706 | FileUploader.prototype._headersGetter = function _headersGetter(parsedHeaders) { 707 | return function (name) { 708 | if (name) { 709 | return parsedHeaders[name.toLowerCase()] || null; 710 | } 711 | return parsedHeaders; 712 | }; 713 | }; 714 | /** 715 | * The XMLHttpRequest transport 716 | * @param {FileItem} item 717 | * @private 718 | */ 719 | 720 | 721 | FileUploader.prototype._xhrTransport = function _xhrTransport(item) { 722 | var _this5 = this; 723 | 724 | var xhr = item._xhr = new XMLHttpRequest(); 725 | var sendable; 726 | 727 | if (!item.disableMultipart) { 728 | sendable = new FormData(); 729 | forEach(item.formData, function (obj) { 730 | forEach(obj, function (value, key) { 731 | sendable.append(key, value); 732 | }); 733 | }); 734 | 735 | sendable.append(item.alias, item._file, item.file.name); 736 | } else { 737 | sendable = item._file; 738 | } 739 | 740 | if (typeof item._file.size != 'number') { 741 | throw new TypeError('The file specified is no longer valid'); 742 | } 743 | 744 | xhr.upload.onprogress = function (event) { 745 | var progress = Math.round(event.lengthComputable ? event.loaded * 100 / event.total : 0); 746 | _this5._onProgressItem(item, progress); 747 | }; 748 | 749 | xhr.onload = function () { 750 | var headers = _this5._parseHeaders(xhr.getAllResponseHeaders()); 751 | var response = _this5._transformResponse(xhr.response, headers); 752 | var gist = _this5._isSuccessCode(xhr.status) ? 'Success' : 'Error'; 753 | var method = '_on' + gist + 'Item'; 754 | _this5[method](item, response, xhr.status, headers); 755 | _this5._onCompleteItem(item, response, xhr.status, headers); 756 | }; 757 | 758 | xhr.onerror = function () { 759 | var headers = _this5._parseHeaders(xhr.getAllResponseHeaders()); 760 | var response = _this5._transformResponse(xhr.response, headers); 761 | _this5._onErrorItem(item, response, xhr.status, headers); 762 | _this5._onCompleteItem(item, response, xhr.status, headers); 763 | }; 764 | 765 | xhr.onabort = function () { 766 | var headers = _this5._parseHeaders(xhr.getAllResponseHeaders()); 767 | var response = _this5._transformResponse(xhr.response, headers); 768 | _this5._onCancelItem(item, response, xhr.status, headers); 769 | _this5._onCompleteItem(item, response, xhr.status, headers); 770 | }; 771 | 772 | xhr.open(item.method, item.url, true); 773 | 774 | xhr.withCredentials = item.withCredentials; 775 | 776 | forEach(item.headers, function (value, name) { 777 | xhr.setRequestHeader(name, value); 778 | }); 779 | 780 | xhr.send(sendable); 781 | }; 782 | /** 783 | * The IFrame transport 784 | * @param {FileItem} item 785 | * @private 786 | */ 787 | 788 | 789 | FileUploader.prototype._iframeTransport = function _iframeTransport(item) { 790 | var _this6 = this; 791 | 792 | var form = element('
'); 793 | var iframe = element('