function fade(obj_to, obj_from, smoothness, duration) { //måste fixas för ie 8, och lite mer

	/* smoothess: between 1 and 100, the lower number the smoother fade, smoothess 100 means it just flipps.
	 * duration: time from start to end in milliseconds. might not be correct if smoothness is very high or very low
	 */

	//lets start with some variable-control, setting and defaultisation
	if(typeof obj_to == "string") obj_to = id(obj_to);
	if(typeof obj_from == "string") obj_from = id(obj_from);
	if(!obj_to || !obj_from) return false;

	if(!smoothness) smoothness = 5;
	if(!duration) duration = 750;

	var interval = duration/(100/smoothness);

	//here comes the logic
	if(window.getComputedStyle) {
		if(obj_from.style.opacity === "") obj_from.style.opacity = 1;
	} else if(obj_from.filters[0]) {
		if(obj_from.filters[0].Opacity === "") obj_from.filters[0].Opacity = 100;
	} else { //this means this method doesn't support this browser and that should be noted
		throw new Error("fade() is not supported, reprogram and do it correctly this time!");
	}

	var op;

	var t = setInterval(function() {

		if(window.getComputedStyle) {
			if((op = obj_from.style.opacity - (smoothness/100)) > 0) obj_from.style.opacity = op;
		} else if(obj_from.filters[0]) {
			if((op = obj_from.filters[0].opacity - smoothness) > 0) obj_from.filters[0].opacity = op;
		}

		if(op <= 0) clearInterval(t);

	}, interval );
}

function positionLayer(obj, mode, e, offsetX, offsetY) { //mode should be either center or mouse, does what the names imply

	obj = id(obj);
	if(!obj || !mode) return;

	var disp = currentStyle(obj, "display");
	var visi = currentStyle(obj, "visibility");
	var pos = currentStyle(obj, "position");

	obj.style.visibility = "hidden";
	obj.style.position = "absolute";
	obj.style.display = "block"; //is needed to calculate obj's dimensions

	var width = currentStyle(obj, "width");
	var height = currentStyle(obj, "height");

	obj.style.display = disp;
	obj.style.visibility = visi;
	obj.style.position = pos;

	var winDim = windowDimensions();

	if(mode == "center") {
		obj.style.left = (winDim.width/2 - width/2) + (offsetX ? offsetX : 0) + "px";
		obj.style.top = (winDim.height/2 - height/2) + (offsetY ? offsetY : 0) + "px";
	} else if(e && mode == "mouse") {
		var y = e.clientY;
		var x = e.clientX;
		obj.style.left = ((x + width > winDim.width) ? x - width : x) + (offsetX ? offsetX : 0) + "px";
		obj.style.top = ((y + height > winDim.height) ? y - height : y) + (offsetY ? offsetY : 0) + "px";
	}
}

function showLayer(obj, mode, e) {

	obj = id(obj);
	if(!obj) return;

	if(mode) positionLayer(obj, mode, e);
	obj.style.display = "block";
	obj.style.visibility = "visible"; // old ie bug
}

function hideLayer(obj) {

	obj = id(obj);
	if(!obj) return;

	obj.style.display = "none";
	obj.style.visibility = "hidden"; // old ie bug
}

function toggleLayer(obj, mode, e) {

	obj = id(obj);
	if(!obj) return;

	var disp = currentStyle(obj, "display");

	if(!disp || disp == "none") {
		if(mode) positionLayer(obj, mode, e);
		obj.style.display = "block";
	} else obj.style.display = "none";
}

function fillScreen(obj, width, offsetX, height, offsetY) { //compensate for position with offsetX/offsetY

	obj = id(obj);
	if(!obj) return;

	var winDim = windowDimensions();

	if(width) obj.style.width = winDim.width - (offsetX ? offsetX : 0) - currentStyle(obj, "padding-left") - currentStyle(obj, "padding-right") + "px";
	if(height) obj.style.height = winDim.height - (offsetY ? offsetY : 0) - currentStyle(obj, "padding-top") - currentStyle(obj, "padding-bottom") + "px";

	if(!obj.resize_event) {
		addEvent(window, "resize", function(){ fillScreen(obj, width, offsetX, height, offsetY); });
		obj.resize_event=true;
	}
}

function disableSelection(e) { //förhindra selection, inte 100%-ig kanske än men...
	if(document.body.onselectstart != 'undefined') {
		document.body.onselectstart = function() {
			document.body.style.cursor = 'default';
			return false;
		}
	} else if(document.body.style.MozUserSelect) {
		document.body.style.MozUserSelect = 'none';
	}
}

function enableSelection() { //återställ selection
	if(typeof document.body.onselectstart == "function") document.body.onselectstart = function() { return true; };
	else if(document.body.style.MozUserSelect) document.body.style.MozUserSelect = 'text';
}

function selectionObj(mode, win) { // mode = range/selection/text,node, win = window if not the current one
	if(!win) win = window;
	if(window.getSelection) var ws = true;

	//här sker det viktiga typ...
	var t = (ws) ? win.window.getSelection() : win.document.selection.createRange();

	if(!mode || mode == 'range') return (ws) ? t.getRangeAt(0) : t;
	else if(mode == 'selection') return t;
	else if(mode == 'text') return (ws) ? t.toString() : t.text;
	else if(mode == 'node') return (ws) ? t.focusNode : t.parentElement();
}

function scrollIntoView(obj, parent, offset, jumpLength, timerSpeed){ //scrolls an object into view, offset is the position the object shall have from parents uppermost border or window

	obj = id(obj);
	parent = id(parent);

	if(!offset) offset = 0;

	if(!obj || !parent) return;
	
	var pos = currentStyle(obj, "top") - offset;

	if(!timerSpeed) {

		parent.scrollTop = pos;

	} else {

		jumpLength = jumpLength || 31; //sets how long it jumps on each timer-tick

		var increase = false; //sets which way to scroll

		var difference = id(parent).scrollTop - pos;

		if(difference <= 0) increase = true;

		var i = Math.abs(Math.floor(difference / jumpLength));

		var interval = setInterval(function() {

			if(i > 1) {
				if(increase) id(parent).scrollTop += jumpLength;
				else id(parent).scrollTop -= jumpLength;

				i--;
			} else {

				id(parent).scrollTop = pos;
				clearInterval(interval);
			}

		}, timerSpeed);
	}
}


// data-structures


/* A storage-friendly structure that only keeps track of the uncollected stuff.
 * Only use the built-in methods.
 */
function CircularQueue(length) {

	if(!length) return;

	this.length = length;

	this._array = [];
	this._nextIndex = 0;
	this._oldest = 0;
	this._started = false;
}

CircularQueue.prototype = {

	add: function(element) {

		this._array[this._nextIndex] = element;

		//if the inserted position is the same as the current oldest, the oldest have to be changed since it is now the newest
		if(this._nextIndex == this._oldest && this._started) this._updateOldestIndex();
		
		//to prevent that oldest follows nextIndex when initiated
		if(!this._started) this._started = true;

		this._updateIndex();
	},

	remove: function() {
		var tmp = this._array[this._oldest];		
		this._updateOldestIndex();
		return tmp;
	},

	toArray: function() {

		var tmp = this._oldest;
		var stop = this._nextIndex;
		var array = [];

		do {

			array.push(this._array[tmp]);

			tmp++;

			if(tmp > (this.length - 1)) tmp = 0;

		} while(tmp != stop);

		return array;
	},

	peek: function() {
		return this._array[this._oldest];
	},

	_updateIndex : function() {
		if((++this._nextIndex) == this.length) this._nextIndex = 0;
	},
	
	_updateOldestIndex: function() {
		if((++this._oldest) == this.length) this._oldest = 0;
	}
}
