171239cb6SGreg Roach/** 271239cb6SGreg Roach * webtrees: online genealogy 3063107dbSGreg Roach * Copyright (C) 2020 webtrees development team 471239cb6SGreg Roach * This program is free software: you can redistribute it and/or modify 571239cb6SGreg Roach * it under the terms of the GNU General Public License as published by 671239cb6SGreg Roach * the Free Software Foundation, either version 3 of the License, or 771239cb6SGreg Roach * (at your option) any later version. 871239cb6SGreg Roach * This program is distributed in the hope that it will be useful, 971239cb6SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 1071239cb6SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1171239cb6SGreg Roach * GNU General Public License for more details. 1271239cb6SGreg Roach * You should have received a copy of the GNU General Public License 1371239cb6SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>. 1471239cb6SGreg Roach */ 1571239cb6SGreg Roach 16efd89170SGreg Roach'use strict'; 1771239cb6SGreg Roach 180d2905f7SGreg Roach(function (webtrees) { 1959e18f0cSGreg Roach const lang = document.documentElement.lang; 2059e18f0cSGreg Roach 210d2905f7SGreg Roach // Identify the script used by some text. 220d2905f7SGreg Roach const scriptRegexes = { 230d2905f7SGreg Roach Han: /[\u3400-\u9FCC]/, 240d2905f7SGreg Roach Grek: /[\u0370-\u03FF]/, 250d2905f7SGreg Roach Cyrl: /[\u0400-\u04FF]/, 260d2905f7SGreg Roach Hebr: /[\u0590-\u05FF]/, 27efd89170SGreg Roach Arab: /[\u0600-\u06FF]/ 280d2905f7SGreg Roach }; 290d2905f7SGreg Roach 3059e18f0cSGreg Roach /** 3159e18f0cSGreg Roach * Tidy the whitespace in a string. 3206fe1eb5SGreg Roach * @param {string} str 3306fe1eb5SGreg Roach * @returns {string} 3459e18f0cSGreg Roach */ 3559e18f0cSGreg Roach function trim (str) { 36efd89170SGreg Roach return str.replace(/\s+/g, ' ').trim(); 3759e18f0cSGreg Roach } 3859e18f0cSGreg Roach 3959e18f0cSGreg Roach /** 4059e18f0cSGreg Roach * Look for non-latin characters in a string. 4106fe1eb5SGreg Roach * @param {string} str 4206fe1eb5SGreg Roach * @returns {string} 4359e18f0cSGreg Roach */ 440d2905f7SGreg Roach webtrees.detectScript = function (str) { 450d2905f7SGreg Roach for (const script in scriptRegexes) { 460d2905f7SGreg Roach if (str.match(scriptRegexes[script])) { 470d2905f7SGreg Roach return script; 480d2905f7SGreg Roach } 4959e18f0cSGreg Roach } 5059e18f0cSGreg Roach 51efd89170SGreg Roach return 'Latn'; 520d2905f7SGreg Roach }; 5359e18f0cSGreg Roach 5459e18f0cSGreg Roach /** 5559e18f0cSGreg Roach * In some languages, the SURN uses a male/default form, but NAME uses a gender-inflected form. 5606fe1eb5SGreg Roach * @param {string} surname 5706fe1eb5SGreg Roach * @param {string} sex 5806fe1eb5SGreg Roach * @returns {string} 5959e18f0cSGreg Roach */ 6059e18f0cSGreg Roach function inflectSurname (surname, sex) { 61efd89170SGreg Roach if (lang === 'pl' && sex === 'F') { 6259e18f0cSGreg Roach return surname 63efd89170SGreg Roach .replace(/ski$/, 'ska') 64efd89170SGreg Roach .replace(/cki$/, 'cka') 65efd89170SGreg Roach .replace(/dzki$/, 'dzka') 66efd89170SGreg Roach .replace(/żki$/, 'żka'); 6759e18f0cSGreg Roach } 6859e18f0cSGreg Roach 6959e18f0cSGreg Roach return surname; 7059e18f0cSGreg Roach } 7159e18f0cSGreg Roach 7259e18f0cSGreg Roach /** 7359e18f0cSGreg Roach * Build a NAME from a NPFX, GIVN, SPFX, SURN and NSFX parts. 7459e18f0cSGreg Roach * Assumes the language of the document is the same as the language of the name. 7506fe1eb5SGreg Roach * @param {string} npfx 7606fe1eb5SGreg Roach * @param {string} givn 7706fe1eb5SGreg Roach * @param {string} spfx 7806fe1eb5SGreg Roach * @param {string} surn 7906fe1eb5SGreg Roach * @param {string} nsfx 8006fe1eb5SGreg Roach * @param {string} sex 8106fe1eb5SGreg Roach * @returns {string} 8259e18f0cSGreg Roach */ 830d2905f7SGreg Roach webtrees.buildNameFromParts = function (npfx, givn, spfx, surn, nsfx, sex) { 84efd89170SGreg Roach const usesCJK = webtrees.detectScript(npfx + givn + spfx + givn + surn + nsfx) === 'Han'; 85efd89170SGreg Roach const separator = usesCJK ? '' : ' '; 8659e18f0cSGreg Roach const surnameFirst = usesCJK || ['hu', 'jp', 'ko', 'vi', 'zh-Hans', 'zh-Hant'].indexOf(lang) !== -1; 8759e18f0cSGreg Roach const patronym = ['is'].indexOf(lang) !== -1; 88efd89170SGreg Roach const slash = patronym ? '' : '/'; 8959e18f0cSGreg Roach 9059e18f0cSGreg Roach // GIVN and SURN may be a comma-separated lists. 9159e18f0cSGreg Roach npfx = trim(npfx); 92063107dbSGreg Roach givn = trim(givn.replace(/,/g, separator)); 9359e18f0cSGreg Roach spfx = trim(spfx); 94063107dbSGreg Roach surn = inflectSurname(trim(surn.replace(/,/g, separator)), sex); 9559e18f0cSGreg Roach nsfx = trim(nsfx); 9659e18f0cSGreg Roach 9759e18f0cSGreg Roach const surname = trim(spfx + separator + surn); 9859e18f0cSGreg Roach 9959e18f0cSGreg Roach const name = surnameFirst ? slash + surname + slash + separator + givn : givn + separator + slash + surname + slash; 10059e18f0cSGreg Roach 10159e18f0cSGreg Roach return trim(npfx + separator + name + separator + nsfx); 1020d2905f7SGreg Roach }; 1030d2905f7SGreg Roach 1040d2905f7SGreg Roach // Insert text at the current cursor position in a text field. 1050d2905f7SGreg Roach webtrees.pasteAtCursor = function (element, text) { 1060d2905f7SGreg Roach if (element !== null) { 1070d2905f7SGreg Roach const caret_pos = element.selectionStart + text.length; 1080d2905f7SGreg Roach const textBefore = element.value.substring(0, element.selectionStart); 1090d2905f7SGreg Roach const textAfter = element.value.substring(element.selectionEnd); 1100d2905f7SGreg Roach element.value = textBefore + text + textAfter; 1110d2905f7SGreg Roach element.setSelectionRange(caret_pos, caret_pos); 1120d2905f7SGreg Roach element.focus(); 11359e18f0cSGreg Roach } 114efd89170SGreg Roach }; 11571239cb6SGreg Roach 11606fe1eb5SGreg Roach /** 1177fb78f8aSGreg Roach * @param {Element} datefield 11806fe1eb5SGreg Roach * @param {string} dmy 11906fe1eb5SGreg Roach */ 120a7a3d6dbSGreg Roach webtrees.reformatDate = function (datefield, dmy) { 121a7a3d6dbSGreg Roach const months = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC']; 122a7a3d6dbSGreg Roach const hijri_months = ['MUHAR', 'SAFAR', 'RABIA', 'RABIT', 'JUMAA', 'JUMAT', 'RAJAB', 'SHAAB', 'RAMAD', 'SHAWW', 'DHUAQ', 'DHUAH']; 123a7a3d6dbSGreg Roach const hebrew_months = ['TSH', 'CSH', 'KSL', 'TVT', 'SHV', 'ADR', 'ADS', 'NSN', 'IYR', 'SVN', 'TMZ', 'AAV', 'ELL']; 124a7a3d6dbSGreg Roach const french_months = ['VEND', 'BRUM', 'FRIM', 'NIVO', 'PLUV', 'VENT', 'GERM', 'FLOR', 'PRAI', 'MESS', 'THER', 'FRUC', 'COMP']; 125a7a3d6dbSGreg Roach const jalali_months = ['FARVA', 'ORDIB', 'KHORD', 'TIR', 'MORDA', 'SHAHR', 'MEHR', 'ABAN', 'AZAR', 'DEY', 'BAHMA', 'ESFAN']; 12671239cb6SGreg Roach 127a7a3d6dbSGreg Roach let datestr = datefield.value; 12871239cb6SGreg Roach // if a date has a date phrase marked by () this has to be excluded from altering 129a7a3d6dbSGreg Roach let datearr = datestr.split('('); 130a7a3d6dbSGreg Roach let datephrase = ''; 13171239cb6SGreg Roach if (datearr.length > 1) { 13271239cb6SGreg Roach datestr = datearr[0]; 13371239cb6SGreg Roach datephrase = datearr[1]; 13471239cb6SGreg Roach } 13571239cb6SGreg Roach 13671239cb6SGreg Roach // Gedcom dates are upper case 13771239cb6SGreg Roach datestr = datestr.toUpperCase(); 13871239cb6SGreg Roach // Gedcom dates have no leading/trailing/repeated whitespace 13980d699d6SGreg Roach datestr = datestr.replace(/\s+/g, ' '); 14071239cb6SGreg Roach datestr = datestr.replace(/(^\s)|(\s$)/, ''); 14171239cb6SGreg Roach // Gedcom dates have spaces between letters and digits, e.g. "01JAN2000" => "01 JAN 2000" 14280d699d6SGreg Roach datestr = datestr.replace(/(\d)([A-Z])/g, '$1 $2'); 14380d699d6SGreg Roach datestr = datestr.replace(/([A-Z])(\d)/g, '$1 $2'); 14471239cb6SGreg Roach 14571239cb6SGreg Roach // Shortcut for quarter format, "Q1 1900" => "BET JAN 1900 AND MAR 1900". See [ 1509083 ] 14671239cb6SGreg Roach if (datestr.match(/^Q ([1-4]) (\d\d\d\d)$/)) { 14771239cb6SGreg Roach datestr = 'BET ' + months[RegExp.$1 * 3 - 3] + ' ' + RegExp.$2 + ' AND ' + months[RegExp.$1 * 3 - 1] + ' ' + RegExp.$2; 14871239cb6SGreg Roach } 14971239cb6SGreg Roach 15071239cb6SGreg Roach // Shortcut for @#Dxxxxx@ 01 01 1400, etc. 15171239cb6SGreg Roach if (datestr.match(/^(@#DHIJRI@|HIJRI)( \d?\d )(\d?\d)( \d?\d?\d?\d)$/)) { 15271239cb6SGreg Roach datestr = '@#DHIJRI@' + RegExp.$2 + hijri_months[parseInt(RegExp.$3, 10) - 1] + RegExp.$4; 15371239cb6SGreg Roach } 15471239cb6SGreg Roach if (datestr.match(/^(@#DJALALI@|JALALI)( \d?\d )(\d?\d)( \d?\d?\d?\d)$/)) { 15571239cb6SGreg Roach datestr = '@#DJALALI@' + RegExp.$2 + jalali_months[parseInt(RegExp.$3, 10) - 1] + RegExp.$4; 15671239cb6SGreg Roach } 15771239cb6SGreg Roach if (datestr.match(/^(@#DHEBREW@|HEBREW)( \d?\d )(\d?\d)( \d?\d?\d?\d)$/)) { 15871239cb6SGreg Roach datestr = '@#DHEBREW@' + RegExp.$2 + hebrew_months[parseInt(RegExp.$3, 10) - 1] + RegExp.$4; 15971239cb6SGreg Roach } 16071239cb6SGreg Roach if (datestr.match(/^(@#DFRENCH R@|FRENCH)( \d?\d )(\d?\d)( \d?\d?\d?\d)$/)) { 16171239cb6SGreg Roach datestr = '@#DFRENCH R@' + RegExp.$2 + french_months[parseInt(RegExp.$3, 10) - 1] + RegExp.$4; 16271239cb6SGreg Roach } 16371239cb6SGreg Roach 1647fb78f8aSGreg Roach // All digit dates 1657fb78f8aSGreg Roach datestr = datestr.replaceAll(/(\d\d)(\d\d)(\d\d)(\d\d)/g, function () { 1667fb78f8aSGreg Roach if (RegExp.$1 > '12' && RegExp.$3 <= '12' && RegExp.$4 <= '31') { 1677fb78f8aSGreg Roach return RegExp.$4 + ' ' + months[RegExp.$3 - 1] + ' ' + RegExp.$1 + RegExp.$2; 1687fb78f8aSGreg Roach } 1697fb78f8aSGreg Roach if (RegExp.$1 <= '31' && RegExp.$2 <= '12' && RegExp.$3 > '12') { 1707fb78f8aSGreg Roach return RegExp.$1 + ' ' + months[RegExp.$2 - 1] + ' ' + RegExp.$3 + RegExp.$4; 1717fb78f8aSGreg Roach } 1727fb78f8aSGreg Roach return RegExp.$1 + RegExp.$2 + RegExp.$3 + RegExp.$4; 1737fb78f8aSGreg Roach }); 1747fb78f8aSGreg Roach 1757fb78f8aSGreg Roach // e.g. 17.11.1860, 3/4/2005 or 1999-12-31. Use locale settings since DMY order is ambiguous. 1767fb78f8aSGreg Roach datestr = datestr.replaceAll(/(\d+)([./-])(\d+)([./-])(\d+)/g, function () { 1772cf1b3d7SGreg Roach let f1 = parseInt(RegExp.$1, 10); 1782cf1b3d7SGreg Roach let f2 = parseInt(RegExp.$3, 10); 1792cf1b3d7SGreg Roach let f3 = parseInt(RegExp.$5, 10); 1802cf1b3d7SGreg Roach let yyyy = new Date().getFullYear(); 1812cf1b3d7SGreg Roach let yy = yyyy % 100; 1822cf1b3d7SGreg Roach let cc = yyyy - yy; 18371239cb6SGreg Roach if (dmy === 'DMY' && f1 <= 31 && f2 <= 12 || f1 > 13 && f1 <= 31 && f2 <= 12 && f3 > 31) { 1847fb78f8aSGreg Roach return f1 + ' ' + months[f2 - 1] + ' ' + (f3 >= 100 ? f3 : (f3 <= yy ? f3 + cc : f3 + cc - 100)); 1857fb78f8aSGreg Roach } 18671239cb6SGreg Roach if (dmy === 'MDY' && f1 <= 12 && f2 <= 31 || f2 > 13 && f2 <= 31 && f1 <= 12 && f3 > 31) { 1877fb78f8aSGreg Roach return f2 + ' ' + months[f1 - 1] + ' ' + (f3 >= 100 ? f3 : (f3 <= yy ? f3 + cc : f3 + cc - 100)); 1887fb78f8aSGreg Roach } 18971239cb6SGreg Roach if (dmy === 'YMD' && f2 <= 12 && f3 <= 31 || f3 > 13 && f3 <= 31 && f2 <= 12 && f1 > 31) { 1907fb78f8aSGreg Roach return f3 + ' ' + months[f2 - 1] + ' ' + (f1 >= 100 ? f1 : (f1 <= yy ? f1 + cc : f1 + cc - 100)); 19171239cb6SGreg Roach } 1927fb78f8aSGreg Roach return RegExp.$1 + RegExp.$2 + RegExp.$3 + RegExp.$4 + RegExp.$5; 1937fb78f8aSGreg Roach }); 19471239cb6SGreg Roach 195a7a3d6dbSGreg Roach datestr = datestr 19671239cb6SGreg Roach // Shortcuts for date ranges 197a7a3d6dbSGreg Roach .replace(/^[>]([\w ]+)$/, 'AFT $1') 198a7a3d6dbSGreg Roach .replace(/^[<]([\w ]+)$/, 'BEF $1') 199a7a3d6dbSGreg Roach .replace(/^([\w ]+)[-]$/, 'FROM $1') 200a7a3d6dbSGreg Roach .replace(/^[-]([\w ]+)$/, 'TO $1') 201a7a3d6dbSGreg Roach .replace(/^[~]([\w ]+)$/, 'ABT $1') 202a7a3d6dbSGreg Roach .replace(/^[*]([\w ]+)$/, 'EST $1') 203a7a3d6dbSGreg Roach .replace(/^[#]([\w ]+)$/, 'CAL $1') 204a7a3d6dbSGreg Roach .replace(/^([\w ]+) ?- ?([\w ]+)$/, 'BET $1 AND $2') 205a7a3d6dbSGreg Roach .replace(/^([\w ]+) ?~ ?([\w ]+)$/, 'FROM $1 TO $2') 20671239cb6SGreg Roach // Convert full months to short months 207a7a3d6dbSGreg Roach .replaceAll('JANUARY', 'JAN') 208a7a3d6dbSGreg Roach .replaceAll('FEBRUARY', 'FEB') 209a7a3d6dbSGreg Roach .replaceAll('MARCH', 'MAR') 210a7a3d6dbSGreg Roach .replaceAll('APRIL', 'APR') 211a7a3d6dbSGreg Roach .replaceAll('JUNE', 'JUN') 212a7a3d6dbSGreg Roach .replaceAll('JULY', 'JUL') 213a7a3d6dbSGreg Roach .replaceAll('AUGUST', 'AUG') 214a7a3d6dbSGreg Roach .replaceAll('SEPTEMBER', 'SEP') 215a7a3d6dbSGreg Roach .replaceAll('OCTOBER', 'OCT') 216a7a3d6dbSGreg Roach .replaceAll('NOVEMBER', 'NOV') 217a7a3d6dbSGreg Roach .replaceAll('DECEMBER', 'DEC') 218a7a3d6dbSGreg Roach // Americans enter dates as SEP 20, 1999 219915908ebSGreg Roach .replaceAll(/(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)\.? (\d\d?)[, ]+(\d\d\d\d)/g, '$2 $1 $3') 22071239cb6SGreg Roach // Apply leading zero to day numbers 221915908ebSGreg Roach .replaceAll(/(^| )(\d [A-Z]{3,5} \d{4})/g, '$10$2'); 22271239cb6SGreg Roach 22371239cb6SGreg Roach if (datephrase) { 22471239cb6SGreg Roach datestr = datestr + ' (' + datephrase; 22571239cb6SGreg Roach } 226a7a3d6dbSGreg Roach 22771239cb6SGreg Roach // Only update it if is has been corrected - otherwise input focus 22871239cb6SGreg Roach // moves to the end of the field unnecessarily 22971239cb6SGreg Roach if (datefield.value !== datestr) { 23071239cb6SGreg Roach datefield.value = datestr; 23171239cb6SGreg Roach } 2322cf1b3d7SGreg Roach }; 23371239cb6SGreg Roach 2342cf1b3d7SGreg Roach let monthLabels = []; 23571239cb6SGreg Roach monthLabels[1] = 'January'; 23671239cb6SGreg Roach monthLabels[2] = 'February'; 23771239cb6SGreg Roach monthLabels[3] = 'March'; 23871239cb6SGreg Roach monthLabels[4] = 'April'; 23971239cb6SGreg Roach monthLabels[5] = 'May'; 24071239cb6SGreg Roach monthLabels[6] = 'June'; 24171239cb6SGreg Roach monthLabels[7] = 'July'; 24271239cb6SGreg Roach monthLabels[8] = 'August'; 24371239cb6SGreg Roach monthLabels[9] = 'September'; 24471239cb6SGreg Roach monthLabels[10] = 'October'; 24571239cb6SGreg Roach monthLabels[11] = 'November'; 24671239cb6SGreg Roach monthLabels[12] = 'December'; 24771239cb6SGreg Roach 2482cf1b3d7SGreg Roach let monthShort = []; 24971239cb6SGreg Roach monthShort[1] = 'JAN'; 25071239cb6SGreg Roach monthShort[2] = 'FEB'; 25171239cb6SGreg Roach monthShort[3] = 'MAR'; 25271239cb6SGreg Roach monthShort[4] = 'APR'; 25371239cb6SGreg Roach monthShort[5] = 'MAY'; 25471239cb6SGreg Roach monthShort[6] = 'JUN'; 25571239cb6SGreg Roach monthShort[7] = 'JUL'; 25671239cb6SGreg Roach monthShort[8] = 'AUG'; 25771239cb6SGreg Roach monthShort[9] = 'SEP'; 25871239cb6SGreg Roach monthShort[10] = 'OCT'; 25971239cb6SGreg Roach monthShort[11] = 'NOV'; 26071239cb6SGreg Roach monthShort[12] = 'DEC'; 26171239cb6SGreg Roach 2622cf1b3d7SGreg Roach let daysOfWeek = []; 26371239cb6SGreg Roach daysOfWeek[0] = 'S'; 26471239cb6SGreg Roach daysOfWeek[1] = 'M'; 26571239cb6SGreg Roach daysOfWeek[2] = 'T'; 26671239cb6SGreg Roach daysOfWeek[3] = 'W'; 26771239cb6SGreg Roach daysOfWeek[4] = 'T'; 26871239cb6SGreg Roach daysOfWeek[5] = 'F'; 26971239cb6SGreg Roach daysOfWeek[6] = 'S'; 27071239cb6SGreg Roach 2712cf1b3d7SGreg Roach let weekStart = 0; 27271239cb6SGreg Roach 27306fe1eb5SGreg Roach /** 27406fe1eb5SGreg Roach * @param {string} jan 27506fe1eb5SGreg Roach * @param {string} feb 27606fe1eb5SGreg Roach * @param {string} mar 27706fe1eb5SGreg Roach * @param {string} apr 27806fe1eb5SGreg Roach * @param {string} may 27906fe1eb5SGreg Roach * @param {string} jun 28006fe1eb5SGreg Roach * @param {string} jul 28106fe1eb5SGreg Roach * @param {string} aug 28206fe1eb5SGreg Roach * @param {string} sep 28306fe1eb5SGreg Roach * @param {string} oct 28406fe1eb5SGreg Roach * @param {string} nov 28506fe1eb5SGreg Roach * @param {string} dec 2862cf1b3d7SGreg Roach * @param {string} sun 2872cf1b3d7SGreg Roach * @param {string} mon 2882cf1b3d7SGreg Roach * @param {string} tue 2892cf1b3d7SGreg Roach * @param {string} wed 2902cf1b3d7SGreg Roach * @param {string} thu 2912cf1b3d7SGreg Roach * @param {string} fri 2922cf1b3d7SGreg Roach * @param {string} sat 2932cf1b3d7SGreg Roach * @param {number} day 29406fe1eb5SGreg Roach */ 2952cf1b3d7SGreg Roach webtrees.calLocalize = function (jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec, sun, mon, tue, wed, thu, fri, sat, day) { 29671239cb6SGreg Roach monthLabels[1] = jan; 29771239cb6SGreg Roach monthLabels[2] = feb; 29871239cb6SGreg Roach monthLabels[3] = mar; 29971239cb6SGreg Roach monthLabels[4] = apr; 30071239cb6SGreg Roach monthLabels[5] = may; 30171239cb6SGreg Roach monthLabels[6] = jun; 30271239cb6SGreg Roach monthLabels[7] = jul; 30371239cb6SGreg Roach monthLabels[8] = aug; 30471239cb6SGreg Roach monthLabels[9] = sep; 30571239cb6SGreg Roach monthLabels[10] = oct; 30671239cb6SGreg Roach monthLabels[11] = nov; 30771239cb6SGreg Roach monthLabels[12] = dec; 30871239cb6SGreg Roach daysOfWeek[0] = sun; 30971239cb6SGreg Roach daysOfWeek[1] = mon; 31071239cb6SGreg Roach daysOfWeek[2] = tue; 31171239cb6SGreg Roach daysOfWeek[3] = wed; 31271239cb6SGreg Roach daysOfWeek[4] = thu; 31371239cb6SGreg Roach daysOfWeek[5] = fri; 31471239cb6SGreg Roach daysOfWeek[6] = sat; 31571239cb6SGreg Roach 31671239cb6SGreg Roach if (day >= 0 && day < 7) { 31771239cb6SGreg Roach weekStart = day; 31871239cb6SGreg Roach } 3192cf1b3d7SGreg Roach }; 32071239cb6SGreg Roach 32106fe1eb5SGreg Roach /** 32206fe1eb5SGreg Roach * @param {string} dateDivId 32306fe1eb5SGreg Roach * @param {string} dateFieldId 32406fe1eb5SGreg Roach * @returns {boolean} 32506fe1eb5SGreg Roach */ 3262cf1b3d7SGreg Roach webtrees.calendarWidget = function (dateDivId, dateFieldId) { 3272cf1b3d7SGreg Roach let dateDiv = document.getElementById(dateDivId); 3282cf1b3d7SGreg Roach let dateField = document.getElementById(dateFieldId); 32971239cb6SGreg Roach 33071239cb6SGreg Roach if (dateDiv.style.visibility === 'visible') { 33171239cb6SGreg Roach dateDiv.style.visibility = 'hidden'; 33271239cb6SGreg Roach return false; 33371239cb6SGreg Roach } 33471239cb6SGreg Roach if (dateDiv.style.visibility === 'show') { 33571239cb6SGreg Roach dateDiv.style.visibility = 'hide'; 33671239cb6SGreg Roach return false; 33771239cb6SGreg Roach } 33871239cb6SGreg Roach 33971239cb6SGreg Roach /* Javascript calendar functions only work with precise gregorian dates "D M Y" or "Y" */ 3402cf1b3d7SGreg Roach let greg_regex = /((\d+ (JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC) )?\d+)/i; 3412cf1b3d7SGreg Roach let date; 34271239cb6SGreg Roach if (greg_regex.exec(dateField.value)) { 34371239cb6SGreg Roach date = new Date(RegExp.$1); 34471239cb6SGreg Roach } else { 34571239cb6SGreg Roach date = new Date(); 34671239cb6SGreg Roach } 34771239cb6SGreg Roach 3482cf1b3d7SGreg Roach dateDiv.innerHTML = calGenerateSelectorContent(dateFieldId, dateDivId, date); 34971239cb6SGreg Roach if (dateDiv.style.visibility === 'hidden') { 35071239cb6SGreg Roach dateDiv.style.visibility = 'visible'; 35171239cb6SGreg Roach return false; 35271239cb6SGreg Roach } 35371239cb6SGreg Roach if (dateDiv.style.visibility === 'hide') { 35471239cb6SGreg Roach dateDiv.style.visibility = 'show'; 35571239cb6SGreg Roach return false; 35671239cb6SGreg Roach } 35771239cb6SGreg Roach 35871239cb6SGreg Roach return false; 3592cf1b3d7SGreg Roach }; 36071239cb6SGreg Roach 36106fe1eb5SGreg Roach /** 36206fe1eb5SGreg Roach * @param {string} dateFieldId 36306fe1eb5SGreg Roach * @param {string} dateDivId 36406fe1eb5SGreg Roach * @param {Date} date 36506fe1eb5SGreg Roach * @returns {string} 36606fe1eb5SGreg Roach */ 3672cf1b3d7SGreg Roach function calGenerateSelectorContent (dateFieldId, dateDivId, date) { 3682cf1b3d7SGreg Roach let i, j; 3692cf1b3d7SGreg Roach let content = '<table border="1"><tr>'; 3702cf1b3d7SGreg Roach content += '<td><select class="form-control" id="' + dateFieldId + '_daySelect" onchange="return webtrees.calUpdateCalendar(\'' + dateFieldId + '\', \'' + dateDivId + '\');">'; 37171239cb6SGreg Roach for (i = 1; i < 32; i++) { 37271239cb6SGreg Roach content += '<option value="' + i + '"'; 37371239cb6SGreg Roach if (date.getDate() === i) { 37471239cb6SGreg Roach content += ' selected="selected"'; 37571239cb6SGreg Roach } 37671239cb6SGreg Roach content += '>' + i + '</option>'; 37771239cb6SGreg Roach } 37871239cb6SGreg Roach content += '</select></td>'; 3792cf1b3d7SGreg Roach content += '<td><select class="form-control" id="' + dateFieldId + '_monSelect" onchange="return webtrees.calUpdateCalendar(\'' + dateFieldId + '\', \'' + dateDivId + '\');">'; 38071239cb6SGreg Roach for (i = 1; i < 13; i++) { 38171239cb6SGreg Roach content += '<option value="' + i + '"'; 38271239cb6SGreg Roach if (date.getMonth() + 1 === i) { 38371239cb6SGreg Roach content += ' selected="selected"'; 38471239cb6SGreg Roach } 38571239cb6SGreg Roach content += '>' + monthLabels[i] + '</option>'; 38671239cb6SGreg Roach } 38771239cb6SGreg Roach content += '</select></td>'; 3882cf1b3d7SGreg Roach content += '<td><input class="form-control" type="text" id="' + dateFieldId + '_yearInput" size="5" value="' + date.getFullYear() + '" onchange="return webtrees.calUpdateCalendar(\'' + dateFieldId + '\', \'' + dateDivId + '\');" /></td></tr>'; 38971239cb6SGreg Roach content += '<tr><td colspan="3">'; 39071239cb6SGreg Roach content += '<table width="100%">'; 39171239cb6SGreg Roach content += '<tr>'; 39271239cb6SGreg Roach j = weekStart; 39371239cb6SGreg Roach for (i = 0; i < 7; i++) { 39471239cb6SGreg Roach content += '<td '; 39571239cb6SGreg Roach content += 'class="descriptionbox"'; 39671239cb6SGreg Roach content += '>'; 39771239cb6SGreg Roach content += daysOfWeek[j]; 39871239cb6SGreg Roach content += '</td>'; 39971239cb6SGreg Roach j++; 40071239cb6SGreg Roach if (j > 6) { 40171239cb6SGreg Roach j = 0; 40271239cb6SGreg Roach } 40371239cb6SGreg Roach } 40471239cb6SGreg Roach content += '</tr>'; 40571239cb6SGreg Roach 4062cf1b3d7SGreg Roach let tdate = new Date(date.getFullYear(), date.getMonth(), 1); 4072cf1b3d7SGreg Roach let day = tdate.getDay(); 40871239cb6SGreg Roach day = day - weekStart; 4092cf1b3d7SGreg Roach let daymilli = 1000 * 60 * 60 * 24; 41071239cb6SGreg Roach tdate = tdate.getTime() - (day * daymilli) + (daymilli / 2); 41171239cb6SGreg Roach tdate = new Date(tdate); 41271239cb6SGreg Roach 41371239cb6SGreg Roach for (j = 0; j < 6; j++) { 41471239cb6SGreg Roach content += '<tr>'; 41571239cb6SGreg Roach for (i = 0; i < 7; i++) { 41671239cb6SGreg Roach content += '<td '; 41771239cb6SGreg Roach if (tdate.getMonth() === date.getMonth()) { 41871239cb6SGreg Roach if (tdate.getDate() === date.getDate()) { 41971239cb6SGreg Roach content += 'class="descriptionbox"'; 42071239cb6SGreg Roach } else { 42171239cb6SGreg Roach content += 'class="optionbox"'; 42271239cb6SGreg Roach } 42371239cb6SGreg Roach } else { 42471239cb6SGreg Roach content += 'style="background-color:#EAEAEA; border: solid #AAAAAA 1px;"'; 42571239cb6SGreg Roach } 4262cf1b3d7SGreg Roach content += '><a href="#" onclick="return webtrees.calDateClicked(\'' + dateFieldId + '\', \'' + dateDivId + '\', ' + tdate.getFullYear() + ', ' + tdate.getMonth() + ', ' + tdate.getDate() + ');">'; 42771239cb6SGreg Roach content += tdate.getDate(); 42871239cb6SGreg Roach content += '</a></td>'; 4292cf1b3d7SGreg Roach let datemilli = tdate.getTime() + daymilli; 43071239cb6SGreg Roach tdate = new Date(datemilli); 43171239cb6SGreg Roach } 43271239cb6SGreg Roach content += '</tr>'; 43371239cb6SGreg Roach } 43471239cb6SGreg Roach content += '</table>'; 43571239cb6SGreg Roach content += '</td></tr>'; 43671239cb6SGreg Roach content += '</table>'; 43771239cb6SGreg Roach 43871239cb6SGreg Roach return content; 43971239cb6SGreg Roach } 44071239cb6SGreg Roach 44106fe1eb5SGreg Roach /** 44206fe1eb5SGreg Roach * @param {string} dateFieldId 44306fe1eb5SGreg Roach * @param {number} year 44406fe1eb5SGreg Roach * @param {number} month 44506fe1eb5SGreg Roach * @param {number} day 44606fe1eb5SGreg Roach * @returns {boolean} 44706fe1eb5SGreg Roach */ 4482cf1b3d7SGreg Roach function calSetDateField (dateFieldId, year, month, day) { 4492cf1b3d7SGreg Roach let dateField = document.getElementById(dateFieldId); 4502cf1b3d7SGreg Roach dateField.value = (day < 10 ? '0' : '') + day + ' ' + monthShort[month + 1] + ' ' + year; 45171239cb6SGreg Roach return false; 45271239cb6SGreg Roach } 45371239cb6SGreg Roach 45406fe1eb5SGreg Roach /** 45506fe1eb5SGreg Roach * @param {string} dateFieldId 45606fe1eb5SGreg Roach * @param {string} dateDivId 45706fe1eb5SGreg Roach * @returns {boolean} 45806fe1eb5SGreg Roach */ 4592cf1b3d7SGreg Roach webtrees.calUpdateCalendar = function (dateFieldId, dateDivId) { 4602cf1b3d7SGreg Roach let dateSel = document.getElementById(dateFieldId + '_daySelect'); 46171239cb6SGreg Roach if (!dateSel) { 46271239cb6SGreg Roach return false; 46371239cb6SGreg Roach } 4642cf1b3d7SGreg Roach let monthSel = document.getElementById(dateFieldId + '_monSelect'); 46571239cb6SGreg Roach if (!monthSel) { 46671239cb6SGreg Roach return false; 46771239cb6SGreg Roach } 4682cf1b3d7SGreg Roach let yearInput = document.getElementById(dateFieldId + '_yearInput'); 46971239cb6SGreg Roach if (!yearInput) { 47071239cb6SGreg Roach return false; 47171239cb6SGreg Roach } 47271239cb6SGreg Roach 4732cf1b3d7SGreg Roach let month = parseInt(monthSel.options[monthSel.selectedIndex].value, 10); 47471239cb6SGreg Roach month = month - 1; 47571239cb6SGreg Roach 4762cf1b3d7SGreg Roach let date = new Date(yearInput.value, month, dateSel.options[dateSel.selectedIndex].value); 4772cf1b3d7SGreg Roach calSetDateField(dateFieldId, date.getFullYear(), date.getMonth(), date.getDate()); 47871239cb6SGreg Roach 4792cf1b3d7SGreg Roach let dateDiv = document.getElementById(dateDivId); 48071239cb6SGreg Roach if (!dateDiv) { 48171239cb6SGreg Roach alert('no dateDiv ' + dateDivId); 48271239cb6SGreg Roach return false; 48371239cb6SGreg Roach } 4842cf1b3d7SGreg Roach dateDiv.innerHTML = calGenerateSelectorContent(dateFieldId, dateDivId, date); 48571239cb6SGreg Roach 48671239cb6SGreg Roach return false; 4872cf1b3d7SGreg Roach }; 48871239cb6SGreg Roach 48906fe1eb5SGreg Roach /** 49006fe1eb5SGreg Roach * @param {string} dateFieldId 49106fe1eb5SGreg Roach * @param {string} dateDivId 49206fe1eb5SGreg Roach * @param {number} year 49306fe1eb5SGreg Roach * @param {number} month 49406fe1eb5SGreg Roach * @param {number} day 49506fe1eb5SGreg Roach * @returns {boolean} 49606fe1eb5SGreg Roach */ 4972cf1b3d7SGreg Roach webtrees.calDateClicked = function (dateFieldId, dateDivId, year, month, day) { 4982cf1b3d7SGreg Roach calSetDateField(dateFieldId, year, month, day); 4992cf1b3d7SGreg Roach webtrees.calendarWidget(dateDivId, dateFieldId); 50071239cb6SGreg Roach return false; 5012cf1b3d7SGreg Roach }; 50271239cb6SGreg Roach 50306fe1eb5SGreg Roach /** 5042cf1b3d7SGreg Roach * Persistent checkbox options to hide/show extra data. 5052cf1b3d7SGreg Roach * @param {string} element_id 50671239cb6SGreg Roach */ 5072cf1b3d7SGreg Roach webtrees.persistentToggle = function (element_id) { 508efd89170SGreg Roach const element = document.getElementById(element_id); 509efd89170SGreg Roach const key = 'state-of-' + element_id; 510efd89170SGreg Roach const state = localStorage.getItem(key); 51171239cb6SGreg Roach 51264490ee2SGreg Roach // Previously selected? 51364490ee2SGreg Roach if (state === 'true') { 5142cf1b3d7SGreg Roach element.click(); 51571239cb6SGreg Roach } 51671239cb6SGreg Roach 51764490ee2SGreg Roach // Remember state for the next page load. 5182cf1b3d7SGreg Roach element.addEventListener('change', function () { 5192cf1b3d7SGreg Roach localStorage.setItem(key, element.checked); 5202cf1b3d7SGreg Roach }); 5212cf1b3d7SGreg Roach }; 52271239cb6SGreg Roach 52306fe1eb5SGreg Roach /** 5242cf1b3d7SGreg Roach * @param {Element} field 52506fe1eb5SGreg Roach * @param {string} pos 52606fe1eb5SGreg Roach * @param {string} neg 52706fe1eb5SGreg Roach */ 5282cf1b3d7SGreg Roach function reformatLatLong (field, pos, neg) { 52971239cb6SGreg Roach // valid LATI or LONG according to Gedcom standard 53071239cb6SGreg Roach // pos (+) : N or E 53171239cb6SGreg Roach // neg (-) : S or W 5322cf1b3d7SGreg Roach let txt = field.value.toUpperCase(); 53371239cb6SGreg Roach txt = txt.replace(/(^\s*)|(\s*$)/g, ''); // trim 53471239cb6SGreg Roach txt = txt.replace(/ /g, ':'); // N12 34 ==> N12.34 53571239cb6SGreg Roach txt = txt.replace(/\+/g, ''); // +17.1234 ==> 17.1234 53671239cb6SGreg Roach txt = txt.replace(/-/g, neg); // -0.5698 ==> W0.5698 53771239cb6SGreg Roach txt = txt.replace(/,/g, '.'); // 0,5698 ==> 0.5698 53871239cb6SGreg Roach // 0°34'11 ==> 0:34:11 53971239cb6SGreg Roach txt = txt.replace(/\u00b0/g, ':'); // ° 54071239cb6SGreg Roach txt = txt.replace(/\u0027/g, ':'); // ' 54171239cb6SGreg Roach // 0:34:11.2W ==> W0.5698 54271239cb6SGreg Roach txt = txt.replace(/^([0-9]+):([0-9]+):([0-9.]+)(.*)/g, function ($0, $1, $2, $3, $4) { 5432cf1b3d7SGreg Roach let n = parseFloat($1); 54471239cb6SGreg Roach n += ($2 / 60); 54571239cb6SGreg Roach n += ($3 / 3600); 54671239cb6SGreg Roach n = Math.round(n * 1E4) / 1E4; 54771239cb6SGreg Roach return $4 + n; 54871239cb6SGreg Roach }); 54971239cb6SGreg Roach // 0:34W ==> W0.5667 55071239cb6SGreg Roach txt = txt.replace(/^([0-9]+):([0-9]+)(.*)/g, function ($0, $1, $2, $3) { 5512cf1b3d7SGreg Roach let n = parseFloat($1); 55271239cb6SGreg Roach n += ($2 / 60); 55371239cb6SGreg Roach n = Math.round(n * 1E4) / 1E4; 55471239cb6SGreg Roach return $3 + n; 55571239cb6SGreg Roach }); 55671239cb6SGreg Roach // 0.5698W ==> W0.5698 5572cf1b3d7SGreg Roach txt = txt.replace(/(.*)(NSEW])$/g, '$2$1'); 55871239cb6SGreg Roach // 17.1234 ==> N17.1234 55971239cb6SGreg Roach if (txt && txt.charAt(0) !== neg && txt.charAt(0) !== pos) { 56071239cb6SGreg Roach txt = pos + txt; 56171239cb6SGreg Roach } 56271239cb6SGreg Roach field.value = txt; 56371239cb6SGreg Roach } 56471239cb6SGreg Roach 56506fe1eb5SGreg Roach /** 5662cf1b3d7SGreg Roach * @param {Element} field 5672cf1b3d7SGreg Roach */ 5682cf1b3d7SGreg Roach webtrees.reformatLatitude = function (field) { 5692cf1b3d7SGreg Roach return reformatLatLong(field, 'N', 'S'); 5702cf1b3d7SGreg Roach }; 5712cf1b3d7SGreg Roach 5722cf1b3d7SGreg Roach /** 5732cf1b3d7SGreg Roach * @param {Element} field 5742cf1b3d7SGreg Roach */ 5752cf1b3d7SGreg Roach webtrees.reformatLongitude = function (field) { 5762cf1b3d7SGreg Roach return reformatLatLong(field, 'E', 'W'); 5772cf1b3d7SGreg Roach }; 5782cf1b3d7SGreg Roach 5792cf1b3d7SGreg Roach /** 58006fe1eb5SGreg Roach * Initialize autocomplete elements. 58106fe1eb5SGreg Roach * @param {string} selector 58206fe1eb5SGreg Roach */ 5832cf1b3d7SGreg Roach webtrees.autocomplete = function (selector) { 58471239cb6SGreg Roach // Use typeahead/bloodhound for autocomplete 58571239cb6SGreg Roach $(selector).each(function () { 586efd89170SGreg Roach const that = this; 58771239cb6SGreg Roach $(this).typeahead(null, { 58871239cb6SGreg Roach display: 'value', 5895e6816beSGreg Roach limit: 0, 59071239cb6SGreg Roach source: new Bloodhound({ 59171239cb6SGreg Roach datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'), 59271239cb6SGreg Roach queryTokenizer: Bloodhound.tokenizers.whitespace, 59371239cb6SGreg Roach remote: { 59471239cb6SGreg Roach url: this.dataset.autocompleteUrl, 595f4abaf12SGreg Roach replace: function (url, uriEncodedQuery) { 596f4abaf12SGreg Roach if (that.dataset.autocompleteExtra) { 597efd89170SGreg Roach const extra = $(document.querySelector(that.dataset.autocompleteExtra)).val(); 598efd89170SGreg Roach return url.replace('QUERY', uriEncodedQuery) + '&extra=' + encodeURIComponent(extra); 599f4abaf12SGreg Roach } 600efd89170SGreg Roach return url.replace('QUERY', uriEncodedQuery); 601f4abaf12SGreg Roach }, 602efd89170SGreg Roach wildcard: 'QUERY' 60371239cb6SGreg Roach } 60471239cb6SGreg Roach }) 60571239cb6SGreg Roach }); 60671239cb6SGreg Roach }); 6072cf1b3d7SGreg Roach }; 6082cf1b3d7SGreg Roach}(window.webtrees = window.webtrees || {})); 60971239cb6SGreg Roach 61071239cb6SGreg Roach// Send the CSRF token on all AJAX requests 61171239cb6SGreg Roach$.ajaxSetup({ 61271239cb6SGreg Roach headers: { 61371239cb6SGreg Roach 'X-CSRF-TOKEN': $('meta[name=csrf]').attr('content') 61471239cb6SGreg Roach } 61571239cb6SGreg Roach}); 61671239cb6SGreg Roach 617*4ed7dff1SGreg Roach/** 618*4ed7dff1SGreg Roach * Initialisation 619*4ed7dff1SGreg Roach */ 620*4ed7dff1SGreg Roach$(function () { 6212cf1b3d7SGreg Roach // Page elements that load automatically via AJAX. 62271239cb6SGreg Roach // This prevents bad robots from crawling resource-intensive pages. 623efd89170SGreg Roach $('[data-ajax-url]').each(function () { 62471239cb6SGreg Roach $(this).load($(this).data('ajaxUrl')); 62571239cb6SGreg Roach }); 62671239cb6SGreg Roach 62771239cb6SGreg Roach // Autocomplete 6282cf1b3d7SGreg Roach webtrees.autocomplete('input[data-autocomplete-url]'); 62971239cb6SGreg Roach 63071239cb6SGreg Roach // Select2 - activate autocomplete fields 631bdbdb10cSGreg Roach const lang = document.documentElement.lang; 632bdbdb10cSGreg Roach const select2_languages = { 633bdbdb10cSGreg Roach 'zh-Hans': 'zh-CN', 634efd89170SGreg Roach 'zh-Hant': 'zh-TW' 635bdbdb10cSGreg Roach }; 636efd89170SGreg Roach $('select.select2').select2({ 637bdbdb10cSGreg Roach language: select2_languages[lang] || lang, 638aee7167dSGreg Roach // Needed for elements that are initially hidden. 639efd89170SGreg Roach width: '100%', 640896a5721SGreg Roach // Do not escape - we do it on the server. 64171239cb6SGreg Roach escapeMarkup: function (x) { 6428ec20abdSGreg Roach return x; 643efd89170SGreg Roach } 644896a5721SGreg Roach }); 645896a5721SGreg Roach 646896a5721SGreg Roach // If we clear the select (using the "X" button), we need an empty value 647896a5721SGreg Roach // (rather than no value at all) for (non-multiple) selects with name="array[]" 648efd89170SGreg Roach $('select.select2:not([multiple])') 649efd89170SGreg Roach .on('select2:unselect', function (evt) { 650efd89170SGreg Roach $(evt.delegateTarget).html('<option value="" selected></option>'); 651bdbdb10cSGreg Roach }); 65271239cb6SGreg Roach 65371239cb6SGreg Roach // Datatables - locale aware sorting 65471239cb6SGreg Roach $.fn.dataTableExt.oSort['text-asc'] = function (x, y) { 655efd89170SGreg Roach return x.localeCompare(y, document.documentElement.lang, { sensitivity: 'base' }); 65671239cb6SGreg Roach }; 65771239cb6SGreg Roach $.fn.dataTableExt.oSort['text-desc'] = function (x, y) { 658efd89170SGreg Roach return y.localeCompare(x, document.documentElement.lang, { sensitivity: 'base' }); 65971239cb6SGreg Roach }; 66071239cb6SGreg Roach 66171239cb6SGreg Roach // DataTables - start hidden to prevent FOUC. 66271239cb6SGreg Roach $('table.datatables').each(function () { 6634843b94fSGreg Roach $(this).DataTable(); 6644843b94fSGreg Roach $(this).removeClass('d-none'); 6654843b94fSGreg Roach }); 6664843b94fSGreg Roach 6674843b94fSGreg Roach // Save button state between pages 668efd89170SGreg Roach document.querySelectorAll('[data-toggle=button][data-persist]').forEach((element) => { 6694843b94fSGreg Roach // Previously selected? 670efd89170SGreg Roach if (localStorage.getItem('state-of-' + element.dataset.persist) === 'T') { 6714843b94fSGreg Roach element.click(); 6724843b94fSGreg Roach } 6734843b94fSGreg Roach // Save state on change 674efd89170SGreg Roach element.addEventListener('click', (event) => { 6754843b94fSGreg Roach // Event occurs *before* the state changes, so reverse T/F. 676efd89170SGreg Roach localStorage.setItem('state-of-' + event.target.dataset.persist, event.target.classList.contains('active') ? 'F' : 'T'); 6774843b94fSGreg Roach }); 6784843b94fSGreg Roach }); 67971239cb6SGreg Roach 68071239cb6SGreg Roach // Activate the on-screen keyboard 6812cf1b3d7SGreg Roach let osk_focus_element; 68271239cb6SGreg Roach $('.wt-osk-trigger').click(function () { 68371239cb6SGreg Roach // When a user clicks the icon, set focus to the corresponding input 68471239cb6SGreg Roach osk_focus_element = document.getElementById($(this).data('id')); 68571239cb6SGreg Roach osk_focus_element.focus(); 68671239cb6SGreg Roach $('.wt-osk').show(); 68771239cb6SGreg Roach }); 68871239cb6SGreg Roach $('.wt-osk-script-button').change(function () { 68971239cb6SGreg Roach $('.wt-osk-script').prop('hidden', true); 69071239cb6SGreg Roach $('.wt-osk-script-' + $(this).data('script')).prop('hidden', false); 69171239cb6SGreg Roach }); 69271239cb6SGreg Roach $('.wt-osk-shift-button').click(function () { 69371239cb6SGreg Roach document.querySelector('.wt-osk-keys').classList.toggle('shifted'); 69471239cb6SGreg Roach }); 69571239cb6SGreg Roach $('.wt-osk-keys').on('click', '.wt-osk-key', function () { 6962cf1b3d7SGreg Roach let key = $(this).contents().get(0).nodeValue; 6972cf1b3d7SGreg Roach let shift_state = $('.wt-osk-shift-button').hasClass('active'); 6982cf1b3d7SGreg Roach let shift_key = $('sup', this)[0]; 69971239cb6SGreg Roach if (shift_state && shift_key !== undefined) { 70071239cb6SGreg Roach key = shift_key.innerText; 70171239cb6SGreg Roach } 7020d2905f7SGreg Roach webtrees.pasteAtCursor(osk_focus_element, key); 70371239cb6SGreg Roach if ($('.wt-osk-pin-button').hasClass('active') === false) { 70471239cb6SGreg Roach $('.wt-osk').hide(); 70571239cb6SGreg Roach } 70671239cb6SGreg Roach }); 70771239cb6SGreg Roach 70871239cb6SGreg Roach $('.wt-osk-close').on('click', function () { 70971239cb6SGreg Roach $('.wt-osk').hide(); 71071239cb6SGreg Roach }); 71171239cb6SGreg Roach}); 7127adfb8e5SGreg Roach 713b3a775f6SGreg Roach// Convert data-confirm and data-post-url attributes into useful behavior. 714efd89170SGreg Roachdocument.addEventListener('click', (event) => { 715a7a3d6dbSGreg Roach const target = event.target.closest('a,button'); 7167adfb8e5SGreg Roach 7177adfb8e5SGreg Roach if (target === null) { 7187adfb8e5SGreg Roach return; 7197adfb8e5SGreg Roach } 7207adfb8e5SGreg Roach 721efd89170SGreg Roach if ('confirm' in target.dataset && !confirm(target.dataset.confirm)) { 7227adfb8e5SGreg Roach event.preventDefault(); 7237adfb8e5SGreg Roach return; 7247adfb8e5SGreg Roach } 7257adfb8e5SGreg Roach 726efd89170SGreg Roach if ('postUrl' in target.dataset) { 727efd89170SGreg Roach const token = document.querySelector('meta[name=csrf]').content; 728ea101122SGreg Roach 729ea101122SGreg Roach fetch(target.dataset.postUrl, { 730ea101122SGreg Roach method: 'POST', 731ea101122SGreg Roach headers: { 732ea101122SGreg Roach 'X-CSRF-TOKEN': token, 733ea101122SGreg Roach 'X-Requested-with': 'XMLHttpRequest', 734ea101122SGreg Roach }, 7352cf1b3d7SGreg Roach }).then(() => { 736ea101122SGreg Roach if ('reloadUrl' in target.dataset) { 737ea101122SGreg Roach // Go somewhere else. e.g. home page after logout. 738ea101122SGreg Roach document.location = target.dataset.reloadUrl; 739ea101122SGreg Roach } else { 740ea101122SGreg Roach // Reload the current page. e.g. change language. 7417adfb8e5SGreg Roach document.location.reload(); 7427adfb8e5SGreg Roach } 743cb8f307bSGreg Roach }).catch((error) => { 744ea101122SGreg Roach alert(error); 745ea101122SGreg Roach }); 7467adfb8e5SGreg Roach } 7477adfb8e5SGreg Roach}); 748