
RedSquare.ElementBinderPlugIn = Base.extend
({
	constructor: function(key, bind)
	{
		// the key is the object property that will be checked for in the binding value
		// don't use the following keys, since they are reserved for the built-in behaviour:
		// keys, options
		
		this.key = key;
		
		// the bind function performs the actual binding to an element. It must take as arguments:
		// * the value of the binding (the key is not necessary)
		// * the HTML element to bind to.
		// You can then additional arguments after that, as required
		
		this.bind = bind;
	}
});

RedSquare.ElementBinder = Base.extend
({
	constructor: function(options)
	{
		this.templates = new Object();
		
		this.templateClassName = 'template';

		this.plugIns = new Array();
		
		if (options)
		{
			this.parentElement = options.parentElement;
			this.onComplete = options.onComplete;	
			this.plugIns = options.plugIns;
			
			if (options.templateClassName)
				this.templateClassName = options.templateClassName;
		}
	},
	
	processBinding: function(binding)
	{
		var elements = this.getElements(binding.key);
		
		for (a=0; a<elements.length; a++)
		{
			var element = elements[a];	

			
			
			if (binding.value.options)
			{
				// this is for a select	
				element.options.length = null;
				
				for (var i=0; i<binding.value.options.length; i++)
				{
					var thisOpt = binding.value.options[i];					
					if (thisOpt)
					{
						var optName = thisOpt[1];
						var optValue = thisOpt[0];
						element.options[element.options.length] = new Option(optName, optValue, false, binding.value.selectedIndex == i);
					}
				}
				//extra check for some reason the selected index was sometimes being set to the one before with the code above
				if (binding.value.selectedIndex) {
					element.selectedIndex = binding.value.selectedIndex;
				}
			}
			else if (binding.value.keys)
			{
				
				if (element.tagName.match(/table/i))
				{
					
					// this is for a table
					
					var rows = '';
					
					var parent = element;
					
					// we have a valid row template to work with
					var tbody = element.getElementsByTagName('tbody');
					
					if (tbody && tbody.length)
						parent = tbody[0];
					
					var afooter = element.getElementsByTagName('tfoot');				
					if( afooter.length )
						var footer = afooter[0];								
					
						
					
					if (!this.templates[binding.key])
					{
						// regard the first row as a template
						var trs = $A(parent.getElementsByTagName('tr'));
						
						if (trs && trs[0])
						{
							this.templates[binding.key] = { tagName: 'tr', text: trs[0].innerHTML, className: trs[0].className };
						}
						
					}
					
					var controlsID = element.id.replace(/table/i,"resultsControls");
					var arPageNav = this.getElements(controlsID);	
							
					if (this.templates[binding.key])
					{
						//
						
						if( binding.value.data.length == 0)
						{
							//alert( binding.value.data.length );
							//alert(parent);
							Element.hide(parent);
							if( footer ) {
								Element.hide(footer);		
							}	
							if (arPageNav)
							{
								for (k=0;k<arPageNav.length;k++) {
									Element.hide(arPageNav[k]);
								}	
							}
						}
						else
						{
							Element.show(parent);
							if( footer ) {
								Element.show(footer);
							}	
							
							if (arPageNav)
							{
								for (k=0;k<arPageNav.length;k++) {
									Element.show(arPageNav[k]);
								}	
							}
							
							for (var i=0; i<binding.value.data.length; i++)
							{
								var rowClass = '';
								var rowId = '';
								
								var thisRow = binding.value.data[i];
								
								if (binding.value.keys.indexOf('rowClass') && thisRow[binding.value.keys.indexOf('rowClass')] != '' )
								{
									rowClass = ' class="' + thisRow[binding.value.keys.indexOf('rowClass')] + '"';		
								}
								
								if (binding.value.keys.indexOf('rowId') && thisRow[binding.value.keys.indexOf('rowId')] != '' )
								{
									rowId = ' id="' + thisRow[binding.value.keys.indexOf('rowId')] + '"';		
								}
								/*
								if (binding.value.rowId && binding.value.rowId[i])
								{
									rowId = ' id="' + binding.value.rowId[i] + '"';		
								}*/
								
								rows += '<tr' + rowId + rowClass + '>' + this.fillRowTemplate(this.templates[binding.key].text, binding.value.keys, binding.value.data[i]) + '</tr>';
							}
							
							//alert(rows);
							if (parent.update)
								parent.update('');
							
							
							
							var rowElements = $A(parent.getElementsByTagName('tr'));
							
							for (var i=0; i<rowElements.length; i++)
							{
								Element.remove(rowElements[i]);	
							}

							new Insertion.Top(parent,rows);
							
							//so that you can access table data in afterElementBind
							//by calling binding.value.dataFor('key',row);
							Object.extend
							(
								binding.value,
								{
									dataFor: function(key, row)
									{
										return this.data[row][this.keys.indexOf(key)];	
									}
								}
							);
						}
					}
					//erorrs div
					var errorsDiv = $(element.id.toString().replace(/table/i,"errors"));		
					
					if (errorsDiv && errorsDiv.id != element.id)
					{
						
						if (binding.value.errorMessage) {
							errorsDiv.update(binding.value.errorMessage.toString());

							errorsDiv.style.display="block";
							//Element.show(errorsDiv);							
							//Element.show(errorsDiv);							
						} else {
							errorsDiv.update("");
							Element.hide(errorsDiv);
						}
					}
					//profiler.endTimer("BindingTable");
					
				}
				else
				{
					// keys and data for some other repeated element
					var html = '';
					
					
					// find the template within this element via the templateClassName
					
					if (!this.templates[binding.key])
					{
						var templates = $A(document.getElementsByClassName(this.templateClassName, element));
						
						if (templates && templates.length && templates[0])
							this.templates[binding.key] = { tagName: templates[0].tagName, className: templates[0].className, text: templates[0].innerHTML };
					}
					
					if (this.templates[binding.key])
					{
						var parent = element;
						
						for (var i=0; i<binding.value.data.length; i++)
						{
							var rowClass = '';
							var rowId = '';
							
							var alternateClass = '';
						
							if (binding.value.alternateClass && binding.value.alternateClass != '' && i % 2 == 1)
								alternateClass = ' ' + binding.value.alternateClass;
							
							if (binding.value.rowClass && binding.value.rowClass[i])
							{
								rowClass = ' class="' + this.removeTemplateClassName(this.templates[binding.key].className) + alternateClass + ' ' + binding.value.rowClass[i] + '"';		
							}
							else
							{	
								rowClass = ' class="' + this.removeTemplateClassName(this.templates[binding.key].className) + alternateClass + '"';	
							}
							
							if (binding.value.rowId && binding.value.rowId[i])
							{
								rowId = ' id="' + binding.value.rowId[i] + '"';		
							}
							
							
							html += '<' + this.templates[binding.key].tagName + rowId + rowClass + '>' + this.fillRowTemplate(this.templates[binding.key].text, binding.value.keys, binding.value.data[i]) + '</' + this.templates[binding.key].tagName + '>';
						}
						
						if (parent.update)
							parent.update('');
						
						new Insertion.Top(parent,html);
						
						
					}
					
				}
			}
			else if (RedSquare.isInputText(element) || RedSquare.isInputHidden(element) || RedSquare.isTextArea(element))
			{
				// this is a binding for a form element
				element.value = binding.value;
			}
			else if (RedSquare.isSelect(element))
			{
				Form.Element.setValue(element.id, binding.value);
			}
			else if (RedSquare.isInputRadio(element))
			{
				//alert(element.id);
				
				if (element.value.trim() == binding.value.toString().trim())
					element.checked = true;
			}
			else if (RedSquare.isInputCheckbox(element))
			{
				binding.value.toString().split(',')._each
				(
					function(bindingValue)
					{
						if (element.value.trim() == bindingValue.trim())
							element.checked = true;
					}
				)
			}
			else if (binding.value.display != null)
			{
			
				//toggle state binding - set the value + the hide/show state			
				if( binding.value.display )
					Element.show( element );
				else
					Element.hide( element );
					
				if( binding.value.value && binding.value.value != '')
					this.updateElement(element, binding.value.value);					
			}
			else
			{
			
				if (typeof(binding.value) == 'object')
				{
					var handled = false;
					
				
					// check for any plugIns to handle this object
					for (p=0; p<this.plugIns.length; p++)
					{
						var plugIn = this.plugIns[p];
						
						$H(binding.value)._each
						(
							function (property)
							{
								
								if (property.key.match(new RegExp(plugIn.key ,'g')))
								{
									// this is a valid plugIn to handle this binding value
									plugIn.bind(element, binding.value);
									handled = true;
								}
							}
						);
						
					}
						
				
					if (!handled)
					{
						
						// bind the data to the entire HTML of this element, that is, replace keys found in all of the element's attributes,
						// and in the innerHTML
						
						// to do this, we need to store the original template text inside this
						
						if (!this.templates[binding.key])
							this.templates[binding.key] = { tagName: element.tagName, text: element.innerHTML, className: element.className };									
						
						
						var finalHTML = this.templates[binding.key].text;
						
						$H(binding.value).each
						(
							function(property)
							{
								
								finalHTML = this.keyReplace(finalHTML, property.key, property.value);
								
								RedSquare.ElementBinder.ATTRIBUTES.each
								(
									function(attributeKey)
									{
										var attribute = element.attributes[attributeKey];
										
										if (attribute && attribute.value)
											attribute.value = this.keyReplace(attribute.value, property.key, property.value);
									}
									.bind(this)
								);
								
							}
							.bind(this)
						);
						
						// clear any keys that were not found (so that [key] items are not left inside the displayed HTML)
						element.innerHTML = this.notFoundReplace(finalHTML);
						
						// now remove the "template" class name from anything inside this element
						
						
						$A(document.getElementsByClassName(this.templateClassName, element)).each
						(
							function (el)
							{
								
								
								el.removeClassName(this.templateClassName);	
							}
							.bind(this)
						);
							
					}
			
					
				}
				else
				{
					// this is a basic binding
					this.updateElement(element, binding.value);					
				}
				
			
			
			}

			
			var element = null;
		} // for (a)

	},
	
	updateElement: function(element, value)
	{
		// check for an attribute assignment
					
		var matches = value.match(/\[([a-z]*)\=([^\]]*)\]/i);
					
		if (matches && matches.length > 0)
		{
			if (element.attributes[matches[1]])
			{
				element.setAttribute(matches[1], matches[2]);
			}
		}
		else
			element.update(value.toString());		
	},
	
	process: function(bindings)
	{
		var startTime = (new Date()).valueOf();
		
		
		
		
		//for when we are just trying to redirect the user to another page in the site
		if (bindings && bindings.redirectRequest)
		{
		
			
			window.location.href = bindings.redirectRequest;
			return;	
		}
		
		var bindingHash = $H(bindings); 
		//profiler.startTimer("BindingProcess");
		bindingHash.each
		(
			this.processBinding.bind(this)
		);
		//profiler.endTimer("BindingProcess");
		var endTime = (new Date()).valueOf();
		
		if (this.onComplete)
			this.onComplete(bindings);
				
		
	},
	
	removeTemplateClassName: function(str)
	{
		return str.replace(new RegExp('\\s?' + this.templateClassName + '\\s?', 'gi'), '');
	},
	
	/*: function(binding)
	{
		Object.extend
		(
			binding,
			{
				dataFor: function(key, row)
				{
					return this.data[row][this.keys.indexOf(key)];	
				}
			}
		);
		
		return binding;
		
	},*/
	
	getDataRowById: function(binding, id, idKey)
	{
		var key = idKey ? idKey : 'id';
		
		if (binding.data && binding.keys)
		{
			return binding.data.detect
			(
				function (row)
				{
					return this.getDataValueByKey(binding, row, key) == id;
				}
				.bind(this)
			);
		}
	},
	
	getDataValueByKey: function(binding, row, key)
	{
		if (row && binding.keys)
		{
			return row[binding.keys.indexOf(key)];
		}
	},
	
	getDataValueByRowIdColKey: function(binding, id, key, idKey)
	{
		return this.getDataValueByKey(binding, this.getDataRowById(binding, id, (idKey ? idKey : 'id')), key);
	},

	getElements: function(key)
	{
		

		
		
		var element = $(key);
		
		var elements = new Array();
		
		
		
		// if an element exists with this ID, return that
		if (element && element.id == key) // need second check because of crazy IE behaviour: http://www.shauninman.com/plete/2004/02/id-vs-name-and-ie-pc 
			elements.push(element);
		
		// now look for numerical sequences of IDs
		
		var index = 1;
		var iKey;
		
		do
		{
			iKey = key + index.toString();
			
			element = $(iKey);
			
			if (element && element.id == iKey) 
				elements.push(element);
			
			index++;
		}
		while (element);
		
		if (elements.length > 0)
			return elements;
		
		
		// if still not found check to see if there are any radio buttons or checkboxes with this name
		formElements = $$('input[type=radio][name=' + key + ']', 'input[type=checkbox][name=' + key + ']');
		
		if (formElements && formElements.length > 0)
			return formElements;
		
		

		return [];
	},
	
	fillRowTemplate: function(template, keys, row)
	{
		var filled;
		var key;
		
		filled = template;
		
		for (var i=0; i<keys.length; i++)
		{
		 	key = keys[i];
			
			filled = this.keyReplace(filled, key, row[i]);
		}
		
		return filled;
	},
	
	keyReplace: function(str, key, value)
	{
		var filled = str;
	
		filled = filled.replace(new RegExp('\\[' + key + '\\]', 'g'), value);
		filled = filled.replace(new RegExp('\%5B' + key + '\%5D', 'g'), value);
		
		
		return filled;
	},

	
	notFoundReplace: function(str)
	{
		// replaces all binding placeholders that were not found with empty string
		var filled = str;
		
		filled = filled.replace(/\[\w*?\]/g, '');
		filled = filled.replace(/\%5B\w*?\%5D/g, '');
		
		return filled;
	}
});
		
Object.extend
(
 	RedSquare.ElementBinder,
	{
		ATTRIBUTES: ['title', 'href', 'alt', 'rel', 'name', 'id', 'value']	
	}
);
