Adiciona uma extensão MV3 (Firefox e Chromium/Brave) que insere CPF ou CNPJ
válido direto no campo focado, pelo menu de contexto de qualquer input,
textarea ou contenteditable.
Extensões rodam em sandbox sem acesso a processos, então não há como invocar
os binários cpfgen/cnpjgen; fazer isso exigiria um host de native messaging
registrado no SO. Como os algoritmos são curtos e puros, foram portados para
JS em extension/generator.js, a partir de main.go (CPF) e de
cnpj_generator/cnpj/generate.go (CNPJ), trocando math/rand por
crypto.getRandomValues. test_generator.mjs compara as duas implementações
para que não divirjam.
A inserção usa document.execCommand("insertText"), que emite os eventos
nativos e portanto funciona em campos controlados por React/Vue — atribuir
.value diretamente seria descartado por esses frameworks.
Somente desktop: o Firefox para Android não implementa a API menus.
Testes carregam a extensão de verdade nos dois navegadores e verificam que os
8 itens de menu são criados e que a inserção funciona nos três tipos de campo.
104 lines
2.6 KiB
JavaScript
104 lines
2.6 KiB
JavaScript
// Porte direto dos geradores Go:
|
|
// cpf_generator/main.go -> generateCPF
|
|
// cnpj_generator/cnpj/generate.go -> generateCNPJ / generateCNPJAlpha
|
|
//
|
|
// Diferença deliberada: aqui o aleatório vem de crypto.getRandomValues em vez
|
|
// de math/rand. Os dígitos verificadores seguem exatamente as mesmas contas.
|
|
|
|
const ALPHA_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
const CNPJ_P1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
|
|
const CNPJ_P2 = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
|
|
|
|
// Inteiro uniforme em [0, max) sem o viés do módulo.
|
|
function randomInt(max) {
|
|
const limit = Math.floor(0xffffffff / max) * max;
|
|
const buf = new Uint32Array(1);
|
|
let v;
|
|
do {
|
|
crypto.getRandomValues(buf);
|
|
v = buf[0];
|
|
} while (v >= limit);
|
|
return v % max;
|
|
}
|
|
|
|
export function generateCPF() {
|
|
const digits = new Array(11);
|
|
|
|
for (let i = 0; i < 9; i++) {
|
|
digits[i] = randomInt(10);
|
|
}
|
|
|
|
let sum = 0;
|
|
for (let i = 0; i < 9; i++) {
|
|
sum += digits[i] * (10 - i);
|
|
}
|
|
let rem = sum % 11;
|
|
digits[9] = rem < 2 ? 0 : 11 - rem;
|
|
|
|
sum = 0;
|
|
for (let i = 0; i < 10; i++) {
|
|
sum += digits[i] * (11 - i);
|
|
}
|
|
rem = sum % 11;
|
|
digits[10] = rem < 2 ? 0 : 11 - rem;
|
|
|
|
return digits.join("");
|
|
}
|
|
|
|
export function formatCPF(cpf) {
|
|
if (cpf.length !== 11) return cpf;
|
|
return `${cpf.slice(0, 3)}.${cpf.slice(3, 6)}.${cpf.slice(6, 9)}-${cpf.slice(9)}`;
|
|
}
|
|
|
|
// Peso posicional do caractere: dígitos valem 0-9, letras valem código ASCII - 48
|
|
// (A = 17), conforme a regra do CNPJ alfanumérico.
|
|
function alphaValue(ch) {
|
|
return ch.charCodeAt(0) - 48;
|
|
}
|
|
|
|
function computeDV(s, table) {
|
|
let total = 0;
|
|
for (let i = 0; i < table.length; i++) {
|
|
total += alphaValue(s[i]) * table[i];
|
|
}
|
|
const r = total % 11;
|
|
return r >= 2 ? 11 - r : 0;
|
|
}
|
|
|
|
function isRepetitive(s) {
|
|
if (s.length === 0) return false;
|
|
for (let i = 1; i < s.length; i++) {
|
|
if (s[i] !== s[0]) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function generateCNPJBase(alpha) {
|
|
for (;;) {
|
|
let s = "";
|
|
for (let i = 0; i < 12; i++) {
|
|
s += alpha
|
|
? ALPHA_CHARS[randomInt(ALPHA_CHARS.length)]
|
|
: String(randomInt(10));
|
|
}
|
|
if (isRepetitive(s)) continue;
|
|
|
|
const dv1 = computeDV(s, CNPJ_P1);
|
|
const dv2 = computeDV(s + dv1, CNPJ_P2);
|
|
return `${s}${dv1}${dv2}`;
|
|
}
|
|
}
|
|
|
|
export function generateCNPJ() {
|
|
return generateCNPJBase(false);
|
|
}
|
|
|
|
export function generateCNPJAlpha() {
|
|
return generateCNPJBase(true);
|
|
}
|
|
|
|
export function formatCNPJ(cnpj) {
|
|
if (cnpj.length !== 14) return cnpj;
|
|
return `${cnpj.slice(0, 2)}.${cnpj.slice(2, 5)}.${cnpj.slice(5, 8)}/${cnpj.slice(8, 12)}-${cnpj.slice(12)}`;
|
|
}
|