var _appEventListenMode = false;	// feature disabled for now


var nullValue = "$$$$$$";
var newline = "\r\n";
//var oXmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
var oXmlHttp = GetXmlHttpObject();
var listdef_01 = "<option value=$$$$$$>.............&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</option>";
var delimStr = "|";

//******************************//
function checkLocation(count) {
  eval("printArea.style.display = \"\"");
  for(i=0;i<count;i++){
    eval("b"+i.toString()+".width=a"+i.toString()+".offsetWidth");

}
 eval("printArea.style.pixelLeft=tabletop.offsetLeft");
 eval("printArea.style.pixelTop=document.body.scrollTop");
 if(eval("tabletop.offsetTop")>=eval("printArea.offsetTop"))
	 {
    	eval("printArea.style.display = \"none\"");
 	}
 }

//*************** ***************//
function trim(s){
	if(s == null || String(s) == "undefined") return "";
	return s.replace(/(^\s*)|(\s*$)/g, "");
}

String.prototype.trim = function() {
	return this.replace(/(^\s*)|(\s*$)/g, '');
};

String.prototype.capitalize = function() {
	if (this == null || this.length == 0) {
		return '';
	}
	
	return this.toUpperCase().charAt(0) + this.toLowerCase().substring(1);
};
//******************************//
function rowClick(objname,flag){
	var o = eval(objname);
	if(flag == 1)
		o.className = "opened";
	else
		o.className = "unopen";
}

function rowMouseOver(node){
	if(node.className != "opened")
		node.className = "mouseover";
}

function rowMouseOut(node){
	if(node.className != "opened")
		node.className = "unopen";
}


function connectToSpec(toURL,func1){
	divwait.style.display = "block";
	oXmlHttp.open("GET", toURL, true);
	oXmlHttp.onreadystatechange = func1;
	try{
		oXmlHttp.send(null);
	}catch(err){
		divwait.style.display = "none";
		alert("Can not connect to server.");
	}
}

function connectTo3(url,s){
  divwait.style.display = "block";
  var httpVals = url + "?" + s;
  oXmlHttp.open("GET",httpVals,true);
  oXmlHttp.onreadystatechange = waitingData;
  try{
    oXmlHttp.send(null);
  }catch(err){
    divwait.style.display = "none";
    alert("Can not connect to server.");
  }
}

function waitingData(){
  if(oXmlHttp.readyState == 4){
    divwait.style.display = "none";
    if(oXmlHttp.status != 200){
      alert("Page returned error state " + oXmlHttp.status);
    }else{
      divretv.innerHTML = oXmlHttp.responseText;
      dataDone(divretv.innerHTML);
    }
  }
}

/***
 *   'a'  no input
 *   'b'  string length<p1
 *   'c'  positive number
 *   'd'  positive integer
 *   'e'  integer range
 *   'f'  number range
 *	 'g'  only in [a-z] and [A-Z]
 *	 'h'  data formate validate
 */
function dataValid(flag,v,p1){
	switch(flag){
		case 'a':
			re = /^\s*$/;
			return re.test(v);
			break;
		case 'b':
			return getISOLength(v) > p1 ? true : false;
			break;
		case 'c':
			re = /^\d+(\.\d+)?$/;
			return re.test(v);
			break;
		case 'd':
			re = /^\d+$/;
			return re.test(v);
			break;
		case 'e':
			re = /^(-|\+)?\d+$/;
			return re.test(v);
			break;
		case 'f':
			re = /^(-|\+)?\d+(\.\d+)?$/;
			return re.test(v);
			break;
		case 'g':
			re = /^([A-Za-z])*$/;
			return re.test(v);
			break;
		case 'h':
			//re = /^\d{1,2}(\-|\/|\.)\d{1,2}\1\d{d}$/;
			re = /^\d{1,2}(\/)\d{1,2}\1\d{4}$/;
			return re.test(v);
			break;
		case 'n':
			//re = /^\d{1,2}(\-|\/|\.)\d{1,2}\1\d{d}$/;
			re = /^[-]?([1-9]{1}[0-9]{0,}(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|\.[0-9]{1,2})$/;			
			return re.test(v);
			alert ("v = " + v);
			break;
	}
	return false;
}

/***
 * r1    number range p1 <= number <= p2
 */
function dataValidOther(flag,v,p1,p2){
	switch(flag){
		case 'r1':
			return (v >= p1 && v <= p2) ? true : false;
			break;
		case 'r2':
			return getISOLength(v) == p1 ? true : false;
			break;
	}
	return false;
}

function getISOLength(str){
	var n = str.length;
	for(var i=0;i<str.length;i++)
		if(str.charCodeAt(i) > 255) n++;
	return n;
}

/***
 *
 * Array(p0,p1,p2)
 * p0   input value
 * p1   description of input value
 * p2   dataValid flag
 * p3   additional parameter for dataValid
 *
 *   'a'  no input
 *   'b'  string length<p1
 *   'c'  positive number
 *   'd'  positive integer
 *   'e'  positive integer range
 *   'f'  number range
 *	 'g'  only in [a-z] and [A-Z]
 *	 'h'  data formate validate
 *   'r1'  number range p1 <= number <= p2()
 */
function checkInput(array1){
	var s = "";
	for(var i=0;i<array1.length;i++){
		var array2 = array1[i];
		switch(array2[2]){
			case 'a':
				if(dataValid('a',array2[0]))
					s += (newline + array2[1] + msg_1001);
				break;
			case 'b':
				if(dataValid('b',array2[0],array2[3]))
					s += (newline + array2[1] + msg_1010.replace(/\?/g,array2[3]));
				break;
			case 'c':
				if(!dataValid('c',array2[0]))
					s += (newline + array2[1] + msg_1002);
				break;
			case 'd':
				if(!dataValid('d',array2[0]))
					s += (newline + array2[1] + msg_1004);
				break;
			case 'e':
				if(!dataValid('e',array2[0]))
					s += (newline + array2[1] + msg_1005);
				break;
			case 'f':
				if(!dataValid('f',array2[0]))
					s += (newline + array2[1] + msg_1003);
				break;
			case 'g':
				if(!dataValid('g',array2[0]))
					s += (newline + array2[1] + msg_1006);
				break;
			case 'h':
				if(!dataValid('h',array2[0]))
					s += (newline + array2[1] + msg_1008);
				break;
			case 'n':
				if(!dataValid('n',array2[0]))
					s += (newline + array2[1] + msg_1011);
				break;
			case 'r1':
				if(!dataValidOther('r1',array2[0],array2[3],array2[4]))
					var tmp = msg_1007.replace(/\?/g,array2[3]);
					s += (newline + array2[1] + tmp.replace(/\*/g,array2[4]));
				break;
		}
	}
	return s == "" ? s : msg_1000 + newline + s;
}

function onFocusSet(msg){
	var k = msg.indexOf(':');
	var obj = trim(msg.substring(0,k));
	document.getElementById(obj).focus();
	document.getElementById(obj).select();
}

// strips all included form elements of characters ' ; " , ? & < >
function clearInput(array1){
	for(var i=0;i<array1.length;i++){
		array1[i].value = clearSpecChars(trim(array1[i].value));
	}
}

//strips all included form elements of characters ; " , ? & < >
function sanitizeNames(arr) {
	for (var i = 0; i < arr.length; i++) {
		arr[i].value = clearNameChars(trim(arr[i].value));
	}
}

// trims form and 
function trimInput(formArr) {
	for (var key in formArr) {
		formArr[key].value = trim(formArr[key].value);
	}
}
// special characters:
// ' ; " , ? & < > \ /
function clearSpecChars(s){
	if(s == null || String(s) == "undefined") return "";
	return s.replace(/[';",\?&<>]/g, "");
}

function clearNameChars(s){
	if(s == null || String(s) == "undefined") return "";
	return s.replace(/[;",\?&<>]/g, "");
}

//
function writeTab(f2,f3){
	if (isNaN(f3)) {
		f3 = 1;
	}
	var labels = new Array();
	switch (f2) {
	case "user":
		labels[0] = "User Information";
		labels[1] = "History";
	break;
	case "servtype":
		labels[0] = "Type Information";
		labels[1] = "Type to Service";
	break;
	case "provider":
		labels[0] = "Provider Information";
		labels[1] = "Provider to District";
		labels[2] = "Provider to Students";
		labels[3] = "History";
	break;
	case "student":
		labels[0] = "Student Information";	
		labels[1] = "Student to Insurance";
		labels[2] = "History";
	break;
	case"school":
		labels[0] = "School Information";
		labels[1] = "School to Calendar";
	break;
	case"service":
		labels[0] = "Service Information";
		labels[1] = "History";
	break;
	case "billlograte":
		labels[0] = "Billlog Rate Information";
		labels[1] = "History";
	break;
	case "clinical":
		labels[0] = "Clinical Notes Information";
		labels[1] = "Service";
	break;
	case "distgroup":
		labels[0] = "District Group";
		labels[1] = "District Coordinator";
	break;
	case "district":
		labels[0] = "District";
		labels[1] = "District Administrator";
		labels[2] = "Password Rules";
		labels[3] = "District Settings";
		labels[4] = "District Calendar";
	break;
	case "import":
		labels[0] = "Student";
		labels[1] = "Provider";
		labels[2] = "Item";
	break;
	}

	document.write("<table border=0 cellpadding=0 cellspacing=0 ><tr><td height=10></td></tr>");
	document.write("<tr>");
	var s2 = "";
	var s3 = "";
	for(var i=1;i<=labels.length;i++){
		if(i == f3){
			s2 = "display:block";
			s3 = "display:none";
		}else{
			s2 = "display:none";
			s3 = "display:block";
		}
		document.write("<td></td>");

		document.write("<td class=tab onclick=func1(this) value=" + i + ">");
		document.write("<div id=tab" + i + "0 style=" + s2 + ">");
		document.write("<table border=0 cellpadding=0 cellspacing=0><tr><td><img src=" + context_path + "/images/tab/4.gif ></td><td align=center valign=bottom background=" + context_path + "/images/tab/1_2.gif>");
		document.write("<table width=100% border=0 cellspacing=0 cellpadding=4><tr><td align=center nowrap><strong><font color=#FFFFFF>" + labels[i-1] + "</strong></td></tr></table></td><td><img src=" + context_path +"/images/tab/3.gif></td></tr></table></div>");

		document.write("<div id=tab" + i + "1 style=" + s3 + ">");
		document.write("<table border=0 cellpadding=0 cellspacing=0><tr><td ><img src=" + context_path +"/images/tab/1_1.gif ></td><td align=center valign=bottom background=" + context_path + "/images/tab/5.gif>");
		document.write("<table width=100% border=0 cellspacing=0 cellpadding=4><tr><td align=center nowrap><strong><font color=#666666>" + labels[i-1] + "</strong></td></tr></table></td><td> <img src=" + context_path +"/images/tab/2.gif></td></tr></table></div>");
		document.write("</td>");
	}
	document.write("</tr></table>");
}

//
function tabRadioClick(i,j){
/*
	eval("tab" + i + "0").style.display = "none";
	eval("tab" + i + "1").style.display = "block";
	eval("tab" + j + "0").style.display = "block";
	eval("tab" + j + "1").style.display = "none";
	eval("tabcontent" + i).style.display = "none";
	eval("tabcontent" + j).style.display = "block";
*/
	$('#tab' + i + '0').css('display', 'none');
	$('#tab' + i + '1').css('display', 'block');
	$('#tab' + j + '0').css('display', 'block');
	$('#tab' + j + '1').css('display', 'none');
	$('#tabcontent' + i).css('display', 'none');
	$('#tabcontent' + j).css('display', 'block');

}

function writeListDef(f){
	if(f == 1) document.write(listdef_01);
}


/**
 * root   XML Root Element
 * from   source component of SELECT
 */
function fillList(root,from){
	clearList(from);
	for(var i=0;i<root.childNodes.length;i++){
		var row = root.childNodes[i];
		from.options[from.length++] = new Option(row.childNodes[1].childNodes[0].nodeValue,row.childNodes[0].childNodes[0].nodeValue);
	}
	root = null;
}
//
function clearList(from){
	from.length = 1;
}

function exchangeListItems(from,to){
	var k = from.selectedIndex;
	if(k >= 1){
		bselected = true;
		for(var p=to.length-1;p>=1;p--)
		{
			if(to.options[p].value==from.options[k].value)
			{
			bselected = false;
			break;
			}
		}
		if(bselected) to.options[to.length++] = new Option(from.options[k].text,from.options[k].value);
		from.options[k] = null;
	}
}

function exchangeAllItems(from,to){
	for(var i=from.length -1;i>=1;i--){
		bselected = true;
		for(var p=to.length-1;p>=1;p--)
		{
			if(to.options[p].value == from.options[i].value)
			{
			bselected = false;
			break;
			}
		}
		if(bselected) to.options[to.length++] = new Option(from.options[i].text,from.options[i].value);
		from.options[i] = null;
	}
}


function writeListYear(f){
	for(var i=2005;i<=2030;i++)
	{
		if(i==f)
	      document.write("<option value="+i+" selected>"+i+"</option>");
		else
		  document.write("<option value="+i+">"+i+"</option>");
	}
}

function writeListMonth(f){
	var months = new Array("Jan&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;","Feb","Mar","Apr","May","June","July","Aug","Sept","Oct","Nov","Dec");
	var months_value = new Array("01","02","03","04","05","06","07","08","09","10","11","12");

	for(var i=1;i<=12;i++)
	{
		if(i==parseInt(f))
	      document.write("<option value="+months_value[i-1]+" selected>"+months[i-1]+"</option>");
		else
		  document.write("<option value="+months_value[i-1]+">"+months[i-1]+"</option>");
	}
}

//sprevost, 1/7/07, add versions of above functions to display month/year of current date - 2 months.
function writeListYearAdj(f,m){
	writeListYear(f);
}

function writeListMonthAdj(f){
	writeListMonth(f);
}

function writeDivWait(){
	document.write("<div id='divretv' style='display:none'></div>");
	document.write("<div id='divwait' style='position:absolute; left:300px; top:140px; z-index:100; display:none; border-style:outset; border-width:3px; padding:10px 30px; background-color:#dddddd'><font style='font-size:20pt;font-weight:bold; color:#ff8307'> &nbsp;Please Wait... </font></div>");
}

function hideDivWait(){
  document.getElementById('divwait').style.display = "none";
}

function showDivWait(){
  document.getElementById('divwait').style.display = "block";
}

function writeLoadMessage() {	
	var loadingMessage = jQuery('<div id="loadingMessage"></div>').appendTo('body');
	var loadingImage = jQuery('<div id="loadingImage"></div>').appendTo(loadingMessage);
	var loadingText = jQuery('<span id="loadingText">Please Wait...</span>').appendTo(loadingMessage);
	
	loadingMessage.css({
		'display': 'none',
		'background-color': '#DDDDDD',
		'border': '1px solid',
		'border-radius': '5px 5px 5px 5px',
		'padding': '12px',
		'position': 'absolute',
		'left': '40%',
		'top': '40%',
		'z-index': '100'
	});
	loadingImage.css({
		'background-image': 'url("'  + context_path + '/public/images/ajax_load_message.gif")',
		'height': '32px',
		'width': '32px',
		'float': 'left'
	});
	loadingText.css({
		'font-size': '20pt',
		'font-weight': 'bold',
		'color': '#FF8307',
		'margin-left': '10px'
	});
}

function hideLoadMessage(){
	jQuery('#loadingMessage').hide();
}

function showLoadMessage(){
	jQuery('#loadingMessage').show();
}

function frameAddSplit(){
	var f0 = parent.document.getElementsByName("frameright0")[0];
	var s1 = f0.attributes["rows"].value;
	f0.attributes["rows0"].value = s1;
	var nr = f0.attributes["rowsAdd"]!=null?f0.attributes["rowsAdd"].value:"";
	f0.attributes["rows"].value = nr;
}
function frameTopSplit(){
	var f0 = parent.document.getElementsByName("frameright0")[0];
	var s1 = f0.attributes["rows"].value;
	f0.attributes["rows0"].value = s1;
	var nr = f0.attributes["rows1"]!=null?f0.attributes["rows1"].value:"";
	f0.attributes["rows"].value = nr;
}
function frameCenterSplit(){
	var f0 = parent.document.getElementsByName("frameright0")[0];
	var s1 = f0.attributes["rows"].value;
	f0.attributes["rows0"].value = s1;
	var nr = f0.attributes["rows2"]!=null?f0.attributes["rows2"].value:"";
	f0.attributes["rows"].value = nr;
}
function frameTop(){
	var f0 = parent.document.getElementsByName("frameright0")[0];
	var s1 = f0.attributes["rows"].value;
	f0.attributes["rows0"].value = s1;
	var nr = f0.attributes["rowsTop"]!=null?f0.attributes["rowsTop"].value:"";
	f0.attributes["rows"].value = nr;
}
function frameFootAll(){
	var f0 = parent.document.getElementsByName("frameright0")[0];
	var s1 = f0.attributes["rows"].value;
	f0.attributes["rows0"].value = s1;
	var nr = f0.attributes["rows1"]!=null?f0.attributes["rows3"].value:"";
	f0.attributes["rows"].value = nr;
}

function JHshNumberText()
{
	if ( !(((window.event.keyCode >= 48) && (window.event.keyCode <= 57))))
		{
			window.event.keyCode = 0 ;
		}
} 

function writeDate(input_id){

	var dateImg = jQuery("<img src='" + context_path + "/public/images/datatable/date_picker.gif'></img>");
	dateImg.click(function(){
		disp1(input_id);
	});
	var input = jQuery('#' + input_id);
	
	// Some of these inputs don't have an ID and instead have a name
	if (input.length == 0){
		input = jQuery('input[name=' + input_id + ']');
	}
	
	// If we have found no input.
	if (input.length == 0){
		return;
	}
	
	input.parent().append(dateImg);
	dateImg.addClass('datepicker-button');
	
	input.blur(function(){
		if (input.val().length > 0 && !isValidDate(input.val())) {
			alert('The date you have entered is invalid.');
			return;
		}
	});
	
	
	//document.write(" <img class='arrow1' title='Date Format:MM/DD/YYYY' onclick=disp1('"+obj+"')");
	//document.write(" onmouseover=javascript:this.className='arrow2';  ");
	//document.write(" onmouseout=javascript:this.className='arrow1';></img>");
}


function disp1(obj){
    var s = context_path + "/public/js/calendar.jsp";
    
    var ret = window.showModalDialog(s, "", "dialogWidth:550px;"
    		+ " dialogHeight:495px;"
    		+ " dialogTop:" + (screen.availHeight-240)/2 + "px;"
    		+ " dialogLeft:" + (screen.availWidth-400)/2 + "px;");
    
    if (String(ret) != "undefined" && ret != "$$$"){
    	form1.elements[obj].value=ret;
    }
}

function isKeyNumberdot(ifdot) 
{       
    var s_keycode=(navigator.appname=="Netscape")?event.which:event.keyCode;
	if(ifdot==0) {
		if(s_keycode>=48 && s_keycode<=57) {
			return true;
		} else {
			return false;
		}
    } else if (ifdot==1) {
		if((s_keycode>=48 && s_keycode<=57) || s_keycode==46) {
		      return true;
		}else {
		    return false;
		}
    } else if (ifdot==2) {
        if((s_keycode>=48 && s_keycode<=57) || s_keycode==46 || s_keycode==45) {
		      return true;
		} else {
		    return false;
		}
    }
}

//------------------
function EncodeURL(url){
  var s = url.replace(/%/g,"%25");
  s = s.replace(/\+/g,"%2B");
//  s = s.replace(/\//g,"%2F");
  s = s.replace(/\?/g,"%3F");
  s = s.replace(/#/g,"%23");
  s = s.replace(/&/g,"%26");
  return s;
}

//Gets an XmlHttp Object for Ajax use
function GetXmlHttpObject()
{
  var xmlHttp;  
  try{
    // Firefox, Opera 8.0+, Safari
    xmlHttp=new XMLHttpRequest();
  }catch (e){
    // Internet Explorer
    try{
      xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
    }catch (e){
      try{
        xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");
      }catch (e){
        alert("Your browser does not support AJAX!");
        return false;
      }
    }
  }
  return xmlHttp;
}

//
function loadXMLData(toLoad){
  if (window.ActiveXObject){
    var doc=new ActiveXObject("Microsoft.XMLDOM");
    doc.async="false";
    doc.loadXML(toLoad);
    return doc;
  }
  // code for Mozilla, Firefox, Opera, etc.
  else{
    var parser=new DOMParser();
    var doc=parser.parseFromString(toLoad,"text/xml");
    return doc;
  }
}



//Old VBS functions -- converted to js
function URLEncoding(vstrIn){
    alert("This function should not be used: URLEncoding");
    return encodeURI(vstrIn);
}

function bytes2BSTR(vIn){
  alert("This function should not be used: bytes2BSTR");
  return vIn;
}

//Help popup functions
var IE = document.all?true:false;

function writeHelpDiv(){
  document.write("<div id='divhelp' style='position:absolute; left:300px; top:140px; z-index:100; visibility:hidden; border-style:outset; border-width:3px; padding:10px 30px; background-color:#dddddd'><font style='font-size:20pt;font-weight:bold; color:#ff8307'></font></div>");
  document.onmousemove=setDivHelpPos;
}

function showHelpDiv(msg){
  var div = document.getElementById('divhelp');
  div.innerHTML = msg;
  div.style.visibility = "visible";
}

function hideHelpDiv(){
  var div = document.getElementById('divhelp');
  div.style.visibility = "hidden";
  div.innerHTML = "";
}

function setDivHelpPos(e){
  var div = document.getElementById('divhelp');
  var tempX, tempY;
  if(IE){
    tempX = event.clientX + document.body.scrollLeft + 1 + "px";
    tempY = event.clientY + document.body.scrollTop + 1 + "px";
  }else{
    tempX = e.pageX + 1;
    tempY = e.pageY + 1;
  }

  div.style.left = tempX;
  div.style.top = tempY;
}


//expected in format (MM/DD/YYY)
function isValidDate(date_str) {
	var regex = /^[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{4,}$/;
	var d_per_month = new Array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
	
	date_str = date_str.trim();
	if (!regex.test(date_str))
		return false;
	
	var parts = date_str.split('/');
	var month = parseInt(parts[0], 10);
	var day = parseInt(parts[1], 10);
	var year = parseInt(parts[2], 10);
	
	if (month < 1 || month > 12)
		return false;
	if (day < 1 || day > 31)
		return false;
	
	//for February, the freak month
	var is_leap_year;
	if (year % 4 == 0) {
		is_leap_year = true;
		if (year % 400 == 0)
			is_leap_year = false;
	}
	if (is_leap_year)
		d_per_month[1] = 29;

	if (day > d_per_month[month - 1])
		return false;
	
	return true;
}

function isValidDateRange(start_date_str, end_date_str) {
	if (!isValidDate(start_date_str) || !isValidDate(end_date_str)) {
		return false;
	}
	
	var start_date = new Date(start_date_str);
	var end_date = new Date(end_date_str);
	
	if (start_date > end_date) {
		return false;
	}
	
	return true;	
}


function isValidMonth(month_str) {
	var regex = /^[0-9]{1,2}\/[0-9]{4,}$/;	
	month_str = month_str.trim();
	if (!regex.test(month_str))
		return false;
	
	var parts = month_str.split('/');
	var month = parseInt(parts[0], 10);
	var year = parseInt(parts[1], 10);
	
	if (month < 1 || month > 12)
		return false;
	if (year < 0)	// lets prevent some time traveling
		return false;
	
	return true;
}


var _base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

function base64Encode(decoded) {
	if (!decoded)
		return "";
	
	var encoded = "";
	var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
	var i = 0;

	while (i < decoded.length) {
		chr1 = decoded.charCodeAt(i++);
		chr2 = decoded.charCodeAt(i++);
		chr3 = decoded.charCodeAt(i++);

		enc1 = chr1 >> 2;
		enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
		enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
		enc4 = chr3 & 63;
 
		if (isNaN(chr2)) {
			enc3 = enc4 = 64;
		} else if (isNaN(chr3)) {
			enc4 = 64;
		}
 
		encoded += _base64_alphabet.charAt(enc1)
				+ _base64_alphabet.charAt(enc2)
				+ _base64_alphabet.charAt(enc3)
				+ _base64_alphabet.charAt(enc4); 
	}
 
	return encoded;
}


function base64Decode(encoded) {
	var decoded = "";
	var chr1, chr2, chr3;
	var enc1, enc2, enc3, enc4;
	var i = 0;

	encoded = encoded.replace(/[^A-Za-z0-9\+\/\=]/g, "");
	while (i < encoded.length) {
		enc1 = _base64_alphabet.indexOf(encoded.charAt(i++));
		enc2 = _base64_alphabet.indexOf(encoded.charAt(i++));
		enc3 = _base64_alphabet.indexOf(encoded.charAt(i++));
		enc4 = _base64_alphabet.indexOf(encoded.charAt(i++));

		chr1 = (enc1 << 2) | (enc2 >> 4);
		chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
		chr3 = ((enc3 & 3) << 6) | enc4;

		decoded += String.fromCharCode(chr1);
		if (enc3 != 64) {
			decoded += String.fromCharCode(chr2);
		}
		if (enc4 != 64) {
			decoded += String.fromCharCode(chr3);
		}
	}

	return decoded;
}


// these methods are directly compatible with the static methods by the same
// name in com.hcfa.util.common.Export
function serializeTabularData(values) {
	var i, j, serialized = '';
	
	for (i = 0; i < values.length; i++) {
		if (i > 0) {
			serialized += '|';
		}
		var row = values[i];
		for (j = 0; j < row.length; j++) {
			if (j > 0) {
				serialized += '^';
			}
			serialized += base64Encode(row[j]);
		}
	}
	
	return serialized;
}


function deserializeTabularData(serialized) {
	var i, j;
	
	var entries = new Array();
	var rows = serialized.split('|');
	for (i = 0; i < rows.length; i++) {
		var newRow = new Array();
		var cols = rows[i].split('^');
		for (j = 0; j < cols.length; j++) {
			newRow[j] = base64Decode(cols[j]);
		}
		entries[i] = newRow;
	}
	
	return entries;
}

function ieDeserializeTabularData(serialized) {
	return deserializeTabularData(serialized);
}

function debug_table(values) {
	var i, j, row, str = "";

	for (i = 0; i < values.length; i++) {
		str += "\n";
		row = values[i];
		for (j = 0; j < row.length; j++) {
			str += " " + row[j];
		}
	}
	
	return str;
}


function getScrollBarWidth() {
	var inner = document.createElement('p');
	inner.style.width = '100%';
	inner.style.height = '200px';
	
	var outer = document.createElement('div');
	outer.style.position = 'absolute';
	outer.style.top = '0px';
	outer.style.left = '0px';
	outer.style.visibility = 'hidden';
	outer.style.width = '200px';
	outer.style.height = '150px';
	outer.style.overflow = 'hidden';
	outer.appendChild(inner);
	
	document.body.appendChild(outer);
	var w1 = inner.offsetWidth;
	outer.style.overflow = 'scroll';
	var w2 = inner.offsetWidth;
	if (w1 == w2)
		w2 = outer.clientWidth;
	
	document.body.removeChild(outer);
	
	return (w1 - w2);
};


function resizeScrollTable(namespace, resizeBody) {
	var fixBody = resizeBody || false;
	if (fixBody) {
		resizeBodyTable(namespace);
	}
	else {
		resizeHeaderTable(namespace);
	}
}



function resizeHeaderTable(namespace) {
	var header = document.getElementById(namespace + '_header');
	var data = document.getElementById(namespace + '_data');
	var container = document.getElementById(namespace + '_container');
	var expand = document.getElementById(namespace + '_expand');
	if (!container)
		return;
	
	// collect column widths from data table
	var i, colWidths = new Array();
	for (i = 0; i < data.tBodies[0].rows[0].cells.length; i++) {
		colWidths[i] = data.tBodies[0].rows[0].cells[i].width;
	}
	// make sure header widths are in alignment with data
	for (i = 0; i < colWidths.length; i++) {
		header.tBodies[0].rows[0].cells[i].width = colWidths[i];
	}
	
	container.style.width = (data.scrollWidth + getScrollBarWidth()) + 'px';
	var extra = 0;
	try {
		if (attachEvent)	// hack for MSIE
			extra += 2;
	}
	catch (e) {}
	header.style.width = (data.scrollWidth + extra) + 'px';
	if (expand) {
		expand.style.width = (data.scrollWidth + extra) + 'px';
	}
}


function resizeBodyTable(namespace) {
	var header = document.getElementById(namespace + '_header');
	var data = document.getElementById(namespace + '_data');
	var container = document.getElementById(namespace + '_container');
	var expand = document.getElementById(namespace + '_expand');
	if (!container)
		return;
	
	// collect column widths from data table
	var i, colWidths = new Array();
	for (i = 0; i < header.tBodies[0].rows[0].cells.length; i++) {
		colWidths[i] = header.tBodies[0].rows[0].cells[i].width;
	}
	// make sure table widths are in alignment with header
	for (i = 0; i < colWidths.length; i++) {
		data.tBodies[0].rows[0].cells[i].width = colWidths[i];
	}
	
	container.style.width = (header.scrollWidth + getScrollBarWidth()) + 'px';
	var extra = 0;
	try {
		if (attachEvent)	// hack for MSIE
			extra += 2;
	}
	catch (e) {}
	data.style.width = (header.scrollWidth + extra) + 'px';
	if (expand) {
		expand.style.width = (header.scrollWidth + extra) + 'px';
	}
}


function expandTable(namespace) {
	var toggle = document.getElementById(namespace + '_expand');
	var container = document.getElementById(namespace + '_container');
	container.style.height = 'auto';
	toggle.style.display = 'none';
}


function isIntegerValue(rawVal) {
	var regex = /^\d+$/;
	return regex.test(rawVal.trim());
}


function valueInRange(lowBound, highBound, value) {
	if (lowBound > highBound) {
		throw ("Invalid range");
	}
	
	return value >= lowBound && value <= highBound;
}


var _lastRowToggled = null;
function toggleRowHighlight(row) {
	if (_lastRowToggled != null && _lastRowToggled != row) {
		toggleRowHighlight(_lastRowToggled);
	}
	if (row.className == 'opened')
		row.className = 'unopen';
	else
		row.className = 'opened';
	_lastRowToggled = row;
}


function setDrilldownLetter(letter) {
	var doc = parent.selector.document;
	var field = doc.getElementById('last_name');
	if (letter == null) {
		field.value = '';
	}
	else {
		field.value = letter;
	}
	doc.forms['form1'].submit();
}


//expects date string in format 'MM/DD/YYYY'
function strToDate(serv_date) {
	var parts = serv_date.split('/');
	var year = parseInt(parts[2], 10);
	var month = parseInt(parts[0], 10);
	var day = parseInt(parts[1], 10);
	
	return new Date(year, month - 1, day);
}


function isValidDateObj(dateObj) {
	try {
		return !isNaN(dateObj.getTime());
	} catch (e) {
		return false;
	}
}


//returns string in format MM/DD/YYYY (0-padded month, day)
function dateToStr(dateObj) {
	var monthStr = '' + (dateObj.getMonth() + 1);	// implicit cast to string
	if (monthStr.length == 1)
		monthStr = '0' + monthStr;
	var dayStr = '' + dateObj.getDate();			// cast to string
	if (dayStr.length == 1)
		dayStr = '0' + dayStr;
	var year = dateObj.getFullYear();
	
	return monthStr + '/' + dayStr + '/' + year;
}


// expects time in format: "HH:MI AM" or "HH:MI:AM"
function timeToDate(time_str) {
	if (time_str.length == 0){
		return new Date(0, 0, 0, 0, 0, 0);
	}
	
	var hour, minute, meridian, arr, times;

	if (time_str == '--:--:--') {
		hour = 0;
		minute = 0;
		meridian = 'AM';
	}
	else {
		arr = time_str.split(':');
		if (arr.length == 3) {
			hour = parseInt(arr[0], 10);
			minute = parseInt(arr[1], 10);
			meridian = arr[2].toUpperCase();
		}
		else {
			arr = time_str.split(' ');
			times = arr[0].split(':');
			hour = parseInt(times[0], 10);
			minute = parseInt(times[1], 10);
			meridian = arr[1].toUpperCase();
		}
	}
	if (hour == 12)
		hour = 0;
	if (meridian == 'PM') {
		hour += 12;
	}
	var date = new Date(0, 0, 0, hour, minute, 0);
//	alert(time_str + ' => ' + date);
	
	return date;
}


// returns string in format 'HH:MI AM'
function timeToStr(dateObj) {
	var meridian = 'AM';
	var hours = dateObj.getHours();
	if (hours >= 12) {
		hours -= 12;
		meridian = 'PM';
	}
	if (hours == 0)
		hours = '12';
	var minutes = dateObj.getMinutes();
	
	return hours + ':' + (minutes < 10 ? '0' : '') + minutes + ' ' + meridian;
}


// make sure student parental consent dates are valid if present on page
function checkStudentPCDates() {
	var dates = jQuery('.pcDate');
	var nameRegex = /^consent(Expire|Start)(\d+)/;
	var startDate, expDate;
	
	//If only two dates in DOM they may both be blank, but if one
	//Is not blank then validation should happen on all
	if (dates.length == 2 && dates[0].value == '' && dates[1].value == '') {
		return true;
	}
	
	for (var i = 0; i < dates.length; i++) {
		var fname, parts, row, col, range;
		fname = jQuery(dates[i]).attr('name').trim();
		parts = nameRegex.exec(fname);
		col = parts[1].toLowerCase();
		row = parseInt(parts[2], 10);
		switch (row) {
			case 0: range = '1st'; break;
			case 1: range = '2nd'; break;
			case 2: range = '3rd'; break;
			default: range = (row + 1) + 'th'; break;
		}
		if (!isValidDate(dates[i].value)) {
			// date not in format MM/DD/YYYY
			alert('invalid date (' + dates[i].value + ') in '
				+ range + ' parent consent ' + col + ' date');
			return false;
		}
		if (col == 'expire') {
			// validate date range integrity
			startDate = strToDate(dates[i - 1].value);
			expDate = strToDate(dates[i].value);
			if (startDate >= expDate) {
				alert('invalid ' + range + ' date range: '
					+ dates[i - 1].value + ' - ' + dates[i].value);
				return false;
			}
		}
	}
	
	return true;
}


function dateIsWeekday(day, month, year) {
	var monthInt = parseInt(month, 10) - 1;
	var date = new Date(year, monthInt, day);
	
	return date.getDay() != 0 && date.getDay() != 6;
}


function getStackTrace(e) {
	var entry;
	var callstack = [];
	var isCallstackPopulated = false;

	if (e.stack) { // Firefox
		var lines = e.stack.split('\n');
		for (var i=0, len=lines.length; i<len; i++) {
			if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
				entry = '\tin ' + lines[i].split(/\)/)[0] + ')';
				callstack.push(entry);
			}
		}
		//Remove call to printStackTrace()
		//callstack.shift();
		isCallstackPopulated = true;
	}
	else if (window.opera && e.message) { // Opera
		var lines = e.message.split("\n");
		for (var i=0, len=lines.length; i<len; i++) {
			if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
				entry = '\tin ' + lines[i];
				//Append next line also since it has the file info
				if (lines[i+1]) {
					//entry += " at " + lines[i+1];
					i++;
				}
				callstack.push(entry);
			}
		}
		//Remove call to printStackTrace()
		//callstack.shift();
		isCallstackPopulated = true;
	}

	if (!isCallstackPopulated) { //IE and Safari
		var currentFunction = arguments.callee.caller;
		while (currentFunction) {
			var fn = currentFunction.toString();
			var fname = fn.substring(fn.indexOf("function") + 8,
					fn.indexOf("(")) || "anonymous";
			callstack.push(fname);
			currentFunction = currentFunction.caller;
		}
	}

	return callstack;
}

/**
 * Function : dump()
 * Arguments: The data - array,hash(associative array),object
 *    The level - OPTIONAL
 * Returns  : The textual representation of the array.
 * This function was inspired by the print_r function of PHP.
 * This will accept some data as the argument and return a
 * text that will be a more readable version of the
 * array/hash/object that is given.
 * Docs: http://www.openjs.com/scripts/others/dump_function_php_print_r.php
 * 
 * This function is 'borrowed'... but since this file is being transmitted to the client,
 * does that count as 'distributing the source code'?
 */
function dump(arr,level, rlimit, ilimit, html) {
	var dumped_text = "";
try
{
	if(!level) level = 0;
	if(!rlimit) rlimit = 3;
	if(!ilimit) ilimit = 20;
	if(!html) html = false;
	
	var bc = '\n';
	var padchar = "    ";
	if(html == true){
		bc = '<br/>';
		padchar = "&nbsp; ";
	}
	
	if(level > rlimit) return "";
	
	//The padding given at the beginning of the line.
	var level_padding = "";
	for(var j=0;j<level+1;j+=1) level_padding += padchar;
	if(typeof(arr) == 'object') { //Array/Hashes/Objects
		var i = 0;
		for(var item in arr) {
			if(i > ilimit)
				break;
			var value = arr[item]; //problem here??
			
			if(typeof(value) == 'object') { //If it is an array,
				dumped_text += level_padding + "'" + item + "' ..."+ bc;
				dumped_text += dump(value,level+1, rlimit,ilimit,html);
			} else {
				dumped_text += level_padding + "'" + item + "' =) \"" + value + "\"" + bc;
			}
			i+=1;
		}
	} else { //Stings/Chars/Numbers etc.
		dumped_text = "===)"+arr+"(===("+typeof(arr)+")";
	}
}catch (e) {				
		var sTrace = '';
		if (e.stack) {
			var cStack = getStackTrace(e);
			sTrace = '\n\n' + cStack.join('\n');
		}
		
		var context = '';
		var funcName = 'dump';
		
		alert('JavaScript method ' + funcName + ' failed:\n\n'
			 + context + '\t' + e.message + sTrace);
}
	return dumped_text;
}


// used to get size of an associative array
function arrSize(arr) {
	size = 0;
	for (var key in arr) {
		size++;
	}

	return size;
}


var _jqueryClockOpts = {
		defaultTime: timeToDate('08:00 AM'),
		timeSteps: [1, 1, 0],
		initialField: 1,
		ampmPrefix: ' ',
		spinnerSize: [15, 16, 0],
		spinnerBigSize: [30, 32, 0],
		spinnerImage: context_path + '/public/images/spinnerUpDown.png',
		useMouseWheel: false,
		spinnerIncDecOnly: true	
};

var _jqueryBillingClockOpts = {
		defaultTime: timeToDate('08:00 AM'),
		timeSteps: [1, 1, 0],
		initialField: 0,
		ampmPrefix: ' ',
		spinnerSize: [15, 16, 0],
		spinnerBigSize: [30, 32, 0],
		spinnerImage: context_path + '/public/images/spinnerUpDown.png',
		useMouseWheel: false,
		spinnerIncDecOnly: true	
};

// Therapy Note escape 
function escapeTherapyNote(note) {
	var escaped = encodeURIComponent(note);
	escaped = escaped.replace(/%20/g, " ");		// %20 is ugly in the DB
	escaped = escaped.replace(/\+/g, "%2B");	// weird that JS escape() ignores '+'
	
	return escaped;
}


// Goal escape
function escapeGoal(goal) {
	return escapeTherapyNote(goal);
}


function jQueryNotice(message, options) {
	var settings = {
		title: 'Notice'
	};
	jQuery.extend(settings, options);
	jQueryAlert(message, settings);
}
//Create a generic Alert jQuery error dialog.
function jQueryAlert(message, options) {
	var settings = {
		title: 'Alert',
		width: '380',
		height: 'auto',
		fnOnClose: function() {}
	};
	jQuery.extend(settings, options);
	
	var dialog = jQuery('<div>' + message + '</div>').dialog({
		closeOnEscape: true,
		resizable: false,
		draggable: true,
		bgiframe: true,
		autoOpen: false,
		width: settings.width,
		height: settings.height,
		modal: false,
		title: settings.title,
		buttons: {
			'Okay': function() { 
				jQuery(this).dialog('close'); 
			}
		},
		open: function(ev, ui) {
			var modal = jQuery('<div class="jquery_alert_message"></div>');
			modal.css({'width': jQuery(document).width(), 'height': jQuery(document).height()});
			modal.appendTo('body').show();
		},
		close: function(ev, ui) {
			settings.fnOnClose();
			jQuery(this).remove();
			jQuery('div.jquery_alert_message').remove();
		}
	});
	
	dialog.dialog('open');
	if (jQuery.browser.msie) {
		if (settings.height == 'auto') {
			// hack to fix stupid jQuery Dialog auto height issue in MSIE
			var newHeight = parseInt(dialog.css('height'), 10) + 28;
			dialog.css('height', newHeight);
		}
	}
}

// Create a generic jQuery confirm dialog replacement
// pass in event handlers for "Okay", "Cancel" events
function jQueryConfirm(message, options) {
	var settings = {
		title: 'Confirm',
		height: 'auto',
		width: '380',
		confirmLabel: 'Okay',
		fnOnConfirm: function() {},
		cancelLabel: 'Cancel',
		fnOnCancel: function() {}
	};
	jQuery.extend(settings, options);
	
	var dialogOpts = {
		closeOnEscape: false,
		resizable: false,
		draggable: settings.draggable || false,
		bgiframe: true,
		autoOpen: false,
		width: settings.width,
		height: settings.height,
		modal: false,
		title: settings.title,
		buttons: {},
		open: function(ev, ui) {
			var modal = jQuery('<div class="jquery_alert_message"></div>');
			modal.css({'width': jQuery(document).width(), 'height': jQuery(document).height()});
			modal.appendTo('body').show();
			// set initial focus on Cancel button
			jQuery(this).parents('.ui-dialog-buttonpane button:eq(1)').focus(); 
			// set initial focus on Cancel button
			jQuery(this).parents('.ui-dialog-buttonpane button:eq(1)').focus(); 
		},
		close: function(ev, ui) {
			jQuery(this).remove();
			jQuery('div.jquery_alert_message').remove();
		}
	};
	// add buttons (labels and event handlers)
	dialogOpts.buttons[settings.confirmLabel] =	function() { 
		jQuery(this).dialog('close');
		settings.fnOnConfirm();
	};
	dialogOpts.buttons[settings.cancelLabel] = function() {
		jQuery(this).dialog('close');
		settings.fnOnCancel();
	};
	var dialog = jQuery('<div>' + message + '</div>').dialog(dialogOpts);
	dialog.dialog('open');
	if (jQuery.browser.msie) {
		if (settings.height == 'auto') {
			// hack to fix stupid jQuery Dialog auto height issue in MSIE
			var newHeight = parseInt(dialog.css('height'), 10) + 28;
			dialog.css('height', newHeight);
		}
	}
	// remove the 'X' button on this dialog, confirm only requires Okay, Cancel
	jQuery('div.ui-dialog a.ui-dialog-titlebar-close').remove();
}


// call this function within a jQuery(document).ready code block
function applyChromeFormSubmitFix() {
	jQuery(':input').each(function() {
		if (this.type != 'textarea') {
			// textareas behave different, newlines don't submit form
			jQuery(this).keypress(function(event) {
				if (event.keyCode == 13 || event.which == 13) {
					return false;
				}
	
				return true;
			});
		}
	});
}

function applyFormSubmit(formJQObj) {
	if (formJQObj == undefined) {
		jQuery(':input').each(function() {
			if (this.type != 'textarea') {
				// textareas behave different, newlines don't submit form
				jQuery(this).keypress(function(event) {
					if (event.keyCode == 13 || event.which == 13) {
						jQuery('#form1').submit();
						return false;
					}
		
					return true;
				});
			}
		});
	} else {
		jQuery(':input', formJQObj).each(function() {
			if (this.type != 'textarea') {
				// textareas behave different, newlines don't submit form
				jQuery(this).keypress(function(event) {
					if (event.keyCode == 13 || event.which == 13) {
						formJQObj.submit();
						return false;
					}
		
					return true;
				});
			}
		});
	}
}

function getCurrentDateRange() {
	var currentDate = new Date();
	
	var year = currentDate.getFullYear();
	year = parseInt(year, 10);
	
	var schoolYearStart = new Date('08/31/' + year);
	
	var month = currentDate.getMonth();
	if (month.length == 1) {
		month = '0' + month;
	}	
	var day = currentDate.getDay();
	if (day.length == 1) {
		day = '0' + day;
	}
		
	if (currentDate > schoolYearStart) {
		return '07/01/' + year + '|' + '06/30/' + (year + 1);
	}

	return '07/01/' + (year - 1) + '|' + '06/30/' + year;
}

function setCurrentDateRange() {
	var currentDate = new Date();
	
	var year = currentDate.getFullYear();
	year = parseInt(year, 10);
	
	var schoolYearStart = new Date('08/31/' + year);
	
	var month = currentDate.getMonth();
	if (month.length == 1) {
		month = '0' + month;
	}	
	var day = currentDate.getDay();
	if (day.length == 1) {
		day = '0' + day;
	}
	
	if (currentDate > schoolYearStart) {
		jQuery('#start_date').val('07/01/' + year);
		jQuery('#end_date').val('06/30/' + (year + 1));
	} else {
		jQuery('#start_date').val('07/01/' + (year - 1));
		jQuery('#end_date').val('06/30/' + year);
	}
}

//checks to make sure the email field contains a valid email address
function isValidEmail(email) {
	var i = email.length;

	if (i > 0) {
		var atloc = email.indexOf('@');
		var dotloc = email.indexOf('.');
		
		//both @ and . are present
		//if @ is second or later character
		//and @ is at least three characters from the end
		//and . is at least one character from the end
		if ((atloc != -1 && dotloc != -1) && (atloc > 0) && (i - atloc > 3) && (i - dotloc > 1)) {
			return true;
		}
	}

	return false;
}

// replaces ? characters in order with value[s] found in array of strings
function buildErrorMessage(errorStr, args) {
	if (args == undefined || args == null ||
			(jQuery.isArray(args) && args.length == 0)) {
		return errorStr;
	}
	
	var regex = /\?/;
	if (!jQuery.isArray(args)) {
		// not an array of arguments, replace single value if possible
		return errorStr.replace(regex, args);
	}
	
	// iterate over array of values
	var newErrStr = errorStr;
	for (var i = 0; i < args.length; i++) {
		newErrStr = newErrStr.replace(regex, args[i]);
	}
	
	return newErrStr;
}


function highlightErrorField(inputField) {
	inputField.focus();
	jQuery(inputField).parent().addClass('errorCell');
	jQuery(inputField).change(function() {
		jQuery(this).parent().removeClass('errorCell');
	});
}


function errorOnField(inputField, errorMsg) {
	if (errorMsg != undefined && errorMsg != null) {
		alert(errorMsg);
	}
	highlightErrorField(inputField);
}


// a shared onkeydown jQuery handler to ensure only numeric keys and backspace
// is allowed for data entry
function denyNonPositiveIntChars(event) {
	// 0-9 (top)
	if (event.keyCode >= 48 && event.keyCode <= 57) {
		return true;
	}
	// 0-9 (numpad)
	if (event.keyCode >= 96 && event.keyCode <= 105) {
		return true;
	}
	// backspace, tab, delete
	if (event.keyCode == 8 || event.keyCode == 9 || event.keyCode == 46) {
		return true;
	}
	// left, right arrows
	if (event.keyCode == 37 || event.keyCode == 39) {
		return true;
	}
	
	event.preventDefault();
	return false;
}


// a shared onchange jQuery handler to ensure only positive integer values
// are allowed in the input field. If an invalid value is detected it is
// reset to 0.
function denyNonPositiveIntValue(event) {
	var regex = /^\s*[0-9]+\s*$/;
	if (!regex.test(jQuery(this).val())) {
		jQuery(this).val(0);
	}
}


//the ServiceRules class encapsulates service rule data and methods
function ServiceRules() {}


ServiceRules.prototype.isGroupService = function(serv_id) {
	for (var i = 0; i < this.groupServices.length; i++) {
		if (this.groupServices[i] == serv_id) {
			return true;
		}
	}
	
	return false;
}


ServiceRules.prototype.getService = function(serv_abbr) {
	var serv_code = serv_abbr.toString().toUpperCase();

	for (var i = 0; i < this.rules.length; i++) {
		var service = this.rules[i];
		if (service.abbrev == serv_code) {
			return service;
		}
	}
	
	return null;
}

ServiceRules.prototype.getServAbbr = function(serv_id) {
	for (var i = 0; i < this.rules.length; i++) {
		var service = this.rules[i];
		if (service.servId == serv_id) {
			return service.abbrev;
		}
	}
	
	return null;
}

ServiceRules.prototype.isValidService = function(serv_abbr) {
	return this.getService(serv_abbr) != null;
}


ServiceRules.prototype.isBillableService = function(serv_abbr) {
	var service = this.getService(serv_abbr);
	if (service == null) {
		return false;
	}
	
	return service.procCode != this.procNotBilled;
}

ServiceRules.prototype.isGroupService = function(serv_abbr) {
	var service = this.getService(serv_abbr);
	if (service == null) {
		return false;
	}
	
	return service.isGroupService;
}


ServiceRules.prototype.getServName = function(serv_abbr) {
	var servName, service = this.getService(serv_abbr);
	if (service == null) {
		return null;
	}
	servName = service.service;
	
	if (service.procCode == this.procNotBilled) {
		var msg = serv_abbr=='NB'?'Not Billed':'No Service, Not Billed';
		servName = '<i>' + msg + '</i>';
	}
	
	return servName;
}


ServiceRules.prototype.getServProcCode = function(serv_abbr) {
	var procCode, service = this.getService(serv_abbr);
	if (service == null) {
		return null;
	}
	
	return service.procCode;
}


ServiceRules.prototype.getServDescription = function(serv_abbr) {
	var procCode, service = this.getService(serv_abbr);
	if (service == null) {
		return null;
	}
	
	return service.descr;
}

ServiceRules.prototype.getServId = function(serv_abbr) {
	var service = this.getService(serv_abbr);
	if (service == null) {
		return -1;
	}
	
	return service.servId;
}


ServiceRules.prototype.getNumUnits = function(serv_date, serv_abbr, minutes) {
	var service, policy, numUnits;
	service = this.getService(serv_abbr);
	if (service == null) {
		return 0;
	}
	if (service.procCode == this.procNotBilled) {
		return 0;
	}
	if (minutes == 0) {
		// even events of 0 minutes should return 0 units
		return 0;
	}
	
	for (var i = 0; i < service.policies.length; i++) {
		policy = service.policies[i];
		var sDate = strToDate(policy.startDate);
		var eDate = strToDate(policy.expireDate);
		if (serv_date >= sDate && serv_date <= eDate) {
			break;
		}
	}
	if (policy.isEvent) {
		return 1;
	}

	// calcUnits() specified dynamically in bill_claims.jsp for correct instance
	numUnits = this.calcUnits(minutes, policy.minPerUnit, this.getServProcCode(serv_abbr));
	if (numUnits > policy.maxUnits) {
		numUnits = policy.maxUnits;
	}
	
	return numUnits;
}


ServiceRules.prototype.isDoses = function(serv_date, serv_abbr) {
	var policy = null;
	var service = this.getService(serv_abbr);
	
	if (service == null) {
		return false;
	}
	
	if (service.procCode == this.procNotBilled) {
		return false;
	}
	
	for (var i = 0; i < service.policies.length; i++) {
		var sDate = strToDate(service.policies[i].startDate);
		var eDate = strToDate(service.policies[i].expireDate);
		if (serv_date >= sDate && serv_date <= eDate) {
			policy = service.policies[i];
			break;
		}
	}
	
	if (policy == null) {
		return false;
	}
	
	return policy.isDoses;
}

ServiceRules.prototype.getActivityId = function(serv_date, serv_abbr) {
	var policy = null;
	
	var serv_code = serv_abbr.toString().toUpperCase();

	for (var i = 0; i < this.rules.length; i++) {
		var service = this.rules[i];
		if (service.abbrev == serv_code) {
			for (var j = 0; j < service.policies.length; j++) {
				var sDate = strToDate(service.policies[j].startDate);
				var eDate = strToDate(service.policies[j].expireDate);
				if (serv_date >= sDate && serv_date <= eDate) {
					policy = service.policies[j];
					break;
				}
			}
		}
	}
	
	if (policy == null) {
		return 0;
	}
	
	return policy.activityId;
}

ServiceRules.prototype.getICD9 = function(serv_date, serv_abbr) {
	var policy = null;
	
	var serv_code = serv_abbr.toString().toUpperCase();

	for (var i = 0; i < this.rules.length; i++) {
		var service = this.rules[i];
		if (service.abbrev == serv_code) {
			for (var j = 0; j < service.policies.length; j++) {
				var sDate = strToDate(service.policies[j].startDate);
				var eDate = strToDate(service.policies[j].expireDate);
				if (serv_date >= sDate && serv_date <= eDate) {
					policy = service.policies[j];
					break;
				}
			}
		}
	}
	
	if (policy == null) {
		return "";
	}
	
	return policy.icd9_code;
}


//the PasswordRules class encapsulates password policy
function PasswordRules() {}

PasswordRules.prototype.setUsername = function(user) {
	this.username = user;
}

// returns the number of recently used, disallowed passwords
PasswordRules.prototype.getPreviousNumAllowed = function() {
	var prevDisallowed = parseInt(this.numPrevDisallowed, 10);
	var daysToExpire = parseInt(this.daysToExpire, 10);
	if (daysToExpire == 0) {
		// district doesn't expire passwords, or disallow previous passwords
		prevDisallowed = 0;
	}
	
	return prevDisallowed;
}

// returns an array of human-readable password policies
PasswordRules.prototype.getPolicies = function() {
	var policies = [];
	var numDisallowed = this.getPreviousNumAllowed();
	var minLength = parseInt(this.minLength, 10);
	var maxLength = parseInt(this.maxLength, 10);
	var requiresDigit = this.requiresDigit == '1';
	var requiresUCase = this.requiresUCase == '1';
	var requiresLCase = this.requiresLCase == '1';
	
	policies[policies.length] = buildErrorMessage(passwd_rule_length, [minLength, maxLength]);
	if (requiresDigit) {
		policies[policies.length] = passwd_rule_digit;
	}
	if (requiresUCase && requiresLCase) {
		policies[policies.length] = passwd_rule_case;		
	} else if (requiresUCase) {
		policies[policies.length] = passwd_rule_ucase;
	} else if (requiresLCase) {
		policies[policies.length] = passwd_rule_lcase;
	}
	policies[policies.length] = passwd_rule_specchar;
	policies[policies.length] = passwd_rule_uname;
	if (numDisallowed > 0) {
		policies[policies.length] = buildErrorMessage(passwd_rule_prevused, numDisallowed);
	}
	
	return policies;
}

// returns an array of all the errors with the password, if any
PasswordRules.prototype.passErrors = function(password) {
	var regex, errs = [];
	var numDisallowed = this.getPreviousNumAllowed();
	var minLength = parseInt(this.minLength, 10);
	var maxLength = parseInt(this.maxLength, 10);
	var requiresDigit = this.requiresDigit == '1';
	var requiresUCase = this.requiresUCase == '1';
	var requiresLCase = this.requiresLCase == '1';
	if (password == this.username) {
		errs[errs.length] = invalid_passwd_uname;
	}
	if (password.length < minLength || password.length > maxLength) {
		errs[errs.length] =
			buildErrorMessage(passwd_rule_length, [minLength, maxLength]);
	}
	if (requiresDigit) {
		regex = /.*[0-9]+.*/;
		if (!regex.test(password)) {
			errs[errs.length] = passwd_rule_digit;
		}
	}
	var caseError = false;
	if (requiresUCase) {
		regex = /.*[A-Z]+.*/;
		if (!regex.test(password)) {
			if (requiresLCase) {
				caseError = true;
				errs[errs.length] = passwd_rule_case;
			} else {
				errs[errs.length] = passwd_rule_ucase;
			}
		}
	}
	if (!caseError && requiresLCase) {
		regex = /.*[a-z]+.*/;
		if (!regex.test(password)) {
			if (requiresUCase) {
				errs[errs.length] = passwd_rule_case;
			} else {
				errs[errs.length] = passwd_rule_lcase;
			}
		}
	}
	// make sure no illegal characters are used
	regex = /^[A-Za-z0-9!@#\$%\^&\*\(\)]+$/;
	if (!regex.test(password)) {
		errs[errs.length] = invalid_passwd_badchar;
	}
	
	return errs;
}


function buildPasswordTooltip(passRules, passField, position, offsets) {
	var parent = jQuery(passField).parent();
	
	var tooltip_icon = jQuery('<img src="' + context_path
			+ '/public/images/tango/help-browser.png" id="tooltip" alt="tooltip" />');
	var tooltip_div = jQuery('<div id="tooltip_dialog" '
		+ 'title="move cursor out of tooltip to hide"> '
		+ '<img alt="close" title="close tooltip" id="tooltip_close" '
		+ 'class="clickable"  src="' + context_path + '/public/images/cancel.png" /> '
		+ '<div id="tooltip_msg"></div></div>');
	parent.append(tooltip_icon);
	parent.append(tooltip_div);
	
	// set password policy on tooltip dialog
	var tool_dialog = jQuery('#tooltip_msg');
	tool_dialog.html('<div class="title">Password Rules</div>');
	var policies = passRules.getPolicies();
	var ul = jQuery('<ul></ul>');
	tool_dialog.append(ul);
	for (var i = 0; i < policies.length; i++) {
		var li = jQuery('<li>' + policies[i] + '</li>');
		ul.append(li);
	}
	
	tooltip = jQuery('#tooltip').tooltip({
		position: position,
		offset:	offsets,
		events: {
			def: 'mouseover,mouseout',
			tooltip: 'mouseover,mouseout'
		}
	});
	
	jQuery('#tooltip_close').click(function() {
		tooltip.tooltip().hide();
	});
	jQuery('#tooltip_close').mouseover(function() {
		jQuery(this).fadeTo('fast', 1.0);
	});
	jQuery('#tooltip_close').mouseout(function() {
		jQuery(this).fadeTo('fast', 0.5);
	});
}



function setMonthsOnSelector(monthSelect, curYear, yearChanging) {
	var monthLabels = [	'BADMONTH',
		'Jan', 'Feb', 'Mar', 'Apr', 'May', 'June',
		'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'
	];
	var startYear = monthSelect.data('minYear');
	var startMonth = monthSelect.data('minMonth');
	var endYear = monthSelect.data('maxYear');
	var endMonth = monthSelect.data('maxMonth');
	var curMonth = null;
	if (yearChanging) {
		curMonth = monthSelect.val();
	}
	var first = 1, last = 12;
	if (curYear == startYear) {
		first = startMonth;
	}
	if (curYear == endYear) {
		last = endMonth;
	}
	
	monthSelect.find('option').remove();
	for (var i = first; i < last + 1; i++) {
		var option = jQuery(document.createElement('option'));
		option.text(monthLabels[i]).val(( i < 10 ? '0' : '') + i);
		monthSelect.append(option);
	}

	if (yearChanging &&
			jQuery('option[value=' + curMonth + ']', monthSelect).length > 0) {
		// currently select month is present, use it again
		monthSelect.val(curMonth);
	} else {
		if (curYear == startYear) {
			monthSelect.val((startMonth < 10 ? '0' : '') + startMonth);
		}
		if (curYear == endYear) {
			monthSelect.val((endMonth < 10 ? '0' : '') + endMonth);		
		}
	}
}


// yearCell is element that will contain year widget
// monthCell is element that will contain month widget
// firstMonth is first selectable month, value is string "YYYYMM"
// lastMonth is last selectable month, value is string "YYYYYMM"
function buildYearMonth(yearCell, monthCell, firstMonth, lastMonth) {
	var startYear = parseInt(firstMonth.substring(0, 4), 10);
	var startMonth = parseInt(firstMonth.substring(4), 10);
	var endYear = parseInt(lastMonth.substring(0, 4), 10);
	var endMonth = parseInt(lastMonth.substring(4), 10);
	
	var yearSelect = jQuery(document.createElement('select'));
	yearSelect.attr({ 'id': 'year', 'name': 'year' });
	yearCell.append(yearSelect);
	for (var i = endYear; i >= startYear; i--) {
		var option = jQuery(document.createElement('option'));
		option.text(i).val(i);
		yearSelect.append(option);
	}
	yearSelect.val(endYear);
	var monthSelect = jQuery(document.createElement('select'));
	monthSelect.attr({ 'id': 'month', 'name': 'month' });
	// store for future reference in event handlers
	monthSelect.data('minMonth', startMonth);
	monthSelect.data('minYear', startYear);
	monthSelect.data('maxMonth', endMonth);
	monthSelect.data('maxYear', endYear);
	monthCell.append(monthSelect);
	// set current months for this year
	setMonthsOnSelector(monthSelect, endYear, false);

	// adjust disabled months depending on year
	yearSelect.change(function() {
		var newYear = parseInt(jQuery(this).val(), 10);
		setMonthsOnSelector(monthSelect, newYear, true);
	});
}


function jQueryCheckInputDialog(inputProblems) {
	var table = '<table style="text-align: center;">';
	var problems = inputProblems.trim().split('\n');
	for (var i = 0; i < problems.length; i++) {
		var parts = problems[i].split(':');
		var field = '<b> ' + parts[0].trim() + '</b>';
		parts.shift();
		var message = parts.join(':');
		table += '<tr><td align="right" valign="top">' + field + ':</td>'
				+ '<td align="left" valign="top" class="paddedVal">'
				+ message + '</td></tr>';
	}
	table += '</table>';
	jQueryAlert(table, { title: 'Please Fix The Following Problems' });
}


function duplicateRecordsConfirm(json, colDefs, options) {
	var dialogOpts = {
		width: 380,
		title: 'Potential Matching ' + json.recordType.capitalize() + ' Records Found',
		confirmLabel: 'Okay',
		fnOnConfirm: function() {},
		cancelLabel: 'Cancel',
		fnOnCancel: function() {}
	};
	jQuery.extend(dialogOpts, options);
	
	
	var dupeTable = '<table class="resultTb nowrap"><tr class="resultHdTd">';
	for (var i = 0; i < colDefs.length; i++) {
		dupeTable += '<th addClass="resultHdTd">' + colDefs[i].label + '</th>';
	}
	dupeTable += '</tr>';
	for (var i = 0; i < json.matches.length; i++) {
		dupeTable += '<tr class="resultTr">';
		for (var j = 0; j < colDefs.length; j++) {
			var value = json.matches[i][colDefs[j].index].trim().toUpperCase();
			var matches = false;
			if (colDefs[j].search != undefined && value.length > 0) {
				matches = value == colDefs[j].search.trim().toUpperCase();
			}
			dupeTable += '<td class="paddedVal' + (matches ? ' search_match' : '')
						+ '">' + value + '</td>';
		}
		dupeTable += '</tr>';
	}
	dupeTable += '</table>';
	
	jQueryConfirm(dupeTable, dialogOpts);
}


// dynamically resizes an iframe's height on the inner document's load event.
// this is mostly to hide evidence of using an iframe such that it behaves like
// another HTML element such as a div block
function resizeIFrameForContent() {
	var iFrame = jQuery(this);
	var innerDoc = (iFrame.get(0).contentDocument) ? iFrame.get(0).contentDocument
				: iFrame.get(0).contentWindow.document;
	iFrame.height(innerDoc.body.scrollHeight + 10);
}


function jsonTabularData(twoDimArr) {
	if (twoDimArr == undefined || twoDimArr == null) {
		return null;
	}
	
	return $.toJSON(twoDimArr);
}


function validateDirPath(evt) {
	var field = jQuery(this);
	// trim whitespace
	field.val(trim(field.val()));
	
	// early exit if blanks are allowed and value is blank
	if (evt.data != undefined && evt.data.allowBlank != undefined
			&& evt.data.allowBlank) {
		if (field.val() == '') {
			field.parent().removeClass('errorCell');
			return;
		}
	}
	
	// add trailing slash if missing
	var lastChar = field.val().substr(field.val().length - 1);
	if (lastChar != '/' && lastChar != '\\') {
		if (field.val().indexOf('/') != -1) {
			field.val(field.val() + '/');
		} else  if (field.val().indexOf('\\') != -1) {
			field.val(field.val() + '\\');
		}
	}
	if (evt.data != undefined && evt.data.allowNet != undefined
			&& evt.data.allowNet) {
		// ex: //192.168.0.4/d$/
		var net_regex = /^\/\/([^\\\|\/:\*\?<>"]+\/)+$/;
		if (field.val().match(net_regex)) {
			field.parent().removeClass('errorCell');
			return;
		}
	}
	// ex: C:/path/to/dir/ or C:\path\to\dir\
	var local_regex = /^[A-z]:(\\([^\\\|\/:\*\?<>"]+\\)+|\/([^\\\|\/:\*\?<>"]+\/)+)$/;
	if (field.val().match(local_regex)) {
		field.parent().removeClass('errorCell');
		return;
	}
	
	// does not match either directory type, mark as an error
	field.parent().addClass('errorCell');
}


function validateWindowsFilepath(evt) {
	var field = jQuery(this);
	var value = trim(field.val());
	
	// trim whitespace
	field.val(value);
	
	// early exit if blanks are allowed and value is blank
	if (evt.data != undefined && evt.data.allowBlank != undefined
			&& evt.data.allowBlank) {
		if (field.val() == '') {
			field.parent().removeClass('errorCell');
			return;
		}
	}
	
	// remove leading, trailing slashes
	var noslash_regex = /^[\\\/]*([^\\\/]*)[\\\/]*$/;
	var groups = noslash_regex.exec(value);
	if (groups.length == 2) {
		value = groups[1];
		field.val(value);
	}
	
	var char_regex = /^[^\\\|\/:\*\?<>"]+$/;
	if (char_regex.test(value)) {
		field.parent().removeClass('errorCell');
	} else {
		field.parent().addClass('errorCell');		
	}
}


function formatGUID(evt) {
	var field = $(this);
	var value = trim(field.val().toUpperCase());
	value = value.replace(/[^0-9A-F]/g, '');	// remove punctuation
	if (value.length != 0 && value.length != 32) {
		var msg = 'GUID expected as 32 hexadecimal characters'
			+ '<br/><br/>ex: <b>{AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}</b>';
		jQueryAlert(msg, { width: 450 });
		return;
	}

	// format GUID to uppercase, add punctuation
	var regex = /^([0-9A-F]{8,})([0-9A-F]{4,})([0-9A-F]{4,})([0-9A-F]{4,})([0-9A-F]{12,})$/;
	value = value.replace(regex, '{$1-$2-$3-$4-$5}');
	field.val(value);
}


function sanitizeWindowsFilename(filename) {
	return filename.replace(/[\\\|\/:\*\?<>"]/g, '');
}


function addToOptGroup(optgroup, option) {
	var added = false;
	for (var i = 0; i < optgroup.children().size(); i++) {
		curOpt = optgroup.children().eq(i);
		if (option.text().toUpperCase() < curOpt.text().toUpperCase()) {
			option.insertBefore(curOpt);
			added = true;
			break;
		}
	}
	if (!added) {
		optgroup.append(option);
	}
}


// used to keep DSCtop cookie settings localized to a single instance
function getWebAppPath() {
	var regex = /^(http:\/\/)?.+\/([^\/]+)\/.*$/;
	return window.location.href.replace(regex, '$2');
}


function setCookie(name, value, expireDays) {
	if (expireDays) {
		var date = new Date();
		date.setTime(date.getTime() + (expireDays * 24 * 60 * 60 * 1000));
		var expires = "; expires=" + date.toGMTString();
	} else {
		var expires = "";
	}
	document.cookie = name + "=" + value + expires + "; path=/" + getWebAppPath();
}


function getCookie(name) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for (var i = 0;i < ca.length; i++) {
		var c = ca[i];
		while (c.charAt(0) == ' ') {
			c = c.substring(1, c.length);
		}
		if (c.indexOf(nameEQ) == 0) {
			return c.substring(nameEQ.length, c.length);
		}
	}
	return null;
}


function deleteCookie(name) {
	setCookie(name, "", -1);
}


function capitalize(str) {
	return str.charAt(0).toUpperCase() + str.slice(1);
}


function isValidNPI(npi) {
	var tmp, sum, i, j;
	/* the NPI is a 10 digit number, but it could be
	 * preceded by the ISO prefix for the USA (80840)
	 * when stored as part of an ID card.  The prefix
	 * must be accounted for, so the NPI check-digit
	 * will be the same with or without prefix.
	 * The magic constant for 80840 is 24.
	 */
	i = npi.length;
	if ((i == 15) && (npi.substring(0,5) == "80840")) {
		sum = 0;
	} else if (i == 10) {
		sum = 24;	/* to compensate for the prefix */
	}
	else {
		return false;	/* length must be 10 or 15 bytes */
	}
	
	/* the algorithm calls for calculating the check-digit
	 * from right to left
	 */
	/* first, intialize the odd/even counter, taking into account
	 * that the rightmost digit is presumed to be the check-sum
	 * so in this case the rightmost digit is even instead of
	 * being odd
	 */
	j = 0;
	/* now scan the NPI from right to left */
	while (i--) {
		/* only digits are valid for the NPI */
		if (isNaN(npi.charAt(i))) {
			return false;
		}
		/* this conversion works for ASCII and EBCDIC */
		tmp = npi.charAt(i) - '0';
		/* the odd positions are multiplied by 2 */
		if (j++ & 1) {
			if ((tmp <<= 1) > 9) {
				tmp -= 10;	/* 'tmp mod 10' */
				tmp++;		/* left digit is '1' */
			}
		}
		sum += tmp;
	}

	/* If the checksum mod 10 is zero then the NPI is valid */
	return sum % 10 === 0;
}


function searchNPIRegistry(ajaxURL, criteria, recordType, dialog) {
	var inUse = $('#npi_reg_check').data('in_use');
	if (inUse != null) {
		// prevent search if a current search is taking place
		return;
	}
	
	var showSpinner = false;
	
	// validate input before checking registry
	if (trim(criteria.npi).length > 0) {
		// validate NPI
		criteria.npi = trim(criteria.npi);
		if (!criteria.npi.match(/^\d{10,}$/)) {
			jQueryAlert('Invalid NPI: value must be 10 digits in length.');
			return;
		}
		if (!isValidNPI(criteria.npi)) {
			jQueryAlert('Invalid NPI: value fails checksum (is there a typo?)');
			return;
		}
	} else {
		// make sure sufficient criteria is specified to warrant a search
		var someCriteria = false;
		for (field in criteria) {
			if (field == 'npi') {
				continue;
			}
			if (trim(criteria[field]).length > 0) {
				someCriteria = true;
				break;
			}
		}
		if (!someCriteria) {
			jQueryAlert('NPI registry search requires some criteria to be specified.');
			return;
		}
		showSpinner = true;
	}
	
	if (showSpinner) {
		$('#npi_spinner').css('visibility', 'visible');
	}
	$('#npi_reg_check').data('in_use', 'true');
	$.ajax({
		url: ajaxURL,
		data: criteria,
		dataType: 'json',
		cache: false,
		success: function(json) {
			$('#npi_reg_check').removeData('in_use');
			$('#npi_spinner').css('visibility', 'hidden');
			if (dialog != undefined) {
				// don't close previous dialog until the next one is ready
				dialog.dialog('close');
			}
			switch (json.criteria.num_rows) {
			case 0:
				jQueryAlert(capitalize(recordType) + ' not found in local NPI database.');
			break;
			case 1:
				populateRegistryForm(json.matches[0], recordType);
			break;
			
			default:
				listMatches(json, ajaxURL, recordType);
			break;
			}
		}
	});
}


function populateRegistryForm(entry, recordType, criteria, ajaxURL) {
	var npi = trim(entry.NPI);
	var formalName = trim(entry.FORMAL_NAME);
	var distName = trim(entry.CLINIC_NAME);
	var firstName = trim(entry.FIRST_NAME);
	var middleName = trim(entry.MIDDLE_NAME);
	var lastName = trim(entry.LAST_NAME);
	var taxonomyCode = trim(entry.TAXONOMY_CODE);
	var license = trim(entry.LICENSE);
	var medId = trim(entry.MED_ID);
	var phone = trim(entry.PHONE);
	var phonePlain = trim(entry.PHONE_PLAIN);
	var fax = trim(entry.FAX);
	var faxPlain = trim(entry.FAX_PLAIN);
	var street1 = trim(entry.STREET1);
	var street2 = trim(entry.STREET2);
	var city = trim(entry.CITY);
	var state = trim(entry.STATE);
	var zipCode = trim(entry.ZIP_CODE);
	var zipPlain = trim(entry.ZIP_PLAIN);
	var fullAddress = '';
	if (street1.length > 0) {
		fullAddress += street1;
		if (street2.length > 0) {
			fullAddress += '\n' + street2;
		}
		fullAddress += '\n' + city + ', ' + state + ' ' + zipCode;
	}
	
	// set values in form
	$('#npireg_name').text(formalName);
	$('#npireg_npi').text(npi);
	$('#npireg_tax').text(taxonomyCode);
	$('#npireg_lic').text(license);
	$('#npireg_cb_lic').attr('checked', license.length > 0);
	$('#npireg_med').text(medId);
	$('#npireg_cb_med').attr('checked', medId.length > 0);
	$('#npireg_phone').text(phone);
	$('#npireg_cb_phone').attr('checked', phone.length > 0);
	$('#npireg_fax').text(fax);
	$('#npireg_cb_fax').attr('checked', fax.length > 0);
	$('#npireg_address').html(fullAddress.replace(/\n/g, '<br>'));
	$('#npireg_cb_address').attr('checked', fullAddress.length > 0);
	
	var npiTitle = entry.NPI + ' (' + (entry.IS_ORG_PROVIDER == '1'
		? 'Organization' : 'Individual') + ')';
	var dialog = $('#npireg_overlay').dialog({
		title: 'Match Found in Registry: ' + npiTitle,
		width: 500,
		draggable: true,
		closeOnEscape: true,
		resizable: false,
		draggable: true,
		bgiframe: true,
		autoOpen: false,
		modal: false,
		buttons: {
			'Go Back': function() {
				searchNPIRegistry(ajaxURL, criteria, recordType, dialog);
				//$(this).dialog('close');
			},
			'Use Selected Values': function() {
				if ($('#npireg_cb_npi').attr('checked')) {
					$('#npi_num').val(npi);
				}
				if ($('#npireg_cb_name').attr('checked')) {
					switch (recordType) {
					case 'provider':
						$('#frm_changed_flag').val('1');
						$('#middle_name').val(middleName);
					case 'physician':
						$('#first_name').val(firstName);
						$('#last_name').val(lastName);
					break;
					case 'district':
						$('#dist_name').val(distName);
					break;
					}
				}
				if (recordType == 'provider' || recordType == 'district') {
					if ($('#npireg_cb_tax').attr('checked')) {
						$('#taxonomy_code').val(taxonomyCode);
						$('#frm_changed_flag').val('1');
					}
				}
				if (recordType == 'provider') {
					if ($('#npireg_cb_lic').attr('checked')) {
						$('#license_number').val(license);
						$('#frm_changed_flag').val('1');
					}
				}
				if ($('#npireg_cb_med').attr('checked')) {
					switch (recordType) {
					case 'provider':
						$('#frm_changed_flag').val('1');
					case 'physician':
						$('#med_id').val(medId);
					break;
					case 'district':
						$('#tax_id').val(medId);
					break;
					}
				}
				if ($('#npireg_cb_address').attr('checked')) {
					switch (recordType) {
					case 'provider':
						var description = trim($('#description').val());
						description += '\n\n' + formalName + '\n';
						description += fullAddress.replace(/<br>/g, '\n');
						$('#description').val(trim(description));
						$('#frm_changed_flag').val('1');
					break;
					case 'physician':
						$('#street').val(trim(street1 + ' ' + street2));
						$('#city').val(city);
						$('#state').val(state);
						$('#zipcode').val(zipCode);
					break;
					case 'district':
						$('#address').val(street1);
						$('#mailbox').val(street2);
						$('#city').val(city);
						$('#state').val(state);
						$('#zipcode').val(zipPlain);
					break;
					}
				}
				if ($('#npireg_cb_phone').attr('checked')) {
					switch (recordType) {
					case 'provider':
						var description = trim($('#description').val());
						description += '\n\nPhone #: ' + phone;
						$('#description').val(trim(description));
						$('#frm_changed_flag').val('1');
					break;
					case 'physician':
						$('#phone_num').val(phone);
					break;
					case 'district':
						$('#phone').val(phonePlain);
					break;
					}
				}
				if ($('#npireg_cb_fax').attr('checked')) {
					switch (recordType) {
					case 'provider':
						var description = trim($('#description').val());
						description += '\n\nFax #: ' + fax;
						$('#description').val(trim(description));
						$('#frm_changed_flag').val('1');
					break;
					case 'district':
						$('#fax').val(faxPlain);
					break;
					}
				}
				$(this).dialog('close');
			},
			'Cancel': function() {
				$(this).dialog('close');
			}
		},
		open: function(ev, ui) {
			// set initial focus on Cancel button
			$(this).parents('.ui-dialog-buttonpane button:last').focus();
		},
		close: function(ev, ui) {}
	});
	
	if (criteria != undefined) {
		// move button to left side
		$('button', dialog.parent()).eq(0).css({
			position: 'absolute',
			left: '20px'
		});
	} else {
		// hide "back" button if not applicable
		$('button', dialog.parent()).eq(0).hide();
	}
	
	dialog.dialog('open');
}


function listMatches(json, ajaxURL, recordType) {
	var criteria = json.criteria;
	var matches = json.matches;
	var offset = criteria.paging * criteria.rows_per_set;
	var range = (offset + 1) + ' to ' + (offset + matches.length);
	
	var html = '<div class="npi_matches"><div class="arrow_bar">'
		+ '<button class="prev_rows" '
		+ (criteria.paging == 0 ? 'disabled' : '') + '>'
		+ '<div class="tangoImage16 arrow_left"></div>'
		+ 'Prev</button>'
		+ '<button class="next_rows" '
		+ (criteria.num_rows <= (offset + matches.length) ? 'disabled' : '') + '>'
		+ '<div class="tangoImage16 arrow_right"></div>'
		+ 'Next</button>'
		+ '</div><table class="resultTb matches" style="width: 100%;">'
		+ '<tr class="formHdTr">'
		+ '<th class="formHdTd">NPI</th>'
		+ '<th class="formHdTd">Name</th>'
		+ '<th class="formHdTd">Taxonomy</th>'
		+ '<th class="formHdTd">Location</th></tr>';
	for (var i = 0; i < matches.length; i++) {
		with (matches[i]) {
			html += '<tr class="formTr">'
				+ '<td class="formTd paddedVal clickable npi" '
				+ ' row_index="' + i + '">' + NPI + '</td>'
				+ '<td class="formTd paddedVal">' + FORMAL_NAME + '</td>'
				+ '<td class="formTd paddedVal">' + TAXONOMY_CODE + '</td>'
				+ '<td class="formTd paddedVal">' + STREET1 + '<br>'
				+ (STREET2.length > 0 ? STREET2 + '<br>' : '')
				+ CITY + ', ' + STATE + ' ' + ZIP_CODE + '</td></tr>';
		}
	}
	html += '</table></div>';
	
	var dialog = $(html).dialog({
		title: 'Showing ' + range + ' of ' + criteria.num_rows + ' matches',
		width: 700,
		height: 300,
		draggable: true,
		closeOnEscape: true,
		resizable: false,
		draggable: true,
		bgiframe: true,
		autoOpen: true,
		modal: false,
		buttons: {},
		open: function(ev, ui) {
			// load the dialog for displaying a single match from NPI registry
			$('td.npi').live('click.npisearch', function() {
				var idx = parseInt($(this).attr('row_index'), 10);
				if (matches[idx] != undefined) {
					populateRegistryForm(matches[idx], recordType, criteria, ajaxURL);
					dialog.dialog('close');
				}
			});
			// handler for fetching previous set
			if (criteria.paging > 0) {
				var prevCriteria = {};
				$.extend(prevCriteria, criteria);
				prevCriteria.paging = prevCriteria.paging - 1;
				$('button.prev_rows').live('click.npisearch', function() {
					searchNPIRegistry(ajaxURL, prevCriteria, recordType, dialog);
					unbindNPIRegistryHandlers();
				});
			}
			// handler for fetching next set
			if (criteria.num_rows > (offset + matches.length)) {
				var nextCriteria = {};
				$.extend(nextCriteria, criteria);
				nextCriteria.paging = nextCriteria.paging + 1;
				$('button.next_rows').live('click.npisearch', function() {
					searchNPIRegistry(ajaxURL, nextCriteria, recordType, dialog);
					unbindNPIRegistryHandlers();
				});
			}
		},
		close: function(ev, ui) {
			unbindNPIRegistryHandlers();
		}
	});
}


function unbindNPIRegistryHandlers() {
	// clean up handlers after use
	$('td.npi').die('click.npisearch');
	$('button.prev_rows').die('click.npisearch');
	$('button.next_rows').die('click.npisearch');
}
