function addEvent(element, type, handler) {
	// assign each event handler a unique ID
	if (!handler.$$guid) handler.$$guid = addEvent.guid++;
	// create a hash table of event types for the element
	if (!element.events) element.events = {};
	// create a hash table of event handlers for each element/event pair
	var handlers = element.events[type];
	if (!handlers) {
		handlers = element.events[type] = {};
		// store the existing event handler (if there is one)
		if (element["on" + type]) {
			handlers[0] = element["on" + type];
		}
	}
	// store the event handler in the hash table
	handlers[handler.$$guid] = handler;
	// assign a global event handler to do all the work
	element["on" + type] = handleEvent;
};
// a counter used to create unique IDs
addEvent.guid = 1;

function removeEvent(element, type, handler) {
	// delete the event handler from the hash table
	if (element.events && element.events[type]) {
		delete element.events[type][handler.$$guid];
	}
};

function handleEvent(event) {
	var returnValue = true;
	// grab the event object (IE uses a global event object)
	event = event || fixEvent(window.event);
	// get a reference to the hash table of event handlers
	var handlers = this.events[event.type];
	// execute each event handler
	for (var i in handlers) {
		this.$$handleEvent = handlers[i];
		if (this.$$handleEvent(event) === false) {
			returnValue = false;
		}
	}
	return returnValue;
};

function fixEvent(event) {
	// add W3C standard event methods
	event.preventDefault = fixEvent.preventDefault;
	event.stopPropagation = fixEvent.stopPropagation;
	return event;
};
fixEvent.preventDefault = function() {
	this.returnValue = false;
};
fixEvent.stopPropagation = function() {
	this.cancelBubble = true;
};


/* ------------------------------------------------------------------------- 
Form library
--------------------------------------------------------------------------- */

function Form (options) {
	// assign form object
	this.form = options.form;
	// if form fields are encrypted (keySalt) use this.
	this.encrypt = options.encrypt || false;
	// validate the form
	this.validate = options.validate || true;
	// remember user details (save as cookie)
	this.remember = options.remember || false;
	// use ajax to submit data
	this.hijaxSubmit = options.hijaxSubmit || false;
	// use hbx to track
	this.tracking = options.tracking || false;
	// error message handling
	this.errMsg = {
		// Checks for when a specified field is required
		required: {
			msg: "This field is required.",
			test: function(obj,load) {
				// Make sure that something was not entered and that this
				// isn't on page load (showing 'field required' messages
				// would be annoying on page load)
				if (obj.getAttribute('type') === 'checkbox') {
					return obj.checked || load;
				} else
					return obj.value || load || obj.value == !obj.defaultValue;
				
			}
		},
		
		// Makes sure that the field s a valid email address
		email: {
			msg: "Not a valid email address.",
			test: function(obj) {
				// Make sure that something was entered and that it looks like
				// an email address
				
				return !obj.value || 
					/^[a-z0-9_+.-]+\@([a-z0-9-]+\.)+[a-z0-9]{2,4}$/i.test( obj.value );
			}
		},
		
		postcode: {
			msg: "Not a valid postcode.",
			test: function(obj) {
				
				var m = /(\d{4}).*(\d{3}).*(\d{3})/.exec( obj.value );
				if ( m ) obj.value = m[1];
				
				return !obj.value || /^[0-9]+$/.test( obj.value );
			}
		},
		
		firstname: {
			msg: "Not a valid first name.",
			test: function(obj) {	
				return !obj.value || /^[a-zA-z]+$/.test(obj.value);
			}
		},
		
		lastname: {
			msg: "Not a valid first name.",
			test: function(obj) {	
				return !obj.value || /^[a-zA-z]+$/.test(obj.value);
			}
		},
		
		// Makes sure that the field is a valid MM/DD/YYYY date
		date: {
			msg: "Not a valid date.",
			test: function(obj) {
				// Make sure that something is entered, and that it
				// looks like a valid MM/DD/YYYY date
				return !obj.value || /^\d{2}\/\d{2}\/\d{2,4}$/.test(obj.value);
			}
		}
	};
}

Form.prototype.init = function() {
	var obj = this;
	
	// is remember fields true
	if (this.remember) 
		// preload the values
		this.preloadValues(this.remember);
		
	if (this.validate) {
		this.watchForm();
		this.watchFields();
	}
}

Form.prototype.preloadValues = function(fields) {
	// check if there are any fields to remember
	if (fields) {
		// go through array of fields
		for (var i=0; i < fields.length; i++) {
			// if encryption, decifer to get true form name, otherwise use unencrypted
			var node = this.form[this.encrypt ? this.decipher(fields[i], document.getElementById("form-num").value) : fields[i]];
			// gather value from user cookie and place in form field
			try {
				var v = readCookie(fields[i]);
				if (v) node.value = v;
			} catch (e) {};
		}
	}
}

Form.prototype.saveValues = function(fields) {
	// check if there are any fields to remember
	if (fields) {
		// go through array of fields
		for (var i=0; i < fields.length; i++) {
			// if encryption, decifer to get true form name, otherwise use unencrypted
			var node = this.form[this.encrypt ? this.decipher(fields[i], document.getElementById("form-num").value) : fields[i]];	
			// gather value from user cookie and place in form field
			createCookie(fields[i], node.value, 1000);
		}
	}
}

Form.prototype.removeValues = function(fields) {
	// check if there are any fields to remember
	if (fields) {
		// go through array of fields
		for (var i=0; i < fields.length; i++) {
			// if encryption, decifer to get true form name, otherwise use unencrypted
			var node = this.form[this.encrypt ? this.decipher(fields[i], document.getElementById("form-num").value) : fields[i]];
			// gather value from user cookie and place in form field
			eraseCookie(node.getAttribute('name'));
		}
	}
}

Form.prototype.track = function() {
	try {
		if (this.tracking === 'storyComments') {
		    if(window.location.href.match(/story|comments/gi)) {
		        var sid = window.location.href.split(",")[2];
		        _hbSet("cv.c5", sid + "-" + hbx.mlc + "|" + hbx.pn);
		        _hbSend();
		    }
		} else if (this.tracking === 'blogComments') {
			_hbSet("cv.c6", window.location.href + "|" + hbx.pn);
    		_hbSend();
		}
	} catch(e) {
	    // no hbx available. leave
	}
}


Form.prototype.decipher = function(s, key) {
	// naming algorithm (keysalt)
	s = s.toLowerCase();
    var r = s.length - 4;
    var f = "";
    var x = 0;
    for(var i = 0; i != 4; i++) {
        f += s.charAt(parseInt((key.charAt(i))-1));
        x += (parseInt(f.charCodeAt(i)) - 96);
    }
    x = x - r;
    f = x + f;
    return f;
}

Form.prototype.hijax = function() {
	var obj = this;
	function appendData (element) {
		data+= element.name;
		data+= "=";
		data+= escape(element.value);
		data+= "&";
	}
    var data = "";
    for (var i=0; i<this.form.elements.length; i++) {
		if (this.form.elements[i].getAttribute('type') != 'radio') {
			appendData(this.form.elements[i]);
		} else if (this.form.elements[i].checked) {
			appendData(this.form.elements[i]);
		}
    }
	data += "ajax=true";
	
 	var request = ndm.ajax.getHTTPObject();
	  if (request) {
	    request.onreadystatechange = function() {
			if (request.readyState == 4) {
			  	if (request.status === 200) {

					obj.hijaxResponse(request.responseText);
				}
			}
		};
		
	    request.open( "POST", this.form.getAttribute('action'), true );
	    request.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
	    request.send(data);
		
	    return true;
		
	  } else {
	    return false;
	  }	 	
}

Form.prototype.hijaxResponse = function(response) {
	var obj = this;

	function performBack(message) {
		var tempStore = [];
		for (var x=0; x < obj.remember.length; x++) {		
			var rem = obj.form[obj.encrypt ? obj.decipher(obj.remember[x], document.getElementById("form-num").value) : obj.remember[x]];
			tempStore.push({element: rem, value: rem.value});
		}
		
		obj.form.reset(tempStore);
		
		for (var i=0; i < tempStore.length; i++ ) {
			tempStore[i].element.value = tempStore[i].value;
		}
			
		message.parentNode.removeChild(message);
		obj.form.style.display = 'block';
	}
	
	var messageWrap = document.createElement('div');
	messageWrap = this.form.parentNode.insertBefore(messageWrap, this.form);
	messageWrap.style.height = this.form.offsetHeight + 'px';
	messageWrap.style.width = this.form.offsetWidth + 'px';
	messageWrap.style.position = 'relative';
	this.form.style.display = 'none';
	
	var response = new ndm.common.message ({
		tagName: 'div',
		parent: messageWrap,
		type: 'parentCenter',
		styles: {
		width: '200px'
		},
		message: response
	});
	
	var c = response.node.getElementsByTagName('a');
	
	for (var i=0; i < c.length; i++) {
		if (c[i].className.indexOf('back') !== -1) {
			c[i].onclick = function() {
				performBack(messageWrap);
				return false;
			}
		}
	}


}

// A function for validating all fields within a form.
// The form argument should be a reference to a form element
// The load argument should be a boolean referring to the fact that
// the validation function is being run on page load, versus dynamically
Form.prototype.validateForm = function( load ) {
    var valid = true;
	
    // Go through all the field elements in form
    // form.elements is an array of all fields in a form
    for ( var i = 0; i < this.form.elements.length; i++ ) {
		
        // Hide any error messages, if they're being shown
        this.hideErrors( this.form.elements[i] );
        // Check to see if the field contains valid contents, or not
        if (this.validateField( this.form.elements[i], load ) )
            valid = false;

    }

    // Return false if a field does not have valid contents
    // true if all fields are valid
    return valid;
}

// Hide any validation error messages that are currently shown
Form.prototype.hideErrors = function ( elem ) {
    // Find the next element after the current field
    var next = elem.nextSibling;
	
    // If the next element is a ul and has a class of errors
    if ( next && next.nodeName == "UL" && next.className == "errors" )
        // Remove it (which is our means of  'hiding')
        elem.parentNode.removeChild( next );
}

// Show a set of errors messages for a specific field within a form
Form.prototype.showErrors = function ( elem, errors ) {
    // Find the next element after the field
    var next = elem.nextSibling;


    // If the field isn't one of our special error-holders.
    if ( next && ( next.nodeName != "UL" || next.className != "errors" ) ) {
        // We need to make one instead
        next = document.createElement( "ul" );
        next.className = "errors";
		
        // and then insert into the correct place in the DOM
        var error = elem.parentNode.insertBefore( next, elem.nextSibling );
		

    }

    // Now that we have a reference to the error holder UL
    // We then loop through all the error messages
    for ( var i = 0; i < errors.length; i++ ) {
		
        // Create a new li wrapper for each
        var li = document.createElement( "li" );
        li.innerHTML = errors[i];
        // and insert it into the DOM
        next.appendChild( li );
    }
}

// Validate a single field's contents
Form.prototype.validateField = function ( elem, load ) {
    var errors = [];

    // Go through all the possible validation techniques
    for ( var name in this.errMsg ) {
        // See if the field has the class specified by the error type
        var re = new RegExp("(^|\\s)" + name + "(\\s|$)");

        // Check to see if  the element has the class and that it passes the
        // validatino test
		
        if ( re.test( elem.className ) && !this.errMsg[name].test( elem, load ) ) {
            // If it fails the validation, add the error message to list
			
			var c = elem.parentNode.childNodes;	
            errors.push( this.errMsg[name].msg );
		} else 
			this.hideErrors(elem);
    }

    // Show the error messages, if they exist
    if ( errors.length )
        this.showErrors( elem, errors );

    // Return false if the field fails any of the validation routines
    return errors.length > 0;
}

Form.prototype.watchForm = function () {
	var obj = this;
	var submitField;
	
	for (var i=0; i < obj.form.elements.length; i++) {
		if (obj.form.elements[i].getAttribute('type') === 'submit') {
			submitField = obj.form.elements[i];
		}
	}

	submitField.onmousedown = function() {
		if(run()){
			obj.form.submit();
		}
		return false;
	}
	
	submitField.onclick = function() {
		return false;
	}
	 
	var run = function() {
       try {
			if (obj.form.remember.checked)
				obj.saveValues(obj.remember);
			else
				obj.removeValues(obj.remember);
	        // make sure that the form's contents validate correctly
		} catch (e) {}
        if (obj.validateForm()) {
			if (obj.tracking !== false) {
				obj.track();
			}
			if (obj.hijaxSubmit) {
				return (!obj.hijax());
			} else return true;
		} else obj.watchFields();
	}
	
    // Watch the form for submission
    this.form.onsubmit = function() {
		run();
    };
}


Form.prototype.watchFields = function () {
	var obj = this;
    // Go through all the field elements in form
    for ( var i = 0; i < this.form.elements.length; i++ ) {
        // and attach a 'blur' event handler (which watches for a user
        // to lose focus of an input element)
        addEvent( this.form.elements[i], 'blur',  function(){
           // Once the focus has been lost, re-validate the field
           return obj.validateField( this );
        });

    }
}

function formsInit() {
	if (document.forms.storyCommentForm) {

		var comments = new Form({
			'form'		  : document.forms.storyCommentForm,
			'validate'	  : true,
			'encrypt'	  : true,
			'remember'	  : ['fullName', 'location', 'email'],
			'hijaxSubmit' : true,
			'tracking'	  : 'storyComments'
		});
		
		comments.init();
	}
	
	if (document.forms.emailfriend) {
		var emailFriend = new Form({
			'form'		  : document.forms.emailfriend,
			'validate'	  : true,
			'remember'	  : ['yourName', 'yourEmail', 'message'],
			'hijaxSubmit' : true		
		});
		
		emailFriend.init();
	}

	if (document.forms.Submit) {
		var registration = new Form({
			'form'		  : document.forms.Submit,
			'validate'	  : true
		});
		
		registration.init();
	}
	
	// blog comment
	if (document.getElementById('comment_form')) {
		var blogForm = new Form({
			'form'		  : document.getElementById('comment_form'),
			'validate'	  : true,
			'remember'	  : ['name', 'location', 'email'],
			'tracking'	  : 'blogComments'
		});
		
		blogForm.init();
		
	}
	
	if (document.forms.WwbManagersForm) {
		var webManagersForm = new Form({
			'form'		  : document.forms.WwbManagersForm,
			'validate'	  : true
		});
		
		webManagersForm.init();
		
	}
	
}




