Files
moodo.pl_geocoding/salony/widget.js

610 lines
17 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
;(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 = `
<div class="mapFull">
<div id="map"></div>
</div>
<div class="mapResults">
<h1>ZNAJDŹ SALON</h1>
<div class="searchCont">
<input id="tinp" type="text" placeholder="ZNAJDŹ SKLEP">
<ul class="suggestList">
<li class="firstSuggest"></li>
</ul>
<label for="tinp">Wpisz Miasto bądź województwo</label>
<button id="search">
<i class="fas fa-search" style="min-height: 14px;"></i>
</button>
</div>
<p class="resultQ">
WYNIKI: <span class="dynamic">0</span>
</p>
<ul id="shopList">
<li class="forCloning place">
<input type="radio" name="shopPlace">
<div class="shopHiddenInfo">
<p class="lat"></p>
<p class="lng"></p>
</div>
<div class="content">
<h2 class="shopAddress"></h2>
<p>GODZINY OTWARCIA:</p>
<p class="shopHours"></p>
<a class="link googleLink" target="_blank" rel="noopener noreferrer">
<i class="fas fa-location-arrow"></i>
Wskazówki dojazdu
</a>
<a class="link moodoLink" target="_blank" rel="noopener noreferrer">
<i class="fas fa-store"></i>
Strona salonu
</a>
</div>
</li>
</ul>
</div>
`
// Add plugins
root.innerHTML += `
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css">
`
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 = '<li class="firstSuggest"></li>'
// 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)
}
})()