xref: /webtrees/resources/js/webtrees.js (revision 71239cb694d278d044f33328daaa60c8ed7431e9)
1*71239cb6SGreg Roach/**
2*71239cb6SGreg Roach * webtrees: online genealogy
3*71239cb6SGreg Roach * Copyright (C) 2018 webtrees development team
4*71239cb6SGreg Roach * This program is free software: you can redistribute it and/or modify
5*71239cb6SGreg Roach * it under the terms of the GNU General Public License as published by
6*71239cb6SGreg Roach * the Free Software Foundation, either version 3 of the License, or
7*71239cb6SGreg Roach * (at your option) any later version.
8*71239cb6SGreg Roach * This program is distributed in the hope that it will be useful,
9*71239cb6SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
10*71239cb6SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11*71239cb6SGreg Roach * GNU General Public License for more details.
12*71239cb6SGreg Roach * You should have received a copy of the GNU General Public License
13*71239cb6SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>.
14*71239cb6SGreg Roach */
15*71239cb6SGreg Roach
16*71239cb6SGreg Roach'use strict';
17*71239cb6SGreg Roach
18*71239cb6SGreg Roachfunction expand_layer(sid)
19*71239cb6SGreg Roach{
20*71239cb6SGreg Roach    $('#' + sid + '_img').toggleClass('icon-plus icon-minus');
21*71239cb6SGreg Roach    $('#' + sid).slideToggle('fast');
22*71239cb6SGreg Roach    $('#' + sid + '-alt').toggle(); // hide something when we show the layer - and vice-versa
23*71239cb6SGreg Roach    return false;
24*71239cb6SGreg Roach}
25*71239cb6SGreg Roach
26*71239cb6SGreg Roach// Accept the changes to a record - and reload the page
27*71239cb6SGreg Roachfunction accept_changes(xref, ged)
28*71239cb6SGreg Roach{
29*71239cb6SGreg Roach    $.post(
30*71239cb6SGreg Roach        'index.php',
31*71239cb6SGreg Roach        {
32*71239cb6SGreg Roach            route: 'accept-changes',
33*71239cb6SGreg Roach            xref: xref,
34*71239cb6SGreg Roach            ged: ged,
35*71239cb6SGreg Roach        },
36*71239cb6SGreg Roach        function () {
37*71239cb6SGreg Roach            location.reload();
38*71239cb6SGreg Roach        }
39*71239cb6SGreg Roach    );
40*71239cb6SGreg Roach    return false;
41*71239cb6SGreg Roach}
42*71239cb6SGreg Roach
43*71239cb6SGreg Roach// Reject the changes to a record - and reload the page
44*71239cb6SGreg Roachfunction reject_changes(xref, ged)
45*71239cb6SGreg Roach{
46*71239cb6SGreg Roach    $.post(
47*71239cb6SGreg Roach        'index.php',
48*71239cb6SGreg Roach        {
49*71239cb6SGreg Roach            route: 'reject-changes',
50*71239cb6SGreg Roach            xref: xref,
51*71239cb6SGreg Roach            ged: ged,
52*71239cb6SGreg Roach        },
53*71239cb6SGreg Roach        function () {
54*71239cb6SGreg Roach            location.reload();
55*71239cb6SGreg Roach        }
56*71239cb6SGreg Roach    );
57*71239cb6SGreg Roach    return false;
58*71239cb6SGreg Roach}
59*71239cb6SGreg Roach
60*71239cb6SGreg Roach// Delete a record - and reload the page
61*71239cb6SGreg Roachfunction delete_record(xref, gedcom)
62*71239cb6SGreg Roach{
63*71239cb6SGreg Roach    $.post(
64*71239cb6SGreg Roach        'index.php',
65*71239cb6SGreg Roach        {
66*71239cb6SGreg Roach            route: 'delete-record',
67*71239cb6SGreg Roach            xref: xref,
68*71239cb6SGreg Roach            ged: gedcom,
69*71239cb6SGreg Roach        },
70*71239cb6SGreg Roach        function () {
71*71239cb6SGreg Roach            location.reload();
72*71239cb6SGreg Roach        }
73*71239cb6SGreg Roach    );
74*71239cb6SGreg Roach
75*71239cb6SGreg Roach    return false;
76*71239cb6SGreg Roach}
77*71239cb6SGreg Roach
78*71239cb6SGreg Roach// Delete a fact - and reload the page
79*71239cb6SGreg Roachfunction delete_fact(message, ged, xref, fact_id)
80*71239cb6SGreg Roach{
81*71239cb6SGreg Roach    if (confirm(message)) {
82*71239cb6SGreg Roach        $.post(
83*71239cb6SGreg Roach            'index.php',
84*71239cb6SGreg Roach            {
85*71239cb6SGreg Roach                route: 'delete-fact',
86*71239cb6SGreg Roach                xref: xref,
87*71239cb6SGreg Roach                fact_id: fact_id,
88*71239cb6SGreg Roach                ged: ged
89*71239cb6SGreg Roach            },
90*71239cb6SGreg Roach            function () {
91*71239cb6SGreg Roach                location.reload();
92*71239cb6SGreg Roach            }
93*71239cb6SGreg Roach        );
94*71239cb6SGreg Roach    }
95*71239cb6SGreg Roach    return false;
96*71239cb6SGreg Roach}
97*71239cb6SGreg Roach
98*71239cb6SGreg Roach// Copy a fact to the clipboard
99*71239cb6SGreg Roachfunction copy_fact(ged, xref, fact_id)
100*71239cb6SGreg Roach{
101*71239cb6SGreg Roach    $.post(
102*71239cb6SGreg Roach        'index.php',
103*71239cb6SGreg Roach        {
104*71239cb6SGreg Roach            route: 'copy-fact',
105*71239cb6SGreg Roach            xref: xref,
106*71239cb6SGreg Roach            fact_id: fact_id,
107*71239cb6SGreg Roach            ged: ged,
108*71239cb6SGreg Roach        },
109*71239cb6SGreg Roach        function () {
110*71239cb6SGreg Roach            location.reload();
111*71239cb6SGreg Roach        }
112*71239cb6SGreg Roach    );
113*71239cb6SGreg Roach    return false;
114*71239cb6SGreg Roach}
115*71239cb6SGreg Roach
116*71239cb6SGreg Roach// Paste a fact from the clipboard
117*71239cb6SGreg Roachfunction paste_fact(ged, xref, element)
118*71239cb6SGreg Roach{
119*71239cb6SGreg Roach    $.post(
120*71239cb6SGreg Roach        'index.php',
121*71239cb6SGreg Roach        {
122*71239cb6SGreg Roach            route: 'paste-fact',
123*71239cb6SGreg Roach            xref: xref,
124*71239cb6SGreg Roach            fact_id: $(element).val(), // element is the <select> containing the option
125*71239cb6SGreg Roach            ged: ged,
126*71239cb6SGreg Roach        },
127*71239cb6SGreg Roach        function () {
128*71239cb6SGreg Roach            location.reload();
129*71239cb6SGreg Roach        }
130*71239cb6SGreg Roach    );
131*71239cb6SGreg Roach    return false;
132*71239cb6SGreg Roach}
133*71239cb6SGreg Roach
134*71239cb6SGreg Roach// Delete a user - and reload the page
135*71239cb6SGreg Roachfunction delete_user(message, user_id)
136*71239cb6SGreg Roach{
137*71239cb6SGreg Roach    if (confirm(message)) {
138*71239cb6SGreg Roach        $.post(
139*71239cb6SGreg Roach            'index.php',
140*71239cb6SGreg Roach            {
141*71239cb6SGreg Roach                route: 'delete-user',
142*71239cb6SGreg Roach                user_id: user_id,
143*71239cb6SGreg Roach            },
144*71239cb6SGreg Roach            function () {
145*71239cb6SGreg Roach                location.reload();
146*71239cb6SGreg Roach            }
147*71239cb6SGreg Roach        );
148*71239cb6SGreg Roach    }
149*71239cb6SGreg Roach    return false;
150*71239cb6SGreg Roach}
151*71239cb6SGreg Roach
152*71239cb6SGreg Roach// Masquerade as another user - and reload the page.
153*71239cb6SGreg Roachfunction masquerade(user_id)
154*71239cb6SGreg Roach{
155*71239cb6SGreg Roach    $.post(
156*71239cb6SGreg Roach        'index.php',
157*71239cb6SGreg Roach        {
158*71239cb6SGreg Roach            route: 'masquerade',
159*71239cb6SGreg Roach            user_id: user_id,
160*71239cb6SGreg Roach        },
161*71239cb6SGreg Roach        function () {
162*71239cb6SGreg Roach            location.reload();
163*71239cb6SGreg Roach        }
164*71239cb6SGreg Roach    );
165*71239cb6SGreg Roach    return false;
166*71239cb6SGreg Roach}
167*71239cb6SGreg Roach
168*71239cb6SGreg Roachvar pastefield;
169*71239cb6SGreg Roachfunction addmedia_links(field, iid, iname)
170*71239cb6SGreg Roach{
171*71239cb6SGreg Roach    pastefield = field;
172*71239cb6SGreg Roach    insertRowToTable(iid, iname);
173*71239cb6SGreg Roach    return false;
174*71239cb6SGreg Roach}
175*71239cb6SGreg Roach
176*71239cb6SGreg Roachfunction valid_date(datefield, dmy)
177*71239cb6SGreg Roach{
178*71239cb6SGreg Roach    var months = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'];
179*71239cb6SGreg Roach    var hijri_months = ['MUHAR', 'SAFAR', 'RABIA', 'RABIT', 'JUMAA', 'JUMAT', 'RAJAB', 'SHAAB', 'RAMAD', 'SHAWW', 'DHUAQ', 'DHUAH'];
180*71239cb6SGreg Roach    var hebrew_months = ['TSH', 'CSH', 'KSL', 'TVT', 'SHV', 'ADR', 'ADS', 'NSN', 'IYR', 'SVN', 'TMZ', 'AAV', 'ELL'];
181*71239cb6SGreg Roach    var french_months = ['VEND', 'BRUM', 'FRIM', 'NIVO', 'PLUV', 'VENT', 'GERM', 'FLOR', 'PRAI', 'MESS', 'THER', 'FRUC', 'COMP'];
182*71239cb6SGreg Roach    var jalali_months = ['FARVA', 'ORDIB', 'KHORD', 'TIR', 'MORDA', 'SHAHR', 'MEHR', 'ABAN', 'AZAR', 'DEY', 'BAHMA', 'ESFAN'];
183*71239cb6SGreg Roach
184*71239cb6SGreg Roach    var datestr = datefield.value;
185*71239cb6SGreg Roach  // if a date has a date phrase marked by () this has to be excluded from altering
186*71239cb6SGreg Roach    var datearr = datestr.split('(');
187*71239cb6SGreg Roach    var datephrase = '';
188*71239cb6SGreg Roach    if (datearr.length > 1) {
189*71239cb6SGreg Roach        datestr = datearr[0];
190*71239cb6SGreg Roach        datephrase = datearr[1];
191*71239cb6SGreg Roach    }
192*71239cb6SGreg Roach
193*71239cb6SGreg Roach  // Gedcom dates are upper case
194*71239cb6SGreg Roach    datestr = datestr.toUpperCase();
195*71239cb6SGreg Roach  // Gedcom dates have no leading/trailing/repeated whitespace
196*71239cb6SGreg Roach    datestr = datestr.replace(/\s+/, ' ');
197*71239cb6SGreg Roach    datestr = datestr.replace(/(^\s)|(\s$)/, '');
198*71239cb6SGreg Roach  // Gedcom dates have spaces between letters and digits, e.g. "01JAN2000" => "01 JAN 2000"
199*71239cb6SGreg Roach    datestr = datestr.replace(/(\d)([A-Z])/, '$1 $2');
200*71239cb6SGreg Roach    datestr = datestr.replace(/([A-Z])(\d)/, '$1 $2');
201*71239cb6SGreg Roach
202*71239cb6SGreg Roach  // Shortcut for quarter format, "Q1 1900" => "BET JAN 1900 AND MAR 1900". See [ 1509083 ]
203*71239cb6SGreg Roach    if (datestr.match(/^Q ([1-4]) (\d\d\d\d)$/)) {
204*71239cb6SGreg Roach        datestr = 'BET ' + months[RegExp.$1 * 3 - 3] + ' ' + RegExp.$2 + ' AND ' + months[RegExp.$1 * 3 - 1] + ' ' + RegExp.$2;
205*71239cb6SGreg Roach    }
206*71239cb6SGreg Roach
207*71239cb6SGreg Roach  // Shortcut for @#Dxxxxx@ 01 01 1400, etc.
208*71239cb6SGreg Roach    if (datestr.match(/^(@#DHIJRI@|HIJRI)( \d?\d )(\d?\d)( \d?\d?\d?\d)$/)) {
209*71239cb6SGreg Roach        datestr = '@#DHIJRI@' + RegExp.$2 + hijri_months[parseInt(RegExp.$3, 10) - 1] + RegExp.$4;
210*71239cb6SGreg Roach    }
211*71239cb6SGreg Roach    if (datestr.match(/^(@#DJALALI@|JALALI)( \d?\d )(\d?\d)( \d?\d?\d?\d)$/)) {
212*71239cb6SGreg Roach        datestr = '@#DJALALI@' + RegExp.$2 + jalali_months[parseInt(RegExp.$3, 10) - 1] + RegExp.$4;
213*71239cb6SGreg Roach    }
214*71239cb6SGreg Roach    if (datestr.match(/^(@#DHEBREW@|HEBREW)( \d?\d )(\d?\d)( \d?\d?\d?\d)$/)) {
215*71239cb6SGreg Roach        datestr = '@#DHEBREW@' + RegExp.$2 + hebrew_months[parseInt(RegExp.$3, 10) - 1] + RegExp.$4;
216*71239cb6SGreg Roach    }
217*71239cb6SGreg Roach    if (datestr.match(/^(@#DFRENCH R@|FRENCH)( \d?\d )(\d?\d)( \d?\d?\d?\d)$/)) {
218*71239cb6SGreg Roach        datestr = '@#DFRENCH R@' + RegExp.$2 + french_months[parseInt(RegExp.$3, 10) - 1] + RegExp.$4;
219*71239cb6SGreg Roach    }
220*71239cb6SGreg Roach
221*71239cb6SGreg Roach  // e.g. 17.11.1860, 03/04/2005 or 1999-12-31. Use locale settings where DMY order is ambiguous.
222*71239cb6SGreg Roach    var qsearch = /^([^\d]*)(\d+)[^\d](\d+)[^\d](\d+)$/i;
223*71239cb6SGreg Roach    if (qsearch.exec(datestr)) {
224*71239cb6SGreg Roach        var f0 = RegExp.$1;
225*71239cb6SGreg Roach        var f1 = parseInt(RegExp.$2, 10);
226*71239cb6SGreg Roach        var f2 = parseInt(RegExp.$3, 10);
227*71239cb6SGreg Roach        var f3 = parseInt(RegExp.$4, 10);
228*71239cb6SGreg Roach        var yyyy = new Date().getFullYear();
229*71239cb6SGreg Roach        var yy = yyyy % 100;
230*71239cb6SGreg Roach        var cc = yyyy - yy;
231*71239cb6SGreg Roach        if (dmy === 'DMY' && f1 <= 31 && f2 <= 12 || f1 > 13 && f1 <= 31 && f2 <= 12 && f3 > 31) {
232*71239cb6SGreg Roach            datestr = f0 + f1 + ' ' + months[f2 - 1] + ' ' + (f3 >= 100 ? f3 : (f3 <= yy ? f3 + cc : f3 + cc - 100));
233*71239cb6SGreg Roach        } else {
234*71239cb6SGreg Roach            if (dmy === 'MDY' && f1 <= 12 && f2 <= 31 || f2 > 13 && f2 <= 31 && f1 <= 12 && f3 > 31) {
235*71239cb6SGreg Roach                datestr = f0 + f2 + ' ' + months[f1 - 1] + ' ' + (f3 >= 100 ? f3 : (f3 <= yy ? f3 + cc : f3 + cc - 100));
236*71239cb6SGreg Roach            } else {
237*71239cb6SGreg Roach                if (dmy === 'YMD' && f2 <= 12 && f3 <= 31 || f3 > 13 && f3 <= 31 && f2 <= 12 && f1 > 31) {
238*71239cb6SGreg Roach                    datestr = f0 + f3 + ' ' + months[f2 - 1] + ' ' + (f1 >= 100 ? f1 : (f1 <= yy ? f1 + cc : f1 + cc - 100));
239*71239cb6SGreg Roach                }
240*71239cb6SGreg Roach            }
241*71239cb6SGreg Roach        }
242*71239cb6SGreg Roach    }
243*71239cb6SGreg Roach
244*71239cb6SGreg Roach  // Shortcuts for date ranges
245*71239cb6SGreg Roach    datestr = datestr.replace(/^[>]([\w ]+)$/, 'AFT $1');
246*71239cb6SGreg Roach    datestr = datestr.replace(/^[<]([\w ]+)$/, 'BEF $1');
247*71239cb6SGreg Roach    datestr = datestr.replace(/^([\w ]+)[-]$/, 'FROM $1');
248*71239cb6SGreg Roach    datestr = datestr.replace(/^[-]([\w ]+)$/, 'TO $1');
249*71239cb6SGreg Roach    datestr = datestr.replace(/^[~]([\w ]+)$/, 'ABT $1');
250*71239cb6SGreg Roach    datestr = datestr.replace(/^[*]([\w ]+)$/, 'EST $1');
251*71239cb6SGreg Roach    datestr = datestr.replace(/^[#]([\w ]+)$/, 'CAL $1');
252*71239cb6SGreg Roach    datestr = datestr.replace(/^([\w ]+) ?- ?([\w ]+)$/, 'BET $1 AND $2');
253*71239cb6SGreg Roach    datestr = datestr.replace(/^([\w ]+) ?~ ?([\w ]+)$/, 'FROM $1 TO $2');
254*71239cb6SGreg Roach
255*71239cb6SGreg Roach  // Convert full months to short months
256*71239cb6SGreg Roach    datestr = datestr.replace(/(JANUARY)/, 'JAN');
257*71239cb6SGreg Roach    datestr = datestr.replace(/(FEBRUARY)/, 'FEB');
258*71239cb6SGreg Roach    datestr = datestr.replace(/(MARCH)/, 'MAR');
259*71239cb6SGreg Roach    datestr = datestr.replace(/(APRIL)/, 'APR');
260*71239cb6SGreg Roach    datestr = datestr.replace(/(MAY)/, 'MAY');
261*71239cb6SGreg Roach    datestr = datestr.replace(/(JUNE)/, 'JUN');
262*71239cb6SGreg Roach    datestr = datestr.replace(/(JULY)/, 'JUL');
263*71239cb6SGreg Roach    datestr = datestr.replace(/(AUGUST)/, 'AUG');
264*71239cb6SGreg Roach    datestr = datestr.replace(/(SEPTEMBER)/, 'SEP');
265*71239cb6SGreg Roach    datestr = datestr.replace(/(OCTOBER)/, 'OCT');
266*71239cb6SGreg Roach    datestr = datestr.replace(/(NOVEMBER)/, 'NOV');
267*71239cb6SGreg Roach    datestr = datestr.replace(/(DECEMBER)/, 'DEC');
268*71239cb6SGreg Roach
269*71239cb6SGreg Roach  // Americans frequently enter dates as SEP 20, 1999
270*71239cb6SGreg Roach  // No need to internationalise this, as this is an english-language issue
271*71239cb6SGreg Roach    datestr = datestr.replace(/(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)\.? (\d\d?)[, ]+(\d\d\d\d)/, '$2 $1 $3');
272*71239cb6SGreg Roach
273*71239cb6SGreg Roach  // Apply leading zero to day numbers
274*71239cb6SGreg Roach    datestr = datestr.replace(/(^| )(\d [A-Z]{3,5} \d{4})/, '$10$2');
275*71239cb6SGreg Roach
276*71239cb6SGreg Roach    if (datephrase) {
277*71239cb6SGreg Roach        datestr = datestr + ' (' + datephrase;
278*71239cb6SGreg Roach    }
279*71239cb6SGreg Roach  // Only update it if is has been corrected - otherwise input focus
280*71239cb6SGreg Roach  // moves to the end of the field unnecessarily
281*71239cb6SGreg Roach    if (datefield.value !== datestr) {
282*71239cb6SGreg Roach        datefield.value = datestr;
283*71239cb6SGreg Roach    }
284*71239cb6SGreg Roach}
285*71239cb6SGreg Roach
286*71239cb6SGreg Roachvar menutimeouts = [];
287*71239cb6SGreg Roach
288*71239cb6SGreg Roachfunction show_submenu(elementid, parentid)
289*71239cb6SGreg Roach{
290*71239cb6SGreg Roach    var pagewidth = document.body.scrollWidth + document.documentElement.scrollLeft;
291*71239cb6SGreg Roach    var element = document.getElementById(elementid);
292*71239cb6SGreg Roach
293*71239cb6SGreg Roach    if (element && element.style) {
294*71239cb6SGreg Roach        if (document.all) {
295*71239cb6SGreg Roach            pagewidth = document.body.offsetWidth;
296*71239cb6SGreg Roach        } else {
297*71239cb6SGreg Roach            pagewidth = document.body.scrollWidth + document.documentElement.scrollLeft - 55;
298*71239cb6SGreg Roach            if (document.documentElement.dir === 'rtl') {
299*71239cb6SGreg Roach                boxright = element.offsetLeft + element.offsetWidth + 10;
300*71239cb6SGreg Roach            }
301*71239cb6SGreg Roach        }
302*71239cb6SGreg Roach
303*71239cb6SGreg Roach      // -- make sure the submenu is the size of the largest child
304*71239cb6SGreg Roach        var maxwidth = 0;
305*71239cb6SGreg Roach        var count = element.childNodes.length;
306*71239cb6SGreg Roach        for (var i = 0; i < count; i++) {
307*71239cb6SGreg Roach            var child = element.childNodes[i];
308*71239cb6SGreg Roach            if (child.offsetWidth > maxwidth + 5) {
309*71239cb6SGreg Roach                maxwidth = child.offsetWidth;
310*71239cb6SGreg Roach            }
311*71239cb6SGreg Roach        }
312*71239cb6SGreg Roach        if (element.offsetWidth < maxwidth) {
313*71239cb6SGreg Roach            element.style.width = maxwidth + 'px';
314*71239cb6SGreg Roach        }
315*71239cb6SGreg Roach        var pelement, boxright;
316*71239cb6SGreg Roach        pelement = document.getElementById(parentid);
317*71239cb6SGreg Roach        if (pelement) {
318*71239cb6SGreg Roach            element.style.left = pelement.style.left;
319*71239cb6SGreg Roach            boxright = element.offsetLeft + element.offsetWidth + 10;
320*71239cb6SGreg Roach            if (boxright > pagewidth) {
321*71239cb6SGreg Roach                var menuleft = pagewidth - element.offsetWidth;
322*71239cb6SGreg Roach                element.style.left = menuleft + 'px';
323*71239cb6SGreg Roach            }
324*71239cb6SGreg Roach        }
325*71239cb6SGreg Roach
326*71239cb6SGreg Roach        if (element.offsetLeft < 0) {
327*71239cb6SGreg Roach            element.style.left = '0px';
328*71239cb6SGreg Roach        }
329*71239cb6SGreg Roach
330*71239cb6SGreg Roach      // -- put scrollbars on really long menus
331*71239cb6SGreg Roach        if (element.offsetHeight > 500) {
332*71239cb6SGreg Roach            element.style.height = '400px';
333*71239cb6SGreg Roach            element.style.overflow = 'auto';
334*71239cb6SGreg Roach        }
335*71239cb6SGreg Roach
336*71239cb6SGreg Roach        element.style.visibility = 'visible';
337*71239cb6SGreg Roach    }
338*71239cb6SGreg Roach    clearTimeout(menutimeouts[elementid]);
339*71239cb6SGreg Roach    menutimeouts[elementid] = null;
340*71239cb6SGreg Roach}
341*71239cb6SGreg Roach
342*71239cb6SGreg Roachfunction hide_submenu(elementid)
343*71239cb6SGreg Roach{
344*71239cb6SGreg Roach    if (typeof menutimeouts[elementid] !== 'number') {
345*71239cb6SGreg Roach        return;
346*71239cb6SGreg Roach    }
347*71239cb6SGreg Roach    var element = document.getElementById(elementid);
348*71239cb6SGreg Roach    if (element && element.style) {
349*71239cb6SGreg Roach        element.style.visibility = 'hidden';
350*71239cb6SGreg Roach    }
351*71239cb6SGreg Roach    clearTimeout(menutimeouts[elementid]);
352*71239cb6SGreg Roach    menutimeouts[elementid] = null;
353*71239cb6SGreg Roach}
354*71239cb6SGreg Roach
355*71239cb6SGreg Roachfunction timeout_submenu(elementid)
356*71239cb6SGreg Roach{
357*71239cb6SGreg Roach    if (typeof menutimeouts[elementid] !== 'number') {
358*71239cb6SGreg Roach        menutimeouts[elementid] = setTimeout("hide_submenu('" + elementid + "')", 100);
359*71239cb6SGreg Roach    }
360*71239cb6SGreg Roach}
361*71239cb6SGreg Roach
362*71239cb6SGreg Roachvar monthLabels = [];
363*71239cb6SGreg RoachmonthLabels[1] = 'January';
364*71239cb6SGreg RoachmonthLabels[2] = 'February';
365*71239cb6SGreg RoachmonthLabels[3] = 'March';
366*71239cb6SGreg RoachmonthLabels[4] = 'April';
367*71239cb6SGreg RoachmonthLabels[5] = 'May';
368*71239cb6SGreg RoachmonthLabels[6] = 'June';
369*71239cb6SGreg RoachmonthLabels[7] = 'July';
370*71239cb6SGreg RoachmonthLabels[8] = 'August';
371*71239cb6SGreg RoachmonthLabels[9] = 'September';
372*71239cb6SGreg RoachmonthLabels[10] = 'October';
373*71239cb6SGreg RoachmonthLabels[11] = 'November';
374*71239cb6SGreg RoachmonthLabels[12] = 'December';
375*71239cb6SGreg Roach
376*71239cb6SGreg Roachvar monthShort = [];
377*71239cb6SGreg RoachmonthShort[1] = 'JAN';
378*71239cb6SGreg RoachmonthShort[2] = 'FEB';
379*71239cb6SGreg RoachmonthShort[3] = 'MAR';
380*71239cb6SGreg RoachmonthShort[4] = 'APR';
381*71239cb6SGreg RoachmonthShort[5] = 'MAY';
382*71239cb6SGreg RoachmonthShort[6] = 'JUN';
383*71239cb6SGreg RoachmonthShort[7] = 'JUL';
384*71239cb6SGreg RoachmonthShort[8] = 'AUG';
385*71239cb6SGreg RoachmonthShort[9] = 'SEP';
386*71239cb6SGreg RoachmonthShort[10] = 'OCT';
387*71239cb6SGreg RoachmonthShort[11] = 'NOV';
388*71239cb6SGreg RoachmonthShort[12] = 'DEC';
389*71239cb6SGreg Roach
390*71239cb6SGreg Roachvar daysOfWeek = [];
391*71239cb6SGreg RoachdaysOfWeek[0] = 'S';
392*71239cb6SGreg RoachdaysOfWeek[1] = 'M';
393*71239cb6SGreg RoachdaysOfWeek[2] = 'T';
394*71239cb6SGreg RoachdaysOfWeek[3] = 'W';
395*71239cb6SGreg RoachdaysOfWeek[4] = 'T';
396*71239cb6SGreg RoachdaysOfWeek[5] = 'F';
397*71239cb6SGreg RoachdaysOfWeek[6] = 'S';
398*71239cb6SGreg Roach
399*71239cb6SGreg Roachvar weekStart = 0;
400*71239cb6SGreg Roach
401*71239cb6SGreg Roachfunction cal_setMonthNames(jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec)
402*71239cb6SGreg Roach{
403*71239cb6SGreg Roach    monthLabels[1] = jan;
404*71239cb6SGreg Roach    monthLabels[2] = feb;
405*71239cb6SGreg Roach    monthLabels[3] = mar;
406*71239cb6SGreg Roach    monthLabels[4] = apr;
407*71239cb6SGreg Roach    monthLabels[5] = may;
408*71239cb6SGreg Roach    monthLabels[6] = jun;
409*71239cb6SGreg Roach    monthLabels[7] = jul;
410*71239cb6SGreg Roach    monthLabels[8] = aug;
411*71239cb6SGreg Roach    monthLabels[9] = sep;
412*71239cb6SGreg Roach    monthLabels[10] = oct;
413*71239cb6SGreg Roach    monthLabels[11] = nov;
414*71239cb6SGreg Roach    monthLabels[12] = dec;
415*71239cb6SGreg Roach}
416*71239cb6SGreg Roach
417*71239cb6SGreg Roachfunction cal_setDayHeaders(sun, mon, tue, wed, thu, fri, sat)
418*71239cb6SGreg Roach{
419*71239cb6SGreg Roach    daysOfWeek[0] = sun;
420*71239cb6SGreg Roach    daysOfWeek[1] = mon;
421*71239cb6SGreg Roach    daysOfWeek[2] = tue;
422*71239cb6SGreg Roach    daysOfWeek[3] = wed;
423*71239cb6SGreg Roach    daysOfWeek[4] = thu;
424*71239cb6SGreg Roach    daysOfWeek[5] = fri;
425*71239cb6SGreg Roach    daysOfWeek[6] = sat;
426*71239cb6SGreg Roach}
427*71239cb6SGreg Roach
428*71239cb6SGreg Roachfunction cal_setWeekStart(day)
429*71239cb6SGreg Roach{
430*71239cb6SGreg Roach    if (day >= 0 && day < 7) {
431*71239cb6SGreg Roach        weekStart = day;
432*71239cb6SGreg Roach    }
433*71239cb6SGreg Roach}
434*71239cb6SGreg Roach
435*71239cb6SGreg Roachfunction calendarWidget(dateDivId, dateFieldId)
436*71239cb6SGreg Roach{
437*71239cb6SGreg Roach    var dateDiv = document.getElementById(dateDivId);
438*71239cb6SGreg Roach    var dateField = document.getElementById(dateFieldId);
439*71239cb6SGreg Roach
440*71239cb6SGreg Roach    if (dateDiv.style.visibility === 'visible') {
441*71239cb6SGreg Roach        dateDiv.style.visibility = 'hidden';
442*71239cb6SGreg Roach        return false;
443*71239cb6SGreg Roach    }
444*71239cb6SGreg Roach    if (dateDiv.style.visibility === 'show') {
445*71239cb6SGreg Roach        dateDiv.style.visibility = 'hide';
446*71239cb6SGreg Roach        return false;
447*71239cb6SGreg Roach    }
448*71239cb6SGreg Roach
449*71239cb6SGreg Roach  /* Javascript calendar functions only work with precise gregorian dates "D M Y" or "Y" */
450*71239cb6SGreg Roach    var greg_regex = /((\d+ (JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC) )?\d+)/i;
451*71239cb6SGreg Roach    var date;
452*71239cb6SGreg Roach    if (greg_regex.exec(dateField.value)) {
453*71239cb6SGreg Roach        date = new Date(RegExp.$1);
454*71239cb6SGreg Roach    } else {
455*71239cb6SGreg Roach        date = new Date();
456*71239cb6SGreg Roach    }
457*71239cb6SGreg Roach
458*71239cb6SGreg Roach    dateDiv.innerHTML = cal_generateSelectorContent(dateFieldId, dateDivId, date);
459*71239cb6SGreg Roach    if (dateDiv.style.visibility === 'hidden') {
460*71239cb6SGreg Roach        dateDiv.style.visibility = 'visible';
461*71239cb6SGreg Roach        return false;
462*71239cb6SGreg Roach    }
463*71239cb6SGreg Roach    if (dateDiv.style.visibility === 'hide') {
464*71239cb6SGreg Roach        dateDiv.style.visibility = 'show';
465*71239cb6SGreg Roach        return false;
466*71239cb6SGreg Roach    }
467*71239cb6SGreg Roach
468*71239cb6SGreg Roach    return false;
469*71239cb6SGreg Roach}
470*71239cb6SGreg Roach
471*71239cb6SGreg Roachfunction cal_generateSelectorContent(dateFieldId, dateDivId, date)
472*71239cb6SGreg Roach{
473*71239cb6SGreg Roach    var i, j;
474*71239cb6SGreg Roach    var content = '<table border="1"><tr>';
475*71239cb6SGreg Roach    content += '<td><select class="form-control" id="' + dateFieldId + '_daySelect" onchange="return cal_updateCalendar(\'' + dateFieldId + '\', \'' + dateDivId + '\');">';
476*71239cb6SGreg Roach    for (i = 1; i < 32; i++) {
477*71239cb6SGreg Roach        content += '<option value="' + i + '"';
478*71239cb6SGreg Roach        if (date.getDate() === i) {
479*71239cb6SGreg Roach            content += ' selected="selected"';
480*71239cb6SGreg Roach        }
481*71239cb6SGreg Roach        content += '>' + i + '</option>';
482*71239cb6SGreg Roach    }
483*71239cb6SGreg Roach    content += '</select></td>';
484*71239cb6SGreg Roach    content += '<td><select class="form-control" id="' + dateFieldId + '_monSelect" onchange="return cal_updateCalendar(\'' + dateFieldId + '\', \'' + dateDivId + '\');">';
485*71239cb6SGreg Roach    for (i = 1; i < 13; i++) {
486*71239cb6SGreg Roach        content += '<option value="' + i + '"';
487*71239cb6SGreg Roach        if (date.getMonth() + 1 === i) {
488*71239cb6SGreg Roach            content += ' selected="selected"';
489*71239cb6SGreg Roach        }
490*71239cb6SGreg Roach        content += '>' + monthLabels[i] + '</option>';
491*71239cb6SGreg Roach    }
492*71239cb6SGreg Roach    content += '</select></td>';
493*71239cb6SGreg Roach    content += '<td><input class="form-control" type="text" id="' + dateFieldId + '_yearInput" size="5" value="' + date.getFullYear() + '" onchange="return cal_updateCalendar(\'' + dateFieldId + '\', \'' + dateDivId + '\');" /></td></tr>';
494*71239cb6SGreg Roach    content += '<tr><td colspan="3">';
495*71239cb6SGreg Roach    content += '<table width="100%">';
496*71239cb6SGreg Roach    content += '<tr>';
497*71239cb6SGreg Roach    j = weekStart;
498*71239cb6SGreg Roach    for (i = 0; i < 7; i++) {
499*71239cb6SGreg Roach        content += '<td ';
500*71239cb6SGreg Roach        content += 'class="descriptionbox"';
501*71239cb6SGreg Roach        content += '>';
502*71239cb6SGreg Roach        content += daysOfWeek[j];
503*71239cb6SGreg Roach        content += '</td>';
504*71239cb6SGreg Roach        j++;
505*71239cb6SGreg Roach        if (j > 6) {
506*71239cb6SGreg Roach            j = 0;
507*71239cb6SGreg Roach        }
508*71239cb6SGreg Roach    }
509*71239cb6SGreg Roach    content += '</tr>';
510*71239cb6SGreg Roach
511*71239cb6SGreg Roach    var tdate = new Date(date.getFullYear(), date.getMonth(), 1);
512*71239cb6SGreg Roach    var day = tdate.getDay();
513*71239cb6SGreg Roach    day = day - weekStart;
514*71239cb6SGreg Roach    var daymilli = 1000 * 60 * 60 * 24;
515*71239cb6SGreg Roach    tdate = tdate.getTime() - (day * daymilli) + (daymilli / 2);
516*71239cb6SGreg Roach    tdate = new Date(tdate);
517*71239cb6SGreg Roach
518*71239cb6SGreg Roach    for (j = 0; j < 6; j++) {
519*71239cb6SGreg Roach        content += '<tr>';
520*71239cb6SGreg Roach        for (i = 0; i < 7; i++) {
521*71239cb6SGreg Roach            content += '<td ';
522*71239cb6SGreg Roach            if (tdate.getMonth() === date.getMonth()) {
523*71239cb6SGreg Roach                if (tdate.getDate() === date.getDate()) {
524*71239cb6SGreg Roach                    content += 'class="descriptionbox"';
525*71239cb6SGreg Roach                } else {
526*71239cb6SGreg Roach                    content += 'class="optionbox"';
527*71239cb6SGreg Roach                }
528*71239cb6SGreg Roach            } else {
529*71239cb6SGreg Roach                content += 'style="background-color:#EAEAEA; border: solid #AAAAAA 1px;"';
530*71239cb6SGreg Roach            }
531*71239cb6SGreg Roach            content += '><a href="#" onclick="return cal_dateClicked(\'' + dateFieldId + '\', \'' + dateDivId + '\', ' + tdate.getFullYear() + ', ' + tdate.getMonth() + ', ' + tdate.getDate() + ');">';
532*71239cb6SGreg Roach            content += tdate.getDate();
533*71239cb6SGreg Roach            content += '</a></td>';
534*71239cb6SGreg Roach            var datemilli = tdate.getTime() + daymilli;
535*71239cb6SGreg Roach            tdate = new Date(datemilli);
536*71239cb6SGreg Roach        }
537*71239cb6SGreg Roach        content += '</tr>';
538*71239cb6SGreg Roach    }
539*71239cb6SGreg Roach    content += '</table>';
540*71239cb6SGreg Roach    content += '</td></tr>';
541*71239cb6SGreg Roach    content += '</table>';
542*71239cb6SGreg Roach
543*71239cb6SGreg Roach    return content;
544*71239cb6SGreg Roach}
545*71239cb6SGreg Roach
546*71239cb6SGreg Roachfunction cal_setDateField(dateFieldId, year, month, day)
547*71239cb6SGreg Roach{
548*71239cb6SGreg Roach    var dateField = document.getElementById(dateFieldId);
549*71239cb6SGreg Roach    if (!dateField) {
550*71239cb6SGreg Roach        return false;
551*71239cb6SGreg Roach    }
552*71239cb6SGreg Roach    if (day < 10) {
553*71239cb6SGreg Roach        day = '0' + day;
554*71239cb6SGreg Roach    }
555*71239cb6SGreg Roach    dateField.value = day + ' ' + monthShort[month + 1] + ' ' + year;
556*71239cb6SGreg Roach    return false;
557*71239cb6SGreg Roach}
558*71239cb6SGreg Roach
559*71239cb6SGreg Roachfunction cal_updateCalendar(dateFieldId, dateDivId)
560*71239cb6SGreg Roach{
561*71239cb6SGreg Roach    var dateSel = document.getElementById(dateFieldId + '_daySelect');
562*71239cb6SGreg Roach    if (!dateSel) {
563*71239cb6SGreg Roach        return false;
564*71239cb6SGreg Roach    }
565*71239cb6SGreg Roach    var monthSel = document.getElementById(dateFieldId + '_monSelect');
566*71239cb6SGreg Roach    if (!monthSel) {
567*71239cb6SGreg Roach        return false;
568*71239cb6SGreg Roach    }
569*71239cb6SGreg Roach    var yearInput = document.getElementById(dateFieldId + '_yearInput');
570*71239cb6SGreg Roach    if (!yearInput) {
571*71239cb6SGreg Roach        return false;
572*71239cb6SGreg Roach    }
573*71239cb6SGreg Roach
574*71239cb6SGreg Roach    var month = parseInt(monthSel.options[monthSel.selectedIndex].value, 10);
575*71239cb6SGreg Roach    month = month - 1;
576*71239cb6SGreg Roach
577*71239cb6SGreg Roach    var date = new Date(yearInput.value, month, dateSel.options[dateSel.selectedIndex].value);
578*71239cb6SGreg Roach    cal_setDateField(dateFieldId, date.getFullYear(), date.getMonth(), date.getDate());
579*71239cb6SGreg Roach
580*71239cb6SGreg Roach    var dateDiv = document.getElementById(dateDivId);
581*71239cb6SGreg Roach    if (!dateDiv) {
582*71239cb6SGreg Roach        alert('no dateDiv ' + dateDivId);
583*71239cb6SGreg Roach        return false;
584*71239cb6SGreg Roach    }
585*71239cb6SGreg Roach    dateDiv.innerHTML = cal_generateSelectorContent(dateFieldId, dateDivId, date);
586*71239cb6SGreg Roach
587*71239cb6SGreg Roach    return false;
588*71239cb6SGreg Roach}
589*71239cb6SGreg Roach
590*71239cb6SGreg Roachfunction cal_dateClicked(dateFieldId, dateDivId, year, month, day)
591*71239cb6SGreg Roach{
592*71239cb6SGreg Roach    cal_setDateField(dateFieldId, year, month, day);
593*71239cb6SGreg Roach    calendarWidget(dateDivId, dateFieldId);
594*71239cb6SGreg Roach    return false;
595*71239cb6SGreg Roach}
596*71239cb6SGreg Roach
597*71239cb6SGreg Roachfunction openerpasteid(id)
598*71239cb6SGreg Roach{
599*71239cb6SGreg Roach    if (window.opener.paste_id) {
600*71239cb6SGreg Roach        window.opener.paste_id(id);
601*71239cb6SGreg Roach    }
602*71239cb6SGreg Roach    window.close();
603*71239cb6SGreg Roach}
604*71239cb6SGreg Roach
605*71239cb6SGreg Roachfunction paste_id(value)
606*71239cb6SGreg Roach{
607*71239cb6SGreg Roach    pastefield.value = value;
608*71239cb6SGreg Roach}
609*71239cb6SGreg Roach
610*71239cb6SGreg Roachfunction pastename(name)
611*71239cb6SGreg Roach{
612*71239cb6SGreg Roach    if (nameElement) {
613*71239cb6SGreg Roach        nameElement.innerHTML = name;
614*71239cb6SGreg Roach    }
615*71239cb6SGreg Roach    if (remElement) {
616*71239cb6SGreg Roach        remElement.style.display = 'block';
617*71239cb6SGreg Roach    }
618*71239cb6SGreg Roach}
619*71239cb6SGreg Roach
620*71239cb6SGreg Roachfunction paste_char(value)
621*71239cb6SGreg Roach{
622*71239cb6SGreg Roach    if (document.selection) {
623*71239cb6SGreg Roach      // IE
624*71239cb6SGreg Roach        pastefield.focus();
625*71239cb6SGreg Roach        document.selection.createRange().text = value;
626*71239cb6SGreg Roach    } else if (pastefield.selectionStart || pastefield.selectionStart === 0) {
627*71239cb6SGreg Roach      // Mozilla/Chrome/Safari
628*71239cb6SGreg Roach        pastefield.value =
629*71239cb6SGreg Roach        pastefield.value.substring(0, pastefield.selectionStart) +
630*71239cb6SGreg Roach        value +
631*71239cb6SGreg Roach        pastefield.value.substring(pastefield.selectionEnd, pastefield.value.length);
632*71239cb6SGreg Roach        pastefield.selectionStart = pastefield.selectionEnd = pastefield.selectionStart + value.length;
633*71239cb6SGreg Roach    } else {
634*71239cb6SGreg Roach      // Fallback? - just append
635*71239cb6SGreg Roach        pastefield.value += value;
636*71239cb6SGreg Roach    }
637*71239cb6SGreg Roach
638*71239cb6SGreg Roach    if (pastefield.id === 'NPFX' || pastefield.id === 'GIVN' || pastefield.id === 'SPFX' || pastefield.id === 'SURN' || pastefield.id === 'NSFX') {
639*71239cb6SGreg Roach        updatewholename();
640*71239cb6SGreg Roach    }
641*71239cb6SGreg Roach}
642*71239cb6SGreg Roach
643*71239cb6SGreg Roach/**
644*71239cb6SGreg Roach * Persistant checkbox options to hide/show extra data.
645*71239cb6SGreg Roach
646*71239cb6SGreg Roach * @param checkbox_id
647*71239cb6SGreg Roach * @param data_selector
648*71239cb6SGreg Roach */
649*71239cb6SGreg Roachfunction persistent_toggle(checkbox_id, data_selector)
650*71239cb6SGreg Roach{
651*71239cb6SGreg Roach    var checkbox = document.getElementById(checkbox_id);
652*71239cb6SGreg Roach    var elements = document.querySelectorAll(data_selector);
653*71239cb6SGreg Roach    var display = localStorage.getItem(checkbox_id);
654*71239cb6SGreg Roach
655*71239cb6SGreg Roach    if (!checkbox) {
656*71239cb6SGreg Roach        return;
657*71239cb6SGreg Roach    }
658*71239cb6SGreg Roach
659*71239cb6SGreg Roach    if (display !== '') {
660*71239cb6SGreg Roach        display = 'none';
661*71239cb6SGreg Roach    }
662*71239cb6SGreg Roach
663*71239cb6SGreg Roach    checkbox.checked = (display === '');
664*71239cb6SGreg Roach    for (var i = 0; i < elements.length; ++i) {
665*71239cb6SGreg Roach        elements[i].style.display = display;
666*71239cb6SGreg Roach    }
667*71239cb6SGreg Roach
668*71239cb6SGreg Roach    checkbox.addEventListener('click', function () {
669*71239cb6SGreg Roach        console.log(display);
670*71239cb6SGreg Roach        display = (display === '' ? 'none' : '');
671*71239cb6SGreg Roach        localStorage.setItem(checkbox_id, display);
672*71239cb6SGreg Roach        for (var i = 0; i < elements.length; ++i) {
673*71239cb6SGreg Roach            elements[i].style.display = display;
674*71239cb6SGreg Roach        }
675*71239cb6SGreg Roach    });
676*71239cb6SGreg Roach}
677*71239cb6SGreg Roach
678*71239cb6SGreg Roachfunction valid_lati_long(field, pos, neg)
679*71239cb6SGreg Roach{
680*71239cb6SGreg Roach  // valid LATI or LONG according to Gedcom standard
681*71239cb6SGreg Roach  // pos (+) : N or E
682*71239cb6SGreg Roach  // neg (-) : S or W
683*71239cb6SGreg Roach    var txt = field.value.toUpperCase();
684*71239cb6SGreg Roach    txt = txt.replace(/(^\s*)|(\s*$)/g, ''); // trim
685*71239cb6SGreg Roach    txt = txt.replace(/ /g, ':'); // N12 34 ==> N12.34
686*71239cb6SGreg Roach    txt = txt.replace(/\+/g, ''); // +17.1234 ==> 17.1234
687*71239cb6SGreg Roach    txt = txt.replace(/-/g, neg); // -0.5698 ==> W0.5698
688*71239cb6SGreg Roach    txt = txt.replace(/,/g, '.'); // 0,5698 ==> 0.5698
689*71239cb6SGreg Roach  // 0°34'11 ==> 0:34:11
690*71239cb6SGreg Roach    txt = txt.replace(/\u00b0/g, ':'); // °
691*71239cb6SGreg Roach    txt = txt.replace(/\u0027/g, ':'); // '
692*71239cb6SGreg Roach  // 0:34:11.2W ==> W0.5698
693*71239cb6SGreg Roach    txt = txt.replace(/^([0-9]+):([0-9]+):([0-9.]+)(.*)/g, function ($0, $1, $2, $3, $4) {
694*71239cb6SGreg Roach        var n = parseFloat($1);
695*71239cb6SGreg Roach        n += ($2 / 60);
696*71239cb6SGreg Roach        n += ($3 / 3600);
697*71239cb6SGreg Roach        n = Math.round(n * 1E4) / 1E4;
698*71239cb6SGreg Roach        return $4 + n;
699*71239cb6SGreg Roach    });
700*71239cb6SGreg Roach  // 0:34W ==> W0.5667
701*71239cb6SGreg Roach    txt = txt.replace(/^([0-9]+):([0-9]+)(.*)/g, function ($0, $1, $2, $3) {
702*71239cb6SGreg Roach        var n = parseFloat($1);
703*71239cb6SGreg Roach        n += ($2 / 60);
704*71239cb6SGreg Roach        n = Math.round(n * 1E4) / 1E4;
705*71239cb6SGreg Roach        return $3 + n;
706*71239cb6SGreg Roach    });
707*71239cb6SGreg Roach  // 0.5698W ==> W0.5698
708*71239cb6SGreg Roach    txt = txt.replace(/(.*)([N|S|E|W]+)$/g, '$2$1');
709*71239cb6SGreg Roach  // 17.1234 ==> N17.1234
710*71239cb6SGreg Roach    if (txt && txt.charAt(0) !== neg && txt.charAt(0) !== pos) {
711*71239cb6SGreg Roach        txt = pos + txt;
712*71239cb6SGreg Roach    }
713*71239cb6SGreg Roach    field.value = txt;
714*71239cb6SGreg Roach}
715*71239cb6SGreg Roach
716*71239cb6SGreg Roach// This is the default way for webtrees to show image galleries.
717*71239cb6SGreg Roach// Custom themes may use a different viewer.
718*71239cb6SGreg Roachfunction activate_colorbox(config)
719*71239cb6SGreg Roach{
720*71239cb6SGreg Roach    $.extend($.colorbox.settings, {
721*71239cb6SGreg Roach      // Don't scroll window with document
722*71239cb6SGreg Roach        fixed: true,
723*71239cb6SGreg Roach        current: '',
724*71239cb6SGreg Roach        previous: '\uf048',
725*71239cb6SGreg Roach        next: '\uf051',
726*71239cb6SGreg Roach        slideshowStart: '\uf04b',
727*71239cb6SGreg Roach        slideshowStop: '\uf04c',
728*71239cb6SGreg Roach        close: '\uf00d'
729*71239cb6SGreg Roach    });
730*71239cb6SGreg Roach    if (config) {
731*71239cb6SGreg Roach        $.extend($.colorbox.settings, config);
732*71239cb6SGreg Roach    }
733*71239cb6SGreg Roach
734*71239cb6SGreg Roach  // Trigger an event when we click on an (any) image
735*71239cb6SGreg Roach    $('body').on('click', 'a.gallery', function () {
736*71239cb6SGreg Roach      // Enable colorbox for images
737*71239cb6SGreg Roach        $('a[type^=image].gallery').colorbox({
738*71239cb6SGreg Roach            photo: true,
739*71239cb6SGreg Roach            maxWidth: '95%',
740*71239cb6SGreg Roach            maxHeight: '95%',
741*71239cb6SGreg Roach            rel: 'gallery', // Turn all images on the page into a slideshow
742*71239cb6SGreg Roach            slideshow: true,
743*71239cb6SGreg Roach            slideshowAuto: false,
744*71239cb6SGreg Roach          // Add wheelzoom to the displayed image
745*71239cb6SGreg Roach            onComplete: function () {
746*71239cb6SGreg Roach              // Disable click on image triggering next image
747*71239cb6SGreg Roach              // https://github.com/jackmoore/colorbox/issues/668
748*71239cb6SGreg Roach                $('.cboxPhoto').unbind('click');
749*71239cb6SGreg Roach
750*71239cb6SGreg Roach                wheelzoom(document.querySelectorAll('.cboxPhoto'));
751*71239cb6SGreg Roach            }
752*71239cb6SGreg Roach        });
753*71239cb6SGreg Roach
754*71239cb6SGreg Roach      // Enable colorbox for audio using <audio></audio>, where supported
755*71239cb6SGreg Roach      // $('html.video a[type^=video].gallery').colorbox({
756*71239cb6SGreg Roach      //  rel:         'nofollow' // Slideshows are just for images
757*71239cb6SGreg Roach      // });
758*71239cb6SGreg Roach
759*71239cb6SGreg Roach      // Enable colorbox for video using <video></video>, where supported
760*71239cb6SGreg Roach      // $('html.audio a[type^=audio].gallery').colorbox({
761*71239cb6SGreg Roach      //  rel:         'nofollow', // Slideshows are just for images
762*71239cb6SGreg Roach      // });
763*71239cb6SGreg Roach
764*71239cb6SGreg Roach      // Allow all other media types remain as download links
765*71239cb6SGreg Roach    });
766*71239cb6SGreg Roach}
767*71239cb6SGreg Roach
768*71239cb6SGreg Roach// Initialize autocomplete elements.
769*71239cb6SGreg Roachfunction autocomplete(selector)
770*71239cb6SGreg Roach{
771*71239cb6SGreg Roach  // Use typeahead/bloodhound for autocomplete
772*71239cb6SGreg Roach    $(selector).each(function () {
773*71239cb6SGreg Roach        $(this).typeahead(null, {
774*71239cb6SGreg Roach            display: 'value',
775*71239cb6SGreg Roach            source: new Bloodhound({
776*71239cb6SGreg Roach                datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
777*71239cb6SGreg Roach                queryTokenizer: Bloodhound.tokenizers.whitespace,
778*71239cb6SGreg Roach                remote: {
779*71239cb6SGreg Roach                    url: this.dataset.autocompleteUrl,
780*71239cb6SGreg Roach                    wildcard: 'QUERY'
781*71239cb6SGreg Roach                }
782*71239cb6SGreg Roach            })
783*71239cb6SGreg Roach        });
784*71239cb6SGreg Roach    });
785*71239cb6SGreg Roach}
786*71239cb6SGreg Roach
787*71239cb6SGreg Roach/* Show / Hide event data for boxes used on charts and elsewhere */
788*71239cb6SGreg Roach$('body').on('click', '.iconz', function (e) {
789*71239cb6SGreg Roach    'use strict';
790*71239cb6SGreg Roach    e.stopPropagation();
791*71239cb6SGreg Roach
792*71239cb6SGreg Roach    var wrapper = $(this).closest('.person_box_template'),
793*71239cb6SGreg Roach    inout = wrapper.find('.inout'),
794*71239cb6SGreg Roach    inout2 = wrapper.find('.inout2'),
795*71239cb6SGreg Roach    namedef = wrapper.find('.namedef'),
796*71239cb6SGreg Roach    basestyle = wrapper.attr('class').match(/(box-style[0-2])/)[1];
797*71239cb6SGreg Roach
798*71239cb6SGreg Roach    function showDetails()
799*71239cb6SGreg Roach    {
800*71239cb6SGreg Roach        wrapper.parent().css('z-index', 100);
801*71239cb6SGreg Roach        toggleExpanded();
802*71239cb6SGreg Roach        namedef.addClass('nameZoom');
803*71239cb6SGreg Roach        inout2.hide(0, function () {
804*71239cb6SGreg Roach            inout.slideDown();
805*71239cb6SGreg Roach        });
806*71239cb6SGreg Roach    }
807*71239cb6SGreg Roach
808*71239cb6SGreg Roach    function hideDetails()
809*71239cb6SGreg Roach    {
810*71239cb6SGreg Roach        inout.slideUp(function () {
811*71239cb6SGreg Roach            inout2.show(0);
812*71239cb6SGreg Roach            namedef.removeClass('nameZoom');
813*71239cb6SGreg Roach            toggleExpanded();
814*71239cb6SGreg Roach            wrapper.parent().css('z-index', '');
815*71239cb6SGreg Roach        });
816*71239cb6SGreg Roach    }
817*71239cb6SGreg Roach
818*71239cb6SGreg Roach    function toggleExpanded()
819*71239cb6SGreg Roach    {
820*71239cb6SGreg Roach        wrapper.toggleClass(function () {
821*71239cb6SGreg Roach            return basestyle + ' ' + basestyle + '-expanded';
822*71239cb6SGreg Roach        });
823*71239cb6SGreg Roach    }
824*71239cb6SGreg Roach
825*71239cb6SGreg Roach    if (!inout.text().length) {
826*71239cb6SGreg Roach        wrapper.css('cursor', 'progress');
827*71239cb6SGreg Roach        inout.load('index.php', {route: 'expand-chart-box', xref: wrapper.data('xref'), ged: wrapper.data('tree')}, function () {
828*71239cb6SGreg Roach            wrapper.css('cursor', '');
829*71239cb6SGreg Roach            showDetails();
830*71239cb6SGreg Roach        });
831*71239cb6SGreg Roach    } else {
832*71239cb6SGreg Roach        if (wrapper.hasClass(basestyle)) {
833*71239cb6SGreg Roach            showDetails();
834*71239cb6SGreg Roach        } else {
835*71239cb6SGreg Roach            hideDetails();
836*71239cb6SGreg Roach        }
837*71239cb6SGreg Roach    }
838*71239cb6SGreg Roach    wrapper.find('.iconz').toggleClass('icon-zoomin icon-zoomout');
839*71239cb6SGreg Roach});
840*71239cb6SGreg Roach
841*71239cb6SGreg Roach/**
842*71239cb6SGreg Roach * Insert text at the current cursor position in an input field.
843*71239cb6SGreg Roach *
844*71239cb6SGreg Roach * @param e The input element.
845*71239cb6SGreg Roach * @param t The text to insert.
846*71239cb6SGreg Roach */
847*71239cb6SGreg Roachfunction insertTextAtCursor(e, t)
848*71239cb6SGreg Roach{
849*71239cb6SGreg Roach    var scrollTop = e.scrollTop;
850*71239cb6SGreg Roach    var selectionStart = e.selectionStart;
851*71239cb6SGreg Roach    var prefix = e.value.substring(0, selectionStart);
852*71239cb6SGreg Roach    var suffix = e.value.substring(e.selectionEnd, e.value.length);
853*71239cb6SGreg Roach    e.value = prefix + t + suffix;
854*71239cb6SGreg Roach    e.selectionStart = selectionStart + t.length;
855*71239cb6SGreg Roach    e.selectionEnd = e.selectionStart;
856*71239cb6SGreg Roach    e.focus();
857*71239cb6SGreg Roach    e.scrollTop = scrollTop;
858*71239cb6SGreg Roach}
859*71239cb6SGreg Roach
860*71239cb6SGreg Roach// Send the CSRF token on all AJAX requests
861*71239cb6SGreg Roach$.ajaxSetup({
862*71239cb6SGreg Roach    headers: {
863*71239cb6SGreg Roach        'X-CSRF-TOKEN': $('meta[name=csrf]').attr('content')
864*71239cb6SGreg Roach    }
865*71239cb6SGreg Roach});
866*71239cb6SGreg Roach
867*71239cb6SGreg Roach// Initialisation
868*71239cb6SGreg Roach$(function () {
869*71239cb6SGreg Roach  // Page elements that load automaticaly via AJAX.
870*71239cb6SGreg Roach  // This prevents bad robots from crawling resource-intensive pages.
871*71239cb6SGreg Roach    $("[data-ajax-url]").each(function () {
872*71239cb6SGreg Roach        $(this).load($(this).data('ajaxUrl'));
873*71239cb6SGreg Roach    });
874*71239cb6SGreg Roach
875*71239cb6SGreg Roach  // Select2 - format entries in the select list
876*71239cb6SGreg Roach    function templateOptionForSelect2(data)
877*71239cb6SGreg Roach    {
878*71239cb6SGreg Roach        if (data.loading) {
879*71239cb6SGreg Roach          // If we're waiting for the server, this will be a "waiting..." message
880*71239cb6SGreg Roach            return data.text;
881*71239cb6SGreg Roach        } else {
882*71239cb6SGreg Roach          // The response from the server is already in HTML, so no need to format it here.
883*71239cb6SGreg Roach            return data.text;
884*71239cb6SGreg Roach        }
885*71239cb6SGreg Roach    }
886*71239cb6SGreg Roach
887*71239cb6SGreg Roach  // Autocomplete
888*71239cb6SGreg Roach    autocomplete('input[data-autocomplete-url]');
889*71239cb6SGreg Roach
890*71239cb6SGreg Roach  // Select2 - activate autocomplete fields
891*71239cb6SGreg Roach    $('select.select2').select2({
892*71239cb6SGreg Roach        width: '90%',
893*71239cb6SGreg Roach      // Do not escape.
894*71239cb6SGreg Roach        escapeMarkup: function (x) {
895*71239cb6SGreg Roach            return x }
896*71239cb6SGreg Roach      // Same formatting for both selections and rsult
897*71239cb6SGreg Roach      //templateResult: templateOptionForSelect2,
898*71239cb6SGreg Roach      //templateSelection: templateOptionForSelect2
899*71239cb6SGreg Roach    })
900*71239cb6SGreg Roach  // If we clear the select (using the "X" button), we need an empty
901*71239cb6SGreg Roach  // value (rather than no value at all) for inputs with name="array[]"
902*71239cb6SGreg Roach    .on('select2:unselect', function (evt) {
903*71239cb6SGreg Roach        $(evt.delegateTarget).append('<option value="" selected="selected"></option>');
904*71239cb6SGreg Roach    })
905*71239cb6SGreg Roach
906*71239cb6SGreg Roach  // Datatables - locale aware sorting
907*71239cb6SGreg Roach    $.fn.dataTableExt.oSort['text-asc'] = function (x, y) {
908*71239cb6SGreg Roach        return x.localeCompare(y, document.documentElement.lang, {'sensitivity': 'base'});
909*71239cb6SGreg Roach    };
910*71239cb6SGreg Roach    $.fn.dataTableExt.oSort['text-desc'] = function (x, y) {
911*71239cb6SGreg Roach        return y.localeCompare(x, document.documentElement.lang, {'sensitivity': 'base'});
912*71239cb6SGreg Roach    };
913*71239cb6SGreg Roach
914*71239cb6SGreg Roach  // DataTables - start hidden to prevent FOUC.
915*71239cb6SGreg Roach    $('table.datatables').each(function () {
916*71239cb6SGreg Roach        $(this).DataTable(); $(this).removeClass('d-none'); });
917*71239cb6SGreg Roach
918*71239cb6SGreg Roach  // Create a new record while editing an existing one.
919*71239cb6SGreg Roach  // Paste the XREF and description into the Select2 element.
920*71239cb6SGreg Roach    $('.wt-modal-create-record').on('show.bs.modal', function (event) {
921*71239cb6SGreg Roach      // Find the element ID that needs to be updated with the new value.
922*71239cb6SGreg Roach        $('form', $(this)).data('element-id', $(event.relatedTarget).data('element-id'));
923*71239cb6SGreg Roach        $('form .form-group input:first', $(this)).focus();
924*71239cb6SGreg Roach    });
925*71239cb6SGreg Roach
926*71239cb6SGreg Roach  // Submit the modal form using AJAX, and paste the returned record ID/NAME into the parent form.
927*71239cb6SGreg Roach    $('.wt-modal-create-record form').on('submit', function (event) {
928*71239cb6SGreg Roach        event.preventDefault();
929*71239cb6SGreg Roach        var elementId = $(this).data('element-id');
930*71239cb6SGreg Roach        $.ajax({
931*71239cb6SGreg Roach            url: 'index.php',
932*71239cb6SGreg Roach            type: 'POST',
933*71239cb6SGreg Roach            data: new FormData(this),
934*71239cb6SGreg Roach            async: false,
935*71239cb6SGreg Roach            cache: false,
936*71239cb6SGreg Roach            contentType: false,
937*71239cb6SGreg Roach            processData: false,
938*71239cb6SGreg Roach            success: function (data) {
939*71239cb6SGreg Roach                $('#' + elementId).select2().empty().append(new Option(data.text, data.id)).val(data.id).trigger('change');
940*71239cb6SGreg Roach            },
941*71239cb6SGreg Roach            failure: function (data) {
942*71239cb6SGreg Roach                alert(data.error_message);
943*71239cb6SGreg Roach            }
944*71239cb6SGreg Roach        });
945*71239cb6SGreg Roach      // Clear the form
946*71239cb6SGreg Roach        this.reset();
947*71239cb6SGreg Roach      // Close the modal
948*71239cb6SGreg Roach        $(this).closest('.wt-modal-create-record').modal('hide');
949*71239cb6SGreg Roach    });
950*71239cb6SGreg Roach
951*71239cb6SGreg Roach  // Activate the langauge selection menu.
952*71239cb6SGreg Roach    $('.menu-language').on('click', '[data-language]', function () {
953*71239cb6SGreg Roach        $.post('index.php', {
954*71239cb6SGreg Roach            route: 'language',
955*71239cb6SGreg Roach            language: $(this).data('language')
956*71239cb6SGreg Roach        }, function () {
957*71239cb6SGreg Roach            window.location.reload();
958*71239cb6SGreg Roach        });
959*71239cb6SGreg Roach
960*71239cb6SGreg Roach        return false;
961*71239cb6SGreg Roach    });
962*71239cb6SGreg Roach
963*71239cb6SGreg Roach  // Activate the theme selection menu.
964*71239cb6SGreg Roach    $('.menu-theme').on('click', '[data-theme]', function () {
965*71239cb6SGreg Roach        $.post('index.php', {
966*71239cb6SGreg Roach            route: 'theme',
967*71239cb6SGreg Roach            theme: $(this).data('theme')
968*71239cb6SGreg Roach        }, function () {
969*71239cb6SGreg Roach            window.location.reload();
970*71239cb6SGreg Roach        });
971*71239cb6SGreg Roach
972*71239cb6SGreg Roach        return false;
973*71239cb6SGreg Roach    });
974*71239cb6SGreg Roach
975*71239cb6SGreg Roach  // Activate the on-screen keyboard
976*71239cb6SGreg Roach    var osk_focus_element;
977*71239cb6SGreg Roach    $('.wt-osk-trigger').click(function () {
978*71239cb6SGreg Roach      // When a user clicks the icon, set focus to the corresponding input
979*71239cb6SGreg Roach        osk_focus_element = document.getElementById($(this).data('id'));
980*71239cb6SGreg Roach        osk_focus_element.focus();
981*71239cb6SGreg Roach        $('.wt-osk').show();
982*71239cb6SGreg Roach
983*71239cb6SGreg Roach    });
984*71239cb6SGreg Roach
985*71239cb6SGreg Roach    $('.wt-osk-script-button').change(function () {
986*71239cb6SGreg Roach        $('.wt-osk-script').prop('hidden', true);
987*71239cb6SGreg Roach        $('.wt-osk-script-' + $(this).data('script')).prop('hidden', false);
988*71239cb6SGreg Roach    });
989*71239cb6SGreg Roach    $('.wt-osk-shift-button').click(function () {
990*71239cb6SGreg Roach        document.querySelector('.wt-osk-keys').classList.toggle('shifted');
991*71239cb6SGreg Roach    });
992*71239cb6SGreg Roach    $('.wt-osk-keys').on('click', '.wt-osk-key', function () {
993*71239cb6SGreg Roach        var key = $(this).contents().get(0).nodeValue;
994*71239cb6SGreg Roach        var shift_state = $('.wt-osk-shift-button').hasClass('active');
995*71239cb6SGreg Roach        var shift_key = $('sup', this)[0];
996*71239cb6SGreg Roach        if (shift_state && shift_key !== undefined) {
997*71239cb6SGreg Roach            key = shift_key.innerText;
998*71239cb6SGreg Roach        }
999*71239cb6SGreg Roach        if (osk_focus_element !== null) {
1000*71239cb6SGreg Roach            var cursorPos = osk_focus_element.selectionStart;
1001*71239cb6SGreg Roach            var v = osk_focus_element.value;
1002*71239cb6SGreg Roach            var textBefore = v.substring(0,  cursorPos);
1003*71239cb6SGreg Roach            var textAfter  = v.substring(cursorPos, v.length);
1004*71239cb6SGreg Roach            osk_focus_element.value = textBefore + key + textAfter;
1005*71239cb6SGreg Roach            if ($('.wt-osk-pin-button').hasClass('active') === false) {
1006*71239cb6SGreg Roach                $('.wt-osk').hide();
1007*71239cb6SGreg Roach            }
1008*71239cb6SGreg Roach        }
1009*71239cb6SGreg Roach    });
1010*71239cb6SGreg Roach
1011*71239cb6SGreg Roach    $('.wt-osk-close').on('click', function () {
1012*71239cb6SGreg Roach        $('.wt-osk').hide();
1013*71239cb6SGreg Roach    });
1014*71239cb6SGreg Roach});
1015