plupload-rails 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  *