1 let weatherDataCache = null; 2 let weatherTab = 'daily'; 3 let finalizePhotos = []; 4 let finalizeVisitId = null; 5 let currentProfile = null; 6 7 function isTrialActive() { 8 if (!currentProfile) return true; 9 return currentProfile.plan !== 'pro'; 10 } 11 12 async function debugProfile() { 13 const uid = currentUser?.id || 'N/A'; 14 const cached = localStorage.getItem('cropflow_profile_' + uid); 15 let cachedPlan = 'â'; 16 try { cachedPlan = JSON.parse(cached||'{}')?.plan || 'â'; } catch(_) {} 17 18 let dbPlan = 'â', dbProEnd = 'â', dbError = ''; 19 try { 20 const { data, error } = await supabaseClient 21 .from('profiles').select('plan, pro_end, is_admin').eq('user_id', uid).maybeSingle(); 22 if (data) { dbPlan = data.plan; dbProEnd = data.pro_end || 'sem data'; } 23 else if (error) { dbError = error.code + ': ' + error.message; } 24 else { dbError = 'perfil não encontrado no banco'; } 25 } catch(e) { dbError = e.message; } 26 27 const msg = [ 28 'DIAGNÃSTICO DO PERFIL', 29 'ââââââââââââââââââââââââ', 30 'ID: ' + uid.substring(0,8) + '...', 31 'Email: ' + (currentUser?.email || 'â'), 32 '', 33 'BANCO DE DADOS:', 34 ' Plano: ' + dbPlan, 35 ' PRO até: ' + dbProEnd, 36 (dbError ? 'Erro: ' + dbError : ''), 37 '', 38 'CACHE LOCAL:', 39 ' Plano em cache: ' + cachedPlan, 40 '', 41 'APP AGORA:', 42 ' currentProfile.plan: ' + (currentProfile?.plan || 'null'), 43 ' isPro(): ' + isPro(), 44 ' isProExpired(): ' + isProExpired(), 45 ].filter(x => x !== undefined).join('\n'); 46 47 alert(msg); 48 } 49 50 const APP_BUILD_TS = window.CF_BUILD || ''; 51 const VAPID_PUBLIC_KEY = 'BCfHLUk_UNlSjCoIWIAHIEzebk4IznQxHfAolt1JuISe6iBRIYzbmlkH4QnQMvYHIW1q0PHGzZduxQXga6cXwUc'; 52 53 // ââ SVG Icon Helper ââââââââââââââââââââââââââââââââââââââââââ 54 const _ICONS = { 55 check: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 13l4 4L19 7"/></svg>`, 56 checkCircle: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>`, 57 clock: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>`, 58 calendar: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg>`, 59 alertTriangle: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>`, 60 bell: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 8A6 6 0 006 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 01-3.46 0"/></svg>`, 61 edit: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>`, 62 trash: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6"/><path d="M10 11v6M14 11v6"/><path d="M9 6V4a1 1 0 011-1h4a1 1 0 011 1v2"/></svg>`, 63 send: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
63<line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>`, 64 fileText: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/></svg>`, 65 lock: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0110 0v4"/></svg>`, 66 image: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>`, 67 target: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/></svg>`, 68 tool: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 000 1.4l1.6 1.6a1 1 0 001.4 0l3.77-3.77a6 6 0 01-7.94 7.94l-6.91 6.91a2.12 2.12 0 01-3-3l6.91-6.91a6 6 0 017.94-7.94l-3.76 3.76z"/></svg>`, 69 leaf: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 8C8 10 5.9 16.17 3.82 19.28A1 1 0 005 21 14.46 14.46 0 0022 7c0-1.66-.2-3.4-.59-5C21 2 18 1 17 1l-1 2.06A5 5 0 0117 8z"/><path d="M6.2 11.4A12 12 0 003 19"/></svg>`, 70 notepad: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><path d="M14 2v6h6M16 13H8M16 17H8M10 9H8"/></svg>`, 71 arrowRight: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg>`, 72 share: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/></svg>`, 73 star: `<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>`, 74 starEmpty: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>`, 75 mapPin: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0118 0z"/><circle cx="12" cy="10" r="3"/></svg>`, 76 user: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>`, 77 info: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>`, 78 trendingUp: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 6 13.5 15.5 8.5 10.5 1 18"/><polyline points="17 6 23 6 23 12"/></svg>`, 79 80 // ââ Ampliação para substituir os emojis da interface ââââââââââââââ 81 // Emoji renderiza diferente em cada sistema, não aceita a cor da marca e 82 // é o marcador visual nº1 de produto improvisado. Mesma grade 24x24 e 83 // mesma espessura dos que já existiam, para o conjunto ler como um só. 84 x: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18M6 6l12 12"/></svg>`, 85 plus: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M5 12h14"/></svg>`, 86 search: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg>`, 87 refresh: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-2.6-6.4"/><polyline points="21 3 21 9 15 9"/></svg>`, 88 settings: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.6 1.6 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.6 1.6 0 0 0-2.7 1.1V21a2 2 0 1 1-4 0v-.1A1.6 1.6 0 0 0 7.9 19.4a1.6 1.6 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.6 1.6 0 0 0-1.1-2.7H2a2 2 0 1 1 0-4h.1a1.6 1.6 0 0 0 1.1-2.7l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.6 1.6 0 0 0 1.8.3 1.6 1.6 0 0 0 1-1.5V2a2 2 0 1 1 4 0v.1a1.6 1.6 0 0 0 2.7 1.1l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.6 1.6 0 0 0-.3 1.8 1.6 1.6 0 0 0 1.5 1H22a2 2 0 1 1 0 4h-.1a1.6 1.6 0 0 0-1.5 1Z"/></svg>`, 89 shield: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10Z"/></svg>`, 90 clipboard: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 3h6v3H9z"/><path d="M15 4.5h2.5A1.5 1.5 0 0 1 19 6v13.5a1.5 1.5 0 0 1-1.5 1.5h-11A1.5 1.5 0 0 1 5 19.5V6a1.5 1.5 0 0 1 1.5-1.5H9"/><path d="M8.5 11h7M8.5 15h5"/></svg>`, 91 chart: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 21h18"/><rect x="5" y="11" width="3.4" height="7" rx="1"/><rect x="10.3" y="6" width="3.4" height="12" rx="1"/><rect x="15.6" y="14" width="3.4" height="4" rx="1"/></svg>`, 92 sprout: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 21V9"/><path d="M12 13c-3.3 0-5-1.9-5-5 3.3 0 5 1.9 5 5Z"/><path d="M12 13c3.3 0 5-1.9 5-5-3.3 0-5 1.9-5 5Z"/></svg>`, 93 bot: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 4c0 7.5-4.2 12-9.5 12H7c0-6.6 4.7-11.2 13-12Z"/><path d="M4.5 20c.7-3.6 2-6.3 4-8.2"/></svg>`, 94 link: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13.5a4 4 0 0 0 5.7.4l3-3a4 4 0 0 0-5.7-5.7l-1.7 1.7"/><path d="M14 10.5a4 4 0 0 0-5.7-.4l-3 3a4 4 0 0 0 5.7 5.7l1.7-1.7"/></svg>`, 95 camera: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 8h3l1.5-2h7L17 8h3a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1Z"/><circle cx="12" cy="13.5" r="3.2"/>
95</svg>`, 96 home: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m3 10.5 9-7 9 7"/><path d="M5.5 9.5V20h13V9.5"/></svg>`, 97 money: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2.5" y="6" width="19" height="12" rx="2"/><circle cx="12" cy="12" r="2.6"/><path d="M6 12h.01M18 12h.01"/></svg>`, 98 download: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v12"/><polyline points="8 11 12 15 16 11"/><path d="M4 19h16"/></svg>`, 99 phone: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="2.5" width="12" height="19" rx="2.5"/><path d="M11 18.5h2"/></svg>`, 100 wifiOff: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 3l20 18"/><path d="M5 12.5a11 11 0 0 1 3.5-2.3"/><path d="M8.5 16a6 6 0 0 1 2.2-1.4"/><path d="M12 20h.01"/><path d="M19.5 12.5a11 11 0 0 0-6-3.2"/></svg>`, 101 moon: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 14.5A8.5 8.5 0 0 1 9.5 4a8.5 8.5 0 1 0 10.5 10.5Z"/></svg>`, 102 cloud: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M7 18a4 4 0 0 1 .6-8A5.5 5.5 0 0 1 18 10.5a3.75 3.75 0 0 1-.4 7.5Z"/></svg>`, 103 book: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4.5A1.5 1.5 0 0 1 5.5 3H19v16H5.5A1.5 1.5 0 0 0 4 20.5Z"/><path d="M4 20.5A1.5 1.5 0 0 1 5.5 19H19v2H5.5A1.5 1.5 0 0 1 4 20.5Z"/></svg>`, 104 }; 105 function _icon(name, size) { 106 const s = size || 16; 107 const svg = _ICONS[name] || _ICONS.info; 108 return svg.replace('<svg ', `<svg width="${s}" height="${s}" style="display:inline-block;vertical-align:middle;flex-shrink:0;" `); 109 } 110 // ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 111 112 const PROFILE_CACHE_VERSION = window.CF_BUILD || ''; 113 function loadCachedProfile() { 114 if (!currentUser) return; 115 116 const savedVersion = localStorage.getItem('cropflow_cache_version'); 117 if (savedVersion !== PROFILE_CACHE_VERSION) { 118 119 Object.keys(localStorage) 120 .filter(k => k.startsWith('cropflow_profile_')) 121 .forEach(k => localStorage.removeItem(k)); 122 sessionStorage.removeItem('cropflow_promo_shown'); 123 localStorage.removeItem('cropflow_last_promo'); 124 localStorage.removeItem('cropflow_banner_dismissed'); 125 localStorage.setItem('cropflow_cache_version', PROFILE_CACHE_VERSION); 126 127 return; 128 } 129 130 const cached = localStorage.getItem('cropflow_profile_' + currentUser.id); 131 if (cached) { 132 try { currentProfile = JSON.parse(cached); } catch(_) {} 133 } 134 } 135 136 function showUpgradeModal(message) { 137 if (message) { 138 const sub = document.getElementById('trialBannerSub'); 139 if (sub) sub.textContent = message; 140 } 141 openModal('modalUpgrade'); 142 } 143 144 // ââ Planos âââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 145 // O checkout anual precisa de um produto próprio na Kirvano. Enquanto o 146 // link não existir, a aba anual leva ao mensal â melhor do que abrir um 147 // link quebrado na hora de cobrar. 148 const CF_PLANOS = { 149 mensal: { 150 checkout: 'https://pay.kirvano.com/db528772-cd14-4119-bf5b-bcd6b820d244', 151 rotulo: 'Assinar PRO â R$ 29,99/mês' 152 }, 153 anual: { 154 checkout: 'https://pay.kirvano.com/4ac3f4c2-cf52-4dc9-b908-74b1ac5fac7e',
155 rotulo: 'Assinar Plano Safra â R$ 249,00/ano' 156 } 157 }; 158 let _cfPlanoSelecionado = 'mensal'; 159 160 function cfSelecionarPlano(plano) { 161 if (!CF_PLANOS[plano]) return; 162 _cfPlanoSelecionado = plano; 163 164 const mensal = plano === 'mensal'; 165 const elM = document.getElementById('cfPriceMensal'); 166 const elA = document.getElementById('cfPriceAnual'); 167 if (elM) elM.style.display = mensal ? 'block' : 'none'; 168 if (elA) elA.style.display = mensal ? 'none' : 'block'; 169 170 document.getElementById('cfTabMensal')?.classList.toggle('is-active', mensal); 171 document.getElementById('cfTabAnual')?.classList.toggle('is-active', !mensal); 172 173 const btn = document.getElementById('cfBtnAssinar'); 174 if (btn) btn.textContent = CF_PLANOS[plano].rotulo; 175 } 176 177 function assinarProKirvano() { 178 const plano = CF_PLANOS[_cfPlanoSelecionado] || CF_PLANOS.mensal; 179 const checkoutUrl = plano.checkout || CF_PLANOS.mensal.checkout; 180 181 if (_cfPlanoSelecionado === 'anual' && !plano.checkout) { 182 console.warn('[Planos] Checkout anual não configurado â caindo no mensal.'); 183 } 184 185 const email = currentUser?.email || ''; 186 // O e-mail precisa bater com o do webhook: é por ele que o pagamento 187 // encontra a conta e libera o PRO. 188 const url = email ? `${checkoutUrl}?email=${encodeURIComponent(email)}` : checkoutUrl; 189 if (typeof cfTrack === 'function') cfTrack('checkout_aberto', { plano: _cfPlanoSelecionado }); 190 window.open(url, '_blank'); 191 } 192 193 function showAlert(msg) { 194 return new Promise(resolve => { 195 const modal = document.getElementById('modalAlert'); 196 document.getElementById('modalAlertText').textContent = msg; 197 modal.style.display = 'flex'; 198 const btn = document.getElementById('modalAlert').querySelector('button'); 199 const handler = () => { modal.style.display = 'none'; btn.removeEventListener('click', handler); resolve(); }; 200 btn.addEventListener('click', handler); 201 }); 202 } 203 204 function showConfirm(msg, confirmLabel = 'Confirmar', danger = true) { 205 return new Promise(resolve => { 206 const modal = document.getElementById('modalConfirm'); 207 document.getElementById('modalConfirmText').textContent = msg; 208 const okBtn = document.getElementById('modalConfirmOk'); 209 okBtn.textContent = confirmLabel; 210 okBtn.className = `flex-1 py-3 ${danger ? 'bg-red-600 hover:bg-red-500' : 'bg-green-600 hover:bg-green-500'} text-white rounded-xl font-semibold text-sm transition`; 211 modal.style.display = 'flex'; 212 const yes = () => { cleanup(); resolve(true); }; 213 const no = () => { cleanup(); resolve(false); }; 214 const cleanup = () => { 215 modal.style.display = 'none'; 216 document.getElementById('modalConfirmOk').removeEventListener('click', yes); 217 document.getElementById('modalConfirmCancel').removeEventListener('click', no); 218 }; 219 document.getElementById('modalConfirmOk').addEventListener('click', yes); 220 document.getElementById('modalConfirmCancel').addEventListener('click', no); 221 }); 222 } 223 224 function sanitize(str) { 225 if (str === null || str === undefined) return ''; 226 return String(str) 227 .replace(/&/g, '&') 228 .replace(/</g, '<') 229 .replace(/>/g, '>') 230 .replace(/"/g, '"') 231 .replace(/'/g, '''); 232 } 233 234 // As chaves de onboarding eram globais no navegador. Uma segunda conta na 235 // mesma maquina nascia com o guia ja marcado como concluido e nunca via 236 // o passo a passo. Agora cada conta tem o seu. 237 function _ob(chave) { 238 const id = (typeof currentUser !== 'undefined' && currentUser && currentUser.id) ? currentUser.id : 'anon'; 239 return chave + '__' + id; 240 } 241 242 function escAttrJs(str) { 243 if (str === null || str === undefined) return ''; 244 return String(str) 245 .replace(/\\/g, '\\\\') 246 .replace(/'/g, "\\'") 247 .replace(/\r?\n/g, '\\n') 248 .replace(/&/g, '&') 249 .replace(/"/g, '"') 250 .replace(/</g, '<') 251 .replace(/>/g, '>'); 252 } 253 254 function sanitizeAI(str) { 255 if (!str) return ''; 256 return String(str) 257 .replace(/&/g, '&') 258 .replace(/</g, '<') 259 .replace(/>/g, '>') 260 .replace(/"/g, '"') 261 .replace(/\n/g, '<br>') 262 .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>') 263 .replace(/\*(.+?)\*/g, '<em>$1</em>'); 264 }
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.