<?php
/**
 * Core Functions that can be used by any page in PGV
 *
 * The functions in this file are common to all PGV pages and include date conversion 
 * routines and sorting functions.
 *
 * phpGedView: Genealogy Viewer
 * Copyright (C) 2002 to 2003  John Finlay and Others
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * @package PhpGedView
 * @version $Id: functions.php,v 1.577 2004/12/08 21:04:38 eikland Exp $
 */

/**
 * security check to prevent hackers from directly accessing this file
 */
if (strstr($_SERVER["PHP_SELF"],"functions.php")) {
	print "Why do you want to do that?";
	exit;
}

/**
 * do not include print functions when using the gdbi protocol
 */
if (strstr($PHP_SELF, "gdbi.php")===false) require($PGV_BASE_DIRECTORY."functions_print.php");

/**
 * initialize and check the database
 *
 * this function will create a database connection and return false if any errors occurred
 * @return boolean true if database successully connected, false if there was an error
 */
function check_db() {
	global $PGV_DATABASE, $DBTYPE, $DBHOST, $DBUSER, $DBPASS, $DBNAME, $DBCONN, $TOTAL_QUERIES, $PHP_SELF;
	global $GEDCOM, $GEDCOMS, $INDEX_DIRECTORY, $BUILDING_INDEX, $indilist, $famlist, $sourcelist, $otherlist;

	if ($PGV_DATABASE=="db") {
		if ((is_object($DBCONN)) && (!DB::isError($DBCONN))) return true;
		//-- initialize query counter
		$TOTAL_QUERIES = 0;
		
		$dsn = array(
			'phptype'  => $DBTYPE,
			'username' => $DBUSER,
			'password' => $DBPASS,
			'hostspec' => $DBHOST,
			'database' => $DBNAME,
		);
		
		$options = array(
			'debug' 	  => 2,
			'portability' => DB_PORTABILITY_ALL,
		);
		
		$DBCONN = DB::connect($dsn, $options);
		if (DB::isError($DBCONN)) {
			//die($DBCONN->getMessage());
			return false;
		}
	
		//-- protect the username and password on other pages
		if (preg_match("/editconfig\.php/", $PHP_SELF)==0) {
			$DBUSER = "";
			$DBPASS = "";
		}
		return true;
	}

	//-- index mode check
	if ($PGV_DATABASE=="index") {
		$indexfile = $INDEX_DIRECTORY.$GEDCOM."_index.php";
	
		//-- check for index files and update them if necessary
		if (!isset($BUILDING_INDEX)) {
			$updateindex=false;
			if ((file_exists($indexfile))&&(file_exists($GEDCOMS[$GEDCOM]["path"]))) {
				$indextime = filemtime($indexfile);
				$gedtime = filemtime($GEDCOMS[$GEDCOM]["path"]);
				if ($indextime < $gedtime) $updateindex=true;
			}
			else {
				$updateindex=true;
			}
	
			if (file_exists($indexfile)) {
				$fp = fopen($indexfile, "r");
				$fcontents = fread($fp, filesize($indexfile));
				fclose($fp);
				$lists = unserialize($fcontents);
				unset($fcontents);
				$indilist = $lists["indilist"];
				$famlist = $lists["famlist"];
				$sourcelist = $lists["sourcelist"];
				$otherlist = $lists["otherlist"];
			}
		}
		return true;
	}
	
	return false;
}

/**
 * get gedcom configuration file
 *
 * this function returns the path to the currently active GEDCOM configuration file
 * @author John Finlay (yalnifj)
 * @return string path to gedcom.ged_conf.php configuration file
 */
function get_config_file() {
	global $GEDCOMS, $GEDCOM, $PGV_BASE_DIRECTORY;

	if (count($GEDCOMS)==0) {
		return $PGV_BASE_DIRECTORY."config_gedcom.php";
	}
	if ((!empty($GEDCOM))&&(isset($GEDCOMS[$GEDCOM]))) return $GEDCOMS[$GEDCOM]["config"];
	foreach($GEDCOMS as $GEDCOM=>$gedarray) {
		$_SESSION["GEDCOM"] = $GEDCOM;
		return $PGV_BASE_DIRECTORY.$gedarray["config"];
	}
}

/**
 * Get the version of the privacy file
 *
 * This function opens the given privacy file and returns the privacy version from the file
 * @author John Finlay (yalnifj)
 * @param string $privfile the path to the privacy file
 * @return string the privacy file version number
 */
function get_privacy_file_version($privfile) {
	$privversion = "0";
	
	//-- check to make sure that the privacy file is the current version
	if (file_exists($privfile)) {
		$privcontents = implode("", file($privfile));
		$ct = preg_match("/PRIVACY_VERSION.*=.*\"(.+)\"/", $privcontents, $match);
		if ($ct>0) {
			$privversion = trim($match[1]);
		}
	}
	
	return $privversion;
}

/**
 * Get the path to the privacy file
 *
 * Get the path to the privacy file for the currently active GEDCOM
 * @author John Finlay (yalnifj)
 * @return string path to the privacy file
 */
function get_privacy_file() {
	global $GEDCOMS, $GEDCOM, $PGV_BASE_DIRECTORY, $REQUIRED_PRIVACY_VERSION;

	$privfile = "privacy.php";
	if (count($GEDCOMS)==0) {
		$privfile = $PGV_BASE_DIRECTORY."privacy.php";
	}
	if ((!empty($GEDCOM))&&(isset($GEDCOMS[$GEDCOM]))) {
		if ((isset($GEDCOMS[$GEDCOM]["privacy"]))&&(file_exists($GEDCOMS[$GEDCOM]["privacy"]))) $privfile = $GEDCOMS[$GEDCOM]["privacy"];
		else $privfile = $PGV_BASE_DIRECTORY."privacy.php";
	}
	else {
		foreach($GEDCOMS as $GEDCOM=>$gedarray) {
			$_SESSION["GEDCOM"] = $GEDCOM;
			if ((isset($gedarray["privacy"]))&&(file_exists($gedarray["privacy"]))) $privfile = $PGV_BASE_DIRECTORY.$gedarray["privacy"];
			else $privfile = $PGV_BASE_DIRECTORY."privacy.php";
		}
	}
	$privversion = get_privacy_file_version($privfile);
	if ($privversion<$REQUIRED_PRIVACY_VERSION) $privfile = $PGV_BASE_DIRECTORY."privacy.php";
	
	return $privfile;
}

/**
 * Get the current time in micro seconds
 *
 * returns a timestamp for the current time in micro seconds
 * obtained from online documentation for the microtime() function
 * on php.net
 * @return float time in micro seconds
 */
function getmicrotime(){
	list($usec, $sec) = explode(" ",microtime());
	return ((float)$usec + (float)$sec);
}

/**
 * Store GEDCOMS array
 *
 * this function will store the <var>$GEDCOMS</var> array in the <var>$INDEX_DIRECTORY</var>/gedcoms.php
 * file.  The gedcoms.php file is included in session.php to create the <var>$GEDCOMS</var>
 * array with every page request.
 * @see session.php
 */
function store_gedcoms() {
	global $GEDCOMS, $pgv_lang, $INDEX_DIRECTORY, $DEFAULT_GEDCOM, $COMMON_NAMES_THRESHOLD, $GEDCOM;

	uasort($GEDCOMS, "gedcomsort");
	$gedcomtext = "<?php\n//--START GEDCOM CONFIGURATIONS\n";
	$gedcomtext .= "\$GEDCOMS = array();\n";
	foreach($GEDCOMS as $GED) {
		$GED["config"] = str_replace($INDEX_DIRECTORY, "\${INDEX_DIRECTORY}", $GED["config"]);
		if (isset($GED["privacy"])) $GED["privacy"] = str_replace($INDEX_DIRECTORY, "\${INDEX_DIRECTORY}", $GED["privacy"]);
		else $GED["privacy"] = "privacy.php";
		$GED["path"] = str_replace($INDEX_DIRECTORY, "\${INDEX_DIRECTORY}", $GED["path"]);
		$GED["title"] = stripslashes($GED["title"]);
		$GED["title"] = preg_replace("/\"/", "\\\"", $GED["title"]);
		$gedcomtext .= "\$gedarray = array();\n";
		$gedcomtext .= "\$gedarray[\"gedcom\"] = \"".$GED["gedcom"]."\";\n";
		$gedcomtext .= "\$gedarray[\"config\"] = \"".$GED["config"]."\";\n";
		$gedcomtext .= "\$gedarray[\"privacy\"] = \"".$GED["privacy"]."\";\n";
		$gedcomtext .= "\$gedarray[\"title\"] = \"".$GED["title"]."\";\n";
		$gedcomtext .= "\$gedarray[\"path\"] = \"".$GED["path"]."\";\n";
		if (empty($GED["commonsurnames"])) {
			if ($GED["gedcom"]==$GEDCOM) {
				$surnames = get_common_surnames($COMMON_NAMES_THRESHOLD);
				$GED["commonsurnames"] = "";
				foreach($surnames as $surname) {
					$GED["commonsurnames"] .= $surname["name"].", ";
				}
			}
			else $GED["commonsurnames"]="";
		}
		$GEDCOMS[$GED["gedcom"]]["commonsurnames"] = $GED["commonsurnames"];
		$gedcomtext .= "\$gedarray[\"commonsurnames\"] = \"".addslashes($GED["commonsurnames"])."\";\n";
		$gedcomtext .= "\$GEDCOMS[\"".$GED["gedcom"]."\"] = \$gedarray;\n";
	}
	$gedcomtext .= "\n\$DEFAULT_GEDCOM = \"$DEFAULT_GEDCOM\";\n";
	$gedcomtext .= "\n?>";

	$fp = fopen($INDEX_DIRECTORY."gedcoms.php", "wb");
	if (!$fp) {
		print "<span class=\"error\">".$pgv_lang["gedcom_config_write_error"]."<br /></span>\n";
	}
	else {
		fwrite($fp, $gedcomtext);
		fclose($fp);
	}
}

//-- check if a person is dead using their ID
function is_dead_id($pid) {
	global $indilist, $BUILDING_INDEX, $GEDCOM, $PGV_DATABASE;

	if (empty($pid)) return true;
	
	//-- if using indexes then first check the indi_isdead array
	if ((!$BUILDING_INDEX)&&(isset($indilist))) {
		//-- check if the person is already in the $indilist cache
		if ((!isset($indilist[$pid]["isdead"]))||($indilist[$pid]["file"]!=$GEDCOM)) {
			//-- load the individual into the cache by calling the find_person_record function
			$gedrec = find_person_record($pid);
			if (empty($gedrec)) return true;
		}
		if ((isset($indilist[$pid]["isdead"]))&&($indilist[$pid]["file"]==$GEDCOM)) {
			if (($PGV_DATABASE!='index')&&($indilist[$pid]["isdead"]==-1)) {
				$indilist[$pid]["isdead"] = update_isdead($pid, $indilist[$pid]);
			}
			return $indilist[$pid]["isdead"];
		}
	}
	return is_dead(find_person_record($pid));
}

/**
 * Get array of common surnames from index
 *
 * This function returns a simple array of the most common surnames
 * found in the individuals list.
 * @author John Finlay
 * @param int $min the number of times a surname must occur before it is added to the array
 */
function get_common_surnames_index($ged) {
	global $GEDCOMS;

	if (empty($GEDCOMS[$ged]["commonsurnames"])) store_gedcoms();
	$surnames = array();
	if (empty($GEDCOMS[$ged]["commonsurnames"])) return $surnames;
	$names = preg_split("/[,;] /", $GEDCOMS[$ged]["commonsurnames"]);
	foreach($names as $name) {
		$name = trim($name);
		if (!empty($name)) $surnames[$name]["name"] = stripslashes($name);
	}
	return $surnames;
}

/**
 * Get array of common surnames
 *
 * This function returns a simple array of the most common surnames
 * found in the individuals list.
 * @author John Finlay
 * @param int $min the number of times a surname must occur before it is added to the array
 */
function get_common_surnames($min) {
	global $TBLPREFIX, $GEDCOM, $indilist, $CONFIGURED, $GEDCOMS, $COMMON_NAMES_ADD, $COMMON_NAMES_REMOVE, $pgv_lang, $HNN;
	
	$surnames = array();
	if (!$CONFIGURED || !adminUserExists() || (count($GEDCOMS)==0) || (!check_for_import($GEDCOM))) return $surnames;
	//-- this line causes a bug where the common surnames list is not properly updated
	// if ((!isset($indilist))||(!is_array($indilist))) return $surnames;
	$ignorenames = preg_split("/[,;] ?/", $COMMON_NAMES_REMOVE);
	$tindilist = get_indi_list();
	foreach($tindilist as $gid=>$indi) {
		$name = check_NN(trim($indi["name"]));
		if (stristr($name, $pgv_lang["NN"].",")===false && stristr($name, $HNN.",")===false) {
			$name = preg_replace(array("/ [jJsS][rR]\.?,/", "/ I+,/"), array(",",","), $name);
			$names = preg_split("/,/", $name);
			$surname = trim($names[0]);
			$surname = str2upper($surname);
			if (!isset($surnames[$surname])) {
				 $surnames[$surname] = array();
				 $surnames[$surname]["match"] = 1;
				 $surnames[$surname]["name"] = $names[0];
			}
			else {
				 $surnames[$surname]["match"]++;
			}
		}
	}

	arsort($surnames);
	$topsurns = array();
	$i=0;
	foreach($surnames as $surname=>$count) {
		if ($count["match"]>=$min) $topsurns[$surname] = $count;
		$i++;
	}
	$addnames = preg_split("/[,;] /", $COMMON_NAMES_ADD);
	if ((count($addnames)==0) && (!empty($COMMON_NAMES_ADD))) $addnames[] = $COMMON_NAMES_ADD;
	foreach($addnames as $name) {
		if (!empty($name)) {
			$topsurns[$name]["name"] = $name;
			$topsurns[$name]["match"] = $min;
		}
	}
	$delnames = preg_split("/[,;] /", $COMMON_NAMES_REMOVE);
	if ((count($delnames)==0) && (!empty($COMMON_NAMES_REMOVE))) $delnames[] = $COMMON_NAMES_REMOVE;
	foreach($delnames as $name) {
		if (!empty($name)) {
			unset($topsurns[$name]);
		}
	}

	uasort($topsurns, "itemsort");
	return $topsurns;
}

/**
 * Get the name from the raw gedcom record
 *
 * @param string $indirec the raw gedcom record to get the name from
 */
function get_name_in_record($indirec) {
	$surname="";
	$name="";
	
	$nt = preg_match("/1 NAME (.*)/", $indirec, $ntmatch);
	if ($nt>0) {
		$name = trim($ntmatch[1]);
		//-- remove any unwanted characters from the name
		if (preg_match("/^\.(\.*)$|^\?(\?*)$|^_(_*)$|^,(,*)$/", $name)) $name = preg_replace(array("/,/","/\./","/_/","/\?/"), array("","","",""), $name);
		$ct = preg_match("~(.*)/(.*)/(.*)~", $name, $match);
		if ($ct>0) {
			$surname = trim($match[2]);
			if (empty($surname)) $surname = "@N.N.";
			$givenname = trim($match[1]);
			$othername = trim($match[3]);
			if (empty($givenname)&&!empty($othername)) {
				$givenname = $othername;
				$othername = "";
			}
			if (empty($givenname)) $givenname = "@P.N.";
			$name = $surname;
			if (!empty($othername)) $name .= " ".$othername;
			$name .= ", ".$givenname;
		}
		
		/*-- commented out for better support of alternate name styles
		//-- split the name at the /
		$names = preg_split("/\//", $ntmatch[1]);
		$name = "";
		
		//-- remove any unwanted characters from the name
		for ($i=0; $i < (count($names)-1); $i++) {
			if (preg_match("/^\.(\.*)$|^\?(\?*)$|^_(_*)$|^,(,*)$/", trim($names[$i]))) $names[$i] = preg_replace(array("/,/","/\./","/_/","/\?/"), array("","","",""), $names[$i]);
		}
		//-- if a surname was found the count will be > 1
		if (count($names)>1) {
			// Check for the firstname
			if (strlen(trim($names[0])) == 0) $names[0] = "@P.N.";
			
			// Check for the lastname
			if (strlen(trim($names[1])) == 0) $name = "@N.N.";
			else $name = $names[1];

			//-- add any surname suffixes such as jr or sr
			for($i=2; $i<count($names); $i++) {
				$names[$i] = trim($names[$i]);
				if (!empty($names[$i]))	$name .= " ".trim($names[$i]);
			}
			//-- add a , and the given names
			$name .= ", ".$names[0];
		}
		else $name = $names[0].", @N.N.";
		*/
	}
	else {
		/*-- this is all extraneous to the 1 NAME tag and according to the gedcom spec
		-- the 1 NAME tag should take preference
		*/
		$ct = preg_match("/2 SURN (.*)/", $indirec, $match);
		if ($ct>0) {
			$gt = preg_match("/2 GIVN (.*)/", $indirec, $gmatch);
			$name = rtrim($match[1]);
			$surname = trim($name);
			if (empty($name)) $name = "@N.N.";
			if ($gt>0) $name.=", ".rtrim($gmatch[1]);
			else $name .= ", @P.N.";
		}
		if (empty($name)) $name = "@N.N.";
		$st = preg_match("/2 NSFX (.*)/", $indirec, $smatch);
		if ($st>0) $name.=" ".rtrim($smatch[1]);
		$pt = preg_match("/2 SPFX (.*)/", $indirec, $pmatch);
//		if ($pt>0) $name =str2lower(trim($pmatch[1]))." ".$name;
		if ($pt>0) $name =strtolower(trim($pmatch[1]))." ".$name;
		if ($nt>0) {
			//-- setup the language alphabet string
			$alphabet = getAlphabet();
			$name .= ", ";
			if (preg_match("/[$alphabet]/i", $names[0])) {
				$name .= trim($names[0]);
			}
			else if (isset($names[2])&&(preg_match("/[$alphabet]/i", $names[2]))) {
				$name .= trim($names[2]);
			}
			else {
				$name .= "@P.N.";
			}
		}
		else $name .= ", @P.N.";
	}
	$name = preg_replace("'\/'", "", $name);
	return $name;
}

/**
 * get the person's name as surname, given names
 *
 * This function will return the given person's name is a format that is good for sorting
 * Surname, given names
 * @param string $pid the gedcom xref id for the person
 * @return string the sortable name
 */
function get_sortable_name($pid) {
	global $TBLPREFIX, $SHOW_LIVING_NAMES, $PRIV_PUBLIC;
	global $GEDCOM, $indilist, $pgv_lang;

//	if (empty($pid)) return false;
	if (empty($pid)) return $name = "@N.N., @P.N.";
	//-- commenting out here, the privacy should be checked before the name is printed, not as it is retrieved
	//if ((!displayDetailsByID($pid))&&(!showLivingNameByID($pid))) return $pgv_lang["private"];

	//-- first check if the person is in the cache
	if ((isset($indilist[$pid]["name"]))&&($indilist[$pid]["file"]==$GEDCOM)) {
		$name = $indilist[$pid]["name"];
	}
	else {
		//-- cache missed, so load the person into the cache with the find_person_record function
		//-- and get the name from the cache again
		$gedrec = find_person_record($pid);
		if (!empty($gedrec)) {
			$name = $indilist[$pid]["name"];
		}
	}
	if (!empty($name)) return $name;
	else return false;
}

/**
 * get the name for a person
 *
 * returns the name in the form Given Name Surname
 * If the <var>$NAME_FROM_GEDCOM</var> variable is true then the name is retrieved from the 
 * gedcom record not from the database index.
 * @param string $pid the xref gedcom id of the person
 * @return string the person's name (Given Name Surname)
 */
function get_person_name($pid) {
	global $NAME_REVERSE;
	global $NAME_FROM_GEDCOM;

	//-- get the name from the gedcom record
	if ($NAME_FROM_GEDCOM) {
		$indirec = find_person_record($pid);
		if (!$indirec) $indirec = find_record_in_file($pid);
		$ct = preg_match("/1 NAME (.*)/", $indirec, $match);
		if ($ct>0) {
			$name = trim($match[1]);
			$name = preg_replace("~/~", "", $name);
			$name = check_NN($name);
			return $name;
		}
		else $fullname = get_name_in_record($indirec);
	}
	else {
		//-- get the name from the indexes
		$fullname = get_sortable_name($pid);
		if ($fullname===false) {
			$NAME_FROM_GEDCOM = true;
			$name = get_person_name($pid);
			$NAME_FROM_GEDCOM = false;
		}
	}
	$names = preg_split("/,/", $fullname);
	if (count($names) > 1) {
		$name = preg_split("/,/", check_NN($names));
		if (!$NAME_REVERSE) {
			if (trim($names[1]) == "@P.N.") $name = trim($names[1])." ".trim($names[0]);
			else $name = trim($names[1])." ".trim($names[0]);
		}
		else {
			if (trim($names[1]) == "@P.N.") $name = trim($names[0])." ".trim($names[1]);
			else $name = trim($names[0])." ".trim($names[1]);
		}
	}
	else $name = $names[0];

	$name = check_NN($name);
	return $name;
}

/**
 * get the descriptive title of the source
 *
 * @param string $sid the gedcom xref id for the source to find
 * @return string the title of the source
 */
function get_source_descriptor($sid) {
	global $TBLPREFIX, $WORD_WRAPPED_NOTES;
	global $GEDCOM, $sourcelist;
	$title = "";
	if ($sid=="") return false;
	
	if (isset($sourcelist[$sid]["name"])) {
		return $sourcelist[$sid]["name"];
	}
	else {
		$gedrec = find_source_record($sid);
		if (!empty($gedrec)) return $sourcelist[$sid]["name"];
	}
	return false;
}

//==== MA
/**
 * get the additional descriptive title of the source
 *
 * @param string $sid the gedcom xref id for the source to find
 * @return string the additional title of the source
 */
function get_add_source_descriptor($sid) {
	global $TBLPREFIX, $WORD_WRAPPED_NOTES;
	global $GEDCOM, $sourcelist;
	$title = "";
	if ($sid=="") return false;
	
	$gedrec = find_source_record($sid);
	if (!empty($gedrec)) {
		$ct = preg_match("/\d ROMN (.*)/", $gedrec, $match);
 		if ($ct>0) return($match[1]);	
		$ct = preg_match("/\d _HEB (.*)/", $gedrec, $match);
 		if ($ct>0) return($match[1]);
 	}
	return false;
}

function get_family_descriptor($fid) {
	global $pgv_lang;
	$parents = find_parents($fid);
	if ($parents["HUSB"]) $hname = get_sortable_name($parents["HUSB"]);
	else $hname = "@N.N., @P.N.";
	if ($parents["WIFE"]) $wname = get_sortable_name($parents["WIFE"]);
	else $wname = "@N.N., @P.N.";
	if (!empty($hname) && !empty($wname)) return check_NN($hname)." + ".check_NN($wname);
	else if (!empty($hname) && empty($wname)) return check_NN($hname);
	else if (empty($hname) && !empty($wname)) return check_NN($wname);
}

function get_family_add_descriptor($fid) {
	global $pgv_lang;
	$parents = find_parents($fid);
	if ($parents["HUSB"]) $hname = get_sortable_add_name($parents["HUSB"]);
	else $hname = "@N.N., @P.N.";
	if ($parents["WIFE"]) $wname = get_sortable_add_name($parents["WIFE"]);
	else $wname = "@N.N., @P.N.";
	if (!empty($hname) && !empty($wname)) return check_NN($hname)." + ".check_NN($wname);
	else if (!empty($hname) && empty($wname)) return check_NN($hname);
	else if (empty($hname) && !empty($wname)) return check_NN($wname);
}

// -- find and return a given individual's second name in format: firstname lastname
function get_add_person_name($pid) {
	global $NAME_REVERSE;
	global $NAME_FROM_GEDCOM;

	//-- get the name from the indexes
	$record = find_person_record($pid);
	$name_record = get_sub_record(1, "1 NAME", $record);
	
	// Check for ROMN name
	$romn = preg_match("/(2 ROMN (.*)|2 _HEB (.*))/", $name_record, $romn_match);
	if ($romn > 0){
		$names = preg_split("/\//", $romn_match[count($romn_match)-1]);
		if (count($names)>1) {
			$fullname = trim($names[1]).",";
			$fullname .= " ".trim($names[0]);
			if (count($names)>2) $fullname .= " ".trim($names[2]);
		}
		else $fullname=$names;   
//-- MA $name = preg_split("/,/", check_NN($fullname));
		if (count($names)>1) {
		if ($NAME_REVERSE) {
//-- MA		if (trim($names[1]) == "@P.N.") $name = trim($name[1])." ".trim($name[0]);
//-- MA		else $name = trim($name[1])." ".trim($name[0]);
			$name = trim($names[1])." ".trim($names[0]);
		}
		else {
//-- MA		if (trim($names[1]) == "@P.N.") $name = trim($name[0])." ".trim($name[1]);
//-- MA		else $name = trim($name[0])." ".trim($name[1]);
			$name = trim($names[0])." ".trim($names[1]);
		}
	}
    else $name = trim($names[0]); 	
//-- MA else $name = trim($name[0]); 			
	}
	else $name = "";
	return $name;
}

// -- find and return a given individual's second name in sort format: familyname, firstname
function get_sortable_add_name($pid) {
	global $NAME_REVERSE;
	global $NAME_FROM_GEDCOM;

	//-- get the name from the indexes
	$record = find_person_record($pid);
	$name_record = get_sub_record(1, "1 NAME", $record);
	
	// Check for ROMN name
	$romn = preg_match("/(2 ROMN (.*)|2 _HEB (.*))/", $name_record, $romn_match);
	if ($romn > 0){
    	$names = preg_split("/\//", $romn_match[count($romn_match)-1]);	
		if ($names[0] == "") $names[0] = "@P.N.";	//-- MA 
		if (empty($names[1])) $names[1] = "@N.N.";	//-- MA	 	
		if (count($names)>1) {
			$fullname = trim($names[1]).",";
			$fullname .= ",# ".trim($names[0]);
			if (count($names)>2) $fullname .= ",% ".trim($names[2]);
		}
		else $fullname=$romn_match[1];
//-- MA	else $fullname=$match[1];
//-- MA		$name = preg_split("/,/", check_NN($fullname));

		if (!$NAME_REVERSE) {
//-- MA			if (trim($names[1]) == "@P.N.") $name = trim($names[1]).", ".trim($name[0]);
//-- MA			else $name = trim($name[1]).", ".trim($name[0]);
			$name = trim($names[1]).", ".trim($names[0]);
		}
		else {
//-- MA			if (trim($names[1]) == "@P.N.") $name = trim($name[0]).",".trim($names[1]);
//-- MA			else $name = trim($name[0]).",".trim($name[1]);
			$name = trim($names[0])." ,".trim($names[1]);
		}
	}
	else $name = get_sortable_name($pid);
	
	return $name;
}	

/**
 * get a gedcom subrecord
 *
 * searches a gedcom record and returns a subrecord of it.  A subrecord is defined starting at a
 * line with level N and all subsequent lines greater than N until the next N level is reached.
 * For example, the following is a BIRT subrecord:
 * <code>1 BIRT
 * 2 DATE 1 JAN 1900
 * 2 PLAC Phoenix, Maricopa, Arizona</code>
 * The following example is the DATE subrecord of the above BIRT subrecord:
 * <code>2 DATE 1 JAN 1900</code>
 * @author John Finlay (yalnifj)
 * @param int $level the N level of the subrecord to get
 * @param string $tag a gedcom tag or string to search for in the record (ie 1 BIRT or 2 DATE)
 * @param string $gedrec the parent gedcom record to search in
 * @param int $num this allows you to specify which matching <var>$tag</var> to get.  Oftentimes a
 * gedcom record will have more that 1 of the same type of subrecord.  An individual may have 
 * multiple events for example.  Passing $num=1 would get the first 1.  Passing $num=2 would get the
 * second one, etc.
 * @return string the subrecord that was found or an empty string "" if not found.
 */
function get_sub_record($level, $tag, $gedrec, $num=1) {
	$pos1=0;
	$subrec = "";
	if (empty($gedrec)) return "";
	while(($num>0)&&($pos1<strlen($gedrec))) {
		$pos1 = strpos($gedrec, $tag, $pos1);
		if ($pos1===false) return "";
		$pos2 = strpos($gedrec, "\n$level", $pos1+1);
		if (!$pos2) {
			if ($num==1) return substr($gedrec, $pos1);
			else return "";
		}
		if ($num==1) $subrec = substr($gedrec, $pos1, $pos2-$pos1);
		$num--;
		$pos1 = $pos2;
	}
	return $subrec;
}

/**
 * find all of the level 1 subrecords of the given record
 * @param string $gedrec the gedcom record to get the subrecords from
 * @param string $ignore a list of tags to ignore
 * @param boolean $families whether to include any records from the family
 * @param boolean $sort whether or not to sort the record by date
 * @return array an array of the raw subrecords to return
 */
function get_all_subrecords($gedrec, $ignore="", $families=true, $sort=true) {
	$repeats = array();
	
	$id = "";
	$gt = preg_match("/0 @(.+)@/", $gedrec, $gmatch);
	if ($gt > 0) {
		$id = $gmatch[1];
	}
	$prev_tags = array();
	$ct = preg_match_all("/\n1 (_?[A-Z]+)(.*)/", $gedrec, $match, PREG_SET_ORDER);
	for($i=0; $i<$ct; $i++) {
		if (strpos($ignore, $match[$i][1])===false) {
			$fact = trim($match[$i][1]);
			if (showFact($fact, $id)&&showFactDetails($fact,$id)) {
				if (isset($prev_tags[$fact])) $prev_tags[$fact]++;
				else $prev_tags[$fact] = 1;
				$subrec = get_sub_record(1, "1 $fact", $gedrec, $prev_tags[$fact]);
				if ($fact=="EVEN") {
					$tt = preg_match("/2 TYPE (.*)/", $subrec, $tmatch);
					if ($tt>0) {
						$type = trim($tmatch[1]);
						if (showFact($type, $id)&&showFactDetails($type,$id)) $repeats[] = trim($subrec)."\r\n";
					}
					else $repeats[] = trim($subrec)."\r\n";
				}
				else $repeats[] = trim($subrec)."\r\n";
			}
		}
	}
	
	//-- look for any records in FAMS records
	if ($families) {
		$ft = preg_match_all("/1 FAMS @(.+)@/", $gedrec, $fmatch, PREG_SET_ORDER);
		for($f=0; $f<$ft; $f++) {
			$famid = $fmatch[$f][1];
			$famrec = find_gedcom_record($fmatch[$f][1]);
			$parents = find_parents_in_record($famrec);
			if ($id==$parents["HUSB"]) $spid = $parents["WIFE"];
			else $spid = $parents["HUSB"];
			$prev_tags = array();
			$ct = preg_match_all("/\n1 (_?[A-Z]{3,5})(.*)/", $famrec, $match, PREG_SET_ORDER);
			for($i=0; $i<$ct; $i++) {
				if (strpos($ignore, $match[$i][1])===false) {
					$fact = trim($match[$i][1]);
					if (showFact($fact, $id)&&showFactDetails($fact,$id)) {
						if (isset($prev_tags[$fact])) $prev_tags[$fact]++;
						else $prev_tags[$fact] = 1;
						$subrec = get_sub_record(1, "1 $fact", $famrec, $prev_tags[$fact]);
						$subrec .= "\r\n1 _PGVS @$spid@\r\n";
						$subrec .= "1 _PGVFS @$famid@\r\n";
						if ($fact=="EVEN") {
							$ct = preg_match("/2 TYPE (.*)/", $subrec, $tmatch);
							if ($ct>0) {
								$type = trim($tmatch[1]);
								if (showFact($type, $id)&&showFactDetails($type,$id)) $repeats[] = trim($subrec)."\r\n";
							}
							else $repeats[] = trim($subrec)."\r\n";
						}
						else $repeats[] = trim($subrec)."\r\n";
					}
				}
			}
		}
	}
	
	if ($sort) usort($repeats, "compare_facts");
	return $repeats;
}

/**
 * find the parents in a family
 *
 * find and return a two element array containing the parents of the given family record
 * @author John Finlay (yalnifj)
 * @param string $famid the gedcom xref id for the family
 * @return array returns a two element array with indexes HUSB and WIFE for the parent ids
 */
function find_parents($famid) {
	global $pgv_lang;

	$famrec = find_family_record($famid);
	if (empty($famrec)) {
		if (userCanEdit(getUserName())) {
			$famrec = find_record_in_file($famid);
			if (empty($famrec)) return false;
		}
		else return false;
	}
	return find_parents_in_record($famrec);
}

/**
 * find the parents in a family record
 *
 * find and return a two element array containing the parents of the given family record
 * @author John Finlay (yalnifj)
 * @param string $famrec the gedcom record of the family to search in
 * @return array returns a two element array with indexes HUSB and WIFE for the parent ids
 */
function find_parents_in_record($famrec) {
	global $pgv_lang;

	if (empty($famrec)) return false;
	$parents = array();
	$ct = preg_match("/1 HUSB @(.*)@/", $famrec, $match);
	if ($ct>0) $parents["HUSB"]=$match[1];
	else $parents["HUSB"]="";
	$ct = preg_match("/1 WIFE @(.*)@/", $famrec, $match);
	if ($ct>0) $parents["WIFE"]=$match[1];
	else $parents["WIFE"]="";
	return $parents;
}

/**
 * find all child family ids
 *
 * searches an individual gedcom record and returns an array of the FAMC ids where this person is a
 * child in the family
 * @param string $pid the gedcom xref id for the person to look in
 * @return array array of family ids
 */
function find_family_ids($pid) {
	$families = array();
	if (!$pid) return $families;

	$indirec = find_person_record($pid);
	$ct = preg_match_all("/1\sFAMC\s@(.*)@/", $indirec, $match, PREG_SET_ORDER);
	for($i=0; $i<$ct; $i++) {
		$families[$i]=$match[$i][1];
	}
	return $families;
}

/**
 * find all spouse family ids
 *
 * searches an individual gedcom record and returns an array of the FAMS ids where this person is a
 * sopuse in the family
 * @param string $pid the gedcom xref id for the person to look in
 * @return array array of family ids
 */
function find_sfamily_ids($pid) {
	$families = array();
	if (empty($pid)) return $families;
	$indirec = find_person_record($pid);
	$ct = preg_match_all("/1\s*FAMS\s*@(.*)@/", $indirec, $match,PREG_SET_ORDER);

	if ($ct>0){
		for($i=0; $i<$ct; $i++) {
			$families[$i] = $match[$i][1];
		}
	}
	return $families;
}

/**
 * find the highlighted media object for a gedcom entity
 *
 * New rules for finding primary picture and using thumbnails either under
 * the thumbs directory or with OBJE's with _THUM:
 * - skip files that have _PRIM/_THUM N
 * - default to first (existing) files
 * - first _PRIM and _THUM with Y override defaults
 * @param string $pid the individual, source, or family id
 * @param string $indirec the gedcom record to look in
 * @return array an object array with indexes "thumb" and "file" for thumbnail and filename
 */
function find_highlighted_object($pid, $indirec) {
	global $MEDIA_DIRECTORY, $GEDCOM, $MEDIA_DIRECTORY_LEVELS, $PGV_IMAGE_DIR, $PGV_IMAGES, $MEDIA_EXTERNAL;
	if (!showFactDetails("OBJE", $pid)) return false;
	$objects = array();
	//-- first find find all of the objects and put them into the object array structure
	$opos1 = strpos($indirec, "1 OBJE");
	while($opos1) {
		$object = array();
		$object["file"] = "";
		$object["thumb"] = "";
		$object["PRIM"] = "";
		$object["THUM"] = "";
		$opos2 = strpos($indirec, "\n1", $opos1+1);
		if ($opos2) $objerec = substr($indirec, $opos1, $opos2-$opos1);
		else $objerec = substr($indirec, $opos1);
		$ct = preg_match("/\d _PRIM (.*)/", $objerec, $match);
		if ($ct>0) $object["PRIM"] = trim($match[1]);
		$ct = preg_match("/\d _THUM (.*)/", $objerec, $match);
		if ($ct>0) $object["THUM"] = trim($match[1]);
		$nt = preg_match("/OBJE @(.*)@/", $objerec, $nmatch);
		if ($nt>0) {
			$objerec = find_gedcom_record($nmatch[1]);
		}
		$ft = preg_match("/\d _*FILE (.*)/", $objerec, $amatch);
		if ($ft>0) {
			$fullpath = trim($amatch[1]);
			$filename = "";
			if (strstr( $fullpath, "://")) {
				$filename=$fullpath;
				$image_type = array("bmp", "gif", "jpeg", "jpg", "pcx", "png", "tiff");
				$path_end=substr($fullpath, strlen($fullpath)-5);
				$type=strtolower(substr($path_end, strpos($path_end, ".")+1));
				if ($MEDIA_EXTERNAL && in_array($type, $image_type)) $thumbnail=trim($fullpath);
				else $thumbnail=$PGV_IMAGE_DIR."/".$PGV_IMAGES["media"]["large"];
				$object["thumb"] = $thumbnail;
				$object["file"] = $filename;
			}
			else if (stristr( $fullpath, "mailto:")===false) {
				$filename = extract_filename($fullpath);
				$thumbnail = $MEDIA_DIRECTORY."thumbs/".$filename;
				$thumbnail = trim($thumbnail);
				$filename = $MEDIA_DIRECTORY.$filename;
				$filename = trim($filename);
				if (file_exists(filename_decode($filename))) {
					$object["thumb"] = $thumbnail;
					$object["file"] = $filename;
				}
			}
		}
		$objects[] = $object;
		$opos1 = strpos($indirec, "1 OBJE", $opos1+1);
	}
	//-- if we didn't find any objects return false
	if (count($objects)==0) return false;
	
	//-- now loop through the objects to find the correct highlight based on the logic defined above
	//--  the default highlighted object is the first one found
	$highlight = $objects[0];
	foreach($objects as $i=>$object) {
		if ($object["THUM"]=="Y") {
			$object["thumb"] = $object["file"];
			$highlight = $object;
		}
		else if (($object["PRIM"]=="Y")&&($highlight["PRIM"]!="Y")) $highlight = $object;
	}
	if (!empty($highlight["file"]) && ($highlight["PRIM"]!="N") && ($highlight["THUM"]!="N")) return $highlight;
	return false;
}

// -- this will define a useful array function that is not available in older versions of php
if (phpversion() < '4.1') {
	eval('function array_key_exists($key, $arr) {
		global $pgv_lang;
		if (!is_array($arr)) {
			print "<span class=\"error\">".$pgv_lang["not_an_array"]."</span>";
		   return false;
		}
		foreach (array_keys($arr) as $k) {
		   if ($k == $key) {
			  return true;
		   }
		}
		return false;
	}');
}

/**
 * compare two fact records by date
 *
 * Compare facts function is used by the usort PHP function to sort fact baseds on date
 * it parses out the year and if the year is the same, it creates a timestamp based on
 * the current year and the month and day information of the fact
 * @param mixed $a an array with the fact record at index 1 or just a string with the factrecord
 * @param mixed $b an array with the fact record at index 1 or just a string with the factrecord
 * @return int -1 if $a should be sorted first, 0 if they are the same, 1 if $b should be sorted first
 */
function compare_facts($a, $b) {
	global $factarray, $pgv_lang;
	
	$adate=0;
	$bdate=0;
	
	if (is_array($a)) $arec = $a[1];
	else $arec = $a;
	if (is_array($b)) $brec = $b[1];
	else $brec = $b;
		 
	$ft = preg_match("/1\s(_?.{3,4})\s(.*)/", $arec, $match);
	if ($ft>0) $afact = $match[1];
	else $afact="";
	$afact = trim($afact);
	
	$ft = preg_match("/1\s(_?.{3,4})\s(.*)/", $brec, $match);
	if ($ft>0) $bfact = $match[1];
	else $bfact="";
	$bfact = trim($bfact);
	
	//-- make sure CHAN facts are displayed at the end of the list
	if ($afact=="CHAN" && $bfact!="CHAN") return 1;
	if ($afact!="CHAN" && $bfact=="CHAN") return -1;
	
	//-- BIRT at the top of the list
	if ($afact=="BIRT" && $bfact!="BIRT") return -1;
	if ($afact!="BIRT" && $bfact=="BIRT") return 1;
	
	//-- DEAT before BURI
	if ($afact=="DEAT" && $bfact=="BURI") return -1;
	if ($afact=="BURI" && $bfact=="DEAT") return 1;
	
	//-- DEAT before CREM
	if ($afact=="DEAT" && $bfact=="CREM") return -1;
	if ($afact=="CREM" && $bfact=="DEAT") return 1;
	
	$cta = preg_match("/2 DATE (.*)/", $arec, $match);
	if ($cta>0) $adate = parse_date(trim($match[1]));
	$ctb = preg_match("/2 DATE (.*)/", $brec, $match);
	if ($ctb>0) $bdate = parse_date(trim($match[1]));

	//-- check if both had a date
	if($cta<$ctb) return 1;
	if($cta>$ctb) return -1;
	//-- neither had a date so sort by fact name
	if(($cta==0)&&($ctb==0)) {
		if ($afact=="EVEN" || $afact=="FACT") {
			$ft = preg_match("/2 TYPE (.*)/", $arec, $match);
			if ($ft>0) $afact = trim($match[1]);
		}
		if ($bfact=="EVEN" || $bfact=="FACT") {
			$ft = preg_match("/2 TYPE (.*)/", $brec, $match);
			if ($ft>0) $bfact = trim($match[1]);
		}
		if (isset($factarray[$afact])) $afact = $factarray[$afact];
		else if (isset($pgv_lang[$afact])) $afact = $pgv_lang[$afact];
		if (isset($factarray[$bfact])) $bfact = $factarray[$bfact];
		else if (isset($pgv_lang[$bfact])) $bfact = $pgv_lang[$bfact];
		return itemsort($afact, $bfact);
	}

	if ($adate[0]["year"]==$bdate[0]["year"]) {
		// Check month
		$montha = $adate[0]["month"];
		$monthb = $bdate[0]["month"];
		
		$montha .= date("Y");
		$monthb .= date("Y");

		$astamp = strtotime($montha);
		$bstamp = strtotime($monthb);

		if ($montha == $monthb) {
		// Check day
			$newa = $adate[0]["day"]." ".$montha;
			$newb = $bdate[0]["day"]." ".$monthb;
			$astamp = strtotime($newa);
			$bstamp = strtotime($newb);
			if ($astamp==$bstamp)	return 0;
			else return ($astamp < $bstamp) ? -1 : 1;
		}
		else return ($astamp < $bstamp) ? -1 : 1;
	}
	return ($adate[0]["year"] < $bdate[0]["year"]) ? -1 : 1;
}

/**
 * fact date sort
 *
 * compare individuals by a fact date
 */
function compare_date($a, $b) {
	global $sortby;
	
	$tag = "BIRT";
	if (!empty($sortby)) $tag = $sortby;
	$abirt = get_sub_record(1, "1 $tag", $a["gedcom"]);
	$bbirt = get_sub_record(1, "1 $tag", $b["gedcom"]);
	$c = compare_facts($abirt, $bbirt);
	if ($c==0) return itemsort($a, $b);
	else return $c;
}

/**
 * get CONT lines
 *
 * get the N+1 CONT or CONC lines of a gedcom subrecord
 * @param int $nlevel the level of the CONT lines to get
 * @param string $nrec the gedcom subrecord to search in
 * @return string a string with all CONT or CONC lines merged
 */
function get_cont($nlevel, $nrec) {
	global $WORD_WRAPPED_NOTES;
	$text = "";
	$tt = preg_match_all("/$nlevel CON[CT](.*)/", $nrec, $cmatch, PREG_SET_ORDER);
	for($i=0; $i<$tt; $i++) {
		if (strstr($cmatch[$i][0], "CONT")) $text.="<br />\n";
		else if ($WORD_WRAPPED_NOTES) $text.=" ";
		$conctxt = ltrim($cmatch[$i][1]);
		$conctxt = preg_replace("/[\r\n]/","",$conctxt);
		$text.=$conctxt;
	}
	$text = preg_replace("/~~/", "<br />", $text);
	return $text;
}

/**
 * get the full file path
 *
 * get the file path from a multimedia gedcom record
 * @param string $mediarec a OBJE subrecord 
 * @return the fullpath from the FILE record
 */
function extract_fullpath($mediarec) {
	preg_match("/(\d) _*FILE (.*)/", $mediarec, $amatch);
	if (empty($amatch[2])) return "";
	$level = trim($amatch[1]);
	$fullpath = trim($amatch[2]);
	$filerec = get_sub_record($level, $amatch[0], $mediarec);
	$fullpath .= get_cont($level+1, $filerec);
	return $fullpath;
}

/**
 * get the relative filename for a media item
 *
 * gets the relative file path from the full media path for a media item.  checks the 
 * <var>$MEDIA_DIRECTORY_LEVELS</var> to make sure the directory structure is maintained.
 * @param string $fullpath the full path from the media record
 * @return string a relative path that can be appended to the <var>$MEDIA_DIRECTORY</var> to reference the item
 */
function extract_filename($fullpath) {
	global $MEDIA_DIRECTORY_LEVELS;
	$filename="";
	$regexp = "'[/\\\]'";
	$ct = preg_match($regexp, $fullpath, $match);
	if ($ct>0) {
		$subelements = preg_split($regexp, $fullpath);
		$subelements = array_reverse($subelements);
		$max = $MEDIA_DIRECTORY_LEVELS;
		if ($max>=count($subelements)) $max=count($subelements)-1;
		for($s=$max; $s>=0; $s--) {
			if ($s!=$max) $filename = $filename."/".$subelements[$s];
			else $filename = $subelements[$s];
		}
	}
	else $filename = $fullpath;
	//print "$filename<br />\n";
	return $filename;
}

//-- functions to take a Jewish date and display it in Hebrew.
//-- provided by: KosherJava
function getFullHebrewJewishDates($year, $month="", $day="", $altYear="", $altMonth="") {
	global $DISPLAY_JEWISH_GERESHAYIM, $TEXT_DIRECTION;
	$USE_FULL_PARTIAL_JEWISH_DATES = true;
	$sb = "<span lang=\"he-IL\" dir=\"rtl\">";
	if($day != "") {
		$sb .= getHebrewJewishDay($day);
		$sb .= " ";
	}
	if($month != "") {
		if($day != "") { //jewish date is exact
			$sb .= getHebrewJewishMonth($month, $year);
			$sb .= " ";
		} else { //only month and not day
			$sb .= getHebrewJewishMonth($month, $year);
			if($USE_FULL_PARTIAL_JEWISH_DATES) {
				if($altMonth != "" && $altMonth != $month) {
					$sb .= " / ";
					$sb .= getHebrewJewishMonth($altMonth, $altYear);
				}
			}
			$sb .= " ";
		}
	}
	if($USE_FULL_PARTIAL_JEWISH_DATES) {
		if($month=="") {
			$sb .= getHebrewJewishYear($year - 1);
			$sb .= " / ";
			$sb .= getHebrewJewishYear($year);
		} else if($altMonth!=0 && $month != $altMonth && $altYear !=0 && $altYear != $year && $day == "") {
			$sb .= getHebrewJewishYear($year);
			$sb .= " / ";
			$sb .= getHebrewJewishYear($altYear);
		} else {
			$sb .= getHebrewJewishYear($year);
		}
	} else {
		$sb .= getHebrewJewishYear($year);
	}

	$sb .= "</span>";
  	if($TEXT_DIRECTION == "ltr") { //only do this for ltr languages
  		$sb.= "&lrm;"; //add entity to return to left to right direction
  	}
	if($DISPLAY_JEWISH_GERESHAYIM == false) {
		$sb = preg_replace(array("/\"/", "/'/"), array("",""), $sb);
	}
	return $sb;
}

function getHebrewJewishDates($year, $month="", $day="", $altYear="", $altMonth="") {
	global $DISPLAY_JEWISH_GERESHAYIM, $TEXT_DIRECTION;
	$sb = "<span lang=\"he-IL\" dir=\"rtl\">";
	if($day != "") {
		$sb .= getHebrewJewishDay($day);
		$sb .= " ";
	}
	if($month != "") {
		if($day != "") { //jewish date is exact
			$sb .= getHebrewJewishMonth($month, $year);
			$sb .= " ";
		} else { //only month and not day
			$sb .= getHebrewJewishMonth($month, $year); //FIXME Since month is based on 1st of the Gregorian date,
			$sb .= " ";							//		Would be nice to return both months.
		}
	}
	if($year != "") {
		$sb .= getHebrewJewishYear($year);
	}
	$sb .= "</span>";
  	if($TEXT_DIRECTION == "ltr") { //only do this for ltr languages
  		$sb.= "&lrm;"; //add entity to return to left to right direction
  	}

	if($DISPLAY_JEWISH_GERESHAYIM == false) {
			$sb = preg_replace(array("/\"/", "/'/"), array("",""), $sb);
	}
	return $sb;
}

function getHebrewJewishYear($year) {
	global $DISPLAY_JEWISH_THOUSANDS;

	$jAlafim = "אלפים";                       //word ALAFIM in Hebrew for display on years evenly divisable by 1000
	$jHundreds = array("", "ק", "ר", "ש", "ת", "תק", "תר","תש", "תת", "תתק");
	$jTens = array("", "י", "כ", "ל", "מ", "נ", "ס", "ע", "פ", "צ");
	$jTenEnds = array("", "י", "ך", "ל", "ם", "ן", "ס", "ע", "ף", "ץ");
	$tavTaz = array("ט\"ו", "ט\"ז");
	$jOnes = array("", "א", "ב", "ג", "ד", "ה", "ו", "ז", "ח", "ט");

	$singleDigitYear = isSingleDigitJeiwshYear($year);
	$thousands = $year / 1000;                                   //get # thousands

	$sb = "";	
	//append thousands to String
	if($year % 1000 == 0) {                                      // in year is 5000, 4000 etc
		$sb .= $jOnes[$thousands];
		$sb .= "'";
		$sb .= " ";
		$sb .= $jAlafim;                                         //add # of thousands plus word thousand (overide alafim boolean)
	} else if($DISPLAY_JEWISH_THOUSANDS) {                       // if alafim boolean display thousands
		$sb .= $jOnes[$thousands];
		$sb .= "'";                                              //append thousands quote
		$sb .= " ";
	}
	$year = $year % 1000;                                        //remove 1000s
	$hundreds = $year / 100;                                     // # of hundreds
	$sb .= $jHundreds[$hundreds];                                //add hundreds to String
	$year = $year % 100;                                         //remove 100s
	if($year == 15) {                                            //special case 15
		$sb .= $tavTaz[0];
	} else if($year == 16) {                                     //special case 16
		$sb .= $tavTaz[1];
	} else {
		$tens = $year / 10;
		if($year % 10 == 0) {                                    // if evenly divisable by 10
			if($singleDigitYear == false) {
				$sb .= $jTenEnds[$tens]; // use end letters so that for example 5750 will end with an end nun
			} else {
				$sb .= $jTens[$tens]; // use standard letters so that for example 5050 will end with a regular nun
			}
		} else {
			$sb .= $jTens[$tens];
			$year = $year % 10;
			$sb .= $jOnes[$year];
		}
	}

	if($singleDigitYear == true) {
		$sb .= "'"; //append single quote
	} else { // append double quote before last digit
        $pos1 = strlen($sb)-2;
// MA	$pos1 = strrpos($sb, "&");
 		$sb = substr($sb, 0, $pos1) . "\"" . substr($sb, $pos1);
		$sb = preg_replace("/\"\"/", "\"", $sb);
	}
	return $sb;
}

function getHebrewJewishMonth($month, $year) {
	$jMonths = array("תשרי",
			"חשון",
			"כסלו",
			"טבת",
			"שבט",
			"אדר א'",
			"אדר ב'",
			"ניסן",
			"אייר",
			"סיון",
			"תמוז",
			"אב",
			"אלול",
			"אדר"); //last 1 is Adar for non leap year

	if (empty($month)) return "";
	if($month == 6) { // if Adar check for leap year
		if(isJewishLeapYear($year)) {
			return $jMonths[5]; //if it is leap year return default php "Adar A"
		} else { // non leap year
			return $jMonths[13];
		}
	} else { // non Adar months
		return $jMonths[$month - 1];
	}
}

function getHebrewJewishDay($day) {
	$jTens = array("", "י", "כ", "ל", "מ", "נ", "ס", "ע", "פ", "צ");
	$jTenEnds = array("", "י", "ך", "ל", "ם", "ן", "ס", "ע", "ף", "ץ");
	$tavTaz = array("ט\"ו", "ט\"ז");
	$jOnes = array("", "א", "ב", "ג", "ד", "ה", "ו", "ז", "ח", "ט");

	if (empty($day)) return "";
	$sb = "";
	if($day < 10) { //single digit days get single quote appended
		$sb .= $jOnes[$day];
		$sb .= "'";
	} else if($day == 15) { //special case 15
		$sb .= $tavTaz[0];
	} else if($day == 16) { //special case 16
		$sb .= $tavTaz[1];
	} else {
		$tens = $day / 10;
		$sb .= $jTens[$tens];
		if($day % 10 == 0) { // 10 or 20 single digit append single quote
			$sb .= "'";
		} else if($day > 10) { // >10 display " between 10s and 1s
			$sb .= "\"";
		}
		$day = $day % 10; //discard 10s
		$sb .= $jOnes[$day];
	}
	return $sb;
}

function isJewishLeapYear($year) {
	if($year % 19 == 0 || $year % 19 == 3 || $year % 19 ==6 || $year % 19 == 8 || $year % 19 == 11
			|| $year % 19 == 14 || $year % 19 == 17) { // 3rd, 6th, 8th, 11th, 14th, 17th or 19th years of 19 year cycle
		return true;
	} else { // non leap year
		return false;
	}
}

function isSingleDigitJeiwshYear($year) {
	$shortYear = $year %1000; //discard thousands
	//next check for all possible single Hebrew digit years
	if($shortYear < 11 || ($shortYear <100 && $shortYear % 10 == 0)  || ($shortYear <= 400 && $shortYear % 100 == 0) ) {
		return true;
	} else {
		return false;
	}
}

function getJewishMonthName($month, $year) {
	global $JEWISH_ASHKENAZ_PRONUNCIATION;
	$ashkenazMonths = array("Tishrei", "Cheshvan", "Kislev", "Teves", "Shevat", "Adar I", "Adar II", "Nisan", "Iyar", "Sivan", "Tamuz", "Av", "Elul", "Adar");
	$sefardMonths = array("Tishrei", "Heshvan", "Kislev", "Tevet", "Shevat", "Adar I", "Adar II", "Nisan", "Iyar", "Sivan", "Tamuz", "Av", "Elul", "Adar");
	$monthNames = $ashkenazMonths;
	if($JEWISH_ASHKENAZ_PRONUNCIATION != true) {
		$monthNames = $sefardMonths;
	}
	if($month == 6) { // if Adar check for leap year
		if(isJewishLeapYear($year)) {
			return $monthNames[5];
		} else {
			return $monthNames[13];
		}
	} else {
		if (isset($monthNames[$month - 1])) return $monthNames[$month - 1];
		else return $monthNames[$month];
	}

}

/**
 * convert a date to other languages or formats
 *
 * converts and translates a date based on the selected language and calendar format
 * @param string $dstr_beg prepend this string to the converted date
 * @param string $dstr_end append the string to the converted date
 * @param int $day the day of month for the date
 * @param string $month the abbreviated month (ie JAN, FEB, MAR, etc)
 * @param int $year the year (ie 1900, 2004, etc)
 * @return string the new converted date
 */
function convert_date($dstr_beg, $dstr_end, $day, $month, $year) {
	global $pgv_lang, $DATE_FORMAT, $LANGUAGE, $CALENDAR_FORMAT, $monthtonum, $TEXT_DIRECTION;
	$altDay=30;
	$month = trim($month);
	$day = trim($day);
	$skipday = false;
	$skipmonth = false;
	if (empty($month)||!isset($monthtonum[strtolower($month)])) {
		$dstr_beg .= " ".$month." ";
		$month = "jan";
		$skipmonth=true;
	}
	if (empty($day)) {
		$day = 1;
		if ((!empty($month))&&(isset($monthtonum[$month]))){
			$yy = $year;
			//-- make sure there is always a year
			if (empty($yy)) $yy = date("Y");
			if (function_exists("cal_days_in_month")) $altDay = cal_days_in_month(CAL_GREGORIAN, $monthtonum[$month], $yy);
			else $altDay = 30;
		}
		$skipday = true;
	}
	if ($CALENDAR_FORMAT=="jewish" && $LANGUAGE != "hebrew" && !empty($year)) {
		$month = $monthtonum[$month];
		$jd = gregoriantojd($month, $day, $year);
		$hebrewDate = jdtojewish($jd);
		list ($hebrewMonth, $hebrewDay, $hebrewYear) = split ('/', $hebrewDate);
		$altJd = gregoriantojd($month, $altDay, $year);
		$altHebrewDate = jdtojewish($altJd);
		list ($altHebrewMonth, $altHebrewDay, $altHebrewYear) = split ('/', $altHebrewDate);

		$hebrewMonthName = getJewishMonthName($hebrewMonth, $hebrewYear);
		if($skipday && !$skipmonth && $altHebrewMonth !=0 && $altHebrewYear !=0 && $hebrewMonth != $altHebrewMonth && $hebrewYear != $altHebrewYear) { //elul tishrai
			$hebrewMonthName .= " ";
			$hebrewMonthName .= $hebrewYear;
			$hebrewYear = " / ";
			$hebrewYear .= getJewishMonthName($altHebrewMonth, $altHebrewYear);
			$hebrewYear .= " ";
			$hebrewYear .= $altHebrewYear;
		} else if($skipday && !$skipmonth && $altHebrewMonth !=0 && $hebrewMonth != $altHebrewMonth) {
			$hebrewMonthName .= " / ";
			$hebrewMonthName .= getJewishMonthName($altHebrewMonth, $altHebrewYear);
		} else if($altHebrewYear !=0 && $hebrewYear != $altHebrewYear && $skipday) {
			$hebrewYear .= " / ";
			$hebrewYear .= $altHebrewYear;
		}
		if ($skipday) $hebrewDay = "";
		if ($skipmonth) $hebrewMonthName = "";
		if ($DATE_FORMAT == "D. M Y" && $skipday) 
			 $newdate = preg_replace("/D/", $hebrewDay, "D M Y");
		else $newdate = preg_replace("/D/", $hebrewDay, $DATE_FORMAT);
		$newdate = preg_replace("/M/", $hebrewMonthName, $newdate);
		$newdate = preg_replace("/Y/", $hebrewYear, $newdate);
		$datestr = $dstr_beg . $newdate . $dstr_end;	
	}
	else if ($CALENDAR_FORMAT=="jewish_and_gregorian" && $LANGUAGE != "hebrew") {
		$monthnum = $monthtonum[$month];
		$jd = gregoriantojd($monthnum, $day, $year);
		$hebrewDate = jdtojewish($jd);
		list ($hebrewMonth, $hebrewDay, $hebrewYear) = split ('/', $hebrewDate);

		$altJd = gregoriantojd($monthnum, $altDay, $year);
		$altHebrewDate = jdtojewish($altJd);
		list ($altHebrewMonth, $altHebrewDay, $altHebrewYear) = split ('/', $altHebrewDate);
		$hebrewMonthName = getJewishMonthName($hebrewMonth, $hebrewYear);

		if($skipday && !$skipmonth && $altHebrewMonth !=0 && $altHebrewYear !=0 && $hebrewMonth != $altHebrewMonth && $hebrewYear != $altHebrewYear ) { //elul tishrai
			$hebrewMonthName .= " ";
			$hebrewMonthName .= $hebrewYear;
			$hebrewYear = " / ";
			$hebrewYear .= getJewishMonthName($altHebrewMonth, $altHebrewYear);
			$hebrewYear .= " ";
			$hebrewYear .= $altHebrewYear;
		} else if($skipday && !$skipmonth && $altHebrewMonth !=0 && $hebrewMonth != $altHebrewMonth) {
			$hebrewMonthName .= " / ";
			$hebrewMonthName .= getJewishMonthName($altHebrewMonth, $altHebrewYear);
		} else if($altHebrewYear !=0 && $hebrewYear != $altHebrewYear && $skipday) {
			$hebrewYear .= " / ";
			$hebrewYear .= $altHebrewYear;
		}

		if ($skipday) $hebrewDay = "";
		if ($skipmonth) $hebrewMonthName = "";
		if (!empty($year)) {
		if ($DATE_FORMAT == "D. M Y" && $skipday) 
			 $newdate = preg_replace("/D/", $hebrewDay, "D M Y");
		else $newdate = preg_replace("/D/", $hebrewDay, $DATE_FORMAT);				
		$newdate = preg_replace("/M/", $hebrewMonthName, $newdate);
		$newdate = preg_replace("/Y/", $hebrewYear, $newdate);
		}
		else $newdate="";
		if ($skipday) $day = "";
		if ($skipmonth) $month = "";
		if ($DATE_FORMAT == "D. M Y" && $skipday) 
			 $gdate = preg_replace("/D/", $day, "D M Y");
		else $gdate = preg_replace("/D/", $day, $DATE_FORMAT);						 
		$gdate = preg_replace("/M/", $month, $gdate);
		$gdate = preg_replace("/Y/", $year, $gdate);
		$gdate = trim($gdate);
		$datestr = $dstr_beg . $newdate . " ($gdate)" . $dstr_end;
	}
	else if (($CALENDAR_FORMAT=="hebrew" || $CALENDAR_FORMAT=="jewish" && $LANGUAGE == "hebrew") && !empty($year)) { 
																					 
		$month = $monthtonum[$month];
		$jd = gregoriantojd($month, $day, $year);
		$hebrewDate = jdtojewish($jd);
		list ($hebrewMonth, $hebrewDay, $hebrewYear) = split ('/', $hebrewDate);

		$altJd = gregoriantojd($month, $altDay, $year);
		$altHebrewDate = jdtojewish($altJd);
		list ($altHebrewMonth, $altHebrewDay, $altHebrewYear) = split ('/', $altHebrewDate);

		if ($skipday) $hebrewDay = "";
		if ($skipmonth) $hebrewMonth = "";
		$newdate = getFullHebrewJewishDates($hebrewYear, $hebrewMonth, $hebrewDay, $altHebrewYear, $altHebrewMonth);
		$datestr = $dstr_beg . $newdate . $dstr_end;
	}
	else if ($CALENDAR_FORMAT=="hebrew_and_gregorian" || $CALENDAR_FORMAT=="jewish_and_gregorian" && $LANGUAGE == "hebrew") { 
		$monthnum = $monthtonum[$month];
		if (preg_match("/^\d+$/", $year)==0) $year = date("Y");
		$jd = gregoriantojd($monthnum, $day, $year);
		$hebrewDate = jdtojewish($jd);
		list ($hebrewMonth, $hebrewDay, $hebrewYear) = split ('/', $hebrewDate);

		$altJd = gregoriantojd($monthnum, $altDay, $year);
		$altHebrewDate = jdtojewish($altJd);
		list ($altHebrewMonth, $altHebrewDay, $altHebrewYear) = split ('/', $altHebrewDate);

		if ($skipday) $hebrewDay = "";
		if ($skipmonth) $hebrewMonth = "";
		if (!empty($year)) $newdate = getFullHebrewJewishDates($hebrewYear, $hebrewMonth, $hebrewDay, $altHebrewYear, $altHebrewMonth);
		else $newdate = "";
		if ($skipday) $day = "";
		if ($skipmonth) $month = "";
		if ($DATE_FORMAT == "D. M Y" && $skipday) 
			 $gdate = preg_replace("/D/", $day, "D M Y");
		else $gdate = preg_replace("/D/", $day, $DATE_FORMAT);						 
		$gdate = preg_replace("/M/", $month, $gdate);
		$gdate = preg_replace("/Y/", $year, $gdate);
		$gdate = trim($gdate);
		$datestr = $dstr_beg  . " ". $newdate . " ($gdate) ". $dstr_end;
	}
	else if ($CALENDAR_FORMAT=="julian") {
		$monthnum = $monthtonum[$month];
		$jd = gregoriantojd($monthnum, $day, $year);
		$jDate = jdtojulian($jd);
		list ($jMonth, $jDay, $jYear) = split ('/', $jDate);
		$jMonthName = jdmonthname ( $jd, 3);
		if ($skipday) $jDay = "";
		if ($skipmonth) $jMonthName = "";
		$newdate = preg_replace("/D/", $jDay, $DATE_FORMAT);
		$newdate = preg_replace("/M/", $jMonthName, $newdate);
		$newdate = preg_replace("/Y/", $jYear, $newdate);
		$datestr = $dstr_beg . $newdate . $dstr_end;
	}
	else if ($CALENDAR_FORMAT=="hijri") {
		$monthnum = $monthtonum[$month];
		$hDate = getHijri($day, $monthnum, $year);
		list ($hMonthName, $hDay, $hYear) = split ('/', $hDate);
		if ($skipday) $hDay = "";
		if ($skipmonth) $hMonthName = "";
		$newdate = preg_replace("/D/", $hDay, $DATE_FORMAT);
		$newdate = preg_replace("/M/", $hMonthName, $newdate);
		$newdate = preg_replace("/Y/", $hYear, $newdate);
		$datestr = $dstr_beg . '<span dir="rtl" lang="ar-sa">'.$newdate . '</span>';
		if($TEXT_DIRECTION == "ltr") { //only do this for ltr languages
	  		$datestr .= "&lrm;"; //add entity to return to left to right direction
	  	}
		$datestr .= $dstr_end;
	}	
	else if ($CALENDAR_FORMAT=="arabic") {
		$monthnum = $monthtonum[$month];
		$aDate = getArabic($day, $monthnum, $year);
		list ($aMonthName, $aDay, $aYear) = split ('/', $aDate);
		if ($skipday) $aDay = "";
		if ($skipmonth) $aMonthName = "";
		$newdate = preg_replace("/D/", $aDay, $DATE_FORMAT);
		$newdate = preg_replace("/M/", $aMonthName, $newdate);
		$newdate = preg_replace("/Y/", $aYear, $newdate);
		$datestr = $dstr_beg . '<span dir="rtl" lang="ar-sa">'.$newdate . '</span>';
		if($TEXT_DIRECTION == "ltr") { //only do this for ltr languages
	  		$datestr .= "&lrm;"; //add entity to return to left to right direction
	  	}
		$datestr .= $dstr_end;
	}	
	else if ($CALENDAR_FORMAT=="french") {
		$monthnum = $monthtonum[$month];
		$jd = gregoriantojd($monthnum, $day, $year);
		$frenchDate = jdtofrench($jd);
		list ($fMonth, $fDay, $fYear) = split ('/', $frenchDate);
		$fMonthName = jdmonthname ( $jd, 5);
		if ($skipday) $fDay = "";
		if ($skipmonth) $fMonthName = "";
		$newdate = preg_replace("/D/", $fDay, $DATE_FORMAT);
		$newdate = preg_replace("/M/", $fMonthName, $newdate);
		$newdate = preg_replace("/Y/", $fYear, $newdate);
		$datestr = $dstr_beg . $newdate . $dstr_end;
	}
	else {
		$temp_format = "~".$DATE_FORMAT;
		if ($skipday)
		{
		  //-- if the D is before the M the get the substr of everthing after the M
		  //-- if the D is after the M then just replace it
		  //-- @TODO figure out how to replace D. anywhere in the string
		  $pos1 = strpos($temp_format, "M");
		  $pos2 = strpos($temp_format, "D");
		  if ($pos2<$pos1) $temp_format = substr($temp_format, $pos1-1);
		  else $temp_format = preg_replace("/D/", "", $temp_format);
		}
		if ($skipmonth)
		{
		  $month = "";
		  $dpos_d_01 = strpos($temp_format, "M");
		  $dpos_d_00 = $dpos_d_01;
		  $dpos_d_02 = strlen($temp_format);
		  if ($dpos_d_01>0)
		  {
			while (!strpos("DY",$temp_format[$dpos_d_01]))
			{
			  $temp_format01 = substr($temp_format,0,$dpos_d_00);
			  $temp_format02 = substr($temp_format,$dpos_d_01);
			  $temp_format = $temp_format01.$temp_format02;
			  $dpos_d_02 = strlen($temp_format);
			  $dpos_d_01++;
			  if ($dpos_d_01 >= $dpos_d_02) break;
			}
		  }
		}
		$newdate = trim(substr($temp_format,1));

		if ($LANGUAGE=="chinese") {
			$day = convert_number($day);
			$yearStr = "".$year;
			$year="";
			for($i=0; $i<strlen($yearStr); $i++) {
				$year .= convert_number($yearStr{$i});
			}
		}
		$newdate = preg_replace("/D/", $day, $newdate);
		$newdate = preg_replace("/M/", $month, $newdate);
		$newdate = preg_replace("/Y/", $year, $newdate);	
		$datestr = $dstr_beg . $newdate . $dstr_end;
	}
	return $datestr;
}

//-- end of Jewish date functions

//-- Hebrew date (escape value @#DHEBREW@) month (Jewish) or full date (Hebrew pages) translation  (meliza)  
function convert_hdate($dstr_beg, $dstr_end, $day, $month, $year) {
	global $LANGUAGE, $CALENDAR_FORMAT, $monthtonum; 
	
	$month = $monthtonum[$month];
	
	if ($LANGUAGE != "hebrew" && ($CALENDAR_FORMAT == "gregorian" || $CALENDAR_FORMAT == "jewish" || $CALENDAR_FORMAT == "jewish_and_gregorian")) {
		if ($month != "") $hebrewMonthName = getJewishMonthName($month, $year);
		else $hebrewMonthName = "";
		$datestr = $dstr_beg . $day . " " . $hebrewMonthName . " " . $year . " " . $dstr_end;
	}
	else {																	  //-- Hebrew page
		$newdate = getHebrewJewishDates($year, $month, $day);
		$datestr = $dstr_beg . $newdate . $dstr_end;
	} 
	return $datestr;		
}	 
													
//-- functions to take a date and display it in Finnish.
//-- provided by: KurtNorgaz
//-- updated by Meliza
function getFinnishDate($datestr, $day) {

	global $pgv_lang;
	
	//-- the Finnish text of the value for one date is shown at the end of the date
	//-- the Finnish values of two dates are replaced by a -
	$array_short = array("aft", "bet", "from", "to");
	foreach($array_short as $value) {
	
	  $oldDateStr = $datestr;
	  $newdatestr = preg_replace("/$value([^a-zA-Z])/i", "" . "\$1", $datestr);
	  if ($newdatestr != $datestr) {
	  
		$datestr = $newdatestr;
	 
		switch ($value) {		
		  case "from" : $datestr = trim($datestr);
						$temp_date = strtolower($datestr);		  
						$pos_of_to = strpos(" ".$temp_date, "to"); 
						$newdatestr = preg_replace("/to/", "", $temp_date); 				  
						if ($newdatestr != $temp_date) {						
							$datestr_01 = trim(substr($datestr, 0, $pos_of_to - 2));
							$datestr_02 = substr($datestr, $pos_of_to + 1); 				
							$datestr = $datestr_01." - ".$datestr_02." ";
						}	
						else $datestr = $datestr." ".$pgv_lang[$value];
						break;		
		  case "bet"  : $datestr = trim($datestr);
						$temp_date = strtolower($datestr);	
						$pos_of_and = strpos(" ".$temp_date, "and");
						$datestr_01 = trim(substr($datestr, 0, $pos_of_and - 2));
						$datestr_02 = substr($datestr, $pos_of_and + 2);						
						if (strlen($datestr_01) > 0 && strlen($datestr_02) > 0)
							$datestr = $datestr_01." - ".$datestr_02." ";						
						break;
		  case "to"   : $datestr = $newdatestr." ".$pgv_lang[$value]; break;
		  case "aft"  : $datestr = $newdatestr." ".$pgv_lang[$value]; break;
		  default	  : $datestr = $oldDateStr; break;
		} 
	  } 	
	}		  
	//-- the Finnish text of the value is shown bau before the date
	$array_short = array("abt", "apx", "bef", "cal", "est", "int", "cir");
	foreach($array_short as $value) {
		$datestr = preg_replace("/^$value([^a-zA-Z])/i", $pgv_lang[$value]."\$1", $datestr);
		$datestr = preg_replace("/(\W)$value([^a-zA-Z])/i", "\$1".$pgv_lang[$value]."\$2", $datestr);  
	}
	//-- Constant 'ta' is appended to the Finnish month values, if a day value exists (for the last date)
	$array_short = array("jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec");
	foreach($array_short as $value) {

	if ($day > 0) { 			  
		 $datestr = preg_replace("/(\W)$value([^a-zA-Z])/i", "\$1".$pgv_lang[$value]."ta"."\$2", $datestr); 
		 $datestr = preg_replace("/^$value([^a-zA-Z])/i", $pgv_lang[$value]."ta"."\$1", $datestr);
	  }
	else {
		 $datestr = preg_replace("/(\W)$value([^a-zA-Z])/i", "\$1".$pgv_lang[$value]."\$2", $datestr); 
		 $datestr = preg_replace("/^$value([^a-zA-Z])/i", $pgv_lang[$value]."\$1", $datestr);  
	  } 
	}
	return $datestr;
}

//-- end of Finnish date functions

//-- functions to take a date and display it in Turkish.
//-- provided by: KurtNorgaz
function getTurkishDate($datestr)
{
	global $pgv_lang;

	$array_short = array("jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec", "est");
	foreach($array_short as $value)
	{
	  $datestr = preg_replace("/$value([^a-zA-Z])/i", $pgv_lang[$value] . "\$1", $datestr);
	}

	$array_short = array("abt", "aft", "and", "bef", "bet", "cal", "from", "int", "to", "cir");

	foreach($array_short as $value)
	{
	  $oldDateStr = $datestr;
	  $newdatestr = preg_replace("/$value([^a-zA-Z])/i", "" . "\$1", $datestr);

	  if ($newdatestr != $datestr)
	  {
		$pos_of_value = strpos(" " . $datestr, $value);
		$datestr = $newdatestr;

		switch ($value)
		{
		  case "from"	: $datestr = trim($datestr);
					  $pos_of_to = strpos(" " . $datestr, "to");
					  $datestr_01 = trim(substr($datestr, 0, $pos_of_to - 1));
					  $datestr_02 = substr($datestr, $pos_of_to - 2);

					  if (strlen($datestr_01) > 0)
					  {
						$last_char = $datestr[strlen($datestr_01)-1];
					  }
					  else $last_char = "";
					  switch ($last_char)
					  {
						case "0" : if (strlen($datestr_01) > 1)
								   {
									 $last_two_char = substr($datestr_01,-2);
								   }
								   else $last_two_char = "";
								   switch ($last_two_char)
								   {
									 case "00" : $extension = "den"; break;
									 case "20" : $extension = "den"; break;
									 case "50" : $extension = "den"; break;
									 case "70" : $extension = "den"; break;
									 case "80" : $extension = "den"; break;
									 default   : $extension = "dan"; break;
								   }
								   break;
						case "6" : $extension = "dan"; break;
						case "9" : $extension = "dan"; break;
						default  : $extension = "den"; break;
					  }
					  $datestr_01 .= stripslashes($pgv_lang[$value]);
					  $datestr_01 = str_replace("#EXT#", $extension, $datestr_01);

					  $datestr = $datestr_01 . $datestr_02;
					  break;

		  case "to" 	: $datestr = trim($datestr);
					  if (strlen($datestr) > 0)
					  {
						$last_char = $datestr[strlen($datestr)-1];
					  }
					  else $last_char = "";
					  switch ($last_char)
					  {
						case "0" : $extension = "a"; break;
						case "9" : $extension = "a"; break;
						case "2" : $extension = "ye"; break;
						case "7" : $extension = "ye"; break;
						case "6" : $extension = "ya"; break;
						default  : $extension = "e"; break;
					  }
					  $datestr .= stripslashes($pgv_lang[$value]);
					  $datestr = str_replace("#EXT#", $extension, $datestr);
					  break;

		  case "bef"	: $datestr = trim($datestr);
					  if (strlen($datestr) > 0)
					  {
						$last_char = $datestr[strlen($datestr)-1];
					  }
					  else $last_char = "";
					  switch ($last_char)
					  {
						case "0" : if (strlen($datestr) > 1)
								   {
									 $last_two_char = substr($datestr,-2);
								   }
								   else $last_two_char = "";
								   switch ($last_two_char)
								   {
									 case "00" : $extension = "den"; break;
									 case "20" : $extension = "den"; break;
									 case "50" : $extension = "den"; break;
									 case "70" : $extension = "den"; break;
									 case "80" : $extension = "den"; break;
									 default   : $extension = "dan"; break;
								   }
								   break;
						case "6" : $extension = "dan"; break;
						case "9" : $extension = "dan"; break;
						default  : $extension = "den"; break;
					  }
					  $datestr .= stripslashes($pgv_lang[$value]);
					  $datestr = str_replace("#EXT#", $extension, $datestr);
					  break;

		  case "cir"	: $datestr .= stripslashes($pgv_lang[$value]);
					  break;

		  default		: $datestr = $oldDateStr;
					  break;
		}
	  }
	}

	return $datestr;
}

//-- end of Turkish date functions


/**
 * parse a gedcom date
 *
 * this function will parse a gedcom date and convert it to the form defined by the language file
 * by calling the convert_date function
 * @param string $datestr the date string (ie everything after the DATE tag)
 * @return string the new date string
 */
function get_changed_date($datestr) {
	global $pgv_lang, $DATE_FORMAT, $LANGUAGE, $CALENDAR_FORMAT, $monthtonum, $dHebrew;
	$checked_dates = array();
	//-- Is the date a Hebrew date
	if (stristr($datestr, "#DHEBREW")) {
		 $dHebrew=1;
		 $datestr = preg_replace("/@([#A-Z]+)@/", "", $datestr); 
	}  
	else $dHebrew=0;

	//-- check for DAY MONTH YEAR dates
	$Dt = "";
	$ct = preg_match_all("/(\d{1,2}\s)?([a-zA-Z]{3})?\s?(\d{4})?/", $datestr, $match, PREG_SET_ORDER);
	for($i=0; $i<$ct; $i++) {
		$match[$i][0] = trim($match[$i][0]);
		if ((!empty($match[$i][0]))&&(!in_array($match[$i][0], $checked_dates))) {
			if (!empty($match[$i][1])) $day = trim($match[$i][1]);
			else $day = "";
			if (!empty($match[$i][2])) $month = strtolower($match[$i][2]);
			else $month = "";

			if (isset($monthtonum[$month])&&(preg_match("/".$month."[a-z]/i", $datestr)==0)) {
								$checked_dates[] = $match[$i][0];
				if (!empty($match[$i][3])) $year = $match[$i][3];
				else $year = "";
				$pos1 = strpos($datestr, $match[$i][0]);
				$pos2 = $pos1 + strlen($match[$i][0]);
				$dstr_beg = substr($datestr, 0, $pos1);
				$dstr_end = substr($datestr, $pos2);
				//-- sometimes with partial dates a space char is found in the match and not added to the dstr_beg string
				//-- the following while loop will check for spaces at the start of the match and add them to the dstr_beg
				$j=0;
				while(($j<strlen($match[$i][0]))&&($match[$i][0]{$j}==" ")) {
					$dstr_beg.=" ";
					$j++;
				}
				//<-- Day zero-suppress
				if ($day > 0 && $day < 10) $day = preg_replace("/0/", ""."\$1", $day);
				if (!$dHebrew) {
					$datestr = convert_date($dstr_beg, $dstr_end, $day, $month, $year);
					if ($day != "") $Dt = $day;
				}	
				else {
					$datestr = convert_hdate($dstr_beg, $dstr_end, $day, $month, $year);
					$Dt = "";
				}
			}
			else $month=""; 
		}
	}
	if (!isset($month)) $month="";
	//-- search for just years because the above code will only allow dates with a valid month to pass
	//-- this will make sure years get converted for non romanic alphabets such as hebrew
	$ct = preg_match_all("/.?(\d\d\d\d)/", $datestr, $match, PREG_SET_ORDER);
	
	if ((stristr($CALENDAR_FORMAT, "hebrew")!==false) || (stristr($CALENDAR_FORMAT, "jewish")!==false) || ($dHebrew)) { 
		$checked_dates_str = implode(",", $checked_dates);
		for($i=0; $i<$ct; $i++) {
			$match[$i][0] = trim($match[$i][0]);
			if ((!empty($match[$i][0]))&&(stristr($checked_dates_str, $match[$i][0])===false)&&(strstr($match[$i][0], "#")===false)) {
				$checked_dates_str .= ", ".$match[$i][0];
				$day = "";
				$month = "";
				$year = $match[$i][1];
				if ($year<4000) {
					$pos1 = strpos($datestr, $match[$i][0]);
					$pos2 = $pos1 + strlen($match[$i][0]);
					$dstr_beg = substr($datestr, 0, $pos1);
					$dstr_end = substr($datestr, $pos2);	
					if (!$dHebrew) $datestr = convert_date($dstr_beg, $dstr_end, $day, $month, $year);
					else $datestr = convert_hdate($dstr_beg, $dstr_end, $day, $month, $year);
				}
			}
		}	
	}
	else if ($CALENDAR_FORMAT=="hijri") {
		$checked_dates_str = implode(",", $checked_dates);
		for($i=0; $i<$ct; $i++) {
			$match[$i][0] = trim($match[$i][0]);
			if ((!empty($match[$i][0]))&&(stristr($checked_dates_str, $match[$i][0])===false)&&(strstr($match[$i][0], "#")===false)&&(stristr($datestr, $match[$i][0]."</span>")===false)) {
				$checked_dates_str .= ", ".$match[$i][0];
				$day = "";
				$month = "";
				$year = $match[$i][1];
				//if ($year<4000) {
					$pos1 = strpos($datestr, $match[$i][0]);
					$pos2 = $pos1 + strlen($match[$i][0]);
					$dstr_beg = substr($datestr, 0, $pos1);
					$dstr_end = substr($datestr, $pos2);	
					$datestr = convert_date($dstr_beg, $dstr_end, $day, $month, $year); 
				//}
			}
		}
	}
	if ($LANGUAGE == "turkish") $datestr = getTurkishDate($datestr);
	else {
	if ($LANGUAGE == "finnish") $datestr = getFinnishDate($datestr, $Dt);
	else {
		$array_short = array("jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec", "abt", "aft", "and", "bef", "bet", "cal", "est", "from", "int", "to", "cir", "apx");
		foreach($array_short as $value){
			$datestr = preg_replace("/(\W)$value([^a-zA-Z])/i", "\$1".$pgv_lang[$value]."\$2", $datestr);
			$datestr = preg_replace("/^$value([^a-zA-Z])/i", $pgv_lang[$value]."\$1", $datestr);
		}
	  }
	}
	return $datestr;
} 

/**
 * create an anchor url to the calendar for a date
 *
 * create an anchor url to the calendar for a date and parses the date using the get changed date 
 * function
 * @author Roland (botak)
 * @param string $datestr the date string (ie everything after the DATE tag)
 * @return string a converted date with anchor html tags around it <a href="">date</a>
 */
function get_date_url($datestr){
	if (!stristr($datestr, "#DHEBREW")){
//		Commented out 2 lines as I don't know why they are here
//		$datestrip = preg_replace("/[a-zA-Z]/", "", $datestr);
//		$datestrip = trim($datestrip);

		$checked_dates = array();
		//-- added trim to datestr to fix broken links produced by matches to a single space char
		$ct = preg_match_all("/(\d{1,2}\s)?([a-zA-Z]{3})?\s?(\d{3,4})?/", trim($datestr), $match, PREG_SET_ORDER);
		for($i=0; $i<$ct; $i++) {
			if (substr(strtolower(trim($datestr)), 0, 3)== "bet"){	// Checks if date begin with bet(ween)
				$cb = preg_match_all("/ (\d\d\d\d|\d\d\d)/", trim($datestr), $match_bet, PREG_SET_ORDER);
					if (!empty($match_bet[0][0])) $start_year = trim($match_bet[0][0]);
					else $start_year = "";
					if (!empty($match_bet[1][0])) $end_year = trim($match_bet[1][0]);
					else $end_year = "";
					if ($start_year>$end_year){
						$datelink = $start_year;
						$start_year = $end_year;
						$end_year = $datelink;
					}
					$datelink = "<a href=\"calendar.php?";
					if (isset($start_year) && strlen($start_year) > 0) $datelink .= "year=".$start_year;
					if ((isset($end_year) && strlen($end_year) > 0)&&(isset($start_year) && strlen($start_year) > 0)) $datelink .= "-";
					if (isset($end_year) && strlen($end_year) > 0) $datelink .= $end_year;
					$datelink .= "&amp;filterof=all&amp;action=year\">";
					$datelink .= get_changed_date($datestr)."</a>";
			}
			else {
				$match[$i][0] = trim($match[$i][0]);
				if ((!empty($match[$i][0]))&&(!in_array($match[$i][0], $checked_dates))) {
					$checked_dates[] = $match[$i][0];
					if (!empty($match[$i][1])) $day = trim($match[$i][1]);
					else $day = "";
					if (!empty($match[$i][2])) $month = strtolower($match[$i][2]);
					else $month = "";
					if (!empty($match[$i][3])) $year = $match[$i][3];
					else $year = "";
					$datelink = "<a href=\"calendar.php?";
					if (isset($day) && strlen($day) > 0) $datelink .= "day=".$day."&amp;";
					if (isset($month) && strlen($month) > 0) $datelink .= "month=".$month."&amp;";
					if (isset($year) && strlen($year) > 0) $datelink .= "year=".$year."&amp;";
					$datelink .= "filterof=all&amp;action=";
					if (!empty($day)) $datelink .= "today\">";
					else if (empty($day) && !empty($year)) $datelink .= "year\">";
					else $datelink .= "\">";
					$datelink .= get_changed_date($datestr)."</a>";
				}
			}
		}
		if (!isset($datelink)) $datelink="";
		return $datelink;
	}
	else {
		$datelink = get_changed_date($datestr);
		return $datelink;
	}
}

/**
 * get an individuals age at the given date
 *
 * get an individuals age at the given date
 * @param string $indirec the individual record so that we can get the birth date
 * @param string $datestr the date string (everything after DATE) to calculate the age for
 * @return string the age in an HTML string
 */
function get_age($indirec,$datestr) {
	global $pgv_lang,$monthtonum;
	$estimates = array("abt","aft","bef","est","cir");
	$realbirthdt="";
	
	//-- get birth date for age calculations
	$bpos1 = strpos($indirec, "1 BIRT");
	if ($bpos1) {
		$index = 1;
		$birthrec = get_sub_record(1, "1 BIRT", $indirec, $index);
		while(!empty($birthrec)) {
			$hct = preg_match("/2 DATE.*(@#DHEBREW@)/", $birthrec, $match);
			if ($hct>0) {
				$dct = preg_match("/2 DATE.*(\d\d\d\d)/", $birthrec, $match);
				if ($dct>0) $hebrew_birthyear = trim($match[1]);
				$dct = preg_match("/2 DATE.*(...) \d\d\d\d/", $birthrec, $match);
				if ($dct>0) {
					if (isset($monthtonum[strtolower(trim($match[1]))])) {
						$hebrew_birthmonth = strtolower(trim($match[1]));
					}
				}
				$dct = preg_match("/2 DATE.*(\d+) ... \d\d\d\d/", $birthrec, $match);
				if ($dct>0) {
					$hebrew_birthdate = trim($match[1]);
				}
			}
			else {
				$dct = preg_match("/2 DATE.*(\d\d\d\d)/", $birthrec, $match);
				if ($dct>0) $birthyear = trim($match[1]);
				$dct = preg_match("/2 DATE.*([A-Za-z]{3}) \d\d\d\d/", $birthrec, $match);
				if ($dct>0) {
					if (isset($monthtonum[strtolower(trim($match[1]))])) {
						$birthmonth = $monthtonum[strtolower(trim($match[1]))];
					}
				}
				$dct = preg_match("/2 DATE[^\d]*(\d+) [A-Za-z]{3} \d\d\d\d/", $birthrec, $match);
				if ($dct>0) {
					$birthdate = trim($match[1]);
				}
			}
			$index++;
			$birthrec = get_sub_record(1, "1 BIRT", $indirec, $index);
		}
	}
	
	//-- check if it is a hebrew date
	$hct = preg_match("/@#DHEBREW@/", $datestr, $match);
	if ($hct>0) {
		if (isset($hebrew_birthyear)) $birthyear = $hebrew_birthyear;
		if (isset($hebrew_birthmonth)) $birthmonth = $hebrew_birthmonth;
		if (isset($hebrew_birthdate)) $birthdate = $hebrew_birthdate;
	}
	if ((strtoupper(trim($datestr))!="UNKNOWN")&&(isset($birthyear))) {
		$bt = preg_match("/(\d\d\d\d).*(\d\d\d\d)/", $datestr, $bmatch);
		if ($bt>0) {
			$age1 = $bmatch[1]-$birthyear;
			$age2 = $bmatch[2]-$birthyear;
			$realbirthdt = " <span class=\"age\">(".$pgv_lang["age"]." ".$pgv_lang["apx"]." ".convert_number($age1)."-".convert_number($age2).")</span>";
		}
		else {
			$bt = preg_match("/(\d\d\d\d)/", $datestr, $bmatch);
			if ($bt>0) {
				$age = $bmatch[1]-$birthyear;
				if (isset($birthmonth))	{
					$bt = preg_match("/(...) \d\d\d\d/", $datestr, $bmatch);
					if ($bt>0) {
						if (isset($monthtonum[strtolower(trim($bmatch[1]))])) {
							$emonth = $monthtonum[strtolower(trim($bmatch[1]))];
							if ($emonth<$birthmonth) $age--;
							else if (($emonth==$birthmonth)&&(isset($birthdate))) {
								$bt = preg_match("/(\d+) ... \d\d\d\d/", $datestr, $bmatch);
								if ($bt>0) {
									$edate = trim($bmatch[1]);
									if ($edate<$birthdate) $age--;
								}
							}
						}
					}
				}
				$realbirthdt = " <span class=\"age\">(".$pgv_lang["age"];
				$at= preg_match("/([a-zA-Z]{3})\.?/", $datestr, $amatch);
				if ($at>0) {
					if (in_array(strtolower($amatch[1]), $estimates)) {
						$realbirthdt = $realbirthdt." ".$pgv_lang["apx"];
					}
				}
				$realbirthdt = $realbirthdt." ".convert_number($age).")</span>";
			}
		}
	}
	return $realbirthdt;
}

/**
 * Get relationship between two individuals in the gedcom
 *
 * function to calculate the relationship between two people it uses hueristics based on the 
 * individuals birthdate to try and calculate the shortest path between the two individuals
 * it uses a node cache to help speed up calculations when using relationship privacy
 * this cache is indexed using the string "$pid1-$pid2"
 * @param string $pid1 the ID of the first person to compute the relationship from
 * @param string $pid2 the ID of the second person to compute the relatiohip to
 * @param bool $followspouse whether to add spouses to the path
 * @param int $maxlenght the maximim length of path
 * @param bool $ignore_cache enable or disable the relationship cache
 * @param int $path_to_find which path in the relationship to find, 0 is the shortest path, 1 is the next shortest path, etc
 */
function get_relationship($pid1, $pid2, $followspouse=true, $maxlength=0, $ignore_cache=false, $path_to_find=0) {
	global $TIME_LIMIT, $start_time, $pgv_lang, $NODE_CACHE, $NODE_CACHE_LENGTH, $USE_RELATIONSHIP_PRIVACY;

	$pid1 = strtoupper($pid1);
	$pid2 = strtoupper($pid2);
	$indirec = find_person_record($pid2);
	//-- check the cache
	if ($USE_RELATIONSHIP_PRIVACY && !$ignore_cache) {
		if(isset($NODE_CACHE["$pid1-$pid2"])) {
			if ($NODE_CACHE["$pid1-$pid2"]=="NOT FOUND") return false;
			if (($maxlength==0)||(count($NODE_CACHE["$pid1-$pid2"]["path"])<=$maxlength)) return $NODE_CACHE["$pid1-$pid2"];
			else return false;
		}
		//-- check the cache for person 2's children
		$famids = array();
		$ct = preg_match_all("/1\sFAMS\s@(.*)@/", $indirec, $match, PREG_SET_ORDER);
		for($i=0; $i<$ct; $i++) {
			$famids[$i]=$match[$i][1];
		}
		foreach($famids as $fam) {
			$famrec = find_family_record($fam);
			$ct = preg_match_all("/1 CHIL @(.*)@/", $famrec, $match, PREG_SET_ORDER);
			for($i=0; $i<$ct; $i++) {
				$child = $match[$i][1];
				if (!empty($child)){
					if(isset($NODE_CACHE["$pid1-$child"])) {
						if (($maxlength==0)||(count($NODE_CACHE["$pid1-$child"]["path"])+1<=$maxlength)) {
							$node1 = $NODE_CACHE["$pid1-$child"];
							$node1["path"][] = $pid2;
							$node1["pid"] = $pid2;
							$ct = preg_match("/1 SEX F/", $indirec, $match);
							if ($ct>0) $node1["relations"][] = "mother";
							else $node1["relations"][] = "father";
							$NODE_CACHE["$pid1-$pid2"] = $node1;
							return $node1;
						}
						else return false;
					}
				}
			}
		}
		
		if ((!empty($NODE_CACHE_LENGTH))&&($maxlength>0)) {
			if ($NODE_CACHE_LENGTH>=$maxlength) return false;
		}
	}
	//-- end cache checking

	//-- get the birth year of p2 for calculating heuristics
	$birthrec = get_sub_record(1, "1 BIRT", $indirec);
	$byear2 = -1;
	if ($birthrec!==false) {
		$dct = preg_match("/2 DATE .*(\d\d\d\d)/", $birthrec, $match);
		if ($dct>0) $byear2 = $match[1];
	}
	if ($byear2==-1) {
		$numfams = preg_match_all("/1\s*FAMS\s*@(.*)@/", $indirec, $fmatch, PREG_SET_ORDER);
		for($j=0; $j<$numfams; $j++) {
			// Get the family record
			$famrec = find_family_record($fmatch[$j][1]);

			// Get the set of children
			$ct = preg_match_all("/1 CHIL @(.*)@/", $famrec, $cmatch, PREG_SET_ORDER);
			for($i=0; $i<$ct; $i++) {
				// Get each child's record
				$childrec = find_person_record($cmatch[$i][1]);
				$birthrec = get_sub_record(1, "1 BIRT", $childrec);
				if ($birthrec!==false) {
					$dct = preg_match("/2 DATE .*(\d\d\d\d)/", $birthrec, $bmatch);
					if ($dct>0) $byear2 = $bmatch[1]-25;
				}
			}
		}
	}
	//-- end of approximating birth year

	//-- current path nodes
	$p1nodes = array();
	//-- ids visited
	$visited = array();

	//-- set up first node for person1
	$node1 = array();
	$node1["path"] = array();
	$node1["path"][] = $pid1;
	$node1["length"] = 0;
	$node1["pid"] = $pid1;
	$node1["relations"] = array();
	$node1["relations"][] = "self";
	$p1nodes[] = $node1;

	$visited[$pid1] = true;

	$found = false;
	$count=0;
	while(!$found) {
		//-- the following 2 lines ensure that the user can abort a long relationship calculation
		//-- refer to http://www.php.net/manual/en/features.connection-handling.php for more
		//-- information about why these lines are included
		if (headers_sent()) {
			print " ";
			if ($count%100 == 0) flush();
		}
		$count++;
		$end_time = getmicrotime();
		$exectime = $end_time - $start_time;
		if (($TIME_LIMIT>1)&&($exectime > $TIME_LIMIT-1)) {
			print "<span class=\"error\">".$pgv_lang["timeout_error"]."</span>\n";
			return false;
		}
		if (count($p1nodes)==0) {
			if ($maxlength!=0) {
				if (!isset($NODE_CACHE_LENGTH)) $NODE_CACHE_LENGTH = $maxlength;
				else if ($NODE_CACHE_LENGTH<$maxlength) $NODE_CACHE_LENGTH = $maxlength;
			}
			if (headers_sent()) {
				print "\n<!-- Relationship $pid1-$pid2 NOT FOUND | Visited ".count($visited)." nodes | Required $count iterations.<br />\n";
				print_execution_stats();
				print "-->\n";
			}
			$NODE_CACHE["$pid1-$pid2"] = "NOT FOUND";
			return false;
		}
		//-- search the node list for the shortest path length
		$shortest = -1;
		foreach($p1nodes as $index=>$node) {
			if ($shortest == -1) $shortest = $index;
			else {
				$node1 = $p1nodes[$shortest];
				if ($node1["length"] > $node["length"]) $shortest = $index;
			}		
		}
		if ($shortest==-1) return false;
		$node = $p1nodes[$shortest];
		if (($maxlength==0)||(count($node["path"])<=$maxlength)) {
			if ($node["pid"]==$pid2) {
			}
			else {
				//-- hueristic values
				$fatherh = 1;
				$motherh = 1;
				$siblingh = 2;
				$spouseh = 2;
				$childh = 3;

				//-- generate heuristic values based of the birthdates of the current node and p2
				$indirec = find_person_record($node["pid"]);
				$byear1 = -1;
				$birthrec = get_sub_record(1, "1 BIRT", $indirec);
				if ($birthrec!==false) {
					$dct = preg_match("/2 DATE .*(\d\d\d\d)/", $birthrec, $match);
					if ($dct>0) $byear1 = $match[1];
				}
				if (($byear1!=-1)&&($byear2!=-1)) {
					$yeardiff = $byear1-$byear2;
					if ($yeardiff < -140) {
						$fatherh = 20;
						$motherh = 20;
						$siblingh = 15;
						$spouseh = 15;
						$childh = 1;
					}
					else if ($yeardiff < -100) {
						$fatherh = 15;
						$motherh = 15;
						$siblingh = 10;
						$spouseh = 10;
						$childh = 1;
					}
					else if ($yeardiff < -60) {
						$fatherh = 10;
						$motherh = 10;
						$siblingh = 5;
						$spouseh = 5;
						$childh = 1;
					}
					else if ($yeardiff < -20) {
						$fatherh = 5;
						$motherh = 5;
						$siblingh = 3;
						$spouseh = 3;
						$childh = 1;
					}
					else if ($yeardiff<20) {
						$fatherh = 3;
						$motherh = 3;
						$siblingh = 1;
						$spouseh = 1;
						$childh = 5;
					}
					else if ($yeardiff<60) {
						$fatherh = 1;
						$motherh = 1;
						$siblingh = 5;
						$spouseh = 2;
						$childh = 10;
					}
					else if ($yeardiff<100) {
						$fatherh = 1;
						$motherh = 1;
						$siblingh = 10;
						$spouseh = 3;
						$childh = 15;
					}
					else {
						$fatherh = 1;
						$motherh = 1;
						$siblingh = 15;
						$spouseh = 4;
						$childh = 20;
					}
				}
				//-- check all parents and siblings of this node
				$famids = array();
				$ct = preg_match_all("/1\sFAMC\s@(.*)@/", $indirec, $match, PREG_SET_ORDER);
				for($i=0; $i<$ct; $i++) {
					if (!isset($visited[$match[$i][1]])) $famids[$i]=$match[$i][1];
				}
				foreach($famids as $fam) {
					$visited[$fam] = true;
					$famrec = find_family_record($fam);
					$parents = find_parents_in_record($famrec);
					if ((!empty($parents["HUSB"]))&&(!isset($visited[$parents["HUSB"]]))) {
						$node1 = $node;
						$node1["length"]+=$fatherh;
						$node1["path"][] = $parents["HUSB"];
						$node1["pid"] = $parents["HUSB"];
						$node1["relations"][] = "father";
						$p1nodes[] = $node1;
						if ($node1["pid"]==$pid2) {
							if ($path_to_find>0) $path_to_find--;
							else {
								$found=true;
								$node = $node1;
							}
						}
						else $visited[$parents["HUSB"]] = true;
						if ($USE_RELATIONSHIP_PRIVACY) {
							$NODE_CACHE["$pid1-".$node1["pid"]] = $node;
						}
					}
					if ((!empty($parents["WIFE"]))&&(!isset($visited[$parents["WIFE"]]))) {
						$node1 = $node;
						$node1["length"]+=$motherh;
						$node1["path"][] = $parents["WIFE"];
						$node1["pid"] = $parents["WIFE"];
						$node1["relations"][] = "mother";
						$p1nodes[] = $node1;
						if ($node1["pid"]==$pid2) {
							if ($path_to_find>0) $path_to_find--;
							else {
								$found=true;
								$node = $node1;
							}
						}
						else $visited[$parents["WIFE"]] = true;
						if ($USE_RELATIONSHIP_PRIVACY) {
							$NODE_CACHE["$pid1-".$node1["pid"]] = $node;
						}
					}
					$ct = preg_match_all("/1 CHIL @(.*)@/", $famrec, $match, PREG_SET_ORDER);
					for($i=0; $i<$ct; $i++) {
						$child = $match[$i][1];
						if ((!empty($child))&&(!isset($visited[$child]))) {
							$node1 = $node;
							$node1["length"]+=$siblingh;
							$node1["path"][] = $child;
							$node1["pid"] = $child;
							$node1["relations"][] = "sibling";
							$p1nodes[] = $node1;
							if ($node1["pid"]==$pid2) {
								if ($path_to_find>0) $path_to_find--;
								else {
									$found=true;
									$node = $node1;
								}
							}
							else $visited[$child] = true;
							if ($USE_RELATIONSHIP_PRIVACY) {
								$NODE_CACHE["$pid1-".$node1["pid"]] = $node;
							}
						}
					}
				}
				//-- check all spouses and children of this node
				$famids = array();
				$ct = preg_match_all("/1\sFAMS\s@(.*)@/", $indirec, $match, PREG_SET_ORDER);
				for($i=0; $i<$ct; $i++) {
//					if (!isset($visited[$match[$i][1]])) $famids[$i]=$match[$i][1];
					$famids[$i]=$match[$i][1];
				}
				foreach($famids as $fam) {
					$visited[$fam] = true;
					$famrec = find_family_record($fam);
					if ($followspouse) {
						$parents = find_parents_in_record($famrec);
						if ((!empty($parents["HUSB"]))&&(!isset($visited[$parents["HUSB"]]))) {
							$node1 = $node;
							$node1["length"]+=$spouseh;
							$node1["path"][] = $parents["HUSB"];
							$node1["pid"] = $parents["HUSB"];
							$node1["relations"][] = "spouse";
							$p1nodes[] = $node1;
							if ($node1["pid"]==$pid2) {
								if ($path_to_find>0) $path_to_find--;
								else {
									$found=true;
									$node = $node1;
								}
							}
							else $visited[$parents["HUSB"]] = true;
							if ($USE_RELATIONSHIP_PRIVACY) {
								$NODE_CACHE["$pid1-".$node1["pid"]] = $node;
							}
						}
						if ((!empty($parents["WIFE"]))&&(!isset($visited[$parents["WIFE"]]))) {
							$node1 = $node;
							$node1["length"]+=$spouseh;
							$node1["path"][] = $parents["WIFE"];
							$node1["pid"] = $parents["WIFE"];
							$node1["relations"][] = "spouse";
							$p1nodes[] = $node1;
							if ($node1["pid"]==$pid2) {
								if ($path_to_find>0) $path_to_find--;
								else {
									$found=true;
									$node = $node1;
								}
							}
							else $visited[$parents["WIFE"]] = true;
							if ($USE_RELATIONSHIP_PRIVACY) {
								$NODE_CACHE["$pid1-".$node1["pid"]] = $node;
							}
						}
					}
					$ct = preg_match_all("/1 CHIL @(.*)@/", $famrec, $match, PREG_SET_ORDER);
					for($i=0; $i<$ct; $i++) {
						$child = $match[$i][1];
						if ((!empty($child))&&(!isset($visited[$child]))) {
							$node1 = $node;
							$node1["length"]+=$childh;
							$node1["path"][] = $child;
							$node1["pid"] = $child;
							$node1["relations"][] = "child";
							$p1nodes[] = $node1;
							if ($node1["pid"]==$pid2) {
								if ($path_to_find>0) $path_to_find--;
								else {
									$found=true;
									$node = $node1;
								}
							}
							else $visited[$child] = true;
							if ($USE_RELATIONSHIP_PRIVACY) {
								$NODE_CACHE["$pid1-".$node1["pid"]] = $node;
							}
						}
					}
				}
			}
		}
		unset($p1nodes[$shortest]);
	} //-- end while loop
	if (headers_sent()) {
		print "\n<!-- Relationship $pid1-$pid2 | Visited ".count($visited)." nodes | Required $count iterations.<br />\n";
		print_execution_stats();
		print "-->\n";
	}
	return $node;
}

/**
 * User Name comparison Function
 *
 * This function just needs to call the itemsort function on the fullname
 * field of the array
 * @param array $a first user array
 * @param array $b second user array
 * @return int negative numbers sort $a first, positive sort $b first
 */
function usersort($a, $b) {
	return itemsort($a["fullname"], $b["fullname"]);
}

/**
 * sort arrays or strings
 *
 * this function is called by the uasort PHP function to compare two items and tell which should be
 * sorted first.  It uses the language alphabets to create a string that will is used to compare the
 * strings.  For each letter in the strings, the letter's position in the alphabet string is found.
 * Whichever letter comes first in the alphabet string should be sorted first.
 * @param array $a first item
 * @param array $b second item
 * @return int negative numbers sort $a first, positive sort $b first
 */
function itemsort($a, $b) {
	global $ALPHABET_upper, $ALPHABET_lower, $LANGUAGE, $alphabet, $CHARACTER_SET;

	$alphabet = getAlphabet();

	//-- get rid of jr. Jr. Sr. sr. II, III and van, van der, de lowercase surname prefixes
	//-- a . and space must be behind a-z to ensure shortened prefixes and multiple prefixes are removed

	if (is_array($a) && is_array($b)){
		$aname = preg_replace(array("/ [jJsS][rR]\.?,/", "/ I+,/", "/^[a-z. ]+/"), array(",",",",""), $a["name"]);
		$bname = preg_replace(array("/ [jJsS][rR]\.?,/", "/ I+,/", "/^[a-z. ]+/"), array(",",",",""), $b["name"]);
	}
	else {
		$aname = $a;	//-- get the first letter
		$bname = $b;
	}
	//-- get the name lengths
	$alen = strlen($aname);
	$blen = strlen($bname);

	//-- loop through the characters in the string and if we find one that is different between the strings
	//-- return the difference
	$danishex = array("OE", "AE", "AA");
	for($i=0; ($i<$alen)&&($i<$blen); $i++) {
		if ($LANGUAGE == "hungarian" && $i==0){
			$aletter = substr($aname, $i, 3);
			if ($aletter <> "Dzs");
			else $aletter = substr($aname, $i, 2);
			if ($aletter == "Cs" || $aletter == "Dz" || $aletter == "Gy" || $aletter == "Ly" || $aletter == "Ny" || $aletter == "Sz" || $aletter == "Ty" || $aletter == "Zs");
			else $aletter = $aname{$i};
			
			$bletter = substr($bname, $i, 3);
			if ($bletter == "Dzs");
			else $bletter = substr($bname, $i, 2);
			if ($bletter == "Cs" || $bletter == "Dz" || $bletter == "Gy" || $bletter == "Ly" || $bletter == "Ny" || $bletter == "Sz" || $bletter == "Ty" || $bletter == "Zs");
			else $bletter = $bname{$i};
		}
		else if (($LANGUAGE == "danish" || $LANGUAGE == "norwegian")){
			$aletter = substr($aname, $i, 2);
			if (in_array(strtoupper($aletter), $danishex)) {
				if (strtoupper($aletter) == "AA") {
					if ($aletter == "aa") $aname=substr_replace($aname, "å", $i, 2);
					else $aname=substr_replace($aname, "Å", $i, 2);
				}
				else if (strtoupper($aletter) == "OE") {
					if ($i==0 || $aletter=="Oe") $aname=substr_replace($aname, "Ø", $i, 2);
				}
				else if (strtoupper($aletter) == "AE") {
					if ($aletter == "ae") $aname=substr_replace($aname, "æ", $i, 2);
					else $aname=substr_replace($aname, "Æ", $i, 2);
				}
			}
			$aletter = substr($aname, $i, 1);
			
			$bletter = substr($bname, $i, 2);
			if (in_array(strtoupper($bletter), $danishex)) {
				if (strtoupper($bletter) == "AA") {
					if ($bletter == "aa") $bname=substr_replace($bname, "å", $i, 2);
					else $bname=substr_replace($bname, "Å", $i, 2);
				}
				else if (strtoupper($bletter) == "OE") {
					if ($i==0 || $bletter=="Oe") $bname=substr_replace($bname, "Ø", $i, 2);
				}
				else if (strtoupper($bletter) == "AE") {
					if ($bletter == "ae") $bname=substr_replace($bname, "æ", $i, 2);
					else $bname=substr_replace($bname, "Æ", $i, 2);
				}
			}
			$bletter = substr($bname, $i, 1);
		}
		else {
			$aletter = substr($aname, $i, 1);
			$bletter = substr($bname, $i, 1);
		}
		if ($CHARACTER_SET=="UTF-8") {
			$ord = ord($aletter);
			if ($ord==92 || $ord==195 || $ord==196 || $ord==197 || $ord==206 || $ord==207 || $ord==208 || $ord==209 || $ord==214 || $ord==215 || $ord==216 || $ord==217 || $ord==218 || $ord==219){
				$aletter = stripslashes(substr($aname, $i, 2));
			}
			else if ($ord==228 || $ord==229 || $ord == 230 || $ord==232 || $ord==233){
				$aletter = substr($aname, $i, 3);
			}
			else if (strlen($aletter) == 1) $aletter = strtoupper($aletter);
			
			$ord = ord($bletter);
			if ($ord==92 || $ord==195 || $ord==196 || $ord==197 || $ord==206 || $ord==207 || $ord==208 || $ord==209 || $ord==214 || $ord==215 || $ord==216 || $ord==217 || $ord==218 || $ord==219){
				$bletter = stripslashes(substr($bname, $i, 2));
			}
			else if ($ord==228 || $ord==229 || $ord == 230 || $ord==232 || $ord==233){
				$bletter = substr($bname, $i, 3);
			}
			else if (strlen($bletter) == 1) $bletter = strtoupper($bletter);
		}

		if ($aletter!=$bletter) {
			//-- get the position of the letter in the alphabet string
			$apos = strpos($alphabet, $aletter);
			$bpos = strpos($alphabet, $bletter);

			if (($bpos!==false)&&($apos===false)) return -1;
			if (($bpos===false)&&($apos!==false)) return 1;
			if (($bpos===false)&&($apos===false)) return ord($aletter)-ord($bletter);
			if ($apos!=$bpos) return ($apos-$bpos);
		}
	}

	//-- if we made it through the loop then check if one name is longer than the
	//-- other, the shorter one should be first
	if ($alen!=$blen) return ($alen-$blen);

	//-- the strings are exactly the same so return 0
	return 0;
}

//-- comparison function for usort
//-- used for index mode
function lettersort($a, $b) {

	return itemsort($a["letter"], $b["letter"]);
}

/**
 * find record in file
 *
 * this function finds a gedcom record in the gedcom file by searching through the file 4Kb at a 
 * time
 * @param string $gid the gedcom xref id of the record to find
 */
function find_record_in_file($gid) {
	global $GEDCOMS, $GEDCOM, $indilist;

	$fpged = fopen($GEDCOMS[$GEDCOM]["path"], "r");
	if (!$fpged) return false;
	$BLOCK_SIZE = 1024*4;	//-- 4k bytes per read
	$fcontents = "";
	while(!feof($fpged)) {
		$fcontents = fread($fpged, $BLOCK_SIZE);
		$pos1 = strpos($fcontents, "0 @$gid@", 0);
		if ($pos1===false) $fcontents = "";
		else {
			$pos2 = strpos($fcontents, "\n0", $pos1+1);
			while((!$pos2)&&(!feof($fpged))) {
				$fcontents .= fread($fpged, $BLOCK_SIZE);
				$pos2 = strpos($fcontents, "\n0", $pos1+1);
			}
			if ($pos2) $indirec = substr($fcontents, $pos1, $pos2-$pos1);
			else $indirec = substr($fcontents, $pos1);
			fclose($fpged);
			$ct = preg_match("/0 @.+@ (.+)/", $indirec, $match);
			if ($ct>0) {
				$type = trim($match[1]);
				//-- add record to indilist for caching
				if ($type=="INDI") {
					$indilist[$gid]["gedcom"]=$indirec;
					$indilist[$gid]["name"]=get_name_in_record($indirec);
					$indilist[$gid]["file"]=$GEDCOM;
					$indilist[$gid]["isdead"] = -1;
				}
			}
			return $indirec;
			break;
		}
	}
	fclose($fpged);
// 	Removed line below because if no valid root-id is in the file and user has no person ID
//  then the error that the document has no data will be displayed.
// 	Remove for 3.2 final. NOT sooner due to testing required.
// 	return @find_person_record($gid);
	return false;
}

/**
 * write changes
 *
 * this function writes the $pgv_changes back to the <var>$INDEX_DIRECTORY</var>/pgv_changes.php
 * file so that it can be read in and checked to see if records have been updated.  It also stores
 * old records so that they can be undone.
 * @return bool true if successful false if there was an error
 */
function write_changes() {
	global $GEDCOMS, $GEDCOM, $pgv_changes, $INDEX_DIRECTORY, $CONTACT_EMAIL, $PGV_DATABASE;

	//-- write the changes file
	$changestext = "<?php\n\$pgv_changes = array();\n";
	foreach($pgv_changes as $gid=>$changes) {
		if (count($changes)>0) {
			$changestext .= "\$pgv_changes[\"$gid\"] = array();\n";
			foreach($changes as $change) {
				$changestext .= "// Start of change record.\n";
				$changestext .= "\$change = array();\n";
				$changestext .= "\$change[\"gid\"] = '".$change["gid"]."';\n";
				$changestext .= "\$change[\"gedcom\"] = '".$change["gedcom"]."';\n";
				$changestext .= "\$change[\"type\"] = '".$change["type"]."';\n";
				$changestext .= "\$change[\"status\"] = '".$change["status"]."';\n";
				$changestext .= "\$change[\"user\"] = '".$change["user"]."';\n";
				$changestext .= "\$change[\"time\"] = '".$change["time"]."';\n";
				$changestext .= "\$change[\"undo\"] = '".str_replace("\\\\'", "\\'", preg_replace("/'/", "\\'", $change["undo"]))."';\n";
				$changestext .= "// End of change record.\n";
				$changestext .= "\$pgv_changes[\"$gid\"][] = \$change;\n";
			}
		}
	}
	$changestext .= "\n?>\n";
	$fp = fopen($INDEX_DIRECTORY."pgv_changes.php", "wb");
	if ($fp===false) {
		print "ERROR 6: Unable to open changes file resource.  Unable to complete request.\n";
		return false;
	}
	$fw = fwrite($fp, $changestext);
	if ($fw===false) {
		print "ERROR 7: Unable to write to changes file.\n";
		fclose($fp);
		return false;
	}
	fclose($fp);
	return true;
}

/**
 * get theme names
 *
 * function to get the names of all of the themes as an array
 * it searches the themes directory and reads the name from the theme_name variable
 * in the theme.php file.
 * @return array and array of theme names and their corresponding directory
 */
function get_theme_names() {
	$themes = array();
	$d = dir("themes");
	while (false !== ($entry = $d->read())) {
		if ($entry!="." && $entry!=".." && $entry!="CVS" && is_dir("themes/$entry")) {
			$theme = array();
			$themefile = implode("", file("themes/$entry/theme.php"));
			$tt = preg_match("/theme_name\s+=\s+\"(.*)\";/", $themefile, $match);
			if ($tt>0) $themename = trim($match[1]);
			else $themename = "themes/$entry";
			$theme["name"] = $themename;
			$theme["dir"] = "themes/$entry/";
			$themes[] = $theme;
		}
	}
	$d->close();
	uasort($themes, "itemsort");
	return $themes;
}

/**
 * format a fact for calendar viewing
 *
 * @param string $factrec the fact record
 * @param string $action tells what type calendar the user is viewing
 * @param string $filter should the fact be filtered by living people etc
 * @param string $pid the gedcom xref id of the record this fact belongs to
 * @return string a html text string that can be printed
 */
function get_calendar_fact($factrec, $action, $filterof, $pid, $filterev="all") {
	global $pgv_lang, $factarray, $year, $month, $day, $TEMPLE_CODES, $CALENDAR_FORMAT, $monthtonum, $TEXT_DIRECTION, $SHOW_PEDIGREE_PLACES, $caltype;
    $skipfacts = array("CHAN", "BAPL", "SLGC", "SLGS", "ENDL");	
    $ft = preg_match("/1\s(_?.{3,4})\s(.*)/", $factrec, $match);
    if ($ft>0) $fact = $match[1];
	else return "filter";
	if (in_array($fact, $skipfacts)) return "filter";
	if ((!showFact($fact, $pid))||(!showFactDetails($fact, $pid))) return "";
	$fact = trim($fact);
	$factref = $fact;
	if ($fact=="EVEN" || $fact=="FACT") {
		$ct = preg_match("/2 TYPE (.*)/", $factrec, $tmatch);
		if ($ct>0) {
			$factref = trim($tmatch[1]);
		    if ((!showFact($factref, $pid))||(!showFactDetails($factref, $pid))) return "";
	    }    
	}	
	
	// Use given year for age in monthview
	if ($action == "calendar"){
		$yearnow = $year;
	}
	else	{
		$yearnow = getdate();
		$yearnow = $yearnow["year"];
	}
	$text = "";
	
	if ((!in_array($fact, $skipfacts) && !in_array($factref, $skipfacts)) || ($fact==$filterev) || ($factref==$filterev)) {	
		if ($fact=="EVEN" || $fact=="FACT") {
			if ($ct>0) {
				if (isset($factarray["$factref"])) $text .= $factarray["$factref"];
				else $text .= $factref;
			}
			else $text .= $factarray[$fact];
		}
		else {
			if (isset($factarray[$fact])) $text .= $factarray[$fact];
			else $text .= $fact;
		}
		if ($filterev!="all" && $filterev!=$fact && $filterev!=$factref) return "filter";
		
		if ($text!="") $text=PrintReady($text);   //----  MA
				
		$ct = preg_match("/\d DATE(.*)/", $factrec, $match);
		if ($ct>0) {
			$text .= " - <span class=\"date\">".get_date_url($match[1])."</span>";
//			$yt = preg_match("/ (\d\d\d\d)/", $match[1], $ymatch);
			$yt = preg_match("/ (\d\d\d\d|\d\d\d)/", $match[1], $ymatch);
			if ($yt>0) {
				$age = $yearnow - $ymatch[1];
				$yt2 = preg_match("/(...) (\d\d\d\d|\d\d\d)/", $match[1], $bmatch);
				if ($yt2>0) {
					if (isset($monthtonum[strtolower(trim($bmatch[1]))])) {
						$emonth = $monthtonum[strtolower(trim($bmatch[1]))];
						if ($emonth<$monthtonum[strtolower($month)]) $age--;
						$bt = preg_match("/(\d+) ... (\d\d\d\d|\d\d\d)/", $match[1], $bmatch);
						if ($bt>0) {
							$edate = trim($bmatch[1]);
							if ($edate<$day) $age--;
						}
					}
				}
				$yt3 = preg_match("/(.+) ... (\d\d\d\d|\d\d\d)/", $match[1], $bmatch);
				if ($yt3>0) {
					if ($bmatch[1]>$day) $age--;
				}
				if (($filterof=="recent")&&($age>100)) return "filter";
				// Limit facts to before the given year in monthview
				if (($age<0) && ($action == "calendar")) return "filter";
				if ($action!='year'){
					$text .= " (" . str_replace("#year_var#", convert_number($age), $pgv_lang["year_anniversary"]).")";
				}
 				if($TEXT_DIRECTION == "rtl"){
 					$text .= "&lrm;";
 				}
			}
			if (($action=='today')||($action=='year')) {
				// -- find place for each fact
			if ($SHOW_PEDIGREE_PLACES>0) {
				$ct = preg_match("/2 PLAC (.*)/", $factrec, $match);
				if ($ct>0) {
					$text .=($action=='today'?"<br />":" ");
					$plevels = preg_split("/,/", $match[1]);
					for($plevel=0; $plevel<$SHOW_PEDIGREE_PLACES; $plevel++) {
						if (!empty($plevels[$plevel])) {
							if ($plevel>0) $text .=", ";
//							$text .= trim($plevels[$plevel]);     //--- MA
							$text .= PrintReady($plevels[$plevel]);
						}
						}
					}
				}

				// -- find temple code for lds facts
				$ct = preg_match("/2 TEMP (.*)/", $factrec, $match);
				if ($ct>0) {
					$tcode = $match[1];
					$tcode = trim($tcode);
					if (array_key_exists($tcode, $TEMPLE_CODES)) $text .= "<br />".$pgv_lang["temple"].": ".$TEMPLE_CODES[$tcode];
					else $text .= "<br />".$pgv_lang["temple_code"].$tcode;
				}
			}
		}
		$text .= "<br />";
	}
	if ($text=="") return "filter";
	return $text;
}

//-- this function will convert a digit number to a number in a different language
function convert_number($num) {
	global $pgv_lang, $LANGUAGE;

	if ($LANGUAGE == "chinese") {
		$numstr = "$num";
		$zhnum = "";
		//-- currently limited to numbers <10000
		if (strlen($numstr)>4) return $numstr;

		$ln = strlen($numstr);
		$numstr = strrev($numstr);
		for($i=0; $i<$ln; $i++) {
			if (($i==1)&&($numstr{$i}!="0")) $zhnum = $pgv_lang["10"].$zhnum;
			if (($i==2)&&($numstr{$i}!="0")) $zhnum = $pgv_lang["100"].$zhnum;
			if (($i==3)&&($numstr{$i}!="0")) $zhnum = $pgv_lang["1000"].$zhnum;
			if (($i!=1)||($numstr{$i}!=1)) $zhnum = $pgv_lang[$numstr{$i}].$zhnum;
		}
		return $zhnum;
	}
	return $num;
}

//-- this function is a wrapper to the php mail() function so that we can change settings globally
// for more info on format="flowed" see: http://www.joeclark.org/ffaq.html
// for deatiled info on MIME (RFC 1521) email see: http://www.freesoft.org/CIE/RFC/1521/index.htm
function pgvMail($to, $subject, $message, $extraHeaders){
	global $pgv_lang, $CHARACTER_SET, $LANGUAGE, $PGV_STORE_MESSAGES, $TEXT_DIRECTION;
	$mailFormat = "plain";
	//$mailFormat = "html";
	//$mailFormat = "multipart"

	$mailFormatText = "text/plain";

	$boundry = "PGV-123454321-PGV"; //unique identifier for multipart
	$boundry2 = "PGV-123454321-PGV2";

	if($TEXT_DIRECTION == "rtl") { // needed for rtl but we can change this to a global config
		$mailFormat = "multipart";
	}

	if($mailFormat == "html"){
		$mailFormatText = "text/html";
	} else if($mailFormat == "multipart") {
		$mailFormatText = "multipart/related; \r\n\tboundary=\"$boundry\""; //for double display use:multipart/mixed
	} else {
		$mailFormatText = "text/plain";
	}

	$defaultExtraHeaders = "\r\nContent-type: " . $mailFormatText . ";\r\n";

	if($mailFormat != "multipart"){
		$defaultExtraHeaders .= "\tcharset=\"$CHARACTER_SET\";\r\n\tformat=\"flowed\"\r\nContent-Transfer-Encoding: 8bit\r\n";
	}

	if($mailFormat == "html" || $mailFormat == "multipart"){
		$defaultExtraHeaders .= "Mime-Version: 1.0\r\n";
	}

	$extraHeaders .= $defaultExtraHeaders; //add custom extra header


	if($mailFormat == "html") {
		//wrap message in html
		$htmlMessage = "";
		$htmlMessage .= "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">";
		$htmlMessage .= "<html xmlns=\"http://www.w3.org/1999/xhtml\">";
		$htmlMessage .= "<head>";
		$htmlMessage .= "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />";
		$htmlMessage .= "</head>";
		$htmlMessage .= "<body dir=\"$TEXT_DIRECTION\"><pre>";
		$htmlMessage .= $message; //add message
		$htmlMessage .= "</pre></body>";
		$htmlMessage .= "</html>";
		$message = $htmlMessage;
	} else if($mailFormat == "multipart"){
		//wrap message in html
		$htmlMessage = "--$boundry\r\n";
		$htmlMessage .= "Content-Type: multipart/alternative; \r\n\tboundry=--$boundry2\r\n\r\n";
		$htmlMessage = "--$boundry2\r\n";
		$htmlMessage .= "Content-Type: text/plain; \r\n\tcharset=\"$CHARACTER_SET\";\r\n\tformat=\"flowed\"\r\nContent-Transfer-Encoding: 8bit\r\n\r\n";
		$htmlMessage .= $message;
		$htmlMessage .= "\r\n\r\n--$boundry2\r\n";
		$htmlMessage .= "Content-Type: text/html; \r\n\tcharset=\"$CHARACTER_SET\";\r\n\tformat=\"flowed\"\r\nContent-Transfer-Encoding: 8bit\r\n\r\n";
		$htmlMessage .= "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">";
		$htmlMessage .= "<html xmlns=\"http://www.w3.org/1999/xhtml\">";
		$htmlMessage .= "<head>";
		$htmlMessage .= "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />";
		$htmlMessage .= "</head>";
		$htmlMessage .= "<body dir=\"$TEXT_DIRECTION\"><pre>";
		$htmlMessage .= $message; //add message
		$htmlMessage .= "</pre>";
		$htmlMessage .= "<img src=\"cid:pgvlogo@pgvserver\" style=\"border: 0px; display: block; margin-left: auto; margin-right: auto;\" />";
		$htmlMessage .= "</body>";
		$htmlMessage .= "</html>";
		$htmlMessage .= "\r\n--$boundry2--\r\n";
		$htmlMessage .= "\r\n--$boundry\r\n";
		$htmlMessage .= getPgvMailLogo();
		$htmlMessage .= "\r\n\r\n\r\n\r\n--$boundry--";
		$message = $htmlMessage;
	}
	mail($to, hex4email($subject,$CHARACTER_SET), $message, $extraHeaders);
}

function getPgvMailLogo(){
// the following is a base64 encoded PGV logo for use in html formatted email.
	$pgvLogo =
"Content-Type: image/gif;
	name=\"gedview.gif\"
Content-Transfer-Encoding: base64
Content-ID: <pgvlogo@pgvserver>
Content-Description: gedview.gif
Content-Location: gedview.gif

R0lGODlhZAAtAPcAAAMZLwUmRRIsRBo0SwctURU2UyY+UxE8ZTJHWx5GagxFehVKeyZIaC1NazdT
bSVTfT5Zc0dYaFNjc25ubmBtemRyf3V1dXt7extRgx9SgShbijdegjRkkD1rlERmhk1vj1NqgFl0
jkJwml99mWl6iUxhrFZpr0t2oFF7oVR+pV1utGx6q2N1vG18wHmJmFeApmaMr2uLqHWArHaSq32I
sHuatn+gvXiFwoCAgIODg4WEhYaEhYmGh4eIiImJiYyMjIKPmpGRkZeXl5qbm4aNroiXpJGdqYiP
tImfsouUuJacu5WhrJmksp2os5Knup+lvpWpvKChoaenp6Onraqrq6Cst6ast6qyu7Gys7O2urW4
vLu8vL6/v4ONx42WzJae05asxp2yxp+2zJyj1KWsw6W3x6yywq+2wa2zx6u4xKK6z6q6y7S6wbi9
w7K2yLS7ybm8y6Sr2Kq/06qx3Lu/0ru/3bC34rzAxbXBzb3Cy7LF1rnF0L3J1bTH2bfJ2rzM27/P
38LCw8LFysTGz8fIysvMzcXL0szO0sLO2cTS38jR2c3X39DR09PW2dTY3dnZ2t3e38HE48jL58HR
4MbU4cnW48rX5MvZ5szY5M3Z5c7Z5c3a5s/a5s/b58zQ7sra6Mzb6c7d6tLa4dfa4NPc4NTc4dDa
5NDc59Td5dTV7NLd6NPe6dDe69rc79TX8dbg5t7g4tbg6tPg7Nbh7dji69zi6N/l6tnj7Nvk7Nvk
7dvm793m7d3l7t/n7t/o79/h893o8uLk5eTm6eDn7ebo6eHo7+Tq7ebr7ejp6evs7OLp8OPr8uTr
8ebs8uDq9OLt9ubu9err8+nt8Ovv8+3u8O7v8Onu9O3t9uLk+Orr+uvw9Ovx9uzx9ezy9u7y9u/z
9+3z+e/1+vHy8vDy9PDz9fL09vX29vH0+PL1+PL2+fH2+vP3+/T3+fDw/PH2/PT0/PX4+vb5+/P5
/Pj5+vv7+/n6/Pz7/Pr8/fv+//z8/Pz9/v7+/v7+//7//////wAAACH5BAkAAP8ALAAAAABkAC0A
AAj+AP8JFLglCA8dOxIqVMjDh8OFECNKnEjxx4+FPi5S3KhQB48gWwaKhBXokTl/KFOq9GdOnLiT
K2PKnEkTpT5kyVSKC0avps+U5hoFEvnvUaCe/vYpXcrUHxYcOXJEQZYUpVKr+5Jm1Yqy39WUX7Ve
TXZBCtcoOMRxXbuVqdulKPMNHchFn9e3+/Tpy+uPigUuVCYMSVqtmj6W87zW29dvnrl9/KpRy+dP
n7l54pL1NJfsJGN94h5ZoJL0sBAcwcTp43eZH7965vRVQ6Z0b17bS/X20zeXS069wIMH73shWb6M
1aTgwDGk3JYdyMQFISbuB5V8VJYLQWZOiI8cO6j+MeqBYwchf9XQ4hhd+fAQC8ujmKPyo5q/KEKS
BbEQrL3w/+gNJUg/9xRo4IEH9mVBIFhMQEUWE3DBxQRYPBJhIRNswcgEhWwRISMWDFGNDgs+AkuI
sAhhATFSRCgIewX6I8QFh3iIxSETEEKNBVL0Q8UQ1BCI4JAxblHII/4USI+B9DS5ZJP3KHgBDlP9
4EM99WRUzg9BCDFBlznod0EUUlyQQzA6CIHShEJQEUSGPQThDzIXkBajEDmYQw9yaXqIpGv3OPkk
lAc26Q8hXBziD5aMNuqogo+Ms889F80zzw4/+OPhBVrg4CA/P1zABSGBMJJMDmpq6iCpgsCig5z/
yLCHpYx5mvOqUxZcEMQ8+jh2j6PANnpoIIrWM4+xwRqbmBQWCLNoPxBSEVgW/gRjwQ/1RDFBI06t
OkQy4+AQRD/8QHJtIVIEcp+DDfaIZT8qctHiFv5AckGELA3RgzD4HIulv/8CPOwh/cRj8MEII6wP
F0IQc4/B6GDhEBboYBmFuowMQc49Ef/Qw3bkRIEFPvHcc0gQPfxASD/JRBGnEFyQHA8+blqkBTv3
4BPEBfySg2cwDycstMH+CBJIIfiwA4/SSzPNNDxLwxMP1EuXXM7G8bATDzpUo5P1zOWMY7DWVMOT
9DjsEIiPPjivXaDS5cBTTjn41FMNIWPeo/U4/tVkzc7fZVOt9N/9GI3034gnrvjgi4+NeNNSJ25w
04wrjSU6joyiueaOONLIKFFLnfU9j4wpjN+OL646O/gYfk866aAD++y012577LfjLrvtsu8ujTDH
PNJGKp5IIkkkkdRRBx3w2M7ON8dAnPv0s+MTyNH3nKP99tx37/334KMj/vjkpyPNFocwEggc2LhC
fPHIR5LO9+isgw74+Hd/z/WExPPN/wAMoAAHSMACTkMaCEwgAqeRDmEIIh7ykMYZ6AAHOLzBDW5A
AxnekI4CevCDAqwH/+ThjRKa8IQoTKEKT6gOdaxDGqU6xCEMMYgaDoINx5CGIM5RQmEEIxi2/gCi
EI3xjRUa8YgolMcdAkEIeXDjiVCMohSnSEUoasNzjiBEIe7RjnZkAxvY+MUv4CCIGn2DG904oAIT
qI0quvGNU1QiE9WhjTra8Y52nEYd9ajHO/bRjt2whRZgUQtGDOIaw/hFK1qRikai4RCNaEM4wmEM
K2iQDJgkwxOeYIZv1NEaeNTGNEYZyjyW0o7y4EIgBBEOa7jylbCMpSxn6cpt2OIO4NhGMwRBiBoK
QhB5COYdmFEMLWjuEE+ogx3mMIc4xGEMY4gDN2hJzWrG0h1tWCU4msHNbnrzm+C0hTjHOc5m1OIO
1miGNbZxx224cxvdSGcjFNEIQSihmc8c/8MXvuCFJ+gSnAANqECbEY5sCmKbAw2oNUZBiEY0ghGM
kCENBTEMdCa0m7VkhgXfwNELZpAO6byoSMFZ0FVugxkoTalKV6pSaIwiJw97hxe/aIVBZAEaLM2p
SrlpjJ4a4xhAPUYzdErUorIUHGwIRB6goYymOvWpUH3qMhwRDHNcIxrFECMjr3AILTCjqcsIq1jH
2lRoNAMatmCCEtbK1iQk4QnNiKpc50rXpm4jqXeARjH2yte++rWvyyhFHgbxS2AGEw6GqEUbllGM
pv71qb0whCEOcYcj8NMLXuiCZm/ghWP89bOgDe1et6GFQNzhGb1IrWpXy1rW+sIXqn2tL/72Glg2
hBUXZkCDGXabSTJYoRavuIIoRGEIImR2sze4QQuSwIzWOve50E0tNEp7h2Xw4rrYza52d7HdXXj3
u97tBSquoAhFCOIJy8QnNMdwBEEYIgvO8AUtzNDbTD4BDcXQrn71y9396vcZWQhEG5yBC1zk4sAG
RnCCc1HgAh/4wRBWcC4UYQhF3EEJy3QmNPd5BDbc4Qq9yAUvxkriZUQ4wguGsINR/OAGP+MMAnbG
LWZM4xrb+MY4rrEudAGMWFhhk0DepBKY8ApUsCEXM0YFKUhRiiY7+RU5jrKUb+yMKwgYGLTIspa3
zOUue/nLsXiFmMcsZlrkYhFn2DEqiP5Agza7WQYySMIuvkznOnO5yoFggy5iwec++/nPgA60oO1s
iisEkw0rUG4LWsCCRqvgBrgQtKQn/WdgWFnPq/hzpmOxikxvmtKT9vSn+dzpWJhiEYswxApYoIJW
q8AEJijBCiI9akGXmtOb9jSpcb0KS+f5Fp0OtrCHTexiG/vYw+bzKS5ZXzCsgdfIjraxdXHpWaji
2tjOtravfQpd3GLb4FZFLoBBbmuHm9u6cAYwniHjW+jiGc8Yty7OTe96q+IWVj7DLE7B7377+9/8
ngUQJICIVQDc36vANwkq4II9GPzgATdCBJoABCvs2xJFKAIiXFCEh0P84yA/xS2sEP+IM8iCEyjn
RCdSnvJYzGLGJ78FAgqQCGDMAuWymMXLY9GJTqyiAgAQgAECIIFYcCLht5DFyjmhilk8QwIEQIIA
HCDyKwAAAnyYuiZ8/vKTN30VmlDFyU8xi5MvneUrP/ssSG7yTLj97ZvYRCbk/oY0LMEIDo8FBDTg
BCO8QRWdwEMamGAEPKxiFkUAwAb08AkwiOEUskgE4Q2/iVVUogpLiEAG/PCBBawBGBQgABj2UIQw
QF7yhZ8FIq7AB1UgwvCVSAMeOjF3uc/97bXPhCzYzgq3YyITmAi+2zuhCAMEPQADYIIuILCABAhA
AEVwBgKC/vwi7MIBB1ADLWahbln+5MH4z3fBLPgw/QEUYPNOUMAMFmEADXwiAgHwgC72AH7oWz0C
zqjAAEJhBABUYBa493sCGHzBt3uBcAWhYAkKuIALmAmI0AAa0AdgsAAboAoQkAFioAYZsAGrcIFy
IAYZ8AB7gAAaAAiy4AISQAF4AH9gMAkckAB/QAEAEAOJcAIYoAeI8AAdUAQBAAO3UAYPIAKxwIIu
mABy4AEPsAYGcABhIAEFoAanwIBSyICsUAUll4AKWAmVkIWWcAkPyAGYQAkboAGTsHeTsAgbwAGW
sHeAQAsekAFr4AAa4Aez4IRRhwAZ8AERkAAKAAYI8ACAkAshgAFyQAsjgAENkH3/ssAHDHACfGAA
ebiHCrAGNbAAIFAAGNABBtABl7CAWqiFlgCKWcgKVvAIbHAJn5iKoPiFoYAIDECGe/cHisAAYGiG
suAAGNAHIaAANcB9MLAATgCBM+ACM2ADegCBfeAMILAAchALYbAAC9ABlRB4CeCIrziMxQgIePAA
CuABv3gANaAKqjiOnxgKTfAPVkAJ6riO7GgJD8h3EhAAKbAKDpABf+CKaniBSFABASACl1AGGbAA
JOACBqAAarCLMVAFIJAGtzACCvABS4AAzNgJYsiLp3AJeHAAIrAKH6AACbmQp5AJHWkDfIABGqAH
mcCOKrmSU/APh3AGn7CS6lgJ/3/QABiQAAPwAX2gCiDQAfcIASewhjc5AB4gB5iwCWEgAglQAA0A
A4CgByjwAANgAFBwCn4QlQXAAB1glJ0wAyeJCZawBxDwAqoAlVJJlZvQCUjAAX5wCiOQApcgkzL5
CWYwEFnwBjEpk3zQABygB34QipTwB3+QCJQACICwCXIoB35ACZagjmHYB3pwmIxZCZA5CZaQCFrY
B31gmIRJCYjQB+uYCH8ACJNZmY1JCZMAmoH5B3K5kpfwBlkgEllwBYlgCZ9wm7cJConAl5bACrgJ
CqCQm6AgC2woC7iJm6EQCsGJnMp5nMkJnL8ZCscJnbf5nNEpnMeZnbh5mVcQmypEwQhT0ARV0ATk
SZ5M4AIuUJ7quZ7oyQTr+Z7wGZ/yOZ/0+Z7j2ZIDERAAOw==";

return $pgvLogo;
}

/**
 * hex encode a string
 *
 * this function encodes a string in quoted_printable format
 * found at http://us3.php.net/bin2hex
 */
function hex4email ($string,$charset) {
	global $LANGUAGE;

	//-- check if the string has extended characters in it
	$str = utf8_decode($string);
	//-- if the strings are the same no conversion is necessary
	if ($str==$string) return $string;
	//-- convert to string into quoted_printable format
	$string = bin2hex ($string);
	$encoded = chunk_split($string, 2, '=');
	$encoded = preg_replace ("/=$/","",$encoded);
	$string = "=?$charset?Q?=" . $encoded . "?=";
	return $string;
}

/**
 * decode a filename
 *
 * windows doesn't use UTF-8 for its file system so we have to decode the filename
 * before it can be used on the filesystem
 */
function filename_decode($filename) {
	if (preg_match("/Win32/", $_SERVER["SERVER_SOFTWARE"])>0) return utf8_decode($filename);
	else return $filename;
}

/**
 * encode a filename
 *
 * windows doesn't use UTF-8 for its file system so we have to encode the filename
 * before it can be used in PGV
 */
function filename_encode($filename) {
	if (preg_match("/Win32/", $_SERVER["SERVER_SOFTWARE"])>0) return utf8_encode($filename);
	else return $filename;
}


/**
 * strip name prefixes
 *
 * this function strips the prefixes of lastnames
 */
function strip_prefix($lastname){
	$name = preg_replace(array("/ [jJsS][rR]\.?,/", "/ I+,/", "/^[a-z. ]*/"), array(",","",""), $lastname);
	$name = trim($name);
	$names = preg_split("/,/", $name);
	return $names;
}

//-- This function finds and returns all of the media objects in a given gedcom record
function find_media_in_record($gedrec) {
	global $medialist, $MEDIA_DIRECTORY, $ct, $PGV_IMAGE_DIR, $PGV_IMAGES, $foundlist, $medialinks, $MEDIA_EXTERNAL;

	$pos1=0;
	while($pos1 = strpos($gedrec, " OBJE", $pos1)) {
		//-- get the media sub record from the main gedcom record
		while(($pos1>0)&&($gedrec[$pos1]!="\n")) $pos1--;
		if ($pos1>0) $pos1++;
		$level = $gedrec[$pos1];
		$pos2 = strpos($gedrec, "\n$level ", $pos1+5);
		if ($pos2!==false) {
			$mediarec = substr($gedrec, $pos1, $pos2-$pos1);
			$pos1 = $pos2;
		}
		else {
			$mediarec = substr($gedrec, $pos1);
			$pos1 = strlen($gedrec);
		}
		//-- search if it is an embedded or linked media object
		$embed = preg_match("/(\d) _*FILE (.*)/", $mediarec, $embmatch);
		if ($embed==0) {
			//-- if it is a linked object then store a reference to this individual/family in the
			//-- $medialinks array
			$ct = preg_match("/@(.*)@/", $mediarec, $match);
			if ($ct>0) {
				$oid = $match[1];
				$tt = preg_match("/0 @(.*)@ (.*)/", $gedrec, $match);
				if ($tt>0) $id = $match[1];
				else $id=$ct;
				$type = trim($match[2]);
				if (!isset($medialinks)) $medialinks = array();
				if (!isset($medialinks[$oid])) $medialinks[$oid] = array();
				$medialinks[$oid][$id] = $type;
			}
		}
		else {
			//-- if it is an embedded object then get the filename from it
			$level = $embmatch[1];
			$tt = preg_match("/\d TITL (.*)/", $mediarec, $match);
			$fullpath = extract_fullpath($mediarec);
			$filename = "";
			if ((strstr( $fullpath, "://"))||(strstr( $fullpath, "mailto:"))) {
				$filename=$fullpath;
			    $image_type = array("bmp", "gif", "jpeg", "jpg", "pcx", "png", "tiff");
				$path_end=substr($fullpath, strlen($fullpath)-5);
				$type=strtolower(substr($path_end, strpos($path_end, ".")+1));
				if ($MEDIA_EXTERNAL && in_array($type, $image_type)) $thumbnail=trim($fullpath);
				else $thumbnail=$PGV_IMAGE_DIR."/".$PGV_IMAGES["media"]["large"];
			}
			else {
				$filename = extract_filename($fullpath);
				$thumbnail = $MEDIA_DIRECTORY."thumbs/".$filename;
				$thumbnail = trim($thumbnail);
				$filename = $MEDIA_DIRECTORY.$filename;
				$filename = trim($filename);
			}
			if ($tt>0) $title = trim($match[1]);
			else $title="";
			if (empty($title)) $title = $filename;
			$linked = preg_match("/0 @(.*)@ OBJE/", $mediarec, $match);
			if ($linked>0) {
				$linkid = trim($match[1]);
				$links = $medialinks[$linkid];
			}
			else {
				$tt = preg_match("/0 @(.*)@ (.*)/", $gedrec, $match);
				if ($tt>0) $id = $match[1];
				else $id=$ct;
				$type = trim($match[2]);
				if ((isset($foundlist[$filename]))&&(isset($medialist[$foundlist[$filename]]["link"]))) {
					$links = $medialist[$foundlist[$filename]]["link"];
				}
				else $links = array();
				$links[$id] = $type;
			}
			if (!isset($foundlist[$filename])) {
				$media = array();
				$media["file"] = $filename;
				$media["thumb"] = $thumbnail;
				$media["title"] = $title;
				$media["gedcom"] = $mediarec;
				$media["level"] = $level;
				$medialist[$ct]=$media;
				$foundlist[$filename] = $ct;
				$ct++;
			}
			$medialist[$foundlist[$filename]]["link"]=$links;
		}
	}
}

//-- This function changes the used gedcom connected to a language
function change_gedcom_per_language($new_gedcom_name,$new_language_name)
{
  global $QUERY_STRING;
  global $PHP_SELF;

  $QUERY_STRING = preg_replace("/&amp;/", "&", $QUERY_STRING);
  $QUERY_STRING = preg_replace("/&&/", "&", $QUERY_STRING);
  $terms = preg_split("/&/", $QUERY_STRING);
  $vars = "";
  for ($i=0; $i<count($terms); $i++)
  {
	if (substr($terms[$i],0,7) == "gedcom=")$terms[$i]="";
	if ((!empty($terms[$i]))&&(strstr($terms[$i], "changelanguage")===false)&&(strpos($terms[$i], "NEWLANGUAGE")===false))
	{
	  $vars .= $terms[$i]."&";
	}
  }
  $QUERY_STRING = $vars;
  if (empty($QUERY_STRING))$QUERY_STRING = "GEDCOM=".$new_gedcom_name; else $QUERY_STRING = $QUERY_STRING . "&gedcom=".$new_gedcom_name;
  $QUERY_STRING = preg_replace("/&&/", "&", $QUERY_STRING);
  $_SESSION["GEDCOM"] = "GEDCOM=".$new_gedcom_name;
  $_SESSION['CLANGUAGE'] = $new_language_name;
  header("Location: ".$PHP_SELF."?".$QUERY_STRING);
  exit;
}

//-- This function replaces NN and PN with the language specific translations
function check_NN($names) {
	global $pgv_lang, $HNN;
	
	$fullname = "";
	$hebname = 0; 
    $HNN= "(לא ידוע)";
    $HPN= "(לא ידוע)";
	if (!is_array($names)){
		if (hasRTLText($names)) $hebname = 1;
		if (!$hebname && $names = preg_replace(array("/@N.N./","/@P.N./"), array($pgv_lang["NN"],$pgv_lang["PN"]), trim($names))) return $names;
		else if ($hebname && $names = preg_replace(array("/@N.N./","/@P.N./"), array($HNN,$HPN), trim($names))) return $names;
				return $names;	
	}
	if (count($names) == 2 && stristr($names[0], "@N.N.") && stristr($names[1], "@N.N.")){
		$fullname = $pgv_lang["NN"]. " + ". $pgv_lang["NN"];
	}
	else {
		for($i=0; $i<count($names); $i++) {
			if (hasRTLText($names[$i])) $hebname = 1;
			for($i=0; $i<count($names); $i++) {
				if (stristr($names[$i], "@N.N.") && !$hebname) $names[$i] = preg_replace("/@N.N./", $pgv_lang["NN"], trim($names[$i]));
				else if (stristr($names[$i], "@N.N.") && $hebname) $names[$i] = preg_replace("/@N.N./", $HNN, trim($names[$i]));
				if (trim($names[$i]) == "@P.N." && !$hebname) $names[$i] = $pgv_lang["PN"];
				else if (trim($names[$i]) == "@P.N." && $hebname) $names[$i] = $HPN;
				if (substr(trim($names[$i]), 0, 5) == "@P.N." && strlen(trim($names[$i])) > 5) {
					$names[$i] = substr(trim($names[$i]), 5, (strlen($names[$i])-5));
				}
				if ($i==1 && (stristr($names[0], $pgv_lang["NN"]) || stristr($names[0],$HNN)) && count($names) == 3) $fullname .= ", ";   
				else if ($i==2 && (stristr($names[2], $pgv_lang["NN"])||stristr($names[2],$HNN)) && count($names) == 3) $fullname .= " + ";
				else if ($i==2 && stristr($names[2], "Individual ") && count($names) == 3) $fullname .= " + ";
				else if ($i==2 && count($names) > 3) $fullname .= " + ";
				else $fullname .= ", ";
				$fullname .= trim($names[$i]);
			}
		}
	}
	if (empty($fullname)) return $pgv_lang["NN"];
	if (substr(trim($fullname),-1) === ",") $fullname = substr($fullname,0,strlen(trim($fullname))-1);
	if (substr(trim($fullname),0,2) === ", ") $fullname = substr($fullname,2,strlen(trim($fullname)));
	return $fullname;
}

function getAlphabet(){
	global $ALPHABET_upper, $ALPHABET_lower, $LANGUAGE, $alphabet;

	//-- setup the language alphabet string
	if (!isset($alphabet)) {
		$alphabet = "0123456789".$ALPHABET_upper[$LANGUAGE].$ALPHABET_lower[$LANGUAGE];
		foreach ($ALPHABET_upper as $l => $upper){
			if ($l <> $LANGUAGE) $alphabet.=$upper;
		}
		foreach ($ALPHABET_lower as $l => $lower){
			if ($l <> $LANGUAGE) $alphabet.=$lower;
		}
	}
	return $alphabet;
}

/* ---- function to search the day of the week
   ---- $sw_day = int  $sw_mont = int or alpha (like "jan" or "dec") $sw_year = int
   ---- $weekday (=int) will be returned (1 = monday)
   ---- calculations by Michael Gudaitis (in Java), converted by Jans Luder
*/
function search_weekday( $sw_day, $sw_month, $sw_year) {
	global $monthtonum;
	$sw_month = $monthtonum[strtolower($sw_month)];
	if($sw_month >= 3)$sw_month -= 2;
	else $sw_month += 10;
	if( ($sw_month == 11) || ($sw_month == 12) )$sw_year--;
	$centnum  = floor($sw_year / 100);
	$yearnum  = $sw_year % 100;
	$weekday  = floor(2.6 * $sw_month - .2);
	$weekday += floor($sw_day + $yearnum);
	$weekday += $yearnum / 4;
	$weekday  = floor($weekday);
	$weekday += floor($centnum / 4);
	$weekday -= floor(2 * $centnum);
	$weekday %= 7;
	if($sw_year >= 1700 && $sw_year <= 1751) $weekday -= 3;
	else if($sw_year <= 1699) $weekday -= 4;
	if($weekday < 0) $weekday += 7;
	return $weekday;
}
// end function search_weekday

// 
/**
 * Put all characters in a string in lowercase
 *
 * This function is a replacement for strtolower() and will put all characters in lowercase
 *
 * @author	eikland
 * @param	string $value the text to be converted to lowercase
 * @return	string $value_lower the converted text in lowercase
 * @todo look at function performance as it is much slower than strtolower
 */
function str2lower($value) {
	global $language_settings,$LANGUAGE, $ALPHABET_upper, $ALPHABET_lower;
	global $all_ALPHABET_upper, $all_ALPHABET_lower;
	
	//-- get all of the upper and lower alphabets as a string
	if (!isset($all_ALPHABET_upper)) {
		$all_ALPHABET_upper = "";
		$all_ALPHABET_lower = "";
		foreach ($ALPHABET_upper as $l => $up_alphabet){
			$lo_alphabet = $ALPHABET_lower[$l];
			$ll = strlen($lo_alphabet);
			$ul = strlen($up_alphabet);
			if ($ll < $ul) $lo_alphabet .= substr($up_alphabet, $ll);
			if ($ul < $ll) $up_alphabet .= substr($lo_alphabet, $ul);
			$all_ALPHABET_lower .= $lo_alphabet;
			$all_ALPHABET_upper .= $up_alphabet;
		}
	}

	$value_lower = "";
	$len = strlen($value);
	
	//-- loop through all of the letters in the value and find their position in the 
	//-- upper case alphabet.  Then use that position to get the correct letter from the
	//-- lower case alphabet.
	$ord_value2 = array(92, 195, 196, 197, 206, 208, 209, 214, 215);
	$ord_value3 = array(228, 229, 230, 232, 233);
	for($i=0; $i<$len; $i++) {
		$letter = substr($value, $i, 1);
		$ord = ord($letter);
		if (in_array($ord, $ord_value2)) {
			$i++;
			$letter .= substr($value, $i, 1);
		}
		else if (in_array($ord, $ord_value3)) {
			$i++;
			$letter .= substr($value, $i, 2);
			$i++;
		}
		$pos = strpos($all_ALPHABET_upper, $letter);
		if ($pos!==false) {
			$letter = substr($all_ALPHABET_lower, $pos, strlen($letter));
		}
		$value_lower .= $letter;
	}
	return $value_lower;
}
// END function str2lower

// 
/**
 * Put all characters in a string in uppercase
 *
 * This function is a replacement for strtoupper() and will put all characters in uppercase
 *
 * @author	botak
 * @param	string $value the text to be converted to uppercase
 * @return	string $value_upper the converted text in uppercase
 * @todo look at function performance as it is much slower than strtoupper
 */
function str2upper($value) {
	global $language_settings,$LANGUAGE, $ALPHABET_upper, $ALPHABET_lower;
	global $all_ALPHABET_upper, $all_ALPHABET_lower;
	
	//-- get all of the upper and lower alphabets as a string
	if (!isset($all_ALPHABET_upper)) {
		$all_ALPHABET_upper = "";
		$all_ALPHABET_lower = "";
		foreach ($ALPHABET_upper as $l => $up_alphabet){
			$lo_alphabet = $ALPHABET_lower[$l];
			$ll = strlen($lo_alphabet);
			$ul = strlen($up_alphabet);
			if ($ll < $ul) $lo_alphabet .= substr($up_alphabet, $ll);
			if ($ul < $ll) $up_alphabet .= substr($lo_alphabet, $ul);
			$all_ALPHABET_lower .= $lo_alphabet;
			$all_ALPHABET_upper .= $up_alphabet;
		}
	}
	
	$value_upper = "";
	$len = strlen($value);
	
	//-- loop through all of the letters in the value and find their position in the 
	//-- lower case alphabet.  Then use that position to get the correct letter from the
	//-- upper case alphabet.
	$ord_value2 = array(92, 195, 196, 197, 206, 207, 208, 209, 214, 215, 216, 217, 218, 219);
	$ord_value3 = array(228, 229, 230, 232, 233);
	for($i=0; $i<$len; $i++) {
		$letter = substr($value, $i, 1);
		$ord = ord($letter);
		if (in_array($ord, $ord_value2)) {
			$i++;
			$letter .= substr($value, $i, 1);
		}
		else if (in_array($ord, $ord_value3)) {
			$i++;
			$letter .= substr($value, $i, 2);
			$i++;
		}
		$pos = strpos($all_ALPHABET_lower, $letter);
		if ($pos!==false) {
			$letter = substr($all_ALPHABET_upper, $pos, strlen($letter));
		}
		$value_upper .= $letter;
	}
	return $value_upper;
}
// END function str2upper


// This functions checks if an existing file is physically writeable
// The standard PHP function only checks for the R/O attribute and doesn't
// detect authorisation by ACL.
function file_is_writeable($file) {
	$err_write = false;
	$handle = @fopen($file,"r+");
	if	($handle)	{
		$i = fclose($handle);
		$err_write = true;
	}
	return($err_write);
}

function cleanup_tags_y($irec) {
	$cleanup_facts = array("ANUL","CENS","DIV","DIVF","ENGA","MARR","MARB","MARC","MARL","MARS","BIRT","CHR","DEAT","BURI","CREM","ADOP","DSCR","BAPM","BARM","BASM","BLES","CHRA","CONF","FCOM","ORDN","NATU","EMIG","IMMI","CENS","PROB","WILL","GRAD","RETI");
	$irec .= "\r\n1";
	$ft = preg_match_all("/1\s(_?.{3,4})\s/", $irec, $match);
	for($i=0; $i<$ft; $i++){
		$sfact = $match[1][$i];
		$sfact = trim($sfact);
		if (in_array($sfact, $cleanup_facts)) {
			$srchstr = "/1\s".$sfact."\sY\r\n2/";
			$replstr = "1 ".$sfact."\r\n2";
			$srchstr2 = "/1\s".$sfact."(.{0,1})\r\n2/";
			$srchstr = "/1\s".$sfact."\sY\r\n2/";
			$srchstr3 = "/1\s".$sfact."\sY\r\n1/";
			$irec = preg_replace($srchstr,$replstr,$irec);
			if (preg_match($srchstr2,$irec)){
				$irec = preg_replace($srchstr3,"1",$irec);
			}
		}
	}
	$irec=substr($irec,0,-3);
	return $irec;
}

/**
 * Convert a string to UTF8
 *
 * This function is a replacement for utf8_decode()
 *
 * @author	http://www.php.net/manual/en/function.utf8-decode.php
 * @param	string $in_str the text to be converted
 * @return	string $new_str the converted text
 */
function smart_utf8_decode($in_str)
{
   // Replace ? with a unique string
   $new_str = str_replace("?", "q0u0e0s0t0i0o0n", $in_str);

   // Try the utf8_decode
   $new_str=utf8_decode($new_str);

   // if it contains ? marks
   if (strpos($new_str,"?") !== false)
   {
       // Something went wrong, set new_str to the original string.
       $new_str=$in_str;
   }
   else
   {
       // If not then all is well, put the ?-marks back where is belongs
       $new_str = str_replace("q0u0e0s0t0i0o0n", "?", $new_str);
   }

   return $new_str;
}

function gedcomsort($a, $b) {
	$aname = str2upper($a["title"]);
	$bname = str2upper($b["title"]);

	return itemsort($aname, $bname);
}

/**
 * get a list of the reports in the reports directory
 *
 * When $force is false, the function will first try to read the reports list from the$INDEX_DIRECTORY."/reports.dat"
 * data file.  Otherwise the function will parse the report xml files and get the titles. 
 * @param boolean $force	force the code to look in the directory and parse the files again
 * @return array 	The array of the found reports with indexes [title] [file]
 */
function get_report_list($force=false) {
	global $INDEX_DIRECTORY, $report_array, $vars, $xml_parser, $elementHandler, $LANGUAGE;
	
	$files = array();
	if (!$force) {
		//-- check if the report files have been cached
		if (file_exists($INDEX_DIRECTORY."/reports.dat")) {
			$reportdat = "";
			$fp = fopen($INDEX_DIRECTORY."/reports.dat", "r");
			while ($data = fread($fp, 4096)) {
				$reportdat .= $data;
			}
			fclose($fp);
			$files = unserialize($reportdat);
			foreach($files as $file) {
				if (isset($file["title"][$LANGUAGE]) && (strlen($file["title"][$LANGUAGE])>1)) return $files;
			}
		}
	}
	
	//-- find all of the reports in the reports directory
	$d = dir("reports");
	while (false !== ($entry = $d->read())) {
		if (($entry!=".") && ($entry!="..") && ($entry!="CVS") && (strstr($entry, ".xml")!==false)) {
			if (!isset($files[$entry]["file"])) $files[$entry]["file"] = "reports/".$entry;
		}
	}
	$d->close();
	
	require_once("reportheader.php");
	$report_array = array();
	if (!function_exists("xml_parser_create")) return $report_array();
	foreach($files as $file=>$r) {
		$report_array = array();
		//-- start the sax parser
		$xml_parser = xml_parser_create();
		//-- make sure everything is case sensitive
		xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, false);
		//-- set the main element handler functions
		xml_set_element_handler($xml_parser, "startElement", "endElement");
		//-- set the character data handler
		xml_set_character_data_handler($xml_parser, "characterData");
	
		if (file_exists($r["file"])) {
			//-- open the file
			if (!($fp = fopen($r["file"], "r"))) {
			   die("could not open XML input");
			}
			//-- read the file and parse it 4kb at a time
			while ($data = fread($fp, 4096)) {
				if (!xml_parse($xml_parser, $data, feof($fp))) {
					die(sprintf($data."\nXML error: %s at line %d", xml_error_string(xml_get_error_code($xml_parser)), xml_get_current_line_number($xml_parser)));
				}
			}
			fclose($fp);
			xml_parser_free($xml_parser);
			if (isset($report_array["title"]) && isset($report_array["access"]) && isset($report_array["icon"])) {
				$files[$file]["title"][$LANGUAGE] = $report_array["title"];
				$files[$file]["access"] = $report_array["access"];
				$files[$file]["icon"] = $report_array["icon"];
			}
		}
	}
	
	$fp = @fopen($INDEX_DIRECTORY."/reports.dat", "w");
	@fwrite($fp, serialize($files));
	@fclose($fp);
	
	return $files;
}

/**
 * get first letter
 *
 * get the first letter of a UTF-8 string
 * @param string $text	the text to get the first letter from
 * @return string 	the first letter UTF-8 encoded
 */
function get_first_letter($text, $import=false) {
	global $LANGUAGE, $CHARACTER_SET;
	
	$text=trim(str2upper($text));
	if ($import == true) {
		$hungarianex = array("DZS", "CS", "DZ" ,"GY", "LY", "NY", "SZ", "TY", "ZS");
		$danishex = array("OE", "AE", "AA");
		if (substr($text, 0, 3)=="DZS") $letter = substr($text, 0, 3);
		else if (in_array(substr($text, 0, 2), $hungarianex) || in_array(substr($text, 0, 2), $danishex)) $letter = substr($text, 0, 2);
		else $letter = substr($text, 0, 1);
	}
	else if ($LANGUAGE == "hungarian"){
		$hungarianex = array("CS", "DZ" , "GY", "LY", "NY", "SZ", "TY", "ZS", "DZS");
		if (substr($text, 0, 3)=="DZS") $letter = substr($text, 0, 3);
		else if (in_array(substr($text, 0, 2), $hungarianex)) $letter = substr($text, 0, 2);
		else $letter = substr($text, 0, 1);
	}
	else if ($LANGUAGE == "danish" || $LANGUAGE == "norwegian"){
		$danishex = array("OE", "AE", "AA");
		$letter = str2upper(substr($text, 0, 2));
		if (in_array($letter, $danishex)) {
			if ($letter == "AE") $text = "Æ";
			else if ($letter == "OE") $text = "Ø";
			else if ($letter == "AA") $text = "Å";
		}
		$letter = substr($text, 0, 1);
	}
	else $letter = substr($text, 0, 1);

	//-- if the letter is an other character then A-Z or a-z
	//-- define values of characters to look for
	$ord_value2 = array(92, 195, 196, 197, 206, 207, 208, 209, 214, 215, 216, 217, 218, 219);
	$ord_value3 = array(228, 229, 230, 232, 233);
	$ord = ord($letter);
	if (in_array($ord, $ord_value2)) $letter = stripslashes(substr($text, 0, 2));
	else if (in_array($ord, $ord_value3)) $letter = stripslashes(substr($text, 0, 3));
//	else if (strlen($letter) == 1) $letter = str2upper($letter);
	
	return $letter;
}

/**
 * parse a gedcom date into an array
 *
 * parses a gedcom date IE 1 JAN 2002 into an array of month day and year values
 * @param string $datestr		The date to parse
 * @return array		returns an array with indexes "day"=1 "month"=JAN "mon"=1 "year"=2002 "ext" = abt
 */
function parse_date($datestr) {
	global $monthtonum;
	
	$dates = array();
	$dates[0]["day"] = 1;
	$dates[0]["month"] = "JAN";
	$dates[0]["mon"] = 1;
	$dates[0]["year"] = 0;
	$dates[0]["ext"] = "";
	$strs = preg_split("/[\s\.]+/", $datestr);
	$index = 0;
	for($i=0; $i<count($strs); $i++) {
		$ct = preg_match("/^\d+$/", $strs[$i]);
		if ($ct>0) {
			if (isset($strs[$i+1]) && isset($monthtonum[strtolower($strs[$i+1])])) $dates[$index]["day"] = $strs[$i];
			else {
				$dates[$index]["year"] = $strs[$i];
				$index++;
			}
		}
		else if (isset($monthtonum[strtolower($strs[$i])])) {
			$dates[$index]["month"] = $strs[$i];
			$dates[$index]["mon"] = $monthtonum[strtolower($strs[$i])];
		}
		else {
			if (!isset($dates[$index]["ext"])) $dates[$index]["ext"] = "";
			$dates[$index]["ext"] .= $strs[$i];
		}
	}
	return $dates;
}

/**
 * Force a string in ltr direction
 *
 * This function returns a string in left-to-right direction.
 * To be used for non HTML string output (e.g. GD ImageTtfText function).
 *
 * @author opus27
 * @param string $name input string
 * @return string ltr string
 * @todo more hebrew controls (check numbers or &rlm; tags)
 * @todo other rtl languages
 */
function ltr_string($name) {
	// hebrew string => reverse
	global $RTLOrd;
	
	$found = false;
	foreach($RTLOrd as $ord) {
   		if (strpos($name, chr($ord)) !== false) $found=true;
	}
	if ($found) {
	 	$ltrname = "";
		$i=0;
		while ($i<strlen($name)) {
 			if (in_array(ord(substr($name,$i,1)),$RTLOrd)) {
				$ltrname = substr($name, $i, 2) . $ltrname;
				$i+=2;
			}
			else {
				if ($name{$i}==' ') $ltrname = " " . $ltrname;
				else if ($name{$i}=='(') $ltrname = ")" . $ltrname;
				else if ($name{$i}==')') $ltrname = "(" . $ltrname;
				else if ($name{$i}=='[') $ltrname = "]" . $ltrname;
				else if ($name{$i}==']') $ltrname = "[" . $ltrname;
				else if ($name{$i}=='{') $ltrname = "}" . $ltrname;
				else if ($name{$i}=='}') $ltrname = "{" . $ltrname;
				else $ltrname = $name{$i} . $ltrname;   //--- ???
				$i++;
			}
		}
		$ltrname=str_replace(";mrl&", "", $ltrname);
		$ltrname=str_replace(";mlr&", "", $ltrname);
		return $ltrname;
	}
	// other rtl languages => (to be completed)
	// else
	$ltrname=$name;
	$ltrname=str_replace("&lrm;", "", $ltrname);
	$ltrname=str_replace("&rlm;", "", $ltrname);
	return $ltrname;
}

/**
 * convert HTML entities to to their original characters
 *
 * original found at http://www.php.net/manual/en/function.get-html-translation-table.php
 * @see http://www.php.net/manual/en/function.get-html-translation-table.php
 * @param string $string	the string to remove the entities from
 * @return string	the string with entities converted
 */
function unhtmlentities ($string)  {
	$trans_tbl = get_html_translation_table (HTML_ENTITIES);
	$trans_tbl = array_flip ($trans_tbl);
	$ret = strtr ($string, $trans_tbl);
	$ret = preg_replace('/&#(\d+);/me', "chr('\\1')",$ret);
	//- temporarily remove &lrm; until they can be better handled later
	$ret = preg_replace(array('/&lrm;/','/&rlm;/'), array('',''), $ret);
	return $ret;
} 

/**
 * process a string according to bidirectional rules
 *
 * this function will take a text string and reverse it for RTL languages
 * according to bidi rules.
 * @param string $text	String to change
 * @return string	the new bidi string
 * @todo add other RTL langauges
 */
function bidi_text($text) {
	global $RTLOrd;
	
	//print "[$text] [";
	
	// דו"ח אישי
	//קראטוןםפ שדגכעיחלךף זסבה� מצתץ עברי איתה מאיה (אתקה) שם משפחה ‎
	//מספר מזהה (SSN)
	
	$found = false;
	foreach($RTLOrd as $ord) {
    	if (strpos($text, chr($ord))!==false) $found=true;
	}
	if (!$found) return $text;
// 	if (strpos($text, chr(215))=== false && strpos($text, chr(216))=== false && strpos($text, chr(217))=== false && strpos($text, chr(218))=== false && strpos($text, chr(219))=== false) return $text;

	$special_chars = array(' ','"','\'','(',')','[',']',':',"\n");
	$newtext = "";
	$parts = array();
	$temp = "";
	$state = 0;
	$p = 0;
	for($i=0; $i<strlen($text); $i++) {
		$letter = $text{$i};
		//print $letter.ord($letter).",";
		//-- handle Hebrew chars
		if (in_array(ord($letter),$RTLOrd)) {
//		if (ord($letter)==215 || ord($letter)==216 || ord($letter)==217 || ord($letter)==218 || ord($letter)==219) {}
			if (!empty($temp)) {
				//-- just in case the $temp is a Hebrew char push it onto the stack
				if (in_array(ord($temp{0}),$RTLOrd));
// 				if (ord($temp{0})==215 || ord($temp{0})==216 || ord($temp{0})==217 || ord($temp{0})==218 || ord($temp{0})==219) array_push($parts, $temp);
				//-- if the $temp starts with a char in the special_chars array then remove the space and push it onto the stack seperately
				else if (in_array($temp{strlen($temp)-1}, $special_chars)) {
					$char = substr($temp, strlen($temp)-1);
					$temp = substr($temp, 0, strlen($temp)-1);
					if ($char=="[") $char = "]";
					else if ($char=="(") $char = ")";
					array_push($parts, $temp);
					array_push($parts, $char);
				}
				//-- otherwise push it onto the begining of the stack
				else array_unshift($parts, $temp);
			}
			$temp = $letter . $text{$i+1};
			$i++;
			if ($i < strlen($text)-1) {
				$l = $text{$i+1};
				if (in_array($l, $special_chars)) {
					if ($l=="]") $l = "[";
					else if ($l==")") $l = "(";
					$temp = $l . $temp;
					$i++;
				}
			}
			array_push($parts, $temp);
			$temp = "";
		}
		else if (ord($letter)==226) {
			if ($i < strlen($text)-2) {
				$l = $letter.$text{$i+1}.$text{$i+2};
				$i += 2;
				if (($l=="‏")||($l=="‎")) {
					if (!empty($temp)) {
						$last = array_pop($parts);
						if ($temp{0}==")") $last = '(' . $last;
						else if ($temp{0}=="(") $last = ')' . $last;
						else if ($temp{0}=="]") $last = '[' . $last;
						else if ($temp{0}=="[") $last = ']' . $last;
						array_push($parts, $last);
						$temp = "";
					}
				}
			}
		}
		else $temp .= $letter;
	}
	if (!empty($temp)) {
		if (in_array(ord($temp{0}),$RTLOrd)) array_push($parts, $temp);
// 		if (ord($temp{0})==215 || ord($temp{0})==216 || ord($temp{0})==217 || ord($temp{0})==218 || ord($temp{0})==219) array_push($parts, $temp);
		//-- if the $temp starts with a char in the special_chars array then remove the space and push it onto the stack seperately
		/*else if (in_array($temp{strlen($temp)-1}, $special_chars)) {
			$char = substr($temp, strlen($temp)-1);
			$temp = substr($temp, 0, strlen($temp)-1);
			array_push($parts, $temp);
			array_push($parts, $char);
		}*/
		//else if ($temp{0}==')') array_unshift($parts, $temp);
		else array_push($parts, $temp);
	}
	//print_r($parts);
	$parts = array_reverse($parts);
	$newtext = implode("", $parts);
	return $newtext;
}

/**
 * Extract the surname from a name
 *
 * This function will extract the surname from an individual name in the form
 * Surname, Given Name
 * All surnames are stored in the global $surnames array
 * It will only get the surnames that start with the letter $alpha
 * For names like van den Burg, it will only return the "Burg"
 * It will work if the surname is all lowercase
 * @param string $indiname	the name to extract the surname from
 */
function extract_surname($indiname) {
	global $surnames, $alpha, $surname, $show_all, $i, $testname;
	
	if (!isset($testname)) $testname="";
	$names = preg_split("/,/", $indiname);
	$names[0] = preg_replace(array("/ [jJsS][rR]\.?,/", "/ I+,/", "/^[a-z\. ]*/"), array(",",",",""), $names[0]);
	$names[0] = trim($names[0]);
	//-- check for all lowercase name and start over
	if (empty($names[0])) $names = preg_split("/,/", $indiname);
	// Match names with chosen first letter
	$names[0] = trim($names[0]);
	$sort_letter= get_first_letter($names[0]);
	if (($show_all=="yes") || empty($alpha) || ($alpha==$sort_letter)) {
		$name = preg_replace(array("/ [jJsS][rR]\.?,/", "/ I+,/"), array(",",","), $indiname);
		$tnames = preg_split("/,/", $name);
		$tsurname = trim($tnames[0]);
		$tsurname = str2upper($tsurname);
		if (empty($surname) || (str2upper($surname)==$tsurname)) {
			if (!isset($surnames[$tsurname])) {
				$surnames[$tsurname] = array();
				$surnames[$tsurname]["name"] = $tnames[0];
				$surnames[$tsurname]["match"] = 1;
				$surnames[$tsurname]["fam"] = 1;
				$surnames[$tsurname]["alpha"] = $sort_letter;
			}
			else {
				$surnames[$tsurname]["match"]++;
				if ($i==0 || $testname != $tnames[0]) $surnames[$tsurname]["fam"]++;
			}
			if ($i==0) $testname = $tnames[0];
			return $tsurname; 
			}
	}
	return false;
}


	$SpecialChar = array(' ','.',',','"','\'','/','\\','|',':',';','+','&','#','@','-','=','*','%','!','?','$',"\n");
	$SpecialPar = array('(',')','[',']','{','}');
	$SpecialNum  = array('0','1','2','3','4','5','6','7','8','9');
	
	$RTLOrd = array(215,216,217,218,219);

/**
 * Verify if text is a RtL character
 *
 * This will verify if text is a RtL character
 * @param string $text to verify
 */
function oneRTLText($text) { 
	global $RTLOrd;
	
	return (strlen($text)==2 && in_array(ord($text),$RTLOrd));
}

/**
 * Verify if text starts by a RtL character
 *
 * This will verify if text starts by a RtL character
 * @param string $text to verify
 */
function begRTLText($text) { 
	global $RTLOrd;	
	return (in_array(ord(substr(trim($text),0,2)),$RTLOrd) || in_array(ord(substr(trim($text),1,2)),$RTLOrd));
}

/**
 * Verify if text ends by a RtL character
 *
 * This will verify if text ends by a RtL character
 * @param string $text to verify
 */
function endRTLText($text) { 
	global $RTLOrd;	
	return (in_array(ord(substr(trim($text),strlen(trim($text))-2,2)),$RTLOrd) || in_array(ord(substr(trim($text),strlen(trim($text))-3,2)),$RTLOrd));
}

/**
 * Verify if text is RtL
 *
 * This will verify if text has RtL characters
 * @param string $text to verify
 */
function hasRTLText($text) { 
	global $RTLOrd;	
	for ($i=0; $i<strlen($text); $i++) {
	  if (in_array(ord(substr(trim($text),$i,2)),$RTLOrd)) return true;
	}
	return false;
} 

/**
 * Verify if text is LtR
 *
 * This will verify if text has LtR characters that are not special characters
 * @param string $text to verify
 */
function hasLTRText($text) { 
	global $SpecialChar, $SpecialPar, $SpecialNum, $RTLOrd;
	
	for ($i=0; $i<strlen($text); $i++) {
		if (in_array(ord(substr(trim($text),$i,2)),$RTLOrd) || in_array(ord(substr(trim($text),$i-1,2)),$RTLOrd)) $i++;
	  	else {
			$byte = substr(trim($text),$i,1);
		    if (!in_array($byte,$SpecialChar) && !in_array($byte,$SpecialPar) && !in_array($byte,$SpecialNum)) return true;
	    }
	}
	return false;
}

/**
 * Prepare text with parenthesis for printing 
 * Convert & to &amp; for xhtml compliance
 *
 * @param string $text to be printed
 */
function PrintReady($text) {
	global $TEXT_DIRECTION, $SpecialChar, $SpecialPar; 
	
	$SpecialOpen = '(';
//	$SpecialClose = array(')');
	$SpecialClose = array('(');
	
	//-- convert all & to &amp;
	$text = preg_replace("/&/", "&amp;", $text);
	//-- make sure we didn't double convert &amp; to &amp;amp;
	$text = preg_replace("/&amp;amp;/", "&amp;", $text);
    $text=trim($text);
    if ($TEXT_DIRECTION=="ltr" && hasRTLText($text)) {
   		if (hasLTRText($text)) {
	   		// Text contains both RtL and LtR characters
	   		// return the parenthesis with surrounding &rlm; and the rest as is

	   		$printvalue = "";
	   		$first = 1;
	   		$linestart = 0;
	   		for ($i=0; $i<strlen($text); $i++) {
                $byte = substr($text,$i,1); 
				if (substr($text,$i,6) == "<br />") $linestart = $i+6; 
				if (in_array($byte,$SpecialPar)	||
                   (($i==strlen($text)-1 || substr($text,$i+1,6)=="<br />") && in_array($byte,$SpecialChar))) {
			   		if ($first==1) { 
				   		if ($byte==")" && !in_array(substr($text,$i+1),$SpecialClose)) {
 			   		    	 $printvalue .= "&lrm;".$byte."&lrm;";
 			   			$linestart = $i+1;				   			
 			   			}
 				   		else 
				   		if (in_array($byte,$SpecialChar)) {                          //-- all special chars
				   		    if (hasRTLText(substr($text,$linestart,4))) 
				   		    	 $printvalue .= "&rlm;".$byte."&rlm;";
				   			else $printvalue .= "&lrm;".$byte."&lrm;";
			   			}
				   		else {
				   		$first = 0;
				   			if (hasRTLText(substr($text,$i+1,4))) {
				   		     $printvalue .= "&rlm;";
				   			    $ltrflag = 0;
			   			     }
				   			else {
					   			$printvalue .= "&lrm;";
				   			    $ltrflag = 1;
				   			}
				   		$printvalue .= substr($text,$i,1);	
			   		}
			   		}
			   		else {
				   		$first = 1;
				   		$printvalue .= substr($text,$i,1);
				   		if ($ltrflag)  
				   		     $printvalue .= "&lrm;";
				   		else $printvalue .= "&rlm;"; 
			   		}
		   		}
		   			 else if (oneRTLText(substr($text,$i,2))) {
			   		    	$printvalue .= substr($text,$i,2);
			   		    	$i++;
		   		    	}
		   		    	else $printvalue .= substr($text,$i,1);
	   		}
			if (!$first)    
				if ($ltrflag)  
					 $printvalue .= "&lrm;";
				else $printvalue .= "&rlm;";
				 
 			return $printvalue;
   		}
   		else return "&rlm;".$text."&rlm;"; 
	}
	else if ($TEXT_DIRECTION=="rtl" && hasLTRText($text)) {
   		$printvalue = "";
   		$linestart = 0;
   		$first = 1;
   		for ($i=0; $i<strlen($text); $i++) {
            $byte = substr($text,$i,1); 
            if (substr($text,$i,6) == "<br />") $linestart = $i+6; 
			if (in_array($byte,$SpecialPar)	|| (($i==strlen($text)-1 || substr($text,$i+1,6)=="<br />") && in_array($byte,$SpecialChar))) {
		   		if ($first==1) { 
			   		if ($byte==")" && !in_array(substr($text,$i+1),$SpecialClose)) {
			   		    	$printvalue .= "&rlm;".$byte."&rlm;";
			   				$linestart = $i+1;	
			   			}
				   		else 
			   		if (in_array($byte,$SpecialChar) && ($i==strlen($text)-1 || substr($text,$i+1,6)=="<br />")) {
			   		    if (hasRTLText(substr($text,$linestart,4))) 
			   		    	 $printvalue .= "&rlm;".$byte."&rlm;";
			   			else $printvalue .= "&lrm;".$byte."&lrm;";
		   			}
		   			else {
			   		$first = 0;
				   		if (hasRTLText(substr($text,$i+1,4))) {
			   		     $printvalue .= "&rlm;";
			   			    $ltrflag = 0;
			   		     }
				   		else {
					   		$printvalue .= "&lrm;";
			   			    $ltrflag = 1;
				   		}
			   		$printvalue .= substr($text,$i,1);	
		   		}
		   		}
		   		else {
			   		$first = 1;
			   		$printvalue .= substr($text,$i,1);
			   		if ($ltrflag)  
			   		     $printvalue .= "&lrm;";
			   		else $printvalue .= "&rlm;"; 
		   		}
	   		}
	   		else {
		   		if (oneRTLText(substr($text,$i,2))) {
		   		    $printvalue .= substr($text,$i,2);
		   		    $i++;
	   		    }
	   		    else $printvalue .= substr($text,$i,1);
   		    }
   		}
	 	if (!$first) if ($ltrflag) $printvalue .= "&lrm;";
		else $printvalue .= "&rlm;";
		return $printvalue;	  	   			    	
     }  	 
	 else return $text;
}

/**
 * clean up user submitted input before submitting it to the SQL query
 *
 * This function will take user submitted input string and remove any special characters
 * before they are submitted to the SQL query.
 * Examples of invalid characters are _ & ?
 * @param string $pid	The string to cleanup
 * @return string	The cleaned up string
 */
function clean_input($pid) {
	$pid = preg_replace("/[%?_]/", "", $pid);
	return $pid;
}





/**
 * Probably used to ensure floating points are rounded up and down properly
 *
 * @param float $float the number to round nicely
 * @return float a nicely rounded number?
 * @author VisualMind (visualmind@php.net)
 */
function _ardInt($float) 
{
       return ($float < -0.0000001) ? ceil($float-0.0000001) : floor($float+0.0000001);
 }

 
/**
 * Get the Hijri conversion from gregorian $d $m $y
 *
 * @param int $d	The gregorian day of month
 * @param int $m	The gregorian month number
 * @param int $y	The gregorian Year
 * @return string a string containing the required date in Hijri form (day arabic_month_as_text year).
 * @author VisualMind (visualmind@php.net).
 * @author sfezz (sfezz@users.sourceforge.net).
 */
function getHijri($d, $m, $y) 
{
	// note: see the $format string to change the form of the Hijri returned

	// manipulated by sfezz to use UTF-8 and to work nicely in phpGedView
	// Hijri dates run according to the moon cycle and loose about 11 days per year.
	// Due to the nature of the moon not being in sync with the sun, it is possible
	// for the Hijri date to loose or gain a day, which is subsequently gained or lost.
	// This means Hijri date calculations are only accurate to day +/- 1.
	
	// The days of the week are the same and start on Sunday. The translation of the dates are as
	// follows: The First, The Second, The Third, The Fourth, The Fifth, The Gathering, The Sabbath.
	// We can include the word 'day' before each of these, but it is usual to leave it out.
	
	// Hijri works on the time since the emigration of the Prophet Mohammad from Mecca to Madina
	// which occured in 622 AD.
	// see here for more: http://en.wikipedia.org/wiki/Islamic_calendar
	
	$use_span=true;

	$arDay = array("Sat"=>"السبت", 
	         "Sun"=>"الاحد", 
	         "Mon"=>"الأثنين", 
	         "Tue"=>"الثلاثاء", 
	         "Wed"=>"الاربعاء", 
	         "Thu"=>"الخميس", 
	         "Fri"=>"الجمعه");
	$ampm=array('am'=>'صبح','pm'=>'مساء'); 

	// -- commented out because the date function will not work on dates < 1970
	// list($d,$m,$y,$dayname,$monthname,$am)=explode(' ',date('d m Y D M a', $timestamp));


	if (($y>1582)||(($y==1582)&&($m>10))||(($y==1582)&&($m==10)&&($d>14))) {
		$jd = _ardInt((1461*($y+4800+ _ardInt(($m-14)/12)))/4);
		$jd += _ardInt((367*($m-2-12*( _ardInt(($m-14)/12))))/12);
		$jd -= _ardInt((3*( _ardInt(($y+4900 + _ardInt(($m-14)/12))/100)))/4);
		$jd +=$d-32075;
	} else 	{
		$jd = 367*$y- _ardInt((7*($y+5001+ _ardInt(($m-9)/7)))/4)+ _ardInt((275*$m)/9)+$d+1729777;
	}
	$l=$jd-1948440+10632;
	$n= _ardInt(($l-1)/10631);
	$l=$l-10631*$n+355;  // Correction: 355 instead of 354
	$j=( _ardInt((10985-$l)/5316))*( _ardInt((50*$l)/17719))+( _ardInt($l/5670))*( _ardInt((43*$l)/15238));
	$l=$l -( _ardInt((30-$j)/15))*( _ardInt((17719*$j)/50))-( _ardInt($j/16))*( _ardInt((15238*$j)/43))+29;
	$m=_ardInt((24*$l)/709);
	$d=$l- _ardInt((709*$m)/24);
	$y=30*$n+$j-30;		
	
	
	$hjMonth = array("محرّم", 
				"صفر", 
				"ربيع الأول", 
				"ربيع الثان", 
				"جمادى الأول", 
				"جمادى الثاني", 
				"رجب", 
				"شعبان", 
				"رمضان", 
				"شوّال", 
				"ذو القعدة", 
				"ذو الحجة"); 
	
	$format = "F/j/Y"; // <------------- Change this to show different forms of the Hijri system
	
	$format=str_replace('j', $d, $format);
	$format=str_replace('d', str_pad($d,2,0,STR_PAD_LEFT), $format);
	//$format=str_replace('l', $arDay[$dayname], $format);
	if (isset($hjMonth[$m-1])) $format=str_replace('F', $hjMonth[$m-1], $format);
	$format=str_replace('m', str_pad($m,2,0,STR_PAD_LEFT), $format);
	$format=str_replace('n', $m, $format);
	$format=str_replace('Y', $y, $format);
	$format=str_replace('y', substr($y,2), $format);
	//$format=str_replace('a', substr($ampm[$am],0,1), $format);
	//$format=str_replace('A', $ampm[$am], $format);

	//$date = date($format, $timestamp);
	return $format;
  }


/**
 * Get the Gregorian from a Hijri date.
 *
 * @param int $d the Hijri day
 * @param int $m the Hijri month
 * @param int $y the Hijri year
 * @return string a string containing the required date in gregorian, (d-m-y)
 * @author VisualMind (visualmind@php.net)
 * @author sfezz (sfezz@users.sourceforge.net)
 */
function dateHijri2Greg($d, $m, $y) 
{
	
	$jd=_ardInt((11*$y+3)/30)+354*$y+30*$m-_ardInt(($m-1)/2)+$d+1948440-386;
	if ($jd> 2299160 ) {
		$l=$jd+68569;
		$n=_ardInt((4*$l)/146097);
		$l=$l-_ardInt((146097*$n+3)/4);
		$i=_ardInt((4000*($l+1))/1461001);
		$l=$l-_ardInt((1461*$i)/4)+31;
		$j=_ardInt((80*$l)/2447);
		$d=$l-_ardInt((2447*$j)/80);
		$l=_ardInt($j/11);
		$m=$j+2-12*$l;
		$y=100*($n-49)+$i+$l;
	} else	{
		$j=$jd+1402;
		$k=_ardInt(($j-1)/1461);
		$l=$j-1461*$k;
		$n=_ardInt(($l-1)/365)-_ardInt($l/1461);
		$i=$l-365*$n+30;
		$j=_ardInt((80*$i)/2447);
		$d=$i-_ardInt((2447*$j)/80);
		$i=_ardInt($j/11);
		$m=$j+2-12*$i;
		$y=4*$k+$n+$i-4716;
	}

	return "$d-$m-$y"; 
} 
	

 
/**
 * Get the Arabic form of a gregorian date
 *
 * @param int $d	The gregorian day of month
 * @param int $m	The gregorian month number
 * @param int $y	The gregorian Year
 * @return string a string containing the required date in Hijri form (day arabic_month_as_text year)
 * @author VisualMind (visualmind@php.net)
 * @author sfezz (sfezz@users.sourceforge.net)
 */
function getArabic($d, $m, $y) 
{

	// This is the same as the Gregorian date, although the names of things are different.

	$use_span=true;

		$arDay = array("Sat"=>"السبت", 
	         "Sun"=>"الاحد", 
	         "Mon"=>"الأثنين", 
	         "Tue"=>"الثلاثاء", 
	         "Wed"=>"الاربعاء", 
	         "Thu"=>"الخميس", 
	         "Fri"=>"الجمعه");
	$ampm=array('am'=>'صبح','pm'=>'مساء'); 

	// -- commented out because the date function will not work on dates < 1970
	// list($d,$m,$y,$dayname,$monthname,$am)=explode(' ',date('d m Y D M a', $timestamp));

	$arMonth=array("ياناير",
			"فبراير",
			"مارس",
			"ابريل",
			"مايو",
			"يونيو",
			"يوليو",
			"اغسطس",
			"سبتمبر",
			"اكتوبر",
			"نوفمبر",
			"ديسمبر");
			
	
	$format = "F/j/Y"; // <------------- Change this to show different forms of the Arabic date

	$format=str_replace('j', $d, $format);
	//$format=str_replace('l', $arDay[$dayname], $format);
	$format=str_replace('F', $arMonth[$m-1], $format);
	$format=str_replace('Y', $y, $format);
	//$format=str_replace('a', substr($ampm[$am],0,1), $format);
	//$format=str_replace('A', $ampm[$am], $format);
    
	return $format;
	//$date = date($format, $timestamp);
	//if ($use_span) return '<span dir="rtl" lang="ar-sa">'.$date.'</span>'; 
	//else return $date;
  }

// optional extra file
if (file_exists($PGV_BASE_DIRECTORY . "functions.extra.php")) require $PGV_BASE_DIRECTORY . "functions.extra.php";
?>