function Movement (step, posEvnt, negEvnt) {
	/*
	 * private properties
	 */
	var self = this;
	var posEvent = posEvnt;
	var negEvent = negEvnt;

	var items = [];
	var id = false;

	// moves all linked objects
	var move = function() {
		var moved = false;

		for (var i = 0; i < items.length; i++) {
			moved |= items[i].move(self.range);
		}

		if (!moved) {
			self.stop();
		}
	};


	/*
	 * public properties
	 */
	this.range = step;


	/*
	 * public methods
	 */
	// starts movement
	this.start = function() {
		if (id) {
			window.clearInterval(id);
		}

		id = window.setInterval(move, 40);
	};

	// stops movement
	this.stop = function() {
		window.clearInterval(id);
		id = false;
	};

	// returns a linked object
	this.get = function (index) {
		return items[index];
	};

	// links an object
	this.add = function (item) {
		if (!item.move) {
			return false;
		}

		var index = items.length;

		if (!negEvent || posEvent == negEvent) {
			item.element[posEvent] = function() {
				self.get(index).forward(item.isFolded());
				self.start();
				return false;
			};
		}
		else {
			item.element[posEvent] = function() {
				self.get(index).forward(true);
				self.start();
			};
			
			item.element[negEvent] = function() {
				self.get(index).forward();
				self.start();
			};
		}
		
		items.push(item);
		return true;
	};

	// unlinks an object
	this.remove = function (item) {
		var index;

		for (var i = 0; i < items.length; i++) {
			if (items[i] === item) {
				index = i;
				break;
			}
		}

		if (index) {
			items[index].element[posEvent] = function() {};
			items[index].element[negEvent] = function() {};
			items.splice(index, 1);

			return true;
		}
		return false;
	};
}

function Movable (obj, property, minDim, iniDim, maxDim, level) {
	/*
	 * private properties
	 */
	var self = this;
	var dir = 0;
	var value = iniDim;
	var affected;

	/*
	 * public properties
	 */
	this.element = obj;
	this.style = property.split(':')[0];
	this.unit = property.split(':')[1];
	this.minValue = minDim;
	this.maxValue = maxDim;

	/*
	 * private functions
	 */
	// constructor
	function init()	{
		if (!self.unit) {
			self.unit = '';
		}
		
		if (!level) {
			level = 0;
		}

		if (level > 0) {
			for (var i = 0; i < level && obj.hasChildNodes(); i++) {
				obj = obj.firstChild;
				
				while (obj.nextSibling && !obj.style) {
					obj = obj.nextSibling;
				}
				
				if (!obj.style) {
					obj = obj.parentNode;
					break;
				}
			}
		}
		else {
			for (var i = 0; i > level && obj.parentNode; i--) {
				obj = obj.parentNode;
			}
		}

		self.affected = obj;
		self.setValue(value);
	}

	/*
	 * public methods
	 */
	// returns direction
	this.isFolded = function() {
		return (self.value < this.maxValue) && !(self.dir > 0);
	}
	 
	// specifies direction of movement
	this.forward = function (yes) {
		self.dir = (yes) ? 1 : -1;
	};

	// sets value of style
	this.setValue = function (val) {
		var overflow = false;

		self.value = val;

		if (self.value < self.minValue) {
			self.value = self.minValue;
			overflow = true;
		}
		else if (self.value > self.maxValue) {
			self.value = self.maxValue;
			overflow = true;
		}

		self.affected.style[self.style] = self.value + self.unit;
		return overflow;
	};

	// modifies style by one step
	this.move = function (range) {
		if (!self.dir) {
			return false;
		}

		if (self.setValue(self.value + self.dir * range)) {
			self.dir = 0;
		}

		return true;
	};

	/*
	 * construction
	 */
	init();
}