PageSourceSearch

https://uitgeverijpluim.nl/boeken

html uitgeverijpluim.nl collected 2026-09-24 19:10:19 UTC 216,133 bytes, 4,055 lines download raw bytes

1<!doctype html>
2<html lang="nl">
3<head >
4    <meta charset="utf-8"/>
5<meta name="title" content="Boeken  | 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<meta name="page-type" content="product-listing"/>
11<title>Boeken  | Uitgeverij Pluim</title>
12        <link rel="stylesheet" type="text/css" defer="defer" href="https://uitgeverijpluim.nl/static/version1790248052/frontend/Pluim/Hyva/nl_NL/css/styles.css" />
13<script type="text/javascript" src="https://uitgeverijpluim.nl/static/version1790248052/frontend/Pluim/Hyva/nl_NL/Smile_ElasticsuiteTracker/js/tracking.js"></script>
13
14<link rel="canonical" href="https://uitgeverijpluim.nl/boeken" />
15<link rel="icon" type="image/x-icon" href="https://uitgeverijpluim.nl/media/favicon/websites/1/logo_sxfncc_1.jpg" />
16<link rel="shortcut icon" type="image/x-icon" href="https://uitgeverijpluim.nl/media/favicon/websites/1/logo_sxfncc_1.jpg" />
17<link rel="dns-prefetch" href="https://acdn.adnxs.com">
18<link rel="dns-prefetch" href="https://ib.adnxs.com">
19
20<!-- Xandr Universal Pixel - Optimized -->
21<script async defer>
22(function() {
23  // Initialize pixie stub immediately
24  !function(e,i){
25    if(!e.pixie){
26      var n=e.pixie=function(e,i,a){
27        n.actionQueue.push({action:e,actionValue:i,params:a})
28      };
29      n.actionQueue=[];
30      var a=i.createElement("script");
31      a.async=!0;
32      a.src="https://acdn.adnxs.com/dmp/up/pixie.js";
33      var t=i.getElementsByTagName("head")[0];
34      t.insertBefore(a,t.firstChild)
35    }
36  }(window,document);
37  
38  // Queue consent and initialization
39  pixie('consent', 'default', { 'ad_storage': 'granted', 'wait_for_update': 1000});
40  pixie('init', 'cf677036-49f6-4ee6-8255-72e7d7a8944c');
41  pixie('event', 'PageView');
42})();
43</script>
43    
44<script nonce="ZTc3MHNqenIwcWZ2NnRtNHpuY2VjbnV2MDhwbWRtMDI=">
45    window.YIREO_GOOGLETAGMANAGER2_ENABLED = true;
46    (function (events) {
47        const initYireoGoogleTagManager2 = function () {
48            events.forEach(function (eventType) {
49                window.removeEventListener(eventType, initYireoGoogleTagManager2);
50            });
51
52                        (function (w, d, s, l, i) {
53                w[l] = w[l] || [];
54                w[l].push({'gtm.start': new Date().getTime(), event: 'gtm.js'});
55                var f = d.getElementsByTagName(s)[0],
56                    j = d.createElement(s),
57                    dl = l != 'dataLayer' ? '&l=' + l : '';
58                j.async = true;
59                j.src = 'https\u003A\u002F\u002Fwww.googletagmanager.com' + '/gtm.js?id=' + i + dl;
60                f.parentNode.insertBefore(j, f);
61            })(window, document, 'script', 'dataLayer', 'GTM\u002DWDGVFB9');
62                    };
63
64        events.forEach(function (eventType) {
65            window.addEventListener(eventType, initYireoGoogleTagManager2, {once: true, passive: true})
66        });
67    })(['load', 'keydown', 'mouseover', 'scroll', 'touchstart', 'wheel']);
68
69    </script>
vendor: 1 bytes, line 69
69
70<script>
71    (function( hyva, undefined ) {
72
73        function lifetimeToExpires(options, defaults) {
74
75            const lifetime = options.lifetime || defaults.lifetime;
76
77            if (lifetime) {
78                const date = new Date;
79                date.setTime(date.getTime() + lifetime * 1000);
80                return date;
81            }
82
83            return null;
84        }
85
86        function generateRandomString() {
87
88            const allowedCharacters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
89                length = 16;
90
91            let formKey = '',
92                charactersLength = allowedCharacters.length;
93
94            for (let i = 0; i < length; i++) {
95                formKey += allowedCharacters[Math.round(Math.random() * (charactersLength - 1))]
96            }
97
98            return formKey;
99        }
100
101        const sessionCookieMarker = {noLifetime: true}
102
103        const cookieTempStorage = {};
104
105        const internalCookie = {
106            get(name) {
107                const v = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
108                return v ? v[2] : null;
109            },
110            set(name, value, days, skipSetDomain) {
111                let expires,
112                    path,
113                    domain,
114                    secure,
115                    samesite;
116
117                const defaultCookieConfig = {
118                    expires: null,
119                    path: '/',
120                    domain: null,
121                    secure: false,
122                    lifetime: null,
123                    samesite: 'lax'
124                };
125
126                const cookieConfig = window.COOKIE_CONFIG || {};
127
128                expires = days && days !== sessionCookieMarker
129                    ? lifetimeToExpires({lifetime: 24 * 60 * 60 * days, expires: null}, defaultCookieConfig)
130                    : lifetimeToExpires(window.COOKIE_CONFIG, defaultCookieConfig) || defaultCookieConfig.expires;
131
132                path = cookieConfig.path || defaultCookieConfig.path;
133                domain = !skipSetDomain && (cookieConfig.domain || defaultCookieConfig.domain);
134                secure = cookieConfig.secure || defaultCookieConfig.secure;
135                samesite = cookieConfig.samesite || defaultCookieConfig.samesite;
136
137                document.cookie = name + "=" + encodeURIComponent(value) +
138                    (expires && days !== sessionCookieMarker ? '; expires=' + expires.toGMTString() : '') +
139                    (path ? '; path=' + path : '') +
140                    (domain ? '; domain=' + domain : '') +
141                    (secure ? '; secure' : '') +
142                    (samesite ? '; samesite=' + samesite : 'lax');
143            },
144            isWebsiteAllowedToSaveCookie() {
145                const allowedCookies = this.get('user_allowed_save_cookie');
146                if (allowedCookies) {
147                    const allowedWebsites = JSON.parse(unescape(allowedCookies));
148
149                    return allowedWebsites[CURRENT_WEBSITE_ID] === 1;
150                }
151                return false;
152            },
153            getGroupByCookieName(name) {
154                const cookieConsentConfig = window.cookie_consent_config || {};
155                let group = null;
156                for (let prop in cookieConsentConfig) {
157                    if (!cookieConsentConfig.hasOwnProperty(prop)) continue;
158                    if (cookieConsentConfig[prop].includes(name)) {
159                        group = prop;
160                        break;
161                    }
162                }
163                return group;
164            },
165            isCookieAllowed(name) {
166                const cookieGroup = this.getGroupByCookieName(name);
167                return cookieGroup
168                    ? window.cookie_consent_groups[cookieGroup]
169                    : this.isWebsiteAllowedToSaveCookie();
170            },
171            saveTempStorageCookies() {
172                for (const [name, data] of Object.entries(cookieTempStorage)) {
173                    if (this.isCookieAllowed(name)) {
174                        this.set(name, data['value'], data['days'], data['skipSetDomain']);
175                        delete cookieTempStorage[name];
176                    }
177                }
178            }
179        };
180
181        hyva.getCookie = (name) => {
182            const cookieConfig = window.COOKIE_CONFIG || {};
183
184            if (cookieConfig.cookie_restriction_enabled && ! internalCookie.isCookieAllowed(name)) {
185                return cookieTempStorage[name] ? cookieTempStorage[name]['value'] : null;
186            }
187
188            return internalCookie.get(name);
189        }
190
191        hyva.setCookie = (name, value, days, skipSetDomain) => {
192            const cookieConfig = window.COOKIE_CONFIG || {};
193
194            if (cookieConfig.cookie_restriction_enabled && ! internalCookie.isCookieAllowed(name)) {
195                cookieTempStorage[name] = {value, days, skipSetDomain};
196                return;
197            }
198            return internalCookie.set(name, value, days, skipSetDomain);
199        }
200
201
202        hyva.setSessionCookie = (name, value, skipSetDomain) => {
203            return hyva.setCookie(name, value, sessionCookieMarker, skipSetDomain)
204        }
205
206        hyva.getBrowserStorage = () => {
207            const browserStorage = window.localStorage || window.sessionStorage;
208            if (!browserStorage) {
209                console.warn('Browser Storage is unavailable');
210                return false;
211            }
212            try {
213                browserStorage.setItem('storage_test', '1');
214                browserStorage.removeItem('storage_test');
215            } catch (error) {
216                console.warn('Browser Storage is not accessible', error);
217                return false;
218            }
219            return browserStorage;
220        }
221
222        hyva.postForm = (postParams) => {
223            const form = document.createElement("form");
224
225            let data = postParams.data;
226
227            if (! postParams.skipUenc && ! data.uenc) {
228                data.uenc = btoa(window.location.href);
229            }
230            form.method = "POST";
231            form.action = postParams.action;
232
233            Object.keys(postParams.data).map(key => {
234                const field = document.createElement("input");
235                field.type = 'hidden'
236                field.value = postParams.data[key];
237                field.name = key;
238                form.appendChild(field);
239            });
240
241            const form_key = document.createElement("input");
242            form_key.type = 'hidden';
243            form_key.value = hyva.getFormKey();
244            form_key.name="form_key";
245            form.appendChild(form_key);
246
247            document.body.appendChild(form);
248
249            form.submit();
250        }
251
252        hyva.getFormKey = function () {
253            let formKey = hyva.getCookie('form_key');
254
255            if (!formKey) {
256                formKey = generateRandomString();
257                hyva.setCookie('form_key', formKey);
258            }
259
260            return formKey;
261        }
262
263        hyva.formatPrice = (value, showSign, options = {}) => {
264            const groupSeparator = options.groupSeparator;
265            const decimalSeparator = options.decimalSeparator
266            delete options.groupSeparator;
267            delete options.decimalSeparator;
268            const formatter = new Intl.NumberFormat(
269                'nl\u002DNL',
270                Object.assign({
271                    style: 'currency',
272                    currency: 'EUR',
273                    signDisplay: showSign ? 'always' : 'auto'
274                }, options)
275            );
276            return (typeof Intl.NumberFormat.prototype.formatToParts === 'function') ?
277                formatter.formatToParts(value).map(({type, value}) => {
278                    switch (type) {
279                        case 'currency':
280                            return '\u20AC' || value;
281                        case 'minusSign':
282                            return '- ';
283                        case 'plusSign':
284                            return '+ ';
285                        case 'group':
286                            return groupSeparator !== undefined ? groupSeparator : value;
287                        case 'decimal':
288                            return decimalSeparator !== undefined ? decimalSeparator : value;
289                        default :
290                            return value;
291                    }
292                }).reduce((string, part) => string + part) :
293                formatter.format(value);
294        }
295
296        const formatStr = function (str, nStart) {
297            const args = Array.from(arguments).slice(2);
298
299            return str.replace(/(%+)([0-9]+)/g, (m, p, n) => {
300                const idx = parseInt(n) - nStart;
301
302                if (args[idx] === null || args[idx] === void 0) {
303                    return m;
304                }
305                return p.length % 2
306                    ? p.slice(0, -1).replace('%%', '%') + args[idx]
307                    : p.replace('%%', '%') + n;
308            })
309        }
310
311        hyva.str = function (string) {
312            const args = Array.from(arguments);
313            args.splice(1, 0, 1);
314
315            return formatStr.apply(undefined, args);
316        }
317
318        hyva.strf = function () {
319            const args = Array.from(arguments);
320            args.splice(1, 0, 0);
321
322            return formatStr.apply(undefined, args);
323        }
324
325        /**
326         * Take a html string as `content` parameter and
327         * extract an element from the DOM to replace in
328         * the current page under the same selector,
329         * defined by `targetSelector`
330         */
331        hyva.replaceDomElement = (targetSelector, content) => {
332            // Parse the content and extract the DOM node using the `targetSelector`
333            const parser = new DOMParser();
334            const doc = parser.parseFromString(content, 'text/html');
335            const contentNode = doc.querySelector(targetSelector);
336
337            // Bail if content or target can't be found
338            if (!contentNode || !document.querySelector(targetSelector)) {
339                return;
340            }
341
342                        hyva.activateScripts(contentNode);
343            
344            // Replace the old DOM node with the new content
345            document.querySelector(targetSelector).replaceWith(contentNode);
346
347            // Reload customerSectionData and display cookie-messages if present
348            window.dispatchEvent(new CustomEvent("reload-customer-section-data"));
349            hyva.initMessages();
350        }
351
352        hyva.removeScripts = (contentNode) => {
353            const scripts = contentNode.getElementsByTagName('script');
354            for (let i = 0; i < scripts.length; i++) {
355                scripts[i].parentNode.removeChild(scripts[i]);
356            }
357            const templates = contentNode.getElementsByTagName('template');
358            for (let i = 0; i < templates.length; i++) {
359                const container = document.createElement('div');
360                container.innerHTML = templates[i].innerHTML;
361                hyva.removeScripts(container);
362                templates[i].innerHTML = container.innerHTML;
363            }
364        }
365
366        hyva.activateScripts = (contentNode) => {
367            // Create new array from HTMLCollection to avoid mutation of collection while manipulating the DOM.
368            const scripts = Array.from(contentNode.getElementsByTagName('script'));
369
370            // Iterate over all script tags to duplicate+inject each into the head
371            for (const original of scripts) {
372                const script = document.createElement('script');
373                original.type && (script.type = original.type);
374                script.innerHTML = original.innerHTML;
375
376                // Remove the original (non-executing) script from the contentNode
377                original.parentNode.removeChild(original)
378
379                // Add script to head
380                document.head.appendChild(script);
381            }
382
383            return contentNode;
384        }
385
386        const replace = {['+']: '-', ['/']: '_', ['=']: ','};
387        hyva.getUenc = () => btoa(window.location.href).replace(/[+/=]/g, match => replace[match]);
388
389        let currentTrap;
390
391        const focusableElements = (rootElement) => {
392            const selector = 'button, [href], input, select, textarea, details, [tabindex]:not([tabindex="-1"]';
393            return Array.from(rootElement.querySelectorAll(selector))
394                .filter(el => {
395                    return el.style.display !== 'none'
396                        && !el.disabled
397                        && el.tabIndex !== -1
398                        && (el.offsetWidth || el.offsetHeight || el.getClientRects().length)
399                })
400        }
401
402        const focusTrap = (e) => {
403            const isTabPressed = e.key === 'Tab' || e.keyCode === 9;
404            if (!isTabPressed) return;
405
406            const focusable = focusableElements(currentTrap)
407            const firstFocusableElement = focusable[0]
408            const lastFocusableElement = focusable[focusable.length - 1]
409
410            e.shiftKey
411                ? document.activeElement === firstFocusableElement && (lastFocusableElement.focus(), e.preventDefault())
412                : document.activeElement === lastFocusableElement && (firstFocusableElement.focus(), e.preventDefault())
413        };
414
415        hyva.releaseFocus = (rootElement) => {
416            if (currentTrap && (!rootElement || rootElement === currentTrap)) {
417                currentTrap.removeEventListener('keydown', focusTrap)
418                currentTrap = null
419            }
420        }
421        hyva.trapFocus = (rootElement) => {
422            if (!rootElement) return;
423            hyva.releaseFocus()
424            currentTrap = rootElement
425            rootElement.addEventListener('keydown', focusTrap)
426            const firstElement = focusableElements(rootElement)[0]
427            firstElement && firstElement.focus()
428        }
429
430        hyva.safeParseNumber = (rawValue) => {
431            const number = rawValue ? parseFloat(rawValue) : null;
432
433            return Array.isArray(number) || isNaN(number) ? rawValue : number;
434        }
435
436        const toCamelCase = s => s.split('_').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join('');
437        hyva.createBooleanObject = (name, value = false, additionalMethods = {}) => {
438
439            const camelCase = toCamelCase(name);
440            const key = '__hyva_bool_' + name
441
442            return new Proxy(Object.assign(
443                additionalMethods,
444                {
445                    [key]: !!value,
446                    [name]() {return !!this[key]},
447                    ['!' + name]() {return !this[key]}, // @deprecated This does not work with non-CSP Alpine
448                    ['not' + camelCase]() {return !this[key]},
449                    ['toggle' + camelCase]() {this[key] = !this[key]},
450                    [`set${camelCase}True`]() {this[key] = true},
451                    [`set${camelCase}False`]() {this[key] = false},
452                }
453            ), {
454                set(target, prop, value) {
455                    return prop === name
456                        ? (target[key] = !!value)
457                        : Reflect.set(...arguments);
458                }
459            })
460        }
461
462                hyva.alpineInitialized = (fn) => window.addEventListener('alpine:initialized', fn, {once: true})
463        window.addEventListener('alpine:init', () => Alpine.data('{}', () => ({})), {once: true});
464                window.addEventListener('user-allowed-save-cookie', () => internalCookie.saveTempStorageCookies())
465
466    }( window.hyva = window.hyva || {} ));
467    //# sourceURL=hyva.js
468</script>
vendor: 1 bytes, line 468
468
469<script>
470    if (HTMLScriptElement.supports && HTMLScriptElement.supports('speculationrules') && document.body) {
471        const specScript = document.createElement('script');
472        specScript.type = 'speculationrules';
473        specRules = {
474            'prerender': [{
475                'where': {
476                    'and': [
477                        { 'href_matches': '/*' },
478                        { "not": { "href_matches": "/(customer|checkout|wishlist)*" } },
479                        { 'not': { 'selector_matches': '.do-not-prerender'}},
480                    { 'not': { 'selector_matches': '[rel~=nofollow]' } }
481                    ]
482                },
483                'eagerness': 'moderate'
484            }]
485        };
486        specScript.textContent = JSON.stringify(specRules);
487        document.body.append(specScript);
488    }
489</script>
489
490</head>
491<body id="html-body" class="page-with-filter page-products categorypath-boeken category-boeken catalog-category-view page-layout-2columns-left">
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/version1790248052/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="7SrAQN4OFxHzhKHj" />
523<script nonce="ZTc3MHNqenIwcWZ2NnRtNHpuY2VjbnV2MDhwbWRtMDI=">
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="ZTc3MHNqenIwcWZ2NnRtNHpuY2VjbnV2MDhwbWRtMDI=">
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 underline">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="category-view mx-auto flex flex-col items-center"><div class="w-full border-2 border-solid border-gray-900 bg-none text-center uppercase mx-auto mb-4 p-4 lg:p-8 bg-cover">
1650    <h1>Boeken</h1>
1651</div>
1652</div><div class="row"><div><div class="row"><div class="columns"><div class="sidebar sidebar-main">
1653    <div
1654        role="region"
1655        aria-label="Product&#x20;filters"
1656        class="block border border-container bg-container-darker p-4 md:border-0 md:bg-transparent md:py-0 md:px-0 my-6"
1657        x-data="initLayeredNavigation()"
1658        x-init="checkIsMobileResolution()"
1659        @resize.window.debounce="checkIsMobileResolution()"
1660        @visibilitychange.window.debounce="checkIsMobileResolution()"
1661    >
1662        <h2>
1663            <div
1664                class="block-title h-10 flex items-center justify-between"
1665                :class="{ 'cursor-pointer' : isMobile }"
1666                :role="isMobile ? 'button' : 'presentation'"
1667                :aria-expanded="isMobile ? blockOpen : undefined"
1668                :aria-controls="isMobile ? 'filters-content' : undefined"
1669                :tabindex="isMobile ? 0 : undefined"
1670                @click="blockOpen = !blockOpen"
1671                @keydown.enter="blockOpen = !blockOpen"
1672                @keydown.space="blockOpen = !blockOpen"
1673            >
1674                <span class="text-primary text-md md:text-3xl font-medium uppercase">
1675                    Filteren                </span>
1676                <span class="py-1 px-1 bg-container-lighter rounded border border-container-darker hidden"
1677                    :class="{ 'block' : isMobile, 'hidden': !isMobile }">
1678                    <svg width="24" height="24" viewBox="0 0 24 24" fill="none"
1679                        xmlns="http://www.w3.org/2000/svg"
1680                        class="transition-transform transform duration-300 ease-in-out"
1681                        :class="blockOpen ? 'rotate-180' : ''">
1682                        <path d="M19 9L12 16L5 9" stroke="#4A5568" stroke-width="2" stroke-linecap="round"
1683                            stroke-linejoin="round"/>
1684                    </svg>
1685                </span>
1686            </div>
1687        </h2>
1688        <div
1689            id="filters-content"
1690            class="block-content filter-content pt-3"
1691            x-show="isMobile && blockOpen || !isMobile"
1692        >
1693                                                <a
1694                    href="#products-list"
1695                    class="sr-only focus:not-sr-only bg-blue-600 text-white px-12 py-4 text-center block rounded-sm"
1696            >
1697                Skip to product list            </a>
1698                                                                    <div
1699                        role="group"
1700                        aria-labelledby="filter-option-0-title"
1701                        class="filter-option card my-4"
1702                        x-data="{ open: true }"
1703                    >
1704                        <h3
1705                            id="filter-option-0-title"
1706                            class="filter-options-title"
1707                        >
1708                            <button
1709                                class="flex justify-between items-center cursor-pointer hover:text-secondary-darker border-container w-full"
1710                                :class="{ 'border-b pb-4': open }"
1711                                @click="open = !open"
1712                                aria-controls="filter-option-0-content"
1713                                :aria-expanded="open"
1714                            >
1715                                <span class="title text-md md:text-lg font-semibold">
1716                                    Categorie                                    <span class="sr-only"> filter</span>
1717                                </span>
1718                                <span class="py-1 px-1 rounded border border-container">
1719                                    <svg
1720                                        width="24"
1721                                        height="24"
1722                                        viewBox="0 0 24 24"
1723                                        fill="none"
1724                                        xmlns="http://www.w3.org/2000/svg"
1725                                        class="transition-transform transform duration-300 ease-in-out"
1726                                        :class="open ? 'rotate-180' : ''"
1727                                        aria-hidden="true"
1728                                    >
1729                                        <path
1730                                            d="M19 9L12 16L5 9"
1731                                            stroke="#4A5568"
1732                                            stroke-width="2"
1733                                            stroke-linecap="round"
1734                                            stroke-linejoin="round"
1735                                        />
1736                                    </svg>
1737                                </span>
1738                            </button>
1739                        </h3>
1740                        <div
1741                            id="filter-option-0-content"
1742                            class="filter-options-content pt-3"
1743                            x-show="open"
1744                        >
1745                            
1746<ol class="items" aria-label="Categorie&#x20;filter&#x20;options">
1747            <li class="item my-1">
1748                            <a
1749                    href="https://uitgeverijpluim.nl/boeken/fictie"
1750                    class="flex items-center"
1751                >
1752                                                            Fictie                                            (<span class="count">82</span>
1753                        <span class="sr-only">products available</span>)
1754
1755                                                        </a>
1756                    </li>
1757            <li class="item my-1">
1758                            <a
1759                    href="https://uitgeverijpluim.nl/boeken/poezie"
1760                    class="flex items-center"
1761                >
1762                                                            Poëzie                                            (<span class="count">22</span>
1763                        <span class="sr-only">products available</span>)
1764
1765                                                        </a>
1766                    </li>
1767            <li class="item my-1">
1768                            <a
1769                    href="https://uitgeverijpluim.nl/boeken/non-fictie"
1770                    class="flex items-center"
1771                >
1772                                                            Non-fictie                                            (<span class="count">149</span>
1773                        <span class="sr-only">products available</span>)
1774
1775                                                        </a>
1776                    </li>
1777            <li class="item my-1">
1778                            <a
1779                    href="https://uitgeverijpluim.nl/boeken/verwacht"
1780                    class="flex items-center"
1781                >
1782                                                            Verwacht                                            (<span class="count">11</span>
1783                        <span class="sr-only">products available</span>)
1784
1785                                                        </a>
1786                    </li>
1787    </ol>
1788                        </div>
1789                    </div>
1790                                                        </div>
1791    </div>
1792    
1792<script>
1793        function initLayeredNavigation() {
1794            return {
1795                isMobile: false,
1796                blockOpen: false,
1797                checkIsMobileResolution: function () {
1798                    this.isMobile = window.outerWidth < 768
1799                }
1800            }
1801        }
1802    </script>
1802
1803    </div><div class="column main">    <section class="py-2 lg:py-8">
1804    
1804<script>
1805    function initToolbar_6ab5759acae63() {
1806        return {
1807            options: {
1808                url: 'https://uitgeverijpluim.nl/boeken'
1809            },
1810            oneDirection: false,
1811            changeUrl: function (paramName, paramValue, defaultValue) {
1812                var decode = window.decodeURIComponent,
1813                    urlPaths = this.options.url.split('?'),
1814                    baseUrl = urlPaths[0],
1815                    urlParams = urlPaths[1] ? urlPaths[1].split('&') : [],
1816                    paramData = {},
1817                    parameters, i, form, params, key, input, formKey;
1818
1819                for (i = 0; i < urlParams.length; i++) {
1820                    parameters = urlParams[i].split('=');
1821                    paramData[decode(parameters[0])] = parameters[1] !== undefined ?
1822                        decode(parameters[1].replace(/\+/g, '%20')) :
1823                        '';
1824                }
1825                paramData[paramName] = paramValue;
1826
1827                if (this.options.post) {
1828                    form = document.createElement('form');
1829                    params = [this.options.mode, this.options.direction, this.options.order, this.options.limit];
1830
1831                    for (key in paramData) {
1832                        if (params.indexOf(key) !== -1) { //eslint-disable-line max-depth
1833                            input = document.createElement('input');
1834                            input.name = key;
1835                            input.value = paramData[key];
1836                            form.appendChild(input);
1837                            delete paramData[key];
1838                        }
1839                    }
1840                    formKey = document.createElement('input');
1841                    formKey.name = 'form_key';
1842                    formKey.value = this.options.formKey;
1843                    form.appendChild(formKey);
1844
1845                    paramData = $.param(paramData);
1846                    baseUrl += paramData.length ? '?' + paramData : '';
1847
1848                    form.action = baseUrl;
1849                    form.method = 'POST';
1850                    document.body.appendChild(form);
1851                    form.submit();
1852                } else {
1853                    if (paramValue === defaultValue) { //eslint-disable-line eqeqeq
1854                        delete paramData[paramName];
1855                    }
1856                    paramData = Object.keys(paramData).map(function(k) {
1857                        return encodeURIComponent(k) + '=' + encodeURIComponent(paramData[k])
1858                    }).join('&');
1859
1860                    location.href = baseUrl + (paramData.length ? '?' + paramData : '');
1861                }
1862            }
1863        }
1864    }
1865</script>
1865
1866    <div x-data="initToolbar_6ab5759acae63()"
1867         class="toolbar toolbar-products h-28 grid grid-cols-3 grid-flow-row gap-2 items-center">
1868                            <div class="toolbar-sorter sorter flex items-center order-1 justify-left col-span-1 invisible md:inline-block">
1869    <label class="sr-only sorter-label" for="sorter">Sorteer op</label>
1870    <select id="sorter" data-role="sorter" class="form-select text-sm leading-normal w-full rounded-none h-2md py-1 px-3 text-gray-700 border border-gray-200 focus:outline-none focus:shadow-none focus:border-gray-1400 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-gray-900"
1871            @change="changeUrl('product_list_order',event.currentTarget.options[event.currentTarget.selectedIndex].value,'position')">
1872                    <option value="position"
1873                                >
1874                Positie            </option>
1875                    <option value="name"
1876                                >
1877                Productnaam            </option>
1878                    <option value="price"
1879                                >
1880                prijs            </option>
1881                    <option value="publish_date"
1882                                    selected="selected"
1883                                >
1884                Verschijningsdatum            </option>
1885            </select>
1886                <a title="Van&#x20;laag&#x20;naar&#x20;hoog&#x20;sorteren"
1887           href="#"
1888           class="action sorter-action sort-desc text-gray-400 ml-2"
1889           data-role="direction-switcher"
1890           data-value="asc"
1891           @click.prevent="changeUrl('product_list_dir','asc','asc')"
1892           x-show="!oneDirection"
1893        >
1894            <span>
1895                <svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
1896                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4h13M3 8h9m-9 4h6m4 0l4-4m0 0l4 4m-4-4v12" />
1897                </svg>
1898            </span>
1899        </a>
1900    </div>
1901        
1902                
1903    <div class="pages order-2 col-span-2 justify-end flex">
1904                    <strong class="sr-only label pages-label" id="paging-label_154">Pagina</strong>
1905            <ul class="pagination flex ml-auto -mr-3" aria-labelledby="paging-label_154">
1906            
1907            
1908            
1909                                            <li>
1910                    <a class="inline-block py-3 px-5 uppercase hover:text-gray-800 border-b-2&#x20;text-gray-800 border-gray-800 hover:text-gray-1400 transition-colors duration-400" href="https://uitgeverijpluim.nl/boeken" title="Pagina 1">
1911                        1                    </a>
1912                </li>
1913                                            <li>
1914                    <a class="inline-block py-3 px-5 uppercase hover:text-gray-800 text-gray-700 border-gray-800 hover:text-gray-1400 transition-colors duration-400" href="https://uitgeverijpluim.nl/boeken?p=2" title="Pagina 2">
1915                        2                    </a>
1916                </li>
1917                                            <li>
1918                    <a class="inline-block py-3 px-5 uppercase hover:text-gray-800 text-gray-700 border-gray-800 hover:text-gray-1400 transition-colors duration-400" href="https://uitgeverijpluim.nl/boeken?p=3" title="Pagina 3">
1919                        3                    </a>
1920                </li>
1921                                            <li>
1922                    <a class="inline-block py-3 px-5 uppercase hover:text-gray-800 text-gray-700 border-gray-800 hover:text-gray-1400 transition-colors duration-400" href="https://uitgeverijpluim.nl/boeken?p=4" title="Pagina 4">
1923                        4                    </a>
1924                </li>
1925                                            <li>
1926                    <a class="inline-block py-3 px-5 uppercase hover:text-gray-800 text-gray-700 border-gray-800 hover:text-gray-1400 transition-colors duration-400" href="https://uitgeverijpluim.nl/boeken?p=5" title="Pagina 5">
1927                        5                    </a>
1928                </li>
1929            
1930            
1931            
1932                            <li>
1933                                        <a class="inline-block py-3 px-5 uppercase text-gray-700 border-b-2 border-transparent hover:text-gray-800 transition-colors duration-400" href="https://uitgeverijpluim.nl/boeken?p=2" title="Volgende">
1934                        <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
1935                            <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path>
1936                        </svg>
1937                    </a>
1938                </li>
1939                        </ul>
1940
1941            </div>
1942
1943    </div>
1944            <div class="products wrapper mode-grid products-grid">
1945        <div class="mx-auto md:grid md:grid-cols-3 gap-x-4 pt-4 pb-12 -mr-2">
1946                                        <div class="item product product-item w-full mt-8">
1947            <form method="post"
1948          action="https://uitgeverijpluim.nl/checkout/cart/add/uenc/aHR0cHM6Ly91aXRnZXZlcmlqcGx1aW0ubmwvYm9la2Vu/product/890/"
1949          class="product_addtocart_form card card-interactive mr-2 mb-2"
1950            >
1951        <input name="form_key" type="hidden" value="7SrAQN4OFxHzhKHj" />        <input type="hidden" name="product" value="890"/>
1952                                    <div class="relative ">
1953                                <div class="relative overflow-hidden group cursor-pointer z-30 aspect-[2/3] bg-white">
1954                        <a class="absolute w-full h-full z-30"
1955                       href="https://uitgeverijpluim.nl/boeken/de-dichter-en-de-duivel-t-shirt" aria-label="De dichter en de duivel - T-shirt"></a>
1956                    <img class="w-full h-full object-cover" width="480" height="768" src="https://images.uitgeverijpluim.nl/IJMBK7OxZCX_F02YzJNyteKnaJb1jJ4DBt4Ma_SOZdQ/w:0/h:768/rt:fit/g:sm/el:1/ex:1:ce:0:0/aHR0cHM6Ly91aXRnZXZlcmlqcGx1aW0ubmwvbWVkaWEvY2F0YWxvZy9wcm9kdWN0L2NhY2hlLzBlNTdhMjc1NmViNWNhNDRiZjY2YTUxZTM1OTU3MzRhL3QvLS90LXNoaXJ0X3Zvb3JrYW50X2RlX2RpY2h0ZXJfZW5fZGVfZHVpdmVsLmpwZw.jpg" alt="De dichter en de duivel - T-shirt" itemprop="image" loading="eager" fetchpriority="high">
1957                                            <div
1958                            class="hidden lg:flex absolute z-50 left-0 top-0 w-full h-full items-center justify-center transform translate-x-full transition-transform duration-400 group-hover:translate-x-0 pointer-events-none">
1959                            <a class="absolute w-full h-full z-40 opacity-0 bg-gray-400 bg-opacity-50 group-hover:opacity-100 pointer-events-none"
1960                               href="https://uitgeverijpluim.nl/boeken/de-dichter-en-de-duivel-t-shirt" aria-label="De dichter en de duivel - T-shirt"></a>
1961
1962                            <a href="https://uitgeverijpluim.nl/boeken/de-dichter-en-de-duivel-t-shirt" aria-label="De dichter en de duivel - T-shirt"
1963                               class="z-50 my-1 btn btn--hover block uppercase py-sm px-6 border-3 border-gray-1000 bg-white relative text-gray-900 hover:text-white pointer-events-auto">Bekijk product</a>
1964                        </div>
1965                                    </div>
1966                <div class="mt-6">
1967                    <div class="text-sm leading-normal uppercase mb-1px">
1968                        <a class="text-gray-900 hover:text-gray-1400 transition-colors duration-400"
1969                           href="https://uitgeverijpluim.nl/boeken/de-dichter-en-de-duivel-t-shirt" aria-label="De dichter en de duivel - T-shirt">
1970                            De dichter en de duivel - T-shirt                        </a>
1971                    </div>
1972                    <div class="flex justify-between items-center">
1973                        <p class="text-gray-1400 text-base leading-normal">
1974                            <ins class="no-underline">€ 25,00</ins>
1975                        </p>
1976                    </div>
1977                </div>
1978            </div>
1979                            </form>
1980</div>
1981                            <div class="item product product-item w-full mt-8">
1982            <form method="post"
1983          action="https://uitgeverijpluim.nl/checkout/cart/add/uenc/aHR0cHM6Ly91aXRnZXZlcmlqcGx1aW0ubmwvYm9la2Vu/product/917/"
1984          class="product_addtocart_form card card-interactive mr-2 mb-2"
1985            >
1986        <input name="form_key" type="hidden" value="7SrAQN4OFxHzhKHj" />        <input type="hidden" name="product" value="917"/>
1987                                    <div class="relative ">
1988                                <div class="relative overflow-hidden group cursor-pointer z-30 aspect-[2/3] bg-white">
1989                        <a class="absolute w-full h-full z-30"
1990                       href="https://uitgeverijpluim.nl/boeken/wij-komen-van-de-werkplaats" aria-label="Hannah van Binsbergen - Wij komen van de werkplaats"></a>
1991                    <img class="w-full h-full object-cover" width="480" height="768" src="https://images.uitgeverijpluim.nl/GD7Tph62C0UrEg6ZZ3br1I6NwZQHDNCrWaq1-o-44gE/w:0/h:768/rt:fit/g:sm/el:1/ex:1:ce:0:0/aHR0cHM6Ly91aXRnZXZlcmlqcGx1aW0ubmwvbWVkaWEvY2F0YWxvZy9wcm9kdWN0L2NhY2hlLzBlNTdhMjc1NmViNWNhNDRiZjY2YTUxZTM1OTU3MzRhL2IvaS9iaW5zYmVyZ2VuX2hhbm5haF92YW5fLV93aWpfa29tZW5fdmFuX2RlX3dlcmtwbGFhdHMuanBlZw.jpeg" alt="Wij komen van de werkplaats" itemprop="image" loading="eager" fetchpriority="high">
1992                                            <div
1993                            class="hidden lg:flex absolute z-50 left-0 top-0 w-full h-full items-center justify-center transform translate-x-full transition-transform duration-400 group-hover:translate-x-0 pointer-events-none">
1994                            <a class="absolute w-full h-full z-40 opacity-0 bg-gray-400 bg-opacity-50 group-hover:opacity-100 pointer-events-none"
1995                               href="https://uitgeverijpluim.nl/boeken/wij-komen-van-de-werkplaats" aria-label="Hannah van Binsbergen - Wij komen van de werkplaats"></a>
1996
1997                            <a href="https://uitgeverijpluim.nl/boeken/wij-komen-van-de-werkplaats" aria-label="Hannah van Binsbergen - Wij komen van de werkplaats"
1998                               class="z-50 my-1 btn btn--hover block uppercase py-sm px-6 border-3 border-gray-1000 bg-white relative text-gray-900 hover:text-white pointer-events-auto">Bekijk product</a>
1999                        </div>
2000                                    </div>
2001                <div class="mt-6">
2002                    <div class="text-sm leading-normal uppercase mb-1px">
2003                        <a class="text-gray-900 hover:text-gray-1400 transition-colors duration-400"
2004                           href="https://uitgeverijpluim.nl/boeken/wij-komen-van-de-werkplaats" aria-label="Hannah van Binsbergen - Wij komen van de werkplaats">
2005                            Hannah van Binsbergen - Wij komen van de werkplaats                        </a>
2006                    </div>
2007                    <div class="flex justify-between items-center">
2008                        <p class="text-gray-1400 text-base leading-normal">
2009                            <ins class="no-underline">€ 21,99</ins>
2010                        </p>
2011                    </div>
2012                </div>
2013            </div>
2014                            </form>
2015</div>
2016                            <div class="item product product-item w-full mt-8">
2017            <form method="post"
2018          action="https://uitgeverijpluim.nl/checkout/cart/add/uenc/aHR0cHM6Ly91aXRnZXZlcmlqcGx1aW0ubmwvYm9la2Vu/product/914/"
2019          class="product_addtocart_form card card-interactive mr-2 mb-2"
2020            >
2021        <input name="form_key" type="hidden" value="7SrAQN4OFxHzhKHj" />        <input type="hidden" name="product" value="914"/>
2022                                    <div class="relative ">
2023                                <div class="relative overflow-hidden group cursor-pointer z-30 aspect-[2/3] bg-white">
2024                        <a class="absolute w-full h-full z-30"
2025                       href="https://uitgeverijpluim.nl/boeken/zwijgen-is-zilver-kunst-en-activisme" aria-label="Quinsy Gario - Zwijgen is zilver. Kunst en activisme"></a>
2026                    <img class="w-full h-full object-cover" width="480" height="768" src="https://images.uitgeverijpluim.nl/hQasEFXOEOJJuX613HEcVx1hQJsYsLW2vpwtBGZ2vL4/w:0/h:768/rt:fit/g:sm/el:1/ex:1:ce:0:0/aHR0cHM6Ly91aXRnZXZlcmlqcGx1aW0ubmwvbWVkaWEvY2F0YWxvZy9wcm9kdWN0L2NhY2hlLzBlNTdhMjc1NmViNWNhNDRiZjY2YTUxZTM1OTU3MzRhL2cvYS9nYXJpb19xdWluc3lfLV96d2lqZ2VuX2lzX3ppbHZlci5qcGc.jpg" alt="Zwijgen is zilver. Kunst en activisme" itemprop="image" loading="eager" fetchpriority="high">
2027                                            <div
2028                            class="hidden lg:flex absolute z-50 left-0 top-0 w-full h-full items-center justify-center transform translate-x-full transition-transform duration-400 group-hover:translate-x-0 pointer-events-none">
2029                            <a class="absolute w-full h-full z-40 opacity-0 bg-gray-400 bg-opacity-50 group-hover:opacity-100 pointer-events-none"
2030                               href="https://uitgeverijpluim.nl/boeken/zwijgen-is-zilver-kunst-en-activisme" aria-label="Quinsy Gario - Zwijgen is zilver. Kunst en activisme"></a>
2031
2032                            <a href="https://uitgeverijpluim.nl/boeken/zwijgen-is-zilver-kunst-en-activisme" aria-label="Quinsy Gario - Zwijgen is zilver. Kunst en activisme"
2033                               class="z-50 my-1 btn btn--hover block uppercase py-sm px-6 border-3 border-gray-1000 bg-white relative text-gray-900 hover:text-white pointer-events-auto">Bekijk product</a>
2034                        </div>
2035                                    </div>
2036                <div class="mt-6">
2037                    <div class="text-sm leading-normal uppercase mb-1px">
2038                        <a class="text-gray-900 hover:text-gray-1400 transition-colors duration-400"
2039                           href="https://uitgeverijpluim.nl/boeken/zwijgen-is-zilver-kunst-en-activisme" aria-label="Quinsy Gario - Zwijgen is zilver. Kunst en activisme">
2040                            Quinsy Gario - Zwijgen is zilver. Kunst en activisme                        </a>
2041                    </div>
2042                    <div class="flex justify-between items-center">
2043                        <p class="text-gray-1400 text-base leading-normal">
2044                            <ins class="no-underline">€ 21,99</ins>
2045                        </p>
2046                    </div>
2047                </div>
2048            </div>
2049                            </form>
2050</div>
2051                            <div class="item product product-item w-full mt-8">
2052            <form method="post"
2053          action="https://uitgeverijpluim.nl/checkout/cart/add/uenc/aHR0cHM6Ly91aXRnZXZlcmlqcGx1aW0ubmwvYm9la2Vu/product/920/"
2054          class="product_addtocart_form card card-interactive mr-2 mb-2"
2055            >
2056        <input name="form_key" type="hidden" value="7SrAQN4OFxHzhKHj" />        <input type="hidden" name="product" value="920"/>
2057                                    <div class="relative ">
2058                                <div class="relative overflow-hidden group cursor-pointer z-30 aspect-[2/3] bg-white">
2059                        <a class="absolute w-full h-full z-30"
2060                       href="https://uitgeverijpluim.nl/boeken/joods-zijn-na-de-vernietiging-van-gaza" aria-label="Peter Beinart - Joods zijn na de vernietiging van Gaza"></a>
2061                    <img class="w-full h-full object-cover" width="480" height="768" src="https://images.uitgeverijpluim.nl/uZCiCBTbXwsx5KVWgQr1E_BLApbexyC3iBeI2ymNNJE/w:0/h:768/rt:fit/g:sm/el:1/ex:1:ce:0:0/aHR0cHM6Ly91aXRnZXZlcmlqcGx1aW0ubmwvbWVkaWEvY2F0YWxvZy9wcm9kdWN0L2NhY2hlLzBlNTdhMjc1NmViNWNhNDRiZjY2YTUxZTM1OTU3MzRhL2IvZS9iZWluYXJ0X3BldGVyXy1fam9vZHNfemlqbl9uYV9kZV92ZXJuaWV0aWdpbmdfdmFuX2dhemEuanBn.jpg" alt="Joods zijn na de vernietiging van Gaza" itemprop="image" loading="lazy" decoding="async" fetchpriority="low">
2062                                            <div
2063                            class="hidden lg:flex absolute z-50 left-0 top-0 w-full h-full items-center justify-center transform translate-x-full transition-transform duration-400 group-hover:translate-x-0 pointer-events-none">
2064                            <a class="absolute w-full h-full z-40 opacity-0 bg-gray-400 bg-opacity-50 group-hover:opacity-100 pointer-events-none"
2065                               href="https://uitgeverijpluim.nl/boeken/joods-zijn-na-de-vernietiging-van-gaza" aria-label="Peter Beinart - Joods zijn na de vernietiging van Gaza"></a>
2066
2067                            <a href="https://uitgeverijpluim.nl/boeken/joods-zijn-na-de-vernietiging-van-gaza" aria-label="Peter Beinart - Joods zijn na de vernietiging van Gaza"
2068                               class="z-50 my-1 btn btn--hover block uppercase py-sm px-6 border-3 border-gray-1000 bg-white relative text-gray-900 hover:text-white pointer-events-auto">Bekijk product</a>
2069                        </div>
2070                                    </div>
2071                <div class="mt-6">
2072                    <div class="text-sm leading-normal uppercase mb-1px">
2073                        <a class="text-gray-900 hover:text-gray-1400 transition-colors duration-400"
2074                           href="https://uitgeverijpluim.nl/boeken/joods-zijn-na-de-vernietiging-van-gaza" aria-label="Peter Beinart - Joods zijn na de vernietiging van Gaza">
2075                            Peter Beinart - Joods zijn na de vernietiging van Gaza                        </a>
2076                    </div>
2077                    <div class="flex justify-between items-center">
2078                        <p class="text-gray-1400 text-base leading-normal">
2079                            <ins class="no-underline">€ 22,99</ins>
2080                        </p>
2081                    </div>
2082                </div>
2083            </div>
2084                            </form>
2085</div>
2086                            <div class="item product product-item w-full mt-8">
2087            <form method="post"
2088          action="https://uitgeverijpluim.nl/checkout/cart/add/uenc/aHR0cHM6Ly91aXRnZXZlcmlqcGx1aW0ubmwvYm9la2Vu/product/907/"
2089          class="product_addtocart_form card card-interactive mr-2 mb-2"
2090            >
2091        <input name="form_key" type="hidden" value="7SrAQN4OFxHzhKHj" />        <input type="hidden" name="product" value="907"/>
2092                                    <div class="relative ">
2093                                <div class="relative overflow-hidden group cursor-pointer z-30 aspect-[2/3] bg-white">
2094                        <a class="absolute w-full h-full z-30"
2095                       href="https://uitgeverijpluim.nl/boeken/ik-ben-oud-dat-komt-door-mijn-leeftijd" aria-label="Yvonne Kroonenberg - Ik ben oud, dat komt door mijn leeftijd"></a>
2096                    <img class="w-full h-full object-cover" width="480" height="768" src="https://images.uitgeverijpluim.nl/hbc_jHuDZ99SEC7Be7EztI0-qq5quK-W51OIdXWcobQ/w:0/h:768/rt:fit/g:sm/el:1/ex:1:ce:0:0/aHR0cHM6Ly91aXRnZXZlcmlqcGx1aW0ubmwvbWVkaWEvY2F0YWxvZy9wcm9kdWN0L2NhY2hlLzBlNTdhMjc1NmViNWNhNDRiZjY2YTUxZTM1OTU3MzRhL2svci9rcm9vbmVuYmVyZ195dm9ubmVfLV9pa19iZW5fb3VkX2RhdF9rb210X2Rvb3JfbWlqbl9sZWVmdGlqZC5qcGc.jpg" alt="Ik ben oud, dat komt door mijn leeftijd" itemprop="image" loading="lazy" decoding="async" fetchpriority="low">
2097                                            <div
2098                            class="hidden lg:flex absolute z-50 left-0 top-0 w-full h-full items-center justify-center transform translate-x-full transition-transform duration-400 group-hover:translate-x-0 pointer-events-none">
2099                            <a class="absolute w-full h-full z-40 opacity-0 bg-gray-400 bg-opacity-50 group-hover:opacity-100 pointer-events-none"
2100                               href="https://uitgeverijpluim.nl/boeken/ik-ben-oud-dat-komt-door-mijn-leeftijd" aria-label="Yvonne Kroonenberg - Ik ben oud, dat komt door mijn leeftijd"></a>
2101
2102                            <a href="https://uitgeverijpluim.nl/boeken/ik-ben-oud-dat-komt-door-mijn-leeftijd" aria-label="Yvonne Kroonenberg - Ik ben oud, dat komt door mijn leeftijd"
2103                               class="z-50 my-1 btn btn--hover block uppercase py-sm px-6 border-3 border-gray-1000 bg-white relative text-gray-900 hover:text-white pointer-events-auto">Bekijk product</a>
2104                        </div>
2105                                    </div>
2106                <div class="mt-6">
2107                    <div class="text-sm leading-normal uppercase mb-1px">
2108                        <a class="text-gray-900 hover:text-gray-1400 transition-colors duration-400"
2109                           href="https://uitgeverijpluim.nl/boeken/ik-ben-oud-dat-komt-door-mijn-leeftijd" aria-label="Yvonne Kroonenberg - Ik ben oud, dat komt door mijn leeftijd">
2110                            Yvonne Kroonenberg - Ik ben oud, dat komt door mijn leeftijd                        </a>
2111                    </div>
2112                    <div class="flex justify-between items-center">
2113                        <p class="text-gray-1400 text-base leading-normal">
2114                            <ins class="no-underline">€ 22,99</ins>
2115                        </p>
2116                    </div>
2117                </div>
2118            </div>
2119                            </form>
2120</div>
2121                            <div class="item product product-item w-full mt-8">
2122            <form method="post"
2123          action="https://uitgeverijpluim.nl/checkout/cart/add/uenc/aHR0cHM6Ly91aXRnZXZlcmlqcGx1aW0ubmwvYm9la2Vu/product/910/"
2124          class="product_addtocart_form card card-interactive mr-2 mb-2"
2125            >
2126        <input name="form_key" type="hidden" value="7SrAQN4OFxHzhKHj" />        <input type="hidden" name="product" value="910"/>
2127                                    <div class="relative ">
2128                                <div class="relative overflow-hidden group cursor-pointer z-30 aspect-[2/3] bg-white">
2129                        <a class="absolute w-full h-full z-30"
2130                       href="https://uitgeverijpluim.nl/boeken/claus-het-verscheurde-leven-van-een-prins" aria-label="Jutta Chorus en Bas Blokker - Claus. Het verscheurde leven van een prins"></a>
2131                    <img class="w-full h-full object-cover" width="480" height="768" src="https://images.uitgeverijpluim.nl/XfcNMrIfRdjvRfgZFePpox-BLly4T_bQCrLCTWQZBuc/w:0/h:768/rt:fit/g:sm/el:1/ex:1:ce:0:0/aHR0cHM6Ly91aXRnZXZlcmlqcGx1aW0ubmwvbWVkaWEvY2F0YWxvZy9wcm9kdWN0L2NhY2hlLzBlNTdhMjc1NmViNWNhNDRiZjY2YTUxZTM1OTU3MzRhL2MvaC9jaG9ydXNfZW5fYmxva2tlcl8tX2NsYXVzLnBuZw.png" alt="Claus. Het verscheurde leven van een prins" itemprop="image" loading="lazy" decoding="async" fetchpriority="low">
2132                                            <div
2133                            class="hidden lg:flex absolute z-50 left-0 top-0 w-full h-full items-center justify-center transform translate-x-full transition-transform duration-400 group-hover:translate-x-0 pointer-events-none">
2134                            <a class="absolute w-full h-full z-40 opacity-0 bg-gray-400 bg-opacity-50 group-hover:opacity-100 pointer-events-none"
2135                               href="https://uitgeverijpluim.nl/boeken/claus-het-verscheurde-leven-van-een-prins" aria-label="Jutta Chorus en Bas Blokker - Claus. Het verscheurde leven van een prins"></a>
2136
2137                            <a href="https://uitgeverijpluim.nl/boeken/claus-het-verscheurde-leven-van-een-prins" aria-label="Jutta Chorus en Bas Blokker - Claus. Het verscheurde leven van een prins"
2138                               class="z-50 my-1 btn btn--hover block uppercase py-sm px-6 border-3 border-gray-1000 bg-white relative text-gray-900 hover:text-white pointer-events-auto">Bekijk product</a>
2139                        </div>
2140                                    </div>
2141                <div class="mt-6">
2142                    <div class="text-sm leading-normal uppercase mb-1px">
2143                        <a class="text-gray-900 hover:text-gray-1400 transition-colors duration-400"
2144                           href="https://uitgeverijpluim.nl/boeken/claus-het-verscheurde-leven-van-een-prins" aria-label="Jutta Chorus en Bas Blokker - Claus. Het verscheurde leven van een prins">
2145                            Jutta Chorus en Bas Blokker - Claus. Het verscheurde leven van een prins                        </a>
2146                    </div>
2147                    <div class="flex justify-between items-center">
2148                        <p class="text-gray-1400 text-base leading-normal">
2149                            <ins class="no-underline">
2149€ 36,99</ins>
2150                        </p>
2151                    </div>
2152                </div>
2153            </div>
2154                            </form>
2155</div>
2156                            <div class="item product product-item w-full mt-8">
2157            <form method="post"
2158          action="https://uitgeverijpluim.nl/checkout/cart/add/uenc/aHR0cHM6Ly91aXRnZXZlcmlqcGx1aW0ubmwvYm9la2Vu/product/904/"
2159          class="product_addtocart_form card card-interactive mr-2 mb-2"
2160            >
2161        <input name="form_key" type="hidden" value="7SrAQN4OFxHzhKHj" />        <input type="hidden" name="product" value="904"/>
2162                                    <div class="relative ">
2163                                <div class="relative overflow-hidden group cursor-pointer z-30 aspect-[2/3] bg-white">
2164                        <a class="absolute w-full h-full z-30"
2165                       href="https://uitgeverijpluim.nl/boeken/auschwitz-is-dood" aria-label="David Wertheim - Auschwitz is dood"></a>
2166                    <img class="w-full h-full object-cover" width="480" height="768" src="https://images.uitgeverijpluim.nl/sWPyJdNm5_aeeZU8i_sx3jRJrB41lTSo79BjiHRVRnw/w:0/h:768/rt:fit/g:sm/el:1/ex:1:ce:0:0/aHR0cHM6Ly91aXRnZXZlcmlqcGx1aW0ubmwvbWVkaWEvY2F0YWxvZy9wcm9kdWN0L2NhY2hlLzBlNTdhMjc1NmViNWNhNDRiZjY2YTUxZTM1OTU3MzRhL3cvZS93ZXJ0aGVpbV9kYXZpZF8tX2F1c2Nod2l0el9pc19kb29kXzEuanBn.jpg" alt="Auschwitz is dood" itemprop="image" loading="lazy" decoding="async" fetchpriority="low">
2167                                            <div
2168                            class="hidden lg:flex absolute z-50 left-0 top-0 w-full h-full items-center justify-center transform translate-x-full transition-transform duration-400 group-hover:translate-x-0 pointer-events-none">
2169                            <a class="absolute w-full h-full z-40 opacity-0 bg-gray-400 bg-opacity-50 group-hover:opacity-100 pointer-events-none"
2170                               href="https://uitgeverijpluim.nl/boeken/auschwitz-is-dood" aria-label="David Wertheim - Auschwitz is dood"></a>
2171
2172                            <a href="https://uitgeverijpluim.nl/boeken/auschwitz-is-dood" aria-label="David Wertheim - Auschwitz is dood"
2173                               class="z-50 my-1 btn btn--hover block uppercase py-sm px-6 border-3 border-gray-1000 bg-white relative text-gray-900 hover:text-white pointer-events-auto">Bekijk product</a>
2174                        </div>
2175                                    </div>
2176                <div class="mt-6">
2177                    <div class="text-sm leading-normal uppercase mb-1px">
2178                        <a class="text-gray-900 hover:text-gray-1400 transition-colors duration-400"
2179                           href="https://uitgeverijpluim.nl/boeken/auschwitz-is-dood" aria-label="David Wertheim - Auschwitz is dood">
2180                            David Wertheim - Auschwitz is dood                        </a>
2181                    </div>
2182                    <div class="flex justify-between items-center">
2183                        <p class="text-gray-1400 text-base leading-normal">
2184                            <ins class="no-underline">€ 21,99</ins>
2185                        </p>
2186                    </div>
2187                </div>
2188            </div>
2189                            </form>
2190</div>
2191                            <div class="item product product-item w-full mt-8">
2192            <form method="post"
2193          action="https://uitgeverijpluim.nl/checkout/cart/add/uenc/aHR0cHM6Ly91aXRnZXZlcmlqcGx1aW0ubmwvYm9la2Vu/product/860/"
2194          class="product_addtocart_form card card-interactive mr-2 mb-2"
2195            >
2196        <input name="form_key" type="hidden" value="7SrAQN4OFxHzhKHj" />        <input type="hidden" name="product" value="860"/>
2197                                    <div class="relative ">
2198                                <div class="relative overflow-hidden group cursor-pointer z-30 aspect-[2/3] bg-white">
2199                        <a class="absolute w-full h-full z-30"
2200                       href="https://uitgeverijpluim.nl/boeken/samen-een-manifest-tegen-een-harteloze-samenleving" aria-label="Ece Temelkuran - Samen. Een manifest tegen een harteloze samenleving"></a>
2201                    <img class="w-full h-full object-cover" width="480" height="768" src="https://images.uitgeverijpluim.nl/j9k-GraBG8-y0uPHqQ7IeaNkAmkQ7OPgns1bz1Jhodk/w:0/h:768/rt:fit/g:sm/el:1/ex:1:ce:0:0/aHR0cHM6Ly91aXRnZXZlcmlqcGx1aW0ubmwvbWVkaWEvY2F0YWxvZy9wcm9kdWN0L2NhY2hlLzBlNTdhMjc1NmViNWNhNDRiZjY2YTUxZTM1OTU3MzRhL3QvZS90ZW1lbGt1cmFuX2VjZV8tX3NhbWVuLmpwZw.jpg" alt="Samen. Een manifest tegen een harteloze samenleving" itemprop="image" loading="lazy" decoding="async" fetchpriority="low">
2202                                            <div
2203                            class="hidden lg:flex absolute z-50 left-0 top-0 w-full h-full items-center justify-center transform translate-x-full transition-transform duration-400 group-hover:translate-x-0 pointer-events-none">
2204                            <a class="absolute w-full h-full z-40 opacity-0 bg-gray-400 bg-opacity-50 group-hover:opacity-100 pointer-events-none"
2205                               href="https://uitgeverijpluim.nl/boeken/samen-een-manifest-tegen-een-harteloze-samenleving" aria-label="Ece Temelkuran - Samen. Een manifest tegen een harteloze samenleving"></a>
2206
2207                            <a href="https://uitgeverijpluim.nl/boeken/samen-een-manifest-tegen-een-harteloze-samenleving" aria-label="Ece Temelkuran - Samen. Een manifest tegen een harteloze samenleving"
2208                               class="z-50 my-1 btn btn--hover block uppercase py-sm px-6 border-3 border-gray-1000 bg-white relative text-gray-900 hover:text-white pointer-events-auto">Bekijk product</a>
2209                        </div>
2210                                    </div>
2211                <div class="mt-6">
2212                    <div class="text-sm leading-normal uppercase mb-1px">
2213                        <a class="text-gray-900 hover:text-gray-1400 transition-colors duration-400"
2214                           href="https://uitgeverijpluim.nl/boeken/samen-een-manifest-tegen-een-harteloze-samenleving" aria-label="Ece Temelkuran - Samen. Een manifest tegen een harteloze samenleving">
2215                            Ece Temelkuran - Samen. Een manifest tegen een harteloze samenleving                        </a>
2216                    </div>
2217                    <div class="flex justify-between items-center">
2218                        <p class="text-gray-1400 text-base leading-normal">
2219                            <ins class="no-underline">€ 22,99</ins>
2220                        </p>
2221                    </div>
2222                </div>
2223            </div>
2224                            </form>
2225</div>
2226                            <div class="item product product-item w-full mt-8">
2227            <form method="post"
2228          action="https://uitgeverijpluim.nl/checkout/cart/add/uenc/aHR0cHM6Ly91aXRnZXZlcmlqcGx1aW0ubmwvYm9la2Vu/product/877/"
2229          class="product_addtocart_form card card-interactive mr-2 mb-2"
2230            >
2231        <input name="form_key" type="hidden" value="7SrAQN4OFxHzhKHj" />        <input type="hidden" name="product" value="877"/>
2232                                    <div class="relative ">
2233                                <div class="relative overflow-hidden group cursor-pointer z-30 aspect-[2/3] bg-white">
2234                        <a class="absolute w-full h-full z-30"
2235                       href="https://uitgeverijpluim.nl/boeken/brooklyn-de-memoires-van-abel-sikkink" aria-label="Bert Wagendorp - Brooklyn. De memoires van Abel Sikkink"></a>
2236                    <img class="w-full h-full object-cover" width="480" height="768" src="https://images.uitgeverijpluim.nl/2cqRBsAHUN8IEq7fj1PpWOLdak7C6kpkvRVM3ncRnfw/w:0/h:768/rt:fit/g:sm/el:1/ex:1:ce:0:0/aHR0cHM6Ly91aXRnZXZlcmlqcGx1aW0ubmwvbWVkaWEvY2F0YWxvZy9wcm9kdWN0L2NhY2hlLzBlNTdhMjc1NmViNWNhNDRiZjY2YTUxZTM1OTU3MzRhL3cvYS93YWdlbmRvcnBfYmVydF8tX2Jyb29rbHluLmpwZw.jpg" alt="Brooklyn. De memoires van Abel Sikkink" itemprop="image" loading="lazy" decoding="async" fetchpriority="low">
2237                                            <div
2238                            class="hidden lg:flex absolute z-50 left-0 top-0 w-full h-full items-center justify-center transform translate-x-full transition-transform duration-400 group-hover:translate-x-0 pointer-events-none">
2239                            <a class="absolute w-full h-full z-40 opacity-0 bg-gray-400 bg-opacity-50 group-hover:opacity-100 pointer-events-none"
2240                               href="https://uitgeverijpluim.nl/boeken/brooklyn-de-memoires-van-abel-sikkink" aria-label="Bert Wagendorp - Brooklyn. De memoires van Abel Sikkink"></a>
2241
2242                            <a href="https://uitgeverijpluim.nl/boeken/brooklyn-de-memoires-van-abel-sikkink" aria-label="Bert Wagendorp - Brooklyn. De memoires van Abel Sikkink"
2243                               class="z-50 my-1 btn btn--hover block uppercase py-sm px-6 border-3 border-gray-1000 bg-white relative text-gray-900 hover:text-white pointer-events-auto">Bekijk product</a>
2244                        </div>
2245                                    </div>
2246                <div class="mt-6">
2247                    <div class="text-sm leading-normal uppercase mb-1px">
2248                        <a class="text-gray-900 hover:text-gray-1400 transition-colors duration-400"
2249                           href="https://uitgeverijpluim.nl/boeken/brooklyn-de-memoires-van-abel-sikkink" aria-label="Bert Wagendorp - Brooklyn. De memoires van Abel Sikkink">
2250                            Bert Wagendorp - Brooklyn. De memoires van Abel Sikkink                        </a>
2251                    </div>
2252                    <div class="flex justify-between items-center">
2253                        <p class="text-gray-1400 text-base leading-normal">
2254                            <ins class="no-underline">€ 26,99</ins>
2255                        </p>
2256                    </div>
2257                </div>
2258            </div>
2259                            </form>
2260</div>
2261                            <div class="item product product-item w-full mt-8">
2262            <form method="post"
2263          action="https://uitgeverijpluim.nl/checkout/cart/add/uenc/aHR0cHM6Ly91aXRnZXZlcmlqcGx1aW0ubmwvYm9la2Vu/product/886/"
2264          class="product_addtocart_form card card-interactive mr-2 mb-2"
2265            >
2266        <input name="form_key" type="hidden" value="7SrAQN4OFxHzhKHj" />        <input type="hidden" name="product" value="886"/>
2267                                    <div class="relative ">
2268                                <div class="relative overflow-hidden group cursor-pointer z-30 aspect-[2/3] bg-white">
2269                        <a class="absolute w-full h-full z-30"
2270                       href="https://uitgeverijpluim.nl/boeken/control-en-delete-herinneringen-aan-autoritair-china" aria-label="Garrie van Pinxteren - Control en delete. Herinneringen aan autoritair China"></a>
2271                    <img class="w-full h-full object-cover" width="480" height="768" src="https://images.uitgeverijpluim.nl/bxFNbWrIprExsNBmrB4uku5B_7HzxoTpor4Lnwq1CTo/w:0/h:768/rt:fit/g:sm/el:1/ex:1:ce:0:0/aHR0cHM6Ly91aXRnZXZlcmlqcGx1aW0ubmwvbWVkaWEvY2F0YWxvZy9wcm9kdWN0L2NhY2hlLzBlNTdhMjc1NmViNWNhNDRiZjY2YTUxZTM1OTU3MzRhL3AvaS9waW54dGVyZW5fZ2FycmllX3Zhbl8tX2NvbnRyb2xfZW5fZGVsZXRlLnBuZw.png" alt="Control en delete. Herinneringen aan autoritair China" itemprop="image" loading="lazy" decoding="async" fetchpriority="low">
2272                                            <div
2273                            class="hidden lg:flex absolute z-50 left-0 top-0 w-full h-full items-center justify-center transform translate-x-full transition-transform duration-400 group-hover:translate-x-0 pointer-events-none">
2274                            <a class="absolute w-full h-full z-40 opacity-0 bg-gray-400 bg-opacity-50 group-hover:opacity-100 pointer-events-none"
2275                               href="https://uitgeverijpluim.nl/boeken/control-en-delete-herinneringen-aan-autoritair-china" aria-label="Garrie van Pinxteren - Control en delete. Herinneringen aan autoritair China"></a>
2276
2277                            <a href="https://uitgeverijpluim.nl/boeken/control-en-delete-herinneringen-aan-autoritair-china" aria-label="Garrie van Pinxteren - Control en delete. Herinneringen aan autoritair China"
2278                               class="z-50 my-1 btn btn--hover block uppercase py-sm px-6 border-3 border-gray-1000 bg-white relative text-gray-900 hover:text-white pointer-events-auto">Bekijk product</a>
2279                        </div>
2280                                    </div>
2281                <div class="mt-6">
2282                    <div class="text-sm leading-normal uppercase mb-1px">
2283                        <a class="text-gray-900 hover:text-gray-1400 transition-colors duration-400"
2284                           href="https://uitgeverijpluim.nl/boeken/control-en-delete-herinneringen-aan-autoritair-china" aria-label="Garrie van Pinxteren - Control en delete. Herinneringen aan autoritair China">
2285                            Garrie van Pinxteren - Control en delete. Herinneringen aan autoritair China                        </a>
2286                    </div>
2287                    <div class="flex justify-between items-center">
2288                        <p class="text-gray-1400 text-base leading-normal">
2289                            <ins class="no-underline">€ 24,99</ins>
2290                        </p>
2291                    </div>
2292                </div>
2293            </div>
2294                            </form>
2295</div>
2296                            <div class="item product product-item w-full mt-8">
2297            <form method="post"
2298          action="https://uitgeverijpluim.nl/checkout/cart/add/uenc/aHR0cHM6Ly91aXRnZXZlcmlqcGx1aW0ubmwvYm9la2Vu/product/901/"
2299          class="product_addtocart_form card card-interactive mr-2 mb-2"
2300            >
2301        <input name="form_key" type="hidden" value="7SrAQN4OFxHzhKHj" />        <input type="hidden" name="product" value="901"/>
2302                                    <div class="relative ">
2303                                <div class="relative overflow-hidden group cursor-pointer z-30 aspect-[2/3] bg-white">
2304                        <a class="absolute w-full h-full z-30"
2305                       href="https://uitgeverijpluim.nl/boeken/thomas-in-het-verpleeghuis" aria-label="Thomas van der Meer - Thomas in het verpleeghuis"></a>
2306                    <img class="w-full h-full object-cover" width="480" height="768" src="https://images.uitgeverijpluim.nl/fSJQJ9XQtG7QAAAxow2XruMA6Yy59zCP9y62SH77i30/w:0/h:768/rt:fit/g:sm/el:1/ex:1:ce:0:0/aHR0cHM6Ly91aXRnZXZlcmlqcGx1aW0ubmwvbWVkaWEvY2F0YWxvZy9wcm9kdWN0L2NhY2hlLzBlNTdhMjc1NmViNWNhNDRiZjY2YTUxZTM1OTU3MzRhL28vbS9vbXNsYWdfdGhvbWFzX2luX2hldF92ZXJwbGVlZ2h1aXMyLmpwZw.jpg" alt="Thomas in het verpleeghuis" itemprop="image" loading="lazy" decoding="async" fetchpriority="low">
2307                                            <div
2308                            class="hidden lg:flex absolute z-50 left-0 top-0 w-full h-full items-center justify-center transform translate-x-full transition-transform duration-400 group-hover:translate-x-0 pointer-events-none">
2309                            <a class="absolute w-full h-full z-40 opacity-0 bg-gray-400 bg-opacity-50 group-hover:opacity-100 pointer-events-none"
2310                               href="https://uitgeverijpluim.nl/boeken/thomas-in-het-verpleeghuis" aria-label="Thomas van der Meer - Thomas in het verpleeghuis"></a>
2311
2312                            <a href="https://uitgeverijpluim.nl/boeken/thomas-in-het-verpleeghuis" aria-label="Thomas van der Meer - Thomas in het verpleeghuis"
2313                               class="z-50 my-1 btn btn--hover block uppercase py-sm px-6 border-3 border-gray-1000 bg-white relative text-gray-900 hover:text-white pointer-events-auto">Bekijk product</a>
2314                        </div>
2315                                    </div>
2316                <div class="mt-6">
2317                    <div class="text-sm leading-normal uppercase mb-1px">
2318                        <a class="text-gray-900 hover:text-gray-1400 transition-colors duration-400"
2319                           href="https://uitgeverijpluim.nl/boeken/thomas-in-het-verpleeghuis" aria-label="Thomas van der Meer - Thomas in het verpleeghuis">
2320                            Thomas van der Meer - Thomas in het verpleeghuis                        </a>
2321                    </div>
2322                    <div class="flex justify-between items-center">
2323                        <p class="text-gray-1400 text-base leading-normal">
2324                            <ins class="no-underline">€ 22,99</ins>
2325                        </p>
2326                    </div>
2327                </div>
2328            </div>
2329                            </form>
2330</div>
2331                            <div class="item product product-item w-full mt-8">
2332            <form method="post"
2333          action="https://uitgeverijpluim.nl/checkout/cart/add/uenc/aHR0cHM6Ly91aXRnZXZlcmlqcGx1aW0ubmwvYm9la2Vu/product/883/"
2334          class="product_addtocart_form card card-interactive mr-2 mb-2"
2335            >
2336        <input name="form_key" type="hidden" value="7SrAQN4OFxHzhKHj" />        <input type="hidden" name="product" value="883"/>
2337                                    <div class="relative ">
2338                                <div class="relative overflow-hidden group cursor-pointer z-30 aspect-[2/3] bg-white">
2339                        <a class="absolute w-full h-full z-30"
2340                       href="https://uitgeverijpluim.nl/boeken/middle-management-science" aria-label="Nachoem M. Wijnberg - Middle management science."></a>
2341                    <img class="w-full h-full object-cover" width="480" height="768" src="https://images.uitgeverijpluim.nl/dcjgBXAU8FtDyhdUbO9GKKRK9wPFlx83KaINQOrwMFA/w:0/h:768/rt:fit/g:sm/el:1/ex:1:ce:0:0/aHR0cHM6Ly91aXRnZXZlcmlqcGx1aW0ubmwvbWVkaWEvY2F0YWxvZy9wcm9kdWN0L2NhY2hlLzBlNTdhMjc1NmViNWNhNDRiZjY2YTUxZTM1OTU3MzRhL3cvaS93aWpuYmVyZ19uYWNob2VtXy1fbWlkZGxlX21hbmFnZW1lbnRfc2NpZW5jZS5qcGc.jpg" alt="Middle management science." itemprop="image" loading="lazy" decoding="async" fetchpriority="low">
2342                                            <div
2343                            class="hidden lg:flex absolute z-50 left-0 top-0 w-full h-full items-center justify-center transform translate-x-full transition-transform duration-400 group-hover:translate-x-0 pointer-events-none">
2344                            <a class="absolute w-full h-full z-40 opacity-0 bg-gray-400 bg-opacity-50 group-hover:opacity-100 pointer-events-none"
2345                               href="https://uitgeverijpluim.nl/boeken/middle-management-science" aria-label="Nachoem M. Wijnberg - Middle management science."></a>
2346
2347                            <a href="https://uitgeverijpluim.nl/boeken/middle-management-science" aria-label="Nachoem M. Wijnberg - Middle management science."
2348                               class="z-50 my-1 btn btn--hover block uppercase py-sm px-6 border-3 border-gray-1000 bg-white relative text-gray-900 hover:text-white pointer-events-auto">Bekijk product</a>
2349                        </div>
2350                                    </div>
2351                <div class="mt-6">
2352                    <div class="text-sm leading-normal uppercase mb-1px">
2353                        <a class="text-gray-900 hover:text-gray-1400 transition-colors duration-400"
2354                           href="https://uitgeverijpluim.nl/boeken/middle-management-science" aria-label="Nachoem M. Wijnberg - Middle management science.">
2355                            Nachoem M. Wijnberg - Middle management science.                        </a>
2356                    </div>
2357                    <div class="flex justify-between items-center">
2358                        <p class="text-gray-1400 text-base leading-normal">
2359                            <ins class="no-underline">€ 24,99</ins>
2360                        </p>
2361                    </div>
2362                </div>
2363            </div>
2364                            </form>
2365</div>
2366                    </div>
2367    </div>
2368    
2368<script>
2369    function initToolbar_6ab5759ad4c9e() {
2370        return {
2371            options: {
2372                url: 'https://uitgeverijpluim.nl/boeken'
2373            },
2374            oneDirection: false,
2375            changeUrl: function (paramName, paramValue, defaultValue) {
2376                var decode = window.decodeURIComponent,
2377                    urlPaths = this.options.url.split('?'),
2378                    baseUrl = urlPaths[0],
2379                    urlParams = urlPaths[1] ? urlPaths[1].split('&') : [],
2380                    paramData = {},
2381                    parameters, i, form, params, key, input, formKey;
2382
2383                for (i = 0; i < urlParams.length; i++) {
2384                    parameters = urlParams[i].split('=');
2385                    paramData[decode(parameters[0])] = parameters[1] !== undefined ?
2386                        decode(parameters[1].replace(/\+/g, '%20')) :
2387                        '';
2388                }
2389                paramData[paramName] = paramValue;
2390
2391                if (this.options.post) {
2392                    form = document.createElement('form');
2393                    params = [this.options.mode, this.options.direction, this.options.order, this.options.limit];
2394
2395                    for (key in paramData) {
2396                        if (params.indexOf(key) !== -1) { //eslint-disable-line max-depth
2397                            input = document.createElement('input');
2398                            input.name = key;
2399                            input.value = paramData[key];
2400                            form.appendChild(input);
2401                            delete paramData[key];
2402                        }
2403                    }
2404                    formKey = document.createElement('input');
2405                    formKey.name = 'form_key';
2406                    formKey.value = this.options.formKey;
2407                    form.appendChild(formKey);
2408
2409                    paramData = $.param(paramData);
2410                    baseUrl += paramData.length ? '?' + paramData : '';
2411
2412                    form.action = baseUrl;
2413                    form.method = 'POST';
2414                    document.body.appendChild(form);
2415                    form.submit();
2416                } else {
2417                    if (paramValue === defaultValue) { //eslint-disable-line eqeqeq
2418                        delete paramData[paramName];
2419                    }
2420                    paramData = Object.keys(paramData).map(function(k) {
2421                        return encodeURIComponent(k) + '=' + encodeURIComponent(paramData[k])
2422                    }).join('&');
2423
2424                    location.href = baseUrl + (paramData.length ? '?' + paramData : '');
2425                }
2426            }
2427        }
2428    }
2429</script>
2429
2430    <div x-data="initToolbar_6ab5759ad4c9e()"
2431         class="toolbar toolbar-products h-28 grid grid-cols-3 grid-flow-row gap-2 items-center">
2432                    <div class="colspan-1">&nbsp;</div>
2433                
2434                
2435    <div class="pages order-2 col-span-2 justify-end flex">
2436                    <strong class="sr-only label pages-label" id="paging-label_520">Pagina</strong>
2437            <ul class="pagination flex ml-auto -mr-3" aria-labelledby="paging-label_520">
2438            
2439            
2440            
2441                                            <li>
2442                    <a class="inline-block py-3 px-5 uppercase hover:text-gray-800 border-b-2&#x20;text-gray-800 border-gray-800 hover:text-gray-1400 transition-colors duration-400" href="https://uitgeverijpluim.nl/boeken" title="Pagina 1">
2443                        1                    </a>
2444                </li>
2445                                            <li>
2446                    <a class="inline-block py-3 px-5 uppercase hover:text-gray-800 text-gray-700 border-gray-800 hover:text-gray-1400 transition-colors duration-400" href="https://uitgeverijpluim.nl/boeken?p=2" title="Pagina 2">
2447                        2                    </a>
2448                </li>
2449                                            <li>
2450                    <a class="inline-block py-3 px-5 uppercase hover:text-gray-800 text-gray-700 border-gray-800 hover:text-gray-1400 transition-colors duration-400" href="https://uitgeverijpluim.nl/boeken?p=3" title="Pagina 3">
2451                        3                    </a>
2452                </li>
2453                                            <li>
2454                    <a class="inline-block py-3 px-5 uppercase hover:text-gray-800 text-gray-700 border-gray-800 hover:text-gray-1400 transition-colors duration-400" href="https://uitgeverijpluim.nl/boeken?p=4" title="Pagina 4">
2455                        4                    </a>
2456                </li>
2457                                            <li>
2458                    <a class="inline-block py-3 px-5 uppercase hover:text-gray-800 text-gray-700 border-gray-800 hover:text-gray-1400 transition-colors duration-400" href="https://uitgeverijpluim.nl/boeken?p=5" title="Pagina 5">
2459                        5                    </a>
2460                </li>
2461            
2462            
2463            
2464                            <li>
2465                                        <a class="inline-block py-3 px-5 uppercase text-gray-700 border-b-2 border-transparent hover:text-gray-800 transition-colors duration-400" href="https://uitgeverijpluim.nl/boeken?p=2" title="Volgende">
2466                        <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
2467                            <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path>
2468                        </svg>
2469                    </a>
2470                </li>
2471                        </ul>
2472
2473            </div>
2474
2475    </div>
2476</section>
2477</div></div></div></div></div></main><footer class="page-footer"><div class="footer content"><footer class="mt-16 lg:mt-32">
2478    <div class="grid grid-cols-12 gap-4">
2479        <div class="col-span-12 sm:col-span-3">
2480            <h2 class="uppercase text-gray-1400 mb-md text-3xl">Uitgeverij Pluim</h2>
2481            <ul class="mb-5">
2482                <li><a href="/privacybeleid"
2483                       class="block uppercase text-secondary text-sm leading-normal hover:text-primary transition-colors duration-400">Privacybeleid</a></li>
2484                <li class="mt-4 lg:mt-0"><a href="/over-ons"
2485                                            class="block uppercase text-secondary text-sm leading-normal hover:text-primary transition-colors duration-400">Over
2486                        ons</a></li>
2487                <li class="mt-4 lg:mt-0"><a href="/contact"
2488                                            class="block uppercase text-secondary text-sm leading-normal hover:text-primary transition-colors duration-400">Contact</a>
2489                </li>
2490            </ul>
2491        </div>
2492        <div class="col-span-12 sm:col-span-5 lg:col-span-4 xl:col-span-3">
2493            <div class="lg:w-1/4 md:w-1/2 w-full lg:px-4">
2494     <form
2495        class="form subscribe mb-2md"
2496        action="https://uitgeverijpluim.nl/newsletter/subscriber/new/"
2497        method="post"
2498        x-data="initNewsletterForm"
2499        @submit.prevent="submitForm"
2500        id="newsletter-validate-detail"
2501        aria-label="Abonneren&#x20;op&#x20;de&#x20;nieuwsbrief"
2502    >
2503        <h2 class="uppercase text-gray-1400 mb-md text-3xl">Nieuwsbrief</h2>
2504        <div class="flex justify-center gap-2 md:justify-start">
2505            <label for="newsletter-subscribe" class="sr-only">
2506                E-mailadres            </label>
2507            <input
2508                name="email"
2509                type="email"
2510                required
2511                id="newsletter-subscribe"
2512                class="text-gray-1000 h-2md flex-grow border-2 border-gray-300 px-3 mr-1"
2513                placeholder="E-mailadres"
2514                autocomplete="email"
2515                aria-describedby="footer-newsletter-heading"
2516            >
2517            <input name="form_key" type="hidden" value="7SrAQN4OFxHzhKHj" />                        <button type="submit" class="bg-primary h-2md block px-3 text-gray-900 uppercase">
2518                OK            </button>
2519        </div>
2520        <div>
2521            <template x-if="displayErrorMessage">
2522                <p class="flex items-center text-red">
2523                    <span class="inline-block w-8 h-8 mr-3">
2524                        <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">
2525  <path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
2526</svg>
2527                    </span>
2528                    <template x-for="errorMessage in errorMessages">
2529                        <span x-html="errorMessage"></span>
2530                    </template>
2531                </p>
2532            </template>
2533        </div>
2534    </form>
2535    <div class="w-full">
2536            </div>
2537    
2537<script>
2538        function initNewsletterForm() {
2539            return {
2540                errors: 0,
2541                hasCaptchaToken: 0,
2542                displayErrorMessage: false,
2543                errorMessages: [],
2544                setErrorMessages(messages) {
2545                    this.errorMessages = [messages]
2546                    this.displayErrorMessage = this.errorMessages.length
2547                },
2548                submitForm() {
2549                    // Do not rename $form, the variable is expected to be declared in the recaptcha output
2550                    const $form = document.querySelector('#newsletter-validate-detail');
2551                    
2552                    if (this.errors === 0) {
2553                        $form.submit();
2554                    }
2555                }
2556            }
2557        }
2558
2559        window.addEventListener('alpine:init', () => Alpine.data('initNewsletterForm', initNewsletterForm), {once: true})
2560    </script>
2560
2561    </div>
2562            <ul class="flex md:hidden">
2563                <li class="pr-4">
2564                    <a class="text-gray-700 text-xl leading-normal" href="https://www.instagram.com/uitgeverijpluim/"
2565                       target="_blank" title="Instagram" rel="noreferrer">
2566                        <svg viewBox="0 0 576 512" stroke="currentColor" class="h-5 w-5">
2567                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
2568                                  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>
2569                        </svg>
2570                    </a>
2571                </li>
2572                <li class="pr-4">
2573                    <a class="text-gray-700 text-xl leading-normal" href="https://www.facebook.com/uitgeverijpluim"
2574                       target="_blank" title="Facebook" rel="noreferrer">
2575                        <svg viewBox="0 0 576 512" stroke="currentColor" class="h-5 w-5">
2576                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
2577                                  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>
2578                        </svg>
2579                    </a>
2580                </li>
2581                <li class="pr-4">
2582                    <a class="text-gray-700 text-xl leading-normal"
2583                       href="https://www.youtube.com/channel/UCRr07oGz4RdkdjrntFZ76nw" target="_blank" title="YouTube"
2584                       rel="noreferrer">
2585                        <svg viewBox="0 0 576 512" stroke="currentColor" class="h-5 w-5">
2586                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
2587                                  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>
2588                        </svg>
2589                    </a>
2590                </li>
2591                <li class="pr-4">
2592                    <a class="text-gray-700 text-xl leading-normal" href="https://twitter.com/uitgeverijpluim"
2593                       target="_blank" title="Twitter" rel="noreferrer">
2594                        <svg viewBox="0 0 576 512" stroke="currentColor" class="h-5 w-5">
2595                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
2596                                  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>
2597                        </svg>
2598                    </a>
2599                </li>
2600                <li class="pr-4">
2601                    <a class="text-gray-700 text-xl leading-normal" href="https://soundcloud.com/uitgeverijpluim"
2602                       target="_blank" title="Soundcloud" rel="noreferrer">
2603                        <svg viewBox="0 0 576 512" stroke="currentColor" class="h-5 w-5">
2604                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
2605                                  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
2605-7.191A32 32 0 0 0 512 336v-48c0-141.479-114.496-256-256-256z"
2606                            "></path>
2607                        </svg>
2608                    </a>
2609                </li>
2610            </ul>
2611        </div>
2612    </div>
2613    <p class="text-xs leading-normal mt-12 mb-5 lg:text-right">&copy; Uitgeverij Pluim</p>
2614</footer>
2615</div>
2616</div>
2617</div>
2618</div>
2619</main>
2620</body>
2621</html>
2622</div></footer>
2622<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 2622
2622
2623<script>
2624(() => {
2625    class BFCacheHandler {
2626        constructor() {
2627            this.options = {
2628                isCustomerLoggedIn: false,
2629                enableUserInteractionRefreshMiniCart: true,
2630                autoCloseMenuMobile: true            };
2631
2632            this.userInteractionEvents = ['touchstart', 'mouseover', 'wheel', 'scroll', 'keydown'];
2633            this._reloadTriggered = false;
2634            this.dom = {};
2635
2636            // Centralized selectors for reuse
2637            this.selectors = {
2638                cartDrawer: "[x-data^='initCartDrawer']",
2639                mobileMenu: "[x-data^='initMenuMobile']",
2640                desktopMenu: "[x-data^='initMenuDesktop']"
2641            };
2642        }
2643
2644                getElement(key) {
2645            const cached = this.dom[key];
2646            if (cached && document.contains(cached)) return cached;
2647            const el = document.querySelector(this.selectors[key]);
2648            if (el) this.dom[key] = el;
2649            return el;
2650        }
2651
2652        init() {
2653            this.refreshMiniCart();
2654            this.reloadCustomerLoginPage();
2655            this.actionAutoCloseMenu(this.options.autoCloseMenuMobile);
2656        }
2657
2658                refreshMiniCart() {
2659            if (this.options.enableUserInteractionRefreshMiniCart) {
2660                this.refreshMiniCartOnUserInteraction();
2661            } else {
2662                this.actionRefreshMiniCart();
2663            }
2664        }
2665
2666        refreshMiniCartOnUserInteraction() {
2667            const refreshMiniCart = () => {
2668                this.userInteractionEvents.forEach(eventType =>
2669                    window.removeEventListener(eventType, refreshMiniCart)
2670                );
2671                this.actionRefreshMiniCart();
2672            };
2673            const opts = { once: true, passive: true };
2674            this.userInteractionEvents.forEach(eventType =>
2675                window.addEventListener(eventType, refreshMiniCart, opts)
2676            );
2677        }
2678
2679                reloadCustomerLoginPage() {
2680            const backendLoggedInState = this.options.isCustomerLoggedIn;
2681
2682            let customerData = null;
2683            try {
2684                const cacheStorage = localStorage.getItem('mage-cache-storage');
2685                if (cacheStorage) {
2686                    customerData = JSON.parse(cacheStorage).customer || null;
2687                }
2688            } catch {
2689                // Ignore parse errors silently
2690            }
2691
2692            const frontendLoggedInState = Boolean(customerData?.firstname);
2693
2694            if (frontendLoggedInState !== backendLoggedInState && !this._reloadTriggered) {
2695                this._reloadTriggered = true;
2696                window.location.reload();
2697            }
2698        }
2699
2700                actionRefreshMiniCart() {
2701            window.dispatchEvent(new CustomEvent('reload-customer-section-data'));
2702        }
2703
2704                actionAutoCloseMenu(autoCloseMenuMobile) {
2705            window.dispatchEvent(new Event('clear-messages'));
2706
2707            const cartDrawer = this.getElement('cartDrawer');
2708            if (cartDrawer && typeof Alpine !== 'undefined') {
2709                requestAnimationFrame(() => {
2710                    const cartDrawerData = Alpine.$data(cartDrawer);
2711                    if (cartDrawerData?.open !== undefined) {
2712                        cartDrawerData.open = false;
2713                        document.body.style.overflow = '';
2714                    }
2715                });
2716            }
2717
2718            if (autoCloseMenuMobile) {
2719                this.handleMenuClosure();
2720            }
2721        }
2722
2723                handleMenuClosure() {
2724            if (typeof Alpine === 'undefined' || !Alpine.$data) return;
2725
2726            const mobileMenu = this.getElement('mobileMenu');
2727            if (mobileMenu) {
2728                const mobileMenuData = Alpine.$data(mobileMenu);
2729                if (mobileMenuData?.open !== undefined) {
2730                    mobileMenuData.open = false;
2731                }
2732            }
2733
2734            const desktopMenu = this.getElement('desktopMenu');
2735            if (desktopMenu) {
2736                const desktopMenuData = Alpine.$data(desktopMenu);
2737                if (desktopMenuData?.hoverPanelActiveId !== undefined) {
2738                    desktopMenuData.hoverPanelActiveId = 0;
2739                }
2740            }
2741        }
2742    }
2743
2744        window.addEventListener('pageshow', (event) => {
2745        if (event.persisted) {
2746            (window._bfcacheHandler ??= new BFCacheHandler()).init();
2747        }
2748    }, { passive: true });
2749})();
2750</script>
2750
2751
vendor: 2,386 bytes, lines 2752-2812
2752<script>
2753    (() => {
2754        function src_default(Alpine) {
2755            Alpine.directive("intersect", Alpine.skipDuringClone((el, { value, expression, modifiers }, { evaluateLater, cleanup }) => {
2756                let evaluate = evaluateLater(expression);
2757                let options = {
2758                    rootMargin: getRootMargin(modifiers),
2759                    threshold: getThreshold(modifiers)
2760                };
2761                let observer = new IntersectionObserver((entries) => {
2762                    entries.forEach((entry) => {
2763                        if (entry.isIntersecting === (value === "leave"))
2764                            return;
2765                        evaluate();
2766                        modifiers.includes("once") && observer.disconnect();
2767                    });
2768                }, options);
2769                observer.observe(el);
2770                cleanup(() => {
2771                    observer.disconnect();
2772                });
2773            }));
2774        }
2775        function getThreshold(modifiers) {
2776            if (modifiers.includes("full"))
2777                return 0.99;
2778            if (modifiers.includes("half"))
2779                return 0.5;
2780            if (!modifiers.includes("threshold"))
2781                return 0;
2782            let threshold = modifiers[modifiers.indexOf("threshold") + 1];
2783            if (threshold === "100")
2784                return 1;
2785            if (threshold === "0")
2786                return 0;
2787            return Number(`.${threshold}`);
2788        }
2789        function getLengthValue(rawValue) {
2790            let match = rawValue.match(/^(-?[0-9]+)(px|%)?$/);
2791            return match ? match[1] + (match[2] || "px") : void 0;
2792        }
2793        function getRootMargin(modifiers) {
2794            const key = "margin";
2795            const fallback = "0px 0px 0px 0px";
2796            const index = modifiers.indexOf(key);
2797            if (index === -1)
2798                return fallback;
2799            let values = [];
2800            for (let i = 1; i < 5; i++) {
2801                values.push(getLengthValue(modifiers[index + i] || ""));
2802            }
2803            values = values.filter((v) => v !== void 0);
2804            return values.length ? values.join(" ").trim() : fallback;
2805        }
2806
2807        document.addEventListener("alpine:init", () => {
2808            window.Alpine.plugin(src_default);
2809        });
2810    })();
2811    //# sourceURL=alpine-intersect.js
2812</script>
2812
2813
2814<script>
2815    let SnapSlider = class {
2816        constructor(el, {
2817            labelSepparator = 'van',
2818            autoPager,
2819            groupPager
2820        } = {}) {
2821            this.el = el;
2822            this.track = this.el.querySelector("[data-track]");
2823            if (!this.track) {
2824                console.warn(
2825                    "No Slider track defined, reverting back to CSS slider.\nPlease create a wrapper for your slides with the attribute data-track",
2826                    this.el
2827                );
2828                return;
2829            }
2830            this.initialLoad = true;
2831            this.slides = [];
2832            this.inViewObserver;
2833            this.mutationObserver;
2834            this.resizeObserver;
2835            this.pager = this.el.querySelector("[data-pager]");
2836            this.navBtns = Array.from(
2837                this.el.querySelectorAll("[data-next], [data-prev]")
2838            );
2839            this.slideLabelSepparator = this.el.dataset.slideLabelSepparator || labelSepparator;
2840            this.useAutoPager = autoPager || this.el.hasAttribute("data-auto-pager") || false;
2841            this.useGroupPager = groupPager || this.el.hasAttribute("data-group-pager") || false;
2842            this.sliderLabel = this.el.hasAttribute("aria-label") && this.el.getAttribute("aria-label").toLowerCase().trim().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
2843            this.sliderId = this.el.id || this.sliderLabel || "slider";
2844            this.markerIdName = "data-target-id";
2845            this.pagerClasses = this.el.dataset.pagerClass || "snap-pager";
2846            this.markerClasses = this.el.dataset.markerClass || "snap-marker";
2847            this.init();
2848        }
2849        init() {
2850            this.setupSlides();
2851            this.setupNav();
2852            if (this.useAutoPager) {
2853                this.createPager();
2854            } else {
2855                this.setupPager();
2856            }
2857            this.setupObservers();
2858            this.setupMutationObserver();
2859            this.setupResizeObserver();
2860            this.el.addEventListener("click", this.eventHandler.bind(this));
2861            this.el.addEventListener("keydown", this.eventHandler.bind(this));
2862        }
2863        destroy() {
2864            this.inViewObserver?.disconnect();
2865            this.mutationObserver?.disconnect();
2866            this.resizeObserver?.disconnect();
2867            this.el.removeEventListener("click", this.eventHandler.bind(this));
2868            this.el.removeEventListener("keydown", this.eventHandler.bind(this));
2869        }
2870        roundUpIfGreaterThan(number, min = 8) {
2871            const decimalPart = number - Math.floor(number);
2872            return decimalPart >= min ? Math.ceil(number) : Math.floor(number);
2873        }
2874        getInViewItems() {
2875            const inViewSlides = this.track.querySelectorAll("[data-in-view]");
2876            const firstInViewSlide = inViewSlides[0] || null;
2877            const lastInViewSlide = inViewSlides[inViewSlides.length - 1] || null;
2878            const isAtStart = firstInViewSlide === this.slides[0];
2879            const isAtEnd = lastInViewSlide === this.slides[this.slides.length - 1];
2880            return {
2881                inViewSlides,
2882                totalInViewSlides: inViewSlides.length,
2883                firstInViewSlide,
2884                lastInViewSlide,
2885                isAtStart,
2886                isAtEnd,
2887                hasNoOverflow: isAtStart && isAtEnd
2888            };
2889        }
2890        groupPagerMarkers() {
2891            if (this.slides.length === 0 || !this.pager || !this.useGroupPager) return;
2892            const totalVisibleSlides = this.roundUpIfGreaterThan(
2893                this.track.offsetWidth / this.slides[0].offsetWidth
2894            );
2895            const markers = Array.from(this.pager.querySelectorAll("a, button"));
2896            markers.forEach((marker, index) => {
2897                marker.style.display = index % totalVisibleSlides === 0 ? null : "none";
2898            });
2899        }
2900        handleInView(entries) {
2901            entries.forEach((entry) => {
2902                const marker = this.pager && (this.pager.querySelector(`[href="#${entry.target.id}"]`) || this.pager.querySelector(
2903                    `[${this.markerIdName}="${entry.target.id}"]`
2904                ));
2905                entry.target.toggleAttribute("data-in-view", entry.isIntersecting);
2906                entry.target.toggleAttribute("inert", !entry.isIntersecting);
2907                marker?.setAttribute("aria-current", entry.isIntersecting);
2908                marker?.setAttribute("tabindex", entry.isIntersecting ? "0" : "-1");
2909            });
2910            const {
2911                isAtStart,
2912                isAtEnd,
2913                hasNoOverflow
2914            } = this.getInViewItems();
2915            this.navBtns.forEach((btn) => {
2916                if (btn.hasAttribute("data-next")) {
2917                    btn.style.visibility = hasNoOverflow ? "hidden" : null;
2918                    isAtEnd ? btn.setAttribute("disabled", "") : btn.removeAttribute("disabled");
2919                }
2920                if (btn.hasAttribute("data-prev")) {
2921                    btn.style.visibility = hasNoOverflow ? "hidden" : null;
2922                    isAtStart ? btn.setAttribute("disabled", "") : btn.removeAttribute("disabled");
2923                }
2924            });
2925            if (this.pager) {
2926                this.pager.style.visibility = hasNoOverflow ? "hidden" : null;
2927            }
2928            if (!this.initialLoad) {
2929                this.el.dispatchEvent(
2930                    new CustomEvent("slideChange", {
2931                        detail: this.getInViewItems()
2932                    })
2933                );
2934            } else {
2935                this.initialLoad = false;
2936            }
2937            if (document.activeElement.parentElement.hasAttribute("data-pager")) {
2938                const activeItems = this.pager.querySelectorAll('[tabindex="0"]');
2939                if (activeItems.length) {
2940                    activeItems[0].focus({ preventScroll: true });
2941                }
2942            }
2943        }
2944        getSlides() {
2945            if (!this.track) return [];
2946            return Array.from(this.track.children).filter(
2947                (child) => child.tagName.toLowerCase() !== "template" && child.tagName.toLowerCase() !== "style" && child.tagName.toLowerCase() !== "script"
2948            );
2949        }
2950        refreshSlides() {
2951            this.initialLoad = true;
2952            this.slides = this.getSlides();
2953            this.setupSlides();
2954            if (this.useAutoPager) {
2955                this.createPager();
2956            } else {
2957                this.setupPager();
2958            }
2959            this.groupPagerMarkers();
2960            this.setupObservers();
2961        }
2962        setupSlides() {
2963            this.slides = this.getSlides();
2964            this.slides.forEach((slide, index) => {
2965                const totalSlides = this.slides.length;
2966                const currentSlide = index + 1;
2967                const existingLabel = slide.getAttribute("aria-label") || "";
2968                const isImage = slide.tagName.toLowerCase() === "img" || slide.tagName.toLowerCase() === "picture";
2969                const slideLabelTag = isImage ? "alt" : "aria-label";
2970                const hasAutoLabel = existingLabel.startsWith(
2971                    `${currentSlide} ${this.slideLabelSepparator} `
2972                );
2973                if (!slide.hasAttribute("id")) {
2974                    slide.setAttribute(
2975                        "id",
2976                        `${this.sliderId}-item-${currentSlide}`
2977                    );
2978                }
2979                if (!slide.hasAttribute(slideLabelTag) || slide.getAttribute(slideLabelTag) === "" || hasAutoLabel) {
2980                    slide.setAttribute(
2981                        slideLabelTag,
2982                        `${currentSlide} ${this.slideLabelSepparator} ${totalSlides}`
2983                    );
2984                }
2985                if (slide.tagName.toLowerCase() === "div" && !slide.hasAttribute("role")) {
2986                    slide.setAttribute("role", "group");
2987                }
2988                if (this.pager && !isImage) {
2989                    slide.setAttribute("role", "tabpanel");
2990                }
2991                if (slide.getAttribute("role") === "group") {
2992                    slide.setAttribute("aria-roledescription", "item");
2993                }
2994            });
2995            if (!this.el.hasAttribute("role") && this.el.tagName !== "SECTION") {
2996                this.el.setAttribute("role", "region");
2997            }
2998            if (!this.el.hasAttribute("aria-roledescription")) {
2999                this.el.setAttribute("aria-roledescription", "carousel");
3000            }
3001            this.track.setAttribute("tabindex", 0);
3002            this.track.setAttribute("aria-live", "polite");
3003        }
3004        setupNav() {
3005            this.navBtns.forEach((btn) => {
3006                btn.setAttribute("disabled", "");
3007                btn.removeAttribute("hidden");
3008                btn.classList.remove("invisible");
3009                btn.style.visibility = null;
3010            });
3011        }
3012        createPager() {
3013            const newPager = document.createElement("nav");
3014            newPager.setAttribute("data-pager", "");
3015            newPager.setAttribute("role", "tablist");
3016            newPager.classList.add(...this.pagerClasses.split(" "));
3017            this.slides.forEach((slide, index) => {
3018                const marker = document.createElement("button");
3019                const slideId = slide.id || `${this.sliderId}-item-${index + 1}`;
3020                marker.setAttribute(this.markerIdName, slideId);
3021                marker.classList.add(...this.markerClasses.split(" "));
3022                this.setupPagerMarker(marker, index);
3023                newPager.appendChild(marker);
3024                if (slide.tagName.toLowerCase() !== "img" && slide.tagName.toLowerCase() !== "picture") {
3025                    slide.removeAttribute("aria-roledescription");
3026                    slide.setAttribute("role", "tabpanel");
3027                }
3028            });
3029            if (this.pager) {
3030                this.pager.setAttribute("role", "tablist");
3031                this.pager.replaceChildren(...newPager.children);
3032            } else {
3033                this.track.after(newPager);
3034                this.pager = this.el.querySelector("[data-pager]");
3035            }
3036        }
3037        setupPager() {
3038            if (!this.pager) return;
3039            this.pager.setAttribute("role", "tablist");
3040            const items = Array.from(this.pager.querySelectorAll("a, button"));
3041            items.forEach((marker, index) => this.setupPagerMarker(marker, index));
3042        }
3043        setupPagerMarker(marker, index) {
3044            const markerId = marker.getAttribute("href")?.slice(1) || marker.getAttribute(this.markerIdName);
3045            const slideId = markerId || `${this.sliderId}-item-${index + 1}`;
3046            if (!markerId) {
3047                marker.setAttribute(this.markerIdName, slideId);
3048            }
3049            marker.setAttribute("role", "tab");
3050            marker.setAttribute("aria-controls", slideId);
3051            marker.setAttribute("aria-posinset", index + 1);
3052            marker.setAttribute("aria-setsize", this.slides.length);
3053            marker.setAttribute("tabindex", "-1");
3054            if (!marker.hasAttribute("aria-label")) {
3055                marker.setAttribute("aria-label", `Slide ${index + 1}`);
3056            }
3057        }
3058        setupObservers() {
3059            if (this.inViewObserver) {
3060                this.inViewObserver.disconnect();
3061            }
3062            this.inViewObserver = new IntersectionObserver(
3063                this.handleInView.bind(this), {
3064                    root: this.track,
3065                    threshold: 0.8
3066                }
3067            );
3068            this.slides.forEach((slide) => this.inViewObserver.observe(slide));
3069        }
3070        setupMutationObserver() {
3071            this.mutationObserver = new MutationObserver(
3072                this.refreshSlides.bind(this)
3073            );
3074            this.mutationObserver.observe(this.track, {
3075                childList: true,
3076                subtree: false
3077            });
3078        }
3079        setupResizeObserver() {
3080            this.resizeObserver = new ResizeObserver(
3081                this.groupPagerMarkers.bind(this)
3082            );
3083            this.resizeObserver.observe(this.el);
3084        }
3085        goToSlideDir(dir = "next") {
3086            const {
3087                firstInViewSlide,
3088                lastInViewSlide
3089            } = this.getInViewItems();
3090            const isPrev = dir === "prev";
3091            let targetSlide = isPrev ? firstInViewSlide?.previousElementSibling : lastInViewSlide?.nextElementSibling;
3092            if (!targetSlide) return;
3093            if (targetSlide.tagName.toLowerCase() === "template") {
3094                targetSlide = isPrev ? targetSlide?.previousElementSibling : targetSlide?.nextElementSibling;
3095            }
3096            targetSlide.scrollIntoView({ block: "nearest", inline: isPrev ? "end" : "start" });
3097        }
3098        pagerToSlide(event) {
3099            if (!event.target.closest("[data-pager]")) return;
3100            const marker = event.target.closest("a, button");
3101            if (!marker) return;
3102            event.preventDefault();
3103            const slideId = marker.getAttribute("href")?.slice(1) || marker.getAttribute(this.markerIdName);
3104            const targetSlide = this.track.querySelector(`#${slideId}`);
3105            targetSlide?.scrollIntoView({ block: "nearest", inline: "start" });
3106        }
3107        eventHandler(event) {
3108            const target = event.target.closest(
3109                "[data-next], [data-prev], [data-pager]"
3110            );
3111            if (!target) return;
3112            if (event.type === "click") {
3113                if (target.hasAttribute("data-next")) {
3114                    this.goToSlideDir("next");
3115                } else if (target.hasAttribute("data-prev")) {
3116                    this.goToSlideDir("prev");
3117                } else if (target.closest("[data-pager]")) {
3118                    this.pagerToSlide(event);
3119                }
3120            }
3121            if (event.type === "keydown" && target.closest("[data-pager]")) {
3122                if (event.key === "ArrowRight") {
3123                    this.goToSlideDir("next");
3124                } else if (event.key === "ArrowLeft") {
3125                    this.goToSlideDir("prev");
3126                }
3127            }
3128        }
3129    };
3130
3131    (() => {
3132        function alpineSnapSlider(Alpine) {
3133            Alpine.directive("snap-slider", (el, {
3134                modifiers
3135            }, {
3136                cleanup
3137            }) => {
3138                const autoPager = modifiers.includes("auto-pager");
3139                const groupPager = modifiers.includes("group-pager");
3140                const slider = new SnapSlider(el, {
3141                    autoPager,
3142                    groupPager
3143                });
3144                cleanup(() => {
3145                    slider.destroy();
3146                });
3147            });
3148        }
3149
3150        document.addEventListener("alpine:init", () => {
3151            window.Alpine.plugin(alpineSnapSlider);
3152        });
3153    })();
3154</script>
3154
3155
3156<script type="module"
3157        src="https://uitgeverijpluim.nl/static/version1790248052/frontend/Pluim/Hyva/nl_NL/Hyva_Theme/js/alpine3.min.js"
3158        defer
3159        crossorigin
3160></script>
vendor: 1 bytes, line 3160
3160
3161<script>
3162    function dispatchMessages(messages, hideAfter) {
3163        const messagesEvent = new CustomEvent("messages-loaded", {
3164            detail: {
3165                messages: messages,
3166                hideAfter: hideAfter
3167            }
3168        });
3169        window.dispatchEvent(messagesEvent);
3170    }
3171
3172        if (typeof hyva === 'undefined' || (!hyva.getBrowserStorage || !hyva.getCookie || !hyva.setCookie)) {
3173        console.warn("Hyvä helpers are not loaded yet. Make sure they are included before this script");
3174    }
3175
3176    (function( hyva, undefined ) {
3177
3178        hyva.initFormKey = () => {
3179            if (typeof hyva.getFormKey !== 'function') return;
3180
3181            const formKey = hyva.getFormKey();
3182            if (!formKey) return;
3183
3184            document.querySelectorAll('input[name="form_key"]').forEach((input) => {
3185                input.value = formKey;
3186            });
3187        }
3188
3189        hyva.initMessages = () => {
3190            try {
3191                const messages = hyva.getCookie('mage-messages');
3192                window.mageMessages = messages ? JSON.parse(decodeURIComponent(messages).replace(/\+/g, ' ')) : [];
3193
3194                dispatchMessages(window.mageMessages);
3195
3196                                // empty `mage-messages` cookie
3197                const skipSetDomain = true;
3198                hyva.setCookie('mage-messages','', -1, skipSetDomain);
3199
3200            } catch (error) {
3201                console.warn('Error parsing Cookie Messages:', error);
3202            }
3203        }
3204
3205        window.addEventListener('DOMContentLoaded', hyva.initFormKey);
3206
3207        window.addEventListener('pageshow', (event) => {
3208            if (event.persisted) {
3209                hyva.initFormKey();
3210            }
3211        });
3212
3213        hyva.alpineInitialized(hyva.initMessages)
3214    }( window.hyva = window.hyva || {} ));
3215    //# sourceURL=hyva-cookies.js
3216</script>
vendor: 1 bytes, line 3216
3216
3217<script>
3218    (() => {
3219        const private_content_key = 'mage-cache-storage';
3220        const private_content_expire_key = 'mage-cache-timeout';
3221        const private_content_version_key = 'private_content_version';
3222        const section_data_ids_key = 'section_data_ids';
3223        const mage_cache_session_id_key = 'mage-cache-sessid';
3224        const last_visited_store_key = 'last_visited_store';
3225
3226        const ttl = 3600;
3227
3228                if (typeof hyva === 'undefined' || (!hyva.getBrowserStorage || !hyva.getCookie || !hyva.setCookie)) {
3229            console.warn("Hyvä helpers are not loaded yet. Make sure they are included before this script");
3230        }
3231
3232        function loadSectionData () {
3233            const browserStorage = hyva.getBrowserStorage();
3234            if (!browserStorage) {
3235                typeof window.dispatchMessages !== "undefined" && window.dispatchMessages(
3236                    [{
3237                        type: "warning",
3238                        text: "Schakel LocalStorage in in je browser."
3239                    }]
3240                );
3241                return;
3242            }
3243            try {
3244                let isInvalid = false;
3245
3246                                if (hyva.getCookie(last_visited_store_key) !== CURRENT_STORE_CODE) {
3247                    isInvalid = true;
3248                }
3249                hyva.setCookie(last_visited_store_key, CURRENT_STORE_CODE, false, false);
3250
3251                                if (!hyva.getCookie(mage_cache_session_id_key)) {
3252                    isInvalid = true;
3253                    browserStorage.removeItem(private_content_key);
3254                    const skipSetDomain = true;
3255                    const days = false;
3256                    hyva.setCookie(mage_cache_session_id_key, true, days, skipSetDomain)
3257                }
3258
3259                const cookieVersion = hyva.getCookie(private_content_version_key);
3260                const storageVersion = browserStorage.getItem(private_content_version_key);
3261
3262                if (cookieVersion && !storageVersion || cookieVersion !== storageVersion) {
3263                    isInvalid = true;
3264                }
3265
3266                const privateContentExpires = browserStorage.getItem(private_content_expire_key);
3267                if (privateContentExpires && new Date(privateContentExpires) < new Date()) {
3268                    browserStorage.removeItem(private_content_key);
3269                }
3270
3271                if (isInvalid && cookieVersion) {
3272                    fetchPrivateContent([]);
3273                } else if (cookieVersion && storageVersion && cookieVersion === storageVersion) {
3274                    const privateContent = JSON.parse(browserStorage.getItem(private_content_key));
3275                    if (
3276                        privateContent &&
3277                        privateContentExpires &&
3278                        privateContent.cart &&
3279                        privateContent.customer
3280                    ) {
3281                        dispatchPrivateContent(privateContent);
3282                    } else {
3283                        fetchPrivateContent([]);
3284                    }
3285                } else {
3286                    if (document.getElementById('default-section-data')) {
3287                        const privateContent = JSON.parse(document.getElementById('default-section-data').textContent.trim());
3288                        dispatchPrivateContent(privateContent);
3289                    } else {
3290                        dispatchPrivateContent({});
3291                    }
3292                }
3293
3294            } catch (error) {
3295                console.warn('Error retrieving Private Content:', error);
3296            }
3297        }
3298
3299        hyva.alpineInitialized(loadSectionData)
3300        window.addEventListener('reload-customer-section-data', loadSectionData);
3301
3302                window.processSectionDataBeforeDispatch = window.processSectionDataBeforeDispatch || (data => data);
3303
3304        function dispatchPrivateContent(data) {
3305
3306            const privateContentEvent = new CustomEvent("private-content-loaded", {
3307                detail: {
3308                    data: window.processSectionDataBeforeDispatch(data)
3309                }
3310            });
3311            window.dispatchEvent(privateContentEvent);
3312        }
3313
3314                function fetchPrivateContent(sections) {
3315            fetch(`${BASE_URL}customer/section/load/?sections=${encodeURIComponent(sections.join(','))}`, {
3316                method: 'GET',
3317                headers: {
3318                    'Content-Type': 'application/json',
3319                    'X-Requested-With': 'XMLHttpRequest'
3320                }
3321            })
3322                .then(response => response.ok && response.json())
3323                .then(
3324                    data => {
3325                        if (data) {
3326                            try {
3327                                const browserStorage = hyva.getBrowserStorage();
3328
3329                                // merge new data preserving non-invalidated sections
3330                                const oldSectionData = JSON.parse(browserStorage.getItem(private_content_key) || '{}') || {};
3331
3332                                                                if ((! data.cart || ! data.cart.cartId) && oldSectionData['checkout-data']) {
3333                                    delete oldSectionData['checkout-data'];
3334                                }
3335                                const newSectionData = Object.assign(oldSectionData, data);
3336
3337                                dispatchPrivateContent(newSectionData);
3338
3339                                // don't persist messages, they've been dispatched already
3340                                if (newSectionData.messages && newSectionData.messages.messages ) {
3341                                    newSectionData.messages.messages = [];
3342                                }
3343
3344                                browserStorage.setItem(private_content_key, JSON.stringify(newSectionData));
3345
3346                                const expiresAt = new Date(Date.now() + (ttl * 1000)).toISOString();
3347                                browserStorage.setItem(private_content_expire_key, expiresAt);
3348
3349                                const newCookieVersion = hyva.getCookie(private_content_version_key);
3350                                browserStorage.setItem(private_content_version_key, newCookieVersion);
3351
3352                                // We don't need the section_data_ids in Hyvä, but we store them for compatibility
3353                                // with Luma Fallback. Otherwise, not all sections are loaded in Luma Checkout
3354                                hyva.setCookie(
3355                                    section_data_ids_key,
3356                                    JSON.stringify(
3357                                        Object.keys(data).reduce((sectionDataIds, sectionKey) => {
3358                                            sectionDataIds[sectionKey] = data[sectionKey]['data_id'];
3359                                            return sectionDataIds;
3360                                        }, {})
3361                                    ),
3362                                    false,
3363                                    true
3364                                );
3365                            } catch (error) {
3366                                console.warn("Couldn't store privateContent", error);
3367                            }
3368                        }
3369                    }
3370                )
3371                .catch(error => {
3372                    console.warn("Couldn't fetch privateContent", error);
3373                });
3374        }
3375    })();
3376    //# sourceURL=hyva-private-content.js
3377</script>
vendor: 1 bytes, line 3377
3377
3378<script>
3379    (() => {
3380        document.addEventListener('submit', event => event.target.action = event.target.action.replace('%25uenc%25', hyva.getUenc()));
3381    })()
3382</script>
vendor: 1 bytes, line 3382
3382
3383<script>
3384    (events => {
3385        const dispatchUserInteractionEvent = () => {
3386            events.forEach(type => window.removeEventListener(type, dispatchUserInteractionEvent))
3387            window.dispatchEvent(new Event('init-external-scripts'))
3388        };
3389        events.forEach(type => window.addEventListener(type, dispatchUserInteractionEvent, {once: true, passive: true}))
3390    })(['touchstart', 'mousemove', 'wheel', 'scroll', 'keydown']);
3391    //# sourceURL=hyva-init-external-scripts.js
3392</script>
vendor: 1 bytes, line 3392
3392
3393<script>
3394    document.addEventListener("alpine:init", () => {
3395        Alpine.data('hyvaCmsToggle', () => ({
3396            open: false,
3397            notOpen() {
3398                return !this.open;
3399            },
3400            show() {
3401                this.open = true
3402            },
3403            hide() {
3404                this.open = false
3405            },
3406            toggle() {
3407                this.open = !this.open
3408            }
3409        }))
3410    });
3411</script>
vendor: 1 bytes, line 3411
3411
3412<script>
3413    function initSmileAttribute(options) {
3414        "use strict";
3415
3416        return {
3417            options             : options,
3418            searchLabel         : 'Zoeken (%s)',
3419            showMoreLabel       : 'Toon meer',
3420            showLessLabel       : 'Toon minder',
3421            noSearchResultLabel : 'Geen waarde die overeenkomt met de zoekopdracht <b>%s</b>.',
3422            expanded: false,
3423            items: [],
3424            lastSelectIndex: 0,
3425            maxSize: 0,
3426            displaySearch: false,
3427            displayedItems: [],
3428            fulltextSearch: null,
3429            searchPlaceholder: '',
3430            result: 0,
3431
3432            /**
3433             * Component initialization
3434             */
3435            initialize: function () {
3436
3437                this.items = this.options.items.map(this.addItemId.bind(this));
3438
3439                var lastSelectedIndex = Math.max.apply(null, (this.items.map(
3440                    function (v, k) {return v['is_selected'] ? k : 0;}))
3441                );
3442                this.maxSize = Math.max(this.options.maxSize, lastSelectedIndex + 1);
3443
3444                this.$nextTick(() => {
3445                    this.initSearchPlaceholder();
3446                    this.onShowLess();
3447                    this.displaySearch = this.displayShowMore();
3448                    this.displayedItems = this.getDisplayedItems();
3449                });
3450            },
3451
3452            /**
3453             * Init the place holder
3454             */
3455            initSearchPlaceholder: function () {
3456                var examples = this.items.slice(0, 2).map(function (item) {return item.label});
3457
3458                if (this.items.length > 2) {
3459                    examples.push('...');
3460                }
3461
3462                // Tip to make html chars interpretable by the browser when the value is get from JS.
3463                let placeholder = document.createElement('div');
3464                placeholder.innerHTML = this.searchLabel.replace('%s', examples.join(', '));
3465
3466                this.searchPlaceholder = placeholder.textContent;
3467            },
3468
3469            /**
3470             * Triggered when typing on the search input
3471             */
3472            onSearchChange: function (event) {
3473                var text = event.target.value;
3474                if (text.trim() === "") {
3475                    this.fulltextSearch = null;
3476                    this.onShowLess();
3477                } else {
3478                    this.fulltextSearch = text;
3479                    this.onShowMore();
3480                }
3481
3482                return true;
3483            },
3484
3485            /**
3486             * Triggered when leaving the search field.
3487             */
3488            onSearchFocusOut: function(event) {
3489                var text = event.target.value;
3490                if (text.trim() === "") {
3491                    this.fulltextSearch = null;
3492                }
3493            },
3494
3495            /**
3496             * Retrieve additional Results
3497             *
3498             * @param callback
3499             */
3500            loadAdditionalItems: function (callback) {
3501                fetch(this.options.ajaxLoadUrl, {
3502                    method: 'GET',
3503                    headers: {
3504                        'X-Requested-With': 'XMLHttpRequest',
3505                    },
3506                })
3507                .then((response) =>  response.json())
3508                .then((data) => {
3509                        this.items = data.map(this.addItemId.bind(this));
3510                        this.options.hasMoreItems = false;
3511
3512                        if (callback) {
3513                            return callback();
3514                        }
3515                    }
3516                );
3517            },
3518
3519            /**
3520             * Retrieve items to display
3521             *
3522             * @returns {*}
3523             */
3524            getDisplayedItems: function () {
3525                var items = this.items;
3526
3527                if (this.expanded === false) {
3528                    items = this.items.slice(0, this.maxSize);
3529                }
3530
3531                if (this.getFulltextSearch()) {
3532                    var searchTokens    = this.slugify(this.getFulltextSearch()).split('-');
3533                    var lastSearchToken = searchTokens.splice(-1, 1)[0];
3534
3535                    items = items.filter(function(item) {
3536                        var isValidItem = true;
3537                        var itemTokens = this.slugify(item.label).split('-');
3538                        searchTokens.forEach(function(currentToken) {
3539                            if (itemTokens.indexOf(currentToken) === -1) {
3540                                isValidItem = false;
3541                            }
3542                        })
3543                        if (isValidItem && lastSearchToken) {
3544                            var ngrams = itemTokens.map(function(token) {return token.substring(0, lastSearchToken.length)});
3545                            isValidItem = ngrams.indexOf(lastSearchToken) !== -1;
3546                        }
3547                        return isValidItem;
3548                    }.bind(this))
3549                }
3550                this.result = items.length;
3551                return items;
3552            },
3553
3554            /**
3555             * Does the search have a result
3556             */
3557            hasSearchResult: function () {
3558                return this.getDisplayedItems().length > 0
3559            },
3560
3561            /**
3562             * Search result message
3563             */
3564            getSearchResultMessage : function() {
3565                return this.noSearchResultLabel.replace("%s", '"' + this.getFulltextSearch() + '"')
3566            },
3567
3568            getFulltextSearch : function() {
3569                return this.fulltextSearch;
3570            },
3571
3572            /**
3573             * Callback for the "Show more" button
3574             */
3575            onShowMore: function () {
3576                if (this.options.hasMoreItems) {
3577                    this.loadAdditionalItems(this.onShowMore.bind(this));
3578                } else {
3579                    this.expanded = true;
3580                }
3581
3582                this.displayedItems = this.getDisplayedItems();
3583            },
3584
3585            /**
3586             * Index the text to be searched.
3587             */
3588            slugify: function(text) {
3589                return text.toString().toLowerCase()
3590                    .replace(/\s+/g, '-')                                              // Replace spaces with -
3591                    .replace(/[^\w\u0400-\u052F\u2DE0-\u2DFF\uA640-\uA69F'\-]+/g, '')  // Remove all non-word chars
3592                    .replace(/\-\-+/g, '-')                                            // Replace multiple - with single -
3593                    .replace(/^-+/, '')                                                // Trim - from start of text
3594            },
3595
3596            /**
3597             * Callback for the "Show less" button
3598             */
3599            onShowLess: function () {
3600                this.expanded = false;
3601                this.displayedItems = this.getDisplayedItems();
3602            },
3603
3604            /**
3605             * Check if the filter can be expanded
3606             *
3607             * @returns {boolean}
3608             */
3609            enableExpansion : function () {
3610                return this.options.hasMoreItems || this.items.length > this.maxSize;
3611            },
3612
3613            /**
3614             * Displays the "Show More" link
3615             *
3616             * @returns {*|boolean}
3617             */
3618            displayShowMore: function () {
3619                return this.enableExpansion() && this.expanded === false && !this.getFulltextSearch();
3620            },
3621
3622            /**
3623             * Displays the "Show Less" link
3624             *
3625             * @returns {*|boolean}
3626             */
3627            displayShowLess: function () {
3628                return this.enableExpansion() && this.expanded === true && !this.getFulltextSearch();
3629            },
3630
3631            /**
3632             * Add an id to items.
3633             */
3634            addItemId: function (item) {
3635                item.id = Math.floor(Math.random() * 1000000) + "_option_";
3636                item.displayProductCount = this.options.displayProductCount && (item.count >= 1);
3637
3638                return item;
3639            },
3640        };
3641    };
3642</script>
vendor: 1 bytes, line 3642
3642
3643<script>
3644    function rangeSlider(options, isPriceSlider) {
3645        "use strict";
3646
3647        return {
3648            options: options,
3649            priceSlider: isPriceSlider,
3650            lastThumbUpdatedMax: true,
3651            minfocused: false,
3652            maxfocused: false,
3653            sliderZindex: 20,
3654            messageTemplates: {
3655                "displayOne": '<span class="msg">1 product</span>',
3656                "displayCount": '<span class="msg"><%- count %> producten</span>',
3657                "displayEmpty": '<span class="msg-error">No items in the current range.</span>'
3658            },
3659            minprice: 0,
3660            maxprice: 0,
3661            fromLabel: '',
3662            toLabel: '',
3663            min: 0,
3664            max: 0,
3665            minZindex: 0,
3666            maxZindex: 0,
3667            minthumb: 0,
3668            maxthumb: 0,
3669            message: '',
3670            url: '',
3671            showAdaptiveSlider: false,
3672            intervals: [],
3673
3674            initialize: function () {
3675                this.options = Object.assign({}, this.options, options);
3676                this.url = this.options.urlTemplate;
3677
3678                if (this.options.sliderZindex !== undefined) {
3679                    this.sliderZindex = this.options.sliderZindex;
3680                }
3681
3682                this.showAdaptiveSlider =  false;
3683                if (this.options.showAdaptiveSlider !== undefined) {
3684                    this.showAdaptiveSlider = this.options.showAdaptiveSlider;
3685                }
3686
3687                if (this.options.maxLabelOffset === undefined) {
3688                    this.options.maxLabelOffset = 0.01;
3689                }
3690
3691                if (this.options.rate === undefined) {
3692                    this.options.rate = 1.0000;
3693                }
3694
3695                if (this.showAdaptiveSlider) {
3696                    this._initAdaptiveSliderValues();
3697                } else {
3698                    this._initSliderValues();
3699                }
3700
3701                this.mintrigger();
3702                this.maxtrigger();
3703            },
3704            _initSliderValues: function () {
3705                this.rate = parseFloat(this.options.rate);
3706                this.minprice = Math.floor(this.options.currentValue.from * this.rate);
3707                this.maxprice = Math.round(this.options.currentValue.to * this.rate);
3708                this.intervals    = this.options.intervals.map(
3709                    function(item) { item.value = Math.round(item.value * this.rate); return item}.bind(this)
3710                );
3711                this.min = Math.floor(this.options.minValue * this.rate);
3712                this.max = Math.round(this.options.maxValue * this.rate);
3713            },
3714
3715            _initAdaptiveSliderValues: function () {
3716                this.intervals = this.options.adaptiveIntervals;
3717                this.options.adaptiveIntervals[0].value = Math.floor(this.options.adaptiveIntervals[0].value);
3718                this.minprice = this._getAdaptiveValue(Number(this.options.currentValue.from));
3719                this.maxprice = this._getAdaptiveValue(Number(this.options.currentValue.to));
3720                this.rate = parseFloat(this.options.rate);
3721                this.intervals = this.intervals.map(
3722                    function(item) { item.originalValue = Math.ceil(item.originalValue * this.rate); return item}.bind(this)
3723                );
3724                this.min = this.intervals[0].value;
3725                this.max = this.intervals[this.intervals.length - 1].value;
3726            },
3727            mintrigger: function () {
3728                this.lastThumbUpdatedMax = false;
3729                this.validation();
3730                this.minprice = this._getClosestAdaptiveValue(Math.min(this.minprice, this.maxprice));
3731                this.minthumb = ((this.minprice - this.min) / (this.max - this.min)) * 100;
3732                this._onChangeAfter();
3733            },
3734            maxtrigger: function () {
3735                this.lastThumbUpdatedMax = true;
3736                this.validation();
3737                this.maxprice = this._getClosestAdaptiveValue(Math.max(this.maxprice, this.minprice));
3738                this.maxthumb = ((this.maxprice - this.min) / (this.max - this.min)) * 100;
3739                this._onChangeAfter();
3740            },
3741            updateFocusState: function (targetName, stateValue) {
3742                this[`${targetName}focused`] = stateValue
3743            },
3744            _onChangeAfter: function () {
3745                this._updateSliderZindexes();
3746                this._applyRange();
3747                this._refreshDisplay();
3748            },
3749            validation: function () {
3750                if (/^[\d.]*$/.test(this.minprice)) {
3751                    if (this.minprice > this.max) {
3752                        this.minprice = this._getAdaptiveValue(Number(this.options.currentValue.from));
3753                    }
3754                    if (this.minprice < this.min) {
3755                        this.minprice = this.min;
3756                    }
3757                } else {
3758                    this.minprice = this._getAdaptiveValue(Number(this.options.currentValue.from));
3759                }
3760                if (/^[\d.]*$/.test(this.maxprice)) {
3761                    if (this.maxprice > this.max) {
3762                        this.maxprice = this.max;
3763                    }
3764                    if (this.maxprice < this.min) {
3765                        this.maxprice = this._getAdaptiveValue(Number(this.options.currentValue.to));
3766                    }
3767                } else {
3768                    this.maxprice = this._getAdaptiveValue(Number(this.options.currentValue.to))
3769                }
3770            },
3771            getItemCount: function () {
3772                var from = this.minprice, to = this.maxprice, intervals = this.intervals;
3773                return intervals.map(function (item) {
3774                    return item.value >= from && item.value < to ? item.count : 0;
3775                })
3776                    .reduce(function (a, b) {
3777                        return a + b;
3778                    });
3779            },
3780            _refreshDisplay: function () {
3781                this.count = this.getItemCount();
3782                let minprice = this._getOriginalValue(this.minprice);
3783                if (minprice >= this._getOriginalValue(this.max) ) {
3784                    minprice = this._getOriginalValue(this.max) - this.options.maxLabelOffset;
3785                }
3786                this.fromLabel = this._formatLabel(minprice);
3787
3788                let maxprice = this._getOriginalValue(this.maxprice) - this.options.maxLabelOffset;
3789                if (maxprice < this._getOriginalValue(this.min)) {
3790                    maxprice = this._getOriginalValue(this.maxprice);
3791                }
3792                this.toLabel = this._formatLabel(maxprice);
3793
3794                var messageTemplate = this.messageTemplates[this.count > 0 ? (this.count > 1 ? 'displayCount' : 'displayOne') : 'displayEmpty'];
3795                this.message = this._unescape(messageTemplate.replace('<%- count %>', this.count));
3796            },
3797            _formatLabel: function (value) {
3798                var formattedValue = value;
3799
3800                if (this.priceSlider && this.options.fieldFormat) {
3801                    formattedValue = this._formatPrice(value);
3802                } else if (this.options.fieldFormat) {
3803                    var pattern = this.options.fieldFormat.pattern || '%s';
3804                    formattedValue = pattern.replace(
3805                        '%s',
3806                        Number.parseFloat(formattedValue).toFixed(this.options.fieldFormat.precision)
3807                    );
3808                }
3809
3810                return formattedValue;
3811            },
3812            _formatPrice: function (value) {
3813                var formatter = new Intl.NumberFormat(
3814                    document.documentElement.lang,
3815                    {
3816                        style: 'currency',
3817                        currency: 'EUR',
3818                    }
3819                );
3820
3821                return formatter.format(value);
3822            },
3823            _applyRange: function () {
3824                var range = {
3825                    from : this._getOriginalValue(this.minprice) * (1 / this.rate),
3826                    to   : this._getOriginalValue(this.maxprice) * (1 / this.rate),
3827                };
3828
3829                this.url = this._unescape(this.options.urlTemplate.replace('<%- from %>', range.from).replace('<%- to %>', range.to));
3830            },
3831            _updateSliderZindexes: function () {
3832                this.minZindex = this.sliderZindex;
3833                this.maxZindex = this.sliderZindex;
3834                if (this.minprice == this.maxprice) {
3835                    if (this.minprice === this.min || (this.minprice !== this.min && this.lastThumbUpdatedMax)) {
3836                        this.maxZindex = this.sliderZindex + 1;
3837                    }
3838
3839                    if (this.maxprice === this.max || (this.maxprice !== this.max && !this.lastThumbUpdatedMax)) {
3840                        this.minZindex = this.sliderZindex + 1;
3841                    }
3842
3843                }
3844            },
3845            _getAdaptiveValue : function (value) {
3846                if (!this.showAdaptiveSlider) {
3847                    return value;
3848                }
3849
3850                var adaptiveValue = this.intervals[0].value;
3851                var found = false;
3852                this.intervals.forEach(function (item) {
3853                    if (found === false && item.originalValue === value) {
3854                        adaptiveValue = item.value;
3855                        found = true;
3856                    }
3857
3858                    if (found === false && item.originalValue < value) {
3859                        adaptiveValue = item.value;
3860                    }
3861                });
3862
3863                return adaptiveValue;
3864            },
3865
3866            _getClosestAdaptiveValue : function (value) {
3867                if (!this.showAdaptiveSlider) {
3868                    return value;
3869                }
3870
3871                var closestValue = this.intervals[0].value;
3872                var found = false;
3873                this.intervals.forEach(function (item) {
3874                    if (item.value === value) {
3875                        closestValue = value;
3876                        found = true;
3877                    }
3878
3879                    if (found === false && item.value < value) {
3880                        closestValue = item.value;
3881                    }
3882                });
3883
3884                return closestValue;
3885            },
3886
3887            _getOriginalValue : function (value) {
3888                if (!this.showAdaptiveSlider) {
3889                    return value;
3890                }
3891
3892                var originalValue = null;
3893                this.intervals.forEach(function (item) {
3894                    if (item.value === value) {
3895                        originalValue = item.originalValue;
3896                    }
3897                });
3898
3899                return originalValue;
3900            },
3901
3902            _unescape: function (str) {
3903                return str.replace(/&lt;%|%3C%/g, '<%').replace(/%&gt;|%%3E/g, '%>');
3904            },
3905        }
3906    }
3907</script>
vendor: 1 bytes, line 3907
3907
3908<script type="text/json" id="default-section-data">
3909    {"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":1790277018},"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 3909
3909
3910<script>
3911    window.processSectionDataBeforeDispatch && (window.processSectionDataBeforeDispatch = new Proxy(window.processSectionDataBeforeDispatch, {
3912        apply(target, thisArg, [data]) {
3913            data.hyva_commerce_messages = data.hyva_commerce_messages || {};
3914            return target.call(thisArg, data);
3915        }
3916    }));
3917</script>
vendor: 1 bytes, line 3917
3917
3918<script>if (!window.__rumvisionSkip) {
3919    window.rumv = window.rumv || function() { (window.rumv.q = window.rumv.q || []).push(arguments) };
3920    (function(rum, vi,si,on) {
3921        var s = JSON.parse( sessionStorage.getItem('rumv') || '{"pageviews":0}' ); s.pageviews++;
3922        if ( s.urls && s.regex && ( s.page = eval('('+s.regex+')')( s.urls, vi.location.pathname ) ) && !s.page.type ) {
3923            return sessionStorage.setItem('rumv', JSON.stringify( s ) );
3924        }
3925
3926        vi.rumv.storage = s;
3927        var head = si.querySelector('head'), js = si.createElement('script');
3928        js.src = 'https://d5yoctgpv4cpx.cloudfront.net/'+rum+'/v5-'+vi.location.hostname+'.js';
3929        head.appendChild(js);
3930    })( 'RUM\u002D409605D6E0', window, document, 'uitgeverijpluim.nl' );
3931}</script>
3931</div>
3931<script nonce="ZTc3MHNqenIwcWZ2NnRtNHpuY2VjbnV2MDhwbWRtMDI=">
3932    yireoGoogleTagManager2Pusher({"version":"3.10.7","page_type":"category","page_path":"https:\/\/uitgeverijpluim.nl\/boeken","page_title":"Boeken  | Uitgeverij Pluim","virtual_page":"\/boeken","breadcrumb":["Boeken"],"user":{"has_transacted":false},"category_size":12,"products":[{"item_id":"De dichter en de duivel - T-shirt","item_sku":"De dichter en de duivel - T-shirt","magento_sku":"De dichter en de duivel - T-shirt","magento_id":"890","item_name":"De dichter en de duivel - T-shirt","item_list_id":"3","item_list_name":"Boeken","price":25,"item_category":"Boeken","item_category2":"Po\u00ebzie","index":1,"quantity":1},{"item_id":"Wij komen van de werkplaats","item_sku":"Wij komen van de werkplaats","magento_sku":"Wij komen van de werkplaats","magento_id":"917","item_name":"Wij komen van de werkplaats","item_list_id":"3","item_list_name":"Boeken","price":21.99,"item_category":"Boeken","item_category2":"Verwacht","index":2,"quantity":1},{"item_id":"Zwijgen is zilver. Kunst en activisme","item_sku":"Zwijgen is zilver. Kunst en activisme","magento_sku":"Zwijgen is zilver. Kunst en activisme","magento_id":"914","item_name":"Zwijgen is zilver. Kunst en activisme","item_list_id":"3","item_list_name":"Boeken","price":21.99,"item_category":"Boeken","item_category2":"Verwacht","index":3,"quantity":1}],"category_id":"3","category_name":"Boeken"}, 'push (initial page) [data-layer.phtml]');
3933        yireoGoogleTagManager2Pusher({"event":"view_item_list","ecommerce":{"items":[{"item_id":"De dichter en de duivel - T-shirt","item_sku":"De dichter en de duivel - T-shirt","magento_sku":"De dichter en de duivel - T-shirt","magento_id":"890","item_name":"De dichter en de duivel - T-shirt","item_list_id":"3","item_list_name":"Boeken","price":25,"item_category":"Boeken","item_category2":"Po\u00ebzie","index":1,"quantity":1},{"item_id":"Wij komen van de werkplaats","item_sku":"Wij komen van de werkplaats","magento_sku":"Wij komen van de werkplaats","magento_id":"917","item_name":"Wij komen van de werkplaats","item_list_id":"3","item_list_name":"Boeken","price":21.99,"item_category":"Boeken","item_category2":"Verwacht","index":2,"quantity":1},{"item_id":"Zwijgen is zilver. Kunst en activisme","item_sku":"Zwijgen is zilver. Kunst en activisme","magento_sku":"Zwijgen is zilver. Kunst en activisme","magento_id":"914","item_name":"Zwijgen is zilver. Kunst en activisme","item_list_id":"3","item_list_name":"Boeken","price":21.99,"item_category":"Boeken","item_category2":"Verwacht","index":3,"quantity":1}]}}, 'push (page-based event) [data-layer.phtml]');
3934    </script>
vendor: 1 bytes, line 3934
3934
3935<script nonce="ZTc3MHNqenIwcWZ2NnRtNHpuY2VjbnV2MDhwbWRtMDI=">
3936    function yireoGoogleTagManager2FindParentElementWithName(element, parentTagName) {
3937        if (!element.parentElement) {
3938            return null;
3939        }
3940
3941        if (element.parentElement.tagName.toLowerCase() === parentTagName) {
3942            return element.parentElement;
3943        }
3944
3945        return yireoGoogleTagManager2FindParentElementWithName(element.parentElement, parentTagName);
3946    }
3947
3948    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');
3949    if (products && products.length > 0) {
3950        products.forEach(function(product) {
3951            product.addEventListener('click', function(event, s) {
3952                let parentElement = yireoGoogleTagManager2FindParentElementWithName(event.target, 'form');
3953                if (!parentElement) {
3954                    return;
3955                }
3956
3957                const inputElement = parentElement.querySelector('input[name="product"]');
3958                const productId = inputElement.value;
3959                const productData = window['YIREO_GOOGLETAGMANAGER2_PRODUCT_DATA_ID_' + productId] || {};
3960                productData.item_id = productId;
3961
3962                const eventData = {
3963                    'event': 'select_item',
3964                    'ecommerce': {
3965                        'items': [productData]
3966                    }
3967                }
3968
3969                yireoGoogleTagManager2Pusher(eventData, 'push (page event "select_item") [script-product-clicks.phtml]');
3970
3971                if (window['YIREO_GOOGLETAGMANAGER2_DEBUG_CLICKS'] && confirm("Press to continue with redirect") === false) {
3972                    event.preventDefault();
3973                }
3974            });
3975        })
3976    }
3977</script>
vendor: 1 bytes, line 3977
3977
3978<script nonce="ZTc3MHNqenIwcWZ2NnRtNHpuY2VjbnV2MDhwbWRtMDI=">
3979    window.addEventListener('private-content-loaded', function(event) {
3980        window.dataLayer = window.dataLayer || [];
3981
3982        var isEmpty = function (variable) {
3983            if (typeof variable === 'undefined') {
3984                return true;
3985            }
3986
3987            if (Array.isArray(variable) && variable.length === 0) {
3988                return true;
3989            }
3990
3991            return typeof variable === 'object' && Object.keys(variable).length === 0;
3992        }
3993
3994        var getSectionNames = function () {
3995            return ['cart', 'customer'];
3996        }
3997
3998        getSectionNames().forEach(function (sectionName) {
3999            if (!event.detail.data[sectionName].gtm) {
4000                return;
4001            }
4002
4003            const attributes = event.detail.data[sectionName].gtm;
4004            if (!Object.keys(attributes).length > 0) {
4005                return;
4006            }
4007
4008            yireoGoogleTagManager2Pusher(attributes, 'push (attributes) [script-additions.phtml]');
4009        });
4010
4011        let attributes = {};
4012        getSectionNames().forEach(function (sectionName) {
4013            if (!event.detail.data[sectionName].gtm_events) {
4014                return;
4015            }
4016
4017            const gtmEvents = event.detail.data[sectionName].gtm_events;
4018            for (const [eventId, eventData] of Object.entries(gtmEvents)) {
4019                yireoGoogleTagManager2Logger('found event in section "' + sectionName + '"', eventData);
4020
4021                const metaData = eventData.meta;
4022                yireoGoogleTagManager2Pusher(eventData, 'push (customerData "' + sectionName + '"  event "' + eventId + '")');
4023
4024                if (!metaData || metaData.cacheable !== true) {
4025                    yireoGoogleTagManager2Logger('removing event "' + eventId + '" from section "' + sectionName + '"')
4026
4027                    let mageCacheStorage = event.detail.data;
4028
4029                    if (mageCacheStorage !== null
4030                        && typeof mageCacheStorage[sectionName] !== 'undefined'
4031                        && typeof mageCacheStorage[sectionName].gtm_events !== 'undefined'
4032                        && eventId in mageCacheStorage[sectionName].gtm_events) {
4033                        delete mageCacheStorage[sectionName].gtm_events[eventId];
4034                    }
4035
4036                    window.localStorage.setItem('mage-cache-storage', JSON.stringify(mageCacheStorage));
4037                }
4038            }
4039        });
4040
4041        if (false === isEmpty(attributes)) {
4042            yireoGoogleTagManager2Pusher(attributes, 'dataLayer (initial js) [script-additions.phtml]');
4043        }
4044    });
4045
4046    window.addEventListener('toggle-cart', (event) => {
4047        if (!event.detail || !event.detail.isOpen) {
4048            return;
4049        }
4050
4051        window.dispatchEvent(new CustomEvent('minicart_collapse'));
4052    });
4053</script>
4053
4054</body>
4055</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.