uploadbox 0.1.4 → 0.2.0.beta

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 62eaeadebe7535bad0f7073a427adae552ca5f71
4
- data.tar.gz: 2f60ac42239cde465eb8af3b4b93aa10d9e38df6
3
+ metadata.gz: ceb2ca3efe6cd4d8445e02f68131325e8afd0f62
4
+ data.tar.gz: a3c2e0d3bad7ccd9b3b5f48c7f6b906d04e13545
5
5
  SHA512:
6
- metadata.gz: 1cee6a89123479d9ce8c2f3f964ac41a3deea5f303baccfa8db42daf576636fa2cc2721530bbcef671776ca7a7668335c3cb8ab6d9d9d9d417f510974d61d06f
7
- data.tar.gz: 4efde4d34b35e777d9fe107fd603df55271ff4b2e986b51b4a078f0afa3b18296b69eb667a82509afca2ba2441665bcc145c5bdbc520b5a05b26419bbb239a4c
6
+ metadata.gz: 5d82e4d94916edb0360dbde08f0abeca13abd56dcc71d91c9bd5238271ee06a4a0e7451896f8131bb1127f65684874e46ccb43d2ed26583a6cccc1bc66e41f2b
7
+ data.tar.gz: 99e05a4feed7f68e4937257679394877a57258fb6e5a755799339f080092bad9ee13d2fb0dfa6cbe6d428f8835ce5840c8d1356366a6acb218ee5229ae08c96c
data/README.md CHANGED
@@ -14,7 +14,7 @@ Make sure you have [ImageMagick](http://www.imagemagick.org/) installed.
14
14
 
15
15
  Add to Gemfile
16
16
  ```
17
- gem 'uploadbox', '0.1.3'
17
+ gem 'uploadbox', '0.1.4'
18
18
  ```
19
19
 
20
20
  Run generators
@@ -123,4 +123,12 @@ S3_BUCKET=uploads-production \
123
123
  Add Redis
124
124
  ```
125
125
  heroku addons:add rediscloud:20
126
- ```
126
+ ```
127
+
128
+ ## Upgrade from 0.1.x
129
+ If are upgrading from 0.1.x you will need to create a migration to add a column named `original_file` to the `images` table
130
+ ```
131
+ rails g migration add_original_file_to_images original_file:string
132
+ rake db:migrate
133
+ ```
134
+
@@ -1,11 +1,11 @@
1
1
  class @GalleryUploader
2
- constructor: (@container) ->
2
+ constructor: (@container) ->
3
3
  @previews = {}
4
4
 
5
5
  @container.find('input[type="file"]:first').show().fileupload
6
6
  type: 'POST'
7
7
  dataType: 'xml'
8
- replaceFileInput: false
8
+ replaceFileInput: false
9
9
  autoUpload: true
10
10
  formData: @getFormData
11
11
  dropZone: @container
@@ -18,7 +18,7 @@ class @GalleryUploader
18
18
  add: (e, data) =>
19
19
  if @loader
20
20
  @loader.detach()
21
-
21
+
22
22
  if @verifyProcessingInterval
23
23
  clearInterval(@verifyProcessingInterval)
24
24
 
@@ -59,6 +59,6 @@ class @GalleryUploader
59
59
 
60
60
  nextPreview: =>
61
61
  @currentIndex ||= 0
62
- key = Object.keys(@previews)[@currentIndex]
62
+ key = Object.keys(@previews)[@currentIndex]
63
63
  @currentIndex++
64
64
  @previews[key]
@@ -30,12 +30,32 @@ class @ImageUploader
30
30
  clearInterval(@verifyProcessingInterval)
31
31
 
32
32
  if @file.type.match /gif|jpe?g|png/
33
- @loader = $('<div class="progress progress-striped active"><div class="bar" style="width: 0%;"></div></div>').hide()
34
- @preview.prepend(@loader.fadeIn())
33
+ loadImage @file, @appendThumb, {
34
+ maxWidth: @thumbContainer.data('width'),
35
+ maxHeight: @thumbContainer.data('height'),
36
+ minWidth: @thumbContainer.data('width'),
37
+ minHeight: @thumbContainer.data('height'),
38
+ canvas: true,
39
+ crop: true,
40
+ orientation: true
41
+ }
42
+
43
+ @loader = $('<div><div class="progress progress-striped active"><div class="bar" style="width: 0%;"></div></div><div class="uploader-overlay"></div></div>').hide()
44
+ @loader.find('.uploader-overlay').height(@thumbContainer.data('height'))
45
+ @preview.prepend(@loader)
46
+ @loader.show()
47
+
35
48
  data.submit()
36
49
  @container.find('.fileupload').removeClass('processing').addClass('uploading')
37
50
  @container.closest('form').find('[type=submit]').attr("disabled", true)
38
51
 
52
+ appendThumb: (img) =>
53
+ @thumbContainer.html('')
54
+ $(img).hide()
55
+ @container.find('.fileupload-preview.preview').append($(img).show())
56
+ @container.find('.fileupload').removeClass('fileupload-new').addClass('fileupload-uploading')
57
+ @loader.show()
58
+
39
59
  getFormData: (arg) =>
40
60
  file = @file
41
61
  @filePath = @container.find('input[name="key"]').val() + file.name
@@ -55,6 +75,7 @@ class @ImageUploader
55
75
 
56
76
  done: (e, data) =>
57
77
  @container.find('.fileupload').removeClass('uploading').addClass('processing')
78
+
58
79
  $.ajax
59
80
  type: 'POST'
60
81
  url: @fileInput.data('callback-url')
@@ -65,7 +86,7 @@ class @ImageUploader
65
86
  'image[secure_random]': @fileInput.data('secure-random')
66
87
 
67
88
  complete: =>
68
- @verifyProcessingInterval = setInterval(@verifyProcessing, 5000)
89
+ @verifyProcessingInterval = setInterval(@verifyProcessing, 500)
69
90
 
70
91
  error: =>
71
92
  @loader.detach()
@@ -80,10 +101,10 @@ class @ImageUploader
80
101
  dataType: 'json'
81
102
  url: @fileInput.data('find-url')
82
103
  data:
83
- 'name': filename
84
- 'imageable_type': @typeInput.val()
85
- 'upload_name': @uploadNameInput.val()
86
- 'secure_random': @fileInput.data('secure-random')
104
+ image:
105
+ 'imageable_type': @typeInput.val()
106
+ 'upload_name': @uploadNameInput.val()
107
+ 'secure_random': @fileInput.data('secure-random')
87
108
 
88
109
  complete: (data) =>
89
110
  if data.responseJSON.hasOwnProperty('id')
@@ -107,16 +128,11 @@ class @ImageUploader
107
128
  @container.closest('form').find('[type=submit]').attr("disabled", false)
108
129
 
109
130
  showThumb: (image) =>
110
- console.log 'showThumb'
111
- @loader.detach()
131
+ @loader.fadeOut =>
132
+ @loader.detach()
112
133
  @idInput.val(image.id)
113
134
  @container.find('a.btn.fileupload-exists').attr('href', image.url)
114
135
  @thumbContainer.find('img').detach()
115
- img = $('<img/>')
116
- img.attr('src', image.versions[@thumbContainer.data('version')])
117
- img.attr('width', @thumbContainer.data('width'))
118
- img.attr('height', @thumbContainer.data('height')).hide()
119
- @container.find('.fileupload-preview.preview').append(img.fadeIn())
120
136
  @container.find('.fileupload').removeClass('fileupload-new').addClass('fileupload-exists')
121
137
  @container.find('.fileupload').removeClass('uploading').removeClass('processing')
122
138
  @container.closest('form').find('[type=submit]').attr("disabled", false)
@@ -0,0 +1,15 @@
1
+ class @ShowImage
2
+ constructor: (@container) ->
3
+ if @container.data('processing') == true
4
+ loadImage @container.data('original'), @append, {
5
+ maxWidth: @container.find('img').attr('width'),
6
+ maxHeight: @container.find('img').attr('height'),
7
+ minWidth: @container.find('img').attr('width'),
8
+ minHeight: @container.find('img').attr('height'),
9
+ canvas: true,
10
+ crop: true,
11
+ orientation: true
12
+ }
13
+ append: (img) =>
14
+ @container.append(img)
15
+
@@ -1,15 +1,16 @@
1
1
  class @UploaderPreview
2
2
  constructor: (@container, @file) ->
3
- @loader = $('<div class="progress progress-striped active"><div class="bar" style="width: 0%;"></div></div>').hide()
3
+ @thumbContainer = @container.find('.fileupload-preview.thumbnail')
4
+ @loader = $('<div><div class="progress progress-striped active"><div class="bar" style="width: 0%;"></div></div><div class="uploader-overlay"></div></div>').hide()
4
5
 
5
6
  @startLoader() if @isUploadStarting()
6
-
7
+
7
8
  @container.find('a.btn.fileupload-exists').bind('ajax:success', @delete)
8
9
 
9
10
  @container.find('input[type="file"]:first').show().fileupload
10
11
  type: 'POST'
11
12
  dataType: 'xml'
12
- replaceFileInput: false
13
+ replaceFileInput: false
13
14
  autoUpload: true
14
15
  formData: @getFormData
15
16
  dropZone: @container
@@ -26,28 +27,44 @@ class @UploaderPreview
26
27
  @thumbContainer = @container.find('.fileupload-preview.thumbnail')
27
28
 
28
29
  startLoader: =>
29
- @container.prepend(@loader.fadeIn())
30
+ loadImage @file, @appendThumb, {
31
+ maxWidth: @thumbContainer.data('width'),
32
+ maxHeight: @thumbContainer.data('height'),
33
+ minWidth: @thumbContainer.data('width'),
34
+ minHeight: @thumbContainer.data('height'),
35
+ canvas: true,
36
+ crop: true,
37
+ orientation: true
38
+ }
39
+ @loader.find('.uploader-overlay').height(@thumbContainer.data('height'))
40
+ @container.prepend(@loader.show())
30
41
  @container.find('.fileupload').removeClass('processing').addClass('uploading')
31
42
 
32
-
33
43
  add: (e, data) =>
34
44
  @file = data.files[0]
35
45
 
36
46
  if @loader
37
47
  @loader.detach()
38
-
48
+
39
49
  if @verifyProcessingInterval
40
50
  clearInterval(@verifyProcessingInterval)
41
51
 
42
52
  if @file.type.match /gif|jpe?g|png/
53
+
43
54
  data.context = @id()
44
55
  @startLoader()
45
56
  @container.closest('form').find('[type=submit]').attr("disabled", true)
46
57
  data.submit()
47
58
 
59
+ appendThumb: (img) =>
60
+ @thumbContainer.html('')
61
+ @thumbContainer.append(img)
62
+ @container.find('.fileupload').removeClass('fileupload-new').addClass('fileupload-uploading')
63
+ @loader.show()
64
+
48
65
  getFormData: =>
49
66
  @filePath = "uploads/#{@id()}/" + @file.name
50
-
67
+
51
68
  [
52
69
  { name: 'key', value: @filePath },
53
70
  { name: 'acl', value: @container.find('input[name="acl"]').val() },
@@ -58,7 +75,7 @@ class @UploaderPreview
58
75
  { name: 'file', value: @file }
59
76
  ]
60
77
 
61
-
78
+
62
79
  isUploadStarting: =>
63
80
  @container.find('.fileupload').hasClass('fileupload-new')
64
81
 
@@ -71,18 +88,20 @@ class @UploaderPreview
71
88
 
72
89
  done: (data) =>
73
90
  @container.find('.fileupload').removeClass('uploading').addClass('processing')
91
+ @container.closest('form').find('[type=submit]').attr("disabled", false)
92
+
74
93
  $.ajax
75
94
  type: 'POST'
76
95
  url: @fileInput.data('callback-url')
77
- data:
96
+ data:
78
97
  'image[remote_file_url]': @fileInput.data('url') + @filePath
79
98
  'image[imageable_type]': @typeInput.val()
80
99
  'image[upload_name]': @uploadNameInput.val()
81
100
  'image[secure_random]': @id()
82
-
101
+
83
102
  complete: =>
84
- @verifyProcessingInterval = setInterval(@verifyProcessing, 5000)
85
-
103
+ @verifyProcessingInterval = setInterval(@verifyProcessing, 500)
104
+
86
105
  error: =>
87
106
  @loader.detach()
88
107
  @container.find('.fileupload').removeClass('uploading').removeClass('processing')
@@ -96,33 +115,28 @@ class @UploaderPreview
96
115
  dataType: 'json'
97
116
  url: @fileInput.data('find-url')
98
117
  data:
99
- 'name': filename
100
- 'imageable_type': @typeInput.val()
101
- 'upload_name': @uploadNameInput.val()
102
- 'secure_random': @id()
118
+ image:
119
+ imageable_type: @typeInput.val()
120
+ upload_name: @uploadNameInput.val()
121
+ secure_random: @id()
103
122
 
104
123
  complete: (data) =>
105
124
  if data.responseJSON.hasOwnProperty('id')
106
125
  clearInterval(@verifyProcessingInterval)
107
126
  @showThumb(data.responseJSON)
108
-
127
+
109
128
  error: =>
110
129
  @loader.detach()
111
130
  @container.detach()
112
131
 
113
132
  showThumb: (image) =>
114
- @loader.detach()
133
+ console.log 'showThumb'
134
+ @loader.fadeOut =>
135
+ @loader.detach()
115
136
  @idInput.val(image.id)
116
137
  @container.find('a.btn.fileupload-exists').attr('href', image.url)
117
- @thumbContainer.find('img').detach()
118
- img = $('<img/>')
119
- img.attr('src', image.versions[@thumbContainer.data('version')])
120
- img.attr('width', @thumbContainer.data('width'))
121
- img.attr('height', @thumbContainer.data('height')).hide()
122
- @container.find('.fileupload-preview.thumbnail').append(img.fadeIn())
123
- @container.find('.fileupload').removeClass('fileupload-new').addClass('fileupload-exists')
138
+ @container.find('.fileupload').removeClass('fileupload-new').removeClass('fileupload-uploading').addClass('fileupload-exists')
124
139
  @container.find('.fileupload').removeClass('uploading').removeClass('processing')
125
- @container.closest('form').find('[type=submit]').attr("disabled", false)
126
140
 
127
141
  fail: =>
128
142
  @loader.detach()
@@ -0,0 +1 @@
1
+ !function(a){"use strict";var b=a.HTMLCanvasElement&&a.HTMLCanvasElement.prototype,c=a.Blob&&function(){try{return Boolean(new Blob)}catch(a){return!1}}(),d=c&&a.Uint8Array&&function(){try{return 100===new Blob([new Uint8Array(100)]).size}catch(a){return!1}}(),e=a.BlobBuilder||a.WebKitBlobBuilder||a.MozBlobBuilder||a.MSBlobBuilder,f=(c||e)&&a.atob&&a.ArrayBuffer&&a.Uint8Array&&function(a){var b,f,g,h,i,j;for(b=a.split(",")[0].indexOf("base64")>=0?atob(a.split(",")[1]):decodeURIComponent(a.split(",")[1]),f=new ArrayBuffer(b.length),g=new Uint8Array(f),h=0;h<b.length;h+=1)g[h]=b.charCodeAt(h);return i=a.split(",")[0].split(":")[1].split(";")[0],c?new Blob([d?g:f],{type:i}):(j=new e,j.append(f),j.getBlob(i))};a.HTMLCanvasElement&&!b.toBlob&&(b.mozGetAsFile?b.toBlob=function(a,c,d){d&&b.toDataURL&&f?a(f(this.toDataURL(c,d))):a(this.mozGetAsFile("blob",c))}:b.toDataURL&&f&&(b.toBlob=function(a,b,c){a(f(this.toDataURL(b,c)))})),"function"==typeof define&&define.amd?define(function(){return f}):a.dataURLtoBlob=f}(this);
@@ -1,5 +1,5 @@
1
1
  /*
2
- * jQuery File Upload Plugin 5.32.3
2
+ * jQuery File Upload Plugin 5.40.1
3
3
  * https://github.com/blueimp/jQuery-File-Upload
4
4
  *
5
5
  * Copyright 2010, Sebastian Tschan
@@ -9,8 +9,8 @@
9
9
  * http://www.opensource.org/licenses/MIT
10
10
  */
11
11
 
12
- /*jslint nomen: true, unparam: true, regexp: true */
13
- /*global define, window, document, location, File, Blob, FormData */
12
+ /* jshint nomen:false */
13
+ /* global define, window, document, location, Blob, FormData */
14
14
 
15
15
  (function (factory) {
16
16
  'use strict';
@@ -40,9 +40,11 @@
40
40
  $('<input type="file">').prop('disabled'));
41
41
 
42
42
  // The FileReader API is not actually used, but works as feature detection,
43
- // as e.g. Safari supports XHR file uploads via the FormData API,
44
- // but not non-multipart XHR file uploads:
45
- $.support.xhrFileUpload = !!(window.XMLHttpRequestUpload && window.FileReader);
43
+ // as some Safari versions (5?) support XHR file uploads via the FormData API,
44
+ // but not non-multipart XHR file uploads.
45
+ // window.XMLHttpRequestUpload is not available on IE10, so we check for
46
+ // window.ProgressEvent instead to detect XHR2 file upload capability:
47
+ $.support.xhrFileUpload = !!(window.ProgressEvent && window.FileReader);
46
48
  $.support.xhrFormDataFileUpload = !!window.FormData;
47
49
 
48
50
  // Detect support for Blob slicing (required for chunked uploads):
@@ -88,6 +90,14 @@
88
90
  // To limit the number of files uploaded with one XHR request,
89
91
  // set the following option to an integer greater than 0:
90
92
  limitMultiFileUploads: undefined,
93
+ // The following option limits the number of files uploaded with one
94
+ // XHR request to keep the request size under or equal to the defined
95
+ // limit in bytes:
96
+ limitMultiFileUploadSize: undefined,
97
+ // Multipart file uploads add a number of bytes to each uploaded file,
98
+ // therefore the following option adds an overhead for each file used
99
+ // in the limitMultiFileUploadSize configuration:
100
+ limitMultiFileUploadSizeOverhead: 512,
91
101
  // Set the following option to true to issue all file upload requests
92
102
  // in a sequential order:
93
103
  sequentialUploads: false,
@@ -174,6 +184,9 @@
174
184
  // handlers using jQuery's Deferred callbacks:
175
185
  // data.submit().done(func).fail(func).always(func);
176
186
  add: function (e, data) {
187
+ if (e.isDefaultPrevented()) {
188
+ return false;
189
+ }
177
190
  if (data.autoUpload || (data.autoUpload !== false &&
178
191
  $(this).fileupload('option', 'autoUpload'))) {
179
192
  data.process().done(function () {
@@ -280,7 +293,7 @@
280
293
 
281
294
  _getFormData: function (options) {
282
295
  var formData;
283
- if (typeof options.formData === 'function') {
296
+ if ($.type(options.formData) === 'function') {
284
297
  return options.formData(options.form);
285
298
  }
286
299
  if ($.isArray(options.formData)) {
@@ -360,10 +373,18 @@
360
373
  // Trigger a custom progress event with a total data property set
361
374
  // to the file size(s) of the current upload and a loaded data
362
375
  // property calculated accordingly:
363
- this._trigger('progress', e, data);
376
+ this._trigger(
377
+ 'progress',
378
+ $.Event('progress', {delegatedEvent: e}),
379
+ data
380
+ );
364
381
  // Trigger a global progress event for all current file uploads,
365
382
  // including ajax calls queued for sequential file uploads:
366
- this._trigger('progressall', e, this._progress);
383
+ this._trigger(
384
+ 'progressall',
385
+ $.Event('progressall', {delegatedEvent: e}),
386
+ this._progress
387
+ );
367
388
  }
368
389
  },
369
390
 
@@ -398,7 +419,8 @@
398
419
  file = options.files[0],
399
420
  // Ignore non-multipart setting if not supported:
400
421
  multipart = options.multipart || !$.support.xhrFileUpload,
401
- paramName = options.paramName[0];
422
+ paramName = $.type(options.paramName) === 'array' ?
423
+ options.paramName[0] : options.paramName;
402
424
  options.headers = $.extend({}, options.headers);
403
425
  if (options.contentRange) {
404
426
  options.headers['Content-Range'] = options.contentRange;
@@ -408,7 +430,7 @@
408
430
  encodeURI(file.name) + '"';
409
431
  }
410
432
  if (!multipart) {
411
- options.contentType = file.type;
433
+ options.contentType = file.type || 'application/octet-stream';
412
434
  options.data = options.blob || file;
413
435
  } else if ($.support.xhrFormDataFileUpload) {
414
436
  if (options.postMessage) {
@@ -425,7 +447,8 @@
425
447
  } else {
426
448
  $.each(options.files, function (index, file) {
427
449
  formData.push({
428
- name: options.paramName[index] || paramName,
450
+ name: ($.type(options.paramName) === 'array' &&
451
+ options.paramName[index]) || paramName,
429
452
  value: file
430
453
  });
431
454
  });
@@ -448,9 +471,10 @@
448
471
  if (that._isInstanceOf('File', file) ||
449
472
  that._isInstanceOf('Blob', file)) {
450
473
  formData.append(
451
- options.paramName[index] || paramName,
474
+ ($.type(options.paramName) === 'array' &&
475
+ options.paramName[index]) || paramName,
452
476
  file,
453
- file.name
477
+ file.uploadName || file.name
454
478
  );
455
479
  }
456
480
  });
@@ -534,8 +558,10 @@
534
558
  options.url = options.form.prop('action') || location.href;
535
559
  }
536
560
  // The HTTP request method must be "POST" or "PUT":
537
- options.type = (options.type || options.form.prop('method') || '')
538
- .toUpperCase();
561
+ options.type = (options.type ||
562
+ ($.type(options.form.prop('method')) === 'string' &&
563
+ options.form.prop('method')) || ''
564
+ ).toUpperCase();
539
565
  if (options.type !== 'POST' && options.type !== 'PUT' &&
540
566
  options.type !== 'PATCH') {
541
567
  options.type = 'POST';
@@ -594,22 +620,32 @@
594
620
  // Adds convenience methods to the data callback argument:
595
621
  _addConvenienceMethods: function (e, data) {
596
622
  var that = this,
597
- getPromise = function (data) {
598
- return $.Deferred().resolveWith(that, [data]).promise();
623
+ getPromise = function (args) {
624
+ return $.Deferred().resolveWith(that, args).promise();
599
625
  };
600
626
  data.process = function (resolveFunc, rejectFunc) {
601
627
  if (resolveFunc || rejectFunc) {
602
628
  data._processQueue = this._processQueue =
603
- (this._processQueue || getPromise(this))
604
- .pipe(resolveFunc, rejectFunc);
629
+ (this._processQueue || getPromise([this])).pipe(
630
+ function () {
631
+ if (data.errorThrown) {
632
+ return $.Deferred()
633
+ .rejectWith(that, [data]).promise();
634
+ }
635
+ return getPromise(arguments);
636
+ }
637
+ ).pipe(resolveFunc, rejectFunc);
605
638
  }
606
- return this._processQueue || getPromise(this);
639
+ return this._processQueue || getPromise([this]);
607
640
  };
608
641
  data.submit = function () {
609
642
  if (this.state() !== 'pending') {
610
643
  data.jqXHR = this.jqXHR =
611
- (that._trigger('submit', e, this) !== false) &&
612
- that._onSend(e, this);
644
+ (that._trigger(
645
+ 'submit',
646
+ $.Event('submit', {delegatedEvent: e}),
647
+ this
648
+ ) !== false) && that._onSend(e, this);
613
649
  }
614
650
  return this.jqXHR || that._getXHRPromise();
615
651
  };
@@ -617,7 +653,9 @@
617
653
  if (this.jqXHR) {
618
654
  return this.jqXHR.abort();
619
655
  }
620
- return that._getXHRPromise();
656
+ this.errorThrown = 'abort';
657
+ that._trigger('fail', null, this);
658
+ return that._getXHRPromise(false);
621
659
  };
622
660
  data.state = function () {
623
661
  if (this.jqXHR) {
@@ -627,6 +665,10 @@
627
665
  return that._getDeferredState(this._processQueue);
628
666
  }
629
667
  };
668
+ data.processing = function () {
669
+ return !this.jqXHR && this._processQueue && that
670
+ ._getDeferredState(this._processQueue) === 'pending';
671
+ };
630
672
  data.progress = function () {
631
673
  return this._progress;
632
674
  };
@@ -829,7 +871,11 @@
829
871
  // Set timer for bitrate progress calculation:
830
872
  options._bitrateTimer = new that._BitrateTimer();
831
873
  jqXHR = jqXHR || (
832
- ((aborted || that._trigger('send', e, options) === false) &&
874
+ ((aborted || that._trigger(
875
+ 'send',
876
+ $.Event('send', {delegatedEvent: e}),
877
+ options
878
+ ) === false) &&
833
879
  that._getXHRPromise(false, options.context, aborted)) ||
834
880
  that._chunkedUpload(options) || $.ajax(options)
835
881
  ).done(function (result, textStatus, jqXHR) {
@@ -900,39 +946,70 @@
900
946
  var that = this,
901
947
  result = true,
902
948
  options = $.extend({}, this.options, data),
949
+ files = data.files,
950
+ filesLength = files.length,
903
951
  limit = options.limitMultiFileUploads,
952
+ limitSize = options.limitMultiFileUploadSize,
953
+ overhead = options.limitMultiFileUploadSizeOverhead,
954
+ batchSize = 0,
904
955
  paramName = this._getParamName(options),
905
956
  paramNameSet,
906
957
  paramNameSlice,
907
958
  fileSet,
908
- i;
909
- if (!(options.singleFileUploads || limit) ||
959
+ i,
960
+ j = 0;
961
+ if (limitSize && (!filesLength || files[0].size === undefined)) {
962
+ limitSize = undefined;
963
+ }
964
+ if (!(options.singleFileUploads || limit || limitSize) ||
910
965
  !this._isXHRUpload(options)) {
911
- fileSet = [data.files];
966
+ fileSet = [files];
912
967
  paramNameSet = [paramName];
913
- } else if (!options.singleFileUploads && limit) {
968
+ } else if (!(options.singleFileUploads || limitSize) && limit) {
914
969
  fileSet = [];
915
970
  paramNameSet = [];
916
- for (i = 0; i < data.files.length; i += limit) {
917
- fileSet.push(data.files.slice(i, i + limit));
971
+ for (i = 0; i < filesLength; i += limit) {
972
+ fileSet.push(files.slice(i, i + limit));
918
973
  paramNameSlice = paramName.slice(i, i + limit);
919
974
  if (!paramNameSlice.length) {
920
975
  paramNameSlice = paramName;
921
976
  }
922
977
  paramNameSet.push(paramNameSlice);
923
978
  }
979
+ } else if (!options.singleFileUploads && limitSize) {
980
+ fileSet = [];
981
+ paramNameSet = [];
982
+ for (i = 0; i < filesLength; i = i + 1) {
983
+ batchSize += files[i].size + overhead;
984
+ if (i + 1 === filesLength ||
985
+ ((batchSize + files[i + 1].size + overhead) > limitSize) ||
986
+ (limit && i + 1 - j >= limit)) {
987
+ fileSet.push(files.slice(j, i + 1));
988
+ paramNameSlice = paramName.slice(j, i + 1);
989
+ if (!paramNameSlice.length) {
990
+ paramNameSlice = paramName;
991
+ }
992
+ paramNameSet.push(paramNameSlice);
993
+ j = i + 1;
994
+ batchSize = 0;
995
+ }
996
+ }
924
997
  } else {
925
998
  paramNameSet = paramName;
926
999
  }
927
- data.originalFiles = data.files;
928
- $.each(fileSet || data.files, function (index, element) {
1000
+ data.originalFiles = files;
1001
+ $.each(fileSet || files, function (index, element) {
929
1002
  var newData = $.extend({}, data);
930
1003
  newData.files = fileSet ? element : [element];
931
1004
  newData.paramName = paramNameSet[index];
932
1005
  that._initResponseObject(newData);
933
1006
  that._initProgressObject(newData);
934
1007
  that._addConvenienceMethods(e, newData);
935
- result = that._trigger('add', e, newData);
1008
+ result = that._trigger(
1009
+ 'add',
1010
+ $.Event('add', {delegatedEvent: e}),
1011
+ newData
1012
+ );
936
1013
  return result;
937
1014
  });
938
1015
  return result;
@@ -1101,7 +1178,11 @@
1101
1178
  if (that.options.replaceFileInput) {
1102
1179
  that._replaceFileInput(data.fileInput);
1103
1180
  }
1104
- if (that._trigger('change', e, data) !== false) {
1181
+ if (that._trigger(
1182
+ 'change',
1183
+ $.Event('change', {delegatedEvent: e}),
1184
+ data
1185
+ ) !== false) {
1105
1186
  that._onAdd(e, data);
1106
1187
  }
1107
1188
  });
@@ -1118,9 +1199,12 @@
1118
1199
  data.files.push(file);
1119
1200
  }
1120
1201
  });
1121
- if (this._trigger('paste', e, data) === false ||
1122
- this._onAdd(e, data) === false) {
1123
- return false;
1202
+ if (this._trigger(
1203
+ 'paste',
1204
+ $.Event('paste', {delegatedEvent: e}),
1205
+ data
1206
+ ) !== false) {
1207
+ this._onAdd(e, data);
1124
1208
  }
1125
1209
  }
1126
1210
  },
@@ -1134,7 +1218,11 @@
1134
1218
  e.preventDefault();
1135
1219
  this._getDroppedFiles(dataTransfer).always(function (files) {
1136
1220
  data.files = files;
1137
- if (that._trigger('drop', e, data) !== false) {
1221
+ if (that._trigger(
1222
+ 'drop',
1223
+ $.Event('drop', {delegatedEvent: e}),
1224
+ data
1225
+ ) !== false) {
1138
1226
  that._onAdd(e, data);
1139
1227
  }
1140
1228
  });
@@ -1144,14 +1232,13 @@
1144
1232
  _onDragOver: function (e) {
1145
1233
  e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer;
1146
1234
  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
- }
1235
+ if (dataTransfer && $.inArray('Files', dataTransfer.types) !== -1 &&
1236
+ this._trigger(
1237
+ 'dragover',
1238
+ $.Event('dragover', {delegatedEvent: e})
1239
+ ) !== false) {
1240
+ e.preventDefault();
1241
+ dataTransfer.dropEffect = 'copy';
1155
1242
  }
1156
1243
  },
1157
1244
 
@@ -1220,15 +1307,21 @@
1220
1307
 
1221
1308
  _initDataAttributes: function () {
1222
1309
  var that = this,
1223
- options = this.options;
1310
+ options = this.options,
1311
+ clone = $(this.element[0].cloneNode(false));
1224
1312
  // Initialize options set via HTML5 data-attributes:
1225
1313
  $.each(
1226
- $(this.element[0].cloneNode(false)).data(),
1314
+ clone.data(),
1227
1315
  function (key, value) {
1228
- if (that._isRegExpOption(key, value)) {
1229
- value = that._getRegExp(value);
1316
+ var dataAttributeName = 'data-' +
1317
+ // Convert camelCase to hyphen-ated key:
1318
+ key.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
1319
+ if (clone.attr(dataAttributeName)) {
1320
+ if (that._isRegExpOption(key, value)) {
1321
+ value = that._getRegExp(value);
1322
+ }
1323
+ options[key] = value;
1230
1324
  }
1231
- options[key] = value;
1232
1325
  }
1233
1326
  );
1234
1327
  },
@@ -1,5 +1,5 @@
1
1
  /*
2
- * jQuery Iframe Transport Plugin 1.7
2
+ * jQuery Iframe Transport Plugin 1.8.2
3
3
  * https://github.com/blueimp/jQuery-File-Upload
4
4
  *
5
5
  * Copyright 2011, Sebastian Tschan
@@ -9,8 +9,7 @@
9
9
  * http://www.opensource.org/licenses/MIT
10
10
  */
11
11
 
12
- /*jslint unparam: true, nomen: true */
13
- /*global define, window, document */
12
+ /* global define, window, document */
14
13
 
15
14
  (function (factory) {
16
15
  'use strict';
@@ -27,7 +26,7 @@
27
26
  // Helper variable to create unique names for the transport iframes:
28
27
  var counter = 0;
29
28
 
30
- // The iframe transport accepts three additional options:
29
+ // The iframe transport accepts four additional options:
31
30
  // options.fileInput: a jQuery collection of file input fields
32
31
  // options.paramName: the parameter name for the file form data,
33
32
  // overrides the name property of the file input field(s),
@@ -35,9 +34,16 @@
35
34
  // options.formData: an array of objects with name and value properties,
36
35
  // equivalent to the return data of .serializeArray(), e.g.:
37
36
  // [{name: 'a', value: 1}, {name: 'b', value: 2}]
37
+ // options.initialIframeSrc: the URL of the initial iframe src,
38
+ // by default set to "javascript:false;"
38
39
  $.ajaxTransport('iframe', function (options) {
39
40
  if (options.async) {
40
- var form,
41
+ // javascript:false as initial iframe src
42
+ // prevents warning popups on HTTPS in IE6:
43
+ /*jshint scripturl: true */
44
+ var initialIframeSrc = options.initialIframeSrc || 'javascript:false;',
45
+ /*jshint scripturl: false */
46
+ form,
41
47
  iframe,
42
48
  addParamChar;
43
49
  return {
@@ -56,15 +62,13 @@
56
62
  options.url = options.url + addParamChar + '_method=PATCH';
57
63
  options.type = 'POST';
58
64
  }
59
- // javascript:false as initial iframe src
60
- // prevents warning popups on HTTPS in IE6.
61
65
  // IE versions below IE8 cannot set the name property of
62
66
  // elements that have already been added to the DOM,
63
67
  // so we set the name along with the iframe HTML markup:
64
68
  counter += 1;
65
69
  iframe = $(
66
- '<iframe src="javascript:false;" name="iframe-transport-' +
67
- counter + '"></iframe>'
70
+ '<iframe src="' + initialIframeSrc +
71
+ '" name="iframe-transport-' + counter + '"></iframe>'
68
72
  ).bind('load', function () {
69
73
  var fileInputClones,
70
74
  paramNames = $.isArray(options.paramName) ?
@@ -95,7 +99,7 @@
95
99
  );
96
100
  // Fix for IE endless progress bar activity bug
97
101
  // (happens on form submits to iframe targets):
98
- $('<iframe src="javascript:false;"></iframe>')
102
+ $('<iframe src="' + initialIframeSrc + '"></iframe>')
99
103
  .appendTo(form);
100
104
  window.setTimeout(function () {
101
105
  // Removing the form in a setTimeout call
@@ -138,6 +142,8 @@
138
142
  .prop('enctype', 'multipart/form-data')
139
143
  // enctype must be set as encoding for IE:
140
144
  .prop('encoding', 'multipart/form-data');
145
+ // Remove the HTML5 form attribute from the input(s):
146
+ options.fileInput.removeAttr('form');
141
147
  }
142
148
  form.submit();
143
149
  // Insert the file input fields at their original location
@@ -145,7 +151,10 @@
145
151
  if (fileInputClones && fileInputClones.length) {
146
152
  options.fileInput.each(function (index, input) {
147
153
  var clone = $(fileInputClones[index]);
148
- $(input).prop('name', clone.prop('name'));
154
+ // Restore the original name and form properties:
155
+ $(input)
156
+ .prop('name', clone.prop('name'))
157
+ .attr('form', clone.attr('form'));
149
158
  clone.replaceWith(input);
150
159
  });
151
160
  }
@@ -159,7 +168,7 @@
159
168
  // concat is used to avoid the "Script URL" JSLint error:
160
169
  iframe
161
170
  .unbind('load')
162
- .prop('src', 'javascript'.concat(':false;'));
171
+ .prop('src', initialIframeSrc);
163
172
  }
164
173
  if (form) {
165
174
  form.remove();
@@ -0,0 +1 @@
1
+ !function(a){"use strict";var b=function(a,c,d){var e,f,g=document.createElement("img");if(g.onerror=c,g.onload=function(){!f||d&&d.noRevoke||b.revokeObjectURL(f),c&&c(b.scale(g,d))},b.isInstanceOf("Blob",a)||b.isInstanceOf("File",a))e=f=b.createObjectURL(a),g._type=a.type;else{if("string"!=typeof a)return!1;e=a,d&&d.crossOrigin&&(g.crossOrigin=d.crossOrigin)}return e?(g.src=e,g):b.readFile(a,function(a){var b=a.target;b&&b.result?g.src=b.result:c&&c(a)})},c=window.createObjectURL&&window||window.URL&&URL.revokeObjectURL&&URL||window.webkitURL&&webkitURL;b.isInstanceOf=function(a,b){return Object.prototype.toString.call(b)==="[object "+a+"]"},b.transformCoordinates=function(){},b.getTransformedOptions=function(a){return a},b.renderImageToCanvas=function(a,b,c,d,e,f,g,h,i,j){return a.getContext("2d").drawImage(b,c,d,e,f,g,h,i,j),a},b.hasCanvasOption=function(a){return a.canvas||a.crop},b.scale=function(a,c){c=c||{};var d,e,f,g,h,i,j,k,l,m=document.createElement("canvas"),n=a.getContext||b.hasCanvasOption(c)&&m.getContext,o=a.naturalWidth||a.width,p=a.naturalHeight||a.height,q=o,r=p,s=function(){var a=Math.max((f||q)/q,(g||r)/r);a>1&&(q=Math.ceil(q*a),r=Math.ceil(r*a))},t=function(){var a=Math.min((d||q)/q,(e||r)/r);1>a&&(q=Math.ceil(q*a),r=Math.ceil(r*a))};return n&&(c=b.getTransformedOptions(c),j=c.left||0,k=c.top||0,c.sourceWidth?(h=c.sourceWidth,void 0!==c.right&&void 0===c.left&&(j=o-h-c.right)):h=o-j-(c.right||0),c.sourceHeight?(i=c.sourceHeight,void 0!==c.bottom&&void 0===c.top&&(k=p-i-c.bottom)):i=p-k-(c.bottom||0),q=h,r=i),d=c.maxWidth,e=c.maxHeight,f=c.minWidth,g=c.minHeight,n&&d&&e&&c.crop?(q=d,r=e,l=h/i-d/e,0>l?(i=e*h/d,void 0===c.top&&void 0===c.bottom&&(k=(p-i)/2)):l>0&&(h=d*i/e,void 0===c.left&&void 0===c.right&&(j=(o-h)/2))):((c.contain||c.cover)&&(f=d=d||f,g=e=e||g),c.cover?(t(),s()):(s(),t())),n?(m.width=q,m.height=r,b.transformCoordinates(m,c),b.renderImageToCanvas(m,a,j,k,h,i,0,0,q,r)):(a.width=q,a.height=r,a)},b.createObjectURL=function(a){return c?c.createObjectURL(a):!1},b.revokeObjectURL=function(a){return c?c.revokeObjectURL(a):!1},b.readFile=function(a,b,c){if(window.FileReader){var d=new FileReader;if(d.onload=d.onerror=b,c=c||"readAsDataURL",d[c])return d[c](a),d}return!1},"function"==typeof define&&define.amd?define(function(){return b}):a.loadImage=b}(this),function(a){"use strict";"function"==typeof define&&define.amd?define(["load-image"],a):a(window.loadImage)}(function(a){"use strict";if(window.navigator&&window.navigator.platform&&/iP(hone|od|ad)/.test(window.navigator.platform)){var b=a.renderImageToCanvas;a.detectSubsampling=function(a){var b,c;return a.width*a.height>1048576?(b=document.createElement("canvas"),b.width=b.height=1,c=b.getContext("2d"),c.drawImage(a,-a.width+1,0),0===c.getImageData(0,0,1,1).data[3]):!1},a.detectVerticalSquash=function(a,b){var c,d,e,f,g,h=a.naturalHeight||a.height,i=document.createElement("canvas"),j=i.getContext("2d");for(b&&(h/=2),i.width=1,i.height=h,j.drawImage(a,0,0),c=j.getImageData(0,0,1,h).data,d=0,e=h,f=h;f>d;)g=c[4*(f-1)+3],0===g?e=f:d=f,f=e+d>>1;return f/h||1},a.renderImageToCanvas=function(c,d,e,f,g,h,i,j,k,l){if("image/jpeg"===d._type){var m,n,o,p,q=c.getContext("2d"),r=document.createElement("canvas"),s=1024,t=r.getContext("2d");if(r.width=s,r.height=s,q.save(),m=a.detectSubsampling(d),m&&(e/=2,f/=2,g/=2,h/=2),n=a.detectVerticalSquash(d,m),m||1!==n){for(f*=n,k=Math.ceil(s*k/g),l=Math.ceil(s*l/h/n),j=0,p=0;h>p;){for(i=0,o=0;g>o;)t.clearRect(0,0,s,s),t.drawImage(d,e,f,g,h,-o,-p,g,h),q.drawImage(r,0,0,s,s,i,j,k,l),o+=s,i+=k;p+=s,j+=l}return q.restore(),c}}return b(c,d,e,f,g,h,i,j,k,l)}}}),function(a){"use strict";"function"==typeof define&&define.amd?define(["load-image"],a):a(window.loadImage)}(function(a){"use strict";var b=a.hasCanvasOption;a.hasCanvasOption=function(a){return b(a)||a.orientation},a.transformCoordinates=function(a,b){var c=a.getContext("2d"),d=a.width,e=a.height,f=b.orientation;if(f)switch(f>4&&(a.width=e,a.height=d),f){case 2:c.translate(d,0),c.scale(-1,1);break;case 3:c.translate(d,e),c.rotate(Math.PI);break;case 4:c.translate(0,e),c.scale(1,-1);break;case 5:c.rotate(.5*Math.PI),c.scale(1,-1);break;case 6:c.rotate(.5*Math.PI),c.translate(0,-e);break;case 7:c.rotate(.5*Math.PI),c.translate(d,-e),c.scale(-1,1);break;case 8:c.rotate(-.5*Math.PI),c.translate(-d,0)}},a.getTransformedOptions=function(a){if(!a.orientation||1===a.orientation)return a;var b,c={};for(b in a)a.hasOwnProperty(b)&&(c[b]=a[b]);switch(a.orientation){case 2:c.left=a.right,c.right=a.left;break;case 3:c.left=a.right,c.top=a.bottom,c.right=a.left,c.bottom=a.top;break;case 4:c.top=a.bottom,c.bottom=a.top;break;case 5:c.left=a.top,c.top=a.left,c.right=a.bottom,c.bottom=a.right;break;case 6:c.left=a.top,c.top=a.right,c.right=a.bottom,c.bottom=a.left;break;case 7:c.left=a.bottom,c.top=a.right,c.right=a.top,c.bottom=a.left;break;case 8:c.left=a.bottom,c.top=a.left,c.right=a.top,c.bottom=a.right}return a.orientation>4&&(c.maxWidth=a.maxHeight,c.maxHeight=a.maxWidth,c.minWidth=a.minHeight,c.minHeight=a.minWidth,c.sourceWidth=a.sourceHeight,c.sourceHeight=a.sourceWidth),c}}),function(a){"use strict";"function"==typeof define&&define.amd?define(["load-image"],a):a(window.loadImage)}(function(a){"use strict";var b=window.Blob&&(Blob.prototype.slice||Blob.prototype.webkitSlice||Blob.prototype.mozSlice);a.blobSlice=b&&function(){var a=this.slice||this.webkitSlice||this.mozSlice;return a.apply(this,arguments)},a.metaDataParsers={jpeg:{65505:[]}},a.parseMetaData=function(b,c,d){d=d||{};var e=this,f=d.maxMetaDataSize||262144,g={},h=!(window.DataView&&b&&b.size>=12&&"image/jpeg"===b.type&&a.blobSlice);(h||!a.readFile(a.blobSlice.call(b,0,f),function(b){if(b.target.error)return console.log(b.target.error),c(g),void 0;var f,h,i,j,k=b.target.result,l=new DataView(k),m=2,n=l.byteLength-4,o=m;if(65496===l.getUint16(0)){for(;n>m&&(f=l.getUint16(m),f>=65504&&65519>=f||65534===f);){if(h=l.getUint16(m+2)+2,m+h>l.byteLength){console.log("Invalid meta data: Invalid segment size.");break}if(i=a.metaDataParsers.jpeg[f])for(j=0;j<i.length;j+=1)i[j].call(e,l,m,h,g,d);m+=h,o=m}!d.disableImageHead&&o>6&&(g.imageHead=k.slice?k.slice(0,o):new Uint8Array(k).subarray(0,o))}else console.log("Invalid JPEG file: Missing JPEG marker.");c(g)},"readAsArrayBuffer"))&&c(g)}}),function(a){"use strict";"function"==typeof define&&define.amd?define(["load-image","load-image-meta"],a):a(window.loadImage)}(function(a){"use strict";a.ExifMap=function(){return this},a.ExifMap.prototype.map={Orientation:274},a.ExifMap.prototype.get=function(a){return this[a]||this[this.map[a]]},a.getExifThumbnail=function(a,b,c){var d,e,f;if(!c||b+c>a.byteLength)return console.log("Invalid Exif data: Invalid thumbnail data."),void 0;for(d=[],e=0;c>e;e+=1)f=a.getUint8(b+e),d.push((16>f?"0":"")+f.toString(16));return"data:image/jpeg,%"+d.join("%")},a.exifTagTypes={1:{getValue:function(a,b){return a.getUint8(b)},size:1},2:{getValue:function(a,b){return String.fromCharCode(a.getUint8(b))},size:1,ascii:!0},3:{getValue:function(a,b,c){return a.getUint16(b,c)},size:2},4:{getValue:function(a,b,c){return a.getUint32(b,c)},size:4},5:{getValue:function(a,b,c){return a.getUint32(b,c)/a.getUint32(b+4,c)},size:8},9:{getValue:function(a,b,c){return a.getInt32(b,c)},size:4},10:{getValue:function(a,b,c){return a.getInt32(b,c)/a.getInt32(b+4,c)},size:8}},a.exifTagTypes[7]=a.exifTagTypes[1],a.getExifValue=function(b,c,d,e,f,g){var h,i,j,k,l,m,n=a.exifTagTypes[e];if(!n)return console.log("Invalid Exif data: Invalid tag type."),void 0;if(h=n.size*f,i=h>4?c+b.getUint32(d+8,g):d+8,i+h>b.byteLength)return console.log("Invalid Exif data: Invalid data offset."),void 0;if(1===f)return n.getValue(b,i,g);for(j=[],k=0;f>k;k+=1)j[k]=n.getValue(b,i+k*n.size,g);if(n.ascii){for(l="",k=0;k<j.length&&(m=j[k],"\x00"!==m);k+=1)l+=m;return l}return j},a.parseExifTag=function(b,c,d,e,f){var g=b.getUint16(d,e);f.exif[g]=a.getExifValue(b,c,d,b.getUint16(d+2,e),b.getUint32(d+4,e),e)},a.parseExifTags=function(a,b,c,d,e){var f,g,h;if(c+6>a.byteLength)return console.log("Invalid Exif data: Invalid directory offset."),void 0;if(f=a.getUint16(c,d),g=c+2+12*f,g+4>a.byteLength)return console.log("Invalid Exif data: Invalid directory size."),void 0;for(h=0;f>h;h+=1)this.parseExifTag(a,b,c+2+12*h,d,e);return a.getUint32(g,d)},a.parseExifData=function(b,c,d,e,f){if(!f.disableExif){var g,h,i,j=c+10;if(1165519206===b.getUint32(c+4)){if(j+8>b.byteLength)return console.log("Invalid Exif data: Invalid segment size."),void 0;if(0!==b.getUint16(c+8))return console.log("Invalid Exif data: Missing byte alignment offset."),void 0;switch(b.getUint16(j)){case 18761:g=!0;break;case 19789:g=!1;break;default:return console.log("Invalid Exif data: Invalid byte alignment marker."),void 0}if(42!==b.getUint16(j+2,g))return console.log("Invalid Exif data: Missing TIFF marker."),void 0;h=b.getUint32(j+4,g),e.exif=new a.ExifMap,h=a.parseExifTags(b,j,j+h,g,e),h&&!f.disableExifThumbnail&&(i={exif:{}},h=a.parseExifTags(b,j,j+h,g,i),i.exif[513]&&(e.exif.Thumbnail=a.getExifThumbnail(b,j+i.exif[513],i.exif[514]))),e.exif[34665]&&!f.disableExifSub&&a.parseExifTags(b,j,j+e.exif[34665],g,e),e.exif[34853]&&!f.disableExifGps&&a.parseExifTags(b,j,j+e.exif[34853],g,e)}}},a.metaDataParsers.jpeg[65505].push(a.parseExifData)}),function(a){"use strict";"function"==typeof define&&define.amd?define(["load-image","load-image-exif"],a):a(window.loadImage)}(function(a){"use strict";a.ExifMap.prototype.tags={256:"ImageWidth",257:"ImageHeight",34665:"ExifIFDPointer",34853:"GPSInfoIFDPointer",40965:"InteroperabilityIFDPointer",258:"BitsPerSample",259:"Compression",262:"PhotometricInterpretation",274:"Orientation",277:"SamplesPerPixel",284:"PlanarConfiguration",530:"YCbCrSubSampling",531:"YCbCrPositioning",282:"XResolution",283:"YResolution",296:"ResolutionUnit",273:"StripOffsets",278:"RowsPerStrip",279:"StripByteCounts",513:"JPEGInterchangeFormat",514:"JPEGInterchangeFormatLength",301:"TransferFunction",318:"WhitePoint",319:"PrimaryChromaticities",529:"YCbCrCoefficients",532:"ReferenceBlackWhite",306:"DateTime",270:"ImageDescription",271:"Make",272:"Model",305:"Software",315:"Artist",33432:"Copyright",36864:"ExifVersion",40960:"FlashpixVersion",40961:"ColorSpace",40962:"PixelXDimension",40963:"PixelYDimension",42240:"Gamma",37121:"ComponentsConfiguration",37122:"CompressedBitsPerPixel",37500:"MakerNote",37510:"UserComment",40964:"RelatedSoundFile",36867:"DateTimeOriginal",36868:"DateTimeDigitized",37520:"SubSecTime",37521:"SubSecTimeOriginal",37522:"SubSecTimeDigitized",33434:"ExposureTime",33437:"FNumber",34850:"ExposureProgram",34852:"SpectralSensitivity",34855:"PhotographicSensitivity",34856:"OECF",34864:"SensitivityType",34865:"StandardOutputSensitivity",34866:"RecommendedExposureIndex",34867:"ISOSpeed",34868:"ISOSpeedLatitudeyyy",34869:"ISOSpeedLatitudezzz",37377:"ShutterSpeedValue",37378:"ApertureValue",37379:"BrightnessValue",37380:"ExposureBias",37381:"MaxApertureValue",37382:"SubjectDistance",37383:"MeteringMode",37384:"LightSource",37385:"Flash",37396:"SubjectArea",37386:"FocalLength",41483:"FlashEnergy",41484:"SpatialFrequencyResponse",41486:"FocalPlaneXResolution",41487:"FocalPlaneYResolution",41488:"FocalPlaneResolutionUnit",41492:"SubjectLocation",41493:"ExposureIndex",41495:"SensingMethod",41728:"FileSource",41729:"SceneType",41730:"CFAPattern",41985:"CustomRendered",41986:"ExposureMode",41987:"WhiteBalance",41988:"DigitalZoomRatio",41989:"FocalLengthIn35mmFilm",41990:"SceneCaptureType",41991:"GainControl",41992:"Contrast",41993:"Saturation",41994:"Sharpness",41995:"DeviceSettingDescription",41996:"SubjectDistanceRange",42016:"ImageUniqueID",42032:"CameraOwnerName",42033:"BodySerialNumber",42034:"LensSpecification",42035:"LensMake",42036:"LensModel",42037:"LensSerialNumber",0:"GPSVersionID",1:"GPSLatitudeRef",2:"GPSLatitude",3:"GPSLongitudeRef",4:"GPSLongitude",5:"GPSAltitudeRef",6:"GPSAltitude",7:"GPSTimeStamp",8:"GPSSatellites",9:"GPSStatus",10:"GPSMeasureMode",11:"GPSDOP",12:"GPSSpeedRef",13:"GPSSpeed",14:"GPSTrackRef",15:"GPSTrack",16:"GPSImgDirectionRef",17:"GPSImgDirection",18:"GPSMapDatum",19:"GPSDestLatitudeRef",20:"GPSDestLatitude",21:"GPSDestLongitudeRef",22:"GPSDestLongitude",23:"GPSDestBearingRef",24:"GPSDestBearing",25:"GPSDestDistanceRef",26:"GPSDestDistance",27:"GPSProcessingMethod",28:"GPSAreaInformation",29:"GPSDateStamp",30:"GPSDifferential",31:"GPSHPositioningError"},a.ExifMap.prototype.stringValues={ExposureProgram:{0:"Undefined",1:"Manual",2:"Normal program",3:"Aperture priority",4:"Shutter priority",5:"Creative program",6:"Action program",7:"Portrait mode",8:"Landscape mode"},MeteringMode:{0:"Unknown",1:"Average",2:"CenterWeightedAverage",3:"Spot",4:"MultiSpot",5:"Pattern",6:"Partial",255:"Other"},LightSource:{0:"Unknown",1:"Daylight",2:"Fluorescent",3:"Tungsten (incandescent light)",4:"Flash",9:"Fine weather",10:"Cloudy weather",11:"Shade",12:"Daylight fluorescent (D 5700 - 7100K)",13:"Day white fluorescent (N 4600 - 5400K)",14:"Cool white fluorescent (W 3900 - 4500K)",15:"White fluorescent (WW 3200 - 3700K)",17:"Standard light A",18:"Standard light B",19:"Standard light C",20:"D55",21:"D65",22:"D75",23:"D50",24:"ISO studio tungsten",255:"Other"},Flash:{0:"Flash did not fire",1:"Flash fired",5:"Strobe return light not detected",7:"Strobe return light detected",9:"Flash fired, compulsory flash mode",13:"Flash fired, compulsory flash mode, return light not detected",15:"Flash fired, compulsory flash mode, return light detected",16:"Flash did not fire, compulsory flash mode",24:"Flash did not fire, auto mode",25:"Flash fired, auto mode",29:"Flash fired, auto mode, return light not detected",31:"Flash fired, auto mode, return light detected",32:"No flash function",65:"Flash fired, red-eye reduction mode",69:"Flash fired, red-eye reduction mode, return light not detected",71:"Flash fired, red-eye reduction mode, return light detected",73:"Flash fired, compulsory flash mode, red-eye reduction mode",77:"Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",79:"Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",89:"Flash fired, auto mode, red-eye reduction mode",93:"Flash fired, auto mode, return light not detected, red-eye reduction mode",95:"Flash fired, auto mode, return light detected, red-eye reduction mode"},SensingMethod:{1:"Undefined",2:"One-chip color area sensor",3:"Two-chip color area sensor",4:"Three-chip color area sensor",5:"Color sequential area sensor",7:"Trilinear sensor",8:"Color sequential linear sensor"},SceneCaptureType:{0:"Standard",1:"Landscape",2:"Portrait",3:"Night scene"},SceneType:{1:"Directly photographed"},CustomRendered:{0:"Normal process",1:"Custom process"},WhiteBalance:{0:"Auto white balance",1:"Manual white balance"},GainControl:{0:"None",1:"Low gain up",2:"High gain up",3:"Low gain down",4:"High gain down"},Contrast:{0:"Normal",1:"Soft",2:"Hard"},Saturation:{0:"Normal",1:"Low saturation",2:"High saturation"},Sharpness:{0:"Normal",1:"Soft",2:"Hard"},SubjectDistanceRange:{0:"Unknown",1:"Macro",2:"Close view",3:"Distant view"},FileSource:{3:"DSC"},ComponentsConfiguration:{0:"",1:"Y",2:"Cb",3:"Cr",4:"R",5:"G",6:"B"},Orientation:{1:"top-left",2:"top-right",3:"bottom-right",4:"bottom-left",5:"left-top",6:"right-top",7:"right-bottom",8:"left-bottom"}},a.ExifMap.prototype.getText=function(a){var b=this.get(a);switch(a){case"LightSource":case"Flash":case"MeteringMode":case"ExposureProgram":case"SensingMethod":case"SceneCaptureType":case"SceneType":case"CustomRendered":case"WhiteBalance":case"GainControl":case"Contrast":case"Saturation":case"Sharpness":case"SubjectDistanceRange":case"FileSource":case"Orientation":return this.stringValues[a][b];case"ExifVersion":case"FlashpixVersion":return String.fromCharCode(b[0],b[1],b[2],b[3]);case"ComponentsConfiguration":return this.stringValues[a][b[0]]+this.stringValues[a][b[1]]+this.stringValues[a][b[2]]+this.stringValues[a][b[3]];case"GPSVersionID":return b[0]+"."+b[1]+"."+b[2]+"."+b[3]}return String(b)},function(a){var b,c=a.tags,d=a.map;for(b in c)c.hasOwnProperty(b)&&(d[c[b]]=b)}(a.ExifMap.prototype),a.ExifMap.prototype.getAll=function(){var a,b,c={};for(a in this)this.hasOwnProperty(a)&&(b=this.tags[a],b&&(c[b]=this.getText(b)));return c}});
@@ -1,10 +1,13 @@
1
1
  #= require jquery.ui.widget
2
+ #= require _uuid
3
+ #= require load-image.min
4
+ #= require canvas-to-blob.min
2
5
  #= require jquery.iframe-transport
3
6
  #= require cors/jquery.postmessage-transport
4
7
  #= require cors/jquery.xdr-transport
5
- #= require _uuid
6
8
  #= require jquery.fileupload
7
9
  #= require _image_uploader
8
10
  #= require _uploader_preview
9
11
  #= require _gallery_uploader
10
- #= require _manager
12
+ #= require _show_image
13
+ #= require _manager
@@ -1,8 +1,24 @@
1
1
  @import bootstrap/progress
2
2
 
3
+ .uploadbox-image-container
4
+ background: #eee
5
+ display: inline-block
6
+ img, canvas
7
+ display: inline-block
8
+ vertical-align: bottom
9
+
10
+ .uploadbox-image-container.uploadbox-processing
11
+ background: #eee
12
+
3
13
  .uploadbox-image-uploader
4
14
  position: relative
5
15
 
16
+ .uploader-overlay
17
+ width: 100%
18
+ background: rgba(0, 0, 0, .6)
19
+ position: absolute
20
+ z-index: 1
21
+
6
22
  .btn
7
23
  background-color: #f7f7f7
8
24
  text-align: center
@@ -25,11 +41,11 @@
25
41
  width: 100%
26
42
  top: 10px
27
43
 
28
- &.uploading:before
29
- content: 'Enviando'
44
+ // &.uploading:before
45
+ // content: 'Enviando'
30
46
 
31
- &.processing:before
32
- content: 'Processando'
47
+ // &.processing:before
48
+ // content: 'Processando'
33
49
 
34
50
 
35
51
  .fileupload-new .btn.fileupload-exists
@@ -110,7 +126,7 @@
110
126
  .btn
111
127
  vertical-align: middle
112
128
 
113
- .fileupload-exists .fileupload-new, .fileupload-new .fileupload-exists
129
+ .fileupload-exists .fileupload-new, .fileupload-new .fileupload-exists, .fileupload-uploading .fileupload-new
114
130
  display: none
115
131
 
116
132
  .fileupload-inline .fileupload-controls
@@ -157,7 +173,7 @@
157
173
  width: 100%
158
174
 
159
175
  .progress
160
- height: 10px
176
+ height: 7px
161
177
  width: 100%
162
178
  margin-bottom: 0
163
179
  border-radius: 0
@@ -183,4 +199,8 @@
183
199
  border: 0
184
200
  &:hover
185
201
  background: black
186
- color: white
202
+ color: white
203
+
204
+ .fileupload-uploading
205
+ .btn.fileupload-exists
206
+ display: none
@@ -3,19 +3,29 @@ module Uploadbox
3
3
  layout false
4
4
 
5
5
  def create
6
+ attributes = image_params
7
+ attributes["imageable_type"].constantize # load class
8
+ attributes["original_file"] = attributes["remote_file_url"]
9
+
10
+ attributes.delete("upload_name")
11
+ attributes.delete("remote_file_url")
12
+
13
+ upload = Uploadbox.const_get(upload_class_name).create!(attributes)
14
+
6
15
  if Uploadbox.background_processing
7
- Resque.enqueue(ProcessImage, image_params)
16
+ Resque.enqueue(ProcessImage, {id: upload.id, upload_class_name: upload_class_name})
8
17
  else
9
- Image.create_upload(image_params)
18
+ upload.process
10
19
  end
20
+
11
21
  render nothing: true
12
22
  end
13
23
 
14
24
  def find
15
- params[:imageable_type].constantize # load class
25
+ # binding.pry
26
+ image_params["imageable_type"].constantize # load class
16
27
 
17
- upload_class_name = params[:imageable_type] + params[:upload_name].camelize
18
- @image = Uploadbox.const_get(upload_class_name).find_by(secure_random: params[:secure_random])
28
+ @image = Uploadbox.const_get(upload_class_name).find_by(secure_random: image_params[:secure_random])
19
29
  end
20
30
 
21
31
  def destroy
@@ -26,5 +36,9 @@ module Uploadbox
26
36
  def image_params
27
37
  params.require(:image).permit(:remote_file_url, :imageable_type, :upload_name, :secure_random)
28
38
  end
39
+
40
+ def upload_class_name
41
+ image_params["imageable_type"] + image_params["upload_name"].camelize
42
+ end
29
43
  end
30
44
  end
@@ -4,7 +4,6 @@ module Uploadbox
4
4
  Base64.encode64(policy_data.to_json).gsub("\n", "")
5
5
  end
6
6
 
7
-
8
7
  def s3_signature
9
8
  Base64.encode64(
10
9
  OpenSSL::HMAC.digest(
@@ -17,7 +16,21 @@ module Uploadbox
17
16
 
18
17
  def img(source, options={})
19
18
  if source.respond_to?(:url) and source.respond_to?(:width) and source.respond_to?(:height)
20
- image_tag(source.url, {width: source.width, height: source.height}.merge(options))
19
+ if source.processing?
20
+ data = {
21
+ processing: source.processing?,
22
+ original: source.original_file,
23
+ component: 'ShowImage'
24
+ }
25
+ content_tag :div, class: 'uploadbox-image-container uploadbox-processing', style: "width: #{source.width}px; height: #{source.height}px", data: data do
26
+ image_tag(source.url, {width: source.width, height: source.height, style: 'display: none'}.merge(options))
27
+ end
28
+ else
29
+ data = {}
30
+ content_tag :div, class: 'uploadbox-image-container', width: source.width, height: source.height, data: data do
31
+ image_tag(source.url, {width: source.width, height: source.height}.merge(options))
32
+ end
33
+ end
21
34
  else
22
35
  image_tag(source, options)
23
36
  end
@@ -1,9 +1,9 @@
1
1
  class ProcessImage
2
2
  extend HerokuResqueAutoScale
3
-
3
+
4
4
  @queue = :process_image
5
5
 
6
- def self.perform(image_params)
7
- Image.create_upload(image_params)
6
+ def self.perform(attributes)
7
+ Uploadbox.const_get(attributes['upload_class_name']).find(attributes['id']).process
8
8
  end
9
9
  end
data/app/models/image.rb CHANGED
@@ -1,11 +1,11 @@
1
1
  class Image < ActiveRecord::Base
2
2
  belongs_to :imageable, polymorphic: true
3
3
 
4
- def self.create_upload(attributes)
5
- attributes["imageable_type"].constantize # load class
4
+ def process
5
+ update(remote_file_url: original_file)
6
+ end
6
7
 
7
- upload_class_name = attributes["imageable_type"] + attributes["upload_name"].camelize
8
- attributes.delete("upload_name")
9
- Uploadbox.const_get(upload_class_name).create!(attributes)
8
+ def processing?
9
+ file.blank?
10
10
  end
11
11
  end
data/config/routes.rb CHANGED
@@ -3,5 +3,7 @@ Uploadbox::Engine.routes.draw do
3
3
  resources :images, only: [:create, :update, :destroy] do
4
4
  get 'find', on: :collection
5
5
  end
6
- # mount Resque::Server.new, :at => "/resque"
6
+ if Uploadbox.resque_server
7
+ mount Resque::Server.new, :at => "/resque"
8
+ end
7
9
  end
@@ -3,11 +3,17 @@ Uploadbox.retina_quality = 30
3
3
  Uploadbox.image_quality = 70
4
4
 
5
5
  if Rails.env.production?
6
+ Uploadbox.background_processing = true
7
+
6
8
  REDIS = Redis.connect(url: ENV["REDISCLOUD_URL"])
7
9
  Resque.redis = REDIS
8
10
  Resque.after_fork = Proc.new { ActiveRecord::Base.establish_connection }
9
11
  end
10
12
 
13
+ if Rails.env.development?
14
+ Uploadbox.resque_server = true
15
+ end
16
+
11
17
  CarrierWave.configure do |config|
12
18
  config.storage = :fog
13
19
 
@@ -8,6 +8,7 @@ class CreateImages < ActiveRecord::Migration
8
8
  t.boolean :retina, default: false
9
9
  t.string :upload_name
10
10
  t.string :secure_random, index: true
11
+ t.string :original_file
11
12
 
12
13
  t.timestamps
13
14
  end
@@ -22,7 +22,7 @@ module Uploadbox
22
22
  # @post.picture?
23
23
  define_method("#{upload_name}?") do
24
24
  upload = send("#{upload_name}_upload")
25
- !!(upload and upload.file?)
25
+ !!(upload and (upload.processing? or upload.file?))
26
26
  end
27
27
 
28
28
  # @post.picture
@@ -36,6 +36,10 @@ module Uploadbox
36
36
  def to_s
37
37
  url
38
38
  end
39
+
40
+ def processing?
41
+ false
42
+ end
39
43
  end
40
44
 
41
45
  placeholder = Class.new do
@@ -66,7 +70,7 @@ module Uploadbox
66
70
 
67
71
  # Post.update_picture_versions!
68
72
  self.define_singleton_method "update_#{upload_name}_versions!" do
69
- Uploadbox.const_get(upload_class_name).find_each{|upload| upload.file.recreate_versions!}
73
+ Uploadbox.const_get(upload_class_name).find_each{ |upload| upload.file.recreate_versions! if upload.file? }
70
74
  end
71
75
 
72
76
  # Uploadbox::PostPicture < Image
@@ -111,6 +115,14 @@ module Uploadbox
111
115
  def height
112
116
  dimensions[1]
113
117
  end
118
+
119
+ def processing?
120
+ model.processing?
121
+ end
122
+
123
+ def original_file
124
+ model.original_file
125
+ end
114
126
  end
115
127
  end
116
128
  mount_uploader :file, dynamic_uploader
@@ -209,6 +221,14 @@ module Uploadbox
209
221
  def height
210
222
  dimensions[1]
211
223
  end
224
+
225
+ def processing?
226
+ model.processing?
227
+ end
228
+
229
+ def original_file
230
+ model.original_file
231
+ end
212
232
  end
213
233
  end
214
234
  mount_uploader :file, dynamic_uploader
@@ -1,3 +1,3 @@
1
1
  module Uploadbox
2
- VERSION = "0.1.4"
2
+ VERSION = "0.2.0.beta"
3
3
  end
data/lib/uploadbox.rb CHANGED
@@ -5,4 +5,5 @@ module Uploadbox
5
5
  mattr_accessor :image_quality
6
6
  mattr_accessor :retina_quality
7
7
  mattr_accessor :background_processing
8
+ mattr_accessor :resque_server
8
9
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uploadbox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.2.0.beta
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julio Protzek
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-03-25 00:00:00.000000000 Z
12
+ date: 2014-03-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -332,25 +332,25 @@ dependencies:
332
332
  - !ruby/object:Gem::Version
333
333
  version: 0.7.2
334
334
  - !ruby/object:Gem::Dependency
335
- name: sqlite3
335
+ name: pg
336
336
  requirement: !ruby/object:Gem::Requirement
337
337
  requirements:
338
338
  - - ~>
339
339
  - !ruby/object:Gem::Version
340
- version: '1.3'
340
+ version: '0.17'
341
341
  - - '>='
342
342
  - !ruby/object:Gem::Version
343
- version: 1.3.7
343
+ version: 0.17.1
344
344
  type: :development
345
345
  prerelease: false
346
346
  version_requirements: !ruby/object:Gem::Requirement
347
347
  requirements:
348
348
  - - ~>
349
349
  - !ruby/object:Gem::Version
350
- version: '1.3'
350
+ version: '0.17'
351
351
  - - '>='
352
352
  - !ruby/object:Gem::Version
353
- version: 1.3.7
353
+ version: 0.17.1
354
354
  - !ruby/object:Gem::Dependency
355
355
  name: rspec-rails
356
356
  requirement: !ruby/object:Gem::Requirement
@@ -536,13 +536,16 @@ files:
536
536
  - app/assets/javascripts/_gallery_uploader.coffee
537
537
  - app/assets/javascripts/_image_uploader.coffee
538
538
  - app/assets/javascripts/_manager.coffee
539
+ - app/assets/javascripts/_show_image.coffee
539
540
  - app/assets/javascripts/_uploader_preview.coffee
540
541
  - app/assets/javascripts/_uuid.js
542
+ - app/assets/javascripts/canvas-to-blob.min.js
541
543
  - app/assets/javascripts/cors/jquery.postmessage-transport.js
542
544
  - app/assets/javascripts/cors/jquery.xdr-transport.js
543
545
  - app/assets/javascripts/jquery.fileupload.js
544
546
  - app/assets/javascripts/jquery.iframe-transport.js
545
547
  - app/assets/javascripts/jquery.ui.widget.js
548
+ - app/assets/javascripts/load-image.min.js
546
549
  - app/assets/javascripts/uploadbox.coffee
547
550
  - app/assets/stylesheets/bootstrap/_progress.css
548
551
  - app/assets/stylesheets/uploadbox.sass
@@ -589,9 +592,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
589
592
  version: 1.9.3
590
593
  required_rubygems_version: !ruby/object:Gem::Requirement
591
594
  requirements:
592
- - - '>='
595
+ - - '>'
593
596
  - !ruby/object:Gem::Version
594
- version: '0'
597
+ version: 1.3.1
595
598
  requirements: []
596
599
  rubyforge_project:
597
600
  rubygems_version: 2.1.11