plupload-rails 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +34 -35
  3. data/lib/plupload/rails/version.rb +2 -2
  4. data/vendor/assets/images/jquery.plupload.queue/backgrounds.gif +0 -0
  5. data/vendor/assets/images/jquery.plupload.queue/buttons-disabled.png +0 -0
  6. data/vendor/assets/images/jquery.plupload.queue/buttons.png +0 -0
  7. data/vendor/assets/images/jquery.plupload.queue/delete.gif +0 -0
  8. data/vendor/assets/images/jquery.plupload.queue/done.gif +0 -0
  9. data/vendor/assets/images/jquery.plupload.queue/error.gif +0 -0
  10. data/vendor/assets/images/jquery.plupload.queue/throbber.gif +0 -0
  11. data/vendor/assets/images/jquery.plupload.queue/transp50.png +0 -0
  12. data/vendor/assets/images/jquery.ui.plupload/loading.gif +0 -0
  13. data/vendor/assets/images/jquery.ui.plupload/plupload.png +0 -0
  14. data/vendor/assets/javascripts/jquery.plupload.queue.js +27 -17
  15. data/vendor/assets/javascripts/jquery.ui.plupload.js +308 -217
  16. data/vendor/assets/javascripts/moxie.js +906 -661
  17. data/vendor/assets/javascripts/plupload.dev.js +727 -537
  18. data/vendor/assets/javascripts/plupload/i18n/ar.js +2 -0
  19. data/vendor/assets/javascripts/plupload/i18n/bs.js +1 -0
  20. data/vendor/assets/javascripts/plupload/i18n/ca.js +2 -0
  21. data/vendor/assets/javascripts/plupload/i18n/cs.js +1 -0
  22. data/vendor/assets/javascripts/plupload/i18n/cy.js +1 -0
  23. data/vendor/assets/javascripts/plupload/i18n/da.js +1 -0
  24. data/vendor/assets/javascripts/plupload/i18n/de.js +2 -1
  25. data/vendor/assets/javascripts/plupload/i18n/el.js +1 -0
  26. data/vendor/assets/javascripts/plupload/i18n/en.js +1 -0
  27. data/vendor/assets/javascripts/plupload/i18n/es.js +2 -1
  28. data/vendor/assets/javascripts/plupload/i18n/et.js +1 -0
  29. data/vendor/assets/javascripts/plupload/i18n/fa.js +1 -0
  30. data/vendor/assets/javascripts/plupload/i18n/fi.js +1 -0
  31. data/vendor/assets/javascripts/plupload/i18n/fr.js +2 -1
  32. data/vendor/assets/javascripts/plupload/i18n/he.js +2 -0
  33. data/vendor/assets/javascripts/plupload/i18n/hr.js +1 -0
  34. data/vendor/assets/javascripts/plupload/i18n/hu.js +2 -1
  35. data/vendor/assets/javascripts/plupload/i18n/hy.js +1 -0
  36. data/vendor/assets/javascripts/plupload/i18n/id.js +2 -0
  37. data/vendor/assets/javascripts/plupload/i18n/it.js +1 -0
  38. data/vendor/assets/javascripts/plupload/i18n/ja.js +1 -0
  39. data/vendor/assets/javascripts/plupload/i18n/ka.js +1 -0
  40. data/vendor/assets/javascripts/plupload/i18n/kk.js +2 -0
  41. data/vendor/assets/javascripts/plupload/i18n/ko.js +1 -0
  42. data/vendor/assets/javascripts/plupload/i18n/lt.js +1 -0
  43. data/vendor/assets/javascripts/plupload/i18n/lv.js +2 -1
  44. data/vendor/assets/javascripts/plupload/i18n/nl.js +1 -0
  45. data/vendor/assets/javascripts/plupload/i18n/pl.js +1 -0
  46. data/vendor/assets/javascripts/plupload/i18n/pt_BR.js +1 -0
  47. data/vendor/assets/javascripts/plupload/i18n/ro.js +2 -1
  48. data/vendor/assets/javascripts/plupload/i18n/ru.js +2 -1
  49. data/vendor/assets/javascripts/plupload/i18n/sk.js +1 -0
  50. data/vendor/assets/javascripts/plupload/i18n/sr.js +1 -0
  51. data/vendor/assets/javascripts/plupload/i18n/sv.js +1 -0
  52. data/vendor/assets/javascripts/plupload/i18n/th_TH.js +1 -0
  53. data/vendor/assets/javascripts/plupload/i18n/tr.js +1 -0
  54. data/vendor/assets/javascripts/plupload/i18n/uk_UA.js +1 -0
  55. data/vendor/assets/javascripts/plupload/i18n/zh_CN.js +1 -0
  56. data/vendor/assets/javascripts/plupload/i18n/zh_TW.js +2 -0
  57. data/vendor/assets/misc/Moxie.swf +0 -0
  58. data/vendor/assets/misc/Moxie.xap +0 -0
  59. data/vendor/assets/stylesheets/{jquery.plupload.queue.scss → jquery.plupload.queue.css.erb} +15 -11
  60. data/vendor/assets/stylesheets/{jquery.ui.plupload.scss → jquery.ui.plupload.css.erb} +3 -3
  61. metadata +18 -12
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Plupload - multi-runtime File Uploader
3
- * v2.0.0beta
3
+ * v2.1.1
4
4
  *
5
5
  * Copyright 2013, Moxiecode Systems AB
6
6
  * Released under GPL License.
@@ -8,7 +8,7 @@
8
8
  * License: http://www.plupload.com/license
9
9
  * Contributing: http://www.plupload.com/contributing
10
10
  *
11
- * Date: 2012-11-30
11
+ * Date: 2014-01-16
12
12
  */
13
13
  /**
14
14
  * Plupload.js
@@ -36,12 +36,10 @@ function normalizeCaps(settings) {
36
36
  // Feature notation is deprecated, use caps (this thing here is required for backward compatibility)
37
37
  var map = {
38
38
  chunks: 'slice_blob',
39
- resize: 'send_binary_string',
40
39
  jpgresize: 'send_binary_string',
41
40
  pngresize: 'send_binary_string',
42
41
  progress: 'report_upload_progress',
43
42
  multi_selection: 'select_multiple',
44
- max_file_size: 'access_binary',
45
43
  dragdrop: 'drag_and_drop',
46
44
  drop_element: 'drag_and_drop',
47
45
  headers: 'send_custom_headers',
@@ -73,6 +71,10 @@ function normalizeCaps(settings) {
73
71
  if (settings.chunk_size > 0) {
74
72
  caps.slice_blob = true;
75
73
  }
74
+
75
+ if (settings.resize.enabled) {
76
+ caps.send_binary_string = true;
77
+ }
76
78
 
77
79
  plupload.each(settings, function(value, feature) {
78
80
  resolve(feature, !!value, true); // strict check
@@ -95,7 +97,7 @@ var plupload = {
95
97
  * @static
96
98
  * @final
97
99
  */
98
- VERSION : '2.0.0beta',
100
+ VERSION : '2.1.1',
99
101
 
100
102
  /**
101
103
  * Inital state of the queue and also the state ones it's finished all it's uploads.
@@ -301,6 +303,32 @@ var plupload = {
301
303
  */
302
304
  guid : o.guid,
303
305
 
306
+ /**
307
+ * Get array of DOM Elements by their ids.
308
+ *
309
+ * @method get
310
+ * @for Utils
311
+ * @param {String} id Identifier of the DOM Element
312
+ * @return {Array}
313
+ */
314
+ get : function get(ids) {
315
+ var els = [], el;
316
+
317
+ if (o.typeOf(ids) !== 'array') {
318
+ ids = [ids];
319
+ }
320
+
321
+ var i = ids.length;
322
+ while (i--) {
323
+ el = o.get(ids[i]);
324
+ if (el) {
325
+ els.push(el);
326
+ }
327
+ }
328
+
329
+ return els.length ? els : null;
330
+ },
331
+
304
332
  /**
305
333
  * Executes the callback function for each item in array/object. If you return false in the
306
334
  * callback it will break the loop.
@@ -542,28 +570,35 @@ var plupload = {
542
570
  * @return {String} Formatted size string.
543
571
  */
544
572
  formatSize : function(size) {
573
+
545
574
  if (size === undef || /\D/.test(size)) {
546
575
  return plupload.translate('N/A');
547
576
  }
548
577
 
578
+ function round(num, precision) {
579
+ return Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision);
580
+ }
581
+
582
+ var boundary = Math.pow(1024, 4);
583
+
549
584
  // TB
550
- if (size > 1099511627776) {
551
- return Math.round(size / 1099511627776, 1) + " " + plupload.translate('tb');
585
+ if (size > boundary) {
586
+ return round(size / boundary, 1) + " " + plupload.translate('tb');
552
587
  }
553
588
 
554
589
  // GB
555
- if (size > 1073741824) {
556
- return Math.round(size / 1073741824, 1) + " " + plupload.translate('gb');
590
+ if (size > (boundary/=1024)) {
591
+ return round(size / boundary, 1) + " " + plupload.translate('gb');
557
592
  }
558
593
 
559
594
  // MB
560
- if (size > 1048576) {
561
- return Math.round(size / 1048576, 1) + " " + plupload.translate('mb');
595
+ if (size > (boundary/=1024)) {
596
+ return round(size / boundary, 1) + " " + plupload.translate('mb');
562
597
  }
563
598
 
564
599
  // KB
565
600
  if (size > 1024) {
566
- return Math.round(size / 1024, 1) + " " + plupload.translate('kb');
601
+ return Math.round(size / 1024) + " " + plupload.translate('kb');
567
602
  }
568
603
 
569
604
  return size + " " + plupload.translate('b');
@@ -592,12 +627,10 @@ var plupload = {
592
627
  * @return {String} Type of compatible runtime
593
628
  */
594
629
  predictRuntime : function(config, runtimes) {
595
- var up, runtime;
596
- if (runtimes) {
597
- config.runtimes = runtimes;
598
- }
630
+ var up, runtime;
631
+
599
632
  up = new plupload.Uploader(config);
600
- runtime = up.runtime;
633
+ runtime = o.Runtime.thatCan(up.getOption().required_features, runtimes || config.runtimes);
601
634
  up.destroy();
602
635
  return runtime;
603
636
  },
@@ -620,49 +653,25 @@ var plupload = {
620
653
  };
621
654
 
622
655
 
623
- plupload.addFileFilter('mime_types', (function() {
624
- var _filters, _extRegExp;
625
-
626
- // Convert extensions to regexp
627
- function getExtRegExp(filters) {
628
- var extensionsRegExp = [];
629
-
630
- plupload.each(filters, function(filter) {
631
- plupload.each(filter.extensions.split(/,/), function(ext) {
632
- if (/^\s*\*\s*$/.test(ext)) {
633
- extensionsRegExp.push('\\.*');
634
- } else {
635
- extensionsRegExp.push('\\.' + ext.replace(new RegExp('[' + ('/^$.*+?|()[]{}\\'.replace(/./g, '\\$&')) + ']', 'g'), '\\$&'));
636
- }
637
- });
656
+ plupload.addFileFilter('mime_types', function(filters, file, cb) {
657
+ if (filters.length && !filters.regexp.test(file.name)) {
658
+ this.trigger('Error', {
659
+ code : plupload.FILE_EXTENSION_ERROR,
660
+ message : plupload.translate('File extension error.'),
661
+ file : file
638
662
  });
639
-
640
- return new RegExp('(' + extensionsRegExp.join('|') + ')$', 'i');
663
+ cb(false);
664
+ } else {
665
+ cb(true);
641
666
  }
642
-
643
- return function(filters, file, cb) {
644
- if (!_extRegExp || filters != _filters) { // make sure we do it only once, unless filters got changed
645
- _extRegExp = getExtRegExp(filters);
646
- _filters = [].slice.call(filters);
647
- }
648
-
649
- if (!_extRegExp.test(file.name)) {
650
- this.trigger('Error', {
651
- code : plupload.FILE_EXTENSION_ERROR,
652
- message : plupload.translate('File extension error.'),
653
- file : file
654
- });
655
- cb(false);
656
- } else {
657
- cb(true);
658
- }
659
- };
660
- }()));
667
+ });
661
668
 
662
669
 
663
670
  plupload.addFileFilter('max_file_size', function(maxSize, file, cb) {
664
671
  var undef;
665
672
 
673
+ maxSize = plupload.parseSize(maxSize);
674
+
666
675
  // Invalid file size
667
676
  if (file.size !== undef && maxSize && file.size > maxSize) {
668
677
  this.trigger('Error', {
@@ -728,7 +737,7 @@ plupload.addFileFilter('prevent_duplicates', function(value, file, cb) {
728
737
  @param {String} [settings.silverlight_xap_url] URL of the Silverlight xap.
729
738
  @param {Boolean} [settings.unique_names=false] If true will generate unique filenames for uploaded files.
730
739
  */
731
- plupload.Uploader = function(settings) {
740
+ plupload.Uploader = function(options) {
732
741
  /**
733
742
  * Fires when the current RunTime has been initialized.
734
743
  *
@@ -743,6 +752,17 @@ plupload.Uploader = function(settings) {
743
752
  * @param {plupload.Uploader} uploader Uploader instance sending the event.
744
753
  */
745
754
 
755
+ /**
756
+ * Fires when the option is changed in via uploader.setOption().
757
+ *
758
+ * @event OptionChanged
759
+ * @since 2.1
760
+ * @param {plupload.Uploader} uploader Uploader instance sending the event.
761
+ * @param {String} name Name of the option that was changed
762
+ * @param {Mixed} value New value for the specified option
763
+ * @param {Mixed} oldValue Previous value of the option
764
+ */
765
+
746
766
  /**
747
767
  * Fires when the silverlight/flash or other shim needs to move.
748
768
  *
@@ -790,13 +810,22 @@ plupload.Uploader = function(settings) {
790
810
  */
791
811
 
792
812
  /**
793
- * Fires while a file was removed from queue.
813
+ * Fires when file is removed from the queue.
794
814
  *
795
815
  * @event FilesRemoved
796
816
  * @param {plupload.Uploader} uploader Uploader instance sending the event.
797
817
  * @param {Array} files Array of files that got removed.
798
818
  */
799
819
 
820
+ /**
821
+ * Fires for every filtered file before it is added to the queue.
822
+ *
823
+ * @event FileFiltered
824
+ * @since 2.1
825
+ * @param {plupload.Uploader} uploader Uploader instance sending the event.
826
+ * @param {plupload.File} file Another file that has to be added to the queue.
827
+ */
828
+
800
829
  /**
801
830
  * Fires after files were filtered and added to the queue.
802
831
  *
@@ -845,9 +874,17 @@ plupload.Uploader = function(settings) {
845
874
  * @event Destroy
846
875
  * @param {plupload.Uploader} uploader Uploader instance sending the event.
847
876
  */
848
- var files = [], events = {}, required_caps = {},
849
- startTime, total, disabled = false,
850
- fileInput, fileDrop, xhr;
877
+ var uid = plupload.guid()
878
+ , settings
879
+ , files = []
880
+ , preferred_caps = {}
881
+ , fileInputs = []
882
+ , fileDrops = []
883
+ , startTime
884
+ , total
885
+ , disabled = false
886
+ , xhr
887
+ ;
851
888
 
852
889
 
853
890
  // Private methods
@@ -879,11 +916,13 @@ plupload.Uploader = function(settings) {
879
916
  }
880
917
  }
881
918
 
919
+
882
920
  function calcFile(file) {
883
921
  file.percent = file.size > 0 ? Math.ceil(file.loaded / file.size * 100) : 100;
884
922
  calc();
885
923
  }
886
924
 
925
+
887
926
  function calc() {
888
927
  var i, file;
889
928
 
@@ -923,14 +962,59 @@ plupload.Uploader = function(settings) {
923
962
  }
924
963
  }
925
964
 
926
- function initControls() {
927
- var self = this, initialized = 0;
965
+
966
+ function getRUID() {
967
+ var ctrl = fileInputs[0] || fileDrops[0];
968
+ if (ctrl) {
969
+ return ctrl.getRuntime().uid;
970
+ }
971
+ return false;
972
+ }
973
+
974
+
975
+ function runtimeCan(file, cap) {
976
+ if (file.ruid) {
977
+ var info = o.Runtime.getInfo(file.ruid);
978
+ if (info) {
979
+ return info.can(cap);
980
+ }
981
+ }
982
+ return false;
983
+ }
984
+
985
+
986
+ function bindEventListeners() {
987
+ this.bind('FilesAdded', onFilesAdded);
988
+
989
+ this.bind('CancelUpload', onCancelUpload);
990
+
991
+ this.bind('BeforeUpload', onBeforeUpload);
992
+
993
+ this.bind('UploadFile', onUploadFile);
994
+
995
+ this.bind('UploadProgress', onUploadProgress);
996
+
997
+ this.bind('StateChanged', onStateChanged);
998
+
999
+ this.bind('QueueChanged', calc);
1000
+
1001
+ this.bind('Error', onError);
1002
+
1003
+ this.bind('FileUploaded', onFileUploaded);
1004
+
1005
+ this.bind('Destroy', onDestroy);
1006
+ }
1007
+
1008
+
1009
+ function initControls(settings, cb) {
1010
+ var self = this, inited = 0, queue = [];
928
1011
 
929
1012
  // common settings
930
1013
  var options = {
931
1014
  accept: settings.filters.mime_types,
932
1015
  runtime_order: settings.runtimes,
933
- required_caps: required_caps,
1016
+ required_caps: settings.required_features,
1017
+ preferred_caps: preferred_caps,
934
1018
  swf_url: settings.flash_swf_url,
935
1019
  xap_url: settings.silverlight_xap_url
936
1020
  };
@@ -942,15 +1026,15 @@ plupload.Uploader = function(settings) {
942
1026
  }
943
1027
  });
944
1028
 
945
- o.inSeries([
946
- function(cb) {
947
- // Initialize file dialog trigger
948
- if (settings.browse_button) {
949
- fileInput = new o.FileInput(plupload.extend({}, options, {
1029
+ // initialize file pickers - there can be many
1030
+ if (settings.browse_button) {
1031
+ plupload.each(settings.browse_button, function(el) {
1032
+ queue.push(function(cb) {
1033
+ var fileInput = new o.FileInput(plupload.extend({}, options, {
950
1034
  name: settings.file_data_name,
951
1035
  multiple: settings.multi_selection,
952
1036
  container: settings.container,
953
- browse_button: settings.browse_button
1037
+ browse_button: el
954
1038
  }));
955
1039
 
956
1040
  fileInput.onready = function() {
@@ -963,7 +1047,8 @@ plupload.Uploader = function(settings) {
963
1047
  multi_selection: info.can('select_multiple')
964
1048
  });
965
1049
 
966
- initialized++;
1050
+ inited++;
1051
+ fileInputs.push(this);
967
1052
  cb();
968
1053
  };
969
1054
 
@@ -973,24 +1058,20 @@ plupload.Uploader = function(settings) {
973
1058
 
974
1059
  fileInput.bind('mouseenter mouseleave mousedown mouseup', function(e) {
975
1060
  if (!disabled) {
976
- var bButton = o.get(settings.browse_button);
977
- if (bButton) {
978
- if (settings.browse_button_hover) {
979
- if ('mouseenter' === e.type) {
980
- o.addClass(bButton, settings.browse_button_hover);
981
- } else if ('mouseleave' === e.type) {
982
- o.removeClass(bButton, settings.browse_button_hover);
983
- }
1061
+ if (settings.browse_button_hover) {
1062
+ if ('mouseenter' === e.type) {
1063
+ o.addClass(el, settings.browse_button_hover);
1064
+ } else if ('mouseleave' === e.type) {
1065
+ o.removeClass(el, settings.browse_button_hover);
984
1066
  }
1067
+ }
985
1068
 
986
- if (settings.browse_button_active) {
987
- if ('mousedown' === e.type) {
988
- o.addClass(bButton, settings.browse_button_active);
989
- } else if ('mouseup' === e.type) {
990
- o.removeClass(bButton, settings.browse_button_active);
991
- }
1069
+ if (settings.browse_button_active) {
1070
+ if ('mousedown' === e.type) {
1071
+ o.addClass(el, settings.browse_button_active);
1072
+ } else if ('mouseup' === e.type) {
1073
+ o.removeClass(el, settings.browse_button_active);
992
1074
  }
993
- bButton = null;
994
1075
  }
995
1076
  }
996
1077
  });
@@ -1001,24 +1082,25 @@ plupload.Uploader = function(settings) {
1001
1082
  });
1002
1083
 
1003
1084
  fileInput.init();
1004
- } else {
1005
- cb();
1006
- }
1007
- },
1085
+ });
1086
+ });
1087
+ }
1008
1088
 
1009
- function(cb) {
1010
- // Initialize drag/drop interface if requested
1011
- if (settings.drop_element) {
1012
- fileDrop = new o.FileDrop(plupload.extend({}, options, {
1013
- drop_zone: settings.drop_element
1089
+ // initialize drop zones
1090
+ if (settings.drop_element) {
1091
+ plupload.each(settings.drop_element, function(el) {
1092
+ queue.push(function(cb) {
1093
+ var fileDrop = new o.FileDrop(plupload.extend({}, options, {
1094
+ drop_zone: el
1014
1095
  }));
1015
1096
 
1016
1097
  fileDrop.onready = function() {
1017
1098
  var info = o.Runtime.getInfo(this.ruid);
1018
1099
 
1019
- self.features.dragdrop = info.can('drag_and_drop');
1100
+ self.features.dragdrop = info.can('drag_and_drop'); // for backward compatibility
1020
1101
 
1021
- initialized++;
1102
+ inited++;
1103
+ fileDrops.push(this);
1022
1104
  cb();
1023
1105
  };
1024
1106
 
@@ -1032,40 +1114,18 @@ plupload.Uploader = function(settings) {
1032
1114
  });
1033
1115
 
1034
1116
  fileDrop.init();
1035
- } else {
1036
- cb();
1037
- }
1038
- }
1039
- ],
1040
- function() {
1041
- if (typeof(settings.init) == "function") {
1042
- settings.init(self);
1043
- } else {
1044
- plupload.each(settings.init, function(func, name) {
1045
- self.bind(name, func);
1046
1117
  });
1047
- }
1118
+ });
1119
+ }
1048
1120
 
1049
- if (initialized) {
1050
- self.trigger('PostInit');
1051
- } else {
1052
- self.trigger('Error', {
1053
- code : plupload.INIT_ERROR,
1054
- message : plupload.translate('Init error.')
1055
- });
1121
+
1122
+ o.inSeries(queue, function() {
1123
+ if (typeof(cb) === 'function') {
1124
+ cb(inited);
1056
1125
  }
1057
1126
  });
1058
1127
  }
1059
1128
 
1060
- function runtimeCan(file, cap) {
1061
- if (file.ruid) {
1062
- var info = o.Runtime.getInfo(file.ruid);
1063
- if (info) {
1064
- return info.can(cap);
1065
- }
1066
- }
1067
- return false;
1068
- }
1069
1129
 
1070
1130
  function resizeImage(blob, params, cb) {
1071
1131
  var img = new o.Image();
@@ -1091,50 +1151,476 @@ plupload.Uploader = function(settings) {
1091
1151
  }
1092
1152
 
1093
1153
 
1094
- // Inital total state
1095
- total = new plupload.QueueProgress();
1154
+ function setOption(option, value, init) {
1155
+ var self = this, reinitRequired = false;
1096
1156
 
1097
- // Default settings
1098
- settings = plupload.extend({
1099
- runtimes: o.Runtime.order,
1100
- max_retries: 0,
1101
- multipart : true,
1102
- multi_selection : true,
1103
- file_data_name : 'file',
1104
- flash_swf_url : 'js/Moxie.swf',
1105
- silverlight_xap_url : 'js/Moxie.xap',
1106
- send_chunk_number: true // whether to send chunks and chunk numbers, or total and offset bytes
1107
- }, settings);
1157
+ function _setOption(option, value, init) {
1158
+ var oldValue = settings[option];
1108
1159
 
1109
- // Resize defaults
1110
- if (settings.resize) {
1111
- settings.resize = plupload.extend({
1112
- preserve_headers: true,
1113
- crop: false
1114
- }, settings.resize);
1160
+ switch (option) {
1161
+ case 'max_file_size':
1162
+ if (option === 'max_file_size') {
1163
+ settings.max_file_size = settings.filters.max_file_size = value;
1164
+ }
1165
+ break;
1166
+
1167
+ case 'chunk_size':
1168
+ if (value = plupload.parseSize(value)) {
1169
+ settings[option] = value;
1170
+ }
1171
+ break;
1172
+
1173
+ case 'filters':
1174
+ // for sake of backward compatibility
1175
+ if (plupload.typeOf(value) === 'array') {
1176
+ value = {
1177
+ mime_types: value
1178
+ };
1179
+ }
1180
+
1181
+ if (init) {
1182
+ plupload.extend(settings.filters, value);
1183
+ } else {
1184
+ settings.filters = value;
1185
+ }
1186
+
1187
+ // if file format filters are being updated, regenerate the matching expressions
1188
+ if (value.mime_types) {
1189
+ settings.filters.mime_types.regexp = (function(filters) {
1190
+ var extensionsRegExp = [];
1191
+
1192
+ plupload.each(filters, function(filter) {
1193
+ plupload.each(filter.extensions.split(/,/), function(ext) {
1194
+ if (/^\s*\*\s*$/.test(ext)) {
1195
+ extensionsRegExp.push('\\.*');
1196
+ } else {
1197
+ extensionsRegExp.push('\\.' + ext.replace(new RegExp('[' + ('/^$.*+?|()[]{}\\'.replace(/./g, '\\$&')) + ']', 'g'), '\\$&'));
1198
+ }
1199
+ });
1200
+ });
1201
+
1202
+ return new RegExp('(' + extensionsRegExp.join('|') + ')$', 'i');
1203
+ }(settings.filters.mime_types));
1204
+ }
1205
+ break;
1206
+
1207
+ case 'resize':
1208
+ if (init) {
1209
+ plupload.extend(settings.resize, value, {
1210
+ enabled: true
1211
+ });
1212
+ } else {
1213
+ settings.resize = value;
1214
+ }
1215
+ break;
1216
+
1217
+ case 'prevent_duplicates':
1218
+ settings.prevent_duplicates = settings.filters.prevent_duplicates = !!value;
1219
+ break;
1220
+
1221
+ case 'browse_button':
1222
+ case 'drop_element':
1223
+ value = plupload.get(value);
1224
+
1225
+ case 'container':
1226
+ case 'runtimes':
1227
+ case 'multi_selection':
1228
+ case 'flash_swf_url':
1229
+ case 'silverlight_xap_url':
1230
+ settings[option] = value;
1231
+ if (!init) {
1232
+ reinitRequired = true;
1233
+ }
1234
+ break;
1235
+
1236
+ default:
1237
+ settings[option] = value;
1238
+ }
1239
+
1240
+ if (!init) {
1241
+ self.trigger('OptionChanged', option, value, oldValue);
1242
+ }
1243
+ }
1244
+
1245
+ if (typeof(option) === 'object') {
1246
+ plupload.each(option, function(value, option) {
1247
+ _setOption(option, value, init);
1248
+ });
1249
+ } else {
1250
+ _setOption(option, value, init);
1251
+ }
1252
+
1253
+ if (init) {
1254
+ // Normalize the list of required capabilities
1255
+ settings.required_features = normalizeCaps(plupload.extend({}, settings));
1256
+
1257
+ // Come up with the list of capabilities that can affect default mode in a multi-mode runtimes
1258
+ preferred_caps = normalizeCaps(plupload.extend({}, settings, {
1259
+ required_features: true
1260
+ }));
1261
+ } else if (reinitRequired) {
1262
+ self.trigger('Destroy');
1263
+
1264
+ initControls.call(self, settings, function(inited) {
1265
+ if (inited) {
1266
+ self.runtime = o.Runtime.getInfo(getRUID()).type;
1267
+ self.trigger('Init', { runtime: self.runtime });
1268
+ self.trigger('PostInit');
1269
+ } else {
1270
+ self.trigger('Error', {
1271
+ code : plupload.INIT_ERROR,
1272
+ message : plupload.translate('Init error.')
1273
+ });
1274
+ }
1275
+ });
1276
+ }
1115
1277
  }
1116
1278
 
1117
- // Set file filters
1118
- if (plupload.typeOf(settings.filters) === 'array') {
1119
- settings.filters = {
1120
- mime_types: settings.filters
1121
- };
1279
+
1280
+ // Internal event handlers
1281
+ function onFilesAdded(up, filteredFiles) {
1282
+ // Add files to queue
1283
+ [].push.apply(files, filteredFiles);
1284
+
1285
+ up.trigger('QueueChanged');
1286
+ up.refresh();
1287
+ }
1288
+
1289
+
1290
+ function onBeforeUpload(up, file) {
1291
+ // Generate unique target filenames
1292
+ if (settings.unique_names) {
1293
+ var matches = file.name.match(/\.([^.]+)$/), ext = "part";
1294
+ if (matches) {
1295
+ ext = matches[1];
1296
+ }
1297
+ file.target_name = file.id + '.' + ext;
1298
+ }
1299
+ }
1300
+
1301
+
1302
+ function onUploadFile(up, file) {
1303
+ var url = up.settings.url
1304
+ , chunkSize = up.settings.chunk_size
1305
+ , retries = up.settings.max_retries
1306
+ , features = up.features
1307
+ , offset = 0
1308
+ , blob
1309
+ ;
1310
+
1311
+ // make sure we start at a predictable offset
1312
+ if (file.loaded) {
1313
+ offset = file.loaded = chunkSize * Math.floor(file.loaded / chunkSize);
1314
+ }
1315
+
1316
+ function handleError() {
1317
+ if (retries-- > 0) {
1318
+ delay(uploadNextChunk, 1000);
1319
+ } else {
1320
+ file.loaded = offset; // reset all progress
1321
+
1322
+ up.trigger('Error', {
1323
+ code : plupload.HTTP_ERROR,
1324
+ message : plupload.translate('HTTP Error.'),
1325
+ file : file,
1326
+ response : xhr.responseText,
1327
+ status : xhr.status,
1328
+ responseHeaders: xhr.getAllResponseHeaders()
1329
+ });
1330
+ }
1331
+ }
1332
+
1333
+ function uploadNextChunk() {
1334
+ var chunkBlob, formData, args, curChunkSize;
1335
+
1336
+ // File upload finished
1337
+ if (file.status == plupload.DONE || file.status == plupload.FAILED || up.state == plupload.STOPPED) {
1338
+ return;
1339
+ }
1340
+
1341
+ // Standard arguments
1342
+ args = {name : file.target_name || file.name};
1343
+
1344
+ if (chunkSize && features.chunks && blob.size > chunkSize) { // blob will be of type string if it was loaded in memory
1345
+ curChunkSize = Math.min(chunkSize, blob.size - offset);
1346
+ chunkBlob = blob.slice(offset, offset + curChunkSize);
1347
+ } else {
1348
+ curChunkSize = blob.size;
1349
+ chunkBlob = blob;
1350
+ }
1351
+
1352
+ // If chunking is enabled add corresponding args, no matter if file is bigger than chunk or smaller
1353
+ if (chunkSize && features.chunks) {
1354
+ // Setup query string arguments
1355
+ if (up.settings.send_chunk_number) {
1356
+ args.chunk = Math.ceil(offset / chunkSize);
1357
+ args.chunks = Math.ceil(blob.size / chunkSize);
1358
+ } else { // keep support for experimental chunk format, just in case
1359
+ args.offset = offset;
1360
+ args.total = blob.size;
1361
+ }
1362
+ }
1363
+
1364
+ xhr = new o.XMLHttpRequest();
1365
+
1366
+ // Do we have upload progress support
1367
+ if (xhr.upload) {
1368
+ xhr.upload.onprogress = function(e) {
1369
+ file.loaded = Math.min(file.size, offset + e.loaded);
1370
+ up.trigger('UploadProgress', file);
1371
+ };
1372
+ }
1373
+
1374
+ xhr.onload = function() {
1375
+ // check if upload made itself through
1376
+ if (xhr.status >= 400) {
1377
+ handleError();
1378
+ return;
1379
+ }
1380
+
1381
+ retries = up.settings.max_retries; // reset the counter
1382
+
1383
+ // Handle chunk response
1384
+ if (curChunkSize < blob.size) {
1385
+ chunkBlob.destroy();
1386
+
1387
+ offset += curChunkSize;
1388
+ file.loaded = Math.min(offset, blob.size);
1389
+
1390
+ up.trigger('ChunkUploaded', file, {
1391
+ offset : file.loaded,
1392
+ total : blob.size,
1393
+ response : xhr.responseText,
1394
+ status : xhr.status,
1395
+ responseHeaders: xhr.getAllResponseHeaders()
1396
+ });
1397
+
1398
+ // stock Android browser doesn't fire upload progress events, but in chunking mode we can fake them
1399
+ if (o.Env.browser === 'Android Browser') {
1400
+ // doesn't harm in general, but is not required anywhere else
1401
+ up.trigger('UploadProgress', file);
1402
+ }
1403
+ } else {
1404
+ file.loaded = file.size;
1405
+ }
1406
+
1407
+ chunkBlob = formData = null; // Free memory
1408
+
1409
+ // Check if file is uploaded
1410
+ if (!offset || offset >= blob.size) {
1411
+ // If file was modified, destory the copy
1412
+ if (file.size != file.origSize) {
1413
+ blob.destroy();
1414
+ blob = null;
1415
+ }
1416
+
1417
+ up.trigger('UploadProgress', file);
1418
+
1419
+ file.status = plupload.DONE;
1420
+
1421
+ up.trigger('FileUploaded', file, {
1422
+ response : xhr.responseText,
1423
+ status : xhr.status,
1424
+ responseHeaders: xhr.getAllResponseHeaders()
1425
+ });
1426
+ } else {
1427
+ // Still chunks left
1428
+ delay(uploadNextChunk, 1); // run detached, otherwise event handlers interfere
1429
+ }
1430
+ };
1431
+
1432
+ xhr.onerror = function() {
1433
+ handleError();
1434
+ };
1435
+
1436
+ xhr.onloadend = function() {
1437
+ this.destroy();
1438
+ xhr = null;
1439
+ };
1440
+
1441
+ // Build multipart request
1442
+ if (up.settings.multipart && features.multipart) {
1443
+
1444
+ args.name = file.target_name || file.name;
1445
+
1446
+ xhr.open("post", url, true);
1447
+
1448
+ // Set custom headers
1449
+ plupload.each(up.settings.headers, function(value, name) {
1450
+ xhr.setRequestHeader(name, value);
1451
+ });
1452
+
1453
+ formData = new o.FormData();
1454
+
1455
+ // Add multipart params
1456
+ plupload.each(plupload.extend(args, up.settings.multipart_params), function(value, name) {
1457
+ formData.append(name, value);
1458
+ });
1459
+
1460
+ // Add file and send it
1461
+ formData.append(up.settings.file_data_name, chunkBlob);
1462
+ xhr.send(formData, {
1463
+ runtime_order: up.settings.runtimes,
1464
+ required_caps: up.settings.required_features,
1465
+ preferred_caps: preferred_caps,
1466
+ swf_url: up.settings.flash_swf_url,
1467
+ xap_url: up.settings.silverlight_xap_url
1468
+ });
1469
+ } else {
1470
+ // if no multipart, send as binary stream
1471
+ url = plupload.buildUrl(up.settings.url, plupload.extend(args, up.settings.multipart_params));
1472
+
1473
+ xhr.open("post", url, true);
1474
+
1475
+ xhr.setRequestHeader('Content-Type', 'application/octet-stream'); // Binary stream header
1476
+
1477
+ // Set custom headers
1478
+ plupload.each(up.settings.headers, function(value, name) {
1479
+ xhr.setRequestHeader(name, value);
1480
+ });
1481
+
1482
+ xhr.send(chunkBlob, {
1483
+ runtime_order: up.settings.runtimes,
1484
+ required_caps: up.settings.required_features,
1485
+ preferred_caps: preferred_caps,
1486
+ swf_url: up.settings.flash_swf_url,
1487
+ xap_url: up.settings.silverlight_xap_url
1488
+ });
1489
+ }
1490
+ }
1491
+
1492
+ blob = file.getSource();
1493
+
1494
+ // Start uploading chunks
1495
+ if (up.settings.resize.enabled && runtimeCan(blob, 'send_binary_string') && !!~o.inArray(blob.type, ['image/jpeg', 'image/png'])) {
1496
+ // Resize if required
1497
+ resizeImage.call(this, blob, up.settings.resize, function(resizedBlob) {
1498
+ blob = resizedBlob;
1499
+ file.size = resizedBlob.size;
1500
+ uploadNextChunk();
1501
+ });
1502
+ } else {
1503
+ uploadNextChunk();
1504
+ }
1505
+ }
1506
+
1507
+
1508
+ function onUploadProgress(up, file) {
1509
+ calcFile(file);
1510
+ }
1511
+
1512
+
1513
+ function onStateChanged(up) {
1514
+ if (up.state == plupload.STARTED) {
1515
+ // Get start time to calculate bps
1516
+ startTime = (+new Date());
1517
+ } else if (up.state == plupload.STOPPED) {
1518
+ // Reset currently uploading files
1519
+ for (var i = up.files.length - 1; i >= 0; i--) {
1520
+ if (up.files[i].status == plupload.UPLOADING) {
1521
+ up.files[i].status = plupload.QUEUED;
1522
+ calc();
1523
+ }
1524
+ }
1525
+ }
1526
+ }
1527
+
1528
+
1529
+ function onCancelUpload() {
1530
+ if (xhr) {
1531
+ xhr.abort();
1532
+ }
1533
+ }
1534
+
1535
+
1536
+ function onFileUploaded(up) {
1537
+ calc();
1538
+
1539
+ // Upload next file but detach it from the error event
1540
+ // since other custom listeners might want to stop the queue
1541
+ delay(function() {
1542
+ uploadNext.call(up);
1543
+ }, 1);
1544
+ }
1545
+
1546
+
1547
+ function onError(up, err) {
1548
+ // Set failed status if an error occured on a file
1549
+ if (err.file) {
1550
+ err.file.status = plupload.FAILED;
1551
+ calcFile(err.file);
1552
+
1553
+ // Upload next file but detach it from the error event
1554
+ // since other custom listeners might want to stop the queue
1555
+ if (up.state == plupload.STARTED) { // upload in progress
1556
+ up.trigger('CancelUpload');
1557
+ delay(function() {
1558
+ uploadNext.call(up);
1559
+ }, 1);
1560
+ }
1561
+ }
1122
1562
  }
1123
- settings.filters = plupload.extend({
1124
- mime_types: [],
1125
- prevent_duplicates: !!settings.prevent_duplicates,
1126
- max_file_size: settings.max_file_size
1127
- }, settings.filters);
1128
1563
 
1129
1564
 
1130
- // Convert settings
1131
- settings.filters.max_file_size = plupload.parseSize(settings.filters.max_file_size) || 0;
1132
- settings.chunk_size = plupload.parseSize(settings.chunk_size) || 0;
1565
+ function onDestroy(up) {
1566
+ up.stop();
1567
+
1568
+ // Purge the queue
1569
+ plupload.each(files, function(file) {
1570
+ file.destroy();
1571
+ });
1572
+ files = [];
1573
+
1574
+ if (fileInputs.length) {
1575
+ plupload.each(fileInputs, function(fileInput) {
1576
+ fileInput.destroy();
1577
+ });
1578
+ fileInputs = [];
1579
+ }
1580
+
1581
+ if (fileDrops.length) {
1582
+ plupload.each(fileDrops, function(fileDrop) {
1583
+ fileDrop.destroy();
1584
+ });
1585
+ fileDrops = [];
1586
+ }
1133
1587
 
1134
- // Normalize the list of required capabilities
1135
- settings.required_features = required_caps = normalizeCaps(plupload.extend({}, settings));
1588
+ preferred_caps = {};
1589
+ disabled = false;
1590
+ startTime = xhr = null;
1591
+ total.reset();
1592
+ }
1136
1593
 
1137
1594
 
1595
+ // Default settings
1596
+ settings = {
1597
+ runtimes: o.Runtime.order,
1598
+ max_retries: 0,
1599
+ chunk_size: 0,
1600
+ multipart: true,
1601
+ multi_selection: true,
1602
+ file_data_name: 'file',
1603
+ flash_swf_url: 'js/Moxie.swf',
1604
+ silverlight_xap_url: 'js/Moxie.xap',
1605
+ filters: {
1606
+ mime_types: [],
1607
+ prevent_duplicates: false,
1608
+ max_file_size: 0
1609
+ },
1610
+ resize: {
1611
+ enabled: false,
1612
+ preserve_headers: true,
1613
+ crop: false
1614
+ },
1615
+ send_chunk_number: true // whether to send chunks and chunk numbers, or total and offset bytes
1616
+ };
1617
+
1618
+
1619
+ setOption.call(this, options, null, true);
1620
+
1621
+ // Inital total state
1622
+ total = new plupload.QueueProgress();
1623
+
1138
1624
  // Add public methods
1139
1625
  plupload.extend(this, {
1140
1626
 
@@ -1144,7 +1630,8 @@ plupload.Uploader = function(settings) {
1144
1630
  * @property id
1145
1631
  * @type String
1146
1632
  */
1147
- id : plupload.guid(),
1633
+ id : uid,
1634
+ uid : uid, // mOxie uses this to differentiate between event targets
1148
1635
 
1149
1636
  /**
1150
1637
  * Current state of the total uploading progress. This one can either be plupload.STARTED or plupload.STOPPED.
@@ -1171,7 +1658,7 @@ plupload.Uploader = function(settings) {
1171
1658
  * @property runtime
1172
1659
  * @type String
1173
1660
  */
1174
- runtime : o.Runtime.thatCan(required_caps, settings.runtimes), // predict runtime
1661
+ runtime : null,
1175
1662
 
1176
1663
  /**
1177
1664
  * Current upload queue, an array of File instances.
@@ -1207,12 +1694,6 @@ plupload.Uploader = function(settings) {
1207
1694
  init : function() {
1208
1695
  var self = this;
1209
1696
 
1210
- settings.browse_button = o.get(settings.browse_button);
1211
-
1212
- // Check if drop zone requested
1213
- settings.drop_element = o.get(settings.drop_element);
1214
-
1215
-
1216
1697
  if (typeof(settings.preinit) == "function") {
1217
1698
  settings.preinit(self);
1218
1699
  } else {
@@ -1221,7 +1702,6 @@ plupload.Uploader = function(settings) {
1221
1702
  });
1222
1703
  }
1223
1704
 
1224
-
1225
1705
  // Check for required options
1226
1706
  if (!settings.browse_button || !settings.url) {
1227
1707
  this.trigger('Error', {
@@ -1231,284 +1711,55 @@ plupload.Uploader = function(settings) {
1231
1711
  return;
1232
1712
  }
1233
1713
 
1714
+ bindEventListeners.call(this);
1234
1715
 
1235
- self.bind("FilesAdded", function(up, filteredFiles) {
1236
- // Add files to queue
1237
- [].push.apply(files, filteredFiles);
1238
-
1239
- delay(function() {
1240
- self.trigger("QueueChanged");
1241
- self.refresh();
1242
- }, 1);
1243
- });
1244
-
1245
- self.bind("CancelUpload", function() {
1246
- if (xhr) {
1247
- xhr.abort();
1248
- }
1249
- });
1250
-
1251
- // Generate unique target filenames
1252
- if (settings.unique_names) {
1253
- self.bind("BeforeUpload", function(up, file) {
1254
- var matches = file.name.match(/\.([^.]+)$/), ext = "part";
1255
- if (matches) {
1256
- ext = matches[1];
1257
- }
1258
- file.target_name = file.id + '.' + ext;
1259
- });
1260
- }
1261
-
1262
- self.bind("UploadFile", function(up, file) {
1263
- var url = up.settings.url, features = up.features, chunkSize = settings.chunk_size,
1264
- retries = settings.max_retries,
1265
- blob, offset = 0;
1266
-
1267
- // make sure we start at a predictable offset
1268
- if (file.loaded) {
1269
- offset = file.loaded = chunkSize * Math.floor(file.loaded / chunkSize);
1270
- }
1271
-
1272
- function handleError() {
1273
- if (retries-- > 0) {
1274
- delay(uploadNextChunk, 1);
1275
- } else {
1276
- file.loaded = offset; // reset all progress
1277
-
1278
- up.trigger('Error', {
1279
- code : plupload.HTTP_ERROR,
1280
- message : plupload.translate('HTTP Error.'),
1281
- file : file,
1282
- response : xhr.responseText,
1283
- status : xhr.status,
1284
- responseHeaders: xhr.getAllResponseHeaders()
1285
- });
1286
- }
1287
- }
1288
-
1289
- function uploadNextChunk() {
1290
- var chunkBlob, formData, args, curChunkSize;
1291
-
1292
- // File upload finished
1293
- if (file.status == plupload.DONE || file.status == plupload.FAILED || up.state == plupload.STOPPED) {
1294
- return;
1295
- }
1296
-
1297
- // Standard arguments
1298
- args = {name : file.target_name || file.name};
1299
-
1300
- if (chunkSize && features.chunks && blob.size > chunkSize) { // blob will be of type string if it was loaded in memory
1301
- curChunkSize = Math.min(chunkSize, blob.size - offset);
1302
- chunkBlob = blob.slice(offset, offset + curChunkSize);
1303
- } else {
1304
- curChunkSize = blob.size;
1305
- chunkBlob = blob;
1306
- }
1307
-
1308
- // If chunking is enabled add corresponding args, no matter if file is bigger than chunk or smaller
1309
- if (chunkSize && features.chunks) {
1310
- // Setup query string arguments
1311
- if (settings.send_chunk_number) {
1312
- args.chunk = Math.ceil(offset / chunkSize);
1313
- args.chunks = Math.ceil(blob.size / chunkSize);
1314
- } else { // keep support for experimental chunk format, just in case
1315
- args.offset = offset;
1316
- args.total = blob.size;
1317
- }
1318
- }
1319
-
1320
- xhr = new o.XMLHttpRequest();
1321
-
1322
- // Do we have upload progress support
1323
- if (xhr.upload) {
1324
- xhr.upload.onprogress = function(e) {
1325
- file.loaded = Math.min(file.size, offset + e.loaded);
1326
- up.trigger('UploadProgress', file);
1327
- };
1328
- }
1329
-
1330
- xhr.onload = function() {
1331
- // check if upload made itself through
1332
- if (xhr.status >= 400) {
1333
- handleError();
1334
- return;
1335
- }
1336
-
1337
- // Handle chunk response
1338
- if (curChunkSize < blob.size) {
1339
- chunkBlob.destroy();
1340
-
1341
- offset += curChunkSize;
1342
- file.loaded = Math.min(offset, blob.size);
1343
-
1344
- up.trigger('ChunkUploaded', file, {
1345
- offset : file.loaded,
1346
- total : blob.size,
1347
- response : xhr.responseText,
1348
- status : xhr.status,
1349
- responseHeaders: xhr.getAllResponseHeaders()
1350
- });
1351
-
1352
- // stock Android browser doesn't fire upload progress events, but in chunking mode we can fake them
1353
- if (o.Env.browser === 'Android Browser') {
1354
- // doesn't harm in general, but is not required anywhere else
1355
- up.trigger('UploadProgress', file);
1356
- }
1357
- } else {
1358
- file.loaded = file.size;
1359
- }
1360
-
1361
- chunkBlob = formData = null; // Free memory
1362
-
1363
- // Check if file is uploaded
1364
- if (!offset || offset >= blob.size) {
1365
- // If file was modified, destory the copy
1366
- if (file.size != file.origSize) {
1367
- blob.destroy();
1368
- blob = null;
1369
- }
1370
-
1371
- up.trigger('UploadProgress', file);
1372
-
1373
- file.status = plupload.DONE;
1374
-
1375
- up.trigger('FileUploaded', file, {
1376
- response : xhr.responseText,
1377
- status : xhr.status,
1378
- responseHeaders: xhr.getAllResponseHeaders()
1379
- });
1380
- } else {
1381
- // Still chunks left
1382
- delay(uploadNextChunk, 1); // run detached, otherwise event handlers interfere
1383
- }
1384
- };
1385
-
1386
- xhr.onerror = function() {
1387
- handleError();
1388
- };
1389
-
1390
- xhr.onloadend = function() {
1391
- this.destroy();
1392
- xhr = null;
1393
- };
1394
-
1395
- // Build multipart request
1396
- if (up.settings.multipart && features.multipart) {
1397
-
1398
- args.name = file.target_name || file.name;
1399
-
1400
- xhr.open("post", url, true);
1401
-
1402
- // Set custom headers
1403
- plupload.each(up.settings.headers, function(value, name) {
1404
- xhr.setRequestHeader(name, value);
1405
- });
1406
-
1407
- formData = new o.FormData();
1408
-
1409
- // Add multipart params
1410
- plupload.each(plupload.extend(args, up.settings.multipart_params), function(value, name) {
1411
- formData.append(name, value);
1412
- });
1413
-
1414
- // Add file and send it
1415
- formData.append(up.settings.file_data_name, chunkBlob);
1416
- xhr.send(formData, {
1417
- runtime_order: up.settings.runtimes,
1418
- required_caps: required_caps,
1419
- swf_url: up.settings.flash_swf_url,
1420
- xap_url: up.settings.silverlight_xap_url
1421
- });
1422
- } else {
1423
- // if no multipart, send as binary stream
1424
- url = plupload.buildUrl(up.settings.url, plupload.extend(args, up.settings.multipart_params));
1425
-
1426
- xhr.open("post", url, true);
1427
-
1428
- xhr.setRequestHeader('Content-Type', 'application/octet-stream'); // Binary stream header
1429
-
1430
- // Set custom headers
1431
- plupload.each(up.settings.headers, function(value, name) {
1432
- xhr.setRequestHeader(name, value);
1433
- });
1434
-
1435
- xhr.send(chunkBlob, {
1436
- runtime_order: up.settings.runtimes,
1437
- required_caps: required_caps,
1438
- swf_url: up.settings.flash_swf_url,
1439
- xap_url: up.settings.silverlight_xap_url
1440
- });
1441
- }
1442
- }
1443
-
1444
- blob = file.getSource();
1445
-
1446
- // Start uploading chunks
1447
- if (!o.isEmptyObj(up.settings.resize) && runtimeCan(blob, 'send_binary_string') && !!~o.inArray(blob.type, ['image/jpeg', 'image/png'])) {
1448
- // Resize if required
1449
- resizeImage.call(this, blob, up.settings.resize, function(resizedBlob) {
1450
- blob = resizedBlob;
1451
- file.size = resizedBlob.size;
1452
- uploadNextChunk();
1453
- });
1716
+ initControls.call(this, settings, function(inited) {
1717
+ if (typeof(settings.init) == "function") {
1718
+ settings.init(self);
1454
1719
  } else {
1455
- uploadNextChunk();
1456
- }
1457
- });
1458
-
1459
- self.bind('UploadProgress', function(up, file) {
1460
- calcFile(file);
1461
- });
1462
-
1463
- self.bind('StateChanged', function(up) {
1464
- if (up.state == plupload.STARTED) {
1465
- // Get start time to calculate bps
1466
- startTime = (+new Date());
1467
- } else if (up.state == plupload.STOPPED) {
1468
- // Reset currently uploading files
1469
- for (var i = up.files.length - 1; i >= 0; i--) {
1470
- if (up.files[i].status == plupload.UPLOADING) {
1471
- up.files[i].status = plupload.QUEUED;
1472
- calc();
1473
- }
1474
- }
1720
+ plupload.each(settings.init, function(func, name) {
1721
+ self.bind(name, func);
1722
+ });
1475
1723
  }
1476
- });
1477
-
1478
- self.bind('QueueChanged', calc);
1479
1724
 
1480
- self.bind("Error", function(up, err) {
1481
- // Set failed status if an error occured on a file
1482
- if (err.file) {
1483
- err.file.status = plupload.FAILED;
1484
-
1485
- calcFile(err.file);
1486
-
1487
- // Upload next file but detach it from the error event
1488
- // since other custom listeners might want to stop the queue
1489
- if (up.state == plupload.STARTED) {
1490
- delay(function() {
1491
- uploadNext.call(self);
1492
- }, 1);
1493
- }
1725
+ if (inited) {
1726
+ self.runtime = o.Runtime.getInfo(getRUID()).type;
1727
+ self.trigger('Init', { runtime: self.runtime });
1728
+ self.trigger('PostInit');
1729
+ } else {
1730
+ self.trigger('Error', {
1731
+ code : plupload.INIT_ERROR,
1732
+ message : plupload.translate('Init error.')
1733
+ });
1494
1734
  }
1495
1735
  });
1736
+ },
1496
1737
 
1497
- self.bind("FileUploaded", function() {
1498
- calc();
1499
-
1500
- // Upload next file but detach it from the error event
1501
- // since other custom listeners might want to stop the queue
1502
- delay(function() {
1503
- uploadNext.call(self);
1504
- }, 1);
1505
- });
1506
-
1507
- // some dependent scripts hook onto Init to alter configuration options, raw UI, etc (like Queue Widget),
1508
- // therefore we got to fire this one, before we dive into the actual initializaion
1509
- self.trigger('Init', { runtime: this.runtime });
1738
+ /**
1739
+ * Set the value for the specified option(s).
1740
+ *
1741
+ * @method setOption
1742
+ * @since 2.1
1743
+ * @param {String|Object} option Name of the option to change or the set of key/value pairs
1744
+ * @param {Mixed} [value] Value for the option (is ignored, if first argument is object)
1745
+ */
1746
+ setOption: function(option, value) {
1747
+ setOption.call(this, option, value, !this.runtime); // until runtime not set we do not need to reinitialize
1748
+ },
1510
1749
 
1511
- initControls.call(this);
1750
+ /**
1751
+ * Get the value for the specified option or the whole configuration, if not specified.
1752
+ *
1753
+ * @method getOption
1754
+ * @since 2.1
1755
+ * @param {String} [option] Name of the option to get
1756
+ * @return {Mixed} Value for the option or the whole set
1757
+ */
1758
+ getOption: function(option) {
1759
+ if (!option) {
1760
+ return settings;
1761
+ }
1762
+ return settings[option];
1512
1763
  },
1513
1764
 
1514
1765
  /**
@@ -1518,10 +1769,12 @@ plupload.Uploader = function(settings) {
1518
1769
  * @method refresh
1519
1770
  */
1520
1771
  refresh : function() {
1521
- if (fileInput) {
1522
- fileInput.trigger("Refresh");
1772
+ if (fileInputs.length) {
1773
+ plupload.each(fileInputs, function(fileInput) {
1774
+ fileInput.trigger('Refresh');
1775
+ });
1523
1776
  }
1524
- this.trigger("Refresh");
1777
+ this.trigger('Refresh');
1525
1778
  },
1526
1779
 
1527
1780
  /**
@@ -1532,7 +1785,7 @@ plupload.Uploader = function(settings) {
1532
1785
  start : function() {
1533
1786
  if (this.state != plupload.STARTED) {
1534
1787
  this.state = plupload.STARTED;
1535
- this.trigger("StateChanged");
1788
+ this.trigger('StateChanged');
1536
1789
 
1537
1790
  uploadNext.call(this);
1538
1791
  }
@@ -1546,8 +1799,8 @@ plupload.Uploader = function(settings) {
1546
1799
  stop : function() {
1547
1800
  if (this.state != plupload.STOPPED) {
1548
1801
  this.state = plupload.STOPPED;
1549
- this.trigger("StateChanged");
1550
- this.trigger("CancelUpload");
1802
+ this.trigger('StateChanged');
1803
+ this.trigger('CancelUpload');
1551
1804
  }
1552
1805
  },
1553
1806
 
@@ -1561,11 +1814,13 @@ plupload.Uploader = function(settings) {
1561
1814
  disableBrowse : function() {
1562
1815
  disabled = arguments[0] !== undef ? arguments[0] : true;
1563
1816
 
1564
- if (fileInput) {
1565
- fileInput.disable(disabled);
1817
+ if (fileInputs.length) {
1818
+ plupload.each(fileInputs, function(fileInput) {
1819
+ fileInput.disable(disabled);
1820
+ });
1566
1821
  }
1567
1822
 
1568
- this.trigger("DisableBrowse", disabled);
1823
+ this.trigger('DisableBrowse', disabled);
1569
1824
  },
1570
1825
 
1571
1826
  /**
@@ -1590,6 +1845,7 @@ plupload.Uploader = function(settings) {
1590
1845
  * if any files were added to the queue. Otherwise nothing happens.
1591
1846
  *
1592
1847
  * @method addFile
1848
+ * @since 2.0
1593
1849
  * @param {plupload.File|mOxie.File|File|Node|Array} file File or files to add to the queue.
1594
1850
  * @param {String} [fileName] If specified, will be used as a name for the file
1595
1851
  */
@@ -1600,14 +1856,6 @@ plupload.Uploader = function(settings) {
1600
1856
  , ruid
1601
1857
  ;
1602
1858
 
1603
- function getRUID() {
1604
- var ctrl = fileDrop || fileInput;
1605
- if (ctrl) {
1606
- return ctrl.getRuntime().uid;
1607
- }
1608
- return false;
1609
- }
1610
-
1611
1859
  function filterFile(file, cb) {
1612
1860
  var queue = [];
1613
1861
  o.each(self.settings.filters, function(rule, name) {
@@ -1657,8 +1905,9 @@ plupload.Uploader = function(settings) {
1657
1905
  filterFile(file, function(err) {
1658
1906
  if (!err) {
1659
1907
  files.push(file);
1908
+ self.trigger("FileFiltered", file);
1660
1909
  }
1661
- cb();
1910
+ delay(cb, 1); // do not build up recursions or eventually we might hit the limits
1662
1911
  });
1663
1912
  });
1664
1913
  }
@@ -1720,14 +1969,27 @@ plupload.Uploader = function(settings) {
1720
1969
  // Splice and trigger events
1721
1970
  var removed = files.splice(start === undef ? 0 : start, length === undef ? files.length : length);
1722
1971
 
1972
+ // if upload is in progress we need to stop it and restart after files are removed
1973
+ var restartRequired = false;
1974
+ if (this.state == plupload.STARTED) { // upload in progress
1975
+ restartRequired = true;
1976
+ this.stop();
1977
+ }
1978
+
1723
1979
  this.trigger("FilesRemoved", removed);
1724
- this.trigger("QueueChanged");
1725
1980
 
1726
1981
  // Dispose any resources allocated by those files
1727
1982
  plupload.each(removed, function(file) {
1728
1983
  file.destroy();
1729
1984
  });
1730
1985
 
1986
+ this.trigger("QueueChanged");
1987
+ this.refresh();
1988
+
1989
+ if (restartRequired) {
1990
+ this.start();
1991
+ }
1992
+
1731
1993
  return removed;
1732
1994
  },
1733
1995
 
@@ -1739,27 +2001,6 @@ plupload.Uploader = function(settings) {
1739
2001
  * @param {String} name Event name to fire.
1740
2002
  * @param {Object..} Multiple arguments to pass along to the listener functions.
1741
2003
  */
1742
- trigger : function(name) {
1743
- var list = events[name.toLowerCase()], i, args;
1744
-
1745
- // console.log(name, arguments);
1746
-
1747
- if (list) {
1748
- // Replace name with sender in args
1749
- args = Array.prototype.slice.call(arguments);
1750
- args[0] = this;
1751
-
1752
- // Dispatch event to all listeners
1753
- for (i = 0; i < list.length; i++) {
1754
- // Fire event, break chain if false is returned
1755
- if (list[i].func.apply(list[i].scope, args) === false) {
1756
- return false;
1757
- }
1758
- }
1759
- }
1760
-
1761
- return true;
1762
- },
1763
2004
 
1764
2005
  /**
1765
2006
  * Check whether uploader has any listeners to the specified event.
@@ -1767,9 +2008,7 @@ plupload.Uploader = function(settings) {
1767
2008
  * @method hasEventListener
1768
2009
  * @param {String} name Event name to check for.
1769
2010
  */
1770
- hasEventListener : function(name) {
1771
- return !!events[name.toLowerCase()];
1772
- },
2011
+
1773
2012
 
1774
2013
  /**
1775
2014
  * Adds an event listener by name.
@@ -1780,12 +2019,13 @@ plupload.Uploader = function(settings) {
1780
2019
  * @param {Object} scope Optional scope to execute the specified function in.
1781
2020
  */
1782
2021
  bind : function(name, func, scope) {
1783
- var list;
1784
-
1785
- name = name.toLowerCase();
1786
- list = events[name] || [];
1787
- list.push({func : func, scope : scope || this});
1788
- events[name] = list;
2022
+ var self = this;
2023
+ // adapt moxie EventTarget style to Plupload-like
2024
+ plupload.Uploader.prototype.bind.call(this, name, function() {
2025
+ var args = [].slice.call(arguments);
2026
+ args.splice(0, 1, self); // replace event object with uploader instance
2027
+ return func.apply(this, args);
2028
+ }, 0, scope);
1789
2029
  },
1790
2030
 
1791
2031
  /**
@@ -1795,42 +2035,13 @@ plupload.Uploader = function(settings) {
1795
2035
  * @param {String} name Name of event to remove.
1796
2036
  * @param {function} func Function to remove from listener.
1797
2037
  */
1798
- unbind : function(name) {
1799
- name = name.toLowerCase();
1800
-
1801
- var list = events[name], i, func = arguments[1];
1802
-
1803
- if (list) {
1804
- if (func !== undef) {
1805
- for (i = list.length - 1; i >= 0; i--) {
1806
- if (list[i].func === func) {
1807
- list.splice(i, 1);
1808
- break;
1809
- }
1810
- }
1811
- } else {
1812
- list = [];
1813
- }
1814
-
1815
- // delete event list if it has become empty
1816
- if (!list.length) {
1817
- delete events[name];
1818
- }
1819
- }
1820
- },
1821
2038
 
1822
2039
  /**
1823
2040
  * Removes all event listeners.
1824
2041
  *
1825
2042
  * @method unbindAll
1826
2043
  */
1827
- unbindAll : function() {
1828
- var self = this;
1829
2044
 
1830
- plupload.each(events, function(list, name) {
1831
- self.unbind(name);
1832
- });
1833
- },
1834
2045
 
1835
2046
  /**
1836
2047
  * Destroys Plupload instance and cleans after itself.
@@ -1838,36 +2049,15 @@ plupload.Uploader = function(settings) {
1838
2049
  * @method destroy
1839
2050
  */
1840
2051
  destroy : function() {
1841
- this.stop();
1842
-
1843
- // Purge the queue
1844
- plupload.each(files, function(file) {
1845
- file.destroy();
1846
- });
1847
- files = [];
1848
-
1849
- if (fileInput) {
1850
- fileInput.destroy();
1851
- fileInput = null;
1852
- }
1853
-
1854
- if (fileDrop) {
1855
- fileDrop.destroy();
1856
- fileDrop = null;
1857
- }
1858
-
1859
- required_caps = {};
1860
- startTime = total = disabled = xhr = null;
1861
-
1862
2052
  this.trigger('Destroy');
1863
-
1864
- // Clean-up after uploader itself
2053
+ settings = total = null; // purge these exclusively
1865
2054
  this.unbindAll();
1866
- events = {};
1867
2055
  }
1868
2056
  });
1869
2057
  };
1870
2058
 
2059
+ plupload.Uploader.prototype = o.EventTarget.instance;
2060
+
1871
2061
  /**
1872
2062
  * Constructs a new file instance.
1873
2063
  *