1// ===== 1. UTILITY FUNCTIONS ===== 2const regEx_email = /^[\ a-z0-9._-]+@[a-z0-9.-]+\.[a-z]{2,6}$/i; 3 4if(typeof String.prototype.trim !== 'function') { 5 String.prototype.trim = function() { 6 return this.replace(/^\s+|\s+$/g, ''); 7 } 8} 9 10function isIE() { 11 return /*@cc_on!@*/false || !!document.documentMode; 12} 13 14Number.prototype.formatMoney = function(c, d, t){ 15 var n = this, 16 c = isNaN(c = Math.abs(c)) ? 0 : c, 17 d = d == undefined ? "," : d, 18 t = t == undefined ? " " : t, 19 s = n < 0 ? "-" : "", 20 i = String(parseInt(n = Math.abs(Number(n) || 0).toFixed(c))), 21 j = (j = i.length) > 3 ? j % 3 : 0; 22 return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : ""); 23}; 24 25function removeMultipleSpaces(html) { 26 return html.replace(/\s+/g, ' '); 27} 28// ===== 2. VIDEO HANDLING ===== 29document.querySelectorAll('.videoIsHere').forEach(container => { 30 const video = container.querySelector('video'); 31 const img = container.querySelector('img'); 32 33 if (!video || !img) return; 34 35 // Nastavenà video atributů 36 video.setAttribute('playsinline', ''); 37 38 // Zkontrolujeme, zda nÄkterý z rodiÄovských elementů má tÅÃdu 'loopFalse' 39 const hasLoopFalseParent = container.closest('.loopFalse') !== null; 40 41 // NastavÃme loop na true, pokud nenà pÅÃtomen rodiÄ s tÅÃdou 'loopFalse' 42 video.loop = !hasLoopFalseParent; 43 44 // PÅidáme transition pro plynulé pÅechody obrázku 45 img.style.transition = 'opacity 0.5s ease-in-out'; 46 47 // NastavÃme obrázek jako viditelný na zaÄátku 48 img.style.opacity = '1'; 49 50 const observer = new IntersectionObserver((entries) => { 51 entries.forEach(entry => { 52 if (entry.isIntersecting) { 53 setTimeout(() => { 54 video.load(); 55 }, 100); 56 observer.unobserve(container); 57 } 58 }); 59 }, { 60 rootMargin: '50px', 61 threshold: 0.1 62 }); 63 64 observer.observe(container); 65 66 let playPromise = null; 67 let resetTimeout = null; 68 69 container.addEventListener('mouseenter', () => { 70 // ZruÅ¡Ãme pÅÃpadné Äekánà na reset 71 if (resetTimeout) { 72 clearTimeout(resetTimeout); 73 resetTimeout = null; 74 } 75 76 // Skryjeme obrázek 77 img.style.opacity = '0'; 78 79 // PÅehrajeme video od zaÄátku 80 video.currentTime = 0; 81 82 if (video.readyState >= 2) { 83 playPromise = video.play(); 84 } else { 85 video.addEventListener('canplay', () => { 86 playPromise = video.play(); 87 }, { once: true }); 88 } 89 }); 90 91 container.addEventListener('mouseleave', () => { 92 // ZastavÃme pÅehrávánà videa 93 if (playPromise !== null) { 94 playPromise.then(() => { 95 video.pause(); 96 97 // ZaÄneme zobrazovat obrázek ihned 98 img.style.opacity = '1'; 99 100 // Po 500ms vrátÃme video na zaÄátek 101 resetTimeout = setTimeout(() => { 102 video.currentTime = 0; 103 }, 500); 104 }).catch(error => console.log('Chyba pÅehrávánÃ:', error)); 105 } else { 106 // Pokud video jeÅ¡tÄ nezaÄalo hrát, pouze zobrazÃme obrázek 107 img.style.opacity = '1'; 108 } 109 }); 110}); 111//// ===== 2. VIDEO HANDLING ===== 112//document.querySelectorAll('.videoIsHere').forEach(container => { 113// const video = container.querySelector('video'); 114// 115// if (!video) return; 116// 117// video.setAttribute('playsinline', ''); 118// 119// const observer = new IntersectionObserver((entries) => { 120// entries.forEach(entry => { 121// if (entry.isIntersecting) { 122// setTimeout(() => { 123// video.load(); 124// }, 100); 125// observer.unobserve(container); 126// } 127// }); 128// }, {
129// rootMargin: '50px', 130// threshold: 0.1 131// }); 132// 133// observer.observe(container); 134// 135// let playPromise = null; 136// 137// container.addEventListener('mouseenter', () => { 138// if (video.readyState >= 2) { 139// playPromise = video.play(); 140// } else { 141// video.addEventListener('canplay', () => { 142// playPromise = video.play(); 143// }, { once: true }); 144// } 145// }); 146// 147// container.addEventListener('mouseleave', () => { 148// if (playPromise !== null) { 149// playPromise.then(() => video.pause()) 150// .catch(error => console.log('Chyba pÅehrávánÃ:', error)); 151// } 152// }); 153//}); 154 155// ===== 3. SCROLL HANDLING ===== 156// ===== 3. SCROLL HANDLING ===== 157// Hlavnà vertikálnà instance Lenis 158const lenis = new Lenis({ 159 duration: 1.2, 160 easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)), 161 direction: 'vertical', 162 gestureDirection: 'vertical', 163 smooth: true, 164 mouseMultiplier: 1.5, 165 smoothTouch: false, 166 touchMultiplier: 2, 167 infinite: false 168}); 169 170function raf(time) { 171 lenis.raf(time); 172 requestAnimationFrame(raf); 173} 174requestAnimationFrame(raf); 175 176// Najdeme prvky s horizontálnÃm skrolovánÃm 177const horizontalScrollElements = document.querySelectorAll('.ref-submenu, .ref-menu'); 178 179// PÅidáme vlastnà horizontálnà skrolovánà pro vÅ¡echna nalezená prvky 180horizontalScrollElements.forEach(element => { 181 if (element) { 182 // NastavÃme CSS vlastnosti programovÄ pro jistotu 183 element.style.overflowX = 'auto'; 184 element.style.overflowY = 'hidden'; 185 element.style.whiteSpace = 'nowrap'; 186 187 // Speciálnà obsluha události wheel pro horizontálnà skrolovánà 188 element.addEventListener('wheel', function(e) { 189 // Zkontrolujeme, zda prvek má horizontálnà pÅeteÄenà 190 const hasHorizontalOverflow = element.scrollWidth > element.clientWidth; 191 192 if (hasHorizontalOverflow && e.deltaY !== 0) { 193 // Pouze pokud má prvek pÅeteÄenÃ, zabránÃme výchozÃmu chovánà 194 195 // Kontrola, zda jsme na konci skrolovánà 196 const isAtLeftEdge = element.scrollLeft <= 0; 197 const isAtRightEdge = element.scrollLeft + element.clientWidth >= element.scrollWidth; 198 199 // Pokud jsme na kraji a uživatel se snažà skrolovat dál v tom smÄru, 200 // umožnÃme normálnà (vertikálnÃ) skrolovánà 201 if ((isAtLeftEdge && e.deltaY < 0) || (isAtRightEdge && e.deltaY > 0)) { 202 // Umožnà bÄžné vertikálnà skrolovánÃ, když jsme na hranici horizontálnÃho posunu 203 return; 204 } 205 206 // ZastavÃme propagaci události, aby ji nezachytil Lenis 207 e.stopPropagation(); 208 // ZabránÃme výchozÃmu chovánà 209 e.preventDefault(); 210 211 // NastavÃme rychlost skrolovánà 212 const scrollSpeed = 2; 213 // PÅevedeme vertikálnà pohyb na horizontálnà 214 element.scrollLeft += e.deltaY * scrollSpeed; 215 } 216 // V opaÄném pÅÃpadÄ necháme událost propadnout pro bÄžné vertikálnà skrolovánà 217 }, { passive: false }); // passive: false je nezbytné pro e.preventDefault() 218 } 219}); 220 221function smoothScroll(target) { 222 const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0; 223 224 if (isTouchDevice) { 225 if (typeof target === 'number') { 226 window.scrollTo({ 227 top: 0, 228 behavior: 'smooth' 229 }); 230 } else { 231 target.scrollIntoView({ 232 behavior: 'smooth' 233 }); 234 } 235 } else { 236 lenis.scrollTo(target, { 237 offset: 0, 238 duration: 5 239 }); 240 } 241} 242 243document.querySelector('.scroll-top').addEventListener('click', function(e) { 244 e.preventDefault(); 245 smoothScroll(0); 246}); 247 248 249 250 251// Definujeme pole tÅÃd, pro které nechceme použÃt smooth scroll 252const excludedClasses = ['carousel-control-next', 'carousel-control-prev']; 253 254document.querySelectorAll('a[href^="#"]').forEach(anchor => { 255 anchor.addEventListener('click', function (e) { 256 // Kontrola, zda prvek neobsahuje nÄkterou z vylouÄených tÅÃd
257 const shouldExclude = excludedClasses.some(className => 258 this.classList.contains(className) 259 ); 260 261 // Pokud má prvek nÄkterou z vylouÄených tÅÃd, nechceme preventDefault a smooth scroll 262 if (shouldExclude) { 263 return; 264 } 265 266 e.preventDefault(); 267 const href = this.getAttribute('href'); 268 269 if (href === '#') { 270 return; 271 } 272 273 let target; 274 // Podpora pro scroll na class: href="#class-nazevTridy" 275 if (href.startsWith('#class-')) { 276 const className = href.replace('#class-', ''); 277 target = document.querySelector('.' + className); 278 } else { 279 target = document.querySelector(href); 280 } 281 282 if (target) { 283 smoothScroll(target); 284 } 285 }); 286}); 287 288window.addEventListener('scroll', function() { 289 const scrollTop = document.querySelector('.scroll-top'); 290 const viewportHeight = window.innerHeight; 291 292 if (window.scrollY > viewportHeight) { 293 scrollTop.classList.add('show'); 294 } else { 295 scrollTop.classList.remove('show'); 296 } 297}); 298 299// ===== 4. CUSTOM CURSOR ===== 300class CustomCursor { 301 constructor() { 302 this.$cursor = $(".cursor"); 303 304 if (this.$cursor.length === 0) { 305 console.error('Cursor element nenalezen'); 306 return; 307 } 308 309 this.isTouchDevice = 'ontouchstart' in document.documentElement; 310 this.init(); 311 } 312 313 init() { 314 if (this.isTouchDevice) { 315 this.disableCursor(); 316 return; 317 } 318 this.setupCursor(); 319 } 320 321 disableCursor() { 322 this.$cursor.hide(); 323 $('html, *').css('cursor', 'auto'); 324 } 325 326 setupCursor() { 327 $(document).on('mousemove', (e) => { 328 requestAnimationFrame(() => { 329 this.$cursor.css({ 330 top: e.clientY + 'px', 331 left: e.clientX + 'px' 332 }); 333 }); 334 }); 335 336 const hoverElements = ` 337 div:not(.whole-field-url) a, 338 a:not(.container-link), 339 .slide-down, 340 #contactForm50Submit, 341 .menu-toggler-svg, 342 .mobile-menu-open, 343 .next, 344 .prev, 345 .arrow-collapse 346 `; 347 348 $(hoverElements).hover( 349 () => this.$cursor.css('transform', 'scale(3.2)'), 350 () => this.$cursor.css('transform', 'scale(1)') 351 ); 352 353 $("a.container-link, .whole-field-url a.stretched-link, button").hover( 354 () => { 355 this.$cursor 356 .addClass('arrow-up') 357 .css('transform', 'scale(3.2)'); 358 }, 359 () => { 360 this.$cursor 361 .removeClass('arrow-up') 362 .css('transform', 'scale(1)'); 363 } 364 ); 365 366 $("a.arrow-up-right, .arrow-up-right a").hover( 367 () => { 368 this.$cursor 369 .addClass('arrow-up-right') 370 .css('transform', 'scale(3.2)'); 371 }, 372 () => { 373 this.$cursor 374 .removeClass('arrow-up-right') 375 .css('transform', 'scale(1)'); 376 } 377 ); 378 379 $(".slider-cover > div > div").hover( 380 () => { 381 this.$cursor 382 .addClass('slide-horizontal') 383 .css('transform', 'scale(3.2)'); 384 }, 385 () => { 386 this.$cursor 387 .removeClass('slide-horizontal') 388 .css('transform', 'scale(1)'); 389 } 390 ); 391 392 $(".prev, .carousel-control-prev").hover( 393 () => { 394 this.$cursor 395 .addClass('slide-left') 396 .css('transform', 'scale(3.2)'); 397 }, 398 () => { 399 this.$cursor 400 .removeClass('slide-left') 401 .css('transform', 'scale(1)'); 402 } 403 ); 404 405 $(".playVideo").hover( 406 () => { 407 this.$cursor 408 .addClass('playIcon') 409 .css('transform', 'scale(3.2)'); 410 }, 411 () => { 412 this.$cursor 413 .removeClass('playIcon') 414 .css('transform', 'scale(1)'); 415 } 416 ); 417 418 $(".modal").hover( 419 () => { 420 this.$cursor 421 .addClass('closeModal') 422 .css('transform', 'scale(3.2)'); 423 }, 424 () => { 425 this.$cursor 426 .removeClass('closeModal') 427 .css('transform', 'scale(1)'); 428 } 429 ); 430 431 $(".closeForm").hover( 432 () => { 433 this.$cursor 434 .addClass('closeFormIcon') 435 .css('transform', 'scale(3.2)'); 436 }, 437 () => {
438 this.$cursor 439 .removeClass('closeFormIcon') 440 .css('transform', 'scale(1)'); 441 } 442 ); 443 $("a.questionMark").hover( 444 () => { 445 this.$cursor 446 .addClass('questionIconIcon') 447 .css('transform', 'scale(3.2)'); 448 }, 449 () => { 450 this.$cursor 451 .removeClass('questionIconIcon') 452 .css('transform', 'scale(1)'); 453 } 454 ); 455 456 $(".next, .carousel-control-next").hover( 457 () => { 458 this.$cursor 459 .addClass('slide-right') 460 .css('transform', 'scale(3.2)'); 461 }, 462 () => { 463 this.$cursor 464 .removeClass('slide-right') 465 .css('transform', 'scale(1)'); 466 } 467 ); 468 469 $('[data-toggle="lightbox"]').hover( 470 () => { 471 this.$cursor 472 .addClass('zoom') 473 .css('transform', 'scale(3.2)'); 474 }, 475 () => { 476 this.$cursor 477 .removeClass('zoom') 478 .css('transform', 'scale(1)'); 479 } 480 ); 481 482 $(document).on('mousedown', () => { 483 this.$cursor.css('transform', 'scale(0.2)'); 484 }).on('mouseup', () => { 485 this.$cursor.css('transform', 'scale(1)'); 486 }); 487 488 $(document).on('mouseleave', () => { 489 this.$cursor.css('opacity', '0'); 490 }).on('mouseenter', () => { 491 this.$cursor.css('opacity', '1'); 492 }); 493 } 494} 495 496// ===== 5. TEXT FORMATTING ===== 497function lineUpFormat(selector, callback) { 498 const processedClass = 'ci-line-processed'; 499 500 function createLineUpStructure(element) { 501 const $element = $(element); 502 503 if ($element.hasClass(processedClass)) { 504 return; 505 } 506 507 const lineUpDiv = $('<div class="ci-line-element"></div>'); 508 for(let i = 0; i < 8; i++) { 509 lineUpDiv.append('<div></div>'); 510 } 511 512 $element 513 .append(lineUpDiv) 514 .removeClass('ci-line') 515 .addClass('position-relative') 516 .addClass(processedClass); 517 } 518 519 $(selector).each(function() { 520 createLineUpStructure(this); 521 }); 522 523 if (typeof callback === 'function') { 524 callback(); 525 } 526} 527 528function lightBoldFormat(selector) { 529 function wrapWordsInSpans(text) { 530 let processedText = text.replace(/<br\s*\/?>/gi, ' LINEBREAK_TOKEN '); 531 532 return processedText.split(' ') 533 .map(word => { 534 if (word === 'LINEBREAK_TOKEN') { 535 return '<span class="word clearBoth"></span>'; 536 } else if (word) { 537 return `<span class="word">${word}</span>`; 538 } 539 return ''; 540 }) 541 .join(' '); 542 } 543 544 function processElement(element) { 545 let $element = $(element); 546 let $lineUp = $element.find('.ci-line-element').detach(); 547 let containerWidth = $element.width(); 548 let originalContent = $element.html() 549 .replace(/<span[^>]*>(.*?)<\/span>/g, '$1') 550 .trim(); 551 552 $element.html(wrapWordsInSpans(originalContent)); 553 $element.append($lineUp); 554 555 let currentWidth = 0; 556 let firstLineComplete = false; 557 558 $element.find('span.word').each(function() { 559 let $word = $(this); 560 561 if ($word.hasClass('clearBoth')) { 562 currentWidth = 0; 563 firstLineComplete = true; 564 return; 565 } 566 567 let wordWidth = $word.outerWidth(true); 568 569 if (!firstLineComplete) { 570 if (currentWidth + wordWidth > containerWidth) { 571 firstLineComplete = true; 572 } else { 573 $word.addClass('font-weight-light'); 574 currentWidth += wordWidth; 575 } 576 } 577 }); 578 } 579 580 let elements = $(selector); 581 elements.each(function() { 582 processElement(this); 583 }); 584 585 let resizeTimer; 586 $(window).on('resize', function() { 587 clearTimeout(resizeTimer); 588 resizeTimer = setTimeout(function() { 589 elements.each(function() { 590 processElement(this); 591 }); 592 }, 250); 593 }); 594} 595 596 597// ===== 6. IMAGE AND BACKGROUND HANDLING ===== 598const handleResponsiveImages = (isResize = false) => { 599 // Detekce device pixel ratio (systémové mÄÅÃtko) 600 const pixelRatio = window.devicePixelRatio || 1; 601 const isSystemScaled = pixelRatio > 1 && pixelRatio < 2; // PÅibližnÄ 125% = 1.25 602 const isRetina = pixelRatio >= 2; // Detekce retina displeje 603 604 const getImageSize = (width) => { 605 // Pro tisk nebo printablePage vždy použijeme full velikost 606 if ($('body').hasClass('printablePage')) { 607 return 'full'; 608 } 609 // Pro retina displeje vždy použijeme full velikost 610 if (isRetina) { 611 return 'full'; 612 } 613 // Pokud je systémové mÄÅÃtko aktivnà (napÅ. 125%), použijeme vÄtšà verzi obrázku 614 if (isSystemScaled) { 615 // ZvÄtÅ¡Ãme požadovanou velikost obrázku pro lepšà ostrost 616 if (width < 321) return 'medium'; // MÃsto 'small' 617 else if (width < 641) return 'full'; // MÃsto 'medium' 618 else return 'full'; // MÃsto 'large' 619 } else { 620 // Původnà logika 621 if (width < 321) return 'medium'; 622 else if (width < 641) return 'large'; 623 else if (width < 1025) return 'large'; 624 else return 'full'; 625 } 626 }; 627 628 const updateImageUrl = ($elem) => { 629 const isImage = $elem.is('img'); 630 const dataSrcImageUrl = isImage ? 631 $elem.attr('data-src') : 632 $elem.attr('data-bg'); 633 634 if (!dataSrcImageUrl) return; 635 636 const $parent = $elem.parent(); 637 const $imageBox = $elem.closest('.image-box'); 638 639 // Kontrola, zda se v nadÅazených prvcÃch nacházà tÅÃda fullImage 640 const hasFullImageParent = $elem.parents('.fullImage').length > 0; 641 642 // Pokud je rodiÄ s tÅÃdou fullImage, vždy použijeme size "full", jinak standardnà výpoÄet 643 const elementWidth = $imageBox.length ? 644 $imageBox.width() : 645 $parent.width(); 646 const size = hasFullImageParent ? 'full' : getImageSize(elementWidth); 647 648 let newUrl; 649 if (dataSrcImageUrl.includes('-small') || 650 dataSrcImageUrl.includes('-medium') || 651 dataSrcImageUrl.includes('-large') || 652 dataSrcImageUrl.includes('-full')) { 653 newUrl = dataSrcImageUrl.replace(/-(?:small|medium|large|full)/, `-${size}`); 654 } 655 else if (dataSrcImageUrl.includes('/small/') || 656 dataSrcImageUrl.includes('/medium/') || 657 dataSrcImageUrl.includes('/large/') || 658 dataSrcImageUrl.includes('/full/')) { 659 newUrl = dataSrcImageUrl.replace(/\/(small|medium|large|full)\//, `/${size}/`); 660 } 661 if (newUrl) { 662 if (isResize) { 663 if (isImage && $elem.attr('src')) { 664 $elem.attr('src', newUrl); 665 } else if ($elem.hasClass('image-box') && $elem.css('background-image') !== 'none') { 666 $elem.css('background-image', `url(${newUrl})`); 667 } 668 } 669 670 if (isImage) { 671 $elem.attr('data-src', newUrl); 672 if (!isResize && !$elem.attr('src')) { 673 $elem.attr('src', newUrl); 674 } 675 } else if ($elem.hasClass('image-box')) { 676 $elem.attr('data-bg', newUrl); 677 } 678 } 679 }; 680 $('img[data-responsive="YES"]').each(function() { 681 updateImageUrl($(this)); 682 }); 683 684 $('.image-box[data-change-url="YES"]').each(function() { 685 updateImageUrl($(this)); 686 }); 687 688 $('img[data-change-url="YES"]').each(function() { 689 updateImageUrl($(this)); 690 }); 691 692 693}; 694 695// Image hover effect plugin 696$.fn.showImageOrVideoOnOver = function(options) { 697 const config = $.extend({ 698 lerp: 0.1 699 }, options); 700 701 const lerp = (start, end, factor) => start * (1 - factor) + end * factor; 702 703 const getViewportCenter = () => ({ 704 x: window.innerWidth / 2, 705 y: window.innerHeight / 2 706 }); 707 708 return this.each(function() { 709 const container = $(this); 710 const movableElements = container.find('.moveByCursor'); 711 let animationFrame; 712 713 const positions = new Map(); 714 715 const center = getViewportCenter(); 716 movableElements.each(function() { 717 positions.set(this, { 718 current: { x: center.x, y: center.y }, 719 target: { x: center.x, y: center.y } 720 }); 721 }); 722 723 const animate = () => { 724 movableElements.each(function() { 725 const element = $(this); 726 const pos = positions.get(this); 727
728 pos.current.x = lerp(pos.current.x, pos.target.x, config.lerp); 729 pos.current.y = lerp(pos.current.y, pos.target.y, config.lerp); 730 731 element.css({ 732 'left': pos.current.x - (element.outerWidth() / 2) + 'px', 733 'top': pos.current.y - (element.outerHeight() / 2) + 'px' 734 }); 735 }); 736 737 animationFrame = requestAnimationFrame(animate); 738 }; 739 740 container.on('mousemove', function(e) { 741 movableElements.each(function() { 742 const pos = positions.get(this); 743 pos.target.x = e.clientX; 744 pos.target.y = e.clientY; 745 }); 746 }); 747 748 container.on('mouseenter', function() { 749 cancelAnimationFrame(animationFrame); 750 animate(); 751 }); 752 753 container.on('mouseleave', function() { 754 cancelAnimationFrame(animationFrame); 755 const center = getViewportCenter(); 756 757 movableElements.each(function() { 758 const pos = positions.get(this); 759 if (!container.is(':hover')) { 760 pos.target = { x: center.x, y: center.y }; 761 pos.current = { x: center.x, y: center.y }; 762 } 763 }); 764 }); 765 766 $(window).on('resize', function() { 767 const center = getViewportCenter(); 768 movableElements.each(function() { 769 const pos = positions.get(this); 770 if (!container.is(':hover')) { 771 pos.target = { x: center.x, y: center.y }; 772 pos.current = { x: center.x, y: center.y }; 773 } 774 }); 775 }); 776 777 movableElements.each(function() { 778 const element = $(this); 779 element.css({ 780 'left': center.x - (element.outerWidth() / 2) + 'px', 781 'top': center.y - (element.outerHeight() / 2) + 'px' 782 }); 783 }); 784 }); 785}; 786 787// ===== 7. HEADER AND NAVIGATION HANDLING ===== 788let lastScrollTop = 0; 789const header = document.getElementById('custom-navbar'); 790const headerHeight = header.offsetHeight; 791 792function handleScroll() { 793 if (window.innerWidth >= 992) { 794 header.style.transform = 'translateY(0)'; 795 return; 796 } 797 798 let currentScroll = window.pageYOffset || document.documentElement.scrollTop; 799 800 if (currentScroll > lastScrollTop && currentScroll > headerHeight) { 801 header.style.transform = 'translateY(-100%)'; 802 } else { 803 header.style.transform = 'translateY(0)'; 804 } 805 806 lastScrollTop = currentScroll; 807} 808 809window.addEventListener('scroll', handleScroll); 810window.addEventListener('resize', handleScroll); 811 812// Background color handling 813document.querySelectorAll('[class*="mybg-"]').forEach(element => { 814 const classes = element.className.split(' '); 815 const bgClass = classes.find(className => className.startsWith('mybg-')); 816 817 if (bgClass) { 818 const color = bgClass.split('-')[1]; 819 element.style.setProperty('background-color', '#' + color, 'important'); 820 } 821}); 822// Text color handling 823document.querySelectorAll('[class*="mytext-"]').forEach(element => { 824 const classes = element.className.split(' '); 825 const textClass = classes.find(className => className.startsWith('mytext-')); 826 827 if (textClass) { 828 const color = textClass.split('-')[1]; 829 element.style.setProperty('color', '#' + color, 'important'); 830 } 831}); 832 833// ===== 8. SECTION VISIBILITY TRACKING ===== 834function initSectionObserver() { 835 const sections = document.querySelectorAll('[id]'); 836 const navLinks = document.querySelectorAll('a[href^="#"]'); 837 838 if (sections.length === 0 || navLinks.length === 0) { 839 console.warn('Nebyly nalezeny žádné sekce nebo odkazy!'); 840 return; 841 } 842 843 const observerOptions = { 844 root: null, 845 rootMargin: '-50% 0px -50% 0px', // Sledujeme stÅed viewportu 846 threshold: 0 // StaÄà jakákoliv viditelnost 847 }; 848 849 const observer = new IntersectionObserver((entries) => { 850 // Najdeme sekci, která je nejvÃce ve viewportu 851 let maxVisibleSection = null; 852 let maxVisibleRatio = 0; 853 854 entries.forEach(entry => { 855 const rect = entry.boundingClientRect; 856 const windowHeight = window.innerHeight; 857 858 // VypoÄÃtáme, jak moc je sekce ve stÅedu viewportu 859 const distanceFromCenter = Math.abs( 860 (rect.top + rect.height / 2) - (windowHeight / 2) 861 ); 862 863 if (entry.isIntersecting && (!maxVisibleSection || distanceFromCenter < maxVisibleRatio)) { 864 maxVisibleSection = entry.target; 865 maxVisibleRatio = distanceFromCenter; 866 } 867 }); 868 869 if (maxVisibleSection) { 870 navLinks.forEach(link => link.classList.remove('active')); 871 const activeLink = document.querySelector(`a[href="#${maxVisibleSection.id}"]`); 872 if (activeLink) activeLink.classList.add('active'); 873 } 874 }, observerOptions); 875 876 sections.forEach(section => observer.observe(section)); 877 878 navLinks.forEach(link => { 879 link.addEventListener('click', (e) => { 880 e.preventDefault(); 881 const targetId = link.getAttribute('href').substring(1); 882 const targetElement = document.getElementById(targetId); 883 if (targetElement) { 884 targetElement.scrollIntoView({ behavior: 'smooth' }); 885 } 886 }); 887 }); 888} 889 890if (document.readyState === 'loading') { 891 document.addEventListener('DOMContentLoaded', initSectionObserver); 892} else { 893 initSectionObserver(); 894} 895 896// ===== 9. DOCUMENT READY HANDLERS ===== 897$(document).ready(function() { 898 new CustomCursor(); 899 900 lineUpFormat('.ci-line', function() { 901 //lightBoldFormat('.lightBoldTitle'); 902 }); 903 904 $('.showImageOrVideoOnOver').showImageOrVideoOnOver({ 905 lerp: 0.025 906 }); 907 908 const $showDifferencesToggle = $('#showDifferences'); 909 if ($showDifferencesToggle.length) { 910 const updateDifferencesState = () => { 911 const $differencesLists = $('ul.activeDifferences'); 912 if (!$differencesLists.length) return; 913 $differencesLists.toggleClass('isActive', $showDifferencesToggle.is(':checked')); 914 }; 915 916 $showDifferencesToggle.on('change', updateDifferencesState); 917 updateDifferencesState(); 918 } 919 920 $('[class*="more-items-"]').on('click touchstart', function() { 921 922 // PoÄkej chvÃli, aby se nový obsah stihl naÄÃst a vložit do DOM 923 setTimeout(function() { 924 handleResponsiveImages(true); 925 }, 100); 926 }); 927 928 // Header and content height setup 929 const headerHeight = $('header').outerHeight(true); 930 const footerHeight = $('footer').outerHeight(true); 931 const contentHeight = $(window).height() - headerHeight - footerHeight; 932 const fixedContentHeight = $(window).height() - footerHeight; 933 934 if ($('header').hasClass('fixed-top')) { 935 $('#main').css({ 'min-height': fixedContentHeight }); 936 } else { 937 $('#main').css({ 'min-height': contentHeight }); 938 } 939 940 // Lazy video loading 941 if ($('.lazy-video').length) { 942 $('.lazy-video').each(function() { 943 var lazyvideo = $(this); 944 var lazyvideoParent = $(this).parent(); 945 var lazyTop = lazyvideo.offset().top; 946 947 if (lazyTop < $(window).height()) { 948 var src = lazyvideo.attr('data-src'); 949 lazyvideo.attr('src', src); 950 if (!lazyvideoParent.hasClass('loaded')) { 951 lazyvideoParent.trigger('load'); 952 lazyvideoParent.trigger('play'); 953 lazyvideoParent.addClass('loaded'); 954 } 955 } 956 }); 957 } 958 959 // Menu handlers 960 $('body').on('click', '.menu-toggler-svg', function(e) { 961 e.preventDefault(); 962 $('body').toggleClass('modal-open mobile-open'); 963 $('html').toggleClass('modal-open'); 964 }); 965 966 // Form handlers 967 // PromÄnná pro uloženà aktuálnà formClass
968 let currentFormClass = ''; 969 970 $('.closeForm').on('click touchstart', function(e) { 971 e.preventDefault(); 972 $('body').removeClass('formular-open'); 973 $('html').removeClass('formular-open'); 974 if (currentFormClass) { 975 $('body').removeClass(currentFormClass); 976 currentFormClass = ''; 977 } 978 }); 979 980 $(document).on('click touchstart', function(e) { 981 if ($('body').hasClass('formular-open')) { 982 // Ignorovat kliknutà na .openForm, .colForm a bootbox modal 983 if (!$(e.target).closest('.colForm').length && 984 !$(e.target).closest('.openForm').length && 985 !$(e.target).closest('.bootbox.modal').length) { 986 $('body').removeClass('formular-open'); 987 $('html').removeClass('formular-open'); 988 if (currentFormClass) { 989 $('body').removeClass(currentFormClass); 990 currentFormClass = ''; 991 } 992 } 993 } 994 }); 995 996 $(document).on('keydown', function(e) { 997 if (e.keyCode === 27 || e.key === 'Escape') { 998 if ($('body').hasClass('formular-open')) { 999 $('body').removeClass('formular-open'); 1000 $('html').removeClass('formular-open'); 1001 if (currentFormClass) { 1002 $('body').removeClass(currentFormClass); 1003 currentFormClass = ''; 1004 } 1005 } 1006 } 1007 }); 1008 1009 $('.openForm').on('click', function(e) { 1010 e.preventDefault(); 1011 const $body = $('body'); 1012 const $html = $('html'); 1013 1014 if ($body.hasClass('formular-open')) { 1015 $body.removeClass('formular-open'); 1016 $html.removeClass('formular-open'); 1017 if (currentFormClass) { 1018 $body.removeClass(currentFormClass); 1019 currentFormClass = ''; 1020 } 1021 } else { 1022 $body.addClass('formular-open'); 1023 $html.addClass('formular-open'); 1024 1025 // Extrakce formClass-xxx a pÅidánà do body 1026 const classes = $(this).attr('class').split(' '); 1027 const formClassMatch = classes.find(cls => cls.startsWith('formClass-')); 1028 if (formClassMatch) { 1029 currentFormClass = formClassMatch.replace('formClass-', ''); 1030 $body.addClass(currentFormClass); 1031 } 1032 1033 if ($(this).hasClass('getTitle')) { 1034 const btnText = $(this).attr("title"); 1035 const pageTitle = document.title; 1036 const msg = btnText + ': ' + pageTitle + '\n\n'; 1037 $('#formular_321 #contactForm50-value-dotaz').val(msg); 1038 } 1039 if ($(this).hasClass('getOnlyTitle')) { 1040 const btnText = $(this).attr("title"); 1041 const pageTitle = document.title; 1042 const msg = btnText + '\n\n'; 1043 $('#formular_321 #contactForm50-value-dotaz').val(msg); 1044 } 1045 } 1046 }); 1047 1048 // External links handling 1049 $('a').each(function() { 1050 var a = new RegExp('/' + window.location.host + '/'); 1051 if (!a.test(this.href)) { 1052 $(this).attr("target", "_blank"); 1053 } 1054 }); 1055 1056 // SVG handling 1057 var mySVGsToInject = document.querySelectorAll('img.inject-me'); 1058 SVGInjector(mySVGsToInject, { 1059 evalScripts: 'once', 1060 pngFallback: 'assets/png', 1061 each: function (svg) {} 1062 }); 1063 1064 if (!Modernizr.svg) { 1065 $('img[src$=".inject-me"]').each(function() { 1066 $(this).attr('src', $(this).attr('src').replace('.svg', '.png')); 1067 }); 1068 } 1069 1070 // Cart handling 1071 $('#kosik1Form').submit(function(event) { 1072 if ($('#souhlas:checked').length == 0) { 1073 event.preventDefault(); 1074 alert('Pro vytvoÅenà objednávky, musÃte souhlasit s ObchodnÃmi podmÃnkami.'); 1075 return false; 1076 } 1077 }); 1078 1079 if ($("#addToCartFormSubmiter").length) { 1080 $("#addToCartFormSubmiter").click(function(event) { 1081 event.preventDefault(); 1082 1083 if (addToCartFormBeforeSubmit()) { 1084 var imageOffset = $(".mainProductImage").offset(); 1085 var cartOffset = $(".openCart").offset(); 1086 1087 if (typeof(imageOffset) != "undefined" && typeof(cartOffset) != "undefined") { 1088 aTop = cartOffset.top; 1089 aLeft = cartOffset.left; 1090 1091 $(".mainProductImage").clone() 1092 .appendTo('body') 1093 .addClass('mainProductImage-copy') 1094 .css({ 1095 position: "absolute", 1096 top: imageOffset.top, 1097 left: imageOffset.left 1098 }) 1099 .css({zIndex: 9999999999999}) 1100 .animate({ 1101 top: aTop + "px", 1102 left: aLeft + "px",
1103 width: "20px", 1104 opacity: 0 1105 }, 666, 'swing', function() { 1106 $("#addToCartForm").submit(); 1107 }); 1108 } else { 1109 $("#addToCartForm").submit(); 1110 } 1111 } 1112 }); 1113 } 1114 1115 /*$('.accordion-button').on('click', function(e) { 1116 // UložÃme si referenci na prvek a zjistÃme, zda má tÅÃdu .collapsed 1117 const $button = $(this); 1118 const isCollapsed = $button.hasClass('collapsed'); 1119 1120 // Scrollujeme jen pokud je akordeon zavÅený (má tÅÃdu .collapsed) 1121 if (isCollapsed) { 1122 const target = $button.closest(".accordion-item")[0]; 1123 1124 // PoÄkáme než se panel otevÅe a pak scrollujeme 1125 setTimeout(function() { 1126 smoothScroll(target); 1127 }, 300); 1128 } 1129 });*/ 1130 /*$('.accordion-button').on('click', function(e) { 1131 e.preventDefault(); // Stop browser's automatic behavior 1132 1133 var $button = $(this); 1134 var isOpening = $button.hasClass('collapsed'); 1135 1136 // Manually trigger the accordion 1137 var targetId = $button.attr('data-bs-target'); 1138 $(targetId).collapse('toggle'); 1139 1140 if (isOpening) { 1141 var $card = $button.closest('.accordion-item'); 1142 1143 setTimeout(function() { 1144 $('html, body').animate({ 1145 scrollTop: $card.offset().top - 80 1146 }, 350); 1147 }, 450); 1148 } 1149 });*/ 1150 1151 /*$('.accordion-button').on('click', function(e) { 1152 var $button = $(this); 1153 var isOpening = $button.hasClass('collapsed'); 1154 1155 if (isOpening) { 1156 // Don't prevent default - let Bootstrap handle the accordion 1157 var $card = $button.closest('.accordion-item'); 1158 var targetId = $button.attr('data-bs-target') || $button.attr('data-target'); 1159 var $targetCollapse = $(targetId); 1160 1161 // Just add scroll behavior when it opens 1162 $targetCollapse.one('shown.bs.collapse', function() { 1163 setTimeout(function() { 1164 $('html, body').animate({ 1165 scrollTop: $card.offset().top - 80 1166 }, 100); 1167 }, 100); 1168 }); 1169 } 1170});*/ 1171 1172}); 1173 1174// ===== 10. WINDOW LOAD AND SCROLL HANDLERS ===== 1175 1176// Window resize handler with debouncing 1177let resizeTimer; 1178$(window).on('resize', function() { 1179 console.log('Resize event triggered'); 1180 1181 // Use debounce to prevent too many calls 1182 clearTimeout(resizeTimer); 1183 resizeTimer = setTimeout(function() { 1184 console.log('Executing resize handler'); 1185 1186 // Handle responsive images with isResize=true 1187 handleResponsiveImages(true); 1188 1189 // Adjust heights 1190 const headerHeight = $('header').outerHeight(true); 1191 const footerHeight = $('footer').outerHeight(true); 1192 const contentHeight = $(window).height() - headerHeight - footerHeight; 1193 const fixedContentHeight = $(window).height() - footerHeight; 1194 1195 if ($('header').hasClass('fixed-top')) { 1196 $('#main').css({ 'min-height': fixedContentHeight }); 1197 } else { 1198 $('#main').css({ 'min-height': contentHeight }); 1199 } 1200 1201 // Mobile menu handling 1202 if ($(window).width() > 768) { 1203 $('body').removeClass('modal-open mobile-open'); 1204 $('html').removeClass('modal-open'); 1205 } 1206 }, 250); // Wait for 250ms after the last resize event 1207}); 1208 1209// Window load handler 1210$(window).on("load", function() { 1211 handleResponsiveImages(false); 1212 1213 new MiniLazyload({ 1214 rootMargin: "500px", 1215 threshold: .5 1216 }); 1217 1218 if ($("header").is(".fixed-top")) { 1219 var navbarHeight = $(".navbar").outerHeight(true); 1220 if ($("#main").children(":first").is(".pt-0")) { 1221 $("#main").css({"padding-top": 0}); 1222 } else { 1223 $("#main").css({"padding-top": navbarHeight}); 1224 } 1225 if ($("#main").children(":first").is(".text-white")) { 1226 $(".desktop-navbar:not(.on-end)").addClass("menu-white"); 1227 } else { 1228 $(".desktop-navbar:not(.on-end)").removeClass("menu-white"); 1229 } 1230 } 1231 1232 // Popup handling 1233 if (typeof($('[id*="popup-"]')) !== "undefined") { 1234 $('[id*="popup-"]').on('click', function() { 1235 $.cookie('cta-cookie', 'open', { expires: 1 }); 1236 }); 1237 1238 if ($.cookie('cta-cookie') !== 'open') { 1239 $('[id*="popup-"]').modal(); 1240 } 1241 } 1242}); 1243 1244// Combined scroll and load handler 1245$(window).on("scroll load", function() { 1246 // Header scroll effects 1247 if ($(this).scrollTop() > 10) { 1248 $('.desktop-navbar:not(.on-end)').removeClass("bg-transparent"); 1249 if ($('#main').children(':first').is('.text-white')) { 1250 $('.desktop-navbar:not(.on-end)').removeClass("menu-white"); 1251 } 1252 $('header').addClass('small-menu'); 1253 } else { 1254 $('.desktop-navbar:not(.on-end)').addClass("bg-transparent"); 1255 if ($('#main').children(':first').is('.text-white')) { 1256 $('.desktop-navbar:not(.on-end)').addClass("menu-white"); 1257 } 1258 $('header').removeClass('small-menu'); 1259 } 1260 1261 // Lazy video loading on scroll 1262 if ($('.lazy-video').length) { 1263 $('.lazy-video').each(function() { 1264 var lazyvideo = $(this); 1265 var lazyvideoParent = $(this).parent(); 1266 var viewBottom = $(window).scrollTop() + $(window).height(); 1267 var lazyTop = lazyvideo.offset().top; 1268 1269 if (lazyTop <= viewBottom) { 1270 var src = lazyvideo.attr('data-src'); 1271 lazyvideo.attr('src', src); 1272 if (!lazyvideoParent.hasClass('loaded')) { 1273 lazyvideoParent.trigger('load'); 1274 lazyvideoParent.trigger('play'); 1275 lazyvideoParent.addClass('loaded'); 1276 } 1277 } 1278 }); 1279 } 1280 1281 // Element tracking 1282 if ($('.track').length || $('.track-always').length) { 1283 var windowTop = $(window).scrollTop(); 1284 var windowBottom = windowTop + $(window).height(); 1285 1286 $('.track').each(function() { 1287 var divTop = $(this).offset().top; 1288 var divBottom = divTop + $(this).height(); 1289 if ((divTop + divBottom) / 2 >= windowTop && 1290 (divTop + divBottom) / 2 <= windowBottom) { 1291 $(this).addClass('in-view'); 1292 } 1293 }); 1294 1295 $('.track-always').each(function() { 1296 var divTop = $(this).offset().top; 1297 var divBottom = divTop + $(this).height(); 1298 if ((divTop + divBottom) / 2 >= windowTop && 1299 (divTop + divBottom) / 2 <= windowBottom) { 1300 $(this).addClass('in-view'); 1301 } else { 1302 $(this).removeClass('in-view'); 1303 } 1304 }); 1305 } 1306}); 1307 1308// Funkce pro pÅidánà Äásti URL za lomÃtkem jako tÅÃdy do body 1309function addPathAsClassToBody() { 1310 // ZÃskánà URL cesty za doménou 1311 const path = window.location.pathname; 1312 1313 // OdstranÄnà poÄáteÄnÃho lomÃtka a zÃskánà prvé Äásti cesty 1314 // NapÅÃklad z "/produkty/elektronika" zÃskáme "produkty" 1315 const firstPathSegment = path.split('/').filter(segment => segment !== '')[0] || 'home'; 1316 1317 // PÅidánà tÅÃdy do body elementu 1318 document.body.classList.add(firstPathSegment); 1319 1320 // Pro úÄely ladÄnà můžeme vypsat do konzole 1321 //console.log(`PÅidána tÅÃda '${firstPathSegment}' do body elementu`); 1322} 1323// Initialize tooltips 1324$('[data-toggle="tooltip"],[data-toggle-second="tooltip"]').tooltip({ trigger: 'hover' }); 1325// Funkce pro dynamické pÅepoÄÃtávánà cen podle hashtagu v URL 1326function updatePricesFromHash() { 1327 // Zkontrolujeme, zda existuje div s class "countPrice" 1328 const countPriceDiv = document.querySelector('.countPrice'); 1329 if (!countPriceDiv) { 1330 return; // Pokud div neexistuje, funkce se ukonÄà 1331 } 1332 1333 // PÅeÄteme URL a extrahujeme hashtag 1334 const hash = window.location.hash; 1335 if (!hash || hash.length <= 1) { 1336 return; // Pokud nenà hashtag, ukonÄÃme 1337 } 1338 1339 // Extrahujeme hodnotu z hashtagu (odstranÃme #) 1340 const hashValue = hash.substring(1); 1341 1342 // RozdÄlÃme podle stÅednÃku na ÄÃselnou a textovou Äást 1343 const hashParts = hashValue.split(';'); 1344 const priceValue = hashParts[0]; 1345 const nameValue = hashParts[1] ? decodeURIComponent(hashParts[1]) : ''; // Dekódujeme URL encoding 1346 1347 // PÅevedeme ÄÃselnou Äást na ÄÃslo 1348 const basePrice = parseFloat(priceValue); 1349 1350 // Zkontrolujeme, zda je hodnota platné ÄÃslo 1351 if (isNaN(basePrice) || basePrice <= 0) { 1352 return; // Pokud nenà platné ÄÃslo, ukonÄÃme 1353 } 1354 1355 // Najdeme prvky s cenami v rámci countPrice divu 1356 const highPriceElement = countPriceDiv.querySelector('.yourPriceHigh'); 1357 const middlePriceElement = countPriceDiv.querySelector('.yourPriceMiddle'); 1358 const lowPriceElement = countPriceDiv.querySelector('.yourPriceLow'); 1359 const nameElement = countPriceDiv.querySelector('.yourPriceName'); 1360
1361 // Aktualizujeme ceny podle koeficientů 1362 if (highPriceElement) { 1363 highPriceElement.textContent = Math.round(basePrice).toLocaleString('cs-CZ'); 1364 } 1365 1366 if (middlePriceElement) { 1367 const middlePrice = basePrice * 0.75; 1368 middlePriceElement.textContent = Math.round(middlePrice).toLocaleString('cs-CZ'); 1369 } 1370 1371 if (lowPriceElement) { 1372 const lowPrice = basePrice * 0.5; 1373 lowPriceElement.textContent = Math.round(lowPrice).toLocaleString('cs-CZ'); 1374 } 1375 1376 // Aktualizujeme název produktu, pokud je textová Äást k dispozici 1377 if (nameElement && nameValue) { 1378 nameElement.textContent = nameValue; 1379 } 1380} 1381 1382// SpustÃme funkci pÅi naÄtenà stránky 1383document.addEventListener('DOMContentLoaded', updatePricesFromHash); 1384 1385// SpustÃme funkci pÅi zmÄnÄ hashtagu (pokud uživatel zmÄnà URL) 1386window.addEventListener('hashchange', updatePricesFromHash); 1387 1388// Nová jednoduchá funkce pro sledovánà scrollu a zmÄnu barev 1389function initSectionColorTransition() { 1390 const sections = document.querySelectorAll('.changeSectionColor'); 1391 1392 // Pokud nejsou žádné sekce s tÅÃdou changeSectionColor, ukonÄÃme 1393 if (sections.length === 0) return; 1394 1395 let currentActiveIndex = 0; 1396 1397 let lastDefinedColor = '#ffffff'; // Uchováváme poslednà definovanou barvu 1398 1399 // Funkce pro zÃskánà barvy ze sekce (z class) 1400 function getSectionColor(section) { 1401 const classList = section.className; 1402 const colorMatch = classList.match(/data-sectionColor;([a-fA-F0-9]{6})/); 1403 1404 if (colorMatch && colorMatch[1]) { 1405 return '#' + colorMatch[1]; 1406 } 1407 1408 return null; // VrátÃme null mÃsto fallback barvy 1409 } 1410 1411 // Funkce pro aplikaci barvy na vÅ¡echna sekce 1412 function applyColor(sectionIndex) { 1413 if (sectionIndex !== currentActiveIndex && sectionIndex >= 0 && sectionIndex < sections.length) { 1414 currentActiveIndex = sectionIndex; 1415 const sectionColor = getSectionColor(sections[currentActiveIndex]); 1416 1417 // Pokud má sekce definovanou barvu, aktualizujeme lastDefinedColor 1418 if (sectionColor !== null) { 1419 lastDefinedColor = sectionColor; 1420 } 1421 // Jinak použÃváme poslednà definovanou barvu (lastDefinedColor zůstává stejná) 1422 1423 // Aplikujeme aktuálnà lastDefinedColor na vÅ¡echna sekce 1424 sections.forEach(section => { 1425 section.style.setProperty('background-color', lastDefinedColor, 'important'); 1426 }); 1427 } 1428 } 1429 1430 // Hlavnà funkce pro detekci aktivnà sekce 1431 function detectActiveSection() { 1432 const windowHeight = window.innerHeight; 1433 const scrollTop = window.pageYOffset || document.documentElement.scrollTop; 1434 1435 // Threshold: 50% výšky viewportu 1436 const threshold = windowHeight * 0.5; 1437 1438 // Projdeme vÅ¡echna sekce a najdeme tu, která je nejvÃce viditelná 1439 let bestMatch = currentActiveIndex; // Zachováme souÄasný index jako výchozà 1440 let bestVisibility = 0; 1441 let foundVisibleSection = false; 1442 1443 sections.forEach((section, index) => { 1444 const rect = section.getBoundingClientRect(); 1445 const sectionTop = rect.top; 1446 const sectionBottom = rect.bottom; 1447 1448 // VypoÄÃtáme, kolik procent sekce je viditelných 1449 let visibleHeight = 0; 1450 1451 if (sectionTop < 0 && sectionBottom > 0) { 1452 // Sekce zaÄÃná nad viewportem 1453 visibleHeight = Math.min(sectionBottom, windowHeight); 1454 } else if (sectionTop >= 0 && sectionTop < windowHeight) { 1455 // Sekce zaÄÃná ve viewportu 1456 visibleHeight = Math.min(sectionBottom, windowHeight) - sectionTop; 1457 } 1458 1459 // Pokud je viditelná alespoÅ polovina threshold, může být aktivnà 1460 if (visibleHeight >= threshold) { 1461 foundVisibleSection = true; 1462 const visibility = visibleHeight / windowHeight; 1463 if (visibility > bestVisibility) { 1464 bestVisibility = visibility; 1465 bestMatch = index; 1466 } 1467 } 1468 }); 1469 1470 // Aplikujeme barvu pouze pokud jsme naÅ¡li viditelnou sekci 1471 if (foundVisibleSection) { 1472 applyColor(bestMatch); 1473 } 1474 // Pokud žádná sekce nenà dostateÄnÄ viditelná, zachováme souÄasnou barvu 1475 } 1476 1477 // Inicializace - najdeme prvé sekce s definovanou barvou 1478 let initialColorSet = false;
1479 for (let i = 0; i < sections.length; i++) { 1480 const color = getSectionColor(sections[i]); 1481 if (color !== null) { 1482 lastDefinedColor = color; 1483 initialColorSet = true; 1484 break; 1485 } 1486 } 1487 1488 // Pokud žádná sekce nemá definovanou barvu, použijeme výchozà 1489 if (!initialColorSet) { 1490 lastDefinedColor = '#ffffff'; 1491 } 1492 1493 // Aplikujeme poÄáteÄnà barvu na vÅ¡echna sekce 1494 sections.forEach(section => { 1495 section.style.setProperty('background-color', lastDefinedColor, 'important'); 1496 }); 1497 1498 // SpustÃme detekci aktivnà sekce hned po inicializaci 1499 detectActiveSection(); 1500 1501 // Throttled scroll handler s unikátnÃm názvem promÄnné 1502 let colorTransitionTicking = false; 1503 function onColorTransitionScroll() { 1504 if (!colorTransitionTicking) { 1505 requestAnimationFrame(() => { 1506 detectActiveSection(); 1507 colorTransitionTicking = false; 1508 }); 1509 colorTransitionTicking = true; 1510 } 1511 } 1512 1513 // Event listenery 1514 window.addEventListener('scroll', onColorTransitionScroll); 1515 window.addEventListener('resize', detectActiveSection); 1516} 1517 1518// SpustÃme po naÄtenà DOMu 1519document.addEventListener('DOMContentLoaded', initSectionColorTransition); 1520 1521// ===== 11. IMAGE COMPARE SLIDER ===== 1522function initImageCompareSliders() { 1523 // Vynutà -full verzi obrázku (compare potÅebuje plnou kvalitu) 1524 function toFullSizeUrl(url) { 1525 if (!url) return url; 1526 url = url.replace(/-(?:small|medium|large)(?=[\.\-])/, '-full'); 1527 url = url.replace(/\/(small|medium|large)\//, '/full/'); 1528 return url; 1529 } 1530 1531 document.querySelectorAll('.imageCompareSlider').forEach(container => { 1532 const images = container.querySelectorAll('img'); 1533 if (images.length < 2) return; 1534 1535 const img1Src = toFullSizeUrl(images[0].getAttribute('data-src') || images[0].getAttribute('src')); 1536 const img2Src = toFullSizeUrl(images[1].getAttribute('data-src') || images[1].getAttribute('src')); 1537 const img1Alt = images[0].getAttribute('alt') || ''; 1538 const img2Alt = images[1].getAttribute('alt') || ''; 1539 1540 // Najdeme .item-1 a nahradÃme obsah 1541 const itemCol = container.querySelector('.item-1'); 1542 if (!itemCol) return; 1543 1544 itemCol.innerHTML = ` 1545 <div class="image-compare-wrapper"> 1546 <img src="${img2Src}" alt="${img2Alt}" class="image-compare-after img-fluid" draggable="false"> 1547 <div class="image-compare-before"> 1548 <img src="${img1Src}" alt="${img1Alt}" draggable="false"> 1549 </div> 1550 <div class="image-compare-handle"></div> 1551 </div> 1552 `; 1553 1554 const wrapper = itemCol.querySelector('.image-compare-wrapper'); 1555 const beforeDiv = itemCol.querySelector('.image-compare-before'); 1556 const handle = itemCol.querySelector('.image-compare-handle'); 1557 const beforeImg = beforeDiv.querySelector('img'); 1558 1559 // Inline styly pro wrapper 1560 Object.assign(wrapper.style, { 1561 position: 'relative', 1562 overflow: 'hidden', 1563 cursor: 'col-resize', 1564 userSelect: 'none', 1565 webkitUserSelect: 'none' 1566 }); 1567 1568 // After obrázek (spodnà vrstva â urÄuje rozmÄr) 1569 const afterImg = wrapper.querySelector('.image-compare-after'); 1570 Object.assign(afterImg.style, { 1571 display: 'block', 1572 width: '100%', 1573 pointerEvents: 'none' 1574 }); 1575 1576 // Before overlay (oÅezávaný) 1577 Object.assign(beforeDiv.style, { 1578 position: 'absolute', 1579 top: '0', 1580 left: '0', 1581 height: '100%', 1582 width: '50%', 1583 overflow: 'hidden' 1584 }); 1585 1586 // Before obrázek â Å¡ÃÅka = celý wrapper, ne jen oÅez 1587 Object.assign(beforeImg.style, { 1588 display: 'block', 1589 height: '100%', 1590 objectFit: 'cover', 1591 pointerEvents: 'none' 1592 }); 1593 1594 // Handle (svislá Äára + koleÄko) 1595 Object.assign(handle.style, { 1596 position: 'absolute', 1597 top: '0', 1598 left: '50%', 1599 transform: 'translateX(-50%)', 1600 width: '4px', 1601 height: '100%', 1602 background: '#000', 1603 cursor: 'col-resize', 1604 zIndex: '2' 1605 }); 1606
1607 function syncBeforeImgWidth() { 1608 beforeImg.style.width = wrapper.offsetWidth + 'px'; 1609 } 1610 syncBeforeImgWidth(); 1611 1612 let isTouchDragging = false; 1613 1614 function updatePosition(clientX) { 1615 const rect = wrapper.getBoundingClientRect(); 1616 let pos = (clientX - rect.left) / rect.width; 1617 pos = Math.max(0, Math.min(1, pos)); 1618 1619 beforeDiv.style.width = (pos * 100) + '%'; 1620 handle.style.left = (pos * 100) + '%'; 1621 syncBeforeImgWidth(); 1622 } 1623 1624 // Desktop â handle sleduje kurzor pÅi hoveru 1625 wrapper.addEventListener('mousemove', (e) => { 1626 updatePosition(e.clientX); 1627 }); 1628 1629 // Touch â drag na mobilu i na notebooku s touchscreenem 1630 wrapper.addEventListener('touchstart', (e) => { 1631 isTouchDragging = true; 1632 updatePosition(e.touches[0].clientX); 1633 }, { passive: true }); 1634 1635 document.addEventListener('touchmove', (e) => { 1636 if (!isTouchDragging) return; 1637 updatePosition(e.touches[0].clientX); 1638 e.preventDefault(); 1639 }, { passive: false }); 1640 1641 document.addEventListener('touchend', () => { 1642 isTouchDragging = false; 1643 }); 1644 1645 // Resize sync 1646 window.addEventListener('resize', syncBeforeImgWidth); 1647 }); 1648} 1649 1650document.addEventListener('DOMContentLoaded', initImageCompareSliders); 1651 1652// SpuÅ¡tÄnà funkce po naÄtenà stránky 1653document.addEventListener('DOMContentLoaded', addPathAsClassToBody); 1654 1655// Initialize tooltips 1656$('[data-toggle="tooltip"],[data-toggle-second="tooltip"]').tooltip({ trigger: 'hover' }); 1657 1658// ===== YOUTUBE MOVE ===== 1659document.addEventListener('DOMContentLoaded', function() { 1660 const section = document.querySelector('.youtubeMove'); 1661 if (!section) return; 1662 1663 const youtubeEl = section.querySelector('.youtubeToMove'); 1664 if (!youtubeEl) return; 1665 1666 const articleList = section.querySelector('.article-list-1'); 1667 if (!articleList) return; 1668 1669 const firstListItem = articleList.querySelector('.list-item'); 1670 articleList.insertBefore(youtubeEl, firstListItem); 1671});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.