PageSourceSearch

https://bassonline.com/wp-content/themes/blocksy-child/js/bol-geo.js?ver=1760100655

js bassonline.com collected 2026-09-24 08:48:38 UTC 5,784 bytes, 166 lines download raw bytes

1(function(){
2  const LS_KEY = 'bol_geo_v2'; // {lat,lng,source,ts}
3  const MAX_AGE_MS = 7*24*60*60*1000;
4
5  function getCached() {
6    try {
7      const v = JSON.parse(localStorage.getItem(LS_KEY) || 'null');
8      if (!v) return null;
9      if (Date.now() - (v.ts||0) > MAX_AGE_MS) return null;
10      return v;
11    } catch(e){ return null; }
12  }
13
14  function cacheLoc(lat, lng, source) {
15    localStorage.setItem(LS_KEY, JSON.stringify({lat:+lat, lng:+lng, source, ts: Date.now()}));
16    document.dispatchEvent(new CustomEvent('bol:geo:ready', {detail:{lat:+lat, lng:+lng, source}}));
17  }
18
19  function tryBrowser() {
20    return new Promise((resolve, reject)=>{
21      if (!navigator.geolocation) return reject('no-geo');
22      // short timeout, no high accuracy (less friction, faster)
23      const opts = { enableHighAccuracy: false, timeout: 1500, maximumAge: 24*60*60*1000 };
24      navigator.geolocation.getCurrentPosition(
25        pos => resolve({lat: pos.coords.latitude, lng: pos.coords.longitude, source: 'browser'}),
26        _err => reject('denied-or-timeout'),
27        opts
28      );
29    });
30  }
31
32  function tryServerApprox() {
33    return fetch('/wp-json/bol/v1/approx', {credentials:'same-origin'})
34      .then(r=>r.json())
35      .then(j=>({lat:j.lat, lng:j.lng, source:j.source||'approx'}));
36  }
37
38  async function init() {
39    const cached = getCached();
40    if (cached) {
41      document.dispatchEvent(new CustomEvent('bol:geo:ready', {detail:cached}));
42      // refresh in background if stale-ish (> 1 day)
43      if (Date.now() - cached.ts > 24*60*60*1000) {
44        Promise.any([tryBrowser(), tryServerApprox()]).then(v=>cacheLoc(v.lat,v.lng,v.source)).catch(()=>{});
45      }
46      return;
47    }
48    // race both; whichever is first wins
49    try {
50      const winner = await Promise.any([tryBrowser(), tryServerApprox()]);
51      cacheLoc(winner.lat, winner.lng, winner.source);
52    } catch(e){
53      // still nothing? set US centroid to keep UI predictable
54      cacheLoc(39.8283, -98.5795, 'fallback');
55    }
56  }
57
58  init();
59
60  // Expose getter for other scripts
61  window.BOL_GEO = {
62    get: ()=> getCached(),
63    refresh: ()=> localStorage.removeItem(LS_KEY)
64  };
65})();
66
67(function(){
68  function injectPills(root) {
69    var scope = root || document;
70    var wraps = scope.querySelectorAll('.bol-card-wrap[data-bol-distance]:not([data-bol-pill])');
71    wraps.forEach(function (wrap) {
72      var dist = wrap.getAttribute('data-bol-distance');
73      if (!dist) return;
74
75      // Find a likely image wrapper on these cards
76      var img = wrap.querySelector('.bol-card-image, .bol-card__image, .bol-img, .card-image');
77      if (!img) return;
78
79      wrap.setAttribute('data-bol-pill','1');
80
81      // Ensure positioned container for absolute pill
82      var pos = window.getComputedStyle(img).position;
83      if (!pos || pos === 'static') img.style.position = 'relative';
84
85      var pill = document.createElement('span');
86      pill.className = 'bol-pill bol-pill--distance';
87      pill.setAttribute('aria-label', 'Distance');
88      pill.textContent = dist;
89      img.appendChild(pill);
90    });
91  }
92
93  // Run now (even if DOMContentLoaded already fired)
94  if (document.readyState === 'loading') {
95    document.addEventListener('DOMContentLoaded', function(){ injectPills(document); });
96  } else {
97    injectPills(document);
98  }
99
100  // Also listen for a custom event we’ll fire after nearby sections render
101  document.addEventListener('bol:nearby:render', function(e){
102    injectPills((e.detail && e.detail.root) || document);
103  });
104})();
105
106(function(){
107  // Haversine in km
108  function hav(lat1, lon1, lat2, lon2){
109    var R=6371, dLat=(lat2-lat1)*Math.PI/180, dLon=(lon2-lon1)*Math.PI/180;
110    var a=Math.sin(dLat/2)**2 + Math.cos(lat1*Math.PI/180)*Math.cos(lat2*Math.PI/180)*Math.sin(dLon/2)**2;
111    return 2*R*Math.asin(Math.min(1, Math.sqrt(a)));
112  }
113  function toMiles(km){ return Math.round(km*0.621371*10)/10; }
114
115  function updateGrid(root, user){
116    if (!root || !user || typeof user.lat!=='number' || typeof user.lng!=='number') return;
117    var cards = Array.from(root.querySelectorAll('.bol-card-wrap[data-lat][data-lng]'));
118    if (!cards.length) return;
119
120    cards.forEach(function(card){
121      var lat = parseFloat(card.getAttribute('data-lat'));
122      var lng = parseFloat(card.getAttribute('data-lng'));
123      if (!isFinite(lat) || !isFinite(lng)) return;
124
125      var dkm = hav(user.lat, user.lng, lat, lng);
126      card.dataset.dkmbol = dkm.toFixed(1);
127
128      // Update the pill on the image wrapper
129      var img = card.querySelector('.bol-card-image, .bol-card__image, .card-image, .bol-img');
130      if (img){
131        if (getComputedStyle(img).position === 'static') img.style.position = 'relative';
132        img.setAttribute('data-bol-distance', toMiles(dkm)+'mi');
133      }
134    });
135
136    // sort by distance and re-append
137    cards.sort(function(a,b){
138      var da = parseFloat(a.dataset.dkmbol||'1e9');
139      var db = parseFloat(b.dataset.dkmbol||'1e9');
140      return da - db;
141    }
141).forEach(function(c){ root.appendChild(c); });
142  }
143
144  function fetchApprox(){
145    return fetch('/wp-json/bol/v1/approx', {credentials:'same-origin'})
146      .then(function(r){ return r.json(); })
147      .then(function(j){
148        if (j && typeof j.lat==='number' && typeof j.lng==='number') return j;
149        return null;
150      })
151      .catch(function(){ return null; });
152  }
153
154  function init(){
155    var grids = Array.from(document.querySelectorAll('.bol-grid[data-geo-sort="distance"]'));
156    if (!grids.length) return;
157    fetchApprox().then(function(user){
158      if (!user) return;
159      grids.forEach(function(g){ updateGrid(g, user); });
160    });
161  }
162
163  if (document.readyState === 'loading') {
164    document.addEventListener('DOMContentLoaded', init);
165  } else { init(); }
166})();

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.