/**
  * Generate the likely value of this census column, based on available information.
  *
  * @param Individual      $individual
  * @param Individual|null $head
  *
  * @return string
  */
 public function generate(Individual $individual, Individual $head = null)
 {
     if ($individual->getSex() !== 'F') {
         return '';
     }
     $count = 0;
     foreach ($individual->getSpouseFamilies() as $family) {
         foreach ($family->getChildren() as $child) {
             if ($child->getBirthDate()->isOK() && Date::compare($child->getBirthDate(), $this->date()) < 0 && $child->getBirthDate() != $child->getDeathDate() && (!$child->getDeathDate()->isOK() || Date::compare($child->getDeathDate(), $this->date()) > 0)) {
                 $count++;
             }
         }
     }
     return (string) $count;
 }
 /**
  * Generate the likely value of this census column, based on available information.
  *
  * @param Individual      $individual
  * @param Individual|null $head
  *
  * @return string
  */
 public function generate(Individual $individual, Individual $head = null)
 {
     $marriage_date = null;
     foreach ($individual->getSpouseFamilies() as $family) {
         foreach ($family->getFacts('MARR', true) as $fact) {
             if ($fact->getDate()->isOK() && Date::compare($fact->getDate(), $this->date()) <= 0) {
                 $marriage_date = $fact->getDate();
             }
         }
     }
     if ($marriage_date === null) {
         return '';
     } else {
         return (string) Date::getAge($marriage_date, $this->date(), 0);
     }
 }
 /**
  * Generate the likely value of this census column, based on available information.
  *
  * @param Individual      $individual
  * @param Individual|null $head
  *
  * @return string
  */
 public function generate(Individual $individual, Individual $head = null)
 {
     $place = $individual->getBirthPlace();
     // Did we emigrate or naturalise?
     foreach ($individual->getFacts('IMMI|EMIG|NATU', true) as $fact) {
         if (Date::compare($fact->getDate(), $this->date()) <= 0) {
             $place = $fact->getPlace()->getGedcomName();
         }
     }
     $place = explode(', ', $place);
     $place = end($place);
     if ($place === 'England' || $place === 'Scotland' || $place === 'Wales') {
         return 'British';
     } else {
         return $place;
     }
 }
 /**
  * What was an individual's likely name on a given date, allowing
  * for marriages and married names.
  *
  * @param Individual $individual
  * @param Date       $census_date
  *
  * @return string[]
  */
 protected function nameAtCensusDate(Individual $individual, Date $census_date)
 {
     $names = $individual->getAllNames();
     $name = $names[0];
     foreach ($individual->getSpouseFamilies() as $family) {
         foreach ($family->getFacts('MARR') as $marriage) {
             if ($marriage->getDate()->isOK() && Date::compare($marriage->getDate(), $census_date) < 0) {
                 $spouse = $family->getSpouse($individual);
                 foreach ($names as $individual_name) {
                     foreach ($spouse->getAllNames() as $spouse_name) {
                         if ($individual_name['type'] === '_MARNM' && $individual_name['surn'] === $spouse_name['surn']) {
                             return $individual_name;
                         }
                     }
                 }
             }
         }
     }
     return $name;
 }
Exemple #5
0
         $gedrec .= FunctionsEdit::addNewFact($match);
     }
 }
 $gedrec .= "\n" . GedcomCodePedi::createNewFamcPedi($PEDI, $xref);
 if (Filter::postBool('SOUR_INDI')) {
     $gedrec = FunctionsEdit::handleUpdates($gedrec);
 } else {
     $gedrec = FunctionsEdit::updateRest($gedrec);
 }
 // Create the new child
 $new_child = $family->getTree()->createRecord($gedrec);
 // Insert new child at the right place
 $done = false;
 foreach ($family->getFacts('CHIL') as $fact) {
     $old_child = $fact->getTarget();
     if ($old_child && 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 (Filter::post('goto') === 'new') {
     $controller->addInlineJavascript('closePopupAndReloadParent("' . $new_child->getRawUrl() . '");');
 } else {
     $controller->addInlineJavascript('closePopupAndReloadParent();');
 }
 /**
  * Function checkFact
  *
  * Does this fact meet the search criteria?
  *
  * @todo This function is public to support the PHP5.3 closure workaround.
  *
  * @param  Fact $fact
  *
  * @return bool
  */
 public function checkFact(Fact $fact)
 {
     $valid = !in_array($fact->getTag(), $this->nonfacts);
     if ($valid && $this->place_obj) {
         $valid = stripos($fact->getPlace()->getGedcomName(), $this->place_obj->getGedcomName()) !== false;
     }
     if ($valid && $this->startDate) {
         if ($this->strictDate && $this->calendar !== $this->defaultCalendar) {
             $valid = stripos($fact->getAttribute('DATE'), $this->calendar) !== false;
         }
         if ($valid) {
             $date = $fact->getDate();
             $valid = $date->isOK() && Date::compare($date, $this->startDate) >= 0 && Date::compare($date, $this->endDate) <= 0;
         }
     }
     return $valid;
 }
    /**
     * {@inhericDoc}
     * @see \MyArtJaub\Webtrees\Mvc\View\AbstractView::renderContent()
     */
    protected function renderContent()
    {
        if ($this->data->get('has_sosa', false)) {
            $table_id = $this->data->get('table_id');
            ?>
   
        
        <div id="sosa-indi-list" class="sosa-list">
        	<table id="<?php 
            echo $table_id;
            ?>
">
				<thead>
    				<tr>
    					<th colspan="22">
    						<div class="btn-toolbar">
    							<div class="btn-group">
    								<button
    									class="ui-state-default"
    									data-filter-column="18"
    									data-filter-value="M"
    									title="<?php 
            echo I18N::translate('Show only males.');
            ?>
"
    									type="button"
    								><?php 
            echo Individual::sexImage('M', 'large');
            ?>
    								</button>
    								<button
    									class="ui-state-default"
    									data-filter-column="18"
    									data-filter-value="F"
    									title="<?php 
            echo I18N::translate('Show only females.');
            ?>
"
    									type="button"
    								>
    									<?php 
            echo Individual::sexImage('F', 'large');
            ?>
    								</button>
    								<button
    									class="ui-state-default"
    									data-filter-column="18"
    									data-filter-value="U"
    									title="<?php 
            echo I18N::translate('Show only individuals for whom the gender is not known.');
            ?>
"
    									type="button"
    								>
    									<?php 
            echo Individual::sexImage('U', 'large');
            ?>
    								</button>
    							</div>
    							<div class="btn-group">
    								<button
    									class="ui-state-default"
    									data-filter-column="20"
    									data-filter-value="N"
    									title="<?php 
            echo I18N::translate('Show individuals who are alive or couples where both partners are alive.');
            ?>
"
    									type="button"
    								>
    									<?php 
            echo I18N::translate('Alive');
            ?>
    								</button>
    								<button
    									class="ui-state-default"
    									data-filter-column="20"
    									data-filter-value="Y"
    									title="<?php 
            echo I18N::translate('Show individuals who are dead or couples where both partners are deceased.');
            ?>
"
    									type="button"
    								>
    									<?php 
            echo I18N::translate('Dead');
            ?>
    								</button>
    								<button
    									class="ui-state-default"
    									data-filter-column="20"
    									data-filter-value="YES"
    									title="<?php 
            echo I18N::translate('Show individuals who died more than 100 years ago.');
            ?>
"
    									type="button"
    								><?php 
            echo GedcomTag::getLabel('DEAT');
            ?>
&gt;100
    								</button>
    								<button
    									class="ui-state-default"
    									data-filter-column="20"
    									data-filter-value="Y100"
    									title="<?php 
            echo I18N::translate('Show individuals who died within the last 100 years.');
            ?>
"
    									type="button"
    								><?php 
            echo GedcomTag::getLabel('DEAT');
            ?>
&lt;=100
    								</button>
    							</div>
    							<div class="btn-group">
    								<button
    									class="ui-state-default"
    									data-filter-column="19"
    									data-filter-value="YES"
    									title="<?php 
            echo I18N::translate('Show individuals born more than 100 years ago.');
            ?>
"
    									type="button"
    								><?php 
            echo GedcomTag::getLabel('BIRT');
            ?>
&gt;100
    								</button>
    								<button
    									class="ui-state-default"
    									data-filter-column="19"
    									data-filter-value="Y100"
    									title="<?php 
            echo I18N::translate('Show individuals born within the last 100 years.');
            ?>
"
    									type="button"
    								><?php 
            echo GedcomTag::getLabel('BIRT');
            ?>
&lt;=100
    								</button>
    							</div>
    							<div class="btn-group">
    								<button
    									class="ui-state-default"
    									data-filter-column="21"
    									data-filter-value="R"
    									title="<?php 
            echo 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"
    								>
    									<?php 
            echo I18N::translate('Roots');
            ?>
    								</button>
    								<button
    									class="ui-state-default"
    									data-filter-column="21"
    									data-filter-value="L"
    									title="<?php 
            echo I18N::translate('Show “leaves” couples or individuals.  These are individuals who are alive but have no children recorded in the database.');
            ?>
"
    									type="button"
    								>
    									<?php 
            echo I18N::translate('Leaves');
            ?>
    								</button>
    							</div>
    						</div>
    					</th>
    				</tr>
					<tr>
						<th><?php 
            echo I18N::translate('Sosa');
            ?>
</th>
						<th><?php 
            echo GedcomTag::getLabel('INDI');
            ?>
</th>
						<th><?php 
            echo GedcomTag::getLabel('GIVN');
            ?>
</th>
						<th><?php 
            echo GedcomTag::getLabel('SURN');
            ?>
</th>
						<th>GIVN</th>
						<th>SURN</th>
						<th><?php 
            echo GedcomTag::getLabel('BIRT');
            ?>
</th>
						<th>SORT_BIRT</th>
						<th><?php 
            echo GedcomTag::getLabel('PLAC');
            ?>
</th>
						<?php 
            if (ModuleManager::getInstance()->isOperational(Constants::MODULE_MAJ_ISSOURCED_NAME)) {
                ?>
						<th><i class="icon-source" title="<?php 
                echo I18N::translate('Sourced birth');
                ?>
" border="0"></i></th>
						<th>SORT_BIRTSC</th>
						<?php 
            } else {
                ?>
						<th></th>
						<th></th>
						<?php 
            }
            ?>
						<th><?php 
            echo GedcomTag::getLabel('DEAT');
            ?>
</th>
						<th>SORT_DEAT</th>
						<th><?php 
            echo GedcomTag::getLabel('AGE');
            ?>
</th>
						<th>AGE</th>
						<th><?php 
            echo GedcomTag::getLabel('PLAC');
            ?>
</th>
						<?php 
            if (ModuleManager::getInstance()->isOperational(Constants::MODULE_MAJ_ISSOURCED_NAME)) {
                ?>
						<th><i class="icon-source" title="<?php 
                echo I18N::translate('Sourced death');
                ?>
" border="0"></i></th>
						<th>SORT_DEATSC</th>
						<?php 
            } else {
                ?>
						<th></th>
						<th></th>
						<?php 
            }
            ?>
						<th>SEX</th>
						<th>BIRT</th>
						<th>DEAT</th>
						<th>TREE</th>
					</tr>
				</thead>
			<tbody>
			
			<?php 
            foreach ($this->data->get('sosa_list') as $sosa => $person) {
                /** @var \Fisharebest\Webtrees\Individual $person */
                if ($person->isPendingAddtion()) {
                    $class = ' class="new"';
                } elseif ($person->isPendingDeletion()) {
                    $class = ' class="old"';
                } else {
                    $class = '';
                }
                $dperson = new \MyArtJaub\Webtrees\Individual($person);
                ?>
			
        		<tr <?php 
                echo $class;
                ?>
>
        			<td class="transparent"><?php 
                echo $sosa;
                ?>
</td>
        			<td class="transparent"><?php 
                echo $person->getXref();
                ?>
</td>
        			<td colspan="2">
        			<?php 
                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 = '';
                    }
                    ?>
        				<a <?php 
                    echo $title . ' ' . $class;
                    ?>
 href="<?php 
                    echo $person->getHtmlUrl();
                    ?>
">
        					<?php 
                    echo \Fisharebest\Webtrees\Functions\FunctionsPrint::highlightSearchHits($name['full']);
                    ?>
        				</a>
        				<?php 
                    echo $sex_image . FunctionsPrint::formatSosaNumbers($dperson->getSosaNumbers(), 1, 'smaller');
                    ?>
        				<br/>
            		<?php 
                }
                echo $person->getPrimaryParentsNames('parents details1', 'none');
                ?>
            		</td>
            		<td style="display:none;"></td>
            		<td>
            			<?php 
                echo Filter::escapeHtml(str_replace('@P.N.', 'AAAA', $givn)) . 'AAAA' . Filter::escapeHtml(str_replace('@N.N.', 'AAAA', $surn));
                ?>
            		</td>
            		<td>
            			<?php 
                echo Filter::escapeHtml(str_replace('@N.N.', 'AAAA', $surn)) . 'AAAA' . Filter::escapeHtml(str_replace('@P.N.', 'AAAA', $givn));
                ?>
            		</td>
            		<td>
            		<?php 
                if ($birth_dates = $person->getAllBirthDates()) {
                    foreach ($birth_dates as $num => $birth_date) {
                        if ($num) {
                            ?>
<br/><?php 
                        }
                        ?>
    						<?php 
                        echo $birth_date->display(true);
                    }
                } else {
                    $birth_date = new Date('');
                    if ($person->getTree()->getPreference('SHOW_EST_LIST_DATES')) {
                        $birth_date = $person->getEstimatedBirthDate();
                        echo $birth_date->display(true);
                    } else {
                        echo '&nbsp;';
                    }
                    $birth_dates[0] = new Date('');
                }
                ?>
            		</td>
            		<td><?php 
                echo $birth_date->julianDay();
                ?>
</td>
        			<td>
        			<?php 
                foreach ($person->getAllBirthPlaces() as $n => $birth_place) {
                    $tmp = new \Fisharebest\Webtrees\Place($birth_place, $person->getTree());
                    if ($n) {
                        ?>
<br><?php 
                    }
                    ?>
        				<a href="'<?php 
                    echo $tmp->getURL();
                    ?>
" title="<?php 
                    echo strip_tags($tmp->getFullName());
                    ?>
">
        					<?php 
                    echo \Fisharebest\Webtrees\Functions\FunctionsPrint::highlightSearchHits($tmp->getShortName());
                    ?>
        				</a>
        			<?php 
                }
                ?>
        			</td>
        			<?php 
                if (ModuleManager::getInstance()->isOperational(Constants::MODULE_MAJ_ISSOURCED_NAME)) {
                    $isBSourced = $dperson->isBirthSourced();
                    ?>
				   	<td><?php 
                    echo FunctionsPrint::formatIsSourcedIcon('E', $isBSourced, 'BIRT', 1, 'medium');
                    ?>
</td>
					<td><?php 
                    echo $isBSourced;
                    ?>
</td>
					<?php 
                } else {
                    ?>
					<td>&nbsp;</td>
					<td></td>
					<?php 
                }
                ?>
					<td>
					<?php 
                if ($death_dates = $person->getAllDeathDates()) {
                    foreach ($death_dates as $num => $death_date) {
                        if ($num) {
                            ?>
<br/><?php 
                        }
                        ?>
					 		<?php 
                        echo $death_date->display(true);
                    }
                } else {
                    $death_date = $person->getEstimatedDeathDate();
                    if ($person->getTree()->getPreference('SHOW_EST_LIST_DATES') && $death_date->minimumJulianDay() < WT_CLIENT_JD) {
                        echo $death_date->display(true);
                    } elseif ($person->isDead()) {
                        echo I18N::translate('yes');
                        $death_date = new Date('');
                    } else {
                        echo '&nbsp;';
                        $death_date = new Date('');
                    }
                    $death_dates[0] = new Date('');
                }
                ?>
			         </td>
			         <td><?php 
                echo $death_date->julianDay();
                ?>
</td>
			         <td><?php 
                echo Date::getAge($birth_dates[0], $death_dates[0], 2);
                ?>
</td>
			         <td><?php 
                echo Date::getAge($birth_dates[0], $death_dates[0], 1);
                ?>
</td>
			         <td>
        			 <?php 
                foreach ($person->getAllDeathPlaces() as $n => $death_place) {
                    $tmp = new Place($death_place, $person->getTree());
                    if ($n) {
                        ?>
<br><?php 
                    }
                    ?>
        				<a href="'<?php 
                    echo $tmp->getURL();
                    ?>
" title="<?php 
                    echo strip_tags($tmp->getFullName());
                    ?>
">
        					<?php 
                    echo \Fisharebest\Webtrees\Functions\FunctionsPrint::highlightSearchHits($tmp->getShortName());
                    ?>
        				</a>
        			<?php 
                }
                ?>
        			</td>
        			<?php 
                if (ModuleManager::getInstance()->isOperational(Constants::MODULE_MAJ_ISSOURCED_NAME)) {
                    $isDSourced = $dperson->isDeathSourced();
                    ?>
				   	<td><?php 
                    echo FunctionsPrint::formatIsSourcedIcon('E', $isDSourced, 'DEAT', 1, 'medium');
                    ?>
</td>
					<td><?php 
                    echo $isDSourced;
                    ?>
</td>
					<?php 
                } else {
                    ?>
					<td>&nbsp;</td>
					<td></td>
					<?php 
                }
                ?>
					<td><?php 
                echo $person->getSex();
                ?>
</td>
					<td>
					<?php 
                if (!$person->canShow() || Date::compare($birth_date, new Date(date('Y') - 100)) > 0) {
                    echo 'Y100';
                } else {
                    echo 'YES';
                }
                ?>
        			</td>
        			<td>
        			<?php 
                if (Date::compare($death_dates[0], new Date(date('Y') - 100)) > 0) {
                    echo 'Y100';
                } elseif ($death_dates[0]->minimumJulianDay() || $person->isDead()) {
                    echo 'YES';
                } else {
                    echo 'N';
                }
                ?>
			         </td>
			         <td>
					<?php 
                if (!$person->getChildFamilies()) {
                    echo 'R';
                } elseif (!$person->isDead() && $person->getNumberOfChildren() < 1) {
                    echo 'L';
                } else {
                    echo '&nbsp;';
                }
                ?>
					</td>
				</tr>
        	<?php 
            }
            ?>
        	</tbody>
        	<tfoot>
				<tr>
					<th class="ui-state-default" colspan="22">
						<div class="center">
							<?php 
            echo I18N::translate('Number of Sosa ancestors: %1$s known / %2$s theoretical (%3$s)', I18N::number($this->data->get('sosa_count')), I18N::number($this->data->get('sosa_theo')), I18N::percentage($this->data->get('sosa_ratio'), 2));
            ?>
							<?php 
            if ($this->data->get('sosa_hidden') > 0) {
                echo '[' . I18N::translate('%s hidden', I18N::number($this->data->get('sosa_hidden'))) . ']';
            }
            ?>
						</div>
					</th>
				</tr>
				<tr>
					<th colspan="22">
						<div class="btn-toolbar">
							<div class="btn-group">
								<button type="button" class="ui-state-default btn-toggle-parents">
									<?php 
            echo I18N::translate('Show parents');
            ?>
								</button>
								<button id="btn-toggle-statistics-<?php 
            echo $table_id;
            ?>
" type="button" class="ui-state-default btn-toggle-statistics">
									<?php 
            echo I18N::translate('Show statistics charts');
            ?>
								</button>
							</div>
						</div>
					</th>
				</tr>
			</tfoot>
        	</table>
				<div id="indi_list_table-charts_<?php 
            echo $table_id;
            ?>
" style="display:none">
					<table class="list-charts">
						<tr>
							<td><?php 
            echo $this->data->get('chart_births');
            ?>
</td>
							<td><?php 
            echo $this->data->get('chart_deaths');
            ?>
</td>
						</tr>
						<tr>
							<td colspan="2"><?php 
            echo $this->data->get('chart_ages');
            ?>
</td>
						</tr>
					</table>
				</div>
			</div>
		<?php 
        }
    }
Exemple #8
0
 /**
  * Static helper function to sort an array of people by death date
  *
  * @param Individual $x
  * @param Individual $y
  *
  * @return int
  */
 public static function compareDeathDate(Individual $x, Individual $y)
 {
     return Date::compare($x->getEstimatedDeathDate(), $y->getEstimatedDeathDate());
 }
Exemple #9
0
											);'>
											<?php 
        echo $gparent->getFullName();
        ?>
										</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 = 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 = strip_tags($chnam[0]['full']);
                // Child’s Full Name// Child’s Full Name
                $chdob = $chchild->getBirthDate()->julianDay();
                // Child’s Date of Birth (Julian)
                $chdod = $chchild->getDeathDate()->julianDay();
                // Child’s Date of Death (Julian)
                $chBLD = $chfulln . ', ' . $chdob . ', ' . $chdod;
                array_push($chBLDarray, $chBLD);
 /**
  * Get any historical events.
  *
  * @param Individual $person
  *
  * @return Fact[]
  */
 private static function historicalFacts(Individual $person)
 {
     $SHOW_RELATIVES_EVENTS = $person->getTree()->getPreference('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(Site::getPreference('INDEX_DIRECTORY') . 'histo.' . WT_LOCALE . '.php')) {
             $histo = array();
             require Site::getPreference('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 Fact($hist, $person, 'histo');
                 $sdate = $fact->getDate();
                 if ($sdate->isOK() && Date::compare($birt_date, $sdate) <= 0 && Date::compare($sdate, $deat_date) <= 0) {
                     $facts[] = $fact;
                 }
             }
         }
     }
     return $facts;
 }
 /**
  * XML <List>
  *
  * @param array $attrs an array of key value pairs for the attributes
  */
 private function listStartHandler($attrs)
 {
     global $WT_TREE;
     $this->process_repeats++;
     if ($this->process_repeats > 1) {
         return;
     }
     $match = array();
     if (isset($attrs['sortby'])) {
         $sortby = $attrs['sortby'];
         if (preg_match("/\\\$(\\w+)/", $sortby, $match)) {
             $sortby = $this->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 = Database::prepare("SELECT xref, 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 = :tree_id" . "  GROUP BY xref" . " )")->execute(array('tree_id' => $WT_TREE->getTreeId()))->fetchAll();
             $this->list = array();
             foreach ($rows as $row) {
                 $this->list[] = GedcomRecord::getInstance($row->xref, $WT_TREE, $row->gedcom);
             }
             break;
         case 'individual':
             $sql_select = "SELECT i_id AS xref, i_gedcom AS gedcom FROM `##individuals` ";
             $sql_join = "";
             $sql_where = " WHERE i_file = :tree_id";
             $sql_order_by = "";
             $sql_params = array('tree_id' => $WT_TREE->getTreeId());
             foreach ($attrs as $attr => $value) {
                 if (strpos($attr, 'filter') === 0 && $value) {
                     $value = $this->substituteVars($value, false);
                     // 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=i_file AND {$attr}.d_gid=i_id)";
                         $sql_where .= " AND {$attr}.d_fact = :{$attr}fact";
                         $sql_params[$attr . 'fact'] = $match[1];
                         $date = new Date($match[3]);
                         if ($match[2] == "LTE") {
                             $sql_where .= " AND {$attr}.d_julianday2 <= :{$attr}date";
                             $sql_params[$attr . 'date'] = $date->maximumJulianDay();
                         } else {
                             $sql_where .= " AND {$attr}.d_julianday1 >= :{$attr}date";
                             $sql_params[$attr . 'date'] = $date->minimumJulianDay();
                         }
                         if ($sortby == $match[1]) {
                             $sortby = "";
                             $sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.d_julianday1";
                         }
                         unset($attrs[$attr]);
                         // This filter has been fully processed
                     } elseif (preg_match('/^NAME CONTAINS (.*)$/', $value, $match)) {
                         // Do nothing, unless you have to
                         if ($match[1] != '' || $sortby == 'NAME') {
                             $sql_join .= " JOIN `##name` AS {$attr} ON (n_file=i_file AND n_id=i_id)";
                             // Search the DB only if there is any name supplied
                             if ($match[1] != "") {
                                 $names = explode(" ", $match[1]);
                                 foreach ($names as $n => $name) {
                                     $sql_where .= " AND {$attr}.n_full LIKE CONCAT('%', :{$attr}name{$n}, '%')";
                                     $sql_params[$attr . 'name' . $n] = $name;
                                 }
                             }
                             // Let the DB do the name sorting even when no name was entered
                             if ($sortby == "NAME") {
                                 $sortby = "";
                                 $sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.n_sort";
                             }
                         }
                         unset($attrs[$attr]);
                         // This filter has been fully processed
                     } elseif (preg_match('/^REGEXP \\/(.+)\\//', $value, $match)) {
                         $sql_where .= " AND i_gedcom REGEXP :{$attr}gedcom";
                         // PDO helpfully escapes backslashes for us, preventing us from matching "\n1 FACT"
                         $sql_params[$attr . 'gedcom'] = str_replace('\\n', "\n", $match[1]);
                         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 = i_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 = i_id)";
                         $sql_where .= " AND {$attr}a.p_place LIKE CONCAT('%', :{$attr}place, '%')";
                         $sql_params[$attr . 'place'] = $match[1];
                         // Don't unset this filter. This is just initial filtering
                     } elseif (preg_match('/^(\\w*):*(\\w*) CONTAINS (.+)$/', $value, $match)) {
                         $sql_where .= " AND i_gedcom LIKE CONCAT('%', :{$attr}contains1, '%', :{$attr}contains2, '%', :{$attr}contains3, '%')";
                         $sql_params[$attr . 'contains1'] = $match[1];
                         $sql_params[$attr . 'contains2'] = $match[2];
                         $sql_params[$attr . 'contains3'] = $match[3];
                         // Don't unset this filter. This is just initial filtering
                     }
                 }
             }
             $this->list = array();
             $rows = Database::prepare($sql_select . $sql_join . $sql_where . $sql_order_by)->execute($sql_params)->fetchAll();
             foreach ($rows as $row) {
                 $this->list[$row->xref] = Individual::getInstance($row->xref, $WT_TREE, $row->gedcom);
             }
             break;
         case 'family':
             $sql_select = "SELECT f_id AS xref, f_gedcom AS gedcom FROM `##families`";
             $sql_join = "";
             $sql_where = " WHERE f_file = :tree_id";
             $sql_order_by = "";
             $sql_params = array('tree_id' => $WT_TREE->getTreeId());
             foreach ($attrs as $attr => $value) {
                 if (strpos($attr, 'filter') === 0 && $value) {
                     $value = $this->substituteVars($value, false);
                     // 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=f_file AND {$attr}.d_gid=f_id)";
                         $sql_where .= " AND {$attr}.d_fact = :{$attr}fact";
                         $sql_params[$attr . 'fact'] = $match[1];
                         $date = new Date($match[3]);
                         if ($match[2] == "LTE") {
                             $sql_where .= " AND {$attr}.d_julianday2 <= :{$attr}date";
                             $sql_params[$attr . 'date'] = $date->maximumJulianDay();
                         } else {
                             $sql_where .= " AND {$attr}.d_julianday1 >= :{$attr}date";
                             $sql_params[$attr . 'date'] = $date->minimumJulianDay();
                         }
                         if ($sortby == $match[1]) {
                             $sortby = "";
                             $sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.d_julianday1";
                         }
                         unset($attrs[$attr]);
                         // This filter has been fully processed
                     } elseif (preg_match('/^REGEXP \\/(.+)\\//', $value, $match)) {
                         $sql_where .= " AND f_gedcom REGEXP :{$attr}gedcom";
                         // PDO helpfully escapes backslashes for us, preventing us from matching "\n1 FACT"
                         $sql_params[$attr . 'gedcom'] = str_replace('\\n', "\n", $match[1]);
                         unset($attrs[$attr]);
                         // This filter has been fully processed
                     } elseif (preg_match('/^NAME CONTAINS (.+)$/', $value, $match)) {
                         // Do nothing, unless you have to
                         if ($match[1] != '' || $sortby == 'NAME') {
                             $sql_join .= " JOIN `##name` AS {$attr} ON n_file = f_file AND n_id IN (f_husb, f_wife)";
                             // Search the DB only if there is any name supplied
                             if ($match[1] != "") {
                                 $names = explode(" ", $match[1]);
                                 foreach ($names as $n => $name) {
                                     $sql_where .= " AND {$attr}.n_full LIKE CONCAT('%', :{$attr}name{$n}, '%')";
                                     $sql_params[$attr . 'name' . $n] = $name;
                                 }
                             }
                             // Let the DB do the name sorting even when no name was entered
                             if ($sortby == "NAME") {
                                 $sortby = "";
                                 $sql_order_by .= ($sql_order_by ? ", " : " 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=f_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=f_id)";
                         $sql_where .= " AND {$attr}a.p_place LIKE CONCAT('%', :{$attr}place, '%')";
                         $sql_params[$attr . 'place'] = $match[1];
                         // Don't unset this filter. This is just initial filtering
                     } elseif (preg_match('/^(\\w*):*(\\w*) CONTAINS (.+)$/', $value, $match)) {
                         $sql_where .= " AND f_gedcom LIKE CONCAT('%', :{$attr}contains1, '%', :{$attr}contains2, '%', :{$attr}contains3, '%')";
                         $sql_params[$attr . 'contains1'] = $match[1];
                         $sql_params[$attr . 'contains2'] = $match[2];
                         $sql_params[$attr . 'contains3'] = $match[3];
                         // Don't unset this filter. This is just initial filtering
                     }
                 }
             }
             $this->list = array();
             $rows = Database::prepare($sql_select . $sql_join . $sql_where . $sql_order_by)->execute($sql_params)->fetchAll();
             foreach ($rows as $row) {
                 $this->list[$row->xref] = Family::getInstance($row->xref, $WT_TREE, $row->gedcom);
             }
             break;
         default:
             throw new \DomainException('Invalid list name: ' . $listname);
     }
     $filters = array();
     $filters2 = array();
     if (isset($attrs['filter1']) && count($this->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 @(.+)@/", $this->gedrec, $match)) {
                             $value = "'" . $match[1] . "'";
                         }
                     } elseif ($id == "fact") {
                         $value = "'" . $this->fact . "'";
                     } elseif ($id == "desc") {
                         $value = "'" . $this->desc . "'";
                     } else {
                         if (preg_match("/\\d {$id} (.+)/", $this->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 = $this->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 ($this->list as $key => $record) {
             foreach ($filters as $filter) {
                 if (!preg_match("/" . $filter . "/i", $record->privatizeGedcom(Auth::accessLevel($WT_TREE)))) {
                     unset($this->list[$key]);
                     break;
                 }
             }
         }
     }
     if ($filters2) {
         $mylist = array();
         foreach ($this->list as $indi) {
             $key = $indi->getXref();
             $grec = $indi->privatizeGedcom(Auth::accessLevel($WT_TREE));
             $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 = $this->getGedcomValue($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 = Functions::getSubRecord(1, $tag, $grec);
                     }
                     switch ($expr) {
                         case "GTE":
                             if ($t == "DATE") {
                                 $date1 = new Date($v);
                                 $date2 = new Date($val);
                                 $keep = Date::compare($date1, $date2) >= 0;
                             } elseif ($val >= $v) {
                                 $keep = true;
                             }
                             break;
                         case "LTE":
                             if ($t == "DATE") {
                                 $date1 = new Date($v);
                                 $date2 = new Date($val);
                                 $keep = 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;
             }
         }
         $this->list = $mylist;
     }
     switch ($sortby) {
         case 'NAME':
             uasort($this->list, '\\Fisharebest\\Webtrees\\GedcomRecord::compare');
             break;
         case 'CHAN':
             uasort($this->list, function (GedcomRecord $x, GedcomRecord $y) {
                 return $y->lastChangeTimestamp(true) - $x->lastChangeTimestamp(true);
             });
             break;
         case 'BIRT:DATE':
             uasort($this->list, '\\Fisharebest\\Webtrees\\Individual::compareBirthDate');
             break;
         case 'DEAT:DATE':
             uasort($this->list, '\\Fisharebest\\Webtrees\\Individual::compareDeathDate');
             break;
         case 'MARR:DATE':
             uasort($this->list, '\\Fisharebest\\Webtrees\\Family::compareMarrDate');
             break;
         default:
             // unsorted or already sorted by SQL
             break;
     }
     array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
     $this->repeat_bytes = xml_get_current_line_number($this->parser) + 1;
 }
 /**
  * Find the current spouse family of an individual
  *
  * @param Individual $individual
  *
  * @return Family|null
  */
 public function spouseFamily(Individual $individual)
 {
     // Exclude families that were created after this census date
     $families = array();
     foreach ($individual->getSpouseFamilies() as $family) {
         if (Date::compare($family->getMarriageDate(), $this->date()) <= 0) {
             $families[] = $family;
         }
     }
     if (empty($families)) {
         return null;
     } else {
         usort($families, function (Family $x, Family $y) {
             return Date::compare($x->getMarriageDate(), $y->getMarriageDate());
         });
         return end($families);
     }
 }
 /**
  * Is the individual dead.
  *
  * @param Individual $individual
  *
  * @return bool
  */
 private function isDead(Individual $individual)
 {
     return $individual->getDeathDate()->isOK() && Date::compare($individual->getDeathDate(), $this->date()) < 0;
 }
    /**
     * 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">&nbsp;</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') . '&gt;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') . '&lt;=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 .= '&nbsp;';
            }
            $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 .= '&nbsp;';
            }
            $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;
    }
Exemple #15
0
 /**
  * Print fact DATE/TIME
  *
  * @param Fact $event event containing the date/age
  * @param GedcomRecord $record the person (or couple) whose ages should be printed
  * @param bool $anchor option to print a link to calendar
  * @param bool $time option to print TIME value
  *
  * @return string
  */
 public static function formatFactDate(Fact $event, GedcomRecord $record, $anchor, $time)
 {
     global $pid;
     $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('/\\n2 DATE (.+)/', $factrec, $match)) {
         $date = new Date($match[1]);
         $html .= ' ' . $date->display($anchor);
         // time
         if ($time && preg_match('/\\n3 TIME (.+)/', $factrec, $match)) {
             $html .= ' – <span class="date">' . $match[1] . '</span>';
         }
         $fact = $event->getTag();
         if ($record instanceof Individual) {
             if ($fact === 'BIRT' && $record->getTree()->getPreference('SHOW_PARENTS_AGE')) {
                 // age of parents at child birth
                 $html .= self::formatParentsAges($record, $date);
             } elseif ($fact !== 'CHAN' && $fact !== '_TODO') {
                 // age at event
                 $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 Date('');
                 }
                 $ageText = '';
                 if (Date::compare($date, $death_date) <= 0 || !$record->isDead() || $fact == 'DEAT') {
                     // Before death, print age
                     $age = 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 = '(' . I18N::translate('Age') . ' ' . FunctionsDate::getAgeAtEvent($age, false) . ')';
                             }
                         }
                     }
                 }
                 if ($fact != 'DEAT' && Date::compare($date, $death_date) >= 0) {
                     // After death, print time since death
                     $age = FunctionsDate::getAgeAtEvent(Date::getAgeGedcom($death_date, $date), true);
                     if ($age != '') {
                         if (Date::getAgeGedcom($death_date, $date) == "0d") {
                             $ageText = '(' . I18N::translate('on the date of death') . ')';
                         } else {
                             $ageText = '(' . $age . ' ' . I18N::translate('after death') . ')';
                             // Family events which occur after death are probably errors
                             if ($event->getParent() instanceof Family) {
                                 $ageText .= '<i class="icon-warning"></i>';
                             }
                         }
                     }
                 }
                 if ($ageText) {
                     $html .= ' <span class="age">' . $ageText . '</span>';
                 }
             }
         } elseif ($record instanceof Family) {
             $indi = Individual::getInstance($pid, $record->getTree());
             if ($indi) {
                 $birth_date = $indi->getBirthDate();
                 $death_date = $indi->getDeathDate();
                 $ageText = '';
                 if (Date::compare($date, $death_date) <= 0) {
                     $age = 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 = '(' . I18N::translate('Age') . ' ' . FunctionsDate::getAgeAtEvent($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 .= I18N::translate('yes');
         }
     }
     // print gedcom ages
     foreach (array(GedcomTag::getLabel('AGE') => $fact_age, GedcomTag::getLabel('HUSB') => $husb_age, GedcomTag::getLabel('WIFE') => $wife_age) as $label => $age) {
         if ($age != '') {
             $html .= ' <span class="label">' . $label . ':</span> <span class="age">' . FunctionsDate::getAgeAtEvent($age, false) . '</span>';
         }
     }
     return $html;
 }
Exemple #16
0
 /**
  * Static Helper functions to sort events
  *
  * @param Fact $a Fact one
  * @param Fact $b Fact two
  *
  * @return int
  */
 public static function compareDate(Fact $a, Fact $b)
 {
     if ($a->getDate()->isOK() && $b->getDate()->isOK()) {
         // If both events have dates, compare by date
         $ret = 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;
     }
 }
    /**
     * {@inhericDoc}
     * @see \MyArtJaub\Webtrees\Mvc\View\AbstractView::renderContent()
     */
    protected function renderContent()
    {
        if ($this->data->get('has_sosa', false)) {
            $table_id = $this->data->get('table_id');
            ?>
   
        
		<div id="sosa-fam-list" class="sosa-list">
			<table id="<?php 
            echo $table_id;
            ?>
">
				<thead>
					<tr>
						<th colspan="24">
							<div class="btn-toolbar">
								<div class="btn-group">
									<button
										type="button"
										data-filter-column="22"
										data-filter-value="N"
										class="ui-state-default"
										title="<?php 
            echo I18N::translate('Show individuals who are alive or couples where both partners are alive.');
            ?>
"
									>
									<?php 
            echo I18N::translate('Both alive');
            ?>
									</button>
									<button
										type="button"
										data-filter-column="22"
										data-filter-value="W"
										class="ui-state-default"
										title="<?php 
            echo I18N::translate('Show couples where only the female partner is deceased.');
            ?>
"
									>
									<?php 
            echo I18N::translate('Widower');
            ?>
									</button>
									<button
										type="button"
										data-filter-column="22"
										data-filter-value="H"
										class="ui-state-default"
										title="<?php 
            echo I18N::translate('Show couples where only the male partner is deceased.');
            ?>
"
									>
									<?php 
            echo I18N::translate('Widow');
            ?>
									</button>
									<button
										type="button"
										data-filter-column="22"
										data-filter-value="Y"
										class="ui-state-default"
										title="<?php 
            echo I18N::translate('Show individuals who are dead or couples where both partners are deceased.');
            ?>
"
									>
									<?php 
            echo I18N::translate('Both dead');
            ?>
									</button>
								</div>
								<div class="btn-group">
									<button
										type="button"
										data-filter-column="23"
										data-filter-value="R"
										class="ui-state-default"
										title="<?php 
            echo 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.');
            ?>
"
									>
									<?php 
            echo I18N::translate('Roots');
            ?>
									</button>
									<button
										type="button"
										data-filter-column="23"
										data-filter-value="L"
										class="ui-state-default"
										title="<?php 
            echo I18N::translate('Show “leaves” couples or individuals.  These are individuals who are alive but have no children recorded in the database.');
            ?>
"
									>
									<?php 
            echo I18N::translate('Leaves');
            ?>
									</button>
								</div>
								<div class="btn-group">
									<button
										type="button"
										data-filter-column="21"
										data-filter-value="U"
										class="ui-state-default"
										title="<?php 
            echo I18N::translate('Show couples with an unknown marriage date.');
            ?>
"
									>
									<?php 
            echo GedcomTag::getLabel('MARR');
            ?>
									</button>
									<button
										type="button"
										data-filter-column="21"
										data-filter-value="YES"
										class="ui-state-default"
										title="<?php 
            echo I18N::translate('Show couples who married more than 100 years ago.');
            ?>
"
									>
									<?php 
            echo GedcomTag::getLabel('MARR');
            ?>
&gt;100
									</button>
									<button
										type="button"
										data-filter-column="21"
										data-filter-value="Y100"
										class="ui-state-default"
										title="<?php 
            echo I18N::translate('Show couples who married within the last 100 years.');
            ?>
"
									>
									<?php 
            echo GedcomTag::getLabel('MARR');
            ?>
&lt;=100
									</button>
									<button
										type="button"
										data-filter-column="21"
										data-filter-value="D"
										class="ui-state-default"
										title="<?php 
            echo I18N::translate('Show divorced couples.');
            ?>
"
									>
									<?php 
            echo GedcomTag::getLabel('DIV');
            ?>
									</button>
									<button
										type="button"
										data-filter-column="21"
										data-filter-value="M"
										class="ui-state-default"
										title="<?php 
            echo I18N::translate('Show couples where either partner married more than once.');
            ?>
"
									>
									<?php 
            echo I18N::translate('Multiple marriages');
            ?>
									</button>
								</div>
							</div>
						</th>
					</tr>
					<tr>
						<th><?php 
            echo I18N::translate('Sosa');
            ?>
</th>
						<th>SOSA</th>
						<th><?php 
            echo GedcomTag::getLabel('GIVN');
            ?>
</th>
						<th><?php 
            echo GedcomTag::getLabel('SURN');
            ?>
</th>
						<th>HUSB:GIVN_SURN</th>
						<th>HUSB:SURN_GIVN</th>
						<th><?php 
            echo GedcomTag::getLabel('AGE');
            ?>
</th>
						<th>AGE</th>
						<th><?php 
            echo GedcomTag::getLabel('GIVN');
            ?>
</th>
						<th><?php 
            echo GedcomTag::getLabel('SURN');
            ?>
</th>
						<th>WIFE:GIVN_SURN</th>
						<th>WIFE:SURN_GIVN</th>
						<th><?php 
            echo GedcomTag::getLabel('AGE');
            ?>
</th>
						<th>AGE</th>
						<th><?php 
            echo GedcomTag::getLabel('MARR');
            ?>
</th>
						<th>MARR:DATE</th>
						<th><?php 
            echo GedcomTag::getLabel('PLAC');
            ?>
</th>';
						<?php 
            if (ModuleManager::getInstance()->isOperational(Constants::MODULE_MAJ_ISSOURCED_NAME)) {
                ?>
						<th><i class="icon-source" title="<?php 
                echo I18N::translate('Sourced marriage');
                ?>
" border="0"></i></th>
						<th>SORT_MARRSC</th>
						<?php 
            } else {
                ?>
						<th>&nbsp;</th>
						<th></th>
						<?php 
            }
            ?>
						<th><i class="icon-children" title="<?php 
            echo I18N::translate('Children');
            ?>
"></i></th>
						<th>NCHI</th>
						<th>MARR</th>
						<th>DEAT</th>
						<th>TREE</th>
					</tr>
				</thead>
				<tbody>
			
			<?php 
            foreach ($this->data->get('sosa_list') as $sosa => $family) {
                /** @var \Fisharebest\Webtrees\Family $person */
                //PERSO Create decorator for Family
                $dfamily = new Family($family);
                $husb = $family->getHusband();
                if (is_null($husb)) {
                    $husb = new Individual('H', '0 @H@ INDI', null, $family->getTree());
                }
                $dhusb = new \MyArtJaub\Webtrees\Individual($husb);
                $wife = $family->getWife();
                if (is_null($wife)) {
                    $wife = new Individual('W', '0 @W@ INDI', null, $family->getTree());
                }
                $dwife = new \MyArtJaub\Webtrees\Individual($wife);
                $mdate = $family->getMarriageDate();
                if ($family->isPendingAddtion()) {
                    $class = ' class="new"';
                } elseif ($family->isPendingDeletion()) {
                    $class = ' class="old"';
                } else {
                    $class = '';
                }
                ?>
			
        		<tr <?php 
                echo $class;
                ?>
>
        			<td class="transparent"><?php 
                echo I18N::translate('%1$d/%2$d', $sosa, ($sosa + 1) % 10);
                ?>
</td>
        			<td class="transparent"><?php 
                echo $sosa;
                ?>
</td>
        			<!--  HUSBAND -->
        			<td colspan="2">
        			<?php 
                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 = '';
                    }
                    ?>
        				<a <?php 
                    echo $title . ' ' . $class;
                    ?>
 href="<?php 
                    echo $husb->getHtmlUrl();
                    ?>
">
        					<?php 
                    echo \Fisharebest\Webtrees\Functions\FunctionsPrint::highlightSearchHits($name['full']);
                    ?>
        				</a>
        				<?php 
                    echo $sex_image . FunctionsPrint::formatSosaNumbers($dhusb->getSosaNumbers(), 1, 'smaller');
                    ?>
        				<br/>
            		<?php 
                }
                echo $husb->getPrimaryParentsNames('parents details1', 'none');
                ?>
            		</td>
            		<!-- Dummy column to match colspan in header -->
            		<td style="display:none;"></td>
            		<td>
            			<?php 
                echo Filter::escapeHtml(str_replace('@P.N.', 'AAAA', $givn)) . 'AAAA' . Filter::escapeHtml(str_replace('@N.N.', 'AAAA', $surn));
                ?>
            		</td>
            		<td>
            			<?php 
                echo Filter::escapeHtml(str_replace('@N.N.', 'AAAA', $surn)) . 'AAAA' . Filter::escapeHtml(str_replace('@P.N.', 'AAAA', $givn));
                ?>
            		</td>
            		<?php 
                $hdate = $husb->getBirthDate();
                ?>
            		<td><?php 
                Date::getAge($hdate, $mdate, 2);
                ?>
</td>
            		<td><?php 
                Date::getAge($hdate, $mdate, 1);
                ?>
</td>
            		<!--  WIFE -->
        			<td colspan="2">
        			<?php 
                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 = '';
                    }
                    ?>
        				<a <?php 
                    echo $title . ' ' . $class;
                    ?>
 href="<?php 
                    echo $wife->getHtmlUrl();
                    ?>
">
        					<?php 
                    echo \Fisharebest\Webtrees\Functions\FunctionsPrint::highlightSearchHits($name['full']);
                    ?>
        				</a>
        				<?php 
                    echo $sex_image . FunctionsPrint::formatSosaNumbers($dwife->getSosaNumbers(), 1, 'smaller');
                    ?>
        				<br/>
            		<?php 
                }
                echo $wife->getPrimaryParentsNames('parents details1', 'none');
                ?>
            		</td>
            		<!-- Dummy column to match colspan in header -->
            		<td style="display:none;"></td>
            		<td>
            			<?php 
                echo Filter::escapeHtml(str_replace('@P.N.', 'AAAA', $givn)) . 'AAAA' . Filter::escapeHtml(str_replace('@N.N.', 'AAAA', $surn));
                ?>
            		</td>
            		<td>
            			<?php 
                echo Filter::escapeHtml(str_replace('@N.N.', 'AAAA', $surn)) . 'AAAA' . Filter::escapeHtml(str_replace('@P.N.', 'AAAA', $givn));
                ?>
            		</td>
            		<?php 
                $wdate = $wife->getBirthDate();
                ?>
            		<td><?php 
                Date::getAge($wdate, $mdate, 2);
                ?>
</td>
            		<td><?php 
                Date::getAge($wdate, $mdate, 1);
                ?>
</td>
            		<td><?php 
                if ($marriage_dates = $family->getAllMarriageDates()) {
                    foreach ($marriage_dates as $n => $marriage_date) {
                        if ($n) {
                            echo '<br>';
                        }
                        ?>
        					<div><?php 
                        echo $marriage_date->display(true);
                        ?>
</div>
        				<?php 
                    }
                } elseif ($family->getFacts('_NMR')) {
                    echo I18N::translate('no');
                } elseif ($family->getFacts('MARR')) {
                    echo I18N::translate('yes');
                } else {
                    echo '&nbsp;';
                }
                ?>
            		</td>
            		<td><?php 
                echo $marriage_dates ? $marriage_date->julianDay() : 0;
                ?>
</td>
            		<td><?php 
                foreach ($family->getAllMarriagePlaces() as $n => $marriage_place) {
                    $tmp = new Place($marriage_place, $family->getTree());
                    if ($n) {
                        ?>
<br><?php 
                    }
                    ?>
        				<a href="'<?php 
                    echo $tmp->getURL();
                    ?>
" title="<?php 
                    echo strip_tags($tmp->getFullName());
                    ?>
">
        					<?php 
                    echo \Fisharebest\Webtrees\Functions\FunctionsPrint::highlightSearchHits($tmp->getShortName());
                    ?>
        				</a>
        			<?php 
                }
                ?>
        			</td>
        			<?php 
                if (ModuleManager::getInstance()->isOperational(Constants::MODULE_MAJ_ISSOURCED_NAME)) {
                    $isMSourced = $dfamily->isMarriageSourced();
                    ?>
				   	<td><?php 
                    echo FunctionsPrint::formatIsSourcedIcon('E', $isMSourced, 'MARR', 1, 'medium');
                    ?>
</td>
					<td><?php 
                    echo $isMSourced;
                    ?>
</td>
					<?php 
                } else {
                    ?>
					<td>&nbsp;</td>
					<td></td>
					<?php 
                }
                ?>
					<?php 
                $nchi = $family->getNumberOfChildren();
                ?>
					<td><?php 
                echo I18N::number($nchi);
                ?>
</td>
					<td><?php 
                echo $nchi;
                ?>
</td>
					<td><?php 
                if (!$mdate->isOK()) {
                    echo 'U';
                } else {
                    if (Date::compare($mdate, new Date(date('Y') - 100)) > 0) {
                        echo 'Y100';
                    } else {
                        echo 'YES';
                    }
                }
                if ($family->getFacts(WT_EVENTS_DIV)) {
                    echo 'D';
                }
                if (count($husb->getSpouseFamilies()) > 1 || count($wife->getSpouseFamilies()) > 1) {
                    echo 'M';
                }
                ?>
					</td>
					<td><?php 
                if ($husb->isDead() && $wife->isDead()) {
                    echo 'Y';
                }
                if ($husb->isDead() && !$wife->isDead()) {
                    if ($wife->getSex() == 'F') {
                        echo 'H';
                    }
                    if ($wife->getSex() == 'M') {
                        echo 'W';
                    }
                    // male partners
                }
                if (!$husb->isDead() && $wife->isDead()) {
                    if ($husb->getSex() == 'M') {
                        echo 'W';
                    }
                    if ($husb->getSex() == 'F') {
                        echo 'H';
                    }
                    // female partners
                }
                if (!$husb->isDead() && !$wife->isDead()) {
                    echo 'N';
                }
                ?>
        			</td>
        			<td><?php 
                if (!$husb->getChildFamilies() && !$wife->getChildFamilies()) {
                    echo 'R';
                } elseif (!$husb->isDead() && !$wife->isDead() && $family->getNumberOfChildren() < 1) {
                    echo 'L';
                } else {
                    echo '&nbsp;';
                }
                ?>
			         </td>
				</tr>
        	<?php 
            }
            ?>
        	</tbody>
        	<tfoot>
				<tr>
					<th colspan="24">
						<div class="btn-toolbar">
							<div class="btn-group">
								<button type="button" class="ui-state-default btn-toggle-parents">
									<?php 
            echo I18N::translate('Show parents');
            ?>
								</button>
								<button id="btn-toggle-statistics-<?php 
            echo $table_id;
            ?>
" type="button" class="ui-state-default btn-toggle-statistics">
									<?php 
            echo I18N::translate('Show statistics charts');
            ?>
								</button>
							</div>
						</div>
					</th>
				</tr>
			</tfoot>
        	</table>
				<div id="fam_list_table-charts_<?php 
            echo $table_id;
            ?>
" style="display:none">
					<table class="list-charts">
						<tr>
							<td><?php 
            echo $this->data->get('chart_births');
            ?>
</td>
							<td><?php 
            echo $this->data->get('chart_marriages');
            ?>
</td>
						</tr>
						<tr>
							<td colspan="2"><?php 
            echo $this->data->get('chart_ages');
            ?>
</td>
						</tr>
					</table>
				</div>
			</div>
		<?php 
        } else {
            ?>
        <p class="warning"><?php 
            echo I18N::translate('No family has been found for generation %d', $this->data->get('generation'));
            ?>
</p>
        <?php 
        }
    }
    /**
     * 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') . '&gt;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') . '&lt;=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 .= '&nbsp;';
            }
            $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;
    }