jquery.fileupload-image.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. /*
  2. * jQuery File Upload Image Preview & Resize Plugin
  3. * https://github.com/blueimp/jQuery-File-Upload
  4. *
  5. * Copyright 2013, Sebastian Tschan
  6. * https://blueimp.net
  7. *
  8. * Licensed under the MIT license:
  9. * https://opensource.org/licenses/MIT
  10. */
  11. /* global define, require */
  12. (function(factory) {
  13. 'use strict';
  14. if (typeof define === 'function' && define.amd) {
  15. // Register as an anonymous AMD module:
  16. define([
  17. 'jquery',
  18. 'load-image',
  19. 'load-image-meta',
  20. 'load-image-scale',
  21. 'load-image-exif',
  22. 'canvas-to-blob',
  23. './jquery.fileupload-process'
  24. ], factory);
  25. } else if (typeof exports === 'object') {
  26. // Node/CommonJS:
  27. factory(
  28. require('jquery'),
  29. require('blueimp-load-image/js/load-image'),
  30. require('blueimp-load-image/js/load-image-meta'),
  31. require('blueimp-load-image/js/load-image-scale'),
  32. require('blueimp-load-image/js/load-image-exif'),
  33. require('blueimp-canvas-to-blob'),
  34. require('./jquery.fileupload-process')
  35. );
  36. } else {
  37. // Browser globals:
  38. factory(window.jQuery, window.loadImage);
  39. }
  40. })(function($, loadImage) {
  41. 'use strict';
  42. // Prepend to the default processQueue:
  43. $.blueimp.fileupload.prototype.options.processQueue.unshift(
  44. {
  45. action: 'loadImageMetaData',
  46. disableImageHead: '@',
  47. disableExif: '@',
  48. disableExifThumbnail: '@',
  49. disableExifSub: '@',
  50. disableExifGps: '@',
  51. disabled: '@disableImageMetaDataLoad'
  52. },
  53. {
  54. action: 'loadImage',
  55. // Use the action as prefix for the "@" options:
  56. prefix: true,
  57. fileTypes: '@',
  58. maxFileSize: '@',
  59. noRevoke: '@',
  60. disabled: '@disableImageLoad'
  61. },
  62. {
  63. action: 'resizeImage',
  64. // Use "image" as prefix for the "@" options:
  65. prefix: 'image',
  66. maxWidth: '@',
  67. maxHeight: '@',
  68. minWidth: '@',
  69. minHeight: '@',
  70. crop: '@',
  71. orientation: '@',
  72. forceResize: '@',
  73. disabled: '@disableImageResize'
  74. },
  75. {
  76. action: 'saveImage',
  77. quality: '@imageQuality',
  78. type: '@imageType',
  79. disabled: '@disableImageResize'
  80. },
  81. {
  82. action: 'saveImageMetaData',
  83. disabled: '@disableImageMetaDataSave'
  84. },
  85. {
  86. action: 'resizeImage',
  87. // Use "preview" as prefix for the "@" options:
  88. prefix: 'preview',
  89. maxWidth: '@',
  90. maxHeight: '@',
  91. minWidth: '@',
  92. minHeight: '@',
  93. crop: '@',
  94. orientation: '@',
  95. thumbnail: '@',
  96. canvas: '@',
  97. disabled: '@disableImagePreview'
  98. },
  99. {
  100. action: 'setImage',
  101. name: '@imagePreviewName',
  102. disabled: '@disableImagePreview'
  103. },
  104. {
  105. action: 'deleteImageReferences',
  106. disabled: '@disableImageReferencesDeletion'
  107. }
  108. );
  109. // The File Upload Resize plugin extends the fileupload widget
  110. // with image resize functionality:
  111. $.widget('blueimp.fileupload', $.blueimp.fileupload, {
  112. options: {
  113. // The regular expression for the types of images to load:
  114. // matched against the file type:
  115. loadImageFileTypes: /^image\/(gif|jpeg|png|svg\+xml)$/,
  116. // The maximum file size of images to load:
  117. loadImageMaxFileSize: 10000000, // 10MB
  118. // The maximum width of resized images:
  119. imageMaxWidth: 1920,
  120. // The maximum height of resized images:
  121. imageMaxHeight: 1080,
  122. // Defines the image orientation (1-8) or takes the orientation
  123. // value from Exif data if set to true:
  124. imageOrientation: false,
  125. // Define if resized images should be cropped or only scaled:
  126. imageCrop: false,
  127. // Disable the resize image functionality by default:
  128. disableImageResize: true,
  129. // The maximum width of the preview images:
  130. previewMaxWidth: 80,
  131. // The maximum height of the preview images:
  132. previewMaxHeight: 80,
  133. // Defines the preview orientation (1-8) or takes the orientation
  134. // value from Exif data if set to true:
  135. previewOrientation: true,
  136. // Create the preview using the Exif data thumbnail:
  137. previewThumbnail: true,
  138. // Define if preview images should be cropped or only scaled:
  139. previewCrop: false,
  140. // Define if preview images should be resized as canvas elements:
  141. previewCanvas: true
  142. },
  143. processActions: {
  144. // Loads the image given via data.files and data.index
  145. // as img element, if the browser supports the File API.
  146. // Accepts the options fileTypes (regular expression)
  147. // and maxFileSize (integer) to limit the files to load:
  148. loadImage: function(data, options) {
  149. if (options.disabled) {
  150. return data;
  151. }
  152. var that = this,
  153. file = data.files[data.index],
  154. // eslint-disable-next-line new-cap
  155. dfd = $.Deferred();
  156. if (
  157. ($.type(options.maxFileSize) === 'number' &&
  158. file.size > options.maxFileSize) ||
  159. (options.fileTypes && !options.fileTypes.test(file.type)) ||
  160. !loadImage(
  161. file,
  162. function(img) {
  163. if (img.src) {
  164. data.img = img;
  165. }
  166. dfd.resolveWith(that, [data]);
  167. },
  168. options
  169. )
  170. ) {
  171. return data;
  172. }
  173. return dfd.promise();
  174. },
  175. // Resizes the image given as data.canvas or data.img
  176. // and updates data.canvas or data.img with the resized image.
  177. // Also stores the resized image as preview property.
  178. // Accepts the options maxWidth, maxHeight, minWidth,
  179. // minHeight, canvas and crop:
  180. resizeImage: function(data, options) {
  181. if (options.disabled || !(data.canvas || data.img)) {
  182. return data;
  183. }
  184. // eslint-disable-next-line no-param-reassign
  185. options = $.extend({ canvas: true }, options);
  186. var that = this,
  187. // eslint-disable-next-line new-cap
  188. dfd = $.Deferred(),
  189. img = (options.canvas && data.canvas) || data.img,
  190. resolve = function(newImg) {
  191. if (
  192. newImg &&
  193. (newImg.width !== img.width ||
  194. newImg.height !== img.height ||
  195. options.forceResize)
  196. ) {
  197. data[newImg.getContext ? 'canvas' : 'img'] = newImg;
  198. }
  199. data.preview = newImg;
  200. dfd.resolveWith(that, [data]);
  201. },
  202. thumbnail;
  203. if (data.exif) {
  204. if (options.orientation === true) {
  205. options.orientation = data.exif.get('Orientation');
  206. }
  207. if (options.thumbnail) {
  208. thumbnail = data.exif.get('Thumbnail');
  209. if (thumbnail) {
  210. loadImage(thumbnail, resolve, options);
  211. return dfd.promise();
  212. }
  213. }
  214. // Prevent orienting the same image twice:
  215. if (data.orientation) {
  216. delete options.orientation;
  217. } else {
  218. data.orientation = options.orientation;
  219. }
  220. }
  221. if (img) {
  222. resolve(loadImage.scale(img, options));
  223. return dfd.promise();
  224. }
  225. return data;
  226. },
  227. // Saves the processed image given as data.canvas
  228. // inplace at data.index of data.files:
  229. saveImage: function(data, options) {
  230. if (!data.canvas || options.disabled) {
  231. return data;
  232. }
  233. var that = this,
  234. file = data.files[data.index],
  235. // eslint-disable-next-line new-cap
  236. dfd = $.Deferred();
  237. if (data.canvas.toBlob) {
  238. data.canvas.toBlob(
  239. function(blob) {
  240. if (!blob.name) {
  241. if (file.type === blob.type) {
  242. blob.name = file.name;
  243. } else if (file.name) {
  244. blob.name = file.name.replace(
  245. /\.\w+$/,
  246. '.' + blob.type.substr(6)
  247. );
  248. }
  249. }
  250. // Don't restore invalid meta data:
  251. if (file.type !== blob.type) {
  252. delete data.imageHead;
  253. }
  254. // Store the created blob at the position
  255. // of the original file in the files list:
  256. data.files[data.index] = blob;
  257. dfd.resolveWith(that, [data]);
  258. },
  259. options.type || file.type,
  260. options.quality
  261. );
  262. } else {
  263. return data;
  264. }
  265. return dfd.promise();
  266. },
  267. loadImageMetaData: function(data, options) {
  268. if (options.disabled) {
  269. return data;
  270. }
  271. var that = this,
  272. // eslint-disable-next-line new-cap
  273. dfd = $.Deferred();
  274. loadImage.parseMetaData(
  275. data.files[data.index],
  276. function(result) {
  277. $.extend(data, result);
  278. dfd.resolveWith(that, [data]);
  279. },
  280. options
  281. );
  282. return dfd.promise();
  283. },
  284. saveImageMetaData: function(data, options) {
  285. if (
  286. !(
  287. data.imageHead &&
  288. data.canvas &&
  289. data.canvas.toBlob &&
  290. !options.disabled
  291. )
  292. ) {
  293. return data;
  294. }
  295. var file = data.files[data.index],
  296. blob = new Blob(
  297. [
  298. data.imageHead,
  299. // Resized images always have a head size of 20 bytes,
  300. // including the JPEG marker and a minimal JFIF header:
  301. this._blobSlice.call(file, 20)
  302. ],
  303. { type: file.type }
  304. );
  305. blob.name = file.name;
  306. data.files[data.index] = blob;
  307. return data;
  308. },
  309. // Sets the resized version of the image as a property of the
  310. // file object, must be called after "saveImage":
  311. setImage: function(data, options) {
  312. if (data.preview && !options.disabled) {
  313. data.files[data.index][options.name || 'preview'] = data.preview;
  314. }
  315. return data;
  316. },
  317. deleteImageReferences: function(data, options) {
  318. if (!options.disabled) {
  319. delete data.img;
  320. delete data.canvas;
  321. delete data.preview;
  322. delete data.imageHead;
  323. }
  324. return data;
  325. }
  326. }
  327. });
  328. });