/**
 * AvatarGeneratorUI
 * Copyright (c) 2010 Matthew Norris (http://www.avatar-generator.com)
 */
var AvatarGeneratorUI = Class.create({
	initialize: function(avatarID) {
		// constants, vars, etc
		
		
		this.CROP_HANDLE_WIDTH = 7;
		this.SEARCH_RESULTS_WIDTH = 4;
		this.SEARCH_RESULTS_BOX_SIZE = 190; // pixels
		this.MASK_DELAY = 100; // delay before calling callback after unmasking
		this.MAX_IMAGE_CONTAINER_WIDTH = 600;// = ImageProcessor.MAX_RESIZE_THUMB_WIDTH + 2
		this.IMAGE_PADDING = 0; // 10; // 10; // amount of padding in pixels round the image
		this.MIN_CROP_CONTAINER_WIDTH = 322; // 300 + 10 padding + 2 border
		this.RESIZE_IMAGE_WIDTH 	= 1;
		this.RESIZE_IMAGE_HEIGHT	= 2;
		// set to false whilst paging through results and true when something on the search form gets changed.
		this.newSearch = true; 
		this.searchRunning = false;
		this.currentTabScroller = null;
		this.avatarID = avatarID;
		this.numSteps = 6;
		this.currentStep = 1;
		// The divs to resize when the window resizes, along with the
		// height of the remaining divs combined and the minimum div height
		
		// This is the height of the viewport between the header and footer, in IE8 with google toolbar installed.
		var borderHeight = 1; // 
		this.numCropImagesLoaded = 0;
		
		// IMPORTANT
		/**
		 * The remaining div height is always 100 (the height of the header and footer) plus the height of whatever else there is.
		 * minHeight is the overall minimum height minus the height of any extra divs.
		 */
		// div_id: [remainingDivHeight, minimumHeight, maxHeight]
		
		var minWindowHeight = 560;
		
		this.resizeTargetDivs = $H({
			"loading_mask"						: [0, 					minWindowHeight],
			"content_container" 				: [120 + borderHeight,	minWindowHeight - 120],
			"content"							: [120 + borderHeight,	minWindowHeight - 120], 
			"search_results_container"			: [165 + borderHeight,	minWindowHeight - 165],
			"crop_left_panel"					: [165 + borderHeight,	minWindowHeight - 165],
			"crop_right_panel"					: [165 + borderHeight,	minWindowHeight - 165],
			"resize_image_panel"				: [200 + borderHeight,	minWindowHeight - 200],
			"resize_image_container_container"	: [200 + borderHeight,	minWindowHeight - 200],
			"add_border_left_panel"				: [165 + borderHeight,	minWindowHeight - 165],
			"add_border_right_panel"			: [165 + borderHeight,	minWindowHeight - 165],
			"add_border_gallery"				: [270 + borderHeight,	minWindowHeight - 270],
			"add_text_left_panel"				: [165 + borderHeight,	minWindowHeight - 165],
			"add_text_right_panel"				: [165 + borderHeight,	minWindowHeight - 165],
			"download_left_panel"				: [165 + borderHeight,	minWindowHeight - 165],
			"download_right_panel"				: [165 + borderHeight,	minWindowHeight - 165],
			"welcome_container"					: [130 + borderHeight,	minWindowHeight - 130]
		});
		this.resizeFunctions = [
			this.centerElement.curry("crop_left_panel", "crop_adjustment_form").bind(this),
			this.centerElement.curry("crop_right_panel", "crop_image_container").bind(this),
			this.centerElement.curry("resize_image_container_container", "resize_image_container").bind(this),
			this.centerElement.curry("loading_mask", "loading_mask_message").bind(this),
			this.centerElement.curry("add_border_right_panel", "add_border_image_container").bind(this),
			this.centerElement.curry("add_text_left_panel", "add_text_form_container").bind(this),
			this.centerElement.curry("add_text_right_panel", "add_text_image_container").bind(this),
			this.centerElement.curry("download_left_panel", "download_form_container").bind(this),
			this.centerElement.curry("download_right_panel", "download_ads_container").bind(this),
			this.centerElement.curry("welcome_container", "welcome_message", -5).bind(this),
			this.centerElement.curry("search_results_container", "choose_instructions").bind(this),
			this.moveColourPickers.bind(this)
		]
		
		// window resize event handler
		this.resizeDivs();
		Event.observe(window, "resize", this.resizeDivs.bindAsEventListener(this));
//		
		// tab event handlers
		$("content_container").scrollLeft = 0; // firefox remembers div scroll position when you refresh :S
		var i = 0;
		$$(".step_tab").each(function(element) {
			element.observe("click", this.gotoStep.curry(i).bindAsEventListener(this));
			element.observe("focus", function(event) {
				event.element().blur();
			});
			i++;
		}.bind(this))
		this.initWelcomer();
		this.initUploadBar(); // This includes search init.
		this.initCropper();
		this.initResizer();
		this.initBorderer();
		this.initTexter();
		this.initDownloader();
		this.initBackButtons();
		buttonsInit();
		Event.observe(document, "click", this.removeDownloadMenu.bind(this));
		Event.observe(window, "resize", this.removeDownloadMenu.bind(this));
	},
	
	/**
	 * addDownloadMenu - adds the download menu, accepts an element and callback function to be
	 * executed when the download menu item is clicked.
	 */
	addDownloadMenu: function(element, clickCallback) {
		var xPos = null;
		var yPos = null;
		var oldXPos = null;
		var oldYPos = null;
		this.observeLeftClick(element, function(event) {
			Event.extend(event);
			xPos = event.pointerX();
			yPos = event.pointerY();
			var newPos = (xPos != oldXPos || yPos != oldYPos) ? true : false;
			oldXPos = xPos;
			oldYPos = yPos;
			if ($("download_menu") == undefined || newPos) {
				if ($("download_menu") != undefined) // cleanup if menu is being repositioned
					$("download_menu").remove();
				var buf = 
					"<div id=\"download_menu\" style=\"" +
						"left: "+event.pointerX()+"px;" +
						"top: "+event.pointerY()+"px;" +
					"\">" +
						"Download now" +
					"</div>";
				Element.extend(document.body).insert(buf)
				$("download_menu").observe("click", clickCallback.bind(this));
				this.observeLeftClick($("download_menu"), function() {})
			}
			// When the window is resized, just get rid of the menu
		}.bind(this));
	},
	removeDownloadMenu: function() {
		if ($("download_menu") != undefined)
			$("download_menu").remove();
	},
	showDownloadPreview: function(callback) {
		this.genericSendAJAX("downloadPreview", {
			parameters: {},
			callback: function() {
				$("download_preview").update(
					"<img " +
						"id=\"download_preview_image\"" +
						"src=\"/uploaded_images/av_" + readCookie("session") + "_"+this.avatarID+"_download_preview.jpg?" + this.getRnd() + "\"" +
						"class=\"hasLayout\" />"
				);
				this.observeLeftClick($("download_preview_image"), function() {});
				$("download_preview_image").observe("load", callback.bind(this));
			}.bind(this)
		});
	},
	observeLeftClick: function(element, callback) {
		element.oncontextmenu = function(eventIn) {
			var eventFwd = (eventIn == undefined) ? event : eventIn; // it's undefined in IE, but the global "event" is not.
			callback(eventFwd);
			return false;
		}.bind(this)
	},
	initBackButtons: function() {
		var steps = ["crop", "resize", "border", "text", "download"];
		var offset = 2;
		var i;
		for (i = 0; i < steps.length; i++) {
			$("step_nav_"+steps[i]+"_back").observe("click", function(i) {
				if (!$("step_tab_6").hasClassName("disabled"))
					$("step_tab_6").addClassName("disabled");
				var stepNum = i;
				var j;
				for (j = stepNum-1; j > 0; j--) {
					if (!$("step_tab_"+j).hasClassName("disabled")) {
						this.gotoStep(j);
						break;
					}
				}
			}.curry(i+offset).bind(this));
		}
	},
	genericSendAJAX: function(command, options) {
		options.parameters.command 		= command
		options.parameters.avatar_id 	= this.avatarID;
		new Ajax.Request("/ajax.php?command="+command, {
			method: "post",
			parameters: options.parameters,
			onSuccess: function(transport) {
				this.genericReceiveAJAX(transport.responseText, options.callback)
			}.bind(this)
		});
	},
	genericReceiveAJAX: function(response, callback) {
		try {
			var responseObject = response.evalJSON();
			if (responseObject && responseObject.success)
				callback(responseObject);
			else if (responseObject && responseObject.error) // clean error
				this.showError(responseObject.error)
		} catch (e) {
			this.showError(response);
		}
			
	},
	moveColourPickers: function() {
		if ($("border_colour_picker_indicator").color != undefined)
			$("border_colour_picker_indicator").color.moveBox();
		if ($("text_colour_picker_indicator").color != undefined)
			$("text_colour_picker_indicator").color.moveBox();
	},
	disableButton: function(id) {
		$(id).hide();
		$(id+"_disabled").show();
	},
	enableButton: function(id) {
		$(id+"_disabled").hide();
		$(id).show();
	},
	mask: function(message) {
		
		$("loading_mask_message").update(message);
		$("loading_mask_container").show();
		this.centerElement("loading_mask", "loading_mask_message");
	},
	unMask: function() {
		$("loading_mask_container").hide();
	},
	showLoading: function(message) {
		var buf = 
			"<div id=\"error\">" +
				"<table cellpadding=\"0\" cellspacing=\"0\" width=\"100%\"><tr>" +
					"<td valign=\"top\"><img src=\"/images/spinner_mask.gif\" id=\"mask_icon\" /></td>" +
					"<td width=\"100%\">" + message + "</td>" + 
				"</tr></table>" +
			"</div>";
		this.mask(buf);
	},
	showError: function(message) {
		var buf = 
			"<div id=\"error\">" +
				"<table cellpadding=\"0\" cellspacing=\"0\" width=\"100%\"><tr>" +
					"<td valign=\"top\"><img src=\"/images/error.png\" id=\"mask_icon\" /></td>" +
					"<td width=\"100%\" valign=\"middle\">" +
						message + "<br />" +
					"</td>" + 
				"</tr><tr>" +
					"<td colspan=\"2\">" +
						"<div align=\"center\"><div id=\"dismiss_error\">Close</div></div>" +
					"</td>" +
				"</tr></table>" +
			"</div>";
		this.mask(buf);
		buttonsInit();
		$("dismiss_error").observe("click", this.unMask);
	},
	gotoStep: function(destStep, event, callback) {
		if (
			$("step_tab_"+destStep).hasClassName("selected")
			|| (
				event !== undefined
				&& event !== null
				&& (
					event.element().hasClassName("disabled")
					|| event.element().up().hasClassName("disabled") // in case the number gets clicked
				) // check needed for internal calls, where there is no event.
			)
		)
			return;
		if (this.currentTabScroller !== null)
			this.currentTabScroller.cancel();
		// update the tab classes
		$$(".step_tab").each(function(element) {
			element.removeClassName("selected");
		});
		$("step_tab_" + destStep).addClassName("selected");
		this.currentTabScroller = new TabScrollerUI(destStep, function() {
			this.currentStep = destStep;  // This is the onComplete function
			if (callback != undefined)
				callback();
		}.bind(this));
	},
	// disables tabs in front of the current step
	revertToStep: function(stepID) {
		var i;
		for (i = 1; i <= this.numSteps; i++) {
			if (i <= stepID)
				$("step_tab_"+i).removeClassName("disabled")
			if (i > stepID)
				$("step_tab_"+i).addClassName("disabled");
		}
	},
	// Window resize event handler
	// pair.key = id of target element
	// pair.value = combined height of remaining divs
	resizeDivs: function() {
		this.resizeTargetDivs.each(function(pair) {
			var newHeight = document.documentElement.clientHeight - pair.value[0];
			if (newHeight < pair.value[1])
				newHeight = pair.value[1]; // enforces minimum height
			if (newHeight > 0)
				$(pair.key).style.height = newHeight + "px";
		}.bind(this));
		this.resizeFunctions.each(function(f) {
			f();
		}.bind(this));
	},
	// Called when the window gets resized or when the crop image is first loaded
	centerElement: function (outer, inner, vertDiffIn, horizDiffIn) {
		if (!$(outer) || !$(inner))
			return;

		var vertDiff 	= (vertDiffIn != undefined) ? vertDiffIn : 0;
		var horizDiff 	= (horizDiffIn != undefined) ? horizDiffIn : 0;
		
		var outerPanel = $(outer);
		var innerPanel = $(inner)
		
		var innerPanelWidth = innerPanel.getWidth();
		var innerPanelHeight = innerPanel.getHeight();
		var outerPanelWidth = outerPanel.getWidth();
		var outerPanelHeight = outerPanel.getHeight();
		
		if (outerPanelHeight < innerPanelHeight) {
			var leftBase = 5;
			if (inner == "crop_image_container") {
			 	leftMargin = (innerPanelWidth < this.MAX_IMAGE_CONTAINER_WIDTH) 
			 		? Math.round((outerPanelWidth - innerPanelWidth) / 2) : leftBase;
			} else {
				leftMargin = Math.round((outerPanelWidth - innerPanelWidth) / 2) - this.IMAGE_PADDING;
			}
	 		topMargin = 0;
		} else {
			leftMargin = Math.round((outerPanelWidth - innerPanelWidth) / 2) - this.IMAGE_PADDING - horizDiff;
			topMargin = Math.round((outerPanelHeight - innerPanelHeight) / 2) - vertDiff;
		}
//		$("debug").insert("leftMargin: "+leftMargin+", topMargin: "+topMargin+"<br />");
		innerPanel.setStyle({
			"left"	: leftMargin + "px", 
			"top"	: topMargin + "px"
		})
		
	},
	
	// helper
	pixelBoxHandleKey: function(event) {
		// Increment or decrement the value.
		var keyOK = false;
		if 			(event.keyCode == 38) { // up arrow
			var v = parseInt(event.element().value);
			v = (isNaN(v)) ? 0 : v;
			event.element().value = v + 1;
			keyOK = true;
		} else if 	(event.keyCode == 40) { // down arrow
			var v = parseInt(event.element().value);
			v = (isNaN(v)) ? 0 : v;
			event.element().value = v - 1;
			keyOK = true;
		// These ones just do their default actions...
		} else if 	(event.keyCode >= 48 && event.keyCode <= 57) // number
			keyOK = true;
		else if 	(event.keyCode >= 96 && event.keyCode <= 105) // keypad number
			keyOK = true;
		else if 	(event.keyCode == 8 || event.keyCode == 46) { // backspace and delete
//				if (event.element().value.length <= 1) {
//					event.element().value = "0";
//					Event.stop(event);
//				}
			keyOK = true;
		}
		if (!keyOK) {
			Event.stop(event);
			return false;
		} else
			return true;
	},
	
	// ################################################################################################################################
	// ########################################################## WELCOMER ############################################################
	// ################################################################################################################################
	
	initWelcomer: function() {
		$("welcome_go_button").observe("click", function() {
			this.revertToStep(1);
			this.gotoStep(1);
		}.bind(this));
	},
	
	// ################################################################################################################################
	// ########################################################## UPLOAD BAR ##########################################################
	// ################################################################################################################################
	
	initUploadBar: function() {
		// Init google search object
		google.search.Search.getBranding($("google_branding"), google.search.Search.HORIZONTAL_BRANDING);
		this.imageSearch = new google.search.ImageSearch();
		this.imageSearch.setNoHtmlGeneration();
		
		// Init the size slider
		var sizeSlider = $("size_slider");
		new Control.Slider(sizeSlider.down('.handle'), sizeSlider, {
			range: $R(1, 4),
			values: [1, 2, 3, 4], 
			sliderValue: 2,
			onSlide: this.doSizeSlide.bind(this),
			onChange: this.doSizeSlide.bind(this),
			onStartDrag: function(handle) {
				handle.addClassName("pressed");
			},
			onFinishDrag: function(handle) {
				handle.removeClassName("pressed");
			}
			
		});
		this.searchImageSize = google.search.ImageSearch.IMAGESIZE_MEDIUM;

		// Init the search box and button		
		$("search_button").observe("click", this.searchButtonClick.bindAsEventListener(this));
		$("search_box").observe('keypress', function(event) {
			if (event.keyCode == Event.KEY_RETURN)
				this.doNewSearch();
		}.bind(this));
		
		// Init the "Upload an image" button.
		new AjaxUpload("upload_button", {
			action: "/ajax.php?command=upload&avatar_id="+this.avatarID,
			autoSubmit: true,
			onSubmit: function() {
				this.showLoading("Uploading...");
			}.bind(this),
			onComplete: function(file, response) {
				// generic receieve AJAX does some error checks
				this.genericReceiveAJAX(response, function(responseObject) {
					this.displayImageForCropping(responseObject);
				}.bind(this));
			}.bind(this)
		});
	},
	doNewSearch: function () {
		this.newSearch = true;
		this.searchRunning = true;
		this.doSearch();
	},
	getRnd: function() {
		return Math.floor(Math.random()*100000000);
	},
	loadingPanel:  
		"<tr id=\"results_loading_panel\">" +
			"<td colspan=\"4\" align=\"center\" valign=\"middle\" class=\"results_loading_panel_cell\">" +
				"<div id=\"results_loading_panel_cell_div\">" +
					"<img src=\"/images/spinner_search.gif\" />" + 
					"<div>Loading...</div>" +
				"</div>" +
			"</td>" +
		"</tr>",
	doSearch: function() {
		if ($("choose_instructions"))
			$("choose_instructions").remove();
		if (this.newSearch)
			$("search_results").update("");
		$("search_results").insert(this.loadingPanel);
		this.imageSearch.setResultSetSize(google.search.Search.LARGE_RESULTSET);
		this.imageSearch.setRestriction(google.search.ImageSearch.RESTRICT_IMAGESIZE, this.searchImageSize);
		this.imageSearch.setSearchCompleteCallback(this, this.searchOnComplete, [this.imageSearch]);
		this.imageSearch.execute($F("search_box"));
	},
	searchOnComplete: function() {
		// Remove the loading panel
		$("results_loading_panel").remove();
		if (this.imageSearch.results.length == 0) {
			$("search_results").insert(
				"<tr id=\"results_loading_panel\">" +
					"<td colspan=\"4\" align=\"center\" valign=\"middle\" id=\"no_results_panel_cell\">" +
						"<div id=\"no_results_panel_cell_div\">No results</div>" +
					"</td>" +
				"</tr>"
			);
		} else {
			// Add fresh rows of thumbnails
			var firstRow = (this.newSearch) ? "google_thumbnail_first" : "";
			this.newSearch = false;
			var alt = 1;
			var buf = "<tr>";
			for (i = 0; i < this.imageSearch.results.length; i++) {
				if (
					i % this.SEARCH_RESULTS_WIDTH == 0
					&& i != this.SEARCH_RESULTS_WIDTH - 1
					&& i != 0
				) {
					firstRow = "";
					buf += "</tr><tr>";
				}
				buf +=
					"<td " +
						"class=\"google_thumbnail "+firstRow+"\" " +
						"id=\"google_thumbnail_p"+this.imageSearch.cursor.currentPageIndex+"_e"+i+"\" " +
						"align=\"center\" valign=\"middle\">" +
						
						"<img src=\""+this.imageSearch.results[i].tbUrl+"\" /><br />" +
						this.imageSearch.results[i].width + " x " + this.imageSearch.results[i].height
					"</td>";
			}
			var remainder = this.imageSearch.results.length % this.SEARCH_RESULTS_WIDTH;
			if (remainder != 0)
				buf += "<td class=\"google_thumbnail_nohover\" colspan=\""+remainder+"\" align=\"center\" valign=\"middle\"></td>";
			buf += "</tr>";
			$("search_results").insert(buf);
			
			// Attach click event handlers to the thumbnails
			for (i = 0; i < this.imageSearch.results.length; i++) {
				$("google_thumbnail_p"+this.imageSearch.cursor.currentPageIndex+"_e"+i).observe("click", function(url) {
					this.showLoading("Fetching...");
					this.genericSendAJAX("fetch", {
						parameters: {
							url: url
						},
						callback: function(responseObject) {
							this.displayImageForCropping(responseObject);
						}.bind(this)
					});
				}.curry(this.imageSearch.results[i].unescapedUrl).bind(this));
			}
			var nextPage = this.imageSearch.cursor.currentPageIndex + 1;
	
			// Add the more results button to the end of the table.
			buf = "";
			var moreResultsExist = null
			if (this.imageSearch.cursor.pages[nextPage] !== undefined) {
				moreResultsExist = true;
				buf += 
					"<tr id=\"more_results_button\" align=\"center\" valign=\"middle\">" +
						"<td colspan=\"" + this.SEARCH_RESULTS_WIDTH + "\"><div>More results</div></td>" +
					"</tr>";
			} else {
				moreResultsExist = false;
				buf += 
					"<tr id=\"more_results_button\" align=\"center\" valign=\"middle\">" +
						"<td colspan=\"" + this.SEARCH_RESULTS_WIDTH + "\">" +
							"<div>Find even more on Google</div>" + 
						"</td>" +
					"</tr>";
			}
			$("search_results").insert(buf);
			
			// Scroll to the bottom of the page.
			$("search_results_container").scrollTop = $("search_results_container").scrollHeight;
			
			moreButton = $("more_results_button");
			moreButton.observe("click", function() {
				if (moreResultsExist) {
					var moreButton = $("more_results_button");
					moreButton.stopObserving();
					moreButton.remove();
					$("search_results").insert(this.loadingPanel);
					this.imageSearch.gotoPage(nextPage);
				} else
					window.open(this.imageSearch.cursor.moreResultsUrl); 
			}.bind(this));
		}
		this.searchRunning = false;
	},
	searchButtonClick: function() {
		if (
			$F("search_box").replace(/^\s*/, "").replace(/\s*$/, "") // make sure there is a search term
			&& !this.searchRunning // make sure the user hasn't already submitted a search...
		)
			this.doNewSearch();
	},
	doSizeSlide: function(value) {
		this.newSearch = true;
		// Read the slider value and convert it into a google variable
		switch (value) {
			case 1:
				this.searchImageSize = google.search.ImageSearch.IMAGESIZE_SMALL;
				$("size_description").update("Size: Small"); 
				break;
			case 2:
				this.searchImageSize = google.search.ImageSearch.IMAGESIZE_MEDIUM;
				$("size_description").update("Size: Medium");
				break;
			case 3:
				this.searchImageSize = google.search.ImageSearch.IMAGESIZE_LARGE;
				$("size_description").update("Size: Large");
				break;
			case 4:
				this.searchImageSize = google.search.ImageSearch.IMAGESIZE_EXTRA_LARGE;
				$("size_description").update("Size: Extra Large");
				break;
		}
	},
	
	// #############################################################################################################################
	// ########################################################## CROPPER ##########################################################
	// #############################################################################################################################
	
	// Called by the search / upload UI, does what it says 
	displayImageForCropping: function(imageProperties) {
		this.revertToStep(2);
		this.originalDimensions = {
			w: imageProperties.originalWidth,
			h: imageProperties.originalHeight
		};
		this.displayDimensions = {
			w: imageProperties.displayWidth,
			h: imageProperties.displayHeight
		}
		this.numCropImagesLoaded = 0;
		var existingImage = $("crop_image");
		if (existingImage != undefined)
			existingImage.remove();
		// min width = 300px;
		$("crop_image_container").update(
			"<div id=\"crop_image_instructions\" class=\"hasLayout\">" +
				"Click and drag on the picture to select the area to crop. To make a square, " +
				"hold down <b>&lt;shift&gt;</b>. Press <b>&ldquo;Next&rdquo;</b> when you have finished." + 
			"</div>" + 
			"<div id=\"crop_image_wrapper\" class=\"hasLayout\" " +
				"style=\"width: "+imageProperties.displayWidth+"px; height: "+imageProperties.displayHeight+"px;\">" +
				"<img " +
					"id=\"crop_image\"" +
					"src=\"/uploaded_images/av_" + readCookie("session") + "_"+this.avatarID+"_source_display.jpg?" + this.getRnd() + "\"" +
					"class=\"hasLayout\" />" +
			"</div>" +
			"<div id=\"crop_image_dimensions\" class=\"hasLayout\">" +
				this.originalDimensions.w + " x " + this.originalDimensions.h + " px" + 
			"</div>"
		);
		this.observeLeftClick($("crop_image"), function() {});
		var w = ((imageProperties.displayWidth + 12) < this.MIN_CROP_CONTAINER_WIDTH)
			? (this.MIN_CROP_CONTAINER_WIDTH + 10 + 12)
			: (imageProperties.displayWidth + 12);
		$("crop_image_container").setStyle({"width": w+"px"});
		// Init the cropper drawing event handlers - just one
		var container = $("crop_image_wrapper");
		container.stopObserving(); 
		container.observe("mousedown", this.cropperMouseDown.bindAsEventListener(this));
		
		// Only scroll over once both images have finished loading.
		$("crop_image").observe("load", function() {
			this.resetCropper();
			this.centerElement("crop_right_panel", "crop_image_container");
			this.unMask();
			this.gotoStep(2);
		}.bind(this));
	},
	
	initCropper: function() {
		this.oldCropDimensions = null;
		this.resetCropper();
		// Init the cropper moving event handlers. Shares its mouseDown with the cropperMouseDown
		document.observe("mousemove", this.moverMouseMove.bindAsEventListener(this));
		document.observe("mouseup", this.moverMouseUp.bindAsEventListener(this));
		// Cropper handle mousedowns are added elsewhere
		document.observe("mousemove", this.handleMouseMove.bindAsEventListener(this)); // Cropper handles
		document.observe("mouseup", this.handleMouseUp.bindAsEventListener(this));
		
		var clearIfDashed = function() {
			if (this.value == "-" && this.hasCrop)
				this.clear();
		}
		// Initialize the boxes where you type the dimensions
		$("crop_top_left_x").observe("click", clearIfDashed);
		$("crop_top_left_y").observe("click", clearIfDashed);
		$("crop_bottom_right_x").observe("click", clearIfDashed);
		$("crop_bottom_right_y").observe("click", clearIfDashed);
		$("crop_width").observe("click", clearIfDashed);
		$("crop_height").observe("click", clearIfDashed);
		
		// Initialize the "Next >" button...
		$("step_nav_crop_next").observe("click", this.cropperNext.bindAsEventListener(this));
		
		var delayedDimensionsUpdate = function(event) {
			var f = function(element) {
				this.updateInternalCropDimensions(element, true);
			}.bind(this)
			f.curry(event.element()).defer();
		}.bind(this);
		$("crop_top_left_x").observe("keydown", function(event) {
			if(this.hasCrop)
				this.addZIndexesToHandles("w");
			if (this.pixelBoxHandleKey(event))
				delayedDimensionsUpdate(event);
		}.bind(this));
		$("crop_top_left_y").observe("keydown", function(event) {
			if(this.hasCrop)
				this.addZIndexesToHandles("n");
			if (this.pixelBoxHandleKey(event))
				delayedDimensionsUpdate(event);
		}.bind(this));
		$("crop_bottom_right_x").observe("keydown", function(event) {
			if(this.hasCrop)
				this.addZIndexesToHandles("e");
			if (this.pixelBoxHandleKey(event))
				delayedDimensionsUpdate(event);
		}.bind(this));
		$("crop_bottom_right_y").observe("keydown", function(event) {
			if(this.hasCrop)
				this.addZIndexesToHandles("s");
			if (this.pixelBoxHandleKey(event))
				delayedDimensionsUpdate(event);
		}.bind(this));
		$("crop_width").observe("keydown", function(event) { // corner update code goes in these two...
			if (this.pixelBoxHandleKey(event)) {
				var f = function(element) {
					// clip
					if (element.value < 1)
						element.value = 1;
					else if (element.value > this.originalDimensions.w)
						element.value = this.originalDimensions.w;
					// update the corners
					var widthDiff = element.value - ($("crop_bottom_right_x").value - $("crop_top_left_x").value);
					var topLeftX 		= $("crop_top_left_x");
					var bottomRightX 	= $("crop_bottom_right_x");
					
					var isOdd = element.value % 2;
					var swap = (widthDiff < 0) ? !isOdd : isOdd;
					var sign = (widthDiff < 0) ? -1 : 1;
					var baseIncrement = Math.floor(Math.abs(widthDiff) / 2);
					var incrementLeft, incrementRight;
					if (widthDiff == 0)
						incrementLeft = incrementRight = 0;
					else { 
						incrementLeft 	= baseIncrement + ((swap) ? 1 : 0);
						incrementRight 	= baseIncrement + ((swap) ? 0 : 1);
					}
					topLeftX.value 		= parseInt(topLeftX.value) - (sign * incrementLeft);
					bottomRightX.value 	= parseInt(bottomRightX.value) + (sign * incrementRight);
					
					// Now handle overflow situations...
					if (topLeftX.value < 0) {
						var diff = 0 - topLeftX.value;
						topLeftX.value = 0;
						bottomRightX.value = parseInt(bottomRightX.value) + diff;
						if (bottomRightX.value > this.originalDimensions.w)
							bottomRightX.value = this.originalDimensions.w;
					}
					if (bottomRightX.value > this.originalDimensions.w) {
						var diff = this.originalDimensions.w - bottomRightX.value;
						bottomRightX.value = this.originalDimensions.w;
						topLeftX.value = parseInt(topLeftX.value) + diff;
						if (topLeftX.value < 0)
							topLeftX.value = 0;
					}
					this.updateInternalCropDimensions(element, false);
				}.bind(this)
				f.curry(event.element()).defer();
			}
		}.bind(this)); 
		$("crop_height").observe("keydown", function(event) {
			if (this.pixelBoxHandleKey(event)) {
				var f = function(element) {
					// clip
					if (element.value < 1)
						element.value = 1;
					else if (element.value > this.originalDimensions.h)
						element.value = this.originalDimensions.h;
					// update the corners
					var widthDiff = element.value - ($("crop_bottom_right_y").value - $("crop_top_left_y").value);
					var topLeftY 		= $("crop_top_left_y");
					var bottomRightY 	= $("crop_bottom_right_y");
					
					var isOdd = element.value % 2;
					var swap = (widthDiff < 0) ? !isOdd : isOdd;
					var sign = (widthDiff < 0) ? -1 : 1;
					var baseIncrement = Math.floor(Math.abs(widthDiff) / 2);
					var incrementLeft, incrementRight;
					if (widthDiff == 0)
						incrementLeft = incrementRight = 0;
					else { 
						incrementLeft 	= baseIncrement + ((swap) ? 1 : 0);
						incrementRight 	= baseIncrement + ((swap) ? 0 : 1);
					}
					topLeftY.value 		= parseInt(topLeftY.value) - (sign * incrementLeft);
					bottomRightY.value 	= parseInt(bottomRightY.value) + (sign * incrementRight);
					
					// Now handle overflow situations...
					if (topLeftY.value < 0) {
						var diff = 0 - topLeftY.value;
						topLeftY.value = 0;
						bottomRightY.value = parseInt(bottomRightY.value) + diff;
						if (bottomRightY.value > this.originalDimensions.h)
							bottomRightY.value = this.originalDimensions.h;
					}
					if (bottomRightY.value > this.originalDimensions.h) {
						var diff = this.originalDimensions.h - bottomRightY.value;
						bottomRightY.value = this.originalDimensions.h;
						topLeftY.value = parseInt(topLeftY.value) + diff;
						if (topLeftY.value < 0)
							topLeftY.value = 0;
					}
					this.updateInternalCropDimensions(element, false);
				}.bind(this)
				f.curry(event.element()).defer();
			}
		}.bind(this));
	},
	// Updates internal crop dimensions from the form elements - but only if they're valid...
	updateInternalCropDimensions: function(modifiedElement, corner) {
		this.revertToStep(2);
		var topLeftX 		= parseInt($("crop_top_left_x").value);
		var topLeftY 		= parseInt($("crop_top_left_y").value);
		var bottomRightX 	= parseInt($("crop_bottom_right_x").value);
		var bottomRightY 	= parseInt($("crop_bottom_right_y").value);
		var width 			= parseInt($("crop_width").value);
		var height 			= parseInt($("crop_height").value);
		topLeftX 		= isNaN(topLeftX) ? 0 : topLeftX;
		topLeftY 		= isNaN(topLeftY) ? 0 : topLeftY;
		bottomRightX 	= isNaN(bottomRightX) ? 0 : bottomRightX;
		bottomRightY 	= isNaN(bottomRightY) ? 0 : bottomRightY;
		width 			= isNaN(width) ? 0 : width;
		height 			= isNaN(height) ? 0 : height;
//			console.log("if (["+topLeftX+"] < ["+bottomRightX+"] && ["+topLeftY+"] < ["+bottomRightY+"])");
		if (
			topLeftX < bottomRightX 
			&& topLeftY < bottomRightY
			&& 0 <= topLeftX && topLeftX < this.originalDimensions.w 
			&& 0 <= topLeftY && topLeftY < this.originalDimensions.h
			&& 0 < bottomRightX && bottomRightX <= this.originalDimensions.w
			&& 0 < bottomRightY && bottomRightY <= this.originalDimensions.h
		) {
			// apply width/height adjustment correction...
			var xScale = this.displayDimensions.w / this.originalDimensions.w;
			var yScale = this.displayDimensions.h / this.originalDimensions.h;
			
			this.cropDimensions.topLeftX 		= Math.round(topLeftX * xScale);
			this.cropDimensions.topLeftY 		= Math.round(topLeftY * yScale);
			this.cropDimensions.bottomRightX	= Math.round(bottomRightX * xScale);
			this.cropDimensions.bottomRightY 	= Math.round(bottomRightY * yScale);
			
			if (corner) {
				var width 	= bottomRightX - topLeftX;
				var height	= bottomRightY - topLeftY;
				$("crop_width").value 	= (width < 1) ? 1 : width;
				$("crop_height").value 	= (height < 1) ? 1 : height;
			} 
			
			this.evalCropDimensions(null, true);
			this.drawCropperHandles();
			// text box error css classes are removed when needed in evalCropDimensions...
		} else {
			if (modifiedElement.id != "crop_width" && modifiedElement.id != "crop_height")
				modifiedElement.addClassName("pixel_box_error");
		}
	},
	
	resetCropper: function() {
		this.disableButton("step_nav_crop_next");
		this.cropperActive = false;
		this.moverActive = false;
		this.handleActive = null;
		this.hasCrop = false;
		this.oldCropWidth = null;
		this.oldCropHeight = null;
		this.cropDimensions = {
			topLeftX: null,
			topLeftY: null,
			bottomRightX: null,
			bottomRightY: null
		}
		
		this.removeIfExists("crop_bounding_box");
		this.removeIfExists("crop_top_flank");
		this.removeIfExists("crop_left_flank");
		this.removeIfExists("crop_right_flank");
		this.removeIfExists("crop_bottom_flank");
		$("crop_top_left_x").value 		= "-";
		$("crop_top_left_y").value 		= "-"; 
		$("crop_bottom_right_x").value 	= "-";
		$("crop_bottom_right_y").value	= "-";
		$("crop_width").value 			= "-";
		$("crop_height").value			= "-";
		this.removeCropperHandles();
		var wrapper = $("crop_image_wrapper");
		if (wrapper != undefined)
			wrapper.setStyle({"cursor": "crosshair"});
		$("crop_top_left_x").disable();
		$("crop_top_left_y").disable();
		$("crop_bottom_right_x").disable();
		$("crop_bottom_right_y").disable();
		$("crop_width").disable();
		$("crop_height").disable();
	},
	cropperMouseDown: function(event) {
		this.revertToStep(2);
		var coord = this.getCropperMouseCoord(event);
		if (this.hasCrop
			&& coord.x >= this.cropDimensions.topLeftX
			&& coord.x <= this.cropDimensions.bottomRightX
		 	&& coord.y >= this.cropDimensions.topLeftY
			&& coord.y <= this.cropDimensions.bottomRightY
		) {
			// Moving an existing box
			this.moverMouseDown(coord);
			Event.stop(event);
		}
		else {
			// We're drawing a new box, so initialise it.
			this.resetCropper();
			this.cropDimensions = { 
				topLeftX: coord.x,
				topLeftY: coord.y,
				bottomRightX: coord.x + 1,
				bottomRightY: coord.y + 1
			}
			this.newDrag = true;
			this.newSquare = true;
			this.showHandles = false;
			this.handleMouseDown("se", event);
			$("crop_top_left_x").enable();
			$("crop_top_left_y").enable();
			$("crop_bottom_right_x").enable();
			$("crop_bottom_right_y").enable();
			$("crop_width").enable();
			$("crop_height").enable();
		}
	},
	
	moverMouseDown: function(coord) {
		this.revertToStep(2);
		this.moverActive = true; // drag and mouseup handled by moverMouseMove and moverMouseUp
		this.moverTopLeftCornerDiff = {
			x: coord.x - this.cropDimensions.topLeftX,
			y: coord.y - this.cropDimensions.topLeftY
		}
	},
	moverMouseMove: function(event) {
		if (this.moverActive) {
			var coord 	= this.getCropperMouseCoord(event);
			var cropImage = $("crop_image");
			var imageWidth = cropImage.width;
			var imageHeight = cropImage.height;
			
			var boxWidth 	= this.cropDimensions.bottomRightX - this.cropDimensions.topLeftX;
			var boxHeight 	= this.cropDimensions.bottomRightY - this.cropDimensions.topLeftY;
			
			// calculate new box coordinates
			this.cropDimensions.topLeftX 		= coord.x - this.moverTopLeftCornerDiff.x;
			this.cropDimensions.topLeftY 		= coord.y - this.moverTopLeftCornerDiff.y;
			this.cropDimensions.bottomRightX 	= coord.x - this.moverTopLeftCornerDiff.x + boxWidth;
			this.cropDimensions.bottomRightY 	= coord.y - this.moverTopLeftCornerDiff.y + boxHeight;
			
			// restrict to image boundaries
			if (this.cropDimensions.topLeftX < 0) {
				this.cropDimensions.topLeftX = 0;
				this.cropDimensions.bottomRightX = boxWidth;
			}
			if (this.cropDimensions.topLeftY < 0) {
				this.cropDimensions.topLeftY = 0;
				this.cropDimensions.bottomRightY = boxHeight;
			}
			if (this.cropDimensions.bottomRightX > imageWidth) {
				this.cropDimensions.bottomRightX = imageWidth;
				this.cropDimensions.topLeftX = imageWidth - boxWidth;
			}
			if (this.cropDimensions.bottomRightY > imageHeight) {
				this.cropDimensions.bottomRightY = imageHeight;
				this.cropDimensions.topLeftY = imageHeight - boxHeight;
			}
			
			this.evalCropDimensions();
			this.drawCropperHandles();
			Event.stop(event);
		}
	},
	moverMouseUp: function(event) {
		if (this.moverActive) {
			this.moverActive = false;
			Event.stop(event); 
		}
	},
	drawCropperHandles: function() {
		if (this.showHandles) {
			var addHandleIfMissing = function(target) { // helper
				var element = $(target);
				if (element == undefined) {
					$("crop_image_wrapper").insert("<div id=\""+target+"\" class=\"crop_handle\"><!-- --></div>");
					element = $(target);
					var directionID = target.split("_").pop();
					element.observe("mousedown", this.handleMouseDown.curry(directionID).bindAsEventListener(this));
				}
				return element;
			}.bind(this);
			if (
				this.cropDimensions.topLeftX != undefined,
				this.cropDimensions.topLeftY != undefined,
				this.cropDimensions.bottomRightX != undefined,
				this.cropDimensions.bottomRightY != undefined
			) {
				var offset = ((this.CROP_HANDLE_WIDTH - 1) / 2) + 1;
				var width = this.cropDimensions.bottomRightX - this.cropDimensions.topLeftX;
				var height = this.cropDimensions.bottomRightY - this.cropDimensions.topLeftY;
				var halfWidth = Math.round(width / 2);
				var halfHeight = Math.round(height / 2);
				
				addHandleIfMissing("crop_handle_n").setStyle({
					"left": (this.cropDimensions.topLeftX + halfWidth - offset - 1)+"px",
					"top": (this.cropDimensions.topLeftY - offset)+"px"
				});
				addHandleIfMissing("crop_handle_ne").setStyle({
					"left": (this.cropDimensions.bottomRightX - offset - 1)+"px",
					"top": (this.cropDimensions.topLeftY - offset)+"px"
				});
				addHandleIfMissing("crop_handle_e").setStyle({
					"left": (this.cropDimensions.bottomRightX - offset - 1)+"px",
					"top": (this.cropDimensions.topLeftY + halfHeight - offset - 1)+"px"
				});
				addHandleIfMissing("crop_handle_se").setStyle({
					"left": (this.cropDimensions.bottomRightX - offset - 1)+"px",
					"top": (this.cropDimensions.bottomRightY - offset - 1)+"px"
				});
				addHandleIfMissing("crop_handle_s").setStyle({
					"left": (this.cropDimensions.topLeftX + halfWidth - offset - 1)+"px",
					"top": (this.cropDimensions.bottomRightY - offset - 1)+"px"
				});
				addHandleIfMissing("crop_handle_sw").setStyle({
					"left": (this.cropDimensions.topLeftX - offset)+"px",
					"top": (this.cropDimensions.bottomRightY - offset - 1)+"px"
				});
				addHandleIfMissing("crop_handle_w").setStyle({
					"left": (this.cropDimensions.topLeftX - offset)+"px",
					"top": (this.cropDimensions.topLeftY + halfHeight - offset - 1)+"px"
				});
				addHandleIfMissing("crop_handle_nw").setStyle({
					"left": (this.cropDimensions.topLeftX - offset)+"px",
					"top": (this.cropDimensions.topLeftY - offset)+"px"
				});
				
				// Apply z-indexes.
				if (this.handleActive)
					this.addZIndexesToHandles(this.handleActive)
			}
		}
	},
	cropperZIndexes: $H({ // Helper variables for addZIndexesToHandles
		nw: [8, 7, 6,
			 7,    5,
			 6, 5, 4],
			 
		n: 	[6, 7, 6,
			 5,    5,
			 4, 4, 4],
			 
		ne: [6, 7, 8,
			 5,    7,
			 4, 5, 6],
			 
		e: 	[4, 5, 6,
			 4,    7,
			 4, 5, 6],
			 
		se: [4, 5, 6,
			 5,    7,
			 6, 7, 8],
			 
		s: 	[4, 4, 4,
			 5,    5,
			 6, 7, 6],
			 
		sw: [6, 5, 4,
			 7,    5,
			 8, 7, 6],
			 
		w: 	[6, 5, 4,
			 7,    4,
			 6, 5, 4]
	}),
	handleNamesMatrix: 
		["nw", 	"n", 	"ne",
		 "w",			"e",
		 "sw",	"s",	"se"],
	addZIndexesToHandles: function(handleID) {
		var i;
		var l = this.handleNamesMatrix.length;
		for (i = 0; i < l; i++) {
			$("crop_handle_"+this.handleNamesMatrix[i]).setStyle(
				"z-index: " + this.cropperZIndexes.get(handleID)[i]
			);
		}
	},
	/**
	 * These handleMouseX functions are the most complicated and niggly bit of the whole thing :S
	 */
	handleMouseDown: function(handleID, event) {
		this.revertToStep(2);
		this.handleActive = handleID;
		this.evalCropDimensions();
		this.drawCropperHandles();
		this.setHandleActive(handleID);
		var cropImage = $("crop_image");
		var coord = this.getCropperMouseCoord(event);
		var width = this.cropDimensions.bottomRightX - this.cropDimensions.topLeftX;
		var height = this.cropDimensions.bottomRightY - this.cropDimensions.topLeftY;
		var halfWidth = Math.round(width / 2);
		var halfHeight = Math.round(height / 2);
		
		// This ensures the right kind of dragging action when you're at the edges
//		if (height == 1 && this.cropDimensions.bottomRightY == cropImage.height) {
//			switch (handleID) {
//				case "sw": 	this.setHandleActive("nw"); break;
//				case "s": 	this.setHandleActive("n"); break;
//				case "se": 	this.setHandleActive("ne"); break;
//			}
//		} else if (width == 1 && this.cropDimensions.bottomRightX == cropImage.width) {
//			switch (handleID) {
//				case "ne": 	this.setHandleActive("nw"); break;
//				case "e": 	this.setHandleActive("w"); break;
//				case "se": 	this.setHandleActive("sw"); break;
//			}
//		}
		switch(handleID) {
			case "nw": this.handleDiff = {
					x: coord.x - this.cropDimensions.topLeftX,
					y: coord.y - this.cropDimensions.topLeftY
				}; break;
			case "n": this.handleDiff = {
					y: coord.y - this.cropDimensions.topLeftY
				}; break;
			case "ne": this.handleDiff = {
					x: coord.x - this.cropDimensions.bottomRightX + 1,
					y: coord.y - this.cropDimensions.topLeftY
				}; break;
			case "e": this.handleDiff = {
					x: coord.x - this.cropDimensions.bottomRightX + 1
				}; break;
			case "se": this.handleDiff = {
					x: coord.x - this.cropDimensions.bottomRightX + 1,
					y: coord.y - this.cropDimensions.bottomRightY + 1
				}; break;
			case "s": this.handleDiff = {
					y: coord.y - this.cropDimensions.bottomRightY + 1
				}; break;
			case "sw": this.handleDiff = {
					x: coord.x - this.cropDimensions.topLeftX,
					y: coord.y - this.cropDimensions.bottomRightY + 1
				}; break;
			case "w": this.handleDiff = {
					x: coord.x - this.cropDimensions.topLeftX
				}; break;

		}
		Event.stop(event);
	},
	setHandleActive: function(handleID) {
		this.handleActive = handleID;
		var handleElement = $("crop_handle_" + handleID);
		if (handleElement != undefined) {
			var cursor = handleElement.getStyle("cursor");
			setCursorIfExists("crop_top_flank", cursor);
			setCursorIfExists("crop_right_flank", cursor);
			setCursorIfExists("crop_bottom_flank", cursor);
			setCursorIfExists("crop_left_flank", cursor);
			setCursorIfExists("crop_image_wrapper", cursor);
		} else {
			// set to crosshair.
		}
		function setCursorIfExists(id, cursor) {
			var div = $(id);
			if (div != undefined)
				div.setStyle({"cursor": cursor});
		}
	},
	
	handleMouseMove: function(event) { // A Balrog dwelleth here
		if (this.handleActive != null) {
			this.newDrag = false;
			var noSwitch = false;
			var temp = null;
			var coord = this.getCropperMouseCoord(event);
			var cropImage = $("crop_image");
			
			// first, ensure X does not exceed the image width
			var bX = coord.x + 1 - this.handleDiff.x; // for comparison with or setting into the bottom right hand corner
			var bY = coord.y + 1 - this.handleDiff.y;
			var tX = coord.x - this.handleDiff.x; // for comparison with or setting into the top left hand corner
			var tY = coord.y - this.handleDiff.y;
//			console.log("bX = ["+bX+"], tX = ["+tX+"], bY = ["+bY+"], tY = ["+tY+"]");
			
			// Perform clipping / manipulation of the cursor coordinates
			
			// Pre-clipping
			if (this.cropDimensions.bottomRightX == cropImage.width && tX > cropImage.width - 1)
				tX = cropImage.width - 1;
			if (this.cropDimensions.bottomRightY == cropImage.height && tY > cropImage.height - 1)
				tY = cropImage.height - 1;
			if (this.cropDimensions.topLeftX == 0 && bX < 1)
				bX = 1;
			if (this.cropDimensions.topLeftY == 0 && bY < 1)
				bY = 1;
			
			if (event.shiftKey) {
				// This code forces a square and makes sure it falls within the image boundaries
				var clip;
				var bW = bX - this.cropDimensions.topLeftX;
				var bH = bY - this.cropDimensions.topLeftY;
				var tW = this.cropDimensions.bottomRightX - tX;
				var tH = this.cropDimensions.bottomRightY - tY;
				
				
				// at the beginning, work out which direction the square is going in
				var smallSquareSize = 16; // a way off fuzzing the start area outward a bit
				if (
					this.newSquare == true
					&& this.cropDimensions.bottomRightX - this.cropDimensions.topLeftX <= smallSquareSize
					&& this.cropDimensions.bottomRightY - this.cropDimensions.topLeftY <= smallSquareSize
				) {
					if (
						bX > this.cropDimensions.bottomRightX
						&& tY < this.cropDimensions.topLeftY
					)
						this.setHandleActive("ne");						
					else if (
						bX > this.cropDimensions.bottomRightX
						&& bY > this.cropDimensions.bottomRightY
					)
						this.setHandleActive("se");
					else if (
						tX < this.cropDimensions.topLeftX
						&& bY > this.cropDimensions.bottomRightY
					)
						this.setHandleActive("sw");
					else if (
						tX > this.cropDimensions.topLeftX
						&& tY > this.cropDimensions.topRightY
					)
						this.setHandleActive("nw");
				} else
					this.newSquare = false;
				
				switch (this.handleActive) {
					case "nw":
						if (tH > tW)
							tX = this.cropDimensions.bottomRightX - tH;
						else
							tY = this.cropDimensions.bottomRightY - tW;
						if (tX < 0) {
							clip = tX;
							tX = 0;
							tY -= clip; 
						}
						if (tY < 0) {
							clip = tY;
							tY = 0;
							tX -= clip; 
						}
						bX = tX + 1;
						bY = tY + 1;
						break;
						
					case "ne":
						if (tH > bW)
							bX = this.cropDimensions.topLeftX + tH;
						else
							tY = this.cropDimensions.bottomRightY - bW;
						if (bX >= cropImage.width) {
							clip = bX - cropImage.width;
							bX = cropImage.width;
							tY += clip; 
						}
						if (tY < 0) {
							clip = tY;
							tY = 0;
							bX += clip; 
						}
						tX = bX - 1;
						bY = tY + 1;
						break;
						
					case "se":
						if (bH > bW)
							bX = this.cropDimensions.topLeftX + bH;
						else 
							bY = this.cropDimensions.topLeftY + bW;
						if (bX >= cropImage.width) {
							clip = bX - cropImage.width;
							bX = cropImage.width;
							bY -= clip; 
						}
						if (bY >= cropImage.height) {
							clip = bY - cropImage.height;
							bY = cropImage.height;
							bX -= clip; 
						}
						tX = bX - 1;
						tY = bY - 1;
						break;
						
					case "sw":
						if (bH > tW)
							tX = this.cropDimensions.bottomRightX - bH;
						else
							bY = this.cropDimensions.topLeftY + tW;
						if (tX < 0) {
							clip = tX;
							tX = 0;
							bY += clip; 
						}
						if (bY >= cropImage.height) {
							clip = bY - cropImage.height;
							bY = cropImage.height;
							tX += clip; 
						}
						
						bX = tX + 1;
						tY = bY - 1;
						break;
				}
			} else {
				this.newSquare = false;
				// Just clip according to the image boundaries as it's rectangle drawing tiem.
				if (bX > cropImage.width)
					bX = cropImage.width;
				if (bY > cropImage.height)
					bY = cropImage.height;
				if (tX < 0)
					tX = 0;
				if (tY < 0)
					tY = 0;
				
			}

			switch (this.handleActive) {
				case "nw":
					if (bX > this.cropDimensions.bottomRightX && bY > this.cropDimensions.bottomRightY)
						this.setHandleActive("se");
					else if (bX > this.cropDimensions.bottomRightX)
						this.setHandleActive("ne");
					else if (bY > this.cropDimensions.bottomRightY)
						this.setHandleActive("sw");
					if (bX > this.cropDimensions.bottomRightX) {
						temp = this.cropDimensions.bottomRightX;
						this.cropDimensions.bottomRightX = bX;
						this.cropDimensions.topLeftX = temp;
					} else
						this.cropDimensions.topLeftX = tX;
					if (bY > this.cropDimensions.bottomRightY) {
						temp = this.cropDimensions.bottomRightY;
						this.cropDimensions.bottomRightY = bY;
						this.cropDimensions.topLeftY = temp;
					} else
						this.cropDimensions.topLeftY = tY;
					
					break;
					
				case "n":
					if (bY > this.cropDimensions.bottomRightY) {
						this.setHandleActive("s");
						temp = this.cropDimensions.bottomRightY;
						this.cropDimensions.bottomRightY = bY;
						this.cropDimensions.topLeftY = temp;
					} else
						this.cropDimensions.topLeftY = tY;
					break;
					
				case "ne":
					if (tX < this.cropDimensions.topLeftX && bY > this.cropDimensions.bottomRightY)
						this.setHandleActive("sw");
					else if (tX < this.cropDimensions.topLeftX)
						this.setHandleActive("nw");
					else if (bY > this.cropDimensions.bottomRightY)
						this.setHandleActive("se");
					if (tX < this.cropDimensions.topLeftX) {
						temp = this.cropDimensions.topLeftX;
						this.cropDimensions.topLeftX = tX;
						this.cropDimensions.bottomRightX = temp;
					} else
						this.cropDimensions.bottomRightX = bX;
					if (bY > this.cropDimensions.bottomRightY) {
						temp = this.cropDimensions.bottomRightY;
						this.cropDimensions.bottomRightY = bY;
						this.cropDimensions.topLeftY = temp;
					} else
						this.cropDimensions.topLeftY = tY;
					break;
					
				case "e":
					if (tX < this.cropDimensions.topLeftX) {
						this.setHandleActive("w");
						temp = this.cropDimensions.topLeftX;
						this.cropDimensions.topLeftX = tX;
						this.cropDimensions.bottomRightX = temp;
					} else
						this.cropDimensions.bottomRightX = bX;
					break;
					
				case "se":
					if (tX < this.cropDimensions.topLeftX && tY < this.cropDimensions.topLeftY)
						this.setHandleActive("nw");
					else if (tX < this.cropDimensions.topLeftX)
						this.setHandleActive("sw");
					else if (tY < this.cropDimensions.topLeftY)
						this.setHandleActive("ne");
					if (tX < this.cropDimensions.topLeftX) {
						temp = this.cropDimensions.topLeftX;
						this.cropDimensions.topLeftX = tX;
						this.cropDimensions.bottomRightX = temp;
					} else
						this.cropDimensions.bottomRightX = bX;
					if (tY < this.cropDimensions.topLeftY) {
						temp = this.cropDimensions.topLeftY;
						this.cropDimensions.topLeftY = tY;
						this.cropDimensions.bottomRightY = temp;
					} else
						this.cropDimensions.bottomRightY = bY;
					break;
					
				case "s":
					if (tY < this.cropDimensions.topLeftY) {
						this.setHandleActive("n");
						temp = this.cropDimensions.topLeftY;
						this.cropDimensions.topLeftY = tY;
						this.cropDimensions.bottomRightY = temp;
					} else
						this.cropDimensions.bottomRightY = bY;
					break;
					
				case "sw":
					if (bX > this.cropDimensions.bottomRightX && tY < this.cropDimensions.topLeftY)
						this.setHandleActive("ne");
					else if (bX > this.cropDimensions.bottomRightX)
						this.setHandleActive("se");
					else if (tY < this.cropDimensions.topLeftY)
						this.setHandleActive("nw");
					if (bX > this.cropDimensions.bottomRightX) {
						temp = this.cropDimensions.bottomRightX;
						this.cropDimensions.bottomRightX = bX;
						this.cropDimensions.topLeftX = temp;
					} else
						this.cropDimensions.topLeftX = tX;
					if (tY < this.cropDimensions.topLeftY) {
						temp = this.cropDimensions.topLeftY;
						this.cropDimensions.topLeftY = tY;
						this.cropDimensions.bottomRightY = temp;
					} else
						this.cropDimensions.bottomRightY = bY;
					break;
					
				case "w":
					if (bX > this.cropDimensions.bottomRightX) {
						this.setHandleActive("e");
						temp = this.cropDimensions.bottomRightX;
						this.cropDimensions.bottomRightX = bX;
						this.cropDimensions.topLeftX = temp;
					} else
						this.cropDimensions.topLeftX = tX
					break;
			}
			this.evalCropDimensions();
			this.drawCropperHandles();
			Event.stop(event);
		}
	},
	handleMouseUp: function(event) {
		if (this.handleActive != null) {
			var width = this.cropDimensions.bottomRightX - this.cropDimensions.topLeftX;
			var height = this.cropDimensions.bottomRightY - this.cropDimensions.topLeftY;
			if (width < 2 && height < 2)
				this.resetCropper();
			else {
				var newHandles = (!this.showHandles) ? true : false;
				this.showHandles = true;
				this.evalCropDimensions();
				this.handleActive = null; // evalCropDimensions needs to know what the handle is on mouseUp
				this.drawCropperHandles();
				if (newHandles)
					this.addZIndexesToHandles("se");
				this.resetCursors();
			}
			Event.stop(event);
		}
	},
	resetCursors: function() {
		var topFlank = $("crop_top_flank");
		if (topFlank != undefined)
			$("crop_top_flank").setStyle({"cursor": "crosshair"});
		var rightFlank = $("crop_right_flank");
		if (rightFlank != undefined)
			$("crop_right_flank").setStyle({"cursor": "crosshair"});
		var bottomFlank = $("crop_bottom_flank");
		if (bottomFlank != undefined)
			$("crop_bottom_flank").setStyle({"cursor": "crosshair"});
		var leftFlank = $("crop_left_flank");
		if (leftFlank != undefined)
			$("crop_left_flank").setStyle({"cursor": "crosshair"});
		var boundingBox = $("crop_bounding_box");
		if (boundingBox != undefined)
			$("crop_image_wrapper").setStyle({"cursor": "move"});
		else 
			$("crop_image_wrapper").setStyle({"cursor": "crosshair"});
	},
	removeCropperHandles: function() {
		this.removeIfExists("crop_handle_n");
		this.removeIfExists("crop_handle_ne");
		this.removeIfExists("crop_handle_e");
		this.removeIfExists("crop_handle_se");
		this.removeIfExists("crop_handle_s");
		this.removeIfExists("crop_handle_sw");
		this.removeIfExists("crop_handle_w");
		this.removeIfExists("crop_handle_nw");
	},
	removeIfExists: function(id) {
		var element = $(id);
		if (element != undefined)
			element.remove();
	},
	getCropperMouseCoord: function(event) {
		var container = $("crop_image_wrapper");
		var offset = container.cumulativeOffset();
		var pointer  = [Event.pointerX(event), Event.pointerY(event)];
		offset[0] -= $("content_container").scrollLeft;
		offset[1] -= $("crop_right_panel").scrollTop;
		var x = pointer[0] - offset[0] - 1;
		var y = pointer[1] - offset[1] - 1;
		return {
			x: x, 
			y: y
		}
	},
	evalCropDimensions: function(event, fromInputs) {
		// helper function
		var addBoxIfMissing = function(target) {
			var element = $(target);
			if (element == undefined) {
				$("crop_image_wrapper").insert("<div id=\""+target+"\" class=\""+target+"\"><!-- --></div>");
				element = $(target);
			}
			return element;
		}
		
		if (fromInputs) { // this is only reached when the input values are correct
			this.sourceTopLeft.x = $("crop_top_left_x").value;
			this.sourceTopLeft.y = $("crop_top_left_y").value;
			this.sourceBottomRight.x = $("crop_bottom_right_x").value;
			this.sourceBottomRight.y = $("crop_bottom_right_y").value;
			 
		} else {
		
			// calculate dimensions in terms of true image size
			var xScale = this.originalDimensions.w / this.displayDimensions.w;
			var yScale = this.originalDimensions.h / this.displayDimensions.h;
			// if the user picked a square, force the scaled crop into being a square
			// first calculate the width and height of the bounding box on the screen
			var cropWidth = this.cropDimensions.bottomRightX - this.cropDimensions.topLeftX;
			var cropHeight = this.cropDimensions.bottomRightY - this.cropDimensions.topLeftY;
			
			this.sourceTopLeft = {
				x: Math.round(this.cropDimensions.topLeftX * xScale),
				y: Math.round(this.cropDimensions.topLeftY * yScale)
			}
			this.sourceBottomRight = {
				x: Math.round(this.cropDimensions.bottomRightX * xScale),
				y: Math.round(this.cropDimensions.bottomRightY * yScale)
			}
			
			var newWidth 	= this.sourceBottomRight.x - this.sourceTopLeft.x;
			var newHeight 	= this.sourceBottomRight.y - this.sourceTopLeft.y;
			
			
			if (
				cropWidth != cropHeight
				|| cropWidth != this.oldCropWidth
				|| cropHeight != this.oldCropHeight
			)
				this.sourceSquareSize = null;
			if (!this.sourceSquareSize) // keeps the source square dimensions the same whilst dragging....
				this.sourceSquareSize = (newWidth < newHeight) ? newWidth : newHeight;
			
			this.oldCropWidth = cropWidth;
			this.oldCropHeight = cropHeight;
			
			var cropImage = $("crop_image");
			if (cropWidth == cropHeight) { // it's a square
	//			console.log("is square");
				// coorection takes place at the handle point via this code - if there is no handle
				// then choose one based on where the square is
				var fakeHandle = false;
				if (!this.handleActive) {
					fakeHandle = true;
					this.handleActive = "se";
					if (this.cropDimensions.topLeftX == 0 && this.cropDimensions.topLeftY == 0)
						this.handleActive = "se";
					if (this.cropDimensions.topLeftX == cropImage.width && this.cropDimensions.topLeftY == 0)
						this.handleActive = "sw";
					if (this.cropDimensions.topLeftX == cropImage.width && this.cropDimensions.topLeftY == cropImage.height)
						this.handleActive = "nw"
					if (this.cropDimensions.topLeftX == 0 && this.cropDimensions.topLeftY == cropImage.height)
						this.handleActive = "ne";
				}
				switch (this.handleActive) {
					case ("nw"):
						this.sourceTopLeft.x = this.sourceBottomRight.x - this.sourceSquareSize;
						this.sourceTopLeft.y = this.sourceBottomRight.y - this.sourceSquareSize;
						break;
					case ("ne"):
						this.sourceBottomRight.x = this.sourceTopLeft.x + this.sourceSquareSize;
						this.sourceTopLeft.y = this.sourceBottomRight.y - this.sourceSquareSize;
						break;
					case ("se"):
						this.sourceBottomRight.x = this.sourceTopLeft.x + this.sourceSquareSize;
						this.sourceBottomRight.y = this.sourceTopLeft.y + this.sourceSquareSize;
						break;
					case ("sw"):
						this.sourceTopLeft.x = this.sourceBottomRight.x - this.sourceSquareSize;
						this.sourceBottomRight.y = this.sourceTopLeft.y + this.sourceSquareSize;
						break;
				}
				if (fakeHandle)
					this.handleActive = null;
			}
			if (
				this.cropDimensions.topLeftX == this.cropDimensions.bottomRightX
				|| this.cropDimensions.topLeftY == this.cropDimensions.bottomRightY
			)
				this.hasCrop = false;
			else {
				this.hasCrop = true;
				this.enableButton("step_nav_crop_next");
			}
	
			// also update those text boxes...
			$("crop_top_left_x").value 		= (this.hasCrop) ? this.sourceTopLeft.x : "-";
			$("crop_top_left_y").value 		= (this.hasCrop) ? this.sourceTopLeft.y : "-"; 
			$("crop_bottom_right_x").value 	= (this.hasCrop) ? this.sourceBottomRight.x : "-";
			$("crop_bottom_right_y").value	= (this.hasCrop) ? this.sourceBottomRight.y : "-";
			$("crop_width").value 			= (this.hasCrop) ? this.sourceBottomRight.x - this.sourceTopLeft.x : "-";
			$("crop_height").value			= (this.hasCrop) ? this.sourceBottomRight.y - this.sourceTopLeft.y : "-";
		}
		
		// Draw the bounding box
		var boundingBox = addBoxIfMissing("crop_bounding_box");
		var width = this.cropDimensions.bottomRightX - this.cropDimensions.topLeftX;
		var height = this.cropDimensions.bottomRightY - this.cropDimensions.topLeftY;
		if (width < 2 && height < 2) {
			boundingBox
				.removeClassName("crop_bounding_box_mini_horiz")
				.removeClassName("crop_bounding_box_mini_vert")
				.addClassName("crop_bounding_box_mini");
			border = 0;
		} else if (width < 2) {
			// need a 1px border down the left hand side - mini_vert
			boundingBox
				.removeClassName("crop_bounding_box_mini_horiz")
				.removeClassName("crop_bounding_box_mini")
				.addClassName("crop_bounding_box_mini_vert");
			border = 1;
				
		} else if (height < 2) {
			// need a 1px border along the top - mini_horiz
			boundingBox
				.removeClassName("crop_bounding_box_mini_vert")
				.removeClassName("crop_bounding_box_mini")
				.addClassName("crop_bounding_box_mini_horiz");
			border = 1;
		} else {
			boundingBox
				.removeClassName("crop_bounding_box_mini_horiz")
				.removeClassName("crop_bounding_box_mini_vert")
				.removeClassName("crop_bounding_box_mini");
			border = 2;
		}
		boundingBox.setStyle({
			"width"		: (this.cropDimensions.bottomRightX - this.cropDimensions.topLeftX - border) + "px",
			"height"	: (this.cropDimensions.bottomRightY - this.cropDimensions.topLeftY - border) + "px",
			"top"		: this.cropDimensions.topLeftY + "px",
			"left"		: this.cropDimensions.topLeftX + "px"
		});
		
		var cropImage = $("crop_image");
		var horizClip = 0;
		// Draw the flanking divs
		if (this.newDrag) { // happens when the user clicks - i.e. there's a 1x1 px box
			this.removeIfExists("crop_top_flank");
			this.removeIfExists("crop_left_flank");
			this.removeIfExists("crop_right_flank");
			this.removeIfExists("crop_bottom_flank");
		} else { // the box is > 1x1 px
			addBoxIfMissing("crop_top_flank").setStyle({
				"left" 		: "0px",
				"top" 		: "0px",
				"width"		: cropImage.width+"px",
				"height" 	: (this.cropDimensions.topLeftY + 1)+"px"
			});
			if (height > 1) {
				horizClip = (width == 1) ? 1 : 0;
				addBoxIfMissing("crop_left_flank").setStyle({
					"left" 		: "0px",
					"top" 		: (this.cropDimensions.topLeftY + 1)+"px",
					"width"		: (this.cropDimensions.topLeftX + 1 - horizClip)+"px",
					"height" 	: (this.cropDimensions.bottomRightY - this.cropDimensions.topLeftY - 2)+"px"
				});
				addBoxIfMissing("crop_right_flank").setStyle({
					"left" 		: (this.cropDimensions.bottomRightX - 1)+"px",
					"top" 		: (this.cropDimensions.topLeftY + 1)+"px",
					"width"		: (cropImage.width - this.cropDimensions.bottomRightX + 1)+"px",
					"height" 	: (this.cropDimensions.bottomRightY - this.cropDimensions.topLeftY - 2)+"px"
				});
			} else {
				this.removeIfExists("crop_left_flank");
				this.removeIfExists("crop_right_flank");
			}
			var clip = (height > 1) ? 1 : 0;
			addBoxIfMissing("crop_bottom_flank").setStyle({
				"left" 		: "0px",
				"top" 		: (this.cropDimensions.bottomRightY - clip) + "px",
				"width"		: cropImage.width + "px",
				"height" 	: (cropImage.height - this.cropDimensions.bottomRightY + 1)+"px"
			});
		}
		
		// Update box yellow error flags - remove if needed...
		var topLeftX 		= parseInt($("crop_top_left_x").value);
		var topLeftY 		= parseInt($("crop_top_left_y").value);
		var bottomRightX 	= parseInt($("crop_bottom_right_x").value);
		var bottomRightY 	= parseInt($("crop_bottom_right_y").value);
		var error = false;
		if (
			topLeftX < bottomRightX
			&& 0 <= topLeftX && topLeftX < this.originalDimensions.w
			&& 0 < bottomRightX && bottomRightX <= this.originalDimensions.w
		) {
			$("crop_top_left_x").removeClassName("pixel_box_error");
			$("crop_bottom_right_x").removeClassName("pixel_box_error");
		}
		if (
			topLeftY < bottomRightY
			&& 0 <= topLeftY && topLeftY < this.originalDimensions.h
			&& 0 < bottomRightY && bottomRightY <= this.originalDimensions.h
		) {
			$("crop_top_left_y").removeClassName("pixel_box_error");
			$("crop_bottom_right_y").removeClassName("pixel_box_error");
		}
	},
	cropperNext: function() {
		if (this.hasCrop) {
			
			this.currentCropDimensions = {
				w : this.sourceBottomRight.x - this.sourceTopLeft.x,
				h :	this.sourceBottomRight.y - this.sourceTopLeft.y
			}
			this.newCropDimensions = (
				this.oldCropDimensions != null
				&& this.oldCropDimensions.w == this.currentCropDimensions.w
				&& this.oldCropDimensions.h == this.currentCropDimensions.h
			) ? false : true;
			
//			console.log(this.oldCropDimensions);
//			console.log(this.currentCropDimensions);
//			console.log(this.newCropDimensions);
			
			this.oldCropDimensions = {};
			this.oldCropDimensions.w = this.currentCropDimensions.w;
			this.oldCropDimensions.h = this.currentCropDimensions.h;
			
			// post the dimensions off to an ajax script which chops the image...
			// display "please wait" while this is happening
			// then flip over to the next page
			
			this.showLoading("Cropping...");
			this.genericSendAJAX("crop", {
				parameters: {
					top_left_x		: this.sourceTopLeft.x,
					top_left_y		: this.sourceTopLeft.y,
					bottom_right_x	: this.sourceBottomRight.x,
					bottom_right_y	: this.sourceBottomRight.y
				},
				callback: function (responseObject) {
					// display function here...
					$("resize_image_container").update(
						"<img " +
							"id=\"resize_image\"" +
							"src=\"/uploaded_images/av_" + readCookie("session") + "_"+this.avatarID+"_cropped_display.jpg?" + this.getRnd() + "\"" +
							"class=\"hasLayout\" />"
					);
					this.addDownloadMenu($("resize_image"), this.resizeDownload.bind(this));
					$("resize_image").observe("load", function() {
						this.resizeDivs();
						this.gotoStep(3);
						this.displayImageForResizing(responseObject);
						this.unMask();
					}.bind(this));
				}.bind(this)
			});
		} else {
			this.showError("Please select a cropping area first.");
		}
	},
	// #############################################################################################################################
	// ########################################################## RESIZER ##########################################################
	// #############################################################################################################################
	
	initResizer: function() { // called by main constructor
		this.resizeDimensions = {w: null, h: null};
		$("step_nav_resize_download").observe("click", this.resizeDownload.bindAsEventListener(this));
		// Attach the resize text box event handlers
		var updateDimensions = function(newWidth, newHeight) {
			// update text boxes
			$("resize_width").value = newWidth;
			$("resize_height").value = newHeight;
			 
			// also update internal dimensions (both)
			this.resizeDimensions.w = newWidth;
			this.resizeDimensions.h = newHeight;
			
			// also update the slider.
			this.resizeSlider.setValue((this.resizeOriginalDimensions.w > this.resizeOriginalDimensions.h) ? newWidth : newHeight);
		}.bind(this);
		$("resize_width").observe("keydown", function(event) {
			if (this.pixelBoxHandleKey(event)) {
				this.revertToStep(3);
				var f = function() {
					var element = $("resize_width");
					var newWidth = element.value;
					var newHeight = Math.round(element.value * (this.resizeOriginalDimensions.h / this.resizeOriginalDimensions.w));
					if (newWidth < 1)
						newWidth = 1;
					else if (newWidth > this.resizeOriginalDimensions.w)
						newWidth = this.resizeOriginalDimensions.w;
					updateDimensions(newWidth, newHeight);
				}.bind(this)
				f.defer();
			}
		}.bind(this));
		$("resize_height").observe("keydown", function(event) {
			if (this.pixelBoxHandleKey(event)) {
				this.revertToStep(3);
				var f = function() {
					var element = $("resize_height");
					var newHeight = element.value;
					var newWidth = Math.round(element.value * (this.resizeOriginalDimensions.w / this.resizeOriginalDimensions.h));
					if (newHeight < 1)
						newHeight = 1;
					else if (newHeight > this.resizeOriginalDimensions.h)
						newHeight = this.resizeOriginalDimensions.h;
					updateDimensions(newWidth, newHeight);
				}.bind(this)
				f.defer();
			}
		}.bind(this));
		$("step_nav_resize_next").observe("click", this.resizeNext.bindAsEventListener(this));
	},
	doResize: function(callback) {
		// post the new width and height off to the php
		this.showLoading("Resizing...");
		this.genericSendAJAX("resize", {
			parameters: {
				width: this.resizeDimensions.w,
				height: this.resizeDimensions.h
			},
			callback: function(responseObject) {
				callback(responseObject);
			}.bind(this)
		});
	},
	resizeDownload: function() {
		this.doResize(function(responseObject) {
			this.showDownloadPreview(function() {
				this.unMask();
				this.revertToStep(6);
				$("step_tab_4").addClassName("disabled");
				$("step_tab_5").addClassName("disabled");
				this.gotoStep(6);
			}.bind(this));
		}.bind(this))
	},
	resizeNext: function(responseObject) {
		this.doResize(function() {
			$("add_border_image_container").update(
				"<img " +
					"id=\"bordered_image\"" +
					"src=\"/uploaded_images/av_" + readCookie("session") + "_"+this.avatarID+"_resized_display.jpg?" + this.getRnd() + "\"" +
					"class=\"hasLayout\" />"
			);
			this.addDownloadMenu($("bordered_image"), this.borderDownload.bind(this));
			$("bordered_image").observe("load", function() {
				this.revertToStep(4);
				this.centerElement("add_border_right_panel", "add_border_image_container");
				this.unMask();
				if (this.borderID) {
					this.applyBorder(null, null, null, function() {
						this.gotoStep(4);
					}.bind(this));
				} else
					this.gotoStep(4);
			}.bind(this));
		}.bind(this));
	},
	displayImageForResizing: function(responseObject) {
		this.revertToStep(3);
		var DIMENSIONS_BOX_WIDTH = 275;
		
		// Init the resize slider
		var imageWidth 	= responseObject.width;
		var imageHeight	= responseObject.height;
		
		
		
		this.resizeOriginalDimensions 	= {w: imageWidth, h: imageHeight};
		this.resizeDimensions 			= {w: imageWidth, h: imageHeight};
		
		// update text boxes
		$("resize_width").value = imageWidth;
		$("resize_height").value = imageHeight;
		
		var sliderWidth 		= (imageWidth > imageHeight) ? imageWidth 	: imageHeight;
		var sliderControls 		= (imageWidth > imageHeight) ? this.RESIZE_IMAGE_WIDTH 	: this.RESIZE_IMAGE_HEIGHT;
		
		
		
		var resizeSlider = $("resize_slider");
		if (this.newCropDimensions) { // retain old setting if it's not new
			resizeSlider.setStyle({"width": (sliderWidth+10)+"px"});
			$("resize_instructions").setStyle({"left": (sliderWidth+DIMENSIONS_BOX_WIDTH)+"px"});
		}
		
		var values = [];
		var i;
		for (i = 1; i <= sliderWidth; i++)
			values.push(i);

		var resizeImage = function(value) {
//			console.log(value);
			this.revertToStep(3);
			var newWidth, newHeight;
			if (sliderControls == this.RESIZE_IMAGE_WIDTH) {
				newWidth 	= value;
				newHeight	= Math.round(value * (imageHeight / imageWidth));
			} else {
				newHeight	= value;
				newWidth	= Math.round(value * (imageWidth / imageHeight));
			} 
			// make sure dom width and height are always at least 1
			if (newWidth < 1)
				newWidth = 1;
			if (newHeight < 1)
				newHeight = 1;
			
			this.resizeDimensions.w = newWidth;
			this.resizeDimensions.h = newHeight;

			$("resize_image").setStyle({
				"width"		: newWidth+"px",
				"height"	: newHeight+"px"
			});
			
			// update text boxes
			this.centerElement("resize_image_container_container", "resize_image_container");
			
			$("resize_width").value = newWidth;
			$("resize_height").value = newHeight;
			
			
		}.bind(this);

		var sliderValue = (this.newCropDimensions == false) ? this.resizeSlider.value : sliderWidth; 

		if (this.resizeSlider != undefined)
			this.resizeSlider.dispose();
		this.resizeSlider = new Control.Slider(resizeSlider.down('.handle'), resizeSlider, {
			range: $R(1, sliderWidth),
			values: values, 
			sliderValue: sliderValue,
			onSlide: resizeImage.bind(this),
			onChange: resizeImage.bind(this),
			onStartDrag: function(handle) {
				handle.addClassName("pressed");
			}.bind(this),
			onFinishDrag: function(handle) {
				handle.removeClassName("pressed");
			},
			scrollableDiv: $("content_container")
		});
		if (this.newCropDimensions == false)
			resizeImage(sliderValue);
	},
	
	// #############################################################################################################################
	// ######################################################### BORDERER ##########################################################
	// #############################################################################################################################
	
	initBorderer: function() {
		this.borderID = null;
		$$(".border_example_cell").each(function(element) {
			element.observe("click", this.applyBorder.curry(element.id.split("_").pop()).bind(this));
		}.bind(this));
		$("step_nav_border_download").observe("click", this.borderDownload.bindAsEventListener(this));
		$("step_nav_border_next").observe("click", this.borderNext.bindAsEventListener(this));
	},
	borderNext: function() {
		this.applyBorder(null, null, true);
	},
	borderDownload: function() {
		this.applyBorder(null, null, null, function(responseObject) {
			this.showDownloadPreview(function () {
				this.unMask();
				this.revertToStep(6);
				$("step_tab_5").addClassName("disabled");
				this.gotoStep(6);
			}.bind(this));
		}.bind(this));
	},
	applyBorder: function(borderID, colour, isFinal, callback) {
		this.borderID = (borderID == undefined || borderID == null) ? this.borderID : borderID;
		var cellID = null;
		$$(".border_example_cell").each(function(element) {
			cellID = element.id.split("_").pop();
			if (cellID == this.borderID)
				element.addClassName("border_example_cell_selected");
			else
				element.removeClassName("border_example_cell_selected");
		}.bind(this));
		this.revertToStep(4);
		this.borderColour 	= $("border_colour_picker_indicator").value;
		this.showLoading("Adding border...");
		this.genericSendAJAX("addBorder", {
			parameters: {
				border_id: this.borderID,
				colour: this.borderColour
			},
			callback: function(responseObject) {
				if (isFinal) { // means go to the next step
					$("add_text_image_container").update(
						"<img " +
							"id=\"text_image\"" +
							"src=\"/uploaded_images/av_" + readCookie("session") + "_"+this.avatarID+"_bordered_display.jpg?" + this.getRnd() + "\"" +
							"class=\"hasLayout\" />"
					);
					this.addDownloadMenu($("text_image"), this.gotoDownload.bind(this));
					$("text_image").observe("load", function() {
						this.centerElement("add_text_right_panel", "add_text_image_container");
						this.unMask();
						this.revertToStep(5);
//						// wipe the form here. // commented out - now retains form values.
//						$("add_text_text").value = "";
//						$("text_colour_picker_indicator").color.fromString("FFFFFF");
//						$("font_face").value = "acknowledge";
//						$("font_size").value = "10";
//						$("add_text_position").value = "c";
//						$("left_right_margin").value = "12";
//						$("top_bottom_margin").value = "12";
//						$("text-outline_colour_picker_indicator").color.fromString("000000");
//						$("outline_width").value = "1";

						if ($("add_text_text").value != "") {
							this.previewText(function() {
								this.unMask();
								this.gotoStep(5, null, function() {
									$("add_text_text").focus();
								}.bind(this));
							}.bind(this));		
						} else {
							this.gotoStep(5, null, function() {
								$("add_text_text").focus();
							}.bind(this));
						}
					}.bind(this));
				} else {
					$("add_border_image_container").update(
						"<img " +
							"id=\"bordered_image\"" +
							"src=\"/uploaded_images/av_" + readCookie("session") + "_"+this.avatarID+"_bordered_display.jpg?" + this.getRnd() + "\"" +
							"class=\"hasLayout\" />"
					);
					this.addDownloadMenu($("bordered_image"), this.borderDownload.bind(this));
					$("bordered_image").observe("load", function() {
						this.unMask();
						if (callback != undefined)
							callback();
					}.bind(this))
				}
			}.bind(this)
		})
	},
	
	// #############################################################################################################################
	// ########################################################## TEXTER ###########################################################
	// #############################################################################################################################
	
	fonts : $H({
		acknowledge: $H({
			defaultSize: 10,
			bold: false,
			italic: false
		}),
		alphbeta: $H({
			defaultSize: 10,
			bold: false,
			italic: false
		}),
		arial: $H({
			defaultSize: null,
			bold: true,
			italic: true
		}),
		cour: $H({
			defaultSize: null,
			bold: true,
			italic: true
		}),
		sf_intermosaic: $H({
			defaultSize: 6,
			bold: false,
			italic: false
		}),
		starmap: $H({
			defaultSize: 8,
			bold: false,
			italic: false
		}),
		tahoma: $H({
			defaultSize: null,
			bold: true,
			italic: false
		}),
		times: $H({
			defaultSize: null,
			bold: true,
			italic: true
		}),
		trebuc: $H({
			defaultSize: null,
			bold: true,
			italic: true
		})
	}),
	
	initTexter: function() {
		$("text_preview_button").observe("click", this.previewText.bindAsEventListener(this));
		$("font_face").observe("change", this.chooseFont.bindAsEventListener(this));
		$("text_bold").observe("mousedown", this.bold.bindAsEventListener(this));
		$("text_italic").observe("mousedown", this.italic.bindAsEventListener(this));
		
		$("left_right_margin").observe("keydown", function(event) {
			this.pixelBoxHandleKey(event);
		}.bind(this));
		$("top_bottom_margin").observe("keydown", function(event) {
			this.pixelBoxHandleKey(event);
		}.bind(this));
		$("outline_width").observe("keydown", function(event) {
			this.pixelBoxHandleKey(event);
		}.bind(this));
		$("step_nav_text_next").observe("click", this.gotoDownload.bindAsEventListener(this));
		
//		var bgOpacitySlider = $("bg_opacity_slider");
//		var sliderMove = function(value) {
//			$("bg_opacity").value = value;
//		}
//		var values = [];
//		var i;
//		for (i = 0; i <= 100; i++)
//			values.push(i);
//		this.bgOpacitySlider = new Control.Slider(bgOpacitySlider.down('.handle'), bgOpacitySlider, {
//			range: $R(0, 100),
//			values: values,
//			sliderValue: 50,
//			onSlide: sliderMove.bind(this),
//			onChange: sliderMove.bind(this),
//			onStartDrag: function(handle) {
//				handle.addClassName("pressed");
//			}.bind(this),
//			onFinishDrag: function(handle) {
//				handle.removeClassName("pressed");
//			},
//			scrollableDiv: $("content_container")
//		});
		this.bold = false;
		this.italic = false;
	},
	bold: function(event) {
		var f = Object.extend(function() { // must wait for buttons.js to do its thing first.
			this.bold = ($("text_bold").hasClassName("form_button_pressed")) ? true : false;
		}.bind(this))
		f.defer();
	},
	italic: function(event) {
		var f = Object.extend(function() {
			this.italic = ($("text_italic").hasClassName("form_button_pressed")) ? true : false;
		}.bind(this));
		f.defer();
	},
	
	DEFAULT_FONT_SIZE: 12,
	
	chooseFont: function(event) {
		this.bold 	= false;
		this.italic	= false;
		var font = this.fonts.get(event.element().value);
		if (font.get("bold") == true) {
			$("text_bold_disabled").hide();
			$("text_bold").show();
			if ($("text_bold").hasClassName("form_button_pressed"))
				$("text_bold").removeClassName("form_button_pressed").addClassName("form_button");
		} else {
			$("text_bold").hide();
			$("text_bold_disabled").show();
		}
		if (font.get("italic") == true) {
			$("text_italic_disabled").hide();
			$("text_italic").show();
			if ($("text_italic").hasClassName("form_button_pressed"))
				$("text_italic").removeClassName("form_button_pressed").addClassName("form_button");
		} else {
			$("text_italic").hide();
			$("text_italic_disabled").show();
		}
		var defaultSize = font.get("defaultSize")
		$("font_size").value = (defaultSize) ? defaultSize : this.DEFAULT_FONT_SIZE;
	},
	previewText: function(callback) {
		this.addText(function() {
			$("add_text_image_container").update(
				"<img " +
					"id=\"text_image\"" +
					"src=\"/uploaded_images/av_" + readCookie("session") + "_"+this.avatarID+"_texted_display.jpg?" + this.getRnd() + "\"" +
					"class=\"hasLayout\" />"
			);
			this.addDownloadMenu($("text_image"), this.gotoDownload.bind(this));
			$("text_image").observe("load", function() {
				if (Object.isFunction(callback))
					callback();
				else {
					this.revertToStep(5);
					this.unMask();
				}
			}.bind(this));
		}.bind(this));
	},
	gotoDownload: function() {
		this.previewText(function() {
			this.showDownloadPreview(function() {
				this.revertToStep(6);
				this.unMask();
				this.gotoStep(6);
			}.bind(this));
		}.bind(this));
	},
	addText: function(callback) {
		this.revertToStep(5);
		this.showLoading("Adding text...");
		var formData = $("add_text_form").serialize(true);
		formData.bold	= this.bold;
		formData.italic	= this.italic;
		this.genericSendAJAX("addText", {
			parameters: formData,
			callback: function() {
				callback();
			}.bind(this)
		})
	},
	// #############################################################################################################################
	// ###################################################### DOWNLOADER ###########################################################
	// #############################################################################################################################
	FORMAT_PNG: "png",
	FORMAT_GIF: "gif",
	FORMAT_JPEG: "jpeg",
	initDownloader: function () {
		$("download_format_png").observe("click", this.changeFormat.curry(this.FORMAT_PNG).bind(this));
		$("download_format_gif").observe("click", this.changeFormat.curry(this.FORMAT_GIF).bind(this));
		$("download_format_jpeg").observe("click", this.changeFormat.curry(this.FORMAT_JPEG).bind(this));
		$("download_button").observe("click", this.downloadButtonClick.bindAsEventListener(this));
	},
	changeFormat: function(formatID) {
		if (formatID == this.FORMAT_JPEG) {
			$("change_colour_row").show();
			// .. show colour box
		} else {
			$("change_colour_row").hide();
			// .. hide colour box
		}
	},
	downloadButtonClick: function() {
		
		var format;
		if ($("download_format_png").checked)
			format = "png";
		else if($("download_format_gif").checked)
			format = "gif";
		else if ($("download_format_jpeg").checked)
			format = "jpeg";
		
		$("download_button").href = "/download.php" +
				"?format=" + format +
				"&avatar_id=" + this.avatarID + 
				"&background="+$("download_colour_picker_indicator").value;
		
//		new Ajax.Request("/ajax/finish.php", {
//			method: "post",
//			parameters: {
//				format: format,
//				background: $("download_colour_picker_indicator").value
//			},
//			onSuccess: function(transport) {
//				var responseObject = transport.responseText.evalJSON();
//				if (responseObject.error != undefined)
//					this.showError(responseObject.error);
//				else {
//					this.unMask();
//					location.href = "/download.php?file="+responseObject.filename;
//				}
//			}.bind(this)
//		});
		
		// return the name of the file to download
	}
	
});

if ((window.XMLHttpRequest == undefined) && (ActiveXObject != undefined))
	window.location = "/ie6_fail.php"; 


google.load("search", "1");

Event.observe(window, "load", function() {
	window.avatarGeneratorUI = new AvatarGeneratorUI(window.avatarID);
});

