s3_cors_fileupload 0.2.0 → 0.2.1

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 (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