/**
  * Print SOUR structure
  *  This function prints the input array of SOUR sub-records built by the
  *  getSourceStructure() function.
  *
  * @param string[] $textSOUR
  *
  * @return string
  */
 public static function printSourceStructure($textSOUR)
 {
     global $WT_TREE;
     $html = '';
     if ($textSOUR['PAGE']) {
         $html .= GedcomTag::getLabelValue('PAGE', Filter::expandUrls($textSOUR['PAGE']));
     }
     if ($textSOUR['EVEN']) {
         $html .= GedcomTag::getLabelValue('EVEN', Filter::escapeHtml($textSOUR['EVEN']));
         if ($textSOUR['ROLE']) {
             $html .= GedcomTag::getLabelValue('ROLE', Filter::escapeHtml($textSOUR['ROLE']));
         }
     }
     if ($textSOUR['DATE'] || count($textSOUR['TEXT'])) {
         if ($textSOUR['DATE']) {
             $date = new Date($textSOUR['DATE']);
             $html .= GedcomTag::getLabelValue('DATA:DATE', $date->display());
         }
         foreach ($textSOUR['TEXT'] as $text) {
             $html .= GedcomTag::getLabelValue('TEXT', Filter::formatText($text, $WT_TREE));
         }
     }
     if ($textSOUR['QUAY'] != '') {
         $html .= GedcomTag::getLabelValue('QUAY', GedcomCodeQuay::getValue($textSOUR['QUAY']));
     }
     return '<div class="indent">' . $html . '</div>';
 }
 /**
  * add a new tag input field
  *
  * called for each fact to be edited on a form.
  * Fact level=0 means a new empty form : data are POSTed by name
  * else data are POSTed using arrays :
  * glevels[] : tag level
  *  islink[] : tag is a link
  *     tag[] : tag name
  *    text[] : tag value
  *
  * @param string $tag fact record to edit (eg 2 DATE xxxxx)
  * @param string $upperlevel optional upper level tag (eg BIRT)
  * @param string $label An optional label to echo instead of the default
  * @param string $extra optional text to display after the input field
  * @param Individual $person For male/female translations
  *
  * @return string
  */
 public static function addSimpleTag($tag, $upperlevel = '', $label = '', $extra = null, Individual $person = null)
 {
     global $tags, $main_fact, $xref, $bdm, $action, $WT_TREE;
     // Keep track of SOUR fields, so we can reference them in subsequent PAGE fields.
     static $source_element_id;
     $subnamefacts = array('NPFX', 'GIVN', 'SPFX', 'SURN', 'NSFX', '_MARNM_SURN');
     preg_match('/^(?:(\\d+) (' . WT_REGEX_TAG . ') ?(.*))/', $tag, $match);
     list(, $level, $fact, $value) = $match;
     $level = (int) $level;
     // element name : used to POST data
     if ($level === 0) {
         if ($upperlevel) {
             $element_name = $upperlevel . '_' . $fact;
         } else {
             $element_name = $fact;
         }
     } else {
         $element_name = 'text[]';
     }
     if ($level === 1) {
         $main_fact = $fact;
     }
     // element id : used by javascript functions
     if ($level === 0) {
         $element_id = $fact;
     } else {
         $element_id = $fact . Uuid::uuid4();
     }
     if ($upperlevel) {
         $element_id = $upperlevel . '_' . $fact . Uuid::uuid4();
     }
     // field value
     $islink = substr($value, 0, 1) === '@' && substr($value, 0, 2) !== '@#';
     if ($islink) {
         $value = trim(substr($tag, strlen($fact) + 3), ' @\\r');
     } else {
         $value = (string) substr($tag, strlen($fact) + 3);
     }
     if ($fact === 'REPO' || $fact === 'SOUR' || $fact === 'OBJE' || $fact === 'FAMC') {
         $islink = true;
     }
     if ($fact === 'SHARED_NOTE_EDIT' || $fact === 'SHARED_NOTE') {
         $islink = true;
         $fact = 'NOTE';
     }
     // label
     echo '<tr id="', $element_id, '_tr"';
     if ($fact === 'MAP' || ($fact === 'LATI' || $fact === 'LONG') && $value === '') {
         echo ' style="display:none;"';
     }
     echo '>';
     if (in_array($fact, $subnamefacts) || $fact === 'LATI' || $fact === 'LONG') {
         echo '<td class="optionbox wrap width25">';
     } else {
         echo '<td class="descriptionbox wrap width25">';
     }
     // tag name
     if ($label) {
         echo $label;
     } elseif ($upperlevel) {
         echo GedcomTag::getLabel($upperlevel . ':' . $fact);
     } else {
         echo GedcomTag::getLabel($fact);
     }
     // If using GEDFact-assistant window
     if ($action === 'addnewnote_assisted') {
         // Do not print on GEDFact Assistant window
     } else {
         // Not all facts have help text.
         switch ($fact) {
             case 'NAME':
                 if ($upperlevel !== 'REPO' && $upperlevel !== 'UNKNOWN') {
                     echo FunctionsPrint::helpLink($fact);
                 }
                 break;
             case 'DATE':
             case 'PLAC':
             case 'RESN':
             case 'ROMN':
             case 'SURN':
             case '_HEB':
                 echo FunctionsPrint::helpLink($fact);
                 break;
         }
     }
     // tag level
     if ($level > 0) {
         if ($fact === 'TEXT' && $level > 1) {
             echo '<input type="hidden" name="glevels[]" value="', $level - 1, '">';
             echo '<input type="hidden" name="islink[]" value="0">';
             echo '<input type="hidden" name="tag[]" value="DATA">';
             // leave data text[] value empty because the following TEXT line will cause the DATA to be added
             echo '<input type="hidden" name="text[]" value="">';
         }
         echo '<input type="hidden" name="glevels[]" value="', $level, '">';
         echo '<input type="hidden" name="islink[]" value="', $islink, '">';
         echo '<input type="hidden" name="tag[]" value="', $fact, '">';
     }
     echo '</td>';
     // value
     echo '<td class="optionbox wrap">';
     // retrieve linked NOTE
     if ($fact === 'NOTE' && $islink) {
         $note1 = Note::getInstance($value, $WT_TREE);
         if ($note1) {
             $noterec = $note1->getGedcom();
             preg_match('/' . $value . '/i', $noterec, $notematch);
             $value = $notematch[0];
         }
     }
     // Show names for spouses in MARR/HUSB/AGE and MARR/WIFE/AGE
     if ($fact === 'HUSB' || $fact === 'WIFE') {
         $family = Family::getInstance($xref, $WT_TREE);
         if ($family) {
             $spouse_link = $family->getFirstFact($fact);
             if ($spouse_link) {
                 $spouse = $spouse_link->getTarget();
                 if ($spouse) {
                     echo $spouse->getFullName();
                 }
             }
         }
     }
     if (in_array($fact, Config::emptyFacts()) && ($value === '' || $value === 'Y' || $value === 'y')) {
         echo '<input type="hidden" id="', $element_id, '" name="', $element_name, '" value="', $value, '">';
         if ($level <= 1) {
             echo '<input type="checkbox" ';
             if ($value) {
                 echo 'checked';
             }
             echo ' onclick="document.getElementById(\'' . $element_id . '\').value = (this.checked) ? \'Y\' : \'\';">';
             echo I18N::translate('yes');
         }
         if ($fact === 'CENS' && $value === 'Y') {
             echo self::censusDateSelector(WT_LOCALE, $xref);
             if (Module::getModuleByName('GEDFact_assistant') && GedcomRecord::getInstance($xref, $WT_TREE) instanceof Individual) {
                 echo '<div></div><a href="#" style="display: none;" id="assistant-link" onclick="return activateCensusAssistant();">' . I18N::translate('Create a new shared note using assistant') . '</a></div>';
             }
         }
     } elseif ($fact === 'TEMP') {
         echo self::selectEditControl($element_name, GedcomCodeTemp::templeNames(), I18N::translate('No temple - living ordinance'), $value);
     } elseif ($fact === 'ADOP') {
         echo self::editFieldAdoption($element_name, $value, '', $person);
     } elseif ($fact === 'PEDI') {
         echo self::editFieldPedigree($element_name, $value, '', $person);
     } elseif ($fact === 'STAT') {
         echo self::selectEditControl($element_name, GedcomCodeStat::statusNames($upperlevel), '', $value);
     } elseif ($fact === 'RELA') {
         echo self::editFieldRelationship($element_name, strtolower($value));
     } elseif ($fact === 'QUAY') {
         echo self::selectEditControl($element_name, GedcomCodeQuay::getValues(), '', $value);
     } elseif ($fact === '_WT_USER') {
         echo self::editFieldUsername($element_name, $value);
     } elseif ($fact === 'RESN') {
         echo self::editFieldRestriction($element_name, $value);
     } elseif ($fact === '_PRIM') {
         echo '<select id="', $element_id, '" name="', $element_name, '" >';
         echo '<option value=""></option>';
         echo '<option value="Y" ';
         if ($value === 'Y') {
             echo ' selected';
         }
         echo '>', I18N::translate('always'), '</option>';
         echo '<option value="N" ';
         if ($value === 'N') {
             echo 'selected';
         }
         echo '>', I18N::translate('never'), '</option>';
         echo '</select>';
         echo '<p class="small text-muted">', I18N::translate('Use this image for charts and on the individual’s page.'), '</p>';
     } elseif ($fact === 'SEX') {
         echo '<select id="', $element_id, '" name="', $element_name, '"><option value="M" ';
         if ($value === 'M') {
             echo 'selected';
         }
         echo '>', I18N::translate('Male'), '</option><option value="F" ';
         if ($value === 'F') {
             echo 'selected';
         }
         echo '>', I18N::translate('Female'), '</option><option value="U" ';
         if ($value === 'U' || empty($value)) {
             echo 'selected';
         }
         echo '>', I18N::translateContext('unknown gender', 'Unknown'), '</option></select>';
     } elseif ($fact === 'TYPE' && $level === 3) {
         //-- Build the selector for the Media 'TYPE' Fact
         echo '<select name="text[]"><option selected value="" ></option>';
         $selectedValue = strtolower($value);
         if (!array_key_exists($selectedValue, GedcomTag::getFileFormTypes())) {
             echo '<option selected value="', Filter::escapeHtml($value), '" >', Filter::escapeHtml($value), '</option>';
         }
         foreach (GedcomTag::getFileFormTypes() as $typeName => $typeValue) {
             echo '<option value="', $typeName, '" ';
             if ($selectedValue === $typeName) {
                 echo 'selected';
             }
             echo '>', $typeValue, '</option>';
         }
         echo '</select>';
     } elseif ($fact === 'NAME' && $upperlevel !== 'REPO' && $upperlevel !== 'UNKNOWN' || $fact === '_MARNM') {
         // Populated in javascript from sub-tags
         echo '<input type="hidden" id="', $element_id, '" name="', $element_name, '" onchange="updateTextName(\'', $element_id, '\');" value="', Filter::escapeHtml($value), '" class="', $fact, '">';
         echo '<span id="', $element_id, '_display" dir="auto">', Filter::escapeHtml($value), '</span>';
         echo ' <a href="#edit_name" onclick="convertHidden(\'', $element_id, '\'); return false;" class="icon-edit_indi" title="' . I18N::translate('Edit name') . '"></a>';
     } else {
         // textarea
         if ($fact === 'TEXT' || $fact === 'ADDR' || $fact === 'NOTE' && !$islink) {
             echo '<textarea id="', $element_id, '" name="', $element_name, '" dir="auto">', Filter::escapeHtml($value), '</textarea><br>';
         } else {
             // text
             // If using GEDFact-assistant window
             if ($action === 'addnewnote_assisted') {
                 echo '<input type="text" id="', $element_id, '" name="', $element_name, '" value="', Filter::escapeHtml($value), '" style="width:4.1em;" dir="ltr"';
             } else {
                 echo '<input type="text" id="', $element_id, '" name="', $element_name, '" value="', Filter::escapeHtml($value), '" dir="ltr"';
             }
             echo ' class="', $fact, '"';
             if (in_array($fact, $subnamefacts)) {
                 echo ' onblur="updatewholename();" onkeyup="updatewholename();"';
             }
             // Extra markup for specific fact types
             switch ($fact) {
                 case 'ALIA':
                 case 'ASSO':
                 case '_ASSO':
                     echo ' data-autocomplete-type="ASSO" data-autocomplete-extra="input.DATE"';
                     break;
                 case 'DATE':
                     echo ' onblur="valid_date(this);" onmouseout="valid_date(this);"';
                     break;
                 case 'GIVN':
                     echo ' autofocus data-autocomplete-type="GIVN"';
                     break;
                 case 'LATI':
                     echo ' onblur="valid_lati_long(this, \'N\', \'S\');" onmouseout="valid_lati_long(this, \'N\', \'S\');"';
                     break;
                 case 'LONG':
                     echo ' onblur="valid_lati_long(this, \'E\', \'W\');" onmouseout="valid_lati_long(this, \'E\', \'W\');"';
                     break;
                 case 'NOTE':
                     // Shared notes. Inline notes are handled elsewhere.
                     echo ' data-autocomplete-type="NOTE"';
                     break;
                 case 'OBJE':
                     echo ' data-autocomplete-type="OBJE"';
                     break;
                 case 'PAGE':
                     echo ' data-autocomplete-type="PAGE" data-autocomplete-extra="#' . $source_element_id . '"';
                     break;
                 case 'PLAC':
                     echo ' data-autocomplete-type="PLAC"';
                     break;
                 case 'REPO':
                     echo ' data-autocomplete-type="REPO"';
                     break;
                 case 'SOUR':
                     $source_element_id = $element_id;
                     echo ' data-autocomplete-type="SOUR"';
                     break;
                 case 'SURN':
                 case '_MARNM_SURN':
                     echo ' data-autocomplete-type="SURN"';
                     break;
                 case 'TIME':
                     echo ' pattern="([0-1][0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?" dir="ltr" placeholder="' . I18N::translate('hh:mm or hh:mm:ss') . '"';
                     break;
             }
             echo '>';
         }
         $tmp_array = array('TYPE', 'TIME', 'NOTE', 'SOUR', 'REPO', 'OBJE', 'ASSO', '_ASSO', 'AGE');
         // split PLAC
         if ($fact === 'PLAC') {
             echo '<div id="', $element_id, '_pop" style="display: inline;">';
             echo FunctionsPrint::printSpecialCharacterLink($element_id), ' ', FunctionsPrint::printFindPlaceLink($element_id);
             echo '<span  onclick="jQuery(\'tr[id^=', $upperlevel, '_LATI],tr[id^=', $upperlevel, '_LONG],tr[id^=LATI],tr[id^=LONG]\').toggle(\'fast\'); return false;" class="icon-target" title="', GedcomTag::getLabel('LATI'), ' / ', GedcomTag::getLabel('LONG'), '"></span>';
             echo '</div>';
             if (Module::getModuleByName('places_assistant')) {
                 \PlacesAssistantModule::setup_place_subfields($element_id);
                 \PlacesAssistantModule::print_place_subfields($element_id);
             }
         } elseif (!in_array($fact, $tmp_array)) {
             echo FunctionsPrint::printSpecialCharacterLink($element_id);
         }
     }
     // MARRiage TYPE : hide text field and show a selection list
     if ($fact === 'TYPE' && $level === 2 && $tags[0] === 'MARR') {
         echo '<script>';
         echo 'document.getElementById(\'', $element_id, '\').style.display=\'none\'';
         echo '</script>';
         echo '<select id="', $element_id, '_sel" onchange="document.getElementById(\'', $element_id, '\').value=this.value;" >';
         foreach (array('Unknown', 'Civil', 'Religious', 'Partners') as $key) {
             if ($key === 'Unknown') {
                 echo '<option value="" ';
             } else {
                 echo '<option value="', $key, '" ';
             }
             $a = strtolower($key);
             $b = strtolower($value);
             if ($b !== '' && strpos($a, $b) !== false || strpos($b, $a) !== false) {
                 echo 'selected';
             }
             echo '>', GedcomTag::getLabel('MARR_' . strtoupper($key)), '</option>';
         }
         echo '</select>';
     } elseif ($fact === 'TYPE' && $level === 0) {
         // NAME TYPE : hide text field and show a selection list
         $onchange = 'onchange="document.getElementById(\'' . $element_id . '\').value=this.value;"';
         echo self::editFieldNameType($element_name, $value, $onchange, $person);
         echo '<script>document.getElementById("', $element_id, '").style.display="none";</script>';
     }
     // popup links
     switch ($fact) {
         case 'DATE':
             echo self::printCalendarPopup($element_id);
             break;
         case 'FAMC':
         case 'FAMS':
             echo FunctionsPrint::printFindFamilyLink($element_id);
             break;
         case 'ALIA':
         case 'ASSO':
         case '_ASSO':
             echo FunctionsPrint::printFindIndividualLink($element_id, $element_id . '_description');
             break;
         case 'FILE':
             FunctionsPrint::printFindMediaLink($element_id, '0file');
             break;
         case 'SOUR':
             echo FunctionsPrint::printFindSourceLink($element_id, $element_id . '_description'), ' ', self::printAddNewSourceLink($element_id);
             //-- checkboxes to apply '1 SOUR' to BIRT/MARR/DEAT as '2 SOUR'
             if ($level === 1) {
                 echo '<br>';
                 switch ($WT_TREE->getPreference('PREFER_LEVEL2_SOURCES')) {
                     case '2':
                         // records
                         $level1_checked = 'checked';
                         $level2_checked = '';
                         break;
                     case '1':
                         // facts
                         $level1_checked = '';
                         $level2_checked = 'checked';
                         break;
                     case '0':
                         // none
                     // none
                     default:
                         $level1_checked = '';
                         $level2_checked = '';
                         break;
                 }
                 if (strpos($bdm, 'B') !== false) {
                     echo ' <label><input type="checkbox" name="SOUR_INDI" ', $level1_checked, ' value="1">', I18N::translate('Individual'), '</label>';
                     if (preg_match_all('/(' . WT_REGEX_TAG . ')/', $WT_TREE->getPreference('QUICK_REQUIRED_FACTS'), $matches)) {
                         foreach ($matches[1] as $match) {
                             if (!in_array($match, explode('|', WT_EVENTS_DEAT))) {
                                 echo ' <label><input type="checkbox" name="SOUR_', $match, '" ', $level2_checked, ' value="1">', GedcomTag::getLabel($match), '</label>';
                             }
                         }
                     }
                 }
                 if (strpos($bdm, 'D') !== false) {
                     if (preg_match_all('/(' . WT_REGEX_TAG . ')/', $WT_TREE->getPreference('QUICK_REQUIRED_FACTS'), $matches)) {
                         foreach ($matches[1] as $match) {
                             if (in_array($match, explode('|', WT_EVENTS_DEAT))) {
                                 echo ' <label><input type="checkbox" name="SOUR_', $match, '"', $level2_checked, ' value="1">', GedcomTag::getLabel($match), '</label>';
                             }
                         }
                     }
                 }
                 if (strpos($bdm, 'M') !== false) {
                     echo ' <label><input type="checkbox" name="SOUR_FAM" ', $level1_checked, ' value="1">', I18N::translate('Family'), '</label>';
                     if (preg_match_all('/(' . WT_REGEX_TAG . ')/', $WT_TREE->getPreference('QUICK_REQUIRED_FAMFACTS'), $matches)) {
                         foreach ($matches[1] as $match) {
                             echo ' <label><input type="checkbox" name="SOUR_', $match, '"', $level2_checked, ' value="1">', GedcomTag::getLabel($match), '</label>';
                         }
                     }
                 }
             }
             break;
         case 'REPO':
             echo FunctionsPrint::printFindRepositoryLink($element_id), ' ', self::printAddNewRepositoryLink($element_id);
             break;
         case 'NOTE':
             // Shared Notes Icons ========================================
             if ($islink) {
                 // Print regular Shared Note icons ---------------------------
                 echo ' ', FunctionsPrint::printFindNoteLink($element_id, $element_id . '_description'), ' ', self::printAddNewNoteLink($element_id);
                 if ($value) {
                     echo ' ', self::printEditNoteLink($value);
                 }
             }
             break;
         case 'OBJE':
             echo FunctionsPrint::printFindMediaLink($element_id, '1media');
             if (!$value) {
                 echo ' ', self::printAddNewMediaLink($element_id);
                 $value = 'new';
             }
             break;
     }
     echo '<div id="' . $element_id . '_description">';
     // current value
     if ($fact === 'DATE') {
         $date = new Date($value);
         echo $date->display();
     }
     if (($fact === 'ASSO' || $fact === '_ASSO') && $value === '') {
         if ($level === 1) {
             echo '<p class="small text-muted">' . I18N::translate('An associate is another individual who was involved with this individual, such as a friend or an employer.') . '</p>';
         } else {
             echo '<p class="small text-muted">' . I18N::translate('An associate is another individual who was involved with this fact or event, such as a witness or a priest.') . '</p>';
         }
     }
     if ($value && $value !== 'new' && $islink) {
         switch ($fact) {
             case 'ALIA':
             case 'ASSO':
             case '_ASSO':
                 $tmp = Individual::getInstance($value, $WT_TREE);
                 if ($tmp) {
                     echo ' ', $tmp->getFullName();
                 }
                 break;
             case 'SOUR':
                 $tmp = Source::getInstance($value, $WT_TREE);
                 if ($tmp) {
                     echo ' ', $tmp->getFullName();
                 }
                 break;
             case 'NOTE':
                 $tmp = Note::getInstance($value, $WT_TREE);
                 if ($tmp) {
                     echo ' ', $tmp->getFullName();
                 }
                 break;
             case 'OBJE':
                 $tmp = Media::getInstance($value, $WT_TREE);
                 if ($tmp) {
                     echo ' ', $tmp->getFullName();
                 }
                 break;
             case 'REPO':
                 $tmp = Repository::getInstance($value, $WT_TREE);
                 if ($tmp) {
                     echo ' ', $tmp->getFullName();
                 }
                 break;
         }
     }
     // pastable values
     if ($fact === 'FORM' && $upperlevel === 'OBJE') {
         FunctionsPrint::printAutoPasteLink($element_id, Config::fileFormats());
     }
     echo '</div>', $extra, '</td></tr>';
     return $element_id;
 }