/** * Render the Ajax response for the sortable table of Sosa family * @param AjaxController $controller */ protected function renderFamSosaListIndi(AjaxController $controller) { global $WT_TREE; $listFamSosa = $this->sosa_provider->getFamilySosaListAtGeneration($this->generation); $this->view_bag->set('has_sosa', false); if (count($listFamSosa) > 0) { $this->view_bag->set('has_sosa', true); $table_id = 'table-sosa-fam-' . Uuid::uuid4(); $this->view_bag->set('table_id', $table_id); $controller->addExternalJavascript(WT_JQUERY_DATATABLES_JS_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.fn.dataTableExt.oSort["num-html-asc" ]=function(a,b) {a=parseFloat(a.replace(/<[^<]*>/, "")); b=parseFloat(b.replace(/<[^<]*>/, "")); return (a<b) ? -1 : (a>b ? 1 : 0);}; jQuery.fn.dataTableExt.oSort["num-html-desc"]=function(a,b) {a=parseFloat(a.replace(/<[^<]*>/, "")); b=parseFloat(b.replace(/<[^<]*>/, "")); return (a>b) ? -1 : (a<b ? 1 : 0);}; jQuery("#' . $table_id . '").dataTable( { dom: \'<"H"<"filtersH_' . $table_id . '"><"dt-clear">pf<"dt-clear">irl>t<"F"pl<"dt-clear"><"filtersF_' . $table_id . '">>\', ' . I18N::datatablesI18N(array(16, 32, 64, 128, -1)) . ', jQueryUI: true, autoWidth: false, processing: true, retrieve: true, columns: [ /* 0-Sosa */ { dataSort: 1, class: "center"}, /* 1-SOSA */ { type: "num", visible: false }, /* 2-Husb Givn */ { dataSort: 4}, /* 3-Husb Surn */ { dataSort: 5}, /* 4-GIVN,SURN */ { type: "unicode", visible: false}, /* 5-SURN,GIVN */ { type: "unicode", visible: false}, /* 6-Husb Age */ { dataSort: 7, class: "center"}, /* 7-AGE */ { type: "num", visible: false}, /* 8-Wife Givn */ { dataSort: 10}, /* 9-Wife Surn */ { dataSort: 11}, /* 10-GIVN,SURN */ { type: "unicode", visible: false}, /* 11-SURN,GIVN */ { type: "unicode", visible: false}, /* 12-Wife Age */ { dataSort: 13, class: "center"}, /* 13-AGE */ { type: "num", visible: false}, /* 14-Marr Date */ { dataSort: 15, class: "center"}, /* 15-MARR:DATE */ { visible: false}, /* 16-Marr Plac */ { type: "unicode", class: "center"}, /* 17-Marr Sour */ { dataSort : 18, class: "center", visible: ' . (ModuleManager::getInstance()->isOperational(Constants::MODULE_MAJ_ISSOURCED_NAME) ? 'true' : 'false') . ' }, /* 18-Sort Sour */ { visible: false}, /* 19-Children */ { dataSort: 20, class: "center"}, /* 20-NCHI */ { type: "num", visible: false}, /* 21-MARR */ { visible: false}, /* 22-DEAT */ { visible: false}, /* 23-TREE */ { visible: false} ], sorting: [[0, "asc"]], displayLength: 16, 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("#sosa-fam-list").css("visibility", "visible"); jQuery("#btn-toggle-statistics-' . $table_id . '").click(); '); $stats = new Stats($WT_TREE); $max_age = max($stats->oldestMarriageMaleAge(), $stats->oldestMarriageFemaleAge()) + 1; //-- init chart data $marr_by_age = array(); for ($age = 0; $age <= $max_age; $age++) { $marr_by_age[$age] = ''; } $birt_by_decade = array(); $marr_by_decade = array(); for ($year = 1550; $year < 2030; $year += 10) { $birt_by_decade[$year] = ''; $marr_by_decade[$year] = ''; } foreach ($listFamSosa as $sosa => $fid) { $sfamily = Family::getInstance($fid, $WT_TREE); if (!$sfamily || !$sfamily->canShow()) { unset($sfamily[$sosa]); continue; } $mdate = $sfamily->getMarriageDate(); if (($husb = $sfamily->getHusband()) && ($hdate = $husb->getBirthDate()) && $hdate->isOK() && $mdate->isOK()) { if (FunctionsPrint::isDateWithinChartsRange($hdate)) { $birt_by_decade[(int) ($hdate->gregorianYear() / 10) * 10] .= $husb->getSex(); } $hage = Date::getAge($hdate, $mdate, 0); if ($hage >= 0 && $hage <= $max_age) { $marr_by_age[$hage] .= $husb->getSex(); } } if (($wife = $sfamily->getWife()) && ($wdate = $wife->getBirthDate()) && $wdate->isOK() && $mdate->isOK()) { if (FunctionsPrint::isDateWithinChartsRange($wdate)) { $birt_by_decade[(int) ($wdate->gregorianYear() / 10) * 10] .= $wife->getSex(); } $wage = Date::getAge($wdate, $mdate, 0); if ($wage >= 0 && $wage <= $max_age) { $marr_by_age[$wage] .= $wife->getSex(); } } if ($mdate->isOK() && FunctionsPrint::isDateWithinChartsRange($mdate) && $husb && $wife) { $marr_by_decade[(int) ($mdate->gregorianYear() / 10) * 10] .= $husb->getSex() . $wife->getSex(); } $listFamSosa[$sosa] = $sfamily; } $this->view_bag->set('sosa_list', $listFamSosa); $this->view_bag->set('chart_births', FunctionsPrintLists::chartByDecade($birt_by_decade, I18N::translate('Decade of birth'))); $this->view_bag->set('chart_marriages', FunctionsPrintLists::chartByDecade($marr_by_decade, I18N::translate('Decade of marriage'))); $this->view_bag->set('chart_ages', FunctionsPrintLists::chartByAge($marr_by_age, I18N::translate('Age in year of marriage'))); } ViewFactory::make('SosaListFam', $this, $controller, $this->view_bag)->render(); }
<td class="facts_value">', $stats->minAgeOfMarriageFamily(), '</td> </tr> </table> <br> <b>', I18N::translate('Age in year of marriage'), '</b> <table class="facts_table"> <tr> <td class="facts_label">', I18N::translate('Youngest male'), ' - ', $stats->youngestMarriageMaleAge(true), '</td> <td class="facts_label">', I18N::translate('Youngest female'), ' - ', $stats->youngestMarriageFemaleAge(true), '</td> </tr> <tr> <td class="facts_value">', $stats->youngestMarriageMale(), '</td> <td class="facts_value">', $stats->youngestMarriageFemale(), '</td> </tr> <tr> <td class="facts_label">', I18N::translate('Oldest male'), ' - ', $stats->oldestMarriageMaleAge(true), '</td> <td class="facts_label">', I18N::translate('Oldest female'), ' - ', $stats->oldestMarriageFemaleAge(true), '</td> </tr> <tr> <td class="facts_value">', $stats->oldestMarriageMale(), '</td> <td class="facts_value">', $stats->oldestMarriageFemale(), '</td> </tr> <tr> <td class="facts_value statistics-page" colspan="2">', $stats->statsMarrAge(), '</td> </tr> </table> <br> <b>', I18N::translate('Age at birth of child'), '</b> <table class="facts_table"> <tr> <td class="facts_label">', I18N::translate('Youngest father'), ' - ', $stats->youngestFatherAge(true), '</td>
/** * Print a table of families * * @param Family[] $datalist * * @return string */ public static function familyTable($datalist) { global $WT_TREE, $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_JS_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 . '">>\', ' . 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: ' . ($WT_TREE->getPreference('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 Stats($WT_TREE); $max_age = max($stats->oldestMarriageMaleAge(), $stats->oldestMarriageFemaleAge()) + 1; //-- init chart data $marr_by_age = array(); for ($age = 0; $age <= $max_age; $age++) { $marr_by_age[$age] = ''; } $birt_by_decade = array(); $marr_by_decade = array(); 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="' . I18N::translate('Show individuals who are alive or couples where both partners are alive.') . '" > ' . I18N::translate('Both alive') . ' </button> <button type="button" data-filter-column="21" data-filter-value="W" class="ui-state-default" title="' . I18N::translate('Show couples where only the female partner is deceased.') . '" > ' . I18N::translate('Widower') . ' </button> <button type="button" data-filter-column="21" data-filter-value="H" class="ui-state-default" title="' . I18N::translate('Show couples where only the male partner is deceased.') . '" > ' . I18N::translate('Widow') . ' </button> <button type="button" data-filter-column="21" data-filter-value="Y" class="ui-state-default" title="' . I18N::translate('Show individuals who are dead or couples where both partners are deceased.') . '" > ' . 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="' . 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.') . '" > ' . I18N::translate('Roots') . ' </button> <button type="button" data-filter-column="22" data-filter-value="L" class="ui-state-default" title="' . I18N::translate('Show “leaves” couples or individuals. These are individuals who are alive but have no children recorded in the database.') . '" > ' . 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="' . I18N::translate('Show couples with an unknown marriage date.') . '" > ' . GedcomTag::getLabel('MARR') . ' </button> <button type="button" data-filter-column="20" data-filter-value="YES" class="ui-state-default" title="' . I18N::translate('Show couples who married more than 100 years ago.') . '" > ' . GedcomTag::getLabel('MARR') . '>100 </button> <button type="button" data-filter-column="20" data-filter-value="Y100" class="ui-state-default" title="' . I18N::translate('Show couples who married within the last 100 years.') . '" > ' . GedcomTag::getLabel('MARR') . '<=100 </button> <button type="button" data-filter-column="20" data-filter-value="D" class="ui-state-default" title="' . I18N::translate('Show divorced couples.') . '" > ' . GedcomTag::getLabel('DIV') . ' </button> <button type="button" data-filter-column="20" data-filter-value="M" class="ui-state-default" title="' . I18N::translate('Show couples where either partner married more than once.') . '" > ' . I18N::translate('Multiple marriages') . ' </button> </div> </div> </th> </tr> <tr> <th>' . GedcomTag::getLabel('GIVN') . '</th> <th>' . GedcomTag::getLabel('SURN') . '</th> <th>HUSB:GIVN_SURN</th> <th>HUSB:SURN_GIVN</th> <th>' . GedcomTag::getLabel('AGE') . '</th> <th>AGE</th> <th>' . GedcomTag::getLabel('GIVN') . '</th> <th>' . GedcomTag::getLabel('SURN') . '</th> <th>WIFE:GIVN_SURN</th> <th>WIFE:SURN_GIVN</th> <th>' . GedcomTag::getLabel('AGE') . '</th> <th>AGE</th> <th>' . GedcomTag::getLabel('MARR') . '</th> <th>MARR:DATE</th> <th><i class="icon-reminder" title="' . I18N::translate('Anniversary') . '"></i></th> <th>' . GedcomTag::getLabel('PLAC') . '</th> <th><i class="icon-children" title="' . I18N::translate('Children') . '"></i></th> <th>NCHI</th> <th>' . GedcomTag::getLabel('CHAN') . '</th> <th>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"> ' . I18N::translate('Show parents') . ' </button> <button type="button" class="ui-state-default btn-toggle-statistics"> ' . I18N::translate('Show statistics charts') . ' </button> </div> </div> </th> </tr> </tfoot> <tbody>'; $d100y = new Date(date('Y') - 100); // 100 years ago foreach ($datalist as $family) { //-- Retrieve husband and wife $husb = $family->getHusband(); if (is_null($husb)) { $husb = new Individual('H', '0 @H@ INDI', null, $family->getTree()); } $wife = $family->getWife(); if (is_null($wife)) { $wife = new Individual('W', '0 @W@ INDI', null, $family->getTree()); } if (!$family->canShow()) { continue; } if ($family->isPendingAddtion()) { $class = ' class="new"'; } elseif ($family->isPendingDeletion()) { $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(GedcomTag::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 . '>' . FunctionsPrint::highlightSearchHits($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>' . Filter::escapeHtml(str_replace('@P.N.', 'AAAA', $givn)) . 'AAAA' . Filter::escapeHtml(str_replace('@N.N.', 'AAAA', $surn)) . '</td>'; $html .= '<td>' . Filter::escapeHtml(str_replace('@N.N.', 'AAAA', $surn)) . 'AAAA' . 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 = Date::getAge($hdate, $mdate, 0); if ($hage >= 0 && $hage <= $max_age) { $marr_by_age[$hage] .= $husb->getSex(); } } $html .= '<td>' . Date::getAge($hdate, $mdate, 2) . '</td><td>' . 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(GedcomTag::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 . '>' . FunctionsPrint::highlightSearchHits($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>' . Filter::escapeHtml(str_replace('@P.N.', 'AAAA', $givn)) . 'AAAA' . Filter::escapeHtml(str_replace('@N.N.', 'AAAA', $surn)) . '</td>'; $html .= '<td>' . Filter::escapeHtml(str_replace('@N.N.', 'AAAA', $surn)) . 'AAAA' . 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 = Date::getAge($wdate, $mdate, 0); if ($wage >= 0 && $wage <= $max_age) { $marr_by_age[$wage] .= $wife->getSex(); } } $html .= '<td>' . Date::getAge($wdate, $mdate, 2) . '</td><td>' . 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(true) . '</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 .= I18N::translate('no'); } elseif ($family->getFacts('MARR')) { $html .= I18N::translate('yes'); } else { $html .= ' '; } $html .= '</td>'; //-- Event date (sortable)hidden by datatables code $html .= '<td>'; if ($marriage_dates) { $html .= $marriage_date->julianDay(); } else { $html .= 0; } $html .= '</td>'; //-- Marriage anniversary $html .= '<td>' . Date::getAge($mdate, null, 2) . '</td>'; //-- Marriage place $html .= '<td>'; foreach ($family->getAllMarriagePlaces() as $n => $marriage_place) { $tmp = new Place($marriage_place, $family->getTree()); if ($n) { $html .= '<br>'; } $html .= '<a href="' . $tmp->getURL() . '" title="' . strip_tags($tmp->getFullName()) . '">'; $html .= FunctionsPrint::highlightSearchHits($tmp->getShortName()) . '</a>'; } $html .= '</td>'; //-- Number of children $nchi = $family->getNumberOfChildren(); $html .= '<td>' . I18N::number($nchi) . '</td><td>' . $nchi . '</td>'; //-- Last change $html .= '<td>' . $family->LastChangeTimestamp() . '</td>'; $html .= '<td>' . $family->LastChangeTimestamp(true) . '</td>'; //-- Sorting by marriage date $html .= '<td>'; if (!$family->canShow() || !$mdate->isOK()) { $html .= 'U'; } else { if (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> ' . self::chartByDecade($birt_by_decade, I18N::translate('Decade of birth')) . ' </td> <td> ' . self::chartByDecade($marr_by_decade, I18N::translate('Decade of marriage')) . ' </td> </tr> <tr> <td colspan="2"> ' . self::chartByAge($marr_by_age, I18N::translate('Age in year of marriage')) . ' </td> </tr> </table> </div> </div>'; return $html; }
/** * Print a table of families * * @param Family[] $families * * @return string */ public static function familyTable($families) { global $WT_TREE, $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_JS_URL)->addInlineJavascript(' jQuery.fn.dataTableExt.oSort["text-asc"] = textCompareAsc; jQuery.fn.dataTableExt.oSort["text-desc"] = textCompareDesc; jQuery("#' . $table_id . '").dataTable( { dom: \'<"H"<"filtersH_' . $table_id . '"><"dt-clear">pf<"dt-clear">irl>t<"F"pl<"dt-clear"><"filtersF_' . $table_id . '">>\', ' . I18N::datatablesI18N() . ', jQueryUI: true, autoWidth: false, processing: true, retrieve: true, columns: [ /* Given names */ { type: "text" }, /* Surnames */ { type: "text" }, /* Age */ { type: "num" }, /* Given names */ { type: "text" }, /* Surnames */ { type: "text" }, /* Age */ { type: "num" }, /* Marriage date */ { type: "num" }, /* Anniversary */ { type: "num" }, /* Marriage place */ { type: "text" }, /* Children */ { type: "num" }, /* Last change */ { visible: ' . ($WT_TREE->getPreference('SHOW_LAST_CHANGE') ? 'true' : 'false') . ' }, /* Filter marriage */ { sortable: false }, /* Filter alive/dead */ { sortable: false }, /* Filter tree */ { sortable: 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 Stats($WT_TREE); $max_age = max($stats->oldestMarriageMaleAge(), $stats->oldestMarriageFemaleAge()) + 1; // init chart data $marr_by_age = array(); for ($age = 0; $age <= $max_age; $age++) { $marr_by_age[$age] = ''; } $birt_by_decade = array(); $marr_by_decade = array(); 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="14"> <div class="btn-toolbar"> <div class="btn-group"> <button type="button" data-filter-column="12" data-filter-value="N" class="ui-state-default" title="' . I18N::translate('Show individuals who are alive or couples where both partners are alive.') . '" > ' . I18N::translate('Both alive') . ' </button> <button type="button" data-filter-column="12" data-filter-value="W" class="ui-state-default" title="' . I18N::translate('Show couples where only the female partner is dead.') . '" > ' . I18N::translate('Widower') . ' </button> <button type="button" data-filter-column="12" data-filter-value="H" class="ui-state-default" title="' . I18N::translate('Show couples where only the male partner is dead.') . '" > ' . I18N::translate('Widow') . ' </button> <button type="button" data-filter-column="12" data-filter-value="Y" class="ui-state-default" title="' . I18N::translate('Show individuals who are dead or couples where both partners are dead.') . '" > ' . I18N::translate('Both dead') . ' </button> </div> <div class="btn-group"> <button type="button" data-filter-column="13" data-filter-value="R" class="ui-state-default" title="' . 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.') . '" > ' . I18N::translate('Roots') . ' </button> <button type="button" data-filter-column="13" data-filter-value="L" class="ui-state-default" title="' . I18N::translate('Show “leaves” couples or individuals. These are individuals who are alive but have no children recorded in the database.') . '" > ' . I18N::translate('Leaves') . ' </button> </div> <div class="btn-group"> <button type="button" data-filter-column="11" data-filter-value="U" class="ui-state-default" title="' . I18N::translate('Show couples with an unknown marriage date.') . '" > ' . GedcomTag::getLabel('MARR') . ' </button> <button type="button" data-filter-column="11" data-filter-value="YES" class="ui-state-default" title="' . I18N::translate('Show couples who married more than 100 years ago.') . '" > ' . GedcomTag::getLabel('MARR') . '>100 </button> <button type="button" data-filter-column="11" data-filter-value="Y100" class="ui-state-default" title="' . I18N::translate('Show couples who married within the last 100 years.') . '" > ' . GedcomTag::getLabel('MARR') . '<=100 </button> <button type="button" data-filter-column="11" data-filter-value="D" class="ui-state-default" title="' . I18N::translate('Show divorced couples.') . '" > ' . GedcomTag::getLabel('DIV') . ' </button> <button type="button" data-filter-column="11" data-filter-value="M" class="ui-state-default" title="' . I18N::translate('Show couples where either partner married more than once.') . '" > ' . I18N::translate('Multiple marriages') . ' </button> </div> </div> </th> </tr> <tr> <th>' . GedcomTag::getLabel('GIVN') . '</th> <th>' . GedcomTag::getLabel('SURN') . '</th> <th>' . GedcomTag::getLabel('AGE') . '</th> <th>' . GedcomTag::getLabel('GIVN') . '</th> <th>' . GedcomTag::getLabel('SURN') . '</th> <th>' . GedcomTag::getLabel('AGE') . '</th> <th>' . GedcomTag::getLabel('MARR') . '</th> <th><i class="icon-reminder" title="' . I18N::translate('Anniversary') . '"></i></th> <th>' . GedcomTag::getLabel('PLAC') . '</th> <th><i class="icon-children" title="' . I18N::translate('Children') . '"></i></th> <th>' . GedcomTag::getLabel('CHAN') . '</th> <th hidden></th> <th hidden></th> <th hidden></th> </tr> </thead> <tfoot> <tr> <th colspan="14"> <div class="btn-toolbar"> <div class="btn-group"> <button type="button" class="ui-state-default btn-toggle-parents"> ' . I18N::translate('Show parents') . ' </button> <button type="button" class="ui-state-default btn-toggle-statistics"> ' . I18N::translate('Show statistics charts') . ' </button> </div> </div> </th> </tr> </tfoot> <tbody>'; $hundred_years_ago = new Date(date('Y') - 100); foreach ($families as $family) { // Retrieve husband and wife $husb = $family->getHusband(); if (is_null($husb)) { $husb = new Individual('H', '0 @H@ INDI', null, $family->getTree()); } $wife = $family->getWife(); if (is_null($wife)) { $wife = new Individual('W', '0 @W@ INDI', null, $family->getTree()); } if (!$family->canShow()) { continue; } if ($family->isPendingAddtion()) { $class = ' class="new"'; } elseif ($family->isPendingDeletion()) { $class = ' class="old"'; } else { $class = ''; } $html .= '<tr' . $class . '>'; // Husband name(s) // Extract Given names and Surnames for sorting list($surn_givn, $givn_surn) = self::sortableNames($husb); $html .= '<td colspan="2" data-sort="' . Filter::escapeHtml($givn_surn) . '">'; foreach ($husb->getAllNames() as $num => $name) { if ($name['type'] == 'NAME') { $title = ''; } else { $title = 'title="' . strip_tags(GedcomTag::getLabel($name['type'], $husb)) . '"'; } if ($num == $husb->getPrimaryName()) { $class = ' class="name2"'; $sex_image = $husb->getSexImage(); } 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 . '>' . FunctionsPrint::highlightSearchHits($name['full']) . '</a>' . $sex_image . '<br>'; } } // Husband parents $html .= $husb->getPrimaryParentsNames('parents details1', 'none'); $html .= '</td>'; // Hidden column for sortable name $html .= '<td hidden data-sort="' . Filter::escapeHtml($surn_givn) . '"></td>'; // Husband age $mdate = $family->getMarriageDate(); $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 = Date::getAge($hdate, $mdate, 0); if ($hage >= 0 && $hage <= $max_age) { $marr_by_age[$hage] .= $husb->getSex(); } } $html .= '<td class="center" data=-sort="' . Date::getAge($hdate, $mdate, 1) . '">' . Date::getAge($hdate, $mdate, 2) . '</td>'; // Wife name(s) // Extract Given names and Surnames for sorting list($surn_givn, $givn_surn) = self::sortableNames($wife); $html .= '<td colspan="2" data-sort="' . Filter::escapeHtml($givn_surn) . '">'; foreach ($wife->getAllNames() as $num => $name) { if ($name['type'] == 'NAME') { $title = ''; } else { $title = 'title="' . strip_tags(GedcomTag::getLabel($name['type'], $wife)) . '"'; } if ($num == $wife->getPrimaryName()) { $class = ' class="name2"'; $sex_image = $wife->getSexImage(); } 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 . '>' . FunctionsPrint::highlightSearchHits($name['full']) . '</a>' . $sex_image . '<br>'; } } // Wife parents $html .= $wife->getPrimaryParentsNames('parents details1', 'none'); $html .= '</td>'; // Hidden column for sortable name $html .= '<td hidden data-sort="' . Filter::escapeHtml($surn_givn) . '"></td>'; // Wife age $mdate = $family->getMarriageDate(); $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 = Date::getAge($wdate, $mdate, 0); if ($wage >= 0 && $wage <= $max_age) { $marr_by_age[$wage] .= $wife->getSex(); } } $html .= '<td class="center" data-sort="' . Date::getAge($wdate, $mdate, 1) . '">' . Date::getAge($wdate, $mdate, 2) . '</td>'; // Marriage date $html .= '<td data-sort="' . $family->getMarriageDate()->julianDay() . '">'; if ($marriage_dates = $family->getAllMarriageDates()) { foreach ($marriage_dates as $n => $marriage_date) { if ($n) { $html .= '<br>'; } $html .= '<div>' . $marriage_date->display(true) . '</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 .= I18N::translate('no'); } elseif ($family->getFacts('MARR')) { $html .= I18N::translate('yes'); } else { $html .= ' '; } $html .= '</td>'; // Marriage anniversary $html .= '<td class="center" data-sort="' . -$family->getMarriageDate()->julianDay() . '">' . Date::getAge($family->getMarriageDate(), null, 2) . '</td>'; // Marriage place $html .= '<td>'; foreach ($family->getAllMarriagePlaces() as $n => $marriage_place) { $tmp = new Place($marriage_place, $family->getTree()); if ($n) { $html .= '<br>'; } $html .= '<a href="' . $tmp->getURL() . '" title="' . strip_tags($tmp->getFullName()) . '">'; $html .= FunctionsPrint::highlightSearchHits($tmp->getShortName()) . '</a>'; } $html .= '</td>'; // Number of children $html .= '<td class="center" data-sort="' . $family->getNumberOfChildren() . '">' . I18N::number($family->getNumberOfChildren()) . '</td>'; // Last change $html .= '<td data-sort="' . $family->lastChangeTimestamp(true) . '">' . $family->lastChangeTimestamp() . '</td>'; // Filter by marriage date $html .= '<td hidden>'; if (!$family->canShow() || !$mdate->isOK()) { $html .= 'U'; } else { if (Date::compare($mdate, $hundred_years_ago) > 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>'; // Filter by alive/dead $html .= '<td hidden>'; 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>'; // Filter by roots/leaves $html .= '<td hidden>'; if (!$husb->getChildFamilies() && !$wife->getChildFamilies()) { $html .= 'R'; } elseif (!$husb->isDead() && !$wife->isDead() && $family->getNumberOfChildren() === 0) { $html .= 'L'; } $html .= '</td> </tr>'; } $html .= ' </tbody> </table> <div id="fam_list_table-charts_' . $table_id . '" style="display:none"> <table class="list-charts"> <tr> <td>' . self::chartByDecade($birt_by_decade, I18N::translate('Decade of birth')) . '</td> <td>' . self::chartByDecade($marr_by_decade, I18N::translate('Decade of marriage')) . '</td> </tr> <tr> <td colspan="2">' . self::chartByAge($marr_by_age, I18N::translate('Age in year of marriage')) . '</td> </tr> </table> </div> </div>'; return $html; }