;(function () { console.log('Salony widget loaded ....') const API_URL = 'https://serwer1852487.home.pl/Geo/api/salony/' const root = document.getElementById('salony-widget') const initialCity = root.dataset.city || null if (!root) return root.innerHTML = `

ZNAJDŹ SALON

WYNIKI: 0

` // Add plugins root.innerHTML += ` ` injectStyles() let map let markers = [] let places = [] var iconB = 'https://moodo.pl/data/include/cms/salony-1/mark2.png' var iconW = 'https://moodo.pl/data/include/cms/salony-1/markw2.png' fetch(API_URL) .then((r) => r.json()) .then((data) => { places = data // setTimeout(() => { // initMap() // buildSuggestList() // // renderList(data) // renderMarkers(data) // }, 200) setTimeout(() => { initMap() buildSuggestList() renderMarkers(data) console.log('Initial city:', initialCity) if (initialCity) { const type = detectFilterType(initialCity) document.getElementById('tinp').value = initialCity filterList(initialCity, type) activateFirstResult() } }, 200) }) function prepareToCompare(str) { return convertSpecialsCharacters(str.toString().toLowerCase().trim()) } function convertSpecialsCharacters(str) { const conversions = {} conversions.a = 'ą' conversions.e = 'ę' conversions.z = 'ż|ź' conversions.s = 'ś' conversions.c = 'ć' conversions.l = 'ł' conversions.o = 'ó|ò' conversions.A = 'Ą' conversions.E = 'Ę' conversions.Z = 'Ż|Ź' conversions.S = 'Ś' conversions.C = 'Ć' conversions.L = 'Ł' conversions.O = 'Ó|Ò' for (const i in conversions) { const re = new RegExp(conversions[i], 'g') str = str.replace(re, i) } return str } // ---------------- MAP ---------------- function initMap() { map = new google.maps.Map(document.getElementById('map'), { zoom: 6, center: { lat: 52.1, lng: 19.4 }, mapTypeId: 'roadmap', }) } function renderMarkers(data) { data.forEach((place) => { place.shops.forEach((shop) => { if (!shop.lat || !shop.lng) return const marker = new google.maps.Marker({ map, icon: iconB, position: { lat: +shop.lat, lng: +shop.lng }, }) marker.shopLat = +shop.lat marker.shopLng = +shop.lng marker.addListener('click', () => { map.setZoom(16) map.panTo(marker.getPosition()) filterList(place.name) highlightMarker(marker) }) markers.push(marker) }) }) } function highlightMarker(selectedMarker) { markers.forEach((marker) => { marker.setIcon(marker === selectedMarker ? iconW : iconB) }) } // ---------------- LIST ---------------- function renderList(data) { const list = document.getElementById('shopList') list .querySelectorAll('.place:not(.forCloning)') .forEach((el) => el.remove()) let count = 0 data.forEach((place) => { place.shops.forEach((shop) => { const clone = list.querySelector('.forCloning').cloneNode(true) clone.classList.remove('forCloning') clone.querySelector('.lat').textContent = shop.lat clone.querySelector('.lng').textContent = shop.lng clone.querySelector('.shopAddress').textContent = `${place.name} - ${ shop.address || '' }` clone.querySelector('.shopHours').textContent = shop.openHours || '—' // GOOGLE LINK const googleLink = clone.querySelector('.googleLink') if (shop.urlAddress) { googleLink.href = shop.urlAddress } else { googleLink.remove() } // MOODO LINK const moodoLink = clone.querySelector('.moodoLink') if (shop.shopUrl) { moodoLink.href = shop.shopUrl } else { moodoLink.remove() } clone.addEventListener('click', (e) => { map.setZoom(16) map.panTo({ lat: +shop.lat, lng: +shop.lng }) document .querySelectorAll('#shopList .place.active') .forEach((el) => el.classList.remove('active')) const ancestor = e.target.closest('.place') ancestor.classList.add('active') ancestor.getElementsByTagName('INPUT')[0].checked = true const markerToHighlight = markers.find( (m) => m.shopLat === +shop.lat && m.shopLng === +shop.lng ) if (markerToHighlight) highlightMarker(markerToHighlight) }) list.appendChild(clone) count++ }) }) document.querySelector('.dynamic').textContent = count document.querySelector('.resultQ').classList.add('active') } // ---------------- SEARCH ---------------- // function buildSuggestList() { // const ul = document.querySelector('.suggestList') // places.forEach((p) => { // const li = document.createElement('li') // li.textContent = p.name // li.onclick = () => { // document.getElementById('tinp').value = p.name // filterList(p.name) // } // ul.appendChild(li) // }) // } function buildSuggestList() { const ul = document.querySelector('.suggestList') ul.innerHTML = '
  • ' // 1️⃣ WOJEWÓDZTWA getUniqueWojewodztwa().forEach((woj) => { const li = document.createElement('li') li.textContent = woj li.dataset.type = 'woj' li.onclick = () => { document.getElementById('tinp').value = woj filterList(woj, 'woj') } ul.appendChild(li) }) // 2️⃣ MIASTA places.forEach((p) => { const li = document.createElement('li') li.textContent = p.name li.dataset.type = 'city' li.onclick = () => { document.getElementById('tinp').value = p.name filterList(p.name, 'city') } ul.appendChild(li) }) } function filterList(val, type = 'city') { const query = prepareToCompare(val) let result = [] if (type === 'woj') { result = places.filter((p) => prepareToCompare(p.woj) === query) } else { // exact city result = places.filter((p) => prepareToCompare(p.name) === query) // fallback (np. wpisywanie) if (!result.length) { result = places.filter((p) => prepareToCompare(p.name).includes(query)) } } renderList(result) // 🗺️ CENTRUJ MAPĘ (WOJ + MIASTO) fitMapToPlaces(result) } function filterAvailablePlaces(actualVal) { let listElements = document.querySelectorAll('.suggestList li') for (const element of listElements) { if ( !prepareToCompare(element.innerText).includes( prepareToCompare(actualVal) ) ) { element.style.display = 'none' } else { element.style.display = 'block' } } } const searchInp = document.getElementById('tinp') searchInp.onfocus = function (e) { e.target.closest('.searchCont').classList.add('active') } searchInp.addEventListener('focusout', function (e) { let targ = e.target setTimeout(function () { targ.closest('.searchCont').classList.remove('active') }, 200) }) searchInp.onkeyup = function (e) { if (e.keyCode == 13) { document.getElementById('search').click() } else { filterAvailablePlaces(searchInp.value) } } document.getElementById('search').onclick = () => { filterList(document.getElementById('tinp').value) } // ---------------- HELPERS ---------------- function activateFirstResult() { const first = document.querySelector('#shopList .place:not(.forCloning)') if (!first) return first.classList.add('active') first.querySelector('input').checked = true const lat = +first.querySelector('.lat').textContent const lng = +first.querySelector('.lng').textContent map.setZoom(16) map.panTo({ lat, lng }) const marker = markers.find((m) => m.shopLat === lat && m.shopLng === lng) if (marker) highlightMarker(marker) } function getUniqueWojewodztwa() { const set = new Set() places.forEach((p) => { if (p.woj) set.add(p.woj) }) return Array.from(set).sort() } function detectFilterType(val) { const q = prepareToCompare(val) const isWoj = places.some((p) => prepareToCompare(p.woj) === q) return isWoj ? 'woj' : 'city' } function fitMapToPlaces(placesArr) { if (!placesArr || !placesArr.length) return const bounds = new google.maps.LatLngBounds() let hasPoints = false placesArr.forEach((place) => { place.shops.forEach((shop) => { if (shop.lat && shop.lng) { bounds.extend({ lat: +shop.lat, lng: +shop.lng, }) hasPoints = true } }) }) if (hasPoints) { map.fitBounds(bounds) setTimeout(() => { if (map.getZoom() > 12) map.setZoom(12) }, 0) } } // ---------------- STYLES ---------------- function injectStyles() { const style = document.createElement('style') style.innerHTML = ` #salony-widget *{ margin: 0; padding: 0; box-sizing: border-box; } #salony-widget{ min-height: 40vw; display: flex; flex-direction: row; } #salony-widget .mapFull { flex-basis:65%; min-height:40vw; } #salony-widget .mapFull #map{ filter: grayscale(100%); height: 100%; width: 100%; } #salony-widget .mapResults { padding: 0 16px 0 48px; flex-grow: 0; flex-shrink: 0; flex-basis: 35%; } #salony-widget .mapResults h1 { color: #333; font-family: 'Raleway', sans-serif; font-size: 29px; font-weight: 800; line-height: 1.3; margin: 0; } #salony-widget .mapResults .searchCont { margin: 18px 0; display: flex; align-items: center; justify-content: space-between; position: relative; max-width: 340px; border: 2px solid rgba(0, 0, 0, .7); } #salony-widget .mapResults .searchCont input{ font-family: 'Raleway', sans-serif; border: none; font-size: 14px; outline: none; padding: 10px; color: #333; width: calc(100% - 40px); height: 40px; box-shadow: none; border-radius: 0; } #salony-widget .mapResults .searchCont label{ font-family: 'Raleway', sans-serif; font-size: 12px; font-weight: 800; position: absolute; color: rgba(0, 0, 0, .6); top: -10px; padding: 2px; background: #fff; left: 4px; transform: scale(0); opacity: 0; transition: 0.5s; } #salony-widget .mapResults .searchCont button{ cursor: pointer; border: none; width: 40px; height: 40px; outline: none; display: inline-flex; align-items: center; justify-content: center; background: #fff; } #salony-widget .mapResults .searchCont .suggestList { position: absolute; box-shadow: 2px 3px 10px rgba(0, 0, 0, .3); top: 20px; border-radius: 3px; padding: 0 !important; max-height: 100px; z-index: 10; overflow: auto; width: 100%; background: #fff; display: none; margin: 20px 0; list-style-type: none; } #salony-widget .mapResults .searchCont.active .suggestList { display: block; } #salony-widget .mapResults .searchCont .suggestList li { font-family: 'Raleway', sans-serif; font-size: 13px; cursor: pointer; padding: 5px; transition: 0.3s; align-items: flex-start; } #salony-widget .mapResults .searchCont .suggestList li:hover { background: #f2f2f2; } #salony-widget .mapResults .searchCont .suggestList li.firstSuggest { padding: 0px; } #salony-widget .mapResults .searchCont label { font-family: 'Raleway', sans-serif; font-size: 12px; font-weight: 800; position: absolute; color: rgba(0, 0, 0, .6); top: -10px; padding: 2px; background: #fff; left: 4px; transform: scale(0); opacity: 0; transition: 0.5s; } #salony-widget .mapResults .searchCont.active label { transform: scale(1); opacity: 1; } #salony-widget .mapResults .resultQ { visibility: hidden; color: #333; font-family: 'Raleway', sans-serif; font-weight: 300; font-size: 14px; } #salony-widget .mapResults .resultQ.active { visibility: visible; } #salony-widget .mapResults #shopList { padding: 0 5px; margin: 20px 0; list-style-type: none; max-height: 360px; overflow: auto; } #salony-widget .mapResults #shopList li.place.forCloning { display: none; } #salony-widget .mapResults #shopList li.place { display: flex; align-items: flex-start; padding: 10px 0; cursor: pointer; } #salony-widget .mapResults #shopList li.place a { font-family: 'Raleway', sans-serif; margin-top: 8px; display: none; font-size: 13px; color: #000; text-decoration: none; } #salony-widget .mapResults #shopList li.place.active a { display: block; } #salony-widget .mapResults #shopList li.place input{ margin-right: 10px; position: relative; top: 6px; color: #000; } #salony-widget .mapResults #shopList li.place .shopHiddenInfo{ display: none; } #salony-widget .mapResults #shopList li.place .content h2{ color: #333; line-height: 1.3; font-family: 'Raleway', sans-serif; font-weight: 500; font-size: 14px; } #salony-widget .mapResults #shopList li.place .content p{ color: #333; font-family: 'Raleway', sans-serif; line-height: 1.3; font-weight: 300; font-size: 14px; } @media(max-width: 978px) { #salony-widget{ flex-direction: column; row-gap: 20px; } #salony-widget .mapResults{ padding-left: 16px; } #salony-widget .mapFull #map{ min-height: 40vw; } } ` document.head.appendChild(style) } })()