fine_uploader 3.4.1 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,85 +2,87 @@
2
2
  qq.DragAndDrop = function(o) {
3
3
  "use strict";
4
4
 
5
- var options, dz, dirPending,
5
+ var options, dz,
6
6
  droppedFiles = [],
7
- droppedEntriesCount = 0,
8
- droppedEntriesParsedCount = 0,
9
7
  disposeSupport = new qq.DisposeSupport();
10
8
 
11
9
  options = {
12
- dropArea: null,
13
- extraDropzones: [],
14
- hideDropzones: true,
15
- multiple: true,
10
+ dropZoneElements: [],
11
+ hideDropZonesBeforeEnter: false,
12
+ allowMultipleItems: true,
16
13
  classes: {
17
14
  dropActive: null
18
15
  },
19
- callbacks: {
20
- dropProcessing: function(isProcessing, files) {},
21
- error: function(code, filename) {},
22
- log: function(message, level) {}
23
- }
16
+ callbacks: new qq.DragAndDrop.callbacks()
24
17
  };
25
18
 
26
- qq.extend(options, o);
19
+ qq.extend(options, o, true);
27
20
 
28
- function maybeUploadDroppedFiles() {
29
- if (droppedEntriesCount === droppedEntriesParsedCount && !dirPending) {
30
- options.callbacks.log('Grabbed ' + droppedFiles.length + " files after tree traversal.");
31
- dz.dropDisabled(false);
32
- options.callbacks.dropProcessing(false, droppedFiles);
33
- }
34
- }
35
- function addDroppedFile(file) {
36
- droppedFiles.push(file);
37
- droppedEntriesParsedCount+=1;
38
- maybeUploadDroppedFiles();
21
+ setupDragDrop();
22
+
23
+ function uploadDroppedFiles(files) {
24
+ options.callbacks.dropLog('Grabbed ' + files.length + " dropped files.");
25
+ dz.dropDisabled(false);
26
+ options.callbacks.processingDroppedFilesComplete(files);
39
27
  }
40
28
 
41
29
  function traverseFileTree(entry) {
42
- var dirReader, i;
43
-
44
- droppedEntriesCount+=1;
30
+ var dirReader, i,
31
+ parseEntryPromise = new qq.Promise();
45
32
 
46
33
  if (entry.isFile) {
47
34
  entry.file(function(file) {
48
- addDroppedFile(file);
35
+ droppedFiles.push(file);
36
+ parseEntryPromise.success();
37
+ },
38
+ function(fileError) {
39
+ options.callbacks.dropLog("Problem parsing '" + entry.fullPath + "'. FileError code " + fileError.code + ".", "error");
40
+ parseEntryPromise.failure();
49
41
  });
50
42
  }
51
43
  else if (entry.isDirectory) {
52
- dirPending = true;
53
44
  dirReader = entry.createReader();
54
45
  dirReader.readEntries(function(entries) {
55
- droppedEntriesParsedCount+=1;
46
+ var entriesLeft = entries.length;
47
+
56
48
  for (i = 0; i < entries.length; i+=1) {
57
- traverseFileTree(entries[i]);
58
- }
49
+ traverseFileTree(entries[i]).done(function() {
50
+ entriesLeft-=1;
59
51
 
60
- dirPending = false;
52
+ if (entriesLeft === 0) {
53
+ parseEntryPromise.success();
54
+ }
55
+ });
56
+ }
61
57
 
62
58
  if (!entries.length) {
63
- maybeUploadDroppedFiles();
59
+ parseEntryPromise.success();
64
60
  }
61
+ }, function(fileError) {
62
+ options.callbacks.dropLog("Problem parsing '" + entry.fullPath + "'. FileError code " + fileError.code + ".", "error");
63
+ parseEntryPromise.failure();
65
64
  });
66
65
  }
66
+
67
+ return parseEntryPromise;
67
68
  }
68
69
 
69
70
  function handleDataTransfer(dataTransfer) {
70
- var i, items, entry;
71
+ var i, items, entry,
72
+ pendingFolderPromises = [],
73
+ handleDataTransferPromise = new qq.Promise();
71
74
 
72
- options.callbacks.dropProcessing(true);
75
+ options.callbacks.processingDroppedFiles();
73
76
  dz.dropDisabled(true);
74
77
 
75
- if (dataTransfer.files.length > 1 && !options.multiple) {
76
- options.callbacks.dropProcessing(false);
77
- options.callbacks.error('tooManyFilesError', "");
78
+ if (dataTransfer.files.length > 1 && !options.allowMultipleItems) {
79
+ options.callbacks.processingDroppedFilesComplete([]);
80
+ options.callbacks.dropError('tooManyFilesError', "");
78
81
  dz.dropDisabled(false);
82
+ handleDataTransferPromise.failure();
79
83
  }
80
84
  else {
81
85
  droppedFiles = [];
82
- droppedEntriesCount = 0;
83
- droppedEntriesParsedCount = 0;
84
86
 
85
87
  if (qq.isFolderDropSupported(dataTransfer)) {
86
88
  items = dataTransfer.items;
@@ -91,22 +93,29 @@ qq.DragAndDrop = function(o) {
91
93
  //due to a bug in Chrome's File System API impl - #149735
92
94
  if (entry.isFile) {
93
95
  droppedFiles.push(items[i].getAsFile());
94
- if (i === items.length-1) {
95
- maybeUploadDroppedFiles();
96
- }
97
96
  }
98
97
 
99
98
  else {
100
- traverseFileTree(entry);
99
+ pendingFolderPromises.push(traverseFileTree(entry).done(function() {
100
+ pendingFolderPromises.pop();
101
+ if (pendingFolderPromises.length === 0) {
102
+ handleDataTransferPromise.success();
103
+ }
104
+ }));
101
105
  }
102
106
  }
103
107
  }
104
108
  }
105
109
  else {
106
- options.callbacks.dropProcessing(false, dataTransfer.files);
107
- dz.dropDisabled(false);
110
+ droppedFiles = dataTransfer.files;
111
+ }
112
+
113
+ if (pendingFolderPromises.length === 0) {
114
+ handleDataTransferPromise.success();
108
115
  }
109
116
  }
117
+
118
+ return handleDataTransferPromise;
110
119
  }
111
120
 
112
121
  function setupDropzone(dropArea){
@@ -120,12 +129,14 @@ qq.DragAndDrop = function(o) {
120
129
  qq(dropArea).removeClass(options.classes.dropActive);
121
130
  },
122
131
  onDrop: function(e){
123
- if (options.hideDropzones) {
132
+ if (options.hideDropZonesBeforeEnter) {
124
133
  qq(dropArea).hide();
125
134
  }
126
135
  qq(dropArea).removeClass(options.classes.dropActive);
127
136
 
128
- handleDataTransfer(e.dataTransfer);
137
+ handleDataTransfer(e.dataTransfer).done(function() {
138
+ uploadDroppedFiles(droppedFiles);
139
+ });
129
140
  }
130
141
  });
131
142
 
@@ -133,7 +144,7 @@ qq.DragAndDrop = function(o) {
133
144
  dz.dispose();
134
145
  });
135
146
 
136
- if (options.hideDropzones) {
147
+ if (options.hideDropZonesBeforeEnter) {
137
148
  qq(dropArea).hide();
138
149
  }
139
150
  }
@@ -152,60 +163,49 @@ qq.DragAndDrop = function(o) {
152
163
  }
153
164
 
154
165
  function setupDragDrop(){
155
- if (options.dropArea) {
156
- options.extraDropzones.push(options.dropArea);
157
- }
158
-
159
- var i, dropzones = options.extraDropzones;
166
+ var dropZones = options.dropZoneElements;
160
167
 
161
- for (i=0; i < dropzones.length; i+=1){
162
- setupDropzone(dropzones[i]);
163
- }
168
+ qq.each(dropZones, function(idx, dropZone) {
169
+ setupDropzone(dropZone);
170
+ })
164
171
 
165
172
  // IE <= 9 does not support the File API used for drag+drop uploads
166
- if (options.dropArea && (!qq.ie() || qq.ie10())) {
173
+ if (dropZones.length && (!qq.ie() || qq.ie10())) {
167
174
  disposeSupport.attach(document, 'dragenter', function(e) {
168
175
  if (!dz.dropDisabled() && isFileDrag(e)) {
169
- if (qq(options.dropArea).hasClass(options.classes.dropDisabled)) {
170
- return;
171
- }
172
-
173
- options.dropArea.style.display = 'block';
174
- for (i=0; i < dropzones.length; i+=1) {
175
- dropzones[i].style.display = 'block';
176
- }
176
+ qq.each(dropZones, function(idx, dropZone) {
177
+ qq(dropZone).css({display: 'block'});
178
+ });
177
179
  }
178
180
  });
179
181
  }
180
182
  disposeSupport.attach(document, 'dragleave', function(e){
181
- if (options.hideDropzones && qq.FineUploader.prototype._leaving_document_out(e)) {
182
- for (i=0; i < dropzones.length; i+=1) {
183
- qq(dropzones[i]).hide();
184
- }
183
+ if (options.hideDropZonesBeforeEnter && qq.FineUploader.prototype._leaving_document_out(e)) {
184
+ qq.each(dropZones, function(idx, dropZone) {
185
+ qq(dropZone).hide();
186
+ });
185
187
  }
186
188
  });
187
189
  disposeSupport.attach(document, 'drop', function(e){
188
- if (options.hideDropzones) {
189
- for (i=0; i < dropzones.length; i+=1) {
190
- qq(dropzones[i]).hide();
191
- }
190
+ if (options.hideDropZonesBeforeEnter) {
191
+ qq.each(dropZones, function(idx, dropZone) {
192
+ qq(dropZone).hide();
193
+ });
192
194
  }
193
195
  e.preventDefault();
194
196
  });
195
197
  }
196
198
 
197
199
  return {
198
- setup: function() {
199
- setupDragDrop();
200
- },
201
-
202
200
  setupExtraDropzone: function(element) {
203
- options.extraDropzones.push(element);
201
+ options.dropZoneElements.push(element);
204
202
  setupDropzone(element);
205
203
  },
206
204
 
207
- removeExtraDropzone: function(element) {
208
- var i, dzs = options.extraDropzones;
205
+ removeDropzone: function(element) {
206
+ var i,
207
+ dzs = options.dropZoneElements;
208
+
209
209
  for(i in dzs) {
210
210
  if (dzs[i] === element) {
211
211
  return dzs.splice(i, 1);
@@ -220,6 +220,18 @@ qq.DragAndDrop = function(o) {
220
220
  };
221
221
  };
222
222
 
223
+ qq.DragAndDrop.callbacks = function() {
224
+ return {
225
+ processingDroppedFiles: function() {},
226
+ processingDroppedFilesComplete: function(files) {},
227
+ dropError: function(code, errorSpecifics) {
228
+ qq.log("Drag & drop error code '" + code + " with these specifics: '" + errorSpecifics + "'", "error");
229
+ },
230
+ dropLog: function(message, level) {
231
+ qq.log(message, level);
232
+ }
233
+ }
234
+ }
223
235
 
224
236
  qq.UploadDropZone = function(o){
225
237
  "use strict";
@@ -78,7 +78,7 @@ qq.UploadHandler = function(o) {
78
78
  }
79
79
  };
80
80
 
81
- if (qq.isXhrUploadSupported()) {
81
+ if (qq.supportedFeatures.ajaxUploading) {
82
82
  handlerImpl = new qq.UploadHandlerXhr(options, dequeue, log);
83
83
  }
84
84
  else {
@@ -7,8 +7,8 @@ qq.UploadHandlerXhr = function(o, uploadCompleteCallback, logCallback) {
7
7
  log = logCallback,
8
8
  fileState = [],
9
9
  cookieItemDelimiter = "|",
10
- chunkFiles = options.chunking.enabled && qq.isFileChunkingSupported(),
11
- resumeEnabled = options.resume.enabled && chunkFiles && qq.areCookiesEnabled(),
10
+ chunkFiles = options.chunking.enabled && qq.supportedFeatures.chunking,
11
+ resumeEnabled = options.resume.enabled && chunkFiles && qq.supportedFeatures.resume,
12
12
  resumeId = getResumeId(),
13
13
  multipart = options.forceMultipart || options.paramsInBody,
14
14
  api;
@@ -503,7 +503,7 @@ qq.UploadHandlerXhr = function(o, uploadCompleteCallback, logCallback) {
503
503
  if (fileOrBlobData instanceof File) {
504
504
  id = fileState.push({file: fileOrBlobData}) - 1;
505
505
  }
506
- else if (fileOrBlobData.blob instanceof Blob) {
506
+ else if (qq.isBlob(fileOrBlobData.blob)) {
507
507
  id = fileState.push({blobData: fileOrBlobData}) - 1;
508
508
  }
509
509
  else {
@@ -64,16 +64,9 @@
64
64
  $callbackEl = $el;
65
65
 
66
66
  callbacks[prop] = function() {
67
- var origFunc = func,
68
- args = Array.prototype.slice.call(arguments),
69
- jqueryHandlerResult = $callbackEl.triggerHandler(name, args);
67
+ var args = Array.prototype.slice.call(arguments);
70
68
 
71
- if (jqueryHandlerResult === undefined &&
72
- $.inArray(prop, uploaderInst.getPromissoryCallbackNames()) >= 0) {
73
- return origFunc();
74
- }
75
-
76
- return jqueryHandlerResult;
69
+ return $callbackEl.triggerHandler(name, args);
77
70
  };
78
71
  });
79
72
  };
@@ -3,7 +3,7 @@ qq.Promise = function() {
3
3
  "use strict";
4
4
 
5
5
  var successValue, failureValue,
6
- successCallback, failureCallback,
6
+ successCallback, failureCallback, doneCallback,
7
7
  state = 0;
8
8
 
9
9
  return {
@@ -18,6 +18,19 @@ qq.Promise = function() {
18
18
  else if (onSuccess) {
19
19
  onSuccess(successValue);
20
20
  }
21
+
22
+ return this;
23
+ },
24
+
25
+ done: function(callback) {
26
+ if (state === 0) {
27
+ doneCallback = callback;
28
+ }
29
+ else {
30
+ callback();
31
+ }
32
+
33
+ return this;
21
34
  },
22
35
 
23
36
  success: function(val) {
@@ -28,6 +41,10 @@ qq.Promise = function() {
28
41
  successCallback(val);
29
42
  }
30
43
 
44
+ if(doneCallback) {
45
+ doneCallback();
46
+ }
47
+
31
48
  return this;
32
49
  },
33
50
 
@@ -39,6 +56,10 @@ qq.Promise = function() {
39
56
  failureCallback(val);
40
57
  }
41
58
 
59
+ if(doneCallback) {
60
+ doneCallback();
61
+ }
62
+
42
63
  return this;
43
64
  }
44
65
  };
@@ -27,7 +27,7 @@ qq.FineUploaderBasic = function(o){
27
27
  callbacks: {
28
28
  onSubmit: function(id, name){},
29
29
  onSubmitted: function(id, name){},
30
- onComplete: function(id, name, responseJSON){},
30
+ onComplete: function(id, name, responseJSON, maybeXhr){},
31
31
  onCancel: function(id, name){},
32
32
  onUpload: function(id, name){},
33
33
  onUploadChunk: function(id, name, chunkData){},
@@ -41,9 +41,7 @@ qq.FineUploaderBasic = function(o){
41
41
  onSubmitDelete: function(id) {},
42
42
  onDelete: function(id){},
43
43
  onDeleteComplete: function(id, xhr, isError){},
44
- onPasteReceived: function(blob) {
45
- return new qq.Promise().success();
46
- }
44
+ onPasteReceived: function(blob) {}
47
45
  },
48
46
  messages: {
49
47
  typeError: "{file} has an invalid extension. Valid extension(s): {extensions}.",
@@ -92,6 +90,7 @@ qq.FineUploaderBasic = function(o){
92
90
  return fileOrBlobName;
93
91
  },
94
92
  text: {
93
+ defaultResponseError: "Upload failure reason unknown",
95
94
  sizeSymbols: ['kB', 'MB', 'GB', 'TB', 'PB', 'EB']
96
95
  },
97
96
  deleteFile : {
@@ -125,7 +124,9 @@ qq.FineUploaderBasic = function(o){
125
124
  this._autoRetries = [];
126
125
  this._retryTimeouts = [];
127
126
  this._preventRetries = [];
128
- this._netFilesUploadedOrQueued = 0;
127
+
128
+ this._netUploadedOrQueued = 0;
129
+ this._netUploaded = 0;
129
130
 
130
131
  this._paramsStore = this._createParamsStore("request");
131
132
  this._deleteFileParamsStore = this._createParamsStore("deleteFile");
@@ -184,9 +185,12 @@ qq.FineUploaderBasic.prototype = {
184
185
  this._endpointStore.setEndpoint(endpoint, id);
185
186
  }
186
187
  },
187
- getInProgress: function(){
188
+ getInProgress: function() {
188
189
  return this._filesInProgress.length;
189
190
  },
191
+ getNetUploads: function() {
192
+ return this._netUploaded;
193
+ },
190
194
  uploadStoredFiles: function(){
191
195
  "use strict";
192
196
  var idToUpload;
@@ -202,7 +206,7 @@ qq.FineUploaderBasic.prototype = {
202
206
  },
203
207
  retry: function(id) {
204
208
  if (this._onBeforeManualRetry(id)) {
205
- this._netFilesUploadedOrQueued++;
209
+ this._netUploadedOrQueued++;
206
210
  this._handler.retry(id);
207
211
  return true;
208
212
  }
@@ -235,24 +239,25 @@ qq.FineUploaderBasic.prototype = {
235
239
  this._button.reset();
236
240
  this._paramsStore.reset();
237
241
  this._endpointStore.reset();
238
- this._netFilesUploadedOrQueued = 0;
242
+ this._netUploadedOrQueued = 0;
243
+ this._netUploaded = 0;
239
244
 
240
245
  if (this._pasteHandler) {
241
246
  this._pasteHandler.reset();
242
247
  }
243
248
  },
244
- addFiles: function(filesBlobDataOrInputs) {
249
+ addFiles: function(filesDataOrInputs, params, endpoint) {
245
250
  var self = this,
246
251
  verifiedFilesOrInputs = [],
247
252
  index, fileOrInput;
248
253
 
249
- if (filesBlobDataOrInputs) {
250
- if (!window.FileList || !(filesBlobDataOrInputs instanceof FileList)) {
251
- filesBlobDataOrInputs = [].concat(filesBlobDataOrInputs);
254
+ if (filesDataOrInputs) {
255
+ if (!window.FileList || !(filesDataOrInputs instanceof FileList)) {
256
+ filesDataOrInputs = [].concat(filesDataOrInputs);
252
257
  }
253
258
 
254
- for (index = 0; index < filesBlobDataOrInputs.length; index+=1) {
255
- fileOrInput = filesBlobDataOrInputs[index];
259
+ for (index = 0; index < filesDataOrInputs.length; index+=1) {
260
+ fileOrInput = filesDataOrInputs[index];
256
261
 
257
262
  if (qq.isFileOrInput(fileOrInput)) {
258
263
  verifiedFilesOrInputs.push(fileOrInput);
@@ -263,10 +268,10 @@ qq.FineUploaderBasic.prototype = {
263
268
  }
264
269
 
265
270
  this.log('Processing ' + verifiedFilesOrInputs.length + ' files or inputs...');
266
- this._uploadFileOrBlobDataList(verifiedFilesOrInputs);
271
+ this._uploadFileOrBlobDataList(verifiedFilesOrInputs, params, endpoint);
267
272
  }
268
273
  },
269
- addBlobs: function(blobDataOrArray) {
274
+ addBlobs: function(blobDataOrArray, params, endpoint) {
270
275
  if (blobDataOrArray) {
271
276
  var blobDataArray = [].concat(blobDataOrArray),
272
277
  verifiedBlobDataList = [],
@@ -287,7 +292,7 @@ qq.FineUploaderBasic.prototype = {
287
292
  }
288
293
  });
289
294
 
290
- this._uploadFileOrBlobDataList(verifiedBlobDataList);
295
+ this._uploadFileOrBlobDataList(verifiedBlobDataList, params, endpoint);
291
296
  }
292
297
  else {
293
298
  this.log("undefined or non-array parameter passed into addBlobs", "error");
@@ -320,15 +325,12 @@ qq.FineUploaderBasic.prototype = {
320
325
  this._deleteFileEndpointStore.setEndpoint(endpoint, id);
321
326
  }
322
327
  },
323
- getPromissoryCallbackNames: function() {
324
- return ["onPasteReceived"];
325
- },
326
328
  _createUploadButton: function(element){
327
329
  var self = this;
328
330
 
329
331
  var button = new qq.UploadButton({
330
332
  element: element,
331
- multiple: this._options.multiple && qq.isXhrUploadSupported(),
333
+ multiple: this._options.multiple && qq.supportedFeatures.ajaxUploading,
332
334
  acceptFiles: this._options.validation.acceptFiles,
333
335
  onChange: function(input){
334
336
  self._onInputChange(input);
@@ -368,7 +370,7 @@ qq.FineUploaderBasic.prototype = {
368
370
  },
369
371
  onComplete: function(id, name, result, xhr){
370
372
  self._onComplete(id, name, result, xhr);
371
- self._options.callbacks.onComplete(id, name, result);
373
+ self._options.callbacks.onComplete(id, name, result, xhr);
372
374
  },
373
375
  onCancel: function(id, name){
374
376
  self._onCancel(id, name);
@@ -438,10 +440,10 @@ qq.FineUploaderBasic.prototype = {
438
440
  self.log(str, level);
439
441
  },
440
442
  pasteReceived: function(blob) {
441
- var pasteReceivedCallback = self._options.callbacks.onPasteReceived,
442
- promise = pasteReceivedCallback(blob);
443
+ var callback = self._options.callbacks.onPasteReceived,
444
+ promise = callback(blob);
443
445
 
444
- if (promise.then) {
446
+ if (promise && promise.then) {
445
447
  promise.then(function(successData) {
446
448
  self._handlePasteSuccess(blob, successData);
447
449
  }, function(failureData) {
@@ -449,7 +451,7 @@ qq.FineUploaderBasic.prototype = {
449
451
  });
450
452
  }
451
453
  else {
452
- self.log("Promise contract not fulfilled in pasteReceived callback handler! Ignoring pasted item.", "error");
454
+ self._handlePasteSuccess(blob);
453
455
  }
454
456
  }
455
457
  }
@@ -485,7 +487,7 @@ qq.FineUploaderBasic.prototype = {
485
487
  });
486
488
  },
487
489
  _onSubmit: function(id, name) {
488
- this._netFilesUploadedOrQueued++;
490
+ this._netUploadedOrQueued++;
489
491
 
490
492
  if (this._options.autoUpload) {
491
493
  this._filesInProgress.push(id);
@@ -495,14 +497,17 @@ qq.FineUploaderBasic.prototype = {
495
497
  },
496
498
  _onComplete: function(id, name, result, xhr) {
497
499
  if (!result.success) {
498
- this._netFilesUploadedOrQueued--;
500
+ this._netUploadedOrQueued--;
501
+ }
502
+ else {
503
+ this._netUploaded++;
499
504
  }
500
505
 
501
506
  this._removeFromFilesInProgress(id);
502
507
  this._maybeParseAndSendUploadError(id, name, result, xhr);
503
508
  },
504
509
  _onCancel: function(id, name){
505
- this._netFilesUploadedOrQueued--;
510
+ this._netUploadedOrQueued--;
506
511
 
507
512
  this._removeFromFilesInProgress(id);
508
513
 
@@ -515,10 +520,7 @@ qq.FineUploaderBasic.prototype = {
515
520
  },
516
521
  _isDeletePossible: function() {
517
522
  return (this._options.deleteFile.enabled &&
518
- (!this._options.cors.expected ||
519
- (this._options.cors.expected && (qq.ie10() || !qq.ie()))
520
- )
521
- );
523
+ (!this._options.cors.expected || qq.supportedFeatures.deleteFileCors));
522
524
  },
523
525
  _onSubmitDelete: function(id) {
524
526
  if (this._isDeletePossible()) {
@@ -541,7 +543,8 @@ qq.FineUploaderBasic.prototype = {
541
543
  this._options.callbacks.onError(id, name, "Delete request failed with response code " + xhr.status, xhr);
542
544
  }
543
545
  else {
544
- this._netFilesUploadedOrQueued--;
546
+ this._netUploadedOrQueued--;
547
+ this._netUploaded--;
545
548
  this.log("Delete request for '" + name + "' has succeeded.");
546
549
  }
547
550
  },
@@ -553,7 +556,7 @@ qq.FineUploaderBasic.prototype = {
553
556
  },
554
557
  _onUpload: function(id, name){},
555
558
  _onInputChange: function(input){
556
- if (qq.isXhrUploadSupported()){
559
+ if (qq.supportedFeatures.ajaxUploading){
557
560
  this.addFiles(input.files);
558
561
  } else {
559
562
  this.addFiles(input);
@@ -594,7 +597,7 @@ qq.FineUploaderBasic.prototype = {
594
597
  return false;
595
598
  }
596
599
 
597
- if (itemLimit > 0 && this._netFilesUploadedOrQueued+1 > itemLimit) {
600
+ if (itemLimit > 0 && this._netUploadedOrQueued+1 > itemLimit) {
598
601
  this._itemError("retryFailTooManyItems", "");
599
602
  return false;
600
603
  }
@@ -615,12 +618,12 @@ qq.FineUploaderBasic.prototype = {
615
618
  this._options.callbacks.onError(id, name, "XHR returned response code " + xhr.status, xhr);
616
619
  }
617
620
  else {
618
- var errorReason = response.error ? response.error : "Upload failure reason unknown";
621
+ var errorReason = response.error ? response.error : this._options.text.defaultResponseError;
619
622
  this._options.callbacks.onError(id, name, errorReason, xhr);
620
623
  }
621
624
  }
622
625
  },
623
- _uploadFileOrBlobDataList: function(fileOrBlobDataList){
626
+ _uploadFileOrBlobDataList: function(fileOrBlobDataList, params, endpoint) {
624
627
  var index,
625
628
  validationDescriptors = this._getValidationDescriptors(fileOrBlobDataList),
626
629
  batchValid = this._isBatchValid(validationDescriptors);
@@ -629,7 +632,7 @@ qq.FineUploaderBasic.prototype = {
629
632
  if (fileOrBlobDataList.length > 0) {
630
633
  for (index = 0; index < fileOrBlobDataList.length; index++){
631
634
  if (this._validateFileOrBlobData(fileOrBlobDataList[index])){
632
- this._upload(fileOrBlobDataList[index]);
635
+ this._upload(fileOrBlobDataList[index], params, endpoint);
633
636
  } else {
634
637
  if (this._options.validation.stopOnFirstInvalidFile){
635
638
  return;
@@ -642,10 +645,18 @@ qq.FineUploaderBasic.prototype = {
642
645
  }
643
646
  }
644
647
  },
645
- _upload: function(blobOrFileContainer){
648
+ _upload: function(blobOrFileContainer, params, endpoint) {
646
649
  var id = this._handler.add(blobOrFileContainer);
647
650
  var name = this._handler.getName(id);
648
651
 
652
+ if (params) {
653
+ this.setParams(params, id);
654
+ }
655
+
656
+ if (endpoint) {
657
+ this.setEndpoint(endpoint, id);
658
+ }
659
+
649
660
  if (this._options.callbacks.onSubmit(id, name) !== false) {
650
661
  this._onSubmit(id, name);
651
662
  this._options.callbacks.onSubmitted(id, name);
@@ -665,7 +676,7 @@ qq.FineUploaderBasic.prototype = {
665
676
  //first, defer the check to the callback (ask the integrator)
666
677
  var errorMessage,
667
678
  itemLimit = this._options.validation.itemLimit,
668
- proposedNetFilesUploadedOrQueued = this._netFilesUploadedOrQueued + validationDescriptors.length,
679
+ proposedNetFilesUploadedOrQueued = this._netUploadedOrQueued + validationDescriptors.length,
669
680
  batchValid = this._options.callbacks.onValidateBatch(validationDescriptors) !== false;
670
681
 
671
682
  //if the callback hasn't rejected the batch, run some internal tests on the batch next
@@ -717,10 +728,12 @@ qq.FineUploaderBasic.prototype = {
717
728
 
718
729
  return true;
719
730
  },
720
- _itemError: function(code, name) {
731
+ _itemError: function(code, nameOrNames) {
721
732
  var message = this._options.messages[code],
722
733
  allowedExtensions = [],
723
- extensionsForMessage;
734
+ names = [].concat(nameOrNames),
735
+ name = names[0],
736
+ extensionsForMessage, placeholderMatch;
724
737
 
725
738
  function r(name, replacement){ message = message.replace(name, replacement); }
726
739
 
@@ -741,6 +754,13 @@ qq.FineUploaderBasic.prototype = {
741
754
  r('{sizeLimit}', this._formatSize(this._options.validation.sizeLimit));
742
755
  r('{minSizeLimit}', this._formatSize(this._options.validation.minSizeLimit));
743
756
 
757
+ placeholderMatch = message.match(/(\{\w+\})/g);
758
+ if (placeholderMatch !== null) {
759
+ qq.each(placeholderMatch, function(idx, placeholder) {
760
+ r(placeholder, names[idx]);
761
+ });
762
+ }
763
+
744
764
  this._options.callbacks.onError(null, name, message);
745
765
 
746
766
  return message;
@@ -850,7 +870,7 @@ qq.FineUploaderBasic.prototype = {
850
870
  size = this._parseFileOrBlobDataSize(fileOrBlobData);
851
871
 
852
872
  fileDescriptor.name = name;
853
- if (size) {
873
+ if (size !== undefined) {
854
874
  fileDescriptor.size = size;
855
875
  }
856
876