PageSourceSearch

https://nfoto.no/wp-content/plugins/hashbar-pro/assets/js/announcement-bar-frontend.js?ver=2.0.2

js nfoto.no collected 2026-09-26 03:11:15 UTC 31,437 bytes, 672 lines download raw bytes

1;(function ($) {
2    "use strict";
3
4    $.fn.get_transparent_selector = function get_transparent_selector(){
5        let transparent_header_selector = $(this).closest('.hthb-notification.hthb-pos--top[data-transparent_header_selector]').last().data('transparent_header_selector');
6        if($(this).closest('.hthb-notification.hthb-pos--top').hasClass('hthb-transparent')){
7            return transparent_header_selector;
8        }
9
10        return false;
11    }
12
13    $.fn.add_transparent_header_spacing = function add_transparent_header_spacing(top_notification_height){
14        if( $('body').find('#wpadminbar').length ){
15            top_notification_height = Number.parseInt(top_notification_height + $('body').find('#wpadminbar').height());
16        }
17    }
18
19    function calculate_top_zero(){
20        if( $('body').find('#wpadminbar').length ){
21            return '32px';
22        } else{
23            return '0px';
24        }
25    }
26
27    var timeout = 400;
28    $(window).on('load',function(){
29        var top_notification_height         = Number.parseInt($('.hthb-notification.hthb-pos--top').last().height()),
30            bottom_notification_height      = $('.hthb-notification.hthb-pos--bottom').last().height(),
31            left_wall_notification_width    = $('.hthb-notification.hthb-pos--left-wall').last().width(),
32            right_wall_notification_width   = $('.hthb-notification.hthb-pos--right-wall').last().width();
33
34        var position;
35
36        // if load as minimized disabled
37        $('.hthb-notification.hthb-state--open').each(function(){
38            position = $(this).getPostion();
39
40            if( position == 'top' ){
41                $('body').addClass('hthb hthb-pt--' + top_notification_height );
42                if( $(this).get_transparent_selector() ){
43
44                    $($(this).get_transparent_selector()).addClass('hthb-top-unset');
45                    $($(this).get_transparent_selector()).css( {'top':'unset'} );
46
47                }
48            } else if( position == 'bottom'){
49                $('body').css('padding-bottom', bottom_notification_height + 'px');
50            }
51
52            // Implement how many time to show
53            var time_to_show           = $(this).data('time_to_show'),
54                id                     = $(this).data('id') ? '_' + $(this).data('id') : '', 
55                coockie_count          = Cookies.get('hashbarpro_cookiecount' + id),
56                hashbarpro_oldcookie   = Cookies.get('hashbarpro_oldcookie' + id);
57
58            if( time_to_show && time_to_show > 0 ){
59
60                if(document.cookie.indexOf("hashbarpro_oldcookie" + id) >= 0){
61                    if(time_to_show == hashbarpro_oldcookie && coockie_count){
62                        coockie_count++;
63                        Cookies.set('hashbarpro_cookiecount' + id, coockie_count, { expires: 7 });
64                    } else {
65                        Cookies.set('hashbarpro_oldcookie' + id, time_to_show, { expires: 7 });
66                        Cookies.set('hashbarpro_cookiecount' + id, 1, { expires: 7 });
67                    }
68                } else {
69                    Cookies.set('hashbarpro_oldcookie' + id, time_to_show, { expires: 7 });
70                    Cookies.set('hashbarpro_cookiecount' + id, 1, { expires: 7 });
71                }
72            }
73
74            if( coockie_count > time_to_show ){
75               $(this).css({'display': 'none'});
76               $(this).removeClass('hthb-state--open').addClass('hthb-state--minimized');
77
78               if( position == 'top' ){
79                    $('body').removeClass('hthb');
80               }
81            }
82            
83        });
84
85        // Load as mimimized if option is set to minimized
86        $('.hthb-notification.hthb-state--minimized').each(function(){
87             var position = $(this).getPostion();
88             if( position == 'top' || position == 'top-promo' || position == 'bottom' || position == 'bottom-promo' ){
89                $(this).find('.hthb-row').css('display', 'none');
90             }
91            
92        });
93
94        // Left/right wall
95        $('.hthb-notification.hthb-state--minimized').each(function(){
96            var position = $(this).getPostion();
97            if( position == 'left-wall' ){
98                  $(this).css('left', '-' + left_wall_notification_width + 'px' );
99            } else if( position == 'right-wall' ){
100                 $(this).css('right', '-' + right_wall_notification_width + 'px' );
101            }
102           
103        });
104
105        setTimeout(function(){
106            $('.hthb-notification').addClass('hthb-loaded');
107        }, timeout );
108
109        $('.hthb-notification').each(function() {
110            const delay_time = $(this).attr('data-delay_time');
111            if(+delay_time) {
112                setTimeout(() => {
113                    $(this).showNotification( top_notification_height, bottom_notification_height, left_wall_notification_width, right_wall_notification_width );
114                }, +delay_time*1000);
115            }
116        })
117
118        // When click close button
119        $('.hthb-close-toggle').on('click', function(){
120            const $hthb_id = $(this).closest('.hthb-notification')[0].dataset.id;
121            let $expire_time = +hashbar_localize.cookies_expire_time
122            if(hashbar_localize.cookies_expire_type === 'hours') {
123                $expire_time = $expire_time/24;
124            }
125            if(hashbar_localize.cookies_expire_type === 'minutes') {
126                $expire_time = $expire_time/1440;
127            }
128
129            $(this).minimizeNotification( top_notification_height, bottom_notification_height, left_wall_notification_width, right_wall_notification_width);
130
131            // Keep closed
132            if( hashbar_localize.bar_keep_closed == '1' ){
133                Cookies.set(`keep_closed_bar_${$hthb_id}`, '1', { expires: $expire_time, path: '/' });
134            }
135
136            // Don't show forever
137            if( hashbar_localize.dont_show_bar_after_close == '1' ) {
138                Cookies.set( `dont_show_bar_${$hthb_id}`, '1', { expires: $expire_time, path: '/' } );
139            }
140        });
141
142        // When clicked open button
143        $('.hthb-open-toggle').on('click', function(){
144            $(this).showNotification( top_notification_height, bottom_notification_height, left_wall_notification_width, right_wall_notification_width );
145        });
146
147        // When scroll position matched with a notification
148        // Show the notifications
149        var window_inner_height             = $(window).height(),
150            page_height                     = $('body').height();
151
152        let current_scroll_position         = window.pageYOffset || document.documentElement.scrollTop,
153            current_scroll_position_percent = current_scroll_position / scroll_pos_max * 100;
154            current_scroll_position_percent = Number.parseInt(current_scroll_position_percent);
155
156        var scroll_pos_max = $(document).height() - $(window).height();
157
158        $(window).on('scroll', function(e){
159            current_scroll_position         = $(window).scrollTop(),
160            current_scroll_position_percent = current_scroll_position / scroll_pos_max * 100;
161            current_scroll_position_percent = Number.parseInt(current_scroll_position_percent);
162
163            $(`.hthb-scroll`).each(function(){
164                let scroll_to_show = $(this).data('scroll_to_show'),
165                    scroll_to_hide = $(this).data('scroll_to_hide'),
166                    hthb_id = $(this)[0].dataset.id;
167
168                $(this).trigger_notification_on_scroll(hthb_id, scroll_to_show, scroll_to_hide, current_scroll_position, scroll_pos_max);
169            });
170        });
171    });
172    
173    /**
174     * Calculate % of a given value.
175     * For example, If the give value is 1000 & we want to know the 75% of it.
176     * It will return 750 as the result.
177     *
178     * @param number percent_amount, % amount.
179     * @param number percent_of, Total amount from where the % should be calculated.
180     * @return number
181     */
182    function percent_of(percent_amount, percent_of){
183        percent_of = Number.parseInt(percent_of);
184        percent_amount = Number.parseInt(percent_amount);
185        
186        return percent_of * percent_amount / 100;
187    }
188
189    $.fn.getPostion = function(){
190        if(this.closest('.hthb-notification').hasClass('hthb-pos--top')){
191            return 'top';
192        }
193
194        if(this.closest('.hthb-notification').hasClass('hthb-pos--bottom')){
195            return 'bottom';
196        }
197
198        if(this.closest('.hthb-notification').hasClass('hthb-pos--left-wall')){
199            return 'left-wall';
200        }
201
202        if(this.closest('.hthb-notification').hasClass('hthb-pos--right-wall')){
203            return 'right-wall';
204        }
205
206        if(this.closest('.hthb-notification').hasClass('hthb-pos--bottom-promo')){
207            return 'bottom-promo';
208        }
209
210        if(this.closest('.hthb-notification').hasClass('hthb-pos--top-promo')){
211            return 'top-promo';
212        }
213    }
214
215    $.fn.minimizeNotification = function(top_notification_height, bottom_notification_height, left_wall_notification_width, right_wall_notification_width){
216        var postion = this.getPostion(),
217            left_wall_notification_width = this.closest('.hthb-notification').width(),
218            right_wall_notification_width = this.closest('.hthb-notification').width();
219
220        this.closest('.hthb-notification').removeClass('hthb-state--open');
221        this.closest('.hthb-notification').addClass('hthb-state--minimized');
222
223        if(postion != 'left-wall' && postion != 'right-wall'){
224            this.closest('.hthb-notification').find('.hthb-row').slideToggle();
225        }
226        
227        if( postion == 'top' ){
228            $('body').removeClass('hthb');
229
230            // for sticky
231            if( this.get_transparent_selector() ){
232                $(this.get_transparent_selector()).addClass('hthb-top-unset');
233                $(this.get_transparent_selector()).css({'top': calculate_top_zero()});
234            }
235        } else if( postion == 'bottom' ){
236            $('body').css('padding-bottom', '');
237        } else if( postion == 'left-wall' ){
238            this.closest('.hthb-notification').css('left', '-' + left_wall_notification_width + 'px' );
239        } else if( postion == 'right-wall' ){
240            this.closest('.hthb-notification').css('right', '-' + right_wall_notification_width + 'px' );
241        }
242    }
243
244    $.fn.showNotification = function(top_notification_height, bottom_notification_height, left_wall_notification_width, right_wall_notification_width){
245        var postion = this.getPostion(),
246        left_wall_notification_width = this.closest('.hthb-notification').width(),
247        right_wall_notification_width = this.closest('.hthb-notification').width();
248
249        this.closest('.hthb-notification').removeClass('hthb-state--minimized');
250        this.closest('.hthb-notification').addClass('hthb-state--open');
251
252        if(postion != 'left-wall' && postion != 'right-wall'){
253            this.closest('.hthb-notification').find('.hthb-row').slideToggle();
254        }
255        
256        if( postion == 'top' ){
257            $('body').addClass('hthb hthb-pt--'+ top_notification_height );
258
259            // for sticky
260            if( this.get_transparent_selector() ){
261                $(this.get_transparent_selector()).addClass('hthb-top-unset');
262                $(this.get_transparent_selector()).css({'top': 'unset'});
263            }
264        } else if( postion == 'bottom' ){
265            $('body').css('padding-bottom', bottom_notification_height + 'px');
266        } else if( postion == 'left-wall' ){
267            this.closest('.hthb-notification').css('left', '');
268        } else if( postion == 'right-wall' ){
269            this.closest('.hthb-notification').css('right', '');
270        }
271    }
272
273    $.fn.trigger_click_on_open_button = function(){
274        $(this).addClass('hthb-trigger-open-clicked').removeClass('hthb-trigger-close-clicked');
275        $(this).find('.hthb-open-toggle').trigger('click');
276    }
277
278    $.fn.trigger_click_on_close_button = function(){
279        $(this).removeClass('hthb-trigger-open-clicked').addClass('hthb-trigger-close-clicked');
280        $(this).find('.hthb-close-toggle').trigger('click');
281    }
282
283    $.fn.check_keep_close_bar = function(id){
284        var keep_closed_bar = Cookies.get(`keep_closed_bar_${id}`);
285        if( hashbar_localize.bar_keep_closed === '1' && keep_closed_bar){
286            return false;
287        }else{
288            return true;
289        }
290    }
291
292    $.fn.trigger_notification_on_scroll = function( id, scroll_to_show, scroll_to_hide, current_scroll_position, scroll_pos_max ){
293        if( (scroll_to_show && typeof scroll_to_show == 'string' && scroll_to_show.indexOf('%')) > 0 && (scroll_to_hide && typeof scroll_to_hide == 'string' && scroll_to_hide.indexOf('%')) > 1 ){
294            scroll_to_show = Number.parseInt(scroll_to_show);
295            scroll_to_hide = Number.parseInt(scroll_to_hide);
296            // 20% ,  80%
297
298            if(current_scroll_position > percent_of(scroll_to_show, scroll_pos_max) &&  current_scroll_position < percent_of(scroll_to_hide, scroll_pos_max) ){
299                if( !$(this).is('.hthb-state--open') && !$(this).is('.hthb-trigger-open-clicked') ){
300                    if($(this).check_keep_close_bar(id)){
301                        $(this).trigger_click_on_open_button(); // show
302                    }
303                }
304            } else{
305                if( !$(this).is('.hthb-state--minimized') ){
306                    $(this).trigger_click_on_close_button(); // hide
307                }
308            }
309        } else if( (scroll_to_show && typeof scroll_to_show == 'string' && scroll_to_show.indexOf('%')) &&  (scroll_to_hide === '' || scroll_to_hide == undefined) ){
310            scroll_to_show = Number.parseInt(scroll_to_show);
311            // 20% , ''/undefined
312
313            if( current_scroll_position > percent_of(scroll_to_show, scroll_pos_max) ){
314                if( !$(this).is('.hthb-state--open') && !$(this).is('.hthb-trigger-open-clicked') ){
315                    if($(this).check_keep_close_bar(id)){
316                        $(this).trigger_click_on_open_button(); // show
317                    }
318                }
319            } else {
320                if( !$(this).is('.hthb-state--minimized') ){
321                    $(this).trigger_click_on_close_button(); // hide
322                }
323            }
324        } else if( (scroll_to_show && typeof scroll_to_show == 'number') && (scroll_to_hide && typeof scroll_to_hide == 'string')){
325            // 300 , 80%
326
327            if( current_scroll_position > scroll_to_show &&  current_scroll_position < percent_of(scroll_to_hide, scroll_pos_max) ){
328                if( !$(this).is('.hthb-state--open') && !$(this).is('.hthb-trigger-open-clicked') ){
329                    if($(this).check_keep_close_bar(id)){
330                        $(this).trigger_click_on_open_button(); // show
331                    }
332                }
333            } else{
334                if( !$(this).is('.hthb-state--minimized') ){
335                    $(this).trigger_click_on_close_button(); // hide
336                }
337            }
338
339        } else if( (scroll_to_show === '' || scroll_to_show == undefined) && (scroll_to_hide && typeof scroll_to_hide == 'string' && scroll_to_hide.indexOf('%')) ){
340            scroll_to_hide = Number.parseInt(scroll_to_hide);
341            // empty / undefined , 90%
342
343            if( current_scroll_position > percent_of(scroll_to_hide, scroll_pos_max) ){
344                if( !$(this).is('.hthb-state--minimized') ){
345                    $(this).trigger_click_on_close_button(); // hide
346                }
347            } else{
348                if( !$(this).is('.hthb-state--open') && !$(this).is('.hthb-trigger-open-clicked') ){
349                    if($(this).check_keep_close_bar(id)){
350                        $(this).trigger_click_on_open_button(); // show 
351                    }
352                }
353            }
354
355        } else if( (scroll_to_show && typeof scroll_to_show == 'number') && (scroll_to_hide === '' || scroll_to_hide == undefined) ){
356            // 300 , empty/undefined 
357            if( current_scroll_position < scroll_to_show ){
358                if( !$(this).is('.hthb-state--minimized') ){
359                    $(this).trigger_click_on_close_button(); // hide
360                }
361
362            } else{
363                if( !$(this).is('.hthb-state--open') && !$(this).is('.hthb-trigger-open-clicked') ){
364                    if($(this).check_keep_close_bar(id)){
365                        $(this).trigger_click_on_open_button(); // show
366                    }
367                }
368            }
369            
370        } else {
371        }
372    }
373
374    $(document).ready(function(){
375        $(".hthb-countdown").each(function(){
376            var countdown_id = "#"+$(this).attr('id')+" .hthb-countdown-section .hthb-countdown-wrap",
377                finalDate    = $(countdown_id).data('countdown'),
378                customLabel  = $(countdown_id).data('custom_label');
379            $(countdown_id).countdown(finalDate, function (event) {
380                $(countdown_id+' .countdown-day').html(event.strftime('%D'));
381                $(countdown_id+' .countdown-day-text').html(customLabel.day);
382                $(countdown_id+' .countdown-hour').html(event.strftime('%H'));
383                $(countdown_id+' .countdown-hour-text').html(customLabel.hour);
384                $(countdown_id+' .countdown-minute').html(event.strftime('%M'));
385                $(countdown_id+' .countdown-minite-text').html(customLabel.min);
386                $(countdown_id+' .countdown-second').html(event.strftime('%S'));
387                $(countdown_id+' .countdown-second-text').html(customLabel.sec);
388            });
389        });
390
391        // Initialize new announcement bar countdowns
392        initializeNewAnnouncementBars();
393    });
394
395    /**
396     * Initialize new announcement bars with countdown timers
397     */
398    function initializeNewAnnouncementBars() {
399        var bars = document.querySelectorAll('.hashbar-announcement-bar');
400        if (bars.length === 0) {
401            return;
402        }
403
404        bars.forEach(function(bar) {
405            if (bar.getAttribute('data-countdown-enabled') === 'true') {
406                initializeCountdown(bar);
407            }
408        });
409    }
410
411    /**
412     * Initialize countdown timer
413     */
414    function initializeCountdown(bar) {
415        // Prevent multiple initializations on the same bar
416        if (bar.hasAttribute('data-countdown-initialized')) {
417            return;
418        }
419        bar.setAttribute('data-countdown-initialized', 'true');
420
421        var countdownType = bar.getAttribute('data-countdown-type');
422        var countdownDate = bar.getAttribute('data-countdown-date');
423
424        // Find the bar wrapper (parent of the bar if it exists)
425        var barWrapper = bar.parentElement && bar.parentElement.classList.contains('hashbar-announcement-bar-wrapper')
426            ? bar.parentElement
427            : bar;
428
429        // Query all countdown timers in the wrapper (could be before, inline, after, or below)
430        var timerElements = barWrapper.querySelectorAll('.hashbar-countdown-timer-wrapper, .hashbar-countdown-timer-text');
431
432        if (timerElements.length === 0 || !countdownDate) {
433            return;
434        }
435
436        // Copy color/label settings from bar to each timer element for use during rendering
437        var cdBgColor      = bar.getAttribute('data-countdown-bg-color') || '';
438        var cdTextColor    = bar.getAttribute('data-countdown-text-color') || '';
439        var cdLabelColor   = bar.getAttribute('data-countdown-label-color') || '';
440        var rawLabelDays    = bar.getAttribute('data-countdown-label-days');
441        var rawLabelHours   = bar.getAttribute('data-countdown-label-hours');
442        var rawLabelMinutes = bar.getAttribute('data-countdown-label-minutes');
443        var rawLabelSeconds = bar.getAttribute('data-countdown-label-seconds');
444        timerElements.forEach(function(timerEl) {
445            if (cdBgColor)    timerEl.setAttribute('data-countdown-bg-color', cdBgColor);
446            if (cdTextColor)  timerEl.setAttribute('data-countdown-text-color', cdTextColor);
447            if (cdLabelColor) timerEl.setAttribute('data-countdown-label-color', cdLabelColor);
448            // PHP sets style-specific defaults for new bars; '' means user explicitly cleared the label.
449            // Use !== null so '' (cleared) passes through rather than falling back to JS defaults.
450            var elStyle = timerEl.getAttribute('data-countdown-style') || 'simple';
451            var defDays, defHours, defMins, defSecs;
452            if (elStyle === 'box' || elStyle === 'circular') {
453                defDays = 'Day'; defHours = 'Hour'; defMins = 'Minute'; defSecs = 'Second';
454            } else if (elStyle === 'simple') {
455                defDays = 'd'; defHours = 'h'; defMins = 'm'; defSecs = 's';
456            } else {
457                defDays = 'Days'; defHours = 'Hours'; defMins = 'Minutes'; defSecs = 'Seconds';
458            }
459            timerEl.setAttribute('data-countdown-label-days',    rawLabelDays    !== null ? rawLabelDays    : defDays);
460            timerEl.setAttribute('data-countdown-label-hours',   rawLabelHours   !== null ? rawLabelHours   : defHours);
461            timerEl.setAttribute('data-countdown-label-minutes', rawLabelMinutes !== null ? rawLabelMinutes : defMins);
462            timerEl.setAttribute('data-countdown-label-seconds', rawLabelSeconds !== null ? rawLabelSeconds : defSecs);
463            if ((elStyle === 'simple' || elStyle === 'digital') && cdTextColor) {
464                timerEl.style.color = cdTextColor;
465            }
466            if (elStyle === 'digital' && cdBgColor) {
467                timerEl.style.backgroundColor = cdBgColor;
468                timerEl.style.padding = '4px 8px';
469                timerEl.style.borderRadius = '4px';
470            }
471        });
472
473        // Expired action handler — runs once when the countdown reaches zero.
474        var expiredAction  = bar.getAttribute('data-countdown-expired-action')  || 'show_message';
475        var expiredMessage = bar.getAttribute('data-countdown-expired-message') || '';
476        var expiryHandled  = false;
477
478        function handleExpiry() {
479            if (expiryHandled) return;
480            expiryHandled = true;
481            if (expiredAction === 'hide_bar') {
482                barWrapper.style.display = 'none';
483            } else if (expiredAction === 'show_message') {
484                var safeMsg = expiredMessage
485                    .replace(/&/g, '&amp;')
486                    .replace(/</g, '&lt;')
487                    .replace(/>/g, '&gt;');
488                timerElements.forEach(function(el) {
489                    el.innerHTML = '<span class="hashbar-countdown-expired-message">' + safeMsg + '</span>';
490                });
491            }
492        }
493
494        function updateAllTimers() {
495            var now = new Date().getTime();
496            var countDate = new Date(countdownDate).getTime();
497            var distance = countDate - now;
498
499            // Handle expiry before any DOM update — prevents flash of 00:00 for already-expired bars
500            if (distance < 0) {
501                handleExpiry();
502                return;
503            }
504
505            timerElements.forEach(function(timerElement) {
506                updateCountdown(timerElement, countdownDate, countdownType);
507            });
508        }
509
510        // Call synchronously on load so expired bars are hidden before first paint
511        updateAllTimers();
512        var intervalId;
513        if (!expiryHandled) {
514            intervalId = setInterval(function() {
515                updateAllTimers();
516                if (expiryHandled) clearInterval(intervalId);
517            }, 1000);
518            bar.setAttribute('data-countdown-interval', intervalId);
519        }
520    }
521
522    /**
523     * Update countdown display
524     */
525    function updateCountdown(timerElement, countdownDate, countdownType) {
526        var now = new Date().getTime();
527        var countDate = new Date(countdownDate).getTime();
528        var distance = countDate - now;
529
530        if (distance < 0) {
531            return;
532        }
533
534        var days = Math.floor(distance / (1000 * 60 * 60 * 24));
535        var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
536        var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
537        var seconds = Math.floor((distance % (1000 * 60)) / 1000);
538
539        // Get display options from element attributes
540        var showDays = timerElement.getAttribute('data-show-days') !== 'false';
541        var showHours = timerElement.getAttribute('data-show-hours') !== 'false';
542        var showMinutes = timerElement.getAttribute('data-show-minutes') !== 'false';
543        var showSeconds = timerElement.getAttribute('data-show-seconds') !== 'false';
544
545        // Determine style (simple, digital, circular)
546        var style = timerElement.getAttribute('data-countdown-style') || 'simple';
547
548        if (style === 'simple') {
549            // Simple text format using custom labels (no space — label acts as suffix)
550            var _lDays    = timerElement.getAttribute('data-countdown-label-days');
551            var _lHours   = timerElement.getAttribute('data-countdown-label-hours');
552            var _lMinutes = timerElement.getAttribute('data-countdown-label-minutes');
553            var _lSeconds = timerElement.getAttribute('data-countdown-label-seconds');
554            var lDays    = _lDays    !== null ? _lDays    : 'd';
555            var lHours   = _lHours   !== null ? _lHours   : 'h';
556            var lMinutes = _lMinutes !== null ? _lMinutes : 'm';
557            var lSeconds = _lSeconds !== null ? _lSeconds : 's';
558            var parts = [];
559            if (showDays && days > 0) parts.push(days + lDays);
560            if (showHours && hours > 0) parts.push(hours + lHours);
561            if (showMinutes && minutes > 0) parts.push(minutes + lMinutes);
562            if (showSeconds) parts.push(seconds + lSeconds);
563
564            timerElement.textContent = parts.length > 0 ? parts.join(' ') : '0' + lSeconds;
565        } else if (style === 'digital') {
566            // Digital format: "05:03:45:30"
567            var displayText = '';
568            if (showDays) displayText += padZero(days) + ':';
569            if (showHours) displayText += padZero(hours) + ':';
570            if (showMinutes) displayText += padZero(minutes) + ':';
571            if (showSeconds) displayText += padZero(seconds);
572
573            // Remove trailing colon if no seconds are shown
574            timerElement.textContent = displayText.replace(/:$/, '');
575        } else if (style === 'circular' || style === 'box') {
576            // Circular or box format - update boxes
577            updateBoxCountdown(timerElement, days, hours, minutes, seconds, showDays, showHours, showMinutes, showSeconds);
578        } else if (countdownType === 'compact') {
579            // Legacy compact format
580            if (days > 0) {
581                timerElement.textContent = days + 'd ' + hours + 'h';
582            } else if (hours > 0) {
583                timerElement.textContent = hours + 'h ' + minutes + 'm';
584            } else {
585                timerElement.textContent = minutes + 'm ' + seconds + 's';
586            }
587        } else {
588            // Legacy detailed format
589            timerElement.textContent = padZero(days) + ':' + padZero(hours) + ':' + padZero(minutes) + ':' + padZero(seconds);
590        }
591    }
592
593    /**
594     * Update box-style countdown display (circular or rounded squares)
595     */
596    function updateBoxCountdown(timerElement, days, hours, minutes, seconds, showDays, showHours, showMinutes, showSeconds) {
597        var bgColor    = timerElement.getAttribute('data-countdown-bg-color') || '';
598        var textColor  = timerElement.getAttribute('data-countdown-text-color') || '';
599        var labelColor = timerElement.getAttribute('data-countdown-label-color') || '';
600        var _rawDays  = timerElement.getAttribute('data-countdown-label-days');
601        var _rawHours = timerElement.getAttribute('data-countdown-label-hours');
602        var _rawMins  = timerElement.getAttribute('data-countdown-label-minutes');
603        var _rawSecs  = timerElement.getAttribute('data-countdown-label-seconds');
604        var labelDays  = _rawDays  !== null ? _rawDays  : 'Day';
605        var labelHours = _rawHours !== null ? _rawHours : 'Hour';
606        var labelMins  = _rawMins  !== null ? _rawMins  : 'Minute';
607        var labelSecs  = _rawSecs  !== null ? _rawSecs  : 'Second';
608
609        function applyColors(numberEl, labelEl) {
610            if (textColor && numberEl)  numberEl.style.color = textColor;
611            if (labelColor && labelEl)  labelEl.style.color  = labelColor;
612            if (bgColor && numberEl) {
613                var wrapper = numberEl.closest('.countdown-unit, .hb-countdown-unit, .countdown-circle-box, .countdown-box');
614                if (wrapper) {
615                    wrapper.style.backgroundColor = bgColor;
616                    wrapper.style.borderColor = bgColor;
617                }
618            }
619        }
620
621        // Update days box (works with both .countdown-circle-box and .countdown-box classes)
622        if (showDays) {
623            var daysBox = timerElement.querySelector('.countdown-days');
624            if (daysBox) {
625                daysBox.textContent = String(days).padStart(2, '0');
626            }
627            var daysLabel = timerElement.querySelector('.countdown-days-label, .countdown-day-text');
628            if (daysLabel && daysLabel.textContent !== labelDays) daysLabel.textContent = labelDays;
629            applyColors(daysBox, daysLabel);
630        }
631
632        // Update hours box
633        if (showHours) {
634            var hoursBox = timerElement.querySelector('.countdown-hours');
635            if (hoursBox) {
636                hoursBox.textContent = String(hours).padStart(2, '0');
637            }
638            var hoursLabel = timerElement.querySelector('.countdown-hours-label, .countdown-hour-text');
639            if (hoursLabel && hoursLabel.textContent !== labelHours) hoursLabel.textContent = labelHours;
640            applyColors(hoursBox, hoursLabel);
641        }
642
643        // Update minutes box
644        if (showMinutes) {
645            var minutesBox = timerElement.querySelector('.countdown-minutes');
646            if (minutesBox) {
647                minutesBox.textContent = String(minutes).padStart(2, '0');
648            }
649            var minutesLabel = timerElement.querySelector('.countdown-minutes-label, .countdown-minute-text, .countdown-minite-text');
650            if (minutesLabel && minutesLabel.textContent !== labelMins) minutesLabel.textContent = labelMins;
651            applyColors(minutesBox, minutesLabel);
652        }
653
654        // Update seconds box
655        if (showSeconds) {
656            var secondsBox = timerElement.querySelector('.countdown-seconds');
657            if (secondsBox) {
658                secondsBox.textContent = String(seconds).padStart(2, '0');
659            }
660            var secondsLabel = timerElement.querySelector('.countdown-seconds-label, .countdown-second-text');
661            if (secondsLabel && secondsLabel.textContent !== labelSecs) secondsLabel.textContent = labelSecs;
662            applyColors(secondsBox, secondsLabel);
663        }
664    }
665
666    /**
667     * Pad number with leading zero
668     */
669    function padZero(num) {
670        return (num < 10 ? '0' : '') + num;
671    }
672})(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.