/* Compiling file: extension.js */
/**
 * Extension
 * 
 *  Extend some core JavaScript functionality. 
 * @author Jared Armstrong
 * @version 0.1
 *
 * @copyright Jared Armstrong
 * You are welcome to use this section of code, providing you reference this as the source.
 *
 */

/**
 * Return a function so that the constructer for the class is called as __construct in the objects method.
 */
Class = { 
	create: function() { 
		return function() { 
			this.__construct.apply(this, arguments);	
		}	
	}
}

/**
 * Return the function, but have its scope apply to a different object.
 * @usage myreturn = myFunction.bind(newObject, arg1, [...]);
 */
Function.prototype.bind = function(obj) {
	var __method = this;
	return function() { return __method.apply(obj, arguments); }
}

/**
 * Return Array|Object hay as a string, with elements seperated by String glue.
 */
Array.implode = function(glue, hay) {
	var ra = "";	
	for (var i in hay){if (typeof hay[i] != 'function' && typeof hay[i] != 'object') { if (ra != "") ra += glue; ra += hay[i]; } }
	
	return ra;		
}

/**
 * Push an element off the *front* of an array, and return the new array.
 */
Array.shift = function(arr) { 
	var ret = [];
	for (var i = 0; i < (arr.length-1); i++) {
		ret[i] = arr[i+1]	
	}
	return ret;
}

/**
 * Return whether or not search is in hay. Works for objects or arrays. 
 * If returnId is specified, return the object key of the first matching value.
 * 
 * @usage: 
 *  if (Array.inArray("this", ['nope', 'hah', 'this'])) //...
 * 
 */
Array.inArray = function(search, hay, returnId) {
	for (var i in hay)
		if (hay[i] == search)
			if (returnId == true) return i;
			else return true;
	return false;	
}

/**
 * Return a copy of the object.
 */
Object.copy = function (obj) { 
	var newObj = {}
	for (var i in obj) {
		newObj[i] = obj[i];	
	}
	return newObj;	
}

/**
 * Fix parseInt.
 */
__parseInt = parseInt;
parseInt = function(i) {
	// Must remove leading 0's
	try { if (typeof i.charAt == "function") while (i.charAt(0) == "0") i = i.substring(1); } catch(e) {}
	return __parseInt(i);	
}

/**
 * Extend the methods of an object with those passed. Create the object if it doesn't exist.
 */
Object.extend = function(original, extension) {
	if (typeof original == 'undefined') original = {};
	if (typeof extension != "object") return original;
	for (var m in extension) {
		original[m] = extension[m];
	}
	return original;
}

/**
 * Modify a class adding the methods of the parent if they have not been overloaded in the extension.
 */
Class.extend = function(parent, extension) {
	if (typeof extension.prototype == 'undefined') {
		var extensionclass = Class.create();
		extensionclass.prototype = extension;
		extension = extensionclass;	
	}
	extension.prototype.__parent = {};
	if (typeof parent.prototype != 'undefined') {
		for (var method in parent.prototype) {
			if (typeof extension.prototype[method] == 'undefined')
				extension.prototype[method] = parent.prototype[method];
			else 
				extension.prototype.__parent[method] = parent.prototype[method];		
		}	
	}
	return extension;
}


/**
 * Get a timestamp in milliseconds.
 */
function Timestamp() { return (new Date()).getTime(); }

/**
 * Try a bunch of functions, return the result of first one that works, or false if all fail.
 */ 
var Try = { 
	these: function() {
		var returnThis = false;
		for (var i = 0; i < arguments.length; i++) {			
			try { returnThis = arguments[i](); break; }
			catch (e) { }
		}		
		return returnThis;	
	}
}

/**
 * Inspect an object 
 */
var Inspect = function(obj, depth) {
	if (!depth) depth = 0;
	var resp = "{\n";
	for (var i in obj) {
		resp += i+": ";
		if (typeof obj[i] == 'object' && depth > 0)
			resp += Inspect(obj[i], depth-1);
		else
			resp += obj[i];
		resp += ", \n";	
	}
	return resp + "}";
}

Inspect.draw = function(obj, depth) {
	var html = Inspect(obj, depth);
	var link = document.createElement('input');
	link.type = "button";
	link.value = "Close";
	link.onclick = function() { document.body.removeChild(this.parentNode); }.bind(link);
	var nd = document.createElement('div');
	document.body.appendChild(nd);	
	nd.innerHTML = "<pre style='font-size: 11px; color:#000000; height: 600px; overflow: scroll'>"+html+"</pre>";
	
	nd.style.width  = "650px";
	nd.style.overflow = "scroll";
	nd.style.position = "absolute";
	nd.style.top = "15";
	nd.style.left = "15";
	nd.style.border = "3px solid gray";

	nd.style.backgroundColor = "#ffffff";
	
	nd.appendChild(link);
	
}

/**
 * Cookie handling.
 *
 * @usage:
 *  Cookie.set("mycookie", "myvalue", 3600); // Set a cookie for an hour
 *  Cookie.get("mycookie"); // Get the cookie if it exists. Returns null if it doesn't
 *  Cookie.unset("mycookie"); // Delete the cookie
 */
var Cookie = {
	set: function (name, value, seconds) {
		if (seconds) {
			var date = new Date();
			date.setTime(date.getTime()+(seconds*1000));
			var expires = "; expires="+date.toGMTString();
		}
		else var expires = "";
		document.cookie = name+"="+value+expires+"; path=/";		
	},
	get: function(name) { 
		var nameEQ = name + "=";
		var ca = document.cookie.split(';');
		for(var i = 0; i < ca.length; i++) {
			var c = ca[i];
			while (c.charAt(0)==' ') c = c.substring(1,c.length);
			if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
		}
		return null;		
	},
	unset: function(name) { 
		Cookie.set(name, "", -1);	
	}
}

/**
 * Return the number of elements in an object.
 */
Object.count = function(obj) {
	var n = 0;
	for (var i in obj)	n++;
	return n;
}


/**
 * Regular expression escaping
 */ 
RegExp.escape = function(text) {
  if (!arguments.callee.sRE) {
    arguments.callee.sRE = new RegExp('(\\' + ['/', '.', '$', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\'].join('|\\') + ')', 'g');
  }

  return text.replace(arguments.callee.sRE, '\\$1');
}

/**
 * Create a regexp, but return a precached one if one was already made.
 */
RegExp.C = function(exp, sets) {
	var expn = "?"+exp+"?"+sets;
	if (!arguments.callee.__CACHED_REGEXP) arguments.callee.__CACHED_REGEXP = {};
	if (typeof arguments.callee.__CACHED_REGEXP[expn] == 'undefined') {
		arguments.callee.__CACHED_REGEXP[expn] = new RegExp(exp, sets);	
	}	
	return arguments.callee.__CACHED_REGEXP[expn];
}


/**
 * Return the DOM element with the passed ID.
 */
function $(id) { return document.getElementById(id); }


/**
 * Return a random element of an array.
 */
Array.rand = function(arr) {
	try {
		return arr[Math.floor(Math.random()*arr.length)];
	} catch (e) { return null; }	
}

/**
 * Code past this point is copyright. You may not use any code past this point without written permission.
 */



/* Compiling file: ajax.class.js */
/**
 * Ajax Transport Class
 * 
 *  Core handling of XMLHttpRequest requests.
 * @author Jared Armstrong
 * @version 0.1
 * @requires extension.js
 *
 * Example Usage:
 * --------------
 * new Ajax.Request("/test.php", {method: "get"}, function(response) { alert(response); });
 * new Ajax.Request("/post.php", {method: "post", vars: {"test": "value", "page": "testpage"}}, function(response, mime) {
 *    if (mime == "text/javascript") {
 *       eval(response);
 *    }
 * });
 */
var Ajax = {
	Request: Class.create(),
	getTransport: function () {
		return Try.these( (function(){ return new ActiveXObject("Msxml2.XMLHTTP"); }), (function(){ return new ActiveXObject("Microsoft.XMLHTTP"); }), (function(){ return new XMLHttpRequest(); }) );
	},
	makePostBody: function(v) {
		var q = ""; for (var i in v) { if (q!="")q+="&"; q+=escape(i)+'='+escape(v[i]); } return q;
	},
	Timestamp: function() { var d = new Date(); return d.getTime(); }
}

Ajax.Request.prototype = {
	url: null,
	transport: null,
	__construct: function(url, opts, callback) {
		this.url = url;
		this.transport = Ajax.getTransport();	
		this.transport.onreadystatechange = this.handleStateChange.bind(this);
		this.callback = callback;
		
		if (opts.method == 'GET' || opts.method == 'get') {
			this.callback = v1;
			this.get();
			return;	
		}		
		
		if (opts.vars) var vars = opts.vars; 
		else if (opts.variables) var vars = opts.variables;
		else var vars = {};
		
		this.post(vars);
	},
	post: function(vars) { 
		var postBody;
		if (typeof vars == 'object') {
			postBody = Ajax.makePostBody(vars);	
		}
		
		this.transport.open("POST", this.url, true);
		this.sendHeaders();
		this.transport.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
		this.transport.send(postBody);			
		

	}	,
	get: function() { 		
		this.transport.open("GET", this.url, true);
		this.sendHeaders();
		this.transport.send(null);		
	},
	sendHeaders: function() { 
		this.transport.setRequestHeader('X-JSRequestedAt', Ajax.Timestamp());
		this.transport.setRequestHeader("X-RequestFrom", "javascript/ajax-call");					
	}, 
	handleStateChange: function() { 
		if (this.transport.readyState == 4) {
			// Prevent IE memory leak
			this.transport.onreadystatechange = function() { }
			if (this.transport.status == 200) {
				if (typeof this.callback == 'function') this.callback(this.transport.responseText, this.transport.getResponseHeader('content-type'));	
			}	
			delete(this.transport);
		}	
	}
}




/* Compiling file: DynamicDisplay.class.js */
/**
 * Useful classes for providing templatable listed data display.
 */
var DynamicDisplay = {};
DynamicDisplay.Abstract = Class.create();
DynamicDisplay.Abstract.prototype = {
	conf: null,
	data: null,	
	

	__construct: function(conf) {
		this.conf = conf;
		
		if (typeof this.conf['style'] != 'undefined') {
			if (typeof this.conf['style'].vars == 'undefined') {
				//this.conf['style'].vars = this.style.vars;
			}
			this.style = this.conf['style'];
		}
	},
	
	populate: function(data) {
		this.data = data;	
	},
	
	applyTo: function(dom) {
		dom.innerHTML = this.create();	
	},

	replaceVariables: function(vars, into, i) {
		into = this.replaceSpecials(into, i);
		for (var c in vars) {				
			into = this.replaceVar(c, eval(vars[c]), into);
		}
		return into;
	},
	
	replaceSpecials: function(into, i) {
		var matches = into.match(RegExp.C("\\{\\$\\.([a-zA-Z]+(\\(\\))?)\\}", "g"));
		var replaced = {};
		for (var x in matches) {
			
			if (!(RegExp.C("^\\{\\$\\.([a-zA-Z]+(\\(\\))?)\\}$")).test(matches[x])) {
				continue;
			}
			var tr = matches[x].substring(3).substring(0, matches[x].length-4);	
			
			if (typeof replaced[tr] == 'undefined') {
				replaced[tr] = true;
				try {
				into = into.replace(RegExp.C(RegExp.escape(matches[x]), "g"), eval("this.data[i]."+tr));	
				} catch(e) {}
			}
		}
		return into;	
	},
	
	replaceVar: function(c, rwith, rin) {
		return rin.replace(new RegExp("\\{"+c+"\\}", "g"), rwith);	
	}
}

/**
 * Creates text data from lists in the form of objects.
 */
DynamicDisplay.Text = Class.extend(DynamicDisplay.Abstract, {
	style: {vars: {"%i": "i", "%t": "this.data[i].toString()"}, template: " &bull; {%i}: {%t} <br />"},
	create: function() {
		var nhtml = "";		
		for (var i in this.data) {
			nhtml += this.replaceVariables(this.style.vars, this.style.template, i);
		}
		return nhtml;	
	}	
});

/**
 * Creates a SELECT dropdown from a list of objects.
 */
DynamicDisplay.Dropdown = Class.extend(DynamicDisplay.Abstract, {
	style: {vars: {"%v": "i", "%t": "this.data[i].toString()"}, template: "{%t}"},
	disabled: false,
	selected: null,
	select: function(val) {
		this.selected = val;	
	},
	
	create: function(asHtml) {
		var nhtml = "<select "+(this.style['onchange'] != "" ? "onchange=\""+this.style['onchange']+"\"":"")
				+ (this.disabled==true ? " disabled='disabled'" : "")
			  + ">";
			 
		if  (this.style['header']) nhtml += "<option value=''>"+this.style['header']+"</option>";		
		for (var i in this.data) {
			nhtml += "<option value='"+eval(this.style.vars["%v"])+"'"+(this.selected!=null&&this.selected==eval(this.style.vars["%v"])?" selected='selected'":"")+">"+this.replaceVariables(this.style.vars, this.style.template, i)+"</option>";
		}
		return nhtml + "</select>";		
	}
});





/* Compiling file: MangaViewer.class.js */
/**
 * You may not use this code without permission.
 */

var MangaViewer = Class.create();
MangaViewer.prototype = {
	/**
	 * @vars
	 */
	Events: null,
	pageImage: null,
	mangaList: null,
	
	selectedManga: null,
	
	args: null,	
	conf: null,
	mangaRequestResponse: null,
	
	hasSelectedManga: false,
	hasSelectedChapter: false,
	hasSelectedPage: false,
	
	/**
	 * Create a new MangaViewer.
	 *
	 * @param {Object} args A keyed object with DOM object paramaters. 
	 */
	__construct: function(args) {
		//this.Events = new MVEvents(this);
		
		this.pageImage = new MVPageImage(args);				
		this.pageImage.setObject(args['pageImageObject']);	
				
		this.mangaList = new MVMangaList(args['conf']);		
		this.conf = args;
		
		this.Events = new MVEventHandler(); // 
		
		this.callEvent("loading", true, true);		
	},
	
	callEvent: function(event, ignoreTree, ignoreSave) {
		var eventData, eventName;
		if (typeof event == "object") {
			eventData = event[1];
			eventName = event[0];
		} else {
			eventData = {};
			eventName = event;	
		}
		
		this.Events.callEvent(eventName, eventData, this, this.conf.events, true, true);
	},
	
	loadManga: function(src, evars) {
	
		new Ajax.Request(src, {method: "post", vars: Object.extend({"cmd": "getList"}, evars||{})}, function(response, mime) {
		if (mime == "text/json" || true) {
			this.mangaRequestResponse = eval("("+response+")");
			this.mangaList.populate(this.mangaRequestResponse);
			this.loadMangaDone();
		} else { this.callEvent("onloaderror", true, true); }}.bind(this));	
	},
	
	loadMangaDone: function() {
		//Inspect.draw(this.mangaRequestResponse, 3);
		this.callEvent("onload", true, true);		
		
		this.updateDisplayObjects("mangas", this.mangaList.mangas);	
		this.updateDisplayObjects("chapter", false);	
		this.updateDisplayObjects("pages", false);		
		this.callEvent("showmainindex", true, true);	
	},
	
	selectManga: function(name) {
		if (name == "") {
			if (this.hasSelectedPage) this.callEvent("pageunselected", true, true);	
			if (this.hasSelectedChapter) this.callEvent("chapterunselected", true, true);	
			if (this.hasSelectedManga) this.callEvent("mangaunselected", true, true);
			this.hasSelectedManga = false; this.hasSelectedChapter = false; this.hasSelectedPage = false;
			this.updateDisplayObjects("chapter", false);	
			this.updateDisplayObjects("pages", false);	
			this.updateDisplayObjects("mangas", this.mangaList.mangas);	// Reselect the same manga
			this.callEvent("showmainindex", true, true);
			return;
		}
				
		if (this.mangaList.select(name)) {
			/* Call Events */
			if (this.hasSelectedPage) this.callEvent("pageunselected", true, true);	
			if (this.hasSelectedChapter) this.callEvent("chapterunselected", true, true);	
			if (this.hasSelectedManga)	this.callEvent("mangaunselected", true, true);
			this.callEvent("mangaselected");
			
			this.hasSelectedManga = true; this.hasSelectedChapter = false; this.hasSelectedPage = false;
			///////
			// update dispalys to reflect selected.
			this.updateDisplayObjects("mangas", this.mangaList.mangas, name);		
			

			this.selectedManga = new MVSelectedManga(this.mangaList.get(name), this.conf['conf']);		
			this.updateDisplayObjects("chapter", this.selectedManga.getChapterList());
			this.updateDisplayObjects("pages", false);
			
			// Another event 
			this.callEvent("showchapterindex");
		}
	},
	
	selectChapter: function(chapter) {
		if (chapter == "") {
			// Unselecting a chapter
			// Display the chapter index
			this.selectedManga.selectChapter(null);
			if (this.hasSelectedPage) this.callEvent("pageunselected", true, true);	
			if (this.hasSelectedChapter) this.callEvent("chapterunselected", true, true);	
			this.hasSelectedChapter = false; this.hasSelectedPage = false;
			this.updateDisplayObjects("pages", false); // Unselect all pages 
			this.updateDisplayObjects("chapter", this.selectedManga.getChapterList()); // Reselect the right chapters
			this.callEvent("showchapterindex");
			return;	
		}

		chapter = MangaViewer.chapterPad(chapter); 
		
		if (this.selectedManga.selectChapter(chapter)) {
			/* Call events */
			if (this.hasSelectedPage) this.callEvent("pageunselected", true, true);	
			if (this.hasSelectedChapter) this.callEvent("chapterunselected", true, true);					
			this.callEvent("chapterselected");
			this.hasSelectedChapter = true; this.hasSelectedPage = false;
			//////

			
			// Change all Displays to reflect this selection
			this.updateDisplayObjects("chapter", this.selectedManga.getChapterList(), chapter);
			this.selectPage(1);
		}
	},
	
	selectPage: function(page) {
		if (page == "") {
			// Unselecting a page.
			// Display the first page, for now.
			this.selectedManga.selectedChapter.selectPage(0);
			this.selectPage(1);
			return;
		}
		if (this.selectedManga.selectedChapter.selectPage(page)) {
			/* Call events */
			if (this.hasSelectedPage) this.callEvent("pageunselected", true, true);					
			this.callEvent("pageselected", true, true); // This is bugged atm, the list is teh fail!
			this.hasSelectedPage = page;
			//////
			this.updateDisplayObjects("pages", this.selectedManga.selectedChapter.getPageList(), page);	

			this.displayPage(page);			
		}	
	},
	
	displayPage: function(page) {
		this.callEvent("pageimageloading", true, true);	
		this.pageImage.callback = this.handlePageImageEvent.bind(this);	
		this.pageImage.setSrc(this.createThisImgSrc(page));	
	},
	
	showNextPage: function() {
		if(this.selectedManga.selectedChapter.hasNextPage()) {
			this.selectPage(this.selectedManga.selectedChapter.nextPage());	
		}	
	},
	showPrevPage: function() {
		if(this.selectedManga.selectedChapter.hasPrevPage()) {
			this.selectPage(this.selectedManga.selectedChapter.prevPage());	
		}	
	},
	
	handlePageImageEvent: function(event) {
		if (!this.hasSelectedPage) return; // User might have unselected page display.
		switch(event) {
			case "load":
				this.callEvent("pageimageonload", true, true);
				break;	
			case "error":
				this.callEvent("pageimageonerror", true, true);
				break;
			case "abort":
				this.callEvent("pageimageonabort", true, true);
				break;
		}
	},
	
	updateDisplayObjects: function(otype, dataobj, selected) {
		var dos;
		if (typeof this.conf[otype+"DisplayObjects"] != 'undefined') {
			dos = this.conf[otype+"DisplayObjects"];	
		} else if (typeof this.conf[otype+"DisplayObject"] != 'undefined') {
			dos = [this.conf[otype+"DisplayObject"]];	
		} else {
			return;	
		}
		
		for (var i = 0; i < dos.length; i++) {
			var cdo = dos[i];
			if (dataobj == false && cdo[1]['noData'] == "hide") {
				cdo[0].style.visibility = "hidden"; continue;	
			}
			
			switch(cdo[1]['displayType']) {
				case "text":
					var ddo = new DynamicDisplay.Text(cdo[1]);
					ddo.populate(dataobj);
					ddo.applyTo(cdo[0]);
					break;
				case "dropdown":
					var ddo = new DynamicDisplay.Dropdown(cdo[1]);
					if (dataobj == false && cdo[1]['noData'] == "disable") {
						ddo.disabled = true;
					}
					if (typeof selected != 'undefined') ddo.select(selected);
					ddo.populate(dataobj || {});
					ddo.applyTo(cdo[0]);
					break;	
				// Functions are called as:
				//
				//  function(domElement, dataToPopulateWith, mainObject, displayObject); 
				//			> displayObject is the base of the object, with *all* data.
				//
				case "function":
					// Call a function with access to this object;
					if (typeof cdo[1]["function"] == "function")
						cdo[1]["function"](cdo[0], dataobj||{}, this, cdo);
					break;
			}
		}	
	},
	
	createThisImgSrc: function(page) {		
		var src = this.createImgSrc(page, this.selectedManga.selectedChapter.chapter, this.selectedManga.manga.name, this.selectedManga.selectedChapter.imageFormat);
		return src;
	},
	createImgSrc: function(page, chapter, manga, format) {
		return this.conf['imageUrl']+"/"+manga+"/"+MangaViewer.chapterPad(chapter)+"/"+MangaViewer.pagePad(page)+"."+(format?format:"png");
	}
}

// Static functions
MangaViewer.createImgSrc = function(manga, chapter, page, format) {
	return "";
	return "http://ftp1.download.narutofan.com/bt/test/"+manga+"/"+MangaViewer.chapterPad(chapter)+"/"+MangaViewer.pagePad(page)+"."+(format?format:"png");
}
MangaViewer.pagePad = function(n) {
	n = parseInt(n);
	if (n < 10)
		return "0"+n;
	return ""+n+"";		
}
MangaViewer.chapterPad = function(n) {
	n = parseInt(n);
	if (n < 10)
		return "00"+n;
	else if (n < 100) 
		return "0"+n;
	return ""+n+"";		
}

/**
 * Function to display a chapter listing with nice image thumbnails.
 */
MangaViewer.TplChapterThumbnails = function(domElement, data, mainObject, displayObject) {
	if (!mainObject.hasSelectedManga) return;
	var count = 0;
	var html = "<table cellpadding='1' cellspacing='1' width='99%'>";
	for (var i in data) {
		//Inspect.draw({"t": mainObject.createImgSrc(1, i, mainObject.selectedManga.manga.name, 'thumb.jpg')});
		if (count % 4 == 0) html += "<tr>";
		html += "<td align='center'><a href='#' onclick='"+mainObject.conf['ObjectName']+".selectChapter(\""+i+"\"); return false;'><img src='"+mainObject.createImgSrc(1, i, mainObject.selectedManga.manga.name, 'thumb.jpg')+"' onerror='MangaViewer.TplChapterThumbnailsErrorSrc(this);' /> <br />Chapter "+i+"</a></td>";
		if (count % 4 == 3) { html += "</tr>"; }
		count++;
	}
	if (count == 0) {
		html += "<tr><td>There are no chapters available for this manga.</td></tr></table>";	
	}
	domElement.innerHTML = html;	
}

MangaViewer.TplChapterThumbnailsErrorSrc = function(ob) {
	if(ob.src!="images/ch_thumb_na.gif"){ ob.src="images/ch_thumb_na.gif"; ob.onerror=function() {};}
}





/* Compiling file: MVChapter.class.js */
/**
 * You may not use this code without permission.
 */

var MVChapter = Class.create();
MVChapter.prototype = {
	/**
	 * @vars
	 */
	chapter: null,
	manga: null,
	currentPage: 0,
	pages: 0,
	imageFormat: "jpg",
	
	__construct: function(chapter, manga) {
		this.chapter = chapter;
		this.manga = manga;
	},
	
	setData: function(data) {
		this.setPages(data.pages);
		this.imageFormat = data.format;
	},
	
	setPages: function(pages) {
		this.pages = pages;	
	},
	
	hasNextPage: function(currPage) {
		if (typeof currPage == 'undefined')
			currPage = this.currentPage;
		return (this.pages > this.currentPage);
	},
	hasPrevPage: function(currPage) {
		if (typeof currPage == 'undefined')
			currPage = this.currentPage;
		return (currPage > 1);	
	},
	prevPage: function() {
		if(!this.hasPrevPage()) return 1;
		return this.currentPage-1;
	},
	nextPage: function() {
		if(!this.hasNextPage()) return this.currentPage;
		return this.currentPage+1;
	},
	
	selectPage: function(page) {
		page = parseInt(page);
		if (this.currentPage == page || this.pages < page) return false;
		this.currentPage = page;
		return true;
	},
	
	getPageList: function() {
		var obj = {};
		for (var i = 0; i < this.pages; i++) {
			obj[this.numberPad(i+1)] = this.numberPad(i+1);		
		}
		return obj;
	},
	
	numberPad: function(n) {
		if (n < 10)
			return "0"+n;
		return ""+n+"";	
	},
		
	preload: function() {
		// preload the next two pages
		if (this.hasNextPage(this.currentPage))
			(new MVImage(MangaViewer.createImgSrc(this.manga.name, this.chapter, this.currentPage+1))).preload();
		if (this.hasNextPage(this.currentPage+1))
			(new MVImage(MangaViewer.createImgSrc(this.manga.name, this.chapter, this.currentPage+2))).preload();
	}	
}



/* Compiling file: MVEventHandler.class.js */
/**
 * You may not use this code without permission.
 */

// Events format
//  "eventName": { showid: ["idofanelement", ...], /*will set display visibility to on getElementById().*/
//                 show: [DOMElement, ...] // Will set display to visible as above, but without gEBI()
//				   ccid: {"idofelement": "New innerhtml content", "id": function(){}, ...},
//                 cc: [[domElement, "new inner html content"], ... ] // 'Change Content' = 'cc'
//				  hideid: [...], hide: [...],
//				  callfunction: function() { }
//             },
//
//   Events called in a treelike structure
//
//     mangaSelected -> chapterSelected -> pageSelected -> pageUnselected ->chapterUnselected -> ...
//
var MVEventHandler = Class.create();
MVEventHandler.prototype = {
	__construct: function() { },
	// The defaults, changable.
	EventStructure: [["mangaselected", "chapterselected", "pageselected"],
					 ["pageunselected", "chapterunselected", "mangaunselected"]],
	EventStructureLastCall: [null, null],
	
	callEvent: function(eventName, eventData, callerObject, eventInformation, ignoreTree, ignoreSave) {
		eventName = eventName.toLowerCase();
		if (ignoreTree) {
			this._callEventData(eventName, eventInformation, eventData, callerObject);
			return;	
		}		
		
		// This is a little broken :S
		// Works but, need to reset the tree of [0] & [1] with resepctive events from [1] & [0].
		for (var i in this.EventStructure) {			
			var EventTree = this.EventStructure[i];
			var x = Array.inArray(eventName, EventTree, true);

			if (x !== false) {
				x = parseInt(x);		
				var q = 0;
				if (this.EventStructureLastCall[i] != null && !ignoreSave) {
					q = this.EventStructureLastCall[i]+1;
				}
				q = parseInt(q);
				for (var t = q; t <= x; t++) 
					this._callEventData(EventTree[t], eventInformation, eventData, callerObject);
				this.EventStructureLastCall[i] = x;
			}	
		}
	},
	
	_callEventData: function(eventName, eiobj, eventData, callerObject) {
		// If there is no handler data for this event, return.
		if (typeof eiobj[eventName] != "object") {
			return;
		}
		
		// Get handler data for the event.
		var ei = eiobj[eventName];
		
		// Hide/Show Dom elements
		for (var elk in ei.hide) {
			var el = ei.hide[elk];
			try { el.style.display = "none"; el.style.visibility = "hidden"; } catch(e) {}
		}
		for (var elik in ei.hideid) {
			var eli = ei.hideid[elik];
			try { var el = document.getElementById(eli); el.style.display = "none"; el.style.visibility = "hidden"; }	catch(e) {}	
		}
		for (var elk in ei.show) {
			var el = ei.show[elk];
			try { el.style.display = ""; el.style.visibility = "visible"; }	catch(e) {}
		}
		for (var elik in ei.showid) {
			var eli = ei.showid[elik];
			try { var el = document.getElementById(eli); el.style.display = ""; el.style.visibility = "visible"; }	catch(e) {}	
		}
		
		// Execute "change content" handlers (Static text, or function return data)
		for (var ccei in ei.cc) {
			var cce = ei.cc[ccei];
			if (typeof cce[1] == "function") {
				cce[0].innerHTML = cce[1]();	
			} else {
				cce[0].innerHTML = cce[1];	
			}
		}
		for (var ccei in ei.ccid) {
			var el = document.getElementById(ei.ccid[ccei][0]) ;
			if (typeof  ei.ccid[ccei][1] == "function") {
				el.innerHTML =  ei.ccid[ccei][1](eventData, callerObject);	
			} else {
				el.innerHTML =  ei.ccid[ccei][1];	
			}
		}
		
		// Run the call function, if there exists one.
		if (typeof ei.callfunction == "function")
			ei.callfunction();
	}
	
}




/* Compiling file: MVImage.class.js */
/**
 * You may not use this code without permission.
 */

var MVImage = Class.create();
MVImage.prototype = {
	/**
	 * @vars
	 */
	src: "",
	imageObj: null,
	imageObjStatus: 0,
	callbackFunction: null,
	
	/**
	 * Create a new MVImage.
	 *
	 * @param {Object} args A keyed object with DOM object paramaters. 
	 */
	__construct: function(src) {
		this.src = src;
	},	
	
	preload: function(callback) {
		if (this.imageObj != null) {
			return;
		}
		
		if (typeof callback == 'function')
			this.callbackFunction = callback;
			
		this.imageObj = new Image();
		this.imageObj.onerror = this.onError.bind(this);
		this.imageObj.onabort = this.onAbort.bind(this);
		this.imageObj.onload = this.onLoad.bind(this);
		this.imageObj.src = this.src;	
	},
	
	hasPreloaded: function() {
		return (this.imageObj != null);
	},
	
	hasLoaded: function() {
		return (this.imageObjStatus == 1);	
	},
	
	onError: function() {
		this.imageObjStatus = 0;
		if (this.callbackFunction != null) {
			this.callbackFunction("error", this.src, this.imageObj);
		}
	},
	
	onAbort: function() {
		this.imageObjStatus = -1;
		if (this.callbackFunction != null) {
			this.callbackFunction("abort", this.src, this.imageObj);
		}
	},
	
	onLoad: function() {
		this.imageObjStatus = 1;
		if (this.callbackFunction != null) {
			this.callbackFunction("load", this.src, this.imageObj);
		}
	}
	
}



/* Compiling file: MVMangaList.class.js */
/**
 * You may not use this code without permission.
 */

var MVMangaList = Class.create();
MVMangaList.prototype = {
	/**
	 * @vars
	 */
	mangas: [],
	displayObjects: [],
	conf: null,
	
	/**
	 * Create a new list.
	 *
	 * @param {Object} args A keyed object with DOM object paramaters. 
	 */
	__construct: function(conf) {
		this.conf = conf;
	},
	
	setDisplayObject: function(obj, conf) {
		this.displayObjects[this.displayObjects.length] = [obj, conf];	
	},
	
	populate: function(list) {
		for (var name in list) {
			this.mangas[this.mangas.length] = new MVManga(name, list[name]['chapters'], list[name]['chapterData']);	
		}	
		this.updateDisplayObjects();	
	},
	
	get: function(name) {
		for (var i = 0; i < this.mangas.length; i++) {
			if (this.mangas[i].name == name)
				return this.mangas[i];	
		}		
		return null;
	},
	
	select: function(name) {
		// Todo: Make the list hide/whatever
		return true;
	},
	
	unselect: function() {
		// Todo: reshow the list/whatever
	},
	
	updateDisplayObjects: function() {
		for (var i = 0; i < this.displayObjects.length; i++) {
			var cdo = this.displayObjects[i];
			if (cdo[1]['displayType'] == "text") {
				//this.updateDisplayObjectsText(cdo);	
				var d = new DynamicDisplay.Text(cdo[1]);
				d.populate(this.mangas);
				d.applyTo(cdo[0]);
			}	
		}	
	},
	
	updateDisplayObjectsText: function(cdo) {
		var nhtml = "";
		for (var i = 0; i < this.mangas.length; i++) {
			var l = "<a href='#select' onclick='"+this.conf['ObjectName']+".selectManga(\""+this.mangas[i].name+"\"); return false;'>";
			nhtml += " - " + l + this.mangas[i].name + "</a> ("+this.mangas[i].chapters+" chapters) <br />";
		}
		cdo[0].innerHTML = nhtml	
	}
	
};

var MVManga = Class.create();
MVManga.prototype = {
	chapters: 0,
	chapterObjs: {},
	name: "Unnamed",
	chapterData: null,
	__construct: function(name, chapters, chapterData) {
		this.name = name;
		this.chapters = chapters;
		this.chapterData = chapterData;
	}
};

var MVMangaChapter = Class.create();
MVMangaChapter.prototype = {
	manga: null,
	pages: 0,
	
	__construct: function(manga, pages) {
		this.manga = manga;
		this.pages = pages;
	},
	
	hasNextPage: function(currentPage) {
		return (currentPage < this.pages);
	},
	
	getManga: function() {
		return this.manga;	
	}

	
}



/* Compiling file: MVPageImage.class.js */
/**
 * You may not use this code without permission.
 */

var MVPageImage = Class.create();
MVPageImage.prototype = {
	/**
	 * @vars
	 */
	dom: null,
	callback: null,
	maxHeight: 1000,
	maxWidth: 700,
	
	/**
	 * Create a new MangaViewer.
	 *
	 * @param {Object} args A keyed object with DOM object paramaters. 
	 */
	__construct: function(conf) {
		if (conf['imageMaxHeight']) {			
			this.maxHeight = conf['imageMaxHeight'];
		}
		if (conf['imageMaxWidth']) { 
			this.maxWidth = conf['imageMaxWidth'];
		}
	},
	
	setObject: function(obj) {
		if (typeof obj != 'object') {
			alert("Image object is not valid: MVPageImage.setObject"); 
			return false;
		} 
		
		this.dom = obj;
		
		if (typeof this.dom.naturalWidth == 'undefined') {
			this.dom.requiresPreload = true;	
		} else {
			this.dom.requiresPreload = false;	
		}
		this.dom.onerror = this.onError.bind(this);
		this.dom.onabort = this.onAbort.bind(this);
		this.dom.onload = this.onLoad.bind(this);
	},
	
	applyImage: function(im) {
		this.dom.src = im.src;
	},
	
	setCallback: function (callback) {
		this.callback = callback;
	},
	
	setSrc: function(src) {
		// As we are changing the width/height of the image,
		// if ie: make invisible Image to get nat/width height
		// and set callback to set this.dom.src = that and this.dom.naturalWidth/this.dom.naturalHeight
		// to Image().width/height respectively.
		if (this.dom.requiresPreload || typeof this.dom.naturalWidth == 'undefined') {
			(new MVImage(src)).preload(function(status, src, imobj) {
				this.dom.naturalWidth = imobj.width;
				this.dom.naturalHeight = imobj.height;
				this.dom.src = src;
			}.bind(this));
		} else {
			// It is fine just to set the SRC as this.
			this.dom.src = src;	
		}
	},
	
	onError: function() {
		this.imageObjStatus = 0;
		if (this.callback != null) {
			this.callback("error");
		}
	},
	
	onAbort: function() {
		this.imageObjStatus = -1;
		if (this.callback != null) {
			this.callback("abort");
		}
	},
	
	onLoad: function() {
		this.imageObjStatus = 1;		
		
		// IE will not like it if the image is not displayed, so call callback first.
		if (this.callback != null) {
			this.callback("load");
		}

		if (typeof this.dom.naturalWidth != "undefined") {
			
			// Let's assume, for simplicitie's state that its either a really
			// long image, or a really wide image
			
			if (this.dom.naturalHeight > this.maxHeight && this.dom.naturalWidth > this.maxWidth) {
				if (this.dom.naturalHeight / this.maxHeight > this.dom.naturalWidth / this.maxWidth) {			
					this.dom.height = this.maxHeight;
					this.dom.width = Math.floor(this.dom.naturalWidth * this.maxHeight / this.dom.naturalHeight);
				} else {
					this.dom.width = this.maxWidth;	
					this.dom.height = Math.floor(this.dom.naturalHeight * this.maxWidth / this.dom.naturalWidth);
				}
			} else if (this.dom.naturalHeight > this.maxHeight) {
				this.dom.height = this.maxHeight;
				this.dom.width = Math.floor(this.dom.naturalWidth * this.maxHeight / this.dom.naturalHeight);
			} else if (this.dom.naturalWidth > this.maxWidth) {
				this.dom.width = this.maxWidth;	
				this.dom.height = Math.floor(this.dom.naturalHeight * this.maxWidth / this.dom.naturalWidth);
			}
		}
	}
	
}



/* Compiling file: MVSelectedManga.class.js */
/**
 * You may not use this code without permission.
 */

var MVSelectedManga = Class.create();
MVSelectedManga.prototype = {
	/**
	 * @vars
	 */
	conf: null,
	manga: null,
	
	displayObjects: null,
	
	selectedChapter: null,
	

	__construct: function(manga, conf) {
		this.manga = manga;
		this.conf = conf;
	},
	
	getChapterList: function() {
		var obj = {};
		for (var i = 0; i < this.manga.chapters; i++) {
			obj[this.numberPad(i+1)] = this.numberPad(i+1);
		}
		return obj;	
	},
	
	selectChapter: function(chapter) {
		if (this.selectedChapter != null) if (this.selectedChapter.chapter == chapter) return false;
		this.selectedChapter = new MVChapter(chapter, this.manga);		
		try { 
			this.selectedChapter.setData(this.manga.chapterData[this.numberPad(chapter)]);  
		} catch (e) {}
		
		return true;
	},
	
	numberPad: function(n) {
		n = parseInt(n);
		if (n < 10)
			return "00"+n;
		else if (n < 100)
			return "0"+n;
		return ""+n+"";	
	}
	
}



