weekly release 2.4dev
[moodle.git] / lib / yuilib / 3.7.1 / build / uploader-queue / uploader-queue-debug.js
blob8581260f2f73d952037940a839b91549d7dee87e
1 /*
2 YUI 3.7.1 (build 5627)
3 Copyright 2012 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
6 */
7 YUI.add('uploader-queue', function (Y, NAME) {
10     /**
11      * The class manages a queue of files that should be uploaded to the server.
12      * It initializes the required number of uploads, tracks them as they progress,
13      * and automatically advances to the next upload when a preceding one has completed.
14      * @module uploader-queue
15      */     
17     var Lang = Y.Lang,
18         Bind = Y.bind,
19         Win = Y.config.win,
20         queuedFiles,
21         numberOfUploads,        
22         currentUploadedByteValues,
23         currentFiles,
24         totalBytesUploaded,
25         totalBytes;
27     /**
28      * This class manages a queue of files to be uploaded to the server.
29      * @class Uploader.Queue
30      * @extends Base
31      * @constructor
32      * @param {Object} config Configuration object
33      */
34     var UploaderQueue = function(o) {
35         this.queuedFiles = [];
36         this.uploadRetries = {};
37         this.numberOfUploads = 0;
38         this.currentUploadedByteValues = {};
39         this.currentFiles = {};
40         this.totalBytesUploaded = 0;
41         this.totalBytes = 0;      
42   
43         UploaderQueue.superclass.constructor.apply(this, arguments);
44     };
47     Y.extend(UploaderQueue, Y.Base, {
49        /**
50         * Stored value of the current queue state
51         * @property _currentState
52         * @type {String}
53         * @protected
54         * @default UploaderQueue.STOPPED
55         */
56         _currentState: UploaderQueue.STOPPED,
58        /**
59         * Construction logic executed during UploaderQueue instantiation.
60         *
61         * @method initializer
62         * @protected
63         */
64         initializer : function (cfg) {
66         },
68        /**
69         * Handles and retransmits upload start event.
70         * 
71         * @method _uploadStartHandler
72         * @param event The event dispatched during the upload process.
73         * @private
74         */
75         _uploadStartHandler : function (event) {
76            var updatedEvent = event;
77            updatedEvent.file = event.target;
78            updatedEvent.originEvent = event;
79            
80            this.fire("uploadstart", updatedEvent);          
81         },
83        /**
84         * Handles and retransmits upload error event.
85         * 
86         * @method _uploadErrorHandler
87         * @param event The event dispatched during the upload process.
88         * @private
89         */
90         _uploadErrorHandler : function (event) {
91            var errorAction = this.get("errorAction");
92            var updatedEvent = event;
93            updatedEvent.file = event.target;
94            updatedEvent.originEvent = event;
96            this.numberOfUploads-=1;
97            delete this.currentFiles[event.target.get("id")];
98            this._detachFileEvents(event.target);
100            event.target.cancelUpload();
102            if (errorAction === UploaderQueue.STOP) {
103              this.pauseUpload();
104            }
106            else if (errorAction === UploaderQueue.RESTART_ASAP) {
107             var fileid = event.target.get("id"),
108                 retries = this.uploadRetries[fileid] || 0;
109             if (retries < this.get("retryCount")) {
110                this.uploadRetries[fileid] = retries + 1;
111                this.addToQueueTop(event.target);
112             }
113                this._startNextFile();
114            }
115            else if (errorAction === UploaderQueue.RESTART_AFTER) {
116             var fileid = event.target.get("id"),
117                 retries = this.uploadRetries[fileid] || 0;
118             if (retries < this.get("retryCount")) {
119                this.uploadRetries[fileid] = retries + 1;
120                this.addToQueueBottom(event.target);
121             }
122               this._startNextFile();
123            }
125            this.fire("uploaderror", updatedEvent);  
126         },
128        /**
129         * Launches the upload of the next file in the queue.
130         * 
131         * @method _startNextFile
132         * @private
133         */
134         _startNextFile : function () {
135           if (this.queuedFiles.length > 0) {
136             var currentFile = this.queuedFiles.shift(),
137                fileId = currentFile.get("id"),
138                parameters = this.get("perFileParameters"),
139                fileParameters = parameters.hasOwnProperty(fileId) ? parameters[fileId] : parameters;
141                this.currentUploadedByteValues[fileId] = 0;
143                currentFile.on("uploadstart", this._uploadStartHandler, this);
144                currentFile.on("uploadprogress", this._uploadProgressHandler, this);
145                currentFile.on("uploadcomplete", this._uploadCompleteHandler, this);
146                currentFile.on("uploaderror", this._uploadErrorHandler, this);
147                currentFile.on("uploadcancel", this._uploadCancelHandler, this);
149                currentFile.set("xhrHeaders", this.get("uploadHeaders"));
150                currentFile.set("xhrWithCredentials", this.get("withCredentials"));
152                currentFile.startUpload(this.get("uploadURL"), fileParameters, this.get("fileFieldName"));
154                this._registerUpload(currentFile);
155           }
156         },
158        /**
159         * Register a new upload process.
160         * 
161         * @method _registerUpload
162         * @private
163         */
164         _registerUpload : function (file) {
165           this.numberOfUploads += 1;
166           this.currentFiles[file.get("id")] = file;
167         },
169        /**
170         * Unregisters a new upload process.
171         * 
172         * @method _unregisterUpload
173         * @private
174         */
175         _unregisterUpload : function (file) {
176           if (this.numberOfUploads > 0) {
177             this.numberOfUploads -=1;
178           }
179           delete this.currentFiles[file.get("id")];
180           delete this.uploadRetries[file.get("id")];
182           this._detachFileEvents(file);
183         },
185         _detachFileEvents : function (file) {
186           file.detach("uploadstart", this._uploadStartHandler);
187           file.detach("uploadprogress", this._uploadProgressHandler);
188           file.detach("uploadcomplete", this._uploadCompleteHandler);
189           file.detach("uploaderror", this._uploadErrorHandler);
190           file.detach("uploadcancel", this._uploadCancelHandler);
191         },
193        /**
194         * Handles and retransmits upload complete event.
195         * 
196         * @method _uploadCompleteHandler
197         * @param event The event dispatched during the upload process.
198         * @private
199         */
200         _uploadCompleteHandler : function (event) {
202            this._unregisterUpload(event.target);
204            this.totalBytesUploaded += event.target.get("size");
205            delete this.currentUploadedByteValues[event.target.get("id")];
208            if (this.queuedFiles.length > 0 && this._currentState === UploaderQueue.UPLOADING) {
209                this._startNextFile();
210            }
211            
212            var updatedEvent = event;
213            updatedEvent.file = event.target;
214            updatedEvent.originEvent = event;
216            var uploadedTotal = this.totalBytesUploaded;
218            Y.each(this.currentUploadedByteValues, function (value) {
219               uploadedTotal += value; 
220            });
221            
222            var percentLoaded = Math.min(100, Math.round(10000*uploadedTotal/this.totalBytes) / 100); 
223            
224            this.fire("totaluploadprogress", {bytesLoaded: uploadedTotal, 
225                                              bytesTotal: this.totalBytes,
226                                              percentLoaded: percentLoaded});
228            this.fire("uploadcomplete", updatedEvent);
230            if (this.queuedFiles.length === 0 && this.numberOfUploads <= 0) {
231                this.fire("alluploadscomplete");
232                this._currentState = UploaderQueue.STOPPED;
233            }
236         },
238        /**
239         * Handles and retransmits upload cancel event.
240         * 
241         * @method _uploadCancelHandler
242         * @param event The event dispatched during the upload process.
243         * @private
244         */
245         _uploadCancelHandler : function (event) {
246           
247           var updatedEvent = event;
248           updatedEvent.originEvent = event;
249           updatedEvent.file = event.target;
251           this.fire("uploadcacel", updatedEvent);
252         },
256        /**
257         * Handles and retransmits upload progress event.
258         * 
259         * @method _uploadProgressHandler
260         * @param event The event dispatched during the upload process.
261         * @private
262         */
263         _uploadProgressHandler : function (event) {
264           
265           this.currentUploadedByteValues[event.target.get("id")] = event.bytesLoaded;
266           
267           var updatedEvent = event;
268           updatedEvent.originEvent = event;
269           updatedEvent.file = event.target;
271           this.fire("uploadprogress", updatedEvent);
272           
273           var uploadedTotal = this.totalBytesUploaded;
275           Y.each(this.currentUploadedByteValues, function (value) {
276              uploadedTotal += value; 
277           });
278           
279           var percentLoaded = Math.min(100, Math.round(10000*uploadedTotal/this.totalBytes) / 100);
281           this.fire("totaluploadprogress", {bytesLoaded: uploadedTotal, 
282                                             bytesTotal: this.totalBytes,
283                                             percentLoaded: percentLoaded});
284         },
286        /**
287         * Starts uploading the queued up file list.
288         * 
289         * @method startUpload
290         */
291         startUpload: function() {
292            
293            this.queuedFiles = this.get("fileList").slice(0);
294            this.numberOfUploads = 0;
295            this.currentUploadedByteValues = {};
296            this.currentFiles = {};
297            this.totalBytesUploaded = 0;
298            
299            this._currentState = UploaderQueue.UPLOADING;
301            while (this.numberOfUploads < this.get("simUploads") && this.queuedFiles.length > 0) {
302                 this._startNextFile();
303            }
304         },
306        /**
307         * Pauses the upload process. The ongoing file uploads
308         * will complete after this method is called, but no
309         * new ones will be launched.
310         * 
311         * @method pauseUpload
312         */
313         pauseUpload: function () {
314             this._currentState = UploaderQueue.STOPPED;
315         },
317        /**
318         * Restarts a paused upload process.
319         * 
320         * @method restartUpload
321         */
322         restartUpload: function () {
323             this._currentState = UploaderQueue.UPLOADING;
324             while (this.numberOfUploads < this.get("simUploads")) {
325                this._startNextFile();
326             }
327         },
329        /**
330         * If a particular file is stuck in an ongoing upload without
331         * any progress events, this method allows to force its reupload
332         * by cancelling its upload and immediately relaunching it.
333         * 
334         * @method forceReupload
335         * @param file {Y.File} The file to force reupload on.
336         */
337         forceReupload : function (file) {
338             var id = file.get("id");
339             if (this.currentFiles.hasOwnProperty(id)) {
340               file.cancelUpload();
341               this._unregisterUpload(file);
342               this.addToQueueTop(file);
343               this._startNextFile();
344             }
345         },
347        /**
348         * Add a new file to the top of the queue (the upload will be
349         * launched as soon as the current number of uploading files
350         * drops below the maximum permissible value).
351         * 
352         * @method addToQueueTop
353         * @param file {Y.File} The file to add to the top of the queue.
354         */
355         addToQueueTop: function (file) {
356             this.queuedFiles.unshift(file);
357         },
359        /**
360         * Add a new file to the bottom of the queue (the upload will be
361         * launched after all the other queued files are uploaded.)
362         * 
363         * @method addToQueueBottom
364         * @param file {Y.File} The file to add to the bottom of the queue.
365         */
366         addToQueueBottom: function (file) {
367             this.queuedFiles.push(file);
368         },
370        /**
371         * Cancels a specific file's upload. If no argument is passed,
372         * all ongoing uploads are cancelled and the upload process is
373         * stopped.
374         * 
375         * @method cancelUpload
376         * @param file {Y.File} An optional parameter - the file whose upload
377         * should be cancelled.
378         */
379         cancelUpload: function (file) {
381           if (file) {
382             var id = file.get("id");
383             if (this.currentFiles[id]) {
384               this.currentFiles[id].cancelUpload();
385               this._unregisterUpload(this.currentFiles[id]);
386               if (this._currentState === UploaderQueue.UPLOADING) {
387                 this._startNextFile();
388               }
389             }
390             else {
391               for (var i = 0, len = this.queuedFiles.length; i < len; i++) {
392                 if (this.queuedFiles[i].get("id") === id) {
393                   this.queuedFiles.splice(i, 1);
394                   break;
395                 }
396               }
397             }
398           }
399           else {
400             for (var fid in this.currentFiles) {
401               this.currentFiles[fid].cancelUpload();
402               this._unregisterUpload(this.currentFiles[fid]);
403             }
405             this.currentUploadedByteValues = {};
406             this.currentFiles = {};
407             this.totalBytesUploaded = 0;
408             this.fire("alluploadscancelled");
409             this._currentState = UploaderQueue.STOPPED;
410           }
411         }
412     }, 
414     {
415       /** 
416        * Static constant for the value of the `errorAction` attribute:
417        * prescribes the queue to continue uploading files in case of 
418        * an error.
419        * @property CONTINUE
420        * @readOnly
421        * @type {String}
422        * @static
423        */
424         CONTINUE: "continue",
426       /** 
427        * Static constant for the value of the `errorAction` attribute:
428        * prescribes the queue to stop uploading files in case of 
429        * an error.
430        * @property STOP
431        * @readOnly
432        * @type {String}
433        * @static
434        */
435         STOP: "stop",
437       /** 
438        * Static constant for the value of the `errorAction` attribute:
439        * prescribes the queue to restart a file upload immediately in case of 
440        * an error.
441        * @property RESTART_ASAP
442        * @readOnly
443        * @type {String}
444        * @static
445        */
446         RESTART_ASAP: "restartasap",
448       /** 
449        * Static constant for the value of the `errorAction` attribute:
450        * prescribes the queue to restart an errored out file upload after 
451        * other files have finished uploading.
452        * @property RESTART_AFTER
453        * @readOnly
454        * @type {String}
455        * @static
456        */
457         RESTART_AFTER: "restartafter",
459       /** 
460        * Static constant for the value of the `_currentState` property:
461        * implies that the queue is currently not uploading files.
462        * @property STOPPED
463        * @readOnly
464        * @type {String}
465        * @static
466        */
467         STOPPED: "stopped",
469       /** 
470        * Static constant for the value of the `_currentState` property:
471        * implies that the queue is currently uploading files.
472        * @property UPLOADING
473        * @readOnly
474        * @type {String}
475        * @static
476        */
477         UPLOADING: "uploading",
479        /**
480         * The identity of the class.
481         *
482         * @property NAME
483         * @type String
484         * @default 'uploaderqueue'
485         * @readOnly
486         * @protected
487         * @static
488         */
489         NAME: 'uploaderqueue',
491        /**
492         * Static property used to define the default attribute configuration of
493         * the class.
494         *
495         * @property ATTRS
496         * @type {Object}
497         * @protected
498         * @static
499         */
500         ATTRS: {
501        
502           /**
503            * Maximum number of simultaneous uploads; must be in the
504            * range between 1 and 5. The value of `2` is default. It
505            * is recommended that this value does not exceed 3.
506            * @property simUploads
507            * @type Number
508            * @default 2
509            */
510            simUploads: {
511                value: 2,
512                validator: function (val, name) {
513                    return (val >= 1 && val <= 5);
514                }
515            },
516    
517           /**
518            * The action to take in case of error. The valid values for this attribute are: 
519            * `Y.Uploader.Queue.CONTINUE` (the upload process should continue on other files, 
520            * ignoring the error), `Y.Uploader.Queue.STOP` (the upload process 
521            * should stop completely), `Y.Uploader.Queue.RESTART_ASAP` (the upload 
522            * should restart immediately on the errored out file and continue as planned), or
523            * Y.Uploader.Queue.RESTART_AFTER (the upload of the errored out file should restart
524            * after all other files have uploaded)
525            * @property errorAction
526            * @type String
527            * @default Y.Uploader.Queue.CONTINUE
528            */
529            errorAction: {
530                value: "continue",
531                validator: function (val, name) {
532                    return (val === UploaderQueue.CONTINUE || val === UploaderQueue.STOP || val === UploaderQueue.RESTART_ASAP || val === UploaderQueue.RESTART_AFTER);
533                }
534            },
536           /**
537            * The total number of bytes that has been uploaded.
538            * @property bytesUploaded
539            * @type Number
540            */   
541            bytesUploaded: {
542                readOnly: true,
543                value: 0
544            },
545    
546           /**
547            * The total number of bytes in the queue.
548            * @property bytesTotal
549            * @type Number
550            */ 
551            bytesTotal: {
552                readOnly: true,
553                value: 0
554            },
556           /**
557            * The queue file list. This file list should only be modified
558            * before the upload has been started; modifying it after starting
559            * the upload has no effect, and `addToQueueTop` or `addToQueueBottom` methods
560            * should be used instead.
561            * @property fileList
562            * @type Number
563            */    
564            fileList: {
565                value: [],
566                lazyAdd: false,
567                setter: function (val) {
568                    var newValue = val;
569                    Y.Array.each(newValue, function (value) {
570                        this.totalBytes += value.get("size");
571                    }, this);
572     
573                    return val;
574                }   
575            },
577           /**
578            * A String specifying what should be the POST field name for the file
579            * content in the upload request.
580            *
581            * @attribute fileFieldName
582            * @type {String}
583            * @default Filedata
584            */   
585            fileFieldName: {
586               value: "Filedata"
587            },
589           /**
590            * The URL to POST the file upload requests to.
591            *
592            * @attribute uploadURL
593            * @type {String}
594            * @default ""
595            */  
596            uploadURL: {
597              value: ""
598            },
600           /**
601            * Additional HTTP headers that should be included
602            * in the upload request. Due to Flash Player security
603            * restrictions, this attribute is only honored in the
604            * HTML5 Uploader.
605            *
606            * @attribute uploadHeaders
607            * @type {Object}
608            * @default {}
609            */  
610            uploadHeaders: {
611              value: {}
612            },
614           /**
615            * A Boolean that specifies whether the file should be
616            * uploaded with the appropriate user credentials for the
617            * domain. Due to Flash Player security restrictions, this
618            * attribute is only honored in the HTML5 Uploader.
619            *
620            * @attribute withCredentials
621            * @type {Boolean}
622            * @default true
623            */  
624            withCredentials: {
625              value: true
626            },
629           /**
630            * An object, keyed by `fileId`, containing sets of key-value pairs
631            * that should be passed as POST variables along with each corresponding
632            * file.
633            *
634            * @attribute perFileParameters
635            * @type {Object}
636            * @default {}
637            */   
638            perFileParameters: {
639              value: {}
640            },
642           /**
643            * The number of times to try re-uploading a file that failed to upload before
644            * cancelling its upload.
645            *
646            * @attribute retryCount
647            * @type {Number}
648            * @default 3
649            */ 
650            retryCount: {
651              value: 3
652            }
654         }
655     });
658     Y.namespace('Uploader');
659     Y.Uploader.Queue = UploaderQueue;
661 }, '3.7.1', {"requires": ["base"]});