File: /home/internet/public_html/whois/app.js
/**
* WHOIS Terminal — app.js
* Handles input cleaning, domain extraction, API call, and terminal rendering.
*/
(function () {
'use strict';
/* ── DOM refs ── */
const input = document.getElementById('domain-input');
const btn = document.getElementById('lookup-btn');
const output = document.getElementById('output');
/* ── Event bindings ── */
input.addEventListener('keydown', function (e) {
if (e.key === 'Enter') triggerLookup();
});
btn.addEventListener('click', triggerLookup);
window.addEventListener('load', function () { input.focus(); });
/* ──────────────────────────────────────────────
INPUT CLEANING
Accepts: URLs, comma/slash/pipe/semi/colon/space separated tokens,
"Domain: example.com" style, punycode, etc.
────────────────────────────────────────────── */
function cleanAndExtractDomain(raw) {
if (!raw || !raw.trim()) return null;
// 1. Trim outer whitespace
var s = raw.trim();
// 2. Split on common separators: , / | ; : and whitespace
// But we keep the token itself intact for URL parsing
var tokens = s.split(/[\s,\/\|\;]+/);
for (var i = 0; i < tokens.length; i++) {
var token = tokens[i].trim();
if (!token) continue;
// Remove trailing colons (e.g. "Domain:" → "Domain", value is next token)
token = token.replace(/:$/, '');
if (!token) continue;
// 3. If it looks like a URL (has protocol), extract hostname
var hostname = '';
if (/^https?:\/\//i.test(token)) {
try {
var url = new URL(token);
hostname = url.hostname;
} catch (e) {
// malformed URL — strip protocol manually
hostname = token.replace(/^https?:\/\//i, '').split('/')[0].split('?')[0].split('#')[0];
}
} else if (/^\/\//.test(token)) {
// protocol-relative
hostname = token.replace(/^\/\//, '').split('/')[0].split('?')[0];
} else {
hostname = token;
}
// 4. Remove any remaining path / query / fragment
hostname = hostname.split('/')[0].split('?')[0].split('#')[0];
// 5. Remove port if present (e.g. example.com:8080)
hostname = hostname.replace(/:\d+$/, '');
// 6. Remove leading "www." (case-insensitive)
hostname = hostname.replace(/^www\./i, '');
// 7. Lowercase
hostname = hostname.toLowerCase();
// 8. Validate: must have at least one dot, valid label chars (including xn-- for punycode)
// Label chars: a-z0-9, hyphens (not at start/end of label)
if (isValidDomain(hostname)) {
return hostname;
}
}
return null; // no valid domain found
}
function isValidDomain(domain) {
if (!domain) return false;
if (domain.length > 253) return false;
// Must contain at least one dot
if (domain.indexOf('.') === -1) return false;
// Split labels and validate each
var labels = domain.split('.');
if (labels.length < 2) return false;
for (var i = 0; i < labels.length; i++) {
var label = labels[i];
if (!label) return false; // empty label (leading/trailing dot)
if (label.length > 63) return false;
// Valid: a-z, 0-9, hyphen; must not start or end with hyphen
if (!/^[a-z0-9]([a-z0-9\-]*[a-z0-9])?$/.test(label) && !/^[a-z0-9]$/.test(label)) return false;
// Allow xn-- punycode
}
// TLD must be at least 2 chars
var tld = labels[labels.length - 1];
if (tld.length < 2) return false;
return true;
}
/* ──────────────────────────────────────────────
LOOKUP TRIGGER
────────────────────────────────────────────── */
function triggerLookup() {
var raw = input.value;
var domain = cleanAndExtractDomain(raw);
if (!domain) {
renderError('Invalid domain input', 'Could not extract a valid domain from the provided input.');
return;
}
renderLoading(domain);
fetchWhois(domain);
}
/* ──────────────────────────────────────────────
API CALL
────────────────────────────────────────────── */
function fetchWhois(domain) {
var controller = null;
var timeoutId = null;
var aborted = false;
if (typeof AbortController !== 'undefined') {
controller = new AbortController();
timeoutId = setTimeout(function () {
aborted = true;
controller.abort();
}, 12000); // 12s client-side timeout
}
var opts = { method: 'GET' };
if (controller) opts.signal = controller.signal;
var url = 'api/whois.php?domain=' + encodeURIComponent(domain);
fetch(url, opts)
.then(function (resp) {
if (timeoutId) clearTimeout(timeoutId);
if (!resp.ok) {
throw new Error('HTTP ' + resp.status);
}
return resp.json();
})
.then(function (data) {
if (data.error) {
renderError('Lookup failed', data.error);
} else {
renderResult(data);
}
})
.catch(function (err) {
if (timeoutId) clearTimeout(timeoutId);
if (aborted || (err && err.name === 'AbortError')) {
renderError('Lookup failed', 'Request timed out — WHOIS server did not respond in time.');
} else {
renderError('Lookup failed', err.message || 'Network error or server unreachable.');
}
});
}
/* ──────────────────────────────────────────────
RENDERING
────────────────────────────────────────────── */
function clearOutput() {
output.innerHTML = '';
}
function renderLoading(domain) {
clearOutput();
output.innerHTML =
'<div class="cmd-echo">$ whois ' + esc(domain) + '</div>' +
'<div class="loading-line">Querying WHOIS servers<span class="dots"></span></div>';
}
function renderResult(data) {
var fields = [
{ label: 'Domain Name', key: 'domainName' },
{ label: 'Registrar URL', key: 'registrarUrl' },
{ label: 'Updated Date', key: 'updatedDate' },
{ label: 'Creation Date', key: 'creationDate' },
{ label: 'Expiry Date', key: 'expiryDate' },
{ label: 'Domain Status', key: 'domainStatus' },
{ label: 'Name Server', key: 'nameServer' },
];
var rows = '';
fields.forEach(function (f) {
var val = data[f.key];
// Normalize arrays to comma-separated string
if (Array.isArray(val)) {
val = val.filter(Boolean).join(', ');
}
val = (val && String(val).trim()) ? String(val).trim() : 'N/A';
var isNA = (val === 'N/A');
rows +=
'<div class="result-row">' +
'<span class="result-label">' + esc(f.label) + ':</span>' +
'<span class="result-value' + (isNA ? ' na' : '') + '">' + esc(val) + '</span>' +
'</div>';
});
clearOutput();
output.innerHTML =
'<div class="cmd-echo">$ whois ' + esc(data.domainName || '') + '</div>' +
'<hr class="divider" />' +
'<div class="result-block">' + rows + '</div>';
}
function renderError(main, detail) {
clearOutput();
output.innerHTML =
'<div class="error-block">' +
'<div class="error-main">✖ ' + esc(main) + '</div>' +
(detail ? '<div class="error-detail">' + esc(detail) + '</div>' : '') +
'</div>';
}
/* ──────────────────────────────────────────────
UTILITIES
────────────────────────────────────────────── */
function esc(str) {
return String(str)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"');
}
})();