function Viewer () {
	this.init = function (args) {
		this.container = args.container;
		this.content = args.container + "Content";
		this.nav = args.container + "Nav";
		this.navPrev = args.container + "NavPrev";
		this.navNext = args.container + "NavNext";
		this.navClose = args.container + "NavClose";
		if ($ (this.container).not (":visible").length == 0) {
			this.callback (this, undefined, this.success, "hide", 1);
		}
		this.error = args.error;
		this.success = args.success;
		this.height = args.height;
		this.width = args.width;
		this.loading = args.loading;
		this.elem = args.elem;
		this.url = args.url;
		this.bind (this, undefined);
		this.timeout = undefined;
	}
	this.callback = function (v, ev, callback, msg, clrTimeout) {
		if (msg == "hide") {
			$ (v.container).css ("display", "none");
			$ (v.content).css ("display", "none").html ("");
		}
		$ (v.container).css ("background-image", "none");
		clearTimeout (v.timeout);
		if (clrTimeout) {
			v.timeout = undefined;
		}
		if (callback) {
			callback (ev, msg);
		}
	}
	this.bind = function (v, ev) {
		$ (v.elem).unbind ("click").bind ("click", function () {
			v.show (v, $ (this));
			return false;
		});
	}
	this.show = function (v, ev) {
		if (!v.timeout) {
			var msg = "update";
			var dfr = $.Deferred ();
			if ($ (v.container).not (":visible").length) {
				$ (v.container).css ({
					"display": "block",
					"height": v.height,
					"width": v.width,
					"left": document.documentElement.clientWidth / 2 - v.width / 2,
					"top": document.documentElement.clientHeight / 2 - v.height / 2 - $ (v.nav).height () / 2,
					"position": "absolute"
				});
				$ (v.navClose).unbind ("click").bind ("click", function () {
					v.callback (v, undefined, v.success, "hide", 1);
				});
				$ (v.nav).css ("top", v.height);
				$ (v.navClose).css ("left", v.width / 2 - $ (v.navClose).width () / 2);
				msg = "show";
			};
			$ (v.content).css ("display", "none");
			v.callback (v, ev, undefined, "show", 1);
			v.timeout = setTimeout (function () {
				v.callback (v, ev, v.loading, "", 1);
			}, 1000);
			$.ajax ({
				async: true,
				data: {value: ev.attr ("value")},
				type: "POST",
				url: v.url,
				complete: function (jqXHR, statusText) {
					if (statusText == "success") {
						$ (v.content).html (jqXHR.responseText);
						if ($ (v.content + " img").length) {
							var img = new Image ();
							img.src = $ (v.content + " img").attr ("src");
							if (img.complete) {
								dfr.resolve ();
							} else {
								img.onload = function () {
									dfr.resolve ();
								};
							}
						} else {
							dfr.resolve ();
						}
					} else {
						dfr.reject ();
					}
				}
			});
			$.when (dfr).always (function () {
				if (dfr.isResolved ()) {
					v.callback (v, ev, v.success, msg, 0);
				} else {
					v.callback (v, ev, v.error, "", 0);
				}
				$ (v.navPrev).unbind().bind ("click", function () {
					(ev.prev ().length ? ev.prev () : $ (v.elem).last ()).trigger (jQuery.Event ("click"));
				});
				$ (v.navNext).unbind().bind ("click", function () {
					(ev.next ().length ? ev.next () : $ (v.elem).first ()).trigger (jQuery.Event ("click"));
				});
				$ (v.content).css ({
					"display": "block",
					"left": (v.width - $ (v.content).outerWidth ()) / 2 + "px",
					"top": (v.height - $ (v.content).outerHeight ()) / 2 + "px"
				});				
				setTimeout (function () {
					v.timeout = undefined;
				}, 125);
			});
		}
	}
}
