text: COMPONENT('crop', 'dragdrop:true;format:{0}', function(self, config) {
  
  	var canvas, context;
  	var img = new Image();
  	var can = false;
  	var is = false;
  	var zoom = 100;
  	var current = { x: 0, y: 0 };
  	var offset = { x: 0, y: 0 };
  	var cache = { x: 0, y: 0, zoom: 0 };
  	var width = 0;
  
  	self.bindvisible();
  	self.novalidate();
  	self.nocompile && self.nocompile();
  	self.getter = null;
  
  	img.crossOrigin = 'anonymous';
  
  	img.onload = function () {
  		can = true;
  		zoom = 100;
  
  		var width = config.width;
  		var height = config.height;
  
  		var nw = (img.width / 2);
  		var nh = (img.height / 2);
  
  		if (img.width > width) {
  			var p = (width / (img.width / 100));
  			zoom -= zoom - p;
  			nh = ((img.height * (p / 100)) / 2);
  			nw = ((img.width * (p / 100)) / 2);
  		}
  
  		// centering
  		cache.x = current.x = (width / 2) - nw;
  		cache.y = current.y = (height / 2) - nh;
  		cache.zoom = zoom;
  		self.redraw();
  	};
  
  	self.configure = function(key, value, init) {
  		if (init)
  			return;
  		switch (key) {
  			case 'width':
  			case 'height':
  				cache.x = current.x = cache.y = current.y = 0;
  				setTimeout2(self._id + 'resize', self.redraw, 50);
  				break;
  		}
  	};
  
  	self.output = function(type) {
  		var canvas2 = document.createElement('canvas');
  		var ctx2 = canvas2.getContext('2d');
  
  		canvas2.width = config.width;
  		canvas2.height = config.height;
  
  		ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
  
  		if (config.background) {
  			ctx2.fillStyle = config.background;
  			ctx2.fillRect(0, 0, canvas2.width, canvas2.height);
  		}
  
  		var w = img.width;
  		var h = img.height;
  
  		w = ((w / 100) * zoom);
  		h = ((h / 100) * zoom);
  
  		ctx2.drawImage(img, current.x || 0, current.y || 0, w, h);
  		return type ? canvas2.toDataURL(type) : !config.background && self.isTransparent(canvas2) ? canvas2.toDataURL('image/png') : canvas2.toDataURL('image/jpeg');
  	};
  
  	self.make = function() {
  
  		self.aclass('ui-crop');
  		self.append('<input type="file" style="display:none" accept="image/*" /><ul><li data-type="upload"><span class="fa fa-folder"></span></li><li data-type="plus"><span class="fa fa-plus"></span></li><li data-type="refresh"><span class="fa fa-refresh"></span></li><li data-type="minus"><span class="fa fa-minus"></span></li></ul><canvas></canvas>');
  
  		canvas = self.find('canvas')[0];
  		context = canvas.getContext('2d');
  
  		self.event('click', 'li', function(e) {
  
  			e.preventDefault();
  			e.stopPropagation();
  
  			var type = $(this).attr('data-type');
  
  			switch (type) {
  				case 'upload':
  					self.find('input').trigger('click');
  					break;
  				case 'plus':
  					zoom += 3;
  					if (zoom > 300)
  						zoom = 300;
  					current.x -= 3;
  					current.y -= 3;
  					self.redraw();
  					break;
  				case 'minus':
  					zoom -= 3;
  					if (zoom < 3)
  						zoom = 3;
  					current.x += 3;
  					current.y += 3;
  					self.redraw();
  					break;
  				case 'refresh':
  					zoom = cache.zoom;
  					self.redraw();
  					break;
  			}
  
  		});
  
  		self.find('input').on('change', function() {
  			var file = this.files[0];
  			self.load(file);
  			this.value = '';
  
  		});
  
  		$(canvas).on('mousedown', function (e) {
  
  			if (self.disabled || !can)
  				return;
  
  			is = true;
  			var rect = canvas.getBoundingClientRect();
  			var x = e.clientX - rect.left;
  			var y = e.clientY - rect.top;
  			offset.x = x - current.x;
  			offset.y = y - current.y;
  		});
  
  		config.dragdrop && $(canvas).on('dragenter dragover dragexit drop dragleave', function (e) {
  
  			if (self.disabled)
  				return;
  
  			e.stopPropagation();
  			e.preventDefault();
  
  			switch (e.type) {
  				case 'drop':
  					self.rclass('ui-crop-dragdrop');
  					break;
  				case 'dragenter':
  				case 'dragover':
  					self.aclass('ui-crop-dragdrop');
  					return;
  				case 'dragexit':
  				case 'dragleave':
  				default:
  					self.rclass('ui-crop-dragdrop');
  					return;
  			}
  
  			var files = e.originalEvent.dataTransfer.files;
  			files[0] && self.load(files[0]);
  		});
  
  		self.load = function(file) {
  			self.getOrientation(file, function(orient) {
  				var reader = new FileReader();
  				reader.onload = function () {
  					if (orient < 2) {
  						img.src = reader.result;
  						setTimeout(function() {
  							self.change(true);
  						}, 500);
  					} else {
  						SETTER('loading', 'show');
  						self.resetOrientation(reader.result, orient, function(url) {
  							SETTER('loading', 'hide', 500);
  							img.src = url;
  							self.change(true);
  						});
  					}
  				};
  				reader.readAsDataURL(file);
  			});
  		};
  
  		self.event('mousemove mouseup', function (e) {
  
  			if (e.type === 'mouseup') {
  				is && self.change();
  				is = false;
  				return;
  			}
  
  			if (self.disabled || !can || !is)
  				return;
  
  			var rect = canvas.getBoundingClientRect();
  			var x = e.clientX - rect.left;
  			var y = e.clientY - rect.top;
  			current.x = x - offset.x;
  			current.y = y - offset.y;
  			self.redraw();
  		});
  	};
  
  	self.redraw = function() {
  
  		var ratio = width < config.width ? width / config.width : 1;
  
  		canvas.width = width < config.width ? width : config.width;
  		canvas.height = width < config.width ? (config.height / config.width) * width : config.height;
  
  		var w = img.width;
  		var h = img.height;
  
  		w = ((w / 100) * zoom);
  		h = ((h / 100) * zoom);
  
  		context.clearRect(0, 0, canvas.width, canvas.height);
  
  		if (config.background) {
  			context.fillStyle = config.background;
  			context.fillRect(0, 0, canvas.width, canvas.height);
  		}
  
  		context.drawImage(img, (current.x || 0) * ratio, (current.y || 0) * ratio, w * ratio, h * ratio);
  	};
  
  	self.setter = function(value) {
  		self.width(function(w) {
  			width = w;
  			if (value)
  				img.src = config.format.format(value);
  			else
  				self.redraw();
  		});
  	};
  
  	self.isTransparent = function(canvas) {
  		var id = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height);
  		for (var i = 0, length = id.data.length; i < length; i += 4) {
  			if (id.data[i + 3] !== 255)
  				return true;
  		}
  		return false;
  	};
  
  	// http://stackoverflow.com/a/32490603
  	self.getOrientation = function(file, callback) {
  		var reader = new FileReader();
  		reader.onload = function(e) {
  			var view = new DataView(e.target.result);
  			if (view.getUint16(0, false) != 0xFFD8)
  				return callback(-2);
  			var length = view.byteLength;
  			var offset = 2;
  			while (offset < length) {
  				var marker = view.getUint16(offset, false);
  				offset += 2;
  				if (marker == 0xFFE1) {
  					if (view.getUint32(offset += 2, false) != 0x45786966)
  						return callback(-1);
  					var little = view.getUint16(offset += 6, false) == 0x4949;
  					offset += view.getUint32(offset + 4, little);
  					var tags = view.getUint16(offset, little);
  					offset += 2;
  					for (var i = 0; i < tags; i++)
  						if (view.getUint16(offset + (i * 12), little) == 0x0112)
  							return callback(view.getUint16(offset + (i * 12) + 8, little));
  				} else if ((marker & 0xFF00) != 0xFF00)
  					break;
  				else
  					offset += view.getUint16(offset, false);
  			}
  			return callback(-1);
  		};
  		reader.readAsArrayBuffer(file.slice(0, 64 * 1024));
  	};
  
  	self.resetOrientation = function(src, srcOrientation, callback) {
  		var img = new Image();
  		img.onload = function() {
  			var width = img.width;
  			var height = img.height;
  			var canvas = document.createElement('canvas');
  			var ctx = canvas.getContext('2d');
  
  			// set proper canvas dimensions before transform & export
  			if (4 < srcOrientation && srcOrientation < 9) {
  				canvas.width = height;
  				canvas.height = width;
  			} else {
  				canvas.width = width;
  				canvas.height = height;
  			}
  			switch (srcOrientation) {
  				case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;
  				case 3: ctx.transform(-1, 0, 0, -1, width, height); break;
  				case 4: ctx.transform(1, 0, 0, -1, 0, height); break;
  				case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
  				case 6: ctx.transform(0, 1, -1, 0, height, 0); break;
  				case 7: ctx.transform(0, -1, -1, 0, height, width); break;
  				case 8: ctx.transform(0, -1, 1, 0, 0, width); break;
  			}
  			ctx.drawImage(img, 0, 0);
  			callback(canvas.toDataURL());
  		};
  		img.src = src;
  	};
  });