1(function ($) { 2 "use strict"; 3 /** 4 * @param $scope The Widget wrapper element as a jQuery element 5 * @param $ The jQuery alias 6 */ 7 // Make sure you run this code under Elementor. 8 $(window).on("elementor/frontend/init", function () { 9 // global widget 10 elementorFrontend.hooks.addAction("frontend/element_ready/widget", function ($scope, $) { 11 // background image 12 let bgImage = $scope.find("[data-bg-image]"); 13 if (bgImage.length > 0) { 14 bgImage.each(function () { 15 $(this).css("background-image", "url(" + $(this).attr("data-bg-image") + ")"); 16 }); 17 } 18 19 let maskImage = $scope.find("[data-mask]"); 20 if (maskImage.length > 0) { 21 maskImage.each(function () { 22 $(this).css("mask-image", "url(" + $(this).attr("data-mask") + ")"); 23 }); 24 } 25 26 // PureCounter initialization 27 let pureCounter = $scope.find(".purecounter"); 28 if (pureCounter.length > 0 && typeof PureCounter !== "undefined") { 29 // Clear previous counter instance if necessary 30 if (window.pureCounter) { 31 window.pureCounter.destroy && window.pureCounter.destroy(); // optional cleanup if needed 32 } 33 // Create new counter instance 34 window.pureCounter = new PureCounter(); 35 } 36 37 // right swipe 38 document.querySelectorAll(".rightSwipeWrap").forEach((wrap, i) => { 39 gsap.set(wrap.querySelectorAll(".right-swipe"), { 40 transformPerspective: 1200, 41 x: "10rem", 42 rotateY: -20, 43 opacity: 0, 44 transformOrigin: "right center", 45 }); 46 gsap.to(wrap.querySelectorAll(".right-swipe"), { 47 transformPerspective: 1200, 48 x: 0, 49 rotateY: 0, 50 opacity: 1, 51 delay: 0.3, 52 ease: "power3.out", 53 scrollTrigger: { 54 trigger: wrap, 55 start: "top 80%", 56 id: "rightSwipeWrap-" + i, 57 toggleActions: "play none none none", 58 // markers: true, 59 }, 60 }); 61 }); 62 63 // left swipe 64 document.querySelectorAll(".leftSwipeWrap").forEach((wrap, i) => { 65 gsap.set(wrap.querySelectorAll(".left-swipe"), { 66 transformPerspective: 1200, 67 x: "-10rem", 68 rotateY: 20, 69 opacity: 0, 70 transformOrigin: "left center", 71 }); 72 gsap.to(wrap.querySelectorAll(".left-swipe"), { 73 transformPerspective: 1200, 74 x: 0, 75 rotateY: 0, 76 opacity: 1, 77 delay: 0.3, 78 ease: "power3.out", 79 scrollTrigger: { 80 trigger: wrap, 81 start: "top 80%", 82 id: "leftSwipeWrap-" + i, 83 toggleActions: "play none none none", 84 // markers: true, 85 }, 86 }); 87 }); 88 }); 89 90 // tj-hero-slider 91 elementorFrontend.hooks.addAction( 92 "frontend/element_ready/tj-hero-banner-slider.default", 93 94 function ($scope, $) { 95 let heroSlider = $scope.find(".full-slider-active"); 96 97 let bannerWrap = $scope.find(".bannerWrap"); 98 99 let loop = bannerWrap.attr("data-loop") == "yes" ? true : false; 100 let nav = bannerWrap.attr("data-nav") == "yes" ? true : false; 101 let autoplay = bannerWrap.attr("data-autoplay") == "yes" ? true : false; 102 let delay = bannerWrap.attr("data-delay") ? bannerWrap.attr("data-delay") : "5000"; 103 104 if (heroSlider.length > 0) { 105 const sliderId = heroSlider.attr("id"); 106 107 var heroSlide = new Swiper(`#${sliderId}`, { 108 slidesPerView: 1, 109 spaceBetween: 0, 110 effect: "fade", 111 loop: loop, 112 speed: 2000, 113 ...(autoplay 114 ? { 115 autoplay: { 116 delay: delay, 117 }, 118 } 119 : {}), 120 ...(nav 121 ? { 122 navigation: { 123 prevEl: ".tj-btn-prev", 124 nextEl: ".tj-btn-next", 125 }, 126 } 127 : {}), 128 pagination: { 129 el: ".tj-sw-pagination", 130 type: "fraction", 131 clickable: true, 132 renderFraction: function (currentClass, totalClass) { 133 return '<span class="' + currentClass + '"></span>' + ' <span class="dash"><span class="dash-inner"></span></span> ' + '<span class="' + totalClass + '"></span>'; 134 }, 135 }, 136 on: { 137 init: function () { 138 updateDashWidth(this); 139 }, 140 slideChange: function () { 141 updateDashWidth(this); 142 }, 143 }, 144 }); 145 function updateDashWidth(swiper) { 146 const dashInner = swiper.el.querySelector(".dash-inner"); 147 if (dashInner) { 148 const realIndex = swiper.realIndex; 149 const totalSlides = swiper.slides.length - swiper.loopedSlides * 2; 150 const progressPercent = ((realIndex + 1) / totalSlides) * 100; 151 dashInner.style.width = progressPercent + "%"; 152 } 153 } 154 } 155 } 156 ); 157 158 // tj-counter-two 159 elementorFrontend.hooks.addAction( 160 "frontend/element_ready/tj-counter-two.default", 161 162 function ($scope, $) { 163 var $counterItem = $scope.find(".funfact-item-two"); 164 165 if ($counterItem.length > 0) { 166 var $counter = $counterItem.find(".odometer"); 167 168 if (!$counter.length) { 169 return; 170 } 171
172 $counter.appear( 173 function () { 174 var odo = $counter; 175 odo.each(function () { 176 var countNumber = $(this).attr("data-count"); 177 $(this).html(countNumber); 178 }); 179 }, 180 { 181 offset: "80%", 182 } 183 ); 184 } 185 } 186 ); 187 188 // tj-counter 189 elementorFrontend.hooks.addAction( 190 "frontend/element_ready/tj-counter.default", 191 192 function ($scope, $) { 193 var $counterItem = $scope.find(".counter-item"); 194 if ($counterItem.length > 0) { 195 var $counter = $counterItem.find(".odometer"); 196 if (!$counter.length) { 197 return; 198 } 199 200 $counter.appear( 201 function () { 202 var odo = $counter; 203 odo.each(function () { 204 var countNumber = $(this).attr("data-count"); 205 $(this).html(countNumber); 206 }); 207 }, 208 { 209 offset: "80%", 210 } 211 ); 212 } 213 } 214 ); 215 216 // tj-about 217 elementorFrontend.hooks.addAction( 218 "frontend/element_ready/tj-about.default", 219 220 function ($scope, $) { 221 var $counterItem = $scope.find(".funfact-item-one"); 222 if ($counterItem.length > 0) { 223 var $counter = $counterItem.find(".odometer"); 224 if (!$counter.length) { 225 return; 226 } 227 $counter.appear( 228 function () { 229 var odo = $counter; 230 odo.each(function () { 231 var countNumber = $(this).attr("data-count"); 232 $(this).html(countNumber); 233 }); 234 }, 235 { 236 offset: "80%", 237 } 238 ); 239 } 240 } 241 ); 242 243 // tj-about-two 244 elementorFrontend.hooks.addAction( 245 "frontend/element_ready/tj-about-two.default", 246 247 function ($scope, $) { 248 var $counterItem = $scope.find(".counter-item"); 249 if ($counterItem.length > 0) { 250 var $counter = $counterItem.find(".odometer"); 251 if (!$counter.length) { 252 return; 253 } 254 $counter.appear( 255 function () { 256 var odo = $counter; 257 odo.each(function () { 258 var countNumber = $(this).attr("data-count"); 259 $(this).html(countNumber); 260 }); 261 }, 262 { 263 offset: "80%", 264 } 265 ); 266 } 267 } 268 ); 269 270 // tj-about-image 271 elementorFrontend.hooks.addAction( 272 "frontend/element_ready/tj-about-image.default", 273 274 function ($scope, $) { 275 var $counterItem = $scope.find(".testimonial-funfact"); 276 if ($counterItem.length > 0) { 277 var $counter = $counterItem.find(".odometer"); 278 if (!$counter.length) { 279 return; 280 } 281 $counter.appear( 282 function () { 283 var odo = $counter; 284 odo.each(function () { 285 var countNumber = $(this).attr("data-count"); 286 $(this).html(countNumber); 287 }); 288 }, 289 { 290 offset: "80%", 291 } 292 ); 293 } 294 } 295 ); 296 297 // tj-banner-four 298 elementorFrontend.hooks.addAction( 299 "frontend/element_ready/tj-banner-four.default", 300 301 function ($scope, $) { 302 var $counterItem = $scope.find(".counter-item"); 303 if ($counterItem.length > 0) { 304 var $counter = $counterItem.find(".odometer"); 305 if (!$counter.length) { 306 return; 307 } 308 $counter.appear( 309 function () { 310 var odo = $counter; 311 odo.each(function () { 312 var countNumber = $(this).attr("data-count"); 313 $(this).html(countNumber); 314 }); 315 }, 316 { 317 offset: "80%", 318 } 319 ); 320 } 321 } 322 ); 323 324 // image slider 325 elementorFrontend.hooks.addAction( 326 "frontend/element_ready/tj-image-slider.default", 327 328 function ($scope, $) { 329 let wrap = $scope.find(".carouselWrap"); 330 331 var loop = wrap.attr("data-loop") == "yes" ? true : false;
332 var autoplay = wrap.attr("data-autoplay") == "yes" ? true : false; 333 var delay = wrap.attr("data-delay") ? wrap.attr("data-delay") : "1"; 334 335 // Image Slider 1 336 let imageSlider = $scope.find(".h6-hero-slider"); 337 if (imageSlider.length > 0) { 338 let sliderId = imageSlider.attr("id"); 339 340 var imageSlide = new Swiper(`#${sliderId}`, { 341 slidesPerView: "auto", 342 spaceBetween: 30, 343 freemode: true, 344 centeredSlides: true, 345 loop: loop, 346 speed: 8000, 347 allowTouchMove: false, 348 349 ...(autoplay 350 ? { 351 autoplay: { 352 delay: delay, 353 }, 354 } 355 : {}), 356 357 breakpoints: { 358 0: { 359 spaceBetween: 20, 360 }, 361 1400: { 362 spaceBetween: 30, 363 }, 364 }, 365 }); 366 } 367 } 368 ); 369 370 // tj-services 371 elementorFrontend.hooks.addAction( 372 "frontend/element_ready/tj-services.default", 373 374 function ($scope, $) { 375 let wrap = $scope.find(".carouselWrap"); 376 377 var loop = wrap.attr("data-loop") == "yes" ? true : false; 378 var dot = wrap.attr("data-dot") == "yes" ? true : false; 379 var autoplay = wrap.attr("data-autoplay") == "yes" ? true : false; 380 var delay = wrap.attr("data-delay") ? wrap.attr("data-delay") : "5000"; 381 382 // Service Slider 1 383 let serviceSlider = $scope.find(".tj-service-slider"); 384 if (serviceSlider.length > 0) { 385 let sliderId = serviceSlider.attr("id"); 386 387 var serviceSlide = new Swiper(`#${sliderId}`, { 388 slidesPerView: 3, 389 spaceBetween: 24, 390 loop: loop, 391 speed: 1500, 392 393 ...(autoplay 394 ? { 395 autoplay: { 396 delay: delay, 397 }, 398 } 399 : {}), 400 401 ...(dot 402 ? { 403 pagination: { 404 el: ".service-pagination", 405 clickable: true, 406 }, 407 } 408 : {}), 409 navigation: { 410 nextEl: ".slider-next", 411 prevEl: ".slider-prev", 412 }, 413 414 breakpoints: { 415 0: { 416 slidesPerView: 1, 417 }, 418 768: { 419 slidesPerView: 2, 420 }, 421 992: { 422 slidesPerView: 3, 423 }, 424 1024: { 425 slidesPerView: 3, 426 }, 427 }, 428 }); 429 } 430 431 let serviceSlider6 = $scope.find(".h6-services-slider"); 432 if (serviceSlider6.length > 0) { 433 let sliderId6 = serviceSlider6.attr("id"); 434 435 var serviceSlide6 = new Swiper(`#${sliderId6}`, { 436 slidesPerView: 1, 437 spaceBetween: 30, 438 loop: loop, 439 speed: 1500, 440 441 ...(autoplay 442 ? { 443 autoplay: { 444 delay: delay, 445 }, 446 } 447 : {}), 448 449 ...(dot 450 ? { 451 pagination: { 452 el: ".swiper_pagination", 453 clickable: true, 454 }, 455 } 456 : {}), 457 breakpoints: { 458 0: { 459 slidesPerView: 1, 460 }, 461 768: { 462 slidesPerView: 2, 463 }, 464 992: { 465 slidesPerView: 3, 466 }, 467 }, 468 }); 469 } 470 471 // GSAP ScrollTrigger Animation 472 if (typeof gsap !== "undefined" && typeof ScrollTrigger !== "undefined") { 473 gsap.registerPlugin(ScrollTrigger); 474 475 const serviceStack = $scope.find(".service-stack"); 476 477 if (serviceStack.length > 0 && window.innerWidth > 991) { 478 serviceStack.each(function () { 479 gsap.to(this, { 480 opacity: 0, 481 scale: 0.9, 482 y: 50, 483 scrollTrigger: { 484 trigger: this, 485 scrub: true, 486 start: "top top", 487 pin: true, 488 pinSpacing: false, 489 markers: false, 490 }, 491 }); 492 }); 493 } 494 } 495 } 496 ); 497 498 // service list 499 elementorFrontend.hooks.addAction("frontend/element_ready/tj-service-list.default", function ($scope, $) { 500 const wrap = $scope.find(".carouselWrap"); 501 502 const loop = wrap.attr("data-loop") === "yes"; 503 const dot = wrap.attr("data-dot") == "yes" ? true : false;
504 const autoplay = wrap.attr("data-autoplay") === "yes"; 505 const delay = parseInt(wrap.attr("data-delay") || "5000"); 506 507 let serviceContent = $scope.find(".h5-service-content"); 508 let serviceItem = $scope.find(".service-style-5"); 509 $(serviceContent).on("mouseover", function () { 510 $(this).parent(serviceItem).addClass("active").siblings().removeClass("active"); 511 }); 512 513 // home 9 service slider 514 const serviceSlider9 = $scope.find(".h9-services-slider"); 515 if (serviceSlider9.length > 0) { 516 const sliderId = serviceSlider9.attr("id"); 517 518 new Swiper(`#${sliderId}`, { 519 slidesPerView: 1, 520 spaceBetween: 30, 521 loop: loop, 522 speed: 1500, 523 ...(autoplay && { 524 autoplay: { 525 delay: delay, 526 }, 527 }), 528 ...(dot 529 ? { 530 pagination: { 531 el: ".swiper_pagination", 532 clickable: true, 533 }, 534 } 535 : {}), 536 breakpoints: { 537 0: { 538 slidesPerView: 1, 539 }, 540 768: { 541 slidesPerView: 2, 542 }, 543 992: { 544 slidesPerView: 3, 545 }, 546 }, 547 }); 548 } 549 }); 550 551 // tj-project 552 elementorFrontend.hooks.addAction("frontend/element_ready/tj-project-carousel.default", function ($scope, $) { 553 const wrap = $scope.find(".carouselWrap"); 554 555 const loop = wrap.attr("data-loop") === "yes"; 556 const dot = wrap.attr("data-dot") == "yes" ? true : false; 557 const nav = wrap.attr("data-nav") == "yes" ? true : false; 558 const autoplay = wrap.attr("data-autoplay") === "yes"; 559 const delay = parseInt(wrap.attr("data-delay") || "5000"); 560 561 // Find Swiper inside this widget 562 const projectSlider = $scope.find(".project-slider"); 563 if (projectSlider.length > 0) { 564 const sliderId = projectSlider.attr("id"); 565 566 new Swiper(`#${sliderId}`, { 567 slidesPerView: 4, 568 spaceBetween: 24, 569 loop: loop, 570 speed: 2000, 571 ...(autoplay && { 572 autoplay: { 573 delay: delay, 574 }, 575 }), 576 ...(nav && { 577 navigation: { 578 nextEl: ".project-next", 579 prevEl: ".project-prev", 580 }, 581 }), 582 breakpoints: { 583 375: { 584 slidesPerView: 1, 585 }, 586 576: { 587 slidesPerView: 2, 588 }, 589 768: { 590 slidesPerView: 2, 591 }, 592 992: { 593 slidesPerView: 3, 594 }, 595 1200: { 596 slidesPerView: 3, 597 }, 598 1400: { 599 slidesPerView: 4, 600 }, 601 }, 602 }); 603 } 604 // Custom Cursor Logic 605 const cursor = document.querySelector(".tj-cursor"); // global 606 const container = $scope[0].querySelector(".project-slider-one"); // scoped swiper container 607 608 if (cursor && container) { 609 // Initial GSAP setup 610 gsap.set(cursor, { xPercent: -50, yPercent: -50 }); 611 612 // Move cursor on pointer 613 document.addEventListener("pointermove", (e) => { 614 gsap.to(cursor, { 615 duration: 0, 616 x: e.clientX, 617 y: e.clientY, 618 }); 619 }); 620 621 // Show/hide on hover within this widget's container 622 container.addEventListener("mouseenter", () => { 623 cursor.style.opacity = "1"; 624 cursor.style.visibility = "visible"; 625 }); 626 627 container.addEventListener("mouseleave", () => { 628 cursor.style.opacity = "0"; 629 cursor.style.visibility = "hidden"; 630 }); 631 632 // Wrap `cursor` with jQuery 633 const $cursor = $(cursor); 634 635 $("a, .sub-menu").on("mouseenter", function () { 636 $cursor.addClass("d-none"); 637 }); 638 $("a, .sub-menu").on("mouseleave", function () { 639 $cursor.removeClass("d-none"); 640 }); 641 } 642 643 // home 5 projects 644 const projectSlider5 = $scope.find(".h5-case-study-slider"); 645 if (projectSlider5.length > 0) { 646 const sliderId = projectSlider5.attr("id"); 647 648 new Swiper(`#${sliderId}`, { 649 slidesPerView: 1, 650 spaceBetween: 20, 651 loop: loop, 652 speed: 2000,
653 arrow: false, 654 ...(autoplay && { 655 autoplay: { 656 delay: delay, 657 }, 658 }), 659 ...(dot 660 ? { 661 pagination: { 662 el: ".swiper_pagination", 663 clickable: true, 664 }, 665 } 666 : {}), 667 breakpoints: { 668 992: { 669 slidesPerView: 2, 670 spaceBetween: 30, 671 }, 672 }, 673 }); 674 } 675 676 // home 7 projects 677 const project7Slider = $scope.find(".h7-case-study-slider"); 678 if (project7Slider.length > 0) { 679 const sliderId = project7Slider.attr("id"); 680 681 var project7 = new Swiper(`#${sliderId}`, { 682 slidesPerView: 1, 683 loop: loop, 684 speed: 1800, 685 effect: "coverflow", 686 grabCursor: true, 687 centeredSlides: true, 688 spaceBetween: -100, 689 coverflowEffect: { 690 rotate: 0, 691 stretch: 0, 692 depth: 800, 693 modifier: 1, 694 slideShadows: false, 695 }, 696 ...(autoplay && { 697 autoplay: { 698 delay: delay, 699 }, 700 }), 701 ...(dot 702 ? { 703 pagination: { 704 el: ".swiper_pagination", 705 clickable: true, 706 }, 707 } 708 : {}), 709 ...(nav 710 ? { 711 navigation: { 712 nextEl: ".tj-project-nav-next", 713 prevEl: ".tj-project-nav-prev", 714 }, 715 } 716 : {}), 717 }); 718 } 719 720 // home 8 projects 721 const projectSlider8 = $scope.find(".h8-case-study-slider"); 722 if (projectSlider8.length > 0) { 723 const sliderId = projectSlider8.attr("id"); 724 725 new Swiper(`#${sliderId}`, { 726 slidesPerView: 1, 727 spaceBetween: 20, 728 loop: loop, 729 speed: 2000, 730 arrow: false, 731 ...(autoplay && { 732 autoplay: { 733 delay: delay, 734 }, 735 }), 736 ...(dot 737 ? { 738 pagination: { 739 el: ".swiper_pagination", 740 clickable: true, 741 }, 742 } 743 : {}), 744 breakpoints: { 745 992: { 746 slidesPerView: 2, 747 spaceBetween: 30, 748 }, 749 }, 750 }); 751 } 752 753 // home 9 projects 754 let h8BlogWrap = $scope.find(".h9-case-study-wrapper"); 755 if (h8BlogWrap.length > 0) { 756 $(".h9-case-study-item").on("mouseover", function () { 757 $(this).addClass("active").siblings().removeClass("active"); 758 }); 759 } 760 }); 761 762 // tj project stack 763 elementorFrontend.hooks.addAction( 764 "frontend/element_ready/tj-project-list-2.default", 765 766 function ($scope, $) { 767 // GSAP ScrollTrigger Animation 768 if (typeof gsap !== "undefined" && typeof ScrollTrigger !== "undefined") { 769 gsap.registerPlugin(ScrollTrigger); 770 771 const h6ProjectStack = $scope.find(".project-stack"); 772 773 if (h6ProjectStack.length > 0 && window.innerWidth > 991) { 774 h6ProjectStack.each(function () { 775 gsap.to(this, { 776 scrollTrigger: { 777 trigger: this, 778 scrub: true, 779 start: "top top", 780 pin: true, 781 pinSpacing: false, 782 markers: false, 783 }, 784 }); 785 }); 786 } 787 788 const h10ProjectStack = $scope.find(".service-stack"); 789 if (h10ProjectStack.length > 0 && window.innerWidth > 991) { 790 h10ProjectStack.each(function () { 791 gsap.to(this, { 792 opacity: 0, 793 scale: 0.9, 794 y: 50, 795 scrollTrigger: { 796 trigger: this, 797 scrub: true, 798 start: "top top", 799 pin: true, 800 pinSpacing: false, 801 markers: false, 802 }, 803 }); 804 }); 805 } 806 } 807 } 808 ); 809 810 // tj-brand-carousel 811 elementorFrontend.hooks.addAction( 812 "frontend/element_ready/tj-brand-carousel.default", 813 814 function ($scope, $) { 815 let wrap = $scope.find(".carouselWrap"); 816 817 var loop = wrap.attr("data-loop") == "yes" ? true : false;
818 var autoplay = wrap.attr("data-autoplay") == "yes" ? true : false; 819 var delay = wrap.attr("data-delay") ? wrap.attr("data-delay") : "1"; 820 821 // brand 1 822 let brandCarouselWrap1 = $scope.find(".brand-slider-1"); 823 if (brandCarouselWrap1.length > 0) { 824 let carouselId = brandCarouselWrap1.attr("id"); 825 var brandCarousel = new Swiper(`#${carouselId}`, { 826 slidesPerView: "auto", 827 spaceBetween: 30, 828 freemode: true, 829 centeredSlides: true, 830 allowTouchMove: false, 831 loop: loop, 832 speed: 3000, 833 ...(autoplay 834 ? { 835 autoplay: { 836 delay: delay, 837 disableOnInteraction: false, 838 }, 839 } 840 : {}), 841 breakpoints: { 842 0: { 843 slidesPerView: 2, 844 }, 845 576: { 846 slidesPerView: 2.5, 847 }, 848 768: { 849 slidesPerView: 3.3, 850 }, 851 992: { 852 slidesPerView: 4.5, 853 }, 854 1200: { 855 slidesPerView: 5.2, 856 }, 857 1400: { 858 slidesPerView: 6, 859 }, 860 }, 861 }); 862 } 863 864 // brand 2 865 let brandCarouselWrap2 = $scope.find(".brand-slider-2"); 866 if (brandCarouselWrap2.length > 0) { 867 let carouselId = brandCarouselWrap2.attr("id"); 868 var brandCarousel2 = new Swiper(`#${carouselId}`, { 869 slidesPerView: "auto", 870 spaceBetween: 30, 871 freemode: true, 872 centeredSlides: true, 873 allowTouchMove: false, 874 loop: loop, 875 speed: 3000, 876 ...(autoplay 877 ? { 878 autoplay: { 879 delay: delay, 880 disableOnInteraction: false, 881 }, 882 } 883 : {}), 884 breakpoints: { 885 0: { 886 slidesPerView: 2, 887 }, 888 576: { 889 slidesPerView: 2.5, 890 }, 891 768: { 892 slidesPerView: 3.3, 893 }, 894 992: { 895 slidesPerView: 4.5, 896 }, 897 1200: { 898 slidesPerView: 5.2, 899 }, 900 1400: { 901 slidesPerView: 6, 902 }, 903 }, 904 }); 905 } 906 } 907 ); 908 909 // tj-marquee 910 elementorFrontend.hooks.addAction( 911 "frontend/element_ready/tj-marquee.default", 912 913 function ($scope, $) { 914 let marquee = $scope.find(".marquee-slider"); 915 916 if (marquee.length > 0) { 917 const marqueeId = marquee.attr("id"); 918 919 var marqueeSlider = new Swiper(`#${marqueeId}`, { 920 slidesPerView: "auto", 921 spaceBetween: 0, 922 freemode: true, 923 centeredSlides: true, 924 loop: true, 925 speed: 4000, 926 allowTouchMove: false, 927 autoplay: { 928 delay: 1, 929 disableOnInteraction: true, 930 }, 931 }); 932 } 933 934 // home 6 935 let h6Marquee = $scope.find(".h6-marquee-slider"); 936 if (h6Marquee.length > 0) { 937 const marqueeId = h6Marquee.attr("id"); 938 939 let marqueeSlider = new Swiper(`#${marqueeId}`, { 940 slidesPerView: "auto", 941 spaceBetween: 20, 942 freemode: true, 943 centeredSlides: true, 944 loop: true, 945 speed: 4000, 946 allowTouchMove: false, 947 autoplay: { 948 delay: 1, 949 }, 950 }); 951 } 952 } 953 ); 954 955 // tj-project-list 956 elementorFrontend.hooks.addAction( 957 "frontend/element_ready/tj-project-list.default", 958 959 function ($scope, $) { 960 let scrollContent = $scope.find(".marquee-slider-wrapper-two"); 961 962 if (scrollContent.length > 0) { 963 gsap.to(".marquee-slider-wrapper-two", { 964 scrollTrigger: { 965 trigger: ".tj-project-section-two", 966 start: "top center-=200", 967 pin: ".marquee-slider-wrapper-two", 968 end: "bottom bottom-=200", 969 markers: false, 970 pinSpacing: false, 971 scrub: 1, 972 }, 973 }); 974 } 975 } 976 ); 977 978 // tj-team 979 elementorFrontend.hooks.addAction( 980 "frontend/element_ready/tj-team.default", 981 982 function ($scope, $) { 983 let wrap = $scope.find(".carouselWrap"); 984 985 let loop = wrap.attr("data-loop") == "yes" ? true : false;
986 let dot = wrap.attr("data-dot") == "yes" ? true : false; 987 let autoplay = wrap.attr("data-autoplay") == "yes" ? true : false; 988 let delay = wrap.attr("data-delay") ? wrap.attr("data-delay") : "5000"; 989 990 let teamSlider = $scope.find(".tj-team-slider"); 991 if (teamSlider.length > 0) { 992 const sliderId = teamSlider.attr("id"); 993 994 var teamslide = new Swiper(`#${sliderId}`, { 995 slidesPerView: 4, 996 spaceBetween: 24, 997 loop: loop, 998 speed: 1500, 999 ...(autoplay 1000 ? { 1001 autoplay: { 1002 delay: delay, 1003 }, 1004 } 1005 : {}), 1006 ...(dot 1007 ? { 1008 pagination: { 1009 el: ".service-pagination", 1010 clickable: true, 1011 }, 1012 } 1013 : {}), 1014 breakpoints: { 1015 0: { 1016 slidesPerView: 1, 1017 }, 1018 460: { 1019 slidesPerView: 2, 1020 }, 1021 768: { 1022 slidesPerView: 2, 1023 }, 1024 992: { 1025 slidesPerView: 3, 1026 }, 1027 1024: { 1028 slidesPerView: 4, 1029 }, 1030 }, 1031 }); 1032 } 1033 1034 // home 10 team 1035 let teamWrapper = $scope.find(".h10-team-wrapper"); 1036 if (teamWrapper.length > 0) { 1037 $(".h10-team-item").on("mouseover", function () { 1038 $(this).addClass("active").siblings().removeClass("active"); 1039 }); 1040 } 1041 } 1042 ); 1043 1044 // tj-testimonial 1045 elementorFrontend.hooks.addAction( 1046 "frontend/element_ready/tj-testimonial.default", 1047 1048 function ($scope, $) { 1049 let wrap = $scope.find(".carouselWrap"); 1050 1051 let loop = wrap.attr("data-loop") == "yes" ? true : false; 1052 let nav = wrap.attr("data-nav") == "yes" ? true : false; 1053 let dot = wrap.attr("data-dot") == "yes" ? true : false; 1054 let autoplay = wrap.attr("data-autoplay") == "yes" ? true : false; 1055 let delay = wrap.attr("data-delay") ? wrap.attr("data-delay") : "5000"; 1056 1057 let testimonialSlider = $scope.find(".tj-testimonial-slider"); 1058 if (testimonialSlider.length > 0) { 1059 const sliderId = testimonialSlider.attr("id"); 1060 1061 var testimonial = new Swiper(`#${sliderId}`, { 1062 slidesPerView: 3, 1063 spaceBetween: 24, 1064 loop: loop, 1065 speed: 1500, 1066 ...(autoplay 1067 ? { 1068 autoplay: { 1069 delay: delay, 1070 }, 1071 } 1072 : {}), 1073 ...(nav 1074 ? { 1075 navigation: { 1076 nextEl: ".slider-next", 1077 prevEl: ".slider-prev", 1078 }, 1079 } 1080 : {}), 1081 ...(dot 1082 ? { 1083 pagination: { 1084 el: ".testimonial-pagination", 1085 clickable: true, 1086 }, 1087 } 1088 : {}), 1089 breakpoints: { 1090 0: { 1091 slidesPerView: 1, 1092 }, 1093 768: { 1094 slidesPerView: 1, 1095 }, 1096 992: { 1097 slidesPerView: 2, 1098 }, 1099 1024: { 1100 slidesPerView: 2, 1101 }, 1102 }, 1103 }); 1104 var star_rating_width = $(".fill-ratings span").width(); 1105 $(".star-ratings").width(star_rating_width); 1106 } 1107 1108 // testimonial slider 2 1109 let testimonialSlider2 = $scope.find(".tj-testimonial-slider-two"); 1110 if (testimonialSlider2.length > 0) { 1111 const sliderId = testimonialSlider2.attr("id"); 1112 var testimonial = new Swiper(`#${sliderId}`, { 1113 slidesPerView: 1, 1114 spaceBetween: 0, 1115 loop: loop, 1116 speed: 1500, 1117 ...(autoplay 1118 ? { 1119 autoplay: { 1120 delay: delay, 1121 }, 1122 } 1123 : {}), 1124 ...(nav 1125 ? { 1126 navigation: { 1127 nextEl: ".slider-next", 1128 prevEl: ".slider-prev", 1129 }, 1130 } 1131 : {}), 1132 }); 1133 var star_rating_width = $(".fill-ratings span").width(); 1134 $(".star-ratings").width(star_rating_width); 1135 } 1136 1137 // testimonial slider 3 1138 let testimonialSlider3 = $scope.find(".testimonial-slider-two"); 1139 if (testimonialSlider3.length > 0) { 1140 const sliderId = testimonialSlider3.attr("id"); 1141 1142 var testimonial = new Swiper(`#${sliderId}`, { 1143 slidesPerView: 3, 1144 spaceBetween: 65, 1145 centeredSlides: true, 1146 loop: loop, 1147 speed: 2000,
1148 ...(autoplay 1149 ? { 1150 autoplay: { 1151 delay: delay, 1152 }, 1153 } 1154 : {}), 1155 ...(dot 1156 ? { 1157 pagination: { 1158 el: ".testimonial-pagination", 1159 clickable: true, 1160 }, 1161 } 1162 : {}), 1163 breakpoints: { 1164 320: { 1165 slidesPerView: 1, 1166 }, 1167 576: { 1168 slidesPerView: 1.5, 1169 }, 1170 768: { 1171 slidesPerView: 1.5, 1172 }, 1173 992: { 1174 slidesPerView: 1.5, 1175 }, 1176 1200: { 1177 slidesPerView: 1.9, 1178 }, 1179 1440: { 1180 slidesPerView: 2.9, 1181 }, 1182 }, 1183 }); 1184 var star_rating_width = $(".fill-ratings span").width(); 1185 $(".star-ratings").width(star_rating_width); 1186 } 1187 1188 // testimonial slider 4 1189 let testimonialSlider4 = $scope.find(".h6-testimonial-slider"); 1190 if (testimonialSlider4.length > 0) { 1191 let slider4Id = testimonialSlider4.attr("id"); 1192 1193 let testimonial = new Swiper(`#${slider4Id}`, { 1194 slidesPerView: 1, 1195 spaceBetween: 30, 1196 loop: loop, 1197 speed: 1500, 1198 ...(autoplay 1199 ? { 1200 autoplay: { 1201 delay: delay, 1202 }, 1203 } 1204 : {}), 1205 ...(nav 1206 ? { 1207 navigation: { 1208 nextEl: ".slider-next", 1209 prevEl: ".slider-prev", 1210 }, 1211 } 1212 : {}), 1213 ...(dot 1214 ? { 1215 pagination: { 1216 el: ".testimonial-pagination", 1217 clickable: true, 1218 }, 1219 } 1220 : {}), 1221 breakpoints: { 1222 450: { 1223 slidesPerView: 1.5, 1224 }, 1225 576: { 1226 slidesPerView: 1.5, 1227 }, 1228 768: { 1229 slidesPerView: 1.5, 1230 }, 1231 992: { 1232 slidesPerView: 2.5, 1233 }, 1234 1200: { 1235 slidesPerView: 2.5, 1236 }, 1237 1400: { 1238 slidesPerView: 3, 1239 }, 1240 }, 1241 }); 1242 var star_rating_width = $(".fill-ratings span").width(); 1243 $(".star-ratings").width(star_rating_width); 1244 } 1245 1246 // testimonial tab 1247 let h7TestimonialWrap = $scope.find(".h7-testimonial-wrap"); 1248 if (h7TestimonialWrap.length > 0) { 1249 $(".h7-testimonial-single .testimonial-content").on("click", function () { 1250 $(this).parent(".h7-testimonial-single").addClass("active").siblings().removeClass("active"); 1251 }); 1252 var star_rating_width = $(".fill-ratings span").width(); 1253 $(".star-ratings").width(star_rating_width); 1254 } 1255 1256 // testimonial slider 8 1257 1258 const testimonialsMarqueeSliders = {}; 1259 1260 function testimonialsMarquee(selector, isReverse = false, speed = 8000) { 1261 const screenWidth = window.innerWidth; 1262 const direction = screenWidth >= 768 ? "vertical" : "horizontal"; 1263 1264 // Destroy previous instance if exists 1265 if (testimonialsMarqueeSliders[selector]) { 1266 testimonialsMarqueeSliders[selector].destroy(true, true); 1267 } 1268 1269 // Init if element exists 1270 if ($(selector).length > 0) { 1271 testimonialsMarqueeSliders[selector] = new Swiper(selector, { 1272 slidesPerView: "auto", 1273 spaceBetween: screenWidth >= 768 ? 30 : 24, 1274 centeredSlides: true, 1275 loop: true, 1276 allowTouchMove: false, 1277 speed: speed, 1278 direction: direction, 1279 autoplay: { 1280 delay: 0, 1281 disableOnInteraction: false, 1282 reverseDirection: isReverse, 1283 }, 1284 }); 1285 } 1286 } 1287 1288 // Use scoped selectors within widget 1289 const upSelector = $scope.find(".tj-slider-up"); 1290 const downSelector = $scope.find(".tj-slider-down"); 1291 1292 if (upSelector.length) { 1293 testimonialsMarquee(`.${upSelector.attr("class").split(" ").join(".")}`, false, 8000); 1294 } 1295 if (downSelector.length) { 1296 testimonialsMarquee(`.${downSelector.attr("class").split(" ").join(".")}`, true, 8000); 1297 } 1298 1299 // home 9 testimonial 1300 let clientThumb = $scope.find(".client-thumb"); 1301 if (clientThumb.length > 0) { 1302 let clientId = clientThumb.attr("id"); 1303 var thumbs = new Swiper(`#${clientId}`, { 1304 slidesPerView: 3, 1305 spaceBetween: 20, 1306 loop: true, 1307 speed: 1500, 1308 freeMode: true, 1309 watchSlidesProgress: true, 1310 slideToClickedSlide: true, 1311 breakpoints: { 1312 0: { 1313 slidesPerView: 1.4, 1314 spaceBetween: 10, 1315 }, 1316 430: { 1317 slidesPerView: 1.6, 1318 }, 1319 530: { 1320 slidesPerView: 2, 1321 spaceBetween: 10, 1322 }, 1323 768: { 1324 slidesPerView: 2.4, 1325 }, 1326 992: { 1327 slidesPerView: 3, 1328 centeredSlides: true, 1329 }, 1330 }, 1331 }); 1332 } 1333 1334 let testimonialSlider9 = $scope.find(".h9-testimonial-slider"); 1335 if (testimonialSlider9.length > 0) { 1336 let slider9Id = testimonialSlider9.attr("id"); 1337 1338 let testimonial = new Swiper(`#${slider9Id}`, { 1339 slidesPerView: 1, 1340 spaceBetween: 28, 1341 loopedSlides: 3, 1342 loop: loop, 1343 speed: 1500, 1344 ...(autoplay 1345 ? { 1346 autoplay: { 1347 delay: delay, 1348 }, 1349 } 1350 : {}), 1351 ...(nav 1352 ? { 1353 navigation: { 1354 nextEl: ".slider-next", 1355 prevEl: ".slider-prev", 1356 }, 1357 } 1358 : {}), 1359 ...(dot 1360 ? { 1361 pagination: { 1362 el: ".swiper_pagination", 1363 clickable: true, 1364 }, 1365 } 1366 : {}), 1367 }); 1368 testimonial.controller.control = thumbs; 1369 thumbs.controller.control = testimonial; 1370 } 1371 1372 // home 10 testimonial 1373 let testimonialSlider10 = $scope.find(".h10-testimonial-slider"); 1374 if (testimonialSlider10.length > 0) { 1375 let slider10Id = testimonialSlider10.attr("id"); 1376 1377 let testimonial10 = new Swiper(`#${slider10Id}`, { 1378 slidesPerView: 1, 1379 spaceBetween: 30, 1380 loop: loop, 1381 speed: 1500, 1382 navigation: { 1383 nextEl: ".slider-next", 1384 prevEl: ".slider-prev", 1385 }, 1386 ...(autoplay 1387 ? { 1388 autoplay: { 1389 delay: delay, 1390 }, 1391 } 1392 : {}), 1393 ...(dot 1394 ? { 1395 pagination: { 1396 el: ".testimonial-pagination", 1397 clickable: true, 1398 }, 1399 } 1400 : {}), 1401 breakpoints: { 1402 768: { 1403 slidesPerView: 2, 1404 }, 1405 1200: { 1406 slidesPerView: 3, 1407 }, 1408 }, 1409 }); 1410 } 1411 } 1412 ); 1413 1414 // tj-progress-bar 1415 elementorFrontend.hooks.addAction( 1416 "frontend/element_ready/tj-progress-bar.default", 1417 1418 function ($scope, $) { 1419 // circle progress bar 1420 let progressItem = $scope.find(".knob"); 1421 if (typeof $.fn.knob != "undefined") { 1422 $(progressItem).each(function () { 1423 var $this = $(this), 1424 knobVal = $this.attr("data-rel"); 1425 1426 $this.knob({ 1427 draw: function () { 1428 $(this.i).val(this.cv + "%"); 1429 }, 1430 }); 1431 1432 $this.appear( 1433 function () { 1434 $({ 1435 value: 0, 1436 }).animate( 1437 { 1438 value: knobVal, 1439 }, 1440 { 1441 duration: 2000,
1442 easing: "swing", 1443 step: function () { 1444 $this.val(Math.ceil(this.value)).trigger("change"); 1445 }, 1446 } 1447 ); 1448 }, 1449 { 1450 accX: 0, 1451 accY: -150, 1452 } 1453 ); 1454 }); 1455 } 1456 // horizontal progress bar 1457 const progressContainers = document.querySelectorAll(".tj-progress"); 1458 1459 if (progressContainers?.length) { 1460 progressContainers.forEach((progressContainer) => { 1461 const targetedProgressBar = progressContainer.querySelector(".tj-progress__bar"); 1462 const completedPercent = parseInt(targetedProgressBar.getAttribute("data-perchant"), 10) || 0; 1463 1464 gsap.to(targetedProgressBar, { 1465 width: `${completedPercent}%`, // Correct width 1466 ease: "power2.out", 1467 duration: 1, 1468 scrollTrigger: { 1469 trigger: progressContainer, // Use container for better scroll handling 1470 start: "top 90%", 1471 end: "top 30%", 1472 }, 1473 onUpdate: function () { 1474 let progressValue = Math.round(this.progress() * 100); // Corrected scaling 1475 let displayPercent = Math.round((completedPercent * progressValue) / 100); // Fixes low % issue 1476 1477 const percentageText = progressContainer.querySelector(".tj-progress__perchant"); 1478 if (percentageText) { 1479 percentageText.textContent = displayPercent + "%"; 1480 } 1481 }, 1482 }); 1483 }); 1484 } 1485 } 1486 ); 1487 1488 // tj-process 1489 elementorFrontend.hooks.addAction("frontend/element_ready/tj-process.default", function ($scope, $) { 1490 let processItem = $scope.find(".h5-process_item"); 1491 1492 if (processItem.length > 0) { 1493 // Add active class to the first item by default 1494 processItem.first().addClass("active"); 1495 1496 // On hover, activate the hovered item and deactivate siblings 1497 processItem.on("mouseenter", function () { 1498 $(this).addClass("active").siblings().removeClass("active"); 1499 }); 1500 } 1501 1502 // process style 2 1503 let processWrapper = $scope.find(".h7-process-inner"); 1504 if (processWrapper.length > 0) { 1505 // process hover effect 1506 const $processContainer = $(".h7-process-inner"); 1507 if ($processContainer.length) { 1508 const $processItems = $processContainer.find(".h7-process-item"); 1509 const $processLineActive = $processContainer.find(".process-line-active"); 1510 // add active class to item 1511 if ($processItems.length) { 1512 const totalPortion = 100 / $processItems.length; 1513 1514 $processLineActive.css({ 1515 left: "0", 1516 top: "0", 1517 }); 1518 1519 // Activate the first item by default 1520 $processItems.removeClass("active"); 1521 $processItems.eq(0).addClass("active"); 1522 $processLineActive.css("top", `0%`); 1523 1524 $processItems.each(function (idx) { 1525 $(this).on("mouseenter", function () { 1526 $processItems.removeClass("active"); 1527 $processLineActive.css("top", `${totalPortion * idx}%`); 1528 $(this).addClass("active"); 1529 }); 1530 }); 1531 } 1532 } 1533 } 1534 1535 // process style 3 1536 let processWrapper8 = $scope.find(".h8-process-inner"); 1537 if (processWrapper8.length > 0) { 1538 // process hover effect 1539 const $processContainer8 = $(".h8-process-inner"); 1540 if ($processContainer8.length) { 1541 const $processItems8 = $processContainer8.find(".process-item"); 1542 const $processLineActive8 = $processContainer8.find(".process-line-active"); 1543 // add active class to item 1544 if ($processItems8.length) { 1545 const totalPortion = 100 / $processItems8.length; 1546 1547 $processLineActive8.css({ 1548 left: "0", 1549 top: "0", 1550 }); 1551 1552 // Activate the first item by default 1553 $processItems8.removeClass("active"); 1554 $processItems8.eq(0).addClass("active");
1555 $processLineActive8.css("left", `0%`); 1556 1557 $processItems8.each(function (idx) { 1558 $(this).on("mouseenter", function () { 1559 $processItems8.removeClass("active"); 1560 $processLineActive8.css("left", `${totalPortion * idx}%`); 1561 $(this).addClass("active"); 1562 }); 1563 }); 1564 } 1565 } 1566 } 1567 }); 1568 1569 // tj-advanced-tab 1570 elementorFrontend.hooks.addAction("frontend/element_ready/tj-advanced-tab.default", function ($scope, $) { 1571 let wrapper = $scope.find(".h6-team-wrapper"); 1572 let teamSlider = $scope.find(".team-tab-slider"); 1573 1574 let tabs = $scope.find(".h6-tab"); 1575 let tabsContent = $scope.find(".h6-tab-content"); 1576 1577 if (wrapper.length > 0) { 1578 let sliderID = teamSlider.attr("id"); 1579 1580 let team = new Swiper(`#${sliderID}`, { 1581 slidesPerView: 1, 1582 spaceBetween: 24, 1583 loop: false, 1584 speed: 1500, 1585 breakpoints: { 1586 320: { 1587 spaceBetween: 15, 1588 slidesPerView: 2.5, 1589 }, 1590 576: { 1591 slidesPerView: 3.5, 1592 }, 1593 768: { 1594 slidesPerView: 4.2, 1595 }, 1596 1024: { 1597 slidesPerView: 5.5, 1598 }, 1599 1200: { 1600 slidesPerView: 6, 1601 }, 1602 }, 1603 }); 1604 1605 [...tabs].forEach((tab) => { 1606 tab.addEventListener("click", () => { 1607 [...tabs].forEach((t) => t.classList.remove("active")); 1608 [...tabsContent].forEach((c) => c.classList.remove("active")); 1609 1610 tab.classList.add("active"); 1611 const index = tab.getAttribute("data-index"); 1612 document.getElementById("tab-" + index).classList.add("active"); 1613 }); 1614 }); 1615 } 1616 }); 1617 1618 // tj-video-info 1619 elementorFrontend.hooks.addAction("frontend/element_ready/tj-video-info.default", function ($scope, $) { 1620 let videoPopup = $scope.find(".video-popup"); 1621 1622 if (videoPopup.length > 0) { 1623 new VenoBox({ 1624 selector: ".video-popup", 1625 numeration: true, 1626 spinner: "pulse", 1627 }); 1628 } 1629 }); 1630 1631 // home 8 latest post 1632 elementorFrontend.hooks.addAction("frontend/element_ready/tj-latest-posts.default", function ($scope, $) { 1633 let h8BlogWrap = $scope.find(".h8-blog-wrapper"); 1634 if (h8BlogWrap.length > 0) { 1635 $(".h8-blog-item .blog-images").on("mouseover", function () { 1636 $(this).parent(".h8-blog-item").addClass("active").siblings().removeClass("active"); 1637 }); 1638 } 1639 }); 1640 1641 // home 9 hero banner 1642 elementorFrontend.hooks.addAction("frontend/element_ready/tj-hero-banner.default", function ($scope, $) { 1643 let marquee9 = $scope.find(".h9-hero-marquee-slider"); 1644 if (marquee9.length > 0) { 1645 const marqueeId = marquee9.attr("id"); 1646 var marqueeSlider = new Swiper(`#${marqueeId}`, { 1647 slidesPerView: "auto", 1648 spaceBetween: 0, 1649 freemode: true, 1650 centeredSlides: true, 1651 loop: true, 1652 speed: 8000, 1653 allowTouchMove: false, 1654 autoplay: { 1655 delay: 1, 1656 disableOnInteraction: true, 1657 }, 1658 }); 1659 } 1660 1661 // Initialize Chart.js pie chart 1662 const ctx = $scope.find(".tj-pie-chart"); 1663 if (ctx.length > 0 && typeof Chart !== "undefined") { 1664 ctx.each(function () { 1665 const ctxSingle = this; 1666 1667 // Get data-color and sanitize 1668 let bgColorsAttr = ctxSingle.getAttribute("data-color"); 1669 1670 // Fix: support comma-separated or JSON-like string 1671 let backgroundColors = ["#f7f7f7", "#f7f7f7", "#0075ff"]; 1672 if (bgColorsAttr) { 1673 try { 1674 // Try to parse as JSON first (if it's like '["#fff","#000"]') 1675 if (bgColorsAttr.trim().startsWith("[")) { 1676 backgroundColors = JSON.parse(bgColorsAttr); 1677 } else { 1678 // Fallback to comma-separated string 1679 backgroundColors = bgColorsAttr.split(",").map((c) => c.trim().replace(/^['"]|['"]$/g, "")); 1680 } 1681 } catch (e) { 1682 console.warn("Invalid data-color format", e); 1683 } 1684 } 1685 1686 const data = { 1687 datasets: [ 1688 { 1689 label: "47", 1690 data: [20, 12, 15], 1691 backgroundColor: backgroundColors, 1692 borderWidth: 0, 1693 spacing: 4, 1694 hoverOffset: 0, 1695 }, 1696 ], 1697 }; 1698 1699 const options = { 1700 cutout: "60%", 1701 }; 1702 1703 new Chart(ctxSingle, { 1704 type: "doughnut", 1705 data, 1706 options, 1707 }); 1708 }); 1709 } 1710 // end 1711 }); 1712 }); 1713})(jQuery);
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.