avatars_for_rails 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (26) hide show
  1. data/{lib/generators/avatars_for_rails/templates/public → app/assets}/images/Jcrop.gif +0 -0
  2. data/{lib/generators/avatars_for_rails/templates/public → app/assets}/images/cancel.png +0 -0
  3. data/avatars_for_rails.gemspec +1 -1
  4. data/vendor/assets/javascripts/jquery.fileupload-ui.js +333 -63
  5. data/vendor/assets/javascripts/jquery.fileupload.js +654 -173
  6. data/vendor/assets/stylesheets/jquery.fileupload-ui.css +91 -21
  7. metadata +27 -46
  8. data/lib/generators/avatars_for_rails/templates/public/images/pbar-ani.gif +0 -0
  9. data/lib/generators/avatars_for_rails/templates/public/images/rails.png +0 -0
  10. data/lib/generators/avatars_for_rails/templates/public/javascripts/application.js +0 -2
  11. data/lib/generators/avatars_for_rails/templates/public/javascripts/avatars.js +0 -8
  12. data/lib/generators/avatars_for_rails/templates/public/javascripts/controls.js +0 -965
  13. data/lib/generators/avatars_for_rails/templates/public/javascripts/dragdrop.js +0 -974
  14. data/lib/generators/avatars_for_rails/templates/public/javascripts/effects.js +0 -1123
  15. data/lib/generators/avatars_for_rails/templates/public/javascripts/jquery-ui.min.js +0 -401
  16. data/lib/generators/avatars_for_rails/templates/public/javascripts/jquery.Jcrop.min.js +0 -163
  17. data/lib/generators/avatars_for_rails/templates/public/javascripts/jquery.fileupload-ui.js +0 -529
  18. data/lib/generators/avatars_for_rails/templates/public/javascripts/jquery.fileupload.js +0 -956
  19. data/lib/generators/avatars_for_rails/templates/public/javascripts/jquery.form.js +0 -815
  20. data/lib/generators/avatars_for_rails/templates/public/javascripts/jquery.js +0 -7179
  21. data/lib/generators/avatars_for_rails/templates/public/javascripts/prototype.js +0 -6001
  22. data/lib/generators/avatars_for_rails/templates/public/javascripts/rails.js +0 -158
  23. data/lib/generators/avatars_for_rails/templates/public/stylesheets/.gitkeep +0 -0
  24. data/lib/generators/avatars_for_rails/templates/public/stylesheets/avatars.css +0 -115
  25. data/lib/generators/avatars_for_rails/templates/public/stylesheets/jquery.Jcrop.css +0 -35
  26. data/lib/generators/avatars_for_rails/templates/public/stylesheets/jquery.fileupload-ui.css +0 -140
@@ -1,89 +1,156 @@
1
1
  /*
2
- * jQuery File Upload Plugin 3.4
2
+ * jQuery File Upload Plugin 4.4.2
3
+ * https://github.com/blueimp/jQuery-File-Upload
4
+ *
5
+ * Copyright 2010, Sebastian Tschan
6
+ * https://blueimp.net
3
7
  *
4
- * Copyright 2010, Sebastian Tschan, AQUANTUM
5
8
  * Licensed under the MIT license:
6
9
  * http://creativecommons.org/licenses/MIT/
7
- *
8
- * https://blueimp.net
9
- * http://www.aquantum.de
10
10
  */
11
11
 
12
12
  /*jslint browser: true */
13
- /*global File, FileReader, FormData, unescape, jQuery */
13
+ /*global XMLHttpRequestUpload, File, FileReader, FormData, ProgressEvent, unescape, jQuery, upload */
14
14
 
15
15
  (function ($) {
16
+ 'use strict';
16
17
 
17
- var FileUpload,
18
- methods;
18
+ var defaultNamespace = 'file_upload',
19
+ undef = 'undefined',
20
+ func = 'function',
21
+ FileUpload,
22
+ methods,
23
+
24
+ MultiLoader = function (callBack, numOrList) {
25
+ var loaded = 0,
26
+ list = [];
27
+ if (numOrList) {
28
+ if (numOrList.length) {
29
+ list = numOrList;
30
+ } else {
31
+ list[numOrList - 1] = null;
32
+ }
33
+ }
34
+ this.complete = function () {
35
+ loaded += 1;
36
+ if (loaded === list.length) {
37
+ callBack(list);
38
+ loaded = 0;
39
+ list = [];
40
+ }
41
+ };
42
+ this.push = function (item) {
43
+ list.push(item);
44
+ };
45
+ this.getList = function () {
46
+ return list;
47
+ };
48
+ },
49
+
50
+ SequenceHandler = function () {
51
+ var sequence = [];
52
+ this.push = function (callBack) {
53
+ sequence.push(callBack);
54
+ if (sequence.length === 1) {
55
+ callBack();
56
+ }
57
+ };
58
+ this.next = function () {
59
+ sequence.shift();
60
+ if (sequence.length) {
61
+ sequence[0]();
62
+ }
63
+ };
64
+ };
19
65
 
20
66
  FileUpload = function (container) {
21
67
  var fileUpload = this,
22
- uploadForm = (container.is('form') ? container : container.find('form').first()),
23
- fileInput = uploadForm.find('input:file').first(),
68
+ uploadForm,
69
+ fileInput,
24
70
  settings = {
25
- namespace: 'file_upload',
26
- cssClass: 'file_upload',
71
+ namespace: defaultNamespace,
72
+ uploadFormFilter: function (index) {
73
+ return true;
74
+ },
75
+ fileInputFilter: function (index) {
76
+ return true;
77
+ },
78
+ cssClass: defaultNamespace,
27
79
  dragDropSupport: true,
28
80
  dropZone: container,
29
- url: uploadForm.attr('action'),
30
- method: uploadForm.attr('method'),
31
- fieldName: fileInput.attr('name'),
81
+ url: function (form) {
82
+ return form.attr('action');
83
+ },
84
+ method: function (form) {
85
+ return form.attr('method');
86
+ },
87
+ fieldName: function (input) {
88
+ return input.attr('name');
89
+ },
90
+ formData: function (form) {
91
+ return form.serializeArray();
92
+ },
93
+ requestHeaders: null,
32
94
  multipart: true,
33
95
  multiFileRequest: false,
34
- formData: function () {
35
- return uploadForm.serializeArray();
36
- },
37
96
  withCredentials: false,
38
- forceIframeUpload: false
97
+ forceIframeUpload: false,
98
+ sequentialUploads: false,
99
+ maxChunkSize: null,
100
+ maxFileReaderSize: 50000000
39
101
  },
40
102
  documentListeners = {},
41
103
  dropZoneListeners = {},
42
- fileInputListeners = {},
43
- undef = 'undefined',
44
- func = 'function',
45
- num = 'number',
46
104
  protocolRegExp = /^http(s)?:\/\//,
47
-
48
- MultiLoader = function (callBack, numberComplete) {
49
- var loaded = 0;
50
- this.complete = function () {
51
- loaded += 1;
52
- if (loaded === numberComplete) {
53
- callBack();
54
- }
55
- };
105
+ optionsReference,
106
+ multiLoader = new MultiLoader(function (list) {
107
+ if (typeof settings.onLoadAll === func) {
108
+ settings.onLoadAll(list);
109
+ }
110
+ }),
111
+ sequenceHandler = new SequenceHandler(),
112
+
113
+ completeNext = function () {
114
+ multiLoader.complete();
115
+ sequenceHandler.next();
56
116
  },
57
117
 
58
118
  isXHRUploadCapable = function () {
59
- return typeof XMLHttpRequest !== undef && typeof File !== undef && (
60
- !settings.multipart || typeof FormData !== undef || typeof FileReader !== undef
61
- );
119
+ return typeof XMLHttpRequest !== undef && typeof XMLHttpRequestUpload !== undef &&
120
+ typeof File !== undef && (!settings.multipart || typeof FormData !== undef ||
121
+ (typeof FileReader !== undef && typeof XMLHttpRequest.prototype.sendAsBinary === func));
62
122
  },
63
123
 
64
124
  initEventHandlers = function () {
65
125
  if (settings.dragDropSupport) {
66
126
  if (typeof settings.onDocumentDragEnter === func) {
67
- documentListeners['dragenter.' + settings.namespace] = settings.onDocumentDragEnter;
127
+ documentListeners['dragenter.' + settings.namespace] = function (e) {
128
+ settings.onDocumentDragEnter(e);
129
+ };
68
130
  }
69
131
  if (typeof settings.onDocumentDragLeave === func) {
70
- documentListeners['dragleave.' + settings.namespace] = settings.onDocumentDragLeave;
132
+ documentListeners['dragleave.' + settings.namespace] = function (e) {
133
+ settings.onDocumentDragLeave(e);
134
+ };
71
135
  }
72
136
  documentListeners['dragover.' + settings.namespace] = fileUpload.onDocumentDragOver;
73
137
  documentListeners['drop.' + settings.namespace] = fileUpload.onDocumentDrop;
74
138
  $(document).bind(documentListeners);
75
139
  if (typeof settings.onDragEnter === func) {
76
- dropZoneListeners['dragenter.' + settings.namespace] = settings.onDragEnter;
140
+ dropZoneListeners['dragenter.' + settings.namespace] = function (e) {
141
+ settings.onDragEnter(e);
142
+ };
77
143
  }
78
144
  if (typeof settings.onDragLeave === func) {
79
- dropZoneListeners['dragleave.' + settings.namespace] = settings.onDragLeave;
145
+ dropZoneListeners['dragleave.' + settings.namespace] = function (e) {
146
+ settings.onDragLeave(e);
147
+ };
80
148
  }
81
149
  dropZoneListeners['dragover.' + settings.namespace] = fileUpload.onDragOver;
82
150
  dropZoneListeners['drop.' + settings.namespace] = fileUpload.onDrop;
83
151
  settings.dropZone.bind(dropZoneListeners);
84
152
  }
85
- fileInputListeners['change.' + settings.namespace] = fileUpload.onChange;
86
- fileInput.bind(fileInputListeners);
153
+ fileInput.bind('change.' + settings.namespace, fileUpload.onChange);
87
154
  },
88
155
 
89
156
  removeEventHandlers = function () {
@@ -93,41 +160,185 @@
93
160
  $.each(dropZoneListeners, function (key, value) {
94
161
  settings.dropZone.unbind(key, value);
95
162
  });
96
- $.each(fileInputListeners, function (key, value) {
97
- fileInput.unbind(key, value);
98
- });
163
+ fileInput.unbind('change.' + settings.namespace);
99
164
  },
100
165
 
101
- initUploadEventHandlers = function (files, index, xhr, settings) {
102
- if (typeof settings.onProgress === func) {
103
- xhr.upload.onprogress = function (e) {
104
- settings.onProgress(e, files, index, xhr, settings);
166
+ isChunkedUpload = function (settings) {
167
+ return typeof settings.uploadedBytes !== undef;
168
+ },
169
+
170
+ createProgressEvent = function (lengthComputable, loaded, total) {
171
+ var event;
172
+ if (typeof document.createEvent === func && typeof ProgressEvent !== undef) {
173
+ event = document.createEvent('ProgressEvent');
174
+ event.initProgressEvent(
175
+ 'progress',
176
+ false,
177
+ false,
178
+ lengthComputable,
179
+ loaded,
180
+ total
181
+ );
182
+ } else {
183
+ event = {
184
+ lengthComputable: true,
185
+ loaded: loaded,
186
+ total: total
105
187
  };
106
188
  }
189
+ return event;
190
+ },
191
+
192
+ getProgressTotal = function (files, index, settings) {
193
+ var i,
194
+ total;
195
+ if (typeof settings.progressTotal === undef) {
196
+ if (files[index]) {
197
+ total = files[index].size;
198
+ settings.progressTotal = total ? total : 1;
199
+ } else {
200
+ total = 0;
201
+ for (i = 0; i < files.length; i += 1) {
202
+ total += files[i].size;
203
+ }
204
+ settings.progressTotal = total;
205
+ }
206
+ }
207
+ return settings.progressTotal;
208
+ },
209
+
210
+ handleGlobalProgress = function (event, files, index, xhr, settings) {
211
+ var progressEvent,
212
+ loaderList,
213
+ globalLoaded = 0,
214
+ globalTotal = 0;
215
+ if (event.lengthComputable && typeof settings.onProgressAll === func) {
216
+ settings.progressLoaded = parseInt(
217
+ event.loaded / event.total * getProgressTotal(files, index, settings),
218
+ 10
219
+ );
220
+ loaderList = multiLoader.getList();
221
+ $.each(loaderList, function (index, item) {
222
+ // item is an array with [files, index, xhr, settings]
223
+ globalLoaded += item[3].progressLoaded || 0;
224
+ globalTotal += getProgressTotal(item[0], item[1], item[3]);
225
+ });
226
+ progressEvent = createProgressEvent(
227
+ true,
228
+ globalLoaded,
229
+ globalTotal
230
+ );
231
+ settings.onProgressAll(progressEvent, loaderList);
232
+ }
233
+ },
234
+
235
+ handleLoadEvent = function (event, files, index, xhr, settings) {
236
+ var progressEvent;
237
+ if (isChunkedUpload(settings)) {
238
+ settings.uploadedBytes += settings.chunkSize;
239
+ progressEvent = createProgressEvent(
240
+ true,
241
+ settings.uploadedBytes,
242
+ files[index].size
243
+ );
244
+ if (typeof settings.onProgress === func) {
245
+ settings.onProgress(progressEvent, files, index, xhr, settings);
246
+ }
247
+ handleGlobalProgress(progressEvent, files, index, xhr, settings);
248
+ if (settings.uploadedBytes < files[index].size) {
249
+ if (typeof settings.resumeUpload === func) {
250
+ settings.resumeUpload(
251
+ event,
252
+ files,
253
+ index,
254
+ xhr,
255
+ settings,
256
+ function () {
257
+ upload(event, files, index, xhr, settings, true);
258
+ }
259
+ );
260
+ } else {
261
+ upload(event, files, index, xhr, settings, true);
262
+ }
263
+ return;
264
+ }
265
+ }
266
+ settings.progressLoaded = getProgressTotal(files, index, settings);
107
267
  if (typeof settings.onLoad === func) {
108
- xhr.onload = function (e) {
109
- settings.onLoad(e, files, index, xhr, settings);
110
- };
268
+ settings.onLoad(event, files, index, xhr, settings);
111
269
  }
112
- if (typeof settings.onAbort === func) {
113
- xhr.onabort = function (e) {
114
- settings.onAbort(e, files, index, xhr, settings);
270
+ completeNext();
271
+ },
272
+
273
+ handleProgressEvent = function (event, files, index, xhr, settings) {
274
+ var progressEvent = event;
275
+ if (isChunkedUpload(settings) && event.lengthComputable) {
276
+ progressEvent = createProgressEvent(
277
+ true,
278
+ settings.uploadedBytes + parseInt(event.loaded / event.total * settings.chunkSize, 10),
279
+ files[index].size
280
+ );
281
+ }
282
+ if (typeof settings.onProgress === func) {
283
+ settings.onProgress(progressEvent, files, index, xhr, settings);
284
+ }
285
+ handleGlobalProgress(progressEvent, files, index, xhr, settings);
286
+ },
287
+
288
+ initUploadEventHandlers = function (files, index, xhr, settings) {
289
+ if (xhr.upload) {
290
+ xhr.upload.onprogress = function (e) {
291
+ handleProgressEvent(e, files, index, xhr, settings);
115
292
  };
116
293
  }
117
- if (typeof settings.onError === func) {
118
- xhr.onerror = function (e) {
294
+ xhr.onload = function (e) {
295
+ handleLoadEvent(e, files, index, xhr, settings);
296
+ };
297
+ xhr.onabort = function (e) {
298
+ settings.progressTotal = settings.progressLoaded;
299
+ if (typeof settings.onAbort === func) {
300
+ settings.onAbort(e, files, index, xhr, settings);
301
+ }
302
+ completeNext();
303
+ };
304
+ xhr.onerror = function (e) {
305
+ settings.progressTotal = settings.progressLoaded;
306
+ if (typeof settings.onError === func) {
119
307
  settings.onError(e, files, index, xhr, settings);
120
- };
308
+ }
309
+ completeNext();
310
+ };
311
+ },
312
+
313
+ getUrl = function (settings) {
314
+ if (typeof settings.url === func) {
315
+ return settings.url(settings.uploadForm || uploadForm);
316
+ }
317
+ return settings.url;
318
+ },
319
+
320
+ getMethod = function (settings) {
321
+ if (typeof settings.method === func) {
322
+ return settings.method(settings.uploadForm || uploadForm);
323
+ }
324
+ return settings.method;
325
+ },
326
+
327
+ getFieldName = function (settings) {
328
+ if (typeof settings.fieldName === func) {
329
+ return settings.fieldName(settings.fileInput || fileInput);
121
330
  }
331
+ return settings.fieldName;
122
332
  },
123
333
 
124
334
  getFormData = function (settings) {
335
+ var formData;
125
336
  if (typeof settings.formData === func) {
126
- return settings.formData();
337
+ return settings.formData(settings.uploadForm || uploadForm);
127
338
  } else if ($.isArray(settings.formData)) {
128
339
  return settings.formData;
129
340
  } else if (settings.formData) {
130
- var formData = [];
341
+ formData = [];
131
342
  $.each(settings.formData, function (name, value) {
132
343
  formData.push({name: name, value: value});
133
344
  });
@@ -151,12 +362,35 @@
151
362
  return true;
152
363
  },
153
364
 
154
- nonMultipartUpload = function (file, xhr, sameDomain) {
365
+ initUploadRequest = function (files, index, xhr, settings) {
366
+ var file = files[index],
367
+ url = getUrl(settings),
368
+ sameDomain = isSameDomain(url);
369
+ xhr.open(getMethod(settings), url, true);
155
370
  if (sameDomain) {
156
- xhr.setRequestHeader('X-File-Name', unescape(encodeURIComponent(file.name)));
371
+ xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
372
+ if (!settings.multipart || isChunkedUpload(settings)) {
373
+ xhr.setRequestHeader('X-File-Name', file.name);
374
+ xhr.setRequestHeader('X-File-Type', file.type);
375
+ xhr.setRequestHeader('X-File-Size', file.size);
376
+ if (!isChunkedUpload(settings)) {
377
+ xhr.setRequestHeader('Content-Type', file.type);
378
+ } else if (!settings.multipart) {
379
+ xhr.setRequestHeader('Content-Type', 'application/octet-stream');
380
+ }
381
+ }
382
+ } else if (settings.withCredentials) {
383
+ xhr.withCredentials = true;
384
+ }
385
+ if ($.isArray(settings.requestHeaders)) {
386
+ $.each(settings.requestHeaders, function (index, header) {
387
+ xhr.setRequestHeader(header.name, header.value);
388
+ });
389
+ } else if (settings.requestHeaders) {
390
+ $.each(settings.requestHeaders, function (name, value) {
391
+ xhr.setRequestHeader(name, value);
392
+ });
157
393
  }
158
- xhr.setRequestHeader('Content-Type', file.type);
159
- xhr.send(file);
160
394
  },
161
395
 
162
396
  formDataUpload = function (files, xhr, settings) {
@@ -166,41 +400,47 @@
166
400
  formData.append(field.name, field.value);
167
401
  });
168
402
  for (i = 0; i < files.length; i += 1) {
169
- formData.append(settings.fieldName, files[i]);
403
+ formData.append(getFieldName(settings), files[i]);
170
404
  }
171
405
  xhr.send(formData);
172
406
  },
173
407
 
174
408
  loadFileContent = function (file, callBack) {
175
- var fileReader = new FileReader();
176
- fileReader.onload = function (e) {
177
- file.content = e.target.result;
178
- callBack();
179
- };
180
- fileReader.readAsBinaryString(file);
409
+ file.reader = new FileReader();
410
+ file.reader.onload = callBack;
411
+ file.reader.readAsBinaryString(file);
181
412
  },
182
413
 
183
- buildMultiPartFormData = function (boundary, files, fields) {
414
+ utf8encode = function (str) {
415
+ return unescape(encodeURIComponent(str));
416
+ },
417
+
418
+ buildMultiPartFormData = function (boundary, files, filesFieldName, fields) {
184
419
  var doubleDash = '--',
185
420
  crlf = '\r\n',
186
- formData = '';
421
+ formData = '',
422
+ buffer = [];
187
423
  $.each(fields, function (index, field) {
188
424
  formData += doubleDash + boundary + crlf +
189
425
  'Content-Disposition: form-data; name="' +
190
- unescape(encodeURIComponent(field.name)) +
426
+ utf8encode(field.name) +
191
427
  '"' + crlf + crlf +
192
- unescape(encodeURIComponent(field.value)) + crlf;
428
+ utf8encode(field.value) + crlf;
193
429
  });
194
430
  $.each(files, function (index, file) {
195
431
  formData += doubleDash + boundary + crlf +
196
432
  'Content-Disposition: form-data; name="' +
197
- unescape(encodeURIComponent(settings.fieldName)) +
198
- '"; filename="' + unescape(encodeURIComponent(file.name)) + '"' + crlf +
199
- 'Content-Type: ' + file.type + crlf + crlf +
200
- file.content + crlf;
433
+ utf8encode(filesFieldName) +
434
+ '"; filename="' + utf8encode(file.name) + '"' + crlf +
435
+ 'Content-Type: ' + utf8encode(file.type) + crlf + crlf;
436
+ buffer.push(formData);
437
+ buffer.push(file.reader.result);
438
+ delete file.reader;
439
+ formData = crlf;
201
440
  });
202
441
  formData += doubleDash + boundary + doubleDash + crlf;
203
- return formData;
442
+ buffer.push(formData);
443
+ return buffer.join('');
204
444
  },
205
445
 
206
446
  fileReaderUpload = function (files, xhr, settings) {
@@ -212,6 +452,7 @@
212
452
  xhr.sendAsBinary(buildMultiPartFormData(
213
453
  boundary,
214
454
  files,
455
+ getFieldName(settings),
215
456
  getFormData(settings)
216
457
  ));
217
458
  }, files.length);
@@ -220,67 +461,99 @@
220
461
  }
221
462
  },
222
463
 
223
- upload = function (files, index, xhr, settings) {
224
- var sameDomain = isSameDomain(settings.url),
225
- filesToUpload;
226
- initUploadEventHandlers(files, index, xhr, settings);
227
- xhr.open(settings.method, settings.url, true);
228
- if (sameDomain) {
229
- xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
230
- } else if (settings.withCredentials) {
231
- xhr.withCredentials = true;
464
+ getBlob = function (file, settings) {
465
+ var blob,
466
+ ub = settings.uploadedBytes,
467
+ mcs = settings.maxChunkSize;
468
+ if (file && typeof file.slice === func && (ub || (mcs && mcs < file.size))) {
469
+ settings.uploadedBytes = ub = ub || 0;
470
+ blob = file.slice(ub, mcs || file.size - ub);
471
+ settings.chunkSize = blob.size;
472
+ return blob;
232
473
  }
233
- if (!settings.multipart) {
234
- nonMultipartUpload(files[index], xhr, sameDomain);
235
- } else {
236
- if (typeof index === num) {
237
- filesToUpload = [files[index]];
238
- } else {
239
- filesToUpload = files;
474
+ return file;
475
+ },
476
+
477
+ upload = function (event, files, index, xhr, settings, nextChunk) {
478
+ var send;
479
+ send = function () {
480
+ if (!nextChunk) {
481
+ if (typeof settings.onSend === func &&
482
+ settings.onSend(event, files, index, xhr, settings) === false) {
483
+ completeNext();
484
+ return;
485
+ }
240
486
  }
241
- if (typeof FormData !== undef) {
242
- formDataUpload(filesToUpload, xhr, settings);
243
- } else if (typeof FileReader !== undef) {
244
- fileReaderUpload(filesToUpload, xhr, settings);
487
+ var blob = getBlob(files[index], settings),
488
+ filesToUpload;
489
+ initUploadEventHandlers(files, index, xhr, settings);
490
+ initUploadRequest(files, index, xhr, settings);
491
+ if (!settings.multipart) {
492
+ if (xhr.upload) {
493
+ xhr.send(blob);
494
+ } else {
495
+ $.error('Browser does not support XHR file uploads');
496
+ }
245
497
  } else {
246
- $.error('Browser does neither support FormData nor FileReader interface');
498
+ filesToUpload = (typeof index === 'number') ? [blob] : files;
499
+ if (typeof FormData !== undef) {
500
+ formDataUpload(filesToUpload, xhr, settings);
501
+ } else if (typeof FileReader !== undef && typeof xhr.sendAsBinary === func) {
502
+ fileReaderUpload(filesToUpload, xhr, settings);
503
+ } else {
504
+ $.error('Browser does not support multipart/form-data XHR file uploads');
505
+ }
506
+ }
507
+ };
508
+ if (!nextChunk) {
509
+ multiLoader.push(Array.prototype.slice.call(arguments, 1));
510
+ if (settings.sequentialUploads) {
511
+ sequenceHandler.push(send);
512
+ return;
247
513
  }
248
514
  }
515
+ send();
249
516
  },
250
517
 
251
- handleUpload = function (event, files, index) {
518
+ handleUpload = function (event, files, input, form, index) {
252
519
  var xhr = new XMLHttpRequest(),
253
520
  uploadSettings = $.extend({}, settings);
254
- if (typeof settings.initUpload === func) {
255
- settings.initUpload(
521
+ uploadSettings.fileInput = input;
522
+ uploadSettings.uploadForm = form;
523
+ if (typeof uploadSettings.initUpload === func) {
524
+ uploadSettings.initUpload(
256
525
  event,
257
526
  files,
258
527
  index,
259
528
  xhr,
260
529
  uploadSettings,
261
530
  function () {
262
- upload(files, index, xhr, uploadSettings);
531
+ upload(event, files, index, xhr, uploadSettings);
263
532
  }
264
533
  );
265
534
  } else {
266
- upload(files, index, xhr, uploadSettings);
535
+ upload(event, files, index, xhr, uploadSettings);
267
536
  }
268
537
  },
269
538
 
270
- handleFiles = function (event, files) {
271
- var i;
272
- if (settings.multiFileRequest) {
273
- handleUpload(event, files);
539
+ handleLegacyGlobalProgress = function (event, files, index, iframe, settings) {
540
+ var total = 0,
541
+ progressEvent;
542
+ if (typeof index === undef) {
543
+ $.each(files, function (index, file) {
544
+ total += file.size ? file.size : 1;
545
+ });
274
546
  } else {
275
- for (i = 0; i < files.length; i += 1) {
276
- handleUpload(event, files, i);
277
- }
547
+ total = files[index].size ? files[index].size : 1;
278
548
  }
549
+ progressEvent = createProgressEvent(true, total, total);
550
+ settings.progressLoaded = total;
551
+ handleGlobalProgress(progressEvent, files, index, iframe, settings);
279
552
  },
280
553
 
281
- legacyUploadFormDataInit = function (input, settings) {
554
+ legacyUploadFormDataInit = function (input, form, settings) {
282
555
  var formData = getFormData(settings);
283
- uploadForm.find(':input').not(':disabled')
556
+ form.find(':input').not(':disabled')
284
557
  .attr('disabled', true)
285
558
  .addClass(settings.namespace + '_disabled');
286
559
  $.each(formData, function (index, field) {
@@ -288,80 +561,165 @@
288
561
  .attr('name', field.name)
289
562
  .val(field.value)
290
563
  .addClass(settings.namespace + '_form_data')
291
- .insertBefore(fileInput);
564
+ .appendTo(form);
292
565
  });
293
- input.insertAfter(fileInput);
566
+ input
567
+ .attr('name', getFieldName(settings))
568
+ .appendTo(form);
294
569
  },
295
570
 
296
- legacyUploadFormDataReset = function (input, settings) {
297
- input.remove();
298
- uploadForm.find('.' + settings.namespace + '_disabled')
571
+ legacyUploadFormDataReset = function (input, form, settings) {
572
+ input.detach();
573
+ form.find('.' + settings.namespace + '_disabled')
299
574
  .removeAttr('disabled')
300
575
  .removeClass(settings.namespace + '_disabled');
301
- uploadForm.find('.' + settings.namespace + '_form_data').remove();
302
- },
303
-
304
- legacyUpload = function (input, iframe, settings) {
305
- iframe
306
- .unbind('abort')
307
- .bind('abort', function (e) {
308
- iframe.readyState = 0;
309
- // javascript:false as iframe src prevents warning popups on HTTPS in IE6
310
- // concat is used here to prevent the "Script URL" JSLint error:
311
- iframe.unbind('load').attr('src', 'javascript'.concat(':false;'));
312
- if (typeof settings.onAbort === func) {
313
- settings.onAbort(e, [{name: input.val(), type: null, size: null}], 0, iframe, settings);
314
- }
315
- })
316
- .unbind('load')
317
- .bind('load', function (e) {
318
- iframe.readyState = 4;
319
- if (typeof settings.onLoad === func) {
320
- settings.onLoad(e, [{name: input.val(), type: null, size: null}], 0, iframe, settings);
321
- }
322
- });
323
- uploadForm
324
- .attr('action', settings.url)
325
- .attr('target', iframe.attr('name'));
326
- legacyUploadFormDataInit(input, settings);
327
- iframe.readyState = 2;
328
- uploadForm.get(0).submit();
329
- legacyUploadFormDataReset(input, settings);
576
+ form.find('.' + settings.namespace + '_form_data').remove();
577
+ },
578
+
579
+ legacyUpload = function (event, files, input, form, iframe, settings, index) {
580
+ var send;
581
+ send = function () {
582
+ if (typeof settings.onSend === func && settings.onSend(event, files, index, iframe, settings) === false) {
583
+ completeNext();
584
+ return;
585
+ }
586
+ var originalAction = form.attr('action'),
587
+ originalMethod = form.attr('method'),
588
+ originalTarget = form.attr('target');
589
+ iframe
590
+ .unbind('abort')
591
+ .bind('abort', function (e) {
592
+ iframe.readyState = 0;
593
+ // javascript:false as iframe src prevents warning popups on HTTPS in IE6
594
+ // concat is used here to prevent the "Script URL" JSLint error:
595
+ iframe.unbind('load').attr('src', 'javascript'.concat(':false;'));
596
+ handleLegacyGlobalProgress(e, files, index, iframe, settings);
597
+ if (typeof settings.onAbort === func) {
598
+ settings.onAbort(e, files, index, iframe, settings);
599
+ }
600
+ completeNext();
601
+ })
602
+ .unbind('load')
603
+ .bind('load', function (e) {
604
+ iframe.readyState = 4;
605
+ handleLegacyGlobalProgress(e, files, index, iframe, settings);
606
+ if (typeof settings.onLoad === func) {
607
+ settings.onLoad(e, files, index, iframe, settings);
608
+ }
609
+ // Fix for IE endless progress bar activity bug
610
+ // (happens on form submits to iframe targets):
611
+ $('<iframe src="javascript:false;" style="display:none;"></iframe>')
612
+ .appendTo(form).remove();
613
+ completeNext();
614
+ });
615
+ form
616
+ .attr('action', getUrl(settings))
617
+ .attr('method', getMethod(settings))
618
+ .attr('target', iframe.attr('name'));
619
+ legacyUploadFormDataInit(input, form, settings);
620
+ iframe.readyState = 2;
621
+ form.get(0).submit();
622
+ legacyUploadFormDataReset(input, form, settings);
623
+ form
624
+ .attr('action', originalAction)
625
+ .attr('method', originalMethod)
626
+ .attr('target', originalTarget);
627
+ };
628
+ multiLoader.push([files, index, iframe, settings]);
629
+ if (settings.sequentialUploads) {
630
+ sequenceHandler.push(send);
631
+ } else {
632
+ send();
633
+ }
330
634
  },
331
635
 
332
- handleLegacyUpload = function (event, input) {
636
+ handleLegacyUpload = function (event, input, form, index) {
637
+ if (!(event && input && form)) {
638
+ $.error('Iframe based File Upload requires a file input change event');
639
+ return;
640
+ }
333
641
  // javascript:false as iframe src prevents warning popups on HTTPS in IE6:
334
- var iframe = $('<iframe src="javascript:false;" style="display:none" name="iframe_' +
642
+ var iframe = $('<iframe src="javascript:false;" style="display:none;" name="iframe_' +
335
643
  settings.namespace + '_' + (new Date()).getTime() + '"></iframe>'),
336
- uploadSettings = $.extend({}, settings);
644
+ uploadSettings = $.extend({}, settings),
645
+ files = event.target && event.target.files;
646
+ files = files ? Array.prototype.slice.call(files, 0) : [{name: input.val(), type: null, size: null}];
647
+ index = files.length === 1 ? 0 : index;
648
+ uploadSettings.fileInput = input;
649
+ uploadSettings.uploadForm = form;
337
650
  iframe.readyState = 0;
338
651
  iframe.abort = function () {
339
652
  iframe.trigger('abort');
340
653
  };
341
654
  iframe.bind('load', function () {
342
655
  iframe.unbind('load');
343
- if (typeof settings.initUpload === func) {
344
- settings.initUpload(
656
+ if (typeof uploadSettings.initUpload === func) {
657
+ uploadSettings.initUpload(
345
658
  event,
346
- [{name: input.val(), type: null, size: null}],
347
- 0,
659
+ files,
660
+ index,
348
661
  iframe,
349
662
  uploadSettings,
350
663
  function () {
351
- legacyUpload(input, iframe, uploadSettings);
664
+ legacyUpload(event, files, input, form, iframe, uploadSettings, index);
352
665
  }
353
666
  );
354
667
  } else {
355
- legacyUpload(input, iframe, uploadSettings);
668
+ legacyUpload(event, files, input, form, iframe, uploadSettings, index);
669
+ }
670
+ }).appendTo(form);
671
+ },
672
+
673
+ canHandleXHRUploadSize = function (files) {
674
+ var bytes = 0,
675
+ totalBytes = 0,
676
+ i;
677
+ if (settings.multipart && typeof FormData === undef) {
678
+ for (i = 0; i < files.length; i += 1) {
679
+ bytes = files[i].size;
680
+ if (bytes > settings.maxFileReaderSize) {
681
+ return false;
682
+ }
683
+ totalBytes += bytes;
684
+ }
685
+ if (settings.multiFileRequest && totalBytes > settings.maxFileReaderSize) {
686
+ return false;
356
687
  }
357
- }).appendTo(uploadForm);
688
+ }
689
+ return true;
358
690
  },
359
691
 
360
- resetFileInput = function () {
361
- var inputClone = fileInput.clone(true);
692
+ handleFiles = function (event, files, input, form) {
693
+ if (!canHandleXHRUploadSize(files)) {
694
+ handleLegacyUpload(event, input, form);
695
+ return;
696
+ }
697
+ var i;
698
+ files = Array.prototype.slice.call(files, 0);
699
+ if (settings.multiFileRequest && settings.multipart && files.length) {
700
+ handleUpload(event, files, input, form);
701
+ } else {
702
+ for (i = 0; i < files.length; i += 1) {
703
+ handleUpload(event, files, input, form, i);
704
+ }
705
+ }
706
+ },
707
+
708
+ initUploadForm = function () {
709
+ uploadForm = (container.is('form') ? container : container.find('form'))
710
+ .filter(settings.uploadFormFilter);
711
+ },
712
+
713
+ initFileInput = function () {
714
+ fileInput = (uploadForm.length ? uploadForm : container).find('input:file')
715
+ .filter(settings.fileInputFilter);
716
+ },
717
+
718
+ replaceFileInput = function (input) {
719
+ var inputClone = input.clone(true);
362
720
  $('<form/>').append(inputClone).get(0).reset();
363
- fileInput.replaceWith(inputClone);
364
- fileInput = inputClone;
721
+ input.after(inputClone).detach();
722
+ initFileInput();
365
723
  };
366
724
 
367
725
  this.onDocumentDragOver = function (e) {
@@ -386,10 +744,10 @@
386
744
  return false;
387
745
  }
388
746
  var dataTransfer = e.originalEvent.dataTransfer;
389
- if (dataTransfer) {
747
+ if (dataTransfer && dataTransfer.files) {
390
748
  dataTransfer.dropEffect = dataTransfer.effectAllowed = 'copy';
749
+ e.preventDefault();
391
750
  }
392
- e.preventDefault();
393
751
  };
394
752
 
395
753
  this.onDrop = function (e) {
@@ -409,18 +767,28 @@
409
767
  settings.onChange(e) === false) {
410
768
  return false;
411
769
  }
770
+ var input = $(e.target),
771
+ form = $(e.target.form);
772
+ if (form.length === 1) {
773
+ input.data(defaultNamespace + '_form', form);
774
+ replaceFileInput(input);
775
+ } else {
776
+ form = input.data(defaultNamespace + '_form');
777
+ }
412
778
  if (!settings.forceIframeUpload && e.target.files && isXHRUploadCapable()) {
413
- handleFiles(e, e.target.files);
779
+ handleFiles(e, e.target.files, input, form);
414
780
  } else {
415
- handleLegacyUpload(e, $(e.target));
781
+ handleLegacyUpload(e, input, form);
416
782
  }
417
- resetFileInput();
418
783
  };
419
784
 
420
785
  this.init = function (options) {
421
786
  if (options) {
422
787
  $.extend(settings, options);
788
+ optionsReference = options;
423
789
  }
790
+ initUploadForm();
791
+ initFileInput();
424
792
  if (container.data(settings.namespace)) {
425
793
  $.error('FileUpload with namespace "' + settings.namespace + '" already assigned to this element');
426
794
  return;
@@ -428,16 +796,92 @@
428
796
  container
429
797
  .data(settings.namespace, fileUpload)
430
798
  .addClass(settings.cssClass);
431
- settings.dropZone.addClass(settings.cssClass);
799
+ settings.dropZone.not(container).addClass(settings.cssClass);
800
+ initEventHandlers();
801
+ if (typeof settings.init === func) {
802
+ settings.init();
803
+ }
804
+ };
805
+
806
+ this.options = function (options) {
807
+ var oldCssClass,
808
+ oldDropZone,
809
+ uploadFormFilterUpdate,
810
+ fileInputFilterUpdate;
811
+ if (typeof options === undef) {
812
+ return $.extend({}, settings);
813
+ }
814
+ if (optionsReference) {
815
+ $.extend(optionsReference, options);
816
+ }
817
+ removeEventHandlers();
818
+ $.each(options, function (name, value) {
819
+ switch (name) {
820
+ case 'namespace':
821
+ $.error('The FileUpload namespace cannot be updated.');
822
+ return;
823
+ case 'uploadFormFilter':
824
+ uploadFormFilterUpdate = true;
825
+ fileInputFilterUpdate = true;
826
+ break;
827
+ case 'fileInputFilter':
828
+ fileInputFilterUpdate = true;
829
+ break;
830
+ case 'cssClass':
831
+ oldCssClass = settings.cssClass;
832
+ break;
833
+ case 'dropZone':
834
+ oldDropZone = settings.dropZone;
835
+ break;
836
+ }
837
+ settings[name] = value;
838
+ });
839
+ if (uploadFormFilterUpdate) {
840
+ initUploadForm();
841
+ }
842
+ if (fileInputFilterUpdate) {
843
+ initFileInput();
844
+ }
845
+ if (typeof oldCssClass !== undef) {
846
+ container
847
+ .removeClass(oldCssClass)
848
+ .addClass(settings.cssClass);
849
+ (oldDropZone ? oldDropZone : settings.dropZone).not(container)
850
+ .removeClass(oldCssClass);
851
+ settings.dropZone.not(container).addClass(settings.cssClass);
852
+ } else if (oldDropZone) {
853
+ oldDropZone.not(container).removeClass(settings.cssClass);
854
+ settings.dropZone.not(container).addClass(settings.cssClass);
855
+ }
432
856
  initEventHandlers();
433
857
  };
434
858
 
859
+ this.option = function (name, value) {
860
+ var options;
861
+ if (typeof value === undef) {
862
+ return settings[name];
863
+ }
864
+ options = {};
865
+ options[name] = value;
866
+ fileUpload.options(options);
867
+ };
868
+
435
869
  this.destroy = function () {
870
+ if (typeof settings.destroy === func) {
871
+ settings.destroy();
872
+ }
436
873
  removeEventHandlers();
437
874
  container
438
875
  .removeData(settings.namespace)
439
876
  .removeClass(settings.cssClass);
440
- settings.dropZone.removeClass(settings.cssClass);
877
+ settings.dropZone.not(container).removeClass(settings.cssClass);
878
+ };
879
+
880
+ this.upload = function (files) {
881
+ if (typeof files.length === undef) {
882
+ files = [files];
883
+ }
884
+ handleFiles(null, files);
441
885
  };
442
886
  };
443
887
 
@@ -447,10 +891,36 @@
447
891
  (new FileUpload($(this))).init(options);
448
892
  });
449
893
  },
894
+
895
+ option: function (option, value, namespace) {
896
+ namespace = namespace ? namespace : defaultNamespace;
897
+ var fileUpload = $(this).data(namespace);
898
+ if (fileUpload) {
899
+ if (!option) {
900
+ return fileUpload.options();
901
+ } else if (typeof option === 'string' && typeof value === undef) {
902
+ return fileUpload.option(option);
903
+ }
904
+ } else {
905
+ $.error('No FileUpload with namespace "' + namespace + '" assigned to this element');
906
+ }
907
+ return this.each(function () {
908
+ var fu = $(this).data(namespace);
909
+ if (fu) {
910
+ if (typeof option === 'string') {
911
+ fu.option(option, value);
912
+ } else {
913
+ fu.options(option);
914
+ }
915
+ } else {
916
+ $.error('No FileUpload with namespace "' + namespace + '" assigned to this element');
917
+ }
918
+ });
919
+ },
450
920
 
451
- destroy : function (namespace) {
921
+ destroy: function (namespace) {
922
+ namespace = namespace ? namespace : defaultNamespace;
452
923
  return this.each(function () {
453
- namespace = namespace ? namespace : 'file_upload';
454
924
  var fileUpload = $(this).data(namespace);
455
925
  if (fileUpload) {
456
926
  fileUpload.destroy();
@@ -458,7 +928,18 @@
458
928
  $.error('No FileUpload with namespace "' + namespace + '" assigned to this element');
459
929
  }
460
930
  });
461
-
931
+ },
932
+
933
+ upload: function (files, namespace) {
934
+ namespace = namespace ? namespace : defaultNamespace;
935
+ return this.each(function () {
936
+ var fileUpload = $(this).data(namespace);
937
+ if (fileUpload) {
938
+ fileUpload.upload(files);
939
+ } else {
940
+ $.error('No FileUpload with namespace "' + namespace + '" assigned to this element');
941
+ }
942
+ });
462
943
  }
463
944
  };
464
945
 
@@ -468,7 +949,7 @@
468
949
  } else if (typeof method === 'object' || !method) {
469
950
  return methods.init.apply(this, arguments);
470
951
  } else {
471
- $.error('Method ' + method + ' does not exist on jQuery.fileUpload');
952
+ $.error('Method "' + method + '" does not exist on jQuery.fileUpload');
472
953
  }
473
954
  };
474
955