MDSG = {
//	SOLDTO_CHARS: 5,
//	OF: 'SAP',
	SOLDTO_CHARS: 7,
	OF: 'OF',
	_: ''
};

// some custom form-related utility functions
var FormUtils = {}; //namespacing
FormUtils.INPUTS = ['input', 'select', 'textarea'];

FormUtils.COMPANY_FIELDS = ['localFullName1', 'localFullName2'];
FormUtils.ADDRESS_FIELDS = ['addr1', 'addr2', 'addr3',	'city', 'countryCode', 'stateCode', 'postalCodeCity'];
FormUtils.ALL_ADDRESS_FIELDS = FormUtils.COMPANY_FIELDS.concat(FormUtils.ADDRESS_FIELDS);
FormUtils.CONTACT_FIELDS = ['salutation', 'firstName', 'lastName',	'title', 'primaryPhone', 'primaryEmail', 'primaryFax'];

//system characters, excluding ENTER, BACKSPACE, DELETE
FormUtils.FILTERED_INPUT_KEYS = (function() { with (Event) {
	return [KEY_TAB, KEY_ESC, KEY_LEFT, KEY_UP, KEY_RIGHT, KEY_DOWN, KEY_HOME, KEY_END, KEY_PAGEUP , KEY_PAGEDOWN, 16 ,17 ,18 , 45 /*ins*/];  
  }})();
  
FormUtils.DEFAULT_COUNTRY_STATE = {
	countryCode: "US",
	stateCode: "NY"
};

/**
 * tests whether given element is an INPUT form element
 * @param {Element} el
 */
FormUtils.isInput = function(el){
	return FormUtils.INPUTS.include(el.tagName.toLowerCase());
}
 
// a very basic form populate method
// renders data into DOM elements using DWRUtil methods and Prototype
FormUtils.renderValues = function(bean, beanName) {
  if (bean) {
  	  var prefix = beanName ? beanName + "." : "";
	  for (var prop in bean) {
	  	var elem = $(prefix + prop);
	  	if (elem) {	  
		    var value = bean[prop];
		  	var valueStr = "";		  	
		  	if (value instanceof Date) {
		  		valueStr = value.shortFormat();
		  	} else {
		  		valueStr = value;//value != null ? value.toString() : "";
		  	}	
	  		DWRUtil.setValue(elem, valueStr);
	  	}	
	  }
  }	  
}

FormUtils.getValues = function(bean, beanName, fields) {
  if (!bean) return;
  try {
  	  var prefix = beanName ? beanName + "." : "";
	  for (var prop in bean) {
	  	var fieldId = prefix + prop; 
	  	if (fields && fields.indexOf(fieldId) == -1)
	  		continue;	  	
	  	var elem = $(fieldId);
	  	if (elem) {
	  		//convert value into string and trim 
	  		var value = (""+DWRUtil.getValue(elem)).trim();
	  		if (elem.isDate) {
	  			value = value ? new Date(value) : null;
			}	
	  		//update bean property with adjusted value
	  		bean[prop] = value;	  		
	  	}	
	  }
   } catch(e) {alert("FormUtils.getValues err: " + e.message)}
}

FormUtils.fillBeanFromDocument = function(bean, prefix, options) {
		options = options || {};
    for (var property in bean) {
      // Are there any elements with that id or name
      var id = prefix + property;
      if ($(id) != null || document.getElementsByName(id).length >= 1) {
        bean[property] = DWRUtil.getValue(id);
      }
      //complex bean?
      if (options.complexBean && typeof property != 'function'){
      	if(prefix!=null && prefix.trim()!=""){
      		FormUtils.fillBeanFromDocument(bean[property], prefix + '.' + property);
	    }else{
	    	FormUtils.fillBeanFromDocument(bean[property], property);
	    }
      }
    }
    return bean;
}

FormUtils.fillDocumentFromBean = function(bean, prefix,options) {
	options = options || {};
	for (var property in bean) {
		// Are there any elements with that id or name
		var id = prefix + property;
		if ($(id) != null || document.getElementsByName(id).length >= 1) {
			DWRUtil.setValue(id, bean[property]);
		}
		//complex bean?
	    if (options.complexBean && typeof property != 'function'){
     	 	FormUtils.fillDocumentFromBean(bean[property], prefix + property+ '.');
	    }
	}
}

FormUtils.formatAddress = function(addressData, options) {
 	try {
		var opts = { multiline : false,	showCompany : false, skipNotSpecifiedState : true};
		opts = Object.extend(opts, options);
		var addrTxt = "";
		if (addressData) {	
			if (opts.showCompany && addressData.localFullName1)
				addrTxt+=addressData.localFullName1.trim() + (opts.multiline ? "<br/>" : ", ");
			addrTxt+=addressData.addr1 + (opts.multiline ? "<br/>" : ", ");
			if (addressData.addr2)
				addrTxt+=addressData.addr2 + (opts.multiline ? "<br/>" : ", ");
			if (addressData.addr3)
				addrTxt+=addressData.addr3 + (opts.multiline ? "<br/>" : ", ");
			if (addressData.city)	
				addrTxt+=addressData.city;
			if (addressData.stateCode && (addressData.stateCode.trim()!='XX' || opts.skipNotSpecifiedState==false))
				addrTxt+=", " + addressData.stateCode.trim();
			if (addressData.countryCode)
				addrTxt+=", " + addressData.countryCode.trim();
			if (addressData.postalCodeCity) {
				addrTxt+=", " + addressData.postalCodeCity.trim();
			}
		} else if (opts.messageOnEmpty) {
			return "<i>No address info available</i>";
		}
		return addrTxt;	
	} catch (e) { alert('FormUtils.formatAddress err: ' + e.message)};
}	

FormUtils.formatContact = function(contact, options) {
	try	{
		var opts = { multiline : false };
		opts = Object.extend(opts, options);
		var result = "";
		if (contact && !contact.empty) {
			result += (contact.salutation ? (contact.salutation + ' ') : '')
				+ (contact.firstName ? (contact.firstName + ' ') : '')
				+ (contact.lastName ? contact.lastName : '')
				+ (opts.multiline ? '<br />' : ' ');
			if (contact.primaryPhone && contact.primaryPhone.length>0) {
				result += contact.primaryPhone + ' (p)';
			}
			if (contact.primaryFax && contact.primaryFax.length>0) {
				result += ' ' + contact.primaryFax + ' (f)' + (opts.multiline ? '<br />' : ' ');
			} else {
				result +=  (opts.multiline ? '<br />' : ' ');
			}
			if (contact.primaryEmail && contact.primaryEmail.length>0) {
				var email = contact.primaryEmail;
				if (opts.showEmailLink) 
					email="<a href='mailto:" + email + "'>"+email+"</a>";
				result += email;
			}
		} else if (opts.messageOnEmpty) {
			return "<i>No contact info available</i>";
		}
		return result;	
	} catch (e) {
		alert('FormUtils.formatContact: ' + e.message)
	};
}	

FormUtils.formatSoldTo = function(soldToN, options) {
	options = options || {};
	if (soldToN) {
		return soldTo;
	} else if (options.messageOnEmpty){
		return '<i>- not set -</i>';
	}
}
/** prints out 'yes/no' for flag value 'Y/N' or 'true/false'*/
FormUtils.formatYesNo = function(flag) {
	return flag !== undefined ? (flag == "Y" || flag.toString() == "true" ? "Yes" : "No") : "N/A";
}

/**
 * This function binds country combobox with state combobox.
 * Same function is used for binding itself, and for country onchange event listener
 * 
 * @param {Object} countrySelect - the HTML select element for countries, or its id
 * @param {Object} stateSelect - the HTML select element for states, or its id
 * @param {Object} callOptions - different options to control the binding
 *  The following options could be set:
 *  doBind - whether to run bind initialization, every direct (not from a listener) call to this fuction usually sets this option
 *  selectedValue - this value will be initially selected in the states combobox
 *  prependOptions - which options should be added to the state combobox in addition to states, example: {prependOptions:{' ':'-- All --'}}. This behaviour can be changed by the 'mode' option
 *  mode - 'edit' or 'search'. Any mode implies doBind option and also adds this functionality:
 *  	'edit' mode - removes 'XX' and 'STL' countries, replaces 'XX' state's description with blank
 *  	'search' mode - removes 'XX' and 'STL' countries, removes 'XX' state, adds '-- All --' country and state with blank values (a space in fact - ' ', for better setValue/filter initializing handling)
 */
FormUtils.bindStates = function(countrySelect, stateSelect, callOptions) {
	if (!countrySelect || !stateSelect)	{
		alert("FormUtils.bindStates: countrySelect or stateSelect is not specified");
		return;
	}
	if (!FormUtils.states) {
		alert("FormUtils.bindStates: states aren't initialized");
		return;
	}	
	countrySelect = $(countrySelect);
	stateSelect = $(stateSelect);
	DWRUtil.removeAllOptions(stateSelect);
	var params = callOptions || {};
	DWRUtil.addOptions(stateSelect, params.prependOptions);
	DWRUtil.addOptions(stateSelect, FormUtils.states[$F(countrySelect)]);
	//slightly difficult to read condition, because only first call with mode implies doBind set
	var doBind = params.doBind || ((params.mode == 'edit' || params.mode == 'search') && params.doBind == undefined);
	if (params.mode == 'edit' || params.mode == 'search'){
		//remove 'Not Specified' and 'stateless' countries
		if (doBind) {
			for (i=0; i<countrySelect.options.length; i++){
				if (countrySelect.options.item(i).value=='XX' || countrySelect.options.item(i).value=='STL'){
					countrySelect.options[i]=null;
					i--;
				}
			}
		}
		//remove 'Not Specified' state
		for (i=0; i<stateSelect.options.length; i++){
			if (stateSelect.options.item(i).value=='XX'){
				stateSelect.options[i]=null;
				break;
			}
		}
	}
	var all = '-- All --';
	var currentCountryCode = DWRUtil.getValue(countrySelect);
	if (params.mode == 'edit' && currentCountryCode!='US' && currentCountryCode!='CA'){
		FormUtils.addOption(stateSelect, new Option(' ', 'XX'), 0);
	} else if (params.mode == 'search'){
		stateSelect.add(new Option(all, ' '), 0);
		if (doBind) countrySelect.add(new Option(all, ' '), 0);
	}
	if (params.selectedValue) {
		DWRUtil.setValue(stateSelect, params.selectedValue);
	} else if (params.mode == 'search'){
		stateSelect.selectedIndex = 0;
	}
	if (doBind) {
		//assign onchange handler to 'countryCode'
		//do not use Event.observe - linkAddresses will not work
		countrySelect.onchange = FormUtils.bindStates.bind(null, countrySelect, stateSelect, {prependOptions : params.prependOptions, mode : params.mode, doBind:false});
	}
}

FormUtils.populateCheckboxes = function(checkboxName, values) { 
    alert("populateCheckboxes");
	try{	
	var nodes = $A(document.getElementsByName(checkboxName));
	alert(nodes);
	nodes.each( function(node) {
		node.checked = (values.indexOf(node.value)!= -1);
	});
  }
  catch (e){
     alert("populate checkboxes:"+e.message);
  }	  
 }

FormUtils.parseAttr = function(value, prefix) {
	if (!value) return null;
	prefix = FormUtils.escapeRE(prefix);
	var match = value.match(prefix + '(.*)');
	return (match && match[1]) ? match[1] : null;
}

FormUtils.escapeRE = function(re){
	return re.replace(/\./g, '\\.');
}

FormUtils.createPrefixRE = function(prefix){
	prefix = FormUtils.escapeRE(prefix);
	return new RegExp("^" + prefix + "(.*)$");
}

//parses first group of given regexp (passes as string) in given value
FormUtils.parseId = function(value, regexp_s) {
	if (!value) return null;
	var match = value.match(regexp_s);
	return (match && match[1]) ? match[1] : null;
}

FormUtils.enableDisable = function(elem, doEnable, options) {
	options = options || {};
	options.clearSelect = (options.clearSelect==undefined) ? true : options.clearSelect;
	elem = $(elem);
	if (FormUtils.INPUTS.include(elem.tagName.toLowerCase())){
		elem.disabled = !doEnable;
		doEnable ? Element.removeClassName(elem, 'disabled') : Element.addClassName(elem, 'disabled');
		elem.disabled_x = elem.disabled;
	}
	if (!doEnable && elem.tagName.toLowerCase()=='select' && options.clearSelect){
		//clear select box
		elem.selectedIndex = -1;
	}
}

FormUtils.getInputs = function(baseElem) {
	baseElem = $(baseElem);
	var result = $A(baseElem.getElementsByTagName('input'));
	result.concat($A(baseElem.getElementsByTagName('select')));
	return result;
}

FormUtils.getElementsByIdPrefix = function(baseElem, prefix){
	prefix = prefix.replace(/\./g, '\\.');
	var children = $(baseElem).getElementsByTagName('*');
	var re = new RegExp("^" + prefix + "(.*)$");
  return $A(children).inject([], function(elements, child) {
    if (child.id.match(re))
      elements.push(Element.extend(child));
    return elements;
  });
}

FormUtils.parseQueryString = function(queryString){
	// Turn <plus> back to <space>
	// See: http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4.1
	queryString = queryString.replace(/\+/g, ' ');
	var args = queryString.split('&'); // parse out name/value pairs separated via &
	var result = new Object();
	
	// split out each name=value pair
	for (var i=0; i<args.length; i++) {
		var value;
		var pair = args[i].split('=')
		var name = unescape(pair[0]);
		if (pair.length == 2)
			value = unescape(pair[1]);
		else
			value = '';
		result[name] = value;
	}
	return result;
}


FormUtils.validationMessage = function(message){
	alert(message);
}

FormUtils.notificationMessage = function(message){
	alert(message);
}

FormUtils.USER_COMMENT_MAX_LENGTH = 4000;

FormUtils.validateVAN = function(VAN){
	return /^[\w \-_]{1,10}$/.test(VAN);
}

FormUtils.validateDate = function(dateStr) {
	return Date.validateDate(dateStr);
}

/** 
 * mark input field with error sign and change bg color
 * You should place 'error sign' placeholder close to input field similar to the following:
 * <span class="smallIcon errorSignTiny" id="<errElemId>" style="visibility : hidden"/>
 */
FormUtils.showError = function(elem, outputElem, text) {
	if (!$(outputElem)) return;
	Element.addClassName(elem, "error");
	Element.visibleOn(outputElem);
	$(outputElem).title = text;

}

/** remove error marks from field */
FormUtils.hideError = function(elem, outputElem) {
	if (!$(outputElem)) return;
	Element.removeClassName(elem, "error");
	Element.visibleOff(outputElem);	
}

/**
 * Assign event handler for 'instant' changes.  
 * For text input fields it will assign 'onKeypress' event handler instead of 'onChange'
 * 
 * @param {Object} fields an Array of input fields to be decorated 
 * @param {Object} eventHandler pointer to event handler
 */
FormUtils.observeInstantChanges = function(arg, eventHandler) {	
	var fields = arg;
	if (!arg.each){
		fields = new Array();
		fields.push(arg);
	}
	fields.each(function(field) {		
		var field = $(field);
		var fieldType = field.type.toLowerCase(); 
		//Event.observe for 'change' doesn't work when we change field value thru js
		//report as soon as user starts typing in text fields
		if (fieldType.match(/textarea|text/)) {
			//here we can wrap event handler into another event handler which will skips filter some of the keyCode
			field.observe("keyup",FormUtils._bindWithSystemKeysFilter(eventHandler));
		} else if (fieldType.match(/radio|checkbox/)) {
			field.observe("click", eventHandler);			
		} else {
			field.observe("change", eventHandler);
		}
	});	
}
/**
 * internal function, binds specified eventHandler to fire when only non-system key pressed 
 * @param {Object} eventHandler pointer to function
 */
FormUtils._bindWithSystemKeysFilter = function(eventHandler) {
	return function(event) {
		event = event || window.event;
		var srcKeyCode = event.keyCode;
		if (!FormUtils.FILTERED_INPUT_KEYS.find(function(key) {return srcKeyCode == key})) {
			eventHandler();
		}
	}
}

FormUtils.isEmptyString = function(s){
	return ('' + s).trim()=='';
}

FormUtils.isEmptyNumber = function(n){
	return n==0 || FormUtils.isEmptyString(n);
}

FormUtils.addOption = function(selectObject, newOption, num){
	var oldOption = selectObject.options[num];
	Try.these(
		function(){
			selectObject.add(newOption, num); //IE way
		},
		function(){
			selectObject.add(newOption, oldOption); //Standards way
		}
	);
}

/**
 * Fixes edit/textarea width bug in IE ()
 * Target Element must be hidden on initial page draw to not affect the drawing process
 * @param {HtmlElement} targetElement - target element
 * @param {Number} width - target width
 */
FormUtils.fixEditWidth = function(targetElement, width){
	targetElement.style.width = width + 'px';
	targetElement.style.display = 'block';
}

FormUtils.linkAddresses = function(prefixFrom, prefixTo, companySameCheckbox, addressSameCheckbox){
	FormUtils._linkHelper(FormUtils.COMPANY_FIELDS, prefixFrom, prefixTo, companySameCheckbox);
	FormUtils._linkHelper(FormUtils.ADDRESS_FIELDS, prefixFrom, prefixTo, addressSameCheckbox);
}

FormUtils.linkContacts = function(prefixFrom, prefixTo, contactSameCheckbox, options){
	options = options || {};
	if (options.withTestEmail){
		FormUtils._linkHelper(FormUtils.CONTACT_FIELDS.concat('primaryEmailTest'), prefixFrom, prefixTo, contactSameCheckbox);
	} else {
		FormUtils._linkHelper(FormUtils.CONTACT_FIELDS, prefixFrom, prefixTo, contactSameCheckbox);
	}
}

/**
 * private helper function for the FormUtils.linkAddresses and FormUtils.linkContacts methods
 */
FormUtils._linkHelper = function(fields, prefixFrom, prefixTo, chkBox){
	chkBox = $(chkBox);
	if (chkBox){
		fields.each(function(s){
			var elFrom = $(prefixFrom + s);
			var elTo = $(prefixTo + s);
			FormUtils.observeInstantChanges(elFrom, FormUtils._copyIfCheckboxSet.bind(null, elFrom, elTo, chkBox));
			//initial run
			FormUtils._copyIfCheckboxSet(elFrom, elTo, chkBox);
		});
		//no additional listeners for checkboxes, because of complex Mozilla Event model
		chkBox.onclick = function(){
			fields.each(function(s){
				var elFrom = $(prefixFrom + s);
				var elTo = $(prefixTo + s);
				FormUtils.enableDisable(elTo, !chkBox.checked, {clearSelect : false});
				FormUtils._copyIfCheckboxSet(elFrom, elTo, chkBox);
			});
		};
		//initial run
		chkBox.onclick();
	}
}

FormUtils._fireEvent = function(el, evnt){
	try { eval('el.' + 'on' + evnt + '()'); } catch (e) {}
	try { el.fireEvent('on' + evnt); } catch (e) {}
	//cant get Mozilla events wok correctly
	try {FormUtils._fireMozillaEvent(el, evnt); } catch (e) {}
}

//this doesn't work properly 
//select's onchange isn't calling observing listeners somehow
//checkox's click checks the checkbox
//but at least 'keyup' works fine
FormUtils._fireMozillaEvent = function(el, evnt){
	var eventObject;
	switch (evnt) {
		case 'DOMActivate':
		case 'DOMFocusIn':
		case 'DOMFocusOut':
			// UIEvent
			eventObject = el.ownerDocument.createEvent('UIEvents');
			eventObject.initUIEvent(evnt, true, true, el.ownerDocument.defaultView, 1);
			break;

		case 'click':
		case 'mousedown':
		case 'mousemove':
		case 'mouseout':
		case 'mouseover':
		case 'mouseup':
		case 'contextmenu': // proprietary Gecko event
			// MouseEvent
			eventObject = el.ownerDocument.createEvent('MouseEvents');
			eventObject.initMouseEvent(evnt, true, true, el.ownerDocument.defaultView, 1, 
				el.ownerDocument.getBoxObjectFor(el.ownerDocument.getElementsByTagName('body').item(0)).screenX, 
				el.ownerDocument.getBoxObjectFor(el.ownerDocument.getElementsByTagName('body').item(0)).screenY, 
				0, 0, false, false, false, false, 0, el);
			break;

		case 'keydown':
		case 'keypress':
		case 'keyup':
			// Gecko-only KeyEvent - to be replaced by TextEvent in DOM3 Events
			eventObject = el.ownerDocument.createEvent('KeyEvents');
			eventObject.initKeyEvent(evnt, true, true, el.ownerDocument.defaultView, false, false, false, false, 0, 0);
			break;

		case 'DOMAttrModified':
		case 'DOMCharacterDataModified':
		case 'DOMNodeInserted':
		case 'DOMNodeInsertedIntoDocument':
		case 'DOMNodeRemoved':
		case 'DOMNodeRemovedFromDocument':
		case 'DOMSubtreeModified':
			// MutationEvent
			eventObject = el.ownerDocument.createEvent('MutationEvents');
			eventObject.initMutationEvent(evnt, true, false, el, null, null, null, MutationEvent.MODIFICATION);
			break;

		case 'abort':
		case 'blur':
		case 'change':
		case 'error':
		case 'focus':
		case 'load':
		case 'reset':
		case 'resize':
		case 'select':
		case 'scroll':
		case 'submit':
		case 'unload':
			// HTMLEvent
			eventObject = el.ownerDocument.createEvent('HTMLEvents');
			eventObject.initEvent(evnt, true, true);
			break;
		
		default:
			// CustomEvent
			eventObject = el.ownerDocument.createEvent('Events');
			eventObject.initEvent(evnt, true, true);
	}
	el.dispatchEvent(eventObject);
}

FormUtils._copyIfCheckboxSet = function(elFrom, elTo, chkBox){
	if (chkBox.checked){
		DWRUtil.setValue(elTo, DWRUtil.getValue(elFrom));
		FormUtils._fireEvent(elTo, FormUtils._getChangingEventType(elTo));
	}
}

/**
 * binds toUppercase function on element contents change
 * argument can be single element (or elementId), or an array of elements (or element ids)
 * @param {Object} arg
 */
FormUtils.bindUppercase = function(arg, prefix){
	var fields = arg;
	if (!arg.each){
		fields = new Array();
		fields.push(arg);
	}
	fields.each(function(field) {
		if (prefix){
			field = prefix + field;
		}
		field = $(field);
		if (field && field.type.toLowerCase().match(/textarea|text/)) {
			//field.observe("keyup",FormUtils._bindWithSystemKeysFilter(FormUtils._uppercase.bind(null, field)));
			//this is for the clipboard
			field.observe("blur",FormUtils._bindWithSystemKeysFilter(FormUtils._uppercase.bind(null, field)));
		}
	});	
}

FormUtils._uppercase = function(el){
	if (!el.preventObserve_uppercase){
		el.value=el.value.toUpperCase();
		//preventing recursion
		el.preventObserve_uppercase = true;
		FormUtils._fireEvent(el, 'keyup');
		el.preventObserve_uppercase = false;
	}
}

FormUtils._getChangingEventType = function(el){
	var fieldType = el.type.toLowerCase(); 
	if (fieldType.match(/textarea|text/)) {
		return 'keyup';
	} else if (fieldType.match(/radio|checkbox/)) {
		return 'click';
	} else {
		return 'change';
	}
}

FormUtils.enableDisableForm = function(form, doEnable){
	Form.getElements(form).each(function(elem){
		if (doEnable && !elem.disabled_x){
			FormUtils.enableDisable(elem, true);
		} else {
			FormUtils.enableDisable(elem, false, {clearSelect: false});
		}
	});
}

/**
 * Submits form, then disables all form's fields to prevent user interaction
 */
FormUtils.submitDisabledForm = function(theForm){
	theForm.submit();
	FormUtils.enableDisableForm(theForm, false);
	return false;
}
