/* ==== Easing function ==== */
function Ease (position) {
	this.position = position;
}
Ease.prototype = {
	// ---- ease ----
	ease : function () {
		if (this.m == 0 && this.s == -1) {
			this.moving = false;
			return this.position;
		}
		this.moving = true;
		this.m += this.s;
		this.position += (this.d * this.m * .0025);
		if (this.m == 20) this.s = -1;
		return this.position;
	},
	// ---- set target value ----
	setTarget : function (target) {
		this.target = target;
		this.m = 0;
		this.s = 1;
		this.d = target - this.position;
	}
}

var grid = {
	// ---- variables ----
	O : [],
	M : [],
	D : {},
	screen : {
		w : 0,
		h : 0
	},
	m : false,
	moving: false,

	/* ==== create HTML Elements ==== */
	createHTML : function (id, x, y) {
		// ---- resizable container ----
		var o = document.createElement('span');
		o.className = 'gridSpan';
		var p = document.getElementById(id);
		// ---- injection from HTML source ----
		if (p) o.innerHTML = p.innerHTML;
		grid.screen.obj.appendChild(o);
		// ---- variables ----
		grid.O.push(o);
		o.m = false;
		o.X = x;
		o.Y = y;
		o.c = o.childNodes[o.firstChild.nodeType == 3 ? 1 : 0]
		if (!grid.M[y]) grid.M[y] = [];
		grid.M[y][x] = o;
		grid.D[id] = o;
		// ---- create easing params ----
		o.w = new Ease(10);
		o.h = new Ease(10);
		o.x = new Ease(grid.screen.w / 2);
		o.y = new Ease(grid.screen.h / 2);

		/* ==== click event ==== */
		o.onmousedown = function (e) {
			if (window.event) e = window.event;
			var tg = (e.target) ? e.target : e.srcElement;
			// ---- different div ----
			if (this != grid.m) {
				if (grid.m) grid.m.m = false;
				grid.m = this;
				grid.position();
			} else {
				// ---- same div - protect clicks ----
				if ((tg.tagName == "IMG" || tg.className.indexOf('click') >= 0) && tg.parentNode.tagName != "A") {
					grid.m = false;
					grid.position();
				}
			}
			return false;
		}

		/* ==== HTML rendering ==== */
		o.animate = function () {
			var o   = this.style;
			o.left   = Math.round(this.x.ease()) + 'px';
			o.top    = Math.round(this.y.ease()) + 'px';
			o.width  = Math.round(this.w.ease()) + 'px';
			o.height = Math.round(this.h.ease()) + 'px';
			// ---- moving or not moving that's the question ----
			if (this.x.moving || this.y.moving) grid.moving = true;
		}
	},

	/* ==== init script ==== */
	init : function (ids, marg, nw, nh) {
		this.screen.obj = document.getElementById('screen');
		this.ids = ids;
		this.H = ids.length;
		this.L = 0;
		// ---- dimensions ----
		this.marg = marg;
		this.nw = nw;
		this.nh = nh;
		this.resize();
		// ---- click on background event ----
		this.screen.obj.onmousedown = function (e) {
			if (window.event) e = window.event;
			var tg = (e.target) ? e.target : e.srcElement;
			if (tg.id == "screen") {
				grid.m = false;
				grid.position();
			}
			return false;
		}
	},

	/* ==== build structure ==== */
	structure : function () {
		for (var i = 0; i < this.H; i++) this.L = Math.max(this.L, this.ids[i].length);
		for (var i = 0; i < this.H; i++) {
			var l = this.ids[i];
			for (var j = 0; j < l.length; j++) {
				var id = l[j];
				if (id) this.createHTML(id, j, i);
			}
		}
		// ---- Init complete ----
		grid.init = false;
		this.resize();
	},

	/* ==== set position targets ==== */
	position : function () {
		// ---- collapsed position ----
		var x = (grid.screen.w - (grid.L * grid.nw)) / 2;
		var y = (grid.screen.h - (grid.H * grid.nh)) / 2;
		var i = 0, o;
		while( o = grid.O[i++] ) {
			o.w.setTarget(grid.nw - grid.marg);
			o.h.setTarget(grid.nh - grid.marg);
			o.x.setTarget(x + grid.marg + o.X * grid.nw);
			o.y.setTarget(y + grid.marg + o.Y * grid.nh);
		}
		// ---- expanded div ----
		if (grid.m) {
			// ---- expanded position ----
			grid.m.w.setTarget(grid.m.c.offsetWidth);
			grid.m.h.setTarget(grid.m.c.offsetHeight);
			grid.m.x.setTarget(grid.marg + (grid.screen.w - grid.m.w.target) / 2);
			grid.m.y.setTarget(grid.marg + (grid.screen.h - grid.m.h.target) / 2);
			// ---- bottom y ----
			var y = grid.m.y.target;
			for (var i = grid.m.Y; i < grid.H; i++) {
				for (var j = 0; j < grid.L; j++) {
					var o = grid.M[i][j];
					if (o) o.y.setTarget(y);
				}
				y += grid.nh;
			}
			// ---- top y ----
			var y = grid.m.y.target - grid.nh;
			for (var i = grid.m.Y - 1; i >= 0; i--) {
				for (var j = 0; j < grid.L; j++) {
					var o = grid.M[i][j];
					if (o) o.y.setTarget(y);
				}
				y -= grid.nh;
			}
			// ---- right x y ----
			var y = grid.m.y.target + grid.m.h.target + grid.marg;
			for (var i = 0; i < grid.H; i++) {
				var o = grid.M[i][grid.m.X];
				if (o) {
					o.x.setTarget(grid.m.x.target);
					if (i > grid.m.Y) {
						o.y.setTarget(y);
						y += o.h.target + grid.marg;
					}
				}
			}
			// ---- left x ----
			var x = grid.m.x.target - grid.nw;
			for (var i = grid.m.X - 1; i >= 0; i--) {
				for (var j = 0; j < grid.H; j++) {
					var o = grid.M[j][i];
					if (o) o.x.setTarget(x);
				}
				x -= grid.nw;
			}
			// ---- right x ----
			var x = grid.m.x.target + grid.m.w.target + grid.marg;
			for (var i = grid.m.X + 1; i < grid.L; i++) {
				for (var j = 0; j < grid.H; j++) {
					var o = grid.M[j][i];
					if (o) o.x.setTarget(x);
				}
				x += grid.nw;
			}
		}
		// ---- start animation if not already running ----
		if (!grid.sid) grid.sid = setInterval(grid.run, 32);
	},

	/* ==== goto id ==== */
	goto : function (id) {
		if (grid.D[id]) {
			if (grid.m) grid.m.m = false;
			grid.m = grid.D[id];
			grid.position();
		}
	},

	/* ==== resize event ==== */
	resize : function () {
		var o = grid.screen.obj;
		if (o && o.offsetWidth) {
			grid.screen.h = o.offsetHeight - grid.marg;
			grid.screen.w = o.offsetWidth - grid.marg;
			// ---- step 1: DOM loaded - step 2: createHTML - step 3: resize ----
			if (!grid.init) grid.position(); else grid.structure();
		} else setTimeout(grid.resize, 32);
	},

	/* ==== main loop ==== */
	run : function () {
		// ---- divs ----
		grid.moving = false;
		var i = 0, o;
		while( o = grid.O[i++] ) o.animate();
		// ---- scroll background ----
		grid.screen.obj.style.backgroundPosition = Math.round(-grid.O[0].x.position * .5) + 'px ' + Math.round(-grid.O[0].y.position * .5) + 'px';
		// ---- stop animation if nothing is moving ----
		if (!grid.moving && grid.sid) {
			clearInterval(grid.sid);
			grid.sid = false;
		}
	}
}
