├── .gitignore ├── .npmignore ├── .travis.yml ├── 1.chunk.js ├── 2.chunk.js ├── ISSUE_TEMPLATE.md ├── README.md ├── bower.json ├── dist ├── 1.chunk.js ├── 1.chunk.js.map ├── 2.chunk.js ├── 2.chunk.js.map ├── lrz.all.bundle.js ├── lrz.bundle.js └── 那么多文件,应该引用哪个?.txt ├── gulpfile.js ├── karma.conf.js ├── lrz.all.bundle.js ├── lrz.bundle.js ├── package.json ├── src ├── lib │ ├── Blob.FormData.shim.js │ ├── Promise.js │ ├── bootstrap.min.css │ ├── exif.js │ ├── jpeg_encoder_basic.js │ └── megapix-image.js ├── lrz.all.js ├── lrz.js └── 那么多文件,应该引用哪个?.txt ├── test ├── demo.gif ├── img │ ├── loading.gif │ ├── orientation_1.JPG │ ├── orientation_3.JPG │ ├── orientation_6.JPG │ ├── orientation_8.JPG │ └── transparent_png.png ├── index.html ├── index.js ├── lrz.spec.js ├── qrcode.png ├── server.html └── server.js ├── webpack.config.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .idea 3 | node_modules 4 | bower_components -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .idea 3 | node_modules 4 | bower_components 5 | test 6 | src 7 | gulpfile.js 8 | webpack.config.js -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.12" -------------------------------------------------------------------------------- /1.chunk.js: -------------------------------------------------------------------------------- 1 | webpackJsonp([1],{ 2 | 3 | /***/ 7: 4 | /***/ (function(module, exports, __webpack_require__) { 5 | 6 | var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/** 7 | * Mega pixel image rendering library for iOS6 Safari 8 | * 9 | * Fixes iOS6 Safari's image file rendering issue for large size image (over mega-pixel), 10 | * which causes unexpected subsampling when drawing it in canvas. 11 | * By using this library, you can safely render the image with proper stretching. 12 | * 13 | * Copyright (c) 2012 Shinichi Tomita 14 | * Released under the MIT license 15 | */ 16 | (function () { 17 | 18 | /** 19 | * Detect subsampling in loaded image. 20 | * In iOS, larger images than 2M pixels may be subsampled in rendering. 21 | */ 22 | function detectSubsampling (img) { 23 | var iw = img.naturalWidth, ih = img.naturalHeight; 24 | if (iw * ih > 1024 * 1024) { // subsampling may happen over megapixel image 25 | var canvas = document.createElement('canvas'); 26 | canvas.width = canvas.height = 1; 27 | var ctx = canvas.getContext('2d'); 28 | ctx.drawImage(img, -iw + 1, 0); 29 | // subsampled image becomes half smaller in rendering size. 30 | // check alpha channel value to confirm image is covering edge pixel or not. 31 | // if alpha value is 0 image is not covering, hence subsampled. 32 | return ctx.getImageData(0, 0, 1, 1).data[3] === 0; 33 | } else { 34 | return false; 35 | } 36 | } 37 | 38 | /** 39 | * Detecting vertical squash in loaded image. 40 | * Fixes a bug which squash image vertically while drawing into canvas for some images. 41 | */ 42 | function detectVerticalSquash (img, iw, ih) { 43 | var canvas = document.createElement('canvas'); 44 | canvas.width = 1; 45 | canvas.height = ih; 46 | var ctx = canvas.getContext('2d'); 47 | ctx.drawImage(img, 0, 0); 48 | var data = ctx.getImageData(0, 0, 1, ih).data; 49 | // search image edge pixel position in case it is squashed vertically. 50 | var sy = 0; 51 | var ey = ih; 52 | var py = ih; 53 | while (py > sy) { 54 | var alpha = data[(py - 1) * 4 + 3]; 55 | if (alpha === 0) { 56 | ey = py; 57 | } else { 58 | sy = py; 59 | } 60 | py = (ey + sy) >> 1; 61 | } 62 | var ratio = (py / ih); 63 | return (ratio === 0) ? 1 : ratio; 64 | } 65 | 66 | /** 67 | * Rendering image element (with resizing) and get its data URL 68 | */ 69 | function renderImageToDataURL (img, options, doSquash) { 70 | var canvas = document.createElement('canvas'); 71 | renderImageToCanvas(img, canvas, options, doSquash); 72 | return canvas.toDataURL("image/jpeg", options.quality || 0.8); 73 | } 74 | 75 | /** 76 | * Rendering image element (with resizing) into the canvas element 77 | */ 78 | function renderImageToCanvas (img, canvas, options, doSquash) { 79 | var iw = img.naturalWidth, ih = img.naturalHeight; 80 | var width = options.width, height = options.height; 81 | var ctx = canvas.getContext('2d'); 82 | ctx.save(); 83 | transformCoordinate(canvas, ctx, width, height, options.orientation); 84 | var subsampled = detectSubsampling(img); 85 | if (subsampled) { 86 | iw /= 2; 87 | ih /= 2; 88 | } 89 | var d = 1024; // size of tiling canvas 90 | var tmpCanvas = document.createElement('canvas'); 91 | tmpCanvas.width = tmpCanvas.height = d; 92 | var tmpCtx = tmpCanvas.getContext('2d'); 93 | var vertSquashRatio = doSquash ? detectVerticalSquash(img, iw, ih) : 1; 94 | var dw = Math.ceil(d * width / iw); 95 | var dh = Math.ceil(d * height / ih / vertSquashRatio); 96 | var sy = 0; 97 | var dy = 0; 98 | while (sy < ih) { 99 | var sx = 0; 100 | var dx = 0; 101 | while (sx < iw) { 102 | tmpCtx.clearRect(0, 0, d, d); 103 | tmpCtx.drawImage(img, -sx, -sy); 104 | ctx.drawImage(tmpCanvas, 0, 0, d, d, dx, dy, dw, dh); 105 | sx += d; 106 | dx += dw; 107 | } 108 | sy += d; 109 | dy += dh; 110 | } 111 | ctx.restore(); 112 | tmpCanvas = tmpCtx = null; 113 | } 114 | 115 | /** 116 | * Transform canvas coordination according to specified frame size and orientation 117 | * Orientation value is from EXIF tag 118 | */ 119 | function transformCoordinate (canvas, ctx, width, height, orientation) { 120 | switch (orientation) { 121 | case 5: 122 | case 6: 123 | case 7: 124 | case 8: 125 | canvas.width = height; 126 | canvas.height = width; 127 | break; 128 | default: 129 | canvas.width = width; 130 | canvas.height = height; 131 | } 132 | switch (orientation) { 133 | case 2: 134 | // horizontal flip 135 | ctx.translate(width, 0); 136 | ctx.scale(-1, 1); 137 | break; 138 | case 3: 139 | // 180 rotate left 140 | ctx.translate(width, height); 141 | ctx.rotate(Math.PI); 142 | break; 143 | case 4: 144 | // vertical flip 145 | ctx.translate(0, height); 146 | ctx.scale(1, -1); 147 | break; 148 | case 5: 149 | // vertical flip + 90 rotate right 150 | ctx.rotate(0.5 * Math.PI); 151 | ctx.scale(1, -1); 152 | break; 153 | case 6: 154 | // 90 rotate right 155 | ctx.rotate(0.5 * Math.PI); 156 | ctx.translate(0, -height); 157 | break; 158 | case 7: 159 | // horizontal flip + 90 rotate right 160 | ctx.rotate(0.5 * Math.PI); 161 | ctx.translate(width, -height); 162 | ctx.scale(-1, 1); 163 | break; 164 | case 8: 165 | // 90 rotate left 166 | ctx.rotate(-0.5 * Math.PI); 167 | ctx.translate(-width, 0); 168 | break; 169 | default: 170 | break; 171 | } 172 | } 173 | 174 | 175 | /** 176 | * MegaPixImage class 177 | */ 178 | function MegaPixImage (srcImage) { 179 | if (window.Blob && srcImage instanceof Blob) { 180 | var img = new Image(); 181 | var URL = window.URL && window.URL.createObjectURL ? window.URL : 182 | window.webkitURL && window.webkitURL.createObjectURL ? window.webkitURL : 183 | null; 184 | if (!URL) { 185 | throw Error("No createObjectURL function found to create blob url"); 186 | } 187 | img.src = URL.createObjectURL(srcImage); 188 | this.blob = srcImage; 189 | srcImage = img; 190 | } 191 | if (!srcImage.naturalWidth && !srcImage.naturalHeight) { 192 | var _this = this; 193 | srcImage.onload = function () { 194 | var listeners = _this.imageLoadListeners; 195 | if (listeners) { 196 | _this.imageLoadListeners = null; 197 | for (var i = 0, len = listeners.length; i < len; i++) { 198 | listeners[i](); 199 | } 200 | } 201 | }; 202 | this.imageLoadListeners = []; 203 | } 204 | this.srcImage = srcImage; 205 | } 206 | 207 | /** 208 | * Rendering megapix image into specified target element 209 | */ 210 | MegaPixImage.prototype.render = function (target, options, callback) { 211 | if (this.imageLoadListeners) { 212 | var _this = this; 213 | this.imageLoadListeners.push(function () { 214 | _this.render(target, options, callback); 215 | }); 216 | return; 217 | } 218 | options = options || {}; 219 | var srcImage = this.srcImage, 220 | src = srcImage.src, 221 | srcLength = src.length, 222 | imgWidth = srcImage.naturalWidth, imgHeight = srcImage.naturalHeight, 223 | width = options.width, height = options.height, 224 | maxWidth = options.maxWidth, maxHeight = options.maxHeight, 225 | doSquash = this.blob && this.blob.type === 'image/jpeg' || 226 | src.indexOf('data:image/jpeg') === 0 || 227 | src.indexOf('.jpg') === srcLength - 4 || 228 | src.indexOf('.jpeg') === srcLength - 5; 229 | if (width && !height) { 230 | height = (imgHeight * width / imgWidth) << 0; 231 | } else if (height && !width) { 232 | width = (imgWidth * height / imgHeight) << 0; 233 | } else { 234 | width = imgWidth; 235 | height = imgHeight; 236 | } 237 | if (maxWidth && width > maxWidth) { 238 | width = maxWidth; 239 | height = (imgHeight * width / imgWidth) << 0; 240 | } 241 | if (maxHeight && height > maxHeight) { 242 | height = maxHeight; 243 | width = (imgWidth * height / imgHeight) << 0; 244 | } 245 | var opt = {width: width, height: height}; 246 | for (var k in options) opt[k] = options[k]; 247 | 248 | var tagName = target.tagName.toLowerCase(); 249 | if (tagName === 'img') { 250 | target.src = renderImageToDataURL(this.srcImage, opt, doSquash); 251 | } else if (tagName === 'canvas') { 252 | renderImageToCanvas(this.srcImage, target, opt, doSquash); 253 | } 254 | if (typeof this.onrender === 'function') { 255 | this.onrender(target); 256 | } 257 | if (callback) { 258 | callback(); 259 | } 260 | }; 261 | 262 | /** 263 | * Export class to global 264 | */ 265 | if (true) { 266 | !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function () { 267 | return MegaPixImage; 268 | }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // for AMD loader 269 | } else { 270 | this.MegaPixImage = MegaPixImage; 271 | } 272 | 273 | })(); 274 | 275 | 276 | /***/ }) 277 | 278 | }); -------------------------------------------------------------------------------- /2.chunk.js: -------------------------------------------------------------------------------- 1 | webpackJsonp([2],{ 2 | 3 | /***/ 8: 4 | /***/ (function(module, exports) { 5 | 6 | function JPEGEncoder (l) { 7 | var o = this; 8 | var s = Math.round; 9 | var k = Math.floor; 10 | var O = new Array(64); 11 | var K = new Array(64); 12 | var d = new Array(64); 13 | var Z = new Array(64); 14 | var u; 15 | var h; 16 | var G; 17 | var T; 18 | var n = new Array(65535); 19 | var m = new Array(65535); 20 | var P = new Array(64); 21 | var S = new Array(64); 22 | var j = []; 23 | var t = 0; 24 | var a = 7; 25 | var A = new Array(64); 26 | var f = new Array(64); 27 | var U = new Array(64); 28 | var e = new Array(256); 29 | var C = new Array(2048); 30 | var x; 31 | var i = [0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18, 24, 31, 40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63]; 32 | var g = [0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0]; 33 | var c = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; 34 | var w = [0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125]; 35 | var E = [1, 2, 3, 0, 4, 17, 5, 18, 33, 49, 65, 6, 19, 81, 97, 7, 34, 113, 20, 50, 129, 145, 161, 8, 35, 66, 177, 193, 21, 82, 209, 240, 36, 51, 98, 114, 130, 9, 10, 22, 23, 24, 25, 26, 37, 38, 39, 40, 41, 42, 52, 53, 54, 55, 56, 57, 58, 67, 68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87, 88, 89, 90, 99, 100, 101, 102, 103, 104, 105, 106, 115, 116, 117, 118, 119, 120, 121, 122, 131, 132, 133, 134, 135, 136, 137, 138, 146, 147, 148, 149, 150, 151, 152, 153, 154, 162, 163, 164, 165, 166, 167, 168, 169, 170, 178, 179, 180, 181, 182, 183, 184, 185, 186, 194, 195, 196, 197, 198, 199, 200, 201, 202, 210, 211, 212, 213, 214, 215, 216, 217, 218, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250]; 36 | var v = [0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0]; 37 | var Y = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; 38 | var J = [0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119]; 39 | var B = [0, 1, 2, 3, 17, 4, 5, 33, 49, 6, 18, 65, 81, 7, 97, 113, 19, 34, 50, 129, 8, 20, 66, 145, 161, 177, 193, 9, 35, 51, 82, 240, 21, 98, 114, 209, 10, 22, 36, 52, 225, 37, 241, 23, 24, 25, 26, 38, 39, 40, 41, 42, 53, 54, 55, 56, 57, 58, 67, 68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87, 88, 89, 90, 99, 100, 101, 102, 103, 104, 105, 106, 115, 116, 117, 118, 119, 120, 121, 122, 130, 131, 132, 133, 134, 135, 136, 137, 138, 146, 147, 148, 149, 150, 151, 152, 153, 154, 162, 163, 164, 165, 166, 167, 168, 169, 170, 178, 179, 180, 181, 182, 183, 184, 185, 186, 194, 195, 196, 197, 198, 199, 200, 201, 202, 210, 211, 212, 213, 214, 215, 216, 217, 218, 226, 227, 228, 229, 230, 231, 232, 233, 234, 242, 243, 244, 245, 246, 247, 248, 249, 250]; 40 | 41 | function M (ag) { 42 | var af = [16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92, 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99]; 43 | for (var ae = 0; ae < 64; ae++) { 44 | var aj = k((af[ae] * ag + 50) / 100); 45 | if (aj < 1) { 46 | aj = 1 47 | } else { 48 | if (aj > 255) { 49 | aj = 255 50 | } 51 | } 52 | O[i[ae]] = aj 53 | } 54 | var ah = [17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, 24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99]; 55 | for (var ad = 0; ad < 64; ad++) { 56 | var ai = k((ah[ad] * ag + 50) / 100); 57 | if (ai < 1) { 58 | ai = 1 59 | } else { 60 | if (ai > 255) { 61 | ai = 255 62 | } 63 | } 64 | K[i[ad]] = ai 65 | } 66 | var ac = [1, 1.387039845, 1.306562965, 1.175875602, 1, 0.785694958, 0.5411961, 0.275899379]; 67 | var ab = 0; 68 | for (var ak = 0; ak < 8; ak++) { 69 | for (var aa = 0; aa < 8; aa++) { 70 | d[ab] = (1 / (O[i[ab]] * ac[ak] * ac[aa] * 8)); 71 | Z[ab] = (1 / (K[i[ab]] * ac[ak] * ac[aa] * 8)); 72 | ab++ 73 | } 74 | } 75 | } 76 | 77 | function q (ae, aa) { 78 | var ad = 0; 79 | var ag = 0; 80 | var af = new Array(); 81 | for (var ab = 1; ab <= 16; ab++) { 82 | for (var ac = 1; ac <= ae[ab]; ac++) { 83 | af[aa[ag]] = []; 84 | af[aa[ag]][0] = ad; 85 | af[aa[ag]][1] = ab; 86 | ag++; 87 | ad++ 88 | } 89 | ad *= 2 90 | } 91 | return af 92 | } 93 | 94 | function W () { 95 | u = q(g, c); 96 | h = q(v, Y); 97 | G = q(w, E); 98 | T = q(J, B) 99 | } 100 | 101 | function z () { 102 | var ac = 1; 103 | var ab = 2; 104 | for (var aa = 1; aa <= 15; aa++) { 105 | for (var ad = ac; ad < ab; ad++) { 106 | m[32767 + ad] = aa; 107 | n[32767 + ad] = []; 108 | n[32767 + ad][1] = aa; 109 | n[32767 + ad][0] = ad 110 | } 111 | for (var ae = -(ab - 1); ae <= -ac; ae++) { 112 | m[32767 + ae] = aa; 113 | n[32767 + ae] = []; 114 | n[32767 + ae][1] = aa; 115 | n[32767 + ae][0] = ab - 1 + ae 116 | } 117 | ac <<= 1; 118 | ab <<= 1 119 | } 120 | } 121 | 122 | function V () { 123 | for (var aa = 0; aa < 256; aa++) { 124 | C[aa] = 19595 * aa; 125 | C[(aa + 256) >> 0] = 38470 * aa; 126 | C[(aa + 512) >> 0] = 7471 * aa + 32768; 127 | C[(aa + 768) >> 0] = -11059 * aa; 128 | C[(aa + 1024) >> 0] = -21709 * aa; 129 | C[(aa + 1280) >> 0] = 32768 * aa + 8421375; 130 | C[(aa + 1536) >> 0] = -27439 * aa; 131 | C[(aa + 1792) >> 0] = -5329 * aa 132 | } 133 | } 134 | 135 | function X (aa) { 136 | var ac = aa[0]; 137 | var ab = aa[1] - 1; 138 | while (ab >= 0) { 139 | if (ac & (1 << ab)) { 140 | t |= (1 << a) 141 | } 142 | ab--; 143 | a--; 144 | if (a < 0) { 145 | if (t == 255) { 146 | F(255); 147 | F(0) 148 | } else { 149 | F(t) 150 | } 151 | a = 7; 152 | t = 0 153 | } 154 | } 155 | } 156 | 157 | function F (aa) { 158 | j.push(e[aa]) 159 | } 160 | 161 | function p (aa) { 162 | F((aa >> 8) & 255); 163 | F((aa) & 255) 164 | } 165 | 166 | function N (aZ, ap) { 167 | var aL, aK, aJ, aI, aH, aD, aC, aB; 168 | var aN = 0; 169 | var aR; 170 | const aq = 8; 171 | const ai = 64; 172 | for (aR = 0; aR < aq; ++aR) { 173 | aL = aZ[aN]; 174 | aK = aZ[aN + 1]; 175 | aJ = aZ[aN + 2]; 176 | aI = aZ[aN + 3]; 177 | aH = aZ[aN + 4]; 178 | aD = aZ[aN + 5]; 179 | aC = aZ[aN + 6]; 180 | aB = aZ[aN + 7]; 181 | var aY = aL + aB; 182 | var aO = aL - aB; 183 | var aX = aK + aC; 184 | var aP = aK - aC; 185 | var aU = aJ + aD; 186 | var aQ = aJ - aD; 187 | var aT = aI + aH; 188 | var aS = aI - aH; 189 | var an = aY + aT; 190 | var ak = aY - aT; 191 | var am = aX + aU; 192 | var al = aX - aU; 193 | aZ[aN] = an + am; 194 | aZ[aN + 4] = an - am; 195 | var ax = (al + ak) * 0.707106781; 196 | aZ[aN + 2] = ak + ax; 197 | aZ[aN + 6] = ak - ax; 198 | an = aS + aQ; 199 | am = aQ + aP; 200 | al = aP + aO; 201 | var at = (an - al) * 0.382683433; 202 | var aw = 0.5411961 * an + at; 203 | var au = 1.306562965 * al + at; 204 | var av = am * 0.707106781; 205 | var ah = aO + av; 206 | var ag = aO - av; 207 | aZ[aN + 5] = ag + aw; 208 | aZ[aN + 3] = ag - aw; 209 | aZ[aN + 1] = ah + au; 210 | aZ[aN + 7] = ah - au; 211 | aN += 8 212 | } 213 | aN = 0; 214 | for (aR = 0; aR < aq; ++aR) { 215 | aL = aZ[aN]; 216 | aK = aZ[aN + 8]; 217 | aJ = aZ[aN + 16]; 218 | aI = aZ[aN + 24]; 219 | aH = aZ[aN + 32]; 220 | aD = aZ[aN + 40]; 221 | aC = aZ[aN + 48]; 222 | aB = aZ[aN + 56]; 223 | var ar = aL + aB; 224 | var aj = aL - aB; 225 | var az = aK + aC; 226 | var ae = aK - aC; 227 | var aG = aJ + aD; 228 | var ac = aJ - aD; 229 | var aW = aI + aH; 230 | var aa = aI - aH; 231 | var ao = ar + aW; 232 | var aV = ar - aW; 233 | var ay = az + aG; 234 | var aF = az - aG; 235 | aZ[aN] = ao + ay; 236 | aZ[aN + 32] = ao - ay; 237 | var af = (aF + aV) * 0.707106781; 238 | aZ[aN + 16] = aV + af; 239 | aZ[aN + 48] = aV - af; 240 | ao = aa + ac; 241 | ay = ac + ae; 242 | aF = ae + aj; 243 | var aM = (ao - aF) * 0.382683433; 244 | var ad = 0.5411961 * ao + aM; 245 | var a1 = 1.306562965 * aF + aM; 246 | var ab = ay * 0.707106781; 247 | var a0 = aj + ab; 248 | var aA = aj - ab; 249 | aZ[aN + 40] = aA + ad; 250 | aZ[aN + 24] = aA - ad; 251 | aZ[aN + 8] = a0 + a1; 252 | aZ[aN + 56] = a0 - a1; 253 | aN++ 254 | } 255 | var aE; 256 | for (aR = 0; aR < ai; ++aR) { 257 | aE = aZ[aR] * ap[aR]; 258 | P[aR] = (aE > 0) ? ((aE + 0.5) | 0) : ((aE - 0.5) | 0) 259 | } 260 | return P 261 | } 262 | 263 | function b () { 264 | p(65504); 265 | p(16); 266 | F(74); 267 | F(70); 268 | F(73); 269 | F(70); 270 | F(0); 271 | F(1); 272 | F(1); 273 | F(0); 274 | p(1); 275 | p(1); 276 | F(0); 277 | F(0) 278 | } 279 | 280 | function r (aa, ab) { 281 | p(65472); 282 | p(17); 283 | F(8); 284 | p(ab); 285 | p(aa); 286 | F(3); 287 | F(1); 288 | F(17); 289 | F(0); 290 | F(2); 291 | F(17); 292 | F(1); 293 | F(3); 294 | F(17); 295 | F(1) 296 | } 297 | 298 | function D () { 299 | p(65499); 300 | p(132); 301 | F(0); 302 | for (var ab = 0; ab < 64; ab++) { 303 | F(O[ab]) 304 | } 305 | F(1); 306 | for (var aa = 0; aa < 64; aa++) { 307 | F(K[aa]) 308 | } 309 | } 310 | 311 | function H () { 312 | p(65476); 313 | p(418); 314 | F(0); 315 | for (var ae = 0; ae < 16; ae++) { 316 | F(g[ae + 1]) 317 | } 318 | for (var ad = 0; ad <= 11; ad++) { 319 | F(c[ad]) 320 | } 321 | F(16); 322 | for (var ac = 0; ac < 16; ac++) { 323 | F(w[ac + 1]) 324 | } 325 | for (var ab = 0; ab <= 161; ab++) { 326 | F(E[ab]) 327 | } 328 | F(1); 329 | for (var aa = 0; aa < 16; aa++) { 330 | F(v[aa + 1]) 331 | } 332 | for (var ah = 0; ah <= 11; ah++) { 333 | F(Y[ah]) 334 | } 335 | F(17); 336 | for (var ag = 0; ag < 16; ag++) { 337 | F(J[ag + 1]) 338 | } 339 | for (var af = 0; af <= 161; af++) { 340 | F(B[af]) 341 | } 342 | } 343 | 344 | function I () { 345 | p(65498); 346 | p(12); 347 | F(3); 348 | F(1); 349 | F(0); 350 | F(2); 351 | F(17); 352 | F(3); 353 | F(17); 354 | F(0); 355 | F(63); 356 | F(0) 357 | } 358 | 359 | function L (ad, aa, al, at, ap) { 360 | var ag = ap[0]; 361 | var ab = ap[240]; 362 | var ac; 363 | const ar = 16; 364 | const ai = 63; 365 | const ah = 64; 366 | var aq = N(ad, aa); 367 | for (var am = 0; am < ah; ++am) { 368 | S[i[am]] = aq[am] 369 | } 370 | var an = S[0] - al; 371 | al = S[0]; 372 | if (an == 0) { 373 | X(at[0]) 374 | } else { 375 | ac = 32767 + an; 376 | X(at[m[ac]]); 377 | X(n[ac]) 378 | } 379 | var ae = 63; 380 | for (; (ae > 0) && (S[ae] == 0); ae--) { 381 | } 382 | if (ae == 0) { 383 | X(ag); 384 | return al 385 | } 386 | var ao = 1; 387 | var au; 388 | while (ao <= ae) { 389 | var ak = ao; 390 | for (; (S[ao] == 0) && (ao <= ae); ++ao) { 391 | } 392 | var aj = ao - ak; 393 | if (aj >= ar) { 394 | au = aj >> 4; 395 | for (var af = 1; af <= au; ++af) { 396 | X(ab) 397 | } 398 | aj = aj & 15 399 | } 400 | ac = 32767 + S[ao]; 401 | X(ap[(aj << 4) + m[ac]]); 402 | X(n[ac]); 403 | ao++ 404 | } 405 | if (ae != ai) { 406 | X(ag) 407 | } 408 | return al 409 | } 410 | 411 | function y () { 412 | var ab = String.fromCharCode; 413 | for (var aa = 0; aa < 256; aa++) { 414 | e[aa] = ab(aa) 415 | } 416 | } 417 | 418 | this.encode = function (an, aj, aB) { 419 | var aa = new Date().getTime(); 420 | if (aj) { 421 | R(aj) 422 | } 423 | j = new Array(); 424 | t = 0; 425 | a = 7; 426 | p(65496); 427 | b(); 428 | D(); 429 | r(an.width, an.height); 430 | H(); 431 | I(); 432 | var al = 0; 433 | var aq = 0; 434 | var ao = 0; 435 | t = 0; 436 | a = 7; 437 | this.encode.displayName = "_encode_"; 438 | var at = an.data; 439 | var ar = an.width; 440 | var aA = an.height; 441 | var ay = ar * 4; 442 | var ai = ar * 3; 443 | var ah, ag = 0; 444 | var am, ax, az; 445 | var ab, ap, ac, af, ae; 446 | while (ag < aA) { 447 | ah = 0; 448 | while (ah < ay) { 449 | ab = ay * ag + ah; 450 | ap = ab; 451 | ac = -1; 452 | af = 0; 453 | for (ae = 0; ae < 64; ae++) { 454 | af = ae >> 3; 455 | ac = (ae & 7) * 4; 456 | ap = ab + (af * ay) + ac; 457 | if (ag + af >= aA) { 458 | ap -= (ay * (ag + 1 + af - aA)) 459 | } 460 | if (ah + ac >= ay) { 461 | ap -= ((ah + ac) - ay + 4) 462 | } 463 | am = at[ap++]; 464 | ax = at[ap++]; 465 | az = at[ap++]; 466 | A[ae] = ((C[am] + C[(ax + 256) >> 0] + C[(az + 512) >> 0]) >> 16) - 128; 467 | f[ae] = ((C[(am + 768) >> 0] + C[(ax + 1024) >> 0] + C[(az + 1280) >> 0]) >> 16) - 128; 468 | U[ae] = ((C[(am + 1280) >> 0] + C[(ax + 1536) >> 0] + C[(az + 1792) >> 0]) >> 16) - 128 469 | } 470 | al = L(A, d, al, u, G); 471 | aq = L(f, Z, aq, h, T); 472 | ao = L(U, Z, ao, h, T); 473 | ah += 32 474 | } 475 | ag += 8 476 | } 477 | if (a >= 0) { 478 | var aw = []; 479 | aw[1] = a + 1; 480 | aw[0] = (1 << (a + 1)) - 1; 481 | X(aw) 482 | } 483 | p(65497); 484 | if (aB) { 485 | var av = j.length; 486 | var aC = new Uint8Array(av); 487 | for (var au = 0; au < av; au++) { 488 | aC[au] = j[au].charCodeAt() 489 | } 490 | j = []; 491 | var ak = new Date().getTime() - aa; 492 | return aC 493 | } 494 | var ad = "data:image/jpeg;base64," + btoa(j.join("")); 495 | j = []; 496 | var ak = new Date().getTime() - aa; 497 | return ad 498 | }; 499 | function R (ab) { 500 | if (ab <= 0) { 501 | ab = 1 502 | } 503 | if (ab > 100) { 504 | ab = 100 505 | } 506 | if (x == ab) { 507 | return 508 | } 509 | var aa = 0; 510 | if (ab < 50) { 511 | aa = Math.floor(5000 / ab) 512 | } else { 513 | aa = Math.floor(200 - ab * 2) 514 | } 515 | M(aa); 516 | x = ab; 517 | } 518 | 519 | function Q () { 520 | var aa = new Date().getTime(); 521 | if (!l) { 522 | l = 50 523 | } 524 | y(); 525 | W(); 526 | z(); 527 | V(); 528 | R(l); 529 | var ab = new Date().getTime() - aa; 530 | } 531 | 532 | Q() 533 | } 534 | 535 | module.exports = JPEGEncoder; 536 | 537 | /***/ }) 538 | 539 | }); -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 很抱歉,这个项目已不再维护了,可能很长一段时间都不会更新了。 2 | 如果真的需要,请使用之前请一定留意 [Issues](https://github.com/think2011/localResizeIMG/issues?q=is%3Aissue+is%3Aopen+label%3Abug) 里已知的问题 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/think2011/localResizeIMG.svg?branch=master)](https://travis-ci.org/think2011/localResizeIMG) 2 | [![npm version](https://img.shields.io/npm/v/lrz.svg)](https://www.npmjs.com/package/lrz) 3 | [![npm](https://img.shields.io/npm/l/express.svg)]() 4 | 5 | # 🚨重要!! 6 | 7 | 很抱歉,这个项目已不再维护了,可能很长一段时间都不会更新了。 8 | 9 | # 演示 10 | 11 | ![](http://think2011.github.io/localResizeIMG/test/demo.gif) 12 | 13 | # 试试 14 | 15 | ![](https://raw.github.com/think2011/localResizeIMG/master/test/qrcode.png) 16 | 17 | 18 | [点我直接进入演示页面](http://think2011.github.io/localResizeIMG/test/) 19 | 20 | # 简述 21 | 在客户端压缩好要上传的图片可以节省带宽更快的发送给后端,特别适合在移动设备上使用。 22 | 23 | # 为什么需要 24 | 25 | 1. 已踩过很多坑,经过几个版本迭代,以及很多很多网友的反馈帮助、机型测试 26 | * 图片扭曲、某些设备不自动旋转图片方向,没有jpeg压缩算法.. 27 | * 不支持new Blob,formData构造的文件size为0.. 28 | * 还有某些机型和浏览器(例如QQX5浏览器)莫名其妙的BUG.. 29 | 30 | 2. 按需加载(会根据对应设备自动异步载入JS文件,节省不必要带宽) 31 | 32 | 3. 原生JS编写,不依赖例如`jquery`等第三方库,支持AMD or CMD规范。 33 | 34 | > 尽管如此,在某些 `Android` 下依然有莫名其妙的问题,在您使用前,请一定大致浏览下 [issues](https://github.com/think2011/localResizeIMG/issues) 35 | 36 | # 如何获取 37 | 38 | 通过以下方式都可以下载: 39 | 40 | 1. 执行`npm i lrz`(推荐) 41 | 2. 执行`bower install lrz` 42 | 43 | 接着在页面中引入 44 | ```html 45 | 46 | ``` 47 | 48 | # 如何使用 49 | 50 | ### 方式1: 51 | 52 | 如果您的图片来自用户拍摄或者上传的,您需要一个`input file`来获取图片。 53 | 54 | ```html 55 | 56 | ``` 57 | 58 | 接着通过change事件可以得到用户选择的图片 59 | ```js 60 | document.querySelector('#file').addEventListener('change', function () { 61 | lrz(this.files[0]) 62 | .then(function (rst) { 63 | // 处理成功会执行 64 | console.log(rst); 65 | }) 66 | .catch(function (err) { 67 | // 处理失败会执行 68 | }) 69 | .always(function () { 70 | // 不管是成功失败,都会执行 71 | }); 72 | }); 73 | ``` 74 | 75 | ### 方式2: 76 | 77 | 如果您的图片不是来自用户上传的,那么也可以直接传入图片路径。 78 | 79 | ```js 80 | lrz('./xxx/xx/x.png') 81 | .then(function (rst) { 82 | // 处理成功会执行 83 | }) 84 | .catch(function (err){ 85 | // 处理失败会执行 86 | }) 87 | .always(function () { 88 | // 不管是成功失败,都会执行 89 | }); 90 | ``` 91 | 92 | # 后端处理 93 | 94 | [后端处理请查看WIKI。](https://github.com/think2011/localResizeIMG/wiki) 95 | 96 | 97 | # API 98 | 99 | [具体参数说明请查看WIKI。](https://github.com/think2011/localResizeIMG/wiki) 100 | 101 | # 兼容性 102 | 103 | IE10以上及大部分非IE浏览器(chrome、微信什么的) 104 | 105 | # FAQ 106 | 107 | [有疑问请直接在 issues 中提问](https://github.com/think2011/localResizeIMG/issues) 108 | 109 | ``` 110 | 请一定记得附上以下内容:💡 111 | 请一定记得附上以下内容:🙈 112 | 请一定记得附上以下内容:💡 113 | 114 | 平台:微信.. 115 | 设备:iPhone5 IOS7.. 116 | 问题:问题描述.. 117 | ``` 118 | 119 | * Q:有时拍摄完照片后,页面自动刷新或闪退了。 120 | * A:虽然已作了优化处理,但内存似乎还是爆掉了,常见于低配android手机,建议每次只处理一张图片。 121 | 122 | * Q: 怎么批量上传图片? 123 | * A: 您可以自己写个循环来传入用户多选的图片,但在移动端上请谨慎处理,原因同上。 124 | 125 | * Q: 直接传入图片路径的无法生成图片 126 | * A: 可能是跨域的问题,具体请看[CORS_enabled_image](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image) 127 | 128 | * Q: 想要商用可以吗? 129 | * A: 没问题,但请留意issue里已知的问题。 130 | 131 | # 感谢 132 | 133 | * [dwandw](https://github.com/dwandw) 134 | * [yourlin](https://github.com/yourlin) 135 | * [wxt2005](https://github.com/wxt2005) 136 | * [LingRen](https://github.com/LingRen) 137 | 138 | 以上在之前的版本帮忙参与维护的朋友,以及提出问题的朋友们,真的真的很感谢你们。:D 139 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lrz", 3 | "version": "4.9.37", 4 | "homepage": "https://github.com/think2011/localResizeIMG", 5 | "authors": [ 6 | "think2011 <452125301@qq.com>" 7 | ], 8 | "description": "前端本地客户端压缩图片,兼容IOS,Android,PC、自动按需加载文件", 9 | "main": "dist/lrz.bundle.js", 10 | "keywords": [ 11 | "lrz", 12 | "localResizeIMG", 13 | "前端压缩图片", 14 | "前端图片压缩", 15 | "压缩图片" 16 | ], 17 | "license": "MIT", 18 | "ignore": [ 19 | "**/.*", 20 | "node_modules", 21 | "bower_components", 22 | "test", 23 | "tests", 24 | "src", 25 | "gulpfile.js", 26 | "karma.conf.js", 27 | "webpack.config.js" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /dist/1.chunk.js: -------------------------------------------------------------------------------- 1 | webpackJsonp([1],{ 2 | 3 | /***/ 7: 4 | /***/ (function(module, exports, __webpack_require__) { 5 | 6 | var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/** 7 | * Mega pixel image rendering library for iOS6 Safari 8 | * 9 | * Fixes iOS6 Safari's image file rendering issue for large size image (over mega-pixel), 10 | * which causes unexpected subsampling when drawing it in canvas. 11 | * By using this library, you can safely render the image with proper stretching. 12 | * 13 | * Copyright (c) 2012 Shinichi Tomita 14 | * Released under the MIT license 15 | */ 16 | (function () { 17 | 18 | /** 19 | * Detect subsampling in loaded image. 20 | * In iOS, larger images than 2M pixels may be subsampled in rendering. 21 | */ 22 | function detectSubsampling (img) { 23 | var iw = img.naturalWidth, ih = img.naturalHeight; 24 | if (iw * ih > 1024 * 1024) { // subsampling may happen over megapixel image 25 | var canvas = document.createElement('canvas'); 26 | canvas.width = canvas.height = 1; 27 | var ctx = canvas.getContext('2d'); 28 | ctx.drawImage(img, -iw + 1, 0); 29 | // subsampled image becomes half smaller in rendering size. 30 | // check alpha channel value to confirm image is covering edge pixel or not. 31 | // if alpha value is 0 image is not covering, hence subsampled. 32 | return ctx.getImageData(0, 0, 1, 1).data[3] === 0; 33 | } else { 34 | return false; 35 | } 36 | } 37 | 38 | /** 39 | * Detecting vertical squash in loaded image. 40 | * Fixes a bug which squash image vertically while drawing into canvas for some images. 41 | */ 42 | function detectVerticalSquash (img, iw, ih) { 43 | var canvas = document.createElement('canvas'); 44 | canvas.width = 1; 45 | canvas.height = ih; 46 | var ctx = canvas.getContext('2d'); 47 | ctx.drawImage(img, 0, 0); 48 | var data = ctx.getImageData(0, 0, 1, ih).data; 49 | // search image edge pixel position in case it is squashed vertically. 50 | var sy = 0; 51 | var ey = ih; 52 | var py = ih; 53 | while (py > sy) { 54 | var alpha = data[(py - 1) * 4 + 3]; 55 | if (alpha === 0) { 56 | ey = py; 57 | } else { 58 | sy = py; 59 | } 60 | py = (ey + sy) >> 1; 61 | } 62 | var ratio = (py / ih); 63 | return (ratio === 0) ? 1 : ratio; 64 | } 65 | 66 | /** 67 | * Rendering image element (with resizing) and get its data URL 68 | */ 69 | function renderImageToDataURL (img, options, doSquash) { 70 | var canvas = document.createElement('canvas'); 71 | renderImageToCanvas(img, canvas, options, doSquash); 72 | return canvas.toDataURL("image/jpeg", options.quality || 0.8); 73 | } 74 | 75 | /** 76 | * Rendering image element (with resizing) into the canvas element 77 | */ 78 | function renderImageToCanvas (img, canvas, options, doSquash) { 79 | var iw = img.naturalWidth, ih = img.naturalHeight; 80 | var width = options.width, height = options.height; 81 | var ctx = canvas.getContext('2d'); 82 | ctx.save(); 83 | transformCoordinate(canvas, ctx, width, height, options.orientation); 84 | var subsampled = detectSubsampling(img); 85 | if (subsampled) { 86 | iw /= 2; 87 | ih /= 2; 88 | } 89 | var d = 1024; // size of tiling canvas 90 | var tmpCanvas = document.createElement('canvas'); 91 | tmpCanvas.width = tmpCanvas.height = d; 92 | var tmpCtx = tmpCanvas.getContext('2d'); 93 | var vertSquashRatio = doSquash ? detectVerticalSquash(img, iw, ih) : 1; 94 | var dw = Math.ceil(d * width / iw); 95 | var dh = Math.ceil(d * height / ih / vertSquashRatio); 96 | var sy = 0; 97 | var dy = 0; 98 | while (sy < ih) { 99 | var sx = 0; 100 | var dx = 0; 101 | while (sx < iw) { 102 | tmpCtx.clearRect(0, 0, d, d); 103 | tmpCtx.drawImage(img, -sx, -sy); 104 | ctx.drawImage(tmpCanvas, 0, 0, d, d, dx, dy, dw, dh); 105 | sx += d; 106 | dx += dw; 107 | } 108 | sy += d; 109 | dy += dh; 110 | } 111 | ctx.restore(); 112 | tmpCanvas = tmpCtx = null; 113 | } 114 | 115 | /** 116 | * Transform canvas coordination according to specified frame size and orientation 117 | * Orientation value is from EXIF tag 118 | */ 119 | function transformCoordinate (canvas, ctx, width, height, orientation) { 120 | switch (orientation) { 121 | case 5: 122 | case 6: 123 | case 7: 124 | case 8: 125 | canvas.width = height; 126 | canvas.height = width; 127 | break; 128 | default: 129 | canvas.width = width; 130 | canvas.height = height; 131 | } 132 | switch (orientation) { 133 | case 2: 134 | // horizontal flip 135 | ctx.translate(width, 0); 136 | ctx.scale(-1, 1); 137 | break; 138 | case 3: 139 | // 180 rotate left 140 | ctx.translate(width, height); 141 | ctx.rotate(Math.PI); 142 | break; 143 | case 4: 144 | // vertical flip 145 | ctx.translate(0, height); 146 | ctx.scale(1, -1); 147 | break; 148 | case 5: 149 | // vertical flip + 90 rotate right 150 | ctx.rotate(0.5 * Math.PI); 151 | ctx.scale(1, -1); 152 | break; 153 | case 6: 154 | // 90 rotate right 155 | ctx.rotate(0.5 * Math.PI); 156 | ctx.translate(0, -height); 157 | break; 158 | case 7: 159 | // horizontal flip + 90 rotate right 160 | ctx.rotate(0.5 * Math.PI); 161 | ctx.translate(width, -height); 162 | ctx.scale(-1, 1); 163 | break; 164 | case 8: 165 | // 90 rotate left 166 | ctx.rotate(-0.5 * Math.PI); 167 | ctx.translate(-width, 0); 168 | break; 169 | default: 170 | break; 171 | } 172 | } 173 | 174 | 175 | /** 176 | * MegaPixImage class 177 | */ 178 | function MegaPixImage (srcImage) { 179 | if (window.Blob && srcImage instanceof Blob) { 180 | var img = new Image(); 181 | var URL = window.URL && window.URL.createObjectURL ? window.URL : 182 | window.webkitURL && window.webkitURL.createObjectURL ? window.webkitURL : 183 | null; 184 | if (!URL) { 185 | throw Error("No createObjectURL function found to create blob url"); 186 | } 187 | img.src = URL.createObjectURL(srcImage); 188 | this.blob = srcImage; 189 | srcImage = img; 190 | } 191 | if (!srcImage.naturalWidth && !srcImage.naturalHeight) { 192 | var _this = this; 193 | srcImage.onload = function () { 194 | var listeners = _this.imageLoadListeners; 195 | if (listeners) { 196 | _this.imageLoadListeners = null; 197 | for (var i = 0, len = listeners.length; i < len; i++) { 198 | listeners[i](); 199 | } 200 | } 201 | }; 202 | this.imageLoadListeners = []; 203 | } 204 | this.srcImage = srcImage; 205 | } 206 | 207 | /** 208 | * Rendering megapix image into specified target element 209 | */ 210 | MegaPixImage.prototype.render = function (target, options, callback) { 211 | if (this.imageLoadListeners) { 212 | var _this = this; 213 | this.imageLoadListeners.push(function () { 214 | _this.render(target, options, callback); 215 | }); 216 | return; 217 | } 218 | options = options || {}; 219 | var srcImage = this.srcImage, 220 | src = srcImage.src, 221 | srcLength = src.length, 222 | imgWidth = srcImage.naturalWidth, imgHeight = srcImage.naturalHeight, 223 | width = options.width, height = options.height, 224 | maxWidth = options.maxWidth, maxHeight = options.maxHeight, 225 | doSquash = this.blob && this.blob.type === 'image/jpeg' || 226 | src.indexOf('data:image/jpeg') === 0 || 227 | src.indexOf('.jpg') === srcLength - 4 || 228 | src.indexOf('.jpeg') === srcLength - 5; 229 | if (width && !height) { 230 | height = (imgHeight * width / imgWidth) << 0; 231 | } else if (height && !width) { 232 | width = (imgWidth * height / imgHeight) << 0; 233 | } else { 234 | width = imgWidth; 235 | height = imgHeight; 236 | } 237 | if (maxWidth && width > maxWidth) { 238 | width = maxWidth; 239 | height = (imgHeight * width / imgWidth) << 0; 240 | } 241 | if (maxHeight && height > maxHeight) { 242 | height = maxHeight; 243 | width = (imgWidth * height / imgHeight) << 0; 244 | } 245 | var opt = {width: width, height: height}; 246 | for (var k in options) opt[k] = options[k]; 247 | 248 | var tagName = target.tagName.toLowerCase(); 249 | if (tagName === 'img') { 250 | target.src = renderImageToDataURL(this.srcImage, opt, doSquash); 251 | } else if (tagName === 'canvas') { 252 | renderImageToCanvas(this.srcImage, target, opt, doSquash); 253 | } 254 | if (typeof this.onrender === 'function') { 255 | this.onrender(target); 256 | } 257 | if (callback) { 258 | callback(); 259 | } 260 | }; 261 | 262 | /** 263 | * Export class to global 264 | */ 265 | if (true) { 266 | !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function () { 267 | return MegaPixImage; 268 | }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // for AMD loader 269 | } else { 270 | this.MegaPixImage = MegaPixImage; 271 | } 272 | 273 | })(); 274 | 275 | 276 | /***/ }) 277 | 278 | }); -------------------------------------------------------------------------------- /dist/1.chunk.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack:///1.chunk.js","webpack:///src/lib/megapix-image.js"],"names":["webpackJsonp",6,"module","exports","__webpack_require__","__WEBPACK_AMD_DEFINE_ARRAY__","__WEBPACK_AMD_DEFINE_RESULT__","detectSubsampling","img","iw","naturalWidth","ih","naturalHeight","canvas","document","createElement","width","height","ctx","getContext","drawImage","getImageData","data","detectVerticalSquash","sy","ey","py","alpha","ratio","renderImageToDataURL","options","doSquash","renderImageToCanvas","toDataURL","quality","save","transformCoordinate","orientation","subsampled","d","tmpCanvas","tmpCtx","vertSquashRatio","dw","Math","ceil","dh","dy","sx","dx","clearRect","restore","translate","scale","rotate","PI","MegaPixImage","srcImage","window","Blob","Image","URL","createObjectURL","webkitURL","Error","src","this","blob","_this","onload","listeners","imageLoadListeners","i","len","length","prototype","render","target","callback","push","srcLength","imgWidth","imgHeight","maxWidth","maxHeight","type","indexOf","opt","k","tagName","toLowerCase","onrender","apply","undefined"],"mappings":"AAAAA,cAAc,IAERC,EACA,SAASC,EAAQC,EAASC,GCHhC,GAAAC,GAAAC,GAUA,WAMA,QAAAC,GAAAC,GACA,GAAAC,GAAAD,EAAAE,aAAAC,EAAAH,EAAAI,aACA,IAAAH,EAAAE,EAAA,SACA,GAAAE,GAAAC,SAAAC,cAAA,SACAF,GAAAG,MAAAH,EAAAI,OAAA,CACA,IAAAC,GAAAL,EAAAM,WAAA,KAKA,OAJAD,GAAAE,UAAAZ,GAAAC,EAAA,KAIA,IAAAS,EAAAG,aAAA,SAAAC,KAAA,GAEA,SAQA,QAAAC,GAAAf,EAAAC,EAAAE,GACA,GAAAE,GAAAC,SAAAC,cAAA,SACAF,GAAAG,MAAA,EACAH,EAAAI,OAAAN,CACA,IAAAO,GAAAL,EAAAM,WAAA,KACAD,GAAAE,UAAAZ,EAAA,IAMA,KALA,GAAAc,GAAAJ,EAAAG,aAAA,MAAAV,GAAAW,KAEAE,EAAA,EACAC,EAAAd,EACAe,EAAAf,EACAe,EAAAF,GAAA,CACA,GAAAG,GAAAL,EAAA,GAAAI,EAAA,KACA,KAAAC,EACAF,EAAAC,EAEAF,EAAAE,EAEAA,EAAAD,EAAAD,GAAA,EAEA,GAAAI,GAAAF,EAAAf,CACA,YAAAiB,EAAA,EAAAA,EAMA,QAAAC,GAAArB,EAAAsB,EAAAC,GACA,GAAAlB,GAAAC,SAAAC,cAAA,SAEA,OADAiB,GAAAxB,EAAAK,EAAAiB,EAAAC,GACAlB,EAAAoB,UAAA,aAAAH,EAAAI,SAAA,IAMA,QAAAF,GAAAxB,EAAAK,EAAAiB,EAAAC,GACA,GAAAtB,GAAAD,EAAAE,aAAAC,EAAAH,EAAAI,cACAI,EAAAc,EAAAd,MAAAC,EAAAa,EAAAb,OACAC,EAAAL,EAAAM,WAAA,KACAD,GAAAiB,OACAC,EAAAvB,EAAAK,EAAAF,EAAAC,EAAAa,EAAAO,YACA,IAAAC,GAAA/B,EAAAC,EACA8B,KACA7B,GAAA,EACAE,GAAA,EAEA,IAAA4B,GAAA,KACAC,EAAA1B,SAAAC,cAAA,SACAyB,GAAAxB,MAAAwB,EAAAvB,OAAAsB,CAOA,KANA,GAAAE,GAAAD,EAAArB,WAAA,MACAuB,EAAAX,EAAAR,EAAAf,EAAAC,EAAAE,GAAA,EACAgC,EAAAC,KAAAC,KAAAN,EAAAvB,EAAAP,GACAqC,EAAAF,KAAAC,KAAAN,EAAAtB,EAAAN,EAAA+B,GACAlB,EAAA,EACAuB,EAAA,EACApC,EAAAa,GAAA,CAGA,IAFA,GAAAwB,GAAA,EACAC,EAAA,EACAxC,EAAAuC,GACAP,EAAAS,UAAA,IAAAX,KACAE,EAAArB,UAAAZ,GAAAwC,GAAAxB,GACAN,EAAAE,UAAAoB,EAAA,IAAAD,IAAAU,EAAAF,EAAAJ,EAAAG,GACAE,GAAAT,EACAU,GAAAN,CAEAnB,IAAAe,EACAQ,GAAAD,EAEA5B,EAAAiC,UACAX,EAAAC,EAAA,KAOA,QAAAL,GAAAvB,EAAAK,EAAAF,EAAAC,EAAAoB,GACA,OAAAA,GACA,OACA,OACA,OACA,OACAxB,EAAAG,MAAAC,EACAJ,EAAAI,OAAAD,CACA,MACA,SACAH,EAAAG,QACAH,EAAAI,SAEA,OAAAoB,GACA,OAEAnB,EAAAkC,UAAApC,EAAA,GACAE,EAAAmC,MAAA,KACA,MACA,QAEAnC,EAAAkC,UAAApC,EAAAC,GACAC,EAAAoC,OAAAV,KAAAW,GACA,MACA,QAEArC,EAAAkC,UAAA,EAAAnC,GACAC,EAAAmC,MAAA,KACA,MACA,QAEAnC,EAAAoC,OAAA,GAAAV,KAAAW,IACArC,EAAAmC,MAAA,KACA,MACA,QAEAnC,EAAAoC,OAAA,GAAAV,KAAAW,IACArC,EAAAkC,UAAA,GAAAnC,EACA,MACA,QAEAC,EAAAoC,OAAA,GAAAV,KAAAW,IACArC,EAAAkC,UAAApC,GAAAC,GACAC,EAAAmC,MAAA,KACA,MACA,QAEAnC,EAAAoC,QAAA,GAAAV,KAAAW,IACArC,EAAAkC,WAAApC,EAAA,IAWA,QAAAwC,GAAAC,GACA,GAAAC,OAAAC,MAAAF,YAAAE,MAAA,CACA,GAAAnD,GAAA,GAAAoD,OACAC,EAAAH,OAAAG,KAAAH,OAAAG,IAAAC,gBAAAJ,OAAAG,IACAH,OAAAK,WAAAL,OAAAK,UAAAD,gBAAAJ,OAAAK,UACA,IACA,KAAAF,EACA,KAAAG,OAAA,uDAEAxD,GAAAyD,IAAAJ,EAAAC,gBAAAL,GACAS,KAAAC,KAAAV,EACAA,EAAAjD,EAEA,IAAAiD,EAAA/C,eAAA+C,EAAA7C,cAAA,CACA,GAAAwD,GAAAF,IACAT,GAAAY,OAAA,WACA,GAAAC,GAAAF,EAAAG,kBACA,IAAAD,EAAA,CACAF,EAAAG,mBAAA,IACA,QAAAC,GAAA,EAAAC,EAAAH,EAAAI,OAA2DD,EAAAD,EAASA,IACpEF,EAAAE,OAIAN,KAAAK,sBAEAL,KAAAT,WAMAD,EAAAmB,UAAAC,OAAA,SAAAC,EAAA/C,EAAAgD,GACA,GAAAZ,KAAAK,mBAAA,CACA,GAAAH,GAAAF,IAIA,YAHAA,MAAAK,mBAAAQ,KAAA,WACAX,EAAAQ,OAAAC,EAAA/C,EAAAgD,KAIAhD,OACA,IAAA2B,GAAAS,KAAAT,SACAQ,EAAAR,EAAAQ,IACAe,EAAAf,EAAAS,OACAO,EAAAxB,EAAA/C,aAAAwE,EAAAzB,EAAA7C,cACAI,EAAAc,EAAAd,MAAAC,EAAAa,EAAAb,OACAkE,EAAArD,EAAAqD,SAAAC,EAAAtD,EAAAsD,UACArD,EAAAmC,KAAAC,MAAA,eAAAD,KAAAC,KAAAkB,MACA,IAAApB,EAAAqB,QAAA,oBACArB,EAAAqB,QAAA,UAAAN,EAAA,GACAf,EAAAqB,QAAA,WAAAN,EAAA,CACAhE,KAAAC,EACAA,EAAAiE,EAAAlE,EAAAiE,GAAA,EACShE,IAAAD,EACTA,EAAAiE,EAAAhE,EAAAiE,GAAA,GAEAlE,EAAAiE,EACAhE,EAAAiE,GAEAC,GAAAnE,EAAAmE,IACAnE,EAAAmE,EACAlE,EAAAiE,EAAAlE,EAAAiE,GAAA,GAEAG,GAAAnE,EAAAmE,IACAnE,EAAAmE,EACApE,EAAAiE,EAAAhE,EAAAiE,GAAA,EAEA,IAAAK,IAAmBvE,QAAAC,SACnB,QAAAuE,KAAA1D,GAAAyD,EAAAC,GAAA1D,EAAA0D,EAEA,IAAAC,GAAAZ,EAAAY,QAAAC,aACA,SAAAD,EACAZ,EAAAZ,IAAApC,EAAAqC,KAAAT,SAAA8B,EAAAxD,GACS,WAAA0D,GACTzD,EAAAkC,KAAAT,SAAAoB,EAAAU,EAAAxD,GAEA,kBAAAmC,MAAAyB,UACAzB,KAAAyB,SAAAd,GAEAC,GACAA,KAQAzE,KAAAC,EAAA,WACA,MAAAkD,IACSoC,MAAAzF,EAAAE,KAAAwF,SAAAvF,IAAAJ,EAAAC,QAAAG","file":"1.chunk.js","sourcesContent":["webpackJsonp([1],{\n\n/***/ 6:\n/***/ function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/**\n\t * Mega pixel image rendering library for iOS6 Safari\n\t *\n\t * Fixes iOS6 Safari's image file rendering issue for large size image (over mega-pixel),\n\t * which causes unexpected subsampling when drawing it in canvas.\n\t * By using this library, you can safely render the image with proper stretching.\n\t *\n\t * Copyright (c) 2012 Shinichi Tomita \n\t * Released under the MIT license\n\t */\n\t(function () {\n\t\n\t /**\n\t * Detect subsampling in loaded image.\n\t * In iOS, larger images than 2M pixels may be subsampled in rendering.\n\t */\n\t function detectSubsampling (img) {\n\t var iw = img.naturalWidth, ih = img.naturalHeight;\n\t if (iw * ih > 1024 * 1024) { // subsampling may happen over megapixel image\n\t var canvas = document.createElement('canvas');\n\t canvas.width = canvas.height = 1;\n\t var ctx = canvas.getContext('2d');\n\t ctx.drawImage(img, -iw + 1, 0);\n\t // subsampled image becomes half smaller in rendering size.\n\t // check alpha channel value to confirm image is covering edge pixel or not.\n\t // if alpha value is 0 image is not covering, hence subsampled.\n\t return ctx.getImageData(0, 0, 1, 1).data[3] === 0;\n\t } else {\n\t return false;\n\t }\n\t }\n\t\n\t /**\n\t * Detecting vertical squash in loaded image.\n\t * Fixes a bug which squash image vertically while drawing into canvas for some images.\n\t */\n\t function detectVerticalSquash (img, iw, ih) {\n\t var canvas = document.createElement('canvas');\n\t canvas.width = 1;\n\t canvas.height = ih;\n\t var ctx = canvas.getContext('2d');\n\t ctx.drawImage(img, 0, 0);\n\t var data = ctx.getImageData(0, 0, 1, ih).data;\n\t // search image edge pixel position in case it is squashed vertically.\n\t var sy = 0;\n\t var ey = ih;\n\t var py = ih;\n\t while (py > sy) {\n\t var alpha = data[(py - 1) * 4 + 3];\n\t if (alpha === 0) {\n\t ey = py;\n\t } else {\n\t sy = py;\n\t }\n\t py = (ey + sy) >> 1;\n\t }\n\t var ratio = (py / ih);\n\t return (ratio === 0) ? 1 : ratio;\n\t }\n\t\n\t /**\n\t * Rendering image element (with resizing) and get its data URL\n\t */\n\t function renderImageToDataURL (img, options, doSquash) {\n\t var canvas = document.createElement('canvas');\n\t renderImageToCanvas(img, canvas, options, doSquash);\n\t return canvas.toDataURL(\"image/jpeg\", options.quality || 0.8);\n\t }\n\t\n\t /**\n\t * Rendering image element (with resizing) into the canvas element\n\t */\n\t function renderImageToCanvas (img, canvas, options, doSquash) {\n\t var iw = img.naturalWidth, ih = img.naturalHeight;\n\t var width = options.width, height = options.height;\n\t var ctx = canvas.getContext('2d');\n\t ctx.save();\n\t transformCoordinate(canvas, ctx, width, height, options.orientation);\n\t var subsampled = detectSubsampling(img);\n\t if (subsampled) {\n\t iw /= 2;\n\t ih /= 2;\n\t }\n\t var d = 1024; // size of tiling canvas\n\t var tmpCanvas = document.createElement('canvas');\n\t tmpCanvas.width = tmpCanvas.height = d;\n\t var tmpCtx = tmpCanvas.getContext('2d');\n\t var vertSquashRatio = doSquash ? detectVerticalSquash(img, iw, ih) : 1;\n\t var dw = Math.ceil(d * width / iw);\n\t var dh = Math.ceil(d * height / ih / vertSquashRatio);\n\t var sy = 0;\n\t var dy = 0;\n\t while (sy < ih) {\n\t var sx = 0;\n\t var dx = 0;\n\t while (sx < iw) {\n\t tmpCtx.clearRect(0, 0, d, d);\n\t tmpCtx.drawImage(img, -sx, -sy);\n\t ctx.drawImage(tmpCanvas, 0, 0, d, d, dx, dy, dw, dh);\n\t sx += d;\n\t dx += dw;\n\t }\n\t sy += d;\n\t dy += dh;\n\t }\n\t ctx.restore();\n\t tmpCanvas = tmpCtx = null;\n\t }\n\t\n\t /**\n\t * Transform canvas coordination according to specified frame size and orientation\n\t * Orientation value is from EXIF tag\n\t */\n\t function transformCoordinate (canvas, ctx, width, height, orientation) {\n\t switch (orientation) {\n\t case 5:\n\t case 6:\n\t case 7:\n\t case 8:\n\t canvas.width = height;\n\t canvas.height = width;\n\t break;\n\t default:\n\t canvas.width = width;\n\t canvas.height = height;\n\t }\n\t switch (orientation) {\n\t case 2:\n\t // horizontal flip\n\t ctx.translate(width, 0);\n\t ctx.scale(-1, 1);\n\t break;\n\t case 3:\n\t // 180 rotate left\n\t ctx.translate(width, height);\n\t ctx.rotate(Math.PI);\n\t break;\n\t case 4:\n\t // vertical flip\n\t ctx.translate(0, height);\n\t ctx.scale(1, -1);\n\t break;\n\t case 5:\n\t // vertical flip + 90 rotate right\n\t ctx.rotate(0.5 * Math.PI);\n\t ctx.scale(1, -1);\n\t break;\n\t case 6:\n\t // 90 rotate right\n\t ctx.rotate(0.5 * Math.PI);\n\t ctx.translate(0, -height);\n\t break;\n\t case 7:\n\t // horizontal flip + 90 rotate right\n\t ctx.rotate(0.5 * Math.PI);\n\t ctx.translate(width, -height);\n\t ctx.scale(-1, 1);\n\t break;\n\t case 8:\n\t // 90 rotate left\n\t ctx.rotate(-0.5 * Math.PI);\n\t ctx.translate(-width, 0);\n\t break;\n\t default:\n\t break;\n\t }\n\t }\n\t\n\t\n\t /**\n\t * MegaPixImage class\n\t */\n\t function MegaPixImage (srcImage) {\n\t if (window.Blob && srcImage instanceof Blob) {\n\t var img = new Image();\n\t var URL = window.URL && window.URL.createObjectURL ? window.URL :\n\t window.webkitURL && window.webkitURL.createObjectURL ? window.webkitURL :\n\t null;\n\t if (!URL) {\n\t throw Error(\"No createObjectURL function found to create blob url\");\n\t }\n\t img.src = URL.createObjectURL(srcImage);\n\t this.blob = srcImage;\n\t srcImage = img;\n\t }\n\t if (!srcImage.naturalWidth && !srcImage.naturalHeight) {\n\t var _this = this;\n\t srcImage.onload = function () {\n\t var listeners = _this.imageLoadListeners;\n\t if (listeners) {\n\t _this.imageLoadListeners = null;\n\t for (var i = 0, len = listeners.length; i < len; i++) {\n\t listeners[i]();\n\t }\n\t }\n\t };\n\t this.imageLoadListeners = [];\n\t }\n\t this.srcImage = srcImage;\n\t }\n\t\n\t /**\n\t * Rendering megapix image into specified target element\n\t */\n\t MegaPixImage.prototype.render = function (target, options, callback) {\n\t if (this.imageLoadListeners) {\n\t var _this = this;\n\t this.imageLoadListeners.push(function () {\n\t _this.render(target, options, callback);\n\t });\n\t return;\n\t }\n\t options = options || {};\n\t var srcImage = this.srcImage,\n\t src = srcImage.src,\n\t srcLength = src.length,\n\t imgWidth = srcImage.naturalWidth, imgHeight = srcImage.naturalHeight,\n\t width = options.width, height = options.height,\n\t maxWidth = options.maxWidth, maxHeight = options.maxHeight,\n\t doSquash = this.blob && this.blob.type === 'image/jpeg' ||\n\t src.indexOf('data:image/jpeg') === 0 ||\n\t src.indexOf('.jpg') === srcLength - 4 ||\n\t src.indexOf('.jpeg') === srcLength - 5;\n\t if (width && !height) {\n\t height = (imgHeight * width / imgWidth) << 0;\n\t } else if (height && !width) {\n\t width = (imgWidth * height / imgHeight) << 0;\n\t } else {\n\t width = imgWidth;\n\t height = imgHeight;\n\t }\n\t if (maxWidth && width > maxWidth) {\n\t width = maxWidth;\n\t height = (imgHeight * width / imgWidth) << 0;\n\t }\n\t if (maxHeight && height > maxHeight) {\n\t height = maxHeight;\n\t width = (imgWidth * height / imgHeight) << 0;\n\t }\n\t var opt = {width: width, height: height};\n\t for (var k in options) opt[k] = options[k];\n\t\n\t var tagName = target.tagName.toLowerCase();\n\t if (tagName === 'img') {\n\t target.src = renderImageToDataURL(this.srcImage, opt, doSquash);\n\t } else if (tagName === 'canvas') {\n\t renderImageToCanvas(this.srcImage, target, opt, doSquash);\n\t }\n\t if (typeof this.onrender === 'function') {\n\t this.onrender(target);\n\t }\n\t if (callback) {\n\t callback();\n\t }\n\t };\n\t\n\t /**\n\t * Export class to global\n\t */\n\t if (true) {\n\t !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function () {\n\t return MegaPixImage;\n\t }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); // for AMD loader\n\t } else {\n\t this.MegaPixImage = MegaPixImage;\n\t }\n\t\n\t})();\n\n\n/***/ }\n\n});\n\n\n/** WEBPACK FOOTER **\n ** 1.chunk.js\n **/","null\n\n\n/** WEBPACK FOOTER **\n ** src/lib/megapix-image.js\n **/"],"sourceRoot":""} -------------------------------------------------------------------------------- /dist/2.chunk.js: -------------------------------------------------------------------------------- 1 | webpackJsonp([2],{ 2 | 3 | /***/ 8: 4 | /***/ (function(module, exports) { 5 | 6 | function JPEGEncoder (l) { 7 | var o = this; 8 | var s = Math.round; 9 | var k = Math.floor; 10 | var O = new Array(64); 11 | var K = new Array(64); 12 | var d = new Array(64); 13 | var Z = new Array(64); 14 | var u; 15 | var h; 16 | var G; 17 | var T; 18 | var n = new Array(65535); 19 | var m = new Array(65535); 20 | var P = new Array(64); 21 | var S = new Array(64); 22 | var j = []; 23 | var t = 0; 24 | var a = 7; 25 | var A = new Array(64); 26 | var f = new Array(64); 27 | var U = new Array(64); 28 | var e = new Array(256); 29 | var C = new Array(2048); 30 | var x; 31 | var i = [0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18, 24, 31, 40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63]; 32 | var g = [0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0]; 33 | var c = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; 34 | var w = [0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125]; 35 | var E = [1, 2, 3, 0, 4, 17, 5, 18, 33, 49, 65, 6, 19, 81, 97, 7, 34, 113, 20, 50, 129, 145, 161, 8, 35, 66, 177, 193, 21, 82, 209, 240, 36, 51, 98, 114, 130, 9, 10, 22, 23, 24, 25, 26, 37, 38, 39, 40, 41, 42, 52, 53, 54, 55, 56, 57, 58, 67, 68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87, 88, 89, 90, 99, 100, 101, 102, 103, 104, 105, 106, 115, 116, 117, 118, 119, 120, 121, 122, 131, 132, 133, 134, 135, 136, 137, 138, 146, 147, 148, 149, 150, 151, 152, 153, 154, 162, 163, 164, 165, 166, 167, 168, 169, 170, 178, 179, 180, 181, 182, 183, 184, 185, 186, 194, 195, 196, 197, 198, 199, 200, 201, 202, 210, 211, 212, 213, 214, 215, 216, 217, 218, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250]; 36 | var v = [0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0]; 37 | var Y = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; 38 | var J = [0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119]; 39 | var B = [0, 1, 2, 3, 17, 4, 5, 33, 49, 6, 18, 65, 81, 7, 97, 113, 19, 34, 50, 129, 8, 20, 66, 145, 161, 177, 193, 9, 35, 51, 82, 240, 21, 98, 114, 209, 10, 22, 36, 52, 225, 37, 241, 23, 24, 25, 26, 38, 39, 40, 41, 42, 53, 54, 55, 56, 57, 58, 67, 68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87, 88, 89, 90, 99, 100, 101, 102, 103, 104, 105, 106, 115, 116, 117, 118, 119, 120, 121, 122, 130, 131, 132, 133, 134, 135, 136, 137, 138, 146, 147, 148, 149, 150, 151, 152, 153, 154, 162, 163, 164, 165, 166, 167, 168, 169, 170, 178, 179, 180, 181, 182, 183, 184, 185, 186, 194, 195, 196, 197, 198, 199, 200, 201, 202, 210, 211, 212, 213, 214, 215, 216, 217, 218, 226, 227, 228, 229, 230, 231, 232, 233, 234, 242, 243, 244, 245, 246, 247, 248, 249, 250]; 40 | 41 | function M (ag) { 42 | var af = [16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92, 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99]; 43 | for (var ae = 0; ae < 64; ae++) { 44 | var aj = k((af[ae] * ag + 50) / 100); 45 | if (aj < 1) { 46 | aj = 1 47 | } else { 48 | if (aj > 255) { 49 | aj = 255 50 | } 51 | } 52 | O[i[ae]] = aj 53 | } 54 | var ah = [17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, 24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99]; 55 | for (var ad = 0; ad < 64; ad++) { 56 | var ai = k((ah[ad] * ag + 50) / 100); 57 | if (ai < 1) { 58 | ai = 1 59 | } else { 60 | if (ai > 255) { 61 | ai = 255 62 | } 63 | } 64 | K[i[ad]] = ai 65 | } 66 | var ac = [1, 1.387039845, 1.306562965, 1.175875602, 1, 0.785694958, 0.5411961, 0.275899379]; 67 | var ab = 0; 68 | for (var ak = 0; ak < 8; ak++) { 69 | for (var aa = 0; aa < 8; aa++) { 70 | d[ab] = (1 / (O[i[ab]] * ac[ak] * ac[aa] * 8)); 71 | Z[ab] = (1 / (K[i[ab]] * ac[ak] * ac[aa] * 8)); 72 | ab++ 73 | } 74 | } 75 | } 76 | 77 | function q (ae, aa) { 78 | var ad = 0; 79 | var ag = 0; 80 | var af = new Array(); 81 | for (var ab = 1; ab <= 16; ab++) { 82 | for (var ac = 1; ac <= ae[ab]; ac++) { 83 | af[aa[ag]] = []; 84 | af[aa[ag]][0] = ad; 85 | af[aa[ag]][1] = ab; 86 | ag++; 87 | ad++ 88 | } 89 | ad *= 2 90 | } 91 | return af 92 | } 93 | 94 | function W () { 95 | u = q(g, c); 96 | h = q(v, Y); 97 | G = q(w, E); 98 | T = q(J, B) 99 | } 100 | 101 | function z () { 102 | var ac = 1; 103 | var ab = 2; 104 | for (var aa = 1; aa <= 15; aa++) { 105 | for (var ad = ac; ad < ab; ad++) { 106 | m[32767 + ad] = aa; 107 | n[32767 + ad] = []; 108 | n[32767 + ad][1] = aa; 109 | n[32767 + ad][0] = ad 110 | } 111 | for (var ae = -(ab - 1); ae <= -ac; ae++) { 112 | m[32767 + ae] = aa; 113 | n[32767 + ae] = []; 114 | n[32767 + ae][1] = aa; 115 | n[32767 + ae][0] = ab - 1 + ae 116 | } 117 | ac <<= 1; 118 | ab <<= 1 119 | } 120 | } 121 | 122 | function V () { 123 | for (var aa = 0; aa < 256; aa++) { 124 | C[aa] = 19595 * aa; 125 | C[(aa + 256) >> 0] = 38470 * aa; 126 | C[(aa + 512) >> 0] = 7471 * aa + 32768; 127 | C[(aa + 768) >> 0] = -11059 * aa; 128 | C[(aa + 1024) >> 0] = -21709 * aa; 129 | C[(aa + 1280) >> 0] = 32768 * aa + 8421375; 130 | C[(aa + 1536) >> 0] = -27439 * aa; 131 | C[(aa + 1792) >> 0] = -5329 * aa 132 | } 133 | } 134 | 135 | function X (aa) { 136 | var ac = aa[0]; 137 | var ab = aa[1] - 1; 138 | while (ab >= 0) { 139 | if (ac & (1 << ab)) { 140 | t |= (1 << a) 141 | } 142 | ab--; 143 | a--; 144 | if (a < 0) { 145 | if (t == 255) { 146 | F(255); 147 | F(0) 148 | } else { 149 | F(t) 150 | } 151 | a = 7; 152 | t = 0 153 | } 154 | } 155 | } 156 | 157 | function F (aa) { 158 | j.push(e[aa]) 159 | } 160 | 161 | function p (aa) { 162 | F((aa >> 8) & 255); 163 | F((aa) & 255) 164 | } 165 | 166 | function N (aZ, ap) { 167 | var aL, aK, aJ, aI, aH, aD, aC, aB; 168 | var aN = 0; 169 | var aR; 170 | const aq = 8; 171 | const ai = 64; 172 | for (aR = 0; aR < aq; ++aR) { 173 | aL = aZ[aN]; 174 | aK = aZ[aN + 1]; 175 | aJ = aZ[aN + 2]; 176 | aI = aZ[aN + 3]; 177 | aH = aZ[aN + 4]; 178 | aD = aZ[aN + 5]; 179 | aC = aZ[aN + 6]; 180 | aB = aZ[aN + 7]; 181 | var aY = aL + aB; 182 | var aO = aL - aB; 183 | var aX = aK + aC; 184 | var aP = aK - aC; 185 | var aU = aJ + aD; 186 | var aQ = aJ - aD; 187 | var aT = aI + aH; 188 | var aS = aI - aH; 189 | var an = aY + aT; 190 | var ak = aY - aT; 191 | var am = aX + aU; 192 | var al = aX - aU; 193 | aZ[aN] = an + am; 194 | aZ[aN + 4] = an - am; 195 | var ax = (al + ak) * 0.707106781; 196 | aZ[aN + 2] = ak + ax; 197 | aZ[aN + 6] = ak - ax; 198 | an = aS + aQ; 199 | am = aQ + aP; 200 | al = aP + aO; 201 | var at = (an - al) * 0.382683433; 202 | var aw = 0.5411961 * an + at; 203 | var au = 1.306562965 * al + at; 204 | var av = am * 0.707106781; 205 | var ah = aO + av; 206 | var ag = aO - av; 207 | aZ[aN + 5] = ag + aw; 208 | aZ[aN + 3] = ag - aw; 209 | aZ[aN + 1] = ah + au; 210 | aZ[aN + 7] = ah - au; 211 | aN += 8 212 | } 213 | aN = 0; 214 | for (aR = 0; aR < aq; ++aR) { 215 | aL = aZ[aN]; 216 | aK = aZ[aN + 8]; 217 | aJ = aZ[aN + 16]; 218 | aI = aZ[aN + 24]; 219 | aH = aZ[aN + 32]; 220 | aD = aZ[aN + 40]; 221 | aC = aZ[aN + 48]; 222 | aB = aZ[aN + 56]; 223 | var ar = aL + aB; 224 | var aj = aL - aB; 225 | var az = aK + aC; 226 | var ae = aK - aC; 227 | var aG = aJ + aD; 228 | var ac = aJ - aD; 229 | var aW = aI + aH; 230 | var aa = aI - aH; 231 | var ao = ar + aW; 232 | var aV = ar - aW; 233 | var ay = az + aG; 234 | var aF = az - aG; 235 | aZ[aN] = ao + ay; 236 | aZ[aN + 32] = ao - ay; 237 | var af = (aF + aV) * 0.707106781; 238 | aZ[aN + 16] = aV + af; 239 | aZ[aN + 48] = aV - af; 240 | ao = aa + ac; 241 | ay = ac + ae; 242 | aF = ae + aj; 243 | var aM = (ao - aF) * 0.382683433; 244 | var ad = 0.5411961 * ao + aM; 245 | var a1 = 1.306562965 * aF + aM; 246 | var ab = ay * 0.707106781; 247 | var a0 = aj + ab; 248 | var aA = aj - ab; 249 | aZ[aN + 40] = aA + ad; 250 | aZ[aN + 24] = aA - ad; 251 | aZ[aN + 8] = a0 + a1; 252 | aZ[aN + 56] = a0 - a1; 253 | aN++ 254 | } 255 | var aE; 256 | for (aR = 0; aR < ai; ++aR) { 257 | aE = aZ[aR] * ap[aR]; 258 | P[aR] = (aE > 0) ? ((aE + 0.5) | 0) : ((aE - 0.5) | 0) 259 | } 260 | return P 261 | } 262 | 263 | function b () { 264 | p(65504); 265 | p(16); 266 | F(74); 267 | F(70); 268 | F(73); 269 | F(70); 270 | F(0); 271 | F(1); 272 | F(1); 273 | F(0); 274 | p(1); 275 | p(1); 276 | F(0); 277 | F(0) 278 | } 279 | 280 | function r (aa, ab) { 281 | p(65472); 282 | p(17); 283 | F(8); 284 | p(ab); 285 | p(aa); 286 | F(3); 287 | F(1); 288 | F(17); 289 | F(0); 290 | F(2); 291 | F(17); 292 | F(1); 293 | F(3); 294 | F(17); 295 | F(1) 296 | } 297 | 298 | function D () { 299 | p(65499); 300 | p(132); 301 | F(0); 302 | for (var ab = 0; ab < 64; ab++) { 303 | F(O[ab]) 304 | } 305 | F(1); 306 | for (var aa = 0; aa < 64; aa++) { 307 | F(K[aa]) 308 | } 309 | } 310 | 311 | function H () { 312 | p(65476); 313 | p(418); 314 | F(0); 315 | for (var ae = 0; ae < 16; ae++) { 316 | F(g[ae + 1]) 317 | } 318 | for (var ad = 0; ad <= 11; ad++) { 319 | F(c[ad]) 320 | } 321 | F(16); 322 | for (var ac = 0; ac < 16; ac++) { 323 | F(w[ac + 1]) 324 | } 325 | for (var ab = 0; ab <= 161; ab++) { 326 | F(E[ab]) 327 | } 328 | F(1); 329 | for (var aa = 0; aa < 16; aa++) { 330 | F(v[aa + 1]) 331 | } 332 | for (var ah = 0; ah <= 11; ah++) { 333 | F(Y[ah]) 334 | } 335 | F(17); 336 | for (var ag = 0; ag < 16; ag++) { 337 | F(J[ag + 1]) 338 | } 339 | for (var af = 0; af <= 161; af++) { 340 | F(B[af]) 341 | } 342 | } 343 | 344 | function I () { 345 | p(65498); 346 | p(12); 347 | F(3); 348 | F(1); 349 | F(0); 350 | F(2); 351 | F(17); 352 | F(3); 353 | F(17); 354 | F(0); 355 | F(63); 356 | F(0) 357 | } 358 | 359 | function L (ad, aa, al, at, ap) { 360 | var ag = ap[0]; 361 | var ab = ap[240]; 362 | var ac; 363 | const ar = 16; 364 | const ai = 63; 365 | const ah = 64; 366 | var aq = N(ad, aa); 367 | for (var am = 0; am < ah; ++am) { 368 | S[i[am]] = aq[am] 369 | } 370 | var an = S[0] - al; 371 | al = S[0]; 372 | if (an == 0) { 373 | X(at[0]) 374 | } else { 375 | ac = 32767 + an; 376 | X(at[m[ac]]); 377 | X(n[ac]) 378 | } 379 | var ae = 63; 380 | for (; (ae > 0) && (S[ae] == 0); ae--) { 381 | } 382 | if (ae == 0) { 383 | X(ag); 384 | return al 385 | } 386 | var ao = 1; 387 | var au; 388 | while (ao <= ae) { 389 | var ak = ao; 390 | for (; (S[ao] == 0) && (ao <= ae); ++ao) { 391 | } 392 | var aj = ao - ak; 393 | if (aj >= ar) { 394 | au = aj >> 4; 395 | for (var af = 1; af <= au; ++af) { 396 | X(ab) 397 | } 398 | aj = aj & 15 399 | } 400 | ac = 32767 + S[ao]; 401 | X(ap[(aj << 4) + m[ac]]); 402 | X(n[ac]); 403 | ao++ 404 | } 405 | if (ae != ai) { 406 | X(ag) 407 | } 408 | return al 409 | } 410 | 411 | function y () { 412 | var ab = String.fromCharCode; 413 | for (var aa = 0; aa < 256; aa++) { 414 | e[aa] = ab(aa) 415 | } 416 | } 417 | 418 | this.encode = function (an, aj, aB) { 419 | var aa = new Date().getTime(); 420 | if (aj) { 421 | R(aj) 422 | } 423 | j = new Array(); 424 | t = 0; 425 | a = 7; 426 | p(65496); 427 | b(); 428 | D(); 429 | r(an.width, an.height); 430 | H(); 431 | I(); 432 | var al = 0; 433 | var aq = 0; 434 | var ao = 0; 435 | t = 0; 436 | a = 7; 437 | this.encode.displayName = "_encode_"; 438 | var at = an.data; 439 | var ar = an.width; 440 | var aA = an.height; 441 | var ay = ar * 4; 442 | var ai = ar * 3; 443 | var ah, ag = 0; 444 | var am, ax, az; 445 | var ab, ap, ac, af, ae; 446 | while (ag < aA) { 447 | ah = 0; 448 | while (ah < ay) { 449 | ab = ay * ag + ah; 450 | ap = ab; 451 | ac = -1; 452 | af = 0; 453 | for (ae = 0; ae < 64; ae++) { 454 | af = ae >> 3; 455 | ac = (ae & 7) * 4; 456 | ap = ab + (af * ay) + ac; 457 | if (ag + af >= aA) { 458 | ap -= (ay * (ag + 1 + af - aA)) 459 | } 460 | if (ah + ac >= ay) { 461 | ap -= ((ah + ac) - ay + 4) 462 | } 463 | am = at[ap++]; 464 | ax = at[ap++]; 465 | az = at[ap++]; 466 | A[ae] = ((C[am] + C[(ax + 256) >> 0] + C[(az + 512) >> 0]) >> 16) - 128; 467 | f[ae] = ((C[(am + 768) >> 0] + C[(ax + 1024) >> 0] + C[(az + 1280) >> 0]) >> 16) - 128; 468 | U[ae] = ((C[(am + 1280) >> 0] + C[(ax + 1536) >> 0] + C[(az + 1792) >> 0]) >> 16) - 128 469 | } 470 | al = L(A, d, al, u, G); 471 | aq = L(f, Z, aq, h, T); 472 | ao = L(U, Z, ao, h, T); 473 | ah += 32 474 | } 475 | ag += 8 476 | } 477 | if (a >= 0) { 478 | var aw = []; 479 | aw[1] = a + 1; 480 | aw[0] = (1 << (a + 1)) - 1; 481 | X(aw) 482 | } 483 | p(65497); 484 | if (aB) { 485 | var av = j.length; 486 | var aC = new Uint8Array(av); 487 | for (var au = 0; au < av; au++) { 488 | aC[au] = j[au].charCodeAt() 489 | } 490 | j = []; 491 | var ak = new Date().getTime() - aa; 492 | return aC 493 | } 494 | var ad = "data:image/jpeg;base64," + btoa(j.join("")); 495 | j = []; 496 | var ak = new Date().getTime() - aa; 497 | return ad 498 | }; 499 | function R (ab) { 500 | if (ab <= 0) { 501 | ab = 1 502 | } 503 | if (ab > 100) { 504 | ab = 100 505 | } 506 | if (x == ab) { 507 | return 508 | } 509 | var aa = 0; 510 | if (ab < 50) { 511 | aa = Math.floor(5000 / ab) 512 | } else { 513 | aa = Math.floor(200 - ab * 2) 514 | } 515 | M(aa); 516 | x = ab; 517 | } 518 | 519 | function Q () { 520 | var aa = new Date().getTime(); 521 | if (!l) { 522 | l = 50 523 | } 524 | y(); 525 | W(); 526 | z(); 527 | V(); 528 | R(l); 529 | var ab = new Date().getTime() - aa; 530 | } 531 | 532 | Q() 533 | } 534 | 535 | module.exports = JPEGEncoder; 536 | 537 | /***/ }) 538 | 539 | }); -------------------------------------------------------------------------------- /dist/那么多文件,应该引用哪个?.txt: -------------------------------------------------------------------------------- 1 | 1. 一般情况仅需引用 【lrz.bundle.js】 即可。 2 | 但绝对不要删除目录下的【*.chunk.js】,这些文件分别对应了IOS和Android的兼容代码,检测到符合环境时会自动引入。 3 | 4 | 2. 【lrz.all.bundle.js】是包含了所有引用了,莫名其妙的问题下就引用这个吧。 5 | 例如:https://github.com/think2011/localResizeIMG/issues/6 6 | 7 | 3. 【*.map】文件是供调试用的,正式使用删不删除都没关系,因为仅在调试时才会载入。 8 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var plugins = require('gulp-load-plugins')(); 3 | var webpackConfig = require('./webpack.config'); 4 | var packageJSON = require('./package.json'); 5 | var webpack = require("webpack"); 6 | 7 | var paths = { 8 | src : 'src', 9 | dist: 'dist', 10 | test: 'test' 11 | }; 12 | 13 | var files = { 14 | js : paths.src + "/**/*.js", 15 | txt : paths.src + "/**/*.txt", 16 | html : paths.test + '/**/*.html', 17 | lrzAll : paths.dist + '/lrz.all.bundle.js', 18 | lrzAllMap: paths.dist + '/lrz.all.bundle.js.map' 19 | }; 20 | 21 | // 默认 22 | gulp.task('default', ['clean'], function () { 23 | gulp.start('watch'); 24 | }); 25 | 26 | // 开发 27 | gulp.task('dev', function () { 28 | gulp.start([ 29 | 'dev:js', 30 | 'build:html' 31 | ]); 32 | }); 33 | 34 | // 发布 35 | gulp.task('build', ['clean'], function () { 36 | gulp.start([ 37 | 'build:js', 38 | 'build:html', 39 | 'build:copy' 40 | ]); 41 | }); 42 | 43 | 44 | // 监听 45 | gulp.task('watch', ['dev'], function () { 46 | gulp.watch(files.js, ['dev:js']); 47 | }); 48 | 49 | // 清洁 50 | gulp.task('clean', function () { 51 | return gulp.src(paths.dist) 52 | .pipe(plugins.clean({force: true})); 53 | }); 54 | 55 | gulp.task('dev:js', function () { 56 | //webpackConfig.devtool = ['source-map']; 57 | 58 | return gulp.src(files.js) 59 | .pipe(plugins.webpack(webpackConfig)) 60 | .pipe(gulp.dest(paths.dist)); 61 | }); 62 | 63 | gulp.task('build:js', function () { 64 | webpackConfig.devtool = ['source-map']; 65 | webpackConfig.plugins = [ 66 | new webpack.optimize.UglifyJsPlugin({ 67 | compress : { 68 | warnings: false 69 | }, 70 | output : { 71 | comments : false, 72 | semicolons: true 73 | }, 74 | sourceMap: true 75 | }) 76 | ]; 77 | 78 | return gulp.src(paths.src + "/lrz.js") 79 | .pipe(plugins.webpack(webpackConfig)) 80 | .pipe(plugins.replace(/__packageJSON\.version__/g, packageJSON.version)) 81 | .pipe(gulp.dest(paths.dist)); 82 | }); 83 | 84 | gulp.task('build:html', ['build:js'], function () { 85 | return gulp.src(files.html) 86 | .pipe(plugins.staticHash({asset: paths.dist})) 87 | .pipe(gulp.dest(paths.test)); 88 | }); 89 | 90 | gulp.task('build:copy', ['build:html'], function () { 91 | // 未来可能可能采用 #48 92 | /* gulp.src(files.lrzAll) 93 | .pipe(plugins.rename('index.js')) 94 | .pipe(gulp.dest('./')); 95 | 96 | gulp.src(files.lrzAllMap) 97 | .pipe(plugins.rename('index.js.map')) 98 | .pipe(gulp.dest('./'));*/ 99 | 100 | return gulp.src(files.txt) 101 | .pipe(gulp.dest(paths.dist)); 102 | }); 103 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Mon Aug 10 2015 15:37:42 GMT+0800 (CST) 3 | module.exports = function (config) { 4 | config.set({ 5 | 6 | // base path that will be used to resolve all patterns (eg. files, exclude) 7 | basePath: __dirname, 8 | 9 | 10 | // frameworks to use 11 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 12 | frameworks: ['mocha', 'chai'], 13 | 14 | 15 | // list of files / patterns to load in the browser 16 | files: [ 17 | {pattern: "test/img/*.png", watched: false, included: false, served: true}, 18 | './test/**/*.spec.js' 19 | ], 20 | 21 | 22 | // list of files to exclude 23 | exclude: [], 24 | 25 | 26 | // preprocess matching files before serving them to the browser 27 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 28 | preprocessors: { 29 | './test/**/*.spec.js': ['webpack'] 30 | }, 31 | 32 | // test results reporter to use 33 | // possible values: 'dots', 'progress' 34 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 35 | 36 | reporters: ['spec'], 37 | 38 | // web server port 39 | port: 9876, 40 | 41 | 42 | // enable / disable colors in the output (reporters and logs) 43 | colors: true, 44 | 45 | 46 | // level of logging 47 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 48 | logLevel: config.LOG_INFO, 49 | 50 | 51 | // enable / disable watching file and executing tests whenever any file changes 52 | autoWatch: true, 53 | 54 | 55 | // start these browsers 56 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 57 | browsers: ['PhantomJS'], 58 | 59 | 60 | // Continuous Integration mode 61 | // if true, Karma captures browsers, runs the tests and exits 62 | singleRun: true, 63 | 64 | webpack: require('./webpack.config'), 65 | 66 | plugins: [ 67 | require("karma-webpack"), 68 | require("karma-mocha"), 69 | require("karma-chai"), 70 | require("karma-spec-reporter"), 71 | require('karma-phantomjs-launcher') 72 | ] 73 | }); 74 | }; 75 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lrz", 3 | "version": "4.9.41", 4 | "description": "前端本地客户端压缩图片,兼容IOS,Android,PC、自动按需加载文件", 5 | "main": "dist/lrz.all.bundle.js", 6 | "scripts": { 7 | "test": "node ./node_modules/karma/bin/karma start", 8 | "build": "webpack" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/think2011/localResizeIMG.git" 13 | }, 14 | "keywords": [ 15 | "lrz", 16 | "localResizeIMG", 17 | "前端压缩图片", 18 | "前端图片压缩", 19 | "压缩图片" 20 | ], 21 | "bugs": { 22 | "url": "https://github.com/think2011/localResizeIMG/issues" 23 | }, 24 | "author": "think2011", 25 | "license": "MIT", 26 | "devDependencies": { 27 | "chai": "^3.2.0", 28 | "gulp": "^3.9.0", 29 | "gulp-clean": "^0.3.1", 30 | "gulp-load-plugins": "^0.10.0", 31 | "gulp-replace": "^0.5.4", 32 | "gulp-static-hash": "^0.1.3", 33 | "gulp-webpack": "^1.5.0", 34 | "karma": "^0.13.8", 35 | "karma-chai": "^0.1.0", 36 | "karma-mocha": "^0.2.0", 37 | "karma-phantomjs-launcher": "^0.2.1", 38 | "karma-spec-reporter": "0.0.20", 39 | "karma-webpack": "^1.7.0", 40 | "mocha": "^2.2.5", 41 | "phantomjs": "^1.9.17", 42 | "webpack": "^1.11.0" 43 | }, 44 | "dependencies": { 45 | "gulp-insert": "^0.5.0", 46 | "gulp-rename": "^1.2.2" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/lib/Blob.FormData.shim.js: -------------------------------------------------------------------------------- 1 | //@source https://xts.so/demo/compress/index.html 2 | 3 | // 早期版本的浏览器需要用BlobBuilder来构造Blob,创建一个通用构造器来兼容早期版本 4 | var BlobConstructor = ((function () { 5 | try { 6 | new Blob(); 7 | return true; 8 | } catch (e) { 9 | return false; 10 | } 11 | })()) ? window.Blob : function (parts, opts) { 12 | var bb = new ( 13 | window.BlobBuilder 14 | || window.WebKitBlobBuilder 15 | || window.MSBlobBuilder 16 | || window.MozBlobBuilder 17 | ); 18 | parts.forEach(function (p) { 19 | bb.append(p); 20 | }); 21 | 22 | return bb.getBlob(opts ? opts.type : undefined); 23 | }; 24 | 25 | // Android上的AppleWebKit 534以前的内核存在一个Bug, 26 | // 导致FormData加入一个Blob对象后,上传的文件是0字节 27 | function hasFormDataBug () { 28 | var bCheck = ~navigator.userAgent.indexOf('Android') 29 | && ~navigator.vendor.indexOf('Google') 30 | && !~navigator.userAgent.indexOf('Chrome'); 31 | 32 | // QQ X5浏览器也有这个BUG 33 | return bCheck && navigator.userAgent.match(/AppleWebKit\/(\d+)/).pop() <= 534 || /MQQBrowser/g.test(navigator.userAgent); 34 | } 35 | var FormDataShim=(function(){ 36 | var formDataShimNums = 0; 37 | function FormDataShim () { 38 | var 39 | // Store a reference to this 40 | o = this, 41 | 42 | // Data to be sent 43 | parts = [], 44 | 45 | // Boundary parameter for separating the multipart values 46 | boundary = Array(21).join('-') + (+new Date() * (1e16 * Math.random())).toString(36), 47 | 48 | // Store the current XHR send method so we can safely override it 49 | oldSend = XMLHttpRequest.prototype.send; 50 | this.getParts = function () { 51 | return parts.toString(); 52 | }; 53 | this.append = function (name, value, filename) { 54 | parts.push('--' + boundary + '\r\nContent-Disposition: form-data; name="' + name + '"'); 55 | 56 | if (value instanceof Blob) { 57 | parts.push('; filename="' + (filename || 'blob') + '"\r\nContent-Type: ' + value.type + '\r\n\r\n'); 58 | parts.push(value); 59 | } 60 | else { 61 | parts.push('\r\n\r\n' + value); 62 | } 63 | parts.push('\r\n'); 64 | }; 65 | 66 | formDataShimNums++; 67 | XMLHttpRequest.prototype.send = function (val) { 68 | var fr, 69 | data, 70 | oXHR = this; 71 | 72 | if (val === o) { 73 | // Append the final boundary string 74 | parts.push('--' + boundary + '--\r\n'); 75 | // Create the blob 76 | data = new BlobConstructor(parts); 77 | 78 | // Set up and read the blob into an array to be sent 79 | fr = new FileReader(); 80 | fr.onload = function () { 81 | oldSend.call(oXHR, fr.result); 82 | }; 83 | fr.onerror = function (err) { 84 | throw err; 85 | }; 86 | fr.readAsArrayBuffer(data); 87 | 88 | // Set the multipart content type and boudary 89 | this.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary); 90 | formDataShimNums--; 91 | if(formDataShimNums == 0){ 92 | XMLHttpRequest.prototype.send = oldSend; 93 | } 94 | } 95 | else { 96 | oldSend.call(this, val); 97 | } 98 | }; 99 | }; 100 | FormDataShim.prototype = Object.create(FormData.prototype); 101 | return FormDataShim; 102 | })(); 103 | 104 | 105 | module.exports = { 106 | Blob : BlobConstructor, 107 | FormData: hasFormDataBug() ? FormDataShim : FormData 108 | }; 109 | -------------------------------------------------------------------------------- /src/lib/Promise.js: -------------------------------------------------------------------------------- 1 | (function (root) { 2 | 3 | // Use polyfill for setImmediate for performance gains 4 | var asap = (typeof setImmediate === 'function' && setImmediate) || 5 | function (fn) { 6 | setTimeout(fn, 1); 7 | }; 8 | 9 | // Polyfill for Function.prototype.bind 10 | function bind (fn, thisArg) { 11 | return function () { 12 | fn.apply(thisArg, arguments); 13 | } 14 | } 15 | 16 | var isArray = Array.isArray || function (value) { 17 | return Object.prototype.toString.call(value) === "[object Array]" 18 | }; 19 | 20 | function Promise (fn) { 21 | if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new'); 22 | if (typeof fn !== 'function') throw new TypeError('not a function'); 23 | this._state = null; 24 | this._value = null; 25 | this._deferreds = [] 26 | 27 | doResolve(fn, bind(resolve, this), bind(reject, this)) 28 | } 29 | 30 | function handle (deferred) { 31 | var me = this; 32 | if (this._state === null) { 33 | this._deferreds.push(deferred); 34 | return 35 | } 36 | asap(function () { 37 | var cb = me._state ? deferred.onFulfilled : deferred.onRejected 38 | if (cb === null) { 39 | (me._state ? deferred.resolve : deferred.reject)(me._value); 40 | return; 41 | } 42 | var ret; 43 | try { 44 | ret = cb(me._value); 45 | } 46 | catch (e) { 47 | deferred.reject(e); 48 | return; 49 | } 50 | deferred.resolve(ret); 51 | }) 52 | } 53 | 54 | function resolve (newValue) { 55 | try { //Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure 56 | if (newValue === this) throw new TypeError('A promise cannot be resolved with itself.'); 57 | if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) { 58 | var then = newValue.then; 59 | if (typeof then === 'function') { 60 | doResolve(bind(then, newValue), bind(resolve, this), bind(reject, this)); 61 | return; 62 | } 63 | } 64 | this._state = true; 65 | this._value = newValue; 66 | finale.call(this); 67 | } catch (e) { 68 | reject.call(this, e); 69 | } 70 | } 71 | 72 | function reject (newValue) { 73 | this._state = false; 74 | this._value = newValue; 75 | finale.call(this); 76 | } 77 | 78 | function finale () { 79 | for (var i = 0, len = this._deferreds.length; i < len; i++) { 80 | handle.call(this, this._deferreds[i]); 81 | } 82 | this._deferreds = null; 83 | } 84 | 85 | function Handler (onFulfilled, onRejected, resolve, reject) { 86 | this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; 87 | this.onRejected = typeof onRejected === 'function' ? onRejected : null; 88 | this.resolve = resolve; 89 | this.reject = reject; 90 | } 91 | 92 | /** 93 | * Take a potentially misbehaving resolver function and make sure 94 | * onFulfilled and onRejected are only called once. 95 | * 96 | * Makes no guarantees about asynchrony. 97 | */ 98 | function doResolve (fn, onFulfilled, onRejected) { 99 | var done = false; 100 | try { 101 | fn(function (value) { 102 | if (done) return; 103 | done = true; 104 | onFulfilled(value); 105 | }, function (reason) { 106 | if (done) return; 107 | done = true; 108 | onRejected(reason); 109 | }) 110 | } catch (ex) { 111 | if (done) return; 112 | done = true; 113 | onRejected(ex); 114 | } 115 | } 116 | 117 | Promise.prototype['catch'] = function (onRejected) { 118 | return this.then(null, onRejected); 119 | }; 120 | 121 | Promise.prototype.then = function (onFulfilled, onRejected) { 122 | var me = this; 123 | return new Promise(function (resolve, reject) { 124 | handle.call(me, new Handler(onFulfilled, onRejected, resolve, reject)); 125 | }) 126 | }; 127 | 128 | Promise.all = function () { 129 | var args = Array.prototype.slice.call(arguments.length === 1 && isArray(arguments[0]) ? arguments[0] : arguments); 130 | 131 | return new Promise(function (resolve, reject) { 132 | if (args.length === 0) return resolve([]); 133 | var remaining = args.length; 134 | 135 | function res (i, val) { 136 | try { 137 | if (val && (typeof val === 'object' || typeof val === 'function')) { 138 | var then = val.then; 139 | if (typeof then === 'function') { 140 | then.call(val, function (val) { 141 | res(i, val) 142 | }, reject); 143 | return; 144 | } 145 | } 146 | args[i] = val; 147 | if (--remaining === 0) { 148 | resolve(args); 149 | } 150 | } catch (ex) { 151 | reject(ex); 152 | } 153 | } 154 | 155 | for (var i = 0; i < args.length; i++) { 156 | res(i, args[i]); 157 | } 158 | }); 159 | }; 160 | 161 | Promise.resolve = function (value) { 162 | if (value && typeof value === 'object' && value.constructor === Promise) { 163 | return value; 164 | } 165 | 166 | return new Promise(function (resolve) { 167 | resolve(value); 168 | }); 169 | }; 170 | 171 | Promise.reject = function (value) { 172 | return new Promise(function (resolve, reject) { 173 | reject(value); 174 | }); 175 | }; 176 | 177 | Promise.race = function (values) { 178 | return new Promise(function (resolve, reject) { 179 | for (var i = 0, len = values.length; i < len; i++) { 180 | values[i].then(resolve, reject); 181 | } 182 | }); 183 | }; 184 | 185 | /** 186 | * Set the immediate function to execute callbacks 187 | * @param fn {function} Function to execute 188 | * @private 189 | */ 190 | Promise._setImmediateFn = function _setImmediateFn (fn) { 191 | asap = fn; 192 | }; 193 | 194 | 195 | Promise.prototype.always = function (callback) { 196 | var constructor = this.constructor; 197 | 198 | return this.then(function (value) { 199 | return constructor.resolve(callback()).then(function () { 200 | return value; 201 | }); 202 | }, function (reason) { 203 | return constructor.resolve(callback()).then(function () { 204 | throw reason; 205 | }); 206 | }); 207 | }; 208 | 209 | if (typeof module !== 'undefined' && module.exports) { 210 | module.exports = Promise; 211 | } else if (!root.Promise) { 212 | root.Promise = Promise; 213 | } 214 | 215 | })(this); -------------------------------------------------------------------------------- /src/lib/exif.js: -------------------------------------------------------------------------------- 1 | /* exif */ 2 | (function () { 3 | 4 | var debug = false; 5 | 6 | var root = this; 7 | 8 | var EXIF = function (obj) { 9 | if (obj instanceof EXIF) return obj; 10 | if (!(this instanceof EXIF)) return new EXIF(obj); 11 | this.EXIFwrapped = obj; 12 | }; 13 | 14 | if (typeof exports !== 'undefined') { 15 | if (typeof module !== 'undefined' && module.exports) { 16 | exports = module.exports = EXIF; 17 | } 18 | exports.EXIF = EXIF; 19 | } else { 20 | root.EXIF = EXIF; 21 | } 22 | 23 | var ExifTags = EXIF.Tags = { 24 | 25 | // version tags 26 | 0x9000: "ExifVersion", // EXIF version 27 | 0xA000: "FlashpixVersion", // Flashpix format version 28 | 29 | // colorspace tags 30 | 0xA001: "ColorSpace", // Color space information tag 31 | 32 | // image configuration 33 | 0xA002: "PixelXDimension", // Valid width of meaningful image 34 | 0xA003: "PixelYDimension", // Valid height of meaningful image 35 | 0x9101: "ComponentsConfiguration", // Information about channels 36 | 0x9102: "CompressedBitsPerPixel", // Compressed bits per pixel 37 | 38 | // user information 39 | 0x927C: "MakerNote", // Any desired information written by the manufacturer 40 | 0x9286: "UserComment", // Comments by user 41 | 42 | // related file 43 | 0xA004: "RelatedSoundFile", // Name of related sound file 44 | 45 | // date and time 46 | 0x9003: "DateTimeOriginal", // Date and time when the original image was generated 47 | 0x9004: "DateTimeDigitized", // Date and time when the image was stored digitally 48 | 0x9290: "SubsecTime", // Fractions of seconds for DateTime 49 | 0x9291: "SubsecTimeOriginal", // Fractions of seconds for DateTimeOriginal 50 | 0x9292: "SubsecTimeDigitized", // Fractions of seconds for DateTimeDigitized 51 | 52 | // picture-taking conditions 53 | 0x829A: "ExposureTime", // Exposure time (in seconds) 54 | 0x829D: "FNumber", // F number 55 | 0x8822: "ExposureProgram", // Exposure program 56 | 0x8824: "SpectralSensitivity", // Spectral sensitivity 57 | 0x8827: "ISOSpeedRatings", // ISO speed rating 58 | 0x8828: "OECF", // Optoelectric conversion factor 59 | 0x9201: "ShutterSpeedValue", // Shutter speed 60 | 0x9202: "ApertureValue", // Lens aperture 61 | 0x9203: "BrightnessValue", // Value of brightness 62 | 0x9204: "ExposureBias", // Exposure bias 63 | 0x9205: "MaxApertureValue", // Smallest F number of lens 64 | 0x9206: "SubjectDistance", // Distance to subject in meters 65 | 0x9207: "MeteringMode", // Metering mode 66 | 0x9208: "LightSource", // Kind of light source 67 | 0x9209: "Flash", // Flash status 68 | 0x9214: "SubjectArea", // Location and area of main subject 69 | 0x920A: "FocalLength", // Focal length of the lens in mm 70 | 0xA20B: "FlashEnergy", // Strobe energy in BCPS 71 | 0xA20C: "SpatialFrequencyResponse", // 72 | 0xA20E: "FocalPlaneXResolution", // Number of pixels in width direction per FocalPlaneResolutionUnit 73 | 0xA20F: "FocalPlaneYResolution", // Number of pixels in height direction per FocalPlaneResolutionUnit 74 | 0xA210: "FocalPlaneResolutionUnit", // Unit for measuring FocalPlaneXResolution and FocalPlaneYResolution 75 | 0xA214: "SubjectLocation", // Location of subject in image 76 | 0xA215: "ExposureIndex", // Exposure index selected on camera 77 | 0xA217: "SensingMethod", // Image sensor type 78 | 0xA300: "FileSource", // Image source (3 == DSC) 79 | 0xA301: "SceneType", // Scene type (1 == directly photographed) 80 | 0xA302: "CFAPattern", // Color filter array geometric pattern 81 | 0xA401: "CustomRendered", // Special processing 82 | 0xA402: "ExposureMode", // Exposure mode 83 | 0xA403: "WhiteBalance", // 1 = auto white balance, 2 = manual 84 | 0xA404: "DigitalZoomRation", // Digital zoom ratio 85 | 0xA405: "FocalLengthIn35mmFilm", // Equivalent foacl length assuming 35mm film camera (in mm) 86 | 0xA406: "SceneCaptureType", // Type of scene 87 | 0xA407: "GainControl", // Degree of overall image gain adjustment 88 | 0xA408: "Contrast", // Direction of contrast processing applied by camera 89 | 0xA409: "Saturation", // Direction of saturation processing applied by camera 90 | 0xA40A: "Sharpness", // Direction of sharpness processing applied by camera 91 | 0xA40B: "DeviceSettingDescription", // 92 | 0xA40C: "SubjectDistanceRange", // Distance to subject 93 | 94 | // other tags 95 | 0xA005: "InteroperabilityIFDPointer", 96 | 0xA420: "ImageUniqueID" // Identifier assigned uniquely to each image 97 | }; 98 | 99 | var TiffTags = EXIF.TiffTags = { 100 | 0x0100: "ImageWidth", 101 | 0x0101: "ImageHeight", 102 | 0x8769: "ExifIFDPointer", 103 | 0x8825: "GPSInfoIFDPointer", 104 | 0xA005: "InteroperabilityIFDPointer", 105 | 0x0102: "BitsPerSample", 106 | 0x0103: "Compression", 107 | 0x0106: "PhotometricInterpretation", 108 | 0x0112: "Orientation", 109 | 0x0115: "SamplesPerPixel", 110 | 0x011C: "PlanarConfiguration", 111 | 0x0212: "YCbCrSubSampling", 112 | 0x0213: "YCbCrPositioning", 113 | 0x011A: "XResolution", 114 | 0x011B: "YResolution", 115 | 0x0128: "ResolutionUnit", 116 | 0x0111: "StripOffsets", 117 | 0x0116: "RowsPerStrip", 118 | 0x0117: "StripByteCounts", 119 | 0x0201: "JPEGInterchangeFormat", 120 | 0x0202: "JPEGInterchangeFormatLength", 121 | 0x012D: "TransferFunction", 122 | 0x013E: "WhitePoint", 123 | 0x013F: "PrimaryChromaticities", 124 | 0x0211: "YCbCrCoefficients", 125 | 0x0214: "ReferenceBlackWhite", 126 | 0x0132: "DateTime", 127 | 0x010E: "ImageDescription", 128 | 0x010F: "Make", 129 | 0x0110: "Model", 130 | 0x0131: "Software", 131 | 0x013B: "Artist", 132 | 0x8298: "Copyright" 133 | }; 134 | 135 | var GPSTags = EXIF.GPSTags = { 136 | 0x0000: "GPSVersionID", 137 | 0x0001: "GPSLatitudeRef", 138 | 0x0002: "GPSLatitude", 139 | 0x0003: "GPSLongitudeRef", 140 | 0x0004: "GPSLongitude", 141 | 0x0005: "GPSAltitudeRef", 142 | 0x0006: "GPSAltitude", 143 | 0x0007: "GPSTimeStamp", 144 | 0x0008: "GPSSatellites", 145 | 0x0009: "GPSStatus", 146 | 0x000A: "GPSMeasureMode", 147 | 0x000B: "GPSDOP", 148 | 0x000C: "GPSSpeedRef", 149 | 0x000D: "GPSSpeed", 150 | 0x000E: "GPSTrackRef", 151 | 0x000F: "GPSTrack", 152 | 0x0010: "GPSImgDirectionRef", 153 | 0x0011: "GPSImgDirection", 154 | 0x0012: "GPSMapDatum", 155 | 0x0013: "GPSDestLatitudeRef", 156 | 0x0014: "GPSDestLatitude", 157 | 0x0015: "GPSDestLongitudeRef", 158 | 0x0016: "GPSDestLongitude", 159 | 0x0017: "GPSDestBearingRef", 160 | 0x0018: "GPSDestBearing", 161 | 0x0019: "GPSDestDistanceRef", 162 | 0x001A: "GPSDestDistance", 163 | 0x001B: "GPSProcessingMethod", 164 | 0x001C: "GPSAreaInformation", 165 | 0x001D: "GPSDateStamp", 166 | 0x001E: "GPSDifferential" 167 | }; 168 | 169 | var StringValues = EXIF.StringValues = { 170 | ExposureProgram : { 171 | 0: "Not defined", 172 | 1: "Manual", 173 | 2: "Normal program", 174 | 3: "Aperture priority", 175 | 4: "Shutter priority", 176 | 5: "Creative program", 177 | 6: "Action program", 178 | 7: "Portrait mode", 179 | 8: "Landscape mode" 180 | }, 181 | MeteringMode : { 182 | 0 : "Unknown", 183 | 1 : "Average", 184 | 2 : "CenterWeightedAverage", 185 | 3 : "Spot", 186 | 4 : "MultiSpot", 187 | 5 : "Pattern", 188 | 6 : "Partial", 189 | 255: "Other" 190 | }, 191 | LightSource : { 192 | 0 : "Unknown", 193 | 1 : "Daylight", 194 | 2 : "Fluorescent", 195 | 3 : "Tungsten (incandescent light)", 196 | 4 : "Flash", 197 | 9 : "Fine weather", 198 | 10 : "Cloudy weather", 199 | 11 : "Shade", 200 | 12 : "Daylight fluorescent (D 5700 - 7100K)", 201 | 13 : "Day white fluorescent (N 4600 - 5400K)", 202 | 14 : "Cool white fluorescent (W 3900 - 4500K)", 203 | 15 : "White fluorescent (WW 3200 - 3700K)", 204 | 17 : "Standard light A", 205 | 18 : "Standard light B", 206 | 19 : "Standard light C", 207 | 20 : "D55", 208 | 21 : "D65", 209 | 22 : "D75", 210 | 23 : "D50", 211 | 24 : "ISO studio tungsten", 212 | 255: "Other" 213 | }, 214 | Flash : { 215 | 0x0000: "Flash did not fire", 216 | 0x0001: "Flash fired", 217 | 0x0005: "Strobe return light not detected", 218 | 0x0007: "Strobe return light detected", 219 | 0x0009: "Flash fired, compulsory flash mode", 220 | 0x000D: "Flash fired, compulsory flash mode, return light not detected", 221 | 0x000F: "Flash fired, compulsory flash mode, return light detected", 222 | 0x0010: "Flash did not fire, compulsory flash mode", 223 | 0x0018: "Flash did not fire, auto mode", 224 | 0x0019: "Flash fired, auto mode", 225 | 0x001D: "Flash fired, auto mode, return light not detected", 226 | 0x001F: "Flash fired, auto mode, return light detected", 227 | 0x0020: "No flash function", 228 | 0x0041: "Flash fired, red-eye reduction mode", 229 | 0x0045: "Flash fired, red-eye reduction mode, return light not detected", 230 | 0x0047: "Flash fired, red-eye reduction mode, return light detected", 231 | 0x0049: "Flash fired, compulsory flash mode, red-eye reduction mode", 232 | 0x004D: "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected", 233 | 0x004F: "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected", 234 | 0x0059: "Flash fired, auto mode, red-eye reduction mode", 235 | 0x005D: "Flash fired, auto mode, return light not detected, red-eye reduction mode", 236 | 0x005F: "Flash fired, auto mode, return light detected, red-eye reduction mode" 237 | }, 238 | SensingMethod : { 239 | 1: "Not defined", 240 | 2: "One-chip color area sensor", 241 | 3: "Two-chip color area sensor", 242 | 4: "Three-chip color area sensor", 243 | 5: "Color sequential area sensor", 244 | 7: "Trilinear sensor", 245 | 8: "Color sequential linear sensor" 246 | }, 247 | SceneCaptureType : { 248 | 0: "Standard", 249 | 1: "Landscape", 250 | 2: "Portrait", 251 | 3: "Night scene" 252 | }, 253 | SceneType : { 254 | 1: "Directly photographed" 255 | }, 256 | CustomRendered : { 257 | 0: "Normal process", 258 | 1: "Custom process" 259 | }, 260 | WhiteBalance : { 261 | 0: "Auto white balance", 262 | 1: "Manual white balance" 263 | }, 264 | GainControl : { 265 | 0: "None", 266 | 1: "Low gain up", 267 | 2: "High gain up", 268 | 3: "Low gain down", 269 | 4: "High gain down" 270 | }, 271 | Contrast : { 272 | 0: "Normal", 273 | 1: "Soft", 274 | 2: "Hard" 275 | }, 276 | Saturation : { 277 | 0: "Normal", 278 | 1: "Low saturation", 279 | 2: "High saturation" 280 | }, 281 | Sharpness : { 282 | 0: "Normal", 283 | 1: "Soft", 284 | 2: "Hard" 285 | }, 286 | SubjectDistanceRange: { 287 | 0: "Unknown", 288 | 1: "Macro", 289 | 2: "Close view", 290 | 3: "Distant view" 291 | }, 292 | FileSource : { 293 | 3: "DSC" 294 | }, 295 | 296 | Components: { 297 | 0: "", 298 | 1: "Y", 299 | 2: "Cb", 300 | 3: "Cr", 301 | 4: "R", 302 | 5: "G", 303 | 6: "B" 304 | } 305 | }; 306 | 307 | function addEvent (element, event, handler) { 308 | if (element.addEventListener) { 309 | element.addEventListener(event, handler, false); 310 | } else if (element.attachEvent) { 311 | element.attachEvent("on" + event, handler); 312 | } 313 | } 314 | 315 | function imageHasData (img) { 316 | return !!(img.exifdata); 317 | } 318 | 319 | 320 | function base64ToArrayBuffer (base64, contentType) { 321 | contentType = contentType || base64.match(/^data\:([^\;]+)\;base64,/mi)[1] || ''; // e.g. 'data:image/jpeg;base64,...' => 'image/jpeg' 322 | base64 = base64.replace(/^data\:([^\;]+)\;base64,/gmi, ''); 323 | var binary = atob(base64); 324 | var len = binary.length; 325 | var buffer = new ArrayBuffer(len); 326 | var view = new Uint8Array(buffer); 327 | for (var i = 0; i < len; i++) { 328 | view[i] = binary.charCodeAt(i); 329 | } 330 | return buffer; 331 | } 332 | 333 | function objectURLToBlob (url, callback) { 334 | var http = new XMLHttpRequest(); 335 | http.open("GET", url, true); 336 | http.responseType = "blob"; 337 | http.onload = function (e) { 338 | if (this.status == 200 || this.status === 0) { 339 | callback(this.response); 340 | } 341 | }; 342 | http.send(); 343 | } 344 | 345 | function getImageData (img, callback) { 346 | function handleBinaryFile (binFile) { 347 | var data = findEXIFinJPEG(binFile); 348 | var iptcdata = findIPTCinJPEG(binFile); 349 | img.exifdata = data || {}; 350 | img.iptcdata = iptcdata || {}; 351 | if (callback) { 352 | callback.call(img); 353 | } 354 | } 355 | 356 | if (img.src) { 357 | if (/^data\:/i.test(img.src)) { // Data URI 358 | var arrayBuffer = base64ToArrayBuffer(img.src); 359 | handleBinaryFile(arrayBuffer); 360 | 361 | } else if (/^blob\:/i.test(img.src)) { // Object URL 362 | var fileReader = new FileReader(); 363 | fileReader.onload = function (e) { 364 | handleBinaryFile(e.target.result); 365 | }; 366 | objectURLToBlob(img.src, function (blob) { 367 | fileReader.readAsArrayBuffer(blob); 368 | }); 369 | } else { 370 | var http = new XMLHttpRequest(); 371 | http.onload = function () { 372 | if (this.status == 200 || this.status === 0) { 373 | handleBinaryFile(http.response); 374 | } else { 375 | callback(new Error("Could not load image")); 376 | } 377 | http = null; 378 | }; 379 | http.open("GET", img.src, true); 380 | http.responseType = "arraybuffer"; 381 | http.send(null); 382 | } 383 | } else if (window.FileReader && (img instanceof window.Blob || img instanceof window.File)) { 384 | var fileReader = new FileReader(); 385 | fileReader.onload = function (e) { 386 | if (debug) console.log("Got file of length " + e.target.result.byteLength); 387 | handleBinaryFile(e.target.result); 388 | }; 389 | 390 | fileReader.readAsArrayBuffer(img); 391 | } 392 | } 393 | 394 | function findEXIFinJPEG (file) { 395 | var dataView = new DataView(file); 396 | 397 | if (debug) console.log("Got file of length " + file.byteLength); 398 | if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) { 399 | if (debug) console.log("Not a valid JPEG"); 400 | return false; // not a valid jpeg 401 | } 402 | 403 | var offset = 2, 404 | length = file.byteLength, 405 | marker; 406 | 407 | while (offset < length) { 408 | if (dataView.getUint8(offset) != 0xFF) { 409 | if (debug) console.log("Not a valid marker at offset " + offset + ", found: " + dataView.getUint8(offset)); 410 | return false; // not a valid marker, something is wrong 411 | } 412 | 413 | marker = dataView.getUint8(offset + 1); 414 | if (debug) console.log(marker); 415 | 416 | // we could implement handling for other markers here, 417 | // but we're only looking for 0xFFE1 for EXIF data 418 | 419 | if (marker == 225) { 420 | if (debug) console.log("Found 0xFFE1 marker"); 421 | 422 | return readEXIFData(dataView, offset + 4, dataView.getUint16(offset + 2) - 2); 423 | 424 | // offset += 2 + file.getShortAt(offset+2, true); 425 | 426 | } else { 427 | offset += 2 + dataView.getUint16(offset + 2); 428 | } 429 | 430 | } 431 | 432 | } 433 | 434 | function findIPTCinJPEG (file) { 435 | var dataView = new DataView(file); 436 | 437 | if (debug) console.log("Got file of length " + file.byteLength); 438 | if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) { 439 | if (debug) console.log("Not a valid JPEG"); 440 | return false; // not a valid jpeg 441 | } 442 | 443 | var offset = 2, 444 | length = file.byteLength; 445 | 446 | 447 | var isFieldSegmentStart = function (dataView, offset) { 448 | return ( 449 | dataView.getUint8(offset) === 0x38 && 450 | dataView.getUint8(offset + 1) === 0x42 && 451 | dataView.getUint8(offset + 2) === 0x49 && 452 | dataView.getUint8(offset + 3) === 0x4D && 453 | dataView.getUint8(offset + 4) === 0x04 && 454 | dataView.getUint8(offset + 5) === 0x04 455 | ); 456 | }; 457 | 458 | while (offset < length) { 459 | 460 | if (isFieldSegmentStart(dataView, offset)) { 461 | 462 | // Get the length of the name header (which is padded to an even number of bytes) 463 | var nameHeaderLength = dataView.getUint8(offset + 7); 464 | if (nameHeaderLength % 2 !== 0) nameHeaderLength += 1; 465 | // Check for pre photoshop 6 format 466 | if (nameHeaderLength === 0) { 467 | // Always 4 468 | nameHeaderLength = 4; 469 | } 470 | 471 | var startOffset = offset + 8 + nameHeaderLength; 472 | var sectionLength = dataView.getUint16(offset + 6 + nameHeaderLength); 473 | 474 | return readIPTCData(file, startOffset, sectionLength); 475 | 476 | break; 477 | 478 | } 479 | 480 | 481 | // Not the marker, continue searching 482 | offset++; 483 | 484 | } 485 | 486 | } 487 | 488 | var IptcFieldMap = { 489 | 0x78: 'caption', 490 | 0x6E: 'credit', 491 | 0x19: 'keywords', 492 | 0x37: 'dateCreated', 493 | 0x50: 'byline', 494 | 0x55: 'bylineTitle', 495 | 0x7A: 'captionWriter', 496 | 0x69: 'headline', 497 | 0x74: 'copyright', 498 | 0x0F: 'category' 499 | }; 500 | 501 | function readIPTCData (file, startOffset, sectionLength) { 502 | var dataView = new DataView(file); 503 | var data = {}; 504 | var fieldValue, fieldName, dataSize, segmentType, segmentSize; 505 | var segmentStartPos = startOffset; 506 | while (segmentStartPos < startOffset + sectionLength) { 507 | if (dataView.getUint8(segmentStartPos) === 0x1C && dataView.getUint8(segmentStartPos + 1) === 0x02) { 508 | segmentType = dataView.getUint8(segmentStartPos + 2); 509 | if (segmentType in IptcFieldMap) { 510 | dataSize = dataView.getInt16(segmentStartPos + 3); 511 | segmentSize = dataSize + 5; 512 | fieldName = IptcFieldMap[segmentType]; 513 | fieldValue = getStringFromDB(dataView, segmentStartPos + 5, dataSize); 514 | // Check if we already stored a value with this name 515 | if (data.hasOwnProperty(fieldName)) { 516 | // Value already stored with this name, create multivalue field 517 | if (data[fieldName] instanceof Array) { 518 | data[fieldName].push(fieldValue); 519 | } 520 | else { 521 | data[fieldName] = [data[fieldName], fieldValue]; 522 | } 523 | } 524 | else { 525 | data[fieldName] = fieldValue; 526 | } 527 | } 528 | 529 | } 530 | segmentStartPos++; 531 | } 532 | return data; 533 | } 534 | 535 | 536 | function readTags (file, tiffStart, dirStart, strings, bigEnd) { 537 | var entries = file.getUint16(dirStart, !bigEnd), 538 | tags = {}, 539 | entryOffset, tag, 540 | i; 541 | 542 | for (i = 0; i < entries; i++) { 543 | entryOffset = dirStart + i * 12 + 2; 544 | tag = strings[file.getUint16(entryOffset, !bigEnd)]; 545 | if (!tag && debug) console.log("Unknown tag: " + file.getUint16(entryOffset, !bigEnd)); 546 | tags[tag] = readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd); 547 | } 548 | return tags; 549 | } 550 | 551 | 552 | function readTagValue (file, entryOffset, tiffStart, dirStart, bigEnd) { 553 | var type = file.getUint16(entryOffset + 2, !bigEnd), 554 | numValues = file.getUint32(entryOffset + 4, !bigEnd), 555 | valueOffset = file.getUint32(entryOffset + 8, !bigEnd) + tiffStart, 556 | offset, 557 | vals, val, n, 558 | numerator, denominator; 559 | 560 | switch (type) { 561 | case 1: // byte, 8-bit unsigned int 562 | case 7: // undefined, 8-bit byte, value depending on field 563 | if (numValues == 1) { 564 | return file.getUint8(entryOffset + 8, !bigEnd); 565 | } else { 566 | offset = numValues > 4 ? valueOffset : (entryOffset + 8); 567 | vals = []; 568 | for (n = 0; n < numValues; n++) { 569 | vals[n] = file.getUint8(offset + n); 570 | } 571 | return vals; 572 | } 573 | 574 | case 2: // ascii, 8-bit byte 575 | offset = numValues > 4 ? valueOffset : (entryOffset + 8); 576 | return getStringFromDB(file, offset, numValues - 1); 577 | 578 | case 3: // short, 16 bit int 579 | if (numValues == 1) { 580 | return file.getUint16(entryOffset + 8, !bigEnd); 581 | } else { 582 | offset = numValues > 2 ? valueOffset : (entryOffset + 8); 583 | vals = []; 584 | for (n = 0; n < numValues; n++) { 585 | vals[n] = file.getUint16(offset + 2 * n, !bigEnd); 586 | } 587 | return vals; 588 | } 589 | 590 | case 4: // long, 32 bit int 591 | if (numValues == 1) { 592 | return file.getUint32(entryOffset + 8, !bigEnd); 593 | } else { 594 | vals = []; 595 | for (n = 0; n < numValues; n++) { 596 | vals[n] = file.getUint32(valueOffset + 4 * n, !bigEnd); 597 | } 598 | return vals; 599 | } 600 | 601 | case 5: // rational = two long values, first is numerator, second is denominator 602 | if (numValues == 1) { 603 | numerator = file.getUint32(valueOffset, !bigEnd); 604 | denominator = file.getUint32(valueOffset + 4, !bigEnd); 605 | val = new Number(numerator / denominator); 606 | val.numerator = numerator; 607 | val.denominator = denominator; 608 | return val; 609 | } else { 610 | vals = []; 611 | for (n = 0; n < numValues; n++) { 612 | numerator = file.getUint32(valueOffset + 8 * n, !bigEnd); 613 | denominator = file.getUint32(valueOffset + 4 + 8 * n, !bigEnd); 614 | vals[n] = new Number(numerator / denominator); 615 | vals[n].numerator = numerator; 616 | vals[n].denominator = denominator; 617 | } 618 | return vals; 619 | } 620 | 621 | case 9: // slong, 32 bit signed int 622 | if (numValues == 1) { 623 | return file.getInt32(entryOffset + 8, !bigEnd); 624 | } else { 625 | vals = []; 626 | for (n = 0; n < numValues; n++) { 627 | vals[n] = file.getInt32(valueOffset + 4 * n, !bigEnd); 628 | } 629 | return vals; 630 | } 631 | 632 | case 10: // signed rational, two slongs, first is numerator, second is denominator 633 | if (numValues == 1) { 634 | return file.getInt32(valueOffset, !bigEnd) / file.getInt32(valueOffset + 4, !bigEnd); 635 | } else { 636 | vals = []; 637 | for (n = 0; n < numValues; n++) { 638 | vals[n] = file.getInt32(valueOffset + 8 * n, !bigEnd) / file.getInt32(valueOffset + 4 + 8 * n, !bigEnd); 639 | } 640 | return vals; 641 | } 642 | } 643 | } 644 | 645 | function getStringFromDB (buffer, start, length) { 646 | var outstr = "", n; 647 | for (n = start; n < start + length; n++) { 648 | outstr += String.fromCharCode(buffer.getUint8(n)); 649 | } 650 | return outstr; 651 | } 652 | 653 | function readEXIFData (file, start) { 654 | if (getStringFromDB(file, start, 4) != "Exif") { 655 | if (debug) console.log("Not valid EXIF data! " + getStringFromDB(file, start, 4)); 656 | return false; 657 | } 658 | 659 | var bigEnd, 660 | tags, tag, 661 | exifData, gpsData, 662 | tiffOffset = start + 6; 663 | 664 | // test for TIFF validity and endianness 665 | if (file.getUint16(tiffOffset) == 0x4949) { 666 | bigEnd = false; 667 | } else if (file.getUint16(tiffOffset) == 0x4D4D) { 668 | bigEnd = true; 669 | } else { 670 | if (debug) console.log("Not valid TIFF data! (no 0x4949 or 0x4D4D)"); 671 | return false; 672 | } 673 | 674 | if (file.getUint16(tiffOffset + 2, !bigEnd) != 0x002A) { 675 | if (debug) console.log("Not valid TIFF data! (no 0x002A)"); 676 | return false; 677 | } 678 | 679 | var firstIFDOffset = file.getUint32(tiffOffset + 4, !bigEnd); 680 | 681 | if (firstIFDOffset < 0x00000008) { 682 | if (debug) console.log("Not valid TIFF data! (First offset less than 8)", file.getUint32(tiffOffset + 4, !bigEnd)); 683 | return false; 684 | } 685 | 686 | tags = readTags(file, tiffOffset, tiffOffset + firstIFDOffset, TiffTags, bigEnd); 687 | 688 | if (tags.ExifIFDPointer) { 689 | exifData = readTags(file, tiffOffset, tiffOffset + tags.ExifIFDPointer, ExifTags, bigEnd); 690 | for (tag in exifData) { 691 | switch (tag) { 692 | case "LightSource" : 693 | case "Flash" : 694 | case "MeteringMode" : 695 | case "ExposureProgram" : 696 | case "SensingMethod" : 697 | case "SceneCaptureType" : 698 | case "SceneType" : 699 | case "CustomRendered" : 700 | case "WhiteBalance" : 701 | case "GainControl" : 702 | case "Contrast" : 703 | case "Saturation" : 704 | case "Sharpness" : 705 | case "SubjectDistanceRange" : 706 | case "FileSource" : 707 | exifData[tag] = StringValues[tag][exifData[tag]]; 708 | break; 709 | 710 | case "ExifVersion" : 711 | case "FlashpixVersion" : 712 | exifData[tag] = String.fromCharCode(exifData[tag][0], exifData[tag][1], exifData[tag][2], exifData[tag][3]); 713 | break; 714 | 715 | case "ComponentsConfiguration" : 716 | exifData[tag] = 717 | StringValues.Components[exifData[tag][0]] + 718 | StringValues.Components[exifData[tag][1]] + 719 | StringValues.Components[exifData[tag][2]] + 720 | StringValues.Components[exifData[tag][3]]; 721 | break; 722 | } 723 | tags[tag] = exifData[tag]; 724 | } 725 | } 726 | 727 | if (tags.GPSInfoIFDPointer) { 728 | gpsData = readTags(file, tiffOffset, tiffOffset + tags.GPSInfoIFDPointer, GPSTags, bigEnd); 729 | for (tag in gpsData) { 730 | switch (tag) { 731 | case "GPSVersionID" : 732 | gpsData[tag] = gpsData[tag][0] + 733 | "." + gpsData[tag][1] + 734 | "." + gpsData[tag][2] + 735 | "." + gpsData[tag][3]; 736 | break; 737 | } 738 | tags[tag] = gpsData[tag]; 739 | } 740 | } 741 | 742 | return tags; 743 | } 744 | 745 | EXIF.getData = function (img, callback) { 746 | if ((img instanceof Image || img instanceof HTMLImageElement) && !img.complete) return false; 747 | 748 | if (!imageHasData(img)) { 749 | getImageData(img, callback); 750 | } else { 751 | if (callback) { 752 | callback.call(img); 753 | } 754 | } 755 | return true; 756 | } 757 | 758 | EXIF.getTag = function (img, tag) { 759 | if (!imageHasData(img)) return; 760 | return img.exifdata[tag]; 761 | } 762 | 763 | EXIF.getAllTags = function (img) { 764 | if (!imageHasData(img)) return {}; 765 | var a, 766 | data = img.exifdata, 767 | tags = {}; 768 | for (a in data) { 769 | if (data.hasOwnProperty(a)) { 770 | tags[a] = data[a]; 771 | } 772 | } 773 | return tags; 774 | } 775 | 776 | EXIF.pretty = function (img) { 777 | if (!imageHasData(img)) return ""; 778 | var a, 779 | data = img.exifdata, 780 | strPretty = ""; 781 | for (a in data) { 782 | if (data.hasOwnProperty(a)) { 783 | if (typeof data[a] == "object") { 784 | if (data[a] instanceof Number) { 785 | strPretty += a + " : " + data[a] + " [" + data[a].numerator + "/" + data[a].denominator + "]\r\n"; 786 | } else { 787 | strPretty += a + " : [" + data[a].length + " values]\r\n"; 788 | } 789 | } else { 790 | strPretty += a + " : " + data[a] + "\r\n"; 791 | } 792 | } 793 | } 794 | return strPretty; 795 | } 796 | 797 | EXIF.readFromBinaryFile = function (file) { 798 | return findEXIFinJPEG(file); 799 | } 800 | 801 | if (typeof define === 'function' && define.amd) { 802 | define('exif-js', [], function () { 803 | return EXIF; 804 | }); 805 | } 806 | }.call(this)); -------------------------------------------------------------------------------- /src/lib/jpeg_encoder_basic.js: -------------------------------------------------------------------------------- 1 | function JPEGEncoder (l) { 2 | var o = this; 3 | var s = Math.round; 4 | var k = Math.floor; 5 | var O = new Array(64); 6 | var K = new Array(64); 7 | var d = new Array(64); 8 | var Z = new Array(64); 9 | var u; 10 | var h; 11 | var G; 12 | var T; 13 | var n = new Array(65535); 14 | var m = new Array(65535); 15 | var P = new Array(64); 16 | var S = new Array(64); 17 | var j = []; 18 | var t = 0; 19 | var a = 7; 20 | var A = new Array(64); 21 | var f = new Array(64); 22 | var U = new Array(64); 23 | var e = new Array(256); 24 | var C = new Array(2048); 25 | var x; 26 | var i = [0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18, 24, 31, 40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63]; 27 | var g = [0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0]; 28 | var c = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; 29 | var w = [0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125]; 30 | var E = [1, 2, 3, 0, 4, 17, 5, 18, 33, 49, 65, 6, 19, 81, 97, 7, 34, 113, 20, 50, 129, 145, 161, 8, 35, 66, 177, 193, 21, 82, 209, 240, 36, 51, 98, 114, 130, 9, 10, 22, 23, 24, 25, 26, 37, 38, 39, 40, 41, 42, 52, 53, 54, 55, 56, 57, 58, 67, 68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87, 88, 89, 90, 99, 100, 101, 102, 103, 104, 105, 106, 115, 116, 117, 118, 119, 120, 121, 122, 131, 132, 133, 134, 135, 136, 137, 138, 146, 147, 148, 149, 150, 151, 152, 153, 154, 162, 163, 164, 165, 166, 167, 168, 169, 170, 178, 179, 180, 181, 182, 183, 184, 185, 186, 194, 195, 196, 197, 198, 199, 200, 201, 202, 210, 211, 212, 213, 214, 215, 216, 217, 218, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250]; 31 | var v = [0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0]; 32 | var Y = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; 33 | var J = [0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119]; 34 | var B = [0, 1, 2, 3, 17, 4, 5, 33, 49, 6, 18, 65, 81, 7, 97, 113, 19, 34, 50, 129, 8, 20, 66, 145, 161, 177, 193, 9, 35, 51, 82, 240, 21, 98, 114, 209, 10, 22, 36, 52, 225, 37, 241, 23, 24, 25, 26, 38, 39, 40, 41, 42, 53, 54, 55, 56, 57, 58, 67, 68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87, 88, 89, 90, 99, 100, 101, 102, 103, 104, 105, 106, 115, 116, 117, 118, 119, 120, 121, 122, 130, 131, 132, 133, 134, 135, 136, 137, 138, 146, 147, 148, 149, 150, 151, 152, 153, 154, 162, 163, 164, 165, 166, 167, 168, 169, 170, 178, 179, 180, 181, 182, 183, 184, 185, 186, 194, 195, 196, 197, 198, 199, 200, 201, 202, 210, 211, 212, 213, 214, 215, 216, 217, 218, 226, 227, 228, 229, 230, 231, 232, 233, 234, 242, 243, 244, 245, 246, 247, 248, 249, 250]; 35 | 36 | function M (ag) { 37 | var af = [16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92, 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99]; 38 | for (var ae = 0; ae < 64; ae++) { 39 | var aj = k((af[ae] * ag + 50) / 100); 40 | if (aj < 1) { 41 | aj = 1 42 | } else { 43 | if (aj > 255) { 44 | aj = 255 45 | } 46 | } 47 | O[i[ae]] = aj 48 | } 49 | var ah = [17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, 24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99]; 50 | for (var ad = 0; ad < 64; ad++) { 51 | var ai = k((ah[ad] * ag + 50) / 100); 52 | if (ai < 1) { 53 | ai = 1 54 | } else { 55 | if (ai > 255) { 56 | ai = 255 57 | } 58 | } 59 | K[i[ad]] = ai 60 | } 61 | var ac = [1, 1.387039845, 1.306562965, 1.175875602, 1, 0.785694958, 0.5411961, 0.275899379]; 62 | var ab = 0; 63 | for (var ak = 0; ak < 8; ak++) { 64 | for (var aa = 0; aa < 8; aa++) { 65 | d[ab] = (1 / (O[i[ab]] * ac[ak] * ac[aa] * 8)); 66 | Z[ab] = (1 / (K[i[ab]] * ac[ak] * ac[aa] * 8)); 67 | ab++ 68 | } 69 | } 70 | } 71 | 72 | function q (ae, aa) { 73 | var ad = 0; 74 | var ag = 0; 75 | var af = new Array(); 76 | for (var ab = 1; ab <= 16; ab++) { 77 | for (var ac = 1; ac <= ae[ab]; ac++) { 78 | af[aa[ag]] = []; 79 | af[aa[ag]][0] = ad; 80 | af[aa[ag]][1] = ab; 81 | ag++; 82 | ad++ 83 | } 84 | ad *= 2 85 | } 86 | return af 87 | } 88 | 89 | function W () { 90 | u = q(g, c); 91 | h = q(v, Y); 92 | G = q(w, E); 93 | T = q(J, B) 94 | } 95 | 96 | function z () { 97 | var ac = 1; 98 | var ab = 2; 99 | for (var aa = 1; aa <= 15; aa++) { 100 | for (var ad = ac; ad < ab; ad++) { 101 | m[32767 + ad] = aa; 102 | n[32767 + ad] = []; 103 | n[32767 + ad][1] = aa; 104 | n[32767 + ad][0] = ad 105 | } 106 | for (var ae = -(ab - 1); ae <= -ac; ae++) { 107 | m[32767 + ae] = aa; 108 | n[32767 + ae] = []; 109 | n[32767 + ae][1] = aa; 110 | n[32767 + ae][0] = ab - 1 + ae 111 | } 112 | ac <<= 1; 113 | ab <<= 1 114 | } 115 | } 116 | 117 | function V () { 118 | for (var aa = 0; aa < 256; aa++) { 119 | C[aa] = 19595 * aa; 120 | C[(aa + 256) >> 0] = 38470 * aa; 121 | C[(aa + 512) >> 0] = 7471 * aa + 32768; 122 | C[(aa + 768) >> 0] = -11059 * aa; 123 | C[(aa + 1024) >> 0] = -21709 * aa; 124 | C[(aa + 1280) >> 0] = 32768 * aa + 8421375; 125 | C[(aa + 1536) >> 0] = -27439 * aa; 126 | C[(aa + 1792) >> 0] = -5329 * aa 127 | } 128 | } 129 | 130 | function X (aa) { 131 | var ac = aa[0]; 132 | var ab = aa[1] - 1; 133 | while (ab >= 0) { 134 | if (ac & (1 << ab)) { 135 | t |= (1 << a) 136 | } 137 | ab--; 138 | a--; 139 | if (a < 0) { 140 | if (t == 255) { 141 | F(255); 142 | F(0) 143 | } else { 144 | F(t) 145 | } 146 | a = 7; 147 | t = 0 148 | } 149 | } 150 | } 151 | 152 | function F (aa) { 153 | j.push(e[aa]) 154 | } 155 | 156 | function p (aa) { 157 | F((aa >> 8) & 255); 158 | F((aa) & 255) 159 | } 160 | 161 | function N (aZ, ap) { 162 | var aL, aK, aJ, aI, aH, aD, aC, aB; 163 | var aN = 0; 164 | var aR; 165 | const aq = 8; 166 | const ai = 64; 167 | for (aR = 0; aR < aq; ++aR) { 168 | aL = aZ[aN]; 169 | aK = aZ[aN + 1]; 170 | aJ = aZ[aN + 2]; 171 | aI = aZ[aN + 3]; 172 | aH = aZ[aN + 4]; 173 | aD = aZ[aN + 5]; 174 | aC = aZ[aN + 6]; 175 | aB = aZ[aN + 7]; 176 | var aY = aL + aB; 177 | var aO = aL - aB; 178 | var aX = aK + aC; 179 | var aP = aK - aC; 180 | var aU = aJ + aD; 181 | var aQ = aJ - aD; 182 | var aT = aI + aH; 183 | var aS = aI - aH; 184 | var an = aY + aT; 185 | var ak = aY - aT; 186 | var am = aX + aU; 187 | var al = aX - aU; 188 | aZ[aN] = an + am; 189 | aZ[aN + 4] = an - am; 190 | var ax = (al + ak) * 0.707106781; 191 | aZ[aN + 2] = ak + ax; 192 | aZ[aN + 6] = ak - ax; 193 | an = aS + aQ; 194 | am = aQ + aP; 195 | al = aP + aO; 196 | var at = (an - al) * 0.382683433; 197 | var aw = 0.5411961 * an + at; 198 | var au = 1.306562965 * al + at; 199 | var av = am * 0.707106781; 200 | var ah = aO + av; 201 | var ag = aO - av; 202 | aZ[aN + 5] = ag + aw; 203 | aZ[aN + 3] = ag - aw; 204 | aZ[aN + 1] = ah + au; 205 | aZ[aN + 7] = ah - au; 206 | aN += 8 207 | } 208 | aN = 0; 209 | for (aR = 0; aR < aq; ++aR) { 210 | aL = aZ[aN]; 211 | aK = aZ[aN + 8]; 212 | aJ = aZ[aN + 16]; 213 | aI = aZ[aN + 24]; 214 | aH = aZ[aN + 32]; 215 | aD = aZ[aN + 40]; 216 | aC = aZ[aN + 48]; 217 | aB = aZ[aN + 56]; 218 | var ar = aL + aB; 219 | var aj = aL - aB; 220 | var az = aK + aC; 221 | var ae = aK - aC; 222 | var aG = aJ + aD; 223 | var ac = aJ - aD; 224 | var aW = aI + aH; 225 | var aa = aI - aH; 226 | var ao = ar + aW; 227 | var aV = ar - aW; 228 | var ay = az + aG; 229 | var aF = az - aG; 230 | aZ[aN] = ao + ay; 231 | aZ[aN + 32] = ao - ay; 232 | var af = (aF + aV) * 0.707106781; 233 | aZ[aN + 16] = aV + af; 234 | aZ[aN + 48] = aV - af; 235 | ao = aa + ac; 236 | ay = ac + ae; 237 | aF = ae + aj; 238 | var aM = (ao - aF) * 0.382683433; 239 | var ad = 0.5411961 * ao + aM; 240 | var a1 = 1.306562965 * aF + aM; 241 | var ab = ay * 0.707106781; 242 | var a0 = aj + ab; 243 | var aA = aj - ab; 244 | aZ[aN + 40] = aA + ad; 245 | aZ[aN + 24] = aA - ad; 246 | aZ[aN + 8] = a0 + a1; 247 | aZ[aN + 56] = a0 - a1; 248 | aN++ 249 | } 250 | var aE; 251 | for (aR = 0; aR < ai; ++aR) { 252 | aE = aZ[aR] * ap[aR]; 253 | P[aR] = (aE > 0) ? ((aE + 0.5) | 0) : ((aE - 0.5) | 0) 254 | } 255 | return P 256 | } 257 | 258 | function b () { 259 | p(65504); 260 | p(16); 261 | F(74); 262 | F(70); 263 | F(73); 264 | F(70); 265 | F(0); 266 | F(1); 267 | F(1); 268 | F(0); 269 | p(1); 270 | p(1); 271 | F(0); 272 | F(0) 273 | } 274 | 275 | function r (aa, ab) { 276 | p(65472); 277 | p(17); 278 | F(8); 279 | p(ab); 280 | p(aa); 281 | F(3); 282 | F(1); 283 | F(17); 284 | F(0); 285 | F(2); 286 | F(17); 287 | F(1); 288 | F(3); 289 | F(17); 290 | F(1) 291 | } 292 | 293 | function D () { 294 | p(65499); 295 | p(132); 296 | F(0); 297 | for (var ab = 0; ab < 64; ab++) { 298 | F(O[ab]) 299 | } 300 | F(1); 301 | for (var aa = 0; aa < 64; aa++) { 302 | F(K[aa]) 303 | } 304 | } 305 | 306 | function H () { 307 | p(65476); 308 | p(418); 309 | F(0); 310 | for (var ae = 0; ae < 16; ae++) { 311 | F(g[ae + 1]) 312 | } 313 | for (var ad = 0; ad <= 11; ad++) { 314 | F(c[ad]) 315 | } 316 | F(16); 317 | for (var ac = 0; ac < 16; ac++) { 318 | F(w[ac + 1]) 319 | } 320 | for (var ab = 0; ab <= 161; ab++) { 321 | F(E[ab]) 322 | } 323 | F(1); 324 | for (var aa = 0; aa < 16; aa++) { 325 | F(v[aa + 1]) 326 | } 327 | for (var ah = 0; ah <= 11; ah++) { 328 | F(Y[ah]) 329 | } 330 | F(17); 331 | for (var ag = 0; ag < 16; ag++) { 332 | F(J[ag + 1]) 333 | } 334 | for (var af = 0; af <= 161; af++) { 335 | F(B[af]) 336 | } 337 | } 338 | 339 | function I () { 340 | p(65498); 341 | p(12); 342 | F(3); 343 | F(1); 344 | F(0); 345 | F(2); 346 | F(17); 347 | F(3); 348 | F(17); 349 | F(0); 350 | F(63); 351 | F(0) 352 | } 353 | 354 | function L (ad, aa, al, at, ap) { 355 | var ag = ap[0]; 356 | var ab = ap[240]; 357 | var ac; 358 | const ar = 16; 359 | const ai = 63; 360 | const ah = 64; 361 | var aq = N(ad, aa); 362 | for (var am = 0; am < ah; ++am) { 363 | S[i[am]] = aq[am] 364 | } 365 | var an = S[0] - al; 366 | al = S[0]; 367 | if (an == 0) { 368 | X(at[0]) 369 | } else { 370 | ac = 32767 + an; 371 | X(at[m[ac]]); 372 | X(n[ac]) 373 | } 374 | var ae = 63; 375 | for (; (ae > 0) && (S[ae] == 0); ae--) { 376 | } 377 | if (ae == 0) { 378 | X(ag); 379 | return al 380 | } 381 | var ao = 1; 382 | var au; 383 | while (ao <= ae) { 384 | var ak = ao; 385 | for (; (S[ao] == 0) && (ao <= ae); ++ao) { 386 | } 387 | var aj = ao - ak; 388 | if (aj >= ar) { 389 | au = aj >> 4; 390 | for (var af = 1; af <= au; ++af) { 391 | X(ab) 392 | } 393 | aj = aj & 15 394 | } 395 | ac = 32767 + S[ao]; 396 | X(ap[(aj << 4) + m[ac]]); 397 | X(n[ac]); 398 | ao++ 399 | } 400 | if (ae != ai) { 401 | X(ag) 402 | } 403 | return al 404 | } 405 | 406 | function y () { 407 | var ab = String.fromCharCode; 408 | for (var aa = 0; aa < 256; aa++) { 409 | e[aa] = ab(aa) 410 | } 411 | } 412 | 413 | this.encode = function (an, aj, aB) { 414 | var aa = new Date().getTime(); 415 | if (aj) { 416 | R(aj) 417 | } 418 | j = new Array(); 419 | t = 0; 420 | a = 7; 421 | p(65496); 422 | b(); 423 | D(); 424 | r(an.width, an.height); 425 | H(); 426 | I(); 427 | var al = 0; 428 | var aq = 0; 429 | var ao = 0; 430 | t = 0; 431 | a = 7; 432 | this.encode.displayName = "_encode_"; 433 | var at = an.data; 434 | var ar = an.width; 435 | var aA = an.height; 436 | var ay = ar * 4; 437 | var ai = ar * 3; 438 | var ah, ag = 0; 439 | var am, ax, az; 440 | var ab, ap, ac, af, ae; 441 | while (ag < aA) { 442 | ah = 0; 443 | while (ah < ay) { 444 | ab = ay * ag + ah; 445 | ap = ab; 446 | ac = -1; 447 | af = 0; 448 | for (ae = 0; ae < 64; ae++) { 449 | af = ae >> 3; 450 | ac = (ae & 7) * 4; 451 | ap = ab + (af * ay) + ac; 452 | if (ag + af >= aA) { 453 | ap -= (ay * (ag + 1 + af - aA)) 454 | } 455 | if (ah + ac >= ay) { 456 | ap -= ((ah + ac) - ay + 4) 457 | } 458 | am = at[ap++]; 459 | ax = at[ap++]; 460 | az = at[ap++]; 461 | A[ae] = ((C[am] + C[(ax + 256) >> 0] + C[(az + 512) >> 0]) >> 16) - 128; 462 | f[ae] = ((C[(am + 768) >> 0] + C[(ax + 1024) >> 0] + C[(az + 1280) >> 0]) >> 16) - 128; 463 | U[ae] = ((C[(am + 1280) >> 0] + C[(ax + 1536) >> 0] + C[(az + 1792) >> 0]) >> 16) - 128 464 | } 465 | al = L(A, d, al, u, G); 466 | aq = L(f, Z, aq, h, T); 467 | ao = L(U, Z, ao, h, T); 468 | ah += 32 469 | } 470 | ag += 8 471 | } 472 | if (a >= 0) { 473 | var aw = []; 474 | aw[1] = a + 1; 475 | aw[0] = (1 << (a + 1)) - 1; 476 | X(aw) 477 | } 478 | p(65497); 479 | if (aB) { 480 | var av = j.length; 481 | var aC = new Uint8Array(av); 482 | for (var au = 0; au < av; au++) { 483 | aC[au] = j[au].charCodeAt() 484 | } 485 | j = []; 486 | var ak = new Date().getTime() - aa; 487 | return aC 488 | } 489 | var ad = "data:image/jpeg;base64," + btoa(j.join("")); 490 | j = []; 491 | var ak = new Date().getTime() - aa; 492 | return ad 493 | }; 494 | function R (ab) { 495 | if (ab <= 0) { 496 | ab = 1 497 | } 498 | if (ab > 100) { 499 | ab = 100 500 | } 501 | if (x == ab) { 502 | return 503 | } 504 | var aa = 0; 505 | if (ab < 50) { 506 | aa = Math.floor(5000 / ab) 507 | } else { 508 | aa = Math.floor(200 - ab * 2) 509 | } 510 | M(aa); 511 | x = ab; 512 | } 513 | 514 | function Q () { 515 | var aa = new Date().getTime(); 516 | if (!l) { 517 | l = 50 518 | } 519 | y(); 520 | W(); 521 | z(); 522 | V(); 523 | R(l); 524 | var ab = new Date().getTime() - aa; 525 | } 526 | 527 | Q() 528 | } 529 | 530 | module.exports = JPEGEncoder; -------------------------------------------------------------------------------- /src/lib/megapix-image.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Mega pixel image rendering library for iOS6 Safari 3 | * 4 | * Fixes iOS6 Safari's image file rendering issue for large size image (over mega-pixel), 5 | * which causes unexpected subsampling when drawing it in canvas. 6 | * By using this library, you can safely render the image with proper stretching. 7 | * 8 | * Copyright (c) 2012 Shinichi Tomita 9 | * Released under the MIT license 10 | */ 11 | (function () { 12 | 13 | /** 14 | * Detect subsampling in loaded image. 15 | * In iOS, larger images than 2M pixels may be subsampled in rendering. 16 | */ 17 | function detectSubsampling (img) { 18 | var iw = img.naturalWidth, ih = img.naturalHeight; 19 | if (iw * ih > 1024 * 1024) { // subsampling may happen over megapixel image 20 | var canvas = document.createElement('canvas'); 21 | canvas.width = canvas.height = 1; 22 | var ctx = canvas.getContext('2d'); 23 | ctx.drawImage(img, -iw + 1, 0); 24 | // subsampled image becomes half smaller in rendering size. 25 | // check alpha channel value to confirm image is covering edge pixel or not. 26 | // if alpha value is 0 image is not covering, hence subsampled. 27 | return ctx.getImageData(0, 0, 1, 1).data[3] === 0; 28 | } else { 29 | return false; 30 | } 31 | } 32 | 33 | /** 34 | * Detecting vertical squash in loaded image. 35 | * Fixes a bug which squash image vertically while drawing into canvas for some images. 36 | */ 37 | function detectVerticalSquash (img, iw, ih) { 38 | var canvas = document.createElement('canvas'); 39 | canvas.width = 1; 40 | canvas.height = ih; 41 | var ctx = canvas.getContext('2d'); 42 | ctx.drawImage(img, 0, 0); 43 | var data = ctx.getImageData(0, 0, 1, ih).data; 44 | // search image edge pixel position in case it is squashed vertically. 45 | var sy = 0; 46 | var ey = ih; 47 | var py = ih; 48 | while (py > sy) { 49 | var alpha = data[(py - 1) * 4 + 3]; 50 | if (alpha === 0) { 51 | ey = py; 52 | } else { 53 | sy = py; 54 | } 55 | py = (ey + sy) >> 1; 56 | } 57 | var ratio = (py / ih); 58 | return (ratio === 0) ? 1 : ratio; 59 | } 60 | 61 | /** 62 | * Rendering image element (with resizing) and get its data URL 63 | */ 64 | function renderImageToDataURL (img, options, doSquash) { 65 | var canvas = document.createElement('canvas'); 66 | renderImageToCanvas(img, canvas, options, doSquash); 67 | return canvas.toDataURL("image/jpeg", options.quality || 0.8); 68 | } 69 | 70 | /** 71 | * Rendering image element (with resizing) into the canvas element 72 | */ 73 | function renderImageToCanvas (img, canvas, options, doSquash) { 74 | var iw = img.naturalWidth, ih = img.naturalHeight; 75 | var width = options.width, height = options.height; 76 | var ctx = canvas.getContext('2d'); 77 | ctx.save(); 78 | transformCoordinate(canvas, ctx, width, height, options.orientation); 79 | var subsampled = detectSubsampling(img); 80 | if (subsampled) { 81 | iw /= 2; 82 | ih /= 2; 83 | } 84 | var d = 1024; // size of tiling canvas 85 | var tmpCanvas = document.createElement('canvas'); 86 | tmpCanvas.width = tmpCanvas.height = d; 87 | var tmpCtx = tmpCanvas.getContext('2d'); 88 | var vertSquashRatio = doSquash ? detectVerticalSquash(img, iw, ih) : 1; 89 | var dw = Math.ceil(d * width / iw); 90 | var dh = Math.ceil(d * height / ih / vertSquashRatio); 91 | var sy = 0; 92 | var dy = 0; 93 | while (sy < ih) { 94 | var sx = 0; 95 | var dx = 0; 96 | while (sx < iw) { 97 | tmpCtx.clearRect(0, 0, d, d); 98 | tmpCtx.drawImage(img, -sx, -sy); 99 | ctx.drawImage(tmpCanvas, 0, 0, d, d, dx, dy, dw, dh); 100 | sx += d; 101 | dx += dw; 102 | } 103 | sy += d; 104 | dy += dh; 105 | } 106 | ctx.restore(); 107 | tmpCanvas = tmpCtx = null; 108 | } 109 | 110 | /** 111 | * Transform canvas coordination according to specified frame size and orientation 112 | * Orientation value is from EXIF tag 113 | */ 114 | function transformCoordinate (canvas, ctx, width, height, orientation) { 115 | switch (orientation) { 116 | case 5: 117 | case 6: 118 | case 7: 119 | case 8: 120 | canvas.width = height; 121 | canvas.height = width; 122 | break; 123 | default: 124 | canvas.width = width; 125 | canvas.height = height; 126 | } 127 | switch (orientation) { 128 | case 2: 129 | // horizontal flip 130 | ctx.translate(width, 0); 131 | ctx.scale(-1, 1); 132 | break; 133 | case 3: 134 | // 180 rotate left 135 | ctx.translate(width, height); 136 | ctx.rotate(Math.PI); 137 | break; 138 | case 4: 139 | // vertical flip 140 | ctx.translate(0, height); 141 | ctx.scale(1, -1); 142 | break; 143 | case 5: 144 | // vertical flip + 90 rotate right 145 | ctx.rotate(0.5 * Math.PI); 146 | ctx.scale(1, -1); 147 | break; 148 | case 6: 149 | // 90 rotate right 150 | ctx.rotate(0.5 * Math.PI); 151 | ctx.translate(0, -height); 152 | break; 153 | case 7: 154 | // horizontal flip + 90 rotate right 155 | ctx.rotate(0.5 * Math.PI); 156 | ctx.translate(width, -height); 157 | ctx.scale(-1, 1); 158 | break; 159 | case 8: 160 | // 90 rotate left 161 | ctx.rotate(-0.5 * Math.PI); 162 | ctx.translate(-width, 0); 163 | break; 164 | default: 165 | break; 166 | } 167 | } 168 | 169 | 170 | /** 171 | * MegaPixImage class 172 | */ 173 | function MegaPixImage (srcImage) { 174 | if (window.Blob && srcImage instanceof Blob) { 175 | var img = new Image(); 176 | var URL = window.URL && window.URL.createObjectURL ? window.URL : 177 | window.webkitURL && window.webkitURL.createObjectURL ? window.webkitURL : 178 | null; 179 | if (!URL) { 180 | throw Error("No createObjectURL function found to create blob url"); 181 | } 182 | img.src = URL.createObjectURL(srcImage); 183 | this.blob = srcImage; 184 | srcImage = img; 185 | } 186 | if (!srcImage.naturalWidth && !srcImage.naturalHeight) { 187 | var _this = this; 188 | srcImage.onload = function () { 189 | var listeners = _this.imageLoadListeners; 190 | if (listeners) { 191 | _this.imageLoadListeners = null; 192 | for (var i = 0, len = listeners.length; i < len; i++) { 193 | listeners[i](); 194 | } 195 | } 196 | }; 197 | this.imageLoadListeners = []; 198 | } 199 | this.srcImage = srcImage; 200 | } 201 | 202 | /** 203 | * Rendering megapix image into specified target element 204 | */ 205 | MegaPixImage.prototype.render = function (target, options, callback) { 206 | if (this.imageLoadListeners) { 207 | var _this = this; 208 | this.imageLoadListeners.push(function () { 209 | _this.render(target, options, callback); 210 | }); 211 | return; 212 | } 213 | options = options || {}; 214 | var srcImage = this.srcImage, 215 | src = srcImage.src, 216 | srcLength = src.length, 217 | imgWidth = srcImage.naturalWidth, imgHeight = srcImage.naturalHeight, 218 | width = options.width, height = options.height, 219 | maxWidth = options.maxWidth, maxHeight = options.maxHeight, 220 | doSquash = this.blob && this.blob.type === 'image/jpeg' || 221 | src.indexOf('data:image/jpeg') === 0 || 222 | src.indexOf('.jpg') === srcLength - 4 || 223 | src.indexOf('.jpeg') === srcLength - 5; 224 | if (width && !height) { 225 | height = (imgHeight * width / imgWidth) << 0; 226 | } else if (height && !width) { 227 | width = (imgWidth * height / imgHeight) << 0; 228 | } else { 229 | width = imgWidth; 230 | height = imgHeight; 231 | } 232 | if (maxWidth && width > maxWidth) { 233 | width = maxWidth; 234 | height = (imgHeight * width / imgWidth) << 0; 235 | } 236 | if (maxHeight && height > maxHeight) { 237 | height = maxHeight; 238 | width = (imgWidth * height / imgHeight) << 0; 239 | } 240 | var opt = {width: width, height: height}; 241 | for (var k in options) opt[k] = options[k]; 242 | 243 | var tagName = target.tagName.toLowerCase(); 244 | if (tagName === 'img') { 245 | target.src = renderImageToDataURL(this.srcImage, opt, doSquash); 246 | } else if (tagName === 'canvas') { 247 | renderImageToCanvas(this.srcImage, target, opt, doSquash); 248 | } 249 | if (typeof this.onrender === 'function') { 250 | this.onrender(target); 251 | } 252 | if (callback) { 253 | callback(); 254 | } 255 | }; 256 | 257 | /** 258 | * Export class to global 259 | */ 260 | if (typeof define === 'function' && define.amd) { 261 | define([], function () { 262 | return MegaPixImage; 263 | }); // for AMD loader 264 | } else { 265 | this.MegaPixImage = MegaPixImage; 266 | } 267 | 268 | })(); 269 | -------------------------------------------------------------------------------- /src/lrz.all.js: -------------------------------------------------------------------------------- 1 | require('megapix-image'); 2 | require('jpeg_encoder_basic'); 3 | 4 | module.exports = require('lrz'); 5 | 6 | 7 | /** 8 | * 9 | *    ┏┓   ┏┓ 10 | *   ┏┛┻━━━┛┻┓ 11 | *   ┃       ┃ 12 | *   ┃   ━   ┃ 13 | *   ┃ ┳┛ ┗┳ ┃ 14 | *   ┃       ┃ 15 | *   ┃   ┻   ┃ 16 | *   ┃       ┃ 17 | *   ┗━┓   ┏━┛Code is far away from bug with the animal protecting 18 | *     ┃   ┃ 神兽保佑,代码无bug 19 | *     ┃   ┃ 20 | *     ┃   ┗━━━┓ 21 | *     ┃      ┣┓ 22 | *     ┃     ┏┛ 23 | *     ┗┓┓┏━┳┓┏┛ 24 | *      ┃┫┫ ┃┫┫ 25 | *      ┗┻┛ ┗┻┛ 26 | * 27 | */ 28 | -------------------------------------------------------------------------------- /src/lrz.js: -------------------------------------------------------------------------------- 1 | // 保证按需加载的文件路径正确 2 | __webpack_public_path__ = getJsDir('lrz') + '/'; 3 | window.URL = window.URL || window.webkitURL; 4 | 5 | var Promise = require('Promise'), 6 | BlobFormDataShim = require('Blob.FormData.shim'), 7 | exif = require('exif'); 8 | 9 | 10 | var UA = (function (userAgent) { 11 | var ISOldIOS = /OS (.*) like Mac OS X/g.exec(userAgent), 12 | isOldAndroid = /Android (\d.*?);/g.exec(userAgent) || /Android\/(\d.*?) /g.exec(userAgent); 13 | 14 | // 判断设备是否是IOS7以下 15 | // 判断设备是否是android4.5以下 16 | // 判断是否iOS 17 | // 判断是否android 18 | // 判断是否QQ浏览器 19 | var IOS_VERSION = ISOldIOS ? +ISOldIOS.pop().replace(/_/g, '.') : 0 20 | return { 21 | oldIOS : ISOldIOS ? IOS_VERSION < 8 : false, 22 | newIOS : ISOldIOS ? IOS_VERSION >= 13.4 : false, 23 | oldAndroid: isOldAndroid ? +isOldAndroid.pop().substr(0, 3) < 4.5 : false, 24 | iOS : /\(i[^;]+;( U;)? CPU.+Mac OS X/.test(userAgent), 25 | android : /Android/g.test(userAgent), 26 | mQQBrowser: /MQQBrowser/g.test(userAgent) 27 | } 28 | })(navigator.userAgent); 29 | 30 | 31 | function Lrz (file, opts) { 32 | var that = this; 33 | 34 | if (!file) throw new Error('没有收到图片,可能的解决方案:https://github.com/think2011/localResizeIMG/issues/7'); 35 | 36 | opts = opts || {}; 37 | 38 | that.defaults = { 39 | width : null, 40 | height : null, 41 | fieldName: 'file', 42 | ingnoreOrientation: UA.iOS ? UA.newIOS : true, 43 | quality : 0.7 44 | }; 45 | 46 | that.file = file; 47 | 48 | for (var p in opts) { 49 | if (!opts.hasOwnProperty(p)) continue; 50 | that.defaults[p] = opts[p]; 51 | } 52 | 53 | return this.init(); 54 | } 55 | 56 | Lrz.prototype.init = function () { 57 | var that = this, 58 | file = that.file, 59 | fileIsString = typeof file === 'string', 60 | fileIsBase64 = /^data:/.test(file), 61 | img = new Image(), 62 | canvas = document.createElement('canvas'), 63 | blob = fileIsString ? file : URL.createObjectURL(file); 64 | 65 | that.img = img; 66 | that.blob = blob; 67 | that.canvas = canvas; 68 | 69 | if (fileIsString) { 70 | that.fileName = fileIsBase64 ? 'base64.jpg' : (file.split('/').pop()); 71 | } else { 72 | that.fileName = file.name; 73 | } 74 | 75 | if (!document.createElement('canvas').getContext) { 76 | throw new Error('浏览器不支持canvas'); 77 | } 78 | 79 | return new Promise(function (resolve, reject) { 80 | img.onerror = function () { 81 | var err = new Error('加载图片文件失败'); 82 | reject(err); 83 | throw err; 84 | }; 85 | 86 | img.onload = function () { 87 | that._getBase64() 88 | .then(function (base64) { 89 | if (base64.length < 10) { 90 | var err = new Error('生成base64失败'); 91 | reject(err); 92 | throw err; 93 | } 94 | 95 | return base64; 96 | }) 97 | .then(function (base64) { 98 | var formData = null; 99 | 100 | // 压缩文件太大就采用源文件,且使用原生的FormData() @source #55 101 | if (typeof that.file === 'object' && base64.length > that.file.size) { 102 | formData = new FormData(); 103 | file = that.file; 104 | } else { 105 | formData = new BlobFormDataShim.FormData(); 106 | file = dataURItoBlob(base64); 107 | } 108 | 109 | formData.append(that.defaults.fieldName, file, (that.fileName.replace(/\..+/g, '.jpg'))); 110 | 111 | resolve({ 112 | formData : formData, 113 | fileLen : +file.size, 114 | base64 : base64, 115 | base64Len: base64.length, 116 | origin : that.file, 117 | file : file 118 | }); 119 | 120 | // 释放内存 121 | for (var p in that) { 122 | if (!that.hasOwnProperty(p)) continue; 123 | 124 | that[p] = null; 125 | } 126 | URL.revokeObjectURL(that.blob); 127 | }); 128 | }; 129 | 130 | // 如果传入的是base64在移动端会报错 131 | !fileIsBase64 && (img.crossOrigin = "*"); 132 | 133 | img.src = blob; 134 | }); 135 | }; 136 | 137 | Lrz.prototype._getBase64 = function () { 138 | var that = this, 139 | img = that.img, 140 | file = that.file, 141 | canvas = that.canvas; 142 | 143 | return new Promise(function (resolve) { 144 | try { 145 | // 传入blob在android4.3以下有bug 146 | exif.getData(typeof file === 'object' ? file : img, function () { 147 | that.orientation = that.defaults.ingnoreOrientation ? 0 : exif.getTag(this, "Orientation"); 148 | 149 | that.resize = that._getResize(); 150 | that.ctx = canvas.getContext('2d'); 151 | 152 | canvas.width = that.resize.width; 153 | canvas.height = that.resize.height; 154 | 155 | // 设置为白色背景,jpg是不支持透明的,所以会被默认为canvas默认的黑色背景。 156 | that.ctx.fillStyle = '#fff'; 157 | that.ctx.fillRect(0, 0, canvas.width, canvas.height); 158 | 159 | // 根据设备对应处理方式 160 | if (UA.oldIOS) { 161 | that._createBase64ForOldIOS().then(resolve); 162 | } 163 | else { 164 | that._createBase64().then(resolve); 165 | } 166 | }); 167 | } catch (err) { 168 | // 这样能解决低内存设备闪退的问题吗? 169 | throw new Error(err); 170 | } 171 | }); 172 | }; 173 | 174 | 175 | Lrz.prototype._createBase64ForOldIOS = function () { 176 | var that = this, 177 | img = that.img, 178 | canvas = that.canvas, 179 | defaults = that.defaults, 180 | orientation = that.orientation; 181 | 182 | return new Promise(function (resolve) { 183 | require(['megapix-image'], function (MegaPixImage) { 184 | var mpImg = new MegaPixImage(img); 185 | 186 | if ("5678".indexOf(orientation) > -1) { 187 | mpImg.render(canvas, { 188 | width : canvas.height, 189 | height : canvas.width, 190 | orientation: orientation 191 | }); 192 | } else { 193 | mpImg.render(canvas, { 194 | width : canvas.width, 195 | height : canvas.height, 196 | orientation: orientation 197 | }); 198 | } 199 | 200 | resolve(canvas.toDataURL('image/jpeg', defaults.quality)); 201 | }); 202 | }); 203 | }; 204 | 205 | Lrz.prototype._createBase64 = function () { 206 | var that = this, 207 | resize = that.resize, 208 | img = that.img, 209 | canvas = that.canvas, 210 | ctx = that.ctx, 211 | defaults = that.defaults, 212 | orientation = that.orientation; 213 | 214 | // 调整为正确方向 215 | switch (orientation) { 216 | case 3: 217 | ctx.rotate(180 * Math.PI / 180); 218 | ctx.drawImage(img, -resize.width, -resize.height, resize.width, resize.height); 219 | break; 220 | case 6: 221 | ctx.rotate(90 * Math.PI / 180); 222 | ctx.drawImage(img, 0, -resize.width, resize.height, resize.width); 223 | break; 224 | case 8: 225 | ctx.rotate(270 * Math.PI / 180); 226 | ctx.drawImage(img, -resize.height, 0, resize.height, resize.width); 227 | break; 228 | 229 | case 2: 230 | ctx.translate(resize.width, 0); 231 | ctx.scale(-1, 1); 232 | ctx.drawImage(img, 0, 0, resize.width, resize.height); 233 | break; 234 | case 4: 235 | ctx.translate(resize.width, 0); 236 | ctx.scale(-1, 1); 237 | ctx.rotate(180 * Math.PI / 180); 238 | ctx.drawImage(img, -resize.width, -resize.height, resize.width, resize.height); 239 | break; 240 | case 5: 241 | ctx.translate(resize.width, 0); 242 | ctx.scale(-1, 1); 243 | ctx.rotate(90 * Math.PI / 180); 244 | ctx.drawImage(img, 0, -resize.width, resize.height, resize.width); 245 | break; 246 | case 7: 247 | ctx.translate(resize.width, 0); 248 | ctx.scale(-1, 1); 249 | ctx.rotate(270 * Math.PI / 180); 250 | ctx.drawImage(img, -resize.height, 0, resize.height, resize.width); 251 | break; 252 | 253 | default: 254 | ctx.drawImage(img, 0, 0, resize.width, resize.height); 255 | } 256 | 257 | return new Promise(function (resolve) { 258 | if (UA.oldAndroid || UA.mQQBrowser || !navigator.userAgent) { 259 | require(['jpeg_encoder_basic'], function (JPEGEncoder) { 260 | var encoder = new JPEGEncoder(), 261 | img = ctx.getImageData(0, 0, canvas.width, canvas.height); 262 | 263 | resolve(encoder.encode(img, defaults.quality * 100)); 264 | }) 265 | } 266 | else { 267 | resolve(canvas.toDataURL('image/jpeg', defaults.quality)); 268 | } 269 | }); 270 | }; 271 | 272 | Lrz.prototype._getResize = function () { 273 | var that = this, 274 | img = that.img, 275 | defaults = that.defaults, 276 | width = defaults.width, 277 | height = defaults.height, 278 | orientation = that.orientation; 279 | 280 | var ret = { 281 | width : img.width, 282 | height: img.height 283 | }; 284 | 285 | if ("5678".indexOf(orientation) > -1) { 286 | ret.width = img.height; 287 | ret.height = img.width; 288 | } 289 | 290 | // 如果原图小于设定,采用原图 291 | if (ret.width < width || ret.height < height) { 292 | return ret; 293 | } 294 | 295 | var scale = ret.width / ret.height; 296 | 297 | if (width && height) { 298 | if (scale >= width / height) { 299 | if (ret.width > width) { 300 | ret.width = width; 301 | ret.height = Math.ceil(width / scale); 302 | } 303 | } else { 304 | if (ret.height > height) { 305 | ret.height = height; 306 | ret.width = Math.ceil(height * scale); 307 | } 308 | } 309 | } 310 | else if (width) { 311 | if (width < ret.width) { 312 | ret.width = width; 313 | ret.height = Math.ceil(width / scale); 314 | } 315 | } 316 | else if (height) { 317 | if (height < ret.height) { 318 | ret.width = Math.ceil(height * scale); 319 | ret.height = height; 320 | } 321 | } 322 | 323 | // 超过这个值base64无法生成,在IOS上 324 | while (ret.width >= 3264 || ret.height >= 2448) { 325 | ret.width *= 0.8; 326 | ret.height *= 0.8; 327 | } 328 | 329 | return ret; 330 | }; 331 | 332 | /** 333 | * 获取当前js文件所在路径,必须得在代码顶部执行此函数 334 | * @returns {string} 335 | */ 336 | function getJsDir (src) { 337 | var script = null; 338 | 339 | if (src) { 340 | script = [].filter.call(document.scripts, function (v) { 341 | return v.src.indexOf(src) !== -1; 342 | })[0]; 343 | } else { 344 | script = document.scripts[document.scripts.length - 1]; 345 | } 346 | 347 | if (!script) return null; 348 | 349 | return script.src.substr(0, script.src.lastIndexOf('/')); 350 | } 351 | 352 | 353 | /** 354 | * 转换成formdata 355 | * @param dataURI 356 | * @returns {*} 357 | * 358 | * @source http://stackoverflow.com/questions/4998908/convert-data-uri-to-file-then-append-to-formdata 359 | */ 360 | function dataURItoBlob (dataURI) { 361 | // convert base64/URLEncoded data component to raw binary data held in a string 362 | var byteString; 363 | if (dataURI.split(',')[0].indexOf('base64') >= 0) 364 | byteString = atob(dataURI.split(',')[1]); 365 | else 366 | byteString = unescape(dataURI.split(',')[1]); 367 | 368 | // separate out the mime component 369 | var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; 370 | 371 | // write the bytes of the string to a typed array 372 | var ia = new Uint8Array(byteString.length); 373 | for (var i = 0; i < byteString.length; i++) { 374 | ia[i] = byteString.charCodeAt(i); 375 | } 376 | 377 | return new BlobFormDataShim.Blob([ia.buffer], {type: mimeString}); 378 | } 379 | 380 | window.lrz = function (file, opts) { 381 | return new Lrz(file, opts); 382 | }; 383 | 384 | // 版本号来自package.json,构建时自动填充 385 | window.lrz.version = '__packageJSON.version__'; 386 | 387 | module.exports = window.lrz; 388 | 389 | /** 390 | * 391 | *    ┏┓   ┏┓ 392 | *   ┏┛┻━━━┛┻┓ 393 | *   ┃       ┃ 394 | *   ┃   ━   ┃ 395 | *   ┃ ┳┛ ┗┳ ┃ 396 | *   ┃       ┃ 397 | *   ┃   ┻   ┃ 398 | *   ┃       ┃ 399 | *   ┗━┓   ┏━┛Code is far away from bug with the animal protecting 400 | *     ┃   ┃ 神兽保佑,代码无bug 401 | *     ┃   ┃ 402 | *     ┃   ┗━━━┓ 403 | *     ┃      ┣┓ 404 | *     ┃     ┏┛ 405 | *     ┗┓┓┏━┳┓┏┛ 406 | *      ┃┫┫ ┃┫┫ 407 | *      ┗┻┛ ┗┻┛ 408 | * 409 | */ 410 | 411 | 412 | -------------------------------------------------------------------------------- /src/那么多文件,应该引用哪个?.txt: -------------------------------------------------------------------------------- 1 | 1. 一般情况仅需引用 【lrz.bundle.js】 即可。 2 | 但绝对不要删除目录下的【*.chunk.js】,这些文件分别对应了IOS和Android的兼容代码,检测到符合环境时会自动引入。 3 | 4 | 2. 【lrz.all.bundle.js】是包含了所有引用了,莫名其妙的问题下就引用这个吧。 5 | 例如:https://github.com/think2011/localResizeIMG/issues/6 6 | 7 | 3. 【*.map】文件是供调试用的,正式使用删不删除都没关系,因为仅在调试时才会载入。 8 | -------------------------------------------------------------------------------- /test/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/think2011/localResizeIMG/8248c3d327723abd70ec38a6cf0268d2a4f6c486/test/demo.gif -------------------------------------------------------------------------------- /test/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/think2011/localResizeIMG/8248c3d327723abd70ec38a6cf0268d2a4f6c486/test/img/loading.gif -------------------------------------------------------------------------------- /test/img/orientation_1.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/think2011/localResizeIMG/8248c3d327723abd70ec38a6cf0268d2a4f6c486/test/img/orientation_1.JPG -------------------------------------------------------------------------------- /test/img/orientation_3.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/think2011/localResizeIMG/8248c3d327723abd70ec38a6cf0268d2a4f6c486/test/img/orientation_3.JPG -------------------------------------------------------------------------------- /test/img/orientation_6.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/think2011/localResizeIMG/8248c3d327723abd70ec38a6cf0268d2a4f6c486/test/img/orientation_6.JPG -------------------------------------------------------------------------------- /test/img/orientation_8.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/think2011/localResizeIMG/8248c3d327723abd70ec38a6cf0268d2a4f6c486/test/img/orientation_8.JPG -------------------------------------------------------------------------------- /test/img/transparent_png.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/think2011/localResizeIMG/8248c3d327723abd70ec38a6cf0268d2a4f6c486/test/img/transparent_png.png -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | lrz4 demo&test 7 | 8 | 9 | 22 | 23 | 24 | 34 | 35 |
36 | 37 |
38 |
39 |

上传图片测试

40 | 切换至带服务端演示 41 |
42 | 配置:宽度不超过800,高度适应,70%压缩率 43 |
44 | UA 45 |
46 |
47 | 48 |
49 | 50 |
51 | 52 |
53 |
54 |

旋转方向测试

55 | 看到的图像应该全是一个方向的,没见到图片是出问题了 56 |
57 |
58 | 59 |
60 | 点击载入 62 | 63 |

方向为【1】的图片

64 |
65 |
66 | 点击载入 68 | 69 |

方向为【3】的图片

70 |
71 |
72 | 点击载入 74 | 75 |

方向为【6】的图片

76 |
77 |
78 | 点击载入 80 | 81 |

方向为【8】的图片

82 |
83 |
84 |
85 | 86 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | window.onerror = function (errMsg, scriptURI, lineNumber, columnNumber, errorObj) { 2 | setTimeout(function () { 3 | var rst = { 4 | "错误信息:": errMsg, 5 | "出错文件:": scriptURI, 6 | "出错行号:": lineNumber, 7 | "出错列号:": columnNumber, 8 | "错误详情:": errorObj 9 | }; 10 | 11 | alert('出错了,下一步将显示错误信息'); 12 | alert(JSON.stringify(rst, null, 10)); 13 | }); 14 | }; 15 | 16 | 17 | [].forEach.call(document.querySelectorAll('[data-src]'), function (el) { 18 | (function (el) { 19 | el.addEventListener('click', function () { 20 | el.src = 'img/loading.gif'; 21 | 22 | lrz(el.dataset.src) 23 | .then(function (rst) { 24 | el.src = rst.base64; 25 | 26 | 27 | return rst; 28 | }); 29 | }); 30 | 31 | fireEvent(el, 'click'); 32 | })(el); 33 | }); 34 | 35 | 36 | document.querySelector('input').addEventListener('change', function () { 37 | var that = this; 38 | 39 | lrz(that.files[0], { 40 | width: 800 41 | }) 42 | .then(function (rst) { 43 | var img = new Image(), 44 | div = document.createElement('div'), 45 | p = document.createElement('p'), 46 | sourceSize = toFixed2(that.files[0].size / 1024), 47 | resultSize = toFixed2(rst.fileLen / 1024), 48 | scale = parseInt(100 - (resultSize / sourceSize * 100)); 49 | 50 | p.style.fontSize = 13 + 'px'; 51 | p.innerHTML = '源文件:' + 52 | sourceSize + 'KB' + 53 | '
' + 54 | '压缩后传输大小:' + 55 | resultSize + 'KB (省' + scale + '%)' + 56 | ' '; 57 | 58 | div.className = 'col-sm-6'; 59 | div.appendChild(img); 60 | div.appendChild(p); 61 | 62 | img.onload = function () { 63 | document.querySelector('#upload-container').appendChild(div); 64 | }; 65 | 66 | img.src = rst.base64; 67 | 68 | /* /!* ==================================================== *!/ 69 | // 原生ajax上传代码,所以看起来特别多 ╮(╯_╰)╭,但绝对能用 70 | // 其他框架,例如ajax处理formData略有不同,请自行google,baidu。 71 | var xhr = new XMLHttpRequest(); 72 | xhr.open('POST', '/upload'); 73 | 74 | xhr.onload = function () { 75 | if (xhr.status === 200) { 76 | // 上传成功 77 | } else { 78 | // 处理其他情况 79 | } 80 | }; 81 | 82 | xhr.onerror = function () { 83 | // 处理错误 84 | }; 85 | 86 | // issues #45 提到似乎有兼容性问题,关于progress 87 | xhr.upload.onprogress = function (e) { 88 | // 上传进度 89 | var percentComplete = ((e.loaded / e.total) || 0) * 100; 90 | }; 91 | 92 | // 添加参数和触发上传 93 | rst.formData.append('a', '我是参数'); 94 | xhr.send(rst.formData); 95 | /!* ==================================================== *!/*/ 96 | 97 | return rst; 98 | }); 99 | }); 100 | 101 | document.querySelector('#version').innerHTML = lrz.version; 102 | document.querySelector('.UA').innerHTML = 'UA: ' + navigator.userAgent; 103 | 104 | function toFixed2 (num) { 105 | return parseFloat(+num.toFixed(2)); 106 | } 107 | 108 | /** 109 | * 替换字符串 !{} 110 | * @param obj 111 | * @returns {String} 112 | * @example 113 | * '我是!{str}'.render({str: '测试'}); 114 | */ 115 | String.prototype.render = function (obj) { 116 | var str = this, reg; 117 | 118 | Object.keys(obj).forEach(function (v) { 119 | reg = new RegExp('\\!\\{' + v + '\\}', 'g'); 120 | str = str.replace(reg, obj[v]); 121 | }); 122 | 123 | return str; 124 | }; 125 | 126 | /** 127 | * 触发事件 - 只是为了兼容演示demo而已 128 | * @param element 129 | * @param event 130 | * @returns {boolean} 131 | */ 132 | function fireEvent (element, event) { 133 | var evt; 134 | 135 | if (document.createEventObject) { 136 | // IE浏览器支持fireEvent方法 137 | evt = document.createEventObject(); 138 | return element.fireEvent('on' + event, evt) 139 | } 140 | else { 141 | // 其他标准浏览器使用dispatchEvent方法 142 | evt = document.createEvent('HTMLEvents'); 143 | // initEvent接受3个参数: 144 | // 事件类型,是否冒泡,是否阻止浏览器的默认行为 145 | evt.initEvent(event, true, true); 146 | return !element.dispatchEvent(evt); 147 | } 148 | } 149 | 150 | /** 151 | * 152 | *    ┏┓   ┏┓ 153 | *   ┏┛┻━━━┛┻┓ 154 | *   ┃       ┃ 155 | *   ┃   ━   ┃ 156 | *   ┃ ┳┛ ┗┳ ┃ 157 | *   ┃       ┃ 158 | *   ┃   ┻   ┃ 159 | *   ┃       ┃ 160 | *   ┗━┓   ┏━┛Code is far away from bug with the animal protecting 161 | *     ┃   ┃ 神兽保佑,代码无bug 162 | *     ┃   ┃ 163 | *     ┃   ┗━━━┓ 164 | *     ┃      ┣┓ 165 | *     ┃     ┏┛ 166 | *     ┗┓┓┏━┳┓┏┛ 167 | *      ┃┫┫ ┃┫┫ 168 | *      ┗┻┛ ┗┻┛ 169 | * 170 | */ 171 | -------------------------------------------------------------------------------- /test/lrz.spec.js: -------------------------------------------------------------------------------- 1 | var lrz = require('lrz'); 2 | var expect = chai.expect; 3 | 4 | describe("测试压缩正常运作", function () { 5 | 6 | it("基本例子:传入路径", function () { 7 | return lrz('base/test/img/transparent_png.png') 8 | .then(function (rst) { 9 | expect(rst).to.all.keys('formData', 'origin', 'base64', 'fileLen', 'base64Len', 'file'); 10 | }); 11 | }); 12 | 13 | it("基本例子:传入base64", function () { 14 | var base64 = 'data:image/png;base64,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'; 15 | 16 | return lrz(base64) 17 | .then(function (rst) { 18 | expect(rst).to.all.keys('formData', 'origin', 'base64', 'fileLen', 'base64Len', 'file'); 19 | }); 20 | }); 21 | }); -------------------------------------------------------------------------------- /test/qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/think2011/localResizeIMG/8248c3d327723abd70ec38a6cf0268d2a4f6c486/test/qrcode.png -------------------------------------------------------------------------------- /test/server.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | lrz4 demo&test 7 | 8 | 9 | 22 | 23 | 24 | 34 | 35 |
36 |
37 |
38 |

上传图片测试

39 | 切换至无服务端演示 40 |
41 | 42 |
43 | 44 | 配置:宽度不超过800,高度适应,70%压缩率 45 |
46 | 演示服务端是跨域上传且免费的,所以较慢,请耐心等待.. 47 |
48 | UA 49 |
50 |
51 | 52 |
53 | 54 |
55 | 56 |
57 |
58 |

旋转方向测试

59 | 看到的图像应该全是一个方向的,没见到图片是出问题了 60 |
61 |
62 | 63 |
64 | 点击载入 66 | 67 |

方向为【1】的图片

68 |
69 |
70 | 点击载入 72 | 73 |

方向为【3】的图片

74 |
75 |
76 | 点击载入 78 | 79 |

方向为【6】的图片

80 |
81 |
82 | 点击载入 84 | 85 |

方向为【8】的图片

86 |
87 |
88 |
89 | 90 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /test/server.js: -------------------------------------------------------------------------------- 1 | window.onerror = function (errMsg, scriptURI, lineNumber, columnNumber, errorObj) { 2 | setTimeout(function () { 3 | var rst = { 4 | "错误信息:": errMsg, 5 | "出错文件:": scriptURI, 6 | "出错行号:": lineNumber, 7 | "出错列号:": columnNumber, 8 | "错误详情:": errorObj 9 | }; 10 | 11 | alert('出错了,下一步将显示错误信息'); 12 | alert(JSON.stringify(rst, null, 10)); 13 | }); 14 | }; 15 | 16 | 17 | [].forEach.call(document.querySelectorAll('[data-src]'), function (el) { 18 | (function (el) { 19 | el.addEventListener('click', function () { 20 | el.src = 'img/loading.gif'; 21 | 22 | lrz(el.dataset.src) 23 | .then(function (rst) { 24 | el.src = rst.base64; 25 | 26 | 27 | return rst; 28 | }); 29 | }); 30 | 31 | fireEvent(el, 'click'); 32 | })(el); 33 | }); 34 | 35 | 36 | document.querySelector('input').addEventListener('change', function () { 37 | var that = this, 38 | progress = document.querySelector('progress'); 39 | 40 | lrz(that.files[0], { 41 | width: 800 42 | }) 43 | .then(function (rst) { 44 | var img = new Image(), 45 | div = document.createElement('div'), 46 | p = document.createElement('p'), 47 | sourceSize = toFixed2(that.files[0].size / 1024), 48 | resultSize = toFixed2(rst.fileLen / 1024), 49 | effect = parseInt(100 - (resultSize / sourceSize * 100)); 50 | 51 | p.style.fontSize = 13 + 'px'; 52 | p.innerHTML = '源文件:!{sourceSize}KB
压缩后传输大小:!{resultSize}KB (省!{effect}%) '.render({ 53 | sourceSize: sourceSize, 54 | resultSize: resultSize, 55 | effect : effect 56 | }); 57 | 58 | div.className = 'col-sm-6'; 59 | div.appendChild(img); 60 | div.appendChild(p); 61 | 62 | img.onload = function () { 63 | document.querySelector('#upload-container').appendChild(div); 64 | }; 65 | 66 | progress.value = 0; 67 | 68 | /* ==================================================== */ 69 | // 原生ajax上传代码,所以看起来特别多 ╮(╯_╰)╭,但绝对能用 70 | // 其他框架,例如ajax处理formData略有不同,请自行google,baidu。 71 | var xhr = new XMLHttpRequest(); 72 | xhr.open('POST', 'http://koa-upload.herokuapp.com'); 73 | 74 | xhr.onload = function () { 75 | var data = JSON.parse(xhr.response); 76 | 77 | if (xhr.status === 200) { 78 | // 上传成功 79 | img.src = rst.base64; 80 | progress.value = 0; 81 | } else { 82 | // 处理错误 83 | alert(data.msg); 84 | 85 | div.remove(); 86 | that.value = null; 87 | } 88 | }; 89 | 90 | xhr.onerror = function (err) { 91 | alert('未知错误:' + JSON.stringify(err, null, 2)); 92 | div.remove(); 93 | that.value = null; 94 | }; 95 | 96 | // issues #45 提到似乎有兼容性问题,关于progress 97 | if (xhr.upload) { 98 | try { 99 | xhr.upload.addEventListener('progress', function (e) { 100 | if (!e.lengthComputable) return false; 101 | 102 | // 上传进度 103 | progress.value = ((e.loaded / e.total) || 0) * 100; 104 | }); 105 | } catch (err) { 106 | console.error('进度展示出错了,似乎不支持此特性?', err); 107 | } 108 | } 109 | 110 | // 添加参数 111 | rst.formData.append('fileLen', rst.fileLen); 112 | rst.formData.append('xxx', '我是其他参数'); 113 | 114 | // 触发上传 115 | xhr.send(rst.formData); 116 | /* ==================================================== */ 117 | 118 | return rst; 119 | }); 120 | }); 121 | 122 | document.querySelector('#version').innerHTML = lrz.version; 123 | document.querySelector('.UA').innerHTML = 'UA: ' + navigator.userAgent; 124 | 125 | function toFixed2(num) { 126 | return parseFloat(+num.toFixed(2)); 127 | } 128 | 129 | /** 130 | * 替换字符串 !{} 131 | * @param obj 132 | * @returns {String} 133 | * @example 134 | * '我是!{str}'.render({str: '测试'}); 135 | */ 136 | String.prototype.render = function (obj) { 137 | var str = this, reg; 138 | 139 | Object.keys(obj).forEach(function (v) { 140 | reg = new RegExp('\\!\\{' + v + '\\}', 'g'); 141 | str = str.replace(reg, obj[v]); 142 | }); 143 | 144 | return str; 145 | }; 146 | 147 | /** 148 | * 触发事件 - 只是为了兼容演示demo而已 149 | * @param element 150 | * @param event 151 | * @returns {boolean} 152 | */ 153 | function fireEvent(element, event) { 154 | var evt; 155 | 156 | if (document.createEventObject) { 157 | // IE浏览器支持fireEvent方法 158 | evt = document.createEventObject(); 159 | return element.fireEvent('on' + event, evt) 160 | } 161 | else { 162 | // 其他标准浏览器使用dispatchEvent方法 163 | evt = document.createEvent('HTMLEvents'); 164 | // initEvent接受3个参数: 165 | // 事件类型,是否冒泡,是否阻止浏览器的默认行为 166 | evt.initEvent(event, true, true); 167 | return !element.dispatchEvent(evt); 168 | } 169 | } 170 | 171 | /** 172 | * 替换字符串 !{} 173 | * @param obj 174 | * @returns {String} 175 | * @example 176 | * '我是!{str}'.render({str: '测试'}); 177 | */ 178 | String.prototype.render = function (obj) { 179 | var str = this, reg; 180 | 181 | Object.keys(obj).forEach(function (v) { 182 | reg = new RegExp('\\!\\{' + v + '\\}', 'g'); 183 | str = str.replace(reg, obj[v]); 184 | }); 185 | 186 | return str; 187 | }; 188 | 189 | 190 | if (!('remove' in Element.prototype)) { 191 | Element.prototype.remove = function () { 192 | this.parentNode.removeChild(this); 193 | }; 194 | } 195 | 196 | /** 197 | * 198 | *    ┏┓   ┏┓ 199 | *   ┏┛┻━━━┛┻┓ 200 | *   ┃       ┃ 201 | *   ┃   ━   ┃ 202 | *   ┃ ┳┛ ┗┳ ┃ 203 | *   ┃       ┃ 204 | *   ┃   ┻   ┃ 205 | *   ┃       ┃ 206 | *   ┗━┓   ┏━┛Code is far away from bug with the animal protecting 207 | *     ┃   ┃ 神兽保佑,代码无bug 208 | *     ┃   ┃ 209 | *     ┃   ┗━━━┓ 210 | *     ┃      ┣┓ 211 | *     ┃     ┏┛ 212 | *     ┗┓┓┏━┳┓┏┛ 213 | *      ┃┫┫ ┃┫┫ 214 | *      ┗┻┛ ┗┻┛ 215 | * 216 | */ 217 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require("webpack"); 3 | 4 | var paths = { 5 | src : './src', 6 | dist: './dist' 7 | }; 8 | 9 | module.exports = { 10 | entry: { 11 | lrz : paths.src + '/lrz', 12 | 'lrz.all': paths.src + '/lrz.all' 13 | }, 14 | 15 | output: { 16 | filename : "[name].bundle.js", 17 | chunkFilename: "[name].chunk.js", 18 | libraryTarget: 'umd' 19 | }, 20 | 21 | resolve: { 22 | root: [ 23 | paths.src + '/', 24 | paths.src + '/lib' 25 | ] 26 | } 27 | }; --------------------------------------------------------------------------------