s3_cors_fileupload 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -2
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +19 -19
  5. data/README.md +13 -1
  6. data/lib/generators/s3_cors_fileupload/install/templates/s3_uploads.js +1 -2
  7. data/lib/generators/s3_cors_fileupload/install/templates/source_file.rb +1 -1
  8. data/lib/generators/s3_cors_fileupload/install/templates/views/erb/_template_download.html.erb +19 -16
  9. data/lib/generators/s3_cors_fileupload/install/templates/views/erb/_template_upload.html.erb +31 -26
  10. data/lib/generators/s3_cors_fileupload/install/templates/views/erb/_template_uploaded.html.erb +18 -13
  11. data/lib/generators/s3_cors_fileupload/install/templates/views/erb/index.html.erb +1 -1
  12. data/lib/generators/s3_cors_fileupload/install/templates/views/haml/_template_download.html.haml +16 -21
  13. data/lib/generators/s3_cors_fileupload/install/templates/views/haml/_template_upload.html.haml +15 -18
  14. data/lib/generators/s3_cors_fileupload/install/templates/views/haml/_template_uploaded.html.haml +12 -13
  15. data/lib/generators/s3_cors_fileupload/install/templates/views/haml/index.html.haml +1 -1
  16. data/lib/s3_cors_fileupload/rails/form_helper.rb +16 -14
  17. data/lib/s3_cors_fileupload/version.rb +3 -3
  18. data/vendor/assets/javascripts/s3_cors_fileupload/index.js +4 -1
  19. data/vendor/assets/javascripts/s3_cors_fileupload/jquery.fileupload-image.js +213 -0
  20. data/vendor/assets/javascripts/s3_cors_fileupload/jquery.fileupload-process.js +164 -0
  21. data/vendor/assets/javascripts/s3_cors_fileupload/jquery.fileupload-ui.js +62 -228
  22. data/vendor/assets/javascripts/s3_cors_fileupload/jquery.fileupload-validate.js +116 -0
  23. data/vendor/assets/javascripts/s3_cors_fileupload/jquery.fileupload.js +212 -84
  24. data/vendor/assets/javascripts/s3_cors_fileupload/jquery.iframe-transport.js +24 -4
  25. data/vendor/assets/javascripts/s3_cors_fileupload/vendor/jquery.ui.widget.js +1 -1
  26. data/vendor/assets/javascripts/s3_cors_fileupload/vendor/load-image.js +138 -172
  27. data/vendor/assets/javascripts/s3_cors_fileupload/vendor/tmpl.js +3 -4
  28. data/vendor/assets/stylesheets/jquery.fileupload-ui.css.erb +12 -28
  29. metadata +5 -2
@@ -1,5 +1,5 @@
1
1
  /*
2
- * jQuery File Upload Plugin 5.26
2
+ * jQuery File Upload Plugin 5.32.0
3
3
  * https://github.com/blueimp/jQuery-File-Upload
4
4
  *
5
5
  * Copyright 2010, Sebastian Tschan
@@ -10,7 +10,7 @@
10
10
  */
11
11
 
12
12
  /*jslint nomen: true, unparam: true, regexp: true */
13
- /*global define, window, document, File, Blob, FormData, location */
13
+ /*global define, window, document, location, File, Blob, FormData */
14
14
 
15
15
  (function (factory) {
16
16
  'use strict';
@@ -27,12 +27,28 @@
27
27
  }(function ($) {
28
28
  'use strict';
29
29
 
30
+ // Detect file input support, based on
31
+ // http://viljamis.com/blog/2012/file-upload-support-on-mobile/
32
+ $.support.fileInput = !(new RegExp(
33
+ // Handle devices which give false positives for the feature detection:
34
+ '(Android (1\\.[0156]|2\\.[01]))' +
35
+ '|(Windows Phone (OS 7|8\\.0))|(XBLWP)|(ZuneWP)' +
36
+ '|(w(eb)?OSBrowser)|(webOS)' +
37
+ '|(Kindle/(1\\.0|2\\.[05]|3\\.0))'
38
+ ).test(window.navigator.userAgent) ||
39
+ // Feature detection for all other devices:
40
+ $('<input type="file">').prop('disabled'));
41
+
30
42
  // The FileReader API is not actually used, but works as feature detection,
31
43
  // as e.g. Safari supports XHR file uploads via the FormData API,
32
44
  // but not non-multipart XHR file uploads:
33
45
  $.support.xhrFileUpload = !!(window.XMLHttpRequestUpload && window.FileReader);
34
46
  $.support.xhrFormDataFileUpload = !!window.FormData;
35
47
 
48
+ // Detect support for Blob slicing (required for chunked uploads):
49
+ $.support.blobSlice = window.Blob && (Blob.prototype.slice ||
50
+ Blob.prototype.webkitSlice || Blob.prototype.mozSlice);
51
+
36
52
  // The fileupload widget listens for change events on file input fields defined
37
53
  // via fileInput setting and paste or drop events of the given dropZone.
38
54
  // In addition to the default jQuery Widget methods, the fileupload widget
@@ -115,6 +131,23 @@
115
131
  // By default, uploads are started automatically when adding files:
116
132
  autoUpload: true,
117
133
 
134
+ // Error and info messages:
135
+ messages: {
136
+ uploadedBytes: 'Uploaded bytes exceed file size'
137
+ },
138
+
139
+ // Translation function, gets the message key to be translated
140
+ // and an object with context specific data as arguments:
141
+ i18n: function (message, context) {
142
+ message = this.messages[message] || message.toString();
143
+ if (context) {
144
+ $.each(context, function (key, value) {
145
+ message = message.replace('{' + key + '}', value);
146
+ });
147
+ }
148
+ return message;
149
+ },
150
+
118
151
  // Additional form data to be sent along with the file uploads can be set
119
152
  // using this option, which accepts an array of objects with name and
120
153
  // value properties, a function returning such an array, a FormData
@@ -127,21 +160,25 @@
127
160
  // The add callback is invoked as soon as files are added to the fileupload
128
161
  // widget (via file input selection, drag & drop, paste or add API call).
129
162
  // If the singleFileUploads option is enabled, this callback will be
130
- // called once for each file in the selection for XHR file uplaods, else
163
+ // called once for each file in the selection for XHR file uploads, else
131
164
  // once for each file selection.
165
+ //
132
166
  // The upload starts when the submit method is invoked on the data parameter.
133
167
  // The data object contains a files property holding the added files
134
- // and allows to override plugin options as well as define ajax settings.
168
+ // and allows you to override plugin options as well as define ajax settings.
169
+ //
135
170
  // Listeners for this callback can also be bound the following way:
136
171
  // .bind('fileuploadadd', func);
172
+ //
137
173
  // data.submit() returns a Promise object and allows to attach additional
138
174
  // handlers using jQuery's Deferred callbacks:
139
175
  // data.submit().done(func).fail(func).always(func);
140
176
  add: function (e, data) {
141
177
  if (data.autoUpload || (data.autoUpload !== false &&
142
- ($(this).data('blueimp-fileupload') ||
143
- $(this).data('fileupload')).options.autoUpload)) {
144
- data.submit();
178
+ $(this).fileupload('option', 'autoUpload'))) {
179
+ data.process().done(function () {
180
+ data.submit();
181
+ });
145
182
  }
146
183
  },
147
184
 
@@ -205,8 +242,9 @@
205
242
  cache: false
206
243
  },
207
244
 
208
- // A list of options that require a refresh after assigning a new value:
209
- _refreshOptionsList: [
245
+ // A list of options that require reinitializing event listeners and/or
246
+ // special initialization code:
247
+ _specialOptions: [
210
248
  'fileInput',
211
249
  'dropZone',
212
250
  'pasteZone',
@@ -214,8 +252,13 @@
214
252
  'forceIframeTransport'
215
253
  ],
216
254
 
255
+ _blobSlice: $.support.blobSlice && function () {
256
+ var slice = this.slice || this.webkitSlice || this.mozSlice;
257
+ return slice.apply(this, arguments);
258
+ },
259
+
217
260
  _BitrateTimer: function () {
218
- this.timestamp = +(new Date());
261
+ this.timestamp = ((Date.now) ? Date.now() : (new Date()).getTime());
219
262
  this.loaded = 0;
220
263
  this.bitrate = 0;
221
264
  this.getBitrate = function (now, loaded, interval) {
@@ -243,7 +286,7 @@
243
286
  if ($.isArray(options.formData)) {
244
287
  return options.formData;
245
288
  }
246
- if (options.formData) {
289
+ if ($.type(options.formData) === 'object') {
247
290
  formData = [];
248
291
  $.each(options.formData, function (name, value) {
249
292
  formData.push({name: name, value: value});
@@ -262,16 +305,34 @@
262
305
  },
263
306
 
264
307
  _initProgressObject: function (obj) {
265
- obj._progress = {
308
+ var progress = {
266
309
  loaded: 0,
267
310
  total: 0,
268
311
  bitrate: 0
269
312
  };
313
+ if (obj._progress) {
314
+ $.extend(obj._progress, progress);
315
+ } else {
316
+ obj._progress = progress;
317
+ }
318
+ },
319
+
320
+ _initResponseObject: function (obj) {
321
+ var prop;
322
+ if (obj._response) {
323
+ for (prop in obj._response) {
324
+ if (obj._response.hasOwnProperty(prop)) {
325
+ delete obj._response[prop];
326
+ }
327
+ }
328
+ } else {
329
+ obj._response = {};
330
+ }
270
331
  },
271
332
 
272
333
  _onProgress: function (e, data) {
273
334
  if (e.lengthComputable) {
274
- var now = +(new Date()),
335
+ var now = ((Date.now) ? Date.now() : (new Date()).getTime()),
275
336
  loaded;
276
337
  if (data._time && data.progressInterval &&
277
338
  (now - data._time < data.progressInterval) &&
@@ -326,8 +387,14 @@
326
387
  }
327
388
  },
328
389
 
390
+ _isInstanceOf: function (type, obj) {
391
+ // Cross-frame instanceof check
392
+ return Object.prototype.toString.call(obj) === '[object ' + type + ']';
393
+ },
394
+
329
395
  _initXHRData: function (options) {
330
- var formData,
396
+ var that = this,
397
+ formData,
331
398
  file = options.files[0],
332
399
  // Ignore non-multipart setting if not supported:
333
400
  multipart = options.multipart || !$.support.xhrFileUpload,
@@ -336,9 +403,11 @@
336
403
  if (options.contentRange) {
337
404
  options.headers['Content-Range'] = options.contentRange;
338
405
  }
339
- if (!multipart) {
406
+ if (!multipart || options.blob || !this._isInstanceOf('File', file)) {
340
407
  options.headers['Content-Disposition'] = 'attachment; filename="' +
341
408
  encodeURI(file.name) + '"';
409
+ }
410
+ if (!multipart) {
342
411
  options.contentType = file.type;
343
412
  options.data = options.blob || file;
344
413
  } else if ($.support.xhrFormDataFileUpload) {
@@ -362,7 +431,7 @@
362
431
  });
363
432
  }
364
433
  } else {
365
- if (options.formData instanceof FormData) {
434
+ if (that._isInstanceOf('FormData', options.formData)) {
366
435
  formData = options.formData;
367
436
  } else {
368
437
  formData = new FormData();
@@ -371,17 +440,13 @@
371
440
  });
372
441
  }
373
442
  if (options.blob) {
374
- options.headers['Content-Disposition'] = 'attachment; filename="' +
375
- encodeURI(file.name) + '"';
376
443
  formData.append(paramName, options.blob, file.name);
377
444
  } else {
378
445
  $.each(options.files, function (index, file) {
379
- // Files are also Blob instances, but some browsers
380
- // (Firefox 3.6) support the File API but not Blobs.
381
446
  // This check allows the tests to run with
382
447
  // dummy objects:
383
- if ((window.Blob && file instanceof Blob) ||
384
- (window.File && file instanceof File)) {
448
+ if (that._isInstanceOf('File', file) ||
449
+ that._isInstanceOf('Blob', file)) {
385
450
  formData.append(
386
451
  options.paramName[index] || paramName,
387
452
  file,
@@ -398,13 +463,13 @@
398
463
  },
399
464
 
400
465
  _initIframeSettings: function (options) {
466
+ var targetHost = $('<a></a>').prop('href', options.url).prop('host');
401
467
  // Setting the dataType to iframe enables the iframe transport:
402
468
  options.dataType = 'iframe ' + (options.dataType || '');
403
469
  // The iframe transport accepts a serialized array as form data:
404
470
  options.formData = this._getFormData(options);
405
471
  // Add redirect url to form data on cross-domain uploads:
406
- if (options.redirect && $('<a></a>').prop('href', options.url)
407
- .prop('host') !== location.host) {
472
+ if (options.redirect && targetHost && targetHost !== location.host) {
408
473
  options.formData.push({
409
474
  name: options.redirectParamName || 'redirect',
410
475
  value: options.redirect
@@ -426,7 +491,7 @@
426
491
  options.dataType = 'postmessage ' + (options.dataType || '');
427
492
  }
428
493
  } else {
429
- this._initIframeSettings(options, 'iframe');
494
+ this._initIframeSettings(options);
430
495
  }
431
496
  },
432
497
 
@@ -526,9 +591,20 @@
526
591
  return this._enhancePromise(promise);
527
592
  },
528
593
 
529
- // Adds convenience methods to the callback arguments:
594
+ // Adds convenience methods to the data callback argument:
530
595
  _addConvenienceMethods: function (e, data) {
531
- var that = this;
596
+ var that = this,
597
+ getPromise = function (data) {
598
+ return $.Deferred().resolveWith(that, [data]).promise();
599
+ };
600
+ data.process = function (resolveFunc, rejectFunc) {
601
+ if (resolveFunc || rejectFunc) {
602
+ data._processQueue = this._processQueue =
603
+ (this._processQueue || getPromise(this))
604
+ .pipe(resolveFunc, rejectFunc);
605
+ }
606
+ return this._processQueue || getPromise(this);
607
+ };
532
608
  data.submit = function () {
533
609
  if (this.state() !== 'pending') {
534
610
  data.jqXHR = this.jqXHR =
@@ -541,16 +617,22 @@
541
617
  if (this.jqXHR) {
542
618
  return this.jqXHR.abort();
543
619
  }
544
- return this._getXHRPromise();
620
+ return that._getXHRPromise();
545
621
  };
546
622
  data.state = function () {
547
623
  if (this.jqXHR) {
548
624
  return that._getDeferredState(this.jqXHR);
549
625
  }
626
+ if (this._processQueue) {
627
+ return that._getDeferredState(this._processQueue);
628
+ }
550
629
  };
551
630
  data.progress = function () {
552
631
  return this._progress;
553
632
  };
633
+ data.response = function () {
634
+ return this._response;
635
+ };
554
636
  },
555
637
 
556
638
  // Parses the Range header from the server response
@@ -569,12 +651,13 @@
569
651
  // should be uploaded in chunks, but does not invoke any
570
652
  // upload requests:
571
653
  _chunkedUpload: function (options, testOnly) {
654
+ options.uploadedBytes = options.uploadedBytes || 0;
572
655
  var that = this,
573
656
  file = options.files[0],
574
657
  fs = file.size,
575
- ub = options.uploadedBytes = options.uploadedBytes || 0,
658
+ ub = options.uploadedBytes,
576
659
  mcs = options.maxChunkSize || fs,
577
- slice = file.slice || file.webkitSlice || file.mozSlice,
660
+ slice = this._blobSlice,
578
661
  dfd = $.Deferred(),
579
662
  promise = dfd.promise(),
580
663
  jqXHR,
@@ -587,7 +670,7 @@
587
670
  return true;
588
671
  }
589
672
  if (ub >= fs) {
590
- file.error = 'Uploaded bytes exceed file size';
673
+ file.error = options.i18n('uploadedBytes');
591
674
  return this._getXHRPromise(
592
675
  false,
593
676
  options.context,
@@ -623,7 +706,7 @@
623
706
  // Create a progress event if no final progress event
624
707
  // with loaded equaling total has been triggered
625
708
  // for this chunk:
626
- if (o._progress.loaded === currentLoaded) {
709
+ if (currentLoaded + o.chunkSize - o._progress.loaded) {
627
710
  that._onProgress($.Event('progress', {
628
711
  lengthComputable: true,
629
712
  loaded: ub - o.uploadedBytes,
@@ -679,9 +762,11 @@
679
762
  this._progress.loaded = this._progress.total = 0;
680
763
  this._progress.bitrate = 0;
681
764
  }
682
- if (!data._progress) {
683
- data._progress = {};
684
- }
765
+ // Make sure the container objects for the .response() and
766
+ // .progress() methods on the data object are available
767
+ // and reset to their initial state:
768
+ this._initResponseObject(data);
769
+ this._initProgressObject(data);
685
770
  data._progress.loaded = data.loaded = data.uploadedBytes || 0;
686
771
  data._progress.total = data.total = this._getTotal(data.files) || 1;
687
772
  data._progress.bitrate = data.bitrate = 0;
@@ -692,7 +777,8 @@
692
777
  },
693
778
 
694
779
  _onDone: function (result, textStatus, jqXHR, options) {
695
- var total = options._progress.total;
780
+ var total = options._progress.total,
781
+ response = options._response;
696
782
  if (options._progress.loaded < total) {
697
783
  // Create a progress event if no final progress event
698
784
  // with loaded equaling total has been triggered:
@@ -702,35 +788,30 @@
702
788
  total: total
703
789
  }), options);
704
790
  }
705
- options.result = result;
706
- options.textStatus = textStatus;
707
- options.jqXHR = jqXHR;
791
+ response.result = options.result = result;
792
+ response.textStatus = options.textStatus = textStatus;
793
+ response.jqXHR = options.jqXHR = jqXHR;
708
794
  this._trigger('done', null, options);
709
795
  },
710
796
 
711
797
  _onFail: function (jqXHR, textStatus, errorThrown, options) {
712
- options.jqXHR = jqXHR;
713
- options.textStatus = textStatus;
714
- options.errorThrown = errorThrown;
715
- this._trigger('fail', null, options);
798
+ var response = options._response;
716
799
  if (options.recalculateProgress) {
717
800
  // Remove the failed (error or abort) file upload from
718
801
  // the global progress calculation:
719
802
  this._progress.loaded -= options._progress.loaded;
720
803
  this._progress.total -= options._progress.total;
721
804
  }
805
+ response.jqXHR = options.jqXHR = jqXHR;
806
+ response.textStatus = options.textStatus = textStatus;
807
+ response.errorThrown = options.errorThrown = errorThrown;
808
+ this._trigger('fail', null, options);
722
809
  },
723
810
 
724
811
  _onAlways: function (jqXHRorResult, textStatus, jqXHRorError, options) {
725
812
  // jqXHRorResult, textStatus and jqXHRorError are added to the
726
813
  // options object via done and fail callbacks
727
- this._active -= 1;
728
814
  this._trigger('always', null, options);
729
- if (this._active === 0) {
730
- // The stop callback is triggered when all uploads have
731
- // been completed, equivalent to the global ajaxStop event:
732
- this._trigger('stop');
733
- }
734
815
  },
735
816
 
736
817
  _onSend: function (e, data) {
@@ -756,13 +837,14 @@
756
837
  }).fail(function (jqXHR, textStatus, errorThrown) {
757
838
  that._onFail(jqXHR, textStatus, errorThrown, options);
758
839
  }).always(function (jqXHRorResult, textStatus, jqXHRorError) {
759
- that._sending -= 1;
760
840
  that._onAlways(
761
841
  jqXHRorResult,
762
842
  textStatus,
763
843
  jqXHRorError,
764
844
  options
765
845
  );
846
+ that._sending -= 1;
847
+ that._active -= 1;
766
848
  if (options.limitConcurrentUploads &&
767
849
  options.limitConcurrentUploads > that._sending) {
768
850
  // Start the next queued upload,
@@ -776,6 +858,11 @@
776
858
  nextSlot = that._slots.shift();
777
859
  }
778
860
  }
861
+ if (that._active === 0) {
862
+ // The stop callback is triggered when all uploads have
863
+ // been completed, equivalent to the global ajaxStop event:
864
+ that._trigger('stop');
865
+ }
779
866
  });
780
867
  return jqXHR;
781
868
  };
@@ -788,7 +875,8 @@
788
875
  this._slots.push(slot);
789
876
  pipe = slot.pipe(send);
790
877
  } else {
791
- pipe = (this._sequence = this._sequence.pipe(send, send));
878
+ this._sequence = this._sequence.pipe(send, send);
879
+ pipe = this._sequence;
792
880
  }
793
881
  // Return the piped Promise object, enhanced with an abort method,
794
882
  // which is delegated to the jqXHR object of the current upload,
@@ -841,6 +929,7 @@
841
929
  var newData = $.extend({}, data);
842
930
  newData.files = fileSet ? element : [element];
843
931
  newData.paramName = paramNameSet[index];
932
+ that._initResponseObject(newData);
844
933
  that._initProgressObject(newData);
845
934
  that._addConvenienceMethods(e, newData);
846
935
  result = that._trigger('add', e, newData);
@@ -1019,44 +1108,50 @@
1019
1108
  },
1020
1109
 
1021
1110
  _onPaste: function (e) {
1022
- var cbd = e.originalEvent.clipboardData,
1023
- items = (cbd && cbd.items) || [],
1111
+ var items = e.originalEvent && e.originalEvent.clipboardData &&
1112
+ e.originalEvent.clipboardData.items,
1024
1113
  data = {files: []};
1025
- $.each(items, function (index, item) {
1026
- var file = item.getAsFile && item.getAsFile();
1027
- if (file) {
1028
- data.files.push(file);
1114
+ if (items && items.length) {
1115
+ $.each(items, function (index, item) {
1116
+ var file = item.getAsFile && item.getAsFile();
1117
+ if (file) {
1118
+ data.files.push(file);
1119
+ }
1120
+ });
1121
+ if (this._trigger('paste', e, data) === false ||
1122
+ this._onAdd(e, data) === false) {
1123
+ return false;
1029
1124
  }
1030
- });
1031
- if (this._trigger('paste', e, data) === false ||
1032
- this._onAdd(e, data) === false) {
1033
- return false;
1034
1125
  }
1035
1126
  },
1036
1127
 
1037
1128
  _onDrop: function (e) {
1129
+ e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer;
1038
1130
  var that = this,
1039
- dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer,
1131
+ dataTransfer = e.dataTransfer,
1040
1132
  data = {};
1041
1133
  if (dataTransfer && dataTransfer.files && dataTransfer.files.length) {
1042
1134
  e.preventDefault();
1135
+ this._getDroppedFiles(dataTransfer).always(function (files) {
1136
+ data.files = files;
1137
+ if (that._trigger('drop', e, data) !== false) {
1138
+ that._onAdd(e, data);
1139
+ }
1140
+ });
1043
1141
  }
1044
- this._getDroppedFiles(dataTransfer).always(function (files) {
1045
- data.files = files;
1046
- if (that._trigger('drop', e, data) !== false) {
1047
- that._onAdd(e, data);
1048
- }
1049
- });
1050
1142
  },
1051
1143
 
1052
1144
  _onDragOver: function (e) {
1053
- var dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer;
1054
- if (this._trigger('dragover', e) === false) {
1055
- return false;
1056
- }
1057
- if (dataTransfer && $.inArray('Files', dataTransfer.types) !== -1) {
1058
- dataTransfer.dropEffect = 'copy';
1059
- e.preventDefault();
1145
+ e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer;
1146
+ var dataTransfer = e.dataTransfer;
1147
+ if (dataTransfer) {
1148
+ if (this._trigger('dragover', e) === false) {
1149
+ return false;
1150
+ }
1151
+ if ($.inArray('Files', dataTransfer.types) !== -1) {
1152
+ dataTransfer.dropEffect = 'copy';
1153
+ e.preventDefault();
1154
+ }
1060
1155
  }
1061
1156
  },
1062
1157
 
@@ -1070,9 +1165,11 @@
1070
1165
  paste: this._onPaste
1071
1166
  });
1072
1167
  }
1073
- this._on(this.options.fileInput, {
1074
- change: this._onChange
1075
- });
1168
+ if ($.support.fileInput) {
1169
+ this._on(this.options.fileInput, {
1170
+ change: this._onChange
1171
+ });
1172
+ }
1076
1173
  },
1077
1174
 
1078
1175
  _destroyEventHandlers: function () {
@@ -1082,12 +1179,12 @@
1082
1179
  },
1083
1180
 
1084
1181
  _setOption: function (key, value) {
1085
- var refresh = $.inArray(key, this._refreshOptionsList) !== -1;
1086
- if (refresh) {
1182
+ var reinit = $.inArray(key, this._specialOptions) !== -1;
1183
+ if (reinit) {
1087
1184
  this._destroyEventHandlers();
1088
1185
  }
1089
1186
  this._super(key, value);
1090
- if (refresh) {
1187
+ if (reinit) {
1091
1188
  this._initSpecialOptions();
1092
1189
  this._initEventHandlers();
1093
1190
  }
@@ -1109,10 +1206,35 @@
1109
1206
  }
1110
1207
  },
1111
1208
 
1112
- _create: function () {
1113
- var options = this.options;
1209
+ _getRegExp: function (str) {
1210
+ var parts = str.split('/'),
1211
+ modifiers = parts.pop();
1212
+ parts.shift();
1213
+ return new RegExp(parts.join('/'), modifiers);
1214
+ },
1215
+
1216
+ _isRegExpOption: function (key, value) {
1217
+ return key !== 'url' && $.type(value) === 'string' &&
1218
+ /^\/.*\/[igm]{0,3}$/.test(value);
1219
+ },
1220
+
1221
+ _initDataAttributes: function () {
1222
+ var that = this,
1223
+ options = this.options;
1114
1224
  // Initialize options set via HTML5 data-attributes:
1115
- $.extend(options, $(this.element[0].cloneNode(false)).data());
1225
+ $.each(
1226
+ $(this.element[0].cloneNode(false)).data(),
1227
+ function (key, value) {
1228
+ if (that._isRegExpOption(key, value)) {
1229
+ value = that._getRegExp(value);
1230
+ }
1231
+ options[key] = value;
1232
+ }
1233
+ );
1234
+ },
1235
+
1236
+ _create: function () {
1237
+ this._initDataAttributes();
1116
1238
  this._initSpecialOptions();
1117
1239
  this._slots = [];
1118
1240
  this._sequence = this._getXHRPromise(true);
@@ -1121,6 +1243,12 @@
1121
1243
  this._initEventHandlers();
1122
1244
  },
1123
1245
 
1246
+ // This method is exposed to the widget API and allows to query
1247
+ // the number of active uploads:
1248
+ active: function () {
1249
+ return this._active;
1250
+ },
1251
+
1124
1252
  // This method is exposed to the widget API and allows to query
1125
1253
  // the widget upload progress.
1126
1254
  // It returns an object with loaded, total and bitrate properties