/*
	Standard Product Configuration Constraint Interface
	Author: Chuck Ingrum, Nathan Huebner
*/

/* 
All material herein (c) 2004 TechniCon Corporation              
All Rights Reserved.

The source code is owned by TechniCon Corporation and is protected by  
copyright laws and international copyright treaties, as well as other   
intellectual property laws and treaties. COPYRIGHT. The source code is  
licensed, not sold. All right, title and interest in the source code    
(including any images, "applets," photographs, animations, video, audio,
music, and text incorporated into the source code), accompanying printed
materials, and any copies you are permitted to make herein, are owned by
TechniCon Corporation, and the source code is protected by United States 
copyright laws and international treaty provisions.  Therefore, you must
treat the source code like any other copyrighted material.      
*/

/*
	These functions are presented as standard examples of the application side
	framework api for the product configuration constraint system.
	
	The functions are all assigned to their framework api name at the bottom of this
	script.
	
	The functions developed here are expected to be application specific. This script
	is presented as a standard development that can be used as a base starting point
	for application development.
	
	Individual functions in this framework may be replaced by defining a function and
	assigning it to the standard api name after this script has been called.
	
	This entire script may be replaced for any single application by developing js function
	with the same api presented here and using them rather than this script.
	
	The use of this script with the constraint system is optional. These functions must
	be present when the domain_display.cfc component is used but alternates to those
	in this script can be utilized.
*/
var imageSelClassOK = "image_sel_class_ok";
var imageSelClassSelected = "image_sel_class_selected";
var imageSelClassProhibit = "image_sel_class_prohibit";
var imageSelClassInvalid = "image_sel_class_invalid";
var imageSelClassHover = "image_sel_class_hover";

function selImage(name, indx) {
	document.getElementById("dom_"+name).selectedIndex = indx;//+ eval('image_'+name+'_base');
	setDomainValue(name, document.getElementById("dom_"+name));
	formChange(name);
}

function selImageOver(oImg, domainID, index) {
	if (oImg.savebc == imageSelClassProhibit) {
		oImg.className = imageSelClassInvalid;
	} else if (oImg.savebc == imageSelClassSelected) {
		oImg.className = imageSelClassSelected;
	} else {
		oImg.className = imageSelClassHover;
	}
}

function selImageOut(oImg, domainID, index) {
	oImg.className = oImg.savebc;
}

function selImageSlideVis(name, pickID)
{
	var oDomFormEle =  getFormObject(name);
	var oDom = oDomains.domains[name];
	curpos = Math.max(0,pickID);

	// no slider
	for (i=0; i<oDomFormEle.options.length-1; i++) {
		choiceid = oDomFormEle.options[i+1].value;
		var oListImage = document.getElementById('pick_'+name+'_'+(i+1));
		if (oDom.members[choiceid].state == Domain.ChoiceRestricted) {
			oListImage.className = (i != pickID)?imageSelClassProhibit:imageSelClassInvalid;
			oListImage.style.cursor = 'not-allowed';
		} else {
			oListImage.className = (i != pickID)?imageSelClassOK:imageSelClassSelected;
			oListImage.style.cursor = 'pointer';
		}
		img_width = oListImage.width;
		oListImage.savebc = oListImage.className;
	}
	var objDiv = document.getElementById("scroll_div_"+name);
	if (objDiv.offsetWidth < objDiv.scrollWidth) {
		var leftside = objDiv.scrollLeft;
		var min_left = curpos*(img_width+5+6);
		var max_left = (curpos+1)*(img_width+5+6);
		var time = 0;
		if (objDiv.scrollLeft>min_left) {
			var distance = objDiv.scrollLeft-min_left;
			var step = distance/16;
			var time = 0;
			for (var j=0; j<7; j++) {
				setTimeout("document.getElementById('scroll_div_"+name+"').scrollLeft = "+Math.floor(objDiv.scrollLeft-(step*j)), time);
				time = time+30;
			}
			setTimeout("document.getElementById('scroll_div_"+name+"').scrollLeft = "+min_left, time);
		} else if ((objDiv.scrollLeft+objDiv.offsetWidth)<max_left) {
			var distance = max_left-(objDiv.scrollLeft+objDiv.offsetWidth);
			var step = distance/16;
			var time = 0;
			for (var j=0; j<7; j++) {
				setTimeout("document.getElementById('scroll_div_"+name+"').scrollLeft = "+Math.floor(objDiv.scrollLeft+(step*j)), time);
				time = time+30;
			}
			setTimeout("document.getElementById('scroll_div_"+name+"').scrollLeft = "+(max_left-objDiv.offsetWidth), time);
		}
	}
	return;
}


function stdFormChange(nTriggerDomain) {
	oDomains.evaluateRules(aExRules, oDomainToRuleMap, nTriggerDomain);
	
	// Cycle through Domains - Mark the domains in a conflict or warning state
	//								Mark the prohibited domain choices
	//								For each domain reset the form element.
	var choiceid;
	var oLabelEle, oSelectedLabelEle;
	var oDomMsgEle, oDomMsgSoftEle, oDomFormEle;
	for (var domid in oDomains.domains) {
		if (oDomains.domains.hasOwnProperty(domid)) {
			oDom = oDomains.domains[domid];
			if (oDom.dtype != "D_CONSTANT" && oDom.dtype != "") {
				oDomMsgEle = document.getElementById('conf_'+domid);
				oDomMsgSoftEle = document.getElementById('confsoft_'+domid);
				oDomOKEle = document.getElementById('choiceok_'+domid);
				if (oDomMsgEle != null && oDomMsgSoftEle != null) {
					oDomMsgEle.style.display = 'none';
					oDomMsgSoftEle.style.display = 'none';
					if (oDomOKEle) oDomOKEle.style.display = 'none';
					oDomFormEle =  getFormObject(domid);
					if (oDom.dtype == "D_LIST") {
						var form_type = getFormObjectType(oDomFormEle)
						switch (form_type) {
						case "select-one":
							//if (oDom.dsv == (-1)) {
							if (!oDom.hasSelection()) {
								oDomFormEle.selectedIndex = 0;
							}
							// set the select "no pick" option to be black so that it appears correctly
							// when the select is set to red/amber
							oDomFormEle.options[0].style.color = gNoConstraintColor;
							for (var i=1; i<oDomFormEle.options.length; i++) { //index 0 is not a domain member
								choiceid = oDomFormEle.options[i].value;
								if (oDom.members[choiceid].state == Domain.ChoiceRestricted) {
									oDomFormEle.options[i].style.color = gExcludedChoiceColor;
								} else {
									oDomFormEle.options[i].style.color = gNoConstraintColor;
								}
								if (oDom.hasSelection() && choiceid == oDom.getDSVChoice(0)) {
									oDomFormEle.selectedIndex = i;
									//This is set below
									//oDomFormEle.style.color = oDomFormEle.options[i].style.color;
								}
							}

							switch (oDom.contype) {
							case Rule.NoConstraint:
								if (oDom.hasSelection()) {
									if (oDomOKEle) oDomOKEle.style.display = '';
								}
								break;
							case Rule.SoftConstraint:
								oDomFormEle.options[oDomFormEle.selectedIndex].style.color = gSoftConstraintColor;
								oDomMsgSoftEle.style.display = '';
								break;
							case Rule.HardConstraint:
								oDomFormEle.options[oDomFormEle.selectedIndex].style.color = gHardConstraintColor;
								oDomMsgEle.style.display = '';
								break;
							}
							// set the select to be the color of the selected option
							oDomFormEle.style.color = oDomFormEle.options[oDomFormEle.selectedIndex].style.color;
							break;
						case "checkbox":
							// change the label to the "color"
							oLabelEle = document.getElementById('cblab'+domid);
							switch (oDom.contype) {
							case Rule.NoConstraint:
								oLabelEle.style.color = gNoConstraintColor;
								if (oDom.hasSelection()) {
									if (oDomOKEle) oDomOKEle.style.display = '';
								}
								break;
							case Rule.SoftConstraint:
								oLabelEle.style.color = gSoftConstraintColor;
								oDomMsgEle.style.display = ''
								break;
							case Rule.HardConstraint:
								oLabelEle.style.color = gHardConstraintColor;
								oDomMsgEle.style.display = '';
								break;
							}
							break;
						case "radio":
							oSelectedLabelEle = null;
							var choice_cnt = 0;
							for (var choiceid in oDom.members) {
								if (oDom.members.hasOwnProperty(choiceid)) {
									choice_cnt++;
								}
							}
							for (i=0; i<choice_cnt; i++) {
								oLabelEle = document.getElementById('cblab'+domid+'_'+(i+1));
								oDomFormEle = document.getElementById('dom_'+domid+'_'+(i+1));//"dom_"+blkindx+"_1"
								choiceid = oDomFormEle.value;
								if (oDom.members[choiceid].state == Domain.ChoiceRestricted) {
									oLabelEle.style.color = gExcludedChoiceColor;
								} else {
									oLabelEle.style.color = gNoConstraintColor;
								}
								if (oDom.hasSelection() && choiceid == oDom.getDSVChoice(0)) {
									oDomFormEle.checked = true;
									oSelectedLabelEle = oLabelEle;
								} else {
									oDomFormEle.checked = false;
								}
							}
							switch (oDom.contype) {
							case Rule.NoConstraint:
								//No Action
								if (oDom.hasSelection()) {
									if (oDomOKEle) oDomOKEle.style.display = '';
								}
								break;
							case Rule.SoftConstraint:
								oDomMsgSoftEle.style.display = '';
								oSelectedLabelEle.style.color = gSoftConstraintColor;
								break;
							case Rule.HardConstraint:
								oDomMsgEle.style.display = '';
								oSelectedLabelEle.style.color = gHardConstraintColor;
								break;
							}
							break;
						case "select-multiple":
							for (var i=0; i<oDomFormEle.options.length; i++) {
								choiceid = oDomFormEle.options[i].value;
								if (oDom.members[choiceid].state == Domain.ChoiceRestricted) {
									oDomFormEle.options[i].style.color = gExcludedChoiceColor;
								} else {
									oDomFormEle.options[i].style.color = gNoConstraintColor;
								}
								oDomFormEle.options[i].selected = false;
								for (var j=0; j<oDom.getNumberOfDSVChoices(); j++) {
									if (choiceid == oDom.getDSVChoice(j)) {
										oDomFormEle.options[i].selected = true;
										/*
										Check to see if this domain has a constraint associated with it.
										Check to see if this selected choice is referenced in any of
										the violated rules. If it is then color with the logic below.
										
										TODO: This algorithm does not take into account that there might be a mixture
											  of hard and soft constraints on choices selected in a domain. Each selection 
											  should be examined to find the highest constraint type to be applied to a choice.
											  This has not been done yet.
										*/
										if (oDom.contype != Rule.NoConstraint) {
											var oRule;
											for (var k=0; k<oDom.getNumberOfViolatedRules(); k++) {
												oRule = oDom.getViolatedRule(k);
												if (oRule.hasMemberChoice(domid, choiceid)) {
													switch (oDom.contype) {
													case Rule.SoftConstraint:
														oDomFormEle.options[i].style.color = gSoftConstraintColor;
														oDomMsgSoftEle.style.display = ''; 
														break;
													case Rule.HardConstraint:
														oDomFormEle.options[i].style.color = gHardConstraintColor;
														oDomMsgEle.style.display = '';
														break;
													default:
														alert("Unrecognized constraint type ("+oDom.contype+") encountered while displaying constraints.");
														break;
													}
												}
											}
										} else {									
											if (oDom.hasSelection()) {
												if (oDomOKEle) oDomOKEle.style.display = '';
											}
										}
									}
								}
							}
							break;
						}
					} else {
						var oDomRange = document.getElementById('dom_'+domid+'_range');
						switch (oDom.contype) {
						case Rule.NoConstraint:
							oDomFormEle.style.color = gNoConstraintColor;
							if (oDomRange) {
								oDomRange.style.color = gNoConstraintColor;
							}
							if (oDom.hasSelection()) {
								if (oDomOKEle) oDomOKEle.style.display = '';
							}
							break;
						case Rule.SoftConstraint:
							oDomFormEle.style.color = gSoftConstraintColor;
							if (oDomRange) {
								oDomRange.style.color = gSoftConstraintColor;
							}
							oDomMsgSoftEle.style.display = '';
							break;
						case Rule.HardConstraint:
							oDomFormEle.style.color = gHardConstraintColor;
							if (oDomRange) {
								oDomRange.style.color = gHardConstraintColor;
							}
							oDomMsgEle.style.display = '';
							break;
						}
					}
				}
				// may be an image picker
				if (null != document.getElementById('scroll_div_'+domid)) {
					// image picker
					if (!oDom.hasSelection()) {
						selImageSlideVis(domid, -1);
					} else {
						pickIndx = document.getElementById("dom_"+domid).selectedIndex;
						selImageSlideVis(domid, pickIndx-1);
					}
				}
			}
		}
	}
	ShowPartNumber();
}

var partNumberOptionSpacer = '';
function stdShowPartNumberAlternate() {
	var oCPNString = document.getElementById('cpn_partnumber');
	oCPNString.innerHTML = oDomains.getPartNumber().join(partNumberOptionSpacer);
	var oCPNState = document.getElementById('cpn_state');
	var sState = oDomains.getConstraintEngineState();
	switch(sState) {
	case 'Partial':
		oCPNState.style.color = '#997700';
		break;
	case 'Complete':
		oCPNState.style.color = '#00A000';
		break;
	case 'Invalid':
		oCPNState.style.color = '#CC0000';
		break;
	}
	oCPNState.innerHTML = sState;
}


/**
This domain demonstrates how the domain blocks can be cycled through to
get the domain objects and use them to evaluate the currection selection state.
The implementation here is for example only. It is basically a copy of the DomainMap 
function getPartNumber().
**/
function stdShowPartNumber() {
	var oDom;
	var aPNBlocks = new Array();
	for (var i=0; i<oDomains.cpcarray.length; i++) {
		oDom = oDomains.cpcarray[i];
		switch (oDom.dtype) {
		case Domain.Constant:
			//It is a constant - user can't pick
			aPNBlocks[i] = oDom.label;
			break;
		case Domain.List:
			aPNBlocks[i] = (oDom.hasSelection()) ? oDom.selectionString():oDomains.nopick;
			break;
		case Domain.Mixed:
		case Domain.IntegerRange:
		case Domain.DecimalRange:
			if (oDom.hasSelection()) {
				try {
					aPNBlocks[i] = DomInputValProcessor(oDom);
				} catch (myexcept) {
					aPNBlocks[i] = oDom.inputval;
				}
			} else {
				aPNBlocks[i] = new String(oDomains.nopick);
			}
			break;
		default:
			alert('Domain ('+oDom.label+') is an unsupported datatype ('+oDom.dtype+')');
			break;
		}
	}
	var oCPNString = document.getElementById('cpn_partnumber');
	oCPNString.innerHTML = aPNBlocks.join('');
	var oCPNState = document.getElementById('cpn_state');
	var sState = oDomains.getConstraintEngineState();
	switch(sState) {
	case 'Partial':
		oCPNState.style.color = '#807000';
		break;
	case 'Complete':
		oCPNState.style.color = '#00A000';
		break;
	case 'Invalid':
		oCPNState.style.color = '#A00000';
		break;
	}
	oCPNState.innerHTML = sState;
}


function stdDomInputValProcessor(oDom, oDomMember, numval) {
	/*
		Get the processing directives from oDomMember and format the number
	*/
	//var sFormattedValue=Number(numval).toString();
	var sFormattedValue = '';

	var sFormatString;
	var sInputString;
	var oNumValue;
	var sNumValue;
	
	//Argument Processing - Depending upon number of arguments provided
	switch (arguments.length) {
	case 1:
		sFormatString = oDom.getMember(oDom.getDSVChoice(0)).value;
		sInputString = oDom.inputval;
		break;
	case 2:
		sFormatString = oDomMember.value;
		sInputString = oDom.inputval;
		break;
	case 3:
		sFormatString = oDomMember.value;
		sInputString = numval;
		break;
	default:
		throw new Error("Unexpected number of arguments"+arguments.length+" in stdDomInputValProcessor");
	}

	if (Trim(sInputString) == '') {
		return sFormattedValue;
	}
	oNumValue = new Number(sInputString);
	if (isNaN(oNumValue.valueOf())) {
		throw new Error("stdDomInputValProcessor passed a non-numeric value ("+oDom.inputval+") to process");
	}
	sNumValue = oNumValue.toString();
	
    switch (oDom.dtype) {
    case Domain.IntegerRange:
        returnVal = integerFieldInputValProcessor(sFormatString, sNumValue);
        break;
    case Domain.Mixed:
        returnVal = mixedFieldInputValProcessor(sFormatString, sNumValue);
        break;
    case Domain.DecimalRange:
        returnVal = decimalFieldInputValProcessor(sFormatString, sNumValue);
        break;
    default:
        alert("Unrecognized domain type "+oDom.dtype+" in domain "+oDom.label+"will processing user input.");
    } 
    return returnVal;

}

function stdFieldInputValProcessor(sFormatString, sNumValue) {
	var sRange = sFormatString;
	var aRange; 
	if (sRange.charAt(0) == '['
		&& sRange.charAt(sRange.length-1) == ']') {
		aRange = sRange.slice(1,-1).split(',');
		if (aRange.length > 1) {
			var digits=parseInt(aRange[1]);
			if (!isNaN(digits)) {
				if (digits == 0) {
					sFormattedValue = Math.floor(sNumValue);
				} else {
					sFormattedValue = formatNumber(sNumValue, digits);
				}
				if (aRange.length > 2) {
					var sPre='';
					var sPost='';
					var nPos=aRange[2].indexOf('%');
					if (nPos >0) {
						sPre = aRange[2].slice(0, nPos);
						if (nPos < aRange[2].length-1) {
							sPost = aRange[2].slice(nPos+1);
						}
					} else if (nPos == 0) {
						sPost = aRange[2].slice(1);
					}
					sFormattedValue = sPre + sFormattedValue + sPost;
				}
			}
		}
	}
	return sFormattedValue;
}

function stdIntegerFieldInputValProcessor(sFormatString, sNumValue) {
	var sRange = sFormatString;
	var aRange; 
	if (sRange.charAt(0) == '['
		&& sRange.charAt(sRange.length-1) == ']') {
		aRange = sRange.slice(1,-1).split(',');
		if (aRange.length > 1) {
			var digits=parseInt(aRange[1]);
			if (!isNaN(digits)) {
				if (digits == 0) {
					sFormattedValue = Math.floor(sNumValue);
				} else {
					sFormattedValue = "0000000000"+Math.floor(sNumValue);
					sFormattedValue = sFormattedValue.substr(sFormattedValue.length-digits);
				}
				if (aRange.length > 2) {
					var sPre='';
					var sPost='';
					var nPos=aRange[2].indexOf('%');
					if (nPos >0) {
						sPre = aRange[2].slice(0, nPos);
						if (nPos < aRange[2].length-1) {
							sPost = aRange[2].slice(nPos+1);
						}
					} else if (nPos == 0) {
						sPost = aRange[2].slice(1);
					}
					sFormattedValue = sPre + sFormattedValue + sPost;
				}
			}
		}
	}
	return sFormattedValue;
}

function stdShowDomainMessages(domainid, oDiv, sTitle) {
	var wobj = self;
	var title = "Conflicts/Warnings"

	if (arguments.length > 1) {
		wobj = arguments[1];
	}
	if (typeof sTitle != "undefined") {
		title = sTitle;
	}
	displayHelp(getPositionX(wobj), getPositionY(wobj), title, getDomainConflictMessages(domainid, '<br>&nbsp;'), 300, 200);
}


function stdShowHelpMessage(sTitle, sText) {
	var wobj = self;
	if (arguments.length > 2) {
		wobj = arguments[2];
	}
	displayHelp(getPositionX(wobj)+wobj.offsetWidth, getPositionY(wobj), sTitle, sText, 300, 200)
}

function stdShowHelpWindow( windowURL, hgt, wid ) {
	var helpwindow = '';
	if ((hgt != 'undefined') && (wid != 'undefined')) {
		helpwindow = showHttpWindow(windowURL, 'help', 'width='+wid+',height='+hgt+',toolbar=0,location=0,directories=0,status=0,menuBar=0,scrollBars=1,resizable=1');
	} else {
		helpwindow = showHttpWindow(windowURL, 'help', 'width=400,height=300,toolbar=0,location=0,directories=0,status=0,menuBar=0,scrollBars=1,resizable=1');
	}
	helpwindow.focus();
	return helpwindow;
}

function Trim(str){
	if (str) {
		return str.toString().replace(/^\s+/,'').replace(/\s+$/,'');
	} else {
		return "";
	}
}

//These are the standard function names which are referred to in other 
//parts of the constraint framework code. Application specific versions of these
//functions may be defined and assigned to the standard names below to override
//use of the standard functions.
//
var formChange = stdFormChange;
var ShowPartNumberAlternate = stdShowPartNumberAlternate;
var ShowPartNumber = stdShowPartNumber;
var DomInputValProcessor = stdDomInputValProcessor;
var showDomainMessages = stdShowDomainMessages;
var showHelpMessage = stdShowHelpMessage;
var showHelpWindow = stdShowHelpWindow;
var integerFieldInputValProcessor = stdIntegerFieldInputValProcessor;
var decimalFieldInputValProcessor = stdFieldInputValProcessor;
var mixedFieldInputValProcessor = stdFieldInputValProcessor;