/*
 * MM service section functions.
 * Requires prototype.
 * @author Chris Lewis <chris@silentcitizen.com> 1/10/2007
 */

/* Initialize the namespace. */
var SC = {
	
	Class: function() {},
	
	SCObject: function() {}
	
}

/* Class */
SC.Class.create = function(parent) {
	var _p = (parent || SC.SCObject);
	
	function clazz() {
		if(typeof(this.init) == 'function') {
			this.init.apply(this, arguments);
		}
	}
	
	clazz.prototype = new _p();
	clazz.prototype.constructor = clazz;
	return clazz;
}
	
/* SCObject */
SC.SCObject.prototype.extend = function(parent) {
	// Apply parent's constructor to this object
	if( arguments.length > 1 ) {
		// Note: 'arguments' is an Object, not an Array
		parent.apply( this, Array.prototype.slice.call( arguments, 1 ) );
	} else {
		parent.call( this );
	}
}


/** {{{ SC.ObjectProxy class.
 * Currently this is just a collection of arbitrary objects.
 */
SC.ObjectProxy = SC.Class.create();
SC.ObjectProxy.prototype = {
	
	init: function() {
		//TODO check is set
		this._objCnt = 0;
		this._objCache = {};
	},
	
	/**
	 * Get an object.
	 * @param oid The object id.
	 * @return The object, or type 'undefined' if it doen't exist.
	 */
	getObject: function(oid) {
		return this._objCache[oid];
	},
	
	/**
	 * See if an object exists.
	 * @param oid The object id.
	 * @return True if it exists, false if not. 
	 */
	hasObject: function(oid) {
		return typeof(this._objCache[oid]) != 'undefined';
	},
	
	/**
	 * Add an object.
	 * @param obj The object to add.
	 * @param oid The id.
	 */
	addObject: function(obj, oid) {
		this._objCache[oid] = obj;
		this._objCnt++;
		//alert('added obje; new size: ' + this.size());
	},
	
	size: function() {
		return this._objCnt;
	}
	
}
/* }}} */


/** {{{ SC.Transitioner base class.
 * SC.Transitioner is the base service transitioner.
 * A Transitioner object is responsible for the actual rendering of a
 * service body. This includes modifying the DOM.
 *
 * The default transitioner simply removes the old element from the document,
 * and then inserts the new one.
 */
SC.Transitioner = SC.Class.create();
SC.Transitioner.prototype = {
	
	/* Flag showing if the transitioning operation is in progress. */
	_transitioning: false,
	
	/* Internal element cache. */
	_cache: {},
	
	/* The currently rendered element. */
	_renderedE: null,
	
	/**
	 * Check if this transitioner is currently transitioning.
	 * @return bool True or false, according to the trasitioning state.
	 * @access public
	 */
	isTransitioning: function() {
		return this._transitioning;
	},
	
	/**
	 * Check to see if an element is cached.
	 * @param TODO
	 * @return bool True if cached, false if not.
	 */
	isCached: function(e) {
		return typeof(this._cache[e.id]) != 'undefined';
	},
	
	/**
	 * Check if an element is currently rendered.
	 */
	isRendered: function(e) {
		return e == (this._renderedE || false);
	},
	
	/**
	 * Render the new element in place of the old one.
	 * @param HTMLElement parent The containing element.
	 * @param HTMLElement from The old element.
	 * @param HTMLElement to The new element.
	 * @access public
	 */
	transition: function(from, to, trigger) {
		this._transitioning = true;
		//Create a new parent.
		var newP = from.parentNode.cloneNode(false);
		//Append the new child to the new parent.
		newP.innerHTML = '<' + to.tagName + ' style="visibility:hidden;">' + to.innerHTML + '</' + to.tagName + '>';
		newP.appendChild(to);
		from.parentNode.parentNode.replaceChild(newP, from.parentNode);
		this._transitioning = false;
	}
} /* }}} */


/** {{{ SC.FadeTransitioner class.
 * A script.aculo.us-based cross fading transitioner.
 */
//SC.FadeTransitioner = function() /*SC.FadeTransitioner()*/ {}
//SC.FadeTransitioner.prototype = new SC.Transitioner();
SC.FadeTransitioner = SC.Class.create(SC.Transitioner);
SC.FadeTransitioner.prototype.transition = function(from, to, trigger) {
	this._transitioning = true;
	
	//Set up the new parent with it's child.
	to.style.display = 'none';	
	/*var newP = from.parentNode.cloneNode(false);
	//Insert invisible content as the innerHTML since this will force the
	//container to expand to size.
	newP.innerHTML = '<' + to.tagName + ' style="visibility:hidden;">' + to.innerHTML + '</' + to.tagName + '>';
	newP.appendChild(to);*/
		
	//Fade the old child from the old parent.
	Effect.Fade(from, { duration: .3 });
	var that = this;
	var _pe = new PeriodicalExecuter(
		function() {
			_pe.stop();
			//Replace old parent with new parent.
			//from.parentNode.parentNode.replaceChild(newP, from.parentNode);
			from.parentNode.replaceChild(to, from);
			Effect.Appear(to, { duration: .3 });
			_pe = new PeriodicalExecuter(
				function() {
					_pe.stop();
					//trigger.style.backgroundColor = '#e5d340';
					that._renderedE = trigger;
					that._transitioning = false;
				}, .3);
		}, .3);
	
	/*
	//Set up the new parent with it's child.
	to.style.display = 'none';	
	var newP = from.parentNode.cloneNode(false);
	//Insert invisible content as the innerHTML since this will force the
	//container to expand to size.
	newP.innerHTML = '<' + to.tagName + ' style="visibility:hidden;">' + to.innerHTML + '</' + to.tagName + '>';
	newP.appendChild(to);
	//Fade the old child from the old parent.
	Effect.Fade(from, { duration: .3 });
	var that = this;
	var _pe = new PeriodicalExecuter(
		function() {
			_pe.stop();
			//Replace old parent with new parent.
			from.parentNode.parentNode.replaceChild(newP, from.parentNode);
			Effect.Appear(to, { duration: .3 });
			_pe = new PeriodicalExecuter(
				function() {
					_pe.stop();
					that._renderedE = trigger;
					that._transitioning = false;
				}, .3);
		}, .3);
		*/
	
} /* }}} */


/** {{{ SC.ElementTransitionManager class.
 * TODO doc
 * TODO use SC.Class.create();
 */
 SC.ElementTransitionManager = function(srcEClass, elementId, dynElementId, urlProvider) {
	this._serviceElementClass = srcEClass;
	this._serviceBodyElement = elementId;
	this._dynElementId = dynElementId;
	this.urlProvider = urlProvider ? urlProvider : null;
	this._initServices();
}
SC.ElementTransitionManager.prototype = {
	
	/* The DOM class of which elements representing service links are of. */
	_serviceElementClass: '',
	
	/* The DOM ID of the element in which we'll insert content. */
	_serviceBodyElement: '',
	
	_transitioner: new SC.FadeTransitioner(),
	
	_dynElementId: null,
	
	/**
	 * Render the body of a service
	 * @param srcE TODO
	 * @access public
	 */
	renderService: function(srcE) {
		//Abort if the transitioner is working or the service is already rendered.
		if(this._transitioner.isTransitioning() || this._transitioner.isRendered(srcE)) {
			return false;
		}
		
		var that = this;
		var opts = {
			method: 'get',
			onSuccess: function(response) {
				that._insertServiceBody(srcE, response.responseText);
			}
		};
		
		if(! this.urlProvider) {
			//Can't continue...
			return;
		}
		
		//DEP prototype (Ajax)
		var myAjax = new Ajax.Request(this.urlProvider.provide(srcE), opts);
	},
	
	/**
	 * Load services by a class name. This means getting the target
	 * elements and attaching click handlers to fetch their contents.
	 * @access private
	 */
	_initServices: function(srcEClass, elementId) {
		var that = this;
		var srcEs = document.getElementsByClassName(this._serviceElementClass);
		for(i = 0; i < srcEs.length; i++) {
			//DEP prototype (Event)
			Event.observe(srcEs[i], 'click',
				function(event) {
					Event.stop(event);
					that.renderService((Event.element(event)));
				}
			);
		}
	},
	
	/**
	 * Insert the service body into the element.
	 * @param HTMLElement srcE The triggering element.
	 * @param string respBody The service body.
	 * @access private
	 */
	_insertServiceBody: function(srcE, respBody) {
		//DEP prototype $()
		//Find the old child container.
		var _c = $(this._dynElementId);
		//Create a new child by cloning the old one and giving it fresh content.
		var c = _c.cloneNode(false);
		c.innerHTML = respBody + '<div class="clr"></div>';
		this._transitioner.transition(_c, c, srcE);
	}
	
} /* }}} */


/* {{{ SC.FetchAndFollowerManager class. */
SC.FetchAndFollowerManager = SC.Class.create();
SC.FetchAndFollowerManager.prototype = {
	
	/* SC constructor. */
	init: function(eSrcClass, objectProxy) {
		this._eSrcClass = eSrcClass;
		this._objectProxy = objectProxy || null;
		
		this._initTriggerElements();
		//TODO supply this
		this._followingEid = 'sc-fetch-follower';
		this._followingE = null;
	},
	
	/* Attach mouse events. */
	_initTriggerElements: function() {
		var that = this;
		var srcEs = document.getElementsByClassName(this._eSrcClass);
		for(i = 0; i < srcEs.length; i++) {
			//DEP prototype (Event)
			Event.observe(srcEs[i], 'mouseover',
				function(event) {
					/*
					 * Fetch the client testimonial via Ajax call and render.
					 */
					var srcE = Event.element(event);
					var point = {
						x: Event.pointerX(event),
						y: Event.pointerY(event)
					};
					
					//TODO need flexible way to get an id and url
					var cid = srcE.id.substring('client-image-'.length);
					if(that._objectProxy.hasObject(cid)) {
						that._renderFollower(point, srcE, cid);//XXX
					} else {
						var opts = {
							method: 'get',
							onSuccess: function(response) {
								//Cache the new content.
								var obj = eval('(' + response.responseText + ')');
								
								that._objectProxy.addObject(obj, obj.id);
								that._renderFollower(point, srcE, cid);//XXX
							}
						};
						//DEP prototype (Ajax)
						var myAjax = new Ajax.Request('/clients/testimonial/' + cid, opts);
					}
				}
			);
			
			Event.observe(srcEs[i], 'mousemove',
				function(event) {
					/*
					 * If the testimonial has been successfully retrieved then
					 * re-position it according to the current mouse pointer
					 * coords. Create a 'following element.'
					 */
					if(that._followingE == null) { return; }
					var point = {
						x: Event.pointerX(event),
						y: Event.pointerY(event)
					};
					var y = point.y - Element.getHeight(that._followingE) - 15;
					//Position element.
					that._followingE.setStyle({left: point.x + 'px', top: y + 'px'});
				}
			);
			
			Event.observe(srcEs[i], 'mouseout',
				function(event) {
					/*
					 * If the testimonial has been successfully retrieved and
					 * rendered, then destroy it. 
					 */
					if(that._followingE == null) { return; }
					that._followingE.parentNode.removeChild(that._followingE);
					that._followingE = null;
				}
			);
			
		}
	},
	
	_renderFollower: function(point, srcE, oid) {
		var obj = this._objectProxy.getObject(oid);
		//Create a new element.
		var newFollower = document.createElement('div');
		newFollower.id = this._followingEid;
		//TODO
		newFollower.innerHTML = "<img src='/img/company/client-bubble-top.gif' alt='top'/><div>" + obj.testimonial + '</div><img src="/img/company/client-bubble-bottom.gif" alt="bottom"/>';
		//Position and show it.
		if(this._followingE != null) {
			//this._followingE.parentNode.removeChild(this._followingE);
			this._followingE.parentNode.replaceChild(newFollower, this._followingE);
		} else {
			document.body.appendChild(newFollower);
		}
		this._followingE = newFollower;
		var y = point.y - Element.getHeight(this._followingE) - 15;
		this._followingE.setStyle({left: point.x + 'px', top: y + 'px'});
	}
	
}
/* }}} */


/* this doesnt belong here */
SC.MmClientDetailsManager = SC.Class.create();
SC.MmClientDetailsManager.prototype = {
	
	init: function(srcClass, objectProxy) {
		this._eSrcClass = srcClass;
		this._objectProxy = objectProxy || null;
		this._initTriggerElements();
		
		/*Event.observe($('client-details'), 'click',
			function(event) {
				Event.stop(event);
				Effect.Fade($('client-details'), { from: .90, to: .0, duration: .3 });
			}
		);*/
	},
	
	renderClientDetails: function(srcE) {
		var cid = srcE.id.substring('client-name-id-'.length);
		var that = this;
		//alert('dis');
		if(this._objectProxy.hasObject(cid)) {
			this._renderDetails(cid);
		} else {
			var opts = {
				method: 'get',
				onSuccess: function(response) {
					var obj = eval('(' + response.responseText + ')');
					//alert(response.responseText);
					that._objectProxy.addObject(obj, obj.id);
					that._renderDetails(cid);
				}
			};
			//DEP prototype (Ajax)
			//TODO need flexible way to get an id and url
			var myAjax = new Ajax.Request('/clients/testimonial/' + cid, opts);
		}
	},
	
	_initTriggerElements: function() {
		var that = this;
		var srcEs = document.getElementsByClassName(this._eSrcClass);
		for(i = 0; i < srcEs.length; i++) {
			//DEP prototype (Event)
			Event.observe(srcEs[i], 'click',
				function(event) {
					Event.stop(event);
					that.renderClientDetails((Event.element(event)));
				}
			);
		}
	},
	
	_renderDetails: function(oid) {
		//Lots of clean up to do here.
		var obj = this._objectProxy.getObject(oid);
		var de = $('client-details');
		var closer = '<br/><div><a style="font-weight:bold;cursor:pointer;" onclick="Effect.Fade($(\'client-details\'), { from: .90, to: .0, duration: .3 });">Back to Client List</a></div>';
		de.innerHTML = '<img style="float:left;" src="' + obj.img + '"/>';
		de.innerHTML += '<div style="margin-left:205px;padding:10px;">' + obj.testimonial + closer + '</div>';
		Effect.Appear(de, { from: .0, to: .90, duration: .3 });
	}
	
}
/* }}} */

