jquery-fileupload-rails 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,629 @@
1
+ /*
2
+ * jQuery File Upload User Interface Plugin 6.6.2
3
+ * https://github.com/blueimp/jQuery-File-Upload
4
+ *
5
+ * Copyright 2010, Sebastian Tschan
6
+ * https://blueimp.net
7
+ *
8
+ * Licensed under the MIT license:
9
+ * http://www.opensource.org/licenses/MIT
10
+ */
11
+
12
+ /*jslint nomen: true, unparam: true, regexp: true */
13
+ /*global define, window, document, URL, webkitURL, FileReader */
14
+
15
+ (function (factory) {
16
+ 'use strict';
17
+ if (typeof define === 'function' && define.amd) {
18
+ // Register as an anonymous AMD module:
19
+ define([
20
+ 'jquery',
21
+ 'tmpl',
22
+ 'load-image',
23
+ './jquery.fileupload-ip'
24
+ ], factory);
25
+ } else {
26
+ // Browser globals:
27
+ factory(
28
+ window.jQuery,
29
+ window.tmpl,
30
+ window.loadImage
31
+ );
32
+ }
33
+ }(function ($, tmpl, loadImage) {
34
+ 'use strict';
35
+
36
+ // The UI version extends the IP (image processing) version or the basic
37
+ // file upload widget and adds complete user interface interaction:
38
+ var parentWidget = ($.blueimpIP || $.blueimp).fileupload;
39
+ $.widget('blueimpUI.fileupload', parentWidget, {
40
+
41
+ options: {
42
+ // By default, files added to the widget are uploaded as soon
43
+ // as the user clicks on the start buttons. To enable automatic
44
+ // uploads, set the following option to true:
45
+ autoUpload: false,
46
+ // The following option limits the number of files that are
47
+ // allowed to be uploaded using this widget:
48
+ maxNumberOfFiles: undefined,
49
+ // The maximum allowed file size:
50
+ maxFileSize: undefined,
51
+ // The minimum allowed file size:
52
+ minFileSize: undefined,
53
+ // The regular expression for allowed file types, matches
54
+ // against either file type or file name:
55
+ acceptFileTypes: /.+$/i,
56
+ // The regular expression to define for which files a preview
57
+ // image is shown, matched against the file type:
58
+ previewSourceFileTypes: /^image\/(gif|jpeg|png)$/,
59
+ // The maximum file size of images that are to be displayed as preview:
60
+ previewSourceMaxFileSize: 5000000, // 5MB
61
+ // The maximum width of the preview images:
62
+ previewMaxWidth: 80,
63
+ // The maximum height of the preview images:
64
+ previewMaxHeight: 80,
65
+ // By default, preview images are displayed as canvas elements
66
+ // if supported by the browser. Set the following option to false
67
+ // to always display preview images as img elements:
68
+ previewAsCanvas: true,
69
+ // The ID of the upload template:
70
+ uploadTemplateId: 'template-upload',
71
+ // The ID of the download template:
72
+ downloadTemplateId: 'template-download',
73
+ // The expected data type of the upload response, sets the dataType
74
+ // option of the $.ajax upload requests:
75
+ dataType: 'json',
76
+
77
+ // The add callback is invoked as soon as files are added to the fileupload
78
+ // widget (via file input selection, drag & drop or add API call).
79
+ // See the basic file upload widget for more information:
80
+ add: function (e, data) {
81
+ var that = $(this).data('fileupload'),
82
+ options = that.options,
83
+ files = data.files;
84
+ that._adjustMaxNumberOfFiles(-files.length);
85
+ data.isAdjusted = true;
86
+ $(this).fileupload('resize', data).done(data, function () {
87
+ data.files.valid = data.isValidated = that._validate(files);
88
+ data.context = that._renderUpload(files)
89
+ .appendTo(options.filesContainer)
90
+ .data('data', data);
91
+ that._renderPreviews(files, data.context);
92
+ that._forceReflow(data.context);
93
+ that._transition(data.context).done(
94
+ function () {
95
+ if ((that._trigger('added', e, data) !== false) &&
96
+ (options.autoUpload || data.autoUpload) &&
97
+ data.autoUpload !== false && data.isValidated) {
98
+ data.submit();
99
+ }
100
+ }
101
+ );
102
+ });
103
+ },
104
+ // Callback for the start of each file upload request:
105
+ send: function (e, data) {
106
+ var that = $(this).data('fileupload');
107
+ if (!data.isValidated) {
108
+ if (!data.isAdjusted) {
109
+ that._adjustMaxNumberOfFiles(-data.files.length);
110
+ }
111
+ if (!that._validate(data.files)) {
112
+ return false;
113
+ }
114
+ }
115
+ if (data.context && data.dataType &&
116
+ data.dataType.substr(0, 6) === 'iframe') {
117
+ // Iframe Transport does not support progress events.
118
+ // In lack of an indeterminate progress bar, we set
119
+ // the progress to 100%, showing the full animated bar:
120
+ data.context
121
+ .find('.progress').addClass(
122
+ !$.support.transition && 'progress-animated'
123
+ )
124
+ .find('.bar').css(
125
+ 'width',
126
+ parseInt(100, 10) + '%'
127
+ );
128
+ }
129
+ return that._trigger('sent', e, data);
130
+ },
131
+ // Callback for successful uploads:
132
+ done: function (e, data) {
133
+ var that = $(this).data('fileupload'),
134
+ template,
135
+ preview;
136
+ if (data.context) {
137
+ data.context.each(function (index) {
138
+ var file = ($.isArray(data.result) &&
139
+ data.result[index]) || {error: 'emptyResult'};
140
+ if (file.error) {
141
+ that._adjustMaxNumberOfFiles(1);
142
+ }
143
+ that._transition($(this)).done(
144
+ function () {
145
+ var node = $(this);
146
+ template = that._renderDownload([file])
147
+ .css('height', node.height())
148
+ .replaceAll(node);
149
+ that._forceReflow(template);
150
+ that._transition(template).done(
151
+ function () {
152
+ data.context = $(this);
153
+ that._trigger('completed', e, data);
154
+ }
155
+ );
156
+ }
157
+ );
158
+ });
159
+ } else {
160
+ template = that._renderDownload(data.result)
161
+ .appendTo(that.options.filesContainer);
162
+ that._forceReflow(template);
163
+ that._transition(template).done(
164
+ function () {
165
+ data.context = $(this);
166
+ that._trigger('completed', e, data);
167
+ }
168
+ );
169
+ }
170
+ },
171
+ // Callback for failed (abort or error) uploads:
172
+ fail: function (e, data) {
173
+ var that = $(this).data('fileupload'),
174
+ template;
175
+ that._adjustMaxNumberOfFiles(data.files.length);
176
+ if (data.context) {
177
+ data.context.each(function (index) {
178
+ if (data.errorThrown !== 'abort') {
179
+ var file = data.files[index];
180
+ file.error = file.error || data.errorThrown ||
181
+ true;
182
+ that._transition($(this)).done(
183
+ function () {
184
+ var node = $(this);
185
+ template = that._renderDownload([file])
186
+ .replaceAll(node);
187
+ that._forceReflow(template);
188
+ that._transition(template).done(
189
+ function () {
190
+ data.context = $(this);
191
+ that._trigger('failed', e, data);
192
+ }
193
+ );
194
+ }
195
+ );
196
+ } else {
197
+ that._transition($(this)).done(
198
+ function () {
199
+ $(this).remove();
200
+ that._trigger('failed', e, data);
201
+ }
202
+ );
203
+ }
204
+ });
205
+ } else if (data.errorThrown !== 'abort') {
206
+ that._adjustMaxNumberOfFiles(-data.files.length);
207
+ data.context = that._renderUpload(data.files)
208
+ .appendTo(that.options.filesContainer)
209
+ .data('data', data);
210
+ that._forceReflow(data.context);
211
+ that._transition(data.context).done(
212
+ function () {
213
+ data.context = $(this);
214
+ that._trigger('failed', e, data);
215
+ }
216
+ );
217
+ } else {
218
+ that._trigger('failed', e, data);
219
+ }
220
+ },
221
+ // Callback for upload progress events:
222
+ progress: function (e, data) {
223
+ if (data.context) {
224
+ data.context.find('.progress .bar').css(
225
+ 'width',
226
+ parseInt(data.loaded / data.total * 100, 10) + '%'
227
+ );
228
+ }
229
+ },
230
+ // Callback for global upload progress events:
231
+ progressall: function (e, data) {
232
+ $(this).find('.fileupload-buttonbar .progress .bar').css(
233
+ 'width',
234
+ parseInt(data.loaded / data.total * 100, 10) + '%'
235
+ );
236
+ },
237
+ // Callback for uploads start, equivalent to the global ajaxStart event:
238
+ start: function (e) {
239
+ var that = $(this).data('fileupload');
240
+ that._transition($(this).find('.fileupload-buttonbar .progress')).done(
241
+ function () {
242
+ that._trigger('started', e);
243
+ }
244
+ );
245
+ },
246
+ // Callback for uploads stop, equivalent to the global ajaxStop event:
247
+ stop: function (e) {
248
+ var that = $(this).data('fileupload');
249
+ that._transition($(this).find('.fileupload-buttonbar .progress')).done(
250
+ function () {
251
+ $(this).find('.bar').css('width', '0%');
252
+ that._trigger('stopped', e);
253
+ }
254
+ );
255
+ },
256
+ // Callback for file deletion:
257
+ destroy: function (e, data) {
258
+ var that = $(this).data('fileupload');
259
+ if (data.url) {
260
+ $.ajax(data);
261
+ }
262
+ that._adjustMaxNumberOfFiles(1);
263
+ that._transition(data.context).done(
264
+ function () {
265
+ $(this).remove();
266
+ that._trigger('destroyed', e, data);
267
+ }
268
+ );
269
+ }
270
+ },
271
+
272
+ // Link handler, that allows to download files
273
+ // by drag & drop of the links to the desktop:
274
+ _enableDragToDesktop: function () {
275
+ var link = $(this),
276
+ url = link.prop('href'),
277
+ name = link.prop('download'),
278
+ type = 'application/octet-stream';
279
+ link.bind('dragstart', function (e) {
280
+ try {
281
+ e.originalEvent.dataTransfer.setData(
282
+ 'DownloadURL',
283
+ [type, name, url].join(':')
284
+ );
285
+ } catch (err) {}
286
+ });
287
+ },
288
+
289
+ _adjustMaxNumberOfFiles: function (operand) {
290
+ if (typeof this.options.maxNumberOfFiles === 'number') {
291
+ this.options.maxNumberOfFiles += operand;
292
+ if (this.options.maxNumberOfFiles < 1) {
293
+ this._disableFileInputButton();
294
+ } else {
295
+ this._enableFileInputButton();
296
+ }
297
+ }
298
+ },
299
+
300
+ _formatFileSize: function (bytes) {
301
+ if (typeof bytes !== 'number') {
302
+ return '';
303
+ }
304
+ if (bytes >= 1000000000) {
305
+ return (bytes / 1000000000).toFixed(2) + ' GB';
306
+ }
307
+ if (bytes >= 1000000) {
308
+ return (bytes / 1000000).toFixed(2) + ' MB';
309
+ }
310
+ return (bytes / 1000).toFixed(2) + ' KB';
311
+ },
312
+
313
+ _hasError: function (file) {
314
+ if (file.error) {
315
+ return file.error;
316
+ }
317
+ // The number of added files is subtracted from
318
+ // maxNumberOfFiles before validation, so we check if
319
+ // maxNumberOfFiles is below 0 (instead of below 1):
320
+ if (this.options.maxNumberOfFiles < 0) {
321
+ return 'maxNumberOfFiles';
322
+ }
323
+ // Files are accepted if either the file type or the file name
324
+ // matches against the acceptFileTypes regular expression, as
325
+ // only browsers with support for the File API report the type:
326
+ if (!(this.options.acceptFileTypes.test(file.type) ||
327
+ this.options.acceptFileTypes.test(file.name))) {
328
+ return 'acceptFileTypes';
329
+ }
330
+ if (this.options.maxFileSize &&
331
+ file.size > this.options.maxFileSize) {
332
+ return 'maxFileSize';
333
+ }
334
+ if (typeof file.size === 'number' &&
335
+ file.size < this.options.minFileSize) {
336
+ return 'minFileSize';
337
+ }
338
+ return null;
339
+ },
340
+
341
+ _validate: function (files) {
342
+ var that = this,
343
+ valid = !!files.length;
344
+ $.each(files, function (index, file) {
345
+ file.error = that._hasError(file);
346
+ if (file.error) {
347
+ valid = false;
348
+ }
349
+ });
350
+ return valid;
351
+ },
352
+
353
+ _renderTemplate: function (func, files) {
354
+ if (!func) {
355
+ return $();
356
+ }
357
+ var result = func({
358
+ files: files,
359
+ formatFileSize: this._formatFileSize,
360
+ options: this.options
361
+ });
362
+ if (result instanceof $) {
363
+ return result;
364
+ }
365
+ return $(this.options.templatesContainer).html(result).children();
366
+ },
367
+
368
+ _renderPreview: function (file, node) {
369
+ var that = this,
370
+ options = this.options,
371
+ deferred = $.Deferred();
372
+ return ((loadImage && loadImage(
373
+ file,
374
+ function (img) {
375
+ node.append(img);
376
+ that._forceReflow(node);
377
+ that._transition(node).done(function () {
378
+ deferred.resolveWith(node);
379
+ });
380
+ },
381
+ {
382
+ maxWidth: options.previewMaxWidth,
383
+ maxHeight: options.previewMaxHeight,
384
+ canvas: options.previewAsCanvas
385
+ }
386
+ )) || deferred.resolveWith(node)) && deferred;
387
+ },
388
+
389
+ _renderPreviews: function (files, nodes) {
390
+ var that = this,
391
+ options = this.options;
392
+ nodes.find('.preview span').each(function (index, element) {
393
+ var file = files[index];
394
+ if (options.previewSourceFileTypes.test(file.type) &&
395
+ ($.type(options.previewSourceMaxFileSize) !== 'number' ||
396
+ file.size < options.previewSourceMaxFileSize)) {
397
+ that._processingQueue = that._processingQueue.pipe(function () {
398
+ var deferred = $.Deferred();
399
+ that._renderPreview(file, $(element)).done(
400
+ function () {
401
+ deferred.resolveWith(that);
402
+ }
403
+ );
404
+ return deferred.promise();
405
+ });
406
+ }
407
+ });
408
+ return this._processingQueue;
409
+ },
410
+
411
+ _renderUpload: function (files) {
412
+ return this._renderTemplate(
413
+ this.options.uploadTemplate,
414
+ files
415
+ );
416
+ },
417
+
418
+ _renderDownload: function (files) {
419
+ return this._renderTemplate(
420
+ this.options.downloadTemplate,
421
+ files
422
+ ).find('a[download]').each(this._enableDragToDesktop).end();
423
+ },
424
+
425
+ _startHandler: function (e) {
426
+ e.preventDefault();
427
+ var button = $(this),
428
+ template = button.closest('.template-upload'),
429
+ data = template.data('data');
430
+ if (data && data.submit && !data.jqXHR && data.submit()) {
431
+ button.prop('disabled', true);
432
+ }
433
+ },
434
+
435
+ _cancelHandler: function (e) {
436
+ e.preventDefault();
437
+ var template = $(this).closest('.template-upload'),
438
+ data = template.data('data') || {};
439
+ if (!data.jqXHR) {
440
+ data.errorThrown = 'abort';
441
+ e.data.fileupload._trigger('fail', e, data);
442
+ } else {
443
+ data.jqXHR.abort();
444
+ }
445
+ },
446
+
447
+ _deleteHandler: function (e) {
448
+ e.preventDefault();
449
+ var button = $(this);
450
+ e.data.fileupload._trigger('destroy', e, {
451
+ context: button.closest('.template-download'),
452
+ url: button.attr('data-url'),
453
+ type: button.attr('data-type') || 'DELETE',
454
+ dataType: e.data.fileupload.options.dataType
455
+ });
456
+ },
457
+
458
+ _forceReflow: function (node) {
459
+ this._reflow = $.support.transition &&
460
+ node.length && node[0].offsetWidth;
461
+ },
462
+
463
+ _transition: function (node) {
464
+ var that = this,
465
+ deferred = $.Deferred();
466
+ if ($.support.transition && node.hasClass('fade')) {
467
+ node.bind(
468
+ $.support.transition.end,
469
+ function (e) {
470
+ // Make sure we don't respond to other transitions events
471
+ // in the container element, e.g. from button elements:
472
+ if (e.target === node[0]) {
473
+ node.unbind($.support.transition.end);
474
+ deferred.resolveWith(node);
475
+ }
476
+ }
477
+ ).toggleClass('in');
478
+ } else {
479
+ node.toggleClass('in');
480
+ deferred.resolveWith(node);
481
+ }
482
+ return deferred;
483
+ },
484
+
485
+ _initButtonBarEventHandlers: function () {
486
+ var fileUploadButtonBar = this.element.find('.fileupload-buttonbar'),
487
+ filesList = this.options.filesContainer,
488
+ ns = this.options.namespace;
489
+ fileUploadButtonBar.find('.start')
490
+ .bind('click.' + ns, function (e) {
491
+ e.preventDefault();
492
+ filesList.find('.start button').click();
493
+ });
494
+ fileUploadButtonBar.find('.cancel')
495
+ .bind('click.' + ns, function (e) {
496
+ e.preventDefault();
497
+ filesList.find('.cancel button').click();
498
+ });
499
+ fileUploadButtonBar.find('.delete')
500
+ .bind('click.' + ns, function (e) {
501
+ e.preventDefault();
502
+ filesList.find('.delete input:checked')
503
+ .siblings('button').click();
504
+ fileUploadButtonBar.find('.toggle')
505
+ .prop('checked', false);
506
+ });
507
+ fileUploadButtonBar.find('.toggle')
508
+ .bind('change.' + ns, function (e) {
509
+ filesList.find('.delete input').prop(
510
+ 'checked',
511
+ $(this).is(':checked')
512
+ );
513
+ });
514
+ },
515
+
516
+ _destroyButtonBarEventHandlers: function () {
517
+ this.element.find('.fileupload-buttonbar button')
518
+ .unbind('click.' + this.options.namespace);
519
+ this.element.find('.fileupload-buttonbar .toggle')
520
+ .unbind('change.' + this.options.namespace);
521
+ },
522
+
523
+ _initEventHandlers: function () {
524
+ parentWidget.prototype._initEventHandlers.call(this);
525
+ var eventData = {fileupload: this};
526
+ this.options.filesContainer
527
+ .delegate(
528
+ '.start button',
529
+ 'click.' + this.options.namespace,
530
+ eventData,
531
+ this._startHandler
532
+ )
533
+ .delegate(
534
+ '.cancel button',
535
+ 'click.' + this.options.namespace,
536
+ eventData,
537
+ this._cancelHandler
538
+ )
539
+ .delegate(
540
+ '.delete button',
541
+ 'click.' + this.options.namespace,
542
+ eventData,
543
+ this._deleteHandler
544
+ );
545
+ this._initButtonBarEventHandlers();
546
+ },
547
+
548
+ _destroyEventHandlers: function () {
549
+ var options = this.options;
550
+ this._destroyButtonBarEventHandlers();
551
+ options.filesContainer
552
+ .undelegate('.start button', 'click.' + options.namespace)
553
+ .undelegate('.cancel button', 'click.' + options.namespace)
554
+ .undelegate('.delete button', 'click.' + options.namespace);
555
+ parentWidget.prototype._destroyEventHandlers.call(this);
556
+ },
557
+
558
+ _enableFileInputButton: function () {
559
+ this.element.find('.fileinput-button input')
560
+ .prop('disabled', false)
561
+ .parent().removeClass('disabled');
562
+ },
563
+
564
+ _disableFileInputButton: function () {
565
+ this.element.find('.fileinput-button input')
566
+ .prop('disabled', true)
567
+ .parent().addClass('disabled');
568
+ },
569
+
570
+ _initTemplates: function () {
571
+ var options = this.options;
572
+ options.templatesContainer = document.createElement(
573
+ options.filesContainer.prop('nodeName')
574
+ );
575
+ if (tmpl) {
576
+ if (options.uploadTemplateId) {
577
+ options.uploadTemplate = tmpl(options.uploadTemplateId);
578
+ }
579
+ if (options.downloadTemplateId) {
580
+ options.downloadTemplate = tmpl(options.downloadTemplateId);
581
+ }
582
+ }
583
+ },
584
+
585
+ _initFilesContainer: function () {
586
+ var options = this.options;
587
+ if (options.filesContainer === undefined) {
588
+ options.filesContainer = this.element.find('.files');
589
+ } else if (!(options.filesContainer instanceof $)) {
590
+ options.filesContainer = $(options.filesContainer);
591
+ }
592
+ },
593
+
594
+ _initSpecialOptions: function () {
595
+ parentWidget.prototype._initSpecialOptions.call(this);
596
+ this._initFilesContainer();
597
+ this._initTemplates();
598
+ },
599
+
600
+ _create: function () {
601
+ parentWidget.prototype._create.call(this);
602
+ this._refreshOptionsList.push(
603
+ 'filesContainer',
604
+ 'uploadTemplateId',
605
+ 'downloadTemplateId'
606
+ );
607
+ if (!$.blueimpIP) {
608
+ this._processingQueue = $.Deferred().resolveWith(this).promise();
609
+ this.resize = function () {
610
+ return this._processingQueue;
611
+ };
612
+ }
613
+ },
614
+
615
+ enable: function () {
616
+ parentWidget.prototype.enable.call(this);
617
+ this.element.find('input, button').prop('disabled', false);
618
+ this._enableFileInputButton();
619
+ },
620
+
621
+ disable: function () {
622
+ this.element.find('input, button').prop('disabled', true);
623
+ this._disableFileInputButton();
624
+ parentWidget.prototype.disable.call(this);
625
+ }
626
+
627
+ });
628
+
629
+ }));