PageSourceSearch

https://book2look.de/Content/js/overview.js

js book2look.de collected 2026-10-02 09:56:55 UTC 56,160 bytes, 1,484 lines download raw bytes

1(function ($) {
2    'use strict';
3
4    var state = {
5        bDark: false,
6        count: 485611996,        
7        live: true,
8        heroFeat: 0,
9        market: 'Global',
10        bIndex: 0,
11        bPlaying: false,
12        bView: 'shelf',
13        wallQuery: '',
14        wallPub: 'All',
15        audioOnly: false,
16        peek: null,
17        wallShown: 24,
18        textStep: 0,
19        contrast: false,
20        reduceMotion: false,
21        spacing: false,
22        plain: false,
23        consent: true,
24        prefsOpen: false,
25        lang: 'en',
26        contactSent: false,
27        coverOk: {},
28        navOpen: false,
29        feed: null,
30        feedLive: false
31    };
32
33    var markets = ['Global', 'DACH', 'UK', 'US'];
34
35    var marketSeed = {
36        'Global': 1,
37        'DACH': 4,
38        'UK': 6,
39        'US': 7
40    };
41    var currentMarket = 'Global';
42
43    var langMarketMap = {
44        en: 'UK',
45        us: 'US',
46        de: 'DACH'
47    };
48
49    function marketForLang(lang) {
50        return langMarketMap[String(lang || '').toLowerCase()] || 'Global';
51    }
52
53    function getMenuLang() {
54        try {
55            if (window.B2L && typeof window.B2L.getLang === 'function') {
56                return window.B2L.getLang();
57            }
58        } catch (e) { }
59
60        return null;
61    }
62
63    function syncMarketWithLang(lang) {
64        currentMarket = marketForLang(lang || getMenuLang());
65        renderMarketTabs();
66        renderTopBiblets();
67    }
68
69    function isOverviewPage() {
70        var pageEl = document.getElementById('lpPageName');
71        return !!pageEl && pageEl.value === 'overview';
72    }
73
74    var pollTimer = null;
75    var tickTimer = null;
76    var heroTimer = null;
77
78    var counterIntervalSec = Number(window.counterIntervalSec || 15) || 15;
79
80    var zooms = [1, 1.15, 1.3];
81    var tSizes = ['13px', '15px', '17px'];
82    var tLabels = ['A', 'A+', 'A++'];
83
84    var palette = [
85        '#2F5F9E',
86        '#E0B21C',
87        '#1F6E5C',
88        '#B3402A',
89        '#5B3A6E',
90        '#3E6C8E',
91        '#7A2E3B',
92        '#C77F1A',
93        '#42556B',
94        '#1E6FA8',
95        '#8A6D2F',
96        '#2A2118'
97    ];
98
99    var rawBooks = [
100        ['XXnSStVzJP', ['9780898000368', '9780898001013'], 'Revelations of Mind', 'Tarthang Tulku', 'Dharma Publishing', 1, 'https://bookshop.org/book/9780898000368', 'Bookshop.org', '#2F5F9E'],
101        ['XlSZkDZUl9', ['9780898005622', '9780913546918'], 'Mother of Knowledge', 'Red Wheel/Weiser', 'Dharma Publishing', 1, 'https://bookshop.org/book/9780913546918', 'Bookshop.org', '#E0B21C'],
102        ['wVvSuZHn6u', ['9780898000849', '9788980048106'], 'Joy of Being', 'Tarthang Tulku', 'Dharma Publishing', 1, 'https://bookshop.org/book/9780898000849', 'Bookshop.org', '#E4D2A8'],
103        ['U1WsUk64zl', ['9780898003932', '9788980047642'], 'Now That I Come to Die', 'Longchenpa', 'Dharma Publishing', 1, 'https://bookshop.org/book/9780898003932', 'Bookshop.org', '#7C8FA6'],
104        ['SNCevxWL8G', ['9780898003406', '9780898003482'], 'Awakening the Balanced Mind', 'Tarthang Tulku', 'Dharma Publishing', 1, 'https://bookshop.org/book/9780898003406', 'Dharma Publishing', '#B489C9'],
105        ['PxjJf3OAK3', ['9780898005332', '9780913546987'], 'Dhammapada', 'Red Wheel/Weiser', 'Dharma Publishing', 1, 'https://bookshop.org/book/9780913546987', 'Bookshop.org', '#B3402A'],
106        ['puRezR2nnU', ['9780898005455', '9780913546956'], 'Knowledge of Freedom: Time to Change', 'Tarthang Tulku', 'Dharma Publishing', 1, 'https://bookshop.org/book/9780913546956', 'Bookshop.org', '#1E6FA8'],
107        ['pbF3M7vbCF', ['9780898004212', '9788980048298'], 'Kum Nye Tibetan Yoga', 'Tarthang Tulku', 'Dharma Publishing', 1, 'https://bookshop.org/book/9780898004212', 'Bookshop.org', '#D9A227'],
108        ['orcRJ7JiKb', ['9780898003499', '9780898003505'], 'Mind Space', 'Ronald E Purser', 'Dharma Publishing', 1, 'https://bookshop.org/book/9780898003499', 'Bookshop.org', '#8FC4E8'],
109        ['73Vc5IyiJE', ['9780898005646', '9780913546567'], 'Openness Mind', 'Tarthang Tulku', 'Dharma Publishing', 1, 'https://bookshop.org/book/9780898005646', 'Bookshop.org', '#1F6E5C'],
110        ['OBzA62XMqg', ['9780898002669', '9788980047567'], 'Dynamics of Time and Space', 'Tarthang Tulku', 'Dharma Publishing', 1, 'https://bookshop.org/book/9780898002669', 'Bookshop.org', '#3B5F52'],
111        ['eEmfHVDJNi', ['9780898000870', '9780898001020'], 'Dimensions of Mind', 'Tarthang Tulku', 'Dharma Publishing', 1, 'https://bookshop.org/book/9780898000870', 'Bookshop.org', '#6E4B8A'],
112        ['UbIkgEcvGg', ['9781394284764', '9781394284788', '9781394284795'], 'Green Maps', 'John Wiley and Sons', 'Wiley', 0, 'https://www.wiley.com/en-us
112/search?pq=9781394284764', 'Wiley.com', '#1F3B2C'],
113        ['PfqpHphIcZ', ['9781394426447', '9781394426461', '9781394426478'], 'Introduction to Radar Remote Sensing', 'Howard A. Zebker', 'Wiley', 0, 'https://www.wiley.com/en-us/search?pq=9781394426447', 'Wiley.com', '#3E6C8E'],
114        ['FDcXx1gk8x', ['9781394406487', '9781394406500', '9781394406517'], 'The FET Centennial', 'John Wiley and Sons', 'Wiley', 0, 'https://www.wiley.com/en-us/search?pq=9781394406487', 'Wiley.com', '#2C4A70'],
115        ['HmVugEXSaW', ['9781479842445', '9781479842506'], 'Jewish Women at Home in the World', 'Melissa R. Klapper', 'NYU Press', 0, 'https://nyupress.org/9781479842445', 'NYU Press', '#7A2E3B'],
116        ['GGINJh1ZWH', ['9781479852406'], 'Before the Badge', 'Samantha J. Simon', 'NYU Press', 0, 'https://nyupress.org/9781479852406', 'NYU Press', '#2A2118'],
117        ['gLD11xntz8', ['9781479834532', '9781479834549'], 'Prison Hashtags', 'Calvin John Smiley, Jan Haldipur', 'NYU Press', 0, 'https://nyupress.org/9781479834532', 'NYU Press', '#42556B'],
118        ['bAh8jODhto', ['9781421455518', '9781421455525'], 'Start-Up Factory', 'James Rushing Daniel', 'Johns Hopkins University Press', 0, 'https://www.press.jhu.edu/books/browse-all?keyword=9781421455525', 'JHU Press', '#5B3A6E'],
119        ['43tveNRkuZ', ['9781421455303', '9781421455310'], 'Tablet Computer', 'Elizabeth R. Petrick', 'Johns Hopkins University Press', 0, 'https://www.press.jhu.edu/books/browse-all?keyword=9781421455310', 'JHU Press', '#8A6D2F'],
120        ['jbhwZ8hka8', ['9781421455440', '9781421455457'], 'Spare Parts', 'Philip Scranton', 'Johns Hopkins University Press', 0, 'https://www.press.jhu.edu/books/browse-all?keyword=9781421455457', 'JHU Press', '#4A6070'],
121        ['7vrsk8eL97', ['9780764347917'], 'The Way of Archery', 'Schiffer', 'Schiffer Publishing, Ltd.', 0, 'https://schifferbooks.com/search?type=product&q=9780764347917', 'Schiffer', '#B3402A'],
122        ['5FbWF7RNMN', ['9780764353499'], 'Sharpening Woodworking Tools', 'Schiffer Craft', 'Schiffer Publishing, Ltd.', 0, 'https://schifferbooks.com/search?type=product&q=9780764353499', 'Schiffer', '#C77F1A'],
123        ['xLliEiRXuP', ['9780369781451', '9781335614261'], 'Baby-Bound to the Greek', 'Millie Adams', 'Harlequin', 0, 'https://www.harlequin.com/search.html?w=9781335614261', 'Harlequin', '#A8324E'],
124        ['smtMbE6MqC', ['9781038990921'], 'Prideful Billionaire For Christmas', 'Jessica Gilmore', 'Mills and Boon Forever', 0, 'https://www.millsandboon.com.au/search/?q=9781038990921', 'Mills and Boon', '#8E2F5F']
125    ];
126
127    var heroFeatures = [
128        {
129            label: 'Sample pages',
130            kicker: 'REAL PAGES',
131            title: 'The book, not a description',
132            desc: "Readers turn the publisher's own pages, set in the book's own typography."
133        },
134        {
135            label: 'Audio',
136            kicker: 'AUDIO',
137            title: 'Hear the narrator first',
138            desc: 'Attach the audiobook excerpt or an author reading. Unlimited clips, played inside the Biblet.'
139        },
140        {
141            label: 'Video',
142            kicker: 'VIDEO',
143            title: 'Trailers, authors, events',
144            desc: 'Unlimited video in the same embed: book trailers, author introductions, festival footage.'
145        },
146        {
147            label: 'One book, three formats',
148            kicker: 'FORMAT LINKING',
149            title: 'Print, ebook and audiobook, linked',
150            desc: 'One Biblet carries every edition, so a single link covers the hardback, the ePub and the audiobook.'
151        },
152        {
153            label: 'Accessibility',
154            kicker: 'ACCESSIBILITY',
155            title: 'Built for every reader',
156            desc: 'Keyboard navigable, screen-reader labelled and contrast-checked to WCAG 2.1 AA, ready for the European Accessibility Act.'
157        },
158        {
159            label: 'Reviews & shops',
160            kicker: 'REVIEWS & SHOPS',
161            title: 'Proof, then a buy button',
162            desc: 'Reviews found across the net attach automatically, and every Biblet ends at the shop links you define.'
163        }
164    ];
165
166    var features = [
167        {
168            tag: 'Biblet',
169            title: 'The Biblet',
170            desc: 'One embed carries the real sample pages plus unlimited audio and video clips.'
171        },
172        {
173            tag: 'Reader',
174            title: 'ePub Reader',
175            desc: 'A full ePub reader for your eBook samples on mobile. The sample reads properly on a phone, not just a desktop.'
176        },
177        {
178            tag: 'Social',
179            title: 'Born social',
180            desc: 'Every share puts a working sample into a feed, with a permanent URL behind it.'
181        },
182        {
183            tag: 'Embed',
184            title: 'Embed anywhere',
185            desc: 'Bloggers and online stores embed their own customised sample, carrying their logo and their links.'
186        },
187        {
188            tag: 'Reviews',
189            title: 'Reviews, connected',
190            desc: 'We find online reviews of your books across the net and connect them to your Biblet automatically.'
191        },
192        {
193            tag: 'Reports',
194            title: 'Know where books go',
195            desc: 'Reporting for every Biblet covers views, opens, page depth and shop clicks, per ISBN and per channel.'
196        },
197        {
198            tag: 'Sell',
199            title: 'Sell your way',
200            desc: 'Define your own shop links and support the booksellers you choose. Every Biblet ends at a buy button.'
201        },
202        {
203            tag: 'QR',
204            title: 'Print to pixel',
205            desc: 'Every Biblet ships with a permanent URL and a print-ready QR code for the book itself, for posters and for catalogues.'
206        }
207    ];
208
209    var communityStats = [
210        {
211            count: '',
212            note: 'communities using book2look'
213        },
214        {
215            count: '',
216            note: 'community Biblet views'
217        },
218        {
219            count: '',
220            note: 'Book Wall Biblet clicks / month'
221        },
222        {
223            count: '193+',
224            note: 'countries reached'
225        }
226    ];
227
228    var communities = [
229        {
230            name: 'Reading communitiesaa',
231            count: '1,180',
232            note: 'Book clubs and reader forums embedding Biblets in their discussion threads.'
233        },
234        {
235            name: 'Libraries & education',
236            count: '640',
237            note: 'Public library portals and university reading lists linking straight to samples.'
238        },
239        {
240            name: 'Retail storefronts',
241            count: '410',
242            note: 'Independent bookshops running customised Biblets on their product pages.'
243        },
244        {
245            name: 'Blogs & review sites',
246            count: '250',
247            note: 'Reviewers embedding logo-customised samples inside their posts.'
248        }
249    ];
250
251    var metricsFields = {};
252
253    var metricsOrder = [
254        { key: 'Average_Session_duration', label: 'average time spent on a Biblet (min:sec)' },
255        { key: 'Session_Page_View', label: 'sample pages viewed per session' },
256        { key: 'EmbedsCount', label: 'websites embedding Biblets' },
257        { key: 'BibletISBNCount', label: 'ISBNs with a Biblet' },
258        { key: 'BibletCOMMCount', label: 'communities using book2look' },
259        { key: 'BookWallClick', label: 'Book Wall Biblet clicks / month' },
260        { key: 'ShoplinkViews', label: 'of views end in a shop-link click' }
261    ];
262
263    var communityStatValueOverrides = {};
264
265    var topBiblets = { Global: [], DACH: [], UK: [], US: [] };
266
267    var publisherSteps = [
268        {
269            n: '01',
270            title: 'Upload once',
271            body: 'Add sample pages, audio and video, and your shop links. Bulk-create Biblets for a whole list from the metadata you already maintain.'
272        },
273        {
274            n: '02',
275            title: 'Distribute everywhere',
276            body: 'Your Biblet travels to retailers, communities, reviewers and the trade databases, carrying their branding and your content.'
277        },
278        {
279            n: '03',
280            title: 'Read the numbers',
281            body: 'Per-ISBN reporting on views, page depth, media plays and shop-link clicks, exportable for your own analysis.'
282        }
283    ];
284
285    function escapeHtml(value) {
286        return $('<div>').text(value == null ? '' : String(value)).html();
287    }
288
289    // Adds a comma after every 3 digits (e.g. 486119514 -> 486,119,514); non-numeric values are returned unchanged
290    function formatThousands(value) {
291        if (value === null || value === undefined) return value;
292        var str = String(value).trim();
293        if (!/^-?\d+(\.\d+)?$/.test(str)) return value;
294        var parts = str.split('.');
295        parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
296        return parts.join('.');
297    }
298
299    var metricsThousandsKeys = ['EmbedsCount', 'BibletISBNCount', 'BibletCOMMCount', 'BookWallClick', 'ShoplinkViews'];
300
301    function escapeAttr(value) {
302        return escapeHtml(value).replace(/"/g, '&quot;');
303    }
304
305    function syncDarkFlag() {
306        try {
307            document.documentElement.setAttribute('data-b2l-dark', state.bDark ? '1' : '0');
308            document.documentElement.setAttribute('data-motion', state.reduceMotion ? 'off' : 'on');
309        } catch (e) { }
310    }
311
312    function getA11y() {
313        return {
314            zoom: zooms[state.textStep] || 1,
315            fontSize: tSizes[state.textStep] || '13px',
316            filter: state.contrast ? 'contrast(1.28) saturate(1.15)' : 'none',
317            ls: state.spacing ? '0.035em' : 'normal',
318            ws: state.spacing ? '0.12em' : 'normal',
319            pulse: state.reduceMotion ? 'none' : 'b2lPulse 2s infinite',
320            float: state.reduceMotion ? 'none' : 'b2lFloat 5s ease-in-out infinite',
321            contrastOn: state.contrast,
322            motionOff: state.reduceMotion,
323            spacingOn: state.spacing,
324            plainOn: state.plain
325        };
326    }
327
328    function books() {
329        return $.map(rawBooks, function (r) {
330            var category = 'Books';
331
332            if (r[4] === 'Dharma Publishing') {
333                category = 'Spirituality';
334            } else if (r[4] === 'Wiley') {
335                category = 'Academic';
336            } else if (r[4] === 'NYU Press') {
337                category = 'Academic';
338            } else if (r[4] === 'Johns Hopkins University Press') {
339                category = 'Academic';
340            } else if (r[4] === 'Schiffer Publishing, Ltd.') {
341                category = 'Craft';
342            } else if (r[4] === 'Harlequin' || r[4] === 'Mills and Boon Forever') {
343                category = 'Fiction';
344            }
345
346            return {
347                bookID: r[0],
348                isbns: r[1],
349                isbn: r[1][0],
350                title: r[2],
351                author: r[3],
352                publisher: r[4],
353                hasAudio: !!r[5],
354                shopUrl: r[6],
355                shop: r[7],
356                color: r[8],
357                series: '',
358                category: category,
359                formats: 'Print · eBook',
360                synopsis: '',
361                shops: r[7],
362                pubShort: r[4],
363                bibletUrl: 'https://www.book2look.com/book/' + r[0],
364                cover: coverUrl(r[0]),
365                coverCss: 'url("' + coverUrl(r[0]) + '")',
366                hasCover: true
367            };
368        });
369    }
370
371    function coverUrl(bookID) {
372        return 'https://www.book2look.com/BookContent/Flipbooks/' + bookID + '_assets/images/' + bookID + '_widget.jpg';
373    }
374
375    function logoUrl(logo) {
376        return logo ? 'https://www.book2look.com/BookContent/PublisherL
376ogo/' + logo : '';
377    }
378
379    function heroBook() {
380        var list = books();
381        return list[state.heroFeat % list.length];
382    }
383
384    // Hero tab texts from the selected language JSON (hero_features), falling back to the built-in English list
385    function localizedHeroFeatures() {
386        try {
387            var strings = window.B2L && window.B2L.translations;
388            if (strings && Array.isArray(strings.hero_features) && strings.hero_features.length === heroFeatures.length) {
389                return strings.hero_features;
390            }
391        } catch (e) { }
392
393        return heroFeatures;
394    }
395
396    function tr(key, fallback) {
397        try {
398            var strings = window.B2L && window.B2L.translations;
399            if (strings && typeof strings[key] === 'string' && strings[key] !== '') {
400                return strings[key];
401            }
402        } catch (e) { }
403
404        return fallback;
405    }
406
407    function renderHeroTabs() {
408        var html = '';
409        var list = localizedHeroFeatures();
410        var activeIndex = state.heroFeat % list.length;
411
412        $.each(list, function (i, feature) {
413            var active = i === activeIndex;
414            var bg = active ? '#33261A' : '#FFFDF8';
415            var fg = active ? '#FFFDF8' : '#33261A';
416
417            html += '<button type="button" aria-pressed="' + active + '" class="b2l-inline-cf1f7e4e87" data-hero-index="' + i + '" style="background:' + bg + ';color:' + fg + '">' + escapeHtml(feature.label) + '</button>';
418        });
419
420        $('#heroTabs').html(html);
421    }
422
423    function renderHero() {
424        var list = localizedHeroFeatures();
425        var feature = list[state.heroFeat % list.length];
426        var book = heroBook();
427
428        renderHeroTabs();
429
430        $('#heroBookLink').attr('href', book.bibletUrl);
431        $('#heroBookLink').attr('aria-label', 'Open the Biblet for ' + book.title + ' in a new tab');
432        $('#heroBookColor').css('background', book.color);
433        $('#heroBookCover').css({
434            'background-image': 'url("' + book.cover + '")',
435            'background-size': 'cover',
436            'background-position': 'center'
437        });
438
439        $('#heroFeatureKicker').text(feature.kicker);
440        $('#heroFeatureTitle').text(feature.title);
441        $('#heroFeatureDesc').text(feature.desc);
442    }
443
444    function renderMetrics() {
445        var values = [state.count.toLocaleString('en-US')];
446        var displayValues = [values[0]];
447        var labels = ['look inside views, live counter'];
448
449        $.each(metricsOrder, function (i, metric) {
450            var raw = metricsFields[metric.key];
451            var value = raw === null || raw === undefined ? '' : raw;
452
453            values.push(value);
454            displayValues.push(metricsThousandsKeys.indexOf(metric.key) !== -1 ? formatThousands(value) : value);
455            labels.push(metric.label);
456        });
457
458        try {
459            var strings = window.B2L && window.B2L.translations;
460            if (strings) {
461                var communitiesLabel = strings.communities && strings.communities.stats_labels && strings.communities.stats_labels.communities_count;
462
463                labels = [
464                    strings.look_inside_views || labels[0],
465                    strings.average_time || labels[1],
466                    strings.sample_pages || labels[2],
467                    strings.websites || labels[3],
468                    strings.isbns || labels[4],
469                    communitiesLabel || labels[5],
470                    strings.book_wall_clicks || labels[6],
471                    strings.shop_link_clicks || labels[7]
472                ];
473            }
474        } catch (e) { }
475
476        var html = '';
477
478        $.each(values, function (i, value) {
479            if (escapeHtml(value) > 0)
480                html += '<div class="b2l-inline-b349161da0"><div class="b2l-inline-fd901c42b7">' + escapeHtml(displayValues[i]) + '</div><div class="b2l-inline-1c0a01a836">' + escapeHtml(labels[i]) + '</div></div>';
481        });
482
483        $('#metricsContainer').html(html);
484    }
485
486    function renderFeatures() {
487        var html = '';
488        var list = features;
489
490        try {
491            var strings = window.B2L && window.B2L.translations;
492            if (strings && Array.isArray(strings.features) && strings.features.length) {
493                list = strings.features;
494            }
495        } catch (e) { }
496
497        $.each(list, function (i, feature) {
498            html += '<div class="b2l-inline-e4f31ff480"><div class="b2l-inline-a0b89b6b52">' + escapeHtml(feature.tag) + '</div>
498<h3 class="b2l-inline-17d6ddaf9f">' + escapeHtml(feature.title) + '</h3><p class="b2l-inline-9036f4d157">' + escapeHtml(feature.desc) + '</p></div>';
499        });
500
501        $('#featuresContainer').html(html);
502    }
503
504    function renderMarketTabs() {
505        if (!isOverviewPage()) {
506            return;
507        }
508
509        var html = '';
510
511        $.each(markets, function (i, market) {
512            var active = market === currentMarket;
513            var bg = active ? '#33261A' : '#FFFDF8';
514            var fg = active ? '#FFFDF8' : '#33261A';
515
516            html += '<button type="button" class="b2l-inline-3d124426ef" aria-pressed="' + active + '" data-market="' + market + '" style="background:' + bg + ';color:' + fg + '">' + market + '</button>';
517        });
518
519        $('#marketTabs').html(html);
520    }
521
522    function renderTopBiblets() {
523        if (!isOverviewPage()) {
524            return;
525        }
526
527        var list = (topBiblets && topBiblets[currentMarket]) || [];
528        var html = '';
529
530        $.each(list.slice(0, 10), function (i, item) {
531            var color = item.color || palette[i % palette.length];
532            var cover = item.cover || coverUrl(item.BIB_ID);
533            var bibletUrl = item.bibletUrl || ('https://www.book2look.com/book/' + item.BIB_ID);
534            var views = typeof item.VIEWCOUNT === 'number' ? item.VIEWCOUNT.toLocaleString('en-US') : item.VIEWCOUNT;
535            var isDach = currentMarket === 'DACH';
536            var publisherLabel = isDach ? 'der Verleger' : 'Publisher';
537            var authorLabel = isDach ? 'Autorin' : 'Author';
538            //var pubLine = publisherLabel + ': ' + item.PUBNAME + ' | ' + authorLabel + ': ' + item.BIBLEAUTHOR;
539            var pubLine = item.PUBNAME + ' | ' + authorLabel + ': ' + item.BIBLEAUTHOR;
540            var logo = logoUrl(item.LOGO);
541            var logoBoxHtml = logo ? '<div style="width:90px;height:52px;display:flex;align-items:center;justify-content:center;background:var(--sf);border:0px solid var(--ln);border-radius:6px;overflow:hidden;"><img src="' + escapeAttr(logo) + '" alt="' + escapeAttr(item.PUBNAME) + ' logo" style="max-width:100%;max-height:100%;object-fit:contain;" onerror="this.parentElement.style.display=\'none\'" /></div>' : '';
542            var viewsBoxHtml = '<div style="background:var(--sf);border:1px solid var(--ln);border-radius:6px;padding:4px 10px;"><span class="b2l-inline-941bef18a5">' + views + '</span></div>';
543                        
544            html += '<div class="b2l-inline-d6efed7462"><span class="b2l-inline-5c9d9e4cb6">' + (i + 1) + '</span><div class="b2l-inline-1bbb558db5" style="background:' + color + '"><a href="' + escapeAttr(bibletUrl) + '" target="_blank" rel="noopener" aria-label="Open the Biblet for ' + escapeAttr(item.BIBLETTITLE) + '" style="position:absolute;inset:0;display:block;"><div role="img" aria-label="Cover artwork" class="b2l-inline-7807df6ec2" style="background-image:url(\'' + cover + '\');"></div></a></div><div><a href="' + escapeAttr(bibletUrl) + '" target="_blank" rel="noopener" class="b2l-inline-350fa3d3fc" style="display:block;text-decoration:none;color:inherit;">' + item.BIBLETTITLE + '</a><div class="b2l-inline-899868bb4e">' + pubLine + '</div></div><div style="display:flex;align-items:center;gap:8px;">' + logoBoxHtml + viewsBoxHtml + '</div></div>';
545        });
546
547        $('#topBibletsContainer').html(html);
548    }
549
550    var communityStatsLabelKeys = ['communities_count', 'community_views', 'book_wall_clicks', 'countries_reached'];
551
552    function renderCommunityStats() {
553        var html = '';
554        var statsLabels = null;
555
556        try {
557            var strings = window.B2L && window.B2L.translations;
558            if (strings && strings.communities && strings.communities.stats_labels) {
559                statsLabels = strings.communities.stats_labels;
560            }
561        } catch (e) { }
562
563        $.each(communityStats, function (i, community) {
564            var key = communityStatsLabelKeys[i];
565            var note = (statsLabels && statsLabels[key]) || community.note;
566            var override = communityStatValueOverrides[key];
567            var count = (override === undefined || override === null || override === '') ? community.count : override;
568
569            if (key === 'communities_count' || key === 'community_views' || key === 'book_wall_clicks') {
570                count = formatThousands(count);
571            }
572
573            html += '<div><div style="font:700 24px \'IBM Plex Mono\',monospace; color:var(--tx);">' +
574                escapeHtml(count) +
575                '</div><div style="font-size:12px; color:var(--mut);">' +
576                escapeHtml(note) +
577                '</div></div>';
578        });
579
580        $('#communityStatsContainer').html(html);
581    }
582
583    function renderCommunities() {
584        var html = '';
585        var localizedItems = null;
586
587        try {
588            var strings = window.B2L && window.B2L.translations;
589            if (strings && strings.communities && Array.isArray(strings.communities.items)) {
590                localizedItems = strings.communities.items;
591            }
592        } catch (e) { }
593
594        $.each(communities, function (i, community) {
595            var name = community.name;
596            var note = community.note;
597            var localized = localizedItems && localizedItems[i];
598
599            if (localized) {
600                name = localized.name || name;
601                note = localized.note || note;
602            }
603
604            html += '<div class="b2l-inline-7daab361e4"><span class="b2l-inline-225ba21800">' + escapeHtml(community.count) + '</span><div><div class="b2l-inline-47c19024e6">' + escapeHtml(name) + '</div><div class="b2l-inline-e9b892ba5f">' + escapeHtml(note) + '</div></div></div>';
605        });
606
607        $('#communitiesContainer').html(html);
608    }
609
610    //OLD Function
611    /*function loadOverviewContent() {
612        //var loc = window.location.pathname || '';
613        //var baseDir = loc.substring(0, loc.lastIndexOf('/') + 1);
614        //var filename = 'overview-content.json';
615        //var primaryUrl = baseDir + 'Content/json/' + filename;
616        //var fallbackUrl = 'Content/json/' + filename;
617        //var rootUrl = '/Content/json/' + filename;
618        var loc = window.location.pathname || '';
619        var baseDir = loc.substring(0, loc.lastIndexOf('/') + 1);
620        var filename = 'overview-content.json';
621        var primaryUrl = baseDir + 'BookContent/WebsiteReport/' + filename;
622        var fallbackUrl = 'BookContent/WebsiteReport/' + filename;
623        var rootUrl = '/BookContent/WebsiteReport/' + filename;
624
625        fetch(primaryUrl)
626            .then(function (res) {
627                if (!res.ok) return fetch(fallbackUrl);
628                return res;
629            })
630            .then(function (res) {
631                if (!res.ok) return fetch(rootUrl);
632                return res;
633            })
634            .then(function (res) {
635                if (!res.ok) throw new Error('overview-content.json not found');
636                return res.json();
637            })
638            .then(function (data) {
639                if (!data) return;
640
641                $.each(metricsOrder, function (i, metric) {
642                    if (data[metric.key] !== undefined) {
643                        metricsFields[metric.key] = data[metric.key];
644                    }
645                });
646
647                if (Array.isArray(data.markets) && data.markets.length) {
648                    markets = data.markets;
649                }
650
651                if (data.marketSeed && typeof data.marketSeed === 'object') {
652                    marketSeed = data.marketSeed;
653                }
654
655                if (Array.isArray(data.books) && data.books.length) {
656                    rawBooks = data.books;
657                }
658
659                if (Array.isArray(data.communityStats) && data.communityStats.length) {
660                    communityStats = data.communityStats;
661                }
662
663                if (Array.isArray(data.communities) && data.communities.length) {
664                    communities = data.communities;
665                }
666
667                if (data.BibletCOMMCount !== undefined) {
668                    communityStatValueOverrides.communities_count = data.BibletCOMMCount;
669                }
670
671                if (data.CommunityBibletViews !== undefined) {
672                    communityStatValueOverrides.community_views = data.CommunityBibletViews;
673                }
674
675                if (data.CommunityBookWallClick !== undefined) {
676                    communityStatValueOverrides.book_wall_clicks = data.CommunityBookWallClick;
677                }
678
679                topBiblets = {
680                    Global: Array.isArray(data.Top_BibletALL) ? data.Top_BibletALL : topBiblets.Global,
681                    DACH: Array.isArray(data.Top_BibletDE) ? data.Top_BibletDE : topBiblets.DACH,
682                    UK: Array.isArray(data.Top_BibletUK) ? data.Top_BibletUK : topBiblets.UK,
683                    US: Array.isArray(data.Top_BibletUS) ? data.Top_BibletUS : topBiblets.US
684                };
685
686                renderHero();
687                renderMetrics();
688                renderMarketTabs();
689                renderTopBiblets();
690                renderCommunityStats();
691                renderCommunities();
692            })
693            .catch(function () { });
694    }*/
695    //New Function with Ajax Request
696    // Loads overview-content.json; if it is missing, loads overview-content-default.json and
697    // keeps checking for overview-content.json so it is picked up automatically once available.
698    var overviewContentPollTimer = null;
699    var OVERVIEW_CONTENT_POLL_MS = 60000;
700
701    function loadOverviewContent() {
702        var loc = window.location.pathname || '';
703        var baseDir = loc.substring(0, loc.lastIndexOf('/') + 1);
704        var filename = 'overview-content.json';
705        var defaultFilename = 'overview-content-default.json';
706
707        function candidateUrls(name) {
708            return [
709                baseDir + 'BookContent/WebsiteReport/' + name,
710                'BookContent/WebsiteReport/' + name
711            ];
712        }
713
714        function stopPolling() {
715            if (overviewContentPollTimer) {
716                clearInterval(overviewContentPollTimer);
717                overviewContentPollTimer = null;
718            }
719        }
720
721        function startPolling() {
722            stopPolling();
723            overviewContentPollTimer = setInterval(function () {
724                loadJson(candidateUrls(filename), null, stopPolling);
725            }, OVERVIEW_CONTENT_POLL_MS);
726        }
727
728        // Tries each url in turn; on the first success applies the data, otherwise calls onFail
729        function loadJson(urls, onFail, onLoaded) {
730            if (!urls.length) {
731                if (onFail) onFail();
732                return;
733            }
734
735            $.ajax({
736                url: urls[0],
737                type: 'GET',
738                dataType: 'json',
739                cache: false,
740
741                success: function (data) {
742                    if (!data) {
743                        loadJson(urls.slice(1), onFail, onLoaded);
744                        return;
745                    }
746
747                    if (onLoaded) onLoaded();
748
749                    $.each(metricsOrder, function (i, metric) {
750                        if (data[metric.key] !== undefined) {
751                            metricsFields[metric.key] = data[metric.key];
752                        }
753                    });
754
755                    if (Array.isArray(data.markets) && data.markets.length) {
756                        markets = data.markets;
757                    }
758
759                    if (data.marketSeed && typeof data.marketSeed === 'object') {
760                        marketSeed = data.marketSeed;
761                    }
762
763                    if (Array.isArray(data.books) && data.books.length) {
764                        rawBooks = data.books;
765                    }
766
767                    if (Array.isArray(data.communityStats) && data.communityStats.length) {
768                        communityStats = data.communityStats;
769                    }
770
771                    if (Array.isArray(data.communities) && data.communities.length) {
772                        communities = data.communities;
773                    }
774
775                    if (data.BibletCOMMCount !== undefined) {
776                        communityStatValueOverrides.communities_count = data.BibletCOMMCount;
777                    }
778
779                    if (data.CommunityBibletViews !== undefined) {
780                        communityStatValueOverrides.community_views = data.CommunityBibletViews;
781                    }
782
783                    if (data.CommunityBookWallClick !== undefined) {
784                        communityStatValueOverrides.book_wall_clicks = data.CommunityBookWallClick;
785                    }
786
787                    topBiblets = {
788                        Global: Array.isArray(data.Top_BibletALL)
789                            ? data.Top_BibletALL
790                            : topBiblets.Global,
791
792                        DACH: Array.isArray(data.Top_BibletDE)
793                            ? data.Top_BibletDE
794                            : topBiblets.DACH,
795
796                        UK: Array.isArray(data.Top_BibletUK)
797                            ? data.Top_BibletUK
798                            : topBiblets.UK,
799
800                        US: Array.isArray(data.Top_BibletUS)
801                            ? data.Top_BibletUS
802                            : topBiblets.US
803                    };
804
805                    renderHero();
806                    renderMetrics();
807                    renderMarketTabs();
808                    renderTopBiblets();
809                    renderCommunityStats();
810                    renderCommunities();
811                },
812
813                error: function () {
814                    loadJson(urls.slice(1), onFail, onLoaded);
815                }
816            });
817        }
818
819        stopPolling();
820
821        loadJson(candidateUrls(filename), function () {
822            // overview-content.json not available: show the default content and wait for the real file
823            loadJson(candidateUrls(defaultFilename), startPolling, startPolling);
824        });
825    }
826
827    function renderPublisherSteps() {
828        $('#publisherStepsContainer').html('');
829
830        $.each(publisherSteps, function (i, step) {
831            $('#publisherStepsContainer').append('<div><div>' + escapeHtml(step.n) + '</div><h3>' + escapeHtml(step.title) + '</h3><p>' + escapeHtml(step.body) + '</p></div>');
832        });
833    }
834
835    function gdprText(lang) {
836        if (lang === 'de') {
837            return 'Wir verwenden Cookies und lokale Speicherung, um Spracheinstellungen, Barrierefreiheit und notwendige Präferenzen zu speichern. Weitere Informationen finden Sie in unserer Datenschutzerklärung.';
838        }
839
840        return 'We use cookies and local storage to remember language, accessibility settings and necessary preferences. Please see our privacy policy for more information.';
841    }
842
843    function loadLanguage() {        
844        var stored = null;
845        var browserLang = (navigator.language || 'en').toLowerCase();
846
847        // Prefer main.js, which reads the stored {lang, ts} JSON; the raw value is JSON, not a language code
848        stored = getMenuLang();
849
850        if (!stored) {
851            try {
852                stored = localStorage.getItem('b2l_lang');
853                if (stored && stored.charAt(0) === '{') {
854                    stored = (JSON.parse(stored) || {}).lang || null;
855                }
856            } catch (e) {
857                stored = null;
858            }
859        }
860
861        if (stored) {
862            state.lang = stored;
863        } else if (browserLang.indexOf('de') === 0) {
864            state.lang = 'de';
865        } else if (browserLang.indexOf('en-gb') === 0 || browserLang.indexOf('en-uk') === 0) {
866            state.lang = 'uk';
867        } else {
868            state.lang = 'en';
869        }
870    }
871
872    function applyLanguage() {
873        $('[data-i18n-key]').each(function () {
874            var key = $(this).attr('data-i18n-key');
875
876            if (!key) {
877                return;
878            }
879
880            if (window.overviewTranslations && window.overviewTranslations[key]) {
881                $(this).text(window.overviewTranslations[key]);
882            }
883        });
884
885        $('#gdprText').text(gdprText(state.lang));
886    }
887
888    function applyAccessibility() {
889      
889  var a11y = getA11y();
890
891        $('body').css({
892            'font-size': a11y.fontSize,
893            'letter-spacing': a11y.ls,
894            'word-spacing': a11y.ws,
895            'filter': a11y.filter
896        });
897
898        $('body').css('transform', 'scale(' + a11y.zoom + ')');
899        $('body').css('transform-origin', 'top left');
900
901        if (state.plain) {
902            $('body').addClass('b2l-plain-mode');
903        } else {
904            $('body').removeClass('b2l-plain-mode');
905        }
906
907        if (state.spacing) {
908            $('body').addClass('b2l-spacing-mode');
909        } else {
910            $('body').removeClass('b2l-spacing-mode');
911        }
912
913        if (state.contrast) {
914            $('body').addClass('b2l-contrast-mode');
915        } else {
916            $('body').removeClass('b2l-contrast-mode');
917        }
918
919        syncDarkFlag();
920    }
921
922    function consentChoice(choice) {
923        state.consent = false;
924        state.prefsOpen = false;
925
926        try {
927            localStorage.setItem('b2l_consent', choice);
928        } catch (e) { }
929
930        try {
931            if (window.LogThis && typeof window.LogThis.trackConsent === 'function') {
932                window.LogThis.trackConsent(choice);
933            }
934        } catch (e) { }
935
936        $('#gdprBanner').hide();
937    }
938
939    function checkConsent() {
940        var stored = null;
941
942        try {
943            stored = localStorage.getItem('b2l_consent');
944        } catch (e) { }
945
946        if (stored === 'accepted' || stored === 'rejected') {
947            state.consent = false;
948            $('#gdprBanner').hide();
949        } else {
950            state.consent = true;
951            $('#gdprBanner').show();
952        }
953
954        $('#gdprText').text(gdprText(state.lang));
955    }
956
957    function wallBase() {
958        return /(^|\/)m\//.test(location.pathname) ? '../' : '';
959    }
960
961    function loadWall() {
962        var file = 'viy7jrt29q.xml';
963        var sameOriginUrl = wallBase() + 'api/bookwall/' + file;
964
965        $.ajax({
966            url: sameOriginUrl,
967            type: 'GET',
968            dataType: 'xml',
969            cache: false,
970            success: function (xml) {
971                state.feed = parseWallXml(xml);
972                state.feedLive = true;
973                renderWall();
974            },
975            error: function () {
976                state.feed = [];
977                state.feedLive = false;
978                renderWall();
979            }
980        });
981    }
982
983    function parseWallXml(xml) {
984        var result = [];
985
986        $(xml).find('item').each(function (index) {
987            var $item = $(this);
988
989            var bookID = $item.find('bookID').first().text() || $item.attr('bookID') || '';
990            var isbn = $item.find('ISBN').first().text() || $item.find('isbn').first().text() || '';
991            var title = $item.find('title').first().text() || '';
992            var author = $item.find('author').first().text() || '';
993            var publisher = $item.find('publisher').first().text() || '';
994            var series = $item.find('SERIES').first().text() || '';
995            var shopUrl = $item.find('shopLink').first().text() || '';
996
997            result.push({
998                bookID: bookID,
999                isbn: isbn,
1000                title: title,
1001                author: author,
1002                publisher: publisher,
1003                series: series,
1004                shopUrl: shopUrl,
1005                color: palette[index % palette.length],
1006                cover: bookID ? coverUrl(bookID) : ''
1007            });
1008        });
1009
1010        return result;
1011    }
1012
1013    function getFilteredWall() {
1014        var list = state.feed || [];
1015        //var query = $(state.wallQuery).toLowerCase();
1016        var query = $(state.wallQuery);
1017
1018        return $.grep(list, function (book) {
1019            var matchesQuery = true;
1020            var matchesPublisher = true;
1021            var matchesAudio = true;
1022
1023            if (query) {
1024                matchesQuery =
1025                    String(book.title).toLowerCase().indexOf(query) >= 0 ||
1026                    String(book.author).toLowerCase().indexOf(query) >= 0 ||
1027                    String(book.isbn).toLowerCase().indexOf(query) >= 0;
1028            }
1029
1030            if (state.wallPub && state.wallPub !== 'All') {
1031                matchesPublisher = book.publisher === state.wallPub;
1032            }
1033
1034            if (state.audioOnly) {
1035                matchesAudio = false;
1036            }
1037
1038            return matchesQuery && matchesPublisher && matchesAudio;
1039        });
1040    }
1041
1042    function renderWall() {
1043        var list = getFilteredWall();
1044        var visible = list.slice(0, state.wallShown);
1045        var html = '';
1046
1047        $.each(visible, function (i, book) {
1048            html += '<div class="b2l-wall-item" data-book-id="' + escapeAttr(book.bookID) + '"><div class="b2l-wall-cover" style="background:' + escapeAttr(book.color) + '"><div role="img" aria-label="Cover artwork" style="background-image:url(\'' + escapeAttr(book.cover) + '\');background-size:cover;background-position:center"></div></div><div class="b2l-wall-info"><h3>' + escapeHtml(book.title) + '</h3><p>' + escapeHtml(book.author) + '</p><p>' + escapeHtml(book.publisher) + '</p></div></div>';
1049        });
1050
1051        $('#wallContainer').html(html);
1052
1053        if (visible.length < list.length) {
1054            $('#wallLoadMore').show();
1055        } else {
1056            $('#wallLoadMore').hide();
1057        }
1058    }
1059
1060    function log(name, data) {
1061        try {
1062            if (window.LogThis && typeof window.LogThis.track === 'function') {
1063                window.LogThis.track(name, data || {});
1064            }
1065        } catch (e) { }
1066    }
1067
1068    function bindEvents() {
1069        window.addEventListener('b2l:translationsloaded', function () {
1070            renderHero();
1071            renderMetrics();
1072            renderFeatures();
1073            renderCommunityStats();
1074            renderCommunities();
1075        });
1076
1077        window.addEventListener('b2l:langchange', function (e) {            
1078            var lang = e && e.detail && e.detail.lang;
1079            if (lang) {
1080                state.lang = lang;
1081                $('#gdprText').text(gdprText(lang));
1082            }
1083            syncMarketWithLang(lang);
1084        });
1085
1086        $(document).on('click', '#heroTabs button', function () {
1087            var index = parseInt($(this).attr('data-hero-index'), 10);
1088
1089            if (isNaN(index)) {
1090                return;
1091            }
1092
1093            state.heroFeat = index;
1094            renderHero();
1095        });
1096
1097        $(document).on('click', '#marketTabs button', function (e) {
1098            e.preventDefault();
1099            currentMarket = $(this).attr('data-market');
1100            renderMarketTabs();
1101            renderTopBiblets();
1102        });
1103
1104        $(document).on('click', '[data-b2l-dark-toggle]', function (e) {
1105            e.preventDefault();
1106            state.bDark = !state.bDark;
1107            syncDarkFlag();
1108            applyAccessibility();
1109        });
1110
1111        $(document).on('click', '[data-a11y-font]', function (e) {
1112            e.preventDefault();
1113
1114            var step = parseInt($(this).attr('data-a11y-font'), 10);
1115
1116            if (!isNaN(step)) {
1117                state.textStep = Math.max(0, Math.min(2, step));
1118                applyAccessibility();
1119            }
1120        });
1121
1122        $(document).on('click', '[data-a11y-contrast]', function (e) {
1123            e.preventDefault();
1124            state.contrast = !state.contrast;
1125            applyAccessibility();
1126        });
1127
1128        $(document).on('click', '[data-a11y-motion]', function (e) {
1129            e.preventDefault();
1130            state.reduceMotion = !state.reduceMotion;
1131            applyAccessibility();
1132        });
1133
1134        $(document).on('click', '[data-a11y-spacing]', function (e) {
1135            e.preventDefault();
1136            state.spacing = !state.spacing;
1137            applyAccessibility();
1138        });
1139
1140        $(document).on('click', '[data-a11y-plain]', function (e) {
1141            e.preventDefault();
1142            state.plain = !state.plain;
1143            applyAccessibility();
1144        });
1145
1146        $(document).on('click', '[data-lang]', function (e) {
1147            e.preventDefault();
1148
1149            var lang = $(this).attr('data-lang');
1150
1151            if (!lang) {
1152                return;
1153            }
1154
1155            state.lang = lang;
1156
1157            try {
1158                localStorage.setItem('b2l_lang', lang);
1159            } catch (err) { }
1160
1161            applyLanguage();
1162        });
1163
1164        $(document).on('click', '[data-consent-accept]', function (e) {
1165            e.preventDefault();
1166            consentChoice('accepted');
1167        });
1168
1169        $(document).on('click', '[data-consent-reject]', function (e) {
1170            e.preventDefault();
1171            consentChoice('rejected');
1172        });
1173
1174        $(document).on('click', '#wallLoadMore', function (e) {
1175            e.preventDefault();
1176            state.wallShown += 24;
1177            renderWall();
1178        });
1179
1180        $(document).on('input', '#wallSearch', function () {
1181            state.wallQuery = $(this).val();
1182            state.wallShown = 24;
1183            renderWall();
1184        });
1185
1186        $(document).on('change', '#wallPublisher', function () {
1187            state.wallPub = $(this).val();
1188            state.wallShown = 24;
1189            renderWall();
1190        });
1191
1192        $(document).on('change', '#wallAudioOnly', function () {
1193            state.audioOnly = $(this).prop('checked');
1194            state.wallShown = 24;
1195            renderWall();
1196        });
1197    }
1198
1199    function init() {        
1200        loadLanguage();
1201        syncDarkFlag();
1202        bindEvents();
1203
1204        try {
1205            if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
1206                state.reduceMotion = true;
1207            }
1208        } catch (e) { }
1209
1210        checkConsent();
1211        applyAccessibility();
1212
1213        currentMarket = marketForLang(getMenuLang());
1214
1215        renderHero();
1216        renderMetrics();
1217        renderFeatures();
1218        renderMarketTabs();
1219        renderTopBiblets();
1220        renderCommunityStats();
1221        renderCommunities();
1222        renderPublisherSteps();
1223
1224        loadOverviewContent();
1225
1226        //loadWall();
1227        //startCounter();
1228
1229        clearInterval(heroTimer);
1230
1231        if (!state.reduceMotion) {
1232            heroTimer = setInterval(function () {
1233                state.heroFeat = (state.heroFeat + 1) % heroFeatures.length;
1234                renderHero();
1235            }, 4500);
1236        }
1237    }
1238
1239    $(function () {        
1240        setTimeout(function () {
1241            init();
1242        }, 500);
1243    });
1244
1245    /*$(window).on("load", function () {
1246        init();
1247    });*/
1248
1249})(jQuery);
1250
1251
1252// New Working Code 01102026
1253var baseUrl = 'https://450million.book2look.com/api/Counter/';
1254var counterIntervalTime = 500;
1255let counterIntervalId, subCounterInterval, initCounterInterval;
1256var viewCountBlocks = 5;
1257var prevCount0, prevCount1, prevCount2, prevCount3, prevCount4;
1258prevCount0 = prevCount1 = prevCount2 = prevCount3 = prevCount4 = 0;
1259let currentValue = 0;//199842540;
1260let viewCounter = 0;
1261let initCount = 0;
1262let initCounterCompleteTime = 3;
1263let secondCounterCompleteTime = 10;
1264
1265window.addEventListener('load', function () {
1266    //console.log("Window loaded");
1267    var count = 0;
1268    getCount('countDisplay');
1269});
1270
1271window.onload = function () {
1272    //console.log("onload");
1273    var count = 0;
1274    getCount('countDisplay');
1275}
1276
1277function getCountOriginal(_id) {
1278    //console.log("getCount:", _id);
1279    switch (_id) {
1280        case 'countDisplay':
1281            fetch(baseUrl + 'GetViewCount').then(response => response.json()).then(data => {
1282                console.log("data:", data);
1283                prevCount0 = prevCount0 == 0 ? parseInt(data) : prevCount0;
1284                currentValue = currentValue == 0 ? parseInt(data) : currentValue;
1285                //updateCounter(_id, prevCount0, data);
1286                updateCounter(_id, data);
1287            })
1288            .catch(error => {
1289                console.error('Error:', error);
1290                updateCounter(_id, 45000000);
1291            });
1292        break;
1293        default: break;
1294    }
1295}
1296
1297// Format a number with a comma after every 3 digits, e.g. 1,000 / 100,000
1298function formatCount(value) {
1299    return Math.floor(Number(value) || 0).toLocaleString('en-US');
1300}
1301
1302// Counter texts per language, read from Content/json/index.{lang}.json (hero.counter_note / counter_live_label / counter_simulated_label)
1303var counterTextDefault = {
1304    note: 'Refreshed every 15 seconds. Biblets are viewed in 193+ countries.',
1305    live: 'LIVE · API',
1306    simulated: 'SIMULATED'
1307};
1308var counterTextByLang = {};
1309
1310// true while the counter API answers; false when it fails or the network is too slow
1311var counterApiLive = false;
1312var counterSlowTimeoutMs = 5000;
1313var counterSlowTimer = null;
1314
1315function getCounterLang() {
1316    try {
1317        if (window.B2L && typeof window.B2L.getLang === 'function') {
1318            return window.B2L.getLang() || 'en';
1319        }
1320    } catch (e) { }
1321    return 'en';
1322}
1323
1324function loadCounterTexts(lang) {
1325    if (counterTextByLang[lang] !== undefined) {
1326        return Promise.resolve(counterTextByLang[lang]);
1327    }
1328
1329    return fetch('Content/json/index.' + lang + '.json')
1330        .then(function (res) {
1331            if (!res.ok) throw new Error('index.' + lang + '.json not found');
1332            return res.json();
1333        })
1334        .then(function (data) {
1335            var hero = (data && data.hero) || {};
1336            counterTextByLang[lang] = {
1337                note: hero.counter_note || counterTextDefault.note,
1338                live: hero.counter_live_label || counterTextDefault.live,
1339                simulated: hero.counter_simulated_label || counterTextDefault.simulated
1340            };
1341            return counterTextByLang[lang];
1342        })
1343        .catch(function () {
1344            return counterTextDefault;
1345        });
1346}
1347
1348// Status label and note shown with the counter
1349function setCounterLabels() {
1350    var liveLabel = document.getElementById('liveLabel');
1351    var countNote = document.getElementById('countNote');
1352    var lang = getCounterLang();
1353
1354    loadCounterTexts(lang).then(function (texts) {
1355        // ignore a late response for a language that is no longer selected
1356        if (lang !== getCounterLang()) return;
1357        if (liveLabel) liveLabel.textContent = counterApiLive ? texts.live : texts.simulated;
1358        if (countNote) countNote.textContent = texts.note;
1359    });
1360}
1361
1362// Switch the label between LIVE · API and SIMULATED
1363function setCounterLive(isLive) {
1364    clearTimeout(counterSlowTimer);
1365    if (counterApiLive !== isLive) {
1366        counterApiLive = isLive;
1367        setCounterLabels();
1368    }
1369}
1370
1371// Show the note in the newly selected language
1372window.addEventListener('b2l:langchange', setCounterLabels);
1373
1374function getCount(_id) {
1375    //console.log("getCount:", _id);
1376    setCounterLabels();
1377
1378    switch (_id) {
1379        case 'countDisplay':
1380            // slow network: show SIMULATED if the API has not answered in time
1381            clearTimeout(counterSlowTimer);
1382            counterSlowTimer = setTimeout(function () {
1383                counterApiLive = false;
1384                setCounterLabels();
1385            }, counterSlowTimeoutMs);
1386
1387            fetch(baseUrl + 'GetViewCount')
1388                .then(response => response.json())
1389                .then(data => {
1390                    console.log("data:", data);
1391                    setCounterLive(!isNaN(parseInt(data)));
1392            prevCount0 = prevCount0 == 0 ? parseInt(data) : prevCount0;
1393            if (currentValue == 0) {
1394                initCount = parseInt(data);
1395                countAnimate(_id, 0, initCount, initCounterCompleteTime * 1000);
1396            }
1397            else {
1398                //updateCounter(_id, parseInt(data));
1399                countAnimate(_id, currentValue, parseInt(data), secondCounterCompleteTime * 1000);
1400            }
1401    })
1402    .catch(error => {
1403        console.error('Error:', error);
1404        setCounterLive(false);
1405        if (currentValue == 0) {
1406            initCount = 449842540;
1407            countAnimate(_id, 0, initCount, initCounterCompleteTime * 1000);
1408        }
1409        else {                        
1410            //updateCounter(_id, 40000000);
1411            countAnimate(_id, currentValue, parseInt(data), secondCounterCompleteTime * 1000);
1412        }
1413    });
1414    break;
1415            default: break;
1416    }
1417}
1418
1419function countAnimate(_id, initVal, lastVal, duration) {
1420    //https://learnersbucket.com/examples/interview/number-increment-counter-in-javascript-react/
1421    let startTime = null;
1422
1423    //get the current timestamp and assign it to the currentTime variable
1424    let currentTime = Date.now();
1425
1426    //pass the current timestamp to the step function
1427    const step = (currentTime) => {
1428        //if the start time is null, assign the current time to startTime
1429        if (!startTime) {
1430            startTime = currentTime;
1431}
1432
1433//calculate the value to be used in calculating the number to be displayed
1434const progress = Math.min((currentTime - startTime) / duration, 1);
1435
1436//calculate what to be displayed using the value gotten above
1437const displayVal = Math.floor(progress * (lastVal - initVal) + initVal);
1438//var url = document.location.href;
1439//if (url.indexOf('Overview') > -1) {
1440    document.getElementById(_id).textContent = formatCount(displayVal);
1441//}
1442//document.getElementById("biblet_views_1").textContent = Math.floor(progress * (lastVal - initVal) + initVal);
1443
1444//checking to make sure the counter does not exceed the last value (lastVal)
1445if (progress < 1) {
1446    window.requestAnimationFrame(step);
1447    } else {
1448        currentValue = lastVal;
1449        getCount('countDisplay');
1450        viewCounter = displayVal;
1451    }
1452};
1453
1454//start animating
1455window.requestAnimationFrame(step);
1456};
1457
1458function updateCounterTest(_id, newCount) {
1459    countAnimate(_id, currentValue, newCount, initCounterCompleteTime * 100);
1460}
1461
1462function updateCounter(_id, newCount) {
1463    clearInterval(counterIntervalId);
1464    const newValue = parseInt(newCount);
1465    const incrementStep = 2;
1466    let tempValue = currentValue;
1467
1468    counterIntervalId = setInterval(() => {
1469        if ((tempValue === newValue && incrementStep === 1)) {
1470            clearInterval(counterIntervalId); // Stop incrementing once the target value is reached
1471            document.getElementById(_id).textContent = formatCount(newValue);
1472            viewCounter = newValue;
1473            getCount('countDisplay');
1474        } else {
1475            tempValue += incrementStep;
1476            //console.log("New value: ",tempValue); // Display the incremented
1477            document.getElementById(_id).textContent = formatCount(tempValue);
1478            viewCounter = tempValue;
1479        }        
1480
1481    }, counterIntervalTime); // Increment every second
1482
1483    currentValue = newValue; // Update the current value
1484}

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.