PageSourceSearch

https://febevex.be/assets/js/memberDirectory.js?v=Tqg9pmofpM6IUKaa

js febevex.be collected 2026-10-07 21:21:35 UTC 15,250 bytes, 379 lines download raw bytes

1(function () {
2    const root = document.querySelector("[data-member-directory]");
3
4    if (!root) {
5        return;
6    }
7
8    const expertiseLabels = {
9        residentieelVastgoed: "Residentieel vastgoed",
10        zonevreemdVastgoed: "Zonevreemd vastgoed",
11        bedrijfsmatigVastgoed: "Bedrijfsmatig vastgoed",
12        commercieelVastgoed: "Commercieel vastgoed",
13        industrieelVastgoed: "Industrieel vastgoed",
14        sevesoVastgoed: "Seveso vastgoed",
15        agrarischVastgoed: "Agrarisch vastgoed",
16        bijzonderVastgoed: "Bijzonder vastgoed",
17        aanvangEnEindeHuur: "Aanvang en einde huur",
18        aanvangEnEindeWerken: "Aanvang en einde werken",
19        verkoopVanVastgoed: "Verkoop van vastgoed",
20        opleveringVanGebouwen: "Oplevering van gebouwen",
21        brandschade: "Brandschade",
22        bouwschade: "Bouwschade",
23        verzekeringen: "Verzekeringen",
24        syndicusMedeEigendom: "Syndicus mede-eigendom",
25        beheerderOG: "Beheerder OG",
26        rentmeester: "Rentmeester",
27        energiedeskundige: "Energiedeskundige",
28        asbestdeskundige: "Asbestdeskundige inventarisatie",
29        veiligheidscoordinator: "Veiligheidscoördinator"
30    };
31    const form = root.querySelector("[data-member-filters]");
32    const nameFilter = root.querySelector("[data-member-name]");
33    const locationFilter = root.querySelector("[data-member-location]");
34    const expertiseFilter = root.querySelector("[data-member-expertise]");
35    const loadingState = root.querySelector("[data-member-loading]");
36    const errorState = root.querySelector("[data-member-error]");
37    const emptyState = root.querySelector("[data-member-empty]");
38    const results = root.querySelector("[data-member-results]");
39    const mapElement = root.querySelector("[data-member-map]");
40    const mapStatus = root.querySelector("[data-member-map-status]");
41    const filters = [nameFilter, locationFilter, expertiseFilter];
42    let members = [];
43    let memberLocations = {};
44    let map;
45    let markerLayer;
46
47    function value(valueToRead) {
48        return valueToRead === null || valueToRead === undefined ? "" : String(valueToRead).trim();
49    }
50
51    function element(tagName, className, text) {
52        const node = document.createElement(tagName);
53
54        if (className) {
55            node.className = className;
56        }
57        if (text !== undefined) {
58            node.textContent = text;
59        }
60
61        return node;
62    }
63
64    function contactIcon(type) {
65        const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
66        const paths = {
67            email: ["M3 5h18v14H3z", "m4 8 5 4 5-4"],
68            phone: ["M7 3H4a1 1 0 0 0-1 1c0 9.4 7.6 17 17 17a1 1 0 0 0 1-1v-3l-4-1-1 2c-4.3-1.2-7.8-4.7-9-9l2-1-2-5Z"],
69            website: ["M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18Z", "M3 12h18", "M12 3c2.2 2.5 3.3 5.5 3.3 9S14.2 18.5 12 21c-2.2-2.5-3.3-5.5-3.3-9S9.8 5.5 12 3Z"]
70        };
71
72        svg.setAttribute("viewBox", "0 0 24 24");
73        svg.setAttribute("aria-hidden", "true");
74        paths[type].forEach(function (pathData) {
75            const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
76            path.setAttribute("d", pathData);
77            svg.append(path);
78        });
79        return svg;
80    }
81
82    function addDetail(container, label, detail) {
83        if (!detail) {
84            return;
85        }
86
87        const row = element("p", "fb-member-card__detail");
88        row.append(element("strong", "fb-member-card__detail-label", label + ": "));
89        row.append(document.createTextNode(detail));
90        container.append(row);
91    }
92
93    function websiteUrl(website) {
94        const enteredUrl = value(website);
95
96        if (!enteredUrl) {
97            return null;
98        }
99
100        try {
101            const parsedUrl = new URL(/^https?:\/\//i.test(enteredUrl) ? enteredUrl : "https://" + enteredUrl);
102            return parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:" ? parsedUrl.href : null;
103        } catch (error) {
104            return null;
105        }
106    }
107
108    function renderAdditions(member, card) {
109        const additions = member.vastgoedtechnicus_additions;
110
111        if (!additions || typeof additions !== "object" || Array.isArray(additions)) {
112            return;
113        }
114
115        const entries = Object.entries(additions).filter(function (entry) {
116            return Array.isArray(entry[1]) && entry[1].some(value);
117        });
118
119        if (!entries.length) {
120            return;
121        }
122
123        const section = element("section", "fb-member-card__additions");
124        section.append(element("h4", "fb-member-card__subtitle", "Aanvullende gegevens"));
125
126        entries.forEach(function (entry) {
127            const group = element("div", "fb-member-card__addition");
128            const list = element("ul", "fb-member-card__addition-list");
129            group.append(element("h5", "fb-member-card__addition-title", value(entry[0])));
130            entry[1].forEach(function (item) {
131                const itemText = value(item);
132                if (itemText) {
133                    list.append(element("li", "fb-member-card__addition-item", itemText));
134                }
135            });
136            group.append(list);
137            section.append(group);
138        });
139
140        card.append(section);
141    }
142
143    function renderMember(member) {
144        const item = element("li", "fb-member-directory__item");
145        const card = element("article", "fb-member-card");
146        const heading = element("h2", "fb-member-card__name", [value(member.firstName), value(member.lastName)].filter(Boolean).join(" "));
147        const memberKey = value(member.email).toLowerCase();
148
149        item.id = "member-" + memberKey.replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
150
151        if (member.credited === true) {
152            heading.append(element("span", "fb-member-card__credited", " ✓ Gecrediteerd"));
153        }
154        card.append(heading);
155
156        const details = element("div", "fb-member-card__details");
157        addDetail(details, "Kantoor", value(member.company));
158
159        const street = [value(member.street), value(member.streetNumber), value(member.streetnumberaddition)].filter(Boolean).join(" ");
160        const locality = [value(member.zipcode), value(member.city)].filter(Boolean).join(" ");
161        addDetail(details, "Adres", street);
162        addDetail(details, "Plaats", locality);
163        addDetail(details, "Provincie", value(member.province));
164        card.append(details);
165
166        const expertise = Object.keys(expertiseLabels).filter(function (key) {
167            return member[key] === true;
168        });
169
170        if (expertise.length) {
171            const expertiseSection = element("section", "fb-member-card__expertise");
172            const expertiseList = element("ul", "fb-member-card__expertise-list");
173            expertiseSection.append(element("h3", "fb-member-card__subtitle", "Expertises"));
174            expertise.forEach(function (key) {
175                expertiseList.append(element("li", "fb-member-card__expertise-item", expertiseLabels[key]));
176            });
177            expertiseSection.append(expertiseList);
178            card.append(expertiseSection);
179        }
180
181        renderAdditions(member, card);
182
183        const contact = element("div", "fb-member-card__contact");
184        const email = value(member.email);
185        const telephone = value(member.telephone);
186        const website = value(member.website);
187        const safeWebsiteUrl = websiteUrl(website);
188
189        if (email) {
190            const emailLink = element("a", "fb-member-card__link fb-member-card__link--email", email);
191            emailLink.href = "mailto:" + email;
192            emailLink.setAttribute("aria-label", "Stuur een e-mail naar " + email);
193            emailLink.prepend(contactIcon("email"));
194            contact.append(emailLink);
195        }
196        if (telephone) {
197            const telephoneLink = element("a", "fb-member-card__link fb-member-card__link--phone", telephone);
198            telephoneLink.href = "tel:" + telephone;
199            telephoneLink.setAttribute("aria-label", "Bel " + telephone);
200            telephoneLink.prepend(contactIcon("phone"));
201            contact.append(telephoneLink);
202        }
203        if (safeWebsiteUrl) {
204            const websiteLink = element("a", "fb-member-card__link fb-member-card__link--website", website);
205            websiteLink.href = safeWebsiteUrl;
206            websiteLink.target = "_blank";
207            websiteLink.rel = "noopener";
208            websiteLink.setAttribute("aria-label", "Bezoek de website van " + value(member.company || member.firstName));
209            websiteLink.prepend(contactIcon("website"));
210            contact.append(websiteLink);
211        } else if (website) {
212            contact.append(element("span", "fb-member-card__website", website));
213        }
214        if (contact.childNodes.length) {
215            card.append(contact);
216        }
217
218        item.append(card);
219        return item;
220    }
221
222    function filteredMembers() {
223        const requestedName = nameFilter.value.toLowerCase();
224        const requestedLocation = locationFilter.value.toLowerCase();
225        const requestedExpertise = expertiseFilter.value;
226
227        return members.filter(function (member) {
228            const fullName = (value(member.firstName) + " " + value(member.lastName)).toLowerCase();
229            const hasName = !requestedName || fullName.includes(requestedName);
230            const hasLocation = !requestedLocation || value(member.province).toLowerCase() === requestedLocation;
231            const hasExpertise = !requestedExpertise || member[requestedExpertise] === true;
232            return hasName && hasLocation && hasExpertise;
233        });
234    }
235
236    function memberLocation(member) {
237        return memberLocations[value(member.email).toLowerCase()];
238    }
239
240    function renderMap(visibleMembers) {
241        if (!map || !markerLayer) {
242            return;
243        }
244
245        markerLayer.clearLayers();
246        const bounds = [];
247        let markerCount = 0;
248
249        visibleMembers.forEach(function (member) {
250            const location = memberLocation(member);
251
252            if (!location || !Number.isFinite(location.latitude) || !Number.isFinite(location.longitude)) {
253                return;
254            }
255
256            const coordinates = [location.latitude, location.longitude];
257            const marker = window.L.circleMarker(coordinates, {
258                radius: 8,
259                weight: 2,
260                color: "#ffffff",
261                fillColor: "#4338ca",
262                fillOpacity: 0.9
263            });
264            const popup = element("div", "fb-member-map-popup");
265            const name = [value(member.firstName), value(member.lastName)].filter(Boolean).join(" ");
266            const address = [
267                [value(member.street), value(member.streetNumber)].filter(Boolean).join(" "),
268                [value(member.zipcode), value(member.city)].filter(Boolean).join(" ")
269            ].filter(Boolean).join(", ");
270            const profileLink = element("a", "fb-member-map-popup__link", "Bekijk contactgegevens");
271
272            popup.append(element("strong", "fb-member-map-popup__name", name));
273            if (value(member.company)) {
274                popup.append(element("span", "fb-member-map-popup__company", value(member.company)));
275            }
276            popup.append(element("span", "fb-member-map-popup__address", address));
277            profileLink.href = "#member-" + value(member.email).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
278            popup.append(profileLink);
279            marker.bindPopup(popup);
280            marker.addTo(markerLayer);
281            bounds.push(coordinates);
282            markerCount += 1;
283        });
284
285        if (bounds.length) {
286            map.fitBounds(bounds, {padding: [35, 35], maxZoom: 11});
287        } else {
288            map.setView([50.85, 4.55], 7);
289        }
290
291        mapStatus.textContent = markerCount === 1 ? "1 vastgoedexpert op de kaart" : markerCount + " vastgoedexperten op de kaart";
292    }
293
294    function initializeMap() {
295        if (!mapElement || !window.L) {
296            mapStatus.textContent = "De kaart kon niet worden geladen.";
297            return;
298        }
299
300        map = window.L.map(mapElement, {scrollWheelZoom: false}).setView([50.85, 4.55], 7);
301        window.L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
302            maxZoom: 19,
303            attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
304        }).addTo(map);
305        markerLayer = window.L.layerGroup().addTo(map);
306        renderMap(filteredMembers());
307    }
308
309    function render() {
310        const visibleMembers = filteredMembers();
311        const fragment = document.createDocumentFragment();
312
313        visibleMembers.forEach(function (member) {
314            fragment.append(renderMember(member));
315        });
316        results.replaceChildren(fragment);
317        emptyState.hidden = visibleMembers.length !== 0;
318        renderMap(visibleMembers);
319    }
320
321    function setFiltersDisabled(disabled) {
322        filters.forEach(function (filter) {
323            filter.disabled = disabled;
324        });
325    }
326
327    form.addEventListener("submit", function (event) {
328        event.preventDefault();
329    });
330    nameFilter.addEventListener("input", render);
331    locationFilter.addEventListener("change", render);
332    expertiseFilter.addEventListener("change", render);
333
334    setFiltersDisabled(true);
335
336    fetch(root.dataset.membersUrl, {headers: {Accept: "application/json"}})
337        .then(function (response) {
338            if (!response.ok) {
339                throw new Error("Members request failed with status " + response.status);
340            }
341            return response.json();
342        })
343        .then(function (payload) {
344            const memberData = payload && payload.data && payload.data.users_user_data;
345
346            if (!Array.isArray(memberData)) {
347                throw new Error("Members response has an invalid structure");
348            }
349
350            members = memberData.filter(function (member) {
351                return member && typeof member === "object" && !Array.isArray(member);
352            });
353            loadingState.hidden = true;
354            results.setAttribute("aria-busy", "false");
355            setFiltersDisabled(false);
356            render();
357
358            return fetch(root.dataset.memberLocationsUrl, {headers: {Accept: "application/json"}});
359        })
360        .then(function (response) {
361            if (!response.ok) {
362                throw new Error("Member locations request failed with status " + response.status);
363            }
364            return response.json();
365        })
366        .then(function (payload) {
367            memberLocations = payload && payload.locations ? payload.locations : {};
368            initializeMap();
369        })
370        .catch(function () {
371            if (!members.length) {
372                loadingState.hidden = true;
373                errorState.hidden = false;
374                results.setAttribute("aria-busy", "false");
375            } else {
376                mapStatus.textContent = "De kaart kon niet worden geladen.";
377            }
378        });
379}());

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.