Passed
Push — 1.7 ( 23cbb7...8df8a8 )
by Greg
08:15
created
app/Fact.php 3 patches
Indentation   +561 added lines, -561 removed lines patch added patch discarded remove patch
@@ -21,565 +21,565 @@
 block discarded – undo
21 21
  * A GEDCOM fact or event object.
22 22
  */
23 23
 class Fact {
24
-	/** @var string Unique identifier for this fact (currently implemented as a hash of the raw data). */
25
-	private $fact_id;
26
-
27
-	/** @var GedcomRecord The GEDCOM record from which this fact is taken */
28
-	private $parent;
29
-
30
-	/** @var string The raw GEDCOM data for this fact */
31
-	private $gedcom;
32
-
33
-	/** @var string The GEDCOM tag for this record */
34
-	private $tag;
35
-
36
-	/** @var bool Is this a recently deleted fact, pending approval? */
37
-	private $pending_deletion = false;
38
-
39
-	/** @var bool Is this a recently added fact, pending approval? */
40
-	private $pending_addition = false;
41
-
42
-	/** @var Date The date of this fact, from the “2 DATE …” attribute */
43
-	private $date;
44
-
45
-	/** @var Place The place of this fact, from the “2 PLAC …” attribute */
46
-	private $place;
47
-
48
-	/** @var int Temporary(!) variable Used by Functions::sortFacts() */
49
-	public $sortOrder;
50
-
51
-	/**
52
-	 * Create an event object from a gedcom fragment.
53
-	 * We need the parent object (to check privacy) and a (pseudo) fact ID to
54
-	 * identify the fact within the record.
55
-	 *
56
-	 * @param string          $gedcom
57
-	 * @param GedcomRecord $parent
58
-	 * @param string          $fact_id
59
-	 *
60
-	 * @throws \InvalidArgumentException
61
-	 */
62
-	public function __construct($gedcom, GedcomRecord $parent, $fact_id) {
63
-		if (preg_match('/^1 (' . WT_REGEX_TAG . ')/', $gedcom, $match)) {
64
-			$this->gedcom  = $gedcom;
65
-			$this->parent  = $parent;
66
-			$this->fact_id = $fact_id;
67
-			$this->tag     = $match[1];
68
-		} else {
69
-			throw new \InvalidArgumentException('Invalid GEDCOM data passed to Fact::_construct(' . $gedcom . ')');
70
-		}
71
-	}
72
-
73
-	/**
74
-	 * Get the value of level 1 data in the fact
75
-	 * Allow for multi-line values
76
-	 *
77
-	 * @return string|null
78
-	 */
79
-	public function getValue() {
80
-		if (preg_match('/^1 (?:' . $this->tag . ') ?(.*(?:(?:\n2 CONT ?.*)*))/', $this->gedcom, $match)) {
81
-			return preg_replace("/\n2 CONT ?/", "\n", $match[1]);
82
-		} else {
83
-			return null;
84
-		}
85
-	}
86
-
87
-	/**
88
-	 * Get the record to which this fact links
89
-	 *
90
-	 * @return Individual|Family|Source|Repository|Media|Note|null
91
-	 */
92
-	public function getTarget() {
93
-		$xref = trim($this->getValue(), '@');
94
-		switch ($this->tag) {
95
-		case 'FAMC':
96
-		case 'FAMS':
97
-			return Family::getInstance($xref, $this->getParent()->getTree());
98
-		case 'HUSB':
99
-		case 'WIFE':
100
-		case 'CHIL':
101
-			return Individual::getInstance($xref, $this->getParent()->getTree());
102
-		case 'SOUR':
103
-			return Source::getInstance($xref, $this->getParent()->getTree());
104
-		case 'OBJE':
105
-			return Media::getInstance($xref, $this->getParent()->getTree());
106
-		case 'REPO':
107
-			return Repository::getInstance($xref, $this->getParent()->getTree());
108
-		case 'NOTE':
109
-			return Note::getInstance($xref, $this->getParent()->getTree());
110
-		default:
111
-			return GedcomRecord::getInstance($xref, $this->getParent()->getTree());
112
-		}
113
-	}
114
-
115
-	/**
116
-	 * Get the value of level 2 data in the fact
117
-	 *
118
-	 * @param string $tag
119
-	 *
120
-	 * @return string|null
121
-	 */
122
-	public function getAttribute($tag) {
123
-		if (preg_match('/\n2 (?:' . $tag . ') ?(.*(?:(?:\n3 CONT ?.*)*)*)/', $this->gedcom, $match)) {
124
-			return preg_replace("/\n3 CONT ?/", "\n", $match[1]);
125
-		} else {
126
-			return null;
127
-		}
128
-	}
129
-
130
-	/**
131
-	 * Do the privacy rules allow us to display this fact to the current user
132
-	 *
133
-	 * @param int|null $access_level
134
-	 *
135
-	 * @return bool
136
-	 */
137
-	public function canShow($access_level = null) {
138
-		if ($access_level === null) {
139
-			$access_level = Auth::accessLevel($this->getParent()->getTree());
140
-		}
141
-
142
-		// Does this record have an explicit RESN?
143
-		if (strpos($this->gedcom, "\n2 RESN confidential")) {
144
-			return Auth::PRIV_NONE >= $access_level;
145
-		}
146
-		if (strpos($this->gedcom, "\n2 RESN privacy")) {
147
-			return Auth::PRIV_USER >= $access_level;
148
-		}
149
-		if (strpos($this->gedcom, "\n2 RESN none")) {
150
-			return true;
151
-		}
152
-
153
-		// Does this record have a default RESN?
154
-		$xref                    = $this->parent->getXref();
155
-		$fact_privacy            = $this->parent->getTree()->getFactPrivacy();
156
-		$individual_fact_privacy = $this->parent->getTree()->getIndividualFactPrivacy();
157
-		if (isset($individual_fact_privacy[$xref][$this->tag])) {
158
-			return $individual_fact_privacy[$xref][$this->tag] >= $access_level;
159
-		}
160
-		if (isset($fact_privacy[$this->tag])) {
161
-			return $fact_privacy[$this->tag] >= $access_level;
162
-		}
163
-
164
-		// No restrictions - it must be public
165
-		return true;
166
-	}
167
-
168
-	/**
169
-	 * Check whether this fact is protected against edit
170
-	 *
171
-	 * @return bool
172
-	 */
173
-	public function canEdit() {
174
-		// Managers can edit anything
175
-		// Members cannot edit RESN, CHAN and locked records
176
-		return
177
-			$this->parent->canEdit() && !$this->isPendingDeletion() && (
178
-				Auth::isManager($this->parent->getTree()) ||
179
-				Auth::isEditor($this->parent->getTree()) && strpos($this->gedcom, "\n2 RESN locked") === false && $this->getTag() != 'RESN' && $this->getTag() != 'CHAN'
180
-			);
181
-	}
182
-
183
-	/**
184
-	 * The place where the event occured.
185
-	 *
186
-	 * @return Place
187
-	 */
188
-	public function getPlace() {
189
-		if ($this->place === null) {
190
-			$this->place = new Place($this->getAttribute('PLAC'), $this->getParent()->getTree());
191
-		}
192
-
193
-		return $this->place;
194
-	}
195
-
196
-	/**
197
-	 * Get the date for this fact.
198
-	 * We can call this function many times, especially when sorting,
199
-	 * so keep a copy of the date.
200
-	 *
201
-	 * @return Date
202
-	 */
203
-	public function getDate() {
204
-		if ($this->date === null) {
205
-			$this->date = new Date($this->getAttribute('DATE'));
206
-		}
207
-
208
-		return $this->date;
209
-	}
210
-
211
-	/**
212
-	 * The raw GEDCOM data for this fact
213
-	 *
214
-	 * @return string
215
-	 */
216
-	public function getGedcom() {
217
-		return $this->gedcom;
218
-	}
219
-
220
-	/**
221
-	 * Get a (pseudo) primary key for this fact.
222
-	 *
223
-	 * @return string
224
-	 */
225
-	public function getFactId() {
226
-		return $this->fact_id;
227
-	}
228
-
229
-	// What sort of fact is this?
230
-	/**
231
-	 * What is the tag (type) of this fact, such as BIRT, MARR or DEAT.
232
-	 *
233
-	 * @return string
234
-	 */
235
-	public function getTag() {
236
-		return $this->tag;
237
-	}
238
-
239
-	/**
240
-	 * Used to convert a real fact (e.g. BIRT) into a close-relative’s fact (e.g. _BIRT_CHIL)
241
-	 *
242
-	 * @param string $tag
243
-	 */
244
-	public function setTag($tag) {
245
-		$this->tag = $tag;
246
-	}
247
-
248
-	//
249
-	/**
250
-	 * The Person/Family record where this Fact came from
251
-	 *
252
-	 * @return Individual|Family|Source|Repository|Media|Note|GedcomRecord
253
-	 */
254
-	public function getParent() {
255
-		return $this->parent;
256
-	}
257
-
258
-	/**
259
-	 * Get the name of this fact type, for use as a label.
260
-	 *
261
-	 * @return string
262
-	 */
263
-	public function getLabel() {
264
-		switch ($this->tag) {
265
-		case 'EVEN':
266
-		case 'FACT':
267
-			if ($this->getAttribute('TYPE')) {
268
-				// Custom FACT/EVEN - with a TYPE
269
-				return I18N::translate(Filter::escapeHtml($this->getAttribute('TYPE')));
270
-			}
271
-			// no break - drop into next case
272
-		default:
273
-			return GedcomTag::getLabel($this->tag, $this->parent);
274
-		}
275
-	}
276
-
277
-	/**
278
-	 * This is a newly deleted fact, pending approval.
279
-	 */
280
-	public function setPendingDeletion() {
281
-		$this->pending_deletion = true;
282
-		$this->pending_addition = false;
283
-	}
284
-
285
-	/**
286
-	 * Is this a newly deleted fact, pending approval.
287
-	 *
288
-	 * @return bool
289
-	 */
290
-	public function isPendingDeletion() {
291
-		return $this->pending_deletion;
292
-	}
293
-
294
-	/**
295
-	 * This is a newly added fact, pending approval.
296
-	 */
297
-	public function setPendingAddition() {
298
-		$this->pending_addition = true;
299
-		$this->pending_deletion = false;
300
-	}
301
-
302
-	/**
303
-	 * Is this a newly added fact, pending approval.
304
-	 *
305
-	 * @return bool
306
-	 */
307
-	public function isPendingAddition() {
308
-		return $this->pending_addition;
309
-	}
310
-
311
-	/**
312
-	 * Source citations linked to this fact
313
-	 *
314
-	 * @return string[]
315
-	 */
316
-	public function getCitations() {
317
-		preg_match_all('/\n(2 SOUR @(' . WT_REGEX_XREF . ')@(?:\n[3-9] .*)*)/', $this->getGedcom(), $matches, PREG_SET_ORDER);
318
-		$citations = array();
319
-		foreach ($matches as $match) {
320
-			$source = Source::getInstance($match[2], $this->getParent()->getTree());
321
-			if ($source->canShow()) {
322
-				$citations[] = $match[1];
323
-			}
324
-		}
325
-
326
-		return $citations;
327
-	}
328
-
329
-	/**
330
-	 * Notes (inline and objects) linked to this fact
331
-	 *
332
-	 * @return string[]|Note[]
333
-	 */
334
-	public function getNotes() {
335
-		$notes = array();
336
-		preg_match_all('/\n2 NOTE ?(.*(?:\n3.*)*)/', $this->getGedcom(), $matches);
337
-		foreach ($matches[1] as $match) {
338
-			$note = preg_replace("/\n3 CONT ?/", "\n", $match);
339
-			if (preg_match('/@(' . WT_REGEX_XREF . ')@/', $note, $nmatch)) {
340
-				$note = Note::getInstance($nmatch[1], $this->getParent()->getTree());
341
-				if ($note && $note->canShow()) {
342
-					// A note object
343
-					$notes[] = $note;
344
-				}
345
-			} else {
346
-				// An inline note
347
-				$notes[] = $note;
348
-			}
349
-		}
350
-
351
-		return $notes;
352
-	}
353
-
354
-	/**
355
-	 * Media objects linked to this fact
356
-	 *
357
-	 * @return Media[]
358
-	 */
359
-	public function getMedia() {
360
-		$media = array();
361
-		preg_match_all('/\n2 OBJE @(' . WT_REGEX_XREF . ')@/', $this->getGedcom(), $matches);
362
-		foreach ($matches[1] as $match) {
363
-			$obje = Media::getInstance($match, $this->getParent()->getTree());
364
-			if ($obje->canShow()) {
365
-				$media[] = $obje;
366
-			}
367
-		}
368
-
369
-		return $media;
370
-	}
371
-
372
-	/**
373
-	 * A one-line summary of the fact - for charts, etc.
374
-	 *
375
-	 * @return string
376
-	 */
377
-	public function summary() {
378
-		$attributes = array();
379
-		$target     = $this->getTarget();
380
-		if ($target) {
381
-			$attributes[] = $target->getFullName();
382
-		} else {
383
-			// Fact value
384
-			$value = $this->getValue();
385
-			if ($value !== '' && $value !== 'Y') {
386
-				$attributes[] = '<span dir="auto">' . Filter::escapeHtml($value) . '</span>';
387
-			}
388
-			// Fact date
389
-			$date = $this->getDate();
390
-			if ($date->isOK()) {
391
-				if (in_array($this->getTag(), explode('|', WT_EVENTS_BIRT)) && $this->getParent() instanceof Individual && $this->getParent()->getTree()->getPreference('SHOW_PARENTS_AGE')) {
392
-					$attributes[] = $date->display() . FunctionsPrint::formatParentsAges($this->getParent(), $date);
393
-				} else {
394
-					$attributes[] = $date->display();
395
-				}
396
-			}
397
-			// Fact place
398
-			if (!$this->getPlace()->isEmpty()) {
399
-				$attributes[] = $this->getPlace()->getShortName();
400
-			}
401
-		}
402
-
403
-		$class = 'fact_' . $this->getTag();
404
-		if ($this->isPendingAddition()) {
405
-			$class .= ' new';
406
-		} elseif ($this->isPendingDeletion()) {
407
-			$class .= ' old';
408
-		}
409
-
410
-		return
411
-			'<div class="' . $class . '">' .
412
-			/* I18N: a label/value pair, such as “Occupation: Farmer”. Some languages may need to change the punctuation. */
413
-			I18N::translate('<span class="label">%1$s:</span> <span class="field" dir="auto">%2$s</span>', $this->getLabel(), implode(' — ', $attributes)) .
414
-			'</div>';
415
-	}
416
-
417
-	/**
418
-	 * Static Helper functions to sort events
419
-	 *
420
-	 * @param Fact $a Fact one
421
-	 * @param Fact $b Fact two
422
-	 *
423
-	 * @return int
424
-	 */
425
-	public static function compareDate(Fact $a, Fact $b) {
426
-		if ($a->getDate()->isOK() && $b->getDate()->isOK()) {
427
-			// If both events have dates, compare by date
428
-			$ret = Date::compare($a->getDate(), $b->getDate());
429
-
430
-			if ($ret == 0) {
431
-				// If dates are the same, compare by fact type
432
-				$ret = self::compareType($a, $b);
433
-
434
-				// If the fact type is also the same, retain the initial order
435
-				if ($ret == 0) {
436
-					$ret = $a->sortOrder - $b->sortOrder;
437
-				}
438
-			}
439
-
440
-			return $ret;
441
-		} else {
442
-			// One or both events have no date - retain the initial order
443
-			return $a->sortOrder - $b->sortOrder;
444
-		}
445
-	}
446
-
447
-	/**
448
-	 * Static method to compare two events by their type.
449
-	 *
450
-	 * @param Fact $a Fact one
451
-	 * @param Fact $b Fact two
452
-	 *
453
-	 * @return int
454
-	 */
455
-	public static function compareType(Fact $a, Fact $b) {
456
-		global $factsort;
457
-
458
-		if (empty($factsort)) {
459
-			$factsort = array_flip(
460
-				array(
461
-					'BIRT',
462
-					'_HNM',
463
-					'ALIA', '_AKA', '_AKAN',
464
-					'ADOP', '_ADPF', '_ADPF',
465
-					'_BRTM',
466
-					'CHR', 'BAPM',
467
-					'FCOM',
468
-					'CONF',
469
-					'BARM', 'BASM',
470
-					'EDUC',
471
-					'GRAD',
472
-					'_DEG',
473
-					'EMIG', 'IMMI',
474
-					'NATU',
475
-					'_MILI', '_MILT',
476
-					'ENGA',
477
-					'MARB', 'MARC', 'MARL', '_MARI', '_MBON',
478
-					'MARR', 'MARR_CIVIL', 'MARR_RELIGIOUS', 'MARR_PARTNERS', 'MARR_UNKNOWN', '_COML',
479
-					'_STAT',
480
-					'_SEPR',
481
-					'DIVF',
482
-					'MARS',
483
-					'_BIRT_CHIL',
484
-					'DIV', 'ANUL',
485
-					'_BIRT_', '_MARR_', '_DEAT_', '_BURI_', // other events of close relatives
486
-					'CENS',
487
-					'OCCU',
488
-					'RESI',
489
-					'PROP',
490
-					'CHRA',
491
-					'RETI',
492
-					'FACT', 'EVEN',
493
-					'_NMR', '_NMAR', 'NMR',
494
-					'NCHI',
495
-					'WILL',
496
-					'_HOL',
497
-					'_????_',
498
-					'DEAT',
499
-					'_FNRL', 'CREM', 'BURI', '_INTE',
500
-					'_YART',
501
-					'_NLIV',
502
-					'PROB',
503
-					'TITL',
504
-					'COMM',
505
-					'NATI',
506
-					'CITN',
507
-					'CAST',
508
-					'RELI',
509
-					'SSN', 'IDNO',
510
-					'TEMP',
511
-					'SLGC', 'BAPL', 'CONL', 'ENDL', 'SLGS',
512
-					'ADDR', 'PHON', 'EMAIL', '_EMAIL', 'EMAL', 'FAX', 'WWW', 'URL', '_URL',
513
-					'FILE', // For media objects
514
-					'AFN', 'REFN', '_PRMN', 'REF', 'RIN', '_UID',
515
-					'OBJE', 'NOTE', 'SOUR',
516
-					'CHAN', '_TODO',
517
-				)
518
-			);
519
-		}
520
-
521
-		// Facts from same families stay grouped together
522
-		// Keep MARR and DIV from the same families from mixing with events from other FAMs
523
-		// Use the original order in which the facts were added
524
-		if ($a->parent instanceof Family && $b->parent instanceof Family && $a->parent !== $b->parent) {
525
-			return $a->sortOrder - $b->sortOrder;
526
-		}
527
-
528
-		$atag = $a->getTag();
529
-		$btag = $b->getTag();
530
-
531
-		// Events not in the above list get mapped onto one that is.
532
-		if (!array_key_exists($atag, $factsort)) {
533
-			if (preg_match('/^(_(BIRT|MARR|DEAT|BURI)_)/', $atag, $match)) {
534
-				$atag = $match[1];
535
-			} else {
536
-				$atag = "_????_";
537
-			}
538
-		}
539
-
540
-		if (!array_key_exists($btag, $factsort)) {
541
-			if (preg_match('/^(_(BIRT|MARR|DEAT|BURI)_)/', $btag, $match)) {
542
-				$btag = $match[1];
543
-			} else {
544
-				$btag = "_????_";
545
-			}
546
-		}
547
-
548
-		// - Don't let dated after DEAT/BURI facts sort non-dated facts before DEAT/BURI
549
-		// - Treat dated after BURI facts as BURI instead
550
-		if ($a->getAttribute('DATE') !== null && $factsort[$atag] > $factsort['BURI'] && $factsort[$atag] < $factsort['CHAN']) {
551
-			$atag = 'BURI';
552
-		}
553
-
554
-		if ($b->getAttribute('DATE') !== null && $factsort[$btag] > $factsort['BURI'] && $factsort[$btag] < $factsort['CHAN']) {
555
-			$btag = 'BURI';
556
-		}
557
-
558
-		$ret = $factsort[$atag] - $factsort[$btag];
559
-
560
-		// If facts are the same then put dated facts before non-dated facts
561
-		if ($ret == 0) {
562
-			if ($a->getAttribute('DATE') !== null && $b->getAttribute('DATE') === null) {
563
-				return -1;
564
-			}
565
-
566
-			if ($b->getAttribute('DATE') !== null && $a->getAttribute('DATE') === null) {
567
-				return 1;
568
-			}
569
-
570
-			// If no sorting preference, then keep original ordering
571
-			$ret = $a->sortOrder - $b->sortOrder;
572
-		}
573
-
574
-		return $ret;
575
-	}
576
-
577
-	/**
578
-	 * Allow native PHP functions such as array_unique() to work with objects
579
-	 *
580
-	 * @return string
581
-	 */
582
-	public function __toString() {
583
-		return $this->fact_id . '@' . $this->parent->getXref();
584
-	}
24
+    /** @var string Unique identifier for this fact (currently implemented as a hash of the raw data). */
25
+    private $fact_id;
26
+
27
+    /** @var GedcomRecord The GEDCOM record from which this fact is taken */
28
+    private $parent;
29
+
30
+    /** @var string The raw GEDCOM data for this fact */
31
+    private $gedcom;
32
+
33
+    /** @var string The GEDCOM tag for this record */
34
+    private $tag;
35
+
36
+    /** @var bool Is this a recently deleted fact, pending approval? */
37
+    private $pending_deletion = false;
38
+
39
+    /** @var bool Is this a recently added fact, pending approval? */
40
+    private $pending_addition = false;
41
+
42
+    /** @var Date The date of this fact, from the “2 DATE …” attribute */
43
+    private $date;
44
+
45
+    /** @var Place The place of this fact, from the “2 PLAC …” attribute */
46
+    private $place;
47
+
48
+    /** @var int Temporary(!) variable Used by Functions::sortFacts() */
49
+    public $sortOrder;
50
+
51
+    /**
52
+     * Create an event object from a gedcom fragment.
53
+     * We need the parent object (to check privacy) and a (pseudo) fact ID to
54
+     * identify the fact within the record.
55
+     *
56
+     * @param string          $gedcom
57
+     * @param GedcomRecord $parent
58
+     * @param string          $fact_id
59
+     *
60
+     * @throws \InvalidArgumentException
61
+     */
62
+    public function __construct($gedcom, GedcomRecord $parent, $fact_id) {
63
+        if (preg_match('/^1 (' . WT_REGEX_TAG . ')/', $gedcom, $match)) {
64
+            $this->gedcom  = $gedcom;
65
+            $this->parent  = $parent;
66
+            $this->fact_id = $fact_id;
67
+            $this->tag     = $match[1];
68
+        } else {
69
+            throw new \InvalidArgumentException('Invalid GEDCOM data passed to Fact::_construct(' . $gedcom . ')');
70
+        }
71
+    }
72
+
73
+    /**
74
+     * Get the value of level 1 data in the fact
75
+     * Allow for multi-line values
76
+     *
77
+     * @return string|null
78
+     */
79
+    public function getValue() {
80
+        if (preg_match('/^1 (?:' . $this->tag . ') ?(.*(?:(?:\n2 CONT ?.*)*))/', $this->gedcom, $match)) {
81
+            return preg_replace("/\n2 CONT ?/", "\n", $match[1]);
82
+        } else {
83
+            return null;
84
+        }
85
+    }
86
+
87
+    /**
88
+     * Get the record to which this fact links
89
+     *
90
+     * @return Individual|Family|Source|Repository|Media|Note|null
91
+     */
92
+    public function getTarget() {
93
+        $xref = trim($this->getValue(), '@');
94
+        switch ($this->tag) {
95
+        case 'FAMC':
96
+        case 'FAMS':
97
+            return Family::getInstance($xref, $this->getParent()->getTree());
98
+        case 'HUSB':
99
+        case 'WIFE':
100
+        case 'CHIL':
101
+            return Individual::getInstance($xref, $this->getParent()->getTree());
102
+        case 'SOUR':
103
+            return Source::getInstance($xref, $this->getParent()->getTree());
104
+        case 'OBJE':
105
+            return Media::getInstance($xref, $this->getParent()->getTree());
106
+        case 'REPO':
107
+            return Repository::getInstance($xref, $this->getParent()->getTree());
108
+        case 'NOTE':
109
+            return Note::getInstance($xref, $this->getParent()->getTree());
110
+        default:
111
+            return GedcomRecord::getInstance($xref, $this->getParent()->getTree());
112
+        }
113
+    }
114
+
115
+    /**
116
+     * Get the value of level 2 data in the fact
117
+     *
118
+     * @param string $tag
119
+     *
120
+     * @return string|null
121
+     */
122
+    public function getAttribute($tag) {
123
+        if (preg_match('/\n2 (?:' . $tag . ') ?(.*(?:(?:\n3 CONT ?.*)*)*)/', $this->gedcom, $match)) {
124
+            return preg_replace("/\n3 CONT ?/", "\n", $match[1]);
125
+        } else {
126
+            return null;
127
+        }
128
+    }
129
+
130
+    /**
131
+     * Do the privacy rules allow us to display this fact to the current user
132
+     *
133
+     * @param int|null $access_level
134
+     *
135
+     * @return bool
136
+     */
137
+    public function canShow($access_level = null) {
138
+        if ($access_level === null) {
139
+            $access_level = Auth::accessLevel($this->getParent()->getTree());
140
+        }
141
+
142
+        // Does this record have an explicit RESN?
143
+        if (strpos($this->gedcom, "\n2 RESN confidential")) {
144
+            return Auth::PRIV_NONE >= $access_level;
145
+        }
146
+        if (strpos($this->gedcom, "\n2 RESN privacy")) {
147
+            return Auth::PRIV_USER >= $access_level;
148
+        }
149
+        if (strpos($this->gedcom, "\n2 RESN none")) {
150
+            return true;
151
+        }
152
+
153
+        // Does this record have a default RESN?
154
+        $xref                    = $this->parent->getXref();
155
+        $fact_privacy            = $this->parent->getTree()->getFactPrivacy();
156
+        $individual_fact_privacy = $this->parent->getTree()->getIndividualFactPrivacy();
157
+        if (isset($individual_fact_privacy[$xref][$this->tag])) {
158
+            return $individual_fact_privacy[$xref][$this->tag] >= $access_level;
159
+        }
160
+        if (isset($fact_privacy[$this->tag])) {
161
+            return $fact_privacy[$this->tag] >= $access_level;
162
+        }
163
+
164
+        // No restrictions - it must be public
165
+        return true;
166
+    }
167
+
168
+    /**
169
+     * Check whether this fact is protected against edit
170
+     *
171
+     * @return bool
172
+     */
173
+    public function canEdit() {
174
+        // Managers can edit anything
175
+        // Members cannot edit RESN, CHAN and locked records
176
+        return
177
+            $this->parent->canEdit() && !$this->isPendingDeletion() && (
178
+                Auth::isManager($this->parent->getTree()) ||
179
+                Auth::isEditor($this->parent->getTree()) && strpos($this->gedcom, "\n2 RESN locked") === false && $this->getTag() != 'RESN' && $this->getTag() != 'CHAN'
180
+            );
181
+    }
182
+
183
+    /**
184
+     * The place where the event occured.
185
+     *
186
+     * @return Place
187
+     */
188
+    public function getPlace() {
189
+        if ($this->place === null) {
190
+            $this->place = new Place($this->getAttribute('PLAC'), $this->getParent()->getTree());
191
+        }
192
+
193
+        return $this->place;
194
+    }
195
+
196
+    /**
197
+     * Get the date for this fact.
198
+     * We can call this function many times, especially when sorting,
199
+     * so keep a copy of the date.
200
+     *
201
+     * @return Date
202
+     */
203
+    public function getDate() {
204
+        if ($this->date === null) {
205
+            $this->date = new Date($this->getAttribute('DATE'));
206
+        }
207
+
208
+        return $this->date;
209
+    }
210
+
211
+    /**
212
+     * The raw GEDCOM data for this fact
213
+     *
214
+     * @return string
215
+     */
216
+    public function getGedcom() {
217
+        return $this->gedcom;
218
+    }
219
+
220
+    /**
221
+     * Get a (pseudo) primary key for this fact.
222
+     *
223
+     * @return string
224
+     */
225
+    public function getFactId() {
226
+        return $this->fact_id;
227
+    }
228
+
229
+    // What sort of fact is this?
230
+    /**
231
+     * What is the tag (type) of this fact, such as BIRT, MARR or DEAT.
232
+     *
233
+     * @return string
234
+     */
235
+    public function getTag() {
236
+        return $this->tag;
237
+    }
238
+
239
+    /**
240
+     * Used to convert a real fact (e.g. BIRT) into a close-relative’s fact (e.g. _BIRT_CHIL)
241
+     *
242
+     * @param string $tag
243
+     */
244
+    public function setTag($tag) {
245
+        $this->tag = $tag;
246
+    }
247
+
248
+    //
249
+    /**
250
+     * The Person/Family record where this Fact came from
251
+     *
252
+     * @return Individual|Family|Source|Repository|Media|Note|GedcomRecord
253
+     */
254
+    public function getParent() {
255
+        return $this->parent;
256
+    }
257
+
258
+    /**
259
+     * Get the name of this fact type, for use as a label.
260
+     *
261
+     * @return string
262
+     */
263
+    public function getLabel() {
264
+        switch ($this->tag) {
265
+        case 'EVEN':
266
+        case 'FACT':
267
+            if ($this->getAttribute('TYPE')) {
268
+                // Custom FACT/EVEN - with a TYPE
269
+                return I18N::translate(Filter::escapeHtml($this->getAttribute('TYPE')));
270
+            }
271
+            // no break - drop into next case
272
+        default:
273
+            return GedcomTag::getLabel($this->tag, $this->parent);
274
+        }
275
+    }
276
+
277
+    /**
278
+     * This is a newly deleted fact, pending approval.
279
+     */
280
+    public function setPendingDeletion() {
281
+        $this->pending_deletion = true;
282
+        $this->pending_addition = false;
283
+    }
284
+
285
+    /**
286
+     * Is this a newly deleted fact, pending approval.
287
+     *
288
+     * @return bool
289
+     */
290
+    public function isPendingDeletion() {
291
+        return $this->pending_deletion;
292
+    }
293
+
294
+    /**
295
+     * This is a newly added fact, pending approval.
296
+     */
297
+    public function setPendingAddition() {
298
+        $this->pending_addition = true;
299
+        $this->pending_deletion = false;
300
+    }
301
+
302
+    /**
303
+     * Is this a newly added fact, pending approval.
304
+     *
305
+     * @return bool
306
+     */
307
+    public function isPendingAddition() {
308
+        return $this->pending_addition;
309
+    }
310
+
311
+    /**
312
+     * Source citations linked to this fact
313
+     *
314
+     * @return string[]
315
+     */
316
+    public function getCitations() {
317
+        preg_match_all('/\n(2 SOUR @(' . WT_REGEX_XREF . ')@(?:\n[3-9] .*)*)/', $this->getGedcom(), $matches, PREG_SET_ORDER);
318
+        $citations = array();
319
+        foreach ($matches as $match) {
320
+            $source = Source::getInstance($match[2], $this->getParent()->getTree());
321
+            if ($source->canShow()) {
322
+                $citations[] = $match[1];
323
+            }
324
+        }
325
+
326
+        return $citations;
327
+    }
328
+
329
+    /**
330
+     * Notes (inline and objects) linked to this fact
331
+     *
332
+     * @return string[]|Note[]
333
+     */
334
+    public function getNotes() {
335
+        $notes = array();
336
+        preg_match_all('/\n2 NOTE ?(.*(?:\n3.*)*)/', $this->getGedcom(), $matches);
337
+        foreach ($matches[1] as $match) {
338
+            $note = preg_replace("/\n3 CONT ?/", "\n", $match);
339
+            if (preg_match('/@(' . WT_REGEX_XREF . ')@/', $note, $nmatch)) {
340
+                $note = Note::getInstance($nmatch[1], $this->getParent()->getTree());
341
+                if ($note && $note->canShow()) {
342
+                    // A note object
343
+                    $notes[] = $note;
344
+                }
345
+            } else {
346
+                // An inline note
347
+                $notes[] = $note;
348
+            }
349
+        }
350
+
351
+        return $notes;
352
+    }
353
+
354
+    /**
355
+     * Media objects linked to this fact
356
+     *
357
+     * @return Media[]
358
+     */
359
+    public function getMedia() {
360
+        $media = array();
361
+        preg_match_all('/\n2 OBJE @(' . WT_REGEX_XREF . ')@/', $this->getGedcom(), $matches);
362
+        foreach ($matches[1] as $match) {
363
+            $obje = Media::getInstance($match, $this->getParent()->getTree());
364
+            if ($obje->canShow()) {
365
+                $media[] = $obje;
366
+            }
367
+        }
368
+
369
+        return $media;
370
+    }
371
+
372
+    /**
373
+     * A one-line summary of the fact - for charts, etc.
374
+     *
375
+     * @return string
376
+     */
377
+    public function summary() {
378
+        $attributes = array();
379
+        $target     = $this->getTarget();
380
+        if ($target) {
381
+            $attributes[] = $target->getFullName();
382
+        } else {
383
+            // Fact value
384
+            $value = $this->getValue();
385
+            if ($value !== '' && $value !== 'Y') {
386
+                $attributes[] = '<span dir="auto">' . Filter::escapeHtml($value) . '</span>';
387
+            }
388
+            // Fact date
389
+            $date = $this->getDate();
390
+            if ($date->isOK()) {
391
+                if (in_array($this->getTag(), explode('|', WT_EVENTS_BIRT)) && $this->getParent() instanceof Individual && $this->getParent()->getTree()->getPreference('SHOW_PARENTS_AGE')) {
392
+                    $attributes[] = $date->display() . FunctionsPrint::formatParentsAges($this->getParent(), $date);
393
+                } else {
394
+                    $attributes[] = $date->display();
395
+                }
396
+            }
397
+            // Fact place
398
+            if (!$this->getPlace()->isEmpty()) {
399
+                $attributes[] = $this->getPlace()->getShortName();
400
+            }
401
+        }
402
+
403
+        $class = 'fact_' . $this->getTag();
404
+        if ($this->isPendingAddition()) {
405
+            $class .= ' new';
406
+        } elseif ($this->isPendingDeletion()) {
407
+            $class .= ' old';
408
+        }
409
+
410
+        return
411
+            '<div class="' . $class . '">' .
412
+            /* I18N: a label/value pair, such as “Occupation: Farmer”. Some languages may need to change the punctuation. */
413
+            I18N::translate('<span class="label">%1$s:</span> <span class="field" dir="auto">%2$s</span>', $this->getLabel(), implode(' — ', $attributes)) .
414
+            '</div>';
415
+    }
416
+
417
+    /**
418
+     * Static Helper functions to sort events
419
+     *
420
+     * @param Fact $a Fact one
421
+     * @param Fact $b Fact two
422
+     *
423
+     * @return int
424
+     */
425
+    public static function compareDate(Fact $a, Fact $b) {
426
+        if ($a->getDate()->isOK() && $b->getDate()->isOK()) {
427
+            // If both events have dates, compare by date
428
+            $ret = Date::compare($a->getDate(), $b->getDate());
429
+
430
+            if ($ret == 0) {
431
+                // If dates are the same, compare by fact type
432
+                $ret = self::compareType($a, $b);
433
+
434
+                // If the fact type is also the same, retain the initial order
435
+                if ($ret == 0) {
436
+                    $ret = $a->sortOrder - $b->sortOrder;
437
+                }
438
+            }
439
+
440
+            return $ret;
441
+        } else {
442
+            // One or both events have no date - retain the initial order
443
+            return $a->sortOrder - $b->sortOrder;
444
+        }
445
+    }
446
+
447
+    /**
448
+     * Static method to compare two events by their type.
449
+     *
450
+     * @param Fact $a Fact one
451
+     * @param Fact $b Fact two
452
+     *
453
+     * @return int
454
+     */
455
+    public static function compareType(Fact $a, Fact $b) {
456
+        global $factsort;
457
+
458
+        if (empty($factsort)) {
459
+            $factsort = array_flip(
460
+                array(
461
+                    'BIRT',
462
+                    '_HNM',
463
+                    'ALIA', '_AKA', '_AKAN',
464
+                    'ADOP', '_ADPF', '_ADPF',
465
+                    '_BRTM',
466
+                    'CHR', 'BAPM',
467
+                    'FCOM',
468
+                    'CONF',
469
+                    'BARM', 'BASM',
470
+                    'EDUC',
471
+                    'GRAD',
472
+                    '_DEG',
473
+                    'EMIG', 'IMMI',
474
+                    'NATU',
475
+                    '_MILI', '_MILT',
476
+                    'ENGA',
477
+                    'MARB', 'MARC', 'MARL', '_MARI', '_MBON',
478
+                    'MARR', 'MARR_CIVIL', 'MARR_RELIGIOUS', 'MARR_PARTNERS', 'MARR_UNKNOWN', '_COML',
479
+                    '_STAT',
480
+                    '_SEPR',
481
+                    'DIVF',
482
+                    'MARS',
483
+                    '_BIRT_CHIL',
484
+                    'DIV', 'ANUL',
485
+                    '_BIRT_', '_MARR_', '_DEAT_', '_BURI_', // other events of close relatives
486
+                    'CENS',
487
+                    'OCCU',
488
+                    'RESI',
489
+                    'PROP',
490
+                    'CHRA',
491
+                    'RETI',
492
+                    'FACT', 'EVEN',
493
+                    '_NMR', '_NMAR', 'NMR',
494
+                    'NCHI',
495
+                    'WILL',
496
+                    '_HOL',
497
+                    '_????_',
498
+                    'DEAT',
499
+                    '_FNRL', 'CREM', 'BURI', '_INTE',
500
+                    '_YART',
501
+                    '_NLIV',
502
+                    'PROB',
503
+                    'TITL',
504
+                    'COMM',
505
+                    'NATI',
506
+                    'CITN',
507
+                    'CAST',
508
+                    'RELI',
509
+                    'SSN', 'IDNO',
510
+                    'TEMP',
511
+                    'SLGC', 'BAPL', 'CONL', 'ENDL', 'SLGS',
512
+                    'ADDR', 'PHON', 'EMAIL', '_EMAIL', 'EMAL', 'FAX', 'WWW', 'URL', '_URL',
513
+                    'FILE', // For media objects
514
+                    'AFN', 'REFN', '_PRMN', 'REF', 'RIN', '_UID',
515
+                    'OBJE', 'NOTE', 'SOUR',
516
+                    'CHAN', '_TODO',
517
+                )
518
+            );
519
+        }
520
+
521
+        // Facts from same families stay grouped together
522
+        // Keep MARR and DIV from the same families from mixing with events from other FAMs
523
+        // Use the original order in which the facts were added
524
+        if ($a->parent instanceof Family && $b->parent instanceof Family && $a->parent !== $b->parent) {
525
+            return $a->sortOrder - $b->sortOrder;
526
+        }
527
+
528
+        $atag = $a->getTag();
529
+        $btag = $b->getTag();
530
+
531
+        // Events not in the above list get mapped onto one that is.
532
+        if (!array_key_exists($atag, $factsort)) {
533
+            if (preg_match('/^(_(BIRT|MARR|DEAT|BURI)_)/', $atag, $match)) {
534
+                $atag = $match[1];
535
+            } else {
536
+                $atag = "_????_";
537
+            }
538
+        }
539
+
540
+        if (!array_key_exists($btag, $factsort)) {
541
+            if (preg_match('/^(_(BIRT|MARR|DEAT|BURI)_)/', $btag, $match)) {
542
+                $btag = $match[1];
543
+            } else {
544
+                $btag = "_????_";
545
+            }
546
+        }
547
+
548
+        // - Don't let dated after DEAT/BURI facts sort non-dated facts before DEAT/BURI
549
+        // - Treat dated after BURI facts as BURI instead
550
+        if ($a->getAttribute('DATE') !== null && $factsort[$atag] > $factsort['BURI'] && $factsort[$atag] < $factsort['CHAN']) {
551
+            $atag = 'BURI';
552
+        }
553
+
554
+        if ($b->getAttribute('DATE') !== null && $factsort[$btag] > $factsort['BURI'] && $factsort[$btag] < $factsort['CHAN']) {
555
+            $btag = 'BURI';
556
+        }
557
+
558
+        $ret = $factsort[$atag] - $factsort[$btag];
559
+
560
+        // If facts are the same then put dated facts before non-dated facts
561
+        if ($ret == 0) {
562
+            if ($a->getAttribute('DATE') !== null && $b->getAttribute('DATE') === null) {
563
+                return -1;
564
+            }
565
+
566
+            if ($b->getAttribute('DATE') !== null && $a->getAttribute('DATE') === null) {
567
+                return 1;
568
+            }
569
+
570
+            // If no sorting preference, then keep original ordering
571
+            $ret = $a->sortOrder - $b->sortOrder;
572
+        }
573
+
574
+        return $ret;
575
+    }
576
+
577
+    /**
578
+     * Allow native PHP functions such as array_unique() to work with objects
579
+     *
580
+     * @return string
581
+     */
582
+    public function __toString() {
583
+        return $this->fact_id . '@' . $this->parent->getXref();
584
+    }
585 585
 }
Please login to merge, or discard this patch.
Switch Indentation   +26 added lines, -26 removed lines patch added patch discarded remove patch
@@ -92,23 +92,23 @@  discard block
 block discarded – undo
92 92
 	public function getTarget() {
93 93
 		$xref = trim($this->getValue(), '@');
94 94
 		switch ($this->tag) {
95
-		case 'FAMC':
96
-		case 'FAMS':
97
-			return Family::getInstance($xref, $this->getParent()->getTree());
98
-		case 'HUSB':
99
-		case 'WIFE':
100
-		case 'CHIL':
101
-			return Individual::getInstance($xref, $this->getParent()->getTree());
102
-		case 'SOUR':
103
-			return Source::getInstance($xref, $this->getParent()->getTree());
104
-		case 'OBJE':
105
-			return Media::getInstance($xref, $this->getParent()->getTree());
106
-		case 'REPO':
107
-			return Repository::getInstance($xref, $this->getParent()->getTree());
108
-		case 'NOTE':
109
-			return Note::getInstance($xref, $this->getParent()->getTree());
110
-		default:
111
-			return GedcomRecord::getInstance($xref, $this->getParent()->getTree());
95
+		    case 'FAMC':
96
+		    case 'FAMS':
97
+			    return Family::getInstance($xref, $this->getParent()->getTree());
98
+		    case 'HUSB':
99
+		    case 'WIFE':
100
+		    case 'CHIL':
101
+			    return Individual::getInstance($xref, $this->getParent()->getTree());
102
+		    case 'SOUR':
103
+			    return Source::getInstance($xref, $this->getParent()->getTree());
104
+		    case 'OBJE':
105
+			    return Media::getInstance($xref, $this->getParent()->getTree());
106
+		    case 'REPO':
107
+			    return Repository::getInstance($xref, $this->getParent()->getTree());
108
+		    case 'NOTE':
109
+			    return Note::getInstance($xref, $this->getParent()->getTree());
110
+		    default:
111
+			    return GedcomRecord::getInstance($xref, $this->getParent()->getTree());
112 112
 		}
113 113
 	}
114 114
 
@@ -262,15 +262,15 @@  discard block
 block discarded – undo
262 262
 	 */
263 263
 	public function getLabel() {
264 264
 		switch ($this->tag) {
265
-		case 'EVEN':
266
-		case 'FACT':
267
-			if ($this->getAttribute('TYPE')) {
268
-				// Custom FACT/EVEN - with a TYPE
269
-				return I18N::translate(Filter::escapeHtml($this->getAttribute('TYPE')));
270
-			}
271
-			// no break - drop into next case
272
-		default:
273
-			return GedcomTag::getLabel($this->tag, $this->parent);
265
+		    case 'EVEN':
266
+		    case 'FACT':
267
+			    if ($this->getAttribute('TYPE')) {
268
+				    // Custom FACT/EVEN - with a TYPE
269
+				    return I18N::translate(Filter::escapeHtml($this->getAttribute('TYPE')));
270
+			    }
271
+			    // no break - drop into next case
272
+		    default:
273
+			    return GedcomTag::getLabel($this->tag, $this->parent);
274 274
 		}
275 275
 	}
276 276
 
Please login to merge, or discard this patch.
Braces   +52 added lines, -26 removed lines patch added patch discarded remove patch
@@ -20,7 +20,8 @@  discard block
 block discarded – undo
20 20
 /**
21 21
  * A GEDCOM fact or event object.
22 22
  */
23
-class Fact {
23
+class Fact
24
+{
24 25
 	/** @var string Unique identifier for this fact (currently implemented as a hash of the raw data). */
25 26
 	private $fact_id;
26 27
 
@@ -59,7 +60,8 @@  discard block
 block discarded – undo
59 60
 	 *
60 61
 	 * @throws \InvalidArgumentException
61 62
 	 */
62
-	public function __construct($gedcom, GedcomRecord $parent, $fact_id) {
63
+	public function __construct($gedcom, GedcomRecord $parent, $fact_id)
64
+	{
63 65
 		if (preg_match('/^1 (' . WT_REGEX_TAG . ')/', $gedcom, $match)) {
64 66
 			$this->gedcom  = $gedcom;
65 67
 			$this->parent  = $parent;
@@ -76,7 +78,8 @@  discard block
 block discarded – undo
76 78
 	 *
77 79
 	 * @return string|null
78 80
 	 */
79
-	public function getValue() {
81
+	public function getValue()
82
+	{
80 83
 		if (preg_match('/^1 (?:' . $this->tag . ') ?(.*(?:(?:\n2 CONT ?.*)*))/', $this->gedcom, $match)) {
81 84
 			return preg_replace("/\n2 CONT ?/", "\n", $match[1]);
82 85
 		} else {
@@ -89,7 +92,8 @@  discard block
 block discarded – undo
89 92
 	 *
90 93
 	 * @return Individual|Family|Source|Repository|Media|Note|null
91 94
 	 */
92
-	public function getTarget() {
95
+	public function getTarget()
96
+	{
93 97
 		$xref = trim($this->getValue(), '@');
94 98
 		switch ($this->tag) {
95 99
 		case 'FAMC':
@@ -119,7 +123,8 @@  discard block
 block discarded – undo
119 123
 	 *
120 124
 	 * @return string|null
121 125
 	 */
122
-	public function getAttribute($tag) {
126
+	public function getAttribute($tag)
127
+	{
123 128
 		if (preg_match('/\n2 (?:' . $tag . ') ?(.*(?:(?:\n3 CONT ?.*)*)*)/', $this->gedcom, $match)) {
124 129
 			return preg_replace("/\n3 CONT ?/", "\n", $match[1]);
125 130
 		} else {
@@ -134,7 +139,8 @@  discard block
 block discarded – undo
134 139
 	 *
135 140
 	 * @return bool
136 141
 	 */
137
-	public function canShow($access_level = null) {
142
+	public function canShow($access_level = null)
143
+	{
138 144
 		if ($access_level === null) {
139 145
 			$access_level = Auth::accessLevel($this->getParent()->getTree());
140 146
 		}
@@ -170,7 +176,8 @@  discard block
 block discarded – undo
170 176
 	 *
171 177
 	 * @return bool
172 178
 	 */
173
-	public function canEdit() {
179
+	public function canEdit()
180
+	{
174 181
 		// Managers can edit anything
175 182
 		// Members cannot edit RESN, CHAN and locked records
176 183
 		return
@@ -185,7 +192,8 @@  discard block
 block discarded – undo
185 192
 	 *
186 193
 	 * @return Place
187 194
 	 */
188
-	public function getPlace() {
195
+	public function getPlace()
196
+	{
189 197
 		if ($this->place === null) {
190 198
 			$this->place = new Place($this->getAttribute('PLAC'), $this->getParent()->getTree());
191 199
 		}
@@ -200,7 +208,8 @@  discard block
 block discarded – undo
200 208
 	 *
201 209
 	 * @return Date
202 210
 	 */
203
-	public function getDate() {
211
+	public function getDate()
212
+	{
204 213
 		if ($this->date === null) {
205 214
 			$this->date = new Date($this->getAttribute('DATE'));
206 215
 		}
@@ -213,7 +222,8 @@  discard block
 block discarded – undo
213 222
 	 *
214 223
 	 * @return string
215 224
 	 */
216
-	public function getGedcom() {
225
+	public function getGedcom()
226
+	{
217 227
 		return $this->gedcom;
218 228
 	}
219 229
 
@@ -222,7 +232,8 @@  discard block
 block discarded – undo
222 232
 	 *
223 233
 	 * @return string
224 234
 	 */
225
-	public function getFactId() {
235
+	public function getFactId()
236
+	{
226 237
 		return $this->fact_id;
227 238
 	}
228 239
 
@@ -232,7 +243,8 @@  discard block
 block discarded – undo
232 243
 	 *
233 244
 	 * @return string
234 245
 	 */
235
-	public function getTag() {
246
+	public function getTag()
247
+	{
236 248
 		return $this->tag;
237 249
 	}
238 250
 
@@ -241,7 +253,8 @@  discard block
 block discarded – undo
241 253
 	 *
242 254
 	 * @param string $tag
243 255
 	 */
244
-	public function setTag($tag) {
256
+	public function setTag($tag)
257
+	{
245 258
 		$this->tag = $tag;
246 259
 	}
247 260
 
@@ -251,7 +264,8 @@  discard block
 block discarded – undo
251 264
 	 *
252 265
 	 * @return Individual|Family|Source|Repository|Media|Note|GedcomRecord
253 266
 	 */
254
-	public function getParent() {
267
+	public function getParent()
268
+	{
255 269
 		return $this->parent;
256 270
 	}
257 271
 
@@ -260,7 +274,8 @@  discard block
 block discarded – undo
260 274
 	 *
261 275
 	 * @return string
262 276
 	 */
263
-	public function getLabel() {
277
+	public function getLabel()
278
+	{
264 279
 		switch ($this->tag) {
265 280
 		case 'EVEN':
266 281
 		case 'FACT':
@@ -277,7 +292,8 @@  discard block
 block discarded – undo
277 292
 	/**
278 293
 	 * This is a newly deleted fact, pending approval.
279 294
 	 */
280
-	public function setPendingDeletion() {
295
+	public function setPendingDeletion()
296
+	{
281 297
 		$this->pending_deletion = true;
282 298
 		$this->pending_addition = false;
283 299
 	}
@@ -287,14 +303,16 @@  discard block
 block discarded – undo
287 303
 	 *
288 304
 	 * @return bool
289 305
 	 */
290
-	public function isPendingDeletion() {
306
+	public function isPendingDeletion()
307
+	{
291 308
 		return $this->pending_deletion;
292 309
 	}
293 310
 
294 311
 	/**
295 312
 	 * This is a newly added fact, pending approval.
296 313
 	 */
297
-	public function setPendingAddition() {
314
+	public function setPendingAddition()
315
+	{
298 316
 		$this->pending_addition = true;
299 317
 		$this->pending_deletion = false;
300 318
 	}
@@ -304,7 +322,8 @@  discard block
 block discarded – undo
304 322
 	 *
305 323
 	 * @return bool
306 324
 	 */
307
-	public function isPendingAddition() {
325
+	public function isPendingAddition()
326
+	{
308 327
 		return $this->pending_addition;
309 328
 	}
310 329
 
@@ -313,7 +332,8 @@  discard block
 block discarded – undo
313 332
 	 *
314 333
 	 * @return string[]
315 334
 	 */
316
-	public function getCitations() {
335
+	public function getCitations()
336
+	{
317 337
 		preg_match_all('/\n(2 SOUR @(' . WT_REGEX_XREF . ')@(?:\n[3-9] .*)*)/', $this->getGedcom(), $matches, PREG_SET_ORDER);
318 338
 		$citations = array();
319 339
 		foreach ($matches as $match) {
@@ -331,7 +351,8 @@  discard block
 block discarded – undo
331 351
 	 *
332 352
 	 * @return string[]|Note[]
333 353
 	 */
334
-	public function getNotes() {
354
+	public function getNotes()
355
+	{
335 356
 		$notes = array();
336 357
 		preg_match_all('/\n2 NOTE ?(.*(?:\n3.*)*)/', $this->getGedcom(), $matches);
337 358
 		foreach ($matches[1] as $match) {
@@ -356,7 +377,8 @@  discard block
 block discarded – undo
356 377
 	 *
357 378
 	 * @return Media[]
358 379
 	 */
359
-	public function getMedia() {
380
+	public function getMedia()
381
+	{
360 382
 		$media = array();
361 383
 		preg_match_all('/\n2 OBJE @(' . WT_REGEX_XREF . ')@/', $this->getGedcom(), $matches);
362 384
 		foreach ($matches[1] as $match) {
@@ -374,7 +396,8 @@  discard block
 block discarded – undo
374 396
 	 *
375 397
 	 * @return string
376 398
 	 */
377
-	public function summary() {
399
+	public function summary()
400
+	{
378 401
 		$attributes = array();
379 402
 		$target     = $this->getTarget();
380 403
 		if ($target) {
@@ -422,7 +445,8 @@  discard block
 block discarded – undo
422 445
 	 *
423 446
 	 * @return int
424 447
 	 */
425
-	public static function compareDate(Fact $a, Fact $b) {
448
+	public static function compareDate(Fact $a, Fact $b)
449
+	{
426 450
 		if ($a->getDate()->isOK() && $b->getDate()->isOK()) {
427 451
 			// If both events have dates, compare by date
428 452
 			$ret = Date::compare($a->getDate(), $b->getDate());
@@ -452,7 +476,8 @@  discard block
 block discarded – undo
452 476
 	 *
453 477
 	 * @return int
454 478
 	 */
455
-	public static function compareType(Fact $a, Fact $b) {
479
+	public static function compareType(Fact $a, Fact $b)
480
+	{
456 481
 		global $factsort;
457 482
 
458 483
 		if (empty($factsort)) {
@@ -579,7 +604,8 @@  discard block
 block discarded – undo
579 604
 	 *
580 605
 	 * @return string
581 606
 	 */
582
-	public function __toString() {
607
+	public function __toString()
608
+	{
583 609
 		return $this->fact_id . '@' . $this->parent->getXref();
584 610
 	}
585 611
 }
Please login to merge, or discard this patch.
app/Stats.php 3 patches
Indentation   +6934 added lines, -6934 removed lines patch added patch discarded remove patch
@@ -34,5529 +34,5529 @@  discard block
 block discarded – undo
34 34
  * are also used elsewhere in the code.
35 35
  */
36 36
 class Stats {
37
-	/** @var Tree Generate statistics for a specified tree. */
38
-	private $tree;
39
-
40
-	/** @var string[] All public functions are available as keywords - except these ones */
41
-	private $public_but_not_allowed = array(
42
-		'__construct', 'embedTags', 'iso3166', 'getAllCountries', 'getAllTagsTable', 'getAllTagsText', 'statsPlaces', 'statsBirthQuery', 'statsDeathQuery', 'statsMarrQuery', 'statsAgeQuery', 'monthFirstChildQuery', 'statsChildrenQuery', 'statsMarrAgeQuery',
43
-	);
44
-
45
-	/** @var string[] List of GEDCOM media types */
46
-	private $_media_types = array('audio', 'book', 'card', 'certificate', 'coat', 'document', 'electronic', 'magazine', 'manuscript', 'map', 'fiche', 'film', 'newspaper', 'painting', 'photo', 'tombstone', 'video', 'other');
47
-
48
-	/**
49
-	 * Create the statistics for a tree.
50
-	 *
51
-	 * @param Tree $tree Generate statistics for this tree
52
-	 */
53
-	public function __construct(Tree $tree) {
54
-		$this->tree = $tree;
55
-	}
56
-
57
-	/**
58
-	 * Return a string of all supported tags and an example of its output in table row form.
59
-	 *
60
-	 * @return string
61
-	 */
62
-	public function getAllTagsTable() {
63
-		$examples = array();
64
-		foreach (get_class_methods($this) as $method) {
65
-			$reflection = new \ReflectionMethod($this, $method);
66
-			if ($reflection->isPublic() && !in_array($method, $this->public_but_not_allowed)) {
67
-				$examples[$method] = $this->$method();
68
-			}
69
-		}
70
-		ksort($examples);
71
-
72
-		$html = '';
73
-		foreach ($examples as $tag => $value) {
74
-			$html .= '<tr>';
75
-			$html .= '<td class="list_value_wrap">' . $tag . '</td>';
76
-			$html .= '<td class="list_value_wrap">' . $value . '</td>';
77
-			$html .= '</tr>';
78
-		}
79
-
80
-		return
81
-			'<table id="keywords" style="width:100%; table-layout:fixed"><thead>' .
82
-			'<tr>' .
83
-			'<th class="list_label_wrap width25">' .
84
-			I18N::translate('Embedded variable') .
85
-			'</th>' .
86
-			'<th class="list_label_wrap width75">' .
87
-			I18N::translate('Resulting value') .
88
-			'</th>' .
89
-			'</tr>' .
90
-			'</thead><tbody>' .
91
-			$html .
92
-			'</tbody></table>';
93
-	}
94
-
95
-	/**
96
-	 * Return a string of all supported tags in plain text.
97
-	 *
98
-	 * @return string
99
-	 */
100
-	public function getAllTagsText() {
101
-		$examples = array();
102
-		foreach (get_class_methods($this) as $method) {
103
-			$reflection = new \ReflectionMethod($this, $method);
104
-			if ($reflection->isPublic() && !in_array($method, $this->public_but_not_allowed)) {
105
-				$examples[$method] = $method;
106
-			}
107
-		}
108
-		ksort($examples);
109
-
110
-		return implode('<br>', $examples);
111
-	}
112
-
113
-	/**
114
-	 * Get tags and their parsed results.
115
-	 *
116
-	 * @param string $text
117
-	 *
118
-	 * @return string[][]
119
-	 */
120
-	private function getTags($text) {
121
-		static $funcs;
122
-
123
-		// Retrive all class methods
124
-		isset($funcs) or $funcs = get_class_methods($this);
125
-
126
-		// Extract all tags from the provided text
127
-		preg_match_all("/#([^#]+)(?=#)/", (string) $text, $match);
128
-		$tags       = $match[1];
129
-		$c          = count($tags);
130
-		$new_tags   = array(); // tag to replace
131
-		$new_values = array(); // value to replace it with
132
-
133
-		/*
37
+    /** @var Tree Generate statistics for a specified tree. */
38
+    private $tree;
39
+
40
+    /** @var string[] All public functions are available as keywords - except these ones */
41
+    private $public_but_not_allowed = array(
42
+        '__construct', 'embedTags', 'iso3166', 'getAllCountries', 'getAllTagsTable', 'getAllTagsText', 'statsPlaces', 'statsBirthQuery', 'statsDeathQuery', 'statsMarrQuery', 'statsAgeQuery', 'monthFirstChildQuery', 'statsChildrenQuery', 'statsMarrAgeQuery',
43
+    );
44
+
45
+    /** @var string[] List of GEDCOM media types */
46
+    private $_media_types = array('audio', 'book', 'card', 'certificate', 'coat', 'document', 'electronic', 'magazine', 'manuscript', 'map', 'fiche', 'film', 'newspaper', 'painting', 'photo', 'tombstone', 'video', 'other');
47
+
48
+    /**
49
+     * Create the statistics for a tree.
50
+     *
51
+     * @param Tree $tree Generate statistics for this tree
52
+     */
53
+    public function __construct(Tree $tree) {
54
+        $this->tree = $tree;
55
+    }
56
+
57
+    /**
58
+     * Return a string of all supported tags and an example of its output in table row form.
59
+     *
60
+     * @return string
61
+     */
62
+    public function getAllTagsTable() {
63
+        $examples = array();
64
+        foreach (get_class_methods($this) as $method) {
65
+            $reflection = new \ReflectionMethod($this, $method);
66
+            if ($reflection->isPublic() && !in_array($method, $this->public_but_not_allowed)) {
67
+                $examples[$method] = $this->$method();
68
+            }
69
+        }
70
+        ksort($examples);
71
+
72
+        $html = '';
73
+        foreach ($examples as $tag => $value) {
74
+            $html .= '<tr>';
75
+            $html .= '<td class="list_value_wrap">' . $tag . '</td>';
76
+            $html .= '<td class="list_value_wrap">' . $value . '</td>';
77
+            $html .= '</tr>';
78
+        }
79
+
80
+        return
81
+            '<table id="keywords" style="width:100%; table-layout:fixed"><thead>' .
82
+            '<tr>' .
83
+            '<th class="list_label_wrap width25">' .
84
+            I18N::translate('Embedded variable') .
85
+            '</th>' .
86
+            '<th class="list_label_wrap width75">' .
87
+            I18N::translate('Resulting value') .
88
+            '</th>' .
89
+            '</tr>' .
90
+            '</thead><tbody>' .
91
+            $html .
92
+            '</tbody></table>';
93
+    }
94
+
95
+    /**
96
+     * Return a string of all supported tags in plain text.
97
+     *
98
+     * @return string
99
+     */
100
+    public function getAllTagsText() {
101
+        $examples = array();
102
+        foreach (get_class_methods($this) as $method) {
103
+            $reflection = new \ReflectionMethod($this, $method);
104
+            if ($reflection->isPublic() && !in_array($method, $this->public_but_not_allowed)) {
105
+                $examples[$method] = $method;
106
+            }
107
+        }
108
+        ksort($examples);
109
+
110
+        return implode('<br>', $examples);
111
+    }
112
+
113
+    /**
114
+     * Get tags and their parsed results.
115
+     *
116
+     * @param string $text
117
+     *
118
+     * @return string[][]
119
+     */
120
+    private function getTags($text) {
121
+        static $funcs;
122
+
123
+        // Retrive all class methods
124
+        isset($funcs) or $funcs = get_class_methods($this);
125
+
126
+        // Extract all tags from the provided text
127
+        preg_match_all("/#([^#]+)(?=#)/", (string) $text, $match);
128
+        $tags       = $match[1];
129
+        $c          = count($tags);
130
+        $new_tags   = array(); // tag to replace
131
+        $new_values = array(); // value to replace it with
132
+
133
+        /*
134 134
 		 * Parse block tags.
135 135
 		 */
136
-		for ($i = 0; $i < $c; $i++) {
137
-			$full_tag = $tags[$i];
138
-			// Added for new parameter support
139
-			$params = explode(':', $tags[$i]);
140
-			if (count($params) > 1) {
141
-				$tags[$i] = array_shift($params);
142
-			} else {
143
-				$params = array();
144
-			}
145
-
146
-			// Generate the replacement value for the tag
147
-			if (method_exists($this, $tags[$i])) {
148
-				$new_tags[]   = "#{$full_tag}#";
149
-				$new_values[] = call_user_func_array(array($this, $tags[$i]), array($params));
150
-			}
151
-		}
152
-
153
-		return array($new_tags, $new_values);
154
-	}
155
-
156
-	/**
157
-	 * Embed tags in text
158
-	 *
159
-	 * @param string $text
160
-	 *
161
-	 * @return string
162
-	 */
163
-	public function embedTags($text) {
164
-		if (strpos($text, '#') !== false) {
165
-			list($new_tags, $new_values) = $this->getTags($text);
166
-			$text                        = str_replace($new_tags, $new_values, $text);
167
-		}
168
-
169
-		return $text;
170
-	}
171
-
172
-	/**
173
-	 * Get the name used for GEDCOM files and URLs.
174
-	 *
175
-	 * @return string
176
-	 */
177
-	public function gedcomFilename() {
178
-		return $this->tree->getName();
179
-	}
180
-
181
-	/**
182
-	 * Get the internal ID number of the tree.
183
-	 *
184
-	 * @return int
185
-	 */
186
-	public function gedcomId() {
187
-		return $this->tree->getTreeId();
188
-	}
189
-
190
-	/**
191
-	 * Get the descriptive title of the tree.
192
-	 *
193
-	 * @return string
194
-	 */
195
-	public function gedcomTitle() {
196
-		return $this->tree->getTitleHtml();
197
-	}
198
-
199
-	/**
200
-	 * Get information from the GEDCOM's HEAD record.
201
-	 *
202
-	 * @return string[]
203
-	 */
204
-	private function gedcomHead() {
205
-		$title   = '';
206
-		$version = '';
207
-		$source  = '';
208
-
209
-		$head = GedcomRecord::getInstance('HEAD', $this->tree);
210
-		$sour = $head->getFirstFact('SOUR');
211
-		if ($sour) {
212
-			$source  = $sour->getValue();
213
-			$title   = $sour->getAttribute('NAME');
214
-			$version = $sour->getAttribute('VERS');
215
-		}
216
-
217
-		return array($title, $version, $source);
218
-	}
219
-
220
-	/**
221
-	 * Get the software originally used to create the GEDCOM file.
222
-	 *
223
-	 * @return string
224
-	 */
225
-	public function gedcomCreatedSoftware() {
226
-		$head = $this->gedcomHead();
227
-
228
-		return $head[0];
229
-	}
230
-
231
-	/**
232
-	 * Get the version of software which created the GEDCOM file.
233
-	 *
234
-	 * @return string
235
-	 */
236
-	public function gedcomCreatedVersion() {
237
-		$head = $this->gedcomHead();
238
-		// fix broken version string in Family Tree Maker
239
-		if (strstr($head[1], 'Family Tree Maker ')) {
240
-			$p       = strpos($head[1], '(') + 1;
241
-			$p2      = strpos($head[1], ')');
242
-			$head[1] = substr($head[1], $p, ($p2 - $p));
243
-		}
244
-		// Fix EasyTree version
245
-		if ($head[2] == 'EasyTree') {
246
-			$head[1] = substr($head[1], 1);
247
-		}
248
-
249
-		return $head[1];
250
-	}
251
-
252
-	/**
253
-	 * Get the date the GEDCOM file was created.
254
-	 *
255
-	 * @return string
256
-	 */
257
-	public function gedcomDate() {
258
-		$head = GedcomRecord::getInstance('HEAD', $this->tree);
259
-		$fact = $head->getFirstFact('DATE');
260
-		if ($fact) {
261
-			$date = new Date($fact->getValue());
262
-
263
-			return $date->display();
264
-		}
265
-
266
-		return '';
267
-	}
268
-
269
-	/**
270
-	 * When was this tree last updated?
271
-	 *
272
-	 * @return string
273
-	 */
274
-	public function gedcomUpdated() {
275
-		$row = Database::prepare(
276
-			"SELECT d_year, d_month, d_day FROM `##dates` WHERE d_julianday1 = (SELECT MAX(d_julianday1) FROM `##dates` WHERE d_file =? AND d_fact='CHAN') LIMIT 1"
277
-		)->execute(array($this->tree->getTreeId()))->fetchOneRow();
278
-		if ($row) {
279
-			$date = new Date("{$row->d_day} {$row->d_month} {$row->d_year}");
280
-
281
-			return $date->display();
282
-		} else {
283
-			return $this->gedcomDate();
284
-		}
285
-	}
286
-
287
-	/**
288
-	 * What is the significant individual from this tree?
289
-	 *
290
-	 * @return string
291
-	 */
292
-	public function gedcomRootId() {
293
-		return $this->tree->getPreference('PEDIGREE_ROOT_ID');
294
-	}
295
-
296
-	/**
297
-	 * Convert totals into percentages.
298
-	 *
299
-	 * @param string $total
300
-	 * @param string $type
301
-	 *
302
-	 * @return string
303
-	 */
304
-	private function getPercentage($total, $type) {
305
-		switch ($type) {
306
-		case 'individual':
307
-			$type = $this->totalIndividualsQuery();
308
-			break;
309
-		case 'family':
310
-			$type = $this->totalFamiliesQuery();
311
-			break;
312
-		case 'source':
313
-			$type = $this->totalSourcesQuery();
314
-			break;
315
-		case 'note':
316
-			$type = $this->totalNotesQuery();
317
-			break;
318
-		case 'all':
319
-		default:
320
-			$type = $this->totalIndividualsQuery() + $this->totalFamiliesQuery() + $this->totalSourcesQuery();
321
-			break;
322
-		}
323
-		if ($type == 0) {
324
-			return I18N::percentage(0, 1);
325
-		} else {
326
-			return I18N::percentage($total / $type, 1);
327
-		}
328
-	}
329
-
330
-	/**
331
-	 * How many GEDCOM records exist in the tree.
332
-	 *
333
-	 * @return string
334
-	 */
335
-	public function totalRecords() {
336
-		return I18N::number($this->totalIndividualsQuery() + $this->totalFamiliesQuery() + $this->totalSourcesQuery());
337
-	}
338
-
339
-	/**
340
-	 * How many individuals exist in the tree.
341
-	 *
342
-	 * @return int
343
-	 */
344
-	private function totalIndividualsQuery() {
345
-		return (int) Database::prepare(
346
-			"SELECT COUNT(*) FROM `##individuals` WHERE i_file = :tree_id"
347
-		)->execute(array(
348
-			'tree_id' => $this->tree->getTreeId(),
349
-		))->fetchOne();
350
-	}
351
-
352
-	/**
353
-	 * How many individuals exist in the tree.
354
-	 *
355
-	 * @return string
356
-	 */
357
-	public function totalIndividuals() {
358
-		return I18N::number($this->totalIndividualsQuery());
359
-	}
360
-
361
-	/**
362
-	 * How many individuals have one or more sources.
363
-	 *
364
-	 * @return int
365
-	 */
366
-	private function totalIndisWithSourcesQuery() {
367
-		return (int) Database::prepare(
368
-			"SELECT COUNT(DISTINCT i_id)" .
369
-			" FROM `##individuals` JOIN `##link` ON i_id = l_from AND i_file = l_file" .
370
-			" WHERE l_file = :tree_id AND l_type = 'SOUR'"
371
-		)->execute(array(
372
-			'tree_id' => $this->tree->getTreeId(),
373
-		))->fetchOne();
374
-	}
375
-
376
-	/**
377
-	 * How many individuals have one or more sources.
378
-	 *
379
-	 * @return string
380
-	 */
381
-	public function totalIndisWithSources() {
382
-		return I18N::number($this->totalIndisWithSourcesQuery());
383
-	}
384
-
385
-	/**
386
-	 * Create a chart showing individuals with/without sources.
387
-	 *
388
-	 * @param string[] $params
389
-	 *
390
-	 * @return string
391
-	 */
392
-	public function chartIndisWithSources($params = array()) {
393
-		$WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
394
-		$WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
395
-		$WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
396
-		$WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
397
-
398
-		if (isset($params[0]) && $params[0] != '') {
399
-			$size = strtolower($params[0]);
400
-		} else {
401
-			$size = $WT_STATS_S_CHART_X . 'x' . $WT_STATS_S_CHART_Y;
402
-		}
403
-		if (isset($params[1]) && $params[1] != '') {
404
-			$color_from = strtolower($params[1]);
405
-		} else {
406
-			$color_from = $WT_STATS_CHART_COLOR1;
407
-		}
408
-		if (isset($params[2]) && $params[2] != '') {
409
-			$color_to = strtolower($params[2]);
410
-		} else {
411
-			$color_to = $WT_STATS_CHART_COLOR2;
412
-		}
413
-		$sizes    = explode('x', $size);
414
-		$tot_indi = $this->totalIndividualsQuery();
415
-		if ($tot_indi == 0) {
416
-			return '';
417
-		} else {
418
-			$tot_sindi_per = round($this->totalIndisWithSourcesQuery() / $tot_indi, 3);
419
-			$chd           = $this->arrayToExtendedEncoding(array(100 - 100 * $tot_sindi_per, 100 * $tot_sindi_per));
420
-			$chl           = I18N::translate('Without sources') . ' - ' . I18N::percentage(1 - $tot_sindi_per, 1) . '|' .
421
-				I18N::translate('With sources') . ' - ' . I18N::percentage($tot_sindi_per, 1);
422
-			$chart_title = I18N::translate('Individuals with sources');
423
-
424
-			return '<img src="https://chart.googleapis.com/chart?cht=p3&amp;chd=e:' . $chd . '&amp;chs=' . $size . '&amp;chco=' . $color_from . ',' . $color_to . '&amp;chf=bg,s,ffffff00&amp;chl=' . rawurlencode($chl) . '" width="' . $sizes[0] . '" height="' . $sizes[1] . '" alt="' . $chart_title . '" title="' . $chart_title . '">';
425
-		}
426
-	}
427
-
428
-	/**
429
-	 * Show the total individuals as a percentage.
430
-	 *
431
-	 * @return string
432
-	 */
433
-	public function totalIndividualsPercentage() {
434
-		return $this->getPercentage($this->totalIndividualsQuery(), 'all');
435
-	}
436
-
437
-	/**
438
-	 * Count the total families.
439
-	 *
440
-	 * @return int
441
-	 */
442
-	private function totalFamiliesQuery() {
443
-		return (int) Database::prepare(
444
-			"SELECT COUNT(*) FROM `##families` WHERE f_file = :tree_id"
445
-		)->execute(array(
446
-			'tree_id' => $this->tree->getTreeId(),
447
-		))->fetchOne();
448
-	}
449
-
450
-	/**
451
-	 * Count the total families.
452
-	 *
453
-	 * @return string
454
-	 */
455
-	public function totalFamilies() {
456
-		return I18N::number($this->totalFamiliesQuery());
457
-	}
458
-
459
-	/**
460
-	 * Count the families with source records.
461
-	 *
462
-	 * @return int
463
-	 */
464
-	private function totalFamsWithSourcesQuery() {
465
-		return (int) Database::prepare(
466
-			"SELECT COUNT(DISTINCT f_id)" .
467
-			" FROM `##families` JOIN `##link` ON f_id = l_from AND f_file = l_file" .
468
-			" WHERE l_file = :tree_id AND l_type = 'SOUR'"
469
-		)->execute(array(
470
-			'tree_id' => $this->tree->getTreeId(),
471
-		))->fetchOne();
472
-	}
473
-
474
-	/**
475
-	 * Count the families with with source records.
476
-	 *
477
-	 * @return string
478
-	 */
479
-	public function totalFamsWithSources() {
480
-		return I18N::number($this->totalFamsWithSourcesQuery());
481
-	}
482
-
483
-	/**
484
-	 * Create a chart of individuals with/without sources.
485
-	 *
486
-	 * @param string[] $params
487
-	 *
488
-	 * @return string
489
-	 */
490
-	public function chartFamsWithSources($params = array()) {
491
-		$WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
492
-		$WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
493
-		$WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
494
-		$WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
495
-
496
-		if (isset($params[0]) && $params[0] != '') {
497
-			$size = strtolower($params[0]);
498
-		} else {
499
-			$size = $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y;
500
-		}
501
-		if (isset($params[1]) && $params[1] != '') {
502
-			$color_from = strtolower($params[1]);
503
-		} else {
504
-			$color_from = $WT_STATS_CHART_COLOR1;
505
-		}
506
-		if (isset($params[2]) && $params[2] != '') {
507
-			$color_to = strtolower($params[2]);
508
-		} else {
509
-			$color_to = $WT_STATS_CHART_COLOR2;
510
-		}
511
-		$sizes   = explode('x', $size);
512
-		$tot_fam = $this->totalFamiliesQuery();
513
-		if ($tot_fam == 0) {
514
-			return '';
515
-		} else {
516
-			$tot_sfam_per = round($this->totalFamsWithSourcesQuery() / $tot_fam, 3);
517
-			$chd          = $this->arrayToExtendedEncoding(array(100 - 100 * $tot_sfam_per, 100 * $tot_sfam_per));
518
-			$chl          = I18N::translate('Without sources') . ' - ' . I18N::percentage(1 - $tot_sfam_per, 1) . '|' .
519
-				I18N::translate('With sources') . ' - ' . I18N::percentage($tot_sfam_per, 1);
520
-			$chart_title = I18N::translate('Families with sources');
521
-
522
-			return "<img src=\"https://chart.googleapis.com/chart?cht=p3&amp;chd=e:{$chd}&chs={$size}&amp;chco={$color_from},{$color_to}&amp;chf=bg,s,ffffff00&amp;chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . $chart_title . "\" title=\"" . $chart_title . "\" />";
523
-		}
524
-	}
525
-
526
-	/**
527
-	 * Show the total families as a percentage.
528
-	 *
529
-	 * @return string
530
-	 */
531
-	public function totalFamiliesPercentage() {
532
-		return $this->getPercentage($this->totalFamiliesQuery(), 'all');
533
-	}
534
-
535
-	/**
536
-	 * Count the total number of sources.
537
-	 *
538
-	 * @return int
539
-	 */
540
-	private function totalSourcesQuery() {
541
-		return (int) Database::prepare(
542
-			"SELECT COUNT(*) FROM `##sources` WHERE s_file = :tree_id"
543
-		)->execute(array(
544
-			'tree_id' => $this->tree->getTreeId(),
545
-		))->fetchOne();
546
-	}
547
-
548
-	/**
549
-	 * Count the total number of sources.
550
-	 *
551
-	 * @return string
552
-	 */
553
-	public function totalSources() {
554
-		return I18N::number($this->totalSourcesQuery());
555
-	}
556
-
557
-	/**
558
-	 * Show the number of sources as a percentage.
559
-	 *
560
-	 * @return string
561
-	 */
562
-	public function totalSourcesPercentage() {
563
-		return $this->getPercentage($this->totalSourcesQuery(), 'all');
564
-	}
565
-
566
-	/**
567
-	 * Count the number of notes.
568
-	 *
569
-	 * @return int
570
-	 */
571
-	private function totalNotesQuery() {
572
-		return (int) Database::prepare(
573
-			"SELECT COUNT(*) FROM `##other` WHERE o_type='NOTE' AND o_file = :tree_id"
574
-		)->execute(array(
575
-			'tree_id' => $this->tree->getTreeId(),
576
-		))->fetchOne();
577
-	}
578
-
579
-	/**
580
-	 * Count the number of notes.
581
-	 *
582
-	 * @return string
583
-	 */
584
-	public function totalNotes() {
585
-		return I18N::number($this->totalNotesQuery());
586
-	}
587
-
588
-	/**
589
-	 * Show the number of notes as a percentage.
590
-	 *
591
-	 * @return string
592
-	 */
593
-	public function totalNotesPercentage() {
594
-		return $this->getPercentage($this->totalNotesQuery(), 'all');
595
-	}
596
-
597
-	/**
598
-	 * Count the number of repositories.
599
-	 *
600
-	 * @return int
601
-	 */
602
-	private function totalRepositoriesQuery() {
603
-		return (int) Database::prepare(
604
-			"SELECT COUNT(*) FROM `##other` WHERE o_type='REPO' AND o_file = :tree_id"
605
-		)->execute(array(
606
-			'tree_id' => $this->tree->getTreeId(),
607
-		))->fetchOne();
608
-	}
609
-
610
-	/**
611
-	 * Count the number of repositories
612
-	 *
613
-	 * @return string
614
-	 */
615
-	public function totalRepositories() {
616
-		return I18N::number($this->totalRepositoriesQuery());
617
-	}
618
-
619
-	/**
620
-	 * Show the total number of repositories as a percentage.
621
-	 *
622
-	 * @return string
623
-	 */
624
-	public function totalRepositoriesPercentage() {
625
-		return $this->getPercentage($this->totalRepositoriesQuery(), 'all');
626
-	}
627
-
628
-	/**
629
-	 * Count the surnames.
630
-	 *
631
-	 * @param string[] $params
632
-	 *
633
-	 * @return string
634
-	 */
635
-	public function totalSurnames($params = array()) {
636
-		if ($params) {
637
-			$opt      = 'IN (' . implode(',', array_fill(0, count($params), '?')) . ')';
638
-			$distinct = '';
639
-		} else {
640
-			$opt      = "IS NOT NULL";
641
-			$distinct = 'DISTINCT';
642
-		}
643
-		$params[] = $this->tree->getTreeId();
644
-		$total =
645
-			Database::prepare(
646
-				"SELECT COUNT({$distinct} n_surn COLLATE '" . I18N::collation() . "')" .
647
-				" FROM `##name`" .
648
-				" WHERE n_surn COLLATE '" . I18N::collation() . "' {$opt} AND n_file=?"
649
-			)->execute(
650
-				$params
651
-			)->fetchOne();
652
-
653
-		return I18N::number($total);
654
-	}
655
-
656
-	/**
657
-	 * Count the number of distinct given names, or count the number of
658
-	 * occurrences of a specific name or names.
659
-	 *
660
-	 * @param string[] $params
661
-	 *
662
-	 * @return string
663
-	 */
664
-	public function totalGivennames($params = array()) {
665
-		if ($params) {
666
-			$qs       = implode(',', array_fill(0, count($params), '?'));
667
-			$params[] = $this->tree->getTreeId();
668
-			$total    =
669
-				Database::prepare("SELECT COUNT( n_givn) FROM `##name` WHERE n_givn IN ({$qs}) AND n_file=?")
670
-					->execute($params)
671
-					->fetchOne();
672
-		} else {
673
-			$total =
674
-				Database::prepare("SELECT COUNT(DISTINCT n_givn) FROM `##name` WHERE n_givn IS NOT NULL AND n_file=?")
675
-					->execute(array($this->tree->getTreeId()))
676
-					->fetchOne();
677
-		}
678
-
679
-		return I18N::number($total);
680
-	}
681
-
682
-	/**
683
-	 * Count the number of events (with dates).
684
-	 *
685
-	 * @param string[] $params
686
-	 *
687
-	 * @return string
688
-	 */
689
-	public function totalEvents($params = array()) {
690
-		$sql  = "SELECT COUNT(*) AS tot FROM `##dates` WHERE d_file=?";
691
-		$vars = array($this->tree->getTreeId());
692
-
693
-		$no_types = array('HEAD', 'CHAN');
694
-		if ($params) {
695
-			$types = array();
696
-			foreach ($params as $type) {
697
-				if (substr($type, 0, 1) == '!') {
698
-					$no_types[] = substr($type, 1);
699
-				} else {
700
-					$types[] = $type;
701
-				}
702
-			}
703
-			if ($types) {
704
-				$sql .= ' AND d_fact IN (' . implode(', ', array_fill(0, count($types), '?')) . ')';
705
-				$vars = array_merge($vars, $types);
706
-			}
707
-		}
708
-		$sql .= ' AND d_fact NOT IN (' . implode(', ', array_fill(0, count($no_types), '?')) . ')';
709
-		$vars = array_merge($vars, $no_types);
710
-
711
-		return I18N::number(Database::prepare($sql)->execute($vars)->fetchOne());
712
-	}
713
-
714
-	/**
715
-	 * Count the number of births.
716
-	 *
717
-	 * @return string
718
-	 */
719
-	public function totalEventsBirth() {
720
-		return $this->totalEvents(explode('|', WT_EVENTS_BIRT));
721
-	}
722
-
723
-	/**
724
-	 * Count the number of births.
725
-	 *
726
-	 * @return string
727
-	 */
728
-	public function totalBirths() {
729
-		return $this->totalEvents(array('BIRT'));
730
-	}
731
-
732
-	/**
733
-	 * Count the number of deaths.
734
-	 *
735
-	 * @return string
736
-	 */
737
-	public function totalEventsDeath() {
738
-		return $this->totalEvents(explode('|', WT_EVENTS_DEAT));
739
-	}
740
-
741
-	/**
742
-	 * Count the number of deaths.
743
-	 *
744
-	 * @return string
745
-	 */
746
-	public function totalDeaths() {
747
-		return $this->totalEvents(array('DEAT'));
748
-	}
749
-
750
-	/**
751
-	 * Count the number of marriages.
752
-	 *
753
-	 * @return string
754
-	 */
755
-	public function totalEventsMarriage() {
756
-		return $this->totalEvents(explode('|', WT_EVENTS_MARR));
757
-	}
758
-
759
-	/**
760
-	 * Count the number of marriages.
761
-	 *
762
-	 * @return string
763
-	 */
764
-	public function totalMarriages() {
765
-		return $this->totalEvents(array('MARR'));
766
-	}
767
-
768
-	/**
769
-	 * Count the number of divorces.
770
-	 *
771
-	 * @return string
772
-	 */
773
-	public function totalEventsDivorce() {
774
-		return $this->totalEvents(explode('|', WT_EVENTS_DIV));
775
-	}
776
-
777
-	/**
778
-	 * Count the number of divorces.
779
-	 *
780
-	 * @return string
781
-	 */
782
-	public function totalDivorces() {
783
-		return $this->totalEvents(array('DIV'));
784
-	}
785
-
786
-	/**
787
-	 * Count the number of other events.
788
-	 *
789
-	 * @return string
790
-	 */
791
-	public function totalEventsOther() {
792
-		$facts    = array_merge(explode('|', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT));
793
-		$no_facts = array();
794
-		foreach ($facts as $fact) {
795
-			$fact       = '!' . str_replace('\'', '', $fact);
796
-			$no_facts[] = $fact;
797
-		}
798
-
799
-		return $this->totalEvents($no_facts);
800
-	}
801
-
802
-	/**
803
-	 * Count the number of males.
804
-	 *
805
-	 * @return int
806
-	 */
807
-	private function totalSexMalesQuery() {
808
-		return (int) Database::prepare(
809
-			"SELECT COUNT(*) FROM `##individuals` WHERE i_file = :tree_id AND i_sex = 'M'"
810
-		)->execute(array(
811
-			'tree_id' => $this->tree->getTreeId(),
812
-		))->fetchOne();
813
-	}
814
-
815
-	/**
816
-	 * Count the number of males.
817
-	 *
818
-	 * @return string
819
-	 */
820
-	public function totalSexMales() {
821
-		return I18N::number($this->totalSexMalesQuery());
822
-	}
823
-
824
-	/**
825
-	 * Count the number of males
826
-	 *
827
-	 * @return string
828
-	 */
829
-	public function totalSexMalesPercentage() {
830
-		return $this->getPercentage($this->totalSexMalesQuery(), 'individual');
831
-	}
832
-
833
-	/**
834
-	 * Count the number of females.
835
-	 *
836
-	 * @return int
837
-	 */
838
-	private function totalSexFemalesQuery() {
839
-		return (int) Database::prepare(
840
-			"SELECT COUNT(*) FROM `##individuals` WHERE i_file = :tree_id AND i_sex = 'F'"
841
-		)->execute(array(
842
-			'tree_id' => $this->tree->getTreeId(),
843
-		))->fetchOne();
844
-	}
845
-
846
-	/**
847
-	 * Count the number of females.
848
-	 *
849
-	 * @return string
850
-	 */
851
-	public function totalSexFemales() {
852
-		return I18N::number($this->totalSexFemalesQuery());
853
-	}
854
-
855
-	/**
856
-	 * Count the number of females.
857
-	 *
858
-	 * @return string
859
-	 */
860
-	public function totalSexFemalesPercentage() {
861
-		return $this->getPercentage($this->totalSexFemalesQuery(), 'individual');
862
-	}
863
-
864
-	/**
865
-	 * Count the number of individuals with unknown sex.
866
-	 *
867
-	 * @return int
868
-	 */
869
-	private function totalSexUnknownQuery() {
870
-		return (int) Database::prepare(
871
-			"SELECT COUNT(*) FROM `##individuals` WHERE i_file = :tree_id AND i_sex = 'U'"
872
-		)->execute(array(
873
-			'tree_id' => $this->tree->getTreeId(),
874
-		))->fetchOne();
875
-	}
876
-
877
-	/**
878
-	 * Count the number of individuals with unknown sex.
879
-	 *
880
-	 * @return string
881
-	 */
882
-	public function totalSexUnknown() {
883
-		return I18N::number($this->totalSexUnknownQuery());
884
-	}
885
-
886
-	/**
887
-	 * Count the number of individuals with unknown sex.
888
-	 *
889
-	 * @return string
890
-	 */
891
-	public function totalSexUnknownPercentage() {
892
-		return $this->getPercentage($this->totalSexUnknownQuery(), 'individual');
893
-	}
894
-
895
-	/**
896
-	 * Generate a chart showing sex distribution.
897
-	 *
898
-	 * @param string[] $params
899
-	 *
900
-	 * @return string
901
-	 */
902
-	public function chartSex($params = array()) {
903
-		$WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
904
-		$WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
905
-
906
-		if (isset($params[0]) && $params[0] != '') {
907
-			$size = strtolower($params[0]);
908
-		} else {
909
-			$size = $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y;
910
-		}
911
-		if (isset($params[1]) && $params[1] != '') {
912
-			$color_female = strtolower($params[1]);
913
-		} else {
914
-			$color_female = 'ffd1dc';
915
-		}
916
-		if (isset($params[2]) && $params[2] != '') {
917
-			$color_male = strtolower($params[2]);
918
-		} else {
919
-			$color_male = '84beff';
920
-		}
921
-		if (isset($params[3]) && $params[3] != '') {
922
-			$color_unknown = strtolower($params[3]);
923
-		} else {
924
-			$color_unknown = '777777';
925
-		}
926
-		$sizes = explode('x', $size);
927
-		// Raw data - for calculation
928
-		$tot_f = $this->totalSexFemalesQuery();
929
-		$tot_m = $this->totalSexMalesQuery();
930
-		$tot_u = $this->totalSexUnknownQuery();
931
-		$tot   = $tot_f + $tot_m + $tot_u;
932
-		// I18N data - for display
933
-		$per_f = $this->totalSexFemalesPercentage();
934
-		$per_m = $this->totalSexMalesPercentage();
935
-		$per_u = $this->totalSexUnknownPercentage();
936
-		if ($tot == 0) {
937
-			return '';
938
-		} elseif ($tot_u > 0) {
939
-			$chd = $this->arrayToExtendedEncoding(array(4095 * $tot_u / $tot, 4095 * $tot_f / $tot, 4095 * $tot_m / $tot));
940
-			$chl =
941
-				I18N::translateContext('unknown people', 'Unknown') . ' - ' . $per_u . '|' .
942
-				I18N::translate('Females') . ' - ' . $per_f . '|' .
943
-				I18N::translate('Males') . ' - ' . $per_m;
944
-			$chart_title =
945
-				I18N::translate('Males') . ' - ' . $per_m . I18N::$list_separator .
946
-				I18N::translate('Females') . ' - ' . $per_f . I18N::$list_separator .
947
-				I18N::translateContext('unknown people', 'Unknown') . ' - ' . $per_u;
948
-
949
-			return "<img src=\"https://chart.googleapis.com/chart?cht=p3&amp;chd=e:{$chd}&amp;chs={$size}&amp;chco={$color_unknown},{$color_female},{$color_male}&amp;chf=bg,s,ffffff00&amp;chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . $chart_title . "\" title=\"" . $chart_title . "\" />";
950
-		} else {
951
-			$chd = $this->arrayToExtendedEncoding(array(4095 * $tot_f / $tot, 4095 * $tot_m / $tot));
952
-			$chl =
953
-				I18N::translate('Females') . ' - ' . $per_f . '|' .
954
-				I18N::translate('Males') . ' - ' . $per_m;
955
-			$chart_title = I18N::translate('Males') . ' - ' . $per_m . I18N::$list_separator .
956
-				I18N::translate('Females') . ' - ' . $per_f;
957
-
958
-			return "<img src=\"https://chart.googleapis.com/chart?cht=p3&amp;chd=e:{$chd}&amp;chs={$size}&amp;chco={$color_female},{$color_male}&amp;chf=bg,s,ffffff00&amp;chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . $chart_title . "\" title=\"" . $chart_title . "\" />";
959
-		}
960
-	}
961
-
962
-	/**
963
-	 * Count the number of living individuals.
964
-	 *
965
-	 * The totalLiving/totalDeceased queries assume that every dead person will
966
-	 * have a DEAT record. It will not include individuals who were born more
967
-	 * than MAX_ALIVE_AGE years ago, and who have no DEAT record.
968
-	 * A good reason to run the “Add missing DEAT records” batch-update!
969
-	 *
970
-	 * @return int
971
-	 */
972
-	private function totalLivingQuery() {
973
-		return (int) Database::prepare(
974
-			"SELECT COUNT(*) FROM `##individuals` WHERE i_file = :tree_id AND i_gedcom NOT REGEXP '\\n1 (" . WT_EVENTS_DEAT . ")'"
975
-		)->execute(array(
976
-			'tree_id' => $this->tree->getTreeId(),
977
-		))->fetchOne();
978
-	}
979
-
980
-	/**
981
-	 * Count the number of living individuals.
982
-	 *
983
-	 * @return string
984
-	 */
985
-	public function totalLiving() {
986
-		return I18N::number($this->totalLivingQuery());
987
-	}
988
-
989
-	/**
990
-	 * Count the number of living individuals.
991
-	 *
992
-	 * @return string
993
-	 */
994
-	public function totalLivingPercentage() {
995
-		return $this->getPercentage($this->totalLivingQuery(), 'individual');
996
-	}
997
-
998
-	/**
999
-	 * Count the number of dead individuals.
1000
-	 *
1001
-	 * @return int
1002
-	 */
1003
-	private function totalDeceasedQuery() {
1004
-		return (int) Database::prepare(
1005
-			"SELECT COUNT(*) FROM `##individuals` WHERE i_file = :tree_id AND i_gedcom REGEXP '\\n1 (" . WT_EVENTS_DEAT . ")'"
1006
-		)->execute(array(
1007
-			'tree_id' => $this->tree->getTreeId(),
1008
-		))->fetchOne();
1009
-	}
1010
-
1011
-	/**
1012
-	 * Count the number of dead individuals.
1013
-	 *
1014
-	 * @return string
1015
-	 */
1016
-	public function totalDeceased() {
1017
-		return I18N::number($this->totalDeceasedQuery());
1018
-	}
1019
-
1020
-	/**
1021
-	 * Count the number of dead individuals.
1022
-	 *
1023
-	 * @return string
1024
-	 */
1025
-	public function totalDeceasedPercentage() {
1026
-		return $this->getPercentage($this->totalDeceasedQuery(), 'individual');
1027
-	}
1028
-
1029
-	/**
1030
-	 * Create a chart showing mortality.
1031
-	 *
1032
-	 * @param string[] $params
1033
-	 *
1034
-	 * @return string
1035
-	 */
1036
-	public function chartMortality($params = array()) {
1037
-		$WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
1038
-		$WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
1039
-
1040
-		if (isset($params[0]) && $params[0] != '') {
1041
-			$size = strtolower($params[0]);
1042
-		} else {
1043
-			$size = $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y;
1044
-		}
1045
-		if (isset($params[1]) && $params[1] != '') {
1046
-			$color_living = strtolower($params[1]);
1047
-		} else {
1048
-			$color_living = 'ffffff';
1049
-		}
1050
-		if (isset($params[2]) && $params[2] != '') {
1051
-			$color_dead = strtolower($params[2]);
1052
-		} else {
1053
-			$color_dead = 'cccccc';
1054
-		}
1055
-		$sizes = explode('x', $size);
1056
-		// Raw data - for calculation
1057
-		$tot_l = $this->totalLivingQuery();
1058
-		$tot_d = $this->totalDeceasedQuery();
1059
-		$tot   = $tot_l + $tot_d;
1060
-		// I18N data - for display
1061
-		$per_l = $this->totalLivingPercentage();
1062
-		$per_d = $this->totalDeceasedPercentage();
1063
-		if ($tot == 0) {
1064
-			return '';
1065
-		} else {
1066
-			$chd = $this->arrayToExtendedEncoding(array(4095 * $tot_l / $tot, 4095 * $tot_d / $tot));
1067
-			$chl =
1068
-				I18N::translate('Living') . ' - ' . $per_l . '|' .
1069
-				I18N::translate('Dead') . ' - ' . $per_d . '|';
1070
-			$chart_title = I18N::translate('Living') . ' - ' . $per_l . I18N::$list_separator .
1071
-				I18N::translate('Dead') . ' - ' . $per_d;
1072
-
1073
-			return "<img src=\"https://chart.googleapis.com/chart?cht=p3&amp;chd=e:{$chd}&amp;chs={$size}&amp;chco={$color_living},{$color_dead}&amp;chf=bg,s,ffffff00&amp;chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . $chart_title . "\" title=\"" . $chart_title . "\" />";
1074
-		}
1075
-	}
1076
-
1077
-	/**
1078
-	 * Count the number of users.
1079
-	 *
1080
-	 * @param string[] $params
1081
-	 *
1082
-	 * @return string
1083
-	 */
1084
-	public function totalUsers($params = array()) {
1085
-		if (isset($params[0])) {
1086
-			$total = count(User::all()) + (int) $params[0];
1087
-		} else {
1088
-			$total = count(User::all());
1089
-		}
1090
-
1091
-		return I18N::number($total);
1092
-	}
1093
-
1094
-	/**
1095
-	 * Count the number of administrators.
1096
-	 *
1097
-	 * @return string
1098
-	 */
1099
-	public function totalAdmins() {
1100
-		return I18N::number(count(User::allAdmins()));
1101
-	}
1102
-
1103
-	/**
1104
-	 * Count the number of administrators.
1105
-	 *
1106
-	 * @return string
1107
-	 */
1108
-	public function totalNonAdmins() {
1109
-		return I18N::number(count(User::all()) - count(User::allAdmins()));
1110
-	}
1111
-
1112
-	/**
1113
-	 * Count the number of media records with a given type.
1114
-	 *
1115
-	 * @param string $type
1116
-	 *
1117
-	 * @return int
1118
-	 */
1119
-	private function totalMediaType($type = 'all') {
1120
-		if (!in_array($type, $this->_media_types) && $type != 'all' && $type != 'unknown') {
1121
-			return 0;
1122
-		}
1123
-		$sql  = "SELECT COUNT(*) AS tot FROM `##media` WHERE m_file=?";
1124
-		$vars = array($this->tree->getTreeId());
1125
-
1126
-		if ($type != 'all') {
1127
-			if ($type == 'unknown') {
1128
-				// There has to be a better way then this :(
1129
-				foreach ($this->_media_types as $t) {
1130
-					$sql .= " AND (m_gedcom NOT LIKE ? AND m_gedcom NOT LIKE ?)";
1131
-					$vars[] = "%3 TYPE {$t}%";
1132
-					$vars[] = "%1 _TYPE {$t}%";
1133
-				}
1134
-			} else {
1135
-				$sql .= " AND (m_gedcom LIKE ? OR m_gedcom LIKE ?)";
1136
-				$vars[] = "%3 TYPE {$type}%";
1137
-				$vars[] = "%1 _TYPE {$type}%";
1138
-			}
1139
-		}
1140
-
1141
-		return (int) Database::prepare($sql)->execute($vars)->fetchOne();
1142
-	}
1143
-
1144
-	/**
1145
-	 * Count the number of media records.
1146
-	 *
1147
-	 * @return string
1148
-	 */
1149
-	public function totalMedia() {
1150
-		return I18N::number($this->totalMediaType('all'));
1151
-	}
1152
-
1153
-	/**
1154
-	 * Count the number of media records with type "audio".
1155
-	 *
1156
-	 * @return string
1157
-	 */
1158
-	public function totalMediaAudio() {
1159
-		return I18N::number($this->totalMediaType('audio'));
1160
-	}
1161
-
1162
-	/**
1163
-	 * Count the number of media records with type "book".
1164
-	 *
1165
-	 * @return string
1166
-	 */
1167
-	public function totalMediaBook() {
1168
-		return I18N::number($this->totalMediaType('book'));
1169
-	}
1170
-
1171
-	/**
1172
-	 * Count the number of media records with type "card".
1173
-	 *
1174
-	 * @return string
1175
-	 */
1176
-	public function totalMediaCard() {
1177
-		return I18N::number($this->totalMediaType('card'));
1178
-	}
1179
-
1180
-	/**
1181
-	 * Count the number of media records with type "certificate".
1182
-	 *
1183
-	 * @return string
1184
-	 */
1185
-	public function totalMediaCertificate() {
1186
-		return I18N::number($this->totalMediaType('certificate'));
1187
-	}
1188
-
1189
-	/**
1190
-	 * Count the number of media records with type "coat of arms".
1191
-	 *
1192
-	 * @return string
1193
-	 */
1194
-	public function totalMediaCoatOfArms() {
1195
-		return I18N::number($this->totalMediaType('coat'));
1196
-	}
1197
-
1198
-	/**
1199
-	 * Count the number of media records with type "document".
1200
-	 *
1201
-	 * @return string
1202
-	 */
1203
-	public function totalMediaDocument() {
1204
-		return I18N::number($this->totalMediaType('document'));
1205
-	}
1206
-
1207
-	/**
1208
-	 * Count the number of media records with type "electronic".
1209
-	 *
1210
-	 * @return string
1211
-	 */
1212
-	public function totalMediaElectronic() {
1213
-		return I18N::number($this->totalMediaType('electronic'));
1214
-	}
1215
-
1216
-	/**
1217
-	 * Count the number of media records with type "magazine".
1218
-	 *
1219
-	 * @return string
1220
-	 */
1221
-	public function totalMediaMagazine() {
1222
-		return I18N::number($this->totalMediaType('magazine'));
1223
-	}
1224
-
1225
-	/**
1226
-	 * Count the number of media records with type "manuscript".
1227
-	 *
1228
-	 * @return string
1229
-	 */
1230
-	public function totalMediaManuscript() {
1231
-		return I18N::number($this->totalMediaType('manuscript'));
1232
-	}
1233
-
1234
-	/**
1235
-	 * Count the number of media records with type "map".
1236
-	 *
1237
-	 * @return string
1238
-	 */
1239
-	public function totalMediaMap() {
1240
-		return I18N::number($this->totalMediaType('map'));
1241
-	}
1242
-
1243
-	/**
1244
-	 * Count the number of media records with type "microfiche".
1245
-	 *
1246
-	 * @return string
1247
-	 */
1248
-	public function totalMediaFiche() {
1249
-		return I18N::number($this->totalMediaType('fiche'));
1250
-	}
1251
-
1252
-	/**
1253
-	 * Count the number of media records with type "microfilm".
1254
-	 *
1255
-	 * @return string
1256
-	 */
1257
-	public function totalMediaFilm() {
1258
-		return I18N::number($this->totalMediaType('film'));
1259
-	}
1260
-
1261
-	/**
1262
-	 * Count the number of media records with type "newspaper".
1263
-	 *
1264
-	 * @return string
1265
-	 */
1266
-	public function totalMediaNewspaper() {
1267
-		return I18N::number($this->totalMediaType('newspaper'));
1268
-	}
1269
-
1270
-	/**
1271
-	 * Count the number of media records with type "painting".
1272
-	 *
1273
-	 * @return string
1274
-	 */
1275
-	public function totalMediaPainting() {
1276
-		return I18N::number($this->totalMediaType('painting'));
1277
-	}
1278
-
1279
-	/**
1280
-	 * Count the number of media records with type "photograph".
1281
-	 *
1282
-	 * @return string
1283
-	 */
1284
-	public function totalMediaPhoto() {
1285
-		return I18N::number($this->totalMediaType('photo'));
1286
-	}
1287
-
1288
-	/**
1289
-	 * Count the number of media records with type "tombstone".
1290
-	 *
1291
-	 * @return string
1292
-	 */
1293
-	public function totalMediaTombstone() {
1294
-		return I18N::number($this->totalMediaType('tombstone'));
1295
-	}
1296
-
1297
-	/**
1298
-	 * Count the number of media records with type "video".
1299
-	 *
1300
-	 * @return string
1301
-	 */
1302
-	public function totalMediaVideo() {
1303
-		return I18N::number($this->totalMediaType('video'));
1304
-	}
1305
-
1306
-	/**
1307
-	 * Count the number of media records with type "other".
1308
-	 *
1309
-	 * @return string
1310
-	 */
1311
-	public function totalMediaOther() {
1312
-		return I18N::number($this->totalMediaType('other'));
1313
-	}
1314
-
1315
-	/**
1316
-	 * Count the number of media records with type "unknown".
1317
-	 *
1318
-	 * @return string
1319
-	 */
1320
-	public function totalMediaUnknown() {
1321
-		return I18N::number($this->totalMediaType('unknown'));
1322
-	}
1323
-
1324
-	/**
1325
-	 * Create a chart of media types.
1326
-	 *
1327
-	 * @param string[] $params
1328
-	 *
1329
-	 * @return string
1330
-	 */
1331
-	public function chartMedia($params = array()) {
1332
-		$WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
1333
-		$WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
1334
-		$WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
1335
-		$WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
1336
-
1337
-		if (isset($params[0]) && $params[0] != '') {
1338
-			$size = strtolower($params[0]);
1339
-		} else {
1340
-			$size = $WT_STATS_S_CHART_X . 'x' . $WT_STATS_S_CHART_Y;
1341
-		}
1342
-		if (isset($params[1]) && $params[1] != '') {
1343
-			$color_from = strtolower($params[1]);
1344
-		} else {
1345
-			$color_from = $WT_STATS_CHART_COLOR1;
1346
-		}
1347
-		if (isset($params[2]) && $params[2] != '') {
1348
-			$color_to = strtolower($params[2]);
1349
-		} else {
1350
-			$color_to = $WT_STATS_CHART_COLOR2;
1351
-		}
1352
-		$sizes = explode('x', $size);
1353
-		$tot   = $this->totalMediaType('all');
1354
-		// Beware divide by zero
1355
-		if ($tot == 0) {
1356
-			return I18N::translate('None');
1357
-		}
1358
-		// Build a table listing only the media types actually present in the GEDCOM
1359
-		$mediaCounts = array();
1360
-		$mediaTypes  = "";
1361
-		$chart_title = "";
1362
-		$c           = 0;
1363
-		$max         = 0;
1364
-		$media       = array();
1365
-		foreach ($this->_media_types as $type) {
1366
-			$count = $this->totalMediaType($type);
1367
-			if ($count > 0) {
1368
-				$media[$type] = $count;
1369
-				if ($count > $max) {
1370
-					$max = $count;
1371
-				}
1372
-				$c += $count;
1373
-			}
1374
-		}
1375
-		$count = $this->totalMediaType('unknown');
1376
-		if ($count > 0) {
1377
-			$media['unknown'] = $tot - $c;
1378
-			if ($tot - $c > $max) {
1379
-				$max = $count;
1380
-			}
1381
-		}
1382
-		if (($max / $tot) > 0.6 && count($media) > 10) {
1383
-			arsort($media);
1384
-			$media = array_slice($media, 0, 10);
1385
-			$c     = $tot;
1386
-			foreach ($media as $cm) {
1387
-				$c -= $cm;
1388
-			}
1389
-			if (isset($media['other'])) {
1390
-				$media['other'] += $c;
1391
-			} else {
1392
-				$media['other'] = $c;
1393
-			}
1394
-		}
1395
-		asort($media);
1396
-		foreach ($media as $type => $count) {
1397
-			$mediaCounts[] = round(100 * $count / $tot, 0);
1398
-			$mediaTypes .= GedcomTag::getFileFormTypeValue($type) . ' - ' . I18N::number($count) . '|';
1399
-			$chart_title .= GedcomTag::getFileFormTypeValue($type) . ' (' . $count . '), ';
1400
-		}
1401
-		$chart_title = substr($chart_title, 0, -2);
1402
-		$chd         = $this->arrayToExtendedEncoding($mediaCounts);
1403
-		$chl         = substr($mediaTypes, 0, -1);
1404
-
1405
-		return "<img src=\"https://chart.googleapis.com/chart?cht=p3&amp;chd=e:{$chd}&amp;chs={$size}&amp;chco={$color_from},{$color_to}&amp;chf=bg,s,ffffff00&amp;chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . $chart_title . "\" title=\"" . $chart_title . "\" />";
1406
-	}
1407
-
1408
-	/**
1409
-	 * Birth and Death
1410
-	 *
1411
-	 * @param string $type
1412
-	 * @param string $life_dir
1413
-	 * @param string $birth_death
1414
-	 *
1415
-	 * @return string
1416
-	 */
1417
-	private function mortalityQuery($type = 'full', $life_dir = 'ASC', $birth_death = 'BIRT') {
1418
-		if ($birth_death == 'MARR') {
1419
-			$query_field = "'MARR'";
1420
-		} elseif ($birth_death == 'DIV') {
1421
-			$query_field = "'DIV'";
1422
-		} elseif ($birth_death == 'BIRT') {
1423
-			$query_field = "'BIRT'";
1424
-		} else {
1425
-			$query_field = "'DEAT'";
1426
-		}
1427
-		if ($life_dir == 'ASC') {
1428
-			$dmod = 'MIN';
1429
-		} else {
1430
-			$dmod = 'MAX';
1431
-		}
1432
-		$rows = $this->runSql(
1433
-			"SELECT d_year, d_type, d_fact, d_gid" .
1434
-			" FROM `##dates`" .
1435
-			" WHERE d_file={$this->tree->getTreeId()} AND d_fact IN ({$query_field}) AND d_julianday1=(" .
1436
-			" SELECT {$dmod}( d_julianday1 )" .
1437
-			" FROM `##dates`" .
1438
-			" WHERE d_file={$this->tree->getTreeId()} AND d_fact IN ({$query_field}) AND d_julianday1<>0 )" .
1439
-			" LIMIT 1"
1440
-		);
1441
-		if (!isset($rows[0])) {
1442
-			return '';
1443
-		}
1444
-		$row    = $rows[0];
1445
-		$record = GedcomRecord::getInstance($row['d_gid'], $this->tree);
1446
-		switch ($type) {
1447
-		default:
1448
-		case 'full':
1449
-			if ($record->canShow()) {
1450
-				$result = $record->formatList('span', false, $record->getFullName());
1451
-			} else {
1452
-				$result = I18N::translate('This information is private and cannot be shown.');
1453
-			}
1454
-			break;
1455
-		case 'year':
1456
-			$date   = new Date($row['d_type'] . ' ' . $row['d_year']);
1457
-			$result = $date->display();
1458
-			break;
1459
-		case 'name':
1460
-			$result = "<a href=\"" . $record->getHtmlUrl() . "\">" . $record->getFullName() . "</a>";
1461
-			break;
1462
-		case 'place':
1463
-			$fact = GedcomRecord::getInstance($row['d_gid'], $this->tree)->getFirstFact($row['d_fact']);
1464
-			if ($fact) {
1465
-				$result = FunctionsPrint::formatFactPlace($fact, true, true, true);
1466
-			} else {
1467
-				$result = I18N::translate('Private');
1468
-			}
1469
-			break;
1470
-		}
1471
-
1472
-		return $result;
1473
-	}
1474
-
1475
-	/**
1476
-	 * Places
1477
-	 *
1478
-	 * @param string $what
1479
-	 * @param string $fact
1480
-	 * @param int    $parent
1481
-	 * @param bool   $country
1482
-	 *
1483
-	 * @return int[]|string[][]
1484
-	 */
1485
-	public function statsPlaces($what = 'ALL', $fact = '', $parent = 0, $country = false) {
1486
-		if ($fact) {
1487
-			if ($what == 'INDI') {
1488
-				$rows = Database::prepare(
1489
-					"SELECT i_gedcom AS ged FROM `##individuals` WHERE i_file = :tree_id AND i_gedcom LIKE '%\n2 PLAC %'"
1490
-				)->execute(array(
1491
-					'tree_id' => $this->tree->getTreeId(),
1492
-				))->fetchAll();
1493
-			} elseif ($what == 'FAM') {
1494
-				$rows = Database::prepare(
1495
-					"SELECT f_gedcom AS ged FROM `##families` WHERE f_file = :tree_id AND f_gedcom LIKE '%\n2 PLAC %'"
1496
-				)->execute(array(
1497
-					'tree_id' => $this->tree->getTreeId(),
1498
-				))->fetchAll();
1499
-			}
1500
-			$placelist = array();
1501
-			foreach ($rows as $row) {
1502
-				if (preg_match('/\n1 ' . $fact . '(?:\n[2-9].*)*\n2 PLAC (.+)/', $row->ged, $match)) {
1503
-					if ($country) {
1504
-						$tmp   = explode(Place::GEDCOM_SEPARATOR, $match[1]);
1505
-						$place = end($tmp);
1506
-					} else {
1507
-						$place = $match[1];
1508
-					}
1509
-					if (!isset($placelist[$place])) {
1510
-						$placelist[$place] = 1;
1511
-					} else {
1512
-						$placelist[$place]++;
1513
-					}
1514
-				}
1515
-			}
1516
-
1517
-			return $placelist;
1518
-		} elseif ($parent > 0) {
1519
-			// used by placehierarchy googlemap module
1520
-			if ($what == 'INDI') {
1521
-				$join = " JOIN `##individuals` ON pl_file = i_file AND pl_gid = i_id";
1522
-			} elseif ($what == 'FAM') {
1523
-				$join = " JOIN `##families` ON pl_file = f_file AND pl_gid = f_id";
1524
-			} else {
1525
-				$join = "";
1526
-			}
1527
-			$rows = $this->runSql(
1528
-				" SELECT" .
1529
-				" p_place AS place," .
1530
-				" COUNT(*) AS tot" .
1531
-				" FROM" .
1532
-				" `##places`" .
1533
-				" JOIN `##placelinks` ON pl_file=p_file AND p_id=pl_p_id" .
1534
-				$join .
1535
-				" WHERE" .
1536
-				" p_id={$parent} AND" .
1537
-				" p_file={$this->tree->getTreeId()}" .
1538
-				" GROUP BY place"
1539
-			);
1540
-
1541
-			return $rows;
1542
-		} else {
1543
-			if ($what == 'INDI') {
1544
-				$join = " JOIN `##individuals` ON pl_file = i_file AND pl_gid = i_id";
1545
-			} elseif ($what == 'FAM') {
1546
-				$join = " JOIN `##families` ON pl_file = f_file AND pl_gid = f_id";
1547
-			} else {
1548
-				$join = "";
1549
-			}
1550
-			$rows = $this->runSql(
1551
-				" SELECT" .
1552
-				" p_place AS country," .
1553
-				" COUNT(*) AS tot" .
1554
-				" FROM" .
1555
-				" `##places`" .
1556
-				" JOIN `##placelinks` ON pl_file=p_file AND p_id=pl_p_id" .
1557
-				$join .
1558
-				" WHERE" .
1559
-				" p_file={$this->tree->getTreeId()}" .
1560
-				" AND p_parent_id='0'" .
1561
-				" GROUP BY country ORDER BY tot DESC, country ASC"
1562
-			);
1563
-
1564
-			return $rows;
1565
-		}
1566
-	}
1567
-
1568
-	/**
1569
-	 * Count total places.
1570
-	 *
1571
-	 * @return int
1572
-	 */
1573
-	private function totalPlacesQuery() {
1574
-		return
1575
-			(int) Database::prepare("SELECT COUNT(*) FROM `##places` WHERE p_file=?")
1576
-				->execute(array($this->tree->getTreeId()))
1577
-				->fetchOne();
1578
-	}
1579
-
1580
-	/**
1581
-	 * Count total places.
1582
-	 *
1583
-	 * @return string
1584
-	 */
1585
-	public function totalPlaces() {
1586
-		return I18N::number($this->totalPlacesQuery());
1587
-	}
1588
-
1589
-	/**
1590
-	 * Create a chart showing where events occurred.
1591
-	 *
1592
-	 * @param string[] $params
1593
-	 *
1594
-	 * @return string
1595
-	 */
1596
-	public function chartDistribution($params = array()) {
1597
-		$WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
1598
-		$WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
1599
-		$WT_STATS_CHART_COLOR3 = Theme::theme()->parameter('distribution-chart-low-values');
1600
-		$WT_STATS_MAP_X        = Theme::theme()->parameter('distribution-chart-x');
1601
-		$WT_STATS_MAP_Y        = Theme::theme()->parameter('distribution-chart-y');
1602
-
1603
-		if (isset($params[0])) {
1604
-			$chart_shows = $params[0];
1605
-		} else {
1606
-			$chart_shows = 'world';
1607
-		}
1608
-		if (isset($params[1])) {
1609
-			$chart_type = $params[1];
1610
-		} else {
1611
-			$chart_type = '';
1612
-		}
1613
-		if (isset($params[2])) {
1614
-			$surname = $params[2];
1615
-		} else {
1616
-			$surname = '';
1617
-		}
1618
-
1619
-		if ($this->totalPlacesQuery() == 0) {
1620
-			return '';
1621
-		}
1622
-		// Get the country names for each language
1623
-		$country_to_iso3166 = array();
1624
-		foreach (I18N::activeLocales() as $locale) {
1625
-			I18N::init($locale->languageTag());
1626
-			$countries = $this->getAllCountries();
1627
-			foreach ($this->iso3166() as $three => $two) {
1628
-				$country_to_iso3166[$three]             = $two;
1629
-				$country_to_iso3166[$countries[$three]] = $two;
1630
-			}
1631
-		}
1632
-		I18N::init(WT_LOCALE);
1633
-		switch ($chart_type) {
1634
-		case 'surname_distribution_chart':
1635
-			if ($surname == "") {
1636
-				$surname = $this->getCommonSurname();
1637
-			}
1638
-			$chart_title = I18N::translate('Surname distribution chart') . ': ' . $surname;
1639
-			// Count how many people are events in each country
1640
-			$surn_countries = array();
1641
-			$indis          = QueryName::individuals($this->tree, I18N::strtoupper($surname), '', '', false, false);
1642
-			foreach ($indis as $person) {
1643
-				if (preg_match_all('/^2 PLAC (?:.*, *)*(.*)/m', $person->getGedcom(), $matches)) {
1644
-					// webtrees uses 3 letter country codes and localised country names, but google uses 2 letter codes.
1645
-					foreach ($matches[1] as $country) {
1646
-						if (array_key_exists($country, $country_to_iso3166)) {
1647
-							if (array_key_exists($country_to_iso3166[$country], $surn_countries)) {
1648
-								$surn_countries[$country_to_iso3166[$country]]++;
1649
-							} else {
1650
-								$surn_countries[$country_to_iso3166[$country]] = 1;
1651
-							}
1652
-						}
1653
-					}
1654
-				}
1655
-			};
1656
-			break;
1657
-		case 'birth_distribution_chart':
1658
-			$chart_title = I18N::translate('Birth by country');
1659
-			// Count how many people were born in each country
1660
-			$surn_countries = array();
1661
-			$b_countries    = $this->statsPlaces('INDI', 'BIRT', 0, true);
1662
-			foreach ($b_countries as $place => $count) {
1663
-				$country = $place;
1664
-				if (array_key_exists($country, $country_to_iso3166)) {
1665
-					if (!isset($surn_countries[$country_to_iso3166[$country]])) {
1666
-						$surn_countries[$country_to_iso3166[$country]] = $count;
1667
-					} else {
1668
-						$surn_countries[$country_to_iso3166[$country]] += $count;
1669
-					}
1670
-				}
1671
-			}
1672
-			break;
1673
-		case 'death_distribution_chart':
1674
-			$chart_title = I18N::translate('Death by country');
1675
-			// Count how many people were death in each country
1676
-			$surn_countries = array();
1677
-			$d_countries    = $this->statsPlaces('INDI', 'DEAT', 0, true);
1678
-			foreach ($d_countries as $place => $count) {
1679
-				$country = $place;
1680
-				if (array_key_exists($country, $country_to_iso3166)) {
1681
-					if (!isset($surn_countries[$country_to_iso3166[$country]])) {
1682
-						$surn_countries[$country_to_iso3166[$country]] = $count;
1683
-					} else {
1684
-						$surn_countries[$country_to_iso3166[$country]] += $count;
1685
-					}
1686
-				}
1687
-			}
1688
-			break;
1689
-		case 'marriage_distribution_chart':
1690
-			$chart_title = I18N::translate('Marriage by country');
1691
-			// Count how many families got marriage in each country
1692
-			$surn_countries = array();
1693
-			$m_countries    = $this->statsPlaces('FAM');
1694
-			// webtrees uses 3 letter country codes and localised country names, but google uses 2 letter codes.
1695
-			foreach ($m_countries as $place) {
1696
-				$country = $place['country'];
1697
-				if (array_key_exists($country, $country_to_iso3166)) {
1698
-					if (!isset($surn_countries[$country_to_iso3166[$country]])) {
1699
-						$surn_countries[$country_to_iso3166[$country]] = $place['tot'];
1700
-					} else {
1701
-						$surn_countries[$country_to_iso3166[$country]] += $place['tot'];
1702
-					}
1703
-				}
1704
-			}
1705
-			break;
1706
-		case 'indi_distribution_chart':
1707
-		default:
1708
-			$chart_title = I18N::translate('Individual distribution chart');
1709
-			// Count how many people have events in each country
1710
-			$surn_countries = array();
1711
-			$a_countries    = $this->statsPlaces('INDI');
1712
-			// webtrees uses 3 letter country codes and localised country names, but google uses 2 letter codes.
1713
-			foreach ($a_countries as $place) {
1714
-				$country = $place['country'];
1715
-				if (array_key_exists($country, $country_to_iso3166)) {
1716
-					if (!isset($surn_countries[$country_to_iso3166[$country]])) {
1717
-						$surn_countries[$country_to_iso3166[$country]] = $place['tot'];
1718
-					} else {
1719
-						$surn_countries[$country_to_iso3166[$country]] += $place['tot'];
1720
-					}
1721
-				}
1722
-			}
1723
-			break;
1724
-		}
1725
-		$chart_url = "https://chart.googleapis.com/chart?cht=t&amp;chtm=" . $chart_shows;
1726
-		$chart_url .= "&amp;chco=" . $WT_STATS_CHART_COLOR1 . "," . $WT_STATS_CHART_COLOR3 . "," . $WT_STATS_CHART_COLOR2; // country colours
1727
-		$chart_url .= "&amp;chf=bg,s,ECF5FF"; // sea colour
1728
-		$chart_url .= "&amp;chs=" . $WT_STATS_MAP_X . "x" . $WT_STATS_MAP_Y;
1729
-		$chart_url .= "&amp;chld=" . implode('', array_keys($surn_countries)) . "&amp;chd=s:";
1730
-		foreach ($surn_countries as $count) {
1731
-			$chart_url .= substr(WT_GOOGLE_CHART_ENCODING, (int) ($count / max($surn_countries) * 61), 1);
1732
-		}
1733
-		$chart = '<div id="google_charts" class="center">';
1734
-		$chart .= '<p>' . $chart_title . '</p>';
1735
-		$chart .= '<div><img src="' . $chart_url . '" alt="' . $chart_title . '" title="' . $chart_title . '" class="gchart" /><br>';
1736
-		$chart .= '<table class="center"><tr>';
1737
-		$chart .= '<td bgcolor="#' . $WT_STATS_CHART_COLOR2 . '" width="12"></td><td>' . I18N::translate('Highest population') . '</td>';
1738
-		$chart .= '<td bgcolor="#' . $WT_STATS_CHART_COLOR3 . '" width="12"></td><td>' . I18N::translate('Lowest population') . '</td>';
1739
-		$chart .= '<td bgcolor="#' . $WT_STATS_CHART_COLOR1 . '" width="12"></td><td>' . I18N::translate('Nobody at all') . '</td>';
1740
-		$chart .= '</tr></table></div></div>';
1741
-
1742
-		return $chart;
1743
-	}
1744
-
1745
-	/**
1746
-	 * A list of common countries.
1747
-	 *
1748
-	 * @return string
1749
-	 */
1750
-	public function commonCountriesList() {
1751
-		$countries = $this->statsPlaces();
1752
-		if (empty($countries)) {
1753
-			return '';
1754
-		}
1755
-		$top10 = array();
1756
-		$i     = 1;
1757
-		// Get the country names for each language
1758
-		$country_names = array();
1759
-		foreach (I18N::activeLocales() as $locale) {
1760
-			I18N::init($locale->languageTag());
1761
-			$all_countries = $this->getAllCountries();
1762
-			foreach ($all_countries as $country_code => $country_name) {
1763
-				$country_names[$country_name] = $country_code;
1764
-			}
1765
-		}
1766
-		I18N::init(WT_LOCALE);
1767
-		$all_db_countries = array();
1768
-		foreach ($countries as $place) {
1769
-			$country = trim($place['country']);
1770
-			if (array_key_exists($country, $country_names)) {
1771
-				if (!isset($all_db_countries[$country_names[$country]][$country])) {
1772
-					$all_db_countries[$country_names[$country]][$country] = (int) $place['tot'];
1773
-				} else {
1774
-					$all_db_countries[$country_names[$country]][$country] += (int) $place['tot'];
1775
-				}
1776
-			}
1777
-		}
1778
-		// get all the user’s countries names
1779
-		$all_countries = $this->getAllCountries();
1780
-		foreach ($all_db_countries as $country_code => $country) {
1781
-			$top10[] = '<li>';
1782
-			foreach ($country as $country_name => $tot) {
1783
-				$tmp   = new Place($country_name, $this->tree);
1784
-				$place = '<a href="' . $tmp->getURL() . '" class="list_item">' . $all_countries[$country_code] . '</a>';
1785
-				$top10[] .= $place . ' - ' . I18N::number($tot);
1786
-			}
1787
-			$top10[] .= '</li>';
1788
-			if ($i++ == 10) {
1789
-				break;
1790
-			}
1791
-		}
1792
-		$top10 = implode('', $top10);
1793
-
1794
-		return '<ul>' . $top10 . '</ul>';
1795
-	}
1796
-
1797
-	/**
1798
-	 * A list of common birth places.
1799
-	 *
1800
-	 * @return string
1801
-	 */
1802
-	public function commonBirthPlacesList() {
1803
-		$places = $this->statsPlaces('INDI', 'BIRT');
1804
-		$top10  = array();
1805
-		$i      = 1;
1806
-		arsort($places);
1807
-		foreach ($places as $place => $count) {
1808
-			$tmp     = new Place($place, $this->tree);
1809
-			$place   = '<a href="' . $tmp->getURL() . '" class="list_item">' . $tmp->getFullName() . '</a>';
1810
-			$top10[] = '<li>' . $place . ' - ' . I18N::number($count) . '</li>';
1811
-			if ($i++ == 10) {
1812
-				break;
1813
-			}
1814
-		}
1815
-		$top10 = implode('', $top10);
1816
-
1817
-		return '<ul>' . $top10 . '</ul>';
1818
-	}
1819
-
1820
-	/**
1821
-	 * A list of common death places.
1822
-	 *
1823
-	 * @return string
1824
-	 */
1825
-	public function commonDeathPlacesList() {
1826
-		$places = $this->statsPlaces('INDI', 'DEAT');
1827
-		$top10  = array();
1828
-		$i      = 1;
1829
-		arsort($places);
1830
-		foreach ($places as $place => $count) {
1831
-			$tmp     = new Place($place, $this->tree);
1832
-			$place   = '<a href="' . $tmp->getURL() . '" class="list_item">' . $tmp->getFullName() . '</a>';
1833
-			$top10[] = '<li>' . $place . ' - ' . I18N::number($count) . '</li>';
1834
-			if ($i++ == 10) {
1835
-				break;
1836
-			}
1837
-		}
1838
-		$top10 = implode('', $top10);
1839
-
1840
-		return '<ul>' . $top10 . '</ul>';
1841
-	}
1842
-
1843
-	/**
1844
-	 * A list of common marriage places.
1845
-	 *
1846
-	 * @return string
1847
-	 */
1848
-	public function commonMarriagePlacesList() {
1849
-		$places = $this->statsPlaces('FAM', 'MARR');
1850
-		$top10  = array();
1851
-		$i      = 1;
1852
-		arsort($places);
1853
-		foreach ($places as $place => $count) {
1854
-			$tmp     = new Place($place, $this->tree);
1855
-			$place   = '<a href="' . $tmp->getURL() . '" class="list_item">' . $tmp->getFullName() . '</a>';
1856
-			$top10[] = '<li>' . $place . ' - ' . I18N::number($count) . '</li>';
1857
-			if ($i++ == 10) {
1858
-				break;
1859
-			}
1860
-		}
1861
-		$top10 = implode('', $top10);
1862
-
1863
-		return '<ul>' . $top10 . '</ul>';
1864
-	}
1865
-
1866
-	/**
1867
-	 * Create a chart of birth places.
1868
-	 *
1869
-	 * @param bool     $simple
1870
-	 * @param bool     $sex
1871
-	 * @param int      $year1
1872
-	 * @param int      $year2
1873
-	 * @param string[] $params
1874
-	 *
1875
-	 * @return array|string
1876
-	 */
1877
-	public function statsBirthQuery($simple = true, $sex = false, $year1 = -1, $year2 = -1, $params = array()) {
1878
-		$WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
1879
-		$WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
1880
-		$WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
1881
-		$WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
1882
-
1883
-		if ($simple) {
1884
-			$sql =
1885
-				"SELECT FLOOR(d_year/100+1) AS century, COUNT(*) AS total FROM `##dates` " .
1886
-				"WHERE " .
1887
-				"d_file={$this->tree->getTreeId()} AND " .
1888
-				"d_year<>0 AND " .
1889
-				"d_fact='BIRT' AND " .
1890
-				"d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')";
1891
-		} elseif ($sex) {
1892
-			$sql =
1893
-				"SELECT d_month, i_sex, COUNT(*) AS total FROM `##dates` " .
1894
-				"JOIN `##individuals` ON d_file = i_file AND d_gid = i_id " .
1895
-				"WHERE " .
1896
-				"d_file={$this->tree->getTreeId()} AND " .
1897
-				"d_fact='BIRT' AND " .
1898
-				"d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')";
1899
-		} else {
1900
-			$sql =
1901
-				"SELECT d_month, COUNT(*) AS total FROM `##dates` " .
1902
-				"WHERE " .
1903
-				"d_file={$this->tree->getTreeId()} AND " .
1904
-				"d_fact='BIRT' AND " .
1905
-				"d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')";
1906
-		}
1907
-		if ($year1 >= 0 && $year2 >= 0) {
1908
-			$sql .= " AND d_year BETWEEN '{$year1}' AND '{$year2}'";
1909
-		}
1910
-		if ($simple) {
1911
-			$sql .= " GROUP BY century ORDER BY century";
1912
-		} else {
1913
-			$sql .= " GROUP BY d_month";
1914
-			if ($sex) {
1915
-				$sql .= ", i_sex";
1916
-			}
1917
-		}
1918
-		$rows = $this->runSql($sql);
1919
-		if ($simple) {
1920
-			if (isset($params[0]) && $params[0] != '') {
1921
-				$size = strtolower($params[0]);
1922
-			} else {
1923
-				$size = $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y;
1924
-			}
1925
-			if (isset($params[1]) && $params[1] != '') {
1926
-				$color_from = strtolower($params[1]);
1927
-			} else {
1928
-				$color_from = $WT_STATS_CHART_COLOR1;
1929
-			}
1930
-			if (isset($params[2]) && $params[2] != '') {
1931
-				$color_to = strtolower($params[2]);
1932
-			} else {
1933
-				$color_to = $WT_STATS_CHART_COLOR2;
1934
-			}
1935
-			$sizes = explode('x', $size);
1936
-			$tot   = 0;
1937
-			foreach ($rows as $values) {
1938
-				$tot += $values['total'];
1939
-			}
1940
-			// Beware divide by zero
1941
-			if ($tot == 0) {
1942
-				return '';
1943
-			}
1944
-			$centuries = "";
1945
-			$counts    = array();
1946
-			foreach ($rows as $values) {
1947
-				$counts[] = round(100 * $values['total'] / $tot, 0);
1948
-				$centuries .= $this->centuryName($values['century']) . ' - ' . I18N::number($values['total']) . '|';
1949
-			}
1950
-			$chd = $this->arrayToExtendedEncoding($counts);
1951
-			$chl = rawurlencode(substr($centuries, 0, -1));
1952
-
1953
-			return "<img src=\"https://chart.googleapis.com/chart?cht=p3&amp;chd=e:{$chd}&amp;chs={$size}&amp;chco={$color_from},{$color_to}&amp;chf=bg,s,ffffff00&amp;chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Births by century') . "\" title=\"" . I18N::translate('Births by century') . "\" />";
1954
-		} else {
1955
-			return $rows;
1956
-		}
1957
-	}
1958
-
1959
-	/**
1960
-	 * Create a chart of death places.
1961
-	 *
1962
-	 * @param bool     $simple
1963
-	 * @param bool     $sex
1964
-	 * @param int      $year1
1965
-	 * @param int      $year2
1966
-	 * @param string[] $params
1967
-	 *
1968
-	 * @return array|string
1969
-	 */
1970
-	public function statsDeathQuery($simple = true, $sex = false, $year1 = -1, $year2 = -1, $params = array()) {
1971
-		$WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
1972
-		$WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
1973
-		$WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
1974
-		$WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
1975
-
1976
-		if ($simple) {
1977
-			$sql =
1978
-				"SELECT FLOOR(d_year/100+1) AS century, COUNT(*) AS total FROM `##dates` " .
1979
-				"WHERE " .
1980
-				"d_file={$this->tree->getTreeId()} AND " .
1981
-				'd_year<>0 AND ' .
1982
-				"d_fact='DEAT' AND " .
1983
-				"d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')";
1984
-		} elseif ($sex) {
1985
-			$sql =
1986
-				"SELECT d_month, i_sex, COUNT(*) AS total FROM `##dates` " .
1987
-				"JOIN `##individuals` ON d_file = i_file AND d_gid = i_id " .
1988
-				"WHERE " .
1989
-				"d_file={$this->tree->getTreeId()} AND " .
1990
-				"d_fact='DEAT' AND " .
1991
-				"d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')";
1992
-		} else {
1993
-			$sql =
1994
-				"SELECT d_month, COUNT(*) AS total FROM `##dates` " .
1995
-				"WHERE " .
1996
-				"d_file={$this->tree->getTreeId()} AND " .
1997
-				"d_fact='DEAT' AND " .
1998
-				"d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')";
1999
-		}
2000
-		if ($year1 >= 0 && $year2 >= 0) {
2001
-			$sql .= " AND d_year BETWEEN '{$year1}' AND '{$year2}'";
2002
-		}
2003
-		if ($simple) {
2004
-			$sql .= " GROUP BY century ORDER BY century";
2005
-		} else {
2006
-			$sql .= " GROUP BY d_month";
2007
-			if ($sex) {
2008
-				$sql .= ", i_sex";
2009
-			}
2010
-		}
2011
-		$rows = $this->runSql($sql);
2012
-		if ($simple) {
2013
-			if (isset($params[0]) && $params[0] != '') {
2014
-				$size = strtolower($params[0]);
2015
-			} else {
2016
-				$size = $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y;
2017
-			}
2018
-			if (isset($params[1]) && $params[1] != '') {
2019
-				$color_from = strtolower($params[1]);
2020
-			} else {
2021
-				$color_from = $WT_STATS_CHART_COLOR1;
2022
-			}
2023
-			if (isset($params[2]) && $params[2] != '') {
2024
-				$color_to = strtolower($params[2]);
2025
-			} else {
2026
-				$color_to = $WT_STATS_CHART_COLOR2;
2027
-			}
2028
-			$sizes = explode('x', $size);
2029
-			$tot   = 0;
2030
-			foreach ($rows as $values) {
2031
-				$tot += $values['total'];
2032
-			}
2033
-			// Beware divide by zero
2034
-			if ($tot == 0) {
2035
-				return '';
2036
-			}
2037
-			$centuries = "";
2038
-			$counts    = array();
2039
-			foreach ($rows as $values) {
2040
-				$counts[] = round(100 * $values['total'] / $tot, 0);
2041
-				$centuries .= $this->centuryName($values['century']) . ' - ' . I18N::number($values['total']) . '|';
2042
-			}
2043
-			$chd = $this->arrayToExtendedEncoding($counts);
2044
-			$chl = rawurlencode(substr($centuries, 0, -1));
2045
-
2046
-			return "<img src=\"https://chart.googleapis.com/chart?cht=p3&amp;chd=e:{$chd}&amp;chs={$size}&amp;chco={$color_from},{$color_to}&amp;chf=bg,s,ffffff00&amp;chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Deaths by century') . "\" title=\"" . I18N::translate('Deaths by century') . "\" />";
2047
-		}
2048
-
2049
-		return $rows;
2050
-	}
2051
-
2052
-	/**
2053
-	 * Find the earliest birth.
2054
-	 *
2055
-	 * @return string
2056
-	 */
2057
-	public function firstBirth() {
2058
-		return $this->mortalityQuery('full', 'ASC', 'BIRT');
2059
-	}
2060
-
2061
-	/**
2062
-	 * Find the earliest birth year.
2063
-	 *
2064
-	 * @return string
2065
-	 */
2066
-	public function firstBirthYear() {
2067
-		return $this->mortalityQuery('year', 'ASC', 'BIRT');
2068
-	}
2069
-
2070
-	/**
2071
-	 * Find the name of the earliest birth.
2072
-	 *
2073
-	 * @return string
2074
-	 */
2075
-	public function firstBirthName() {
2076
-		return $this->mortalityQuery('name', 'ASC', 'BIRT');
2077
-	}
2078
-
2079
-	/**
2080
-	 * Find the earliest birth place.
2081
-	 *
2082
-	 * @return string
2083
-	 */
2084
-	public function firstBirthPlace() {
2085
-		return $this->mortalityQuery('place', 'ASC', 'BIRT');
2086
-	}
2087
-
2088
-	/**
2089
-	 * Find the latest birth.
2090
-	 *
2091
-	 * @return string
2092
-	 */
2093
-	public function lastBirth() {
2094
-		return $this->mortalityQuery('full', 'DESC', 'BIRT');
2095
-	}
2096
-
2097
-	/**
2098
-	 * Find the latest birth year.
2099
-	 *
2100
-	 * @return string
2101
-	 */
2102
-	public function lastBirthYear() {
2103
-		return $this->mortalityQuery('year', 'DESC', 'BIRT');
2104
-	}
2105
-
2106
-	/**
2107
-	 * Find the latest birth name.
2108
-	 *
2109
-	 * @return string
2110
-	 */
2111
-	public function lastBirthName() {
2112
-		return $this->mortalityQuery('name', 'DESC', 'BIRT');
2113
-	}
2114
-
2115
-	/**
2116
-	 * Find the latest birth place.
2117
-	 *
2118
-	 * @return string
2119
-	 */
2120
-	public function lastBirthPlace() {
2121
-		return $this->mortalityQuery('place', 'DESC', 'BIRT');
2122
-	}
2123
-
2124
-	/**
2125
-	 * General query on births.
2126
-	 *
2127
-	 * @param string[] $params
2128
-	 *
2129
-	 * @return string
2130
-	 */
2131
-	public function statsBirth($params = array()) {
2132
-		return $this->statsBirthQuery(true, false, -1, -1, $params);
2133
-	}
2134
-
2135
-	/**
2136
-	 * Find the earliest death.
2137
-	 *
2138
-	 * @return string
2139
-	 */
2140
-	public function firstDeath() {
2141
-		return $this->mortalityQuery('full', 'ASC', 'DEAT');
2142
-	}
2143
-
2144
-	/**
2145
-	 * Find the earliest death year.
2146
-	 *
2147
-	 * @return string
2148
-	 */
2149
-	public function firstDeathYear() {
2150
-		return $this->mortalityQuery('year', 'ASC', 'DEAT');
2151
-	}
2152
-
2153
-	/**
2154
-	 * Find the earliest death name.
2155
-	 *
2156
-	 * @return string
2157
-	 */
2158
-	public function firstDeathName() {
2159
-		return $this->mortalityQuery('name', 'ASC', 'DEAT');
2160
-	}
2161
-
2162
-	/**
2163
-	 * Find the earliest death place.
2164
-	 *
2165
-	 * @return string
2166
-	 */
2167
-	public function firstDeathPlace() {
2168
-		return $this->mortalityQuery('place', 'ASC', 'DEAT');
2169
-	}
2170
-
2171
-	/**
2172
-	 * Find the latest death.
2173
-	 *
2174
-	 * @return string
2175
-	 */
2176
-	public function lastDeath() {
2177
-		return $this->mortalityQuery('full', 'DESC', 'DEAT');
2178
-	}
2179
-
2180
-	/**
2181
-	 * Find the latest death year.
2182
-	 *
2183
-	 * @return string
2184
-	 */
2185
-	public function lastDeathYear() {
2186
-		return $this->mortalityQuery('year', 'DESC', 'DEAT');
2187
-	}
2188
-
2189
-	/**
2190
-	 * Find the latest death name.
2191
-	 *
2192
-	 * @return string
2193
-	 */
2194
-	public function lastDeathName() {
2195
-		return $this->mortalityQuery('name', 'DESC', 'DEAT');
2196
-	}
2197
-
2198
-	/**
2199
-	 * Find the place of the latest death.
2200
-	 *
2201
-	 * @return string
2202
-	 */
2203
-	public function lastDeathPlace() {
2204
-		return $this->mortalityQuery('place', 'DESC', 'DEAT');
2205
-	}
2206
-
2207
-	/**
2208
-	 * General query on deaths.
2209
-	 *
2210
-	 * @param string[] $params
2211
-	 *
2212
-	 * @return string
2213
-	 */
2214
-	public function statsDeath($params = array()) {
2215
-		return $this->statsDeathQuery(true, false, -1, -1, $params);
2216
-	}
2217
-
2218
-	/**
2219
-	 * Lifespan
2220
-	 *
2221
-	 * @param string $type
2222
-	 * @param string $sex
2223
-	 *
2224
-	 * @return string
2225
-	 */
2226
-	private function longlifeQuery($type = 'full', $sex = 'F') {
2227
-		$sex_search = ' 1=1';
2228
-		if ($sex == 'F') {
2229
-			$sex_search = " i_sex='F'";
2230
-		} elseif ($sex == 'M') {
2231
-			$sex_search = " i_sex='M'";
2232
-		}
2233
-
2234
-		$rows = $this->runSql(
2235
-			" SELECT" .
2236
-			" death.d_gid AS id," .
2237
-			" death.d_julianday2-birth.d_julianday1 AS age" .
2238
-			" FROM" .
2239
-			" `##dates` AS death," .
2240
-			" `##dates` AS birth," .
2241
-			" `##individuals` AS indi" .
2242
-			" WHERE" .
2243
-			" indi.i_id=birth.d_gid AND" .
2244
-			" birth.d_gid=death.d_gid AND" .
2245
-			" death.d_file={$this->tree->getTreeId()} AND" .
2246
-			" birth.d_file=death.d_file AND" .
2247
-			" birth.d_file=indi.i_file AND" .
2248
-			" birth.d_fact='BIRT' AND" .
2249
-			" death.d_fact='DEAT' AND" .
2250
-			" birth.d_julianday1<>0 AND" .
2251
-			" death.d_julianday1>birth.d_julianday2 AND" .
2252
-			$sex_search .
2253
-			" ORDER BY" .
2254
-			" age DESC LIMIT 1"
2255
-		);
2256
-		if (!isset($rows[0])) {
2257
-			return '';
2258
-		}
2259
-		$row    = $rows[0];
2260
-		$person = Individual::getInstance($row['id'], $this->tree);
2261
-		switch ($type) {
2262
-		default:
2263
-		case 'full':
2264
-			if ($person->canShowName()) {
2265
-				$result = $person->formatList('span', false, $person->getFullName());
2266
-			} else {
2267
-				$result = I18N::translate('This information is private and cannot be shown.');
2268
-			}
2269
-			break;
2270
-		case 'age':
2271
-			$result = I18N::number((int) ($row['age'] / 365.25));
2272
-			break;
2273
-		case 'name':
2274
-			$result = "<a href=\"" . $person->getHtmlUrl() . "\">" . $person->getFullName() . "</a>";
2275
-			break;
2276
-		}
2277
-
2278
-		return $result;
2279
-	}
2280
-
2281
-	/**
2282
-	 * Find the oldest individuals.
2283
-	 *
2284
-	 * @param string   $type
2285
-	 * @param string   $sex
2286
-	 * @param string[] $params
2287
-	 *
2288
-	 * @return string
2289
-	 */
2290
-	private function topTenOldestQuery($type = 'list', $sex = 'BOTH', $params = array()) {
2291
-		if ($sex === 'F') {
2292
-			$sex_search = " AND i_sex='F' ";
2293
-		} elseif ($sex === 'M') {
2294
-			$sex_search = " AND i_sex='M' ";
2295
-		} else {
2296
-			$sex_search = '';
2297
-		}
2298
-		if (isset($params[0])) {
2299
-			$total = (int) $params[0];
2300
-		} else {
2301
-			$total = 10;
2302
-		}
2303
-		$rows = $this->runSql(
2304
-			"SELECT " .
2305
-			" MAX(death.d_julianday2-birth.d_julianday1) AS age, " .
2306
-			" death.d_gid AS deathdate " .
2307
-			"FROM " .
2308
-			" `##dates` AS death, " .
2309
-			" `##dates` AS birth, " .
2310
-			" `##individuals` AS indi " .
2311
-			"WHERE " .
2312
-			" indi.i_id=birth.d_gid AND " .
2313
-			" birth.d_gid=death.d_gid AND " .
2314
-			" death.d_file={$this->tree->getTreeId()} AND " .
2315
-			" birth.d_file=death.d_file AND " .
2316
-			" birth.d_file=indi.i_file AND " .
2317
-			" birth.d_fact='BIRT' AND " .
2318
-			" death.d_fact='DEAT' AND " .
2319
-			" birth.d_julianday1<>0 AND " .
2320
-			" death.d_julianday1>birth.d_julianday2 " .
2321
-			$sex_search .
2322
-			"GROUP BY deathdate " .
2323
-			"ORDER BY age DESC " .
2324
-			"LIMIT " . $total
2325
-		);
2326
-		if (!isset($rows[0])) {
2327
-			return '';
2328
-		}
2329
-		$top10 = array();
2330
-		foreach ($rows as $row) {
2331
-			$person = Individual::getInstance($row['deathdate'], $this->tree);
2332
-			$age    = $row['age'];
2333
-			if ((int) ($age / 365.25) > 0) {
2334
-				$age = (int) ($age / 365.25) . 'y';
2335
-			} elseif ((int) ($age / 30.4375) > 0) {
2336
-				$age = (int) ($age / 30.4375) . 'm';
2337
-			} else {
2338
-				$age = $age . 'd';
2339
-			}
2340
-			$age = FunctionsDate::getAgeAtEvent($age);
2341
-			if ($person->canShow()) {
2342
-				if ($type == 'list') {
2343
-					$top10[] = "<li><a href=\"" . $person->getHtmlUrl() . "\">" . $person->getFullName() . "</a> (" . $age . ")" . "</li>";
2344
-				} else {
2345
-					$top10[] = "<a href=\"" . $person->getHtmlUrl() . "\">" . $person->getFullName() . "</a> (" . $age . ")";
2346
-				}
2347
-			}
2348
-		}
2349
-		if ($type == 'list') {
2350
-			$top10 = implode('', $top10);
2351
-		} else {
2352
-			$top10 = implode(' ', $top10);
2353
-		}
2354
-		if (I18N::direction() === 'rtl') {
2355
-			$top10 = str_replace(array('[', ']', '(', ')', '+'), array('&rlm;[', '&rlm;]', '&rlm;(', '&rlm;)', '&rlm;+'), $top10);
2356
-		}
2357
-		if ($type == 'list') {
2358
-			return '<ul>' . $top10 . '</ul>';
2359
-		}
2360
-
2361
-		return $top10;
2362
-	}
2363
-
2364
-	/**
2365
-	 * Find the oldest living individuals.
2366
-	 *
2367
-	 * @param string   $type
2368
-	 * @param string   $sex
2369
-	 * @param string[] $params
2370
-	 *
2371
-	 * @return string
2372
-	 */
2373
-	private function topTenOldestAliveQuery($type = 'list', $sex = 'BOTH', $params = array()) {
2374
-		if (!Auth::isMember($this->tree)) {
2375
-			return I18N::translate('This information is private and cannot be shown.');
2376
-		}
2377
-		if ($sex == 'F') {
2378
-			$sex_search = " AND i_sex='F'";
2379
-		} elseif ($sex == 'M') {
2380
-			$sex_search = " AND i_sex='M'";
2381
-		} else {
2382
-			$sex_search = '';
2383
-		}
2384
-		if (isset($params[0])) {
2385
-			$total = (int) $params[0];
2386
-		} else {
2387
-			$total = 10;
2388
-		}
2389
-		$rows = $this->runSql(
2390
-			"SELECT" .
2391
-			" birth.d_gid AS id," .
2392
-			" MIN(birth.d_julianday1) AS age" .
2393
-			" FROM" .
2394
-			" `##dates` AS birth," .
2395
-			" `##individuals` AS indi" .
2396
-			" WHERE" .
2397
-			" indi.i_id=birth.d_gid AND" .
2398
-			" indi.i_gedcom NOT REGEXP '\\n1 (" . WT_EVENTS_DEAT . ")' AND" .
2399
-			" birth.d_file={$this->tree->getTreeId()} AND" .
2400
-			" birth.d_fact='BIRT' AND" .
2401
-			" birth.d_file=indi.i_file AND" .
2402
-			" birth.d_julianday1<>0" .
2403
-			$sex_search .
2404
-			" GROUP BY id" .
2405
-			" ORDER BY age" .
2406
-			" ASC LIMIT " . $total
2407
-		);
2408
-		$top10 = array();
2409
-		foreach ($rows as $row) {
2410
-			$person = Individual::getInstance($row['id'], $this->tree);
2411
-			$age    = (WT_CLIENT_JD - $row['age']);
2412
-			if ((int) ($age / 365.25) > 0) {
2413
-				$age = (int) ($age / 365.25) . 'y';
2414
-			} elseif ((int) ($age / 30.4375) > 0) {
2415
-				$age = (int) ($age / 30.4375) . 'm';
2416
-			} else {
2417
-				$age = $age . 'd';
2418
-			}
2419
-			$age = FunctionsDate::getAgeAtEvent($age);
2420
-			if ($type === 'list') {
2421
-				$top10[] = "<li><a href=\"" . $person->getHtmlUrl() . "\">" . $person->getFullName() . "</a> (" . $age . ")" . "</li>";
2422
-			} else {
2423
-				$top10[] = "<a href=\"" . $person->getHtmlUrl() . "\">" . $person->getFullName() . "</a> (" . $age . ")";
2424
-			}
2425
-		}
2426
-		if ($type === 'list') {
2427
-			$top10 = implode('', $top10);
2428
-		} else {
2429
-			$top10 = implode('; ', $top10);
2430
-		}
2431
-		if (I18N::direction() === 'rtl') {
2432
-			$top10 = str_replace(array('[', ']', '(', ')', '+'), array('&rlm;[', '&rlm;]', '&rlm;(', '&rlm;)', '&rlm;+'), $top10);
2433
-		}
2434
-		if ($type === 'list') {
2435
-			return '<ul>' . $top10 . '</ul>';
2436
-		}
2437
-
2438
-		return $top10;
2439
-	}
2440
-
2441
-	/**
2442
-	 * Find the average lifespan.
2443
-	 *
2444
-	 * @param string $sex
2445
-	 * @param bool   $show_years
2446
-	 *
2447
-	 * @return string
2448
-	 */
2449
-	private function averageLifespanQuery($sex = 'BOTH', $show_years = false) {
2450
-		if ($sex === 'F') {
2451
-			$sex_search = " AND i_sex='F' ";
2452
-		} elseif ($sex === 'M') {
2453
-			$sex_search = " AND i_sex='M' ";
2454
-		} else {
2455
-			$sex_search = '';
2456
-		}
2457
-		$rows = $this->runSql(
2458
-			"SELECT " .
2459
-			" AVG(death.d_julianday2-birth.d_julianday1) AS age " .
2460
-			"FROM " .
2461
-			" `##dates` AS death, " .
2462
-			" `##dates` AS birth, " .
2463
-			" `##individuals` AS indi " .
2464
-			"WHERE " .
2465
-			" indi.i_id=birth.d_gid AND " .
2466
-			" birth.d_gid=death.d_gid AND " .
2467
-			" death.d_file=" . $this->tree->getTreeId() . " AND " .
2468
-			" birth.d_file=death.d_file AND " .
2469
-			" birth.d_file=indi.i_file AND " .
2470
-			" birth.d_fact='BIRT' AND " .
2471
-			" death.d_fact='DEAT' AND " .
2472
-			" birth.d_julianday1<>0 AND " .
2473
-			" death.d_julianday1>birth.d_julianday2 " .
2474
-			$sex_search
2475
-		);
2476
-		if (!isset($rows[0])) {
2477
-			return '';
2478
-		}
2479
-		$row = $rows[0];
2480
-		$age = $row['age'];
2481
-		if ($show_years) {
2482
-			if ((int) ($age / 365.25) > 0) {
2483
-				$age = (int) ($age / 365.25) . 'y';
2484
-			} elseif ((int) ($age / 30.4375) > 0) {
2485
-				$age = (int) ($age / 30.4375) . 'm';
2486
-			} elseif (!empty($age)) {
2487
-				$age = $age . 'd';
2488
-			}
2489
-
2490
-			return FunctionsDate::getAgeAtEvent($age);
2491
-		} else {
2492
-			return I18N::number($age / 365.25);
2493
-		}
2494
-	}
2495
-
2496
-	/**
2497
-	 * General query on ages.
2498
-	 *
2499
-	 * @param bool     $simple
2500
-	 * @param string   $related
2501
-	 * @param string   $sex
2502
-	 * @param int      $year1
2503
-	 * @param int      $year2
2504
-	 * @param string[] $params
2505
-	 *
2506
-	 * @return array|string
2507
-	 */
2508
-	public function statsAgeQuery($simple = true, $related = 'BIRT', $sex = 'BOTH', $year1 = -1, $year2 = -1, $params = array()) {
2509
-		if ($simple) {
2510
-			if (isset($params[0]) && $params[0] != '') {
2511
-				$size = strtolower($params[0]);
2512
-			} else {
2513
-				$size = '230x250';
2514
-			}
2515
-			$sizes = explode('x', $size);
2516
-			$rows  = $this->runSql(
2517
-				"SELECT" .
2518
-				" ROUND(AVG(death.d_julianday2-birth.d_julianday1)/365.25,1) AS age," .
2519
-				" FLOOR(death.d_year/100+1) AS century," .
2520
-				" i_sex AS sex" .
2521
-				" FROM" .
2522
-				" `##dates` AS death," .
2523
-				" `##dates` AS birth," .
2524
-				" `##individuals` AS indi" .
2525
-				" WHERE" .
2526
-				" indi.i_id=birth.d_gid AND" .
2527
-				" birth.d_gid=death.d_gid AND" .
2528
-				" death.d_file={$this->tree->getTreeId()} AND" .
2529
-				" birth.d_file=death.d_file AND" .
2530
-				" birth.d_file=indi.i_file AND" .
2531
-				" birth.d_fact='BIRT' AND" .
2532
-				" death.d_fact='DEAT' AND" .
2533
-				" birth.d_julianday1<>0 AND" .
2534
-				" birth.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND" .
2535
-				" death.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND" .
2536
-				" death.d_julianday1>birth.d_julianday2" .
2537
-				" GROUP BY century, sex ORDER BY century, sex");
2538
-			if (empty($rows)) {
2539
-				return '';
2540
-			}
2541
-			$chxl    = '0:|';
2542
-			$countsm = '';
2543
-			$countsf = '';
2544
-			$countsa = '';
2545
-			$out     = array();
2546
-			foreach ($rows as $values) {
2547
-				$out[$values['century']][$values['sex']] = $values['age'];
2548
-			}
2549
-			foreach ($out as $century => $values) {
2550
-				if ($sizes[0] < 980) {
2551
-					$sizes[0] += 50;
2552
-				}
2553
-				$chxl .= $this->centuryName($century) . '|';
2554
-				$average = 0;
2555
-				if (isset($values['F'])) {
2556
-					$countsf .= $values['F'] . ',';
2557
-					$average = $values['F'];
2558
-				} else {
2559
-					$countsf .= '0,';
2560
-				}
2561
-				if (isset($values['M'])) {
2562
-					$countsm .= $values['M'] . ',';
2563
-					if ($average == 0) {
2564
-						$countsa .= $values['M'] . ',';
2565
-					} else {
2566
-						$countsa .= (($values['M'] + $average) / 2) . ',';
2567
-					}
2568
-				} else {
2569
-					$countsm .= '0,';
2570
-					if ($average == 0) {
2571
-						$countsa .= '0,';
2572
-					} else {
2573
-						$countsa .= $values['F'] . ',';
2574
-					}
2575
-				}
2576
-			}
2577
-			$countsm = substr($countsm, 0, -1);
2578
-			$countsf = substr($countsf, 0, -1);
2579
-			$countsa = substr($countsa, 0, -1);
2580
-			$chd     = 't2:' . $countsm . '|' . $countsf . '|' . $countsa;
2581
-			$decades = '';
2582
-			for ($i = 0; $i <= 100; $i += 10) {
2583
-				$decades .= '|' . I18N::number($i);
2584
-			}
2585
-			$chxl .= '1:||' . I18N::translate('century') . '|2:' . $decades . '|3:||' . I18N::translate('Age') . '|';
2586
-			$title = I18N::translate('Average age related to death century');
2587
-			if (count($rows) > 6 || mb_strlen($title) < 30) {
2588
-				$chtt = $title;
2589
-			} else {
2590
-				$offset  = 0;
2591
-				$counter = array();
2592
-				while ($offset = strpos($title, ' ', $offset + 1)) {
2593
-					$counter[] = $offset;
2594
-				}
2595
-				$half = (int) (count($counter) / 2);
2596
-				$chtt = substr_replace($title, '|', $counter[$half], 1);
2597
-			}
2598
-
2599
-			return '<img src="' . "https://chart.googleapis.com/chart?cht=bvg&amp;chs={$sizes[0]}x{$sizes[1]}&amp;chm=D,FF0000,2,0,3,1|N*f1*,000000,0,-1,11,1|N*f1*,000000,1,-1,11,1&amp;chf=bg,s,ffffff00|c,s,ffffff00&amp;chtt=" . rawurlencode($chtt) . "&amp;chd={$chd}&amp;chco=0000FF,FFA0CB,FF0000&amp;chbh=20,3&amp;chxt=x,x,y,y&amp;chxl=" . rawurlencode($chxl) . "&amp;chdl=" . rawurlencode(I18N::translate('Males') . '|' . I18N::translate('Females') . '|' . I18N::translate('Average age at death')) . "\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Average age related to death century') . "\" title=\"" . I18N::translate('Average age related to death century') . "\" />";
2600
-		} else {
2601
-			$sex_search = '';
2602
-			$years      = '';
2603
-			if ($sex == 'F') {
2604
-				$sex_search = " AND i_sex='F'";
2605
-			} elseif ($sex == 'M') {
2606
-				$sex_search = " AND i_sex='M'";
2607
-			}
2608
-			if ($year1 >= 0 && $year2 >= 0) {
2609
-				if ($related == 'BIRT') {
2610
-					$years = " AND birth.d_year BETWEEN '{$year1}' AND '{$year2}'";
2611
-				} elseif ($related == 'DEAT') {
2612
-					$years = " AND death.d_year BETWEEN '{$year1}' AND '{$year2}'";
2613
-				}
2614
-			}
2615
-			$rows = $this->runSql(
2616
-				"SELECT" .
2617
-				" death.d_julianday2-birth.d_julianday1 AS age" .
2618
-				" FROM" .
2619
-				" `##dates` AS death," .
2620
-				" `##dates` AS birth," .
2621
-				" `##individuals` AS indi" .
2622
-				" WHERE" .
2623
-				" indi.i_id=birth.d_gid AND" .
2624
-				" birth.d_gid=death.d_gid AND" .
2625
-				" death.d_file={$this->tree->getTreeId()} AND" .
2626
-				" birth.d_file=death.d_file AND" .
2627
-				" birth.d_file=indi.i_file AND" .
2628
-				" birth.d_fact='BIRT' AND" .
2629
-				" death.d_fact='DEAT' AND" .
2630
-				" birth.d_julianday1<>0 AND" .
2631
-				" birth.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND" .
2632
-				" death.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND" .
2633
-				" death.d_julianday1>birth.d_julianday2" .
2634
-				$years .
2635
-				$sex_search .
2636
-				" ORDER BY age DESC");
2637
-
2638
-			return $rows;
2639
-		}
2640
-	}
2641
-
2642
-	/**
2643
-	 * General query on ages.
2644
-	 *
2645
-	 * @param string[] $params
2646
-	 *
2647
-	 * @return string
2648
-	 */
2649
-	public function statsAge($params = array()) {
2650
-		return $this->statsAgeQuery(true, 'BIRT', 'BOTH', -1, -1, $params);
2651
-	}
2652
-
2653
-	/**
2654
-	 * Find the lognest lived individual.
2655
-	 *
2656
-	 * @return string
2657
-	 */
2658
-	public function longestLife() {
2659
-		return $this->longlifeQuery('full', 'BOTH');
2660
-	}
2661
-
2662
-	/**
2663
-	 * Find the age of the longest lived individual.
2664
-	 *
2665
-	 * @return string
2666
-	 */
2667
-	public function longestLifeAge() {
2668
-		return $this->longlifeQuery('age', 'BOTH');
2669
-	}
2670
-
2671
-	/**
2672
-	 * Find the name of the longest lived individual.
2673
-	 *
2674
-	 * @return string
2675
-	 */
2676
-	public function longestLifeName() {
2677
-		return $this->longlifeQuery('name', 'BOTH');
2678
-	}
2679
-
2680
-	/**
2681
-	 * Find the oldest individuals.
2682
-	 *
2683
-	 * @param string[] $params
2684
-	 *
2685
-	 * @return string
2686
-	 */
2687
-	public function topTenOldest($params = array()) {
2688
-		return $this->topTenOldestQuery('nolist', 'BOTH', $params);
2689
-	}
2690
-
2691
-	/**
2692
-	 * Find the oldest living individuals.
2693
-	 *
2694
-	 * @param string[] $params
2695
-	 *
2696
-	 * @return string
2697
-	 */
2698
-	public function topTenOldestList($params = array()) {
2699
-		return $this->topTenOldestQuery('list', 'BOTH', $params);
2700
-	}
2701
-
2702
-	/**
2703
-	 * Find the oldest living individuals.
2704
-	 *
2705
-	 * @param string[] $params
2706
-	 *
2707
-	 * @return string
2708
-	 */
2709
-	public function topTenOldestAlive($params = array()) {
2710
-		return $this->topTenOldestAliveQuery('nolist', 'BOTH', $params);
2711
-	}
2712
-
2713
-	/**
2714
-	 * Find the oldest living individuals.
2715
-	 *
2716
-	 * @param string[] $params
2717
-	 *
2718
-	 * @return string
2719
-	 */
2720
-	public function topTenOldestListAlive($params = array()) {
2721
-		return $this->topTenOldestAliveQuery('list', 'BOTH', $params);
2722
-	}
2723
-
2724
-	/**
2725
-	 * Find the average lifespan.
2726
-	 *
2727
-	 * @param bool $show_years
2728
-	 *
2729
-	 * @return string
2730
-	 */
2731
-	public function averageLifespan($show_years = false) {
2732
-		return $this->averageLifespanQuery('BOTH', $show_years);
2733
-	}
2734
-
2735
-	/**
2736
-	 * Find the longest lived female.
2737
-	 *
2738
-	 * @return string
2739
-	 */
2740
-	public function longestLifeFemale() {
2741
-		return $this->longlifeQuery('full', 'F');
2742
-	}
2743
-
2744
-	/**
2745
-	 * Find the age of the longest lived female.
2746
-	 *
2747
-	 * @return string
2748
-	 */
2749
-	public function longestLifeFemaleAge() {
2750
-		return $this->longlifeQuery('age', 'F');
2751
-	}
2752
-
2753
-	/**
2754
-	 * Find the name of the longest lived female.
2755
-	 *
2756
-	 * @return string
2757
-	 */
2758
-	public function longestLifeFemaleName() {
2759
-		return $this->longlifeQuery('name', 'F');
2760
-	}
2761
-
2762
-	/**
2763
-	 * Find the oldest females.
2764
-	 *
2765
-	 * @param string[] $params
2766
-	 *
2767
-	 * @return string
2768
-	 */
2769
-	public function topTenOldestFemale($params = array()) {
2770
-		return $this->topTenOldestQuery('nolist', 'F', $params);
2771
-	}
2772
-
2773
-	/**
2774
-	 * Find the oldest living females.
2775
-	 *
2776
-	 * @param string[] $params
2777
-	 *
2778
-	 * @return string
2779
-	 */
2780
-	public function topTenOldestFemaleList($params = array()) {
2781
-		return $this->topTenOldestQuery('list', 'F', $params);
2782
-	}
2783
-
2784
-	/**
2785
-	 * Find the oldest living females.
2786
-	 *
2787
-	 * @param string[] $params
2788
-	 *
2789
-	 * @return string
2790
-	 */
2791
-	public function topTenOldestFemaleAlive($params = array()) {
2792
-		return $this->topTenOldestAliveQuery('nolist', 'F', $params);
2793
-	}
2794
-
2795
-	/**
2796
-	 * Find the oldest living females.
2797
-	 *
2798
-	 * @param string[] $params
2799
-	 *
2800
-	 * @return string
2801
-	 */
2802
-	public function topTenOldestFemaleListAlive($params = array()) {
2803
-		return $this->topTenOldestAliveQuery('list', 'F', $params);
2804
-	}
2805
-
2806
-	/**
2807
-	 * Find the average lifespan of females.
2808
-	 *
2809
-	 * @param bool $show_years
2810
-	 *
2811
-	 * @return string
2812
-	 */
2813
-	public function averageLifespanFemale($show_years = false) {
2814
-		return $this->averageLifespanQuery('F', $show_years);
2815
-	}
2816
-
2817
-	/**
2818
-	 * Find the longest lived male.
2819
-	 *
2820
-	 * @return string
2821
-	 */
2822
-	public function longestLifeMale() {
2823
-		return $this->longlifeQuery('full', 'M');
2824
-	}
2825
-
2826
-	/**
2827
-	 * Find the age of the longest lived male.
2828
-	 *
2829
-	 * @return string
2830
-	 */
2831
-	public function longestLifeMaleAge() {
2832
-		return $this->longlifeQuery('age', 'M');
2833
-	}
2834
-
2835
-	/**
2836
-	 * Find the name of the longest lived male.
2837
-	 *
2838
-	 * @return string
2839
-	 */
2840
-	public function longestLifeMaleName() {
2841
-		return $this->longlifeQuery('name', 'M');
2842
-	}
2843
-
2844
-	/**
2845
-	 * Find the longest lived males.
2846
-	 *
2847
-	 * @param string[] $params
2848
-	 *
2849
-	 * @return string
2850
-	 */
2851
-	public function topTenOldestMale($params = array()) {
2852
-		return $this->topTenOldestQuery('nolist', 'M', $params);
2853
-	}
2854
-
2855
-	/**
2856
-	 * Find the longest lived males.
2857
-	 *
2858
-	 * @param string[] $params
2859
-	 *
2860
-	 * @return string
2861
-	 */
2862
-	public function topTenOldestMaleList($params = array()) {
2863
-		return $this->topTenOldestQuery('list', 'M', $params);
2864
-	}
2865
-
2866
-	/**
2867
-	 * Find the longest lived living males.
2868
-	 *
2869
-	 * @param string[] $params
2870
-	 *
2871
-	 * @return string
2872
-	 */
2873
-	public function topTenOldestMaleAlive($params = array()) {
2874
-		return $this->topTenOldestAliveQuery('nolist', 'M', $params);
2875
-	}
2876
-
2877
-	/**
2878
-	 * Find the longest lived living males.
2879
-	 *
2880
-	 * @param string[] $params
2881
-	 *
2882
-	 * @return string
2883
-	 */
2884
-	public function topTenOldestMaleListAlive($params = array()) {
2885
-		return $this->topTenOldestAliveQuery('list', 'M', $params);
2886
-	}
2887
-
2888
-	/**
2889
-	 * Find the average male lifespan.
2890
-	 *
2891
-	 * @param bool $show_years
2892
-	 *
2893
-	 * @return string
2894
-	 */
2895
-	public function averageLifespanMale($show_years = false) {
2896
-		return $this->averageLifespanQuery('M', $show_years);
2897
-	}
2898
-
2899
-	/**
2900
-	 * Events
2901
-	 *
2902
-	 * @param string $type
2903
-	 * @param string $direction
2904
-	 * @param string $facts
2905
-	 *
2906
-	 * @return string
2907
-	 */
2908
-	private function eventQuery($type, $direction, $facts) {
2909
-		$eventTypes = array(
2910
-			'BIRT' => I18N::translate('birth'),
2911
-			'DEAT' => I18N::translate('death'),
2912
-			'MARR' => I18N::translate('marriage'),
2913
-			'ADOP' => I18N::translate('adoption'),
2914
-			'BURI' => I18N::translate('burial'),
2915
-			'CENS' => I18N::translate('census added'),
2916
-		);
2917
-
2918
-		$fact_query = "IN ('" . str_replace('|', "','", $facts) . "')";
2919
-
2920
-		if ($direction != 'ASC') {
2921
-			$direction = 'DESC';
2922
-		}
2923
-		$rows = $this->runSql(''
2924
-			. ' SELECT'
2925
-			. ' d_gid AS id,'
2926
-			. ' d_year AS year,'
2927
-			. ' d_fact AS fact,'
2928
-			. ' d_type AS type'
2929
-			. ' FROM'
2930
-			. " `##dates`"
2931
-			. ' WHERE'
2932
-			. " d_file={$this->tree->getTreeId()} AND"
2933
-			. " d_gid<>'HEAD' AND"
2934
-			. " d_fact {$fact_query} AND"
2935
-			. ' d_julianday1<>0'
2936
-			. ' ORDER BY'
2937
-			. " d_julianday1 {$direction}, d_type LIMIT 1"
2938
-		);
2939
-		if (!isset($rows[0])) {
2940
-			return '';
2941
-		}
2942
-		$row    = $rows[0];
2943
-		$record = GedcomRecord::getInstance($row['id'], $this->tree);
2944
-		switch ($type) {
2945
-		default:
2946
-		case 'full':
2947
-			if ($record->canShow()) {
2948
-				$result = $record->formatList('span', false, $record->getFullName());
2949
-			} else {
2950
-				$result = I18N::translate('This information is private and cannot be shown.');
2951
-			}
2952
-			break;
2953
-		case 'year':
2954
-			$date   = new Date($row['type'] . ' ' . $row['year']);
2955
-			$result = $date->display();
2956
-			break;
2957
-		case 'type':
2958
-			if (isset($eventTypes[$row['fact']])) {
2959
-				$result = $eventTypes[$row['fact']];
2960
-			} else {
2961
-				$result = GedcomTag::getLabel($row['fact']);
2962
-			}
2963
-			break;
2964
-		case 'name':
2965
-			$result = "<a href=\"" . $record->getHtmlUrl() . "\">" . $record->getFullName() . "</a>";
2966
-			break;
2967
-		case 'place':
2968
-			$fact = $record->getFirstFact($row['fact']);
2969
-			if ($fact) {
2970
-				$result = FunctionsPrint::formatFactPlace($fact, true, true, true);
2971
-			} else {
2972
-				$result = I18N::translate('Private');
2973
-			}
2974
-			break;
2975
-		}
2976
-
2977
-		return $result;
2978
-	}
2979
-
2980
-	/**
2981
-	 * Find the earliest event.
2982
-	 *
2983
-	 * @return string
2984
-	 */
2985
-	public function firstEvent() {
2986
-		return $this->eventQuery('full', 'ASC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
2987
-	}
2988
-
2989
-	/**
2990
-	 * Find the year of the earliest event.
2991
-	 *
2992
-	 * @return string
2993
-	 */
2994
-	public function firstEventYear() {
2995
-		return $this->eventQuery('year', 'ASC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
2996
-	}
2997
-
2998
-	/**
2999
-	 * Find the type of the earliest event.
3000
-	 *
3001
-	 * @return string
3002
-	 */
3003
-	public function firstEventType() {
3004
-		return $this->eventQuery('type', 'ASC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
3005
-	}
3006
-
3007
-	/**
3008
-	 * Find the name of the individual with the earliest event.
3009
-	 *
3010
-	 * @return string
3011
-	 */
3012
-	public function firstEventName() {
3013
-		return $this->eventQuery('name', 'ASC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
3014
-	}
3015
-
3016
-	/**
3017
-	 * Find the location of the earliest event.
3018
-	 *
3019
-	 * @return string
3020
-	 */
3021
-	public function firstEventPlace() {
3022
-		return $this->eventQuery('place', 'ASC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
3023
-	}
3024
-
3025
-	/**
3026
-	 * Find the latest event.
3027
-	 *
3028
-	 * @return string
3029
-	 */
3030
-	public function lastEvent() {
3031
-		return $this->eventQuery('full', 'DESC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
3032
-	}
3033
-
3034
-	/**
3035
-	 * Find the year of the latest event.
3036
-	 *
3037
-	 * @return string
3038
-	 */
3039
-	public function lastEventYear() {
3040
-		return $this->eventQuery('year', 'DESC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
3041
-	}
3042
-
3043
-	/**
3044
-	 * Find the type of the latest event.
3045
-	 *
3046
-	 * @return string
3047
-	 */
3048
-	public function lastEventType() {
3049
-		return $this->eventQuery('type', 'DESC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
3050
-	}
3051
-
3052
-	/**
3053
-	 * Find the name of the individual with the latest event.
3054
-	 *
3055
-	 * @return string
3056
-	 */
3057
-	public function lastEventName() {
3058
-		return $this->eventQuery('name', 'DESC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
3059
-	}
3060
-
3061
-	/**
3062
-	 * FInd the location of the latest event.
3063
-	 *
3064
-	 * @return string
3065
-	 */
3066
-	public function lastEventPlace() {
3067
-		return $this->eventQuery('place', 'DESC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
3068
-	}
3069
-
3070
-	/**
3071
-	 * Query the database for marriage tags.
3072
-	 *
3073
-	 * @param string $type
3074
-	 * @param string $age_dir
3075
-	 * @param string $sex
3076
-	 * @param bool   $show_years
3077
-	 *
3078
-	 * @return string
3079
-	 */
3080
-	private function marriageQuery($type = 'full', $age_dir = 'ASC', $sex = 'F', $show_years = false) {
3081
-		if ($sex == 'F') {
3082
-			$sex_field = 'f_wife';
3083
-		} else {
3084
-			$sex_field = 'f_husb';
3085
-		}
3086
-		if ($age_dir != 'ASC') {
3087
-			$age_dir = 'DESC';
3088
-		}
3089
-		$rows = $this->runSql(
3090
-			" SELECT fam.f_id AS famid, fam.{$sex_field}, married.d_julianday2-birth.d_julianday1 AS age, indi.i_id AS i_id" .
3091
-			" FROM `##families` AS fam" .
3092
-			" LEFT JOIN `##dates` AS birth ON birth.d_file = {$this->tree->getTreeId()}" .
3093
-			" LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->getTreeId()}" .
3094
-			" LEFT JOIN `##individuals` AS indi ON indi.i_file = {$this->tree->getTreeId()}" .
3095
-			" WHERE" .
3096
-			" birth.d_gid = indi.i_id AND" .
3097
-			" married.d_gid = fam.f_id AND" .
3098
-			" indi.i_id = fam.{$sex_field} AND" .
3099
-			" fam.f_file = {$this->tree->getTreeId()} AND" .
3100
-			" birth.d_fact = 'BIRT' AND" .
3101
-			" married.d_fact = 'MARR' AND" .
3102
-			" birth.d_julianday1 <> 0 AND" .
3103
-			" married.d_julianday2 > birth.d_julianday1 AND" .
3104
-			" i_sex='{$sex}'" .
3105
-			" ORDER BY" .
3106
-			" married.d_julianday2-birth.d_julianday1 {$age_dir} LIMIT 1"
3107
-		);
3108
-		if (!isset($rows[0])) {
3109
-			return '';
3110
-		}
3111
-		$row = $rows[0];
3112
-		if (isset($row['famid'])) {
3113
-			$family = Family::getInstance($row['famid'], $this->tree);
3114
-		}
3115
-		if (isset($row['i_id'])) {
3116
-			$person = Individual::getInstance($row['i_id'], $this->tree);
3117
-		}
3118
-		switch ($type) {
3119
-		default:
3120
-		case 'full':
3121
-			if ($family->canShow()) {
3122
-				$result = $family->formatList('span', false, $person->getFullName());
3123
-			} else {
3124
-				$result = I18N::translate('This information is private and cannot be shown.');
3125
-			}
3126
-			break;
3127
-		case 'name':
3128
-			$result = '<a href="' . $family->getHtmlUrl() . '">' . $person->getFullName() . '</a>';
3129
-			break;
3130
-		case 'age':
3131
-			$age = $row['age'];
3132
-			if ($show_years) {
3133
-				if ((int) ($age / 365.25) > 0) {
3134
-					$age = (int) ($age / 365.25) . 'y';
3135
-				} elseif ((int) ($age / 30.4375) > 0) {
3136
-					$age = (int) ($age / 30.4375) . 'm';
3137
-				} else {
3138
-					$age = $age . 'd';
3139
-				}
3140
-				$result = FunctionsDate::getAgeAtEvent($age);
3141
-			} else {
3142
-				$result = I18N::number((int) ($age / 365.25));
3143
-			}
3144
-			break;
3145
-		}
3146
-
3147
-		return $result;
3148
-	}
3149
-
3150
-	/**
3151
-	 * General query on age at marriage.
3152
-	 *
3153
-	 * @param string   $type
3154
-	 * @param string   $age_dir
3155
-	 * @param string[] $params
3156
-	 *
3157
-	 * @return string
3158
-	 */
3159
-	private function ageOfMarriageQuery($type = 'list', $age_dir = 'ASC', $params = array()) {
3160
-		if (isset($params[0])) {
3161
-			$total = (int) $params[0];
3162
-		} else {
3163
-			$total = 10;
3164
-		}
3165
-		if ($age_dir != 'ASC') {
3166
-			$age_dir = 'DESC';
3167
-		}
3168
-		$hrows = $this->runSql(
3169
-			" SELECT DISTINCT fam.f_id AS family, MIN(husbdeath.d_julianday2-married.d_julianday1) AS age" .
3170
-			" FROM `##families` AS fam" .
3171
-			" LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->getTreeId()}" .
3172
-			" LEFT JOIN `##dates` AS husbdeath ON husbdeath.d_file = {$this->tree->getTreeId()}" .
3173
-			" WHERE" .
3174
-			" fam.f_file = {$this->tree->getTreeId()} AND" .
3175
-			" husbdeath.d_gid = fam.f_husb AND" .
3176
-			" husbdeath.d_fact = 'DEAT' AND" .
3177
-			" married.d_gid = fam.f_id AND" .
3178
-			" married.d_fact = 'MARR' AND" .
3179
-			" married.d_julianday1 < husbdeath.d_julianday2 AND" .
3180
-			" married.d_julianday1 <> 0" .
3181
-			" GROUP BY family" .
3182
-			" ORDER BY age {$age_dir}");
3183
-		$wrows = $this->runSql(
3184
-			" SELECT DISTINCT fam.f_id AS family, MIN(wifedeath.d_julianday2-married.d_julianday1) AS age" .
3185
-			" FROM `##families` AS fam" .
3186
-			" LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->getTreeId()}" .
3187
-			" LEFT JOIN `##dates` AS wifedeath ON wifedeath.d_file = {$this->tree->getTreeId()}" .
3188
-			" WHERE" .
3189
-			" fam.f_file = {$this->tree->getTreeId()} AND" .
3190
-			" wifedeath.d_gid = fam.f_wife AND" .
3191
-			" wifedeath.d_fact = 'DEAT' AND" .
3192
-			" married.d_gid = fam.f_id AND" .
3193
-			" married.d_fact = 'MARR' AND" .
3194
-			" married.d_julianday1 < wifedeath.d_julianday2 AND" .
3195
-			" married.d_julianday1 <> 0" .
3196
-			" GROUP BY family" .
3197
-			" ORDER BY age {$age_dir}");
3198
-		$drows = $this->runSql(
3199
-			" SELECT DISTINCT fam.f_id AS family, MIN(divorced.d_julianday2-married.d_julianday1) AS age" .
3200
-			" FROM `##families` AS fam" .
3201
-			" LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->getTreeId()}" .
3202
-			" LEFT JOIN `##dates` AS divorced ON divorced.d_file = {$this->tree->getTreeId()}" .
3203
-			" WHERE" .
3204
-			" fam.f_file = {$this->tree->getTreeId()} AND" .
3205
-			" married.d_gid = fam.f_id AND" .
3206
-			" married.d_fact = 'MARR' AND" .
3207
-			" divorced.d_gid = fam.f_id AND" .
3208
-			" divorced.d_fact IN ('DIV', 'ANUL', '_SEPR', '_DETS') AND" .
3209
-			" married.d_julianday1 < divorced.d_julianday2 AND" .
3210
-			" married.d_julianday1 <> 0" .
3211
-			" GROUP BY family" .
3212
-			" ORDER BY age {$age_dir}");
3213
-		if (!isset($hrows) && !isset($wrows) && !isset($drows)) {
3214
-			return '';
3215
-		}
3216
-		$rows = array();
3217
-		foreach ($drows as $family) {
3218
-			$rows[$family['family']] = $family['age'];
3219
-		}
3220
-		foreach ($hrows as $family) {
3221
-			if (!isset($rows[$family['family']])) {
3222
-				$rows[$family['family']] = $family['age'];
3223
-			}
3224
-		}
3225
-		foreach ($wrows as $family) {
3226
-			if (!isset($rows[$family['family']])) {
3227
-				$rows[$family['family']] = $family['age'];
3228
-			} elseif ($rows[$family['family']] > $family['age']) {
3229
-				$rows[$family['family']] = $family['age'];
3230
-			}
3231
-		}
3232
-		if ($age_dir === 'DESC') {
3233
-			arsort($rows);
3234
-		} else {
3235
-			asort($rows);
3236
-		}
3237
-		$top10 = array();
3238
-		$i     = 0;
3239
-		foreach ($rows as $fam => $age) {
3240
-			$family = Family::getInstance($fam, $this->tree);
3241
-			if ($type === 'name') {
3242
-				return $family->formatList('span', false, $family->getFullName());
3243
-			}
3244
-			if ((int) ($age / 365.25) > 0) {
3245
-				$age = (int) ($age / 365.25) . 'y';
3246
-			} elseif ((int) ($age / 30.4375) > 0) {
3247
-				$age = (int) ($age / 30.4375) . 'm';
3248
-			} else {
3249
-				$age = $age . 'd';
3250
-			}
3251
-			$age = FunctionsDate::getAgeAtEvent($age);
3252
-			if ($type === 'age') {
3253
-				return $age;
3254
-			}
3255
-			$husb = $family->getHusband();
3256
-			$wife = $family->getWife();
3257
-			if ($husb && $wife && ($husb->getAllDeathDates() && $wife->getAllDeathDates() || !$husb->isDead() || !$wife->isDead())) {
3258
-				if ($family->canShow()) {
3259
-					if ($type === 'list') {
3260
-						$top10[] = "<li><a href=\"" . $family->getHtmlUrl() . "\">" . $family->getFullName() . "</a> (" . $age . ")" . "</li>";
3261
-					} else {
3262
-						$top10[] = "<a href=\"" . $family->getHtmlUrl() . "\">" . $family->getFullName() . "</a> (" . $age . ")";
3263
-					}
3264
-				}
3265
-				if (++$i === $total) {
3266
-					break;
3267
-				}
3268
-			}
3269
-		}
3270
-		if ($type === 'list') {
3271
-			$top10 = implode('', $top10);
3272
-		} else {
3273
-			$top10 = implode('; ', $top10);
3274
-		}
3275
-		if (I18N::direction() === 'rtl') {
3276
-			$top10 = str_replace(array('[', ']', '(', ')', '+'), array('&rlm;[', '&rlm;]', '&rlm;(', '&rlm;)', '&rlm;+'), $top10);
3277
-		}
3278
-		if ($type === 'list') {
3279
-			return '<ul>' . $top10 . '</ul>';
3280
-		}
3281
-
3282
-		return $top10;
3283
-	}
3284
-
3285
-	/**
3286
-	 * Find the ages between spouses.
3287
-	 *
3288
-	 * @param string   $type
3289
-	 * @param string   $age_dir
3290
-	 * @param string[] $params
3291
-	 *
3292
-	 * @return string
3293
-	 */
3294
-	private function ageBetweenSpousesQuery($type = 'list', $age_dir = 'DESC', $params = array()) {
3295
-		if (isset($params[0])) {
3296
-			$total = (int) $params[0];
3297
-		} else {
3298
-			$total = 10;
3299
-		}
3300
-		if ($age_dir === 'DESC') {
3301
-			$sql =
3302
-				"SELECT f_id AS xref, MIN(wife.d_julianday2-husb.d_julianday1) AS age" .
3303
-				" FROM `##families`" .
3304
-				" JOIN `##dates` AS wife ON wife.d_gid = f_wife AND wife.d_file = f_file" .
3305
-				" JOIN `##dates` AS husb ON husb.d_gid = f_husb AND husb.d_file = f_file" .
3306
-				" WHERE f_file = :tree_id" .
3307
-				" AND husb.d_fact = 'BIRT'" .
3308
-				" AND wife.d_fact = 'BIRT'" .
3309
-				" AND wife.d_julianday2 >= husb.d_julianday1 AND husb.d_julianday1 <> 0" .
3310
-				" GROUP BY xref" .
3311
-				" ORDER BY age DESC" .
3312
-				" LIMIT :limit";
3313
-		} else {
3314
-			$sql =
3315
-				"SELECT f_id AS xref, MIN(husb.d_julianday2-wife.d_julianday1) AS age" .
3316
-				" FROM `##families`" .
3317
-				" JOIN `##dates` AS wife ON wife.d_gid = f_wife AND wife.d_file = f_file" .
3318
-				" JOIN `##dates` AS husb ON husb.d_gid = f_husb AND husb.d_file = f_file" .
3319
-				" WHERE f_file = :tree_id" .
3320
-				" AND husb.d_fact = 'BIRT'" .
3321
-				" AND wife.d_fact = 'BIRT'" .
3322
-				" AND husb.d_julianday2 >= wife.d_julianday1 AND wife.d_julianday1 <> 0" .
3323
-				" GROUP BY xref" .
3324
-				" ORDER BY age DESC" .
3325
-				" LIMIT :limit";
3326
-		}
3327
-		$rows = Database::prepare(
3328
-			$sql
3329
-		)->execute(array(
3330
-			'tree_id' => $this->tree->getTreeId(),
3331
-			'limit'   => $total,
3332
-		))->fetchAll();
3333
-
3334
-		$top10 = array();
3335
-		foreach ($rows as $fam) {
3336
-			$family = Family::getInstance($fam->xref, $this->tree);
3337
-			if ($fam->age < 0) {
3338
-				break;
3339
-			}
3340
-			$age = $fam->age;
3341
-			if ((int) ($age / 365.25) > 0) {
3342
-				$age = (int) ($age / 365.25) . 'y';
3343
-			} elseif ((int) ($age / 30.4375) > 0) {
3344
-				$age = (int) ($age / 30.4375) . 'm';
3345
-			} else {
3346
-				$age = $age . 'd';
3347
-			}
3348
-			$age = FunctionsDate::getAgeAtEvent($age);
3349
-			if ($family->canShow()) {
3350
-				if ($type === 'list') {
3351
-					$top10[] = '<li><a href="' . $family->getHtmlUrl() . '">' . $family->getFullName() . '</a> (' . $age . ')' . "</li>";
3352
-				} else {
3353
-					$top10[] = '<a href="' . $family->getHtmlUrl() . '">' . $family->getFullName() . '</a> (' . $age . ')';
3354
-				}
3355
-			}
3356
-		}
3357
-		if ($type === 'list') {
3358
-			$top10 = implode('', $top10);
3359
-			if ($top10) {
3360
-				$top10 = '<ul>' . $top10 . '</ul>';
3361
-			}
3362
-		} else {
3363
-			$top10 = implode(' ', $top10);
3364
-		}
3365
-
3366
-		return $top10;
3367
-	}
3368
-
3369
-	/**
3370
-	 * General query on parents.
3371
-	 *
3372
-	 * @param string $type
3373
-	 * @param string $age_dir
3374
-	 * @param string $sex
3375
-	 * @param bool   $show_years
3376
-	 *
3377
-	 * @return string
3378
-	 */
3379
-	private function parentsQuery($type = 'full', $age_dir = 'ASC', $sex = 'F', $show_years = false) {
3380
-		if ($sex == 'F') {
3381
-			$sex_field = 'WIFE';
3382
-		} else {
3383
-			$sex_field = 'HUSB';
3384
-		}
3385
-		if ($age_dir != 'ASC') {
3386
-			$age_dir = 'DESC';
3387
-		}
3388
-		$rows = $this->runSql(
3389
-			" SELECT" .
3390
-			" parentfamily.l_to AS id," .
3391
-			" childbirth.d_julianday2-birth.d_julianday1 AS age" .
3392
-			" FROM `##link` AS parentfamily" .
3393
-			" JOIN `##link` AS childfamily ON childfamily.l_file = {$this->tree->getTreeId()}" .
3394
-			" JOIN `##dates` AS birth ON birth.d_file = {$this->tree->getTreeId()}" .
3395
-			" JOIN `##dates` AS childbirth ON childbirth.d_file = {$this->tree->getTreeId()}" .
3396
-			" WHERE" .
3397
-			" birth.d_gid = parentfamily.l_to AND" .
3398
-			" childfamily.l_to = childbirth.d_gid AND" .
3399
-			" childfamily.l_type = 'CHIL' AND" .
3400
-			" parentfamily.l_type = '{$sex_field}' AND" .
3401
-			" childfamily.l_from = parentfamily.l_from AND" .
3402
-			" parentfamily.l_file = {$this->tree->getTreeId()} AND" .
3403
-			" birth.d_fact = 'BIRT' AND" .
3404
-			" childbirth.d_fact = 'BIRT' AND" .
3405
-			" birth.d_julianday1 <> 0 AND" .
3406
-			" childbirth.d_julianday2 > birth.d_julianday1" .
3407
-			" ORDER BY age {$age_dir} LIMIT 1"
3408
-		);
3409
-		if (!isset($rows[0])) {
3410
-			return '';
3411
-		}
3412
-		$row = $rows[0];
3413
-		if (isset($row['id'])) {
3414
-			$person = Individual::getInstance($row['id'], $this->tree);
3415
-		}
3416
-		switch ($type) {
3417
-		default:
3418
-		case 'full':
3419
-			if ($person->canShow()) {
3420
-				$result = $person->formatList('span', false, $person->getFullName());
3421
-			} else {
3422
-				$result = I18N::translate('This information is private and cannot be shown.');
3423
-			}
3424
-			break;
3425
-		case 'name':
3426
-			$result = '<a href="' . $person->getHtmlUrl() . '">' . $person->getFullName() . '</a>';
3427
-			break;
3428
-		case 'age':
3429
-			$age = $row['age'];
3430
-			if ($show_years) {
3431
-				if ((int) ($age / 365.25) > 0) {
3432
-					$age = (int) ($age / 365.25) . 'y';
3433
-				} elseif ((int) ($age / 30.4375) > 0) {
3434
-					$age = (int) ($age / 30.4375) . 'm';
3435
-				} else {
3436
-					$age = $age . 'd';
3437
-				}
3438
-				$result = FunctionsDate::getAgeAtEvent($age);
3439
-			} else {
3440
-				$result = (int) ($age / 365.25);
3441
-			}
3442
-			break;
3443
-		}
3444
-
3445
-		return $result;
3446
-	}
3447
-
3448
-	/**
3449
-	 * General query on marriages.
3450
-	 *
3451
-	 * @param bool     $simple
3452
-	 * @param bool     $first
3453
-	 * @param int      $year1
3454
-	 * @param int      $year2
3455
-	 * @param string[] $params
3456
-	 *
3457
-	 * @return string|array
3458
-	 */
3459
-	public function statsMarrQuery($simple = true, $first = false, $year1 = -1, $year2 = -1, $params = array()) {
3460
-		$WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
3461
-		$WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
3462
-		$WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
3463
-		$WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
3464
-
3465
-		if ($simple) {
3466
-			$sql =
3467
-				"SELECT FLOOR(d_year/100+1) AS century, COUNT(*) AS total" .
3468
-				" FROM `##dates`" .
3469
-				" WHERE d_file={$this->tree->getTreeId()} AND d_year<>0 AND d_fact='MARR' AND d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')";
3470
-			if ($year1 >= 0 && $year2 >= 0) {
3471
-				$sql .= " AND d_year BETWEEN '{$year1}' AND '{$year2}'";
3472
-			}
3473
-			$sql .= " GROUP BY century ORDER BY century";
3474
-		} elseif ($first) {
3475
-			$years = '';
3476
-			if ($year1 >= 0 && $year2 >= 0) {
3477
-				$years = " married.d_year BETWEEN '{$year1}' AND '{$year2}' AND";
3478
-			}
3479
-			$sql =
3480
-				" SELECT fam.f_id AS fams, fam.f_husb, fam.f_wife, married.d_julianday2 AS age, married.d_month AS month, indi.i_id AS indi" .
3481
-				" FROM `##families` AS fam" .
3482
-				" LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->getTreeId()}" .
3483
-				" LEFT JOIN `##individuals` AS indi ON indi.i_file = {$this->tree->getTreeId()}" .
3484
-				" WHERE" .
3485
-				" married.d_gid = fam.f_id AND" .
3486
-				" fam.f_file = {$this->tree->getTreeId()} AND" .
3487
-				" married.d_fact = 'MARR' AND" .
3488
-				" married.d_julianday2 <> 0 AND" .
3489
-				$years .
3490
-				" (indi.i_id = fam.f_husb OR indi.i_id = fam.f_wife)" .
3491
-				" ORDER BY fams, indi, age ASC";
3492
-		} else {
3493
-			$sql =
3494
-				"SELECT d_month, COUNT(*) AS total" .
3495
-				" FROM `##dates`" .
3496
-				" WHERE d_file={$this->tree->getTreeId()} AND d_fact='MARR'";
3497
-			if ($year1 >= 0 && $year2 >= 0) {
3498
-				$sql .= " AND d_year BETWEEN '{$year1}' AND '{$year2}'";
3499
-			}
3500
-			$sql .= " GROUP BY d_month";
3501
-		}
3502
-		$rows = $this->runSql($sql);
3503
-		if (!isset($rows)) {
3504
-			return '';
3505
-		}
3506
-		if ($simple) {
3507
-			if (isset($params[0]) && $params[0] != '') {
3508
-				$size = strtolower($params[0]);
3509
-			} else {
3510
-				$size = $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y;
3511
-			}
3512
-			if (isset($params[1]) && $params[1] != '') {
3513
-				$color_from = strtolower($params[1]);
3514
-			} else {
3515
-				$color_from = $WT_STATS_CHART_COLOR1;
3516
-			}
3517
-			if (isset($params[2]) && $params[2] != '') {
3518
-				$color_to = strtolower($params[2]);
3519
-			} else {
3520
-				$color_to = $WT_STATS_CHART_COLOR2;
3521
-			}
3522
-			$sizes = explode('x', $size);
3523
-			$tot   = 0;
3524
-			foreach ($rows as $values) {
3525
-				$tot += (int) $values['total'];
3526
-			}
3527
-			// Beware divide by zero
3528
-			if ($tot === 0) {
3529
-				return '';
3530
-			}
3531
-			$centuries = '';
3532
-			$counts    = array();
3533
-			foreach ($rows as $values) {
3534
-				$counts[] = round(100 * $values['total'] / $tot, 0);
3535
-				$centuries .= $this->centuryName($values['century']) . ' - ' . I18N::number($values['total']) . '|';
3536
-			}
3537
-			$chd = $this->arrayToExtendedEncoding($counts);
3538
-			$chl = substr($centuries, 0, -1);
3539
-
3540
-			return "<img src=\"https://chart.googleapis.com/chart?cht=p3&amp;chd=e:{$chd}&amp;chs={$size}&amp;chco={$color_from},{$color_to}&amp;chf=bg,s,ffffff00&amp;chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Marriages by century') . "\" title=\"" . I18N::translate('Marriages by century') . "\" />";
3541
-		}
3542
-
3543
-		return $rows;
3544
-	}
3545
-
3546
-	/**
3547
-	 * General query on divorces.
3548
-	 *
3549
-	 * @param bool     $simple
3550
-	 * @param bool     $first
3551
-	 * @param int      $year1
3552
-	 * @param int      $year2
3553
-	 * @param string[] $params
3554
-	 *
3555
-	 * @return string|array
3556
-	 */
3557
-	private function statsDivQuery($simple = true, $first = false, $year1 = -1, $year2 = -1, $params = array()) {
3558
-		$WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
3559
-		$WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
3560
-		$WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
3561
-		$WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
3562
-
3563
-		if ($simple) {
3564
-			$sql =
3565
-				"SELECT FLOOR(d_year/100+1) AS century, COUNT(*) AS total" .
3566
-				" FROM `##dates`" .
3567
-				" WHERE d_file={$this->tree->getTreeId()} AND d_year<>0 AND d_fact = 'DIV' AND d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')";
3568
-			if ($year1 >= 0 && $year2 >= 0) {
3569
-				$sql .= " AND d_year BETWEEN '{$year1}' AND '{$year2}'";
3570
-			}
3571
-			$sql .= " GROUP BY century ORDER BY century";
3572
-		} elseif ($first) {
3573
-			$years = '';
3574
-			if ($year1 >= 0 && $year2 >= 0) {
3575
-				$years = " divorced.d_year BETWEEN '{$year1}' AND '{$year2}' AND";
3576
-			}
3577
-			$sql =
3578
-				" SELECT fam.f_id AS fams, fam.f_husb, fam.f_wife, divorced.d_julianday2 AS age, divorced.d_month AS month, indi.i_id AS indi" .
3579
-				" FROM `##families` AS fam" .
3580
-				" LEFT JOIN `##dates` AS divorced ON divorced.d_file = {$this->tree->getTreeId()}" .
3581
-				" LEFT JOIN `##individuals` AS indi ON indi.i_file = {$this->tree->getTreeId()}" .
3582
-				" WHERE" .
3583
-				" divorced.d_gid = fam.f_id AND" .
3584
-				" fam.f_file = {$this->tree->getTreeId()} AND" .
3585
-				" divorced.d_fact = 'DIV' AND" .
3586
-				" divorced.d_julianday2 <> 0 AND" .
3587
-				$years .
3588
-				" (indi.i_id = fam.f_husb OR indi.i_id = fam.f_wife)" .
3589
-				" ORDER BY fams, indi, age ASC";
3590
-		} else {
3591
-			$sql =
3592
-				"SELECT d_month, COUNT(*) AS total FROM `##dates` " .
3593
-				"WHERE d_file={$this->tree->getTreeId()} AND d_fact = 'DIV'";
3594
-			if ($year1 >= 0 && $year2 >= 0) {
3595
-				$sql .= " AND d_year BETWEEN '{$year1}' AND '{$year2}'";
3596
-			}
3597
-			$sql .= " GROUP BY d_month";
3598
-		}
3599
-		$rows = $this->runSql($sql);
3600
-		if (!isset($rows)) {
3601
-			return '';
3602
-		}
3603
-		if ($simple) {
3604
-			if (isset($params[0]) && $params[0] != '') {
3605
-				$size = strtolower($params[0]);
3606
-			} else {
3607
-				$size = $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y;
3608
-			}
3609
-			if (isset($params[1]) && $params[1] != '') {
3610
-				$color_from = strtolower($params[1]);
3611
-			} else {
3612
-				$color_from = $WT_STATS_CHART_COLOR1;
3613
-			}
3614
-			if (isset($params[2]) && $params[2] != '') {
3615
-				$color_to = strtolower($params[2]);
3616
-			} else {
3617
-				$color_to = $WT_STATS_CHART_COLOR2;
3618
-			}
3619
-			$sizes = explode('x', $size);
3620
-			$tot   = 0;
3621
-			foreach ($rows as $values) {
3622
-				$tot += (int) $values['total'];
3623
-			}
3624
-			// Beware divide by zero
3625
-			if ($tot === 0) {
3626
-				return '';
3627
-			}
3628
-			$centuries = '';
3629
-			$counts    = array();
3630
-			foreach ($rows as $values) {
3631
-				$counts[] = round(100 * $values['total'] / $tot, 0);
3632
-				$centuries .= $this->centuryName($values['century']) . ' - ' . I18N::number($values['total']) . '|';
3633
-			}
3634
-			$chd = $this->arrayToExtendedEncoding($counts);
3635
-			$chl = substr($centuries, 0, -1);
3636
-
3637
-			return "<img src=\"https://chart.googleapis.com/chart?cht=p3&amp;chd=e:{$chd}&amp;chs={$size}&amp;chco={$color_from},{$color_to}&amp;chf=bg,s,ffffff00&amp;chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Divorces by century') . "\" title=\"" . I18N::translate('Divorces by century') . "\" />";
3638
-		}
3639
-
3640
-		return $rows;
3641
-	}
3642
-
3643
-	/**
3644
-	 * Find the earliest marriage.
3645
-	 *
3646
-	 * @return string
3647
-	 */
3648
-	public function firstMarriage() {
3649
-		return $this->mortalityQuery('full', 'ASC', 'MARR');
3650
-	}
3651
-
3652
-	/**
3653
-	 * Find the year of the earliest marriage.
3654
-	 *
3655
-	 * @return string
3656
-	 */
3657
-	public function firstMarriageYear() {
3658
-		return $this->mortalityQuery('year', 'ASC', 'MARR');
3659
-	}
3660
-
3661
-	/**
3662
-	 * Find the names of spouses of the earliest marriage.
3663
-	 *
3664
-	 * @return string
3665
-	 */
3666
-	public function firstMarriageName() {
3667
-		return $this->mortalityQuery('name', 'ASC', 'MARR');
3668
-	}
3669
-
3670
-	/**
3671
-	 * Find the place of the earliest marriage.
3672
-	 *
3673
-	 * @return string
3674
-	 */
3675
-	public function firstMarriagePlace() {
3676
-		return $this->mortalityQuery('place', 'ASC', 'MARR');
3677
-	}
3678
-
3679
-	/**
3680
-	 * Find the latest marriage.
3681
-	 *
3682
-	 * @return string
3683
-	 */
3684
-	public function lastMarriage() {
3685
-		return $this->mortalityQuery('full', 'DESC', 'MARR');
3686
-	}
3687
-
3688
-	/**
3689
-	 * Find the year of the latest marriage.
3690
-	 *
3691
-	 * @return string
3692
-	 */
3693
-	public function lastMarriageYear() {
3694
-		return $this->mortalityQuery('year', 'DESC', 'MARR');
3695
-	}
3696
-
3697
-	/**
3698
-	 * Find the names of spouses of the latest marriage.
3699
-	 *
3700
-	 * @return string
3701
-	 */
3702
-	public function lastMarriageName() {
3703
-		return $this->mortalityQuery('name', 'DESC', 'MARR');
3704
-	}
3705
-
3706
-	/**
3707
-	 * Find the location of the latest marriage.
3708
-	 *
3709
-	 * @return string
3710
-	 */
3711
-	public function lastMarriagePlace() {
3712
-		return $this->mortalityQuery('place', 'DESC', 'MARR');
3713
-	}
3714
-
3715
-	/**
3716
-	 * General query on marriages.
3717
-	 *
3718
-	 * @param string[] $params
3719
-	 *
3720
-	 * @return string
3721
-	 */
3722
-	public function statsMarr($params = array()) {
3723
-		return $this->statsMarrQuery(true, false, -1, -1, $params);
3724
-	}
3725
-
3726
-	/**
3727
-	 * Find the earliest divorce.
3728
-	 *
3729
-	 * @return string
3730
-	 */
3731
-	public function firstDivorce() {
3732
-		return $this->mortalityQuery('full', 'ASC', 'DIV');
3733
-	}
3734
-
3735
-	/**
3736
-	 * Find the year of the earliest divorce.
3737
-	 *
3738
-	 * @return string
3739
-	 */
3740
-	public function firstDivorceYear() {
3741
-		return $this->mortalityQuery('year', 'ASC', 'DIV');
3742
-	}
3743
-
3744
-	/**
3745
-	 * Find the names of individuals in the earliest divorce.
3746
-	 *
3747
-	 * @return string
3748
-	 */
3749
-	public function firstDivorceName() {
3750
-		return $this->mortalityQuery('name', 'ASC', 'DIV');
3751
-	}
3752
-
3753
-	/**
3754
-	 * Find the location of the earliest divorce.
3755
-	 *
3756
-	 * @return string
3757
-	 */
3758
-	public function firstDivorcePlace() {
3759
-		return $this->mortalityQuery('place', 'ASC', 'DIV');
3760
-	}
3761
-
3762
-	/**
3763
-	 * Find the latest divorce.
3764
-	 *
3765
-	 * @return string
3766
-	 */
3767
-	public function lastDivorce() {
3768
-		return $this->mortalityQuery('full', 'DESC', 'DIV');
3769
-	}
3770
-
3771
-	/**
3772
-	 * Find the year of the latest divorce.
3773
-	 *
3774
-	 * @return string
3775
-	 */
3776
-	public function lastDivorceYear() {
3777
-		return $this->mortalityQuery('year', 'DESC', 'DIV');
3778
-	}
3779
-
3780
-	/**
3781
-	 * Find the names of the individuals in the latest divorce.
3782
-	 *
3783
-	 * @return string
3784
-	 */
3785
-	public function lastDivorceName() {
3786
-		return $this->mortalityQuery('name', 'DESC', 'DIV');
3787
-	}
3788
-
3789
-	/**
3790
-	 * Find the location of the latest divorce.
3791
-	 *
3792
-	 * @return string
3793
-	 */
3794
-	public function lastDivorcePlace() {
3795
-		return $this->mortalityQuery('place', 'DESC', 'DIV');
3796
-	}
3797
-
3798
-	/**
3799
-	 * General divorce query.
3800
-	 *
3801
-	 * @param string[] $params
3802
-	 *
3803
-	 * @return string
3804
-	 */
3805
-	public function statsDiv($params = array()) {
3806
-		return $this->statsDivQuery(true, false, -1, -1, $params);
3807
-	}
3808
-
3809
-	/**
3810
-	 * General query on ages at marriage.
3811
-	 *
3812
-	 * @param bool     $simple
3813
-	 * @param string   $sex
3814
-	 * @param int      $year1
3815
-	 * @param int      $year2
3816
-	 * @param string[] $params
3817
-	 *
3818
-	 * @return array|string
3819
-	 */
3820
-	public function statsMarrAgeQuery($simple = true, $sex = 'M', $year1 = -1, $year2 = -1, $params = array()) {
3821
-		if ($simple) {
3822
-			if (isset($params[0]) && $params[0] != '') {
3823
-				$size = strtolower($params[0]);
3824
-			} else {
3825
-				$size = '200x250';
3826
-			}
3827
-			$sizes = explode('x', $size);
3828
-			$rows  = $this->runSql(
3829
-				"SELECT " .
3830
-				" ROUND(AVG(married.d_julianday2-birth.d_julianday1-182.5)/365.25,1) AS age, " .
3831
-				" FLOOR(married.d_year/100+1) AS century, " .
3832
-				" 'M' AS sex " .
3833
-				"FROM `##dates` AS married " .
3834
-				"JOIN `##families` AS fam ON (married.d_gid=fam.f_id AND married.d_file=fam.f_file) " .
3835
-				"JOIN `##dates` AS birth ON (birth.d_gid=fam.f_husb AND birth.d_file=fam.f_file) " .
3836
-				"WHERE " .
3837
-				" '{$sex}' IN ('M', 'BOTH') AND " .
3838
-				" married.d_file={$this->tree->getTreeId()} AND married.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND married.d_fact='MARR' AND " .
3839
-				" birth.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND birth.d_fact='BIRT' AND " .
3840
-				" married.d_julianday1>birth.d_julianday1 AND birth.d_julianday1<>0 " .
3841
-				"GROUP BY century, sex " .
3842
-				"UNION ALL " .
3843
-				"SELECT " .
3844
-				" ROUND(AVG(married.d_julianday2-birth.d_julianday1-182.5)/365.25,1) AS age, " .
3845
-				" FLOOR(married.d_year/100+1) AS century, " .
3846
-				" 'F' AS sex " .
3847
-				"FROM `##dates` AS married " .
3848
-				"JOIN `##families` AS fam ON (married.d_gid=fam.f_id AND married.d_file=fam.f_file) " .
3849
-				"JOIN `##dates` AS birth ON (birth.d_gid=fam.f_wife AND birth.d_file=fam.f_file) " .
3850
-				"WHERE " .
3851
-				" '{$sex}' IN ('F', 'BOTH') AND " .
3852
-				" married.d_file={$this->tree->getTreeId()} AND married.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND married.d_fact='MARR' AND " .
3853
-				" birth.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND birth.d_fact='BIRT' AND " .
3854
-				" married.d_julianday1>birth.d_julianday1 AND birth.d_julianday1<>0 " .
3855
-				" GROUP BY century, sex ORDER BY century"
3856
-			);
3857
-			if (empty($rows)) {
3858
-				return '';
3859
-			}
3860
-			$max = 0;
3861
-			foreach ($rows as $values) {
3862
-				if ($max < $values['age']) {
3863
-					$max = $values['age'];
3864
-				}
3865
-			}
3866
-			$chxl    = '0:|';
3867
-			$chmm    = '';
3868
-			$chmf    = '';
3869
-			$i       = 0;
3870
-			$countsm = '';
3871
-			$countsf = '';
3872
-			$countsa = '';
3873
-			$out     = array();
3874
-			foreach ($rows as $values) {
3875
-				$out[$values['century']][$values['sex']] = $values['age'];
3876
-			}
3877
-			foreach ($out as $century => $values) {
3878
-				if ($sizes[0] < 1000) {
3879
-					$sizes[0] += 50;
3880
-				}
3881
-				$chxl .= $this->centuryName($century) . '|';
3882
-				$average = 0;
3883
-				if (isset($values['F'])) {
3884
-					if ($max <= 50) {
3885
-						$value = $values['F'] * 2;
3886
-					} else {
3887
-						$value = $values['F'];
3888
-					}
3889
-					$countsf .= $value . ',';
3890
-					$average = $value;
3891
-					$chmf .= 't' . $values['F'] . ',000000,1,' . $i . ',11,1|';
3892
-				} else {
3893
-					$countsf .= '0,';
3894
-					$chmf .= 't0,000000,1,' . $i . ',11,1|';
3895
-				}
3896
-				if (isset($values['M'])) {
3897
-					if ($max <= 50) {
3898
-						$value = $values['M'] * 2;
3899
-					} else {
3900
-						$value = $values['M'];
3901
-					}
3902
-					$countsm .= $value . ',';
3903
-					if ($average == 0) {
3904
-						$countsa .= $value . ',';
3905
-					} else {
3906
-						$countsa .= (($value + $average) / 2) . ',';
3907
-					}
3908
-					$chmm .= 't' . $values['M'] . ',000000,0,' . $i . ',11,1|';
3909
-				} else {
3910
-					$countsm .= '0,';
3911
-					if ($average == 0) {
3912
-						$countsa .= '0,';
3913
-					} else {
3914
-						$countsa .= $value . ',';
3915
-					}
3916
-					$chmm .= 't0,000000,0,' . $i . ',11,1|';
3917
-				}
3918
-				$i++;
3919
-			}
3920
-			$countsm = substr($countsm, 0, -1);
3921
-			$countsf = substr($countsf, 0, -1);
3922
-			$countsa = substr($countsa, 0, -1);
3923
-			$chmf    = substr($chmf, 0, -1);
3924
-			$chd     = 't2:' . $countsm . '|' . $countsf . '|' . $countsa;
3925
-			if ($max <= 50) {
3926
-				$chxl .= '1:||' . I18N::translate('century') . '|2:|0|10|20|30|40|50|3:||' . I18N::translate('Age') . '|';
3927
-			} else {
3928
-				$chxl .= '1:||' . I18N::translate('century') . '|2:|0|10|20|30|40|50|60|70|80|90|100|3:||' . I18N::translate('Age') . '|';
3929
-			}
3930
-			if (count($rows) > 4 || mb_strlen(I18N::translate('Average age in century of marriage')) < 30) {
3931
-				$chtt = I18N::translate('Average age in century of marriage');
3932
-			} else {
3933
-				$offset  = 0;
3934
-				$counter = array();
3935
-				while ($offset = strpos(I18N::translate('Average age in century of marriage'), ' ', $offset + 1)) {
3936
-					$counter[] = $offset;
3937
-				}
3938
-				$half = (int) (count($counter) / 2);
3939
-				$chtt = substr_replace(I18N::translate('Average age in century of marriage'), '|', $counter[$half], 1);
3940
-			}
3941
-
3942
-			return "<img src=\"" . "https://chart.googleapis.com/chart?cht=bvg&amp;chs={$sizes[0]}x{$sizes[1]}&amp;chm=D,FF0000,2,0,3,1|{$chmm}{$chmf}&amp;chf=bg,s,ffffff00|c,s,ffffff00&amp;chtt=" . rawurlencode($chtt) . "&amp;chd={$chd}&amp;chco=0000FF,FFA0CB,FF0000&amp;chbh=20,3&amp;chxt=x,x,y,y&amp;chxl=" . rawurlencode($chxl) . "&amp;chdl=" . rawurlencode(I18N::translate('Males') . "|" . I18N::translate('Females') . "|" . I18N::translate('Average age')) . "\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Average age in century of marriage') . "\" title=\"" . I18N::translate('Average age in century of marriage') . "\" />";
3943
-		} else {
3944
-			if ($year1 >= 0 && $year2 >= 0) {
3945
-				$years = " married.d_year BETWEEN {$year1} AND {$year2} AND ";
3946
-			} else {
3947
-				$years = '';
3948
-			}
3949
-			$rows = $this->runSql(
3950
-				"SELECT " .
3951
-				" fam.f_id, " .
3952
-				" birth.d_gid, " .
3953
-				" married.d_julianday2-birth.d_julianday1 AS age " .
3954
-				"FROM `##dates` AS married " .
3955
-				"JOIN `##families` AS fam ON (married.d_gid=fam.f_id AND married.d_file=fam.f_file) " .
3956
-				"JOIN `##dates` AS birth ON (birth.d_gid=fam.f_husb AND birth.d_file=fam.f_file) " .
3957
-				"WHERE " .
3958
-				" '{$sex}' IN ('M', 'BOTH') AND {$years} " .
3959
-				" married.d_file={$this->tree->getTreeId()} AND married.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND married.d_fact='MARR' AND " .
3960
-				" birth.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND birth.d_fact='BIRT' AND " .
3961
-				" married.d_julianday1>birth.d_julianday1 AND birth.d_julianday1<>0 " .
3962
-				"UNION ALL " .
3963
-				"SELECT " .
3964
-				" fam.f_id, " .
3965
-				" birth.d_gid, " .
3966
-				" married.d_julianday2-birth.d_julianday1 AS age " .
3967
-				"FROM `##dates` AS married " .
3968
-				"JOIN `##families` AS fam ON (married.d_gid=fam.f_id AND married.d_file=fam.f_file) " .
3969
-				"JOIN `##dates` AS birth ON (birth.d_gid=fam.f_wife AND birth.d_file=fam.f_file) " .
3970
-				"WHERE " .
3971
-				" '{$sex}' IN ('F', 'BOTH') AND {$years} " .
3972
-				" married.d_file={$this->tree->getTreeId()} AND married.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND married.d_fact='MARR' AND " .
3973
-				" birth.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND birth.d_fact='BIRT' AND " .
3974
-				" married.d_julianday1>birth.d_julianday1 AND birth.d_julianday1<>0 "
3975
-			);
3976
-
3977
-			return $rows;
3978
-		}
3979
-	}
3980
-
3981
-	/**
3982
-	 * Find the youngest wife.
3983
-	 *
3984
-	 * @return string
3985
-	 */
3986
-	public function youngestMarriageFemale() {
3987
-		return $this->marriageQuery('full', 'ASC', 'F', false);
3988
-	}
3989
-
3990
-	/**
3991
-	 * Find the name of the youngest wife.
3992
-	 *
3993
-	 * @return string
3994
-	 */
3995
-	public function youngestMarriageFemaleName() {
3996
-		return $this->marriageQuery('name', 'ASC', 'F', false);
3997
-	}
3998
-
3999
-	/**
4000
-	 * Find the age of the youngest wife.
4001
-	 *
4002
-	 * @param bool $show_years
4003
-	 *
4004
-	 * @return string
4005
-	 */
4006
-	public function youngestMarriageFemaleAge($show_years = false) {
4007
-		return $this->marriageQuery('age', 'ASC', 'F', $show_years);
4008
-	}
4009
-
4010
-	/**
4011
-	 * Find the oldest wife.
4012
-	 *
4013
-	 * @return string
4014
-	 */
4015
-	public function oldestMarriageFemale() {
4016
-		return $this->marriageQuery('full', 'DESC', 'F', false);
4017
-	}
4018
-
4019
-	/**
4020
-	 * Find the name of the oldest wife.
4021
-	 *
4022
-	 * @return string
4023
-	 */
4024
-	public function oldestMarriageFemaleName() {
4025
-		return $this->marriageQuery('name', 'DESC', 'F', false);
4026
-	}
4027
-
4028
-	/**
4029
-	 * Find the age of the oldest wife.
4030
-	 *
4031
-	 * @param bool $show_years
4032
-	 *
4033
-	 * @return string
4034
-	 */
4035
-	public function oldestMarriageFemaleAge($show_years = false) {
4036
-		return $this->marriageQuery('age', 'DESC', 'F', $show_years);
4037
-	}
4038
-
4039
-	/**
4040
-	 * Find the youngest husband.
4041
-	 *
4042
-	 * @return string
4043
-	 */
4044
-	public function youngestMarriageMale() {
4045
-		return $this->marriageQuery('full', 'ASC', 'M', false);
4046
-	}
4047
-
4048
-	/**
4049
-	 * Find the name of the youngest husband.
4050
-	 *
4051
-	 * @return string
4052
-	 */
4053
-	public function youngestMarriageMaleName() {
4054
-		return $this->marriageQuery('name', 'ASC', 'M', false);
4055
-	}
4056
-
4057
-	/**
4058
-	 * Find the age of the youngest husband.
4059
-	 *
4060
-	 * @param bool $show_years
4061
-	 *
4062
-	 * @return string
4063
-	 */
4064
-	public function youngestMarriageMaleAge($show_years = false) {
4065
-		return $this->marriageQuery('age', 'ASC', 'M', $show_years);
4066
-	}
4067
-
4068
-	/**
4069
-	 * Find the oldest husband.
4070
-	 *
4071
-	 * @return string
4072
-	 */
4073
-	public function oldestMarriageMale() {
4074
-		return $this->marriageQuery('full', 'DESC', 'M', false);
4075
-	}
4076
-
4077
-	/**
4078
-	 * Find the name of the oldest husband.
4079
-	 *
4080
-	 * @return string
4081
-	 */
4082
-	public function oldestMarriageMaleName() {
4083
-		return $this->marriageQuery('name', 'DESC', 'M', false);
4084
-	}
4085
-
4086
-	/**
4087
-	 * Find the age of the oldest husband.
4088
-	 *
4089
-	 * @param bool $show_years
4090
-	 *
4091
-	 * @return string
4092
-	 */
4093
-	public function oldestMarriageMaleAge($show_years = false) {
4094
-		return $this->marriageQuery('age', 'DESC', 'M', $show_years);
4095
-	}
4096
-
4097
-	/**
4098
-	 * General query on marriage ages.
4099
-	 *
4100
-	 * @param string[] $params
4101
-	 *
4102
-	 * @return string
4103
-	 */
4104
-	public function statsMarrAge($params = array()) {
4105
-		return $this->statsMarrAgeQuery(true, 'BOTH', -1, -1, $params);
4106
-	}
4107
-
4108
-	/**
4109
-	 * Find the age between husband and wife.
4110
-	 *
4111
-	 * @param string[] $params
4112
-	 *
4113
-	 * @return string
4114
-	 */
4115
-	public function ageBetweenSpousesMF($params = array()) {
4116
-		return $this->ageBetweenSpousesQuery('nolist', 'DESC', $params);
4117
-	}
4118
-
4119
-	/**
4120
-	 * Find the age between husband and wife.
4121
-	 *
4122
-	 * @param string[] $params
4123
-	 *
4124
-	 * @return string
4125
-	 */
4126
-	public function ageBetweenSpousesMFList($params = array()) {
4127
-		return $this->ageBetweenSpousesQuery('list', 'DESC', $params);
4128
-	}
4129
-
4130
-	/**
4131
-	 * Find the age between wife and husband..
4132
-	 *
4133
-	 * @param string[] $params
4134
-	 *
4135
-	 * @return string
4136
-	 */
4137
-	public function ageBetweenSpousesFM($params = array()) {
4138
-		return $this->ageBetweenSpousesQuery('nolist', 'ASC', $params);
4139
-	}
4140
-
4141
-	/**
4142
-	 * Find the age between wife and husband..
4143
-	 *
4144
-	 * @param string[] $params
4145
-	 *
4146
-	 * @return string
4147
-	 */
4148
-	public function ageBetweenSpousesFMList($params = array()) {
4149
-		return $this->ageBetweenSpousesQuery('list', 'ASC', $params);
4150
-	}
4151
-
4152
-	/**
4153
-	 * General query on marriage ages.
4154
-	 *
4155
-	 * @return string
4156
-	 */
4157
-	public function topAgeOfMarriageFamily() {
4158
-		return $this->ageOfMarriageQuery('name', 'DESC', array('1'));
4159
-	}
4160
-
4161
-	/**
4162
-	 * General query on marriage ages.
4163
-	 *
4164
-	 * @return string
4165
-	 */
4166
-	public function topAgeOfMarriage() {
4167
-		return $this->ageOfMarriageQuery('age', 'DESC', array('1'));
4168
-	}
4169
-
4170
-	/**
4171
-	 * General query on marriage ages.
4172
-	 *
4173
-	 * @param string[] $params
4174
-	 *
4175
-	 * @return string
4176
-	 */
4177
-	public function topAgeOfMarriageFamilies($params = array()) {
4178
-		return $this->ageOfMarriageQuery('nolist', 'DESC', $params);
4179
-	}
4180
-
4181
-	/**
4182
-	 * General query on marriage ages.
4183
-	 *
4184
-	 * @param string[] $params
4185
-	 *
4186
-	 * @return string
4187
-	 */
4188
-	public function topAgeOfMarriageFamiliesList($params = array()) {
4189
-		return $this->ageOfMarriageQuery('list', 'DESC', $params);
4190
-	}
4191
-
4192
-	/**
4193
-	 * General query on marriage ages.
4194
-	 *
4195
-	 * @return string
4196
-	 */
4197
-	public function minAgeOfMarriageFamily() {
4198
-		return $this->ageOfMarriageQuery('name', 'ASC', array('1'));
4199
-	}
4200
-
4201
-	/**
4202
-	 * General query on marriage ages.
4203
-	 *
4204
-	 * @return string
4205
-	 */
4206
-	public function minAgeOfMarriage() {
4207
-		return $this->ageOfMarriageQuery('age', 'ASC', array('1'));
4208
-	}
4209
-
4210
-	/**
4211
-	 * General query on marriage ages.
4212
-	 *
4213
-	 * @param string[] $params
4214
-	 *
4215
-	 * @return string
4216
-	 */
4217
-	public function minAgeOfMarriageFamilies($params = array()) {
4218
-		return $this->ageOfMarriageQuery('nolist', 'ASC', $params);
4219
-	}
4220
-
4221
-	/**
4222
-	 * General query on marriage ages.
4223
-	 *
4224
-	 * @param string[] $params
4225
-	 *
4226
-	 * @return string
4227
-	 */
4228
-	public function minAgeOfMarriageFamiliesList($params = array()) {
4229
-		return $this->ageOfMarriageQuery('list', 'ASC', $params);
4230
-	}
4231
-
4232
-	/**
4233
-	 * Find the youngest mother
4234
-	 *
4235
-	 * @return string
4236
-	 */
4237
-	public function youngestMother() {
4238
-		return $this->parentsQuery('full', 'ASC', 'F');
4239
-	}
4240
-
4241
-	/**
4242
-	 * Find the name of the youngest mother.
4243
-	 *
4244
-	 * @return string
4245
-	 */
4246
-	public function youngestMotherName() {
4247
-		return $this->parentsQuery('name', 'ASC', 'F');
4248
-	}
4249
-
4250
-	/**
4251
-	 * Find the age of the youngest mother.
4252
-	 *
4253
-	 * @param bool $show_years
4254
-	 *
4255
-	 * @return string
4256
-	 */
4257
-	public function youngestMotherAge($show_years = false) {
4258
-		return $this->parentsQuery('age', 'ASC', 'F', $show_years);
4259
-	}
4260
-
4261
-	/**
4262
-	 * Find the oldest mother.
4263
-	 *
4264
-	 * @return string
4265
-	 */
4266
-	public function oldestMother() {
4267
-		return $this->parentsQuery('full', 'DESC', 'F');
4268
-	}
4269
-
4270
-	/**
4271
-	 * Find the name of the oldest mother.
4272
-	 *
4273
-	 * @return string
4274
-	 */
4275
-	public function oldestMotherName() {
4276
-		return $this->parentsQuery('name', 'DESC', 'F');
4277
-	}
4278
-
4279
-	/**
4280
-	 * Find the age of the oldest mother.
4281
-	 *
4282
-	 * @param bool $show_years
4283
-	 *
4284
-	 * @return string
4285
-	 */
4286
-	public function oldestMotherAge($show_years = false) {
4287
-		return $this->parentsQuery('age', 'DESC', 'F', $show_years);
4288
-	}
4289
-
4290
-	/**
4291
-	 * Find the youngest father.
4292
-	 *
4293
-	 * @return string
4294
-	 */
4295
-	public function youngestFather() {
4296
-		return $this->parentsQuery('full', 'ASC', 'M');
4297
-	}
4298
-
4299
-	/**
4300
-	 * Find the name of the youngest father.
4301
-	 *
4302
-	 * @return string
4303
-	 */
4304
-	public function youngestFatherName() {
4305
-		return $this->parentsQuery('name', 'ASC', 'M');
4306
-	}
4307
-
4308
-	/**
4309
-	 * Find the age of the youngest father.
4310
-	 *
4311
-	 * @param bool $show_years
4312
-	 *
4313
-	 * @return string
4314
-	 */
4315
-	public function youngestFatherAge($show_years = false) {
4316
-		return $this->parentsQuery('age', 'ASC', 'M', $show_years);
4317
-	}
4318
-
4319
-	/**
4320
-	 * Find the oldest father.
4321
-	 *
4322
-	 * @return string
4323
-	 */
4324
-	public function oldestFather() {
4325
-		return $this->parentsQuery('full', 'DESC', 'M');
4326
-	}
4327
-
4328
-	/**
4329
-	 * Find the name of the oldest father.
4330
-	 *
4331
-	 * @return string
4332
-	 */
4333
-	public function oldestFatherName() {
4334
-		return $this->parentsQuery('name', 'DESC', 'M');
4335
-	}
4336
-
4337
-	/**
4338
-	 * Find the age of the oldest father.
4339
-	 *
4340
-	 * @param bool $show_years
4341
-	 *
4342
-	 * @return string
4343
-	 */
4344
-	public function oldestFatherAge($show_years = false) {
4345
-		return $this->parentsQuery('age', 'DESC', 'M', $show_years);
4346
-	}
4347
-
4348
-	/**
4349
-	 * Number of husbands.
4350
-	 *
4351
-	 * @return string
4352
-	 */
4353
-	public function totalMarriedMales() {
4354
-		$n = Database::prepare("SELECT COUNT(DISTINCT f_husb) FROM `##families` WHERE f_file=? AND f_gedcom LIKE '%\\n1 MARR%'")
4355
-			->execute(array($this->tree->getTreeId()))
4356
-			->fetchOne();
4357
-
4358
-		return I18N::number($n);
4359
-	}
4360
-
4361
-	/**
4362
-	 * Number of wives.
4363
-	 *
4364
-	 * @return string
4365
-	 */
4366
-	public function totalMarriedFemales() {
4367
-		$n = Database::prepare("SELECT COUNT(DISTINCT f_wife) FROM `##families` WHERE f_file=? AND f_gedcom LIKE '%\\n1 MARR%'")
4368
-			->execute(array($this->tree->getTreeId()))
4369
-			->fetchOne();
4370
-
4371
-		return I18N::number($n);
4372
-	}
4373
-
4374
-	/**
4375
-	 * General query on family.
4376
-	 *
4377
-	 * @param string $type
4378
-	 *
4379
-	 * @return string
4380
-	 */
4381
-	private function familyQuery($type = 'full') {
4382
-		$rows = $this->runSql(
4383
-			" SELECT f_numchil AS tot, f_id AS id" .
4384
-			" FROM `##families`" .
4385
-			" WHERE" .
4386
-			" f_file={$this->tree->getTreeId()}" .
4387
-			" AND f_numchil = (" .
4388
-			"  SELECT max( f_numchil )" .
4389
-			"  FROM `##families`" .
4390
-			"  WHERE f_file ={$this->tree->getTreeId()}" .
4391
-			" )" .
4392
-			" LIMIT 1"
4393
-		);
4394
-		if (!isset($rows[0])) {
4395
-			return '';
4396
-		}
4397
-		$row    = $rows[0];
4398
-		$family = Family::getInstance($row['id'], $this->tree);
4399
-		switch ($type) {
4400
-		default:
4401
-		case 'full':
4402
-			if ($family->canShow()) {
4403
-				$result = $family->formatList('span', false, $family->getFullName());
4404
-			} else {
4405
-				$result = I18N::translate('This information is private and cannot be shown.');
4406
-			}
4407
-			break;
4408
-		case 'size':
4409
-			$result = I18N::number($row['tot']);
4410
-			break;
4411
-		case 'name':
4412
-			$result = "<a href=\"" . $family->getHtmlUrl() . "\">" . $family->getFullName() . '</a>';
4413
-			break;
4414
-		}
4415
-
4416
-		return $result;
4417
-	}
4418
-
4419
-	/**
4420
-	 * General query on families.
4421
-	 *
4422
-	 * @param string   $type
4423
-	 * @param string[] $params
4424
-	 *
4425
-	 * @return string
4426
-	 */
4427
-	private function topTenFamilyQuery($type = 'list', $params = array()) {
4428
-		if (isset($params[0])) {
4429
-			$total = (int) $params[0];
4430
-		} else {
4431
-			$total = 10;
4432
-		}
4433
-		$rows = $this->runSql(
4434
-			"SELECT f_numchil AS tot, f_id AS id" .
4435
-			" FROM `##families`" .
4436
-			" WHERE" .
4437
-			" f_file={$this->tree->getTreeId()}" .
4438
-			" ORDER BY tot DESC" .
4439
-			" LIMIT " . $total
4440
-		);
4441
-		if (!isset($rows[0])) {
4442
-			return '';
4443
-		}
4444
-		if (count($rows) < $total) {
4445
-			$total = count($rows);
4446
-		}
4447
-		$top10 = array();
4448
-		for ($c = 0; $c < $total; $c++) {
4449
-			$family = Family::getInstance($rows[$c]['id'], $this->tree);
4450
-			if ($family->canShow()) {
4451
-				if ($type === 'list') {
4452
-					$top10[] =
4453
-						'<li><a href="' . $family->getHtmlUrl() . '">' . $family->getFullName() . '</a> - ' .
4454
-						I18N::plural('%s child', '%s children', $rows[$c]['tot'], I18N::number($rows[$c]['tot']));
4455
-				} else {
4456
-					$top10[] =
4457
-						'<a href="' . $family->getHtmlUrl() . '">' . $family->getFullName() . '</a> - ' .
4458
-						I18N::plural('%s child', '%s children', $rows[$c]['tot'], I18N::number($rows[$c]['tot']));
4459
-				}
4460
-			}
4461
-		}
4462
-		if ($type === 'list') {
4463
-			$top10 = implode('', $top10);
4464
-		} else {
4465
-			$top10 = implode('; ', $top10);
4466
-		}
4467
-		if (I18N::direction() === 'rtl') {
4468
-			$top10 = str_replace(array('[', ']', '(', ')', '+'), array('&rlm;[', '&rlm;]', '&rlm;(', '&rlm;)', '&rlm;+'), $top10);
4469
-		}
4470
-		if ($type === 'list') {
4471
-			return '<ul>' . $top10 . '</ul>';
4472
-		}
4473
-
4474
-		return $top10;
4475
-	}
4476
-
4477
-	/**
4478
-	 * Find the ages between siblings.
4479
-	 *
4480
-	 * @param string   $type
4481
-	 * @param string[] $params
4482
-	 *
4483
-	 * @return string
4484
-	 */
4485
-	private function ageBetweenSiblingsQuery($type = 'list', $params = array()) {
4486
-		if (isset($params[0])) {
4487
-			$total = (int) $params[0];
4488
-		} else {
4489
-			$total = 10;
4490
-		}
4491
-		if (isset($params[1])) {
4492
-			$one = $params[1];
4493
-		} else {
4494
-			$one = false;
4495
-		} // each family only once if true
4496
-		$rows = $this->runSql(
4497
-			" SELECT DISTINCT" .
4498
-			" link1.l_from AS family," .
4499
-			" link1.l_to AS ch1," .
4500
-			" link2.l_to AS ch2," .
4501
-			" child1.d_julianday2-child2.d_julianday2 AS age" .
4502
-			" FROM `##link` AS link1" .
4503
-			" LEFT JOIN `##dates` AS child1 ON child1.d_file = {$this->tree->getTreeId()}" .
4504
-			" LEFT JOIN `##dates` AS child2 ON child2.d_file = {$this->tree->getTreeId()}" .
4505
-			" LEFT JOIN `##link` AS link2 ON link2.l_file = {$this->tree->getTreeId()}" .
4506
-			" WHERE" .
4507
-			" link1.l_file = {$this->tree->getTreeId()} AND" .
4508
-			" link1.l_from = link2.l_from AND" .
4509
-			" link1.l_type = 'CHIL' AND" .
4510
-			" child1.d_gid = link1.l_to AND" .
4511
-			" child1.d_fact = 'BIRT' AND" .
4512
-			" link2.l_type = 'CHIL' AND" .
4513
-			" child2.d_gid = link2.l_to AND" .
4514
-			" child2.d_fact = 'BIRT' AND" .
4515
-			" child1.d_julianday2 > child2.d_julianday2 AND" .
4516
-			" child2.d_julianday2 <> 0 AND" .
4517
-			" child1.d_gid <> child2.d_gid" .
4518
-			" ORDER BY age DESC" .
4519
-			" LIMIT " . $total
4520
-		);
4521
-		if (!isset($rows[0])) {
4522
-			return '';
4523
-		}
4524
-		$top10 = array();
4525
-		$dist  = array();
4526
-		foreach ($rows as $fam) {
4527
-			$family = Family::getInstance($fam['family'], $this->tree);
4528
-			$child1 = Individual::getInstance($fam['ch1'], $this->tree);
4529
-			$child2 = Individual::getInstance($fam['ch2'], $this->tree);
4530
-			if ($type == 'name') {
4531
-				if ($child1->canShow() && $child2->canShow()) {
4532
-					$return = '<a href="' . $child2->getHtmlUrl() . '">' . $child2->getFullName() . '</a> ';
4533
-					$return .= I18N::translate('and') . ' ';
4534
-					$return .= '<a href="' . $child1->getHtmlUrl() . '">' . $child1->getFullName() . '</a>';
4535
-					$return .= ' <a href="' . $family->getHtmlUrl() . '">[' . I18N::translate('View this family') . ']</a>';
4536
-				} else {
4537
-					$return = I18N::translate('This information is private and cannot be shown.');
4538
-				}
4539
-
4540
-				return $return;
4541
-			}
4542
-			$age = $fam['age'];
4543
-			if ((int) ($age / 365.25) > 0) {
4544
-				$age = (int) ($age / 365.25) . 'y';
4545
-			} elseif ((int) ($age / 30.4375) > 0) {
4546
-				$age = (int) ($age / 30.4375) . 'm';
4547
-			} else {
4548
-				$age = $age . 'd';
4549
-			}
4550
-			$age = FunctionsDate::getAgeAtEvent($age);
4551
-			if ($type == 'age') {
4552
-				return $age;
4553
-			}
4554
-			if ($type == 'list') {
4555
-				if ($one && !in_array($fam['family'], $dist)) {
4556
-					if ($child1->canShow() && $child2->canShow()) {
4557
-						$return = "<li>";
4558
-						$return .= "<a href=\"" . $child2->getHtmlUrl() . "\">" . $child2->getFullName() . "</a> ";
4559
-						$return .= I18N::translate('and') . " ";
4560
-						$return .= "<a href=\"" . $child1->getHtmlUrl() . "\">" . $child1->getFullName() . "</a>";
4561
-						$return .= " (" . $age . ")";
4562
-						$return .= " <a href=\"" . $family->getHtmlUrl() . "\">[" . I18N::translate('View this family') . "]</a>";
4563
-						$return .= '</li>';
4564
-						$top10[] = $return;
4565
-						$dist[]  = $fam['family'];
4566
-					}
4567
-				} elseif (!$one && $child1->canShow() && $child2->canShow()) {
4568
-					$return = "<li>";
4569
-					$return .= "<a href=\"" . $child2->getHtmlUrl() . "\">" . $child2->getFullName() . "</a> ";
4570
-					$return .= I18N::translate('and') . " ";
4571
-					$return .= "<a href=\"" . $child1->getHtmlUrl() . "\">" . $child1->getFullName() . "</a>";
4572
-					$return .= " (" . $age . ")";
4573
-					$return .= " <a href=\"" . $family->getHtmlUrl() . "\">[" . I18N::translate('View this family') . "]</a>";
4574
-					$return .= '</li>';
4575
-					$top10[] = $return;
4576
-				}
4577
-			} else {
4578
-				if ($child1->canShow() && $child2->canShow()) {
4579
-					$return = $child2->formatList('span', false, $child2->getFullName());
4580
-					$return .= "<br>" . I18N::translate('and') . "<br>";
4581
-					$return .= $child1->formatList('span', false, $child1->getFullName());
4582
-					$return .= "<br><a href=\"" . $family->getHtmlUrl() . "\">[" . I18N::translate('View this family') . "]</a>";
4583
-
4584
-					return $return;
4585
-				} else {
4586
-					return I18N::translate('This information is private and cannot be shown.');
4587
-				}
4588
-			}
4589
-		}
4590
-		if ($type === 'list') {
4591
-			$top10 = implode('', $top10);
4592
-		}
4593
-		if (I18N::direction() === 'rtl') {
4594
-			$top10 = str_replace(array('[', ']', '(', ')', '+'), array('&rlm;[', '&rlm;]', '&rlm;(', '&rlm;)', '&rlm;+'), $top10);
4595
-		}
4596
-		if ($type === 'list') {
4597
-			return '<ul>' . $top10 . '</ul>';
4598
-		}
4599
-
4600
-		return $top10;
4601
-	}
4602
-
4603
-	/**
4604
-	 * Find the month in the year of the birth of the first child.
4605
-	 *
4606
-	 * @param bool     $simple
4607
-	 * @param bool     $sex
4608
-	 * @param int      $year1
4609
-	 * @param int      $year2
4610
-	 * @param string[] $params
4611
-	 *
4612
-	 * @return string|string[][]
4613
-	 */
4614
-	public function monthFirstChildQuery($simple = true, $sex = false, $year1 = -1, $year2 = -1, $params = array()) {
4615
-		$WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
4616
-		$WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
4617
-		$WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
4618
-		$WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
4619
-
4620
-		if ($year1 >= 0 && $year2 >= 0) {
4621
-			$sql_years = " AND (d_year BETWEEN '{$year1}' AND '{$year2}')";
4622
-		} else {
4623
-			$sql_years = '';
4624
-		}
4625
-		if ($sex) {
4626
-			$sql_sex1 = ', i_sex';
4627
-			$sql_sex2 = " JOIN `##individuals` AS child ON child1.d_file = i_file AND child1.d_gid = child.i_id ";
4628
-		} else {
4629
-			$sql_sex1 = '';
4630
-			$sql_sex2 = '';
4631
-		}
4632
-		$sql =
4633
-			"SELECT d_month{$sql_sex1}, COUNT(*) AS total " .
4634
-			"FROM (" .
4635
-			" SELECT family{$sql_sex1}, MIN(date) AS d_date, d_month" .
4636
-			" FROM (" .
4637
-			"  SELECT" .
4638
-			"  link1.l_from AS family," .
4639
-			"  link1.l_to AS child," .
4640
-			"  child1.d_julianday2 AS date," .
4641
-			"  child1.d_month as d_month" .
4642
-			$sql_sex1 .
4643
-			"  FROM `##link` AS link1" .
4644
-			"  LEFT JOIN `##dates` AS child1 ON child1.d_file = {$this->tree->getTreeId()}" .
4645
-			$sql_sex2 .
4646
-			"  WHERE" .
4647
-			"  link1.l_file = {$this->tree->getTreeId()} AND" .
4648
-			"  link1.l_type = 'CHIL' AND" .
4649
-			"  child1.d_gid = link1.l_to AND" .
4650
-			"  child1.d_fact = 'BIRT' AND" .
4651
-			"  child1.d_month IN ('JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC')" .
4652
-			$sql_years .
4653
-			"  ORDER BY date" .
4654
-			" ) AS children" .
4655
-			" GROUP BY family, d_month{$sql_sex1}" .
4656
-			") AS first_child " .
4657
-			"GROUP BY d_month";
4658
-		if ($sex) {
4659
-			$sql .= ', i_sex';
4660
-		}
4661
-		$rows = $this->runSql($sql);
4662
-		if ($simple) {
4663
-			if (isset($params[0]) && $params[0] != '') {
4664
-				$size = strtolower($params[0]);
4665
-			} else {
4666
-				$size = $WT_STATS_S_CHART_X . 'x' . $WT_STATS_S_CHART_Y;
4667
-			}
4668
-			if (isset($params[1]) && $params[1] != '') {
4669
-				$color_from = strtolower($params[1]);
4670
-			} else {
4671
-				$color_from = $WT_STATS_CHART_COLOR1;
4672
-			}
4673
-			if (isset($params[2]) && $params[2] != '') {
4674
-				$color_to = strtolower($params[2]);
4675
-			} else {
4676
-				$color_to = $WT_STATS_CHART_COLOR2;
4677
-			}
4678
-			$sizes = explode('x', $size);
4679
-			$tot   = 0;
4680
-			foreach ($rows as $values) {
4681
-				$tot += $values['total'];
4682
-			}
4683
-			// Beware divide by zero
4684
-			if ($tot == 0) {
4685
-				return '';
4686
-			}
4687
-			$text   = '';
4688
-			$counts = array();
4689
-			foreach ($rows as $values) {
4690
-				$counts[] = round(100 * $values['total'] / $tot, 0);
4691
-				switch ($values['d_month']) {
4692
-				default:
4693
-				case 'JAN':
4694
-					$values['d_month'] = 1;
4695
-					break;
4696
-				case 'FEB':
4697
-					$values['d_month'] = 2;
4698
-					break;
4699
-				case 'MAR':
4700
-					$values['d_month'] = 3;
4701
-					break;
4702
-				case 'APR':
4703
-					$values['d_month'] = 4;
4704
-					break;
4705
-				case 'MAY':
4706
-					$values['d_month'] = 5;
4707
-					break;
4708
-				case 'JUN':
4709
-					$values['d_month'] = 6;
4710
-					break;
4711
-				case 'JUL':
4712
-					$values['d_month'] = 7;
4713
-					break;
4714
-				case 'AUG':
4715
-					$values['d_month'] = 8;
4716
-					break;
4717
-				case 'SEP':
4718
-					$values['d_month'] = 9;
4719
-					break;
4720
-				case 'OCT':
4721
-					$values['d_month'] = 10;
4722
-					break;
4723
-				case 'NOV':
4724
-					$values['d_month'] = 11;
4725
-					break;
4726
-				case 'DEC':
4727
-					$values['d_month'] = 12;
4728
-					break;
4729
-				}
4730
-				$text .= I18N::translate(ucfirst(strtolower(($values['d_month'])))) . ' - ' . $values['total'] . '|';
4731
-			}
4732
-			$chd = $this->arrayToExtendedEncoding($counts);
4733
-			$chl = substr($text, 0, -1);
4734
-
4735
-			return '<img src="https://chart.googleapis.com/chart?cht=p3&amp;chd=e:' . $chd . '&amp;chs=' . $size . '&amp;chco=' . $color_from . ',' . $color_to . '&amp;chf=bg,s,ffffff00&amp;chl=' . $chl . '" width="' . $sizes[0] . '" height="' . $sizes[1] . '" alt="' . I18N::translate('Month of birth of first child in a relation') . '" title="' . I18N::translate('Month of birth of first child in a relation') . '" />';
4736
-		}
4737
-
4738
-		return $rows;
4739
-	}
4740
-
4741
-	/**
4742
-	 * Find the family with the most children.
4743
-	 *
4744
-	 * @return string
4745
-	 */
4746
-	public function largestFamily() {
4747
-		return $this->familyQuery('full');
4748
-	}
4749
-
4750
-	/**
4751
-	 * Find the number of children in the largest family.
4752
-	 *
4753
-	 * @return string
4754
-	 */
4755
-	public function largestFamilySize() {
4756
-		return $this->familyQuery('size');
4757
-	}
4758
-
4759
-	/**
4760
-	 * Find the family with the most children.
4761
-	 *
4762
-	 * @return string
4763
-	 */
4764
-	public function largestFamilyName() {
4765
-		return $this->familyQuery('name');
4766
-	}
4767
-
4768
-	/**
4769
-	 * The the families with the most children.
4770
-	 *
4771
-	 * @param string[] $params
4772
-	 *
4773
-	 * @return string
4774
-	 */
4775
-	public function topTenLargestFamily($params = array()) {
4776
-		return $this->topTenFamilyQuery('nolist', $params);
4777
-	}
4778
-
4779
-	/**
4780
-	 * Find the families with the most children.
4781
-	 *
4782
-	 * @param string[] $params
4783
-	 *
4784
-	 * @return string
4785
-	 */
4786
-	public function topTenLargestFamilyList($params = array()) {
4787
-		return $this->topTenFamilyQuery('list', $params);
4788
-	}
4789
-
4790
-	/**
4791
-	 * Create a chart of the largest families.
4792
-	 *
4793
-	 * @param string[] $params
4794
-	 *
4795
-	 * @return string
4796
-	 */
4797
-	public function chartLargestFamilies($params = array()) {
4798
-		$WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
4799
-		$WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
4800
-		$WT_STATS_L_CHART_X    = Theme::theme()->parameter('stats-large-chart-x');
4801
-		$WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
4802
-
4803
-		if (isset($params[0]) && $params[0] != '') {
4804
-			$size = strtolower($params[0]);
4805
-		} else {
4806
-			$size = $WT_STATS_L_CHART_X . 'x' . $WT_STATS_S_CHART_Y;
4807
-		}
4808
-		if (isset($params[1]) && $params[1] != '') {
4809
-			$color_from = strtolower($params[1]);
4810
-		} else {
4811
-			$color_from = $WT_STATS_CHART_COLOR1;
4812
-		}
4813
-		if (isset($params[2]) && $params[2] != '') {
4814
-			$color_to = strtolower($params[2]);
4815
-		} else {
4816
-			$color_to = $WT_STATS_CHART_COLOR2;
4817
-		}
4818
-		if (isset($params[3]) && $params[3] != '') {
4819
-			$total = strtolower($params[3]);
4820
-		} else {
4821
-			$total = 10;
4822
-		}
4823
-		$sizes = explode('x', $size);
4824
-		$total = (int) $total;
4825
-		$rows  = $this->runSql(
4826
-			" SELECT f_numchil AS tot, f_id AS id" .
4827
-			" FROM `##families`" .
4828
-			" WHERE f_file={$this->tree->getTreeId()}" .
4829
-			" ORDER BY tot DESC" .
4830
-			" LIMIT " . $total
4831
-		);
4832
-		if (!isset($rows[0])) {
4833
-			return '';
4834
-		}
4835
-		$tot = 0;
4836
-		foreach ($rows as $row) {
4837
-			$tot += (int) $row['tot'];
4838
-		}
4839
-		$chd = '';
4840
-		$chl = array();
4841
-		foreach ($rows as $row) {
4842
-			$family = Family::getInstance($row['id'], $this->tree);
4843
-			if ($family->canShow()) {
4844
-				if ($tot == 0) {
4845
-					$per = 0;
4846
-				} else {
4847
-					$per = round(100 * $row['tot'] / $tot, 0);
4848
-				}
4849
-				$chd .= $this->arrayToExtendedEncoding(array($per));
4850
-				$chl[] = htmlspecialchars_decode(strip_tags($family->getFullName())) . ' - ' . I18N::number($row['tot']);
4851
-			}
4852
-		}
4853
-		$chl = rawurlencode(implode('|', $chl));
4854
-
4855
-		return "<img src=\"https://chart.googleapis.com/chart?cht=p3&amp;chd=e:{$chd}&amp;chs={$size}&amp;chco={$color_from},{$color_to}&amp;chf=bg,s,ffffff00&amp;chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Largest families') . "\" title=\"" . I18N::translate('Largest families') . "\" />";
4856
-	}
4857
-
4858
-	/**
4859
-	 * Count the total children.
4860
-	 *
4861
-	 * @return string
4862
-	 */
4863
-	public function totalChildren() {
4864
-		$rows = $this->runSql("SELECT SUM(f_numchil) AS tot FROM `##families` WHERE f_file={$this->tree->getTreeId()}");
4865
-
4866
-		return I18N::number($rows[0]['tot']);
4867
-	}
4868
-
4869
-	/**
4870
-	 * Find the average number of children in families.
4871
-	 *
4872
-	 * @return string
4873
-	 */
4874
-	public function averageChildren() {
4875
-		$rows = $this->runSql("SELECT AVG(f_numchil) AS tot FROM `##families` WHERE f_file={$this->tree->getTreeId()}");
4876
-
4877
-		return I18N::number($rows[0]['tot'], 2);
4878
-	}
4879
-
4880
-	/**
4881
-	 * General query on familes/children.
4882
-	 *
4883
-	 * @param bool     $simple
4884
-	 * @param string   $sex
4885
-	 * @param int      $year1
4886
-	 * @param int      $year2
4887
-	 * @param string[] $params
4888
-	 *
4889
-	 * @return string|string[][]
4890
-	 */
4891
-	public function statsChildrenQuery($simple = true, $sex = 'BOTH', $year1 = -1, $year2 = -1, $params = array()) {
4892
-		if ($simple) {
4893
-			if (isset($params[0]) && $params[0] != '') {
4894
-				$size = strtolower($params[0]);
4895
-			} else {
4896
-				$size = '220x200';
4897
-			}
4898
-			$sizes = explode('x', $size);
4899
-			$max   = 0;
4900
-			$rows  = $this->runSql(
4901
-				" SELECT ROUND(AVG(f_numchil),2) AS num, FLOOR(d_year/100+1) AS century" .
4902
-				" FROM  `##families`" .
4903
-				" JOIN  `##dates` ON (d_file = f_file AND d_gid=f_id)" .
4904
-				" WHERE f_file = {$this->tree->getTreeId()}" .
4905
-				" AND   d_julianday1<>0" .
4906
-				" AND   d_fact = 'MARR'" .
4907
-				" AND   d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')" .
4908
-				" GROUP BY century" .
4909
-				" ORDER BY century");
4910
-			if (empty($rows)) {
4911
-				return '';
4912
-			}
4913
-			foreach ($rows as $values) {
4914
-				if ($max < $values['num']) {
4915
-					$max = $values['num'];
4916
-				}
4917
-			}
4918
-			$chm    = "";
4919
-			$chxl   = "0:|";
4920
-			$i      = 0;
4921
-			$counts = array();
4922
-			foreach ($rows as $values) {
4923
-				if ($sizes[0] < 980) {
4924
-					$sizes[0] += 38;
4925
-				}
4926
-				$chxl .= $this->centuryName($values['century']) . "|";
4927
-				if ($max <= 5) {
4928
-					$counts[] = round($values['num'] * 819.2 - 1, 1);
4929
-				} elseif ($max <= 10) {
4930
-					$counts[] = round($values['num'] * 409.6, 1);
4931
-				} else {
4932
-					$counts[] = round($values['num'] * 204.8, 1);
4933
-				}
4934
-				$chm .= 't' . $values['num'] . ',000000,0,' . $i . ',11,1|';
4935
-				$i++;
4936
-			}
4937
-			$chd = $this->arrayToExtendedEncoding($counts);
4938
-			$chm = substr($chm, 0, -1);
4939
-			if ($max <= 5) {
4940
-				$chxl .= "1:||" . I18N::translate('century') . "|2:|0|1|2|3|4|5|3:||" . I18N::translate('Number of children') . "|";
4941
-			} elseif ($max <= 10) {
4942
-				$chxl .= "1:||" . I18N::translate('century') . "|2:|0|1|2|3|4|5|6|7|8|9|10|3:||" . I18N::translate('Number of children') . "|";
4943
-			} else {
4944
-				$chxl .= "1:||" . I18N::translate('century') . "|2:|0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|3:||" . I18N::translate('Number of children') . "|";
4945
-			}
4946
-
4947
-			return "<img src=\"https://chart.googleapis.com/chart?cht=bvg&amp;chs={$sizes[0]}x{$sizes[1]}&amp;chf=bg,s,ffffff00|c,s,ffffff00&amp;chm=D,FF0000,0,0,3,1|{$chm}&amp;chd=e:{$chd}&amp;chco=0000FF&amp;chbh=30,3&amp;chxt=x,x,y,y&amp;chxl=" . rawurlencode($chxl) . "\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Average number of children per family') . "\" title=\"" . I18N::translate('Average number of children per family') . "\" />";
4948
-		} else {
4949
-			if ($sex == 'M') {
4950
-				$sql =
4951
-					"SELECT num, COUNT(*) AS total FROM " .
4952
-					"(SELECT count(i_sex) AS num FROM `##link` " .
4953
-					"LEFT OUTER JOIN `##individuals` " .
4954
-					"ON l_from=i_id AND l_file=i_file AND i_sex='M' AND l_type='FAMC' " .
4955
-					"JOIN `##families` ON f_file=l_file AND f_id=l_to WHERE f_file={$this->tree->getTreeId()} GROUP BY l_to" .
4956
-					") boys" .
4957
-					" GROUP BY num" .
4958
-					" ORDER BY num";
4959
-			} elseif ($sex == 'F') {
4960
-				$sql =
4961
-					"SELECT num, COUNT(*) AS total FROM " .
4962
-					"(SELECT count(i_sex) AS num FROM `##link` " .
4963
-					"LEFT OUTER JOIN `##individuals` " .
4964
-					"ON l_from=i_id AND l_file=i_file AND i_sex='F' AND l_type='FAMC' " .
4965
-					"JOIN `##families` ON f_file=l_file AND f_id=l_to WHERE f_file={$this->tree->getTreeId()} GROUP BY l_to" .
4966
-					") girls" .
4967
-					" GROUP BY num" .
4968
-					" ORDER BY num";
4969
-			} else {
4970
-				$sql = "SELECT f_numchil, COUNT(*) AS total FROM `##families` ";
4971
-				if ($year1 >= 0 && $year2 >= 0) {
4972
-					$sql .=
4973
-						"AS fam LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->getTreeId()}"
4974
-						. " WHERE"
4975
-						. " married.d_gid = fam.f_id AND"
4976
-						. " fam.f_file = {$this->tree->getTreeId()} AND"
4977
-						. " married.d_fact = 'MARR' AND"
4978
-						. " married.d_year BETWEEN '{$year1}' AND '{$year2}'";
4979
-				} else {
4980
-					$sql .= "WHERE f_file={$this->tree->getTreeId()}";
4981
-				}
4982
-				$sql .= " GROUP BY f_numchil";
4983
-			}
4984
-			$rows = $this->runSql($sql);
4985
-
4986
-			return $rows;
4987
-		}
4988
-	}
4989
-
4990
-	/**
4991
-	 * Genearl query on families/children.
4992
-	 *
4993
-	 * @param string[] $params
4994
-	 *
4995
-	 * @return string
4996
-	 */
4997
-	public function statsChildren($params = array()) {
4998
-		return $this->statsChildrenQuery(true, 'BOTH', -1, -1, $params);
4999
-	}
5000
-
5001
-	/**
5002
-	 * Find the names of siblings with the widest age gap.
5003
-	 *
5004
-	 * @param string[] $params
5005
-	 *
5006
-	 * @return string
5007
-	 */
5008
-	public function topAgeBetweenSiblingsName($params = array()) {
5009
-		return $this->ageBetweenSiblingsQuery('name', $params);
5010
-	}
5011
-
5012
-	/**
5013
-	 * Find the widest age gap between siblings.
5014
-	 *
5015
-	 * @param string[] $params
5016
-	 *
5017
-	 * @return string
5018
-	 */
5019
-	public function topAgeBetweenSiblings($params = array()) {
5020
-		return $this->ageBetweenSiblingsQuery('age', $params);
5021
-	}
5022
-
5023
-	/**
5024
-	 * Find the name of siblings with the widest age gap.
5025
-	 *
5026
-	 * @param string[] $params
5027
-	 *
5028
-	 * @return string
5029
-	 */
5030
-	public function topAgeBetweenSiblingsFullName($params = array()) {
5031
-		return $this->ageBetweenSiblingsQuery('nolist', $params);
5032
-	}
5033
-
5034
-	/**
5035
-	 * Find the siblings with the widest age gaps.
5036
-	 *
5037
-	 * @param string[] $params
5038
-	 *
5039
-	 * @return string
5040
-	 */
5041
-	public function topAgeBetweenSiblingsList($params = array()) {
5042
-		return $this->ageBetweenSiblingsQuery('list', $params);
5043
-	}
5044
-
5045
-	/**
5046
-	 * Find the families with no children.
5047
-	 *
5048
-	 * @return string
5049
-	 */
5050
-	private function noChildrenFamiliesQuery() {
5051
-		$rows = $this->runSql(
5052
-			" SELECT COUNT(*) AS tot" .
5053
-			" FROM  `##families`" .
5054
-			" WHERE f_numchil = 0 AND f_file = {$this->tree->getTreeId()}");
5055
-
5056
-		return $rows[0]['tot'];
5057
-	}
5058
-
5059
-	/**
5060
-	 * Find the families with no children.
5061
-	 *
5062
-	 * @return string
5063
-	 */
5064
-	public function noChildrenFamilies() {
5065
-		return I18N::number($this->noChildrenFamiliesQuery());
5066
-	}
5067
-
5068
-	/**
5069
-	 * Find the families with no children.
5070
-	 *
5071
-	 * @param string[] $params
5072
-	 *
5073
-	 * @return string
5074
-	 */
5075
-	public function noChildrenFamiliesList($params = array()) {
5076
-		if (isset($params[0]) && $params[0] != '') {
5077
-			$type = strtolower($params[0]);
5078
-		} else {
5079
-			$type = 'list';
5080
-		}
5081
-		$rows = $this->runSql(
5082
-			" SELECT f_id AS family" .
5083
-			" FROM `##families` AS fam" .
5084
-			" WHERE f_numchil = 0 AND fam.f_file = {$this->tree->getTreeId()}");
5085
-		if (!isset($rows[0])) {
5086
-			return '';
5087
-		}
5088
-		$top10 = array();
5089
-		foreach ($rows as $row) {
5090
-			$family = Family::getInstance($row['family'], $this->tree);
5091
-			if ($family->canShow()) {
5092
-				if ($type == 'list') {
5093
-					$top10[] = "<li><a href=\"" . $family->getHtmlUrl() . "\">" . $family->getFullName() . "</a></li>";
5094
-				} else {
5095
-					$top10[] = "<a href=\"" . $family->getHtmlUrl() . "\">" . $family->getFullName() . "</a>";
5096
-				}
5097
-			}
5098
-		}
5099
-		if ($type == 'list') {
5100
-			$top10 = implode('', $top10);
5101
-		} else {
5102
-			$top10 = implode('; ', $top10);
5103
-		}
5104
-		if (I18N::direction() === 'rtl') {
5105
-			$top10 = str_replace(array('[', ']', '(', ')', '+'), array('&rlm;[', '&rlm;]', '&rlm;(', '&rlm;)', '&rlm;+'), $top10);
5106
-		}
5107
-		if ($type === 'list') {
5108
-			return '<ul>' . $top10 . '</ul>';
5109
-		}
5110
-
5111
-		return $top10;
5112
-	}
5113
-
5114
-	/**
5115
-	 * Create a chart of children with no families.
5116
-	 *
5117
-	 * @param string[] $params
5118
-	 *
5119
-	 * @return string
5120
-	 */
5121
-	public function chartNoChildrenFamilies($params = array()) {
5122
-		if (isset($params[0]) && $params[0] != '') {
5123
-			$size = strtolower($params[0]);
5124
-		} else {
5125
-			$size = '220x200';
5126
-		}
5127
-		if (isset($params[1]) && $params[1] != '') {
5128
-			$year1 = $params[1];
5129
-		} else {
5130
-			$year1 = -1;
5131
-		}
5132
-		if (isset($params[2]) && $params[2] != '') {
5133
-			$year2 = $params[2];
5134
-		} else {
5135
-			$year2 = -1;
5136
-		}
5137
-		$sizes = explode('x', $size);
5138
-		if ($year1 >= 0 && $year2 >= 0) {
5139
-			$years = " married.d_year BETWEEN '{$year1}' AND '{$year2}' AND";
5140
-		} else {
5141
-			$years = "";
5142
-		}
5143
-		$max  = 0;
5144
-		$tot  = 0;
5145
-		$rows = $this->runSql(
5146
-			"SELECT" .
5147
-			" COUNT(*) AS count," .
5148
-			" FLOOR(married.d_year/100+1) AS century" .
5149
-			" FROM" .
5150
-			" `##families` AS fam" .
5151
-			" JOIN" .
5152
-			" `##dates` AS married ON (married.d_file = fam.f_file AND married.d_gid = fam.f_id)" .
5153
-			" WHERE" .
5154
-			" f_numchil = 0 AND" .
5155
-			" fam.f_file = {$this->tree->getTreeId()} AND" .
5156
-			$years .
5157
-			" married.d_fact = 'MARR' AND" .
5158
-			" married.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')" .
5159
-			" GROUP BY century ORDER BY century"
5160
-		);
5161
-		if (empty($rows)) {
5162
-			return '';
5163
-		}
5164
-		foreach ($rows as $values) {
5165
-			if ($max < $values['count']) {
5166
-				$max = $values['count'];
5167
-			}
5168
-			$tot += (int) $values['count'];
5169
-		}
5170
-		$unknown = $this->noChildrenFamiliesQuery() - $tot;
5171
-		if ($unknown > $max) {
5172
-			$max = $unknown;
5173
-		}
5174
-		$chm    = "";
5175
-		$chxl   = "0:|";
5176
-		$i      = 0;
5177
-		$counts = array();
5178
-		foreach ($rows as $values) {
5179
-			if ($sizes[0] < 980) {
5180
-				$sizes[0] += 38;
5181
-			}
5182
-			$chxl .= $this->centuryName($values['century']) . "|";
5183
-			$counts[] = round(4095 * $values['count'] / ($max + 1));
5184
-			$chm .= 't' . $values['count'] . ',000000,0,' . $i . ',11,1|';
5185
-			$i++;
5186
-		}
5187
-		$counts[] = round(4095 * $unknown / ($max + 1));
5188
-		$chd      = $this->arrayToExtendedEncoding($counts);
5189
-		$chm .= 't' . $unknown . ',000000,0,' . $i . ',11,1';
5190
-		$chxl .= I18N::translateContext('unknown century', 'Unknown') . "|1:||" . I18N::translate('century') . "|2:|0|";
5191
-		$step = $max + 1;
5192
-		for ($d = (int) ($max + 1); $d > 0; $d--) {
5193
-			if (($max + 1) < ($d * 10 + 1) && fmod(($max + 1), $d) == 0) {
5194
-				$step = $d;
5195
-			}
5196
-		}
5197
-		if ($step == (int) ($max + 1)) {
5198
-			for ($d = (int) ($max); $d > 0; $d--) {
5199
-				if ($max < ($d * 10 + 1) && fmod($max, $d) == 0) {
5200
-					$step = $d;
5201
-				}
5202
-			}
5203
-		}
5204
-		for ($n = $step; $n <= ($max + 1); $n += $step) {
5205
-			$chxl .= $n . "|";
5206
-		}
5207
-		$chxl .= "3:||" . I18N::translate('Total families') . "|";
5208
-
5209
-		return "<img src=\"https://chart.googleapis.com/chart?cht=bvg&amp;chs={$sizes[0]}x{$sizes[1]}&amp;chf=bg,s,ffffff00|c,s,ffffff00&amp;chm=D,FF0000,0,0:" . ($i - 1) . ",3,1|{$chm}&amp;chd=e:{$chd}&amp;chco=0000FF,ffffff00&amp;chbh=30,3&amp;chxt=x,x,y,y&amp;chxl=" . rawurlencode($chxl) . "\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Number of families without children') . "\" title=\"" . I18N::translate('Number of families without children') . "\" />";
5210
-	}
5211
-
5212
-	/**
5213
-	 * Find the couple with the most grandchildren.
5214
-	 *
5215
-	 * @param string   $type
5216
-	 * @param string[] $params
5217
-	 *
5218
-	 * @return string
5219
-	 */
5220
-	private function topTenGrandFamilyQuery($type = 'list', $params = array()) {
5221
-		if (isset($params[0])) {
5222
-			$total = (int) $params[0];
5223
-		} else {
5224
-			$total = 10;
5225
-		}
5226
-		$rows = $this->runSql(
5227
-			"SELECT COUNT(*) AS tot, f_id AS id" .
5228
-			" FROM `##families`" .
5229
-			" JOIN `##link` AS children ON children.l_file = {$this->tree->getTreeId()}" .
5230
-			" JOIN `##link` AS mchildren ON mchildren.l_file = {$this->tree->getTreeId()}" .
5231
-			" JOIN `##link` AS gchildren ON gchildren.l_file = {$this->tree->getTreeId()}" .
5232
-			" WHERE" .
5233
-			" f_file={$this->tree->getTreeId()} AND" .
5234
-			" children.l_from=f_id AND" .
5235
-			" children.l_type='CHIL' AND" .
5236
-			" children.l_to=mchildren.l_from AND" .
5237
-			" mchildren.l_type='FAMS' AND" .
5238
-			" mchildren.l_to=gchildren.l_from AND" .
5239
-			" gchildren.l_type='CHIL'" .
5240
-			" GROUP BY id" .
5241
-			" ORDER BY tot DESC" .
5242
-			" LIMIT " . $total
5243
-		);
5244
-		if (!isset($rows[0])) {
5245
-			return '';
5246
-		}
5247
-		$top10 = array();
5248
-		foreach ($rows as $row) {
5249
-			$family = Family::getInstance($row['id'], $this->tree);
5250
-			if ($family->canShow()) {
5251
-				if ($type === 'list') {
5252
-					$top10[] =
5253
-						'<li><a href="' . $family->getHtmlUrl() . '">' . $family->getFullName() . '</a> - ' .
5254
-						I18N::plural('%s grandchild', '%s grandchildren', $row['tot'], I18N::number($row['tot']));
5255
-				} else {
5256
-					$top10[] =
5257
-						'<a href="' . $family->getHtmlUrl() . '">' . $family->getFullName() . '</a> - ' .
5258
-						I18N::plural('%s grandchild', '%s grandchildren', $row['tot'], I18N::number($row['tot']));
5259
-				}
5260
-			}
5261
-		}
5262
-		if ($type === 'list') {
5263
-			$top10 = implode('', $top10);
5264
-		} else {
5265
-			$top10 = implode('; ', $top10);
5266
-		}
5267
-		if (I18N::direction() === 'rtl') {
5268
-			$top10 = str_replace(array('[', ']', '(', ')', '+'), array('&rlm;[', '&rlm;]', '&rlm;(', '&rlm;)', '&rlm;+'), $top10);
5269
-		}
5270
-		if ($type === 'list') {
5271
-			return '<ul>' . $top10 . '</ul>';
5272
-		}
5273
-
5274
-		return $top10;
5275
-	}
5276
-
5277
-	/**
5278
-	 * Find the couple with the most grandchildren.
5279
-	 *
5280
-	 * @param string[] $params
5281
-	 *
5282
-	 * @return string
5283
-	 */
5284
-	public function topTenLargestGrandFamily($params = array()) {
5285
-		return $this->topTenGrandFamilyQuery('nolist', $params);
5286
-	}
5287
-
5288
-	/**
5289
-	 * Find the couple with the most grandchildren.
5290
-	 *
5291
-	 * @param string[] $params
5292
-	 *
5293
-	 * @return string
5294
-	 */
5295
-	public function topTenLargestGrandFamilyList($params = array()) {
5296
-		return $this->topTenGrandFamilyQuery('list', $params);
5297
-	}
5298
-
5299
-	/**
5300
-	 * Find common surnames.
5301
-	 *
5302
-	 * @param string   $type
5303
-	 * @param bool     $show_tot
5304
-	 * @param string[] $params
5305
-	 *
5306
-	 * @return string
5307
-	 */
5308
-	private function commonSurnamesQuery($type = 'list', $show_tot = false, $params = array()) {
5309
-		$threshold          = empty($params[0]) ? 10 : (int) $params[0];
5310
-		$number_of_surnames = empty($params[1]) ? 10 : (int) $params[1];
5311
-		$sorting            = empty($params[2]) ? 'alpha' : $params[2];
5312
-
5313
-		$surname_list = FunctionsDb::getTopSurnames($this->tree->getTreeId(), $threshold, $number_of_surnames);
5314
-		if (empty($surname_list)) {
5315
-			return '';
5316
-		}
5317
-
5318
-		switch ($sorting) {
5319
-		default:
5320
-		case 'alpha':
5321
-			uksort($surname_list, '\Fisharebest\Webtrees\I18N::strcasecmp');
5322
-			break;
5323
-		case 'count':
5324
-			asort($surname_list);
5325
-			break;
5326
-		case 'rcount':
5327
-			arsort($surname_list);
5328
-			break;
5329
-		}
5330
-
5331
-		// Note that we count/display SPFX SURN, but sort/group under just SURN
5332
-		$surnames = array();
5333
-		foreach (array_keys($surname_list) as $surname) {
5334
-			$surnames = array_merge($surnames, QueryName::surnames($this->tree, $surname, '', false, false));
5335
-		}
5336
-
5337
-		return FunctionsPrintLists::surnameList($surnames, ($type == 'list' ? 1 : 2), $show_tot, 'indilist.php', $this->tree);
5338
-	}
5339
-
5340
-	/**
5341
-	 * Find common surnames.
5342
-	 *
5343
-	 * @return string
5344
-	 */
5345
-	public function getCommonSurname() {
5346
-		$surnames = array_keys(FunctionsDb::getTopSurnames($this->tree->getTreeId(), 1, 1));
5347
-
5348
-		return array_shift($surnames);
5349
-	}
5350
-
5351
-	/**
5352
-	 * Find common surnames.
5353
-	 *
5354
-	 * @param string[] $params
5355
-	 *
5356
-	 * @return string
5357
-	 */
5358
-	public function commonSurnames($params = array('', '', 'alpha')) {
5359
-		return $this->commonSurnamesQuery('nolist', false, $params);
5360
-	}
5361
-
5362
-	/**
5363
-	 * Find common surnames.
5364
-	 *
5365
-	 * @param string[] $params
5366
-	 *
5367
-	 * @return string
5368
-	 */
5369
-	public function commonSurnamesTotals($params = array('', '', 'rcount')) {
5370
-		return $this->commonSurnamesQuery('nolist', true, $params);
5371
-	}
5372
-
5373
-	/**
5374
-	 * Find common surnames.
5375
-	 *
5376
-	 * @param string[] $params
5377
-	 *
5378
-	 * @return string
5379
-	 */
5380
-	public function commonSurnamesList($params = array('', '', 'alpha')) {
5381
-		return $this->commonSurnamesQuery('list', false, $params);
5382
-	}
5383
-
5384
-	/**
5385
-	 * Find common surnames.
5386
-	 *
5387
-	 * @param string[] $params
5388
-	 *
5389
-	 * @return string
5390
-	 */
5391
-	public function commonSurnamesListTotals($params = array('', '', 'rcount')) {
5392
-		return $this->commonSurnamesQuery('list', true, $params);
5393
-	}
5394
-
5395
-	/**
5396
-	 * Create a chart of common surnames.
5397
-	 *
5398
-	 * @param string[] $params
5399
-	 *
5400
-	 * @return string
5401
-	 */
5402
-	public function chartCommonSurnames($params = array()) {
5403
-		$WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
5404
-		$WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
5405
-		$WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
5406
-		$WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
5407
-
5408
-		$size               = empty($params[0]) ? $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y : strtolower($params[0]);
5409
-		$color_from         = empty($params[1]) ? $WT_STATS_CHART_COLOR1 : strtolower($params[1]);
5410
-		$color_to           = empty($params[2]) ? $WT_STATS_CHART_COLOR2 : strtolower($params[2]);
5411
-		$number_of_surnames = empty($params[3]) ? 10 : (int) $params[3];
5412
-
5413
-		$sizes    = explode('x', $size);
5414
-		$tot_indi = $this->totalIndividualsQuery();
5415
-		$surnames = FunctionsDb::getTopSurnames($this->tree->getTreeId(), 0, $number_of_surnames);
5416
-		if (empty($surnames)) {
5417
-			return '';
5418
-		}
5419
-		$SURNAME_TRADITION = $this->tree->getPreference('SURNAME_TRADITION');
5420
-		$all_surnames      = array();
5421
-		$tot               = 0;
5422
-		foreach ($surnames as $surname => $num) {
5423
-			$all_surnames = array_merge($all_surnames, QueryName::surnames($this->tree, I18N::strtoupper($surname), '', false, false));
5424
-			$tot += $num;
5425
-		}
5426
-		$chd = '';
5427
-		$chl = array();
5428
-		foreach ($all_surnames as $surns) {
5429
-			$count_per = 0;
5430
-			$max_name  = 0;
5431
-			$top_name  = '';
5432
-			foreach ($surns as $spfxsurn => $indis) {
5433
-				$per = count($indis);
5434
-				$count_per += $per;
5435
-				// select most common surname from all variants
5436
-				if ($per > $max_name) {
5437
-					$max_name = $per;
5438
-					$top_name = $spfxsurn;
5439
-				}
5440
-			}
5441
-			switch ($SURNAME_TRADITION) {
5442
-			case 'polish':
5443
-				// most common surname should be in male variant (Kowalski, not Kowalska)
5444
-				$top_name = preg_replace(array('/ska$/', '/cka$/', '/dzka$/', '/żka$/'), array('ski', 'cki', 'dzki', 'żki'), $top_name);
5445
-			}
5446
-			$per = round(100 * $count_per / $tot_indi, 0);
5447
-			$chd .= $this->arrayToExtendedEncoding(array($per));
5448
-			$chl[] = $top_name . ' - ' . I18N::number($count_per);
5449
-
5450
-		}
5451
-		$per = round(100 * ($tot_indi - $tot) / $tot_indi, 0);
5452
-		$chd .= $this->arrayToExtendedEncoding(array($per));
5453
-		$chl[] = I18N::translate('Other') . ' - ' . I18N::number($tot_indi - $tot);
5454
-
5455
-		$chart_title = implode(I18N::$list_separator, $chl);
5456
-		$chl         = implode('|', $chl);
5457
-
5458
-		return '<img src="https://chart.googleapis.com/chart?cht=p3&amp;chd=e:' . $chd . '&amp;chs=' . $size . '&amp;chco=' . $color_from . ',' . $color_to . '&amp;chf=bg,s,ffffff00&amp;chl=' . rawurlencode($chl) . '" width="' . $sizes[0] . '" height="' . $sizes[1] . '" alt="' . $chart_title . '" title="' . $chart_title . '" />';
5459
-	}
5460
-
5461
-	/**
5462
-	 * Find common given names.
5463
-	 *
5464
-	 * @param string   $sex
5465
-	 * @param string   $type
5466
-	 * @param bool     $show_tot
5467
-	 * @param string[] $params
5468
-	 *
5469
-	 * @return string
5470
-	 */
5471
-	private function commonGivenQuery($sex = 'B', $type = 'list', $show_tot = false, $params = array()) {
5472
-		if (isset($params[0]) && $params[0] != '' && $params[0] >= 0) {
5473
-			$threshold = (int) $params[0];
5474
-		} else {
5475
-			$threshold = 1;
5476
-		}
5477
-		if (isset($params[1]) && $params[1] != '' && $params[1] >= 0) {
5478
-			$maxtoshow = (int) $params[1];
5479
-		} else {
5480
-			$maxtoshow = 10;
5481
-		}
5482
-
5483
-		switch ($sex) {
5484
-		case 'M':
5485
-			$sex_sql = "i_sex='M'";
5486
-			break;
5487
-		case 'F':
5488
-			$sex_sql = "i_sex='F'";
5489
-			break;
5490
-		case 'U':
5491
-			$sex_sql = "i_sex='U'";
5492
-			break;
5493
-		case 'B':
5494
-		default:
5495
-			$sex_sql = "i_sex<>'U'";
5496
-			break;
5497
-		}
5498
-		$ged_id = $this->tree->getTreeId();
5499
-
5500
-		$rows = Database::prepare("SELECT n_givn, COUNT(*) AS num FROM `##name` JOIN `##individuals` ON (n_id=i_id AND n_file=i_file) WHERE n_file={$ged_id} AND n_type<>'_MARNM' AND n_givn NOT IN ('@P.N.', '') AND LENGTH(n_givn)>1 AND {$sex_sql} GROUP BY n_id, n_givn")
5501
-			->fetchAll();
5502
-		$nameList = array();
5503
-		foreach ($rows as $row) {
5504
-			// Split “John Thomas” into “John” and “Thomas” and count against both totals
5505
-			foreach (explode(' ', $row->n_givn) as $given) {
5506
-				// Exclude initials and particles.
5507
-				if (!preg_match('/^([A-Z]|[a-z]{1,3})$/', $given)) {
5508
-					if (array_key_exists($given, $nameList)) {
5509
-						$nameList[$given] += $row->num;
5510
-					} else {
5511
-						$nameList[$given] = $row->num;
5512
-					}
5513
-				}
5514
-			}
5515
-		}
5516
-		arsort($nameList, SORT_NUMERIC);
5517
-		$nameList = array_slice($nameList, 0, $maxtoshow);
5518
-
5519
-		if (count($nameList) == 0) {
5520
-			return '';
5521
-		}
5522
-		if ($type == 'chart') {
5523
-			return $nameList;
5524
-		}
5525
-		$common = array();
5526
-		foreach ($nameList as $given => $total) {
5527
-			if ($maxtoshow !== -1) {
5528
-				if ($maxtoshow-- <= 0) {
5529
-					break;
5530
-				}
5531
-			}
5532
-			if ($total < $threshold) {
5533
-				break;
5534
-			}
5535
-			if ($show_tot) {
5536
-				$tot = ' (' . I18N::number($total) . ')';
5537
-			} else {
5538
-				$tot = '';
5539
-			}
5540
-			switch ($type) {
5541
-			case 'table':
5542
-				$common[] = '<tr><td>' . $given . '</td><td>' . I18N::number($total) . '</td><td>' . $total . '</td></tr>';
5543
-				break;
5544
-			case 'list':
5545
-				$common[] = '<li><span dir="auto">' . $given . '</span>' . $tot . '</li>';
5546
-				break;
5547
-			case 'nolist':
5548
-				$common[] = '<span dir="auto">' . $given . '</span>' . $tot;
5549
-				break;
5550
-			}
5551
-		}
5552
-		if ($common) {
5553
-			switch ($type) {
5554
-			case 'table':
5555
-				global $controller;
5556
-				$table_id = Uuid::uuid4(); // lists requires a unique ID in case there are multiple lists per page
5557
-				$controller
5558
-					->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
5559
-					->addInlineJavascript('
136
+        for ($i = 0; $i < $c; $i++) {
137
+            $full_tag = $tags[$i];
138
+            // Added for new parameter support
139
+            $params = explode(':', $tags[$i]);
140
+            if (count($params) > 1) {
141
+                $tags[$i] = array_shift($params);
142
+            } else {
143
+                $params = array();
144
+            }
145
+
146
+            // Generate the replacement value for the tag
147
+            if (method_exists($this, $tags[$i])) {
148
+                $new_tags[]   = "#{$full_tag}#";
149
+                $new_values[] = call_user_func_array(array($this, $tags[$i]), array($params));
150
+            }
151
+        }
152
+
153
+        return array($new_tags, $new_values);
154
+    }
155
+
156
+    /**
157
+     * Embed tags in text
158
+     *
159
+     * @param string $text
160
+     *
161
+     * @return string
162
+     */
163
+    public function embedTags($text) {
164
+        if (strpos($text, '#') !== false) {
165
+            list($new_tags, $new_values) = $this->getTags($text);
166
+            $text                        = str_replace($new_tags, $new_values, $text);
167
+        }
168
+
169
+        return $text;
170
+    }
171
+
172
+    /**
173
+     * Get the name used for GEDCOM files and URLs.
174
+     *
175
+     * @return string
176
+     */
177
+    public function gedcomFilename() {
178
+        return $this->tree->getName();
179
+    }
180
+
181
+    /**
182
+     * Get the internal ID number of the tree.
183
+     *
184
+     * @return int
185
+     */
186
+    public function gedcomId() {
187
+        return $this->tree->getTreeId();
188
+    }
189
+
190
+    /**
191
+     * Get the descriptive title of the tree.
192
+     *
193
+     * @return string
194
+     */
195
+    public function gedcomTitle() {
196
+        return $this->tree->getTitleHtml();
197
+    }
198
+
199
+    /**
200
+     * Get information from the GEDCOM's HEAD record.
201
+     *
202
+     * @return string[]
203
+     */
204
+    private function gedcomHead() {
205
+        $title   = '';
206
+        $version = '';
207
+        $source  = '';
208
+
209
+        $head = GedcomRecord::getInstance('HEAD', $this->tree);
210
+        $sour = $head->getFirstFact('SOUR');
211
+        if ($sour) {
212
+            $source  = $sour->getValue();
213
+            $title   = $sour->getAttribute('NAME');
214
+            $version = $sour->getAttribute('VERS');
215
+        }
216
+
217
+        return array($title, $version, $source);
218
+    }
219
+
220
+    /**
221
+     * Get the software originally used to create the GEDCOM file.
222
+     *
223
+     * @return string
224
+     */
225
+    public function gedcomCreatedSoftware() {
226
+        $head = $this->gedcomHead();
227
+
228
+        return $head[0];
229
+    }
230
+
231
+    /**
232
+     * Get the version of software which created the GEDCOM file.
233
+     *
234
+     * @return string
235
+     */
236
+    public function gedcomCreatedVersion() {
237
+        $head = $this->gedcomHead();
238
+        // fix broken version string in Family Tree Maker
239
+        if (strstr($head[1], 'Family Tree Maker ')) {
240
+            $p       = strpos($head[1], '(') + 1;
241
+            $p2      = strpos($head[1], ')');
242
+            $head[1] = substr($head[1], $p, ($p2 - $p));
243
+        }
244
+        // Fix EasyTree version
245
+        if ($head[2] == 'EasyTree') {
246
+            $head[1] = substr($head[1], 1);
247
+        }
248
+
249
+        return $head[1];
250
+    }
251
+
252
+    /**
253
+     * Get the date the GEDCOM file was created.
254
+     *
255
+     * @return string
256
+     */
257
+    public function gedcomDate() {
258
+        $head = GedcomRecord::getInstance('HEAD', $this->tree);
259
+        $fact = $head->getFirstFact('DATE');
260
+        if ($fact) {
261
+            $date = new Date($fact->getValue());
262
+
263
+            return $date->display();
264
+        }
265
+
266
+        return '';
267
+    }
268
+
269
+    /**
270
+     * When was this tree last updated?
271
+     *
272
+     * @return string
273
+     */
274
+    public function gedcomUpdated() {
275
+        $row = Database::prepare(
276
+            "SELECT d_year, d_month, d_day FROM `##dates` WHERE d_julianday1 = (SELECT MAX(d_julianday1) FROM `##dates` WHERE d_file =? AND d_fact='CHAN') LIMIT 1"
277
+        )->execute(array($this->tree->getTreeId()))->fetchOneRow();
278
+        if ($row) {
279
+            $date = new Date("{$row->d_day} {$row->d_month} {$row->d_year}");
280
+
281
+            return $date->display();
282
+        } else {
283
+            return $this->gedcomDate();
284
+        }
285
+    }
286
+
287
+    /**
288
+     * What is the significant individual from this tree?
289
+     *
290
+     * @return string
291
+     */
292
+    public function gedcomRootId() {
293
+        return $this->tree->getPreference('PEDIGREE_ROOT_ID');
294
+    }
295
+
296
+    /**
297
+     * Convert totals into percentages.
298
+     *
299
+     * @param string $total
300
+     * @param string $type
301
+     *
302
+     * @return string
303
+     */
304
+    private function getPercentage($total, $type) {
305
+        switch ($type) {
306
+        case 'individual':
307
+            $type = $this->totalIndividualsQuery();
308
+            break;
309
+        case 'family':
310
+            $type = $this->totalFamiliesQuery();
311
+            break;
312
+        case 'source':
313
+            $type = $this->totalSourcesQuery();
314
+            break;
315
+        case 'note':
316
+            $type = $this->totalNotesQuery();
317
+            break;
318
+        case 'all':
319
+        default:
320
+            $type = $this->totalIndividualsQuery() + $this->totalFamiliesQuery() + $this->totalSourcesQuery();
321
+            break;
322
+        }
323
+        if ($type == 0) {
324
+            return I18N::percentage(0, 1);
325
+        } else {
326
+            return I18N::percentage($total / $type, 1);
327
+        }
328
+    }
329
+
330
+    /**
331
+     * How many GEDCOM records exist in the tree.
332
+     *
333
+     * @return string
334
+     */
335
+    public function totalRecords() {
336
+        return I18N::number($this->totalIndividualsQuery() + $this->totalFamiliesQuery() + $this->totalSourcesQuery());
337
+    }
338
+
339
+    /**
340
+     * How many individuals exist in the tree.
341
+     *
342
+     * @return int
343
+     */
344
+    private function totalIndividualsQuery() {
345
+        return (int) Database::prepare(
346
+            "SELECT COUNT(*) FROM `##individuals` WHERE i_file = :tree_id"
347
+        )->execute(array(
348
+            'tree_id' => $this->tree->getTreeId(),
349
+        ))->fetchOne();
350
+    }
351
+
352
+    /**
353
+     * How many individuals exist in the tree.
354
+     *
355
+     * @return string
356
+     */
357
+    public function totalIndividuals() {
358
+        return I18N::number($this->totalIndividualsQuery());
359
+    }
360
+
361
+    /**
362
+     * How many individuals have one or more sources.
363
+     *
364
+     * @return int
365
+     */
366
+    private function totalIndisWithSourcesQuery() {
367
+        return (int) Database::prepare(
368
+            "SELECT COUNT(DISTINCT i_id)" .
369
+            " FROM `##individuals` JOIN `##link` ON i_id = l_from AND i_file = l_file" .
370
+            " WHERE l_file = :tree_id AND l_type = 'SOUR'"
371
+        )->execute(array(
372
+            'tree_id' => $this->tree->getTreeId(),
373
+        ))->fetchOne();
374
+    }
375
+
376
+    /**
377
+     * How many individuals have one or more sources.
378
+     *
379
+     * @return string
380
+     */
381
+    public function totalIndisWithSources() {
382
+        return I18N::number($this->totalIndisWithSourcesQuery());
383
+    }
384
+
385
+    /**
386
+     * Create a chart showing individuals with/without sources.
387
+     *
388
+     * @param string[] $params
389
+     *
390
+     * @return string
391
+     */
392
+    public function chartIndisWithSources($params = array()) {
393
+        $WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
394
+        $WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
395
+        $WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
396
+        $WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
397
+
398
+        if (isset($params[0]) && $params[0] != '') {
399
+            $size = strtolower($params[0]);
400
+        } else {
401
+            $size = $WT_STATS_S_CHART_X . 'x' . $WT_STATS_S_CHART_Y;
402
+        }
403
+        if (isset($params[1]) && $params[1] != '') {
404
+            $color_from = strtolower($params[1]);
405
+        } else {
406
+            $color_from = $WT_STATS_CHART_COLOR1;
407
+        }
408
+        if (isset($params[2]) && $params[2] != '') {
409
+            $color_to = strtolower($params[2]);
410
+        } else {
411
+            $color_to = $WT_STATS_CHART_COLOR2;
412
+        }
413
+        $sizes    = explode('x', $size);
414
+        $tot_indi = $this->totalIndividualsQuery();
415
+        if ($tot_indi == 0) {
416
+            return '';
417
+        } else {
418
+            $tot_sindi_per = round($this->totalIndisWithSourcesQuery() / $tot_indi, 3);
419
+            $chd           = $this->arrayToExtendedEncoding(array(100 - 100 * $tot_sindi_per, 100 * $tot_sindi_per));
420
+            $chl           = I18N::translate('Without sources') . ' - ' . I18N::percentage(1 - $tot_sindi_per, 1) . '|' .
421
+                I18N::translate('With sources') . ' - ' . I18N::percentage($tot_sindi_per, 1);
422
+            $chart_title = I18N::translate('Individuals with sources');
423
+
424
+            return '<img src="https://chart.googleapis.com/chart?cht=p3&amp;chd=e:' . $chd . '&amp;chs=' . $size . '&amp;chco=' . $color_from . ',' . $color_to . '&amp;chf=bg,s,ffffff00&amp;chl=' . rawurlencode($chl) . '" width="' . $sizes[0] . '" height="' . $sizes[1] . '" alt="' . $chart_title . '" title="' . $chart_title . '">';
425
+        }
426
+    }
427
+
428
+    /**
429
+     * Show the total individuals as a percentage.
430
+     *
431
+     * @return string
432
+     */
433
+    public function totalIndividualsPercentage() {
434
+        return $this->getPercentage($this->totalIndividualsQuery(), 'all');
435
+    }
436
+
437
+    /**
438
+     * Count the total families.
439
+     *
440
+     * @return int
441
+     */
442
+    private function totalFamiliesQuery() {
443
+        return (int) Database::prepare(
444
+            "SELECT COUNT(*) FROM `##families` WHERE f_file = :tree_id"
445
+        )->execute(array(
446
+            'tree_id' => $this->tree->getTreeId(),
447
+        ))->fetchOne();
448
+    }
449
+
450
+    /**
451
+     * Count the total families.
452
+     *
453
+     * @return string
454
+     */
455
+    public function totalFamilies() {
456
+        return I18N::number($this->totalFamiliesQuery());
457
+    }
458
+
459
+    /**
460
+     * Count the families with source records.
461
+     *
462
+     * @return int
463
+     */
464
+    private function totalFamsWithSourcesQuery() {
465
+        return (int) Database::prepare(
466
+            "SELECT COUNT(DISTINCT f_id)" .
467
+            " FROM `##families` JOIN `##link` ON f_id = l_from AND f_file = l_file" .
468
+            " WHERE l_file = :tree_id AND l_type = 'SOUR'"
469
+        )->execute(array(
470
+            'tree_id' => $this->tree->getTreeId(),
471
+        ))->fetchOne();
472
+    }
473
+
474
+    /**
475
+     * Count the families with with source records.
476
+     *
477
+     * @return string
478
+     */
479
+    public function totalFamsWithSources() {
480
+        return I18N::number($this->totalFamsWithSourcesQuery());
481
+    }
482
+
483
+    /**
484
+     * Create a chart of individuals with/without sources.
485
+     *
486
+     * @param string[] $params
487
+     *
488
+     * @return string
489
+     */
490
+    public function chartFamsWithSources($params = array()) {
491
+        $WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
492
+        $WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
493
+        $WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
494
+        $WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
495
+
496
+        if (isset($params[0]) && $params[0] != '') {
497
+            $size = strtolower($params[0]);
498
+        } else {
499
+            $size = $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y;
500
+        }
501
+        if (isset($params[1]) && $params[1] != '') {
502
+            $color_from = strtolower($params[1]);
503
+        } else {
504
+            $color_from = $WT_STATS_CHART_COLOR1;
505
+        }
506
+        if (isset($params[2]) && $params[2] != '') {
507
+            $color_to = strtolower($params[2]);
508
+        } else {
509
+            $color_to = $WT_STATS_CHART_COLOR2;
510
+        }
511
+        $sizes   = explode('x', $size);
512
+        $tot_fam = $this->totalFamiliesQuery();
513
+        if ($tot_fam == 0) {
514
+            return '';
515
+        } else {
516
+            $tot_sfam_per = round($this->totalFamsWithSourcesQuery() / $tot_fam, 3);
517
+            $chd          = $this->arrayToExtendedEncoding(array(100 - 100 * $tot_sfam_per, 100 * $tot_sfam_per));
518
+            $chl          = I18N::translate('Without sources') . ' - ' . I18N::percentage(1 - $tot_sfam_per, 1) . '|' .
519
+                I18N::translate('With sources') . ' - ' . I18N::percentage($tot_sfam_per, 1);
520
+            $chart_title = I18N::translate('Families with sources');
521
+
522
+            return "<img src=\"https://chart.googleapis.com/chart?cht=p3&amp;chd=e:{$chd}&chs={$size}&amp;chco={$color_from},{$color_to}&amp;chf=bg,s,ffffff00&amp;chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . $chart_title . "\" title=\"" . $chart_title . "\" />";
523
+        }
524
+    }
525
+
526
+    /**
527
+     * Show the total families as a percentage.
528
+     *
529
+     * @return string
530
+     */
531
+    public function totalFamiliesPercentage() {
532
+        return $this->getPercentage($this->totalFamiliesQuery(), 'all');
533
+    }
534
+
535
+    /**
536
+     * Count the total number of sources.
537
+     *
538
+     * @return int
539
+     */
540
+    private function totalSourcesQuery() {
541
+        return (int) Database::prepare(
542
+            "SELECT COUNT(*) FROM `##sources` WHERE s_file = :tree_id"
543
+        )->execute(array(
544
+            'tree_id' => $this->tree->getTreeId(),
545
+        ))->fetchOne();
546
+    }
547
+
548
+    /**
549
+     * Count the total number of sources.
550
+     *
551
+     * @return string
552
+     */
553
+    public function totalSources() {
554
+        return I18N::number($this->totalSourcesQuery());
555
+    }
556
+
557
+    /**
558
+     * Show the number of sources as a percentage.
559
+     *
560
+     * @return string
561
+     */
562
+    public function totalSourcesPercentage() {
563
+        return $this->getPercentage($this->totalSourcesQuery(), 'all');
564
+    }
565
+
566
+    /**
567
+     * Count the number of notes.
568
+     *
569
+     * @return int
570
+     */
571
+    private function totalNotesQuery() {
572
+        return (int) Database::prepare(
573
+            "SELECT COUNT(*) FROM `##other` WHERE o_type='NOTE' AND o_file = :tree_id"
574
+        )->execute(array(
575
+            'tree_id' => $this->tree->getTreeId(),
576
+        ))->fetchOne();
577
+    }
578
+
579
+    /**
580
+     * Count the number of notes.
581
+     *
582
+     * @return string
583
+     */
584
+    public function totalNotes() {
585
+        return I18N::number($this->totalNotesQuery());
586
+    }
587
+
588
+    /**
589
+     * Show the number of notes as a percentage.
590
+     *
591
+     * @return string
592
+     */
593
+    public function totalNotesPercentage() {
594
+        return $this->getPercentage($this->totalNotesQuery(), 'all');
595
+    }
596
+
597
+    /**
598
+     * Count the number of repositories.
599
+     *
600
+     * @return int
601
+     */
602
+    private function totalRepositoriesQuery() {
603
+        return (int) Database::prepare(
604
+            "SELECT COUNT(*) FROM `##other` WHERE o_type='REPO' AND o_file = :tree_id"
605
+        )->execute(array(
606
+            'tree_id' => $this->tree->getTreeId(),
607
+        ))->fetchOne();
608
+    }
609
+
610
+    /**
611
+     * Count the number of repositories
612
+     *
613
+     * @return string
614
+     */
615
+    public function totalRepositories() {
616
+        return I18N::number($this->totalRepositoriesQuery());
617
+    }
618
+
619
+    /**
620
+     * Show the total number of repositories as a percentage.
621
+     *
622
+     * @return string
623
+     */
624
+    public function totalRepositoriesPercentage() {
625
+        return $this->getPercentage($this->totalRepositoriesQuery(), 'all');
626
+    }
627
+
628
+    /**
629
+     * Count the surnames.
630
+     *
631
+     * @param string[] $params
632
+     *
633
+     * @return string
634
+     */
635
+    public function totalSurnames($params = array()) {
636
+        if ($params) {
637
+            $opt      = 'IN (' . implode(',', array_fill(0, count($params), '?')) . ')';
638
+            $distinct = '';
639
+        } else {
640
+            $opt      = "IS NOT NULL";
641
+            $distinct = 'DISTINCT';
642
+        }
643
+        $params[] = $this->tree->getTreeId();
644
+        $total =
645
+            Database::prepare(
646
+                "SELECT COUNT({$distinct} n_surn COLLATE '" . I18N::collation() . "')" .
647
+                " FROM `##name`" .
648
+                " WHERE n_surn COLLATE '" . I18N::collation() . "' {$opt} AND n_file=?"
649
+            )->execute(
650
+                $params
651
+            )->fetchOne();
652
+
653
+        return I18N::number($total);
654
+    }
655
+
656
+    /**
657
+     * Count the number of distinct given names, or count the number of
658
+     * occurrences of a specific name or names.
659
+     *
660
+     * @param string[] $params
661
+     *
662
+     * @return string
663
+     */
664
+    public function totalGivennames($params = array()) {
665
+        if ($params) {
666
+            $qs       = implode(',', array_fill(0, count($params), '?'));
667
+            $params[] = $this->tree->getTreeId();
668
+            $total    =
669
+                Database::prepare("SELECT COUNT( n_givn) FROM `##name` WHERE n_givn IN ({$qs}) AND n_file=?")
670
+                    ->execute($params)
671
+                    ->fetchOne();
672
+        } else {
673
+            $total =
674
+                Database::prepare("SELECT COUNT(DISTINCT n_givn) FROM `##name` WHERE n_givn IS NOT NULL AND n_file=?")
675
+                    ->execute(array($this->tree->getTreeId()))
676
+                    ->fetchOne();
677
+        }
678
+
679
+        return I18N::number($total);
680
+    }
681
+
682
+    /**
683
+     * Count the number of events (with dates).
684
+     *
685
+     * @param string[] $params
686
+     *
687
+     * @return string
688
+     */
689
+    public function totalEvents($params = array()) {
690
+        $sql  = "SELECT COUNT(*) AS tot FROM `##dates` WHERE d_file=?";
691
+        $vars = array($this->tree->getTreeId());
692
+
693
+        $no_types = array('HEAD', 'CHAN');
694
+        if ($params) {
695
+            $types = array();
696
+            foreach ($params as $type) {
697
+                if (substr($type, 0, 1) == '!') {
698
+                    $no_types[] = substr($type, 1);
699
+                } else {
700
+                    $types[] = $type;
701
+                }
702
+            }
703
+            if ($types) {
704
+                $sql .= ' AND d_fact IN (' . implode(', ', array_fill(0, count($types), '?')) . ')';
705
+                $vars = array_merge($vars, $types);
706
+            }
707
+        }
708
+        $sql .= ' AND d_fact NOT IN (' . implode(', ', array_fill(0, count($no_types), '?')) . ')';
709
+        $vars = array_merge($vars, $no_types);
710
+
711
+        return I18N::number(Database::prepare($sql)->execute($vars)->fetchOne());
712
+    }
713
+
714
+    /**
715
+     * Count the number of births.
716
+     *
717
+     * @return string
718
+     */
719
+    public function totalEventsBirth() {
720
+        return $this->totalEvents(explode('|', WT_EVENTS_BIRT));
721
+    }
722
+
723
+    /**
724
+     * Count the number of births.
725
+     *
726
+     * @return string
727
+     */
728
+    public function totalBirths() {
729
+        return $this->totalEvents(array('BIRT'));
730
+    }
731
+
732
+    /**
733
+     * Count the number of deaths.
734
+     *
735
+     * @return string
736
+     */
737
+    public function totalEventsDeath() {
738
+        return $this->totalEvents(explode('|', WT_EVENTS_DEAT));
739
+    }
740
+
741
+    /**
742
+     * Count the number of deaths.
743
+     *
744
+     * @return string
745
+     */
746
+    public function totalDeaths() {
747
+        return $this->totalEvents(array('DEAT'));
748
+    }
749
+
750
+    /**
751
+     * Count the number of marriages.
752
+     *
753
+     * @return string
754
+     */
755
+    public function totalEventsMarriage() {
756
+        return $this->totalEvents(explode('|', WT_EVENTS_MARR));
757
+    }
758
+
759
+    /**
760
+     * Count the number of marriages.
761
+     *
762
+     * @return string
763
+     */
764
+    public function totalMarriages() {
765
+        return $this->totalEvents(array('MARR'));
766
+    }
767
+
768
+    /**
769
+     * Count the number of divorces.
770
+     *
771
+     * @return string
772
+     */
773
+    public function totalEventsDivorce() {
774
+        return $this->totalEvents(explode('|', WT_EVENTS_DIV));
775
+    }
776
+
777
+    /**
778
+     * Count the number of divorces.
779
+     *
780
+     * @return string
781
+     */
782
+    public function totalDivorces() {
783
+        return $this->totalEvents(array('DIV'));
784
+    }
785
+
786
+    /**
787
+     * Count the number of other events.
788
+     *
789
+     * @return string
790
+     */
791
+    public function totalEventsOther() {
792
+        $facts    = array_merge(explode('|', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT));
793
+        $no_facts = array();
794
+        foreach ($facts as $fact) {
795
+            $fact       = '!' . str_replace('\'', '', $fact);
796
+            $no_facts[] = $fact;
797
+        }
798
+
799
+        return $this->totalEvents($no_facts);
800
+    }
801
+
802
+    /**
803
+     * Count the number of males.
804
+     *
805
+     * @return int
806
+     */
807
+    private function totalSexMalesQuery() {
808
+        return (int) Database::prepare(
809
+            "SELECT COUNT(*) FROM `##individuals` WHERE i_file = :tree_id AND i_sex = 'M'"
810
+        )->execute(array(
811
+            'tree_id' => $this->tree->getTreeId(),
812
+        ))->fetchOne();
813
+    }
814
+
815
+    /**
816
+     * Count the number of males.
817
+     *
818
+     * @return string
819
+     */
820
+    public function totalSexMales() {
821
+        return I18N::number($this->totalSexMalesQuery());
822
+    }
823
+
824
+    /**
825
+     * Count the number of males
826
+     *
827
+     * @return string
828
+     */
829
+    public function totalSexMalesPercentage() {
830
+        return $this->getPercentage($this->totalSexMalesQuery(), 'individual');
831
+    }
832
+
833
+    /**
834
+     * Count the number of females.
835
+     *
836
+     * @return int
837
+     */
838
+    private function totalSexFemalesQuery() {
839
+        return (int) Database::prepare(
840
+            "SELECT COUNT(*) FROM `##individuals` WHERE i_file = :tree_id AND i_sex = 'F'"
841
+        )->execute(array(
842
+            'tree_id' => $this->tree->getTreeId(),
843
+        ))->fetchOne();
844
+    }
845
+
846
+    /**
847
+     * Count the number of females.
848
+     *
849
+     * @return string
850
+     */
851
+    public function totalSexFemales() {
852
+        return I18N::number($this->totalSexFemalesQuery());
853
+    }
854
+
855
+    /**
856
+     * Count the number of females.
857
+     *
858
+     * @return string
859
+     */
860
+    public function totalSexFemalesPercentage() {
861
+        return $this->getPercentage($this->totalSexFemalesQuery(), 'individual');
862
+    }
863
+
864
+    /**
865
+     * Count the number of individuals with unknown sex.
866
+     *
867
+     * @return int
868
+     */
869
+    private function totalSexUnknownQuery() {
870
+        return (int) Database::prepare(
871
+            "SELECT COUNT(*) FROM `##individuals` WHERE i_file = :tree_id AND i_sex = 'U'"
872
+        )->execute(array(
873
+            'tree_id' => $this->tree->getTreeId(),
874
+        ))->fetchOne();
875
+    }
876
+
877
+    /**
878
+     * Count the number of individuals with unknown sex.
879
+     *
880
+     * @return string
881
+     */
882
+    public function totalSexUnknown() {
883
+        return I18N::number($this->totalSexUnknownQuery());
884
+    }
885
+
886
+    /**
887
+     * Count the number of individuals with unknown sex.
888
+     *
889
+     * @return string
890
+     */
891
+    public function totalSexUnknownPercentage() {
892
+        return $this->getPercentage($this->totalSexUnknownQuery(), 'individual');
893
+    }
894
+
895
+    /**
896
+     * Generate a chart showing sex distribution.
897
+     *
898
+     * @param string[] $params
899
+     *
900
+     * @return string
901
+     */
902
+    public function chartSex($params = array()) {
903
+        $WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
904
+        $WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
905
+
906
+        if (isset($params[0]) && $params[0] != '') {
907
+            $size = strtolower($params[0]);
908
+        } else {
909
+            $size = $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y;
910
+        }
911
+        if (isset($params[1]) && $params[1] != '') {
912
+            $color_female = strtolower($params[1]);
913
+        } else {
914
+            $color_female = 'ffd1dc';
915
+        }
916
+        if (isset($params[2]) && $params[2] != '') {
917
+            $color_male = strtolower($params[2]);
918
+        } else {
919
+            $color_male = '84beff';
920
+        }
921
+        if (isset($params[3]) && $params[3] != '') {
922
+            $color_unknown = strtolower($params[3]);
923
+        } else {
924
+            $color_unknown = '777777';
925
+        }
926
+        $sizes = explode('x', $size);
927
+        // Raw data - for calculation
928
+        $tot_f = $this->totalSexFemalesQuery();
929
+        $tot_m = $this->totalSexMalesQuery();
930
+        $tot_u = $this->totalSexUnknownQuery();
931
+        $tot   = $tot_f + $tot_m + $tot_u;
932
+        // I18N data - for display
933
+        $per_f = $this->totalSexFemalesPercentage();
934
+        $per_m = $this->totalSexMalesPercentage();
935
+        $per_u = $this->totalSexUnknownPercentage();
936
+        if ($tot == 0) {
937
+            return '';
938
+        } elseif ($tot_u > 0) {
939
+            $chd = $this->arrayToExtendedEncoding(array(4095 * $tot_u / $tot, 4095 * $tot_f / $tot, 4095 * $tot_m / $tot));
940
+            $chl =
941
+                I18N::translateContext('unknown people', 'Unknown') . ' - ' . $per_u . '|' .
942
+                I18N::translate('Females') . ' - ' . $per_f . '|' .
943
+                I18N::translate('Males') . ' - ' . $per_m;
944
+            $chart_title =
945
+                I18N::translate('Males') . ' - ' . $per_m . I18N::$list_separator .
946
+                I18N::translate('Females') . ' - ' . $per_f . I18N::$list_separator .
947
+                I18N::translateContext('unknown people', 'Unknown') . ' - ' . $per_u;
948
+
949
+            return "<img src=\"https://chart.googleapis.com/chart?cht=p3&amp;chd=e:{$chd}&amp;chs={$size}&amp;chco={$color_unknown},{$color_female},{$color_male}&amp;chf=bg,s,ffffff00&amp;chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . $chart_title . "\" title=\"" . $chart_title . "\" />";
950
+        } else {
951
+            $chd = $this->arrayToExtendedEncoding(array(4095 * $tot_f / $tot, 4095 * $tot_m / $tot));
952
+            $chl =
953
+                I18N::translate('Females') . ' - ' . $per_f . '|' .
954
+                I18N::translate('Males') . ' - ' . $per_m;
955
+            $chart_title = I18N::translate('Males') . ' - ' . $per_m . I18N::$list_separator .
956
+                I18N::translate('Females') . ' - ' . $per_f;
957
+
958
+            return "<img src=\"https://chart.googleapis.com/chart?cht=p3&amp;chd=e:{$chd}&amp;chs={$size}&amp;chco={$color_female},{$color_male}&amp;chf=bg,s,ffffff00&amp;chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . $chart_title . "\" title=\"" . $chart_title . "\" />";
959
+        }
960
+    }
961
+
962
+    /**
963
+     * Count the number of living individuals.
964
+     *
965
+     * The totalLiving/totalDeceased queries assume that every dead person will
966
+     * have a DEAT record. It will not include individuals who were born more
967
+     * than MAX_ALIVE_AGE years ago, and who have no DEAT record.
968
+     * A good reason to run the “Add missing DEAT records” batch-update!
969
+     *
970
+     * @return int
971
+     */
972
+    private function totalLivingQuery() {
973
+        return (int) Database::prepare(
974
+            "SELECT COUNT(*) FROM `##individuals` WHERE i_file = :tree_id AND i_gedcom NOT REGEXP '\\n1 (" . WT_EVENTS_DEAT . ")'"
975
+        )->execute(array(
976
+            'tree_id' => $this->tree->getTreeId(),
977
+        ))->fetchOne();
978
+    }
979
+
980
+    /**
981
+     * Count the number of living individuals.
982
+     *
983
+     * @return string
984
+     */
985
+    public function totalLiving() {
986
+        return I18N::number($this->totalLivingQuery());
987
+    }
988
+
989
+    /**
990
+     * Count the number of living individuals.
991
+     *
992
+     * @return string
993
+     */
994
+    public function totalLivingPercentage() {
995
+        return $this->getPercentage($this->totalLivingQuery(), 'individual');
996
+    }
997
+
998
+    /**
999
+     * Count the number of dead individuals.
1000
+     *
1001
+     * @return int
1002
+     */
1003
+    private function totalDeceasedQuery() {
1004
+        return (int) Database::prepare(
1005
+            "SELECT COUNT(*) FROM `##individuals` WHERE i_file = :tree_id AND i_gedcom REGEXP '\\n1 (" . WT_EVENTS_DEAT . ")'"
1006
+        )->execute(array(
1007
+            'tree_id' => $this->tree->getTreeId(),
1008
+        ))->fetchOne();
1009
+    }
1010
+
1011
+    /**
1012
+     * Count the number of dead individuals.
1013
+     *
1014
+     * @return string
1015
+     */
1016
+    public function totalDeceased() {
1017
+        return I18N::number($this->totalDeceasedQuery());
1018
+    }
1019
+
1020
+    /**
1021
+     * Count the number of dead individuals.
1022
+     *
1023
+     * @return string
1024
+     */
1025
+    public function totalDeceasedPercentage() {
1026
+        return $this->getPercentage($this->totalDeceasedQuery(), 'individual');
1027
+    }
1028
+
1029
+    /**
1030
+     * Create a chart showing mortality.
1031
+     *
1032
+     * @param string[] $params
1033
+     *
1034
+     * @return string
1035
+     */
1036
+    public function chartMortality($params = array()) {
1037
+        $WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
1038
+        $WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
1039
+
1040
+        if (isset($params[0]) && $params[0] != '') {
1041
+            $size = strtolower($params[0]);
1042
+        } else {
1043
+            $size = $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y;
1044
+        }
1045
+        if (isset($params[1]) && $params[1] != '') {
1046
+            $color_living = strtolower($params[1]);
1047
+        } else {
1048
+            $color_living = 'ffffff';
1049
+        }
1050
+        if (isset($params[2]) && $params[2] != '') {
1051
+            $color_dead = strtolower($params[2]);
1052
+        } else {
1053
+            $color_dead = 'cccccc';
1054
+        }
1055
+        $sizes = explode('x', $size);
1056
+        // Raw data - for calculation
1057
+        $tot_l = $this->totalLivingQuery();
1058
+        $tot_d = $this->totalDeceasedQuery();
1059
+        $tot   = $tot_l + $tot_d;
1060
+        // I18N data - for display
1061
+        $per_l = $this->totalLivingPercentage();
1062
+        $per_d = $this->totalDeceasedPercentage();
1063
+        if ($tot == 0) {
1064
+            return '';
1065
+        } else {
1066
+            $chd = $this->arrayToExtendedEncoding(array(4095 * $tot_l / $tot, 4095 * $tot_d / $tot));
1067
+            $chl =
1068
+                I18N::translate('Living') . ' - ' . $per_l . '|' .
1069
+                I18N::translate('Dead') . ' - ' . $per_d . '|';
1070
+            $chart_title = I18N::translate('Living') . ' - ' . $per_l . I18N::$list_separator .
1071
+                I18N::translate('Dead') . ' - ' . $per_d;
1072
+
1073
+            return "<img src=\"https://chart.googleapis.com/chart?cht=p3&amp;chd=e:{$chd}&amp;chs={$size}&amp;chco={$color_living},{$color_dead}&amp;chf=bg,s,ffffff00&amp;chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . $chart_title . "\" title=\"" . $chart_title . "\" />";
1074
+        }
1075
+    }
1076
+
1077
+    /**
1078
+     * Count the number of users.
1079
+     *
1080
+     * @param string[] $params
1081
+     *
1082
+     * @return string
1083
+     */
1084
+    public function totalUsers($params = array()) {
1085
+        if (isset($params[0])) {
1086
+            $total = count(User::all()) + (int) $params[0];
1087
+        } else {
1088
+            $total = count(User::all());
1089
+        }
1090
+
1091
+        return I18N::number($total);
1092
+    }
1093
+
1094
+    /**
1095
+     * Count the number of administrators.
1096
+     *
1097
+     * @return string
1098
+     */
1099
+    public function totalAdmins() {
1100
+        return I18N::number(count(User::allAdmins()));
1101
+    }
1102
+
1103
+    /**
1104
+     * Count the number of administrators.
1105
+     *
1106
+     * @return string
1107
+     */
1108
+    public function totalNonAdmins() {
1109
+        return I18N::number(count(User::all()) - count(User::allAdmins()));
1110
+    }
1111
+
1112
+    /**
1113
+     * Count the number of media records with a given type.
1114
+     *
1115
+     * @param string $type
1116
+     *
1117
+     * @return int
1118
+     */
1119
+    private function totalMediaType($type = 'all') {
1120
+        if (!in_array($type, $this->_media_types) && $type != 'all' && $type != 'unknown') {
1121
+            return 0;
1122
+        }
1123
+        $sql  = "SELECT COUNT(*) AS tot FROM `##media` WHERE m_file=?";
1124
+        $vars = array($this->tree->getTreeId());
1125
+
1126
+        if ($type != 'all') {
1127
+            if ($type == 'unknown') {
1128
+                // There has to be a better way then this :(
1129
+                foreach ($this->_media_types as $t) {
1130
+                    $sql .= " AND (m_gedcom NOT LIKE ? AND m_gedcom NOT LIKE ?)";
1131
+                    $vars[] = "%3 TYPE {$t}%";
1132
+                    $vars[] = "%1 _TYPE {$t}%";
1133
+                }
1134
+            } else {
1135
+                $sql .= " AND (m_gedcom LIKE ? OR m_gedcom LIKE ?)";
1136
+                $vars[] = "%3 TYPE {$type}%";
1137
+                $vars[] = "%1 _TYPE {$type}%";
1138
+            }
1139
+        }
1140
+
1141
+        return (int) Database::prepare($sql)->execute($vars)->fetchOne();
1142
+    }
1143
+
1144
+    /**
1145
+     * Count the number of media records.
1146
+     *
1147
+     * @return string
1148
+     */
1149
+    public function totalMedia() {
1150
+        return I18N::number($this->totalMediaType('all'));
1151
+    }
1152
+
1153
+    /**
1154
+     * Count the number of media records with type "audio".
1155
+     *
1156
+     * @return string
1157
+     */
1158
+    public function totalMediaAudio() {
1159
+        return I18N::number($this->totalMediaType('audio'));
1160
+    }
1161
+
1162
+    /**
1163
+     * Count the number of media records with type "book".
1164
+     *
1165
+     * @return string
1166
+     */
1167
+    public function totalMediaBook() {
1168
+        return I18N::number($this->totalMediaType('book'));
1169
+    }
1170
+
1171
+    /**
1172
+     * Count the number of media records with type "card".
1173
+     *
1174
+     * @return string
1175
+     */
1176
+    public function totalMediaCard() {
1177
+        return I18N::number($this->totalMediaType('card'));
1178
+    }
1179
+
1180
+    /**
1181
+     * Count the number of media records with type "certificate".
1182
+     *
1183
+     * @return string
1184
+     */
1185
+    public function totalMediaCertificate() {
1186
+        return I18N::number($this->totalMediaType('certificate'));
1187
+    }
1188
+
1189
+    /**
1190
+     * Count the number of media records with type "coat of arms".
1191
+     *
1192
+     * @return string
1193
+     */
1194
+    public function totalMediaCoatOfArms() {
1195
+        return I18N::number($this->totalMediaType('coat'));
1196
+    }
1197
+
1198
+    /**
1199
+     * Count the number of media records with type "document".
1200
+     *
1201
+     * @return string
1202
+     */
1203
+    public function totalMediaDocument() {
1204
+        return I18N::number($this->totalMediaType('document'));
1205
+    }
1206
+
1207
+    /**
1208
+     * Count the number of media records with type "electronic".
1209
+     *
1210
+     * @return string
1211
+     */
1212
+    public function totalMediaElectronic() {
1213
+        return I18N::number($this->totalMediaType('electronic'));
1214
+    }
1215
+
1216
+    /**
1217
+     * Count the number of media records with type "magazine".
1218
+     *
1219
+     * @return string
1220
+     */
1221
+    public function totalMediaMagazine() {
1222
+        return I18N::number($this->totalMediaType('magazine'));
1223
+    }
1224
+
1225
+    /**
1226
+     * Count the number of media records with type "manuscript".
1227
+     *
1228
+     * @return string
1229
+     */
1230
+    public function totalMediaManuscript() {
1231
+        return I18N::number($this->totalMediaType('manuscript'));
1232
+    }
1233
+
1234
+    /**
1235
+     * Count the number of media records with type "map".
1236
+     *
1237
+     * @return string
1238
+     */
1239
+    public function totalMediaMap() {
1240
+        return I18N::number($this->totalMediaType('map'));
1241
+    }
1242
+
1243
+    /**
1244
+     * Count the number of media records with type "microfiche".
1245
+     *
1246
+     * @return string
1247
+     */
1248
+    public function totalMediaFiche() {
1249
+        return I18N::number($this->totalMediaType('fiche'));
1250
+    }
1251
+
1252
+    /**
1253
+     * Count the number of media records with type "microfilm".
1254
+     *
1255
+     * @return string
1256
+     */
1257
+    public function totalMediaFilm() {
1258
+        return I18N::number($this->totalMediaType('film'));
1259
+    }
1260
+
1261
+    /**
1262
+     * Count the number of media records with type "newspaper".
1263
+     *
1264
+     * @return string
1265
+     */
1266
+    public function totalMediaNewspaper() {
1267
+        return I18N::number($this->totalMediaType('newspaper'));
1268
+    }
1269
+
1270
+    /**
1271
+     * Count the number of media records with type "painting".
1272
+     *
1273
+     * @return string
1274
+     */
1275
+    public function totalMediaPainting() {
1276
+        return I18N::number($this->totalMediaType('painting'));
1277
+    }
1278
+
1279
+    /**
1280
+     * Count the number of media records with type "photograph".
1281
+     *
1282
+     * @return string
1283
+     */
1284
+    public function totalMediaPhoto() {
1285
+        return I18N::number($this->totalMediaType('photo'));
1286
+    }
1287
+
1288
+    /**
1289
+     * Count the number of media records with type "tombstone".
1290
+     *
1291
+     * @return string
1292
+     */
1293
+    public function totalMediaTombstone() {
1294
+        return I18N::number($this->totalMediaType('tombstone'));
1295
+    }
1296
+
1297
+    /**
1298
+     * Count the number of media records with type "video".
1299
+     *
1300
+     * @return string
1301
+     */
1302
+    public function totalMediaVideo() {
1303
+        return I18N::number($this->totalMediaType('video'));
1304
+    }
1305
+
1306
+    /**
1307
+     * Count the number of media records with type "other".
1308
+     *
1309
+     * @return string
1310
+     */
1311
+    public function totalMediaOther() {
1312
+        return I18N::number($this->totalMediaType('other'));
1313
+    }
1314
+
1315
+    /**
1316
+     * Count the number of media records with type "unknown".
1317
+     *
1318
+     * @return string
1319
+     */
1320
+    public function totalMediaUnknown() {
1321
+        return I18N::number($this->totalMediaType('unknown'));
1322
+    }
1323
+
1324
+    /**
1325
+     * Create a chart of media types.
1326
+     *
1327
+     * @param string[] $params
1328
+     *
1329
+     * @return string
1330
+     */
1331
+    public function chartMedia($params = array()) {
1332
+        $WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
1333
+        $WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
1334
+        $WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
1335
+        $WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
1336
+
1337
+        if (isset($params[0]) && $params[0] != '') {
1338
+            $size = strtolower($params[0]);
1339
+        } else {
1340
+            $size = $WT_STATS_S_CHART_X . 'x' . $WT_STATS_S_CHART_Y;
1341
+        }
1342
+        if (isset($params[1]) && $params[1] != '') {
1343
+            $color_from = strtolower($params[1]);
1344
+        } else {
1345
+            $color_from = $WT_STATS_CHART_COLOR1;
1346
+        }
1347
+        if (isset($params[2]) && $params[2] != '') {
1348
+            $color_to = strtolower($params[2]);
1349
+        } else {
1350
+            $color_to = $WT_STATS_CHART_COLOR2;
1351
+        }
1352
+        $sizes = explode('x', $size);
1353
+        $tot   = $this->totalMediaType('all');
1354
+        // Beware divide by zero
1355
+        if ($tot == 0) {
1356
+            return I18N::translate('None');
1357
+        }
1358
+        // Build a table listing only the media types actually present in the GEDCOM
1359
+        $mediaCounts = array();
1360
+        $mediaTypes  = "";
1361
+        $chart_title = "";
1362
+        $c           = 0;
1363
+        $max         = 0;
1364
+        $media       = array();
1365
+        foreach ($this->_media_types as $type) {
1366
+            $count = $this->totalMediaType($type);
1367
+            if ($count > 0) {
1368
+                $media[$type] = $count;
1369
+                if ($count > $max) {
1370
+                    $max = $count;
1371
+                }
1372
+                $c += $count;
1373
+            }
1374
+        }
1375
+        $count = $this->totalMediaType('unknown');
1376
+        if ($count > 0) {
1377
+            $media['unknown'] = $tot - $c;
1378
+            if ($tot - $c > $max) {
1379
+                $max = $count;
1380
+            }
1381
+        }
1382
+        if (($max / $tot) > 0.6 && count($media) > 10) {
1383
+            arsort($media);
1384
+            $media = array_slice($media, 0, 10);
1385
+            $c     = $tot;
1386
+            foreach ($media as $cm) {
1387
+                $c -= $cm;
1388
+            }
1389
+            if (isset($media['other'])) {
1390
+                $media['other'] += $c;
1391
+            } else {
1392
+                $media['other'] = $c;
1393
+            }
1394
+        }
1395
+        asort($media);
1396
+        foreach ($media as $type => $count) {
1397
+            $mediaCounts[] = round(100 * $count / $tot, 0);
1398
+            $mediaTypes .= GedcomTag::getFileFormTypeValue($type) . ' - ' . I18N::number($count) . '|';
1399
+            $chart_title .= GedcomTag::getFileFormTypeValue($type) . ' (' . $count . '), ';
1400
+        }
1401
+        $chart_title = substr($chart_title, 0, -2);
1402
+        $chd         = $this->arrayToExtendedEncoding($mediaCounts);
1403
+        $chl         = substr($mediaTypes, 0, -1);
1404
+
1405
+        return "<img src=\"https://chart.googleapis.com/chart?cht=p3&amp;chd=e:{$chd}&amp;chs={$size}&amp;chco={$color_from},{$color_to}&amp;chf=bg,s,ffffff00&amp;chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . $chart_title . "\" title=\"" . $chart_title . "\" />";
1406
+    }
1407
+
1408
+    /**
1409
+     * Birth and Death
1410
+     *
1411
+     * @param string $type
1412
+     * @param string $life_dir
1413
+     * @param string $birth_death
1414
+     *
1415
+     * @return string
1416
+     */
1417
+    private function mortalityQuery($type = 'full', $life_dir = 'ASC', $birth_death = 'BIRT') {
1418
+        if ($birth_death == 'MARR') {
1419
+            $query_field = "'MARR'";
1420
+        } elseif ($birth_death == 'DIV') {
1421
+            $query_field = "'DIV'";
1422
+        } elseif ($birth_death == 'BIRT') {
1423
+            $query_field = "'BIRT'";
1424
+        } else {
1425
+            $query_field = "'DEAT'";
1426
+        }
1427
+        if ($life_dir == 'ASC') {
1428
+            $dmod = 'MIN';
1429
+        } else {
1430
+            $dmod = 'MAX';
1431
+        }
1432
+        $rows = $this->runSql(
1433
+            "SELECT d_year, d_type, d_fact, d_gid" .
1434
+            " FROM `##dates`" .
1435
+            " WHERE d_file={$this->tree->getTreeId()} AND d_fact IN ({$query_field}) AND d_julianday1=(" .
1436
+            " SELECT {$dmod}( d_julianday1 )" .
1437
+            " FROM `##dates`" .
1438
+            " WHERE d_file={$this->tree->getTreeId()} AND d_fact IN ({$query_field}) AND d_julianday1<>0 )" .
1439
+            " LIMIT 1"
1440
+        );
1441
+        if (!isset($rows[0])) {
1442
+            return '';
1443
+        }
1444
+        $row    = $rows[0];
1445
+        $record = GedcomRecord::getInstance($row['d_gid'], $this->tree);
1446
+        switch ($type) {
1447
+        default:
1448
+        case 'full':
1449
+            if ($record->canShow()) {
1450
+                $result = $record->formatList('span', false, $record->getFullName());
1451
+            } else {
1452
+                $result = I18N::translate('This information is private and cannot be shown.');
1453
+            }
1454
+            break;
1455
+        case 'year':
1456
+            $date   = new Date($row['d_type'] . ' ' . $row['d_year']);
1457
+            $result = $date->display();
1458
+            break;
1459
+        case 'name':
1460
+            $result = "<a href=\"" . $record->getHtmlUrl() . "\">" . $record->getFullName() . "</a>";
1461
+            break;
1462
+        case 'place':
1463
+            $fact = GedcomRecord::getInstance($row['d_gid'], $this->tree)->getFirstFact($row['d_fact']);
1464
+            if ($fact) {
1465
+                $result = FunctionsPrint::formatFactPlace($fact, true, true, true);
1466
+            } else {
1467
+                $result = I18N::translate('Private');
1468
+            }
1469
+            break;
1470
+        }
1471
+
1472
+        return $result;
1473
+    }
1474
+
1475
+    /**
1476
+     * Places
1477
+     *
1478
+     * @param string $what
1479
+     * @param string $fact
1480
+     * @param int    $parent
1481
+     * @param bool   $country
1482
+     *
1483
+     * @return int[]|string[][]
1484
+     */
1485
+    public function statsPlaces($what = 'ALL', $fact = '', $parent = 0, $country = false) {
1486
+        if ($fact) {
1487
+            if ($what == 'INDI') {
1488
+                $rows = Database::prepare(
1489
+                    "SELECT i_gedcom AS ged FROM `##individuals` WHERE i_file = :tree_id AND i_gedcom LIKE '%\n2 PLAC %'"
1490
+                )->execute(array(
1491
+                    'tree_id' => $this->tree->getTreeId(),
1492
+                ))->fetchAll();
1493
+            } elseif ($what == 'FAM') {
1494
+                $rows = Database::prepare(
1495
+                    "SELECT f_gedcom AS ged FROM `##families` WHERE f_file = :tree_id AND f_gedcom LIKE '%\n2 PLAC %'"
1496
+                )->execute(array(
1497
+                    'tree_id' => $this->tree->getTreeId(),
1498
+                ))->fetchAll();
1499
+            }
1500
+            $placelist = array();
1501
+            foreach ($rows as $row) {
1502
+                if (preg_match('/\n1 ' . $fact . '(?:\n[2-9].*)*\n2 PLAC (.+)/', $row->ged, $match)) {
1503
+                    if ($country) {
1504
+                        $tmp   = explode(Place::GEDCOM_SEPARATOR, $match[1]);
1505
+                        $place = end($tmp);
1506
+                    } else {
1507
+                        $place = $match[1];
1508
+                    }
1509
+                    if (!isset($placelist[$place])) {
1510
+                        $placelist[$place] = 1;
1511
+                    } else {
1512
+                        $placelist[$place]++;
1513
+                    }
1514
+                }
1515
+            }
1516
+
1517
+            return $placelist;
1518
+        } elseif ($parent > 0) {
1519
+            // used by placehierarchy googlemap module
1520
+            if ($what == 'INDI') {
1521
+                $join = " JOIN `##individuals` ON pl_file = i_file AND pl_gid = i_id";
1522
+            } elseif ($what == 'FAM') {
1523
+                $join = " JOIN `##families` ON pl_file = f_file AND pl_gid = f_id";
1524
+            } else {
1525
+                $join = "";
1526
+            }
1527
+            $rows = $this->runSql(
1528
+                " SELECT" .
1529
+                " p_place AS place," .
1530
+                " COUNT(*) AS tot" .
1531
+                " FROM" .
1532
+                " `##places`" .
1533
+                " JOIN `##placelinks` ON pl_file=p_file AND p_id=pl_p_id" .
1534
+                $join .
1535
+                " WHERE" .
1536
+                " p_id={$parent} AND" .
1537
+                " p_file={$this->tree->getTreeId()}" .
1538
+                " GROUP BY place"
1539
+            );
1540
+
1541
+            return $rows;
1542
+        } else {
1543
+            if ($what == 'INDI') {
1544
+                $join = " JOIN `##individuals` ON pl_file = i_file AND pl_gid = i_id";
1545
+            } elseif ($what == 'FAM') {
1546
+                $join = " JOIN `##families` ON pl_file = f_file AND pl_gid = f_id";
1547
+            } else {
1548
+                $join = "";
1549
+            }
1550
+            $rows = $this->runSql(
1551
+                " SELECT" .
1552
+                " p_place AS country," .
1553
+                " COUNT(*) AS tot" .
1554
+                " FROM" .
1555
+                " `##places`" .
1556
+                " JOIN `##placelinks` ON pl_file=p_file AND p_id=pl_p_id" .
1557
+                $join .
1558
+                " WHERE" .
1559
+                " p_file={$this->tree->getTreeId()}" .
1560
+                " AND p_parent_id='0'" .
1561
+                " GROUP BY country ORDER BY tot DESC, country ASC"
1562
+            );
1563
+
1564
+            return $rows;
1565
+        }
1566
+    }
1567
+
1568
+    /**
1569
+     * Count total places.
1570
+     *
1571
+     * @return int
1572
+     */
1573
+    private function totalPlacesQuery() {
1574
+        return
1575
+            (int) Database::prepare("SELECT COUNT(*) FROM `##places` WHERE p_file=?")
1576
+                ->execute(array($this->tree->getTreeId()))
1577
+                ->fetchOne();
1578
+    }
1579
+
1580
+    /**
1581
+     * Count total places.
1582
+     *
1583
+     * @return string
1584
+     */
1585
+    public function totalPlaces() {
1586
+        return I18N::number($this->totalPlacesQuery());
1587
+    }
1588
+
1589
+    /**
1590
+     * Create a chart showing where events occurred.
1591
+     *
1592
+     * @param string[] $params
1593
+     *
1594
+     * @return string
1595
+     */
1596
+    public function chartDistribution($params = array()) {
1597
+        $WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
1598
+        $WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
1599
+        $WT_STATS_CHART_COLOR3 = Theme::theme()->parameter('distribution-chart-low-values');
1600
+        $WT_STATS_MAP_X        = Theme::theme()->parameter('distribution-chart-x');
1601
+        $WT_STATS_MAP_Y        = Theme::theme()->parameter('distribution-chart-y');
1602
+
1603
+        if (isset($params[0])) {
1604
+            $chart_shows = $params[0];
1605
+        } else {
1606
+            $chart_shows = 'world';
1607
+        }
1608
+        if (isset($params[1])) {
1609
+            $chart_type = $params[1];
1610
+        } else {
1611
+            $chart_type = '';
1612
+        }
1613
+        if (isset($params[2])) {
1614
+            $surname = $params[2];
1615
+        } else {
1616
+            $surname = '';
1617
+        }
1618
+
1619
+        if ($this->totalPlacesQuery() == 0) {
1620
+            return '';
1621
+        }
1622
+        // Get the country names for each language
1623
+        $country_to_iso3166 = array();
1624
+        foreach (I18N::activeLocales() as $locale) {
1625
+            I18N::init($locale->languageTag());
1626
+            $countries = $this->getAllCountries();
1627
+            foreach ($this->iso3166() as $three => $two) {
1628
+                $country_to_iso3166[$three]             = $two;
1629
+                $country_to_iso3166[$countries[$three]] = $two;
1630
+            }
1631
+        }
1632
+        I18N::init(WT_LOCALE);
1633
+        switch ($chart_type) {
1634
+        case 'surname_distribution_chart':
1635
+            if ($surname == "") {
1636
+                $surname = $this->getCommonSurname();
1637
+            }
1638
+            $chart_title = I18N::translate('Surname distribution chart') . ': ' . $surname;
1639
+            // Count how many people are events in each country
1640
+            $surn_countries = array();
1641
+            $indis          = QueryName::individuals($this->tree, I18N::strtoupper($surname), '', '', false, false);
1642
+            foreach ($indis as $person) {
1643
+                if (preg_match_all('/^2 PLAC (?:.*, *)*(.*)/m', $person->getGedcom(), $matches)) {
1644
+                    // webtrees uses 3 letter country codes and localised country names, but google uses 2 letter codes.
1645
+                    foreach ($matches[1] as $country) {
1646
+                        if (array_key_exists($country, $country_to_iso3166)) {
1647
+                            if (array_key_exists($country_to_iso3166[$country], $surn_countries)) {
1648
+                                $surn_countries[$country_to_iso3166[$country]]++;
1649
+                            } else {
1650
+                                $surn_countries[$country_to_iso3166[$country]] = 1;
1651
+                            }
1652
+                        }
1653
+                    }
1654
+                }
1655
+            };
1656
+            break;
1657
+        case 'birth_distribution_chart':
1658
+            $chart_title = I18N::translate('Birth by country');
1659
+            // Count how many people were born in each country
1660
+            $surn_countries = array();
1661
+            $b_countries    = $this->statsPlaces('INDI', 'BIRT', 0, true);
1662
+            foreach ($b_countries as $place => $count) {
1663
+                $country = $place;
1664
+                if (array_key_exists($country, $country_to_iso3166)) {
1665
+                    if (!isset($surn_countries[$country_to_iso3166[$country]])) {
1666
+                        $surn_countries[$country_to_iso3166[$country]] = $count;
1667
+                    } else {
1668
+                        $surn_countries[$country_to_iso3166[$country]] += $count;
1669
+                    }
1670
+                }
1671
+            }
1672
+            break;
1673
+        case 'death_distribution_chart':
1674
+            $chart_title = I18N::translate('Death by country');
1675
+            // Count how many people were death in each country
1676
+            $surn_countries = array();
1677
+            $d_countries    = $this->statsPlaces('INDI', 'DEAT', 0, true);
1678
+            foreach ($d_countries as $place => $count) {
1679
+                $country = $place;
1680
+                if (array_key_exists($country, $country_to_iso3166)) {
1681
+                    if (!isset($surn_countries[$country_to_iso3166[$country]])) {
1682
+                        $surn_countries[$country_to_iso3166[$country]] = $count;
1683
+                    } else {
1684
+                        $surn_countries[$country_to_iso3166[$country]] += $count;
1685
+                    }
1686
+                }
1687
+            }
1688
+            break;
1689
+        case 'marriage_distribution_chart':
1690
+            $chart_title = I18N::translate('Marriage by country');
1691
+            // Count how many families got marriage in each country
1692
+            $surn_countries = array();
1693
+            $m_countries    = $this->statsPlaces('FAM');
1694
+            // webtrees uses 3 letter country codes and localised country names, but google uses 2 letter codes.
1695
+            foreach ($m_countries as $place) {
1696
+                $country = $place['country'];
1697
+                if (array_key_exists($country, $country_to_iso3166)) {
1698
+                    if (!isset($surn_countries[$country_to_iso3166[$country]])) {
1699
+                        $surn_countries[$country_to_iso3166[$country]] = $place['tot'];
1700
+                    } else {
1701
+                        $surn_countries[$country_to_iso3166[$country]] += $place['tot'];
1702
+                    }
1703
+                }
1704
+            }
1705
+            break;
1706
+        case 'indi_distribution_chart':
1707
+        default:
1708
+            $chart_title = I18N::translate('Individual distribution chart');
1709
+            // Count how many people have events in each country
1710
+            $surn_countries = array();
1711
+            $a_countries    = $this->statsPlaces('INDI');
1712
+            // webtrees uses 3 letter country codes and localised country names, but google uses 2 letter codes.
1713
+            foreach ($a_countries as $place) {
1714
+                $country = $place['country'];
1715
+                if (array_key_exists($country, $country_to_iso3166)) {
1716
+                    if (!isset($surn_countries[$country_to_iso3166[$country]])) {
1717
+                        $surn_countries[$country_to_iso3166[$country]] = $place['tot'];
1718
+                    } else {
1719
+                        $surn_countries[$country_to_iso3166[$country]] += $place['tot'];
1720
+                    }
1721
+                }
1722
+            }
1723
+            break;
1724
+        }
1725
+        $chart_url = "https://chart.googleapis.com/chart?cht=t&amp;chtm=" . $chart_shows;
1726
+        $chart_url .= "&amp;chco=" . $WT_STATS_CHART_COLOR1 . "," . $WT_STATS_CHART_COLOR3 . "," . $WT_STATS_CHART_COLOR2; // country colours
1727
+        $chart_url .= "&amp;chf=bg,s,ECF5FF"; // sea colour
1728
+        $chart_url .= "&amp;chs=" . $WT_STATS_MAP_X . "x" . $WT_STATS_MAP_Y;
1729
+        $chart_url .= "&amp;chld=" . implode('', array_keys($surn_countries)) . "&amp;chd=s:";
1730
+        foreach ($surn_countries as $count) {
1731
+            $chart_url .= substr(WT_GOOGLE_CHART_ENCODING, (int) ($count / max($surn_countries) * 61), 1);
1732
+        }
1733
+        $chart = '<div id="google_charts" class="center">';
1734
+        $chart .= '<p>' . $chart_title . '</p>';
1735
+        $chart .= '<div><img src="' . $chart_url . '" alt="' . $chart_title . '" title="' . $chart_title . '" class="gchart" /><br>';
1736
+        $chart .= '<table class="center"><tr>';
1737
+        $chart .= '<td bgcolor="#' . $WT_STATS_CHART_COLOR2 . '" width="12"></td><td>' . I18N::translate('Highest population') . '</td>';
1738
+        $chart .= '<td bgcolor="#' . $WT_STATS_CHART_COLOR3 . '" width="12"></td><td>' . I18N::translate('Lowest population') . '</td>';
1739
+        $chart .= '<td bgcolor="#' . $WT_STATS_CHART_COLOR1 . '" width="12"></td><td>' . I18N::translate('Nobody at all') . '</td>';
1740
+        $chart .= '</tr></table></div></div>';
1741
+
1742
+        return $chart;
1743
+    }
1744
+
1745
+    /**
1746
+     * A list of common countries.
1747
+     *
1748
+     * @return string
1749
+     */
1750
+    public function commonCountriesList() {
1751
+        $countries = $this->statsPlaces();
1752
+        if (empty($countries)) {
1753
+            return '';
1754
+        }
1755
+        $top10 = array();
1756
+        $i     = 1;
1757
+        // Get the country names for each language
1758
+        $country_names = array();
1759
+        foreach (I18N::activeLocales() as $locale) {
1760
+            I18N::init($locale->languageTag());
1761
+            $all_countries = $this->getAllCountries();
1762
+            foreach ($all_countries as $country_code => $country_name) {
1763
+                $country_names[$country_name] = $country_code;
1764
+            }
1765
+        }
1766
+        I18N::init(WT_LOCALE);
1767
+        $all_db_countries = array();
1768
+        foreach ($countries as $place) {
1769
+            $country = trim($place['country']);
1770
+            if (array_key_exists($country, $country_names)) {
1771
+                if (!isset($all_db_countries[$country_names[$country]][$country])) {
1772
+                    $all_db_countries[$country_names[$country]][$country] = (int) $place['tot'];
1773
+                } else {
1774
+                    $all_db_countries[$country_names[$country]][$country] += (int) $place['tot'];
1775
+                }
1776
+            }
1777
+        }
1778
+        // get all the user’s countries names
1779
+        $all_countries = $this->getAllCountries();
1780
+        foreach ($all_db_countries as $country_code => $country) {
1781
+            $top10[] = '<li>';
1782
+            foreach ($country as $country_name => $tot) {
1783
+                $tmp   = new Place($country_name, $this->tree);
1784
+                $place = '<a href="' . $tmp->getURL() . '" class="list_item">' . $all_countries[$country_code] . '</a>';
1785
+                $top10[] .= $place . ' - ' . I18N::number($tot);
1786
+            }
1787
+            $top10[] .= '</li>';
1788
+            if ($i++ == 10) {
1789
+                break;
1790
+            }
1791
+        }
1792
+        $top10 = implode('', $top10);
1793
+
1794
+        return '<ul>' . $top10 . '</ul>';
1795
+    }
1796
+
1797
+    /**
1798
+     * A list of common birth places.
1799
+     *
1800
+     * @return string
1801
+     */
1802
+    public function commonBirthPlacesList() {
1803
+        $places = $this->statsPlaces('INDI', 'BIRT');
1804
+        $top10  = array();
1805
+        $i      = 1;
1806
+        arsort($places);
1807
+        foreach ($places as $place => $count) {
1808
+            $tmp     = new Place($place, $this->tree);
1809
+            $place   = '<a href="' . $tmp->getURL() . '" class="list_item">' . $tmp->getFullName() . '</a>';
1810
+            $top10[] = '<li>' . $place . ' - ' . I18N::number($count) . '</li>';
1811
+            if ($i++ == 10) {
1812
+                break;
1813
+            }
1814
+        }
1815
+        $top10 = implode('', $top10);
1816
+
1817
+        return '<ul>' . $top10 . '</ul>';
1818
+    }
1819
+
1820
+    /**
1821
+     * A list of common death places.
1822
+     *
1823
+     * @return string
1824
+     */
1825
+    public function commonDeathPlacesList() {
1826
+        $places = $this->statsPlaces('INDI', 'DEAT');
1827
+        $top10  = array();
1828
+        $i      = 1;
1829
+        arsort($places);
1830
+        foreach ($places as $place => $count) {
1831
+            $tmp     = new Place($place, $this->tree);
1832
+            $place   = '<a href="' . $tmp->getURL() . '" class="list_item">' . $tmp->getFullName() . '</a>';
1833
+            $top10[] = '<li>' . $place . ' - ' . I18N::number($count) . '</li>';
1834
+            if ($i++ == 10) {
1835
+                break;
1836
+            }
1837
+        }
1838
+        $top10 = implode('', $top10);
1839
+
1840
+        return '<ul>' . $top10 . '</ul>';
1841
+    }
1842
+
1843
+    /**
1844
+     * A list of common marriage places.
1845
+     *
1846
+     * @return string
1847
+     */
1848
+    public function commonMarriagePlacesList() {
1849
+        $places = $this->statsPlaces('FAM', 'MARR');
1850
+        $top10  = array();
1851
+        $i      = 1;
1852
+        arsort($places);
1853
+        foreach ($places as $place => $count) {
1854
+            $tmp     = new Place($place, $this->tree);
1855
+            $place   = '<a href="' . $tmp->getURL() . '" class="list_item">' . $tmp->getFullName() . '</a>';
1856
+            $top10[] = '<li>' . $place . ' - ' . I18N::number($count) . '</li>';
1857
+            if ($i++ == 10) {
1858
+                break;
1859
+            }
1860
+        }
1861
+        $top10 = implode('', $top10);
1862
+
1863
+        return '<ul>' . $top10 . '</ul>';
1864
+    }
1865
+
1866
+    /**
1867
+     * Create a chart of birth places.
1868
+     *
1869
+     * @param bool     $simple
1870
+     * @param bool     $sex
1871
+     * @param int      $year1
1872
+     * @param int      $year2
1873
+     * @param string[] $params
1874
+     *
1875
+     * @return array|string
1876
+     */
1877
+    public function statsBirthQuery($simple = true, $sex = false, $year1 = -1, $year2 = -1, $params = array()) {
1878
+        $WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
1879
+        $WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
1880
+        $WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
1881
+        $WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
1882
+
1883
+        if ($simple) {
1884
+            $sql =
1885
+                "SELECT FLOOR(d_year/100+1) AS century, COUNT(*) AS total FROM `##dates` " .
1886
+                "WHERE " .
1887
+                "d_file={$this->tree->getTreeId()} AND " .
1888
+                "d_year<>0 AND " .
1889
+                "d_fact='BIRT' AND " .
1890
+                "d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')";
1891
+        } elseif ($sex) {
1892
+            $sql =
1893
+                "SELECT d_month, i_sex, COUNT(*) AS total FROM `##dates` " .
1894
+                "JOIN `##individuals` ON d_file = i_file AND d_gid = i_id " .
1895
+                "WHERE " .
1896
+                "d_file={$this->tree->getTreeId()} AND " .
1897
+                "d_fact='BIRT' AND " .
1898
+                "d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')";
1899
+        } else {
1900
+            $sql =
1901
+                "SELECT d_month, COUNT(*) AS total FROM `##dates` " .
1902
+                "WHERE " .
1903
+                "d_file={$this->tree->getTreeId()} AND " .
1904
+                "d_fact='BIRT' AND " .
1905
+                "d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')";
1906
+        }
1907
+        if ($year1 >= 0 && $year2 >= 0) {
1908
+            $sql .= " AND d_year BETWEEN '{$year1}' AND '{$year2}'";
1909
+        }
1910
+        if ($simple) {
1911
+            $sql .= " GROUP BY century ORDER BY century";
1912
+        } else {
1913
+            $sql .= " GROUP BY d_month";
1914
+            if ($sex) {
1915
+                $sql .= ", i_sex";
1916
+            }
1917
+        }
1918
+        $rows = $this->runSql($sql);
1919
+        if ($simple) {
1920
+            if (isset($params[0]) && $params[0] != '') {
1921
+                $size = strtolower($params[0]);
1922
+            } else {
1923
+                $size = $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y;
1924
+            }
1925
+            if (isset($params[1]) && $params[1] != '') {
1926
+                $color_from = strtolower($params[1]);
1927
+            } else {
1928
+                $color_from = $WT_STATS_CHART_COLOR1;
1929
+            }
1930
+            if (isset($params[2]) && $params[2] != '') {
1931
+                $color_to = strtolower($params[2]);
1932
+            } else {
1933
+                $color_to = $WT_STATS_CHART_COLOR2;
1934
+            }
1935
+            $sizes = explode('x', $size);
1936
+            $tot   = 0;
1937
+            foreach ($rows as $values) {
1938
+                $tot += $values['total'];
1939
+            }
1940
+            // Beware divide by zero
1941
+            if ($tot == 0) {
1942
+                return '';
1943
+            }
1944
+            $centuries = "";
1945
+            $counts    = array();
1946
+            foreach ($rows as $values) {
1947
+                $counts[] = round(100 * $values['total'] / $tot, 0);
1948
+                $centuries .= $this->centuryName($values['century']) . ' - ' . I18N::number($values['total']) . '|';
1949
+            }
1950
+            $chd = $this->arrayToExtendedEncoding($counts);
1951
+            $chl = rawurlencode(substr($centuries, 0, -1));
1952
+
1953
+            return "<img src=\"https://chart.googleapis.com/chart?cht=p3&amp;chd=e:{$chd}&amp;chs={$size}&amp;chco={$color_from},{$color_to}&amp;chf=bg,s,ffffff00&amp;chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Births by century') . "\" title=\"" . I18N::translate('Births by century') . "\" />";
1954
+        } else {
1955
+            return $rows;
1956
+        }
1957
+    }
1958
+
1959
+    /**
1960
+     * Create a chart of death places.
1961
+     *
1962
+     * @param bool     $simple
1963
+     * @param bool     $sex
1964
+     * @param int      $year1
1965
+     * @param int      $year2
1966
+     * @param string[] $params
1967
+     *
1968
+     * @return array|string
1969
+     */
1970
+    public function statsDeathQuery($simple = true, $sex = false, $year1 = -1, $year2 = -1, $params = array()) {
1971
+        $WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
1972
+        $WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
1973
+        $WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
1974
+        $WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
1975
+
1976
+        if ($simple) {
1977
+            $sql =
1978
+                "SELECT FLOOR(d_year/100+1) AS century, COUNT(*) AS total FROM `##dates` " .
1979
+                "WHERE " .
1980
+                "d_file={$this->tree->getTreeId()} AND " .
1981
+                'd_year<>0 AND ' .
1982
+                "d_fact='DEAT' AND " .
1983
+                "d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')";
1984
+        } elseif ($sex) {
1985
+            $sql =
1986
+                "SELECT d_month, i_sex, COUNT(*) AS total FROM `##dates` " .
1987
+                "JOIN `##individuals` ON d_file = i_file AND d_gid = i_id " .
1988
+                "WHERE " .
1989
+                "d_file={$this->tree->getTreeId()} AND " .
1990
+                "d_fact='DEAT' AND " .
1991
+                "d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')";
1992
+        } else {
1993
+            $sql =
1994
+                "SELECT d_month, COUNT(*) AS total FROM `##dates` " .
1995
+                "WHERE " .
1996
+                "d_file={$this->tree->getTreeId()} AND " .
1997
+                "d_fact='DEAT' AND " .
1998
+                "d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')";
1999
+        }
2000
+        if ($year1 >= 0 && $year2 >= 0) {
2001
+            $sql .= " AND d_year BETWEEN '{$year1}' AND '{$year2}'";
2002
+        }
2003
+        if ($simple) {
2004
+            $sql .= " GROUP BY century ORDER BY century";
2005
+        } else {
2006
+            $sql .= " GROUP BY d_month";
2007
+            if ($sex) {
2008
+                $sql .= ", i_sex";
2009
+            }
2010
+        }
2011
+        $rows = $this->runSql($sql);
2012
+        if ($simple) {
2013
+            if (isset($params[0]) && $params[0] != '') {
2014
+                $size = strtolower($params[0]);
2015
+            } else {
2016
+                $size = $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y;
2017
+            }
2018
+            if (isset($params[1]) && $params[1] != '') {
2019
+                $color_from = strtolower($params[1]);
2020
+            } else {
2021
+                $color_from = $WT_STATS_CHART_COLOR1;
2022
+            }
2023
+            if (isset($params[2]) && $params[2] != '') {
2024
+                $color_to = strtolower($params[2]);
2025
+            } else {
2026
+                $color_to = $WT_STATS_CHART_COLOR2;
2027
+            }
2028
+            $sizes = explode('x', $size);
2029
+            $tot   = 0;
2030
+            foreach ($rows as $values) {
2031
+                $tot += $values['total'];
2032
+            }
2033
+            // Beware divide by zero
2034
+            if ($tot == 0) {
2035
+                return '';
2036
+            }
2037
+            $centuries = "";
2038
+            $counts    = array();
2039
+            foreach ($rows as $values) {
2040
+                $counts[] = round(100 * $values['total'] / $tot, 0);
2041
+                $centuries .= $this->centuryName($values['century']) . ' - ' . I18N::number($values['total']) . '|';
2042
+            }
2043
+            $chd = $this->arrayToExtendedEncoding($counts);
2044
+            $chl = rawurlencode(substr($centuries, 0, -1));
2045
+
2046
+            return "<img src=\"https://chart.googleapis.com/chart?cht=p3&amp;chd=e:{$chd}&amp;chs={$size}&amp;chco={$color_from},{$color_to}&amp;chf=bg,s,ffffff00&amp;chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Deaths by century') . "\" title=\"" . I18N::translate('Deaths by century') . "\" />";
2047
+        }
2048
+
2049
+        return $rows;
2050
+    }
2051
+
2052
+    /**
2053
+     * Find the earliest birth.
2054
+     *
2055
+     * @return string
2056
+     */
2057
+    public function firstBirth() {
2058
+        return $this->mortalityQuery('full', 'ASC', 'BIRT');
2059
+    }
2060
+
2061
+    /**
2062
+     * Find the earliest birth year.
2063
+     *
2064
+     * @return string
2065
+     */
2066
+    public function firstBirthYear() {
2067
+        return $this->mortalityQuery('year', 'ASC', 'BIRT');
2068
+    }
2069
+
2070
+    /**
2071
+     * Find the name of the earliest birth.
2072
+     *
2073
+     * @return string
2074
+     */
2075
+    public function firstBirthName() {
2076
+        return $this->mortalityQuery('name', 'ASC', 'BIRT');
2077
+    }
2078
+
2079
+    /**
2080
+     * Find the earliest birth place.
2081
+     *
2082
+     * @return string
2083
+     */
2084
+    public function firstBirthPlace() {
2085
+        return $this->mortalityQuery('place', 'ASC', 'BIRT');
2086
+    }
2087
+
2088
+    /**
2089
+     * Find the latest birth.
2090
+     *
2091
+     * @return string
2092
+     */
2093
+    public function lastBirth() {
2094
+        return $this->mortalityQuery('full', 'DESC', 'BIRT');
2095
+    }
2096
+
2097
+    /**
2098
+     * Find the latest birth year.
2099
+     *
2100
+     * @return string
2101
+     */
2102
+    public function lastBirthYear() {
2103
+        return $this->mortalityQuery('year', 'DESC', 'BIRT');
2104
+    }
2105
+
2106
+    /**
2107
+     * Find the latest birth name.
2108
+     *
2109
+     * @return string
2110
+     */
2111
+    public function lastBirthName() {
2112
+        return $this->mortalityQuery('name', 'DESC', 'BIRT');
2113
+    }
2114
+
2115
+    /**
2116
+     * Find the latest birth place.
2117
+     *
2118
+     * @return string
2119
+     */
2120
+    public function lastBirthPlace() {
2121
+        return $this->mortalityQuery('place', 'DESC', 'BIRT');
2122
+    }
2123
+
2124
+    /**
2125
+     * General query on births.
2126
+     *
2127
+     * @param string[] $params
2128
+     *
2129
+     * @return string
2130
+     */
2131
+    public function statsBirth($params = array()) {
2132
+        return $this->statsBirthQuery(true, false, -1, -1, $params);
2133
+    }
2134
+
2135
+    /**
2136
+     * Find the earliest death.
2137
+     *
2138
+     * @return string
2139
+     */
2140
+    public function firstDeath() {
2141
+        return $this->mortalityQuery('full', 'ASC', 'DEAT');
2142
+    }
2143
+
2144
+    /**
2145
+     * Find the earliest death year.
2146
+     *
2147
+     * @return string
2148
+     */
2149
+    public function firstDeathYear() {
2150
+        return $this->mortalityQuery('year', 'ASC', 'DEAT');
2151
+    }
2152
+
2153
+    /**
2154
+     * Find the earliest death name.
2155
+     *
2156
+     * @return string
2157
+     */
2158
+    public function firstDeathName() {
2159
+        return $this->mortalityQuery('name', 'ASC', 'DEAT');
2160
+    }
2161
+
2162
+    /**
2163
+     * Find the earliest death place.
2164
+     *
2165
+     * @return string
2166
+     */
2167
+    public function firstDeathPlace() {
2168
+        return $this->mortalityQuery('place', 'ASC', 'DEAT');
2169
+    }
2170
+
2171
+    /**
2172
+     * Find the latest death.
2173
+     *
2174
+     * @return string
2175
+     */
2176
+    public function lastDeath() {
2177
+        return $this->mortalityQuery('full', 'DESC', 'DEAT');
2178
+    }
2179
+
2180
+    /**
2181
+     * Find the latest death year.
2182
+     *
2183
+     * @return string
2184
+     */
2185
+    public function lastDeathYear() {
2186
+        return $this->mortalityQuery('year', 'DESC', 'DEAT');
2187
+    }
2188
+
2189
+    /**
2190
+     * Find the latest death name.
2191
+     *
2192
+     * @return string
2193
+     */
2194
+    public function lastDeathName() {
2195
+        return $this->mortalityQuery('name', 'DESC', 'DEAT');
2196
+    }
2197
+
2198
+    /**
2199
+     * Find the place of the latest death.
2200
+     *
2201
+     * @return string
2202
+     */
2203
+    public function lastDeathPlace() {
2204
+        return $this->mortalityQuery('place', 'DESC', 'DEAT');
2205
+    }
2206
+
2207
+    /**
2208
+     * General query on deaths.
2209
+     *
2210
+     * @param string[] $params
2211
+     *
2212
+     * @return string
2213
+     */
2214
+    public function statsDeath($params = array()) {
2215
+        return $this->statsDeathQuery(true, false, -1, -1, $params);
2216
+    }
2217
+
2218
+    /**
2219
+     * Lifespan
2220
+     *
2221
+     * @param string $type
2222
+     * @param string $sex
2223
+     *
2224
+     * @return string
2225
+     */
2226
+    private function longlifeQuery($type = 'full', $sex = 'F') {
2227
+        $sex_search = ' 1=1';
2228
+        if ($sex == 'F') {
2229
+            $sex_search = " i_sex='F'";
2230
+        } elseif ($sex == 'M') {
2231
+            $sex_search = " i_sex='M'";
2232
+        }
2233
+
2234
+        $rows = $this->runSql(
2235
+            " SELECT" .
2236
+            " death.d_gid AS id," .
2237
+            " death.d_julianday2-birth.d_julianday1 AS age" .
2238
+            " FROM" .
2239
+            " `##dates` AS death," .
2240
+            " `##dates` AS birth," .
2241
+            " `##individuals` AS indi" .
2242
+            " WHERE" .
2243
+            " indi.i_id=birth.d_gid AND" .
2244
+            " birth.d_gid=death.d_gid AND" .
2245
+            " death.d_file={$this->tree->getTreeId()} AND" .
2246
+            " birth.d_file=death.d_file AND" .
2247
+            " birth.d_file=indi.i_file AND" .
2248
+            " birth.d_fact='BIRT' AND" .
2249
+            " death.d_fact='DEAT' AND" .
2250
+            " birth.d_julianday1<>0 AND" .
2251
+            " death.d_julianday1>birth.d_julianday2 AND" .
2252
+            $sex_search .
2253
+            " ORDER BY" .
2254
+            " age DESC LIMIT 1"
2255
+        );
2256
+        if (!isset($rows[0])) {
2257
+            return '';
2258
+        }
2259
+        $row    = $rows[0];
2260
+        $person = Individual::getInstance($row['id'], $this->tree);
2261
+        switch ($type) {
2262
+        default:
2263
+        case 'full':
2264
+            if ($person->canShowName()) {
2265
+                $result = $person->formatList('span', false, $person->getFullName());
2266
+            } else {
2267
+                $result = I18N::translate('This information is private and cannot be shown.');
2268
+            }
2269
+            break;
2270
+        case 'age':
2271
+            $result = I18N::number((int) ($row['age'] / 365.25));
2272
+            break;
2273
+        case 'name':
2274
+            $result = "<a href=\"" . $person->getHtmlUrl() . "\">" . $person->getFullName() . "</a>";
2275
+            break;
2276
+        }
2277
+
2278
+        return $result;
2279
+    }
2280
+
2281
+    /**
2282
+     * Find the oldest individuals.
2283
+     *
2284
+     * @param string   $type
2285
+     * @param string   $sex
2286
+     * @param string[] $params
2287
+     *
2288
+     * @return string
2289
+     */
2290
+    private function topTenOldestQuery($type = 'list', $sex = 'BOTH', $params = array()) {
2291
+        if ($sex === 'F') {
2292
+            $sex_search = " AND i_sex='F' ";
2293
+        } elseif ($sex === 'M') {
2294
+            $sex_search = " AND i_sex='M' ";
2295
+        } else {
2296
+            $sex_search = '';
2297
+        }
2298
+        if (isset($params[0])) {
2299
+            $total = (int) $params[0];
2300
+        } else {
2301
+            $total = 10;
2302
+        }
2303
+        $rows = $this->runSql(
2304
+            "SELECT " .
2305
+            " MAX(death.d_julianday2-birth.d_julianday1) AS age, " .
2306
+            " death.d_gid AS deathdate " .
2307
+            "FROM " .
2308
+            " `##dates` AS death, " .
2309
+            " `##dates` AS birth, " .
2310
+            " `##individuals` AS indi " .
2311
+            "WHERE " .
2312
+            " indi.i_id=birth.d_gid AND " .
2313
+            " birth.d_gid=death.d_gid AND " .
2314
+            " death.d_file={$this->tree->getTreeId()} AND " .
2315
+            " birth.d_file=death.d_file AND " .
2316
+            " birth.d_file=indi.i_file AND " .
2317
+            " birth.d_fact='BIRT' AND " .
2318
+            " death.d_fact='DEAT' AND " .
2319
+            " birth.d_julianday1<>0 AND " .
2320
+            " death.d_julianday1>birth.d_julianday2 " .
2321
+            $sex_search .
2322
+            "GROUP BY deathdate " .
2323
+            "ORDER BY age DESC " .
2324
+            "LIMIT " . $total
2325
+        );
2326
+        if (!isset($rows[0])) {
2327
+            return '';
2328
+        }
2329
+        $top10 = array();
2330
+        foreach ($rows as $row) {
2331
+            $person = Individual::getInstance($row['deathdate'], $this->tree);
2332
+            $age    = $row['age'];
2333
+            if ((int) ($age / 365.25) > 0) {
2334
+                $age = (int) ($age / 365.25) . 'y';
2335
+            } elseif ((int) ($age / 30.4375) > 0) {
2336
+                $age = (int) ($age / 30.4375) . 'm';
2337
+            } else {
2338
+                $age = $age . 'd';
2339
+            }
2340
+            $age = FunctionsDate::getAgeAtEvent($age);
2341
+            if ($person->canShow()) {
2342
+                if ($type == 'list') {
2343
+                    $top10[] = "<li><a href=\"" . $person->getHtmlUrl() . "\">" . $person->getFullName() . "</a> (" . $age . ")" . "</li>";
2344
+                } else {
2345
+                    $top10[] = "<a href=\"" . $person->getHtmlUrl() . "\">" . $person->getFullName() . "</a> (" . $age . ")";
2346
+                }
2347
+            }
2348
+        }
2349
+        if ($type == 'list') {
2350
+            $top10 = implode('', $top10);
2351
+        } else {
2352
+            $top10 = implode(' ', $top10);
2353
+        }
2354
+        if (I18N::direction() === 'rtl') {
2355
+            $top10 = str_replace(array('[', ']', '(', ')', '+'), array('&rlm;[', '&rlm;]', '&rlm;(', '&rlm;)', '&rlm;+'), $top10);
2356
+        }
2357
+        if ($type == 'list') {
2358
+            return '<ul>' . $top10 . '</ul>';
2359
+        }
2360
+
2361
+        return $top10;
2362
+    }
2363
+
2364
+    /**
2365
+     * Find the oldest living individuals.
2366
+     *
2367
+     * @param string   $type
2368
+     * @param string   $sex
2369
+     * @param string[] $params
2370
+     *
2371
+     * @return string
2372
+     */
2373
+    private function topTenOldestAliveQuery($type = 'list', $sex = 'BOTH', $params = array()) {
2374
+        if (!Auth::isMember($this->tree)) {
2375
+            return I18N::translate('This information is private and cannot be shown.');
2376
+        }
2377
+        if ($sex == 'F') {
2378
+            $sex_search = " AND i_sex='F'";
2379
+        } elseif ($sex == 'M') {
2380
+            $sex_search = " AND i_sex='M'";
2381
+        } else {
2382
+            $sex_search = '';
2383
+        }
2384
+        if (isset($params[0])) {
2385
+            $total = (int) $params[0];
2386
+        } else {
2387
+            $total = 10;
2388
+        }
2389
+        $rows = $this->runSql(
2390
+            "SELECT" .
2391
+            " birth.d_gid AS id," .
2392
+            " MIN(birth.d_julianday1) AS age" .
2393
+            " FROM" .
2394
+            " `##dates` AS birth," .
2395
+            " `##individuals` AS indi" .
2396
+            " WHERE" .
2397
+            " indi.i_id=birth.d_gid AND" .
2398
+            " indi.i_gedcom NOT REGEXP '\\n1 (" . WT_EVENTS_DEAT . ")' AND" .
2399
+            " birth.d_file={$this->tree->getTreeId()} AND" .
2400
+            " birth.d_fact='BIRT' AND" .
2401
+            " birth.d_file=indi.i_file AND" .
2402
+            " birth.d_julianday1<>0" .
2403
+            $sex_search .
2404
+            " GROUP BY id" .
2405
+            " ORDER BY age" .
2406
+            " ASC LIMIT " . $total
2407
+        );
2408
+        $top10 = array();
2409
+        foreach ($rows as $row) {
2410
+            $person = Individual::getInstance($row['id'], $this->tree);
2411
+            $age    = (WT_CLIENT_JD - $row['age']);
2412
+            if ((int) ($age / 365.25) > 0) {
2413
+                $age = (int) ($age / 365.25) . 'y';
2414
+            } elseif ((int) ($age / 30.4375) > 0) {
2415
+                $age = (int) ($age / 30.4375) . 'm';
2416
+            } else {
2417
+                $age = $age . 'd';
2418
+            }
2419
+            $age = FunctionsDate::getAgeAtEvent($age);
2420
+            if ($type === 'list') {
2421
+                $top10[] = "<li><a href=\"" . $person->getHtmlUrl() . "\">" . $person->getFullName() . "</a> (" . $age . ")" . "</li>";
2422
+            } else {
2423
+                $top10[] = "<a href=\"" . $person->getHtmlUrl() . "\">" . $person->getFullName() . "</a> (" . $age . ")";
2424
+            }
2425
+        }
2426
+        if ($type === 'list') {
2427
+            $top10 = implode('', $top10);
2428
+        } else {
2429
+            $top10 = implode('; ', $top10);
2430
+        }
2431
+        if (I18N::direction() === 'rtl') {
2432
+            $top10 = str_replace(array('[', ']', '(', ')', '+'), array('&rlm;[', '&rlm;]', '&rlm;(', '&rlm;)', '&rlm;+'), $top10);
2433
+        }
2434
+        if ($type === 'list') {
2435
+            return '<ul>' . $top10 . '</ul>';
2436
+        }
2437
+
2438
+        return $top10;
2439
+    }
2440
+
2441
+    /**
2442
+     * Find the average lifespan.
2443
+     *
2444
+     * @param string $sex
2445
+     * @param bool   $show_years
2446
+     *
2447
+     * @return string
2448
+     */
2449
+    private function averageLifespanQuery($sex = 'BOTH', $show_years = false) {
2450
+        if ($sex === 'F') {
2451
+            $sex_search = " AND i_sex='F' ";
2452
+        } elseif ($sex === 'M') {
2453
+            $sex_search = " AND i_sex='M' ";
2454
+        } else {
2455
+            $sex_search = '';
2456
+        }
2457
+        $rows = $this->runSql(
2458
+            "SELECT " .
2459
+            " AVG(death.d_julianday2-birth.d_julianday1) AS age " .
2460
+            "FROM " .
2461
+            " `##dates` AS death, " .
2462
+            " `##dates` AS birth, " .
2463
+            " `##individuals` AS indi " .
2464
+            "WHERE " .
2465
+            " indi.i_id=birth.d_gid AND " .
2466
+            " birth.d_gid=death.d_gid AND " .
2467
+            " death.d_file=" . $this->tree->getTreeId() . " AND " .
2468
+            " birth.d_file=death.d_file AND " .
2469
+            " birth.d_file=indi.i_file AND " .
2470
+            " birth.d_fact='BIRT' AND " .
2471
+            " death.d_fact='DEAT' AND " .
2472
+            " birth.d_julianday1<>0 AND " .
2473
+            " death.d_julianday1>birth.d_julianday2 " .
2474
+            $sex_search
2475
+        );
2476
+        if (!isset($rows[0])) {
2477
+            return '';
2478
+        }
2479
+        $row = $rows[0];
2480
+        $age = $row['age'];
2481
+        if ($show_years) {
2482
+            if ((int) ($age / 365.25) > 0) {
2483
+                $age = (int) ($age / 365.25) . 'y';
2484
+            } elseif ((int) ($age / 30.4375) > 0) {
2485
+                $age = (int) ($age / 30.4375) . 'm';
2486
+            } elseif (!empty($age)) {
2487
+                $age = $age . 'd';
2488
+            }
2489
+
2490
+            return FunctionsDate::getAgeAtEvent($age);
2491
+        } else {
2492
+            return I18N::number($age / 365.25);
2493
+        }
2494
+    }
2495
+
2496
+    /**
2497
+     * General query on ages.
2498
+     *
2499
+     * @param bool     $simple
2500
+     * @param string   $related
2501
+     * @param string   $sex
2502
+     * @param int      $year1
2503
+     * @param int      $year2
2504
+     * @param string[] $params
2505
+     *
2506
+     * @return array|string
2507
+     */
2508
+    public function statsAgeQuery($simple = true, $related = 'BIRT', $sex = 'BOTH', $year1 = -1, $year2 = -1, $params = array()) {
2509
+        if ($simple) {
2510
+            if (isset($params[0]) && $params[0] != '') {
2511
+                $size = strtolower($params[0]);
2512
+            } else {
2513
+                $size = '230x250';
2514
+            }
2515
+            $sizes = explode('x', $size);
2516
+            $rows  = $this->runSql(
2517
+                "SELECT" .
2518
+                " ROUND(AVG(death.d_julianday2-birth.d_julianday1)/365.25,1) AS age," .
2519
+                " FLOOR(death.d_year/100+1) AS century," .
2520
+                " i_sex AS sex" .
2521
+                " FROM" .
2522
+                " `##dates` AS death," .
2523
+                " `##dates` AS birth," .
2524
+                " `##individuals` AS indi" .
2525
+                " WHERE" .
2526
+                " indi.i_id=birth.d_gid AND" .
2527
+                " birth.d_gid=death.d_gid AND" .
2528
+                " death.d_file={$this->tree->getTreeId()} AND" .
2529
+                " birth.d_file=death.d_file AND" .
2530
+                " birth.d_file=indi.i_file AND" .
2531
+                " birth.d_fact='BIRT' AND" .
2532
+                " death.d_fact='DEAT' AND" .
2533
+                " birth.d_julianday1<>0 AND" .
2534
+                " birth.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND" .
2535
+                " death.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND" .
2536
+                " death.d_julianday1>birth.d_julianday2" .
2537
+                " GROUP BY century, sex ORDER BY century, sex");
2538
+            if (empty($rows)) {
2539
+                return '';
2540
+            }
2541
+            $chxl    = '0:|';
2542
+            $countsm = '';
2543
+            $countsf = '';
2544
+            $countsa = '';
2545
+            $out     = array();
2546
+            foreach ($rows as $values) {
2547
+                $out[$values['century']][$values['sex']] = $values['age'];
2548
+            }
2549
+            foreach ($out as $century => $values) {
2550
+                if ($sizes[0] < 980) {
2551
+                    $sizes[0] += 50;
2552
+                }
2553
+                $chxl .= $this->centuryName($century) . '|';
2554
+                $average = 0;
2555
+                if (isset($values['F'])) {
2556
+                    $countsf .= $values['F'] . ',';
2557
+                    $average = $values['F'];
2558
+                } else {
2559
+                    $countsf .= '0,';
2560
+                }
2561
+                if (isset($values['M'])) {
2562
+                    $countsm .= $values['M'] . ',';
2563
+                    if ($average == 0) {
2564
+                        $countsa .= $values['M'] . ',';
2565
+                    } else {
2566
+                        $countsa .= (($values['M'] + $average) / 2) . ',';
2567
+                    }
2568
+                } else {
2569
+                    $countsm .= '0,';
2570
+                    if ($average == 0) {
2571
+                        $countsa .= '0,';
2572
+                    } else {
2573
+                        $countsa .= $values['F'] . ',';
2574
+                    }
2575
+                }
2576
+            }
2577
+            $countsm = substr($countsm, 0, -1);
2578
+            $countsf = substr($countsf, 0, -1);
2579
+            $countsa = substr($countsa, 0, -1);
2580
+            $chd     = 't2:' . $countsm . '|' . $countsf . '|' . $countsa;
2581
+            $decades = '';
2582
+            for ($i = 0; $i <= 100; $i += 10) {
2583
+                $decades .= '|' . I18N::number($i);
2584
+            }
2585
+            $chxl .= '1:||' . I18N::translate('century') . '|2:' . $decades . '|3:||' . I18N::translate('Age') . '|';
2586
+            $title = I18N::translate('Average age related to death century');
2587
+            if (count($rows) > 6 || mb_strlen($title) < 30) {
2588
+                $chtt = $title;
2589
+            } else {
2590
+                $offset  = 0;
2591
+                $counter = array();
2592
+                while ($offset = strpos($title, ' ', $offset + 1)) {
2593
+                    $counter[] = $offset;
2594
+                }
2595
+                $half = (int) (count($counter) / 2);
2596
+                $chtt = substr_replace($title, '|', $counter[$half], 1);
2597
+            }
2598
+
2599
+            return '<img src="' . "https://chart.googleapis.com/chart?cht=bvg&amp;chs={$sizes[0]}x{$sizes[1]}&amp;chm=D,FF0000,2,0,3,1|N*f1*,000000,0,-1,11,1|N*f1*,000000,1,-1,11,1&amp;chf=bg,s,ffffff00|c,s,ffffff00&amp;chtt=" . rawurlencode($chtt) . "&amp;chd={$chd}&amp;chco=0000FF,FFA0CB,FF0000&amp;chbh=20,3&amp;chxt=x,x,y,y&amp;chxl=" . rawurlencode($chxl) . "&amp;chdl=" . rawurlencode(I18N::translate('Males') . '|' . I18N::translate('Females') . '|' . I18N::translate('Average age at death')) . "\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Average age related to death century') . "\" title=\"" . I18N::translate('Average age related to death century') . "\" />";
2600
+        } else {
2601
+            $sex_search = '';
2602
+            $years      = '';
2603
+            if ($sex == 'F') {
2604
+                $sex_search = " AND i_sex='F'";
2605
+            } elseif ($sex == 'M') {
2606
+                $sex_search = " AND i_sex='M'";
2607
+            }
2608
+            if ($year1 >= 0 && $year2 >= 0) {
2609
+                if ($related == 'BIRT') {
2610
+                    $years = " AND birth.d_year BETWEEN '{$year1}' AND '{$year2}'";
2611
+                } elseif ($related == 'DEAT') {
2612
+                    $years = " AND death.d_year BETWEEN '{$year1}' AND '{$year2}'";
2613
+                }
2614
+            }
2615
+            $rows = $this->runSql(
2616
+                "SELECT" .
2617
+                " death.d_julianday2-birth.d_julianday1 AS age" .
2618
+                " FROM" .
2619
+                " `##dates` AS death," .
2620
+                " `##dates` AS birth," .
2621
+                " `##individuals` AS indi" .
2622
+                " WHERE" .
2623
+                " indi.i_id=birth.d_gid AND" .
2624
+                " birth.d_gid=death.d_gid AND" .
2625
+                " death.d_file={$this->tree->getTreeId()} AND" .
2626
+                " birth.d_file=death.d_file AND" .
2627
+                " birth.d_file=indi.i_file AND" .
2628
+                " birth.d_fact='BIRT' AND" .
2629
+                " death.d_fact='DEAT' AND" .
2630
+                " birth.d_julianday1<>0 AND" .
2631
+                " birth.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND" .
2632
+                " death.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND" .
2633
+                " death.d_julianday1>birth.d_julianday2" .
2634
+                $years .
2635
+                $sex_search .
2636
+                " ORDER BY age DESC");
2637
+
2638
+            return $rows;
2639
+        }
2640
+    }
2641
+
2642
+    /**
2643
+     * General query on ages.
2644
+     *
2645
+     * @param string[] $params
2646
+     *
2647
+     * @return string
2648
+     */
2649
+    public function statsAge($params = array()) {
2650
+        return $this->statsAgeQuery(true, 'BIRT', 'BOTH', -1, -1, $params);
2651
+    }
2652
+
2653
+    /**
2654
+     * Find the lognest lived individual.
2655
+     *
2656
+     * @return string
2657
+     */
2658
+    public function longestLife() {
2659
+        return $this->longlifeQuery('full', 'BOTH');
2660
+    }
2661
+
2662
+    /**
2663
+     * Find the age of the longest lived individual.
2664
+     *
2665
+     * @return string
2666
+     */
2667
+    public function longestLifeAge() {
2668
+        return $this->longlifeQuery('age', 'BOTH');
2669
+    }
2670
+
2671
+    /**
2672
+     * Find the name of the longest lived individual.
2673
+     *
2674
+     * @return string
2675
+     */
2676
+    public function longestLifeName() {
2677
+        return $this->longlifeQuery('name', 'BOTH');
2678
+    }
2679
+
2680
+    /**
2681
+     * Find the oldest individuals.
2682
+     *
2683
+     * @param string[] $params
2684
+     *
2685
+     * @return string
2686
+     */
2687
+    public function topTenOldest($params = array()) {
2688
+        return $this->topTenOldestQuery('nolist', 'BOTH', $params);
2689
+    }
2690
+
2691
+    /**
2692
+     * Find the oldest living individuals.
2693
+     *
2694
+     * @param string[] $params
2695
+     *
2696
+     * @return string
2697
+     */
2698
+    public function topTenOldestList($params = array()) {
2699
+        return $this->topTenOldestQuery('list', 'BOTH', $params);
2700
+    }
2701
+
2702
+    /**
2703
+     * Find the oldest living individuals.
2704
+     *
2705
+     * @param string[] $params
2706
+     *
2707
+     * @return string
2708
+     */
2709
+    public function topTenOldestAlive($params = array()) {
2710
+        return $this->topTenOldestAliveQuery('nolist', 'BOTH', $params);
2711
+    }
2712
+
2713
+    /**
2714
+     * Find the oldest living individuals.
2715
+     *
2716
+     * @param string[] $params
2717
+     *
2718
+     * @return string
2719
+     */
2720
+    public function topTenOldestListAlive($params = array()) {
2721
+        return $this->topTenOldestAliveQuery('list', 'BOTH', $params);
2722
+    }
2723
+
2724
+    /**
2725
+     * Find the average lifespan.
2726
+     *
2727
+     * @param bool $show_years
2728
+     *
2729
+     * @return string
2730
+     */
2731
+    public function averageLifespan($show_years = false) {
2732
+        return $this->averageLifespanQuery('BOTH', $show_years);
2733
+    }
2734
+
2735
+    /**
2736
+     * Find the longest lived female.
2737
+     *
2738
+     * @return string
2739
+     */
2740
+    public function longestLifeFemale() {
2741
+        return $this->longlifeQuery('full', 'F');
2742
+    }
2743
+
2744
+    /**
2745
+     * Find the age of the longest lived female.
2746
+     *
2747
+     * @return string
2748
+     */
2749
+    public function longestLifeFemaleAge() {
2750
+        return $this->longlifeQuery('age', 'F');
2751
+    }
2752
+
2753
+    /**
2754
+     * Find the name of the longest lived female.
2755
+     *
2756
+     * @return string
2757
+     */
2758
+    public function longestLifeFemaleName() {
2759
+        return $this->longlifeQuery('name', 'F');
2760
+    }
2761
+
2762
+    /**
2763
+     * Find the oldest females.
2764
+     *
2765
+     * @param string[] $params
2766
+     *
2767
+     * @return string
2768
+     */
2769
+    public function topTenOldestFemale($params = array()) {
2770
+        return $this->topTenOldestQuery('nolist', 'F', $params);
2771
+    }
2772
+
2773
+    /**
2774
+     * Find the oldest living females.
2775
+     *
2776
+     * @param string[] $params
2777
+     *
2778
+     * @return string
2779
+     */
2780
+    public function topTenOldestFemaleList($params = array()) {
2781
+        return $this->topTenOldestQuery('list', 'F', $params);
2782
+    }
2783
+
2784
+    /**
2785
+     * Find the oldest living females.
2786
+     *
2787
+     * @param string[] $params
2788
+     *
2789
+     * @return string
2790
+     */
2791
+    public function topTenOldestFemaleAlive($params = array()) {
2792
+        return $this->topTenOldestAliveQuery('nolist', 'F', $params);
2793
+    }
2794
+
2795
+    /**
2796
+     * Find the oldest living females.
2797
+     *
2798
+     * @param string[] $params
2799
+     *
2800
+     * @return string
2801
+     */
2802
+    public function topTenOldestFemaleListAlive($params = array()) {
2803
+        return $this->topTenOldestAliveQuery('list', 'F', $params);
2804
+    }
2805
+
2806
+    /**
2807
+     * Find the average lifespan of females.
2808
+     *
2809
+     * @param bool $show_years
2810
+     *
2811
+     * @return string
2812
+     */
2813
+    public function averageLifespanFemale($show_years = false) {
2814
+        return $this->averageLifespanQuery('F', $show_years);
2815
+    }
2816
+
2817
+    /**
2818
+     * Find the longest lived male.
2819
+     *
2820
+     * @return string
2821
+     */
2822
+    public function longestLifeMale() {
2823
+        return $this->longlifeQuery('full', 'M');
2824
+    }
2825
+
2826
+    /**
2827
+     * Find the age of the longest lived male.
2828
+     *
2829
+     * @return string
2830
+     */
2831
+    public function longestLifeMaleAge() {
2832
+        return $this->longlifeQuery('age', 'M');
2833
+    }
2834
+
2835
+    /**
2836
+     * Find the name of the longest lived male.
2837
+     *
2838
+     * @return string
2839
+     */
2840
+    public function longestLifeMaleName() {
2841
+        return $this->longlifeQuery('name', 'M');
2842
+    }
2843
+
2844
+    /**
2845
+     * Find the longest lived males.
2846
+     *
2847
+     * @param string[] $params
2848
+     *
2849
+     * @return string
2850
+     */
2851
+    public function topTenOldestMale($params = array()) {
2852
+        return $this->topTenOldestQuery('nolist', 'M', $params);
2853
+    }
2854
+
2855
+    /**
2856
+     * Find the longest lived males.
2857
+     *
2858
+     * @param string[] $params
2859
+     *
2860
+     * @return string
2861
+     */
2862
+    public function topTenOldestMaleList($params = array()) {
2863
+        return $this->topTenOldestQuery('list', 'M', $params);
2864
+    }
2865
+
2866
+    /**
2867
+     * Find the longest lived living males.
2868
+     *
2869
+     * @param string[] $params
2870
+     *
2871
+     * @return string
2872
+     */
2873
+    public function topTenOldestMaleAlive($params = array()) {
2874
+        return $this->topTenOldestAliveQuery('nolist', 'M', $params);
2875
+    }
2876
+
2877
+    /**
2878
+     * Find the longest lived living males.
2879
+     *
2880
+     * @param string[] $params
2881
+     *
2882
+     * @return string
2883
+     */
2884
+    public function topTenOldestMaleListAlive($params = array()) {
2885
+        return $this->topTenOldestAliveQuery('list', 'M', $params);
2886
+    }
2887
+
2888
+    /**
2889
+     * Find the average male lifespan.
2890
+     *
2891
+     * @param bool $show_years
2892
+     *
2893
+     * @return string
2894
+     */
2895
+    public function averageLifespanMale($show_years = false) {
2896
+        return $this->averageLifespanQuery('M', $show_years);
2897
+    }
2898
+
2899
+    /**
2900
+     * Events
2901
+     *
2902
+     * @param string $type
2903
+     * @param string $direction
2904
+     * @param string $facts
2905
+     *
2906
+     * @return string
2907
+     */
2908
+    private function eventQuery($type, $direction, $facts) {
2909
+        $eventTypes = array(
2910
+            'BIRT' => I18N::translate('birth'),
2911
+            'DEAT' => I18N::translate('death'),
2912
+            'MARR' => I18N::translate('marriage'),
2913
+            'ADOP' => I18N::translate('adoption'),
2914
+            'BURI' => I18N::translate('burial'),
2915
+            'CENS' => I18N::translate('census added'),
2916
+        );
2917
+
2918
+        $fact_query = "IN ('" . str_replace('|', "','", $facts) . "')";
2919
+
2920
+        if ($direction != 'ASC') {
2921
+            $direction = 'DESC';
2922
+        }
2923
+        $rows = $this->runSql(''
2924
+            . ' SELECT'
2925
+            . ' d_gid AS id,'
2926
+            . ' d_year AS year,'
2927
+            . ' d_fact AS fact,'
2928
+            . ' d_type AS type'
2929
+            . ' FROM'
2930
+            . " `##dates`"
2931
+            . ' WHERE'
2932
+            . " d_file={$this->tree->getTreeId()} AND"
2933
+            . " d_gid<>'HEAD' AND"
2934
+            . " d_fact {$fact_query} AND"
2935
+            . ' d_julianday1<>0'
2936
+            . ' ORDER BY'
2937
+            . " d_julianday1 {$direction}, d_type LIMIT 1"
2938
+        );
2939
+        if (!isset($rows[0])) {
2940
+            return '';
2941
+        }
2942
+        $row    = $rows[0];
2943
+        $record = GedcomRecord::getInstance($row['id'], $this->tree);
2944
+        switch ($type) {
2945
+        default:
2946
+        case 'full':
2947
+            if ($record->canShow()) {
2948
+                $result = $record->formatList('span', false, $record->getFullName());
2949
+            } else {
2950
+                $result = I18N::translate('This information is private and cannot be shown.');
2951
+            }
2952
+            break;
2953
+        case 'year':
2954
+            $date   = new Date($row['type'] . ' ' . $row['year']);
2955
+            $result = $date->display();
2956
+            break;
2957
+        case 'type':
2958
+            if (isset($eventTypes[$row['fact']])) {
2959
+                $result = $eventTypes[$row['fact']];
2960
+            } else {
2961
+                $result = GedcomTag::getLabel($row['fact']);
2962
+            }
2963
+            break;
2964
+        case 'name':
2965
+            $result = "<a href=\"" . $record->getHtmlUrl() . "\">" . $record->getFullName() . "</a>";
2966
+            break;
2967
+        case 'place':
2968
+            $fact = $record->getFirstFact($row['fact']);
2969
+            if ($fact) {
2970
+                $result = FunctionsPrint::formatFactPlace($fact, true, true, true);
2971
+            } else {
2972
+                $result = I18N::translate('Private');
2973
+            }
2974
+            break;
2975
+        }
2976
+
2977
+        return $result;
2978
+    }
2979
+
2980
+    /**
2981
+     * Find the earliest event.
2982
+     *
2983
+     * @return string
2984
+     */
2985
+    public function firstEvent() {
2986
+        return $this->eventQuery('full', 'ASC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
2987
+    }
2988
+
2989
+    /**
2990
+     * Find the year of the earliest event.
2991
+     *
2992
+     * @return string
2993
+     */
2994
+    public function firstEventYear() {
2995
+        return $this->eventQuery('year', 'ASC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
2996
+    }
2997
+
2998
+    /**
2999
+     * Find the type of the earliest event.
3000
+     *
3001
+     * @return string
3002
+     */
3003
+    public function firstEventType() {
3004
+        return $this->eventQuery('type', 'ASC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
3005
+    }
3006
+
3007
+    /**
3008
+     * Find the name of the individual with the earliest event.
3009
+     *
3010
+     * @return string
3011
+     */
3012
+    public function firstEventName() {
3013
+        return $this->eventQuery('name', 'ASC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
3014
+    }
3015
+
3016
+    /**
3017
+     * Find the location of the earliest event.
3018
+     *
3019
+     * @return string
3020
+     */
3021
+    public function firstEventPlace() {
3022
+        return $this->eventQuery('place', 'ASC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
3023
+    }
3024
+
3025
+    /**
3026
+     * Find the latest event.
3027
+     *
3028
+     * @return string
3029
+     */
3030
+    public function lastEvent() {
3031
+        return $this->eventQuery('full', 'DESC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
3032
+    }
3033
+
3034
+    /**
3035
+     * Find the year of the latest event.
3036
+     *
3037
+     * @return string
3038
+     */
3039
+    public function lastEventYear() {
3040
+        return $this->eventQuery('year', 'DESC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
3041
+    }
3042
+
3043
+    /**
3044
+     * Find the type of the latest event.
3045
+     *
3046
+     * @return string
3047
+     */
3048
+    public function lastEventType() {
3049
+        return $this->eventQuery('type', 'DESC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
3050
+    }
3051
+
3052
+    /**
3053
+     * Find the name of the individual with the latest event.
3054
+     *
3055
+     * @return string
3056
+     */
3057
+    public function lastEventName() {
3058
+        return $this->eventQuery('name', 'DESC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
3059
+    }
3060
+
3061
+    /**
3062
+     * FInd the location of the latest event.
3063
+     *
3064
+     * @return string
3065
+     */
3066
+    public function lastEventPlace() {
3067
+        return $this->eventQuery('place', 'DESC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
3068
+    }
3069
+
3070
+    /**
3071
+     * Query the database for marriage tags.
3072
+     *
3073
+     * @param string $type
3074
+     * @param string $age_dir
3075
+     * @param string $sex
3076
+     * @param bool   $show_years
3077
+     *
3078
+     * @return string
3079
+     */
3080
+    private function marriageQuery($type = 'full', $age_dir = 'ASC', $sex = 'F', $show_years = false) {
3081
+        if ($sex == 'F') {
3082
+            $sex_field = 'f_wife';
3083
+        } else {
3084
+            $sex_field = 'f_husb';
3085
+        }
3086
+        if ($age_dir != 'ASC') {
3087
+            $age_dir = 'DESC';
3088
+        }
3089
+        $rows = $this->runSql(
3090
+            " SELECT fam.f_id AS famid, fam.{$sex_field}, married.d_julianday2-birth.d_julianday1 AS age, indi.i_id AS i_id" .
3091
+            " FROM `##families` AS fam" .
3092
+            " LEFT JOIN `##dates` AS birth ON birth.d_file = {$this->tree->getTreeId()}" .
3093
+            " LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->getTreeId()}" .
3094
+            " LEFT JOIN `##individuals` AS indi ON indi.i_file = {$this->tree->getTreeId()}" .
3095
+            " WHERE" .
3096
+            " birth.d_gid = indi.i_id AND" .
3097
+            " married.d_gid = fam.f_id AND" .
3098
+            " indi.i_id = fam.{$sex_field} AND" .
3099
+            " fam.f_file = {$this->tree->getTreeId()} AND" .
3100
+            " birth.d_fact = 'BIRT' AND" .
3101
+            " married.d_fact = 'MARR' AND" .
3102
+            " birth.d_julianday1 <> 0 AND" .
3103
+            " married.d_julianday2 > birth.d_julianday1 AND" .
3104
+            " i_sex='{$sex}'" .
3105
+            " ORDER BY" .
3106
+            " married.d_julianday2-birth.d_julianday1 {$age_dir} LIMIT 1"
3107
+        );
3108
+        if (!isset($rows[0])) {
3109
+            return '';
3110
+        }
3111
+        $row = $rows[0];
3112
+        if (isset($row['famid'])) {
3113
+            $family = Family::getInstance($row['famid'], $this->tree);
3114
+        }
3115
+        if (isset($row['i_id'])) {
3116
+            $person = Individual::getInstance($row['i_id'], $this->tree);
3117
+        }
3118
+        switch ($type) {
3119
+        default:
3120
+        case 'full':
3121
+            if ($family->canShow()) {
3122
+                $result = $family->formatList('span', false, $person->getFullName());
3123
+            } else {
3124
+                $result = I18N::translate('This information is private and cannot be shown.');
3125
+            }
3126
+            break;
3127
+        case 'name':
3128
+            $result = '<a href="' . $family->getHtmlUrl() . '">' . $person->getFullName() . '</a>';
3129
+            break;
3130
+        case 'age':
3131
+            $age = $row['age'];
3132
+            if ($show_years) {
3133
+                if ((int) ($age / 365.25) > 0) {
3134
+                    $age = (int) ($age / 365.25) . 'y';
3135
+                } elseif ((int) ($age / 30.4375) > 0) {
3136
+                    $age = (int) ($age / 30.4375) . 'm';
3137
+                } else {
3138
+                    $age = $age . 'd';
3139
+                }
3140
+                $result = FunctionsDate::getAgeAtEvent($age);
3141
+            } else {
3142
+                $result = I18N::number((int) ($age / 365.25));
3143
+            }
3144
+            break;
3145
+        }
3146
+
3147
+        return $result;
3148
+    }
3149
+
3150
+    /**
3151
+     * General query on age at marriage.
3152
+     *
3153
+     * @param string   $type
3154
+     * @param string   $age_dir
3155
+     * @param string[] $params
3156
+     *
3157
+     * @return string
3158
+     */
3159
+    private function ageOfMarriageQuery($type = 'list', $age_dir = 'ASC', $params = array()) {
3160
+        if (isset($params[0])) {
3161
+            $total = (int) $params[0];
3162
+        } else {
3163
+            $total = 10;
3164
+        }
3165
+        if ($age_dir != 'ASC') {
3166
+            $age_dir = 'DESC';
3167
+        }
3168
+        $hrows = $this->runSql(
3169
+            " SELECT DISTINCT fam.f_id AS family, MIN(husbdeath.d_julianday2-married.d_julianday1) AS age" .
3170
+            " FROM `##families` AS fam" .
3171
+            " LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->getTreeId()}" .
3172
+            " LEFT JOIN `##dates` AS husbdeath ON husbdeath.d_file = {$this->tree->getTreeId()}" .
3173
+            " WHERE" .
3174
+            " fam.f_file = {$this->tree->getTreeId()} AND" .
3175
+            " husbdeath.d_gid = fam.f_husb AND" .
3176
+            " husbdeath.d_fact = 'DEAT' AND" .
3177
+            " married.d_gid = fam.f_id AND" .
3178
+            " married.d_fact = 'MARR' AND" .
3179
+            " married.d_julianday1 < husbdeath.d_julianday2 AND" .
3180
+            " married.d_julianday1 <> 0" .
3181
+            " GROUP BY family" .
3182
+            " ORDER BY age {$age_dir}");
3183
+        $wrows = $this->runSql(
3184
+            " SELECT DISTINCT fam.f_id AS family, MIN(wifedeath.d_julianday2-married.d_julianday1) AS age" .
3185
+            " FROM `##families` AS fam" .
3186
+            " LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->getTreeId()}" .
3187
+            " LEFT JOIN `##dates` AS wifedeath ON wifedeath.d_file = {$this->tree->getTreeId()}" .
3188
+            " WHERE" .
3189
+            " fam.f_file = {$this->tree->getTreeId()} AND" .
3190
+            " wifedeath.d_gid = fam.f_wife AND" .
3191
+            " wifedeath.d_fact = 'DEAT' AND" .
3192
+            " married.d_gid = fam.f_id AND" .
3193
+            " married.d_fact = 'MARR' AND" .
3194
+            " married.d_julianday1 < wifedeath.d_julianday2 AND" .
3195
+            " married.d_julianday1 <> 0" .
3196
+            " GROUP BY family" .
3197
+            " ORDER BY age {$age_dir}");
3198
+        $drows = $this->runSql(
3199
+            " SELECT DISTINCT fam.f_id AS family, MIN(divorced.d_julianday2-married.d_julianday1) AS age" .
3200
+            " FROM `##families` AS fam" .
3201
+            " LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->getTreeId()}" .
3202
+            " LEFT JOIN `##dates` AS divorced ON divorced.d_file = {$this->tree->getTreeId()}" .
3203
+            " WHERE" .
3204
+            " fam.f_file = {$this->tree->getTreeId()} AND" .
3205
+            " married.d_gid = fam.f_id AND" .
3206
+            " married.d_fact = 'MARR' AND" .
3207
+            " divorced.d_gid = fam.f_id AND" .
3208
+            " divorced.d_fact IN ('DIV', 'ANUL', '_SEPR', '_DETS') AND" .
3209
+            " married.d_julianday1 < divorced.d_julianday2 AND" .
3210
+            " married.d_julianday1 <> 0" .
3211
+            " GROUP BY family" .
3212
+            " ORDER BY age {$age_dir}");
3213
+        if (!isset($hrows) && !isset($wrows) && !isset($drows)) {
3214
+            return '';
3215
+        }
3216
+        $rows = array();
3217
+        foreach ($drows as $family) {
3218
+            $rows[$family['family']] = $family['age'];
3219
+        }
3220
+        foreach ($hrows as $family) {
3221
+            if (!isset($rows[$family['family']])) {
3222
+                $rows[$family['family']] = $family['age'];
3223
+            }
3224
+        }
3225
+        foreach ($wrows as $family) {
3226
+            if (!isset($rows[$family['family']])) {
3227
+                $rows[$family['family']] = $family['age'];
3228
+            } elseif ($rows[$family['family']] > $family['age']) {
3229
+                $rows[$family['family']] = $family['age'];
3230
+            }
3231
+        }
3232
+        if ($age_dir === 'DESC') {
3233
+            arsort($rows);
3234
+        } else {
3235
+            asort($rows);
3236
+        }
3237
+        $top10 = array();
3238
+        $i     = 0;
3239
+        foreach ($rows as $fam => $age) {
3240
+            $family = Family::getInstance($fam, $this->tree);
3241
+            if ($type === 'name') {
3242
+                return $family->formatList('span', false, $family->getFullName());
3243
+            }
3244
+            if ((int) ($age / 365.25) > 0) {
3245
+                $age = (int) ($age / 365.25) . 'y';
3246
+            } elseif ((int) ($age / 30.4375) > 0) {
3247
+                $age = (int) ($age / 30.4375) . 'm';
3248
+            } else {
3249
+                $age = $age . 'd';
3250
+            }
3251
+            $age = FunctionsDate::getAgeAtEvent($age);
3252
+            if ($type === 'age') {
3253
+                return $age;
3254
+            }
3255
+            $husb = $family->getHusband();
3256
+            $wife = $family->getWife();
3257
+            if ($husb && $wife && ($husb->getAllDeathDates() && $wife->getAllDeathDates() || !$husb->isDead() || !$wife->isDead())) {
3258
+                if ($family->canShow()) {
3259
+                    if ($type === 'list') {
3260
+                        $top10[] = "<li><a href=\"" . $family->getHtmlUrl() . "\">" . $family->getFullName() . "</a> (" . $age . ")" . "</li>";
3261
+                    } else {
3262
+                        $top10[] = "<a href=\"" . $family->getHtmlUrl() . "\">" . $family->getFullName() . "</a> (" . $age . ")";
3263
+                    }
3264
+                }
3265
+                if (++$i === $total) {
3266
+                    break;
3267
+                }
3268
+            }
3269
+        }
3270
+        if ($type === 'list') {
3271
+            $top10 = implode('', $top10);
3272
+        } else {
3273
+            $top10 = implode('; ', $top10);
3274
+        }
3275
+        if (I18N::direction() === 'rtl') {
3276
+            $top10 = str_replace(array('[', ']', '(', ')', '+'), array('&rlm;[', '&rlm;]', '&rlm;(', '&rlm;)', '&rlm;+'), $top10);
3277
+        }
3278
+        if ($type === 'list') {
3279
+            return '<ul>' . $top10 . '</ul>';
3280
+        }
3281
+
3282
+        return $top10;
3283
+    }
3284
+
3285
+    /**
3286
+     * Find the ages between spouses.
3287
+     *
3288
+     * @param string   $type
3289
+     * @param string   $age_dir
3290
+     * @param string[] $params
3291
+     *
3292
+     * @return string
3293
+     */
3294
+    private function ageBetweenSpousesQuery($type = 'list', $age_dir = 'DESC', $params = array()) {
3295
+        if (isset($params[0])) {
3296
+            $total = (int) $params[0];
3297
+        } else {
3298
+            $total = 10;
3299
+        }
3300
+        if ($age_dir === 'DESC') {
3301
+            $sql =
3302
+                "SELECT f_id AS xref, MIN(wife.d_julianday2-husb.d_julianday1) AS age" .
3303
+                " FROM `##families`" .
3304
+                " JOIN `##dates` AS wife ON wife.d_gid = f_wife AND wife.d_file = f_file" .
3305
+                " JOIN `##dates` AS husb ON husb.d_gid = f_husb AND husb.d_file = f_file" .
3306
+                " WHERE f_file = :tree_id" .
3307
+                " AND husb.d_fact = 'BIRT'" .
3308
+                " AND wife.d_fact = 'BIRT'" .
3309
+                " AND wife.d_julianday2 >= husb.d_julianday1 AND husb.d_julianday1 <> 0" .
3310
+                " GROUP BY xref" .
3311
+                " ORDER BY age DESC" .
3312
+                " LIMIT :limit";
3313
+        } else {
3314
+            $sql =
3315
+                "SELECT f_id AS xref, MIN(husb.d_julianday2-wife.d_julianday1) AS age" .
3316
+                " FROM `##families`" .
3317
+                " JOIN `##dates` AS wife ON wife.d_gid = f_wife AND wife.d_file = f_file" .
3318
+                " JOIN `##dates` AS husb ON husb.d_gid = f_husb AND husb.d_file = f_file" .
3319
+                " WHERE f_file = :tree_id" .
3320
+                " AND husb.d_fact = 'BIRT'" .
3321
+                " AND wife.d_fact = 'BIRT'" .
3322
+                " AND husb.d_julianday2 >= wife.d_julianday1 AND wife.d_julianday1 <> 0" .
3323
+                " GROUP BY xref" .
3324
+                " ORDER BY age DESC" .
3325
+                " LIMIT :limit";
3326
+        }
3327
+        $rows = Database::prepare(
3328
+            $sql
3329
+        )->execute(array(
3330
+            'tree_id' => $this->tree->getTreeId(),
3331
+            'limit'   => $total,
3332
+        ))->fetchAll();
3333
+
3334
+        $top10 = array();
3335
+        foreach ($rows as $fam) {
3336
+            $family = Family::getInstance($fam->xref, $this->tree);
3337
+            if ($fam->age < 0) {
3338
+                break;
3339
+            }
3340
+            $age = $fam->age;
3341
+            if ((int) ($age / 365.25) > 0) {
3342
+                $age = (int) ($age / 365.25) . 'y';
3343
+            } elseif ((int) ($age / 30.4375) > 0) {
3344
+                $age = (int) ($age / 30.4375) . 'm';
3345
+            } else {
3346
+                $age = $age . 'd';
3347
+            }
3348
+            $age = FunctionsDate::getAgeAtEvent($age);
3349
+            if ($family->canShow()) {
3350
+                if ($type === 'list') {
3351
+                    $top10[] = '<li><a href="' . $family->getHtmlUrl() . '">' . $family->getFullName() . '</a> (' . $age . ')' . "</li>";
3352
+                } else {
3353
+                    $top10[] = '<a href="' . $family->getHtmlUrl() . '">' . $family->getFullName() . '</a> (' . $age . ')';
3354
+                }
3355
+            }
3356
+        }
3357
+        if ($type === 'list') {
3358
+            $top10 = implode('', $top10);
3359
+            if ($top10) {
3360
+                $top10 = '<ul>' . $top10 . '</ul>';
3361
+            }
3362
+        } else {
3363
+            $top10 = implode(' ', $top10);
3364
+        }
3365
+
3366
+        return $top10;
3367
+    }
3368
+
3369
+    /**
3370
+     * General query on parents.
3371
+     *
3372
+     * @param string $type
3373
+     * @param string $age_dir
3374
+     * @param string $sex
3375
+     * @param bool   $show_years
3376
+     *
3377
+     * @return string
3378
+     */
3379
+    private function parentsQuery($type = 'full', $age_dir = 'ASC', $sex = 'F', $show_years = false) {
3380
+        if ($sex == 'F') {
3381
+            $sex_field = 'WIFE';
3382
+        } else {
3383
+            $sex_field = 'HUSB';
3384
+        }
3385
+        if ($age_dir != 'ASC') {
3386
+            $age_dir = 'DESC';
3387
+        }
3388
+        $rows = $this->runSql(
3389
+            " SELECT" .
3390
+            " parentfamily.l_to AS id," .
3391
+            " childbirth.d_julianday2-birth.d_julianday1 AS age" .
3392
+            " FROM `##link` AS parentfamily" .
3393
+            " JOIN `##link` AS childfamily ON childfamily.l_file = {$this->tree->getTreeId()}" .
3394
+            " JOIN `##dates` AS birth ON birth.d_file = {$this->tree->getTreeId()}" .
3395
+            " JOIN `##dates` AS childbirth ON childbirth.d_file = {$this->tree->getTreeId()}" .
3396
+            " WHERE" .
3397
+            " birth.d_gid = parentfamily.l_to AND" .
3398
+            " childfamily.l_to = childbirth.d_gid AND" .
3399
+            " childfamily.l_type = 'CHIL' AND" .
3400
+            " parentfamily.l_type = '{$sex_field}' AND" .
3401
+            " childfamily.l_from = parentfamily.l_from AND" .
3402
+            " parentfamily.l_file = {$this->tree->getTreeId()} AND" .
3403
+            " birth.d_fact = 'BIRT' AND" .
3404
+            " childbirth.d_fact = 'BIRT' AND" .
3405
+            " birth.d_julianday1 <> 0 AND" .
3406
+            " childbirth.d_julianday2 > birth.d_julianday1" .
3407
+            " ORDER BY age {$age_dir} LIMIT 1"
3408
+        );
3409
+        if (!isset($rows[0])) {
3410
+            return '';
3411
+        }
3412
+        $row = $rows[0];
3413
+        if (isset($row['id'])) {
3414
+            $person = Individual::getInstance($row['id'], $this->tree);
3415
+        }
3416
+        switch ($type) {
3417
+        default:
3418
+        case 'full':
3419
+            if ($person->canShow()) {
3420
+                $result = $person->formatList('span', false, $person->getFullName());
3421
+            } else {
3422
+                $result = I18N::translate('This information is private and cannot be shown.');
3423
+            }
3424
+            break;
3425
+        case 'name':
3426
+            $result = '<a href="' . $person->getHtmlUrl() . '">' . $person->getFullName() . '</a>';
3427
+            break;
3428
+        case 'age':
3429
+            $age = $row['age'];
3430
+            if ($show_years) {
3431
+                if ((int) ($age / 365.25) > 0) {
3432
+                    $age = (int) ($age / 365.25) . 'y';
3433
+                } elseif ((int) ($age / 30.4375) > 0) {
3434
+                    $age = (int) ($age / 30.4375) . 'm';
3435
+                } else {
3436
+                    $age = $age . 'd';
3437
+                }
3438
+                $result = FunctionsDate::getAgeAtEvent($age);
3439
+            } else {
3440
+                $result = (int) ($age / 365.25);
3441
+            }
3442
+            break;
3443
+        }
3444
+
3445
+        return $result;
3446
+    }
3447
+
3448
+    /**
3449
+     * General query on marriages.
3450
+     *
3451
+     * @param bool     $simple
3452
+     * @param bool     $first
3453
+     * @param int      $year1
3454
+     * @param int      $year2
3455
+     * @param string[] $params
3456
+     *
3457
+     * @return string|array
3458
+     */
3459
+    public function statsMarrQuery($simple = true, $first = false, $year1 = -1, $year2 = -1, $params = array()) {
3460
+        $WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
3461
+        $WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
3462
+        $WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
3463
+        $WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
3464
+
3465
+        if ($simple) {
3466
+            $sql =
3467
+                "SELECT FLOOR(d_year/100+1) AS century, COUNT(*) AS total" .
3468
+                " FROM `##dates`" .
3469
+                " WHERE d_file={$this->tree->getTreeId()} AND d_year<>0 AND d_fact='MARR' AND d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')";
3470
+            if ($year1 >= 0 && $year2 >= 0) {
3471
+                $sql .= " AND d_year BETWEEN '{$year1}' AND '{$year2}'";
3472
+            }
3473
+            $sql .= " GROUP BY century ORDER BY century";
3474
+        } elseif ($first) {
3475
+            $years = '';
3476
+            if ($year1 >= 0 && $year2 >= 0) {
3477
+                $years = " married.d_year BETWEEN '{$year1}' AND '{$year2}' AND";
3478
+            }
3479
+            $sql =
3480
+                " SELECT fam.f_id AS fams, fam.f_husb, fam.f_wife, married.d_julianday2 AS age, married.d_month AS month, indi.i_id AS indi" .
3481
+                " FROM `##families` AS fam" .
3482
+                " LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->getTreeId()}" .
3483
+                " LEFT JOIN `##individuals` AS indi ON indi.i_file = {$this->tree->getTreeId()}" .
3484
+                " WHERE" .
3485
+                " married.d_gid = fam.f_id AND" .
3486
+                " fam.f_file = {$this->tree->getTreeId()} AND" .
3487
+                " married.d_fact = 'MARR' AND" .
3488
+                " married.d_julianday2 <> 0 AND" .
3489
+                $years .
3490
+                " (indi.i_id = fam.f_husb OR indi.i_id = fam.f_wife)" .
3491
+                " ORDER BY fams, indi, age ASC";
3492
+        } else {
3493
+            $sql =
3494
+                "SELECT d_month, COUNT(*) AS total" .
3495
+                " FROM `##dates`" .
3496
+                " WHERE d_file={$this->tree->getTreeId()} AND d_fact='MARR'";
3497
+            if ($year1 >= 0 && $year2 >= 0) {
3498
+                $sql .= " AND d_year BETWEEN '{$year1}' AND '{$year2}'";
3499
+            }
3500
+            $sql .= " GROUP BY d_month";
3501
+        }
3502
+        $rows = $this->runSql($sql);
3503
+        if (!isset($rows)) {
3504
+            return '';
3505
+        }
3506
+        if ($simple) {
3507
+            if (isset($params[0]) && $params[0] != '') {
3508
+                $size = strtolower($params[0]);
3509
+            } else {
3510
+                $size = $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y;
3511
+            }
3512
+            if (isset($params[1]) && $params[1] != '') {
3513
+                $color_from = strtolower($params[1]);
3514
+            } else {
3515
+                $color_from = $WT_STATS_CHART_COLOR1;
3516
+            }
3517
+            if (isset($params[2]) && $params[2] != '') {
3518
+                $color_to = strtolower($params[2]);
3519
+            } else {
3520
+                $color_to = $WT_STATS_CHART_COLOR2;
3521
+            }
3522
+            $sizes = explode('x', $size);
3523
+            $tot   = 0;
3524
+            foreach ($rows as $values) {
3525
+                $tot += (int) $values['total'];
3526
+            }
3527
+            // Beware divide by zero
3528
+            if ($tot === 0) {
3529
+                return '';
3530
+            }
3531
+            $centuries = '';
3532
+            $counts    = array();
3533
+            foreach ($rows as $values) {
3534
+                $counts[] = round(100 * $values['total'] / $tot, 0);
3535
+                $centuries .= $this->centuryName($values['century']) . ' - ' . I18N::number($values['total']) . '|';
3536
+            }
3537
+            $chd = $this->arrayToExtendedEncoding($counts);
3538
+            $chl = substr($centuries, 0, -1);
3539
+
3540
+            return "<img src=\"https://chart.googleapis.com/chart?cht=p3&amp;chd=e:{$chd}&amp;chs={$size}&amp;chco={$color_from},{$color_to}&amp;chf=bg,s,ffffff00&amp;chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Marriages by century') . "\" title=\"" . I18N::translate('Marriages by century') . "\" />";
3541
+        }
3542
+
3543
+        return $rows;
3544
+    }
3545
+
3546
+    /**
3547
+     * General query on divorces.
3548
+     *
3549
+     * @param bool     $simple
3550
+     * @param bool     $first
3551
+     * @param int      $year1
3552
+     * @param int      $year2
3553
+     * @param string[] $params
3554
+     *
3555
+     * @return string|array
3556
+     */
3557
+    private function statsDivQuery($simple = true, $first = false, $year1 = -1, $year2 = -1, $params = array()) {
3558
+        $WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
3559
+        $WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
3560
+        $WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
3561
+        $WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
3562
+
3563
+        if ($simple) {
3564
+            $sql =
3565
+                "SELECT FLOOR(d_year/100+1) AS century, COUNT(*) AS total" .
3566
+                " FROM `##dates`" .
3567
+                " WHERE d_file={$this->tree->getTreeId()} AND d_year<>0 AND d_fact = 'DIV' AND d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')";
3568
+            if ($year1 >= 0 && $year2 >= 0) {
3569
+                $sql .= " AND d_year BETWEEN '{$year1}' AND '{$year2}'";
3570
+            }
3571
+            $sql .= " GROUP BY century ORDER BY century";
3572
+        } elseif ($first) {
3573
+            $years = '';
3574
+            if ($year1 >= 0 && $year2 >= 0) {
3575
+                $years = " divorced.d_year BETWEEN '{$year1}' AND '{$year2}' AND";
3576
+            }
3577
+            $sql =
3578
+                " SELECT fam.f_id AS fams, fam.f_husb, fam.f_wife, divorced.d_julianday2 AS age, divorced.d_month AS month, indi.i_id AS indi" .
3579
+                " FROM `##families` AS fam" .
3580
+                " LEFT JOIN `##dates` AS divorced ON divorced.d_file = {$this->tree->getTreeId()}" .
3581
+                " LEFT JOIN `##individuals` AS indi ON indi.i_file = {$this->tree->getTreeId()}" .
3582
+                " WHERE" .
3583
+                " divorced.d_gid = fam.f_id AND" .
3584
+                " fam.f_file = {$this->tree->getTreeId()} AND" .
3585
+                " divorced.d_fact = 'DIV' AND" .
3586
+                " divorced.d_julianday2 <> 0 AND" .
3587
+                $years .
3588
+                " (indi.i_id = fam.f_husb OR indi.i_id = fam.f_wife)" .
3589
+                " ORDER BY fams, indi, age ASC";
3590
+        } else {
3591
+            $sql =
3592
+                "SELECT d_month, COUNT(*) AS total FROM `##dates` " .
3593
+                "WHERE d_file={$this->tree->getTreeId()} AND d_fact = 'DIV'";
3594
+            if ($year1 >= 0 && $year2 >= 0) {
3595
+                $sql .= " AND d_year BETWEEN '{$year1}' AND '{$year2}'";
3596
+            }
3597
+            $sql .= " GROUP BY d_month";
3598
+        }
3599
+        $rows = $this->runSql($sql);
3600
+        if (!isset($rows)) {
3601
+            return '';
3602
+        }
3603
+        if ($simple) {
3604
+            if (isset($params[0]) && $params[0] != '') {
3605
+                $size = strtolower($params[0]);
3606
+            } else {
3607
+                $size = $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y;
3608
+            }
3609
+            if (isset($params[1]) && $params[1] != '') {
3610
+                $color_from = strtolower($params[1]);
3611
+            } else {
3612
+                $color_from = $WT_STATS_CHART_COLOR1;
3613
+            }
3614
+            if (isset($params[2]) && $params[2] != '') {
3615
+                $color_to = strtolower($params[2]);
3616
+            } else {
3617
+                $color_to = $WT_STATS_CHART_COLOR2;
3618
+            }
3619
+            $sizes = explode('x', $size);
3620
+            $tot   = 0;
3621
+            foreach ($rows as $values) {
3622
+                $tot += (int) $values['total'];
3623
+            }
3624
+            // Beware divide by zero
3625
+            if ($tot === 0) {
3626
+                return '';
3627
+            }
3628
+            $centuries = '';
3629
+            $counts    = array();
3630
+            foreach ($rows as $values) {
3631
+                $counts[] = round(100 * $values['total'] / $tot, 0);
3632
+                $centuries .= $this->centuryName($values['century']) . ' - ' . I18N::number($values['total']) . '|';
3633
+            }
3634
+            $chd = $this->arrayToExtendedEncoding($counts);
3635
+            $chl = substr($centuries, 0, -1);
3636
+
3637
+            return "<img src=\"https://chart.googleapis.com/chart?cht=p3&amp;chd=e:{$chd}&amp;chs={$size}&amp;chco={$color_from},{$color_to}&amp;chf=bg,s,ffffff00&amp;chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Divorces by century') . "\" title=\"" . I18N::translate('Divorces by century') . "\" />";
3638
+        }
3639
+
3640
+        return $rows;
3641
+    }
3642
+
3643
+    /**
3644
+     * Find the earliest marriage.
3645
+     *
3646
+     * @return string
3647
+     */
3648
+    public function firstMarriage() {
3649
+        return $this->mortalityQuery('full', 'ASC', 'MARR');
3650
+    }
3651
+
3652
+    /**
3653
+     * Find the year of the earliest marriage.
3654
+     *
3655
+     * @return string
3656
+     */
3657
+    public function firstMarriageYear() {
3658
+        return $this->mortalityQuery('year', 'ASC', 'MARR');
3659
+    }
3660
+
3661
+    /**
3662
+     * Find the names of spouses of the earliest marriage.
3663
+     *
3664
+     * @return string
3665
+     */
3666
+    public function firstMarriageName() {
3667
+        return $this->mortalityQuery('name', 'ASC', 'MARR');
3668
+    }
3669
+
3670
+    /**
3671
+     * Find the place of the earliest marriage.
3672
+     *
3673
+     * @return string
3674
+     */
3675
+    public function firstMarriagePlace() {
3676
+        return $this->mortalityQuery('place', 'ASC', 'MARR');
3677
+    }
3678
+
3679
+    /**
3680
+     * Find the latest marriage.
3681
+     *
3682
+     * @return string
3683
+     */
3684
+    public function lastMarriage() {
3685
+        return $this->mortalityQuery('full', 'DESC', 'MARR');
3686
+    }
3687
+
3688
+    /**
3689
+     * Find the year of the latest marriage.
3690
+     *
3691
+     * @return string
3692
+     */
3693
+    public function lastMarriageYear() {
3694
+        return $this->mortalityQuery('year', 'DESC', 'MARR');
3695
+    }
3696
+
3697
+    /**
3698
+     * Find the names of spouses of the latest marriage.
3699
+     *
3700
+     * @return string
3701
+     */
3702
+    public function lastMarriageName() {
3703
+        return $this->mortalityQuery('name', 'DESC', 'MARR');
3704
+    }
3705
+
3706
+    /**
3707
+     * Find the location of the latest marriage.
3708
+     *
3709
+     * @return string
3710
+     */
3711
+    public function lastMarriagePlace() {
3712
+        return $this->mortalityQuery('place', 'DESC', 'MARR');
3713
+    }
3714
+
3715
+    /**
3716
+     * General query on marriages.
3717
+     *
3718
+     * @param string[] $params
3719
+     *
3720
+     * @return string
3721
+     */
3722
+    public function statsMarr($params = array()) {
3723
+        return $this->statsMarrQuery(true, false, -1, -1, $params);
3724
+    }
3725
+
3726
+    /**
3727
+     * Find the earliest divorce.
3728
+     *
3729
+     * @return string
3730
+     */
3731
+    public function firstDivorce() {
3732
+        return $this->mortalityQuery('full', 'ASC', 'DIV');
3733
+    }
3734
+
3735
+    /**
3736
+     * Find the year of the earliest divorce.
3737
+     *
3738
+     * @return string
3739
+     */
3740
+    public function firstDivorceYear() {
3741
+        return $this->mortalityQuery('year', 'ASC', 'DIV');
3742
+    }
3743
+
3744
+    /**
3745
+     * Find the names of individuals in the earliest divorce.
3746
+     *
3747
+     * @return string
3748
+     */
3749
+    public function firstDivorceName() {
3750
+        return $this->mortalityQuery('name', 'ASC', 'DIV');
3751
+    }
3752
+
3753
+    /**
3754
+     * Find the location of the earliest divorce.
3755
+     *
3756
+     * @return string
3757
+     */
3758
+    public function firstDivorcePlace() {
3759
+        return $this->mortalityQuery('place', 'ASC', 'DIV');
3760
+    }
3761
+
3762
+    /**
3763
+     * Find the latest divorce.
3764
+     *
3765
+     * @return string
3766
+     */
3767
+    public function lastDivorce() {
3768
+        return $this->mortalityQuery('full', 'DESC', 'DIV');
3769
+    }
3770
+
3771
+    /**
3772
+     * Find the year of the latest divorce.
3773
+     *
3774
+     * @return string
3775
+     */
3776
+    public function lastDivorceYear() {
3777
+        return $this->mortalityQuery('year', 'DESC', 'DIV');
3778
+    }
3779
+
3780
+    /**
3781
+     * Find the names of the individuals in the latest divorce.
3782
+     *
3783
+     * @return string
3784
+     */
3785
+    public function lastDivorceName() {
3786
+        return $this->mortalityQuery('name', 'DESC', 'DIV');
3787
+    }
3788
+
3789
+    /**
3790
+     * Find the location of the latest divorce.
3791
+     *
3792
+     * @return string
3793
+     */
3794
+    public function lastDivorcePlace() {
3795
+        return $this->mortalityQuery('place', 'DESC', 'DIV');
3796
+    }
3797
+
3798
+    /**
3799
+     * General divorce query.
3800
+     *
3801
+     * @param string[] $params
3802
+     *
3803
+     * @return string
3804
+     */
3805
+    public function statsDiv($params = array()) {
3806
+        return $this->statsDivQuery(true, false, -1, -1, $params);
3807
+    }
3808
+
3809
+    /**
3810
+     * General query on ages at marriage.
3811
+     *
3812
+     * @param bool     $simple
3813
+     * @param string   $sex
3814
+     * @param int      $year1
3815
+     * @param int      $year2
3816
+     * @param string[] $params
3817
+     *
3818
+     * @return array|string
3819
+     */
3820
+    public function statsMarrAgeQuery($simple = true, $sex = 'M', $year1 = -1, $year2 = -1, $params = array()) {
3821
+        if ($simple) {
3822
+            if (isset($params[0]) && $params[0] != '') {
3823
+                $size = strtolower($params[0]);
3824
+            } else {
3825
+                $size = '200x250';
3826
+            }
3827
+            $sizes = explode('x', $size);
3828
+            $rows  = $this->runSql(
3829
+                "SELECT " .
3830
+                " ROUND(AVG(married.d_julianday2-birth.d_julianday1-182.5)/365.25,1) AS age, " .
3831
+                " FLOOR(married.d_year/100+1) AS century, " .
3832
+                " 'M' AS sex " .
3833
+                "FROM `##dates` AS married " .
3834
+                "JOIN `##families` AS fam ON (married.d_gid=fam.f_id AND married.d_file=fam.f_file) " .
3835
+                "JOIN `##dates` AS birth ON (birth.d_gid=fam.f_husb AND birth.d_file=fam.f_file) " .
3836
+                "WHERE " .
3837
+                " '{$sex}' IN ('M', 'BOTH') AND " .
3838
+                " married.d_file={$this->tree->getTreeId()} AND married.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND married.d_fact='MARR' AND " .
3839
+                " birth.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND birth.d_fact='BIRT' AND " .
3840
+                " married.d_julianday1>birth.d_julianday1 AND birth.d_julianday1<>0 " .
3841
+                "GROUP BY century, sex " .
3842
+                "UNION ALL " .
3843
+                "SELECT " .
3844
+                " ROUND(AVG(married.d_julianday2-birth.d_julianday1-182.5)/365.25,1) AS age, " .
3845
+                " FLOOR(married.d_year/100+1) AS century, " .
3846
+                " 'F' AS sex " .
3847
+                "FROM `##dates` AS married " .
3848
+                "JOIN `##families` AS fam ON (married.d_gid=fam.f_id AND married.d_file=fam.f_file) " .
3849
+                "JOIN `##dates` AS birth ON (birth.d_gid=fam.f_wife AND birth.d_file=fam.f_file) " .
3850
+                "WHERE " .
3851
+                " '{$sex}' IN ('F', 'BOTH') AND " .
3852
+                " married.d_file={$this->tree->getTreeId()} AND married.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND married.d_fact='MARR' AND " .
3853
+                " birth.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND birth.d_fact='BIRT' AND " .
3854
+                " married.d_julianday1>birth.d_julianday1 AND birth.d_julianday1<>0 " .
3855
+                " GROUP BY century, sex ORDER BY century"
3856
+            );
3857
+            if (empty($rows)) {
3858
+                return '';
3859
+            }
3860
+            $max = 0;
3861
+            foreach ($rows as $values) {
3862
+                if ($max < $values['age']) {
3863
+                    $max = $values['age'];
3864
+                }
3865
+            }
3866
+            $chxl    = '0:|';
3867
+            $chmm    = '';
3868
+            $chmf    = '';
3869
+            $i       = 0;
3870
+            $countsm = '';
3871
+            $countsf = '';
3872
+            $countsa = '';
3873
+            $out     = array();
3874
+            foreach ($rows as $values) {
3875
+                $out[$values['century']][$values['sex']] = $values['age'];
3876
+            }
3877
+            foreach ($out as $century => $values) {
3878
+                if ($sizes[0] < 1000) {
3879
+                    $sizes[0] += 50;
3880
+                }
3881
+                $chxl .= $this->centuryName($century) . '|';
3882
+                $average = 0;
3883
+                if (isset($values['F'])) {
3884
+                    if ($max <= 50) {
3885
+                        $value = $values['F'] * 2;
3886
+                    } else {
3887
+                        $value = $values['F'];
3888
+                    }
3889
+                    $countsf .= $value . ',';
3890
+                    $average = $value;
3891
+                    $chmf .= 't' . $values['F'] . ',000000,1,' . $i . ',11,1|';
3892
+                } else {
3893
+                    $countsf .= '0,';
3894
+                    $chmf .= 't0,000000,1,' . $i . ',11,1|';
3895
+                }
3896
+                if (isset($values['M'])) {
3897
+                    if ($max <= 50) {
3898
+                        $value = $values['M'] * 2;
3899
+                    } else {
3900
+                        $value = $values['M'];
3901
+                    }
3902
+                    $countsm .= $value . ',';
3903
+                    if ($average == 0) {
3904
+                        $countsa .= $value . ',';
3905
+                    } else {
3906
+                        $countsa .= (($value + $average) / 2) . ',';
3907
+                    }
3908
+                    $chmm .= 't' . $values['M'] . ',000000,0,' . $i . ',11,1|';
3909
+                } else {
3910
+                    $countsm .= '0,';
3911
+                    if ($average == 0) {
3912
+                        $countsa .= '0,';
3913
+                    } else {
3914
+                        $countsa .= $value . ',';
3915
+                    }
3916
+                    $chmm .= 't0,000000,0,' . $i . ',11,1|';
3917
+                }
3918
+                $i++;
3919
+            }
3920
+            $countsm = substr($countsm, 0, -1);
3921
+            $countsf = substr($countsf, 0, -1);
3922
+            $countsa = substr($countsa, 0, -1);
3923
+            $chmf    = substr($chmf, 0, -1);
3924
+            $chd     = 't2:' . $countsm . '|' . $countsf . '|' . $countsa;
3925
+            if ($max <= 50) {
3926
+                $chxl .= '1:||' . I18N::translate('century') . '|2:|0|10|20|30|40|50|3:||' . I18N::translate('Age') . '|';
3927
+            } else {
3928
+                $chxl .= '1:||' . I18N::translate('century') . '|2:|0|10|20|30|40|50|60|70|80|90|100|3:||' . I18N::translate('Age') . '|';
3929
+            }
3930
+            if (count($rows) > 4 || mb_strlen(I18N::translate('Average age in century of marriage')) < 30) {
3931
+                $chtt = I18N::translate('Average age in century of marriage');
3932
+            } else {
3933
+                $offset  = 0;
3934
+                $counter = array();
3935
+                while ($offset = strpos(I18N::translate('Average age in century of marriage'), ' ', $offset + 1)) {
3936
+                    $counter[] = $offset;
3937
+                }
3938
+                $half = (int) (count($counter) / 2);
3939
+                $chtt = substr_replace(I18N::translate('Average age in century of marriage'), '|', $counter[$half], 1);
3940
+            }
3941
+
3942
+            return "<img src=\"" . "https://chart.googleapis.com/chart?cht=bvg&amp;chs={$sizes[0]}x{$sizes[1]}&amp;chm=D,FF0000,2,0,3,1|{$chmm}{$chmf}&amp;chf=bg,s,ffffff00|c,s,ffffff00&amp;chtt=" . rawurlencode($chtt) . "&amp;chd={$chd}&amp;chco=0000FF,FFA0CB,FF0000&amp;chbh=20,3&amp;chxt=x,x,y,y&amp;chxl=" . rawurlencode($chxl) . "&amp;chdl=" . rawurlencode(I18N::translate('Males') . "|" . I18N::translate('Females') . "|" . I18N::translate('Average age')) . "\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Average age in century of marriage') . "\" title=\"" . I18N::translate('Average age in century of marriage') . "\" />";
3943
+        } else {
3944
+            if ($year1 >= 0 && $year2 >= 0) {
3945
+                $years = " married.d_year BETWEEN {$year1} AND {$year2} AND ";
3946
+            } else {
3947
+                $years = '';
3948
+            }
3949
+            $rows = $this->runSql(
3950
+                "SELECT " .
3951
+                " fam.f_id, " .
3952
+                " birth.d_gid, " .
3953
+                " married.d_julianday2-birth.d_julianday1 AS age " .
3954
+                "FROM `##dates` AS married " .
3955
+                "JOIN `##families` AS fam ON (married.d_gid=fam.f_id AND married.d_file=fam.f_file) " .
3956
+                "JOIN `##dates` AS birth ON (birth.d_gid=fam.f_husb AND birth.d_file=fam.f_file) " .
3957
+                "WHERE " .
3958
+                " '{$sex}' IN ('M', 'BOTH') AND {$years} " .
3959
+                " married.d_file={$this->tree->getTreeId()} AND married.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND married.d_fact='MARR' AND " .
3960
+                " birth.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND birth.d_fact='BIRT' AND " .
3961
+                " married.d_julianday1>birth.d_julianday1 AND birth.d_julianday1<>0 " .
3962
+                "UNION ALL " .
3963
+                "SELECT " .
3964
+                " fam.f_id, " .
3965
+                " birth.d_gid, " .
3966
+                " married.d_julianday2-birth.d_julianday1 AS age " .
3967
+                "FROM `##dates` AS married " .
3968
+                "JOIN `##families` AS fam ON (married.d_gid=fam.f_id AND married.d_file=fam.f_file) " .
3969
+                "JOIN `##dates` AS birth ON (birth.d_gid=fam.f_wife AND birth.d_file=fam.f_file) " .
3970
+                "WHERE " .
3971
+                " '{$sex}' IN ('F', 'BOTH') AND {$years} " .
3972
+                " married.d_file={$this->tree->getTreeId()} AND married.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND married.d_fact='MARR' AND " .
3973
+                " birth.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND birth.d_fact='BIRT' AND " .
3974
+                " married.d_julianday1>birth.d_julianday1 AND birth.d_julianday1<>0 "
3975
+            );
3976
+
3977
+            return $rows;
3978
+        }
3979
+    }
3980
+
3981
+    /**
3982
+     * Find the youngest wife.
3983
+     *
3984
+     * @return string
3985
+     */
3986
+    public function youngestMarriageFemale() {
3987
+        return $this->marriageQuery('full', 'ASC', 'F', false);
3988
+    }
3989
+
3990
+    /**
3991
+     * Find the name of the youngest wife.
3992
+     *
3993
+     * @return string
3994
+     */
3995
+    public function youngestMarriageFemaleName() {
3996
+        return $this->marriageQuery('name', 'ASC', 'F', false);
3997
+    }
3998
+
3999
+    /**
4000
+     * Find the age of the youngest wife.
4001
+     *
4002
+     * @param bool $show_years
4003
+     *
4004
+     * @return string
4005
+     */
4006
+    public function youngestMarriageFemaleAge($show_years = false) {
4007
+        return $this->marriageQuery('age', 'ASC', 'F', $show_years);
4008
+    }
4009
+
4010
+    /**
4011
+     * Find the oldest wife.
4012
+     *
4013
+     * @return string
4014
+     */
4015
+    public function oldestMarriageFemale() {
4016
+        return $this->marriageQuery('full', 'DESC', 'F', false);
4017
+    }
4018
+
4019
+    /**
4020
+     * Find the name of the oldest wife.
4021
+     *
4022
+     * @return string
4023
+     */
4024
+    public function oldestMarriageFemaleName() {
4025
+        return $this->marriageQuery('name', 'DESC', 'F', false);
4026
+    }
4027
+
4028
+    /**
4029
+     * Find the age of the oldest wife.
4030
+     *
4031
+     * @param bool $show_years
4032
+     *
4033
+     * @return string
4034
+     */
4035
+    public function oldestMarriageFemaleAge($show_years = false) {
4036
+        return $this->marriageQuery('age', 'DESC', 'F', $show_years);
4037
+    }
4038
+
4039
+    /**
4040
+     * Find the youngest husband.
4041
+     *
4042
+     * @return string
4043
+     */
4044
+    public function youngestMarriageMale() {
4045
+        return $this->marriageQuery('full', 'ASC', 'M', false);
4046
+    }
4047
+
4048
+    /**
4049
+     * Find the name of the youngest husband.
4050
+     *
4051
+     * @return string
4052
+     */
4053
+    public function youngestMarriageMaleName() {
4054
+        return $this->marriageQuery('name', 'ASC', 'M', false);
4055
+    }
4056
+
4057
+    /**
4058
+     * Find the age of the youngest husband.
4059
+     *
4060
+     * @param bool $show_years
4061
+     *
4062
+     * @return string
4063
+     */
4064
+    public function youngestMarriageMaleAge($show_years = false) {
4065
+        return $this->marriageQuery('age', 'ASC', 'M', $show_years);
4066
+    }
4067
+
4068
+    /**
4069
+     * Find the oldest husband.
4070
+     *
4071
+     * @return string
4072
+     */
4073
+    public function oldestMarriageMale() {
4074
+        return $this->marriageQuery('full', 'DESC', 'M', false);
4075
+    }
4076
+
4077
+    /**
4078
+     * Find the name of the oldest husband.
4079
+     *
4080
+     * @return string
4081
+     */
4082
+    public function oldestMarriageMaleName() {
4083
+        return $this->marriageQuery('name', 'DESC', 'M', false);
4084
+    }
4085
+
4086
+    /**
4087
+     * Find the age of the oldest husband.
4088
+     *
4089
+     * @param bool $show_years
4090
+     *
4091
+     * @return string
4092
+     */
4093
+    public function oldestMarriageMaleAge($show_years = false) {
4094
+        return $this->marriageQuery('age', 'DESC', 'M', $show_years);
4095
+    }
4096
+
4097
+    /**
4098
+     * General query on marriage ages.
4099
+     *
4100
+     * @param string[] $params
4101
+     *
4102
+     * @return string
4103
+     */
4104
+    public function statsMarrAge($params = array()) {
4105
+        return $this->statsMarrAgeQuery(true, 'BOTH', -1, -1, $params);
4106
+    }
4107
+
4108
+    /**
4109
+     * Find the age between husband and wife.
4110
+     *
4111
+     * @param string[] $params
4112
+     *
4113
+     * @return string
4114
+     */
4115
+    public function ageBetweenSpousesMF($params = array()) {
4116
+        return $this->ageBetweenSpousesQuery('nolist', 'DESC', $params);
4117
+    }
4118
+
4119
+    /**
4120
+     * Find the age between husband and wife.
4121
+     *
4122
+     * @param string[] $params
4123
+     *
4124
+     * @return string
4125
+     */
4126
+    public function ageBetweenSpousesMFList($params = array()) {
4127
+        return $this->ageBetweenSpousesQuery('list', 'DESC', $params);
4128
+    }
4129
+
4130
+    /**
4131
+     * Find the age between wife and husband..
4132
+     *
4133
+     * @param string[] $params
4134
+     *
4135
+     * @return string
4136
+     */
4137
+    public function ageBetweenSpousesFM($params = array()) {
4138
+        return $this->ageBetweenSpousesQuery('nolist', 'ASC', $params);
4139
+    }
4140
+
4141
+    /**
4142
+     * Find the age between wife and husband..
4143
+     *
4144
+     * @param string[] $params
4145
+     *
4146
+     * @return string
4147
+     */
4148
+    public function ageBetweenSpousesFMList($params = array()) {
4149
+        return $this->ageBetweenSpousesQuery('list', 'ASC', $params);
4150
+    }
4151
+
4152
+    /**
4153
+     * General query on marriage ages.
4154
+     *
4155
+     * @return string
4156
+     */
4157
+    public function topAgeOfMarriageFamily() {
4158
+        return $this->ageOfMarriageQuery('name', 'DESC', array('1'));
4159
+    }
4160
+
4161
+    /**
4162
+     * General query on marriage ages.
4163
+     *
4164
+     * @return string
4165
+     */
4166
+    public function topAgeOfMarriage() {
4167
+        return $this->ageOfMarriageQuery('age', 'DESC', array('1'));
4168
+    }
4169
+
4170
+    /**
4171
+     * General query on marriage ages.
4172
+     *
4173
+     * @param string[] $params
4174
+     *
4175
+     * @return string
4176
+     */
4177
+    public function topAgeOfMarriageFamilies($params = array()) {
4178
+        return $this->ageOfMarriageQuery('nolist', 'DESC', $params);
4179
+    }
4180
+
4181
+    /**
4182
+     * General query on marriage ages.
4183
+     *
4184
+     * @param string[] $params
4185
+     *
4186
+     * @return string
4187
+     */
4188
+    public function topAgeOfMarriageFamiliesList($params = array()) {
4189
+        return $this->ageOfMarriageQuery('list', 'DESC', $params);
4190
+    }
4191
+
4192
+    /**
4193
+     * General query on marriage ages.
4194
+     *
4195
+     * @return string
4196
+     */
4197
+    public function minAgeOfMarriageFamily() {
4198
+        return $this->ageOfMarriageQuery('name', 'ASC', array('1'));
4199
+    }
4200
+
4201
+    /**
4202
+     * General query on marriage ages.
4203
+     *
4204
+     * @return string
4205
+     */
4206
+    public function minAgeOfMarriage() {
4207
+        return $this->ageOfMarriageQuery('age', 'ASC', array('1'));
4208
+    }
4209
+
4210
+    /**
4211
+     * General query on marriage ages.
4212
+     *
4213
+     * @param string[] $params
4214
+     *
4215
+     * @return string
4216
+     */
4217
+    public function minAgeOfMarriageFamilies($params = array()) {
4218
+        return $this->ageOfMarriageQuery('nolist', 'ASC', $params);
4219
+    }
4220
+
4221
+    /**
4222
+     * General query on marriage ages.
4223
+     *
4224
+     * @param string[] $params
4225
+     *
4226
+     * @return string
4227
+     */
4228
+    public function minAgeOfMarriageFamiliesList($params = array()) {
4229
+        return $this->ageOfMarriageQuery('list', 'ASC', $params);
4230
+    }
4231
+
4232
+    /**
4233
+     * Find the youngest mother
4234
+     *
4235
+     * @return string
4236
+     */
4237
+    public function youngestMother() {
4238
+        return $this->parentsQuery('full', 'ASC', 'F');
4239
+    }
4240
+
4241
+    /**
4242
+     * Find the name of the youngest mother.
4243
+     *
4244
+     * @return string
4245
+     */
4246
+    public function youngestMotherName() {
4247
+        return $this->parentsQuery('name', 'ASC', 'F');
4248
+    }
4249
+
4250
+    /**
4251
+     * Find the age of the youngest mother.
4252
+     *
4253
+     * @param bool $show_years
4254
+     *
4255
+     * @return string
4256
+     */
4257
+    public function youngestMotherAge($show_years = false) {
4258
+        return $this->parentsQuery('age', 'ASC', 'F', $show_years);
4259
+    }
4260
+
4261
+    /**
4262
+     * Find the oldest mother.
4263
+     *
4264
+     * @return string
4265
+     */
4266
+    public function oldestMother() {
4267
+        return $this->parentsQuery('full', 'DESC', 'F');
4268
+    }
4269
+
4270
+    /**
4271
+     * Find the name of the oldest mother.
4272
+     *
4273
+     * @return string
4274
+     */
4275
+    public function oldestMotherName() {
4276
+        return $this->parentsQuery('name', 'DESC', 'F');
4277
+    }
4278
+
4279
+    /**
4280
+     * Find the age of the oldest mother.
4281
+     *
4282
+     * @param bool $show_years
4283
+     *
4284
+     * @return string
4285
+     */
4286
+    public function oldestMotherAge($show_years = false) {
4287
+        return $this->parentsQuery('age', 'DESC', 'F', $show_years);
4288
+    }
4289
+
4290
+    /**
4291
+     * Find the youngest father.
4292
+     *
4293
+     * @return string
4294
+     */
4295
+    public function youngestFather() {
4296
+        return $this->parentsQuery('full', 'ASC', 'M');
4297
+    }
4298
+
4299
+    /**
4300
+     * Find the name of the youngest father.
4301
+     *
4302
+     * @return string
4303
+     */
4304
+    public function youngestFatherName() {
4305
+        return $this->parentsQuery('name', 'ASC', 'M');
4306
+    }
4307
+
4308
+    /**
4309
+     * Find the age of the youngest father.
4310
+     *
4311
+     * @param bool $show_years
4312
+     *
4313
+     * @return string
4314
+     */
4315
+    public function youngestFatherAge($show_years = false) {
4316
+        return $this->parentsQuery('age', 'ASC', 'M', $show_years);
4317
+    }
4318
+
4319
+    /**
4320
+     * Find the oldest father.
4321
+     *
4322
+     * @return string
4323
+     */
4324
+    public function oldestFather() {
4325
+        return $this->parentsQuery('full', 'DESC', 'M');
4326
+    }
4327
+
4328
+    /**
4329
+     * Find the name of the oldest father.
4330
+     *
4331
+     * @return string
4332
+     */
4333
+    public function oldestFatherName() {
4334
+        return $this->parentsQuery('name', 'DESC', 'M');
4335
+    }
4336
+
4337
+    /**
4338
+     * Find the age of the oldest father.
4339
+     *
4340
+     * @param bool $show_years
4341
+     *
4342
+     * @return string
4343
+     */
4344
+    public function oldestFatherAge($show_years = false) {
4345
+        return $this->parentsQuery('age', 'DESC', 'M', $show_years);
4346
+    }
4347
+
4348
+    /**
4349
+     * Number of husbands.
4350
+     *
4351
+     * @return string
4352
+     */
4353
+    public function totalMarriedMales() {
4354
+        $n = Database::prepare("SELECT COUNT(DISTINCT f_husb) FROM `##families` WHERE f_file=? AND f_gedcom LIKE '%\\n1 MARR%'")
4355
+            ->execute(array($this->tree->getTreeId()))
4356
+            ->fetchOne();
4357
+
4358
+        return I18N::number($n);
4359
+    }
4360
+
4361
+    /**
4362
+     * Number of wives.
4363
+     *
4364
+     * @return string
4365
+     */
4366
+    public function totalMarriedFemales() {
4367
+        $n = Database::prepare("SELECT COUNT(DISTINCT f_wife) FROM `##families` WHERE f_file=? AND f_gedcom LIKE '%\\n1 MARR%'")
4368
+            ->execute(array($this->tree->getTreeId()))
4369
+            ->fetchOne();
4370
+
4371
+        return I18N::number($n);
4372
+    }
4373
+
4374
+    /**
4375
+     * General query on family.
4376
+     *
4377
+     * @param string $type
4378
+     *
4379
+     * @return string
4380
+     */
4381
+    private function familyQuery($type = 'full') {
4382
+        $rows = $this->runSql(
4383
+            " SELECT f_numchil AS tot, f_id AS id" .
4384
+            " FROM `##families`" .
4385
+            " WHERE" .
4386
+            " f_file={$this->tree->getTreeId()}" .
4387
+            " AND f_numchil = (" .
4388
+            "  SELECT max( f_numchil )" .
4389
+            "  FROM `##families`" .
4390
+            "  WHERE f_file ={$this->tree->getTreeId()}" .
4391
+            " )" .
4392
+            " LIMIT 1"
4393
+        );
4394
+        if (!isset($rows[0])) {
4395
+            return '';
4396
+        }
4397
+        $row    = $rows[0];
4398
+        $family = Family::getInstance($row['id'], $this->tree);
4399
+        switch ($type) {
4400
+        default:
4401
+        case 'full':
4402
+            if ($family->canShow()) {
4403
+                $result = $family->formatList('span', false, $family->getFullName());
4404
+            } else {
4405
+                $result = I18N::translate('This information is private and cannot be shown.');
4406
+            }
4407
+            break;
4408
+        case 'size':
4409
+            $result = I18N::number($row['tot']);
4410
+            break;
4411
+        case 'name':
4412
+            $result = "<a href=\"" . $family->getHtmlUrl() . "\">" . $family->getFullName() . '</a>';
4413
+            break;
4414
+        }
4415
+
4416
+        return $result;
4417
+    }
4418
+
4419
+    /**
4420
+     * General query on families.
4421
+     *
4422
+     * @param string   $type
4423
+     * @param string[] $params
4424
+     *
4425
+     * @return string
4426
+     */
4427
+    private function topTenFamilyQuery($type = 'list', $params = array()) {
4428
+        if (isset($params[0])) {
4429
+            $total = (int) $params[0];
4430
+        } else {
4431
+            $total = 10;
4432
+        }
4433
+        $rows = $this->runSql(
4434
+            "SELECT f_numchil AS tot, f_id AS id" .
4435
+            " FROM `##families`" .
4436
+            " WHERE" .
4437
+            " f_file={$this->tree->getTreeId()}" .
4438
+            " ORDER BY tot DESC" .
4439
+            " LIMIT " . $total
4440
+        );
4441
+        if (!isset($rows[0])) {
4442
+            return '';
4443
+        }
4444
+        if (count($rows) < $total) {
4445
+            $total = count($rows);
4446
+        }
4447
+        $top10 = array();
4448
+        for ($c = 0; $c < $total; $c++) {
4449
+            $family = Family::getInstance($rows[$c]['id'], $this->tree);
4450
+            if ($family->canShow()) {
4451
+                if ($type === 'list') {
4452
+                    $top10[] =
4453
+                        '<li><a href="' . $family->getHtmlUrl() . '">' . $family->getFullName() . '</a> - ' .
4454
+                        I18N::plural('%s child', '%s children', $rows[$c]['tot'], I18N::number($rows[$c]['tot']));
4455
+                } else {
4456
+                    $top10[] =
4457
+                        '<a href="' . $family->getHtmlUrl() . '">' . $family->getFullName() . '</a> - ' .
4458
+                        I18N::plural('%s child', '%s children', $rows[$c]['tot'], I18N::number($rows[$c]['tot']));
4459
+                }
4460
+            }
4461
+        }
4462
+        if ($type === 'list') {
4463
+            $top10 = implode('', $top10);
4464
+        } else {
4465
+            $top10 = implode('; ', $top10);
4466
+        }
4467
+        if (I18N::direction() === 'rtl') {
4468
+            $top10 = str_replace(array('[', ']', '(', ')', '+'), array('&rlm;[', '&rlm;]', '&rlm;(', '&rlm;)', '&rlm;+'), $top10);
4469
+        }
4470
+        if ($type === 'list') {
4471
+            return '<ul>' . $top10 . '</ul>';
4472
+        }
4473
+
4474
+        return $top10;
4475
+    }
4476
+
4477
+    /**
4478
+     * Find the ages between siblings.
4479
+     *
4480
+     * @param string   $type
4481
+     * @param string[] $params
4482
+     *
4483
+     * @return string
4484
+     */
4485
+    private function ageBetweenSiblingsQuery($type = 'list', $params = array()) {
4486
+        if (isset($params[0])) {
4487
+            $total = (int) $params[0];
4488
+        } else {
4489
+            $total = 10;
4490
+        }
4491
+        if (isset($params[1])) {
4492
+            $one = $params[1];
4493
+        } else {
4494
+            $one = false;
4495
+        } // each family only once if true
4496
+        $rows = $this->runSql(
4497
+            " SELECT DISTINCT" .
4498
+            " link1.l_from AS family," .
4499
+            " link1.l_to AS ch1," .
4500
+            " link2.l_to AS ch2," .
4501
+            " child1.d_julianday2-child2.d_julianday2 AS age" .
4502
+            " FROM `##link` AS link1" .
4503
+            " LEFT JOIN `##dates` AS child1 ON child1.d_file = {$this->tree->getTreeId()}" .
4504
+            " LEFT JOIN `##dates` AS child2 ON child2.d_file = {$this->tree->getTreeId()}" .
4505
+            " LEFT JOIN `##link` AS link2 ON link2.l_file = {$this->tree->getTreeId()}" .
4506
+            " WHERE" .
4507
+            " link1.l_file = {$this->tree->getTreeId()} AND" .
4508
+            " link1.l_from = link2.l_from AND" .
4509
+            " link1.l_type = 'CHIL' AND" .
4510
+            " child1.d_gid = link1.l_to AND" .
4511
+            " child1.d_fact = 'BIRT' AND" .
4512
+            " link2.l_type = 'CHIL' AND" .
4513
+            " child2.d_gid = link2.l_to AND" .
4514
+            " child2.d_fact = 'BIRT' AND" .
4515
+            " child1.d_julianday2 > child2.d_julianday2 AND" .
4516
+            " child2.d_julianday2 <> 0 AND" .
4517
+            " child1.d_gid <> child2.d_gid" .
4518
+            " ORDER BY age DESC" .
4519
+            " LIMIT " . $total
4520
+        );
4521
+        if (!isset($rows[0])) {
4522
+            return '';
4523
+        }
4524
+        $top10 = array();
4525
+        $dist  = array();
4526
+        foreach ($rows as $fam) {
4527
+            $family = Family::getInstance($fam['family'], $this->tree);
4528
+            $child1 = Individual::getInstance($fam['ch1'], $this->tree);
4529
+            $child2 = Individual::getInstance($fam['ch2'], $this->tree);
4530
+            if ($type == 'name') {
4531
+                if ($child1->canShow() && $child2->canShow()) {
4532
+                    $return = '<a href="' . $child2->getHtmlUrl() . '">' . $child2->getFullName() . '</a> ';
4533
+                    $return .= I18N::translate('and') . ' ';
4534
+                    $return .= '<a href="' . $child1->getHtmlUrl() . '">' . $child1->getFullName() . '</a>';
4535
+                    $return .= ' <a href="' . $family->getHtmlUrl() . '">[' . I18N::translate('View this family') . ']</a>';
4536
+                } else {
4537
+                    $return = I18N::translate('This information is private and cannot be shown.');
4538
+                }
4539
+
4540
+                return $return;
4541
+            }
4542
+            $age = $fam['age'];
4543
+            if ((int) ($age / 365.25) > 0) {
4544
+                $age = (int) ($age / 365.25) . 'y';
4545
+            } elseif ((int) ($age / 30.4375) > 0) {
4546
+                $age = (int) ($age / 30.4375) . 'm';
4547
+            } else {
4548
+                $age = $age . 'd';
4549
+            }
4550
+            $age = FunctionsDate::getAgeAtEvent($age);
4551
+            if ($type == 'age') {
4552
+                return $age;
4553
+            }
4554
+            if ($type == 'list') {
4555
+                if ($one && !in_array($fam['family'], $dist)) {
4556
+                    if ($child1->canShow() && $child2->canShow()) {
4557
+                        $return = "<li>";
4558
+                        $return .= "<a href=\"" . $child2->getHtmlUrl() . "\">" . $child2->getFullName() . "</a> ";
4559
+                        $return .= I18N::translate('and') . " ";
4560
+                        $return .= "<a href=\"" . $child1->getHtmlUrl() . "\">" . $child1->getFullName() . "</a>";
4561
+                        $return .= " (" . $age . ")";
4562
+                        $return .= " <a href=\"" . $family->getHtmlUrl() . "\">[" . I18N::translate('View this family') . "]</a>";
4563
+                        $return .= '</li>';
4564
+                        $top10[] = $return;
4565
+                        $dist[]  = $fam['family'];
4566
+                    }
4567
+                } elseif (!$one && $child1->canShow() && $child2->canShow()) {
4568
+                    $return = "<li>";
4569
+                    $return .= "<a href=\"" . $child2->getHtmlUrl() . "\">" . $child2->getFullName() . "</a> ";
4570
+                    $return .= I18N::translate('and') . " ";
4571
+                    $return .= "<a href=\"" . $child1->getHtmlUrl() . "\">" . $child1->getFullName() . "</a>";
4572
+                    $return .= " (" . $age . ")";
4573
+                    $return .= " <a href=\"" . $family->getHtmlUrl() . "\">[" . I18N::translate('View this family') . "]</a>";
4574
+                    $return .= '</li>';
4575
+                    $top10[] = $return;
4576
+                }
4577
+            } else {
4578
+                if ($child1->canShow() && $child2->canShow()) {
4579
+                    $return = $child2->formatList('span', false, $child2->getFullName());
4580
+                    $return .= "<br>" . I18N::translate('and') . "<br>";
4581
+                    $return .= $child1->formatList('span', false, $child1->getFullName());
4582
+                    $return .= "<br><a href=\"" . $family->getHtmlUrl() . "\">[" . I18N::translate('View this family') . "]</a>";
4583
+
4584
+                    return $return;
4585
+                } else {
4586
+                    return I18N::translate('This information is private and cannot be shown.');
4587
+                }
4588
+            }
4589
+        }
4590
+        if ($type === 'list') {
4591
+            $top10 = implode('', $top10);
4592
+        }
4593
+        if (I18N::direction() === 'rtl') {
4594
+            $top10 = str_replace(array('[', ']', '(', ')', '+'), array('&rlm;[', '&rlm;]', '&rlm;(', '&rlm;)', '&rlm;+'), $top10);
4595
+        }
4596
+        if ($type === 'list') {
4597
+            return '<ul>' . $top10 . '</ul>';
4598
+        }
4599
+
4600
+        return $top10;
4601
+    }
4602
+
4603
+    /**
4604
+     * Find the month in the year of the birth of the first child.
4605
+     *
4606
+     * @param bool     $simple
4607
+     * @param bool     $sex
4608
+     * @param int      $year1
4609
+     * @param int      $year2
4610
+     * @param string[] $params
4611
+     *
4612
+     * @return string|string[][]
4613
+     */
4614
+    public function monthFirstChildQuery($simple = true, $sex = false, $year1 = -1, $year2 = -1, $params = array()) {
4615
+        $WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
4616
+        $WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
4617
+        $WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
4618
+        $WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
4619
+
4620
+        if ($year1 >= 0 && $year2 >= 0) {
4621
+            $sql_years = " AND (d_year BETWEEN '{$year1}' AND '{$year2}')";
4622
+        } else {
4623
+            $sql_years = '';
4624
+        }
4625
+        if ($sex) {
4626
+            $sql_sex1 = ', i_sex';
4627
+            $sql_sex2 = " JOIN `##individuals` AS child ON child1.d_file = i_file AND child1.d_gid = child.i_id ";
4628
+        } else {
4629
+            $sql_sex1 = '';
4630
+            $sql_sex2 = '';
4631
+        }
4632
+        $sql =
4633
+            "SELECT d_month{$sql_sex1}, COUNT(*) AS total " .
4634
+            "FROM (" .
4635
+            " SELECT family{$sql_sex1}, MIN(date) AS d_date, d_month" .
4636
+            " FROM (" .
4637
+            "  SELECT" .
4638
+            "  link1.l_from AS family," .
4639
+            "  link1.l_to AS child," .
4640
+            "  child1.d_julianday2 AS date," .
4641
+            "  child1.d_month as d_month" .
4642
+            $sql_sex1 .
4643
+            "  FROM `##link` AS link1" .
4644
+            "  LEFT JOIN `##dates` AS child1 ON child1.d_file = {$this->tree->getTreeId()}" .
4645
+            $sql_sex2 .
4646
+            "  WHERE" .
4647
+            "  link1.l_file = {$this->tree->getTreeId()} AND" .
4648
+            "  link1.l_type = 'CHIL' AND" .
4649
+            "  child1.d_gid = link1.l_to AND" .
4650
+            "  child1.d_fact = 'BIRT' AND" .
4651
+            "  child1.d_month IN ('JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC')" .
4652
+            $sql_years .
4653
+            "  ORDER BY date" .
4654
+            " ) AS children" .
4655
+            " GROUP BY family, d_month{$sql_sex1}" .
4656
+            ") AS first_child " .
4657
+            "GROUP BY d_month";
4658
+        if ($sex) {
4659
+            $sql .= ', i_sex';
4660
+        }
4661
+        $rows = $this->runSql($sql);
4662
+        if ($simple) {
4663
+            if (isset($params[0]) && $params[0] != '') {
4664
+                $size = strtolower($params[0]);
4665
+            } else {
4666
+                $size = $WT_STATS_S_CHART_X . 'x' . $WT_STATS_S_CHART_Y;
4667
+            }
4668
+            if (isset($params[1]) && $params[1] != '') {
4669
+                $color_from = strtolower($params[1]);
4670
+            } else {
4671
+                $color_from = $WT_STATS_CHART_COLOR1;
4672
+            }
4673
+            if (isset($params[2]) && $params[2] != '') {
4674
+                $color_to = strtolower($params[2]);
4675
+            } else {
4676
+                $color_to = $WT_STATS_CHART_COLOR2;
4677
+            }
4678
+            $sizes = explode('x', $size);
4679
+            $tot   = 0;
4680
+            foreach ($rows as $values) {
4681
+                $tot += $values['total'];
4682
+            }
4683
+            // Beware divide by zero
4684
+            if ($tot == 0) {
4685
+                return '';
4686
+            }
4687
+            $text   = '';
4688
+            $counts = array();
4689
+            foreach ($rows as $values) {
4690
+                $counts[] = round(100 * $values['total'] / $tot, 0);
4691
+                switch ($values['d_month']) {
4692
+                default:
4693
+                case 'JAN':
4694
+                    $values['d_month'] = 1;
4695
+                    break;
4696
+                case 'FEB':
4697
+                    $values['d_month'] = 2;
4698
+                    break;
4699
+                case 'MAR':
4700
+                    $values['d_month'] = 3;
4701
+                    break;
4702
+                case 'APR':
4703
+                    $values['d_month'] = 4;
4704
+                    break;
4705
+                case 'MAY':
4706
+                    $values['d_month'] = 5;
4707
+                    break;
4708
+                case 'JUN':
4709
+                    $values['d_month'] = 6;
4710
+                    break;
4711
+                case 'JUL':
4712
+                    $values['d_month'] = 7;
4713
+                    break;
4714
+                case 'AUG':
4715
+                    $values['d_month'] = 8;
4716
+                    break;
4717
+                case 'SEP':
4718
+                    $values['d_month'] = 9;
4719
+                    break;
4720
+                case 'OCT':
4721
+                    $values['d_month'] = 10;
4722
+                    break;
4723
+                case 'NOV':
4724
+                    $values['d_month'] = 11;
4725
+                    break;
4726
+                case 'DEC':
4727
+                    $values['d_month'] = 12;
4728
+                    break;
4729
+                }
4730
+                $text .= I18N::translate(ucfirst(strtolower(($values['d_month'])))) . ' - ' . $values['total'] . '|';
4731
+            }
4732
+            $chd = $this->arrayToExtendedEncoding($counts);
4733
+            $chl = substr($text, 0, -1);
4734
+
4735
+            return '<img src="https://chart.googleapis.com/chart?cht=p3&amp;chd=e:' . $chd . '&amp;chs=' . $size . '&amp;chco=' . $color_from . ',' . $color_to . '&amp;chf=bg,s,ffffff00&amp;chl=' . $chl . '" width="' . $sizes[0] . '" height="' . $sizes[1] . '" alt="' . I18N::translate('Month of birth of first child in a relation') . '" title="' . I18N::translate('Month of birth of first child in a relation') . '" />';
4736
+        }
4737
+
4738
+        return $rows;
4739
+    }
4740
+
4741
+    /**
4742
+     * Find the family with the most children.
4743
+     *
4744
+     * @return string
4745
+     */
4746
+    public function largestFamily() {
4747
+        return $this->familyQuery('full');
4748
+    }
4749
+
4750
+    /**
4751
+     * Find the number of children in the largest family.
4752
+     *
4753
+     * @return string
4754
+     */
4755
+    public function largestFamilySize() {
4756
+        return $this->familyQuery('size');
4757
+    }
4758
+
4759
+    /**
4760
+     * Find the family with the most children.
4761
+     *
4762
+     * @return string
4763
+     */
4764
+    public function largestFamilyName() {
4765
+        return $this->familyQuery('name');
4766
+    }
4767
+
4768
+    /**
4769
+     * The the families with the most children.
4770
+     *
4771
+     * @param string[] $params
4772
+     *
4773
+     * @return string
4774
+     */
4775
+    public function topTenLargestFamily($params = array()) {
4776
+        return $this->topTenFamilyQuery('nolist', $params);
4777
+    }
4778
+
4779
+    /**
4780
+     * Find the families with the most children.
4781
+     *
4782
+     * @param string[] $params
4783
+     *
4784
+     * @return string
4785
+     */
4786
+    public function topTenLargestFamilyList($params = array()) {
4787
+        return $this->topTenFamilyQuery('list', $params);
4788
+    }
4789
+
4790
+    /**
4791
+     * Create a chart of the largest families.
4792
+     *
4793
+     * @param string[] $params
4794
+     *
4795
+     * @return string
4796
+     */
4797
+    public function chartLargestFamilies($params = array()) {
4798
+        $WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
4799
+        $WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
4800
+        $WT_STATS_L_CHART_X    = Theme::theme()->parameter('stats-large-chart-x');
4801
+        $WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
4802
+
4803
+        if (isset($params[0]) && $params[0] != '') {
4804
+            $size = strtolower($params[0]);
4805
+        } else {
4806
+            $size = $WT_STATS_L_CHART_X . 'x' . $WT_STATS_S_CHART_Y;
4807
+        }
4808
+        if (isset($params[1]) && $params[1] != '') {
4809
+            $color_from = strtolower($params[1]);
4810
+        } else {
4811
+            $color_from = $WT_STATS_CHART_COLOR1;
4812
+        }
4813
+        if (isset($params[2]) && $params[2] != '') {
4814
+            $color_to = strtolower($params[2]);
4815
+        } else {
4816
+            $color_to = $WT_STATS_CHART_COLOR2;
4817
+        }
4818
+        if (isset($params[3]) && $params[3] != '') {
4819
+            $total = strtolower($params[3]);
4820
+        } else {
4821
+            $total = 10;
4822
+        }
4823
+        $sizes = explode('x', $size);
4824
+        $total = (int) $total;
4825
+        $rows  = $this->runSql(
4826
+            " SELECT f_numchil AS tot, f_id AS id" .
4827
+            " FROM `##families`" .
4828
+            " WHERE f_file={$this->tree->getTreeId()}" .
4829
+            " ORDER BY tot DESC" .
4830
+            " LIMIT " . $total
4831
+        );
4832
+        if (!isset($rows[0])) {
4833
+            return '';
4834
+        }
4835
+        $tot = 0;
4836
+        foreach ($rows as $row) {
4837
+            $tot += (int) $row['tot'];
4838
+        }
4839
+        $chd = '';
4840
+        $chl = array();
4841
+        foreach ($rows as $row) {
4842
+            $family = Family::getInstance($row['id'], $this->tree);
4843
+            if ($family->canShow()) {
4844
+                if ($tot == 0) {
4845
+                    $per = 0;
4846
+                } else {
4847
+                    $per = round(100 * $row['tot'] / $tot, 0);
4848
+                }
4849
+                $chd .= $this->arrayToExtendedEncoding(array($per));
4850
+                $chl[] = htmlspecialchars_decode(strip_tags($family->getFullName())) . ' - ' . I18N::number($row['tot']);
4851
+            }
4852
+        }
4853
+        $chl = rawurlencode(implode('|', $chl));
4854
+
4855
+        return "<img src=\"https://chart.googleapis.com/chart?cht=p3&amp;chd=e:{$chd}&amp;chs={$size}&amp;chco={$color_from},{$color_to}&amp;chf=bg,s,ffffff00&amp;chl={$chl}\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Largest families') . "\" title=\"" . I18N::translate('Largest families') . "\" />";
4856
+    }
4857
+
4858
+    /**
4859
+     * Count the total children.
4860
+     *
4861
+     * @return string
4862
+     */
4863
+    public function totalChildren() {
4864
+        $rows = $this->runSql("SELECT SUM(f_numchil) AS tot FROM `##families` WHERE f_file={$this->tree->getTreeId()}");
4865
+
4866
+        return I18N::number($rows[0]['tot']);
4867
+    }
4868
+
4869
+    /**
4870
+     * Find the average number of children in families.
4871
+     *
4872
+     * @return string
4873
+     */
4874
+    public function averageChildren() {
4875
+        $rows = $this->runSql("SELECT AVG(f_numchil) AS tot FROM `##families` WHERE f_file={$this->tree->getTreeId()}");
4876
+
4877
+        return I18N::number($rows[0]['tot'], 2);
4878
+    }
4879
+
4880
+    /**
4881
+     * General query on familes/children.
4882
+     *
4883
+     * @param bool     $simple
4884
+     * @param string   $sex
4885
+     * @param int      $year1
4886
+     * @param int      $year2
4887
+     * @param string[] $params
4888
+     *
4889
+     * @return string|string[][]
4890
+     */
4891
+    public function statsChildrenQuery($simple = true, $sex = 'BOTH', $year1 = -1, $year2 = -1, $params = array()) {
4892
+        if ($simple) {
4893
+            if (isset($params[0]) && $params[0] != '') {
4894
+                $size = strtolower($params[0]);
4895
+            } else {
4896
+                $size = '220x200';
4897
+            }
4898
+            $sizes = explode('x', $size);
4899
+            $max   = 0;
4900
+            $rows  = $this->runSql(
4901
+                " SELECT ROUND(AVG(f_numchil),2) AS num, FLOOR(d_year/100+1) AS century" .
4902
+                " FROM  `##families`" .
4903
+                " JOIN  `##dates` ON (d_file = f_file AND d_gid=f_id)" .
4904
+                " WHERE f_file = {$this->tree->getTreeId()}" .
4905
+                " AND   d_julianday1<>0" .
4906
+                " AND   d_fact = 'MARR'" .
4907
+                " AND   d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')" .
4908
+                " GROUP BY century" .
4909
+                " ORDER BY century");
4910
+            if (empty($rows)) {
4911
+                return '';
4912
+            }
4913
+            foreach ($rows as $values) {
4914
+                if ($max < $values['num']) {
4915
+                    $max = $values['num'];
4916
+                }
4917
+            }
4918
+            $chm    = "";
4919
+            $chxl   = "0:|";
4920
+            $i      = 0;
4921
+            $counts = array();
4922
+            foreach ($rows as $values) {
4923
+                if ($sizes[0] < 980) {
4924
+                    $sizes[0] += 38;
4925
+                }
4926
+                $chxl .= $this->centuryName($values['century']) . "|";
4927
+                if ($max <= 5) {
4928
+                    $counts[] = round($values['num'] * 819.2 - 1, 1);
4929
+                } elseif ($max <= 10) {
4930
+                    $counts[] = round($values['num'] * 409.6, 1);
4931
+                } else {
4932
+                    $counts[] = round($values['num'] * 204.8, 1);
4933
+                }
4934
+                $chm .= 't' . $values['num'] . ',000000,0,' . $i . ',11,1|';
4935
+                $i++;
4936
+            }
4937
+            $chd = $this->arrayToExtendedEncoding($counts);
4938
+            $chm = substr($chm, 0, -1);
4939
+            if ($max <= 5) {
4940
+                $chxl .= "1:||" . I18N::translate('century') . "|2:|0|1|2|3|4|5|3:||" . I18N::translate('Number of children') . "|";
4941
+            } elseif ($max <= 10) {
4942
+                $chxl .= "1:||" . I18N::translate('century') . "|2:|0|1|2|3|4|5|6|7|8|9|10|3:||" . I18N::translate('Number of children') . "|";
4943
+            } else {
4944
+                $chxl .= "1:||" . I18N::translate('century') . "|2:|0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|3:||" . I18N::translate('Number of children') . "|";
4945
+            }
4946
+
4947
+            return "<img src=\"https://chart.googleapis.com/chart?cht=bvg&amp;chs={$sizes[0]}x{$sizes[1]}&amp;chf=bg,s,ffffff00|c,s,ffffff00&amp;chm=D,FF0000,0,0,3,1|{$chm}&amp;chd=e:{$chd}&amp;chco=0000FF&amp;chbh=30,3&amp;chxt=x,x,y,y&amp;chxl=" . rawurlencode($chxl) . "\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Average number of children per family') . "\" title=\"" . I18N::translate('Average number of children per family') . "\" />";
4948
+        } else {
4949
+            if ($sex == 'M') {
4950
+                $sql =
4951
+                    "SELECT num, COUNT(*) AS total FROM " .
4952
+                    "(SELECT count(i_sex) AS num FROM `##link` " .
4953
+                    "LEFT OUTER JOIN `##individuals` " .
4954
+                    "ON l_from=i_id AND l_file=i_file AND i_sex='M' AND l_type='FAMC' " .
4955
+                    "JOIN `##families` ON f_file=l_file AND f_id=l_to WHERE f_file={$this->tree->getTreeId()} GROUP BY l_to" .
4956
+                    ") boys" .
4957
+                    " GROUP BY num" .
4958
+                    " ORDER BY num";
4959
+            } elseif ($sex == 'F') {
4960
+                $sql =
4961
+                    "SELECT num, COUNT(*) AS total FROM " .
4962
+                    "(SELECT count(i_sex) AS num FROM `##link` " .
4963
+                    "LEFT OUTER JOIN `##individuals` " .
4964
+                    "ON l_from=i_id AND l_file=i_file AND i_sex='F' AND l_type='FAMC' " .
4965
+                    "JOIN `##families` ON f_file=l_file AND f_id=l_to WHERE f_file={$this->tree->getTreeId()} GROUP BY l_to" .
4966
+                    ") girls" .
4967
+                    " GROUP BY num" .
4968
+                    " ORDER BY num";
4969
+            } else {
4970
+                $sql = "SELECT f_numchil, COUNT(*) AS total FROM `##families` ";
4971
+                if ($year1 >= 0 && $year2 >= 0) {
4972
+                    $sql .=
4973
+                        "AS fam LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->getTreeId()}"
4974
+                        . " WHERE"
4975
+                        . " married.d_gid = fam.f_id AND"
4976
+                        . " fam.f_file = {$this->tree->getTreeId()} AND"
4977
+                        . " married.d_fact = 'MARR' AND"
4978
+                        . " married.d_year BETWEEN '{$year1}' AND '{$year2}'";
4979
+                } else {
4980
+                    $sql .= "WHERE f_file={$this->tree->getTreeId()}";
4981
+                }
4982
+                $sql .= " GROUP BY f_numchil";
4983
+            }
4984
+            $rows = $this->runSql($sql);
4985
+
4986
+            return $rows;
4987
+        }
4988
+    }
4989
+
4990
+    /**
4991
+     * Genearl query on families/children.
4992
+     *
4993
+     * @param string[] $params
4994
+     *
4995
+     * @return string
4996
+     */
4997
+    public function statsChildren($params = array()) {
4998
+        return $this->statsChildrenQuery(true, 'BOTH', -1, -1, $params);
4999
+    }
5000
+
5001
+    /**
5002
+     * Find the names of siblings with the widest age gap.
5003
+     *
5004
+     * @param string[] $params
5005
+     *
5006
+     * @return string
5007
+     */
5008
+    public function topAgeBetweenSiblingsName($params = array()) {
5009
+        return $this->ageBetweenSiblingsQuery('name', $params);
5010
+    }
5011
+
5012
+    /**
5013
+     * Find the widest age gap between siblings.
5014
+     *
5015
+     * @param string[] $params
5016
+     *
5017
+     * @return string
5018
+     */
5019
+    public function topAgeBetweenSiblings($params = array()) {
5020
+        return $this->ageBetweenSiblingsQuery('age', $params);
5021
+    }
5022
+
5023
+    /**
5024
+     * Find the name of siblings with the widest age gap.
5025
+     *
5026
+     * @param string[] $params
5027
+     *
5028
+     * @return string
5029
+     */
5030
+    public function topAgeBetweenSiblingsFullName($params = array()) {
5031
+        return $this->ageBetweenSiblingsQuery('nolist', $params);
5032
+    }
5033
+
5034
+    /**
5035
+     * Find the siblings with the widest age gaps.
5036
+     *
5037
+     * @param string[] $params
5038
+     *
5039
+     * @return string
5040
+     */
5041
+    public function topAgeBetweenSiblingsList($params = array()) {
5042
+        return $this->ageBetweenSiblingsQuery('list', $params);
5043
+    }
5044
+
5045
+    /**
5046
+     * Find the families with no children.
5047
+     *
5048
+     * @return string
5049
+     */
5050
+    private function noChildrenFamiliesQuery() {
5051
+        $rows = $this->runSql(
5052
+            " SELECT COUNT(*) AS tot" .
5053
+            " FROM  `##families`" .
5054
+            " WHERE f_numchil = 0 AND f_file = {$this->tree->getTreeId()}");
5055
+
5056
+        return $rows[0]['tot'];
5057
+    }
5058
+
5059
+    /**
5060
+     * Find the families with no children.
5061
+     *
5062
+     * @return string
5063
+     */
5064
+    public function noChildrenFamilies() {
5065
+        return I18N::number($this->noChildrenFamiliesQuery());
5066
+    }
5067
+
5068
+    /**
5069
+     * Find the families with no children.
5070
+     *
5071
+     * @param string[] $params
5072
+     *
5073
+     * @return string
5074
+     */
5075
+    public function noChildrenFamiliesList($params = array()) {
5076
+        if (isset($params[0]) && $params[0] != '') {
5077
+            $type = strtolower($params[0]);
5078
+        } else {
5079
+            $type = 'list';
5080
+        }
5081
+        $rows = $this->runSql(
5082
+            " SELECT f_id AS family" .
5083
+            " FROM `##families` AS fam" .
5084
+            " WHERE f_numchil = 0 AND fam.f_file = {$this->tree->getTreeId()}");
5085
+        if (!isset($rows[0])) {
5086
+            return '';
5087
+        }
5088
+        $top10 = array();
5089
+        foreach ($rows as $row) {
5090
+            $family = Family::getInstance($row['family'], $this->tree);
5091
+            if ($family->canShow()) {
5092
+                if ($type == 'list') {
5093
+                    $top10[] = "<li><a href=\"" . $family->getHtmlUrl() . "\">" . $family->getFullName() . "</a></li>";
5094
+                } else {
5095
+                    $top10[] = "<a href=\"" . $family->getHtmlUrl() . "\">" . $family->getFullName() . "</a>";
5096
+                }
5097
+            }
5098
+        }
5099
+        if ($type == 'list') {
5100
+            $top10 = implode('', $top10);
5101
+        } else {
5102
+            $top10 = implode('; ', $top10);
5103
+        }
5104
+        if (I18N::direction() === 'rtl') {
5105
+            $top10 = str_replace(array('[', ']', '(', ')', '+'), array('&rlm;[', '&rlm;]', '&rlm;(', '&rlm;)', '&rlm;+'), $top10);
5106
+        }
5107
+        if ($type === 'list') {
5108
+            return '<ul>' . $top10 . '</ul>';
5109
+        }
5110
+
5111
+        return $top10;
5112
+    }
5113
+
5114
+    /**
5115
+     * Create a chart of children with no families.
5116
+     *
5117
+     * @param string[] $params
5118
+     *
5119
+     * @return string
5120
+     */
5121
+    public function chartNoChildrenFamilies($params = array()) {
5122
+        if (isset($params[0]) && $params[0] != '') {
5123
+            $size = strtolower($params[0]);
5124
+        } else {
5125
+            $size = '220x200';
5126
+        }
5127
+        if (isset($params[1]) && $params[1] != '') {
5128
+            $year1 = $params[1];
5129
+        } else {
5130
+            $year1 = -1;
5131
+        }
5132
+        if (isset($params[2]) && $params[2] != '') {
5133
+            $year2 = $params[2];
5134
+        } else {
5135
+            $year2 = -1;
5136
+        }
5137
+        $sizes = explode('x', $size);
5138
+        if ($year1 >= 0 && $year2 >= 0) {
5139
+            $years = " married.d_year BETWEEN '{$year1}' AND '{$year2}' AND";
5140
+        } else {
5141
+            $years = "";
5142
+        }
5143
+        $max  = 0;
5144
+        $tot  = 0;
5145
+        $rows = $this->runSql(
5146
+            "SELECT" .
5147
+            " COUNT(*) AS count," .
5148
+            " FLOOR(married.d_year/100+1) AS century" .
5149
+            " FROM" .
5150
+            " `##families` AS fam" .
5151
+            " JOIN" .
5152
+            " `##dates` AS married ON (married.d_file = fam.f_file AND married.d_gid = fam.f_id)" .
5153
+            " WHERE" .
5154
+            " f_numchil = 0 AND" .
5155
+            " fam.f_file = {$this->tree->getTreeId()} AND" .
5156
+            $years .
5157
+            " married.d_fact = 'MARR' AND" .
5158
+            " married.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@')" .
5159
+            " GROUP BY century ORDER BY century"
5160
+        );
5161
+        if (empty($rows)) {
5162
+            return '';
5163
+        }
5164
+        foreach ($rows as $values) {
5165
+            if ($max < $values['count']) {
5166
+                $max = $values['count'];
5167
+            }
5168
+            $tot += (int) $values['count'];
5169
+        }
5170
+        $unknown = $this->noChildrenFamiliesQuery() - $tot;
5171
+        if ($unknown > $max) {
5172
+            $max = $unknown;
5173
+        }
5174
+        $chm    = "";
5175
+        $chxl   = "0:|";
5176
+        $i      = 0;
5177
+        $counts = array();
5178
+        foreach ($rows as $values) {
5179
+            if ($sizes[0] < 980) {
5180
+                $sizes[0] += 38;
5181
+            }
5182
+            $chxl .= $this->centuryName($values['century']) . "|";
5183
+            $counts[] = round(4095 * $values['count'] / ($max + 1));
5184
+            $chm .= 't' . $values['count'] . ',000000,0,' . $i . ',11,1|';
5185
+            $i++;
5186
+        }
5187
+        $counts[] = round(4095 * $unknown / ($max + 1));
5188
+        $chd      = $this->arrayToExtendedEncoding($counts);
5189
+        $chm .= 't' . $unknown . ',000000,0,' . $i . ',11,1';
5190
+        $chxl .= I18N::translateContext('unknown century', 'Unknown') . "|1:||" . I18N::translate('century') . "|2:|0|";
5191
+        $step = $max + 1;
5192
+        for ($d = (int) ($max + 1); $d > 0; $d--) {
5193
+            if (($max + 1) < ($d * 10 + 1) && fmod(($max + 1), $d) == 0) {
5194
+                $step = $d;
5195
+            }
5196
+        }
5197
+        if ($step == (int) ($max + 1)) {
5198
+            for ($d = (int) ($max); $d > 0; $d--) {
5199
+                if ($max < ($d * 10 + 1) && fmod($max, $d) == 0) {
5200
+                    $step = $d;
5201
+                }
5202
+            }
5203
+        }
5204
+        for ($n = $step; $n <= ($max + 1); $n += $step) {
5205
+            $chxl .= $n . "|";
5206
+        }
5207
+        $chxl .= "3:||" . I18N::translate('Total families') . "|";
5208
+
5209
+        return "<img src=\"https://chart.googleapis.com/chart?cht=bvg&amp;chs={$sizes[0]}x{$sizes[1]}&amp;chf=bg,s,ffffff00|c,s,ffffff00&amp;chm=D,FF0000,0,0:" . ($i - 1) . ",3,1|{$chm}&amp;chd=e:{$chd}&amp;chco=0000FF,ffffff00&amp;chbh=30,3&amp;chxt=x,x,y,y&amp;chxl=" . rawurlencode($chxl) . "\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . I18N::translate('Number of families without children') . "\" title=\"" . I18N::translate('Number of families without children') . "\" />";
5210
+    }
5211
+
5212
+    /**
5213
+     * Find the couple with the most grandchildren.
5214
+     *
5215
+     * @param string   $type
5216
+     * @param string[] $params
5217
+     *
5218
+     * @return string
5219
+     */
5220
+    private function topTenGrandFamilyQuery($type = 'list', $params = array()) {
5221
+        if (isset($params[0])) {
5222
+            $total = (int) $params[0];
5223
+        } else {
5224
+            $total = 10;
5225
+        }
5226
+        $rows = $this->runSql(
5227
+            "SELECT COUNT(*) AS tot, f_id AS id" .
5228
+            " FROM `##families`" .
5229
+            " JOIN `##link` AS children ON children.l_file = {$this->tree->getTreeId()}" .
5230
+            " JOIN `##link` AS mchildren ON mchildren.l_file = {$this->tree->getTreeId()}" .
5231
+            " JOIN `##link` AS gchildren ON gchildren.l_file = {$this->tree->getTreeId()}" .
5232
+            " WHERE" .
5233
+            " f_file={$this->tree->getTreeId()} AND" .
5234
+            " children.l_from=f_id AND" .
5235
+            " children.l_type='CHIL' AND" .
5236
+            " children.l_to=mchildren.l_from AND" .
5237
+            " mchildren.l_type='FAMS' AND" .
5238
+            " mchildren.l_to=gchildren.l_from AND" .
5239
+            " gchildren.l_type='CHIL'" .
5240
+            " GROUP BY id" .
5241
+            " ORDER BY tot DESC" .
5242
+            " LIMIT " . $total
5243
+        );
5244
+        if (!isset($rows[0])) {
5245
+            return '';
5246
+        }
5247
+        $top10 = array();
5248
+        foreach ($rows as $row) {
5249
+            $family = Family::getInstance($row['id'], $this->tree);
5250
+            if ($family->canShow()) {
5251
+                if ($type === 'list') {
5252
+                    $top10[] =
5253
+                        '<li><a href="' . $family->getHtmlUrl() . '">' . $family->getFullName() . '</a> - ' .
5254
+                        I18N::plural('%s grandchild', '%s grandchildren', $row['tot'], I18N::number($row['tot']));
5255
+                } else {
5256
+                    $top10[] =
5257
+                        '<a href="' . $family->getHtmlUrl() . '">' . $family->getFullName() . '</a> - ' .
5258
+                        I18N::plural('%s grandchild', '%s grandchildren', $row['tot'], I18N::number($row['tot']));
5259
+                }
5260
+            }
5261
+        }
5262
+        if ($type === 'list') {
5263
+            $top10 = implode('', $top10);
5264
+        } else {
5265
+            $top10 = implode('; ', $top10);
5266
+        }
5267
+        if (I18N::direction() === 'rtl') {
5268
+            $top10 = str_replace(array('[', ']', '(', ')', '+'), array('&rlm;[', '&rlm;]', '&rlm;(', '&rlm;)', '&rlm;+'), $top10);
5269
+        }
5270
+        if ($type === 'list') {
5271
+            return '<ul>' . $top10 . '</ul>';
5272
+        }
5273
+
5274
+        return $top10;
5275
+    }
5276
+
5277
+    /**
5278
+     * Find the couple with the most grandchildren.
5279
+     *
5280
+     * @param string[] $params
5281
+     *
5282
+     * @return string
5283
+     */
5284
+    public function topTenLargestGrandFamily($params = array()) {
5285
+        return $this->topTenGrandFamilyQuery('nolist', $params);
5286
+    }
5287
+
5288
+    /**
5289
+     * Find the couple with the most grandchildren.
5290
+     *
5291
+     * @param string[] $params
5292
+     *
5293
+     * @return string
5294
+     */
5295
+    public function topTenLargestGrandFamilyList($params = array()) {
5296
+        return $this->topTenGrandFamilyQuery('list', $params);
5297
+    }
5298
+
5299
+    /**
5300
+     * Find common surnames.
5301
+     *
5302
+     * @param string   $type
5303
+     * @param bool     $show_tot
5304
+     * @param string[] $params
5305
+     *
5306
+     * @return string
5307
+     */
5308
+    private function commonSurnamesQuery($type = 'list', $show_tot = false, $params = array()) {
5309
+        $threshold          = empty($params[0]) ? 10 : (int) $params[0];
5310
+        $number_of_surnames = empty($params[1]) ? 10 : (int) $params[1];
5311
+        $sorting            = empty($params[2]) ? 'alpha' : $params[2];
5312
+
5313
+        $surname_list = FunctionsDb::getTopSurnames($this->tree->getTreeId(), $threshold, $number_of_surnames);
5314
+        if (empty($surname_list)) {
5315
+            return '';
5316
+        }
5317
+
5318
+        switch ($sorting) {
5319
+        default:
5320
+        case 'alpha':
5321
+            uksort($surname_list, '\Fisharebest\Webtrees\I18N::strcasecmp');
5322
+            break;
5323
+        case 'count':
5324
+            asort($surname_list);
5325
+            break;
5326
+        case 'rcount':
5327
+            arsort($surname_list);
5328
+            break;
5329
+        }
5330
+
5331
+        // Note that we count/display SPFX SURN, but sort/group under just SURN
5332
+        $surnames = array();
5333
+        foreach (array_keys($surname_list) as $surname) {
5334
+            $surnames = array_merge($surnames, QueryName::surnames($this->tree, $surname, '', false, false));
5335
+        }
5336
+
5337
+        return FunctionsPrintLists::surnameList($surnames, ($type == 'list' ? 1 : 2), $show_tot, 'indilist.php', $this->tree);
5338
+    }
5339
+
5340
+    /**
5341
+     * Find common surnames.
5342
+     *
5343
+     * @return string
5344
+     */
5345
+    public function getCommonSurname() {
5346
+        $surnames = array_keys(FunctionsDb::getTopSurnames($this->tree->getTreeId(), 1, 1));
5347
+
5348
+        return array_shift($surnames);
5349
+    }
5350
+
5351
+    /**
5352
+     * Find common surnames.
5353
+     *
5354
+     * @param string[] $params
5355
+     *
5356
+     * @return string
5357
+     */
5358
+    public function commonSurnames($params = array('', '', 'alpha')) {
5359
+        return $this->commonSurnamesQuery('nolist', false, $params);
5360
+    }
5361
+
5362
+    /**
5363
+     * Find common surnames.
5364
+     *
5365
+     * @param string[] $params
5366
+     *
5367
+     * @return string
5368
+     */
5369
+    public function commonSurnamesTotals($params = array('', '', 'rcount')) {
5370
+        return $this->commonSurnamesQuery('nolist', true, $params);
5371
+    }
5372
+
5373
+    /**
5374
+     * Find common surnames.
5375
+     *
5376
+     * @param string[] $params
5377
+     *
5378
+     * @return string
5379
+     */
5380
+    public function commonSurnamesList($params = array('', '', 'alpha')) {
5381
+        return $this->commonSurnamesQuery('list', false, $params);
5382
+    }
5383
+
5384
+    /**
5385
+     * Find common surnames.
5386
+     *
5387
+     * @param string[] $params
5388
+     *
5389
+     * @return string
5390
+     */
5391
+    public function commonSurnamesListTotals($params = array('', '', 'rcount')) {
5392
+        return $this->commonSurnamesQuery('list', true, $params);
5393
+    }
5394
+
5395
+    /**
5396
+     * Create a chart of common surnames.
5397
+     *
5398
+     * @param string[] $params
5399
+     *
5400
+     * @return string
5401
+     */
5402
+    public function chartCommonSurnames($params = array()) {
5403
+        $WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
5404
+        $WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
5405
+        $WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
5406
+        $WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
5407
+
5408
+        $size               = empty($params[0]) ? $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y : strtolower($params[0]);
5409
+        $color_from         = empty($params[1]) ? $WT_STATS_CHART_COLOR1 : strtolower($params[1]);
5410
+        $color_to           = empty($params[2]) ? $WT_STATS_CHART_COLOR2 : strtolower($params[2]);
5411
+        $number_of_surnames = empty($params[3]) ? 10 : (int) $params[3];
5412
+
5413
+        $sizes    = explode('x', $size);
5414
+        $tot_indi = $this->totalIndividualsQuery();
5415
+        $surnames = FunctionsDb::getTopSurnames($this->tree->getTreeId(), 0, $number_of_surnames);
5416
+        if (empty($surnames)) {
5417
+            return '';
5418
+        }
5419
+        $SURNAME_TRADITION = $this->tree->getPreference('SURNAME_TRADITION');
5420
+        $all_surnames      = array();
5421
+        $tot               = 0;
5422
+        foreach ($surnames as $surname => $num) {
5423
+            $all_surnames = array_merge($all_surnames, QueryName::surnames($this->tree, I18N::strtoupper($surname), '', false, false));
5424
+            $tot += $num;
5425
+        }
5426
+        $chd = '';
5427
+        $chl = array();
5428
+        foreach ($all_surnames as $surns) {
5429
+            $count_per = 0;
5430
+            $max_name  = 0;
5431
+            $top_name  = '';
5432
+            foreach ($surns as $spfxsurn => $indis) {
5433
+                $per = count($indis);
5434
+                $count_per += $per;
5435
+                // select most common surname from all variants
5436
+                if ($per > $max_name) {
5437
+                    $max_name = $per;
5438
+                    $top_name = $spfxsurn;
5439
+                }
5440
+            }
5441
+            switch ($SURNAME_TRADITION) {
5442
+            case 'polish':
5443
+                // most common surname should be in male variant (Kowalski, not Kowalska)
5444
+                $top_name = preg_replace(array('/ska$/', '/cka$/', '/dzka$/', '/żka$/'), array('ski', 'cki', 'dzki', 'żki'), $top_name);
5445
+            }
5446
+            $per = round(100 * $count_per / $tot_indi, 0);
5447
+            $chd .= $this->arrayToExtendedEncoding(array($per));
5448
+            $chl[] = $top_name . ' - ' . I18N::number($count_per);
5449
+
5450
+        }
5451
+        $per = round(100 * ($tot_indi - $tot) / $tot_indi, 0);
5452
+        $chd .= $this->arrayToExtendedEncoding(array($per));
5453
+        $chl[] = I18N::translate('Other') . ' - ' . I18N::number($tot_indi - $tot);
5454
+
5455
+        $chart_title = implode(I18N::$list_separator, $chl);
5456
+        $chl         = implode('|', $chl);
5457
+
5458
+        return '<img src="https://chart.googleapis.com/chart?cht=p3&amp;chd=e:' . $chd . '&amp;chs=' . $size . '&amp;chco=' . $color_from . ',' . $color_to . '&amp;chf=bg,s,ffffff00&amp;chl=' . rawurlencode($chl) . '" width="' . $sizes[0] . '" height="' . $sizes[1] . '" alt="' . $chart_title . '" title="' . $chart_title . '" />';
5459
+    }
5460
+
5461
+    /**
5462
+     * Find common given names.
5463
+     *
5464
+     * @param string   $sex
5465
+     * @param string   $type
5466
+     * @param bool     $show_tot
5467
+     * @param string[] $params
5468
+     *
5469
+     * @return string
5470
+     */
5471
+    private function commonGivenQuery($sex = 'B', $type = 'list', $show_tot = false, $params = array()) {
5472
+        if (isset($params[0]) && $params[0] != '' && $params[0] >= 0) {
5473
+            $threshold = (int) $params[0];
5474
+        } else {
5475
+            $threshold = 1;
5476
+        }
5477
+        if (isset($params[1]) && $params[1] != '' && $params[1] >= 0) {
5478
+            $maxtoshow = (int) $params[1];
5479
+        } else {
5480
+            $maxtoshow = 10;
5481
+        }
5482
+
5483
+        switch ($sex) {
5484
+        case 'M':
5485
+            $sex_sql = "i_sex='M'";
5486
+            break;
5487
+        case 'F':
5488
+            $sex_sql = "i_sex='F'";
5489
+            break;
5490
+        case 'U':
5491
+            $sex_sql = "i_sex='U'";
5492
+            break;
5493
+        case 'B':
5494
+        default:
5495
+            $sex_sql = "i_sex<>'U'";
5496
+            break;
5497
+        }
5498
+        $ged_id = $this->tree->getTreeId();
5499
+
5500
+        $rows = Database::prepare("SELECT n_givn, COUNT(*) AS num FROM `##name` JOIN `##individuals` ON (n_id=i_id AND n_file=i_file) WHERE n_file={$ged_id} AND n_type<>'_MARNM' AND n_givn NOT IN ('@P.N.', '') AND LENGTH(n_givn)>1 AND {$sex_sql} GROUP BY n_id, n_givn")
5501
+            ->fetchAll();
5502
+        $nameList = array();
5503
+        foreach ($rows as $row) {
5504
+            // Split “John Thomas” into “John” and “Thomas” and count against both totals
5505
+            foreach (explode(' ', $row->n_givn) as $given) {
5506
+                // Exclude initials and particles.
5507
+                if (!preg_match('/^([A-Z]|[a-z]{1,3})$/', $given)) {
5508
+                    if (array_key_exists($given, $nameList)) {
5509
+                        $nameList[$given] += $row->num;
5510
+                    } else {
5511
+                        $nameList[$given] = $row->num;
5512
+                    }
5513
+                }
5514
+            }
5515
+        }
5516
+        arsort($nameList, SORT_NUMERIC);
5517
+        $nameList = array_slice($nameList, 0, $maxtoshow);
5518
+
5519
+        if (count($nameList) == 0) {
5520
+            return '';
5521
+        }
5522
+        if ($type == 'chart') {
5523
+            return $nameList;
5524
+        }
5525
+        $common = array();
5526
+        foreach ($nameList as $given => $total) {
5527
+            if ($maxtoshow !== -1) {
5528
+                if ($maxtoshow-- <= 0) {
5529
+                    break;
5530
+                }
5531
+            }
5532
+            if ($total < $threshold) {
5533
+                break;
5534
+            }
5535
+            if ($show_tot) {
5536
+                $tot = ' (' . I18N::number($total) . ')';
5537
+            } else {
5538
+                $tot = '';
5539
+            }
5540
+            switch ($type) {
5541
+            case 'table':
5542
+                $common[] = '<tr><td>' . $given . '</td><td>' . I18N::number($total) . '</td><td>' . $total . '</td></tr>';
5543
+                break;
5544
+            case 'list':
5545
+                $common[] = '<li><span dir="auto">' . $given . '</span>' . $tot . '</li>';
5546
+                break;
5547
+            case 'nolist':
5548
+                $common[] = '<span dir="auto">' . $given . '</span>' . $tot;
5549
+                break;
5550
+            }
5551
+        }
5552
+        if ($common) {
5553
+            switch ($type) {
5554
+            case 'table':
5555
+                global $controller;
5556
+                $table_id = Uuid::uuid4(); // lists requires a unique ID in case there are multiple lists per page
5557
+                $controller
5558
+                    ->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
5559
+                    ->addInlineJavascript('
5560 5560
 					jQuery("#' . $table_id . '").dataTable({
5561 5561
 						dom: \'t\',
5562 5562
 						autoWidth: false,
@@ -5574,1417 +5574,1417 @@  discard block
 block discarded – undo
5574 5574
 					});
5575 5575
 					jQuery("#' . $table_id . '").css("visibility", "visible");
5576 5576
 				');
5577
-				$lookup = array('M' => I18N::translate('Male'), 'F' => I18N::translate('Female'), 'U' => I18N::translateContext('unknown gender', 'Unknown'), 'B' => I18N::translate('All'));
5578
-
5579
-				return '<table id="' . $table_id . '" class="givn-list"><thead><tr><th class="ui-state-default" colspan="3">' . $lookup[$sex] . '</th></tr><tr><th>' . I18N::translate('Name') . '</th><th>' . I18N::translate('Count') . '</th><th>COUNT</th></tr></thead><tbody>' . implode('', $common) . '</tbody></table>';
5580
-			case 'list':
5581
-				return '<ul>' . implode('', $common) . '</ul>';
5582
-			case 'nolist':
5583
-				return implode(I18N::$list_separator, $common);
5584
-			default:
5585
-				return '';
5586
-			}
5587
-		} else {
5588
-			return '';
5589
-		}
5590
-	}
5591
-
5592
-	/**
5593
-	 * Find common give names.
5594
-	 *
5595
-	 * @param string[] $params
5596
-	 *
5597
-	 * @return string
5598
-	 */
5599
-	public function commonGiven($params = array(1, 10, 'alpha')) {
5600
-		return $this->commonGivenQuery('B', 'nolist', false, $params);
5601
-	}
5602
-
5603
-	/**
5604
-	 * Find common give names.
5605
-	 *
5606
-	 * @param string[] $params
5607
-	 *
5608
-	 * @return string
5609
-	 */
5610
-	public function commonGivenTotals($params = array(1, 10, 'rcount')) {
5611
-		return $this->commonGivenQuery('B', 'nolist', true, $params);
5612
-	}
5613
-
5614
-	/**
5615
-	 * Find common give names.
5616
-	 *
5617
-	 * @param string[] $params
5618
-	 *
5619
-	 * @return string
5620
-	 */
5621
-	public function commonGivenList($params = array(1, 10, 'alpha')) {
5622
-		return $this->commonGivenQuery('B', 'list', false, $params);
5623
-	}
5624
-
5625
-	/**
5626
-	 * Find common give names.
5627
-	 *
5628
-	 * @param string[] $params
5629
-	 *
5630
-	 * @return string
5631
-	 */
5632
-	public function commonGivenListTotals($params = array(1, 10, 'rcount')) {
5633
-		return $this->commonGivenQuery('B', 'list', true, $params);
5634
-	}
5635
-
5636
-	/**
5637
-	 * Find common give names.
5638
-	 *
5639
-	 * @param string[] $params
5640
-	 *
5641
-	 * @return string
5642
-	 */
5643
-	public function commonGivenTable($params = array(1, 10, 'rcount')) {
5644
-		return $this->commonGivenQuery('B', 'table', false, $params);
5645
-	}
5646
-
5647
-	/**
5648
-	 * Find common give names of females.
5649
-	 *
5650
-	 * @param string[] $params
5651
-	 *
5652
-	 * @return string
5653
-	 */
5654
-	public function commonGivenFemale($params = array(1, 10, 'alpha')) {
5655
-		return $this->commonGivenQuery('F', 'nolist', false, $params);
5656
-	}
5657
-
5658
-	/**
5659
-	 * Find common give names of females.
5660
-	 *
5661
-	 * @param string[] $params
5662
-	 *
5663
-	 * @return string
5664
-	 */
5665
-	public function commonGivenFemaleTotals($params = array(1, 10, 'rcount')) {
5666
-		return $this->commonGivenQuery('F', 'nolist', true, $params);
5667
-	}
5668
-
5669
-	/**
5670
-	 * Find common give names of females.
5671
-	 *
5672
-	 * @param string[] $params
5673
-	 *
5674
-	 * @return string
5675
-	 */
5676
-	public function commonGivenFemaleList($params = array(1, 10, 'alpha')) {
5677
-		return $this->commonGivenQuery('F', 'list', false, $params);
5678
-	}
5679
-
5680
-	/**
5681
-	 * Find common give names of females.
5682
-	 *
5683
-	 * @param string[] $params
5684
-	 *
5685
-	 * @return string
5686
-	 */
5687
-	public function commonGivenFemaleListTotals($params = array(1, 10, 'rcount')) {
5688
-		return $this->commonGivenQuery('F', 'list', true, $params);
5689
-	}
5690
-
5691
-	/**
5692
-	 * Find common give names of females.
5693
-	 *
5694
-	 * @param string[] $params
5695
-	 *
5696
-	 * @return string
5697
-	 */
5698
-	public function commonGivenFemaleTable($params = array(1, 10, 'rcount')) {
5699
-		return $this->commonGivenQuery('F', 'table', false, $params);
5700
-	}
5701
-
5702
-	/**
5703
-	 * Find common give names of males.
5704
-	 *
5705
-	 * @param string[] $params
5706
-	 *
5707
-	 * @return string
5708
-	 */
5709
-	public function commonGivenMale($params = array(1, 10, 'alpha')) {
5710
-		return $this->commonGivenQuery('M', 'nolist', false, $params);
5711
-	}
5712
-
5713
-	/**
5714
-	 * Find common give names of males.
5715
-	 *
5716
-	 * @param string[] $params
5717
-	 *
5718
-	 * @return string
5719
-	 */
5720
-	public function commonGivenMaleTotals($params = array(1, 10, 'rcount')) {
5721
-		return $this->commonGivenQuery('M', 'nolist', true, $params);
5722
-	}
5723
-
5724
-	/**
5725
-	 * Find common give names of males.
5726
-	 *
5727
-	 * @param string[] $params
5728
-	 *
5729
-	 * @return string
5730
-	 */
5731
-	public function commonGivenMaleList($params = array(1, 10, 'alpha')) {
5732
-		return $this->commonGivenQuery('M', 'list', false, $params);
5733
-	}
5734
-
5735
-	/**
5736
-	 * Find common give names of males.
5737
-	 *
5738
-	 * @param string[] $params
5739
-	 *
5740
-	 * @return string
5741
-	 */
5742
-	public function commonGivenMaleListTotals($params = array(1, 10, 'rcount')) {
5743
-		return $this->commonGivenQuery('M', 'list', true, $params);
5744
-	}
5745
-
5746
-	/**
5747
-	 * Find common give names of males.
5748
-	 *
5749
-	 * @param string[] $params
5750
-	 *
5751
-	 * @return string
5752
-	 */
5753
-	public function commonGivenMaleTable($params = array(1, 10, 'rcount')) {
5754
-		return $this->commonGivenQuery('M', 'table', false, $params);
5755
-	}
5756
-
5757
-	/**
5758
-	 * Find common give names of unknown sexes.
5759
-	 *
5760
-	 * @param string[] $params
5761
-	 *
5762
-	 * @return string
5763
-	 */
5764
-	public function commonGivenUnknown($params = array(1, 10, 'alpha')) {
5765
-		return $this->commonGivenQuery('U', 'nolist', false, $params);
5766
-	}
5767
-
5768
-	/**
5769
-	 * Find common give names of unknown sexes.
5770
-	 *
5771
-	 * @param string[] $params
5772
-	 *
5773
-	 * @return string
5774
-	 */
5775
-	public function commonGivenUnknownTotals($params = array(1, 10, 'rcount')) {
5776
-		return $this->commonGivenQuery('U', 'nolist', true, $params);
5777
-	}
5778
-
5779
-	/**
5780
-	 * Find common give names of unknown sexes.
5781
-	 *
5782
-	 * @param string[] $params
5783
-	 *
5784
-	 * @return string
5785
-	 */
5786
-	public function commonGivenUnknownList($params = array(1, 10, 'alpha')) {
5787
-		return $this->commonGivenQuery('U', 'list', false, $params);
5788
-	}
5789
-
5790
-	/**
5791
-	 * Find common give names of unknown sexes.
5792
-	 *
5793
-	 * @param string[] $params
5794
-	 *
5795
-	 * @return string
5796
-	 */
5797
-	public function commonGivenUnknownListTotals($params = array(1, 10, 'rcount')) {
5798
-		return $this->commonGivenQuery('U', 'list', true, $params);
5799
-	}
5800
-
5801
-	/**
5802
-	 * Find common give names of unknown sexes.
5803
-	 *
5804
-	 * @param string[] $params
5805
-	 *
5806
-	 * @return string
5807
-	 */
5808
-	public function commonGivenUnknownTable($params = array(1, 10, 'rcount')) {
5809
-		return $this->commonGivenQuery('U', 'table', false, $params);
5810
-	}
5811
-
5812
-	/**
5813
-	 * Create a chart of common given names.
5814
-	 *
5815
-	 * @param string[] $params
5816
-	 *
5817
-	 * @return string
5818
-	 */
5819
-	public function chartCommonGiven($params = array()) {
5820
-		$WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
5821
-		$WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
5822
-		$WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
5823
-		$WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
5824
-
5825
-		if (isset($params[0]) && $params[0] != '') {
5826
-			$size = strtolower($params[0]);
5827
-		} else {
5828
-			$size = $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y;
5829
-		}
5830
-		if (isset($params[1]) && $params[1] != '') {
5831
-			$color_from = strtolower($params[1]);
5832
-		} else {
5833
-			$color_from = $WT_STATS_CHART_COLOR1;
5834
-		}
5835
-		if (isset($params[2]) && $params[2] != '') {
5836
-			$color_to = strtolower($params[2]);
5837
-		} else {
5838
-			$color_to = $WT_STATS_CHART_COLOR2;
5839
-		}
5840
-		if (isset($params[4]) && $params[4] != '') {
5841
-			$maxtoshow = strtolower($params[4]);
5842
-		} else {
5843
-			$maxtoshow = 7;
5844
-		}
5845
-		$sizes    = explode('x', $size);
5846
-		$tot_indi = $this->totalIndividualsQuery();
5847
-		$given    = $this->commonGivenQuery('B', 'chart');
5848
-		if (!is_array($given)) {
5849
-			return '';
5850
-		}
5851
-		$given = array_slice($given, 0, $maxtoshow);
5852
-		if (count($given) <= 0) {
5853
-			return '';
5854
-		}
5855
-		$tot = 0;
5856
-		foreach ($given as $count) {
5857
-			$tot += $count;
5858
-		}
5859
-		$chd = '';
5860
-		$chl = array();
5861
-		foreach ($given as $givn => $count) {
5862
-			if ($tot == 0) {
5863
-				$per = 0;
5864
-			} else {
5865
-				$per = round(100 * $count / $tot_indi, 0);
5866
-			}
5867
-			$chd .= $this->arrayToExtendedEncoding(array($per));
5868
-			$chl[] = $givn . ' - ' . I18N::number($count);
5869
-		}
5870
-		$per = round(100 * ($tot_indi - $tot) / $tot_indi, 0);
5871
-		$chd .= $this->arrayToExtendedEncoding(array($per));
5872
-		$chl[] = I18N::translate('Other') . ' - ' . I18N::number($tot_indi - $tot);
5873
-
5874
-		$chart_title = implode(I18N::$list_separator, $chl);
5875
-		$chl         = implode('|', $chl);
5876
-
5877
-		return "<img src=\"https://chart.googleapis.com/chart?cht=p3&amp;chd=e:{$chd}&amp;chs={$size}&amp;chco={$color_from},{$color_to}&amp;chf=bg,s,ffffff00&amp;chl=" . rawurlencode($chl) . "\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . $chart_title . "\" title=\"" . $chart_title . "\" />";
5878
-	}
5879
-
5880
-	/**
5881
-	 * Who is currently logged in?
5882
-	 *
5883
-	 * @param string $type
5884
-	 *
5885
-	 * @return string
5886
-	 */
5887
-	private function usersLoggedInQuery($type = 'nolist') {
5888
-		$content = '';
5889
-		// List active users
5890
-		$NumAnonymous = 0;
5891
-		$loggedusers  = array();
5892
-		foreach (User::allLoggedIn() as $user) {
5893
-			if (Auth::isAdmin() || $user->getPreference('visibleonline')) {
5894
-				$loggedusers[] = $user;
5895
-			} else {
5896
-				$NumAnonymous++;
5897
-			}
5898
-		}
5899
-		$LoginUsers = count($loggedusers);
5900
-		if ($LoginUsers == 0 && $NumAnonymous == 0) {
5901
-			return I18N::translate('No signed-in and no anonymous users');
5902
-		}
5903
-		if ($NumAnonymous > 0) {
5904
-			$content .= '<b>' . I18N::plural('%s anonymous signed-in user', '%s anonymous signed-in users', $NumAnonymous, I18N::number($NumAnonymous)) . '</b>';
5905
-		}
5906
-		if ($LoginUsers > 0) {
5907
-			if ($NumAnonymous) {
5908
-				if ($type == 'list') {
5909
-					$content .= "<br><br>";
5910
-				} else {
5911
-					$content .= " " . I18N::translate('and') . " ";
5912
-				}
5913
-			}
5914
-			$content .= '<b>' . I18N::plural('%s signed-in user', '%s signed-in users', $LoginUsers, I18N::number($LoginUsers)) . '</b>';
5915
-			if ($type == 'list') {
5916
-				$content .= '<ul>';
5917
-			} else {
5918
-				$content .= ': ';
5919
-			}
5920
-		}
5921
-		if (Auth::check()) {
5922
-			foreach ($loggedusers as $user) {
5923
-				if ($type == 'list') {
5924
-					$content .= '<li>' . Filter::escapeHtml($user->getRealName()) . ' - ' . Filter::escapeHtml($user->getUserName());
5925
-				} else {
5926
-					$content .= Filter::escapeHtml($user->getRealName()) . ' - ' . Filter::escapeHtml($user->getUserName());
5927
-				}
5928
-				if (Auth::id() != $user->getUserId() && $user->getPreference('contactmethod') != 'none') {
5929
-					if ($type == 'list') {
5930
-						$content .= '<br><a class="icon-email" href="#" onclick="return message(\'' . $user->getUserId() . '\', \'\', \'' . Filter::escapeJs(Functions::getQueryUrl()) . '\');" title="' . I18N::translate('Send a message') . '"></a>';
5931
-					} else {
5932
-						$content .= ' <a class="icon-email" href="#" onclick="return message(\'' . $user->getUserId() . '\', \'\', \'' . Filter::escapeJs(Functions::getQueryUrl()) . '\');" title="' . I18N::translate('Send a message') . '"></a>';
5933
-					}
5934
-				}
5935
-				if ($type == 'list') {
5936
-					$content .= '</li>';
5937
-				}
5938
-			}
5939
-		}
5940
-		if ($type == 'list') {
5941
-			$content .= '</ul>';
5942
-		}
5943
-
5944
-		return $content;
5945
-	}
5946
-
5947
-	/**
5948
-	 * NUmber of users who are currently logged in?
5949
-	 *
5950
-	 * @param string $type
5951
-	 *
5952
-	 * @return int
5953
-	 */
5954
-	private function usersLoggedInTotalQuery($type = 'all') {
5955
-		$anon    = 0;
5956
-		$visible = 0;
5957
-		foreach (User::allLoggedIn() as $user) {
5958
-			if (Auth::isAdmin() || $user->getPreference('visibleonline')) {
5959
-				$visible++;
5960
-			} else {
5961
-				$anon++;
5962
-			}
5963
-		}
5964
-		if ($type == 'anon') {
5965
-			return $anon;
5966
-		} elseif ($type == 'visible') {
5967
-			return $visible;
5968
-		} else {
5969
-			return $visible + $anon;
5970
-		}
5971
-	}
5972
-
5973
-	/**
5974
-	 * Who is currently logged in?
5975
-	 *
5976
-	 * @return string
5977
-	 */
5978
-	public function usersLoggedIn() {
5979
-		return $this->usersLoggedInQuery('nolist');
5980
-	}
5981
-
5982
-	/**
5983
-	 * Who is currently logged in?
5984
-	 *
5985
-	 * @return string
5986
-	 */
5987
-	public function usersLoggedInList() {
5988
-		return $this->usersLoggedInQuery('list');
5989
-	}
5990
-
5991
-	/**
5992
-	 * Who is currently logged in?
5993
-	 *
5994
-	 * @return int
5995
-	 */
5996
-	public function usersLoggedInTotal() {
5997
-		return $this->usersLoggedInTotalQuery('all');
5998
-	}
5999
-
6000
-	/**
6001
-	 * Which visitors are currently logged in?
6002
-	 *
6003
-	 * @return int
6004
-	 */
6005
-	public function usersLoggedInTotalAnon() {
6006
-		return $this->usersLoggedInTotalQuery('anon');
6007
-	}
6008
-
6009
-	/**
6010
-	 * Which visitors are currently logged in?
6011
-	 *
6012
-	 * @return int
6013
-	 */
6014
-	public function usersLoggedInTotalVisible() {
6015
-		return $this->usersLoggedInTotalQuery('visible');
6016
-	}
6017
-
6018
-	/**
6019
-	 * Get the current user's ID.
6020
-	 *
6021
-	 * @return null|string
6022
-	 */
6023
-	public function userId() {
6024
-		return Auth::id();
6025
-	}
6026
-
6027
-	/**
6028
-	 * Get the current user's username.
6029
-	 *
6030
-	 * @param string[] $params
6031
-	 *
6032
-	 * @return string
6033
-	 */
6034
-	public function userName($params = array()) {
6035
-		if (Auth::check()) {
6036
-			return Filter::escapeHtml(Auth::user()->getUserName());
6037
-		} elseif (isset($params[0]) && $params[0] != '') {
6038
-			// if #username:visitor# was specified, then "visitor" will be returned when the user is not logged in
6039
-			return Filter::escapeHtml($params[0]);
6040
-		} else {
6041
-			return '';
6042
-		}
6043
-	}
6044
-
6045
-	/**
6046
-	 * Get the current user's full name.
6047
-	 *
6048
-	 * @return string
6049
-	 */
6050
-	public function userFullName() {
6051
-		return Auth::check() ? Auth::user()->getRealNameHtml() : '';
6052
-	}
6053
-
6054
-	/**
6055
-	 * Get the newest registered user.
6056
-	 *
6057
-	 * @param string   $type
6058
-	 * @param string[] $params
6059
-	 *
6060
-	 * @return string
6061
-	 */
6062
-	private function getLatestUserData($type = 'userid', $params = array()) {
6063
-		static $user_id = null;
6064
-
6065
-		if ($user_id === null) {
6066
-			$user = User::findLatestToRegister();
6067
-		} else {
6068
-			$user = User::find($user_id);
6069
-		}
6070
-
6071
-		switch ($type) {
6072
-		default:
6073
-		case 'userid':
6074
-			return $user->getUserId();
6075
-		case 'username':
6076
-			return Filter::escapeHtml($user->getUserName());
6077
-		case 'fullname':
6078
-			return $user->getRealNameHtml();
6079
-		case 'regdate':
6080
-			if (is_array($params) && isset($params[0]) && $params[0] != '') {
6081
-				$datestamp = $params[0];
6082
-			} else {
6083
-				$datestamp = I18N::dateFormat();
6084
-			}
6085
-
6086
-			return FunctionsDate::timestampToGedcomDate($user->getPreference('reg_timestamp'))->display(false, $datestamp);
6087
-		case 'regtime':
6088
-			if (is_array($params) && isset($params[0]) && $params[0] != '') {
6089
-				$datestamp = $params[0];
6090
-			} else {
6091
-				$datestamp = str_replace('%', '', I18N::timeFormat());
6092
-			}
6093
-
6094
-			return date($datestamp, $user->getPreference('reg_timestamp'));
6095
-		case 'loggedin':
6096
-			if (is_array($params) && isset($params[0]) && $params[0] != '') {
6097
-				$yes = $params[0];
6098
-			} else {
6099
-				$yes = I18N::translate('yes');
6100
-			}
6101
-			if (is_array($params) && isset($params[1]) && $params[1] != '') {
6102
-				$no = $params[1];
6103
-			} else {
6104
-				$no = I18N::translate('no');
6105
-			}
6106
-
6107
-			return Database::prepare("SELECT 1 FROM `##session` WHERE user_id=? LIMIT 1")->execute(array($user->getUserId()))->fetchOne() ? $yes : $no;
6108
-		}
6109
-	}
6110
-
6111
-	/**
6112
-	 * Get the newest registered user's ID.
6113
-	 *
6114
-	 * @return string
6115
-	 */
6116
-	public function latestUserId() {
6117
-		return $this->getLatestUserData('userid');
6118
-	}
6119
-
6120
-	/**
6121
-	 * Get the newest registered user's username.
6122
-	 *
6123
-	 * @return string
6124
-	 */
6125
-	public function latestUserName() {
6126
-		return $this->getLatestUserData('username');
6127
-	}
6128
-
6129
-	/**
6130
-	 * Get the newest registered user's real name.
6131
-	 *
6132
-	 * @return string
6133
-	 */
6134
-	public function latestUserFullName() {
6135
-		return $this->getLatestUserData('fullname');
6136
-	}
6137
-
6138
-	/**
6139
-	 * Get the date of the newest user registration.
6140
-	 *
6141
-	 * @param string[] $params
6142
-	 *
6143
-	 * @return string
6144
-	 */
6145
-	public function latestUserRegDate($params = array()) {
6146
-		return $this->getLatestUserData('regdate', $params);
6147
-	}
6148
-
6149
-	/**
6150
-	 * Find the timestamp of the latest user to register.
6151
-	 *
6152
-	 * @param string[] $params
6153
-	 *
6154
-	 * @return string
6155
-	 */
6156
-	public function latestUserRegTime($params = array()) {
6157
-		return $this->getLatestUserData('regtime', $params);
6158
-	}
6159
-
6160
-	/**
6161
-	 * Find the most recent user to log in.
6162
-	 *
6163
-	 * @param string[] $params
6164
-	 *
6165
-	 * @return string
6166
-	 */
6167
-	public function latestUserLoggedin($params = array()) {
6168
-		return $this->getLatestUserData('loggedin', $params);
6169
-	}
6170
-
6171
-	/**
6172
-	 * Create a link to contact the webmaster.
6173
-	 *
6174
-	 * @return string
6175
-	 */
6176
-	public function contactWebmaster() {
6177
-		$user_id = $this->tree->getPreference('WEBMASTER_USER_ID');
6178
-		$user    = User::find($user_id);
6179
-		if ($user) {
6180
-			return Theme::theme()->contactLink($user);
6181
-		} else {
6182
-			return $user_id;
6183
-		}
6184
-	}
6185
-
6186
-	/**
6187
-	 * Create a link to contact the genealogy contact.
6188
-	 *
6189
-	 * @return string
6190
-	 */
6191
-	public function contactGedcom() {
6192
-		$user_id = $this->tree->getPreference('CONTACT_USER_ID');
6193
-		$user    = User::find($user_id);
6194
-		if ($user) {
6195
-			return Theme::theme()->contactLink($user);
6196
-		} else {
6197
-			return $user_id;
6198
-		}
6199
-	}
6200
-
6201
-	/**
6202
-	 * What is the current date on the server?
6203
-	 *
6204
-	 * @return string
6205
-	 */
6206
-	public function serverDate() {
6207
-		return FunctionsDate::timestampToGedcomDate(WT_TIMESTAMP)->display();
6208
-	}
6209
-
6210
-	/**
6211
-	 * What is the current time on the server (in 12 hour clock)?
6212
-	 *
6213
-	 * @return string
6214
-	 */
6215
-	public function serverTime() {
6216
-		return date('g:i a');
6217
-	}
6218
-
6219
-	/**
6220
-	 * What is the current time on the server (in 24 hour clock)?
6221
-	 *
6222
-	 * @return string
6223
-	 */
6224
-	public function serverTime24() {
6225
-		return date('G:i');
6226
-	}
6227
-
6228
-	/**
6229
-	 * What is the timezone of the server.
6230
-	 *
6231
-	 * @return string
6232
-	 */
6233
-	public function serverTimezone() {
6234
-		return date('T');
6235
-	}
6236
-
6237
-	/**
6238
-	 * What is the client's date.
6239
-	 *
6240
-	 * @return string
6241
-	 */
6242
-	public function browserDate() {
6243
-		return FunctionsDate::timestampToGedcomDate(WT_TIMESTAMP + WT_TIMESTAMP_OFFSET)->display();
6244
-	}
6245
-
6246
-	/**
6247
-	 * What is the client's timestamp.
6248
-	 *
6249
-	 * @return string
6250
-	 */
6251
-	public function browserTime() {
6252
-		return date(str_replace('%', '', I18N::timeFormat()), WT_TIMESTAMP + WT_TIMESTAMP_OFFSET);
6253
-	}
6254
-
6255
-	/**
6256
-	 * What is the browser's tiemzone.
6257
-	 *
6258
-	 * @return string
6259
-	 */
6260
-	public function browserTimezone() {
6261
-		return date('T', WT_TIMESTAMP + WT_TIMESTAMP_OFFSET);
6262
-	}
6263
-
6264
-	/**
6265
-	 * What is the current version of webtrees.
6266
-	 *
6267
-	 * @return string
6268
-	 */
6269
-	public function webtreesVersion() {
6270
-		return WT_VERSION;
6271
-	}
6272
-
6273
-	/**
6274
-	 * These functions provide access to hitcounter for use in the HTML block.
6275
-	 *
6276
-	 * @param string   $page_name
6277
-	 * @param string[] $params
6278
-	 *
6279
-	 * @return string
6280
-	 */
6281
-	private function hitCountQuery($page_name, $params) {
6282
-		if (is_array($params) && isset($params[0]) && $params[0] != '') {
6283
-			$page_parameter = $params[0];
6284
-		} else {
6285
-			$page_parameter = '';
6286
-		}
6287
-
6288
-		if ($page_name === null) {
6289
-			// index.php?ctype=gedcom
6290
-			$page_name      = 'index.php';
6291
-			$page_parameter = 'gedcom:' . ($page_parameter ? Tree::findByName($page_parameter)->getTreeId() : $this->tree->getTreeId());
6292
-		} elseif ($page_name == 'index.php') {
6293
-			// index.php?ctype=user
6294
-			$user           = User::findByIdentifier($page_parameter);
6295
-			$page_parameter = 'user:' . ($user ? $user->getUserId() : Auth::id());
6296
-		} else {
6297
-			// indi/fam/sour/etc.
6298
-		}
6299
-
6300
-		return '<span class="odometer">' . I18N::digits(HitCounter::getCount($this->tree, $page_name, $page_parameter)) . '</span>';
6301
-	}
6302
-
6303
-	/**
6304
-	 * How many times has a page been viewed.
6305
-	 *
6306
-	 * @param string[] $params
6307
-	 *
6308
-	 * @return string
6309
-	 */
6310
-	public function hitCount($params = array()) {
6311
-		return $this->hitCountQuery(null, $params);
6312
-	}
6313
-
6314
-	/**
6315
-	 * How many times has a page been viewed.
6316
-	 *
6317
-	 * @param string[] $params
6318
-	 *
6319
-	 * @return string
6320
-	 */
6321
-	public function hitCountUser($params = array()) {
6322
-		return $this->hitCountQuery('index.php', $params);
6323
-	}
6324
-
6325
-	/**
6326
-	 * How many times has a page been viewed.
6327
-	 *
6328
-	 * @param string[] $params
6329
-	 *
6330
-	 * @return string
6331
-	 */
6332
-	public function hitCountIndi($params = array()) {
6333
-		return $this->hitCountQuery('individual.php', $params);
6334
-	}
6335
-
6336
-	/**
6337
-	 * How many times has a page been viewed.
6338
-	 *
6339
-	 * @param string[] $params
6340
-	 *
6341
-	 * @return string
6342
-	 */
6343
-	public function hitCountFam($params = array()) {
6344
-		return $this->hitCountQuery('family.php', $params);
6345
-	}
6346
-
6347
-	/**
6348
-	 * How many times has a page been viewed.
6349
-	 *
6350
-	 * @param string[] $params
6351
-	 *
6352
-	 * @return string
6353
-	 */
6354
-	public function hitCountSour($params = array()) {
6355
-		return $this->hitCountQuery('source.php', $params);
6356
-	}
6357
-
6358
-	/**
6359
-	 * How many times has a page been viewed.
6360
-	 *
6361
-	 * @param string[] $params
6362
-	 *
6363
-	 * @return string
6364
-	 */
6365
-	public function hitCountRepo($params = array()) {
6366
-		return $this->hitCountQuery('repo.php', $params);
6367
-	}
6368
-
6369
-	/**
6370
-	 * How many times has a page been viewed.
6371
-	 *
6372
-	 * @param string[] $params
6373
-	 *
6374
-	 * @return string
6375
-	 */
6376
-	public function hitCountNote($params = array()) {
6377
-		return $this->hitCountQuery('note.php', $params);
6378
-	}
6379
-
6380
-	/**
6381
-	 * How many times has a page been viewed.
6382
-	 *
6383
-	 * @param string[] $params
6384
-	 *
6385
-	 * @return string
6386
-	 */
6387
-	public function hitCountObje($params = array()) {
6388
-		return $this->hitCountQuery('mediaviewer.php', $params);
6389
-	}
6390
-
6391
-	/**
6392
-	 * Convert numbers to Google's custom encoding.
6393
-	 *
6394
-	 * @link http://bendodson.com/news/google-extended-encoding-made-easy
6395
-	 *
6396
-	 * @param int[] $a
6397
-	 *
6398
-	 * @return string
6399
-	 */
6400
-	private function arrayToExtendedEncoding($a) {
6401
-		$xencoding = WT_GOOGLE_CHART_ENCODING;
6402
-
6403
-		$encoding = '';
6404
-		foreach ($a as $value) {
6405
-			if ($value < 0) {
6406
-				$value = 0;
6407
-			}
6408
-			$first  = (int) ($value / 64);
6409
-			$second = $value % 64;
6410
-			$encoding .= $xencoding[(int) $first] . $xencoding[(int) $second];
6411
-		}
6412
-
6413
-		return $encoding;
6414
-	}
6415
-
6416
-	/**
6417
-	 * Callback function to compare totals.
6418
-	 *
6419
-	 * @param array $a
6420
-	 * @param array $b
6421
-	 *
6422
-	 * @return int
6423
-	 */
6424
-	private function nameTotalSort($a, $b) {
6425
-		return $a['match'] - $b['match'];
6426
-	}
6427
-
6428
-	/**
6429
-	 * Callback function to compare totals.
6430
-	 *
6431
-	 * @param array $a
6432
-	 * @param array $b
6433
-	 *
6434
-	 * @return int
6435
-	 */
6436
-	private function nameTotalReverseSort($a, $b) {
6437
-		return $b['match'] - $a['match'];
6438
-	}
6439
-
6440
-	/**
6441
-	 * Run an SQL query and cache the result.
6442
-	 *
6443
-	 * @param string $sql
6444
-	 *
6445
-	 * @return string[][]
6446
-	 */
6447
-	private function runSql($sql) {
6448
-		static $cache = array();
6449
-
6450
-		$id = md5($sql);
6451
-		if (isset($cache[$id])) {
6452
-			return $cache[$id];
6453
-		}
6454
-		$rows       = Database::prepare($sql)->fetchAll(PDO::FETCH_ASSOC);
6455
-		$cache[$id] = $rows;
6456
-
6457
-		return $rows;
6458
-	}
6459
-
6460
-	/**
6461
-	 * Find the favorites for the tree.
6462
-	 *
6463
-	 * @return string
6464
-	 */
6465
-	public function gedcomFavorites() {
6466
-		if (Module::getModuleByName('gedcom_favorites')) {
6467
-			$block = new FamilyTreeFavoritesModule(WT_MODULES_DIR . 'gedcom_favorites');
6468
-
6469
-			return $block->getBlock(0, false);
6470
-		} else {
6471
-			return '';
6472
-		}
6473
-	}
6474
-
6475
-	/**
6476
-	 * Find the favorites for the user.
6477
-	 *
6478
-	 * @return string
6479
-	 */
6480
-	public function userFavorites() {
6481
-		if (Auth::check() && Module::getModuleByName('user_favorites')) {
6482
-			$block = new UserFavoritesModule(WT_MODULES_DIR . 'gedcom_favorites');
6483
-
6484
-			return $block->getBlock(0, false);
6485
-		} else {
6486
-			return '';
6487
-		}
6488
-	}
6489
-
6490
-	/**
6491
-	 * Find the number of favorites for the tree.
6492
-	 *
6493
-	 * @return int
6494
-	 */
6495
-	public function totalGedcomFavorites() {
6496
-		if (Module::getModuleByName('gedcom_favorites')) {
6497
-			return count(FamilyTreeFavoritesModule::getFavorites($this->tree->getTreeId()));
6498
-		} else {
6499
-			return 0;
6500
-		}
6501
-	}
6502
-
6503
-	/**
6504
-	 * Find the number of favorites for the user.
6505
-	 *
6506
-	 * @return int
6507
-	 */
6508
-	public function totalUserFavorites() {
6509
-		if (Module::getModuleByName('user_favorites')) {
6510
-			return count(UserFavoritesModule::getFavorites(Auth::id()));
6511
-		} else {
6512
-			return 0;
6513
-		}
6514
-	}
6515
-
6516
-	/**
6517
-	 * Create any of the other blocks.
6518
-	 *
6519
-	 * Use as #callBlock:block_name#
6520
-	 *
6521
-	 * @param string[] $params
6522
-	 *
6523
-	 * @return string
6524
-	 */
6525
-	public function callBlock($params = array()) {
6526
-		global $ctype;
6527
-
6528
-		if (isset($params[0]) && $params[0] != '') {
6529
-			$block = $params[0];
6530
-		} else {
6531
-			return '';
6532
-		}
6533
-		$all_blocks = array();
6534
-		foreach (Module::getActiveBlocks($this->tree) as $name => $active_block) {
6535
-			if ($ctype == 'user' && $active_block->isUserBlock() || $ctype == 'gedcom' && $active_block->isGedcomBlock()) {
6536
-				$all_blocks[$name] = $active_block;
6537
-			}
6538
-		}
6539
-		if (!array_key_exists($block, $all_blocks) || $block == 'html') {
6540
-			return '';
6541
-		}
6542
-		// Build the config array
6543
-		array_shift($params);
6544
-		$cfg = array();
6545
-		foreach ($params as $config) {
6546
-			$bits = explode('=', $config);
6547
-			if (count($bits) < 2) {
6548
-				continue;
6549
-			}
6550
-			$v       = array_shift($bits);
6551
-			$cfg[$v] = implode('=', $bits);
6552
-		}
6553
-		$block    = $all_blocks[$block];
6554
-		$block_id = Filter::getInteger('block_id');
6555
-		$content  = $block->getBlock($block_id, false, $cfg);
6556
-
6557
-		return $content;
6558
-	}
6559
-
6560
-	/**
6561
-	 * How many messages in the user's inbox.
6562
-	 *
6563
-	 * @return string
6564
-	 */
6565
-	public function totalUserMessages() {
6566
-		$total = (int) Database::prepare("SELECT COUNT(*) FROM `##message` WHERE user_id = ?")
6567
-			->execute(array(Auth::id()))
6568
-			->fetchOne();
6569
-
6570
-		return I18N::number($total);
6571
-	}
6572
-
6573
-	/**
6574
-	 * How many blog entries exist for this user.
6575
-	 *
6576
-	 * @return string
6577
-	 */
6578
-	public function totalUserJournal() {
6579
-		try {
6580
-			$number = (int) Database::prepare("SELECT COUNT(*) FROM `##news` WHERE user_id = ?")
6581
-				->execute(array(Auth::id()))
6582
-				->fetchOne();
6583
-		} catch (PDOException $ex) {
6584
-			// The module may not be installed, so the table may not exist.
6585
-			$number = 0;
6586
-		}
6587
-
6588
-		return I18N::number($number);
6589
-	}
6590
-
6591
-	/**
6592
-	 * How many news items exist for this tree.
6593
-	 *
6594
-	 * @return string
6595
-	 */
6596
-	public function totalGedcomNews() {
6597
-		try {
6598
-			$number = (int) Database::prepare("SELECT COUNT(*) FROM `##news` WHERE gedcom_id = ?")
6599
-				->execute(array($this->tree->getTreeId()))
6600
-				->fetchOne();
6601
-		} catch (PDOException $ex) {
6602
-			// The module may not be installed, so the table may not exist.
6603
-			$number = 0;
6604
-		}
6605
-
6606
-		return I18N::number($number);
6607
-	}
6608
-
6609
-	/**
6610
-	 * ISO3166 3 letter codes, with their 2 letter equivalent.
6611
-	 * NOTE: this is not 1:1. ENG/SCO/WAL/NIR => GB
6612
-	 * NOTE: this also includes champman codes and others. Should it?
6613
-	 *
6614
-	 * @return string[]
6615
-	 */
6616
-	public function iso3166() {
6617
-		return array(
6618
-			'ABW' => 'AW', 'AFG' => 'AF', 'AGO' => 'AO', 'AIA' => 'AI', 'ALA' => 'AX', 'ALB' => 'AL',
6619
-			'AND' => 'AD', 'ARE' => 'AE', 'ARG' => 'AR', 'ARM' => 'AM', 'ASM' => 'AS',
6620
-			'ATA' => 'AQ', 'ATF' => 'TF', 'ATG' => 'AG', 'AUS' => 'AU', 'AUT' => 'AT', 'AZE' => 'AZ',
6621
-			'BDI' => 'BI', 'BEL' => 'BE', 'BEN' => 'BJ', 'BFA' => 'BF', 'BGD' => 'BD', 'BGR' => 'BG',
6622
-			'BHR' => 'BH', 'BHS' => 'BS', 'BIH' => 'BA', 'BLR' => 'BY', 'BLZ' => 'BZ', 'BMU' => 'BM',
6623
-			'BOL' => 'BO', 'BRA' => 'BR', 'BRB' => 'BB', 'BRN' => 'BN', 'BTN' => 'BT', 'BVT' => 'BV',
6624
-			'BWA' => 'BW', 'CAF' => 'CF', 'CAN' => 'CA', 'CCK' => 'CC', 'CHE' => 'CH', 'CHL' => 'CL',
6625
-			'CHN' => 'CN', 'CIV' => 'CI', 'CMR' => 'CM', 'COD' => 'CD', 'COG' => 'CG',
6626
-			'COK' => 'CK', 'COL' => 'CO', 'COM' => 'KM', 'CPV' => 'CV', 'CRI' => 'CR', 'CUB' => 'CU',
6627
-			'CXR' => 'CX', 'CYM' => 'KY', 'CYP' => 'CY', 'CZE' => 'CZ', 'DEU' => 'DE', 'DJI' => 'DJ',
6628
-			'DMA' => 'DM', 'DNK' => 'DK', 'DOM' => 'DO', 'DZA' => 'DZ', 'ECU' => 'EC', 'EGY' => 'EG',
6629
-			'ENG' => 'GB', 'ERI' => 'ER', 'ESH' => 'EH', 'ESP' => 'ES', 'EST' => 'EE', 'ETH' => 'ET',
6630
-			'FIN' => 'FI', 'FJI' => 'FJ', 'FLK' => 'FK', 'FRA' => 'FR', 'FRO' => 'FO', 'FSM' => 'FM',
6631
-			'GAB' => 'GA', 'GBR' => 'GB', 'GEO' => 'GE', 'GHA' => 'GH', 'GIB' => 'GI', 'GIN' => 'GN',
6632
-			'GLP' => 'GP', 'GMB' => 'GM', 'GNB' => 'GW', 'GNQ' => 'GQ', 'GRC' => 'GR', 'GRD' => 'GD',
6633
-			'GRL' => 'GL', 'GTM' => 'GT', 'GUF' => 'GF', 'GUM' => 'GU', 'GUY' => 'GY', 'HKG' => 'HK',
6634
-			'HMD' => 'HM', 'HND' => 'HN', 'HRV' => 'HR', 'HTI' => 'HT', 'HUN' => 'HU', 'IDN' => 'ID',
6635
-			'IND' => 'IN', 'IOT' => 'IO', 'IRL' => 'IE', 'IRN' => 'IR', 'IRQ' => 'IQ', 'ISL' => 'IS',
6636
-			'ISR' => 'IL', 'ITA' => 'IT', 'JAM' => 'JM', 'JOR' => 'JO', 'JPN' => 'JA', 'KAZ' => 'KZ',
6637
-			'KEN' => 'KE', 'KGZ' => 'KG', 'KHM' => 'KH', 'KIR' => 'KI', 'KNA' => 'KN', 'KOR' => 'KO',
6638
-			'KWT' => 'KW', 'LAO' => 'LA', 'LBN' => 'LB', 'LBR' => 'LR', 'LBY' => 'LY', 'LCA' => 'LC',
6639
-			'LIE' => 'LI', 'LKA' => 'LK', 'LSO' => 'LS', 'LTU' => 'LT', 'LUX' => 'LU', 'LVA' => 'LV',
6640
-			'MAC' => 'MO', 'MAR' => 'MA', 'MCO' => 'MC', 'MDA' => 'MD', 'MDG' => 'MG', 'MDV' => 'MV',
6641
-			'MEX' => 'MX', 'MHL' => 'MH', 'MKD' => 'MK', 'MLI' => 'ML', 'MLT' => 'MT', 'MMR' => 'MM',
6642
-			'MNG' => 'MN', 'MNP' => 'MP', 'MNT' => 'ME', 'MOZ' => 'MZ', 'MRT' => 'MR', 'MSR' => 'MS',
6643
-			'MTQ' => 'MQ', 'MUS' => 'MU', 'MWI' => 'MW', 'MYS' => 'MY', 'MYT' => 'YT', 'NAM' => 'NA',
6644
-			'NCL' => 'NC', 'NER' => 'NE', 'NFK' => 'NF', 'NGA' => 'NG', 'NIC' => 'NI', 'NIR' => 'GB',
6645
-			'NIU' => 'NU', 'NLD' => 'NL', 'NOR' => 'NO', 'NPL' => 'NP', 'NRU' => 'NR', 'NZL' => 'NZ',
6646
-			'OMN' => 'OM', 'PAK' => 'PK', 'PAN' => 'PA', 'PCN' => 'PN', 'PER' => 'PE', 'PHL' => 'PH',
6647
-			'PLW' => 'PW', 'PNG' => 'PG', 'POL' => 'PL', 'PRI' => 'PR', 'PRK' => 'KP', 'PRT' => 'PO',
6648
-			'PRY' => 'PY', 'PSE' => 'PS', 'PYF' => 'PF', 'QAT' => 'QA', 'REU' => 'RE', 'ROM' => 'RO',
6649
-			'RUS' => 'RU', 'RWA' => 'RW', 'SAU' => 'SA', 'SCT' => 'GB', 'SDN' => 'SD', 'SEN' => 'SN',
6650
-			'SER' => 'RS', 'SGP' => 'SG', 'SGS' => 'GS', 'SHN' => 'SH', 'SJM' => 'SJ',
6651
-			'SLB' => 'SB', 'SLE' => 'SL', 'SLV' => 'SV', 'SMR' => 'SM', 'SOM' => 'SO', 'SPM' => 'PM',
6652
-			'STP' => 'ST', 'SUR' => 'SR', 'SVK' => 'SK', 'SVN' => 'SI', 'SWE' => 'SE',
6653
-			'SWZ' => 'SZ', 'SYC' => 'SC', 'SYR' => 'SY', 'TCA' => 'TC', 'TCD' => 'TD', 'TGO' => 'TG',
6654
-			'THA' => 'TH', 'TJK' => 'TJ', 'TKL' => 'TK', 'TKM' => 'TM', 'TLS' => 'TL', 'TON' => 'TO',
6655
-			'TTO' => 'TT', 'TUN' => 'TN', 'TUR' => 'TR', 'TUV' => 'TV', 'TWN' => 'TW', 'TZA' => 'TZ',
6656
-			'UGA' => 'UG', 'UKR' => 'UA', 'UMI' => 'UM', 'URY' => 'UY', 'USA' => 'US', 'UZB' => 'UZ',
6657
-			'VAT' => 'VA', 'VCT' => 'VC', 'VEN' => 'VE', 'VGB' => 'VG', 'VIR' => 'VI', 'VNM' => 'VN',
6658
-			'VUT' => 'VU', 'WLF' => 'WF', 'WLS' => 'GB', 'WSM' => 'WS', 'YEM' => 'YE', 'ZAF' => 'ZA',
6659
-			'ZMB' => 'ZM', 'ZWE' => 'ZW',
6660
-		);
6661
-	}
6662
-
6663
-	/**
6664
-	 * Country codes and names
6665
-	 *
6666
-	 * @return string[]
6667
-	 */
6668
-	public function getAllCountries() {
6669
-		return array(
6670
-			'???' => /* I18N: Name of a country or state */ I18N::translate('Unknown'),
6671
-			'ABW' => /* I18N: Name of a country or state */ I18N::translate('Aruba'),
6672
-			'AFG' => /* I18N: Name of a country or state */ I18N::translate('Afghanistan'),
6673
-			'AGO' => /* I18N: Name of a country or state */ I18N::translate('Angola'),
6674
-			'AIA' => /* I18N: Name of a country or state */ I18N::translate('Anguilla'),
6675
-			'ALA' => /* I18N: Name of a country or state */ I18N::translate('Aland Islands'),
6676
-			'ALB' => /* I18N: Name of a country or state */ I18N::translate('Albania'),
6677
-			'AND' => /* I18N: Name of a country or state */ I18N::translate('Andorra'),
6678
-			'ARE' => /* I18N: Name of a country or state */ I18N::translate('United Arab Emirates'),
6679
-			'ARG' => /* I18N: Name of a country or state */ I18N::translate('Argentina'),
6680
-			'ARM' => /* I18N: Name of a country or state */ I18N::translate('Armenia'),
6681
-			'ASM' => /* I18N: Name of a country or state */ I18N::translate('American Samoa'),
6682
-			'ATA' => /* I18N: Name of a country or state */ I18N::translate('Antarctica'),
6683
-			'ATF' => /* I18N: Name of a country or state */ I18N::translate('French Southern Territories'),
6684
-			'ATG' => /* I18N: Name of a country or state */ I18N::translate('Antigua and Barbuda'),
6685
-			'AUS' => /* I18N: Name of a country or state */ I18N::translate('Australia'),
6686
-			'AUT' => /* I18N: Name of a country or state */ I18N::translate('Austria'),
6687
-			'AZE' => /* I18N: Name of a country or state */ I18N::translate('Azerbaijan'),
6688
-			'AZR' => /* I18N: Name of a country or state */ I18N::translate('Azores'),
6689
-			'BDI' => /* I18N: Name of a country or state */ I18N::translate('Burundi'),
6690
-			'BEL' => /* I18N: Name of a country or state */ I18N::translate('Belgium'),
6691
-			'BEN' => /* I18N: Name of a country or state */ I18N::translate('Benin'),
6692
-			// BES => Bonaire, Sint Eustatius and Saba
6693
-			'BFA' => /* I18N: Name of a country or state */ I18N::translate('Burkina Faso'),
6694
-			'BGD' => /* I18N: Name of a country or state */ I18N::translate('Bangladesh'),
6695
-			'BGR' => /* I18N: Name of a country or state */ I18N::translate('Bulgaria'),
6696
-			'BHR' => /* I18N: Name of a country or state */ I18N::translate('Bahrain'),
6697
-			'BHS' => /* I18N: Name of a country or state */ I18N::translate('Bahamas'),
6698
-			'BIH' => /* I18N: Name of a country or state */ I18N::translate('Bosnia and Herzegovina'),
6699
-			// BLM => Saint Barthélemy
6700
-			'BLR' => /* I18N: Name of a country or state */ I18N::translate('Belarus'),
6701
-			'BLZ' => /* I18N: Name of a country or state */ I18N::translate('Belize'),
6702
-			'BMU' => /* I18N: Name of a country or state */ I18N::translate('Bermuda'),
6703
-			'BOL' => /* I18N: Name of a country or state */ I18N::translate('Bolivia'),
6704
-			'BRA' => /* I18N: Name of a country or state */ I18N::translate('Brazil'),
6705
-			'BRB' => /* I18N: Name of a country or state */ I18N::translate('Barbados'),
6706
-			'BRN' => /* I18N: Name of a country or state */ I18N::translate('Brunei Darussalam'),
6707
-			'BTN' => /* I18N: Name of a country or state */ I18N::translate('Bhutan'),
6708
-			'BVT' => /* I18N: Name of a country or state */ I18N::translate('Bouvet Island'),
6709
-			'BWA' => /* I18N: Name of a country or state */ I18N::translate('Botswana'),
6710
-			'CAF' => /* I18N: Name of a country or state */ I18N::translate('Central African Republic'),
6711
-			'CAN' => /* I18N: Name of a country or state */ I18N::translate('Canada'),
6712
-			'CCK' => /* I18N: Name of a country or state */ I18N::translate('Cocos (Keeling) Islands'),
6713
-			'CHE' => /* I18N: Name of a country or state */ I18N::translate('Switzerland'),
6714
-			'CHL' => /* I18N: Name of a country or state */ I18N::translate('Chile'),
6715
-			'CHN' => /* I18N: Name of a country or state */ I18N::translate('China'),
6716
-			'CIV' => /* I18N: Name of a country or state */ I18N::translate('Cote d’Ivoire'),
6717
-			'CMR' => /* I18N: Name of a country or state */ I18N::translate('Cameroon'),
6718
-			'COD' => /* I18N: Name of a country or state */ I18N::translate('Democratic Republic of the Congo'),
6719
-			'COG' => /* I18N: Name of a country or state */ I18N::translate('Republic of the Congo'),
6720
-			'COK' => /* I18N: Name of a country or state */ I18N::translate('Cook Islands'),
6721
-			'COL' => /* I18N: Name of a country or state */ I18N::translate('Colombia'),
6722
-			'COM' => /* I18N: Name of a country or state */ I18N::translate('Comoros'),
6723
-			'CPV' => /* I18N: Name of a country or state */ I18N::translate('Cape Verde'),
6724
-			'CRI' => /* I18N: Name of a country or state */ I18N::translate('Costa Rica'),
6725
-			'CUB' => /* I18N: Name of a country or state */ I18N::translate('Cuba'),
6726
-			// CUW => Curaçao
6727
-			'CXR' => /* I18N: Name of a country or state */ I18N::translate('Christmas Island'),
6728
-			'CYM' => /* I18N: Name of a country or state */ I18N::translate('Cayman Islands'),
6729
-			'CYP' => /* I18N: Name of a country or state */ I18N::translate('Cyprus'),
6730
-			'CZE' => /* I18N: Name of a country or state */ I18N::translate('Czech Republic'),
6731
-			'DEU' => /* I18N: Name of a country or state */ I18N::translate('Germany'),
6732
-			'DJI' => /* I18N: Name of a country or state */ I18N::translate('Djibouti'),
6733
-			'DMA' => /* I18N: Name of a country or state */ I18N::translate('Dominica'),
6734
-			'DNK' => /* I18N: Name of a country or state */ I18N::translate('Denmark'),
6735
-			'DOM' => /* I18N: Name of a country or state */ I18N::translate('Dominican Republic'),
6736
-			'DZA' => /* I18N: Name of a country or state */ I18N::translate('Algeria'),
6737
-			'ECU' => /* I18N: Name of a country or state */ I18N::translate('Ecuador'),
6738
-			'EGY' => /* I18N: Name of a country or state */ I18N::translate('Egypt'),
6739
-			'ENG' => /* I18N: Name of a country or state */ I18N::translate('England'),
6740
-			'ERI' => /* I18N: Name of a country or state */ I18N::translate('Eritrea'),
6741
-			'ESH' => /* I18N: Name of a country or state */ I18N::translate('Western Sahara'),
6742
-			'ESP' => /* I18N: Name of a country or state */ I18N::translate('Spain'),
6743
-			'EST' => /* I18N: Name of a country or state */ I18N::translate('Estonia'),
6744
-			'ETH' => /* I18N: Name of a country or state */ I18N::translate('Ethiopia'),
6745
-			'FIN' => /* I18N: Name of a country or state */ I18N::translate('Finland'),
6746
-			'FJI' => /* I18N: Name of a country or state */ I18N::translate('Fiji'),
6747
-			'FLD' => /* I18N: Name of a country or state */ I18N::translate('Flanders'),
6748
-			'FLK' => /* I18N: Name of a country or state */ I18N::translate('Falkland Islands'),
6749
-			'FRA' => /* I18N: Name of a country or state */ I18N::translate('France'),
6750
-			'FRO' => /* I18N: Name of a country or state */ I18N::translate('Faroe Islands'),
6751
-			'FSM' => /* I18N: Name of a country or state */ I18N::translate('Micronesia'),
6752
-			'GAB' => /* I18N: Name of a country or state */ I18N::translate('Gabon'),
6753
-			'GBR' => /* I18N: Name of a country or state */ I18N::translate('United Kingdom'),
6754
-			'GEO' => /* I18N: Name of a country or state */ I18N::translate('Georgia'),
6755
-			'GGY' => /* I18N: Name of a country or state */ I18N::translate('Guernsey'),
6756
-			'GHA' => /* I18N: Name of a country or state */ I18N::translate('Ghana'),
6757
-			'GIB' => /* I18N: Name of a country or state */ I18N::translate('Gibraltar'),
6758
-			'GIN' => /* I18N: Name of a country or state */ I18N::translate('Guinea'),
6759
-			'GLP' => /* I18N: Name of a country or state */ I18N::translate('Guadeloupe'),
6760
-			'GMB' => /* I18N: Name of a country or state */ I18N::translate('Gambia'),
6761
-			'GNB' => /* I18N: Name of a country or state */ I18N::translate('Guinea-Bissau'),
6762
-			'GNQ' => /* I18N: Name of a country or state */ I18N::translate('Equatorial Guinea'),
6763
-			'GRC' => /* I18N: Name of a country or state */ I18N::translate('Greece'),
6764
-			'GRD' => /* I18N: Name of a country or state */ I18N::translate('Grenada'),
6765
-			'GRL' => /* I18N: Name of a country or state */ I18N::translate('Greenland'),
6766
-			'GTM' => /* I18N: Name of a country or state */ I18N::translate('Guatemala'),
6767
-			'GUF' => /* I18N: Name of a country or state */ I18N::translate('French Guiana'),
6768
-			'GUM' => /* I18N: Name of a country or state */ I18N::translate('Guam'),
6769
-			'GUY' => /* I18N: Name of a country or state */ I18N::translate('Guyana'),
6770
-			'HKG' => /* I18N: Name of a country or state */ I18N::translate('Hong Kong'),
6771
-			'HMD' => /* I18N: Name of a country or state */ I18N::translate('Heard Island and McDonald Islands'),
6772
-			'HND' => /* I18N: Name of a country or state */ I18N::translate('Honduras'),
6773
-			'HRV' => /* I18N: Name of a country or state */ I18N::translate('Croatia'),
6774
-			'HTI' => /* I18N: Name of a country or state */ I18N::translate('Haiti'),
6775
-			'HUN' => /* I18N: Name of a country or state */ I18N::translate('Hungary'),
6776
-			'IDN' => /* I18N: Name of a country or state */ I18N::translate('Indonesia'),
6777
-			'IND' => /* I18N: Name of a country or state */ I18N::translate('India'),
6778
-			'IOM' => /* I18N: Name of a country or state */ I18N::translate('Isle of Man'),
6779
-			'IOT' => /* I18N: Name of a country or state */ I18N::translate('British Indian Ocean Territory'),
6780
-			'IRL' => /* I18N: Name of a country or state */ I18N::translate('Ireland'),
6781
-			'IRN' => /* I18N: Name of a country or state */ I18N::translate('Iran'),
6782
-			'IRQ' => /* I18N: Name of a country or state */ I18N::translate('Iraq'),
6783
-			'ISL' => /* I18N: Name of a country or state */ I18N::translate('Iceland'),
6784
-			'ISR' => /* I18N: Name of a country or state */ I18N::translate('Israel'),
6785
-			'ITA' => /* I18N: Name of a country or state */ I18N::translate('Italy'),
6786
-			'JAM' => /* I18N: Name of a country or state */ I18N::translate('Jamaica'),
6787
-			//'JEY' => Jersey
6788
-			'JOR' => /* I18N: Name of a country or state */ I18N::translate('Jordan'),
6789
-			'JPN' => /* I18N: Name of a country or state */ I18N::translate('Japan'),
6790
-			'KAZ' => /* I18N: Name of a country or state */ I18N::translate('Kazakhstan'),
6791
-			'KEN' => /* I18N: Name of a country or state */ I18N::translate('Kenya'),
6792
-			'KGZ' => /* I18N: Name of a country or state */ I18N::translate('Kyrgyzstan'),
6793
-			'KHM' => /* I18N: Name of a country or state */ I18N::translate('Cambodia'),
6794
-			'KIR' => /* I18N: Name of a country or state */ I18N::translate('Kiribati'),
6795
-			'KNA' => /* I18N: Name of a country or state */ I18N::translate('Saint Kitts and Nevis'),
6796
-			'KOR' => /* I18N: Name of a country or state */ I18N::translate('Korea'),
6797
-			'KWT' => /* I18N: Name of a country or state */ I18N::translate('Kuwait'),
6798
-			'LAO' => /* I18N: Name of a country or state */ I18N::translate('Laos'),
6799
-			'LBN' => /* I18N: Name of a country or state */ I18N::translate('Lebanon'),
6800
-			'LBR' => /* I18N: Name of a country or state */ I18N::translate('Liberia'),
6801
-			'LBY' => /* I18N: Name of a country or state */ I18N::translate('Libya'),
6802
-			'LCA' => /* I18N: Name of a country or state */ I18N::translate('Saint Lucia'),
6803
-			'LIE' => /* I18N: Name of a country or state */ I18N::translate('Liechtenstein'),
6804
-			'LKA' => /* I18N: Name of a country or state */ I18N::translate('Sri Lanka'),
6805
-			'LSO' => /* I18N: Name of a country or state */ I18N::translate('Lesotho'),
6806
-			'LTU' => /* I18N: Name of a country or state */ I18N::translate('Lithuania'),
6807
-			'LUX' => /* I18N: Name of a country or state */ I18N::translate('Luxembourg'),
6808
-			'LVA' => /* I18N: Name of a country or state */ I18N::translate('Latvia'),
6809
-			'MAC' => /* I18N: Name of a country or state */ I18N::translate('Macau'),
6810
-			// MAF => Saint Martin
6811
-			'MAR' => /* I18N: Name of a country or state */ I18N::translate('Morocco'),
6812
-			'MCO' => /* I18N: Name of a country or state */ I18N::translate('Monaco'),
6813
-			'MDA' => /* I18N: Name of a country or state */ I18N::translate('Moldova'),
6814
-			'MDG' => /* I18N: Name of a country or state */ I18N::translate('Madagascar'),
6815
-			'MDV' => /* I18N: Name of a country or state */ I18N::translate('Maldives'),
6816
-			'MEX' => /* I18N: Name of a country or state */ I18N::translate('Mexico'),
6817
-			'MHL' => /* I18N: Name of a country or state */ I18N::translate('Marshall Islands'),
6818
-			'MKD' => /* I18N: Name of a country or state */ I18N::translate('Macedonia'),
6819
-			'MLI' => /* I18N: Name of a country or state */ I18N::translate('Mali'),
6820
-			'MLT' => /* I18N: Name of a country or state */ I18N::translate('Malta'),
6821
-			'MMR' => /* I18N: Name of a country or state */ I18N::translate('Myanmar'),
6822
-			'MNG' => /* I18N: Name of a country or state */ I18N::translate('Mongolia'),
6823
-			'MNP' => /* I18N: Name of a country or state */ I18N::translate('Northern Mariana Islands'),
6824
-			'MNT' => /* I18N: Name of a country or state */ I18N::translate('Montenegro'),
6825
-			'MOZ' => /* I18N: Name of a country or state */ I18N::translate('Mozambique'),
6826
-			'MRT' => /* I18N: Name of a country or state */ I18N::translate('Mauritania'),
6827
-			'MSR' => /* I18N: Name of a country or state */ I18N::translate('Montserrat'),
6828
-			'MTQ' => /* I18N: Name of a country or state */ I18N::translate('Martinique'),
6829
-			'MUS' => /* I18N: Name of a country or state */ I18N::translate('Mauritius'),
6830
-			'MWI' => /* I18N: Name of a country or state */ I18N::translate('Malawi'),
6831
-			'MYS' => /* I18N: Name of a country or state */ I18N::translate('Malaysia'),
6832
-			'MYT' => /* I18N: Name of a country or state */ I18N::translate('Mayotte'),
6833
-			'NAM' => /* I18N: Name of a country or state */ I18N::translate('Namibia'),
6834
-			'NCL' => /* I18N: Name of a country or state */ I18N::translate('New Caledonia'),
6835
-			'NER' => /* I18N: Name of a country or state */ I18N::translate('Niger'),
6836
-			'NFK' => /* I18N: Name of a country or state */ I18N::translate('Norfolk Island'),
6837
-			'NGA' => /* I18N: Name of a country or state */ I18N::translate('Nigeria'),
6838
-			'NIC' => /* I18N: Name of a country or state */ I18N::translate('Nicaragua'),
6839
-			'NIR' => /* I18N: Name of a country or state */ I18N::translate('Northern Ireland'),
6840
-			'NIU' => /* I18N: Name of a country or state */ I18N::translate('Niue'),
6841
-			'NLD' => /* I18N: Name of a country or state */ I18N::translate('Netherlands'),
6842
-			'NOR' => /* I18N: Name of a country or state */ I18N::translate('Norway'),
6843
-			'NPL' => /* I18N: Name of a country or state */ I18N::translate('Nepal'),
6844
-			'NRU' => /* I18N: Name of a country or state */ I18N::translate('Nauru'),
6845
-			'NZL' => /* I18N: Name of a country or state */ I18N::translate('New Zealand'),
6846
-			'OMN' => /* I18N: Name of a country or state */ I18N::translate('Oman'),
6847
-			'PAK' => /* I18N: Name of a country or state */ I18N::translate('Pakistan'),
6848
-			'PAN' => /* I18N: Name of a country or state */ I18N::translate('Panama'),
6849
-			'PCN' => /* I18N: Name of a country or state */ I18N::translate('Pitcairn'),
6850
-			'PER' => /* I18N: Name of a country or state */ I18N::translate('Peru'),
6851
-			'PHL' => /* I18N: Name of a country or state */ I18N::translate('Philippines'),
6852
-			'PLW' => /* I18N: Name of a country or state */ I18N::translate('Palau'),
6853
-			'PNG' => /* I18N: Name of a country or state */ I18N::translate('Papua New Guinea'),
6854
-			'POL' => /* I18N: Name of a country or state */ I18N::translate('Poland'),
6855
-			'PRI' => /* I18N: Name of a country or state */ I18N::translate('Puerto Rico'),
6856
-			'PRK' => /* I18N: Name of a country or state */ I18N::translate('North Korea'),
6857
-			'PRT' => /* I18N: Name of a country or state */ I18N::translate('Portugal'),
6858
-			'PRY' => /* I18N: Name of a country or state */ I18N::translate('Paraguay'),
6859
-			'PSE' => /* I18N: Name of a country or state */ I18N::translate('Occupied Palestinian Territory'),
6860
-			'PYF' => /* I18N: Name of a country or state */ I18N::translate('French Polynesia'),
6861
-			'QAT' => /* I18N: Name of a country or state */ I18N::translate('Qatar'),
6862
-			'REU' => /* I18N: Name of a country or state */ I18N::translate('Reunion'),
6863
-			'ROM' => /* I18N: Name of a country or state */ I18N::translate('Romania'),
6864
-			'RUS' => /* I18N: Name of a country or state */ I18N::translate('Russia'),
6865
-			'RWA' => /* I18N: Name of a country or state */ I18N::translate('Rwanda'),
6866
-			'SAU' => /* I18N: Name of a country or state */ I18N::translate('Saudi Arabia'),
6867
-			'SCT' => /* I18N: Name of a country or state */ I18N::translate('Scotland'),
6868
-			'SDN' => /* I18N: Name of a country or state */ I18N::translate('Sudan'),
6869
-			'SEA' => /* I18N: Name of a country or state */ I18N::translate('At sea'),
6870
-			'SEN' => /* I18N: Name of a country or state */ I18N::translate('Senegal'),
6871
-			'SER' => /* I18N: Name of a country or state */ I18N::translate('Serbia'),
6872
-			'SGP' => /* I18N: Name of a country or state */ I18N::translate('Singapore'),
6873
-			'SGS' => /* I18N: Name of a country or state */ I18N::translate('South Georgia and the South Sandwich Islands'),
6874
-			'SHN' => /* I18N: Name of a country or state */ I18N::translate('Saint Helena'),
6875
-			'SJM' => /* I18N: Name of a country or state */ I18N::translate('Svalbard and Jan Mayen'),
6876
-			'SLB' => /* I18N: Name of a country or state */ I18N::translate('Solomon Islands'),
6877
-			'SLE' => /* I18N: Name of a country or state */ I18N::translate('Sierra Leone'),
6878
-			'SLV' => /* I18N: Name of a country or state */ I18N::translate('El Salvador'),
6879
-			'SMR' => /* I18N: Name of a country or state */ I18N::translate('San Marino'),
6880
-			'SOM' => /* I18N: Name of a country or state */ I18N::translate('Somalia'),
6881
-			'SPM' => /* I18N: Name of a country or state */ I18N::translate('Saint Pierre and Miquelon'),
6882
-			'SSD' => /* I18N: Name of a country or state */ I18N::translate('South Sudan'),
6883
-			'STP' => /* I18N: Name of a country or state */ I18N::translate('Sao Tome and Principe'),
6884
-			'SUR' => /* I18N: Name of a country or state */ I18N::translate('Suriname'),
6885
-			'SVK' => /* I18N: Name of a country or state */ I18N::translate('Slovakia'),
6886
-			'SVN' => /* I18N: Name of a country or state */ I18N::translate('Slovenia'),
6887
-			'SWE' => /* I18N: Name of a country or state */ I18N::translate('Sweden'),
6888
-			'SWZ' => /* I18N: Name of a country or state */ I18N::translate('Swaziland'),
6889
-			// SXM => Sint Maarten
6890
-			'SYC' => /* I18N: Name of a country or state */ I18N::translate('Seychelles'),
6891
-			'SYR' => /* I18N: Name of a country or state */ I18N::translate('Syria'),
6892
-			'TCA' => /* I18N: Name of a country or state */ I18N::translate('Turks and Caicos Islands'),
6893
-			'TCD' => /* I18N: Name of a country or state */ I18N::translate('Chad'),
6894
-			'TGO' => /* I18N: Name of a country or state */ I18N::translate('Togo'),
6895
-			'THA' => /* I18N: Name of a country or state */ I18N::translate('Thailand'),
6896
-			'TJK' => /* I18N: Name of a country or state */ I18N::translate('Tajikistan'),
6897
-			'TKL' => /* I18N: Name of a country or state */ I18N::translate('Tokelau'),
6898
-			'TKM' => /* I18N: Name of a country or state */ I18N::translate('Turkmenistan'),
6899
-			'TLS' => /* I18N: Name of a country or state */ I18N::translate('Timor-Leste'),
6900
-			'TON' => /* I18N: Name of a country or state */ I18N::translate('Tonga'),
6901
-			'TTO' => /* I18N: Name of a country or state */ I18N::translate('Trinidad and Tobago'),
6902
-			'TUN' => /* I18N: Name of a country or state */ I18N::translate('Tunisia'),
6903
-			'TUR' => /* I18N: Name of a country or state */ I18N::translate('Turkey'),
6904
-			'TUV' => /* I18N: Name of a country or state */ I18N::translate('Tuvalu'),
6905
-			'TWN' => /* I18N: Name of a country or state */ I18N::translate('Taiwan'),
6906
-			'TZA' => /* I18N: Name of a country or state */ I18N::translate('Tanzania'),
6907
-			'UGA' => /* I18N: Name of a country or state */ I18N::translate('Uganda'),
6908
-			'UKR' => /* I18N: Name of a country or state */ I18N::translate('Ukraine'),
6909
-			'UMI' => /* I18N: Name of a country or state */ I18N::translate('US Minor Outlying Islands'),
6910
-			'URY' => /* I18N: Name of a country or state */ I18N::translate('Uruguay'),
6911
-			'USA' => /* I18N: Name of a country or state */ I18N::translate('United States'),
6912
-			'UZB' => /* I18N: Name of a country or state */ I18N::translate('Uzbekistan'),
6913
-			'VAT' => /* I18N: Name of a country or state */ I18N::translate('Vatican City'),
6914
-			'VCT' => /* I18N: Name of a country or state */ I18N::translate('Saint Vincent and the Grenadines'),
6915
-			'VEN' => /* I18N: Name of a country or state */ I18N::translate('Venezuela'),
6916
-			'VGB' => /* I18N: Name of a country or state */ I18N::translate('British Virgin Islands'),
6917
-			'VIR' => /* I18N: Name of a country or state */ I18N::translate('US Virgin Islands'),
6918
-			'VNM' => /* I18N: Name of a country or state */ I18N::translate('Vietnam'),
6919
-			'VUT' => /* I18N: Name of a country or state */ I18N::translate('Vanuatu'),
6920
-			'WLF' => /* I18N: Name of a country or state */ I18N::translate('Wallis and Futuna'),
6921
-			'WLS' => /* I18N: Name of a country or state */ I18N::translate('Wales'),
6922
-			'WSM' => /* I18N: Name of a country or state */ I18N::translate('Samoa'),
6923
-			'YEM' => /* I18N: Name of a country or state */ I18N::translate('Yemen'),
6924
-			'ZAF' => /* I18N: Name of a country or state */ I18N::translate('South Africa'),
6925
-			'ZMB' => /* I18N: Name of a country or state */ I18N::translate('Zambia'),
6926
-			'ZWE' => /* I18N: Name of a country or state */ I18N::translate('Zimbabwe'),
6927
-		);
6928
-	}
6929
-
6930
-	/**
6931
-	 * Century name, English => 21st, Polish => XXI, etc.
6932
-	 *
6933
-	 * @param int $century
6934
-	 *
6935
-	 * @return string
6936
-	 */
6937
-	private function centuryName($century) {
6938
-		if ($century < 0) {
6939
-			return str_replace(-$century, self::centuryName(-$century), /* I18N: BCE=Before the Common Era, for Julian years < 0. See http://en.wikipedia.org/wiki/Common_Era */
6940
-				I18N::translate('%s BCE', I18N::number(-$century)));
6941
-		}
6942
-		// The current chart engine (Google charts) can't handle <sup></sup> markup
6943
-		switch ($century) {
6944
-		case 21:
6945
-			return strip_tags(I18N::translateContext('CENTURY', '21st'));
6946
-		case 20:
6947
-			return strip_tags(I18N::translateContext('CENTURY', '20th'));
6948
-		case 19:
6949
-			return strip_tags(I18N::translateContext('CENTURY', '19th'));
6950
-		case 18:
6951
-			return strip_tags(I18N::translateContext('CENTURY', '18th'));
6952
-		case 17:
6953
-			return strip_tags(I18N::translateContext('CENTURY', '17th'));
6954
-		case 16:
6955
-			return strip_tags(I18N::translateContext('CENTURY', '16th'));
6956
-		case 15:
6957
-			return strip_tags(I18N::translateContext('CENTURY', '15th'));
6958
-		case 14:
6959
-			return strip_tags(I18N::translateContext('CENTURY', '14th'));
6960
-		case 13:
6961
-			return strip_tags(I18N::translateContext('CENTURY', '13th'));
6962
-		case 12:
6963
-			return strip_tags(I18N::translateContext('CENTURY', '12th'));
6964
-		case 11:
6965
-			return strip_tags(I18N::translateContext('CENTURY', '11th'));
6966
-		case 10:
6967
-			return strip_tags(I18N::translateContext('CENTURY', '10th'));
6968
-		case  9:
6969
-			return strip_tags(I18N::translateContext('CENTURY', '9th'));
6970
-		case  8:
6971
-			return strip_tags(I18N::translateContext('CENTURY', '8th'));
6972
-		case  7:
6973
-			return strip_tags(I18N::translateContext('CENTURY', '7th'));
6974
-		case  6:
6975
-			return strip_tags(I18N::translateContext('CENTURY', '6th'));
6976
-		case  5:
6977
-			return strip_tags(I18N::translateContext('CENTURY', '5th'));
6978
-		case  4:
6979
-			return strip_tags(I18N::translateContext('CENTURY', '4th'));
6980
-		case  3:
6981
-			return strip_tags(I18N::translateContext('CENTURY', '3rd'));
6982
-		case  2:
6983
-			return strip_tags(I18N::translateContext('CENTURY', '2nd'));
6984
-		case  1:
6985
-			return strip_tags(I18N::translateContext('CENTURY', '1st'));
6986
-		default:
6987
-			return ($century - 1) . '01-' . $century . '00';
6988
-		}
6989
-	}
5577
+                $lookup = array('M' => I18N::translate('Male'), 'F' => I18N::translate('Female'), 'U' => I18N::translateContext('unknown gender', 'Unknown'), 'B' => I18N::translate('All'));
5578
+
5579
+                return '<table id="' . $table_id . '" class="givn-list"><thead><tr><th class="ui-state-default" colspan="3">' . $lookup[$sex] . '</th></tr><tr><th>' . I18N::translate('Name') . '</th><th>' . I18N::translate('Count') . '</th><th>COUNT</th></tr></thead><tbody>' . implode('', $common) . '</tbody></table>';
5580
+            case 'list':
5581
+                return '<ul>' . implode('', $common) . '</ul>';
5582
+            case 'nolist':
5583
+                return implode(I18N::$list_separator, $common);
5584
+            default:
5585
+                return '';
5586
+            }
5587
+        } else {
5588
+            return '';
5589
+        }
5590
+    }
5591
+
5592
+    /**
5593
+     * Find common give names.
5594
+     *
5595
+     * @param string[] $params
5596
+     *
5597
+     * @return string
5598
+     */
5599
+    public function commonGiven($params = array(1, 10, 'alpha')) {
5600
+        return $this->commonGivenQuery('B', 'nolist', false, $params);
5601
+    }
5602
+
5603
+    /**
5604
+     * Find common give names.
5605
+     *
5606
+     * @param string[] $params
5607
+     *
5608
+     * @return string
5609
+     */
5610
+    public function commonGivenTotals($params = array(1, 10, 'rcount')) {
5611
+        return $this->commonGivenQuery('B', 'nolist', true, $params);
5612
+    }
5613
+
5614
+    /**
5615
+     * Find common give names.
5616
+     *
5617
+     * @param string[] $params
5618
+     *
5619
+     * @return string
5620
+     */
5621
+    public function commonGivenList($params = array(1, 10, 'alpha')) {
5622
+        return $this->commonGivenQuery('B', 'list', false, $params);
5623
+    }
5624
+
5625
+    /**
5626
+     * Find common give names.
5627
+     *
5628
+     * @param string[] $params
5629
+     *
5630
+     * @return string
5631
+     */
5632
+    public function commonGivenListTotals($params = array(1, 10, 'rcount')) {
5633
+        return $this->commonGivenQuery('B', 'list', true, $params);
5634
+    }
5635
+
5636
+    /**
5637
+     * Find common give names.
5638
+     *
5639
+     * @param string[] $params
5640
+     *
5641
+     * @return string
5642
+     */
5643
+    public function commonGivenTable($params = array(1, 10, 'rcount')) {
5644
+        return $this->commonGivenQuery('B', 'table', false, $params);
5645
+    }
5646
+
5647
+    /**
5648
+     * Find common give names of females.
5649
+     *
5650
+     * @param string[] $params
5651
+     *
5652
+     * @return string
5653
+     */
5654
+    public function commonGivenFemale($params = array(1, 10, 'alpha')) {
5655
+        return $this->commonGivenQuery('F', 'nolist', false, $params);
5656
+    }
5657
+
5658
+    /**
5659
+     * Find common give names of females.
5660
+     *
5661
+     * @param string[] $params
5662
+     *
5663
+     * @return string
5664
+     */
5665
+    public function commonGivenFemaleTotals($params = array(1, 10, 'rcount')) {
5666
+        return $this->commonGivenQuery('F', 'nolist', true, $params);
5667
+    }
5668
+
5669
+    /**
5670
+     * Find common give names of females.
5671
+     *
5672
+     * @param string[] $params
5673
+     *
5674
+     * @return string
5675
+     */
5676
+    public function commonGivenFemaleList($params = array(1, 10, 'alpha')) {
5677
+        return $this->commonGivenQuery('F', 'list', false, $params);
5678
+    }
5679
+
5680
+    /**
5681
+     * Find common give names of females.
5682
+     *
5683
+     * @param string[] $params
5684
+     *
5685
+     * @return string
5686
+     */
5687
+    public function commonGivenFemaleListTotals($params = array(1, 10, 'rcount')) {
5688
+        return $this->commonGivenQuery('F', 'list', true, $params);
5689
+    }
5690
+
5691
+    /**
5692
+     * Find common give names of females.
5693
+     *
5694
+     * @param string[] $params
5695
+     *
5696
+     * @return string
5697
+     */
5698
+    public function commonGivenFemaleTable($params = array(1, 10, 'rcount')) {
5699
+        return $this->commonGivenQuery('F', 'table', false, $params);
5700
+    }
5701
+
5702
+    /**
5703
+     * Find common give names of males.
5704
+     *
5705
+     * @param string[] $params
5706
+     *
5707
+     * @return string
5708
+     */
5709
+    public function commonGivenMale($params = array(1, 10, 'alpha')) {
5710
+        return $this->commonGivenQuery('M', 'nolist', false, $params);
5711
+    }
5712
+
5713
+    /**
5714
+     * Find common give names of males.
5715
+     *
5716
+     * @param string[] $params
5717
+     *
5718
+     * @return string
5719
+     */
5720
+    public function commonGivenMaleTotals($params = array(1, 10, 'rcount')) {
5721
+        return $this->commonGivenQuery('M', 'nolist', true, $params);
5722
+    }
5723
+
5724
+    /**
5725
+     * Find common give names of males.
5726
+     *
5727
+     * @param string[] $params
5728
+     *
5729
+     * @return string
5730
+     */
5731
+    public function commonGivenMaleList($params = array(1, 10, 'alpha')) {
5732
+        return $this->commonGivenQuery('M', 'list', false, $params);
5733
+    }
5734
+
5735
+    /**
5736
+     * Find common give names of males.
5737
+     *
5738
+     * @param string[] $params
5739
+     *
5740
+     * @return string
5741
+     */
5742
+    public function commonGivenMaleListTotals($params = array(1, 10, 'rcount')) {
5743
+        return $this->commonGivenQuery('M', 'list', true, $params);
5744
+    }
5745
+
5746
+    /**
5747
+     * Find common give names of males.
5748
+     *
5749
+     * @param string[] $params
5750
+     *
5751
+     * @return string
5752
+     */
5753
+    public function commonGivenMaleTable($params = array(1, 10, 'rcount')) {
5754
+        return $this->commonGivenQuery('M', 'table', false, $params);
5755
+    }
5756
+
5757
+    /**
5758
+     * Find common give names of unknown sexes.
5759
+     *
5760
+     * @param string[] $params
5761
+     *
5762
+     * @return string
5763
+     */
5764
+    public function commonGivenUnknown($params = array(1, 10, 'alpha')) {
5765
+        return $this->commonGivenQuery('U', 'nolist', false, $params);
5766
+    }
5767
+
5768
+    /**
5769
+     * Find common give names of unknown sexes.
5770
+     *
5771
+     * @param string[] $params
5772
+     *
5773
+     * @return string
5774
+     */
5775
+    public function commonGivenUnknownTotals($params = array(1, 10, 'rcount')) {
5776
+        return $this->commonGivenQuery('U', 'nolist', true, $params);
5777
+    }
5778
+
5779
+    /**
5780
+     * Find common give names of unknown sexes.
5781
+     *
5782
+     * @param string[] $params
5783
+     *
5784
+     * @return string
5785
+     */
5786
+    public function commonGivenUnknownList($params = array(1, 10, 'alpha')) {
5787
+        return $this->commonGivenQuery('U', 'list', false, $params);
5788
+    }
5789
+
5790
+    /**
5791
+     * Find common give names of unknown sexes.
5792
+     *
5793
+     * @param string[] $params
5794
+     *
5795
+     * @return string
5796
+     */
5797
+    public function commonGivenUnknownListTotals($params = array(1, 10, 'rcount')) {
5798
+        return $this->commonGivenQuery('U', 'list', true, $params);
5799
+    }
5800
+
5801
+    /**
5802
+     * Find common give names of unknown sexes.
5803
+     *
5804
+     * @param string[] $params
5805
+     *
5806
+     * @return string
5807
+     */
5808
+    public function commonGivenUnknownTable($params = array(1, 10, 'rcount')) {
5809
+        return $this->commonGivenQuery('U', 'table', false, $params);
5810
+    }
5811
+
5812
+    /**
5813
+     * Create a chart of common given names.
5814
+     *
5815
+     * @param string[] $params
5816
+     *
5817
+     * @return string
5818
+     */
5819
+    public function chartCommonGiven($params = array()) {
5820
+        $WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
5821
+        $WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
5822
+        $WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
5823
+        $WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
5824
+
5825
+        if (isset($params[0]) && $params[0] != '') {
5826
+            $size = strtolower($params[0]);
5827
+        } else {
5828
+            $size = $WT_STATS_S_CHART_X . "x" . $WT_STATS_S_CHART_Y;
5829
+        }
5830
+        if (isset($params[1]) && $params[1] != '') {
5831
+            $color_from = strtolower($params[1]);
5832
+        } else {
5833
+            $color_from = $WT_STATS_CHART_COLOR1;
5834
+        }
5835
+        if (isset($params[2]) && $params[2] != '') {
5836
+            $color_to = strtolower($params[2]);
5837
+        } else {
5838
+            $color_to = $WT_STATS_CHART_COLOR2;
5839
+        }
5840
+        if (isset($params[4]) && $params[4] != '') {
5841
+            $maxtoshow = strtolower($params[4]);
5842
+        } else {
5843
+            $maxtoshow = 7;
5844
+        }
5845
+        $sizes    = explode('x', $size);
5846
+        $tot_indi = $this->totalIndividualsQuery();
5847
+        $given    = $this->commonGivenQuery('B', 'chart');
5848
+        if (!is_array($given)) {
5849
+            return '';
5850
+        }
5851
+        $given = array_slice($given, 0, $maxtoshow);
5852
+        if (count($given) <= 0) {
5853
+            return '';
5854
+        }
5855
+        $tot = 0;
5856
+        foreach ($given as $count) {
5857
+            $tot += $count;
5858
+        }
5859
+        $chd = '';
5860
+        $chl = array();
5861
+        foreach ($given as $givn => $count) {
5862
+            if ($tot == 0) {
5863
+                $per = 0;
5864
+            } else {
5865
+                $per = round(100 * $count / $tot_indi, 0);
5866
+            }
5867
+            $chd .= $this->arrayToExtendedEncoding(array($per));
5868
+            $chl[] = $givn . ' - ' . I18N::number($count);
5869
+        }
5870
+        $per = round(100 * ($tot_indi - $tot) / $tot_indi, 0);
5871
+        $chd .= $this->arrayToExtendedEncoding(array($per));
5872
+        $chl[] = I18N::translate('Other') . ' - ' . I18N::number($tot_indi - $tot);
5873
+
5874
+        $chart_title = implode(I18N::$list_separator, $chl);
5875
+        $chl         = implode('|', $chl);
5876
+
5877
+        return "<img src=\"https://chart.googleapis.com/chart?cht=p3&amp;chd=e:{$chd}&amp;chs={$size}&amp;chco={$color_from},{$color_to}&amp;chf=bg,s,ffffff00&amp;chl=" . rawurlencode($chl) . "\" width=\"{$sizes[0]}\" height=\"{$sizes[1]}\" alt=\"" . $chart_title . "\" title=\"" . $chart_title . "\" />";
5878
+    }
5879
+
5880
+    /**
5881
+     * Who is currently logged in?
5882
+     *
5883
+     * @param string $type
5884
+     *
5885
+     * @return string
5886
+     */
5887
+    private function usersLoggedInQuery($type = 'nolist') {
5888
+        $content = '';
5889
+        // List active users
5890
+        $NumAnonymous = 0;
5891
+        $loggedusers  = array();
5892
+        foreach (User::allLoggedIn() as $user) {
5893
+            if (Auth::isAdmin() || $user->getPreference('visibleonline')) {
5894
+                $loggedusers[] = $user;
5895
+            } else {
5896
+                $NumAnonymous++;
5897
+            }
5898
+        }
5899
+        $LoginUsers = count($loggedusers);
5900
+        if ($LoginUsers == 0 && $NumAnonymous == 0) {
5901
+            return I18N::translate('No signed-in and no anonymous users');
5902
+        }
5903
+        if ($NumAnonymous > 0) {
5904
+            $content .= '<b>' . I18N::plural('%s anonymous signed-in user', '%s anonymous signed-in users', $NumAnonymous, I18N::number($NumAnonymous)) . '</b>';
5905
+        }
5906
+        if ($LoginUsers > 0) {
5907
+            if ($NumAnonymous) {
5908
+                if ($type == 'list') {
5909
+                    $content .= "<br><br>";
5910
+                } else {
5911
+                    $content .= " " . I18N::translate('and') . " ";
5912
+                }
5913
+            }
5914
+            $content .= '<b>' . I18N::plural('%s signed-in user', '%s signed-in users', $LoginUsers, I18N::number($LoginUsers)) . '</b>';
5915
+            if ($type == 'list') {
5916
+                $content .= '<ul>';
5917
+            } else {
5918
+                $content .= ': ';
5919
+            }
5920
+        }
5921
+        if (Auth::check()) {
5922
+            foreach ($loggedusers as $user) {
5923
+                if ($type == 'list') {
5924
+                    $content .= '<li>' . Filter::escapeHtml($user->getRealName()) . ' - ' . Filter::escapeHtml($user->getUserName());
5925
+                } else {
5926
+                    $content .= Filter::escapeHtml($user->getRealName()) . ' - ' . Filter::escapeHtml($user->getUserName());
5927
+                }
5928
+                if (Auth::id() != $user->getUserId() && $user->getPreference('contactmethod') != 'none') {
5929
+                    if ($type == 'list') {
5930
+                        $content .= '<br><a class="icon-email" href="#" onclick="return message(\'' . $user->getUserId() . '\', \'\', \'' . Filter::escapeJs(Functions::getQueryUrl()) . '\');" title="' . I18N::translate('Send a message') . '"></a>';
5931
+                    } else {
5932
+                        $content .= ' <a class="icon-email" href="#" onclick="return message(\'' . $user->getUserId() . '\', \'\', \'' . Filter::escapeJs(Functions::getQueryUrl()) . '\');" title="' . I18N::translate('Send a message') . '"></a>';
5933
+                    }
5934
+                }
5935
+                if ($type == 'list') {
5936
+                    $content .= '</li>';
5937
+                }
5938
+            }
5939
+        }
5940
+        if ($type == 'list') {
5941
+            $content .= '</ul>';
5942
+        }
5943
+
5944
+        return $content;
5945
+    }
5946
+
5947
+    /**
5948
+     * NUmber of users who are currently logged in?
5949
+     *
5950
+     * @param string $type
5951
+     *
5952
+     * @return int
5953
+     */
5954
+    private function usersLoggedInTotalQuery($type = 'all') {
5955
+        $anon    = 0;
5956
+        $visible = 0;
5957
+        foreach (User::allLoggedIn() as $user) {
5958
+            if (Auth::isAdmin() || $user->getPreference('visibleonline')) {
5959
+                $visible++;
5960
+            } else {
5961
+                $anon++;
5962
+            }
5963
+        }
5964
+        if ($type == 'anon') {
5965
+            return $anon;
5966
+        } elseif ($type == 'visible') {
5967
+            return $visible;
5968
+        } else {
5969
+            return $visible + $anon;
5970
+        }
5971
+    }
5972
+
5973
+    /**
5974
+     * Who is currently logged in?
5975
+     *
5976
+     * @return string
5977
+     */
5978
+    public function usersLoggedIn() {
5979
+        return $this->usersLoggedInQuery('nolist');
5980
+    }
5981
+
5982
+    /**
5983
+     * Who is currently logged in?
5984
+     *
5985
+     * @return string
5986
+     */
5987
+    public function usersLoggedInList() {
5988
+        return $this->usersLoggedInQuery('list');
5989
+    }
5990
+
5991
+    /**
5992
+     * Who is currently logged in?
5993
+     *
5994
+     * @return int
5995
+     */
5996
+    public function usersLoggedInTotal() {
5997
+        return $this->usersLoggedInTotalQuery('all');
5998
+    }
5999
+
6000
+    /**
6001
+     * Which visitors are currently logged in?
6002
+     *
6003
+     * @return int
6004
+     */
6005
+    public function usersLoggedInTotalAnon() {
6006
+        return $this->usersLoggedInTotalQuery('anon');
6007
+    }
6008
+
6009
+    /**
6010
+     * Which visitors are currently logged in?
6011
+     *
6012
+     * @return int
6013
+     */
6014
+    public function usersLoggedInTotalVisible() {
6015
+        return $this->usersLoggedInTotalQuery('visible');
6016
+    }
6017
+
6018
+    /**
6019
+     * Get the current user's ID.
6020
+     *
6021
+     * @return null|string
6022
+     */
6023
+    public function userId() {
6024
+        return Auth::id();
6025
+    }
6026
+
6027
+    /**
6028
+     * Get the current user's username.
6029
+     *
6030
+     * @param string[] $params
6031
+     *
6032
+     * @return string
6033
+     */
6034
+    public function userName($params = array()) {
6035
+        if (Auth::check()) {
6036
+            return Filter::escapeHtml(Auth::user()->getUserName());
6037
+        } elseif (isset($params[0]) && $params[0] != '') {
6038
+            // if #username:visitor# was specified, then "visitor" will be returned when the user is not logged in
6039
+            return Filter::escapeHtml($params[0]);
6040
+        } else {
6041
+            return '';
6042
+        }
6043
+    }
6044
+
6045
+    /**
6046
+     * Get the current user's full name.
6047
+     *
6048
+     * @return string
6049
+     */
6050
+    public function userFullName() {
6051
+        return Auth::check() ? Auth::user()->getRealNameHtml() : '';
6052
+    }
6053
+
6054
+    /**
6055
+     * Get the newest registered user.
6056
+     *
6057
+     * @param string   $type
6058
+     * @param string[] $params
6059
+     *
6060
+     * @return string
6061
+     */
6062
+    private function getLatestUserData($type = 'userid', $params = array()) {
6063
+        static $user_id = null;
6064
+
6065
+        if ($user_id === null) {
6066
+            $user = User::findLatestToRegister();
6067
+        } else {
6068
+            $user = User::find($user_id);
6069
+        }
6070
+
6071
+        switch ($type) {
6072
+        default:
6073
+        case 'userid':
6074
+            return $user->getUserId();
6075
+        case 'username':
6076
+            return Filter::escapeHtml($user->getUserName());
6077
+        case 'fullname':
6078
+            return $user->getRealNameHtml();
6079
+        case 'regdate':
6080
+            if (is_array($params) && isset($params[0]) && $params[0] != '') {
6081
+                $datestamp = $params[0];
6082
+            } else {
6083
+                $datestamp = I18N::dateFormat();
6084
+            }
6085
+
6086
+            return FunctionsDate::timestampToGedcomDate($user->getPreference('reg_timestamp'))->display(false, $datestamp);
6087
+        case 'regtime':
6088
+            if (is_array($params) && isset($params[0]) && $params[0] != '') {
6089
+                $datestamp = $params[0];
6090
+            } else {
6091
+                $datestamp = str_replace('%', '', I18N::timeFormat());
6092
+            }
6093
+
6094
+            return date($datestamp, $user->getPreference('reg_timestamp'));
6095
+        case 'loggedin':
6096
+            if (is_array($params) && isset($params[0]) && $params[0] != '') {
6097
+                $yes = $params[0];
6098
+            } else {
6099
+                $yes = I18N::translate('yes');
6100
+            }
6101
+            if (is_array($params) && isset($params[1]) && $params[1] != '') {
6102
+                $no = $params[1];
6103
+            } else {
6104
+                $no = I18N::translate('no');
6105
+            }
6106
+
6107
+            return Database::prepare("SELECT 1 FROM `##session` WHERE user_id=? LIMIT 1")->execute(array($user->getUserId()))->fetchOne() ? $yes : $no;
6108
+        }
6109
+    }
6110
+
6111
+    /**
6112
+     * Get the newest registered user's ID.
6113
+     *
6114
+     * @return string
6115
+     */
6116
+    public function latestUserId() {
6117
+        return $this->getLatestUserData('userid');
6118
+    }
6119
+
6120
+    /**
6121
+     * Get the newest registered user's username.
6122
+     *
6123
+     * @return string
6124
+     */
6125
+    public function latestUserName() {
6126
+        return $this->getLatestUserData('username');
6127
+    }
6128
+
6129
+    /**
6130
+     * Get the newest registered user's real name.
6131
+     *
6132
+     * @return string
6133
+     */
6134
+    public function latestUserFullName() {
6135
+        return $this->getLatestUserData('fullname');
6136
+    }
6137
+
6138
+    /**
6139
+     * Get the date of the newest user registration.
6140
+     *
6141
+     * @param string[] $params
6142
+     *
6143
+     * @return string
6144
+     */
6145
+    public function latestUserRegDate($params = array()) {
6146
+        return $this->getLatestUserData('regdate', $params);
6147
+    }
6148
+
6149
+    /**
6150
+     * Find the timestamp of the latest user to register.
6151
+     *
6152
+     * @param string[] $params
6153
+     *
6154
+     * @return string
6155
+     */
6156
+    public function latestUserRegTime($params = array()) {
6157
+        return $this->getLatestUserData('regtime', $params);
6158
+    }
6159
+
6160
+    /**
6161
+     * Find the most recent user to log in.
6162
+     *
6163
+     * @param string[] $params
6164
+     *
6165
+     * @return string
6166
+     */
6167
+    public function latestUserLoggedin($params = array()) {
6168
+        return $this->getLatestUserData('loggedin', $params);
6169
+    }
6170
+
6171
+    /**
6172
+     * Create a link to contact the webmaster.
6173
+     *
6174
+     * @return string
6175
+     */
6176
+    public function contactWebmaster() {
6177
+        $user_id = $this->tree->getPreference('WEBMASTER_USER_ID');
6178
+        $user    = User::find($user_id);
6179
+        if ($user) {
6180
+            return Theme::theme()->contactLink($user);
6181
+        } else {
6182
+            return $user_id;
6183
+        }
6184
+    }
6185
+
6186
+    /**
6187
+     * Create a link to contact the genealogy contact.
6188
+     *
6189
+     * @return string
6190
+     */
6191
+    public function contactGedcom() {
6192
+        $user_id = $this->tree->getPreference('CONTACT_USER_ID');
6193
+        $user    = User::find($user_id);
6194
+        if ($user) {
6195
+            return Theme::theme()->contactLink($user);
6196
+        } else {
6197
+            return $user_id;
6198
+        }
6199
+    }
6200
+
6201
+    /**
6202
+     * What is the current date on the server?
6203
+     *
6204
+     * @return string
6205
+     */
6206
+    public function serverDate() {
6207
+        return FunctionsDate::timestampToGedcomDate(WT_TIMESTAMP)->display();
6208
+    }
6209
+
6210
+    /**
6211
+     * What is the current time on the server (in 12 hour clock)?
6212
+     *
6213
+     * @return string
6214
+     */
6215
+    public function serverTime() {
6216
+        return date('g:i a');
6217
+    }
6218
+
6219
+    /**
6220
+     * What is the current time on the server (in 24 hour clock)?
6221
+     *
6222
+     * @return string
6223
+     */
6224
+    public function serverTime24() {
6225
+        return date('G:i');
6226
+    }
6227
+
6228
+    /**
6229
+     * What is the timezone of the server.
6230
+     *
6231
+     * @return string
6232
+     */
6233
+    public function serverTimezone() {
6234
+        return date('T');
6235
+    }
6236
+
6237
+    /**
6238
+     * What is the client's date.
6239
+     *
6240
+     * @return string
6241
+     */
6242
+    public function browserDate() {
6243
+        return FunctionsDate::timestampToGedcomDate(WT_TIMESTAMP + WT_TIMESTAMP_OFFSET)->display();
6244
+    }
6245
+
6246
+    /**
6247
+     * What is the client's timestamp.
6248
+     *
6249
+     * @return string
6250
+     */
6251
+    public function browserTime() {
6252
+        return date(str_replace('%', '', I18N::timeFormat()), WT_TIMESTAMP + WT_TIMESTAMP_OFFSET);
6253
+    }
6254
+
6255
+    /**
6256
+     * What is the browser's tiemzone.
6257
+     *
6258
+     * @return string
6259
+     */
6260
+    public function browserTimezone() {
6261
+        return date('T', WT_TIMESTAMP + WT_TIMESTAMP_OFFSET);
6262
+    }
6263
+
6264
+    /**
6265
+     * What is the current version of webtrees.
6266
+     *
6267
+     * @return string
6268
+     */
6269
+    public function webtreesVersion() {
6270
+        return WT_VERSION;
6271
+    }
6272
+
6273
+    /**
6274
+     * These functions provide access to hitcounter for use in the HTML block.
6275
+     *
6276
+     * @param string   $page_name
6277
+     * @param string[] $params
6278
+     *
6279
+     * @return string
6280
+     */
6281
+    private function hitCountQuery($page_name, $params) {
6282
+        if (is_array($params) && isset($params[0]) && $params[0] != '') {
6283
+            $page_parameter = $params[0];
6284
+        } else {
6285
+            $page_parameter = '';
6286
+        }
6287
+
6288
+        if ($page_name === null) {
6289
+            // index.php?ctype=gedcom
6290
+            $page_name      = 'index.php';
6291
+            $page_parameter = 'gedcom:' . ($page_parameter ? Tree::findByName($page_parameter)->getTreeId() : $this->tree->getTreeId());
6292
+        } elseif ($page_name == 'index.php') {
6293
+            // index.php?ctype=user
6294
+            $user           = User::findByIdentifier($page_parameter);
6295
+            $page_parameter = 'user:' . ($user ? $user->getUserId() : Auth::id());
6296
+        } else {
6297
+            // indi/fam/sour/etc.
6298
+        }
6299
+
6300
+        return '<span class="odometer">' . I18N::digits(HitCounter::getCount($this->tree, $page_name, $page_parameter)) . '</span>';
6301
+    }
6302
+
6303
+    /**
6304
+     * How many times has a page been viewed.
6305
+     *
6306
+     * @param string[] $params
6307
+     *
6308
+     * @return string
6309
+     */
6310
+    public function hitCount($params = array()) {
6311
+        return $this->hitCountQuery(null, $params);
6312
+    }
6313
+
6314
+    /**
6315
+     * How many times has a page been viewed.
6316
+     *
6317
+     * @param string[] $params
6318
+     *
6319
+     * @return string
6320
+     */
6321
+    public function hitCountUser($params = array()) {
6322
+        return $this->hitCountQuery('index.php', $params);
6323
+    }
6324
+
6325
+    /**
6326
+     * How many times has a page been viewed.
6327
+     *
6328
+     * @param string[] $params
6329
+     *
6330
+     * @return string
6331
+     */
6332
+    public function hitCountIndi($params = array()) {
6333
+        return $this->hitCountQuery('individual.php', $params);
6334
+    }
6335
+
6336
+    /**
6337
+     * How many times has a page been viewed.
6338
+     *
6339
+     * @param string[] $params
6340
+     *
6341
+     * @return string
6342
+     */
6343
+    public function hitCountFam($params = array()) {
6344
+        return $this->hitCountQuery('family.php', $params);
6345
+    }
6346
+
6347
+    /**
6348
+     * How many times has a page been viewed.
6349
+     *
6350
+     * @param string[] $params
6351
+     *
6352
+     * @return string
6353
+     */
6354
+    public function hitCountSour($params = array()) {
6355
+        return $this->hitCountQuery('source.php', $params);
6356
+    }
6357
+
6358
+    /**
6359
+     * How many times has a page been viewed.
6360
+     *
6361
+     * @param string[] $params
6362
+     *
6363
+     * @return string
6364
+     */
6365
+    public function hitCountRepo($params = array()) {
6366
+        return $this->hitCountQuery('repo.php', $params);
6367
+    }
6368
+
6369
+    /**
6370
+     * How many times has a page been viewed.
6371
+     *
6372
+     * @param string[] $params
6373
+     *
6374
+     * @return string
6375
+     */
6376
+    public function hitCountNote($params = array()) {
6377
+        return $this->hitCountQuery('note.php', $params);
6378
+    }
6379
+
6380
+    /**
6381
+     * How many times has a page been viewed.
6382
+     *
6383
+     * @param string[] $params
6384
+     *
6385
+     * @return string
6386
+     */
6387
+    public function hitCountObje($params = array()) {
6388
+        return $this->hitCountQuery('mediaviewer.php', $params);
6389
+    }
6390
+
6391
+    /**
6392
+     * Convert numbers to Google's custom encoding.
6393
+     *
6394
+     * @link http://bendodson.com/news/google-extended-encoding-made-easy
6395
+     *
6396
+     * @param int[] $a
6397
+     *
6398
+     * @return string
6399
+     */
6400
+    private function arrayToExtendedEncoding($a) {
6401
+        $xencoding = WT_GOOGLE_CHART_ENCODING;
6402
+
6403
+        $encoding = '';
6404
+        foreach ($a as $value) {
6405
+            if ($value < 0) {
6406
+                $value = 0;
6407
+            }
6408
+            $first  = (int) ($value / 64);
6409
+            $second = $value % 64;
6410
+            $encoding .= $xencoding[(int) $first] . $xencoding[(int) $second];
6411
+        }
6412
+
6413
+        return $encoding;
6414
+    }
6415
+
6416
+    /**
6417
+     * Callback function to compare totals.
6418
+     *
6419
+     * @param array $a
6420
+     * @param array $b
6421
+     *
6422
+     * @return int
6423
+     */
6424
+    private function nameTotalSort($a, $b) {
6425
+        return $a['match'] - $b['match'];
6426
+    }
6427
+
6428
+    /**
6429
+     * Callback function to compare totals.
6430
+     *
6431
+     * @param array $a
6432
+     * @param array $b
6433
+     *
6434
+     * @return int
6435
+     */
6436
+    private function nameTotalReverseSort($a, $b) {
6437
+        return $b['match'] - $a['match'];
6438
+    }
6439
+
6440
+    /**
6441
+     * Run an SQL query and cache the result.
6442
+     *
6443
+     * @param string $sql
6444
+     *
6445
+     * @return string[][]
6446
+     */
6447
+    private function runSql($sql) {
6448
+        static $cache = array();
6449
+
6450
+        $id = md5($sql);
6451
+        if (isset($cache[$id])) {
6452
+            return $cache[$id];
6453
+        }
6454
+        $rows       = Database::prepare($sql)->fetchAll(PDO::FETCH_ASSOC);
6455
+        $cache[$id] = $rows;
6456
+
6457
+        return $rows;
6458
+    }
6459
+
6460
+    /**
6461
+     * Find the favorites for the tree.
6462
+     *
6463
+     * @return string
6464
+     */
6465
+    public function gedcomFavorites() {
6466
+        if (Module::getModuleByName('gedcom_favorites')) {
6467
+            $block = new FamilyTreeFavoritesModule(WT_MODULES_DIR . 'gedcom_favorites');
6468
+
6469
+            return $block->getBlock(0, false);
6470
+        } else {
6471
+            return '';
6472
+        }
6473
+    }
6474
+
6475
+    /**
6476
+     * Find the favorites for the user.
6477
+     *
6478
+     * @return string
6479
+     */
6480
+    public function userFavorites() {
6481
+        if (Auth::check() && Module::getModuleByName('user_favorites')) {
6482
+            $block = new UserFavoritesModule(WT_MODULES_DIR . 'gedcom_favorites');
6483
+
6484
+            return $block->getBlock(0, false);
6485
+        } else {
6486
+            return '';
6487
+        }
6488
+    }
6489
+
6490
+    /**
6491
+     * Find the number of favorites for the tree.
6492
+     *
6493
+     * @return int
6494
+     */
6495
+    public function totalGedcomFavorites() {
6496
+        if (Module::getModuleByName('gedcom_favorites')) {
6497
+            return count(FamilyTreeFavoritesModule::getFavorites($this->tree->getTreeId()));
6498
+        } else {
6499
+            return 0;
6500
+        }
6501
+    }
6502
+
6503
+    /**
6504
+     * Find the number of favorites for the user.
6505
+     *
6506
+     * @return int
6507
+     */
6508
+    public function totalUserFavorites() {
6509
+        if (Module::getModuleByName('user_favorites')) {
6510
+            return count(UserFavoritesModule::getFavorites(Auth::id()));
6511
+        } else {
6512
+            return 0;
6513
+        }
6514
+    }
6515
+
6516
+    /**
6517
+     * Create any of the other blocks.
6518
+     *
6519
+     * Use as #callBlock:block_name#
6520
+     *
6521
+     * @param string[] $params
6522
+     *
6523
+     * @return string
6524
+     */
6525
+    public function callBlock($params = array()) {
6526
+        global $ctype;
6527
+
6528
+        if (isset($params[0]) && $params[0] != '') {
6529
+            $block = $params[0];
6530
+        } else {
6531
+            return '';
6532
+        }
6533
+        $all_blocks = array();
6534
+        foreach (Module::getActiveBlocks($this->tree) as $name => $active_block) {
6535
+            if ($ctype == 'user' && $active_block->isUserBlock() || $ctype == 'gedcom' && $active_block->isGedcomBlock()) {
6536
+                $all_blocks[$name] = $active_block;
6537
+            }
6538
+        }
6539
+        if (!array_key_exists($block, $all_blocks) || $block == 'html') {
6540
+            return '';
6541
+        }
6542
+        // Build the config array
6543
+        array_shift($params);
6544
+        $cfg = array();
6545
+        foreach ($params as $config) {
6546
+            $bits = explode('=', $config);
6547
+            if (count($bits) < 2) {
6548
+                continue;
6549
+            }
6550
+            $v       = array_shift($bits);
6551
+            $cfg[$v] = implode('=', $bits);
6552
+        }
6553
+        $block    = $all_blocks[$block];
6554
+        $block_id = Filter::getInteger('block_id');
6555
+        $content  = $block->getBlock($block_id, false, $cfg);
6556
+
6557
+        return $content;
6558
+    }
6559
+
6560
+    /**
6561
+     * How many messages in the user's inbox.
6562
+     *
6563
+     * @return string
6564
+     */
6565
+    public function totalUserMessages() {
6566
+        $total = (int) Database::prepare("SELECT COUNT(*) FROM `##message` WHERE user_id = ?")
6567
+            ->execute(array(Auth::id()))
6568
+            ->fetchOne();
6569
+
6570
+        return I18N::number($total);
6571
+    }
6572
+
6573
+    /**
6574
+     * How many blog entries exist for this user.
6575
+     *
6576
+     * @return string
6577
+     */
6578
+    public function totalUserJournal() {
6579
+        try {
6580
+            $number = (int) Database::prepare("SELECT COUNT(*) FROM `##news` WHERE user_id = ?")
6581
+                ->execute(array(Auth::id()))
6582
+                ->fetchOne();
6583
+        } catch (PDOException $ex) {
6584
+            // The module may not be installed, so the table may not exist.
6585
+            $number = 0;
6586
+        }
6587
+
6588
+        return I18N::number($number);
6589
+    }
6590
+
6591
+    /**
6592
+     * How many news items exist for this tree.
6593
+     *
6594
+     * @return string
6595
+     */
6596
+    public function totalGedcomNews() {
6597
+        try {
6598
+            $number = (int) Database::prepare("SELECT COUNT(*) FROM `##news` WHERE gedcom_id = ?")
6599
+                ->execute(array($this->tree->getTreeId()))
6600
+                ->fetchOne();
6601
+        } catch (PDOException $ex) {
6602
+            // The module may not be installed, so the table may not exist.
6603
+            $number = 0;
6604
+        }
6605
+
6606
+        return I18N::number($number);
6607
+    }
6608
+
6609
+    /**
6610
+     * ISO3166 3 letter codes, with their 2 letter equivalent.
6611
+     * NOTE: this is not 1:1. ENG/SCO/WAL/NIR => GB
6612
+     * NOTE: this also includes champman codes and others. Should it?
6613
+     *
6614
+     * @return string[]
6615
+     */
6616
+    public function iso3166() {
6617
+        return array(
6618
+            'ABW' => 'AW', 'AFG' => 'AF', 'AGO' => 'AO', 'AIA' => 'AI', 'ALA' => 'AX', 'ALB' => 'AL',
6619
+            'AND' => 'AD', 'ARE' => 'AE', 'ARG' => 'AR', 'ARM' => 'AM', 'ASM' => 'AS',
6620
+            'ATA' => 'AQ', 'ATF' => 'TF', 'ATG' => 'AG', 'AUS' => 'AU', 'AUT' => 'AT', 'AZE' => 'AZ',
6621
+            'BDI' => 'BI', 'BEL' => 'BE', 'BEN' => 'BJ', 'BFA' => 'BF', 'BGD' => 'BD', 'BGR' => 'BG',
6622
+            'BHR' => 'BH', 'BHS' => 'BS', 'BIH' => 'BA', 'BLR' => 'BY', 'BLZ' => 'BZ', 'BMU' => 'BM',
6623
+            'BOL' => 'BO', 'BRA' => 'BR', 'BRB' => 'BB', 'BRN' => 'BN', 'BTN' => 'BT', 'BVT' => 'BV',
6624
+            'BWA' => 'BW', 'CAF' => 'CF', 'CAN' => 'CA', 'CCK' => 'CC', 'CHE' => 'CH', 'CHL' => 'CL',
6625
+            'CHN' => 'CN', 'CIV' => 'CI', 'CMR' => 'CM', 'COD' => 'CD', 'COG' => 'CG',
6626
+            'COK' => 'CK', 'COL' => 'CO', 'COM' => 'KM', 'CPV' => 'CV', 'CRI' => 'CR', 'CUB' => 'CU',
6627
+            'CXR' => 'CX', 'CYM' => 'KY', 'CYP' => 'CY', 'CZE' => 'CZ', 'DEU' => 'DE', 'DJI' => 'DJ',
6628
+            'DMA' => 'DM', 'DNK' => 'DK', 'DOM' => 'DO', 'DZA' => 'DZ', 'ECU' => 'EC', 'EGY' => 'EG',
6629
+            'ENG' => 'GB', 'ERI' => 'ER', 'ESH' => 'EH', 'ESP' => 'ES', 'EST' => 'EE', 'ETH' => 'ET',
6630
+            'FIN' => 'FI', 'FJI' => 'FJ', 'FLK' => 'FK', 'FRA' => 'FR', 'FRO' => 'FO', 'FSM' => 'FM',
6631
+            'GAB' => 'GA', 'GBR' => 'GB', 'GEO' => 'GE', 'GHA' => 'GH', 'GIB' => 'GI', 'GIN' => 'GN',
6632
+            'GLP' => 'GP', 'GMB' => 'GM', 'GNB' => 'GW', 'GNQ' => 'GQ', 'GRC' => 'GR', 'GRD' => 'GD',
6633
+            'GRL' => 'GL', 'GTM' => 'GT', 'GUF' => 'GF', 'GUM' => 'GU', 'GUY' => 'GY', 'HKG' => 'HK',
6634
+            'HMD' => 'HM', 'HND' => 'HN', 'HRV' => 'HR', 'HTI' => 'HT', 'HUN' => 'HU', 'IDN' => 'ID',
6635
+            'IND' => 'IN', 'IOT' => 'IO', 'IRL' => 'IE', 'IRN' => 'IR', 'IRQ' => 'IQ', 'ISL' => 'IS',
6636
+            'ISR' => 'IL', 'ITA' => 'IT', 'JAM' => 'JM', 'JOR' => 'JO', 'JPN' => 'JA', 'KAZ' => 'KZ',
6637
+            'KEN' => 'KE', 'KGZ' => 'KG', 'KHM' => 'KH', 'KIR' => 'KI', 'KNA' => 'KN', 'KOR' => 'KO',
6638
+            'KWT' => 'KW', 'LAO' => 'LA', 'LBN' => 'LB', 'LBR' => 'LR', 'LBY' => 'LY', 'LCA' => 'LC',
6639
+            'LIE' => 'LI', 'LKA' => 'LK', 'LSO' => 'LS', 'LTU' => 'LT', 'LUX' => 'LU', 'LVA' => 'LV',
6640
+            'MAC' => 'MO', 'MAR' => 'MA', 'MCO' => 'MC', 'MDA' => 'MD', 'MDG' => 'MG', 'MDV' => 'MV',
6641
+            'MEX' => 'MX', 'MHL' => 'MH', 'MKD' => 'MK', 'MLI' => 'ML', 'MLT' => 'MT', 'MMR' => 'MM',
6642
+            'MNG' => 'MN', 'MNP' => 'MP', 'MNT' => 'ME', 'MOZ' => 'MZ', 'MRT' => 'MR', 'MSR' => 'MS',
6643
+            'MTQ' => 'MQ', 'MUS' => 'MU', 'MWI' => 'MW', 'MYS' => 'MY', 'MYT' => 'YT', 'NAM' => 'NA',
6644
+            'NCL' => 'NC', 'NER' => 'NE', 'NFK' => 'NF', 'NGA' => 'NG', 'NIC' => 'NI', 'NIR' => 'GB',
6645
+            'NIU' => 'NU', 'NLD' => 'NL', 'NOR' => 'NO', 'NPL' => 'NP', 'NRU' => 'NR', 'NZL' => 'NZ',
6646
+            'OMN' => 'OM', 'PAK' => 'PK', 'PAN' => 'PA', 'PCN' => 'PN', 'PER' => 'PE', 'PHL' => 'PH',
6647
+            'PLW' => 'PW', 'PNG' => 'PG', 'POL' => 'PL', 'PRI' => 'PR', 'PRK' => 'KP', 'PRT' => 'PO',
6648
+            'PRY' => 'PY', 'PSE' => 'PS', 'PYF' => 'PF', 'QAT' => 'QA', 'REU' => 'RE', 'ROM' => 'RO',
6649
+            'RUS' => 'RU', 'RWA' => 'RW', 'SAU' => 'SA', 'SCT' => 'GB', 'SDN' => 'SD', 'SEN' => 'SN',
6650
+            'SER' => 'RS', 'SGP' => 'SG', 'SGS' => 'GS', 'SHN' => 'SH', 'SJM' => 'SJ',
6651
+            'SLB' => 'SB', 'SLE' => 'SL', 'SLV' => 'SV', 'SMR' => 'SM', 'SOM' => 'SO', 'SPM' => 'PM',
6652
+            'STP' => 'ST', 'SUR' => 'SR', 'SVK' => 'SK', 'SVN' => 'SI', 'SWE' => 'SE',
6653
+            'SWZ' => 'SZ', 'SYC' => 'SC', 'SYR' => 'SY', 'TCA' => 'TC', 'TCD' => 'TD', 'TGO' => 'TG',
6654
+            'THA' => 'TH', 'TJK' => 'TJ', 'TKL' => 'TK', 'TKM' => 'TM', 'TLS' => 'TL', 'TON' => 'TO',
6655
+            'TTO' => 'TT', 'TUN' => 'TN', 'TUR' => 'TR', 'TUV' => 'TV', 'TWN' => 'TW', 'TZA' => 'TZ',
6656
+            'UGA' => 'UG', 'UKR' => 'UA', 'UMI' => 'UM', 'URY' => 'UY', 'USA' => 'US', 'UZB' => 'UZ',
6657
+            'VAT' => 'VA', 'VCT' => 'VC', 'VEN' => 'VE', 'VGB' => 'VG', 'VIR' => 'VI', 'VNM' => 'VN',
6658
+            'VUT' => 'VU', 'WLF' => 'WF', 'WLS' => 'GB', 'WSM' => 'WS', 'YEM' => 'YE', 'ZAF' => 'ZA',
6659
+            'ZMB' => 'ZM', 'ZWE' => 'ZW',
6660
+        );
6661
+    }
6662
+
6663
+    /**
6664
+     * Country codes and names
6665
+     *
6666
+     * @return string[]
6667
+     */
6668
+    public function getAllCountries() {
6669
+        return array(
6670
+            '???' => /* I18N: Name of a country or state */ I18N::translate('Unknown'),
6671
+            'ABW' => /* I18N: Name of a country or state */ I18N::translate('Aruba'),
6672
+            'AFG' => /* I18N: Name of a country or state */ I18N::translate('Afghanistan'),
6673
+            'AGO' => /* I18N: Name of a country or state */ I18N::translate('Angola'),
6674
+            'AIA' => /* I18N: Name of a country or state */ I18N::translate('Anguilla'),
6675
+            'ALA' => /* I18N: Name of a country or state */ I18N::translate('Aland Islands'),
6676
+            'ALB' => /* I18N: Name of a country or state */ I18N::translate('Albania'),
6677
+            'AND' => /* I18N: Name of a country or state */ I18N::translate('Andorra'),
6678
+            'ARE' => /* I18N: Name of a country or state */ I18N::translate('United Arab Emirates'),
6679
+            'ARG' => /* I18N: Name of a country or state */ I18N::translate('Argentina'),
6680
+            'ARM' => /* I18N: Name of a country or state */ I18N::translate('Armenia'),
6681
+            'ASM' => /* I18N: Name of a country or state */ I18N::translate('American Samoa'),
6682
+            'ATA' => /* I18N: Name of a country or state */ I18N::translate('Antarctica'),
6683
+            'ATF' => /* I18N: Name of a country or state */ I18N::translate('French Southern Territories'),
6684
+            'ATG' => /* I18N: Name of a country or state */ I18N::translate('Antigua and Barbuda'),
6685
+            'AUS' => /* I18N: Name of a country or state */ I18N::translate('Australia'),
6686
+            'AUT' => /* I18N: Name of a country or state */ I18N::translate('Austria'),
6687
+            'AZE' => /* I18N: Name of a country or state */ I18N::translate('Azerbaijan'),
6688
+            'AZR' => /* I18N: Name of a country or state */ I18N::translate('Azores'),
6689
+            'BDI' => /* I18N: Name of a country or state */ I18N::translate('Burundi'),
6690
+            'BEL' => /* I18N: Name of a country or state */ I18N::translate('Belgium'),
6691
+            'BEN' => /* I18N: Name of a country or state */ I18N::translate('Benin'),
6692
+            // BES => Bonaire, Sint Eustatius and Saba
6693
+            'BFA' => /* I18N: Name of a country or state */ I18N::translate('Burkina Faso'),
6694
+            'BGD' => /* I18N: Name of a country or state */ I18N::translate('Bangladesh'),
6695
+            'BGR' => /* I18N: Name of a country or state */ I18N::translate('Bulgaria'),
6696
+            'BHR' => /* I18N: Name of a country or state */ I18N::translate('Bahrain'),
6697
+            'BHS' => /* I18N: Name of a country or state */ I18N::translate('Bahamas'),
6698
+            'BIH' => /* I18N: Name of a country or state */ I18N::translate('Bosnia and Herzegovina'),
6699
+            // BLM => Saint Barthélemy
6700
+            'BLR' => /* I18N: Name of a country or state */ I18N::translate('Belarus'),
6701
+            'BLZ' => /* I18N: Name of a country or state */ I18N::translate('Belize'),
6702
+            'BMU' => /* I18N: Name of a country or state */ I18N::translate('Bermuda'),
6703
+            'BOL' => /* I18N: Name of a country or state */ I18N::translate('Bolivia'),
6704
+            'BRA' => /* I18N: Name of a country or state */ I18N::translate('Brazil'),
6705
+            'BRB' => /* I18N: Name of a country or state */ I18N::translate('Barbados'),
6706
+            'BRN' => /* I18N: Name of a country or state */ I18N::translate('Brunei Darussalam'),
6707
+            'BTN' => /* I18N: Name of a country or state */ I18N::translate('Bhutan'),
6708
+            'BVT' => /* I18N: Name of a country or state */ I18N::translate('Bouvet Island'),
6709
+            'BWA' => /* I18N: Name of a country or state */ I18N::translate('Botswana'),
6710
+            'CAF' => /* I18N: Name of a country or state */ I18N::translate('Central African Republic'),
6711
+            'CAN' => /* I18N: Name of a country or state */ I18N::translate('Canada'),
6712
+            'CCK' => /* I18N: Name of a country or state */ I18N::translate('Cocos (Keeling) Islands'),
6713
+            'CHE' => /* I18N: Name of a country or state */ I18N::translate('Switzerland'),
6714
+            'CHL' => /* I18N: Name of a country or state */ I18N::translate('Chile'),
6715
+            'CHN' => /* I18N: Name of a country or state */ I18N::translate('China'),
6716
+            'CIV' => /* I18N: Name of a country or state */ I18N::translate('Cote d’Ivoire'),
6717
+            'CMR' => /* I18N: Name of a country or state */ I18N::translate('Cameroon'),
6718
+            'COD' => /* I18N: Name of a country or state */ I18N::translate('Democratic Republic of the Congo'),
6719
+            'COG' => /* I18N: Name of a country or state */ I18N::translate('Republic of the Congo'),
6720
+            'COK' => /* I18N: Name of a country or state */ I18N::translate('Cook Islands'),
6721
+            'COL' => /* I18N: Name of a country or state */ I18N::translate('Colombia'),
6722
+            'COM' => /* I18N: Name of a country or state */ I18N::translate('Comoros'),
6723
+            'CPV' => /* I18N: Name of a country or state */ I18N::translate('Cape Verde'),
6724
+            'CRI' => /* I18N: Name of a country or state */ I18N::translate('Costa Rica'),
6725
+            'CUB' => /* I18N: Name of a country or state */ I18N::translate('Cuba'),
6726
+            // CUW => Curaçao
6727
+            'CXR' => /* I18N: Name of a country or state */ I18N::translate('Christmas Island'),
6728
+            'CYM' => /* I18N: Name of a country or state */ I18N::translate('Cayman Islands'),
6729
+            'CYP' => /* I18N: Name of a country or state */ I18N::translate('Cyprus'),
6730
+            'CZE' => /* I18N: Name of a country or state */ I18N::translate('Czech Republic'),
6731
+            'DEU' => /* I18N: Name of a country or state */ I18N::translate('Germany'),
6732
+            'DJI' => /* I18N: Name of a country or state */ I18N::translate('Djibouti'),
6733
+            'DMA' => /* I18N: Name of a country or state */ I18N::translate('Dominica'),
6734
+            'DNK' => /* I18N: Name of a country or state */ I18N::translate('Denmark'),
6735
+            'DOM' => /* I18N: Name of a country or state */ I18N::translate('Dominican Republic'),
6736
+            'DZA' => /* I18N: Name of a country or state */ I18N::translate('Algeria'),
6737
+            'ECU' => /* I18N: Name of a country or state */ I18N::translate('Ecuador'),
6738
+            'EGY' => /* I18N: Name of a country or state */ I18N::translate('Egypt'),
6739
+            'ENG' => /* I18N: Name of a country or state */ I18N::translate('England'),
6740
+            'ERI' => /* I18N: Name of a country or state */ I18N::translate('Eritrea'),
6741
+            'ESH' => /* I18N: Name of a country or state */ I18N::translate('Western Sahara'),
6742
+            'ESP' => /* I18N: Name of a country or state */ I18N::translate('Spain'),
6743
+            'EST' => /* I18N: Name of a country or state */ I18N::translate('Estonia'),
6744
+            'ETH' => /* I18N: Name of a country or state */ I18N::translate('Ethiopia'),
6745
+            'FIN' => /* I18N: Name of a country or state */ I18N::translate('Finland'),
6746
+            'FJI' => /* I18N: Name of a country or state */ I18N::translate('Fiji'),
6747
+            'FLD' => /* I18N: Name of a country or state */ I18N::translate('Flanders'),
6748
+            'FLK' => /* I18N: Name of a country or state */ I18N::translate('Falkland Islands'),
6749
+            'FRA' => /* I18N: Name of a country or state */ I18N::translate('France'),
6750
+            'FRO' => /* I18N: Name of a country or state */ I18N::translate('Faroe Islands'),
6751
+            'FSM' => /* I18N: Name of a country or state */ I18N::translate('Micronesia'),
6752
+            'GAB' => /* I18N: Name of a country or state */ I18N::translate('Gabon'),
6753
+            'GBR' => /* I18N: Name of a country or state */ I18N::translate('United Kingdom'),
6754
+            'GEO' => /* I18N: Name of a country or state */ I18N::translate('Georgia'),
6755
+            'GGY' => /* I18N: Name of a country or state */ I18N::translate('Guernsey'),
6756
+            'GHA' => /* I18N: Name of a country or state */ I18N::translate('Ghana'),
6757
+            'GIB' => /* I18N: Name of a country or state */ I18N::translate('Gibraltar'),
6758
+            'GIN' => /* I18N: Name of a country or state */ I18N::translate('Guinea'),
6759
+            'GLP' => /* I18N: Name of a country or state */ I18N::translate('Guadeloupe'),
6760
+            'GMB' => /* I18N: Name of a country or state */ I18N::translate('Gambia'),
6761
+            'GNB' => /* I18N: Name of a country or state */ I18N::translate('Guinea-Bissau'),
6762
+            'GNQ' => /* I18N: Name of a country or state */ I18N::translate('Equatorial Guinea'),
6763
+            'GRC' => /* I18N: Name of a country or state */ I18N::translate('Greece'),
6764
+            'GRD' => /* I18N: Name of a country or state */ I18N::translate('Grenada'),
6765
+            'GRL' => /* I18N: Name of a country or state */ I18N::translate('Greenland'),
6766
+            'GTM' => /* I18N: Name of a country or state */ I18N::translate('Guatemala'),
6767
+            'GUF' => /* I18N: Name of a country or state */ I18N::translate('French Guiana'),
6768
+            'GUM' => /* I18N: Name of a country or state */ I18N::translate('Guam'),
6769
+            'GUY' => /* I18N: Name of a country or state */ I18N::translate('Guyana'),
6770
+            'HKG' => /* I18N: Name of a country or state */ I18N::translate('Hong Kong'),
6771
+            'HMD' => /* I18N: Name of a country or state */ I18N::translate('Heard Island and McDonald Islands'),
6772
+            'HND' => /* I18N: Name of a country or state */ I18N::translate('Honduras'),
6773
+            'HRV' => /* I18N: Name of a country or state */ I18N::translate('Croatia'),
6774
+            'HTI' => /* I18N: Name of a country or state */ I18N::translate('Haiti'),
6775
+            'HUN' => /* I18N: Name of a country or state */ I18N::translate('Hungary'),
6776
+            'IDN' => /* I18N: Name of a country or state */ I18N::translate('Indonesia'),
6777
+            'IND' => /* I18N: Name of a country or state */ I18N::translate('India'),
6778
+            'IOM' => /* I18N: Name of a country or state */ I18N::translate('Isle of Man'),
6779
+            'IOT' => /* I18N: Name of a country or state */ I18N::translate('British Indian Ocean Territory'),
6780
+            'IRL' => /* I18N: Name of a country or state */ I18N::translate('Ireland'),
6781
+            'IRN' => /* I18N: Name of a country or state */ I18N::translate('Iran'),
6782
+            'IRQ' => /* I18N: Name of a country or state */ I18N::translate('Iraq'),
6783
+            'ISL' => /* I18N: Name of a country or state */ I18N::translate('Iceland'),
6784
+            'ISR' => /* I18N: Name of a country or state */ I18N::translate('Israel'),
6785
+            'ITA' => /* I18N: Name of a country or state */ I18N::translate('Italy'),
6786
+            'JAM' => /* I18N: Name of a country or state */ I18N::translate('Jamaica'),
6787
+            //'JEY' => Jersey
6788
+            'JOR' => /* I18N: Name of a country or state */ I18N::translate('Jordan'),
6789
+            'JPN' => /* I18N: Name of a country or state */ I18N::translate('Japan'),
6790
+            'KAZ' => /* I18N: Name of a country or state */ I18N::translate('Kazakhstan'),
6791
+            'KEN' => /* I18N: Name of a country or state */ I18N::translate('Kenya'),
6792
+            'KGZ' => /* I18N: Name of a country or state */ I18N::translate('Kyrgyzstan'),
6793
+            'KHM' => /* I18N: Name of a country or state */ I18N::translate('Cambodia'),
6794
+            'KIR' => /* I18N: Name of a country or state */ I18N::translate('Kiribati'),
6795
+            'KNA' => /* I18N: Name of a country or state */ I18N::translate('Saint Kitts and Nevis'),
6796
+            'KOR' => /* I18N: Name of a country or state */ I18N::translate('Korea'),
6797
+            'KWT' => /* I18N: Name of a country or state */ I18N::translate('Kuwait'),
6798
+            'LAO' => /* I18N: Name of a country or state */ I18N::translate('Laos'),
6799
+            'LBN' => /* I18N: Name of a country or state */ I18N::translate('Lebanon'),
6800
+            'LBR' => /* I18N: Name of a country or state */ I18N::translate('Liberia'),
6801
+            'LBY' => /* I18N: Name of a country or state */ I18N::translate('Libya'),
6802
+            'LCA' => /* I18N: Name of a country or state */ I18N::translate('Saint Lucia'),
6803
+            'LIE' => /* I18N: Name of a country or state */ I18N::translate('Liechtenstein'),
6804
+            'LKA' => /* I18N: Name of a country or state */ I18N::translate('Sri Lanka'),
6805
+            'LSO' => /* I18N: Name of a country or state */ I18N::translate('Lesotho'),
6806
+            'LTU' => /* I18N: Name of a country or state */ I18N::translate('Lithuania'),
6807
+            'LUX' => /* I18N: Name of a country or state */ I18N::translate('Luxembourg'),
6808
+            'LVA' => /* I18N: Name of a country or state */ I18N::translate('Latvia'),
6809
+            'MAC' => /* I18N: Name of a country or state */ I18N::translate('Macau'),
6810
+            // MAF => Saint Martin
6811
+            'MAR' => /* I18N: Name of a country or state */ I18N::translate('Morocco'),
6812
+            'MCO' => /* I18N: Name of a country or state */ I18N::translate('Monaco'),
6813
+            'MDA' => /* I18N: Name of a country or state */ I18N::translate('Moldova'),
6814
+            'MDG' => /* I18N: Name of a country or state */ I18N::translate('Madagascar'),
6815
+            'MDV' => /* I18N: Name of a country or state */ I18N::translate('Maldives'),
6816
+            'MEX' => /* I18N: Name of a country or state */ I18N::translate('Mexico'),
6817
+            'MHL' => /* I18N: Name of a country or state */ I18N::translate('Marshall Islands'),
6818
+            'MKD' => /* I18N: Name of a country or state */ I18N::translate('Macedonia'),
6819
+            'MLI' => /* I18N: Name of a country or state */ I18N::translate('Mali'),
6820
+            'MLT' => /* I18N: Name of a country or state */ I18N::translate('Malta'),
6821
+            'MMR' => /* I18N: Name of a country or state */ I18N::translate('Myanmar'),
6822
+            'MNG' => /* I18N: Name of a country or state */ I18N::translate('Mongolia'),
6823
+            'MNP' => /* I18N: Name of a country or state */ I18N::translate('Northern Mariana Islands'),
6824
+            'MNT' => /* I18N: Name of a country or state */ I18N::translate('Montenegro'),
6825
+            'MOZ' => /* I18N: Name of a country or state */ I18N::translate('Mozambique'),
6826
+            'MRT' => /* I18N: Name of a country or state */ I18N::translate('Mauritania'),
6827
+            'MSR' => /* I18N: Name of a country or state */ I18N::translate('Montserrat'),
6828
+            'MTQ' => /* I18N: Name of a country or state */ I18N::translate('Martinique'),
6829
+            'MUS' => /* I18N: Name of a country or state */ I18N::translate('Mauritius'),
6830
+            'MWI' => /* I18N: Name of a country or state */ I18N::translate('Malawi'),
6831
+            'MYS' => /* I18N: Name of a country or state */ I18N::translate('Malaysia'),
6832
+            'MYT' => /* I18N: Name of a country or state */ I18N::translate('Mayotte'),
6833
+            'NAM' => /* I18N: Name of a country or state */ I18N::translate('Namibia'),
6834
+            'NCL' => /* I18N: Name of a country or state */ I18N::translate('New Caledonia'),
6835
+            'NER' => /* I18N: Name of a country or state */ I18N::translate('Niger'),
6836
+            'NFK' => /* I18N: Name of a country or state */ I18N::translate('Norfolk Island'),
6837
+            'NGA' => /* I18N: Name of a country or state */ I18N::translate('Nigeria'),
6838
+            'NIC' => /* I18N: Name of a country or state */ I18N::translate('Nicaragua'),
6839
+            'NIR' => /* I18N: Name of a country or state */ I18N::translate('Northern Ireland'),
6840
+            'NIU' => /* I18N: Name of a country or state */ I18N::translate('Niue'),
6841
+            'NLD' => /* I18N: Name of a country or state */ I18N::translate('Netherlands'),
6842
+            'NOR' => /* I18N: Name of a country or state */ I18N::translate('Norway'),
6843
+            'NPL' => /* I18N: Name of a country or state */ I18N::translate('Nepal'),
6844
+            'NRU' => /* I18N: Name of a country or state */ I18N::translate('Nauru'),
6845
+            'NZL' => /* I18N: Name of a country or state */ I18N::translate('New Zealand'),
6846
+            'OMN' => /* I18N: Name of a country or state */ I18N::translate('Oman'),
6847
+            'PAK' => /* I18N: Name of a country or state */ I18N::translate('Pakistan'),
6848
+            'PAN' => /* I18N: Name of a country or state */ I18N::translate('Panama'),
6849
+            'PCN' => /* I18N: Name of a country or state */ I18N::translate('Pitcairn'),
6850
+            'PER' => /* I18N: Name of a country or state */ I18N::translate('Peru'),
6851
+            'PHL' => /* I18N: Name of a country or state */ I18N::translate('Philippines'),
6852
+            'PLW' => /* I18N: Name of a country or state */ I18N::translate('Palau'),
6853
+            'PNG' => /* I18N: Name of a country or state */ I18N::translate('Papua New Guinea'),
6854
+            'POL' => /* I18N: Name of a country or state */ I18N::translate('Poland'),
6855
+            'PRI' => /* I18N: Name of a country or state */ I18N::translate('Puerto Rico'),
6856
+            'PRK' => /* I18N: Name of a country or state */ I18N::translate('North Korea'),
6857
+            'PRT' => /* I18N: Name of a country or state */ I18N::translate('Portugal'),
6858
+            'PRY' => /* I18N: Name of a country or state */ I18N::translate('Paraguay'),
6859
+            'PSE' => /* I18N: Name of a country or state */ I18N::translate('Occupied Palestinian Territory'),
6860
+            'PYF' => /* I18N: Name of a country or state */ I18N::translate('French Polynesia'),
6861
+            'QAT' => /* I18N: Name of a country or state */ I18N::translate('Qatar'),
6862
+            'REU' => /* I18N: Name of a country or state */ I18N::translate('Reunion'),
6863
+            'ROM' => /* I18N: Name of a country or state */ I18N::translate('Romania'),
6864
+            'RUS' => /* I18N: Name of a country or state */ I18N::translate('Russia'),
6865
+            'RWA' => /* I18N: Name of a country or state */ I18N::translate('Rwanda'),
6866
+            'SAU' => /* I18N: Name of a country or state */ I18N::translate('Saudi Arabia'),
6867
+            'SCT' => /* I18N: Name of a country or state */ I18N::translate('Scotland'),
6868
+            'SDN' => /* I18N: Name of a country or state */ I18N::translate('Sudan'),
6869
+            'SEA' => /* I18N: Name of a country or state */ I18N::translate('At sea'),
6870
+            'SEN' => /* I18N: Name of a country or state */ I18N::translate('Senegal'),
6871
+            'SER' => /* I18N: Name of a country or state */ I18N::translate('Serbia'),
6872
+            'SGP' => /* I18N: Name of a country or state */ I18N::translate('Singapore'),
6873
+            'SGS' => /* I18N: Name of a country or state */ I18N::translate('South Georgia and the South Sandwich Islands'),
6874
+            'SHN' => /* I18N: Name of a country or state */ I18N::translate('Saint Helena'),
6875
+            'SJM' => /* I18N: Name of a country or state */ I18N::translate('Svalbard and Jan Mayen'),
6876
+            'SLB' => /* I18N: Name of a country or state */ I18N::translate('Solomon Islands'),
6877
+            'SLE' => /* I18N: Name of a country or state */ I18N::translate('Sierra Leone'),
6878
+            'SLV' => /* I18N: Name of a country or state */ I18N::translate('El Salvador'),
6879
+            'SMR' => /* I18N: Name of a country or state */ I18N::translate('San Marino'),
6880
+            'SOM' => /* I18N: Name of a country or state */ I18N::translate('Somalia'),
6881
+            'SPM' => /* I18N: Name of a country or state */ I18N::translate('Saint Pierre and Miquelon'),
6882
+            'SSD' => /* I18N: Name of a country or state */ I18N::translate('South Sudan'),
6883
+            'STP' => /* I18N: Name of a country or state */ I18N::translate('Sao Tome and Principe'),
6884
+            'SUR' => /* I18N: Name of a country or state */ I18N::translate('Suriname'),
6885
+            'SVK' => /* I18N: Name of a country or state */ I18N::translate('Slovakia'),
6886
+            'SVN' => /* I18N: Name of a country or state */ I18N::translate('Slovenia'),
6887
+            'SWE' => /* I18N: Name of a country or state */ I18N::translate('Sweden'),
6888
+            'SWZ' => /* I18N: Name of a country or state */ I18N::translate('Swaziland'),
6889
+            // SXM => Sint Maarten
6890
+            'SYC' => /* I18N: Name of a country or state */ I18N::translate('Seychelles'),
6891
+            'SYR' => /* I18N: Name of a country or state */ I18N::translate('Syria'),
6892
+            'TCA' => /* I18N: Name of a country or state */ I18N::translate('Turks and Caicos Islands'),
6893
+            'TCD' => /* I18N: Name of a country or state */ I18N::translate('Chad'),
6894
+            'TGO' => /* I18N: Name of a country or state */ I18N::translate('Togo'),
6895
+            'THA' => /* I18N: Name of a country or state */ I18N::translate('Thailand'),
6896
+            'TJK' => /* I18N: Name of a country or state */ I18N::translate('Tajikistan'),
6897
+            'TKL' => /* I18N: Name of a country or state */ I18N::translate('Tokelau'),
6898
+            'TKM' => /* I18N: Name of a country or state */ I18N::translate('Turkmenistan'),
6899
+            'TLS' => /* I18N: Name of a country or state */ I18N::translate('Timor-Leste'),
6900
+            'TON' => /* I18N: Name of a country or state */ I18N::translate('Tonga'),
6901
+            'TTO' => /* I18N: Name of a country or state */ I18N::translate('Trinidad and Tobago'),
6902
+            'TUN' => /* I18N: Name of a country or state */ I18N::translate('Tunisia'),
6903
+            'TUR' => /* I18N: Name of a country or state */ I18N::translate('Turkey'),
6904
+            'TUV' => /* I18N: Name of a country or state */ I18N::translate('Tuvalu'),
6905
+            'TWN' => /* I18N: Name of a country or state */ I18N::translate('Taiwan'),
6906
+            'TZA' => /* I18N: Name of a country or state */ I18N::translate('Tanzania'),
6907
+            'UGA' => /* I18N: Name of a country or state */ I18N::translate('Uganda'),
6908
+            'UKR' => /* I18N: Name of a country or state */ I18N::translate('Ukraine'),
6909
+            'UMI' => /* I18N: Name of a country or state */ I18N::translate('US Minor Outlying Islands'),
6910
+            'URY' => /* I18N: Name of a country or state */ I18N::translate('Uruguay'),
6911
+            'USA' => /* I18N: Name of a country or state */ I18N::translate('United States'),
6912
+            'UZB' => /* I18N: Name of a country or state */ I18N::translate('Uzbekistan'),
6913
+            'VAT' => /* I18N: Name of a country or state */ I18N::translate('Vatican City'),
6914
+            'VCT' => /* I18N: Name of a country or state */ I18N::translate('Saint Vincent and the Grenadines'),
6915
+            'VEN' => /* I18N: Name of a country or state */ I18N::translate('Venezuela'),
6916
+            'VGB' => /* I18N: Name of a country or state */ I18N::translate('British Virgin Islands'),
6917
+            'VIR' => /* I18N: Name of a country or state */ I18N::translate('US Virgin Islands'),
6918
+            'VNM' => /* I18N: Name of a country or state */ I18N::translate('Vietnam'),
6919
+            'VUT' => /* I18N: Name of a country or state */ I18N::translate('Vanuatu'),
6920
+            'WLF' => /* I18N: Name of a country or state */ I18N::translate('Wallis and Futuna'),
6921
+            'WLS' => /* I18N: Name of a country or state */ I18N::translate('Wales'),
6922
+            'WSM' => /* I18N: Name of a country or state */ I18N::translate('Samoa'),
6923
+            'YEM' => /* I18N: Name of a country or state */ I18N::translate('Yemen'),
6924
+            'ZAF' => /* I18N: Name of a country or state */ I18N::translate('South Africa'),
6925
+            'ZMB' => /* I18N: Name of a country or state */ I18N::translate('Zambia'),
6926
+            'ZWE' => /* I18N: Name of a country or state */ I18N::translate('Zimbabwe'),
6927
+        );
6928
+    }
6929
+
6930
+    /**
6931
+     * Century name, English => 21st, Polish => XXI, etc.
6932
+     *
6933
+     * @param int $century
6934
+     *
6935
+     * @return string
6936
+     */
6937
+    private function centuryName($century) {
6938
+        if ($century < 0) {
6939
+            return str_replace(-$century, self::centuryName(-$century), /* I18N: BCE=Before the Common Era, for Julian years < 0. See http://en.wikipedia.org/wiki/Common_Era */
6940
+                I18N::translate('%s BCE', I18N::number(-$century)));
6941
+        }
6942
+        // The current chart engine (Google charts) can't handle <sup></sup> markup
6943
+        switch ($century) {
6944
+        case 21:
6945
+            return strip_tags(I18N::translateContext('CENTURY', '21st'));
6946
+        case 20:
6947
+            return strip_tags(I18N::translateContext('CENTURY', '20th'));
6948
+        case 19:
6949
+            return strip_tags(I18N::translateContext('CENTURY', '19th'));
6950
+        case 18:
6951
+            return strip_tags(I18N::translateContext('CENTURY', '18th'));
6952
+        case 17:
6953
+            return strip_tags(I18N::translateContext('CENTURY', '17th'));
6954
+        case 16:
6955
+            return strip_tags(I18N::translateContext('CENTURY', '16th'));
6956
+        case 15:
6957
+            return strip_tags(I18N::translateContext('CENTURY', '15th'));
6958
+        case 14:
6959
+            return strip_tags(I18N::translateContext('CENTURY', '14th'));
6960
+        case 13:
6961
+            return strip_tags(I18N::translateContext('CENTURY', '13th'));
6962
+        case 12:
6963
+            return strip_tags(I18N::translateContext('CENTURY', '12th'));
6964
+        case 11:
6965
+            return strip_tags(I18N::translateContext('CENTURY', '11th'));
6966
+        case 10:
6967
+            return strip_tags(I18N::translateContext('CENTURY', '10th'));
6968
+        case  9:
6969
+            return strip_tags(I18N::translateContext('CENTURY', '9th'));
6970
+        case  8:
6971
+            return strip_tags(I18N::translateContext('CENTURY', '8th'));
6972
+        case  7:
6973
+            return strip_tags(I18N::translateContext('CENTURY', '7th'));
6974
+        case  6:
6975
+            return strip_tags(I18N::translateContext('CENTURY', '6th'));
6976
+        case  5:
6977
+            return strip_tags(I18N::translateContext('CENTURY', '5th'));
6978
+        case  4:
6979
+            return strip_tags(I18N::translateContext('CENTURY', '4th'));
6980
+        case  3:
6981
+            return strip_tags(I18N::translateContext('CENTURY', '3rd'));
6982
+        case  2:
6983
+            return strip_tags(I18N::translateContext('CENTURY', '2nd'));
6984
+        case  1:
6985
+            return strip_tags(I18N::translateContext('CENTURY', '1st'));
6986
+        default:
6987
+            return ($century - 1) . '01-' . $century . '00';
6988
+        }
6989
+    }
6990 6990
 }
Please login to merge, or discard this patch.
Switch Indentation   +406 added lines, -406 removed lines patch added patch discarded remove patch
@@ -303,22 +303,22 @@  discard block
 block discarded – undo
303 303
 	 */
304 304
 	private function getPercentage($total, $type) {
305 305
 		switch ($type) {
306
-		case 'individual':
307
-			$type = $this->totalIndividualsQuery();
308
-			break;
309
-		case 'family':
310
-			$type = $this->totalFamiliesQuery();
311
-			break;
312
-		case 'source':
313
-			$type = $this->totalSourcesQuery();
314
-			break;
315
-		case 'note':
316
-			$type = $this->totalNotesQuery();
317
-			break;
318
-		case 'all':
319
-		default:
320
-			$type = $this->totalIndividualsQuery() + $this->totalFamiliesQuery() + $this->totalSourcesQuery();
321
-			break;
306
+		    case 'individual':
307
+			    $type = $this->totalIndividualsQuery();
308
+			    break;
309
+		    case 'family':
310
+			    $type = $this->totalFamiliesQuery();
311
+			    break;
312
+		    case 'source':
313
+			    $type = $this->totalSourcesQuery();
314
+			    break;
315
+		    case 'note':
316
+			    $type = $this->totalNotesQuery();
317
+			    break;
318
+		    case 'all':
319
+		    default:
320
+			    $type = $this->totalIndividualsQuery() + $this->totalFamiliesQuery() + $this->totalSourcesQuery();
321
+			    break;
322 322
 		}
323 323
 		if ($type == 0) {
324 324
 			return I18N::percentage(0, 1);
@@ -1444,29 +1444,29 @@  discard block
 block discarded – undo
1444 1444
 		$row    = $rows[0];
1445 1445
 		$record = GedcomRecord::getInstance($row['d_gid'], $this->tree);
1446 1446
 		switch ($type) {
1447
-		default:
1448
-		case 'full':
1449
-			if ($record->canShow()) {
1450
-				$result = $record->formatList('span', false, $record->getFullName());
1451
-			} else {
1452
-				$result = I18N::translate('This information is private and cannot be shown.');
1453
-			}
1454
-			break;
1455
-		case 'year':
1456
-			$date   = new Date($row['d_type'] . ' ' . $row['d_year']);
1457
-			$result = $date->display();
1458
-			break;
1459
-		case 'name':
1460
-			$result = "<a href=\"" . $record->getHtmlUrl() . "\">" . $record->getFullName() . "</a>";
1461
-			break;
1462
-		case 'place':
1463
-			$fact = GedcomRecord::getInstance($row['d_gid'], $this->tree)->getFirstFact($row['d_fact']);
1464
-			if ($fact) {
1465
-				$result = FunctionsPrint::formatFactPlace($fact, true, true, true);
1466
-			} else {
1467
-				$result = I18N::translate('Private');
1468
-			}
1469
-			break;
1447
+		    default:
1448
+		    case 'full':
1449
+			    if ($record->canShow()) {
1450
+				    $result = $record->formatList('span', false, $record->getFullName());
1451
+			    } else {
1452
+				    $result = I18N::translate('This information is private and cannot be shown.');
1453
+			    }
1454
+			    break;
1455
+		    case 'year':
1456
+			    $date   = new Date($row['d_type'] . ' ' . $row['d_year']);
1457
+			    $result = $date->display();
1458
+			    break;
1459
+		    case 'name':
1460
+			    $result = "<a href=\"" . $record->getHtmlUrl() . "\">" . $record->getFullName() . "</a>";
1461
+			    break;
1462
+		    case 'place':
1463
+			    $fact = GedcomRecord::getInstance($row['d_gid'], $this->tree)->getFirstFact($row['d_fact']);
1464
+			    if ($fact) {
1465
+				    $result = FunctionsPrint::formatFactPlace($fact, true, true, true);
1466
+			    } else {
1467
+				    $result = I18N::translate('Private');
1468
+			    }
1469
+			    break;
1470 1470
 		}
1471 1471
 
1472 1472
 		return $result;
@@ -1631,96 +1631,96 @@  discard block
 block discarded – undo
1631 1631
 		}
1632 1632
 		I18N::init(WT_LOCALE);
1633 1633
 		switch ($chart_type) {
1634
-		case 'surname_distribution_chart':
1635
-			if ($surname == "") {
1636
-				$surname = $this->getCommonSurname();
1637
-			}
1638
-			$chart_title = I18N::translate('Surname distribution chart') . ': ' . $surname;
1639
-			// Count how many people are events in each country
1640
-			$surn_countries = array();
1641
-			$indis          = QueryName::individuals($this->tree, I18N::strtoupper($surname), '', '', false, false);
1642
-			foreach ($indis as $person) {
1643
-				if (preg_match_all('/^2 PLAC (?:.*, *)*(.*)/m', $person->getGedcom(), $matches)) {
1644
-					// webtrees uses 3 letter country codes and localised country names, but google uses 2 letter codes.
1645
-					foreach ($matches[1] as $country) {
1646
-						if (array_key_exists($country, $country_to_iso3166)) {
1647
-							if (array_key_exists($country_to_iso3166[$country], $surn_countries)) {
1648
-								$surn_countries[$country_to_iso3166[$country]]++;
1649
-							} else {
1650
-								$surn_countries[$country_to_iso3166[$country]] = 1;
1651
-							}
1652
-						}
1653
-					}
1654
-				}
1655
-			};
1656
-			break;
1657
-		case 'birth_distribution_chart':
1658
-			$chart_title = I18N::translate('Birth by country');
1659
-			// Count how many people were born in each country
1660
-			$surn_countries = array();
1661
-			$b_countries    = $this->statsPlaces('INDI', 'BIRT', 0, true);
1662
-			foreach ($b_countries as $place => $count) {
1663
-				$country = $place;
1664
-				if (array_key_exists($country, $country_to_iso3166)) {
1665
-					if (!isset($surn_countries[$country_to_iso3166[$country]])) {
1666
-						$surn_countries[$country_to_iso3166[$country]] = $count;
1667
-					} else {
1668
-						$surn_countries[$country_to_iso3166[$country]] += $count;
1669
-					}
1670
-				}
1671
-			}
1672
-			break;
1673
-		case 'death_distribution_chart':
1674
-			$chart_title = I18N::translate('Death by country');
1675
-			// Count how many people were death in each country
1676
-			$surn_countries = array();
1677
-			$d_countries    = $this->statsPlaces('INDI', 'DEAT', 0, true);
1678
-			foreach ($d_countries as $place => $count) {
1679
-				$country = $place;
1680
-				if (array_key_exists($country, $country_to_iso3166)) {
1681
-					if (!isset($surn_countries[$country_to_iso3166[$country]])) {
1682
-						$surn_countries[$country_to_iso3166[$country]] = $count;
1683
-					} else {
1684
-						$surn_countries[$country_to_iso3166[$country]] += $count;
1685
-					}
1686
-				}
1687
-			}
1688
-			break;
1689
-		case 'marriage_distribution_chart':
1690
-			$chart_title = I18N::translate('Marriage by country');
1691
-			// Count how many families got marriage in each country
1692
-			$surn_countries = array();
1693
-			$m_countries    = $this->statsPlaces('FAM');
1694
-			// webtrees uses 3 letter country codes and localised country names, but google uses 2 letter codes.
1695
-			foreach ($m_countries as $place) {
1696
-				$country = $place['country'];
1697
-				if (array_key_exists($country, $country_to_iso3166)) {
1698
-					if (!isset($surn_countries[$country_to_iso3166[$country]])) {
1699
-						$surn_countries[$country_to_iso3166[$country]] = $place['tot'];
1700
-					} else {
1701
-						$surn_countries[$country_to_iso3166[$country]] += $place['tot'];
1702
-					}
1703
-				}
1704
-			}
1705
-			break;
1706
-		case 'indi_distribution_chart':
1707
-		default:
1708
-			$chart_title = I18N::translate('Individual distribution chart');
1709
-			// Count how many people have events in each country
1710
-			$surn_countries = array();
1711
-			$a_countries    = $this->statsPlaces('INDI');
1712
-			// webtrees uses 3 letter country codes and localised country names, but google uses 2 letter codes.
1713
-			foreach ($a_countries as $place) {
1714
-				$country = $place['country'];
1715
-				if (array_key_exists($country, $country_to_iso3166)) {
1716
-					if (!isset($surn_countries[$country_to_iso3166[$country]])) {
1717
-						$surn_countries[$country_to_iso3166[$country]] = $place['tot'];
1718
-					} else {
1719
-						$surn_countries[$country_to_iso3166[$country]] += $place['tot'];
1720
-					}
1721
-				}
1722
-			}
1723
-			break;
1634
+		    case 'surname_distribution_chart':
1635
+			    if ($surname == "") {
1636
+				    $surname = $this->getCommonSurname();
1637
+			    }
1638
+			    $chart_title = I18N::translate('Surname distribution chart') . ': ' . $surname;
1639
+			    // Count how many people are events in each country
1640
+			    $surn_countries = array();
1641
+			    $indis          = QueryName::individuals($this->tree, I18N::strtoupper($surname), '', '', false, false);
1642
+			    foreach ($indis as $person) {
1643
+				    if (preg_match_all('/^2 PLAC (?:.*, *)*(.*)/m', $person->getGedcom(), $matches)) {
1644
+					    // webtrees uses 3 letter country codes and localised country names, but google uses 2 letter codes.
1645
+					    foreach ($matches[1] as $country) {
1646
+						    if (array_key_exists($country, $country_to_iso3166)) {
1647
+							    if (array_key_exists($country_to_iso3166[$country], $surn_countries)) {
1648
+								    $surn_countries[$country_to_iso3166[$country]]++;
1649
+							    } else {
1650
+								    $surn_countries[$country_to_iso3166[$country]] = 1;
1651
+							    }
1652
+						    }
1653
+					    }
1654
+				    }
1655
+			    };
1656
+			    break;
1657
+		    case 'birth_distribution_chart':
1658
+			    $chart_title = I18N::translate('Birth by country');
1659
+			    // Count how many people were born in each country
1660
+			    $surn_countries = array();
1661
+			    $b_countries    = $this->statsPlaces('INDI', 'BIRT', 0, true);
1662
+			    foreach ($b_countries as $place => $count) {
1663
+				    $country = $place;
1664
+				    if (array_key_exists($country, $country_to_iso3166)) {
1665
+					    if (!isset($surn_countries[$country_to_iso3166[$country]])) {
1666
+						    $surn_countries[$country_to_iso3166[$country]] = $count;
1667
+					    } else {
1668
+						    $surn_countries[$country_to_iso3166[$country]] += $count;
1669
+					    }
1670
+				    }
1671
+			    }
1672
+			    break;
1673
+		    case 'death_distribution_chart':
1674
+			    $chart_title = I18N::translate('Death by country');
1675
+			    // Count how many people were death in each country
1676
+			    $surn_countries = array();
1677
+			    $d_countries    = $this->statsPlaces('INDI', 'DEAT', 0, true);
1678
+			    foreach ($d_countries as $place => $count) {
1679
+				    $country = $place;
1680
+				    if (array_key_exists($country, $country_to_iso3166)) {
1681
+					    if (!isset($surn_countries[$country_to_iso3166[$country]])) {
1682
+						    $surn_countries[$country_to_iso3166[$country]] = $count;
1683
+					    } else {
1684
+						    $surn_countries[$country_to_iso3166[$country]] += $count;
1685
+					    }
1686
+				    }
1687
+			    }
1688
+			    break;
1689
+		    case 'marriage_distribution_chart':
1690
+			    $chart_title = I18N::translate('Marriage by country');
1691
+			    // Count how many families got marriage in each country
1692
+			    $surn_countries = array();
1693
+			    $m_countries    = $this->statsPlaces('FAM');
1694
+			    // webtrees uses 3 letter country codes and localised country names, but google uses 2 letter codes.
1695
+			    foreach ($m_countries as $place) {
1696
+				    $country = $place['country'];
1697
+				    if (array_key_exists($country, $country_to_iso3166)) {
1698
+					    if (!isset($surn_countries[$country_to_iso3166[$country]])) {
1699
+						    $surn_countries[$country_to_iso3166[$country]] = $place['tot'];
1700
+					    } else {
1701
+						    $surn_countries[$country_to_iso3166[$country]] += $place['tot'];
1702
+					    }
1703
+				    }
1704
+			    }
1705
+			    break;
1706
+		    case 'indi_distribution_chart':
1707
+		    default:
1708
+			    $chart_title = I18N::translate('Individual distribution chart');
1709
+			    // Count how many people have events in each country
1710
+			    $surn_countries = array();
1711
+			    $a_countries    = $this->statsPlaces('INDI');
1712
+			    // webtrees uses 3 letter country codes and localised country names, but google uses 2 letter codes.
1713
+			    foreach ($a_countries as $place) {
1714
+				    $country = $place['country'];
1715
+				    if (array_key_exists($country, $country_to_iso3166)) {
1716
+					    if (!isset($surn_countries[$country_to_iso3166[$country]])) {
1717
+						    $surn_countries[$country_to_iso3166[$country]] = $place['tot'];
1718
+					    } else {
1719
+						    $surn_countries[$country_to_iso3166[$country]] += $place['tot'];
1720
+					    }
1721
+				    }
1722
+			    }
1723
+			    break;
1724 1724
 		}
1725 1725
 		$chart_url = "https://chart.googleapis.com/chart?cht=t&amp;chtm=" . $chart_shows;
1726 1726
 		$chart_url .= "&amp;chco=" . $WT_STATS_CHART_COLOR1 . "," . $WT_STATS_CHART_COLOR3 . "," . $WT_STATS_CHART_COLOR2; // country colours
@@ -2259,20 +2259,20 @@  discard block
 block discarded – undo
2259 2259
 		$row    = $rows[0];
2260 2260
 		$person = Individual::getInstance($row['id'], $this->tree);
2261 2261
 		switch ($type) {
2262
-		default:
2263
-		case 'full':
2264
-			if ($person->canShowName()) {
2265
-				$result = $person->formatList('span', false, $person->getFullName());
2266
-			} else {
2267
-				$result = I18N::translate('This information is private and cannot be shown.');
2268
-			}
2269
-			break;
2270
-		case 'age':
2271
-			$result = I18N::number((int) ($row['age'] / 365.25));
2272
-			break;
2273
-		case 'name':
2274
-			$result = "<a href=\"" . $person->getHtmlUrl() . "\">" . $person->getFullName() . "</a>";
2275
-			break;
2262
+		    default:
2263
+		    case 'full':
2264
+			    if ($person->canShowName()) {
2265
+				    $result = $person->formatList('span', false, $person->getFullName());
2266
+			    } else {
2267
+				    $result = I18N::translate('This information is private and cannot be shown.');
2268
+			    }
2269
+			    break;
2270
+		    case 'age':
2271
+			    $result = I18N::number((int) ($row['age'] / 365.25));
2272
+			    break;
2273
+		    case 'name':
2274
+			    $result = "<a href=\"" . $person->getHtmlUrl() . "\">" . $person->getFullName() . "</a>";
2275
+			    break;
2276 2276
 		}
2277 2277
 
2278 2278
 		return $result;
@@ -2942,36 +2942,36 @@  discard block
 block discarded – undo
2942 2942
 		$row    = $rows[0];
2943 2943
 		$record = GedcomRecord::getInstance($row['id'], $this->tree);
2944 2944
 		switch ($type) {
2945
-		default:
2946
-		case 'full':
2947
-			if ($record->canShow()) {
2948
-				$result = $record->formatList('span', false, $record->getFullName());
2949
-			} else {
2950
-				$result = I18N::translate('This information is private and cannot be shown.');
2951
-			}
2952
-			break;
2953
-		case 'year':
2954
-			$date   = new Date($row['type'] . ' ' . $row['year']);
2955
-			$result = $date->display();
2956
-			break;
2957
-		case 'type':
2958
-			if (isset($eventTypes[$row['fact']])) {
2959
-				$result = $eventTypes[$row['fact']];
2960
-			} else {
2961
-				$result = GedcomTag::getLabel($row['fact']);
2962
-			}
2963
-			break;
2964
-		case 'name':
2965
-			$result = "<a href=\"" . $record->getHtmlUrl() . "\">" . $record->getFullName() . "</a>";
2966
-			break;
2967
-		case 'place':
2968
-			$fact = $record->getFirstFact($row['fact']);
2969
-			if ($fact) {
2970
-				$result = FunctionsPrint::formatFactPlace($fact, true, true, true);
2971
-			} else {
2972
-				$result = I18N::translate('Private');
2973
-			}
2974
-			break;
2945
+		    default:
2946
+		    case 'full':
2947
+			    if ($record->canShow()) {
2948
+				    $result = $record->formatList('span', false, $record->getFullName());
2949
+			    } else {
2950
+				    $result = I18N::translate('This information is private and cannot be shown.');
2951
+			    }
2952
+			    break;
2953
+		    case 'year':
2954
+			    $date   = new Date($row['type'] . ' ' . $row['year']);
2955
+			    $result = $date->display();
2956
+			    break;
2957
+		    case 'type':
2958
+			    if (isset($eventTypes[$row['fact']])) {
2959
+				    $result = $eventTypes[$row['fact']];
2960
+			    } else {
2961
+				    $result = GedcomTag::getLabel($row['fact']);
2962
+			    }
2963
+			    break;
2964
+		    case 'name':
2965
+			    $result = "<a href=\"" . $record->getHtmlUrl() . "\">" . $record->getFullName() . "</a>";
2966
+			    break;
2967
+		    case 'place':
2968
+			    $fact = $record->getFirstFact($row['fact']);
2969
+			    if ($fact) {
2970
+				    $result = FunctionsPrint::formatFactPlace($fact, true, true, true);
2971
+			    } else {
2972
+				    $result = I18N::translate('Private');
2973
+			    }
2974
+			    break;
2975 2975
 		}
2976 2976
 
2977 2977
 		return $result;
@@ -3116,32 +3116,32 @@  discard block
 block discarded – undo
3116 3116
 			$person = Individual::getInstance($row['i_id'], $this->tree);
3117 3117
 		}
3118 3118
 		switch ($type) {
3119
-		default:
3120
-		case 'full':
3121
-			if ($family->canShow()) {
3122
-				$result = $family->formatList('span', false, $person->getFullName());
3123
-			} else {
3124
-				$result = I18N::translate('This information is private and cannot be shown.');
3125
-			}
3126
-			break;
3127
-		case 'name':
3128
-			$result = '<a href="' . $family->getHtmlUrl() . '">' . $person->getFullName() . '</a>';
3129
-			break;
3130
-		case 'age':
3131
-			$age = $row['age'];
3132
-			if ($show_years) {
3133
-				if ((int) ($age / 365.25) > 0) {
3134
-					$age = (int) ($age / 365.25) . 'y';
3135
-				} elseif ((int) ($age / 30.4375) > 0) {
3136
-					$age = (int) ($age / 30.4375) . 'm';
3137
-				} else {
3138
-					$age = $age . 'd';
3139
-				}
3140
-				$result = FunctionsDate::getAgeAtEvent($age);
3141
-			} else {
3142
-				$result = I18N::number((int) ($age / 365.25));
3143
-			}
3144
-			break;
3119
+		    default:
3120
+		    case 'full':
3121
+			    if ($family->canShow()) {
3122
+				    $result = $family->formatList('span', false, $person->getFullName());
3123
+			    } else {
3124
+				    $result = I18N::translate('This information is private and cannot be shown.');
3125
+			    }
3126
+			    break;
3127
+		    case 'name':
3128
+			    $result = '<a href="' . $family->getHtmlUrl() . '">' . $person->getFullName() . '</a>';
3129
+			    break;
3130
+		    case 'age':
3131
+			    $age = $row['age'];
3132
+			    if ($show_years) {
3133
+				    if ((int) ($age / 365.25) > 0) {
3134
+					    $age = (int) ($age / 365.25) . 'y';
3135
+				    } elseif ((int) ($age / 30.4375) > 0) {
3136
+					    $age = (int) ($age / 30.4375) . 'm';
3137
+				    } else {
3138
+					    $age = $age . 'd';
3139
+				    }
3140
+				    $result = FunctionsDate::getAgeAtEvent($age);
3141
+			    } else {
3142
+				    $result = I18N::number((int) ($age / 365.25));
3143
+			    }
3144
+			    break;
3145 3145
 		}
3146 3146
 
3147 3147
 		return $result;
@@ -3414,32 +3414,32 @@  discard block
 block discarded – undo
3414 3414
 			$person = Individual::getInstance($row['id'], $this->tree);
3415 3415
 		}
3416 3416
 		switch ($type) {
3417
-		default:
3418
-		case 'full':
3419
-			if ($person->canShow()) {
3420
-				$result = $person->formatList('span', false, $person->getFullName());
3421
-			} else {
3422
-				$result = I18N::translate('This information is private and cannot be shown.');
3423
-			}
3424
-			break;
3425
-		case 'name':
3426
-			$result = '<a href="' . $person->getHtmlUrl() . '">' . $person->getFullName() . '</a>';
3427
-			break;
3428
-		case 'age':
3429
-			$age = $row['age'];
3430
-			if ($show_years) {
3431
-				if ((int) ($age / 365.25) > 0) {
3432
-					$age = (int) ($age / 365.25) . 'y';
3433
-				} elseif ((int) ($age / 30.4375) > 0) {
3434
-					$age = (int) ($age / 30.4375) . 'm';
3435
-				} else {
3436
-					$age = $age . 'd';
3437
-				}
3438
-				$result = FunctionsDate::getAgeAtEvent($age);
3439
-			} else {
3440
-				$result = (int) ($age / 365.25);
3441
-			}
3442
-			break;
3417
+		    default:
3418
+		    case 'full':
3419
+			    if ($person->canShow()) {
3420
+				    $result = $person->formatList('span', false, $person->getFullName());
3421
+			    } else {
3422
+				    $result = I18N::translate('This information is private and cannot be shown.');
3423
+			    }
3424
+			    break;
3425
+		    case 'name':
3426
+			    $result = '<a href="' . $person->getHtmlUrl() . '">' . $person->getFullName() . '</a>';
3427
+			    break;
3428
+		    case 'age':
3429
+			    $age = $row['age'];
3430
+			    if ($show_years) {
3431
+				    if ((int) ($age / 365.25) > 0) {
3432
+					    $age = (int) ($age / 365.25) . 'y';
3433
+				    } elseif ((int) ($age / 30.4375) > 0) {
3434
+					    $age = (int) ($age / 30.4375) . 'm';
3435
+				    } else {
3436
+					    $age = $age . 'd';
3437
+				    }
3438
+				    $result = FunctionsDate::getAgeAtEvent($age);
3439
+			    } else {
3440
+				    $result = (int) ($age / 365.25);
3441
+			    }
3442
+			    break;
3443 3443
 		}
3444 3444
 
3445 3445
 		return $result;
@@ -4397,20 +4397,20 @@  discard block
 block discarded – undo
4397 4397
 		$row    = $rows[0];
4398 4398
 		$family = Family::getInstance($row['id'], $this->tree);
4399 4399
 		switch ($type) {
4400
-		default:
4401
-		case 'full':
4402
-			if ($family->canShow()) {
4403
-				$result = $family->formatList('span', false, $family->getFullName());
4404
-			} else {
4405
-				$result = I18N::translate('This information is private and cannot be shown.');
4406
-			}
4407
-			break;
4408
-		case 'size':
4409
-			$result = I18N::number($row['tot']);
4410
-			break;
4411
-		case 'name':
4412
-			$result = "<a href=\"" . $family->getHtmlUrl() . "\">" . $family->getFullName() . '</a>';
4413
-			break;
4400
+		    default:
4401
+		    case 'full':
4402
+			    if ($family->canShow()) {
4403
+				    $result = $family->formatList('span', false, $family->getFullName());
4404
+			    } else {
4405
+				    $result = I18N::translate('This information is private and cannot be shown.');
4406
+			    }
4407
+			    break;
4408
+		    case 'size':
4409
+			    $result = I18N::number($row['tot']);
4410
+			    break;
4411
+		    case 'name':
4412
+			    $result = "<a href=\"" . $family->getHtmlUrl() . "\">" . $family->getFullName() . '</a>';
4413
+			    break;
4414 4414
 		}
4415 4415
 
4416 4416
 		return $result;
@@ -4689,43 +4689,43 @@  discard block
 block discarded – undo
4689 4689
 			foreach ($rows as $values) {
4690 4690
 				$counts[] = round(100 * $values['total'] / $tot, 0);
4691 4691
 				switch ($values['d_month']) {
4692
-				default:
4693
-				case 'JAN':
4694
-					$values['d_month'] = 1;
4695
-					break;
4696
-				case 'FEB':
4697
-					$values['d_month'] = 2;
4698
-					break;
4699
-				case 'MAR':
4700
-					$values['d_month'] = 3;
4701
-					break;
4702
-				case 'APR':
4703
-					$values['d_month'] = 4;
4704
-					break;
4705
-				case 'MAY':
4706
-					$values['d_month'] = 5;
4707
-					break;
4708
-				case 'JUN':
4709
-					$values['d_month'] = 6;
4710
-					break;
4711
-				case 'JUL':
4712
-					$values['d_month'] = 7;
4713
-					break;
4714
-				case 'AUG':
4715
-					$values['d_month'] = 8;
4716
-					break;
4717
-				case 'SEP':
4718
-					$values['d_month'] = 9;
4719
-					break;
4720
-				case 'OCT':
4721
-					$values['d_month'] = 10;
4722
-					break;
4723
-				case 'NOV':
4724
-					$values['d_month'] = 11;
4725
-					break;
4726
-				case 'DEC':
4727
-					$values['d_month'] = 12;
4728
-					break;
4692
+				    default:
4693
+				    case 'JAN':
4694
+					    $values['d_month'] = 1;
4695
+					    break;
4696
+				    case 'FEB':
4697
+					    $values['d_month'] = 2;
4698
+					    break;
4699
+				    case 'MAR':
4700
+					    $values['d_month'] = 3;
4701
+					    break;
4702
+				    case 'APR':
4703
+					    $values['d_month'] = 4;
4704
+					    break;
4705
+				    case 'MAY':
4706
+					    $values['d_month'] = 5;
4707
+					    break;
4708
+				    case 'JUN':
4709
+					    $values['d_month'] = 6;
4710
+					    break;
4711
+				    case 'JUL':
4712
+					    $values['d_month'] = 7;
4713
+					    break;
4714
+				    case 'AUG':
4715
+					    $values['d_month'] = 8;
4716
+					    break;
4717
+				    case 'SEP':
4718
+					    $values['d_month'] = 9;
4719
+					    break;
4720
+				    case 'OCT':
4721
+					    $values['d_month'] = 10;
4722
+					    break;
4723
+				    case 'NOV':
4724
+					    $values['d_month'] = 11;
4725
+					    break;
4726
+				    case 'DEC':
4727
+					    $values['d_month'] = 12;
4728
+					    break;
4729 4729
 				}
4730 4730
 				$text .= I18N::translate(ucfirst(strtolower(($values['d_month'])))) . ' - ' . $values['total'] . '|';
4731 4731
 			}
@@ -5316,16 +5316,16 @@  discard block
 block discarded – undo
5316 5316
 		}
5317 5317
 
5318 5318
 		switch ($sorting) {
5319
-		default:
5320
-		case 'alpha':
5321
-			uksort($surname_list, '\Fisharebest\Webtrees\I18N::strcasecmp');
5322
-			break;
5323
-		case 'count':
5324
-			asort($surname_list);
5325
-			break;
5326
-		case 'rcount':
5327
-			arsort($surname_list);
5328
-			break;
5319
+		    default:
5320
+		    case 'alpha':
5321
+			    uksort($surname_list, '\Fisharebest\Webtrees\I18N::strcasecmp');
5322
+			    break;
5323
+		    case 'count':
5324
+			    asort($surname_list);
5325
+			    break;
5326
+		    case 'rcount':
5327
+			    arsort($surname_list);
5328
+			    break;
5329 5329
 		}
5330 5330
 
5331 5331
 		// Note that we count/display SPFX SURN, but sort/group under just SURN
@@ -5439,9 +5439,9 @@  discard block
 block discarded – undo
5439 5439
 				}
5440 5440
 			}
5441 5441
 			switch ($SURNAME_TRADITION) {
5442
-			case 'polish':
5443
-				// most common surname should be in male variant (Kowalski, not Kowalska)
5444
-				$top_name = preg_replace(array('/ska$/', '/cka$/', '/dzka$/', '/żka$/'), array('ski', 'cki', 'dzki', 'żki'), $top_name);
5442
+			    case 'polish':
5443
+				    // most common surname should be in male variant (Kowalski, not Kowalska)
5444
+				    $top_name = preg_replace(array('/ska$/', '/cka$/', '/dzka$/', '/żka$/'), array('ski', 'cki', 'dzki', 'żki'), $top_name);
5445 5445
 			}
5446 5446
 			$per = round(100 * $count_per / $tot_indi, 0);
5447 5447
 			$chd .= $this->arrayToExtendedEncoding(array($per));
@@ -5481,19 +5481,19 @@  discard block
 block discarded – undo
5481 5481
 		}
5482 5482
 
5483 5483
 		switch ($sex) {
5484
-		case 'M':
5485
-			$sex_sql = "i_sex='M'";
5486
-			break;
5487
-		case 'F':
5488
-			$sex_sql = "i_sex='F'";
5489
-			break;
5490
-		case 'U':
5491
-			$sex_sql = "i_sex='U'";
5492
-			break;
5493
-		case 'B':
5494
-		default:
5495
-			$sex_sql = "i_sex<>'U'";
5496
-			break;
5484
+		    case 'M':
5485
+			    $sex_sql = "i_sex='M'";
5486
+			    break;
5487
+		    case 'F':
5488
+			    $sex_sql = "i_sex='F'";
5489
+			    break;
5490
+		    case 'U':
5491
+			    $sex_sql = "i_sex='U'";
5492
+			    break;
5493
+		    case 'B':
5494
+		    default:
5495
+			    $sex_sql = "i_sex<>'U'";
5496
+			    break;
5497 5497
 		}
5498 5498
 		$ged_id = $this->tree->getTreeId();
5499 5499
 
@@ -5538,25 +5538,25 @@  discard block
 block discarded – undo
5538 5538
 				$tot = '';
5539 5539
 			}
5540 5540
 			switch ($type) {
5541
-			case 'table':
5542
-				$common[] = '<tr><td>' . $given . '</td><td>' . I18N::number($total) . '</td><td>' . $total . '</td></tr>';
5543
-				break;
5544
-			case 'list':
5545
-				$common[] = '<li><span dir="auto">' . $given . '</span>' . $tot . '</li>';
5546
-				break;
5547
-			case 'nolist':
5548
-				$common[] = '<span dir="auto">' . $given . '</span>' . $tot;
5549
-				break;
5541
+			    case 'table':
5542
+				    $common[] = '<tr><td>' . $given . '</td><td>' . I18N::number($total) . '</td><td>' . $total . '</td></tr>';
5543
+				    break;
5544
+			    case 'list':
5545
+				    $common[] = '<li><span dir="auto">' . $given . '</span>' . $tot . '</li>';
5546
+				    break;
5547
+			    case 'nolist':
5548
+				    $common[] = '<span dir="auto">' . $given . '</span>' . $tot;
5549
+				    break;
5550 5550
 			}
5551 5551
 		}
5552 5552
 		if ($common) {
5553 5553
 			switch ($type) {
5554
-			case 'table':
5555
-				global $controller;
5556
-				$table_id = Uuid::uuid4(); // lists requires a unique ID in case there are multiple lists per page
5557
-				$controller
5558
-					->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
5559
-					->addInlineJavascript('
5554
+			    case 'table':
5555
+				    global $controller;
5556
+				    $table_id = Uuid::uuid4(); // lists requires a unique ID in case there are multiple lists per page
5557
+				    $controller
5558
+					    ->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
5559
+					    ->addInlineJavascript('
5560 5560
 					jQuery("#' . $table_id . '").dataTable({
5561 5561
 						dom: \'t\',
5562 5562
 						autoWidth: false,
@@ -5574,15 +5574,15 @@  discard block
 block discarded – undo
5574 5574
 					});
5575 5575
 					jQuery("#' . $table_id . '").css("visibility", "visible");
5576 5576
 				');
5577
-				$lookup = array('M' => I18N::translate('Male'), 'F' => I18N::translate('Female'), 'U' => I18N::translateContext('unknown gender', 'Unknown'), 'B' => I18N::translate('All'));
5578
-
5579
-				return '<table id="' . $table_id . '" class="givn-list"><thead><tr><th class="ui-state-default" colspan="3">' . $lookup[$sex] . '</th></tr><tr><th>' . I18N::translate('Name') . '</th><th>' . I18N::translate('Count') . '</th><th>COUNT</th></tr></thead><tbody>' . implode('', $common) . '</tbody></table>';
5580
-			case 'list':
5581
-				return '<ul>' . implode('', $common) . '</ul>';
5582
-			case 'nolist':
5583
-				return implode(I18N::$list_separator, $common);
5584
-			default:
5585
-				return '';
5577
+				    $lookup = array('M' => I18N::translate('Male'), 'F' => I18N::translate('Female'), 'U' => I18N::translateContext('unknown gender', 'Unknown'), 'B' => I18N::translate('All'));
5578
+
5579
+				    return '<table id="' . $table_id . '" class="givn-list"><thead><tr><th class="ui-state-default" colspan="3">' . $lookup[$sex] . '</th></tr><tr><th>' . I18N::translate('Name') . '</th><th>' . I18N::translate('Count') . '</th><th>COUNT</th></tr></thead><tbody>' . implode('', $common) . '</tbody></table>';
5580
+			    case 'list':
5581
+				    return '<ul>' . implode('', $common) . '</ul>';
5582
+			    case 'nolist':
5583
+				    return implode(I18N::$list_separator, $common);
5584
+			    default:
5585
+				    return '';
5586 5586
 			}
5587 5587
 		} else {
5588 5588
 			return '';
@@ -6069,42 +6069,42 @@  discard block
 block discarded – undo
6069 6069
 		}
6070 6070
 
6071 6071
 		switch ($type) {
6072
-		default:
6073
-		case 'userid':
6074
-			return $user->getUserId();
6075
-		case 'username':
6076
-			return Filter::escapeHtml($user->getUserName());
6077
-		case 'fullname':
6078
-			return $user->getRealNameHtml();
6079
-		case 'regdate':
6080
-			if (is_array($params) && isset($params[0]) && $params[0] != '') {
6081
-				$datestamp = $params[0];
6082
-			} else {
6083
-				$datestamp = I18N::dateFormat();
6084
-			}
6085
-
6086
-			return FunctionsDate::timestampToGedcomDate($user->getPreference('reg_timestamp'))->display(false, $datestamp);
6087
-		case 'regtime':
6088
-			if (is_array($params) && isset($params[0]) && $params[0] != '') {
6089
-				$datestamp = $params[0];
6090
-			} else {
6091
-				$datestamp = str_replace('%', '', I18N::timeFormat());
6092
-			}
6093
-
6094
-			return date($datestamp, $user->getPreference('reg_timestamp'));
6095
-		case 'loggedin':
6096
-			if (is_array($params) && isset($params[0]) && $params[0] != '') {
6097
-				$yes = $params[0];
6098
-			} else {
6099
-				$yes = I18N::translate('yes');
6100
-			}
6101
-			if (is_array($params) && isset($params[1]) && $params[1] != '') {
6102
-				$no = $params[1];
6103
-			} else {
6104
-				$no = I18N::translate('no');
6105
-			}
6106
-
6107
-			return Database::prepare("SELECT 1 FROM `##session` WHERE user_id=? LIMIT 1")->execute(array($user->getUserId()))->fetchOne() ? $yes : $no;
6072
+		    default:
6073
+		    case 'userid':
6074
+			    return $user->getUserId();
6075
+		    case 'username':
6076
+			    return Filter::escapeHtml($user->getUserName());
6077
+		    case 'fullname':
6078
+			    return $user->getRealNameHtml();
6079
+		    case 'regdate':
6080
+			    if (is_array($params) && isset($params[0]) && $params[0] != '') {
6081
+				    $datestamp = $params[0];
6082
+			    } else {
6083
+				    $datestamp = I18N::dateFormat();
6084
+			    }
6085
+
6086
+			    return FunctionsDate::timestampToGedcomDate($user->getPreference('reg_timestamp'))->display(false, $datestamp);
6087
+		    case 'regtime':
6088
+			    if (is_array($params) && isset($params[0]) && $params[0] != '') {
6089
+				    $datestamp = $params[0];
6090
+			    } else {
6091
+				    $datestamp = str_replace('%', '', I18N::timeFormat());
6092
+			    }
6093
+
6094
+			    return date($datestamp, $user->getPreference('reg_timestamp'));
6095
+		    case 'loggedin':
6096
+			    if (is_array($params) && isset($params[0]) && $params[0] != '') {
6097
+				    $yes = $params[0];
6098
+			    } else {
6099
+				    $yes = I18N::translate('yes');
6100
+			    }
6101
+			    if (is_array($params) && isset($params[1]) && $params[1] != '') {
6102
+				    $no = $params[1];
6103
+			    } else {
6104
+				    $no = I18N::translate('no');
6105
+			    }
6106
+
6107
+			    return Database::prepare("SELECT 1 FROM `##session` WHERE user_id=? LIMIT 1")->execute(array($user->getUserId()))->fetchOne() ? $yes : $no;
6108 6108
 		}
6109 6109
 	}
6110 6110
 
@@ -6941,50 +6941,50 @@  discard block
 block discarded – undo
6941 6941
 		}
6942 6942
 		// The current chart engine (Google charts) can't handle <sup></sup> markup
6943 6943
 		switch ($century) {
6944
-		case 21:
6945
-			return strip_tags(I18N::translateContext('CENTURY', '21st'));
6946
-		case 20:
6947
-			return strip_tags(I18N::translateContext('CENTURY', '20th'));
6948
-		case 19:
6949
-			return strip_tags(I18N::translateContext('CENTURY', '19th'));
6950
-		case 18:
6951
-			return strip_tags(I18N::translateContext('CENTURY', '18th'));
6952
-		case 17:
6953
-			return strip_tags(I18N::translateContext('CENTURY', '17th'));
6954
-		case 16:
6955
-			return strip_tags(I18N::translateContext('CENTURY', '16th'));
6956
-		case 15:
6957
-			return strip_tags(I18N::translateContext('CENTURY', '15th'));
6958
-		case 14:
6959
-			return strip_tags(I18N::translateContext('CENTURY', '14th'));
6960
-		case 13:
6961
-			return strip_tags(I18N::translateContext('CENTURY', '13th'));
6962
-		case 12:
6963
-			return strip_tags(I18N::translateContext('CENTURY', '12th'));
6964
-		case 11:
6965
-			return strip_tags(I18N::translateContext('CENTURY', '11th'));
6966
-		case 10:
6967
-			return strip_tags(I18N::translateContext('CENTURY', '10th'));
6968
-		case  9:
6969
-			return strip_tags(I18N::translateContext('CENTURY', '9th'));
6970
-		case  8:
6971
-			return strip_tags(I18N::translateContext('CENTURY', '8th'));
6972
-		case  7:
6973
-			return strip_tags(I18N::translateContext('CENTURY', '7th'));
6974
-		case  6:
6975
-			return strip_tags(I18N::translateContext('CENTURY', '6th'));
6976
-		case  5:
6977
-			return strip_tags(I18N::translateContext('CENTURY', '5th'));
6978
-		case  4:
6979
-			return strip_tags(I18N::translateContext('CENTURY', '4th'));
6980
-		case  3:
6981
-			return strip_tags(I18N::translateContext('CENTURY', '3rd'));
6982
-		case  2:
6983
-			return strip_tags(I18N::translateContext('CENTURY', '2nd'));
6984
-		case  1:
6985
-			return strip_tags(I18N::translateContext('CENTURY', '1st'));
6986
-		default:
6987
-			return ($century - 1) . '01-' . $century . '00';
6944
+		    case 21:
6945
+			    return strip_tags(I18N::translateContext('CENTURY', '21st'));
6946
+		    case 20:
6947
+			    return strip_tags(I18N::translateContext('CENTURY', '20th'));
6948
+		    case 19:
6949
+			    return strip_tags(I18N::translateContext('CENTURY', '19th'));
6950
+		    case 18:
6951
+			    return strip_tags(I18N::translateContext('CENTURY', '18th'));
6952
+		    case 17:
6953
+			    return strip_tags(I18N::translateContext('CENTURY', '17th'));
6954
+		    case 16:
6955
+			    return strip_tags(I18N::translateContext('CENTURY', '16th'));
6956
+		    case 15:
6957
+			    return strip_tags(I18N::translateContext('CENTURY', '15th'));
6958
+		    case 14:
6959
+			    return strip_tags(I18N::translateContext('CENTURY', '14th'));
6960
+		    case 13:
6961
+			    return strip_tags(I18N::translateContext('CENTURY', '13th'));
6962
+		    case 12:
6963
+			    return strip_tags(I18N::translateContext('CENTURY', '12th'));
6964
+		    case 11:
6965
+			    return strip_tags(I18N::translateContext('CENTURY', '11th'));
6966
+		    case 10:
6967
+			    return strip_tags(I18N::translateContext('CENTURY', '10th'));
6968
+		    case  9:
6969
+			    return strip_tags(I18N::translateContext('CENTURY', '9th'));
6970
+		    case  8:
6971
+			    return strip_tags(I18N::translateContext('CENTURY', '8th'));
6972
+		    case  7:
6973
+			    return strip_tags(I18N::translateContext('CENTURY', '7th'));
6974
+		    case  6:
6975
+			    return strip_tags(I18N::translateContext('CENTURY', '6th'));
6976
+		    case  5:
6977
+			    return strip_tags(I18N::translateContext('CENTURY', '5th'));
6978
+		    case  4:
6979
+			    return strip_tags(I18N::translateContext('CENTURY', '4th'));
6980
+		    case  3:
6981
+			    return strip_tags(I18N::translateContext('CENTURY', '3rd'));
6982
+		    case  2:
6983
+			    return strip_tags(I18N::translateContext('CENTURY', '2nd'));
6984
+		    case  1:
6985
+			    return strip_tags(I18N::translateContext('CENTURY', '1st'));
6986
+		    default:
6987
+			    return ($century - 1) . '01-' . $century . '00';
6988 6988
 		}
6989 6989
 	}
6990 6990
 }
Please login to merge, or discard this patch.
Braces   +662 added lines, -331 removed lines patch added patch discarded remove patch
@@ -33,7 +33,8 @@  discard block
 block discarded – undo
33 33
  * These are primarily used for embedded keywords on HTML blocks, but
34 34
  * are also used elsewhere in the code.
35 35
  */
36
-class Stats {
36
+class Stats
37
+{
37 38
 	/** @var Tree Generate statistics for a specified tree. */
38 39
 	private $tree;
39 40
 
@@ -50,7 +51,8 @@  discard block
 block discarded – undo
50 51
 	 *
51 52
 	 * @param Tree $tree Generate statistics for this tree
52 53
 	 */
53
-	public function __construct(Tree $tree) {
54
+	public function __construct(Tree $tree)
55
+	{
54 56
 		$this->tree = $tree;
55 57
 	}
56 58
 
@@ -59,7 +61,8 @@  discard block
 block discarded – undo
59 61
 	 *
60 62
 	 * @return string
61 63
 	 */
62
-	public function getAllTagsTable() {
64
+	public function getAllTagsTable()
65
+	{
63 66
 		$examples = array();
64 67
 		foreach (get_class_methods($this) as $method) {
65 68
 			$reflection = new \ReflectionMethod($this, $method);
@@ -97,7 +100,8 @@  discard block
 block discarded – undo
97 100
 	 *
98 101
 	 * @return string
99 102
 	 */
100
-	public function getAllTagsText() {
103
+	public function getAllTagsText()
104
+	{
101 105
 		$examples = array();
102 106
 		foreach (get_class_methods($this) as $method) {
103 107
 			$reflection = new \ReflectionMethod($this, $method);
@@ -117,7 +121,8 @@  discard block
 block discarded – undo
117 121
 	 *
118 122
 	 * @return string[][]
119 123
 	 */
120
-	private function getTags($text) {
124
+	private function getTags($text)
125
+	{
121 126
 		static $funcs;
122 127
 
123 128
 		// Retrive all class methods
@@ -160,7 +165,8 @@  discard block
 block discarded – undo
160 165
 	 *
161 166
 	 * @return string
162 167
 	 */
163
-	public function embedTags($text) {
168
+	public function embedTags($text)
169
+	{
164 170
 		if (strpos($text, '#') !== false) {
165 171
 			list($new_tags, $new_values) = $this->getTags($text);
166 172
 			$text                        = str_replace($new_tags, $new_values, $text);
@@ -174,7 +180,8 @@  discard block
 block discarded – undo
174 180
 	 *
175 181
 	 * @return string
176 182
 	 */
177
-	public function gedcomFilename() {
183
+	public function gedcomFilename()
184
+	{
178 185
 		return $this->tree->getName();
179 186
 	}
180 187
 
@@ -183,7 +190,8 @@  discard block
 block discarded – undo
183 190
 	 *
184 191
 	 * @return int
185 192
 	 */
186
-	public function gedcomId() {
193
+	public function gedcomId()
194
+	{
187 195
 		return $this->tree->getTreeId();
188 196
 	}
189 197
 
@@ -192,7 +200,8 @@  discard block
 block discarded – undo
192 200
 	 *
193 201
 	 * @return string
194 202
 	 */
195
-	public function gedcomTitle() {
203
+	public function gedcomTitle()
204
+	{
196 205
 		return $this->tree->getTitleHtml();
197 206
 	}
198 207
 
@@ -201,7 +210,8 @@  discard block
 block discarded – undo
201 210
 	 *
202 211
 	 * @return string[]
203 212
 	 */
204
-	private function gedcomHead() {
213
+	private function gedcomHead()
214
+	{
205 215
 		$title   = '';
206 216
 		$version = '';
207 217
 		$source  = '';
@@ -222,7 +232,8 @@  discard block
 block discarded – undo
222 232
 	 *
223 233
 	 * @return string
224 234
 	 */
225
-	public function gedcomCreatedSoftware() {
235
+	public function gedcomCreatedSoftware()
236
+	{
226 237
 		$head = $this->gedcomHead();
227 238
 
228 239
 		return $head[0];
@@ -233,7 +244,8 @@  discard block
 block discarded – undo
233 244
 	 *
234 245
 	 * @return string
235 246
 	 */
236
-	public function gedcomCreatedVersion() {
247
+	public function gedcomCreatedVersion()
248
+	{
237 249
 		$head = $this->gedcomHead();
238 250
 		// fix broken version string in Family Tree Maker
239 251
 		if (strstr($head[1], 'Family Tree Maker ')) {
@@ -254,7 +266,8 @@  discard block
 block discarded – undo
254 266
 	 *
255 267
 	 * @return string
256 268
 	 */
257
-	public function gedcomDate() {
269
+	public function gedcomDate()
270
+	{
258 271
 		$head = GedcomRecord::getInstance('HEAD', $this->tree);
259 272
 		$fact = $head->getFirstFact('DATE');
260 273
 		if ($fact) {
@@ -271,7 +284,8 @@  discard block
 block discarded – undo
271 284
 	 *
272 285
 	 * @return string
273 286
 	 */
274
-	public function gedcomUpdated() {
287
+	public function gedcomUpdated()
288
+	{
275 289
 		$row = Database::prepare(
276 290
 			"SELECT d_year, d_month, d_day FROM `##dates` WHERE d_julianday1 = (SELECT MAX(d_julianday1) FROM `##dates` WHERE d_file =? AND d_fact='CHAN') LIMIT 1"
277 291
 		)->execute(array($this->tree->getTreeId()))->fetchOneRow();
@@ -289,7 +303,8 @@  discard block
 block discarded – undo
289 303
 	 *
290 304
 	 * @return string
291 305
 	 */
292
-	public function gedcomRootId() {
306
+	public function gedcomRootId()
307
+	{
293 308
 		return $this->tree->getPreference('PEDIGREE_ROOT_ID');
294 309
 	}
295 310
 
@@ -301,7 +316,8 @@  discard block
 block discarded – undo
301 316
 	 *
302 317
 	 * @return string
303 318
 	 */
304
-	private function getPercentage($total, $type) {
319
+	private function getPercentage($total, $type)
320
+	{
305 321
 		switch ($type) {
306 322
 		case 'individual':
307 323
 			$type = $this->totalIndividualsQuery();
@@ -332,7 +348,8 @@  discard block
 block discarded – undo
332 348
 	 *
333 349
 	 * @return string
334 350
 	 */
335
-	public function totalRecords() {
351
+	public function totalRecords()
352
+	{
336 353
 		return I18N::number($this->totalIndividualsQuery() + $this->totalFamiliesQuery() + $this->totalSourcesQuery());
337 354
 	}
338 355
 
@@ -341,7 +358,8 @@  discard block
 block discarded – undo
341 358
 	 *
342 359
 	 * @return int
343 360
 	 */
344
-	private function totalIndividualsQuery() {
361
+	private function totalIndividualsQuery()
362
+	{
345 363
 		return (int) Database::prepare(
346 364
 			"SELECT COUNT(*) FROM `##individuals` WHERE i_file = :tree_id"
347 365
 		)->execute(array(
@@ -354,7 +372,8 @@  discard block
 block discarded – undo
354 372
 	 *
355 373
 	 * @return string
356 374
 	 */
357
-	public function totalIndividuals() {
375
+	public function totalIndividuals()
376
+	{
358 377
 		return I18N::number($this->totalIndividualsQuery());
359 378
 	}
360 379
 
@@ -363,7 +382,8 @@  discard block
 block discarded – undo
363 382
 	 *
364 383
 	 * @return int
365 384
 	 */
366
-	private function totalIndisWithSourcesQuery() {
385
+	private function totalIndisWithSourcesQuery()
386
+	{
367 387
 		return (int) Database::prepare(
368 388
 			"SELECT COUNT(DISTINCT i_id)" .
369 389
 			" FROM `##individuals` JOIN `##link` ON i_id = l_from AND i_file = l_file" .
@@ -378,7 +398,8 @@  discard block
 block discarded – undo
378 398
 	 *
379 399
 	 * @return string
380 400
 	 */
381
-	public function totalIndisWithSources() {
401
+	public function totalIndisWithSources()
402
+	{
382 403
 		return I18N::number($this->totalIndisWithSourcesQuery());
383 404
 	}
384 405
 
@@ -389,7 +410,8 @@  discard block
 block discarded – undo
389 410
 	 *
390 411
 	 * @return string
391 412
 	 */
392
-	public function chartIndisWithSources($params = array()) {
413
+	public function chartIndisWithSources($params = array())
414
+	{
393 415
 		$WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
394 416
 		$WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
395 417
 		$WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
@@ -430,7 +452,8 @@  discard block
 block discarded – undo
430 452
 	 *
431 453
 	 * @return string
432 454
 	 */
433
-	public function totalIndividualsPercentage() {
455
+	public function totalIndividualsPercentage()
456
+	{
434 457
 		return $this->getPercentage($this->totalIndividualsQuery(), 'all');
435 458
 	}
436 459
 
@@ -439,7 +462,8 @@  discard block
 block discarded – undo
439 462
 	 *
440 463
 	 * @return int
441 464
 	 */
442
-	private function totalFamiliesQuery() {
465
+	private function totalFamiliesQuery()
466
+	{
443 467
 		return (int) Database::prepare(
444 468
 			"SELECT COUNT(*) FROM `##families` WHERE f_file = :tree_id"
445 469
 		)->execute(array(
@@ -452,7 +476,8 @@  discard block
 block discarded – undo
452 476
 	 *
453 477
 	 * @return string
454 478
 	 */
455
-	public function totalFamilies() {
479
+	public function totalFamilies()
480
+	{
456 481
 		return I18N::number($this->totalFamiliesQuery());
457 482
 	}
458 483
 
@@ -461,7 +486,8 @@  discard block
 block discarded – undo
461 486
 	 *
462 487
 	 * @return int
463 488
 	 */
464
-	private function totalFamsWithSourcesQuery() {
489
+	private function totalFamsWithSourcesQuery()
490
+	{
465 491
 		return (int) Database::prepare(
466 492
 			"SELECT COUNT(DISTINCT f_id)" .
467 493
 			" FROM `##families` JOIN `##link` ON f_id = l_from AND f_file = l_file" .
@@ -476,7 +502,8 @@  discard block
 block discarded – undo
476 502
 	 *
477 503
 	 * @return string
478 504
 	 */
479
-	public function totalFamsWithSources() {
505
+	public function totalFamsWithSources()
506
+	{
480 507
 		return I18N::number($this->totalFamsWithSourcesQuery());
481 508
 	}
482 509
 
@@ -487,7 +514,8 @@  discard block
 block discarded – undo
487 514
 	 *
488 515
 	 * @return string
489 516
 	 */
490
-	public function chartFamsWithSources($params = array()) {
517
+	public function chartFamsWithSources($params = array())
518
+	{
491 519
 		$WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
492 520
 		$WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
493 521
 		$WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
@@ -528,7 +556,8 @@  discard block
 block discarded – undo
528 556
 	 *
529 557
 	 * @return string
530 558
 	 */
531
-	public function totalFamiliesPercentage() {
559
+	public function totalFamiliesPercentage()
560
+	{
532 561
 		return $this->getPercentage($this->totalFamiliesQuery(), 'all');
533 562
 	}
534 563
 
@@ -537,7 +566,8 @@  discard block
 block discarded – undo
537 566
 	 *
538 567
 	 * @return int
539 568
 	 */
540
-	private function totalSourcesQuery() {
569
+	private function totalSourcesQuery()
570
+	{
541 571
 		return (int) Database::prepare(
542 572
 			"SELECT COUNT(*) FROM `##sources` WHERE s_file = :tree_id"
543 573
 		)->execute(array(
@@ -550,7 +580,8 @@  discard block
 block discarded – undo
550 580
 	 *
551 581
 	 * @return string
552 582
 	 */
553
-	public function totalSources() {
583
+	public function totalSources()
584
+	{
554 585
 		return I18N::number($this->totalSourcesQuery());
555 586
 	}
556 587
 
@@ -559,7 +590,8 @@  discard block
 block discarded – undo
559 590
 	 *
560 591
 	 * @return string
561 592
 	 */
562
-	public function totalSourcesPercentage() {
593
+	public function totalSourcesPercentage()
594
+	{
563 595
 		return $this->getPercentage($this->totalSourcesQuery(), 'all');
564 596
 	}
565 597
 
@@ -568,7 +600,8 @@  discard block
 block discarded – undo
568 600
 	 *
569 601
 	 * @return int
570 602
 	 */
571
-	private function totalNotesQuery() {
603
+	private function totalNotesQuery()
604
+	{
572 605
 		return (int) Database::prepare(
573 606
 			"SELECT COUNT(*) FROM `##other` WHERE o_type='NOTE' AND o_file = :tree_id"
574 607
 		)->execute(array(
@@ -581,7 +614,8 @@  discard block
 block discarded – undo
581 614
 	 *
582 615
 	 * @return string
583 616
 	 */
584
-	public function totalNotes() {
617
+	public function totalNotes()
618
+	{
585 619
 		return I18N::number($this->totalNotesQuery());
586 620
 	}
587 621
 
@@ -590,7 +624,8 @@  discard block
 block discarded – undo
590 624
 	 *
591 625
 	 * @return string
592 626
 	 */
593
-	public function totalNotesPercentage() {
627
+	public function totalNotesPercentage()
628
+	{
594 629
 		return $this->getPercentage($this->totalNotesQuery(), 'all');
595 630
 	}
596 631
 
@@ -599,7 +634,8 @@  discard block
 block discarded – undo
599 634
 	 *
600 635
 	 * @return int
601 636
 	 */
602
-	private function totalRepositoriesQuery() {
637
+	private function totalRepositoriesQuery()
638
+	{
603 639
 		return (int) Database::prepare(
604 640
 			"SELECT COUNT(*) FROM `##other` WHERE o_type='REPO' AND o_file = :tree_id"
605 641
 		)->execute(array(
@@ -612,7 +648,8 @@  discard block
 block discarded – undo
612 648
 	 *
613 649
 	 * @return string
614 650
 	 */
615
-	public function totalRepositories() {
651
+	public function totalRepositories()
652
+	{
616 653
 		return I18N::number($this->totalRepositoriesQuery());
617 654
 	}
618 655
 
@@ -621,7 +658,8 @@  discard block
 block discarded – undo
621 658
 	 *
622 659
 	 * @return string
623 660
 	 */
624
-	public function totalRepositoriesPercentage() {
661
+	public function totalRepositoriesPercentage()
662
+	{
625 663
 		return $this->getPercentage($this->totalRepositoriesQuery(), 'all');
626 664
 	}
627 665
 
@@ -632,7 +670,8 @@  discard block
 block discarded – undo
632 670
 	 *
633 671
 	 * @return string
634 672
 	 */
635
-	public function totalSurnames($params = array()) {
673
+	public function totalSurnames($params = array())
674
+	{
636 675
 		if ($params) {
637 676
 			$opt      = 'IN (' . implode(',', array_fill(0, count($params), '?')) . ')';
638 677
 			$distinct = '';
@@ -661,7 +700,8 @@  discard block
 block discarded – undo
661 700
 	 *
662 701
 	 * @return string
663 702
 	 */
664
-	public function totalGivennames($params = array()) {
703
+	public function totalGivennames($params = array())
704
+	{
665 705
 		if ($params) {
666 706
 			$qs       = implode(',', array_fill(0, count($params), '?'));
667 707
 			$params[] = $this->tree->getTreeId();
@@ -686,7 +726,8 @@  discard block
 block discarded – undo
686 726
 	 *
687 727
 	 * @return string
688 728
 	 */
689
-	public function totalEvents($params = array()) {
729
+	public function totalEvents($params = array())
730
+	{
690 731
 		$sql  = "SELECT COUNT(*) AS tot FROM `##dates` WHERE d_file=?";
691 732
 		$vars = array($this->tree->getTreeId());
692 733
 
@@ -716,7 +757,8 @@  discard block
 block discarded – undo
716 757
 	 *
717 758
 	 * @return string
718 759
 	 */
719
-	public function totalEventsBirth() {
760
+	public function totalEventsBirth()
761
+	{
720 762
 		return $this->totalEvents(explode('|', WT_EVENTS_BIRT));
721 763
 	}
722 764
 
@@ -725,7 +767,8 @@  discard block
 block discarded – undo
725 767
 	 *
726 768
 	 * @return string
727 769
 	 */
728
-	public function totalBirths() {
770
+	public function totalBirths()
771
+	{
729 772
 		return $this->totalEvents(array('BIRT'));
730 773
 	}
731 774
 
@@ -734,7 +777,8 @@  discard block
 block discarded – undo
734 777
 	 *
735 778
 	 * @return string
736 779
 	 */
737
-	public function totalEventsDeath() {
780
+	public function totalEventsDeath()
781
+	{
738 782
 		return $this->totalEvents(explode('|', WT_EVENTS_DEAT));
739 783
 	}
740 784
 
@@ -743,7 +787,8 @@  discard block
 block discarded – undo
743 787
 	 *
744 788
 	 * @return string
745 789
 	 */
746
-	public function totalDeaths() {
790
+	public function totalDeaths()
791
+	{
747 792
 		return $this->totalEvents(array('DEAT'));
748 793
 	}
749 794
 
@@ -752,7 +797,8 @@  discard block
 block discarded – undo
752 797
 	 *
753 798
 	 * @return string
754 799
 	 */
755
-	public function totalEventsMarriage() {
800
+	public function totalEventsMarriage()
801
+	{
756 802
 		return $this->totalEvents(explode('|', WT_EVENTS_MARR));
757 803
 	}
758 804
 
@@ -761,7 +807,8 @@  discard block
 block discarded – undo
761 807
 	 *
762 808
 	 * @return string
763 809
 	 */
764
-	public function totalMarriages() {
810
+	public function totalMarriages()
811
+	{
765 812
 		return $this->totalEvents(array('MARR'));
766 813
 	}
767 814
 
@@ -770,7 +817,8 @@  discard block
 block discarded – undo
770 817
 	 *
771 818
 	 * @return string
772 819
 	 */
773
-	public function totalEventsDivorce() {
820
+	public function totalEventsDivorce()
821
+	{
774 822
 		return $this->totalEvents(explode('|', WT_EVENTS_DIV));
775 823
 	}
776 824
 
@@ -779,7 +827,8 @@  discard block
 block discarded – undo
779 827
 	 *
780 828
 	 * @return string
781 829
 	 */
782
-	public function totalDivorces() {
830
+	public function totalDivorces()
831
+	{
783 832
 		return $this->totalEvents(array('DIV'));
784 833
 	}
785 834
 
@@ -788,7 +837,8 @@  discard block
 block discarded – undo
788 837
 	 *
789 838
 	 * @return string
790 839
 	 */
791
-	public function totalEventsOther() {
840
+	public function totalEventsOther()
841
+	{
792 842
 		$facts    = array_merge(explode('|', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT));
793 843
 		$no_facts = array();
794 844
 		foreach ($facts as $fact) {
@@ -804,7 +854,8 @@  discard block
 block discarded – undo
804 854
 	 *
805 855
 	 * @return int
806 856
 	 */
807
-	private function totalSexMalesQuery() {
857
+	private function totalSexMalesQuery()
858
+	{
808 859
 		return (int) Database::prepare(
809 860
 			"SELECT COUNT(*) FROM `##individuals` WHERE i_file = :tree_id AND i_sex = 'M'"
810 861
 		)->execute(array(
@@ -817,7 +868,8 @@  discard block
 block discarded – undo
817 868
 	 *
818 869
 	 * @return string
819 870
 	 */
820
-	public function totalSexMales() {
871
+	public function totalSexMales()
872
+	{
821 873
 		return I18N::number($this->totalSexMalesQuery());
822 874
 	}
823 875
 
@@ -826,7 +878,8 @@  discard block
 block discarded – undo
826 878
 	 *
827 879
 	 * @return string
828 880
 	 */
829
-	public function totalSexMalesPercentage() {
881
+	public function totalSexMalesPercentage()
882
+	{
830 883
 		return $this->getPercentage($this->totalSexMalesQuery(), 'individual');
831 884
 	}
832 885
 
@@ -835,7 +888,8 @@  discard block
 block discarded – undo
835 888
 	 *
836 889
 	 * @return int
837 890
 	 */
838
-	private function totalSexFemalesQuery() {
891
+	private function totalSexFemalesQuery()
892
+	{
839 893
 		return (int) Database::prepare(
840 894
 			"SELECT COUNT(*) FROM `##individuals` WHERE i_file = :tree_id AND i_sex = 'F'"
841 895
 		)->execute(array(
@@ -848,7 +902,8 @@  discard block
 block discarded – undo
848 902
 	 *
849 903
 	 * @return string
850 904
 	 */
851
-	public function totalSexFemales() {
905
+	public function totalSexFemales()
906
+	{
852 907
 		return I18N::number($this->totalSexFemalesQuery());
853 908
 	}
854 909
 
@@ -857,7 +912,8 @@  discard block
 block discarded – undo
857 912
 	 *
858 913
 	 * @return string
859 914
 	 */
860
-	public function totalSexFemalesPercentage() {
915
+	public function totalSexFemalesPercentage()
916
+	{
861 917
 		return $this->getPercentage($this->totalSexFemalesQuery(), 'individual');
862 918
 	}
863 919
 
@@ -866,7 +922,8 @@  discard block
 block discarded – undo
866 922
 	 *
867 923
 	 * @return int
868 924
 	 */
869
-	private function totalSexUnknownQuery() {
925
+	private function totalSexUnknownQuery()
926
+	{
870 927
 		return (int) Database::prepare(
871 928
 			"SELECT COUNT(*) FROM `##individuals` WHERE i_file = :tree_id AND i_sex = 'U'"
872 929
 		)->execute(array(
@@ -879,7 +936,8 @@  discard block
 block discarded – undo
879 936
 	 *
880 937
 	 * @return string
881 938
 	 */
882
-	public function totalSexUnknown() {
939
+	public function totalSexUnknown()
940
+	{
883 941
 		return I18N::number($this->totalSexUnknownQuery());
884 942
 	}
885 943
 
@@ -888,7 +946,8 @@  discard block
 block discarded – undo
888 946
 	 *
889 947
 	 * @return string
890 948
 	 */
891
-	public function totalSexUnknownPercentage() {
949
+	public function totalSexUnknownPercentage()
950
+	{
892 951
 		return $this->getPercentage($this->totalSexUnknownQuery(), 'individual');
893 952
 	}
894 953
 
@@ -899,7 +958,8 @@  discard block
 block discarded – undo
899 958
 	 *
900 959
 	 * @return string
901 960
 	 */
902
-	public function chartSex($params = array()) {
961
+	public function chartSex($params = array())
962
+	{
903 963
 		$WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
904 964
 		$WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
905 965
 
@@ -969,7 +1029,8 @@  discard block
 block discarded – undo
969 1029
 	 *
970 1030
 	 * @return int
971 1031
 	 */
972
-	private function totalLivingQuery() {
1032
+	private function totalLivingQuery()
1033
+	{
973 1034
 		return (int) Database::prepare(
974 1035
 			"SELECT COUNT(*) FROM `##individuals` WHERE i_file = :tree_id AND i_gedcom NOT REGEXP '\\n1 (" . WT_EVENTS_DEAT . ")'"
975 1036
 		)->execute(array(
@@ -982,7 +1043,8 @@  discard block
 block discarded – undo
982 1043
 	 *
983 1044
 	 * @return string
984 1045
 	 */
985
-	public function totalLiving() {
1046
+	public function totalLiving()
1047
+	{
986 1048
 		return I18N::number($this->totalLivingQuery());
987 1049
 	}
988 1050
 
@@ -991,7 +1053,8 @@  discard block
 block discarded – undo
991 1053
 	 *
992 1054
 	 * @return string
993 1055
 	 */
994
-	public function totalLivingPercentage() {
1056
+	public function totalLivingPercentage()
1057
+	{
995 1058
 		return $this->getPercentage($this->totalLivingQuery(), 'individual');
996 1059
 	}
997 1060
 
@@ -1000,7 +1063,8 @@  discard block
 block discarded – undo
1000 1063
 	 *
1001 1064
 	 * @return int
1002 1065
 	 */
1003
-	private function totalDeceasedQuery() {
1066
+	private function totalDeceasedQuery()
1067
+	{
1004 1068
 		return (int) Database::prepare(
1005 1069
 			"SELECT COUNT(*) FROM `##individuals` WHERE i_file = :tree_id AND i_gedcom REGEXP '\\n1 (" . WT_EVENTS_DEAT . ")'"
1006 1070
 		)->execute(array(
@@ -1013,7 +1077,8 @@  discard block
 block discarded – undo
1013 1077
 	 *
1014 1078
 	 * @return string
1015 1079
 	 */
1016
-	public function totalDeceased() {
1080
+	public function totalDeceased()
1081
+	{
1017 1082
 		return I18N::number($this->totalDeceasedQuery());
1018 1083
 	}
1019 1084
 
@@ -1022,7 +1087,8 @@  discard block
 block discarded – undo
1022 1087
 	 *
1023 1088
 	 * @return string
1024 1089
 	 */
1025
-	public function totalDeceasedPercentage() {
1090
+	public function totalDeceasedPercentage()
1091
+	{
1026 1092
 		return $this->getPercentage($this->totalDeceasedQuery(), 'individual');
1027 1093
 	}
1028 1094
 
@@ -1033,7 +1099,8 @@  discard block
 block discarded – undo
1033 1099
 	 *
1034 1100
 	 * @return string
1035 1101
 	 */
1036
-	public function chartMortality($params = array()) {
1102
+	public function chartMortality($params = array())
1103
+	{
1037 1104
 		$WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
1038 1105
 		$WT_STATS_S_CHART_Y    = Theme::theme()->parameter('stats-small-chart-y');
1039 1106
 
@@ -1081,7 +1148,8 @@  discard block
 block discarded – undo
1081 1148
 	 *
1082 1149
 	 * @return string
1083 1150
 	 */
1084
-	public function totalUsers($params = array()) {
1151
+	public function totalUsers($params = array())
1152
+	{
1085 1153
 		if (isset($params[0])) {
1086 1154
 			$total = count(User::all()) + (int) $params[0];
1087 1155
 		} else {
@@ -1096,7 +1164,8 @@  discard block
 block discarded – undo
1096 1164
 	 *
1097 1165
 	 * @return string
1098 1166
 	 */
1099
-	public function totalAdmins() {
1167
+	public function totalAdmins()
1168
+	{
1100 1169
 		return I18N::number(count(User::allAdmins()));
1101 1170
 	}
1102 1171
 
@@ -1105,7 +1174,8 @@  discard block
 block discarded – undo
1105 1174
 	 *
1106 1175
 	 * @return string
1107 1176
 	 */
1108
-	public function totalNonAdmins() {
1177
+	public function totalNonAdmins()
1178
+	{
1109 1179
 		return I18N::number(count(User::all()) - count(User::allAdmins()));
1110 1180
 	}
1111 1181
 
@@ -1116,7 +1186,8 @@  discard block
 block discarded – undo
1116 1186
 	 *
1117 1187
 	 * @return int
1118 1188
 	 */
1119
-	private function totalMediaType($type = 'all') {
1189
+	private function totalMediaType($type = 'all')
1190
+	{
1120 1191
 		if (!in_array($type, $this->_media_types) && $type != 'all' && $type != 'unknown') {
1121 1192
 			return 0;
1122 1193
 		}
@@ -1146,7 +1217,8 @@  discard block
 block discarded – undo
1146 1217
 	 *
1147 1218
 	 * @return string
1148 1219
 	 */
1149
-	public function totalMedia() {
1220
+	public function totalMedia()
1221
+	{
1150 1222
 		return I18N::number($this->totalMediaType('all'));
1151 1223
 	}
1152 1224
 
@@ -1155,7 +1227,8 @@  discard block
 block discarded – undo
1155 1227
 	 *
1156 1228
 	 * @return string
1157 1229
 	 */
1158
-	public function totalMediaAudio() {
1230
+	public function totalMediaAudio()
1231
+	{
1159 1232
 		return I18N::number($this->totalMediaType('audio'));
1160 1233
 	}
1161 1234
 
@@ -1164,7 +1237,8 @@  discard block
 block discarded – undo
1164 1237
 	 *
1165 1238
 	 * @return string
1166 1239
 	 */
1167
-	public function totalMediaBook() {
1240
+	public function totalMediaBook()
1241
+	{
1168 1242
 		return I18N::number($this->totalMediaType('book'));
1169 1243
 	}
1170 1244
 
@@ -1173,7 +1247,8 @@  discard block
 block discarded – undo
1173 1247
 	 *
1174 1248
 	 * @return string
1175 1249
 	 */
1176
-	public function totalMediaCard() {
1250
+	public function totalMediaCard()
1251
+	{
1177 1252
 		return I18N::number($this->totalMediaType('card'));
1178 1253
 	}
1179 1254
 
@@ -1182,7 +1257,8 @@  discard block
 block discarded – undo
1182 1257
 	 *
1183 1258
 	 * @return string
1184 1259
 	 */
1185
-	public function totalMediaCertificate() {
1260
+	public function totalMediaCertificate()
1261
+	{
1186 1262
 		return I18N::number($this->totalMediaType('certificate'));
1187 1263
 	}
1188 1264
 
@@ -1191,7 +1267,8 @@  discard block
 block discarded – undo
1191 1267
 	 *
1192 1268
 	 * @return string
1193 1269
 	 */
1194
-	public function totalMediaCoatOfArms() {
1270
+	public function totalMediaCoatOfArms()
1271
+	{
1195 1272
 		return I18N::number($this->totalMediaType('coat'));
1196 1273
 	}
1197 1274
 
@@ -1200,7 +1277,8 @@  discard block
 block discarded – undo
1200 1277
 	 *
1201 1278
 	 * @return string
1202 1279
 	 */
1203
-	public function totalMediaDocument() {
1280
+	public function totalMediaDocument()
1281
+	{
1204 1282
 		return I18N::number($this->totalMediaType('document'));
1205 1283
 	}
1206 1284
 
@@ -1209,7 +1287,8 @@  discard block
 block discarded – undo
1209 1287
 	 *
1210 1288
 	 * @return string
1211 1289
 	 */
1212
-	public function totalMediaElectronic() {
1290
+	public function totalMediaElectronic()
1291
+	{
1213 1292
 		return I18N::number($this->totalMediaType('electronic'));
1214 1293
 	}
1215 1294
 
@@ -1218,7 +1297,8 @@  discard block
 block discarded – undo
1218 1297
 	 *
1219 1298
 	 * @return string
1220 1299
 	 */
1221
-	public function totalMediaMagazine() {
1300
+	public function totalMediaMagazine()
1301
+	{
1222 1302
 		return I18N::number($this->totalMediaType('magazine'));
1223 1303
 	}
1224 1304
 
@@ -1227,7 +1307,8 @@  discard block
 block discarded – undo
1227 1307
 	 *
1228 1308
 	 * @return string
1229 1309
 	 */
1230
-	public function totalMediaManuscript() {
1310
+	public function totalMediaManuscript()
1311
+	{
1231 1312
 		return I18N::number($this->totalMediaType('manuscript'));
1232 1313
 	}
1233 1314
 
@@ -1236,7 +1317,8 @@  discard block
 block discarded – undo
1236 1317
 	 *
1237 1318
 	 * @return string
1238 1319
 	 */
1239
-	public function totalMediaMap() {
1320
+	public function totalMediaMap()
1321
+	{
1240 1322
 		return I18N::number($this->totalMediaType('map'));
1241 1323
 	}
1242 1324
 
@@ -1245,7 +1327,8 @@  discard block
 block discarded – undo
1245 1327
 	 *
1246 1328
 	 * @return string
1247 1329
 	 */
1248
-	public function totalMediaFiche() {
1330
+	public function totalMediaFiche()
1331
+	{
1249 1332
 		return I18N::number($this->totalMediaType('fiche'));
1250 1333
 	}
1251 1334
 
@@ -1254,7 +1337,8 @@  discard block
 block discarded – undo
1254 1337
 	 *
1255 1338
 	 * @return string
1256 1339
 	 */
1257
-	public function totalMediaFilm() {
1340
+	public function totalMediaFilm()
1341
+	{
1258 1342
 		return I18N::number($this->totalMediaType('film'));
1259 1343
 	}
1260 1344
 
@@ -1263,7 +1347,8 @@  discard block
 block discarded – undo
1263 1347
 	 *
1264 1348
 	 * @return string
1265 1349
 	 */
1266
-	public function totalMediaNewspaper() {
1350
+	public function totalMediaNewspaper()
1351
+	{
1267 1352
 		return I18N::number($this->totalMediaType('newspaper'));
1268 1353
 	}
1269 1354
 
@@ -1272,7 +1357,8 @@  discard block
 block discarded – undo
1272 1357
 	 *
1273 1358
 	 * @return string
1274 1359
 	 */
1275
-	public function totalMediaPainting() {
1360
+	public function totalMediaPainting()
1361
+	{
1276 1362
 		return I18N::number($this->totalMediaType('painting'));
1277 1363
 	}
1278 1364
 
@@ -1281,7 +1367,8 @@  discard block
 block discarded – undo
1281 1367
 	 *
1282 1368
 	 * @return string
1283 1369
 	 */
1284
-	public function totalMediaPhoto() {
1370
+	public function totalMediaPhoto()
1371
+	{
1285 1372
 		return I18N::number($this->totalMediaType('photo'));
1286 1373
 	}
1287 1374
 
@@ -1290,7 +1377,8 @@  discard block
 block discarded – undo
1290 1377
 	 *
1291 1378
 	 * @return string
1292 1379
 	 */
1293
-	public function totalMediaTombstone() {
1380
+	public function totalMediaTombstone()
1381
+	{
1294 1382
 		return I18N::number($this->totalMediaType('tombstone'));
1295 1383
 	}
1296 1384
 
@@ -1299,7 +1387,8 @@  discard block
 block discarded – undo
1299 1387
 	 *
1300 1388
 	 * @return string
1301 1389
 	 */
1302
-	public function totalMediaVideo() {
1390
+	public function totalMediaVideo()
1391
+	{
1303 1392
 		return I18N::number($this->totalMediaType('video'));
1304 1393
 	}
1305 1394
 
@@ -1308,7 +1397,8 @@  discard block
 block discarded – undo
1308 1397
 	 *
1309 1398
 	 * @return string
1310 1399
 	 */
1311
-	public function totalMediaOther() {
1400
+	public function totalMediaOther()
1401
+	{
1312 1402
 		return I18N::number($this->totalMediaType('other'));
1313 1403
 	}
1314 1404
 
@@ -1317,7 +1407,8 @@  discard block
 block discarded – undo
1317 1407
 	 *
1318 1408
 	 * @return string
1319 1409
 	 */
1320
-	public function totalMediaUnknown() {
1410
+	public function totalMediaUnknown()
1411
+	{
1321 1412
 		return I18N::number($this->totalMediaType('unknown'));
1322 1413
 	}
1323 1414
 
@@ -1328,7 +1419,8 @@  discard block
 block discarded – undo
1328 1419
 	 *
1329 1420
 	 * @return string
1330 1421
 	 */
1331
-	public function chartMedia($params = array()) {
1422
+	public function chartMedia($params = array())
1423
+	{
1332 1424
 		$WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
1333 1425
 		$WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
1334 1426
 		$WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
@@ -1414,7 +1506,8 @@  discard block
 block discarded – undo
1414 1506
 	 *
1415 1507
 	 * @return string
1416 1508
 	 */
1417
-	private function mortalityQuery($type = 'full', $life_dir = 'ASC', $birth_death = 'BIRT') {
1509
+	private function mortalityQuery($type = 'full', $life_dir = 'ASC', $birth_death = 'BIRT')
1510
+	{
1418 1511
 		if ($birth_death == 'MARR') {
1419 1512
 			$query_field = "'MARR'";
1420 1513
 		} elseif ($birth_death == 'DIV') {
@@ -1482,7 +1575,8 @@  discard block
 block discarded – undo
1482 1575
 	 *
1483 1576
 	 * @return int[]|string[][]
1484 1577
 	 */
1485
-	public function statsPlaces($what = 'ALL', $fact = '', $parent = 0, $country = false) {
1578
+	public function statsPlaces($what = 'ALL', $fact = '', $parent = 0, $country = false)
1579
+	{
1486 1580
 		if ($fact) {
1487 1581
 			if ($what == 'INDI') {
1488 1582
 				$rows = Database::prepare(
@@ -1570,7 +1664,8 @@  discard block
 block discarded – undo
1570 1664
 	 *
1571 1665
 	 * @return int
1572 1666
 	 */
1573
-	private function totalPlacesQuery() {
1667
+	private function totalPlacesQuery()
1668
+	{
1574 1669
 		return
1575 1670
 			(int) Database::prepare("SELECT COUNT(*) FROM `##places` WHERE p_file=?")
1576 1671
 				->execute(array($this->tree->getTreeId()))
@@ -1582,7 +1677,8 @@  discard block
 block discarded – undo
1582 1677
 	 *
1583 1678
 	 * @return string
1584 1679
 	 */
1585
-	public function totalPlaces() {
1680
+	public function totalPlaces()
1681
+	{
1586 1682
 		return I18N::number($this->totalPlacesQuery());
1587 1683
 	}
1588 1684
 
@@ -1593,7 +1689,8 @@  discard block
 block discarded – undo
1593 1689
 	 *
1594 1690
 	 * @return string
1595 1691
 	 */
1596
-	public function chartDistribution($params = array()) {
1692
+	public function chartDistribution($params = array())
1693
+	{
1597 1694
 		$WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
1598 1695
 		$WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
1599 1696
 		$WT_STATS_CHART_COLOR3 = Theme::theme()->parameter('distribution-chart-low-values');
@@ -1747,7 +1844,8 @@  discard block
 block discarded – undo
1747 1844
 	 *
1748 1845
 	 * @return string
1749 1846
 	 */
1750
-	public function commonCountriesList() {
1847
+	public function commonCountriesList()
1848
+	{
1751 1849
 		$countries = $this->statsPlaces();
1752 1850
 		if (empty($countries)) {
1753 1851
 			return '';
@@ -1799,7 +1897,8 @@  discard block
 block discarded – undo
1799 1897
 	 *
1800 1898
 	 * @return string
1801 1899
 	 */
1802
-	public function commonBirthPlacesList() {
1900
+	public function commonBirthPlacesList()
1901
+	{
1803 1902
 		$places = $this->statsPlaces('INDI', 'BIRT');
1804 1903
 		$top10  = array();
1805 1904
 		$i      = 1;
@@ -1822,7 +1921,8 @@  discard block
 block discarded – undo
1822 1921
 	 *
1823 1922
 	 * @return string
1824 1923
 	 */
1825
-	public function commonDeathPlacesList() {
1924
+	public function commonDeathPlacesList()
1925
+	{
1826 1926
 		$places = $this->statsPlaces('INDI', 'DEAT');
1827 1927
 		$top10  = array();
1828 1928
 		$i      = 1;
@@ -1845,7 +1945,8 @@  discard block
 block discarded – undo
1845 1945
 	 *
1846 1946
 	 * @return string
1847 1947
 	 */
1848
-	public function commonMarriagePlacesList() {
1948
+	public function commonMarriagePlacesList()
1949
+	{
1849 1950
 		$places = $this->statsPlaces('FAM', 'MARR');
1850 1951
 		$top10  = array();
1851 1952
 		$i      = 1;
@@ -1874,7 +1975,8 @@  discard block
 block discarded – undo
1874 1975
 	 *
1875 1976
 	 * @return array|string
1876 1977
 	 */
1877
-	public function statsBirthQuery($simple = true, $sex = false, $year1 = -1, $year2 = -1, $params = array()) {
1978
+	public function statsBirthQuery($simple = true, $sex = false, $year1 = -1, $year2 = -1, $params = array())
1979
+	{
1878 1980
 		$WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
1879 1981
 		$WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
1880 1982
 		$WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
@@ -1967,7 +2069,8 @@  discard block
 block discarded – undo
1967 2069
 	 *
1968 2070
 	 * @return array|string
1969 2071
 	 */
1970
-	public function statsDeathQuery($simple = true, $sex = false, $year1 = -1, $year2 = -1, $params = array()) {
2072
+	public function statsDeathQuery($simple = true, $sex = false, $year1 = -1, $year2 = -1, $params = array())
2073
+	{
1971 2074
 		$WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
1972 2075
 		$WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
1973 2076
 		$WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
@@ -2054,7 +2157,8 @@  discard block
 block discarded – undo
2054 2157
 	 *
2055 2158
 	 * @return string
2056 2159
 	 */
2057
-	public function firstBirth() {
2160
+	public function firstBirth()
2161
+	{
2058 2162
 		return $this->mortalityQuery('full', 'ASC', 'BIRT');
2059 2163
 	}
2060 2164
 
@@ -2063,7 +2167,8 @@  discard block
 block discarded – undo
2063 2167
 	 *
2064 2168
 	 * @return string
2065 2169
 	 */
2066
-	public function firstBirthYear() {
2170
+	public function firstBirthYear()
2171
+	{
2067 2172
 		return $this->mortalityQuery('year', 'ASC', 'BIRT');
2068 2173
 	}
2069 2174
 
@@ -2072,7 +2177,8 @@  discard block
 block discarded – undo
2072 2177
 	 *
2073 2178
 	 * @return string
2074 2179
 	 */
2075
-	public function firstBirthName() {
2180
+	public function firstBirthName()
2181
+	{
2076 2182
 		return $this->mortalityQuery('name', 'ASC', 'BIRT');
2077 2183
 	}
2078 2184
 
@@ -2081,7 +2187,8 @@  discard block
 block discarded – undo
2081 2187
 	 *
2082 2188
 	 * @return string
2083 2189
 	 */
2084
-	public function firstBirthPlace() {
2190
+	public function firstBirthPlace()
2191
+	{
2085 2192
 		return $this->mortalityQuery('place', 'ASC', 'BIRT');
2086 2193
 	}
2087 2194
 
@@ -2090,7 +2197,8 @@  discard block
 block discarded – undo
2090 2197
 	 *
2091 2198
 	 * @return string
2092 2199
 	 */
2093
-	public function lastBirth() {
2200
+	public function lastBirth()
2201
+	{
2094 2202
 		return $this->mortalityQuery('full', 'DESC', 'BIRT');
2095 2203
 	}
2096 2204
 
@@ -2099,7 +2207,8 @@  discard block
 block discarded – undo
2099 2207
 	 *
2100 2208
 	 * @return string
2101 2209
 	 */
2102
-	public function lastBirthYear() {
2210
+	public function lastBirthYear()
2211
+	{
2103 2212
 		return $this->mortalityQuery('year', 'DESC', 'BIRT');
2104 2213
 	}
2105 2214
 
@@ -2108,7 +2217,8 @@  discard block
 block discarded – undo
2108 2217
 	 *
2109 2218
 	 * @return string
2110 2219
 	 */
2111
-	public function lastBirthName() {
2220
+	public function lastBirthName()
2221
+	{
2112 2222
 		return $this->mortalityQuery('name', 'DESC', 'BIRT');
2113 2223
 	}
2114 2224
 
@@ -2117,7 +2227,8 @@  discard block
 block discarded – undo
2117 2227
 	 *
2118 2228
 	 * @return string
2119 2229
 	 */
2120
-	public function lastBirthPlace() {
2230
+	public function lastBirthPlace()
2231
+	{
2121 2232
 		return $this->mortalityQuery('place', 'DESC', 'BIRT');
2122 2233
 	}
2123 2234
 
@@ -2128,7 +2239,8 @@  discard block
 block discarded – undo
2128 2239
 	 *
2129 2240
 	 * @return string
2130 2241
 	 */
2131
-	public function statsBirth($params = array()) {
2242
+	public function statsBirth($params = array())
2243
+	{
2132 2244
 		return $this->statsBirthQuery(true, false, -1, -1, $params);
2133 2245
 	}
2134 2246
 
@@ -2137,7 +2249,8 @@  discard block
 block discarded – undo
2137 2249
 	 *
2138 2250
 	 * @return string
2139 2251
 	 */
2140
-	public function firstDeath() {
2252
+	public function firstDeath()
2253
+	{
2141 2254
 		return $this->mortalityQuery('full', 'ASC', 'DEAT');
2142 2255
 	}
2143 2256
 
@@ -2146,7 +2259,8 @@  discard block
 block discarded – undo
2146 2259
 	 *
2147 2260
 	 * @return string
2148 2261
 	 */
2149
-	public function firstDeathYear() {
2262
+	public function firstDeathYear()
2263
+	{
2150 2264
 		return $this->mortalityQuery('year', 'ASC', 'DEAT');
2151 2265
 	}
2152 2266
 
@@ -2155,7 +2269,8 @@  discard block
 block discarded – undo
2155 2269
 	 *
2156 2270
 	 * @return string
2157 2271
 	 */
2158
-	public function firstDeathName() {
2272
+	public function firstDeathName()
2273
+	{
2159 2274
 		return $this->mortalityQuery('name', 'ASC', 'DEAT');
2160 2275
 	}
2161 2276
 
@@ -2164,7 +2279,8 @@  discard block
 block discarded – undo
2164 2279
 	 *
2165 2280
 	 * @return string
2166 2281
 	 */
2167
-	public function firstDeathPlace() {
2282
+	public function firstDeathPlace()
2283
+	{
2168 2284
 		return $this->mortalityQuery('place', 'ASC', 'DEAT');
2169 2285
 	}
2170 2286
 
@@ -2173,7 +2289,8 @@  discard block
 block discarded – undo
2173 2289
 	 *
2174 2290
 	 * @return string
2175 2291
 	 */
2176
-	public function lastDeath() {
2292
+	public function lastDeath()
2293
+	{
2177 2294
 		return $this->mortalityQuery('full', 'DESC', 'DEAT');
2178 2295
 	}
2179 2296
 
@@ -2182,7 +2299,8 @@  discard block
 block discarded – undo
2182 2299
 	 *
2183 2300
 	 * @return string
2184 2301
 	 */
2185
-	public function lastDeathYear() {
2302
+	public function lastDeathYear()
2303
+	{
2186 2304
 		return $this->mortalityQuery('year', 'DESC', 'DEAT');
2187 2305
 	}
2188 2306
 
@@ -2191,7 +2309,8 @@  discard block
 block discarded – undo
2191 2309
 	 *
2192 2310
 	 * @return string
2193 2311
 	 */
2194
-	public function lastDeathName() {
2312
+	public function lastDeathName()
2313
+	{
2195 2314
 		return $this->mortalityQuery('name', 'DESC', 'DEAT');
2196 2315
 	}
2197 2316
 
@@ -2200,7 +2319,8 @@  discard block
 block discarded – undo
2200 2319
 	 *
2201 2320
 	 * @return string
2202 2321
 	 */
2203
-	public function lastDeathPlace() {
2322
+	public function lastDeathPlace()
2323
+	{
2204 2324
 		return $this->mortalityQuery('place', 'DESC', 'DEAT');
2205 2325
 	}
2206 2326
 
@@ -2211,7 +2331,8 @@  discard block
 block discarded – undo
2211 2331
 	 *
2212 2332
 	 * @return string
2213 2333
 	 */
2214
-	public function statsDeath($params = array()) {
2334
+	public function statsDeath($params = array())
2335
+	{
2215 2336
 		return $this->statsDeathQuery(true, false, -1, -1, $params);
2216 2337
 	}
2217 2338
 
@@ -2223,7 +2344,8 @@  discard block
 block discarded – undo
2223 2344
 	 *
2224 2345
 	 * @return string
2225 2346
 	 */
2226
-	private function longlifeQuery($type = 'full', $sex = 'F') {
2347
+	private function longlifeQuery($type = 'full', $sex = 'F')
2348
+	{
2227 2349
 		$sex_search = ' 1=1';
2228 2350
 		if ($sex == 'F') {
2229 2351
 			$sex_search = " i_sex='F'";
@@ -2287,7 +2409,8 @@  discard block
 block discarded – undo
2287 2409
 	 *
2288 2410
 	 * @return string
2289 2411
 	 */
2290
-	private function topTenOldestQuery($type = 'list', $sex = 'BOTH', $params = array()) {
2412
+	private function topTenOldestQuery($type = 'list', $sex = 'BOTH', $params = array())
2413
+	{
2291 2414
 		if ($sex === 'F') {
2292 2415
 			$sex_search = " AND i_sex='F' ";
2293 2416
 		} elseif ($sex === 'M') {
@@ -2370,7 +2493,8 @@  discard block
 block discarded – undo
2370 2493
 	 *
2371 2494
 	 * @return string
2372 2495
 	 */
2373
-	private function topTenOldestAliveQuery($type = 'list', $sex = 'BOTH', $params = array()) {
2496
+	private function topTenOldestAliveQuery($type = 'list', $sex = 'BOTH', $params = array())
2497
+	{
2374 2498
 		if (!Auth::isMember($this->tree)) {
2375 2499
 			return I18N::translate('This information is private and cannot be shown.');
2376 2500
 		}
@@ -2446,7 +2570,8 @@  discard block
 block discarded – undo
2446 2570
 	 *
2447 2571
 	 * @return string
2448 2572
 	 */
2449
-	private function averageLifespanQuery($sex = 'BOTH', $show_years = false) {
2573
+	private function averageLifespanQuery($sex = 'BOTH', $show_years = false)
2574
+	{
2450 2575
 		if ($sex === 'F') {
2451 2576
 			$sex_search = " AND i_sex='F' ";
2452 2577
 		} elseif ($sex === 'M') {
@@ -2505,7 +2630,8 @@  discard block
 block discarded – undo
2505 2630
 	 *
2506 2631
 	 * @return array|string
2507 2632
 	 */
2508
-	public function statsAgeQuery($simple = true, $related = 'BIRT', $sex = 'BOTH', $year1 = -1, $year2 = -1, $params = array()) {
2633
+	public function statsAgeQuery($simple = true, $related = 'BIRT', $sex = 'BOTH', $year1 = -1, $year2 = -1, $params = array())
2634
+	{
2509 2635
 		if ($simple) {
2510 2636
 			if (isset($params[0]) && $params[0] != '') {
2511 2637
 				$size = strtolower($params[0]);
@@ -2646,7 +2772,8 @@  discard block
 block discarded – undo
2646 2772
 	 *
2647 2773
 	 * @return string
2648 2774
 	 */
2649
-	public function statsAge($params = array()) {
2775
+	public function statsAge($params = array())
2776
+	{
2650 2777
 		return $this->statsAgeQuery(true, 'BIRT', 'BOTH', -1, -1, $params);
2651 2778
 	}
2652 2779
 
@@ -2655,7 +2782,8 @@  discard block
 block discarded – undo
2655 2782
 	 *
2656 2783
 	 * @return string
2657 2784
 	 */
2658
-	public function longestLife() {
2785
+	public function longestLife()
2786
+	{
2659 2787
 		return $this->longlifeQuery('full', 'BOTH');
2660 2788
 	}
2661 2789
 
@@ -2664,7 +2792,8 @@  discard block
 block discarded – undo
2664 2792
 	 *
2665 2793
 	 * @return string
2666 2794
 	 */
2667
-	public function longestLifeAge() {
2795
+	public function longestLifeAge()
2796
+	{
2668 2797
 		return $this->longlifeQuery('age', 'BOTH');
2669 2798
 	}
2670 2799
 
@@ -2673,7 +2802,8 @@  discard block
 block discarded – undo
2673 2802
 	 *
2674 2803
 	 * @return string
2675 2804
 	 */
2676
-	public function longestLifeName() {
2805
+	public function longestLifeName()
2806
+	{
2677 2807
 		return $this->longlifeQuery('name', 'BOTH');
2678 2808
 	}
2679 2809
 
@@ -2684,7 +2814,8 @@  discard block
 block discarded – undo
2684 2814
 	 *
2685 2815
 	 * @return string
2686 2816
 	 */
2687
-	public function topTenOldest($params = array()) {
2817
+	public function topTenOldest($params = array())
2818
+	{
2688 2819
 		return $this->topTenOldestQuery('nolist', 'BOTH', $params);
2689 2820
 	}
2690 2821
 
@@ -2695,7 +2826,8 @@  discard block
 block discarded – undo
2695 2826
 	 *
2696 2827
 	 * @return string
2697 2828
 	 */
2698
-	public function topTenOldestList($params = array()) {
2829
+	public function topTenOldestList($params = array())
2830
+	{
2699 2831
 		return $this->topTenOldestQuery('list', 'BOTH', $params);
2700 2832
 	}
2701 2833
 
@@ -2706,7 +2838,8 @@  discard block
 block discarded – undo
2706 2838
 	 *
2707 2839
 	 * @return string
2708 2840
 	 */
2709
-	public function topTenOldestAlive($params = array()) {
2841
+	public function topTenOldestAlive($params = array())
2842
+	{
2710 2843
 		return $this->topTenOldestAliveQuery('nolist', 'BOTH', $params);
2711 2844
 	}
2712 2845
 
@@ -2717,7 +2850,8 @@  discard block
 block discarded – undo
2717 2850
 	 *
2718 2851
 	 * @return string
2719 2852
 	 */
2720
-	public function topTenOldestListAlive($params = array()) {
2853
+	public function topTenOldestListAlive($params = array())
2854
+	{
2721 2855
 		return $this->topTenOldestAliveQuery('list', 'BOTH', $params);
2722 2856
 	}
2723 2857
 
@@ -2728,7 +2862,8 @@  discard block
 block discarded – undo
2728 2862
 	 *
2729 2863
 	 * @return string
2730 2864
 	 */
2731
-	public function averageLifespan($show_years = false) {
2865
+	public function averageLifespan($show_years = false)
2866
+	{
2732 2867
 		return $this->averageLifespanQuery('BOTH', $show_years);
2733 2868
 	}
2734 2869
 
@@ -2737,7 +2872,8 @@  discard block
 block discarded – undo
2737 2872
 	 *
2738 2873
 	 * @return string
2739 2874
 	 */
2740
-	public function longestLifeFemale() {
2875
+	public function longestLifeFemale()
2876
+	{
2741 2877
 		return $this->longlifeQuery('full', 'F');
2742 2878
 	}
2743 2879
 
@@ -2746,7 +2882,8 @@  discard block
 block discarded – undo
2746 2882
 	 *
2747 2883
 	 * @return string
2748 2884
 	 */
2749
-	public function longestLifeFemaleAge() {
2885
+	public function longestLifeFemaleAge()
2886
+	{
2750 2887
 		return $this->longlifeQuery('age', 'F');
2751 2888
 	}
2752 2889
 
@@ -2755,7 +2892,8 @@  discard block
 block discarded – undo
2755 2892
 	 *
2756 2893
 	 * @return string
2757 2894
 	 */
2758
-	public function longestLifeFemaleName() {
2895
+	public function longestLifeFemaleName()
2896
+	{
2759 2897
 		return $this->longlifeQuery('name', 'F');
2760 2898
 	}
2761 2899
 
@@ -2766,7 +2904,8 @@  discard block
 block discarded – undo
2766 2904
 	 *
2767 2905
 	 * @return string
2768 2906
 	 */
2769
-	public function topTenOldestFemale($params = array()) {
2907
+	public function topTenOldestFemale($params = array())
2908
+	{
2770 2909
 		return $this->topTenOldestQuery('nolist', 'F', $params);
2771 2910
 	}
2772 2911
 
@@ -2777,7 +2916,8 @@  discard block
 block discarded – undo
2777 2916
 	 *
2778 2917
 	 * @return string
2779 2918
 	 */
2780
-	public function topTenOldestFemaleList($params = array()) {
2919
+	public function topTenOldestFemaleList($params = array())
2920
+	{
2781 2921
 		return $this->topTenOldestQuery('list', 'F', $params);
2782 2922
 	}
2783 2923
 
@@ -2788,7 +2928,8 @@  discard block
 block discarded – undo
2788 2928
 	 *
2789 2929
 	 * @return string
2790 2930
 	 */
2791
-	public function topTenOldestFemaleAlive($params = array()) {
2931
+	public function topTenOldestFemaleAlive($params = array())
2932
+	{
2792 2933
 		return $this->topTenOldestAliveQuery('nolist', 'F', $params);
2793 2934
 	}
2794 2935
 
@@ -2799,7 +2940,8 @@  discard block
 block discarded – undo
2799 2940
 	 *
2800 2941
 	 * @return string
2801 2942
 	 */
2802
-	public function topTenOldestFemaleListAlive($params = array()) {
2943
+	public function topTenOldestFemaleListAlive($params = array())
2944
+	{
2803 2945
 		return $this->topTenOldestAliveQuery('list', 'F', $params);
2804 2946
 	}
2805 2947
 
@@ -2810,7 +2952,8 @@  discard block
 block discarded – undo
2810 2952
 	 *
2811 2953
 	 * @return string
2812 2954
 	 */
2813
-	public function averageLifespanFemale($show_years = false) {
2955
+	public function averageLifespanFemale($show_years = false)
2956
+	{
2814 2957
 		return $this->averageLifespanQuery('F', $show_years);
2815 2958
 	}
2816 2959
 
@@ -2819,7 +2962,8 @@  discard block
 block discarded – undo
2819 2962
 	 *
2820 2963
 	 * @return string
2821 2964
 	 */
2822
-	public function longestLifeMale() {
2965
+	public function longestLifeMale()
2966
+	{
2823 2967
 		return $this->longlifeQuery('full', 'M');
2824 2968
 	}
2825 2969
 
@@ -2828,7 +2972,8 @@  discard block
 block discarded – undo
2828 2972
 	 *
2829 2973
 	 * @return string
2830 2974
 	 */
2831
-	public function longestLifeMaleAge() {
2975
+	public function longestLifeMaleAge()
2976
+	{
2832 2977
 		return $this->longlifeQuery('age', 'M');
2833 2978
 	}
2834 2979
 
@@ -2837,7 +2982,8 @@  discard block
 block discarded – undo
2837 2982
 	 *
2838 2983
 	 * @return string
2839 2984
 	 */
2840
-	public function longestLifeMaleName() {
2985
+	public function longestLifeMaleName()
2986
+	{
2841 2987
 		return $this->longlifeQuery('name', 'M');
2842 2988
 	}
2843 2989
 
@@ -2848,7 +2994,8 @@  discard block
 block discarded – undo
2848 2994
 	 *
2849 2995
 	 * @return string
2850 2996
 	 */
2851
-	public function topTenOldestMale($params = array()) {
2997
+	public function topTenOldestMale($params = array())
2998
+	{
2852 2999
 		return $this->topTenOldestQuery('nolist', 'M', $params);
2853 3000
 	}
2854 3001
 
@@ -2859,7 +3006,8 @@  discard block
 block discarded – undo
2859 3006
 	 *
2860 3007
 	 * @return string
2861 3008
 	 */
2862
-	public function topTenOldestMaleList($params = array()) {
3009
+	public function topTenOldestMaleList($params = array())
3010
+	{
2863 3011
 		return $this->topTenOldestQuery('list', 'M', $params);
2864 3012
 	}
2865 3013
 
@@ -2870,7 +3018,8 @@  discard block
 block discarded – undo
2870 3018
 	 *
2871 3019
 	 * @return string
2872 3020
 	 */
2873
-	public function topTenOldestMaleAlive($params = array()) {
3021
+	public function topTenOldestMaleAlive($params = array())
3022
+	{
2874 3023
 		return $this->topTenOldestAliveQuery('nolist', 'M', $params);
2875 3024
 	}
2876 3025
 
@@ -2881,7 +3030,8 @@  discard block
 block discarded – undo
2881 3030
 	 *
2882 3031
 	 * @return string
2883 3032
 	 */
2884
-	public function topTenOldestMaleListAlive($params = array()) {
3033
+	public function topTenOldestMaleListAlive($params = array())
3034
+	{
2885 3035
 		return $this->topTenOldestAliveQuery('list', 'M', $params);
2886 3036
 	}
2887 3037
 
@@ -2892,7 +3042,8 @@  discard block
 block discarded – undo
2892 3042
 	 *
2893 3043
 	 * @return string
2894 3044
 	 */
2895
-	public function averageLifespanMale($show_years = false) {
3045
+	public function averageLifespanMale($show_years = false)
3046
+	{
2896 3047
 		return $this->averageLifespanQuery('M', $show_years);
2897 3048
 	}
2898 3049
 
@@ -2905,7 +3056,8 @@  discard block
 block discarded – undo
2905 3056
 	 *
2906 3057
 	 * @return string
2907 3058
 	 */
2908
-	private function eventQuery($type, $direction, $facts) {
3059
+	private function eventQuery($type, $direction, $facts)
3060
+	{
2909 3061
 		$eventTypes = array(
2910 3062
 			'BIRT' => I18N::translate('birth'),
2911 3063
 			'DEAT' => I18N::translate('death'),
@@ -2982,7 +3134,8 @@  discard block
 block discarded – undo
2982 3134
 	 *
2983 3135
 	 * @return string
2984 3136
 	 */
2985
-	public function firstEvent() {
3137
+	public function firstEvent()
3138
+	{
2986 3139
 		return $this->eventQuery('full', 'ASC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
2987 3140
 	}
2988 3141
 
@@ -2991,7 +3144,8 @@  discard block
 block discarded – undo
2991 3144
 	 *
2992 3145
 	 * @return string
2993 3146
 	 */
2994
-	public function firstEventYear() {
3147
+	public function firstEventYear()
3148
+	{
2995 3149
 		return $this->eventQuery('year', 'ASC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
2996 3150
 	}
2997 3151
 
@@ -3000,7 +3154,8 @@  discard block
 block discarded – undo
3000 3154
 	 *
3001 3155
 	 * @return string
3002 3156
 	 */
3003
-	public function firstEventType() {
3157
+	public function firstEventType()
3158
+	{
3004 3159
 		return $this->eventQuery('type', 'ASC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
3005 3160
 	}
3006 3161
 
@@ -3009,7 +3164,8 @@  discard block
 block discarded – undo
3009 3164
 	 *
3010 3165
 	 * @return string
3011 3166
 	 */
3012
-	public function firstEventName() {
3167
+	public function firstEventName()
3168
+	{
3013 3169
 		return $this->eventQuery('name', 'ASC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
3014 3170
 	}
3015 3171
 
@@ -3018,7 +3174,8 @@  discard block
 block discarded – undo
3018 3174
 	 *
3019 3175
 	 * @return string
3020 3176
 	 */
3021
-	public function firstEventPlace() {
3177
+	public function firstEventPlace()
3178
+	{
3022 3179
 		return $this->eventQuery('place', 'ASC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
3023 3180
 	}
3024 3181
 
@@ -3027,7 +3184,8 @@  discard block
 block discarded – undo
3027 3184
 	 *
3028 3185
 	 * @return string
3029 3186
 	 */
3030
-	public function lastEvent() {
3187
+	public function lastEvent()
3188
+	{
3031 3189
 		return $this->eventQuery('full', 'DESC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
3032 3190
 	}
3033 3191
 
@@ -3036,7 +3194,8 @@  discard block
 block discarded – undo
3036 3194
 	 *
3037 3195
 	 * @return string
3038 3196
 	 */
3039
-	public function lastEventYear() {
3197
+	public function lastEventYear()
3198
+	{
3040 3199
 		return $this->eventQuery('year', 'DESC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
3041 3200
 	}
3042 3201
 
@@ -3045,7 +3204,8 @@  discard block
 block discarded – undo
3045 3204
 	 *
3046 3205
 	 * @return string
3047 3206
 	 */
3048
-	public function lastEventType() {
3207
+	public function lastEventType()
3208
+	{
3049 3209
 		return $this->eventQuery('type', 'DESC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
3050 3210
 	}
3051 3211
 
@@ -3054,7 +3214,8 @@  discard block
 block discarded – undo
3054 3214
 	 *
3055 3215
 	 * @return string
3056 3216
 	 */
3057
-	public function lastEventName() {
3217
+	public function lastEventName()
3218
+	{
3058 3219
 		return $this->eventQuery('name', 'DESC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
3059 3220
 	}
3060 3221
 
@@ -3063,7 +3224,8 @@  discard block
 block discarded – undo
3063 3224
 	 *
3064 3225
 	 * @return string
3065 3226
 	 */
3066
-	public function lastEventPlace() {
3227
+	public function lastEventPlace()
3228
+	{
3067 3229
 		return $this->eventQuery('place', 'DESC', WT_EVENTS_BIRT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV . '|' . WT_EVENTS_DEAT);
3068 3230
 	}
3069 3231
 
@@ -3077,7 +3239,8 @@  discard block
 block discarded – undo
3077 3239
 	 *
3078 3240
 	 * @return string
3079 3241
 	 */
3080
-	private function marriageQuery($type = 'full', $age_dir = 'ASC', $sex = 'F', $show_years = false) {
3242
+	private function marriageQuery($type = 'full', $age_dir = 'ASC', $sex = 'F', $show_years = false)
3243
+	{
3081 3244
 		if ($sex == 'F') {
3082 3245
 			$sex_field = 'f_wife';
3083 3246
 		} else {
@@ -3156,7 +3319,8 @@  discard block
 block discarded – undo
3156 3319
 	 *
3157 3320
 	 * @return string
3158 3321
 	 */
3159
-	private function ageOfMarriageQuery($type = 'list', $age_dir = 'ASC', $params = array()) {
3322
+	private function ageOfMarriageQuery($type = 'list', $age_dir = 'ASC', $params = array())
3323
+	{
3160 3324
 		if (isset($params[0])) {
3161 3325
 			$total = (int) $params[0];
3162 3326
 		} else {
@@ -3291,7 +3455,8 @@  discard block
 block discarded – undo
3291 3455
 	 *
3292 3456
 	 * @return string
3293 3457
 	 */
3294
-	private function ageBetweenSpousesQuery($type = 'list', $age_dir = 'DESC', $params = array()) {
3458
+	private function ageBetweenSpousesQuery($type = 'list', $age_dir = 'DESC', $params = array())
3459
+	{
3295 3460
 		if (isset($params[0])) {
3296 3461
 			$total = (int) $params[0];
3297 3462
 		} else {
@@ -3376,7 +3541,8 @@  discard block
 block discarded – undo
3376 3541
 	 *
3377 3542
 	 * @return string
3378 3543
 	 */
3379
-	private function parentsQuery($type = 'full', $age_dir = 'ASC', $sex = 'F', $show_years = false) {
3544
+	private function parentsQuery($type = 'full', $age_dir = 'ASC', $sex = 'F', $show_years = false)
3545
+	{
3380 3546
 		if ($sex == 'F') {
3381 3547
 			$sex_field = 'WIFE';
3382 3548
 		} else {
@@ -3456,7 +3622,8 @@  discard block
 block discarded – undo
3456 3622
 	 *
3457 3623
 	 * @return string|array
3458 3624
 	 */
3459
-	public function statsMarrQuery($simple = true, $first = false, $year1 = -1, $year2 = -1, $params = array()) {
3625
+	public function statsMarrQuery($simple = true, $first = false, $year1 = -1, $year2 = -1, $params = array())
3626
+	{
3460 3627
 		$WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
3461 3628
 		$WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
3462 3629
 		$WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
@@ -3554,7 +3721,8 @@  discard block
 block discarded – undo
3554 3721
 	 *
3555 3722
 	 * @return string|array
3556 3723
 	 */
3557
-	private function statsDivQuery($simple = true, $first = false, $year1 = -1, $year2 = -1, $params = array()) {
3724
+	private function statsDivQuery($simple = true, $first = false, $year1 = -1, $year2 = -1, $params = array())
3725
+	{
3558 3726
 		$WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
3559 3727
 		$WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
3560 3728
 		$WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
@@ -3645,7 +3813,8 @@  discard block
 block discarded – undo
3645 3813
 	 *
3646 3814
 	 * @return string
3647 3815
 	 */
3648
-	public function firstMarriage() {
3816
+	public function firstMarriage()
3817
+	{
3649 3818
 		return $this->mortalityQuery('full', 'ASC', 'MARR');
3650 3819
 	}
3651 3820
 
@@ -3654,7 +3823,8 @@  discard block
 block discarded – undo
3654 3823
 	 *
3655 3824
 	 * @return string
3656 3825
 	 */
3657
-	public function firstMarriageYear() {
3826
+	public function firstMarriageYear()
3827
+	{
3658 3828
 		return $this->mortalityQuery('year', 'ASC', 'MARR');
3659 3829
 	}
3660 3830
 
@@ -3663,7 +3833,8 @@  discard block
 block discarded – undo
3663 3833
 	 *
3664 3834
 	 * @return string
3665 3835
 	 */
3666
-	public function firstMarriageName() {
3836
+	public function firstMarriageName()
3837
+	{
3667 3838
 		return $this->mortalityQuery('name', 'ASC', 'MARR');
3668 3839
 	}
3669 3840
 
@@ -3672,7 +3843,8 @@  discard block
 block discarded – undo
3672 3843
 	 *
3673 3844
 	 * @return string
3674 3845
 	 */
3675
-	public function firstMarriagePlace() {
3846
+	public function firstMarriagePlace()
3847
+	{
3676 3848
 		return $this->mortalityQuery('place', 'ASC', 'MARR');
3677 3849
 	}
3678 3850
 
@@ -3681,7 +3853,8 @@  discard block
 block discarded – undo
3681 3853
 	 *
3682 3854
 	 * @return string
3683 3855
 	 */
3684
-	public function lastMarriage() {
3856
+	public function lastMarriage()
3857
+	{
3685 3858
 		return $this->mortalityQuery('full', 'DESC', 'MARR');
3686 3859
 	}
3687 3860
 
@@ -3690,7 +3863,8 @@  discard block
 block discarded – undo
3690 3863
 	 *
3691 3864
 	 * @return string
3692 3865
 	 */
3693
-	public function lastMarriageYear() {
3866
+	public function lastMarriageYear()
3867
+	{
3694 3868
 		return $this->mortalityQuery('year', 'DESC', 'MARR');
3695 3869
 	}
3696 3870
 
@@ -3699,7 +3873,8 @@  discard block
 block discarded – undo
3699 3873
 	 *
3700 3874
 	 * @return string
3701 3875
 	 */
3702
-	public function lastMarriageName() {
3876
+	public function lastMarriageName()
3877
+	{
3703 3878
 		return $this->mortalityQuery('name', 'DESC', 'MARR');
3704 3879
 	}
3705 3880
 
@@ -3708,7 +3883,8 @@  discard block
 block discarded – undo
3708 3883
 	 *
3709 3884
 	 * @return string
3710 3885
 	 */
3711
-	public function lastMarriagePlace() {
3886
+	public function lastMarriagePlace()
3887
+	{
3712 3888
 		return $this->mortalityQuery('place', 'DESC', 'MARR');
3713 3889
 	}
3714 3890
 
@@ -3719,7 +3895,8 @@  discard block
 block discarded – undo
3719 3895
 	 *
3720 3896
 	 * @return string
3721 3897
 	 */
3722
-	public function statsMarr($params = array()) {
3898
+	public function statsMarr($params = array())
3899
+	{
3723 3900
 		return $this->statsMarrQuery(true, false, -1, -1, $params);
3724 3901
 	}
3725 3902
 
@@ -3728,7 +3905,8 @@  discard block
 block discarded – undo
3728 3905
 	 *
3729 3906
 	 * @return string
3730 3907
 	 */
3731
-	public function firstDivorce() {
3908
+	public function firstDivorce()
3909
+	{
3732 3910
 		return $this->mortalityQuery('full', 'ASC', 'DIV');
3733 3911
 	}
3734 3912
 
@@ -3737,7 +3915,8 @@  discard block
 block discarded – undo
3737 3915
 	 *
3738 3916
 	 * @return string
3739 3917
 	 */
3740
-	public function firstDivorceYear() {
3918
+	public function firstDivorceYear()
3919
+	{
3741 3920
 		return $this->mortalityQuery('year', 'ASC', 'DIV');
3742 3921
 	}
3743 3922
 
@@ -3746,7 +3925,8 @@  discard block
 block discarded – undo
3746 3925
 	 *
3747 3926
 	 * @return string
3748 3927
 	 */
3749
-	public function firstDivorceName() {
3928
+	public function firstDivorceName()
3929
+	{
3750 3930
 		return $this->mortalityQuery('name', 'ASC', 'DIV');
3751 3931
 	}
3752 3932
 
@@ -3755,7 +3935,8 @@  discard block
 block discarded – undo
3755 3935
 	 *
3756 3936
 	 * @return string
3757 3937
 	 */
3758
-	public function firstDivorcePlace() {
3938
+	public function firstDivorcePlace()
3939
+	{
3759 3940
 		return $this->mortalityQuery('place', 'ASC', 'DIV');
3760 3941
 	}
3761 3942
 
@@ -3764,7 +3945,8 @@  discard block
 block discarded – undo
3764 3945
 	 *
3765 3946
 	 * @return string
3766 3947
 	 */
3767
-	public function lastDivorce() {
3948
+	public function lastDivorce()
3949
+	{
3768 3950
 		return $this->mortalityQuery('full', 'DESC', 'DIV');
3769 3951
 	}
3770 3952
 
@@ -3773,7 +3955,8 @@  discard block
 block discarded – undo
3773 3955
 	 *
3774 3956
 	 * @return string
3775 3957
 	 */
3776
-	public function lastDivorceYear() {
3958
+	public function lastDivorceYear()
3959
+	{
3777 3960
 		return $this->mortalityQuery('year', 'DESC', 'DIV');
3778 3961
 	}
3779 3962
 
@@ -3782,7 +3965,8 @@  discard block
 block discarded – undo
3782 3965
 	 *
3783 3966
 	 * @return string
3784 3967
 	 */
3785
-	public function lastDivorceName() {
3968
+	public function lastDivorceName()
3969
+	{
3786 3970
 		return $this->mortalityQuery('name', 'DESC', 'DIV');
3787 3971
 	}
3788 3972
 
@@ -3791,7 +3975,8 @@  discard block
 block discarded – undo
3791 3975
 	 *
3792 3976
 	 * @return string
3793 3977
 	 */
3794
-	public function lastDivorcePlace() {
3978
+	public function lastDivorcePlace()
3979
+	{
3795 3980
 		return $this->mortalityQuery('place', 'DESC', 'DIV');
3796 3981
 	}
3797 3982
 
@@ -3802,7 +3987,8 @@  discard block
 block discarded – undo
3802 3987
 	 *
3803 3988
 	 * @return string
3804 3989
 	 */
3805
-	public function statsDiv($params = array()) {
3990
+	public function statsDiv($params = array())
3991
+	{
3806 3992
 		return $this->statsDivQuery(true, false, -1, -1, $params);
3807 3993
 	}
3808 3994
 
@@ -3817,7 +4003,8 @@  discard block
 block discarded – undo
3817 4003
 	 *
3818 4004
 	 * @return array|string
3819 4005
 	 */
3820
-	public function statsMarrAgeQuery($simple = true, $sex = 'M', $year1 = -1, $year2 = -1, $params = array()) {
4006
+	public function statsMarrAgeQuery($simple = true, $sex = 'M', $year1 = -1, $year2 = -1, $params = array())
4007
+	{
3821 4008
 		if ($simple) {
3822 4009
 			if (isset($params[0]) && $params[0] != '') {
3823 4010
 				$size = strtolower($params[0]);
@@ -3983,7 +4170,8 @@  discard block
 block discarded – undo
3983 4170
 	 *
3984 4171
 	 * @return string
3985 4172
 	 */
3986
-	public function youngestMarriageFemale() {
4173
+	public function youngestMarriageFemale()
4174
+	{
3987 4175
 		return $this->marriageQuery('full', 'ASC', 'F', false);
3988 4176
 	}
3989 4177
 
@@ -3992,7 +4180,8 @@  discard block
 block discarded – undo
3992 4180
 	 *
3993 4181
 	 * @return string
3994 4182
 	 */
3995
-	public function youngestMarriageFemaleName() {
4183
+	public function youngestMarriageFemaleName()
4184
+	{
3996 4185
 		return $this->marriageQuery('name', 'ASC', 'F', false);
3997 4186
 	}
3998 4187
 
@@ -4003,7 +4192,8 @@  discard block
 block discarded – undo
4003 4192
 	 *
4004 4193
 	 * @return string
4005 4194
 	 */
4006
-	public function youngestMarriageFemaleAge($show_years = false) {
4195
+	public function youngestMarriageFemaleAge($show_years = false)
4196
+	{
4007 4197
 		return $this->marriageQuery('age', 'ASC', 'F', $show_years);
4008 4198
 	}
4009 4199
 
@@ -4012,7 +4202,8 @@  discard block
 block discarded – undo
4012 4202
 	 *
4013 4203
 	 * @return string
4014 4204
 	 */
4015
-	public function oldestMarriageFemale() {
4205
+	public function oldestMarriageFemale()
4206
+	{
4016 4207
 		return $this->marriageQuery('full', 'DESC', 'F', false);
4017 4208
 	}
4018 4209
 
@@ -4021,7 +4212,8 @@  discard block
 block discarded – undo
4021 4212
 	 *
4022 4213
 	 * @return string
4023 4214
 	 */
4024
-	public function oldestMarriageFemaleName() {
4215
+	public function oldestMarriageFemaleName()
4216
+	{
4025 4217
 		return $this->marriageQuery('name', 'DESC', 'F', false);
4026 4218
 	}
4027 4219
 
@@ -4032,7 +4224,8 @@  discard block
 block discarded – undo
4032 4224
 	 *
4033 4225
 	 * @return string
4034 4226
 	 */
4035
-	public function oldestMarriageFemaleAge($show_years = false) {
4227
+	public function oldestMarriageFemaleAge($show_years = false)
4228
+	{
4036 4229
 		return $this->marriageQuery('age', 'DESC', 'F', $show_years);
4037 4230
 	}
4038 4231
 
@@ -4041,7 +4234,8 @@  discard block
 block discarded – undo
4041 4234
 	 *
4042 4235
 	 * @return string
4043 4236
 	 */
4044
-	public function youngestMarriageMale() {
4237
+	public function youngestMarriageMale()
4238
+	{
4045 4239
 		return $this->marriageQuery('full', 'ASC', 'M', false);
4046 4240
 	}
4047 4241
 
@@ -4050,7 +4244,8 @@  discard block
 block discarded – undo
4050 4244
 	 *
4051 4245
 	 * @return string
4052 4246
 	 */
4053
-	public function youngestMarriageMaleName() {
4247
+	public function youngestMarriageMaleName()
4248
+	{
4054 4249
 		return $this->marriageQuery('name', 'ASC', 'M', false);
4055 4250
 	}
4056 4251
 
@@ -4061,7 +4256,8 @@  discard block
 block discarded – undo
4061 4256
 	 *
4062 4257
 	 * @return string
4063 4258
 	 */
4064
-	public function youngestMarriageMaleAge($show_years = false) {
4259
+	public function youngestMarriageMaleAge($show_years = false)
4260
+	{
4065 4261
 		return $this->marriageQuery('age', 'ASC', 'M', $show_years);
4066 4262
 	}
4067 4263
 
@@ -4070,7 +4266,8 @@  discard block
 block discarded – undo
4070 4266
 	 *
4071 4267
 	 * @return string
4072 4268
 	 */
4073
-	public function oldestMarriageMale() {
4269
+	public function oldestMarriageMale()
4270
+	{
4074 4271
 		return $this->marriageQuery('full', 'DESC', 'M', false);
4075 4272
 	}
4076 4273
 
@@ -4079,7 +4276,8 @@  discard block
 block discarded – undo
4079 4276
 	 *
4080 4277
 	 * @return string
4081 4278
 	 */
4082
-	public function oldestMarriageMaleName() {
4279
+	public function oldestMarriageMaleName()
4280
+	{
4083 4281
 		return $this->marriageQuery('name', 'DESC', 'M', false);
4084 4282
 	}
4085 4283
 
@@ -4090,7 +4288,8 @@  discard block
 block discarded – undo
4090 4288
 	 *
4091 4289
 	 * @return string
4092 4290
 	 */
4093
-	public function oldestMarriageMaleAge($show_years = false) {
4291
+	public function oldestMarriageMaleAge($show_years = false)
4292
+	{
4094 4293
 		return $this->marriageQuery('age', 'DESC', 'M', $show_years);
4095 4294
 	}
4096 4295
 
@@ -4101,7 +4300,8 @@  discard block
 block discarded – undo
4101 4300
 	 *
4102 4301
 	 * @return string
4103 4302
 	 */
4104
-	public function statsMarrAge($params = array()) {
4303
+	public function statsMarrAge($params = array())
4304
+	{
4105 4305
 		return $this->statsMarrAgeQuery(true, 'BOTH', -1, -1, $params);
4106 4306
 	}
4107 4307
 
@@ -4112,7 +4312,8 @@  discard block
 block discarded – undo
4112 4312
 	 *
4113 4313
 	 * @return string
4114 4314
 	 */
4115
-	public function ageBetweenSpousesMF($params = array()) {
4315
+	public function ageBetweenSpousesMF($params = array())
4316
+	{
4116 4317
 		return $this->ageBetweenSpousesQuery('nolist', 'DESC', $params);
4117 4318
 	}
4118 4319
 
@@ -4123,7 +4324,8 @@  discard block
 block discarded – undo
4123 4324
 	 *
4124 4325
 	 * @return string
4125 4326
 	 */
4126
-	public function ageBetweenSpousesMFList($params = array()) {
4327
+	public function ageBetweenSpousesMFList($params = array())
4328
+	{
4127 4329
 		return $this->ageBetweenSpousesQuery('list', 'DESC', $params);
4128 4330
 	}
4129 4331
 
@@ -4134,7 +4336,8 @@  discard block
 block discarded – undo
4134 4336
 	 *
4135 4337
 	 * @return string
4136 4338
 	 */
4137
-	public function ageBetweenSpousesFM($params = array()) {
4339
+	public function ageBetweenSpousesFM($params = array())
4340
+	{
4138 4341
 		return $this->ageBetweenSpousesQuery('nolist', 'ASC', $params);
4139 4342
 	}
4140 4343
 
@@ -4145,7 +4348,8 @@  discard block
 block discarded – undo
4145 4348
 	 *
4146 4349
 	 * @return string
4147 4350
 	 */
4148
-	public function ageBetweenSpousesFMList($params = array()) {
4351
+	public function ageBetweenSpousesFMList($params = array())
4352
+	{
4149 4353
 		return $this->ageBetweenSpousesQuery('list', 'ASC', $params);
4150 4354
 	}
4151 4355
 
@@ -4154,7 +4358,8 @@  discard block
 block discarded – undo
4154 4358
 	 *
4155 4359
 	 * @return string
4156 4360
 	 */
4157
-	public function topAgeOfMarriageFamily() {
4361
+	public function topAgeOfMarriageFamily()
4362
+	{
4158 4363
 		return $this->ageOfMarriageQuery('name', 'DESC', array('1'));
4159 4364
 	}
4160 4365
 
@@ -4163,7 +4368,8 @@  discard block
 block discarded – undo
4163 4368
 	 *
4164 4369
 	 * @return string
4165 4370
 	 */
4166
-	public function topAgeOfMarriage() {
4371
+	public function topAgeOfMarriage()
4372
+	{
4167 4373
 		return $this->ageOfMarriageQuery('age', 'DESC', array('1'));
4168 4374
 	}
4169 4375
 
@@ -4174,7 +4380,8 @@  discard block
 block discarded – undo
4174 4380
 	 *
4175 4381
 	 * @return string
4176 4382
 	 */
4177
-	public function topAgeOfMarriageFamilies($params = array()) {
4383
+	public function topAgeOfMarriageFamilies($params = array())
4384
+	{
4178 4385
 		return $this->ageOfMarriageQuery('nolist', 'DESC', $params);
4179 4386
 	}
4180 4387
 
@@ -4185,7 +4392,8 @@  discard block
 block discarded – undo
4185 4392
 	 *
4186 4393
 	 * @return string
4187 4394
 	 */
4188
-	public function topAgeOfMarriageFamiliesList($params = array()) {
4395
+	public function topAgeOfMarriageFamiliesList($params = array())
4396
+	{
4189 4397
 		return $this->ageOfMarriageQuery('list', 'DESC', $params);
4190 4398
 	}
4191 4399
 
@@ -4194,7 +4402,8 @@  discard block
 block discarded – undo
4194 4402
 	 *
4195 4403
 	 * @return string
4196 4404
 	 */
4197
-	public function minAgeOfMarriageFamily() {
4405
+	public function minAgeOfMarriageFamily()
4406
+	{
4198 4407
 		return $this->ageOfMarriageQuery('name', 'ASC', array('1'));
4199 4408
 	}
4200 4409
 
@@ -4203,7 +4412,8 @@  discard block
 block discarded – undo
4203 4412
 	 *
4204 4413
 	 * @return string
4205 4414
 	 */
4206
-	public function minAgeOfMarriage() {
4415
+	public function minAgeOfMarriage()
4416
+	{
4207 4417
 		return $this->ageOfMarriageQuery('age', 'ASC', array('1'));
4208 4418
 	}
4209 4419
 
@@ -4214,7 +4424,8 @@  discard block
 block discarded – undo
4214 4424
 	 *
4215 4425
 	 * @return string
4216 4426
 	 */
4217
-	public function minAgeOfMarriageFamilies($params = array()) {
4427
+	public function minAgeOfMarriageFamilies($params = array())
4428
+	{
4218 4429
 		return $this->ageOfMarriageQuery('nolist', 'ASC', $params);
4219 4430
 	}
4220 4431
 
@@ -4225,7 +4436,8 @@  discard block
 block discarded – undo
4225 4436
 	 *
4226 4437
 	 * @return string
4227 4438
 	 */
4228
-	public function minAgeOfMarriageFamiliesList($params = array()) {
4439
+	public function minAgeOfMarriageFamiliesList($params = array())
4440
+	{
4229 4441
 		return $this->ageOfMarriageQuery('list', 'ASC', $params);
4230 4442
 	}
4231 4443
 
@@ -4234,7 +4446,8 @@  discard block
 block discarded – undo
4234 4446
 	 *
4235 4447
 	 * @return string
4236 4448
 	 */
4237
-	public function youngestMother() {
4449
+	public function youngestMother()
4450
+	{
4238 4451
 		return $this->parentsQuery('full', 'ASC', 'F');
4239 4452
 	}
4240 4453
 
@@ -4243,7 +4456,8 @@  discard block
 block discarded – undo
4243 4456
 	 *
4244 4457
 	 * @return string
4245 4458
 	 */
4246
-	public function youngestMotherName() {
4459
+	public function youngestMotherName()
4460
+	{
4247 4461
 		return $this->parentsQuery('name', 'ASC', 'F');
4248 4462
 	}
4249 4463
 
@@ -4254,7 +4468,8 @@  discard block
 block discarded – undo
4254 4468
 	 *
4255 4469
 	 * @return string
4256 4470
 	 */
4257
-	public function youngestMotherAge($show_years = false) {
4471
+	public function youngestMotherAge($show_years = false)
4472
+	{
4258 4473
 		return $this->parentsQuery('age', 'ASC', 'F', $show_years);
4259 4474
 	}
4260 4475
 
@@ -4263,7 +4478,8 @@  discard block
 block discarded – undo
4263 4478
 	 *
4264 4479
 	 * @return string
4265 4480
 	 */
4266
-	public function oldestMother() {
4481
+	public function oldestMother()
4482
+	{
4267 4483
 		return $this->parentsQuery('full', 'DESC', 'F');
4268 4484
 	}
4269 4485
 
@@ -4272,7 +4488,8 @@  discard block
 block discarded – undo
4272 4488
 	 *
4273 4489
 	 * @return string
4274 4490
 	 */
4275
-	public function oldestMotherName() {
4491
+	public function oldestMotherName()
4492
+	{
4276 4493
 		return $this->parentsQuery('name', 'DESC', 'F');
4277 4494
 	}
4278 4495
 
@@ -4283,7 +4500,8 @@  discard block
 block discarded – undo
4283 4500
 	 *
4284 4501
 	 * @return string
4285 4502
 	 */
4286
-	public function oldestMotherAge($show_years = false) {
4503
+	public function oldestMotherAge($show_years = false)
4504
+	{
4287 4505
 		return $this->parentsQuery('age', 'DESC', 'F', $show_years);
4288 4506
 	}
4289 4507
 
@@ -4292,7 +4510,8 @@  discard block
 block discarded – undo
4292 4510
 	 *
4293 4511
 	 * @return string
4294 4512
 	 */
4295
-	public function youngestFather() {
4513
+	public function youngestFather()
4514
+	{
4296 4515
 		return $this->parentsQuery('full', 'ASC', 'M');
4297 4516
 	}
4298 4517
 
@@ -4301,7 +4520,8 @@  discard block
 block discarded – undo
4301 4520
 	 *
4302 4521
 	 * @return string
4303 4522
 	 */
4304
-	public function youngestFatherName() {
4523
+	public function youngestFatherName()
4524
+	{
4305 4525
 		return $this->parentsQuery('name', 'ASC', 'M');
4306 4526
 	}
4307 4527
 
@@ -4312,7 +4532,8 @@  discard block
 block discarded – undo
4312 4532
 	 *
4313 4533
 	 * @return string
4314 4534
 	 */
4315
-	public function youngestFatherAge($show_years = false) {
4535
+	public function youngestFatherAge($show_years = false)
4536
+	{
4316 4537
 		return $this->parentsQuery('age', 'ASC', 'M', $show_years);
4317 4538
 	}
4318 4539
 
@@ -4321,7 +4542,8 @@  discard block
 block discarded – undo
4321 4542
 	 *
4322 4543
 	 * @return string
4323 4544
 	 */
4324
-	public function oldestFather() {
4545
+	public function oldestFather()
4546
+	{
4325 4547
 		return $this->parentsQuery('full', 'DESC', 'M');
4326 4548
 	}
4327 4549
 
@@ -4330,7 +4552,8 @@  discard block
 block discarded – undo
4330 4552
 	 *
4331 4553
 	 * @return string
4332 4554
 	 */
4333
-	public function oldestFatherName() {
4555
+	public function oldestFatherName()
4556
+	{
4334 4557
 		return $this->parentsQuery('name', 'DESC', 'M');
4335 4558
 	}
4336 4559
 
@@ -4341,7 +4564,8 @@  discard block
 block discarded – undo
4341 4564
 	 *
4342 4565
 	 * @return string
4343 4566
 	 */
4344
-	public function oldestFatherAge($show_years = false) {
4567
+	public function oldestFatherAge($show_years = false)
4568
+	{
4345 4569
 		return $this->parentsQuery('age', 'DESC', 'M', $show_years);
4346 4570
 	}
4347 4571
 
@@ -4350,7 +4574,8 @@  discard block
 block discarded – undo
4350 4574
 	 *
4351 4575
 	 * @return string
4352 4576
 	 */
4353
-	public function totalMarriedMales() {
4577
+	public function totalMarriedMales()
4578
+	{
4354 4579
 		$n = Database::prepare("SELECT COUNT(DISTINCT f_husb) FROM `##families` WHERE f_file=? AND f_gedcom LIKE '%\\n1 MARR%'")
4355 4580
 			->execute(array($this->tree->getTreeId()))
4356 4581
 			->fetchOne();
@@ -4363,7 +4588,8 @@  discard block
 block discarded – undo
4363 4588
 	 *
4364 4589
 	 * @return string
4365 4590
 	 */
4366
-	public function totalMarriedFemales() {
4591
+	public function totalMarriedFemales()
4592
+	{
4367 4593
 		$n = Database::prepare("SELECT COUNT(DISTINCT f_wife) FROM `##families` WHERE f_file=? AND f_gedcom LIKE '%\\n1 MARR%'")
4368 4594
 			->execute(array($this->tree->getTreeId()))
4369 4595
 			->fetchOne();
@@ -4378,7 +4604,8 @@  discard block
 block discarded – undo
4378 4604
 	 *
4379 4605
 	 * @return string
4380 4606
 	 */
4381
-	private function familyQuery($type = 'full') {
4607
+	private function familyQuery($type = 'full')
4608
+	{
4382 4609
 		$rows = $this->runSql(
4383 4610
 			" SELECT f_numchil AS tot, f_id AS id" .
4384 4611
 			" FROM `##families`" .
@@ -4424,7 +4651,8 @@  discard block
 block discarded – undo
4424 4651
 	 *
4425 4652
 	 * @return string
4426 4653
 	 */
4427
-	private function topTenFamilyQuery($type = 'list', $params = array()) {
4654
+	private function topTenFamilyQuery($type = 'list', $params = array())
4655
+	{
4428 4656
 		if (isset($params[0])) {
4429 4657
 			$total = (int) $params[0];
4430 4658
 		} else {
@@ -4482,7 +4710,8 @@  discard block
 block discarded – undo
4482 4710
 	 *
4483 4711
 	 * @return string
4484 4712
 	 */
4485
-	private function ageBetweenSiblingsQuery($type = 'list', $params = array()) {
4713
+	private function ageBetweenSiblingsQuery($type = 'list', $params = array())
4714
+	{
4486 4715
 		if (isset($params[0])) {
4487 4716
 			$total = (int) $params[0];
4488 4717
 		} else {
@@ -4611,7 +4840,8 @@  discard block
 block discarded – undo
4611 4840
 	 *
4612 4841
 	 * @return string|string[][]
4613 4842
 	 */
4614
-	public function monthFirstChildQuery($simple = true, $sex = false, $year1 = -1, $year2 = -1, $params = array()) {
4843
+	public function monthFirstChildQuery($simple = true, $sex = false, $year1 = -1, $year2 = -1, $params = array())
4844
+	{
4615 4845
 		$WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
4616 4846
 		$WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
4617 4847
 		$WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
@@ -4743,7 +4973,8 @@  discard block
 block discarded – undo
4743 4973
 	 *
4744 4974
 	 * @return string
4745 4975
 	 */
4746
-	public function largestFamily() {
4976
+	public function largestFamily()
4977
+	{
4747 4978
 		return $this->familyQuery('full');
4748 4979
 	}
4749 4980
 
@@ -4752,7 +4983,8 @@  discard block
 block discarded – undo
4752 4983
 	 *
4753 4984
 	 * @return string
4754 4985
 	 */
4755
-	public function largestFamilySize() {
4986
+	public function largestFamilySize()
4987
+	{
4756 4988
 		return $this->familyQuery('size');
4757 4989
 	}
4758 4990
 
@@ -4761,7 +4993,8 @@  discard block
 block discarded – undo
4761 4993
 	 *
4762 4994
 	 * @return string
4763 4995
 	 */
4764
-	public function largestFamilyName() {
4996
+	public function largestFamilyName()
4997
+	{
4765 4998
 		return $this->familyQuery('name');
4766 4999
 	}
4767 5000
 
@@ -4772,7 +5005,8 @@  discard block
 block discarded – undo
4772 5005
 	 *
4773 5006
 	 * @return string
4774 5007
 	 */
4775
-	public function topTenLargestFamily($params = array()) {
5008
+	public function topTenLargestFamily($params = array())
5009
+	{
4776 5010
 		return $this->topTenFamilyQuery('nolist', $params);
4777 5011
 	}
4778 5012
 
@@ -4783,7 +5017,8 @@  discard block
 block discarded – undo
4783 5017
 	 *
4784 5018
 	 * @return string
4785 5019
 	 */
4786
-	public function topTenLargestFamilyList($params = array()) {
5020
+	public function topTenLargestFamilyList($params = array())
5021
+	{
4787 5022
 		return $this->topTenFamilyQuery('list', $params);
4788 5023
 	}
4789 5024
 
@@ -4794,7 +5029,8 @@  discard block
 block discarded – undo
4794 5029
 	 *
4795 5030
 	 * @return string
4796 5031
 	 */
4797
-	public function chartLargestFamilies($params = array()) {
5032
+	public function chartLargestFamilies($params = array())
5033
+	{
4798 5034
 		$WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
4799 5035
 		$WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
4800 5036
 		$WT_STATS_L_CHART_X    = Theme::theme()->parameter('stats-large-chart-x');
@@ -4860,7 +5096,8 @@  discard block
 block discarded – undo
4860 5096
 	 *
4861 5097
 	 * @return string
4862 5098
 	 */
4863
-	public function totalChildren() {
5099
+	public function totalChildren()
5100
+	{
4864 5101
 		$rows = $this->runSql("SELECT SUM(f_numchil) AS tot FROM `##families` WHERE f_file={$this->tree->getTreeId()}");
4865 5102
 
4866 5103
 		return I18N::number($rows[0]['tot']);
@@ -4871,7 +5108,8 @@  discard block
 block discarded – undo
4871 5108
 	 *
4872 5109
 	 * @return string
4873 5110
 	 */
4874
-	public function averageChildren() {
5111
+	public function averageChildren()
5112
+	{
4875 5113
 		$rows = $this->runSql("SELECT AVG(f_numchil) AS tot FROM `##families` WHERE f_file={$this->tree->getTreeId()}");
4876 5114
 
4877 5115
 		return I18N::number($rows[0]['tot'], 2);
@@ -4888,7 +5126,8 @@  discard block
 block discarded – undo
4888 5126
 	 *
4889 5127
 	 * @return string|string[][]
4890 5128
 	 */
4891
-	public function statsChildrenQuery($simple = true, $sex = 'BOTH', $year1 = -1, $year2 = -1, $params = array()) {
5129
+	public function statsChildrenQuery($simple = true, $sex = 'BOTH', $year1 = -1, $year2 = -1, $params = array())
5130
+	{
4892 5131
 		if ($simple) {
4893 5132
 			if (isset($params[0]) && $params[0] != '') {
4894 5133
 				$size = strtolower($params[0]);
@@ -4994,7 +5233,8 @@  discard block
 block discarded – undo
4994 5233
 	 *
4995 5234
 	 * @return string
4996 5235
 	 */
4997
-	public function statsChildren($params = array()) {
5236
+	public function statsChildren($params = array())
5237
+	{
4998 5238
 		return $this->statsChildrenQuery(true, 'BOTH', -1, -1, $params);
4999 5239
 	}
5000 5240
 
@@ -5005,7 +5245,8 @@  discard block
 block discarded – undo
5005 5245
 	 *
5006 5246
 	 * @return string
5007 5247
 	 */
5008
-	public function topAgeBetweenSiblingsName($params = array()) {
5248
+	public function topAgeBetweenSiblingsName($params = array())
5249
+	{
5009 5250
 		return $this->ageBetweenSiblingsQuery('name', $params);
5010 5251
 	}
5011 5252
 
@@ -5016,7 +5257,8 @@  discard block
 block discarded – undo
5016 5257
 	 *
5017 5258
 	 * @return string
5018 5259
 	 */
5019
-	public function topAgeBetweenSiblings($params = array()) {
5260
+	public function topAgeBetweenSiblings($params = array())
5261
+	{
5020 5262
 		return $this->ageBetweenSiblingsQuery('age', $params);
5021 5263
 	}
5022 5264
 
@@ -5027,7 +5269,8 @@  discard block
 block discarded – undo
5027 5269
 	 *
5028 5270
 	 * @return string
5029 5271
 	 */
5030
-	public function topAgeBetweenSiblingsFullName($params = array()) {
5272
+	public function topAgeBetweenSiblingsFullName($params = array())
5273
+	{
5031 5274
 		return $this->ageBetweenSiblingsQuery('nolist', $params);
5032 5275
 	}
5033 5276
 
@@ -5038,7 +5281,8 @@  discard block
 block discarded – undo
5038 5281
 	 *
5039 5282
 	 * @return string
5040 5283
 	 */
5041
-	public function topAgeBetweenSiblingsList($params = array()) {
5284
+	public function topAgeBetweenSiblingsList($params = array())
5285
+	{
5042 5286
 		return $this->ageBetweenSiblingsQuery('list', $params);
5043 5287
 	}
5044 5288
 
@@ -5047,7 +5291,8 @@  discard block
 block discarded – undo
5047 5291
 	 *
5048 5292
 	 * @return string
5049 5293
 	 */
5050
-	private function noChildrenFamiliesQuery() {
5294
+	private function noChildrenFamiliesQuery()
5295
+	{
5051 5296
 		$rows = $this->runSql(
5052 5297
 			" SELECT COUNT(*) AS tot" .
5053 5298
 			" FROM  `##families`" .
@@ -5061,7 +5306,8 @@  discard block
 block discarded – undo
5061 5306
 	 *
5062 5307
 	 * @return string
5063 5308
 	 */
5064
-	public function noChildrenFamilies() {
5309
+	public function noChildrenFamilies()
5310
+	{
5065 5311
 		return I18N::number($this->noChildrenFamiliesQuery());
5066 5312
 	}
5067 5313
 
@@ -5072,7 +5318,8 @@  discard block
 block discarded – undo
5072 5318
 	 *
5073 5319
 	 * @return string
5074 5320
 	 */
5075
-	public function noChildrenFamiliesList($params = array()) {
5321
+	public function noChildrenFamiliesList($params = array())
5322
+	{
5076 5323
 		if (isset($params[0]) && $params[0] != '') {
5077 5324
 			$type = strtolower($params[0]);
5078 5325
 		} else {
@@ -5118,7 +5365,8 @@  discard block
 block discarded – undo
5118 5365
 	 *
5119 5366
 	 * @return string
5120 5367
 	 */
5121
-	public function chartNoChildrenFamilies($params = array()) {
5368
+	public function chartNoChildrenFamilies($params = array())
5369
+	{
5122 5370
 		if (isset($params[0]) && $params[0] != '') {
5123 5371
 			$size = strtolower($params[0]);
5124 5372
 		} else {
@@ -5217,7 +5465,8 @@  discard block
 block discarded – undo
5217 5465
 	 *
5218 5466
 	 * @return string
5219 5467
 	 */
5220
-	private function topTenGrandFamilyQuery($type = 'list', $params = array()) {
5468
+	private function topTenGrandFamilyQuery($type = 'list', $params = array())
5469
+	{
5221 5470
 		if (isset($params[0])) {
5222 5471
 			$total = (int) $params[0];
5223 5472
 		} else {
@@ -5281,7 +5530,8 @@  discard block
 block discarded – undo
5281 5530
 	 *
5282 5531
 	 * @return string
5283 5532
 	 */
5284
-	public function topTenLargestGrandFamily($params = array()) {
5533
+	public function topTenLargestGrandFamily($params = array())
5534
+	{
5285 5535
 		return $this->topTenGrandFamilyQuery('nolist', $params);
5286 5536
 	}
5287 5537
 
@@ -5292,7 +5542,8 @@  discard block
 block discarded – undo
5292 5542
 	 *
5293 5543
 	 * @return string
5294 5544
 	 */
5295
-	public function topTenLargestGrandFamilyList($params = array()) {
5545
+	public function topTenLargestGrandFamilyList($params = array())
5546
+	{
5296 5547
 		return $this->topTenGrandFamilyQuery('list', $params);
5297 5548
 	}
5298 5549
 
@@ -5305,7 +5556,8 @@  discard block
 block discarded – undo
5305 5556
 	 *
5306 5557
 	 * @return string
5307 5558
 	 */
5308
-	private function commonSurnamesQuery($type = 'list', $show_tot = false, $params = array()) {
5559
+	private function commonSurnamesQuery($type = 'list', $show_tot = false, $params = array())
5560
+	{
5309 5561
 		$threshold          = empty($params[0]) ? 10 : (int) $params[0];
5310 5562
 		$number_of_surnames = empty($params[1]) ? 10 : (int) $params[1];
5311 5563
 		$sorting            = empty($params[2]) ? 'alpha' : $params[2];
@@ -5342,7 +5594,8 @@  discard block
 block discarded – undo
5342 5594
 	 *
5343 5595
 	 * @return string
5344 5596
 	 */
5345
-	public function getCommonSurname() {
5597
+	public function getCommonSurname()
5598
+	{
5346 5599
 		$surnames = array_keys(FunctionsDb::getTopSurnames($this->tree->getTreeId(), 1, 1));
5347 5600
 
5348 5601
 		return array_shift($surnames);
@@ -5355,7 +5608,8 @@  discard block
 block discarded – undo
5355 5608
 	 *
5356 5609
 	 * @return string
5357 5610
 	 */
5358
-	public function commonSurnames($params = array('', '', 'alpha')) {
5611
+	public function commonSurnames($params = array('', '', 'alpha'))
5612
+	{
5359 5613
 		return $this->commonSurnamesQuery('nolist', false, $params);
5360 5614
 	}
5361 5615
 
@@ -5366,7 +5620,8 @@  discard block
 block discarded – undo
5366 5620
 	 *
5367 5621
 	 * @return string
5368 5622
 	 */
5369
-	public function commonSurnamesTotals($params = array('', '', 'rcount')) {
5623
+	public function commonSurnamesTotals($params = array('', '', 'rcount'))
5624
+	{
5370 5625
 		return $this->commonSurnamesQuery('nolist', true, $params);
5371 5626
 	}
5372 5627
 
@@ -5377,7 +5632,8 @@  discard block
 block discarded – undo
5377 5632
 	 *
5378 5633
 	 * @return string
5379 5634
 	 */
5380
-	public function commonSurnamesList($params = array('', '', 'alpha')) {
5635
+	public function commonSurnamesList($params = array('', '', 'alpha'))
5636
+	{
5381 5637
 		return $this->commonSurnamesQuery('list', false, $params);
5382 5638
 	}
5383 5639
 
@@ -5388,7 +5644,8 @@  discard block
 block discarded – undo
5388 5644
 	 *
5389 5645
 	 * @return string
5390 5646
 	 */
5391
-	public function commonSurnamesListTotals($params = array('', '', 'rcount')) {
5647
+	public function commonSurnamesListTotals($params = array('', '', 'rcount'))
5648
+	{
5392 5649
 		return $this->commonSurnamesQuery('list', true, $params);
5393 5650
 	}
5394 5651
 
@@ -5399,7 +5656,8 @@  discard block
 block discarded – undo
5399 5656
 	 *
5400 5657
 	 * @return string
5401 5658
 	 */
5402
-	public function chartCommonSurnames($params = array()) {
5659
+	public function chartCommonSurnames($params = array())
5660
+	{
5403 5661
 		$WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
5404 5662
 		$WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
5405 5663
 		$WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
@@ -5468,7 +5726,8 @@  discard block
 block discarded – undo
5468 5726
 	 *
5469 5727
 	 * @return string
5470 5728
 	 */
5471
-	private function commonGivenQuery($sex = 'B', $type = 'list', $show_tot = false, $params = array()) {
5729
+	private function commonGivenQuery($sex = 'B', $type = 'list', $show_tot = false, $params = array())
5730
+	{
5472 5731
 		if (isset($params[0]) && $params[0] != '' && $params[0] >= 0) {
5473 5732
 			$threshold = (int) $params[0];
5474 5733
 		} else {
@@ -5596,7 +5855,8 @@  discard block
 block discarded – undo
5596 5855
 	 *
5597 5856
 	 * @return string
5598 5857
 	 */
5599
-	public function commonGiven($params = array(1, 10, 'alpha')) {
5858
+	public function commonGiven($params = array(1, 10, 'alpha'))
5859
+	{
5600 5860
 		return $this->commonGivenQuery('B', 'nolist', false, $params);
5601 5861
 	}
5602 5862
 
@@ -5607,7 +5867,8 @@  discard block
 block discarded – undo
5607 5867
 	 *
5608 5868
 	 * @return string
5609 5869
 	 */
5610
-	public function commonGivenTotals($params = array(1, 10, 'rcount')) {
5870
+	public function commonGivenTotals($params = array(1, 10, 'rcount'))
5871
+	{
5611 5872
 		return $this->commonGivenQuery('B', 'nolist', true, $params);
5612 5873
 	}
5613 5874
 
@@ -5618,7 +5879,8 @@  discard block
 block discarded – undo
5618 5879
 	 *
5619 5880
 	 * @return string
5620 5881
 	 */
5621
-	public function commonGivenList($params = array(1, 10, 'alpha')) {
5882
+	public function commonGivenList($params = array(1, 10, 'alpha'))
5883
+	{
5622 5884
 		return $this->commonGivenQuery('B', 'list', false, $params);
5623 5885
 	}
5624 5886
 
@@ -5629,7 +5891,8 @@  discard block
 block discarded – undo
5629 5891
 	 *
5630 5892
 	 * @return string
5631 5893
 	 */
5632
-	public function commonGivenListTotals($params = array(1, 10, 'rcount')) {
5894
+	public function commonGivenListTotals($params = array(1, 10, 'rcount'))
5895
+	{
5633 5896
 		return $this->commonGivenQuery('B', 'list', true, $params);
5634 5897
 	}
5635 5898
 
@@ -5640,7 +5903,8 @@  discard block
 block discarded – undo
5640 5903
 	 *
5641 5904
 	 * @return string
5642 5905
 	 */
5643
-	public function commonGivenTable($params = array(1, 10, 'rcount')) {
5906
+	public function commonGivenTable($params = array(1, 10, 'rcount'))
5907
+	{
5644 5908
 		return $this->commonGivenQuery('B', 'table', false, $params);
5645 5909
 	}
5646 5910
 
@@ -5651,7 +5915,8 @@  discard block
 block discarded – undo
5651 5915
 	 *
5652 5916
 	 * @return string
5653 5917
 	 */
5654
-	public function commonGivenFemale($params = array(1, 10, 'alpha')) {
5918
+	public function commonGivenFemale($params = array(1, 10, 'alpha'))
5919
+	{
5655 5920
 		return $this->commonGivenQuery('F', 'nolist', false, $params);
5656 5921
 	}
5657 5922
 
@@ -5662,7 +5927,8 @@  discard block
 block discarded – undo
5662 5927
 	 *
5663 5928
 	 * @return string
5664 5929
 	 */
5665
-	public function commonGivenFemaleTotals($params = array(1, 10, 'rcount')) {
5930
+	public function commonGivenFemaleTotals($params = array(1, 10, 'rcount'))
5931
+	{
5666 5932
 		return $this->commonGivenQuery('F', 'nolist', true, $params);
5667 5933
 	}
5668 5934
 
@@ -5673,7 +5939,8 @@  discard block
 block discarded – undo
5673 5939
 	 *
5674 5940
 	 * @return string
5675 5941
 	 */
5676
-	public function commonGivenFemaleList($params = array(1, 10, 'alpha')) {
5942
+	public function commonGivenFemaleList($params = array(1, 10, 'alpha'))
5943
+	{
5677 5944
 		return $this->commonGivenQuery('F', 'list', false, $params);
5678 5945
 	}
5679 5946
 
@@ -5684,7 +5951,8 @@  discard block
 block discarded – undo
5684 5951
 	 *
5685 5952
 	 * @return string
5686 5953
 	 */
5687
-	public function commonGivenFemaleListTotals($params = array(1, 10, 'rcount')) {
5954
+	public function commonGivenFemaleListTotals($params = array(1, 10, 'rcount'))
5955
+	{
5688 5956
 		return $this->commonGivenQuery('F', 'list', true, $params);
5689 5957
 	}
5690 5958
 
@@ -5695,7 +5963,8 @@  discard block
 block discarded – undo
5695 5963
 	 *
5696 5964
 	 * @return string
5697 5965
 	 */
5698
-	public function commonGivenFemaleTable($params = array(1, 10, 'rcount')) {
5966
+	public function commonGivenFemaleTable($params = array(1, 10, 'rcount'))
5967
+	{
5699 5968
 		return $this->commonGivenQuery('F', 'table', false, $params);
5700 5969
 	}
5701 5970
 
@@ -5706,7 +5975,8 @@  discard block
 block discarded – undo
5706 5975
 	 *
5707 5976
 	 * @return string
5708 5977
 	 */
5709
-	public function commonGivenMale($params = array(1, 10, 'alpha')) {
5978
+	public function commonGivenMale($params = array(1, 10, 'alpha'))
5979
+	{
5710 5980
 		return $this->commonGivenQuery('M', 'nolist', false, $params);
5711 5981
 	}
5712 5982
 
@@ -5717,7 +5987,8 @@  discard block
 block discarded – undo
5717 5987
 	 *
5718 5988
 	 * @return string
5719 5989
 	 */
5720
-	public function commonGivenMaleTotals($params = array(1, 10, 'rcount')) {
5990
+	public function commonGivenMaleTotals($params = array(1, 10, 'rcount'))
5991
+	{
5721 5992
 		return $this->commonGivenQuery('M', 'nolist', true, $params);
5722 5993
 	}
5723 5994
 
@@ -5728,7 +5999,8 @@  discard block
 block discarded – undo
5728 5999
 	 *
5729 6000
 	 * @return string
5730 6001
 	 */
5731
-	public function commonGivenMaleList($params = array(1, 10, 'alpha')) {
6002
+	public function commonGivenMaleList($params = array(1, 10, 'alpha'))
6003
+	{
5732 6004
 		return $this->commonGivenQuery('M', 'list', false, $params);
5733 6005
 	}
5734 6006
 
@@ -5739,7 +6011,8 @@  discard block
 block discarded – undo
5739 6011
 	 *
5740 6012
 	 * @return string
5741 6013
 	 */
5742
-	public function commonGivenMaleListTotals($params = array(1, 10, 'rcount')) {
6014
+	public function commonGivenMaleListTotals($params = array(1, 10, 'rcount'))
6015
+	{
5743 6016
 		return $this->commonGivenQuery('M', 'list', true, $params);
5744 6017
 	}
5745 6018
 
@@ -5750,7 +6023,8 @@  discard block
 block discarded – undo
5750 6023
 	 *
5751 6024
 	 * @return string
5752 6025
 	 */
5753
-	public function commonGivenMaleTable($params = array(1, 10, 'rcount')) {
6026
+	public function commonGivenMaleTable($params = array(1, 10, 'rcount'))
6027
+	{
5754 6028
 		return $this->commonGivenQuery('M', 'table', false, $params);
5755 6029
 	}
5756 6030
 
@@ -5761,7 +6035,8 @@  discard block
 block discarded – undo
5761 6035
 	 *
5762 6036
 	 * @return string
5763 6037
 	 */
5764
-	public function commonGivenUnknown($params = array(1, 10, 'alpha')) {
6038
+	public function commonGivenUnknown($params = array(1, 10, 'alpha'))
6039
+	{
5765 6040
 		return $this->commonGivenQuery('U', 'nolist', false, $params);
5766 6041
 	}
5767 6042
 
@@ -5772,7 +6047,8 @@  discard block
 block discarded – undo
5772 6047
 	 *
5773 6048
 	 * @return string
5774 6049
 	 */
5775
-	public function commonGivenUnknownTotals($params = array(1, 10, 'rcount')) {
6050
+	public function commonGivenUnknownTotals($params = array(1, 10, 'rcount'))
6051
+	{
5776 6052
 		return $this->commonGivenQuery('U', 'nolist', true, $params);
5777 6053
 	}
5778 6054
 
@@ -5783,7 +6059,8 @@  discard block
 block discarded – undo
5783 6059
 	 *
5784 6060
 	 * @return string
5785 6061
 	 */
5786
-	public function commonGivenUnknownList($params = array(1, 10, 'alpha')) {
6062
+	public function commonGivenUnknownList($params = array(1, 10, 'alpha'))
6063
+	{
5787 6064
 		return $this->commonGivenQuery('U', 'list', false, $params);
5788 6065
 	}
5789 6066
 
@@ -5794,7 +6071,8 @@  discard block
 block discarded – undo
5794 6071
 	 *
5795 6072
 	 * @return string
5796 6073
 	 */
5797
-	public function commonGivenUnknownListTotals($params = array(1, 10, 'rcount')) {
6074
+	public function commonGivenUnknownListTotals($params = array(1, 10, 'rcount'))
6075
+	{
5798 6076
 		return $this->commonGivenQuery('U', 'list', true, $params);
5799 6077
 	}
5800 6078
 
@@ -5805,7 +6083,8 @@  discard block
 block discarded – undo
5805 6083
 	 *
5806 6084
 	 * @return string
5807 6085
 	 */
5808
-	public function commonGivenUnknownTable($params = array(1, 10, 'rcount')) {
6086
+	public function commonGivenUnknownTable($params = array(1, 10, 'rcount'))
6087
+	{
5809 6088
 		return $this->commonGivenQuery('U', 'table', false, $params);
5810 6089
 	}
5811 6090
 
@@ -5816,7 +6095,8 @@  discard block
 block discarded – undo
5816 6095
 	 *
5817 6096
 	 * @return string
5818 6097
 	 */
5819
-	public function chartCommonGiven($params = array()) {
6098
+	public function chartCommonGiven($params = array())
6099
+	{
5820 6100
 		$WT_STATS_CHART_COLOR1 = Theme::theme()->parameter('distribution-chart-no-values');
5821 6101
 		$WT_STATS_CHART_COLOR2 = Theme::theme()->parameter('distribution-chart-high-values');
5822 6102
 		$WT_STATS_S_CHART_X    = Theme::theme()->parameter('stats-small-chart-x');
@@ -5884,7 +6164,8 @@  discard block
 block discarded – undo
5884 6164
 	 *
5885 6165
 	 * @return string
5886 6166
 	 */
5887
-	private function usersLoggedInQuery($type = 'nolist') {
6167
+	private function usersLoggedInQuery($type = 'nolist')
6168
+	{
5888 6169
 		$content = '';
5889 6170
 		// List active users
5890 6171
 		$NumAnonymous = 0;
@@ -5951,7 +6232,8 @@  discard block
 block discarded – undo
5951 6232
 	 *
5952 6233
 	 * @return int
5953 6234
 	 */
5954
-	private function usersLoggedInTotalQuery($type = 'all') {
6235
+	private function usersLoggedInTotalQuery($type = 'all')
6236
+	{
5955 6237
 		$anon    = 0;
5956 6238
 		$visible = 0;
5957 6239
 		foreach (User::allLoggedIn() as $user) {
@@ -5975,7 +6257,8 @@  discard block
 block discarded – undo
5975 6257
 	 *
5976 6258
 	 * @return string
5977 6259
 	 */
5978
-	public function usersLoggedIn() {
6260
+	public function usersLoggedIn()
6261
+	{
5979 6262
 		return $this->usersLoggedInQuery('nolist');
5980 6263
 	}
5981 6264
 
@@ -5984,7 +6267,8 @@  discard block
 block discarded – undo
5984 6267
 	 *
5985 6268
 	 * @return string
5986 6269
 	 */
5987
-	public function usersLoggedInList() {
6270
+	public function usersLoggedInList()
6271
+	{
5988 6272
 		return $this->usersLoggedInQuery('list');
5989 6273
 	}
5990 6274
 
@@ -5993,7 +6277,8 @@  discard block
 block discarded – undo
5993 6277
 	 *
5994 6278
 	 * @return int
5995 6279
 	 */
5996
-	public function usersLoggedInTotal() {
6280
+	public function usersLoggedInTotal()
6281
+	{
5997 6282
 		return $this->usersLoggedInTotalQuery('all');
5998 6283
 	}
5999 6284
 
@@ -6002,7 +6287,8 @@  discard block
 block discarded – undo
6002 6287
 	 *
6003 6288
 	 * @return int
6004 6289
 	 */
6005
-	public function usersLoggedInTotalAnon() {
6290
+	public function usersLoggedInTotalAnon()
6291
+	{
6006 6292
 		return $this->usersLoggedInTotalQuery('anon');
6007 6293
 	}
6008 6294
 
@@ -6011,7 +6297,8 @@  discard block
 block discarded – undo
6011 6297
 	 *
6012 6298
 	 * @return int
6013 6299
 	 */
6014
-	public function usersLoggedInTotalVisible() {
6300
+	public function usersLoggedInTotalVisible()
6301
+	{
6015 6302
 		return $this->usersLoggedInTotalQuery('visible');
6016 6303
 	}
6017 6304
 
@@ -6020,7 +6307,8 @@  discard block
 block discarded – undo
6020 6307
 	 *
6021 6308
 	 * @return null|string
6022 6309
 	 */
6023
-	public function userId() {
6310
+	public function userId()
6311
+	{
6024 6312
 		return Auth::id();
6025 6313
 	}
6026 6314
 
@@ -6031,7 +6319,8 @@  discard block
 block discarded – undo
6031 6319
 	 *
6032 6320
 	 * @return string
6033 6321
 	 */
6034
-	public function userName($params = array()) {
6322
+	public function userName($params = array())
6323
+	{
6035 6324
 		if (Auth::check()) {
6036 6325
 			return Filter::escapeHtml(Auth::user()->getUserName());
6037 6326
 		} elseif (isset($params[0]) && $params[0] != '') {
@@ -6047,7 +6336,8 @@  discard block
 block discarded – undo
6047 6336
 	 *
6048 6337
 	 * @return string
6049 6338
 	 */
6050
-	public function userFullName() {
6339
+	public function userFullName()
6340
+	{
6051 6341
 		return Auth::check() ? Auth::user()->getRealNameHtml() : '';
6052 6342
 	}
6053 6343
 
@@ -6059,7 +6349,8 @@  discard block
 block discarded – undo
6059 6349
 	 *
6060 6350
 	 * @return string
6061 6351
 	 */
6062
-	private function getLatestUserData($type = 'userid', $params = array()) {
6352
+	private function getLatestUserData($type = 'userid', $params = array())
6353
+	{
6063 6354
 		static $user_id = null;
6064 6355
 
6065 6356
 		if ($user_id === null) {
@@ -6113,7 +6404,8 @@  discard block
 block discarded – undo
6113 6404
 	 *
6114 6405
 	 * @return string
6115 6406
 	 */
6116
-	public function latestUserId() {
6407
+	public function latestUserId()
6408
+	{
6117 6409
 		return $this->getLatestUserData('userid');
6118 6410
 	}
6119 6411
 
@@ -6122,7 +6414,8 @@  discard block
 block discarded – undo
6122 6414
 	 *
6123 6415
 	 * @return string
6124 6416
 	 */
6125
-	public function latestUserName() {
6417
+	public function latestUserName()
6418
+	{
6126 6419
 		return $this->getLatestUserData('username');
6127 6420
 	}
6128 6421
 
@@ -6131,7 +6424,8 @@  discard block
 block discarded – undo
6131 6424
 	 *
6132 6425
 	 * @return string
6133 6426
 	 */
6134
-	public function latestUserFullName() {
6427
+	public function latestUserFullName()
6428
+	{
6135 6429
 		return $this->getLatestUserData('fullname');
6136 6430
 	}
6137 6431
 
@@ -6142,7 +6436,8 @@  discard block
 block discarded – undo
6142 6436
 	 *
6143 6437
 	 * @return string
6144 6438
 	 */
6145
-	public function latestUserRegDate($params = array()) {
6439
+	public function latestUserRegDate($params = array())
6440
+	{
6146 6441
 		return $this->getLatestUserData('regdate', $params);
6147 6442
 	}
6148 6443
 
@@ -6153,7 +6448,8 @@  discard block
 block discarded – undo
6153 6448
 	 *
6154 6449
 	 * @return string
6155 6450
 	 */
6156
-	public function latestUserRegTime($params = array()) {
6451
+	public function latestUserRegTime($params = array())
6452
+	{
6157 6453
 		return $this->getLatestUserData('regtime', $params);
6158 6454
 	}
6159 6455
 
@@ -6164,7 +6460,8 @@  discard block
 block discarded – undo
6164 6460
 	 *
6165 6461
 	 * @return string
6166 6462
 	 */
6167
-	public function latestUserLoggedin($params = array()) {
6463
+	public function latestUserLoggedin($params = array())
6464
+	{
6168 6465
 		return $this->getLatestUserData('loggedin', $params);
6169 6466
 	}
6170 6467
 
@@ -6173,7 +6470,8 @@  discard block
 block discarded – undo
6173 6470
 	 *
6174 6471
 	 * @return string
6175 6472
 	 */
6176
-	public function contactWebmaster() {
6473
+	public function contactWebmaster()
6474
+	{
6177 6475
 		$user_id = $this->tree->getPreference('WEBMASTER_USER_ID');
6178 6476
 		$user    = User::find($user_id);
6179 6477
 		if ($user) {
@@ -6188,7 +6486,8 @@  discard block
 block discarded – undo
6188 6486
 	 *
6189 6487
 	 * @return string
6190 6488
 	 */
6191
-	public function contactGedcom() {
6489
+	public function contactGedcom()
6490
+	{
6192 6491
 		$user_id = $this->tree->getPreference('CONTACT_USER_ID');
6193 6492
 		$user    = User::find($user_id);
6194 6493
 		if ($user) {
@@ -6203,7 +6502,8 @@  discard block
 block discarded – undo
6203 6502
 	 *
6204 6503
 	 * @return string
6205 6504
 	 */
6206
-	public function serverDate() {
6505
+	public function serverDate()
6506
+	{
6207 6507
 		return FunctionsDate::timestampToGedcomDate(WT_TIMESTAMP)->display();
6208 6508
 	}
6209 6509
 
@@ -6212,7 +6512,8 @@  discard block
 block discarded – undo
6212 6512
 	 *
6213 6513
 	 * @return string
6214 6514
 	 */
6215
-	public function serverTime() {
6515
+	public function serverTime()
6516
+	{
6216 6517
 		return date('g:i a');
6217 6518
 	}
6218 6519
 
@@ -6221,7 +6522,8 @@  discard block
 block discarded – undo
6221 6522
 	 *
6222 6523
 	 * @return string
6223 6524
 	 */
6224
-	public function serverTime24() {
6525
+	public function serverTime24()
6526
+	{
6225 6527
 		return date('G:i');
6226 6528
 	}
6227 6529
 
@@ -6230,7 +6532,8 @@  discard block
 block discarded – undo
6230 6532
 	 *
6231 6533
 	 * @return string
6232 6534
 	 */
6233
-	public function serverTimezone() {
6535
+	public function serverTimezone()
6536
+	{
6234 6537
 		return date('T');
6235 6538
 	}
6236 6539
 
@@ -6239,7 +6542,8 @@  discard block
 block discarded – undo
6239 6542
 	 *
6240 6543
 	 * @return string
6241 6544
 	 */
6242
-	public function browserDate() {
6545
+	public function browserDate()
6546
+	{
6243 6547
 		return FunctionsDate::timestampToGedcomDate(WT_TIMESTAMP + WT_TIMESTAMP_OFFSET)->display();
6244 6548
 	}
6245 6549
 
@@ -6248,7 +6552,8 @@  discard block
 block discarded – undo
6248 6552
 	 *
6249 6553
 	 * @return string
6250 6554
 	 */
6251
-	public function browserTime() {
6555
+	public function browserTime()
6556
+	{
6252 6557
 		return date(str_replace('%', '', I18N::timeFormat()), WT_TIMESTAMP + WT_TIMESTAMP_OFFSET);
6253 6558
 	}
6254 6559
 
@@ -6257,7 +6562,8 @@  discard block
 block discarded – undo
6257 6562
 	 *
6258 6563
 	 * @return string
6259 6564
 	 */
6260
-	public function browserTimezone() {
6565
+	public function browserTimezone()
6566
+	{
6261 6567
 		return date('T', WT_TIMESTAMP + WT_TIMESTAMP_OFFSET);
6262 6568
 	}
6263 6569
 
@@ -6266,7 +6572,8 @@  discard block
 block discarded – undo
6266 6572
 	 *
6267 6573
 	 * @return string
6268 6574
 	 */
6269
-	public function webtreesVersion() {
6575
+	public function webtreesVersion()
6576
+	{
6270 6577
 		return WT_VERSION;
6271 6578
 	}
6272 6579
 
@@ -6278,7 +6585,8 @@  discard block
 block discarded – undo
6278 6585
 	 *
6279 6586
 	 * @return string
6280 6587
 	 */
6281
-	private function hitCountQuery($page_name, $params) {
6588
+	private function hitCountQuery($page_name, $params)
6589
+	{
6282 6590
 		if (is_array($params) && isset($params[0]) && $params[0] != '') {
6283 6591
 			$page_parameter = $params[0];
6284 6592
 		} else {
@@ -6307,7 +6615,8 @@  discard block
 block discarded – undo
6307 6615
 	 *
6308 6616
 	 * @return string
6309 6617
 	 */
6310
-	public function hitCount($params = array()) {
6618
+	public function hitCount($params = array())
6619
+	{
6311 6620
 		return $this->hitCountQuery(null, $params);
6312 6621
 	}
6313 6622
 
@@ -6318,7 +6627,8 @@  discard block
 block discarded – undo
6318 6627
 	 *
6319 6628
 	 * @return string
6320 6629
 	 */
6321
-	public function hitCountUser($params = array()) {
6630
+	public function hitCountUser($params = array())
6631
+	{
6322 6632
 		return $this->hitCountQuery('index.php', $params);
6323 6633
 	}
6324 6634
 
@@ -6329,7 +6639,8 @@  discard block
 block discarded – undo
6329 6639
 	 *
6330 6640
 	 * @return string
6331 6641
 	 */
6332
-	public function hitCountIndi($params = array()) {
6642
+	public function hitCountIndi($params = array())
6643
+	{
6333 6644
 		return $this->hitCountQuery('individual.php', $params);
6334 6645
 	}
6335 6646
 
@@ -6340,7 +6651,8 @@  discard block
 block discarded – undo
6340 6651
 	 *
6341 6652
 	 * @return string
6342 6653
 	 */
6343
-	public function hitCountFam($params = array()) {
6654
+	public function hitCountFam($params = array())
6655
+	{
6344 6656
 		return $this->hitCountQuery('family.php', $params);
6345 6657
 	}
6346 6658
 
@@ -6351,7 +6663,8 @@  discard block
 block discarded – undo
6351 6663
 	 *
6352 6664
 	 * @return string
6353 6665
 	 */
6354
-	public function hitCountSour($params = array()) {
6666
+	public function hitCountSour($params = array())
6667
+	{
6355 6668
 		return $this->hitCountQuery('source.php', $params);
6356 6669
 	}
6357 6670
 
@@ -6362,7 +6675,8 @@  discard block
 block discarded – undo
6362 6675
 	 *
6363 6676
 	 * @return string
6364 6677
 	 */
6365
-	public function hitCountRepo($params = array()) {
6678
+	public function hitCountRepo($params = array())
6679
+	{
6366 6680
 		return $this->hitCountQuery('repo.php', $params);
6367 6681
 	}
6368 6682
 
@@ -6373,7 +6687,8 @@  discard block
 block discarded – undo
6373 6687
 	 *
6374 6688
 	 * @return string
6375 6689
 	 */
6376
-	public function hitCountNote($params = array()) {
6690
+	public function hitCountNote($params = array())
6691
+	{
6377 6692
 		return $this->hitCountQuery('note.php', $params);
6378 6693
 	}
6379 6694
 
@@ -6384,7 +6699,8 @@  discard block
 block discarded – undo
6384 6699
 	 *
6385 6700
 	 * @return string
6386 6701
 	 */
6387
-	public function hitCountObje($params = array()) {
6702
+	public function hitCountObje($params = array())
6703
+	{
6388 6704
 		return $this->hitCountQuery('mediaviewer.php', $params);
6389 6705
 	}
6390 6706
 
@@ -6397,7 +6713,8 @@  discard block
 block discarded – undo
6397 6713
 	 *
6398 6714
 	 * @return string
6399 6715
 	 */
6400
-	private function arrayToExtendedEncoding($a) {
6716
+	private function arrayToExtendedEncoding($a)
6717
+	{
6401 6718
 		$xencoding = WT_GOOGLE_CHART_ENCODING;
6402 6719
 
6403 6720
 		$encoding = '';
@@ -6421,7 +6738,8 @@  discard block
 block discarded – undo
6421 6738
 	 *
6422 6739
 	 * @return int
6423 6740
 	 */
6424
-	private function nameTotalSort($a, $b) {
6741
+	private function nameTotalSort($a, $b)
6742
+	{
6425 6743
 		return $a['match'] - $b['match'];
6426 6744
 	}
6427 6745
 
@@ -6433,7 +6751,8 @@  discard block
 block discarded – undo
6433 6751
 	 *
6434 6752
 	 * @return int
6435 6753
 	 */
6436
-	private function nameTotalReverseSort($a, $b) {
6754
+	private function nameTotalReverseSort($a, $b)
6755
+	{
6437 6756
 		return $b['match'] - $a['match'];
6438 6757
 	}
6439 6758
 
@@ -6444,7 +6763,8 @@  discard block
 block discarded – undo
6444 6763
 	 *
6445 6764
 	 * @return string[][]
6446 6765
 	 */
6447
-	private function runSql($sql) {
6766
+	private function runSql($sql)
6767
+	{
6448 6768
 		static $cache = array();
6449 6769
 
6450 6770
 		$id = md5($sql);
@@ -6462,7 +6782,8 @@  discard block
 block discarded – undo
6462 6782
 	 *
6463 6783
 	 * @return string
6464 6784
 	 */
6465
-	public function gedcomFavorites() {
6785
+	public function gedcomFavorites()
6786
+	{
6466 6787
 		if (Module::getModuleByName('gedcom_favorites')) {
6467 6788
 			$block = new FamilyTreeFavoritesModule(WT_MODULES_DIR . 'gedcom_favorites');
6468 6789
 
@@ -6477,7 +6798,8 @@  discard block
 block discarded – undo
6477 6798
 	 *
6478 6799
 	 * @return string
6479 6800
 	 */
6480
-	public function userFavorites() {
6801
+	public function userFavorites()
6802
+	{
6481 6803
 		if (Auth::check() && Module::getModuleByName('user_favorites')) {
6482 6804
 			$block = new UserFavoritesModule(WT_MODULES_DIR . 'gedcom_favorites');
6483 6805
 
@@ -6492,7 +6814,8 @@  discard block
 block discarded – undo
6492 6814
 	 *
6493 6815
 	 * @return int
6494 6816
 	 */
6495
-	public function totalGedcomFavorites() {
6817
+	public function totalGedcomFavorites()
6818
+	{
6496 6819
 		if (Module::getModuleByName('gedcom_favorites')) {
6497 6820
 			return count(FamilyTreeFavoritesModule::getFavorites($this->tree->getTreeId()));
6498 6821
 		} else {
@@ -6505,7 +6828,8 @@  discard block
 block discarded – undo
6505 6828
 	 *
6506 6829
 	 * @return int
6507 6830
 	 */
6508
-	public function totalUserFavorites() {
6831
+	public function totalUserFavorites()
6832
+	{
6509 6833
 		if (Module::getModuleByName('user_favorites')) {
6510 6834
 			return count(UserFavoritesModule::getFavorites(Auth::id()));
6511 6835
 		} else {
@@ -6522,7 +6846,8 @@  discard block
 block discarded – undo
6522 6846
 	 *
6523 6847
 	 * @return string
6524 6848
 	 */
6525
-	public function callBlock($params = array()) {
6849
+	public function callBlock($params = array())
6850
+	{
6526 6851
 		global $ctype;
6527 6852
 
6528 6853
 		if (isset($params[0]) && $params[0] != '') {
@@ -6562,7 +6887,8 @@  discard block
 block discarded – undo
6562 6887
 	 *
6563 6888
 	 * @return string
6564 6889
 	 */
6565
-	public function totalUserMessages() {
6890
+	public function totalUserMessages()
6891
+	{
6566 6892
 		$total = (int) Database::prepare("SELECT COUNT(*) FROM `##message` WHERE user_id = ?")
6567 6893
 			->execute(array(Auth::id()))
6568 6894
 			->fetchOne();
@@ -6575,7 +6901,8 @@  discard block
 block discarded – undo
6575 6901
 	 *
6576 6902
 	 * @return string
6577 6903
 	 */
6578
-	public function totalUserJournal() {
6904
+	public function totalUserJournal()
6905
+	{
6579 6906
 		try {
6580 6907
 			$number = (int) Database::prepare("SELECT COUNT(*) FROM `##news` WHERE user_id = ?")
6581 6908
 				->execute(array(Auth::id()))
@@ -6593,7 +6920,8 @@  discard block
 block discarded – undo
6593 6920
 	 *
6594 6921
 	 * @return string
6595 6922
 	 */
6596
-	public function totalGedcomNews() {
6923
+	public function totalGedcomNews()
6924
+	{
6597 6925
 		try {
6598 6926
 			$number = (int) Database::prepare("SELECT COUNT(*) FROM `##news` WHERE gedcom_id = ?")
6599 6927
 				->execute(array($this->tree->getTreeId()))
@@ -6613,7 +6941,8 @@  discard block
 block discarded – undo
6613 6941
 	 *
6614 6942
 	 * @return string[]
6615 6943
 	 */
6616
-	public function iso3166() {
6944
+	public function iso3166()
6945
+	{
6617 6946
 		return array(
6618 6947
 			'ABW' => 'AW', 'AFG' => 'AF', 'AGO' => 'AO', 'AIA' => 'AI', 'ALA' => 'AX', 'ALB' => 'AL',
6619 6948
 			'AND' => 'AD', 'ARE' => 'AE', 'ARG' => 'AR', 'ARM' => 'AM', 'ASM' => 'AS',
@@ -6665,7 +6994,8 @@  discard block
 block discarded – undo
6665 6994
 	 *
6666 6995
 	 * @return string[]
6667 6996
 	 */
6668
-	public function getAllCountries() {
6997
+	public function getAllCountries()
6998
+	{
6669 6999
 		return array(
6670 7000
 			'???' => /* I18N: Name of a country or state */ I18N::translate('Unknown'),
6671 7001
 			'ABW' => /* I18N: Name of a country or state */ I18N::translate('Aruba'),
@@ -6934,7 +7264,8 @@  discard block
 block discarded – undo
6934 7264
 	 *
6935 7265
 	 * @return string
6936 7266
 	 */
6937
-	private function centuryName($century) {
7267
+	private function centuryName($century)
7268
+	{
6938 7269
 		if ($century < 0) {
6939 7270
 			return str_replace(-$century, self::centuryName(-$century), /* I18N: BCE=Before the Common Era, for Julian years < 0. See http://en.wikipedia.org/wiki/Common_Era */
6940 7271
 				I18N::translate('%s BCE', I18N::number(-$century)));
Please login to merge, or discard this patch.
app/Mail.php 3 patches
Indentation   +72 added lines, -72 removed lines patch added patch discarded remove patch
@@ -29,80 +29,80 @@  discard block
 block discarded – undo
29 29
  * Send mail messages.
30 30
  */
31 31
 class Mail {
32
-	const EOL = "<br>\r\n"; // End-of-line that works for both TEXT and HTML messages
32
+    const EOL = "<br>\r\n"; // End-of-line that works for both TEXT and HTML messages
33 33
 
34
-	/**
35
-	 * Send an external email message
36
-	 * Caution! gmail may rewrite the "From" header unless you have added the address to your account.
37
-	 *
38
-	 * @param Tree   $tree
39
-	 * @param string $to_email
40
-	 * @param string $to_name
41
-	 * @param string $replyto_email
42
-	 * @param string $replyto_name
43
-	 * @param string $subject
44
-	 * @param string $message
45
-	 *
46
-	 * @return bool
47
-	 */
48
-	public static function send(Tree $tree, $to_email, $to_name, $replyto_email, $replyto_name, $subject, $message) {
49
-		try {
50
-			// Swiftmailer uses the PHP default tmp directory.  On some servers, this
51
-			// is outside the open_basedir list.  Therefore we must set one explicitly.
52
-			File::mkdir(WT_DATA_DIR . 'tmp');
34
+    /**
35
+     * Send an external email message
36
+     * Caution! gmail may rewrite the "From" header unless you have added the address to your account.
37
+     *
38
+     * @param Tree   $tree
39
+     * @param string $to_email
40
+     * @param string $to_name
41
+     * @param string $replyto_email
42
+     * @param string $replyto_name
43
+     * @param string $subject
44
+     * @param string $message
45
+     *
46
+     * @return bool
47
+     */
48
+    public static function send(Tree $tree, $to_email, $to_name, $replyto_email, $replyto_name, $subject, $message) {
49
+        try {
50
+            // Swiftmailer uses the PHP default tmp directory.  On some servers, this
51
+            // is outside the open_basedir list.  Therefore we must set one explicitly.
52
+            File::mkdir(WT_DATA_DIR . 'tmp');
53 53
 
54
-			Swift_Preferences::getInstance()->setTempDir(WT_DATA_DIR . 'tmp');
54
+            Swift_Preferences::getInstance()->setTempDir(WT_DATA_DIR . 'tmp');
55 55
 
56
-			$mail = Swift_Message::newInstance()
57
-				->setSubject($subject)
58
-				->setFrom(Site::getPreference('SMTP_FROM_NAME'), $tree->getPreference('title'))
59
-				->setTo($to_email, $to_name)
60
-				->setReplyTo($replyto_email, $replyto_name)
61
-				->setBody($message, 'text/html')
62
-				->addPart(Filter::unescapeHtml($message), 'text/plain');
56
+            $mail = Swift_Message::newInstance()
57
+                ->setSubject($subject)
58
+                ->setFrom(Site::getPreference('SMTP_FROM_NAME'), $tree->getPreference('title'))
59
+                ->setTo($to_email, $to_name)
60
+                ->setReplyTo($replyto_email, $replyto_name)
61
+                ->setBody($message, 'text/html')
62
+                ->addPart(Filter::unescapeHtml($message), 'text/plain');
63 63
 
64
-			Swift_Mailer::newInstance(self::transport())->send($mail);
65
-		} catch (Exception $ex) {
66
-			Log::addErrorLog('Mail: ' . $ex->getMessage());
64
+            Swift_Mailer::newInstance(self::transport())->send($mail);
65
+        } catch (Exception $ex) {
66
+            Log::addErrorLog('Mail: ' . $ex->getMessage());
67 67
 
68
-			return false;
69
-		}
68
+            return false;
69
+        }
70 70
 
71
-		return true;
72
-	}
71
+        return true;
72
+    }
73 73
 
74
-	/**
75
-	 * Send an automated system message (such as a password reminder) from a tree to a user.
76
-	 *
77
-	 * @param Tree   $tree
78
-	 * @param User   $user
79
-	 * @param string $subject
80
-	 * @param string $message
81
-	 *
82
-	 * @return bool
83
-	 */
84
-	public static function systemMessage(Tree $tree, User $user, $subject, $message) {
85
-		return self::send(
86
-			$tree,
87
-			$user->getEmail(), $user->getRealName(),
88
-			Site::getPreference('SMTP_FROM_NAME'), $tree->getPreference('title'),
89
-			$subject,
90
-			$message
91
-		);
92
-	}
74
+    /**
75
+     * Send an automated system message (such as a password reminder) from a tree to a user.
76
+     *
77
+     * @param Tree   $tree
78
+     * @param User   $user
79
+     * @param string $subject
80
+     * @param string $message
81
+     *
82
+     * @return bool
83
+     */
84
+    public static function systemMessage(Tree $tree, User $user, $subject, $message) {
85
+        return self::send(
86
+            $tree,
87
+            $user->getEmail(), $user->getRealName(),
88
+            Site::getPreference('SMTP_FROM_NAME'), $tree->getPreference('title'),
89
+            $subject,
90
+            $message
91
+        );
92
+    }
93 93
 
94
-	/**
95
-	 * Create a transport mechanism for sending mail
96
-	 *
97
-	 * @return Swift_Transport
98
-	 */
99
-	public static function transport() {
100
-		switch (Site::getPreference('SMTP_ACTIVE')) {
101
-		case 'internal':
102
-			return Swift_MailTransport::newInstance();
103
-		case 'sendmail':
104
-			return Swift_SendmailTransport::newInstance();
105
-		case 'external':
94
+    /**
95
+     * Create a transport mechanism for sending mail
96
+     *
97
+     * @return Swift_Transport
98
+     */
99
+    public static function transport() {
100
+        switch (Site::getPreference('SMTP_ACTIVE')) {
101
+        case 'internal':
102
+            return Swift_MailTransport::newInstance();
103
+        case 'sendmail':
104
+            return Swift_SendmailTransport::newInstance();
105
+        case 'external':
106 106
             $transport = Swift_SmtpTransport::newInstance()
107 107
                 ->setHost(Site::getPreference('SMTP_HOST'))
108 108
                 ->setPort(Site::getPreference('SMTP_PORT'))
@@ -118,10 +118,10 @@  discard block
 block discarded – undo
118 118
                 $transport->setEncryption(Site::getPreference('SMTP_SSL'));
119 119
             }
120 120
 
121
-			return $transport;
122
-		default:
123
-			// For testing
124
-			return Swift_NullTransport::newInstance();
125
-		}
126
-	}
121
+            return $transport;
122
+        default:
123
+            // For testing
124
+            return Swift_NullTransport::newInstance();
125
+        }
126
+    }
127 127
 }
Please login to merge, or discard this patch.
Switch Indentation   +21 added lines, -21 removed lines patch added patch discarded remove patch
@@ -98,30 +98,30 @@
 block discarded – undo
98 98
 	 */
99 99
 	public static function transport() {
100 100
 		switch (Site::getPreference('SMTP_ACTIVE')) {
101
-		case 'internal':
102
-			return Swift_MailTransport::newInstance();
103
-		case 'sendmail':
104
-			return Swift_SendmailTransport::newInstance();
105
-		case 'external':
106
-            $transport = Swift_SmtpTransport::newInstance()
107
-                ->setHost(Site::getPreference('SMTP_HOST'))
108
-                ->setPort(Site::getPreference('SMTP_PORT'))
109
-                ->setLocalDomain(Site::getPreference('SMTP_HELO'));
101
+		    case 'internal':
102
+			    return Swift_MailTransport::newInstance();
103
+		    case 'sendmail':
104
+			    return Swift_SendmailTransport::newInstance();
105
+		    case 'external':
106
+                $transport = Swift_SmtpTransport::newInstance()
107
+                    ->setHost(Site::getPreference('SMTP_HOST'))
108
+                    ->setPort(Site::getPreference('SMTP_PORT'))
109
+                    ->setLocalDomain(Site::getPreference('SMTP_HELO'));
110 110
 
111
-            if (Site::getPreference('SMTP_AUTH')) {
112
-                $transport
113
-                    ->setUsername(Site::getPreference('SMTP_AUTH_USER'))
114
-                    ->setPassword(Site::getPreference('SMTP_AUTH_PASS'));
115
-            }
111
+                if (Site::getPreference('SMTP_AUTH')) {
112
+                    $transport
113
+                        ->setUsername(Site::getPreference('SMTP_AUTH_USER'))
114
+                        ->setPassword(Site::getPreference('SMTP_AUTH_PASS'));
115
+                }
116 116
 
117
-            if (Site::getPreference('SMTP_SSL') !== 'none') {
118
-                $transport->setEncryption(Site::getPreference('SMTP_SSL'));
119
-            }
117
+                if (Site::getPreference('SMTP_SSL') !== 'none') {
118
+                    $transport->setEncryption(Site::getPreference('SMTP_SSL'));
119
+                }
120 120
 
121
-			return $transport;
122
-		default:
123
-			// For testing
124
-			return Swift_NullTransport::newInstance();
121
+			    return $transport;
122
+		    default:
123
+			    // For testing
124
+			    return Swift_NullTransport::newInstance();
125 125
 		}
126 126
 	}
127 127
 }
Please login to merge, or discard this patch.
Braces   +8 added lines, -4 removed lines patch added patch discarded remove patch
@@ -28,7 +28,8 @@  discard block
 block discarded – undo
28 28
 /**
29 29
  * Send mail messages.
30 30
  */
31
-class Mail {
31
+class Mail
32
+{
32 33
 	const EOL = "<br>\r\n"; // End-of-line that works for both TEXT and HTML messages
33 34
 
34 35
 	/**
@@ -45,7 +46,8 @@  discard block
 block discarded – undo
45 46
 	 *
46 47
 	 * @return bool
47 48
 	 */
48
-	public static function send(Tree $tree, $to_email, $to_name, $replyto_email, $replyto_name, $subject, $message) {
49
+	public static function send(Tree $tree, $to_email, $to_name, $replyto_email, $replyto_name, $subject, $message)
50
+	{
49 51
 		try {
50 52
 			// Swiftmailer uses the PHP default tmp directory.  On some servers, this
51 53
 			// is outside the open_basedir list.  Therefore we must set one explicitly.
@@ -81,7 +83,8 @@  discard block
 block discarded – undo
81 83
 	 *
82 84
 	 * @return bool
83 85
 	 */
84
-	public static function systemMessage(Tree $tree, User $user, $subject, $message) {
86
+	public static function systemMessage(Tree $tree, User $user, $subject, $message)
87
+	{
85 88
 		return self::send(
86 89
 			$tree,
87 90
 			$user->getEmail(), $user->getRealName(),
@@ -96,7 +99,8 @@  discard block
 block discarded – undo
96 99
 	 *
97 100
 	 * @return Swift_Transport
98 101
 	 */
99
-	public static function transport() {
102
+	public static function transport()
103
+	{
100 104
 		switch (Site::getPreference('SMTP_ACTIVE')) {
101 105
 		case 'internal':
102 106
 			return Swift_MailTransport::newInstance();
Please login to merge, or discard this patch.
app/Soundex.php 2 patches
Indentation   +759 added lines, -759 removed lines patch added patch discarded remove patch
@@ -19,782 +19,782 @@
 block discarded – undo
19 19
  * Phonetic matching of strings.
20 20
  */
21 21
 class Soundex {
22
-	/**
23
-	 * Which algorithms are supported.
24
-	 *
25
-	 * @return string[]
26
-	 */
27
-	public static function getAlgorithms() {
28
-		return array(
29
-			'std' => /* I18N: http://en.wikipedia.org/wiki/Soundex */ I18N::translate('Russell'),
30
-			'dm'  => /* I18N: http://en.wikipedia.org/wiki/Daitch–Mokotoff_Soundex */ I18N::translate('Daitch-Mokotoff'),
31
-		);
32
-	}
22
+    /**
23
+     * Which algorithms are supported.
24
+     *
25
+     * @return string[]
26
+     */
27
+    public static function getAlgorithms() {
28
+        return array(
29
+            'std' => /* I18N: http://en.wikipedia.org/wiki/Soundex */ I18N::translate('Russell'),
30
+            'dm'  => /* I18N: http://en.wikipedia.org/wiki/Daitch–Mokotoff_Soundex */ I18N::translate('Daitch-Mokotoff'),
31
+        );
32
+    }
33 33
 
34
-	/**
35
-	 * Is there a match between two soundex codes?
36
-	 *
37
-	 * @param string $soundex1
38
-	 * @param string $soundex2
39
-	 *
40
-	 * @return bool
41
-	 */
42
-	public static function compare($soundex1, $soundex2) {
43
-		if ($soundex1 && $soundex2) {
44
-			foreach (explode(':', $soundex1) as $code) {
45
-				if (strpos($soundex2, $code) !== false) {
46
-					return true;
47
-				}
48
-			}
49
-		}
34
+    /**
35
+     * Is there a match between two soundex codes?
36
+     *
37
+     * @param string $soundex1
38
+     * @param string $soundex2
39
+     *
40
+     * @return bool
41
+     */
42
+    public static function compare($soundex1, $soundex2) {
43
+        if ($soundex1 && $soundex2) {
44
+            foreach (explode(':', $soundex1) as $code) {
45
+                if (strpos($soundex2, $code) !== false) {
46
+                    return true;
47
+                }
48
+            }
49
+        }
50 50
 
51
-		return false;
52
-	}
51
+        return false;
52
+    }
53 53
 
54
-	/**
55
-	 * Generate Russell soundex codes for a given text.
56
-	 *
57
-	 * @param $text
58
-	 *
59
-	 * @return null|string
60
-	 */
61
-	public static function russell($text) {
62
-		$words         = preg_split('/\s/', $text, -1, PREG_SPLIT_NO_EMPTY);
63
-		$soundex_array = array();
64
-		foreach ($words as $word) {
65
-			$soundex = soundex($word);
66
-			// Only return codes from recognisable sounds
67
-			if ($soundex !== '0000') {
68
-				$soundex_array[] = $soundex;
69
-			}
70
-		}
71
-		// Combine words, e.g. “New York” as “Newyork”
72
-		if (count($words) > 1) {
73
-			$soundex_array[] = soundex(strtr($text, ' ', ''));
74
-		}
75
-		// A varchar(255) column can only hold 51 4-character codes (plus 50 delimiters)
76
-		$soundex_array = array_slice(array_unique($soundex_array), 0, 51);
54
+    /**
55
+     * Generate Russell soundex codes for a given text.
56
+     *
57
+     * @param $text
58
+     *
59
+     * @return null|string
60
+     */
61
+    public static function russell($text) {
62
+        $words         = preg_split('/\s/', $text, -1, PREG_SPLIT_NO_EMPTY);
63
+        $soundex_array = array();
64
+        foreach ($words as $word) {
65
+            $soundex = soundex($word);
66
+            // Only return codes from recognisable sounds
67
+            if ($soundex !== '0000') {
68
+                $soundex_array[] = $soundex;
69
+            }
70
+        }
71
+        // Combine words, e.g. “New York” as “Newyork”
72
+        if (count($words) > 1) {
73
+            $soundex_array[] = soundex(strtr($text, ' ', ''));
74
+        }
75
+        // A varchar(255) column can only hold 51 4-character codes (plus 50 delimiters)
76
+        $soundex_array = array_slice(array_unique($soundex_array), 0, 51);
77 77
 
78
-		if ($soundex_array) {
79
-			return implode(':', $soundex_array);
80
-		} else {
81
-			return '';
82
-		}
83
-	}
78
+        if ($soundex_array) {
79
+            return implode(':', $soundex_array);
80
+        } else {
81
+            return '';
82
+        }
83
+    }
84 84
 
85
-	/**
86
-	 * Generate Daitch–Mokotoff soundex codes for a given text.
87
-	 *
88
-	 * @param $text
89
-	 *
90
-	 * @return null|string
91
-	 */
92
-	public static function daitchMokotoff($text) {
93
-		$words         = preg_split('/\s/', $text, -1, PREG_SPLIT_NO_EMPTY);
94
-		$soundex_array = array();
95
-		foreach ($words as $word) {
96
-			$soundex_array = array_merge($soundex_array, self::daitchMokotoffWord($word));
97
-		}
98
-		// Combine words, e.g. “New York” as “Newyork”
99
-		if (count($words) > 1) {
100
-			$soundex_array = array_merge($soundex_array, self::daitchMokotoffWord(strtr($text, ' ', '')));
101
-		}
102
-		// A varchar(255) column can only hold 36 6-character codes (plus 35 delimiters)
103
-		$soundex_array = array_slice(array_unique($soundex_array), 0, 36);
85
+    /**
86
+     * Generate Daitch–Mokotoff soundex codes for a given text.
87
+     *
88
+     * @param $text
89
+     *
90
+     * @return null|string
91
+     */
92
+    public static function daitchMokotoff($text) {
93
+        $words         = preg_split('/\s/', $text, -1, PREG_SPLIT_NO_EMPTY);
94
+        $soundex_array = array();
95
+        foreach ($words as $word) {
96
+            $soundex_array = array_merge($soundex_array, self::daitchMokotoffWord($word));
97
+        }
98
+        // Combine words, e.g. “New York” as “Newyork”
99
+        if (count($words) > 1) {
100
+            $soundex_array = array_merge($soundex_array, self::daitchMokotoffWord(strtr($text, ' ', '')));
101
+        }
102
+        // A varchar(255) column can only hold 36 6-character codes (plus 35 delimiters)
103
+        $soundex_array = array_slice(array_unique($soundex_array), 0, 36);
104 104
 
105
-		if ($soundex_array) {
106
-			return implode(':', $soundex_array);
107
-		} else {
108
-			return '';
109
-		}
110
-	}
105
+        if ($soundex_array) {
106
+            return implode(':', $soundex_array);
107
+        } else {
108
+            return '';
109
+        }
110
+    }
111 111
 
112
-	// Determine the Daitch–Mokotoff Soundex code for a word
113
-	// Original implementation by Gerry Kroll, and analysis by Meliza Amity
112
+    // Determine the Daitch–Mokotoff Soundex code for a word
113
+    // Original implementation by Gerry Kroll, and analysis by Meliza Amity
114 114
 
115
-	// Max. table key length (in ASCII bytes -- NOT in UTF-8 characters!)
116
-	const MAXCHAR = 7;
115
+    // Max. table key length (in ASCII bytes -- NOT in UTF-8 characters!)
116
+    const MAXCHAR = 7;
117 117
 
118
-	/**
119
-	 * Name transformation arrays.
120
-	 * Used to transform the Name string to simplify the "sounds like" table.
121
-	 * This is especially useful in Hebrew.
122
-	 *
123
-	 * Each array entry defines the "from" and "to" arguments of an preg($from, $to, $text)
124
-	 * function call to achieve the desired transformations.
125
-	 *
126
-	 * Note about the use of "\x01":
127
-	 * This code, which can’t legitimately occur in the kind of text we're dealing with,
128
-	 * is used as a place-holder so that conditional string replacements can be done.
129
-	 *
130
-	 * @var string[][]
131
-	 */
132
-	private static $transformNameTable = array(
133
-		// Force Yiddish ligatures to be treated as separate letters
134
-		array('װ', 'וו'),
135
-		array('ײ', 'יי'),
136
-		array('ױ', 'וי'),
137
-		array('בו', 'בע'),
138
-		array('פו', 'פע'),
139
-		array('ומ', 'עמ'),
140
-		array('ום', 'עם'),
141
-		array('ונ', 'ענ'),
142
-		array('ון', 'ען'),
143
-		array('וו', 'ב'),
144
-		array("\x01", ''),
145
-		array('ייה$', "\x01ה"),
146
-		array('ייע$', "\x01ע"),
147
-		array('יי', 'ע'),
148
-		array("\x01", 'יי'),
149
-	);
118
+    /**
119
+     * Name transformation arrays.
120
+     * Used to transform the Name string to simplify the "sounds like" table.
121
+     * This is especially useful in Hebrew.
122
+     *
123
+     * Each array entry defines the "from" and "to" arguments of an preg($from, $to, $text)
124
+     * function call to achieve the desired transformations.
125
+     *
126
+     * Note about the use of "\x01":
127
+     * This code, which can’t legitimately occur in the kind of text we're dealing with,
128
+     * is used as a place-holder so that conditional string replacements can be done.
129
+     *
130
+     * @var string[][]
131
+     */
132
+    private static $transformNameTable = array(
133
+        // Force Yiddish ligatures to be treated as separate letters
134
+        array('װ', 'וו'),
135
+        array('ײ', 'יי'),
136
+        array('ױ', 'וי'),
137
+        array('בו', 'בע'),
138
+        array('פו', 'פע'),
139
+        array('ומ', 'עמ'),
140
+        array('ום', 'עם'),
141
+        array('ונ', 'ענ'),
142
+        array('ון', 'ען'),
143
+        array('וו', 'ב'),
144
+        array("\x01", ''),
145
+        array('ייה$', "\x01ה"),
146
+        array('ייע$', "\x01ע"),
147
+        array('יי', 'ע'),
148
+        array("\x01", 'יי'),
149
+    );
150 150
 
151
-	/**
152
-	 * The DM sound coding table is organized this way:
153
-	 * key: a variable-length string that corresponds to the UTF-8 character sequence
154
-	 * represented by the table entry. Currently, that string can be up to 7
155
-	 * bytes long. This maximum length is defined by the value of global variable
156
-	 * $maxchar.
157
-	 *
158
-	 * value: an array as follows:
159
-	 * [0]:  zero if not a vowel
160
-	 * [1]:  sound value when this string is at the beginning of the word
161
-	 * [2]:  sound value when this string is followed by a vowel
162
-	 * [3]:  sound value for other cases
163
-	 * [1],[2],[3] can be repeated several times to create branches in the code
164
-	 * an empty sound value means "ignore in this state"
165
-	 *
166
-	 * @var string[][]
167
-	 */
168
-	private static $dmsounds = array(
169
-		'A'       => array('1', '0', '', ''),
170
-		'À'       => array('1', '0', '', ''),
171
-		'Á'       => array('1', '0', '', ''),
172
-		'Â'       => array('1', '0', '', ''),
173
-		'Ã'       => array('1', '0', '', ''),
174
-		'Ä'       => array('1', '0', '1', '', '0', '', ''),
175
-		'Å'       => array('1', '0', '', ''),
176
-		'Ă'       => array('1', '0', '', ''),
177
-		'Ą'       => array('1', '', '', '', '', '', '6'),
178
-		'Ạ'       => array('1', '0', '', ''),
179
-		'Ả'       => array('1', '0', '', ''),
180
-		'Ấ'       => array('1', '0', '', ''),
181
-		'Ầ'       => array('1', '0', '', ''),
182
-		'Ẩ'       => array('1', '0', '', ''),
183
-		'Ẫ'       => array('1', '0', '', ''),
184
-		'Ậ'       => array('1', '0', '', ''),
185
-		'Ắ'       => array('1', '0', '', ''),
186
-		'Ằ'       => array('1', '0', '', ''),
187
-		'Ẳ'       => array('1', '0', '', ''),
188
-		'Ẵ'       => array('1', '0', '', ''),
189
-		'Ặ'       => array('1', '0', '', ''),
190
-		'AE'      => array('1', '0', '1', ''),
191
-		'Æ'       => array('1', '0', '1', ''),
192
-		'AI'      => array('1', '0', '1', ''),
193
-		'AJ'      => array('1', '0', '1', ''),
194
-		'AU'      => array('1', '0', '7', ''),
195
-		'AV'      => array('1', '0', '7', '', '7', '7', '7'),
196
-		'ÄU'      => array('1', '0', '1', ''),
197
-		'AY'      => array('1', '0', '1', ''),
198
-		'B'       => array('0', '7', '7', '7'),
199
-		'C'       => array('0', '5', '5', '5', '34', '4', '4'),
200
-		'Ć'       => array('0', '4', '4', '4'),
201
-		'Č'       => array('0', '4', '4', '4'),
202
-		'Ç'       => array('0', '4', '4', '4'),
203
-		'CH'      => array('0', '5', '5', '5', '34', '4', '4'),
204
-		'CHS'     => array('0', '5', '54', '54'),
205
-		'CK'      => array('0', '5', '5', '5', '45', '45', '45'),
206
-		'CCS'     => array('0', '4', '4', '4'),
207
-		'CS'      => array('0', '4', '4', '4'),
208
-		'CSZ'     => array('0', '4', '4', '4'),
209
-		'CZ'      => array('0', '4', '4', '4'),
210
-		'CZS'     => array('0', '4', '4', '4'),
211
-		'D'       => array('0', '3', '3', '3'),
212
-		'Ď'       => array('0', '3', '3', '3'),
213
-		'Đ'       => array('0', '3', '3', '3'),
214
-		'DRS'     => array('0', '4', '4', '4'),
215
-		'DRZ'     => array('0', '4', '4', '4'),
216
-		'DS'      => array('0', '4', '4', '4'),
217
-		'DSH'     => array('0', '4', '4', '4'),
218
-		'DSZ'     => array('0', '4', '4', '4'),
219
-		'DT'      => array('0', '3', '3', '3'),
220
-		'DDZ'     => array('0', '4', '4', '4'),
221
-		'DDZS'    => array('0', '4', '4', '4'),
222
-		'DZ'      => array('0', '4', '4', '4'),
223
-		'DŹ'      => array('0', '4', '4', '4'),
224
-		'DŻ'      => array('0', '4', '4', '4'),
225
-		'DZH'     => array('0', '4', '4', '4'),
226
-		'DZS'     => array('0', '4', '4', '4'),
227
-		'E'       => array('1', '0', '', ''),
228
-		'È'       => array('1', '0', '', ''),
229
-		'É'       => array('1', '0', '', ''),
230
-		'Ê'       => array('1', '0', '', ''),
231
-		'Ë'       => array('1', '0', '', ''),
232
-		'Ĕ'       => array('1', '0', '', ''),
233
-		'Ė'       => array('1', '0', '', ''),
234
-		'Ę'       => array('1', '', '', '6', '', '', ''),
235
-		'Ẹ'       => array('1', '0', '', ''),
236
-		'Ẻ'       => array('1', '0', '', ''),
237
-		'Ẽ'       => array('1', '0', '', ''),
238
-		'Ế'       => array('1', '0', '', ''),
239
-		'Ề'       => array('1', '0', '', ''),
240
-		'Ể'       => array('1', '0', '', ''),
241
-		'Ễ'       => array('1', '0', '', ''),
242
-		'Ệ'       => array('1', '0', '', ''),
243
-		'EAU'     => array('1', '0', '', ''),
244
-		'EI'      => array('1', '0', '1', ''),
245
-		'EJ'      => array('1', '0', '1', ''),
246
-		'EU'      => array('1', '1', '1', ''),
247
-		'EY'      => array('1', '0', '1', ''),
248
-		'F'       => array('0', '7', '7', '7'),
249
-		'FB'      => array('0', '7', '7', '7'),
250
-		'G'       => array('0', '5', '5', '5', '34', '4', '4'),
251
-		'Ğ'       => array('0', '', '', ''),
252
-		'GGY'     => array('0', '5', '5', '5'),
253
-		'GY'      => array('0', '5', '5', '5'),
254
-		'H'       => array('0', '5', '5', '', '5', '5', '5'),
255
-		'I'       => array('1', '0', '', ''),
256
-		'Ì'       => array('1', '0', '', ''),
257
-		'Í'       => array('1', '0', '', ''),
258
-		'Î'       => array('1', '0', '', ''),
259
-		'Ï'       => array('1', '0', '', ''),
260
-		'Ĩ'       => array('1', '0', '', ''),
261
-		'Į'       => array('1', '0', '', ''),
262
-		'İ'       => array('1', '0', '', ''),
263
-		'Ỉ'       => array('1', '0', '', ''),
264
-		'Ị'       => array('1', '0', '', ''),
265
-		'IA'      => array('1', '1', '', ''),
266
-		'IE'      => array('1', '1', '', ''),
267
-		'IO'      => array('1', '1', '', ''),
268
-		'IU'      => array('1', '1', '', ''),
269
-		'J'       => array('0', '1', '', '', '4', '4', '4', '5', '5', ''),
270
-		'K'       => array('0', '5', '5', '5'),
271
-		'KH'      => array('0', '5', '5', '5'),
272
-		'KS'      => array('0', '5', '54', '54'),
273
-		'L'       => array('0', '8', '8', '8'),
274
-		'Ľ'       => array('0', '8', '8', '8'),
275
-		'Ĺ'       => array('0', '8', '8', '8'),
276
-		'Ł'       => array('0', '7', '7', '7', '8', '8', '8'),
277
-		'LL'      => array('0', '8', '8', '8', '58', '8', '8', '1', '8', '8'),
278
-		'LLY'     => array('0', '8', '8', '8', '1', '8', '8'),
279
-		'LY'      => array('0', '8', '8', '8', '1', '8', '8'),
280
-		'M'       => array('0', '6', '6', '6'),
281
-		'MĔ'      => array('0', '66', '66', '66'),
282
-		'MN'      => array('0', '66', '66', '66'),
283
-		'N'       => array('0', '6', '6', '6'),
284
-		'Ń'       => array('0', '6', '6', '6'),
285
-		'Ň'       => array('0', '6', '6', '6'),
286
-		'Ñ'       => array('0', '6', '6', '6'),
287
-		'NM'      => array('0', '66', '66', '66'),
288
-		'O'       => array('1', '0', '', ''),
289
-		'Ò'       => array('1', '0', '', ''),
290
-		'Ó'       => array('1', '0', '', ''),
291
-		'Ô'       => array('1', '0', '', ''),
292
-		'Õ'       => array('1', '0', '', ''),
293
-		'Ö'       => array('1', '0', '', ''),
294
-		'Ø'       => array('1', '0', '', ''),
295
-		'Ő'       => array('1', '0', '', ''),
296
-		'Œ'       => array('1', '0', '', ''),
297
-		'Ơ'       => array('1', '0', '', ''),
298
-		'Ọ'       => array('1', '0', '', ''),
299
-		'Ỏ'       => array('1', '0', '', ''),
300
-		'Ố'       => array('1', '0', '', ''),
301
-		'Ồ'       => array('1', '0', '', ''),
302
-		'Ổ'       => array('1', '0', '', ''),
303
-		'Ỗ'       => array('1', '0', '', ''),
304
-		'Ộ'       => array('1', '0', '', ''),
305
-		'Ớ'       => array('1', '0', '', ''),
306
-		'Ờ'       => array('1', '0', '', ''),
307
-		'Ở'       => array('1', '0', '', ''),
308
-		'Ỡ'       => array('1', '0', '', ''),
309
-		'Ợ'       => array('1', '0', '', ''),
310
-		'OE'      => array('1', '0', '', ''),
311
-		'OI'      => array('1', '0', '1', ''),
312
-		'OJ'      => array('1', '0', '1', ''),
313
-		'OU'      => array('1', '0', '', ''),
314
-		'OY'      => array('1', '0', '1', ''),
315
-		'P'       => array('0', '7', '7', '7'),
316
-		'PF'      => array('0', '7', '7', '7'),
317
-		'PH'      => array('0', '7', '7', '7'),
318
-		'Q'       => array('0', '5', '5', '5'),
319
-		'R'       => array('0', '9', '9', '9'),
320
-		'Ř'       => array('0', '4', '4', '4'),
321
-		'RS'      => array('0', '4', '4', '4', '94', '94', '94'),
322
-		'RZ'      => array('0', '4', '4', '4', '94', '94', '94'),
323
-		'S'       => array('0', '4', '4', '4'),
324
-		'Ś'       => array('0', '4', '4', '4'),
325
-		'Š'       => array('0', '4', '4', '4'),
326
-		'Ş'       => array('0', '4', '4', '4'),
327
-		'SC'      => array('0', '2', '4', '4'),
328
-		'ŠČ'      => array('0', '2', '4', '4'),
329
-		'SCH'     => array('0', '4', '4', '4'),
330
-		'SCHD'    => array('0', '2', '43', '43'),
331
-		'SCHT'    => array('0', '2', '43', '43'),
332
-		'SCHTCH'  => array('0', '2', '4', '4'),
333
-		'SCHTSCH' => array('0', '2', '4', '4'),
334
-		'SCHTSH'  => array('0', '2', '4', '4'),
335
-		'SD'      => array('0', '2', '43', '43'),
336
-		'SH'      => array('0', '4', '4', '4'),
337
-		'SHCH'    => array('0', '2', '4', '4'),
338
-		'SHD'     => array('0', '2', '43', '43'),
339
-		'SHT'     => array('0', '2', '43', '43'),
340
-		'SHTCH'   => array('0', '2', '4', '4'),
341
-		'SHTSH'   => array('0', '2', '4', '4'),
342
-		'ß'       => array('0', '', '4', '4'),
343
-		'ST'      => array('0', '2', '43', '43'),
344
-		'STCH'    => array('0', '2', '4', '4'),
345
-		'STRS'    => array('0', '2', '4', '4'),
346
-		'STRZ'    => array('0', '2', '4', '4'),
347
-		'STSCH'   => array('0', '2', '4', '4'),
348
-		'STSH'    => array('0', '2', '4', '4'),
349
-		'SSZ'     => array('0', '4', '4', '4'),
350
-		'SZ'      => array('0', '4', '4', '4'),
351
-		'SZCS'    => array('0', '2', '4', '4'),
352
-		'SZCZ'    => array('0', '2', '4', '4'),
353
-		'SZD'     => array('0', '2', '43', '43'),
354
-		'SZT'     => array('0', '2', '43', '43'),
355
-		'T'       => array('0', '3', '3', '3'),
356
-		'Ť'       => array('0', '3', '3', '3'),
357
-		'Ţ'       => array('0', '3', '3', '3', '4', '4', '4'),
358
-		'TC'      => array('0', '4', '4', '4'),
359
-		'TCH'     => array('0', '4', '4', '4'),
360
-		'TH'      => array('0', '3', '3', '3'),
361
-		'TRS'     => array('0', '4', '4', '4'),
362
-		'TRZ'     => array('0', '4', '4', '4'),
363
-		'TS'      => array('0', '4', '4', '4'),
364
-		'TSCH'    => array('0', '4', '4', '4'),
365
-		'TSH'     => array('0', '4', '4', '4'),
366
-		'TSZ'     => array('0', '4', '4', '4'),
367
-		'TTCH'    => array('0', '4', '4', '4'),
368
-		'TTS'     => array('0', '4', '4', '4'),
369
-		'TTSCH'   => array('0', '4', '4', '4'),
370
-		'TTSZ'    => array('0', '4', '4', '4'),
371
-		'TTZ'     => array('0', '4', '4', '4'),
372
-		'TZ'      => array('0', '4', '4', '4'),
373
-		'TZS'     => array('0', '4', '4', '4'),
374
-		'U'       => array('1', '0', '', ''),
375
-		'Ù'       => array('1', '0', '', ''),
376
-		'Ú'       => array('1', '0', '', ''),
377
-		'Û'       => array('1', '0', '', ''),
378
-		'Ü'       => array('1', '0', '', ''),
379
-		'Ũ'       => array('1', '0', '', ''),
380
-		'Ū'       => array('1', '0', '', ''),
381
-		'Ů'       => array('1', '0', '', ''),
382
-		'Ű'       => array('1', '0', '', ''),
383
-		'Ų'       => array('1', '0', '', ''),
384
-		'Ư'       => array('1', '0', '', ''),
385
-		'Ụ'       => array('1', '0', '', ''),
386
-		'Ủ'       => array('1', '0', '', ''),
387
-		'Ứ'       => array('1', '0', '', ''),
388
-		'Ừ'       => array('1', '0', '', ''),
389
-		'Ử'       => array('1', '0', '', ''),
390
-		'Ữ'       => array('1', '0', '', ''),
391
-		'Ự'       => array('1', '0', '', ''),
392
-		'UE'      => array('1', '0', '', ''),
393
-		'UI'      => array('1', '0', '1', ''),
394
-		'UJ'      => array('1', '0', '1', ''),
395
-		'UY'      => array('1', '0', '1', ''),
396
-		'UW'      => array('1', '0', '1', '', '0', '7', '7'),
397
-		'V'       => array('0', '7', '7', '7'),
398
-		'W'       => array('0', '7', '7', '7'),
399
-		'X'       => array('0', '5', '54', '54'),
400
-		'Y'       => array('1', '1', '', ''),
401
-		'Ý'       => array('1', '1', '', ''),
402
-		'Ỳ'       => array('1', '1', '', ''),
403
-		'Ỵ'       => array('1', '1', '', ''),
404
-		'Ỷ'       => array('1', '1', '', ''),
405
-		'Ỹ'       => array('1', '1', '', ''),
406
-		'Z'       => array('0', '4', '4', '4'),
407
-		'Ź'       => array('0', '4', '4', '4'),
408
-		'Ż'       => array('0', '4', '4', '4'),
409
-		'Ž'       => array('0', '4', '4', '4'),
410
-		'ZD'      => array('0', '2', '43', '43'),
411
-		'ZDZ'     => array('0', '2', '4', '4'),
412
-		'ZDZH'    => array('0', '2', '4', '4'),
413
-		'ZH'      => array('0', '4', '4', '4'),
414
-		'ZHD'     => array('0', '2', '43', '43'),
415
-		'ZHDZH'   => array('0', '2', '4', '4'),
416
-		'ZS'      => array('0', '4', '4', '4'),
417
-		'ZSCH'    => array('0', '4', '4', '4'),
418
-		'ZSH'     => array('0', '4', '4', '4'),
419
-		'ZZS'     => array('0', '4', '4', '4'),
420
-		// Cyrillic alphabet
421
-		'А'   => array('1', '0', '', ''),
422
-		'Б'   => array('0', '7', '7', '7'),
423
-		'В'   => array('0', '7', '7', '7'),
424
-		'Г'   => array('0', '5', '5', '5'),
425
-		'Д'   => array('0', '3', '3', '3'),
426
-		'ДЗ'  => array('0', '4', '4', '4'),
427
-		'Е'   => array('1', '0', '', ''),
428
-		'Ё'   => array('1', '0', '', ''),
429
-		'Ж'   => array('0', '4', '4', '4'),
430
-		'З'   => array('0', '4', '4', '4'),
431
-		'И'   => array('1', '0', '', ''),
432
-		'Й'   => array('1', '1', '', '', '4', '4', '4'),
433
-		'К'   => array('0', '5', '5', '5'),
434
-		'Л'   => array('0', '8', '8', '8'),
435
-		'М'   => array('0', '6', '6', '6'),
436
-		'Н'   => array('0', '6', '6', '6'),
437
-		'О'   => array('1', '0', '', ''),
438
-		'П'   => array('0', '7', '7', '7'),
439
-		'Р'   => array('0', '9', '9', '9'),
440
-		'РЖ'  => array('0', '4', '4', '4'),
441
-		'С'   => array('0', '4', '4', '4'),
442
-		'Т'   => array('0', '3', '3', '3'),
443
-		'У'   => array('1', '0', '', ''),
444
-		'Ф'   => array('0', '7', '7', '7'),
445
-		'Х'   => array('0', '5', '5', '5'),
446
-		'Ц'   => array('0', '4', '4', '4'),
447
-		'Ч'   => array('0', '4', '4', '4'),
448
-		'Ш'   => array('0', '4', '4', '4'),
449
-		'Щ'   => array('0', '2', '4', '4'),
450
-		'Ъ'   => array('0', '', '', ''),
451
-		'Ы'   => array('0', '1', '', ''),
452
-		'Ь'   => array('0', '', '', ''),
453
-		'Э'   => array('1', '0', '', ''),
454
-		'Ю'   => array('0', '1', '', ''),
455
-		'Я'   => array('0', '1', '', ''),
456
-		// Greek alphabet
457
-		'Α'   => array('1', '0', '', ''),
458
-		'Ά'   => array('1', '0', '', ''),
459
-		'ΑΙ'  => array('1', '0', '1', ''),
460
-		'ΑΥ'  => array('1', '0', '1', ''),
461
-		'Β'   => array('0', '7', '7', '7'),
462
-		'Γ'   => array('0', '5', '5', '5'),
463
-		'Δ'   => array('0', '3', '3', '3'),
464
-		'Ε'   => array('1', '0', '', ''),
465
-		'Έ'   => array('1', '0', '', ''),
466
-		'ΕΙ'  => array('1', '0', '1', ''),
467
-		'ΕΥ'  => array('1', '1', '1', ''),
468
-		'Ζ'   => array('0', '4', '4', '4'),
469
-		'Η'   => array('1', '0', '', ''),
470
-		'Ή'   => array('1', '0', '', ''),
471
-		'Θ'   => array('0', '3', '3', '3'),
472
-		'Ι'   => array('1', '0', '', ''),
473
-		'Ί'   => array('1', '0', '', ''),
474
-		'Ϊ'   => array('1', '0', '', ''),
475
-		'ΐ'   => array('1', '0', '', ''),
476
-		'Κ'   => array('0', '5', '5', '5'),
477
-		'Λ'   => array('0', '8', '8', '8'),
478
-		'Μ'   => array('0', '6', '6', '6'),
479
-		'ΜΠ'  => array('0', '7', '7', '7'),
480
-		'Ν'   => array('0', '6', '6', '6'),
481
-		'ΝΤ'  => array('0', '3', '3', '3'),
482
-		'Ξ'   => array('0', '5', '54', '54'),
483
-		'Ο'   => array('1', '0', '', ''),
484
-		'Ό'   => array('1', '0', '', ''),
485
-		'ΟΙ'  => array('1', '0', '1', ''),
486
-		'ΟΥ'  => array('1', '0', '1', ''),
487
-		'Π'   => array('0', '7', '7', '7'),
488
-		'Ρ'   => array('0', '9', '9', '9'),
489
-		'Σ'   => array('0', '4', '4', '4'),
490
-		'ς'   => array('0', '', '', '4'),
491
-		'Τ'   => array('0', '3', '3', '3'),
492
-		'ΤΖ'  => array('0', '4', '4', '4'),
493
-		'ΤΣ'  => array('0', '4', '4', '4'),
494
-		'Υ'   => array('1', '1', '', ''),
495
-		'Ύ'   => array('1', '1', '', ''),
496
-		'Ϋ'   => array('1', '1', '', ''),
497
-		'ΰ'   => array('1', '1', '', ''),
498
-		'ΥΚ'  => array('1', '5', '5', '5'),
499
-		'ΥΥ'  => array('1', '65', '65', '65'),
500
-		'Φ'   => array('0', '7', '7', '7'),
501
-		'Χ'   => array('0', '5', '5', '5'),
502
-		'Ψ'   => array('0', '7', '7', '7'),
503
-		'Ω'   => array('1', '0', '', ''),
504
-		'Ώ'   => array('1', '0', '', ''),
505
-		// Hebrew alphabet
506
-		'א'     => array('1', '0', '', ''),
507
-		'או'    => array('1', '0', '7', ''),
508
-		'אג'    => array('1', '4', '4', '4', '5', '5', '5', '34', '34', '34'),
509
-		'בב'    => array('0', '7', '7', '7', '77', '77', '77'),
510
-		'ב'     => array('0', '7', '7', '7'),
511
-		'גג'    => array('0', '4', '4', '4', '5', '5', '5', '45', '45', '45', '55', '55', '55', '54', '54', '54'),
512
-		'גד'    => array('0', '43', '43', '43', '53', '53', '53'),
513
-		'גה'    => array('0', '45', '45', '45', '55', '55', '55'),
514
-		'גז'    => array('0', '44', '44', '44', '45', '45', '45'),
515
-		'גח'    => array('0', '45', '45', '45', '55', '55', '55'),
516
-		'גכ'    => array('0', '45', '45', '45', '55', '55', '55'),
517
-		'גך'    => array('0', '45', '45', '45', '55', '55', '55'),
518
-		'גצ'    => array('0', '44', '44', '44', '45', '45', '45'),
519
-		'גץ'    => array('0', '44', '44', '44', '45', '45', '45'),
520
-		'גק'    => array('0', '45', '45', '45', '54', '54', '54'),
521
-		'גש'    => array('0', '44', '44', '44', '54', '54', '54'),
522
-		'גת'    => array('0', '43', '43', '43', '53', '53', '53'),
523
-		'ג'     => array('0', '4', '4', '4', '5', '5', '5'),
524
-		'דז'    => array('0', '4', '4', '4'),
525
-		'דד'    => array('0', '3', '3', '3', '33', '33', '33'),
526
-		'דט'    => array('0', '33', '33', '33'),
527
-		'דש'    => array('0', '4', '4', '4'),
528
-		'דצ'    => array('0', '4', '4', '4'),
529
-		'דץ'    => array('0', '4', '4', '4'),
530
-		'ד'     => array('0', '3', '3', '3'),
531
-		'הג'    => array('0', '54', '54', '54', '55', '55', '55'),
532
-		'הכ'    => array('0', '55', '55', '55'),
533
-		'הח'    => array('0', '55', '55', '55'),
534
-		'הק'    => array('0', '55', '55', '55', '5', '5', '5'),
535
-		'הה'    => array('0', '5', '5', '', '55', '55', ''),
536
-		'ה'     => array('0', '5', '5', ''),
537
-		'וי'    => array('1', '', '', '', '7', '7', '7'),
538
-		'ו'     => array('1', '7', '7', '7', '7', '', ''),
539
-		'וו'    => array('1', '7', '7', '7', '7', '', ''),
540
-		'וופ'   => array('1', '7', '7', '7', '77', '77', '77'),
541
-		'זש'    => array('0', '4', '4', '4', '44', '44', '44'),
542
-		'זדז'   => array('0', '2', '4', '4'),
543
-		'ז'     => array('0', '4', '4', '4'),
544
-		'זג'    => array('0', '44', '44', '44', '45', '45', '45'),
545
-		'זז'    => array('0', '4', '4', '4', '44', '44', '44'),
546
-		'זס'    => array('0', '44', '44', '44'),
547
-		'זצ'    => array('0', '44', '44', '44'),
548
-		'זץ'    => array('0', '44', '44', '44'),
549
-		'חג'    => array('0', '54', '54', '54', '53', '53', '53'),
550
-		'חח'    => array('0', '5', '5', '5', '55', '55', '55'),
551
-		'חק'    => array('0', '55', '55', '55', '5', '5', '5'),
552
-		'חכ'    => array('0', '45', '45', '45', '55', '55', '55'),
553
-		'חס'    => array('0', '5', '54', '54'),
554
-		'חש'    => array('0', '5', '54', '54'),
555
-		'ח'     => array('0', '5', '5', '5'),
556
-		'טש'    => array('0', '4', '4', '4'),
557
-		'טד'    => array('0', '33', '33', '33'),
558
-		'טי'    => array('0', '3', '3', '3', '4', '4', '4', '3', '3', '34'),
559
-		'טת'    => array('0', '33', '33', '33'),
560
-		'טט'    => array('0', '3', '3', '3', '33', '33', '33'),
561
-		'ט'     => array('0', '3', '3', '3'),
562
-		'י'     => array('1', '1', '', ''),
563
-		'יא'    => array('1', '1', '', '', '1', '1', '1'),
564
-		'כג'    => array('0', '55', '55', '55', '54', '54', '54'),
565
-		'כש'    => array('0', '5', '54', '54'),
566
-		'כס'    => array('0', '5', '54', '54'),
567
-		'ככ'    => array('0', '5', '5', '5', '55', '55', '55'),
568
-		'כך'    => array('0', '5', '5', '5', '55', '55', '55'),
569
-		'כ'     => array('0', '5', '5', '5'),
570
-		'כח'    => array('0', '55', '55', '55', '5', '5', '5'),
571
-		'ך'     => array('0', '', '5', '5'),
572
-		'ל'     => array('0', '8', '8', '8'),
573
-		'לל'    => array('0', '88', '88', '88', '8', '8', '8'),
574
-		'מנ'    => array('0', '66', '66', '66'),
575
-		'מן'    => array('0', '66', '66', '66'),
576
-		'ממ'    => array('0', '6', '6', '6', '66', '66', '66'),
577
-		'מם'    => array('0', '6', '6', '6', '66', '66', '66'),
578
-		'מ'     => array('0', '6', '6', '6'),
579
-		'ם'     => array('0', '', '6', '6'),
580
-		'נמ'    => array('0', '66', '66', '66'),
581
-		'נם'    => array('0', '66', '66', '66'),
582
-		'ננ'    => array('0', '6', '6', '6', '66', '66', '66'),
583
-		'נן'    => array('0', '6', '6', '6', '66', '66', '66'),
584
-		'נ'     => array('0', '6', '6', '6'),
585
-		'ן'     => array('0', '', '6', '6'),
586
-		'סתש'   => array('0', '2', '4', '4'),
587
-		'סתז'   => array('0', '2', '4', '4'),
588
-		'סטז'   => array('0', '2', '4', '4'),
589
-		'סטש'   => array('0', '2', '4', '4'),
590
-		'סצד'   => array('0', '2', '4', '4'),
591
-		'סט'    => array('0', '2', '4', '4', '43', '43', '43'),
592
-		'סת'    => array('0', '2', '4', '4', '43', '43', '43'),
593
-		'סג'    => array('0', '44', '44', '44', '4', '4', '4'),
594
-		'סס'    => array('0', '4', '4', '4', '44', '44', '44'),
595
-		'סצ'    => array('0', '44', '44', '44'),
596
-		'סץ'    => array('0', '44', '44', '44'),
597
-		'סז'    => array('0', '44', '44', '44'),
598
-		'סש'    => array('0', '44', '44', '44'),
599
-		'ס'     => array('0', '4', '4', '4'),
600
-		'ע'     => array('1', '0', '', ''),
601
-		'פב'    => array('0', '7', '7', '7', '77', '77', '77'),
602
-		'פוו'   => array('0', '7', '7', '7', '77', '77', '77'),
603
-		'פפ'    => array('0', '7', '7', '7', '77', '77', '77'),
604
-		'פף'    => array('0', '7', '7', '7', '77', '77', '77'),
605
-		'פ'     => array('0', '7', '7', '7'),
606
-		'ף'     => array('0', '', '7', '7'),
607
-		'צג'    => array('0', '44', '44', '44', '45', '45', '45'),
608
-		'צז'    => array('0', '44', '44', '44'),
609
-		'צס'    => array('0', '44', '44', '44'),
610
-		'צצ'    => array('0', '4', '4', '4', '5', '5', '5', '44', '44', '44', '54', '54', '54', '45', '45', '45'),
611
-		'צץ'    => array('0', '4', '4', '4', '5', '5', '5', '44', '44', '44', '54', '54', '54'),
612
-		'צש'    => array('0', '44', '44', '44', '4', '4', '4', '5', '5', '5'),
613
-		'צ'     => array('0', '4', '4', '4', '5', '5', '5'),
614
-		'ץ'     => array('0', '', '4', '4'),
615
-		'קה'    => array('0', '55', '55', '5'),
616
-		'קס'    => array('0', '5', '54', '54'),
617
-		'קש'    => array('0', '5', '54', '54'),
618
-		'קק'    => array('0', '5', '5', '5', '55', '55', '55'),
619
-		'קח'    => array('0', '55', '55', '55'),
620
-		'קכ'    => array('0', '55', '55', '55'),
621
-		'קך'    => array('0', '55', '55', '55'),
622
-		'קג'    => array('0', '55', '55', '55', '54', '54', '54'),
623
-		'ק'     => array('0', '5', '5', '5'),
624
-		'רר'    => array('0', '99', '99', '99', '9', '9', '9'),
625
-		'ר'     => array('0', '9', '9', '9'),
626
-		'שטז'   => array('0', '2', '4', '4'),
627
-		'שתש'   => array('0', '2', '4', '4'),
628
-		'שתז'   => array('0', '2', '4', '4'),
629
-		'שטש'   => array('0', '2', '4', '4'),
630
-		'שד'    => array('0', '2', '43', '43'),
631
-		'שז'    => array('0', '44', '44', '44'),
632
-		'שס'    => array('0', '44', '44', '44'),
633
-		'שת'    => array('0', '2', '43', '43'),
634
-		'שג'    => array('0', '4', '4', '4', '44', '44', '44', '4', '43', '43'),
635
-		'שט'    => array('0', '2', '43', '43', '44', '44', '44'),
636
-		'שצ'    => array('0', '44', '44', '44', '45', '45', '45'),
637
-		'שץ'    => array('0', '44', '', '44', '45', '', '45'),
638
-		'שש'    => array('0', '4', '4', '4', '44', '44', '44'),
639
-		'ש'     => array('0', '4', '4', '4'),
640
-		'תג'    => array('0', '34', '34', '34'),
641
-		'תז'    => array('0', '34', '34', '34'),
642
-		'תש'    => array('0', '4', '4', '4'),
643
-		'תת'    => array('0', '3', '3', '3', '4', '4', '4', '33', '33', '33', '44', '44', '44', '34', '34', '34', '43', '43', '43'),
644
-		'ת'     => array('0', '3', '3', '3', '4', '4', '4'),
645
-		// Arabic alphabet
646
-		'ا'   => array('1', '0', '', ''),
647
-		'ب'   => array('0', '7', '7', '7'),
648
-		'ت'   => array('0', '3', '3', '3'),
649
-		'ث'   => array('0', '3', '3', '3'),
650
-		'ج'   => array('0', '4', '4', '4'),
651
-		'ح'   => array('0', '5', '5', '5'),
652
-		'خ'   => array('0', '5', '5', '5'),
653
-		'د'   => array('0', '3', '3', '3'),
654
-		'ذ'   => array('0', '3', '3', '3'),
655
-		'ر'   => array('0', '9', '9', '9'),
656
-		'ز'   => array('0', '4', '4', '4'),
657
-		'س'   => array('0', '4', '4', '4'),
658
-		'ش'   => array('0', '4', '4', '4'),
659
-		'ص'   => array('0', '4', '4', '4'),
660
-		'ض'   => array('0', '3', '3', '3'),
661
-		'ط'   => array('0', '3', '3', '3'),
662
-		'ظ'   => array('0', '4', '4', '4'),
663
-		'ع'   => array('1', '0', '', ''),
664
-		'غ'   => array('0', '0', '', ''),
665
-		'ف'   => array('0', '7', '7', '7'),
666
-		'ق'   => array('0', '5', '5', '5'),
667
-		'ك'   => array('0', '5', '5', '5'),
668
-		'ل'   => array('0', '8', '8', '8'),
669
-		'لا'  => array('0', '8', '8', '8'),
670
-		'م'   => array('0', '6', '6', '6'),
671
-		'ن'   => array('0', '6', '6', '6'),
672
-		'هن'  => array('0', '66', '66', '66'),
673
-		'ه'   => array('0', '5', '5', ''),
674
-		'و'   => array('1', '', '', '', '7', '', ''),
675
-		'ي'   => array('0', '1', '', ''),
676
-		'آ'   => array('0', '1', '', ''),
677
-		'ة'   => array('0', '', '', '3'),
678
-		'ی'   => array('0', '1', '', ''),
679
-		'ى'   => array('1', '1', '', ''),
680
-	);
151
+    /**
152
+     * The DM sound coding table is organized this way:
153
+     * key: a variable-length string that corresponds to the UTF-8 character sequence
154
+     * represented by the table entry. Currently, that string can be up to 7
155
+     * bytes long. This maximum length is defined by the value of global variable
156
+     * $maxchar.
157
+     *
158
+     * value: an array as follows:
159
+     * [0]:  zero if not a vowel
160
+     * [1]:  sound value when this string is at the beginning of the word
161
+     * [2]:  sound value when this string is followed by a vowel
162
+     * [3]:  sound value for other cases
163
+     * [1],[2],[3] can be repeated several times to create branches in the code
164
+     * an empty sound value means "ignore in this state"
165
+     *
166
+     * @var string[][]
167
+     */
168
+    private static $dmsounds = array(
169
+        'A'       => array('1', '0', '', ''),
170
+        'À'       => array('1', '0', '', ''),
171
+        'Á'       => array('1', '0', '', ''),
172
+        'Â'       => array('1', '0', '', ''),
173
+        'Ã'       => array('1', '0', '', ''),
174
+        'Ä'       => array('1', '0', '1', '', '0', '', ''),
175
+        'Å'       => array('1', '0', '', ''),
176
+        'Ă'       => array('1', '0', '', ''),
177
+        'Ą'       => array('1', '', '', '', '', '', '6'),
178
+        'Ạ'       => array('1', '0', '', ''),
179
+        'Ả'       => array('1', '0', '', ''),
180
+        'Ấ'       => array('1', '0', '', ''),
181
+        'Ầ'       => array('1', '0', '', ''),
182
+        'Ẩ'       => array('1', '0', '', ''),
183
+        'Ẫ'       => array('1', '0', '', ''),
184
+        'Ậ'       => array('1', '0', '', ''),
185
+        'Ắ'       => array('1', '0', '', ''),
186
+        'Ằ'       => array('1', '0', '', ''),
187
+        'Ẳ'       => array('1', '0', '', ''),
188
+        'Ẵ'       => array('1', '0', '', ''),
189
+        'Ặ'       => array('1', '0', '', ''),
190
+        'AE'      => array('1', '0', '1', ''),
191
+        'Æ'       => array('1', '0', '1', ''),
192
+        'AI'      => array('1', '0', '1', ''),
193
+        'AJ'      => array('1', '0', '1', ''),
194
+        'AU'      => array('1', '0', '7', ''),
195
+        'AV'      => array('1', '0', '7', '', '7', '7', '7'),
196
+        'ÄU'      => array('1', '0', '1', ''),
197
+        'AY'      => array('1', '0', '1', ''),
198
+        'B'       => array('0', '7', '7', '7'),
199
+        'C'       => array('0', '5', '5', '5', '34', '4', '4'),
200
+        'Ć'       => array('0', '4', '4', '4'),
201
+        'Č'       => array('0', '4', '4', '4'),
202
+        'Ç'       => array('0', '4', '4', '4'),
203
+        'CH'      => array('0', '5', '5', '5', '34', '4', '4'),
204
+        'CHS'     => array('0', '5', '54', '54'),
205
+        'CK'      => array('0', '5', '5', '5', '45', '45', '45'),
206
+        'CCS'     => array('0', '4', '4', '4'),
207
+        'CS'      => array('0', '4', '4', '4'),
208
+        'CSZ'     => array('0', '4', '4', '4'),
209
+        'CZ'      => array('0', '4', '4', '4'),
210
+        'CZS'     => array('0', '4', '4', '4'),
211
+        'D'       => array('0', '3', '3', '3'),
212
+        'Ď'       => array('0', '3', '3', '3'),
213
+        'Đ'       => array('0', '3', '3', '3'),
214
+        'DRS'     => array('0', '4', '4', '4'),
215
+        'DRZ'     => array('0', '4', '4', '4'),
216
+        'DS'      => array('0', '4', '4', '4'),
217
+        'DSH'     => array('0', '4', '4', '4'),
218
+        'DSZ'     => array('0', '4', '4', '4'),
219
+        'DT'      => array('0', '3', '3', '3'),
220
+        'DDZ'     => array('0', '4', '4', '4'),
221
+        'DDZS'    => array('0', '4', '4', '4'),
222
+        'DZ'      => array('0', '4', '4', '4'),
223
+        'DŹ'      => array('0', '4', '4', '4'),
224
+        'DŻ'      => array('0', '4', '4', '4'),
225
+        'DZH'     => array('0', '4', '4', '4'),
226
+        'DZS'     => array('0', '4', '4', '4'),
227
+        'E'       => array('1', '0', '', ''),
228
+        'È'       => array('1', '0', '', ''),
229
+        'É'       => array('1', '0', '', ''),
230
+        'Ê'       => array('1', '0', '', ''),
231
+        'Ë'       => array('1', '0', '', ''),
232
+        'Ĕ'       => array('1', '0', '', ''),
233
+        'Ė'       => array('1', '0', '', ''),
234
+        'Ę'       => array('1', '', '', '6', '', '', ''),
235
+        'Ẹ'       => array('1', '0', '', ''),
236
+        'Ẻ'       => array('1', '0', '', ''),
237
+        'Ẽ'       => array('1', '0', '', ''),
238
+        'Ế'       => array('1', '0', '', ''),
239
+        'Ề'       => array('1', '0', '', ''),
240
+        'Ể'       => array('1', '0', '', ''),
241
+        'Ễ'       => array('1', '0', '', ''),
242
+        'Ệ'       => array('1', '0', '', ''),
243
+        'EAU'     => array('1', '0', '', ''),
244
+        'EI'      => array('1', '0', '1', ''),
245
+        'EJ'      => array('1', '0', '1', ''),
246
+        'EU'      => array('1', '1', '1', ''),
247
+        'EY'      => array('1', '0', '1', ''),
248
+        'F'       => array('0', '7', '7', '7'),
249
+        'FB'      => array('0', '7', '7', '7'),
250
+        'G'       => array('0', '5', '5', '5', '34', '4', '4'),
251
+        'Ğ'       => array('0', '', '', ''),
252
+        'GGY'     => array('0', '5', '5', '5'),
253
+        'GY'      => array('0', '5', '5', '5'),
254
+        'H'       => array('0', '5', '5', '', '5', '5', '5'),
255
+        'I'       => array('1', '0', '', ''),
256
+        'Ì'       => array('1', '0', '', ''),
257
+        'Í'       => array('1', '0', '', ''),
258
+        'Î'       => array('1', '0', '', ''),
259
+        'Ï'       => array('1', '0', '', ''),
260
+        'Ĩ'       => array('1', '0', '', ''),
261
+        'Į'       => array('1', '0', '', ''),
262
+        'İ'       => array('1', '0', '', ''),
263
+        'Ỉ'       => array('1', '0', '', ''),
264
+        'Ị'       => array('1', '0', '', ''),
265
+        'IA'      => array('1', '1', '', ''),
266
+        'IE'      => array('1', '1', '', ''),
267
+        'IO'      => array('1', '1', '', ''),
268
+        'IU'      => array('1', '1', '', ''),
269
+        'J'       => array('0', '1', '', '', '4', '4', '4', '5', '5', ''),
270
+        'K'       => array('0', '5', '5', '5'),
271
+        'KH'      => array('0', '5', '5', '5'),
272
+        'KS'      => array('0', '5', '54', '54'),
273
+        'L'       => array('0', '8', '8', '8'),
274
+        'Ľ'       => array('0', '8', '8', '8'),
275
+        'Ĺ'       => array('0', '8', '8', '8'),
276
+        'Ł'       => array('0', '7', '7', '7', '8', '8', '8'),
277
+        'LL'      => array('0', '8', '8', '8', '58', '8', '8', '1', '8', '8'),
278
+        'LLY'     => array('0', '8', '8', '8', '1', '8', '8'),
279
+        'LY'      => array('0', '8', '8', '8', '1', '8', '8'),
280
+        'M'       => array('0', '6', '6', '6'),
281
+        'MĔ'      => array('0', '66', '66', '66'),
282
+        'MN'      => array('0', '66', '66', '66'),
283
+        'N'       => array('0', '6', '6', '6'),
284
+        'Ń'       => array('0', '6', '6', '6'),
285
+        'Ň'       => array('0', '6', '6', '6'),
286
+        'Ñ'       => array('0', '6', '6', '6'),
287
+        'NM'      => array('0', '66', '66', '66'),
288
+        'O'       => array('1', '0', '', ''),
289
+        'Ò'       => array('1', '0', '', ''),
290
+        'Ó'       => array('1', '0', '', ''),
291
+        'Ô'       => array('1', '0', '', ''),
292
+        'Õ'       => array('1', '0', '', ''),
293
+        'Ö'       => array('1', '0', '', ''),
294
+        'Ø'       => array('1', '0', '', ''),
295
+        'Ő'       => array('1', '0', '', ''),
296
+        'Œ'       => array('1', '0', '', ''),
297
+        'Ơ'       => array('1', '0', '', ''),
298
+        'Ọ'       => array('1', '0', '', ''),
299
+        'Ỏ'       => array('1', '0', '', ''),
300
+        'Ố'       => array('1', '0', '', ''),
301
+        'Ồ'       => array('1', '0', '', ''),
302
+        'Ổ'       => array('1', '0', '', ''),
303
+        'Ỗ'       => array('1', '0', '', ''),
304
+        'Ộ'       => array('1', '0', '', ''),
305
+        'Ớ'       => array('1', '0', '', ''),
306
+        'Ờ'       => array('1', '0', '', ''),
307
+        'Ở'       => array('1', '0', '', ''),
308
+        'Ỡ'       => array('1', '0', '', ''),
309
+        'Ợ'       => array('1', '0', '', ''),
310
+        'OE'      => array('1', '0', '', ''),
311
+        'OI'      => array('1', '0', '1', ''),
312
+        'OJ'      => array('1', '0', '1', ''),
313
+        'OU'      => array('1', '0', '', ''),
314
+        'OY'      => array('1', '0', '1', ''),
315
+        'P'       => array('0', '7', '7', '7'),
316
+        'PF'      => array('0', '7', '7', '7'),
317
+        'PH'      => array('0', '7', '7', '7'),
318
+        'Q'       => array('0', '5', '5', '5'),
319
+        'R'       => array('0', '9', '9', '9'),
320
+        'Ř'       => array('0', '4', '4', '4'),
321
+        'RS'      => array('0', '4', '4', '4', '94', '94', '94'),
322
+        'RZ'      => array('0', '4', '4', '4', '94', '94', '94'),
323
+        'S'       => array('0', '4', '4', '4'),
324
+        'Ś'       => array('0', '4', '4', '4'),
325
+        'Š'       => array('0', '4', '4', '4'),
326
+        'Ş'       => array('0', '4', '4', '4'),
327
+        'SC'      => array('0', '2', '4', '4'),
328
+        'ŠČ'      => array('0', '2', '4', '4'),
329
+        'SCH'     => array('0', '4', '4', '4'),
330
+        'SCHD'    => array('0', '2', '43', '43'),
331
+        'SCHT'    => array('0', '2', '43', '43'),
332
+        'SCHTCH'  => array('0', '2', '4', '4'),
333
+        'SCHTSCH' => array('0', '2', '4', '4'),
334
+        'SCHTSH'  => array('0', '2', '4', '4'),
335
+        'SD'      => array('0', '2', '43', '43'),
336
+        'SH'      => array('0', '4', '4', '4'),
337
+        'SHCH'    => array('0', '2', '4', '4'),
338
+        'SHD'     => array('0', '2', '43', '43'),
339
+        'SHT'     => array('0', '2', '43', '43'),
340
+        'SHTCH'   => array('0', '2', '4', '4'),
341
+        'SHTSH'   => array('0', '2', '4', '4'),
342
+        'ß'       => array('0', '', '4', '4'),
343
+        'ST'      => array('0', '2', '43', '43'),
344
+        'STCH'    => array('0', '2', '4', '4'),
345
+        'STRS'    => array('0', '2', '4', '4'),
346
+        'STRZ'    => array('0', '2', '4', '4'),
347
+        'STSCH'   => array('0', '2', '4', '4'),
348
+        'STSH'    => array('0', '2', '4', '4'),
349
+        'SSZ'     => array('0', '4', '4', '4'),
350
+        'SZ'      => array('0', '4', '4', '4'),
351
+        'SZCS'    => array('0', '2', '4', '4'),
352
+        'SZCZ'    => array('0', '2', '4', '4'),
353
+        'SZD'     => array('0', '2', '43', '43'),
354
+        'SZT'     => array('0', '2', '43', '43'),
355
+        'T'       => array('0', '3', '3', '3'),
356
+        'Ť'       => array('0', '3', '3', '3'),
357
+        'Ţ'       => array('0', '3', '3', '3', '4', '4', '4'),
358
+        'TC'      => array('0', '4', '4', '4'),
359
+        'TCH'     => array('0', '4', '4', '4'),
360
+        'TH'      => array('0', '3', '3', '3'),
361
+        'TRS'     => array('0', '4', '4', '4'),
362
+        'TRZ'     => array('0', '4', '4', '4'),
363
+        'TS'      => array('0', '4', '4', '4'),
364
+        'TSCH'    => array('0', '4', '4', '4'),
365
+        'TSH'     => array('0', '4', '4', '4'),
366
+        'TSZ'     => array('0', '4', '4', '4'),
367
+        'TTCH'    => array('0', '4', '4', '4'),
368
+        'TTS'     => array('0', '4', '4', '4'),
369
+        'TTSCH'   => array('0', '4', '4', '4'),
370
+        'TTSZ'    => array('0', '4', '4', '4'),
371
+        'TTZ'     => array('0', '4', '4', '4'),
372
+        'TZ'      => array('0', '4', '4', '4'),
373
+        'TZS'     => array('0', '4', '4', '4'),
374
+        'U'       => array('1', '0', '', ''),
375
+        'Ù'       => array('1', '0', '', ''),
376
+        'Ú'       => array('1', '0', '', ''),
377
+        'Û'       => array('1', '0', '', ''),
378
+        'Ü'       => array('1', '0', '', ''),
379
+        'Ũ'       => array('1', '0', '', ''),
380
+        'Ū'       => array('1', '0', '', ''),
381
+        'Ů'       => array('1', '0', '', ''),
382
+        'Ű'       => array('1', '0', '', ''),
383
+        'Ų'       => array('1', '0', '', ''),
384
+        'Ư'       => array('1', '0', '', ''),
385
+        'Ụ'       => array('1', '0', '', ''),
386
+        'Ủ'       => array('1', '0', '', ''),
387
+        'Ứ'       => array('1', '0', '', ''),
388
+        'Ừ'       => array('1', '0', '', ''),
389
+        'Ử'       => array('1', '0', '', ''),
390
+        'Ữ'       => array('1', '0', '', ''),
391
+        'Ự'       => array('1', '0', '', ''),
392
+        'UE'      => array('1', '0', '', ''),
393
+        'UI'      => array('1', '0', '1', ''),
394
+        'UJ'      => array('1', '0', '1', ''),
395
+        'UY'      => array('1', '0', '1', ''),
396
+        'UW'      => array('1', '0', '1', '', '0', '7', '7'),
397
+        'V'       => array('0', '7', '7', '7'),
398
+        'W'       => array('0', '7', '7', '7'),
399
+        'X'       => array('0', '5', '54', '54'),
400
+        'Y'       => array('1', '1', '', ''),
401
+        'Ý'       => array('1', '1', '', ''),
402
+        'Ỳ'       => array('1', '1', '', ''),
403
+        'Ỵ'       => array('1', '1', '', ''),
404
+        'Ỷ'       => array('1', '1', '', ''),
405
+        'Ỹ'       => array('1', '1', '', ''),
406
+        'Z'       => array('0', '4', '4', '4'),
407
+        'Ź'       => array('0', '4', '4', '4'),
408
+        'Ż'       => array('0', '4', '4', '4'),
409
+        'Ž'       => array('0', '4', '4', '4'),
410
+        'ZD'      => array('0', '2', '43', '43'),
411
+        'ZDZ'     => array('0', '2', '4', '4'),
412
+        'ZDZH'    => array('0', '2', '4', '4'),
413
+        'ZH'      => array('0', '4', '4', '4'),
414
+        'ZHD'     => array('0', '2', '43', '43'),
415
+        'ZHDZH'   => array('0', '2', '4', '4'),
416
+        'ZS'      => array('0', '4', '4', '4'),
417
+        'ZSCH'    => array('0', '4', '4', '4'),
418
+        'ZSH'     => array('0', '4', '4', '4'),
419
+        'ZZS'     => array('0', '4', '4', '4'),
420
+        // Cyrillic alphabet
421
+        'А'   => array('1', '0', '', ''),
422
+        'Б'   => array('0', '7', '7', '7'),
423
+        'В'   => array('0', '7', '7', '7'),
424
+        'Г'   => array('0', '5', '5', '5'),
425
+        'Д'   => array('0', '3', '3', '3'),
426
+        'ДЗ'  => array('0', '4', '4', '4'),
427
+        'Е'   => array('1', '0', '', ''),
428
+        'Ё'   => array('1', '0', '', ''),
429
+        'Ж'   => array('0', '4', '4', '4'),
430
+        'З'   => array('0', '4', '4', '4'),
431
+        'И'   => array('1', '0', '', ''),
432
+        'Й'   => array('1', '1', '', '', '4', '4', '4'),
433
+        'К'   => array('0', '5', '5', '5'),
434
+        'Л'   => array('0', '8', '8', '8'),
435
+        'М'   => array('0', '6', '6', '6'),
436
+        'Н'   => array('0', '6', '6', '6'),
437
+        'О'   => array('1', '0', '', ''),
438
+        'П'   => array('0', '7', '7', '7'),
439
+        'Р'   => array('0', '9', '9', '9'),
440
+        'РЖ'  => array('0', '4', '4', '4'),
441
+        'С'   => array('0', '4', '4', '4'),
442
+        'Т'   => array('0', '3', '3', '3'),
443
+        'У'   => array('1', '0', '', ''),
444
+        'Ф'   => array('0', '7', '7', '7'),
445
+        'Х'   => array('0', '5', '5', '5'),
446
+        'Ц'   => array('0', '4', '4', '4'),
447
+        'Ч'   => array('0', '4', '4', '4'),
448
+        'Ш'   => array('0', '4', '4', '4'),
449
+        'Щ'   => array('0', '2', '4', '4'),
450
+        'Ъ'   => array('0', '', '', ''),
451
+        'Ы'   => array('0', '1', '', ''),
452
+        'Ь'   => array('0', '', '', ''),
453
+        'Э'   => array('1', '0', '', ''),
454
+        'Ю'   => array('0', '1', '', ''),
455
+        'Я'   => array('0', '1', '', ''),
456
+        // Greek alphabet
457
+        'Α'   => array('1', '0', '', ''),
458
+        'Ά'   => array('1', '0', '', ''),
459
+        'ΑΙ'  => array('1', '0', '1', ''),
460
+        'ΑΥ'  => array('1', '0', '1', ''),
461
+        'Β'   => array('0', '7', '7', '7'),
462
+        'Γ'   => array('0', '5', '5', '5'),
463
+        'Δ'   => array('0', '3', '3', '3'),
464
+        'Ε'   => array('1', '0', '', ''),
465
+        'Έ'   => array('1', '0', '', ''),
466
+        'ΕΙ'  => array('1', '0', '1', ''),
467
+        'ΕΥ'  => array('1', '1', '1', ''),
468
+        'Ζ'   => array('0', '4', '4', '4'),
469
+        'Η'   => array('1', '0', '', ''),
470
+        'Ή'   => array('1', '0', '', ''),
471
+        'Θ'   => array('0', '3', '3', '3'),
472
+        'Ι'   => array('1', '0', '', ''),
473
+        'Ί'   => array('1', '0', '', ''),
474
+        'Ϊ'   => array('1', '0', '', ''),
475
+        'ΐ'   => array('1', '0', '', ''),
476
+        'Κ'   => array('0', '5', '5', '5'),
477
+        'Λ'   => array('0', '8', '8', '8'),
478
+        'Μ'   => array('0', '6', '6', '6'),
479
+        'ΜΠ'  => array('0', '7', '7', '7'),
480
+        'Ν'   => array('0', '6', '6', '6'),
481
+        'ΝΤ'  => array('0', '3', '3', '3'),
482
+        'Ξ'   => array('0', '5', '54', '54'),
483
+        'Ο'   => array('1', '0', '', ''),
484
+        'Ό'   => array('1', '0', '', ''),
485
+        'ΟΙ'  => array('1', '0', '1', ''),
486
+        'ΟΥ'  => array('1', '0', '1', ''),
487
+        'Π'   => array('0', '7', '7', '7'),
488
+        'Ρ'   => array('0', '9', '9', '9'),
489
+        'Σ'   => array('0', '4', '4', '4'),
490
+        'ς'   => array('0', '', '', '4'),
491
+        'Τ'   => array('0', '3', '3', '3'),
492
+        'ΤΖ'  => array('0', '4', '4', '4'),
493
+        'ΤΣ'  => array('0', '4', '4', '4'),
494
+        'Υ'   => array('1', '1', '', ''),
495
+        'Ύ'   => array('1', '1', '', ''),
496
+        'Ϋ'   => array('1', '1', '', ''),
497
+        'ΰ'   => array('1', '1', '', ''),
498
+        'ΥΚ'  => array('1', '5', '5', '5'),
499
+        'ΥΥ'  => array('1', '65', '65', '65'),
500
+        'Φ'   => array('0', '7', '7', '7'),
501
+        'Χ'   => array('0', '5', '5', '5'),
502
+        'Ψ'   => array('0', '7', '7', '7'),
503
+        'Ω'   => array('1', '0', '', ''),
504
+        'Ώ'   => array('1', '0', '', ''),
505
+        // Hebrew alphabet
506
+        'א'     => array('1', '0', '', ''),
507
+        'או'    => array('1', '0', '7', ''),
508
+        'אג'    => array('1', '4', '4', '4', '5', '5', '5', '34', '34', '34'),
509
+        'בב'    => array('0', '7', '7', '7', '77', '77', '77'),
510
+        'ב'     => array('0', '7', '7', '7'),
511
+        'גג'    => array('0', '4', '4', '4', '5', '5', '5', '45', '45', '45', '55', '55', '55', '54', '54', '54'),
512
+        'גד'    => array('0', '43', '43', '43', '53', '53', '53'),
513
+        'גה'    => array('0', '45', '45', '45', '55', '55', '55'),
514
+        'גז'    => array('0', '44', '44', '44', '45', '45', '45'),
515
+        'גח'    => array('0', '45', '45', '45', '55', '55', '55'),
516
+        'גכ'    => array('0', '45', '45', '45', '55', '55', '55'),
517
+        'גך'    => array('0', '45', '45', '45', '55', '55', '55'),
518
+        'גצ'    => array('0', '44', '44', '44', '45', '45', '45'),
519
+        'גץ'    => array('0', '44', '44', '44', '45', '45', '45'),
520
+        'גק'    => array('0', '45', '45', '45', '54', '54', '54'),
521
+        'גש'    => array('0', '44', '44', '44', '54', '54', '54'),
522
+        'גת'    => array('0', '43', '43', '43', '53', '53', '53'),
523
+        'ג'     => array('0', '4', '4', '4', '5', '5', '5'),
524
+        'דז'    => array('0', '4', '4', '4'),
525
+        'דד'    => array('0', '3', '3', '3', '33', '33', '33'),
526
+        'דט'    => array('0', '33', '33', '33'),
527
+        'דש'    => array('0', '4', '4', '4'),
528
+        'דצ'    => array('0', '4', '4', '4'),
529
+        'דץ'    => array('0', '4', '4', '4'),
530
+        'ד'     => array('0', '3', '3', '3'),
531
+        'הג'    => array('0', '54', '54', '54', '55', '55', '55'),
532
+        'הכ'    => array('0', '55', '55', '55'),
533
+        'הח'    => array('0', '55', '55', '55'),
534
+        'הק'    => array('0', '55', '55', '55', '5', '5', '5'),
535
+        'הה'    => array('0', '5', '5', '', '55', '55', ''),
536
+        'ה'     => array('0', '5', '5', ''),
537
+        'וי'    => array('1', '', '', '', '7', '7', '7'),
538
+        'ו'     => array('1', '7', '7', '7', '7', '', ''),
539
+        'וו'    => array('1', '7', '7', '7', '7', '', ''),
540
+        'וופ'   => array('1', '7', '7', '7', '77', '77', '77'),
541
+        'זש'    => array('0', '4', '4', '4', '44', '44', '44'),
542
+        'זדז'   => array('0', '2', '4', '4'),
543
+        'ז'     => array('0', '4', '4', '4'),
544
+        'זג'    => array('0', '44', '44', '44', '45', '45', '45'),
545
+        'זז'    => array('0', '4', '4', '4', '44', '44', '44'),
546
+        'זס'    => array('0', '44', '44', '44'),
547
+        'זצ'    => array('0', '44', '44', '44'),
548
+        'זץ'    => array('0', '44', '44', '44'),
549
+        'חג'    => array('0', '54', '54', '54', '53', '53', '53'),
550
+        'חח'    => array('0', '5', '5', '5', '55', '55', '55'),
551
+        'חק'    => array('0', '55', '55', '55', '5', '5', '5'),
552
+        'חכ'    => array('0', '45', '45', '45', '55', '55', '55'),
553
+        'חס'    => array('0', '5', '54', '54'),
554
+        'חש'    => array('0', '5', '54', '54'),
555
+        'ח'     => array('0', '5', '5', '5'),
556
+        'טש'    => array('0', '4', '4', '4'),
557
+        'טד'    => array('0', '33', '33', '33'),
558
+        'טי'    => array('0', '3', '3', '3', '4', '4', '4', '3', '3', '34'),
559
+        'טת'    => array('0', '33', '33', '33'),
560
+        'טט'    => array('0', '3', '3', '3', '33', '33', '33'),
561
+        'ט'     => array('0', '3', '3', '3'),
562
+        'י'     => array('1', '1', '', ''),
563
+        'יא'    => array('1', '1', '', '', '1', '1', '1'),
564
+        'כג'    => array('0', '55', '55', '55', '54', '54', '54'),
565
+        'כש'    => array('0', '5', '54', '54'),
566
+        'כס'    => array('0', '5', '54', '54'),
567
+        'ככ'    => array('0', '5', '5', '5', '55', '55', '55'),
568
+        'כך'    => array('0', '5', '5', '5', '55', '55', '55'),
569
+        'כ'     => array('0', '5', '5', '5'),
570
+        'כח'    => array('0', '55', '55', '55', '5', '5', '5'),
571
+        'ך'     => array('0', '', '5', '5'),
572
+        'ל'     => array('0', '8', '8', '8'),
573
+        'לל'    => array('0', '88', '88', '88', '8', '8', '8'),
574
+        'מנ'    => array('0', '66', '66', '66'),
575
+        'מן'    => array('0', '66', '66', '66'),
576
+        'ממ'    => array('0', '6', '6', '6', '66', '66', '66'),
577
+        'מם'    => array('0', '6', '6', '6', '66', '66', '66'),
578
+        'מ'     => array('0', '6', '6', '6'),
579
+        'ם'     => array('0', '', '6', '6'),
580
+        'נמ'    => array('0', '66', '66', '66'),
581
+        'נם'    => array('0', '66', '66', '66'),
582
+        'ננ'    => array('0', '6', '6', '6', '66', '66', '66'),
583
+        'נן'    => array('0', '6', '6', '6', '66', '66', '66'),
584
+        'נ'     => array('0', '6', '6', '6'),
585
+        'ן'     => array('0', '', '6', '6'),
586
+        'סתש'   => array('0', '2', '4', '4'),
587
+        'סתז'   => array('0', '2', '4', '4'),
588
+        'סטז'   => array('0', '2', '4', '4'),
589
+        'סטש'   => array('0', '2', '4', '4'),
590
+        'סצד'   => array('0', '2', '4', '4'),
591
+        'סט'    => array('0', '2', '4', '4', '43', '43', '43'),
592
+        'סת'    => array('0', '2', '4', '4', '43', '43', '43'),
593
+        'סג'    => array('0', '44', '44', '44', '4', '4', '4'),
594
+        'סס'    => array('0', '4', '4', '4', '44', '44', '44'),
595
+        'סצ'    => array('0', '44', '44', '44'),
596
+        'סץ'    => array('0', '44', '44', '44'),
597
+        'סז'    => array('0', '44', '44', '44'),
598
+        'סש'    => array('0', '44', '44', '44'),
599
+        'ס'     => array('0', '4', '4', '4'),
600
+        'ע'     => array('1', '0', '', ''),
601
+        'פב'    => array('0', '7', '7', '7', '77', '77', '77'),
602
+        'פוו'   => array('0', '7', '7', '7', '77', '77', '77'),
603
+        'פפ'    => array('0', '7', '7', '7', '77', '77', '77'),
604
+        'פף'    => array('0', '7', '7', '7', '77', '77', '77'),
605
+        'פ'     => array('0', '7', '7', '7'),
606
+        'ף'     => array('0', '', '7', '7'),
607
+        'צג'    => array('0', '44', '44', '44', '45', '45', '45'),
608
+        'צז'    => array('0', '44', '44', '44'),
609
+        'צס'    => array('0', '44', '44', '44'),
610
+        'צצ'    => array('0', '4', '4', '4', '5', '5', '5', '44', '44', '44', '54', '54', '54', '45', '45', '45'),
611
+        'צץ'    => array('0', '4', '4', '4', '5', '5', '5', '44', '44', '44', '54', '54', '54'),
612
+        'צש'    => array('0', '44', '44', '44', '4', '4', '4', '5', '5', '5'),
613
+        'צ'     => array('0', '4', '4', '4', '5', '5', '5'),
614
+        'ץ'     => array('0', '', '4', '4'),
615
+        'קה'    => array('0', '55', '55', '5'),
616
+        'קס'    => array('0', '5', '54', '54'),
617
+        'קש'    => array('0', '5', '54', '54'),
618
+        'קק'    => array('0', '5', '5', '5', '55', '55', '55'),
619
+        'קח'    => array('0', '55', '55', '55'),
620
+        'קכ'    => array('0', '55', '55', '55'),
621
+        'קך'    => array('0', '55', '55', '55'),
622
+        'קג'    => array('0', '55', '55', '55', '54', '54', '54'),
623
+        'ק'     => array('0', '5', '5', '5'),
624
+        'רר'    => array('0', '99', '99', '99', '9', '9', '9'),
625
+        'ר'     => array('0', '9', '9', '9'),
626
+        'שטז'   => array('0', '2', '4', '4'),
627
+        'שתש'   => array('0', '2', '4', '4'),
628
+        'שתז'   => array('0', '2', '4', '4'),
629
+        'שטש'   => array('0', '2', '4', '4'),
630
+        'שד'    => array('0', '2', '43', '43'),
631
+        'שז'    => array('0', '44', '44', '44'),
632
+        'שס'    => array('0', '44', '44', '44'),
633
+        'שת'    => array('0', '2', '43', '43'),
634
+        'שג'    => array('0', '4', '4', '4', '44', '44', '44', '4', '43', '43'),
635
+        'שט'    => array('0', '2', '43', '43', '44', '44', '44'),
636
+        'שצ'    => array('0', '44', '44', '44', '45', '45', '45'),
637
+        'שץ'    => array('0', '44', '', '44', '45', '', '45'),
638
+        'שש'    => array('0', '4', '4', '4', '44', '44', '44'),
639
+        'ש'     => array('0', '4', '4', '4'),
640
+        'תג'    => array('0', '34', '34', '34'),
641
+        'תז'    => array('0', '34', '34', '34'),
642
+        'תש'    => array('0', '4', '4', '4'),
643
+        'תת'    => array('0', '3', '3', '3', '4', '4', '4', '33', '33', '33', '44', '44', '44', '34', '34', '34', '43', '43', '43'),
644
+        'ת'     => array('0', '3', '3', '3', '4', '4', '4'),
645
+        // Arabic alphabet
646
+        'ا'   => array('1', '0', '', ''),
647
+        'ب'   => array('0', '7', '7', '7'),
648
+        'ت'   => array('0', '3', '3', '3'),
649
+        'ث'   => array('0', '3', '3', '3'),
650
+        'ج'   => array('0', '4', '4', '4'),
651
+        'ح'   => array('0', '5', '5', '5'),
652
+        'خ'   => array('0', '5', '5', '5'),
653
+        'د'   => array('0', '3', '3', '3'),
654
+        'ذ'   => array('0', '3', '3', '3'),
655
+        'ر'   => array('0', '9', '9', '9'),
656
+        'ز'   => array('0', '4', '4', '4'),
657
+        'س'   => array('0', '4', '4', '4'),
658
+        'ش'   => array('0', '4', '4', '4'),
659
+        'ص'   => array('0', '4', '4', '4'),
660
+        'ض'   => array('0', '3', '3', '3'),
661
+        'ط'   => array('0', '3', '3', '3'),
662
+        'ظ'   => array('0', '4', '4', '4'),
663
+        'ع'   => array('1', '0', '', ''),
664
+        'غ'   => array('0', '0', '', ''),
665
+        'ف'   => array('0', '7', '7', '7'),
666
+        'ق'   => array('0', '5', '5', '5'),
667
+        'ك'   => array('0', '5', '5', '5'),
668
+        'ل'   => array('0', '8', '8', '8'),
669
+        'لا'  => array('0', '8', '8', '8'),
670
+        'م'   => array('0', '6', '6', '6'),
671
+        'ن'   => array('0', '6', '6', '6'),
672
+        'هن'  => array('0', '66', '66', '66'),
673
+        'ه'   => array('0', '5', '5', ''),
674
+        'و'   => array('1', '', '', '', '7', '', ''),
675
+        'ي'   => array('0', '1', '', ''),
676
+        'آ'   => array('0', '1', '', ''),
677
+        'ة'   => array('0', '', '', '3'),
678
+        'ی'   => array('0', '1', '', ''),
679
+        'ى'   => array('1', '1', '', ''),
680
+    );
681 681
 
682
-	/**
683
-	 * Calculate the Daitch-Mokotoff soundex for a word.
684
-	 *
685
-	 * @param string $name
686
-	 *
687
-	 * @return string[] List of possible DM codes for the word.
688
-	 */
689
-	private static function daitchMokotoffWord($name) {
690
-		// Apply special transformation rules to the input string
691
-		$name = I18N::strtoupper($name);
692
-		foreach (self::$transformNameTable as $transformRule) {
693
-			$name = str_replace($transformRule[0], $transformRule[1], $name);
694
-		}
682
+    /**
683
+     * Calculate the Daitch-Mokotoff soundex for a word.
684
+     *
685
+     * @param string $name
686
+     *
687
+     * @return string[] List of possible DM codes for the word.
688
+     */
689
+    private static function daitchMokotoffWord($name) {
690
+        // Apply special transformation rules to the input string
691
+        $name = I18N::strtoupper($name);
692
+        foreach (self::$transformNameTable as $transformRule) {
693
+            $name = str_replace($transformRule[0], $transformRule[1], $name);
694
+        }
695 695
 
696
-		// Initialize
697
-		$name_script = I18N::textScript($name);
698
-		$noVowels    = ($name_script == 'Hebr' || $name_script == 'Arab');
696
+        // Initialize
697
+        $name_script = I18N::textScript($name);
698
+        $noVowels    = ($name_script == 'Hebr' || $name_script == 'Arab');
699 699
 
700
-		$lastPos         = strlen($name) - 1;
701
-		$currPos         = 0;
702
-		$state           = 1; // 1: start of input string, 2: before vowel, 3: other
703
-		$result          = array(); // accumulate complete 6-digit D-M codes here
704
-		$partialResult   = array(); // accumulate incomplete D-M codes here
705
-		$partialResult[] = array('!'); // initialize 1st partial result  ('!' stops "duplicate sound" check)
700
+        $lastPos         = strlen($name) - 1;
701
+        $currPos         = 0;
702
+        $state           = 1; // 1: start of input string, 2: before vowel, 3: other
703
+        $result          = array(); // accumulate complete 6-digit D-M codes here
704
+        $partialResult   = array(); // accumulate incomplete D-M codes here
705
+        $partialResult[] = array('!'); // initialize 1st partial result  ('!' stops "duplicate sound" check)
706 706
 
707
-		// Loop through the input string.
708
-		// Stop when the string is exhausted or when no more partial results remain
709
-		while (count($partialResult) !== 0 && $currPos <= $lastPos) {
710
-			// Find the DM coding table entry for the chunk at the current position
711
-			$thisEntry = substr($name, $currPos, self::MAXCHAR); // Get maximum length chunk
712
-			while ($thisEntry != '') {
713
-				if (isset(self::$dmsounds[$thisEntry])) {
714
-					break;
715
-				}
716
-				$thisEntry = substr($thisEntry, 0, -1); // Not in table: try a shorter chunk
717
-			}
718
-			if ($thisEntry === '') {
719
-				$currPos++; // Not in table: advance pointer to next byte
720
-				continue; // and try again
721
-			}
707
+        // Loop through the input string.
708
+        // Stop when the string is exhausted or when no more partial results remain
709
+        while (count($partialResult) !== 0 && $currPos <= $lastPos) {
710
+            // Find the DM coding table entry for the chunk at the current position
711
+            $thisEntry = substr($name, $currPos, self::MAXCHAR); // Get maximum length chunk
712
+            while ($thisEntry != '') {
713
+                if (isset(self::$dmsounds[$thisEntry])) {
714
+                    break;
715
+                }
716
+                $thisEntry = substr($thisEntry, 0, -1); // Not in table: try a shorter chunk
717
+            }
718
+            if ($thisEntry === '') {
719
+                $currPos++; // Not in table: advance pointer to next byte
720
+                continue; // and try again
721
+            }
722 722
 
723
-			$soundTableEntry = self::$dmsounds[$thisEntry];
724
-			$workingResult   = $partialResult;
725
-			$partialResult   = array();
726
-			$currPos += strlen($thisEntry);
723
+            $soundTableEntry = self::$dmsounds[$thisEntry];
724
+            $workingResult   = $partialResult;
725
+            $partialResult   = array();
726
+            $currPos += strlen($thisEntry);
727 727
 
728
-			// Not at beginning of input string
729
-			if ($state != 1) {
730
-				if ($currPos <= $lastPos) {
731
-					// Determine whether the next chunk is a vowel
732
-					$nextEntry = substr($name, $currPos, self::MAXCHAR); // Get maximum length chunk
733
-					while ($nextEntry != '') {
734
-						if (isset(self::$dmsounds[$nextEntry])) {
735
-							break;
736
-						}
737
-						$nextEntry = substr($nextEntry, 0, -1); // Not in table: try a shorter chunk
738
-					}
739
-				} else {
740
-					$nextEntry = '';
741
-				}
742
-				if ($nextEntry != '' && self::$dmsounds[$nextEntry][0] != '0') {
743
-					$state = 2;
744
-				} else {
745
-					// Next chunk is a vowel
746
-					$state = 3;
747
-				}
748
-			}
728
+            // Not at beginning of input string
729
+            if ($state != 1) {
730
+                if ($currPos <= $lastPos) {
731
+                    // Determine whether the next chunk is a vowel
732
+                    $nextEntry = substr($name, $currPos, self::MAXCHAR); // Get maximum length chunk
733
+                    while ($nextEntry != '') {
734
+                        if (isset(self::$dmsounds[$nextEntry])) {
735
+                            break;
736
+                        }
737
+                        $nextEntry = substr($nextEntry, 0, -1); // Not in table: try a shorter chunk
738
+                    }
739
+                } else {
740
+                    $nextEntry = '';
741
+                }
742
+                if ($nextEntry != '' && self::$dmsounds[$nextEntry][0] != '0') {
743
+                    $state = 2;
744
+                } else {
745
+                    // Next chunk is a vowel
746
+                    $state = 3;
747
+                }
748
+            }
749 749
 
750
-			while ($state < count($soundTableEntry)) {
751
-				// empty means 'ignore this sound in this state'
752
-				if ($soundTableEntry[$state] == '') {
753
-					foreach ($workingResult as $workingEntry) {
754
-						$tempEntry = $workingEntry;
755
-						$tempEntry[count($tempEntry) - 1] .= '!'; // Prevent false 'doubles'
756
-						$partialResult[] = $tempEntry;
757
-					}
758
-				} else {
759
-					foreach ($workingResult as $workingEntry) {
760
-						if ($soundTableEntry[$state] !== $workingEntry[count($workingEntry) - 1]) {
761
-							// Incoming sound isn't a duplicate of the previous sound
762
-							$workingEntry[] = $soundTableEntry[$state];
763
-						} else {
764
-							// Incoming sound is a duplicate of the previous sound
765
-							// For Hebrew and Arabic, we need to create a pair of D-M sound codes,
766
-							// one of the pair with only a single occurrence of the duplicate sound,
767
-							// the other with both occurrences
768
-							if ($noVowels) {
769
-								$workingEntry[] = $soundTableEntry[$state];
770
-							}
771
-						}
772
-						if (count($workingEntry) < 7) {
773
-							$partialResult[] = $workingEntry;
774
-						} else {
775
-							// This is the 6th code in the sequence
776
-							// We're looking for 7 entries because the first is '!' and doesn't count
777
-							$tempResult = str_replace('!', '', implode('', $workingEntry));
778
-							// Only return codes from recognisable sounds
779
-							if ($tempResult) {
780
-								$result[] = substr($tempResult . '000000', 0, 6);
781
-							}
782
-						}
783
-					}
784
-				}
785
-				$state = $state + 3; // Advance to next triplet while keeping the same basic state
786
-			}
787
-		}
750
+            while ($state < count($soundTableEntry)) {
751
+                // empty means 'ignore this sound in this state'
752
+                if ($soundTableEntry[$state] == '') {
753
+                    foreach ($workingResult as $workingEntry) {
754
+                        $tempEntry = $workingEntry;
755
+                        $tempEntry[count($tempEntry) - 1] .= '!'; // Prevent false 'doubles'
756
+                        $partialResult[] = $tempEntry;
757
+                    }
758
+                } else {
759
+                    foreach ($workingResult as $workingEntry) {
760
+                        if ($soundTableEntry[$state] !== $workingEntry[count($workingEntry) - 1]) {
761
+                            // Incoming sound isn't a duplicate of the previous sound
762
+                            $workingEntry[] = $soundTableEntry[$state];
763
+                        } else {
764
+                            // Incoming sound is a duplicate of the previous sound
765
+                            // For Hebrew and Arabic, we need to create a pair of D-M sound codes,
766
+                            // one of the pair with only a single occurrence of the duplicate sound,
767
+                            // the other with both occurrences
768
+                            if ($noVowels) {
769
+                                $workingEntry[] = $soundTableEntry[$state];
770
+                            }
771
+                        }
772
+                        if (count($workingEntry) < 7) {
773
+                            $partialResult[] = $workingEntry;
774
+                        } else {
775
+                            // This is the 6th code in the sequence
776
+                            // We're looking for 7 entries because the first is '!' and doesn't count
777
+                            $tempResult = str_replace('!', '', implode('', $workingEntry));
778
+                            // Only return codes from recognisable sounds
779
+                            if ($tempResult) {
780
+                                $result[] = substr($tempResult . '000000', 0, 6);
781
+                            }
782
+                        }
783
+                    }
784
+                }
785
+                $state = $state + 3; // Advance to next triplet while keeping the same basic state
786
+            }
787
+        }
788 788
 
789
-		// Zero-fill and copy all remaining partial results
790
-		foreach ($partialResult as $workingEntry) {
791
-			$tempResult = str_replace('!', '', implode('', $workingEntry));
792
-			// Only return codes from recognisable sounds
793
-			if ($tempResult) {
794
-				$result[] = substr($tempResult . '000000', 0, 6);
795
-			}
796
-		}
789
+        // Zero-fill and copy all remaining partial results
790
+        foreach ($partialResult as $workingEntry) {
791
+            $tempResult = str_replace('!', '', implode('', $workingEntry));
792
+            // Only return codes from recognisable sounds
793
+            if ($tempResult) {
794
+                $result[] = substr($tempResult . '000000', 0, 6);
795
+            }
796
+        }
797 797
 
798
-		return $result;
799
-	}
798
+        return $result;
799
+    }
800 800
 }
Please login to merge, or discard this patch.
Braces   +12 added lines, -6 removed lines patch added patch discarded remove patch
@@ -18,13 +18,15 @@  discard block
 block discarded – undo
18 18
 /**
19 19
  * Phonetic matching of strings.
20 20
  */
21
-class Soundex {
21
+class Soundex
22
+{
22 23
 	/**
23 24
 	 * Which algorithms are supported.
24 25
 	 *
25 26
 	 * @return string[]
26 27
 	 */
27
-	public static function getAlgorithms() {
28
+	public static function getAlgorithms()
29
+	{
28 30
 		return array(
29 31
 			'std' => /* I18N: http://en.wikipedia.org/wiki/Soundex */ I18N::translate('Russell'),
30 32
 			'dm'  => /* I18N: http://en.wikipedia.org/wiki/Daitch–Mokotoff_Soundex */ I18N::translate('Daitch-Mokotoff'),
@@ -39,7 +41,8 @@  discard block
 block discarded – undo
39 41
 	 *
40 42
 	 * @return bool
41 43
 	 */
42
-	public static function compare($soundex1, $soundex2) {
44
+	public static function compare($soundex1, $soundex2)
45
+	{
43 46
 		if ($soundex1 && $soundex2) {
44 47
 			foreach (explode(':', $soundex1) as $code) {
45 48
 				if (strpos($soundex2, $code) !== false) {
@@ -58,7 +61,8 @@  discard block
 block discarded – undo
58 61
 	 *
59 62
 	 * @return null|string
60 63
 	 */
61
-	public static function russell($text) {
64
+	public static function russell($text)
65
+	{
62 66
 		$words         = preg_split('/\s/', $text, -1, PREG_SPLIT_NO_EMPTY);
63 67
 		$soundex_array = array();
64 68
 		foreach ($words as $word) {
@@ -89,7 +93,8 @@  discard block
 block discarded – undo
89 93
 	 *
90 94
 	 * @return null|string
91 95
 	 */
92
-	public static function daitchMokotoff($text) {
96
+	public static function daitchMokotoff($text)
97
+	{
93 98
 		$words         = preg_split('/\s/', $text, -1, PREG_SPLIT_NO_EMPTY);
94 99
 		$soundex_array = array();
95 100
 		foreach ($words as $word) {
@@ -686,7 +691,8 @@  discard block
 block discarded – undo
686 691
 	 *
687 692
 	 * @return string[] List of possible DM codes for the word.
688 693
 	 */
689
-	private static function daitchMokotoffWord($name) {
694
+	private static function daitchMokotoffWord($name)
695
+	{
690 696
 		// Apply special transformation rules to the input string
691 697
 		$name = I18N::strtoupper($name);
692 698
 		foreach (self::$transformNameTable as $transformRule) {
Please login to merge, or discard this patch.
app/Report/ReportParserSetup.php 2 patches
Indentation   +110 added lines, -110 removed lines patch added patch discarded remove patch
@@ -22,125 +22,125 @@
 block discarded – undo
22 22
  * Class ReportParserSetup - parse a report.xml file and extract the setup options.
23 23
  */
24 24
 class ReportParserSetup extends ReportParserBase {
25
-	/** @var array An array of report options/parameters */
26
-	private $data = array();
25
+    /** @var array An array of report options/parameters */
26
+    private $data = array();
27 27
 
28
-	/** @var string[] An array of input attributes */
29
-	private $input;
28
+    /** @var string[] An array of input attributes */
29
+    private $input;
30 30
 
31
-	/**
32
-	 * Return the parsed data.
33
-	 *
34
-	 * @return array
35
-	 */
36
-	public function reportProperties() {
37
-		return $this->data;
38
-	}
31
+    /**
32
+     * Return the parsed data.
33
+     *
34
+     * @return array
35
+     */
36
+    public function reportProperties() {
37
+        return $this->data;
38
+    }
39 39
 
40
-	/**
41
-	 * Process <Report>
42
-	 *
43
-	 * @param string[] $attrs
44
-	 */
45
-	protected function reportStartHandler($attrs) {
46
-		$access = Auth::PRIV_PRIVATE;
47
-		if (isset($attrs['access'])) {
48
-			if (isset($$attrs["access"])) {
49
-				$access = $$attrs["access"];
50
-			}
51
-		}
52
-		$this->data['access'] = $access;
40
+    /**
41
+     * Process <Report>
42
+     *
43
+     * @param string[] $attrs
44
+     */
45
+    protected function reportStartHandler($attrs) {
46
+        $access = Auth::PRIV_PRIVATE;
47
+        if (isset($attrs['access'])) {
48
+            if (isset($$attrs["access"])) {
49
+                $access = $$attrs["access"];
50
+            }
51
+        }
52
+        $this->data['access'] = $access;
53 53
 
54
-		if (isset($attrs['icon'])) {
55
-			$this->data['icon'] = $attrs['icon'];
56
-		} else {
57
-			$this->data['icon'] = '';
58
-		}
59
-	}
54
+        if (isset($attrs['icon'])) {
55
+            $this->data['icon'] = $attrs['icon'];
56
+        } else {
57
+            $this->data['icon'] = '';
58
+        }
59
+    }
60 60
 
61
-	/**
62
-	 * Process <var var="">
63
-	 *
64
-	 * @param string[] $attrs
65
-	 */
66
-	protected function varStartHandler($attrs) {
67
-		if (preg_match('/^I18N::number\((.+)\)$/', $attrs['var'], $match)) {
68
-			$this->text .= I18N::number($match[1]);
69
-		} elseif (preg_match('/^I18N::translate\(\'(.+)\'\)$/', $attrs['var'], $match)) {
70
-			$this->text .= I18N::translate($match[1]);
71
-		} elseif (preg_match('/^I18N::translateContext\(\'(.+)\', *\'(.+)\'\)$/', $attrs['var'], $match)) {
72
-			$this->text .= I18N::translateContext($match[1], $match[2]);
73
-		} else {
74
-			$this->text .= $attrs['var'];
75
-		}
76
-	}
61
+    /**
62
+     * Process <var var="">
63
+     *
64
+     * @param string[] $attrs
65
+     */
66
+    protected function varStartHandler($attrs) {
67
+        if (preg_match('/^I18N::number\((.+)\)$/', $attrs['var'], $match)) {
68
+            $this->text .= I18N::number($match[1]);
69
+        } elseif (preg_match('/^I18N::translate\(\'(.+)\'\)$/', $attrs['var'], $match)) {
70
+            $this->text .= I18N::translate($match[1]);
71
+        } elseif (preg_match('/^I18N::translateContext\(\'(.+)\', *\'(.+)\'\)$/', $attrs['var'], $match)) {
72
+            $this->text .= I18N::translateContext($match[1], $match[2]);
73
+        } else {
74
+            $this->text .= $attrs['var'];
75
+        }
76
+    }
77 77
 
78
-	/**
79
-	 * Process <Title>
80
-	 */
81
-	protected function titleStartHandler() {
82
-		$this->text = '';
83
-	}
78
+    /**
79
+     * Process <Title>
80
+     */
81
+    protected function titleStartHandler() {
82
+        $this->text = '';
83
+    }
84 84
 
85
-	/**
86
-	 * Process </Title>
87
-	 */
88
-	protected function titleEndHandler() {
89
-		$this->data['title'] = $this->text;
90
-		$this->text          = '';
91
-	}
85
+    /**
86
+     * Process </Title>
87
+     */
88
+    protected function titleEndHandler() {
89
+        $this->data['title'] = $this->text;
90
+        $this->text          = '';
91
+    }
92 92
 
93
-	/**
94
-	 * Process </Description>
95
-	 */
96
-	protected function descriptionEndHandler() {
97
-		$this->data['description'] = $this->text;
98
-		$this->text                = '';
99
-	}
93
+    /**
94
+     * Process </Description>
95
+     */
96
+    protected function descriptionEndHandler() {
97
+        $this->data['description'] = $this->text;
98
+        $this->text                = '';
99
+    }
100 100
 
101
-	/**
102
-	 * Process <Input>
103
-	 *
104
-	 * @param string[] $attrs
105
-	 */
106
-	protected function inputStartHandler($attrs) {
107
-		$this->text  = '';
108
-		$this->input = array(
109
-			'name'    => isset($attrs['name']) ? $attrs['name'] : '',
110
-			'type'    => isset($attrs['type']) ? $attrs['type'] : '',
111
-			'lookup'  => isset($attrs['lookup']) ? $attrs['lookup'] : '',
112
-			'options' => isset($attrs['options']) ? $attrs['options'] : '',
113
-			'default' => '',
114
-			'value'   => '',
115
-		);
101
+    /**
102
+     * Process <Input>
103
+     *
104
+     * @param string[] $attrs
105
+     */
106
+    protected function inputStartHandler($attrs) {
107
+        $this->text  = '';
108
+        $this->input = array(
109
+            'name'    => isset($attrs['name']) ? $attrs['name'] : '',
110
+            'type'    => isset($attrs['type']) ? $attrs['type'] : '',
111
+            'lookup'  => isset($attrs['lookup']) ? $attrs['lookup'] : '',
112
+            'options' => isset($attrs['options']) ? $attrs['options'] : '',
113
+            'default' => '',
114
+            'value'   => '',
115
+        );
116 116
 
117
-		if (isset($attrs['default'])) {
118
-			if ($attrs['default'] === 'NOW') {
119
-				$this->input['default'] = date('d M Y');
120
-			} else {
121
-				$match = array();
122
-				if (preg_match('/NOW\s*([+\-])\s*(\d+)/', $attrs['default'], $match) > 0) {
123
-					$plus = 1;
124
-					if ($match[1] === '-') {
125
-						$plus = -1;
126
-					}
127
-					$this->input['default'] = date('d M Y', WT_TIMESTAMP + $plus * 60 * 60 * 24 * $match[2]);
128
-				} else {
129
-					$this->input['default'] = $attrs['default'];
130
-				}
131
-			}
132
-		}
133
-	}
117
+        if (isset($attrs['default'])) {
118
+            if ($attrs['default'] === 'NOW') {
119
+                $this->input['default'] = date('d M Y');
120
+            } else {
121
+                $match = array();
122
+                if (preg_match('/NOW\s*([+\-])\s*(\d+)/', $attrs['default'], $match) > 0) {
123
+                    $plus = 1;
124
+                    if ($match[1] === '-') {
125
+                        $plus = -1;
126
+                    }
127
+                    $this->input['default'] = date('d M Y', WT_TIMESTAMP + $plus * 60 * 60 * 24 * $match[2]);
128
+                } else {
129
+                    $this->input['default'] = $attrs['default'];
130
+                }
131
+            }
132
+        }
133
+    }
134 134
 
135
-	/**
136
-	 * Process </Input>
137
-	 */
138
-	protected function inputEndHandler() {
139
-		$this->input['value'] = $this->text;
140
-		if (!isset($this->data['inputs'])) {
141
-			$this->data['inputs'] = array();
142
-		}
143
-		$this->data['inputs'][] = $this->input;
144
-		$this->text             = '';
145
-	}
135
+    /**
136
+     * Process </Input>
137
+     */
138
+    protected function inputEndHandler() {
139
+        $this->input['value'] = $this->text;
140
+        if (!isset($this->data['inputs'])) {
141
+            $this->data['inputs'] = array();
142
+        }
143
+        $this->data['inputs'][] = $this->input;
144
+        $this->text             = '';
145
+    }
146 146
 }
Please login to merge, or discard this patch.
Braces   +18 added lines, -9 removed lines patch added patch discarded remove patch
@@ -21,7 +21,8 @@  discard block
 block discarded – undo
21 21
 /**
22 22
  * Class ReportParserSetup - parse a report.xml file and extract the setup options.
23 23
  */
24
-class ReportParserSetup extends ReportParserBase {
24
+class ReportParserSetup extends ReportParserBase
25
+{
25 26
 	/** @var array An array of report options/parameters */
26 27
 	private $data = array();
27 28
 
@@ -33,7 +34,8 @@  discard block
 block discarded – undo
33 34
 	 *
34 35
 	 * @return array
35 36
 	 */
36
-	public function reportProperties() {
37
+	public function reportProperties()
38
+	{
37 39
 		return $this->data;
38 40
 	}
39 41
 
@@ -42,7 +44,8 @@  discard block
 block discarded – undo
42 44
 	 *
43 45
 	 * @param string[] $attrs
44 46
 	 */
45
-	protected function reportStartHandler($attrs) {
47
+	protected function reportStartHandler($attrs)
48
+	{
46 49
 		$access = Auth::PRIV_PRIVATE;
47 50
 		if (isset($attrs['access'])) {
48 51
 			if (isset($$attrs["access"])) {
@@ -63,7 +66,8 @@  discard block
 block discarded – undo
63 66
 	 *
64 67
 	 * @param string[] $attrs
65 68
 	 */
66
-	protected function varStartHandler($attrs) {
69
+	protected function varStartHandler($attrs)
70
+	{
67 71
 		if (preg_match('/^I18N::number\((.+)\)$/', $attrs['var'], $match)) {
68 72
 			$this->text .= I18N::number($match[1]);
69 73
 		} elseif (preg_match('/^I18N::translate\(\'(.+)\'\)$/', $attrs['var'], $match)) {
@@ -78,14 +82,16 @@  discard block
 block discarded – undo
78 82
 	/**
79 83
 	 * Process <Title>
80 84
 	 */
81
-	protected function titleStartHandler() {
85
+	protected function titleStartHandler()
86
+	{
82 87
 		$this->text = '';
83 88
 	}
84 89
 
85 90
 	/**
86 91
 	 * Process </Title>
87 92
 	 */
88
-	protected function titleEndHandler() {
93
+	protected function titleEndHandler()
94
+	{
89 95
 		$this->data['title'] = $this->text;
90 96
 		$this->text          = '';
91 97
 	}
@@ -93,7 +99,8 @@  discard block
 block discarded – undo
93 99
 	/**
94 100
 	 * Process </Description>
95 101
 	 */
96
-	protected function descriptionEndHandler() {
102
+	protected function descriptionEndHandler()
103
+	{
97 104
 		$this->data['description'] = $this->text;
98 105
 		$this->text                = '';
99 106
 	}
@@ -103,7 +110,8 @@  discard block
 block discarded – undo
103 110
 	 *
104 111
 	 * @param string[] $attrs
105 112
 	 */
106
-	protected function inputStartHandler($attrs) {
113
+	protected function inputStartHandler($attrs)
114
+	{
107 115
 		$this->text  = '';
108 116
 		$this->input = array(
109 117
 			'name'    => isset($attrs['name']) ? $attrs['name'] : '',
@@ -135,7 +143,8 @@  discard block
 block discarded – undo
135 143
 	/**
136 144
 	 * Process </Input>
137 145
 	 */
138
-	protected function inputEndHandler() {
146
+	protected function inputEndHandler()
147
+	{
139 148
 		$this->input['value'] = $this->text;
140 149
 		if (!isset($this->data['inputs'])) {
141 150
 			$this->data['inputs'] = array();
Please login to merge, or discard this patch.
app/Report/ReportHtmlLine.php 2 patches
Indentation   +31 added lines, -31 removed lines patch added patch discarded remove patch
@@ -19,35 +19,35 @@
 block discarded – undo
19 19
  * Class ReportHtmlLine
20 20
  */
21 21
 class ReportHtmlLine extends ReportBaseLine {
22
-	/**
23
-	 * HTML line renderer
24
-	 *
25
-	 * @param ReportHtml $renderer
26
-	 */
27
-	public function render($renderer) {
28
-		if ($this->x1 == '.') {
29
-			$this->x1 = $renderer->getX();
30
-		}
31
-		if ($this->y1 == '.') {
32
-			$this->y1 = $renderer->getY();
33
-		}
34
-		if ($this->x2 == '.') {
35
-			$this->x2 = $renderer->getRemainingWidth();
36
-		}
37
-		if ($this->y2 == '.') {
38
-			$this->y2 = $renderer->getY();
39
-		}
40
-		// Vertical line
41
-		if ($this->x1 == $this->x2) {
42
-			echo "<div style=\"position:absolute;overflow:hidden;border-", $renderer->alignRTL, ":solid black 1pt;", $renderer->alignRTL, ":", $this->x1, "pt;top:", $this->y1 + 1, "pt;width:1pt;height:", $this->y2 - $this->y1, "pt;\"> </div>\n";
43
-		}
44
-		// Horizontal line
45
-		if ($this->y1 == $this->y2) {
46
-			echo "<div style=\"position:absolute;overflow:hidden;border-top:solid black 1pt;", $renderer->alignRTL, ":", $this->x1, "pt;top:", $this->y1 + 1, "pt;width:", $this->x2 - $this->x1, "pt;height:1pt;\"> </div>\n";
47
-		}
48
-		// Keep max Y updated
49
-		// One or the other will be higher... lasy mans way...
50
-		$renderer->addMaxY($this->y1);
51
-		$renderer->addMaxY($this->y2);
52
-	}
22
+    /**
23
+     * HTML line renderer
24
+     *
25
+     * @param ReportHtml $renderer
26
+     */
27
+    public function render($renderer) {
28
+        if ($this->x1 == '.') {
29
+            $this->x1 = $renderer->getX();
30
+        }
31
+        if ($this->y1 == '.') {
32
+            $this->y1 = $renderer->getY();
33
+        }
34
+        if ($this->x2 == '.') {
35
+            $this->x2 = $renderer->getRemainingWidth();
36
+        }
37
+        if ($this->y2 == '.') {
38
+            $this->y2 = $renderer->getY();
39
+        }
40
+        // Vertical line
41
+        if ($this->x1 == $this->x2) {
42
+            echo "<div style=\"position:absolute;overflow:hidden;border-", $renderer->alignRTL, ":solid black 1pt;", $renderer->alignRTL, ":", $this->x1, "pt;top:", $this->y1 + 1, "pt;width:1pt;height:", $this->y2 - $this->y1, "pt;\"> </div>\n";
43
+        }
44
+        // Horizontal line
45
+        if ($this->y1 == $this->y2) {
46
+            echo "<div style=\"position:absolute;overflow:hidden;border-top:solid black 1pt;", $renderer->alignRTL, ":", $this->x1, "pt;top:", $this->y1 + 1, "pt;width:", $this->x2 - $this->x1, "pt;height:1pt;\"> </div>\n";
47
+        }
48
+        // Keep max Y updated
49
+        // One or the other will be higher... lasy mans way...
50
+        $renderer->addMaxY($this->y1);
51
+        $renderer->addMaxY($this->y2);
52
+    }
53 53
 }
Please login to merge, or discard this patch.
Braces   +4 added lines, -2 removed lines patch added patch discarded remove patch
@@ -18,13 +18,15 @@
 block discarded – undo
18 18
 /**
19 19
  * Class ReportHtmlLine
20 20
  */
21
-class ReportHtmlLine extends ReportBaseLine {
21
+class ReportHtmlLine extends ReportBaseLine
22
+{
22 23
 	/**
23 24
 	 * HTML line renderer
24 25
 	 *
25 26
 	 * @param ReportHtml $renderer
26 27
 	 */
27
-	public function render($renderer) {
28
+	public function render($renderer)
29
+	{
28 30
 		if ($this->x1 == '.') {
29 31
 			$this->x1 = $renderer->getX();
30 32
 		}
Please login to merge, or discard this patch.
app/Report/ReportPdfHtml.php 2 patches
Indentation   +34 added lines, -34 removed lines patch added patch discarded remove patch
@@ -19,39 +19,39 @@
 block discarded – undo
19 19
  * Class ReportPdfHtml
20 20
  */
21 21
 class ReportPdfHtml extends ReportBaseHtml {
22
-	/**
23
-	 * Render the output.
24
-	 *
25
-	 * @param      $renderer
26
-	 * @param bool $sub
27
-	 *
28
-	 * @return int|string
29
-	 */
30
-	public function render($renderer, $sub = false) {
31
-		if (!empty($this->attrs['style'])) {
32
-			$renderer->setCurrentStyle($this->attrs['style']);
33
-		}
34
-		if (!empty($this->attrs['width'])) {
35
-			$this->attrs['width'] *= 3.9;
36
-		}
22
+    /**
23
+     * Render the output.
24
+     *
25
+     * @param      $renderer
26
+     * @param bool $sub
27
+     *
28
+     * @return int|string
29
+     */
30
+    public function render($renderer, $sub = false) {
31
+        if (!empty($this->attrs['style'])) {
32
+            $renderer->setCurrentStyle($this->attrs['style']);
33
+        }
34
+        if (!empty($this->attrs['width'])) {
35
+            $this->attrs['width'] *= 3.9;
36
+        }
37 37
 
38
-		$this->text = $this->getStart() . $this->text;
39
-		foreach ($this->elements as $element) {
40
-			if (is_string($element) && $element == "footnotetexts") {
41
-				$renderer->Footnotes();
42
-			} elseif (is_string($element) && $element == "addpage") {
43
-				$renderer->newPage();
44
-			} elseif ($element instanceof ReportBaseHtml) {
45
-				$element->render($renderer, true);
46
-			} else {
47
-				$element->render($renderer);
48
-			}
49
-		}
50
-		$this->text .= $this->getEnd();
51
-		if ($sub) {
52
-			return $this->text;
53
-		}
54
-		$renderer->writeHTML($this->text); //prints 2 empty cells in the Expanded Relatives report
55
-		return 0;
56
-	}
38
+        $this->text = $this->getStart() . $this->text;
39
+        foreach ($this->elements as $element) {
40
+            if (is_string($element) && $element == "footnotetexts") {
41
+                $renderer->Footnotes();
42
+            } elseif (is_string($element) && $element == "addpage") {
43
+                $renderer->newPage();
44
+            } elseif ($element instanceof ReportBaseHtml) {
45
+                $element->render($renderer, true);
46
+            } else {
47
+                $element->render($renderer);
48
+            }
49
+        }
50
+        $this->text .= $this->getEnd();
51
+        if ($sub) {
52
+            return $this->text;
53
+        }
54
+        $renderer->writeHTML($this->text); //prints 2 empty cells in the Expanded Relatives report
55
+        return 0;
56
+    }
57 57
 }
Please login to merge, or discard this patch.
Braces   +4 added lines, -2 removed lines patch added patch discarded remove patch
@@ -18,7 +18,8 @@  discard block
 block discarded – undo
18 18
 /**
19 19
  * Class ReportPdfHtml
20 20
  */
21
-class ReportPdfHtml extends ReportBaseHtml {
21
+class ReportPdfHtml extends ReportBaseHtml
22
+{
22 23
 	/**
23 24
 	 * Render the output.
24 25
 	 *
@@ -27,7 +28,8 @@  discard block
 block discarded – undo
27 28
 	 *
28 29
 	 * @return int|string
29 30
 	 */
30
-	public function render($renderer, $sub = false) {
31
+	public function render($renderer, $sub = false)
32
+	{
31 33
 		if (!empty($this->attrs['style'])) {
32 34
 			$renderer->setCurrentStyle($this->attrs['style']);
33 35
 		}
Please login to merge, or discard this patch.
app/Report/ReportBaseLine.php 2 patches
Indentation   +59 added lines, -59 removed lines patch added patch discarded remove patch
@@ -19,67 +19,67 @@
 block discarded – undo
19 19
  * Class ReportBaseLine
20 20
  */
21 21
 class ReportBaseLine extends ReportBaseElement {
22
-	/**
23
-	 * Start horizontal position, current position (default)
24
-	 *
25
-	 * @var mixed
26
-	 */
27
-	public $x1 = ".";
28
-	/**
29
-	 * Start vertical position, current position (default)
30
-	 *
31
-	 * @var mixed
32
-	 */
33
-	public $y1 = ".";
34
-	/**
35
-	 * End horizontal position, maximum width (default)
36
-	 *
37
-	 * @var mixed
38
-	 */
39
-	public $x2 = ".";
40
-	/**
41
-	 * End vertical position
42
-	 *
43
-	 * @var mixed
44
-	 */
45
-	public $y2 = ".";
22
+    /**
23
+     * Start horizontal position, current position (default)
24
+     *
25
+     * @var mixed
26
+     */
27
+    public $x1 = ".";
28
+    /**
29
+     * Start vertical position, current position (default)
30
+     *
31
+     * @var mixed
32
+     */
33
+    public $y1 = ".";
34
+    /**
35
+     * End horizontal position, maximum width (default)
36
+     *
37
+     * @var mixed
38
+     */
39
+    public $x2 = ".";
40
+    /**
41
+     * End vertical position
42
+     *
43
+     * @var mixed
44
+     */
45
+    public $y2 = ".";
46 46
 
47
-	/**
48
-	 * Create a line class - Base
49
-	 *
50
-	 * @param mixed $x1
51
-	 * @param mixed $y1
52
-	 * @param mixed $x2
53
-	 * @param mixed $y2
54
-	 */
55
-	public function __construct($x1, $y1, $x2, $y2) {
56
-		$this->x1 = $x1;
57
-		$this->y1 = $y1;
58
-		$this->x2 = $x2;
59
-		$this->y2 = $y2;
47
+    /**
48
+     * Create a line class - Base
49
+     *
50
+     * @param mixed $x1
51
+     * @param mixed $y1
52
+     * @param mixed $x2
53
+     * @param mixed $y2
54
+     */
55
+    public function __construct($x1, $y1, $x2, $y2) {
56
+        $this->x1 = $x1;
57
+        $this->y1 = $y1;
58
+        $this->x2 = $x2;
59
+        $this->y2 = $y2;
60 60
 
61
-		return 0;
62
-	}
61
+        return 0;
62
+    }
63 63
 
64
-	/**
65
-	 * Get the height of the line.
66
-	 *
67
-	 * @param $renderer
68
-	 *
69
-	 * @return number
70
-	 */
71
-	public function getHeight($renderer) {
72
-		return abs($this->y2 - $this->y1);
73
-	}
64
+    /**
65
+     * Get the height of the line.
66
+     *
67
+     * @param $renderer
68
+     *
69
+     * @return number
70
+     */
71
+    public function getHeight($renderer) {
72
+        return abs($this->y2 - $this->y1);
73
+    }
74 74
 
75
-	/**
76
-	 * Get the width of the line.
77
-	 *
78
-	 * @param $renderer
79
-	 *
80
-	 * @return number
81
-	 */
82
-	public function getWidth($renderer) {
83
-		return abs($this->x2 - $this->x1);
84
-	}
75
+    /**
76
+     * Get the width of the line.
77
+     *
78
+     * @param $renderer
79
+     *
80
+     * @return number
81
+     */
82
+    public function getWidth($renderer) {
83
+        return abs($this->x2 - $this->x1);
84
+    }
85 85
 }
Please login to merge, or discard this patch.
Braces   +8 added lines, -4 removed lines patch added patch discarded remove patch
@@ -18,7 +18,8 @@  discard block
 block discarded – undo
18 18
 /**
19 19
  * Class ReportBaseLine
20 20
  */
21
-class ReportBaseLine extends ReportBaseElement {
21
+class ReportBaseLine extends ReportBaseElement
22
+{
22 23
 	/**
23 24
 	 * Start horizontal position, current position (default)
24 25
 	 *
@@ -52,7 +53,8 @@  discard block
 block discarded – undo
52 53
 	 * @param mixed $x2
53 54
 	 * @param mixed $y2
54 55
 	 */
55
-	public function __construct($x1, $y1, $x2, $y2) {
56
+	public function __construct($x1, $y1, $x2, $y2)
57
+	{
56 58
 		$this->x1 = $x1;
57 59
 		$this->y1 = $y1;
58 60
 		$this->x2 = $x2;
@@ -68,7 +70,8 @@  discard block
 block discarded – undo
68 70
 	 *
69 71
 	 * @return number
70 72
 	 */
71
-	public function getHeight($renderer) {
73
+	public function getHeight($renderer)
74
+	{
72 75
 		return abs($this->y2 - $this->y1);
73 76
 	}
74 77
 
@@ -79,7 +82,8 @@  discard block
 block discarded – undo
79 82
 	 *
80 83
 	 * @return number
81 84
 	 */
82
-	public function getWidth($renderer) {
85
+	public function getWidth($renderer)
86
+	{
83 87
 		return abs($this->x2 - $this->x1);
84 88
 	}
85 89
 }
Please login to merge, or discard this patch.
app/Report/ReportBaseText.php 2 patches
Indentation   +62 added lines, -62 removed lines patch added patch discarded remove patch
@@ -19,71 +19,71 @@
 block discarded – undo
19 19
  * Class ReportBaseText
20 20
  */
21 21
 class ReportBaseText extends ReportBaseElement {
22
-	/**
23
-	 * Text color in HTML code
24
-	 *
25
-	 * @var string
26
-	 */
27
-	public $color;
28
-	/**
29
-	 * Style name
30
-	 *
31
-	 * @var string
32
-	 */
33
-	public $styleName;
34
-	/**
35
-	 * Remaining width of a cel
36
-	 *
37
-	 * @var int User unit (points)
38
-	 */
39
-	public $wrapWidthRemaining;
40
-	/**
41
-	 * Original width of a cell
42
-	 *
43
-	 * @var int User unit (points)
44
-	 */
45
-	public $wrapWidthCell;
22
+    /**
23
+     * Text color in HTML code
24
+     *
25
+     * @var string
26
+     */
27
+    public $color;
28
+    /**
29
+     * Style name
30
+     *
31
+     * @var string
32
+     */
33
+    public $styleName;
34
+    /**
35
+     * Remaining width of a cel
36
+     *
37
+     * @var int User unit (points)
38
+     */
39
+    public $wrapWidthRemaining;
40
+    /**
41
+     * Original width of a cell
42
+     *
43
+     * @var int User unit (points)
44
+     */
45
+    public $wrapWidthCell;
46 46
 
47
-	/**
48
-	 * Create a Text class - Base
49
-	 *
50
-	 * @param string $style The name of the text style
51
-	 * @param string $color HTML color code
52
-	 */
53
-	public function __construct($style, $color) {
54
-		$this->text               = '';
55
-		$this->color              = $color;
56
-		$this->wrapWidthRemaining = 0;
57
-		$this->styleName          = $style;
47
+    /**
48
+     * Create a Text class - Base
49
+     *
50
+     * @param string $style The name of the text style
51
+     * @param string $color HTML color code
52
+     */
53
+    public function __construct($style, $color) {
54
+        $this->text               = '';
55
+        $this->color              = $color;
56
+        $this->wrapWidthRemaining = 0;
57
+        $this->styleName          = $style;
58 58
 
59
-		return 0;
60
-	}
59
+        return 0;
60
+    }
61 61
 
62
-	/**
63
-	 * Set the width for word-wrapping.
64
-	 *
65
-	 * @param $wrapwidth
66
-	 * @param $cellwidth
67
-	 *
68
-	 * @return mixed
69
-	 */
70
-	public function setWrapWidth($wrapwidth, $cellwidth) {
71
-		$this->wrapWidthCell = $cellwidth;
72
-		if (strpos($this->text, "\n") !== false) {
73
-			$this->wrapWidthRemaining = $cellwidth;
74
-		} else {
75
-			$this->wrapWidthRemaining = $wrapwidth;
76
-		}
62
+    /**
63
+     * Set the width for word-wrapping.
64
+     *
65
+     * @param $wrapwidth
66
+     * @param $cellwidth
67
+     *
68
+     * @return mixed
69
+     */
70
+    public function setWrapWidth($wrapwidth, $cellwidth) {
71
+        $this->wrapWidthCell = $cellwidth;
72
+        if (strpos($this->text, "\n") !== false) {
73
+            $this->wrapWidthRemaining = $cellwidth;
74
+        } else {
75
+            $this->wrapWidthRemaining = $wrapwidth;
76
+        }
77 77
 
78
-		return $this->wrapWidthRemaining;
79
-	}
78
+        return $this->wrapWidthRemaining;
79
+    }
80 80
 
81
-	/**
82
-	 * Get the style name.
83
-	 *
84
-	 * @return string
85
-	 */
86
-	public function getStyleName() {
87
-		return $this->styleName;
88
-	}
81
+    /**
82
+     * Get the style name.
83
+     *
84
+     * @return string
85
+     */
86
+    public function getStyleName() {
87
+        return $this->styleName;
88
+    }
89 89
 }
Please login to merge, or discard this patch.
Braces   +8 added lines, -4 removed lines patch added patch discarded remove patch
@@ -18,7 +18,8 @@  discard block
 block discarded – undo
18 18
 /**
19 19
  * Class ReportBaseText
20 20
  */
21
-class ReportBaseText extends ReportBaseElement {
21
+class ReportBaseText extends ReportBaseElement
22
+{
22 23
 	/**
23 24
 	 * Text color in HTML code
24 25
 	 *
@@ -50,7 +51,8 @@  discard block
 block discarded – undo
50 51
 	 * @param string $style The name of the text style
51 52
 	 * @param string $color HTML color code
52 53
 	 */
53
-	public function __construct($style, $color) {
54
+	public function __construct($style, $color)
55
+	{
54 56
 		$this->text               = '';
55 57
 		$this->color              = $color;
56 58
 		$this->wrapWidthRemaining = 0;
@@ -67,7 +69,8 @@  discard block
 block discarded – undo
67 69
 	 *
68 70
 	 * @return mixed
69 71
 	 */
70
-	public function setWrapWidth($wrapwidth, $cellwidth) {
72
+	public function setWrapWidth($wrapwidth, $cellwidth)
73
+	{
71 74
 		$this->wrapWidthCell = $cellwidth;
72 75
 		if (strpos($this->text, "\n") !== false) {
73 76
 			$this->wrapWidthRemaining = $cellwidth;
@@ -83,7 +86,8 @@  discard block
 block discarded – undo
83 86
 	 *
84 87
 	 * @return string
85 88
 	 */
86
-	public function getStyleName() {
89
+	public function getStyleName()
90
+	{
87 91
 		return $this->styleName;
88 92
 	}
89 93
 }
Please login to merge, or discard this patch.