// work-around for lack of "readyState" in firefox
document.readyState_ai = "loading";
document.observe("dom:loaded", function() { document.readyState_ai = "complete"; });
var anderweb_common = "http://www.anderweb.net/common_includes";

window.globalEval = (function() 
{
	if(typeof window.execScript != 'undefined') {
		return function(str) { window.execScript(str); };
	}
	if(!Prototype.Browser.Opera && !Prototype.Browser.WebKit && typeof window.eval != 'undefined') {
		return function(str) { window.eval(str); };
	}
	return function(str) {
		var head, script;
		head = $$('head')[0];
		if(head) 
		{
			script = new Element('script', {'type': 'text/javascript'});
			script.appendChild(document.createTextNode(str));
			head.appendChild(script);
		}
	};
})();




var CurtainGenerator = Class.create({
	containerDiv:null, 
	curtainDiv:null, curtain_color:"black", curtain_transparency:0.6,
	dialogDiv:null, dialog_width:400, dialog_height:null, callback_dialogReady:null,
	selectBoxes:null,
	
	initialize: function(showNow, curtain_color, curtain_transparency) {
		if(typeof(color) == "string")
			this.curtain_color = curtain_color;
		if(typeof(transparency) == "number")
			this.curtain_transparency = curtain_transparency;
		
		if(typeof(showNow) == "boolean" && showNow)
		{
			if(document.readyState_ai == "loading")
				document.observe("dom:loaded", this.toggleCurtain.bindAsEventListener(this));
			else
				this.toggleCurtain();
		}
	},
	
	
	toggleCurtain: function() {
		// hide curtain
		if(this.containerDiv)
		{
			Effect.Shrink(this.dialogDiv, { duration:1 });
			Effect.Fade(this.curtainDiv, { duration:2, from:this.curtain_transparency, afterFinish:this.toggleCurtain_cleanup.bind(this) });
		}
		
		// show curtain
		else
		{
			this.containerDiv = new Element("div", { style:"position:absolute; top:0px; left:0px;" });
			
			// hide all select boxes (ie6 bug)
			this.selectBoxes = $$("select");																// select boxes within the page
			var selectBoxes_dialog = this.containerDiv.select("select");		// select boxes within the dialog (don't want to hide these!)
			// iterate through the page's select boxes
			for(var i=0; i<this.selectBoxes.length; i++)										
			{
				// iterate through dialog's select boxes
				for(var j=0; j<selectBoxes_dialog.length; j++)
				{									
					// remove from page's select boxes array so they don't get hidden
					if(this.selectBoxes[i] == selectBoxes_dialog[j])								
						this.selectBoxes[i] = null;
				}
				// hide the box if it made it through the loop above
				if(this.selectBoxes[i])
					this.selectBoxes[i].hide();	
			}
			// compact any holes in the array
			this.selectBoxes = this.selectBoxes.compact();
			
			// make curtain
			this.curtainDiv = new Element("div", { style:"background-color:" + this.curtain_color + "; position:absolute; display:none; top:0px; left:0px;"});
			this.containerDiv.insert(this.curtainDiv);
			Event.observe(document.onresize ? document : window, "resize", this.resizeCurtain.bindAsEventListener(this)); 
			this.resizeCurtain();
			
			// make dialog box
			this.dialogDiv = new Element("div", { 
				style:
					"position:absolute; display:none;" + 
					"top:" + (document.viewport.getScrollOffsets().top + Math.round(document.viewport.getHeight() / 2) - Math.round(this.dialog_height / 2)) + "px; " +
					"left:" + (Math.round(document.viewport.getScrollOffsets().left + document.viewport.getWidth() / 2) - Math.round(this.dialog_width / 2)) + "px; " + 
					"width:" + this.dialog_width + "px; " + 
					(this.dialog_height ? ("height:" + this.dialog_height + "px; ") : "") + 
					"border:5px solid white; background-color:#eeeeee;" 
				}
			);
			this.containerDiv.insert(this.dialogDiv);
			
			// add to dom tree
			$(document.body).insert(this.containerDiv);
			
			Effect.Appear(this.curtainDiv, { duration:0.5, to:this.curtain_transparency, afterFinish:this.toggleCurtain_showDialog.bind(this) });
			
			return this.dialogDiv;
		}
	},
	
	toggleCurtain_showDialog: function() {
		Effect.Grow(this.dialogDiv, { afterFinish:this.callback_dialogReady });
	},
	
	resizeCurtain: function() {
		var width, height;
		
		// width
		if(document.viewport.getWidth() > (document.body.offsetWidth + document.body.offsetLeft))
			width = document.viewport.getWidth();
		else
			width = document.body.offsetWidth + document.body.offsetLeft;
		
		// height
		if(document.viewport.getHeight() > (document.body.offsetHeight + document.body.offsetTop))
			height = document.viewport.getHeight();
		else
			height = document.body.offsetHeight + document.body.offsetTop;
		
		this.curtainDiv.setStyle({ width:width + "px", height:height + "px" });
	},
	
	toggleCurtain_cleanup: function() {
		// re-display the select boxes, stupid ie6 bug...
		for(var i=0; i<this.selectBoxes.length; i++)
			this.selectBoxes[i].show();
		this.selectBoxes = null;
		
		this.containerDiv.remove(); 
		this.containerDiv = null;
	}
});










var CurtainGenerator2 = Class.create({
	containerDiv:null, curtainDiv:null, loadingDiv:null, dialogTransport:null, selectBoxes:null, iframes:null, closerDiv:null,
	options:null,
	dialogDiv:null, 												// use dialogDiv to throw custom content into the dialog box (in case you don't provide contentUrl
	dialogDiv_animating:false,							// stores state of animation (ie, is the dialog in the middle of opening or closing?
	
	initialize: function(opts) {
		var default_args = {
			contentUrl:"",											// location of the html/javascript code to throw in our dialog box
			showNow:false,											// display prompter upon initialization of this object (or page load, if page isn't done loading)
			showCloseOption:false,							// this forces a close link to appear above the dialog box (useful in development in case the dialog hangs and you can't close it
			curtain_transparency:0.6,						// level of curtain transparency (between 0 and 1)
			curtain_color:"#000000",						// defaults to black
			dialog_width:600,										
			dialog_height:null,									// null indicates the height will auto-adjust according to the content within
			dialog_border:"5px solid white",
			dialog_backgroundColor:"#eeeeee",
			dialog_padding:10,
			callback_dialogReady:null,					// called when the curtain fully fades in and the dialog box is displayed and ready to receive content
			callback_dialogDone:null,						// called when the curtain fully fades out, useful for any cleanup or additional action that needs to be taken
			debug_curtainHeight:null						// useful during debugging; controls the height of the curtain so you can still click behind the dialog box (non-modal)
		};
		for(var i in default_args)
			if(typeof opts[i] == "undefined") 
				opts[i] = default_args[i];
		this.options = opts;

		// roll curtain now!
		if(this.options["showNow"])
		{
			if(document.readyState_ai == "loading")
				document.observe("dom:loaded", this.toggleCurtain.bindAsEventListener(this));
			else
				this.toggleCurtain();
		}
	},
	
	
	toggleCurtain: function() {
		// hide curtain
		if(this.containerDiv)
		{
			if(this.closerDiv)
				this.closerDiv.remove();
			this.dialogDiv_animating = true;
			Effect.Shrink(this.dialogDiv, { duration:1 });
			Effect.Fade(this.curtainDiv, { duration:2, from:this.options["curtain_transparency"], afterFinish:this.toggleCurtain_cleanup.bind(this) });
		}
		
		// show curtain
		else
		{
			// the container will hold everything
			this.containerDiv = new Element("div", { style:"position:absolute; top:0px; left:0px;" });
			$(document.body).insert(this.containerDiv);
			
			// hide all select boxes (ie6 bug)
			this.selectBoxes = $$("select");																// select boxes within the page
			var selectBoxes_dialog = this.containerDiv.select("select");		// select boxes within the dialog (don't want to hide these!)
			for(var i=0; i<this.selectBoxes.length; i++)										// iterate through the page's select boxes
			{
				for(var j=0; j<selectBoxes_dialog.length; j++)								// iterate through dialog's select boxes
					if(this.selectBoxes[i] == selectBoxes_dialog[j])						// remove from page's select boxes array so they don't get hidden
						this.selectBoxes[i] = null;
				if(this.selectBoxes[i])																				// hide the box if it made it through the loop above
					this.selectBoxes[i].hide();	
			}
			this.selectBoxes = this.selectBoxes.compact();									// compact any holes in the array
			// hide all iframes
			this.iframes = $$("iframe");
			var iframes_dialog = this.containerDiv.select("iframe");
			for(var i=0; i<this.iframes.length; i++)
			{
				for(var j=0; j<iframes_dialog.length; j++)
					if(this.iframes[i] == iframes_dialog[j])
						this.iframes[i] = null;
				if(this.iframes[i])
					this.iframes[i].setStyle({visibility:"hidden"});
			}
			this.iframes = this.iframes.compact();
			
			// make curtain
			this.curtainDiv = new Element("div", { style:"background-color:" + this.options["curtain_color"] + "; position:absolute; display:none; top:0px; left:0px;"});
			this.containerDiv.insert(this.curtainDiv);
			Event.observe(document.onresize ? document : window, "resize", this.resizeCurtain.bindAsEventListener(this));				// resize curtain to browser
			this.resizeCurtain();
			Effect.Appear(this.curtainDiv, { duration:0.5, to:this.options["curtain_transparency"] });
			
			// load the dialog, next
			this.toggleCurtain_loadDialog();
		}
	},
	
	toggleCurtain_loadDialog: function() {
		if(this.options["contentUrl"].length > 0)													// load dialog url content before we show the dialog
		{
			this.loadingDiv = new Element("div", { style:"text-align:center;" });
			this.loadingDiv.insert(new Element("img", { src:anderweb_common + "/images/loading_gray_big.gif" }));
			this.loadingDiv.setStyle({
				position:"absolute",
				top:(document.viewport.getScrollOffsets().top + Math.round(document.viewport.getHeight() / 2) - 32 / 2) + "px",
				left:(Math.round(document.viewport.getScrollOffsets().left + document.viewport.getWidth() / 2) - 32 / 2) + "px"
			});
			this.containerDiv.insert(this.loadingDiv);
			
			var that = this;
			new Ajax.Request(this.options["contentUrl"], {
				method:"get",
				onSuccess: function(transport) {															// successfully loading dialog content
					if(this.loadingDiv)																					// remove loading icon
						this.loadingDiv.remove();	
					this.dialogTransport = transport.responseText.split("[JS]");
					this.toggleCurtain_showDialog();														// open the dialog box
				}.bind(this),
				
				onFailure: function() {
					alert("There was an error communicating with the server.  Please try again.");
				}
			});
		}
		else																															// no content, just load the dialog box
			this.toggleCurtain_showDialog();
	},
	
	
	toggleCurtain_showDialog: function() {	
		// build dialog box			
		this.dialogDiv = new Element("div", { style:
			"position:absolute; " +
			"display:none; " +
			"border:" + this.options["dialog_border"] + "; " +
			"background-color:" + this.options["dialog_backgroundColor"] + "; " +
			"width:" + this.options["dialog_width"] + "px; " +
			(typeof(this.options["dialog_height"]) == "number" ? ("height:" + this.options["dialog_height"] + "px; ") : "") +
			"padding:" + this.options["dialog_padding"] + "px;"
		});		
		
		if(this.options["contentUrl"].length > 0)
			this.dialogDiv.update(this.dialogTransport[0]);
			
		this.containerDiv.insert(this.dialogDiv);
		this.dialogDiv.setStyle({
			top:(document.viewport.getScrollOffsets().top + Math.round(document.viewport.getHeight() / 2) - 
			Math.round((typeof(this.options["dialog_height"]) == "number" ? this.options["dialog_height"] : this.dialogDiv.getHeight()) / 2)) + "px",
			left:(Math.round(document.viewport.getScrollOffsets().left + document.viewport.getWidth() / 2) - Math.round(this.options["dialog_width"] / 2)) + "px"
		});
		this.repositionDialogDiv_start();
		
		
		
		// execute any javascript code, if it's there
		if(this.options["contentUrl"].length > 0 && this.dialogTransport.length > 1)																						
			globalEval(this.dialogTransport[1]);
		
		// now display it		
		this.dialogDiv_animating = true;
		Effect.Grow(this.dialogDiv, { afterFinish:this.toggleCurtain_showDialog_onDone.bind(this)  });
	},
	
	toggleCurtain_showDialog_onDone: function() {
		// close control
		if(this.options["showCloseOption"])
		{
			this.closerDiv = new Element("div", { style:"position:absolute; width:" + this.options["dialog_width"] + "px; font-size:28px; color:#eeeeee; cursor:pointer; text-align:right;" }).update("Close");
			this.closerDiv.setStyle({
				top:((stripToNum(this.dialogDiv.getStyle("top")) - 35) + "px"),
				left:this.dialogDiv.getStyle("left")
			});
			this.closerDiv.observe("click", this.toggleCurtain.bindAsEventListener(this))
			this.containerDiv.insert(this.closerDiv);
		}
		
		if(parseInt(this.dialogDiv.getStyle("top").replace("px", "")) < 0)
			this.dialogDiv.setStyle({ top:"30px" });
		if(typeof(this.options["callback_dialogReady"]) == "function")
			this.options["callback_dialogReady"]();
		this.dialogDiv_animating = false;
	},
	
	
	resizeCurtain: function() {
		var width, height;
		
		// width
		if(document.viewport.getWidth() > (document.body.offsetWidth + document.body.offsetLeft))
			width = document.viewport.getWidth();
		else
			width = document.body.offsetWidth + document.body.offsetLeft;
		
		// height
		if(typeof(this.options["debug_curtainHeight"]) == "number")
			height = this.options["debug_curtainHeight"];
		else if(document.viewport.getHeight() > (document.body.offsetHeight + document.body.offsetTop))
			height = document.viewport.getHeight();
		else
			height = document.body.offsetHeight + document.body.offsetTop;
		
		this.curtainDiv.setStyle({ width:width + "px", height:height + "px" });
	},
	
	toggleCurtain_cleanup: function() {
		// re-display the select boxes, stupid ie6 bug...
		for(var i=0; i<this.selectBoxes.length; i++)
			this.selectBoxes[i].show();
		this.selectBoxes = null;
		// re-display the iframes
		for(var i=0; i<this.iframes.length; i++)
			this.iframes[i].setStyle({visibility:"visible"});
		this.iframes = null;
		
		this.containerDiv.remove(); 
		this.containerDiv = null;
		
		if(typeof(this.options["callback_dialogDone"]) == "function")
			this.options["callback_dialogDone"]();
	},
	
	repositionDialogDiv_start: function() {
		new PeriodicalExecuter(this.repositionDialogDiv_do.bind(this));
	},
	
	repositionDialogDiv_do: function(pe) {
		if(this.dialogDiv && !this.dialogDiv_animating)
		{
			// ensure the dialog box doesn't go into negative pixels
			var topPos = (document.viewport.getScrollOffsets().top + Math.round(document.viewport.getHeight() / 2) - 
				Math.round((typeof(this.options["dialog_height"]) == "number" ? this.options["dialog_height"] : this.dialogDiv.getHeight()) / 2));
			if(topPos < 0)
				topPos = 0;
			
			this.dialogDiv.setStyle({
				top:topPos + "px",
				left:(Math.round(document.viewport.getScrollOffsets().left + document.viewport.getWidth() / 2) - Math.round(this.options["dialog_width"] / 2)) + "px"
			});
		}
	}
});








var Prompter = Class.create({
	container:null, curtain:null, promptContainer:null, dialogContainer:null, inputObj:null,
	prompterScheme:"okCancel",							// okCancel, yesNo
	callback:null, defaultValue:null,
	prompt_width:400, prompt_height:75,			// prompt_height is estimated, depending on contents
	
	initialize: function(scheme) {
		if(typeof(scheme) == "string")
			this.prompterScheme = scheme;
	},
	
	displayPrompt: function(callback, label, defaultValue) {
		this.callback = callback;
		this.defaultValue = typeof(defaultValue) == "string" ? defaultValue : "";
		
		this.curtain = new CurtainGenerator();
		this.curtain.dialog_width = this.prompt_width;
		this.curtain.callback_dialogReady = this.promptReady.bind(this);
		this.dialogContainer = this.curtain.toggleCurtain();
		
		// label
		var promptContainer = new Element("div", { style:"padding:10px;" });
			promptContainer.insert(new Element("div", { }).update(label));
			
			if(this.prompterScheme == "okCancel")
			{
				// input text box
				this.inputObj = new Element("input", { type:"text", style:"width:100%;", value:this.defaultValue });
				promptContainer.insert(this.inputObj);
			}
			
			if(this.prompterScheme == "okCancel")
			{
				var cancelButton_label = "Cancel";
				var okButton_label = "OK";
			}
			else if(this.prompterScheme == "yesNo")
			{
				var cancelButton_label = "No";
				var okButton_label = "Yes";
			}
			// buttons box
			var buttonsDiv = new Element("div", { style:"text-align:right; padding:5px 0px;" });
				var button_cancel = new Element("input", { type:"button", value:cancelButton_label });
				button_cancel.observe("click", this.promptFinished.bindAsEventListener(this, false));
				buttonsDiv.insert(button_cancel);
				
				var button_ok = new Element("input", { type:"button", value:okButton_label, style:"width:50px;" });
				button_ok.observe("click", this.promptFinished.bindAsEventListener(this, true));
				buttonsDiv.insert(button_ok);
			promptContainer.insert(buttonsDiv);
		this.dialogContainer.insert(promptContainer);
		
		if(this.prompterScheme == "okCancel")
			this.inputObj.select();
	},
	
	
	promptReady: function() {
		if(this.prompterScheme == "okCancel")
			this.inputObj.select();
	},
	
	promptFinished: function(ev, success) {
		this.curtain.toggleCurtain();
		
		this.callback(success, this.prompterScheme == "okCancel" ? (success ? this.inputObj.getValue() : "") : "");
	}
});
















var Prompter2 = Class.create({
	curtainObj:null, prompterDiv:null,
	options:null,
	
	initialize: function(opts) {
		var default_args = {
			scheme:"okCancel",		// okCancel, yesNo
			prompt_width:400,			// in pixels
			callback:null,				// called once the user is finished with the prompter
			promptText:"",				// the "queston" to ask the user
			defaultValue:"",			// default value for input field (applicable only for scheme=okCancel
			showNow:true					// display prompter upon initialization of this object (or page load, if page isn't done loading)
		};
		for(var i in default_args)
			if(typeof opts[i] == "undefined") 
				opts[i] = default_args[i];
		this.options = opts;
		
		if(this.options["showNow"])
			this.displayPrompt();
	},
	
	displayPrompt: function() {
		this.curtainObj = new CurtainGenerator2({
			dialog_width:this.options["prompt_width"],
			callback_dialogReady:this.promptReady.bind(this)
			
		});
		this.curtainObj.dialog_width = this.prompt_width;
		this.curtainObj.callback_dialogReady = this.promptReady.bind(this);
		this.curtainObj.toggleCurtain();
	},
	
	
	promptReady: function() {
		// label
		var promptContainer = new Element("div", { style:"padding:10px;" });
			promptContainer.insert(new Element("div", { }).update(this.options["promptText"]));
			
			
			if(this.options["scheme"] == "okCancel")
			{
				// input text box
				this.inputObj = new Element("input", { type:"text", style:"width:100%;", value:this.options["defaultValue"] });
				promptContainer.insert(this.inputObj);
				
				var cancelButton_label = "Cancel";
				var okButton_label = "OK";
			}
			else if(this.options["scheme"] == "yesNo")
			{
				var cancelButton_label = "No";
				var okButton_label = "Yes";
			}
			// buttons box
			var buttonsDiv = new Element("div", { style:"text-align:right; padding:5px 0px;" });
				var button_cancel = new Element("input", { type:"button", value:cancelButton_label });
				button_cancel.observe("click", this.promptFinished.bindAsEventListener(this, false));
				buttonsDiv.insert(button_cancel);
				
				var button_ok = new Element("input", { type:"button", value:okButton_label, style:"width:50px;" });
				button_ok.observe("click", this.promptFinished.bindAsEventListener(this, true));
				buttonsDiv.insert(button_ok);
			promptContainer.insert(buttonsDiv);
		this.curtainObj.dialogDiv.insert(promptContainer);
		
		if(this.prompterScheme == "okCancel")
			this.inputObj.select();
	},
	
	promptFinished: function(ev, success) {
		this.curtainObj.toggleCurtain();
		
		this.options["callback"](success, this.options["scheme"] == "okCancel" ? (success ? this.inputObj.getValue() : "") : "");
	}
});













// MUST HAVE GOOGLE API LOADED!!!
var GoogleMapOverlay = Class.create({
	curtainObj:null, mapDiv:null,
	options:null,
	
	initialize: function(opts) {
		var default_args = {
			latitude:null,
			longitude:null,
			showNow:false,
			curtain_transparency:0.6,
			curtain_color:"#000000",
			dialog_width:700,
			dialog_height:600,
			dialog_border:"5px solid white",
			dialog_backgroundColor:"#eeeeee",
			callback_dialogDone:null
		};
		for(var i in default_args)
			if(typeof opts[i] == "undefined") 
				opts[i] = default_args[i];
		this.options = opts;
		
		if(this.options["showNow"])
		{
			if(document.readyState_ai == "loading")
				document.observe("dom:loaded", this.toggleMap.bindAsEventListener(this));
			else
				this.toggleMap();
		}
		
	},
	
	onLoaded: function() {
		if(this.options["showNow"])
		{
			
		}
	},
	
	
	toggleMap: function() {
		// hide map
		if(this.curtainObj)
			this.curtainObj.toggleCurtain();
		
		// show map
		else
		{
			this.curtainObj = new CurtainGenerator2({ 
				curtain_transparency:this.options["curtain_transparency"],
				curtain_color:this.options["curtain_color"],
				dialog_width:this.options["dialog_width"], 
				dialog_height:this.options["dialog_height"],
				dialog_padding:0,
				dialog_border:this.options["dialog_border"],
				showCloseOption:true,
				showNow:true,
				callback_dialogReady:this.toggleMap_onReady.bind(this),
				callback_dialogDone:this.options["callback_dialogDone"]
			});
		}
	},
	
	
	toggleMap_onReady: function() {
		// the actual map
		this.mapDiv = new Element("div", { style:"width:" + this.options["dialog_width"] + "px; height:" + (this.options["dialog_height"]) + "px;" });
		this.curtainObj.dialogDiv.insert(this.mapDiv);
		
		// loading icon to wait for google maps to complete
		var loadingDiv = new Element("div", { style:"text-align:center;" });
		loadingDiv.insert(new Element("img", { src:anderweb_common + "/images/loading_gray_big.gif" }));
		loadingDiv.setStyle({
			position:"absolute",
			top:(Math.round(this.options["dialog_height"] / 2) - 32 / 2) + "px",
			left:(Math.round(this.options["dialog_width"] / 2) - 32 / 2) + "px"
		});
		this.curtainObj.dialogDiv.insert(loadingDiv);
		
		google.load("maps", "2", { callback:this.showMap.bind(this) });
	},
	
	showMap: function() {
		var latitude, longitude;
		// user passed fixed coordinates, use them
		if(!isNaN(parseInt(this.options["latitude"])) && !isNaN(parseInt(this.options["longitude"])))
		{
			latitude = this.options["latitude"];
			longitude = this.options["longitude"];
		}
		// user passed id's to form elements containing the coordinates
		else
		{
			latitude = $F(this.options["latitude"]);
			longitude = $F(this.options["longitude"]);
		}
		var map = new google.maps.Map2(this.curtainObj.dialogDiv);
		map.setCenter(new GLatLng(latitude, longitude), 15);
		
		var point = new GLatLng(latitude, longitude);
		map.addOverlay(new GMarker(point));
		
		var mapControl = new GMapTypeControl();map.addControl(mapControl);map.addControl(new GLargeMapControl());
	}
});














function number_format( number, decimals, dec_point, thousands_sep ) {
    // Formats a number with grouped thousands  
    // 
    // version: 902.1517
    // discuss at: http://phpjs.org/functions/number_format
    // +   original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +     bugfix by: Michael White (http://getsprink.com)
    // +     bugfix by: Benjamin Lupton
    // +     bugfix by: Allan Jensen (http://www.winternet.no)
    // +    revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
    // +     bugfix by: Howard Yeend
    // +    revised by: Luke Smith (http://lucassmith.name)
    // +     bugfix by: Diogo Resende
    // +     bugfix by: Rival
    // %        note 1: For 1000.55 result with precision 1 in FF/Opera is 1,000.5, but in IE is 1,000.6
    // *     example 1: number_format(1234.56);
    // *     returns 1: '1,235'
    // *     example 2: number_format(1234.56, 2, ',', ' ');
    // *     returns 2: '1 234,56'
    // *     example 3: number_format(1234.5678, 2, '.', '');
    // *     returns 3: '1234.57'
    // *     example 4: number_format(67, 2, ',', '.');
    // *     returns 4: '67,00'
    // *     example 5: number_format(1000);
    // *     returns 5: '1,000'
    // *     example 6: number_format(67.311, 2);
    // *     returns 6: '67.31'
    var n = number, prec = decimals;
    n = !isFinite(+n) ? 0 : +n;
    prec = !isFinite(+prec) ? 0 : Math.abs(prec);
    var sep = (typeof thousands_sep == "undefined") ? ',' : thousands_sep;
    var dec = (typeof dec_point == "undefined") ? '.' : dec_point;

    var s = (prec > 0) ? n.toFixed(prec) : Math.round(n).toFixed(prec); //fix for IE parseFloat(0.55).toFixed(0) = 0;

    var abs = Math.abs(n).toFixed(prec);
    var _, i;

    if (abs >= 1000) {
        _ = abs.split(/\D/);
        i = _[0].length % 3 || 3;

        _[0] = s.slice(0,i + (n < 0)) +
              _[0].slice(i).replace(/(\d{3})/g, sep+'$1');

        s = _.join(dec);
    } else {
        s = s.replace('.', dec);
    }

    return s;
}










function stripToNum(str) 
{ 
	if(str.length > 0)
	{
		var newNum = parseFloat(new String(str).replace(/,/g, ''));
		if(!isNaN(newNum))
			return newNum;
		else
			return str;
	}
	else
		return str;
}










/** Form Validation **/

/** 
 *  FormField - Represents a form element
 */
var FormField = Class.create({
	fieldId:"",
	fieldObj:null,
	fieldLabel:"",
	requirements:null,
	
	initialize: function(fieldId, fieldLabel, req)
	{
		this.fieldId = fieldId;
		this.fieldObj = $(fieldId);
		this.fieldLabel = fieldLabel;
		
		// build requirements
		var default_req = {
			datatypes:[],
			range:null,
			required:false
		};
		for(var i in default_req)
			if(typeof req[i] == "undefined") 
				req[i] = default_req[i];
		this.requirements = req;
	},
	
	checkValidation: function() {
		// required
		if(this.requirements["required"])
			if(this.fieldObj.value.length == 0)
				return { isValid:false, fieldObj:this.fieldObj, errorMessage:("'" + this.fieldLabel + "' is required") };
				
		// datatype: int
		if(this.requirements["datatypes"].indexOf("int") != -1)
		{
			if(!/^[-+]?\d+$/.test(this.fieldObj.value) && this.fieldObj.value.length > 0)
				return { isValid:false, fieldObj:this.fieldObj, errorMessage:("'" + this.fieldLabel + "' must be an integer") };
		}
		
		// datatype: float
		if(this.requirements["datatypes"].indexOf("float") != -1)
			if(!/^[-+]?\d+(\.\d+)?$/.test(this.fieldObj.value) && this.fieldObj.value.length > 0)
				return { isValid:false, fieldObj:this.fieldObj, errorMessage:("'" + this.fieldLabel + "' must be a decimal") };
		
		// datatype: varName
		if(this.requirements["datatypes"].indexOf("varName") != -1)
			if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(this.fieldObj.value) && this.fieldObj.value.length > 0)
				return { isValid:false, fieldObj:this.fieldObj, errorMessage:("'" + this.fieldLabel + "' must be a valid variable name (ie, alpha-numeric characters and underscore; first character cannot be a number)") };
		
		// datatype: email
		if(this.requirements["datatypes"].indexOf("email") != -1)
			if(!/^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/.test(this.fieldObj.value) && this.fieldObj.value.length > 0)
				return { isValid:false, fieldObj:this.fieldObj, errorMessage:("'" + this.fieldLabel + "' must be a valid email address") };
			
		// range
		if(!(this.requirements["range"] === null))
		{
			// finite range
			if(!(this.requirements["range"].from === null) && !(this.requirements["range"].to === null))
			{
				if(this.fieldObj.value < this.requirements["range"].from || this.fieldObj.value > this.requirements["range"].to)
					return { isValid:false, fieldObj:this.fieldObj, errorMessage:("'" + this.fieldLabel + "' must be between " + this.requirements["range"].from + " and " + this.requirements["range"].to) };
			}
			// forward-infinite
			else if(!(this.requirements["range"].from === null) && this.requirements["range"].to === null)
			{
				if(this.fieldObj.value < this.requirements["range"].from)
					return { isValid:false, fieldObj:this.fieldObj, errorMessage:("'" + this.fieldLabel + "' must be greater than or equal to " + this.requirements["range"].from) };
			}
			// backwards-infinite
			else if((this.requirements["range"].from === null) && !(this.requirements["range"].to === null))
			{
				if(this.fieldObj.value > this.requirements["range"].to)
					return { isValid:false, fieldObj:this.fieldObj, errorMessage:("'" + this.fieldLabel + "' must be less than or equal to " + this.requirements["range"].to) };
			}
		}
		return { isValid:true };
	}
});



/** 
 *  FormValidator - The main class for form validation
 */
var FormValidator = Class.create({
	formId:"",
	formObj:null,
	fields:null,
	ranges:null,
	options:null,
	
	initialize: function(formId, opts) {
		this.formId = formId;
		this.formObj = $(formId);
		this.fields = [];
		this.ranges = [];
		
		// build options
		var default_args = {
			invalid_className:""
		};
		for(var i in default_args)
			if(typeof opts[i] == "undefined") 
				opts[i] = default_args[i];
		this.options = opts;
	},
	
	
	addField: function(formField) {
		this.fields[this.fields.length] = formField;
	},
	
	
	addMultiFieldRange: function(fieldId1, fieldId2, fieldLabel, datatype, operator) {
		if(typeof(datatype) == "undefined")
			datatype = "float";
		if(typeof(operator) == "undefined")
			operator = "gt";
		this.ranges[this.ranges.length] = { fieldId1:fieldId1, fieldId2:fieldId2, fieldLabel:fieldLabel, datatype:datatype, operator:operator };
	},
	
	
	checkValidation: function() {
		var invalidFields = [];
		// undo any invalid-styled fields
		if(this.options["invalid_className"].length > 0)
		{
			this.fields.each(function(field) { field.fieldObj.removeClassName(this.options["invalid_className"]); }.bind(this)); 
			this.ranges.each(function(range) { 
				if(range.datatype == "int" || range.datatype == "float")
				{
					$(range.fieldId1).removeClassName(this.options["invalid_className"]);
					$(range.fieldId2).removeClassName(this.options["invalid_className"]);
				}
				else if(range.datatype == "date")
				{
					$(range.fieldId1 + "_year").removeClassName(this.options["invalid_className"]);
					$(range.fieldId1 + "_month").removeClassName(this.options["invalid_className"]);
					$(range.fieldId1 + "_day").removeClassName(this.options["invalid_className"]);
					$(range.fieldId2 + "_year").removeClassName(this.options["invalid_className"]);
					$(range.fieldId2 + "_month").removeClassName(this.options["invalid_className"]);
					$(range.fieldId2 + "_day").removeClassName(this.options["invalid_className"]);
					if($(range.fieldId1 + "_hour"))
					{
						$(range.fieldId1 + "_hour").removeClassName(this.options["invalid_className"]);
						$(range.fieldId1 + "_minute").removeClassName(this.options["invalid_className"]);
						$(range.fieldId1 + "_amPm").removeClassName(this.options["invalid_className"]);
						$(range.fieldId2 + "_hour").removeClassName(this.options["invalid_className"]);
						$(range.fieldId2 + "_minute").removeClassName(this.options["invalid_className"]);
						$(range.fieldId2 + "_amPm").removeClassName(this.options["invalid_className"]);
						if($(range.fieldId1 + "_second"))
						{
							$(range.fieldId1 + "_second").removeClassName(this.options["invalid_className"]);
							$(range.fieldId2 + "_second").removeClassName(this.options["invalid_className"]);
						}
					}
				}
			}.bind(this));
		}
		
		// fields: check all fields
		for(var i=0; i<this.fields.length; i++)
		{
			var results = this.fields[i].checkValidation();
			if(!results.isValid)
				invalidFields[invalidFields.length] = results;
		}
		// ranges: don't check for multi-field ranges until validation above is successful
		if(invalidFields.length == 0)
		{
			for(var i=0; i<this.ranges.length; i++)
			{
				// field2 - field1 == 0 -- they're the same
				// field2 - field1 >  0 -- order of numbers going positive (to the right)
				// field2 - field1 < 0 -- order of numbers going negative (to the left)
				if(this.ranges[i].datatype == "int" || this.ranges[i].datatype == "float")
				{
					var direction = $F(this.ranges[i].fieldId2) - $F(this.ranges[i].fieldId1);
					if(this.ranges[i].operator == "gt" ? (direction <= 0) : (this.ranges[i].operator == "gte" ? (direction < 0) : false))
					{
						invalidFields[invalidFields.length] = { isValid:false, fieldObj:$(this.ranges[i].fieldId1), errorMessage:("The first value must be less than the second value in '" + this.ranges[i].fieldLabel + "'") }
						invalidFields[invalidFields.length] = { isValid:false, fieldObj:$(this.ranges[i].fieldId2) };
					}
				}
				else if(this.ranges[i].datatype == "date")
				{
					var direction = Date.UTC(
							$F(this.ranges[i].fieldId2 + "_year"), 
							$F(this.ranges[i].fieldId2 + "_month"), 
							$F(this.ranges[i].fieldId2 + "_day"),
							$(this.ranges[i].fieldId2 + "_hour") ? convert_12to24(this.ranges[i].fieldId2) : 0,
							$(this.ranges[i].fieldId2 + "_minute") ? $F(this.ranges[i].fieldId2 + "_minute") : 0,
							$(this.ranges[i].fieldId2 + "_second") ? $F(this.ranges[i].fieldId2 + "_second") : 0
						) - 
						Date.UTC(
							$F(this.ranges[i].fieldId1 + "_year"), 
							$F(this.ranges[i].fieldId1 + "_month"), 
							$F(this.ranges[i].fieldId1 + "_day"),
							$(this.ranges[i].fieldId1 + "_hour") ? convert_12to24(this.ranges[i].fieldId1) : 0,
							$(this.ranges[i].fieldId1 + "_minute") ? $F(this.ranges[i].fieldId1 + "_minute") : 0,
							$(this.ranges[i].fieldId1 + "_second") ? $F(this.ranges[i].fieldId1 + "_second") : 0
						);
						//alert(direction + ", operator=" + this.ranges[i].operator + ", " + (this.ranges[i].operator == "gt" ? (direction <= 0) : (this.ranges[i].operator == "gte" ? (direction < 0) : false)));
					if(this.ranges[i].operator == "gt" ? (direction <= 0) : (this.ranges[i].operator == "gte" ? (direction < 0) : false))
					{
						invalidFields[invalidFields.length] = { isValid:false, fieldObj:$(this.ranges[i].fieldId1 + "_year"), errorMessage:("The first date must be " + (this.ranges[i].operator == "gte" ? "on or " : "") + "before the second date in '" + this.ranges[i].fieldLabel + "'") }
						invalidFields[invalidFields.length] = { isValid:false, fieldObj:$(this.ranges[i].fieldId1 + "_month") };
						invalidFields[invalidFields.length] = { isValid:false, fieldObj:$(this.ranges[i].fieldId1 + "_day") };
						invalidFields[invalidFields.length] = { isValid:false, fieldObj:$(this.ranges[i].fieldId2 + "_year") };
						invalidFields[invalidFields.length] = { isValid:false, fieldObj:$(this.ranges[i].fieldId2 + "_month") };
						invalidFields[invalidFields.length] = { isValid:false, fieldObj:$(this.ranges[i].fieldId2 + "_day") };
						if($(this.ranges[i].fieldId1 + "_hour"))
						{ 
							invalidFields[invalidFields.length] = { isValid:false, fieldObj:$(this.ranges[i].fieldId1 + "_hour") };
							invalidFields[invalidFields.length] = { isValid:false, fieldObj:$(this.ranges[i].fieldId1 + "_minute") };
							invalidFields[invalidFields.length] = { isValid:false, fieldObj:$(this.ranges[i].fieldId1 + "_amPm") };
							invalidFields[invalidFields.length] = { isValid:false, fieldObj:$(this.ranges[i].fieldId2 + "_hour") };
							invalidFields[invalidFields.length] = { isValid:false, fieldObj:$(this.ranges[i].fieldId2 + "_minute") };
							invalidFields[invalidFields.length] = { isValid:false, fieldObj:$(this.ranges[i].fieldId2 + "_amPm") };
							if($(this.ranges[i].fieldId1 + "_second"))
							{
								invalidFields[invalidFields.length] = { isValid:false, fieldObj:$(this.ranges[i].fieldId1 + "_second") };
								invalidFields[invalidFields.length] = { isValid:false, fieldObj:$(this.ranges[i].fieldId2 + "_second") };
							}
						}
					}
				}
			}
		}
		
		// throw errors if we have invalid fields
		if(invalidFields.length > 0)
		{
			var errorMessage = "";
			for(var i=0; i<invalidFields.length; i++)
			{
				// build error message
				if(invalidFields[i].errorMessage)
					errorMessage += "    - " + invalidFields[i].errorMessage + "\n";
				if(this.options["invalid_className"].length > 0)
					invalidFields[i].fieldObj.addClassName(this.options["invalid_className"]);
			}
			alert("The following problems were found with the form:\n" + errorMessage);
		}
		
		return invalidFields.length == 0;
	}
});

function is_numeric(num) {
	if (num === '')
		return false;
	else
		return !isNaN(num * 1);
}

function isset () {
	// http://kevin.vanzonneveld.net
	// +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
	// +   improved by: FremyCompany
	// +   improved by: Onno Marsman
	// *     example 1: isset( undefined, true);
	// *     returns 1: false
	// *     example 2: isset( 'Kevin van Zonneveld' );
	// *     returns 2: true
	
	var a=arguments, l=a.length, i=0;
    
	if (l===0) {
		throw new Error('Empty isset'); 
	}

	while (i!==l) {
		if (typeof(a[i])=='undefined' || a[i]===null) { 
			return false; 
		} else { 
			i++; 
		}
	}
	return true;
}
