fileuploader-rails 3.0.0 → 3.0.0.1

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.
@@ -1,575 +0,0 @@
1
- /**
2
- * Class that creates upload widget with drag-and-drop and file list
3
- * @inherits qq.FineUploaderBasic
4
- */
5
- qq.FineUploader = function(o){
6
- // call parent constructor
7
- qq.FineUploaderBasic.apply(this, arguments);
8
-
9
- // additional options
10
- qq.extend(this._options, {
11
- element: null,
12
- listElement: null,
13
- dragAndDrop: {
14
- extraDropzones: [],
15
- hideDropzones: true,
16
- disableDefaultDropzone: false
17
- },
18
- text: {
19
- uploadButton: 'Upload a file',
20
- cancelButton: 'Cancel',
21
- retryButton: 'Retry',
22
- failUpload: 'Upload failed',
23
- dragZone: 'Drop files here to upload',
24
- formatProgress: "{percent}% of {total_size}",
25
- waitingForResponse: "Processing..."
26
- },
27
- template: '<div class="qq-uploader">' +
28
- ((!this._options.dragAndDrop || !this._options.dragAndDrop.disableDefaultDropzone) ? '<div class="qq-upload-drop-area"><span>{dragZoneText}</span></div>' : '') +
29
- (!this._options.button ? '<div class="qq-upload-button"><div>{uploadButtonText}</div></div>' : '') +
30
- (!this._options.listElement ? '<ul class="qq-upload-list"></ul>' : '') +
31
- '</div>',
32
-
33
- // template for one item in file list
34
- fileTemplate: '<li>' +
35
- '<div class="qq-progress-bar"></div>' +
36
- '<span class="qq-upload-spinner"></span>' +
37
- '<span class="qq-upload-finished"></span>' +
38
- '<span class="qq-upload-file"></span>' +
39
- '<span class="qq-upload-size"></span>' +
40
- '<a class="qq-upload-cancel" href="#">{cancelButtonText}</a>' +
41
- '<a class="qq-upload-retry" href="#">{retryButtonText}</a>' +
42
- '<span class="qq-upload-status-text">{statusText}</span>' +
43
- '</li>',
44
- classes: {
45
- // used to get elements from templates
46
- button: 'qq-upload-button',
47
- drop: 'qq-upload-drop-area',
48
- dropActive: 'qq-upload-drop-area-active',
49
- dropDisabled: 'qq-upload-drop-area-disabled',
50
- list: 'qq-upload-list',
51
- progressBar: 'qq-progress-bar',
52
- file: 'qq-upload-file',
53
- spinner: 'qq-upload-spinner',
54
- finished: 'qq-upload-finished',
55
- retrying: 'qq-upload-retrying',
56
- retryable: 'qq-upload-retryable',
57
- size: 'qq-upload-size',
58
- cancel: 'qq-upload-cancel',
59
- retry: 'qq-upload-retry',
60
- statusText: 'qq-upload-status-text',
61
-
62
- // added to list item <li> when upload completes
63
- // used in css to hide progress spinner
64
- success: 'qq-upload-success',
65
- fail: 'qq-upload-fail',
66
-
67
- successIcon: null,
68
- failIcon: null
69
- },
70
- failedUploadTextDisplay: {
71
- mode: 'default', //default, custom, or none
72
- maxChars: 50,
73
- responseProperty: 'error',
74
- enableTooltip: true
75
- },
76
- messages: {
77
- tooManyFilesError: "You may only drop one file"
78
- },
79
- retry: {
80
- showAutoRetryNote: true,
81
- autoRetryNote: "Retrying {retryNum}/{maxAuto}...",
82
- showButton: false
83
- },
84
- showMessage: function(message){
85
- alert(message);
86
- }
87
- }, true);
88
-
89
- // overwrite options with user supplied
90
- qq.extend(this._options, o, true);
91
- this._wrapCallbacks();
92
-
93
- // overwrite the upload button text if any
94
- // same for the Cancel button and Fail message text
95
- this._options.template = this._options.template.replace(/\{dragZoneText\}/g, this._options.text.dragZone);
96
- this._options.template = this._options.template.replace(/\{uploadButtonText\}/g, this._options.text.uploadButton);
97
- this._options.fileTemplate = this._options.fileTemplate.replace(/\{cancelButtonText\}/g, this._options.text.cancelButton);
98
- this._options.fileTemplate = this._options.fileTemplate.replace(/\{retryButtonText\}/g, this._options.text.retryButton);
99
- this._options.fileTemplate = this._options.fileTemplate.replace(/\{statusText\}/g, "");
100
-
101
- this._element = this._options.element;
102
- this._element.innerHTML = this._options.template;
103
- this._listElement = this._options.listElement || this._find(this._element, 'list');
104
-
105
- this._classes = this._options.classes;
106
-
107
- if (!this._button) {
108
- this._button = this._createUploadButton(this._find(this._element, 'button'));
109
- }
110
-
111
- this._bindCancelAndRetryEvents();
112
- this._setupDragDrop();
113
- };
114
-
115
- // inherit from Basic Uploader
116
- qq.extend(qq.FineUploader.prototype, qq.FineUploaderBasic.prototype);
117
-
118
- qq.extend(qq.FineUploader.prototype, {
119
- clearStoredFiles: function() {
120
- qq.FineUploaderBasic.prototype.clearStoredFiles.apply(this, arguments);
121
- this._listElement.innerHTML = "";
122
- },
123
- addExtraDropzone: function(element){
124
- this._setupExtraDropzone(element);
125
- },
126
- removeExtraDropzone: function(element){
127
- var dzs = this._options.dragAndDrop.extraDropzones;
128
- for(var i in dzs) if (dzs[i] === element) return this._options.dragAndDrop.extraDropzones.splice(i,1);
129
- },
130
- getItemByFileId: function(id){
131
- var item = this._listElement.firstChild;
132
-
133
- // there can't be txt nodes in dynamically created list
134
- // and we can use nextSibling
135
- while (item){
136
- if (item.qqFileId == id) return item;
137
- item = item.nextSibling;
138
- }
139
- },
140
- reset: function() {
141
- qq.FineUploaderBasic.prototype.reset.apply(this, arguments);
142
- this._element.innerHTML = this._options.template;
143
- this._listElement = this._options.listElement || this._find(this._element, 'list');
144
- if (!this._options.button) {
145
- this._button = this._createUploadButton(this._find(this._element, 'button'));
146
- }
147
- this._bindCancelAndRetryEvents();
148
- this._setupDragDrop();
149
- },
150
- _leaving_document_out: function(e){
151
- return ((qq.chrome() || (qq.safari() && qq.windows())) && e.clientX == 0 && e.clientY == 0) // null coords for Chrome and Safari Windows
152
- || (qq.firefox() && !e.relatedTarget); // null e.relatedTarget for Firefox
153
- },
154
- _storeFileForLater: function(id) {
155
- qq.FineUploaderBasic.prototype._storeFileForLater.apply(this, arguments);
156
- var item = this.getItemByFileId(id);
157
- qq(this._find(item, 'spinner')).hide();
158
- },
159
- /**
160
- * Gets one of the elements listed in this._options.classes
161
- **/
162
- _find: function(parent, type){
163
- var element = qq(parent).getByClass(this._options.classes[type])[0];
164
- if (!element){
165
- throw new Error('element not found ' + type);
166
- }
167
-
168
- return element;
169
- },
170
- _setupExtraDropzone: function(element){
171
- this._options.dragAndDrop.extraDropzones.push(element);
172
- this._setupDropzone(element);
173
- },
174
- _setupDropzone: function(dropArea){
175
- var self = this;
176
-
177
- var dz = new qq.UploadDropZone({
178
- element: dropArea,
179
- onEnter: function(e){
180
- qq(dropArea).addClass(self._classes.dropActive);
181
- e.stopPropagation();
182
- },
183
- onLeave: function(e){
184
- //e.stopPropagation();
185
- },
186
- onLeaveNotDescendants: function(e){
187
- qq(dropArea).removeClass(self._classes.dropActive);
188
- },
189
- onDrop: function(e){
190
- if (self._options.dragAndDrop.hideDropzones) {
191
- qq(dropArea).hide();
192
- }
193
-
194
- qq(dropArea).removeClass(self._classes.dropActive);
195
- if (e.dataTransfer.files.length > 1 && !self._options.multiple) {
196
- self._error('tooManyFilesError', "");
197
- }
198
- else {
199
- self._uploadFileList(e.dataTransfer.files);
200
- }
201
- }
202
- });
203
-
204
- this.addDisposer(function() { dz.dispose(); });
205
-
206
- if (this._options.dragAndDrop.hideDropzones) {
207
- qq(dropArea).hide();
208
- }
209
- },
210
- _setupDragDrop: function(){
211
- var self, dropArea;
212
-
213
- self = this;
214
-
215
- if (!this._options.dragAndDrop.disableDefaultDropzone) {
216
- dropArea = this._find(this._element, 'drop');
217
- this._options.dragAndDrop.extraDropzones.push(dropArea);
218
- }
219
-
220
- var dropzones = this._options.dragAndDrop.extraDropzones;
221
- var i;
222
- for (i=0; i < dropzones.length; i++){
223
- this._setupDropzone(dropzones[i]);
224
- }
225
-
226
- // IE <= 9 does not support the File API used for drag+drop uploads
227
- if (!this._options.dragAndDrop.disableDefaultDropzone && (!qq.ie() || qq.ie10())) {
228
- this._attach(document, 'dragenter', function(e){
229
- if (qq(dropArea).hasClass(self._classes.dropDisabled)) return;
230
-
231
- dropArea.style.display = 'block';
232
- for (i=0; i < dropzones.length; i++){ dropzones[i].style.display = 'block'; }
233
-
234
- });
235
- }
236
- this._attach(document, 'dragleave', function(e){
237
- if (self._options.dragAndDrop.hideDropzones && qq.FineUploader.prototype._leaving_document_out(e)) {
238
- for (i=0; i < dropzones.length; i++) {
239
- qq(dropzones[i]).hide();
240
- }
241
- }
242
- });
243
- qq(document).attach('drop', function(e){
244
- if (self._options.dragAndDrop.hideDropzones) {
245
- for (i=0; i < dropzones.length; i++) {
246
- qq(dropzones[i]).hide();
247
- }
248
- }
249
- e.preventDefault();
250
- });
251
- },
252
- _onSubmit: function(id, fileName){
253
- qq.FineUploaderBasic.prototype._onSubmit.apply(this, arguments);
254
- this._addToList(id, fileName);
255
- },
256
- // Update the progress bar & percentage as the file is uploaded
257
- _onProgress: function(id, fileName, loaded, total){
258
- qq.FineUploaderBasic.prototype._onProgress.apply(this, arguments);
259
-
260
- var item, progressBar, text, percent, cancelLink, size;
261
-
262
- item = this.getItemByFileId(id);
263
- progressBar = this._find(item, 'progressBar');
264
- percent = Math.round(loaded / total * 100);
265
-
266
- if (loaded === total) {
267
- cancelLink = this._find(item, 'cancel');
268
- qq(cancelLink).hide();
269
-
270
- qq(progressBar).hide();
271
- qq(this._find(item, 'statusText')).setText(this._options.text.waitingForResponse);
272
-
273
- // If last byte was sent, just display final size
274
- text = this._formatSize(total);
275
- }
276
- else {
277
- // If still uploading, display percentage
278
- text = this._formatProgress(loaded, total);
279
-
280
- qq(progressBar).css({display: 'block'});
281
- }
282
-
283
- // Update progress bar element
284
- qq(progressBar).css({width: percent + '%'});
285
-
286
- size = this._find(item, 'size');
287
- qq(size).css({display: 'inline'});
288
- qq(size).setText(text);
289
- },
290
- _onComplete: function(id, fileName, result, xhr){
291
- qq.FineUploaderBasic.prototype._onComplete.apply(this, arguments);
292
-
293
- var item = this.getItemByFileId(id);
294
-
295
- qq(this._find(item, 'statusText')).clearText();
296
-
297
- qq(item).removeClass(this._classes.retrying);
298
- qq(this._find(item, 'progressBar')).hide();
299
-
300
- if (!this._options.disableCancelForFormUploads || qq.UploadHandlerXhr.isSupported()) {
301
- qq(this._find(item, 'cancel')).hide();
302
- }
303
- qq(this._find(item, 'spinner')).hide();
304
-
305
- if (result.success){
306
- qq(item).addClass(this._classes.success);
307
- if (this._classes.successIcon) {
308
- this._find(item, 'finished').style.display = "inline-block";
309
- qq(item).addClass(this._classes.successIcon);
310
- }
311
- } else {
312
- qq(item).addClass(this._classes.fail);
313
- if (this._classes.failIcon) {
314
- this._find(item, 'finished').style.display = "inline-block";
315
- qq(item).addClass(this._classes.failIcon);
316
- }
317
- if (this._options.retry.showButton && !this._preventRetries[id]) {
318
- qq(item).addClass(this._classes.retryable);
319
- }
320
- this._controlFailureTextDisplay(item, result);
321
- }
322
- },
323
- _onUpload: function(id, fileName, xhr){
324
- qq.FineUploaderBasic.prototype._onUpload.apply(this, arguments);
325
-
326
- var item = this.getItemByFileId(id);
327
- this._showSpinner(item);
328
- },
329
- _onBeforeAutoRetry: function(id) {
330
- var item, progressBar, cancelLink, failTextEl, retryNumForDisplay, maxAuto, retryNote;
331
-
332
- qq.FineUploaderBasic.prototype._onBeforeAutoRetry.apply(this, arguments);
333
-
334
- item = this.getItemByFileId(id);
335
- progressBar = this._find(item, 'progressBar');
336
-
337
- this._showCancelLink(item);
338
- progressBar.style.width = 0;
339
- qq(progressBar).hide();
340
-
341
- if (this._options.retry.showAutoRetryNote) {
342
- failTextEl = this._find(item, 'statusText');
343
- retryNumForDisplay = this._autoRetries[id] + 1;
344
- maxAuto = this._options.retry.maxAutoAttempts;
345
-
346
- retryNote = this._options.retry.autoRetryNote.replace(/\{retryNum\}/g, retryNumForDisplay);
347
- retryNote = retryNote.replace(/\{maxAuto\}/g, maxAuto);
348
-
349
- qq(failTextEl).setText(retryNote);
350
- if (retryNumForDisplay === 1) {
351
- qq(item).addClass(this._classes.retrying);
352
- }
353
- }
354
- },
355
- //return false if we should not attempt the requested retry
356
- _onBeforeManualRetry: function(id) {
357
- if (qq.FineUploaderBasic.prototype._onBeforeManualRetry.apply(this, arguments)) {
358
- var item = this.getItemByFileId(id);
359
- this._find(item, 'progressBar').style.width = 0;
360
- qq(item).removeClass(this._classes.fail);
361
- this._showSpinner(item);
362
- this._showCancelLink(item);
363
- return true;
364
- }
365
- return false;
366
- },
367
- _addToList: function(id, fileName){
368
- var item = qq.toElement(this._options.fileTemplate);
369
- if (this._options.disableCancelForFormUploads && !qq.UploadHandlerXhr.isSupported()) {
370
- var cancelLink = this._find(item, 'cancel');
371
- qq(cancelLink).remove();
372
- }
373
-
374
- item.qqFileId = id;
375
-
376
- var fileElement = this._find(item, 'file');
377
- qq(fileElement).setText(this._formatFileName(fileName));
378
- qq(this._find(item, 'size')).hide();
379
- if (!this._options.multiple) this._clearList();
380
- this._listElement.appendChild(item);
381
- },
382
- _clearList: function(){
383
- this._listElement.innerHTML = '';
384
- this.clearStoredFiles();
385
- },
386
- /**
387
- * delegate click event for cancel & retry links
388
- **/
389
- _bindCancelAndRetryEvents: function(){
390
- var self = this,
391
- list = this._listElement;
392
-
393
- this._attach(list, 'click', function(e){
394
- e = e || window.event;
395
- var target = e.target || e.srcElement;
396
-
397
- if (qq(target).hasClass(self._classes.cancel) || qq(target).hasClass(self._classes.retry)){
398
- qq.preventDefault(e);
399
-
400
- var item = target.parentNode;
401
- while(item.qqFileId == undefined) {
402
- item = target = target.parentNode;
403
- }
404
-
405
- if (qq(target).hasClass(self._classes.cancel)) {
406
- self.cancel(item.qqFileId);
407
- qq(item).remove();
408
- }
409
- else {
410
- qq(item).removeClass(self._classes.retryable);
411
- self.retry(item.qqFileId);
412
- }
413
- }
414
- });
415
- },
416
- _formatProgress: function (uploadedSize, totalSize) {
417
- var message = this._options.text.formatProgress;
418
- function r(name, replacement) { message = message.replace(name, replacement); }
419
-
420
- r('{percent}', Math.round(uploadedSize / totalSize * 100));
421
- r('{total_size}', this._formatSize(totalSize));
422
- return message;
423
- },
424
- _controlFailureTextDisplay: function(item, response) {
425
- var mode, maxChars, responseProperty, failureReason, shortFailureReason;
426
-
427
- mode = this._options.failedUploadTextDisplay.mode;
428
- maxChars = this._options.failedUploadTextDisplay.maxChars;
429
- responseProperty = this._options.failedUploadTextDisplay.responseProperty;
430
-
431
- if (mode === 'custom') {
432
- failureReason = response[responseProperty];
433
- if (failureReason) {
434
- if (failureReason.length > maxChars) {
435
- shortFailureReason = failureReason.substring(0, maxChars) + '...';
436
- }
437
- }
438
- else {
439
- failureReason = this._options.text.failUpload;
440
- this.log("'" + responseProperty + "' is not a valid property on the server response.", 'warn');
441
- }
442
-
443
- qq(this._find(item, 'statusText')).setText(shortFailureReason || failureReason);
444
-
445
- if (this._options.failedUploadTextDisplay.enableTooltip) {
446
- this._showTooltip(item, failureReason);
447
- }
448
- }
449
- else if (mode === 'default') {
450
- qq(this._find(item, 'statusText')).setText(this._options.text.failUpload);
451
- }
452
- else if (mode !== 'none') {
453
- this.log("failedUploadTextDisplay.mode value of '" + mode + "' is not valid", 'warn');
454
- }
455
- },
456
- //TODO turn this into a real tooltip, with click trigger (so it is usable on mobile devices). See case #355 for details.
457
- _showTooltip: function(item, text) {
458
- item.title = text;
459
- },
460
- _showSpinner: function(item) {
461
- var spinnerEl = this._find(item, 'spinner');
462
- spinnerEl.style.display = "inline-block";
463
- },
464
- _showCancelLink: function(item) {
465
- if (!this._options.disableCancelForFormUploads || qq.UploadHandlerXhr.isSupported()) {
466
- var cancelLink = this._find(item, 'cancel');
467
- cancelLink.style.display = 'inline';
468
- }
469
- },
470
- _error: function(code, fileName){
471
- var message = qq.FineUploaderBasic.prototype._error.apply(this, arguments);
472
- this._options.showMessage(message);
473
- }
474
- });
475
-
476
- qq.UploadDropZone = function(o){
477
- this._options = {
478
- element: null,
479
- onEnter: function(e){},
480
- onLeave: function(e){},
481
- // is not fired when leaving element by hovering descendants
482
- onLeaveNotDescendants: function(e){},
483
- onDrop: function(e){}
484
- };
485
- qq.extend(this._options, o);
486
- qq.extend(this, qq.DisposeSupport);
487
-
488
- this._element = this._options.element;
489
-
490
- this._disableDropOutside();
491
- this._attachEvents();
492
- };
493
-
494
- qq.UploadDropZone.prototype = {
495
- _dragover_should_be_canceled: function(){
496
- return qq.safari() || (qq.firefox() && qq.windows());
497
- },
498
- _disableDropOutside: function(e){
499
- // run only once for all instances
500
- if (!qq.UploadDropZone.dropOutsideDisabled ){
501
-
502
- // for these cases we need to catch onDrop to reset dropArea
503
- if (this._dragover_should_be_canceled){
504
- qq(document).attach('dragover', function(e){
505
- e.preventDefault();
506
- });
507
- } else {
508
- qq(document).attach('dragover', function(e){
509
- if (e.dataTransfer){
510
- e.dataTransfer.dropEffect = 'none';
511
- e.preventDefault();
512
- }
513
- });
514
- }
515
-
516
- qq.UploadDropZone.dropOutsideDisabled = true;
517
- }
518
- },
519
- _attachEvents: function(){
520
- var self = this;
521
-
522
- self._attach(self._element, 'dragover', function(e){
523
- if (!self._isValidFileDrag(e)) return;
524
-
525
- var effect = qq.ie() ? null : e.dataTransfer.effectAllowed;
526
- if (effect == 'move' || effect == 'linkMove'){
527
- e.dataTransfer.dropEffect = 'move'; // for FF (only move allowed)
528
- } else {
529
- e.dataTransfer.dropEffect = 'copy'; // for Chrome
530
- }
531
-
532
- e.stopPropagation();
533
- e.preventDefault();
534
- });
535
-
536
- self._attach(self._element, 'dragenter', function(e){
537
- if (!self._isValidFileDrag(e)) return;
538
-
539
- self._options.onEnter(e);
540
- });
541
-
542
- self._attach(self._element, 'dragleave', function(e){
543
- if (!self._isValidFileDrag(e)) return;
544
-
545
- self._options.onLeave(e);
546
-
547
- var relatedTarget = document.elementFromPoint(e.clientX, e.clientY);
548
- // do not fire when moving a mouse over a descendant
549
- if (qq(this).contains(relatedTarget)) return;
550
-
551
- self._options.onLeaveNotDescendants(e);
552
- });
553
-
554
- self._attach(self._element, 'drop', function(e){
555
- if (!self._isValidFileDrag(e)) return;
556
-
557
- e.preventDefault();
558
- self._options.onDrop(e);
559
- });
560
- },
561
- _isValidFileDrag: function(e){
562
- // e.dataTransfer currently causing IE errors
563
- // IE9 does NOT support file API, so drag-and-drop is not possible
564
- if (qq.ie() && !qq.ie10()) return false;
565
-
566
- var dt = e.dataTransfer,
567
- // do not check dt.types.contains in webkit, because it crashes safari 4
568
- isSafari = qq.safari();
569
-
570
- // dt.effectAllowed is none in Safari 5
571
- // dt.types.contains check is for firefox
572
- var effectTest = qq.ie10() ? true : dt.effectAllowed != 'none';
573
- return dt && effectTest && (dt.files || (!isSafari && dt.types.contains && dt.types.contains('Files')));
574
- }
575
- };