1// services 2 3const services = document.querySelectorAll(".services-list li"); 4 5const handleIntersection = (entries, observer) => { 6 entries.forEach(entry => { 7 if (entry.isIntersecting) { 8 entry.target.classList.add('on'); 9 observer.unobserve(entry.target); 10 } 11 }); 12}; 13 14const observerOptions = { 15 root: null, 16 rootMargin: '50px', 17 threshold: 0.5 18}; 19 20 21const observer = new IntersectionObserver(handleIntersection, observerOptions); 22 23if(services.length > 0){ 24 services.forEach(service => { 25 observer.observe(service); 26 }); 27} 28 29// service 30 31const serviceBody = document.querySelectorAll(".service-description"); 32 33if(serviceBody.length > 0) serviceBody.forEach(service => observer.observe(service)); 34 35 36// moto 37 38const homeMoto = document.querySelector(".home-moto"); 39 40if(homeMoto) observer.observe(homeMoto); 41 42// home contact 43 44const homeContact = document.querySelector(".home-contact--wrapper"); 45if(homeContact) observer.observe(homeContact); 46 47 48// home services 49 50const homeSerNav = document.querySelector(".home-services .services-nav"); 51const homeServices = document.querySelector(".swiperHomeService"); 52 53if(homeSerNav && homeServices){ 54 observer.observe(homeSerNav); 55 observer.observe(homeServices); 56} 57 58// standard transition 59 60const anim = document.querySelectorAll(".anim"); 61 62if(anim.length > 0){ 63 anim.forEach(el => observer.observe(el)); 64} 65 66// careers 67 68const careersSwiper = new Swiper('.careers-carousel.swiper', { 69 navigation: { 70 nextEl: '.swiper-button-next', 71 prevEl: '.swiper-button-prev', 72 }, 73}); 74 75// vacanvies 76 77const vacancies = document.querySelectorAll(".vacancy"); 78 79if(vacancies.length > 0){ 80 vacancies.forEach(vacancy => vacancy.addEventListener("click", function(){ 81 vacancy.classList.toggle("open"); 82 const vacancyDescription = vacancy.querySelector(".vacancy--description"); 83 toggleHeight(vacancyDescription); 84 })); 85} 86 87function toggleHeight(item, start = 0) { 88 if (item.style.height === '0px' || item.style.height === '' || item.style.height === `${start}px` ) { 89 // Î¥ÏολÏγιÏε Ïο ÏÏαγμαÏÎ¹ÎºÏ ÏÏÎ¿Ï 90 console.log(start); 91 item.style.height = 'auto'; 92 const height = item.scrollHeight + 'px'; 93 item.style.height = `${start}px`; // ÎÏαναÏοÏά ÏÏο 0 94 95 // Îεκίνα Ïο transition 96 setTimeout(() => { 97 item.style.height = height; 98 }, 10); // ÎικÏή ÎºÎ±Î¸Ï ÏÏÎÏηÏη για να εÏαÏμοÏÏεί Ïο transition 99 } else { 100 // ÎλείÏιμο ÏÎ¿Ï ÏÏοιÏÎµÎ¯Î¿Ï 101 item.style.height = item.scrollHeight + 'px';
101 // ÎÏÎ¯Î¶Î¿Ï Î¼Îµ Ïο ÏÏÎÏον ÏÏÎ¿Ï Î³Î¹Î± να ξεκινήÏει Ïο transition 102 setTimeout(() => { 103 item.style.height = `${start}px`; 104 }, 10); 105 } 106} 107 108// home services swiper 109 110const autoplayDuration = 5000; 111 112const servicesThumbs = new Swiper('.services-nav.swiper', { 113 spaceBetween: 10, 114 slidesPerView: "auto", 115 freeMode: true, 116 watchSlidesProgress: true, 117 breakpoints : { 118 920 : { 119 spaceBetween: 0, 120 slidesPerView: 5, 121 }, 122 }, 123}); 124const servicesSwiper = new Swiper('.swiperHomeService.swiper', { 125 navigation: { 126 nextEl: '.swiper-button-next', 127 prevEl: '.swiper-button-prev', 128 }, 129 thumbs: { 130 swiper: servicesThumbs 131 }, 132 autoplay: { 133 delay: autoplayDuration, 134 disableOnInteraction: false, 135 }, 136 on: { 137 autoplayTimeLeft(s, time, progress) { 138 const active = document.querySelector(".home-services .service-nav-item.swiper-slide-thumb-active span"); 139 let move = ((autoplayDuration - time)/autoplayDuration) * -100; 140 if(move === -100) move = 0; 141 active.style.setProperty('--pos', `${move}%`); 142 }, 143 init(swiper) { 144 syncServiceCanvasLink(swiper); 145 }, 146 slideChange(swiper) { 147 syncServiceCanvasLink(swiper); 148 } 149 } 150}); 151 152function syncServiceCanvasLink(swiper) { 153 const canvasLink = document.getElementById('serviceCanvasLink'); 154 if (!canvasLink || !swiper) return; 155 156 const activeSlide = swiper.slides[swiper.activeIndex]; 157 const slideLink = activeSlide && activeSlide.querySelector('.slide-link'); 158 if (slideLink && slideLink.getAttribute('href')) { 159 canvasLink.setAttribute('href', slideLink.getAttribute('href')); 160 } 161} 162 163if(servicesSwiper) servicesSwiper.autoplay.stop(); 164 165const autoplayObserver = new IntersectionObserver((entries) => { 166 entries.forEach(entry => { 167 if (entry.isIntersecting) { 168 // Το ÏÏοιÏείο είναι οÏαÏÏ - ξεκίνα Ïο autoplay 169 servicesSwiper.autoplay.start(); 170 } else { 171 // Το ÏÏοιÏείο δεν είναι οÏαÏÏ - ÏÏαμάÏα Ïο autoplay 172 servicesSwiper.autoplay.stop(); 173 } 174 }); 175}); 176 177if(homeServices) autoplayObserver.observe(homeServices); 178 179 180// menu 181 182const body = document.querySelector("body"); 183const panel = document.querySelector(".panel"); 184const menu = document.querySelector(".menu"); 185const menuBtn = document.querySelector(".menu-btn"); 186 187if(menu && menuBtn){ 188 menuBtn.addEventListener("click", function(){ 189 body.classList.toggle("menu-on"); 190 body.classList.toggle("stop-scrolling"); 191 menu.classList.toggle("on"); 192 panel.classList.toggle("on"); 193 }); 194} 195 196if(panel){ 197 panel.addEventListener("click", function(){ 198 body.classList.toggle("menu-on"); 199 body.classList.toggle("stop-scrolling"); 200 menu.classList.toggle("on"); 201 panel.classList.toggle("on"); 202 }); 203} 204 205// transitions 206 207// hero 208 209const hero =document.querySelector(".hero"); 210 211if(hero){ 212 observer.observe(hero); 213 setTimeout(() => hero.classList.add("clear"), 5000); 214} 215 216// setion header 217 218const headerIntersection = (entries, headerObserverOptions) => { 219 entries.forEach(entry => { 220 if (entry.isIntersecting) { 221 entry.target.classList.add('on'); 222 observer.unobserve(entry.target); 223 } 224 }); 225}; 226 227const headerObserverOptions = { 228 root: null, 229 rootMargin: window.innerWidth > 920 ? '-100px' : '20px', 230 threshold: 0.5 231}; 232 233const headerObserver = new IntersectionObserver(headerIntersection, headerObserverOptions); 234 235const sectionHeaders = document.querySelectorAll(".section-header"); 236 237if(sectionHeaders.length > 0){ 238 sectionHeaders.forEach(header => headerObserver.observe(header)); 239} 240 241 242// home transitions 243 244const homeClients = document.querySelector(".home-clients-list"); 245if(homeClients) headerObserver.observe(homeClients); 246 247const clientsSwiperEl = document.querySelector('.clientsSwiper'); 248if (clientsSwiperEl) { 249 new Swiper('.clientsSwiper', { 250 slidesPerView: 2.5, 251 spaceBetween: 24, 252 loop: true,
253 speed: 800, 254 autoplay: { 255 delay: 2500, 256 disableOnInteraction: false, 257 }, 258 breakpoints: { 259 580: { 260 slidesPerView: 3.5, 261 spaceBetween: 32, 262 }, 263 950: { 264 slidesPerView: 5, 265 spaceBetween: 40, 266 }, 267 1280: { 268 slidesPerView: 6, 269 spaceBetween: 50, 270 }, 271 }, 272 }); 273} 274 275// home ai 276 277const homeAi = document.querySelector(".home-aibanner"); 278if(homeAi) headerObserver.observe(homeAi); 279 280 281// service hash 282 283document.addEventListener('DOMContentLoaded', () => { 284 // Î£Ï Î½Î¬ÏÏηÏη για Ïην εμÏάνιÏη ÏÎ¿Ï ÏÏÏÏÎ¿Ï div 285 function showSectionFromHash() { 286 // ΠαίÏÎ½Î¿Ï Î¼Îµ Ïο hash αÏÏ Ïην URL (Ï.Ï. "#area", "#map", "#info") 287 const hash = window.location.hash; 288 289 console.log(hash); 290 291 // ÎÏÏÎ²Î¿Ï Î¼Îµ Ïλα Ïα ÏμήμαÏα ÏεÏιεÏομÎÎ½Î¿Ï 292 document.querySelectorAll('.service-box').forEach(section => { 293 section.classList.remove('active'); 294 }); 295 296 const activeNav = document.querySelector(".service-nav-item.selected"); 297 if(activeNav) activeNav.classList.remove("selected"); 298 299 300 // Îν Ï ÏάÏÏει hash και ανÏιÏÏοιÏεί Ïε κάÏοιο ID 301 if (hash) { 302 const targetId = hash.substring(1); // ÎÏαιÏοÏμε Ïο "#" 303 if(window.innerWidth > 620){ 304 const targetSection = document.getElementById(targetId); 305 306 if (targetSection) { 307 targetSection.classList.add('active'); // ÎμÏÎ±Î½Î¯Î¶Î¿Ï Î¼Îµ Ïο εÏιλεγμÎνο Ïμήμα 308 } 309 }else{ 310 console.log("target : " + targetId); 311 const slides = document.querySelectorAll(".singleServiceSwiper .swiper-slide"); 312 for(let i = 0; i < slides.length; i++){ 313 console.log(slides[i].id); 314 if(slides[i].id === targetId){ 315 initSwiper(i); 316 break; 317 } 318 } 319 320 } 321 const linkText = `.service-nav-item a[href="#${targetId}"]`; 322 const selectedNav = document.querySelector(linkText); 323 if(selectedNav){ 324 selectedNav.closest(".service-nav-item").classList.add("selected"); 325 const title = selectedNav.querySelector("span"); 326 // console.log(title.innerText); 327 const breadcrumbsTitle = document.querySelector(".breadcrumbs .active-title"); 328 breadcrumbsTitle.innerText = title.innerText; 329 } 330 } 331 // Îν δεν Ï ÏάÏÏει hash, μÏοÏείÏε να αÏοÏαÏίÏεÏε αν θα εμÏανίζεÏαι κάÏι default 332 // Ï.Ï. να εμÏανιÏÏεί Ïο #AREA αν δεν Ï ÏάÏÏει hash 333 else { 334 const defaultSection = document.getElementById('data-gathering'); 335 if (defaultSection) { 336 defaultSection.classList.add('active'); 337 const selectedNav = document.querySelector('.service-nav-item a[href="#data-gathering"]'); 338 if(selectedNav) selectedNav.closest(".service-nav-item").classList.add("selected"); 339 const breadcrumbsTitle = document.querySelector(".breadcrumbs .active-title"); 340 breadcrumbsTitle.innerText = "Data Gathering & Analysis"; 341 } 342 } 343 } 344 345 // Îαλεί Ïη ÏÏ Î½Î¬ÏÏηÏη ÏÏαν ÏοÏÏÏνει η Ïελίδα 346 showSectionFromHash(); 347 348 // Îαλεί Ïη ÏÏ Î½Î¬ÏÏηÏη ÏÏαν αλλάζει Ïο hash (Ï.Ï. ο ÏÏήÏÏÎ·Ï ÎºÎ¬Î½ÎµÎ¹ copy-paste URL με διαÏοÏεÏÎ¹ÎºÏ hash) 349 window.addEventListener('hashchange', showSectionFromHash); 350}); 351 352 353let singleServiceSwiper; // ÎηλÏÏÏε Ïη μεÏαβληÏή για Ïο Swiper global (εκÏÏÏ ÏÎ·Ï ÏÏ Î½Î¬ÏÏηÏηÏ) 354 355function initSwiper(start = 0) { 356 // ÎλÎγξÏε αν η οθÏνη είναι μικÏÏÏεÏη ή ίÏη με 620px 357 if (window.innerWidth <= 620) { 358 // Îν δεν ÎÏει ήδη αÏÏικοÏοιηθεί Ïο Swiper 359 if (!singleServiceSwiper) { 360 singleServiceSwiper = new Swiper('.singleServiceSwiper.swiper', { 361 // Îι ÏÏ Î¸Î¼Î¯ÏÎµÎ¹Ï ÏÎ¿Ï Swiper ÏÎ±Ï 362 slidesPerView: 1, 363 spaceBetween: 16, 364 navigation: { 365 nextEl: '.swiper-button-next', 366 prevEl: '.swiper-button-prev', 367 }, 368 initialSlide : start, 369 // Î ÏοÏθÎÏÏε ÏÏÎµÏ ÏÏ Î¸Î¼Î¯ÏÎµÎ¹Ï Î¸ÎλεÏε 370 }); 371 console.log('Swiper initialized.'); 372 } 373 } else { 374 // Îν η οθÏνη είναι μεγαλÏÏεÏη αÏÏ 620px και Ïο Swiper είÎ
374½Î±Î¹ ενεÏÎ³Ï 375 if (singleServiceSwiper) { 376 singleServiceSwiper.destroy(true, true); // ÎαÏαÏÏÏÎÏει Ïο Swiper και καθαÏίζει Ïο DOM 377 singleServiceSwiper = null; // Îηδενίζει Ïη μεÏαβληÏή 378 console.log('Swiper destroyed.'); 379 } 380 } 381} 382 383// 1. ÎαλÎÏÏε Ïην initSwiper ÏÏαν ÏοÏÏÏνει η Ïελίδα 384// document.addEventListener('DOMContentLoaded', initSwiper); 385 386// 2. ÎαλÎÏÏε Ïην initSwiper ÏÏαν αλλάζει Ïο μÎÎ³ÎµÎ¸Î¿Ï ÏÎ¿Ï ÏαÏαθÏÏÎ¿Ï 387// ΧÏηÏιμοÏοιήÏÏε debouncing για καλÏÏεÏη αÏÏδοÏη Ïε resize events 388let resizeTimeout; 389window.addEventListener('resize', () => { 390 clearTimeout(resizeTimeout); 391 resizeTimeout = setTimeout(initSwiper, 200); // ΠεÏιμÎνει 200ms μεÏά Ïο resize για να ÏÏÎξει 392}); 393 394 395// clients 396 397const clientsWrapper = document.querySelector(".clients-list"); 398const clientsList = document.querySelector(".clients-list ul"); 399const clientsBtn = document.querySelector(".clients-all"); 400 401if(clientsBtn){ 402 clientsBtn.addEventListener("click", function(){ 403 clientsWrapper.classList.toggle("open"); 404 toggleHeight(clientsList, 78); 405 }); 406} 407 408// video observer 409 410const handleVideoIntersection = (entries, observer) => { 411 entries.forEach(entry => { 412 if (entry.isIntersecting) { 413 entry.target.play(); 414 }else{ 415 entry.target.pause(); 416 } 417 }); 418}; 419 420const videoObserverOptions = { 421 root: null, 422 rootMargin: '0px', 423 threshold: 0.25 424}; 425 426 427const videoObserver = new IntersectionObserver(handleVideoIntersection, videoObserverOptions); 428 429const videos = document.querySelectorAll("video"); 430if(videos.length > 0){ 431 videos.forEach(vid => videoObserver.observe(vid)); 432} 433 434// service canvas 435 436// ÎÎ´Ï Î¼ÏοÏÎµÎ¯Ï Î½Î± ÏÏοÏθÎÏÎµÎ¹Ï Ïον JavaScript κÏδικα για να ÏÏεδιάÏÎµÎ¹Ï ÏÏο canvas 437const canvas = document.getElementById('serviceCanvas'); 438 439if(canvas){ 440 const ctx = canvas.getContext('2d'); 441 442 // ÎÏÎ¯Î¶Î¿Ï Î¼Îµ ÏÎ¹Ï Î´Î¹Î±ÏÏάÏÎµÎ¹Ï ÏÎ¿Ï canvas 443 const canvasWidth = canvas.width; 444 const canvasHeight = canvas.height; 445 446 // ÎÏÎ¯Î¶Î¿Ï Î¼Îµ ÏÎ¹Ï ÏαÏαμÎÏÏÎ¿Ï Ï Î³Î¹Î± Ïα ÏεÏÏάγÏνα 447 const numberOfSquares = 40; 448 const minSquareSize = 5; 449 const maxSquareSize = 20; 450 const delayPerSquare = 200; 451 452 const squares = []; 453 const bigSquares = []; 454 const maxAttempts = 50; 455 456 function drawRandomSquare(color) { 457 // ÎÏÎ¯Î¶Î¿Ï Î¼Îµ Ïο ÏÏÏμα 458 ctx.fillStyle = color; 459 460 const squareSize = getRandomInt(10, 25) 461 462 // ÎÎ·Î¼Î¹Î¿Ï ÏγοÏμε ÏÏ ÏÎ±Î¯ÎµÏ ÏÏ Î½ÏεÏαγμÎÎ½ÎµÏ Î³Î¹Î± κάθε ÏεÏÏάγÏνο 463 const randomX = Math.random() * (canvasWidth - squareSize); 464 const randomY = Math.random() * (canvasHeight - squareSize); 465 466 // ΣÏÎµÎ´Î¹Î¬Î¶Î¿Ï Î¼Îµ Ïο ÏεÏÏάγÏνο 467 ctx.fillRect(randomX, randomY, squareSize, squareSize); 468 } 469 470 function getRandomInt(min, max) { 471 min = Math.ceil(min); // ΣÏÏÎ¿Î³Î³Ï Î»Î¿Ïοιεί ÏÏÎ¿Ï Ïα ÏÎ¬Î½Ï Ïο ελάÏιÏÏο ÏÏιο 472 max = Math.floor(max); // ΣÏÏÎ¿Î³Î³Ï Î»Î¿Ïοιεί ÏÏÎ¿Ï Ïα κάÏÏ Ïο μÎγιÏÏο ÏÏιο 473 return Math.floor(Math.random() * (max - min + 1)) + min; 474 } 475
476 // Î£Ï Î½Î¬ÏÏηÏη ελÎγÏÎ¿Ï ÏÏγκÏÎ¿Ï ÏÎ·Ï Î¼ÎµÏÎ±Î¾Ï Î´Ïο ÏεÏÏαγÏνÏν 477 function isColliding(square1, square2) { 478 return square1.x < square2.x + square2.size && 479 square1.x + square1.size > square2.x && 480 square1.y < square2.y + square2.size && 481 square1.y + square1.size > square2.y; 482 } 483 484 // Î£Ï Î½Î¬ÏÏηÏη για Ïη Î´Î·Î¼Î¹Î¿Ï Ïγία ενÏÏ ÏεÏÏαγÏÎ½Î¿Ï Î¼Îµ ÎλεγÏο ÏÏγκÏÎ¿Ï ÏÎ·Ï 485 function createSquare(color, isBig = false) { 486 let square; 487 let attempts = 0; 488 let isOverlapping = true; 489 490 while (isOverlapping && attempts < maxAttempts) { 491 // ÎÎ·Î¼Î¹Î¿Ï Ïγία ÏÏ ÏαίÏν ÏÏ Î½ÏεÏαγμÎνÏν 492 // const size = Math.floor(Math.random() * (maxSquareSize - 10 + 1)) + 10; 493 const size = isBig ? getRandomInt(30, 70) : getRandomInt(minSquareSize, maxSquareSize); 494 const x = Math.random() * (canvasWidth - size); 495 const y = Math.random() * (canvasHeight - size); 496 497 square = { 498 x, y, 499 size: 0, 500 color, 501 maxSize: size, 502 speed: Math.random() * 2 + 0.1, 503 isBig : isBig 504 }; 505 506 isOverlapping = false; 507 if(!isBig){ 508 for (const existingSquare of squares) { 509 if (isColliding(square, existingSquare)) { 510 isOverlapping = true; 511 break; 512 } 513 } 514 attempts++; 515 } 516 } 517 518 // ÎÏιÏÏÏÎÏÎ¿Ï Î¼Îµ Ïο ÏεÏÏάγÏνο μÏνο αν βÏÎθηκε θÎÏη ÏÏÏÎ¯Ï ÎµÏÎ¹ÎºÎ¬Î»Ï Ïη 519 if (isOverlapping && !isBig) { 520 console.log("Îεν βÏÎθηκε ελεÏθεÏη θÎÏη για Îνα ÏεÏÏάγÏνο μεÏά αÏÏ " + maxAttempts + " ÏÏοÏÏάθειεÏ."); 521 return null; 522 } 523 524 return square; 525 } 526 527 for (let i = 0; i < numberOfSquares; i++) { 528 // const color = (i % 3 == 0) ? '#1EFF1E' : '#F8F5F0'; 529 const color = '#F8F5F0'; 530 const newSquare = createSquare(color); 531 if (newSquare) { 532 squares.push(newSquare); 533 } 534 } 535 536 const bigSquareInterval = 2000; // 5 Î´ÎµÏ ÏεÏÏλεÏÏα 537 let lastBigSquareTime = Date.now(); 538 539 // ÎεÏαβληÏÎÏ Î³Î¹Î± Ïη διαÏείÏιÏη ÏÎ·Ï Î´Î·Î¼Î¹Î¿Ï ÏÎ³Î¯Î±Ï Î½ÎÏν ÏεÏÏαγÏνÏν 540 let newSquareCount = 0; 541 const newSquareInterval = 500; // Îάθε 0.5 Î´ÎµÏ ÏεÏÏλεÏÏα ÏÏοÏÏαθοÏμε να ÏÏοÏθÎÏÎ¿Ï Î¼Îµ νÎο ÏεÏÏάγÏνο 542 let lastNewSquareTime = Date.now(); 543 544 // Î£Ï Î½Î¬ÏÏηÏη animation loop 545 function animate() { 546 // ÎαθαÏÎ¯Î¶Î¿Ï Î¼Îµ Ïλο Ïο canvas 547 ctx.clearRect(0, 0, canvasWidth, canvasHeight); 548 549 // ΣÏÎµÎ´Î¹Î¬Î¶Î¿Ï Î¼Îµ Ïλα Ïα ÏεÏÏάγÏνα 550 squares.forEach(square => { 551 // Îν Ïο ÏεÏÏάγÏνο δεν ÎÏει ÏÏάÏει Ïο μÎγιÏÏο μÎγεθοÏ, Ïο μεγαλÏÎ½Î¿Ï Î¼Îµ 552 553 if (square.size < square.maxSize) { 554 square.size += square.speed; 555 if (square.size > square.maxSize) { 556 square.size = square.maxSize; // ÎξαÏÏÎ±Î»Î¯Î¶Î¿Ï Î¼Îµ ÏÏι δεν θα Ïο ξεÏεÏάÏει 557 } 558 } 559 560 // ΣÏÎµÎ´Î¹Î¬Î¶Î¿Ï Î¼Îµ Ïο ÏεÏÏάγÏνο με Ïο ÏÏÎÏον μÎγεθÏÏ ÏÎ¿Ï 561 ctx.fillStyle = square.color; 562 ctx.fillRect(square.x, square.y, square.size, square.size); 563 }); 564 565 // ÎλεγÏÎ¿Ï Î³Î¹Î± ÏÏοÏθήκη νÎÎ¿Ï ÏεÏÏαγÏÎ½Î¿Ï 566 const now = Date.now(); 567 if (squares.length >= numberOfSquares && now - lastNewSquareTime > newSquareInterval) { 568 // ÎÏαιÏοÏμε Ïο ÏÏÏÏο ÏεÏÏάγÏνο 569 squares.shift(); 570 571 // ÎÎ·Î¼Î¹Î¿Ï ÏγοÏμε Îνα νÎο ÏεÏÏάγÏνο 572 const color = '#F8F5F0'; 573 const newSquare = createSquare(color, 0); // Το νÎο ÏεÏÏάγÏνο ξεκινά αÏÏ Î¼ÎÎ³ÎµÎ¸Î¿Ï 0 574 if (newSquare) { 575 squares.push(newSquare); 576 } 577 578 newSquareCount++;
579 lastNewSquareTime = now; 580 } 581 582 bigSquares.forEach((square, index) => { 583 if (square.size < square.maxSize) { 584 square.size += square.speed; 585 if (square.size > square.maxSize) { 586 square.size = square.maxSize; 587 } 588 } 589 ctx.strokeStyle = '#1EFF1E'; 590 ctx.lineWidth = 1; 591 ctx.strokeRect(square.x, square.y, square.size, square.size); 592 593 if (index % 2 === 0) { 594 const lineLength = square.size * 2; 595 const centerX = square.x + square.size / 2; 596 const centerY = square.y + square.size / 2; 597 598 // Î ÏÏÏη κάθεÏη γÏαμμή 599 ctx.beginPath(); 600 ctx.moveTo(centerX, centerY - lineLength / 2); 601 ctx.lineTo(centerX, centerY + lineLength / 2); 602 ctx.strokeStyle = '#FF6CF5'; 603 ctx.stroke(); 604 605 // ÎεÏÏεÏη κάθεÏη γÏαμμή 606 ctx.beginPath(); 607 ctx.moveTo(centerX - lineLength / 2, centerY); 608 ctx.lineTo(centerX + lineLength / 2, centerY); 609 ctx.strokeStyle = '#FF6CF5'; 610 ctx.stroke(); 611 } 612 }); 613 if (now - lastBigSquareTime > bigSquareInterval) { 614 if (bigSquares.length >= 5) { 615 bigSquares.shift(); 616 } 617 618 const newBigSquare = createSquare('', true); 619 if (newBigSquare) { 620 bigSquares.push(newBigSquare); 621 } 622 623 lastBigSquareTime = now; 624 } 625 626 // Îαλεί ξανά Ïη ÏÏ Î½Î¬ÏÏηÏη ÏÏο εÏÏμενο διαθÎÏιμο frame 627 requestAnimationFrame(animate); 628 } 629 630 // Îεκινάμε Ïο animation 631 animate(); 632} 633 634// share modal 635(function initShareModal() { 636 const openBtns = document.querySelectorAll('[data-share-open]'); 637 const modal = document.querySelector('[data-share-modal]'); 638 if (!openBtns.length || !modal) return; 639 640 const toast = modal.querySelector('[data-share-toast]'); 641 const pageUrl = window.location.href; 642 const pageTitle = document.title; 643 644 const shareUrls = { 645 linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(pageUrl)}`, 646 facebook: `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(pageUrl)}`, 647 x: `https://twitter.com/intent/tweet?url=${encodeURIComponent(pageUrl)}&text=${encodeURIComponent(pageTitle)}`, 648 }; 649 650 const openModal = () => { 651 modal.hidden = false; 652 document.body.classList.add('share-modal-open'); 653 }; 654 655 const closeModal = () => { 656 modal.hidden = true; 657 document.body.classList.remove('share-modal-open'); 658 if (toast) toast.hidden = true; 659 }; 660 661 const copyLink = async () => { 662 try { 663 await navigator.clipboard.writeText(pageUrl); 664 } catch (_) { 665 const input = document.createElement('input'); 666 input.value = pageUrl; 667 document.body.appendChild(input); 668 input.select(); 669 document.execCommand('copy'); 670 document.body.removeChild(input); 671 } 672 673 if (toast) { 674 toast.hidden = false; 675 setTimeout(() => { 676 toast.hidden = true; 677 }, 1500); 678 } 679 }; 680 681 openBtns.forEach((btn) => { 682 btn.addEventListener('click', (e) => { 683 e.preventDefault(); 684 openModal(); 685 }); 686 }); 687 688 modal.addEventListener('click', (e) => { 689 if (e.target.closest('[data-share-close]')) { 690 closeModal(); 691 return; 692 } 693 694 const target = e.target.closest('[data-share-network]'); 695 if (!target) return; 696 e.preventDefault(); 697 698 const network = target.getAttribute('data-share-network'); 699 if (network === 'copy') { 700 copyLink(); 701 return; 702 } 703 704 const url = shareUrls[network]; 705 if (!url) return; 706 window.open(url, '_blank', 'noopener,noreferrer,width=640,height=720'); 707 closeModal(); 708 });
709 710 document.addEventListener('keydown', (e) => { 711 if (e.key === 'Escape' && !modal.hidden) closeModal(); 712 }); 713})(); 714 715// Open existing Quantcast / InMobi Choice privacy UI 716(function initQcPrivacyLink() { 717 function openPrivacyUi() { 718 try { 719 const tcf = window.__tcfapi || (window.top && window.top.__tcfapi); 720 if (typeof tcf === 'function') { 721 tcf('displayConsentUi', 2, function () {}); 722 return true; 723 } 724 } catch (_) {} 725 726 try { 727 const usp = window.__uspapi || (window.top && window.top.__uspapi); 728 if (typeof usp === 'function') { 729 usp('displayUspUi'); 730 return true; 731 } 732 } catch (_) {} 733 734 const qcLink = 735 document.getElementById('qc-cmp2-persistent-link') || 736 document.querySelector('.qc-cmp2-persistent-link'); 737 if (qcLink) { 738 qcLink.dispatchEvent(new MouseEvent('click', { 739 bubbles: true, 740 cancelable: true, 741 view: window, 742 })); 743 return true; 744 } 745 return false; 746 } 747 748 document.addEventListener('click', (e) => { 749 const trigger = e.target.closest('[data-qc-privacy]'); 750 if (!trigger) return; 751 e.preventDefault(); 752 753 if (openPrivacyUi()) return; 754 755 let tries = 0; 756 const timer = setInterval(() => { 757 tries += 1; 758 if (openPrivacyUi() || tries >= 40) clearInterval(timer); 759 }, 100); 760 }); 761})();
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.