class Search {
constructor() {
this.searchDebounced = _.debounce((query) => {
if (query.length < 3)
return;
fetch(`/hints.ajax?q=${encodeURIComponent(query)}`)
.then(response => {
if (!response.ok)
throw new Error(`statusText is ${response.statusText}`);
return response.json();
})
.then(({response, error}) => {
this.unlockButton();
if (error)
throw new Error(error);
this.autoComplete.setData(response.map(item => {
return {label: item, value: ''};
}));
this.autoComplete.renderIfNeeded();
})
}, 150);
this._filterDebounced = _.debounce((e) => {
let filter = e.target;
globalMaps.setFilter(filter.value);
}, 500)
let field = document.getElementById('queryInput');
let btn = document.getElementById('querySubmit');
let filterField = document.getElementById('filterInput');
this.autoComplete = new Autocomplete(field, {
data: [],
maximumItems: 10,
onInput: (value) => {
this.searchDebounced(value);
},
onSelectItem: ({label}) => {
// console.log('selected:', label)
},
highlightClass: 'text-danger'
});
btn.addEventListener('click', this.onSubmit);
field.addEventListener('keydown', this.onInputKeyDown);
filterField.addEventListener('input', this._filterDebounced);
this.btn = btn;
this.field = field;
}
onInputKeyDown = (e) => {
if (e.keyCode === 10 || e.keyCode === 13)
this.onSubmit();
}
getOffers(query, page) {
fetch(`/offers.ajax?q=${encodeURIComponent(this.field.value)}&page=${page}`)
.then(response => {
if (!response.ok)
throw new Error(`statusText is ${response.statusText}`);
return response.json();
})
.then(({error, tradeNames, end, offers, pages}) => {
if (error)
throw new Error(error);
if (tradeNames) {
this.autoComplete.setData(tradeNames.map(item => {
return {label: item, value: ''};
}));
this.autoComplete.renderIfNeeded();
return this.unlockButton();
}
for (let offer of offers)
globalMaps.addOffer(offer);
if (page >= pages) {
return this.unlockButton();
} else {
this.lockButton(pages > 1 ? `${page} из ${pages}` : null);
setTimeout(() => {
this.getOffers(query, page + 1);
}, 1000)
}
})
.catch((error) => {
console.error(error);
alert(error);
this.unlockButton();
})
}
onSubmit = (e) => {
if (this.isLocked())
return;
this.lockButton('Загрузка...');
globalMaps.removeAllPoints();
this.getOffers(this.field.value, 1);
}
isLocked() {
return this.btn.classList.contains('disabled');
}
lockButton(text) {
if (text !== null)
this.btn.innerText = text;
this.btn.classList.add('disabled');
}
unlockButton() {
this.btn.classList.remove('disabled');
this.btn.innerText = 'Поиск';
}
}
class Maps {
constructor() {
/**
* @type {ymaps.Map}
*/
this.map = null;
ymaps.ready(this.onInit);
this.filter = null;
this.places = {};
}
onInit = () => {
this.map = new ymaps.Map("mapContainer", {
center: [59.94, 30.32],
zoom: 11
});
this.map.controls.remove('searchControl');
}
addPoint({geo, offersRef, hint, pharmacyName, pharmacyAddress, pharmacyPhone}) {
let mark = new ymaps.Placemark(geo, {
hintContent: hint,
}, {
preset: 'islands#dotIcon',
openEmptyBalloon: true,
iconColor: '#3caa3c'
});
mark.events.add('balloonopen', e => {
let lines = offersRef.map(offer => {
return `${offer.name} (${offer.price} руб.)`
});
let html = `${pharmacyName}
`;
html += `${pharmacyAddress}
`;
html += `тел: ${pharmacyPhone}
`;
html += lines.join('
');
mark.properties.set('balloonContent', html);
});
this.map.geoObjects.add(mark);
return mark;
}
removeAllPoints() {
this.map.geoObjects.removeAll();
}
addOffer(offer) {
// console.log('[addOffer]', offer);
let hash = offer.pharmacy.hash;
if (hash in this.places)
this.places[hash].offers.push(offer);
else
this.places[hash] = {offers: [offer]};
if (!this.places[hash].mark && this.isAllowed(offer.name))
this.showPlaceOnMap(hash, offer);
}
showPlaceOnMap(hash, offer) {
this.places[hash].mark = this.addPoint({
geo: offer.pharmacy.geo,
hint: offer.pharmacy.name,
pharmacyName: offer.pharmacy.name,
pharmacyAddress: offer.pharmacy.address,
pharmacyPhone: offer.pharmacy.phone,
offersRef: this.places[hash].offers
});
}
hidePlaceFromMap(hash) {
if (this.places[hash].mark) {
this.map.geoObjects.remove(this.places[hash].mark);
delete this.places[hash].mark;
}
}
setFilter(filter) {
if (!filter)
filter = null;
this.filter = filter;
for (let hash in this.places) {
if (!this.places.hasOwnProperty(hash))
continue;
let pl = this.places[hash];
let ok = pl.offers.filter(o => this.isAllowed(o.name))
if (pl.mark && !ok.length)
this.hidePlaceFromMap(hash);
else if (!pl.mark && ok.length)
this.showPlaceOnMap(hash, ok[0]);
}
}
isAllowed(productName) {
return this.filter === null || productName.indexOf(this.filter) !== -1;
}
}
let globalMaps = null;
let globalSearch = null;
window.addEventListener('DOMContentLoaded', function() {
globalSearch = new Search();
globalMaps = new Maps();
});