1<?php use Fisharebest\Webtrees\Date; ?> 2<?php use Fisharebest\Webtrees\Family; ?> 3<?php use Fisharebest\Webtrees\Functions\FunctionsDate; ?> 4<?php use Fisharebest\Webtrees\Functions\FunctionsPrint; ?> 5<?php use Fisharebest\Webtrees\I18N; ?> 6<?php use Fisharebest\Webtrees\Individual; ?> 7 8<script> 9 var bottomy = <?= ($topyear - $baseyear) * $scale - 5 ?>; 10 var topy = 0; 11 var baseyear = <?= $baseyear - (25 / $scale) ?>; 12 var birthyears = []; 13 var birthmonths = []; 14 var birthdays = []; 15 <?php 16 foreach ($individuals as $c => $indi) { 17 if (!empty($birthyears[$indi->xref()])) { 18 echo 'birthyears[' . $c . ']=' . $birthyears[$indi->xref()] . ';'; 19 } 20 if (!empty($birthmonths[$indi->xref()])) { 21 echo 'birthmonths[' . $c . ']=' . $birthmonths[$indi->xref()] . ';'; 22 } 23 if (!empty($birthdays[$indi->xref()])) { 24 echo 'birthdays[' . $c . ']=' . $birthdays[$indi->xref()] . ';'; 25 } 26 } 27 ?> 28 29 var bheight = <?= $bheight ?>; 30 var scale = <?= $scale ?>; 31 32 timeline_chart_div = document.getElementById("timeline_chart"); 33 timeline_chart_div.style.height = '<?= 0 + ($topyear - $baseyear) * $scale * 1.1 ?>px'; 34 35 /** 36 * Find the position of an event, relative to an element. 37 * 38 * @param event 39 * @param element 40 */ 41 function clickPosition(event, element) { 42 var xpos = event.pageX; 43 var ypos = event.pageY; 44 45 if (element.offsetParent) { 46 do { 47 xpos -= element.offsetLeft; 48 ypos -= element.offsetTop; 49 } while (element = element.offsetParent); 50 } 51 52 return {x: xpos, y: ypos} 53 } 54 55 var ob = null; 56 var Y = 0; 57 var X = 0; 58 var oldx = 0; 59 var oldlinew = 0; 60 var personnum = 0; 61 var type = 0; 62 var boxmean = 0; 63 64 function ageCursorMouseDown(divbox, num) { 65 ob = divbox; 66 personnum = num; 67 type = 0; 68 X = ob.offsetLeft; 69 Y = ob.offsetTop; 70 } 71 72 function factMouseDown(divbox, num, mean) { 73 ob = divbox; 74 personnum = num; 75 boxmean = mean; 76 type = 1; 77 oldx = ob.offsetLeft; 78 oldlinew = 0; 79 } 80 81 document.onmousemove = function (e) { 82 var textDirection = document.documentElement.dir; 83 84 if (ob === null) { 85 return true; 86 } 87 var newx = 0; 88 var newy = 0; 89 if (type === 0) { 90 // age boxes 91 newPosition = clickPosition(e, document.getElementById("timeline_chart")); 92 newx = newPosition.x; 93 newy = newPosition.y; 94 95 if (oldx === 0) { 96 oldx = newx; 97 } 98 if (newy < topy - bheight / 2) { 99 newy = topy - bheight / 2; 100 } 101 if (newy > bottomy) { 102 newy = bottomy - 1; 103 } 104 ob.style.top = newy + "px"; 105 var tyear = (newy + bheight - 4 - topy + scale) / scale + baseyear; 106 var year = Math.floor(tyear); 107 var month = Math.floor(tyear * 12 - year * 12); 108 var day = Math.floor(tyear * 365 - year * 365 - month * 30); 109 var mstamp = year * 365 + month * 30 + day; 110 var bdstamp = birthyears[personnum] * 365 + birthmonths[personnum] * 30 + birthdays[personnum]; 111 var daydiff = mstamp - bdstamp; 112 var ba = 1; 113 if (daydiff < 0) { 114 ba = -1; 115 daydiff = (bdstamp - mstamp); 116 } 117 var yage = Math.floor(daydiff / 365); 118 var mage = Math.floor((daydiff - yage * 365) / 30); 119 var dage = Math.floor(daydiff - yage * 365 - mage * 30); 120 if (dage < 0) { 121 mage = mage - 1; 122 } 123 if (dage < -30) { 124 dage = 30 + dage; 125 } 126 if (mage < 0) { 127 yage = yage - 1; 128 } 129 if (mage < -11) { 130 mage = 12 + mage; 131 } 132 var yearform = document.getElementById('yearform' + personnum); 133 var ageform = document.getElementById('ageform' + personnum); 134 yearform.innerHTML = year + " " + month + " <?= mb_substr(I18N::translate('Month:'), 0, 1) ?> " + day + " <?= mb_substr(I18N::translate('Day:'), 0, 1) ?>"; 135 if (ba * yage > 1 || ba * yage < -1 || ba * yage === 0) { 136 ageform.innerHTML = (ba * yage) + " <?= mb_substr(I18N::translate('years'), 0, 1) ?> " + (ba * mage) + " <?= mb_substr(I18N::translate('Month:'), 0, 1) ?> " + (ba * dage) + " <?= mb_substr(I18N::translate('Day:'), 0, 1) ?>"; 137 } else { 138 ageform.innerHTML = (ba * yage) + " <?= mb_substr(I18N::translate('Year:'), 0, 1) ?> " + (ba * mage) + " <?= mb_substr(I18N::translate('Month:'), 0, 1) ?> " + (ba * dage) + " <?= mb_substr(I18N::translate('Day:'), 0, 1) ?>"; 139 } 140 var line = document.getElementById('ageline' + personnum); 141 var temp = newx - oldx; 142 143 if (textDirection === 'rtl') { 144 temp = temp * -1; 145 } 146 line.style.width = (line.width + temp) + "px"; 147 oldx = newx; 148 return false; 149 } else { 150 // fact boxes 151 var linewidth; 152 newPosition = clickPosition(e, document.getElementById("timeline_chart")); 153 newx = newPosition.x; 154 newy = newPosition.y; 155 if (oldx === 0) { 156 oldx = newx; 157 } 158 linewidth = e.pageX; 159 160 // get diagnal line box 161 var dbox = document.getElementById('dbox' + personnum); 162 var etopy; 163 var ebottomy; 164 // set up limits 165 if (boxmean - 175 < topy) { 166 etopy = topy; 167 } else { 168 etopy = boxmean - 175; 169 } 170 if (boxmean + 175 > bottomy) { 171 ebottomy = bottomy; 172 } else { 173 ebottomy = boxmean + 175; 174 } 175 // check if in the bounds of the limits 176 if (newy < etopy) { 177 newy = etopy; 178 } 179 if (newy > ebottomy) { 180 newy = ebottomy; 181 } 182 // calculate the change in Y position 183 var dy = newy - ob.offsetTop; 184 // check if we are above the starting point and switch the background image 185 186 if (newy < boxmean) { 187 if (textDirection === 'rtl') { 188 dbox.style.backgroundImage = "url('<?= asset('css/images/dline2.png') ?>')"; 189 dbox.style.backgroundPosition = "0% 0%"; 190 } else { 191 dbox.style.backgroundImage = "url('<?= asset('css/images/dline.png') ?>')"; 192 dbox.style.backgroundPosition = "0% 100%"; 193 } 194 dy = -dy; 195 dbox.style.top = (newy + bheight / 3) + "px"; 196 } else { 197 if (textDirection === 'rtl') { 198 dbox.style.backgroundImage = "url('<?= asset('css/images/dline.png') ?>')"; 199 dbox.style.backgroundPosition = "0% 100%"; 200 } else { 201 dbox.style.backgroundImage = "url('<?= asset('css/images/dline2.png') ?>')"; 202 dbox.style.backgroundPosition = "0% 0%"; 203 } 204 205 dbox.style.top = (boxmean + bheight / 3) + "px"; 206 } 207 // the new X posistion moves the same as the y position 208 if (textDirection === 'rtl') { 209 newx = dbox.offsetRight + Math.abs(newy - boxmean); 210 } else { 211 newx = dbox.offsetLeft + Math.abs(newy - boxmean); 212 } 213 // set the X position of the box 214 if (textDirection === 'rtl') { 215 ob.style.right = newx + "px"; 216 } else { 217 ob.style.left = newx + "px"; 218 } 219 // set new top positions 220 ob.style.top = newy + "px"; 221 // get the width for the diagnal box 222 var newwidth = (ob.offsetLeft - dbox.offsetLeft); 223 // set the width 224 dbox.style.width = newwidth + "px"; 225 if (textDirection === 'rtl') { 226 dbox.style.right = (dbox.offsetRight - newwidth) + 'px'; 227 } 228 dbox.style.height = newwidth + "px"; 229 // change the line width to the change in the mouse X position 230 line = document.getElementById('boxline' + personnum); 231 if (oldlinew !== 0) { 232 line.width = line.width + (linewidth - oldlinew); 233 } 234 oldlinew = linewidth; 235 oldx = newx; 236 return false; 237 } 238 }; 239 240 document.onmouseup = function () { 241 ob = null; 242 oldx = 0; 243 } 244</script> 245 246<div id="timeline_chart"> 247 <!-- print the timeline line image --> 248 <div id="line" style="position:absolute; <?= I18N::direction() === 'ltr' ? 'left:22px;' : 'right:22px;' ?> top:0;"> 249 <img src="<?= e(asset('css/images/vline.png')) ?>" width="3" height="<?= 0 + ($topyear - $baseyear) * $scale ?>"> 250 </div> 251 252 <!-- print divs for the grid --> 253 <div id="scale<?= e($baseyear) ?>" style="position:absolute; <?= I18N::direction() === 'ltr' ? 'left' : 'right' ?>:0; top:-5px; font-size: 7pt; text-align: <?= I18N::direction() === 'ltr' ? 'left' : 'right' ?>;"> 254 <?= $baseyear ?> 255 </div> 256 <?php 257 // at a scale of 25 or higher, show every year 258 $mod = 25 / $scale; 259 if ($mod < 1) { 260 $mod = 1; 261 } 262 for ($i = $baseyear + 1; $i < $topyear; $i++) { 263 if ($i % $mod === 0) { 264 echo '<div id="scale' . $i . '" style="position:absolute; ' . (I18N::direction() === 'ltr' ? 'left:0;' : 'right:0;') . ' top:' . ((($i - $baseyear) * $scale) - $scale / 2) . 'px; font-size: 7pt; text-align:' . (I18N::direction() === 'ltr' ? 'left' : 'right') . ';">'; 265 echo $i; 266 echo '</div>'; 267 } 268 } 269 echo ''; 270 ?> 271 <div id="scale<?= e($topyear) ?>" style="position:absolute; <?= I18N::direction() === 'ltr' ? 'left' : 'right' ?>:0; top:<?= ($topyear - $baseyear) * $scale ?>px; font-size: 7pt; text-align:<?= I18N::direction() === 'ltr' ? 'left' : 'right' ?>;"> 272 <?= e($topyear) ?> 273 </div> 274 275 <?php foreach ($indifacts as $factcount => $event) : ?> 276 <?php 277 $desc = $event->value(); 278 $gdate = $event->date(); 279 $date = $gdate->minimumDate(); 280 $date = $date->convertToCalendar('gregorian'); 281 $year = $date->year(); 282 $month = max(1, $date->month()); 283 $day = max(1, $date->day()); 284 $xoffset = 0 + 22; 285 $yoffset = 0 + (($year - $baseyear) * $scale) - $scale; 286 $yoffset = $yoffset + (($month / 12) * $scale); 287 $yoffset = $yoffset + (($day / 30) * ($scale / 12)); 288 $yoffset = (int) $yoffset; 289 $place = (int) ($yoffset / $bheight); 290 $i = 1; 291 $j = 0; 292 $tyoffset = 0; 293 while (isset($placements[$place])) { 294 if ($i === $j) { 295 $tyoffset = $bheight * $i; 296 $i++; 297 } else { 298 $tyoffset = -1 * $bheight * $j; 299 $j++; 300 } 301 $place = (int) (($yoffset + $tyoffset) / $bheight); 302 } 303 $yoffset += $tyoffset; 304 $xoffset += abs($tyoffset); 305 $placements[$place] = $yoffset; 306 307 echo "<div id=\"fact$factcount\" style=\"position:absolute; " . (I18N::direction() === 'ltr' ? 'left: ' . $xoffset : 'right: ' . $xoffset) . 'px; top:' . $yoffset . 'px; font-size: 8pt; height: ' . $bheight . "px;\" onmousedown=\"factMouseDown(this, '" . $factcount . "', " . ($yoffset - $tyoffset) . ');">'; 308 echo '<table cellspacing="0" cellpadding="0" border="0" style="cursor: grab;"><tr><td>'; 309 echo '<img src="' . e(asset('css/images/hline.png')) . '" id="boxline' . $factcount . '" height="3" width="10" style="padding-'; 310 if (I18N::direction() === 'ltr') { 311 echo 'left: 3px;">'; 312 } else { 313 echo 'right: 3px;">'; 314 } 315 316 $col = array_search($event->record(), $individuals, true); 317 if ($col === false) { 318 // Marriage event - use the color of the husband 319 $col = array_search($event->record()->husband(), $individuals, true); 320 } 321 if ($col === false) { 322 // Marriage event - use the color of the wife 323 $col = array_search($event->record()->wife(), $individuals, true); 324 } 325 $col = $col % 6; 326 echo '</td><td class="person' . $col . '">'; 327 if (count($individuals) > 6) { 328 // We only have six colours, so show naes if more than this number 329 echo $event->record()->fullName() . ' — '; 330 } 331 $record = $event->record(); 332 echo $event->label(); 333 echo ' — '; 334 if ($record instanceof Individual) { 335 echo FunctionsPrint::formatFactDate($event, $record, false, false); 336 } elseif ($record instanceof Family) { 337 echo $gdate->display(); 338 if ($record->husband() && $record->husband()->getBirthDate()->isOK()) { 339 $ageh = FunctionsDate::getAgeAtEvent(Date::getAgeGedcom($record->husband()->getBirthDate(), $gdate)); 340 } else { 341 $ageh = null; 342 } 343 if ($record->wife() && $record->wife()->getBirthDate()->isOK()) { 344 $agew = FunctionsDate::getAgeAtEvent(Date::getAgeGedcom($record->wife()->getBirthDate(), $gdate)); 345 } else { 346 $agew = null; 347 } 348 if ($ageh && $agew) { 349 echo '<span class="age"> ', I18N::translate('Husband’s age'), ' ', $ageh, ' ', I18N::translate('Wife’s age'), ' ', $agew, '</span>'; 350 } elseif ($ageh) { 351 echo '<span class="age"> ', I18N::translate('Age'), ' ', $ageh, '</span>'; 352 } elseif ($agew) { 353 echo '<span class="age"> ', I18N::translate('Age'), ' ', $ageh, '</span>'; 354 } 355 } 356 echo ' ' . e($desc); 357 if ($event->place()->gedcomName() !== '') { 358 echo ' — ' . $event->place()->shortName(); 359 } 360 // Print spouses names for family events 361 if ($event->record() instanceof Family) { 362 echo ' — <a href="', e($event->record()->url()), '">', $event->record()->fullName(), '</a>'; 363 } 364 echo '</td></tr></table>'; 365 echo '</div>'; 366 if (I18N::direction() === 'ltr') { 367 $img = asset('css/images/dline2.png'); 368 $ypos = '0%'; 369 } else { 370 $img = asset('css/images/dline.png'); 371 $ypos = '100%'; 372 } 373 $dyoffset = ($yoffset - $tyoffset) + $bheight / 3; 374 if ($tyoffset < 0) { 375 $dyoffset = $yoffset + $bheight / 3; 376 if (I18N::direction() === 'ltr') { 377 $img = asset('css/images/dline.png'); 378 $ypos = '100%'; 379 } else { 380 $img = asset('css/images/dline2.png'); 381 $ypos = '0%'; 382 } 383 } 384 ?> 385 386 <!-- diagonal line --> 387 <div id="dbox<?= $factcount ?>" style="position:absolute; <?= (I18N::direction() === 'ltr' ? 'left: ' . (0 + 25) : 'right: ' . (0 + 25)) ?>px; top:<?= ($dyoffset) ?>px; font-size: 8pt; height: <?= abs($tyoffset) ?>px; width: <?= abs($tyoffset) ?>px; background-image: url('<?= e($img) ?>'); background-position: 0% <?= $ypos ?>;"> 388 </div> 389 <?php endforeach ?> 390 391 <!-- age cursors --> 392 <?php foreach ($individuals as $p => $indi) : ?> 393 <?php $ageyoffset = 0 + ($bheight * $p); ?> 394 <div id="agebox<?= $p ?>" style="cursor:move; position:absolute; <?= I18N::direction() === 'ltr' ? 'left:20px;' : 'right:20px;' ?> top:<?= $ageyoffset ?>px; height:<?= $bheight ?>px; display:none;" onmousedown="ageCursorMouseDown(this, <?= $p ?>);"> 395 <table cellspacing="0" cellpadding="0"> 396 <tr> 397 <td> 398 <img src="<?= e(asset('css/images/hline.png')) ?>" id="ageline<?= $p ?>" width="25" height="3"> 399 </td> 400 <td> 401 <?php if (!empty($birthyears[$indi->xref()])) : ?> 402 <?php $tyear = round(($ageyoffset + ($bheight / 2)) / $scale) + $baseyear; ?> 403 <table class="person<?= $p % 6 ?>" style="cursor: grab;"> 404 <tr> 405 <td> 406 <?= I18N::translate('Year:') ?> 407 <span id="yearform<?= $p ?>" class="field"> 408 <?= $tyear ?> 409 </span> 410 </td> 411 <td> 412 (<?= I18N::translate('Age') ?> <span id="ageform<?= $p ?>" class="field"><?= $tyear - $birthyears[$indi->xref()] ?></span>) 413 </td> 414 </tr> 415 </table> 416 <?php endif ?> 417 </td> 418 </tr> 419 </table> 420 <br> 421 <br> 422 <br> 423 </div> 424 <br> 425 <br> 426 <br> 427 <br> 428 <?php endforeach ?> 429</div> 430