jQuery.extend({
	buildfileupload: function(s) {
		try {
			var reader = new FileReader();
			var canvaszoom = false;
			if(s.maxfilesize && s.files[0].size > s.maxfilesize * 1024) {
				canvaszoom = true;
			}

			var picupload = function(picdata) {

				if(!XMLHttpRequest.prototype.sendAsBinary){
					XMLHttpRequest.prototype.sendAsBinary = function(datastr) {
						function byteValue(x) {
							return x.charCodeAt(0) & 0xff;
						}
						var ords = Array.prototype.map.call(datastr, byteValue);
						var ui8a = new Uint8Array(ords);
						this.send(ui8a.buffer);
					}
				}

				var xhr = new XMLHttpRequest(),
					file = s.files[0],
					index = 0,
					start_time = new Date().getTime(),
					boundary = '------multipartformboundary' + (new Date).getTime(),
					builder;

				builder = jQuery.getbuilder(s, file.name, picdata, boundary);

				if(s.uploadpercent) {
					xhr.upload.onprogress = function(e) {
						if(e.lengthComputable) {
							var percent = Math.ceil((e.loaded / e.total) * 100);
							$('#' + s.uploadpercent).html(percent + '%');
						}
					};
				}

				xhr.open("POST", s.uploadurl, true);
				xhr.setRequestHeader('content-type', 'multipart/form-data; boundary='
					+ boundary);

				xhr.sendAsBinary(builder);

				xhr.onerror = function() {
					s.error();
				};
				xhr.onabort = function() {
					s.error();
				};
				xhr.ontimeout = function() {
					s.error();
				};
				xhr.onload = function() {
					if(xhr.responseText) {
						s.success(xhr.responseText);
					}
				};
			};

			var getorientation = function(binfile) {
				function getbyteat(offset) {
					return binfile.charCodeAt(offset) & 0xFF;
				}
				function getbytesat(offset, length) {
					var bytes = [];
					for(var i=0; i<length; i++) {
						bytes[i] = binfile.charCodeAt((offset + i)) & 0xFF;
					}
					return bytes;
				}
				function getshortat(offset, bigendian) {
					var shortat = bigendian ?
						(getbyteat(offset) << 8) + getbyteat(offset + 1)
						: (getbyteat(offset + 1) << 8) + getbyteat(offset);
					if(shortat < 0) {
						shortat += 65536;
					}
					return shortat;
				}
				function getlongat(offset, bigendian) {
					var byte1 = getbyteat(offset);
					var byte2 = getbyteat(offset + 1);
					var byte3 = getbyteat(offset + 2);
					var byte4 = getbyteat(offset + 3);
					var longat = bigendian ?
						(((((byte1 << 8) + byte2) << 8) + byte3) << 8) + byte4
						: (((((byte4 << 8) + byte3) << 8) + byte2) << 8) + byte1;
					if(longat < 0) longat += 4294967296;
					return longat;
				}
				function getslongat(offset, bigendian) {
					var ulongat = getlongat(offset, bigendian);
					if(ulongat > 2147483647) {
						return ulongat - 4294967296;
					} else {
						return ulongat;
					}
				}
				function getstringat(offset, length) {
					var str = [];
					var bytes = getbytesat(offset, length);
					for(var i=0; i<length; i++) {
						str[i] = String.fromCharCode(bytes[i]);
					}
					return str.join('');
				}
				function readtagvalue(entryoffset, tiffstart, dirstart, bigend) {
					var type = getshortat(entryoffset + 2, bigend);
					var numvalues = getlongat(entryoffset + 4, bigend);
					var valueoffset = getlongat(entryoffset + 8, bigend) + tiffstart;
					var offset, vals;
					switch(type) {
						case 1:
						case 7:
							if(numvalues == 1) {
								return getbyteat(entryoffset + 8, bigend);
							} else {
								offset = numvalues > 4 ? valueoffset : (entryoffset + 8);
								vals = [];
								for(var n=0; n<numvalues; n++) {
									vals[n] = getbyteat(offset + n);
								}
								return vals;
							}
						case 2:
							offset = numvalues > 4 ? valueoffset : (entryoffset + 8);
							return getstringat(offset, numvalues - 1);
						case 3:
							if(numvalues == 1) {
								return getshortat(entryoffset + 8, bigend);
							} else {
								offset = numvalues > 2 ? valueoffset : (entryoffset + 8);
								vals = [];
								for(var n=0;n<numvalues; n++) {
									vals[n] = getshortat(offset + 2 * n, bigend);
								}
								return vals;
							}
						case 4:
							if(numvalues == 1) {
								return getlongat(entryoffset + 8, bigend);
							} else {
								vals = [];
								for(var n=0; n<numvalues; i++) {
									vals[n] = getlongat(valueoffset + 4 * n, bigend);
								}
								return vals;
							}
						case 5:
							if(numvalues == 1) {
								var numerator = getlongat(valueoffset, bigend);
								var denominator = getlongat(valueoffset + 4, bigend);
								var val = new Number(numerator / denominator);
								val.numerator = numerator;
								val.denominator = denominator;
								return val;
							} else {
								vals = [];
								for(var n=0; n<numvalues; n++) {
									var numerator = getlongat(valueoffset + 8*n, bigend);
									var denominator = getlongat(valueoffset+4 + 8*n, bigend);
									vals[n] = new Number(numerator / denominator);
									vals[n].numerator = numerator;
									vals[n].denominator = denominator;
								}
								return vals;
							}
						case 9:
							if(numvalues == 1) {
								return getslongat(entryoffset + 8, bigend);
							} else {
								vals = [];
								for(var n=0;n<numvalues; n++) {
									vals[n] = getslongat(valueoffset + 4 * n, bigend);
								}
								return vals;
							}
						case 10:
							if(numvalues == 1) {
								return getslongat(valueoffset, bigend) / getslongat(valueoffset+4, bigend);
							} else {
								vals = [];
								for(var n=0; n<numvalues; n++) {
									vals[n] = getslongat(valuesoffset + 8*n, bigend) / getslongat(valueoffset+4 + 8*n, bigend);
								}
								return vals;
							}
					}
				}
				function readtags(tiffstart, dirstart, strings, bigend) {
					var entries = getshortat(dirstart, bigend);
					var tags = {}, entryofffset, tag;
					for(var i=0; i<entries; i++) {
						entryoffset = dirstart + i *12 + 2;
						tag = strings[getshortat(entryoffset, bigend)];
						tags[tag] = readtagvalue(entryoffset, tiffstart, dirstart, bigend);
					}
					return tags;
				}
				function readexifdata(start) {
					if(getstringat(start, 4) != 'Exif') {
						return false;
					}
					var bigend;
					var tags, tag;
					var tiffoffset = start + 6;
					if(getshortat(tiffoffset) == 0x4949) {
						bigend = false;
					} else if(getshortat(tiffoffset) == 0x4D4D) {
						bigend = true;
					} else {
						return false;
					}
					if(getshortat(tiffoffset + 2, bigend) != 0x002A) {
						return false;
					}
					if(getlongat(tiffoffset + 4, bigend) != 0x00000008) {
						return false;
					}
					var tifftags = {
						0x0112 : "Orientation"
					};
					tags = readtags(tiffoffset, tiffoffset + 8, tifftags, bigend);
					return tags;
				}
				if(getbyteat(0) != 0xFF || getbyteat(1) != 0xD8) {
					return false;
				}
				var offset = 2;
				var length = binfile.length;
				var marker;
				while(offset < length) {
					if(getbyteat(offset) != 0xFF) {
						return false;
					}
					marker = getbyteat(offset + 1);
					if(marker == 22400 || marker == 225) {
						return readexifdata(offset + 4);
					} else {
						offset += 2 + getshortat(offset + 2, true);
					}
				}
			};



			var detectsubsampling = function(img, imgwidth, imgheight) {
				if(imgheight * imgwidth > 1024 * 1024) {
					var tmpcanvas = document.createElement('canvas');
					tmpcanvas.width = tmpcanvas.height = 1;
					var tmpctx = tmpcanvas.getContext('2d');
					tmpctx.imageSmoothingQuality = 'high';
					tmpctx.drawImage(img, -imgwidth + 1, 0);
					return tmpctx.getImageData(0, 0, 1, 1).data[3] === 0;
				} else {
					return false;
				}
			};
			var detectverticalsquash = function(img, imgheight) {
				var tmpcanvas = document.createElement('canvas');
				tmpcanvas.width = 1;
				tmpcanvas.height = imgheight;
				var tmpctx = tmpcanvas.getContext('2d');
				tmpctx.imageSmoothingQuality = 'high';
				tmpctx.drawImage(img, 0, 0);
				var data = tmpctx.getImageData(0, 0, 1, imgheight).data;
				var sy = 0;
				var ey = imgheight;
				var py = imgheight;
				while(py > sy) {
					var alpha = data[(py - 1) * 4 + 3];
					if(alpha === 0) {
						ey = py;
					} else {
						sy = py;
					}
					py = (ey + sy) >> 1;
				}
				var ratio = py / imgheight;
				return (ratio === 0) ? 1 : ratio;
			};
			var transformcoordinate = function(canvas, ctx, width, height, orientation) {
				switch(orientation) {
					case 5:
					case 6:
					case 7:
					case 8:
						canvas.width = height;
						canvas.height = width;
						break;
					default:
						canvas.width = width;
						canvas.height = height;
				}
				switch(orientation) {
					case 2:
						ctx.translate(width, 0);
						ctx.scale(-1, 1);
						break;
					case 3:
						ctx.translate(width, height);
						ctx.rotate(Math.PI);
						break;
					case 4:
						ctx.translate(0, height);
						ctx.scale(1, -1);
						break;
					case 5:
						ctx.rotate(0.5 * Math.PI);
						ctx.scale(1, -1);
						break;
					case 6:
						ctx.rotate(0.5 * Math.PI);
						ctx.translate(0, -height);
						break;
					case 7:
						ctx.rotate(0.5 * Math.PI);
						ctx.translate(width, -height);
						ctx.scale(-1, 1);
						break;
					case 8:
						ctx.rotate(-0.5 * Math.PI);
						ctx.translate(-width, 0);
						break;
				}
			};

			var maxheight = s.maxfilesize > 1000 ? (s.maxfilesize > 2000 ? 2560 : 1920) : 1280;
			var maxwidth = s.maxfilesize > 1000 ? (s.maxfilesize > 2000 ? 1440 : 1080) : 720;
			var canvas = document.createElement('canvas');
			var ctx = canvas.getContext('2d');
			ctx.imageSmoothingQuality = 'high';

			var img = new Image();
			img.onload = function() {
				$this = $(this);
				var imgwidth = this.width ? this.width : $this.width();
				var imgheight = this.height ? this.height : $this.height();

				var canvaswidth = maxwidth;
				var canvasheight = maxheight;
				var newwidth = imgwidth;
				var newheight = imgheight;
				if(imgwidth/imgheight <= canvaswidth/canvasheight && imgheight >= canvasheight) {
					newheight = canvasheight;
					newwidth = Math.ceil(canvasheight/imgheight*imgwidth);
				} else if(imgwidth/imgheight > canvaswidth/canvasheight && imgwidth >= canvaswidth) {
					newwidth = canvaswidth;
					newheight = Math.ceil(canvaswidth/imgwidth*imgheight);
				}

				ctx.save();

				var imgfilebinary = this.src.replace(/data:.+;base64,/, '');
				if(typeof atob == 'function') {
					imgfilebinary = atob(imgfilebinary);
				} else {
					imgfilebinary = jQuery.base64decode(imgfilebinary);
				}
				var orientation = getorientation(imgfilebinary);
				orientation = orientation.Orientation;

				if(detectsubsampling(this, imgwidth, imgheight)) {
					imgheight = imgheight / 2;
					imgwidth = imgwidth / 2;
				}
				var vertsquashratio = detectverticalsquash(this, imgheight);
				transformcoordinate(canvas, ctx, newwidth, newheight, orientation);
				ctx.drawImage(this, 0, 0, imgwidth, imgheight, 0, 0, newwidth, newheight/vertsquashratio);
				ctx.restore();

				var newdataurl = canvas.toDataURL(s.files[0].type).replace(/data:.+;base64,/, '');

				if(typeof atob == 'function') {
					picupload(atob(newdataurl));
				} else {
					picupload(jQuery.base64decode(newdataurl));
				}
			};

			reader.index = 0;
			reader.onloadend = function(e) {
				if(canvaszoom) {
					img.src = e.target.result;
				} else {
					picupload(e.target.result);
				}
				return;
			};
			if(canvaszoom) {
				reader.readAsDataURL(s.files[0]);
			} else {
				reader.readAsBinaryString(s.files[0]);
			}
		} catch(err) {
			return s.error();
		}
		return;
    },
	getbuilder: function(s, filename, filedata, boundary) {
		var dashdash = '--',
			crlf = '\r\n',
			builder = '';

		for(var i in s.uploadformdata) {
			builder += dashdash;
			builder += boundary;
			builder += crlf;
			builder += 'Content-Disposition: form-data; name="' + i + '"';
			builder += crlf;
			builder += crlf;
			builder += s.uploadformdata[i];
			builder += crlf;
		}

		builder += dashdash;
		builder += boundary;
		builder += crlf;
		builder += 'Content-Disposition: form-data; name="' + s.uploadinputname + '"';
		builder += '; filename="' + filename + '"';
		builder += crlf;

		builder += 'Content-Type: application/octet-stream';
		builder += crlf;
		builder += crlf;

		builder += filedata;
		builder += crlf;

		builder += dashdash;
		builder += boundary;
		builder += dashdash;
		builder += crlf;
		return builder;
	}
});

jQuery.extend({
	base64encode: function(input) {
		var output = '';
		var chr1, chr2, chr3 = '';
		var enc1, enc2, enc3, enc4 = '';
		var i = 0;
		do {
			chr1 = input.charCodeAt(i++);
			chr2 = input.charCodeAt(i++);
			chr3 = input.charCodeAt(i++);
			enc1 = chr1 >> 2;
			enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
			enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
			enc4 = chr3 & 63;
			if (isNaN(chr2)){
				enc3 = enc4 = 64;
			} else if (isNaN(chr3)){
				enc4 = 64;
			}
			output = output+this._keys.charAt(enc1)+this._keys.charAt(enc2)+this._keys.charAt(enc3)+this._keys.charAt(enc4);
			chr1 = chr2 = chr3 = '';
			enc1 = enc2 = enc3 = enc4 = '';
		} while (i < input.length);
		return output;
	},
	base64decode: function(input) {
		var output = '';
		var chr1, chr2, chr3 = '';
		var enc1, enc2, enc3, enc4 = '';
		var i = 0;
		if (input.length%4!=0){
			return '';
		}
		var base64test = /[^A-Za-z0-9\+\/\=]/g;
		if (base64test.exec(input)){
			return '';
		}
		do {
			enc1 = this._keys.indexOf(input.charAt(i++));
			enc2 = this._keys.indexOf(input.charAt(i++));
			enc3 = this._keys.indexOf(input.charAt(i++));
			enc4 = this._keys.indexOf(input.charAt(i++));
			chr1 = (enc1 << 2) | (enc2 >> 4);
			chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
			chr3 = ((enc3 & 3) << 6) | enc4;
			output = output + String.fromCharCode(chr1);
			if (enc3 != 64){
				output+=String.fromCharCode(chr2);
			}
			if (enc4 != 64){
				output+=String.fromCharCode(chr3);
			}
			chr1 = chr2 = chr3 = '';
			enc1 = enc2 = enc3 = enc4 = '';
		} while (i < input.length);
		return output;
	},
	_keys: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
});