$(document).ready(function(){
    $(".forwardForm").click(function() {
		 if ($("#"+$(this).attr("id")+'-form').is(":hidden")) {
		 	$("#"+$(this).attr("id")+'-form').slideDown("slow");
      	} else {
		 	$("#"+$(this).attr("id")+'-form').slideUp("slow");
      }
      })

	// debug
	var debug = $('#debug');
	if(debug.length > 0) {
		var toggleDebug = $('<a href="">debug</a>').click(function() {
			debug.toggle();
			return false;
		});
		$('#signinout p:eq(0)').append(' [').append(toggleDebug).append(']');
	}


  });

function addNode(elementName,textNode,parentNode)
{
	var newNode = document.createElement(elementName);
	var bitOfText = document.createTextNode(textNode);
	newNode.appendChild(bitOfText);
	parentNode.appendChild(newNode);
};


function confirmValidation()
{
	var validPassword = 0;
	var passwordsMatch = 0;
	var slugValid = 0;
	var termsValid = 0;
	var slugValid  = /^[a-zA-Z0-9_\.\-]{3,40}$/;
	var passValid = /^\S{6,12}$/;
	var validated = true;
	document.getElementById('PasswordInput').className ='';
	document.getElementById('SlugInput').className = '';;
	document.getElementById('TermsInput').className = '';;

	if (!slugValid.test(document.getElementById('MemberSlug').value)) {
		document.getElementById('SlugInput').className = 'error';
		validated = false;
	}
	if (!passValid.test(document.getElementById('MemberPasswd').value)) {
		document.getElementById('PasswordInput').className = 'error';
		validated = false;
	}
	if(document.getElementById('MemberTOS').checked != 1) {
		document.getElementById('TermsInput').className = 'error';
		validated = false;
	}
	return validated;


/*	if(validPassword && passwordsMatch && slugValid&&termsValid && slugNew) {
		formErrors.appendChild(errorMessage);
		formErrors.style.display = 'block';
		return true;
	} else {
		if(slugNew ==0) addNode('li','That Nickname is already in use.',errorMessage);
		if(termsValid == 0) addNode('li','You must agree to the terms of use.',errorMessage);
		if(validPassword == 0) addNode('li','Password must be 6 to 12 characters.',errorMessage);
		if(passwordsMatch == 0) addNode('li','Your passwords do not match.',errorMessage);
		if(slugValid == 0) addNode('li','Slug must be atleast 3 characters with no spaces.',errorMessage);
		formErrors.appendChild(errorMessage);
		formErrors.style.display = 'block';
		return false;
	} */
};

function forwardValidation(id)
{
	var validEmail = 0;
	document.getElementById('meua-'+id).value=navigator.userAgent;
	var errors = false;
	var forwardTo = document.getElementById('ForwardEmail'+id).value;
	var filter  = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
	if (filter.test(document.getElementById('ForwardEmail'+id).value)) {
		validEmail = 1;
	} else {
		document.getElementById('ForwardEmail'+id).className = 'error';
		errors = true;
	}
	if (errors == false) {
		return true;
	}
	return false;
};

function inputValidation()
{
	var validName = 0;
	var validEmail = 0;
	var validComments = 0;
	var validPassword = 0;
	var errors = false;
	var userType = document.getElementById('userType').value;
	document.getElementById('meua').value=navigator.userAgent;
	document.getElementById('AuthorPassInput').className = '';
	document.getElementById('AuthorEmailInput').className = '';
	document.getElementById('AuthorNameInput').className = '';
	document.getElementById('CommentsInput').className = '';
	var filter  = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
	var passValid = /[\w\s]{3,}/;
	if((userType=='member' || userType=='Anonymous') && passValid.test(document.getElementById('C_Comments').value)) {
		document.getElementById('commentform').setAttribute('action','#'+document.getElementById('comments-add').parentNode.getAttribute('id'));
		return true;
	}
	if(document.getElementById('LoggedIn').value == 0) {
		if(document.getElementById('AuthorName').value != '') {
			validName = 1;
		}
		if (filter.test(document.getElementById('AuthorEmail').value)) {
			validEmail = 1;
		}
		if (passValid.test(document.getElementById('AuthorPass').value)) {
			validPassword = 1;
		}
		if(document.getElementById('AuthorPassInput').style.display != 'none' && validPassword == 0) {
			document.getElementById('AuthorPassInput').className = 'error';
			errors = true;
		}
		if (document.getElementById('AuthorNameInput').style.display != 'none' && validName == 0) {
			document.getElementById('AuthorNameInput').className = 'error';
			errors = true;
		}
		if (validEmail == 0) {
			document.getElementById('AuthorEmailInput').className = 'error';
			errors = true;
		}
	}
	if (passValid.test(document.getElementById('C_Comments').value)) {
		validComments = 1;
	}
	if (validComments == 0) {
		document.getElementById('CommentsInput').className = 'error';
		errors = true;
	}
		document.getElementById('commentform').setAttribute('action','#'+document.getElementById('comments-add').parentNode.getAttribute('id'));
	if (errors == false) {
//		formErrors.appendChild(errorMessage);
//		formErrors.style.display = 'none';
		return true;
	}
//	formErrors.appendChild(errorMessage);
//	formErrors.style.display = 'block';
	return false;
};

function attachCommentForm(commentNode)
{
	var commentForm = document.getElementById('comments-add');
	var replyName = document.getElementById('n'+commentNode);
	var attachPoint = document.getElementById(commentNode);
	if (commentNode == 'c0') {
		document.getElementById('replyLink').style.display = 'none';
		document.getElementById('comments-add').className = 'module wide-full';
		document.getElementById('commentFormTitle').firstChild.nodeValue = 'Add a comment';
	} else {
		document.getElementById('replyLink').style.display = 'inline';
		document.getElementById('comments-add').className = 'module';
		if (replyName) {
			document.getElementById('commentFormTitle').firstChild.nodeValue = 'Reply to '+replyName.value;
		}
	}
	var newCommentForm = commentForm.parentNode.removeChild(commentForm);
	attachPoint.appendChild(newCommentForm);
};

function previewComments() {
	document.getElementById('C_Preview').innerHTML = document.getElementById('C_Comments').value.replace(/\n/g,'<br />');
};

/**
 * SWFObject v1.5.1: Flash Player detection and embed - http://blog.deconcept.com/swfobject/
 *
 * SWFObject is (c) 2007 Geoff Stearns and is released under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 *
 */
if(typeof deconcept == "undefined") var deconcept = {};
if(typeof deconcept.util == "undefined") deconcept.util = {};
if(typeof deconcept.SWFObjectUtil == "undefined") deconcept.SWFObjectUtil = {};
deconcept.SWFObject = function(swf, id, w, h, ver, c, quality, xiRedirectUrl, redirectUrl, detectKey) {
	if (!document.getElementById) { return; }
	this.DETECT_KEY = detectKey ? detectKey : 'detectflash';
	this.skipDetect = deconcept.util.getRequestParameter(this.DETECT_KEY);
	this.params = {};
	this.variables = {};
	this.attributes = [];
	if(swf) { this.setAttribute('swf', swf); }
	if(id) { this.setAttribute('id', id); }
	if(w) { this.setAttribute('width', w); }
	if(h) { this.setAttribute('height', h); }
	if(ver) { this.setAttribute('version', new deconcept.PlayerVersion(ver.toString().split("."))); }
	this.installedVer = deconcept.SWFObjectUtil.getPlayerVersion();
	if (!window.opera && document.all && this.installedVer.major > 7) {
		// only add the onunload cleanup if the Flash Player version supports External Interface and we are in IE
		// fixes bug in some fp9 versions see http://blog.deconcept.com/2006/07/28/swfobject-143-released/
		if (!deconcept.unloadSet) {
			deconcept.SWFObjectUtil.prepUnload = function() {
				__flash_unloadHandler = function(){};
				__flash_savedUnloadHandler = function(){};
				window.attachEvent("onunload", deconcept.SWFObjectUtil.cleanupSWFs);
			}
			window.attachEvent("onbeforeunload", deconcept.SWFObjectUtil.prepUnload);
			deconcept.unloadSet = true;
		}
	}
	if(c) { this.addParam('bgcolor', c); }
	var q = quality ? quality : 'high';
	this.addParam('quality', q);
	this.setAttribute('useExpressInstall', false);
	this.setAttribute('doExpressInstall', false);
	var xir = (xiRedirectUrl) ? xiRedirectUrl : window.location;
	this.setAttribute('xiRedirectUrl', xir);
	this.setAttribute('redirectUrl', '');
	if(redirectUrl) { this.setAttribute('redirectUrl', redirectUrl); }
};
deconcept.SWFObject.prototype = {
	useExpressInstall: function(path) {
		this.xiSWFPath = !path ? "expressinstall.swf" : path;
		this.setAttribute('useExpressInstall', true);
	},
	setAttribute: function(name, value){
		this.attributes[name] = value;
	},
	getAttribute: function(name){
		return this.attributes[name] || "";
	},
	addParam: function(name, value){
		this.params[name] = value;
	},
	getParams: function(){
		return this.params;
	},
	addVariable: function(name, value){
		this.variables[name] = value;
	},
	getVariable: function(name){
		return this.variables[name] || "";
	},
	getVariables: function(){
		return this.variables;
	},
	getVariablePairs: function(){
		var variablePairs = [];
		var key;
		var variables = this.getVariables();
		for(key in variables){
			variablePairs[variablePairs.length] = key +"="+ variables[key];
		}
		return variablePairs;
	},
	getSWFHTML: function() {
		var swfNode = "";
		if (navigator.plugins && navigator.mimeTypes && navigator.mimeTypes.length) { // netscape plugin architecture
			if (this.getAttribute("doExpressInstall")) {
				this.addVariable("MMplayerType", "PlugIn");
				this.setAttribute('swf', this.xiSWFPath);
			}
			swfNode = '<embed type="application/x-shockwave-flash" src="'+ this.getAttribute('swf') +'" width="'+ this.getAttribute('width') +'" height="'+ this.getAttribute('height') +'" style="'+ (this.getAttribute('style') || "") +'"';
			swfNode += ' id="'+ this.getAttribute('id') +'" name="'+ this.getAttribute('id') +'" ';
			var params = this.getParams();
			 for(var key in params){ swfNode += [key] +'="'+ params[key] +'" '; }
			var pairs = this.getVariablePairs().join("&");
			 if (pairs.length > 0){ swfNode += 'flashvars="'+ pairs +'"'; }
			swfNode += '/>';
		} else { // PC IE
			if (this.getAttribute("doExpressInstall")) {
				this.addVariable("MMplayerType", "ActiveX");
				this.setAttribute('swf', this.xiSWFPath);
			}
			swfNode = '<object id="'+ this.getAttribute('id') +'" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="'+ this.getAttribute('width') +'" height="'+ this.getAttribute('height') +'" style="'+ (this.getAttribute('style') || "") +'">';
			swfNode += '<param name="movie" value="'+ this.getAttribute('swf') +'" />';
			var params = this.getParams();
			for(var key in params) {
			 swfNode += '<param name="'+ key +'" value="'+ params[key] +'" />';
			}
			var pairs = this.getVariablePairs().join("&");
			if(pairs.length > 0) {swfNode += '<param name="flashvars" value="'+ pairs +'" />';}
			swfNode += "</object>";
		}
		return swfNode;
	},
	write: function(elementId){
		if(this.getAttribute('useExpressInstall')) {
			// check to see if we need to do an express install
			var expressInstallReqVer = new deconcept.PlayerVersion([6,0,65]);
			if (this.installedVer.versionIsValid(expressInstallReqVer) && !this.installedVer.versionIsValid(this.getAttribute('version'))) {
				this.setAttribute('doExpressInstall', true);
				this.addVariable("MMredirectURL", escape(this.getAttribute('xiRedirectUrl')));
				document.title = document.title.slice(0, 47) + " - Flash Player Installation";
				this.addVariable("MMdoctitle", document.title);
			}
		}
		if(this.skipDetect || this.getAttribute('doExpressInstall') || this.installedVer.versionIsValid(this.getAttribute('version'))){
			var n = (typeof elementId == 'string') ? document.getElementById(elementId) : elementId;
			n.innerHTML = this.getSWFHTML();
			return true;
		}else{
			if(this.getAttribute('redirectUrl') != "") {
				document.location.replace(this.getAttribute('redirectUrl'));
			}
		}
		return false;
	}
};

/* ---- detection functions ---- */
deconcept.SWFObjectUtil.getPlayerVersion = function(){
	var PlayerVersion = new deconcept.PlayerVersion([0,0,0]);
	if(navigator.plugins && navigator.mimeTypes.length){
		var x = navigator.plugins["Shockwave Flash"];
		if(x && x.description) {
			PlayerVersion = new deconcept.PlayerVersion(x.description.replace(/([a-zA-Z]|\s)+/, "").replace(/(\s+r|\s+b[0-9]+)/, ".").split("."));
		}
	}else if (navigator.userAgent && navigator.userAgent.indexOf("Windows CE") >= 0){ // if Windows CE
		var axo = 1;
		var counter = 3;
		while(axo) {
			try {
				counter++;
				axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash."+ counter);
//				document.write("player v: "+ counter);
				PlayerVersion = new deconcept.PlayerVersion([counter,0,0]);
			} catch (e) {
				axo = null;
			}
		}
	} else { // Win IE (non mobile)
		// do minor version lookup in IE, but avoid fp6 crashing issues
		// see http://blog.deconcept.com/2006/01/11/getvariable-setvariable-crash-internet-explorer-flash-6/
		try{
			var axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");
		}catch(e){
			try {
				var axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
				PlayerVersion = new deconcept.PlayerVersion([6,0,21]);
				axo.AllowScriptAccess = "always"; // error if player version < 6.0.47 (thanks to Michael Williams @ Adobe for this code)
			} catch(e) {
				if (PlayerVersion.major == 6) {
					return PlayerVersion;
				}
			}
			try {
				axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
			} catch(e) {}
		}
		if (axo != null) {
			PlayerVersion = new deconcept.PlayerVersion(axo.GetVariable("$version").split(" ")[1].split(","));
		}
	}
	return PlayerVersion;
};
deconcept.PlayerVersion = function(arrVersion){
	this.major = arrVersion[0] != null ? parseInt(arrVersion[0]) : 0;
	this.minor = arrVersion[1] != null ? parseInt(arrVersion[1]) : 0;
	this.rev = arrVersion[2] != null ? parseInt(arrVersion[2]) : 0;
};
deconcept.PlayerVersion.prototype.versionIsValid = function(fv){
	if(this.major < fv.major) return false;
	if(this.major > fv.major) return true;
	if(this.minor < fv.minor) return false;
	if(this.minor > fv.minor) return true;
	if(this.rev < fv.rev) return false;
	return true;
};
/* ---- get value of query string param ---- */
deconcept.util = {
	getRequestParameter: function(param) {
		var q = document.location.search || document.location.hash;
		if (param == null) { return q; }
		if(q) {
			var pairs = q.substring(1).split("&");
			for (var i=0; i < pairs.length; i++) {
				if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {
					return pairs[i].substring((pairs[i].indexOf("=")+1));
				}
			}
		}
		return "";
	}
};
/* fix for video streaming bug */
deconcept.SWFObjectUtil.cleanupSWFs = function() {
	var objects = document.getElementsByTagName("OBJECT");
	for (var i = objects.length - 1; i >= 0; i--) {
		objects[i].style.display = 'none';
		for (var x in objects[i]) {
			if (typeof objects[i][x] == 'function') {
				objects[i][x] = function(){};
			}
		}
	}
};
/* add document.getElementById if needed (mobile IE < 5) */
if (!document.getElementById && document.all) { document.getElementById = function(id) { return document.all[id]; }}

/* add some aliases for ease of use/backwards compatibility */
var getQueryParamValue = deconcept.util.getRequestParameter;
var FlashObject = deconcept.SWFObject; // for legacy support
var SWFObject = deconcept.SWFObject;


/* String Prototype additions */
String.prototype.lTrim = function () {return this.replace(/^\s*/, "");};
String.prototype.rTrim = function () {return this.replace(/\s*$/, "");};
String.prototype.trim = function () {return this.rTrim().lTrim();};
String.prototype.endsWith = function(sEnd) {return (this.substr(this.length-sEnd.length)==sEnd);};
String.prototype.startsWith = function(sStart) {return (this.substr(0,sStart.length)==sStart);};
String.prototype.format = function() {
	var s = this;
	for (var i=0; i < arguments.length; i++){
		s = s.replace("{" + (i) + "}", arguments[i]);
	}
	return(s);
};

String.prototype.removeSpaces = function() {return this.replace(/ /gi,'');};
String.prototype.onlyNum = function() {	return this.replace(/[^\d\-]/g,'');};
String.prototype.onlyPositiveNum = function() {	return this.replace(/[^\d]/g,'');};

/* Array Prototype additions */
Array.prototype.contains = function (d) {
	var e, i = 0;
	while (e = this[i++]) {if (d == e) return true};
	return false;
};

/* Finds the index of the first occurence of item in the array, or -1 if not found */
Array.prototype.indexOf = function(item) {
  for (var i = 0; i < this.length; i++) {
    if (this[i] == item) { return i; }
  }
  return -1;
};

/* Adds an item on to the end of an array */
Array.prototype.add = function(item) {
  this[this.length] = item;
};

Array.prototype.remove = function(item) {
  for(var i=0; i<this.length; i++) {
    if(this[i]==item) { this.splice(i,1);}
  }
  return this;
};

/*
The Reader Class

Example instantiation
---------------------------------------------------------------
	var myReader=new Reader(pages,sizes,opts);
	myReader.gotoPage(7);
	... etc


The three instantiation arguments
---------------------------------------------------------------
	1) pages: an array of objects describing the pages available in the system
		[{name:'Page 1', url:'one.jpg', handedness:'L'},{...},{...}]

	2) sizes: an array of WIDTHxHEIGHT values, from smallest to largest. The first size will ALWAYS be used for thumbnails
		['100x150','300x450','1000x1500','2000x3000']

	3) opt: a hash of values. All are optional. If not provided, See immediately below (var opt={...}) for the defaults.
		opt.canvasId: the DOM id of the canvas div. Defaults to 'canvas'
		opt.showThumbails: Whether or not to initially show thumbnails. If "true," overrides the starting page, layout, etc.
			- Accepts: true|false.
		opt.page: The starting page number. Page 0 is the cover.
			- Accepts: a number 0-max physical page.
		opt.size: the size of image to use for non-thumbnail views. If provided in conjunction with a "thumbnail" starting mode,
           this is the size that will be used by default when the user navigates to a onepage or twopage mode.
			- Accepts: fitx|fity|fitxy|large| a  number from 1 to max size-1
		opt.layout: how to layout pages in non-thumbnail views. If provided in conjunction with a "thumbnail" starting mode,
           this is the layout that will be used by default when the user clicks on a thumbnail.
		  - Accepts: onepage|twopage
		opt.debug: whether or not to display the debug overlay.
			- Accepts: true|false
		opt.fudge: The program uses this value when determining the size of image to use in the "best fit" sizing modes. A typical value might be 1.1, which would mean
            that the image can be up to 10% bigger than the size of the "canvas" that the Reader uses to display the image. You would use a fudge
						greater than 1 if you want to want the user to get the next the largest reasonably sized image, even if they have to scroll a bit
						to see the entire image. A fudge factor of 1 (the default) means that an imager large than the canvas size will *never* be used
						for the "best fit" sizing modes.
			- Accepts: a number >= 1
		opt.gutter: a size in pixels. This is the extra space around an image, a whitespace buffer between the image and the edge of the reader.
			- Accepts: a number >= 0
		opt.sessionId: an arbitrary string; reader will send this back with its ajax confirmation pings.
		opt.pingUrl: the URL to which to send "page Read" pings.
		opt.preloadDepth: the number of pages to preload. In two-page mode, 2(preloadDepth) images will be loaded.

Reader Public Methods
---------------------------------------------------------------
	myReader.setLayout(layout);
		- changes the layout (for example, onepage mode, twopage mode)
		- Accepted values: onepage|twopage|thumbnail
	myReader.toggleLayout();
		- toggles the layout between onepage and twopage mode. Takes no args.
	myReader.setSize(size);
		- sets the size of the onepage or twopage images.
		- Accepted values: fitx|fity|fitxy|large| a  number from 1 to max size-1
	myReader.gotoPage(pageNum);
		- makes the reader goto the page specified. The cover is page 0.
		- Accepted values: a number in the rage 0-max physical page. Note that this is the *physical* page number, NOT the page's index in the page array.
	myReader.getPage();
		- returns a number representing the current phyisical page the reader is displaying.
	myReader.nextPage();
	myReader.previousPage();
		- navigates to the next/previous page. If reader is at the "end", the method call will do nothing. If the reader is in two-page mode,
		  it will increment to the next L-hand/R-hand pair of pages.

Dependencies
---------------------------------------------------------------
	1) jquery.js (jquery.com) and some jquery-based libraries, currently encapsulated in ilib.js
	2) a few functions from acl-general.js -- these functions may be incorporated into this file to improve encapsulation if desired.

Copyright 2006-2008 Andre Lewis (earthcode.com) and ComicMix
*/

function Reader(pages,sizes,opt_) {
// Defaults for the "opt" argument
	var opt = {
		canvasId:'canvas',
		showThumbnails:true,
		page:0,
		size:'fitx',
		layout:'onepage',
		debug:false,
		fudge:1.0,
		gutter:0,
		sessionId:'',
		pingUrl:'',
		preloadDepth:0,
		next_issue: null,
		prev_issue: null
		};
	jQuery.extend(opt, opt_); // overrides opt with user-supplied arguments
	this.opt=opt;

	// acl function
	var acl = {};

	acl.style=function(a,o){
		if (typeof(a)=='string') {a=acl.getManyElements(a);}
		for (var i=0;i<a.length;i++){
			for (var s in o) { a[i].style[s]=o[s];}
		}
	};
	acl.getManyElements=function(s){
		t=s.split(',');
		a=[];
		for (var i=0;i<t.length;i++){a[a.length]=$id(t[i])};
		return a;
	};

	/*
	To Use:
		var pos = acl.getElementPosition(element);
		var left = pos.left;
		var top = pos.top;
	*/
	acl.getElementPosition = function(eElement) {
			var nLeftPos = eElement.offsetLeft;          // initialize var to store calculations
			var nTopPos = eElement.offsetTop;            // initialize var to store calculations
			var eParElement = eElement.offsetParent;     // identify first offset parent element
			while (eParElement != null ) {                // move up through element hierarchy
						nLeftPos += eParElement.offsetLeft;      // appending left offset of each parent
						nTopPos += eParElement.offsetTop;
						eParElement = eParElement.offsetParent;  // until no more offset parents exist
			}
			return {left:nLeftPos, top:nTopPos};
	};



	/* viewport dimensions */
	acl.viewportWidth=function() {
	  return self.innerWidth || (document.documentElement.clientWidth || document.body.clientWidth);
	};

	acl.viewportHeight=function() {
	  return self.innerHeight || (document.documentElement.clientHeight || document.body.clientHeight);
	};

	acl.viewportSize=function() {
		return { width: acl.viewportWidth(), height: acl.viewportHeight() };
	};

	var $id = function(sId) { return document.getElementById(sId); };


	// All of our instance variables are grouped under G:
	var G={
		canvas:$id(opt.canvasId),
		canvasSize:{x:0,y:0},
		canvasPos:acl.getElementPosition($id(opt.canvasId)),
		draggableSize:{x:0,y:0},
		size:opt.size,
		layout:opt.layout,
		showThumbnails:opt.showThumbnails,
		pageNum:0,
		pages:pages,
		next_issue:opt.next_issue,
		prev_issue:opt.prev_issue,
		sizes:sizes,
		loadedImages:{}, // used to retain images that have already been preloaded
		imageSizeIndex:0, // holds the current image size index
		innerCanvas: null, // is set later
		initialized:false
	};

	// ----------------------------------------------------------------------------------
	// Private method for overriding defualt settings with those stored in cookies
	// ----------------------------------------------------------------------------------
	function getCookiePrefs() {
		setLayout($.cookie('readerLayout'));
		setSize($.cookie('readerSize'));
	}

	// ----------------------------------------------------------------------------------
	// Public Layout and sizing methods
	// ----------------------------------------------------------------------------------
	/* valid layouts are 'onepage','twopage','thumbnail' */
	function setLayout(layout) {
		if (['onepage','twopage','thumbnail'].contains(layout)) {
			if (layout == 'thumbnail') {
				G.showThumbnails=true;
				G.innerCanvas.style.display = 'none';
			} else {
				G.layout=layout;
				$.cookie('readerLayout', layout, {path: '/', expires: 7});
				G.innerCanvas.style.display = 'block';
				G.showThumbnails=false;
			}
			renderCanvas();
			renderDebug();
		}
	};

	function getLayout() {
		return G.layout;
	};


	/* Set the size of the image -- either numeric sizes, or 'fitx','fity','fitxy','large' */
	function setSize(size) {
		if (['fitx','fity','fitxy','large'].contains(size) || String(size).match(/^\d+$/)) {
			G.innerCanvas.style.display = 'block';
			G.showThumbnails=false;
			G.size=size;
			$.cookie('readerSize', size, {path: '/', expires: 7});
			renderCanvas();
			renderDebug();
			$(document).trigger('setSize');
		}
	};

	function getSize() {
		return G.size;
	};

	function zoomIn() {
		cursize = determineImageSizeIndex();
		if((sizes.length-1) == cursize) return ;
		setSize(cursize+1);
	};

	function zoomOut() {
		cursize = determineImageSizeIndex();
		if(1 == cursize) return;
		setSize(cursize-1);

	};

	function toggleLayout() {
		if (G.layout != 'onepage') {
			setLayout('onepage');
		} else {
			setLayout('twopage')
		}
	}

	// ----------------------------------------------------------------------------------
	// Public page navigation methods
	// ----------------------------------------------------------------------------------
	/* This takes the physical page number, not the page index */
	function gotoPage(pageNum, track){
		pageNum=parseInt(pageNum);
		if(pageNum == -1 && G.prev_issue != null &&  G.prev_issue != '') {
			window.location = G.prev_issue;
		}
		if(pageNum == G.pages.length && G.next_issue != null && G.next_issue != '') {
			window.location = G.next_issue;
		}
		if (pageNum < pages.length && pageNum>=0) { // only go if its a valid page
			// simple handling of two-page layout -- if we're in twopage layout, ensure we land on a left-hand page
			if(G.layout == 'twopage') {
				curpage = G.pages[pageNum];
				prevpage = G.pages[pageNum-1];

				if(prevpage != null && curpage.handedness == 'R' && prevpage.handedness == 'L') {
					return gotoPage(pageNum - 1, false);
				}
			}
			G.pageNum=pageNum;
			if (typeof(track) == 'undefined' || track == true) {
				$.ajaxHistory.update('p'+pages[G.pageNum].nav);
			}
			G.showThumbnails=false; // in case we're in thumbnail layout -- get out of it
			G.innerCanvas.style.display = 'block';
			renderCanvas();
			renderDebug();
		}

	};
	function nextPage(){
		if (getNextPage() !== false) {
			gotoPage(getNextPage());
		} else if (G.next_issue != null && G.next_issue != '') {
			gotoPage(G.pages.length);
		}
	}
	function previousPage(){
		if (getPreviousPage() !== false) {
			gotoPage(getPreviousPage());
		} else 	if (G.prev_issue != null && G.prev_issue != '') {
			gotoPage(-1);
		}
	}
	function getPage(){
		return G.pageNum;
	}
	function getNextPage() {
		var numPagesToMove = 1;
		if(G.layout == 'twopage') {
			curpage = G.pages[G.pageNum];
			nextpage = G.pages[G.pageNum+1];
			if(nextpage != null && nextpage.handedness == 'R' && curpage.handedness == 'L')
				numPagesToMove = 2;
		}

		if (G.pageNum < G.pages.length-numPagesToMove) {
			return G.pageNum+numPagesToMove;
		}
		return false;
	}
	function getPreviousPage() {
		var numPagesToMove = 1;
		if(G.layout == 'twopage') {
			curpage = G.pages[G.pageNum];
			prevpage = G.pages[G.pageNum-1];
			prevprevpage = G.pages[G.pageNum-2];
			if(prevprevpage != null && prevpage != null && prevprevpage.handedness == 'L' && prevpage.handedness == 'R' && curpage.handedness == 'L')
				numPagesToMove = 2;
		}

		if ((G.pageNum-numPagesToMove) >= 0) {
			return G.pageNum-numPagesToMove;
		}
		return false;
	}
	function getPageFromURI() {
		var re = new RegExp(/#p(\d+)/);
		var pg = re.exec(location.hash);
		if (pg && typeof(pg[1]) != 'undefined') {
			for(var i = 0; i < pages.length; i++) {
				if (pages[i].nav == pg[1]) {
					gotoPage(i);
					return true;
				}
			}
		}
		return false;
	}

	// ----------------------------------------------------------------------------------
	// Private and supporting methods
	// ----------------------------------------------------------------------------------
	/* The all-important renderCanvas method. Takes into account layout, image sizing, canvas sizing, etc. to render the Reader display. */
	function renderCanvas() {
		if (G.initialized) {

			if (G.showThumbnails) {
				G.oneImage.style.display='none';
				G.twoPage.style.display='none';
				G.thumbnails.style.display='block';
				acl.style([G.innerCanvas],{top:'0px',left:'0px', height:G.canvasSize.y, width:G.canvasSize.x});
				$(document).trigger('thumbnailMode');
			} else {
				// handles switching of one-page to two-page on a R handed page
				if(G.layout == 'twopage') {
					curpage = G.pages[G.pageNum];
					prevpage = G.pages[G.pageNum-1];
					if(curpage.handedness == 'R' && prevpage != null && prevpage.handedness == 'L')
						return gotoPage(G.pageNum-1);
				}

				// given the available image sizes, find the one that's the best fit for our current canvas size
				G.imageSizeIndex=determineImageSizeIndex();
				setDraggableSize(G.imageSizeIndex,G.layout);
				imageSize=G.sizes[G.imageSizeIndex].split('x');

				// centering -- the whole reason we have canvasInner
				var x=(G.canvasSize.x-G.draggableSize.x)/2
				if (x<40) {x=40}; //adjust for side nav
				var y=(G.canvasSize.y-G.draggableSize.y)/2;
				if (y<0) {y=0};
				var width=(G.draggableSize.x > (G.canvasSize.x-80)) ? G.canvasSize.x-80 : G.draggableSize.x //adjust for side nav
				var height=(G.draggableSize.y > G.canvasSize.y) ? G.canvasSize.y : G.draggableSize.y

				acl.style([G.innerCanvas],{left:x+'px',top:y+'px',width:width+'px',height:height+'px'});

				if (G.layout == 'onepage' || (!isTwoPageLayout() && G.layout == 'twopage')){
					G.oneImage.style.display='block';
					G.twoPage.style.display='none';
					G.thumbnails.style.display='none';

					// show the single image
					var url=getImageUrl(G.pageNum,G.imageSizeIndex);
					var img=$(G.oneImage);
					if (img.attr('src') !=url) {
						//console.debug('current: '+img.attr('src')+' -- new: '+url);
						G.loadingDiv.style.display='block';
						img.attr('src',url).css({top:'0px',left:'0px'});
						ping(G.pageNum,'',G.sizes[G.imageSizeIndex]);
						preloadImages();
					}
				} else if (G.layout == 'twopage'){

					//show page + next page
					G.oneImage.style.display='none';
					G.twoPage.style.display='block';
					G.thumbnails.style.display='none';

					// layout and show the two images
					var leftSrc=getImageUrl(G.pageNum,G.imageSizeIndex);
					if ($(G.leftPage).attr('src') !=leftSrc) {
						var rightPageNum=''; // used to ping the server
						// take care of the left-hand image
						G.loadingDiv.style.display='block';
						$(G.twoPage).css({width:(Number(imageSize[0])*2+opt.gutter)+'px',top:'0px',left:'0px'});
						$(G.leftPage).attr('src',getImageUrl(G.pageNum,G.imageSizeIndex));
						// potentially take care of the right-hand page. There's never a time
						// when we need to change the right image *without* changing the left image.
						if (G.pageNum+1<G.pages.length) {
							var rightImagePos=imageSize[0]+'px';
							$(G.rightPage).attr('src',getImageUrl(G.pageNum+1,G.imageSizeIndex)).css({left:rightImagePos,visibility:'visible'});
							rightPageNum=G.pageNum+1;
						} else {
							$(G.rightPage).css({visibility:'hidden'});
						}

						ping(G.pageNum,rightPageNum,G.sizes[G.imageSizeIndex]);
						preloadImages();
					}; // end of if the leftPage src is different
				}; // end of if onepage/twopage
				// triggers
				$('#inner-canvas').trigger('canvasResize');
			}; // end of thumbnail
		renderDebug();
		};// end of if initialized
	};

	/* This function is called when pages are actually displayed in one-page or two-page mode.
	In sends confirmation pings back to the server at the URL provided in opts.
	*/
	function ping(pageLeft,pageRight,imageSize){
		var pingData={pageLeft:pageLeft, pageRight:pageRight, sessionId:opt.sessionId,
							thumbnails:G.showThumbnails,layout:G.layout,sizeMode:G.size,imageSize:imageSize,
							canvasSize:$(G.canvas).width()+'x'+$(G.canvas).height()};
	//	$.get( opt.pingUrl, pingData);
	}

	/* Preload images by setting the source of off-screen image tags. The number of images preloaded
	depends on opt.preloadDepth and the current page mode. Images are tracked in G.preloadedImages by URL,
	so the are only loaded once.*/
	function preloadImages() {
		var numToPreload=opt.preloadDepth * (G.layout == 'twopage' ? 2 : 1 );
		for(i=1;i<=numToPreload;i++){
			var pageNum=parseInt(G.pageNum)+i;
			if (pageNum<G.pages.length) {
				var url=getImageUrl(pageNum,G.imageSizeIndex);
				if (G.loadedImages[url] !=true){
					var img=document.createElement('img');
					$(img).css({position:'absolute',top: '0px',left:'-3000px',width:'60px'}).attr('src',url);
					document.body.appendChild(img);
					G.loadedImages[url]=true;
					//console.debug("loaded "+url);
				}
			}
		}
	}

	/* Set global vars for the image size were are using.
		 The layout will come into play when the draggable size is a derivation of
		 of the image size (for example, two-page layout.
	*/
	function setDraggableSize(imageNum,layout) {
		temp=G.sizes[imageNum].split('x');
		G.draggableSize.x=temp[0] * (isTwoPageLayout() ? 2 : 1); // this takes care of twopage layout for sizing
		G.draggableSize.x=(G.draggableSize.x > G.canvasSize.x) ? G.draggableSize.x + 80 : G.draggableSize.x // adjust for side nav
		G.draggableSize.y=temp[1];
	};

	function isTwoPageLayout() {
		if(G.layout == 'twopage') {
			curpage = G.pages[G.pageNum];
			prevpage = G.pages[G.pageNum-1];
			nextpage = G.pages[G.pageNum+1];

			if((curpage.handedness == 'L' && nextpage == null) ||
				(curpage.handedness == 'R' && (prevpage == null || prevpage.handedness == 'R'))) {
				return false;
			}
			else if(curpage.handedness == 'L' && nextpage != null) {
				return true;
			}
		}
		return false;
	}

	/* Taking into account images available, canvas size, and layout mode, and size mode,
		 this method determine which imagesize fits best in the canvas.
	   This function returns an integer representing an index in the sizes array (as passed in at instantiation)
	   it will NEVER return 0, which represents the thumbnail.
	   if G.size = a number or 'large', this method will return the appropriate number without going through the sizing algorithm.
	*/
	function determineImageSizeIndex(){
		var res=1;
		if (G.size =='large') {
			res=G.sizes.length-1;
		} else if (String(G.size).match(/^\d+$/) && G.size >0 && G.size <G.sizes.length){
			res=G.size;
		} else {
			// it's a "bestfit" scenario
			var x=G.canvasSize.x;
			var y=G.canvasSize.y;
			var done=false;
			var width=null;
			var height=null;
			for (var i=G.sizes.length-1; i>=1; i--){ // iterate backwords, but only down unto 1 -- thumbnail will never be the best fit
				temp=G.sizes[i].split('x');
				width=temp[0];
				height=temp[1];
				// account for twopage layouts
				if (isTwoPageLayout()) {
					width=width*2;
				}
				if (G.size=='fitx') {
					done = (x*opt.fudge>=width);
				}else if (G.size=='fity') {
					done = (y*opt.fudge>=height);
				}else { //fitxy
					done = ( x*opt.fudge>=width && y*opt.fudge>=height)
				}
				if (done) { // this is where the fudge factor will come into play
					res=i;
					break;
				}
			}
		}
		return parseInt(res);
	};

	/* Returns the image url for a given image and size.
	This is the function that can/should be updated when image URLs become more complicated or obfuscated. */
	function getImageUrl(pageNum, imageSizeIndex){
		var page = G.pages[pageNum];
		if(imageSizeIndex == (G.sizes.length-1)) {
			var size='';
		} else {
			var size=G.sizes[imageSizeIndex].split('x')[0] + 'w/';
		}
		var url=page.url.replace('SIZE/',size);
		//debug('getImageUrl: '+url);
		return url;
	};

	/* pad the given string with enough 0's so length = given length */
	function padString(string, length){
		var padding = "";
		var padLength = length-String(string).length;
		for (var i = 0; i<padLength; i++) { padding += "0"}
		return padding+string;
	};

	function renderDebug() {
		if (opt.debug) {
			html ='Thumbnails?: '+G.showThumbnails;
			html +='<br /> layout: '+G.layout;
			html +='<br /> size: '+G.size;
			html +='<br /> Canvas: '+$(G.canvas).width()+'x'+$(G.canvas).height();
			html +='<br /> pages: '+pages.length;
			html +='<br /> page#: '+getPage()+ '('+pages[G.pageNum].name+')';
			html +='<br /> draggable x,y: '+G.draggableSize.x+','+G.draggableSize.y;
			$(G.debugDiv).html(html);
		}
	};

	/* This function is called while the user drags the image. It checks to make sure the image doesn't get dragged too far.
		The complexity is really only needed because of the original requirement to have a "gutter" around the image; it's
		overkill when the gutter is set to 0.
	*/
	function dragCallback(e, ui) {
		var pos = checkBounds(ui.position.left, ui.position.top);
		return {top: pos.y, left: pos.x};
	}

	function checkBounds(x, y){
		var leftBound=(G.canvasSize.x - G.draggableSize.x - opt.gutter);
		leftBound = leftBound > 0 ? 0 : leftBound;
		x = x < leftBound ? leftBound : x;

		var topBound=(G.canvasSize.y - G.draggableSize.y - opt.gutter);
		topBound = topBound > 0 ? 0 : topBound;
		y = y < topBound ? topBound : y;

		x = x > opt.gutter ? opt.gutter : x;
		y = y > opt.gutter ? opt.gutter : y;

		return {x:x,y:y}
	};

	/* Called whenever the browser viewport is resized; determines the new canvas size and re-renders the Reader.*/
	function setCanvasSize() {
		G.canvasSize.x=$('#'+opt.canvasId).parent().width();
		G.canvasSize.y=acl.viewportHeight();
		$(G.canvas).width(G.canvasSize.x+'px').height(G.canvasSize.y+'px');
		renderCanvas();

		// When the sidebar gets rolled-up make sure the image hasn't been dragged too far.
		var elm = ($('#inner-canvas div:visible').length) ? '#inner-canvas div':'#inner-canvas img:visible:first';
		var pos = $(elm).offset();
		var newPos = checkBounds(pos.left, pos.top);
		$(elm).css({top: newPos.y+'px', left: newPos.x+'px'});

		renderDebug(); //debug
	};

	function grabbingCursor() {
		//console.debug($.browser);
		//document.body.style.cursor = 'wait';
		G.oneImage.style.cursor='-moz-grabbing';
	};

	function handCursor() {
		//console.debug('stop dragging');
		G.oneImage.style.cursor='-moz-grab';
		//document.body.style.cursor = 'default';
	};

	//--------------------------------------------------------------
	// Specify methods that are public here
	//--------------------------------------------------------------
	this.setLayout=setLayout;
	this.getLayout=getLayout;
	this.toggleLayout=toggleLayout;
	this.setSize=setSize;
	this.getSize=getSize;
	this.zoomIn=zoomIn;
	this.zoomOut=zoomOut;
	this.gotoPage=gotoPage;
	this.getPage=getPage;
	this.getNextPage=getNextPage;
	this.getPreviousPage=getPreviousPage;
	this.nextPage=nextPage;
	this.previousPage=previousPage;
	this.renderDebug=renderDebug;
	this.getImageUrl=getImageUrl;
	this.setCanvasSize=setCanvasSize;


	//--------------------------------------------------------------
	// Start Initialization
	//--------------------------------------------------------------
	//the innerCanvas holds both onePage and twoPage panes. It exists because we need
	// to center the visual elements without messing up dragging.
	var innerCanvas=document.createElement('div');
	innerCanvas.id = 'inner-canvas';
	G.canvas.appendChild(innerCanvas);
	G.innerCanvas=innerCanvas;
	acl.style([G.innerCanvas],{position:'absolute',top:'0px',left:'0px',overflow:'hidden'});

	// append the image for showing one page at a time
	var oneImage = document.createElement('img');
	oneImage.className='comic-page one-page';
	var grabStyle=$.browser.mozilla ? '-moz-grab' : 'move';
	acl.style([oneImage],{display:'none',position:'relative',top:'0px',left:'0px','zIndex':0,cursor:grabStyle});
	G.oneImage=oneImage;
	G.innerCanvas.appendChild(oneImage);
	$(oneImage).draggable({
			drag:dragCallback
	}).load(function(){
		G.loadingDiv.style.display='none';
	});

	oneImage.parentNode.style.background='black';

	// append the image for two-page layout
	var twoPage = document.createElement('div');
	var leftPage = document.createElement('img');
	var rightPage = document.createElement('img');
	leftPage.className='comic-page left-page';
	rightPage.className='comic-page right-page';
	//acl.style([leftPage,rightPage],{border:'1px solid #aaa'}); // use this to programatically set styles of two-page images
	twoPage.appendChild(leftPage);
	twoPage.appendChild(rightPage);
	$(rightPage).css({position:'absolute'});
	acl.style([twoPage],{display:'none',position:'absolute',top:'0px',left:'0px','zIndex':0,cursor:grabStyle});
	G.twoPage=twoPage;
	G.leftPage=leftPage;
	G.rightPage=rightPage;
	G.innerCanvas.appendChild(twoPage);
	$(twoPage).draggable({
			drag:dragCallback
	});
	$(rightPage).load(function() {
		G.loadingDiv.style.display='none';
	});

	// append the div for showing thumbnails.
	var thumbnails = document.createElement('div');
	//acl.style([thumbnails],{display:'none',overflow:'auto',position:'absolute',top:'5px',left:'5px','zIndex':1,cursor:grabStyle});
	acl.style([thumbnails],{display:'none',overflow:'auto',height:'100%',padding:'5px 0 0 5px','zIndex':1,cursor:grabStyle});
	G.thumbnails=thumbnails;
	G.canvas.appendChild(thumbnails);
	$(thumbnails).draggable({
			drag:dragCallback
	});
	/// populate thumbnails
	var html='';
	var temp=G.sizes[0].split('x');
	var width=temp[0];
	var height=temp[1];
	for (var j = 0; j < 1; j++){
		for (var i=0;i<pages.length;i++) {
			page=pages[i];
			var url=getImageUrl(i,0);
			html+="<a class='thumbnail-container' style='width:{0}px;height:{1}px;float:left;' href='#' onclick='reader.gotoPage({5});return false;'><img src='{2}' class='thumbnail' style='cursor:pointer' width='{3}' height='{4}' /><span class='thumbnail-caption' style='text-align:center;color:#666'>{6}</span></a>".format(20+parseFloat(width),20+parseFloat(height),url,width,height,i,page.name);
		}
		html+="<div style='clear:both;'></div>";
	};
	$(G.thumbnails).html(html);

	// append the element for rendering debug output
	if(opt.debug) {
	var debugDiv = document.createElement('div');
	acl.style([debugDiv],{position:'absolute',top:'30px',right:'10px','zIndex':1000,background:'#55D',color:'white',padding:'0px 4px 0px 4px'});
	G.canvas.appendChild(debugDiv);
	G.debugDiv=debugDiv;
	}

	// append the "loading" div
	var loadingDiv = document.createElement('div');
	acl.style([loadingDiv],{display:'none',position:'absolute',top:'5px',right:'10px','zIndex':1000,background:'#D00',color:'white',padding:'0px 4px 0px 4px'});
	loadingDiv.innerHTML='loading ...';
	G.canvas.appendChild(loadingDiv);
	G.loadingDiv=loadingDiv;

	// ----------------------------------------------------------------------------------------------------------
	// apply event binding
	$(window).resize(function(){
		setCanvasSize();
	});
	/* mousewheel handling */
	$(G.canvas).mousewheel(function(event, delta) {
		if (!isTwoPageLayout()){
			var display = G.oneImage;
		} else {
			var display = G.twoPage;
		}

		var top=Number(display.style.top.onlyNum());
		var left=Number(display.style.left.onlyNum());
		var nudge=20;

		if(delta > 0) {
			nudge=top>=0?0:nudge;
			$(display).css({top:top+nudge+'px'});
		} else if(delta < 0) {
			nudge=top<-1*(G.draggableSize.y-G.canvasSize.y)?0:nudge;
			$(display).css({top:top-nudge+'px'});
		}
		event.preventDefault();

	});

	/* keypress handling */
	$(document).keydown(function(e){
		if (!isTwoPageLayout()){
			var display = G.oneImage;
		} else {
			var display = G.twoPage;
		}

		var top=Number(display.style.top.onlyNum());
		var left=Number(display.style.left.onlyNum());
		var nudge=20;

		k=e.keyCode;
		switch (k) {
			case 80: // p
				gotoPage(-1);
			break;
			case 78: // n
				gotoPage(G.pages.length);
			break;
			case 90: // z
			case 190: // +
				zoomIn();
			break;
			case 88: // x
			case 188: // -
				zoomOut();
			break;
			case 66: // 0
			case 48: // 0
				setSize('fitxy');
			break;
			case 33: //pageup
				// move image up the height of one canvas, or to the previous page
				if (top>=0) {
					previousPage();
					readernav.choosePage(getPage());
				} else {
					$(display).animate({top:0},'normal');
				}
			break;
			case 32: //spacebar
			case 34: //pagedown
				// move image down the height of one canvas, or to the next page
				if (G.draggableSize.y < G.canvasSize.y || top-G.canvasSize.y <= -1*G.draggableSize.y) {
					nextPage();
					readernav.choosePage(getPage());
				} else {
					var footer = G.draggableSize.y-G.canvasSize.y+top; // top is assumed to be negative if anything
					var delta = G.canvasSize.y < footer ? G.canvasSize.y : footer;
					$(display).animate({top:(top-delta)},'normal');
				}
			break;
			case 37: // cursor left
				nudge=left>=0?0:nudge;
				$(display).css({left:left+nudge+'px'});
			break;
			case 38: // cursor up
				nudge=top>=0?0:nudge;
				$(display).css({top:top+nudge+'px'});
			break;
			case 39: // cursor right
				nudge=left<-1*(G.draggableSize.x-G.canvasSize.x)?0:nudge;
				$(display).css({left:left-nudge+'px'});
			break;
			case 40: // cursor down
				nudge=top<-1*(G.draggableSize.y-G.canvasSize.y)?0:nudge;
				$(display).css({top:top-nudge+'px'});
			break;
		}
	});

	getCookiePrefs();

	// set canvas size
	setCanvasSize();
	if (!G.showThumbnails) {
		setLayout(G.layout);
		setSize(G.size);
		if (!getPageFromURI()) {
			gotoPage(opt.page);
		}
	}

	// now, render the canvas. It hasn't rendered yet because G.initialized has been false until now
	G.initialized=true;
	renderCanvas();
	//--------------------------------------------------------------
	// End Initialization
	//--------------------------------------------------------------
};
//debug=console.debug;



/*
The Reader Navigation Class

Copyright 2006-2007 ComicMix
*/

function ReaderNavigation(reader,pages,sizes) {

	// All of our instance variables are grouped under G:
	var G={
		pages:pages,
		sizes:sizes,
		reader:reader,
		pageListSize: 0,
		initialized:false
	};

	// ----------------------------------------------------------------------------------
	// Private method for overriding defualt settings with those stored in cookies
	// ----------------------------------------------------------------------------------
	function getCookiePrefs() {
		toggleSidebar($.cookie('readerSidebar'));
	}

	// ----------------------------------------------------------------------------------
	// Public methods
	// ----------------------------------------------------------------------------------

	function toggleSidebar(state) {
		if (typeof(state) !== 'undefined') {
			$('#reader-container, #sidebar-toggle').removeClass('closed').addClass(state);
		} else {
			$('#reader-container, #sidebar-toggle').toggleClass('closed');
		}
		$('#sidebar-toggle').attr('title', ($('#sidebar-toggle').hasClass('closed') ? 'Show Navigation':'Hide Navigation'));
		$.cookie('readerSidebar', ($('#reader-container').hasClass('closed') ? 'closed':''), {path: '/', expires: 7});
		reader.setCanvasSize();
	}

	function chooseLayout(layout) {
		var prev_layout = reader.getLayout();
		reader.setLayout(layout);

		if (prev_layout != reader.getLayout()) {
			renderPageNavigation();
		}

		$('.layout-setter').removeClass('selected');
		$('#'+layout).addClass('selected');

		if (layout == 'twopage') {
			$('reader-container').addClass('view_two-page');
		}
	};
	function chooseSize(size) {
		reader.setSize(size);
	};
	function choosePage(pagenum) {
		$('#page-list ol li').removeClass('selected').children('#page-'+pagenum).parent().addClass('selected');
		reader.gotoPage(pagenum);
		renderArrowNavigation();
		$('#page-list ol').trigger('goto', [($('#page-list ol li.page.selected').prevAll().length-1)]);
	};

	/* Called whenever the browser viewport is resized; determines the new sidebar size.*/
	function setNavigationSize() {
		$('#reader-sidebar').height($(window).height()+'px');
		$('#page-list').height(($(window).height() - $('#page-list').offset().top - 33) + 'px'); //33 is for copyright at bottom
		G.pageListSize = ($('#help:visible').length) ? $('#page-list').height() + $('#help').height() + 15:$('#page-list').height(); // 15 for padding on #help
		setArrowSize();
	};

	function setArrowSize() {
		// Arrows to left and right of canvas
	    var canvasElm = $('#inner-canvas');
		$('#prev').css({
			top: canvasElm.css('top'),
			left: (parseInt(canvasElm.css('left'))-40)+'px',
			height: canvasElm.css('height')
		});

		$('#next').css({
			top: canvasElm.css('top'),
			left: (parseInt(canvasElm.css('left')) + parseInt(canvasElm.css('width')))+'px',
			height: canvasElm.css('height')
		});
	}

	function renderArrowNavigation() {
		// Arrows to left and right of canvas
		if (!$('#prev').length) {
			$('#canvas').prepend($('<a href="" id="prev" class="prev-link">Previous Page</a>').click(function() {
				if (reader.getPreviousPage() !== false) {
					choosePage(reader.getPreviousPage());
				} else {
					choosePage(-1);
				}
				return false;
			}));
		}
		if ((reader.opt.prev_issue == null || reader.opt.prev_issue == '') && reader.getPreviousPage() === false) {
			$('#prev').remove();
		}

		if (!$('#next').length) {
			$('#canvas').append($('<a href="" id="next" class="next-link">Next Page</a>').click(function() {
				if (reader.getNextPage() !== false) {
					choosePage(reader.getNextPage());
				} else {
					choosePage(pages.length);
				}
				return false;
			}));
		}
		if ((reader.opt.next_issue == null || reader.opt.next_issue == '') && reader.getNextPage() === false) {
			$('#next').remove();
		}

		setArrowSize();

		$('#inner-canvas img:visible:first').hover(
		function() {
			$('#prev').addClass('hover');
		},
		function() {
			$('#prev').removeClass('hover');
		});

		$('#inner-canvas img:visible:first').parent().children('img:visible:last').hover(
		function() {
			$('#next').addClass('hover');
		},
		function() {
			$('#next').removeClass('hover');
		});
	}

	function renderPageNavigation() {
		renderArrowNavigation();

		var navContainer = $('#page-list');
		navContainer.removeClass().addClass(reader.getLayout());
		var nav = $('#page-list ol').empty();

		// Prev issue
		if(reader.opt.prev_issue != null && reader.opt.prev_issue != '') {
			if(!$('#prev-issue').length) {
				navContainer.prepend($('<p class="prev-link"><a href="#" id="prev-issue">Previous Issue</a></p>').click(function(){
						choosePage(-1);
						return false;
				}));
			}
			nav.prepend('<li class="spacer"></li>');
		}

		// Issue pages
		for(var i = 0; i < G.pages.length; ++i) {
			if(i == G.pages.length) break;

			curpage = G.pages[i];
			curpage.name = (curpage.nav == 'Cover') ? curpage.nav:'#'+curpage.nav;

			var linkContent = "<img src=\"{0}\" alt=\"{1}\" /><span>{2}</span>".format(reader.getImageUrl(i, 0), curpage.name, curpage.name);

			if(reader.getLayout() == 'onepage') {
				var selected = (i == reader.getPage()) ? ' selected':'';
				nav.append($('<li class="page'+selected+'"></li>').append($('<a id="page-'+i+'" href="#p'+curpage.nav+'">'+linkContent+'</a>').click(function(){
					if (this.id.onlyPositiveNum() != reader.getPage()) {
						choosePage(this.id.onlyPositiveNum());
					}
					return false;
				})));
			}else if(reader.getLayout() == 'twopage') {
				prevpage = G.pages[i-1];
				nextpage = G.pages[i+1];
				if(i > 1 && curpage.handedness == 'R') continue;

				if (nextpage != null) {
					var linkContent2Pg = "<img src=\"{0}\" alt=\"{1}\" />".format(reader.getImageUrl(i, 0), curpage.name);
					linkContent2Pg += "<img src=\"{0}\" alt=\"{1}\" />".format(reader.getImageUrl(i+1, 0), nextpage.nav);
					linkContent2Pg += '<span>'+curpage.name+' - '+nextpage.nav+'</span>';
				}

				var selected = (i == reader.getPage() || (prevpage != null && G.pages[reader.getPage()].handedness == 'R' && prevpage.handedness == 'L')) ? ' selected':'';

				if(curpage.handedness == 'L' && nextpage != null) {
					nav.append($('<li class="page twopage'+selected+'"></li>').append($('<a id="page-'+i+'" href="#p'+curpage.nav+'">'+linkContent2Pg+'</a>').click(function(){
						if (this.id.onlyPositiveNum() != reader.getPage()) {
							choosePage(this.id.onlyPositiveNum());
						}
						return false;
					})));
					++i;
				}else {
					nav.append($('<li class="page'+selected+'"></li>').append($('<a id="page-'+i+'" href="#p'+curpage.nav+'">'+linkContent+'</a>').click(function(){
						if (this.id.onlyPositiveNum() != reader.getPage()) {
							choosePage(this.id.onlyPositiveNum());
						}
						return false;
					})));
				}
			}
		}

		// Next issue
		if(reader.opt.next_issue != null && reader.opt.next_issue != '') {
			if(!$('#next-issue').length) {
				navContainer.append($('<p class="next-link"><a href="#" id="next-issue">Next Issue</a></p>').click(function(){
						choosePage(pages.length);
						return false;
				}));
			}
			nav.append('<li class="spacer"></li>');
		}

		// Scroll to current page
		choosePage(reader.getPage());
	};

	//--------------------------------------------------------------
	// Specify methods that are public here
	//--------------------------------------------------------------
	this.toggleSidebar = toggleSidebar;
	this.chooseLayout = chooseLayout;
	this.chooseSize = chooseSize;
	this.choosePage = choosePage;
	this.setNavigationSize = setNavigationSize;
	this.renderPageNavigation = renderPageNavigation;

	//--------------------------------------------------------------
	// Start Initialization
	//--------------------------------------------------------------
	$('#inner-canvas').bind('canvasResize', function() {
		setNavigationSize();
	});

	$(document).bind('setSize', function() {
		$('.size-setter').removeClass('selected');
		$('#'+reader.getSize()).addClass('selected');
	});

	// Setup scrollTo
	$('#page-list ol').serialScroll({
		items:'li',
		axis:'y',
		duration:800,
		force:false,
		stop:true,
		lock:false,
		lazy: true,
		cycle:false,
		jump: true,
		constant: false
	});

	chooseLayout(reader.getLayout());
	chooseSize(reader.getSize());
	renderPageNavigation();
	setNavigationSize();
	getCookiePrefs();

	//bind size and layout
	$('.layout-setter').click(function(){
		chooseLayout(this.id);
		return false;
	});

	$('.size-setter').click(function(){
		chooseSize(this.id);
		return false;
	});

	$('#sizeplus').click(function() {
		reader.zoomIn();
		return false;
	});
	$('#sizeminus').click(function() {
		reader.zoomOut();
		return false;
	});

	//bind sidebar toggle
	$('#sidebar-toggle').click(function() {
		toggleSidebar();
		return false;
	});

	//bind info
	$('#header .issue-info').click(function() {
		$('#reader-index, #reader-index-overlay').toggleClass('open');
		return false;
	});

	$('#reader-index .close, #reader-index-overlay').click(function() {
		$('#reader-index, #reader-index-overlay').removeClass();
		return false;
	});

	//bind help
	$('#help-link').click(function() {
		if ($('#help:visible').length) {
			$('#page-list').animate({height: G.pageListSize+'px'}, 'normal');
		} else {
			$('#page-list').animate({height: (G.pageListSize - $('#help').height() - 15)+'px'}, 'normal'); // 15 for padding on #help
		}
		$('#help').slideToggle('normal');
		return false;
	});

	G.initialized=true;
	//--------------------------------------------------------------
	// End Initialization
	//--------------------------------------------------------------
};
//debug=console.debug;


