ppLib = {};

ppLib.getMemberInfo = function() {
	var a = null;
	var m = ppLib.getCookie("M");
	if(m) {
		a = m.split("&");
		for(var i = 0; i < a.length; ++i)
			if(a[i].match(/^\d+$/))
				a[i] = Number(a[i]);
	}
	return a;
}

// add (or replace the first instance of) 'param' on a URL. 
ppLib.setUrlParam = function(url, param, value){
	var i = url.indexOf("?");
	if(i == -1)
		return url + "?" + param + "=" + escape(value);
	var vs;
	if(url.indexOf("?"+param+"=", i) == i)
		vs = i + param.length + 2;
	else if((vs = url.indexOf("&"+param+"=", i)) != -1)
		vs += param.length + 2;
	else {
		url += "&" + param + "=";
		vs = url.length;
	}
	var ve = url.indexOf("&", vs);
	var result = url.substr(0,vs) + escape(value);
	if( ve != -1 )
		result += url.substr(ve);
	return result;
}

ppLib.cachebuster = function(url, param){
	param = param || "_r";
	return ppLib.setUrlParam(url, param, Math.floor(Math.random()*1000000));
}

ppLib.getCookie = function(name) {
	var expr = new RegExp("(?:^|; )" + name + "=([^;]+)");
	var result = expr.exec(document.cookie);
	if( result )
		return result[1];
	else
		return null;
}

ppLib.setCookie = function(name, val, path, domain) {
	var c = name + "=";
	if( val )
		c += val;
	if( path )
		c += ";path=" + path;
	if( domain )
		c += ";domain=" + domain;
	if( ! val )
		c += ";expires=Thu, 01-Jan-1970 00:00:01 GMT";
	document.cookie = c;
}

ppLib.getWindowDimensions = function() {
	if (window.innerWidth)
		return [window.innerWidth, window.innerHeight];
	else
		return [document.documentElement.clientWidth, document.documentElement.clientHeight];
}

djLib = {};

function dj_undef(name, root){
	return (typeof (root || this)[name] == "undefined");
}

// calls callObj.callFnName after srcObj.srcFnName is invoked
djLib.connect = function(srcObj, srcFnName, callObj, callFnName) {
	var mjp = djLib.getForMethod(srcObj, srcFnName);

	mjp.addAdvice(callObj, callFnName);

	return mjp; // a MethodJoinPoint object
};

djLib.disconnect = function(srcObj, srcFnName, callObj, callFnName) {
	if (! srcObj[srcFnName])
		return;
		
	var jpname = srcFnName + "$joinpoint";
	var joinpoint = srcObj[jpname];
	if (! joinpoint)
		return;
	else 
		joinpoint.removeAdvice(srcObj, srcFnName, callObj, callFnName);
};

djLib.setTapestryAjaxHeader = function(xmlHttpObj) {
	xmlHttpObj.setRequestHeader('dojo-ajax-request', '1');
	return true;
};

djLib.callAjaxWithTapestryCallback = function(ajaxUrl, urlParams, callbackFn) {
	var ajaxParams = {	
			beforeSend: djLib.setTapestryAjaxHeader,
			dataType:	"xml",
			cache:		false
		 };

	ajaxParams.url		= ajaxUrl;
	ajaxParams.data		= urlParams;
	ajaxParams.success	= callbackFn;
	jQuery.ajax(ajaxParams);
};

djLib.serialize = function(arry) {
	var s = "[";
	for(var i = 0; i < arry.length; ++i)
	{
		if( i > 0 ) s += ',';
		var v = arry[i];
		if( typeof(v)=="string") s += '"'+v+'"';
		else s += v;
	}
	s += "]";
	return s;
};


function MethodJoinPoint(/*Object*/obj, /*String*/funcName) {
	this.object = obj;
	this.methodname = funcName;
	this.methodfunc = this.object[funcName];
	this.after = [];
	
	this.run = function() {
		// summary:
		//		execute the connection represented by this join point. The
		//		arguments passed to run() will be passed to the function and
		//		its listeners.
		var result = null;
		if (this.methodfunc)
			result = this.object[this.methodname].apply(this.object, arguments);

		// clone the array so in-place disconnects don't disturb the calls
		var afterCallees = [];
		for (var i in this.after)
			afterCallees.push(this.after[i]);
		
		for (var i in afterCallees) {
			var ao = afterCallees[i];
			var callObj = ao[0];
			var callFnName = ao[1];
			var callFn = callObj[callFnName];
			if (!callFn)
				alert("no function found named " + callFnName);
			else
				callFn.apply(callObj, arguments); 
		}

		return result;
	};
	
	this.addAdvice = function(callObj, callFnName) {
		this.after.push([callObj, callFnName]);
	};
	
	this.removeAdvice = function(srcObj, srcFnName, callObj, callFnName) {
		for (var i in this.after) {
			var ao = this.after[i];
			var obj = ao[0];
			var fnName = ao[1];
			if ((callObj == obj) && (callFnName == fnName)) {
				this.after.splice(i, 1);
				break;
			}
		}
	};
}

// creates a joinpoint for a function, if one doesn't exist yet
djLib.getForMethod = function(/*Object*/obj, /*String*/funcName) {
	var ofn = obj[funcName];
	if (ofn && (typeof ofn != "function")) {
		alert("invalid function named " + f.uncName);
	}
	
	// we hide our joinpoint instance in obj[funcName + '$joinpoint']
	var jpname = funcName + "$joinpoint";
	var jpfuncname = funcName + "$joinpoint$method";
	var joinpoint = obj[jpname];
	if(!joinpoint){
		obj[jpfuncname] = ofn;
		joinpoint = obj[jpname] = new MethodJoinPoint(obj, jpfuncname);

		obj[funcName] = function(){ 
			return joinpoint.run.apply(joinpoint, arguments); 
		}
	}
	return joinpoint; // MethodJoinPoint
}

var tap_currentContext = this;
function tap_undef(name, object) {
	return (typeof (object || tap_currentContext)[name] == "undefined");
}

if (tap_undef("dojo", this)) {
    var dojo={
    byId: function(id) {return jQuery(id);},
	require:function(){}, 
	registerModulePath: function() {},
	widget: { byId: function(id) {return jQuery(id);}, getWidgetById: function(id) {return jQuery(id);} }
	};
}

var tapestry={
    version: "4.1.6.jquery",
    locale: "en-us",
    scriptInFlight: false, // whether or not javascript is currently being eval'd, default false
    ScriptFragment:new RegExp('(?:<script.*?>)((\n|.|\r)*?)(?:<\/script>)', 'im'), // regexp for script elements
    GlobalScriptFragment: new RegExp('(?:<script.*?>)((\n|.|\r)*?)(?:<\/script>)', 'img'), // regexp for global script fragments
    isIE: jQuery.browser.msie,
    // setup firebug logging - useful for finding unimplemented methods
    log: function() {                   
        if (window.console) console.log.apply(this, arguments);
    },
    /**
     * Executes the passed function when the document has done loading
     */
    addOnLoad: function(fn){ jQuery(document).ready(fn); },
    /**
     * Returns the dom node with the given id
     */
    byId: function(id) {return (typeof id=='string') ? document.getElementById(id) : id;},
 
    /**
     * Makes sure that the given namespace (passed as a string) exists
     * and is a valid javascript object.  
     */
    provide: function(){
        //tapestry.log('t.provide', arguments);
    },

    /**
     * Connects the event of the given target node to the given function of
     * the global namespace "tapestry".
     * Users of this method usually attach custom methods to the tapestry namespace
     * before calling this.
     *
     * Parameters: target, event, funcName
     */
    connect: function(target, event, funcName) {
        if (event.indexOf('on')==0) event=event.substring(2);
        jQuery(tapestry.byId(target)).bind(event, tapestry[funcName]);
    },
    
    /**
     * Function: connectBefore
     */
    connectBefore:function(target, event, funcName){
            target = tapestry.byId(target);
            var original = target[event];
            target[event] = function() {
                tapestry[funcName].apply(this, arguments);
                original.apply(this, arguments);
            };
    },    

    /**
     * Disconnects the event of the given target node from the given function of
     * the global namespace "tapestry"
     *
     * Parameters: target, event, funcName
     */
    cleanConnect: function(target, event, funcName) {
        if (event.indexOf('on')==0) event=event.substring(2);
        jQuery(tapestry.byId(target)).unbind(event, tapestry[funcName]);        
    },
    
    connectWidget: function() {tapestry.log('t.connectWidget', arguments);},   
    
    cleanConnectWidget: function() {tapestry.log('t.cleanConnectWidget', arguments);},    

	/**
	 * Perform an XHR.
	 * Implementation should set the mime-type to either "text/xml" or
	 * "text/json" and include the request headers described in the comments to the
	 * json parameter.
     * Implementations are also responsible for handling the responses.  
	 *
	 * Parameters:
	 * 	url     - The url to bind the request to.
	 * 	content - A properties map of optional extra content to send.
	 *  json    - (Optional) Boolean parameter specifying whether or not to create a
	 * 		    json request. If true, the request headers should include "json":true.
	 *          If false or unspecified, they should contain "dojo-ajax-request":true
	 */
    bind: function(url, content, json){
        //tapestry.log('t.bind', arguments);
        
        var parms = {
            url:url,
            content:content,
            useCache:true,
            preventCache:true,
            encoding: tapestry.requestEncoding,
            error: (function(){tapestry.error.apply(this, arguments);})
        };

        // setup content type
        if (typeof json != "undefined" && json) {
            parms.handleAs = "json";
            parms.headers={"json":true};
            parms.load=(function(){tapestry.loadJson.apply(this, arguments);});
        } else {
            parms.handleAs = "xml";
            parms.headers={"dojo-ajax-request":true};
            parms.load=(function(){tapestry.load.apply(this, arguments);});
        }
        var postData = "dojo-ajax-request=true";
        if (content) postData += "&" + jQuery.param(content);

        var ajaxData={
			type: "GET",
			cache: false,
			url: url,
			data: postData,
			success: parms.load,
			dataType: parms.handleAs
        };
        jQuery.ajax(ajaxData);

    },
    
    error: function(){
        tapestry.log('error');
    },
    
    load: function(data, kwArgs){
    	var success = true;
        if (!data) {
                tapestry.log("No data received in response.");
                return false;
        }

        var resp=data.getElementsByTagName("ajax-response");
        if (!resp || resp.length < 1 || !resp[0].childNodes) {
                tapestry.log("No ajax-response elements received.");
                return false;
        }

        var elms=resp[0].childNodes;
        var statusMsgs=[];
        var bodyScripts=[];
        var initScripts=[];
        var rawData=[];
        for (var i=0; i<elms.length; i++) {
            var elmType=elms[i].getAttribute("type");
            var id=elms[i].getAttribute("id");

            if (elmType == "exception") {
                    tapestry.log("Remote server exception received.");
                    tapestry.presentException(elms[i], kwArgs);
                    return false;
            } else if (elmType == "page") {
                window.location=elms[i].getAttribute("url");
                return;
            } else if (elmType == "status") {
				statusMsgs.push(id);
				statusMsgs.push(tapestry.html.getContentAsString(elms[i]));
                continue;
            }

            // handle javascript evaluations
            if (elmType == "script") {

                    if (id == "initializationscript") {
                            initScripts.push(elms[i]);
                            continue;
                    } else if (id == "bodyscript") {
                            bodyScripts.push(elms[i]);
                            continue;
                    } else if (id == "includescript") {
                            // includes get processed immediately (synchronously)
                            var includes=elms[i].getElementsByTagName("include");
                            if (!includes){continue;}
                            for (var e=0; e<includes.length;e++) {
                                    tapestry.loadScriptFromUrl(includes[e].getAttribute("url"));
                            }
                            continue;
                    }
            } else {
                    rawData.push(elms[i]);
            }

            if (!id){
                    tapestry.log("No element id found in ajax-response node.");
                    success = false;
                    continue;
            }

            var node=tapestry.byId(id);
            if (!node) {
                    tapestry.log("No node could be found to update content in with id " + id);
                    success = false;
                    continue;
            }

            tapestry.loadContent(id, node, elms[i]);
        }

        // load body scripts before initialization
        for (var i=0; i<bodyScripts.length; i++) {
                tapestry.loadScriptContent(bodyScripts[i], true);
        }

        for (var i=0; i<rawData.length; i++) {
                tapestry.loadScriptContent(rawData[i], true);
        }

        for (var i=0; i<initScripts.length; i++) {
                tapestry.loadScriptContent(initScripts[i], true);
        }        
        return success;
    },
    
    loadJson: function(type, data, http, kwArgs){        
    },
    
    loadContent: function(id, node, element){
        if (typeof element.childNodes != "undefined" && element.childNodes.length > 0) {
            for (var i = 0; i < element.childNodes.length; i++) {
                if (element.childNodes[i].nodeType != 1) { continue; }

                var nodeId = element.childNodes[i].getAttribute("id");
                if (nodeId) {
                    element=element.childNodes[i];
                    break;
                }
            }
        }
        
        jQuery(node).unbind();
    	
         var content=tapestry.html.getContentAsString(element); 
        // fix for IE - setting innerHTML does not work for SELECTs
        if (tapestry.isIE && node.outerHTML && node.nodeName == "SELECT") {
            node.outerHTML = node.outerHTML.replace(/(<SELECT[^<]*>).*(<\/SELECT>)/, '$1' + content + '$2');
            node=tapestry.byId(id);
        } else if (content && content.length > 0){
            node.innerHTML=content;
        }

        // copy attributes
		var atts=element.attributes;
		var attnode, i=0;
		while((attnode=atts[i++])){
			if(tapestry.isIE){
				if(!attnode){ continue; }
				if((typeof attnode == "object")&&
					(typeof attnode.nodeValue == 'undefined')||
					(attnode.nodeValue == null)||
					(attnode.nodeValue == '')){
					continue;
				}
			}

			var nn = attnode.nodeName;
			var nv = attnode.nodeValue;
			if (nn == "id" || nn == "type" || nn == "name"){continue;}

			if (nn == "style") {
                jQuery(node).attr('style', nv);
            } else if (nn == "class") {
                jQuery(node).addClass(nv);
			} else if (nn == "value") {
                node.value = nv;
            } else {
                node.setAttribute(nn, nv);
            }
        }

    	// apply disabled/not disabled
    	var disabled = element.getAttribute("disabled");
        if (!disabled && node["disabled"]) {
            node.disabled = false;
        } else if (disabled) {
            node.disabled = true;
        }
    },    
    
    loadScriptContent: function(element, async){	
        if (typeof async == "undefined") { async = true; }
        async = this.isIE;

        if (tapestry.scriptInFlight) {
            setTimeout(function() { tapestry.loadScriptContent(element, async);}, 5);
            return;
        }

	var text = tapestry.html.getContentAsString(element);
	text.replace(this.GlobalScriptFragment, '');
	var scripts = text.match(this.GlobalScriptFragment);

	if (!scripts) { return; }

        if (async) {
        	setTimeout(function() {
        		tapestry._evaluateScripts(scripts);
        	}, 60);
        } else {
        	tapestry._evaluateScripts(scripts);
        }		
    },
    
    _evaluateScripts:function(scripts){
	tapestry.scriptInFlight = true;

	for (var i=0; i<scripts.length; i++) {
	    var scr = scripts[i].match(this.ScriptFragment)[1];
	    if(!scr || scr.length <= 0){continue;}
	    try {
            eval(scr);
	    } catch (e) {
            tapestry.scriptInFlight = false;
            tapestry.log("Error evaluating script: " + scr, e, false);
	    }
	}

	tapestry.scriptInFlight = false;
    },
    
    loadScriptFromUrl: function(url){
        jQuery.getScript(url);        
    },    

    /**
     * Helper that builds the content from eventName and (triggered) id and then forwards
     * execution to tapestry.bind
     * 
     * @param url
     * @param id
     * @param isJson
     * @param eventName
     */
    linkOnClick: function(url, id, isJson, eventName) {
        var content={beventname:(eventName || "onClick")};
        content["beventtarget.id"]=id;
        tapestry.bind(url, content, isJson);
        return false;        
    },
    
    presentException: function(node, kwArgs) {
    	var data = tapestry.html.getContentAsString(node);
    	jQuery.facebox(data);
    }
};

tapestry.form = {
    forms:{},
    currentFocus:null,
    /**
     * Submits the specified form.
     * Should check the value of form.submitmode to find out what type of
     * submission (cancel, refresh or normal) to do and whether to run client validation.
     *
     * Parameters:
     * form			-	The form or form id to submit.
     * submitName	- 	(Optional) Submit name to use when submitting. This is used
     * 					to associate a form submission with a particular component.
     *                  Implementations will typically just set form.submitname to this value.
     * parms		-	(Optional) Extra set of parameters. Implementations can just look for
     *                  the async key and if that's set to true, they should perform an async
     *                  submission.
     */
    submit:function(form, submitName, parms){
        //tapestry.log("t.f.submit", arguments);
        form=tapestry.byId(form);
        if (!form) {
                tapestry.log("t.f.submit: Form not found with id " + form, arguments);
                return;
        }
        var id=form.getAttribute("id");
        if (submitName){
            form.submitname.value = submitName;
        }

        if (form.submitmode["value"]
                && (form.submitmode.value == "cancel" || form.submitmode.value == "refresh")
                && !parms) {
            form.submit();
            return;
        }

        if (!tapestry.form.validation.validateForm(form, this.forms[id]))
            return;

        if (parms && parms["async"] && parms.async) {
            tapestry.form.submitAsync(form, null, submitName, parms);
            return;
        } else if(this.forms[id] && this.forms[id].async){
            tapestry.form.submitAsync(form);
            return;
        }
        
        form.submit();
    },

    /** Same as submit, but forces cancel submission */
    cancel: function(formId, submitName, parms) {tapestry.log('t.f.submit', arguments);},

    /** Same as submit, but forces refresh submission */
    refresh: function(formId, submitName, parms) {tapestry.log('t.f.submit', arguments);},

    /**
	 * Registers a form and allows definition of its properties.
	 * Implementation should keep track of such properties and
	 * use them later on, when the form is submitted.
	 *
	 * Parameters:
	 *	id		-	The form or form id to register.
	 *  async	-	Boolean, if true form submission should be asynchronous.
	 *  json	-	Boolean, if true form submission should be asyncrhronous json.
	 */
    registerForm: function(id, async, json) {
        //tapestry.log("t.f.registerForm", arguments);
        var form=tapestry.byId(id);
        if (!form) {
                tapestry.log("t.f.registerForm: Form not found with id " + id);
                return;
        }

        // make sure id is correct just in case node passed in has only name
        id=form.getAttribute("id");

        // if previously connected, cleanup and reconnect
        if (this.forms[id]) {
	    jQuery(form).unbind('submit', this.onFormSubmit);
            for(var i = 0; i < form.elements.length; i++) {
                var node = form.elements[i];
                if(node && node.type 
                    && (node.type.toLowerCase()=="submit" || node.type.toLowerCase()=="button")) {
			jQuery(node).unbind('click', tapestry.form.inputClicked);
                }
            }

            var inputs = form.getElementsByTagName("input");
            for(var i = 0; i < inputs.length; i++) {
                    var input = inputs[i];
                    if(input.type.toLowerCase() == "image" && input.form == form) {
			    jQuery(input).unbind('click', tapestry.form.inputClicked);
                    }
            }

	    jQuery(form).unbind('submit', this.overrideSubmit);
            delete this.forms[id];
        }

        this.forms[id]={};
        this.forms[id].validateForm=true;
        this.forms[id].profiles=[];
        this.forms[id].async=(typeof async != "undefined") ? async : false;
        this.forms[id].json=(typeof json != "undefined") ? json : false;

        if (!this.forms[id].async) {
            jQuery(form).bind('submit', tapestry.form.onFormSubmit);
        } else {
            for(var i = 0; i < form.elements.length; i++) {
                var node = form.elements[i];
                if(node && node.type 
                    && (node.type.toLowerCase()=="submit" || node.type.toLowerCase()=="button")) {
                        jQuery(node).bind('click', tapestry.form.inputClicked);
                }
            }

            var inputs = form.getElementsByTagName("input");
            for(var i = 0; i < inputs.length; i++) {
                var input = inputs[i];
                if(input.type.toLowerCase() == "image" && input.form == form) {
                    jQuery(input).bind('click', tapestry.form.inputClicked);
                }
            }

            jQuery(form).bind('submit', tapestry.form.overrideSubmit);
        }        
    },
    
    overrideSubmit: function(e){
        tapestry.event.stopEvent(e);
        var elm = e.target;
        if (elm["form"]){
            elm = elm.form;
        }
        tapestry.form.submitAsync(elm);
    },
    
    inputClicked:function(e){
        var node = e.currentTarget;
        if(node.disabled || !node["form"]) { return; }
        tapestry.form.forms[node.form.getAttribute("id")].clickedButton = node;
    },
    
    onFormSubmit:function(evt){
        if(!evt || !evt["target"]) {
            tapestry.log("No valid form event found with argument: " + evt);
            return;
        }

        var id=evt.target.getAttribute("id");
        if (!id) {
                tapestry.log("Form had no id attribute.");
                return;
        }
        var form = tapestry.byId(id);

        if (form.submitmode["value"]
                && (form.submitmode.value == "cancel" || form.submitmode.value == "refresh")) {
                return;
        }

        if (!tapestry.form.validation.validateForm(form, tapestry.form.forms[id])) {
                tapestry.event.stopEvent(evt);
        }
    },
    
    submitAsync:function(form, content, submitName, parms){
        form=tapestry.byId(form);
        if (!form) {
                tapestry.log("t.f.submitAsync: Form not found with id " + id);
                return;
        }
        var formId=form.getAttribute("id");

        if (!tapestry.form.validation.validateForm(form, this.forms[formId])) {
                tapestry.log("Form validation failed for form with id " + formId);
                return;
        }

        if (submitName){
            var previous = form.submitname.value;
            form.submitname.value=submitName;
            if(!content){ content={}; }
            if(form[submitName]){
                    content[submitName]=form[submitName].value;
            }
        }
		
        // handle submissions from input buttons
        if (tapestry.form.forms[formId]["clickedButton"]) {
            if (!content) { content={}; }
            content[tapestry.form.forms[formId].clickedButton.getAttribute("name")]=this.forms[formId].clickedButton.getAttribute("value");
            delete tapestry.form.forms[formId].clickedButton;
        }

        var kwArgs={
            form:form,
            content:content,
            useCache:true,
            preventCache:true,
            error: (function(){tapestry.error.apply(this, arguments);}),
            encoding: tapestry.requestEncoding
        };
		
        // check for override
        if (parms){
            if (parms['url']) { kwArgs.url=parms.url; }
        }

        if (this.forms[formId].json || parms && parms.json) {
            kwArgs.headers = {"json":true};
            kwArgs.handleAs = "json";
            kwArgs.load = (function(){tapestry.loadJson.apply(this, arguments);});
        } else {
            kwArgs.headers = {"dojo-ajax-request":true};
            kwArgs.handleAs = "xml";
            kwArgs.load = (function(){tapestry.load.apply(this, arguments);});
        }
        var postData = jQuery.param(jQuery(form).serializeArray()) + "&dojo-ajax-request=true";
        if (content) postData += "&" + jQuery.param(content);

        var ajaxData={
			type: "POST",
			url: form.action,
			data: postData,
			success: kwArgs.load,
			dataType: kwArgs.handleAs
        };
        jQuery.ajax(ajaxData);

        if (submitName){
            form.submitname.value = previous;
        }
    },

    param: function( a ) {
        var s = [];

        // If an array was passed in, assume that it is an array
        // of form elements
        if ( a.constructor == Array || a.jquery )
            // Serialize the form elements
            jQuery.each( a, function(){
                s.push( encodeURIComponent(this.name) + "=" + /*encodeURIComponent*/( this.value ) );
            });

        // Otherwise, assume that it's an object of key/value pairs
        else
            // Serialize the key/values
            for ( var j in a )
                // If the value is an array then the key names need to be repeated
                if ( a[j] && a[j].constructor == Array )
                    jQuery.each( a[j], function(){
                        s.push( encodeURIComponent(j) + "=" + /*encodeURIComponent*/( this ) );
                    });
                else
                    s.push( encodeURIComponent(j) + "=" + /*encodeURIComponent*/( a[j] ) );

        // Return the resulting serialization
        return s.join("&").replace(/%20/g, "+");
    },

    /**
     * Registers a form validation/translation profile.
     * TODO: Describe profile structure.
     *
	 * Parameters:
	 *	formId		-	The form or form id to register profile with.
	 *	profile	    -	The object containing all of the validation/value constraints for the form.
     */    
    registerProfile: function(id, profile) {
        if (!this.forms[id]) return;
        this.forms[id].profiles.push(profile);
    },

	/**
	 * Clears any previously registered validation profiles on the specified form.
	 *
	 * Parameters:
	 *	formId      -   The form id to clear profiles for.
	 */
    clearProfiles: function(id) {
        if (!this.forms[id]) return;
		
        for (var i=0; i < this.forms[id].profiles.length; i++) {
                delete this.forms[id].profiles[i];
        }
        this.forms[id].profiles=[];
     },

    /**
     * Brings keyboard input focus to the specified field.
     */
    focusField: function(field) {
        jQuery('#'+field).focus();
    },

    setFormValidating: function() {
        tapestry.log('t.f.setFormValidating', arguments);
    },

    datetime: {
        isValidDate: function(value, flags) {
	    if(!value){return false;}

	    if (!flags){
		tapestry.log("isValidDate: value and flags must be specified");
		return true;
	    }
	    
	    var dateValue=null;
	    try {
		dateValue = tapestry.form.datetime.parse(value, flags);
	    } catch (e) {
		tapestry.log("Error parsing input date.", e, true);
		return false;
	    }

	    if(dateValue == null) { return false; }	    
	    
	    return true;
	},

	parse: function(value, options) {
	    options = options || {};
	    var locale = options.locale ? options.locale.toLowerCase() : tapestry.locale;
	    var info = {};
	    if (!options.selector) {
		options.selector = "dateOnly";
	    }
	    var datePattern = options.datePattern;
	    var timePattern = options.timePattern;
	    
	    var pattern = datePattern;
	    if (options.selector == "timeOnly") {
		pattern = timePattern;
	    } else if (options.selector == "dateTime") {
		pattern = datePattern + " " + timePattern;
	    }
	    
	    var groups = [];
	    var dateREString = tapestry.form.datetime._processPattern(pattern,
		function(usePattern){
		    return tapestry.form.datetime._buildDateTimeRE(groups, info, options, usePattern);}
	    );
		
	    var dateRE = new RegExp("^" + dateREString + "$");
	    var match = dateRE.exec(value);
	    if (!match) {
		return null;
	    }
	    var result = new Date(1972, 0);
	    // TODO: get the correct date represented by the text here (data/format.js)
	    return result;
        },
	
	_processPattern: function(pattern, applyPattern, applyLiteral, applyAll) {
	    var identity = function (x) {
		    return x;
	    };
	    applyPattern = applyPattern || identity;
	    applyLiteral = applyLiteral || identity;
	    applyAll = applyAll || identity;
	    var chunks = pattern.match(/(''|[^'])+/g);
	    var literal = false;
	    for (var i = 0; i < chunks.length; i++) {
		if (!chunks[i]) {
		    chunks[i] = "";
		} else {
		    chunks[i] = (literal ? applyLiteral : applyPattern)(chunks[i]);
		    literal = !literal;
		}
	    }
	    return applyAll(chunks.join(""));
	},
	
	_buildDateTimeRE: function(groups, info, options, pattern) {
	    return pattern.replace(/([a-z])\1*/ig, function (match) {
		var s;
		var c = match.charAt(0);
		var l = match.length;
		switch (c) {
		  case "y":
			s = "\\d" + ((l == 2) ? "{2,4}" : "+");
			break;
		  case "M":
			s = (l > 2) ? "\\S+" : "\\d{1,2}";
			break;
		  case "d":
			s = "\\d{1,2}";
			break;
		  case "E":
			s = "\\S+";
			break;
		  case "h":
		  case "H":
		  case "K":
		  case "k":
			s = "\\d{1,2}";
			break;
		  case "m":
		  case "s":
			s = "[0-5]\\d";
			break;
		  case "S":
			s = "\\d{1,3}";
			break;
		  case "a":
			var am = options.am || info.am || "AM";
			var pm = options.pm || info.pm || "PM";
			if (options.strict) {
				s = am + "|" + pm;
			} else {
				s = am;
				s += (am != am.toLowerCase()) ? "|" + am.toLowerCase() : "";
				s += "|";
				s += (pm != pm.toLowerCase()) ? pm + "|" + pm.toLowerCase() : pm;
			}
			break;
		  default:
			tapestry.log("parse of date format, pattern=" + pattern);
		}
		if (groups) {
			groups.push(match);
		}
		return "\\s*(" + s + ")\\s*";
	    });
	}
	
    },
    
    validation: {
	missingClass:"fieldMissing", // default css class that will be applied to fields missing a value
	invalidClass:"fieldInvalid", // default css class applied to fields with invalid data	
        isReal: function(value, locale, flags) {
	    var isArray = function(it) {return (it && it instanceof Array || typeof it == "array");};
	    flags = (typeof flags == "object") ? flags : {};
	    var formatData = tapestry.form.validation._mapToLocalizedFormatData(tapestry.form.validation.FORMAT_TABLE, locale);
	    if (typeof flags.separator == "undefined") {
		flags.separator = formatData[1];
	    } else {
		if (isArray(flags.separator) && flags.separator.length === 0) {
			flags.separator = [formatData[1], ""];
		}
	    }
	    if (typeof flags.decimal == "undefined") {
		flags.decimal = formatData[2];
	    }
	    if (typeof flags.groupSize == "undefined") {
		flags.groupSize = formatData[3];
	    }
	    if (typeof flags.groupSize2 == "undefined") {
		flags.groupSize2 = formatData[4];
	    }
	    var re = new RegExp("^" + tapestry.form.validation.realNumberRE(flags) + "$");
	    return re.test(value);
        },
        greaterThanOrEqual: function(value, minString, flags) {
	    flags.validate=false;
	    var min = tapestry.form.validation.numberParse(minString, null, flags);
	    var num = tapestry.form.validation.numberParse(value, null, flags);
	    if (Number.NaN == num) { return false; }
	    return num >= min;
	},
        lessThanOrEqual: function(value, maxString, flags) {
	    flags.validate=false;
	    var max = tapestry.form.validation.numberParse(maxString, null, flags);
	    var num = tapestry.form.validation.numberParse(value, null, flags);
	    if (Number.NaN == num) { return false; }
	    return num <= max;
	},
        isText: function(value, flags) {
	    flags = (typeof flags == "object") ? flags : {};
	    if (/^\s*$/.test(value)) {
		    return false;
	    }
	    if (typeof flags.length == "number" && flags.length != value.length) {
		    return false;
	    }
	    if (typeof flags.minlength == "number" && flags.minlength > value.length) {
		    return false;
	    }
	    if (typeof flags.maxlength == "number" && flags.maxlength < value.length) {
		    return false;
	    }
	    return true;
        },
        isEmailAddress: function(value, flags) {
	    var re = new RegExp("^[a-zA-Z0-9\\._-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,6}$", "i");
	    return re.test(value);
	},
        isValidPattern: function(value, pattern) {
	    if (typeof value != "string" || typeof pattern != "string") { return false; }
	    var re = new RegExp(pattern);
	    return re.test(value);
	},
	isEqual:function(value, other){
	    var otherValue = tapestry.byId(other).value;
	    return value == otherValue;
	},
	isNotEqual:function(value, other){
	    return !tapestry.form.validation.isEqual(value, other);
	},
	numberParse: function (value, locale, flags) {
	    flags = (typeof flags == "object") ? flags : {};
	    var formatData = tapestry.form.validation._mapToLocalizedFormatData(tapestry.form.validation.FORMAT_TABLE, locale);
	    if (typeof flags.separator == "undefined") {
		    flags.separator = formatData[1];
	    }
	    if (typeof flags.decimal == "undefined") {
		    flags.decimal = formatData[2];
	    }
	    if (typeof flags.groupSize == "undefined") {
		    flags.groupSize = formatData[3];
	    }
	    if (typeof flags.groupSize2 == "undefined") {
		    flags.groupSize2 = formatData[4];
	    }
	    if (typeof flags.validate == "undefined") {
		    flags.validate = true;
	    }
	    if (flags.validate && !tapestry.form.validation.isReal(value, locale, flags)) {
		    return Number.NaN;
	    }
	    var numbers = value.split(flags.decimal);
	    if (numbers.length > 2) {
		    return Number.NaN;
	    }
	    var whole = Number(numbers[0].replace(new RegExp("\\" + flags.separator, "g"), ""));
	    var fract = (numbers.length == 1) ? 0 : Number(numbers[1]) / Math.pow(10, String(numbers[1]).length);
	    return whole + fract;
	},
	realNumberRE:function(flags){
	    flags = (typeof flags == "object") ? flags : {};
	    if (typeof flags.places != "number") {
		    flags.places = Infinity;
	    }
	    if (typeof flags.decimal != "string") {
		    flags.decimal = ".";
	    }
	    if (typeof flags.fractional == "undefined") {
		    flags.fractional = [true, false];
	    }
	    if (typeof flags.exponent == "undefined") {
		    flags.exponent = [true, false];
	    }
	    if (typeof flags.eSigned == "undefined") {
		    flags.eSigned = [true, false];
	    }
	    var integerRE = tapestry.form.validation.integerRE(flags);
	    var decimalRE = (function (q) {
		    var re = "";
		    if (q && (flags.places > 0)) {
			    re = "\\" + flags.decimal;
			    if (flags.places == Infinity) {
				    re = "(" + re + "\\d+)?";
			    } else {
				    re = re + "\\d{" + flags.places + "}";
			    }
		    }
		    return re;
	    })(flags.fractional);
	    var exponentRE = (function (q) {
		    if (q) {
			    return "([eE]" + tapestry.form.validation.integerRE({signed:flags.eSigned}) + ")";
		    }
		    return "";
	    })(flags.exponent);
	    return integerRE + decimalRE + exponentRE;	    
        },
	integerRE:function(flags){
	    flags = (typeof flags == "object") ? flags : {};
	    if (typeof flags.signed == "undefined") {
		flags.signed = [true, false];
	    }
	    if (typeof flags.separator == "undefined") {
		flags.separator = "";
	    } else {
		if (typeof flags.groupSize == "undefined") {
			flags.groupSize = 3;
		}
	    }
	    var signRE = (function (q) {
		return q ? "[-+]" : "";
	    })(flags.signed);
	    var numberRE = (function (sep) {
		if (sep == "") {
			return "(0|[1-9]\\d*)";
		}
		var grp = flags.groupSize, grp2 = flags.groupSize2;
		if (typeof grp2 != "undefined") {
			var grp2RE = "(0|[1-9]\\d{0," + (grp2 - 1) + "}([" + sep + "]\\d{" + grp2 + "})*[" + sep + "]\\d{" + grp + "})";
			return ((grp - grp2) > 0) ? "(" + grp2RE + "|(0|[1-9]\\d{0," + (grp - 1) + "}))" : grp2RE;
		}
		return "(0|[1-9]\\d{0," + (grp - 1) + "}([" + sep + "]\\d{" + grp + "})*)";
	    })(flags.separator);
	    return signRE + numberRE;	    
        },
	
        validateForm: function(form, props) {
	    if (typeof form == "undefined") {return false;}
	    if (typeof props == "undefined") {return true;} 
            if (!props.validateForm) {return true;}	    
	    
	    try {
		this.clearValidationDecorations(form, props);

		for (var i=0; i < props.profiles.length; i++) {
		    var results=this.check(form, props.profiles[i]);
		    if (!this.processResults(form, results, props.profiles[i])) {
			this.summarizeErrors(form, results, props.profiles[i]);
			return false;
		    }
		}
	    } catch (e) {
		    tapestry.log("Error validating", e, true);
		    return false;
	    }
	    return true;
	},

	processResults:function(form, results, profile){
	    if (results.isSuccessful()) { return true; }

	    var formValid=true;
	    if (results.hasMissing()) {
		    var missing=results.getMissing();
		    for (var i=0; i < missing.length; i++) {
			this.handleMissingField(missing[i], profile);
		    }
		    formValid=false;
	    }

	    if (results.hasInvalid()) {
		    var invalid=results.getInvalid();
		    for (var i=0; i < invalid.length; i++) {
			this.handleInvalidField(invalid[i], profile);
		    }
		    formValid=false;
	    }

	    return formValid;
	},

	handleMissingField:function(field, profile){
	    field=tapestry.byId(field);
	    if (tap_undef("type", field)) {return;}
	    jQuery(field).removeClass(this.invalidClass);

	    if (!jQuery(field).hasClass(this.missingClass)){
		    jQuery(field).addClass(this.missingClass);
	    }
	},

	handleInvalidField:function(field, profile){
	    field=tapestry.byId(field);
	    if (tap_undef("type", field)) {return;}
	    jQuery(field).removeClass(this.missingClass);

	    if (!jQuery(field).hasClass(this.invalidClass)){
		    jQuery(field).addClass(this.invalidClass);
	    }
	},

	clearValidationDecorations:function(form, props){
	    for (var i=0; i < props.profiles.length; i++) {

		for (var fieldName in props.profiles[i]) {
		    if (tap_undef("type", form.elements[fieldName]) || typeof form.elements[fieldName].type == "undefined"
			    || form.elements[fieldName].type == "submit"
			    || form.elements[fieldName].type == "hidden") { continue; }

		    jQuery(form.elements[fieldName])
			.removeClass(this.missingClass)
			.removeClass(this.invalidClass);
		}
	    }
	},

	summarizeErrors:function(form, results, profile){
	    var merrs=[];
	    var ierrs=[];
	    tapestry.form.currentFocus=null;

	    if (results.hasMissing()){
		var fields=results.getMissing();
		for (var i=0; i<fields.length; i++){
		    if(i==0 && !tapestry.form.currentFocus){
			    tapestry.form.currentFocus=fields[i];
		    }
		    if (profile[fields[i]] && profile[fields[i]]["required"]){
			    var allRequired = jQuery.makeArray(profile[fields[i]]["required"]);
			    for (var z=0; z < allRequired.length; z++)
				    merrs.push(allRequired[z]);
		    }
		}
	    }
	    if (results.hasInvalid()){
		var fields=results.getInvalid();
		for (var i=0; i<fields.length; i++){
		    if(i==0 && !tapestry.form.currentFocus){
			    tapestry.form.currentFocus=fields[i];
		    }
		    if (profile[fields[i]] && profile[fields[i]]["constraints"]){
			var allConstraints = jQuery.makeArray(profile[fields[i]]["constraints"]);
			for (var z=0; z < allConstraints.length; z++)
				ierrs.push(allConstraints[z]);
		    }
		}
	    }

	    var msg="";
	    if (merrs.length > 0) {
		msg+='<ul class="missingList">';
		for (var i=0; i<merrs.length;i++) {
			msg+="<li>"+merrs[i]+"</li>";
		}
		msg+="</ul>";
	    }
	    if (ierrs.length > 0) {
		msg+='<ul class="invalidList">';
		for (var i=0; i<ierrs.length;i++) {
			msg+="<li>"+ierrs[i]+"</li>";
		}
		msg+="</ul>";
	    }

	    jQuery.facebox(msg);
	},
	check: function(form, profile) {
	    var missing = [];
	    var invalid = [];
	    var results = {isSuccessful:function () {
		    return (!this.hasInvalid() && !this.hasMissing());
	    }, hasMissing:function () {
		    return (missing.length > 0);
	    }, getMissing:function () {
		    return missing;
	    }, isMissing:function (elemname) {
		    for (var i = 0; i < missing.length; i++) {
			    if (elemname == missing[i]) {
				    return true;
			    }
		    }
		    return false;
	    }, hasInvalid:function () {
		    return (invalid.length > 0);
	    }, getInvalid:function () {
		    return invalid;
	    }, isInvalid:function (elemname) {
		    for (var i = 0; i < invalid.length; i++) {
			    if (elemname == invalid[i]) {
				    return true;
			    }
		    }
		    return false;
	    }};
	    var isString = function(it) {return (typeof it == "string" || it instanceof String);};
	    var isArray = function(it) {return (it && it instanceof Array || typeof it == "array");};
	    var isFunction = function(it) {return (it instanceof Function || typeof it == "function");};
	    var isObject = function (it) { 
		if (typeof it == "undefined") { return false; }
		return (typeof it == "object" || it === null || 
		     isArray(it) || isFunction(it) );
	    };

	    if (profile.required instanceof Array) {
		for (var i = 0; i < profile.required.length; i++) {
		    if (!isString(profile.required[i])) {
			continue;
		    }
		    var elem = form[profile.required[i]];
		    if (!tap_undef("type", elem) && (elem.type == "text" || elem.type == "textarea" || elem.type == "password") && /^\s*$/.test(elem.value)) {
			missing[missing.length] = elem.name;
		    } else {
			if (!tap_undef("type", elem) && (elem.type == "select-one" || elem.type == "select-multiple") && (elem.selectedIndex == -1 || /^\s*$/.test(elem.options[elem.selectedIndex].value))) {
			    missing[missing.length] = elem.name;
			} else {
			    if (elem instanceof Array) {
				var checked = false;
				for (var j = 0; j < elem.length; j++) {
					if (elem[j].checked) {
						checked = true;
					}
				}
				if (!checked) {
					missing[missing.length] = elem[0].name;
				}
			    }
			}
		    }
		}
	    }
	    if (profile.required instanceof Array) {
		for (var i = 0; i < profile.required.length; i++) {
		    if (!isObject(profile.required[i])) {
			    continue;
		    }
		    var elem, numRequired;
		    for (var name in profile.required[i]) {
			    elem = form[name];
			    numRequired = profile.required[i][name];
		    }
		    if (elem instanceof Array) {
			var checked = 0;
			for (var j = 0; j < elem.length; j++) {
				if (elem[j].checked) {
					checked++;
				}
			}
			if (checked < numRequired) {
				missing[missing.length] = elem[0].name;
			}
		    } else {
			if (!tap_undef("type", elem) && elem.type == "select-multiple") {
				var selected = 0;
				for (var j = 0; j < elem.options.length; j++) {
					if (elem.options[j].selected && !/^\s*$/.test(elem.options[j].value)) {
						selected++;
					}
				}
				if (selected < numRequired) {
					missing[missing.length] = elem.name;
				}
			}
		    }
		}
	    }    
	    if (isObject(profile.constraints)) {
		for (name in profile.constraints) {
		    var elem = form[name];
		    if (!elem) {
			continue;
		    }
		    if (!tap_undef("tagName", elem) && (elem.tagName.toLowerCase().indexOf("input") >= 0 || elem.tagName.toLowerCase().indexOf("textarea") >= 0) && /^\s*$/.test(elem.value)) {
			continue;
		    }
		    var isValid = true;
		    if (isFunction(profile.constraints[name])) {
			isValid = profile.constraints[name](elem.value);
		    } else {
			if (isArray(profile.constraints[name])) {
			    if (isArray(profile.constraints[name][0])) {
				for (var i = 0; i < profile.constraints[name].length; i++) {
				    isValid = this.evaluateConstraint(profile, profile.constraints[name][i], name, elem);
				    if (!isValid) {
					break;
				    }
				}
			    } else {
				isValid = this.evaluateConstraint(profile, profile.constraints[name], name, elem);
			    }
			}
		    }
		    if (!isValid) {
			invalid[invalid.length] = elem.name;
		    }
		}
	    }

	    return results;
	},
	evaluateConstraint: function (profile, constraint, fieldName, elem) {
	    var isValidSomething = constraint[0];
	    var params = constraint.slice(1);
	    params.unshift(elem.value);
	    if (typeof isValidSomething != "undefined") {
		    return isValidSomething.apply(null, params);
	    }
	    return false;
	}
    },
    FORMAT_TABLE: {"ar-ae":["", "", ",", 1], "ar-bh":["", "", ",", 1], "ar-dz":["", "", ",", 1], 
	"ar-eg":["", "", ",", 1], "ar-jo":["", "", ",", 1], "ar-kw":["", "", ",", 1], 
	"ar-lb":["", "", ",", 1], "ar-ma":["", "", ",", 1], "ar-om":["", "", ",", 1], 
	"ar-qa":["", "", ",", 1], "ar-sa":["", "", ",", 1], "ar-sy":["", "", ",", 1], 
	"ar-tn":["", "", ",", 1], "ar-ye":["", "", ",", 1], "cs-cz":[".", ".", ",", 3], 
	"da-dk":[".", ".", ",", 3], "de-at":[".", ".", ",", 3], "de-de":[".", ".", ",", 3], 
	"de-lu":[".", ".", ",", 3], "de-ch":["'", "'", ".", 3], "el-gr":[".", ".", ",", 3], 
	"en-au":[",", ",", ".", 3], "en-ca":[",", ",", ".", 3], "en-gb":[",", ",", ".", 3], 
	"en-hk":[",", ",", ".", 3], "en-ie":[",", ",", ".", 3], "en-in":[",", ",", ".", 3, 2], 
	"en-nz":[",", ",", ".", 3], "en-us":[",", ",", ".", 3], "en-za":[",", ",", ".", 3], 
	"es-ar":[".", ".", ",", 3], "es-bo":[".", ".", ",", 3], "es-cl":[".", ".", ",", 3], 
	"es-co":[".", ".", ",", 3], "es-cr":[".", ".", ",", 3], "es-do":[".", ".", ",", 3], 
	"es-ec":[".", ".", ",", 3], "es-es":[".", ".", ",", 3], "es-gt":[",", ",", ".", 3], 
	"es-hn":[",", ",", ".", 3], "es-mx":[",", ",", ".", 3], "es-ni":[",", ",", ".", 3], 
	"es-pa":[",", ",", ".", 3], "es-pe":[",", ",", ".", 3], "es-pr":[",", ",", ".", 3], 
	"es-py":[".", ".", ",", 3], "es-sv":[",", ",", ".", 3], "es-uy":[".", ".", ",", 3], 
	"es-ve":[".", ".", ",", 3], "fi-fi":[" ", " ", ",", 3], "fr-be":[".", ".", ",", 3], 
	"fr-ca":[" ", " ", ",", 3], "fr-ch":[" ", " ", ".", 3], "fr-fr":[" ", " ", ",", 3], 
	"fr-lu":[".", ".", ",", 3], "he-il":[",", ",", ".", 3], "hu-hu":[" ", " ", ",", 3], 
	"it-ch":[" ", " ", ".", 3], "it-it":[".", ".", ",", 3], "ja-jp":[",", ",", ".", 3], 
	"ko-kr":[",", ",", ".", 3], "no-no":[".", ".", ",", 3], "nl-be":[" ", " ", ",", 3], 
	"nl-nl":[".", ".", ",", 3], "pl-pl":[".", ".", ",", 3], "pt-br":[".", ".", ",", 3], 
	"pt-pt":[".", ".", "$", 3], "ru-ru":[" ", " ", ",", 3], "sv-se":[".", " ", ",", 3], 
	"tr-tr":[".", ".", ",", 3], "zh-cn":[",", ",", ".", 3], "zh-hk":[",", ",", ".", 3], 
	"zh-tw":[",", ",", ".", 3], "*":[",", ",", ".", 3]},
    _mapToLocalizedFormatData: function (table, locale) {
	locale = locale ? locale.toLowerCase() : tapestry.locale;

	var data = table[locale];
	if (typeof data == "undefined") {
		data = table["*"];
	}
	return data;
    }

};

tapestry.event = {
    stopEvent: function(e) {
		e.preventDefault();
		e.stopPropagation();
	},
    buildEventProperties:function(event, props, args){
		
	       if (!props) props={};
	       
           if(event["type"]) props.beventtype=event.type;
           if(event["keys"]) props.beventkeys=event.keys;
           if(event["charCode"]) props.beventcharCode=event.charCode;
           if(event["pageX"]) props.beventpageX=event.pageX;
           if(event["pageY"]) props.beventpageY=event.pageY;
           if(event["layerX"]) props.beventlayerX=event.layerX;
           if(event["layerY"]) props.beventlayerY=event.layerY;

           if (event["target"])  {
        	   	var t = jQuery(event.target);
        	   	if( t.attr("id") )
        		   props["beventtarget.id"] = t.attr("id");
	       		while(t) {
	    			var m = t.attr("ajaxargs");
	    			if( typeof m != "undefined" ) {
	    				props.methodArguments = m;
	    				break;
	    			}
	    			if( t.attr("id") == props.bcomponentid )
	    				break;
	    			t = t.parent();
	    		}
           }

           return props;
	   }
};

tapestry.widget = {
    synchronizeWidgetState: function() {tapestry.log('t.w.synchronizeWidgetState', arguments);}
};

/**
 * package: tapestry.html
 * Provides functionality related to parsing and rendering dom nodes.
 */
tapestry.html={

    CompactElementRegexp:/<([a-zA-Z](?!nput)[a-zA-Z]*)([^>]*?)\/>/g, // regexp for compact html elements
    CompactElementReplacer:'<$1$2></$1>', // replace pattern for compact html elements

    /**
     * Function: getContentAsString
     *
     * Takes a dom node and returns its contents rendered in a string.
     *
     * The resulting string does NOT contain any markup (or attributes) of
     * the given node - only child nodes are rendered and returned.Content
     *
     * Implementation Note: This function tries to make use of browser
     * specific features (the xml attribute of nodes in IE and the XMLSerializer
     * object in Mozilla derivatives) - if those fails, a generic implementation
     * is used that is guaranteed to work in all platforms.
	 *
	 * Parameters:
	 *
	 *	node - The dom node.
	 * Returns:
	 *
	 * The string representation of the given node's contents.
	 */
	getContentAsString:function(node){
		if (typeof node.xml != "undefined") {
			return this._getContentAsStringIE(node);
		} else if (typeof XMLSerializer != "undefined" ) {
			return this._getContentAsStringMozilla(node);
		} else {
			return this._getContentAsStringGeneric(node);
		}
	},

        /**
         * Function: getElementAsString
         *
         * Takes a dom node and returns itself and its contents rendered in a string.
         *
         * Implementation Note: This function uses a generic implementation in order
         * to generate the returned string.
         *
         * Parameters:
         *
         *	node - The dom node.
         * Returns:
         *
         * The string representation of the given node.
         */
	getElementAsString:function(node){
		if (!node) { return ""; }

		var s='<' + node.nodeName;
		// add attributes
		if (node.attributes && node.attributes.length > 0) {
			for (var i=0; i < node.attributes.length; i++) {
				s += " " + node.attributes[i].name + "=\"" + node.attributes[i].value + "\"";
			}
		}
		// close start tag
		s += '>';
		// content of tag
		s += this._getContentAsStringGeneric(node);
		// end tag
		s += '</' + node.nodeName + '>';
		return s;
	},
	
	enhanceExceptionPage:function(){
		jQuery('.toggle').click(function() { jQuery('#' + jQuery(this)[0].id+'Data').toggle(); return false;});
		//jQuery('.toggleContent').toggle();
		//jQuery('.toggleContent:first').show();		
	},

	_getContentAsStringIE:function(node){
		var s=""; //blank works around an IE-bug
    	for (var i = 0; i < node.childNodes.length; i++){
        	s += node.childNodes[i].xml;
    	}
    	return s;
	},

	_getContentAsStringMozilla:function(node){
        if (!this.xmlSerializer){ this.xmlSerializer = new XMLSerializer();}

	    var s = "";
        for (var i = 0; i < node.childNodes.length; i++) {
	        s += this.xmlSerializer.serializeToString(node.childNodes[i]);
	        if (s == "undefined")
		        return this._getContentAsStringGeneric(node);
        }

        return this._processCompactElements(s);
	},

	_getContentAsStringGeneric:function(node){
		var s="";
		if (node == null) { return s; }
		for (var i = 0; i < node.childNodes.length; i++) {
			switch (node.childNodes[i].nodeType) {
				case 1: // ELEMENT_NODE
				case 5: // ENTITY_REFERENCE_NODE
					s += this.getElementAsString(node.childNodes[i]);
					break;
				case 3: // TEXT_NODE
				case 2: // ATTRIBUTE_NODE
				case 4: // CDATA_SECTION_NODE
					s += node.childNodes[i].nodeValue;
					break;
				default:
					break;
			}
		}
		return s;
	},

	_processCompactElements:function(htmlData)
 	{
		 return htmlData.replace(this.CompactElementRegexp, this.CompactElementReplacer);
 	}
}

tapestry.provide("tapestry.core");
tapestry.provide("tapestry.html");
tapestry.provide("tapestry.event");
tapestry.provide("tapestry.lang");
tapestry.provide("tapestry.form");
tapestry.provide("tapestry.form.datetime");

