function addClass(el,className){ if (el.classList) el.classList.add(className); else el.className += ' ' + className; } function removeClass(el,className){ if (el.classList) el.classList.remove(className); else el.className = el.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' '); } function toggleClass(el,className) { if (el.classList) { el.classList.toggle(className); } else { var classes = el.className.split(' '); var existingIndex = classes.indexOf(className); if (existingIndex >= 0) classes.splice(existingIndex, 1); else classes.push(className); el.className = classes.join(' '); } } function hasClass(el,className) { return new RegExp('(\\s|^)'+className+'(\\s|$)').test(el.className); } function getScrollTop() { return window.scrollY || document.documentElement.scrollTop; } function scrollShouldAjaxLoad(el, offset, preset) { offset = offset || 100; preset = preset || false; if(!el) { return false; } try { var windowHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); var r = el.getBoundingClientRect(); if(el.offsetHeight + r.top - offset < windowHeight) { if(preset) { if(el.offsetHeight + r.top + preset > windowHeight) { return true; } return false; } return true; } return false; } catch(e) { return false; } } var smoothScroll = { documentVerticalScrollPosition: function(){ if(self.pageYOffset) return self.pageYOffset; if(document.documentElement && document.documentElement.scrollTop) return document.documentElement.scrollTop; if(document.body.scrollTop) return document.body.scrollTop; return 0; }, viewportHeight: function(){ return (document.compatMode === "CSS1Compat") ? document.documentElement.clientHeight : document.body.clientHeight; }, documentHeight: function(){ return (document.height !== undefined) ? document.height : document.body.offsetHeight; }, documentMaximumScrollPosition: function(){ return this.documentHeight() - this.viewportHeight(); }, elementVerticalClientPosition: function(element){ var rectangle = element.getBoundingClientRect(); return rectangle.top; }, verticalTickToPosition: function(currentPosition, targetPosition){ var filter = 0.2; var fps = 60; var difference = parseFloat(targetPosition) - parseFloat(currentPosition); var arrived = (Math.abs(difference) <= 0.5); if(arrived){ scrollTo(0.0, targetPosition); return; } currentPosition = (parseFloat(currentPosition) * (1.0 - filter)) + (parseFloat(targetPosition) * filter); scrollTo(0.0, Math.round(currentPosition)); var self = this; setTimeout(function(){ self.verticalTickToPosition(currentPosition, targetPosition); }, (1000 / fps)); }, toElement: function(element, padding){ if(!element){ console.warn("Couldn't find target element."); return; } var targetPosition = this.documentVerticalScrollPosition() + this.elementVerticalClientPosition(element) - padding; var currentPosition = this.documentVerticalScrollPosition(); var maximumScrollPosition = this.documentMaximumScrollPosition(); if(targetPosition > maximumScrollPosition) targetPosition = maximumScrollPosition; this.verticalTickToPosition(currentPosition, targetPosition); } }; function Cookie(name, defaultValue, path) { this.name = name; this.value = null; this.path = path || "/"; var ca = document.cookie.split(';'); for(var i=0; i 120) { addClass(body, "shrink"); } else { removeClass(body, "shrink"); } } const NavbarSearchItem_Product = props => { const { data: p, active, highlightIndex, onMouseEnter, } = props; return {p.title}
{p.title}
{p.price} Kč
}; const NavbarSearchItem_Generic = props => { const { data: r, active, highlightIndex, onMouseEnter, } = props; return {r.title} }; const NavbarSearchItem_Post = props => { const { data: p, active, highlightIndex, onMouseEnter, } = props; return {p.title}
{p.title}
} class NavbarSearch extends React.Component { constructor(props) { super(props) this.state = { value: "", opened: false, nodes: [], highlighted: -1, preloading: false, used: false, }; this.loading = false; this.blurTimeout = undefined; this.searchTimeout = undefined; this.rowTypes = [ { type: "ranking", title: __navSearchLang.acZebricky, component: NavbarSearchItem_Post, }, { type: "review", title: __navSearchLang.acRecenze, component: NavbarSearchItem_Post, }, { type: "blogPost", title: __navSearchLang.acClanky, component: NavbarSearchItem_Post, }, { type: "product", title: __navSearchLang.acProdukty, component: NavbarSearchItem_Product, }, { type: "category", title: __navSearchLang.acKategorieProduktu, component: NavbarSearchItem_Generic, }, { type: "brand", title: __navSearchLang.acVyrobci, component: NavbarSearchItem_Generic, }, ]; this._open = this.open.bind(this); this._close = this.close.bind(this); this._toggleOpen = this.toggleOpen.bind(this); this._search = this.search.bind(this); this._onChange = this.onChange.bind(this); this._onBlur = this.onBlur.bind(this); this._onFocus = this.onFocus.bind(this); this._onMouseEnter = this.onMouseEnter.bind(this); this._onKeyDown = this.onKeyDown.bind(this); } componentDidUpdate(prevProps, prevState, snapshot) { if(!prevState.opened && this.state.opened) { const input = this.refs.input; input.focus() input.select() } } open() { this.setState({ opened: true, }) this.props.onToggle?.(true) } close() { this.setState({ opened: false, }) this.props.onToggle?.(false) } toggleOpen() { if(this.state.opened) { this.close() } else { this.open() } } async search() { if(this.loading) return; this.loading = true; const nextState = {}; try { const fd = new FormData(); fd.append("request", "index/search") fd.append("query", this.state.value) fd.append("all", "1") const r = await fetch(`${__httppath}/ajax/`, { method: "POST", body: fd, }); const e = await r.json(); if(e.status == "OK") { nextState.nodes = e.data.nodes; nextState.highlighted = -1; nextState.used = true; } else { console.error(e) } } catch(err) { console.error(err) } finally { nextState.preloading = false; this.setState(nextState) this.loading = false; } } onChange(ev) { const val = ev.currentTarget.value; const isSearchable = val.length >= 3; this.setState({ value: val, preloading: isSearchable, }, () => { if(isSearchable) { clearTimeout(this.searchTimeout) this.searchTimeout = setTimeout(this._search, 750); } }) } onBlur() { clearTimeout(this.blurTimeout) this.blurTimeout = setTimeout(this._close, 300); } onFocus() { clearTimeout(this.blurTimeout) } onMouseEnter(ev) { const hlindex = parseInt(ev.currentTarget.dataset.jsHlindex); this.setState({ highlighted: hlindex, }) } onKeyDown(ev) { switch(ev.key) { case "ArrowDown": { ev.preventDefault() const nodeCount = this.state.nodes.length; let next = this.state.highlighted + 1; if(next > nodeCount - 1) next = -1; this.setState({ highlighted: next, }) } break; case "ArrowUp": { ev.preventDefault() const nodeCount = this.state.nodes.length; let next = this.state.highlighted - 1; if(next < -1) next = nodeCount - 1; this.setState({ highlighted: next, }) } break; case "Enter": const hl = this.state.highlighted; if(hl > -1 && this.state.nodes[hl] !== undefined) { window.location.href = this.state.nodes[hl].link; } else { window.location.href = `${__httppath}/hledat/?q=${encodeURIComponent(this.state.value)}`; } break; case "Escape": this.refs.input.blur() break; } } render() { let hlIterator = -1; let results; const { nodes, highlighted, preloading, used, opened, value, } = this.state; if(used && opened) { const segments = []; for(const t of this.rowTypes) { const results = nodes.filter(e => e.type == t.type); if(results.length == 0) continue; segments.push(
{t.title}
{results.map(r => { hlIterator++; return React.createElement(t.component, { key: r.id, data: r, active: highlighted == hlIterator, highlightIndex: hlIterator, onMouseEnter: this._onMouseEnter, }) })}
) } results = (
{segments} {nodes.length > 0 ? __navSearchLang.acVsechnyVysledky : __navSearchLang.acZadneVysledky}
); } return
{preloading &&
} {results}
} } const onNavbarSearchToggle = opened => { if(opened) { $navbar.navbar.classList.add("search-active") } else { $navbar.navbar.classList.remove("search-active") } }; ReactDOM.render(, document.getElementById("navbar-search")) var siTriggers = document.querySelectorAll("[data-subitems-trigger]"); var siCloses = document.querySelectorAll("[data-subitems-close]"); for(var i=0; i { if(trigger.hasAttribute("href")) { trigger.addEventListener("click", (ev) => { if(window.innerWidth < 768) { ev.preventDefault() } }) } }) var trackedProducts = document.querySelectorAll("a[data-tr-product]"); for(var i=0; i
); } }); var $SHOWCOOKIECONFIRM = new Cookie("_ccf", "1"); if($SHOWCOOKIECONFIRM.value === "1") { ReactDOM.render(, document.getElementById("cookie-confirm")); }