/**
 * Ajax upload plugin for jQuery
 * 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 1.0 (24.01.2009)
 */

(function($) {
	// we need jQuery to run
	if (!$) return;

	/** Function generates unique id */
	var get_uid = function() {
		var uid = 0;
		return function() {
			return uid++;
		}
	} ();

	/** button - jQuery element
	*  option - hash of options :
	*  action - URL which iframe will use to post data
	*  name - file input name
	*  data - extra data hash to post within the file
	*  onSubmit - callback to fire on file submit
	*  onComplete - callback to fire when iframe has finished loading	 */
	window.Ajax_upload = function(button, options) {
		// make sure it is jquery object
		button = $(button);

		if (button.size() != 1) {
			//You should pass only 1 element to ajax upload ot once
			return;
		}

		this.button = button;
		this.wrapper = null;
		this.form = null;
		this.input = null;
		this.iframe = null;

		this.disabled = false;
		this.submitting = false;

		this.settings = {
			action: 'upload.php', // Location of the server-side upload script
			name: 'userfile', // File upload name
			data: {}, // Additional data to send
			onSubmit: function(file, extension) { }, // Callback to fire when user selects file // You can return false to cancel upload
			onComplete: function(file, response) { } // Fired when file upload is completed
		};

		// Merge the users options with our defaults	
		$.extend(this.settings, options);

		this.create_wrapper();
		this.create_input();

		if (jQuery.browser.msie) {
			this.make_parent_opaque(); //fixes bug, which occur when you use div with transparent background as upload button
		}

		this.create_iframe();
	}

	// assigning methods to our class
	Ajax_upload.prototype = {
		set_data: function(data) {
			this.settings.data = data;
		},
		disable: function() {
			this.disabled = true;
			if (!this.submitting) {
				this.input.attr('disabled', true);
				this.button.removeClass('hover');
			}
		},
		enable: function() {
			this.disabled = false;
			this.input.attr('disabled', false);
		},
		/** Creates wrapper for button and invisible file input */
		create_wrapper: function() {
			var button = this.button, wrapper; // Shorten names

			wrapper = this.wrapper = $('<div></div>')
				.insertAfter(button)
				.append(button);

			setTimeout(function() {// wait a bit because of FF bug it can't properly calculate the outerHeight
				wrapper.css({
					position: 'relative'
					, display: 'block'
					, overflow: 'hidden'
					, height: button.outerHeight(true) // we need dimensions because of ie bug that allows to move input outside even if overflow set to hidden
					, width: button.outerWidth(true)
				});
			}, 1);

			var self = this;
			wrapper.mousemove(function(e) {
				// Move the input with the mouse, so the user can't misclick it
				if (!self.input) {
					return;
				}

				self.input.css({
					top: e.pageY - wrapper.offset().top - 5 + 'px'
					, left: e.pageX - wrapper.offset().left - 170 + 'px'
				});
			});


		},
		/** Creates invisible file input above the button */
		create_input: function() {
			var self = this;

			this.input =
				$('<input type="file" tabindex="-1" />')
				.attr('name', this.settings.name)
				.css({
					'position': 'absolute'
					, 'margin': 0
					, 'padding': 0
					, 'width': '220px'
					, 'height': '10px'
					, 'opacity': 0
					, cursor: 'pointer'
				})
				.change(function() {
					if ($(this).val() == '') {
						// there is no file
						return;
					}

					self.submitting = true; // we need to lock "disable" method
					self.submit(); // Submit form when value is changed

					if (self.disabled) {
						self.disable();
					}

					self.submitting = false; // unlock "disable" method

					// clear input to allow user to select same file
					// Thanks to boston for fix
					$(this).val('');
				})
				.appendTo(this.wrapper)

				.hover(// Emulate button hover effect
					function() { self.button.addClass('hover'); }
					, function() { self.button.removeClass('hover'); }
				);

			if (this.disabled) {
				this.input.attr('disabled', true);
			}

		},
		/** Creates iframe with unique name */
		create_iframe: function() {
			// unique name
			// We cannot use getTime, because it sometimes return
			// same value in safari :(
			var id = 'valums97hhu' + get_uid();

			// create iframe, so we dont need to refresh page
			this.iframe =
				$('<iframe id="' + id + '" name="' + id + '"></iframe>')
				.css('display', 'none')
				.appendTo('body');
		},
		/** Upload file without refreshing the page */
		submit: function() {
			var self = this, settings = this.settings;

			var file = this.file_from_path(this.input.val()); // get filename from input

			// execute user event
			if (settings.onSubmit.call(this, file, this.get_ext(file)) === false) {
				return; // Do not continue if user function returns false
			}

			this.create_form();
			this.input.appendTo(this.form);
			this.form.submit();
			this.input.remove(); this.input = null;
			this.form.remove(); this.form = null;
			this.submitting = false;

			this.create_input(); // create new input

			var iframe = this.iframe;
			iframe.load(function() {
				if (iframe[0].src == "about:blank") {
					return;
				}

				var response = iframe.contents().find('body').html();
				response = response.toUpperCase().replace('<PRE>', '');
				response = response.toUpperCase().replace('</PRE>', '');

				settings.onComplete.call(self, file, response);
				// Workaround for FF2 bug, which causes cursor to be in busy state after post.
				setTimeout(function() {
					// Workaround for ie6 bug, which causes progress bar to be in busy state after post.
					// Thanks to Lei for the fix
					iframe[0].src = "about:blank";
					iframe.remove();
				}, 1);
			});

			// Create new iframe, so we can have multiple uploads at once
			this.create_iframe();
		},
		/** Creates form, that will be submitted to iframe */
		create_form: function() {
			// method, enctype must be specified here
			// because changing this attr on the fly is not allowed in IE 6/7
			this.form =
				$('<form method="post" enctype="multipart/form-data"></form>')
				.attr({
					"action": this.settings.action
					, "target": this.iframe.attr('name')
				})
				.appendTo('body');

			// Create hidden input element for each data key
			for (var i in this.settings.data) {
				$('<input type="hidden" />')
					.appendTo(this.form)
					.attr({
						'name': i
						, 'value': this.settings.data[i]
					});
			}
		},
		file_from_path: function(file) {
			return file.replace(/.*(\/|\\)/, "");
		},
		get_ext: function(file) {
			return (/[.]/.exec(file)) ? /[^.]+$/.exec(file.toLowerCase()) : '';
		},
		make_parent_opaque: function() {
			// ie transparent background bug
			this.button.add(this.button.parents()).each(function() {
				var color = $(this).css('backgroundColor');
				var image = $(this).css('backgroundImage');

				if (color != 'transparent' || image != 'none') {
					$(this).css('opacity', 1);
					return false;
				}
			});
		}

	};
})(jQuery);