Files
logsdu/src/format.ts

104 lines
4.1 KiB
TypeScript
Raw Normal View History

2026-08-10 14:25:30 -03:00
// *************************************************************************
// * (C)opyright 2026 by Ruben Carlo Benante *
// * *
// * This program is free software; you can redistribute it and/or modify *
// * it under the terms of the GNU General Public License as published by *
// * the Free Software Foundation, either version 3 of the License, or *
// * (at your option) any later version. *
// * *
// * This program is distributed in the hope that it will be useful, *
// * but WITHOUT ANY WARRANTY; without even the implied warranty of *
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
// * GNU General Public License for more details. *
// * *
// * You should have received a copy of the GNU General Public License *
// * along with this program. If not, see http://www.gnu.org/licenses/. *
// * *
// * Contact author at: *
// * Ruben Carlo Benante *
// * rcb@beco.cc *
// *************************************************************************
// The portal drives its three inputs with Inputmask, so the values we store
// have to already be in the shape the masks expect (the placeholders on the
// page: "2000101010", "01/01/2000", "000.000.000-00"). Normalising here means
// the options page accepts a raw paste of digits and stores something the
// masks will not fight with.
/** Strip everything that is not a digit. */
export function digits(value: string): string {
return value.replace(/\D+/g, '');
}
/** Registration number: digits only, at most 10. */
export function formatRa(value: string): string {
return digits(value).slice(0, 10);
}
/** Birth date as dd/mm/yyyy, formatted progressively while typing. */
export function formatDate(value: string): string {
const d = digits(value).slice(0, 8);
const parts: string[] = [];
if (d.length > 0) parts.push(d.slice(0, 2));
if (d.length > 2) parts.push(d.slice(2, 4));
if (d.length > 4) parts.push(d.slice(4, 8));
return parts.join('/');
}
/** CPF as 000.000.000-00, formatted progressively while typing. */
export function formatCpf(value: string): string {
const d = digits(value).slice(0, 11);
let out = d.slice(0, 3);
if (d.length > 3) out += `.${d.slice(3, 6)}`;
if (d.length > 6) out += `.${d.slice(6, 9)}`;
if (d.length > 9) out += `-${d.slice(9, 11)}`;
return out;
}
export function isCompleteRa(value: string): boolean {
return digits(value).length > 0;
}
/**
* A complete, calendar-valid date. Deliberately checks the round trip rather
* than just the digit count, so 31/02/2000 is rejected before it ever reaches
* the portal.
*/
export function isCompleteDate(value: string): boolean {
const m = /^(\d{2})\/(\d{2})\/(\d{4})$/.exec(value);
if (!m) return false;
const day = Number(m[1]);
const month = Number(m[2]);
const year = Number(m[3]);
const date = new Date(year, month - 1, day);
return (
date.getFullYear() === year &&
date.getMonth() === month - 1 &&
date.getDate() === day
);
}
export function isCompleteCpf(value: string): boolean {
return digits(value).length === 11;
}
/**
* Origin of a user-typed site address, or null if it cannot be parsed.
* Accepts input without a scheme ("saladigital.example.com") by assuming
* https, which is what someone pasting an address from the URL bar expects.
*/
export function toOrigin(value: string): string | null {
const trimmed = value.trim();
if (!trimmed) return null;
const withScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed)
? trimmed
: `https://${trimmed}`;
try {
const url = new URL(withScheme);
if (url.protocol !== 'http:' && url.protocol !== 'https:') return null;
return url.origin;
} catch {
return null;
}
}