PageSourceSearch

https://www.anthrazit.org/appl/js/lib.js?c=e273

js anthrazit.org collected 2026-09-24 10:54:40 UTC 45,598 bytes, 1,343 lines download raw bytes

1/*
2 ########################### anthrazit ag ############################
3 ### +-----------------------------------------------------------+ ###
4 ### |      Project Name: moPage                                 | ###
5 ### |      Author: Stalder, Lukas                               | ###
6 ### |      Datum: 03.01.2012                                    | ###
7 ### |      (c) anthrazit ag, Zuerich                            | ###
8 ### +-----------------------------------------------------------+ ###
9 #####################################################################
10 */
11
12// lazy-load the swisstopo / OpenLayers runtime on demand (idempotent).
13// Used by map previews that are not present at page load.
14window.loadSwissMapAssets = (function () {
15	var loading = false, queue = [];
16	return function (cb) {
17		if (window.SwissMap) { cb(); return; }
18		if (cb) queue.push(cb);
19		if (loading) return;
20		loading = true;
21		var css = document.createElement('link');
22		css.rel = 'stylesheet';
23		css.href = '/appl/css/openlayers.css';
24		document.head.appendChild(css);
25		var s1 = document.createElement('script');
26		s1.src = '/appl/js/node_modules/ol/dist/ol.js';
27		s1.onload = function () {
28			var s2 = document.createElement('script');
29			s2.src = '/appl/js/node_modules/ol-mapbox-style/dist/olms.js';
30			s2.onload = function () {
31				var s3 = document.createElement('script');
32				s3.src = '/appl/js/swissmap/swissmap.js';
33				s3.onload = function () {
34					var s4 = document.createElement('script');
35					s4.src = '/appl/js/swissmap/gllppicker.js';
36					s4.onload = function () { queue.forEach(function (f) { f(); }); queue = []; };
37					document.head.appendChild(s4);
38				};
39				document.head.appendChild(s3);
40			};
41			document.head.appendChild(s2);
42		};
43		document.head.appendChild(s1);
44	};
45})();
46
47// JAVASCRIPT Lib
48
49
50var resetCount = 0;
51var theTitle = document.title;
52var resizeTimer;
53
54function round(value, decimals) {
55    return Number(Math.round(value + 'e' + decimals) + 'e-' + decimals);
56}
57
58function getElement(id) {
59    var obj;
60    if (document.getElementById) {
61        obj = document.getElementById(id);
62    } else if (document.all) {
63        obj = document.all.id;
64    }
65    return obj;
66}
67
68/** strip slashes
69 * @param str
70 * @returns {*}
71 */
72function stripSlashes(str) {
73    return str.replace(/\\/g, '');
74}
75
76/**
77 * Generate a string of random characters of the specified length
78 *
79 * @param int length   Length of the string to generate
80 * @param bool nodupes Set to true if duplicate characters should not be allowed
81 * @return string
82 */
83function getRandomString(length, nodupes) {
84    // "typecast" the length to an integer
85    length = parseInt(length, 10);
86
87    // make sure the output is at least one character long
88    if (length < 1) {
89        length = 1;
90    }
91
92    // list of characters to use
93    var chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
94
95    // final output string
96    var output = '';
97
98    // list of used characters (only used if nodupes === true)
99    var used = '';
100
101    // make sure we don't have an infinite loop
102    if ((nodupes === true) && (length > chars.length)) {
103        length = chars.length;
104    }
105
106    // loop as many times as `length` defines
107    do {
108        // get a random number between 0 and length of `chars`
109        var randnum = Math.floor(Math.random() * chars.length)
110
111        // extract the random character
112        var chr = chars.charAt(randnum);
113
114        // check if we are not allowed to include duplicate characters
115        if (nodupes === true) {
116            // true if character already used
117
118            var added = (used.indexOf(chr) !== -1);
119
120            // skip the character if already used
121            if (added === true) {
122                continue;
123            }
124
125            // add character to the list of used ones
126            used += chr;
127        }
128
129        // add character to the output string
130        output += chr;
131    } while (output.length < length);
132
133    return output;
134}
135
136
137function countChars(obj, displayid) {
138    var mlength = obj.getAttribute ? parseInt(obj.getAttribute("maxlength")) : "";
139    if (obj.getAttribute && obj.value.length > mlength) {
140        obj.value = obj.value.substring(0, mlength);
141    }
142    if (displayid) {
143        var disp = getElement(displayid);
144        disp.innerHTML = (mlength - obj.value.length);
145    }
146}
147
148
149// simple jquery seconds countdown
150if (typeof (jQuery) !== 'undefined') {
151    (function ($) {
152
153        $.fn.reloadafter = function (options) {
154
155            var settings = {seconds: 60};
156            $.extend(settings, options);
157
158            var interval = 1000;
159            var secs = settings.seconds + 1;
160            var target = $(this);
161
162            var remainingTime = function () {
163                secs--;
164                if (secs < 1) {
165                    counterStop();
166                    window.location.href = settings.url;
167                    secs = 1;
168                }
169
170                var m = Math.floor(secs / 60);
171                var s = secs % 60;
172
173
174                if (m > 0) counter = m + " min " + s + " sek";
175                else counter = secs + " sek ";
176                return counter;
177            }
178
179            var counterUpdate = function () {
180                target.html(remainingTime());
181            }
182
183
184            var counterStart = function () {
185                counterUpdate();
186                executer = setInterval(function () {
187                    counterUpdate()
188                }, interval);
189            }
190
191            var counterStop = function () {
192                clearInterval(executer);
193            }
194
195            if (secs > 0) {
196                counterStart()
197            }
198
199        };
200
201    })(jQuery);
202}
203
204function rawurldecode(str) {
205    // Decodes URL-encodes string  
206    // 
207    // version: 901.1411
208    // discuss at: http://phpjs.org/functions/rawurldecode
209    // +   original by: Brett Zamir
210    // *     example 1: rawurldecode('Kevin+van+Zonneveld%21');
211    // *     returns 1: 'Kevin+van+Zonneveld!'
212    // *     example 2: rawurldecode('http%3A%2F%2Fkevin.vanzonneveld.net%2F');
213    // *     returns 2: 'http://kevin.vanzonneveld.net/'
214    // *     example 3: rawurldecode('http%3A%2F%2Fwww.google.nl%2Fsearch%3Fq%3Dphp.js%26ie%3Dutf-8%26oe%3Dutf-8%26aq%3Dt%26rls%3Dcom.ubuntu%3Aen-US%3Aunofficial%2
2146client%3Dfirefox-a');
215    // *     returns 3: 'http://www.google.nl/search?q=php.js&ie=utf-8&oe=utf-8&aq=t&rls=com.ubuntu:en-US:unofficial&client=firefox-a'
216    var histogram = {};
217    var ret = str.toString();
218
219    var replacer = function (search, replace, str) {
220        var tmp_arr = [];
221        tmp_arr = str.split(search);
222        return tmp_arr.join(replace);
223    };
224
225    // The histogram is identical to the one in urlencode.
226    histogram["'"] = '%27';
227    histogram['('] = '%28';
228    histogram[')'] = '%29';
229    histogram['*'] = '%2A';
230    histogram['~'] = '%7E';
231    histogram['!'] = '%21';
232
233    for (replace in histogram) {
234        search = histogram[replace]; // Switch order when decoding
235        ret = replacer(search, replace, ret) // Custom replace. No regexing
236    }
237
238    // End with decodeURIComponent, which most resembles PHP's encoding functions
239    ret = decodeURIComponent(ret);
240
241    return ret;
242}
243
244function setCoordsCookie(lat, long) {
245    var the_date = new Date(2038, 1, 1, 0, 0, 0, 0);
246    var now = new Date();
247
248    var the_cookie = 'geoLatitude=' + lat + ';expires=' + the_date.toUTCString() + ';path=/; secure; samesite=none';
249    window.document.cookie = the_cookie;
250
251    var the_cookie = 'geoLongitude=' + long + ';expires=' + the_date.toUTCString() + ';path=/; secure; samesite=none';
252    window.document.cookie = the_cookie;
253
254    var the_cookie = 'geoLocationFetched=' + (now.getTime() / 1000) + ';expires=' + the_date.toUTCString() + ';path=/; secure; samesite=none';
255    window.document.cookie = the_cookie;
256
257}
258
259
260function getGeoLocation(url_reload, msg_fail, msg_denied, url_setpos) {
261    if (navigator.geolocation) {
262        navigator.geolocation.getCurrentPosition(function (position) {
263                setCoordsCookie(position.coords.latitude, position.coords.longitude);
264
265                if (url_reload != false) {
266                    window.location.href = url_reload;
267                }
268            },
269            function (err) {
270                //alert(err.code + '  :: ' + err.message);
271                if (err.code == 1) {
272                    //window.location.href = document.URL + '&msg=txt_msg_geo_denied';
273                    alert(msg_denied);
274                } else if (err.code == 2) {
275                    alert(msg_fail);
276                    if (typeof url_setpos !== 'undefined') {
277                        window.location.href = url_setpos;
278                    }
279
280                }
281            }
282        );
283
284    } else {
285        alert(msg_fail);
286        //window.location.href = url_reload;
287    }
288}
289
290function SetNoPosCookie() {
291    var exp = new Date();
292    exp.setMinutes(exp.getMinutes() + 3);
293    var the_cookie = 'geoLocationNoPos=true;expires=' + exp.toUTCString() + ';path=/; secure; samesite=none';
294    window.document.cookie = the_cookie;
295    return true;
296}
297
298// try to get cords from current location and redirect to given URL and replace {lat} {long} by retrieved coords
299function getGeoLocationRedirectCoords(url_reload, msg_fail) {
300    if (navigator.geolocation) {
301        navigator.geolocation.getCurrentPosition(
302            function (position) {
303                var the_date = new Date(2038, 1, 1, 0, 0, 0, 0);
304
305                var the_cookie = 'geoLatitude=' + position.coords.latitude + '; expires=' + the_date.toGMTString() + '; path=/; secure; samesite=none';
306                document.cookie = the_cookie;
307
308                var the_cookie = 'geoLongitude=' + position.coords.longitude + '; expires=' + the_date.toGMTString() + '; path=/;  secure; samesite=none';
309                document.cookie = the_cookie;
310
311                // redirect to given url and replace lat /long
312                if (url_reload != false) {
313                    url_reload = url_reload.replace("{lat}", position.coords.latitude);
314                    url_reload = url_reload.replace("{long}", position.coords.longitude);
315                    window.location.href = url_reload;
316                }
317            },
318            function (error) {
319                alert(msg_fail);
320            },
321            {timeout: 1000, maximumAge: 1000}
322        );
323
324    } else {
325        alert(msg_fail);
326    }
327}
328
329// get all cookies
330function get_cookies_array() {
331    var cookies = {};
332    if (document.cookie && document.cookie != '') {
333        var split = document.cookie.split(';');
334        for (var i = 0; i < split.length; i++) {
335            var name_value = split[i].split("=");
336            name_value[0] = name_value[0].replace(/^ /, '');
337            cookies[decodeURIComponent(name_value[0])] = decodeURIComponent(name_value[1]);
338        }
339    }
340    return cookies;
341}
342
343
344function deleteStoredLocationCookie() {
345    document.cookie = 'locations' + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT; ';
346    return true;
347}
348
349/**
350 * determinefrontendid from screen width
351 * @param w
352 * @returns {number}
353 */
354function getFrontendId(w, env) {
355    var sizes = [];
356    //if (env == 'cms') sizes.push([4, 1024]);
357    if (env == 'cms') sizes.push([4, 1112]);
358    sizes.push([6, 800]);
359    sizes.push([3, 320]);
360
361    for (i = 0; i < sizes.length; i++) {
362        var s = sizes[i];
363        //if (w > (s[1] - 10)) {
364        if (w >= (s[1])) {
365            return s[0];
366        }
367    }
368    return 3;
369}
370
371/**
372 * writes current windfow size in cookie
373 */
374function writeWindowSizeInCookie(env) {
375    clearTimeout(resizeTimer);
376    resizeTimer = setTimeout(function () {
377
378        // get last stored screen width
379        let width = getCookie('currentWidth');
380        let oldorientation = getCookie('orientation');
381
382        // set new widths in cookies
383        setCookie('currentWidth', window.innerWidth);
384        setCookie('currentHeight', window.innerHeight);
385        setCookie('availWidth', screen.availWidth);
386        setCookie('availHeight', screen.availHeight);
387        if (typeof (appHeaders) !== 'undefined' && typeof (appHeaders['Moapp-Screen-Orientation']) !== 'undefined') setCookie('orientation', appHeaders['Moapp-Screen-Orientation']);
388
389        // reload page if resized more than 400px and frontendid has changed
390        let res = {};
391        res.old = getFrontendId(width, env);
392        res.new = getFrontendId(window.innerWidth, env);
393
394        // write in footer for debugging
395        $('#_jsscreensize').html(width + ' &gt; ' + window.innerWidth);
396
397        // check if in app
398        let reload = true;
399        if (typeof (appHeaders) !== 'undefined' && typeof (appHeaders['Moapp-Service-Id']) !== 'undefined' && getstr(appHeaders['Moapp-Device-Type']) == 'iphone') reload = false;
400        if ($('body').hasClass('noReload')) reload = false;
401
402        // trigger hidden reload page link at bottom of page
403        if (res.old != res.new && reload == true) {
404            document.getElementById('_reloadpagelink').click();
405        }
406    }, 25);
407}
408
409function setCookie(name, value, days) {
410    value = String(value);
411    //console.log('cookiename: ' + name + ' value: ' + value + ' days: ' + days);
412    if (value.length > 0) {
413        if (typeof (days) != 'undefined') {
414            var the_date = new Date();
415            the_date.setDate(the_date.getDate() + days);
416        } else {
417            var the_date = new Date(2038, 1, 1, 0, 0, 0, 0);
418        }
419    } else {
420        var the_date = new Date(1970, 1, 1, 0, 0, 0, 0);
421    }
422    var the_cookie = name + '=' + value + '; expires=' + the_date.toGMTString() + '; path=/; secure; samesite=none';
423    document.cookie = the_cookie;
424}
425
426function setCookieBySec(name, value, sec) {
427    value = String(value);
428    if (value.length > 0) {
429        if (typeof (sec) != 'undefined') {
430            var the_date = new Date();
431            the_date.setTime(the_date.getTime() + (sec * 1000));
432        } else {
433            var the_date = new Date(2038, 1, 1, 0, 0, 0, 0);
434        }
435    } else {
436        var the_date = new Date(1970, 1, 1, 0, 0, 0, 0);
437    }
438    var the_cookie = name + '=' + value + '; expires=' + the_date.toGMTString() + '; path=/; secure; samesite=none';
439    document.cookie = the_cookie;
440}
441
442function getCookie(cname) {
443    var name = cname + "=";
444    var ca = document.cookie.split(';');
445    for (var i = 0; i < ca.length; i++) {
446        var c = ca[i];
447        while (c.charAt(0) == ' ') c = c.substring(1);
448        if (c.indexOf(name) == 0) return c.substring(name.length, c.length);
449    }
450    return "";
451}
452
453
454/**
455 * browser local notification
456 * @param title
457 * @param body
458 * @param url
459 */
460var notify = function (title, body, url, icon) {
461
462    if (typeof (icon) == 'undefined') icon = null;
463
464    if (!'Notification' in window) {
465        return;
466    }
467
468    if (Notification.permission === 'default') {
469        Notification.requestPermission(function () {
470            notify(title, body, url);
471        });
472    }
473
474    // If the user has granted permission for this domain to send notifications...
475    else if (Notification.permission === 'granted') {
476        var n = new Notification(
477            title,
478            {
479                'body': body,
480                'tag': title,
481                'icon': icon
482            }
483        );
484
485        // Remove the notification from Notification Center when clicked.
486        n.onclick = function () {
487            if (url != null) location.href = url;
488            this.close();
489        };
490
491        // Callback function when the notification is closed.
492        n.onclose = function () {
493            // console.log('Notification closed');
494        };
495    }
496    // If the user does not want notifications to come from this domain...
497    else if (Notification.permission === 'denied') {
498        return;
499    }
500};
501
502function triggerLink(id) {
503    document.getElementById(id).click();
504}
505
506
507function checkDateTime(eDate, eTime) {
508    var date = eDate.val().split('-');
509    var time = sanitizeTime(eTime.val());
510    if (isDate(date[0], date[1], date[2])) {
511        eTime.val(time);
512        return true;
513    }
514    return false;
515}
516
517
518function getWeekday(date) {
519    var c = date.split('.');
520    var d = new Date(c[2] + '-' + c[1] + '-' + c[0]);
521    var wd = ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'];
522    return wd[d.getDay()];
523}
524
525function isDate(y, m, d) {
526    y = parseInt(y);
527    m = parseInt(m);
528    d = parseInt(d);
529    var date = new Date(y, m - 1, d);
530    var convertedDate =
531        "" + date.getFullYear() + (date.getMonth() + 1) + date.getDate();
532    var givenDate = "" + y + m + d;
533    return (givenDate == convertedDate);
534}
535
536function getCurrentDate() {
537    var d = new Date();
538    return addZero(d.getDate()) + '.' + addZero(d.getMonth() + 1) + '.' + d.getFullYear();
539}
540
541
542function addZero(i) {
543    if (i < 10) {
544        i = "0" + i;
545    }
546    return i;
547}
548
549function getCurrentTime() {
550    var d = new Date();
551    return addZero(d.getHours()) + ':' + addZero(d.getMinutes());
552}
553
554/**
555 * full screen alert msg box
556 * @param msg
557 * @returns {boolean}
558 */
559function showAlert(msg) {
560    var scrollTop = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
561
562    $('#topMsgIcon').html("<img src='/appl/grfx/alerts/info_c.png' alt='alert' border='0' class='iconAlert' />");
563    $('#topMsgText').html(msg);
564
565    $('#topMsgOverlay').show();
566    //$('#topMsgOverlay').css();
567    return true;
568}
569
570function showTopAlertBox(msg, msgtype, duration, el) {
571
572    if (typeof (msgtype) == 'undefined') msgtype = 2;
573    if (typeof (duration) == 'undefined') duration = 5000;
574    if (typeof (el) == 'undefined') el = '#dynTopMsgContainer';
575    let html = '';
576
577    // show differetn alert type in backend
578    if (isBackend === true) {
579        let types = {
580            2: 'info-circle',
581            3: 'check-circle',
582            4: 'exclamation-triangle',
583            5: 'exclamation-circle',
584        };
585
586        // cosntruct html
587        html = "<div id='alertContainer' class='animate__animated animate__fadeInDownBig' data-exitclass='animate__fadeOutUpBig'><div class='wrapperBox'>";
588        html += "<div class='tb'><div class='tr type" + msgtype + "'>";
589        if (msgtype > 0) {
590            html += "<div class='td colIcon'><div class='wrapper'><span class='symbol fa fa-" + types[msgtype] + "'>&nbsp;</span></div></div>";
591        }
592        html += "<div class='td colBody'><div class='wrapper'>" + msg + "</div></div>"
593        html += "</div></div></div></div>";
594
595        // msg on frontend
596    } else {
597        let icons = {
598            2: 'circle-info',
599            3: 'circle-check',
600            4: 'triangle-exclamation',
601            5: 'hand',
602            6: 'comment'
603        };
604
605        // construct msg
606        html = "<div class='elementInfoboxTop boxModel absolute' id='topMsg'>";
607        html += "<div class='infobox tb boxModel msgtype" + msgtype + "'><div class='tr'>";
608        if (icons[msgtype].length > 0) html += "<div class='td cellIcon'><span class='icon symbol fa fa-" + icons[msgtype] + "'></span></div>";
609        html += "<div class='td'>" + msg + "</div>";
610        html += "<div class='td closer'><a href='javascript:;' onclick=\"$('#topMsg').hide();\" class='close_' ><span class='symbol fa fa-xmark'></span></a></div>";
611        html += "</div></div>";
612        html += "</div>";
613    }
614
615    //$(el).css({'position': 'fixed', 'top': '80px'});
616
617    // already an open message?
618    if ($(el).is(':visible')) {
619        setTimeout(function () {
620            $(el).html(html).show().delay(duration).fadeOut();
621            $('#topMsg').width($(el).width());
622        }, 5000);
623
624    } else {
625        $(el).html(html).show().delay(duration).fadeOut();
626        $('#topMsg').width($(el).width());
627    }
628}
629
630function redrawCoordPicker() {
631    var currCenter = GlobalCoordPicker.getCenter();
632    google.maps.event.trigger(GlobalCoordPicker, 'resize');
633    GlobalCoordPicker.setCenter(currCenter);
634}
635
636function inIframe() {
637    try {
638        return window.self !== window.top;
639    } catch (e) {
640        return true;
641    }
642}
643
644function sanitizeTime(time) {
645    time = time.replace('.', ':');
646    if (time == '') time = '00:00';
647    if (time.indexOf(':') == -1) time += ':00';
648    var tmp = time.split(':');
649    if (parseInt(tmp[1]) < 10 && parseInt(tmp[1]) != 0) tmp[1] = '0' + parseInt(tmp[1]);
650    if (parseInt(tmp[1]) > 59) tmp[1] = '00';
651    time = tmp[0] + ':' + tmp[1];
652    return time;
653}
654
655function checkFileType(el, allowed, msgfail) {
656    //var type = el.files[0].type;
657    var type = document.getElementById(el).files[0].type;
658    var match = false;
659
660    for (var index = 0; index < allowed.length; ++index) {
661        if (type == allowed[index]) {
662            match = true;
663        }
664    }
665
666    if (match === false) {
667        alert(msgfail);
668        return false;
669    } else {
670        return true;
671    }
672}
673
674function closeDivOnClickOutside(elhide, elclear, eltriggerclick) {
675    var el = $(elhide);
676    var elcl = $(elclear);
677    var elclick = $(eltriggerclick);
678
679    // hide on clicked outside
680    $(document).mouseup(function (e) {
681        if (!el.is(e.target) && el.has(e.target).length === 0) {
682            if (eltriggerclick != null) {
683                elclick.trigger('click');
684            } else {
685                el.hide();
686                if (elclear != null) elcl.html('');
687            }
688            $('#darkenerFull').hide();
689            $('.desaturate').removeClass('desaturate');
690        }
691    });
692
693    // hide on hit esc
694    $(document).on('keydown', function (e) {
695        if (e.keyCode === 27) { // ESC
696            if (eltriggerclick != null) {
697                elclick.trigger('click');
698            } else {
699                el.hide();
700                if (elclear != null) elcl.html('');
701            }
702            $('#darkenerFull').hide();
703        }
704    });
705}
706
707function showRichAlert(msg, title, okbutton, addclasswindow) {
708    if (typeof (title) == 'undefined') title = 'Hinweis';
709    if (typeof (okbutton) == 'undefined') okbutton = true;
710    title = "<span class='symbol gi gii gi-info-sign'></span> " + title;
711    taskWindowHandler($('#mainWrapperMid'), msg, title, null, okbutton, false, false, addclasswindow);
712}
713
714function taskWindowHandler(el, data, title, addcssclass, okbutton, cancelbutton, urlok, addclasswindow, nocloseonclickoutside, winheight, reloadurlonclose) {
715
716    if (typeof (addclasswindow) == 'undefined' || addclasswindow == null) addclasswindow = '';
717    if (typeof (addcssclass) == 'undefined' || addcssclass == null) addcssclass = '';
718    if (typeof (okbutton) == 'undefined' || okbutton == false) okbutton = false;
719    if (typeof (cancelbutton) == 'undefined' || cancelbutton === false) {
720        cancelbutton = false;
721    } else okbutton = 'OK';
722    if (typeof (nocloseonclickoutside) == 'undefined' || nocloseonclickoutside == null) {
723        nocloseonclickoutside = false;
724    }
725    if (typeof (winheight) == 'undefined' || winheight == null) {
726        winheight = 0.85;
727    }
728
729    // construct html task window
730    var html = "<div id='darkenerFull'><div class='tb boxModel'><div class='tr'><div class='td' id='clickToClose'>";
731    html += "<div id='taskWindow' class='" + addclasswindow + "'>";
732    html += "<div class='titleBar'><a class='autoWidth rightAuto' href='javascript:;' id='closerTaskWindow'><span class='symbol gi gi-remove-sign'></span></a><h3 class='backlink'>" + title + "</h3></div>";
733    html += "<div id='taskWindowContentWrapper' class='" + addcssclass + "'>";
734    html += "<div class='wrapperPadding'>";
735    html += data;
736
737    if (cancelbutton !== false) {
738        html += "<div class='marginTop' align='right'>";
739        html += "<a class='button autoWidth' href='javascript:;' onclick=\"$('#closerTaskWindow').trigger('click');\">" + cancelbutton + "</a> "
740        html += "<a class='button autoWidth' href='" + urlok + "'>" + okbutton + "</a>"
741        html += "<div class='clear'></div></div>";
742    } else {
743        if (okbutton != false) {
744            oktext = (okbutton === true) ? 'OK' : okbutton;
745            html += "<div class='marginTop' align='right'><a class='button autoWidth' href='javascript:;' id='taskWindowOkButton' onclick=\"$('#closerTaskWindow').trigger('click');\">" + oktext + "</a></div>"
746        }
747    }
748    html += "</div></div></div></div></div></div></div>";
749
750    // first remove old task window
751    $('#darkenerFull').remove();
752
753    el.after(html);
754    $('html').addClass('modal-open');
755    $('body').addClass('modal-open');
756    closeTaskWindowListener(el, nocloseonclickoutside, reloadurlonclose);
757    $('#taskWindowOkButton').focus();
758
759    // adjust task window height
760    // var _top = $('#taskWindow').position().top;
761    //console.log(window.innerHeight);
762    var h = (window.innerHeight - $('#taskWindow .titleBar').height() - 15) * winheight;
763    $('#taskWindowContentWrapper').css({"max-height": h + "px"});
764}
765
766function taskWindowRemoveCloseListeners(appender) {
767    $(document).off("mouseup");
768    //$(document).off("keydown");
769    $('#closerTaskWindow').off("click");
770    $(appender).trigger('change');
771}
772
773function closeTaskWindowListener(appender, nocloseonclickoutside, reloadurlonclose) {
774    var el = $('#taskWindow');
775
776    if (typeof (reloadurlonclose) == 'undefined') reloadurlonclose = false;
777
778    // hide on clicked outside
779    if (nocloseonclickoutside !== true) {
780        $(document).on("mouseup touchend", function (e) {
781            //console.log(e.target);
782            if (!el.is(e.target) && el.has(e.target).length === 0) {
783                closeTaskWindow();
784            }
785        });
786    }
787
788    /*
789    // hide on hit esc
790    $(document).on('keydown', function (e) {
791        if (e.keyCode === 27) { // ESC
792            $('#darkenerFull').remove();
793            taskWindowRemoveCloseListeners(appender);
794            $('._content, .myFooter').removeClass('blurred');
795        }
796    });
797    */
798
799    // close button hit
800    $('#closerTaskWindow').click(function () {
801        $('#darkenerFull').remove();
802        taskWindowRemoveCloseListeners(appender);
803        $('._content, .myFooter').removeClass('blurred');
804        $('body').removeClass('modal-open');
805        $('html').removeClass('modal-open');
806        if (reloadurlonclose == true) location.reload();
807    });
808}
809
810function closeTaskWindow() {
811    $('#closerTaskWindow').trigger('click');
812}
813
814/**
815 * Displays an information window modal with a title, content, and optional buttons.
816 *
817 * @param {string} title The title to display in the information window.
818 * @param {string} content The HTML content to be displayed in the body of the information window.
819 * @param {Array<Array<string>>} [btns] An optional array of buttons where each button is represented as a two-element array.
820 *                                      The first element is the button's URL and the second element is the button's label.
821 * @return {void} This function does not return a value; it injects the modal structure into the DOM and manages interactions.
822 */
823function infoWindow(title, content, btns, id) {
824    let hidealerts = [];
825    let c = '';
826
827    let hasid = (typeof id !== 'undefined' && id.length > 0);
828
829    // window id? if set > don't show again checkbox
830    if (hasid) {
831        c = getCookie('_hidealerts');
832        if (c != '') {
833            hidealerts = JSON.parse(c);
834
835            // leave here if don't show again was checked earlier
836            if (hidealerts.indexOf(id) > -1) {
837                return false;
838            }
839        }
840    }
841
842    // construct html
843    let html = "<div class='infoWindow floating'>";
844    html += "<div class='caption flex nowrap'><h3 class='backlink'>" + title + "</h3><a href='javascript:;' class='closer fa fa-xmark'></a></div>";
845    html += "<div class='body'>";
846    html += content;
847    html += "</div>";
848
849    // window id? if set > don't show again checkbox
850    if (hasid) {
851        html += "<div class='checkboxWrapper'><input type='checkbox' id='infoWindowShowAgain' /><label for='infoWindowShowAgain'>Nicht mehr anzeigen</label></div>";
852    }
853
854    // buttons
855    if (typeof btns !== 'undefined' && btns.length > 0) {
856        html += "<div class='btns flex nowrap'>";
857        let id = '';
858        for (let i = 0; i < btns.length; i++) {
859            id = (typeof btns[i][2] !== 'undefined') ? btns[i][2] : '';
860            html += "<a class='button' href='" + btns[i][0] + "' id='" + id + "'>" + btns[i][1] + "</a>";
861        }
862        html += "</div>";
863    }
864    html += "</div>";
865
866    html = "<div id='darkenerFull' class='flex center dynInfoWindow'>" + html + "</div>";
867
868    $('#darkenerFull').remove();
869
870    let el = $('#mainWrapperMid');
871    el.after(html);
872    $('html').addClass('modal-open');
873    $('body').addClass('modal-open');
874
875    // close window again
876    let win = $('#darkenerFull .infoWindow');
877
878    // hide on clicked outside
879    $(document).on("mouseup touchend", function (e) {
880        if (!win.is(e.target) && win.has(e.target).length === 0) {
881            win.find('.closer').trigger('click');
882        }
883    });
884
885    // button with # url clicked > close win
886    win.find('.btns a').click(function (e) {
887        if ($(this).attr('href') == '#') {
888            e.preventDefault();
889        }
890        win.find('.closer').trigger('click');
891    });
892
893    // close button hit / clicked outside
894    win.find('.closer').click(function () {
895
896        // store don't show again in cookie
897        if (hasid) {
898            if (win.find('#infoWindowShowAgain').is(':checked')) hidealerts.push(id);
899            else hidealerts = removeByValue(hidealerts, id);
900            if (hidealerts.length > 0) {
901                c = JSON.stringify(uniqueArray(hidealerts));
902                setCookie('_hidealerts', c, 365);
903            } else {
904                setCookie('_hidealerts', '', -1);
905            }
906        }
907
908        $('#darkenerFull').remove();
909        $(document).off("mouseup");
910        $(this).off("click");
911        $(el).trigger('change');
912        $('._content, .myFooter').removeClass('blurred');
913        $('body').removeClass('modal-open');
914        $('html').removeClass('modal-open');
915    });
916}
917
918function uniqueArray(arr) {
919    return [...new Set(arr)];
920}
921
922function removeByValue(array, value) {
923    return array.filter(item => item !== value);
924}
925
926function removeByKey(obj, key) {
927    if (Array.isArray(obj)) {
928        // numeric index case (real array)
929        if (key >= 0 && key < obj.length) {
930            return obj.slice(0, key).concat(obj.slice(key + 1));
931        }
932        return obj; // invalid index, return unchanged
933    } else if (obj !== null && typeof obj === "object") {
934        // object case (associative "array")
935        const {[key]: _, ...rest} = obj;
936        return rest;
937    }
938    return obj; // if not array or object, return unchanged
939}
940
941function geoGetInfoWindow(title, content, buttontext) {
942    if (buttontext == null) buttontext = 'Weiter';
943    var html = "<table><tr><td class='alignTop'><strong>" + title + "</strong><br>" + content + "</td><td class='alignBottom alignRight'><span class='button autoWidth'>" + buttontext + "</span></td><td width='25'></td></tr></table>";
944    return html;
945}
946
947function getLoadingWheel(msg, cssclass, el) {
948    if (typeof (el) == 'undefined') el = 'div';
949    return "<" + el + " class='" + cssclass + "'><img src='/appl/grfx/mopage/waiting.gif' alt='activitywheel' border='0' class='imgWaiting'/> " + msg + "</" + el + ">";
950}
951
952function getLoadingDots(msg, cssclass) {
953    return "<div class='" + cssclass + "'><img src='/appl/grfx/mopage/loadingdots.svg' alt='activitywheel' border='0' class='imgWaitingDots'/> " + msg + "</div>";
954}
955
956function makeSameHeight(cssclass) {
957    var h = 0;
958    $('.' + cssclass).each(function () {
959        h = h > $(this).height() ? h : $(this).height();
960    });
961    $('.' + cssclass).height(h);
962}
963
964if (typeof ($.datepicker) !== 'undefined') {
965    $.datepicker._gotoToday = function (id) {
966        $(id).datepicker('setDate', new Date()).datepicker('hide').blur();
967        $.datepicker._selectDate.apply(this, [id]);
968    };
969}
970
971
972// Function to reveal lightbox and adding YouTube autoplay
973function revealVideo(div, video_id) {
974    var video = document.getElementById(video_id).src;
975    document.getElementById(video_id).src = video + '&autoplay=1'; // adding autoplay to the URL
976    document.getElementById(div).style.display = 'block';
977}
978
979// Hiding the lightbox and removing YouTube autoplay
980function hideVideo(div, video_id) {
981    var video = document.getElementById(video_id).src;
982    var cleaned = video.replace('&autoplay=1', ''); // removing autoplay form url
983    document.getElementById(video_id).src = cleaned;
984    document.getElementById(div).style.display = 'none';
985}
986
987function inArray(needle, haystack) {
988    var length = haystack.length;
989    for (var i = 0; i < length; i++) {
990        if (haystack[i] == needle) return true;
991    }
992    return false;
993}
994
995function scrollToAnchor(aid, addheight) {
996    if (typeof (addheight) == 'undefined') addheight = 0;
997    var aTag = $("a[name='" + aid + "']");
998    $('html,body').animate({scrollTop: aTag.offset().top - addheight}, 'slow');
999}
1000
1001function checkInView(container, elem, partial) {
1002    var contHeight = container.height();
1003    var contTop = container.scrollTop();
1004    var contBottom = contTop + contHeight;
1005    var elemTop = elem.offset().top - container.offset().top;
1006    var elemBottom = elemTop + elem.height();
1007    var isTotal = (elemTop >= 0 && elemBottom <= contHeight);
1008    var isPart = ((elemTop < 0 && elemBottom > 0) || (elemTop > 0 && elemTop <= container.height())) && partial;
1009    return isTotal || isPart;
1010}
1011
1012
1013// ENCODE UTF8
1014function encode_utf8(s) {
1015    return unescape(encodeURIComponent(s));
1016}
1017
1018// DECODE UTF8
1019function decode_utf8(s) {
1020    return decodeURIComponent(escape(s));
1021}
1022
1023String.prototype.toHtmlEntities = function () {
1024    return this.replace(/./gm, function (s) {
1025        // return "&#" + s.charCodeAt(0) + ";";
1026        return (s.match(/[a-z0-9\s]+/i)) ? s : "&#" + s.charCodeAt(0) + ";";
1027    });
1028};
1029
1030function doFormLabels(el) {
1031    $(el).find('input,textarea,select').each(function () {
1032        let label = $(this).attr('placeholder');
1033        if (typeof (label) !== 'undefined' && label.length > 0) {
1034            $(this).before("<label>" + label + "</label>");
1035            $(this).parent().append("<div class='clear'></div>");
1036        }
1037    });
1038}
1039
1040var delay = (function () {
1041    var timer = 0;
1042    return function (cb, ms) {
1043        clearTimeout(timer);
1044        timer = setTimeout(cb, ms);
1045    };
1046})();
1047
1048
1049function copyPageLink() {
1050
1051    /* Get the text field */
1052    var copyText = document.getElementById("currentPageLink");
1053
1054    /* Select the text field */
1055    copyText.select();
1056    copyText.setSelectionRange(0, 99999); /* For mobile devices */
1057
1058    /* Copy the text inside the text field */
1059    navigator.clipboard.writeText(copyText.value);
1060
1061    /* Alert the copied text */
1062    showTopAlertBox("Link wurde erfolgreich kopiert!", 3, 2000)
1063    //alert("Copied the text: " + copyText.value);
1064}
1065
1066function dump(data) {
1067    $.post('/appl/api.php?do=dump', data);
1068}
1069
1070function _currconv(val, idel) {
1071    let amount = val / 100;
1072    amount = amount.toLocaleString('de-CH', {style: 'currency', currency: 'CHF'});
1073    $('#' + idel).text(amount);
1074}
1075
1076function getstr(val) {
1077    if (typeof (val) == 'undefined') return '';
1078    return val;
1079}
1080
1081function hexToRgbA(hex, trnsp) {
1082    var c;
1083    trnsp = (typeof (trnsp) === 'undefined') ? trnsp = 1 : trnsp;
1084    if (/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)) {
1085        c = hex.substring(1).split('');
1086        if (c.length == 3) {
1087            c = [c[0], c[0], c[1], c[1], c[2], c[2]];
1088        }
1089        c = '0x' + c.join('');
1090        return 'rgba(' + [(c >> 16) & 255, (c >> 8) & 255, c & 255].join(',') + ',' + trnsp + ')';
1091    }
1092    // throw new Error('Bad Hex');
1093}
1094
1095function getUnixTime(date = Date.now()) {
1096    return Math.floor(date / 1000)
1097}
1098
1099function debounce(fn, delay) {
1100    let timeout;
1101    return function () {
1102        clearTimeout(timeout);
1103        timeout = setTimeout(() => fn.apply(this, arguments), delay);
1104    };
1105}
1106
1107function normalizeText(text) {
1108    const umlautMap = {
1109        'ä': 'ae', 'ö': 'oe', 'ü': 'ue',
1110        'Ä': 'ae', 'Ö': 'oe', 'Ü': 'ue',
1111        'ß': 'ss'
1112    };
1113
1114    return text
1115        .replace(/[äöüÄÖÜß]/g, match => umlautMap[match])
1116        .toLowerCase()
1117        .replace(/[^a-z0-9]/g, '');
1118}
1119
1120// ── WebAuthn / Passkey helpers ────────────────────────────────────────────────
1121
1122function passkeyB64ToBuffer(b64url) {
1123    const b64 = b64url.replace(/-/g, '+').replace(/_/g, '/');
1124    const bin = atob(b64);
1125    const buf = new Uint8Array(bin.length);
1126    for (let i = 0; i < bin.length; i++) buf[i] = bin.charCodeAt(i);
1127    return buf.buffer;
1128}
1129
1130function passkeyBufToB64(buf) {
1131    const bytes = new Uint8Array(buf);
1132    let bin = '';
1133    bytes.forEach(b => bin += String.fromCharCode(b));
1134    return btoa(bin).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
1135}
1136
1137function passkeyCredToJSON(cred) {
1138    const r = cred.response;
1139    const obj = { id: cred.id, rawId: passkeyBufToB64(cred.rawId), type: cred.type, response: {} };
1140    if (r.clientDataJSON)    obj.response.clientDataJSON    = passkeyBufToB64(r.clientDataJSON);
1141    if (r.authenticatorData) obj.response.authenticatorData = passkeyBufToB64(r.authenticatorData);
1142    if (r.attestationObject) obj.response.attestationObject = passkeyBufToB64(r.attestationObject);
1143    if (r.signature)         obj.response.signature         = passkeyBufToB64(r.signature);
1144    if (r.userHandle)        obj.response.userHandle        = passkeyBufToB64(r.userHandle);
1145    if (cred.authenticatorAttachment) obj.authenticatorAttachment = cred.authenticatorAttachment;
1146    return obj;
1147}
1148
1149function passkeyAutoName(credential) {
1150    let os = '';
1151    if (navigator.userAgentData?.platform) {
1152        os = navigator.userAgentData.platform;
1153    } else {
1154        const ua = navigator.userAgent;
1155        if (/iPhone/.test(ua))       os = 'iPhone';
1156        else if (/iPad/.test(ua))    os = 'iPad';
1157        else if (/Android/.test(ua)) os = 'Android';
1158        else if (/Mac/.test(ua))     os = 'Mac';
1159        else if (/Windows/.test(ua)) os = 'Windows';
1160        else if (/Linux/.test(ua))   os = 'Linux';
1161    }
1162    if (credential.authenticatorAttachment === 'cross-platform') return (os ? os + ' · ' : '') + 'Sicherheitsschlüssel';
1163    return os || 'Passkey';
1164}
1165
1166function passkeyInitLogin(apiUrl) {
1167    if (!window.PublicKeyCredential) return;
1168    $('#passkeyLoginWrapper').removeClass('hidden')
1169
1170    function showMsg(msg, isError) {
1171        const el = document.getElementById('passkeyLoginMsg');
1172        el.textContent = msg;
1173        el.style.color = isError ? '#c00' : '';
1174        el.style.display = '';
1175    }
1176
1177    async function passkeyAuth() {
1178        const btn = document.getElementById('btnPasskeyLogin');
1179        btn.disabled = true;
1180        showMsg('Passkey wird abgefragt…');
1181        try {
1182            const optRes = await fetch(apiUrl + '?do=passkeyAuthOptions');
1183            const options = await optRes.json();
1184            options.challenge = passkeyB64ToBuffer(options.challenge);
1185            if (options.allowCredentials) {
1186                options.allowCredentials = options.allowCredentials.map(c => ({ ...c, id: passkeyB64ToBuffer(c.id) }));
1187            }
1188            const credential = await navigator.credentials.get({ publicKey: options });
1189            const stay = document.querySelector('[name=stayloggedin]')?.checked ? '1' : '0';
1190            const verRes = await fetch(apiUrl + '?do=passkeyAuthVerify&stayloggedin=' + stay, {
1191                method:  'POST',
1192                headers: { 'Content-Type': 'application/json' },
1193                body:    JSON.stringify(passkeyCredToJSON(credential))
1194            });
1195            const result = await verRes.json();
1196            if (result.ok) {
1197                showMsg('Erfolgreich – wird weitergeleitet…');
1198                window.location = result.redirect;
1199            } else {
1200                showMsg(result.error || 'Fehler bei der Authentifizierung.', true);
1201                btn.disabled = false;
1202            }
1203        } catch (e) {
1204            if (e.name !== 'NotAllowedError') showMsg('Fehler: ' + e.message, true);
1205            else showMsg('');
1206            btn.disabled = false;
1207        }
1208    }
1209
1210    document.getElementById('btnPasskeyLogin').addEventListener('click', passkeyAuth);
1211
1212    if (PublicKeyCredential.isConditionalMediationAvailable) {
1213        PublicKeyCredential.isConditionalMediationAvailable().then(async available => {
1214            if (!available) return;
1215            const optRes = await fetch(apiUrl + '?do=passkeyAuthOptions');
1216            const options = await optRes.json();
1217            options.challenge = passkeyB64ToBuffer(options.challenge);
1218            options.allowCredentials = [];
1219            try {
1220                const credential = await navigator.credentials.get({ publicKey: options, mediation: 'conditional' });
1221                const verRes = await fetch(apiUrl + '?do=passkeyAuthVerify', {
1222                    method:  'POST',
1223                    headers: { 'Content-Type': 'application/json' },
1224                    body:    JSON.stringify(passkeyCredToJSON(credential))
1225                });
1226                const result = await verRes.json();
1227                if (result.ok) window.location = result.redirect;
1228            } catch (e) {
1229            }
1230        });
1231    }
1232}
1233
1234function passkeyInitManager(apiUrl) {
1235    if (!window.PublicKeyCredential) {
1236        $('#btnAddPasskey').addClass('hidden')
1237        return;
1238    }
1239
1240    function showMsg(msg, ok) {
1241        const el = document.getElementById('passkeyAddMsg');
1242        el.textContent = msg;
1243        el.style.color = ok ? 'green' : '#c00';
1244        $('#passkeyAddMsg').removeClass('hidden');
1245    }
1246
1247    document.getElementById('btnAddPasskey').addEventListener('click', async () => {
1248        const btn = document.getElementById('btnAddPasskey');
1249        btn.disabled = true;
1250        try {
1251            const optRes = await fetch(apiUrl + '?do=passkeyRegisterOptions');
1252            const options = await optRes.json();
1253            options.challenge = passkeyB64ToBuffer(options.challenge);
1254            options.user.id   = passkeyB64ToBuffer(options.user.id);
1255            if (options.excludeCredentials) {
1256                options.excludeCredentials = options.excludeCredentials.map(c => ({ ...c, id: passkeyB64ToBuffer(c.id) }));
1257            }
1258            const credential = await navigator.credentials.create({ publicKey: options });
1259            const name = passkeyAutoName(credential);
1260            const verRes = await fetch(apiUrl + '?do=passkeyRegisterVerify', {
1261                method:  'POST',
1262                headers: { 'Content-Type': 'application/json', 'X-Passkey-Name': name },
1263                body:    JSON.stringify(passkeyCredToJSON(credential))
1264            });
1265            const result = await verRes.json();
1266            if (result.ok) { showMsg('Passkey erfolgreich gespeichert.', true); location.reload(); }
1267            else showMsg(result.error || 'Fehler beim Speichern.', false);
1268        } catch (e) {
1269            if (e.name !== 'NotAllowedError') showMsg('Fehler: ' + e.message, false);
1270        }
1271        btn.disabled = false;
1272    });
1273
1274    window.passkeyDelete = async function (id, btn) {
1275        if (!confirm('Passkey wirklich löschen?')) return;
1276        btn.disabled = true;
1277        const fd = new FormData();
1278        fd.append('id', id);
1279        await fetch(apiUrl + '?do=passkeyDelete', { method: 'POST', body: fd });
1280        btn.closest('.theRow').remove();
1281        if (!document.querySelectorAll('#passkeyList .theRow').length) location.reload();
1282    };
1283}
1284
1285function onceVisible(el, callback) {
1286    el = el instanceof jQuery ? el[0] : el;
1287
1288    // already visible? just measure now
1289    if (el.offsetParent !== null) {
1290        callback(el.offsetWidth);
1291        return;
1292    }
1293
1294    // find the nearest ancestor that's actually hiding it
1295    let target = el.parentElement;
1296    while (target && getComputedStyle(target).display !== 'none') {
1297        target = target.parentElement;
1298    }
1299    if (!target) target = document.body;
1300
1301    const observer = new MutationObserver(() => {
1302        if (el.offsetParent !== null) {
1303            observer.disconnect();
1304            callback(el.offsetWidth);
1305        }
1306    });
1307
1308    observer.observe(target, {
1309        attributes: true,
1310        attributeFilter: ['style', 'class'],
1311        subtree: true // in case a different ancestor toggles
1312    });
1313}
1314
1315$.fn.onceVisible = function (callback) {
1316    return this.each(function () {
1317        onceVisible(this, callback.bind(this));
1318    });
1319};
1320
1321function resizeMapContainer(frontendid){
1322    $(document).ready(function() {
1323        var h = $('#_vertical_').offset().top;
1324        if (frontendid == 6){
1325            $('#theMapContainer').css({top: h});
1326        } else {
1327            h = window.innerHeight - h;
1328            if ($('.elementStickyButtonBottom').length > 0 && $('.elementStickyButtonBottom').is(':hidden') == false) {
1329                h = h - $('.elementStickyButtonBottom').height();
1330                $('#theMapContainer').css({bottom: $('.elementStickyButtonBottom').height()});
1331            }
1332            $('#theMapContainer').height(h - 1);
1333        }
1334    });
1335}
1336
1337function getFullHeight(el) {
1338    if (el instanceof jQuery) el = el[0];
1339    const style = getComputedStyle(el);
1340    const marginTop = parseFloat(style.marginTop);
1341    const marginBottom = parseFloat(style.marginBottom);
1342    return el.offsetHeight + marginTop + marginBottom;
1343}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.