jquery.fileupload-rails 0.1.2 → 1.0.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,702 +0,0 @@
1
- /*
2
- * jQuery File Upload User Interface Plugin 6.9.1
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-fp'
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 FP (file processing) version or the basic
37
- // file upload widget and adds complete user interface interaction:
38
- var parentWidget = ($.blueimpFP || $.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 container for the list of files. If undefined, it is set to
74
- // an element with class "files" inside of the widget element:
75
- filesContainer: undefined,
76
- // By default, files are appended to the files container.
77
- // Set the following option to true, to prepend files instead:
78
- prependFiles: false,
79
- // The expected data type of the upload response, sets the dataType
80
- // option of the $.ajax upload requests:
81
- dataType: 'json',
82
-
83
- // The add callback is invoked as soon as files are added to the fileupload
84
- // widget (via file input selection, drag & drop or add API call).
85
- // See the basic file upload widget for more information:
86
- add: function (e, data) {
87
- var that = $(this).data('fileupload'),
88
- options = that.options,
89
- files = data.files;
90
- $(this).fileupload('process', data).done(function () {
91
- that._adjustMaxNumberOfFiles(-files.length);
92
- data.isAdjusted = true;
93
- data.files.valid = data.isValidated = that._validate(files);
94
- data.context = that._renderUpload(files).data('data', data);
95
- options.filesContainer[
96
- options.prependFiles ? 'prepend' : 'append'
97
- ](data.context);
98
- that._renderPreviews(files, data.context);
99
- that._forceReflow(data.context);
100
- that._transition(data.context).done(
101
- function () {
102
- if ((that._trigger('added', e, data) !== false) &&
103
- (options.autoUpload || data.autoUpload) &&
104
- data.autoUpload !== false && data.isValidated) {
105
- data.submit();
106
- }
107
- }
108
- );
109
- });
110
- },
111
- // Callback for the start of each file upload request:
112
- send: function (e, data) {
113
- var that = $(this).data('fileupload');
114
- if (!data.isValidated) {
115
- if (!data.isAdjusted) {
116
- that._adjustMaxNumberOfFiles(-data.files.length);
117
- }
118
- if (!that._validate(data.files)) {
119
- return false;
120
- }
121
- }
122
- if (data.context && data.dataType &&
123
- data.dataType.substr(0, 6) === 'iframe') {
124
- // Iframe Transport does not support progress events.
125
- // In lack of an indeterminate progress bar, we set
126
- // the progress to 100%, showing the full animated bar:
127
- data.context
128
- .find('.progress').addClass(
129
- !$.support.transition && 'progress-animated'
130
- )
131
- .attr('aria-valuenow', 100)
132
- .find('.bar').css(
133
- 'width',
134
- '100%'
135
- );
136
- }
137
- return that._trigger('sent', e, data);
138
- },
139
- // Callback for successful uploads:
140
- done: function (e, data) {
141
- var that = $(this).data('fileupload'),
142
- template;
143
- if (data.context) {
144
- data.context.each(function (index) {
145
- var file = ($.isArray(data.result) &&
146
- data.result[index]) || {error: 'emptyResult'};
147
- if (file.error) {
148
- that._adjustMaxNumberOfFiles(1);
149
- }
150
- that._transition($(this)).done(
151
- function () {
152
- var node = $(this);
153
- template = that._renderDownload([file])
154
- .css('height', node.height())
155
- .replaceAll(node);
156
- that._forceReflow(template);
157
- that._transition(template).done(
158
- function () {
159
- data.context = $(this);
160
- that._trigger('completed', e, data);
161
- }
162
- );
163
- }
164
- );
165
- });
166
- } else {
167
- template = that._renderDownload(data.result)
168
- .appendTo(that.options.filesContainer);
169
- that._forceReflow(template);
170
- that._transition(template).done(
171
- function () {
172
- data.context = $(this);
173
- that._trigger('completed', e, data);
174
- }
175
- );
176
- }
177
- },
178
- // Callback for failed (abort or error) uploads:
179
- fail: function (e, data) {
180
- var that = $(this).data('fileupload'),
181
- template;
182
- that._adjustMaxNumberOfFiles(data.files.length);
183
- if (data.context) {
184
- data.context.each(function (index) {
185
- if (data.errorThrown !== 'abort') {
186
- var file = data.files[index];
187
- file.error = file.error || data.errorThrown ||
188
- true;
189
- that._transition($(this)).done(
190
- function () {
191
- var node = $(this);
192
- template = that._renderDownload([file])
193
- .replaceAll(node);
194
- that._forceReflow(template);
195
- that._transition(template).done(
196
- function () {
197
- data.context = $(this);
198
- that._trigger('failed', e, data);
199
- }
200
- );
201
- }
202
- );
203
- } else {
204
- that._transition($(this)).done(
205
- function () {
206
- $(this).remove();
207
- that._trigger('failed', e, data);
208
- }
209
- );
210
- }
211
- });
212
- } else if (data.errorThrown !== 'abort') {
213
- that._adjustMaxNumberOfFiles(-data.files.length);
214
- data.context = that._renderUpload(data.files)
215
- .appendTo(that.options.filesContainer)
216
- .data('data', data);
217
- that._forceReflow(data.context);
218
- that._transition(data.context).done(
219
- function () {
220
- data.context = $(this);
221
- that._trigger('failed', e, data);
222
- }
223
- );
224
- } else {
225
- that._trigger('failed', e, data);
226
- }
227
- },
228
- // Callback for upload progress events:
229
- progress: function (e, data) {
230
- if (data.context) {
231
- var progress = parseInt(data.loaded / data.total * 100, 10);
232
- data.context.find('.progress')
233
- .attr('aria-valuenow', progress)
234
- .find('.bar').css(
235
- 'width',
236
- progress + '%'
237
- );
238
- }
239
- },
240
- // Callback for global upload progress events:
241
- progressall: function (e, data) {
242
- var $this = $(this),
243
- progress = parseInt(data.loaded / data.total * 100, 10),
244
- globalProgressNode = $this.find('.fileupload-progress'),
245
- extendedProgressNode = globalProgressNode
246
- .find('.progress-extended');
247
- if (extendedProgressNode.length) {
248
- extendedProgressNode.html(
249
- $this.data('fileupload')._renderExtendedProgress(data)
250
- );
251
- }
252
- globalProgressNode
253
- .find('.progress')
254
- .attr('aria-valuenow', progress)
255
- .find('.bar').css(
256
- 'width',
257
- progress + '%'
258
- );
259
- },
260
- // Callback for uploads start, equivalent to the global ajaxStart event:
261
- start: function (e) {
262
- var that = $(this).data('fileupload');
263
- that._transition($(this).find('.fileupload-progress')).done(
264
- function () {
265
- that._trigger('started', e);
266
- }
267
- );
268
- },
269
- // Callback for uploads stop, equivalent to the global ajaxStop event:
270
- stop: function (e) {
271
- var that = $(this).data('fileupload');
272
- that._transition($(this).find('.fileupload-progress')).done(
273
- function () {
274
- $(this).find('.progress')
275
- .attr('aria-valuenow', '0')
276
- .find('.bar').css('width', '0%');
277
- $(this).find('.progress-extended').html(' ');
278
- that._trigger('stopped', e);
279
- }
280
- );
281
- },
282
- // Callback for file deletion:
283
- destroy: function (e, data) {
284
- var that = $(this).data('fileupload');
285
- if (data.url) {
286
- $.ajax(data);
287
- that._adjustMaxNumberOfFiles(1);
288
- }
289
- that._transition(data.context).done(
290
- function () {
291
- $(this).remove();
292
- that._trigger('destroyed', e, data);
293
- }
294
- );
295
- }
296
- },
297
-
298
- // Link handler, that allows to download files
299
- // by drag & drop of the links to the desktop:
300
- _enableDragToDesktop: function () {
301
- var link = $(this),
302
- url = link.prop('href'),
303
- name = link.prop('download'),
304
- type = 'application/octet-stream';
305
- link.bind('dragstart', function (e) {
306
- try {
307
- e.originalEvent.dataTransfer.setData(
308
- 'DownloadURL',
309
- [type, name, url].join(':')
310
- );
311
- } catch (err) {}
312
- });
313
- },
314
-
315
- _adjustMaxNumberOfFiles: function (operand) {
316
- if (typeof this.options.maxNumberOfFiles === 'number') {
317
- this.options.maxNumberOfFiles += operand;
318
- if (this.options.maxNumberOfFiles < 1) {
319
- this._disableFileInputButton();
320
- } else {
321
- this._enableFileInputButton();
322
- }
323
- }
324
- },
325
-
326
- _formatFileSize: function (bytes) {
327
- if (typeof bytes !== 'number') {
328
- return '';
329
- }
330
- if (bytes >= 1000000000) {
331
- return (bytes / 1000000000).toFixed(2) + ' GB';
332
- }
333
- if (bytes >= 1000000) {
334
- return (bytes / 1000000).toFixed(2) + ' MB';
335
- }
336
- return (bytes / 1000).toFixed(2) + ' KB';
337
- },
338
-
339
- _formatBitrate: function (bits) {
340
- if (typeof bits !== 'number') {
341
- return '';
342
- }
343
- if (bits >= 1000000000) {
344
- return (bits / 1000000000).toFixed(2) + ' Gbit/s';
345
- }
346
- if (bits >= 1000000) {
347
- return (bits / 1000000).toFixed(2) + ' Mbit/s';
348
- }
349
- if (bits >= 1000) {
350
- return (bits / 1000).toFixed(2) + ' kbit/s';
351
- }
352
- return bits + ' bit/s';
353
- },
354
-
355
- _formatTime: function (seconds) {
356
- var date = new Date(seconds * 1000),
357
- days = parseInt(seconds / 86400, 10);
358
- days = days ? days + 'd ' : '';
359
- return days +
360
- ('0' + date.getUTCHours()).slice(-2) + ':' +
361
- ('0' + date.getUTCMinutes()).slice(-2) + ':' +
362
- ('0' + date.getUTCSeconds()).slice(-2);
363
- },
364
-
365
- _formatPercentage: function (floatValue) {
366
- return (floatValue * 100).toFixed(2) + ' %';
367
- },
368
-
369
- _renderExtendedProgress: function (data) {
370
- return this._formatBitrate(data.bitrate) + ' | ' +
371
- this._formatTime(
372
- (data.total - data.loaded) * 8 / data.bitrate
373
- ) + ' | ' +
374
- this._formatPercentage(
375
- data.loaded / data.total
376
- ) + ' | ' +
377
- this._formatFileSize(data.loaded) + ' / ' +
378
- this._formatFileSize(data.total);
379
- },
380
-
381
- _hasError: function (file) {
382
- if (file.error) {
383
- return file.error;
384
- }
385
- // The number of added files is subtracted from
386
- // maxNumberOfFiles before validation, so we check if
387
- // maxNumberOfFiles is below 0 (instead of below 1):
388
- if (this.options.maxNumberOfFiles < 0) {
389
- return 'maxNumberOfFiles';
390
- }
391
- // Files are accepted if either the file type or the file name
392
- // matches against the acceptFileTypes regular expression, as
393
- // only browsers with support for the File API report the type:
394
- if (!(this.options.acceptFileTypes.test(file.type) ||
395
- this.options.acceptFileTypes.test(file.name))) {
396
- return 'acceptFileTypes';
397
- }
398
- if (this.options.maxFileSize &&
399
- file.size > this.options.maxFileSize) {
400
- return 'maxFileSize';
401
- }
402
- if (typeof file.size === 'number' &&
403
- file.size < this.options.minFileSize) {
404
- return 'minFileSize';
405
- }
406
- return null;
407
- },
408
-
409
- _validate: function (files) {
410
- var that = this,
411
- valid = !!files.length;
412
- $.each(files, function (index, file) {
413
- file.error = that._hasError(file);
414
- if (file.error) {
415
- valid = false;
416
- }
417
- });
418
- return valid;
419
- },
420
-
421
- _renderTemplate: function (func, files) {
422
- if (!func) {
423
- return $();
424
- }
425
- var result = func({
426
- files: files,
427
- formatFileSize: this._formatFileSize,
428
- options: this.options
429
- });
430
- if (result instanceof $) {
431
- return result;
432
- }
433
- return $(this.options.templatesContainer).html(result).children();
434
- },
435
-
436
- _renderPreview: function (file, node) {
437
- var that = this,
438
- options = this.options,
439
- dfd = $.Deferred();
440
- return ((loadImage && loadImage(
441
- file,
442
- function (img) {
443
- node.append(img);
444
- that._forceReflow(node);
445
- that._transition(node).done(function () {
446
- dfd.resolveWith(node);
447
- });
448
- if (!$.contains(document.body, node[0])) {
449
- // If the element is not part of the DOM,
450
- // transition events are not triggered,
451
- // so we have to resolve manually:
452
- dfd.resolveWith(node);
453
- }
454
- },
455
- {
456
- maxWidth: options.previewMaxWidth,
457
- maxHeight: options.previewMaxHeight,
458
- canvas: options.previewAsCanvas
459
- }
460
- )) || dfd.resolveWith(node)) && dfd;
461
- },
462
-
463
- _renderPreviews: function (files, nodes) {
464
- var that = this,
465
- options = this.options;
466
- nodes.find('.preview span').each(function (index, element) {
467
- var file = files[index];
468
- if (options.previewSourceFileTypes.test(file.type) &&
469
- ($.type(options.previewSourceMaxFileSize) !== 'number' ||
470
- file.size < options.previewSourceMaxFileSize)) {
471
- that._processingQueue = that._processingQueue.pipe(function () {
472
- var dfd = $.Deferred();
473
- that._renderPreview(file, $(element)).done(
474
- function () {
475
- dfd.resolveWith(that);
476
- }
477
- );
478
- return dfd.promise();
479
- });
480
- }
481
- });
482
- return this._processingQueue;
483
- },
484
-
485
- _renderUpload: function (files) {
486
- return this._renderTemplate(
487
- this.options.uploadTemplate,
488
- files
489
- );
490
- },
491
-
492
- _renderDownload: function (files) {
493
- return this._renderTemplate(
494
- this.options.downloadTemplate,
495
- files
496
- ).find('a[download]').each(this._enableDragToDesktop).end();
497
- },
498
-
499
- _startHandler: function (e) {
500
- e.preventDefault();
501
- var button = $(this),
502
- template = button.closest('.template-upload'),
503
- data = template.data('data');
504
- if (data && data.submit && !data.jqXHR && data.submit()) {
505
- button.prop('disabled', true);
506
- }
507
- },
508
-
509
- _cancelHandler: function (e) {
510
- e.preventDefault();
511
- var template = $(this).closest('.template-upload'),
512
- data = template.data('data') || {};
513
- if (!data.jqXHR) {
514
- data.errorThrown = 'abort';
515
- e.data.fileupload._trigger('fail', e, data);
516
- } else {
517
- data.jqXHR.abort();
518
- }
519
- },
520
-
521
- _deleteHandler: function (e) {
522
- e.preventDefault();
523
- var button = $(this);
524
- e.data.fileupload._trigger('destroy', e, {
525
- context: button.closest('.template-download'),
526
- url: button.attr('data-url'),
527
- type: button.attr('data-type') || 'DELETE',
528
- dataType: e.data.fileupload.options.dataType
529
- });
530
- },
531
-
532
- _forceReflow: function (node) {
533
- return $.support.transition && node.length &&
534
- node[0].offsetWidth;
535
- },
536
-
537
- _transition: function (node) {
538
- var dfd = $.Deferred();
539
- if ($.support.transition && node.hasClass('fade')) {
540
- node.bind(
541
- $.support.transition.end,
542
- function (e) {
543
- // Make sure we don't respond to other transitions events
544
- // in the container element, e.g. from button elements:
545
- if (e.target === node[0]) {
546
- node.unbind($.support.transition.end);
547
- dfd.resolveWith(node);
548
- }
549
- }
550
- ).toggleClass('in');
551
- } else {
552
- node.toggleClass('in');
553
- dfd.resolveWith(node);
554
- }
555
- return dfd;
556
- },
557
-
558
- _initButtonBarEventHandlers: function () {
559
- var fileUploadButtonBar = this.element.find('.fileupload-buttonbar'),
560
- filesList = this.options.filesContainer,
561
- ns = this.options.namespace;
562
- fileUploadButtonBar.find('.start')
563
- .bind('click.' + ns, function (e) {
564
- e.preventDefault();
565
- filesList.find('.start button').click();
566
- });
567
- fileUploadButtonBar.find('.cancel')
568
- .bind('click.' + ns, function (e) {
569
- e.preventDefault();
570
- filesList.find('.cancel button').click();
571
- });
572
- fileUploadButtonBar.find('.delete')
573
- .bind('click.' + ns, function (e) {
574
- e.preventDefault();
575
- filesList.find('.delete input:checked')
576
- .siblings('button').click();
577
- fileUploadButtonBar.find('.toggle')
578
- .prop('checked', false);
579
- });
580
- fileUploadButtonBar.find('.toggle')
581
- .bind('change.' + ns, function (e) {
582
- filesList.find('.delete input').prop(
583
- 'checked',
584
- $(this).is(':checked')
585
- );
586
- });
587
- },
588
-
589
- _destroyButtonBarEventHandlers: function () {
590
- this.element.find('.fileupload-buttonbar button')
591
- .unbind('click.' + this.options.namespace);
592
- this.element.find('.fileupload-buttonbar .toggle')
593
- .unbind('change.' + this.options.namespace);
594
- },
595
-
596
- _initEventHandlers: function () {
597
- parentWidget.prototype._initEventHandlers.call(this);
598
- var eventData = {fileupload: this};
599
- this.options.filesContainer
600
- .delegate(
601
- '.start button',
602
- 'click.' + this.options.namespace,
603
- eventData,
604
- this._startHandler
605
- )
606
- .delegate(
607
- '.cancel button',
608
- 'click.' + this.options.namespace,
609
- eventData,
610
- this._cancelHandler
611
- )
612
- .delegate(
613
- '.delete button',
614
- 'click.' + this.options.namespace,
615
- eventData,
616
- this._deleteHandler
617
- );
618
- this._initButtonBarEventHandlers();
619
- },
620
-
621
- _destroyEventHandlers: function () {
622
- var options = this.options;
623
- this._destroyButtonBarEventHandlers();
624
- options.filesContainer
625
- .undelegate('.start button', 'click.' + options.namespace)
626
- .undelegate('.cancel button', 'click.' + options.namespace)
627
- .undelegate('.delete button', 'click.' + options.namespace);
628
- parentWidget.prototype._destroyEventHandlers.call(this);
629
- },
630
-
631
- _enableFileInputButton: function () {
632
- this.element.find('.fileinput-button input')
633
- .prop('disabled', false)
634
- .parent().removeClass('disabled');
635
- },
636
-
637
- _disableFileInputButton: function () {
638
- this.element.find('.fileinput-button input')
639
- .prop('disabled', true)
640
- .parent().addClass('disabled');
641
- },
642
-
643
- _initTemplates: function () {
644
- var options = this.options;
645
- options.templatesContainer = document.createElement(
646
- options.filesContainer.prop('nodeName')
647
- );
648
- if (tmpl) {
649
- if (options.uploadTemplateId) {
650
- options.uploadTemplate = tmpl(options.uploadTemplateId);
651
- }
652
- if (options.downloadTemplateId) {
653
- options.downloadTemplate = tmpl(options.downloadTemplateId);
654
- }
655
- }
656
- },
657
-
658
- _initFilesContainer: function () {
659
- var options = this.options;
660
- if (options.filesContainer === undefined) {
661
- options.filesContainer = this.element.find('.files');
662
- } else if (!(options.filesContainer instanceof $)) {
663
- options.filesContainer = $(options.filesContainer);
664
- }
665
- },
666
-
667
- _initSpecialOptions: function () {
668
- parentWidget.prototype._initSpecialOptions.call(this);
669
- this._initFilesContainer();
670
- this._initTemplates();
671
- },
672
-
673
- _create: function () {
674
- parentWidget.prototype._create.call(this);
675
- this._refreshOptionsList.push(
676
- 'filesContainer',
677
- 'uploadTemplateId',
678
- 'downloadTemplateId'
679
- );
680
- if (!$.blueimpFP) {
681
- this._processingQueue = $.Deferred().resolveWith(this).promise();
682
- this.process = function () {
683
- return this._processingQueue;
684
- };
685
- }
686
- },
687
-
688
- enable: function () {
689
- parentWidget.prototype.enable.call(this);
690
- this.element.find('input, button').prop('disabled', false);
691
- this._enableFileInputButton();
692
- },
693
-
694
- disable: function () {
695
- this.element.find('input, button').prop('disabled', true);
696
- this._disableFileInputButton();
697
- parentWidget.prototype.disable.call(this);
698
- }
699
-
700
- });
701
-
702
- }));