var jqpan = {
	
	state : "fitwidth", // "fitwidth", "fitheight", "fh-scrolling", "actual"

	selector : "#jqpan-image",
	
	fh_scroll_amount : 100,
	fh_scroll_shift_factor : 4,

	act_scroll_amount : 100,
	act_scroll_shift_factor : 4,	

	win_width : 0,
	win_height : 0,

	fw_width : 0,
	fw_height : 0,
	fw_top : 0,

	fh_width : 0,
	fh_height : 0,
	fh_scroll_left : 0,
	fh_scrolling_direction : "right", // "right", "left"
	fh_scrolling_delay : 10,
	fh_scrolling_amount : 2,
	fh_scrolling_timeoutID : 0,
	fh_scroll_animate_duration : 100,
	fh_scroll_end_animate_duration : 500,

	position_width : 200,
	position_height : null,
	position_show : true,
	position_inner_fh_width : 0,
	position_inner_act_height : 0,
	position_inner_act_width : 0,

	act_width : 0,
	act_height : 0,

	act_scroll_left : 0,
	act_scroll_top : 0,
	act_scroll_animate_duration : 100,

	overlay_fade_delay : 1500,
	overlay_fadein_duration : 400,
	overlay_fadeout_duration : 800,
	overlay_fade_timeoutID : 0,

	main_mouseover : false,
	
	obj_jqpan_selector : null,

	init : function () {
		// get initial width and height
		jqpan.obj_jqpan_selector = $(jqpan.selector);
		jqpan.act_width = jqpan.obj_jqpan_selector.width();
		jqpan.act_height = jqpan.obj_jqpan_selector.height();
		// set up UI elements
		jqpan.makeDocument();
		jqpan.makeWrapper();
		jqpan.makeMain();
		jqpan.makeArrows();
		jqpan.makePosition();
		jqpan.updateDimensions();
		// get opacity right initially for Internet Explorer
		$(".jqpan-box").css("opacity", "0.5");
		// initial state
		jqpan.switchToState("fitwidth");
	},

	updateDimensions : function () {
		fh_scroll_left_percentage = 0;
		if (jqpan.fh_width > 0) {
			fh_scroll_left_percentage = jqpan.fh_scroll_left / jqpan.fh_width;
		}
		jqpan.win_width = $(window).width();
		jqpan.win_height = $(window).height();
		$("#jqpan-wrapper").width(jqpan.win_width);
		$("#jqpan-wrapper").height(jqpan.win_height);
		jqpan.fw_width = jqpan.win_width;
		jqpan.fw_height = Math.round(jqpan.act_height / jqpan.act_width * jqpan.win_width);
		jqpan.fw_top = Math.round((jqpan.win_height - jqpan.fw_height) / 2);
		jqpan.fh_width = Math.round(jqpan.act_width / jqpan.act_height * jqpan.win_height);
		jqpan.fh_height = jqpan.win_height;
		jqpan.fh_scroll_left = Math.round(fh_scroll_left_percentage * jqpan.fh_width);
		$("#jqpan-main").css("left", "20px");
		$("#jqpan-main").css("bottom", "20px");
		$("#jqpan-arrow-left").css("margin-top", "-" + Math.round($("#jqpan-arrow-left").outerHeight() / 2) + "px");
		$("#jqpan-arrow-right").css("margin-top", "-" + Math.round($("#jqpan-arrow-right").outerHeight() / 2) + "px");
		$("#jqpan-arrow-up").css("margin-left", "-" + Math.round($("#jqpan-arrow-up").outerWidth() / 2) + "px");
		$("#jqpan-arrow-down").css("margin-left", "-" + Math.round($("#jqpan-arrow-down").outerWidth() / 2) + "px");
		$("#jqpan-position-outer").width(jqpan.position_width);
		jqpan.position_height = Math.round(jqpan.act_height / jqpan.act_width * jqpan.position_width);
		$("#jqpan-position-outer").height(jqpan.position_height);
		jqpan.position_inner_fh_width = Math.round(jqpan.position_width * jqpan.win_width / jqpan.fh_width) - 2;
		jqpan.position_inner_act_width = Math.round(jqpan.position_width * jqpan.win_width / jqpan.act_width) - 2;
		jqpan.position_inner_act_height = Math.round(jqpan.position_height * jqpan.win_height / jqpan.act_height) - 2;
	},

	switchToState : function (state) {
		if ((jqpan.state === "fh-scrolling") && (state !== "fh-scrolling")) {
			window.clearTimeout(jqpan.fh_scrolling_timeoutID);
		}
		jqpan.state = state;
		switch (jqpan.state) {
			case "fitwidth":
				jqpan.obj_jqpan_selector.css("cursor", "pointer");
				$(".jqpan-fw").removeClass("jqpan-notstate");
				$(".jqpan:not(.jqpan-fw)").addClass("jqpan-notstate");
				$("#jqpan-position-inner").draggable("disable");
				jqpan.obj_jqpan_selector.draggable("destroy");
				jqpan.render();
				break;
			case "fitheight":
				jqpan.obj_jqpan_selector.css("cursor", "default");
				$("#jqpan-position-inner").draggable("enable");
				$("#jqpan-position-inner").height(jqpan.position_height - 2);
				$("#jqpan-position-inner").width(jqpan.position_inner_fh_width);
				jqpan.obj_jqpan_selector.draggable("destroy");
				jqpan.obj_jqpan_selector.draggable({ containment: [ jqpan.win_width - jqpan.fh_width, 0, 0, 0 ], start: jqpan.fh_dragStart, drag: jqpan.fh_dragListener, stop: jqpan.fh_dragStop });
				$(".jqpan-fh").removeClass("jqpan-notstate");
				$(".jqpan:not(.jqpan-fh)").addClass("jqpan-notstate");
				jqpan.fh_scroll_boundary_check();
				jqpan.render();
				break;
			case "fh-scrolling":
				jqpan.obj_jqpan_selector.css("cursor", "default");
				$(".jqpan-fh-scrolling").removeClass("jqpan-notstate");
				$(".jqpan:not(.jqpan-fh-scrolling)").addClass("jqpan-notstate");
				$("#jqpan-position-inner").draggable("enable");
				$("#jqpan-position-inner").height(jqpan.position_height - 2);
				$("#jqpan-position-inner").width(jqpan.position_inner_fh_width);
				jqpan.obj_jqpan_selector.draggable("destroy");
				jqpan.obj_jqpan_selector.draggable({ containment: [ jqpan.win_width - jqpan.fh_width, 0, 0, 0 ], start: jqpan.fh_dragStart, drag: jqpan.fh_dragListener, stop: jqpan.fh_dragStop });
				jqpan.render();
				jqpan.fh_scrolling_timestep();
				break;
			case "actual":
				jqpan.obj_jqpan_selector.css("cursor", "default");
				$(".jqpan-act").removeClass("jqpan-notstate");
				$(".jqpan:not(.jqpan-act)").addClass("jqpan-notstate");
				$("#jqpan-position-inner").draggable("disable");
				$("#jqpan-position-inner").height(jqpan.position_inner_act_height);
				$("#jqpan-position-inner").width(jqpan.position_inner_act_width);
				jqpan.obj_jqpan_selector.draggable("destroy");
				jqpan.obj_jqpan_selector.draggable({ containment: [ jqpan.win_width - jqpan.act_width, jqpan.win_height - jqpan.act_height, 0, 0 ], drag: jqpan.act_dragListener });
				jqpan.render();
				break;
		}
		jqpan.overlay_fadein();
		jqpan.overlay_fadeout_start();
	},

	render : function (smooth, duration) {
		if (smooth === null) smooth = false;
		if (duration === null) duration = 0;
		switch (jqpan.state) {
			case "fitwidth":
				jqpan.obj_jqpan_selector.height(jqpan.fw_height);
				jqpan.obj_jqpan_selector.width(jqpan.fw_width);
				jqpan.obj_jqpan_selector.css("top", jqpan.fw_top + "px");
				jqpan.obj_jqpan_selector.css("left", "0px");
				break;
			case "fitheight":
				jqpan.obj_jqpan_selector.height(jqpan.fh_height);
				jqpan.obj_jqpan_selector.width(jqpan.fh_width);
				jqpan.obj_jqpan_selector.css("top", "0px");
				if (smooth && (duration > 0)) {
					jqpan.obj_jqpan_selector.stop().animate({
						left: "-" + jqpan.fh_scroll_left + "px"
					}, duration);
				} else {
					jqpan.obj_jqpan_selector.stop().css("left", "-" + jqpan.fh_scroll_left + "px");
				}
				break;
			case "fh-scrolling":
				jqpan.obj_jqpan_selector.height(jqpan.fh_height);
				jqpan.obj_jqpan_selector.width(jqpan.fh_width);
				jqpan.obj_jqpan_selector.css("top", "0px");
				if (smooth && (duration > 0)) {
					jqpan.obj_jqpan_selector.stop().animate({
						left: "-" + jqpan.fh_scroll_left + "px"
					}, duration, null, callback);
				} else {
					$(jqpan.selector).css("left", "-" + jqpan.fh_scroll_left + "px");
				}
				break;
			case "actual":
				jqpan.obj_jqpan_selector.height(jqpan.act_height);
				jqpan.obj_jqpan_selector.width(jqpan.act_width);
				if (smooth && (duration > 0)) {
					jqpan.obj_jqpan_selector.stop().animate({
						top: "-" + jqpan.act_scroll_top + "px", 
						left: "-" + jqpan.act_scroll_left + "px"
					}, duration);
				} else {
					jqpan.obj_jqpan_selector.stop().css("top", "-" + jqpan.act_scroll_top + "px");
					jqpan.obj_jqpan_selector.css("left", "-" + jqpan.act_scroll_left + "px");
				}
				break;
		}
		jqpan.render_position(smooth, duration);
	},

	render_position : function (smooth, duration) {
		if (smooth === null) smooth = false;
		if (duration === null) duration = 0;
		switch (jqpan.state) {
			case "fitheight":
				if (smooth && (duration > 0)) {
					$("#jqpan-position-inner").stop().animate({left: Math.round(jqpan.fh_scroll_left / jqpan.fh_width * jqpan.position_width) + "px"}, duration);
				} else {
					$("#jqpan-position-inner").stop().css("left", Math.round(jqpan.fh_scroll_left / jqpan.fh_width * jqpan.position_width) + "px");
				}
				$("#jqpan-position-inner").css("top", "0px");
				jqpan.obj_jqpan_selector.draggable({ containment: [ jqpan.win_width - jqpan.fh_width, 0, 0, 0 ] });
				break;
			case "fh-scrolling":
				$("#jqpan-position-inner").css("left", Math.round(jqpan.fh_scroll_left / jqpan.fh_width * jqpan.position_width) + "px");
				$("#jqpan-position-inner").css("top", "0px");
				$("#jqpan-position-inner").draggable("enable");
				break;
			case "actual":
				if (smooth && (duration > 0)) {
					$("#jqpan-position-inner").stop().animate({
						left: Math.round(jqpan.act_scroll_left / jqpan.act_width * jqpan.position_width) + "px",
						top: Math.round(jqpan.act_scroll_top / jqpan.act_height * jqpan.position_height) + "px"
					}, duration);
				} else {
					$("#jqpan-position-inner").css("left", Math.round(jqpan.act_scroll_left / jqpan.act_width * jqpan.position_width) + "px");
					$("#jqpan-position-inner").css("top", Math.round(jqpan.act_scroll_top / jqpan.act_height * jqpan.position_height) + "px");
				}
				$("#jqpan-position-inner").draggable("enable");
				break;
		}
		$(".jqpan-notstate").hide();
		$(".jqpan-disabled").hide();
	},

	keydownListener : function (e) {
		switch (e.which) {
			case 27: // esc
				if ((jqpan.state === "fitheight") || (jqpan.state === "fh-scrolling")) {
					jqpan.switchToState("fitwidth");
				} else if (jqpan.state === "actual") {
					position_left_ratio = (jqpan.act_scroll_left + (jqpan.win_width / 2)) / jqpan.act_width;
					jqpan.fh_scroll_left = Math.round(position_left_ratio * jqpan.fh_width - (jqpan.win_width / 2));
					jqpan.fh_scroll_boundary_check();
					jqpan.switchToState("fitheight");
				}
				break;
			case 35: // end
				if (jqpan.state === "fitheight") {
					jqpan.fh_scroll(jqpan.fh_width, "right", true);
				} else if(jqpan.state === "fh-scrolling") {
					jqpan.switchToState("fitheight");
					jqpan.fh_scroll(jqpan.fh_width, "right", true);
				}
				break;
			case 36: // home
				if (jqpan.state === "fitheight") {
					jqpan.fh_scroll(jqpan.fh_width, "left", true);
				} else if(jqpan.state === "fh-scrolling") {
					jqpan.switchToState("fitheight");
					jqpan.fh_scroll(jqpan.fh_width, "left", true);
				}
				break;
			case 37: // left arrow
				if (jqpan.state === "fitheight") {
					if (e.shiftKey) {
						jqpan.fh_scroll(jqpan.fh_scroll_shift_factor * jqpan.fh_scroll_amount, "left", true);
					} else {
						jqpan.fh_scroll(jqpan.fh_scroll_amount, "left", true);
					}
				} else if (jqpan.state === "fh-scrolling") {
					if (e.shiftKey) {
						jqpan.fh_scroll(jqpan.fh_scroll_shift_factor * jqpan.fh_scroll_amount, "left", false);
					} else {
						jqpan.fh_scroll(jqpan.fh_scroll_amount, "left", false);
					}
				} else if (jqpan.state === "actual") {
					if (e.shiftKey) {
						jqpan.act_scroll(jqpan.act_scroll_shift_factor * jqpan.act_scroll_amount, "left", true);
					} else {
						jqpan.act_scroll(jqpan.act_scroll_amount, "left", true);
					}
				}
				break;
			case 38: // up arrow
				if (jqpan.state === "actual") {
					if (e.shiftKey) {
						jqpan.act_scroll(jqpan.act_scroll_shift_factor * jqpan.act_scroll_amount, "up", true);
					} else {
						jqpan.act_scroll(jqpan.act_scroll_amount, "up", true);
					}
				}
				break;
			case 39: // right arrow
				if (jqpan.state === "fitheight") {
					if (e.shiftKey) {
						jqpan.fh_scroll(jqpan.fh_scroll_shift_factor * jqpan.fh_scroll_amount, "right", true);
					} else {
						jqpan.fh_scroll(jqpan.fh_scroll_amount, "right", true);
					}
				} else if(jqpan.state === "fh-scrolling") {
					if (e.shiftKey) {
						jqpan.fh_scroll(jqpan.fh_scroll_shift_factor * jqpan.fh_scroll_amount, "right", false);
					} else {
						jqpan.fh_scroll(jqpan.fh_scroll_amount, "right", false);
					}
				} else if (jqpan.state === "actual") {
					if (e.shiftKey) {
						jqpan.act_scroll(jqpan.act_scroll_shift_factor * jqpan.act_scroll_amount, "right", true);
					} else {
						jqpan.act_scroll(jqpan.act_scroll_amount, "right", true);
					}
				}
				break;
			case 40: // down arrow
				if (jqpan.state === "actual") {
					if (e.shiftKey) {
						jqpan.act_scroll(jqpan.act_scroll_shift_factor * jqpan.act_scroll_amount, "down", true);
					} else {
						jqpan.act_scroll(jqpan.act_scroll_amount, "down", true);
					}
				}
				break;
		}
	},

	keypressListener : function (e) {
		switch (e.which) {
			case 81:  // Q
			case 113: // q
				jqpan.switchToState("fitwidth");
				break;
			case 82:  // R
			case 114: // r
				if (jqpan.state === "fh-scrolling") {
					jqpan.fh_scroll_reverse();
				}
				break;
			case 32:  // space
			case 83:  // S
			case 115: // s
				if (jqpan.state !== "fh-scrolling") {
					jqpan.switchToState("fh-scrolling");
				} else {
					jqpan.switchToState("fitheight");
				}
				break;
			case 90:   // Z
			case 122:  // z
				if (jqpan.state === "fitwidth") {
					jqpan.switchToState("fitheight");
				} else if (jqpan.state === "fitheight") {
					position_left_ratio = (jqpan.fh_scroll_left + (jqpan.win_width / 2)) / jqpan.fh_width;
					jqpan.act_scroll_left = Math.round(position_left_ratio * jqpan.act_width - (jqpan.win_width / 2));
					jqpan.act_scroll_boundary_check();
					jqpan.switchToState("actual");
				}
				break;
			case 43: // +
			case 61: // =
				if (jqpan.state === "fh-scrolling") {
					jqpan.fh_scroll_faster();
				}
				break;
			case 45: // -
				if (jqpan.state === "fh-scrolling") {
					jqpan.fh_scroll_slower();
				}
				break;
			case 63:  // ?
			case 72:  // H
			case 104: // h
				jqpan.overlay_fadein();
				jqpan.overlay_fadeout_start();
				break;
		}
		// window.console.log(e.which);
	},

	mousemoveListener : function (e) {
		jqpan.overlay_fadein();
		if (!jqpan.main_mouseover) {
			jqpan.overlay_fadeout_start();
		}
	},

	resizeListener : function (e) {
		jqpan.overlay_fadein();
		jqpan.updateDimensions();
		jqpan.render();
		jqpan.overlay_fadeout_start();
	},

	fh_scroll : function (scroll, direction, smooth) {
		var retValue = false;
		if (direction === "right") {
			jqpan.fh_scroll_left += scroll;
		} else {
			jqpan.fh_scroll_left -= scroll;
		}
		retValue = jqpan.fh_scroll_boundary_check();
		if (smooth) {
			if (scroll < jqpan.fh_width) {
				jqpan.render(true, jqpan.fh_scroll_animate_duration);
			} else {
				jqpan.render(true, jqpan.fh_scroll_end_animate_duration);
			}
		} else {
			jqpan.render();
		}
		return retValue;
	},

	fh_scroll_boundary_check : function () {
		if (jqpan.fh_scroll_left >= (jqpan.fh_width - jqpan.win_width)) {
			jqpan.fh_scroll_left = jqpan.fh_width - jqpan.win_width;
			$("#jqpan-arrow-left").removeClass("jqpan-notstate-hidden");
			$("#jqpan-arrow-right").addClass("jqpan-notstate-hidden");
			jqpan.fh_scrolling_direction = "left";
			return true;
		} else if (jqpan.fh_scroll_left <= 0) {
			jqpan.fh_scroll_left = 0;
			$("#jqpan-arrow-right").removeClass("jqpan-notstate-hidden");
			$("#jqpan-arrow-left").addClass("jqpan-notstate-hidden");
			jqpan.fh_scrolling_direction = "right";
			return true;
		} else {
			$("#jqpan-arrow-left").removeClass("jqpan-notstate-hidden");
			$("#jqpan-arrow-right").removeClass("jqpan-notstate-hidden");
			return false;
		}
	},

	fh_scroll_reverse : function () {
		if (jqpan.fh_scrolling_direction === "left") {
			jqpan.fh_scrolling_direction = "right";
		} else {
			jqpan.fh_scrolling_direction = "left";
		}
	},

	fh_scroll_faster : function () {
		if (jqpan.fh_scrolling_delay <= 20) {
			jqpan.fh_scrolling_amount++;
		} else {
			jqpan.fh_scrolling_delay /= 1.30;
		}
	},

	fh_scroll_slower : function () {
		if (jqpan.fh_scrolling_amount > 1) {
			jqpan.fh_scrolling_amount--;
		} else {
			jqpan.fh_scrolling_delay *= 1.30;
		}
	},

	fh_scrolling_timestep : function () {
		var scrollingDone = jqpan.fh_scroll(jqpan.fh_scrolling_amount, jqpan.fh_scrolling_direction, false);
		if (!scrollingDone) {
			jqpan.fh_scrolling_timeoutID = window.setTimeout(jqpan.fh_scrolling_timestep, jqpan.fh_scrolling_delay);
		} else {
			jqpan.switchToState("fitheight");
		}
	},

	fh_dragStart : function () {
		if (jqpan.state === "fh-scrolling") {
			window.clearTimeout(jqpan.fh_scrolling_timeoutID);
		}
	},

	fh_dragListener : function (e, ui) {
		jqpan.fh_scroll_left = -ui.position.left;
		jqpan.fh_scroll_boundary_check();
		jqpan.render_position();
	},
	
	fh_dragStop : function () {
		if (jqpan.state === "fh-scrolling") {
			jqpan.fh_scrolling_timestep();
		}
	},

	act_scroll : function (scroll, direction, smooth) {
		var retValue = false;
		switch (direction) {
			case "right":
				jqpan.act_scroll_left += scroll;
				break;
			case "left":
				jqpan.act_scroll_left -= scroll;
				break;
			case "down":
				jqpan.act_scroll_top += scroll;
				break;
			case "up":
				jqpan.act_scroll_top -= scroll;
				break;
		}
		jqpan.act_scroll_boundary_check();
		if (smooth) {
			jqpan.render(true, jqpan.act_scroll_animate_duration);
		} else {
			jqpan.render();
		}
	},

	act_scroll_boundary_check : function () {
		if (jqpan.act_scroll_left >= (jqpan.act_width - jqpan.win_width)) {
			jqpan.act_scroll_left = jqpan.act_width - jqpan.win_width;
			$("#jqpan-arrow-left").removeClass("jqpan-notstate-hidden");
			$("#jqpan-arrow-right").addClass("jqpan-notstate-hidden");
		} else if (jqpan.act_scroll_left <= 0) {
			jqpan.act_scroll_left = 0;
			$("#jqpan-arrow-right").removeClass("jqpan-notstate-hidden");
			$("#jqpan-arrow-left").addClass("jqpan-notstate-hidden");
		} else {
			$("#jqpan-arrow-left").removeClass("jqpan-notstate-hidden");
			$("#jqpan-arrow-right").removeClass("jqpan-notstate-hidden");
		}
		if (jqpan.act_scroll_top >= (jqpan.act_height - jqpan.win_height)) {
			jqpan.act_scroll_top = jqpan.act_height - jqpan.win_height;
			$("#jqpan-arrow-up").removeClass("jqpan-notstate-hidden");
			$("#jqpan-arrow-down").addClass("jqpan-notstate-hidden");
		} else if (jqpan.act_scroll_top <= 0) {
			jqpan.act_scroll_top = 0;
			$("#jqpan-arrow-down").removeClass("jqpan-notstate-hidden");
			$("#jqpan-arrow-up").addClass("jqpan-notstate-hidden");
		} else {
			$("#jqpan-arrow-up").removeClass("jqpan-notstate-hidden");
			$("#jqpan-arrow-down").removeClass("jqpan-notstate-hidden");
		}
	},

	overlay_fadein : function () {
		window.clearTimeout(jqpan.overlay_fade_timeoutID);
		$(".jqpan:not(.jqpan-notstate,.jqpan-disabled)").fadeIn(
			jqpan.overlay_fadein_duration, 
			function () {
				$("#jqpan-position-inner, #jqpan-position-outer").css("opacity", "0.5");
			}
		);
	},

	overlay_fadeout_start : function () {
		jqpan.overlay_fade_timeoutID = window.setTimeout(function () {
			$(".jqpan:not(.jqpan-notstate,.jqpan-disabled)").fadeOut(jqpan.overlay_fadeout_duration);
		}, jqpan.overlay_fade_delay);
	},

	positionClick : function (e) {
		if ((jqpan.state === "fitheight") || (jqpan.state === "fh-scrolling")) {
			inner_offset_left = $("#jqpan-position-inner").offset().left;
			if ((e.clientX < inner_offset_left) || (e.clientX > inner_offset_left + jqpan.position_inner_fh_width)) {
				position_left_ratio = (e.clientX - $("#jqpan-position-outer").offset().left - 2) / jqpan.position_width;
				jqpan.fh_scroll_left = Math.round(position_left_ratio * jqpan.fh_width - (jqpan.win_width / 2));
				retValue = jqpan.fh_scroll_boundary_check();
				if (retValue && (jqpan.state === "fh-scrolling")) {
					window.clearTimeout(jqpan.fh_scrolling_timeoutID);
					jqpan.switchToState("fitheight");
				}
				jqpan.render((jqpan.state === "fitheight"), jqpan.fh_scroll_animate_duration);
			}
		} else if (jqpan.state === "actual") {
			inner_offset_left = $("#jqpan-position-inner").offset().left;
			inner_offset_top = $("#jqpan-position-inner").offset().top;
			if ((e.clientX < inner_offset_left) || (e.clientX > inner_offset_left + jqpan.position_inner_act_width) || (e.clientY < inner_offset_top) || (e.clientY > inner_offset_top + jqpan.position_inner_act_height)) {
				position_left_ratio = (e.clientX - $("#jqpan-position-outer").offset().left - 2) / jqpan.position_width;
				position_top_ratio = (e.clientY - $("#jqpan-position-outer").offset().top - 2) / jqpan.position_height;
				jqpan.act_scroll_left = Math.round(position_left_ratio * jqpan.act_width - (jqpan.win_width / 2));
				jqpan.act_scroll_top = Math.round(position_top_ratio * jqpan.act_height - (jqpan.win_height / 2));
				jqpan.act_scroll_boundary_check();
				jqpan.render(true, jqpan.act_scroll_animate_duration);
			}
		}
	},

	act_dragListener : function (e, ui) {
		jqpan.act_scroll_left = -ui.position.left;
		jqpan.act_scroll_top = -ui.position.top;
		jqpan.act_scroll_boundary_check();
		jqpan.render_position();
	},
	
	positionDragStart : function () {
		if (jqpan.state === "fh-scrolling") {
			window.clearTimeout(jqpan.fh_scrolling_timeoutID);
		}
	},

	positionDragListener : function (e, ui) {
		if ((jqpan.state === "fitheight") || (jqpan.state === "fh-scrolling")) {
			position_left_ratio = ui.position.left / (jqpan.position_width - jqpan.position_inner_fh_width - 2);
			jqpan.fh_scroll_left = Math.round(position_left_ratio * (jqpan.fh_width - jqpan.win_width));
			jqpan.fh_scroll_boundary_check();
			jqpan.render();
		} else if (jqpan.state === "actual") {
			position_left_ratio = ui.position.left / (jqpan.position_width - jqpan.position_inner_act_width - 2);
			position_top_ratio = ui.position.top / (jqpan.position_height - jqpan.position_inner_act_height - 2);
			jqpan.act_scroll_left = Math.round(position_left_ratio * (jqpan.act_width - jqpan.win_width));
			jqpan.act_scroll_top = Math.round(position_top_ratio * (jqpan.act_height - jqpan.win_height));
			jqpan.act_scroll_boundary_check();
			jqpan.render();
		}
	},
	
	positionDragStop : function () {
		if (jqpan.state === "fh-scrolling") {
			if (jqpan.fh_scroll_boundary_check()) {
				jqpan.switchToState("fitheight");
			} else {
				jqpan.fh_scrolling_timestep();
			}
		}
	},
	
	arrowClick : function (direction) {
		if (jqpan.state === "fitheight") {
			jqpan.fh_scroll(jqpan.fh_scroll_amount, direction, true);
		} else if (jqpan.state === "actual") {
			jqpan.act_scroll(jqpan.act_scroll_amount, direction, true);
		}
	},

	imageClick : function (e) {
		if (jqpan.state === "fitwidth") {
			position_left_ratio = e.clientX / jqpan.win_width;
			jqpan.fh_scroll_left = Math.round(position_left_ratio * jqpan.fh_width - e.clientX);
			jqpan.fh_scroll_boundary_check();
			jqpan.switchToState("fitheight");
		}
	},

	imageDblclick : function (e) {
		if (jqpan.state === "fitheight") {
			position_left_ratio = (e.clientX + jqpan.fh_scroll_left) / jqpan.fh_width;
			position_top_ratio = e.clientY / jqpan.fh_height;
			jqpan.act_scroll_left = Math.round(position_left_ratio * jqpan.act_width - e.clientX);
			jqpan.act_scroll_top = Math.round(position_top_ratio * jqpan.act_height - e.clientY);
			jqpan.act_scroll_boundary_check();
			jqpan.switchToState("actual");
		} else if (jqpan.state === "actual") {
			position_left_ratio = (e.clientX + jqpan.act_scroll_left) / jqpan.act_width;
			position_top_ratio = (e.clientY + jqpan.act_scroll_top) / jqpan.act_height;
			jqpan.fh_scroll_left = Math.round(position_left_ratio * jqpan.fh_width - e.clientX);
			jqpan.fh_scroll_top = Math.round(position_top_ratio * jqpan.fh_height - e.clientY);
			jqpan.fh_scroll_boundary_check();
			jqpan.switchToState("fitheight");
		}
	},
    
    drawLeftArrow : function (top, left, height, width, thickness) {
        d = $('<div class="jqpan-drawing"></div>');
        d.css("padding", "0px");
        d.css("height", height + "px");
        d.css("width", width + "px");
        d.css("left", left + "px");
        d.css("top", top + "px");
        d.css("position", "absolute");
        e = $("<div></div>");
        e.css("position", "absolute");
        e.css("right", "0px");
        e.css("top", Math.round((height - thickness) / 2) + "px");
		e.css("left", Math.round(height / 2) + "px");
		e.css("height", thickness + "px");
		d.append(e);
        for (i = Math.round(height / 2) - 1; i >= 0; i--) {
            e = $("<div></div>");
            e.css("position", "absolute");
            e.css("left", (Math.round(height / 2) - 1 - i) + "px");
            e.css("top", i + "px");
            e.css("width", "1px");
            e.css("bottom", i + "px");
            d.append(e);
        }
        return d;
    },
	
    drawRightArrow : function (top, left, height, width, thickness) {
        d = $('<div class="jqpan-drawing"></div>');
        d.css("padding", "0px");
        d.css("height", height + "px");
        d.css("width", width + "px");
        d.css("left", left + "px");
        d.css("top", top + "px");
        d.css("position", "absolute");
        e = $("<div></div>");
        e.css("position", "absolute");
        e.css("left", "0px");
        e.css("top", Math.round((height - thickness) / 2) + "px");
		e.css("width", Math.round(width - (height / 2)) + "px");
		e.css("height", thickness + "px");
		d.append(e);
        for (i = 0; i < Math.round(height / 2); i++) {
            e = $("<div></div>");
            e.css("position", "absolute");
            e.css("left", Math.round(width - (height / 2) + i) + "px");
            e.css("top", i + "px");
            e.css("width", "1px");
            e.css("bottom", i + "px");
            d.append(e);
        }
        return d;
    },
	
    drawUpArrow : function (top, left, height, width, thickness) {
        d = $('<div class="jqpan-drawing"></div>');
        d.css("padding", "0px");
        d.css("height", height + "px");
        d.css("width", width + "px");
        d.css("left", left + "px");
        d.css("top", top + "px");
        d.css("position", "absolute");
        e = $("<div></div>");
        e.css("position", "absolute");
        e.css("bottom", "0px");
        e.css("left", Math.round((width - thickness) / 2) + "px");
		e.css("top", Math.round(width / 2) + "px");
		e.css("width", thickness + "px");
		d.append(e);
        for (i = Math.round(width / 2) - 1; i >= 0; i--) {
            e = $("<div></div>");
            e.css("position", "absolute");
            e.css("top", (Math.round(width / 2) - 1 - i) + "px");
            e.css("left", i + "px");
            e.css("height", "1px");
            e.css("right", i + "px");
            d.append(e);
        }
        return d;
    },
	
    drawDownArrow : function (top, left, height, width, thickness) {
        d = $('<div class="jqpan-drawing"></div>');
        d.css("padding", "0px");
        d.css("height", height + "px");
        d.css("width", width + "px");
        d.css("left", left + "px");
        d.css("top", top + "px");
        d.css("position", "absolute");
        e = $("<div></div>");
        e.css("position", "absolute");
        e.css("top", "0px");
        e.css("left", Math.round((width - thickness) / 2) + "px");
		e.css("bottom", Math.round(width / 2) + "px");
		e.css("width", thickness + "px");
		d.append(e);
        for (i = 0; i < Math.round(width / 2); i++) {
            e = $("<div></div>");
            e.css("position", "absolute");
            e.css("top", (height - Math.round(width / 2) + i) + "px");
            e.css("left", i + "px");
            e.css("height", "1px");
            e.css("right", i + "px");
            d.append(e);
        }
        return d;
    },
	
	makeArrows : function () {
		$("body").append(
			'<div id="jqpan-arrow-left" class="jqpan jqpan-fh jqpan-act jqpan-box jqpan-arrow jqpan-clickable"></div>' +
			'<div id="jqpan-arrow-right" class="jqpan jqpan-fh jqpan-act jqpan-box jqpan-arrow jqpan-clickable"></div>' +
			'<div id="jqpan-arrow-up" class="jqpan jqpan-act jqpan-box jqpan-arrow jqpan-clickable"></div>' +
			'<div id="jqpan-arrow-down" class="jqpan jqpan-act jqpan-box jqpan-arrow jqpan-clickable"></div>'
		);
        $("#jqpan-arrow-left").append(jqpan.drawLeftArrow(20, 10, 30, 50, 10));
        $("#jqpan-arrow-right").append(jqpan.drawRightArrow(20, 10, 30, 50, 10));
        $("#jqpan-arrow-up").append(jqpan.drawUpArrow(10, 20, 50, 30, 10));
        $("#jqpan-arrow-down").append(jqpan.drawDownArrow(10, 20, 50, 30, 10));
		$("#jqpan-arrow-left").mouseover(function () { jqpan.main_mouseover = true; } );
		$("#jqpan-arrow-right").mouseover(function () { jqpan.main_mouseover = true; } );
		$("#jqpan-arrow-up").mouseover(function () { jqpan.main_mouseover = true; } );
		$("#jqpan-arrow-down").mouseover(function () { jqpan.main_mouseover = true; } );
		$("#jqpan-arrow-left").mouseout(function () { jqpan.main_mouseover = false; } );
		$("#jqpan-arrow-right").mouseout(function () { jqpan.main_mouseover = false; } );
		$("#jqpan-arrow-up").mouseout(function () { jqpan.main_mouseover = false; } );
		$("#jqpan-arrow-down").mouseout(function () { jqpan.main_mouseover = false; } );
		$("#jqpan-arrow-left").click(function () { jqpan.arrowClick("left"); });
		$("#jqpan-arrow-right").click(function () { jqpan.arrowClick("right"); });
		$("#jqpan-arrow-up").click(function () { jqpan.arrowClick("up"); });
		$("#jqpan-arrow-down").click(function () { jqpan.arrowClick("down"); });
		$("#jqpan-arrow-left").dblclick(jqpan.clearSelection);
		$("#jqpan-arrow-right").dblclick(jqpan.clearSelection);
		$("#jqpan-arrow-up").dblclick(jqpan.clearSelection);
		$("#jqpan-arrow-down").dblclick(jqpan.clearSelection);
		$("head").append(
			'<style type="text/css">' + 
			'.jqpan-arrow { position: absolute; font-size: 36pt; line-height: 10pt; font-weight: bold; }' +
			'#jqpan-arrow-left { left: 20px; top: 50%; padding: 20px 0px; height: 30px; width: 70px; }' +
			'#jqpan-arrow-right { right: 20px; top: 50%; padding: 20px 0px; height: 30px; width: 70px; }' +
			'#jqpan-arrow-up { top: 20px; left: 50%; padding: 0px 20px; height: 70px; width: 30px; }' +
			'#jqpan-arrow-down { bottom: 20px; left: 50%; padding: 0px 20px; height: 70px; width: 30px; }' +
			'</style>'
		);

	},
	
	makePosition : function () {
		$("body").append(
			'<div id="jqpan-position-outer" class="jqpan jqpan-fh jqpan-fh-scrolling jqpan-act">' +
			'<div id="jqpan-position-inner" class="jqpan jqpan-fh jqpan-fh-scrolling jqpan-act jqpan-clickable"></div>' +
			'</div>'
		);
		$("#jqpan-position-outer").mouseover(function () { jqpan.main_mouseover = true; } );
		$("#jqpan-position-outer").mouseout(function () { jqpan.main_mouseover = false; } );
		$("#jqpan-position-inner").mouseover(function () { jqpan.main_mouseover = true; } );
		$("#jqpan-position-inner").mouseout(function () { jqpan.main_mouseover = false; } );
		$("#jqpan-position-inner").draggable({ containment: 'parent', cursor: 'pointer', start: jqpan.positionDragStart, drag: jqpan.positionDragListener, stop: jqpan.positionDragStop });
		$("#jqpan-position-outer").click(jqpan.positionClick);
		if (!jqpan.position_show) {
			$("#jqpan-position-outer").addClass("jqpan-disabled");
		}
		$("head").append(
			'<style type="text/css">' +
			'#jqpan-position-inner, #jqpan-position-outer { cursor: pointer; position: absolute; padding: 0px; margin: 0px; background-color: #000000; opacity: 0.5; border-width: 1px; border-color: #999999; border-style: solid; }' +
			'#jqpan-position-outer { top: 20px; right: 20px; }' +
			'#jqpan-position-inner { top: 0px; background-color: #cccccc; }' +
			'</style>'
		);
	},
	
    drawPlay : function (top, left, height) {
        d = $('<div class="jqpan-drawing"></div>');
        d.css("padding", "0px");
        d.css("height", height + "px");
        d.css("width", height + "px");
        d.css("left", left + "px");
        d.css("top", top + "px");
        d.css("position", "absolute");
        for (i = 0; i < height; i++) {
            e = $("<div></div>");
            e.css("position", "absolute");
            e.css("left", i + "px");
            e.css("top", Math.round(i / 2) + "px");
            e.css("width", "1px");
            e.css("bottom", Math.round(i / 2) + "px");
            d.append(e);
        }
        return d;
    },
	
    drawPause : function (top, left, height, width, thickness) {
        d = $('<div class="jqpan-drawing"></div>');
        d.css("padding", "0px");
        d.css("height", height + "px");
        d.css("width", height + "px");
        d.css("left", left + "px");
        d.css("top", top + "px");
        d.css("position", "absolute");
		e = $("<div></div>");
		e.css("position", "absolute");
		e.css("left", Math.round((width - 3 * thickness) / 2) + "px");
		e.css("top", "0px");
		e.css("width", thickness + "px");
		e.css("bottom", "0px");
		d.append(e);
		e = $("<div></div>");
		e.css("position", "absolute");
		e.css("left", Math.round((width + 2 * thickness) / 2) + "px");
		e.css("top", "0px");
		e.css("width", thickness + "px");
		e.css("bottom", "0px");
		d.append(e);
        return d;
    },
	
	makeMain : function () {
		$("body").append(
			'<div id="jqpan-main" class="jqpan jqpan-box jqpan-fw jqpan-fh jqpan-fh-scrolling jqpan-act">' +
			'<div id="jqpan-caption" class="jqpan jqpan-fw jqpan-fh jqpan-fh-scrolling jqpan-act"></div>' +
			'<br />' +
			'<div id="jqpan-main-scroll-startstop" class="jqpan jqpan-fw jqpan-fh jqpan-fh-scrolling">' +
			'<div id="jqpan-main-scroll-start" class="jqpan jqpan-fw jqpan-fh jqpan-clickable"></div>' +
			'<div id="jqpan-main-scroll-stop" class="jqpan jqpan-fh-scrolling jqpan-clickable"></div>' +
			'</div>' +
			'<div>' +
			'<span id="jqpan-main-zoomin" class="jqpan jqpan-fw jqpan-fh jqpan-clickable">zoom in: z</span>' +
			'<span class="jqpan jqpan-fh">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>' +
			'<span id="jqpan-main-zoomout" class="jqpan jqpan-fh jqpan-act jqpan-clickable">zoom out: esc</span>' +
			'</div>' +
			'<div class="jqpan jqpan-fh-scrolling">' +
			'<span id="jqpan-main-scroll-reverse" class="jqpan-clickable">reverse: r</span>' +
			'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' +
			'<span id="jqpan-main-scroll-faster" class="jqpan-clickable">faster: +</span>' +
			'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' +
			'<span id="jqpan-main-scroll-slower" class="jqpan-clickable">slower: -</span>' +
			'</div>' +
			'</div>'
		);
		$("#jqpan-main-scroll-start").append(jqpan.drawPlay(0, 0, 40));
		$("#jqpan-main-scroll-stop").append(jqpan.drawPause(0, 0, 40, 40, 10));
		$("#jqpan-main-zoomin").click(function () { jqpan.switchToState("fitheight"); });
		$("#jqpan-main-zoomout").click(function () { jqpan.switchToState("fitwidth"); });
		$("#jqpan-main-scroll-start").click(function () { jqpan.switchToState("fh-scrolling"); });
		$("#jqpan-main-scroll-stop").click(function () { jqpan.switchToState("fitheight"); });
		$("#jqpan-main-scroll-reverse").click(jqpan.fh_scroll_reverse);
		$("#jqpan-main-scroll-faster").click(jqpan.fh_scroll_faster);
		$("#jqpan-main-scroll-slower").click(jqpan.fh_scroll_slower);
		$("#jqpan-main").mouseover(function () { jqpan.main_mouseover = true; } );
		$("#jqpan-main").mouseout(function () { jqpan.main_mouseover = false; } );
		$("#jqpan-caption").text($(jqpan.selector).attr("alt"));
		$("head").append(
			'<style type="text/css">' +
			'#jqpan-main { font-size: 11pt; width: 400px; }' +
			'#jqpan-caption { line-height: 30px; font-size: 30px; padding: 0px 30px; }' +
			'#jqpan-main-scroll-startstop { height: 50px; }' +
			'#jqpan-main-scroll-start, #jqpan-main-scroll-stop { height: 50px; width: 50px; position: absolute; left: 50%; margin-left: -25px; }' +
			'</style>'
		);
	},
	
	makeDocument : function () {
		$("head").append(
			'<style type="text/css">' +
			'body { overflow: hidden; }' +
			'body, html, div, img { margin: 0px; padding: 0px; border-width: 0px; }' +
			'body { background-color: #666666; }' +
			'.jqpan-box { position: absolute; padding: 15px; text-align: center; background-color: #000000; opacity: 0.5; color: #999999; font-family: Helvetica, Arial, sans-serif; -webkit-border-radius: 8px; -moz-border-radius: 8px; }' +
            '.jqpan-drawing div { background-color: #999999; }' +
			'.jqpan-clickable { cursor: pointer; }' +
			'.jqpan-clickable:hover { color: #ffffff; }' +
            '.jqpan-clickable:hover .jqpan-drawing div { background-color: #ffffff; }' +
			'.jqpan-notstate { display: none; }' +
			'.jqpan-notstate-hidden { visibility: hidden; }' +
			'</style>'
		);	
		$(document).keypress(jqpan.keypressListener);
		$(document).keydown(jqpan.keydownListener);
		$(document).mousemove(jqpan.mousemoveListener);
		$(window).resize(jqpan.resizeListener);
	},
	
	makeWrapper : function () {
		jqpan.obj_jqpan_selector.wrap('<div id="jqpan-wrapper"></div>');
		jqpan.obj_jqpan_selector.click(jqpan.imageClick);
		jqpan.obj_jqpan_selector.dblclick(jqpan.imageDblclick);
		$("head").append(
			'<style type="text/css">' +
			'#jqpan-wrapper { position: absolute; top: 0px; left: 0px; overflow: hidden; }' +
			'#jqpan-image { position: absolute; top: 0px; left: 0px; }' +
			'</style>'
		);
	},
	
	clearSelection : function () {
		var body = document.getElementsByTagName("body")[0];
		window.getSelection().collapse(body,0);
	}

};

$(document).ready(jqpan.init);

