/** * Generate the HTML content of this block. * * @param int $block_id * @param bool $template * @param string[] $cfg * * @return string */ public function getBlock($block_id, $template = true, $cfg = array()) { global $WT_TREE, $ctype; $show_last_update = $this->getBlockSetting($block_id, 'show_last_update', '1'); $show_common_surnames = $this->getBlockSetting($block_id, 'show_common_surnames', '1'); $stat_indi = $this->getBlockSetting($block_id, 'stat_indi', '1'); $stat_fam = $this->getBlockSetting($block_id, 'stat_fam', '1'); $stat_sour = $this->getBlockSetting($block_id, 'stat_sour', '1'); $stat_media = $this->getBlockSetting($block_id, 'stat_media', '1'); $stat_repo = $this->getBlockSetting($block_id, 'stat_repo', '1'); $stat_surname = $this->getBlockSetting($block_id, 'stat_surname', '1'); $stat_events = $this->getBlockSetting($block_id, 'stat_events', '1'); $stat_users = $this->getBlockSetting($block_id, 'stat_users', '1'); $stat_first_birth = $this->getBlockSetting($block_id, 'stat_first_birth', '1'); $stat_last_birth = $this->getBlockSetting($block_id, 'stat_last_birth', '1'); $stat_first_death = $this->getBlockSetting($block_id, 'stat_first_death', '1'); $stat_last_death = $this->getBlockSetting($block_id, 'stat_last_death', '1'); $stat_long_life = $this->getBlockSetting($block_id, 'stat_long_life', '1'); $stat_avg_life = $this->getBlockSetting($block_id, 'stat_avg_life', '1'); $stat_most_chil = $this->getBlockSetting($block_id, 'stat_most_chil', '1'); $stat_avg_chil = $this->getBlockSetting($block_id, 'stat_avg_chil', '1'); // This can be overriden when embedding in an HTML block $block = '0'; $stat_link = '1'; foreach (array('show_common_surnames', 'stat_indi', 'stat_fam', 'stat_sour', 'stat_media', 'stat_surname', 'stat_events', 'stat_users', 'stat_first_birth', 'stat_last_birth', 'stat_first_death', 'stat_last_death', 'stat_long_life', 'stat_avg_life', 'stat_most_chil', 'stat_avg_chil', 'stat_link', 'block') as $name) { if (array_key_exists($name, $cfg)) { ${$name} = $cfg[$name]; } } $id = $this->getName() . $block_id; $class = $this->getName() . '_block'; if ($ctype === 'gedcom' && Auth::isManager($WT_TREE) || $ctype === 'user' && Auth::check()) { $title = '<a class="icon-admin" title="' . I18N::translate('Configure') . '" href="block_edit.php?block_id=' . $block_id . '&ged=' . $WT_TREE->getNameHtml() . '&ctype=' . $ctype . '"></a>'; } else { $title = ''; } $title .= $this->getTitle(); $stats = new Stats($WT_TREE); $content = '<b>' . $WT_TREE->getTitleHtml() . '</b><br>'; if ($show_last_update) { $content .= '<div>' . I18N::translate('This family tree was last updated on %s.', strip_tags($stats->gedcomUpdated())) . '</div>'; } /** Responsive Design */ $content .= '<div class="stat-table1">'; if ($stat_indi) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Individuals') . '</div><div class="facts_value stats_value stat-cell"><a href="' . "indilist.php?surname_sublist=no&ged=" . $WT_TREE->getNameUrl() . '">' . $stats->totalIndividuals() . '</a></div></div>'; $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Males') . '</div><div class="facts_value stats_value stat-cell">' . $stats->totalSexMales() . '<br>' . $stats->totalSexMalesPercentage() . '</div></div>'; $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Females') . '</div><div class="facts_value stats_value stat-cell">' . $stats->totalSexFemales() . '<br>' . $stats->totalSexFemalesPercentage() . '</div></div>'; } if ($stat_surname) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Total surnames') . '</div><div class="facts_value stats_value stat-cell"><a href="indilist.php?show_all=yes&surname_sublist=yes&ged=' . $WT_TREE->getNameUrl() . '">' . $stats->totalSurnames() . '</a></div></div>'; } if ($stat_fam) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Families') . '</div><div class="facts_value stats_value stat-cell"><a href="famlist.php?ged=' . $WT_TREE->getNameUrl() . '">' . $stats->totalFamilies() . '</a></div></div>'; } if ($stat_sour) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Sources') . '</div><div class="facts_value stats_value stat-cell"><a href="sourcelist.php?ged=' . $WT_TREE->getNameUrl() . '">' . $stats->totalSources() . '</a></div></div>'; } if ($stat_media) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Media objects') . '</div><div class="facts_value stats_value stat-cell"><a href="medialist.php?ged=' . $WT_TREE->getNameUrl() . '">' . $stats->totalMedia() . '</a></div></div>'; } if ($stat_repo) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Repositories') . '</div><div class="facts_value stats_value stat-cell"><a href="repolist.php?ged=' . $WT_TREE->getNameUrl() . '">' . $stats->totalRepositories() . '</a></div></div>'; } if ($stat_events) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Total events') . '</div><div class="facts_value stats_value stat-cell">' . $stats->totalEvents() . '</div></div>'; } if ($stat_users) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Total users') . '</div><div class="facts_value stats_value stat-cell">'; if (Auth::isManager($WT_TREE)) { $content .= '<a href="admin_users.php">' . $stats->totalUsers() . '</a>'; } else { $content .= $stats->totalUsers(); } $content .= '</div></div>'; } if (!$block) { $content .= '</div><div class="facts_table stat-table2">'; } if ($stat_first_birth) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Earliest birth year') . '</div><div class="facts_value stats_value stat-cell">' . $stats->firstBirthYear() . '</div>'; if (!$block) { $content .= '<div class="facts_value stat-cell left">' . $stats->firstBirth() . '</div>'; } $content .= '</div>'; } if ($stat_last_birth) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Latest birth year') . '</div><div class="facts_value stats_value stat-cell">' . $stats->lastBirthYear() . '</div>'; if (!$block) { $content .= '<div class="facts_value stat-cell left">' . $stats->lastBirth() . '</div>'; } $content .= '</div>'; } if ($stat_first_death) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Earliest death year') . '</div><div class="facts_value stats_value stat-cell">' . $stats->firstDeathYear() . '</div>'; if (!$block) { $content .= '<div class="facts_value stat-cell left">' . $stats->firstDeath() . '</div>'; } $content .= '</div>'; } if ($stat_last_death) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Latest death year') . '</div><div class="facts_value stats_value stat-cell">' . $stats->lastDeathYear() . '</div>'; if (!$block) { $content .= '<div class="facts_value stat-cell left">' . $stats->lastDeath() . '</div>'; } $content .= '</div>'; } if ($stat_long_life) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Individual who lived the longest') . '</div><div class="facts_value stats_value stat-cell">' . $stats->longestLifeAge() . '</div>'; if (!$block) { $content .= '<div class="facts_value stat-cell left">' . $stats->longestLife() . '</div>'; } $content .= '</div>'; } if ($stat_avg_life) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Average age at death') . '</div><div class="facts_value stats_value stat-cell">' . $stats->averageLifespan() . '</div>'; if (!$block) { $content .= '<div class="facts_value stat-cell left">' . I18N::translate('Males') . ': ' . $stats->averageLifespanMale(); $content .= ' ' . I18N::translate('Females') . ': ' . $stats->averageLifespanFemale() . '</div>'; } $content .= '</div>'; } if ($stat_most_chil && !$block) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Family with the most children') . '</div><div class="facts_value stats_value stat-cell">' . $stats->largestFamilySize() . '</div>'; if (!$block) { $content .= '<div class="facts_value stat-cell left">' . $stats->largestFamily() . '</div>'; } $content .= '</div>'; } if ($stat_avg_chil) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Average number of children per family') . '</div><div class="facts_value stats_value stat-cell">' . $stats->averageChildren() . '</div>'; if (!$block) { $content .= '<div class="facts_value stat-cell left"></div>'; } $content .= '</div>'; } $content .= '</div>'; if ($stat_link) { $content .= '<div class="clearfloat"><a href="statistics.php?ged=' . $WT_TREE->getNameUrl() . '" rel="nofollow"><b>' . I18N::translate('View statistics as graphs') . '</b></a></div>'; } if ($show_common_surnames) { $surnames = FunctionsDb::getCommonSurnames($WT_TREE->getPreference('COMMON_NAMES_THRESHOLD'), $WT_TREE); if (count($surnames) > 0) { $content .= '<div class="clearfloat"><p><b>' . I18N::translate('Most common surnames') . '</b></p>'; $content .= '<div class="common_surnames">'; $i = 0; foreach ($surnames as $indexval => $surname) { if (stristr($surname['name'], '@N.N') === false) { if ($i > 0) { $content .= ', '; } $content .= '<a href="' . "indilist.php?ged=" . $WT_TREE->getNameUrl() . "&surname=" . rawurlencode($surname['name']) . '">' . $surname['name'] . '</a>'; $i++; } } $content .= '</div>'; } } if ($template) { return Theme::theme()->formatBlock($id, $title, $class, $content); } else { return $content; } }
/** * Print a table of individuals * * @param Individual[] $datalist * @param string $option * * @return string */ public static function individualTable($datalist, $option = '') { global $controller, $WT_TREE; $table_id = 'table-indi-' . 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.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 . '">T<"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 givn */ { dataSort: 2 }, /* 1 surn */ { dataSort: 3 }, /* 2 GIVN,SURN */ { type: "unicode", visible: false }, /* 3 SURN,GIVN */ { type: "unicode", visible: false }, /* 4 sosa */ { dataSort: 5, class: "center", visible: ' . ($option == 'sosa' ? 'true' : 'false') . ' }, /* 5 SOSA */ { type: "num", visible: false }, /* 6 birt date */ { dataSort: 7 }, /* 7 BIRT:DATE */ { visible: false }, /* 8 anniv */ { dataSort: 7, class: "center" }, /* 9 birt plac */ { type: "unicode" }, /* 10 children */ { dataSort: 11, class: "center" }, /* 11 children */ { type: "num", visible: false }, /* 12 deat date */ { dataSort: 13 }, /* 13 DEAT:DATE */ { visible: false }, /* 14 anniv */ { dataSort: 13, class: "center" }, /* 15 age */ { dataSort: 16, class: "center" }, /* 16 AGE */ { type: "num", visible: false }, /* 17 deat plac */ { type: "unicode" }, /* 18 CHAN */ { dataSort: 19, visible: ' . ($WT_TREE->getPreference('SHOW_LAST_CHANGE') ? 'true' : 'false') . ' }, /* 19 CHAN_sort */ { visible: false }, /* 20 SEX */ { visible: false }, /* 21 BIRT */ { visible: false }, /* 22 DEAT */ { visible: false }, /* 23 TREE */ { visible: false } ], sorting: [[' . ($option == 'sosa' ? '4, "asc"' : '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("#indi_list_table-charts_' . $table_id . '").slideToggle(); }) /* Filter buttons in table header */ .on("click", "button[data-filter-column]", function() { var btn = jQuery(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(".indi-list").css("visibility", "visible"); jQuery(".loading-image").css("display", "none"); '); $stats = new Stats($WT_TREE); // Bad data can cause "longest life" to be huge, blowing memory limits $max_age = min($WT_TREE->getPreference('MAX_ALIVE_AGE'), $stats->longestLifeAge()) + 1; // Inititialise chart data $deat_by_age = array(); for ($age = 0; $age <= $max_age; $age++) { $deat_by_age[$age] = ''; } $birt_by_decade = array(); $deat_by_decade = array(); for ($year = 1550; $year < 2030; $year += 10) { $birt_by_decade[$year] = ''; $deat_by_decade[$year] = ''; } $html = ' <div class="loading-image"></div> <div class="indi-list"> <table id="' . $table_id . '"> <thead> <tr> <th colspan="24"> <div class="btn-toolbar"> <div class="btn-group"> <button class="ui-state-default" data-filter-column="20" data-filter-value="M" title="' . I18N::translate('Show only males.') . '" type="button" > ' . Individual::sexImage('M', 'large') . ' </button> <button class="ui-state-default" data-filter-column="20" data-filter-value="F" title="' . I18N::translate('Show only females.') . '" type="button" > ' . Individual::sexImage('F', 'large') . ' </button> <button class="ui-state-default" data-filter-column="20" data-filter-value="U" title="' . I18N::translate('Show only individuals for whom the gender is not known.') . '" type="button" > ' . Individual::sexImage('U', 'large') . ' </button> </div> <div class="btn-group"> <button class="ui-state-default" data-filter-column="22" data-filter-value="N" title="' . I18N::translate('Show individuals who are alive or couples where both partners are alive.') . '" type="button" > ' . I18N::translate('Alive') . ' </button> <button class="ui-state-default" data-filter-column="22" data-filter-value="Y" title="' . I18N::translate('Show individuals who are dead or couples where both partners are dead.') . '" type="button" > ' . I18N::translate('Dead') . ' </button> <button class="ui-state-default" data-filter-column="22" data-filter-value="YES" title="' . I18N::translate('Show individuals who died more than 100 years ago.') . '" type="button" > ' . GedcomTag::getLabel('DEAT') . '>100 </button> <button class="ui-state-default" data-filter-column="22" data-filter-value="Y100" title="' . I18N::translate('Show individuals who died within the last 100 years.') . '" type="button" > ' . GedcomTag::getLabel('DEAT') . '<=100 </button> </div> <div class="btn-group"> <button class="ui-state-default" data-filter-column="21" data-filter-value="YES" title="' . I18N::translate('Show individuals born more than 100 years ago.') . '" type="button" > ' . GedcomTag::getLabel('BIRT') . '>100 </button> <button class="ui-state-default" data-filter-column="21" data-filter-value="Y100" title="' . I18N::translate('Show individuals born within the last 100 years.') . '" type="button" > ' . GedcomTag::getLabel('BIRT') . '<=100 </button> </div> <div class="btn-group"> <button class="ui-state-default" data-filter-column="23" data-filter-value="R" 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.') . '" type="button" > ' . I18N::translate('Roots') . ' </button> <button class="ui-state-default" data-filter-column="23" data-filter-value="L" title="' . I18N::translate('Show “leaves” couples or individuals. These are individuals who are alive but have no children recorded in the database.') . '" type="button" > ' . I18N::translate('Leaves') . ' </button> </div> </div> </th> </tr> <tr> <th>' . GedcomTag::getLabel('GIVN') . '</th> <th>' . GedcomTag::getLabel('SURN') . '</th> <th>GIVN</th> <th>SURN</th> <th>' . I18N::translate('Sosa') . '</th> <th>SOSA</th> <th>' . GedcomTag::getLabel('BIRT') . '</th> <th>SORT_BIRT</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('DEAT') . '</th> <th>SORT_DEAT</th> <th><i class="icon-reminder" title="' . I18N::translate('Anniversary') . '"></i></th> <th>' . GedcomTag::getLabel('AGE') . '</th> <th>AGE</th> <th>' . GedcomTag::getLabel('PLAC') . '</th> <th>' . GedcomTag::getLabel('CHAN') . '</th> <th>CHAN</th> <th>SEX</th> <th>BIRT</th> <th>DEAT</th> <th>TREE</th> </tr> </thead> <tfoot> <tr> <th colspan="24"> <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 $unique_indis = array(); // Don't double-count indis with multiple names. foreach ($datalist as $key => $person) { if (!$person->canShowName()) { continue; } if ($person->isPendingAddtion()) { $class = ' class="new"'; } elseif ($person->isPendingDeletion()) { $class = ' class="old"'; } else { $class = ''; } $html .= '<tr' . $class . '>'; //-- Indi name(s) $html .= '<td colspan="2">'; foreach ($person->getAllNames() as $num => $name) { if ($name['type'] == 'NAME') { $title = ''; } else { $title = 'title="' . strip_tags(GedcomTag::getLabel($name['type'], $person)) . '"'; } if ($num == $person->getPrimaryName()) { $class = ' class="name2"'; $sex_image = $person->getSexImage(); list($surn, $givn) = explode(',', $name['sort']); } else { $class = ''; $sex_image = ''; } $html .= '<a ' . $title . ' href="' . $person->getHtmlUrl() . '"' . $class . '>' . FunctionsPrint::highlightSearchHits($name['full']) . '</a>' . $sex_image . '<br>'; } // Indi parents $html .= $person->getPrimaryParentsNames('parents details1', 'none'); $html .= '</td>'; // Dummy column to match colspan in header $html .= '<td style="display:none;"></td>'; //-- 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 .= '<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>'; //-- SOSA if ($option == 'sosa') { $html .= '<td><a href="relationship.php?pid1=' . $datalist[1] . '&pid2=' . $person->getXref() . '" title="' . I18N::translate('Relationships') . '">' . I18N::number($key) . '</a></td><td>' . $key . '</td>'; } else { $html .= '<td></td><td>0</td>'; } //-- Birth date $html .= '<td>'; if ($birth_dates = $person->getAllBirthDates()) { foreach ($birth_dates as $num => $birth_date) { if ($num) { $html .= '<br>'; } $html .= $birth_date->display(true); } if ($birth_dates[0]->gregorianYear() >= 1550 && $birth_dates[0]->gregorianYear() < 2030 && !isset($unique_indis[$person->getXref()])) { $birt_by_decade[(int) ($birth_dates[0]->gregorianYear() / 10) * 10] .= $person->getSex(); } } else { $birth_date = $person->getEstimatedBirthDate(); if ($person->getTree()->getPreference('SHOW_EST_LIST_DATES')) { $html .= $birth_date->display(true); } else { $html .= ' '; } $birth_dates[0] = new Date(''); } $html .= '</td>'; //-- Event date (sortable)hidden by datatables code $html .= '<td>' . $birth_date->julianDay() . '</td>'; //-- Birth anniversary $html .= '<td>' . Date::getAge($birth_dates[0], null, 2) . '</td>'; //-- Birth place $html .= '<td>'; foreach ($person->getAllBirthPlaces() as $n => $birth_place) { $tmp = new Place($birth_place, $person->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 = $person->getNumberOfChildren(); $html .= '<td>' . I18N::number($nchi) . '</td><td>' . $nchi . '</td>'; //-- Death date $html .= '<td>'; if ($death_dates = $person->getAllDeathDates()) { foreach ($death_dates as $num => $death_date) { if ($num) { $html .= '<br>'; } $html .= $death_date->display(true); } if ($death_dates[0]->gregorianYear() >= 1550 && $death_dates[0]->gregorianYear() < 2030 && !isset($unique_indis[$person->getXref()])) { $deat_by_decade[(int) ($death_dates[0]->gregorianYear() / 10) * 10] .= $person->getSex(); } } else { $death_date = $person->getEstimatedDeathDate(); // Estimated death dates are a fixed number of years after the birth date. // Don't show estimates in the future. if ($person->getTree()->getPreference('SHOW_EST_LIST_DATES') && $death_date->minimumJulianDay() < WT_CLIENT_JD) { $html .= $death_date->display(true); } elseif ($person->isDead()) { $html .= I18N::translate('yes'); } else { $html .= ' '; } $death_dates[0] = new Date(''); } $html .= '</td>'; //-- Event date (sortable)hidden by datatables code $html .= '<td>' . $death_date->julianDay() . '</td>'; //-- Death anniversary $html .= '<td>' . Date::getAge($death_dates[0], null, 2) . '</td>'; //-- Age at death $age = Date::getAge($birth_dates[0], $death_dates[0], 0); if (!isset($unique_indis[$person->getXref()]) && $age >= 0 && $age <= $max_age) { $deat_by_age[$age] .= $person->getSex(); } // Need both display and sortable age $html .= '<td>' . Date::getAge($birth_dates[0], $death_dates[0], 2) . '</td><td>' . Date::getAge($birth_dates[0], $death_dates[0], 1) . '</td>'; //-- Death place $html .= '<td>'; foreach ($person->getAllDeathPlaces() as $n => $death_place) { $tmp = new Place($death_place, $person->getTree()); if ($n) { $html .= '<br>'; } $html .= '<a href="' . $tmp->getURL() . '" title="' . strip_tags($tmp->getFullName()) . '">'; $html .= FunctionsPrint::highlightSearchHits($tmp->getShortName()) . '</a>'; } $html .= '</td>'; //-- Last change $html .= '<td>' . $person->lastChangeTimestamp() . '</td>'; $html .= '<td>' . $person->lastChangeTimestamp(true) . '</td>'; //-- Sorting by gender $html .= '<td>' . $person->getSex() . '</td>'; //-- Filtering by birth date $html .= '<td>'; if (!$person->canShow() || Date::compare($birth_date, $d100y) > 0) { $html .= 'Y100'; } else { $html .= 'YES'; } $html .= '</td>'; //-- Filtering by death date $html .= '<td>'; // Died in last 100 years? Died? Not dead? if (Date::compare($death_dates[0], $d100y) > 0) { $html .= 'Y100'; } elseif ($death_dates[0]->minimumJulianDay() || $person->isDead()) { $html .= 'YES'; } else { $html .= 'N'; } $html .= '</td>'; //-- Roots or Leaves ? $html .= '<td>'; if (!$person->getChildFamilies()) { $html .= 'R'; } elseif (!$person->isDead() && $person->getNumberOfChildren() < 1) { $html .= 'L'; } else { $html .= ' '; } $html .= '</td>'; $html .= '</tr>'; $unique_indis[$person->getXref()] = true; } $html .= ' </tbody> </table> <div id="indi_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($deat_by_decade, I18N::translate('Decade of death')) . ' </td> </tr> <tr> <td colspan="2"> ' . self::chartByAge($deat_by_age, I18N::translate('Age related to death year')) . ' </td> </tr> </table> </div> </div>'; return $html; }
/** * Generate the HTML content of this block. * * @param int $block_id * @param bool $template * @param string[] $cfg * * @return string */ public function getBlock($block_id, $template = true, $cfg = array()) { global $WT_TREE, $ctype; $show_last_update = $this->getBlockSetting($block_id, 'show_last_update', '1'); $show_common_surnames = $this->getBlockSetting($block_id, 'show_common_surnames', '1'); $number_of_surnames = $this->getBlockSetting($block_id, 'number_of_surnames', self::DEFAULT_NUMBER_OF_SURNAMES); $stat_indi = $this->getBlockSetting($block_id, 'stat_indi', '1'); $stat_fam = $this->getBlockSetting($block_id, 'stat_fam', '1'); $stat_sour = $this->getBlockSetting($block_id, 'stat_sour', '1'); $stat_media = $this->getBlockSetting($block_id, 'stat_media', '1'); $stat_repo = $this->getBlockSetting($block_id, 'stat_repo', '1'); $stat_surname = $this->getBlockSetting($block_id, 'stat_surname', '1'); $stat_events = $this->getBlockSetting($block_id, 'stat_events', '1'); $stat_users = $this->getBlockSetting($block_id, 'stat_users', '1'); $stat_first_birth = $this->getBlockSetting($block_id, 'stat_first_birth', '1'); $stat_last_birth = $this->getBlockSetting($block_id, 'stat_last_birth', '1'); $stat_first_death = $this->getBlockSetting($block_id, 'stat_first_death', '1'); $stat_last_death = $this->getBlockSetting($block_id, 'stat_last_death', '1'); $stat_long_life = $this->getBlockSetting($block_id, 'stat_long_life', '1'); $stat_avg_life = $this->getBlockSetting($block_id, 'stat_avg_life', '1'); $stat_most_chil = $this->getBlockSetting($block_id, 'stat_most_chil', '1'); $stat_avg_chil = $this->getBlockSetting($block_id, 'stat_avg_chil', '1'); // This can be overriden when embedding in an HTML block $block = '0'; foreach (array('show_common_surnames', 'number_common_surnames', 'stat_indi', 'stat_fam', 'stat_sour', 'stat_media', 'stat_surname', 'stat_events', 'stat_users', 'stat_first_birth', 'stat_last_birth', 'stat_first_death', 'stat_last_death', 'stat_long_life', 'stat_avg_life', 'stat_most_chil', 'stat_avg_chil', 'block') as $name) { if (array_key_exists($name, $cfg)) { ${$name} = $cfg[$name]; } } $id = $this->getName() . $block_id; $class = $this->getName() . '_block'; if ($ctype === 'gedcom' && Auth::isManager($WT_TREE) || $ctype === 'user' && Auth::check()) { $title = '<a class="icon-admin" title="' . I18N::translate('Preferences') . '" href="block_edit.php?block_id=' . $block_id . '&ged=' . $WT_TREE->getNameHtml() . '&ctype=' . $ctype . '"></a>'; } else { $title = ''; } $title .= $this->getTitle() . ' — ' . $WT_TREE->getTitleHtml(); $stats = new Stats($WT_TREE); $content = ''; if ($show_last_update) { $content .= '<p>' . I18N::translate('This family tree was last updated on %s.', strip_tags($stats->gedcomUpdated())) . '</p>'; } /** Responsive Design */ $content .= '<div class="stat-table1">'; if ($stat_indi) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Individuals') . '</div><div class="facts_value stats_value stat-cell"><a href="' . "indilist.php?surname_sublist=no&ged=" . $WT_TREE->getNameUrl() . '">' . $stats->totalIndividuals() . '</a></div></div>'; $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Males') . '</div><div class="facts_value stats_value stat-cell">' . $stats->totalSexMales() . '<br>' . $stats->totalSexMalesPercentage() . '</div></div>'; $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Females') . '</div><div class="facts_value stats_value stat-cell">' . $stats->totalSexFemales() . '<br>' . $stats->totalSexFemalesPercentage() . '</div></div>'; } if ($stat_surname) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Total surnames') . '</div><div class="facts_value stats_value stat-cell"><a href="indilist.php?show_all=yes&surname_sublist=yes&ged=' . $WT_TREE->getNameUrl() . '">' . $stats->totalSurnames() . '</a></div></div>'; } if ($stat_fam) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Families') . '</div><div class="facts_value stats_value stat-cell"><a href="famlist.php?ged=' . $WT_TREE->getNameUrl() . '">' . $stats->totalFamilies() . '</a></div></div>'; } if ($stat_sour) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Sources') . '</div><div class="facts_value stats_value stat-cell"><a href="sourcelist.php?ged=' . $WT_TREE->getNameUrl() . '">' . $stats->totalSources() . '</a></div></div>'; } if ($stat_media) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Media objects') . '</div><div class="facts_value stats_value stat-cell"><a href="medialist.php?ged=' . $WT_TREE->getNameUrl() . '">' . $stats->totalMedia() . '</a></div></div>'; } if ($stat_repo) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Repositories') . '</div><div class="facts_value stats_value stat-cell"><a href="repolist.php?ged=' . $WT_TREE->getNameUrl() . '">' . $stats->totalRepositories() . '</a></div></div>'; } if ($stat_events) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Total events') . '</div><div class="facts_value stats_value stat-cell">' . $stats->totalEvents() . '</div></div>'; } if ($stat_users) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Total users') . '</div><div class="facts_value stats_value stat-cell">'; if (Auth::isManager($WT_TREE)) { $content .= '<a href="admin_users.php">' . $stats->totalUsers() . '</a>'; } else { $content .= $stats->totalUsers(); } $content .= '</div></div>'; } if (!$block) { $content .= '</div><div class="facts_table stat-table2">'; } if ($stat_first_birth) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Earliest birth year') . '</div><div class="facts_value stats_value stat-cell">' . $stats->firstBirthYear() . '</div>'; if (!$block) { $content .= '<div class="facts_value stat-cell left">' . $stats->firstBirth() . '</div>'; } $content .= '</div>'; } if ($stat_last_birth) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Latest birth year') . '</div><div class="facts_value stats_value stat-cell">' . $stats->lastBirthYear() . '</div>'; if (!$block) { $content .= '<div class="facts_value stat-cell left">' . $stats->lastBirth() . '</div>'; } $content .= '</div>'; } if ($stat_first_death) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Earliest death year') . '</div><div class="facts_value stats_value stat-cell">' . $stats->firstDeathYear() . '</div>'; if (!$block) { $content .= '<div class="facts_value stat-cell left">' . $stats->firstDeath() . '</div>'; } $content .= '</div>'; } if ($stat_last_death) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Latest death year') . '</div><div class="facts_value stats_value stat-cell">' . $stats->lastDeathYear() . '</div>'; if (!$block) { $content .= '<div class="facts_value stat-cell left">' . $stats->lastDeath() . '</div>'; } $content .= '</div>'; } if ($stat_long_life) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Individual who lived the longest') . '</div><div class="facts_value stats_value stat-cell">' . $stats->longestLifeAge() . '</div>'; if (!$block) { $content .= '<div class="facts_value stat-cell left">' . $stats->longestLife() . '</div>'; } $content .= '</div>'; } if ($stat_avg_life) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Average age at death') . '</div><div class="facts_value stats_value stat-cell">' . $stats->averageLifespan() . '</div>'; if (!$block) { $content .= '<div class="facts_value stat-cell left">' . I18N::translate('Males') . ': ' . $stats->averageLifespanMale(); $content .= ' ' . I18N::translate('Females') . ': ' . $stats->averageLifespanFemale() . '</div>'; } $content .= '</div>'; } if ($stat_most_chil && !$block) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Family with the most children') . '</div><div class="facts_value stats_value stat-cell">' . $stats->largestFamilySize() . '</div>'; if (!$block) { $content .= '<div class="facts_value stat-cell left">' . $stats->largestFamily() . '</div>'; } $content .= '</div>'; } if ($stat_avg_chil) { $content .= '<div class="stat-row"><div class="facts_label stat-cell">' . I18N::translate('Average number of children per family') . '</div><div class="facts_value stats_value stat-cell">' . $stats->averageChildren() . '</div>'; if (!$block) { $content .= '<div class="facts_value stat-cell left"></div>'; } $content .= '</div>'; } $content .= '</div>'; if ($show_common_surnames) { $surnames = FunctionsDb::getTopSurnames($WT_TREE->getTreeId(), 0, (int) $number_of_surnames); $all_surnames = array(); foreach (array_keys($surnames) as $surname) { $all_surnames = array_merge($all_surnames, QueryName::surnames($WT_TREE, $surname, '', false, false)); } if (!empty($surnames)) { ksort($all_surnames); $content .= '<div class="clearfloat">'; $content .= '<p>'; $content .= '<strong>' . I18N::translate('Most common surnames') . '</strong>'; $content .= '<br>'; $content .= '<span class="common_surnames">' . FunctionsPrintLists::surnameList($all_surnames, 2, false, 'indilist.php', $WT_TREE) . '</span>'; $content .= '</p>'; $content .= '</div>'; } } if ($template) { return Theme::theme()->formatBlock($id, $title, $class, $content); } else { return $content; } }
/** * Print a table of individuals * * @param Individual[] $indiviudals * @param string $option * * @return string */ public static function individualTable($indiviudals, $option = '') { global $controller, $WT_TREE; $table_id = 'table-indi-' . 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 . '">T<"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" }, /* SOSA numnber */ { type: "num", visible: ' . ($option == 'sosa' ? 'true' : 'false') . ' }, /* Birth date */ { type: "num" }, /* Anniversary */ { type: "num" }, /* Birthplace */ { type: "text" }, /* Children */ { type: "num" }, /* Deate date */ { type: "num" }, /* Anniversary */ { type: "num" }, /* Age */ { type: "num" }, /* Death place */ { type: "text" }, /* Last change */ { visible: ' . ($WT_TREE->getPreference('SHOW_LAST_CHANGE') ? 'true' : 'false') . ' }, /* Filter sex */ { sortable: false }, /* Filter birth */ { sortable: false }, /* Filter death */ { sortable: false }, /* Filter tree */ { sortable: false } ], sorting: [[' . ($option == 'sosa' ? '4, "asc"' : '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("#indi_list_table-charts_' . $table_id . '").slideToggle(); }) /* Filter buttons in table header */ .on("click", "button[data-filter-column]", function() { var btn = jQuery(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(".indi-list").css("visibility", "visible"); jQuery(".loading-image").css("display", "none"); '); $stats = new Stats($WT_TREE); // Bad data can cause "longest life" to be huge, blowing memory limits $max_age = min($WT_TREE->getPreference('MAX_ALIVE_AGE'), $stats->longestLifeAge()) + 1; // Inititialise chart data $deat_by_age = array(); for ($age = 0; $age <= $max_age; $age++) { $deat_by_age[$age] = ''; } $birt_by_decade = array(); $deat_by_decade = array(); for ($year = 1550; $year < 2030; $year += 10) { $birt_by_decade[$year] = ''; $deat_by_decade[$year] = ''; } $html = ' <div class="loading-image"></div> <div class="indi-list"> <table id="' . $table_id . '"> <thead> <tr> <th colspan="16"> <div class="btn-toolbar"> <div class="btn-group"> <button class="ui-state-default" data-filter-column="12" data-filter-value="M" title="' . I18N::translate('Show only males.') . '" type="button" > ' . Individual::sexImage('M', 'large') . ' </button> <button class="ui-state-default" data-filter-column="12" data-filter-value="F" title="' . I18N::translate('Show only females.') . '" type="button" > ' . Individual::sexImage('F', 'large') . ' </button> <button class="ui-state-default" data-filter-column="12" data-filter-value="U" title="' . I18N::translate('Show only individuals for whom the gender is not known.') . '" type="button" > ' . Individual::sexImage('U', 'large') . ' </button> </div> <div class="btn-group"> <button class="ui-state-default" data-filter-column="14" data-filter-value="N" title="' . I18N::translate('Show individuals who are alive or couples where both partners are alive.') . '" type="button" > ' . I18N::translate('Alive') . ' </button> <button class="ui-state-default" data-filter-column="14" data-filter-value="Y" title="' . I18N::translate('Show individuals who are dead or couples where both partners are dead.') . '" type="button" > ' . I18N::translate('Dead') . ' </button> <button class="ui-state-default" data-filter-column="14" data-filter-value="YES" title="' . I18N::translate('Show individuals who died more than 100 years ago.') . '" type="button" > ' . GedcomTag::getLabel('DEAT') . '>100 </button> <button class="ui-state-default" data-filter-column="14" data-filter-value="Y100" title="' . I18N::translate('Show individuals who died within the last 100 years.') . '" type="button" > ' . GedcomTag::getLabel('DEAT') . '<=100 </button> </div> <div class="btn-group"> <button class="ui-state-default" data-filter-column="13" data-filter-value="YES" title="' . I18N::translate('Show individuals born more than 100 years ago.') . '" type="button" > ' . GedcomTag::getLabel('BIRT') . '>100 </button> <button class="ui-state-default" data-filter-column="13" data-filter-value="Y100" title="' . I18N::translate('Show individuals born within the last 100 years.') . '" type="button" > ' . GedcomTag::getLabel('BIRT') . '<=100 </button> </div> <div class="btn-group"> <button class="ui-state-default" data-filter-column="15" data-filter-value="R" 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.') . '" type="button" > ' . I18N::translate('Roots') . ' </button> <button class="ui-state-default" data-filter-column="15" data-filter-value="L" title="' . I18N::translate('Show “leaves” couples or individuals. These are individuals who are alive but have no children recorded in the database.') . '" type="button" > ' . I18N::translate('Leaves') . ' </button> </div> </div> </th> </tr> <tr> <th>' . GedcomTag::getLabel('GIVN') . '</th> <th>' . GedcomTag::getLabel('SURN') . '</th> <th>' . I18N::translate('Sosa') . '</th> <th>' . GedcomTag::getLabel('BIRT') . '</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('DEAT') . '</th> <th><i class="icon-reminder" title="' . I18N::translate('Anniversary') . '"></i></th> <th>' . GedcomTag::getLabel('AGE') . '</th> <th>' . GedcomTag::getLabel('PLAC') . '</th> <th>' . GedcomTag::getLabel('CHAN') . '</th> <th hidden></th> <th hidden></th> <th hidden></th> <th hidden></th> </tr> </thead> <tfoot> <tr> <th colspan="16"> <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); $unique_indis = array(); // Don't double-count indis with multiple names. foreach ($indiviudals as $key => $individual) { if (!$individual->canShowName()) { continue; } if ($individual->isPendingAddtion()) { $class = ' class="new"'; } elseif ($individual->isPendingDeletion()) { $class = ' class="old"'; } else { $class = ''; } $html .= '<tr' . $class . '>'; // Extract Given names and Surnames for sorting list($surn_givn, $givn_surn) = self::sortableNames($individual); $html .= '<td colspan="2" data-sort="' . Filter::escapeHtml($givn_surn) . '">'; foreach ($individual->getAllNames() as $num => $name) { if ($name['type'] == 'NAME') { $title = ''; } else { $title = 'title="' . strip_tags(GedcomTag::getLabel($name['type'], $individual)) . '"'; } if ($num == $individual->getPrimaryName()) { $class = ' class="name2"'; $sex_image = $individual->getSexImage(); } else { $class = ''; $sex_image = ''; } $html .= '<a ' . $title . ' href="' . $individual->getHtmlUrl() . '"' . $class . '>' . FunctionsPrint::highlightSearchHits($name['full']) . '</a>' . $sex_image . '<br>'; } $html .= $individual->getPrimaryParentsNames('parents details1', 'none'); $html .= '</td>'; // Hidden column for sortable name $html .= '<td hidden data-sort="' . Filter::escapeHtml($surn_givn) . '"></td>'; // SOSA $html .= '<td class="center" data-sort="' . $key . '"><a href="relationship.php?pid1=' . $indiviudals[1] . '&pid2=' . $individual->getXref() . '" title="' . I18N::translate('Relationships') . '">' . I18N::number($key) . '</a></td>'; // Birth date $birth_dates = $individual->getAllBirthDates(); $html .= '<td data-sort="' . $individual->getEstimatedBirthDate()->julianDay() . '">'; foreach ($birth_dates as $n => $birth_date) { if ($n > 0) { $html .= '<br>'; } $html .= $birth_date->display(true); } $html .= '</td>'; // Birth anniversary if (isset($birth_dates[0]) && $birth_dates[0]->gregorianYear() >= 1550 && $birth_dates[0]->gregorianYear() < 2030 && !isset($unique_indis[$individual->getXref()])) { $birt_by_decade[(int) ($birth_dates[0]->gregorianYear() / 10) * 10] .= $individual->getSex(); $anniversary = Date::getAge($birth_dates[0], null, 2); } else { $anniversary = ''; } $html .= '<td class="center" data-sort="' . -$individual->getEstimatedBirthDate()->julianDay() . '">' . $anniversary . '</td>'; // Birth place $html .= '<td>'; foreach ($individual->getAllBirthPlaces() as $n => $birth_place) { $tmp = new Place($birth_place, $individual->getTree()); if ($n > 0) { $html .= '<br>'; } $html .= '<a href="' . $tmp->getURL() . '" title="' . strip_tags($tmp->getFullName()) . '">'; $html .= FunctionsPrint::highlightSearchHits($tmp->getShortName()) . '</a>'; } $html .= '</td>'; // Number of children $number_of_children = $individual->getNumberOfChildren(); $html .= '<td class="center" data-sort="' . $number_of_children . '">' . I18N::number($number_of_children) . '</td>'; // Death date $death_dates = $individual->getAllDeathDates(); $html .= '<td data-sort="' . $individual->getEstimatedDeathDate()->julianDay() . '">'; foreach ($death_dates as $num => $death_date) { if ($num) { $html .= '<br>'; } $html .= $death_date->display(true); } $html .= '</td>'; // Death anniversary if (isset($death_dates[0]) && $death_dates[0]->gregorianYear() >= 1550 && $death_dates[0]->gregorianYear() < 2030 && !isset($unique_indis[$individual->getXref()])) { $birt_by_decade[(int) ($death_dates[0]->gregorianYear() / 10) * 10] .= $individual->getSex(); $anniversary = Date::getAge($death_dates[0], null, 2); } else { $anniversary = ''; } $html .= '<td class="center" data-sort="' . -$individual->getEstimatedDeathDate()->julianDay() . '">' . $anniversary . '</td>'; // Age at death if (isset($birth_dates[0]) && isset($death_dates[0])) { $age_at_death = Date::getAge($birth_dates[0], $death_dates[0], 0); $age_at_death_sort = Date::getAge($birth_dates[0], $death_dates[0], 2); if (!isset($unique_indis[$individual->getXref()]) && $age >= 0 && $age <= $max_age) { $deat_by_age[$age_at_death] .= $individual->getSex(); } } else { $age_at_death = ''; $age_at_death_sort = PHP_INT_MAX; } $html .= '<td class="center" data-sort="' . $age_at_death_sort . '">' . $age_at_death . '</td>'; // Death place $html .= '<td>'; foreach ($individual->getAllDeathPlaces() as $n => $death_place) { $tmp = new Place($death_place, $individual->getTree()); if ($n > 0) { $html .= '<br>'; } $html .= '<a href="' . $tmp->getURL() . '" title="' . strip_tags($tmp->getFullName()) . '">'; $html .= FunctionsPrint::highlightSearchHits($tmp->getShortName()) . '</a>'; } $html .= '</td>'; // Last change $html .= '<td data-sort="' . $individual->lastChangeTimestamp(true) . '">' . $individual->lastChangeTimestamp() . '</td>'; // Filter by sex $html .= '<td hidden>' . $individual->getSex() . '</td>'; // Filter by birth date $html .= '<td hidden>'; if (!$individual->canShow() || Date::compare($individual->getEstimatedBirthDate(), $hundred_years_ago) > 0) { $html .= 'Y100'; } else { $html .= 'YES'; } $html .= '</td>'; // Filter by death date $html .= '<td hidden>'; // Died in last 100 years? Died? Not dead? if (isset($death_dates[0]) && Date::compare($death_dates[0], $hundred_years_ago) > 0) { $html .= 'Y100'; } elseif ($individual->isDead()) { $html .= 'YES'; } else { $html .= 'N'; } $html .= '</td>'; // Filter by roots/leaves $html .= '<td hidden>'; if (!$individual->getChildFamilies()) { $html .= 'R'; } elseif (!$individual->isDead() && $individual->getNumberOfChildren() < 1) { $html .= 'L'; $html .= ' '; } $html .= '</td>'; $html .= '</tr>'; $unique_indis[$individual->getXref()] = true; } $html .= ' </tbody> </table> <div id="indi_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($deat_by_decade, I18N::translate('Decade of death')) . ' </td> </tr> <tr> <td colspan="2"> ' . self::chartByAge($deat_by_age, I18N::translate('Age related to death year')) . ' </td> </tr> </table> </div> </div>'; return $html; }