1/* Plan offline web â helpers puros (paridad con apps/mobile offlineStore). */ 2window.EmapOfflinePlan = (function () { 3 'use strict'; 4 5 const PLAN_MAX_AGE_MS = 36 * 3600 * 1000; 6 7 function samePlace(a, b, meters) { 8 if (meters == null) meters = 80; 9 if (!a || !b) return false; 10 const dlat = (Number(a.lat) - Number(b.lat)) * 111320; 11 const mlon = 111320 * Math.cos(((Number(a.lat) + Number(b.lat)) / 2) * (Math.PI / 180)); 12 const dlon = (Number(a.lon) - Number(b.lon)) * mlon; 13 if (![dlat, dlon].every(Number.isFinite)) return false; 14 return Math.hypot(dlat, dlon) <= meters; 15 } 16 17 function usableRoutes(routes) { 18 return Object.values(routes || {}).some( 19 (r) => r && !r.unavailable && r.geometry && r.geometry.coordinates 20 && r.geometry.coordinates.length > 1, 21 ); 22 } 23 24 function bannerKey(opts) { 25 opts = opts || {}; 26 if (opts.navigating) return 'offlineBannerNav'; 27 if (opts.routeFromCache || opts.hasRoute) return 'offlineBannerRoute'; 28 return 'offlineBanner'; 29 } 30 31 function slimPlace(p) { 32 if (!p) return null; 33 return { 34 lat: p.lat, 35 lon: p.lon, 36 label: p.label || p.name || '', 37 name: p.name || p.label || '', 38 }; 39 } 40 41 function slimRoute(r) { 42 if (!r || r.unavailable) return r; 43 return { 44 duration: r.duration, 45 distance: r.distance, 46 geometry: r.geometry, 47 legs: r.legs, 48 start_time: r.start_time, 49 end_time: r.end_time, 50 fare: r.fare, 51 recommendation: r.recommendation, 52 alternatives: Array.isArray(r.alternatives) 53 ? r.alternatives.map((a) => ({ 54 duration: a.duration, 55 distance: a.distance, 56 geometry: a.geometry, 57 legs: a.legs, 58 })) 59 : undefined, 60 selected: r.selected, 61 }; 62 } 63 64 function planMatches(plan, origin, dest) { 65 if (!plan || !plan.origin || !plan.destination || !origin || !dest) return false; 66 return samePlace(plan.origin, origin) && samePlace(plan.destination, dest); 67 } 68 69 function slimPlan(plan) { 70 if (!plan || !plan.origin || !plan.destination || !plan.routes) return null; 71 if (!usableRoutes(plan.routes)) return null; 72 return { 73 ts: Date.now(), 74 origin: slimPlace(plan.origin), 75 destination: slimPlace(plan.destination), 76 via: plan.via ? slimPlace(plan.via) : null, 77 mode: plan.mode, 78 when: plan.when || null, 79 arriveBy: !!plan.arriveBy, 80 routes: Object.fromEntries( 81 Object.entries(plan.routes).map(([k, v]) => [k, slimRoute(v)]), 82 ), 83 }; 84 } 85 86 function isPlanFresh(plan, now) { 87 if (now == null) now = Date.now(); 88 return !!(plan && plan.ts && now - plan.ts <= PLAN_MAX_AGE_MS); 89 } 90 91 return { 92 samePlace, usableRoutes, bannerKey, planMatches, slimPlan, isPlanFresh, 93 PLAN_KEY: 'emap.lastPlan', 94 }; 95})();
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.