angularjs-file-upload-rails 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +40 -0
- data/Rakefile +1 -0
- data/angularjs-file-upload.gemspec +22 -0
- data/app/assets/javascripts/angularjs-file-upload.js +1289 -0
- data/lib/angularjs-file-upload-rails.rb +2 -0
- data/lib/angularjs-file-upload/rails.rb +6 -0
- data/lib/angularjs-file-upload/version.rb +3 -0
- metadata +99 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: eebaf468ac5a74e0da00f5db6a8d40267a39b712
|
|
4
|
+
data.tar.gz: a700b2c4e863e97ef119d064cba43fc64933b68f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: aaa843b398eae0f2d35372b1c8a8a15ce5b2f9939a05e61d11ff85bb505f47a820a7c57b695f46ab5fe4bde28183e581d3e2689410d81aab39dd73725b606a41
|
|
7
|
+
data.tar.gz: 4ff131e06d45a8d509442e3a39ab03dd74048d02560bab2aea4d6763b06be9d331bf5458ab521741a548d169229cd9a2f4e0d12dd88a28557b516ef5f34b1847
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
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.
|
data/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Angularjs::File::Upload
|
|
2
|
+
|
|
3
|
+
A gem that uses [angular-file-upload](https://github.com/nervgh/angular-file-upload) as an asset in the Rails Asset Pipeline.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add this line to your application's Gemfile:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem 'angularjs-file-upload', '~> 1.0.1'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
And then execute:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
$ bundle install
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or install it yourself as:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
$ gem install angularjs-file-upload
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
Include it in your JavaScript manifest (e.g. `application.js`)
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
//= require angularjs-file-upload
|
|
31
|
+
```
|
|
32
|
+
\* *be sure that angular is required before angularjs-file-upload*
|
|
33
|
+
|
|
34
|
+
## Contributing
|
|
35
|
+
|
|
36
|
+
1. Fork it
|
|
37
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
38
|
+
3. Commit your changes (`git commit -m 'Add some feature'`)
|
|
39
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
40
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
|
@@ -0,0 +1,22 @@
|
|
|
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 = ["Marthyn@live.nl"]
|
|
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"]
|
|
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
|
|
@@ -0,0 +1,1289 @@
|
|
|
1
|
+
/*
|
|
2
|
+
angular-file-upload v1.0.1
|
|
3
|
+
https://github.com/nervgh/angular-file-upload
|
|
4
|
+
*/
|
|
5
|
+
(function(angular, factory) {
|
|
6
|
+
if (typeof define === 'function' && define.amd) {
|
|
7
|
+
define('angular-file-upload', ['angular'], function(angular) {
|
|
8
|
+
return factory(angular);
|
|
9
|
+
});
|
|
10
|
+
} else {
|
|
11
|
+
return factory(angular);
|
|
12
|
+
}
|
|
13
|
+
}(typeof angular === 'undefined' ? null : angular || null, function(angular) {
|
|
14
|
+
|
|
15
|
+
var module = angular.module('angularFileUpload', []);
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Classes
|
|
20
|
+
*
|
|
21
|
+
* FileUploader
|
|
22
|
+
* FileUploader.FileLikeObject
|
|
23
|
+
* FileUploader.FileItem
|
|
24
|
+
* FileUploader.FileDirective
|
|
25
|
+
* FileUploader.FileSelect
|
|
26
|
+
* FileUploader.FileDrop
|
|
27
|
+
* FileUploader.FileOver
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
module
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
.value('fileUploaderOptions', {
|
|
34
|
+
url: '/',
|
|
35
|
+
alias: 'file',
|
|
36
|
+
headers: {},
|
|
37
|
+
queue: [],
|
|
38
|
+
progress: 0,
|
|
39
|
+
autoUpload: false,
|
|
40
|
+
removeAfterUpload: false,
|
|
41
|
+
method: 'POST',
|
|
42
|
+
filters: [],
|
|
43
|
+
formData: [],
|
|
44
|
+
queueLimit: Number.MAX_VALUE,
|
|
45
|
+
withCredentials: false
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
.factory('FileUploader', ['fileUploaderOptions', '$rootScope', '$http', '$window', '$compile',
|
|
50
|
+
function(fileUploaderOptions, $rootScope, $http, $window, $compile) {
|
|
51
|
+
/**
|
|
52
|
+
* Creates an instance of FileUploader
|
|
53
|
+
* @param {Object} [options]
|
|
54
|
+
* @constructor
|
|
55
|
+
*/
|
|
56
|
+
function FileUploader(options) {
|
|
57
|
+
var settings = angular.copy(fileUploaderOptions);
|
|
58
|
+
angular.extend(this, settings, options, {
|
|
59
|
+
isUploading: false,
|
|
60
|
+
_nextIndex: 0,
|
|
61
|
+
_failFilterIndex: -1,
|
|
62
|
+
_directives: {select: [], drop: [], over: []}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// add default filters
|
|
66
|
+
this.filters.unshift({name: 'queueLimit', fn: this._queueLimitFilter});
|
|
67
|
+
this.filters.unshift({name: 'folder', fn: this._folderFilter});
|
|
68
|
+
}
|
|
69
|
+
/**********************
|
|
70
|
+
* PUBLIC
|
|
71
|
+
**********************/
|
|
72
|
+
/**
|
|
73
|
+
* Checks a support the html5 uploader
|
|
74
|
+
* @returns {Boolean}
|
|
75
|
+
* @readonly
|
|
76
|
+
*/
|
|
77
|
+
FileUploader.prototype.isHTML5 = !!($window.File && $window.FormData);
|
|
78
|
+
/**
|
|
79
|
+
* Adds items to the queue
|
|
80
|
+
* @param {FileList|File|HTMLInputElement} files
|
|
81
|
+
* @param {Object} [options]
|
|
82
|
+
* @param {Array<Function>|String} filters
|
|
83
|
+
*/
|
|
84
|
+
FileUploader.prototype.addToQueue = function(files, options, filters) {
|
|
85
|
+
var list = angular.isElement(files) ? [files]: files;
|
|
86
|
+
var arrayOfFilters = this._getFilters(filters);
|
|
87
|
+
var count = this.queue.length;
|
|
88
|
+
var addedFileItems = [];
|
|
89
|
+
|
|
90
|
+
angular.forEach(list, function(file) {
|
|
91
|
+
var item = this._getFileOrFileLikeObject(file);
|
|
92
|
+
|
|
93
|
+
if (this._isValidFile(item, arrayOfFilters, options)) {
|
|
94
|
+
var input = this.isFile(item) ? null : file;
|
|
95
|
+
var fileItem = new FileUploader.FileItem(this, item, options, input);
|
|
96
|
+
addedFileItems.push(fileItem);
|
|
97
|
+
this.queue.push(fileItem);
|
|
98
|
+
this._onAfterAddingFile(fileItem);
|
|
99
|
+
} else {
|
|
100
|
+
var filter = this.filters[this._failFilterIndex];
|
|
101
|
+
this._onWhenAddingFileFailed(item, filter, options);
|
|
102
|
+
}
|
|
103
|
+
}, this);
|
|
104
|
+
|
|
105
|
+
if(this.queue.length !== count) {
|
|
106
|
+
this._onAfterAddingAll(addedFileItems);
|
|
107
|
+
this.progress = this._getTotalProgress();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
this._render();
|
|
111
|
+
if (this.autoUpload) this.uploadAll();
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* Remove items from the queue. Remove last: index = -1
|
|
115
|
+
* @param {FileItem|Number} value
|
|
116
|
+
*/
|
|
117
|
+
FileUploader.prototype.removeFromQueue = function(value) {
|
|
118
|
+
var index = this.getIndexOfItem(value);
|
|
119
|
+
var item = this.queue[index];
|
|
120
|
+
if (item.isUploading) item.cancel();
|
|
121
|
+
this.queue.splice(index, 1);
|
|
122
|
+
item._destroy();
|
|
123
|
+
this.progress = this._getTotalProgress();
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Clears the queue
|
|
127
|
+
*/
|
|
128
|
+
FileUploader.prototype.clearQueue = function() {
|
|
129
|
+
while(this.queue.length) {
|
|
130
|
+
this.queue[0].remove();
|
|
131
|
+
}
|
|
132
|
+
this.progress = 0;
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Uploads a item from the queue
|
|
136
|
+
* @param {FileItem|Number} value
|
|
137
|
+
*/
|
|
138
|
+
FileUploader.prototype.uploadItem = function(value) {
|
|
139
|
+
var index = this.getIndexOfItem(value);
|
|
140
|
+
var item = this.queue[index];
|
|
141
|
+
var transport = this.isHTML5 ? '_xhrTransport' : '_iframeTransport';
|
|
142
|
+
|
|
143
|
+
item._prepareToUploading();
|
|
144
|
+
if(this.isUploading) return;
|
|
145
|
+
|
|
146
|
+
this.isUploading = true;
|
|
147
|
+
this[transport](item);
|
|
148
|
+
};
|
|
149
|
+
/**
|
|
150
|
+
* Cancels uploading of item from the queue
|
|
151
|
+
* @param {FileItem|Number} value
|
|
152
|
+
*/
|
|
153
|
+
FileUploader.prototype.cancelItem = function(value) {
|
|
154
|
+
var index = this.getIndexOfItem(value);
|
|
155
|
+
var item = this.queue[index];
|
|
156
|
+
var prop = this.isHTML5 ? '_xhr' : '_form';
|
|
157
|
+
if (item && item.isUploading) item[prop].abort();
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* Uploads all not uploaded items of queue
|
|
161
|
+
*/
|
|
162
|
+
FileUploader.prototype.uploadAll = function() {
|
|
163
|
+
var items = this.getNotUploadedItems().filter(function(item) {
|
|
164
|
+
return !item.isUploading;
|
|
165
|
+
});
|
|
166
|
+
if (!items.length) return;
|
|
167
|
+
|
|
168
|
+
angular.forEach(items, function(item) {
|
|
169
|
+
item._prepareToUploading();
|
|
170
|
+
});
|
|
171
|
+
items[0].upload();
|
|
172
|
+
};
|
|
173
|
+
/**
|
|
174
|
+
* Cancels all uploads
|
|
175
|
+
*/
|
|
176
|
+
FileUploader.prototype.cancelAll = function() {
|
|
177
|
+
var items = this.getNotUploadedItems();
|
|
178
|
+
angular.forEach(items, function(item) {
|
|
179
|
+
item.cancel();
|
|
180
|
+
});
|
|
181
|
+
};
|
|
182
|
+
/**
|
|
183
|
+
* Returns "true" if value an instance of File
|
|
184
|
+
* @param {*} value
|
|
185
|
+
* @returns {Boolean}
|
|
186
|
+
* @private
|
|
187
|
+
*/
|
|
188
|
+
FileUploader.prototype.isFile = function(value) {
|
|
189
|
+
var fn = $window.File;
|
|
190
|
+
return (fn && value instanceof fn);
|
|
191
|
+
};
|
|
192
|
+
/**
|
|
193
|
+
* Returns "true" if value an instance of FileLikeObject
|
|
194
|
+
* @param {*} value
|
|
195
|
+
* @returns {Boolean}
|
|
196
|
+
* @private
|
|
197
|
+
*/
|
|
198
|
+
FileUploader.prototype.isFileLikeObject = function(value) {
|
|
199
|
+
return value instanceof FileUploader.FileLikeObject;
|
|
200
|
+
};
|
|
201
|
+
/**
|
|
202
|
+
* Returns a index of item from the queue
|
|
203
|
+
* @param {Item|Number} value
|
|
204
|
+
* @returns {Number}
|
|
205
|
+
*/
|
|
206
|
+
FileUploader.prototype.getIndexOfItem = function(value) {
|
|
207
|
+
return angular.isNumber(value) ? value : this.queue.indexOf(value);
|
|
208
|
+
};
|
|
209
|
+
/**
|
|
210
|
+
* Returns not uploaded items
|
|
211
|
+
* @returns {Array}
|
|
212
|
+
*/
|
|
213
|
+
FileUploader.prototype.getNotUploadedItems = function() {
|
|
214
|
+
return this.queue.filter(function(item) {
|
|
215
|
+
return !item.isUploaded;
|
|
216
|
+
});
|
|
217
|
+
};
|
|
218
|
+
/**
|
|
219
|
+
* Returns items ready for upload
|
|
220
|
+
* @returns {Array}
|
|
221
|
+
*/
|
|
222
|
+
FileUploader.prototype.getReadyItems = function() {
|
|
223
|
+
return this.queue
|
|
224
|
+
.filter(function(item) {
|
|
225
|
+
return (item.isReady && !item.isUploading);
|
|
226
|
+
})
|
|
227
|
+
.sort(function(item1, item2) {
|
|
228
|
+
return item1.index - item2.index;
|
|
229
|
+
});
|
|
230
|
+
};
|
|
231
|
+
/**
|
|
232
|
+
* Destroys instance of FileUploader
|
|
233
|
+
*/
|
|
234
|
+
FileUploader.prototype.destroy = function() {
|
|
235
|
+
angular.forEach(this._directives, function(key) {
|
|
236
|
+
angular.forEach(this._directives[key], function(object) {
|
|
237
|
+
object.destroy();
|
|
238
|
+
}, this);
|
|
239
|
+
}, this);
|
|
240
|
+
};
|
|
241
|
+
/**
|
|
242
|
+
* Callback
|
|
243
|
+
* @param {Array} fileItems
|
|
244
|
+
*/
|
|
245
|
+
FileUploader.prototype.onAfterAddingAll = function(fileItems) {};
|
|
246
|
+
/**
|
|
247
|
+
* Callback
|
|
248
|
+
* @param {FileItem} fileItem
|
|
249
|
+
*/
|
|
250
|
+
FileUploader.prototype.onAfterAddingFile = function(fileItem) {};
|
|
251
|
+
/**
|
|
252
|
+
* Callback
|
|
253
|
+
* @param {File|Object} item
|
|
254
|
+
* @param {Object} filter
|
|
255
|
+
* @param {Object} options
|
|
256
|
+
* @private
|
|
257
|
+
*/
|
|
258
|
+
FileUploader.prototype.onWhenAddingFileFailed = function(item, filter, options) {};
|
|
259
|
+
/**
|
|
260
|
+
* Callback
|
|
261
|
+
* @param {FileItem} fileItem
|
|
262
|
+
*/
|
|
263
|
+
FileUploader.prototype.onBeforeUploadItem = function(fileItem) {};
|
|
264
|
+
/**
|
|
265
|
+
* Callback
|
|
266
|
+
* @param {FileItem} fileItem
|
|
267
|
+
* @param {Number} progress
|
|
268
|
+
*/
|
|
269
|
+
FileUploader.prototype.onProgressItem = function(fileItem, progress) {};
|
|
270
|
+
/**
|
|
271
|
+
* Callback
|
|
272
|
+
* @param {Number} progress
|
|
273
|
+
*/
|
|
274
|
+
FileUploader.prototype.onProgressAll = function(progress) {};
|
|
275
|
+
/**
|
|
276
|
+
* Callback
|
|
277
|
+
* @param {FileItem} item
|
|
278
|
+
* @param {*} response
|
|
279
|
+
* @param {Number} status
|
|
280
|
+
* @param {Object} headers
|
|
281
|
+
*/
|
|
282
|
+
FileUploader.prototype.onSuccessItem = function(item, response, status, headers) {};
|
|
283
|
+
/**
|
|
284
|
+
* Callback
|
|
285
|
+
* @param {FileItem} item
|
|
286
|
+
* @param {*} response
|
|
287
|
+
* @param {Number} status
|
|
288
|
+
* @param {Object} headers
|
|
289
|
+
*/
|
|
290
|
+
FileUploader.prototype.onErrorItem = function(item, response, status, headers) {};
|
|
291
|
+
/**
|
|
292
|
+
* Callback
|
|
293
|
+
* @param {FileItem} item
|
|
294
|
+
* @param {*} response
|
|
295
|
+
* @param {Number} status
|
|
296
|
+
* @param {Object} headers
|
|
297
|
+
*/
|
|
298
|
+
FileUploader.prototype.onCancelItem = function(item, response, status, headers) {};
|
|
299
|
+
/**
|
|
300
|
+
* Callback
|
|
301
|
+
* @param {FileItem} item
|
|
302
|
+
* @param {*} response
|
|
303
|
+
* @param {Number} status
|
|
304
|
+
* @param {Object} headers
|
|
305
|
+
*/
|
|
306
|
+
FileUploader.prototype.onCompleteItem = function(item, response, status, headers) {};
|
|
307
|
+
/**
|
|
308
|
+
* Callback
|
|
309
|
+
*/
|
|
310
|
+
FileUploader.prototype.onCompleteAll = function() {};
|
|
311
|
+
/**********************
|
|
312
|
+
* PRIVATE
|
|
313
|
+
**********************/
|
|
314
|
+
/**
|
|
315
|
+
* Returns the total progress
|
|
316
|
+
* @param {Number} [value]
|
|
317
|
+
* @returns {Number}
|
|
318
|
+
* @private
|
|
319
|
+
*/
|
|
320
|
+
FileUploader.prototype._getTotalProgress = function(value) {
|
|
321
|
+
if(this.removeAfterUpload) return value || 0;
|
|
322
|
+
|
|
323
|
+
var notUploaded = this.getNotUploadedItems().length;
|
|
324
|
+
var uploaded = notUploaded ? this.queue.length - notUploaded : this.queue.length;
|
|
325
|
+
var ratio = 100 / this.queue.length;
|
|
326
|
+
var current = (value || 0) * ratio / 100;
|
|
327
|
+
|
|
328
|
+
return Math.round(uploaded * ratio + current);
|
|
329
|
+
};
|
|
330
|
+
/**
|
|
331
|
+
* Returns array of filters
|
|
332
|
+
* @param {Array<Function>|String} filters
|
|
333
|
+
* @returns {Array<Function>}
|
|
334
|
+
* @private
|
|
335
|
+
*/
|
|
336
|
+
FileUploader.prototype._getFilters = function(filters) {
|
|
337
|
+
if (angular.isUndefined(filters)) return this.filters;
|
|
338
|
+
if (angular.isArray(filters)) return filters;
|
|
339
|
+
var names = filters.split(/\s*,/);
|
|
340
|
+
return this.filters.filter(function(filter) {
|
|
341
|
+
return names.indexOf(filter.name) !== -1;
|
|
342
|
+
}, this);
|
|
343
|
+
};
|
|
344
|
+
/**
|
|
345
|
+
* Updates html
|
|
346
|
+
* @private
|
|
347
|
+
*/
|
|
348
|
+
FileUploader.prototype._render = function() {
|
|
349
|
+
if (!$rootScope.$$phase) $rootScope.$apply();
|
|
350
|
+
};
|
|
351
|
+
/**
|
|
352
|
+
* Returns "true" if item is a file (not folder)
|
|
353
|
+
* @param {File|FileLikeObject} item
|
|
354
|
+
* @returns {Boolean}
|
|
355
|
+
* @private
|
|
356
|
+
*/
|
|
357
|
+
FileUploader.prototype._folderFilter = function(item) {
|
|
358
|
+
return !!(item.size || item.type);
|
|
359
|
+
};
|
|
360
|
+
/**
|
|
361
|
+
* Returns "true" if the limit has not been reached
|
|
362
|
+
* @returns {Boolean}
|
|
363
|
+
* @private
|
|
364
|
+
*/
|
|
365
|
+
FileUploader.prototype._queueLimitFilter = function() {
|
|
366
|
+
return this.queue.length < this.queueLimit;
|
|
367
|
+
};
|
|
368
|
+
/**
|
|
369
|
+
* Returns "true" if file pass all filters
|
|
370
|
+
* @param {File|Object} file
|
|
371
|
+
* @param {Array<Function>} filters
|
|
372
|
+
* @param {Object} options
|
|
373
|
+
* @returns {Boolean}
|
|
374
|
+
* @private
|
|
375
|
+
*/
|
|
376
|
+
FileUploader.prototype._isValidFile = function(file, filters, options) {
|
|
377
|
+
this._failFilterIndex = -1;
|
|
378
|
+
return !filters.length ? true : filters.every(function(filter) {
|
|
379
|
+
this._failFilterIndex++;
|
|
380
|
+
return filter.fn.call(this, file, options);
|
|
381
|
+
}, this);
|
|
382
|
+
};
|
|
383
|
+
/**
|
|
384
|
+
* Returns a file or a file-like object
|
|
385
|
+
* @param {File|HTMLInputElement} some
|
|
386
|
+
* @returns {File|Object}
|
|
387
|
+
* @private
|
|
388
|
+
*/
|
|
389
|
+
FileUploader.prototype._getFileOrFileLikeObject = function(some) {
|
|
390
|
+
if (this.isFile(some) || this.isFileLikeObject(some)) return some;
|
|
391
|
+
return new FileUploader.FileLikeObject(some.value);
|
|
392
|
+
};
|
|
393
|
+
/**
|
|
394
|
+
* Checks whether upload successful
|
|
395
|
+
* @param {Number} status
|
|
396
|
+
* @returns {Boolean}
|
|
397
|
+
* @private
|
|
398
|
+
*/
|
|
399
|
+
FileUploader.prototype._isSuccessCode = function(status) {
|
|
400
|
+
return (status >= 200 && status < 300) || status === 304;
|
|
401
|
+
};
|
|
402
|
+
/**
|
|
403
|
+
* Transforms the server response
|
|
404
|
+
* @param {*} response
|
|
405
|
+
* @returns {*}
|
|
406
|
+
* @private
|
|
407
|
+
*/
|
|
408
|
+
FileUploader.prototype._transformResponse = function(response) {
|
|
409
|
+
angular.forEach($http.defaults.transformResponse, function(transformFn) {
|
|
410
|
+
response = transformFn(response);
|
|
411
|
+
});
|
|
412
|
+
return response;
|
|
413
|
+
};
|
|
414
|
+
/**
|
|
415
|
+
* Parsed response headers
|
|
416
|
+
* @param headers
|
|
417
|
+
* @returns {Object}
|
|
418
|
+
* @see https://github.com/angular/angular.js/blob/master/src/ng/http.js
|
|
419
|
+
* @private
|
|
420
|
+
*/
|
|
421
|
+
FileUploader.prototype._parseHeaders = function(headers) {
|
|
422
|
+
var parsed = {}, key, val, i;
|
|
423
|
+
|
|
424
|
+
if (!headers) return parsed;
|
|
425
|
+
|
|
426
|
+
function trim(string) {
|
|
427
|
+
return string.replace(/^\s+/, '').replace(/\s+$/, '');
|
|
428
|
+
}
|
|
429
|
+
function lowercase(string) {
|
|
430
|
+
return string.toLowerCase();
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
angular.forEach(headers.split('\n'), function(line) {
|
|
434
|
+
i = line.indexOf(':');
|
|
435
|
+
key = lowercase(trim(line.substr(0, i)));
|
|
436
|
+
val = trim(line.substr(i + 1));
|
|
437
|
+
|
|
438
|
+
if (key) {
|
|
439
|
+
parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
return parsed;
|
|
444
|
+
};
|
|
445
|
+
/**
|
|
446
|
+
* The XMLHttpRequest transport
|
|
447
|
+
* @param {FileItem} item
|
|
448
|
+
* @private
|
|
449
|
+
*/
|
|
450
|
+
FileUploader.prototype._xhrTransport = function(item) {
|
|
451
|
+
var xhr = item._xhr = new XMLHttpRequest();
|
|
452
|
+
var form = new FormData();
|
|
453
|
+
var that = this;
|
|
454
|
+
|
|
455
|
+
that._onBeforeUploadItem(item);
|
|
456
|
+
|
|
457
|
+
angular.forEach(item.formData, function(obj) {
|
|
458
|
+
angular.forEach(obj, function(value, key) {
|
|
459
|
+
form.append(key, value);
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
form.append(item.alias, item._file);
|
|
464
|
+
|
|
465
|
+
xhr.upload.onprogress = function(event) {
|
|
466
|
+
var progress = Math.round(event.lengthComputable ? event.loaded * 100 / event.total : 0);
|
|
467
|
+
that._onProgressItem(item, progress);
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
xhr.onload = function() {
|
|
471
|
+
var headers = that._parseHeaders(xhr.getAllResponseHeaders());
|
|
472
|
+
var response = that._transformResponse(xhr.response);
|
|
473
|
+
var gist = that._isSuccessCode(xhr.status) ? 'Success' : 'Error';
|
|
474
|
+
var method = '_on' + gist + 'Item';
|
|
475
|
+
that[method](item, response, xhr.status, headers);
|
|
476
|
+
that._onCompleteItem(item, response, xhr.status, headers);
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
xhr.onerror = function() {
|
|
480
|
+
var headers = that._parseHeaders(xhr.getAllResponseHeaders());
|
|
481
|
+
var response = that._transformResponse(xhr.response);
|
|
482
|
+
that._onErrorItem(item, response, xhr.status, headers);
|
|
483
|
+
that._onCompleteItem(item, response, xhr.status, headers);
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
xhr.onabort = function() {
|
|
487
|
+
var headers = that._parseHeaders(xhr.getAllResponseHeaders());
|
|
488
|
+
var response = that._transformResponse(xhr.response);
|
|
489
|
+
that._onCancelItem(item, response, xhr.status, headers);
|
|
490
|
+
that._onCompleteItem(item, response, xhr.status, headers);
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
xhr.open(item.method, item.url, true);
|
|
494
|
+
|
|
495
|
+
xhr.withCredentials = item.withCredentials;
|
|
496
|
+
|
|
497
|
+
angular.forEach(item.headers, function(value, name) {
|
|
498
|
+
xhr.setRequestHeader(name, value);
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
xhr.send(form);
|
|
502
|
+
this._render();
|
|
503
|
+
};
|
|
504
|
+
/**
|
|
505
|
+
* The IFrame transport
|
|
506
|
+
* @param {FileItem} item
|
|
507
|
+
* @private
|
|
508
|
+
*/
|
|
509
|
+
FileUploader.prototype._iframeTransport = function(item) {
|
|
510
|
+
var form = angular.element('<form style="display: none;" />');
|
|
511
|
+
var iframe = angular.element('<iframe name="iframeTransport' + Date.now() + '">');
|
|
512
|
+
var input = item._input;
|
|
513
|
+
var that = this;
|
|
514
|
+
|
|
515
|
+
if (item._form) item._form.replaceWith(input); // remove old form
|
|
516
|
+
item._form = form; // save link to new form
|
|
517
|
+
|
|
518
|
+
that._onBeforeUploadItem(item);
|
|
519
|
+
|
|
520
|
+
input.prop('name', item.alias);
|
|
521
|
+
|
|
522
|
+
angular.forEach(item.formData, function(obj) {
|
|
523
|
+
angular.forEach(obj, function(value, key) {
|
|
524
|
+
form.append(angular.element('<input type="hidden" name="' + key + '" value="' + value + '" />'));
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
form.prop({
|
|
529
|
+
action: item.url,
|
|
530
|
+
method: 'POST',
|
|
531
|
+
target: iframe.prop('name'),
|
|
532
|
+
enctype: 'multipart/form-data',
|
|
533
|
+
encoding: 'multipart/form-data' // old IE
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
iframe.bind('load', function() {
|
|
537
|
+
try {
|
|
538
|
+
// Fix for legacy IE browsers that loads internal error page
|
|
539
|
+
// when failed WS response received. In consequence iframe
|
|
540
|
+
// content access denied error is thrown becouse trying to
|
|
541
|
+
// access cross domain page. When such thing occurs notifying
|
|
542
|
+
// with empty response object. See more info at:
|
|
543
|
+
// http://stackoverflow.com/questions/151362/access-is-denied-error-on-accessing-iframe-document-object
|
|
544
|
+
// Note that if non standard 4xx or 5xx error code returned
|
|
545
|
+
// from WS then response content can be accessed without error
|
|
546
|
+
// but 'XHR' status becomes 200. In order to avoid confusion
|
|
547
|
+
// returning response via same 'success' event handler.
|
|
548
|
+
|
|
549
|
+
// fixed angular.contents() for iframes
|
|
550
|
+
var html = iframe[0].contentDocument.body.innerHTML;
|
|
551
|
+
} catch (e) {}
|
|
552
|
+
|
|
553
|
+
var xhr = {response: html, status: 200, dummy: true};
|
|
554
|
+
var response = that._transformResponse(xhr.response);
|
|
555
|
+
var headers = {};
|
|
556
|
+
|
|
557
|
+
that._onSuccessItem(item, response, xhr.status, headers);
|
|
558
|
+
that._onCompleteItem(item, response, xhr.status, headers);
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
form.abort = function() {
|
|
562
|
+
var xhr = {status: 0, dummy: true};
|
|
563
|
+
var headers = {};
|
|
564
|
+
var response;
|
|
565
|
+
|
|
566
|
+
iframe.unbind('load').prop('src', 'javascript:false;');
|
|
567
|
+
form.replaceWith(input);
|
|
568
|
+
|
|
569
|
+
that._onCancelItem(item, response, xhr.status, headers);
|
|
570
|
+
that._onCompleteItem(item, response, xhr.status, headers);
|
|
571
|
+
};
|
|
572
|
+
|
|
573
|
+
input.after(form);
|
|
574
|
+
form.append(input).append(iframe);
|
|
575
|
+
|
|
576
|
+
form[0].submit();
|
|
577
|
+
this._render();
|
|
578
|
+
};
|
|
579
|
+
/**
|
|
580
|
+
* Inner callback
|
|
581
|
+
* @param {File|Object} item
|
|
582
|
+
* @param {Object} filter
|
|
583
|
+
* @param {Object} options
|
|
584
|
+
* @private
|
|
585
|
+
*/
|
|
586
|
+
FileUploader.prototype._onWhenAddingFileFailed = function(item, filter, options) {
|
|
587
|
+
this.onWhenAddingFileFailed(item, filter, options);
|
|
588
|
+
};
|
|
589
|
+
/**
|
|
590
|
+
* Inner callback
|
|
591
|
+
* @param {FileItem} item
|
|
592
|
+
*/
|
|
593
|
+
FileUploader.prototype._onAfterAddingFile = function(item) {
|
|
594
|
+
this.onAfterAddingFile(item);
|
|
595
|
+
};
|
|
596
|
+
/**
|
|
597
|
+
* Inner callback
|
|
598
|
+
* @param {Array<FileItem>} items
|
|
599
|
+
*/
|
|
600
|
+
FileUploader.prototype._onAfterAddingAll = function(items) {
|
|
601
|
+
this.onAfterAddingAll(items);
|
|
602
|
+
};
|
|
603
|
+
/**
|
|
604
|
+
* Inner callback
|
|
605
|
+
* @param {FileItem} item
|
|
606
|
+
* @private
|
|
607
|
+
*/
|
|
608
|
+
FileUploader.prototype._onBeforeUploadItem = function(item) {
|
|
609
|
+
item._onBeforeUpload();
|
|
610
|
+
this.onBeforeUploadItem(item);
|
|
611
|
+
};
|
|
612
|
+
/**
|
|
613
|
+
* Inner callback
|
|
614
|
+
* @param {FileItem} item
|
|
615
|
+
* @param {Number} progress
|
|
616
|
+
* @private
|
|
617
|
+
*/
|
|
618
|
+
FileUploader.prototype._onProgressItem = function(item, progress) {
|
|
619
|
+
var total = this._getTotalProgress(progress);
|
|
620
|
+
this.progress = total;
|
|
621
|
+
item._onProgress(progress);
|
|
622
|
+
this.onProgressItem(item, progress);
|
|
623
|
+
this.onProgressAll(total);
|
|
624
|
+
this._render();
|
|
625
|
+
};
|
|
626
|
+
/**
|
|
627
|
+
* Inner callback
|
|
628
|
+
* @param {FileItem} item
|
|
629
|
+
* @param {*} response
|
|
630
|
+
* @param {Number} status
|
|
631
|
+
* @param {Object} headers
|
|
632
|
+
* @private
|
|
633
|
+
*/
|
|
634
|
+
FileUploader.prototype._onSuccessItem = function(item, response, status, headers) {
|
|
635
|
+
item._onSuccess(response, status, headers);
|
|
636
|
+
this.onSuccessItem(item, response, status, headers);
|
|
637
|
+
};
|
|
638
|
+
/**
|
|
639
|
+
* Inner callback
|
|
640
|
+
* @param {FileItem} item
|
|
641
|
+
* @param {*} response
|
|
642
|
+
* @param {Number} status
|
|
643
|
+
* @param {Object} headers
|
|
644
|
+
* @private
|
|
645
|
+
*/
|
|
646
|
+
FileUploader.prototype._onErrorItem = function(item, response, status, headers) {
|
|
647
|
+
item._onError(response, status, headers);
|
|
648
|
+
this.onErrorItem(item, response, status, headers);
|
|
649
|
+
};
|
|
650
|
+
/**
|
|
651
|
+
* Inner callback
|
|
652
|
+
* @param {FileItem} item
|
|
653
|
+
* @param {*} response
|
|
654
|
+
* @param {Number} status
|
|
655
|
+
* @param {Object} headers
|
|
656
|
+
* @private
|
|
657
|
+
*/
|
|
658
|
+
FileUploader.prototype._onCancelItem = function(item, response, status, headers) {
|
|
659
|
+
item._onCancel(response, status, headers);
|
|
660
|
+
this.onCancelItem(item, response, status, headers);
|
|
661
|
+
};
|
|
662
|
+
/**
|
|
663
|
+
* Inner callback
|
|
664
|
+
* @param {FileItem} item
|
|
665
|
+
* @param {*} response
|
|
666
|
+
* @param {Number} status
|
|
667
|
+
* @param {Object} headers
|
|
668
|
+
* @private
|
|
669
|
+
*/
|
|
670
|
+
FileUploader.prototype._onCompleteItem = function(item, response, status, headers) {
|
|
671
|
+
item._onComplete(response, status, headers);
|
|
672
|
+
this.onCompleteItem(item, response, status, headers);
|
|
673
|
+
|
|
674
|
+
var nextItem = this.getReadyItems()[0];
|
|
675
|
+
this.isUploading = false;
|
|
676
|
+
|
|
677
|
+
if(angular.isDefined(nextItem)) {
|
|
678
|
+
nextItem.upload();
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
this.onCompleteAll();
|
|
683
|
+
this.progress = this._getTotalProgress();
|
|
684
|
+
this._render();
|
|
685
|
+
};
|
|
686
|
+
/**********************
|
|
687
|
+
* STATIC
|
|
688
|
+
**********************/
|
|
689
|
+
/**
|
|
690
|
+
* @borrows FileUploader.prototype.isFile
|
|
691
|
+
*/
|
|
692
|
+
FileUploader.isFile = FileUploader.prototype.isFile;
|
|
693
|
+
/**
|
|
694
|
+
* @borrows FileUploader.prototype.isFileLikeObject
|
|
695
|
+
*/
|
|
696
|
+
FileUploader.isFileLikeObject = FileUploader.prototype.isFileLikeObject;
|
|
697
|
+
/**
|
|
698
|
+
* @borrows FileUploader.prototype.isHTML5
|
|
699
|
+
*/
|
|
700
|
+
FileUploader.isHTML5 = FileUploader.prototype.isHTML5;
|
|
701
|
+
/**
|
|
702
|
+
* Inherits a target (Class_1) by a source (Class_2)
|
|
703
|
+
* @param {Function} target
|
|
704
|
+
* @param {Function} source
|
|
705
|
+
*/
|
|
706
|
+
FileUploader.inherit = function(target, source) {
|
|
707
|
+
target.prototype = Object.create(source.prototype);
|
|
708
|
+
target.prototype.constructor = target;
|
|
709
|
+
target.super_ = source;
|
|
710
|
+
};
|
|
711
|
+
FileUploader.FileLikeObject = FileLikeObject;
|
|
712
|
+
FileUploader.FileItem = FileItem;
|
|
713
|
+
FileUploader.FileDirective = FileDirective;
|
|
714
|
+
FileUploader.FileSelect = FileSelect;
|
|
715
|
+
FileUploader.FileDrop = FileDrop;
|
|
716
|
+
FileUploader.FileOver = FileOver;
|
|
717
|
+
|
|
718
|
+
// ---------------------------
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* Creates an instance of FileLikeObject
|
|
722
|
+
* @param {String} fakePath
|
|
723
|
+
* @constructor
|
|
724
|
+
*/
|
|
725
|
+
function FileLikeObject(fakePath) {
|
|
726
|
+
var path = fakePath;
|
|
727
|
+
this.lastModifiedDate = null;
|
|
728
|
+
this.size = null;
|
|
729
|
+
this.type = 'like/' + path.slice(path.lastIndexOf('.') + 1).toLowerCase();
|
|
730
|
+
this.name = path.slice(path.lastIndexOf('/') + path.lastIndexOf('\\') + 2);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// ---------------------------
|
|
734
|
+
|
|
735
|
+
/**
|
|
736
|
+
* Creates an instance of FileItem
|
|
737
|
+
* @param {FileUploader} uploader
|
|
738
|
+
* @param {File|FileLikeObject|HTMLInputElement} file
|
|
739
|
+
* @param {File|Object} options
|
|
740
|
+
* @param {HTMLInputElement} [input]
|
|
741
|
+
* @constructor
|
|
742
|
+
*/
|
|
743
|
+
function FileItem(uploader, file, options, input) {
|
|
744
|
+
file = uploader._getFileOrFileLikeObject(file);
|
|
745
|
+
|
|
746
|
+
angular.extend(this, {
|
|
747
|
+
url: uploader.url,
|
|
748
|
+
alias: uploader.alias,
|
|
749
|
+
headers: angular.copy(uploader.headers),
|
|
750
|
+
formData: angular.copy(uploader.formData),
|
|
751
|
+
removeAfterUpload: uploader.removeAfterUpload,
|
|
752
|
+
withCredentials: uploader.withCredentials,
|
|
753
|
+
method: uploader.method
|
|
754
|
+
}, options, {
|
|
755
|
+
uploader: uploader,
|
|
756
|
+
file: angular.copy(file),
|
|
757
|
+
isReady: false,
|
|
758
|
+
isUploading: false,
|
|
759
|
+
isUploaded: false,
|
|
760
|
+
isSuccess: false,
|
|
761
|
+
isCancel: false,
|
|
762
|
+
isError: false,
|
|
763
|
+
progress: 0,
|
|
764
|
+
index: null,
|
|
765
|
+
_file: file
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
if (input) {
|
|
769
|
+
this._input = angular.element(input);
|
|
770
|
+
this._replaceNode(this._input);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
/**********************
|
|
774
|
+
* PUBLIC
|
|
775
|
+
**********************/
|
|
776
|
+
/**
|
|
777
|
+
* Uploads a FileItem
|
|
778
|
+
*/
|
|
779
|
+
FileItem.prototype.upload = function() {
|
|
780
|
+
this.uploader.uploadItem(this);
|
|
781
|
+
};
|
|
782
|
+
/**
|
|
783
|
+
* Cancels uploading of FileItem
|
|
784
|
+
*/
|
|
785
|
+
FileItem.prototype.cancel = function() {
|
|
786
|
+
this.uploader.cancelItem(this);
|
|
787
|
+
};
|
|
788
|
+
/**
|
|
789
|
+
* Removes a FileItem
|
|
790
|
+
*/
|
|
791
|
+
FileItem.prototype.remove = function() {
|
|
792
|
+
this.uploader.removeFromQueue(this);
|
|
793
|
+
};
|
|
794
|
+
/**
|
|
795
|
+
* Callback
|
|
796
|
+
* @private
|
|
797
|
+
*/
|
|
798
|
+
FileItem.prototype.onBeforeUpload = function() {};
|
|
799
|
+
/**
|
|
800
|
+
* Callback
|
|
801
|
+
* @param {Number} progress
|
|
802
|
+
* @private
|
|
803
|
+
*/
|
|
804
|
+
FileItem.prototype.onProgress = function(progress) {};
|
|
805
|
+
/**
|
|
806
|
+
* Callback
|
|
807
|
+
* @param {*} response
|
|
808
|
+
* @param {Number} status
|
|
809
|
+
* @param {Object} headers
|
|
810
|
+
*/
|
|
811
|
+
FileItem.prototype.onSuccess = function(response, status, headers) {};
|
|
812
|
+
/**
|
|
813
|
+
* Callback
|
|
814
|
+
* @param {*} response
|
|
815
|
+
* @param {Number} status
|
|
816
|
+
* @param {Object} headers
|
|
817
|
+
*/
|
|
818
|
+
FileItem.prototype.onError = function(response, status, headers) {};
|
|
819
|
+
/**
|
|
820
|
+
* Callback
|
|
821
|
+
* @param {*} response
|
|
822
|
+
* @param {Number} status
|
|
823
|
+
* @param {Object} headers
|
|
824
|
+
*/
|
|
825
|
+
FileItem.prototype.onCancel = function(response, status, headers) {};
|
|
826
|
+
/**
|
|
827
|
+
* Callback
|
|
828
|
+
* @param {*} response
|
|
829
|
+
* @param {Number} status
|
|
830
|
+
* @param {Object} headers
|
|
831
|
+
*/
|
|
832
|
+
FileItem.prototype.onComplete = function(response, status, headers) {};
|
|
833
|
+
/**********************
|
|
834
|
+
* PRIVATE
|
|
835
|
+
**********************/
|
|
836
|
+
/**
|
|
837
|
+
* Inner callback
|
|
838
|
+
*/
|
|
839
|
+
FileItem.prototype._onBeforeUpload = function() {
|
|
840
|
+
this.isReady = true;
|
|
841
|
+
this.isUploading = true;
|
|
842
|
+
this.isUploaded = false;
|
|
843
|
+
this.isSuccess = false;
|
|
844
|
+
this.isCancel = false;
|
|
845
|
+
this.isError = false;
|
|
846
|
+
this.progress = 0;
|
|
847
|
+
this.onBeforeUpload();
|
|
848
|
+
};
|
|
849
|
+
/**
|
|
850
|
+
* Inner callback
|
|
851
|
+
* @param {Number} progress
|
|
852
|
+
* @private
|
|
853
|
+
*/
|
|
854
|
+
FileItem.prototype._onProgress = function(progress) {
|
|
855
|
+
this.progress = progress;
|
|
856
|
+
this.onProgress(progress);
|
|
857
|
+
};
|
|
858
|
+
/**
|
|
859
|
+
* Inner callback
|
|
860
|
+
* @param {*} response
|
|
861
|
+
* @param {Number} status
|
|
862
|
+
* @param {Object} headers
|
|
863
|
+
* @private
|
|
864
|
+
*/
|
|
865
|
+
FileItem.prototype._onSuccess = function(response, status, headers) {
|
|
866
|
+
this.isReady = false;
|
|
867
|
+
this.isUploading = false;
|
|
868
|
+
this.isUploaded = true;
|
|
869
|
+
this.isSuccess = true;
|
|
870
|
+
this.isCancel = false;
|
|
871
|
+
this.isError = false;
|
|
872
|
+
this.progress = 100;
|
|
873
|
+
this.index = null;
|
|
874
|
+
this.onSuccess(response, status, headers);
|
|
875
|
+
};
|
|
876
|
+
/**
|
|
877
|
+
* Inner callback
|
|
878
|
+
* @param {*} response
|
|
879
|
+
* @param {Number} status
|
|
880
|
+
* @param {Object} headers
|
|
881
|
+
* @private
|
|
882
|
+
*/
|
|
883
|
+
FileItem.prototype._onError = function(response, status, headers) {
|
|
884
|
+
this.isReady = false;
|
|
885
|
+
this.isUploading = false;
|
|
886
|
+
this.isUploaded = true;
|
|
887
|
+
this.isSuccess = false;
|
|
888
|
+
this.isCancel = false;
|
|
889
|
+
this.isError = true;
|
|
890
|
+
this.progress = 0;
|
|
891
|
+
this.index = null;
|
|
892
|
+
this.onError(response, status, headers);
|
|
893
|
+
};
|
|
894
|
+
/**
|
|
895
|
+
* Inner callback
|
|
896
|
+
* @param {*} response
|
|
897
|
+
* @param {Number} status
|
|
898
|
+
* @param {Object} headers
|
|
899
|
+
* @private
|
|
900
|
+
*/
|
|
901
|
+
FileItem.prototype._onCancel = function(response, status, headers) {
|
|
902
|
+
this.isReady = false;
|
|
903
|
+
this.isUploading = false;
|
|
904
|
+
this.isUploaded = false;
|
|
905
|
+
this.isSuccess = false;
|
|
906
|
+
this.isCancel = true;
|
|
907
|
+
this.isError = false;
|
|
908
|
+
this.progress = 0;
|
|
909
|
+
this.index = null;
|
|
910
|
+
this.onCancel(response, status, headers);
|
|
911
|
+
};
|
|
912
|
+
/**
|
|
913
|
+
* Inner callback
|
|
914
|
+
* @param {*} response
|
|
915
|
+
* @param {Number} status
|
|
916
|
+
* @param {Object} headers
|
|
917
|
+
* @private
|
|
918
|
+
*/
|
|
919
|
+
FileItem.prototype._onComplete = function(response, status, headers) {
|
|
920
|
+
this.onComplete(response, status, headers);
|
|
921
|
+
if (this.removeAfterUpload) this.remove();
|
|
922
|
+
};
|
|
923
|
+
/**
|
|
924
|
+
* Destroys a FileItem
|
|
925
|
+
*/
|
|
926
|
+
FileItem.prototype._destroy = function() {
|
|
927
|
+
if (this._input) this._input.remove();
|
|
928
|
+
if (this._form) this._form.remove();
|
|
929
|
+
delete this._form;
|
|
930
|
+
delete this._input;
|
|
931
|
+
};
|
|
932
|
+
/**
|
|
933
|
+
* Prepares to uploading
|
|
934
|
+
* @private
|
|
935
|
+
*/
|
|
936
|
+
FileItem.prototype._prepareToUploading = function() {
|
|
937
|
+
this.index = this.index || ++this.uploader._nextIndex;
|
|
938
|
+
this.isReady = true;
|
|
939
|
+
};
|
|
940
|
+
/**
|
|
941
|
+
* Replaces input element on his clone
|
|
942
|
+
* @param {JQLite|jQuery} input
|
|
943
|
+
* @private
|
|
944
|
+
*/
|
|
945
|
+
FileItem.prototype._replaceNode = function(input) {
|
|
946
|
+
var clone = $compile(input.clone())(input.scope());
|
|
947
|
+
clone.prop('value', null); // FF fix
|
|
948
|
+
input.css('display', 'none');
|
|
949
|
+
input.after(clone); // remove jquery dependency
|
|
950
|
+
};
|
|
951
|
+
|
|
952
|
+
// ---------------------------
|
|
953
|
+
|
|
954
|
+
/**
|
|
955
|
+
* Creates instance of {FileDirective} object
|
|
956
|
+
* @param {Object} options
|
|
957
|
+
* @param {Object} options.uploader
|
|
958
|
+
* @param {HTMLElement} options.element
|
|
959
|
+
* @param {Object} options.events
|
|
960
|
+
* @param {String} options.prop
|
|
961
|
+
* @constructor
|
|
962
|
+
*/
|
|
963
|
+
function FileDirective(options) {
|
|
964
|
+
angular.extend(this, options);
|
|
965
|
+
this.uploader._directives[this.prop].push(this);
|
|
966
|
+
this._saveLinks();
|
|
967
|
+
this.bind();
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* Map of events
|
|
971
|
+
* @type {Object}
|
|
972
|
+
*/
|
|
973
|
+
FileDirective.prototype.events = {};
|
|
974
|
+
/**
|
|
975
|
+
* Binds events handles
|
|
976
|
+
*/
|
|
977
|
+
FileDirective.prototype.bind = function() {
|
|
978
|
+
for(var key in this.events) {
|
|
979
|
+
var prop = this.events[key];
|
|
980
|
+
this.element.bind(key, this[prop]);
|
|
981
|
+
}
|
|
982
|
+
};
|
|
983
|
+
/**
|
|
984
|
+
* Unbinds events handles
|
|
985
|
+
*/
|
|
986
|
+
FileDirective.prototype.unbind = function() {
|
|
987
|
+
for(var key in this.events) {
|
|
988
|
+
this.element.unbind(key, this.events[key]);
|
|
989
|
+
}
|
|
990
|
+
};
|
|
991
|
+
/**
|
|
992
|
+
* Destroys directive
|
|
993
|
+
*/
|
|
994
|
+
FileDirective.prototype.destroy = function() {
|
|
995
|
+
var index = this.uploader._directives[this.prop].indexOf(this);
|
|
996
|
+
this.uploader._directives[this.prop].splice(index, 1);
|
|
997
|
+
this.unbind();
|
|
998
|
+
// this.element = null;
|
|
999
|
+
};
|
|
1000
|
+
/**
|
|
1001
|
+
* Saves links to functions
|
|
1002
|
+
* @private
|
|
1003
|
+
*/
|
|
1004
|
+
FileDirective.prototype._saveLinks = function() {
|
|
1005
|
+
for(var key in this.events) {
|
|
1006
|
+
var prop = this.events[key];
|
|
1007
|
+
this[prop] = this[prop].bind(this);
|
|
1008
|
+
}
|
|
1009
|
+
};
|
|
1010
|
+
|
|
1011
|
+
// ---------------------------
|
|
1012
|
+
|
|
1013
|
+
FileUploader.inherit(FileSelect, FileDirective);
|
|
1014
|
+
|
|
1015
|
+
/**
|
|
1016
|
+
* Creates instance of {FileSelect} object
|
|
1017
|
+
* @param {Object} options
|
|
1018
|
+
* @constructor
|
|
1019
|
+
*/
|
|
1020
|
+
function FileSelect(options) {
|
|
1021
|
+
FileSelect.super_.apply(this, arguments);
|
|
1022
|
+
|
|
1023
|
+
if(!this.uploader.isHTML5) {
|
|
1024
|
+
this.element.removeAttr('multiple');
|
|
1025
|
+
}
|
|
1026
|
+
this.element.prop('value', null); // FF fix
|
|
1027
|
+
}
|
|
1028
|
+
/**
|
|
1029
|
+
* Map of events
|
|
1030
|
+
* @type {Object}
|
|
1031
|
+
*/
|
|
1032
|
+
FileSelect.prototype.events = {
|
|
1033
|
+
$destroy: 'destroy',
|
|
1034
|
+
change: 'onChange'
|
|
1035
|
+
};
|
|
1036
|
+
/**
|
|
1037
|
+
* Name of property inside uploader._directive object
|
|
1038
|
+
* @type {String}
|
|
1039
|
+
*/
|
|
1040
|
+
FileSelect.prototype.prop = 'select';
|
|
1041
|
+
/**
|
|
1042
|
+
* Returns options
|
|
1043
|
+
* @return {Object|undefined}
|
|
1044
|
+
*/
|
|
1045
|
+
FileSelect.prototype.getOptions = function() {};
|
|
1046
|
+
/**
|
|
1047
|
+
* Returns filters
|
|
1048
|
+
* @return {Array<Function>|String|undefined}
|
|
1049
|
+
*/
|
|
1050
|
+
FileSelect.prototype.getFilters = function() {};
|
|
1051
|
+
/**
|
|
1052
|
+
* Event handler
|
|
1053
|
+
*/
|
|
1054
|
+
FileSelect.prototype.onChange = function() {
|
|
1055
|
+
var files = this.uploader.isHTML5 ? this.element[0].files : this.element[0];
|
|
1056
|
+
var options = this.getOptions();
|
|
1057
|
+
var filters = this.getFilters();
|
|
1058
|
+
|
|
1059
|
+
if (!this.uploader.isHTML5) this.destroy();
|
|
1060
|
+
this.uploader.addToQueue(files, options, filters);
|
|
1061
|
+
if (this.uploader.isHTML5) this.element.prop('value', null);
|
|
1062
|
+
};
|
|
1063
|
+
|
|
1064
|
+
// ---------------------------
|
|
1065
|
+
|
|
1066
|
+
FileUploader.inherit(FileDrop, FileDirective);
|
|
1067
|
+
|
|
1068
|
+
/**
|
|
1069
|
+
* Creates instance of {FileDrop} object
|
|
1070
|
+
* @param {Object} options
|
|
1071
|
+
* @constructor
|
|
1072
|
+
*/
|
|
1073
|
+
function FileDrop(options) {
|
|
1074
|
+
FileDrop.super_.apply(this, arguments);
|
|
1075
|
+
}
|
|
1076
|
+
/**
|
|
1077
|
+
* Map of events
|
|
1078
|
+
* @type {Object}
|
|
1079
|
+
*/
|
|
1080
|
+
FileDrop.prototype.events = {
|
|
1081
|
+
$destroy: 'destroy',
|
|
1082
|
+
drop: 'onDrop',
|
|
1083
|
+
dragover: 'onDragOver',
|
|
1084
|
+
dragleave: 'onDragLeave'
|
|
1085
|
+
};
|
|
1086
|
+
/**
|
|
1087
|
+
* Name of property inside uploader._directive object
|
|
1088
|
+
* @type {String}
|
|
1089
|
+
*/
|
|
1090
|
+
FileDrop.prototype.prop = 'drop';
|
|
1091
|
+
/**
|
|
1092
|
+
* Returns options
|
|
1093
|
+
* @return {Object|undefined}
|
|
1094
|
+
*/
|
|
1095
|
+
FileDrop.prototype.getOptions = function() {};
|
|
1096
|
+
/**
|
|
1097
|
+
* Returns filters
|
|
1098
|
+
* @return {Array<Function>|String|undefined}
|
|
1099
|
+
*/
|
|
1100
|
+
FileDrop.prototype.getFilters = function() {};
|
|
1101
|
+
/**
|
|
1102
|
+
* Event handler
|
|
1103
|
+
*/
|
|
1104
|
+
FileDrop.prototype.onDrop = function(event) {
|
|
1105
|
+
var transfer = this._getTransfer(event);
|
|
1106
|
+
if (!transfer) return;
|
|
1107
|
+
var options = this.getOptions();
|
|
1108
|
+
var filters = this.getFilters();
|
|
1109
|
+
this._preventAndStop(event);
|
|
1110
|
+
angular.forEach(this.uploader._directives.over, this._removeOverClass, this);
|
|
1111
|
+
this.uploader.addToQueue(transfer.files, options, filters);
|
|
1112
|
+
};
|
|
1113
|
+
/**
|
|
1114
|
+
* Event handler
|
|
1115
|
+
*/
|
|
1116
|
+
FileDrop.prototype.onDragOver = function(event) {
|
|
1117
|
+
var transfer = this._getTransfer(event);
|
|
1118
|
+
if(!this._haveFiles(transfer.types)) return;
|
|
1119
|
+
transfer.dropEffect = 'copy';
|
|
1120
|
+
this._preventAndStop(event);
|
|
1121
|
+
angular.forEach(this.uploader._directives.over, this._addOverClass, this);
|
|
1122
|
+
};
|
|
1123
|
+
/**
|
|
1124
|
+
* Event handler
|
|
1125
|
+
*/
|
|
1126
|
+
FileDrop.prototype.onDragLeave = function(event) {
|
|
1127
|
+
if (event.target !== this.element[0]) return;
|
|
1128
|
+
this._preventAndStop(event);
|
|
1129
|
+
angular.forEach(this.uploader._directives.over, this._removeOverClass, this);
|
|
1130
|
+
};
|
|
1131
|
+
/**
|
|
1132
|
+
* Helper
|
|
1133
|
+
*/
|
|
1134
|
+
FileDrop.prototype._getTransfer = function(event) {
|
|
1135
|
+
return event.dataTransfer ? event.dataTransfer : event.originalEvent.dataTransfer; // jQuery fix;
|
|
1136
|
+
};
|
|
1137
|
+
/**
|
|
1138
|
+
* Helper
|
|
1139
|
+
*/
|
|
1140
|
+
FileDrop.prototype._preventAndStop = function(event) {
|
|
1141
|
+
event.preventDefault();
|
|
1142
|
+
event.stopPropagation();
|
|
1143
|
+
};
|
|
1144
|
+
/**
|
|
1145
|
+
* Returns "true" if types contains files
|
|
1146
|
+
* @param {Object} types
|
|
1147
|
+
*/
|
|
1148
|
+
FileDrop.prototype._haveFiles = function(types) {
|
|
1149
|
+
if (!types) return false;
|
|
1150
|
+
if (types.indexOf) {
|
|
1151
|
+
return types.indexOf('Files') !== -1;
|
|
1152
|
+
} else if(types.contains) {
|
|
1153
|
+
return types.contains('Files');
|
|
1154
|
+
} else {
|
|
1155
|
+
return false;
|
|
1156
|
+
}
|
|
1157
|
+
};
|
|
1158
|
+
/**
|
|
1159
|
+
* Callback
|
|
1160
|
+
*/
|
|
1161
|
+
FileDrop.prototype._addOverClass = function(item) {
|
|
1162
|
+
item.addOverClass();
|
|
1163
|
+
};
|
|
1164
|
+
/**
|
|
1165
|
+
* Callback
|
|
1166
|
+
*/
|
|
1167
|
+
FileDrop.prototype._removeOverClass = function(item) {
|
|
1168
|
+
item.removeOverClass();
|
|
1169
|
+
};
|
|
1170
|
+
|
|
1171
|
+
// ---------------------------
|
|
1172
|
+
|
|
1173
|
+
FileUploader.inherit(FileOver, FileDirective);
|
|
1174
|
+
|
|
1175
|
+
/**
|
|
1176
|
+
* Creates instance of {FileDrop} object
|
|
1177
|
+
* @param {Object} options
|
|
1178
|
+
* @constructor
|
|
1179
|
+
*/
|
|
1180
|
+
function FileOver(options) {
|
|
1181
|
+
FileOver.super_.apply(this, arguments);
|
|
1182
|
+
}
|
|
1183
|
+
/**
|
|
1184
|
+
* Map of events
|
|
1185
|
+
* @type {Object}
|
|
1186
|
+
*/
|
|
1187
|
+
FileOver.prototype.events = {
|
|
1188
|
+
$destroy: 'destroy'
|
|
1189
|
+
};
|
|
1190
|
+
/**
|
|
1191
|
+
* Name of property inside uploader._directive object
|
|
1192
|
+
* @type {String}
|
|
1193
|
+
*/
|
|
1194
|
+
FileOver.prototype.prop = 'over';
|
|
1195
|
+
/**
|
|
1196
|
+
* Over class
|
|
1197
|
+
* @type {string}
|
|
1198
|
+
*/
|
|
1199
|
+
FileOver.prototype.overClass = 'nv-file-over';
|
|
1200
|
+
/**
|
|
1201
|
+
* Adds over class
|
|
1202
|
+
*/
|
|
1203
|
+
FileOver.prototype.addOverClass = function() {
|
|
1204
|
+
this.element.addClass(this.getOverClass());
|
|
1205
|
+
};
|
|
1206
|
+
/**
|
|
1207
|
+
* Removes over class
|
|
1208
|
+
*/
|
|
1209
|
+
FileOver.prototype.removeOverClass = function() {
|
|
1210
|
+
this.element.removeClass(this.getOverClass());
|
|
1211
|
+
};
|
|
1212
|
+
/**
|
|
1213
|
+
* Returns over class
|
|
1214
|
+
* @returns {String}
|
|
1215
|
+
*/
|
|
1216
|
+
FileOver.prototype.getOverClass = function() {
|
|
1217
|
+
return this.overClass;
|
|
1218
|
+
};
|
|
1219
|
+
|
|
1220
|
+
return FileUploader;
|
|
1221
|
+
}])
|
|
1222
|
+
|
|
1223
|
+
|
|
1224
|
+
.directive('nvFileSelect', ['$parse', 'FileUploader', function($parse, FileUploader) {
|
|
1225
|
+
return {
|
|
1226
|
+
link: function(scope, element, attributes) {
|
|
1227
|
+
var uploader = scope.$eval(attributes.uploader);
|
|
1228
|
+
|
|
1229
|
+
if (!(uploader instanceof FileUploader)) {
|
|
1230
|
+
throw new TypeError('"Uploader" must be an instance of FileUploader');
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
var object = new FileUploader.FileSelect({
|
|
1234
|
+
uploader: uploader,
|
|
1235
|
+
element: element
|
|
1236
|
+
});
|
|
1237
|
+
|
|
1238
|
+
object.getOptions = $parse(attributes.options).bind(object, scope);
|
|
1239
|
+
object.getFilters = function() {return attributes.filters;};
|
|
1240
|
+
}
|
|
1241
|
+
};
|
|
1242
|
+
}])
|
|
1243
|
+
|
|
1244
|
+
|
|
1245
|
+
.directive('nvFileDrop', ['$parse', 'FileUploader', function($parse, FileUploader) {
|
|
1246
|
+
return {
|
|
1247
|
+
link: function(scope, element, attributes) {
|
|
1248
|
+
var uploader = scope.$eval(attributes.uploader);
|
|
1249
|
+
|
|
1250
|
+
if (!(uploader instanceof FileUploader)) {
|
|
1251
|
+
throw new TypeError('"Uploader" must be an instance of FileUploader');
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
if (!uploader.isHTML5) return;
|
|
1255
|
+
|
|
1256
|
+
var object = new FileUploader.FileDrop({
|
|
1257
|
+
uploader: uploader,
|
|
1258
|
+
element: element
|
|
1259
|
+
});
|
|
1260
|
+
|
|
1261
|
+
object.getOptions = $parse(attributes.options).bind(object, scope);
|
|
1262
|
+
object.getFilters = function() {return attributes.filters;};
|
|
1263
|
+
}
|
|
1264
|
+
};
|
|
1265
|
+
}])
|
|
1266
|
+
|
|
1267
|
+
|
|
1268
|
+
.directive('nvFileOver', ['FileUploader', function(FileUploader) {
|
|
1269
|
+
return {
|
|
1270
|
+
link: function(scope, element, attributes) {
|
|
1271
|
+
var uploader = scope.$eval(attributes.uploader);
|
|
1272
|
+
|
|
1273
|
+
if (!(uploader instanceof FileUploader)) {
|
|
1274
|
+
throw new TypeError('"Uploader" must be an instance of FileUploader');
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
var object = new FileUploader.FileOver({
|
|
1278
|
+
uploader: uploader,
|
|
1279
|
+
element: element
|
|
1280
|
+
});
|
|
1281
|
+
|
|
1282
|
+
object.getOverClass = function() {
|
|
1283
|
+
return attributes.overClass || this.overClass;
|
|
1284
|
+
};
|
|
1285
|
+
}
|
|
1286
|
+
};
|
|
1287
|
+
}])
|
|
1288
|
+
return module;
|
|
1289
|
+
}));
|