s3_cors_fileupload 0.1.5 → 0.2.0.pre1

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 (67) hide show
  1. checksums.yaml +7 -0
  2. data/.document +4 -3
  3. data/.gitignore +3 -7
  4. data/CHANGELOG.md +9 -1
  5. data/Gemfile +11 -11
  6. data/Gemfile.lock +66 -74
  7. data/README.md +4 -3
  8. data/Rakefile +7 -9
  9. data/lib/generators/s3_cors_fileupload/install/templates/s3_uploads.js +16 -22
  10. data/lib/generators/s3_cors_fileupload/install/templates/s3_uploads_controller.rb +1 -1
  11. data/lib/generators/s3_cors_fileupload/install/templates/source_file.rb +2 -2
  12. data/lib/generators/s3_cors_fileupload/install/templates/views/erb/_template_download.html.erb +2 -2
  13. data/lib/generators/s3_cors_fileupload/install/templates/views/erb/_template_upload.html.erb +4 -4
  14. data/lib/generators/s3_cors_fileupload/install/templates/views/erb/_template_uploaded.html.erb +1 -1
  15. data/lib/generators/s3_cors_fileupload/install/templates/views/erb/index.html.erb +2 -2
  16. data/lib/generators/s3_cors_fileupload/install/templates/views/haml/_template_download.html.haml +2 -2
  17. data/lib/generators/s3_cors_fileupload/install/templates/views/haml/_template_upload.html.haml +4 -4
  18. data/lib/generators/s3_cors_fileupload/install/templates/views/haml/_template_uploaded.html.haml +2 -2
  19. data/lib/generators/s3_cors_fileupload/install/templates/views/haml/index.html.haml +2 -2
  20. data/lib/s3_cors_fileupload/rails/config.rb +0 -1
  21. data/lib/s3_cors_fileupload/rails/form_helper.rb +2 -4
  22. data/lib/s3_cors_fileupload/rails/policy_helper.rb +2 -2
  23. data/lib/s3_cors_fileupload/version.rb +3 -3
  24. data/s3_cors_fileupload.gemspec +12 -15
  25. data/spec/dummy/Rakefile +7 -0
  26. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  27. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  28. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  29. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  30. data/spec/dummy/app/models/.gitkeep +0 -0
  31. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  32. data/spec/dummy/config.ru +4 -0
  33. data/spec/dummy/config/amazon_s3.yml +17 -0
  34. data/spec/dummy/config/application.rb +64 -0
  35. data/spec/dummy/config/boot.rb +10 -0
  36. data/spec/dummy/config/database.yml +25 -0
  37. data/spec/dummy/config/environment.rb +5 -0
  38. data/spec/dummy/config/environments/development.rb +37 -0
  39. data/spec/dummy/config/environments/production.rb +67 -0
  40. data/spec/dummy/config/environments/test.rb +37 -0
  41. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  42. data/spec/dummy/config/initializers/inflections.rb +15 -0
  43. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  44. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  45. data/spec/dummy/config/initializers/session_store.rb +8 -0
  46. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  47. data/spec/dummy/config/locales/en.yml +5 -0
  48. data/spec/dummy/config/routes.rb +58 -0
  49. data/spec/dummy/db/.gitkeep +0 -0
  50. data/spec/dummy/lib/assets/.gitkeep +0 -0
  51. data/spec/dummy/log/.gitkeep +0 -0
  52. data/spec/dummy/public/404.html +26 -0
  53. data/spec/dummy/public/422.html +26 -0
  54. data/spec/dummy/public/500.html +25 -0
  55. data/spec/dummy/public/favicon.ico +0 -0
  56. data/spec/dummy/script/rails +6 -0
  57. data/spec/lib/generators/install/install_generator_spec.rb +160 -0
  58. data/spec/lib/s3_cors_fileupload/rails/config_spec.rb +43 -0
  59. data/spec/{s3_cors_fileupload → lib/s3_cors_fileupload}/version_spec.rb +0 -0
  60. data/spec/spec_helper.rb +11 -2
  61. data/vendor/assets/javascripts/s3_cors_fileupload/jquery.fileupload-ui.js +117 -43
  62. data/vendor/assets/javascripts/s3_cors_fileupload/jquery.fileupload.js +187 -92
  63. data/vendor/assets/javascripts/s3_cors_fileupload/jquery.iframe-transport.js +20 -7
  64. data/vendor/assets/javascripts/s3_cors_fileupload/vendor/jquery.ui.widget.js +72 -53
  65. data/vendor/assets/javascripts/s3_cors_fileupload/vendor/load-image.js +225 -37
  66. data/vendor/assets/stylesheets/jquery.fileupload-ui.css.erb +6 -8
  67. metadata +78 -21
@@ -1,5 +1,5 @@
1
1
  /*
2
- * jQuery Iframe Transport Plugin 1.5
2
+ * jQuery Iframe Transport Plugin 1.6.1
3
3
  * https://github.com/blueimp/jQuery-File-Upload
4
4
  *
5
5
  * Copyright 2011, Sebastian Tschan
@@ -36,13 +36,26 @@
36
36
  // equivalent to the return data of .serializeArray(), e.g.:
37
37
  // [{name: 'a', value: 1}, {name: 'b', value: 2}]
38
38
  $.ajaxTransport('iframe', function (options) {
39
- if (options.async && (options.type === 'POST' || options.type === 'GET')) {
39
+ if (options.async) {
40
40
  var form,
41
- iframe;
41
+ iframe,
42
+ addParamChar;
42
43
  return {
43
44
  send: function (_, completeCallback) {
44
45
  form = $('<form style="display:none;"></form>');
45
46
  form.attr('accept-charset', options.formAcceptCharset);
47
+ addParamChar = /\?/.test(options.url) ? '&' : '?';
48
+ // XDomainRequest only supports GET and POST:
49
+ if (options.type === 'DELETE') {
50
+ options.url = options.url + addParamChar + '_method=DELETE';
51
+ options.type = 'POST';
52
+ } else if (options.type === 'PUT') {
53
+ options.url = options.url + addParamChar + '_method=PUT';
54
+ options.type = 'POST';
55
+ } else if (options.type === 'PATCH') {
56
+ options.url = options.url + addParamChar + '_method=PATCH';
57
+ options.type = 'POST';
58
+ }
46
59
  // javascript:false as initial iframe src
47
60
  // prevents warning popups on HTTPS in IE6.
48
61
  // IE versions below IE8 cannot set the name property of
@@ -155,16 +168,16 @@
155
168
  $.ajaxSetup({
156
169
  converters: {
157
170
  'iframe text': function (iframe) {
158
- return $(iframe[0].body).text();
171
+ return iframe && $(iframe[0].body).text();
159
172
  },
160
173
  'iframe json': function (iframe) {
161
- return $.parseJSON($(iframe[0].body).text());
174
+ return iframe && $.parseJSON($(iframe[0].body).text());
162
175
  },
163
176
  'iframe html': function (iframe) {
164
- return $(iframe[0].body).html();
177
+ return iframe && $(iframe[0].body).html();
165
178
  },
166
179
  'iframe script': function (iframe) {
167
- return $.globalEval($(iframe[0].body).text());
180
+ return iframe && $.globalEval($(iframe[0].body).text());
168
181
  }
169
182
  }
170
183
  });
@@ -1,8 +1,8 @@
1
1
  /*
2
- * jQuery UI Widget 1.9.0+amd
2
+ * jQuery UI Widget 1.10.1+amd
3
3
  * https://github.com/blueimp/jQuery-File-Upload
4
4
  *
5
- * Copyright 2012 jQuery Foundation and other contributors
5
+ * Copyright 2013 jQuery Foundation and other contributors
6
6
  * Released under the MIT license.
7
7
  * http://jquery.org/license
8
8
  *
@@ -34,6 +34,9 @@ $.cleanData = function( elems ) {
34
34
 
35
35
  $.widget = function( name, base, prototype ) {
36
36
  var fullName, existingConstructor, constructor, basePrototype,
37
+ // proxiedPrototype allows the provided prototype to remain unmodified
38
+ // so that it can be used as a mixin for multiple widgets (#8876)
39
+ proxiedPrototype = {},
37
40
  namespace = name.split( "." )[ 0 ];
38
41
 
39
42
  name = name.split( "." )[ 1 ];
@@ -80,43 +83,43 @@ $.widget = function( name, base, prototype ) {
80
83
  // inheriting from
81
84
  basePrototype.options = $.widget.extend( {}, basePrototype.options );
82
85
  $.each( prototype, function( prop, value ) {
83
- if ( $.isFunction( value ) ) {
84
- prototype[ prop ] = (function() {
85
- var _super = function() {
86
- return base.prototype[ prop ].apply( this, arguments );
87
- },
88
- _superApply = function( args ) {
89
- return base.prototype[ prop ].apply( this, args );
90
- };
91
- return function() {
92
- var __super = this._super,
93
- __superApply = this._superApply,
94
- returnValue;
95
-
96
- this._super = _super;
97
- this._superApply = _superApply;
98
-
99
- returnValue = value.apply( this, arguments );
100
-
101
- this._super = __super;
102
- this._superApply = __superApply;
103
-
104
- return returnValue;
105
- };
106
- })();
86
+ if ( !$.isFunction( value ) ) {
87
+ proxiedPrototype[ prop ] = value;
88
+ return;
107
89
  }
90
+ proxiedPrototype[ prop ] = (function() {
91
+ var _super = function() {
92
+ return base.prototype[ prop ].apply( this, arguments );
93
+ },
94
+ _superApply = function( args ) {
95
+ return base.prototype[ prop ].apply( this, args );
96
+ };
97
+ return function() {
98
+ var __super = this._super,
99
+ __superApply = this._superApply,
100
+ returnValue;
101
+
102
+ this._super = _super;
103
+ this._superApply = _superApply;
104
+
105
+ returnValue = value.apply( this, arguments );
106
+
107
+ this._super = __super;
108
+ this._superApply = __superApply;
109
+
110
+ return returnValue;
111
+ };
112
+ })();
108
113
  });
109
114
  constructor.prototype = $.widget.extend( basePrototype, {
110
115
  // TODO: remove support for widgetEventPrefix
111
116
  // always use the name + a colon as the prefix, e.g., draggable:start
112
117
  // don't prefix for widgets that aren't DOM-based
113
- widgetEventPrefix: name
114
- }, prototype, {
118
+ widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name
119
+ }, proxiedPrototype, {
115
120
  constructor: constructor,
116
121
  namespace: namespace,
117
122
  widgetName: name,
118
- // TODO remove widgetBaseClass, see #8155
119
- widgetBaseClass: fullName,
120
123
  widgetFullName: fullName
121
124
  });
122
125
 
@@ -151,8 +154,17 @@ $.widget.extend = function( target ) {
151
154
  for ( ; inputIndex < inputLength; inputIndex++ ) {
152
155
  for ( key in input[ inputIndex ] ) {
153
156
  value = input[ inputIndex ][ key ];
154
- if (input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
155
- target[ key ] = $.isPlainObject( value ) ? $.widget.extend( {}, target[ key ], value ) : value;
157
+ if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
158
+ // Clone objects
159
+ if ( $.isPlainObject( value ) ) {
160
+ target[ key ] = $.isPlainObject( target[ key ] ) ?
161
+ $.widget.extend( {}, target[ key ], value ) :
162
+ // Don't extend strings, arrays, etc. with objects
163
+ $.widget.extend( {}, value );
164
+ // Copy everything else by reference
165
+ } else {
166
+ target[ key ] = value;
167
+ }
156
168
  }
157
169
  }
158
170
  }
@@ -160,7 +172,7 @@ $.widget.extend = function( target ) {
160
172
  };
161
173
 
162
174
  $.widget.bridge = function( name, object ) {
163
- var fullName = object.prototype.widgetFullName;
175
+ var fullName = object.prototype.widgetFullName || name;
164
176
  $.fn[ name ] = function( options ) {
165
177
  var isMethodCall = typeof options === "string",
166
178
  args = slice.call( arguments, 1 ),
@@ -196,7 +208,7 @@ $.widget.bridge = function( name, object ) {
196
208
  if ( instance ) {
197
209
  instance.option( options || {} )._init();
198
210
  } else {
199
- new object( options, this );
211
+ $.data( this, fullName, new object( options, this ) );
200
212
  }
201
213
  });
202
214
  }
@@ -205,7 +217,7 @@ $.widget.bridge = function( name, object ) {
205
217
  };
206
218
  };
207
219
 
208
- $.Widget = function( options, element ) {};
220
+ $.Widget = function( /* options, element */ ) {};
209
221
  $.Widget._childConstructors = [];
210
222
 
211
223
  $.Widget.prototype = {
@@ -233,11 +245,14 @@ $.Widget.prototype = {
233
245
  this.focusable = $();
234
246
 
235
247
  if ( element !== this ) {
236
- // 1.9 BC for #7810
237
- // TODO remove dual storage
238
- $.data( element, this.widgetName, this );
239
248
  $.data( element, this.widgetFullName, this );
240
- this._on({ remove: "destroy" });
249
+ this._on( true, this.element, {
250
+ remove: function( event ) {
251
+ if ( event.target === element ) {
252
+ this.destroy();
253
+ }
254
+ }
255
+ });
241
256
  this.document = $( element.style ?
242
257
  // element within the document
243
258
  element.ownerDocument :
@@ -355,25 +370,36 @@ $.Widget.prototype = {
355
370
  return this._setOption( "disabled", true );
356
371
  },
357
372
 
358
- _on: function( element, handlers ) {
373
+ _on: function( suppressDisabledCheck, element, handlers ) {
374
+ var delegateElement,
375
+ instance = this;
376
+
377
+ // no suppressDisabledCheck flag, shuffle arguments
378
+ if ( typeof suppressDisabledCheck !== "boolean" ) {
379
+ handlers = element;
380
+ element = suppressDisabledCheck;
381
+ suppressDisabledCheck = false;
382
+ }
383
+
359
384
  // no element argument, shuffle and use this.element
360
385
  if ( !handlers ) {
361
386
  handlers = element;
362
387
  element = this.element;
388
+ delegateElement = this.widget();
363
389
  } else {
364
390
  // accept selectors, DOM elements
365
- element = $( element );
391
+ element = delegateElement = $( element );
366
392
  this.bindings = this.bindings.add( element );
367
393
  }
368
394
 
369
- var instance = this;
370
395
  $.each( handlers, function( event, handler ) {
371
396
  function handlerProxy() {
372
397
  // allow widgets to customize the disabled handling
373
398
  // - disabled as an array instead of boolean
374
399
  // - disabled class as method for disabling individual parts
375
- if ( instance.options.disabled === true ||
376
- $( this ).hasClass( "ui-state-disabled" ) ) {
400
+ if ( !suppressDisabledCheck &&
401
+ ( instance.options.disabled === true ||
402
+ $( this ).hasClass( "ui-state-disabled" ) ) ) {
377
403
  return;
378
404
  }
379
405
  return ( typeof handler === "string" ? instance[ handler ] : handler )
@@ -390,7 +416,7 @@ $.Widget.prototype = {
390
416
  eventName = match[1] + instance.eventNamespace,
391
417
  selector = match[2];
392
418
  if ( selector ) {
393
- instance.widget().delegate( selector, eventName, handlerProxy );
419
+ delegateElement.delegate( selector, eventName, handlerProxy );
394
420
  } else {
395
421
  element.bind( eventName, handlerProxy );
396
422
  }
@@ -485,7 +511,7 @@ $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
485
511
  if ( options.delay ) {
486
512
  element.delay( options.delay );
487
513
  }
488
- if ( hasOptions && $.effects && ( $.effects.effect[ effectName ] || $.uiBackCompat !== false && $.effects[ effectName ] ) ) {
514
+ if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
489
515
  element[ method ]( options );
490
516
  } else if ( effectName !== method && element[ effectName ] ) {
491
517
  element[ effectName ]( options.duration, options.easing, callback );
@@ -501,11 +527,4 @@ $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
501
527
  };
502
528
  });
503
529
 
504
- // DEPRECATED
505
- if ( $.uiBackCompat !== false ) {
506
- $.Widget.prototype._getCreateOptions = function() {
507
- return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ];
508
- };
509
- }
510
-
511
530
  }));
@@ -1,18 +1,20 @@
1
1
  /*
2
- * JavaScript Load Image 1.2.1
2
+ * JavaScript Load Image 1.5
3
3
  * https://github.com/blueimp/JavaScript-Load-Image
4
4
  *
5
5
  * Copyright 2011, Sebastian Tschan
6
6
  * https://blueimp.net
7
7
  *
8
+ * iOS image scaling fixes based on
9
+ * https://github.com/stomita/ios-imagefile-megapixel
10
+ *
8
11
  * Licensed under the MIT license:
9
12
  * http://www.opensource.org/licenses/MIT
10
13
  */
11
14
 
12
- /*jslint nomen: true */
15
+ /*jslint nomen: true, bitwise: true */
13
16
  /*global window, document, URL, webkitURL, Blob, File, FileReader, define */
14
17
 
15
-
16
18
  (function ($) {
17
19
  'use strict';
18
20
 
@@ -28,22 +30,38 @@
28
30
  if (oUrl && !(options && options.noRevoke)) {
29
31
  loadImage.revokeObjectURL(oUrl);
30
32
  }
31
- callback(loadImage.scale(img, options));
33
+ if (callback) {
34
+ callback(loadImage.scale(img, options));
35
+ }
32
36
  };
33
- if ((window.Blob && file instanceof Blob) ||
34
- // Files are also Blob instances, but some browsers
35
- // (Firefox 3.6) support the File API but not Blobs:
36
- (window.File && file instanceof File)) {
37
+ if (loadImage.isInstanceOf('Blob', file) ||
38
+ // Files are also Blob instances, but some browsers
39
+ // (Firefox 3.6) support the File API but not Blobs:
40
+ loadImage.isInstanceOf('File', file)) {
37
41
  url = oUrl = loadImage.createObjectURL(file);
38
- } else {
42
+ // Store the file type for resize processing:
43
+ img._type = file.type;
44
+ } else if (typeof file === 'string') {
39
45
  url = file;
46
+ if (options && options.crossOrigin) {
47
+ img.crossOrigin = options.crossOrigin;
48
+ }
49
+ } else {
50
+ return false;
40
51
  }
41
52
  if (url) {
42
53
  img.src = url;
43
54
  return img;
44
55
  }
45
- return loadImage.readFile(file, function (url) {
46
- img.src = url;
56
+ return loadImage.readFile(file, function (e) {
57
+ var target = e.target;
58
+ if (target && target.result) {
59
+ img.src = target.result;
60
+ } else {
61
+ if (callback) {
62
+ callback(e);
63
+ }
64
+ }
47
65
  });
48
66
  },
49
67
  // The check for URL.revokeObjectURL fixes an issue with Opera 12,
@@ -52,41 +70,212 @@
52
70
  (window.URL && URL.revokeObjectURL && URL) ||
53
71
  (window.webkitURL && webkitURL);
54
72
 
73
+ loadImage.isInstanceOf = function (type, obj) {
74
+ // Cross-frame instanceof check
75
+ return Object.prototype.toString.call(obj) === '[object ' + type + ']';
76
+ };
77
+
78
+ // Detects subsampling in JPEG images:
79
+ loadImage.detectSubsampling = function (img) {
80
+ var canvas,
81
+ context;
82
+ if (img.width * img.height > 1024 * 1024) { // only consider mexapixel images
83
+ canvas = document.createElement('canvas');
84
+ canvas.width = canvas.height = 1;
85
+ context = canvas.getContext('2d');
86
+ context.drawImage(img, -img.width + 1, 0);
87
+ // subsampled image becomes half smaller in rendering size.
88
+ // check alpha channel value to confirm image is covering edge pixel or not.
89
+ // if alpha value is 0 image is not covering, hence subsampled.
90
+ return context.getImageData(0, 0, 1, 1).data[3] === 0;
91
+ }
92
+ return false;
93
+ };
94
+
95
+ // Detects vertical squash in JPEG images:
96
+ loadImage.detectVerticalSquash = function (img, correctedHeight) {
97
+ var canvas = document.createElement('canvas'),
98
+ context = canvas.getContext('2d'),
99
+ data,
100
+ sy,
101
+ ey,
102
+ py,
103
+ alpha;
104
+ canvas.width = 1;
105
+ canvas.height = correctedHeight;
106
+ context.drawImage(img, 0, 0);
107
+ data = context.getImageData(0, 0, 1, correctedHeight).data;
108
+ // search image edge pixel position in case it is squashed vertically:
109
+ sy = 0;
110
+ ey = correctedHeight;
111
+ py = correctedHeight;
112
+ while (py > sy) {
113
+ alpha = data[(py - 1) * 4 + 3];
114
+ if (alpha === 0) {
115
+ ey = py;
116
+ } else {
117
+ sy = py;
118
+ }
119
+ py = (ey + sy) >> 1;
120
+ }
121
+ return (py / correctedHeight) || 1;
122
+ };
123
+
124
+ // Renders image to canvas while working around iOS image scaling bugs:
125
+ // https://github.com/blueimp/JavaScript-Load-Image/issues/13
126
+ loadImage.renderImageToCanvas = function (
127
+ canvas,
128
+ img,
129
+ sourceX,
130
+ sourceY,
131
+ sourceWidth,
132
+ sourceHeight,
133
+ destX,
134
+ destY,
135
+ destWidth,
136
+ destHeight
137
+ ) {
138
+ var context = canvas.getContext('2d'),
139
+ tmpCanvas = document.createElement('canvas'),
140
+ tileSize = tmpCanvas.width = tmpCanvas.height = 1024,
141
+ tmpContext = tmpCanvas.getContext('2d'),
142
+ vertSquashRatio,
143
+ tileX,
144
+ tileY;
145
+ context.save();
146
+ if (loadImage.detectSubsampling(img)) {
147
+ sourceWidth /= 2;
148
+ sourceHeight /= 2;
149
+ }
150
+ vertSquashRatio = loadImage.detectVerticalSquash(img, sourceHeight);
151
+ destWidth = Math.ceil(tileSize * destWidth / sourceWidth);
152
+ destHeight = Math.ceil(
153
+ tileSize * destHeight / sourceHeight / vertSquashRatio
154
+ );
155
+ destY = 0;
156
+ tileY = 0;
157
+ while (tileY < sourceHeight) {
158
+ destX = 0;
159
+ tileX = 0;
160
+ while (tileX < sourceWidth) {
161
+ tmpContext.clearRect(0, 0, tileSize, tileSize);
162
+ tmpContext.drawImage(
163
+ img,
164
+ sourceX,
165
+ sourceY,
166
+ sourceWidth,
167
+ sourceHeight,
168
+ -tileX,
169
+ -tileY,
170
+ sourceWidth,
171
+ sourceHeight
172
+ );
173
+ context.drawImage(
174
+ tmpCanvas,
175
+ 0,
176
+ 0,
177
+ tileSize,
178
+ tileSize,
179
+ destX,
180
+ destY,
181
+ destWidth,
182
+ destHeight
183
+ );
184
+ tileX += tileSize;
185
+ destX += destWidth;
186
+ }
187
+ tileY += tileSize;
188
+ destY += destHeight;
189
+ }
190
+ context.restore();
191
+ };
192
+
55
193
  // Scales the given image (img or canvas HTML element)
56
194
  // using the given options.
57
195
  // Returns a canvas object if the browser supports canvas
58
- // and the canvas option is true or a canvas object is passed
59
- // as image, else the scaled image:
196
+ // and the canvas or crop option is true or a canvas object
197
+ // is passed as image, else the scaled image:
60
198
  loadImage.scale = function (img, options) {
61
199
  options = options || {};
62
200
  var canvas = document.createElement('canvas'),
201
+ useCanvas = img.getContext ||
202
+ ((options.canvas || options.crop) && canvas.getContext),
63
203
  width = img.width,
64
204
  height = img.height,
205
+ maxWidth = options.maxWidth,
206
+ maxHeight = options.maxHeight,
207
+ sourceWidth = width,
208
+ sourceHeight = height,
209
+ sourceX = 0,
210
+ sourceY = 0,
211
+ destX = 0,
212
+ destY = 0,
213
+ destWidth,
214
+ destHeight,
215
+ scale;
216
+ if (useCanvas && maxWidth && maxHeight && options.crop) {
217
+ destWidth = maxWidth;
218
+ destHeight = maxHeight;
219
+ if (width / height < maxWidth / maxHeight) {
220
+ sourceHeight = maxHeight * width / maxWidth;
221
+ sourceY = (height - sourceHeight) / 2;
222
+ } else {
223
+ sourceWidth = maxWidth * height / maxHeight;
224
+ sourceX = (width - sourceWidth) / 2;
225
+ }
226
+ } else {
227
+ destWidth = width;
228
+ destHeight = height;
65
229
  scale = Math.max(
66
- (options.minWidth || width) / width,
67
- (options.minHeight || height) / height
230
+ (options.minWidth || destWidth) / destWidth,
231
+ (options.minHeight || destHeight) / destHeight
68
232
  );
69
- if (scale > 1) {
70
- width = parseInt(width * scale, 10);
71
- height = parseInt(height * scale, 10);
72
- }
73
- scale = Math.min(
74
- (options.maxWidth || width) / width,
75
- (options.maxHeight || height) / height
76
- );
77
- if (scale < 1) {
78
- width = parseInt(width * scale, 10);
79
- height = parseInt(height * scale, 10);
233
+ if (scale > 1) {
234
+ destWidth = Math.ceil(destWidth * scale);
235
+ destHeight = Math.ceil(destHeight * scale);
236
+ }
237
+ scale = Math.min(
238
+ (maxWidth || destWidth) / destWidth,
239
+ (maxHeight || destHeight) / destHeight
240
+ );
241
+ if (scale < 1) {
242
+ destWidth = Math.ceil(destWidth * scale);
243
+ destHeight = Math.ceil(destHeight * scale);
244
+ }
80
245
  }
81
- if (img.getContext || (options.canvas && canvas.getContext)) {
82
- canvas.width = width;
83
- canvas.height = height;
84
- canvas.getContext('2d')
85
- .drawImage(img, 0, 0, width, height);
246
+ if (useCanvas) {
247
+ canvas.width = destWidth;
248
+ canvas.height = destHeight;
249
+ if (img._type === 'image/jpeg') {
250
+ loadImage.renderImageToCanvas(
251
+ canvas,
252
+ img,
253
+ sourceX,
254
+ sourceY,
255
+ sourceWidth,
256
+ sourceHeight,
257
+ destX,
258
+ destY,
259
+ destWidth,
260
+ destHeight
261
+ );
262
+ } else {
263
+ canvas.getContext('2d').drawImage(
264
+ img,
265
+ sourceX,
266
+ sourceY,
267
+ sourceWidth,
268
+ sourceHeight,
269
+ destX,
270
+ destY,
271
+ destWidth,
272
+ destHeight
273
+ );
274
+ }
86
275
  return canvas;
87
276
  }
88
- img.width = width;
89
- img.height = height;
277
+ img.width = destWidth;
278
+ img.height = destHeight;
90
279
  return img;
91
280
  };
92
281
 
@@ -99,13 +288,12 @@
99
288
  };
100
289
 
101
290
  // Loads a given File object via FileReader interface,
102
- // invokes the callback with a data url:
291
+ // invokes the callback with the event object (load or error).
292
+ // The result can be read via event.target.result:
103
293
  loadImage.readFile = function (file, callback) {
104
294
  if (window.FileReader && FileReader.prototype.readAsDataURL) {
105
295
  var fileReader = new FileReader();
106
- fileReader.onload = function (e) {
107
- callback(e.target.result);
108
- };
296
+ fileReader.onload = fileReader.onerror = callback;
109
297
  fileReader.readAsDataURL(file);
110
298
  return fileReader;
111
299
  }