function PrepareObject(target, button) {
	this.target = target;
	this.button = button;
	
	this.target.style.overflow = "hidden";
	if (!this.target.style.height) {
	   this.target.style.height = this.target.scrollHeight + "px";
	}
}

function Expandable(startFrom) {
	this.FIELDS_PREFIX = "expand_target_";
	this.BUTTONS_PREFIX = "expand_button_";	
	this.BOTTOMS_PREFIX = null;
	this.MIN_ELEMENT_HEIGHT = 1;
	this.OPERATION_INTERVAL = 10;
	this.EXPAND_INTERVAL = 15;
	this.COLLAPSE_INTERVAL = 15;
	this.BOUNCE_INTERVAL = 50;
	this.BOUNCE_DISTANCE = 5;
	
	this.openedField = null;
	this.startFrom = startFrom;
	this.collection = null;
	this.processInProgress = null;
	this.collapseWaiter = null;	
	this.bounceWaiter = null;
	this.synchronize = true;
    this.bounce = true;
	this.bounceLoops = 0;
	
	this.callbackFunction = null;		
	
	this.getExpandableElements = function(open) {
		var open = (arguments[0] == true) ? true : false,
			i = this.startFrom,
			keepGoing = true;
		this.collection = new Array();
		
		while(keepGoing) {	
			if (document.getElementById(this.FIELDS_PREFIX + i)) {			
				if (this.BOTTOMS_PREFIX != null) {
					this.collection.push(
						new PrepareObject(
							document.getElementById(this.BOTTOMS_PREFIX + i),
							null
						)
					);
				}
				this.collection.push(
					new PrepareObject(
						document.getElementById(this.FIELDS_PREFIX + i),
						document.getElementById(this.BUTTONS_PREFIX + i)
					)
				);
				this.attachEvent(this.collection.length-1);
				
				if (!open) {
					if (parseInt(document.getElementById(this.FIELDS_PREFIX + i).style.height) != this.MIN_ELEMENT_HEIGHT-1) {
					    document.getElementById(this.FIELDS_PREFIX + i).style.height = this.MIN_ELEMENT_HEIGHT-1 + "px";
					}
				}
				i++;
			} else {
				keepGoing = false;
			}
		}
	};
}
Expandable.prototype.attachEvent = function(id) {
	var obj = this;
	this.collection[id].button.onclick = function(e) {
		obj.onClickEvent(id);		
	};
};
Expandable.prototype.onClickEvent = function(id) {
    if (!this.processInProgress) {
    	
    	this.close(this.openedField, 1);    	
		var haveToOpen = parseInt(this.collection[id].target.style.height) == this.MIN_ELEMENT_HEIGHT-1;
		
		if (haveToOpen) {
			var obj = this,
				operationWaiting = setInterval(function() {
				if (obj.BOTTOMS_PREFIX != null) {
					if (id != null) {
						obj.open(id-1, 1);
					}
				} else {
					obj.open(id, 1);
					if (obj.synchronize) obj.openedField = id;
				}
				clearInterval(operationWaiting);
			}, obj.OPERATION_INTERVAL);
		} else if (!haveToOpen) {
			this.close(id, 1);
			this.openedField = null;
		}
	}	
}
Expandable.prototype.close = function(id, iter) {	
	if (id != null) {		
		this.processInProgress = true;
		var obj = this;
		
		clearInterval(this.collapseWaiter);		
		
		this.collapseWaiter = setInterval(function() {
			if (parseInt(obj.collection[id].target.style.height) > obj.MIN_ELEMENT_HEIGHT) {
				obj.collection[id].target.style.height = 
					Fibonacci.GET_PREV(parseInt(obj.collection[id].target.style.height)) + "px";
			} else {								
				obj.collection[id].target.style.height = obj.MIN_ELEMENT_HEIGHT-1 + "px";
				obj.processInProgress = false;
				clearInterval(obj.collapseWaiter);	
				              
				if (obj.callbackFunction != null) {
		            obj.callbackFunction(id);
		        }				               
			}			
		}, this.COLLAPSE_INTERVAL);
	}
};
Expandable.prototype.open = function(id, iter) {
	if (id != null) {
		this.processInProgress = true;
		
		clearInterval(this.expandWaiter);

		var obj = this,
			someTimeForIEToStart = setInterval(function () {
			var scrollHeight = parseInt(obj.collection[id].target.scrollHeight);
			
			obj.expandWaiter = setInterval(function() {
				if (parseInt(obj.collection[id].target.style.height) < scrollHeight) {
				    var newHeight = Fibonacci.GET_NEXT(parseInt(obj.collection[id].target.style.height));
				    newHeight = (newHeight > scrollHeight)? scrollHeight : newHeight; 
					obj.collection[id].target.style.height = newHeight + "px";
				} else {               
                    clearInterval(obj.expandWaiter);                    
                    
                    if (obj.callbackFunction != null) {
                        obj.callbackFunction(id);
                    }
                    if (obj.bounce) {               
                    	obj._bounce(id);
                    } else {
                    	obj._finishOpenProcess(id);
                    }
				}
			}, obj.EXPAND_INTERVAL);
			
			clearInterval(someTimeForIEToStart);
		}, this.EXPAND_INTERVAL);
	}
};
Expandable.prototype._bounce = function(id) {
    this.bounceLoops = 0;
    var obj = this;
    
    this.bounceWaiter = setInterval(function() {
        if (obj.bounceLoops >= 1) {
            obj._finishOpenProcess(id);
            clearInterval(obj.bounceWaiter);
        } else {
            obj.collection[id].target.style.height = (parseInt(obj.collection[id].target.style.height) - obj.BOUNCE_DISTANCE) + "px";
            obj.bounceLoops++;
        }
    }, this.BOUNCE_INTERVAL);
};
Expandable.prototype._finishOpenProcess = function(id) {
    var scrollHeight = parseInt(this.collection[id].target.scrollHeight);
    this.collection[id].target.style.height = scrollHeight + "px";
    this.processInProgress = false;
};
Expandable.prototype.callFunction = function(id) {
	var button = this.collection[id].button;
	if (button.className == "service_edit") {
		button.className = "service_manips";
	} else if (button.className == "service_manips") {
		button.className = "service_edit";
	}
};