Merge branch 'master' into work

box-drawing
Ondřej Hruška 7 years ago
commit ced948b6eb
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 6
      base.php
  2. 19
      compile_html.php
  3. 37
      js/appcommon.js
  4. 15
      js/term/connection.js
  5. 36
      js/term/index.js
  6. 9
      js/term/screen.js
  7. 2
      js/term/screen_parser.js
  8. 8
      js/wifi.js
  9. 192
      lang/en.php
  10. 2
      pages/_head.php
  11. 3
      pages/cfg_network.php
  12. 90
      pages/cfg_system.php
  13. 40
      pages/cfg_term.php
  14. 2
      pages/cfg_wifi.php
  15. 20
      pages/cfg_wifi_conn.php
  16. 2
      pages/help/charsets.php
  17. 4
      pages/help/cmd_system.php
  18. 2
      pages/help/sgr_colors.php
  19. 2
      pages/help/sgr_styles.php
  20. 2
      sass/app.scss
  21. 16
      sass/form/_form_layout.scss
  22. 15
      sass/layout/_box.scss
  23. 3
      sass/pages/_about.scss

@ -97,7 +97,11 @@ function je($s)
function tr($key) function tr($key)
{ {
global $_messages; global $_messages;
return isset($_messages[$key]) ? $_messages[$key] : ('??' . $key . '??'); if (isset($_messages[$key])) return $_messages[$key];
else {
ob_end_clean();
die('??' . $key . '??');
}
} }
/** Like eval, but allows <?php and ?> */ /** Like eval, but allows <?php and ?> */

@ -22,9 +22,22 @@ ob_start();
foreach($_pages as $_k => $p) { foreach($_pages as $_k => $p) {
if ($p->bodyclass == 'api') { if ($p->bodyclass == 'api') {
if (ESP_DEMO) { if (ESP_DEMO) {
$target = 'term.html'; echo "Generating: ~$_k.html (bounce)\n";
echo "Generating: ~$_k.html -> $target\n";
$s = "<!DOCTYPE HTML><meta http-equiv=\"refresh\" content=\"0;url=$target\">"; if ($_k=='index') {
$s = "<!DOCTYPE HTML><meta http-equiv=\"refresh\" content=\"0;url=term.html\">";
}
else {
$s = "<!DOCTYPE HTML>
<script>
var ref = document.referrer;
var qat = document.referrer.indexOf('?');
if (qat !== -1) ref = ref.substring(0, qat)
location.href=ref+'?msg=Request ignored, this is a demo.';
</script>";
}
} else { } else {
continue; continue;
} }

@ -33,7 +33,11 @@ $.ready(function () {
let h = x.querySelector('h2') let h = x.querySelector('h2')
let hdl = function () { let hdl = function () {
$(x).toggleClass('expanded') if ($(x).hasClass('d-expanded')) {
$(x).removeClass('d-expanded')
} else {
$(x).toggleClass('expanded')
}
} }
$(h).on('click', hdl).on('keypress', cr(hdl)) $(h).on('click', hdl).on('keypress', cr(hdl))
}) })
@ -90,24 +94,39 @@ $.ready(function () {
// (a way to pass errors back from server via redirect) // (a way to pass errors back from server via redirect)
let errAt = window.location.search.indexOf('err=') let errAt = window.location.search.indexOf('err=')
if (errAt !== -1 && qs('.Box.errors')) { if (errAt !== -1 && qs('.Box.errors')) {
let errs = window.location.search.substr(errAt + 4).split(',') let errs = decodeURIComponent(window.location.search.substr(errAt + 4)).split(',')
let humanReadableErrors = [] let humanReadableErrors = []
errs.forEach(function (er) { errs.forEach(function (er) {
let lbls = qsa('label[for="' + er + '"]') let lbls = qsa('label[for="' + er + '"]')
for (let i = 0; i < lbls.length; i++) { if (lbls) {
let lbl = lbls[i] for (let i = 0; i < lbls.length; i++) {
lbl.classList.add('error') let lbl = lbls[i]
if (i === 0) humanReadableErrors.push(lbl.childNodes[0].textContent.trim().replace(/: ?$/, '')) lbl.classList.add('error')
if (i === 0) humanReadableErrors.push(lbl.childNodes[0].textContent.trim().replace(/: ?$/, ''))
}
} else {
humanReadableErrors.push(er)
} }
// else {
// hres.push(er)
// }
}) })
qs('.Box.errors .list').innerHTML = humanReadableErrors.join(', ') qs('.Box.errors .list').innerHTML = humanReadableErrors.join(', ')
qs('.Box.errors').classList.remove('hidden') qs('.Box.errors').classList.remove('hidden')
} }
let msgAt = window.location.search.indexOf('msg=')
if (msgAt !== -1 && qs('.Box.message')) {
let msg = decodeURIComponent(window.location.search.substr(msgAt + 4))
let box = qs('.Box.message')
box.innerHTML = msg
box.classList.remove('hidden')
setTimeout(() => {
box.classList.add('hiding')
setTimeout(() => {
box.classList.add('hidden')
}, 1000)
}, 2000)
}
modal.init() modal.init()
notify.init() notify.init()

@ -19,12 +19,18 @@ module.exports = class TermConnection extends EventEmitter {
this.pageShown = false this.pageShown = false
this.disconnectTimeout = null
document.addEventListener('visibilitychange', () => { document.addEventListener('visibilitychange', () => {
if (document.hidden === true) { if (document.hidden === true) {
console.info('Window lost focus, freeing socket') console.info('Window lost focus, freeing socket')
this.closeSocket() // Delayed, avoid disconnecting if the background time is short
clearTimeout(this.heartbeatTimeout) this.disconnectTimeout = setTimeout(() => {
this.closeSocket()
clearTimeout(this.heartbeatTimeout)
}, 1000)
} else { } else {
clearTimeout(this.disconnectTimeout)
console.info('Window got focus, re-connecting') console.info('Window got focus, re-connecting')
this.init() this.init()
} }
@ -80,7 +86,6 @@ module.exports = class TermConnection extends EventEmitter {
break break
default: default:
this.emit('load')
this.screen.load(evt.data) this.screen.load(evt.data)
if (!this.pageShown) { if (!this.pageShown) {
window.showPage() window.showPage()
@ -118,7 +123,7 @@ module.exports = class TermConnection extends EventEmitter {
console.error('Socket not ready') console.error('Socket not ready')
return false return false
} }
if (typeof message != 'string') { if (typeof message !== 'string') {
message = JSON.stringify(message) message = JSON.stringify(message)
} }
this.ws.send(message) this.ws.send(message)
@ -161,7 +166,7 @@ module.exports = class TermConnection extends EventEmitter {
heartbeat () { heartbeat () {
clearTimeout(this.heartbeatTimeout) clearTimeout(this.heartbeatTimeout)
this.heartbeatTimeout = setTimeout(() => this.onHeartbeatFail(), 2000) this.heartbeatTimeout = setTimeout(() => this.onHeartbeatFail(), 2500)
} }
onHeartbeatFail () { onHeartbeatFail () {

@ -14,33 +14,47 @@ module.exports = function (opts) {
const input = TermInput(conn, screen) const input = TermInput(conn, screen)
const termUpload = TermUpload(conn, input, screen) const termUpload = TermUpload(conn, input, screen)
screen.input = input screen.input = input
screen.conn = conn
input.termUpload = termUpload input.termUpload = termUpload
// we delay the display of "connecting" to avoid flash when changing tabs with the terminal open let showSplashTimeout = null
let showConnectingTimeout = -1 let showSplash = (obj, delay = 250) => {
clearTimeout(showSplashTimeout)
showSplashTimeout = setTimeout(() => {
screen.window.statusScreen = obj
}, delay)
}
conn.on('open', () => { conn.on('open', () => {
showConnectingTimeout = setTimeout(() => { // console.log('*open')
screen.window.statusScreen = { title: 'Connecting', loading: true } showSplash({ title: 'Connecting', loading: true })
}, 250)
}) })
conn.on('connect', () => { conn.on('connect', () => {
clearTimeout(showConnectingTimeout) // console.log('*connect')
screen.window.statusScreen = { title: 'Waiting for content', loading: true } showSplash({ title: 'Waiting for content', loading: true })
}) })
conn.on('load', () => { conn.on('load', () => {
// console.log('*load')
clearTimeout(showSplashTimeout)
if (screen.window.statusScreen) screen.window.statusScreen = null if (screen.window.statusScreen) screen.window.statusScreen = null
}) })
conn.on('disconnect', () => { conn.on('disconnect', () => {
clearTimeout(showConnectingTimeout) // console.log('*disconnect')
screen.window.statusScreen = { title: 'Disconnected' } showSplash({ title: 'Disconnected' })
screen.screen = [] screen.screen = []
screen.screenFG = [] screen.screenFG = []
screen.screenBG = [] screen.screenBG = []
screen.screenAttrs = [] screen.screenAttrs = []
}) })
conn.on('silence', () => { screen.window.statusScreen = { title: 'Waiting for server', loading: true } }) conn.on('silence', () => {
// console.log('*silence')
showSplash({ title: 'Waiting for server', loading: true }, 0)
})
// conn.on('ping-fail', () => { screen.window.statusScreen = { title: 'Disconnected' } }) // conn.on('ping-fail', () => { screen.window.statusScreen = { title: 'Disconnected' } })
conn.on('ping-success', () => { screen.window.statusScreen = { title: 'Re-connecting', loading: true } }) conn.on('ping-success', () => {
// console.log('*ping-success')
showSplash({ title: 'Re-connecting', loading: true }, 0)
})
conn.init() conn.init()
input.init(opts) input.init(opts)

@ -30,6 +30,15 @@ module.exports = class TermScreen extends EventEmitter {
return () => console.warn('TermScreen#input not set!') return () => console.warn('TermScreen#input not set!')
} }
}) })
// dummy. Handle for Conn
this.conn = new Proxy({}, {
get () {
return () => console.warn('TermScreen#conn not set!')
},
set (a, b) {
return () => console.warn('TermScreen#conn not set!')
}
})
this.cursor = { this.cursor = {
x: 0, x: 0,

@ -199,7 +199,7 @@ module.exports = class ScreenParser {
if (this.screen.window.debug) console.log(`Blinky cells: ${this.screen.blinkingCellCount}`) if (this.screen.window.debug) console.log(`Blinky cells: ${this.screen.blinkingCellCount}`)
this.screen.renderer.scheduleDraw('load', 16) this.screen.renderer.scheduleDraw('load', 16)
this.screen.emit('load') this.screen.conn.emit('load')
} }
/** /**

@ -7,8 +7,8 @@ const tr = require('./lang')
let curSSID let curSSID
// Get XX % for a slider input // Get XX % for a slider input
function rangePt (inp) { function calc_dBm (inp) {
return Math.round(((inp.value / inp.max) * 100)) + '%' return `+${(inp.value * 0.25).toFixed(2)} dBm`
} }
// Display selected STA SSID etc // Display selected STA SSID etc
@ -142,11 +142,11 @@ const tr = require('./lang')
let inp = x.querySelector('input') let inp = x.querySelector('input')
let disp1 = x.querySelector('.x-disp1') let disp1 = x.querySelector('.x-disp1')
let disp2 = x.querySelector('.x-disp2') let disp2 = x.querySelector('.x-disp2')
let t = rangePt(inp) let t = calc_dBm(inp)
$(disp1).html(t) $(disp1).html(t)
$(disp2).html(t) $(disp2).html(t)
$(inp).on('input', function () { $(inp).on('input', function () {
t = rangePt(inp) t = calc_dBm(inp)
$(disp1).html(t) $(disp1).html(t)
$(disp2).html(t) $(disp2).html(t)
}) })

@ -12,10 +12,24 @@ return [
'menu.term' => 'Back to Terminal', 'menu.term' => 'Back to Terminal',
'menu.cfg_system' => 'System Settings', 'menu.cfg_system' => 'System Settings',
'menu.cfg_wifi_conn' => 'Connecting to Network', 'menu.cfg_wifi_conn' => 'Connecting to Network',
'menu.settings' => 'Settings', 'menu.settings' => 'Settings',
'title.term' => 'Terminal', // not used - api etc. Added to suppress warnings
'menu.term_set' => '',
'menu.wifi_connstatus' => '',
'menu.wifi_set' => '',
'menu.wifi_scan' => '',
'menu.network_set' => '',
'menu.system_set' => '',
'menu.write_defaults' => '',
'menu.restore_defaults' => '',
'menu.restore_hard' => '',
'menu.reset_screen' => '',
'menu.index' => '',
// Terminal page
'title.term' => 'Terminal', // page title of the terminal page
'term_nav.config' => 'Config', 'term_nav.config' => 'Config',
'term_nav.wifi' => 'WiFi', 'term_nav.wifi' => 'WiFi',
@ -26,18 +40,14 @@ return [
'term_nav.keybd' => 'Keyboard', 'term_nav.keybd' => 'Keyboard',
'term_nav.paste_prompt' => 'Paste text to send:', 'term_nav.paste_prompt' => 'Paste text to send:',
'net.ap' => 'DHCP Server (AP)', // Terminal settings page
'net.sta' => 'DHCP Client (Station)',
'net.sta_mac' => 'Station MAC',
'net.ap_mac' => 'AP MAC',
'net.details' => 'MAC addresses',
'term.defaults' => 'Initial Settings', 'term.defaults' => 'Initial Settings',
'term.expert' => 'Expert Options', 'term.expert' => 'Expert Options',
'term.explain_initials' => ' 'term.explain_initials' => '
Those are the initial settings used after ESPTerm powers on or when the screen Those are the initial settings used after ESPTerm powers on, or when the screen
reset command is received. Some options can be changed by the application via escape sequences, reset command is received (<code>\ec</code>). They can be changed by the
those changes won\'t be saved in Flash. terminal application using escape sequences.
', ',
'term.explain_expert' => ' 'term.explain_expert' => '
Those are advanced config options that usually don\'t need to be changed. Those are advanced config options that usually don\'t need to be changed.
@ -58,7 +68,7 @@ return [
'term.term_width' => 'Width', 'term.term_width' => 'Width',
'term.term_height' => 'Height', 'term.term_height' => 'Height',
'term.buttons' => 'Button labels', 'term.buttons' => 'Button labels',
'term.theme' => 'Color scheme', 'term.theme' => 'Color palette',
'term.cursor_shape' => 'Cursor style', 'term.cursor_shape' => 'Cursor style',
'term.parser_tout_ms' => 'Parser timeout', 'term.parser_tout_ms' => 'Parser timeout',
'term.display_tout_ms' => 'Redraw delay', 'term.display_tout_ms' => 'Redraw delay',
@ -66,14 +76,14 @@ return [
'term.fn_alt_mode' => 'SS3 Fn keys', 'term.fn_alt_mode' => 'SS3 Fn keys',
'term.show_config_links' => 'Show nav links', 'term.show_config_links' => 'Show nav links',
'term.show_buttons' => 'Show buttons', 'term.show_buttons' => 'Show buttons',
'term.loopback' => 'Local Echo', 'term.loopback' => 'Local Echo (<span style="text-decoration:overline">SRM</span>)',
'term.crlf_mode' => 'Enter sends CR+LF', 'term.crlf_mode' => 'Enter = CR+LF (LNM)',
'term.want_all_fn' => 'Capture all keys<br>(F5, F11, F12…)', 'term.want_all_fn' => 'Capture all keys<br>(F5, F11, F12…)',
'term.button_msgs' => 'Button codes<br>(ASCII, dec, CSV)', 'term.button_msgs' => 'Button codes<br>(ASCII, dec, CSV)',
'term.color_fg' => 'Default fg.', 'term.color_fg' => 'Default fg.',
'term.color_bg' => 'Default bg.', 'term.color_bg' => 'Default bg.',
'term.color_fg_prev' => 'Fg. colors', 'term.color_fg_prev' => 'Foreground',
'term.color_bg_prev' => 'Bg. colors', 'term.color_bg_prev' => 'Background',
'term.colors_preview' => 'Defaults', 'term.colors_preview' => 'Defaults',
'cursor.block_blink' => 'Block, blinking', 'cursor.block_blink' => 'Block, blinking',
@ -83,23 +93,7 @@ return [
'cursor.bar_blink' => 'I-bar, blinking', 'cursor.bar_blink' => 'I-bar, blinking',
'cursor.bar_steady' => 'I-bar, steady', 'cursor.bar_steady' => 'I-bar, steady',
// // terminal color labels // Network config page
// 'color.0' => 'Black',
// 'color.1' => 'Red',
// 'color.2' => 'Green',
// 'color.3' => 'Yellow',
// 'color.4' => 'Blue',
// 'color.5' => 'Purple',
// 'color.6' => 'Cyan',
// 'color.7' => 'Silver',
// 'color.8' => 'Gray',
// 'color.9' => 'Light Red',
// 'color.10' => 'Light Green',
// 'color.11' => 'Light Yellow',
// 'color.12' => 'Light Blue',
// 'color.13' => 'Light Purple',
// 'color.14' => 'Light Cyan',
// 'color.15' => 'White',
'net.explain_sta' => ' 'net.explain_sta' => '
Switch off Dynamic IP to configure the static IP address.', Switch off Dynamic IP to configure the static IP address.',
@ -118,6 +112,14 @@ return [
'net.sta_addr_mask' => 'Subnet mask', 'net.sta_addr_mask' => 'Subnet mask',
'net.sta_addr_gw' => 'Gateway IP', 'net.sta_addr_gw' => 'Gateway IP',
'net.ap' => 'DHCP Server (AP)',
'net.sta' => 'DHCP Client (Station)',
'net.sta_mac' => 'Station MAC',
'net.ap_mac' => 'AP MAC',
'net.details' => 'MAC addresses',
// Wifi config page
'wifi.ap' => 'Built-in Access Point', 'wifi.ap' => 'Built-in Access Point',
'wifi.sta' => 'Join Existing Network', 'wifi.sta' => 'Join Existing Network',
@ -143,76 +145,89 @@ return [
'wifi.enter_passwd' => 'Enter password for ":ssid:"', 'wifi.enter_passwd' => 'Enter password for ":ssid:"',
'wifi.sta_explain' => 'After selecting a network, press Apply to connect.', 'wifi.sta_explain' => 'After selecting a network, press Apply to connect.',
'wifi.conn.status' => 'Status:', // Wifi connecting status page
'wifi.conn.back_to_config' => 'Back to WiFi config',
'wifi.conn.telemetry_lost' => 'Telemetry lost; something went wrong, or your device disconnected.', 'wificonn.status' => 'Status:',
'wifi.conn.explain_android_sucks' => ' 'wificonn.back_to_config' => 'Back to WiFi config',
'wificonn.telemetry_lost' => 'Telemetry lost; something went wrong, or your device disconnected.',
'wificonn.explain_android_sucks' => '
If you\'re configuring ESPTerm via a smartphone, or were connected If you\'re configuring ESPTerm via a smartphone, or were connected
from another external network, your device may lose connection and this from another external network, your device may lose connection and this
progress indicator won\'t work. Please wait a while (~ 15 seconds), progress indicator won\'t work. Please wait a while (~ 15 seconds),
then check if the connection succeeded.', then check if the connection succeeded.',
'wifi.conn.explain_reset' => ' 'wificonn.explain_reset' => '
To force enable the built-in AP, hold the BOOT To force enable the built-in AP, hold the BOOT
button until the blue LED starts flashing. Hold the button longer (until the LED button until the blue LED starts flashing. Hold the button longer (until the LED
flashes rapidly) for a "factory reset".', flashes rapidly) for a "factory reset".',
'wifi.conn.disabled' =>"Station mode is disabled.", 'wificonn.disabled' =>"Station mode is disabled.",
'wifi.conn.idle' =>"Idle, not connected and has no IP.", 'wificonn.idle' =>"Idle, not connected and has no IP.",
'wifi.conn.success' => "Connected! Received IP ", 'wificonn.success' => "Connected! Received IP ",
'wifi.conn.working' => "Connecting to selected AP", 'wificonn.working' => "Connecting to selected AP",
'wifi.conn.fail' => "Connection failed, check settings & try again. Cause: ", 'wificonn.fail' => "Connection failed, check settings & try again. Cause: ",
'system.save_restore' => 'Save & Restore', // Access restrictions form
'system.confirm_restore' => 'Restore all settings to their default values?',
'system.confirm_restore_hard' =>
'Restore to firmware default settings? This will reset ' .
'all active settings and switch to AP mode with the default SSID.',
'system.confirm_store_defaults' =>
'Enter admin password to confirm you want to overwrite the default settings.',
'system.password' => 'Admin password:',
'system.restore_defaults' => 'Reset to saved defaults',
'system.write_defaults' => 'Save active settings as defaults',
'system.restore_hard' => 'Reset active settings to firmware defaults',
'system.explain_persist' => '
ESPTerm saves all settings in Flash. The active settings can be copied to
the "defaults area" and restored later using the blue button below.
',
'system.uart' => 'Serial Port',
'system.explain_uart' => '
This form controls the primary, communication UART. The debug UART is fixed
at 115.200 baud, one stop-bit and no parity.
',
'system.security' => 'Access Restrictions', 'pwlock.title' => 'Access Restrictions',
'system.explain_security' => ' 'pwlock.explain' => '
Some parts, or all of the web interface can be protected by a password prompt. Some parts, or all of the web interface can be protected by a password prompt.
Leave the new password fields empty if you do not wish to change it. Leave the new password fields empty if you do not wish to change it.<br>
The default password is "%def_access_pw%".
', ',
'system.pwlock' => 'Protected pages', 'pwlock.region' => 'Protected pages',
'system.pwlock.none' => 'None, all open', 'pwlock.region.none' => 'None, all open',
'system.pwlock.settings_noterm' => 'WiFi, Net & System settings', 'pwlock.region.settings_noterm' => 'WiFi, Net & System settings',
'system.pwlock.settings' => 'All settings pages', 'pwlock.region.settings' => 'All settings pages',
'system.pwlock.menus' => 'This entire menu section', 'pwlock.region.menus' => 'This entire menu section',
'system.pwlock.all' => 'Everything, even terminal', 'pwlock.region.all' => 'Everything, even terminal',
'system.new_access_pw' => 'New password', 'pwlock.new_access_pw' => 'New password',
'system.new_access_pw2' => 'New pass., repeat', 'pwlock.new_access_pw2' => 'Repeat',
'system.admin_pw' => 'Admin password', 'pwlock.admin_pw' => 'Admin password',
'system.access_name' => 'Username', 'pwlock.access_name' => 'Username',
'system.change_adminpw' => 'Change Admin Password', // Setting admin password
'system.explain_adminpw' =>
'adminpw.title' => 'Change Admin Password',
'adminpw.explain' =>
' '
The "admin password" is used to manipulate the stored default settings The "admin password" is used to manipulate the stored default settings
and to change access restrictions. This password is not saved as part and to change access restrictions. This password is not saved as part
of the main config, i.e. using save / restore does not affect this of the main config, i.e. using save / restore does not affect this
password. When the admin password is forgotten, the easiest way to password. When the admin password is forgotten, the easiest way to
re-gain access is to wipe and re-flash the chip. re-gain access is to wipe and re-flash the chip.<br>
The default admin password is "%def_admin_pw%".
',
'adminpw.new_admin_pw' => 'New admin password',
'adminpw.new_admin_pw2' => 'Repeat',
'adminpw.old_admin_pw' => 'Old admin password',
// Persist form
'persist.title' => 'Save & Restore',
'persist.explain' => '
ESPTerm saves all settings in Flash. The active settings can be copied to
the "defaults area" and restored later using the blue button below.
', ',
'system.new_admin_pw' => 'New admin pass.', 'persist.confirm_restore' => 'Restore all settings to their default values?',
'system.new_admin_pw2' => 'New pass., repeat', 'persist.confirm_restore_hard' =>
'system.old_admin_pw' => 'Old admin pass.', 'Restore to firmware default settings? This will reset ' .
'all active settings and switch to AP mode with the default SSID.',
'persist.confirm_store_defaults' =>
'Enter admin password to confirm you want to overwrite the default settings.',
'persist.password' => 'Admin password:',
'persist.restore_defaults' => 'Reset to saved defaults',
'persist.write_defaults' => 'Save active settings as defaults',
'persist.restore_hard' => 'Reset active settings to factory defaults',
'persist.restore_hard_explain' => '(This clears the WiFi config! Does not affect saved defaults or admin password.)',
// UART settings form
'uart.title' => 'Serial Port Parameters',
'uart.explain' => '
This form controls the communication UART. The debug UART is fixed
at 115.200 baud, one stop-bit and no parity.
',
'uart.baud' => 'Baud rate', 'uart.baud' => 'Baud rate',
'uart.parity' => 'Parity', 'uart.parity' => 'Parity',
'uart.parity.none' => 'None', 'uart.parity.none' => 'None',
@ -223,6 +238,19 @@ return [
'uart.stop_bits.one_and_half' => 'One and half', 'uart.stop_bits.one_and_half' => 'One and half',
'uart.stop_bits.two' => 'Two', 'uart.stop_bits.two' => 'Two',
// HW tuning form
'hwtuning.title' => 'Hardware Tuning',
'hwtuning.explain' => '
ESP8266 can be overclocked from 80&nbsp;MHz to 160&nbsp;MHz.
This will make it more responsive and allow faster screen updates
at the expense of slightly higher power consumption. This can also make
it more susceptible to interference. Use with care.
',
'hwtuning.overclock' => 'Overclock to 160MHz',
// Generic button / dialog labels
'apply' => 'Apply!', 'apply' => 'Apply!',
'enabled' => 'Enabled', 'enabled' => 'Enabled',
'disabled' => 'Disabled', 'disabled' => 'Disabled',

@ -32,4 +32,6 @@ if (strpos($_GET['BODYCLASS'], 'cfg') !== false) {
<span class="lead"><?= tr('form_errors') ?></span>&nbsp;<span class="list"></span> <span class="lead"><?= tr('form_errors') ?></span>&nbsp;<span class="list"></span>
</div> </div>
<div class="Box message hidden"></div>
<?php endif; ?> <?php endif; ?>

@ -2,6 +2,7 @@
$ipmask='pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}$"'; $ipmask='pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}$"';
?> ?>
<!-- STA -->
<form class="Box str mobcol" action="<?= e(url('network_set')) ?>" method="GET" id="form-netsta"> <form class="Box str mobcol" action="<?= e(url('network_set')) ?>" method="GET" id="form-netsta">
<h2 tabindex=0><?= tr('net.sta') ?></h2> <h2 tabindex=0><?= tr('net.sta') ?></h2>
@ -35,6 +36,7 @@ $ipmask='pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}$"';
</div> </div>
</form> </form>
<!-- AP -->
<form class="Box str mobcol" action="<?= e(url('network_set')) ?>" method="GET" id="form-netap"> <form class="Box str mobcol" action="<?= e(url('network_set')) ?>" method="GET" id="form-netap">
<h2 tabindex=0><?= tr('net.ap') ?></h2> <h2 tabindex=0><?= tr('net.ap') ?></h2>
@ -73,6 +75,7 @@ $ipmask='pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}$"';
</div> </div>
</form> </form>
<!-- MACs -->
<div class="Box mobcol"> <div class="Box mobcol">
<h2><?= tr('net.details') ?></h2> <h2><?= tr('net.details') ?></h2>

@ -1,107 +1,131 @@
<!-- Persist -->
<div class="Box str mobcol"> <div class="Box str mobcol">
<h2 tabindex=0><?= tr('system.save_restore') ?></h2> <h2 tabindex=0><?= tr('persist.title') ?></h2>
<div class="Row explain nomargintop"> <div class="Row explain nomargintop">
<?= tr('system.explain_persist') ?> <?= tr('persist.explain') ?>
</div> </div>
<div class="Row buttons"> <div class="Row buttons2">
<a class="button icn-restore" <a class="button icn-restore"
onclick="return confirm('<?= tr('system.confirm_restore') ?>');" onclick="return confirm('<?= tr('persist.confirm_restore') ?>');"
href="<?= e(url('restore_defaults')) ?>"> href="<?= e(url('restore_defaults')) ?>">
<?= tr('system.restore_defaults') ?> <?= tr('persist.restore_defaults') ?>
</a> </a>
</div> </div>
<div class="Row buttons"> <div class="Row buttons2">
<a onclick="writeDefaults(); return false;" href="#"><?= tr('system.write_defaults') ?></a> <a onclick="writeDefaults(); return false;" href="#"><?= tr('persist.write_defaults') ?></a>
</div> </div>
<div class="Row buttons"> <div class="Row buttons2">
<a onclick="return confirm('<?= tr('system.confirm_restore_hard') ?>');" <a onclick="return confirm('<?= tr('persist.confirm_restore_hard') ?>');"
href="<?= e(url('restore_hard')) ?>"> href="<?= e(url('restore_hard')) ?>">
<?= tr('system.restore_hard') ?> <?= tr('persist.restore_hard') ?>
</a> </a><br>
<?= tr('persist.restore_hard_explain') ?>
</div> </div>
</div> </div>
<!-- Overclock -->
<form class="Box str mobcol" action="<?= e(url('system_set')) ?>" method="GET" id="form-hw">
<h2 tabindex=0><?= tr('hwtuning.title') ?></h2>
<div class="Row explain">
<?= tr('hwtuning.explain') ?>
</div>
<div class="Row checkbox" >
<label><?= tr('hwtuning.overclock') ?></label><!--
--><span class="box" tabindex=0 role=checkbox></span>
<input type="hidden" id="overclock" name="overclock" value="%overclock%">
</div>
<div class="Row buttons">
<a class="button icn-ok" href="#" onclick="qs('#form-hw').submit()"><?= tr('apply') ?></a>
</div>
</form>
<?php <?php
$NOFILL = 'readonly onfocus="this.removeAttribute(\'readonly\')" style="cursor:text" autocomplete="off"'; $NOFILL = 'readonly onfocus="this.removeAttribute(\'readonly\')" style="cursor:text" autocomplete="off"';
?> ?>
<form class="Box str mobcol" action="<?= e(url('system_set')) ?>" method="GET" id="form-2"> <!-- Access perms -->
<h2 tabindex=0><?= tr('system.security') ?></h2> <form class="Box str mobcol" action="<?= e(url('system_set')) ?>" method="GET" id="form-access">
<h2 tabindex=0><?= tr('pwlock.title') ?></h2>
<div class="Row explain"> <div class="Row explain">
<?= tr('system.explain_security') ?> <?= tr('pwlock.explain') ?>
</div> </div>
<div class="Row"> <div class="Row">
<label for="pwlock"><?= tr("system.pwlock") ?></label> <label for="pwlock"><?= tr("pwlock.region") ?></label>
<select name="pwlock" id="pwlock"> <select name="pwlock" id="pwlock">
<option value="0"><?= tr("system.pwlock.none") ?></option> <option value="0"><?= tr("pwlock.region.none") ?></option>
<option value="1"><?= tr("system.pwlock.settings_noterm") ?></option> <option value="1"><?= tr("pwlock.region.settings_noterm") ?></option>
<option value="2"><?= tr("system.pwlock.settings") ?></option> <option value="2"><?= tr("pwlock.region.settings") ?></option>
<option value="3"><?= tr("system.pwlock.menus") ?></option> <option value="3"><?= tr("pwlock.region.menus") ?></option>
<option value="4"><?= tr("system.pwlock.all") ?></option> <option value="4"><?= tr("pwlock.region.all") ?></option>
</select> </select>
</div> </div>
<div class="Row"> <div class="Row">
<label for="access_name"><?= tr('system.access_name') ?></label> <label for="access_name"><?= tr('pwlock.access_name') ?></label>
<input type="text" name="access_name" id="access_name" value="%h:access_name%"> <input type="text" name="access_name" id="access_name" value="%h:access_name%">
</div> </div>
<div class="Row"> <div class="Row">
<label for="access_pw"><?= tr('system.new_access_pw') ?></label> <label for="access_pw"><?= tr('pwlock.new_access_pw') ?></label>
<input type="password" name="access_pw" id="access_pw" <?=$NOFILL?>> <input type="password" name="access_pw" id="access_pw" <?=$NOFILL?>>
</div> </div>
<div class="Row"> <div class="Row">
<label for="access_pw2"><?= tr('system.new_access_pw2') ?></label> <label for="access_pw2"><?= tr('pwlock.new_access_pw2') ?></label>
<input type="password" name="access_pw2" id="access_pw2" <?=$NOFILL?>> <input type="password" name="access_pw2" id="access_pw2" <?=$NOFILL?>>
</div> </div>
<div class="Row"> <div class="Row">
<label for="pw"><?= tr('system.admin_pw') ?></label> <label for="pw"><?= tr('pwlock.admin_pw') ?></label>
<input type="password" name="pw" id="pw" required> <input type="password" name="pw" id="pw" required>
</div> </div>
<div class="Row buttons"> <div class="Row buttons">
<a class="button icn-ok" href="#" onclick="qs('#form-2').submit()"><?= tr('apply') ?></a> <a class="button icn-ok" href="#" onclick="qs('#form-access').submit()"><?= tr('apply') ?></a>
</div> </div>
</form> </form>
<form class="Box str mobcol" action="<?= e(url('system_set')) ?>" method="GET" id="form-3"> <!-- Admin pw -->
<h2 tabindex=0><?= tr('system.change_adminpw') ?></h2> <form class="Box str mobcol" action="<?= e(url('system_set')) ?>" method="GET" id="form-admin">
<h2 tabindex=0><?= tr('adminpw.title') ?></h2>
<div class="Row explain"> <div class="Row explain">
<?= tr('system.explain_adminpw') ?> <?= tr('adminpw.explain') ?>
</div> </div>
<div class="Row"> <div class="Row">
<label for="admin_pw"><?= tr('system.new_admin_pw') ?></label> <label for="admin_pw"><?= tr('adminpw.new_admin_pw') ?></label>
<input type="password" name="admin_pw" id="admin_pw"> <input type="password" name="admin_pw" id="admin_pw">
</div> </div>
<div class="Row"> <div class="Row">
<label for="admin_pw2"><?= tr('system.new_admin_pw2') ?></label> <label for="admin_pw2"><?= tr('adminpw.new_admin_pw2') ?></label>
<input type="password" name="admin_pw2" id="admin_pw2"> <input type="password" name="admin_pw2" id="admin_pw2">
</div> </div>
<div class="Row"> <div class="Row">
<label for="pw"><?= tr('system.old_admin_pw') ?></label> <label for="pw"><?= tr('adminpw.old_admin_pw') ?></label>
<input type="password" name="pw" id="pw" required> <input type="password" name="pw" id="pw" required>
</div> </div>
<div class="Row buttons"> <div class="Row buttons">
<a class="button icn-ok" href="#" onclick="qs('#form-3').submit()"><?= tr('apply') ?></a> <a class="button icn-ok" href="#" onclick="qs('#form-admin').submit()"><?= tr('apply') ?></a>
</div> </div>
</form> </form>
<script> <script>
function writeDefaults() { function writeDefaults() {
var pw = prompt('<?= tr('system.confirm_store_defaults') ?>'); var pw = prompt('<?= tr('persist.confirm_store_defaults') ?>');
if (!pw) return; if (!pw) return;
location.href = <?=json_encode(url('write_defaults')) ?> + '?pw=' + pw; location.href = <?=json_encode(url('write_defaults')) ?> + '?pw=' + pw;
} }

@ -2,7 +2,8 @@
<a href="<?= e(url('reset_screen')) ?>"><?= tr('term.reset_screen') ?></a> <a href="<?= e(url('reset_screen')) ?>"><?= tr('term.reset_screen') ?></a>
</div> </div>
<form class="Box mobopen str" action="<?= e(url('term_set')) ?>" method="GET" id='form-scheme'> <!-- Theme -->
<form class="Box mobcol str" action="<?= e(url('term_set')) ?>" method="GET" id='form-scheme'>
<h2><?= tr('term.color_scheme') ?></h2> <h2><?= tr('term.color_scheme') ?></h2>
<div class="Row explain"> <div class="Row explain">
@ -119,7 +120,8 @@
</div> </div>
</form> </form>
<form class="Box fold str" action="<?= e(url('term_set')) ?>" method="GET" id='form-initial'> <!-- Initials -->
<form class="Box mobcol str" action="<?= e(url('term_set')) ?>" method="GET" id='form-initial'>
<h2><?= tr('term.defaults') ?></h2> <h2><?= tr('term.defaults') ?></h2>
<div class="Row explain"> <div class="Row explain">
@ -166,16 +168,29 @@
<input class="tiny" type="text" name="bm5" id="bm5" value="%h:bm5%"> <input class="tiny" type="text" name="bm5" id="bm5" value="%h:bm5%">
</div> </div>
<div class="Row checkbox" >
<label><?= tr('term.crlf_mode') ?></label><!--
--><span class="box" tabindex=0 role=checkbox></span>
<input type="hidden" id="crlf_mode" name="crlf_mode" value="%crlf_mode%">
</div>
<div class="Row checkbox" >
<label><?= tr('term.loopback') ?></label><!--
--><span class="box" tabindex=0 role=checkbox></span>
<input type="hidden" id="loopback" name="loopback" value="%loopback%">
</div>
<div class="Row buttons"> <div class="Row buttons">
<a class="button icn-ok" href="#" onclick="qs('#form-initial').submit()"><?= tr('apply') ?></a> <a class="button icn-ok" href="#" onclick="qs('#form-initial').submit()"><?= tr('apply') ?></a>
</div> </div>
</form> </form>
<form class="Box fold str" action="<?= e(url('term_set')) ?>" method="GET" id="form-uart"> <!-- UART -->
<h2 tabindex=0><?= tr('system.uart') ?></h2> <form class="Box mobcol str" action="<?= e(url('term_set')) ?>" method="GET" id="form-uart">
<h2 tabindex=0><?= tr('uart.title') ?></h2>
<div class="Row explain"> <div class="Row explain">
<?= tr('system.explain_uart') ?> <?= tr('uart.explain') ?>
</div> </div>
<div class="Row"> <div class="Row">
@ -225,7 +240,8 @@
</div> </div>
</form> </form>
<form class="Box fold str" action="<?= e(url('term_set')) ?>" method="GET" id='form-expert'> <!-- Expert terminal opts -->
<form class="Box mobcol str" action="<?= e(url('term_set')) ?>" method="GET" id='form-expert'>
<h2><?= tr('term.expert') ?></h2> <h2><?= tr('term.expert') ?></h2>
<div class="Row explain"> <div class="Row explain">
@ -262,24 +278,12 @@
<input type="hidden" id="want_all_fn" name="want_all_fn" value="%want_all_fn%"> <input type="hidden" id="want_all_fn" name="want_all_fn" value="%want_all_fn%">
</div> </div>
<div class="Row checkbox" >
<label><?= tr('term.crlf_mode') ?></label><!--
--><span class="box" tabindex=0 role=checkbox></span>
<input type="hidden" id="crlf_mode" name="crlf_mode" value="%crlf_mode%">
</div>
<div class="Row checkbox" > <div class="Row checkbox" >
<label><?= tr('term.show_config_links') ?></label><!-- <label><?= tr('term.show_config_links') ?></label><!--
--><span class="box" tabindex=0 role=checkbox></span> --><span class="box" tabindex=0 role=checkbox></span>
<input type="hidden" id="show_config_links" name="show_config_links" value="%show_config_links%"> <input type="hidden" id="show_config_links" name="show_config_links" value="%show_config_links%">
</div> </div>
<div class="Row checkbox" >
<label><?= tr('term.loopback') ?></label><!--
--><span class="box" tabindex=0 role=checkbox></span>
<input type="hidden" id="loopback" name="loopback" value="%loopback%">
</div>
<div class="Row buttons"> <div class="Row buttons">
<a class="button icn-ok" href="#" onclick="qs('#form-expert').submit()"><?= tr('apply') ?></a> <a class="button icn-ok" href="#" onclick="qs('#form-expert').submit()"><?= tr('apply') ?></a>
</div> </div>

@ -1,3 +1,4 @@
<!-- AP -->
<form class="Box str mobcol" action="<?= e(url('wifi_set')) ?>" method="GET" id="form-ap"> <form class="Box str mobcol" action="<?= e(url('wifi_set')) ?>" method="GET" id="form-ap">
<h2 tabindex=0><?= tr('wifi.ap') ?></h2> <h2 tabindex=0><?= tr('wifi.ap') ?></h2>
@ -42,6 +43,7 @@
</div> </div>
</form> </form>
<!-- STA -->
<form class="Box str mobcol expanded" action="<?= e(url('wifi_set')) ?>" method="GET" id="form-sta"> <form class="Box str mobcol expanded" action="<?= e(url('wifi_set')) ?>" method="GET" id="form-sta">
<h2 tabindex=0><?= tr('wifi.sta') ?></h2> <h2 tabindex=0><?= tr('wifi.sta') ?></h2>

@ -1,13 +1,13 @@
<h1><?= tr('menu.cfg_wifi_conn') ?></h1> <h1><?= tr('menu.cfg_wifi_conn') ?></h1>
<div class="Box"> <div class="Box">
<p><b><?= tr('wifi.conn.status') ?></b> <span id="status"></span><span class="anim-dots">.</span></p> <p><b><?= tr('wificonn.status') ?></b> <span id="status"></span><span class="anim-dots">.</span></p>
<a href="<?= e(url('cfg_wifi')) ?>" id="backbtn" class="button"><?= tr('wifi.conn.back_to_config') ?></a> <a href="<?= e(url('cfg_wifi')) ?>" id="backbtn" class="button"><?= tr('wificonn.back_to_config') ?></a>
</div> </div>
<div class="Box"> <div class="Box">
<p><?= tr('wifi.conn.explain_android_sucks') ?></p> <p><?= tr('wificonn.explain_android_sucks') ?></p>
<p><?= tr('wifi.conn.explain_reset') ?></p> <p><?= tr('wificonn.explain_reset') ?></p>
</div> </div>
<script> <script>
@ -16,15 +16,15 @@
var failCounter = 0; var failCounter = 0;
var messages = <?= json_encode([ var messages = <?= json_encode([
'disabled' => tr('wifi.conn.disabled'), 'disabled' => tr('wificonn.disabled'),
'idle' => tr('wifi.conn.idle'), 'idle' => tr('wificonn.idle'),
'success' => tr('wifi.conn.success'), 'success' => tr('wificonn.success'),
'working' => tr('wifi.conn.working'), 'working' => tr('wificonn.working'),
'fail' => tr('wifi.conn.fail'), 'fail' => tr('wificonn.fail'),
]) ?>; ]) ?>;
function onFail() { function onFail() {
$("#status").html(<?= json_encode(tr('wifi.conn.telemetry_lost')) ?>); $("#status").html(<?= json_encode(tr('wificonn.telemetry_lost')) ?>);
$('.anim-dots').addClass('hidden'); $('.anim-dots').addClass('hidden');
} }

@ -41,7 +41,7 @@
} }
?> ?>
<h3>Switching commands</h3> <h3>Codepage switching commands</h3>
<p> <p>
There are two character set slots, G0 and G1. There are two character set slots, G0 and G1.

@ -14,8 +14,8 @@
<tr> <tr>
<td>`\ec`</td> <td>`\ec`</td>
<td> <td>
Clear screen, reset attributes and cursor. Clear screen, reset attributes and cursor. This command also restores the default
The screen size, title and button labels remain unchanged. screen size, title, button labels and messages.
</td> </td>
</tr> </tr>
<tr> <tr>

@ -1,6 +1,6 @@
<div class="Box fold"> <div class="Box fold">
<h2>Commands: Color SGR</h2> <h2>Commands: Color attributes</h2>
<div class="Row v"> <div class="Row v">
<p> <p>

@ -1,5 +1,5 @@
<div class="Box fold"> <div class="Box fold">
<h2>Commands: Style SGR</h2> <h2>Commands: Style attributes</h2>
<div class="Row v"> <div class="Row v">
<p> <p>

@ -7,7 +7,7 @@
@import "utils"; @import "utils";
$form-label-w: 160px; $form-label-w: 180px;
$form-label-gap: 8px; $form-label-gap: 8px;
$form-field-w: 250px; $form-field-w: 250px;

@ -12,6 +12,16 @@ form { @include naked(); }
} }
} }
.Box.message {
font-weight: bold;
color: #0fca50;
transition: opacity linear 1s;
opacity: 1;
&.hiding {
opacity: 0;
}
}
.Row { .Row {
vertical-align: middle; vertical-align: middle;
margin: 12px auto; margin: 12px auto;
@ -65,13 +75,17 @@ form { @include naked(); }
} }
// buttons2 is the same style, but different selector for use in the admin page // buttons2 is the same style, but different selector for use in the admin page
&.buttons { &.buttons, &.buttons2 {
margin: 16px auto; margin: 16px auto;
input, .button { input, .button {
margin-right: dist(-1); margin-right: dist(-1);
} }
} }
&.buttons2 {
display:block;
}
&.centered { &.centered {
justify-content: center; justify-content: center;
} }

@ -139,12 +139,27 @@
&.expanded { &.expanded {
.Row { .Row {
display: flex; display: flex;
&.explain { display: block; }
&.v { &.v {
display: block; display: block;
} }
} }
} }
// desktop-expanded
@include media($tablet-min) {
&.d-expanded {
.Row {
display: flex;
&.explain { display: block; }
&.v {
display: block;
}
}
}
}
} }
.Box.fold { .Box.fold {

@ -24,7 +24,8 @@
.page-help { .page-help {
code { code {
background: rgba(33, 97, 109, 0.31); background: #2da2b9;
color: black;
border-radius: 1px; border-radius: 1px;
padding: 0 2px; padding: 0 2px;
} }

Loading…
Cancel
Save