1/** 2 * Histórico local das últimas páginas visitadas (atalhos no menu lateral do dashboard). 3 * Persistência: localStorage. Carregado de forma assÃncrona a partir de csrf.js. 4 */ 5(function () { 6 'use strict'; 7 8 var STORAGE_KEY = 'catellix_nav_recent_v1'; 9 var MAX = 5; 10 11 var SKIP_PREFIXES = ['/static/', '/api/', '/docs', '/openapi']; 12 var SKIP_PATHS = { 13 '/login': 1, 14 '/register': 1, 15 '/logout': 1, 16 '/reset_password': 1, 17 '/set_new_password': 1, 18 '/confirm_email': 1, 19 '/comece-gratis': 1 20 }; 21 22 /** Rotas conhecidas: chave = pathname + query normalizado */ 23 var ROUTES = { 24 '/dashboard': { label: 'Painel', icon: 'ð', module: 'dashboard' }, 25 '/dashboard?tab=alerts': { label: 'Alertas', icon: 'ð', module: 'dashboard_alerts' }, 26 '/dashboard?tab=impact': { label: 'Impacto', icon: 'ð°', module: 'dashboard_impact' }, 27 '/dashban': { label: 'DashBan', icon: 'ð', module: 'dashban' }, 28 '/custom-dashboards': { label: 'Meus painéis', icon: 'ï¼', module: 'custom_dashboards' }, 29 '/network-maps': { label: 'Mapas', icon: 'ðº', module: 'network_maps' }, 30 '/perfil': { label: 'Perfil', icon: 'ð¤', module: 'perfil' }, 31 '/settings': { label: 'Definições', icon: 'â', module: 'settings' }, 32 '/cad_hosts': { label: 'Hosts', icon: 'â', module: 'cad_hosts' }, 33 '/ipam': { label: 'IPAM', icon: 'ð¡', module: 'ipam' }, 34 '/pops': { label: 'POPs', icon: 'ð¢', module: 'pops' }, 35 '/backup': { label: 'Backup', icon: 'â«', module: 'backup' }, 36 '/tasks': { label: 'Tarefas', icon: 'â', module: 'tasks' }, 37 '/pop-monitoring': { label: 'Câmaras', icon: 'ð¹', module: 'pop_monitoring' }, 38 '/servidores': { label: 'Servidores', icon: 'ð¥', module: 'servidores' }, 39 '/pc-notebook': { label: 'Endpoints', icon: 'ð»', module: 'desktop_lite' }, 40 '/looking-glass': { label: 'LG', icon: 'ð', module: 'looking_glass' }, 41 '/bgp': { label: 'BGP', icon: 'â¡', module: 'bgp' }, 42 '/ospf': { label: 'OSPF', icon: 'â', module: 'ospf' }, 43 '/mpls': { label: 'MPLS', icon: '⬡', module: 'mpls' }, 44 '/layer2': { label: 'Layer 2', icon: 'â¦', module: 'layer2' }, 45 '/tools': { label: 'Ferramentas', icon: 'ð§', module: 'tools' }, 46 '/analise-rede': { label: 'Latência', icon: 'ð', module: 'analise_rede' }, 47 '/reports': { label: 'Relatórios', icon: 'ð', module: 'reports' }, 48 '/flow': { label: 'Flow', icon: 'ã°', module: 'flow' }, 49 '/gmud': { label: 'Mudanças', icon: 'ð', module: 'gmud' }, 50 '/wifi-analyser': { label: 'WiâFi', icon: 'ð¶', module: 'dashboard' }, 51 '/detector': { label: 'Detector', icon: 'ð¡', module: null }, 52 '/constructor': { label: 'Constructor', icon: 'â¦', module: 'constructor' }, 53 '/fasttask': { label: 'Fast Task', icon: 'â', module: null }, 54 '/analise_hosts': { label: 'Análise de Hosts', icon: 'ð', module: 'analise_rede' }, 55 '/manager': { label: 'ERP', icon: 'â¡', module: null }, 56 '/manager/users-licenses': { label: 'Manager â Licenças Monitoring', icon: 'ð', module: null }, 57 '/admin/childrenllix': { label: 'Manager â Kids licenças', icon: 'ð±', module: null }, 58 '/admin/cupons': { label: 'Cupons', icon: 'ð', module: 'admin_cupons' }, 59 '/admin/vendas-whatsapp': { label: 'WhatsApp', icon: 'ð±', module: 'admin_vendas_whatsapp' }, 60 '/admin/security-audit': { label: 'Auditoria', icon: 'ð¡', module: null }, 61 '/revenda/dashboard': { label: 'Painel Empresa', icon: 'ð¢', module: null } 62 }; 63 64 function normalizeUrlPathSearch(pathname, search) { 65 try { 66 var p = pathname || '/'; 67 if (p.length > 1 && p.charAt(p.length - 1) === '/') p = p.slice(0, -1); 68 var raw = (search && search.charAt(0) === '?') ? search.slice(1) : (search || ''); 69 var sp = new URLSearchParams(raw); 70 var keys = Array.from(sp.keys()).sort(); 71 var q = new URLSearchParams(); 72 keys.forEach(function (k) { 73 var vals = sp.getAll(k).slice().sort();
74 vals.forEach(function (v) { q.append(k, v); }); 75 }); 76 var qs = q.toString(); 77 return p + (qs ? '?' + qs : ''); 78 } catch (e) { 79 return (pathname || '/') + (search || ''); 80 } 81 } 82 83 function normalizeStoredUrl(u) { 84 if (!u || typeof u !== 'string') return ''; 85 try { 86 if (u.charAt(0) === '/') { 87 var q = u.indexOf('?'); 88 if (q < 0) return normalizeUrlPathSearch(u, ''); 89 return normalizeUrlPathSearch(u.slice(0, q), u.slice(q)); 90 } 91 var parsed = new URL(u, window.location.origin); 92 return normalizeUrlPathSearch(parsed.pathname, parsed.search); 93 } catch (e2) { 94 return u; 95 } 96 } 97 98 function shouldSkipPath(pathNorm) { 99 var low = (pathNorm.split('?')[0] || '').toLowerCase(); 100 if (SKIP_PATHS[low]) return true; 101 for (var i = 0; i < SKIP_PREFIXES.length; i++) { 102 if (low.indexOf(SKIP_PREFIXES[i]) === 0) return true; 103 } 104 return false; 105 } 106 107 function humanizePath(pathNorm) { 108 var base = pathNorm.split('?')[0] || '/'; 109 if (base === '/') return 'InÃcio'; 110 var parts = base.replace(/^\/+/, '').split('/'); 111 var last = parts[parts.length - 1] || base; 112 return last.replace(/[-_]+/g, ' ').replace(/\b\w/g, function (c) { return c.toUpperCase(); }); 113 } 114 115 function getMeta(pathNorm) { 116 if (ROUTES[pathNorm]) return ROUTES[pathNorm]; 117 var q = pathNorm.indexOf('?'); 118 if (q > 0) { 119 var bare = pathNorm.slice(0, q); 120 if (ROUTES[bare]) return ROUTES[bare]; 121 } 122 return { label: humanizePath(pathNorm), icon: 'ð', module: null }; 123 } 124 125 function readList() { 126 try { 127 var raw = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]'); 128 return Array.isArray(raw) ? raw : []; 129 } catch (e) { 130 return []; 131 } 132 } 133 134 function writeList(arr) { 135 try { 136 localStorage.setItem(STORAGE_KEY, JSON.stringify(arr.slice(0, MAX))); 137 } catch (e) {} 138 } 139 140 function recordVisit() { 141 var pathNorm = normalizeUrlPathSearch(window.location.pathname, window.location.search); 142 if (!pathNorm || shouldSkipPath(pathNorm)) return; 143 144 var meta = getMeta(pathNorm); 145 var entry = { 146 url: pathNorm, 147 label: meta.label, 148 icon: meta.icon, 149 module: meta.module 150 }; 151 152 var list = readList(); 153 var next = [entry]; 154 for (var i = 0; i < list.length; i++) { 155 var it = list[i]; 156 if (!it || !it.url) continue; 157 if (normalizeStoredUrl(it.url) === pathNorm) continue; 158 next.push(it); 159 if (next.length >= MAX) break; 160 } 161 writeList(next); 162 } 163 164 var MANAGER_RESTRICTED_PREFIXES = ['/manager', '/admin/security-audit', '/admin/cupons', '/admin/vendas-whatsapp', '/admin/childrenllix', '/admin/provedores', '/admin/escolas', '/admin/prospectos']; 165 166 function isManagerRestrictedPath(url) { 167 var path = (url || '').split('?')[0].toLowerCase(); 168 for (var i = 0; i < MANAGER_RESTRICTED_PREFIXES.length; i++) { 169 if (path === MANAGER_RESTRICTED_PREFIXES[i] || path.indexOf(MANAGER_RESTRICTED_PREFIXES[i] + '/') === 0) return true; 170 } 171 return false; 172 } 173 174 function renderNavShortcuts() { 175 var box = document.getElementById('navRecentShortcuts'); 176 if (!box) return; 177 178 while (box.firstChild) box.removeChild(box.firstChild); 179 180 var cur = normalizeUrlPathSearch(window.location.pathname, window.location.search); 181 var raw = readList(); 182 var seen = {}; 183 var shown = 0; 184 185 var isManagerErp = false; 186 try { 187 var u = JSON.parse(localStorage.getItem('user') || '{}'); 188 isManagerErp = ((u.email || '').trim().toLowerCase() === '[email protected]'); 189 } catch (e) {} 190 191 for (var i = 0; i < raw.length && shown < MAX; i++) { 192 var it = raw[i]; 193 if (!it || !it.url) continue; 194 var nu = normalizeStoredUrl(it.url); 195 if (!nu || nu === cur) continue; 196 if (seen[nu]) continue; 197 if (!isManagerErp && isManagerRestrictedPath(nu)) continue; 198 seen[nu] = true; 199 200 var a = document.createElement('a'); 201 a.href = nu; 202 a.className = 'nav-recent-link'; 203 a.setAttribute('role', 'menuitem'); 204 if (it.module) a.setAttribute('data-module-id', it.module); 205 206 var icon = document.createElement('span'); 207 icon.className = 'nav-icon'; 208 icon.setAttribute('aria-hidden', 'true'); 209 icon.textContent = it.icon || 'ð'; 210 211 var span = document.createElement('span'); 212 span.textContent = it.label || humanizePath(nu); 213 214 a.appendChild(icon); 215 a.appendChild(span); 216 box.appendChild(a); 217 shown++; 218 } 219 220 var isWeb = false;
221 try { isWeb = window.__catellixEnabledWebModulesIsWeb === true; } catch (e1) {} 222 var enabled = []; 223 try { enabled = window.__catellixEnabledWebModules || []; } catch (e2) {} 224 if (isWeb && enabled.length) { 225 box.querySelectorAll('a[data-module-id]').forEach(function (link) { 226 var mid = link.getAttribute('data-module-id'); 227 if (!mid) return; 228 link.style.display = enabled.indexOf(mid) >= 0 ? '' : 'none'; 229 }); 230 } 231 232 var block = document.getElementById('navRecentBlock'); 233 if (block) { 234 var anyVis = false; 235 for (var k = 0; k < box.children.length; k++) { 236 if (box.children[k].style.display !== 'none') { anyVis = true; break; } 237 } 238 block.style.display = (box.childElementCount && anyVis) ? '' : 'none'; 239 } 240 } 241 242 function bindLogoutClear() { 243 try { 244 document.querySelectorAll('a[href="/logout"]').forEach(function (a) { 245 a.addEventListener('click', function () { 246 try { localStorage.removeItem(STORAGE_KEY); } catch (e) {} 247 }); 248 }); 249 } catch (e) {} 250 } 251 252 function run() { 253 recordVisit(); 254 bindLogoutClear(); 255 256 function tryRender() { 257 if (document.getElementById('navRecentShortcuts')) { 258 renderNavShortcuts(); 259 return; 260 } 261 if (document.readyState === 'loading') { 262 document.addEventListener('DOMContentLoaded', tryRender); 263 return; 264 } 265 setTimeout(tryRender, 50); 266 } 267 tryRender(); 268 } 269 270 window.CatellixRecentPages = { 271 record: recordVisit, 272 render: renderNavShortcuts, 273 normalize: normalizeStoredUrl 274 }; 275 276 run(); 277})();
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.