fileuploader-rails 2.1.2 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Class for uploading files using xhr
3
+ * @inherits qq.UploadHandlerAbstract
4
+ */
5
+ qq.UploadHandlerXhr = function(o){
6
+ qq.UploadHandlerAbstract.apply(this, arguments);
7
+
8
+ this._files = [];
9
+ this._xhrs = [];
10
+
11
+ // current loaded size in bytes for each file
12
+ this._loaded = [];
13
+ };
14
+
15
+ // static method
16
+ qq.UploadHandlerXhr.isSupported = function(){
17
+ var input = document.createElement('input');
18
+ input.type = 'file';
19
+
20
+ return (
21
+ 'multiple' in input &&
22
+ typeof File != "undefined" &&
23
+ typeof FormData != "undefined" &&
24
+ typeof (new XMLHttpRequest()).upload != "undefined" );
25
+ };
26
+
27
+ // @inherits qq.UploadHandlerAbstract
28
+ qq.extend(qq.UploadHandlerXhr.prototype, qq.UploadHandlerAbstract.prototype)
29
+
30
+ qq.extend(qq.UploadHandlerXhr.prototype, {
31
+ /**
32
+ * Adds file to the queue
33
+ * Returns id to use with upload, cancel
34
+ **/
35
+ add: function(file){
36
+ if (!(file instanceof File)){
37
+ throw new Error('Passed obj in not a File (in qq.UploadHandlerXhr)');
38
+ }
39
+
40
+ return this._files.push(file) - 1;
41
+ },
42
+ getName: function(id){
43
+ var file = this._files[id];
44
+ // fix missing name in Safari 4
45
+ //NOTE: fixed missing name firefox 11.0a2 file.fileName is actually undefined
46
+ return (file.fileName !== null && file.fileName !== undefined) ? file.fileName : file.name;
47
+ },
48
+ getSize: function(id){
49
+ var file = this._files[id];
50
+ return file.fileSize != null ? file.fileSize : file.size;
51
+ },
52
+ /**
53
+ * Returns uploaded bytes for file identified by id
54
+ */
55
+ getLoaded: function(id){
56
+ return this._loaded[id] || 0;
57
+ },
58
+ isValid: function(id) {
59
+ return this._files[id] !== undefined;
60
+ },
61
+ reset: function() {
62
+ qq.UploadHandlerAbstract.prototype.reset.apply(this, arguments);
63
+ this._files = [];
64
+ this._xhrs = [];
65
+ this._loaded = [];
66
+ },
67
+ /**
68
+ * Sends the file identified by id and additional query params to the server
69
+ * @param {Object} params name-value string pairs
70
+ */
71
+ _upload: function(id, params){
72
+ this._options.onUpload(id, this.getName(id), true);
73
+
74
+ var file = this._files[id],
75
+ name = this.getName(id),
76
+ size = this.getSize(id);
77
+
78
+ this._loaded[id] = 0;
79
+
80
+ var xhr = this._xhrs[id] = new XMLHttpRequest();
81
+ var self = this;
82
+
83
+ xhr.upload.onprogress = function(e){
84
+ if (e.lengthComputable){
85
+ self._loaded[id] = e.loaded;
86
+ self._options.onProgress(id, name, e.loaded, e.total);
87
+ }
88
+ };
89
+
90
+ xhr.onreadystatechange = function(){
91
+ if (xhr.readyState == 4){
92
+ self._onComplete(id, xhr);
93
+ }
94
+ };
95
+
96
+ // build query string
97
+ params = params || {};
98
+ params[this._options.inputName] = name;
99
+ var queryString = qq.obj2url(params, this._options.endpoint);
100
+
101
+ var protocol = this._options.demoMode ? "GET" : "POST";
102
+ xhr.open(protocol, queryString, true);
103
+ xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
104
+ xhr.setRequestHeader("X-File-Name", encodeURIComponent(name));
105
+ xhr.setRequestHeader("Cache-Control", "no-cache");
106
+ if (this._options.forceMultipart) {
107
+ var formData = new FormData();
108
+ formData.append(this._options.inputName, file);
109
+ file = formData;
110
+ } else {
111
+ xhr.setRequestHeader("Content-Type", "application/octet-stream");
112
+ //NOTE: return mime type in xhr works on chrome 16.0.9 firefox 11.0a2
113
+ xhr.setRequestHeader("X-Mime-Type",file.type );
114
+ }
115
+ for (key in this._options.customHeaders){
116
+ xhr.setRequestHeader(key, this._options.customHeaders[key]);
117
+ };
118
+
119
+ this.log('Sending upload request for ' + id);
120
+ xhr.send(file);
121
+ },
122
+ _onComplete: function(id, xhr){
123
+ "use strict";
124
+ // the request was aborted/cancelled
125
+ if (!this._files[id]) { return; }
126
+
127
+ var name = this.getName(id);
128
+ var size = this.getSize(id);
129
+ var response; //the parsed JSON response from the server, or the empty object if parsing failed.
130
+
131
+ this._options.onProgress(id, name, size, size);
132
+
133
+ this.log("xhr - server response received for " + id);
134
+ this.log("responseText = " + xhr.responseText);
135
+
136
+ try {
137
+ if (typeof JSON.parse === "function") {
138
+ response = JSON.parse(xhr.responseText);
139
+ } else {
140
+ response = eval("(" + xhr.responseText + ")");
141
+ }
142
+ } catch(error){
143
+ this.log('Error when attempting to parse xhr response text (' + error + ')', 'error');
144
+ response = {};
145
+ }
146
+
147
+ if (xhr.status !== 200 || !response.success){
148
+ if (this._options.onAutoRetry(id, name, response, xhr)) {
149
+ return;
150
+ }
151
+ }
152
+
153
+ this._options.onComplete(id, name, response, xhr);
154
+
155
+ this._xhrs[id] = null;
156
+ this._dequeue(id);
157
+ },
158
+ _cancel: function(id){
159
+ this._options.onCancel(id, this.getName(id));
160
+
161
+ this._files[id] = null;
162
+
163
+ if (this._xhrs[id]){
164
+ this._xhrs[id].abort();
165
+ this._xhrs[id] = null;
166
+ }
167
+ }
168
+ });
@@ -0,0 +1,12 @@
1
+ /**
2
+ * http://github.com/Valums-File-Uploader/file-uploader
3
+ *
4
+ * Multiple file upload component with progress-bar, drag-and-drop, support for all modern browsers.
5
+ *
6
+ * Original version: 1.0 © 2010 Andrew Valums ( andrew(at)valums.com )
7
+ * Current Maintainer (2.0+): © 2012, Ray Nicholus ( fineuploader(at)garstasio.com )
8
+ *
9
+ * Licensed under MIT license, GNU GPL 2 or later, GNU LGPL 2 or later, see license.txt.
10
+ */
11
+
12
+ var qq = qq || {};
@@ -0,0 +1,148 @@
1
+ (function($) {
2
+ "use strict";
3
+ var uploader, $el, init, dataStore, pluginOption, pluginOptions, addCallbacks, transformOptions, isValidCommand,
4
+ delegateCommand;
5
+
6
+ pluginOptions = ['uploaderType'];
7
+
8
+ init = function (options) {
9
+ if (options) {
10
+ var xformedOpts = transformOptions(options);
11
+ addCallbacks(xformedOpts);
12
+
13
+ if (pluginOption('uploaderType') === 'basic') {
14
+ uploader(new qq.FineUploaderBasic(xformedOpts));
15
+ }
16
+ else {
17
+ uploader(new qq.FineUploader(xformedOpts));
18
+ }
19
+ }
20
+
21
+ return $el;
22
+ };
23
+
24
+ dataStore = function(key, val) {
25
+ var data = $el.data('fineuploader');
26
+
27
+ if (val) {
28
+ if (data === undefined) {
29
+ data = {};
30
+ }
31
+ data[key] = val;
32
+ $el.data('fineuploader', data);
33
+ }
34
+ else {
35
+ if (data === undefined) {
36
+ return null;
37
+ }
38
+ return data[key];
39
+ }
40
+ };
41
+
42
+ //the underlying Fine Uploader instance is stored in jQuery's data stored, associated with the element
43
+ // tied to this instance of the plug-in
44
+ uploader = function(instanceToStore) {
45
+ return dataStore('uploader', instanceToStore);
46
+ };
47
+
48
+ pluginOption = function(option, optionVal) {
49
+ return dataStore(option, optionVal);
50
+ };
51
+
52
+ //implement all callbacks defined in Fine Uploader as functions that trigger appropriately names events and
53
+ // return the result of executing the bound handler back to Fine Uploader
54
+ addCallbacks = function(transformedOpts) {
55
+ var callbacks = transformedOpts.callbacks = {};
56
+
57
+ $.each(new qq.FineUploaderBasic()._options.callbacks, function(prop, func) {
58
+ var name, $callbackEl;
59
+
60
+ name = /^on(\w+)/.exec(prop)[1];
61
+ name = name.substring(0, 1).toLowerCase() + name.substring(1);
62
+ $callbackEl = $el;
63
+
64
+ callbacks[prop] = function() {
65
+ var args = Array.prototype.slice.call(arguments);
66
+ return $callbackEl.triggerHandler(name, args);
67
+ };
68
+ });
69
+ };
70
+
71
+ //transform jQuery objects into HTMLElements, and pass along all other option properties
72
+ transformOptions = function(source, dest) {
73
+ var xformed, arrayVals;
74
+
75
+ if (dest === undefined) {
76
+ if (source.uploaderType !== 'basic') {
77
+ xformed = { element : $el[0] };
78
+ }
79
+ else {
80
+ xformed = {};
81
+ }
82
+ }
83
+ else {
84
+ xformed = dest;
85
+ }
86
+
87
+ $.each(source, function(prop, val) {
88
+ if ($.inArray(prop, pluginOptions) >= 0) {
89
+ pluginOption(prop, val);
90
+ }
91
+ else if (val instanceof $) {
92
+ xformed[prop] = val[0];
93
+ }
94
+ else if ($.isPlainObject(val)) {
95
+ xformed[prop] = {};
96
+ transformOptions(val, xformed[prop]);
97
+ }
98
+ else if ($.isArray(val)) {
99
+ arrayVals = [];
100
+ $.each(val, function(idx, arrayVal) {
101
+ if (arrayVal instanceof $) {
102
+ $.merge(arrayVals, arrayVal);
103
+ }
104
+ else {
105
+ arrayVals.push(arrayVal);
106
+ }
107
+ });
108
+ xformed[prop] = arrayVals;
109
+ }
110
+ else {
111
+ xformed[prop] = val;
112
+ }
113
+ });
114
+
115
+ if (dest === undefined) {
116
+ return xformed;
117
+ }
118
+ };
119
+
120
+ isValidCommand = function(command) {
121
+ return $.type(command) === "string" &&
122
+ !command.match(/^_/) && //enforce private methods convention
123
+ uploader()[command] !== undefined;
124
+ };
125
+
126
+ //assuming we have already verified that this is a valid command, call the associated function in the underlying
127
+ // Fine Uploader instance (passing along the arguments from the caller) and return the result of the call back to the caller
128
+ delegateCommand = function(command) {
129
+ return uploader()[command].apply(uploader(), Array.prototype.slice.call(arguments, 1));
130
+ };
131
+
132
+ $.fn.fineUploader = function(optionsOrCommand) {
133
+ $el = this;
134
+
135
+ if (uploader() && isValidCommand(optionsOrCommand)) {
136
+ return delegateCommand.apply(this, arguments);
137
+ }
138
+ else if (typeof optionsOrCommand === 'object' || !optionsOrCommand) {
139
+ return init.apply(this, arguments);
140
+ }
141
+ else {
142
+ $.error('Method ' + optionsOrCommand + ' does not exist on jQuery.fineUploader');
143
+ }
144
+
145
+ return this;
146
+ };
147
+
148
+ }(jQuery));
@@ -0,0 +1,483 @@
1
+ qq.FineUploaderBasic = function(o){
2
+ var that = this;
3
+ this._options = {
4
+ debug: false,
5
+ button: null,
6
+ multiple: true,
7
+ maxConnections: 3,
8
+ disableCancelForFormUploads: false,
9
+ autoUpload: true,
10
+ request: {
11
+ endpoint: '/server/upload',
12
+ params: {},
13
+ customHeaders: {},
14
+ forceMultipart: false,
15
+ inputName: 'qqfile'
16
+ },
17
+ validation: {
18
+ allowedExtensions: [],
19
+ sizeLimit: 0,
20
+ minSizeLimit: 0,
21
+ stopOnFirstInvalidFile: true
22
+ },
23
+ callbacks: {
24
+ onSubmit: function(id, fileName){}, // return false to cancel submit
25
+ onComplete: function(id, fileName, responseJSON){},
26
+ onCancel: function(id, fileName){},
27
+ onUpload: function(id, fileName, xhr){},
28
+ onProgress: function(id, fileName, loaded, total){},
29
+ onError: function(id, fileName, reason) {},
30
+ onAutoRetry: function(id, fileName, attemptNumber) {},
31
+ onManualRetry: function(id, fileName) {},
32
+ onValidate: function(fileData) {} // return false to prevent upload
33
+ },
34
+ messages: {
35
+ typeError: "{file} has an invalid extension. Valid extension(s): {extensions}.",
36
+ sizeError: "{file} is too large, maximum file size is {sizeLimit}.",
37
+ minSizeError: "{file} is too small, minimum file size is {minSizeLimit}.",
38
+ emptyError: "{file} is empty, please select files again without it.",
39
+ noFilesError: "No files to upload.",
40
+ onLeave: "The files are being uploaded, if you leave now the upload will be cancelled."
41
+ },
42
+ retry: {
43
+ enableAuto: false,
44
+ maxAutoAttempts: 3,
45
+ autoAttemptDelay: 5,
46
+ preventRetryResponseProperty: 'preventRetry'
47
+ }
48
+ };
49
+
50
+ qq.extend(this._options, o, true);
51
+ this._wrapCallbacks();
52
+ qq.extend(this, qq.DisposeSupport);
53
+
54
+ // number of files being uploaded
55
+ this._filesInProgress = 0;
56
+
57
+ this._storedFileIds = [];
58
+
59
+ this._autoRetries = [];
60
+ this._retryTimeouts = [];
61
+ this._preventRetries = [];
62
+
63
+ this._handler = this._createUploadHandler();
64
+
65
+ if (this._options.button){
66
+ this._button = this._createUploadButton(this._options.button);
67
+ }
68
+
69
+ this._preventLeaveInProgress();
70
+ };
71
+
72
+ qq.FineUploaderBasic.prototype = {
73
+ log: function(str, level) {
74
+ if (this._options.debug && (!level || level === 'info')) {
75
+ qq.log('[FineUploader] ' + str);
76
+ }
77
+ else if (level && level !== 'info') {
78
+ qq.log('[FineUploader] ' + str, level);
79
+
80
+ }
81
+ },
82
+ setParams: function(params){
83
+ this._options.request.params = params;
84
+ },
85
+ getInProgress: function(){
86
+ return this._filesInProgress;
87
+ },
88
+ uploadStoredFiles: function(){
89
+ "use strict";
90
+ while(this._storedFileIds.length) {
91
+ this._filesInProgress++;
92
+ this._handler.upload(this._storedFileIds.shift(), this._options.request.params);
93
+ }
94
+ },
95
+ clearStoredFiles: function(){
96
+ this._storedFileIds = [];
97
+ },
98
+ retry: function(id) {
99
+ if (this._onBeforeManualRetry(id)) {
100
+ this._handler.retry(id);
101
+ return true;
102
+ }
103
+ else {
104
+ return false;
105
+ }
106
+ },
107
+ cancel: function(fileId) {
108
+ this._handler.cancel(fileId);
109
+ },
110
+ reset: function() {
111
+ this.log("Resetting uploader...");
112
+ this._handler.reset();
113
+ this._filesInProgress = 0;
114
+ this._storedFileIds = [];
115
+ this._autoRetries = [];
116
+ this._retryTimeouts = [];
117
+ this._preventRetries = [];
118
+ this._button.reset();
119
+ },
120
+ _createUploadButton: function(element){
121
+ var self = this;
122
+
123
+ var button = new qq.UploadButton({
124
+ element: element,
125
+ multiple: this._options.multiple && qq.UploadHandlerXhr.isSupported(),
126
+ acceptFiles: this._options.validation.acceptFiles,
127
+ onChange: function(input){
128
+ self._onInputChange(input);
129
+ }
130
+ });
131
+
132
+ this.addDisposer(function() { button.dispose(); });
133
+ return button;
134
+ },
135
+ _createUploadHandler: function(){
136
+ var self = this,
137
+ handlerClass;
138
+
139
+ if(qq.UploadHandlerXhr.isSupported()){
140
+ handlerClass = 'UploadHandlerXhr';
141
+ } else {
142
+ handlerClass = 'UploadHandlerForm';
143
+ }
144
+
145
+ var handler = new qq[handlerClass]({
146
+ debug: this._options.debug,
147
+ endpoint: this._options.request.endpoint,
148
+ forceMultipart: this._options.request.forceMultipart,
149
+ maxConnections: this._options.maxConnections,
150
+ customHeaders: this._options.request.customHeaders,
151
+ inputName: this._options.request.inputName,
152
+ demoMode: this._options.demoMode,
153
+ log: this.log,
154
+ onProgress: function(id, fileName, loaded, total){
155
+ self._onProgress(id, fileName, loaded, total);
156
+ self._options.callbacks.onProgress(id, fileName, loaded, total);
157
+ },
158
+ onComplete: function(id, fileName, result, xhr){
159
+ self._onComplete(id, fileName, result, xhr);
160
+ self._options.callbacks.onComplete(id, fileName, result);
161
+ },
162
+ onCancel: function(id, fileName){
163
+ self._onCancel(id, fileName);
164
+ self._options.callbacks.onCancel(id, fileName);
165
+ },
166
+ onUpload: function(id, fileName, xhr){
167
+ self._onUpload(id, fileName, xhr);
168
+ self._options.callbacks.onUpload(id, fileName, xhr);
169
+ },
170
+ onAutoRetry: function(id, fileName, responseJSON, xhr) {
171
+ self._preventRetries[id] = responseJSON[self._options.retry.preventRetryResponseProperty];
172
+
173
+ if (self._shouldAutoRetry(id, fileName, responseJSON)) {
174
+ self._maybeParseAndSendUploadError(id, fileName, responseJSON, xhr);
175
+ self._options.callbacks.onAutoRetry(id, fileName, self._autoRetries[id] + 1);
176
+ self._onBeforeAutoRetry(id, fileName);
177
+
178
+ self._retryTimeouts[id] = setTimeout(function() {
179
+ self._onAutoRetry(id, fileName, responseJSON)
180
+ }, self._options.retry.autoAttemptDelay * 1000);
181
+
182
+ return true;
183
+ }
184
+ else {
185
+ return false;
186
+ }
187
+ }
188
+ });
189
+
190
+ return handler;
191
+ },
192
+ _preventLeaveInProgress: function(){
193
+ var self = this;
194
+
195
+ this._attach(window, 'beforeunload', function(e){
196
+ if (!self._filesInProgress){return;}
197
+
198
+ var e = e || window.event;
199
+ // for ie, ff
200
+ e.returnValue = self._options.messages.onLeave;
201
+ // for webkit
202
+ return self._options.messages.onLeave;
203
+ });
204
+ },
205
+ _onSubmit: function(id, fileName){
206
+ if (this._options.autoUpload) {
207
+ this._filesInProgress++;
208
+ }
209
+ },
210
+ _onProgress: function(id, fileName, loaded, total){
211
+ },
212
+ _onComplete: function(id, fileName, result, xhr){
213
+ this._filesInProgress--;
214
+ this._maybeParseAndSendUploadError(id, fileName, result, xhr);
215
+ },
216
+ _onCancel: function(id, fileName){
217
+ clearTimeout(this._retryTimeouts[id]);
218
+
219
+ var storedFileIndex = qq.indexOf(this._storedFileIds, id);
220
+ if (this._options.autoUpload || storedFileIndex < 0) {
221
+ this._filesInProgress--;
222
+ }
223
+ else if (!this._options.autoUpload) {
224
+ this._storedFileIds.splice(storedFileIndex, 1);
225
+ }
226
+ },
227
+ _onUpload: function(id, fileName, xhr){
228
+ },
229
+ _onInputChange: function(input){
230
+ if (this._handler instanceof qq.UploadHandlerXhr){
231
+ this._uploadFileList(input.files);
232
+ } else {
233
+ if (this._validateFile(input)){
234
+ this._uploadFile(input);
235
+ }
236
+ }
237
+ this._button.reset();
238
+ },
239
+ _onBeforeAutoRetry: function(id, fileName) {
240
+ this.log("Waiting " + this._options.retry.autoAttemptDelay + " seconds before retrying " + fileName + "...");
241
+ },
242
+ _onAutoRetry: function(id, fileName, responseJSON) {
243
+ this.log("Retrying " + fileName + "...");
244
+ this._autoRetries[id]++;
245
+ this._handler.retry(id);
246
+ },
247
+ _shouldAutoRetry: function(id, fileName, responseJSON) {
248
+ if (!this._preventRetries[id] && this._options.retry.enableAuto) {
249
+ if (this._autoRetries[id] === undefined) {
250
+ this._autoRetries[id] = 0;
251
+ }
252
+
253
+ return this._autoRetries[id] < this._options.retry.maxAutoAttempts
254
+ }
255
+
256
+ return false;
257
+ },
258
+ //return false if we should not attempt the requested retry
259
+ _onBeforeManualRetry: function(id) {
260
+ if (this._preventRetries[id]) {
261
+ this.log("Retries are forbidden for id " + id, 'warn');
262
+ return false;
263
+ }
264
+ else if (this._handler.isValid(id)) {
265
+ var fileName = this._handler.getName(id);
266
+
267
+ if (this._options.callbacks.onManualRetry(id, fileName) === false) {
268
+ return false;
269
+ }
270
+
271
+ this.log("Retrying upload for '" + fileName + "' (id: " + id + ")...");
272
+ this._filesInProgress++;
273
+ return true;
274
+ }
275
+ else {
276
+ this.log("'" + id + "' is not a valid file ID", 'error');
277
+ return false;
278
+ }
279
+ },
280
+ _maybeParseAndSendUploadError: function(id, fileName, response, xhr) {
281
+ //assuming no one will actually set the response code to something other than 200 and still set 'success' to true
282
+ if (!response.success){
283
+ if (xhr && xhr.status !== 200 && !response.error) {
284
+ this._options.callbacks.onError(id, fileName, "XHR returned response code " + xhr.status);
285
+ }
286
+ else {
287
+ var errorReason = response.error ? response.error : "Upload failure reason unknown";
288
+ this._options.callbacks.onError(id, fileName, errorReason);
289
+ }
290
+ }
291
+ },
292
+ _uploadFileList: function(files){
293
+ var validationDescriptors, index, batchInvalid;
294
+
295
+ validationDescriptors = this._getValidationDescriptors(files);
296
+ if (validationDescriptors.length > 1) {
297
+ batchInvalid = this._options.callbacks.onValidate(validationDescriptors) === false;
298
+ }
299
+
300
+ if (!batchInvalid) {
301
+ if (files.length > 0) {
302
+ for (index = 0; index < files.length; index++){
303
+ if (this._validateFile(files[index])){
304
+ this._uploadFile(files[index]);
305
+ } else {
306
+ if (this._options.validation.stopOnFirstInvalidFile){
307
+ return;
308
+ }
309
+ }
310
+ }
311
+ }
312
+ else {
313
+ this._error('noFilesError', "");
314
+ }
315
+ }
316
+ },
317
+ _uploadFile: function(fileContainer){
318
+ var id = this._handler.add(fileContainer);
319
+ var fileName = this._handler.getName(id);
320
+
321
+ if (this._options.callbacks.onSubmit(id, fileName) !== false){
322
+ this._onSubmit(id, fileName);
323
+ if (this._options.autoUpload) {
324
+ this._handler.upload(id, this._options.request.params);
325
+ }
326
+ else {
327
+ this._storeFileForLater(id);
328
+ }
329
+ }
330
+ },
331
+ _storeFileForLater: function(id) {
332
+ this._storedFileIds.push(id);
333
+ },
334
+ _validateFile: function(file){
335
+ var validationDescriptor, name, size;
336
+
337
+ validationDescriptor = this._getValidationDescriptor(file);
338
+ name = validationDescriptor.name;
339
+ size = validationDescriptor.size;
340
+
341
+ if (this._options.callbacks.onValidate([validationDescriptor]) === false) {
342
+ return false;
343
+ }
344
+
345
+ if (!this._isAllowedExtension(name)){
346
+ this._error('typeError', name);
347
+ return false;
348
+
349
+ }
350
+ else if (size === 0){
351
+ this._error('emptyError', name);
352
+ return false;
353
+
354
+ }
355
+ else if (size && this._options.validation.sizeLimit && size > this._options.validation.sizeLimit){
356
+ this._error('sizeError', name);
357
+ return false;
358
+
359
+ }
360
+ else if (size && size < this._options.validation.minSizeLimit){
361
+ this._error('minSizeError', name);
362
+ return false;
363
+ }
364
+
365
+ return true;
366
+ },
367
+ _error: function(code, fileName){
368
+ var message = this._options.messages[code];
369
+ function r(name, replacement){ message = message.replace(name, replacement); }
370
+
371
+ var extensions = this._options.validation.allowedExtensions.join(', ');
372
+
373
+ r('{file}', this._formatFileName(fileName));
374
+ r('{extensions}', extensions);
375
+ r('{sizeLimit}', this._formatSize(this._options.validation.sizeLimit));
376
+ r('{minSizeLimit}', this._formatSize(this._options.validation.minSizeLimit));
377
+
378
+ this._options.callbacks.onError(null, fileName, message);
379
+
380
+ return message;
381
+ },
382
+ _formatFileName: function(name){
383
+ if (name.length > 33){
384
+ name = name.slice(0, 19) + '...' + name.slice(-13);
385
+ }
386
+ return name;
387
+ },
388
+ _isAllowedExtension: function(fileName){
389
+ var ext = (-1 !== fileName.indexOf('.'))
390
+ ? fileName.replace(/.*[.]/, '').toLowerCase()
391
+ : '';
392
+ var allowed = this._options.validation.allowedExtensions;
393
+
394
+ if (!allowed.length){return true;}
395
+
396
+ for (var i=0; i<allowed.length; i++){
397
+ if (allowed[i].toLowerCase() == ext){ return true;}
398
+ }
399
+
400
+ return false;
401
+ },
402
+ _formatSize: function(bytes){
403
+ var i = -1;
404
+ do {
405
+ bytes = bytes / 1024;
406
+ i++;
407
+ } while (bytes > 99);
408
+
409
+ return Math.max(bytes, 0.1).toFixed(1) + ['kB', 'MB', 'GB', 'TB', 'PB', 'EB'][i];
410
+ },
411
+ _wrapCallbacks: function() {
412
+ var self, safeCallback;
413
+
414
+ self = this;
415
+
416
+ safeCallback = function(name, callback, args) {
417
+ try {
418
+ return callback.apply(self, args);
419
+ }
420
+ catch (exception) {
421
+ self.log("Caught exception in '" + name + "' callback - " + exception, 'error');
422
+ }
423
+ }
424
+
425
+ for (var prop in this._options.callbacks) {
426
+ (function() {
427
+ var oldCallback = self._options.callbacks[prop];
428
+ self._options.callbacks[prop] = function() {
429
+ return safeCallback(prop, oldCallback, arguments);
430
+ }
431
+ }());
432
+ }
433
+ },
434
+ _parseFileName: function(file) {
435
+ var name;
436
+
437
+ if (file.value){
438
+ // it is a file input
439
+ // get input value and remove path to normalize
440
+ name = file.value.replace(/.*(\/|\\)/, "");
441
+ } else {
442
+ // fix missing properties in Safari 4 and firefox 11.0a2
443
+ name = (file.fileName !== null && file.fileName !== undefined) ? file.fileName : file.name;
444
+ }
445
+
446
+ return name;
447
+ },
448
+ _parseFileSize: function(file) {
449
+ var size;
450
+
451
+ if (!file.value){
452
+ // fix missing properties in Safari 4 and firefox 11.0a2
453
+ size = (file.fileSize !== null && file.fileSize !== undefined) ? file.fileSize : file.size;
454
+ }
455
+
456
+ return size;
457
+ },
458
+ _getValidationDescriptor: function(file) {
459
+ var name, size, fileDescriptor;
460
+
461
+ fileDescriptor = {};
462
+ name = this._parseFileName(file);
463
+ size = this._parseFileSize(file);
464
+
465
+ fileDescriptor.name = name;
466
+ if (size) {
467
+ fileDescriptor.size = size;
468
+ }
469
+
470
+ return fileDescriptor;
471
+ },
472
+ _getValidationDescriptors: function(files) {
473
+ var index, fileDescriptors;
474
+
475
+ fileDescriptors = [];
476
+
477
+ for (index = 0; index < files.length; index++) {
478
+ fileDescriptors.push(files[index]);
479
+ }
480
+
481
+ return fileDescriptors;
482
+ }
483
+ };