'use strict'
const api = window.pfTool
let I18N = {}
function tt(key, params) {
var parts = String(key).split('.')
var cur = I18N
for (var i = 0; i < parts.length; i++) {
if (cur && typeof cur === 'object' && parts[i] in cur) {
cur = cur[parts[i]]
} else {
return key
}
}
if (typeof cur !== 'string') return key
if (!params) return cur
return cur.replace(/\{\{\s*(\w+)\s*\}\}/g, function (_m, name) {
return name in params ? String(params[name]) : '{{' + name + '}}'
})
}
function escapeHtml(s) {
return String(s).replace(/[&<>"']/g, function (c) {
return c === '&' ? '&' : c === '<' ? '<' : c === '>' ? '>' : c === '"' ? '"' : '''
})
}
const pageHost = document.getElementById('pageHost')
const logViewer = document.getElementById('logViewer')
const logBody = document.getElementById('logBody')
const logToggle = document.getElementById('logToggle')
logToggle.addEventListener('click', function () {
logViewer.classList.toggle('collapsed')
if (logViewer.classList.contains('collapsed')) {
logViewer.style.height = '36px'
logToggle.textContent = tt('logViewer.expand')
} else {
logViewer.style.height = ''
logToggle.textContent = tt('logViewer.collapse')
}
})
api.onLog(function (line) {
logViewer.hidden = false
logBody.textContent += line + '\n'
logBody.scrollTop = logBody.scrollHeight
})
function applyStaticI18n() {
document.title = tt('app.title')
var h1 = document.querySelector('.appHeader h1')
if (h1) h1.textContent = tt('app.title')
var logH2 = logViewer.querySelector('header h2')
if (logH2) logH2.textContent = tt('logViewer.heading')
logToggle.textContent = tt('logViewer.collapse')
}
function renderMain() {
pageHost.innerHTML =
' ' + escapeHtml(tt('main.intro')) + '' + escapeHtml(tt('main.heading')) + '
' +
'
' + escapeHtml(tt('main.cgnatWarn', { ip: outcome.externalIp || '?' })) + '
' } var manual = outcome.localIp ? tt('main.manualForward', { port: outcome.port, localIp: outcome.localIp }) : tt('main.manualForwardNoIp', { port: outcome.port }) extra += '' + escapeHtml(manual) + '
' } resultEl.innerHTML = '' + escapeHtml(msg) + '
' + extra + '' + escapeHtml(tt('main.detailLabel', { detail: outcome.detail || '' })) + '
' + '' + escapeHtml(tt('main.working')) + '
' api.openPort(port).then(function (outcome) { showResult(outcome) }).catch(function (err) { resultEl.innerHTML = '' + escapeHtml(tt('main.error', { message: (err && err.message) || String(err) })) + '
' }).then(function () { setBusy(false) }) }) closeBtn.addEventListener('click', function () { var port = parsePort() portEl.value = String(port) setBusy(true) api.closePort(port).then(function () { resultEl.innerHTML = '' + escapeHtml(tt('main.closed', { port: port })) + '
' }).then(function () { setBusy(false) }) }) quitBtn.addEventListener('click', function () { api.quit() }) } ;(async function () { try { I18N = (await api.loadLocale()) || {} } catch (_) { I18N = {} } applyStaticI18n() renderMain() })()