PageSourceSearch

https://ssupl.com/data/skin/front/19-07_C/js/bnn_featured_gear_brands.js?

js ssupl.com collected 2026-10-09 23:37:41 UTC 23,498 bytes, 844 lines download raw bytes

1(function (window, document) {
2    'use strict';
3
4    var ROOT_ID = 'ssupl-featured-gear-brands';
5    var STYLE_ID = 'ssupl-featured-gear-brands-style';
6    var MOBILE_BREAKPOINT = 767;
7
8    /* =========================================================
9       FEATURED GEAR BRANDS
10       image 항목에 사용할 메인 이미지 URL만 입력하면 됩니다.
11    ========================================================= */
12
13    var BRANDS = [
14        {
15            name: 'SHOEI',
16            href: '/goods/goods_list.php?cateCd=008414',
17            image: 'https://ssupl01.hgodo.com/img_new/goods/editor/250521/b87e060654ece51e54a57a71b9390d23_155038.png',
18            imageAlt: 'SHOEI 쇼에이',
19            logo: '/data/skin/front/19-07_C/img/etc/e/a_11.jpg',
20            logoAlt: 'SHOEI',
21            description: '라이딩을 위한 프리미엄 헬멧'
22        },
23        {
24            name: 'HJC',
25            href: '/goods/goods_list.php?cateCd=008287',
26            image: 'https://ssupl01.hgodo.com/img_new/goods/editor/240809/1d26396396e8958812530f60cda8086a_143732.jpg',
27            imageAlt: 'HJC 헬멧',
28            logo: 'https://ssupl01.hgodo.com/img/etc/main/hjc.jpg',
29            logoAlt: 'HJC',
30            description: '세계가 선택한 프리미엄 모터사이클 헬멧'
31        },
32        {
33            name: 'ALPINESTARS',
34            href: '/goods/goods_list.php?cateCd=008256',
35            image: 'https://ssupl01.hgodo.com/img_new/goods/editor/260517/a558b7189ef92fbc4b442ee843ad48f7_021103.jpg',
36            imageAlt: '알파인스타즈 ALPINESTARS',
37            logo: '/data/skin/front/19-07_C/img/etc/e/a_14.jpg',
38            logoAlt: 'ALPINESTARS',
39            description: '레이싱에서 시작된 프리미엄 라이딩 기어'
40        },
41        {
42            name: "REV'IT",
43            href: '/goods/goods_list.php?cateCd=008321',
44            image: "https://www.bikepop.co.kr/bemarket/revit3/quantum3h2ojacket_top5.jpg",
45            imageAlt: "레빗 REV'IT",
46            logo: '/data/skin/front/19-07_C/img/etc/e/a_16.jpg',
47            logoAlt: "REV'IT",
48            description: '기능과 스타일을 결합한 프리미엄 라이딩 기어'
49        },
50        {
51            name: 'DAYTONA',
52            href: '/goods/goods_list.php?cateCd=008477',
53            image: 'https://godomall-storage.cdn-nhncommerce.com/44751f1b0dd5a7a4197137ced7bf7245/editor/goods/category-goods/36/b89ec8483f40fd84a7b68b645ba50849.png',
54            imageAlt: 'DAYTONA 데이토나',
55            logo: 'https://ssupl01.hgodo.com/img/etc/main/daytona.jpg',
56            logoAlt: 'DAYTONA',
57            description: '매일의 라이딩을 더 편리하게 만드는 모터사이클 기어'
58        }
59    ];
60
61
62    /* =========================================================
63       UTIL
64    ========================================================= */
65
66    function pad(number) {
67        return number < 10 ? '0' + number : String(number);
68    }
69
70    function escapeHtml(value) {
71        return String(value)
72            .replace(/&/g, '&amp;')
73            .replace(/</g, '&lt;')
74            .replace(/>/g, '&gt;')
75            .replace(/"/g, '&quot;')
76            .replace(/'/g, '&#039;');
77    }
78
79    function shuffleBrands(items) {
80        var shuffled = items.slice();
81
82        for (var i = shuffled.length - 1; i > 0; i--) {
83            var randomIndex = Math.floor(Math.random() * (i + 1));
84            var temporary = shuffled[i];
85
86            shuffled[i] = shuffled[randomIndex];
87            shuffled[randomIndex] = temporary;
88        }
89
90        return shuffled;
91    }
92
93
94    /* =========================================================
95       STYLE
96    ========================================================= */
97
98    function addStyle() {
99        if (document.getElementById(STYLE_ID)) {
100            return;
101        }
102
103        var style = document.createElement('style');
104        style.id = STYLE_ID;
105
106        style.textContent = [
107
108            '#' + ROOT_ID + ',',
109            '#' + ROOT_ID + ' *{',
110                'box-sizing:border-box;',
111            '}',
112
113            '#' + ROOT_ID + '{',
114                'position:relative;',
115                'width:100%;',
116                'margin:0;',
117                'padding:28px 0 0;',
118                'overflow:hidden;',
119                'background:#fff;',
120                'color:#111;',
121                'font-family:-apple-system,BlinkMacSystemFont,"Pretendard","Noto Sans KR","Apple SD Gothic Neo",Arial,sans-serif;',
122            '}',
123
124            '#' + ROOT_ID + ' a{',
125                'color:inherit;',
126                'text-decoration:none;',
127            '}',
128
129            '#' + ROOT_ID + ' button{',
130                'font-family:inherit;',
131            '}',
132
133
134            /* ==============================
135               INNER
136            ============================== */
137
138            '#' + ROOT_ID + ' .sfg-inner{',
139                'position:relative;',
140                'width:100%;',
141                'max-width:1200px;',
142                'margin:0 auto;',
143                'padding:0;',
144            '}',
145
146
147            /* ==============================
148               COUNT
149            ============================== */
150
151            '#' + ROOT_ID + ' .sfg-top{',
152                'display:flex;',
153                'align-items:center;',
154                'justify-content:flex-end;',
155                'min-height:18px;',
156                'margin-bottom:14px;',
157            '}',
158
159            '#' + ROOT_ID + ' .sfg-count{',
160                'color:#aaa;',
161                'font-size:12px;',
162                'line-height:1;',
163            '}',
164
165            '#' + ROOT_ID + ' .sfg-current{',
166                'color:#111;',
167                'font-weight:800;',
168            '}',
169
170
171            /* ==============================
172               SLIDER
173            ============================== */
174
175            '#' + ROOT_ID + ' .sfg-stage{',
176                'position:relative;',
177            '}',
178
179            '#' + ROOT_ID + ' .sfg-viewport{',
180                'width:100%;',
181                'overflow:hidden;',
182                'touch-action:pan-y;',
183            '}',
184
185            '#' + ROOT_ID + ' .sfg-track{',
186                'display:flex;',
187                'align-items:stretch;',
188                'margin:0;',
189                'padding:0;',
190                'transform:translate3d(0,0,0);',
191                'transition:transform .45s cubic-bezier(.22,.61,.36,1);',
192                'will-change:transform;',
193            '}',
194
195
196            /* ==============================
197               CARD
198            ============================== */
199
200            '#' + ROOT_ID + ' .sfg-card{',
201                'display:block;',
202                'position:relative;',
203                'flex:0 0 auto;',
204                'min-width:0;',
205                'aspect-ratio:4/3;',
206                'overflow:hidden;',
207                'border:0;',
208                'background:#111;',
209                'isolation:isolate;',
210            '}',
211
212            '#' + ROOT_ID + ' .sfg-image{',
213                'position:absolute;',
214                'inset:0;',
215                'z-index:-3;',
216                'width:100%;',
217                'height:100%;',
218                'overflow:hidden;',
219                'background:#111;',
220            '}',
221
222            '#' + ROOT_ID + ' .sfg-image img{',
223                'display:block;',
224                'width:100%;',
225                'height:100%;',
226                'object-fit:cover;',
227                'transition:transform .45s ease;',
228            '}',
229
230            '#' + ROOT_ID + ' .sfg-card:hover .sfg-image img{',
231                'transform:scale(1.035);',
232            '}',
233
234            '#' + ROOT_ID + ' .sfg-card:before{',
235                'content:"";',
236                'position:absolute;',
237                'inset:0;',
238                'z-index:-2;',
239                'background:linear-gradient(180deg,rgba(0,0,0,0) 30%,rgba(0,0,0,.12) 48%,rgba(0,0,0,.88) 100%);',
240            '}',
241
242            '#' + ROOT_ID + ' .sfg-card:after{',
243                'content:"";',
244                'position:absolute;',
245                'inset:0;',
246                'z-index:-1;',
247                'background:linear-gradient(90deg,rgba(0,0,0,.25),rgba(0,0,0,0) 65%);',
248            '}',
249
250
251            /* ==============================
252               COPY
253            ============================== */
254
255            '#' + ROOT_ID + ' .sfg-copy{',
256                'position:absolute;',
257                'right:0;',
258                'bottom:0;',
259                'left:0;',
260                'z-index:2;',
261                'width:100%;',
262                'padding:24px;',
263                'color:#fff;',
264            '}',
265
266            '#' + ROOT_ID + ' .sfg-logo{',
267                'display:inline-flex;',
268                'align-items:center;',
269                'width:auto;',
270                'height:30px;',
271                'margin:0 0 13px;',
272                'padding:5px 9px;',
273                'background:#fff;',
274            '}',
275
276            '#' + ROOT_ID + ' .sfg-logo img{',
277                'display:block;',
278                'width:auto;',
279                'max-width:110px;',
280                'height:auto;',
281                'max-height:20px;',
282                'object-fit:contain;',
283            '}',
284
285            '#' + ROOT_ID + ' .sfg-copy h3{',
286                'margin:0;',
287                'color:#fff;',
288                'font-size:18px;',
289                'font-weight:750;',
290                'line-height:1.4;',
291                'letter-spacing:-.45px;',
292                'word-break:keep-all;',
293                'text-shadow:0 2px 8px rgba(0,0,0,.4);',
294            '}',
295
296            '#' + ROOT_ID + ' .sfg-brand-link{',
297                'display:flex;',
298                'align-items:center;',
299                'justify-content:space-between;',
300                'margin-top:14px;',
301                'color:rgba(255,255,255,.78);',
302                'font-size:12px;',
303                'font-weight:600;',
304                'line-height:1;',
305            '}',
306
307            '#' + ROOT_ID + ' .sfg-brand-link b{',
308                'margin-left:auto;',
309                'color:#fff;',
310                'font-size:16px;',
311                'font-weight:400;',
312            '}',
313
314
315            /* ==============================
316               ARROWS
317            ============================== */
318
319            '#' + ROOT_ID + ' .sfg-arrow{',
320                'display:flex;',
321                'align-items:center;',
322                'justify-content:center;',
323                'position:absolute;',
324                'top:50%;',
325                'z-index:5;',
326                'width:42px;',
327                'height:42px;',
328                'margin:0;',
329                'padding:0;',
330                'border:1px solid #ddd;',
331                'border-radius:50%;',
332                'background:rgba(255,255,255,.96);',
333                'color:#222;',
334                'font-size:24px;',
335                'line-height:1;',
336                'box-shadow:0 4px 14px rgba(0,0,0,.14);',
337                'transform:translateY(-50%);',
338                'cursor:pointer;',
339            '}',
340
341            '#' + ROOT_ID + ' .sfg-prev{',
342                'left:10px;',
343            '}',
344
345            '#' + ROOT_ID + ' .sfg-next{',
346                'right:10px;',
347            '}',
348
349            '#' + ROOT_ID + ' .sfg-arrow:disabled{',
350                'opacity:.3;',
351                'cursor:default;',
352            '}',
353
354
355            /* ==============================
356               PROGRESS
357            ============================== */
358
359            '#' + ROOT_ID + ' .sfg-progress{',
360                'position:relative;',
361                'width:100%;',
362                'height:2px;',
363                'margin-top:26px;',
364                'overflow:hidden;',
365                'background:#e8e8e8;',
366            '}',
367
368            '#' + ROOT_ID + ' .sfg-progress-bar{',
369                'display:block;',
370                'width:0;',
371                'height:100%;',
372                'background:#111;',
373                'transition:width .35s ease;',
374            '}',
375
376
377            /* ==============================
378               MOBILE
379            ============================== */
380
381            '@media screen and (max-width:767px){',
382
383                '#' + ROOT_ID + '{',
384                    'padding:20px 0 0;',
385                '}',
386
387                '#' + ROOT_ID + ' .sfg-inner{',
388                    'padding:0 16px;',
389                '}',
390
391                '#' + ROOT_ID + ' .sfg-top{',
392                    'margin-bottom:10px;',
393                '}',
394
395                '#' + ROOT_ID + ' .sfg-count{',
396                    'font-size:11px;',
397                '}',
398
399                '#' + ROOT_ID + ' .sfg-copy{',
400                    'padding:20px 18px;',
401                '}',
402
403                '#' + ROOT_ID + ' .sfg-logo{',
404                    'height:28px;',
405                    'margin-bottom:11px;',
406                    'padding:4px 8px;',
407                '}',
408
409                '#' + ROOT_ID + ' .sfg-logo img{',
410                    'max-width:100px;',
411                    'max-height:18px;',
412                '}',
413
414                '#' + ROOT_ID + ' .sfg-copy h3{',
415                    'font-size:17px;',
416                '}',
417
418                '#' + ROOT_ID + ' .sfg-brand-link{',
419                    'margin-top:12px;',
420                    'font-size:11px;',
421                '}',
422
423                '#' + ROOT_ID + ' .sfg-arrow{',
424                    'display:none;',
425                '}',
426
427                '#' + ROOT_ID + ' .sfg-progress{',
428                    'margin-top:20px;',
429                '}',
430
431            '}',
432
433
434            /* ==============================
435               SMALL MOBILE
436            ============================== */
437
438            '@media screen and (max-width:359px){',
439
440                '#' + ROOT_ID + ' .sfg-inner{',
441                    'padding:0 12px;',
442                '}',
443
444                '#' + ROOT_ID + ' .sfg-copy{',
445                    'padding:17px 15px;',
446                '}',
447
448                '#' + ROOT_ID + ' .sfg-copy h3{',
449                    'font-size:16px;',
450                '}',
451
452            '}'
453
454        ].join('');
455
456        document.head.appendChild(style);
457    }
458
459
460    /* =========================================================
461       CREATE CARD
462    ========================================================= */
463
464    function createCard(brand) {
465
466        return [
467
468            '<a href="',
469            escapeHtml(brand.href),
470            '" class="sfg-card">',
471
472                '<div class="sfg-image">',
473
474                    '<img src="',
475                    escapeHtml(brand.image),
476                    '" alt="',
477                    escapeHtml(brand.imageAlt),
478                    '" loading="lazy" draggable="false">',
479
480                '</div>',
481
482                '<div class="sfg-copy">',
483
484                    '<div class="sfg-logo">',
485
486                        '<img src="',
487                        escapeHtml(brand.logo),
488                        '" alt="',
489                        escapeHtml(brand.logoAlt),
490                        '" loading="lazy" draggable="false">',
491
492                    '</div>',
493
494                    '<h3>',
495                        escapeHtml(brand.description),
496                    '</h3>',
497
498                    '<span class="sfg-brand-link">',
499
500                        escapeHtml(brand.name),
501
502                        '<b>→</b>',
503
504                    '</span>',
505
506                '</div>',
507
508            '</a>'
509
510        ].join('');
511    }
512
513
514    /* =========================================================
515       RENDER
516    ========================================================= */
517
518    function renderMainSlider(root) {
519
520        var randomizedBrands = shuffleBrands(BRANDS);
521
522        var cards = randomizedBrands
523            .map(createCard)
524            .join('');
525
526        root.innerHTML = [
527
528            '<div class="sfg-inner">',
529
530                '<div class="sfg-top">',
531
532                    '<span class="sfg-count">',
533
534                        '<b class="sfg-current">01</b>',
535
536                        ' / ',
537
538                        '<span class="sfg-total">01</span>',
539
540                    '</span>',
541
542                '</div>',
543
544                '<div class="sfg-stage">',
545
546                    '<button type="button" class="sfg-arrow sfg-prev" aria-label="이전 브랜드">',
547                        '‹',
548                    '</button>',
549
550                    '<div class="sfg-viewport">',
551
552                        '<div class="sfg-track">',
553                            cards,
554                        '</div>',
555
556                    '</div>',
557
558                    '<button type="button" class="sfg-arrow sfg-next" aria-label="다음 브랜드">',
559                        '›',
560                    '</button>',
561
562                '</div>',
563
564                '<div class="sfg-progress" aria-hidden="true">',
565
566                    '<span class="sfg-progress-bar"></span>',
567
568                '</div>',
569
570            '</div>'
571
572        ].join('');
573    }
574
575
576    /* =========================================================
577       SLIDER
578    ========================================================= */
579
580    function startSlider(root) {
581
582        var viewport =
583            root.querySelector('.sfg-viewport');
584
585        var track =
586            root.querySelector('.sfg-track');
587
588        var cards =
589            Array.prototype.slice.call(
590                root.querySelectorAll('.sfg-card')
591            );
592
593        var prevButton =
594            root.querySelector('.sfg-prev');
595
596        var nextButton =
597            root.querySelector('.sfg-next');
598
599        var currentText =
600            root.querySelector('.sfg-current');
601
602        var totalText =
603            root.querySelector('.sfg-total');
604
605        var progressBar =
606            root.querySelector('.sfg-progress-bar');
607
608        var currentPage = 0;
609        var perView = 1;
610        var pageCount = 1;
611        var cardWidth = 0;
612        var gap = 14;
613        var resizeTimer = null;
614        var touchStartX = 0;
615        var touchMoveX = 0;
616
617
618        function update(animated) {
619
620            var offset =
621                currentPage *
622                perView *
623                (cardWidth + gap);
624
625            track.style.transition =
626                animated === false
627                    ? 'none'
628                    : 'transform .45s cubic-bezier(.22,.61,.36,1)';
629
630            track.style.transform =
631                'translate3d(' + (-offset) + 'px,0,0)';
632
633            currentText.textContent =
634                pad(currentPage + 1);
635
636            totalText.textContent =
637                pad(pageCount);
638
639            progressBar.style.width =
640                (
641                    ((currentPage + 1) / pageCount) *
642                    100
643                ) + '%';
644
645            prevButton.disabled =
646                currentPage === 0;
647
648            nextButton.disabled =
649                currentPage === pageCount - 1;
650        }
651
652
653        function setup() {
654
655            perView =
656                window.innerWidth <= MOBILE_BREAKPOINT
657                    ? 1
658                    : 2;
659
660            gap =
661                window.innerWidth <= MOBILE_BREAKPOINT
662                    ? 10
663                    : 14;
664
665            cardWidth =
666                (
667                    viewport.clientWidth -
668                    (gap * (perView - 1))
669                ) / perView;
670
671            pageCount =
672                Math.max(
673                    1,
674                    Math.ceil(cards.length / perView)
675                );
676
677            currentPage =
678                Math.min(
679                    currentPage,
680                    pageCount - 1
681                );
682
683            track.style.gap =
684                gap + 'px';
685
686            cards.forEach(function (card) {
687
688                card.style.width =
689                    cardWidth + 'px';
690
691                card.style.flexBasis =
692                    cardWidth + 'px';
693
694            });
695
696            update(false);
697        }
698
699
700        function goTo(page) {
701
702            currentPage =
703                Math.max(
704                    0,
705                    Math.min(page, pageCount - 1)
706                );
707
708            update(true);
709        }
710
711
712        prevButton.onclick = function () {
713            goTo(currentPage - 1);
714        };
715
716
717        nextButton.onclick = function () {
718            goTo(currentPage + 1);
719        };
720
721
722        /* ==============================
723           TOUCH SWIPE
724        ============================== */
725
726        viewport.addEventListener(
727            'touchstart',
728            function (event) {
729
730                touchStartX =
731                    event.touches[0].clientX;
732
733                touchMoveX =
734                    touchStartX;
735
736            },
737            {
738                passive: true
739            }
740        );
741
742
743        viewport.addEventListener(
744            'touchmove',
745            function (event) {
746
747                touchMoveX =
748                    event.touches[0].clientX;
749
750            },
751            {
752                passive: true
753            }
754        );
755
756
757        viewport.addEventListener(
758            'touchend',
759            function () {
760
761                var distance =
762                    touchMoveX - touchStartX;
763
764                if (distance < -40) {
765                    goTo(currentPage + 1);
766                }
767
768                if (distance > 40) {
769                    goTo(currentPage - 1);
770                }
771
772            }
773        );
774
775
776        /* ==============================
777           RESIZE
778        ============================== */
779
780        window.addEventListener(
781            'resize',
782            function () {
783
784                window.clearTimeout(resizeTimer);
785
786                resizeTimer =
787                    window.setTimeout(
788                        setup,
789                        150
790                    );
791
792            }
793        );
794
795
796        setup();
797    }
798
799
800    /* =========================================================
801       INIT
802    ========================================================= */
803
804    function init() {
805
806        var root =
807            document.getElementById(ROOT_ID);
808
809        if (!root) {
810            return;
811        }
812
813        addStyle();
814
815        if (
816            root.getAttribute('data-ready') === 'true'
817        ) {
818            return;
819        }
820
821        root.setAttribute(
822            'data-ready',
823            'true'
824        );
825
826        renderMainSlider(root);
827        startSlider(root);
828    }
829
830
831    if (document.readyState === 'loading') {
832
833        document.addEventListener(
834            'DOMContentLoaded',
835            init
836        );
837
838    } else {
839
840        init();
841
842    }
843
844})(window, document);

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.