function SliceViewer(idOrElement)
{
	this.keyHole = new DynamicElement(idOrElement);
	this.keyHoleSize = this.keyHole.getSize();

	this.sliceContainer = new DynamicElement();
	this.innerPosition = {x: 0, y: 0};
	
	this.imagePath = "";
	this.imagePrefix = "";
	this.imageExtension = ".jpg";
	
	this.sliceWidth = 0;
	this.sliceHeight = 0;
	this.sliceCountX = 0;
	this.sliceCountY = 0;

	this.totalWidth = 0;
	this.totalHeight = 0;

	this.slices = [];
	
	this.id = SliceViewer.instances.length;
	SliceViewer.instances[this.id] = this;
	
	this.onClick = null;
	this.onChange = null;
	this.animationTimer = null;
	this.animation = SliceViewer.animateCallback;
	
	this.init();
}
SliceViewer.instances = [];

SliceViewer.prototype.getStaticReference = function()
{
	return "SliceViewer.instances[" + this.id + "]";
}

SliceViewer.prototype.invokeClickEvent = function(sender, position)
{
	var e = {sender: sender, x: position.x, y: position.y};
	if(this.onClick)
		this.onClick(e);
}

SliceViewer.prototype.show = function()
{
	this.refresh();
	this.keyHole.show();
	this.sliceContainer.show();
}

SliceViewer.prototype.hide = function()
{
	this.keyHole.hide();
	this.sliceContainer.hide();
}

SliceViewer.prototype.init = function()
{
	this.keyHole.appendChild(this.sliceContainer);
	this.keyHole.setStyle("overflow", "hidden");
	
	this.sliceContainer.setSize(0, 0);
//	this.sliceContainer.setInnerPosition(0, 0);
}

SliceViewer.prototype.setImagePath = function(path, prefix, extension)
{
	this.imagePath = path;
	this.imagePrefix = prefix;
	this.imageExtension = extension;
}

/*
SliceViewer.prototype.setView = function(width, height)
{
	this.keyHole.setSize(width, height);
	this.keyHoleSize = this.keyHole.getSize();
}
*/

SliceViewer.prototype.createSlices = function(sliceWidth, sliceHeight, countX, countY)
{
	this.sliceWidth = sliceWidth;
	this.sliceHeight = sliceHeight;
	
	this.sliceCountX = countX;
	this.sliceCountY = countY;

	this.totalWidth = this.sliceWidth * this.sliceCountX;
	this.totalHeight = this.sliceHeight * this.sliceCountY;
	
	this.sliceContainer.setSize(this.totalWidth, this.totalHeight);
	
	for(var y = 0; y < this.sliceCountY; y++)
	{
		for(var x = 0; x < this.sliceCountX; x++)
		{
			var slice = new Slice(this, x, y, this.sliceWidth, this.sliceHeight, SliceViewer.onRender2DSlice);
			slice.setPosition(this.sliceWidth * x, this.sliceHeight * y);
//			slice.setStyle("border", "1px solid white");
			
			this.sliceContainer.appendChild(slice);
			
			this.slices.push(slice);
		}
	}
}

SliceViewer.prototype.appendChild = function(childElement)
{
	this.sliceContainer.appendChild(childElement);
}

SliceViewer.advanceAnimation = function(viewer, path, timeout, onFinished)
{
	if(viewer.animationTimer)
		clearTimeout(viewer.animationTimer);
		
/*
	var pos = viewer.adjustInnerPosition(path[0], path[1]);
	pos = viewer.calculateInnerPosition(pos.x, pos.y);
	var note = createNote("x", pos.x, pos.y);
	viewer.appendChild(note);
*/
	
	viewer.setInnerPosition(path[0], path[1]);
	path.shift();
	path.shift();
	
	if(path.length > 1)
		viewer.animationTimer = setTimeout("SliceViewer.advanceAnimation(" + viewer.getStaticReference() + ", [" + path.toString() + "], " + timeout + ", " + onFinished + ");", timeout);
	else
	{
		// animation done
		if( onFinished )
			onFinished();
	}
}

SliceViewer.animateCallback = function (t, b, c, d) 
{
	if ((t/=d/2) < 1) return c/2*t*t*t + b;
	return c/2*((t-=2)*t*t + 2) + b;
}

SliceViewer.prototype.animateToPosition = function(x, y, referenceX, referenceY, animationFunction, onFinished )
{
	var startPos = this.innerPosition;
	var endPos = this.calculateInnerPosition(x, y, referenceX, referenceY);
	
	var diffX = endPos.x - startPos.x;
	var diffY = endPos.y - startPos.y;
	var steps = Math.sqrt(diffX * diffX + diffY * diffY) / 25;
	var path = [];
	
	if(!animationFunction)
		animationFunction = SliceViewer.animateCallback;
		
	for(var i = 0; i < steps; i++)
	{
		path.push(animationFunction(i, startPos.x, diffX, steps));
		path.push(animationFunction(i, startPos.y, diffY, steps));
	}
	
	if(path.length > 0)
		SliceViewer.advanceAnimation(this, path, 25, onFinished);
}

SliceViewer.prototype.moveToCenter = function(x, y, animate)
{
	if(animate)
		return this.animateToPosition(x, y, this.animation);
		
	var pos = this.calculateInnerPosition(x, y);
	this.setInnerPosition(pos.x, pos.y);
}

SliceViewer.prototype.moveToReference = function(x, y, referenceX, referenceY, animate, onFinished)
{
	if(animate)
		return this.animateToPosition(x, y, referenceX, referenceY, this.animation, onFinished);
		
	if( onFinished )
		onFinished();
		
	var pos = this.calculateInnerPosition(x, y, referenceX, referenceY);
	this.setInnerPosition(pos.x, pos.y);
}

SliceViewer.prototype.getPosition = function(referenceX, referenceY)
{
	if(typeof referenceX == "undefined")
		referenceX = this.keyHoleSize.width / 2;
		
	if(typeof referenceY == "undefined")
		referenceY = this.keyHoleSize.height / 2;
		
	return {x: Math.round(referenceX - this.innerPosition.x), y: Math.round(referenceY - this.innerPosition.y)};
}

SliceViewer.prototype.calculateInnerPosition = function(x, y, referenceX, referenceY)
{
	if(typeof referenceX == "undefined")
		referenceX = this.keyHoleSize.width / 2;
		
	if(typeof referenceY == "undefined")
		referenceY = this.keyHoleSize.height / 2;
		
	return {x: Math.round(referenceX - x), y: Math.round(referenceY - y)};
}

SliceViewer.prototype.calculateDistance = function(x, y, referenceX, referenceY)
{
	var pos = this.calculateInnerPosition(x, y, referenceX, referenceY);
	return Math.sqrt((pos.x - this.innerPosition.x) * (pos.x - this.innerPosition.x) + (pos.y - this.innerPosition.y) * (pos.y - this.innerPosition.y));
}

SliceViewer.prototype.adjustInnerPosition = function(xPos, yPos)
{
	var pos = {x: xPos, y: yPos};
	
	if(pos.x > 0)
		pos.x = 0;
	if(pos.y > 0)
		pos.y = 0;

	if(this.totalWidth + pos.x < this.keyHoleSize.width)
		pos.x = this.keyHoleSize.width - this.totalWidth;
	if(this.totalHeight + pos.y < this.keyHoleSize.height)
		pos.y = this.keyHoleSize.height - this.totalHeight;
	
	return pos;
}

SliceViewer.prototype.refresh = function()
{
	this.sliceContainer.setPosition(this.innerPosition.x, this.innerPosition.y);
	var spanX = Math.floor(this.keyHoleSize.width / this.sliceWidth) + 2;
	var spanY = Math.floor(this.keyHoleSize.height / this.sliceHeight) + 2;
	var startX = Math.floor(-this.innerPosition.x / this.sliceWidth);
	var startY = Math.floor(-this.innerPosition.y / this.sliceHeight);
	
	var index = 0;
	for(var j = startY; j < startY + spanY; j++)
		for(var i = startX; i < startX + spanX; i++)
		{
			index = j * this.sliceCountX + i;
			if(this.slices[index])
				this.slices[index].load();
		}
/*
	for(var i = 0; i < this.slices.length; i++)
	{
		var pos = this.slices[i].getPosition();
		var size = this.slices[i].getSize();
		if(
			(this.innerPosition.x + pos.x + size.width > 0 && this.innerPosition.x + pos.x < this.keyHoleSize.width) && 
			(this.innerPosition.y + pos.y + size.height > 0 && this.innerPosition.y + pos.y < this.keyHoleSize.height)
		)
			this.slices[i].load();
	}
*/
}

SliceViewer.prototype.setInnerPosition = function(x, y)
{
	this.innerPosition = this.adjustInnerPosition(x, y);
	this.refresh();
	if(this.onChange)
	{
		var e = {sender: this};
		this.onChange(e);
	}
}

SliceViewer.onRenderPhotoshopSlice = function(slice)
{
	var count = slice.yIndex * slice.viewer.sliceCountX + slice.xIndex + 1;
	if(count < 10)
		count = "0" + count;
		
	return '<img src="'+slice.viewer.imagePath+'/'+slice.viewer.imagePrefix + count + slice.viewer.imageExtension +'" alt="" />';
}

SliceViewer.onRender2DSlice = function(slice)
{
	var countX = slice.xIndex + 1;
	if(countX < 10)
		countX = "0" + countX;
		
	var countY = slice.yIndex + 1;
	if(countY < 10)
		countY = "0" + countY;
		
	return '<img src="'+slice.viewer.imagePath+'/'+slice.viewer.imagePrefix + countX + '_' + countY + slice.viewer.imageExtension +'" alt="" />';
}

SliceViewer.getCoordinatesByMouseEvent = function(e)
{
	if(typeof e.layerX != "undefined")
		return {x: e.layerX, y: e.layerY};
		
	return {x: e.offsetX, y: e.offsetY};
}








function Slice(viewer, x, y, width, height, onRender)
{
	this.DynamicElement();
	
	this.viewer = viewer;
	
	this.xIndex = x;
	this.yIndex = y;
	
	this.width = width;
	this.height = height;
	this.setSize(width, height);
	
	this.onRender = onRender;
	
	this.id = Slice.instances.length;
	Slice.instances[this.id] = this;
	
	this.loaded = false;
	
	this.init();
}
Slice.inherits("DynamicElement");
Slice.instances = [];

Slice.prototype.init = function()
{
	this.elm.onmouseup = new Function("e", "if(!e) e = window.event; return " + this.getStaticReference() + ".onMouseUp(e);");
	this.setHtml("Loading " + this.id + ": " + this.xIndex + ", " + this.yIndex + "...");
}

Slice.prototype.getStaticReference = function()
{
	return "Slice.instances[" + this.id + "]";
}
	
Slice.prototype.onMouseUp = function(e)
{
	var pos = SliceViewer.getCoordinatesByMouseEvent(e);
	viewer.invokeClickEvent(this, {x: pos.x + this.width * this.xIndex, y: pos.y + this.height * this.yIndex});
}

Slice.prototype.load = function()
{
	if(this.loaded)
		return;
		
	this.loaded = true;
	this.setHtml(this.onRender(this));
	this.show();
}

