
Partilha de carteira na saída do escritório: o que a OAB permite (e o que veda) na transição
Saiu de um escritório? Entenda a partilha de carteira sob a ótica do CED e do Provimento 205/2021 do CFOAB. Conformidade é vantagem competitiva.
/* ============================================================ Coetus Lead Track - captura de atribuicao (site principal) Cria/atualiza o cookie "coetus_attribution", compartilhado com o subdominio ola.coetus.com.br via dominio .coetus.com.br. INSTALACAO (WordPress): - Plugin "Head, Footer and Code" -> secao HEAD. - Cole o conteudo deste arquivo envolvido pelas tags: - NAO instalar no subdominio ola (la o PHP cuida disso). Comportamento: - Registra TODA visita ao site, mesmo sem UTM. - Cada visita empilha uma entrada no array "visits" do cookie. - Chaves curtas (t,u,c,s,m,ct,tm,f,g,r) para economizar espaco. - Campo "u" guarda origin+pathname (SEM query string); os UTMs ficam em campos separados. - Cap de 20 entradas com politica hibrida: 5 primeiras + 15 ultimas. - Timestamps em UTC (ISO 8601 com sufixo Z) - padrao do projeto desde julho/2026; NAO voltar para offset local (o matching de Possiveis Leads no painel depende de UTC em tudo). - Guarda de tamanho: se o cookie codificado passar de ~3,8 KB, visitas antigas (fora das 5 primeiras) sao descartadas ate caber, evitando que o navegador rejeite o cookie inteiro. ============================================================ */ (function () { 'use strict'; /* ---- Configuracao (espelha o .env do projeto PHP) ---- */ var COOKIE_NAME = 'coetus_attribution'; var COOKIE_DOMAIN = '.coetus.com.br'; var COOKIE_DAYS = 90; var MAX_VISITS = 15; // cap total: 5 primeiras + 10 ultimas var KEEP_FIRST_VISITS = 5; var MAX_FIELD_LEN = 180; // truncagem por campo nas entradas de visita var MAX_COOKIE_BYTES = 3800; // margem de seguranca sob o limite de ~4096 var DEBUG = false; // true APENAS para testar; NUNCA em producao function log() { if (DEBUG && window.console) { console.log.apply(console, ['[CoetusTrack]'].concat([].slice.call(arguments))); } } /* ---- Data/hora em UTC (ISO 8601 com sufixo Z, sem milissegundos) ---- */ function nowUtc() { return new Date().toISOString().replace(/\.\d{3}Z$/, 'Z'); } /* ---- UUID v4 ---- */ function uuidv4() { if (window.crypto && typeof window.crypto.randomUUID === 'function') { return window.crypto.randomUUID(); } var b = new Uint8Array(16); window.crypto.getRandomValues(b); b[6] = (b[6] & 0x0f) | 0x40; b[8] = (b[8] & 0x3f) | 0x80; var h = []; for (var i = 0; i < 16; i++) { h.push((b[i] + 0x100).toString(16).slice(1)); } return h[0]+h[1]+h[2]+h[3]+'-'+h[4]+h[5]+'-'+h[6]+h[7]+'-' + h[8]+h[9]+'-'+h[10]+h[11]+h[12]+h[13]+h[14]+h[15]; } /* ---- Decodifica valor de cookie tratando "+" como espaco (compatibilidade com cookie eventualmente escrito pelo PHP) ---- */ function urlDecode(s) { return decodeURIComponent(s.replace(/\+/g, '%20')); } /* ---- Trunca strings longas (so nas entradas de visita) ---- */ function clip(s) { return (typeof s === 'string' && s.length > MAX_FIELD_LEN) ? s.slice(0, MAX_FIELD_LEN) : s; } /* ---- Parametros de rastreamento da URL ---- */ function getTracking() { var q = new URLSearchParams(window.location.search); function v(k) { var x = q.get(k); return (x && x.trim() !== '') ? x.trim() : null; } return { campaign: v('utm_campaign'), source: v('utm_source'), medium: v('utm_medium'), content: v('utm_content'), term: v('utm_term'), fbclid: v('fbclid'), gclid: v('gclid') }; } function hasTracking(t) { return !!(t.campaign || t.source || t.medium || t.content || t.term || t.fbclid || t.gclid); } /* ---- Monta um objeto de toque (first ou last) ---- Nomes longos mantidos para compatibilidade com o PHP. Aqui o "url" mantem a URL completa (com query string) porque first/last aparecem apenas uma vez no cookie, sem multiplicar o gasto de espaco. */ function buildTouch(t) { return { campaign: t.campaign, source: t.source || 'direct', medium: t.medium, content: t.content, term: t.term, fbclid: t.fbclid, gclid: t.gclid, url: window.location.href, at: nowUtc() }; } /* ---- Monta uma entrada do array "visits" ---- Usa chaves curtas e OMITE campos null/vazios para economizar bytes. O campo "u" guarda apenas origin+pathname (sem query string) porque os UTMs ja estao em campos separados. Campos individuais sao truncados em MAX_FIELD_LEN para conter o tamanho do cookie. */ function buildVisitEntry(t) { var entry = { t: nowUtc(), u: clip(window.location.origin + window.location.pathname) }; if (t.campaign) entry.c = clip(t.campaign); if (t.source) entry.s = clip(t.source); if (t.medium) entry.m = clip(t.medium); if (t.content) entry.ct = clip(t.content); if (t.term) entry.tm = clip(t.term); if (t.fbclid) entry.f = clip(t.fbclid); if (t.gclid) entry.g = clip(t.gclid); if (document.referrer) entry.r = clip(document.referrer); return entry; } /* ---- Empilha uma visita no array com politica hibrida de cap ---- Quando estoura MAX_VISITS, preserva as KEEP_FIRST_VISITS primeiras e mantem as (MAX_VISITS - KEEP_FIRST_VISITS) mais recentes. */ function pushVisit(cookie, entry) { if (!Array.isArray(cookie.visits)) { cookie.visits = []; } cookie.visits.push(entry); if (cookie.visits.length > MAX_VISITS) { var first = cookie.visits.slice(0, KEEP_FIRST_VISITS); var last = cookie.visits.slice(cookie.visits.length - (MAX_VISITS - KEEP_FIRST_VISITS)); cookie.visits = first.concat(last); } } /* ---- Cookie: leitura ---- */ function readCookie() { try { var pairs = document.cookie ? document.cookie.split(';') : []; for (var i = 0; i < pairs.length; i++) { var p = pairs[i].split('='); if (p[0].trim() === COOKIE_NAME) { var data = JSON.parse(urlDecode(p.slice(1).join('='))); if (data && typeof data.uid === 'string' && data.uid) { return data; } return null; } } } catch (e) { log('cookie corrompido, tratando como inexistente', e); } return null; } /* ---- Cookie: escrita ---- Guarda de tamanho: navegadores rejeitam SILENCIOSAMENTE cookies acima de ~4096 bytes (nome+valor). Se o valor codificado passar de MAX_COOKIE_BYTES, descarta a visita mais antiga fora do bloco preservado ate caber - perder visitas antigas e melhor do que perder o cookie inteiro. */ function writeCookie(data) { var value = encodeURIComponent(JSON.stringify(data)); while (value.length > MAX_COOKIE_BYTES && Array.isArray(data.visits) && data.visits.length > 1) { var idx = data.visits.length > KEEP_FIRST_VISITS ? KEEP_FIRST_VISITS : 0; data.visits.splice(idx, 1); value = encodeURIComponent(JSON.stringify(data)); log('cookie acima do limite; visita antiga descartada', { entries: data.visits.length }); } var maxAge = COOKIE_DAYS * 24 * 60 * 60; document.cookie = COOKIE_NAME + '=' + value + '; domain=' + COOKIE_DOMAIN + '; path=/' + '; max-age=' + maxAge + '; secure' + '; samesite=Lax'; } /* ---- Logica principal ---- TODA visita ao site principal gera uma entrada no array "visits", inclusive visitas diretas. */ try { var tracking = getTracking(); var marked = hasTracking(tracking); var cookie = readCookie(); var visitEntry = buildVisitEntry(tracking); if (!cookie) { // Sem cookie: cria do zero. Visita direta marca first/last com source="direct". var touch = buildTouch(tracking); cookie = { uid: uuidv4(), first: touch, last: touch, visits: [visitEntry] }; writeCookie(cookie); log('cookie criado', { marked: marked, entries: 1, uid: cookie.uid }); return; } // Cookie existe: tolerancia a cookie legado sem campo visits. if (!Array.isArray(cookie.visits)) { cookie.visits = []; } // Se ha tracking na URL, atualiza last (first permanece imutavel). if (marked) { cookie.last = buildTouch(tracking); } // Sempre empilha a visita atual (com cap hibrido). pushVisit(cookie, visitEntry); writeCookie(cookie); log('visita registrada', { marked: marked, entries: cookie.visits.length, uid: cookie.uid }); } catch (e) { log('erro inesperado', e); } })();
Ir para o conteúdo