function format_fam_table($datalist) { global $GEDCOM, $SHOW_LAST_CHANGE, $SEARCH_SPIDER, $controller; $table_id = 'table-fam-' . Uuid::uuid4(); // lists requires a unique ID in case there are multiple lists per page $controller->addExternalJavascript(WT_JQUERY_DATATABLES_URL)->addInlineJavascript(' jQuery.fn.dataTableExt.oSort["unicode-asc" ]=function(a,b) {return a.replace(/<[^<]*>/, "").localeCompare(b.replace(/<[^<]*>/, ""))}; jQuery.fn.dataTableExt.oSort["unicode-desc"]=function(a,b) {return b.replace(/<[^<]*>/, "").localeCompare(a.replace(/<[^<]*>/, ""))}; jQuery("#' . $table_id . '").dataTable( { dom: \'<"H"<"filtersH_' . $table_id . '"><"dt-clear">pf<"dt-clear">irl>t<"F"pl<"dt-clear"><"filtersF_' . $table_id . '">>\', ' . WT_I18N::datatablesI18N() . ', jQueryUI: true, autoWidth: false, processing: true, retrieve: true, columns: [ /* 0 husb givn */ {dataSort: 2}, /* 1 husb surn */ {dataSort: 3}, /* 2 GIVN,SURN */ {type: "unicode", visible: false}, /* 3 SURN,GIVN */ {type: "unicode", visible: false}, /* 4 age */ {dataSort: 5, class: "center"}, /* 5 AGE */ {type: "num", visible: false}, /* 6 wife givn */ {dataSort: 8}, /* 7 wife surn */ {dataSort: 9}, /* 8 GIVN,SURN */ {type: "unicode", visible: false}, /* 9 SURN,GIVN */ {type: "unicode", visible: false}, /* 10 age */ {dataSort: 11, class: "center"}, /* 11 AGE */ {type: "num", visible: false}, /* 12 marr date */ {dataSort: 13}, /* 13 MARR:DATE */ {visible: false}, /* 14 anniv */ {dataSort: 13, class: "center"}, /* 15 marr plac */ {type: "unicode"}, /* 16 children */ {dataSort: 17, class: "center"}, /* 17 NCHI */ {type: "num", visible: false}, /* 18 CHAN */ {dataSort: 19, visible: ' . ($SHOW_LAST_CHANGE ? 'true' : 'false') . '}, /* 19 CHAN_sort */ {visible: false}, /* 20 MARR */ {visible: false}, /* 21 DEAT */ {visible: false}, /* 22 TREE */ {visible: false} ], sorting: [[1, "asc"]], displayLength: 20, pagingType: "full_numbers" }); jQuery("#' . $table_id . '") /* Hide/show parents */ .on("click", ".btn-toggle-parents", function() { jQuery(this).toggleClass("ui-state-active"); jQuery(".parents", jQuery(this).closest("table").DataTable().rows().nodes()).slideToggle(); }) /* Hide/show statistics */ .on("click", ".btn-toggle-statistics", function() { jQuery(this).toggleClass("ui-state-active"); jQuery("#fam_list_table-charts_' . $table_id . '").slideToggle(); }) /* Filter buttons in table header */ .on("click", "button[data-filter-column]", function() { var btn = $(this); // De-activate the other buttons in this button group btn.siblings().removeClass("ui-state-active"); // Apply (or clear) this filter var col = jQuery("#' . $table_id . '").DataTable().column(btn.data("filter-column")); if (btn.hasClass("ui-state-active")) { btn.removeClass("ui-state-active"); col.search("").draw(); } else { btn.addClass("ui-state-active"); col.search(btn.data("filter-value")).draw(); } }); jQuery(".fam-list").css("visibility", "visible"); jQuery(".loading-image").css("display", "none"); '); $stats = new WT_Stats($GEDCOM); $max_age = max($stats->oldestMarriageMaleAge(), $stats->oldestMarriageFemaleAge()) + 1; //-- init chart data for ($age = 0; $age <= $max_age; $age++) { $marr_by_age[$age] = ''; } for ($year = 1550; $year < 2030; $year += 10) { $birt_by_decade[$year] = ''; $marr_by_decade[$year] = ''; } $html = ' <div class="loading-image"> </div> <div class="fam-list"> <table id="' . $table_id . '"> <thead> <tr> <th colspan="23"> <div class="btn-toolbar"> <div class="btn-group"> <button type="button" data-filter-column="21" data-filter-value="N" class="ui-state-default" title="' . WT_I18N::translate('Show individuals who are alive or couples where both partners are alive.') . '" > ' . WT_I18N::translate('Both alive') . ' </button> <button type="button" data-filter-column="21" data-filter-value="W" class="ui-state-default" title="' . WT_I18N::translate('Show couples where only the female partner is deceased.') . '" > ' . WT_I18N::translate('Widower') . ' </button> <button type="button" data-filter-column="21" data-filter-value="H" class="ui-state-default" title="' . WT_I18N::translate('Show couples where only the male partner is deceased.') . '" > ' . WT_I18N::translate('Widow') . ' </button> <button type="button" data-filter-column="21" data-filter-value="Y" class="ui-state-default" title="' . WT_I18N::translate('Show individuals who are dead or couples where both partners are deceased.') . '" > ' . WT_I18N::translate('Both dead') . ' </button> </div> <div class="btn-group"> <button type="button" data-filter-column="22" data-filter-value="R" class="ui-state-default" title="' . WT_I18N::translate('Show “roots” couples or individuals. These individuals may also be called “patriarchs”. They are individuals who have no parents recorded in the database.') . '" > ' . WT_I18N::translate('Roots') . ' </button> <button type="button" data-filter-column="22" data-filter-value="L" class="ui-state-default" title="' . WT_I18N::translate('Show “leaves” couples or individuals. These are individuals who are alive but have no children recorded in the database.') . '" > ' . WT_I18N::translate('Leaves') . ' </button> </div> <div class="btn-group"> <button type="button" data-filter-column="20" data-filter-value="U" class="ui-state-default" title="' . WT_I18N::translate('Show couples with an unknown marriage date.') . '" > ' . WT_Gedcom_Tag::getLabel('MARR') . ' </button> <button type="button" data-filter-column="20" data-filter-value="YES" class="ui-state-default" title="' . WT_I18N::translate('Show couples who married more than 100 years ago.') . '" > ' . WT_Gedcom_Tag::getLabel('MARR') . '>100 </button> <button type="button" data-filter-column="20" data-filter-value="Y100" class="ui-state-default" title="' . WT_I18N::translate('Show couples who married within the last 100 years.') . '" > ' . WT_Gedcom_Tag::getLabel('MARR') . '<=100 </button> <button type="button" data-filter-column="20" data-filter-value="D" class="ui-state-default" title="' . WT_I18N::translate('Show divorced couples.') . '" > ' . WT_Gedcom_Tag::getLabel('DIV') . ' </button> <button type="button" data-filter-column="20" data-filter-value="M" class="ui-state-default" title="' . WT_I18N::translate('Show couples where either partner married more than once.') . '" > ' . WT_I18N::translate('Multiple marriages') . ' </button> </div> </div> </th> </tr> <tr> <th>' . WT_Gedcom_Tag::getLabel('GIVN') . '</th> <th>' . WT_Gedcom_Tag::getLabel('SURN') . '</th> <th>HUSB:GIVN_SURN</th> <th>HUSB:SURN_GIVN</th> <th>' . WT_Gedcom_Tag::getLabel('AGE') . '</th> <th>AGE</th> <th>' . WT_Gedcom_Tag::getLabel('GIVN') . '</th> <th>' . WT_Gedcom_Tag::getLabel('SURN') . '</th> <th>WIFE:GIVN_SURN</th> <th>WIFE:SURN_GIVN</th> <th>' . WT_Gedcom_Tag::getLabel('AGE') . '</th> <th>AGE</th> <th>' . WT_Gedcom_Tag::getLabel('MARR') . '</th> <th>MARR:DATE</th> <th><i class="icon-reminder" title="' . WT_I18N::translate('Anniversary') . '"></i></th> <th>' . WT_Gedcom_Tag::getLabel('PLAC') . '</th> <th><i class="icon-children" title="' . WT_I18N::translate('Children') . '"></i></th> <th>NCHI</th> <th' . ($SHOW_LAST_CHANGE ? '' : '') . '>' . WT_Gedcom_Tag::getLabel('CHAN') . '</th> <th' . ($SHOW_LAST_CHANGE ? '' : '') . '>CHAN</th> <th>MARR</th> <th>DEAT</th> <th>TREE</th> </tr> </thead> <tfoot> <tr> <th colspan="23"> <div class="btn-toolbar"> <div class="btn-group"> <button type="button" class="ui-state-default btn-toggle-parents"> ' . WT_I18N::translate('Show parents') . ' </button> <button type="button" class="ui-state-default btn-toggle-statistics"> ' . WT_I18N::translate('Show statistics charts') . ' </button> </div> </div> </th> </tr> </tfoot> <tbody>'; $d100y = new WT_Date(date('Y') - 100); // 100 years ago foreach ($datalist as $family) { //-- Retrieve husband and wife $husb = $family->getHusband(); if (is_null($husb)) { $husb = new WT_Individual('H', '0 @H@ INDI', null, WT_GED_ID); } $wife = $family->getWife(); if (is_null($wife)) { $wife = new WT_Individual('W', '0 @W@ INDI', null, WT_GED_ID); } if (!$family->canShow()) { continue; } if ($family->isNew()) { $class = ' class="new"'; } elseif ($family->isOld()) { $class = ' class="old"'; } else { $class = ''; } $html .= '<tr' . $class . '>'; //-- Husband name(s) $html .= '<td colspan="2">'; 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 .= '<a ' . $title . ' href="' . $family->getHtmlUrl() . '"' . $class . '>' . highlight_search_hits($name['full']) . '</a>' . $sex_image . '<br>'; } } // Husband parents $html .= $husb->getPrimaryParentsNames('parents details1', 'none'); $html .= '</td>'; // Dummy column to match colspan in header $html .= '<td style="display:none;"></td>'; //-- 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 .= '<td>' . WT_Filter::escapeHtml(str_replace('@P.N.', 'AAAA', $givn)) . 'AAAA' . WT_Filter::escapeHtml(str_replace('@N.N.', 'AAAA', $surn)) . '</td>'; $html .= '<td>' . WT_Filter::escapeHtml(str_replace('@N.N.', 'AAAA', $surn)) . 'AAAA' . WT_Filter::escapeHtml(str_replace('@P.N.', 'AAAA', $givn)) . '</td>'; $mdate = $family->getMarriageDate(); //-- Husband age $hdate = $husb->getBirthDate(); if ($hdate->isOK() && $mdate->isOK()) { if ($hdate->gregorianYear() >= 1550 && $hdate->gregorianYear() < 2030) { $birt_by_decade[(int) ($hdate->gregorianYear() / 10) * 10] .= $husb->getSex(); } $hage = WT_Date::getAge($hdate, $mdate, 0); if ($hage >= 0 && $hage <= $max_age) { $marr_by_age[$hage] .= $husb->getSex(); } } $html .= '<td>' . WT_Date::getAge($hdate, $mdate, 2) . '</td><td>' . WT_Date::getAge($hdate, $mdate, 1) . '</td>'; //-- Wife name(s) $html .= '<td colspan="2">'; 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 .= '<a ' . $title . ' href="' . $family->getHtmlUrl() . '"' . $class . '>' . highlight_search_hits($name['full']) . '</a>' . $sex_image . '<br>'; } } // Wife parents $html .= $wife->getPrimaryParentsNames('parents details1', 'none'); $html .= '</td>'; // Dummy column to match colspan in header $html .= '<td style="display:none;"></td>'; //-- 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 .= '<td>' . WT_Filter::escapeHtml(str_replace('@P.N.', 'AAAA', $givn)) . 'AAAA' . WT_Filter::escapeHtml(str_replace('@N.N.', 'AAAA', $surn)) . '</td>'; $html .= '<td>' . WT_Filter::escapeHtml(str_replace('@N.N.', 'AAAA', $surn)) . 'AAAA' . WT_Filter::escapeHtml(str_replace('@P.N.', 'AAAA', $givn)) . '</td>'; $mdate = $family->getMarriageDate(); //-- Wife age $wdate = $wife->getBirthDate(); if ($wdate->isOK() && $mdate->isOK()) { if ($wdate->gregorianYear() >= 1550 && $wdate->gregorianYear() < 2030) { $birt_by_decade[(int) ($wdate->gregorianYear() / 10) * 10] .= $wife->getSex(); } $wage = WT_Date::getAge($wdate, $mdate, 0); if ($wage >= 0 && $wage <= $max_age) { $marr_by_age[$wage] .= $wife->getSex(); } } $html .= '<td>' . WT_Date::getAge($wdate, $mdate, 2) . '</td><td>' . WT_Date::getAge($wdate, $mdate, 1) . '</td>'; //-- Marriage date $html .= '<td>'; if ($marriage_dates = $family->getAllMarriageDates()) { foreach ($marriage_dates as $n => $marriage_date) { if ($n) { $html .= '<br>'; } $html .= '<div>' . $marriage_date->Display(!$SEARCH_SPIDER) . '</div>'; } if ($marriage_dates[0]->gregorianYear() >= 1550 && $marriage_dates[0]->gregorianYear() < 2030) { $marr_by_decade[(int) ($marriage_dates[0]->gregorianYear() / 10) * 10] .= $husb->getSex() . $wife->getSex(); } } elseif ($family->getFacts('_NMR')) { $html .= WT_I18N::translate('no'); } elseif ($family->getFacts('MARR')) { $html .= WT_I18N::translate('yes'); } else { $html .= ' '; } $html .= '</td>'; //-- Event date (sortable)hidden by datatables code $html .= '<td>'; if ($marriage_dates) { $html .= $marriage_date->JD(); } else { $html .= 0; } $html .= '</td>'; //-- Marriage anniversary $html .= '<td>' . WT_Date::getAge($mdate, null, 2) . '</td>'; //-- Marriage place $html .= '<td>'; foreach ($family->getAllMarriagePlaces() as $n => $marriage_place) { $tmp = new WT_Place($marriage_place, WT_GED_ID); if ($n) { $html .= '<br>'; } if ($SEARCH_SPIDER) { $html .= $tmp->getShortName(); } else { $html .= '<a href="' . $tmp->getURL() . '" title="' . strip_tags($tmp->getFullName()) . '">'; $html .= highlight_search_hits($tmp->getShortName()) . '</a>'; } } $html .= '</td>'; //-- Number of children $nchi = $family->getNumberOfChildren(); $html .= '<td>' . WT_I18N::number($nchi) . '</td><td>' . $nchi . '</td>'; //-- Last change if ($SHOW_LAST_CHANGE) { $html .= '<td>' . $family->LastChangeTimestamp() . '</td>'; } else { $html .= '<td> </td>'; } //-- Last change hidden sort column if ($SHOW_LAST_CHANGE) { $html .= '<td>' . $family->LastChangeTimestamp(true) . '</td>'; } else { $html .= '<td> </td>'; } //-- Sorting by marriage date $html .= '<td>'; if (!$family->canShow() || !$mdate->isOK()) { $html .= 'U'; } else { if (WT_Date::Compare($mdate, $d100y) > 0) { $html .= 'Y100'; } else { $html .= 'YES'; } } if ($family->getFacts(WT_EVENTS_DIV)) { $html .= 'D'; } if (count($husb->getSpouseFamilies()) > 1 || count($wife->getSpouseFamilies()) > 1) { $html .= 'M'; } $html .= '</td>'; //-- Sorting alive/dead $html .= '<td>'; if ($husb->isDead() && $wife->isDead()) { $html .= 'Y'; } if ($husb->isDead() && !$wife->isDead()) { if ($wife->getSex() == 'F') { $html .= 'H'; } if ($wife->getSex() == 'M') { $html .= 'W'; } // male partners } if (!$husb->isDead() && $wife->isDead()) { if ($husb->getSex() == 'M') { $html .= 'W'; } if ($husb->getSex() == 'F') { $html .= 'H'; } // female partners } if (!$husb->isDead() && !$wife->isDead()) { $html .= 'N'; } $html .= '</td>'; //-- Roots or Leaves $html .= '<td>'; if (!$husb->getChildFamilies() && !$wife->getChildFamilies()) { $html .= 'R'; } elseif (!$husb->isDead() && !$wife->isDead() && $family->getNumberOfChildren() < 1) { $html .= 'L'; } else { $html .= ' '; } $html .= '</td> </tr>'; } $html .= ' </tbody> </table> <div id="fam_list_table-charts_' . $table_id . '" style="display:none"> <table class="list-charts"> <tr> <td> ' . print_chart_by_decade($birt_by_decade, WT_I18N::translate('Decade of birth')) . ' </td> <td> ' . print_chart_by_decade($marr_by_decade, WT_I18N::translate('Decade of marriage')) . ' </td> </tr> <tr> <td colspan="2"> ' . print_chart_by_age($marr_by_age, WT_I18N::translate('Age in year of marriage')) . ' </td> </tr> </table> </div> </div>'; return $html; }
<?php echo $gparent->getFullName(); // Full Name (Link) ?> </a> </td> <tr> <?php } // Spouse Children foreach ($family->getChildren() as $child) { // Get Spouse child’s marriage status $married = ""; $marrdate = ""; foreach ($child->getSpouseFamilies() as $childfamily) { $marrdate = $childfamily->getMarriageDate(); $married = WT_Date::Compare($censdate, $marrdate); } // Get Child’s Children $chBLDarray = array(); foreach ($child->getSpouseFamilies() as $childfamily) { $chchildren = $childfamily->getChildren(); foreach ($chchildren as $chchild) { $chnam = $chchild->getAllNames(); $chfulln = rtrim($chnam[0]['givn'], '*') . " " . $chnam[0]['surname']; $chfulln = str_replace("@N.N.", "(" . WT_I18N::translate('unknown') . ")", $chfulln); $chfulln = str_replace("@P.N.", "(" . WT_I18N::translate('unknown') . ")", $chfulln); $chfulln = WT_Filter::escapeHtml($chfulln); // Child’s Full Name// Child’s Full Name $chdob = ($chchild->getBirthDate()->minJD() + $chchild->getBirthDate()->maxJD()) / 2; // Child’s Date of Birth (Julian) $chdod = ($chchild->getDeathDate()->minJD() + $chchild->getDeathDate()->maxJD()) / 2;
function format_fact_date(WT_Fact $event, WT_GedcomRecord $record, $anchor = false, $time = false) { global $pid, $SEARCH_SPIDER, $SHOW_PARENTS_AGE; $factrec = $event->getGedcom(); $html = ''; // Recorded age if (preg_match('/\\n2 AGE (.+)/', $factrec, $match)) { $fact_age = $match[1]; } else { $fact_age = ''; } if (preg_match('/\\n2 HUSB\\n3 AGE (.+)/', $factrec, $match)) { $husb_age = $match[1]; } else { $husb_age = ''; } if (preg_match('/\\n2 WIFE\\n3 AGE (.+)/', $factrec, $match)) { $wife_age = $match[1]; } else { $wife_age = ''; } // Calculated age if (preg_match('/2 DATE (.+)/', $factrec, $match)) { $date = new WT_Date($match[1]); $html .= ' ' . $date->Display($anchor && !$SEARCH_SPIDER); // time if ($time) { $timerec = get_sub_record(2, '2 TIME', $factrec); if ($timerec == '') { $timerec = get_sub_record(2, '2 DATE', $factrec); } if (preg_match('/[2-3] TIME (.*)/', $timerec, $tmatch)) { $html .= '<span class="date"> - ' . $tmatch[1] . '</span>'; } } $fact = $event->getTag(); if ($record instanceof WT_Individual) { // age of parents at child birth if ($fact == 'BIRT' && $SHOW_PARENTS_AGE) { $html .= format_parents_age($record, $date); } else { if ($fact != 'CHAN' && $fact != '_TODO') { $birth_date = $record->getBirthDate(); // Can't use getDeathDate(), as this also gives BURI/CREM events, which // wouldn't give the correct "days after death" result for people with // no DEAT. $death_event = $record->getFirstFact('DEAT'); if ($death_event) { $death_date = $death_event->getDate(); } else { $death_date = new WT_Date(''); } $ageText = ''; if (WT_Date::Compare($date, $death_date) <= 0 || !$record->isDead() || $fact == 'DEAT') { // Before death, print age $age = WT_Date::GetAgeGedcom($birth_date, $date); // Only show calculated age if it differs from recorded age if ($age != '') { if ($fact_age != '' && $fact_age != $age || $fact_age == '' && $husb_age == '' && $wife_age == '' || $husb_age != '' && $record->getSex() == 'M' && $husb_age != $age || $wife_age != '' && $record->getSex() == 'F' && $wife_age != $age) { if ($age != "0d") { $ageText = '(' . WT_I18N::translate('Age') . ' ' . get_age_at_event($age, false) . ')'; } } } } if ($fact != 'DEAT' && WT_Date::Compare($date, $death_date) >= 0) { // After death, print time since death $age = get_age_at_event(WT_Date::GetAgeGedcom($death_date, $date), true); if ($age != '') { if (WT_Date::GetAgeGedcom($death_date, $date) == "0d") { $ageText = '(' . WT_I18N::translate('on the date of death') . ')'; } else { $ageText = '(' . $age . ' ' . WT_I18N::translate('after death') . ')'; // Family events which occur after death are probably errors if ($event->getParent() instanceof WT_Family) { $ageText .= '<i class="icon-warning"></i>'; } } } } if ($ageText) { $html .= ' <span class="age">' . $ageText . '</span>'; } } } } elseif ($record instanceof WT_Family) { $indi = WT_Individual::getInstance($pid); if ($indi) { $birth_date = $indi->getBirthDate(); $death_date = $indi->getDeathDate(); $ageText = ''; if (WT_Date::Compare($date, $death_date) <= 0) { $age = WT_Date::GetAgeGedcom($birth_date, $date); // Only show calculated age if it differs from recorded age if ($age != '' && $age > 0) { if ($fact_age != '' && $fact_age != $age || $fact_age == '' && $husb_age == '' && $wife_age == '' || $husb_age != '' && $indi->getSex() == 'M' && $husb_age != $age || $wife_age != '' && $indi->getSex() == 'F' && $wife_age != $age) { $ageText = '(' . WT_I18N::translate('Age') . ' ' . get_age_at_event($age, false) . ')'; } } } if ($ageText) { $html .= ' <span class="age">' . $ageText . '</span>'; } } } } else { // 1 DEAT Y with no DATE => print YES // 1 BIRT 2 SOUR @S1@ => print YES // 1 DEAT N is not allowed // It is not proper GEDCOM form to use a N(o) value with an event tag to infer that it did not happen. $factdetail = explode(' ', trim($factrec)); if (isset($factdetail) && (count($factdetail) == 3 && strtoupper($factdetail[2]) == 'Y') || count($factdetail) == 4 && $factdetail[2] == 'SOUR') { $html .= WT_I18N::translate('yes'); } } // print gedcom ages foreach (array(WT_Gedcom_Tag::getLabel('AGE') => $fact_age, WT_Gedcom_Tag::getLabel('HUSB') => $husb_age, WT_Gedcom_Tag::getLabel('WIFE') => $wife_age) as $label => $age) { if ($age != '') { $html .= ' <span class="label">' . $label . ':</span> <span class="age">' . get_age_at_event($age, false) . '</span>'; } } return $html; }
static function CompareDeatDate($x, $y) { return WT_Date::Compare($x->getEstimatedDeathDate(), $y->getEstimatedDeathDate()); }
function __construct() { global $WT_SESSION; parent::__construct(); $this->setPageTitle(WT_I18N::translate('Lifespans')); $this->colorindex = 0; $this->Fcolorindex = 0; $this->Mcolorindex = 0; $this->zoomfactor = 10; $this->color = "#0000FF"; $this->currentYear = date("Y"); $this->deathMod = 0; $this->endDate = $this->currentYear; // Request parameters $newpid = WT_Filter::get('newpid', WT_REGEX_XREF); $remove = WT_Filter::get('remove', WT_REGEX_XREF); $pids = WT_Filter::getArray('pids', WT_REGEX_XREF); $clear = WT_Filter::getBool('clear'); $addfam = WT_Filter::getBool('addFamily'); $place = WT_Filter::get('place'); $beginYear = WT_Filter::getInteger('beginYear', 0, date('Y') + 100, 0); $endYear = WT_Filter::getInteger('endYear', 0, date('Y') + 100, 0); if ($clear) { // Empty list $this->pids = array(); } elseif ($pids) { // List of specified records $this->pids = $pids; } elseif ($place) { // All records found in a place $wt_place = new WT_Place($place, WT_GED_ID); $this->pids = WT_DB::prepare("SELECT DISTINCT pl_gid FROM `##placelinks` WHERE pl_p_id=? AND pl_file=?")->execute(array($wt_place->getPlaceId(), WT_GED_ID))->fetchOneColumn(); $this->place = $place; } else { // Modify an existing list of records if (is_array($WT_SESSION->timeline_pids)) { $this->pids = $WT_SESSION->timeline_pids; } else { $this->pids = array(); } if ($remove) { foreach ($this->pids as $key => $value) { if ($value == $remove) { unset($this->pids[$key]); } } } elseif ($newpid) { $person = WT_Individual::getInstance($newpid); $this->addFamily($person, $addfam); } elseif (!$this->pids) { $this->addFamily($this->getSignificantIndividual(), false); } } $WT_SESSION->timeline_pids = $this->pids; $this->beginYear = $beginYear; $this->endYear = $endYear; if ($beginYear == 0 || $endYear == 0) { //-- cleanup user input $this->pids = array_unique($this->pids); //removes duplicates foreach ($this->pids as $key => $value) { if ($value != $remove) { $this->pids[$key] = $value; $person = WT_Individual::getInstance($value); // list of linked records includes families as well as individuals. if ($person) { $bdate = $person->getEstimatedBirthDate(); if ($bdate->isOK() && $person->canShow()) { $this->people[] = $person; } } } } } else { //--Finds if the begin year and end year textboxes are not empty //-- reset the people array when doing a year range search $this->people = array(); //Takes the begining year and end year passed by the postback and modifies them and uses them to populate //the time line //Variables to restrict the person boxes to the year searched. //--Searches for individuals who had an even between the year begin and end years $indis = self::search_indis_year_range($beginYear, $endYear); //--Populates an array of people that had an event within those years foreach ($indis as $person) { if (empty($searchplace) || in_array($person->getXref(), $this->pids)) { $bdate = $person->getEstimatedBirthDate(); if ($bdate->isOK() && $person->canShow()) { $this->people[] = $person; } } } $WT_SESSION->timeline_pids = null; } // Sort the array in order of birth year uasort($this->people, function (WT_Individual $a, WT_Individual $b) { return WT_Date::Compare($a->getEstimatedBirthDate(), $b->getEstimatedBirthDate()); }); //If there is people in the array posted back this if occurs if (isset($this->people[0])) { //Find the maximum Death year and mimimum Birth year for each individual returned in the array. $bdate = $this->people[0]->getEstimatedBirthDate(); $ddate = $this->people[0]->getEstimatedDeathDate(); $this->timelineMinYear = $bdate->gregorianYear(); $this->timelineMaxYear = $ddate->gregorianYear() ? $ddate->gregorianYear() : date('Y'); foreach ($this->people as $value) { $bdate = $value->getEstimatedBirthDate(); $ddate = $value->getEstimatedDeathDate(); $this->timelineMinYear = min($this->timelineMinYear, $bdate->gregorianYear()); $this->timelineMaxYear = max($this->timelineMaxYear, $ddate->gregorianYear() ? $ddate->gregorianYear() : date('Y')); } if ($this->timelineMaxYear > $this->currentYear) { $this->timelineMaxYear = $this->currentYear; } } else { // Sets the default timeline length $this->timelineMinYear = date("Y") - 101; $this->timelineMaxYear = date("Y"); } }
/** * Static helper function to sort an array of families by marriage date * * @param WT_Family $x * @param WT_Family $y * * @return int */ public static function compareMarrDate(WT_Family $x, WT_Family $y) { return WT_Date::Compare($x->getMarriageDate(), $y->getMarriageDate()); }
private static function historical_facts(WT_Individual $person) { global $SHOW_RELATIVES_EVENTS; $facts = array(); if ($SHOW_RELATIVES_EVENTS) { // Only include events between birth and death $birt_date = $person->getEstimatedBirthDate(); $deat_date = $person->getEstimatedDeathDate(); if (file_exists(WT_Site::preference('INDEX_DIRECTORY') . 'histo.' . WT_LOCALE . '.php')) { require WT_Site::preference('INDEX_DIRECTORY') . 'histo.' . WT_LOCALE . '.php'; foreach ($histo as $hist) { // Earlier versions of the WIKI encouraged people to use HTML entities, // rather than UTF8 encoding. $hist = html_entity_decode($hist, ENT_QUOTES, 'UTF-8'); $fact = new WT_Fact($hist, $person, 'histo'); $sdate = $fact->getDate(); if ($sdate->isOK() && WT_Date::Compare($birt_date, $sdate) <= 0 && WT_Date::Compare($sdate, $deat_date) <= 0) { $facts[] = $fact; } } } } return $facts; }
/** * XML <List> start element handler * * @see ListEHandler() * * @param array $attrs an array of key value pairs for the attributes */ function ListSHandler($attrs) { global $gedrec, $repeats, $repeatBytes, $list, $repeatsStack, $processRepeats, $parser, $vars, $sortby; global $GEDCOM; $processRepeats++; if ($processRepeats > 1) { return; } $match = array(); if (isset($attrs['sortby'])) { $sortby = $attrs['sortby']; if (preg_match("/\\\$(\\w+)/", $sortby, $match)) { $sortby = $vars[$match[1]]['id']; $sortby = trim($sortby); } } else { $sortby = "NAME"; } if (isset($attrs['list'])) { $listname = $attrs['list']; } else { $listname = "individual"; } // Some filters/sorts can be applied using SQL, while others require PHP switch ($listname) { case "pending": $rows = WT_DB::prepare("SELECT xref, gedcom_id, CASE new_gedcom WHEN '' THEN old_gedcom ELSE new_gedcom END AS gedcom" . " FROM `##change`" . " WHERE (xref, change_id) IN (" . " SELECT xref, MAX(change_id)" . " FROM `##change`" . " WHERE status='pending' AND gedcom_id=?" . " GROUP BY xref" . " )")->execute(array(WT_GED_ID))->fetchAll(); $list = array(); foreach ($rows as $row) { $list[] = WT_GedcomRecord::getInstance($row->xref, $row->gedcom_id, $row->gedcom); } break; case "individual": case "family": $sql_col_prefix = substr($listname, 0, 1) . "_"; // i_ for individual, f_ for family, etc. $sql_join = array(); $sql_where = array($sql_col_prefix . "file=" . WT_GED_ID); $sql_order_by = array(); foreach ($attrs as $attr => $value) { if (strpos($attr, "filter") === 0 && $value) { // Substitute global vars $value = preg_replace_callback('/\\$(\\w+)/', function ($matches) use($vars) { return $vars[$matches[1]]['id']; }, $value); // Convert the various filters into SQL if (preg_match('/^(\\w+):DATE (LTE|GTE) (.+)$/', $value, $match)) { $sql_join[] = "JOIN `##dates` AS {$attr} ON ({$attr}.d_file={$sql_col_prefix}file AND {$attr}.d_gid={$sql_col_prefix}id)"; $sql_where[] = "{$attr}.d_fact='{$match[1]}'"; $date = new WT_Date($match[3]); if ($match[2] == "LTE") { $sql_where[] = "{$attr}.d_julianday2<=" . $date->minJD(); } else { $sql_where[] = "{$attr}.d_julianday1>=" . $date->minJD(); } if ($sortby == $match[1]) { $sortby = ""; $sql_order_by[] = "{$attr}.d_julianday1"; } unset($attrs[$attr]); // This filter has been fully processed } elseif ($listname == "individual" && preg_match('/^NAME CONTAINS (.*)$/', $value, $match)) { // Do nothing, unless you have to if ($match[1] != "" or $sortby == "NAME") { $sql_join[] = "JOIN `##name` AS {$attr} ON (n_file={$sql_col_prefix}file AND n_id={$sql_col_prefix}id)"; // Search the DB only if there is any name supplied if ($match[1] != "") { $names = explode(" ", $match[1]); foreach ($names as $name) { $sql_where[] = "{$attr}.n_full LIKE " . WT_DB::quote("%{$name}%"); } } // Let the DB do the name sorting even when no name was entered if ($sortby == "NAME") { $sortby = ""; $sql_order_by[] = "{$attr}.n_sort"; } } unset($attrs[$attr]); // This filter has been fully processed } elseif ($listname == "individual" && preg_match('/^REGEXP \\/(.+)\\//', $value, $match)) { $sql_where[] = "i_gedcom REGEXP '" . $match[1] . "'"; unset($attrs[$attr]); // This filter has been fully processed } elseif ($listname == "family" && preg_match('/^REGEXP \\/(.+)\\//', $value, $match)) { $sql_where[] = "f_gedcom REGEXP '" . $match[1] . "'"; unset($attrs[$attr]); // This filter has been fully processed } elseif ($listname == "family" && preg_match('/^NAME CONTAINS (.+)$/', $value, $match)) { // Eventually, family "names" will be stored in wt_name. Until then, an extra is needed.... $sql_join[] = "JOIN `##link` AS {$attr}a ON ({$attr}a.l_file={$sql_col_prefix}file AND {$attr}a.l_from={$sql_col_prefix}id)"; $sql_join[] = "JOIN `##name` AS {$attr}b ON ({$attr}b.n_file={$sql_col_prefix}file AND n_id={$sql_col_prefix}id)"; $sql_where[] = "{$attr}a.l_type=IN ('HUSB, 'WIFE')"; $sql_where[] = "{$attr}.n_full LIKE " . WT_DB::quote("%{$match[1]}%"); if ($sortby == "NAME") { $sortby = ""; $sql_order_by[] = "{$attr}.n_sort"; } unset($attrs[$attr]); // This filter has been fully processed } elseif (preg_match('/^(?:\\w+):PLAC CONTAINS (.+)$/', $value, $match)) { $sql_join[] = "JOIN `##places` AS {$attr}a ON ({$attr}a.p_file={$sql_col_prefix}file)"; $sql_join[] = "JOIN `##placelinks` AS {$attr}b ON ({$attr}a.p_file={$attr}b.pl_file AND {$attr}b.pl_p_id={$attr}a.p_id AND {$attr}b.pl_gid={$sql_col_prefix}id)"; $sql_where[] = "{$attr}a.p_place LIKE " . WT_DB::quote("%{$match[1]}%"); // Don't unset this filter. This is just the first primary PLAC filter to reduce the returned list from the DB } elseif ($listname == "individual" && preg_match('/^(\\w*):*(\\w*) CONTAINS (.+)$/', $value, $match)) { $query = ""; // Level 1 tag if ($match[1] != "") { $query .= "%1 {$match[1]}%"; } // Level 2 tag if ($match[2] != "") { $query .= "%2 {$match[2]}%"; } // Contains what? if ($match[3] != "") { $query .= "%{$match[3]}%"; } $sql_where[] = "i_gedcom LIKE " . WT_DB::quote($query); } elseif ($listname == "family" && preg_match('/^(\\w*):*(\\w*) CONTAINS (.+)$/', $value, $match)) { $query = ""; // Level 1 tag if ($match[1] != "") { $query .= "%1 {$match[1]}%"; } // Level 2 tag if ($match[2] != "") { $query .= "%2 {$match[2]}%"; } // Contains what? if ($match[3] != "") { $query .= "%{$match[3]}%"; } $sql_where[] = "f_gedcom LIKE " . WT_DB::quote($query); } else { // TODO: what other filters can we apply in SQL? } } } if ($listname == "family") { $list = search_fams_custom($sql_join, $sql_where, $sql_order_by); } else { $list = search_indis_custom($sql_join, $sql_where, $sql_order_by); } // Clean up the SQL queries - they will not be used again unset($sql_join, $sql_where, $sql_order_by); break; default: die("Invalid list name: {$listname}"); } $filters = array(); $filters2 = array(); if (isset($attrs['filter1']) and count($list) > 0) { foreach ($attrs as $key => $value) { if (preg_match("/filter(\\d)/", $key)) { $condition = $value; if (preg_match("/@(\\w+)/", $condition, $match)) { $id = $match[1]; $value = "''"; if ($id == "ID") { if (preg_match("/0 @(.+)@/", $gedrec, $match)) { $value = "'" . $match[1] . "'"; } } elseif ($id == "fact") { $value = "'{$fact}'"; } elseif ($id == "desc") { $value = "'{$desc}'"; } else { if (preg_match("/\\d {$id} (.+)/", $gedrec, $match)) { $value = "'" . str_replace("@", "", trim($match[1])) . "'"; } } $condition = preg_replace("/@{$id}/", $value, $condition); } //-- handle regular expressions if (preg_match("/([A-Z:]+)\\s*([^\\s]+)\\s*(.+)/", $condition, $match)) { $tag = trim($match[1]); $expr = trim($match[2]); $val = trim($match[3]); if (preg_match("/\\\$(\\w+)/", $val, $match)) { $val = $vars[$match[1]]['id']; $val = trim($val); } if ($val) { $searchstr = ""; $tags = explode(":", $tag); //-- only limit to a level number if we are specifically looking at a level if (count($tags) > 1) { $level = 1; foreach ($tags as $t) { if (!empty($searchstr)) { $searchstr .= "[^\n]*(\n[2-9][^\n]*)*\n"; } //-- search for both EMAIL and _EMAIL... silly double gedcom standard if ($t == "EMAIL" || $t == "_EMAIL") { $t = "_?EMAIL"; } $searchstr .= $level . " " . $t; $level++; } } else { if ($tag == "EMAIL" || $tag == "_EMAIL") { $tag = "_?EMAIL"; } $t = $tag; $searchstr = "1 " . $tag; } switch ($expr) { case "CONTAINS": if ($t == "PLAC") { $searchstr .= "[^\n]*[, ]*" . $val; } else { $searchstr .= "[^\n]*" . $val; } $filters[] = $searchstr; break; default: $filters2[] = array("tag" => $tag, "expr" => $expr, "val" => $val); break; } } } } } } //-- apply other filters to the list that could not be added to the search string if ($filters) { foreach ($list as $key => $record) { foreach ($filters as $filter) { if (!preg_match("/" . $filter . "/i", $record->privatizeGedcom(WT_USER_ACCESS_LEVEL))) { unset($list[$key]); break; } } } } if ($filters2) { $mylist = array(); foreach ($list as $indi) { $key = $indi->getXref(); $grec = $indi->privatizeGedcom(WT_USER_ACCESS_LEVEL); $keep = true; foreach ($filters2 as $filter) { if ($keep) { $tag = $filter['tag']; $expr = $filter['expr']; $val = $filter['val']; if ($val == "''") { $val = ""; } $tags = explode(":", $tag); $t = end($tags); $v = get_gedcom_value($tag, 1, $grec); //-- check for EMAIL and _EMAIL (silly double gedcom standard :P) if ($t == "EMAIL" && empty($v)) { $tag = str_replace("EMAIL", "_EMAIL", $tag); $tags = explode(":", $tag); $t = end($tags); $v = get_sub_record(1, $tag, $grec); } $level = count($tags); switch ($expr) { case "GTE": if ($t == "DATE") { $date1 = new WT_Date($v); $date2 = new WT_Date($val); $keep = WT_Date::Compare($date1, $date2) >= 0; } elseif ($val >= $v) { $keep = true; } break; case "LTE": if ($t == "DATE") { $date1 = new WT_Date($v); $date2 = new WT_Date($val); $keep = WT_Date::Compare($date1, $date2) <= 0; } elseif ($val >= $v) { $keep = true; } break; default: if ($v == $val) { $keep = true; } else { $keep = false; } break; } } } if ($keep) { $mylist[$key] = $indi; } } $list = $mylist; } switch ($sortby) { case "NAME": uasort($list, array("WT_GedcomRecord", "compare")); break; case "CHAN": uasort($list, function (WT_GedcomRecord $x, WT_GedcomRecord $y) { $f1 = $x->getFirstFact('CHAN'); $f2 = $y->getFirstFact('CHAN'); if ($f1 && $f2) { $d1 = $f1->getDate(); $d2 = $f2->getDate(); $cmp = WT_Date::compare($d1, $d2); if ($cmp) { return $cmp; } else { // Same date. Compare times preg_match('/\\n3 TIME (.+)/', $f1->getGedcom(), $m1); preg_match('/\\n3 TIME (.+)/', $f2->getGedcom(), $m2); return strcmp($m1[1], $m2[1]); } } else { return 0; } }); break; case "BIRT:DATE": uasort($list, array("WT_Individual", "CompareBirtDate")); break; case "DEAT:DATE": uasort($list, array("WT_Individual", "CompareDeatDate")); break; case "MARR:DATE": uasort($list, array("WT_Family", "compareMarrDate")); break; default: // unsorted or already sorted by SQL break; } array_push($repeatsStack, array($repeats, $repeatBytes)); $repeatBytes = xml_get_current_line_number($parser) + 1; }
$gedrec .= addNewFact($match); } } $gedrec .= "\n" . WT_Gedcom_Code_Pedi::createNewFamcPedi($PEDI, $xref); if (WT_Filter::postBool('SOUR_INDI')) { $gedrec = handle_updates($gedrec); } else { $gedrec = updateRest($gedrec); } // Create the new child $new_child = WT_GedcomRecord::createRecord($gedrec, WT_GED_ID); // Insert new child at the right place $done = false; foreach ($family->getFacts('CHIL') as $fact) { $old_child = $fact->getTarget(); if ($old_child && WT_Date::Compare($new_child->getEstimatedBirthDate(), $old_child->getEstimatedBirthDate()) < 0) { // Insert before this child $family->updateFact($fact->getFactId(), '1 CHIL @' . $new_child->getXref() . "@\n" . $fact->getGedcom(), !$keep_chan); $done = true; break; } } if (!$done) { // Append child at end $family->createFact('1 CHIL @' . $new_child->getXref() . '@', !$keep_chan); } if (WT_Filter::post('goto') == 'new') { $controller->addInlineJavascript('closePopupAndReloadParent("' . $new_child->getRawUrl() . '");'); } else { $controller->addInlineJavascript('closePopupAndReloadParent();'); }
function compare_people($a, $b) { return WT_Date::Compare($a->getEstimatedBirthDate(), $b->getEstimatedBirthDate()); }
/** * Static Helper functions to sort events * * @param WT_Fact $a Fact one * @param WT_Fact $b Fact two * * @return integer */ public static function CompareDate(WT_Fact $a, WT_Fact $b) { if ($a->getDate()->isOK() && $b->getDate()->isOK()) { // If both events have dates, compare by date $ret = WT_Date::Compare($a->getDate(), $b->getDate()); if ($ret == 0) { // If dates are the same, compare by fact type $ret = self::CompareType($a, $b); // If the fact type is also the same, retain the initial order if ($ret == 0) { $ret = $a->sortOrder - $b->sortOrder; } } return $ret; } else { // One or both events have no date - retain the initial order return $a->sortOrder - $b->sortOrder; } }