/**
 * Alliance Software Content Management System
 *
 * @copyright Copyright &copy; 2004, Alliance Software
 * No unauthorised use without the express written permission
 * of Alliance Software
 *
 * @package core
 *
 */

//////////////////////////////////////////////////////////////////////////////

var debug;
if (debug === undefined) debug = false;

// Is variable empty?
function isEmpty(x)
{
	return (x === false) ||
			(x === undefined) ||
			(x === null) ||
			(x === '');
}

// Does variable have a valid value?
function isValid(x)
{
	return (x !== null) && (x !== undefined);
}

// Trim leading/trailing spaces from string
if (String.prototype.trim === undefined) {
	String.prototype.trim = function()
	{
		return isEmpty(this)
				? ''
				: this.replace(/^\s+|\s+$/g, '');
	}
}
//////////////////////////////////////////////////////////////////////////////
// Validate and do nothing
function nullValidate(field, fieldTitle, params)
{
	return null;
}

//////////////////////////////////////////////////////////////////////////////
// Trigger an error
// Used to expose missing field validation
function incompleteValidate(field, fieldTitle, params)
{
	if (debug) alert(fieldTitle + ' has no javascript validation');
	return null;
}

//////////////////////////////////////////////////////////////////////////////
// Validate a floating point number
function floatValidate(field, fieldTitle, params)
{
	var min = params.min;
	var max = params.max;

	// check for a number
	if (!field.value.match(/^-?[0-9]*\.?[0-9]*$/g)) {
		return fieldTitle + ' must be a number';
	}

	// check >= min
	if (isValid(min) && field.value < min) return fieldTitle + ' is too small';

	// check <= max
	if (isValid(max) && field.value > max) return fieldTitle + ' is too large';

	return null;
}

//////////////////////////////////////////////////////////////////////////////
// Validate an integer
function intValidate(field, fieldTitle, params)
{
	var r = floatValidate(field, fieldTitle, params);
	if (!isEmpty(r)) return r;

	// check for an integer
	if (!field.value.match(/^-?[0-9]+(\.0*)?$/g)) {
		return fieldTitle + ' must be a whole number';
	}

	return null;
}

//////////////////////////////////////////////////////////////////////////////
// Validate a string
function stringValidate(field, fieldTitle, params)
{
	var size = params.size;
	var regex = params.regex;
	var regexError = (params.regexError) ? String(params.regexError) : '';
	var minSize = params.minSize;

	// min & max size
	if (isValid(minSize) && field.value.length < minSize) {
		return fieldTitle + ' is not long enough';
	}

	if (isValid(size) && field.value.length > size) {
		return fieldTitle + ' is too long';
	}

	// regex
	if (isValid(regex) && !field.value.match(regex)) {
		return fieldTitle + " is not in the correct format\n"
			+ regexError;
	}

	return null;
}

// Validate a password
function passwordValidate(field, fieldTitle, params)
{
	return stringValidate(field, fieldTitle, params);
}

//////////////////////////////////////////////////////////////////////////////
// Validate a <select> by selecting everything
// Used in double-selects to select everything in the target list
function selectValidateSelectAll(field, fieldTitle, params)
{
	for (var i = 0; i < field.options.length; i++) {
		field.options[i].selected = true;
	}
	return null;
}

//////////////////////////////////////////////////////////////////////////////
// Validate that at least one checkbox is checked
function checkboxValidateAtLeastOne(field, fieldTitle, params)
{
	var checked = false;
	for (var i = 0; i < field.length; i++) {
		if (field[i].checked) checked = true;
	}
	if (!checked) {
		return fieldTitle + ' must have at least one selected item';
	}	
	return null;
}

//////////////////////////////////////////////////////////////////////////////
// Credit card expiry date validation
function cardExpiryValidate( field, fieldTitle, params) 
{
	// TODO
	return null;	
}

//////////////////////////////////////////////////////////////////////////////
// Form validation object
FormValidator = function () {
	var me = (this == window) ? {} : this;

	var entries = [];
		/* The entries array should eventially end up something like this:
			{
			fieldId: {	field : (document.getElementById('page_name')),
					fieldTitle : 'Page name',
					required : false
				},
			fieldId2: {	field : (document.getElementById('page_number')),
					fieldTitle : 'Page Number',
					validateFunction : intValidate,
					min : 6,
					max : 7,
					required : true
				}
			}
		*/

	/**
	 * Add a new validation Entry
	 * @param {} newEntry. @see entries format above.
	 */
	var addEntry = function(newEntry) {
		// default to numeric index
		var newId = entries.length;

		// but if they give us an object with an id, we'll use the id as the index
		if (typeof newEntry['field'] == 'object'
			&& newEntry['field'].id !== undefined)
		{
			newId = newEntry['field'].id;
		}

		entries[newId] = newEntry;
	}

	/**
	 * Perform validation and display necessary alerts
	 * @return false if any error encountered
	 */
	var validate = function() {
		if (FormValidator.disableValidation) return true;

		for (var index in entries) {
			// get entry and some properties
			var entry = entries[index];
			if (typeof entry == "function") continue;
			var field = entry.field;

			// ---- dont validate display:none fields ----
			// Only occurrs if jquery present
			if (typeof(jQuery) !== 'undefined') {
				// TODO: a non-jquery version of this
				var visible = true;
				$(field).parents().each(function() {
					if ('none' == $(this).css('display')) {
						visible = false;
					}
				});
				if (!visible) continue;
			}
			// -------------------------------------------
			
			var fieldTitle = entry.fieldTitle;
			var validateFunction = entry.validateFunction;

			// remove spaces
			if (typeof field == "object") {
				
				// need to get the actual content from a tinymce field
				if (/mceEditor/.test(field.className)) {
					if (typeof(tinyMCE.getContent) == 'function') {
						field.value = tinyMCE.getContent(field.id);
					} else {
						field.value = tinyMCE.get(field.id).getContent();
					}
				}
				
						
				var err = '';
				// precheck for empty fields (makes validation easier)
				if ((isEmpty(field.value) && isEmpty(field.checked))
					&& entry.required !== null)
				{
					if (entry.required) {
						err = (fieldTitle + ' must contain a value');
					}
				
				// validate the field
				} else {
					if (!isEmpty(validateFunction)) {
						err = validateFunction(field, fieldTitle, entry);
					}
				}

				// if we got an error, display it
				if (!isEmpty(err)) {
					alert('Error: ' + err);
					// set focus to the error field
					// without try/catch, if this call fails the form submit is not cancelled
					try {
						// Array, attempt focus on first element
						if (typeof field[0] != 'undefined') {
							field[0].focus();
						} else {
							field.focus();
						}
					} catch (e) {
					}
					return false;
				}
				
			} else if (typeof field == 'function') {
				
				var err = '';
				
				// multiple checkboxes that go into an array fall here
				if (!isEmpty(validateFunction)) {
					err = validateFunction(field, fieldTitle, entry);
				}
				
				// if we got an error, display it
				if (!isEmpty(err)) {
					alert('Error: ' + err);
					// set focus to the error field
					field[0].focus();
					return false;
				}
			} else {
				if (debug) alert('Field ' + fieldTitle + ' not found');
			}
		}
		return true;
	}

	// expose public functions
	me.addEntry = addEntry;
	me.validate = validate;
	return me;
}

FormValidator.validationEnabled = true;

//////////////////////////////////////////////////////////////////////////////
/**
 * Gets a form by id or name
 *
 * Will first look for an element with the specified ID; if not found, will
 * try with the specified name
 *
 * @param string formId
 * @return DOMElement
 */
function getForm(formId)
{
	var el = document.getElementById(formId);
	if (!el) el = document[formId];
	return el;
}

/**
 * Obtain the validator object for a form
 * 
 * Needed as an interface to backwards compat forms;
 *  some forms have name="...", some id="..."
 *
 * This checks for name first, if it can't find that looks for id
 *
 * @param string formName
 */
function getValidator(formName)
{
	var validator;

	// look for form element by name
	if (document.forms[formName]) {
		validator = document.forms[formName].validator;
	}
	
	// look for element by id
	if (document.getElementById(formName) && !validator) {
		validator = document.getElementById(formName).validator;
	}

	return validator;
}

/**
 * Initialise the validator for a form
 *
 * Follows same priority rules as getValidator()
 * 
 * @param string formName
 */
function initValidator(formName)
{
	var el;

	// look for form element by name
	if (document.forms[formName]) {
		el = document.forms[formName];
	}
	
	// look for element by id
	if (document.getElementById(formName) && !el) {
		el = document.getElementById(formName);
	}

	el.validator = new FormValidator();
	return el.validator;
}

/**
 * Enable/disable form validation (used in test scripts
 * so we can verify server side validation is working)
 *
 */
function formValidatorEnable(enabled)
{
	FormValidator.disableValidation = !enabled;
}

//////////////////////////////////////////////////////////////////////////////
/**
 * Run validation over a given form
 *
 * @param string formName form name/id to validate
 *
 * @see FormValidator.validate()
 */
function checkForm(formName)
{
	var validator = getValidator(formName);

	// validate if we can
	if (validator && typeof validator.validate == 'function') {
		return validator.validate();
	}

	// something went wrong
	if (typeof window.console.debug == 'function') {
		window.console.debug('Cannot find form validator for '+formname);
	}
	return true;
}

//////////////////////////////////////////////////////////////////////////////
/** 
 * Monitor focus/blur events on an element
 * Necessary because no JS way of detecting element focus
 *
 * @param mixed el (HtmlElement|jquery selector) to attach to
 * @param bool useAttribute (optional) Use isFocused attribute?
 * 							If no, will create a new object property
 *							Defaults to true
 *
 * Can be tested in selenium with assertAtribute('...@isFocused', true);
 */
function attachIsFocusedHook(el, useAttribute)
{
	if (useAttribute === undefined) useAttribute = true;

	if (useAttribute) {
		$(el)
			.focus(function() { $(this).attr('isFocused', true); console.debug(this); })
			.blur(function() { $(this).attr('isFocused', false); console.debug(this); })
			.attr('isFocused', null);
	} else {
		$(el)
			.focus(function() { this.isFocused = true; })
			.blur(function() { this.isFocused = false; })
			.each(function() { this.isFocused = null; });
	}
}

//////////////////////////////////////////////////////////////////////////////
// Move selected element value from <select> to text box
function moveSelectValue(form, selectFrom, textTo)
{
	from = form.elements[selectFrom];	
	if (from.options.length > 0 && from.selectedIndex >= 0) {
		form.elements[textTo].value = from.options[from.selectedIndex].value;
	}
}

//////////////////////////////////////////////////////////////////////////////
// Move highlighted elements in a <select> to another <select>
function moveSelectElements(form, selectFrom, selectTo)
{
	from = form.elements[selectFrom];
	to = form.elements[selectTo];

	while (from.selectedIndex >= 0) {
		if (from.options.length > 0) {
			for (var i = 0; i < from.options.length; i++) {
				while (i < from.options.length && from.options[i].selected == true) {
					var o = new Option(from.options[i].text, from.options[i].value, false, false);
					to.options[to.options.length] = o;
					from.options[i] = null;
				}
			}
		}
	}

	sortOptions(to);
}

// Sort the options in a select
function sortOptions(select)
{
	// copy out the options
	var options = new Array();
	for (var i = 0; i < select.options.length; i++) {
        options[i] = {
			'text' : select[i].text,
			'value' : select[i].value
			};
	}

	// sort
	options.sort(function(a,b) {
			if (a.text < b.text) return -1;
			if (a.text > b.text) return +1;
			return 0;
		});

	// remove old options
	while (select.options.length) select.options[0] = null;

	// insert new options back in
	for (var i = 0; i < options.length; i++) {
		var o = new Option(
					options[i].text,
					options[i].value,
					false,
					false);
		select.options[select.options.length] = o;
	}
}

//////////////////////////////////////////////////////////////////////////////

