﻿/**
 * Usage:
 * dynamicContent="{param}, {param}, ... , {param}"
 * or
 * GScript.DynamicContent.call("{param}, {param}, ... , {param}");
 *
 * param:
 * - target (string) -- the target layer to load the source
 * - source (string) -- the source to be loaded
 * - element (string) -- the element of the source to be loaded
 * - appear (number) -- effect duation
 * - formData (string) -- use for sending form data
 * - canAppendInfoToURL (boolean) -- use for cracking cache problem 
 */
GScript=Object.extend({
	DynamicContent: null
}, window.GScript || {});

var DynamicContent=Class.create({
	prefixBG: '__DynamicContent_Background',
	prefixContent: '__DynamicContent_Content_',
	prefixLoading: '__DynamicContent_Loading_',
	prefixTmpLayer: '__DynamicContent_TmpLayer',
	
	targetLayerCode: [],
	
	defaultLoadingLayerCSS: 'background-color: #000000; background-position: center center; background-repeat: no-repeat; background-image: url(\'img/loading/black.gif\');',
	defaultEncoding: 'utf-8',
	defaultAppearDuration: 0.5,

	clickListener : null,

	initialize: function(){
		if(!this.clickListener){
			this.clickListener = (function(event){
				var param = this.getParam(Event.element(event), 'dynamicContent', true);
		        if(param != null){
		        	event.stop();
		        	this.update(param);
		        }
			}).bind(this);
			document.observe('click', this.clickListener);
		}
	},
	
	destory: function(){
		document.stopObserving('click', this.clickListener);
		this.clickListner = null;
	},
	
	getParam: function(trigger, keyword, propagation){
		if(propagation){
			var param = this.getParam(trigger, keyword, false);
			if(param){
				return param;
			}
			else{
				var trigger = trigger.ancestors();
				for(var i=0; i<trigger.length; i++){
					param = this.getParam(trigger[i], keyword, false);
					if(param){
						return param;
					}
				}
				return null;
			}
	 	}
	 	else{
			var param = trigger.readAttribute(keyword);
			return param == null || param.empty() ? null : this.constructArrayParam(param, trigger.readAttribute("href"));
		}
	},
	 
	constructArrayParam: function(param, source){
		return Try.these(
			(function(){
				var paramArray = "<script>[" + param + "]</script>";
				paramArray = paramArray.evalScripts()[0];
				for(var i=0; i<paramArray.length; i++){
					paramArray[i].source = paramArray[i].source || source;
					paramArray[i].encoding = paramArray[i].encoding || this.defaultEncoding;
					paramArray[i].appear = paramArray[i].appear == null ? this.defaultAppearDuration : paramArray[i].appear;
					
					// reformat the source link based on the form data
					if($(paramArray[i].formData) != null){
						options = $(paramArray[i].formData).serialize();
						if(paramArray[i].source.indexOf("?") > 0){
							paramArray[i].source += "&" + options;
						}
						else{
							paramArray[i].source += "?" + options;
						}
					}
				}
				return paramArray;
			}).bind(this)
		) || null;
	},
	
	call: function(param, trigger){
		this.update(this.constructArrayParam(param, $(trigger) == null ? null : $(trigger).readAttribute("href")));
	},
	
	update: function(param){
		if(param != null){
			var loadingSource = [];
			for(var i=0; i<param.length; i++){
				// run the beginning script
				this.evalScriptInternal(param[i].beginScript, null, 0);
			
				// fade out the layer. no need to display anything and no need for AJAX request
				if(param[i].source == null || param[i].source == '__fade__' || param[i].element == '__fade__'){
					this.createLayer(param[i]);
					$(param[i].target).setStyle({height: $(param[i].target).getStyle('height')});
					$(param[i].target).setStyle({width: $(param[i].target).getStyle('width')});
					
					Utility.setOpacity(this.prefixContent + param[i].target, {duration: param[i].appear, to: 0});
					Utility.setOpacity(this.prefixLoading + param[i].target, {duration: param[i].appear, to: 0});
					
					if(param[i].width || param[i].height){
						Utility.setSize(this.prefixContent + param[i].target, {width: param[i].width, height: param[i].height, duration: param[i].appear});
					}
					else{
						Utility.setSize(this.prefixContent + param[i].target, {width: 0, height: 0, duration: param[i].appear});
					}
					
					this.evalScriptInternal('$(this.prefixContent + \'' + param[i].target + '\').update(); this.targetLayerCode[\'' + param[i].target + '\'] = null;', null, param[i].appear*1000 + 100);
					this.evalScriptInternal(param[i].finishScript, null, param[i].appear*1000 + 100);
					
					param[i].source = "";
				}
				
				if(param[i].source && !param[i].source.empty()){
					var loaded = false;
					for(var k=0; k<loadingSource.length; k++){
						if(loadingSource[k] == (param[i].source + "#" + param[i].encoding)){
							loaded = true;
							break;
						}
					}
					if(!loaded){
						loadingSource.push(param[i].source + "#" + param[i].encoding);
						this.loadSource(param[i].source, param[i].encoding, param);
					}
				}
			} // end for
		}
	},
	
	targetCode: function(param){
		return param.source + "#" + param.element + "#" + param.random + "#" + param.lastActionTime;
	},
	
	createLayer:function(param){
		if(!$(this.prefixContent + param.target)){
			$(param.target).update(
				'<div id="' + this.prefixContent + param.target + '" style="position:relative; top: 0px; left: 0px;">' + $(param.target).innerHTML + '</div>' +
				'<div id="' + this.prefixLoading + param.target + '" style="display: none; position:absolute; top: 0px; left: 0px; ' +
				(param.className == null ? this.defaultLoadingLayerCSS + '"' : '" class="' +　param.className + '"') + '></div>'
			);
		}
		else{
		
		}
	},
	
	/**
	 * load the source, and set the target(s) to the source element
	 */
	loadSource: function(source, encoding, param){
		var numToUpdate = 0;
		for(var i=0; i<param.length; i++){
			if(param[i].source == source){
				var isFirstClick = this.targetLayerCode[param[i].target] == null;
				
				param[i].lastActionTime = (new Date()).getTime();
				param[i].random = Math.round(Math.random() * 100000);
				this.targetLayerCode[param[i].target] = this.targetCode(param[i]);

				// no effect is applying on this layer now
				if(isFirstClick){
					this.createLayer(param[i]);
					$(param[i].target).setStyle({height: $(param[i].target).getStyle('height')});
					$(param[i].target).setStyle({width: $(param[i].target).getStyle('width')});

					// left corner 
					if(param[i].effect == 'leftCorner'){
						var effect = 'leftCorner';
						$(this.prefixLoading + param[i].target).setStyle({width: '0px', height: '0px'});
						$(this.prefixLoading + param[i].target).setOpacity(1);
					}

					// top
					else if(param[i].effect == 'top'){
						var effect = 'top';
						$(this.prefixLoading + param[i].target).setStyle({width: $(param[i].target).getStyle('width'), height: '0px'});
						$(this.prefixLoading + param[i].target).setOpacity(1);
					}

					// left
					else if(param[i].effect == 'left'){
						var effect = 'left';
						$(this.prefixLoading + param[i].target).setStyle({width: '0px', height: $(param[i].target).getStyle('height')});
						$(this.prefixLoading + param[i].target).setOpacity(1);
					}

					// fade 
					else{
						var effect = 'fade';
						$(this.prefixLoading + param[i].target).setOpacity(0);
						$(this.prefixLoading + param[i].target).setStyle({width: $(param[i].target).getStyle('width'), height: $(param[i].target).getStyle('height')});
					}
					
					$(this.prefixLoading + param[i].target).writeAttribute('effect', effect);
				}
				
				else{
					Utility.stopSetSize(param[i].target);
					var effect = $(this.prefixLoading + param[i].target).readAttribute('effect');
				}
				
				$(this.prefixLoading + param[i].target).show();
				
				if(effect == 'fade'){
					Utility.setOpacity(this.prefixLoading + param[i].target, {duration: param[i].appear, to: 1});
				}
					
				else if(effect == 'leftCorner'){
					Utility.setSize(this.prefixLoading + param[i].target, {width: $(param[i].target).getStyle('width'), height:  $(param[i].target).getStyle('height'), duration: param[i].appear});
				}
				
				else if(effect == 'top'){
					Utility.setSize(this.prefixLoading + param[i].target, {width: $(param[i].target).getStyle('width'), height:  $(param[i].target).getStyle('height'), duration: param[i].appear});
				}
				
				else if(effect == 'left'){
					Utility.setSize(this.prefixLoading + param[i].target, {width: $(param[i].target).getStyle('width'), height:  $(param[i].target).getStyle('height'), duration: param[i].appear});
				}
				
				numToUpdate++;
			} // end if 
		} // end for i
		
		var requestTime = (new Date()).getTime();
		
		if(Utility.isImgLink(source)){
			this.setImg(source, param, numToUpdate);
		}
		
		else if(Utility.isFlashLink(source)){
			this.setIFrame(source, param, numToUpdate);
		}
		
		else{
			this.setExternalHTML(source, encoding, param, numToUpdate);
		}
	},

	setImg: function(source, param, numToUpdate){
		var tmpLayer = this.prefixTmpLayer + (new Date()).getTime() + Math.round(Math.random() * 1000000);
		$$('body')[0].appendChild(Builder.node('div', {id: tmpLayer, style:'display: none; position:absolute; top:0px; left:0px;'}));
		$(tmpLayer).update('<img src="' + source + '" border="0" />');
		this.loadElement(tmpLayer, source, param, numToUpdate, 'img');
	},
	
	setExternalHTML: function(source, encoding, param, numToUpdate){
		var formatedSource = param.canAppendInfoToURL != "false" && param.canAppendInfoToURL != false ? (source.indexOf("?") < 0 ? source + "?lastClickTime=" + (new Date()).getTime() : source + "&lastClickTime=" + (new Date()).getTime()) : source;
		new Ajax.Request(formatedSource, {
			method: 'get',
			encoding: encoding,
			onSuccess: (function(message){
				var tmpLayer = this.prefixTmpLayer + (new Date()).getTime() + Math.round(Math.random() * 1000000);
				$$('body')[0].appendChild(Builder.node('div', {id: tmpLayer, style:'display: none; position:absolute; top:0px; left:0px;'}));
				$(tmpLayer).update(message.responseText);
				// alert(message.responseText);
				//	message.responseText.evalScripts();
				this.loadElement(tmpLayer, source, param, numToUpdate, 'html');
			}).bind(this),
			onFailure: (function(message){
				var tmpLayer = this.prefixTmpLayer + (new Date()).getTime() + Math.round(Math.random() * 1000000);
				$$('body')[0].appendChild(Builder.node('div', {id: tmpLayer, style:'display: none; position:absolute; top:0px; left:0px;'}));
				$(tmpLayer).update("<b>Error 404</b>: the requested URL is not found!");
				//	message.responseText.evalScripts();
				this.loadElement(tmpLayer, source, param, numToUpdate, 'html');
			}).bind(this),
			onException: (function(message){
			}).bind(this)
		});
	},
	
	setIFrame: function(source, param, numToUpdate){
		var tmpLayer = this.prefixTmpLayer + (new Date()).getTime() + Math.round(Math.random() * 1000000);
		$$('body')[0].appendChild(Builder.node('div', {id: tmpLayer, style:'display: none; position:absolute; top:0px; left:0px;'}));
		$(tmpLayer).update('<iframe src="' + source + '" marginwidth="1" marginheight="0" frameborder="0" scrolling="no" style="margin: 0px;"></iframe>');
		this.loadElement(tmpLayer, source, param, numToUpdate, 'iframe');
	},
	
	loadElement: function(tmpLayer, source, param, numToUpdate, mediaType){
		new PeriodicalExecuter((function(pe){
			var currentTime = (new Date()).getTime();
			for(var i=0; i<param.length; i++){
				if(param[i].source == source && !param[i].executed){
					if((param[i].appear * 1000 + param[i].lastActionTime) < currentTime){
						param[i].executed = true;
						if(this.targetLayerCode[param[i].target] == this.targetCode(param[i])){
							if(param[i].element == null){
								this.resizeAndShow($(tmpLayer).innerHTML, param[i], mediaType);
							}
							
							else{
								var descendants = $(tmpLayer).descendants();
								var found = false;
								for(var j=0; j<descendants.length; j++){
									if(descendants[j].identify() == param[i].element){
										this.resizeAndShow(descendants[j], param[i], mediaType);
										found = true;
										break;
									}
								} // end for j
								
								if(!found){
									this.resizeAndShow("<b>Error:</b> Source layer not found!", param[i], mediaType);
								}
							}
						}
						
						if(--numToUpdate == 0){
							pe.stop();
							$(tmpLayer).remove();
						}
					}
				}
			}
		}).bind(this), 0.2);
	},
	
	resizeAndShow: function(content, param, mediaType){
		if(this.targetLayerCode[param.target] == this.targetCode(param)){
			$(this.prefixContent + param.target).setStyle({position: 'absolute'});
			$(this.prefixContent + param.target).setOpacity(0);
			if(mediaType == 'iframe'){
				$(this.prefixContent + param.target).hide();
			}
			$(this.prefixContent + param.target).update(content);

			// check all images are loaded. Try 150 times for 0.2 seconds each period.
			var trial = 0, maxTrial = 150;
			new PeriodicalExecuter((function(pe){
				if(this.targetLayerCode[param.target] != this.targetCode(param)){
					pe.stop();
				}
				else if(Utility.isAllImgLoaded($(this.prefixContent + param.target)) || ++trial == maxTrial){
					pe.stop();
					
					if(mediaType == 'iframe'){
						if(param.width){
							$(this.prefixContent + param.target).childElements()[0].setStyle({width: param.width});
						}
						else{
							$(this.prefixContent + param.target).childElements()[0].setStyle({width: $(param.target).getStyle('width')});
						}
					
						if(param.width){
							$(this.prefixContent + param.target).childElements()[0].setStyle({height: param.height});
						}
						else{
							$(this.prefixContent + param.target).childElements()[0].setStyle({height: $(param.target).getStyle('height')});
						}
						
					}
						$(this.prefixContent + param.target).getStyle('width');
					
					// resize
					if(param.width){
						$(this.prefixContent + param.target).setStyle({width: param.width});
					}
					else{
						$(this.prefixContent + param.target).setStyle({width: 'auto'});
					}
					if(param.height){
						$(this.prefixContent + param.target).setStyle({height: param.height});
					}
					else{
						$(this.prefixContent + param.target).setStyle({height: 'auto'});
					}

					$(this.prefixLoading + param.target).setStyle({width: '100%', height: '100%'});
					Utility.setSize(param.target, {width: $(this.prefixContent + param.target).getStyle('width'), height: $(this.prefixContent + param.target).getStyle('height'), duration: param.appear});
					
					new PeriodicalExecuter((function(pe){
						if(Utility.setSizeState(param.target) == "finished"){
							pe.stop();
							if(this.targetLayerCode[param.target] == this.targetCode(param)){
								$(this.prefixContent + param.target).setStyle({position: 'relative'});
								$(this.prefixContent + param.target).show();
								$(this.prefixContent + param.target).setOpacity(1);
							
								// corner effect
								if(param.effect == 'leftCorner'){
									$(this.prefixLoading + param.target).writeAttribute('effect', 'leftCorner');
									var dimension = $(param.target).getDimensions();
									$(this.prefixLoading + param.target).setStyle({width: dimension.width + 'px', height: dimension.height + 'px'});
									Utility.setSize(this.prefixLoading + param.target, {width: '0px', height: '0px', duration: param.appear});
								}
								
								else if(param.effect == 'top'){
									$(this.prefixLoading + param.target).writeAttribute('effect', 'top');
									var dimension = $(param.target).getDimensions();
									$(this.prefixLoading + param.target).setStyle({width: dimension.width + 'px', height: dimension.height + 'px'});
									Utility.setSize(this.prefixLoading + param.target, {width: dimension.width + 'px', height: '0px', duration: param.appear});
								}
								
								else if(param.effect == 'left'){
									$(this.prefixLoading + param.target).writeAttribute('effect', 'left');
									var dimension = $(param.target).getDimensions();
									$(this.prefixLoading + param.target).setStyle({width: dimension.width + 'px', height: dimension.height + 'px'});
									Utility.setSize(this.prefixLoading + param.target, {width: '0px', height: dimension.height + 'px', duration: param.appear});
								}
								
								// fade effect
								else{
									$(this.prefixLoading + param.target).writeAttribute('effect', 'fade');
									Utility.setOpacity(this.prefixLoading + param.target, {duration: param.appear , to: 0, hide: true});
								}
								
								this.finalProcess(param);
							}
						}
					}).bind(this), 0.2);
				}
			}).bind(this), 0.2);
		}
	},
	
	finalProcess: function(param){
		setTimeout((function(){
			if(this.targetLayerCode[param.target] == this.targetCode(param)){
				this.evalScriptInternal(param.finishScript, param, 0);
//				$(param.target).setStyle({width: 'auto', height: 'auto'});
				if(this.targetLayerCode[param.target] == this.targetCode(param)){
					this.targetLayerCode[param.target] = null;
				}
			}
		}).bind(this), param.appear * 1000 + 100);
	},
	
	evalScriptInternal: function(script, param, delay){
		if(script != null){
			setTimeout((function(){
				if(param == null || this.targetLayerCode[param.target] == this.targetCode(param)){
					eval(script);
				}
			}).bind(this), delay);
		}
	}
	
});

if($$('body')[0]){
	if(!GScript.DynamicContent){
		GScript.DynamicContent = new DynamicContent();
	}
}
else{
	document.observe('dom:loaded', function(){
		if(!GScript.DynamicContent){
			GScript.DynamicContent = new DynamicContent();
		}
	});
}
