Subversion Repositories Integrator Subversion

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 espaco 1
/**
2
 * Plupload - multi-runtime File Uploader
3
 * v2.1.2
4
 *
5
 * Copyright 2013, Moxiecode Systems AB
6
 * Released under GPL License.
7
 *
8
 * License: http://www.plupload.com/license
9
 * Contributing: http://www.plupload.com/contributing
10
 *
11
 * Date: 2014-05-14
12
 */
13
/**
14
 * Plupload.js
15
 *
16
 * Copyright 2013, Moxiecode Systems AB
17
 * Released under GPL License.
18
 *
19
 * License: http://www.plupload.com/license
20
 * Contributing: http://www.plupload.com/contributing
21
 */
22
 
23
/*global mOxie:true */
24
 
25
;(function(window, o, undef) {
26
 
27
var delay = window.setTimeout
28
, fileFilters = {}
29
;
30
 
31
// convert plupload features to caps acceptable by mOxie
32
function normalizeCaps(settings) {             
33
        var features = settings.required_features, caps = {};
34
 
35
        function resolve(feature, value, strict) {
36
                // Feature notation is deprecated, use caps (this thing here is required for backward compatibility)
37
                var map = {
38
                        chunks: 'slice_blob',
39
                        jpgresize: 'send_binary_string',
40
                        pngresize: 'send_binary_string',
41
                        progress: 'report_upload_progress',
42
                        multi_selection: 'select_multiple',
43
                        dragdrop: 'drag_and_drop',
44
                        drop_element: 'drag_and_drop',
45
                        headers: 'send_custom_headers',
46
                        urlstream_upload: 'send_binary_string',
47
                        canSendBinary: 'send_binary',
48
                        triggerDialog: 'summon_file_dialog'
49
                };
50
 
51
                if (map[feature]) {
52
                        caps[map[feature]] = value;
53
                } else if (!strict) {
54
                        caps[feature] = value;
55
                }
56
        }
57
 
58
        if (typeof(features) === 'string') {
59
                plupload.each(features.split(/\s*,\s*/), function(feature) {
60
                        resolve(feature, true);
61
                });
62
        } else if (typeof(features) === 'object') {
63
                plupload.each(features, function(value, feature) {
64
                        resolve(feature, value);
65
                });
66
        } else if (features === true) {
67
                // check settings for required features
68
                if (settings.chunk_size > 0) {
69
                        caps.slice_blob = true;
70
                }
71
 
72
                if (settings.resize.enabled || !settings.multipart) {
73
                        caps.send_binary_string = true;
74
                }
75
 
76
                plupload.each(settings, function(value, feature) {
77
                        resolve(feature, !!value, true); // strict check
78
                });
79
        }
80
 
81
        return caps;
82
}
83
 
84
/**
85
 * @module plupload    
86
 * @static
87
 */
88
var plupload = {
89
        /**
90
         * Plupload version will be replaced on build.
91
         *
92
         * @property VERSION
93
         * @for Plupload
94
         * @static
95
         * @final
96
         */
97
        VERSION : '2.1.2',
98
 
99
        /**
100
         * Inital state of the queue and also the state ones it's finished all it's uploads.
101
         *
102
         * @property STOPPED
103
         * @static
104
         * @final
105
         */
106
        STOPPED : 1,
107
 
108
        /**
109
         * Upload process is running
110
         *
111
         * @property STARTED
112
         * @static
113
         * @final
114
         */
115
        STARTED : 2,
116
 
117
        /**
118
         * File is queued for upload
119
         *
120
         * @property QUEUED
121
         * @static
122
         * @final
123
         */
124
        QUEUED : 1,
125
 
126
        /**
127
         * File is being uploaded
128
         *
129
         * @property UPLOADING
130
         * @static
131
         * @final
132
         */
133
        UPLOADING : 2,
134
 
135
        /**
136
         * File has failed to be uploaded
137
         *
138
         * @property FAILED
139
         * @static
140
         * @final
141
         */
142
        FAILED : 4,
143
 
144
        /**
145
         * File has been uploaded successfully
146
         *
147
         * @property DONE
148
         * @static
149
         * @final
150
         */
151
        DONE : 5,
152
 
153
        // Error constants used by the Error event
154
 
155
        /**
156
         * Generic error for example if an exception is thrown inside Silverlight.
157
         *
158
         * @property GENERIC_ERROR
159
         * @static
160
         * @final
161
         */
162
        GENERIC_ERROR : -100,
163
 
164
        /**
165
         * HTTP transport error. For example if the server produces a HTTP status other than 200.
166
         *
167
         * @property HTTP_ERROR
168
         * @static
169
         * @final
170
         */
171
        HTTP_ERROR : -200,
172
 
173
        /**
174
         * Generic I/O error. For example if it wasn't possible to open the file stream on local machine.
175
         *
176
         * @property IO_ERROR
177
         * @static
178
         * @final
179
         */
180
        IO_ERROR : -300,
181
 
182
        /**
183
         * @property SECURITY_ERROR
184
         * @static
185
         * @final
186
         */
187
        SECURITY_ERROR : -400,
188
 
189
        /**
190
         * Initialization error. Will be triggered if no runtime was initialized.
191
         *
192
         * @property INIT_ERROR
193
         * @static
194
         * @final
195
         */
196
        INIT_ERROR : -500,
197
 
198
        /**
199
         * File size error. If the user selects a file that is too large it will be blocked and an error of this type will be triggered.
200
         *
201
         * @property FILE_SIZE_ERROR
202
         * @static
203
         * @final
204
         */
205
        FILE_SIZE_ERROR : -600,
206
 
207
        /**
208
         * File extension error. If the user selects a file that isn't valid according to the filters setting.
209
         *
210
         * @property FILE_EXTENSION_ERROR
211
         * @static
212
         * @final
213
         */
214
        FILE_EXTENSION_ERROR : -601,
215
 
216
        /**
217
         * Duplicate file error. If prevent_duplicates is set to true and user selects the same file again.
218
         *
219
         * @property FILE_DUPLICATE_ERROR
220
         * @static
221
         * @final
222
         */
223
        FILE_DUPLICATE_ERROR : -602,
224
 
225
        /**
226
         * Runtime will try to detect if image is proper one. Otherwise will throw this error.
227
         *
228
         * @property IMAGE_FORMAT_ERROR
229
         * @static
230
         * @final
231
         */
232
        IMAGE_FORMAT_ERROR : -700,
233
 
234
        /**
235
         * While working on files runtime may run out of memory and will throw this error.
236
         *
237
         * @since 2.1.2
238
         * @property MEMORY_ERROR
239
         * @static
240
         * @final
241
         */
242
        MEMORY_ERROR : -701,
243
 
244
        /**
245
         * Each runtime has an upper limit on a dimension of the image it can handle. If bigger, will throw this error.
246
         *
247
         * @property IMAGE_DIMENSIONS_ERROR
248
         * @static
249
         * @final
250
         */
251
        IMAGE_DIMENSIONS_ERROR : -702,
252
 
253
        /**
254
         * Mime type lookup table.
255
         *
256
         * @property mimeTypes
257
         * @type Object
258
         * @final
259
         */
260
        mimeTypes : o.mimes,
261
 
262
        /**
263
         * In some cases sniffing is the only way around :(
264
         */
265
        ua: o.ua,
266
 
267
        /**
268
         * Gets the true type of the built-in object (better version of typeof).
269
         * @credits Angus Croll (http://javascriptweblog.wordpress.com/)
270
         *
271
         * @method typeOf
272
         * @static
273
         * @param {Object} o Object to check.
274
         * @return {String} Object [[Class]]
275
         */
276
        typeOf: o.typeOf,
277
 
278
        /**
279
         * Extends the specified object with another object.
280
         *
281
         * @method extend
282
         * @static
283
         * @param {Object} target Object to extend.
284
         * @param {Object..} obj Multiple objects to extend with.
285
         * @return {Object} Same as target, the extended object.
286
         */
287
        extend : o.extend,
288
 
289
        /**
290
         * Generates an unique ID. This is 99.99% unique since it takes the current time and 5 random numbers.
291
         * The only way a user would be able to get the same ID is if the two persons at the same exact milisecond manages
292
         * to get 5 the same random numbers between 0-65535 it also uses a counter so each call will be guaranteed to be page unique.
293
         * It's more probable for the earth to be hit with an ansteriod. You can also if you want to be 100% sure set the plupload.guidPrefix property
294
         * to an user unique key.
295
         *
296
         * @method guid
297
         * @static
298
         * @return {String} Virtually unique id.
299
         */
300
        guid : o.guid,
301
 
302
        /**
303
         * Get array of DOM Elements by their ids.
304
         *
305
         * @method get
306
         * @for Utils
307
         * @param {String} id Identifier of the DOM Element
308
         * @return {Array}
309
        */
310
        get : function get(ids) {
311
                var els = [], el;
312
 
313
                if (o.typeOf(ids) !== 'array') {
314
                        ids = [ids];
315
                }
316
 
317
                var i = ids.length;
318
                while (i--) {
319
                        el = o.get(ids[i]);
320
                        if (el) {
321
                                els.push(el);
322
                        }
323
                }
324
 
325
                return els.length ? els : null;
326
        },
327
 
328
        /**
329
         * Executes the callback function for each item in array/object. If you return false in the
330
         * callback it will break the loop.
331
         *
332
         * @method each
333
         * @static
334
         * @param {Object} obj Object to iterate.
335
         * @param {function} callback Callback function to execute for each item.
336
         */
337
        each : o.each,
338
 
339
        /**
340
         * Returns the absolute x, y position of an Element. The position will be returned in a object with x, y fields.
341
         *
342
         * @method getPos
343
         * @static
344
         * @param {Element} node HTML element or element id to get x, y position from.
345
         * @param {Element} root Optional root element to stop calculations at.
346
         * @return {object} Absolute position of the specified element object with x, y fields.
347
         */
348
        getPos : o.getPos,
349
 
350
        /**
351
         * Returns the size of the specified node in pixels.
352
         *
353
         * @method getSize
354
         * @static
355
         * @param {Node} node Node to get the size of.
356
         * @return {Object} Object with a w and h property.
357
         */
358
        getSize : o.getSize,
359
 
360
        /**
361
         * Encodes the specified string.
362
         *
363
         * @method xmlEncode
364
         * @static
365
         * @param {String} s String to encode.
366
         * @return {String} Encoded string.
367
         */
368
        xmlEncode : function(str) {
369
                var xmlEncodeChars = {'<' : 'lt', '>' : 'gt', '&' : 'amp', '"' : 'quot', '\'' : '#39'}, xmlEncodeRegExp = /[<>&\"\']/g;
370
 
371
                return str ? ('' + str).replace(xmlEncodeRegExp, function(chr) {
372
                        return xmlEncodeChars[chr] ? '&' + xmlEncodeChars[chr] + ';' : chr;
373
                }) : str;
374
        },
375
 
376
        /**
377
         * Forces anything into an array.
378
         *
379
         * @method toArray
380
         * @static
381
         * @param {Object} obj Object with length field.
382
         * @return {Array} Array object containing all items.
383
         */
384
        toArray : o.toArray,
385
 
386
        /**
387
         * Find an element in array and return it's index if present, otherwise return -1.
388
         *
389
         * @method inArray
390
         * @static
391
         * @param {mixed} needle Element to find
392
         * @param {Array} array
393
         * @return {Int} Index of the element, or -1 if not found
394
         */
395
        inArray : o.inArray,
396
 
397
        /**
398
         * Extends the language pack object with new items.
399
         *
400
         * @method addI18n
401
         * @static
402
         * @param {Object} pack Language pack items to add.
403
         * @return {Object} Extended language pack object.
404
         */
405
        addI18n : o.addI18n,
406
 
407
        /**
408
         * Translates the specified string by checking for the english string in the language pack lookup.
409
         *
410
         * @method translate
411
         * @static
412
         * @param {String} str String to look for.
413
         * @return {String} Translated string or the input string if it wasn't found.
414
         */
415
        translate : o.translate,
416
 
417
        /**
418
         * Checks if object is empty.
419
         *
420
         * @method isEmptyObj
421
         * @static
422
         * @param {Object} obj Object to check.
423
         * @return {Boolean}
424
         */
425
        isEmptyObj : o.isEmptyObj,
426
 
427
        /**
428
         * Checks if specified DOM element has specified class.
429
         *
430
         * @method hasClass
431
         * @static
432
         * @param {Object} obj DOM element like object to add handler to.
433
         * @param {String} name Class name
434
         */
435
        hasClass : o.hasClass,
436
 
437
        /**
438
         * Adds specified className to specified DOM element.
439
         *
440
         * @method addClass
441
         * @static
442
         * @param {Object} obj DOM element like object to add handler to.
443
         * @param {String} name Class name
444
         */
445
        addClass : o.addClass,
446
 
447
        /**
448
         * Removes specified className from specified DOM element.
449
         *
450
         * @method removeClass
451
         * @static
452
         * @param {Object} obj DOM element like object to add handler to.
453
         * @param {String} name Class name
454
         */
455
        removeClass : o.removeClass,
456
 
457
        /**
458
         * Returns a given computed style of a DOM element.
459
         *
460
         * @method getStyle
461
         * @static
462
         * @param {Object} obj DOM element like object.
463
         * @param {String} name Style you want to get from the DOM element
464
         */
465
        getStyle : o.getStyle,
466
 
467
        /**
468
         * Adds an event handler to the specified object and store reference to the handler
469
         * in objects internal Plupload registry (@see removeEvent).
470
         *
471
         * @method addEvent
472
         * @static
473
         * @param {Object} obj DOM element like object to add handler to.
474
         * @param {String} name Name to add event listener to.
475
         * @param {Function} callback Function to call when event occurs.
476
         * @param {String} (optional) key that might be used to add specifity to the event record.
477
         */
478
        addEvent : o.addEvent,
479
 
480
        /**
481
         * Remove event handler from the specified object. If third argument (callback)
482
         * is not specified remove all events with the specified name.
483
         *
484
         * @method removeEvent
485
         * @static
486
         * @param {Object} obj DOM element to remove event listener(s) from.
487
         * @param {String} name Name of event listener to remove.
488
         * @param {Function|String} (optional) might be a callback or unique key to match.
489
         */
490
        removeEvent: o.removeEvent,
491
 
492
        /**
493
         * Remove all kind of events from the specified object
494
         *
495
         * @method removeAllEvents
496
         * @static
497
         * @param {Object} obj DOM element to remove event listeners from.
498
         * @param {String} (optional) unique key to match, when removing events.
499
         */
500
        removeAllEvents: o.removeAllEvents,
501
 
502
        /**
503
         * Cleans the specified name from national characters (diacritics). The result will be a name with only a-z, 0-9 and _.
504
         *
505
         * @method cleanName
506
         * @static
507
         * @param {String} s String to clean up.
508
         * @return {String} Cleaned string.
509
         */
510
        cleanName : function(name) {
511
                var i, lookup;
512
 
513
                // Replace diacritics
514
                lookup = [
515
                        /[\300-\306]/g, 'A', /[\340-\346]/g, 'a',
516
                        /\307/g, 'C', /\347/g, 'c',
517
                        /[\310-\313]/g, 'E', /[\350-\353]/g, 'e',
518
                        /[\314-\317]/g, 'I', /[\354-\357]/g, 'i',
519
                        /\321/g, 'N', /\361/g, 'n',
520
                        /[\322-\330]/g, 'O', /[\362-\370]/g, 'o',
521
                        /[\331-\334]/g, 'U', /[\371-\374]/g, 'u'
522
                ];
523
 
524
                for (i = 0; i < lookup.length; i += 2) {
525
                        name = name.replace(lookup[i], lookup[i + 1]);
526
                }
527
 
528
                // Replace whitespace
529
                name = name.replace(/\s+/g, '_');
530
 
531
                // Remove anything else
532
                name = name.replace(/[^a-z0-9_\-\.]+/gi, '');
533
 
534
                return name;
535
        },
536
 
537
        /**
538
         * Builds a full url out of a base URL and an object with items to append as query string items.
539
         *
540
         * @method buildUrl
541
         * @static
542
         * @param {String} url Base URL to append query string items to.
543
         * @param {Object} items Name/value object to serialize as a querystring.
544
         * @return {String} String with url + serialized query string items.
545
         */
546
        buildUrl : function(url, items) {
547
                var query = '';
548
 
549
                plupload.each(items, function(value, name) {
550
                        query += (query ? '&' : '') + encodeURIComponent(name) + '=' + encodeURIComponent(value);
551
                });
552
 
553
                if (query) {
554
                        url += (url.indexOf('?') > 0 ? '&' : '?') + query;
555
                }
556
 
557
                return url;
558
        },
559
 
560
        /**
561
         * Formats the specified number as a size string for example 1024 becomes 1 KB.
562
         *
563
         * @method formatSize
564
         * @static
565
         * @param {Number} size Size to format as string.
566
         * @return {String} Formatted size string.
567
         */
568
        formatSize : function(size) {
569
 
570
                if (size === undef || /\D/.test(size)) {
571
                        return plupload.translate('N/A');
572
                }
573
 
574
                function round(num, precision) {
575
                        return Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision);
576
                }
577
 
578
                var boundary = Math.pow(1024, 4);
579
 
580
                // TB
581
                if (size > boundary) {
582
                        return round(size / boundary, 1) + " " + plupload.translate('tb');
583
                }
584
 
585
                // GB
586
                if (size > (boundary/=1024)) {
587
                        return round(size / boundary, 1) + " " + plupload.translate('gb');
588
                }
589
 
590
                // MB
591
                if (size > (boundary/=1024)) {
592
                        return round(size / boundary, 1) + " " + plupload.translate('mb');
593
                }
594
 
595
                // KB
596
                if (size > 1024) {
597
                        return Math.round(size / 1024) + " " + plupload.translate('kb');
598
                }
599
 
600
                return size + " " + plupload.translate('b');
601
        },
602
 
603
 
604
        /**
605
         * Parses the specified size string into a byte value. For example 10kb becomes 10240.
606
         *
607
         * @method parseSize
608
         * @static
609
         * @param {String|Number} size String to parse or number to just pass through.
610
         * @return {Number} Size in bytes.
611
         */
612
        parseSize : o.parseSizeStr,
613
 
614
 
615
        /**
616
         * A way to predict what runtime will be choosen in the current environment with the
617
         * specified settings.
618
         *
619
         * @method predictRuntime
620
         * @static
621
         * @param {Object|String} config Plupload settings to check
622
         * @param {String} [runtimes] Comma-separated list of runtimes to check against
623
         * @return {String} Type of compatible runtime
624
         */
625
        predictRuntime : function(config, runtimes) {
626
                var up, runtime;
627
 
628
                up = new plupload.Uploader(config);
629
                runtime = o.Runtime.thatCan(up.getOption().required_features, runtimes || config.runtimes);
630
                up.destroy();
631
                return runtime;
632
        },
633
 
634
        /**
635
         * Registers a filter that will be executed for each file added to the queue.
636
         * If callback returns false, file will not be added.
637
         *
638
         * Callback receives two arguments: a value for the filter as it was specified in settings.filters
639
         * and a file to be filtered. Callback is executed in the context of uploader instance.
640
         *
641
         * @method addFileFilter
642
         * @static
643
         * @param {String} name Name of the filter by which it can be referenced in settings.filters
644
         * @param {String} cb Callback - the actual routine that every added file must pass
645
         */
646
        addFileFilter: function(name, cb) {
647
                fileFilters[name] = cb;
648
        }
649
};
650
 
651
 
652
plupload.addFileFilter('mime_types', function(filters, file, cb) {
653
        if (filters.length && !filters.regexp.test(file.name)) {
654
                this.trigger('Error', {
655
                        code : plupload.FILE_EXTENSION_ERROR,
656
                        message : plupload.translate('File extension error.'),
657
                        file : file
658
                });
659
                cb(false);
660
        } else {
661
                cb(true);
662
        }
663
});
664
 
665
 
666
plupload.addFileFilter('max_file_size', function(maxSize, file, cb) {
667
        var undef;
668
 
669
        maxSize = plupload.parseSize(maxSize);
670
 
671
        // Invalid file size
672
        if (file.size !== undef && maxSize && file.size > maxSize) {
673
                this.trigger('Error', {
674
                        code : plupload.FILE_SIZE_ERROR,
675
                        message : plupload.translate('File size error.'),
676
                        file : file
677
                });
678
                cb(false);
679
        } else {
680
                cb(true);
681
        }
682
});
683
 
684
 
685
plupload.addFileFilter('prevent_duplicates', function(value, file, cb) {
686
        if (value) {
687
                var ii = this.files.length;
688
                while (ii--) {
689
                        // Compare by name and size (size might be 0 or undefined, but still equivalent for both)
690
                        if (file.name === this.files[ii].name && file.size === this.files[ii].size) {
691
                                this.trigger('Error', {
692
                                        code : plupload.FILE_DUPLICATE_ERROR,
693
                                        message : plupload.translate('Duplicate file error.'),
694
                                        file : file
695
                                });
696
                                cb(false);
697
                                return;
698
                        }
699
                }
700
        }
701
        cb(true);
702
});
703
 
704
 
705
/**
706
@class Uploader
707
@constructor
708
 
709
@param {Object} settings For detailed information about each option check documentation.
710
        @param {String|DOMElement} settings.browse_button id of the DOM element or DOM element itself to use as file dialog trigger.
711
        @param {String} settings.url URL of the server-side upload handler.
712
        @param {Number|String} [settings.chunk_size=0] Chunk size in bytes to slice the file into. Shorcuts with b, kb, mb, gb, tb suffixes also supported. `e.g. 204800 or "204800b" or "200kb"`. By default - disabled.
713
        @param {Boolean} [settings.send_chunk_number=true] Whether to send chunks and chunk numbers, or total and offset bytes.
714
        @param {String} [settings.container] id of the DOM element to use as a container for uploader structures. Defaults to document.body.
715
        @param {String|DOMElement} [settings.drop_element] id of the DOM element or DOM element itself to use as a drop zone for Drag-n-Drop.
716
        @param {String} [settings.file_data_name="file"] Name for the file field in Multipart formated message.
717
        @param {Object} [settings.filters={}] Set of file type filters.
718
                @param {Array} [settings.filters.mime_types=[]] List of file types to accept, each one defined by title and list of extensions. `e.g. {title : "Image files", extensions : "jpg,jpeg,gif,png"}`. Dispatches `plupload.FILE_EXTENSION_ERROR`
719
                @param {String|Number} [settings.filters.max_file_size=0] Maximum file size that the user can pick, in bytes. Optionally supports b, kb, mb, gb, tb suffixes. `e.g. "10mb" or "1gb"`. By default - not set. Dispatches `plupload.FILE_SIZE_ERROR`.
720
                @param {Boolean} [settings.filters.prevent_duplicates=false] Do not let duplicates into the queue. Dispatches `plupload.FILE_DUPLICATE_ERROR`.
721
        @param {String} [settings.flash_swf_url] URL of the Flash swf.
722
        @param {Object} [settings.headers] Custom headers to send with the upload. Hash of name/value pairs.
723
        @param {Number} [settings.max_retries=0] How many times to retry the chunk or file, before triggering Error event.
724
        @param {Boolean} [settings.multipart=true] Whether to send file and additional parameters as Multipart formated message.
725
        @param {Object} [settings.multipart_params] Hash of key/value pairs to send with every file upload.
726
        @param {Boolean} [settings.multi_selection=true] Enable ability to select multiple files at once in file dialog.
727
        @param {String|Object} [settings.required_features] Either comma-separated list or hash of required features that chosen runtime should absolutely possess.
728
        @param {Object} [settings.resize] Enable resizng of images on client-side. Applies to `image/jpeg` and `image/png` only. `e.g. {width : 200, height : 200, quality : 90, crop: true}`
729
                @param {Number} [settings.resize.width] If image is bigger, it will be resized.
730
                @param {Number} [settings.resize.height] If image is bigger, it will be resized.
731
                @param {Number} [settings.resize.quality=90] Compression quality for jpegs (1-100).
732
                @param {Boolean} [settings.resize.crop=false] Whether to crop images to exact dimensions. By default they will be resized proportionally.
733
        @param {String} [settings.runtimes="html5,flash,silverlight,html4"] Comma separated list of runtimes, that Plupload will try in turn, moving to the next if previous fails.
734
        @param {String} [settings.silverlight_xap_url] URL of the Silverlight xap.
735
        @param {Boolean} [settings.unique_names=false] If true will generate unique filenames for uploaded files.
736
        @param {Boolean} [settings.send_file_name=true] Whether to send file name as additional argument - 'name' (required for chunked uploads and some other cases where file name cannot be sent via normal ways).
737
*/
738
plupload.Uploader = function(options) {
739
        /**
740
         * Fires when the current RunTime has been initialized.
741
         *
742
         * @event Init
743
         * @param {plupload.Uploader} uploader Uploader instance sending the event.
744
         */
745
 
746
        /**
747
         * Fires after the init event incase you need to perform actions there.
748
         *
749
         * @event PostInit
750
         * @param {plupload.Uploader} uploader Uploader instance sending the event.
751
         */
752
 
753
        /**
754
         * Fires when the option is changed in via uploader.setOption().
755
         *
756
         * @event OptionChanged
757
         * @since 2.1
758
         * @param {plupload.Uploader} uploader Uploader instance sending the event.
759
         * @param {String} name Name of the option that was changed
760
         * @param {Mixed} value New value for the specified option
761
         * @param {Mixed} oldValue Previous value of the option
762
         */
763
 
764
        /**
765
         * Fires when the silverlight/flash or other shim needs to move.
766
         *
767
         * @event Refresh
768
         * @param {plupload.Uploader} uploader Uploader instance sending the event.
769
         */
770
 
771
        /**
772
         * Fires when the overall state is being changed for the upload queue.
773
         *
774
         * @event StateChanged
775
         * @param {plupload.Uploader} uploader Uploader instance sending the event.
776
         */
777
 
778
        /**
779
         * Fires when browse_button is clicked and browse dialog shows.
780
         *
781
         * @event Browse
782
         * @since 2.1.2
783
         * @param {plupload.Uploader} uploader Uploader instance sending the event.
784
         */    
785
 
786
        /**
787
         * Fires for every filtered file before it is added to the queue.
788
         *
789
         * @event FileFiltered
790
         * @since 2.1
791
         * @param {plupload.Uploader} uploader Uploader instance sending the event.
792
         * @param {plupload.File} file Another file that has to be added to the queue.
793
         */
794
 
795
        /**
796
         * Fires when the file queue is changed. In other words when files are added/removed to the files array of the uploader instance.
797
         *
798
         * @event QueueChanged
799
         * @param {plupload.Uploader} uploader Uploader instance sending the event.
800
         */
801
 
802
        /**
803
         * Fires after files were filtered and added to the queue.
804
         *
805
         * @event FilesAdded
806
         * @param {plupload.Uploader} uploader Uploader instance sending the event.
807
         * @param {Array} files Array of file objects that were added to queue by the user.
808
         */
809
 
810
        /**
811
         * Fires when file is removed from the queue.
812
         *
813
         * @event FilesRemoved
814
         * @param {plupload.Uploader} uploader Uploader instance sending the event.
815
         * @param {Array} files Array of files that got removed.
816
         */
817
 
818
        /**
819
         * Fires when just before a file is uploaded. This event enables you to override settings
820
         * on the uploader instance before the file is uploaded.
821
         *
822
         * @event BeforeUpload
823
         * @param {plupload.Uploader} uploader Uploader instance sending the event.
824
         * @param {plupload.File} file File to be uploaded.
825
         */
826
 
827
        /**
828
         * Fires when a file is to be uploaded by the runtime.
829
         *
830
         * @event UploadFile
831
         * @param {plupload.Uploader} uploader Uploader instance sending the event.
832
         * @param {plupload.File} file File to be uploaded.
833
         */
834
 
835
        /**
836
         * Fires while a file is being uploaded. Use this event to update the current file upload progress.
837
         *
838
         * @event UploadProgress
839
         * @param {plupload.Uploader} uploader Uploader instance sending the event.
840
         * @param {plupload.File} file File that is currently being uploaded.
841
         */    
842
 
843
        /**
844
         * Fires when file chunk is uploaded.
845
         *
846
         * @event ChunkUploaded
847
         * @param {plupload.Uploader} uploader Uploader instance sending the event.
848
         * @param {plupload.File} file File that the chunk was uploaded for.
849
         * @param {Object} response Object with response properties.
850
         */
851
 
852
        /**
853
         * Fires when a file is successfully uploaded.
854
         *
855
         * @event FileUploaded
856
         * @param {plupload.Uploader} uploader Uploader instance sending the event.
857
         * @param {plupload.File} file File that was uploaded.
858
         * @param {Object} response Object with response properties.
859
         */
860
 
861
        /**
862
         * Fires when all files in a queue are uploaded.
863
         *
864
         * @event UploadComplete
865
         * @param {plupload.Uploader} uploader Uploader instance sending the event.
866
         * @param {Array} files Array of file objects that was added to queue/selected by the user.
867
         */
868
 
869
        /**
870
         * Fires when a error occurs.
871
         *
872
         * @event Error
873
         * @param {plupload.Uploader} uploader Uploader instance sending the event.
874
         * @param {Object} error Contains code, message and sometimes file and other details.
875
         */
876
 
877
        /**
878
         * Fires when destroy method is called.
879
         *
880
         * @event Destroy
881
         * @param {plupload.Uploader} uploader Uploader instance sending the event.
882
         */
883
        var uid = plupload.guid()
884
        , settings
885
        , files = []
886
        , preferred_caps = {}
887
        , fileInputs = []
888
        , fileDrops = []
889
        , startTime
890
        , total
891
        , disabled = false
892
        , xhr
893
        ;
894
 
895
 
896
        // Private methods
897
        function uploadNext() {
898
                var file, count = 0, i;
899
 
900
                if (this.state == plupload.STARTED) {
901
                        // Find first QUEUED file
902
                        for (i = 0; i < files.length; i++) {
903
                                if (!file && files[i].status == plupload.QUEUED) {
904
                                        file = files[i];
905
                                        if (this.trigger("BeforeUpload", file)) {
906
                                                file.status = plupload.UPLOADING;
907
                                                this.trigger("UploadFile", file);
908
                                        }
909
                                } else {
910
                                        count++;
911
                                }
912
                        }
913
 
914
                        // All files are DONE or FAILED
915
                        if (count == files.length) {
916
                                if (this.state !== plupload.STOPPED) {
917
                                        this.state = plupload.STOPPED;
918
                                        this.trigger("StateChanged");
919
                                }
920
                                this.trigger("UploadComplete", files);
921
                        }
922
                }
923
        }
924
 
925
 
926
        function calcFile(file) {
927
                file.percent = file.size > 0 ? Math.ceil(file.loaded / file.size * 100) : 100;
928
                calc();
929
        }
930
 
931
 
932
        function calc() {
933
                var i, file;
934
 
935
                // Reset stats
936
                total.reset();
937
 
938
                // Check status, size, loaded etc on all files
939
                for (i = 0; i < files.length; i++) {
940
                        file = files[i];
941
 
942
                        if (file.size !== undef) {
943
                                // We calculate totals based on original file size
944
                                total.size += file.origSize;
945
 
946
                                // Since we cannot predict file size after resize, we do opposite and
947
                                // interpolate loaded amount to match magnitude of total
948
                                total.loaded += file.loaded * file.origSize / file.size;
949
                        } else {
950
                                total.size = undef;
951
                        }
952
 
953
                        if (file.status == plupload.DONE) {
954
                                total.uploaded++;
955
                        } else if (file.status == plupload.FAILED) {
956
                                total.failed++;
957
                        } else {
958
                                total.queued++;
959
                        }
960
                }
961
 
962
                // If we couldn't calculate a total file size then use the number of files to calc percent
963
                if (total.size === undef) {
964
                        total.percent = files.length > 0 ? Math.ceil(total.uploaded / files.length * 100) : 0;
965
                } else {
966
                        total.bytesPerSec = Math.ceil(total.loaded / ((+new Date() - startTime || 1) / 1000.0));
967
                        total.percent = total.size > 0 ? Math.ceil(total.loaded / total.size * 100) : 0;
968
                }
969
        }
970
 
971
 
972
        function getRUID() {
973
                var ctrl = fileInputs[0] || fileDrops[0];
974
                if (ctrl) {
975
                        return ctrl.getRuntime().uid;
976
                }
977
                return false;
978
        }
979
 
980
 
981
        function runtimeCan(file, cap) {
982
                if (file.ruid) {
983
                        var info = o.Runtime.getInfo(file.ruid);
984
                        if (info) {
985
                                return info.can(cap);
986
                        }
987
                }
988
                return false;
989
        }
990
 
991
 
992
        function bindEventListeners() {
993
                this.bind('FilesAdded FilesRemoved', function(up) {
994
                        up.trigger('QueueChanged');
995
                        up.refresh();
996
                });
997
 
998
                this.bind('CancelUpload', onCancelUpload);
999
 
1000
                this.bind('BeforeUpload', onBeforeUpload);
1001
 
1002
                this.bind('UploadFile', onUploadFile);
1003
 
1004
                this.bind('UploadProgress', onUploadProgress);
1005
 
1006
                this.bind('StateChanged', onStateChanged);
1007
 
1008
                this.bind('QueueChanged', calc);
1009
 
1010
                this.bind('Error', onError);
1011
 
1012
                this.bind('FileUploaded', onFileUploaded);
1013
 
1014
                this.bind('Destroy', onDestroy);
1015
        }
1016
 
1017
 
1018
        function initControls(settings, cb) {
1019
                var self = this, inited = 0, queue = [];
1020
 
1021
                // common settings
1022
                var options = {
1023
                        runtime_order: settings.runtimes,
1024
                        required_caps: settings.required_features,
1025
                        preferred_caps: preferred_caps,
1026
                        swf_url: settings.flash_swf_url,
1027
                        xap_url: settings.silverlight_xap_url
1028
                };
1029
 
1030
                // add runtime specific options if any
1031
                plupload.each(settings.runtimes.split(/\s*,\s*/), function(runtime) {
1032
                        if (settings[runtime]) {
1033
                                options[runtime] = settings[runtime];
1034
                        }
1035
                });
1036
 
1037
                // initialize file pickers - there can be many
1038
                if (settings.browse_button) {
1039
                        plupload.each(settings.browse_button, function(el) {
1040
                                queue.push(function(cb) {
1041
                                        var fileInput = new o.FileInput(plupload.extend({}, options, {
1042
                                                accept: settings.filters.mime_types,
1043
                                                name: settings.file_data_name,
1044
                                                multiple: settings.multi_selection,
1045
                                                container: settings.container,
1046
                                                browse_button: el
1047
                                        }));
1048
 
1049
                                        fileInput.onready = function() {
1050
                                                var info = o.Runtime.getInfo(this.ruid);
1051
 
1052
                                                // for backward compatibility
1053
                                                o.extend(self.features, {
1054
                                                        chunks: info.can('slice_blob'),
1055
                                                        multipart: info.can('send_multipart'),
1056
                                                        multi_selection: info.can('select_multiple')
1057
                                                });
1058
 
1059
                                                inited++;
1060
                                                fileInputs.push(this);
1061
                                                cb();
1062
                                        };
1063
 
1064
                                        fileInput.onchange = function() {
1065
                                                self.addFile(this.files);
1066
                                        };
1067
 
1068
                                        fileInput.bind('mouseenter mouseleave mousedown mouseup', function(e) {
1069
                                                if (!disabled) {
1070
                                                        if (settings.browse_button_hover) {
1071
                                                                if ('mouseenter' === e.type) {
1072
                                                                        o.addClass(el, settings.browse_button_hover);
1073
                                                                } else if ('mouseleave' === e.type) {
1074
                                                                        o.removeClass(el, settings.browse_button_hover);
1075
                                                                }
1076
                                                        }
1077
 
1078
                                                        if (settings.browse_button_active) {
1079
                                                                if ('mousedown' === e.type) {
1080
                                                                        o.addClass(el, settings.browse_button_active);
1081
                                                                } else if ('mouseup' === e.type) {
1082
                                                                        o.removeClass(el, settings.browse_button_active);
1083
                                                                }
1084
                                                        }
1085
                                                }
1086
                                        });
1087
 
1088
                                        fileInput.bind('mousedown', function() {
1089
                                                self.trigger('Browse');
1090
                                        });
1091
 
1092
                                        fileInput.bind('error runtimeerror', function() {
1093
                                                fileInput = null;
1094
                                                cb();
1095
                                        });
1096
 
1097
                                        fileInput.init();
1098
                                });
1099
                        });
1100
                }
1101
 
1102
                // initialize drop zones
1103
                if (settings.drop_element) {
1104
                        plupload.each(settings.drop_element, function(el) {
1105
                                queue.push(function(cb) {
1106
                                        var fileDrop = new o.FileDrop(plupload.extend({}, options, {
1107
                                                drop_zone: el
1108
                                        }));
1109
 
1110
                                        fileDrop.onready = function() {
1111
                                                var info = o.Runtime.getInfo(this.ruid);
1112
 
1113
                                                self.features.dragdrop = info.can('drag_and_drop'); // for backward compatibility
1114
 
1115
                                                inited++;
1116
                                                fileDrops.push(this);
1117
                                                cb();
1118
                                        };
1119
 
1120
                                        fileDrop.ondrop = function() {
1121
                                                self.addFile(this.files);
1122
                                        };
1123
 
1124
                                        fileDrop.bind('error runtimeerror', function() {
1125
                                                fileDrop = null;
1126
                                                cb();
1127
                                        });
1128
 
1129
                                        fileDrop.init();
1130
                                });
1131
                        });
1132
                }
1133
 
1134
 
1135
                o.inSeries(queue, function() {
1136
                        if (typeof(cb) === 'function') {
1137
                                cb(inited);
1138
                        }
1139
                });
1140
        }
1141
 
1142
 
1143
        function resizeImage(blob, params, cb) {
1144
                var img = new o.Image();
1145
 
1146
                try {
1147
                        img.onload = function() {
1148
                                // no manipulation required if...
1149
                                if (params.width > this.width &&
1150
                                        params.height > this.height &&
1151
                                        params.quality === undef &&
1152
                                        params.preserve_headers &&
1153
                                        !params.crop
1154
                                ) {
1155
                                        this.destroy();
1156
                                        return cb(blob);
1157
                                }
1158
                                // otherwise downsize
1159
                                img.downsize(params.width, params.height, params.crop, params.preserve_headers);
1160
                        };
1161
 
1162
                        img.onresize = function() {
1163
                                cb(this.getAsBlob(blob.type, params.quality));
1164
                                this.destroy();
1165
                        };
1166
 
1167
                        img.onerror = function() {
1168
                                cb(blob);
1169
                        };
1170
 
1171
                        img.load(blob);
1172
                } catch(ex) {
1173
                        cb(blob);
1174
                }
1175
        }
1176
 
1177
 
1178
        function setOption(option, value, init) {
1179
                var self = this, reinitRequired = false;
1180
 
1181
                function _setOption(option, value, init) {
1182
                        var oldValue = settings[option];
1183
 
1184
                        switch (option) {
1185
                                case 'max_file_size':
1186
                                        if (option === 'max_file_size') {
1187
                                                settings.max_file_size = settings.filters.max_file_size = value;
1188
                                        }
1189
                                        break;
1190
 
1191
                                case 'chunk_size':
1192
                                        if (value = plupload.parseSize(value)) {
1193
                                                settings[option] = value;
1194
                                                settings.send_file_name = true;
1195
                                        }
1196
                                        break;
1197
 
1198
                                case 'multipart':
1199
                                        settings[option] = value;
1200
                                        if (!value) {
1201
                                                settings.send_file_name = true;
1202
                                        }
1203
                                        break;
1204
 
1205
                                case 'unique_names':
1206
                                        settings[option] = value;
1207
                                        if (value) {
1208
                                                settings.send_file_name = true;
1209
                                        }
1210
                                        break;
1211
 
1212
                                case 'filters':
1213
                                        // for sake of backward compatibility
1214
                                        if (plupload.typeOf(value) === 'array') {
1215
                                                value = {
1216
                                                        mime_types: value
1217
                                                };
1218
                                        }
1219
 
1220
                                        if (init) {
1221
                                                plupload.extend(settings.filters, value);
1222
                                        } else {
1223
                                                settings.filters = value;
1224
                                        }
1225
 
1226
                                        // if file format filters are being updated, regenerate the matching expressions
1227
                                        if (value.mime_types) {
1228
                                                settings.filters.mime_types.regexp = (function(filters) {
1229
                                                        var extensionsRegExp = [];
1230
 
1231
                                                        plupload.each(filters, function(filter) {
1232
                                                                plupload.each(filter.extensions.split(/,/), function(ext) {
1233
                                                                        if (/^\s*\*\s*$/.test(ext)) {
1234
                                                                                extensionsRegExp.push('\\.*');
1235
                                                                        } else {
1236
                                                                                extensionsRegExp.push('\\.' + ext.replace(new RegExp('[' + ('/^$.*+?|()[]{}\\'.replace(/./g, '\\$&')) + ']', 'g'), '\\$&'));
1237
                                                                        }
1238
                                                                });
1239
                                                        });
1240
 
1241
                                                        return new RegExp('(' + extensionsRegExp.join('|') + ')$', 'i');
1242
                                                }(settings.filters.mime_types));
1243
                                        }
1244
                                        break;
1245
 
1246
                                case 'resize':
1247
                                        if (init) {
1248
                                                plupload.extend(settings.resize, value, {
1249
                                                        enabled: true
1250
                                                });
1251
                                        } else {
1252
                                                settings.resize = value;
1253
                                        }
1254
                                        break;
1255
 
1256
                                case 'prevent_duplicates':
1257
                                        settings.prevent_duplicates = settings.filters.prevent_duplicates = !!value;
1258
                                        break;
1259
 
1260
                                case 'browse_button':
1261
                                case 'drop_element':
1262
                                                value = plupload.get(value);
1263
 
1264
                                case 'container':
1265
                                case 'runtimes':
1266
                                case 'multi_selection':
1267
                                case 'flash_swf_url':
1268
                                case 'silverlight_xap_url':
1269
                                        settings[option] = value;
1270
                                        if (!init) {
1271
                                                reinitRequired = true;
1272
                                        }
1273
                                        break;
1274
 
1275
                                default:
1276
                                        settings[option] = value;
1277
                        }
1278
 
1279
                        if (!init) {
1280
                                self.trigger('OptionChanged', option, value, oldValue);
1281
                        }
1282
                }
1283
 
1284
                if (typeof(option) === 'object') {
1285
                        plupload.each(option, function(value, option) {
1286
                                _setOption(option, value, init);
1287
                        });
1288
                } else {
1289
                        _setOption(option, value, init);
1290
                }
1291
 
1292
                if (init) {
1293
                        // Normalize the list of required capabilities
1294
                        settings.required_features = normalizeCaps(plupload.extend({}, settings));
1295
 
1296
                        // Come up with the list of capabilities that can affect default mode in a multi-mode runtimes
1297
                        preferred_caps = normalizeCaps(plupload.extend({}, settings, {
1298
                                required_features: true
1299
                        }));
1300
                } else if (reinitRequired) {
1301
                        self.trigger('Destroy');
1302
 
1303
                        initControls.call(self, settings, function(inited) {
1304
                                if (inited) {
1305
                                        self.runtime = o.Runtime.getInfo(getRUID()).type;
1306
                                        self.trigger('Init', { runtime: self.runtime });
1307
                                        self.trigger('PostInit');
1308
                                } else {
1309
                                        self.trigger('Error', {
1310
                                                code : plupload.INIT_ERROR,
1311
                                                message : plupload.translate('Init error.')
1312
                                        });
1313
                                }
1314
                        });
1315
                }
1316
        }
1317
 
1318
 
1319
        // Internal event handlers
1320
        function onBeforeUpload(up, file) {
1321
                // Generate unique target filenames
1322
                if (up.settings.unique_names) {
1323
                        var matches = file.name.match(/\.([^.]+)$/), ext = "part";
1324
                        if (matches) {
1325
                                ext = matches[1];
1326
                        }
1327
                        file.target_name = file.id + '.' + ext;
1328
                }
1329
        }
1330
 
1331
 
1332
        function onUploadFile(up, file) {
1333
                var url = up.settings.url
1334
                , chunkSize = up.settings.chunk_size
1335
                , retries = up.settings.max_retries
1336
                , features = up.features
1337
                , offset = 0
1338
                , blob
1339
                ;
1340
 
1341
                // make sure we start at a predictable offset
1342
                if (file.loaded) {
1343
                        offset = file.loaded = chunkSize ? chunkSize * Math.floor(file.loaded / chunkSize) : 0;
1344
                }
1345
 
1346
                function handleError() {
1347
                        if (retries-- > 0) {
1348
                                delay(uploadNextChunk, 1000);
1349
                        } else {
1350
                                file.loaded = offset; // reset all progress
1351
 
1352
                                up.trigger('Error', {
1353
                                        code : plupload.HTTP_ERROR,
1354
                                        message : plupload.translate('HTTP Error.'),
1355
                                        file : file,
1356
                                        response : xhr.responseText,
1357
                                        status : xhr.status,
1358
                                        responseHeaders: xhr.getAllResponseHeaders()
1359
                                });
1360
                        }
1361
                }
1362
 
1363
                function uploadNextChunk() {
1364
                        var chunkBlob, formData, args = {}, curChunkSize;
1365
 
1366
                        // make sure that file wasn't cancelled and upload is not stopped in general
1367
                        if (file.status !== plupload.UPLOADING || up.state === plupload.STOPPED) {
1368
                                return;
1369
                        }
1370
 
1371
                        // send additional 'name' parameter only if required
1372
                        if (up.settings.send_file_name) {
1373
                                args.name = file.target_name || file.name;
1374
                        }
1375
 
1376
                        if (chunkSize && features.chunks && blob.size > chunkSize) { // blob will be of type string if it was loaded in memory 
1377
                                curChunkSize = Math.min(chunkSize, blob.size - offset);
1378
                                chunkBlob = blob.slice(offset, offset + curChunkSize);
1379
                        } else {
1380
                                curChunkSize = blob.size;
1381
                                chunkBlob = blob;
1382
                        }
1383
 
1384
                        // If chunking is enabled add corresponding args, no matter if file is bigger than chunk or smaller
1385
                        if (chunkSize && features.chunks) {
1386
                                // Setup query string arguments
1387
                                if (up.settings.send_chunk_number) {
1388
                                        args.chunk = Math.ceil(offset / chunkSize);
1389
                                        args.chunks = Math.ceil(blob.size / chunkSize);
1390
                                } else { // keep support for experimental chunk format, just in case
1391
                                        args.offset = offset;
1392
                                        args.total = blob.size;
1393
                                }
1394
                        }
1395
 
1396
                        xhr = new o.XMLHttpRequest();
1397
 
1398
                        // Do we have upload progress support
1399
                        if (xhr.upload) {
1400
                                xhr.upload.onprogress = function(e) {
1401
                                        file.loaded = Math.min(file.size, offset + e.loaded);
1402
                                        up.trigger('UploadProgress', file);
1403
                                };
1404
                        }
1405
 
1406
                        xhr.onload = function() {
1407
                                // check if upload made itself through
1408
                                if (xhr.status >= 400) {
1409
                                        handleError();
1410
                                        return;
1411
                                }
1412
 
1413
                                retries = up.settings.max_retries; // reset the counter
1414
 
1415
                                // Handle chunk response
1416
                                if (curChunkSize < blob.size) {
1417
                                        chunkBlob.destroy();
1418
 
1419
                                        offset += curChunkSize;
1420
                                        file.loaded = Math.min(offset, blob.size);
1421
 
1422
                                        up.trigger('ChunkUploaded', file, {
1423
                                                offset : file.loaded,
1424
                                                total : blob.size,
1425
                                                response : xhr.responseText,
1426
                                                status : xhr.status,
1427
                                                responseHeaders: xhr.getAllResponseHeaders()
1428
                                        });
1429
 
1430
                                        // stock Android browser doesn't fire upload progress events, but in chunking mode we can fake them
1431
                                        if (o.Env.browser === 'Android Browser') {
1432
                                                // doesn't harm in general, but is not required anywhere else
1433
                                                up.trigger('UploadProgress', file);
1434
                                        }
1435
                                } else {
1436
                                        file.loaded = file.size;
1437
                                }
1438
 
1439
                                chunkBlob = formData = null; // Free memory
1440
 
1441
                                // Check if file is uploaded
1442
                                if (!offset || offset >= blob.size) {
1443
                                        // If file was modified, destory the copy
1444
                                        if (file.size != file.origSize) {
1445
                                                blob.destroy();
1446
                                                blob = null;
1447
                                        }
1448
 
1449
                                        up.trigger('UploadProgress', file);
1450
 
1451
                                        file.status = plupload.DONE;
1452
 
1453
                                        up.trigger('FileUploaded', file, {
1454
                                                response : xhr.responseText,
1455
                                                status : xhr.status,
1456
                                                responseHeaders: xhr.getAllResponseHeaders()
1457
                                        });
1458
                                } else {
1459
                                        // Still chunks left
1460
                                        delay(uploadNextChunk, 1); // run detached, otherwise event handlers interfere
1461
                                }
1462
                        };
1463
 
1464
                        xhr.onerror = function() {
1465
                                handleError();
1466
                        };
1467
 
1468
                        xhr.onloadend = function() {
1469
                                this.destroy();
1470
                                xhr = null;
1471
                        };
1472
 
1473
                        // Build multipart request
1474
                        if (up.settings.multipart && features.multipart) {
1475
                                xhr.open("post", url, true);
1476
 
1477
                                // Set custom headers
1478
                                plupload.each(up.settings.headers, function(value, name) {
1479
                                        xhr.setRequestHeader(name, value);
1480
                                });
1481
 
1482
                                formData = new o.FormData();
1483
 
1484
                                // Add multipart params
1485
                                plupload.each(plupload.extend(args, up.settings.multipart_params), function(value, name) {
1486
                                        formData.append(name, value);
1487
                                });
1488
 
1489
                                // Add file and send it
1490
                                formData.append(up.settings.file_data_name, chunkBlob);
1491
                                xhr.send(formData, {
1492
                                        runtime_order: up.settings.runtimes,
1493
                                        required_caps: up.settings.required_features,
1494
                                        preferred_caps: preferred_caps,
1495
                                        swf_url: up.settings.flash_swf_url,
1496
                                        xap_url: up.settings.silverlight_xap_url
1497
                                });
1498
                        } else {
1499
                                // if no multipart, send as binary stream
1500
                                url = plupload.buildUrl(up.settings.url, plupload.extend(args, up.settings.multipart_params));
1501
 
1502
                                xhr.open("post", url, true);
1503
 
1504
                                xhr.setRequestHeader('Content-Type', 'application/octet-stream'); // Binary stream header
1505
 
1506
                                // Set custom headers
1507
                                plupload.each(up.settings.headers, function(value, name) {
1508
                                        xhr.setRequestHeader(name, value);
1509
                                });
1510
 
1511
                                xhr.send(chunkBlob, {
1512
                                        runtime_order: up.settings.runtimes,
1513
                                        required_caps: up.settings.required_features,
1514
                                        preferred_caps: preferred_caps,
1515
                                        swf_url: up.settings.flash_swf_url,
1516
                                        xap_url: up.settings.silverlight_xap_url
1517
                                });
1518
                        }
1519
                }
1520
 
1521
                blob = file.getSource();
1522
 
1523
                // Start uploading chunks
1524
                if (up.settings.resize.enabled && runtimeCan(blob, 'send_binary_string') && !!~o.inArray(blob.type, ['image/jpeg', 'image/png'])) {
1525
                        // Resize if required
1526
                        resizeImage.call(this, blob, up.settings.resize, function(resizedBlob) {
1527
                                blob = resizedBlob;
1528
                                file.size = resizedBlob.size;
1529
                                uploadNextChunk();
1530
                        });
1531
                } else {
1532
                        uploadNextChunk();
1533
                }
1534
        }
1535
 
1536
 
1537
        function onUploadProgress(up, file) {
1538
                calcFile(file);
1539
        }
1540
 
1541
 
1542
        function onStateChanged(up) {
1543
                if (up.state == plupload.STARTED) {
1544
                        // Get start time to calculate bps
1545
                        startTime = (+new Date());
1546
                } else if (up.state == plupload.STOPPED) {
1547
                        // Reset currently uploading files
1548
                        for (var i = up.files.length - 1; i >= 0; i--) {
1549
                                if (up.files[i].status == plupload.UPLOADING) {
1550
                                        up.files[i].status = plupload.QUEUED;
1551
                                        calc();
1552
                                }
1553
                        }
1554
                }
1555
        }
1556
 
1557
 
1558
        function onCancelUpload() {
1559
                if (xhr) {
1560
                        xhr.abort();
1561
                }
1562
        }
1563
 
1564
 
1565
        function onFileUploaded(up) {
1566
                calc();
1567
 
1568
                // Upload next file but detach it from the error event
1569
                // since other custom listeners might want to stop the queue
1570
                delay(function() {
1571
                        uploadNext.call(up);
1572
                }, 1);
1573
        }
1574
 
1575
 
1576
        function onError(up, err) {
1577
                if (err.code === plupload.INIT_ERROR) {
1578
                        up.destroy();
1579
                }
1580
                // Set failed status if an error occured on a file
1581
                else if (err.file) {
1582
                        err.file.status = plupload.FAILED;
1583
                        calcFile(err.file);
1584
 
1585
                        // Upload next file but detach it from the error event
1586
                        // since other custom listeners might want to stop the queue
1587
                        if (up.state == plupload.STARTED) { // upload in progress
1588
                                up.trigger('CancelUpload');
1589
                                delay(function() {
1590
                                        uploadNext.call(up);
1591
                                }, 1);
1592
                        }
1593
                }
1594
        }
1595
 
1596
 
1597
        function onDestroy(up) {
1598
                up.stop();
1599
 
1600
                // Purge the queue
1601
                plupload.each(files, function(file) {
1602
                        file.destroy();
1603
                });
1604
                files = [];
1605
 
1606
                if (fileInputs.length) {
1607
                        plupload.each(fileInputs, function(fileInput) {
1608
                                fileInput.destroy();
1609
                        });
1610
                        fileInputs = [];
1611
                }
1612
 
1613
                if (fileDrops.length) {
1614
                        plupload.each(fileDrops, function(fileDrop) {
1615
                                fileDrop.destroy();
1616
                        });
1617
                        fileDrops = [];
1618
                }
1619
 
1620
                preferred_caps = {};
1621
                disabled = false;
1622
                startTime = xhr = null;
1623
                total.reset();
1624
        }
1625
 
1626
 
1627
        // Default settings
1628
        settings = {
1629
                runtimes: o.Runtime.order,
1630
                max_retries: 0,
1631
                chunk_size: 0,
1632
                multipart: true,
1633
                multi_selection: true,
1634
                file_data_name: 'file',
1635
                flash_swf_url: 'js/Moxie.swf',
1636
                silverlight_xap_url: 'js/Moxie.xap',
1637
                filters: {
1638
                        mime_types: [],
1639
                        prevent_duplicates: false,
1640
                        max_file_size: 0
1641
                },
1642
                resize: {
1643
                        enabled: false,
1644
                        preserve_headers: true,
1645
                        crop: false
1646
                },
1647
                send_file_name: true,
1648
                send_chunk_number: true
1649
        };
1650
 
1651
 
1652
        setOption.call(this, options, null, true);
1653
 
1654
        // Inital total state
1655
        total = new plupload.QueueProgress();
1656
 
1657
        // Add public methods
1658
        plupload.extend(this, {
1659
 
1660
                /**
1661
                 * Unique id for the Uploader instance.
1662
                 *
1663
                 * @property id
1664
                 * @type String
1665
                 */
1666
                id : uid,
1667
                uid : uid, // mOxie uses this to differentiate between event targets
1668
 
1669
                /**
1670
                 * Current state of the total uploading progress. This one can either be plupload.STARTED or plupload.STOPPED.
1671
                 * These states are controlled by the stop/start methods. The default value is STOPPED.
1672
                 *
1673
                 * @property state
1674
                 * @type Number
1675
                 */
1676
                state : plupload.STOPPED,
1677
 
1678
                /**
1679
                 * Map of features that are available for the uploader runtime. Features will be filled
1680
                 * before the init event is called, these features can then be used to alter the UI for the end user.
1681
                 * Some of the current features that might be in this map is: dragdrop, chunks, jpgresize, pngresize.
1682
                 *
1683
                 * @property features
1684
                 * @type Object
1685
                 */
1686
                features : {},
1687
 
1688
                /**
1689
                 * Current runtime name.
1690
                 *
1691
                 * @property runtime
1692
                 * @type String
1693
                 */
1694
                runtime : null,
1695
 
1696
                /**
1697
                 * Current upload queue, an array of File instances.
1698
                 *
1699
                 * @property files
1700
                 * @type Array
1701
                 * @see plupload.File
1702
                 */
1703
                files : files,
1704
 
1705
                /**
1706
                 * Object with name/value settings.
1707
                 *
1708
                 * @property settings
1709
                 * @type Object
1710
                 */
1711
                settings : settings,
1712
 
1713
                /**
1714
                 * Total progess information. How many files has been uploaded, total percent etc.
1715
                 *
1716
                 * @property total
1717
                 * @type plupload.QueueProgress
1718
                 */
1719
                total : total,
1720
 
1721
 
1722
                /**
1723
                 * Initializes the Uploader instance and adds internal event listeners.
1724
                 *
1725
                 * @method init
1726
                 */
1727
                init : function() {
1728
                        var self = this;
1729
 
1730
                        if (typeof(settings.preinit) == "function") {
1731
                                settings.preinit(self);
1732
                        } else {
1733
                                plupload.each(settings.preinit, function(func, name) {
1734
                                        self.bind(name, func);
1735
                                });
1736
                        }
1737
 
1738
                        bindEventListeners.call(this);
1739
 
1740
                        // Check for required options
1741
                        if (!settings.browse_button || !settings.url) {
1742
                                this.trigger('Error', {
1743
                                        code : plupload.INIT_ERROR,
1744
                                        message : plupload.translate('Init error.')
1745
                                });
1746
                                return;
1747
                        }
1748
 
1749
                        initControls.call(this, settings, function(inited) {
1750
                                if (typeof(settings.init) == "function") {
1751
                                        settings.init(self);
1752
                                } else {
1753
                                        plupload.each(settings.init, function(func, name) {
1754
                                                self.bind(name, func);
1755
                                        });
1756
                                }
1757
 
1758
                                if (inited) {
1759
                                        self.runtime = o.Runtime.getInfo(getRUID()).type;
1760
                                        self.trigger('Init', { runtime: self.runtime });
1761
                                        self.trigger('PostInit');
1762
                                } else {
1763
                                        self.trigger('Error', {
1764
                                                code : plupload.INIT_ERROR,
1765
                                                message : plupload.translate('Init error.')
1766
                                        });
1767
                                }
1768
                        });
1769
                },
1770
 
1771
                /**
1772
                 * Set the value for the specified option(s).
1773
                 *
1774
                 * @method setOption
1775
                 * @since 2.1
1776
                 * @param {String|Object} option Name of the option to change or the set of key/value pairs
1777
                 * @param {Mixed} [value] Value for the option (is ignored, if first argument is object)
1778
                 */
1779
                setOption: function(option, value) {
1780
                        setOption.call(this, option, value, !this.runtime); // until runtime not set we do not need to reinitialize
1781
                },
1782
 
1783
                /**
1784
                 * Get the value for the specified option or the whole configuration, if not specified.
1785
                 *
1786
                 * @method getOption
1787
                 * @since 2.1
1788
                 * @param {String} [option] Name of the option to get
1789
                 * @return {Mixed} Value for the option or the whole set
1790
                 */
1791
                getOption: function(option) {
1792
                        if (!option) {
1793
                                return settings;
1794
                        }
1795
                        return settings[option];
1796
                },
1797
 
1798
                /**
1799
                 * Refreshes the upload instance by dispatching out a refresh event to all runtimes.
1800
                 * This would for example reposition flash/silverlight shims on the page.
1801
                 *
1802
                 * @method refresh
1803
                 */
1804
                refresh : function() {
1805
                        if (fileInputs.length) {
1806
                                plupload.each(fileInputs, function(fileInput) {
1807
                                        fileInput.trigger('Refresh');
1808
                                });
1809
                        }
1810
                        this.trigger('Refresh');
1811
                },
1812
 
1813
                /**
1814
                 * Starts uploading the queued files.
1815
                 *
1816
                 * @method start
1817
                 */
1818
                start : function() {
1819
                        if (this.state != plupload.STARTED) {
1820
                                this.state = plupload.STARTED;
1821
                                this.trigger('StateChanged');
1822
 
1823
                                uploadNext.call(this);
1824
                        }
1825
                },
1826
 
1827
                /**
1828
                 * Stops the upload of the queued files.
1829
                 *
1830
                 * @method stop
1831
                 */
1832
                stop : function() {
1833
                        if (this.state != plupload.STOPPED) {
1834
                                this.state = plupload.STOPPED;
1835
                                this.trigger('StateChanged');
1836
                                this.trigger('CancelUpload');
1837
                        }
1838
                },
1839
 
1840
 
1841
                /**
1842
                 * Disables/enables browse button on request.
1843
                 *
1844
                 * @method disableBrowse
1845
                 * @param {Boolean} disable Whether to disable or enable (default: true)
1846
                 */
1847
                disableBrowse : function() {
1848
                        disabled = arguments[0] !== undef ? arguments[0] : true;
1849
 
1850
                        if (fileInputs.length) {
1851
                                plupload.each(fileInputs, function(fileInput) {
1852
                                        fileInput.disable(disabled);
1853
                                });
1854
                        }
1855
 
1856
                        this.trigger('DisableBrowse', disabled);
1857
                },
1858
 
1859
                /**
1860
                 * Returns the specified file object by id.
1861
                 *
1862
                 * @method getFile
1863
                 * @param {String} id File id to look for.
1864
                 * @return {plupload.File} File object or undefined if it wasn't found;
1865
                 */
1866
                getFile : function(id) {
1867
                        var i;
1868
                        for (i = files.length - 1; i >= 0; i--) {
1869
                                if (files[i].id === id) {
1870
                                        return files[i];
1871
                                }
1872
                        }
1873
                },
1874
 
1875
                /**
1876
                 * Adds file to the queue programmatically. Can be native file, instance of Plupload.File,
1877
                 * instance of mOxie.File, input[type="file"] element, or array of these. Fires FilesAdded,
1878
                 * if any files were added to the queue. Otherwise nothing happens.
1879
                 *
1880
                 * @method addFile
1881
                 * @since 2.0
1882
                 * @param {plupload.File|mOxie.File|File|Node|Array} file File or files to add to the queue.
1883
                 * @param {String} [fileName] If specified, will be used as a name for the file
1884
                 */
1885
                addFile : function(file, fileName) {
1886
                        var self = this
1887
                        , queue = []
1888
                        , filesAdded = []
1889
                        , ruid
1890
                        ;
1891
 
1892
                        function filterFile(file, cb) {
1893
                                var queue = [];
1894
                                o.each(self.settings.filters, function(rule, name) {
1895
                                        if (fileFilters[name]) {
1896
                                                queue.push(function(cb) {
1897
                                                        fileFilters[name].call(self, rule, file, function(res) {
1898
                                                                cb(!res);
1899
                                                        });
1900
                                                });
1901
                                        }
1902
                                });
1903
                                o.inSeries(queue, cb);
1904
                        }
1905
 
1906
                        /**
1907
                         * @method resolveFile
1908
                         * @private
1909
                         * @param {o.File|o.Blob|plupload.File|File|Blob|input[type="file"]} file
1910
                         */
1911
                        function resolveFile(file) {
1912
                                var type = o.typeOf(file);
1913
 
1914
                                // o.File
1915
                                if (file instanceof o.File) {
1916
                                        if (!file.ruid && !file.isDetached()) {
1917
                                                if (!ruid) { // weird case
1918
                                                        return false;
1919
                                                }
1920
                                                file.ruid = ruid;
1921
                                                file.connectRuntime(ruid);
1922
                                        }
1923
                                        resolveFile(new plupload.File(file));
1924
                                }
1925
                                // o.Blob 
1926
                                else if (file instanceof o.Blob) {
1927
                                        resolveFile(file.getSource());
1928
                                        file.destroy();
1929
                                }
1930
                                // plupload.File - final step for other branches
1931
                                else if (file instanceof plupload.File) {
1932
                                        if (fileName) {
1933
                                                file.name = fileName;
1934
                                        }
1935
 
1936
                                        queue.push(function(cb) {
1937
                                                // run through the internal and user-defined filters, if any
1938
                                                filterFile(file, function(err) {
1939
                                                        if (!err) {
1940
                                                                // make files available for the filters by updating the main queue directly
1941
                                                                files.push(file);
1942
                                                                // collect the files that will be passed to FilesAdded event
1943
                                                                filesAdded.push(file);
1944
 
1945
                                                                self.trigger("FileFiltered", file);
1946
                                                        }
1947
                                                        delay(cb, 1); // do not build up recursions or eventually we might hit the limits
1948
                                                });
1949
                                        });
1950
                                }
1951
                                // native File or blob
1952
                                else if (o.inArray(type, ['file', 'blob']) !== -1) {
1953
                                        resolveFile(new o.File(null, file));
1954
                                }
1955
                                // input[type="file"]
1956
                                else if (type === 'node' && o.typeOf(file.files) === 'filelist') {
1957
                                        // if we are dealing with input[type="file"]
1958
                                        o.each(file.files, resolveFile);
1959
                                }
1960
                                // mixed array of any supported types (see above)
1961
                                else if (type === 'array') {
1962
                                        fileName = null; // should never happen, but unset anyway to avoid funny situations
1963
                                        o.each(file, resolveFile);
1964
                                }
1965
                        }
1966
 
1967
                        ruid = getRUID();
1968
 
1969
                        resolveFile(file);
1970
 
1971
                        if (queue.length) {
1972
                                o.inSeries(queue, function() {
1973
                                        // if any files left after filtration, trigger FilesAdded
1974
                                        if (filesAdded.length) {
1975
                                                self.trigger("FilesAdded", filesAdded);
1976
                                        }
1977
                                });
1978
                        }
1979
                },
1980
 
1981
                /**
1982
                 * Removes a specific file.
1983
                 *
1984
                 * @method removeFile
1985
                 * @param {plupload.File|String} file File to remove from queue.
1986
                 */
1987
                removeFile : function(file) {
1988
                        var id = typeof(file) === 'string' ? file : file.id;
1989
 
1990
                        for (var i = files.length - 1; i >= 0; i--) {
1991
                                if (files[i].id === id) {
1992
                                        return this.splice(i, 1)[0];
1993
                                }
1994
                        }
1995
                },
1996
 
1997
                /**
1998
                 * Removes part of the queue and returns the files removed. This will also trigger the FilesRemoved and QueueChanged events.
1999
                 *
2000
                 * @method splice
2001
                 * @param {Number} start (Optional) Start index to remove from.
2002
                 * @param {Number} length (Optional) Lengh of items to remove.
2003
                 * @return {Array} Array of files that was removed.
2004
                 */
2005
                splice : function(start, length) {
2006
                        // Splice and trigger events
2007
                        var removed = files.splice(start === undef ? 0 : start, length === undef ? files.length : length);
2008
 
2009
                        // if upload is in progress we need to stop it and restart after files are removed
2010
                        var restartRequired = false;
2011
                        if (this.state == plupload.STARTED) { // upload in progress
2012
                                plupload.each(removed, function(file) {
2013
                                        if (file.status === plupload.UPLOADING) {
2014
                                                restartRequired = true; // do not restart, unless file that is being removed is uploading
2015
                                                return false;
2016
                                        }
2017
                                });
2018
 
2019
                                if (restartRequired) {
2020
                                        this.stop();
2021
                                }
2022
                        }
2023
 
2024
                        this.trigger("FilesRemoved", removed);
2025
 
2026
                        // Dispose any resources allocated by those files
2027
                        plupload.each(removed, function(file) {
2028
                                file.destroy();
2029
                        });
2030
 
2031
                        if (restartRequired) {
2032
                                this.start();
2033
                        }
2034
 
2035
                        return removed;
2036
                },
2037
 
2038
                /**
2039
                 * Dispatches the specified event name and it's arguments to all listeners.
2040
                 *
2041
                 *
2042
                 * @method trigger
2043
                 * @param {String} name Event name to fire.
2044
                 * @param {Object..} Multiple arguments to pass along to the listener functions.
2045
                 */
2046
 
2047
                /**
2048
                 * Check whether uploader has any listeners to the specified event.
2049
                 *
2050
                 * @method hasEventListener
2051
                 * @param {String} name Event name to check for.
2052
                 */
2053
 
2054
 
2055
                /**
2056
                 * Adds an event listener by name.
2057
                 *
2058
                 * @method bind
2059
                 * @param {String} name Event name to listen for.
2060
                 * @param {function} func Function to call ones the event gets fired.
2061
                 * @param {Object} scope Optional scope to execute the specified function in.
2062
                 */
2063
                bind : function(name, func, scope) {
2064
                        var self = this;
2065
                        // adapt moxie EventTarget style to Plupload-like
2066
                        plupload.Uploader.prototype.bind.call(this, name, function() {
2067
                                var args = [].slice.call(arguments);
2068
                                args.splice(0, 1, self); // replace event object with uploader instance
2069
                                return func.apply(this, args);
2070
                        }, 0, scope);
2071
                },
2072
 
2073
                /**
2074
                 * Removes the specified event listener.
2075
                 *
2076
                 * @method unbind
2077
                 * @param {String} name Name of event to remove.
2078
                 * @param {function} func Function to remove from listener.
2079
                 */
2080
 
2081
                /**
2082
                 * Removes all event listeners.
2083
                 *
2084
                 * @method unbindAll
2085
                 */
2086
 
2087
 
2088
                /**
2089
                 * Destroys Plupload instance and cleans after itself.
2090
                 *
2091
                 * @method destroy
2092
                 */
2093
                destroy : function() {
2094
                        this.trigger('Destroy');
2095
                        settings = total = null; // purge these exclusively
2096
                        this.unbindAll();
2097
                }
2098
        });
2099
};
2100
 
2101
plupload.Uploader.prototype = o.EventTarget.instance;
2102
 
2103
/**
2104
 * Constructs a new file instance.
2105
 *
2106
 * @class File
2107
 * @constructor
2108
 *
2109
 * @param {Object} file Object containing file properties
2110
 * @param {String} file.name Name of the file.
2111
 * @param {Number} file.size File size.
2112
 */
2113
plupload.File = (function() {
2114
        var filepool = {};
2115
 
2116
        function PluploadFile(file) {
2117
 
2118
                plupload.extend(this, {
2119
 
2120
                        /**
2121
                         * File id this is a globally unique id for the specific file.
2122
                         *
2123
                         * @property id
2124
                         * @type String
2125
                         */
2126
                        id: plupload.guid(),
2127
 
2128
                        /**
2129
                         * File name for example "myfile.gif".
2130
                         *
2131
                         * @property name
2132
                         * @type String
2133
                         */
2134
                        name: file.name || file.fileName,
2135
 
2136
                        /**
2137
                         * File type, `e.g image/jpeg`
2138
                         *
2139
                         * @property type
2140
                         * @type String
2141
                         */
2142
                        type: file.type || '',
2143
 
2144
                        /**
2145
                         * File size in bytes (may change after client-side manupilation).
2146
                         *
2147
                         * @property size
2148
                         * @type Number
2149
                         */
2150
                        size: file.size || file.fileSize,
2151
 
2152
                        /**
2153
                         * Original file size in bytes.
2154
                         *
2155
                         * @property origSize
2156
                         * @type Number
2157
                         */
2158
                        origSize: file.size || file.fileSize,
2159
 
2160
                        /**
2161
                         * Number of bytes uploaded of the files total size.
2162
                         *
2163
                         * @property loaded
2164
                         * @type Number
2165
                         */
2166
                        loaded: 0,
2167
 
2168
                        /**
2169
                         * Number of percentage uploaded of the file.
2170
                         *
2171
                         * @property percent
2172
                         * @type Number
2173
                         */
2174
                        percent: 0,
2175
 
2176
                        /**
2177
                         * Status constant matching the plupload states QUEUED, UPLOADING, FAILED, DONE.
2178
                         *
2179
                         * @property status
2180
                         * @type Number
2181
                         * @see plupload
2182
                         */
2183
                        status: plupload.QUEUED,
2184
 
2185
                        /**
2186
                         * Date of last modification.
2187
                         *
2188
                         * @property lastModifiedDate
2189
                         * @type {String}
2190
                         */
2191
                        lastModifiedDate: file.lastModifiedDate || (new Date()).toLocaleString(), // Thu Aug 23 2012 19:40:00 GMT+0400 (GET)
2192
 
2193
                        /**
2194
                         * Returns native window.File object, when it's available.
2195
                         *
2196
                         * @method getNative
2197
                         * @return {window.File} or null, if plupload.File is of different origin
2198
                         */
2199
                        getNative: function() {
2200
                                var file = this.getSource().getSource();
2201
                                return o.inArray(o.typeOf(file), ['blob', 'file']) !== -1 ? file : null;
2202
                        },
2203
 
2204
                        /**
2205
                         * Returns mOxie.File - unified wrapper object that can be used across runtimes.
2206
                         *
2207
                         * @method getSource
2208
                         * @return {mOxie.File} or null
2209
                         */
2210
                        getSource: function() {
2211
                                if (!filepool[this.id]) {
2212
                                        return null;
2213
                                }
2214
                                return filepool[this.id];
2215
                        },
2216
 
2217
                        /**
2218
                         * Destroys plupload.File object.
2219
                         *
2220
                         * @method destroy
2221
                         */
2222
                        destroy: function() {
2223
                                var src = this.getSource();
2224
                                if (src) {
2225
                                        src.destroy();
2226
                                        delete filepool[this.id];
2227
                                }
2228
                        }
2229
                });
2230
 
2231
                filepool[this.id] = file;
2232
        }
2233
 
2234
        return PluploadFile;
2235
}());
2236
 
2237
 
2238
/**
2239
 * Constructs a queue progress.
2240
 *
2241
 * @class QueueProgress
2242
 * @constructor
2243
 */
2244
 plupload.QueueProgress = function() {
2245
        var self = this; // Setup alias for self to reduce code size when it's compressed
2246
 
2247
        /**
2248
         * Total queue file size.
2249
         *
2250
         * @property size
2251
         * @type Number
2252
         */
2253
        self.size = 0;
2254
 
2255
        /**
2256
         * Total bytes uploaded.
2257
         *
2258
         * @property loaded
2259
         * @type Number
2260
         */
2261
        self.loaded = 0;
2262
 
2263
        /**
2264
         * Number of files uploaded.
2265
         *
2266
         * @property uploaded
2267
         * @type Number
2268
         */
2269
        self.uploaded = 0;
2270
 
2271
        /**
2272
         * Number of files failed to upload.
2273
         *
2274
         * @property failed
2275
         * @type Number
2276
         */
2277
        self.failed = 0;
2278
 
2279
        /**
2280
         * Number of files yet to be uploaded.
2281
         *
2282
         * @property queued
2283
         * @type Number
2284
         */
2285
        self.queued = 0;
2286
 
2287
        /**
2288
         * Total percent of the uploaded bytes.
2289
         *
2290
         * @property percent
2291
         * @type Number
2292
         */
2293
        self.percent = 0;
2294
 
2295
        /**
2296
         * Bytes uploaded per second.
2297
         *
2298
         * @property bytesPerSec
2299
         * @type Number
2300
         */
2301
        self.bytesPerSec = 0;
2302
 
2303
        /**
2304
         * Resets the progress to it's initial values.
2305
         *
2306
         * @method reset
2307
         */
2308
        self.reset = function() {
2309
                self.size = self.loaded = self.uploaded = self.failed = self.queued = self.percent = self.bytesPerSec = 0;
2310
        };
2311
};
2312
 
2313
window.plupload = plupload;
2314
 
2315
}(window, mOxie));