Bilacode / index.html
CVNSS's picture
Update index.html
783e73e verified
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<title>CVNSS4.0 ⇄ Bila Hex/Nhị phân | Chưa hỗ trợ chuỗi dài</title>
<style>
body { background: #1e2232; font-family: 'Fira Mono', monospace; margin: 0; }
.container { max-width: 760px; margin: 44px auto; background: #181f29; border-radius: 16px;
box-shadow: 0 3px 18px #000c; padding: 32px 24px 24px 24px;}
h2 { color: #e0e7ef; font-weight: 500; margin-bottom: 8px;}
label { font-weight: bold; color: #38bdf8; margin-top: 12px; display: block;}
textarea { width: 100%; font-size: 1em; background: #1e293b; color: #f1f5f9;
border-radius: 7px; padding: 12px; border: 1px solid #334155;
margin-bottom: 14px; min-height: 48px; resize: vertical;}
button { background: #38bdf8; color: #020617; border: none; border-radius: 8px;
padding: 11px 20px; font-size: 1em; font-family: inherit; font-weight: 600;
margin: 7px 13px 7px 0; box-shadow: 0 1px 5px #0891b2aa; transition: 0.12s;}
button:hover { background: #0ea5e9; color: #fff;}
.outblock { background: #222b38; border-radius: 8px; margin-top: 12px; margin-bottom: 10px; }
.outlabel { color: #6ee7b7; font-size: 0.99em; padding: 8px 0 2px 2px; }
.output { color: #e5e5e5; background: none; padding: 8px 8px 4px 8px; font-size: 1.08em;
word-break: break-all; font-family: 'Fira Mono', monospace; min-height: 16px;}
.error { color: #f87171; font-weight: bold; padding: 7px 0 0 0;}
.note { color: #a5b4fc; font-size: 0.97em; margin-top: 12px;}
.byline { text-align: right; margin-top: 26px; color: #64748b; font-size: 0.97em;}
@media (max-width: 800px) { .container {padding: 10px; max-width: 99vw;} }
::selection { background: #38bdf888; }
</style>
</head>
<body>
<div class="container">
<h2>CVNSS4.0 ⇄ Bila Hex/Nhị phân<br> Hiện chỉ hỗ trợ khoảng 30 ký tự để test</h2>
<label for="ascii">CVNSS4.0:</label>
<textarea id="ascii" placeholder="Nhập chữ cái, số, dấu cách... Chuỗi dài cỡ 50 ký tự đều được!"></textarea>
<button onclick="asciiToCBOR()">CVNSS4.0 → Bila</button>
<button onclick="clearAll()">Xoá</button>
<div class="error" id="err_ascii"></div>
<div class="outblock">
<div class="outlabel">Bila Hex:</div>
<div class="output" id="cborhex"></div>
<div class="outlabel">Bila Nhị phân:</div>
<div class="output" id="cborbin"></div>
</div>
<label for="cborhex_in">Bila Hex:</label>
<textarea id="cborhex_in" placeholder="Nhập mã Hex hợp lệ (header 78xx, 79xxxx, ... không giới hạn độ dài)"></textarea>
<button onclick="cborHexToAscii()">Bila Hex → CVNSS4.0</button>
<div class="error" id="err_hex"></div>
<label for="cborbin_in">Bila Nhị phân:</label>
<textarea id="cborbin_in" placeholder="Nhập mã Nhị phân hợp lệ"></textarea>
<button onclick="cborBinToAscii()">Bila Nhị phân → CVNSS4.0</button>
<div class="error" id="err_bin"></div>
<div class="outblock">
<div class="outlabel">Kết quả:</div>
<div class="output" id="ascii_out"></div>
</div>
<div class="note">
• Nhập/dán tối đa 30-40 ký tự.<br>
• Tự động loại ký tự không hợp lệ (chỉ giữ A–Z, a–z, 0–9, dấu cách).<br>
• Mã Hex dài sẽ tự động dùng header 78xx, 79xxxx theo chuẩn RFC7049.<br>
• Kết quả chưa tương thích sẽ báo lỗi, lấy ký tự CVNSS4.0 (<a href="https://chuvnsongsong.com/" target="_blank" style="color:#38bdf8">Chữ VN Song Song 4.0</a>).
</div>
<div class="byline">© Long Ngo, 2025 – Bản demo đang thử nghiệm</div>
</div>
<script>
// Chặn realtime ký tự lạ khi gõ/dán vào textarea (giữ input sạch)
document.getElementById('ascii').addEventListener('input', function() {
let clean = this.value.replace(/[^A-Za-z0-9 ]+/g, '');
if(this.value !== clean) {
this.value = clean;
document.getElementById('err_ascii').textContent = '❌ Đã tự động loại bỏ ký tự không hợp lệ!';
} else {
document.getElementById('err_ascii').textContent = '';
}
});
function cleanASCII(s) {
return s.replace(/[\r\n\t\u200B-\u200D\uFEFF]/g, "");
}
function checkValidASCII(s) {
return /^[A-Za-z0-9 ]+$/.test(s);
}
// Hỗ trợ CBOR chuẩn mọi độ dài: <24, <256, <65536 ký tự
function asciiToCBOR() {
let t = document.getElementById('ascii').value;
document.getElementById('err_ascii').textContent = "";
if (!t) return showCBOR('', '');
let tclean = cleanASCII(t);
if (!checkValidASCII(tclean)) {
let wrong = tclean.replace(/[A-Za-z0-9 ]/g, "");
document.getElementById('err_ascii').textContent =
'❌ Chỉ cho phép A–Z, a–z, 0–9, dấu cách! Ký tự lỗi: [' + wrong.split("").join(" ") + ']';
showCBOR('', '');
return;
}
let encoder = new TextEncoder();
let bytes = encoder.encode(tclean);
let hex = "", b = "";
if (bytes.length < 24) {
hex = (0x60 + bytes.length).toString(16);
} else if (bytes.length < 256) {
hex = "78" + bytes.length.toString(16).padStart(2, "0");
} else if (bytes.length < 65536) {
hex = "79" + bytes.length.toString(16).padStart(4, "0");
} else {
document.getElementById('err_ascii').textContent = '❌ Chuỗi quá dài (tối đa 65.535 ký tự)!';
showCBOR('', '');
return;
}
hex += Array.from(bytes).map(x => x.toString(16).padStart(2, "0")).join('');
b = hex.match(/.{1,2}/g).map(x => parseInt(x, 16).toString(2).padStart(8, '0')).join('');
showCBOR(hex, b);
}
function cborHexToAscii() {
let hex = document.getElementById('cborhex_in').value
.replace(/[^0-9a-fA-F]/g, '')
.toLowerCase();
document.getElementById('err_hex').textContent = "";
if (!hex) return showAscii('');
if (hex.length % 2 !== 0) {
document.getElementById('err_hex').textContent = '❌ Hex phải có số ký tự chẵn!';
showAscii('');
return;
}
if (!/^[0-9a-f]+$/.test(hex)) {
document.getElementById('err_hex').textContent = '❌ Hex không hợp lệ!';
showAscii('');
return;
}
try {
let idx=0, l=0, fb=parseInt(hex.substr(idx,2),16); idx+=2;
if ((fb&0xe0)===0x60) l=fb&0x1f;
else if(fb===0x78) { l=parseInt(hex.substr(idx,2),16); idx+=2; }
else if(fb===0x79) { l=parseInt(hex.substr(idx,4),16); idx+=4; }
else { document.getElementById('err_hex').textContent = '❌ Không đúng mã string'; showAscii(''); return; }
let bh = hex.substr(idx, l*2);
if (bh.length<l*2) {
document.getElementById('err_hex').textContent = '❌ Thiếu bytes, cần ' + (l*2) + ' ký tự hex, chỉ có ' + bh.length;
showAscii('');
return;
}
let bs=[];
for(let i=0;i<bh.length;i+=2) bs.push(parseInt(bh.substr(i,2),16));
let s;
try {
s = new TextDecoder().decode(new Uint8Array(bs));
} catch (e) {
document.getElementById('err_hex').textContent = '❌ Không thể giải mã UTF-8: ' + e.message;
showAscii('');
return;
}
let clean = cleanASCII(s);
if (!checkValidASCII(clean)) {
let wrong = clean.replace(/[A-Za-z0-9 ]/g, "");
document.getElementById('err_hex').textContent =
'❌ Dữ liệu nhập không hợp lệ (chỉ cho A–Z, a–z, 0–9, dấu cách)! Ký tự lỗi: [' +
wrong.split("").join(" ") + ']';
showAscii('');
return;
}
showAscii(clean);
} catch (err) {
document.getElementById('err_hex').textContent = '❌ Lỗi giải mã: ' + err.message;
showAscii('');
}
}
function cborBinToAscii() {
let bin = document.getElementById('cborbin_in').value.trim().replace(/\s+/g,'');
document.getElementById('err_bin').textContent = "";
if (!bin) return showAscii('');
if (!/^[01]+$/.test(bin)) { document.getElementById('err_bin').textContent = '❌ Nhị phân không hợp lệ!'; showAscii(''); return; }
if (bin.length%8!==0) { document.getElementById('err_bin').textContent = '❌ Chuỗi phải là bội số 8!'; showAscii(''); return; }
try {
let idx=0, l=0, fb=parseInt(bin.substr(idx,8),2); idx+=8;
if ((fb&0xe0)===0x60) l=fb&0x1f;
else if(fb===0x78) { l=parseInt(bin.substr(idx,8),2); idx+=8; }
else if(fb===0x79) { l=parseInt(bin.substr(idx,16),2); idx+=16; }
else { document.getElementById('err_bin').textContent = '❌ Không đúng CBOR string'; showAscii(''); return; }
let bs=[];
for(let i=0;i<l;i++) {
if(idx+8>bin.length) { document.getElementById('err_bin').textContent = '❌ Thiếu bytes'; showAscii(''); return; }
bs.push(parseInt(bin.substr(idx,8),2)); idx+=8;
}
let s;
try {
s = new TextDecoder().decode(new Uint8Array(bs));
} catch (e) {
document.getElementById('err_bin').textContent = '❌ Không thể giải mã UTF-8: ' + e.message;
showAscii('');
return;
}
let clean = cleanASCII(s);
if (!checkValidASCII(clean)) {
let wrong = clean.replace(/[A-Za-z0-9 ]/g, "");
document.getElementById('err_bin').textContent =
'❌ Dữ liệu nhập không hợp lệ (chỉ cho A–Z, a–z, 0–9, dấu cách)! Ký tự lỗi: [' +
wrong.split("").join(" ") + ']';
showAscii('');
return;
}
showAscii(clean);
} catch (err) {
document.getElementById('err_bin').textContent = '❌ Lỗi giải mã: ' + err.message;
showAscii('');
}
}
function showCBOR(hex, bin) {
document.getElementById('cborhex').textContent = hex;
document.getElementById('cborbin').textContent = bin;
document.getElementById('ascii_out').textContent = "";
}
function showAscii(ascii) {
document.getElementById('ascii_out').textContent = ascii;
}
function clearAll() {
document.getElementById('ascii').value = '';
document.getElementById('cborhex_in').value = '';
document.getElementById('cborbin_in').value = '';
document.getElementById('err_ascii').textContent = '';
document.getElementById('err_hex').textContent = '';
document.getElementById('err_bin').textContent = '';
showCBOR('', '');
showAscii('');
}
</script>
</body>
</html>