fine_uploader 3.4.1 → 3.5.0

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.
@@ -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