PageSourceSearch

https://uitgeverijpluim.nl/

html uitgeverijpluim.nl collected 2026-10-02 05:13:29 UTC 137,121 bytes, 2,776 lines download raw bytes

1<!doctype html>
2<html lang="nl">
3<head >
4    <meta charset="utf-8"/>
5<meta name="title" content="Homepage  | Uitgeverij Pluim"/>
6<meta name="description" content="Uitgeverij Pluim"/>
7<meta name="keywords" content="boeken,uitgeverij,pluim"/>
8<meta name="robots" content="INDEX,FOLLOW"/>
9<meta name="viewport" content="width=device-width, initial-scale=1"/>
10<title>Homepage  | Uitgeverij Pluim</title>
11        <link rel="stylesheet" type="text/css" defer="defer" href="https://uitgeverijpluim.nl/static/version1790668939/frontend/Pluim/Hyva/nl_NL/css/styles.css" />
12<script type="text/javascript" src="https://uitgeverijpluim.nl/static/version1790668939/frontend/Pluim/Hyva/nl_NL/Smile_ElasticsuiteTracker/js/tracking.js"></script>
12
13<link rel="icon" type="image/x-icon" href="https://uitgeverijpluim.nl/media/favicon/websites/1/logo_sxfncc_1.jpg" />
14<link rel="shortcut icon" type="image/x-icon" href="https://uitgeverijpluim.nl/media/favicon/websites/1/logo_sxfncc_1.jpg" />
15<link rel="dns-prefetch" href="https://acdn.adnxs.com">
16<link rel="dns-prefetch" href="https://ib.adnxs.com">
17
18<!-- Xandr Universal Pixel - Optimized -->
19<script async defer>
20(function() {
21  // Initialize pixie stub immediately
22  !function(e,i){
23    if(!e.pixie){
24      var n=e.pixie=function(e,i,a){
25        n.actionQueue.push({action:e,actionValue:i,params:a})
26      };
27      n.actionQueue=[];
28      var a=i.createElement("script");
29      a.async=!0;
30      a.src="https://acdn.adnxs.com/dmp/up/pixie.js";
31      var t=i.getElementsByTagName("head")[0];
32      t.insertBefore(a,t.firstChild)
33    }
34  }(window,document);
35  
36  // Queue consent and initialization
37  pixie('consent', 'default', { 'ad_storage': 'granted', 'wait_for_update': 1000});
38  pixie('init', 'cf677036-49f6-4ee6-8255-72e7d7a8944c');
39  pixie('event', 'PageView');
40})();
41</script>
41    
42<script nonce="MzJpbnJpdDZjbzFsOHF3bG5pazUydXh3b3RsYnU3bHk=">
43    window.YIREO_GOOGLETAGMANAGER2_ENABLED = true;
44    (function (events) {
45        const initYireoGoogleTagManager2 = function () {
46            events.forEach(function (eventType) {
47                window.removeEventListener(eventType, initYireoGoogleTagManager2);
48            });
49
50                        (function (w, d, s, l, i) {
51                w[l] = w[l] || [];
52                w[l].push({'gtm.start': new Date().getTime(), event: 'gtm.js'});
53                var f = d.getElementsByTagName(s)[0],
54                    j = d.createElement(s),
55                    dl = l != 'dataLayer' ? '&l=' + l : '';
56                j.async = true;
57                j.src = 'https\u003A\u002F\u002Fwww.googletagmanager.com' + '/gtm.js?id=' + i + dl;
58                f.parentNode.insertBefore(j, f);
59            })(window, document, 'script', 'dataLayer', 'GTM\u002DWDGVFB9');
60                    };
61
62        events.forEach(function (eventType) {
63            window.addEventListener(eventType, initYireoGoogleTagManager2, {once: true, passive: true})
64        });
65    })(['load', 'keydown', 'mouseover', 'scroll', 'touchstart', 'wheel']);
66
67    </script>
vendor: 1 bytes, line 67
67
68<script>
69    (function( hyva, undefined ) {
70
71        function lifetimeToExpires(options, defaults) {
72
73            const lifetime = options.lifetime || defaults.lifetime;
74
75            if (lifetime) {
76                const date = new Date;
77                date.setTime(date.getTime() + lifetime * 1000);
78                return date;
79            }
80
81            return null;
82        }
83
84        function generateRandomString() {
85
86            const allowedCharacters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
87                length = 16;
88
89            let formKey = '',
90                charactersLength = allowedCharacters.length;
91
92            for (let i = 0; i < length; i++) {
93                formKey += allowedCharacters[Math.round(Math.random() * (charactersLength - 1))]
94            }
95
96            return formKey;
97        }
98
99        const sessionCookieMarker = {noLifetime: true}
100
101        const cookieTempStorage = {};
102
103        const internalCookie = {
104            get(name) {
105                const v = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
106                return v ? v[2] : null;
107            },
108            set(name, value, days, skipSetDomain) {
109                let expires,
110                    path,
111                    domain,
112                    secure,
113                    samesite;
114
115                const defaultCookieConfig = {
116                    expires: null,
117                    path: '/',
118                    domain: null,
119                    secure: false,
120                    lifetime: null,
121                    samesite: 'lax'
122                };
123
124                const cookieConfig = window.COOKIE_CONFIG || {};
125
126                expires = days && days !== sessionCookieMarker
127                    ? lifetimeToExpires({lifetime: 24 * 60 * 60 * days, expires: null}, defaultCookieConfig)
128                    : lifetimeToExpires(window.COOKIE_CONFIG, defaultCookieConfig) || defaultCookieConfig.expires;
129
130                path = cookieConfig.path || defaultCookieConfig.path;
131                domain = !skipSetDomain && (cookieConfig.domain || defaultCookieConfig.domain);
132                secure = cookieConfig.secure || defaultCookieConfig.secure;
133                samesite = cookieConfig.samesite || defaultCookieConfig.samesite;
134
135                document.cookie = name + "=" + encodeURIComponent(value) +
136                    (expires && days !== sessionCookieMarker ? '; expires=' + expires.toGMTString() : '') +
137                    (path ? '; path=' + path : '') +
138                    (domain ? '; domain=' + domain : '') +
139                    (secure ? '; secure' : '') +
140                    (samesite ? '; samesite=' + samesite : 'lax');
141            },
142            isWebsiteAllowedToSaveCookie() {
143                const allowedCookies = this.get('user_allowed_save_cookie');
144                if (allowedCookies) {
145                    const allowedWebsites = JSON.parse(unescape(allowedCookies));
146
147                    return allowedWebsites[CURRENT_WEBSITE_ID] === 1;
148                }
149                return false;
150            },
151            getGroupByCookieName(name) {
152                const cookieConsentConfig = window.cookie_consent_config || {};
153                let group = null;
154                for (let prop in cookieConsentConfig) {
155                    if (!cookieConsentConfig.hasOwnProperty(prop)) continue;
156                    if (cookieConsentConfig[prop].includes(name)) {
157                        group = prop;
158                        break;
159                    }
160                }
161                return group;
162            },
163            isCookieAllowed(name) {
164                const cookieGroup = this.getGroupByCookieName(name);
165                return cookieGroup
166                    ? window.cookie_consent_groups[cookieGroup]
167                    : this.isWebsiteAllowedToSaveCookie();
168            },
169            saveTempStorageCookies() {
170                for (const [name, data] of Object.entries(cookieTempStorage)) {
171                    if (this.isCookieAllowed(name)) {
172                        this.set(name, data['value'], data['days'], data['skipSetDomain']);
173                        delete cookieTempStorage[name];
174                    }
175                }
176            }
177        };
178
179        hyva.getCookie = (name) => {
180            const cookieConfig = window.COOKIE_CONFIG || {};
181
182            if (cookieConfig.cookie_restriction_enabled && ! internalCookie.isCookieAllowed(name)) {
183                return cookieTempStorage[name] ? cookieTempStorage[name]['value'] : null;
184            }
185
186            return internalCookie.get(name);
187        }
188
189        hyva.setCookie = (name, value, days, skipSetDomain) => {
190            const cookieConfig = window.COOKIE_CONFIG || {};
191
192            if (cookieConfig.cookie_restriction_enabled && ! internalCookie.isCookieAllowed(name)) {
193                cookieTempStorage[name] = {value, days, skipSetDomain};
194                return;
195            }
196            return internalCookie.set(name, value, days, skipSetDomain);
197        }
198
199
200        hyva.setSessionCookie = (name, value, skipSetDomain) => {
201            return hyva.setCookie(name, value, sessionCookieMarker, skipSetDomain)
202        }
203
204        hyva.getBrowserStorage = () => {
205            const browserStorage = window.localStorage || window.sessionStorage;
206            if (!browserStorage) {
207                console.warn('Browser Storage is unavailable');
208                return false;
209            }
210            try {
211                browserStorage.setItem('storage_test', '1');
212                browserStorage.removeItem('storage_test');
213            } catch (error) {
214                console.warn('Browser Storage is not accessible', error);
215                return false;
216            }
217            return browserStorage;
218        }
219
220        hyva.postForm = (postParams) => {
221            const form = document.createElement("form");
222
223            let data = postParams.data;
224
225            if (! postParams.skipUenc && ! data.uenc) {
226                data.uenc = btoa(window.location.href);
227            }
228            form.method = "POST";
229            form.action = postParams.action;
230
231            Object.keys(postParams.data).map(key => {
232                const field = document.createElement("input");
233                field.type = 'hidden'
234                field.value = postParams.data[key];
235                field.name = key;
236                form.appendChild(field);
237            });
238
239            const form_key = document.createElement("input");
240            form_key.type = 'hidden';
241            form_key.value = hyva.getFormKey();
242            form_key.name="form_key";
243            form.appendChild(form_key);
244
245            document.body.appendChild(form);
246
247            form.submit();
248        }
249
250        hyva.getFormKey = function () {
251            let formKey = hyva.getCookie('form_key');
252
253            if (!formKey) {
254                formKey = generateRandomString();
255                hyva.setCookie('form_key', formKey);
256            }
257
258            return formKey;
259        }
260
261        hyva.formatPrice = (value, showSign, options = {}) => {
262            const groupSeparator = options.groupSeparator;
263            const decimalSeparator = options.decimalSeparator
264            delete options.groupSeparator;
265            delete options.decimalSeparator;
266            const formatter = new Intl.NumberFormat(
267                'nl\u002DNL',
268                Object.assign({
269                    style: 'currency',
270                    currency: 'EUR',
271                    signDisplay: showSign ? 'always' : 'auto'
272                }, options)
273            );
274            return (typeof Intl.NumberFormat.prototype.formatToParts === 'function') ?
275                formatter.formatToParts(value).map(({type, value}) => {
276                    switch (type) {
277                        case 'currency':
278                            return '\u20AC' || value;
279                        case 'minusSign':
280                            return '- ';
281                        case 'plusSign':
282                            return '+ ';
283                        case 'group':
284                            return groupSeparator !== undefined ? groupSeparator : value;
285                        case 'decimal':
286                            return decimalSeparator !== undefined ? decimalSeparator : value;
287                        default :
288                            return value;
289                    }
290                }).reduce((string, part) => string + part) :
291                formatter.format(value);
292        }
293
294        const formatStr = function (str, nStart) {
295            const args = Array.from(arguments).slice(2);
296
297            return str.replace(/(%+)([0-9]+)/g, (m, p, n) => {
298                const idx = parseInt(n) - nStart;
299
300                if (args[idx] === null || args[idx] === void 0) {
301                    return m;
302                }
303                return p.length % 2
304                    ? p.slice(0, -1).replace('%%', '%') + args[idx]
305                    : p.replace('%%', '%') + n;
306            })
307        }
308
309        hyva.str = function (string) {
310            const args = Array.from(arguments);
311            args.splice(1, 0, 1);
312
313            return formatStr.apply(undefined, args);
314        }
315
316        hyva.strf = function () {
317            const args = Array.from(arguments);
318            args.splice(1, 0, 0);
319
320            return formatStr.apply(undefined, args);
321        }
322
323        /**
324         * Take a html string as `content` parameter and
325         * extract an element from the DOM to replace in
326         * the current page under the same selector,
327         * defined by `targetSelector`
328         */
329        hyva.replaceDomElement = (targetSelector, content) => {
330            // Parse the content and extract the DOM node using the `targetSelector`
331            const parser = new DOMParser();
332            const doc = parser.parseFromString(content, 'text/html');
333            const contentNode = doc.querySelector(targetSelector);
334
335            // Bail if content or target can't be found
336            if (!contentNode || !document.querySelector(targetSelector)) {
337                return;
338            }
339
340                        hyva.activateScripts(contentNode);
341            
342            // Replace the old DOM node with the new content
343            document.querySelector(targetSelector).replaceWith(contentNode);
344
345            // Reload customerSectionData and display cookie-messages if present
346            window.dispatchEvent(new CustomEvent("reload-customer-section-data"));
347            hyva.initMessages();
348        }
349
350        hyva.removeScripts = (contentNode) => {
351            const scripts = contentNode.getElementsByTagName('script');
352            for (let i = 0; i < scripts.length; i++) {
353                scripts[i].parentNode.removeChild(scripts[i]);
354            }
355            const templates = contentNode.getElementsByTagName('template');
356            for (let i = 0; i < templates.length; i++) {
357                const container = document.createElement('div');
358                container.innerHTML = templates[i].innerHTML;
359                hyva.removeScripts(container);
360                templates[i].innerHTML = container.innerHTML;
361            }
362        }
363
364        hyva.activateScripts = (contentNode) => {
365            // Create new array from HTMLCollection to avoid mutation of collection while manipulating the DOM.
366            const scripts = Array.from(contentNode.getElementsByTagName('script'));
367
368            // Iterate over all script tags to duplicate+inject each into the head
369            for (const original of scripts) {
370                const script = document.createElement('script');
371                original.type && (script.type = original.type);
372                script.innerHTML = original.innerHTML;
373
374                // Remove the original (non-executing) script from the contentNode
375                original.parentNode.removeChild(original)
376
377                // Add script to head
378                document.head.appendChild(script);
379            }
380
381            return contentNode;
382        }
383
384        const replace = {['+']: '-', ['/']: '_', ['=']: ','};
385        hyva.getUenc = () => btoa(window.location.href).replace(/[+/=]/g, match => replace[match]);
386
387        let currentTrap;
388
389        const focusableElements = (rootElement) => {
390            const selector = 'button, [href], input, select, textarea, details, [tabindex]:not([tabindex="-1"]';
391            return Array.from(rootElement.querySelectorAll(selector))
392                .filter(el => {
393                    return el.style.display !== 'none'
394                        && !el.disabled
395                        && el.tabIndex !== -1
396                        && (el.offsetWidth || el.offsetHeight || el.getClientRects().length)
397                })
398        }
399
400        const focusTrap = (e) => {
401            const isTabPressed = e.key === 'Tab' || e.keyCode === 9;
402            if (!isTabPressed) return;
403
404            const focusable = focusableElements(currentTrap)
405            const firstFocusableElement = focusable[0]
406            const lastFocusableElement = focusable[focusable.length - 1]
407
408            e.shiftKey
409                ? document.activeElement === firstFocusableElement && (lastFocusableElement.focus(), e.preventDefault())
410                : document.activeElement === lastFocusableElement && (firstFocusableElement.focus(), e.preventDefault())
411        };
412
413        hyva.releaseFocus = (rootElement) => {
414            if (currentTrap && (!rootElement || rootElement === currentTrap)) {
415                currentTrap.removeEventListener('keydown', focusTrap)
416                currentTrap = null
417            }
418        }
419        hyva.trapFocus = (rootElement) => {
420            if (!rootElement) return;
421            hyva.releaseFocus()
422            currentTrap = rootElement
423            rootElement.addEventListener('keydown', focusTrap)
424            const firstElement = focusableElements(rootElement)[0]
425            firstElement && firstElement.focus()
426        }
427
428        hyva.safeParseNumber = (rawValue) => {
429            const number = rawValue ? parseFloat(rawValue) : null;
430
431            return Array.isArray(number) || isNaN(number) ? rawValue : number;
432        }
433
434        const toCamelCase = s => s.split('_').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join('');
435        hyva.createBooleanObject = (name, value = false, additionalMethods = {}) => {
436
437            const camelCase = toCamelCase(name);
438            const key = '__hyva_bool_' + name
439
440            return new Proxy(Object.assign(
441                additionalMethods,
442                {
443                    [key]: !!value,
444                    [name]() {return !!this[key]},
445                    ['!' + name]() {return !this[key]}, // @deprecated This does not work with non-CSP Alpine
446                    ['not' + camelCase]() {return !this[key]},
447                    ['toggle' + camelCase]() {this[key] = !this[key]},
448                    [`set${camelCase}True`]() {this[key] = true},
449                    [`set${camelCase}False`]() {this[key] = false},
450                }
451            ), {
452                set(target, prop, value) {
453                    return prop === name
454                        ? (target[key] = !!value)
455                        : Reflect.set(...arguments);
456                }
457            })
458        }
459
460                hyva.alpineInitialized = (fn) => window.addEventListener('alpine:initialized', fn, {once: true})
461        window.addEventListener('alpine:init', () => Alpine.data('{}', () => ({})), {once: true});
462                window.addEventListener('user-allowed-save-cookie', () => internalCookie.saveTempStorageCookies())
463
464    }( window.hyva = window.hyva || {} ));
465    //# sourceURL=hyva.js
466</script>
vendor: 1 bytes, line 466
466
467<script>
468    if (HTMLScriptElement.supports && HTMLScriptElement.supports('speculationrules') && document.body) {
469        const specScript = document.createElement('script');
470        specScript.type = 'speculationrules';
471        specRules = {
472            'prerender': [{
473                'where': {
474                    'and': [
475                        { 'href_matches': '/*' },
476                        { "not": { "href_matches": "/(customer|checkout|wishlist)*" } },
477                        { 'not': { 'selector_matches': '.do-not-prerender'}},
478                    { 'not': { 'selector_matches': '[rel~=nofollow]' } }
479                    ]
480                },
481                'eagerness': 'moderate'
482            }]
483        };
484        specScript.textContent = JSON.stringify(specRules);
485        document.body.append(specScript);
486    }
487</script>
487
488
489<link rel="canonical" href="https://uitgeverijpluim.nl/" />
490</head>
491<body id="html-body" class="prismicio-direct-singleton page-layout-3columns">
vendor: 68 bytes, lines 491-492
491
492<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=
492GTM-WDGVFB9
vendor: 83 bytes, line 492
492" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
493<script>
494    var BASE_URL = 'https://uitgeverijpluim.nl/';
495    var THEME_PATH = 'https://uitgeverijpluim.nl/static/version1790668939/frontend/Pluim/Hyva/nl_NL';
496    var COOKIE_CONFIG = {
497        "expires": null,
498        "path": "\u002F",
499        "domain": ".uitgeverijpluim.nl",
500        "secure": false,
501        "lifetime": "3600",
502        "cookie_restriction_enabled": false    };
503    var CURRENT_STORE_CODE = 'default';
504    var CURRENT_WEBSITE_ID = '1';
505
506    window.hyva = window.hyva || {}
507
508    window.cookie_consent_groups = window.cookie_consent_groups || {}
509    window.cookie_consent_groups['necessary'] = true;
510
511    window.cookie_consent_config = window.cookie_consent_config || {};
512    window.cookie_consent_config['necessary'] = [].concat(
513        window.cookie_consent_config['necessary'] || [],
514        [
515            'user_allowed_save_cookie',
516            'form_key',
517            'mage-messages',
518            'private_content_version',
519            'mage-cache-sessid'
520        ]
521    );
522</script>
522
523<input name="form_key" type="hidden" value="K8rVxMaAEFHP72U6" />
523<script nonce="MzJpbnJpdDZjbzFsOHF3bG5pazUydXh3b3RsYnU3bHk=">
524    function yireoGoogleTagManager2Pusher(eventData, message, callback) {
525        window.YIREO_GOOGLETAGMANAGER2_PAST_EVENTS = window.YIREO_GOOGLETAGMANAGER2_PAST_EVENTS || [];
526
527        function doCallback(cb) {
528            if (undefined === cb) {
529                return;
530            }
531
532            cb();
533        }
534
535
536        const copyEventData = Object.assign({}, eventData);
537        let metaData = {};
538        if (copyEventData.meta) {
539            metaData = copyEventData.meta;
540            delete copyEventData.meta;
541        }
542
543        const eventHash = btoa(encodeURIComponent(JSON.stringify(copyEventData)));
544        if (window.YIREO_GOOGLETAGMANAGER2_PAST_EVENTS.includes(eventHash)) {
545            yireoGoogleTagManager2Logger('Warning: Event already triggered', eventData);
546            doCallback(callback);
547            return;
548        }
549
550        if (metaData && metaData.allowed_pages && metaData.allowed_pages.length > 0
551            && false === metaData.allowed_pages.some(page => window.location.pathname.includes(page))) {
552            yireoGoogleTagManager2Logger('Warning: Skipping event, not in allowed pages', window.location.pathname, eventData);
553            doCallback(callback);
554            return;
555        }
556
557        if (metaData && metaData.allowed_events && metaData.allowed_events.length > 0) {
558            for (const [allowedEventKey, allowedEvent] of Object.entries(metaData.allowed_events)) {
559                window.addEventListener(allowedEvent, function () {
560                    const eventDataCopy = Object.assign({}, eventData);
561                    eventDataCopy.meta.allowed_events = false;
562                    yireoGoogleTagManager2Pusher(eventDataCopy, 'push (allowed event "' + eventData.event + '") [script-pusher]');
563                });
564            }
565
566            yireoGoogleTagManager2Logger('Warning: Skipping event, not in allowed events', window.location.pathname, eventData);
567            doCallback(callback);
568            return;
569        }
570
571        if (!message) {
572            message = 'push (unknown) [unknown]';
573        }
574
575        yireoGoogleTagManager2Logger(message, eventData);
576        window.dataLayer = window.dataLayer || [];
577
578        if (eventData.ecommerce) {
579            window.dataLayer.push({ecommerce: null});
580        }
581
582        try {
583            window.dataLayer.push(eventData);
584            window.YIREO_GOOGLETAGMANAGER2_PAST_EVENTS.push(eventHash);
585        } catch(error) {
586            doCallback(callback);
587        }
588
589        doCallback(callback);
590    }
591</script>
vendor: 1 bytes, line 591
591
592<script nonce="MzJpbnJpdDZjbzFsOHF3bG5pazUydXh3b3RsYnU3bHk=">
593    function yireoGoogleTagManager2Logger(...args) {
594        const debug = window.YIREO_GOOGLETAGMANAGER2_DEBUG || false;
595        if (false === debug) {
596            return;
597        }
598
599        var color = 'gray';
600        if (args[0].toLowerCase().startsWith('push')) {
601            color = 'green';
602        }
603
604        if (args[0].toLowerCase().startsWith('warning')) {
605            color = 'orange';
606        }
607
608        var css = 'color:white; background-color:' + color + '; padding:1px;'
609        console.log('%cYireo_GoogleTagManager2', css, ...args);
610    }
611</script>
611
612
613<script>
614    (function(hyva) {
615                const formValidationRules = {
616            required(value, options, field, context) {
617                const el = field.element.type === 'hidden' ? createTextInputFrom(field.element) : field.element,
618                    fieldName = getFieldName(field),
619                    msg = fieldName
620                        ? hyva.str('Het\u0020veld\u0020\u00251\u0020is\u0020verplicht.', fieldName)
621                        : 'Dit\u0020is\u0020een\u0020verplicht\u0020veld.';
622
623                if (el.type === 'radio' || el.type === 'checkbox') {
624                    return (value === undefined || value.length === 0) ? msg : true;
625                }
626
627                el.setAttribute('required', '');
628                el.checkValidity();
629
630                return el.validity.valueMissing ? msg : true;
631            },
632            maxlength(value, options, field, context) {
633                const n = Number(options)
634                if (value.length > n) {
635                    const fieldName = getFieldName(field);
636                    return fieldName
637                        ? n === 1
638                            ? hyva.str('Het\u0020veld\u0020\u00251\u0020mag\u0020niet\u0020langer\u0020zijn\u0020dan\u00201\u0020teken.', fieldName)
639                            : hyva.str('Het\u0020veld\u0020\u00251\u0020mag\u0020niet\u0020langer\u0020zijn\u0020dan\u0020\u00252\u0020tekens.', fieldName, options)
640                        : n === 1
641                            ? 'Voer\u0020niet\u0020meer\u0020dan\u00201\u0020teken\u0020in.'
642                            : hyva.strf('Voer\u0020niet\u0020meer\u0020dan\u0020\u00250\u0020tekens\u0020in.', options);
643                }
644                return true;
645            },
646            minlength(value, options, field, context) {
647                const n = Number(options)
648                if (value.length > 0 && value.length < n) {
649                    const fieldName = getFieldName(field);
650                    return fieldName
651                        ? n === 1
652                            ? hyva.str('Het\u0020veld\u0020\u00251\u0020moet\u0020minimaal\u00201\u0020teken\u0020lang\u0020zijn.', fieldName)
653                            : hyva.str('Het\u0020veld\u0020\u00251\u0020moet\u0020minimaal\u0020\u00252\u0020tekens\u0020lang\u0020zijn.', fieldName, options)
654                        : n === 1
655                            ? 'Voer\u0020minimaal\u00201\u0020teken\u0020in.'
656                            : hyva.strf('Voer\u0020minimaal\u0020\u00250\u0020tekens\u0020in.', options);
657                }
658                return true;
659            },
660            max(value, options, field, context) {
661                field.element.setAttribute('max', options);
662                field.element.checkValidity();
663                if (field.element.validity.rangeOverflow) {
664                    const fieldName = getFieldName(field);
665                    return fieldName
666                        ? hyva.str('Het\u0020veld\u0020\u00251\u0020moet\u0020een\u0020waarde\u0020kleiner\u0020dan\u0020of\u0020gelijk\u0020aan\u0020\u0022\u00252\u0022\u0020bevatten.', fieldName, options)
667                        : hyva.strf('Voer\u0020een\u0020waarde\u0020kleiner\u0020dan\u0020of\u0020gelijk\u0020aan\u0020\u0022\u00250\u0022\u0020in.', options);
668                }
669                return true;
670            },
671            min(value, options, field, context) {
672                field.element.setAttribute('min', options);
673                field.element.checkValidity();
674                if (field.element.validity.rangeUnderflow) {
675                    const fieldName = getFieldName(field);
676                    return fieldName
677                        ? hyva.str('Het\u0020veld\u0020\u00251\u0020moet\u0020een\u0020waarde\u0020groter\u0020dan\u0020of\u0020gelijk\u0020aan\u0020\u0022\u00252\u0022\u0020bevatten.', fieldName, options)
678                        : hyva.strf('Voer\u0020een\u0020waarde\u0020groter\u0020dan\u0020of\u0020gelijk\u0020aan\u0020\u0022\u00250\u0022\u0020in.', options);
679                }
680                return true;
681            },
682            step(value, options, field, context) {
683                field.element.setAttribute('step', options);
684                field.element.checkValidity();
685                if (field.element.validity.stepMismatch) {
686                    const val = Number(value),
687                        step = Number(options),
688                        fieldName = getFieldName(field),
689                        nearestLowerValue = Math.floor(val / step) * step,
690                        nearestUpperValue = Math.ceil(val / step) * step;
691                    return fieldName
692                        ? hyva.str('Het\u0020veld\u0020\u00251\u0020is\u0020ongeldig.\u0020De\u0020twee\u0020dichtstbijzijnde\u0020geldige\u0020waarden\u0020zijn\u0020\u0022\u00252\u0022\u0020en\u0020\u0022\u00253\u0022.', fieldName, nearestLowerValue, nearestUpperValue)
693                        : hyva.strf('Voer\u0020een\u0020geldige\u0020waarde\u0020in.\u0020De\u0020twee\u0020dichtstbijzijnde\u0020geldige\u0020waarden\u0020zijn\u0020\u0022\u00250\u0022\u0020en\u0020\u0022\u00251\u0022.', nearestLowerValue, nearestUpperValue);
694                }
695                return true;
696            },
697            pattern(value, options, field, context) {
698                field.element.setAttribute('pattern', options);
699                field.element.checkValidity();
700                if (field.element.validity.patternMismatch) {
701                    const fieldName = getFieldName(field);
702                    return fieldName
703                        ? field.element.pattern
704                            ? hyva.str('Het\u0020veld\u0020\u00251\u0020moet\u0020overeenkomen\u0020met\u0020het\u0020gevraagde\u0020formaat\u003A\u0020\u00252.', fieldName, field.element.pattern)
705                            : hyva.str('Het\u0020veld\u0020\u00251\u0020moet\u0020overeenkomen\u0020met\u0020het\u0020gevraagde\u0020formaat.', fieldName)
706                        : field.element.pattern
707                            ? hyva.strf('Houd\u0020je\u0020aan\u0020het\u0020gevraagde\u0020formaat\u003A\u0020\u00250.', field.element.pattern)
708                            : 'Houd\u0020je\u0020aan\u0020het\u0020gevraagde\u0020formaat.';
709                }
710                return true;
711            },
712            email(value, options, field, context) {
713                                const rule = /^([a-z0-9,!\#\$%&'\*\+\/=\?\^_`\{\|\}~-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z0-9,!\#\$%&'\*\+\/=\?\^_`\{\|\}~-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*@([a-z0-9-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z0-9-]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*\.(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]){2,})$/i;
714                if (value.length > 0 && !rule.test(value)) {
715                    const fieldName = getFieldName(field);
716                    return fieldName
717                        ? hyva.str('Het\u0020veld\u0020\u00251\u0020moet\u0020een\u0020geldig\u0020e\u002Dmailadres\u0020bevatten\u0020\u0028bijv.\u0020naam\u0040domein.nl\u0029.', fieldName)
718                        : 'Voer\u0020een\u0020geldig\u0020e\u002Dmailadres\u0020in\u0020\u0028Bijv.\u003A\u0020johndoe\u0040domain.com\u0029.';
719                }
720                return true;
721            },
722            password(value, options, field, context) {
723                const rule = /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$/;
724                if (value.length > 0 && !rule.test(value)) {
725                    const fieldName = getFieldName(field);
726                    return fieldName
727                        ? hyva.str('Het\u0020veld\u0020\u00251\u0020moet\u0020minimaal\u0020\u00E9\u00E9n\u0020hoofdletter,\u0020\u00E9\u00E9n\u0020kleine\u0020letter,\u0020\u00E9\u00E9n\u0020cijfer\u0020en\u0020\u00E9\u00E9n\u0020speciaal\u0020teken\u0020bevatten\u0020\u0028\u0023\u003F\u0021\u0040\u0024\u0025\u005E\u0026\u002A\u002D\u0029', fieldName)
728                        : 'Voer\u0020minimaal\u0020\u00E9\u00E9n\u0020hoofdletter,\u0020\u00E9\u00E9n\u0020kleine\u0020letter,\u0020\u00E9\u00E9n\u0020cijfer\u0020en\u0020\u00E9\u00E9n\u0020speciaal\u0020teken\u0020in\u0020\u0028\u0023\u003F\u0021\u0040\u0024\u0025\u005E\u0026\u002A\u002D\u0029';
729                }
730                return true;
731            },
732            equalTo(value, options, field, context) {
733                const dependencyField = context.fields[options].element;
734                if (value !== dependencyField.value) {
735                    const dependencyFieldName =
736                        dependencyField.label ||
737                        dependencyField.title ||
738                        (dependencyField.labels && dependencyField.labels[0] && dependencyField.labels[0].innerText) ||
739                        dependencyField.name,
740                        fieldName = getFieldName(field);
741                    return fieldName
742                        ? hyva.str('De\u0020waarde\u0020van\u0020\u00251\u0020moet\u0020gelijk\u0020zijn\u0020aan\u0020\u0022\u00252\u0022.', fieldName, dependencyFieldName)
743                        : hyva.strf('De\u0020waarde\u0020van\u0020dit\u0020veld\u0020moet\u0020gelijk\u0020zijn\u0020aan\u0020\u0022\u00250\u0022.', dependencyFieldName);
744                }
745                return true;
746            }
747        };
748
749                function raceSome(promises, pred) {
750            return new Promise((resolve, reject) => {
751
752                if (promises.length === 0) {
753                    return resolve();
754                }
755
756                let settled = false, nDone = 0;
757
758                const resolveIf = v => {
759                    if (!settled && (pred(v) || ++nDone === promises.length)) {
760                        settled = true;
761                        resolve(v);
762                    }
763                    return v;
764                }
765
766                promises.map(promise => {
767                    promise.then(resolveIf).catch(reason => {
768                        settled = true;
769                        reject(reason)
770                    });
771                    return promise;
772                });
773            });
774        }
775
776        const INPUT_ATTRIBUTE_RULES = {min: 'min', max: 'max', required: 'required', minlength: 'minlength', maxlength: 'maxlength', step: 'step', pattern: 'pattern'}
777        const INPUT_TYPE_RULES = {email: 'email'}
778
779        function getRules(element) {
780            let rules = {};
781            Object.keys(INPUT_ATTRIBUTE_RULES).forEach(attrName => {
782                if (element.hasAttribute(attrName)) {
783                    rules[INPUT_ATTRIBUTE_RULES[attrName]] = element.getAttribute(attrName);
784                }
785            })
786            if (INPUT_TYPE_RULES[element.type]) {
787                rules[INPUT_TYPE_RULES[element.type]] = true;
788            }
789
790            if (element.dataset.validate) {
791                try {
792                    Object.assign(rules, JSON.parse(element.dataset.validate));
793                } catch (error) {
794                    console.error('Validator error. Cannot parse data-validate attribute of element:\n', element);
795                }
796            }
797
798            return rules;
799        }
800
801        function isInvalidRuleResult(ruleState) {
802            return typeof ruleState === 'string' || !ruleState || (ruleState.type && ruleState.content);
803        }
804
805        async function runValidateFn(rule, options, value, field) {
806            return formValidationRules[rule](value, options, field, this);
807        }
808
809        function generateId() {
810            let id;
811            do {
812                id = `${this.idPrefix}-${++this.idSeq}`;
813            } while (document.getElementById(id));
814            return id;
815        }
816
817        function isVisible(element) {
818            const el = element.type !== 'hidden' ? element : (element.parentElement || {});
819            return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length)
820        }
821
822        function elementWillValidate(element) {
823            return (element.willValidate || element.type === 'hidden')
824                && element.tagName !== 'BUTTON'
825                && element.disabled === false
826                && !(element.tagName === 'INPUT' && element.type === 'submit')
827                && (element.hasAttribute('data-validate-hidden') || isVisible(element))
828        }
829
830        function createMessageContainer(el, fieldWrapperClassName) {
831            if (! el.parentElement) {
832                return;
833            }
834            const refocus = document.activeElement === el;
835            const wrapper = document.createElement('div');
836            wrapper.classList.add.apply(wrapper.classList, fieldWrapperClassName.split(' '));
837            el.parentElement.insertBefore(wrapper, el);
838            wrapper.appendChild(el);
839            refocus && document.activeElement !== el && el.focus();
840            return wrapper;
841        }
842
843        function containerNotFound(selector, el) {
844            const msg = `Cannot find message container element ${selector} of ${el.name}`;
845            console.error(msg, el);
846            throw msg;
847        }
848
849        function createTextInputFrom(el) {
850            const text = document.createElement('INPUT');
851            text.type = 'text';
852            text.value = el.value;
853            return text;
854        }
855
856        function classNamesToSelector(classNames) {
857            return classNames.split(' ')
858                .filter(className => className.length > 0)
859                .map(className => `.${className}`)
860                .join('')
861        }
862
863        function hasMessagesWrapper(field, messagesWrapperClassName) {
864            return this.getMessageContainer(field).querySelector(classNamesToSelector(messagesWrapperClassName));
865        }
866
867        function getMessagesWrapper(field, messagesWrapperClassName) {
868            if (hasMessagesWrapper.call(this, field, messagesWrapperClassName)) {
869                return this.getMessageContainer(field).querySelector(classNamesToSelector(messagesWrapperClassName));
870            }
871
872            const msgWrapper = document.createElement('ul');
873            const msgId = generateId.call(this);
874            msgWrapper.id = msgId;
875            field.element.setAttribute('aria-errormessage', msgId);
876            field.element.setAttribute('aria-describedby', msgId);
877            msgWrapper.classList.add.apply(msgWrapper.classList, messagesWrapperClassName.split(' '));
878            if (field.validateOnChange) {
879                msgWrapper.setAttribute('aria-live', 'polite');
880            }
881            this.getMessageContainer(field).appendChild(msgWrapper);
882
883            return msgWrapper;
884        }
885
886        function getCheckedValues(field) {
887            const name = field.element.name.replace(/([\\"])/g, '\\$1');
888            const elements = field.element.form.querySelectorAll('input[name="' + name + '"]:checked');
889            return Array.from(elements).map(el => el.value);
890        }
891
892        function escapeHtml(s) {
893            const div = document.createElement('div')
894            div.innerText = s;
895            return div.innerHTML;
896        }
897
898        
899        function formValidation(form, options = {}) {
900            const formElement = form || this.$el;
901
902            if (formElement.dataset && formElement.dataset.options) {
903                try {
904                    options = Object.assign(options, JSON.parse(formElement.dataset.options || '{}'));
905                } catch (e) {
906                    throw new Error('Cannot read the form options from the data-options attribute: not val
906id JSON');
907                }
908            }
909
910            // Disable browser default validation
911            if (formElement.tagName === 'FORM') {
912                formElement.setAttribute('novalidate', '');
913            } else {
914                console.error('formValidation can be initialized only on FORM element', formElement);
915                return;
916            }
917
918            options = Object.assign({
919                fieldWrapperClassName: 'field field-reserved',
920                messagesWrapperClassName: 'messages',
921                validClassName: 'field-success',
922                invalidClassName: 'field-error',
923                pageMessagesWrapperSelector: null,
924                scrollToFirstError: true,
925            }, options || {});
926
927            return {
928                state: {
929                    valid: false,
930                },
931                isSubmitting: false,
932                fields: {},
933                idSeq: 0,
934                idPrefix: formElement.id || 'vld-msg',
935                setupFields(elements) {
936                    this.fields = {};
937                    Array.from(elements).forEach(element => {
938                        if (elementWillValidate(element)) {
939                            this.setupField(element);
940                        }
941                    });
942                },
943                setupField(element) {
944                    if (! element) return;
945                    const onChange = !!element.dataset.onChange;
946                    if (elementWillValidate(element)) {
947                        const rules = getRules(element);
948                        if (Object.keys(rules).length > 0) {
949                            if (this.fields[element.name]) {
950                                Object.assign(this.fields[element.name].rules, rules);
951                            } else {
952                                this.fields[element.name] = {
953                                    element,
954                                    rules: rules,
955                                    validateOnChange: onChange,
956                                    state: {
957                                        valid: null,
958                                        rules: {}
959                                    }
960                                }
961                            }
962                        }
963                    } else {
964                        console.error('Element will not validate', element);
965                    }
966                },
967                onSubmit(event) {
968                    if (event.target.tagName === 'FORM') {
969                        event.preventDefault();
970
971                        if (this.isSubmitting) return;
972                        this.isSubmitting = true;
973
974                        this.validate()
975                            .then(() => event.target.submit())
976                            .catch(invalidElements => {
977                                this.isSubmitting = false;
978                            });
979                    }
980                },
981                onChange(event) {
982                    event.target.dataset.onChange = 'true';
983                    if (!Object.keys(this.fields).length) {
984                        this.setupFields(formElement.elements);
985                    }
986                    if (!Object.keys(this.fields).includes(event.target.name)) {
987                        this.setupField(event.target);
988                    }
989                    const field = this.fields[event.target.name];
990
991                    this.validateField(field);
992                    field && field.element.removeAttribute('data-on-change')
993                },
994                validateSafe() {
995                    return new Promise(resolve => this.validate().then(() => resolve(true)).catch(() => {}))
996                },
997                validate() {
998                    if (!Object.keys(this.fields).length || !Object.keys(this.fields).length !== formElement.elements.length) {
999                        this.setupFields(formElement.elements);
1000                    }
1001                    return new Promise(async (resolve, reject) => {
1002                        if (formElement.elements) {
1003                                                        await raceSome(this.validateFields(), result => result !== true)
1004                            const invalidFields = Object.values(this.fields).filter(field => !field.state.valid);
1005                            this.state.valid = invalidFields.length === 0;
1006                            if (this.state.valid) {
1007                                resolve();
1008                            } else {
1009                                if (options.scrollToFirstError && invalidFields.length > 0) {
1010                                    invalidFields[0].element.focus()
1011                                    invalidFields[0].element.select && invalidFields[0].element.select();
1012                                }
1013                                reject(invalidFields.map(field => field.element));
1014                            }
1015                        }
1016                    });
1017                },
1018                                validateFields() {
1019                    const fields = Object.values(this.fields);
1020
1021                                        fields.forEach(field => {
1022                        this.getMessageContainer(field).classList.remove(options.validClassName, options.invalidClassName)
1023                    });
1024                                        return fields.map(field => this.validateField(field))
1025                },
1026                                validateField(field) {
1027                                        if (! field || ! elementWillValidate(field.element)) {
1028                        return new Promise(resolve => resolve(true))
1029                    }
1030
1031                    let value;
1032                    if (field.element.type === 'checkbox') {
1033                        value = getCheckedValues(field);
1034                    } else if (field.element.type === 'radio') {
1035                        value = getCheckedValues(field)[0] || undefined;
1036                    } else if (field.element.tagName === 'SELECT' && field.element.multiple) {
1037                        value = Array.from(field.element.selectedOptions).map(opt => opt.value);
1038                    } else {
1039                        value = field.element.value;
1040                    }
1041
1042                    const rules = field.rules || {};
1043
1044                                        field.state.valid = true;
1045                    this.showFieldState(field);
1046
1047                
1048                                        const fieldValidations = Object.keys(rules).filter(rule => formValidationRules[rule]).map(async rule => {
1049                        return runValidateFn.call(this, rule, rules[rule], value, field).then(result => {
1050                            field.state.rules[rule] = result;
1051                            return result;
1052                        })
1053                    });
1054
1055                    return new Promise(resolve => {
1056                                                Promise.all(fieldValidations).then(results => {
1057                                                                                    field.state.valid = !elementWillValidate(field.element) || rules.length === 0 || !results.some(isInvalidRuleResult)
1058                            this.showFieldState(field);
1059                            resolve(field.state.valid);
1060                        })
1061                    });
1062                },
1063                                getMessagesByField(field) {
1064                    const messages = [];
1065                    const invalidRules = Object.keys(field.state.rules).filter(rule => isInvalidRuleResult(field.state.rules[rule]));
1066
1067                    field.rules && Object.keys(field.rules).forEach((rule) => {
1068                        if (invalidRules.includes(rule)) {
1069                            const customMessage = field.element.getAttribute('data-msg-' + rule);
1070                            const message = customMessage ? customMessage : field.state.rules[rule];
1071                            const ruleOptions = JSON.parse(JSON.stringify(field.rules[rule]));
1072
1073                            if (typeof message === 'undefined' || message === null || (typeof message !== 'string' && ! message.type)) {
1074                                messages.push(hyva.strf('Validation rule "%0" failed.', rule));
1075                            } else if (Array.isArray(ruleOptions)) {
1076                                ruleOptions.unshift(message.type ? message.content : message);
1077                                const content = hyva.strf.apply(null, ruleOptions);
1078                                messages.push(message.type ? {type: message.type, content} : content);
1079                            } else {
1080                                const content = hyva.strf(message.type ? message.content : message, ruleOptions)
1081                                messages.push(message.type ? {type: message.type, content} : content);
1082                            }
1083                        }
1084                    });
1085                    return messages;
1086                },
1087                /** @deprecated */
1088                getFieldWrapper(field) {
1089                                        return this.getMessageContainer(field)
1090                },
1091                getMessageContainer(field) {
1092                    let container;
1093                    const pageSelector = field.element.getAttribute('data-validation-container') || options.pageMessagesContainerSelector;
1094                    if (pageSelector) {
1095                        container = document.querySelector(pageSelector)
1096                            || containerNotFound(pageSelector, field.element)
1097                    } else {
1098                        const containerSelector = classNamesToSelector(options.fieldWrapperClassName);
1099                        container = field.element.closest(containerSelector)
1100                            || createMessageContainer(field.element, options.fieldWrapperClassName)
1101                            || containerNotFound(containerSelector, field.element);
1102                    }
1103
1104                    return container;
1105                },
1106                showFieldState(field) {
1107                    const container = this.getMessageContainer(field),
1108                        hasErrorMessages = hasMessagesWrapper.call(this, field, options.messagesWrapperClassName),
1109                        messages = this.getMessagesByField(field).map(m => {
1110                            return m.type !== 'html' ? escapeHtml(m.type ? m.content : m) : m.content;
1111                        });
1112                    container.classList.toggle(options.validClassName, field.state.valid && ! hasErrorMessages);
1113                    container.classList.toggle(options.invalidClassName, !field.state.valid || hasErrorMessages);
1114                    this.createHtmlErrorMessage(field, messages);
1115
1116                    if (field.state.valid) {
1117                        field.element.removeAttribute('aria-invalid');
1118                    } else {
1119                        field.element.setAttribute('aria-invalid', 'true');
1120                        if (! document.activeElement) {
1121                            field.element.focus();
1122                        }
1123                    }
1124                },
1125                removeMessages(field, messagesClass) {
1126                    if (! hasMessagesWrapper.call(this, field, messagesClass || options.messagesWrapperClassName)) {
1127                        return;
1128                    }
1129
1130                    const msgWrapper = getMessagesWrapper.call(this, field, messagesClass || options.messagesWrapperClassName);
1131                    const messages = msgWrapper.querySelectorAll(`[data-msg-field='${field.element.name}']`);
1132                    Array.from(messages).forEach(msg => msg.remove());
1133                    if (msgWrapper && msgWrapper.childElementCount === 0) {
1134                        field.element.removeAttribute('aria-errormessage');
1135                        field.element.removeAttribute('aria-describedby');
1136                        msgWrapper.remove();
1137                    }
1138                },
1139                createErrorMessage(field, messages) {
1140                    const htmlMessages = (Array.isArray(messages) ? messages : [messages]).map(escapeHtml)
1141                    this.createHtmlErrorMessage(field, htmlMessages);
1142                },
1143                createHtmlErrorMessage(field, messages) {
1144                    this.removeMessages(field, options.messagesWrapperClassName);
1145                    field.element.removeAttribute('aria-errormessage');
1146                    field.element.removeAttribute('aria-describedby');
1147
1148                    if (!field.state.valid) {
1149                        const msgWrapper = this.addHtmlMessages(field, options.messagesWrapperClassName, messages);
1150                        field.element.setAttribute('aria-errormessage', msgWrapper.id);
1151                        field.element.setAttribute('aria-describedby', msgWrapper.id);
1152                    }
1153                },
1154                /** @deprecated */
1155                createMessage(field, message) {
1156                                        return this.addMessages(field, options.messagesWrapperClassName, message);
1157                },
1158                addMessages(field, messagesClass, messages) {
1159                    const htmlMessages = (Array.isArray(messages) ? messages : [messages]).map(escapeHtml)
1160                    return this.addHtmlMessages(field, messagesClass, htmlMessages);
1161                },
1162                addHtmlMessages(field, messagesClass, htmlMessages) {
1163                    const msgWrapper = getMessagesWrapper.call(this, field, messagesClass);
1164
1165                    (Array.isArray(htmlMessages) ? htmlMessages : [htmlMessages]).forEach((htmlMessage) => {
1166                        const li = document.createElement('li');
1167                        li.innerHTML = htmlMessage;
1168                        li.setAttribute('data-msg-field', field.element.name);
1169                        msgWrapper.appendChild(li);
1170                    });
1171
1172                    return msgWrapper;
1173                },
1174                setField(name, value) {
1175                    this.fields[name].element.value = value;
1176                    this.fields[name].element.dispatchEvent((new Event('input')));
1177                    this.validateField(this.fields[name]);
1178                }
1179            }
1180        }
1181
1182        function getFieldName(field) {
1183                        const fieldName = field.element?.getAttribute('title')
1184                || document.querySelector(`label[for="${field.element?.id}"]`)?.innerText;
1185
1186            return fieldName
1187                ? fieldName.trim()
1188                : '';
1189        }
1190
1191        hyva.formValidation = formValidation;
1192        hyva.formValidation.rules = formValidationRules;
1193        hyva.formValidation.setInputAttributeRuleName = (attrName, ruleName) => INPUT_ATTRIBUTE_RULES[attrName] = ruleName || attrName;
1194        hyva.formValidation.setInputTypeRuleName = (typeName, ruleName) => INPUT_TYPE_RULES[typeName] = ruleName || typeName;
1195        hyva.formValidation.addRule = (name, validator) => formValidationRules[name] = validator;
1196        hyva.formValidation.getFieldName = getFieldName;
1197    }(window.hyva = window.hyva || {}));
1198
1199    window.addEventListener('alpine:init', () => {
1200        Alpine.data('hyva.formValidation', hyva.formValidation);
1201        Alpine.data('hyva.formValidation($el)', hyva.formValidation);
1202    }, {once: true});
1203    //# sourceURL=hyva-advanced-form-validation.js
1204</script>
1204
1205<div class="page-wrapper">
1205<script>
1206    function initHeader () {
1207        return {
1208            menuOpen: false,
1209            cart: {},
1210            getData: function getData(data) {
1211                if (data.cart) { this.cart = data.cart }
1212            }
1213        }
1214    }
1215</script>
1215
1216
1217<main :class="{'translate-x-64 transform': menuOpen}" class="duration-500 transition-all main" role="main" x-data="initHeader()" @toggle-mobile-menu.window="menuOpen = !menuOpen" @private-content-loaded.window="getData(event.detail.data)">
1218    <div>
1219        <div class="container mx-auto px-3 sm:px-4">
1220            <div class="grid grid-cols-12">
1221                <header class="col-span-12 lg:col-span-3 xl:col-span-2">
1222                    <h1 class="pt-4 pb-2 lg:pb-6 mt-4 lg:my-6 flex justify-center lg:justify-start">
1223                        <div class="order-1 md:order-1 w-full sm:w-auto pb-2 sm:pb-0">
1224    <a class="pt-4 flex items-center justify-center tracking-wide no-underline hover:no-underline font-medium font-title text-gray-800 text-xl leading-normal"
1225       href="https://uitgeverijpluim.nl/"
1226       title="Uitgeverij&#x20;Pluim"
1227       aria-label="store logo">
1228        <img src="https://uitgeverijpluim.nl/media/logo/websites/1/logo_sxfncc_1.jpg"
1229             title="Uitgeverij&#x20;Pluim"
1230             alt="Uitgeverij&#x20;Pluim"
1231            width="200"            height="130"        />
1232    </a>
1233</div>
1234                    </h1>
1235                    <div x-cloak @click="$dispatch('toggle-mobile-menu')"  :class="{'hidden': !menuOpen}" class="fixed  top-0 left-0 w-full h-full z-40 bg-opacity-25 bg-black transform transition-all duration-500"></div>
1236
1237<nav x-cloak class="-translate-x-64 fixed left-0 top-0 w-64 lg:w-full h-screen overflow-y-auto z-40 transform transition-all lg:translate-x-0 duration-500 bg-white p-5 lg:p-0 lg:relative">
1238    <ul>
1239        <li>
1240            <a href="/" class="block py-1 uppercase text-gray-1400 hover:text-gray-900 transition-colors duration-400">Home</a>
1241        </li>
1242        <li>
1243            <a href="/schrijvers" class="block py-1 uppercase text-gray-1400 hover:text-gray-900 transition-colors duration-400">Schrijvers</a>
1244        </li>
1245                    <li>
1246                <a href="https://uitgeverijpluim.nl/boeken"
1247                    title="Boeken"
1248                    class="block py-1 uppercase text-gray-1400 hover:text-gray-900 transition-colors duration-400">Boeken</a>
1249            </li>
1250                            <li>
1251                    <ul class="pt-1 pb-4">
1252                                                    <li><a href="https://uitgeverijpluim.nl/boeken/fictie" title="Fictie" class="block py-1 px-2 leading-snug text-gray-900 hover:text-gray-1400 transition-colors duration-400">Fictie</a></li>
1253                                                    <li><a href="https://uitgeverijpluim.nl/boeken/poezie" title="Poëzie" class="block py-1 px-2 leading-snug text-gray-900 hover:text-gray-1400 transition-colors duration-400">Poëzie</a></li>
1254                                                    <li><a href="https://uitgeverijpluim.nl/boeken/non-fictie" title="Non-fictie" class="block py-1 px-2 leading-snug text-gray-900 hover:text-gray-1400 transition-colors duration-400">Non-fictie</a></li>
1255                                                    <li><a href="https://uitgeverijpluim.nl/boeken/verwacht" title="Verwacht" class="block py-1 px-2 leading-snug text-gray-900 hover:text-gray-1400 transition-colors duration-400">Verwacht</a></li>
1256                                            </ul>
1257                </li>
1258                            <li>
1259            <a href="/nieuws" class="block py-1 uppercase text-gray-1400 hover:text-gray-900 transition-colors duration-400">Nieuws</a>
1260        </li>
1261        <li>
1262            <a href="/agenda" class="block py-1 uppercase text-gray-1400 hover:text-gray-900 transition-colors duration-400">Agenda</a>
1263        </li>
1264        <li>
1265            <a href="/over-ons" class="block py-1 uppercase text-gray-1400 hover:text-gray-900 transition-colors duration-400">Over ons</a>
1266        </li>
1267        <li>
1268            <a href="/contact" class="block py-1 uppercase text-gray-1400 hover:text-gray-900 transition-colors duration-400">Contact</a>
1269        </li>
1270    </ul>
1271</nav>
1272                </header>
1273                <div class="col-span-12 lg:col-span-9 xl:col-span-10">
1274                    
1275<script>
1276    function initCartSummary() {
1277        return {
1278            searchOpen: false,
1279            cart: {},
1280            getSummary: function (data) {
1281                if (data.cart) {
1282                    this.cart = data.cart
1283                }
1284            }
1285        }
1286    }
1287</script>
1287
1288<div id="cart-drawer" class="relative flex justify-between py-4 lg:py-6">
1289    <div class="flex items-center">
1290    <button x-data @click="$dispatch('toggle-mobile-menu')" aria-label="Menu openen"
1291                name="menu"
1292                class="-ml-2 sm:-ml-4 text-gray-1400 px-2 sm:px-4 py-4 lg:hidden focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-gray-1400">
1293            <svg viewBox="0 0 120 80" fill="currentColor" stroke="currentColor"
1294                 stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="w-4 h-4 ml-2" >
1295                <rect width="120" height="15"></rect>
1296                <rect y="35" width="120" height="15"></rect>
1297                <rect y="70" width="120" height="15"></rect>
1298            </svg>
1299        </button>
1300    </div>
1301    <nav class="flex-1 navbar" @keydown.window.escape="searchOpen = false;" x-data="initCartSummary()"
1302         @private-content-loaded.window="getSummary(event.detail.data)">
1303        <ul class="flex justify-end -mr-2 sm:-mr-4">
1304            <li class="justify-end w-full lg:w-auto flex lg:relative">
1305                <a href="#" aria-label="Search" class="order-2 flex items-center h-full px-2 sm:px-4 text-gray-900 text-base leading-normal sm:text-lg leading-normal"
1306                   @click.prevent="searchOpen = !searchOpen; $nextTick(function () { $refs.searchInput.select(); });" id="menu-search-icon">
1307                    <svg class="h-5 w-5 -mt-1" stroke="currentColor" fill="none" viewBox="0 0 24 24">
1308                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
1309                              d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
1310                    </svg>
1311                </a>
1312                <div
1313                    :class="{ 'block': searchOpen, 'hidden': !searchOpen }"
1314                    @click.outside="searchOpen = false"
1315                    x-show="searchOpen"
1316                    class="hidden order-1 flex-1 lg:flex-0 w-full top-full sm:h-full sm:w-64 lg:w-96">
1317                    <form class="pt-3" id="search_mini_form" action="https://uitgeverijpluim.nl/catalogsearch/result/"
1318                          method="get">
1319                        <input x-ref="searchInput"
1320                               type="search"
1321                               placeholder="Doorzoek&#x20;de&#x20;hele&#x20;winkel"
1322                               id="search-menu"
1323                               name="q"
1324                               value=""
1325                               maxlength="128"
1326                               class="text-gray-1000 placeholder-gray-800 w-full px-3 h-2md focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-gray-1400"
1327                               style="background-color: #fafafa; border-color: #fafafa;">
1328                    </form>
1329                </div>
1330            </li>
1331            <li>
1332                <a title="Mijn account"
1333                   href="https://uitgeverijpluim.nl/customer/account/index/"
1334                   class="block px-2 sm:px-4 py-4 uppercase text-gray-1400 hover:text-gray-900 transition-colors duration-400">
1335                    <svg viewBox="0 0 576 512" stroke="currentColor" class="h-5 w-5">
1336                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
1337                              d="M224 256c70.7 0 128-57.3 128-128S294.7 0 224 0 96 57.3 96 128s57.3 128 128 128zm89.6 32h-16.7c-22.2 10.2-46.9 16-72.9 16s-50.6-5.8-72.9-16h-16.7C60.2 288 0 348.2 0 422.4V464c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48v-41.6c0-74.2-60.2-134.4-134.4-134.4z"></path>
1338                    </svg>
1339                </a>
1340            </li>
1341            <li class="relative">
1342                <template x-if="cart && cart.summary_count > 0">
1343                    <div class="right-0 top-0 mt-2 absolute bg-gray-800 text-white h-5 w-5 rounded-full flex items-center justify-center text-xs leading-normal" x-html="cart.summary_count"></div>
1344                </template>
1345                <a title="Winkelwagen"
1346                   href="https://uitgeverijpluim.nl/checkout/cart/index/"
1347                   class="block px-2 sm:px-4 py-4 uppercase text-gray-1400 hover:text-gray-900 transition-colors duration-400">
1348                    <svg viewBox="0 0 576 512" stroke="currentColor" class="h-5 w-5">
1349                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
1350                              d="M576 216v16c0 13.255-10.745 24-24 24h-8l-26.113 182.788C514.509 462.435 494.257 480 470.37 480H105.63c-23.887 0-44.139-17.565-47.518-41.212L32 256h-8c-13.255 0-24-10.745-24-24v-16c0-13.255 10.745-24 24-24h67.341l106.78-146.821c10.395-14.292 30.407-17.453 44.701-7.058 14.293 10.395 17.453 30.408 7.058 44.701L170.477 192h235.046L326.12 82.821c-10.395-14.292-7.234-34.306 7.059-44.701 14.291-10.395 34.306-7.235 44.701 7.058L484.659 192H552c13.255 0 24 10.745 24 24zM312 392V280c0-13.255-10.745-24-24-24s-24 10.745-24 24v112c0 13.255 10.745 24 24 24s24-10.745 24-24zm112 0V280c0-13.255-10.745-24-24-24s-24 10.745-24 24v112c0 13.255 10.745 24 24 24s24-10.745 24-24zm-224 0V280c0-13.255-10.745-24-24-24s-24 10.745-24 24v112c0 13.255 10.745 24 24 24s24-10.745 24-24z"></path>
1351                    </svg>
1352                </a>
1353            </li>
1354            <li class="hidden md:block">
1355                <div class="p-4 text-lg leading-normal font-bold">/</div>
1356            </li>
1357            <li class="hidden md:block">
1358                <a title="Instagram" target="_new" href="https://www.instagram.com/uitgeverijpluim/"
1359                   class="block px-2 sm:px-4 py-4 uppercase text-gray-1400 hover:text-gray-900 transition-colors duration-400">
1360                    <svg viewBox="0 0 576 512" stroke="currentColor" class="h-5 w-5">
1361                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
1362                              d="M224.1 141c-63.6 0-114.9 51.3-114.9 114.9s51.3 114.9 114.9 114.9S339 319.5 339 255.9 287.7 141 224.1 141zm0 189.6c-41.1 0-74.7-33.5-74.7-74.7s33.5-74.7 74.7-74.7 74.7 33.5 74.7 74.7-33.6 74.7-74.7 74.7zm146.4-194.3c0 14.9-12 26.8-26.8 26.8-14.9 0-26.8-12-26.8-26.8s12-26.8 26.8-26.8 26.8 12 26.8 26.8zm76.1 27.2c-1.7-35.9-9.9-67.7-36.2-93.9-26.2-26.2-58-34.4-93.9-36.2-37-2.1-147.9-2.1-184.9 0-35.8 1.7-67.6 9.9-93.9 36.1s-34.4 58-36.2 93.9c-2.1 37-2.1 147.9 0 184.9 1.7 35.9 9.9 67.7 36.2 93.9s58 34.4 93.9 36.2c37 2.1 147.9 2.1 184.9 0 35.9-1.7 67.7-9.9 93.9-36.2 26.2-26.2 34.4-58 36.2-93.9 2.1-37 2.1-147.8 0-184.8zM398.8 388c-7.8 19.6-22.9 34.7-42.6 42.6-29.5 11.7-99.5 9-132.1 9s-102.7 2.6-132.1-9c-19.6-7.8-34.7-22.9-42.6-42.6-11.7-29.5-9-99.5-9-132.1s-2.6-102.7 9-132.1c7.8-19.6 22.9-34.7 42.6-42.6 29.5-11.7 99.5-9 132.1-9s102.7-2.6 132.1 9c19.6 7.8 34.7 22.9 42.6 42.6 11.7 29.5 9 99.5 9 132.1s2.7 102.7-9 132.1z"></path>
1363                    </svg>
1364                </a>
1365            </li>
1366            <li class="hidden md:block">
1367                <a title="Facebook" target="_new" href="https://www.facebook.com/uitgeverijpluim"
1368                   class="block px-2 sm:px-4 py-4 uppercase text-gray-1400 hover:text-gray-900 transition-colors duration-400">
1369                    <svg viewBox="0 0 576 512" stroke="currentColor" class="h-5 w-5">
1370                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
1371                              d="M279.14 288l14.22-92.66h-88.91v
1371-60.13c0-25.35 12.42-50.06 52.24-50.06h40.42V6.26S260.43 0 225.36 0c-73.22 0-121.08 44.38-121.08 124.72v70.62H22.89V288h81.39v224h100.17V288z"></path>
1372                    </svg>
1373                </a>
1374            </li>
1375            <li class="hidden md:block">
1376                <a title="YouTube" target="_new"
1377                   href="https://www.youtube.com/channel/UCRr07oGz4RdkdjrntFZ76nw"
1378                   class="block px-2 sm:px-4 py-4 uppercase text-gray-1400 hover:text-gray-900 transition-colors duration-400">
1379                    <svg viewBox="0 0 576 512" stroke="currentColor" class="h-5 w-5">
1380                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
1381                              d="M549.655 124.083c-6.281-23.65-24.787-42.276-48.284-48.597C458.781 64 288 64 288 64S117.22 64 74.629 75.486c-23.497 6.322-42.003 24.947-48.284 48.597-11.412 42.867-11.412 132.305-11.412 132.305s0 89.438 11.412 132.305c6.281 23.65 24.787 41.5 48.284 47.821C117.22 448 288 448 288 448s170.78 0 213.371-11.486c23.497-6.321 42.003-24.171 48.284-47.821 11.412-42.867 11.412-132.305 11.412-132.305s0-89.438-11.412-132.305zm-317.51 213.508V175.185l142.739 81.205-142.739 81.201z"></path>
1382                    </svg>
1383                </a>
1384            </li>
1385            <li class="hidden md:block">
1386                <a title="Soundcloud" target="_new" href="https://soundcloud.com/uitgeverijpluim"
1387                   class="block px-2 sm:px-4 py-4 uppercase text-gray-1400 hover:text-gray-900 transition-colors duration-400">
1388                    <svg viewBox="0 0 576 512" stroke="currentColor" class="h-5 w-5">
1389                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
1390                              d="M256 32C114.52 32 0 146.496 0 288v48a32 32 0 0 0 17.689 28.622l14.383 7.191C34.083 431.903 83.421 480 144 480h24c13.255 0 24-10.745 24-24V280c0-13.255-10.745-24-24-24h-24c-31.342 0-59.671 12.879-80 33.627V288c0-105.869 86.131-192 192-192s192 86.131 192 192v1.627C427.671 268.879 399.342 256 368 256h-24c-13.255 0-24 10.745-24 24v176c0 13.255 10.745 24 24 24h24c60.579 0 109.917-48.098 111.928-108.187l14.382-7.191A32 32 0 0 0 512 336v-48c0-141.479-114.496-256-256-256z"
1391                        "></path>
1392                    </svg>
1393                </a>
1394            </li>
1395        </ul>
1396    </nav>
1397</div>
1398                    <!--Cart Drawer-->
1399                    
1399<script>
1400    function initCartDrawer() {
1401        return {
1402            open: false,
1403            cart: {},
1404            getData: function (data) {
1405                if (data.cart) {
1406                    this.cart = data.cart
1407                }
1408            },
1409            getCartItems: function getCartItems () {
1410                return this.cart && this.cart.items.sort(function(a,b) { return a.item_id - b.item_id }) || {}
1411            },
1412            deleteItemFromCart: function deleteItemFromCart (itemId) {
1413                var formKey = document.querySelector('input[name=form_key]').value;
1414                fetch(BASE_URL+"checkout/sidebar/removeItem/", {
1415                    "headers": {
1416                        "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
1417                    },
1418                    "body": "form_key="+ formKey + "&item_id="+itemId,
1419                    "method": "POST",
1420                    "mode": "cors",
1421                    "credentials": "include"
1422                }).then(function (response) {
1423                    if (response.redirected) {
1424                        window.location.href = response.url;
1425                    } else if (response.ok) {
1426                        return response.json();
1427                    } else {
1428                        typeof window.dispatchMessages !== "undefined" && window.dispatchMessages(
1429                            [{
1430                                type: "warning",
1431                                text: "Kan item niet uit offerte verwijderen."
1432                            }], 5000
1433                        );
1434                    }
1435                }).then(function (response) {
1436                    typeof window.dispatchMessages !== "undefined" && window.dispatchMessages(
1437                        [{
1438                            type: response.success ? "success" : "error",
1439                            text: response.success
1440                                ? "Je hebt het product verwijderd."
1441                                : response.error_message
1442                        }], 5000
1443                    );
1444                    var reloadCustomerDataEvent = new CustomEvent("reload-customer-section-data");
1445                    window.dispatchEvent(reloadCustomerDataEvent);
1446                });
1447            }
1448        }
1449    }
1450</script>
1450
1451<section id="cart-drawer"
1452         x-data="initCartDrawer()"
1453         @private-content-loaded.window="getData(event.detail.data)"
1454         @toggle-cart.window="open=true;"
1455         @keydown.window.escape="open=false"
1456>
1457    <template x-if="cart && cart.summary_count">
1458        <div role="dialog"
1459             aria-labelledby="cart-drawer-title"
1460             aria-modal="true"
1461             @click.outside="open = false"
1462             class="fixed inset-y-0 right-0 max-w-full flex">
1463            <div class="relative w-screen max-w-md"
1464                 x-show="open"
1465                 x-transition:enter="transform transition ease-in-out duration-500 sm:duration-700"
1466                 x-transition:enter-start="translate-x-full"
1467                 x-transition:enter-end="translate-x-0"
1468                 x-transition:leave="transform transition ease-in-out duration-500 sm:duration-700"
1469                 x-transition:leave-start="translate-x-0"
1470                 x-transition:leave-end="translate-x-full"
1471            >
1472                <div
1473                    x-show="open"
1474                    x-transition:enter="ease-in-out duration-500"
1475                    x-transition:enter-start="opacity-0"
1476                    x-transition:enter-end="opacity-100"
1477                    x-transition:leave="ease-in-out duration-500"
1478                    x-transition:leave-start="opacity-100"
1479                    x-transition:leave-end="opacity-0" class="absolute top-0 right-0 mt-2 p-2 flex">
1480                    <button @click="open = false;" aria-label="Close panel" class="text-gray-300 hover:text-black transition ease-in-out duration-150 p-2">
1481                        <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
1482                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
1483                        </svg>
1484                    </button>
1485                </div>
1486                <div class="h-full flex flex-col space-y-6 py-6 bg-white shadow-xl">
1487                    <header class="px-4 sm:px-6">
1488                        <h2 id="cart-drawer-title" class="text-lg leading-normal leading-7 font-medium text-gray-900">
1489                            Winkelwagen                        </h2>
1490                    </header>
1491                    <div class="relative grid gap-6 bg-white py-6 sm:gap-8 px-4 sm:px-6 border-b border-container overflow-y-auto">
1492                        <template x-for="item in getCartItems()">
1493                            <div class="-m-3 p-3 flex items-start space-x-4 rounded-lg hover:bg-gray-100 transition ease-in-out duration-150">
1494                                <a :href="item.product_url" class="w-1/4">
1495                                    <img
1496                                        :src="item.product_image.src"
1497                                        :alt="item.product_name"
1498                                        :width="item.product_image.width"
1499                                        :height="item.product_image.height"
1500                                        loading="lazy"
1501                                    />
1502                                </a>
1503                                <div class="space-y-2 w-3/4">
1504                                    <div>
1505                                        <p class="text-xl leading-normal"><span x-html="item.qty"></span> x <span x-html="item.product_name"></span></p>
1506                                        <p class="text-sm leading-normal"><span x-html="item.product_sku"></p>
1507                                    </div>
1508                                    <template x-for="option in item.options">
1509                                        <div class="pt-2">
1510                                            <p class="font-semibold" x-text="option.label + ':'"></p>
1511                                            <p class="text-secondary" x-text="option.value"></p>
1512                                        </div>
1513                                    </template>
1514                                    <p><span x-html="item.product_price"></span></p>
1515                                    <div class="pt-4">
1516                                        <a :href="item.configure_url"
1517                                           class="btn btn-primary inline-flex mr-2 p-2">
1518                                            <svg fill="none" viewBox="0 0 24 24" stroke="currentColor" size="16" class="h-5 w-5">
1519                                                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z">
1520
1521                                                </path>
1522                                            </svg>
1523                                        </a>
1524                                        <button class="btn btn-primary inline-flex p-2"
1525                                            @click="deleteItemFromCart(item.item_id)"
1526                                        >
1527                                            <svg fill="none" viewBox="0 0 24 24" stroke="currentColor" size="16" class="h-5 w-5">
1528                                                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
1529                                            </svg>
1530                                        </button>
1531                                    </div>
1532
1533                                </div>
1534                            </div>
1535                        </template>
1536                    </div>
1537                    <div class="relative grid gap-6 bg-white py-6 sm:gap-8 px-4 sm:px-6">
1538                        <div class="w-full -m-3 p-3 space-x-4 roun
1538ded-lg hover:bg-gray-100 transition ease-in-out duration-150">
1539                            <p>Subtotal: <span x-html="cart.subtotal"></span></p>
1540                        </div>
1541                        <div class="w-full -m-3 p-3 space-x-4 rounded-lg hover:bg-gray-100 transition ease-in-out duration-150">
1542                            <a href="https://uitgeverijpluim.nl/checkout/index/" class="btn btn-primary inline-flex">Checkout</a>
1543                            <span>or</span> <a href="https://uitgeverijpluim.nl/checkout/cart/" class="underline">Go to Cart</a>
1544                        </div>
1545                    </div>
1546                </div>
1547            </div>
1548        </div>
1549    </template>
1550</section>
1551<main id="maincontent" class="page-main"><div class="page messages">
1551<script>
1552        function initMessages() {
1553        "use strict";
1554        return {
1555            messages: window.mageMessages || [],
1556            isEmpty() {
1557                return this.messages.reduce(
1558                    function (isEmpty, message) {
1559                        return isEmpty && message === undefined
1560                    }, true
1561                )
1562            },
1563            hasMessages() {
1564                return !this.isEmpty();
1565            },
1566            hasMessage() {
1567                return !!this.message;
1568            },
1569            removeMessage() {
1570                this.messages[this.index] = undefined;
1571            },
1572            addMessages(messages, hideAfter) {
1573                messages.map((message) => {
1574                    this.messages = this.messages.concat(message);
1575                    if (hideAfter === undefined && message.type === 'success' && window.defaultSuccessMessageTimeout) {
1576                        hideAfter = window.defaultSuccessMessageTimeout;
1577                    }
1578                    if (hideAfter) {
1579                        this.setHideTimeOut(this.messages.length -1, hideAfter);
1580                    }
1581                });
1582            },
1583            setHideTimeOut(messageIndex, hideAfter) {
1584                setTimeout((messageIndex) => {
1585                    this.removeMessage(messageIndex);
1586                }, hideAfter, messageIndex);
1587            },
1588            eventListeners: {
1589                ['@messages-loaded.window'](event) {
1590                    this.addMessages(event.detail.messages, event.detail.hideAfter)
1591                },
1592                ['@private-content-loaded.window'](event) {
1593                    const data = event.detail.data;
1594                    if (
1595                        data.messages &&
1596                        data.messages.messages &&
1597                        data.messages.messages.length
1598                    ) {
1599                        this.addMessages(data.messages.messages);
1600                    }
1601                },
1602                ['@clear-messages.window']() {
1603                    this.messages = [];
1604                }
1605            },
1606            getMessageUiId() {
1607                return 'message-' + this.message.type;
1608            }
1609        }
1610    }
1611
1612    window.addEventListener('alpine:init', () => Alpine.data('initMessages', initMessages), {once: true})
1613</script>
1613
1614<section id="messages"
1615         x-data="initMessages"
1616         x-bind="eventListeners"
1617         aria-live="assertive"
1618         role="alert"
1619>
1620    <template x-if="hasMessages">
1621        <div class="w-full">
1622            <div class="messages container mx-auto py-3">
1623                <template x-for="(message, index) in messages" :key="index">
1624                    <div>
1625                        <template x-if="hasMessage">
1626                            <div class="message"
1627                                 :class="message.type"
1628                                 :ui-id="getMessageUiId"
1629                            >
1630                                <span x-html="message.text"></span>
1631                                <button
1632                                    type="button"
1633                                    class="text-gray-600 hover:text-black"
1634                                    aria-label="Bericht sluiten"
1635                                    @click.prevent="removeMessage"
1636                                >
1637                                    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="stroke-current" width="18" height="18" aria-hidden="true">
1638  <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
1639</svg>
1640                                </button>
1641                            </div>
1642                        </template>
1643                    </div>
1644                </template>
1645            </div>
1646        </div>
1647    </template>
1648</section>
1649</div><div class="row"><div><div class="row"><div class="columns"><div class="column main"><section class="content-wrap box-border block font-normal text-sm leading-normal leading-normal mb-2 lg:mb-24 text-black">
1650    <div class="grid lg:grid-cols-6">
1651        
1652
1653
1654<div class="bg-green-700 mb-2 ml-1&#x20;lg&#x3A;col-span-6&#x20;order-1">
1655    <a aria-label="omslag&#x20;In&#x20;alle&#x20;verscheidenheid&#x20;van&#x20;Marli&#x20;Huijer&#x20;" href="/boeken/non-fictie/de-ander-tegengif-tegen-vijanddenken">        <img
1656            src="https://images.prismic.io/uitgeverijpluim/tDzPyVfkg3rVIBvY_bannergrootMarliHuijer-1-.png?auto=format,compress&amp;ar=1%3A0.33&amp;fit=crop&amp;w=1400"
1657            width="1400"
1658            height="462"
1659            srcset="https&#x3A;&#x2F;&#x2F;images.prismic.io&#x2F;uitgeverijpluim&#x2F;tDzPyVfkg3rVIBvY_bannergrootMarliHuijer-1-.png&#x3F;auto&#x3D;format,compress&amp;ar&#x3D;1&#x25;3A0.33&amp;fit&#x3D;crop&amp;w&#x3D;1400 1024w,
1660                    https&#x3A;&#x2F;&#x2F;images.prismic.io&#x2F;uitgeverijpluim&#x2F;tDzPyVfkg3rVIBvY_bannergrootMarliHuijer-1-.png&#x3F;auto&#x3D;format,compress&amp;ar&#x3D;1&#x25;3A0.33&amp;fit&#x3D;crop&amp;w&#x3D;1400 700w"
1661            sizes="(min-width: 1024px) 1024w, 700w"
1662            alt="omslag&#x20;In&#x20;alle&#x20;verscheidenheid&#x20;van&#x20;Marli&#x20;Huijer&#x20;"
1663            class="w-full transition duration-500 ease-in-out hover:opacity-50">
1664        </a></div>
1665
1666
1667
1668<div class="bg-green-700 mb-2 ml-1&#x20;lg&#x3A;col-span-2&#x20;order-3&#x20;lg&#x3A;order-2">
1669    <a aria-label="boekomslag&#x20;Red" href="/boeken/fictie/red">        <img
1670            src="https://images.prismic.io/uitgeverijpluim/oJ2PUlgldqU2c4U6_Redbannerlinksonder.png?auto=format,compress&amp;ar=1%3A1.33&amp;fit=crop&amp;w=1400"
1671            width="1400"
1672            height="1862"
1673            srcset="https&#x3A;&#x2F;&#x2F;images.prismic.io&#x2F;uitgeverijpluim&#x2F;oJ2PUlgldqU2c4U6_Redbannerlinksonder.png&#x3F;auto&#x3D;format,compress&amp;ar&#x3D;1&#x25;3A1.33&amp;fit&#x3D;crop&amp;w&#x3D;1400 1024w,
1674                    https&#x3A;&#x2F;&#x2F;images.prismic.io&#x2F;uitgeverijpluim&#x2F;oJ2PUlgldqU2c4U6_Redbannerlinksonder.png&#x3F;auto&#x3D;format,compress&amp;ar&#x3D;1&#x25;3A0.5&amp;fit&#x3D;crop&amp;crop&#x3D;edges&amp;w&#x3D;700 700w"
1675            sizes="(min-width: 1024px) 1024w, 700w"
1676            alt="boekomslag&#x20;Red"
1677            class="w-full transition duration-500 ease-in-out hover:opacity-50">
1678        </a></div>
1679
1680
1681
1682<div class="bg-green-700 mb-2 ml-1&#x20;lg&#x3A;col-span-4&#x20;order-2&#x20;lg&#x3A;order-3">
1683    <a aria-label="omslag&#x20;Massih&#x20;Hutak&#x20;met&#x20;een&#x20;aanbeveling&#x20;van&#x20;Karin&#x20;Amatmoekrim" href="/boeken/fictie/voor-issa">        <img
1684            src="https://images.prismic.io/uitgeverijpluim/hOOcb97_XCldbHcq_Bannerwebsiterechtsonder-1-.png?auto=format,compress&amp;ar=1%3A0.66&amp;fit=crop&amp;w=1400"
1685            width="1400"
1686            height="924"
1687            srcset="https&#x3A;&#x2F;&#x2F;images.prismic.io&#x2F;uitgeverijpluim&#x2F;hOOcb97_XCldbHcq_Bannerwebsiterechtsonder-1-.png&#x3F;auto&#x3D;format,compress&amp;ar&#x3D;1&#x25;3A0.66&amp;fit&#x3D;crop&amp;w&#x3D;1400 1024w,
1688                    https&#x3A;&#x2F;&#x2F;images.prismic.io&#x2F;uitgeverijpluim&#x2F;hOOcb97_XCldbHcq_Bannerwebsiterechtsonder-1-.png&#x3F;auto&#x3D;format,compress&amp;ar&#x3D;1&#x25;3A0.66&amp;fit&#x3D;crop&amp;w&#x3D;1400 700w"
1689            sizes="(min-width: 1024px) 1024w, 700w"
1690            alt="omslag&#x20;Massih&#x20;Hutak&#x20;met&#x20;een&#x20;aanbeveling&#x20;van&#x20;Karin&#x20;Amatmoekrim"
1691            class="w-full transition duration-500 ease-in-out hover:opacity-50">
1692        </a></div>
1693    </div>
1694</section>
1695</div></div></div></div></div></main><footer class="page-footer"><div class="footer content"><footer class="mt-16 lg:mt-32">
1696    <div class="grid grid-cols-12 gap-4">
1697        <div class="col-span-12 sm:col-span-3">
1698            <h2 class="uppercase text-gray-1400 mb-md text-3xl">Uitgeverij Pluim</h2>
1699            <ul class="mb-5">
1700                <li><a href="/privacybeleid"
1701                       class="block uppercase text-secondary text-sm leading-normal hover:text-primary transition-colors duration-400">Privacybeleid</a></li>
1702                <li class="mt-4 lg:mt-0"><a href="/over-ons"
1703                                            class="block uppercase text-secondary text-sm leading-normal hover:text-primary transition-colors duration-400">Over
1704                        ons</a></li>
1705                <li class="mt-4 lg:mt-0"><a href="/contact"
1706                                            class="block uppercase text-secondary text-sm leading-normal hover:text-primary transition-colors duration-400">Contact</a>
1707                </li>
1708            </ul>
1709        </div>
1710        <div class="col-span-12 sm:col-span-5 lg:col-span-4 xl:col-span-3">
1711            <div class="lg:w-1/4 md:w-1/2 w-full lg:px-4">
1712     <form
1713        class="form subscribe mb-2md"
1714        action="https://uitgeverijpluim.nl/newsletter/subscriber/new/"
1715        method="post"
1716        x-data="initNewsletterForm"
1717        @submit.prevent="submitForm"
1718        id="newsletter-validate-detail"
1719        aria-label="Abonneren&#x20;op&#x20;de&#x20;nieuwsbrief"
1720    >
1721        <h2 class="uppercase text-gray-1400 mb-md text-3xl">Nieuwsbrief</h2>
1722        <div class="flex justify-center gap-2 md:justify-start">
1723            <label for="newsletter-subscribe" class="sr-only">
1724                E-mailadres            </label>
1725            <input
1726                name="email"
1727                type="email"
1728                required
1729                id="newsletter-subscribe"
1730                class="text-gray-1000 h-2md flex-grow border-2 border-gray-300 px-3 mr-1"
1731                placeholder="E-mailadres"
1732                autocomplete="email"
1733                aria-describedby="footer-newsletter-heading"
1734            >
1735            <input name="form_key" type="hidden" value="K8rVxMaAEFHP72U6" />                        <button type="submit" class="bg-primary h-2md block px-3 text-gray-900 uppercase">
1736                OK            </button>
1737        </div>
1738        <div>
1739            <template x-if="displayErrorMessage">
1740                <p class="flex items-center text-red">
1741                    <span class="inline-block w-8 h-8 mr-3">
1742                        <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" width="24" height="24" aria-hidden="true">
1743  <path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
1744</svg>
1745                    </span>
1746                    <template x-for="errorMessage in errorMessages">
1747                        <span x-html="errorMessage"></span>
1748                    </template>
1749                </p>
1750            </template>
1751        </div>
1752    </form>
1753    <div class="w-full">
1754            </div>
1755    
1755<script>
1756        function initNewsletterForm() {
1757            return {
1758                errors: 0,
1759                hasCaptchaToken: 0,
1760                displayErrorMessage: false,
1761                errorMessages: [],
1762                setErrorMessages(messages) {
1763                    this.errorMessages = [messages]
1764                    this.displayErrorMessage = this.errorMessages.length
1765                },
1766                submitForm() {
1767                    // Do not rename $form, the variable is expected to be declared in the recaptcha output
1768                    const $form = document.querySelector('#newsletter-validate-detail');
1769                    
1770                    if (this.errors === 0) {
1771                        $form.submit();
1772                    }
1773                }
1774            }
1775        }
1776
1777        window.addEventListener('alpine:init', () => Alpine.data('initNewsletterForm', initNewsletterForm), {once: true})
1778    </script>
1778
1779    </div>
1780            <ul class="flex md:hidden">
1781                <li class="pr-4">
1782                    <a class="text-gray-700 text-xl leading-normal" href="https://www.instagram.com/uitgeverijpluim/"
1783                       target="_blank" title="Instagram" rel="noreferrer">
1784                        <svg viewBox="0 0 576 512" stroke="currentColor" class="h-5 w-5">
1785                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
1786                                  d="M224.1 141c-63.6 0-114.9 51.3-114.9 114.9s51.3 114.9 114.9 114.9S339 319.5 339 255.9 287.7 141 224.1 141zm0 189.6c-41.1 0-74.7-33.5-74.7-74.7s33.5-74.7 74.7-74.7 74.7 33.5 74.7 74.7-33.6 74.7-74.7 74.7zm146.4-194.3c0 14.9-12 26.8-26.8 26.8-14.9 0-26.8-12-26.8-26.8s12-26.8 26.8-26.8 26.8 12 26.8 26.8zm76.1 27.2c-1.7-35.9-9.9-67.7-36.2-93.9-26.2-26.2-58-34.4-93.9-36.2-37-2.1-147.9-2.1-184.9 0-35.8 1.7-67.6 9.9-93.9 36.1s-34.4 58-36.2 93.9c-2.1 37-2.1 147.9 0 184.9 1.7 35.9 9.9 67.7 36.2 93.9s58 34.4 93.9 36.2c37 2.1 147.9 2.1 184.9 0 35.9-1.7 67.7-9.9 93.9-36.2 26.2-26.2 34.4-58 36.2-93.9 2.1-37 2.1-147.8 0-184.8zM398.8 388c-7.8 19.6-22.9 34.7-42.6 42.6-29.5 11.7-99.5 9-132.1 9s-102.7 2.6-132.1-9c-19.6-7.8-34.7-22.9-42.6-42.6-11.7-29.5-9-99.5-9-132.1s-2.6-102.7 9-132.1c7.8-19.6 22.9-34.7 42.6-42.6 29.5-11.7 99.5-9 132.1-9s102.7-2.6 132.1 9c19.6 7.8 34.7 22.9 42.6 42.6 11.7 29.5 9 99.5 9 132.1s2.7 102.7-9 132.1z"></path>
1787                        </svg>
1788                    </a>
1789                </li>
1790                <li class="pr-4">
1791                    <a class="text-gray-700 text-xl leading-normal" href="https://www.facebook.com/uitgeverijpluim"
1792                       target="_blank" title="Facebook" rel="noreferrer">
1793                        <svg viewBox="0 0 576 512" stroke="currentColor" class="h-5 w-5">
1794                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
1795                                  d="M279.14 288l14.22-92.66h-88.91v-60.13c0-25.35 12.42-50.06 52.24-50.06h40.42V6.26S260.43 0 225.36 0c-73.22 0-121.08 44.38-121.08 124.72v70.62H22.89V288h81.39v224h100.17V288z"></path>
1796                        </svg>
1797                    </a>
1798                </li>
1799                <li class="pr-4">
1800                    <a class="text-gray-700 text-xl leading-normal"
1801                       href="https://www.youtube.com/channel/UCRr07oGz4RdkdjrntFZ76nw" target="_blank" title="YouTube"
1802                       rel="noreferrer">
1803                        <svg viewBox="0 0 576 512" stroke="currentColor" class="h-5 w-5">
1804                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
1805                                  d="M549.655 124.083c-6.281-23.65-24.787-42.276-48.284-48.597C458.781 64 288 64 288 64S117.22 64 74.629 75.486c-23.497 6.322-42.003 24.947-48.284 48.597-11.412 42.867-11.412 132.305-11.412 132.305s0 89.438 11.412 132.305c6.281 23.65 24.787 41.5 48.284 47.821C117.22 448 288 448 288 448s170.78 0 213.371-11.486c23.497-6.321 42.003-24.171 48.284-47.821 11.412-42.867 11.412-132.305 11.412-132.305s0-89.438-11.412-132.305zm-317.51 213.508V175.185l142.739 81.205-142.739 81.201z"></path>
1806                        </svg>
1807                    </a>
1808                </li>
1809                <li class="pr-4">
1810                    <a class="text-gray-700 text-xl leading-normal" href="https://twitter.com/uitgeverijpluim"
1811                       target="_blank" title="Twitter" rel="noreferrer">
1812                        <svg viewBox="0 0 576 512" stroke="currentColor" class="h-5 w-5">
1813                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
1814                                  d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"></path>
1815                        </svg>
1816                    </a>
1817                </li>
1818                <li class="pr-4">
1819                    <a class="text-gray-700 text-xl leading-normal" href="https://soundcloud.com/uitgeverijpluim"
1820                       target="_blank" title="Soundcloud" rel="noreferrer">
1821                        <svg viewBox="0 0 576 512" stroke="currentColor" class="h-5 w-5">
1822                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
1823                                  d="M256 32C114.52 32 0 146.496 0 288v48a32 32 0 0 0 17.689 28.622l14.383 7.191C34.083 431.903 83.421 480 144 480h24c13.255 0 24-10.745 24-24V280c0-13.255-10.745-24-24-24h-24c-31.342 0-59.671 12.879-80 33.627V288c0-105.869 86.131-192 192-192s192 86.131 192 192v1.627C427.671 268.879 399.342 256 368 256h-24c-13.255 0-24 10.745-24 24v176c0 13.255 10.745 24 24 24h24c60.579 0 109.917-48.098 111.928-108.187l14.382
1823-7.191A32 32 0 0 0 512 336v-48c0-141.479-114.496-256-256-256z"
1824                            "></path>
1825                        </svg>
1826                    </a>
1827                </li>
1828            </ul>
1829        </div>
1830    </div>
1831    <p class="text-xs leading-normal mt-12 mb-5 lg:text-right">&copy; Uitgeverij Pluim</p>
1832</footer>
1833</div>
1834</div>
1835</div>
1836</div>
1837</main>
1838</body>
1839</html>
1840</div></footer>
1840<script type="speculationrules">{"prefetch":[{"source":"document","where":{"and":[{"href_matches":"\/*"},{"not":{"href_matches":"\/*(customer|login|logout|auth|cart|checkout|search|download|redirect|rewrite|store|productalert)\/*"}},{"not":{"href_matches":"*.pdf"}},{"not":{"href_matches":"*.zip"}},{"not":{"selector_matches":".do-not-prerender"}},{"not":{"selector_matches":"[rel=nofollow]"}},{"not":{"selector_matches":"[target=_blank]"}},{"not":{"selector_matches":"[target=_parent]"}},{"not":{"selector_matches":"[target=_top]"}}]},"eagerness":"moderate"}]}</script>
vendor: 1 bytes, line 1840
1840
1841<script>
1842(() => {
1843    class BFCacheHandler {
1844        constructor() {
1845            this.options = {
1846                isCustomerLoggedIn: false,
1847                enableUserInteractionRefreshMiniCart: true,
1848                autoCloseMenuMobile: true            };
1849
1850            this.userInteractionEvents = ['touchstart', 'mouseover', 'wheel', 'scroll', 'keydown'];
1851            this._reloadTriggered = false;
1852            this.dom = {};
1853
1854            // Centralized selectors for reuse
1855            this.selectors = {
1856                cartDrawer: "[x-data^='initCartDrawer']",
1857                mobileMenu: "[x-data^='initMenuMobile']",
1858                desktopMenu: "[x-data^='initMenuDesktop']"
1859            };
1860        }
1861
1862                getElement(key) {
1863            const cached = this.dom[key];
1864            if (cached && document.contains(cached)) return cached;
1865            const el = document.querySelector(this.selectors[key]);
1866            if (el) this.dom[key] = el;
1867            return el;
1868        }
1869
1870        init() {
1871            this.refreshMiniCart();
1872            this.reloadCustomerLoginPage();
1873            this.actionAutoCloseMenu(this.options.autoCloseMenuMobile);
1874        }
1875
1876                refreshMiniCart() {
1877            if (this.options.enableUserInteractionRefreshMiniCart) {
1878                this.refreshMiniCartOnUserInteraction();
1879            } else {
1880                this.actionRefreshMiniCart();
1881            }
1882        }
1883
1884        refreshMiniCartOnUserInteraction() {
1885            const refreshMiniCart = () => {
1886                this.userInteractionEvents.forEach(eventType =>
1887                    window.removeEventListener(eventType, refreshMiniCart)
1888                );
1889                this.actionRefreshMiniCart();
1890            };
1891            const opts = { once: true, passive: true };
1892            this.userInteractionEvents.forEach(eventType =>
1893                window.addEventListener(eventType, refreshMiniCart, opts)
1894            );
1895        }
1896
1897                reloadCustomerLoginPage() {
1898            const backendLoggedInState = this.options.isCustomerLoggedIn;
1899
1900            let customerData = null;
1901            try {
1902                const cacheStorage = localStorage.getItem('mage-cache-storage');
1903                if (cacheStorage) {
1904                    customerData = JSON.parse(cacheStorage).customer || null;
1905                }
1906            } catch {
1907                // Ignore parse errors silently
1908            }
1909
1910            const frontendLoggedInState = Boolean(customerData?.firstname);
1911
1912            if (frontendLoggedInState !== backendLoggedInState && !this._reloadTriggered) {
1913                this._reloadTriggered = true;
1914                window.location.reload();
1915            }
1916        }
1917
1918                actionRefreshMiniCart() {
1919            window.dispatchEvent(new CustomEvent('reload-customer-section-data'));
1920        }
1921
1922                actionAutoCloseMenu(autoCloseMenuMobile) {
1923            window.dispatchEvent(new Event('clear-messages'));
1924
1925            const cartDrawer = this.getElement('cartDrawer');
1926            if (cartDrawer && typeof Alpine !== 'undefined') {
1927                requestAnimationFrame(() => {
1928                    const cartDrawerData = Alpine.$data(cartDrawer);
1929                    if (cartDrawerData?.open !== undefined) {
1930                        cartDrawerData.open = false;
1931                        document.body.style.overflow = '';
1932                    }
1933                });
1934            }
1935
1936            if (autoCloseMenuMobile) {
1937                this.handleMenuClosure();
1938            }
1939        }
1940
1941                handleMenuClosure() {
1942            if (typeof Alpine === 'undefined' || !Alpine.$data) return;
1943
1944            const mobileMenu = this.getElement('mobileMenu');
1945            if (mobileMenu) {
1946                const mobileMenuData = Alpine.$data(mobileMenu);
1947                if (mobileMenuData?.open !== undefined) {
1948                    mobileMenuData.open = false;
1949                }
1950            }
1951
1952            const desktopMenu = this.getElement('desktopMenu');
1953            if (desktopMenu) {
1954                const desktopMenuData = Alpine.$data(desktopMenu);
1955                if (desktopMenuData?.hoverPanelActiveId !== undefined) {
1956                    desktopMenuData.hoverPanelActiveId = 0;
1957                }
1958            }
1959        }
1960    }
1961
1962        window.addEventListener('pageshow', (event) => {
1963        if (event.persisted) {
1964            (window._bfcacheHandler ??= new BFCacheHandler()).init();
1965        }
1966    }, { passive: true });
1967})();
1968</script>
1968
1969
1970<script>
1971    (() => {
1972        function src_default(Alpine) {
1973            Alpine.directive("intersect", Alpine.skipDuringClone((el, { value, expression, modifiers }, { evaluateLater, cleanup }) => {
1974                let evaluate = evaluateLater(expression);
1975                let options = {
1976                    rootMargin: getRootMargin(modifiers),
1977                    threshold: getThreshold(modifiers)
1978                };
1979                let observer = new IntersectionObserver((entries) => {
1980                    entries.forEach((entry) => {
1981                        if (entry.isIntersecting === (value === "leave"))
1982                            return;
1983                        evaluate();
1984                        modifiers.includes("once") && observer.disconnect();
1985                    });
1986                }, options);
1987                observer.observe(el);
1988                cleanup(() => {
1989                    observer.disconnect();
1990                });
1991            }));
1992        }
1993        function getThreshold(modifiers) {
1994            if (modifiers.includes("full"))
1995                return 0.99;
1996            if (modifiers.includes("half"))
1997                return 0.5;
1998            if (!modifiers.includes("threshold"))
1999                return 0;
2000            let threshold = modifiers[modifiers.indexOf("threshold") + 1];
2001            if (threshold === "100")
2002                return 1;
2003            if (threshold === "0")
2004                return 0;
2005            return Number(`.${threshold}`);
2006        }
2007        function getLengthValue(rawValue) {
2008            let match = rawValue.match(/^(-?[0-9]+)(px|%)?$/);
2009            return match ? match[1] + (match[2] || "px") : void 0;
2010        }
2011        function getRootMargin(modifiers) {
2012            const key = "margin";
2013            const fallback = "0px 0px 0px 0px";
2014            const index = modifiers.indexOf(key);
2015            if (index === -1)
2016                return fallback;
2017            let values = [];
2018            for (let i = 1; i < 5; i++) {
2019                values.push(getLengthValue(modifiers[index + i] || ""));
2020            }
2021            values = values.filter((v) => v !== void 0);
2022            return values.length ? values.join(" ").trim() : fallback;
2023        }
2024
2025        document.addEventListener("alpine:init", () => {
2026            window.Alpine.plugin(src_default);
2027        });
2028    })();
2029    //# sourceURL=alpine-intersect.js
2030</script>
2030
2031
2032<script>
2033    let SnapSlider = class {
2034        constructor(el, {
2035            labelSepparator = 'van',
2036            autoPager,
2037            groupPager
2038        } = {}) {
2039            this.el = el;
2040            this.track = this.el.querySelector("[data-track]");
2041            if (!this.track) {
2042                console.warn(
2043                    "No Slider track defined, reverting back to CSS slider.\nPlease create a wrapper for your slides with the attribute data-track",
2044                    this.el
2045                );
2046                return;
2047            }
2048            this.initialLoad = true;
2049            this.slides = [];
2050            this.inViewObserver;
2051            this.mutationObserver;
2052            this.resizeObserver;
2053            this.pager = this.el.querySelector("[data-pager]");
2054            this.navBtns = Array.from(
2055                this.el.querySelectorAll("[data-next], [data-prev]")
2056            );
2057            this.slideLabelSepparator = this.el.dataset.slideLabelSepparator || labelSepparator;
2058            this.useAutoPager = autoPager || this.el.hasAttribute("data-auto-pager") || false;
2059            this.useGroupPager = groupPager || this.el.hasAttribute("data-group-pager") || false;
2060            this.sliderLabel = this.el.hasAttribute("aria-label") && this.el.getAttribute("aria-label").toLowerCase().trim().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
2061            this.sliderId = this.el.id || this.sliderLabel || "slider";
2062            this.markerIdName = "data-target-id";
2063            this.pagerClasses = this.el.dataset.pagerClass || "snap-pager";
2064            this.markerClasses = this.el.dataset.markerClass || "snap-marker";
2065            this.init();
2066        }
2067        init() {
2068            this.setupSlides();
2069            this.setupNav();
2070            if (this.useAutoPager) {
2071                this.createPager();
2072            } else {
2073                this.setupPager();
2074            }
2075            this.setupObservers();
2076            this.setupMutationObserver();
2077            this.setupResizeObserver();
2078            this.el.addEventListener("click", this.eventHandler.bind(this));
2079            this.el.addEventListener("keydown", this.eventHandler.bind(this));
2080        }
2081        destroy() {
2082            this.inViewObserver?.disconnect();
2083            this.mutationObserver?.disconnect();
2084            this.resizeObserver?.disconnect();
2085            this.el.removeEventListener("click", this.eventHandler.bind(this));
2086            this.el.removeEventListener("keydown", this.eventHandler.bind(this));
2087        }
2088        roundUpIfGreaterThan(number, min = 8) {
2089            const decimalPart = number - Math.floor(number);
2090            return decimalPart >= min ? Math.ceil(number) : Math.floor(number);
2091        }
2092        getInViewItems() {
2093            const inViewSlides = this.track.querySelectorAll("[data-in-view]");
2094            const firstInViewSlide = inViewSlides[0] || null;
2095            const lastInViewSlide = inViewSlides[inViewSlides.length - 1] || null;
2096            const isAtStart = firstInViewSlide === this.slides[0];
2097            const isAtEnd = lastInViewSlide === this.slides[this.slides.length - 1];
2098            return {
2099                inViewSlides,
2100                totalInViewSlides: inViewSlides.length,
2101                firstInViewSlide,
2102                lastInViewSlide,
2103                isAtStart,
2104                isAtEnd,
2105                hasNoOverflow: isAtStart && isAtEnd
2106            };
2107        }
2108        groupPagerMarkers() {
2109            if (this.slides.length === 0 || !this.pager || !this.useGroupPager) return;
2110            const totalVisibleSlides = this.roundUpIfGreaterThan(
2111                this.track.offsetWidth / this.slides[0].offsetWidth
2112            );
2113            const markers = Array.from(this.pager.querySelectorAll("a, button"));
2114            markers.forEach((marker, index) => {
2115                marker.style.display = index % totalVisibleSlides === 0 ? null : "none";
2116            });
2117        }
2118        handleInView(entries) {
2119            entries.forEach((entry) => {
2120                const marker = this.pager && (this.pager.querySelector(`[href="#${entry.target.id}"]`) || this.pager.querySelector(
2121                    `[${this.markerIdName}="${entry.target.id}"]`
2122                ));
2123                entry.target.toggleAttribute("data-in-view", entry.isIntersecting);
2124                entry.target.toggleAttribute("inert", !entry.isIntersecting);
2125                marker?.setAttribute("aria-current", entry.isIntersecting);
2126                marker?.setAttribute("tabindex", entry.isIntersecting ? "0" : "-1");
2127            });
2128            const {
2129                isAtStart,
2130                isAtEnd,
2131                hasNoOverflow
2132            } = this.getInViewItems();
2133            this.navBtns.forEach((btn) => {
2134                if (btn.hasAttribute("data-next")) {
2135                    btn.style.visibility = hasNoOverflow ? "hidden" : null;
2136                    isAtEnd ? btn.setAttribute("disabled", "") : btn.removeAttribute("disabled");
2137                }
2138                if (btn.hasAttribute("data-prev")) {
2139                    btn.style.visibility = hasNoOverflow ? "hidden" : null;
2140                    isAtStart ? btn.setAttribute("disabled", "") : btn.removeAttribute("disabled");
2141                }
2142            });
2143            if (this.pager) {
2144                this.pager.style.visibility = hasNoOverflow ? "hidden" : null;
2145            }
2146            if (!this.initialLoad) {
2147                this.el.dispatchEvent(
2148                    new CustomEvent("slideChange", {
2149                        detail: this.getInViewItems()
2150                    })
2151                );
2152            } else {
2153                this.initialLoad = false;
2154            }
2155            if (document.activeElement.parentElement.hasAttribute("data-pager")) {
2156                const activeItems = this.pager.querySelectorAll('[tabindex="0"]');
2157                if (activeItems.length) {
2158                    activeItems[0].focus({ preventScroll: true });
2159                }
2160            }
2161        }
2162        getSlides() {
2163            if (!this.track) return [];
2164            return Array.from(this.track.children).filter(
2165                (child) => child.tagName.toLowerCase() !== "template" && child.tagName.toLowerCase() !== "style" && child.tagName.toLowerCase() !== "script"
2166            );
2167        }
2168        refreshSlides() {
2169            this.initialLoad = true;
2170            this.slides = this.getSlides();
2171            this.setupSlides();
2172            if (this.useAutoPager) {
2173                this.createPager();
2174            } else {
2175                this.setupPager();
2176            }
2177            this.groupPagerMarkers();
2178            this.setupObservers();
2179        }
2180        setupSlides() {
2181            this.slides = this.getSlides();
2182            this.slides.forEach((slide, index) => {
2183                const totalSlides = this.slides.length;
2184                const currentSlide = index + 1;
2185                const existingLabel = slide.getAttribute("aria-label") || "";
2186                const isImage = slide.tagName.toLowerCase() === "img" || slide.tagName.toLowerCase() === "picture";
2187                const slideLabelTag = isImage ? "alt" : "aria-label";
2188                const hasAutoLabel = existingLabel.startsWith(
2189                    `${currentSlide} ${this.slideLabelSepparator} `
2190                );
2191                if (!slide.hasAttribute("id")) {
2192                    slide.setAttribute(
2193                        "id",
2194                        `${this.sliderId}-item-${currentSlide}`
2195                    );
2196                }
2197                if (!slide.hasAttribute(slideLabelTag) || slide.getAttribute(slideLabelTag) === "" || hasAutoLabel) {
2198                    slide.setAttribute(
2199                        slideLabelTag,
2200                        `${currentSlide} ${this.slideLabelSepparator} ${totalSlides}`
2201                    );
2202                }
2203                if (slide.tagName.toLowerCase() === "div" && !slide.hasAttribute("role")) {
2204                    slide.setAttribute("role", "group");
2205                }
2206                if (this.pager && !isImage) {
2207                    slide.setAttribute("role", "tabpanel");
2208                }
2209                if (slide.getAttribute("role") === "group") {
2210                    slide.setAttribute("aria-roledescription", "item");
2211                }
2212            });
2213            if (!this.el.hasAttribute("role") && this.el.tagName !== "SECTION") {
2214                this.el.setAttribute("role", "region");
2215            }
2216            if (!this.el.hasAttribute("aria-roledescription")) {
2217                this.el.setAttribute("aria-roledescription", "carousel");
2218            }
2219            this.track.setAttribute("tabindex", 0);
2220            this.track.setAttribute("aria-live", "polite");
2221        }
2222        setupNav() {
2223            this.navBtns.forEach((btn) => {
2224                btn.setAttribute("disabled", "");
2225                btn.removeAttribute("hidden");
2226                btn.classList.remove("invisible");
2227                btn.style.visibility = null;
2228            });
2229        }
2230        createPager() {
2231            const newPager = document.createElement("nav");
2232            newPager.setAttribute("data-pager", "");
2233            newPager.setAttribute("role", "tablist");
2234            newPager.classList.add(...this.pagerClasses.split(" "));
2235            this.slides.forEach((slide, index) => {
2236                const marker = document.createElement("button");
2237                const slideId = slide.id || `${this.sliderId}-item-${index + 1}`;
2238                marker.setAttribute(this.markerIdName, slideId);
2239                marker.classList.add(...this.markerClasses.split(" "));
2240                this.setupPagerMarker(marker, index);
2241                newPager.appendChild(marker);
2242                if (slide.tagName.toLowerCase() !== "img" && slide.tagName.toLowerCase() !== "picture") {
2243                    slide.removeAttribute("aria-roledescription");
2244                    slide.setAttribute("role", "tabpanel");
2245                }
2246            });
2247            if (this.pager) {
2248                this.pager.setAttribute("role", "tablist");
2249                this.pager.replaceChildren(...newPager.children);
2250            } else {
2251                this.track.after(newPager);
2252                this.pager = this.el.querySelector("[data-pager]");
2253            }
2254        }
2255        setupPager() {
2256            if (!this.pager) return;
2257            this.pager.setAttribute("role", "tablist");
2258            const items = Array.from(this.pager.querySelectorAll("a, button"));
2259            items.forEach((marker, index) => this.setupPagerMarker(marker, index));
2260        }
2261        setupPagerMarker(marker, index) {
2262            const markerId = marker.getAttribute("href")?.slice(1) || marker.getAttribute(this.markerIdName);
2263            const slideId = markerId || `${this.sliderId}-item-${index + 1}`;
2264            if (!markerId) {
2265                marker.setAttribute(this.markerIdName, slideId);
2266            }
2267            marker.setAttribute("role", "tab");
2268            marker.setAttribute("aria-controls", slideId);
2269            marker.setAttribute("aria-posinset", index + 1);
2270            marker.setAttribute("aria-setsize", this.slides.length);
2271            marker.setAttribute("tabindex", "-1");
2272            if (!marker.hasAttribute("aria-label")) {
2273                marker.setAttribute("aria-label", `Slide ${index + 1}`);
2274            }
2275        }
2276        setupObservers() {
2277            if (this.inViewObserver) {
2278                this.inViewObserver.disconnect();
2279            }
2280            this.inViewObserver = new IntersectionObserver(
2281                this.handleInView.bind(this), {
2282                    root: this.track,
2283                    threshold: 0.8
2284                }
2285            );
2286            this.slides.forEach((slide) => this.inViewObserver.observe(slide));
2287        }
2288        setupMutationObserver() {
2289            this.mutationObserver = new MutationObserver(
2290                this.refreshSlides.bind(this)
2291            );
2292            this.mutationObserver.observe(this.track, {
2293                childList: true,
2294                subtree: false
2295            });
2296        }
2297        setupResizeObserver() {
2298            this.resizeObserver = new ResizeObserver(
2299                this.groupPagerMarkers.bind(this)
2300            );
2301            this.resizeObserver.observe(this.el);
2302        }
2303        goToSlideDir(dir = "next") {
2304            const {
2305                firstInViewSlide,
2306                lastInViewSlide
2307            } = this.getInViewItems();
2308            const isPrev = dir === "prev";
2309            let targetSlide = isPrev ? firstInViewSlide?.previousElementSibling : lastInViewSlide?.nextElementSibling;
2310            if (!targetSlide) return;
2311            if (targetSlide.tagName.toLowerCase() === "template") {
2312                targetSlide = isPrev ? targetSlide?.previousElementSibling : targetSlide?.nextElementSibling;
2313            }
2314            targetSlide.scrollIntoView({ block: "nearest", inline: isPrev ? "end" : "start" });
2315        }
2316        pagerToSlide(event) {
2317            if (!event.target.closest("[data-pager]")) return;
2318            const marker = event.target.closest("a, button");
2319            if (!marker) return;
2320            event.preventDefault();
2321            const slideId = marker.getAttribute("href")?.slice(1) || marker.getAttribute(this.markerIdName);
2322            const targetSlide = this.track.querySelector(`#${slideId}`);
2323            targetSlide?.scrollIntoView({ block: "nearest", inline: "start" });
2324        }
2325        eventHandler(event) {
2326            const target = event.target.closest(
2327                "[data-next], [data-prev], [data-pager]"
2328            );
2329            if (!target) return;
2330            if (event.type === "click") {
2331                if (target.hasAttribute("data-next")) {
2332                    this.goToSlideDir("next");
2333                } else if (target.hasAttribute("data-prev")) {
2334                    this.goToSlideDir("prev");
2335                } else if (target.closest("[data-pager]")) {
2336                    this.pagerToSlide(event);
2337                }
2338            }
2339            if (event.type === "keydown" && target.closest("[data-pager]")) {
2340                if (event.key === "ArrowRight") {
2341                    this.goToSlideDir("next");
2342                } else if (event.key === "ArrowLeft") {
2343                    this.goToSlideDir("prev");
2344                }
2345            }
2346        }
2347    };
2348
2349    (() => {
2350        function alpineSnapSlider(Alpine) {
2351            Alpine.directive("snap-slider", (el, {
2352                modifiers
2353            }, {
2354                cleanup
2355            }) => {
2356                const autoPager = modifiers.includes("auto-pager");
2357                const groupPager = modifiers.includes("group-pager");
2358                const slider = new SnapSlider(el, {
2359                    autoPager,
2360                    groupPager
2361                });
2362                cleanup(() => {
2363                    slider.destroy();
2364                });
2365            });
2366        }
2367
2368        document.addEventListener("alpine:init", () => {
2369            window.Alpine.plugin(alpineSnapSlider);
2370        });
2371    })();
2372</script>
2372
2373
2374<script type="module"
2375        src="https://uitgeverijpluim.nl/static/version1790668939/frontend/Pluim/Hyva/nl_NL/Hyva_Theme/js/alpine3.min.js"
2376        defer
2377        crossorigin
2378></script>
vendor: 1 bytes, line 2378
2378
2379<script>
2380    function dispatchMessages(messages, hideAfter) {
2381        const messagesEvent = new CustomEvent("messages-loaded", {
2382            detail: {
2383                messages: messages,
2384                hideAfter: hideAfter
2385            }
2386        });
2387        window.dispatchEvent(messagesEvent);
2388    }
2389
2390        if (typeof hyva === 'undefined' || (!hyva.getBrowserStorage || !hyva.getCookie || !hyva.setCookie)) {
2391        console.warn("Hyvä helpers are not loaded yet. Make sure they are included before this script");
2392    }
2393
2394    (function( hyva, undefined ) {
2395
2396        hyva.initFormKey = () => {
2397            if (typeof hyva.getFormKey !== 'function') return;
2398
2399            const formKey = hyva.getFormKey();
2400            if (!formKey) return;
2401
2402            document.querySelectorAll('input[name="form_key"]').forEach((input) => {
2403                input.value = formKey;
2404            });
2405        }
2406
2407        hyva.initMessages = () => {
2408            try {
2409                const messages = hyva.getCookie('mage-messages');
2410                window.mageMessages = messages ? JSON.parse(decodeURIComponent(messages).replace(/\+/g, ' ')) : [];
2411
2412                dispatchMessages(window.mageMessages);
2413
2414                                // empty `mage-messages` cookie
2415                const skipSetDomain = true;
2416                hyva.setCookie('mage-messages','', -1, skipSetDomain);
2417
2418            } catch (error) {
2419                console.warn('Error parsing Cookie Messages:', error);
2420            }
2421        }
2422
2423        window.addEventListener('DOMContentLoaded', hyva.initFormKey);
2424
2425        window.addEventListener('pageshow', (event) => {
2426            if (event.persisted) {
2427                hyva.initFormKey();
2428            }
2429        });
2430
2431        hyva.alpineInitialized(hyva.initMessages)
2432    }( window.hyva = window.hyva || {} ));
2433    //# sourceURL=hyva-cookies.js
2434</script>
vendor: 1 bytes, line 2434
2434
2435<script>
2436    (() => {
2437        const private_content_key = 'mage-cache-storage';
2438        const private_content_expire_key = 'mage-cache-timeout';
2439        const private_content_version_key = 'private_content_version';
2440        const section_data_ids_key = 'section_data_ids';
2441        const mage_cache_session_id_key = 'mage-cache-sessid';
2442        const last_visited_store_key = 'last_visited_store';
2443
2444        const ttl = 3600;
2445
2446                if (typeof hyva === 'undefined' || (!hyva.getBrowserStorage || !hyva.getCookie || !hyva.setCookie)) {
2447            console.warn("Hyvä helpers are not loaded yet. Make sure they are included before this script");
2448        }
2449
2450        function loadSectionData () {
2451            const browserStorage = hyva.getBrowserStorage();
2452            if (!browserStorage) {
2453                typeof window.dispatchMessages !== "undefined" && window.dispatchMessages(
2454                    [{
2455                        type: "warning",
2456                        text: "Schakel LocalStorage in in je browser."
2457                    }]
2458                );
2459                return;
2460            }
2461            try {
2462                let isInvalid = false;
2463
2464                                if (hyva.getCookie(last_visited_store_key) !== CURRENT_STORE_CODE) {
2465                    isInvalid = true;
2466                }
2467                hyva.setCookie(last_visited_store_key, CURRENT_STORE_CODE, false, false);
2468
2469                                if (!hyva.getCookie(mage_cache_session_id_key)) {
2470                    isInvalid = true;
2471                    browserStorage.removeItem(private_content_key);
2472                    const skipSetDomain = true;
2473                    const days = false;
2474                    hyva.setCookie(mage_cache_session_id_key, true, days, skipSetDomain)
2475                }
2476
2477                const cookieVersion = hyva.getCookie(private_content_version_key);
2478                const storageVersion = browserStorage.getItem(private_content_version_key);
2479
2480                if (cookieVersion && !storageVersion || cookieVersion !== storageVersion) {
2481                    isInvalid = true;
2482                }
2483
2484                const privateContentExpires = browserStorage.getItem(private_content_expire_key);
2485                if (privateContentExpires && new Date(privateContentExpires) < new Date()) {
2486                    browserStorage.removeItem(private_content_key);
2487                }
2488
2489                if (isInvalid && cookieVersion) {
2490                    fetchPrivateContent([]);
2491                } else if (cookieVersion && storageVersion && cookieVersion === storageVersion) {
2492                    const privateContent = JSON.parse(browserStorage.getItem(private_content_key));
2493                    if (
2494                        privateContent &&
2495                        privateContentExpires &&
2496                        privateContent.cart &&
2497                        privateContent.customer
2498                    ) {
2499                        dispatchPrivateContent(privateContent);
2500                    } else {
2501                        fetchPrivateContent([]);
2502                    }
2503                } else {
2504                    if (document.getElementById('default-section-data')) {
2505                        const privateContent = JSON.parse(document.getElementById('default-section-data').textContent.trim());
2506                        dispatchPrivateContent(privateContent);
2507                    } else {
2508                        dispatchPrivateContent({});
2509                    }
2510                }
2511
2512            } catch (error) {
2513                console.warn('Error retrieving Private Content:', error);
2514            }
2515        }
2516
2517        hyva.alpineInitialized(loadSectionData)
2518        window.addEventListener('reload-customer-section-data', loadSectionData);
2519
2520                window.processSectionDataBeforeDispatch = window.processSectionDataBeforeDispatch || (data => data);
2521
2522        function dispatchPrivateContent(data) {
2523
2524            const privateContentEvent = new CustomEvent("private-content-loaded", {
2525                detail: {
2526                    data: window.processSectionDataBeforeDispatch(data)
2527                }
2528            });
2529            window.dispatchEvent(privateContentEvent);
2530        }
2531
2532                function fetchPrivateContent(sections) {
2533            fetch(`${BASE_URL}customer/section/load/?sections=${encodeURIComponent(sections.join(','))}`, {
2534                method: 'GET',
2535                headers: {
2536                    'Content-Type': 'application/json',
2537                    'X-Requested-With': 'XMLHttpRequest'
2538                }
2539            })
2540                .then(response => response.ok && response.json())
2541                .then(
2542                    data => {
2543                        if (data) {
2544                            try {
2545                                const browserStorage = hyva.getBrowserStorage();
2546
2547                                // merge new data preserving non-invalidated sections
2548                                const oldSectionData = JSON.parse(browserStorage.getItem(private_content_key) || '{}') || {};
2549
2550                                                                if ((! data.cart || ! data.cart.cartId) && oldSectionData['checkout-data']) {
2551                                    delete oldSectionData['checkout-data'];
2552                                }
2553                                const newSectionData = Object.assign(oldSectionData, data);
2554
2555                                dispatchPrivateContent(newSectionData);
2556
2557                                // don't persist messages, they've been dispatched already
2558                                if (newSectionData.messages && newSectionData.messages.messages ) {
2559                                    newSectionData.messages.messages = [];
2560                                }
2561
2562                                browserStorage.setItem(private_content_key, JSON.stringify(newSectionData));
2563
2564                                const expiresAt = new Date(Date.now() + (ttl * 1000)).toISOString();
2565                                browserStorage.setItem(private_content_expire_key, expiresAt);
2566
2567                                const newCookieVersion = hyva.getCookie(private_content_version_key);
2568                                browserStorage.setItem(private_content_version_key, newCookieVersion);
2569
2570                                // We don't need the section_data_ids in Hyvä, but we store them for compatibility
2571                                // with Luma Fallback. Otherwise, not all sections are loaded in Luma Checkout
2572                                hyva.setCookie(
2573                                    section_data_ids_key,
2574                                    JSON.stringify(
2575                                        Object.keys(data).reduce((sectionDataIds, sectionKey) => {
2576                                            sectionDataIds[sectionKey] = data[sectionKey]['data_id'];
2577                                            return sectionDataIds;
2578                                        }, {})
2579                                    ),
2580                                    false,
2581                                    true
2582                                );
2583                            } catch (error) {
2584                                console.warn("Couldn't store privateContent", error);
2585                            }
2586                        }
2587                    }
2588                )
2589                .catch(error => {
2590                    console.warn("Couldn't fetch privateContent", error);
2591                });
2592        }
2593    })();
2594    //# sourceURL=hyva-private-content.js
2595</script>
vendor: 1 bytes, line 2595
2595
2596<script>
2597    (() => {
2598        document.addEventListener('submit', event => event.target.action = event.target.action.replace('%25uenc%25', hyva.getUenc()));
2599    })()
2600</script>
vendor: 1 bytes, line 2600
2600
2601<script>
2602    (events => {
2603        const dispatchUserInteractionEvent = () => {
2604            events.forEach(type => window.removeEventListener(type, dispatchUserInteractionEvent))
2605            window.dispatchEvent(new Event('init-external-scripts'))
2606        };
2607        events.forEach(type => window.addEventListener(type, dispatchUserInteractionEvent, {once: true, passive: true}))
2608    })(['touchstart', 'mousemove', 'wheel', 'scroll', 'keydown']);
2609    //# sourceURL=hyva-init-external-scripts.js
2610</script>
vendor: 1 bytes, line 2610
2610
2611<script>
2612    document.addEventListener("alpine:init", () => {
2613        Alpine.data('hyvaCmsToggle', () => ({
2614            open: false,
2615            notOpen() {
2616                return !this.open;
2617            },
2618            show() {
2619                this.open = true
2620            },
2621            hide() {
2622                this.open = false
2623            },
2624            toggle() {
2625                this.open = !this.open
2626            }
2627        }))
2628    });
2629</script>
vendor: 1 bytes, line 2629
2629
2630<script type="text/json" id="default-section-data">
2631    {"messages":[],"customer":[],"compare-products":[],"last-ordered-items":[],"cart":[],"directory-data":{"BE":{"name":"Belgi\u00eb","regions":{"609":{"code":"VAN","name":"Antwerpen"},"610":{"code":"WBR","name":"Brabant wallon"},"611":{"code":"BRU","name":"Brussels-Capital Region"},"612":{"code":"WHT","name":"Hainaut"},"614":{"code":"WLG","name":"Li\u00e8ge"},"613":{"code":"VLI","name":"Limburg"},"615":{"code":"WLX","name":"Luxembourg"},"616":{"code":"WNA","name":"Namur"},"617":{"code":"VOV","name":"Oost-Vlaanderen"},"618":{"code":"VBR","name":"Vlaams-Brabant"},"619":{"code":"VWV","name":"West-Vlaanderen"}}},"NL":{"name":"Nederland"},"data_id":1790918008},"instant-purchase":[],"loggedAsCustomer":[],"captcha":[],"persistent":[],"review":[],"wishlist":{"items":[]},"hyva_commerce_messages":[],"hyva_checkout":[],"gtm-checkout":[],"recently_viewed_product":[],"recently_compared_product":[],"product_data_storage":[],"paypal-billing-agreement":[],"paypal-buyer-country":[]}</script>
vendor: 1 bytes, line 2631
2631
2632<script>
2633    window.processSectionDataBeforeDispatch && (window.processSectionDataBeforeDispatch = new Proxy(window.processSectionDataBeforeDispatch, {
2634        apply(target, thisArg, [data]) {
2635            data.hyva_commerce_messages = data.hyva_commerce_messages || {};
2636            return target.call(thisArg, data);
2637        }
2638    }));
2639</script>
vendor: 1 bytes, line 2639
2639
2640<script>if (!window.__rumvisionSkip) {
2641    window.rumv = window.rumv || function() { (window.rumv.q = window.rumv.q || []).push(arguments) };
2642    (function(rum, vi,si,on) {
2643        var s = JSON.parse( sessionStorage.getItem('rumv') || '{"pageviews":0}' ); s.pageviews++;
2644        if ( s.urls && s.regex && ( s.page = eval('('+s.regex+')')( s.urls, vi.location.pathname ) ) && !s.page.type ) {
2645            return sessionStorage.setItem('rumv', JSON.stringify( s ) );
2646        }
2647
2648        vi.rumv.storage = s;
2649        var head = si.querySelector('head'), js = si.createElement('script');
2650        js.src = 'https://d5yoctgpv4cpx.cloudfront.net/'+rum+'/v5-'+vi.location.hostname+'.js';
2651        head.appendChild(js);
2652    })( 'RUM\u002D409605D6E0', window, document, 'uitgeverijpluim.nl' );
2653}</script>
2653</div>
2653<script nonce="MzJpbnJpdDZjbzFsOHF3bG5pazUydXh3b3RsYnU3bHk=">
2654    yireoGoogleTagManager2Pusher({"version":"3.10.7","page_type":"prismicio\/direct\/singleton","page_path":"https:\/\/uitgeverijpluim.nl\/","page_title":"Homepage  | Uitgeverij Pluim","virtual_page":"","breadcrumb":[],"user":{"has_transacted":false}}, 'push (initial page) [data-layer.phtml]');
2655    </script>
vendor: 1 bytes, line 2655
2655
2656<script nonce="MzJpbnJpdDZjbzFsOHF3bG5pazUydXh3b3RsYnU3bHk=">
2657    function yireoGoogleTagManager2FindParentElementWithName(element, parentTagName) {
2658        if (!element.parentElement) {
2659            return null;
2660        }
2661
2662        if (element.parentElement.tagName.toLowerCase() === parentTagName) {
2663            return element.parentElement;
2664        }
2665
2666        return yireoGoogleTagManager2FindParentElementWithName(element.parentElement, parentTagName);
2667    }
2668
2669    const products = document.querySelectorAll('.products\u0020a.product,\u0020.products\u0020a.product\u002Ditem\u002Dlink,\u0020.card\u0020a.product\u002Ditem\u002Dlink,\u0020.card\u0020a.product\u002Ditem\u002Dphoto,\u0020.card\u0020a.btn\u002Dprimary');
2670    if (products && products.length > 0) {
2671        products.forEach(function(product) {
2672            product.addEventListener('click', function(event, s) {
2673                let parentElement = yireoGoogleTagManager2FindParentElementWithName(event.target, 'form');
2674                if (!parentElement) {
2675                    return;
2676                }
2677
2678                const inputElement = parentElement.querySelector('input[name="product"]');
2679                const productId = inputElement.value;
2680                const productData = window['YIREO_GOOGLETAGMANAGER2_PRODUCT_DATA_ID_' + productId] || {};
2681                productData.item_id = productId;
2682
2683                const eventData = {
2684                    'event': 'select_item',
2685                    'ecommerce': {
2686                        'items': [productData]
2687                    }
2688                }
2689
2690                yireoGoogleTagManager2Pusher(eventData, 'push (page event "select_item") [script-product-clicks.phtml]');
2691
2692                if (window['YIREO_GOOGLETAGMANAGER2_DEBUG_CLICKS'] && confirm("Press to continue with redirect") === false) {
2693                    event.preventDefault();
2694                }
2695            });
2696        })
2697    }
2698</script>
vendor: 1 bytes, line 2698
2698
2699<script nonce="MzJpbnJpdDZjbzFsOHF3bG5pazUydXh3b3RsYnU3bHk=">
2700    window.addEventListener('private-content-loaded', function(event) {
2701        window.dataLayer = window.dataLayer || [];
2702
2703        var isEmpty = function (variable) {
2704            if (typeof variable === 'undefined') {
2705                return true;
2706            }
2707
2708            if (Array.isArray(variable) && variable.length === 0) {
2709                return true;
2710            }
2711
2712            return typeof variable === 'object' && Object.keys(variable).length === 0;
2713        }
2714
2715        var getSectionNames = function () {
2716            return ['cart', 'customer'];
2717        }
2718
2719        getSectionNames().forEach(function (sectionName) {
2720            if (!event.detail.data[sectionName].gtm) {
2721                return;
2722            }
2723
2724            const attributes = event.detail.data[sectionName].gtm;
2725            if (!Object.keys(attributes).length > 0) {
2726                return;
2727            }
2728
2729            yireoGoogleTagManager2Pusher(attributes, 'push (attributes) [script-additions.phtml]');
2730        });
2731
2732        let attributes = {};
2733        getSectionNames().forEach(function (sectionName) {
2734            if (!event.detail.data[sectionName].gtm_events) {
2735                return;
2736            }
2737
2738            const gtmEvents = event.detail.data[sectionName].gtm_events;
2739            for (const [eventId, eventData] of Object.entries(gtmEvents)) {
2740                yireoGoogleTagManager2Logger('found event in section "' + sectionName + '"', eventData);
2741
2742                const metaData = eventData.meta;
2743                yireoGoogleTagManager2Pusher(eventData, 'push (customerData "' + sectionName + '"  event "' + eventId + '")');
2744
2745                if (!metaData || metaData.cacheable !== true) {
2746                    yireoGoogleTagManager2Logger('removing event "' + eventId + '" from section "' + sectionName + '"')
2747
2748                    let mageCacheStorage = event.detail.data;
2749
2750                    if (mageCacheStorage !== null
2751                        && typeof mageCacheStorage[sectionName] !== 'undefined'
2752                        && typeof mageCacheStorage[sectionName].gtm_events !== 'undefined'
2753                        && eventId in mageCacheStorage[sectionName].gtm_events) {
2754                        delete mageCacheStorage[sectionName].gtm_events[eventId];
2755                    }
2756
2757                    window.localStorage.setItem('mage-cache-storage', JSON.stringify(mageCacheStorage));
2758                }
2759            }
2760        });
2761
2762        if (false === isEmpty(attributes)) {
2763            yireoGoogleTagManager2Pusher(attributes, 'dataLayer (initial js) [script-additions.phtml]');
2764        }
2765    });
2766
2767    window.addEventListener('toggle-cart', (event) => {
2768        if (!event.detail || !event.detail.isOpen) {
2769            return;
2770        }
2771
2772        window.dispatchEvent(new CustomEvent('minicart_collapse'));
2773    });
2774</script>
2774
2775</body>
2776</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.