/**
 * @file	estimator.js
 * Core script for the MOS cost estimator tool, plus tooltip script from parent site.
 * @requires	pricing.js	All pricing info is set there.  See notes for DOM ID requirements.
 */

 
 
 //////////////////////////////////////////////////////////

/**
 * Tooltip scripts (from parent site)
 */
function showToolTip(oAnchorTag,e)
{
	xOffset = 50;
	yOffset = 20;
	contentToDisplay = document.getElementById(oAnchorTag.id + "Content").innerHTML;
	if (contentToDisplay != null && contentToDisplay != '') {
		toolTipContainer = document.getElementById("tooltip");
		toolTipContainer.style.top = getMouseY(e) - yOffset + "px";
		toolTipContainer.style.left = getMouseX(e) + xOffset + "px";
		toolTipContainer.innerHTML = contentToDisplay;
		toolTipContainer.style.display = "block";
	}
};
function hideToolTip()
{
		toolTipContainer = document.getElementById("tooltip");
		toolTipContainer.style.display = "none";
};
function getMouseX(e)
{
	mouseX = 0;
	if (e.clientX)
 	{
		mouseX = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
	}
	return mouseX;
};
function getMouseY(e)
{
	mouseY = 0;
	if (e.clientY) 	{
		mouseY = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
	}
	return mouseY;

};
// end Tooltip scripts




/**
 * packed MOS lib and utilities scripts
 */
eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('P.Z=8(a,b){n(7 c 1z b){k(t a[c]==\'H\'){a[c]=b[c]}}9 a};P.Z(1T.1N,{2n:8(){7 a=4.2g(4.l(\'?\')+1).q(\'&\');7 b={};7 c=a.h;n(7 i=0;i<c;i++){Y=a[i].q(\'=\');b[Y[0]]=Y[1]}9 b},M:8(a,b){k(b.1X(a)!=-1){9 m}s{9 A}},2x:8(){9 4.1b().F(/^\\w+\\:\\/\\//,\'\').q(\'/\')[0]}});P.Z(L.1N,{E:8(a){4[4.h]=a;9 4.h},1D:8(a,b){k(f.h==0)9 a;k(t a!=\'1C\')a=0;k(a<0)a=14.13(0,4.h+a);k(a>4.h){k(f.h>2)a=4.h;s 9[]}k(f.h<2)b=4.h-a;b=(t b==\'1C\')?14.13(0,b):0;1u=4.1t(a,a+b);11=4.1t(a+b);4.h=a;n(7 i=2;i<f.h;i++){4[4.h]=f[i]}n(7 i=0;i<11.h;i++){4[4.h]=11[i]}9 1u},l:8(a,b){k(b==C){b=0}s k(b<0){b=14.13(0,4.h+b)}n(7 i=b;i<4.h;i++){k(4[i]===a){9 i}}9-1},M:8(a){9 4.l(a)!=-1},Q:8(a){7 i=4.l(a);k(i!=-1){4.1D(i,1)}}});k(t 27.u==\'H\'){u=J P()};u.24={1l:A,X:8(a){a=(a)?m:A;4.1l=a}},u.20={1W:8(a,b,c){k(a.1h){a.1h(b,c,m);9 m}s k(a.1f){7 r=a.1f("1S"+b,c);9 r}s{9 A}},1Q:8(e){k(e){k(e.1j){e.1j();e.2w()}s{e.2t=A;e.2q=m}}s{};9 A}};u.1L={V:8(){4.G=C;4.I=C;4.S=C;4.1b=8(){9 4.G+\'.\'+4.I+\'.\'+4.S};4.X=8(a){7 v=a.q(\'.\');4.G=U(v[0])||0;4.I=U(v[1])||0;4.S=U(v[2])||0};4.1F=8(a){7 v=a.1b().q(\'.\');n(7 i=0;i<3;i++){v[i]=U(v[i])||0};T(m){y(v[0]<4.G):9 m;y(v[0]<=4.G&&v[1]<4.I):9 m;y(v[0]<=4.G&&v[1]<=4.I&&v[2]<=4.S):9 m;12:9 A}}}};u.1c={16:\'\',1B:\'\',15:{V:8(){4.1A=u.1L.V;4.1A()}},1y:8(){7 a=\'\';7 b=\'\';7 c=1x.2a.1w();T(m){y(c.l(\'1v\')!=-1):a=\'29\';7 i=c.l(\'1v\');7 v=c.K(i+5).q(\';\')[0];z;y(c.l(\'1s\')!=-1&&c.l(\'R:\')!=-1):a=\'1s\';7 i=c.l(\'R:\');7 v=c.K(i+3).q(\')\')[0];z;y(c.l(\'1r\')!=-1):a=\'28\';7 i=c.l(\'1r\');7 v=c.K(i).q(\' \')[0].q(\'/\')[1];z;y(c.l(\'10\')!=-1):a=\'10\';7 i=c.l(\'10\');7 v=c.K(i+6).q(\' \')[0];z;y(c.l(\'1p\')!=-1&&c.l(\'R:\')!=-1):a=\'1p\';7 i=c.l(\'R:\');7 v=c.K(i+3).q(\')\')[0];z};4.1B=a;4.15.V();4.15.X(v)},1o:8(){7 p=1x.16.1w();k(p.l(\'26\')!=-1){p=\'25\'}s k(p.l(\'1n\')!=-1){p=\'1n\'}s k(p.l(\'1m\')!=-1){p=\'1m\'}s{p=\'23\'};4.16=p}};u.1c.1y();u.1c.1o();u.18={22:8(a){a.21.1Z=\'1Y\'},1i:8(a){7 b=B.1V(a);n(7 i=1;i<f.h;i++){k(t f[i]==\'1g\'){b.W(4.1e(f[i]))}s k(t f[i]==\'O\'&&t f[i].1U!=\'H\'){b.W(f[i])}s k(t f[i]==\'O\'&&t f[i].h!=\'H\'){n(7 j=0;j<f[i].h;j++){b.W(f[i][j].1R(m))}}s k(t f[i]==\'O\'){4.1q(b,f[i])}};9 b},1q:8(a,b){n(7 c 1z b){T(m){y(c==\'D\'):a.D=b[c];z;y(c==\'1d\'):a.1d=b[c];z;12:a.1P(c,b[c])}}},1e:8(a){9 B.1O(a)},Q:8(a){7 b=J L();n(7 i=0;i<f.h;i++){7 c=(t f[i]==\'1g\')?4.1k(f[i]):f[i];b.E(c.2v.2u(c))};9(b.h>1)?b:b[0]},1k:8(a){7 b=J L();7 c;n(7 i=0;i<f.h;i++){7 d;k(d=B.2r(f[i])){b.E(d)}};T(m){y(f.h==1&&b.h==1):c=b[0];z;y(f.h>1):c=b;z;12:c=A;z};9 c},2p:8(a){7 b=J L();n(7 i=0;i<f.h;i++){1a=B.19(f[i]);n(7 j=0;j<1a.h;j++){b.E(1a[j])}};9 b},2o:8(a){7 o=J L();7 b=(t B.19!=\'H\')?B.19(\'*\'):B.2m;n(7 i=0;i<b.h;i++){(4.17(b[i],f))?o.E(b[i]):m};9 o},1K:8(a,b){7 c=a.D.q(\' \');(!c.M(b))?c.E(b):m;a.D=c.1J(\' \')},1I:8(a,b){7 c=a.D.q(\' \');(c.M(b))?c.Q(b):m;a.D=c.1J(\' \')},2l:8(a,b,c){k(4.17(a,b)){4.1I(a,b)};4.1K(a,c)},17:8(a,b){7 c=f;7 d=1;k(t f[1]==\'O\'){c=f[1];d=0};7 e=A;n(7 i=d;i<c.h;i++){k(a.D.q(\' \').M(c[i])){e=m;z}};9 e},1H:8(a,b){7 c=C;k(t a.1G!=\'H\'){c=a.1G}s{c=B.2k.1H(a,C)};9 c[b]}};u.2j={2i:8(a){a+=\'\';x=a.q(\'.\');N=x[0];1E=x.h>1?\'.\'+x[1]:\'\';7 b=/(\\d+)(\\d{3})/;2h(b.1F(N)){N=N.F(b,\'$1\'+\',\'+\'$2\')};9 N+1E},2f:8(a){k(!a){9 A}a=a.F(/\\$/g,\'\');9 a.F(/\\,/g,\'\')},2e:8(a){7 b=u.18.1i("2s");b.2d=a.F(/</g,"&2c;").F(/>/g,"&2b;");9 b.1M;b.1M=C;u.18.Q(b)}};',62,158,'||||this|||var|function|return||||||arguments||length|||if|indexOf|true|for|||split||else|typeof|MOS||||case|break|false|document|null|className|push|replace|major|undefined|minor|new|substring|Array|contains|x1|object|Object|remove|rv|rev|switch|parseInt|init|appendChild|set|query_pair|augment|opera|endArray|default|max|Math|version|platform|hasClass|dom|getElementsByTagName|els|toString|client|htmlFor|_text|attachEvent|string|addEventListener|create|preventDefault|getById|state|linux|mac|detectPlatform|gecko|_attributes|applewebkit|mozilla|slice|removeArray|msie|toLowerCase|navigator|detectBrowser|in|versionclass|browser|number|splice|x2|test|currentStyle|getComputedStyle|removeClass|join|addClass|clientObjectTemplate|value|prototype|createTextNode|setAttribute|stop|cloneNode|on|String|nodeName|createElement|add|search|block|display|event|style|show|unknown|ready|windows|win|window|safari|ie|userAgent|gt|lt|innerHTML|htmlEntityDecode|stripCommas|substr|while|addCommas|utilities|defaultView|swapClass|all|toQueryParams|getByClass|getByTag|cancelBubble|getElementById|textarea|returnValue|removeChild|parentNode|stopPropagation|getHostname'.split('|'),0,{}))




if (typeof window.MOS == 'undefined') {
	alert('Application Error:\n\nRequired MOS object does not exist.');
};


/**
 * Estimator
 * @requires	config.js	All pricing data is set there as JSON object.  See notes there for DOM ID requirements.
 * @version		2.1
 * @created 	October 2008
 * @revised		09 March 2009  New optional decimals in pricing display.
 */
MOS.estimator = {

	/**
	 *  _defaults
	 *  Required IDs and CSS classes, etc.  Can be selectively overridden in invocation of init method on target page.
	 *  @example
	 *  	Estimator.init({
	 *			'subtotalClass':	'my_subtotal_class_is_better_than_yours'
	 *		});
	 */
	_defaults: {
		'formId':					'estimator',
		'grandTotalId':				'grand_total',
		'subtotalClass':			'subtotal',
		'toggleClass':				'toggle',
		'minMsgClass':				'minimum_seats_msg',
		'spanRowsClass':			'spanrow',
		'newClass':					'new',
		'existingClass':			'existing',
		'showClass':				'display',
		'hideClass':				'no_display',
		'bposToggleId':				'customer_type', /* select menu */
		'bposInputClass':			'bpos',
		'bposMessageId':			'bpos_message',
		'bposMessageWidth_default':	'240px',
		'bposMessageWidth_wide':	'585px',
		'currency':					'',
		'showPageHeader':			false,
		'show_decimal':				true
	},
	
	init: function(aParams) {
		
		/* Get the names of our goodies: */
		var formId = (aParams && aParams['formId'] && aParams['formId'] != '')
			? aParams['formId']
			: this._defaults['formId'];
		var grandTotalId = (aParams && aParams['grandTotalId'] && aParams['grandTotalId'] != '')
			? aParams['grandTotalId']
			: this._defaults['grandTotalId'];
		var subtotalClass = (aParams && aParams['subtotalClass'] && aParams['subtotalClass'] != '')
			? aParams['subtotalClass']
			: this._defaults['subtotalClass'];
		var toggleClass = (aParams && aParams['toggleClass'] && aParams['toggleClass'] != '')
			? aParams['toggleClass']
			: this._defaults['toggleClass'];
		var minMsgClass = (aParams && aParams['minMsgClass'] && aParams['minMsgClass'] != '')
			? aParams['minMsgClass']
			: this._defaults['minMsgClass'];		
		var spanRowsClass = (aParams && aParams['spanRowsClass'] && aParams['spanRowsClass'] != '')
			? aParams['spanRowsClass']
			: this._defaults['spanRowsClass'];
		var newClass = (aParams && aParams['newClass'] && aParams['newClass'] != '')
			? aParams['newClass']
			: this._defaults['newClass'];
		var existingClass = (aParams && aParams['existingClass'] && aParams['existingClass'] != '')
			? aParams['existingClass']
			: this._defaults['existingClass'];
		var showClass = (aParams && aParams['showClass'] && aParams['showClass'] != '')
			? aParams['showClass']
			: this._defaults['showClass'];
 		var hideClass = (aParams && aParams['hideClass'] && aParams['hideClass'] != '')
			? aParams['hideClass']
			: this._defaults['hideClass'];
			
		var bposToggleId = (aParams && aParams['bposToggleId'] && aParams['bposToggleId'] != '')
			? aParams['bposToggleId']
			: this._defaults['bposToggleId'];
			
		var bposInputClass = (aParams && aParams['bposInputClass'] && aParams['bposInputClass'] != '')
			? aParams['bposInputClass']
			: this._defaults['bposInputClass'];
		var bposMessageId = (aParams && aParams['bposMessageId'] && aParams['bposMessageId'] != '')
			? aParams['bposMessageId']
			: this._defaults['bposMessageId'];
		var bposMessageWidth_default = (aParams && aParams['bposMessageWidth_default'] && aParams['bposMessageWidth_default'] != '')
			? aParams['bposMessageWidth_default']
			: this._defaults['bposMessageWidth_default'];
		var bposMessageWidth_wide = (aParams && aParams['bposMessageWidth_wide'] && aParams['bposMessageWidth_wide'] != '')
			? aParams['bposMessageWidth_wide']
			: this._defaults['bposMessageWidth_wide'];
			
		var currencyCheck = this._getCurrency();
		this.currency = (currencyCheck && currencyCheck !='')
			? currencyCheck
			: this._defaults['currency'];
		
		if (Estimator.Prices['show_decimal']===true) {
			this.show_decimal = true;
		} else if (Estimator.Prices['show_decimal']===false) {
			this.show_decimal = false;
		} else {
			this.show_decimal = this._defaults['show_decimal'];
		}
		
		/* Collect our goodies into properties: */
		this._form 						= MOS.dom.getById(formId);
		this._grandTotalField 			= MOS.dom.getById(grandTotalId);
		this._subtotalFields 			= MOS.dom.getByClass(subtotalClass);
		this._toggleTDs 				= MOS.dom.getByClass(toggleClass);
		this._minMsgClass 				= MOS.dom.getByClass(minMsgClass);
		this._toggleMsg 				= MOS.dom.getByClass(toggleClass);
		this._SpanRows 					= MOS.dom.getByClass(spanRowsClass);
		this._newClass					= newClass;
		this._existingClass				= existingClass;
		this._showClass					= showClass;
		this._hideClass					= hideClass;
		this._bposToggleId				= bposToggleId;
		this._bposProducts 				= MOS.dom.getByClass(bposInputClass);
		this._bposMessage				= MOS.dom.getById(bposMessageId);
		this.bposMessageWidth_default 	= bposMessageWidth_default;
		this.bposMessageWidth_wide		= bposMessageWidth_wide;


		/** 
		 * UI Failsafe:  if page not in iframe/frame, it's a standalone page.
		 * Set flag to display inline header div (default CSS = display: none;)
		 */
		try {
			if (window.frameElement == null) { this.showPageHeader = true; };
		} catch (e) {
			/* do nothing */
		};
		this._drawPageHeader(); 
		

		this._existingFields = [];
			tmpExistingFields = MOS.dom.getByClass(this._existingClass);
			var tally = 0;  var len = tmpExistingFields.length;
			for (var i=0; i< len; i++) {
				if (tmpExistingFields[i].tagName.toLowerCase() == 'input') {
					this._existingFields[tally] = tmpExistingFields[i];
					tally++;
				};
			};
			tmpExistingFields = null;
		
		this._newFields = [];
			tmpNewFields = MOS.dom.getByClass(this._newClass);
			var tally = 0;  var len = tmpNewFields.length;
			for (var i=0; i<len; i++) {
				if (tmpNewFields[i].tagName.toLowerCase() == 'input') {
					this._newFields[tally] = tmpNewFields[i];
					tally++;
				};
			};
			tmpNewFields = null;
			
		/* Now confirm we're good to go, and complain if not: */
		var message = [];
		if (!this._form) { message.push(' * Form not found by script.'); };
		if (typeof Estimator.Prices == 'undefined') { message.push(' * Pricing data not found.'); };
		if (this._existingFields.length == 0) { message.push(' * Data fields for existing seats not found.'); };
		if (!this._subtotalFields || this._subtotalFields.length == 0) { message.push(' * Subtotal form fields not found.'); };
		if (!this._grandTotalField) { message.push(' * Grand total form field not found.'); };
		if (message.length > 0) {
			var alertText = 'Application Error:\n\n' 
				+ message.join('\n') 
				+'\n\nThis tool may not function correctly.\nWe apologize for the inconvenience.';
			alert(alertText);
		};
		
		
		/* PROCEED */
		if (!alertText) {
			/* show/hide products per config settings in config.js */
			this._displayProducts();
			/* get current value of form toggler */
			var toggleSelect = document.getElementById(this._defaults['bposToggleId']) || Estimator._newClass;
			/* hide designated fields */
			this.toggleFormFields(toggleSelect);
		};
		
		/* clean up */
		message = null; alertText = null;
		
	},
	
	_drawPageHeader: function(sId) {
		var headerId = sId || 'header';
		if (this.showPageHeader === true) {
			var headerDiv = MOS.dom.getById(headerId);
			if (headerDiv) {
				/* show the inline header div */
				headerDiv.style.display = 'block'; /* yes, plz set 'block' explicitly */
			} else {
				/* Final failsafe: dynamically insert missing header div & HTML (in English) */
				var content = MOS.dom.getById('content');
				var headerDiv = MOS.dom.create('div',{id: headerId});
				headerDiv.innerHTML = '<div class="inner1"><div class="inner2">'
					+'<div class="headline"><h1>Microsoft Online Services Cost Estimator</h1></div>'
					+'<div class="button">'
						+'<a title="Close window" href="javascript:window.close();"><span>Close this window</span></a>'
					+'</div>'
					+'<div class="clear"></div></div></div>';
				var parentNode = content.parentNode;
				parentNode.insertBefore(headerDiv,content);
			};
		};
	},
	
	clearForm: function() {
		this._form.reset();
		this.showBposMessage();
	},
	

	
	/**
	 *  _displayProducts
	 *  Actually, this hides table rows in the form based on False boolean settings in Estimator.Prices['display_products'].
	 *  @require	config.js (or localized equivalent)	See file for further instructions.
	 */
	_displayProducts: function(){
		if (!Estimator.Prices['display_products']) { return false; };
		var hide_these = new Array();
		for (product in Estimator.Prices['display_products']) {
			if (Estimator.Prices['display_products'][product] === false) {
				var id= (product=='buy_button') ? product : 'row_'+ product;
				if (product == 'Business_Productivity_Online_Standard_Suite') {
					var id2 = id +'_B';
				};
				var tmp = document.getElementById(id);
				if (tmp) {
					tmp.style.display = 'none';
				};
				tmp = null;
				if (id2) {
					/* hide second row of BPOS */
					var tmp = document.getElementById(id2);
					if (tmp) {
						tmp.style.display = 'none';
					};
					tmp = null;
				};
			};
		};
	},
	
	/**
	 * _getCurrency
	 * Retrieve currency property of the Prices object, set in pricing.js
	 * @return 	string	Currency symbol, suitable for display in a form input.
	 */
	_getCurrency: function() {
		var curr = Estimator.Prices['currency'];
		if (curr.indexOf('$')===0 || curr == '') {
			/* dollar or null: accept */
		} else if (curr.indexOf('&')===0) {
			curr = MOS.utilities.htmlEntityDecode(curr);
		} else if (curr.match(/[a-zA-Z]/g) != null) {
			/* alpha: accept, but make sure has trailing space */
			if ( curr.substr(curr.length-1,curr.length) != ' ') {
				curr = curr +' ';
			}
		} else {
			/* we assume it's UTF-8 */
			alert('Error: currency setting may not use UTF-8 encoding. Sorry.');
			curr = false;
		};
		return (curr) ? curr : false;
	},
	
	

	/** 
	 * Locate appropriate price range for iCount.
	 * @return	mixed	name of price range within JSON object (oPrices)
	 */
	_findPriceRange: function(oPrices, iCount) {
		// oPrices = pricing JSON object
		if (!oPrices || oPrices == '' || typeof oPrices != 'object') { return false; };
		if (typeof iCount != 'number') { return false; };
		
		var aTmp = new Array();
		for (var prop in oPrices) { aTmp.push(prop); };
		
		var result = false;
		var len = aTmp.length;

		
		/* Deal with Chrome's "broken" object sorting */
		if ( navigator.userAgent.toLowerCase().indexOf('chrome') > -1 ){
			aTmp.sort(function(a,b){return a - b});
		} /* end Chrome hack */
		
		for (var i=0; i<len; i++) {	
			if (i === 0) {
				if (iCount <= parseInt(aTmp[i])) {
					result = parseInt(aTmp[i]);
					break;
				} else {
					if (iCount > parseInt(aTmp[i]) && iCount < parseInt(aTmp[i+1])) {
						result = parseInt(aTmp[i+1]);
						break;
					};
				};
			} else {
				var iTmp = i+1;
				/* 	if count = top of tier, use top of tier
					if count > top of tier && less than next top of tier:  use next top of tier
					else:  keep looking
				*/
				if (iCount === parseInt(aTmp[i])) {
					result = parseInt(aTmp[i]);
					break;
				} else if (iCount > parseInt(aTmp[i]) && iCount <= parseInt(aTmp[iTmp]) ) {
					result = parseInt(aTmp[iTmp]);
					break;
				};
			};
		}; // end for
		if (!result) { result = 'maximum'; };
		return result;
	},
	
	getPrice: function(oPrices, iNumber, pDoCalc) {
		if (!oPrices || oPrices == '' || typeof oPrices != 'object') { return false; };
		if (typeof iNumber != 'number') { return false; };
		pDoCalc= (pDoCalc) ? true : false;
		var range = this._findPriceRange(oPrices, iNumber);
		var price = oPrices[range].toFixed(2);
		if (pDoCalc) { var result = iNumber * price; }
		 else { var result = price; };
		
		return result.toFixed(2);
	},
	
	showBposMessage: function() {
		if (!this._bposProducts) { return false; };
		var exchangeFlag = false;
		var sharepointFlag = false;
		var tally = 0;
		var len = this._bposProducts.length;
		for (var i=0; i<len; i++) {
			if (!isNaN(this._bposProducts[i].value) && this._bposProducts[i].value > 0) {
				tally++;
				if ( this._bposProducts[i].id.match(/exchange/gi) ) {
					exchangeFlag = true;
				};
				if ( this._bposProducts[i].id.match(/sharepoint/gi) ) {
					sharepointFlag = true;
				};
			};
		};
		if ((exchangeFlag && sharepointFlag) || (tally >= 3)) {
			this._bposMessage.style.display = 'inline';
			this._setBposMessageWidth();
		} else {
			this._bposMessage.style.display = 'none';
		};
	},
	
	_setBposMessageWidth: function() {
		var span = this._checkSpanRows();
		if (span == 4) {
			this._bposMessage.style.width = this.bposMessageWidth_wide;
		} else {
			this._bposMessage.style.width = this.bposMessageWidth_default;
		};
	},
	
	_checkSpanRows: function() {
		var ret = false;
		for (var i=0; i<1; i++) { /* just do once */
			ret = this._SpanRows[i].colSpan
		};
		return ret;
	},
	
	updateGrandTotal: function() {
		if (!this._grandTotalField || !this._subtotalFields) { return false; };
		var len = this._subtotalFields.length;
		var subsTmp = 0;
		var err = 0;
		for (var i=0; i<len; i++) {
			var st = this._subtotalFields[i].value;
			/* strip currency symbol if present (likely, but don't assume) */
			if (st.indexOf(this.currency) > -1) {
				st = st.substr(this.currency.length);
			}
			st = parseFloat( MOS.utilities.stripCommas(st) );
			if (isNaN(st)) { 
				/* do nothing - null value (or perhaps invalid) */
			} else {
				subsTmp = subsTmp + st;
			};
		};
		
		this._grandTotalField.value = this._formatCurrency(subsTmp);
	},
	
	_findOutputField: function(cssClass) {
		var outputField = false;
		for (var i=0; i< this._subtotalFields.length; i++) {
			if ( MOS.dom.hasClass(this._subtotalFields[i], cssClass) ) {
				outputField = this._subtotalFields[i];
				break;
			};
		};
		return outputField;
	},
	_findInputFields: function(css) {
		/* gather designated input groups by "axis" class & remove the subtotal field from the collection */
		var thisInputGroup = MOS.dom.getByClass(css);
		for (var i=0; i< thisInputGroup.length; i++) {
			if ( MOS.dom.hasClass(thisInputGroup[i], 'subtotal') ) {
				thisInputGroup.splice(i, 1);
			};
		};
		return thisInputGroup;
	},
	_validateInput: function(fInput) {
		if (!fInput) { return false; };
		var val = fInput.value;
		while (val.match(/^0/)) {
			val = val.replace(/^0/,'');	
		};
		val = val.replace(/[a-zA-Z\,\$\.\!\@\#\%\^\&\*\(\)\-\+\=\<\>\?\{\}\[\]\~\`\/]/,'');
		fInput.value = val;		
	},
	
	processChange: function(fInput) {
		if (!fInput) { return false; };
		this._validateInput(fInput);
		//	this.updateGrandTotal();
		//	this.showBposMessage();
	},
	
	
	/**
	 * _getFormAxis
	 * @param 	{fInput}	form input object
	 * @return	{Object Array}	outputField: subtotal input object for the axis
	 *							thisInputGroup: array of user inputs for the axis
	 */
	_getFormAxis: function(fInput) {
		if (!fInput) { return false; }
		/* extract our row identifier:  .axis{number} */
		var cssTmp = fInput.className.split(' ');
		for (var i=0; i<cssTmp.length; i++) {
			var x = cssTmp[i].substring(0,4);
			if (x == 'axis') { 
				var css = cssTmp[i];
			};
		};
		if (!css) { alert('Error: required CSS class not found'); };
		
		/* get our input collections: */
		var outputField = this._findOutputField(css);
			if (!outputField) { alert('Error: subtotal field not found.'); };
		var thisInputGroup = this._findInputFields(css);
			if (!thisInputGroup) { alert('Error: user count fields not found.'); };
		
		var Axis = {
			'outputField': outputField,
			'thisInputGroup': thisInputGroup
		};
		
		return Axis;
	},
	
	
	_isBposEcal: function(sId) {
		if (!sId) { return false; };
		return (sId.match(/ecal$/i)) ? true : false;
	},
	_isBposCoreCal: function(sId) {
		if (!sId) { return false; };
		return (sId.match(/core_cal$/i)) ? true : false;
	},
	
	toggleFormFields: function(fSelection) {
		if (!this._toggleTDs) { return false; };
		var TDlen = this._toggleTDs.length;
		var MSGlen = this._minMsgClass.length;
		var val = (fSelection.value) ? fSelection.value : fSelection;
			
		/* handle colspans for the wide rows */
		var SpanLen = this._SpanRows.length;
		var wideSpan = 4;
		var thinSpan = 2;

		switch (val) {
			case 'new':
				for (var i=0; i<TDlen; i++) {
					MOS.dom.swapClass(this._toggleTDs[i], this._showClass, this._hideClass);
				};
				for (var i=0; i<MSGlen; i++) {
					MOS.dom.swapClass(this._minMsgClass[i], this._hideClass, '');
				};
				document.getElementById('h2existing').style.display = 'none';
				document.getElementById('h2new').style.display = 'block';
				
				//	document.getElementById('minimum_seats_msg').style.display = 'block';
				
				for (var i=0; i<SpanLen; i++) {
					this._SpanRows[i].colSpan = thinSpan;
				};
				this._clearHiddenFields();
				break;
			
			default:
				for (var i=0; i<TDlen; i++) {
					MOS.dom.swapClass(this._toggleTDs[i], this._hideClass, this._showClass);
				};
				for (var i=0; i<MSGlen; i++) {
					MOS.dom.swapClass(this._minMsgClass[i], this._showClass, this._hideClass);
				};
				document.getElementById('h2existing').style.display = 'block';
				document.getElementById('h2new').style.display = 'none';
				
				//	document.getElementById('minimum_seats_msg').style.display = 'none';

				for (var i=0; i<SpanLen; i++) {
					this._SpanRows[i].colSpan = wideSpan;
				};
		}; // end switch
		// reset width of upsell message as needed
		this._setBposMessageWidth();
	},
	
	_clearHiddenFields: function() {
		if (!this._existingFields || this._existingFields.length==0) { return false; };
		var len = this._existingFields.length;
		for (var i=0; i<len; i++) {
			this._existingFields[i].value = '';
			this.processChange(this._existingFields[i])
		};
	},
	
	show: function(aLink){
		return true;
	},
	
	
	/* Process subtotaling for one axis */
	_processExecute: function(thisInputGroup, outputField) {
		if (!outputField) 	{ alert('Error: subtotal field not found.'); };
		if (!thisInputGroup) { alert('Error: count fields not found.'); };
		
		/* gather data from inputs in group */
		var values = new Array;
		for (var i=0; i< thisInputGroup.length; i++) {
			var tmpVal 	= parseInt(thisInputGroup[i].value);
			if (MOS.dom.hasClass(thisInputGroup[i], this._existingClass)) {
				values[i] = {
					'type': this._existingClass,
					'id': thisInputGroup[i].id,
					'val': tmpVal,
					'css': thisInputGroup[i].className.split(' ')
				};
			} else {
				values[i] = {
					'type': this._newClass,
					'id': thisInputGroup[i].id,
					'val': tmpVal,
					'css': thisInputGroup[i].className.split(' ')
				};
			};
		}; /* end for */
		
		this._calculate(values, outputField);
		
	},  // end _processExecute
	
	
	/**
	 *  _calculate
	 *  Calculate and output subtotal for a single form axis.
	 *  @param	{values}	Custom array of input properties for one axis (set in _processExecute)
	 *  @param	{outputField}	Form input object of subtotal for that axis
	 */
	_calculate: function(values, outputField) {
		if (!values) { alert('Error: no values to calculate.'); }
		if (!outputField) { alert('Error: no subotal field to calculate to.'); }
		
		/* tally seats (new & old) */
		var totalSeats = 0;
		var fullPrice = 0;
		  var fullPriceId = false;
		var discount1 = 0;
		  var discount1Id = false;
		var discount2 = 0;
		  var discount2Id = false;
		
		for (var i=0; i<values.length; i++) {
			
			/* tally new seats */
			if (values[i].type == this._newClass && !isNaN(values[i].val)) {
				totalSeats = values[i].val;
				fullPrice = values[i].val;
				fullPriceId = values[i].id;
			};
		
			/* tally existing seats */
			if (values[i].type == this._existingClass && !isNaN(values[i].val)) {
				/* assign BPOS Core CAL seats to secondary reducor var */
				if ( this._isBposCoreCal( values[i].id ) ) {
					discount2 = discount2 + values[i].val;
					discount2Id = values[i].id;
				}
				else {
					discount1 = discount1 + values[i].val;
					discount1Id = values[i].id;
				};
			};
		}; /* end for */
		
		/* leaven our discount tallies */
		if (discount1>0 && discount1>totalSeats) {
			discount1 = totalSeats;
		};
		if (discount2>0 && discount2>totalSeats) {
			discount2 = totalSeats - discount1;
		};
		
		/* decrement fullPrice tally if/as appropriate */
		if (!isNaN(fullPrice) && fullPrice > 0) {
			if (!isNaN(discount1) && discount1>0) {
				fullPrice = fullPrice - discount1;
			};
		};
		/* if there's anything left... */
		if (fullPrice > 0 && (!isNaN(discount2) && discount2>0)) {
			fullPrice = fullPrice - discount2;
		} else {
			discount2 = 0;
		};
		/* sanity check */
		if (isNaN(fullPrice)) { fullPrice = 0; };
		
		
		var subtotal = 0;
		if (totalSeats > 0) {
			if (fullPrice > 0) {
				subtotal = parseFloat(subtotal)
						+  parseFloat( this.getPrice(Estimator.Prices[fullPriceId], fullPrice, true) );
			};
			if (discount1 > 0) {
				subtotal = parseFloat(subtotal)
						+  parseFloat( this.getPrice(Estimator.Prices[discount1Id], discount1, true) );
			};
			if (discount2 > 0) {
				subtotal = parseFloat(subtotal)
						+  parseFloat( this.getPrice(Estimator.Prices[discount2Id], discount2, true) );
			};
		};
		
		outputField.value = this._formatCurrency(subtotal);
		
	}, /* end _calculate */
	
	
	_formatCurrency: function(iNum){
		if (!iNum || iNum==null || typeof iNum != 'number' || iNum<=0) { return ''; }
		iNum= (this.show_decimal) ? iNum.toFixed(2) : parseInt(iNum);
		return (isNaN(iNum) || iNum<=0 || iNum===false) ? '' : this.currency + MOS.utilities.addCommas(iNum);
	},
	
	
	doSubmit: function() {
		if (!this._newFields || this._newFields.length === 0) {
			alert('Error: form fields not found by script.');
		}
		var len = this._newFields.length;
		for (var i=0; i<len; i++) {
			var stuff = this._getFormAxis(this._newFields[i]);
			this._processExecute(stuff['thisInputGroup'], stuff['outputField']);
		};
		
		this.updateGrandTotal();
		this.showBposMessage();
	}
	

}; // end MOS.estimator
Estimator = MOS.estimator;