1(function() { 2 3 // See readme.md 4 5 var settings = window['geolocation3_settings']; 6 // geolocationEndpoint - endpoint we get geolocation 7 // version - VERSION that is passed in. If this does not match saved value then 8 // it is treated as though the value does not exist. 9 10 var LOCALSTORAGE_PREFIX = 'jzl-geolocation3-'; 11 function saveLocalStorage( obj, expiresInDays ) { 12 13 var json = JSON.stringify(obj); 14 window.localStorage.setItem( LOCALSTORAGE_PREFIX + 'data', json ); 15 window.localStorage.setItem( LOCALSTORAGE_PREFIX + 'version', settings.version ); 16 17 var now = (new Date()).getTime(); 18 var expires = now + 24 * 60 * 60 * 1000 * expiresInDays; 19 window.localStorage.setItem( LOCALSTORAGE_PREFIX + 'expiration', String(expires) ); 20 21 } 22 23 function loadLocalStorage() { 24 25 if( window.localStorage.getItem(LOCALSTORAGE_PREFIX + 'version') !== settings.version ){ 26 return null; 27 } 28 var expires = window.localStorage.getItem( LOCALSTORAGE_PREFIX + 'expiration' ); 29 if( expires == null ) { 30 return null; 31 } 32 expires = parseInt( expires, 10 ); 33 var now = (new Date()).getTime(); 34 if( now > expires ) { 35 return null; 36 } 37 38 var json = window.localStorage.getItem( LOCALSTORAGE_PREFIX + 'data' ); 39 if( json == null ) { 40 return null; 41 } 42 var obj; 43 try { 44 obj = JSON.parse(json); 45 } catch( ex ) { 46 obj = null; 47 } 48 return obj; 49 50 } 51 52 var _currentData = null; // current in-memory copy 53 54 function saveGeolocation( updatedData ) { 55 if( updatedData !== _currentData ) { 56 _currentData = updatedData; 57 var expiresInDays = 7; 58 console.log( 'Saving', updatedData, 'to localstorage for', expiresInDays, 'days' ); 59 saveLocalStorage( updatedData, expiresInDays ); 60 } 61 } 62 function loadGeolocation() { 63 var data = loadLocalStorage(); 64 // copy over just the fields we care about 65 data = data != null ? { 66 latlng: { 67 lat: (data.latlng != null && data.latlng.lat != null ) ? data.latlng.lat : null, 68 lng: (data.latlng != null && data.latlng.lng != null ) ? data.latlng.lng : null, 69 }, 70 countryName: data.countryName != null ? data.countryName : null, 71 countryNameShort: data.countryNameShort != null ? data.countryNameShort : null, 72 postalCode: data.postalCode != null ? data.postalCode : null, 73 state: data.state != null ? data.state : null, 74 stateShort: data.stateShort != null ? data.stateShort : null, 75 city: data.city != null ? data.city : null, 76 nearestRooftop: data.nearestRooftop != null ? data.nearestRooftop : null, 77 nearestRooftopsByDrivetime: data.nearestRooftopsByDrivetime != null ? data.nearestRooftopsByDrivetime : null, 78 overrideType: data.overrideType != null ? data.overrideType : null, 79 timestamp: data.timestamp != null ? data.timestamp : null, 80 } : null; 81 _currentData = data; 82 return data; 83 } 84 85 var _currentGeolocation = undefined; 86 var _currentGeolocationResolve = undefined; 87 var _currentGeolocationResolved = false; 88 var _currentGeolocationWaiting = new Promise(function(resolve) { 89 _currentGeolocationResolve = resolve; 90 }); 91 92 function getCurrentGeolocation() { 93 return _currentGeolocationWaiting 94 .then(function() { 95 return _currentGeolocation; 96 }); 97 } 98 function setCurrentGeolocation(data) { 99 data = data != null ? data : {}; 100 _currentGeolocation = data; 101 if(!_currentGeolocationResolved) { 102 _currentGeolocationResolved = true; 103 _currentGeolocationResolve(); 104 } 105 } 106 107 function cloneJsObject(obj) { 108 return JSON.parse( JSON.stringify(obj) ); 109 } 110 111 function dispatchEvent( obj, initialLoad, changed ) { 112 // This used to be called jzl_set_zip_code 113 console.log( 'Dispatching jzl_set_geolocation event', _currentData, initialLoad ? '[INITIAL]' : '' ); 114 var customEvent = new CustomEvent('jzl_set_geolocation', { 115 detail: { 116 geolocationData: obj != null ? cloneJsObject( obj ) : {}, 117 initialLoad: initialLoad, 118 changed: changed != null ? changed : {}, 119 }, 120 }); 121 window.dispatchEvent(customEvent); 122 } 123 124 function getPostalCodeAndNearestRooftopId(geolocationData) { 125 var postalCode = geolocationData != null ? geolocationData.postalCode : null;
126 var isRooftopInfoAvailable = geolocationData != null && 127 geolocationData.nearestRooftop != null && 128 geolocationData.nearestRooftop.storeInfo != null; 129 var nearestRooftopId = isRooftopInfoAvailable ? 130 geolocationData.nearestRooftop.storeInfo.Id : null; 131 132 return { postalCode: postalCode, nearestRooftopId: nearestRooftopId }; 133 } 134 135 function doGeolocationPostalCode(countryName, postalCode) { 136 137 var promise = new Promise(function(resolve, reject) { 138 if( countryName == null || countryName === '' ) { 139 reject('MISSING_COUNTRY_NAME'); 140 return; 141 } 142 if( postalCode == null || postalCode === '' ) { 143 reject('MISSING_POSTAL_CODE'); 144 return; 145 } 146 147 getCurrentGeolocation() 148 .then(function(currentData) { 149 var postalCodeAndNearestRooftopID = getPostalCodeAndNearestRooftopId(currentData); 150 var params = { 151 currentPostalCode: postalCodeAndNearestRooftopID.postalCode, 152 currentNearestRooftopId: postalCodeAndNearestRooftopID.nearestRooftopId, 153 countryName: countryName, 154 postalCode: postalCode, 155 overrideType: 'country+postal_code', 156 }; 157 158 resolve(params); 159 }); 160 }); 161 162 return promise 163 .then(function(params) { 164 return applyGeolocation(params); 165 }); 166 } 167 168 function doGeolocationBrowserLatLng() { 169 170 var promise = new Promise(function(resolve, reject) { 171 if (!("geolocation" in window.navigator)) { 172 reject('NOT_AVAILABLE'); 173 return; 174 } 175 getCurrentGeolocation() 176 .then(function(currentData) { 177 var timeoutId = setTimeout(function() { 178 reject('TIMEOUT'); 179 }, 10000); 180 181 window.navigator 182 .geolocation 183 .getCurrentPosition(function(position) { 184 clearTimeout(timeoutId); 185 186 var postalCodeAndNearestRooftopID = getPostalCodeAndNearestRooftopId(currentData); 187 var params = { 188 currentPostalCode: postalCodeAndNearestRooftopID.postalCode, 189 currentNearestRooftopId: postalCodeAndNearestRooftopID.nearestRooftopId, 190 latlng: { 191 lat: position.coords.latitude, 192 lng: position.coords.longitude, 193 }, 194 overrideType: 'latlng', 195 }; 196 197 resolve(params); 198 }, function(error) { 199 console.log('Error from geolocation', error); 200 reject('GEOLOCATION_ERROR'); 201 }); 202 }); 203 }); 204 205 return promise 206 .then(function(params) { 207 return applyGeolocation(params); 208 }); 209 210 } 211 212 function applyGeolocation( params, initialLoad ) { 213 return doGeolocation( params ) 214 .then(function( result ) { 215 var updatedData = result.geolocationData; 216 var changed = result.changed; 217 saveGeolocation( updatedData ); 218 setCurrentGeolocation( updatedData ); 219 dispatchEvent( updatedData, initialLoad, changed ); 220 }); 221 } 222 223 function checkRefresh( currentData ) { 224 225 var now = (new Date()).getTime(); 226 227 // Localstorage and its overrides are valid for 7 days, but 228 // we will refresh the data from the server if it is more than 6 hours old. 229 var age = now - currentData.timestamp; 230 var maxHours = 6; 231 if( age > maxHours * 60 * 60 * 1000 ) { 232 console.log( 'timestamp older than ' + maxHours + ' hours, refreshing all.' ); 233 return true; 234 } 235 236 console.log( 'current data not expired, using as is.' ); 237 return false;
238 239 } 240 241 jazelReady(function() { 242 243 var params = null; 244 245 // When the page loads, first try the cookie to see if we have 246 // anything. 247 var currentData = loadGeolocation(); 248 249 // If we are going to be forced to apply an override zip code 250 if( settings.overridePostalCode != null ) { 251 if( currentData == null || currentData.overrideType == null ) { 252 params = { 253 countryName: 'United States', 254 postalCode: settings.overridePostalCode, 255 overrideType: 'country+postal_code', 256 }; 257 if( currentData != null ) { 258 var postalCodeAndNearestRooftopID = getPostalCodeAndNearestRooftopId(currentData); 259 params.currentPostalCode = postalCodeAndNearestRooftopID.postalCode; 260 params.currentNearestRooftopId = postalCodeAndNearestRooftopID.nearestRooftopId; 261 } 262 } 263 } 264 265 if( params == null ) { 266 if( currentData != null ) { 267 console.log("previously saved GeoLocation", currentData); 268 var postalCodeAndNearestRooftopID = getPostalCodeAndNearestRooftopId(currentData); 269 270 params = { 271 currentPostalCode: postalCodeAndNearestRooftopID.postalCode, 272 currentNearestRooftopId: postalCodeAndNearestRooftopID.nearestRooftopId, 273 currentData: currentData, 274 needRefresh: checkRefresh( currentData ), 275 }; 276 277 } else { 278 console.log("no previously saved GeoLocation"); 279 // and we try to detect it based on CF/IP-proxy 280 } 281 } 282 283 applyGeolocation( params, true ) 284 .catch(function() { 285 // If this fails, then notify everyone of it anyway 286 setCurrentGeolocation( currentData ); 287 dispatchEvent( currentData, true, null ); 288 }); 289 290 }); 291 292 function doGeolocation( params ) { 293 if (params == null) { 294 params = {}; 295 } 296 return new Promise(function(resolve, reject) { 297 var currentData = params.currentData; 298 if( currentData != null ) { 299 if( !params.needRefresh ) { 300 resolve({geolocationData: currentData, changed: null}); 301 return; 302 } 303 params.overrideType = currentData.overrideType; 304 if( currentData.overrideType === 'country+postal_code' ) { 305 params.postalCode = currentData.postalCode; 306 params.countryName = currentData.countryName; 307 } else if( currentData.overrideType === 'latlng' ) { 308 params.latlng = { 309 lat: currentData.latlng.lat, 310 lng: currentData.latlng.lng, 311 }; 312 } 313 } 314 315 setTimeout(function() { 316 reject('TIMEOUT'); 317 }, 15000); 318 var endpoint = settings.geolocationEndpoint; 319 jQuery 320 .post(endpoint, { 321 params: JSON.stringify( params ), 322 }) 323 .done(function(result) { 324 if( result.error ) { 325 reject( result.message ); 326 return; 327 } 328 var geolocationData = result.geolocationData; 329 geolocationData.timestamp = (new Date()).getTime(); 330 var changed = result.changed; 331 resolve({geolocationData: geolocationData, changed: changed}); 332 }) 333 .fail(function( jqxhr, textStatus, error ) { 334 reject('AJAX_ERROR'); 335 }); 336 }); 337 } 338 339 var errorMessages = { 340 'TIMEOUT': 'Connection timed out. Try again later.', 341 'AJAX_ERROR': 'Something went wrong with the Ajax connection. Try again later.', 342 'NOT_AVAILABLE': 'Geolocation not available in this browser.', 343 'GEOLOCATION_ERROR': 'Browser did not provide Geolocation data.', 344 'MISSING_COUNTRY_NAME': 'Country name must be provided.', 345 'MISSING_POSTAL_CODE': 'Zip Code must be provided.', 346 'UNKNOWN_COUNTRY_NAME': 'Provided Country name is unknown.', 347 'UNKNOWN_POSTAL_CODE': 'Provided Zip Code is unknown.', 348 'MISSING_LATLNG': 'Latitude/Longitude must be provided.', 349 'MISSING_LATITUDE': 'Latitude must be provided.', 350 'MISSING_LONGITUDE': 'Longitude must be provided.', 351 'REV_GEOCODE_FAILED': 'Your location could not be resolved to a supported address.', 352 'DEFAULT_GEOCODE_FAILED': 'Your location could not be detected.', 353 }; 354 355 function getErrorMessageText( key ) { 356 var msg = errorMessages[key]; 357 if( msg == null ) { 358 msg = 'Unknown error.'; 359 } 360 return msg; 361 } 362 363 window.geolocation3 = { 364 getCurrentGeolocation: getCurrentGeolocation, 365 doGeolocationPostalCode: doGeolocationPostalCode, 366 doGeolocationBrowserLatLng: doGeolocationBrowserLatLng, 367 getErrorMessageText: getErrorMessageText, 368 }; 369 370})();
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.