PageSourceSearch

https://www.restaurantmarie.de/js/_essentials/basics.js

js restaurantmarie.de collected 2026-10-06 17:50:37 UTC 11,153 bytes, 318 lines download raw bytes

1/**
2 * replace external link target and rel attributes 
3 * to open external links in a new tab
4 * @param {string} selector css selector for all links to handle 
5 */
6function handleExternalLinks(selector) {
7    selector = selector ?? `a[href]:not([href*="${window.location.hostname}"]):not([href^="javascript"])`
8    const links = document.querySelectorAll(selector);
9    links.forEach(link => {
10        if (link.hostname !== window.location.hostname) {
11            if (!link.target) {
12                link.target = '_blank';
13            }
14            if (!link.hasAttribute("rel")) {
15                link.setAttribute('rel', 'noreferrer');
16            }
17        }
18    });
19}
20
21/**
22 * body class handling for no-js and touch devices
23 */
24function applyBodyClasses() {
25    const body = document.body;
26    body.classList.remove('no-js');
27    body.classList.add('javascript');
28    if ('ontouchstart' in document.documentElement) body.classList.add('is-touch');
29}
30
31/**
32 * create email links from data-email placeholders
33 */
34function createEmailLinks() {
35    const mailLinks = document.querySelectorAll('a[data-email]');
36    mailLinks.forEach(link => {
37        const data = JSON.parse(link.getAttribute('data-email')),
38            escape = link.querySelector('span.escape');
39        link.href = `mailto:${data.name}@${data.host}`
40        if (escape) escape.outerHTML = '@';
41    });
42}
43
44/**
45 * check if a current css breakpoint of viewport width is matching
46 * @param {string} width name of the breakpoint 
47 * @param {string} direction direction, can be either "min", or "max"
48 * @returns boolean
49 */
50function breakpoint(width, direction) {
51    const breakpoints = new Array();
52    for (bp in cssData['gridMap']) {
53        if (cssData['gridMap'][bp].width === '$rowMaxWidth') breakpoints[bp] = parseInt(cssData['rowMaxWidth'].replace('px', '')) / 16 + 'em';
54        else breakpoints[bp] = parseInt(cssData['gridMap'][bp].width.replace('px', '')) / 16 + 'em';
55    }
56    direction = (typeof direction === 'undefined') ? 'min' : direction;
57    if (typeof width === 'undefined') {
58        return breakpoints
59    } else {
60        if (direction === 'min') return window.matchMedia("(min-width: " + breakpoints[width] + ")").matches;
61        else return window.matchMedia("(max-width: " + breakpoints[width].replace(/(\d|\W)*/, function (m) { return parseFloat(m) - 0.0625 }) + ")").matches
62    }
63}
64
65/**
66 * returns the current css breakpoints name as a string, e.g. "large"
67 * @author Dominik Kressler
68 * @thanks Eric Holtkamp
69 * @date 2020-05-05
70 * 
71 */
72function currentBp() {
73    const bp = breakpoint();
74    const checkWidth = (window.innerWidth + 1) / parseInt(cssData['base-font-size']);
75    const values = Object.values(bp).map(function (x) { return parseFloat(x); });
76    values.sort(function (a, b) { return a - b; });
77    let result = false;
78
79    for (let i = 0, c = values.length; i < c; i++) {
80        if (i < values.length - 1 && checkWidth >= values[i] && checkWidth <= values[(i + 1)]) {
81            result = values[i];
82            break;
83        }
84    }
85    if (result === false) result = values[values.length - 1];
86
87    return Object.keys(bp)[Object.values(bp).indexOf(result + 'em')];
88}
89
90/**
91 * add direction of scrolling as body class
92 * @param {HTMLElement} element the element to apply classes to
93 */
94function addScrollDirection(element) {
95    let lastScrollTop = 0;
96    document.addEventListener('scroll', function (e) {
97        const scrollTop = window.scrollY || document.documentElement.scrollTop;
98        if (scrollTop > lastScrollTop) {
99            element.classList.add('scrolling-down');
100            element.classList.remove('scrolling-up');
101        }
102        else if (scrollTop != lastScrollTop) {
103            element.classList.remove('scrolling-down');
104            element.classList.add('scrolling-up');
105        }
106        lastScrollTop = scrollTop <= 0 ? 0 : scrollTop;
107    });
108}
109
110/**
111 * allow clicking elements with a matching tab index by pressing "Enter"
112 */
113function handleEnterKeyClick() {
114    window.addEventListener('keydown', function (e) {
115        if (e.key === "Enter" && document.activeElement.getAttribute('tabindex') != null) {
116            document.activeElement.click();
117        }
118    });
119}
120
121/**
122 * add class to element if scroll detection injection
123 * @param {string or number} scrollDetect detect a element or fix number for init
124 * @param {string} scrollClassName name of scroll class for element
125 * @param {HTMLElement} element the element to apply classes to
126 */
127function addScrollClass(scrollDetect = 100, scrollClassName = 'scrolled', element = document.body) {
128    if (typeof scrollDetect === "string") {
129        scrollDetect = document.querySelector(scrollDetect).getBoundingClientRect().height
130    }
131
132    function init() {
133        const scrollTop = window.scrollY || document.documentElement.scrollTop;
134        if (scrollTop >= scrollDetect) {
135            element.classList.add(scrollClassName)
136        }
137        else {
138            element.classList.remove(scrollClassName)
139        }
140    }
141
142    init()
143
144    window.addEventListener('scroll', () => {
145        init()
146    }, false)
147}
148
149/**
150 * load fonts from theme css variables
151 * this will create font-face declarations with new font-family names
152 * to overwrite existing ones, and set the theme variables from
153 * the template as actual variables once the fonts loaded
154 */
155async function loadFonts() {
156
157    /**
158     * suffix which gets appended to font family, 
159     * needed to overwrite previous font face declarations,
160     * because font face declarations for the same font family
161     * do not work reliably
162     */
163    const suffix = "theme";
164
165    /**
166     * wrapper for theme cms theme variables,
167     * used for observation to detect changes to font variables
168     */
169    const themeWrapper = document.querySelector("head");
170
171    /**
172     * setup mutation observer to listen for changes
173     * to the font configuration
174     */
175    const setUpObserver = () => {
176        const observer = new MutationObserver(() => {
177            console.log("font config change detected");
178            updateFonts();
179        });
180        observer.observe(themeWrapper, {
181            childList: true,
182            subtree: true,
183        });
184    };
185
186    /**
187     * get theme variable style tag
188     * @returns {HTMLStyleElement|undefined}
189     */
190    const getThemeVariableStyle = () => {
191        const themeFontsTemplate = document.querySelector("#cms-theme-fonts");
192        if (!themeFontsTemplate) return;
193        const themeFontsDoc = themeFontsTemplate.content.cloneNode(true);
194        const themeStyle = themeFontsDoc.querySelector("style");
195        return themeStyle;
196    };
197
198    /**
199     * parse css stylesheet from theme variable style tag
200     * @param {HTMLStyleElement} styleEl
201     * @returns {[CSSStyleDeclaration|undefined, CSSStyleSheet]}
202     */
203    const parseThemeVariables = (styleEl) => {
204        const sheet = new CSSStyleSheet();
205        sheet.replaceSync(styleEl.innerHTML);
206        const style = sheet.cssRules[0].style;
207        return [style, sheet];
208    };
209
210    /**
211     * create font face declarations from cms theme fonts template
212     * @param {object} globalFontVariables
213     * @param {CSSStyleDeclaration} themeFontVariables
214     * @returns {FontFace[]}
215     */
216    const createFontFaces = (globalFontVariables, themeFontVariables) => {
217        const fonts = [];
218        // create all font files and associate them
219        // with the themes actual font names and weights
220        for (const fontKey in globalFontVariables) {
221            const fontName = themeFontVariables.getPropertyValue(`--font-${fontKey}`);
222            const escapedFontName = fontName.replaceAll("\"", "");
223            const randomizedName = `${escapedFontName}-${suffix}`;
224            // update font name var
225            themeFontVariables.setProperty(`--font-${fontKey}`, `"${randomizedName}"`);
226
227            // get font weights
228            for (const weightKey in globalFontVariables[fontKey].weights) {
229                const fontWeight = themeFontVariables.getPropertyValue(`--font-${fontKey}-${weightKey}`);
230                // remove surrounding quotation marks for defining FontFace
231                // define font face for each weight
232                fonts.push(
233                    new FontFace(randomizedName, `url(/fonts/font-${fontKey}-${weightKey}.woff2)`, {
234                        weight: fontWeight,
235                    })
236                );
237            }
238        }
239        return fonts;
240    };
241
242    /**
243     * initiates loading  all font faces in parallel,
244     * resolves when all fonts loaded
245     * @param {FontFace[]} fonts
246     * @returns {Promise<void>}
247     */
248    const loadAllFontFaces = async (fonts) => {
249        // return Promise.allSettled(fonts.map(font => font.load()))
250        return Promise.all(fonts.map((font) => font.load()));
251    };
252
253    /**
254     * replaces all existing font face declarations and their corresponding theme variables
255     * in the document with new ones
256     * @param {FontFace[]} fonts
257     * @param {CSSStyleSheet} style
258     * @param {string[]} fontsToDelete array of previously added font names (font family),
259     *                                      which will be removed when font declaration updates
260     */
261    const replaceFontsInDocument = (fonts, style, fontsToDelete) => {
262        // add loaded fonts to the document
263        fonts.forEach((font) => document.fonts.add(font));
264
265        // remove previous font declarations
266        fontsToDelete.map((font) => document.fonts.delete(font));
267
268        // add style tag with css variables containing font names
269        // to the dom, remove any old ones
270        const loadedFonts = document.querySelector("#cms-theme-fonts-loaded");
271        if (loadedFonts) loadedFonts.remove();
272        document.adoptedStyleSheets.push(style);
273        window.WizardMessageBroker && window.WizardMessageBroker.fireEvent("cms-theme-fonts-loaded", {});
274        window.dispatchEvent(new CustomEvent("cms-theme-fonts-loaded"));
275    };
276
277    /**
278     * keep track of previous added fonts, which should be removed on a font update,
279     */
280    let oldFonts = [];
281
282    /**
283     * update all fonts with new values
284     */
285    const updateFonts = async () => {
286        try {
287            const style = getThemeVariableStyle();
288            if (!style) {
289                console.log("No theme font snippet found");
290                return;
291            }
292            const [themeFontVariables, sheet] = parseThemeVariables(style);
293            const globalFontVariables = window.cssData["fontMap"];
294            const fonts = createFontFaces(globalFontVariables, themeFontVariables);
295            await loadAllFontFaces(fonts);
296            replaceFontsInDocument(fonts, sheet, oldFonts);
297            oldFonts = fonts;
298        } catch (e) {
299            console.error(`Font file could not be loaded`, e);
300        }
301    };
302
303    //setUpObserver();
304    return await updateFonts();
305}
306
307
308/**
309 * summarize all basics functions
310 */
311function basics() {
312    loadFonts();
313    applyBodyClasses();
314    createEmailLinks();
315    handleExternalLinks();
316    addScrollDirection(document.body);
317    handleEnterKeyClick();
318}

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.