';
var datemilli = tdate.getTime() + daymilli;
tdate = new Date(datemilli);
}
content += '
';
}
content += '
';
content += '
';
content += '
';
return content;
}
function cal_setDateField(dateFieldId, year, month, day)
{
var dateField = document.getElementById(dateFieldId);
if (!dateField) {
return false;
}
if (day < 10) {
day = '0' + day;
}
dateField.value = day + ' ' + monthShort[month + 1] + ' ' + year;
return false;
}
function cal_updateCalendar(dateFieldId, dateDivId)
{
var dateSel = document.getElementById(dateFieldId + '_daySelect');
if (!dateSel) {
return false;
}
var monthSel = document.getElementById(dateFieldId + '_monSelect');
if (!monthSel) {
return false;
}
var yearInput = document.getElementById(dateFieldId + '_yearInput');
if (!yearInput) {
return false;
}
var month = parseInt(monthSel.options[monthSel.selectedIndex].value, 10);
month = month - 1;
var date = new Date(yearInput.value, month, dateSel.options[dateSel.selectedIndex].value);
cal_setDateField(dateFieldId, date.getFullYear(), date.getMonth(), date.getDate());
var dateDiv = document.getElementById(dateDivId);
if (!dateDiv) {
alert('no dateDiv ' + dateDivId);
return false;
}
dateDiv.innerHTML = cal_generateSelectorContent(dateFieldId, dateDivId, date);
return false;
}
function cal_dateClicked(dateFieldId, dateDivId, year, month, day)
{
cal_setDateField(dateFieldId, year, month, day);
calendarWidget(dateDivId, dateFieldId);
return false;
}
function openerpasteid(id)
{
if (window.opener.paste_id) {
window.opener.paste_id(id);
}
window.close();
}
function paste_id(value)
{
pastefield.value = value;
}
function pastename(name)
{
if (nameElement) {
nameElement.innerHTML = name;
}
if (remElement) {
remElement.style.display = 'block';
}
}
function paste_char(value)
{
if (document.selection) {
// IE
pastefield.focus();
document.selection.createRange().text = value;
} else if (pastefield.selectionStart || pastefield.selectionStart === 0) {
// Mozilla/Chrome/Safari
pastefield.value =
pastefield.value.substring(0, pastefield.selectionStart) +
value +
pastefield.value.substring(pastefield.selectionEnd, pastefield.value.length);
pastefield.selectionStart = pastefield.selectionEnd = pastefield.selectionStart + value.length;
} else {
// Fallback? - just append
pastefield.value += value;
}
if (pastefield.id === 'NPFX' || pastefield.id === 'GIVN' || pastefield.id === 'SPFX' || pastefield.id === 'SURN' || pastefield.id === 'NSFX') {
updatewholename();
}
}
/**
* Persistant checkbox options to hide/show extra data.
* @param element_id
*/
function persistent_toggle(element_id)
{
let element = document.getElementById(element_id);
let key = 'state-of-' + element_id;
let state = localStorage.getItem(key);
// Previously selected?
if (state === 'true') {
$(element).click();
}
// Remember state for the next page load.
$(element).on('change', function() { localStorage.setItem(key, element.checked); });
}
function valid_lati_long(field, pos, neg)
{
// valid LATI or LONG according to Gedcom standard
// pos (+) : N or E
// neg (-) : S or W
var txt = field.value.toUpperCase();
txt = txt.replace(/(^\s*)|(\s*$)/g, ''); // trim
txt = txt.replace(/ /g, ':'); // N12 34 ==> N12.34
txt = txt.replace(/\+/g, ''); // +17.1234 ==> 17.1234
txt = txt.replace(/-/g, neg); // -0.5698 ==> W0.5698
txt = txt.replace(/,/g, '.'); // 0,5698 ==> 0.5698
// 0°34'11 ==> 0:34:11
txt = txt.replace(/\u00b0/g, ':'); // °
txt = txt.replace(/\u0027/g, ':'); // '
// 0:34:11.2W ==> W0.5698
txt = txt.replace(/^([0-9]+):([0-9]+):([0-9.]+)(.*)/g, function ($0, $1, $2, $3, $4) {
var n = parseFloat($1);
n += ($2 / 60);
n += ($3 / 3600);
n = Math.round(n * 1E4) / 1E4;
return $4 + n;
});
// 0:34W ==> W0.5667
txt = txt.replace(/^([0-9]+):([0-9]+)(.*)/g, function ($0, $1, $2, $3) {
var n = parseFloat($1);
n += ($2 / 60);
n = Math.round(n * 1E4) / 1E4;
return $3 + n;
});
// 0.5698W ==> W0.5698
txt = txt.replace(/(.*)([N|S|E|W]+)$/g, '$2$1');
// 17.1234 ==> N17.1234
if (txt && txt.charAt(0) !== neg && txt.charAt(0) !== pos) {
txt = pos + txt;
}
field.value = txt;
}
// This is the default way for webtrees to show image galleries.
// Custom themes may use a different viewer.
function activate_colorbox(config)
{
$.extend($.colorbox.settings, {
// Don't scroll window with document
fixed: true,
current: '',
previous: '\uf048',
next: '\uf051',
slideshowStart: '\uf04b',
slideshowStop: '\uf04c',
close: '\uf00d'
});
if (config) {
$.extend($.colorbox.settings, config);
}
// Trigger an event when we click on an (any) image
$('body').on('click', 'a.gallery', function () {
// Enable colorbox for images
$('a[type^=image].gallery').colorbox({
photo: true,
maxWidth: '95%',
maxHeight: '95%',
rel: 'gallery', // Turn all images on the page into a slideshow
slideshow: true,
slideshowAuto: false,
// Add wheelzoom to the displayed image
onComplete: function () {
// Disable click on image triggering next image
// https://github.com/jackmoore/colorbox/issues/668
$('.cboxPhoto').unbind('click');
wheelzoom(document.querySelectorAll('.cboxPhoto'));
}
});
// Enable colorbox for audio using , where supported
// $('html.video a[type^=video].gallery').colorbox({
// rel: 'nofollow' // Slideshows are just for images
// });
// Enable colorbox for video using , where supported
// $('html.audio a[type^=audio].gallery').colorbox({
// rel: 'nofollow', // Slideshows are just for images
// });
// Allow all other media types remain as download links
});
}
// Initialize autocomplete elements.
function autocomplete(selector)
{
// Use typeahead/bloodhound for autocomplete
$(selector).each(function () {
let that = this;
$(this).typeahead(null, {
display: 'value',
source: new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: this.dataset.autocompleteUrl,
replace: function(url, uriEncodedQuery) {
if (that.dataset.autocompleteExtra) {
let extra = $(document.querySelector(that.dataset.autocompleteExtra)).val();
return url.replace("QUERY",uriEncodedQuery) + '&extra=' + encodeURIComponent(extra)
}
return url.replace("QUERY",uriEncodedQuery);
},
wildcard: 'QUERY',
}
})
});
});
}
/**
* Insert text at the current cursor position in an input field.
*
* @param e The input element.
* @param t The text to insert.
*/
function insertTextAtCursor(e, t)
{
var scrollTop = e.scrollTop;
var selectionStart = e.selectionStart;
var prefix = e.value.substring(0, selectionStart);
var suffix = e.value.substring(e.selectionEnd, e.value.length);
e.value = prefix + t + suffix;
e.selectionStart = selectionStart + t.length;
e.selectionEnd = e.selectionStart;
e.focus();
e.scrollTop = scrollTop;
}
/**
* Draws a google pie chart.
*
* @param {String} elementId The element id of the HTML element the chart is rendered too
* @param {Array} data The chart data array
* @param {Array} colors The chart color array
* @param {String} title The chart title
* @param {String} labeledValueText The type of how to display the slice text
*/
function drawPieChart(elementId, data, colors, title, labeledValueText)
{
var data = google.visualization.arrayToDataTable(data);
var options = {
title: title,
height: '100%',
width: '100%',
pieStartAngle: 0,
pieSliceText: 'none',
pieSliceTextStyle: {
color: '#777'
},
pieHole: 0.4, // Donut
//is3D: true, // 3D (not together with pieHole)
legend: {
alignment: 'center',
// Flickers on mouseover :(
labeledValueText: labeledValueText || 'value',
position: 'labeled'
},
chartArea: {
left: 0,
top: '5%',
height: '90%',
width: '100%'
},
tooltip: {
trigger: 'none',
text: 'both'
},
backgroundColor: 'transparent',
colors: colors
};
var chart = new google.visualization.PieChart(document.getElementById(elementId));
chart.draw(data, options);
}
/**
* Draws a google column chart.
*
* @param {String} elementId The element id of the HTML element the chart is rendered too
* @param {Array} data The chart data array
* @param {Object} options The chart specific options to overwrite the default ones
*/
function drawColumnChart(elementId, data, options)
{
var defaults = {
title: '',
subtitle: '',
titleTextStyle: {
color: '#757575',
fontName: 'Roboto',
fontSize: '16px',
bold: false,
italic: false
},
height: '100%',
width: '100%',
vAxis: {
title: ''
},
hAxis: {
title: ''
},
legend: {
position: 'none'
},
backgroundColor: 'transparent'
};
options = Object.assign(defaults, options);
var chart = new google.visualization.ColumnChart(document.getElementById(elementId));
var data = google.visualization.arrayToDataTable(data);
chart.draw(data, options);
}
/**
* Draws a google combo chart.
*
* @param {String} elementId The element id of the HTML element the chart is rendered too
* @param {Array} data The chart data array
* @param {Object} options The chart specific options to overwrite the default ones
*/
function drawComboChart(elementId, data, options)
{
var defaults = {
title: '',
subtitle: '',
titleTextStyle: {
color: '#757575',
fontName: 'Roboto',
fontSize: '16px',
bold: false,
italic: false
},
height: '100%',
width: '100%',
vAxis: {
title: ''
},
hAxis: {
title: ''
},
legend: {
position: 'none'
},
seriesType: 'bars',
series: {
2: {
type: 'line'
}
},
colors: [],
backgroundColor: 'transparent'
};
options = Object.assign(defaults, options);
var chart = new google.visualization.ComboChart(document.getElementById(elementId));
var data = google.visualization.arrayToDataTable(data);
chart.draw(data, options);
}
/**
* Draws a google geo chart.
*
* @param {String} elementId The element id of the HTML element the chart is rendered too
* @param {Array} data The chart data array
* @param {Object} options The chart specific options to overwrite the default ones
*/
function drawGeoChart(elementId, data, options)
{
var defaults = {
title: '',
subtitle: '',
height: '100%',
width: '100%'
};
options = Object.assign(defaults, options);
var chart = new google.visualization.GeoChart(document.getElementById(elementId));
var data = google.visualization.arrayToDataTable(data);
chart.draw(data, options);
}
// Send the CSRF token on all AJAX requests
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name=csrf]').attr('content')
}
});
// Initialisation
$(function () {
// Page elements that load automaticaly via AJAX.
// This prevents bad robots from crawling resource-intensive pages.
$("[data-ajax-url]").each(function () {
$(this).load($(this).data('ajaxUrl'));
});
// Select2 - format entries in the select list
function templateOptionForSelect2(data)
{
if (data.loading) {
// If we're waiting for the server, this will be a "waiting..." message
return data.text;
} else {
// The response from the server is already in HTML, so no need to format it here.
return data.text;
}
}
// Autocomplete
autocomplete('input[data-autocomplete-url]');
// Select2 - activate autocomplete fields
const lang = document.documentElement.lang;
const select2_languages = {
'zh-Hans': 'zh-CN',
'zh-Hant': 'zh-TW',
};
$("select.select2").select2({
language: select2_languages[lang] || lang,
// "auto" breaks content that is initially hidden.
// "100%" breaks content that isn't.
// "90%"
width: "90%",
// Do not escape.
escapeMarkup: function (x) {
return x;
},
// Same formatting for both selections and rsult
//templateResult: templateOptionForSelect2,
//templateSelection: templateOptionForSelect2
})
.on("select2:unselect", function (evt) {
// If we clear the select (using the "X" button), we need an empty
// value (rather than no value at all) for inputs with name="array[]"
$(evt.delegateTarget).html("");
});
// Datatables - locale aware sorting
$.fn.dataTableExt.oSort['text-asc'] = function (x, y) {
return x.localeCompare(y, document.documentElement.lang, {'sensitivity': 'base'});
};
$.fn.dataTableExt.oSort['text-desc'] = function (x, y) {
return y.localeCompare(x, document.documentElement.lang, {'sensitivity': 'base'});
};
// DataTables - start hidden to prevent FOUC.
$('table.datatables').each(function () {
$(this).DataTable(); $(this).removeClass('d-none'); });
// Activate the on-screen keyboard
var osk_focus_element;
$('.wt-osk-trigger').click(function () {
// When a user clicks the icon, set focus to the corresponding input
osk_focus_element = document.getElementById($(this).data('id'));
osk_focus_element.focus();
$('.wt-osk').show();
});
$('.wt-osk-script-button').change(function () {
$('.wt-osk-script').prop('hidden', true);
$('.wt-osk-script-' + $(this).data('script')).prop('hidden', false);
});
$('.wt-osk-shift-button').click(function () {
document.querySelector('.wt-osk-keys').classList.toggle('shifted');
});
$('.wt-osk-keys').on('click', '.wt-osk-key', function () {
var key = $(this).contents().get(0).nodeValue;
var shift_state = $('.wt-osk-shift-button').hasClass('active');
var shift_key = $('sup', this)[0];
if (shift_state && shift_key !== undefined) {
key = shift_key.innerText;
}
if (osk_focus_element !== null) {
var cursorPos = osk_focus_element.selectionStart;
var v = osk_focus_element.value;
var textBefore = v.substring(0, cursorPos);
var textAfter = v.substring(cursorPos, v.length);
osk_focus_element.value = textBefore + key + textAfter;
if ($('.wt-osk-pin-button').hasClass('active') === false) {
$('.wt-osk').hide();
}
}
});
$('.wt-osk-close').on('click', function () {
$('.wt-osk').hide();
});
});
// Convert data-confirm, data-post-url, and data-reload-url attributes into useful behaviour.
document.addEventListener("click", (event) => {
const target = event.target.closest("a");
if (target === null) {
return;
}
if ("confirm" in target.dataset && !confirm(target.dataset.confirm)) {
event.preventDefault();
return;
}
if ("postUrl" in target.dataset) {
let request = new XMLHttpRequest();
let token = document.querySelector("meta[name=csrf]").content;
request.open("POST", target.dataset.postUrl, true);
request.setRequestHeader("X-CSRF-TOKEN", token);
request.onreadystatechange = () => {
if (request.readyState === request.DONE) {
if (request.status >= 200 && request.status <= 299 && "reloadUrl" in target.dataset) {
document.location = target.dataset.reloadUrl;
} else {
document.location.reload();
}
}
};
request.send();
event.preventDefault();
}
});