PageSourceSearch

https://www.grammatip.de/login

html grammatip.de collected 2026-10-01 23:52:16 UTC 15,364 bytes, 417 lines download raw bytes

1<!DOCTYPE html>
2<html lang="de">
3<head>
4
5    <meta charset="utf-8"/>
6    <meta name="viewport" content="width=device-width, initial-scale=1">
7    <meta name="description" content="login_page_seo_meta_tag_description">
8
9    <title>Grammatip</title>
10
11    
11<script src="/javascripts/cookie-helper.js?v=1655670100"></script>
11
12    <link rel="shortcut icon" href="/favicon.ico?v=1782478304" type="image/x-icon" />
13
14    <!-- PWA Start -->
15<!-- General -->
16<meta data-hid="mobile-web-app-capable" name="mobile-web-app-capable" content="yes">
17<meta data-hid="og:type" name="og:type" property="og:type" content="website">
18<meta data-hid="og:title" name="og:title" property="og:title" content="Grammatip">
19<meta data-hid="og:site_name" name="og:site_name" property="og:site_name" content="Grammatip">
20<link rel="icon" type="image/x-icon" href="/favicon.ico">
21<link data-hid="shortcut-icon" rel="shortcut icon" href="/android-chrome-192x192.png">
22
23<!-- Apple -->
24<meta data-hid="apple-mobile-web-app-title" name="apple-mobile-web-app-title" content="Grammatip">
25<meta name="theme-color" content="#2D57FC"/>
26<meta name="apple-mobile-web-app-status-bar" content="#2D57FC"/>
27<link data-hid="apple-touch-icon" rel="apple-touch-icon" href="/android-chrome-512x512.png" sizes="512x512">
28
29<!-- Windows -->
30<meta name="application-name" content="Grammatip"/>
31<meta name="msapplication-TileImage" content="/android-chrome-512x512.png">
32<meta name="msapplication-TileColor" content="#2D57FC">
33
34<!-- Manifest -->
35<link rel="manifest" href="/manifest.json" data-hid="manifest" crossorigin="use-credentials">
36
37<!-- Service worker -->
38<script>
39    
40    window.serviceWorkerSupported = true;
41
42    function isIOS16() {
43        return (
44                navigator.appVersion.includes('Version/16') ||
45                navigator.appVersion.includes('OS 16')
46            ) && (
47                /iPhone/.test(navigator.userAgent) ||
48                /iPad/.test(navigator.userAgent) ||
49                /iPod/.test(navigator.userAgent) ||
50                /Macintosh/.test(navigator.userAgent)
51            );
52    }
53
54
55    /**
56     * @param tryOnce
57     * @returns {Promise<void|ServiceWorker|*>}
58     *
59     * @see https://stackoverflow.com/a/67612740
60     */
61    async function registerServiceWorker(tryOnce = false) {
62        if (isIOS16()) {
63            window.serviceWorkerSupported = false;
64
65            try {
66                const reg = await navigator.serviceWorker.getRegistration('/');
67                await reg.unregister();
68            } catch (err) {
69                return;
70            }
71
72            return;
73        }
74
75        if (!('serviceWorker' in navigator)) {
76            window.serviceWorkerSupported = false;
77            throw new Error('serviceWorker not supported');
78        }
79
80        const url = (new URL(`/serviceWorker.js?v=g8534h`, location)).toString();
81        //console.info('Registering worker');
82        const registration = await navigator.serviceWorker.register(url, {
83            scope: '/',
84        });
85
86        const registeredWorker = registration.active || registration.waiting || registration.installing;
87        //console.info('Registered worker:', registeredWorker);
88        if (registeredWorker?.scriptURL != url) {
89            console.log('[ServiceWorker] Old URL:', registeredWorker?.scriptURL || 'none', 'updating to:', url);
90            await registration.update();
91            //console.info('Updated worker');
92        }
93
94        //console.info('Waiting for ready worker');
95        let serviceReg = await navigator.serviceWorker.ready;
96        //console.info('Ready registration:', serviceReg);
97
98        if (!navigator.serviceWorker.controller) {
99            //console.info('Worker isn’t controlling, re-register');
100            try {
101                const reg = await navigator.serviceWorker.getRegistration('/');
102                //console.info('Unregistering worker');
103                await reg.unregister();
104                //console.info('Successfully unregistered, trying registration again');
105                return registerServiceWorker();
106            } catch (err) {
107                window.serviceWorkerSupported = false;
108                console.error(`ServiceWorker failed to re-register after hard-refresh, reloading the page!`, err);
109                return location.reload();
110            }
111        }
112
113        let serviceWorker = serviceReg.active || serviceReg.waiting || serviceReg.installing;
114        if (!serviceWorker) {
115            //console.info('No worker on registration, getting registration again');
116            serviceReg = await navigator.serviceWorker.getRegistration('/');
117            serviceWorker = serviceReg.active || serviceReg.waiting || serviceReg.installing;
118        }
119
120        if (!serviceWorker) {
121            //console.info('No worker on registration, waiting 50ms');
122            await sleep(50); // adjustable or skippable, have a play around
123        }
124
125        serviceWorker = serviceReg.active || serviceReg.waiting || serviceReg.installing;
126        if (!serviceWorker) {
127            window.serviceWorkerSupported = false;
128            throw new Error('after waiting on .ready, still no worker');
129        }
130
131        if (serviceWorker.state == 'redundant') {
132            //console.info('Worker is redundant, trying again');
133            return registerServiceWorker();
134        }
135
136        if (serviceWorker.state != 'activated') {
137            //console.info('Worker IS controlling, but not active yet, waiting on event. state=', serviceWorker.state);
138            try {
139                // timeout is adjustable, but you do want one in case the statechange
140                // doesn't fire / with the wrong state because it gets queued,
141                // see ServiceWorker.onstatechange MDN docs.
142                await timeout(100, new Promise((resolve) => {
143                    serviceWorker.addEventListener('statechange', (e) => {
144                        if (e.target.state == 'activated') resolve();
145                    });
146                }));
147            } catch (err) {
148                if (err instanceof TimeoutError) {
149                    if (serviceWorker.state != 'activated') {
150                        if (tryOnce) {
151                            //console.info('Worker is still not active. state=', serviceWorker.state);
152                            window.serviceWorkerSupported = false;
153                            throw new Error('failed to activate service worker');
154                        } else {
155                            //console.info('Worker is still not active, retrying once');
156                            return registerServiceWorker(true);
157                        }
158                    }
159                } else {
160                    // should be unreachable
161                    throw err;
162                }
163            }
164        }
165
166        //console.info('Worker is controlling and active, we’re good folks!');
167        return serviceWorker;
168    }
169
170    class TimeoutError extends Error {
171    }
172
173    /**
174     * Run promise but reject after some timeout.
175     *
176     * @template T
177     * @param {number} ms Milliseconds until timing out
178     * @param {Promise<T>} promise Promise to run until timeout (note that it will keep running after timeout)
179     * @returns {Promise<T, Error>}
180     */
181    function timeout(ms, promise) {
182        return new Promise((resolve, reject) => {
183            const timer = setTimeout(() => {
184                reject(new TimeoutError);
185            }, ms);
186
187            promise.then((result) => {
188                clearTimeout(timer);
189                resolve(result);
190            }, (error) => {
191                clearTimeout(timer);
192                reject(error);
193            });
194        })
195    }
196
197    registerServiceWorker()
198    
199</script>
199
200<!-- PWA End -->
201
202    
203        <link rel="stylesheet" href="/assets/new/css/web-app.css?id=be3c8f791d151789a2b9627dc861ecbe" type="text/css">
204    
205    
206
207    
208<script>
209    var analyticsCookies = ['_gcl_au', '_ga', '_gid', '_gat_UA-630585-12'];
210    for (var i in analyticsCookies) {
211        if (!getCookieValue(analyticsCookies[i])) {
212            continue;
213        }
214
215        document.cookie = analyticsCookies[i] + '=; path=/; domain=; expires=Thu, 08 Jan 1970 00:00:01 GMT';
216    }
217</script>
217
218
219</head>
220<body class=" body-height">
221
222<div id="vue-app">
223    <cookies-consent-popup></cookies-consent-popup>
224    
225    
226    <section class="header-top">
227    <div class="container py-sm-5 py-3">
228        <div class="row">
229            <div class="col-2 col-sm-6">
230                <a href="/">
231                    <span class="logo"><span class="d-none d-sm-inline text-body">grammatip.de</span></span>
232                </a>
233            </div>
234                            <div class="col-10 col-sm-6">
235                                            <a href="/" class="btn btn-primary btn-teardrop btn-xl float-end">Zur Startseite</a>
236                                    </div>
237                    </div>
238    </div>
239</section>
240
241    <section class="login-section">
242        <div class="container py-sm-5 py-3">
243            <div class="row d-flex justify-content-center align-items-center h-100">
244                <div class="col col-lg-8 col-xl-6">
245                    <div class="card">
246                        <div class="row g-0">
247                            <div class="col d-flex align-items-center">
248                                <div class="card-body">
249                                    <login-form></login-form>
250                                </div>
251                            </div>
252                        </div>
253                    </div>
254                </div>
255            </div>
256
257            <div class="row d-flex justify-content-center align-items-center h-100 mt-5">
258                <div class="col col-lg-6 col-xl-4">
259                    <sso-buttons action-verb="einloggen"></sso-buttons>
260                </div>
261            </div>
262        </div>
263    </section>
264
265
266            <footer class="page-footer">
267    <div class="container-fluid">
268        <div class="row flex-row-reverse d-none d-lg-flex">
269            <div class="col m-0">
270                <div class="d-flex flex-column flex-md-row justify-content-md-end">
271                    <a class="open-cookie-popup cursor-pointer">Cookie Einstellungen</a>
272                    <a href="/policies/privacy">Datenschutz</a>
273                    <a href="/grammatip_de_avv.pdf" target="_blank" download>Datenverarbeitungsvertrag (AVV)</a>
274                    <a href="/policies/terms">AGB</a>
275                    <a href="/policies/impressum">Impressum</a>
276                </div>
277            </div>
278            <div class="col-md-auto m-0 d-flex align-items-end justify-content-end justify-content-md-start">
279                <div class="d-flex">
280                    <span>Entwickelt von Lemma GmbH</span>
281                </div>
282            </div>
283        </div>
284    </div>
285</footer>
286    </div>
287
288
289    
289<script>
290        window.COOKIE_DOMAIN = '.grammatip.de';
291        window.isPWA = ('' === '1');
292    </script>
292
293    
293<script src="/assets/new/js/manifest.js?id=d4bcf00853dad0a3d96b3597ed8ec186"></script>
293
294    
294<script src="/assets/new/js/vendor.js?id=a19c435f8194eff597d954c4ab255344"></script>
294
295    
295<script src="/assets/new/js/app.js?id=4377efb2389f3ca32b5da6ebbf1fa628"></script>
295
296    
296<script>
297    setInterval(function () {
298        var xmlHttp = new XMLHttpRequest();
299        xmlHttp.open('GET', '/api/v1/keep-alive?time=' + Date.now(), true);
300        xmlHttp.send(null);
301    }, 300 * 1000);
302</script>
302
303    
303<script>
304    // Sets the timeout variable to use later
305    let timeoutId;
306
307    // Listen for the window: online event
308    window.addEventListener('online', async (e) => {
309        timeoutId = setTimeout(loop, 2000);
310    });
311
312    // Loop function to be called every 2 seconds, via setTimeout
313    async function loop() {
314        // "Ping" the server and wait for response.
315        let status = await ping()
316        if (status === 200) {
317            // Make new notification
318            let notification = {
319                detail: {message: "Internetverbindung ist wieder hergestellt.", type: 'success'},
320            };
321
322            // Launch the notification
323            const event = new CustomEvent('add-notification', notification);
324            window.dispatchEvent(event);
325            // Stops the looping
326            clearTimeout(timeoutId)
327        } else {
328            // Run the loop if the "ping" failed
329            timeoutId = setTimeout(loop, 2000);
330        }
331    }
332
333    // "ping" the current website
334    function ping() {
335        let URL = window.location.origin;
336        // Defines the configurations for the request
337        let settings = {
338            url: URL,
339            method: "HEAD",
340            timeout: 5000,
341            // Minimizing the data send
342            maxContentLength: 1,
343            maxBodyLength: 1,
344            transformResponse: [function (data, headers) {
345                // Return empty string, because we are not interested in any data
346                return "";
347            }],
348            headers: {
349                accept: "application/json",
350            },
351        };
352
353        // Sends the request and the returned data is set to only be the status code
354        // Also catches and logs any error that might occur
355        return axios(settings)
356            .then((response) => this.status = response.status)
357            .catch((error) => console.log(error.message));
358    }
359
360
361    // Listens to offline event and sends a notification
362    window.addEventListener('offline', (e) => {
363        clearTimeout(timeoutId)
364        let notification = {
365            detail: {message: "Es besteht momentan keine Internetverbindung.", type: 'error'},
366        };
367
368        const event = new CustomEvent('add-notification', notification);
369
370        window.dispatchEvent(event);
371    });
372
373</script>
373
374
375    
376    <!-- Matomo -->
377    
377<script type="text/javascript">
378        var consentCookie = JSON.parse(getCookieValue('COOKIE_CONSENT'));
379                var siteId = '4'; // Prod site id
380        
381        var _paq = window._paq = window._paq || [];
382        if (!consentCookie || consentCookie.statistics != true) {
383            _paq.push(['requireCookieConsent']); // Require cookie consent before adding cookies
384        }
385
386        /* tracker methods like "setCustomDimension" should be called before "trackPageView" */
387        
388                
389        _paq.push(['trackPageView']);
390        _paq.push(['enableLinkTracking']);
391        _paq.push(['enableHeartBeatTimer', 30]);
392        (function () {
393            var u = "//analytics.grammatip.com/";
394            _paq.push(['setTrackerUrl', u + 'matomo.php']);
395            _paq.push(['setSiteId', siteId]);
396            var d = document, g = d.createElement('script'), s = d.getElementsByTagName('script')[0];
397            g.type = 'text/javascript';
398            g.async = true;
399            g.src = u + 'matomo.js';
400            s.parentNode.insertBefore(g, s);
401        })();
402    </script>
402
403    <!-- End Matomo Code -->
404
405
406    
406<script>
407        // enable (bootstrap) tooltips
408        if (!window.isTouchDevice) {
409            var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
410            var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
411                return new bootstrap.Tooltip(tooltipTriggerEl);
412            })
413        }
414    </script>
414
415
416</body>
417</html>

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.