1<?php use Fisharebest\Webtrees\Auth; ?> 2<?php use Fisharebest\Webtrees\Config; ?> 3<?php use Fisharebest\Webtrees\Fact; ?> 4<?php use Fisharebest\Webtrees\FontAwesome; ?> 5<?php use Fisharebest\Webtrees\Functions\FunctionsEdit; ?> 6<?php use Fisharebest\Webtrees\Gedcom; ?> 7<?php use Fisharebest\Webtrees\GedcomTag; ?> 8<?php use Fisharebest\Webtrees\I18N; ?> 9<?php use Fisharebest\Webtrees\SurnameTradition; ?> 10<?php use Fisharebest\Webtrees\View; ?> 11 12<?php 13if ($individual !== null) { 14 $xref = $individual->xref(); 15 $cancel_url = $individual->url(); 16} elseif ($family !== null) { 17 $xref = $family->xref(); 18 $cancel_url = $family->url(); 19} else { 20 $cancel_url = route('admin-trees'); 21 $xref = 'new'; 22} 23 24// Different cultures do surnames differently 25$surname_tradition = SurnameTradition::create($tree->getPreference('SURNAME_TRADITION')); 26 27if ($name_fact !== null) { 28 // Editing an existing name 29 $name_fact_id = $name_fact->id(); 30 $namerec = $name_fact->gedcom(); 31 $name_fields = [ 32 'NAME' => $name_fact->value(), 33 'TYPE' => $name_fact->attribute('TYPE'), 34 'NPFX' => $name_fact->attribute('NPFX'), 35 'GIVN' => $name_fact->attribute('GIVN'), 36 'NICK' => $name_fact->attribute('NICK'), 37 'SPFX' => $name_fact->attribute('SPFX'), 38 'SURN' => $name_fact->attribute('SURN'), 39 'NSFX' => $name_fact->attribute('NSFX'), 40 ]; 41 42 // Populate any missing subfields from the NAME field 43 $npfx_accept = implode('|', Config::namePrefixes()); 44 if (preg_match('/(((' . $npfx_accept . ')\.? +)*)([^\n\/"]*)("(.*)")? *\/(([a-z]{2,3} +)*)(.*)\/ *(.*)/i', $name_fields['NAME'], $name_bits)) { 45 $name_fields['NPFX'] = $name_fields['NPFX'] ?: $name_bits[1]; 46 $name_fields['GIVN'] = $name_fields['GIVN'] ?: $name_bits[4]; 47 $name_fields['NICK'] = $name_fields['NICK'] ?: $name_bits[6]; 48 $name_fields['SPFX'] = $name_fields['SPFX'] ?: trim($name_bits[7]); 49 $name_fields['SURN'] = $name_fields['SURN'] ?: preg_replace('~/[^/]*/~', ',', $name_bits[9]); 50 $name_fields['NSFX'] = $name_fields['NSFX'] ?: $name_bits[10]; 51 } 52} else { 53 // Creating a new name 54 $name_fact_id = ''; 55 $namerec = ''; 56 $name_fields = [ 57 'NAME' => '', 58 'TYPE' => '', 59 'NPFX' => '', 60 'GIVN' => '', 61 'NICK' => '', 62 'SPFX' => '', 63 'SURN' => '', 64 'NSFX' => '', 65 ]; 66 67 // Inherit surname from parents, spouse or child 68 if ($family) { 69 $father = $family->getHusband(); 70 if ($father && $father->getFirstFact('NAME')) { 71 $father_name = $father->getFirstFact('NAME')->value(); 72 } else { 73 $father_name = ''; 74 } 75 $mother = $family->getWife(); 76 if ($mother && $mother->getFirstFact('NAME')) { 77 $mother_name = $mother->getFirstFact('NAME')->value(); 78 } else { 79 $mother_name = ''; 80 } 81 } else { 82 $father = null; 83 $mother = null; 84 $father_name = ''; 85 $mother_name = ''; 86 } 87 if ($individual && $individual->getFirstFact('NAME')) { 88 $indi_name = $individual->getFirstFact('NAME')->value(); 89 } else { 90 $indi_name = ''; 91 } 92 93 switch ($nextaction) { 94 case 'add_child_to_family_action': 95 $name_fields = array_merge($name_fields, $surname_tradition->newChildNames($father_name, $mother_name, $gender)); 96 break; 97 case 'add_child_to_individual_action': 98 if ($individual->getSex() === 'F') { 99 $name_fields = array_merge($name_fields, $surname_tradition->newChildNames('', $indi_name, $gender)); 100 } else { 101 $name_fields = array_merge($name_fields, $surname_tradition->newChildNames($indi_name, '', $gender)); 102 } 103 break; 104 case 'add_parent_to_individual_action': 105 $name_fields = array_merge($name_fields, $surname_tradition->newParentNames($indi_name, $gender)); 106 break; 107 case 'add_spouse_to_family_action': 108 if ($father) { 109 $name_fields = array_merge($name_fields, $surname_tradition->newSpouseNames($father_name, $gender)); 110 } else { 111 $name_fields = array_merge($name_fields, $surname_tradition->newSpouseNames($mother_name, $gender)); 112 } 113 break; 114 case 'add_spouse_to_individual_action': 115 $name_fields = array_merge($name_fields, $surname_tradition->newSpouseNames($indi_name, $gender)); 116 break; 117 case 'add_unlinked_indi_action': 118 case 'update': 119 if ($surname_tradition->hasSurnames()) { 120 $name_fields['NAME'] = '//'; 121 } 122 break; 123 } 124} 125 126$bdm = ''; // used to copy '1 SOUR' to '2 SOUR' for BIRT DEAT MARR 127 128?> 129<h2 class="wt-page-title"><?= $title ?></h2> 130 131<form method="post" onsubmit="return checkform();"> 132 <input type="hidden" name="ged" value="<?= e($tree->name()) ?>"> 133 <input type="hidden" name="action" value="<?= e($nextaction) ?>"> 134 <input type="hidden" name="fact_id" value="<?= e($name_fact_id) ?>"> 135 <input type="hidden" name="xref" value="<?= e($xref) ?>"> 136 <input type="hidden" name="famtag" value="<?= e($famtag) ?>"> 137 <input type="hidden" name="gender" value="<?= $gender ?>"> 138 <?= csrf_field() ?> 139 140 <?php if ($nextaction === 'add_child_to_family_action' || $nextaction === 'add_child_to_individual_action') : ?> 141 <?= FunctionsEdit::addSimpleTag($tree, '0 PEDI') ?> 142 <?php endif ?> 143 144 <?php 145 // First - standard name fields 146 foreach ($name_fields as $tag => $value) { 147 if (substr_compare($tag, '_', 0, 1) !== 0) { 148 echo FunctionsEdit::addSimpleTag($tree, '0 ' . $tag . ' ' . $value, '', '', $individual); 149 } 150 } 151 152 // Second - advanced name fields 153 if ($surname_tradition->hasMarriedNames() || preg_match('/\n2 _MARNM /', $namerec)) { 154 $adv_name_fields = ['_MARNM' => '']; 155 } else { 156 $adv_name_fields = []; 157 } 158 if (preg_match_all('/(' . Gedcom::REGEX_TAG . ')/', $tree->getPreference('ADVANCED_NAME_FACTS'), $match)) { 159 foreach ($match[1] as $tag) { 160 // Ignore advanced facts that duplicate standard facts 161 if (!in_array($tag, ['TYPE', 'NPFX', 'GIVN', 'NICK', 'SPFX', 'SURN', 'NSFX'])) { 162 $adv_name_fields[$tag] = ''; 163 } 164 } 165 } 166 167 foreach (array_keys($adv_name_fields) as $tag) { 168 // Edit existing tags, grouped together 169 if (preg_match_all('/2 ' . $tag . ' (.+)/', $namerec, $match)) { 170 foreach ($match[1] as $value) { 171 echo FunctionsEdit::addSimpleTag($tree, '2 ' . $tag . ' ' . $value, '', GedcomTag::getLabel('NAME:' . $tag, $individual)); 172 if ($tag === '_MARNM') { 173 preg_match_all('/\/([^\/]*)\//', $value, $matches); 174 echo FunctionsEdit::addSimpleTag($tree, '2 _MARNM_SURN ' . implode(',', $matches[1])); 175 } 176 } 177 } 178 // Allow a new tag to be entered 179 if (!array_key_exists($tag, $name_fields)) { 180 echo FunctionsEdit::addSimpleTag($tree, '0 ' . $tag, '', GedcomTag::getLabel('NAME:' . $tag, $individual)); 181 if ($tag === '_MARNM') { 182 echo FunctionsEdit::addSimpleTag($tree, '0 _MARNM_SURN'); 183 } 184 } 185 } 186 187 // Third - new/existing custom name fields 188 foreach ($name_fields as $tag => $value) { 189 if (substr_compare($tag, '_', 0, 1) === 0) { 190 echo FunctionsEdit::addSimpleTag($tree, '0 ' . $tag . ' ' . $value); 191 if ($tag === '_MARNM') { 192 preg_match_all('/\/([^\/]*)\//', $value, $matches); 193 echo FunctionsEdit::addSimpleTag($tree, '2 _MARNM_SURN ' . implode(',', $matches[1])); 194 } 195 } 196 } 197 198 // Fourth - SOUR, NOTE, _CUSTOM, etc. 199 if ($namerec !== '') { 200 $gedlines = explode("\n", $namerec); // -- find the number of lines in the record 201 $fields = explode(' ', $gedlines[0]); 202 $glevel = $fields[0]; 203 $level = $glevel; 204 $type = $fields[1]; 205 $tags = []; 206 $i = 0; 207 do { 208 if ($type !== 'TYPE' && !array_key_exists($type, $name_fields) && !array_key_exists($type, $adv_name_fields)) { 209 $text = ''; 210 for ($j = 2; $j < count($fields); $j++) { 211 if ($j > 2) { 212 $text .= ' '; 213 } 214 $text .= $fields[$j]; 215 } 216 while (($i + 1 < count($gedlines)) && (preg_match('/' . ($level + 1) . ' CONT ?(.*)/', $gedlines[$i + 1], $cmatch) > 0)) { 217 $text .= "\n" . $cmatch[1]; 218 $i++; 219 } 220 echo FunctionsEdit::addSimpleTag($tree, $level . ' ' . $type . ' ' . $text); 221 } 222 $tags[] = $type; 223 $i++; 224 if (isset($gedlines[$i])) { 225 $fields = explode(' ', $gedlines[$i]); 226 $level = $fields[0]; 227 if (isset($fields[1])) { 228 $type = $fields[1]; 229 } 230 } 231 } while (($level > $glevel) && ($i < count($gedlines))); 232 } 233 234 // If we are adding a new individual, add the basic details 235 if ($nextaction !== 'update') { 236 echo '</table><br><table class="table wt-facts-table">'; 237 // 1 SEX 238 if ($famtag === 'HUSB' || $gender === 'M') { 239 echo FunctionsEdit::addSimpleTag($tree, '0 SEX M'); 240 } elseif ($famtag === 'WIFE' || $gender === 'F') { 241 echo FunctionsEdit::addSimpleTag($tree, '0 SEX F'); 242 } else { 243 echo FunctionsEdit::addSimpleTag($tree, '0 SEX U'); 244 } 245 $bdm = 'BD'; 246 if (preg_match_all('/(' . Gedcom::REGEX_TAG . ')/', $tree->getPreference('QUICK_REQUIRED_FACTS'), $matches)) { 247 foreach ($matches[1] as $match) { 248 if (!in_array($match, Gedcom::DEATH_EVENTS)) { 249 FunctionsEdit::addSimpleTags($tree, $match); 250 } 251 } 252 } 253 //-- if adding a spouse add the option to add a marriage fact to the new family 254 if ($nextaction === 'add_spouse_to_individual_action' || $nextaction === 'add_spouse_to_family_action') { 255 $bdm .= 'M'; 256 if (preg_match_all('/(' . Gedcom::REGEX_TAG . ')/', $tree->getPreference('QUICK_REQUIRED_FAMFACTS'), $matches)) { 257 foreach ($matches[1] as $match) { 258 FunctionsEdit::addSimpleTags($tree, $match); 259 } 260 } 261 } 262 if (preg_match_all('/(' . Gedcom::REGEX_TAG . ')/', $tree->getPreference('QUICK_REQUIRED_FACTS'), $matches)) { 263 foreach ($matches[1] as $match) { 264 if (in_array($match, Gedcom::DEATH_EVENTS)) { 265 FunctionsEdit::addSimpleTags($tree, $match); 266 } 267 } 268 } 269 } 270 271 echo '</table>'; 272 if ($nextaction === 'update') { 273 // GEDCOM 5.5.1 spec says NAME doesn’t get a OBJE 274 echo view('cards/add-source-citation', [ 275 'level' => 2, 276 'full_citations' => $tree->getPreference('FULL_SOURCES'), 277 'tree' => $tree, 278 ]); 279 echo view('cards/add-note', [ 280 'level' => 2, 281 'tree' => $tree, 282 ]); 283 echo view('cards/add-shared-note', [ 284 'level' => 2, 285 'tree' => $tree, 286 ]); 287 echo view('cards/add-restriction', [ 288 'level' => 2, 289 'tree' => $tree, 290 ]); 291 } else { 292 echo view('cards/add-source-citation', [ 293 'bdm' => $bdm, 294 'level' => 1, 295 'full_citations' => $tree->getPreference('FULL_SOURCES'), 296 'prefer_level2_sources' => $tree->getPreference('PREFER_LEVEL2_SOURCES'), 297 'quick_required_facts' => $tree->getPreference('QUICK_REQUIRED_FACTS'), 298 'quick_required_famfacts' => $tree->getPreference('QUICK_REQUIRED_FAMFACTS'), 299 'tree' => $tree, 300 ]); 301 echo view('cards/add-note', [ 302 'level' => 1, 303 'tree' => $tree, 304 ]); 305 echo view('cards/add-shared-note', [ 306 'level' => 1, 307 'tree' => $tree, 308 ]); 309 echo view('cards/add-restriction', [ 310 'level' => 1, 311 'tree' => $tree, 312 ]); 313 } 314 315 ?> 316 <div class="row form-group"> 317 <div class="col-sm-9 offset-sm-3"> 318 <button class="btn btn-primary" type="submit"> 319 <?= FontAwesome::decorativeIcon('save') ?> 320 <?= /* I18N: A button label. */ 321 I18N::translate('save') ?> 322 </button> 323 <?php if (preg_match('/^add_(child|spouse|parent|unlinked_indi)/', $nextaction)) : ?> 324 <button class="btn btn-primary" type="submit" name="goto" value="<?= $xref ?>"> 325 <?= FontAwesome::decorativeIcon('save') ?> 326 <?= /* I18N: A button label. */ 327 I18N::translate('go to new individual') ?> 328 </button> 329 <?php endif ?> 330 <a class="btn btn-secondary" href="<?= e($cancel_url) ?>"> 331 <?= FontAwesome::decorativeIcon('cancel') ?> 332 <?= /* I18N: A button label. */ 333 I18N::translate('cancel') ?> 334 </a> 335 336 <?php if ($name_fact instanceof Fact && (Auth::isAdmin() || $tree->getPreference('SHOW_GEDCOM_RECORD'))) : ?> 337 <a class="btn btn-link" href="<?= e(route('edit-raw-fact', ['xref' => $xref, 'fact_id' => $name_fact->id(), 'ged' => $tree->name()])) ?>"> 338 <?= I18N::translate('Edit the raw GEDCOM') ?> 339 </a> 340 <?php endif ?> 341 </div> 342 </div> 343</form> 344 345<?= view('modals/on-screen-keyboard') ?> 346<?= view('modals/ajax') ?> 347<?= view('edit/initialize-calendar-popup') ?> 348 349<?php View::push('javascript') ?> 350<script> 351 var SURNAME_TRADITION = <?= json_encode($tree->getPreference('SURNAME_TRADITION')) ?>; 352 var gender = <?= json_encode($gender) ?>; 353 var famtag = <?= json_encode($famtag) ?>; 354 355 var NAME = $("[name=NAME]"); 356 357 function trim(str) { 358 str = str.replace(/\s\s+/g, " "); 359 return str.replace(/(^\s+)|(\s+$)/g, ""); 360 } 361 362 function lang_class(str) { 363 if (str.match(/[\u0370-\u03FF]/)) return "greek"; 364 if (str.match(/[\u0400-\u04FF]/)) return "cyrillic"; 365 if (str.match(/[\u0590-\u05FF]/)) return "hebrew"; 366 if (str.match(/[\u0600-\u06FF]/)) return "arabic"; 367 return "latin"; // No matched text implies latin :-) 368 } 369 370 // Generate a full name from the name components 371 function generate_name() { 372 var npfx = $("[name=NPFX]").val(); 373 var givn = $("[name=GIVN]").val(); 374 var spfx = $("[name=SPFX]").val(); 375 var surn = $("[name=SURN]").val(); 376 var nsfx = $("[name=NSFX]").val(); 377 378 if (SURNAME_TRADITION === "polish" && (gender === "F" || famtag === "WIFE")) { 379 surn = surn.replace(/ski$/, "ska"); 380 surn = surn.replace(/cki$/, "cka"); 381 surn = surn.replace(/dzki$/, "dzka"); 382 surn = surn.replace(/żki$/, "żka"); 383 } 384 385 // Commas are used in the GIVN and SURN field to separate lists of surnames. 386 // For example, to differentiate the two Spanish surnames from an English 387 // double-barred name. 388 // Commas *may* be used in other fields, and will form part of the NAME. 389 var locale = document.documentElement.lang; 390 if (locale === "vi" || locale === "hu") { 391 // Default format: /SURN/ GIVN 392 return trim(npfx + " /" + trim(spfx + " " + surn).replace(/ *, */g, " ") + "/ " + givn.replace(/ *, */g, " ") + " " + nsfx); 393 } else if (locale === "zh-Hans" || locale === "zh-Hant") { 394 // Default format: /SURN/GIVN 395 return npfx + "/" + spfx + surn + "/" + givn + nsfx; 396 } else { 397 // Default format: GIVN /SURN/ 398 return trim(npfx + " " + givn.replace(/ *, */g, " ") + " /" + trim(spfx + " " + surn).replace(/ *, */g, " ") + "/ " + nsfx); 399 } 400 } 401 402 // Update the NAME and _MARNM fields from the name components 403 // and also display the value in read-only "gedcom" format. 404 function updatewholename() { 405 // Don’t update the name if the user manually changed it 406 if (manualChange) { 407 return; 408 } 409 410 var npfx = $("[name=NPFX]").val(); 411 var givn = $("[name=GIVN]").val(); 412 var spfx = $("[name=SPFX]").val(); 413 var surn = $("[name=SURN]").val(); 414 var nsfx = $("[name=NSFX]").val(); 415 var name = generate_name(); 416 417 var display_id = NAME.attr('id') + "_display"; 418 419 NAME.val(name); 420 $("#" + display_id).text(name); 421 // Married names inherit some NSFX values, but not these 422 nsfx = nsfx.replace(/^(I|II|III|IV|V|VI|Junior|Jr\.?|Senior|Sr\.?)$/i, ""); 423 // Update _MARNM field from _MARNM_SURN field and display it 424 // Be careful of mixing latin/hebrew/etc. character sets. 425 var ip = document.getElementsByTagName("input"); 426 var marnm_id = ""; 427 var romn = ""; 428 var heb = ""; 429 for (var i = 0; i < ip.length; i++) { 430 var val = trim(ip[i].value); 431 if (ip[i].id.indexOf("_HEB") === 0) 432 heb = val; 433 if (ip[i].id.indexOf("ROMN") === 0) 434 romn = val; 435 if (ip[i].id.indexOf("_MARNM") === 0) { 436 if (ip[i].id.indexOf("_MARNM_SURN") === 0) { 437 var msurn = ""; 438 if (val !== "") { 439 var lc = lang_class(document.getElementById(ip[i].id).value); 440 if (lang_class(name) === lc) 441 msurn = trim(npfx + " " + givn + " /" + val + "/ " + nsfx); 442 else if (lc === "hebrew") 443 msurn = heb.replace(/\/.*\//, "/" + val + "/"); 444 else if (lang_class(romn) === lc) 445 msurn = romn.replace(/\/.*\//, "/" + val + "/"); 446 } 447 document.getElementById(marnm_id).value = msurn; 448 document.getElementById(marnm_id + "_display").innerHTML = msurn; 449 } else { 450 marnm_id = ip[i].id; 451 } 452 } 453 } 454 } 455 456 // Toggle the name editor fields between 457 // <input type="hidden"> <span style="display:inline"> 458 // <input type="text"> <span style="display:none"> 459 460 var oldName = ""; 461 462 // Calls to generate_name() trigger an update - hence need to 463 // set the manual change to true first. We are probably 464 // listening to the wrong events on the input fields... 465 var manualChange = generate_name() !== NAME.val(); 466 467 function convertHidden(eid) { 468 var input1 = $("#" + eid); 469 var input2 = $("#" + eid + "_display"); 470 // Note that IE does not allow us to change the type of an input, so we must create a new one. 471 if (input1.attr("type") === "hidden") { 472 input1.replaceWith(input1.clone().attr("type", "text")); 473 input2.hide(); 474 } else { 475 input1.replaceWith(input1.clone().attr("type", "hidden")); 476 input2.show(); 477 } 478 } 479 480 /** 481 * if the user manually changed the NAME field, then update the textual 482 * HTML representation of it 483 * If the value changed set manualChange to true so that changing 484 * the other fields doesn’t change the NAME line 485 */ 486 function updateTextName(eid) { 487 var element = document.getElementById(eid); 488 if (element) { 489 if (element.value !== oldName) { 490 manualChange = true; 491 } 492 var delement = document.getElementById(eid + "_display"); 493 if (delement) { 494 delement.innerHTML = element.value; 495 } 496 } 497 } 498 499 function checkform() { 500 var ip = document.getElementsByTagName("input"); 501 for (var i = 0; i < ip.length; i++) { 502 // ADD slashes to _HEB and _AKA names 503 if (ip[i].id.indexOf("_AKA") === 0 || ip[i].id.indexOf("_HEB") === 0 || ip[i].id.indexOf("ROMN") === 0) 504 if (ip[i].value.indexOf("/") < 0 && ip[i].value !== "") 505 ip[i].value = ip[i].value.replace(/([^\s]+)\s*$/, "/$1/"); 506 // Blank out temporary _MARNM_SURN 507 if (ip[i].id.indexOf("_MARNM_SURN") === 0) 508 ip[i].value = ""; 509 // Convert "xxx yyy" and "xxx y yyy" surnames to "xxx,yyy" 510 if ((SURNAME_TRADITION === "spanish" || "SURNAME_TRADITION" === "portuguese") && ip[i].id.indexOf("SURN") === 0) { 511 ip[i].value = document.forms[0].SURN.value.replace(/^\s*([^\s,]{2,})\s+([iIyY] +)?([^\s,]{2,})\s*$/, "$1,$3"); 512 } 513 } 514 return true; 515 } 516 517 // If the name isn’t initially formed from the components in a standard way, 518 // then don’t automatically update it. 519 if (NAME.val() !== generate_name() && NAME.val() !== "//") { 520 convertHidden(NAME.attr("id")); 521 } 522</script> 523<?php View::endpush() ?> 524 525