;(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 = `
`
// 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)
}
})()