
Array.each = function( f ) {
	var i = 0;
	for( i = 0; i < this.length; i++ ) {
		f( this[i] );
	}
}
Array.eachWithIndex = function( f ) {
	var i = 0;
	for( i = 0; i < this.length; i++ ) {
		f( i, this[i] );
	}
}

function MCAM() { // Multiple Channel AJAX Mechanism
	this.requester = null;
	this.handlers = new Array();
	this.dirtyList = new Array();
	this.url = uriForCurrentAction();
	
	this.requestObject = function() {
		try { 
			return new XMLHttpRequest(); 
		} catch (error) { 
			try { 
				return new ActiveXObject("Microsoft.XMLHTTP"); 
			} catch (error) {
				return null;
			}
		}
		return null;
	};
	this.registerType = function( handlerType, handler ) {
		this.handlers[handlerType] = handler;
	};
	
	// Parses an XJSON data item. XJSON is a completely non-standard
	// XML format with roughly the expressive power of JSON: <object>,
	// <array>, <string>, <number> and <boolean> tags behave as you would
	// expect. Arbitrary XML data can be added using <xml> or <html> tags
	// (which are currently synonymous with one another).
	//
	// data ::= <string> string </string>
	//		| <number> number-as-string </number>
	//		| <boolean> true-or-false </boolean>
	//		| <null />
	//		| <array> data ... </array>
	//		| <object> <field1> data </field1> <field2> data </field2> ... </object>
	//		| <html> any ... </html>
	//		| <xml> any ... </xml>
	//
	// Lylux uses this format for two reasons:
	//   - no need for extra rendering code on the server
	//	 (just like outputting a web page)
	//   - easy to embed HTML data (all the parsing is done for free)

	this.parseXjson = function (node) {

		var ans = null;

		switch (node.tagName) {
			case "string":
				ans = node.textContent;
				break;

			case "number":
				ans = parseFloat (node.textContent);
				break;

			case "boolean":
				ans = node.textContent == "true" ? true : false;
				break;

			case "null":
				ans = null;
				break;

			case "array":
				ans = [];
				unlib.forEach (function (child) {
					if (child.tagName) {
						ans [ans.length] = this.parseXjson(child);
					}
				}, node.childNodes);
				break;
			case "object":
				ans = {};
				unlib.forEach (function (child) {
					if(child.tagName) {
						var name = child.tagName;

						// Skip text nodes to find the first child element:
						var grandchild = child.firstChild;
						while (!grandchild.tagName) {
							grandchild = grandchild.nextSibling;
						}

						ans [name] = this.parseXjson (grandchild);
					}
				}, node.childNodes);
				break;

			case "html":
			case "xml":
				ans = node.childNodes;
				break

			default:
				throw ["Unrecognised XJSON data type", node];
		}
		return ans;
	};
	this.handleChannel = function( node ) {
		var id = '';
		var type = '';
		var content = '';
		var i = 0;

		for( i = 0; i < node.childNodes.length; i++ ) {
			switch( node.childNodes[i].tagName ) {
				case 'id':
					id = node.childNodes[i].firstChild.nodeValue;
					break;
				case 'type':
					type = node.childNodes[i].firstChild.nodeValue;
					break;
				case 'content':
					content = node.childNodes[i];
					break;
			}
		}
		
		try {
			return this.handlers[type]( id, type, content );
		} catch( e ) {
			alert( 'Unable to invoke handler: "' + type + '" for id "' + id + '"' );
			return false;
		}
	};
	this.handleEvent = function() {
		switch( this.requester.readyState ) {
			case 4: {
				if( this.requester.status == 200 ) {
					var i = 0;
					var successful = true;
					try {
						var root = this.requester.responseXML.firstChild;
						if( this.requester.responseXML.documentElement )
							root = this.requester.responseXML.documentElement;
						for( i = 0; i < root.childNodes.length; i++ ) {
							if( root.childNodes[i].tagName == 'channel' && !this.handleChannel( root.childNodes[i] ) && successful ) {
								successful = false;
							}
						}
					} catch ( e ) {
						alert( 'Error Decoding MCAM Packet:\n' + this.requester.responseText );
					}
					if( !successful )
						alert( 'Error Decoding MCAM Packet:\n' + this.requester.responseText );
				} else {
					alert('All going wrong -> ' + this.requester.status + ' : ' + this.url);
				}
				this.dirtyList = new Array();
				break;
			}
		}
	};
	this.registerDirtyComponent = function ( id ) {
		this.dirtyList.push(id);
	};
	this.fireBackgroundEvent = function( component, event_type, extra ) {
		var url = this.url;
		var self = this;
		var parameters = '';
		var i = 0;
		
		for( i = 0; i < this.dirtyList.length; i++ ) {
			var nodeid = this.dirtyList[i];
			var node = document.getElementById(nodeid);
			if( node && IsValidFormComponent(node) ) {
				parameters += SerializeFormComponent( nodeid, node );
			}
		}
		
		this.requester = this.requestObject();
		this.requester.open( "POST", url ); 
		this.requester.setRequestHeader( 'Content-Type','application/x-www-form-urlencoded' );
		this.requester.onreadystatechange = function() { self.handleEvent(); };
		this.requester.send(  'uieventcomponent='+ component +
								'&uieventdata=' + event_type +
								'&uieventextra=' + encodeURIComponent(extra) +
								getViewState() + 
								parameters );
	};
	this.fireForegroundEvent = function( component, event_type, extra ) {
		submitComponentForm( component, event_type, extra );
	};
	this.setTargetURL = function( url ) {
		this.url = url;
	};
	this.registerType( 'Replace', function( id, type, content ) { 
		var node = document.getElementById(id);
		if( node ) {
			if( content.firstChild )
				wfinsertAdjacentHTML( node, 'replace', content.firstChild.nodeValue );
			else
				node.parentNode.removeChild( node );
		} else {
			alert( 'no data' );
		}
		return true;
	} );
	this.registerType( 'SetValue', function( id, type, content ) {
		var node = document.getElementById(id);
		if( node ) {
			document.getElementById(id).value = content.firstChild.nodeValue;
		}
		return true;
	});
	this.registerType( 'Error', function( id, type, content ) {
		var errorMessage = content.firstChild.nodeValue;
		alert( 'MCAM.Error: ' + errorMessage );
		return true;
	});
	this.registerType( 'EventOutput', function( id, type, content ) {
		var output = content.firstChild.nodeValue;
		alert( 'MCAM.EventOutput: ' + output );
		return true;
	});
};

var mcam = new MCAM();
