fileuploader-rails 3.0.0 → 3.0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- };