/**
 * Ajax upload
 * Project page - http://valums.com/ajax-upload/
 * Copyright (c) 2008 Andris Valums, http://valums.com
 * Licensed under the MIT license (http://valums.com/mit-license/)
 * Version 2.0 (21.02.2009)
 */
(function() {

	var d = document, w = window;

	/**
	 * Get element by id
	 */
	function $(element) {
		if (typeof element == "string") 
			element = d.getElementById(element);
		return element;
	}

	/**
	 * Attaches event to a dom element
	 */
	function addEvent(el, type, fn) {
		if (w.addEventListener) {
			el.addEventListener(type, fn, false);
		} else if (w.attachEvent) {
			var f = function() {
				fn.call(el, w.event);
			};
			el.attachEvent('on' + type, f);
		}
	}

	/**
	 * Creates and returns element from html chunk
	 */
	var toElement = function() {
		var div = d.createElement('div');
		return function(html) {
			div.innerHTML = html;
			var el = div.childNodes[0];
			div.removeChild(el);
			return el;
		}
	}();

	function hasClass(ele, cls) {
		return ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'));
	}
	function addClass(ele, cls) {
		if (!hasClass(ele, cls)) 
			ele.className += " " + cls;
	}
	function removeClass(ele, cls) {
		var reg = new RegExp('(\\s|^)' + cls + '(\\s|$)');
		ele.className = ele.className.replace(reg, ' ');
	}

	function getOffset(el) {
		if (w.jQuery) {
			return jQuery(el).offset();
		}
		
		var top = 0, left = 0;
		do {
			top += el.offsetTop || 0;
			left += el.offsetLeft || 0;
		} while (el = el.offsetParent);

		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
		};
	}

	/**
	 * Crossbrowser mouse coordinates
	 */
	function getMouseCoords(e) {
		// pageX/Y is not supported in IE
		// http://www.quirksmode.org/dom/w3c_cssom.html
		if (!e.pageX && e.clientX) {
			return {
				x: e.clientX + d.body.scrollLeft + d.documentElement.scrollLeft,
				y: e.clientY + d.body.scrollTop + d.documentElement.scrollTop
			};
		}

		return {
			x: e.pageX,
			y: e.pageY
		};
	}
	/**
	 * Function generates unique id
	 */
	var getUID = function() {
		var id = 0;
		return function() {
			return 'ValumsAjaxUpload' + id++;
		};
	}();

	function fileFromPath(file) {
		return file.replace(/.*(\/|\\)/, "");
	}

	function getExt(file) {
		return (/[.]/.exec(file)) ? /[^.]+$/.exec(file.toLowerCase()) : '';
	}

	(function() {
		// iframe will be shared by each instance.
		var iframe = null;

		// Please use AjaxUpload , Ajax_upload will be removed in the next version
		Ajax_upload = AjaxUpload = function(button, options) {
			if (button.jquery) {
				// jquery object was passed
				button = button[0];
			} else if (typeof button == "string" && /^#.*/.test(button)) {
				button = button.slice(1);
			}
			button = $(button);

			this._input = null;
			this._button = button;
			this._disabled = false;
			this._submitting = false;

			this._settings = {
				// Location of the server-side upload script
				action: 'upload.php',
				// File upload name
				name: 'userfile',
				// Additional data to send
				data: {},
				// Submit file as soon as it's selected
				autoSubmit: true,
				// When user selects a file, useful with autoSubmit disabled
				onChange: function(file, extension) {},
				// Callback to fire before file is uploaded
				// You can return false to cancel upload
				onSubmit: function(file, extension) {},
				// Fired when file upload is completed
				onComplete: function(file, response) {}
			};

			// Merge the users options with our defaults
			for (var i in options) {
				this._settings[i] = options[i];
			}

			this._createInput();
			this._rerouteClicks();

			// 1 iframe for all inputs
			if (!iframe) {
				this._createIframe();
			}
		};

		// assigning methods to our class
		AjaxUpload.prototype = {
			setData: function(data) {
				this._settings.data = data;
			},
			disable: function() {
				this._disabled = true;
			},
			enable: function() {
				this._disabled = false;
			},
			// use setData instead, set_data will be removed in the next version
			set_data: function(data) {
				this.setData(data);
			},
			/**
			 * Creates invisible file input above the button
			 */
			_createInput: function() {
				var self = this;

				var input = d.createElement("input");
				input.setAttribute('type', 'file');
				input.setAttribute('name', this._settings.name);
				var styles = {
					'position': 'absolute',
					'margin': '-5px 0 0 -175px',
					'padding': 0,
					'width': '220px',
					'height': '10px',
					'opacity': 0,
					'cursor': 'pointer',
					'display': 'none'
				};
				for (var i in styles) {
					input.style[i] = styles[i];
				}

				// Make sure that element opacity exists
				// (IE uses filter instead)
				if (!(input.style.opacity === "0")) {
					input.style.filter = "alpha(opacity=0)";
				}
				d.body.appendChild(input);

				addEvent(input, 'change', function() {
					// get filename from input
					var file = fileFromPath(this.value);
					if (self._settings.onChange.call(self, file, getExt(file)) == false) {
						return;
					}
					// Submit form when value is changed
					if (self._settings.autoSubmit) {
						self.submit();
					}
				});
				
				this._input = input;
			},
			_rerouteClicks: function() {
				var self = this;
				
				// IE displays 'access denied' error when using this method
				// other browsers just ignore click()
				// addEvent(this._button, 'click', function(e){
				//   self._input.click();
				// });

				var box, over = false;
				addEvent(self._button, 'mouseover', function(e) {
					if (!self._input || over) 
						return;
					over = true;
					box = getBox(self._button);
				});

				// we can't use mouseout on the button,
				// because invisible input is over it
				addEvent(document, 'mousemove', function(e) {
					var input = self._input;
					if (!input || !over) 
						return;
					if (self._disabled) {
						removeClass(self._button, 'hover');
						input.style.display = 'none';
						return;
					}

					var c = getMouseCoords(e);

					if ((c.x >= box.left) && (c.x <= box.right) && (c.y >= box.top) && (c.y <= box.bottom)) {
						input.style.top = c.y + 'px';
						input.style.left = c.x + 'px';
						input.style.display = 'block';
						addClass(self._button, 'hover');
					} else {
						// mouse left the button
						over = false;
						input.style.display = 'none';
						removeClass(self._button, 'hover');
					}
				});
			},
			/**
			 * Creates iframe with unique name
			 */
			_createIframe: function() {
				// unique name
				// We cannot use getTime, because it sometimes return
				// same value in safari :(
				var id = getUID();
				// I haven't forgotten var statement, 1 iframe will be shared by each instance.
				_iframe = toElement('<iframe name="' + id + '" />');
				_iframe.id = id;
				_iframe.style.display = 'none';
				d.body.appendChild(_iframe);
			},
			/**
			 * Upload file without refreshing the page
			 */
			submit: function() {
				var self = this, settings = this._settings;
				if (this._input.value === '') {
					// there is no file
					return;
				}
				// get filename from input
				var file = fileFromPath(this._input.value);
				// execute user event
				if (!(settings.onSubmit.call(this, file, getExt(file)) == false)) {
					// Do not submit if user function returns false
					var form = this._createForm();
					form.appendChild(this._input);
					form.submit();
					d.body.removeChild(form);
					form = null;
					this._input = null;
					// create new input
					this._createInput();
					var iframe = _iframe;
					addEvent(iframe, 'load', function() {
						var toDeleteFlag = false;
						if (iframe.src == "about:blank") {
							// First time around, do not delete.
							if (toDeleteFlag) {
								iframe.remove();
							}
							return;
						}
						var doc = iframe.contentDocument ? iframe.contentDocument : frames[iframe.id].document;
						var response = doc.body.innerHTML;
						settings.onComplete.call(self, file, response);
						// Reload blank page, so that reloading main page
						// does not re-submit the post. Also, remember to
						// delete the frame
						iframe.src = "about:blank";
						toDeleteFlag = true;
					});
					// Create new iframe, so we can have multiple uploads at once
					this._createIframe();
				} else {
					// clear input to allow user to select same file
					this._input.value = '';
				}
			},
			/**
			 * Creates form, that will be submitted to iframe
			 */
			_createForm: function() {
				var settings = this._settings;

				// method, enctype must be specified here
				// because changing this attr on the fly is not allowed in IE 6/7
				// $('<form method="post" enctype="multipart/form-data"></form>')			
				var form = toElement('<form method="post" enctype="multipart/form-data"></form>');
				form.style.display = 'none';
				form.action = settings.action;
				form.target = _iframe.name;
				d.body.appendChild(form);
				
				// Create hidden input element for each data key
				for (var prop in settings.data) {
					var el = d.createElement("input");
					el.type = 'hidden';
					el.name = prop;
					el.value = settings.data[prop];
					form.appendChild(el);
				}
				return form;
			}
		};
	})();
})();
