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, '"'); 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.