(function (){
	function log(){
		if (typeof(console) != 'undefined' && typeof(console.log) == 'function'){
			Array.prototype.unshift.call(arguments, '[Ajax Upload]');
			console.log(Array.prototype.join.call(arguments, ' '));
		}
	} 
	function addEvent(el, type, fn){
		if (el.addEventListener){
			el.addEventListener(type, fn, false);
		} else if (el.attachEvent){
			el.attachEvent('on' + type, function(){
				fn.call(el);
			});
		} else {
			throw new Error('not supported or DOM not loaded');
		}
	}
	function addResizeEvent(fn){
		var timeout;
		addEvent(window, 'resize', function(){
			if (timeout){
				clearTimeout(timeout);
			}
			timeout = setTimeout(fn, 100);
		});
	}
	if (document.documentElement.getBoundingClientRect){
		var getOffset = function(el){
			var box = el.getBoundingClientRect();
			var doc = el.ownerDocument;
			var body = doc.body;
			var docElem = doc.documentElement;
			var clientTop = docElem.clientTop || body.clientTop || 0;
			var clientLeft = docElem.clientLeft || body.clientLeft || 0;
			var zoom = 1;            
			if (body.getBoundingClientRect){
				var bound = body.getBoundingClientRect();
				zoom = (bound.right - bound.left) / body.clientWidth;
			}
			if (zoom > 1){
				clientTop = 0;
				clientLeft = 0;
			}
			var top = box.top / zoom + (window.pageYOffset || docElem && docElem.scrollTop / zoom || body.scrollTop / zoom) - clientTop, left = box.left / zoom + (window.pageXOffset || docElem && docElem.scrollLeft / zoom || body.scrollLeft / zoom) - clientLeft;
			return {top:top, left:left};
		};
	}
	else{
		var getOffset = function(el){
			var top = 0, left = 0;
			do {
				top += el.offsetTop || 0;
				left += el.offsetLeft || 0;
				el = el.offsetParent;
			}
			while (el);
			return {left:left, top:top};
		};
	}
	function getBox(el){
		var left, right, top, bottom;
		var offset = getOffset(el);
		left = offset.left;
		top = offset.top;
		right = left + el.offsetWidth;
		bottom = top + el.offsetHeight;
		return {left:left, right:right, top:top, bottom:bottom};
	}
	function addStyles(el, styles){
		for (var name in styles){
			if (styles.hasOwnProperty(name)){
				el.style[name] = styles[name];
			}
		}
	}  
	function copyLayout(from, to){
		var box = getBox(from);
		addStyles(to, {position:'absolute', left:box.left + 'px', top:box.top + 'px', width:from.offsetWidth + 'px', height:from.offsetHeight + 'px'});
	}
	var toElement = (function(){
		var div = document.createElement('div');
		return function(html){
			div.innerHTML = html;
			var el = div.firstChild;
			return div.removeChild(el);
		};
	})();
	var getUID = (function(){
		var id = 0;
		return function(){
			return 'ValumsAjaxUpload' + id++;
		};
	})();
	function fileFromPath(file){
		return file.replace(/.*(\/|\\)/, "");
	}
	function getExt(file){
		return (-1 !== file.indexOf('.')) ? file.replace(/.*[.]/, '') : '';
	}
	function hasClass(el, name){
		var re = new RegExp('\\b' + name + '\\b');
		return re.test(el.className);
	}
	function addClass(el, name){
		if (!hasClass(el, name)){
			el.className += ' ' + name;
		}
	}
	function removeClass(el, name){
		var re = new RegExp('\\b' + name + '\\b');
		el.className = el.className.replace(re, '');
	}
	function removeNode(el){
		el.parentNode.removeChild(el);
	}
	window.AjaxUpload = function(button, options){
		this._settings = {
			action:'upload.php',
			name:'userfile',
			data:{},
			autoSubmit:true,
			responseType:false,
			hoverClass:'hover',
			disabledClass:'disabled',
			onChange:function(file, extension){},
			onSubmit:function(file, extension){},
			onComplete:function(file, response){}
		};
		for (var i in options){
			if (options.hasOwnProperty(i)){
				this._settings[i] = options[i];
			}
		}
		if (button.jquery){
			button = button[0];
		}
		else if (typeof button == "string"){
			if (/^#.*/.test(button)){
				button = button.slice(1);
			}
			button = document.getElementById(button);
		}
		if (!button || button.nodeType !== 1){
			throw new Error("Please make sure that you're passing a valid element");
		}
		if (button.nodeName.toUpperCase() == 'A'){
			addEvent(button, 'click', function(e){
				if (e && e.preventDefault){
					e.preventDefault();
				}
				else if (window.event){
					window.event.returnValue = false;
				}
			});
		}
		this._button = button;
		this._input = null;
		this._disabled = false;
		this.enable();
		this._rerouteClicks();
	};
	AjaxUpload.prototype = {
		setData:function(data){
			this._settings.data = data;
		},
		disable:function(){
			addClass(this._button, this._settings.disabledClass);
			this._disabled = true;
			var nodeName = this._button.nodeName.toUpperCase();
			if (nodeName == 'INPUT' || nodeName == 'BUTTON'){
				this._button.setAttribute('disabled', 'disabled');
			}
			if (this._input){
				this._input.parentNode.style.visibility = 'hidden';
			}
		},
		enable:function(){
			removeClass(this._button, this._settings.disabledClass);
			this._button.removeAttribute('disabled');
			this._disabled = false;
		},
		_createInput: function(){
			var self = this;
			var input = document.createElement("input");
			input.setAttribute('type', 'file');
			input.setAttribute('name', this._settings.name);
			addStyles(input, {
				'position':'absolute',
				'right':0,
				'margin':0,
				'padding':0,
				'fontSize':'480px',
				'cursor':'pointer'
			});
			var div = document.createElement("div");
			addStyles(div, {
				'display':'block',
				'position':'absolute',
				'overflow':'hidden',
				'margin':0,
				'padding':0,
				'opacity':0,
				'direction':'ltr',
				'zIndex':2147483583
			});
			if (div.style.opacity !== "0"){
				if (typeof(div.filters) == 'undefined'){
					throw new Error('Opacity not supported by the browser');
				}
				div.style.filter = "alpha(opacity=0)";
			}
			addEvent(input, 'change', function(){
				if (!input || input.value === ''){
					return;
				}
				var file = fileFromPath(input.value);
				if (false === self._settings.onChange.call(self, file, getExt(file))){
					self._clearInput();
					return;
				}
				if (self._settings.autoSubmit){
					self.submit();
				}
			});
			addEvent(input, 'mouseover', function(){addClass(self._button, self._settings.hoverClass);});
			addEvent(input, 'mouseout', function(){removeClass(self._button, self._settings.hoverClass); input.parentNode.style.visibility = 'hidden';});
			div.appendChild(input);
			document.body.appendChild(div);
			this._input = input;
		},
		_clearInput:function(){
			if (!this._input){
				return;
			}
			removeNode(this._input.parentNode);
			this._input = null;
			this._createInput();
			removeClass(this._button, this._settings.hoverClass);
		},
		_rerouteClicks:function(){
			var self = this;
			addEvent(self._button, 'mouseover', function(){
				if (self._disabled){
					return;
				}
				if (!self._input){
					self._createInput();
				}
				var div = self._input.parentNode;
				copyLayout(self._button, div);
				div.style.visibility = 'visible';
			});
		},
		_createIframe:function(){
			var id = getUID();
			var iframe = toElement('<iframe src="javascript:false;" name="' + id + '" />');
			iframe.setAttribute('id', id);
			iframe.style.display = 'none';
			document.body.appendChild(iframe);
			return iframe;
		},
		_createForm:function(iframe){
			var settings = this._settings;
			var form = toElement('<form method="post" enctype="multipart/form-data"></form>');
			form.setAttribute('action', settings.action);
			form.setAttribute('target', iframe.name);
			form.style.display = 'none';
			document.body.appendChild(form);
			for (var prop in settings.data){
				if (settings.data.hasOwnProperty(prop)){
					var el = document.createElement("input");
					el.setAttribute('type', 'hidden');
					el.setAttribute('name', prop);
					el.setAttribute('value', settings.data[prop]);
					form.appendChild(el);
				}
			}
			return form;
		},
		_getResponse:function(iframe, file){
			var toDeleteFlag = false, self = this, settings = this._settings;
			addEvent(iframe, 'load', function(){
				if (iframe.src == "javascript:'%3Chtml%3E%3C/html%3E';" || iframe.src == "javascript:'<html></html>';"){                                                                        
					if (toDeleteFlag){
						setTimeout(function(){
							removeNode(iframe);
						}, 0);
					}
					return;
				}
				var doc = iframe.contentDocument ? iframe.contentDocument : window.frames[iframe.id].document;
				if (doc.readyState && doc.readyState != 'complete'){
					return;
				}
				if (doc.body && doc.body.innerHTML == "false"){
					return;
				}
				var response;
				if (doc.XMLDocument){
					response = doc.XMLDocument;
				}
				else if (doc.body){
					response = doc.body.innerHTML;
					if (settings.responseType && settings.responseType.toLowerCase() == 'json'){
						if (doc.body.firstChild && doc.body.firstChild.nodeName.toUpperCase() == 'PRE'){
							response = doc.body.firstChild.firstChild.nodeValue;
						}
						if (response){
							response = eval("(" + response + ")");
						}
						else{
							response = {};
						}
					}
				}
				else{
					response = doc;
				}
				settings.onComplete.call(self, file, response);
				toDeleteFlag = true;
				iframe.src = "javascript:'<html></html>';";
			});
		},
		submit:function(){
			var self = this, settings = this._settings;
			if (!this._input || this._input.value === ''){
				return;
			}
			var file = fileFromPath(this._input.value);
			if (false === settings.onSubmit.call(this, file, getExt(file))){
				this._clearInput();
				return;
			}
			var iframe = this._createIframe();
			var form = this._createForm(iframe);
			removeNode(this._input.parentNode);
			removeClass(self._button, self._settings.hoverClass);
			form.appendChild(this._input);
			form.submit();
			removeNode(form); form = null;
			removeNode(this._input); this._input = null;
			this._getResponse(iframe, file);
			this._createInput();
		}
	};
})();
