'. /* I18N: Abbreviation for "Sosa-Stradonitz number". This is a person's surname, so may need transliterating into non-latin alphabets. */ WT_I18N::translate('Sosa'). '
';
$html .= '
SOSA
';
$html .= '
'. WT_Gedcom_Tag::getLabel('BIRT'). '
';
$html .= '
SORT_BIRT
';
$html .= '
';
$html .= '
'. WT_Gedcom_Tag::getLabel('PLAC'). '
';
$html .= '
';
$html .= '
NCHI
';
$html .= '
'. WT_Gedcom_Tag::getLabel('DEAT'). '
';
$html .= '
SORT_DEAT
';
$html .= '
';
$html .= '
'. WT_Gedcom_Tag::getLabel('AGE'). '
';
$html .= '
AGE
';
$html .= '
'. WT_Gedcom_Tag::getLabel('PLAC'). '
';
$html .= '
'. WT_Gedcom_Tag::getLabel('CHAN'). '
';
$html .= '
SEX
';
$html .= '
BIRT
';
$html .= '
DEAT
';
$html .= '
TREE
';
$html .= '
';
//-- table body
$html .= '';
$d100y=new WT_Date(date('Y')-100); // 100 years ago
$dateY = date('Y');
$unique_indis=array(); // Don't double-count indis with multiple names.
foreach ($datalist as $key=>$value) {
if (is_object($value)) { // Array of objects
$person=$value;
} elseif (!is_array($value)) { // Array of IDs
$person = WT_Person::getInstance($value);
} else { // Array of search results
$gid = $key;
if (isset($value['gid'])) $gid = $value['gid']; // from indilist
if (isset($value[4])) $gid = $value[4]; // from indilist ALL
$person = WT_Person::getInstance($gid);
}
if (is_null($person)) continue;
if ($person->getType() !== 'INDI') continue;
if (!$person->canDisplayName()) {
continue;
}
//-- place filtering
if ($option=='BIRT_PLAC' && strstr($person->getBirthPlace(), $filter)===false) continue;
if ($option=='DEAT_PLAC' && strstr($person->getDeathPlace(), $filter)===false) continue;
$html .= '
';
// Dummy column to match colspan in header
$html .= '
';
//-- GIVN/SURN
// Use "AAAA" as a separator (instead of ",") as JavaScript.localeCompare() ignores
// punctuation and "ANN,ROACH" would sort after "ANNE,ROACH", instead of before it.
// Similarly, @N.N. would sort as NN.
$html .= '
';
//-- Age at death
if ($birth_dates[0]->isOK() && $death_dates[0]->isOK()) {
$age=WT_Date::GetAgeYears($birth_dates[0], $death_dates[0]);
if (!isset($unique_indis[$person->getXref()])) {
$deat_by_age[max(0, min($max_age, $age))] .= $person->getSex();
}
} else {
$age='';
}
// Need both display and sortable age
$html .= '
';
//-- table body
$html .= '';
$num = 0;
$d100y=new WT_Date(date('Y')-100); // 100 years ago
foreach ($datalist as $key => $value) {
if (is_object($value)) { // Array of objects
$family=$value;
} elseif (!is_array($value)) { // Array of IDs
$family=WT_Family::getInstance($value);
} else { // Array of search results
$gid = "";
if (isset($value['gid'])) $gid = $value['gid'];
if (isset($value['gedcom'])) $family = new WT_Family($value['gedcom']);
else $family = WT_Family::getInstance($gid);
}
if (is_null($family)) continue;
if ($family->getType() !== 'FAM') continue;
//-- Retrieve husband and wife
$husb = $family->getHusband();
if (is_null($husb)) $husb = new WT_Person('');
$wife = $family->getWife();
if (is_null($wife)) $wife = new WT_Person('');
if (!$family->canDisplayDetails()) {
continue;
}
//-- place filtering
if ($option=='MARR_PLAC' && strstr($family->getMarriagePlace(), $filter)===false) continue;
$html .= '
';
//-- Husband name(s)
$html .= '
';
foreach ($husb->getAllNames() as $num=>$name) {
if ($name['type']=='NAME') {
$title='';
} else {
$title='title="'.strip_tags(WT_Gedcom_Tag::getLabel($name['type'], $husb)).'"';
}
if ($num==$husb->getPrimaryName()) {
$class=' class="name2"';
$sex_image=$husb->getSexImage();
list($surn, $givn)=explode(',', $name['sort']);
} else {
$class='';
$sex_image='';
}
// Only show married names if they are the name we are filtering by.
if ($name['type']!='_MARNM' || $num==$husb->getPrimaryName()) {
$html .= ''. highlight_search_hits($name['full']). ''. $sex_image. ' ';
}
}
// Husband parents
$html .= $husb->getPrimaryParentsNames('parents_'.$table_id.' details1', 'none');
$html .= '
';
// Dummy column to match colspan in header
$html .= '
';
//-- Husb GIVN
// Use "AAAA" as a separator (instead of ",") as JavaScript.localeCompare() ignores
// punctuation and "ANN,ROACH" would sort after "ANNE,ROACH", instead of before it.
// Similarly, @N.N. would sort as NN.
$html .= '
';
foreach ($wife->getAllNames() as $num=>$name) {
if ($name['type']=='NAME') {
$title='';
} else {
$title='title="'.strip_tags(WT_Gedcom_Tag::getLabel($name['type'], $wife)).'"';
}
if ($num==$wife->getPrimaryName()) {
$class=' class="name2"';
$sex_image=$wife->getSexImage();
list($surn, $givn)=explode(',', $name['sort']);
} else {
$class='';
$sex_image='';
}
// Only show married names if they are the name we are filtering by.
if ($name['type']!='_MARNM' || $num==$wife->getPrimaryName()) {
$html .= ''. highlight_search_hits($name['full']). ''. $sex_image. ' ';
}
}
// Wife parents
$html .= $wife->getPrimaryParentsNames("parents_".$table_id." details1", 'none');
$html .= '
';
// Dummy column to match colspan in header
$html .= '
';
//-- Wife GIVN
//-- Husb GIVN
// Use "AAAA" as a separator (instead of ",") as JavaScript.localeCompare() ignores
// punctuation and "ANN,ROACH" would sort after "ANNE,ROACH", instead of before it.
// Similarly, @N.N. would sort as NN.
$html .= '
';
}
// Print a table of surnames, for the top surnames block, the indi/fam lists, etc.
// $surnames - array (of SURN, of array of SPFX_SURN, of array of PID)
// $type - "indilist.php" (counts of individuals) or "famlist.php" (counts of spouses)
function format_surname_table($surnames, $script) {
global $controller;
$controller
->addExternalJavaScript(WT_STATIC_URL.'js/jquery/jquery.dataTables.min.js')
->addInlineJavaScript('
jQuery.fn.dataTableExt.oSort["num-asc" ]=function(a,b) {a=parseFloat(a); b=parseFloat(b); return (ab ? 1 : 0);};
jQuery.fn.dataTableExt.oSort["num-desc"]=function(a,b) {a=parseFloat(a); b=parseFloat(b); return (a>b) ? -1 : (a'.
'
'.WT_Gedcom_Tag::getLabel('SURN').'
'.
'
'.
'
'.$col_heading.'
'.
'
'.
'';
$tbody='';
$unique_surn=array();
$unique_indi=array();
$n=0; // We have already sorted the data - use this as a surrogate sort key
foreach ($surnames as $surn=>$surns) {
// Each surname links back to the indi/fam surname list
if ($surn) {
$url=$script.'?surname='.rawurlencode($surn).'&ged='.WT_GEDURL;
} else {
$url=$script.'?alpha=,&ged='.WT_GEDURL;
}
// Row counter
$tbody.='
';
// Surname
$tbody.='
';
if (count($surns)==1) {
// Single surname variant
foreach ($surns as $spfxsurn=>$indis) {
$tbody.=''.htmlspecialchars($spfxsurn).'';
$unique_surn[$spfxsurn]=true;
foreach (array_keys($indis) as $pid) {
$unique_indi[$pid]=true;
}
}
} else {
// Multiple surname variants, e.g. von Groot, van Groot, van der Groot, etc.
foreach ($surns as $spfxsurn=>$indis) {
$tbody.=''.htmlspecialchars($spfxsurn).' ';
$unique_surn[$spfxsurn]=true;
foreach (array_keys($indis) as $pid) {
$unique_indi[$pid]=true;
}
}
}
$tbody.='
';
// Sort column for name
$tbody.='
'.$n++.'
';
// Surname count
$tbody.='
';
if (count($surns)==1) {
// Single surname variant
foreach ($surns as $spfxsurn=>$indis) {
$subtotal=count($indis);
$tbody.= WT_I18N::number($subtotal);
}
} else {
$subtotal=0;
// Multiple surname variants, e.g. von Groot, van Groot, van der Groot, etc.
foreach ($surns as $spfxsurn=>$indis) {
$subtotal+=count($indis);
$tbody.=WT_I18N::number(count($indis)).' ';
}
$tbody.=WT_I18N::number($subtotal);
}
$tbody.='
';
// add hidden numeric sort column
$tbody.='
'. $subtotal. '
';
}
return
'
'.
''.$thead.''.
''.$tbody.''.
'
';
}
// Print a tagcloud of surnames.
// @param $surnames array (of SURN, of array of SPFX_SURN, of array of PID)
// @param $type string, indilist or famlist
// @param $totals, boolean, show totals after each name
function format_surname_tagcloud($surnames, $script, $totals) {
$cloud=new Zend_Tag_Cloud(
array(
'tagDecorator'=>array(
'decorator'=>'HtmlTag',
'options'=>array(
'htmlTags'=>array(),
'fontSizeUnit'=>'%',
'minFontSize'=>80,
'maxFontSize'=>250
)
),
'cloudDecorator'=>array(
'decorator'=>'HtmlCloud',
'options'=>array(
'htmlTags'=>array(
'div'=>array(
'class'=>'tag_cloud'
)
)
)
)
)
);
foreach ($surnames as $surn=>$surns) {
foreach ($surns as $spfxsurn=>$indis) {
$cloud->appendTag(array(
'title'=>$totals ? WT_I18N::translate('%1$s (%2$d)', $spfxsurn, count($indis)) : $spfxsurn,
'weight'=>count($indis),
'params'=>array(
'url'=>$surn ?
$script.'?surname='.urlencode($surn).'&ged='.WT_GEDURL :
$script.'?alpha=,&ged='.WT_GEDURL
)
));
}
}
return (string)$cloud;
}
// Print a list of surnames.
// @param $surnames array (of SURN, of array of SPFX_SURN, of array of PID)
// @param $style, 1=bullet list, 2=semicolon-separated list, 3=tabulated list with up to 4 columns
// @param $totals, boolean, show totals after each name
// @param $type string, indilist or famlist
function format_surname_list($surnames, $style, $totals, $script) {
global $GEDCOM;
$html=array();
foreach ($surnames as $surn=>$surns) {
// Each surname links back to the indilist
if ($surn) {
$url=$script.'?surname='.urlencode($surn).'&ged='.rawurlencode($GEDCOM);
} else {
$url=$script.'?alpha=,&ged='.rawurlencode($GEDCOM);
}
// If all the surnames are just case variants, then merge them into one
// Comment out this block if you want SMITH listed separately from Smith
$first_spfxsurn=null;
foreach ($surns as $spfxsurn=>$indis) {
if ($first_spfxsurn) {
if (utf8_strtoupper($spfxsurn)==utf8_strtoupper($first_spfxsurn)) {
$surns[$first_spfxsurn]=array_merge($surns[$first_spfxsurn], $surns[$spfxsurn]);
unset ($surns[$spfxsurn]);
}
} else {
$first_spfxsurn=$spfxsurn;
}
}
$subhtml=''.htmlspecialchars(implode(', ', array_keys($surns))).'';
if ($totals) {
$subtotal=0;
foreach ($surns as $spfxsurn=>$indis) {
$subtotal+=count($indis);
}
$subhtml.=' ('.$subtotal.')';
}
$html[]=$subhtml;
}
switch ($style) {
case 1:
return '
'.implode('
', $html).'
';
case 2:
return implode('; ', $html);
case 3:
$i = 0;
$count = count($html);
$count_indi = 0;
$col = 1;
if ($count>36) $col=4;
else if ($count>18) $col=3;
else if ($count>6) $col=2;
$newcol=ceil($count/$col);
$html2 ='
';
$html2.='
';
foreach ($html as $surn=>$surns) {
$html2.= $surns.' ';
$i++;
if ($i==$newcol && $i<$count) {
$html2.='
';
$newcol=$i+ceil($count/$col);
}
}
$html2.='
';
return $html2;
}
}
// print a list of recent changes
function print_changes_list($change_ids, $sort) {
$n = 0;
$arr=array();
foreach ($change_ids as $change_id) {
$record = WT_GedcomRecord::getInstance($change_id);
if (!$record || !$record->canDisplayDetails()) {
continue;
}
// setup sorting parameters
$arr[$n]['record'] = $record;
$arr[$n]['jd'] = ($sort == 'name') ? 1 : $n;
$arr[$n]['anniv'] = $record->LastChangeTimestamp(false, true);
$arr[$n++]['fact'] = $record->getSortName(); // in case two changes have same timestamp
}
switch ($sort) {
case 'name':
uasort($arr, 'event_sort_name');
break;
case 'date_asc':
uasort($arr, 'event_sort');
$arr = array_reverse($arr);
break;
case 'date_desc':
uasort($arr, 'event_sort');
}
$return = '';
foreach ($arr as $value) {
$return .= '' . $value['record']->getFullName() . '';
$return .= '
';
if ($value['record']->getType() == 'INDI') {
if ($value['record']->getAddName()) {
$return .= '' . $value['record']->getAddName() . '';
}
}
$return .= /* I18N: [a record was] Changed on by */ WT_I18N::translate('Changed on %1$s by %2$s', $value['record']->LastChangeTimestamp(false), $value['record']->LastChangeUser());
$return .= '
';
}
// Print a final summary message about restricted/filtered facts
$summary = "";
if ($endjd==WT_CLIENT_JD) {
// We're dealing with the Today's Events block
if ($output==0) {
if ($filter==0) {
$summary = WT_I18N::translate('No events exist for today.');
} else {
$summary = WT_I18N::translate('No events for living people exist for today.');
}
}
} else {
// We're dealing with the Upcoming Events block
if ($output==0) {
if ($filter==0) {
if ($endjd==$startjd) {
$summary = WT_I18N::translate('No events exist for tomorrow.');
} else {
// I18N: tanslation for %d==1 is unused; it is translated separately as tomorrow
$summary = WT_I18N::plural('No events exist for the next %d day.', 'No events exist for the next %d days.', $endjd-$startjd+1, $endjd-$startjd+1);
}
} else {
if ($endjd==$startjd) {
$summary = WT_I18N::translate('No events for living people exist for tomorrow.');
} else {
// I18N: tanslation for %d==1 is unused; it is translated separately as tomorrow
$summary = WT_I18N::plural('No events for living people exist for the next %d day.', 'No events for living people exist for the next %d days.', $endjd-$startjd+1, $endjd-$startjd+1);
}
}
}
}
if ($summary!="") {
$return .= ''. $summary. '';
}
return $return;
}
/**
* print a list of events
*
* This performs the same function as print_events_table(), but formats the output differently.
*/
function print_events_list($startjd, $endjd, $events='BIRT MARR DEAT', $only_living=false, $sort_by='anniv') {
// Did we have any output? Did we skip anything?
$output = 0;
$filter = 0;
$return = '';
$filtered_events = array();
foreach (get_events_list($startjd, $endjd, $events) as $value) {
$record = WT_GedcomRecord::getInstance($value['id']);
//-- only living people ?
if ($only_living) {
if ($record->getType()=="INDI" && $record->isDead()) {
$filter ++;
continue;
}
if ($record->getType()=="FAM") {
$husb = $record->getHusband();
if (is_null($husb) || $husb->isDead()) {
$filter ++;
continue;
}
$wife = $record->getWife();
if (is_null($wife) || $wife->isDead()) {
$filter ++;
continue;
}
}
}
// Privacy
if (!$record->canDisplayDetails() || !canDisplayFact($record->getXref(), $record->getGedId(), $value['factrec'])) {
continue;
}
$output ++;
$value['name'] = $record->getFullName();
$value['url'] = $record->getHtmlUrl();
if ($record->getType()=="INDI") {
$value['sex'] = $record->getSexImage();
} else {
$value['sex'] = '';
}
$filtered_events[] = $value;
}
// Now we've filtered the list, we can sort by event, if required
switch ($sort_by) {
case 'anniv':
uasort($filtered_events, 'event_sort');
break;
case 'alpha':
uasort($filtered_events, 'event_sort_name');
break;
}
foreach ($filtered_events as $value) {
$return .= "".$value['name']."".$value['sex'];
$return .= "
";
$return .= WT_Gedcom_Tag::getLabel($value['fact']).' - '.$value['date']->Display(true);
if ($value['anniv']!=0) $return .= " (" . WT_I18N::translate('%s year anniversary', $value['anniv']).")";
if (!empty($value['plac'])) $return .= " - ".$value['plac']."";
$return .= "
";
}
// Print a final summary message about restricted/filtered facts
$summary = "";
if ($endjd==WT_CLIENT_JD) {
// We're dealing with the Today's Events block
if ($output==0) {
if ($filter==0) {
$summary = WT_I18N::translate('No events exist for today.');
} else {
$summary = WT_I18N::translate('No events for living people exist for today.');
}
}
} else {
// We're dealing with the Upcoming Events block
if ($output==0) {
if ($filter==0) {
if ($endjd==$startjd) {
$summary = WT_I18N::translate('No events exist for tomorrow.');
} else {
// I18N: tanslation for %d==1 is unused; it is translated separately as tomorrow
$summary = WT_I18N::plural('No events exist for the next %d day.', 'No events exist for the next %d days.', $endjd-$startjd+1, $endjd-$startjd+1);
}
} else {
if ($endjd==$startjd) {
$summary = WT_I18N::translate('No events for living people exist for tomorrow.');
} else {
// I18N: tanslation for %d==1 is unused; it is translated separately as tomorrow
$summary = WT_I18N::plural('No events for living people exist for the next %d day.', 'No events for living people exist for the next %d days.', $endjd-$startjd+1, $endjd-$startjd+1);
}
}
}
}
if ($summary) {
$return .= "". $summary. "";
}
return $return;
}
// print a chart by age using Google chart API
function print_chart_by_age($data, $title) {
$count = 0;
$agemax = 0;
$vmax = 0;
$avg = 0;
foreach ($data as $age=>$v) {
$n = strlen($v);
$vmax = max($vmax, $n);
$agemax = max($agemax, $age);
$count += $n;
$avg += $age*$n;
}
if ($count<1) return;
$avg = round($avg/$count);
$chart_url = "http://chart.apis.google.com/chart?cht=bvs"; // chart type
$chart_url .= "&chs=725x150"; // size
$chart_url .= "&chbh=3,2,2"; // bvg : 4,1,2
$chart_url .= "&chf=bg,s,FFFFFF99"; //background color
$chart_url .= "&chco=0000FF,FFA0CB,FF0000"; // bar color
$chart_url .= "&chdl=".rawurlencode(WT_I18N::translate('Males'))."|".rawurlencode(WT_I18N::translate('Females'))."|".rawurlencode(WT_I18N::translate('Average age').": ".$avg); // legend & average age
$chart_url .= "&chtt=".rawurlencode($title); // title
$chart_url .= "&chxt=x,y,r"; // axis labels specification
$chart_url .= "&chm=V,FF0000,0,".($avg-0.3).",1"; // average age line marker
$chart_url .= "&chxl=0:|"; // label
for ($age=0; $age<=$agemax; $age+=5) {
$chart_url .= $age."|||||"; // x axis
}
$chart_url .= "|1:||".rawurlencode(WT_I18N::percentage($vmax/$count)); // y axis
$chart_url .= "|2:||";
$step = $vmax;
for ($d=floor($vmax); $d>0; $d--) {
if ($vmax<($d*10+1) && fmod($vmax, $d)==0) $step = $d;
}
if ($step==floor($vmax)) {
for ($d=floor($vmax-1); $d>0; $d--) {
if (($vmax-1)<($d*10+1) && fmod(($vmax-1), $d)==0) $step = $d;
}
}
for ($n=$step; $n<$vmax; $n+=$step) {
$chart_url .= $n."|";
}
$chart_url .= rawurlencode($vmax." / ".$count); // r axis
$chart_url .= "&chg=100,".round(100*$step/$vmax, 1).",1,5"; // grid
$chart_url .= "&chd=s:"; // data : simple encoding from A=0 to 9=61
$CHART_ENCODING61 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for ($age=0; $age<=$agemax; $age++) {
$chart_url .= $CHART_ENCODING61[floor(substr_count($data[$age], "M")*61/$vmax)];
}
$chart_url .= ",";
for ($age=0; $age<=$agemax; $age++) {
$chart_url .= $CHART_ENCODING61[floor(substr_count($data[$age], "F")*61/$vmax)];
}
$html = '';
return $html;
}
// print a chart by decade using Google chart API
function print_chart_by_decade($data, $title) {
$count = 0;
$vmax = 0;
foreach ($data as $age=>$v) {
$n = strlen($v);
$vmax = max($vmax, $n);
$count += $n;
}
if ($count<1) return;
$chart_url = "http://chart.apis.google.com/chart?cht=bvs"; // chart type
$chart_url .= "&chs=360x150"; // size
$chart_url .= "&chbh=3,3"; // bvg : 4,1,2
$chart_url .= "&chf=bg,s,FFFFFF99"; //background color
$chart_url .= "&chco=0000FF,FFA0CB"; // bar color
$chart_url .= "&chtt=".rawurlencode($title); // title
$chart_url .= "&chxt=x,y,r"; // axis labels specification
$chart_url .= "&chxl=0:|<|||"; // <1570
for ($y=1600; $y<2030; $y+=50) {
$chart_url .= $y."|||||"; // x axis
}
$chart_url .= "|1:||".rawurlencode(WT_I18N::percentage($vmax/$count)); // y axis
$chart_url .= "|2:||";
$step = $vmax;
for ($d=floor($vmax); $d>0; $d--) {
if ($vmax<($d*10+1) && fmod($vmax, $d)==0) $step = $d;
}
if ($step==floor($vmax)) {
for ($d=floor($vmax-1); $d>0; $d--) {
if (($vmax-1)<($d*10+1) && fmod(($vmax-1), $d)==0) $step = $d;
}
}
for ($n=$step; $n<$vmax; $n+=$step) {
$chart_url .= $n."|";
}
$chart_url .= rawurlencode($vmax." / ".$count); // r axis
$chart_url .= "&chg=100,".round(100*$step/$vmax, 1).",1,5"; // grid
$chart_url .= "&chd=s:"; // data : simple encoding from A=0 to 9=61
$CHART_ENCODING61 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for ($y=1570; $y<2030; $y+=10) {
$chart_url .= $CHART_ENCODING61[floor(substr_count($data[$y], "M")*61/$vmax)];
}
$chart_url .= ",";
for ($y=1570; $y<2030; $y+=10) {
$chart_url .= $CHART_ENCODING61[floor(substr_count($data[$y], "F")*61/$vmax)];
}
$html = '';
return $html;
}