/*-----------------------------------------------------------------*
 | TRANSFORM                                                       |
 *-----------------------------------------------------------------*/

var Transform = new function() {
	/*
	rotate(obj, angle)
	scale(obj, sx, sy)
	rotateAndScale(obj, angle, sx, sy)
	opacity(obj, alpha)
	gradient(obj, start, end, horizontal)
	*/
	
	var _this;
	var _transformproperty;
	var _canvas;
	
	{
		_this = this;
		var testobj = $("<div></div>");
		var test = testobj.get(0);
		_transformproperty = findProperty(test, ["MsFilter", "filter", "transform", "OTransform", "KhtmlTransform", "WebkitTransform", "MozTransform"]);
		if(_transformproperty == "filter" || _transformproperty == "MsFilter") {
			_this.rotate = ieRotate;
			_this.scale = ieScale;
			_this.rotateAndScale = ieRotateAndScale;
			_this.opacity = ieOpacity;
			_this.gradient = ieGradient;
		}
		else {
			if(_transformproperty) {
				_this.rotate = stdRotate;
				_this.scale = stdScale;
				_this.rotateAndScale = stdRotateAndScale;
			}
			_this.opacity = stdOpacity;
			stdGradient(testobj, "000000", "ffffff", true);
			if(test.style.backgroundImage != "") {
				_this.gradient = stdGradient;
			}
			else {
				mozGradient(testobj, "000000", "ffffff", true);
				if(test.style.backgroundImage != "") {
					_this.gradient = mozGradient;
				}
				else {
					_canvas = $("<canvas></canvas>").get(0);
					if(_canvas.getContext && _canvas.toDataURL) {
						_this.gradient = canvasGradient;
					}
					else {
						_canvas = null;
					}
				}
			}
		}
	}
	
	function findProperty(test, properties) {
		var i = properties.length;
		var result;
		while(i--) {
			result = properties[i];
			if(test.style[result] != null) break;
			else result = null;
		}
		return result;
	}
	
	function stdRotate(obj, angle) {
		var value = "";
		if(angle != 0) value = "rotate("+angle+"rad)";
		obj.css(_transformproperty, value);
	}
	
	function stdScale(obj, scalex, scaley) {
		var value = "";
		if(scalex != 1 || scaley != 1) value = "scale("+scalex+", "+scaley+")";
		obj.css(_transformproperty, value);
	}
	
	function stdRotateAndScale(obj, angle, scalex, scaley) {
		var value = "";
		if(scalex != 1 || scaley != 1) value = "scale("+scalex+", "+scaley+")";
		if(angle != 0) value += " rotate("+angle+"rad)";
		obj.css(_transformproperty, value);
	}
	
	function stdOpacity(obj, alpha) {
		if(alpha >= 1) alpha = "";
		obj.css("opacity", alpha);
	}
	
	function stdGradient(obj, start, end, horizontal) {
		var direction;
		if(horizontal) direction = "left top, right top";
		else direction = "left top, left bottom";
		obj.css("backgroundImage", "-webkit-gradient(linear, "+direction+", color-stop(0, "+rgbColor(start)+"), color-stop(1, "+rgbColor(end)+"))");
	}
	
	function ieRotate(obj, angle) {
		var cosangle = Math.cos(angle);
		var sinangle = Math.sin(angle);
		ieApplyMatrix(obj, cosangle, sinangle, -sinangle, cosangle);
	}
	
	function ieScale(obj, scalex, scaley) {
		ieApplyMatrix(obj, scalex, 0, 0, scaley);
	}
	
	function ieRotateAndScale(obj, angle, scalex, scaley) {
		var cosangle = Math.cos(angle);
		var sinangle = Math.sin(angle);
		ieApplyMatrix(obj, scalex*cosangle, scaley*sinangle, -scalex*sinangle, scaley*cosangle);
	}
	
	function ieApplyMatrix(obj, a, b, c, d) {
		var element = obj.get(0);
		if(!element.transformorigin) {
			element.transformorigin = {
				left: parseFloat(obj.css("marginLeft")) || 0,
				top: parseFloat(obj.css("marginTop")) || 0,
				width: obj.width(),
				height: obj.height()
			}
		}
		var filter;
		try {
			filter = element.filters.item("DXImageTransform.Microsoft.Matrix");
			filter.M11 = a;
			filter.M21 = b;
			filter.M12 = c;
			filter.M22 = d;
		} 
		catch(error) {
			filter = obj.css(_transformproperty) || "";
			if(filter.length > 0) filter = " "+filter;
			filter = "progid:DXImageTransform.Microsoft.Matrix(M11="+a+", M21="+b+", M12="+c+", M22="+d+", SizingMethod='auto expand')"+filter;
			obj.css(_transformproperty, filter);
		}
		filter.enabled = (a != 1 || b != 0 || c != 0 || d != 1);
		var offsetx = (element.transformorigin.width-obj.width())/2;
		var offsety = (element.transformorigin.height-obj.height())/2;
		obj.css({
			marginLeft: (element.transformorigin.left+offsetx)+"px",
			marginTop: (element.transformorigin.top+offsety)+"px"
		});
	}
	
	function ieOpacity(obj, alpha) {
		var element = obj.get(0);
		alpha = Math.floor(alpha*100);
		var filter;
		try {
			filter = element.filters.item("DXImageTransform.Microsoft.Alpha");
			filter.opacity = alpha;
		} 
		catch(error) {
			filter = obj.css(_transformproperty);
			if(filter.length > 0) filter += " ";
			filter += "progid:DXImageTransform.Microsoft.Alpha(opacity="+alpha+")";
			obj.css(_transformproperty, filter);
		}
		filter.enabled = (alpha < 100);
	}
	
	function ieGradient(obj, start, end, horizontal) {
		var element = obj.get(0);
		var direction;
		if(horizontal) direction = 1;
		else direction = 0;
		var filter;
		try {
			filter = element.filters.item("DXImageTransform.Microsoft.Gradient");
			filter.GradientType = direction;
			filter.StartColorStr = "#"+start;
			filter.EndColorStr = "#"+end;
		} 
		catch(error) {
			filter = obj.css(_transformproperty) || "";
			if(filter.length > 0) filter += " ";
			filter += "progid:DXImageTransform.Microsoft.Gradient(GradientType="+direction+", StartColorStr='#"+start+"', EndColorStr='#"+end+"')";
			obj.css(_transformproperty, filter);
		}
	}
	
	function mozGradient(obj, start, end, horizontal) {
		var direction;
		if(horizontal) direction = "left";
		else direction = "top";
		obj.css("backgroundImage", "-moz-linear-gradient("+direction+", "+rgbColor(start)+", "+rgbColor(end)+")");
	}
	
	function canvasGradient(obj, start, end, horizontal) {
		_canvas.width = obj.width();
		_canvas.height = obj.height();
		var graphics = _canvas.getContext('2d');
		var gradient;
		if(horizontal) gradient = graphics.createLinearGradient(0, 0, _canvas.width, 0);
		else gradient = graphics.createLinearGradient(0, 0, 0, _canvas.height);
		gradient.addColorStop(0, rgbColor(start));
		gradient.addColorStop(1, rgbColor(end));
		graphics.fillStyle = gradient;
		graphics.fillRect(0, 0, _canvas.width, _canvas.height);
		obj.css({
			backgroundImage: "url('"+_canvas.toDataURL()+"')",
			backgroundSize: "100% 100%"
		});
	}
	
	function rgbColor(color) {
		var c = parseInt(color, 16);
		var r = c>>16&0xff;
		var g = c>>8&0xff;
		var b = c&0xff;
		if(color.length > 6) {
			var a = (c>>24&0xff)/255;
			return "rgba("+r+", "+g+", "+b+", "+a+")";
		}
		else {
			return "rgb("+r+", "+g+", "+b+")";
		}
	}
};

/*-----------------------------------------------------------------*
 | TWEENER                                                         |
 *-----------------------------------------------------------------*/

var Tweener = new function() {
	/*
	add(name, length, step, step_this, stepparams, complete, complete_this, completeparams)
	kill(name, end)
	*/
	
	var _this;
	var _table;
	var _count;
	
	{
		_this = this;
		_table = new Object();
		_count = 0;
	}
	
	_this.add = function(name, length, step, step_this, stepparams, complete, complete_this, completeparams) {
		if(name) kill(name);
		else name = parseInt(Math.random()*0xffffff).toString(16);
		var now = (new Date()).getTime();
		stepparams.t = 0;
		_table[name] = {start: now, length: length*1000, step: step, step_this: step_this, stepparams: stepparams, complete: complete, complete_this: complete_this, completeparams: completeparams};
		_count++;
		if(_count == 1) setTimeout(tick, 30);
		return name;
	};
	
	_this.kill = function(name, end) {
		var obj = _table[name];
		if(obj) {
			if(end) {
				obj.stepparams.t = 1;
				obj.step.call(obj.step_this, obj.stepparams);
				if(obj.complete) obj.complete.call(obj.complete_this, obj.completeparams);
			}
			delete _table[name];
			_count--;
		}
	};
	
	function tick() {
		var now = (new Date()).getTime();
		var obj;
		var time;
		for(var name in _table) {
			obj = _table[name];
			time = now-obj.start;
			if(time > 0) {
				if(time < obj.length) {
					obj.stepparams.t = time/obj.length;
					obj.step.call(obj.step_this, obj.stepparams);
				}
				else {
					Tweener.kill(name, true);
				}
			}
		}
		if(_count > 0) setTimeout(tick, 30);
	}
};

/*-----------------------------------------------------------------*
 | PAGELOADER                                                      |
 *-----------------------------------------------------------------*/

var PageLoader = function(maxerror) {
	/*
	pages
	
	setPages(pages)
	setIndex(index)
	getIndex()
	getCurrent()
	start()
	pause()
	resume()
	stop()
	isLoading()
	
	@complete
	*/
	
	var _this;
	var _maxerror;
	var _index;
	var _offset;
	var _loadindex;
	var _loadoffset;
	var _forward;
	var _loading;
	var _timer;
	
	{
		_this = this;
		_maxerror = maxerror || 3;
		_index = 0;
		_offset = 0;
		_loadindex = _index;
		_loadoffset = _offset;
		_forward = true;
		_loading = false;
	}
	
	_this.setPages = function(pages) {
		_this.stop();
		_this.pages = pages;
	};
	
	_this.setIndex = function(index) {
		_index = index;
		_offset = 0;
		if(_this.pages && _index > _this.pages.length/2) _forward = false;
		else _forward = true;
	};
	
	_this.getIndex = function() {
		return _loadindex;
	};
	
	_this.start = function() {
		if(_this.pages) {
			if(_loading) _this.stop();
			_loading = true;
			process();
		}
	};
	
	_this.pause = function() {
		if(_timer) {
			clearTimeout(_timer);
			_timer = null;
		}
		if(_this.pages) {
			var page = _this.pages[_loadindex];
			page.content.unbind("error load", loadHandler);
		}
		_loading = false;
	};
	
	_this.resume = _this.start;
	
	_this.stop = function() {
		if(_loading) {
			_this.pause();
			_this.setIndex(0);
		}
	};
	
	_this.isLoading = function() {
		return _loading;
	};
	
	function process() {
		_loadindex = _index;
		_loadoffset = _offset;
		var page = _this.pages[_loadindex];
		if(page.loaded || page.error > _maxerror) next();
		else page.content.one("error load", loadHandler).attr("src", page.url);
	}
	
	function loadHandler(event) {
		var page = _this.pages[_loadindex];
		if(event.type.match(/load/i)) page.loaded = true;
		else page.error++;
		$(_this).triggerHandler("complete", [_loadindex]);
		_timer = setTimeout(next, 50);
	}
	
	function next() {
		_timer = null;
		if(_index == _loadindex && _offset == _loadoffset) {
			_offset++;
			if(_offset < _this.pages.length) {
				if(_forward) {
					if(_offset >= _this.pages.length-_index) {
						_index--;
					}
					else {
						_index += _offset;
						_forward = !_forward;
					}
				}
				else {
					if(_offset > _index) {
						_index++;
					}
					else {
						_index -= _offset;
						_forward = !_forward;
					}
				}
				process();
			}
			else {
				_this.stop();
			}
		}
		else {
			process();
		}
	}
};

/*-----------------------------------------------------------------*
 | PAGE                                                            |
 *-----------------------------------------------------------------*/

var Page = function(magazine, index, url) {
	/*
	index
	url
	error
	loaded
	obj
	content
	*/
	
	var _this;
	var _magazine;
	
	{
		_this = this;
		_magazine = magazine;
		_this.index = index;
		_this.url = url;
		_this.error = 0;
		_this.loaded = false;
		_this.obj = $("<div></div>").css({
			left: ((1-index%2)*_magazine.pagewidth)+"px",
			width: (_magazine.pagewidth)+"px",
			height: (_magazine.pageheight)+"px",
			position: "absolute",
			overflow: "hidden",
			backgroundColor: "#eeeeee",
			filter: "progid:DXImageTransform.Microsoft.Matrix(M11=1, M21=0, M12=0, M22=1, SizingMethod='auto expand')",
			zoom: "1"
		});
		_this.content = $("<img></img>").css({width: "100%", height: "100%"}).appendTo(_this.obj);
	}
};

/*-----------------------------------------------------------------*
 | FLIPPAGE                                                        |
 *-----------------------------------------------------------------*/

var FlipPage = function(magazine) {
	/*
	frontindex
	backindex
	
	flipFrom(frontindex, backindex, px, py)
	flipTo(qx, qy)
	fixedFlipTo(qx, qy)
	release(flipped)
	*/
	
	var _this;
	var _magazine;
	var _maskfrontobj;
	var _frontobj;
	var _maskbackobj;
	var _frontshadowobj;
	var _backobj;
	var _mask_backshadowobj;
	var _backshadowobj;
	var _pagecx;
	var _px;
	var _py;
	
	{
		_this = this;
		_magazine = magazine;
		var maskcss = {
			left: (_magazine.pagewidth-_magazine.maskpx)+"px",
			top: (-_magazine.maskpy)+"px",
			width: (_magazine.masksize)+"px",
			height: (_magazine.masksize)+"px",
			position: "absolute",
			overflow: "hidden"
		};
		_maskfrontobj = $("<div></div>").css(maskcss);
		_maskbackobj = $("<div></div>").css(maskcss);
		_frontshadowobj = $("<div></div>").css({
			top: (_magazine.maskpy)+"px",
			width: (_magazine.pagewidth)+"px",
			height: (_magazine.pageheight)+"px",
			backgroundColor: "#000000",
			position: "absolute"
		});
		_mask_backshadowobj = $("<div></div>").css({
			top: (_magazine.maskpy)+"px",
			width: (_magazine.pagewidth)+"px",
			height: (_magazine.pageheight)+"px",
			position: "absolute",
			overflow: "hidden"
		});
		_backshadowobj = $("<div></div>").css({
			width: (_magazine.pagewidth)+"px",
			height: (_magazine.masksize)+"px",
			position: "absolute"
		});
		var gradientobj = $("<div></div>").css({
			width: (_magazine.pagewidth)+"px",
			height: (_magazine.masksize)+"px"
		});
		Transform.gradient(gradientobj, "00000000", "ff000000", true);
		_backshadowobj.append(gradientobj);
		_mask_backshadowobj.append(_backshadowobj);
		_maskbackobj.append(_frontshadowobj);
		_maskbackobj.append(_mask_backshadowobj);
	}
	
	_this.flipFrom = function(frontindex, backindex, px, py) {
		_this.frontindex = frontindex;
		_this.backindex = backindex;
		_frontobj = _magazine.getPageAt(_this.frontindex).obj;
		_frontobj.after(_maskfrontobj);
		_maskfrontobj.append(_frontobj);
		_frontobj.css("top", (_magazine.maskpy)+"px");
		_backobj = _magazine.getPageAt(_this.backindex).obj;
		_magazine.containerobj.append(_maskbackobj);
		Transform.opacity(_mask_backshadowobj, 1);
		_mask_backshadowobj.before(_backobj);
		_backobj.css("top", (_magazine.maskpy)+"px");
		var steps = _this.backindex-_this.frontindex;
		_pagecx = _magazine.halfpagewidth*steps/Math.abs(steps);
		_px = px;
		_py = py;
		_this.flipTo(_px, _py);
	};
	
	_this.flipTo = function(qx, qy) {
		var dx = qx-_px;
		var dy = qy-_py;
		var dl = Math.sqrt(dx*dx+dy*dy);
		var dxi = 1;
		if(dl != 0) dxi = dx/dl;
		var dyi = 0;
		if(dl != 0) dyi = dy/dl;
		var angle = Math.atan2(-dy, -dx);
		var dpcp = Math.abs((_pagecx-_px)*dxi+(_magazine.halfpageheight-_py)*dyi);
		var ro = dl/2-dpcp;
		var co = _magazine.halfmasksize+ro;
		var maskcss = {
			left: (_magazine.pagewidth+_pagecx+co*dxi-_magazine.halfmasksize)+"px",
			top: (_magazine.halfpageheight+co*dyi-_magazine.halfmasksize)+"px"
		};
		
		Transform.rotate(_maskfrontobj, angle);
		_maskfrontobj.css(maskcss);
		
		Transform.rotate(_frontobj, -angle);
		_frontobj.css("left", (_magazine.masksize+ro-_magazine.halfpagewidth)+"px");
		
		Transform.rotate(_maskbackobj, angle);
		_maskbackobj.css(maskcss);
		
		Transform.rotate(_backobj, angle);
		_backobj.css("left", (_magazine.masksize-ro-_magazine.halfpagewidth)+"px");
		
		var s = (_magazine.halfpagewidth-Math.abs(ro*2*dxi)+Math.abs(_magazine.halfpagewidth*dxi)+Math.abs(_magazine.halfpageheight*dyi))/_magazine.pagewidth-1;
		s = 1.5-s*s/2;
		
		Transform.rotateAndScale(_frontshadowobj, angle, s, 1);
		_frontshadowobj.css("left", (_magazine.masksize-ro*s-_magazine.halfpagewidth)+"px");
		Transform.opacity(_frontshadowobj, (s-1)/2);
		
		Transform.rotate(_mask_backshadowobj, angle);
		_mask_backshadowobj.css("left", (_magazine.masksize-ro-_magazine.halfpagewidth)+"px");
		if(ro > 0) Transform.opacity(_mask_backshadowobj, (s-1)/0.5);
		
		Transform.rotate(_backshadowobj, -angle);
		_backshadowobj.css({
			left: (-ro*dxi)+"px",
			top: (_magazine.halfpageheight+ro*dyi-_magazine.halfmasksize)+"px"
		});
	};
	
	_this.fixedFlipTo = function(qx, qy) {
		var dx = qx-_px;
		var dy = qy-_py;
		var dl = Math.sqrt(dx*dx+dy*dy);
		var dxi = 1;
		if(dl != 0) dxi = dx/dl;
		var dyi = 0;
		if(dl != 0) dyi = dy/dl;
		
		if(dx != 0 || dy != 0) {
			var cx = _px+dx/2;
			var cy = _py+dy/2;
			var fclen;
			if(cy > _py) fclen = (cx*dxi+cy*dyi)*2;
			else fclen = (cx*dxi+(cy-_magazine.pageheight)*dyi)*2;
			if(fclen > 0) {
				if(fclen >= dl) {
					qx = _px;
					qy = _py;
					dx = 0;
					dy = 0;
					dxi = 1;
					dyi = 0;
					dl = 0;
				}
				else {
					var ox = fclen*dxi;
					var oy = fclen*dyi;
					qx -= ox;
					qy -= oy;
					dx -= ox;
					dy -= oy;
					dl -= fclen;
				}
			}
		}
		
		var angle = Math.atan2(-dy, -dx);
		var dpcp = Math.abs((_pagecx-_px)*dxi+(_magazine.halfpageheight-_py)*dyi);
		var ro = dl/2-dpcp;
		var co = _magazine.halfmasksize+ro;
		var maskcss = {
			left: (_magazine.pagewidth+_pagecx+co*dxi-_magazine.halfmasksize)+"px",
			top: (_magazine.halfpageheight+co*dyi-_magazine.halfmasksize)+"px"
		};
		
		Transform.rotate(_maskfrontobj, angle);
		_maskfrontobj.css(maskcss);
		
		Transform.rotate(_frontobj, -angle);
		_frontobj.css("left", (_magazine.masksize+ro-_magazine.halfpagewidth)+"px");
		
		Transform.rotate(_maskbackobj, angle);
		_maskbackobj.css(maskcss);
		
		Transform.rotate(_backobj, angle);
		_backobj.css("left", (_magazine.masksize-ro-_magazine.halfpagewidth)+"px");
		
		var s = (_magazine.halfpagewidth-Math.abs(ro*2*dxi)+Math.abs(_magazine.halfpagewidth*dxi)+Math.abs(_magazine.halfpageheight*dyi))/_magazine.pagewidth-1;
		s = 1.5-Math.min(s*s, 1)/2;
		
		Transform.rotateAndScale(_frontshadowobj, angle, s, 1);
		_frontshadowobj.css("left", (_magazine.masksize-ro*s-_magazine.halfpagewidth)+"px");
		Transform.opacity(_frontshadowobj, (s-1)/2);
		
		Transform.rotate(_mask_backshadowobj, angle);
		_mask_backshadowobj.css("left", (_magazine.masksize-ro-_magazine.halfpagewidth)+"px");
		if(ro > 0) Transform.opacity(_mask_backshadowobj, (s-1)/0.5);
		
		Transform.rotate(_backshadowobj, -angle);
		_backshadowobj.css({
			left: (-ro*dxi)+"px",
			top: (_magazine.halfpageheight+ro*dyi-_magazine.halfmasksize)+"px"
		});
	};
	
	_this.release = function(flipped) {
		if(flipped) {
			_frontobj.remove();
			_maskbackobj.after(_backobj);
		}
		else {
			_backobj.remove();
			_maskfrontobj.after(_frontobj);
		}
		Transform.rotate(_frontobj, 0);
		_frontobj.css({
			left: ((1-_this.frontindex%2)*_magazine.pagewidth)+"px",
			top: "0px"
		});
		Transform.rotate(_backobj, 0);
		_backobj.css({
			left: ((1-_this.backindex%2)*_magazine.pagewidth)+"px",
			top: "0px"
		});
		_maskfrontobj.remove();
		_maskbackobj.remove();
	};
};

/*-----------------------------------------------------------------*
 | MAGAZINE                                                        |
 *-----------------------------------------------------------------*/

var Magazine = function(pagewidth, pageheight, flipstacksize, list, rightindex) {
	/*
	pagewidth
	pageheight
	halfpagewidth
	halfpageheight
	masksize
	halfmasksize
	maskpx
	maskpy
	magazinepx
	magazinepy
	width
	height
	obj
	containerobj
	rightindex
	currentrightindex
	
	numPages()
	getPageAt(index)
	flipTo(index)
	isFlipping()
	
	@start
	@complete
	*/
	
	var _this;
	var _leftstack;
	var _rightstack;
	var _flipstack;
	var _flippages;
	var _loader;
	
	{
		_this = this;
		_this.pagewidth = pagewidth;
		_this.pageheight = pageheight;
		_this.halfpagewidth = _this.pagewidth/2;
		_this.halfpageheight = _this.pageheight/2;
		_this.masksize = Math.ceil(Math.sqrt(_this.pagewidth*_this.pagewidth+_this.pageheight*_this.pageheight));
		_this.halfmasksize = _this.masksize/2;
		_this.maskpx = _this.masksize-_this.pagewidth;
		_this.maskpy = (_this.masksize-_this.pageheight)/2;
		_this.magazinepx = 0;
		_this.magazinepy = Math.ceil((_this.masksize-_this.pageheight)*0.4);
		_this.width = (_this.pagewidth+_this.magazinepx)*2;
		_this.height = _this.pageheight+_this.magazinepy*2;
		_this.obj = $("<div></div>").css({
			width: (_this.width)+"px",
			height: (_this.height)+"px",
			position: "absolute"
		});
		_this.containerobj = $("<div></div>").css({
			left: (_this.magazinepx)+"px",
			top: (_this.magazinepy)+"px",
			width: (_this.pagewidth*2)+"px",
			height: (_this.pageheight)+"px",
			position: "absolute"
		}).appendTo(_this.obj);
		_flipstack = new Array(flipstacksize);
		var i = _flipstack.length;
		while(i--) _flipstack[i] = new FlipPage(_this);
		_flippages = new Array();
		_this.rightindex = rightindex;
		_this.currentrightindex = _this.rightindex;
		var pages = new Array(list.length);
		for(var i = 0; i < pages.length; i++) pages[i] = new Page(_this, i, "/"+list[i].image);
		_loader = new PageLoader();
		_loader.setPages(pages);
		_loader.setIndex(Math.min(_this.rightindex, _loader.pages.length-1));
		_loader.start();
		updateStacks(_this.rightindex);
	}
	
	_this.numPages = function() {
		return _loader.pages.length;
	};
	
	_this.getPageAt = function(index) {
		return _loader.pages[index];
	};
	
	_this.flipTo = function(rightindex) {
		var tweenfunction;
		var flippage;
		if(rightindex > _this.rightindex) {
			tweenfunction = tweenFlipForward;
			flippage = flipStart(rightindex, _this.pagewidth, _this.pageheight);
		}
		else if(rightindex < _this.rightindex) {
			tweenfunction = tweenFlipBackward;
			flippage = flipStart(rightindex, -_this.pagewidth, _this.pageheight);
		}
		if(flippage) {
			$(_this).triggerHandler("start");
			Tweener.add(null, 0.5, tweenfunction, _this, {flippage: flippage}, tweenFlipComplete, _this);
		}
	};
	
	_this.isFlipping = function() {
		return (_flippages && _flippages.length > 0);
	};
	
	function updateStacks(rightindex) {
		_leftstack = 0;
		_rightstack = 0;
		var i;
		var page;
		i = rightindex-1;
		while(i > 0 && _leftstack < 2) {
			page = _loader.pages[i];
			_this.containerobj.prepend(page.obj);
			_leftstack++;
			i -= 2;
		}
		i = rightindex;
		while(i < _loader.pages.length && _rightstack < 2) {
			page = _loader.pages[i];
			_this.containerobj.prepend(page.obj);
			_rightstack++;
			i += 2;
		}
	}
	
	function flipStart(rightindex, px, py) {
		if(rightindex >= 0 && rightindex <= _loader.pages.length && _flipstack.length > 0) {
			var doflip = false;
			var flippage;
			if(rightindex > _this.rightindex) {
				if(_flippages.length > 0) {
					flippage = _flippages[0];
					doflip = (flippage.backindex-flippage.frontindex > 0);
				}
				else {
					doflip = true;
				}
				if(doflip) {
					preUpdateRightStack(_this.rightindex, rightindex);
					flippage = _flipstack.pop();
					_flippages.push(flippage);
					flippage.flipFrom(_this.rightindex, rightindex-1, px, py);
					_this.rightindex = rightindex;
					return flippage;
				}
				else {
					return null;
				}
			}
			else if(rightindex < _this.rightindex) {
				if(_flippages.length > 0) {
					flippage = _flippages[0];
					doflip = (flippage.backindex-flippage.frontindex < 0);
				}
				else {
					doflip = true;
				}
				if(doflip) {
					preUpdateLeftStack(_this.rightindex, rightindex);
					flippage = _flipstack.pop();
					_flippages.push(flippage);
					flippage.flipFrom(_this.rightindex-1, rightindex, px, py);
					_this.rightindex = rightindex;
					return flippage;
				}
				else {
					return null;
				}
			}
			else {
				return null;
			}
		}
		else {
			return null;
		}
	}
	
	function preUpdateLeftStack(lastrightindex, rightindex) {
		var i;
		var page;
		i = lastrightindex-3;
		while(i > rightindex-1 && _leftstack > 1) {
			page = _loader.pages[i];
			page.obj.remove();
			_leftstack--;
			i -= 2;
		}
		_leftstack--;
		i = rightindex-1-_leftstack*2;
		while(i > 0 && _leftstack < 2) {
			page = _loader.pages[i];
			_this.containerobj.prepend(page.obj);
			_leftstack++;
			i -= 2;
		}
	}
	
	function preUpdateRightStack(lastrightindex, rightindex) {
		var i;
		var page;
		i = lastrightindex+2;
		while(i < rightindex && _rightstack > 1) {
			page = _loader.pages[i];
			page.obj.remove();
			_rightstack--;
			i += 2;
		}
		_rightstack--;
		i = rightindex+_rightstack*2;
		while(i < _loader.pages.length && _rightstack < 2) {
			page = _loader.pages[i];
			_this.containerobj.prepend(page.obj);
			_rightstack++;
			i += 2;
		}
	}
	
	function tweenFlipForward(params) {
		var t = params.t*2-1;
		t = 1-t*t;
		params.flippage.flipTo(_this.pagewidth-params.t*2*_this.pagewidth, _this.pageheight+t*_this.maskpy);
	}
	
	function tweenFlipBackward(params) {
		var t = params.t*2-1;
		t = 1-t*t;
		params.flippage.flipTo(params.t*2*_this.pagewidth-_this.pagewidth, _this.pageheight+t*_this.maskpy);
	}
	
	function tweenFlipComplete() {
		var flippage = _flippages.shift();
		flippage.release(true);
		var rightindex;
		if(flippage.backindex > flippage.frontindex) {
			rightindex = flippage.backindex+1;
			postUpdateLeftStack(_this.currentrightindex, rightindex);
			_this.currentrightindex = rightindex;
		}
		else {
			rightindex = flippage.backindex;
			postUpdateRightStack(_this.currentrightindex, rightindex);
			_this.currentrightindex = rightindex;
		}
		_flipstack.push(flippage);
		var page = _loader.pages[flippage.backindex];
		if(!page.loaded) {
			_loader.pause();
			_loader.setIndex(flippage.backindex);
			_loader.resume();
		}
		$(_this).triggerHandler("complete");
	}
	
	function postUpdateLeftStack(lastrightindex, rightindex) {
		var i;
		var page;
		i = lastrightindex-1-(_leftstack-1)*2;
		while(i < rightindex-3 && _leftstack > 0) {
			page = _loader.pages[i];
			page.obj.remove();
			_leftstack--;
			i += 2;
		}
		_leftstack++;
		i = rightindex-1-_leftstack*2;
		while(i > 0 && _leftstack < 2) {
			page = _loader.pages[i];
			_this.containerobj.prepend(page.obj);
			_leftstack++;
			i -= 2;
		}
	}
	
	function postUpdateRightStack(lastrightindex, rightindex) {
		var i;
		var page;
		i = lastrightindex+(_rightstack-1)*2;
		while(i > rightindex+2 && _rightstack > 0) {
			page = _loader.pages[i];
			page.obj.remove();
			_rightstack--;
			i -= 2;
		}
		_rightstack++;
		i = rightindex+_rightstack*2;
		while(i < _loader.pages.length && _rightstack < 2) {
			page = _loader.pages[i];
			_this.containerobj.prepend(page.obj);
			_rightstack++;
			i += 2;
		}
	}
};

/*-----------------------------------------------------------------*
 | ZOOMIMAGE                                                       |
 *-----------------------------------------------------------------*/

var ZoomImage = function(pagewidth, pageheight) {
	/*
	obj
	
	load(lefturl, righturl)
	unload()
	*/
	
	var _this;
	var _left;
	var _right;
	
	{
		_this = this;
		_this.obj = $("<div></div>").css({position: "absolute", width: (pagewidth*2)+"px", height: pageheight+"px", overflow: "hidden"});
		_left = $("<img></img>").css({position: "absolute", top: "0", width: "50%", height: "100%", display: "none"});
		_right = _left.clone().css("left", "50%").appendTo(_this.obj);
		_left.css("left", "0").appendTo(_this.obj);
	}
	
	_this.load = function(lefturl, righturl) {
		_this.unload();
		if(lefturl) {
			if(_left.attr("src") != lefturl) _left.one("load", loadHandler).attr("src", lefturl);
			else _left.css("display", "block");
		}
		if(righturl) {
			if(_right.attr("src") != righturl) _right.one("load", _right, loadHandler).attr("src", righturl);
			else _right.css("display", "block");
		}
	};
	
	_this.unload = function() {
		_left.unbind("load", loadHandler).css("display", "none");
		_right.unbind("load", loadHandler).css("display", "none");
	};
	
	function loadHandler(event) {
		$(this).css("display", "block");
	} 
};

/*-----------------------------------------------------------------*
 | EMAGAZINE                                                       |
 *-----------------------------------------------------------------*/

var eMagazine = function(id, width, height, siteaccess, pagewidth, pageheight, download, index) {
	/*
	obj
	
	previous()
	next()
	flipTo(index)
	*/
	
	var _this;
	var _pagewidth;
	var _pageheight;
	var _download;
	var _rightindex;
	var _view;
	var _list;
	var _magazine;
	var _zoomimage;
	var _input;
	var _controlsheight;
	var _zoomtimer;
	var _trackstart;
	var _trackcurrent;
	
	{
		if(Transform.rotate && Transform.scale && Transform.gradient) {
			_this = this;
			_pagewidth = pagewidth;
			_pageheight = pageheight;
			_download = download;
			_rightindex = index || 0;
			_view = {width: width, height:height, zoom: true, fullscreen: null};			
			_this.obj = $("#magazine-"+id).data("magazine", _this).empty().css({width: _view.width+"px", height: _view.height+"px", overflow: "hidden"});
			var position = _this.obj.css("position");
			if(!position || !position.match(/absolute/i)) _this.obj.css("position", "relative");
			$.getJSON("/"+siteaccess+"/em_amfphp/ajax/["+id+"]/Extension_Current_Classes_Lib_Amfphp_Services/getMagazine", ajaxCompleteHandler);
		}
	}
	
	_this.previous = function() {
		if(_magazine) {
			_rightindex = Math.max(_rightindex-2, 0);
			_magazine.flipTo(_rightindex);
		}
	};
	
	_this.next = function() {
		if(_magazine) {
			_rightindex = Math.min(_rightindex+2, Math.ceil(_magazine.numPages()/2)*2);
			_magazine.flipTo(_rightindex);
		}
	};
	
	_this.flipTo = function(index) {
		if(_magazine) {
			_rightindex = Math.ceil(index/2)*2;
			_magazine.flipTo(_rightindex);
		}
	};
	
	function ajaxCompleteHandler(result) {
		_list = result;
		_rightindex = Math.min(Math.max(_rightindex, 0), _list.mid.length-1);
		_rightindex = Math.ceil(_rightindex/2)*2;
		_magazine = new Magazine(_pagewidth, _pageheight, 3, _list.mid, _rightindex);
		_magazine.obj.css({left: "50%", top: "50%"}).appendTo(_this.obj);
		_zoomimage = new ZoomImage(_pagewidth, _pageheight);
		_zoomimage.obj.css({left: _magazine.magazinepx+"px", top: _magazine.magazinepy+"px"});
		var controls = $("<div></div>").css({position: "absolute", width: "100%"}).addClass("controls").appendTo(_this.obj);
		var left = $("<span></span>").addClass("arrow previous").click(_this.previous);
		var right = $("<span></span>").addClass("arrow next").click(_this.next);
		var length = (""+_magazine.numPages()).length*2+1;
		_input = $("<input></input>").attr({type: "text", size: length, maxlength: length}).addClass("input").focus(inputFocusHandler).change(inputChangeHandler).keypress(inputKeyPressHandler);
		var controlbox = $("<div></div>").css({position: "relative"}).addClass("controlbox");
		controlbox.append(left).append("Seite(n)").append(_input).append("von "+_magazine.numPages()).append(right).appendTo(controls);
		var zoom = $("<div></div>").css({position: "absolute"}).addClass("button zoom").appendTo(controls).click(zoomClickHandler);
		var download = $("<div></div>").css({position: "absolute"}).addClass("button download").appendTo(controls).click(downloadClickHandler);
		var fullscreen = $("<div></div>").css({position: "absolute"}).addClass("button fullscreen").appendTo(controls).click(fullscreenClickHandler);
		_controlsheight = controls.innerHeight();
		_view.originx = -_magazine.width/2;
		_view.originy = -(_magazine.height+_controlsheight)/2;
		_zoomtimer = 0;
		resizeHandler(null);
		zoomClickHandler(null);
		$(_magazine).bind("start", flipStartHandler).bind("complete", flipCompleteHandler).triggerHandler("complete");
		_magazine.obj.bind("touchstart mousedown", trackStartHandler);
	}
	
	function inputFocusHandler(event) {
		_input.select();
	}
	
	function inputChangeHandler(event) {
		$(_magazine).triggerHandler("complete");
	}
	
	function inputKeyPressHandler(event) {
		if(event.keyCode == "13") {
			_input.blur();
			var index = parseInt(_input.val());
			if(isNaN(index)) {
				$(_magazine).triggerHandler("complete");
			}
			else {
				index = Math.min(Math.max(index-1, 0), _magazine.numPages()-1);
				if(index == _magazine.rightindex-1 || index == _magazine.rightindex) $(_magazine).triggerHandler("flipcomplete");
				else _this.flipTo(index);
			}
		}
	}
	
	function zoomClickHandler(event) {
		$(_magazine).triggerHandler("start");
		if(_view.zoom) {
			_view.scale = Math.min(_view.width/_magazine.width, _view.height/(_magazine.height-_controlsheight), 1);
			_view.offsetx = 0;
			_view.offsety = 0;
		}
		else {
			_view.scale = 1;
			_zoomtimer = setTimeout(showZoomImage, 100);
		}
		_view.zoom = !_view.zoom;
		_magazine.obj.css({marginLeft: (_view.originx+_view.offsetx)+"px", marginTop: (_view.originy+_view.offsety)+"px"});
		Transform.scale(_magazine.obj, _view.scale, _view.scale);
	}
	
	function downloadClickHandler(event) {
		document.location.href = _download;
	}
	
	function fullscreenClickHandler(event) {
		var w = $(window);
		var h = $("html");
		var b = $("body");
		if(_view.fullscreen) {
			h.css({width: "", height: ""});
			b.css(_view.fullscreen.bodycss);
			_this.obj.css(_view.fullscreen.objcss);
			if(_view.fullscreen.prev.length > 0) _view.fullscreen.prev.after(_this.obj);
			else _view.fullscreen.parent.append(_this.obj);
			_view.fullscreen = null;
			w.triggerHandler("resize");
			w.unbind("resize", resizeHandler);
		}
		else {
			_view.fullscreen = {
				bodycss: {width: "", height: "", overflow: b.css("overflow") || ""},
				objcss: {position: _this.obj.css("position"), left: _this.obj.css("left") || "", top: _this.obj.css("top") || "", width: _this.obj.css("width") || "", height: _this.obj.css("height") || ""},
				prev: _this.obj.prev(),
				parent: _this.obj.parent()
			};
			h.css({width: "100%", height: "100%"});
			b.css({width: "100%", height: "100%", overflow: "hidden"});
			_this.obj.css({position: "absolute", left: "0", top: "0", width: "100%", height: "100%"}).appendTo(b);
			w.resize(resizeHandler).triggerHandler("resize");
		}
	}
	
	function flipStartHandler(event) {
		if(_zoomtimer) {
			clearTimeout(_zoomtimer);
			_zoomtimer = null;
		}
		else if(_zoomimage.obj.parent().length > 0) {
			_zoomimage.obj.remove();
			_zoomimage.unload();
		}
	}
	
	function flipCompleteHandler(event) {
		var index = _magazine.currentrightindex;
		if(index >= _magazine.numPages()) _input.val(""+_magazine.numPages());
		else if(index > 0) _input.val(index+"-"+(index+1));
		else _input.val("1");
		if(_view.zoom && !_magazine.isFlipping()) _zoomtimer = setTimeout(showZoomImage, 100);
	}
	
	function showZoomImage() {
		_zoomtimer = null;
		var index = _magazine.currentrightindex;
		if(index >= _magazine.numPages()) _zoomimage.load("/"+_list.max[_magazine.numPages()-1].image, null);
		else if(index > 0) _zoomimage.load("/"+_list.max[index-1].image, "/"+_list.max[index].image);
		else _zoomimage.load(null, "/"+_list.max[0].image);
		_zoomimage.obj.appendTo(_magazine.obj);
	}
	
	function resizeHandler(event) {
		_view.width = _this.obj.width() || _view.width;
		_view.height = _this.obj.height() || _view.height;
		if(!_view.zoom) {
			_view.scale = Math.min(_view.width/_magazine.width, _view.height/(_magazine.height-_controlsheight), 1);
			Transform.scale(_magazine.obj, _view.scale, _view.scale);
		}
	}
	
	function trackStartHandler(event) {
		var touches = event.originalEvent.touches;
		if(touches && touches.length > 0) {
			if(touches.length < 2) {
				_trackstart = touches[0];
			}
			else {
				_trackstart = null;
				_trackend = null;
				_this.obj.unbind("touchmove mousemove", trackMoveHandler).unbind("touchend mouseup", trackEndHandler);
			}
		}
		else {
			event.preventDefault();
			_trackstart = event;
		}
		if(_trackstart) {
			_trackstart = {screenX: _trackstart.screenX, pageX: _trackstart.pageX, pageY: _trackstart.pageY};
			_trackend = _trackstart;
			_this.obj.bind("touchmove mousemove", trackMoveHandler).bind("touchend mouseup", trackEndHandler);
		}
	}
	
	function trackMoveHandler(event) {
		event.preventDefault();
		var touches = event.originalEvent.touches;
		if(touches && touches.length > 0) _trackend = touches[0];
		else _trackend = event;
		if(_view.zoom) {
			_view.offsetx += _trackend.pageX-_trackstart.pageX;
			_view.offsety += _trackend.pageY-_trackstart.pageY;
			_magazine.obj.css({marginLeft: (_view.originx+_view.offsetx)+"px", marginTop: (_view.originy+_view.offsety)+"px"});
			_trackstart.pageX = _trackend.pageX;
			_trackstart.pageY = _trackend.pageY;
		}
	}
	
	function trackEndHandler(event) {
		_this.obj.unbind("touchmove mousemove", trackMoveHandler).unbind("touchend mouseup", trackEndHandler);
		if(!_view.zoom) {
			var delta = _trackend.screenX-_trackstart.screenX;
			if(delta < -30) _this.next();
			else if(delta > 30) _this.previous();
		}
	}
};
