frontend improvements

pull/30/head
Ondřej Hruška 8 years ago
parent 21b304de9f
commit f6621252f6
  1. 11
      html_orig/_debug_replacements.php
  2. 8
      html_orig/_pages.php
  3. 4
      html_orig/css/app.css
  4. 14
      html_orig/index.php
  5. 258
      html_orig/js/app.js
  6. 41
      html_orig/jssrc/appcommon.js
  7. 32
      html_orig/jssrc/notif.js
  8. 4
      html_orig/jssrc/term.js
  9. 35
      html_orig/jssrc/utils.js
  10. 56
      html_orig/jssrc/wifi.js
  11. 12
      html_orig/lang/en.php
  12. 3
      html_orig/packjs.sh
  13. 3
      html_orig/pages/_cfg_menu.php
  14. 4
      html_orig/pages/_head.php
  15. 51
      html_orig/pages/about.php
  16. 27
      html_orig/pages/cfg_wifi.php
  17. 53
      html_orig/pages/cfg_wifi_conn.php
  18. 37
      html_orig/pages/help.php
  19. 5
      html_orig/sass/layout/_base.scss
  20. 76
      html_orig/sass/layout/_espterm_specific_old.scss
  21. 53
      html_orig/term.html
  22. 17
      html_orig/term_test.php
  23. 89
      html_orig/wifi.html
  24. 26
      html_orig/wifi_conn.html
  25. 15
      html_orig/wifi_test.php

@ -1,5 +1,10 @@
<?php
/**
* Those replacements are done by the development server to test it locally
* without esphttpd. This is needed mainly for places where the replacements
* are given to JavaScript, to avoid syntax errors with %%
*/
return [
'%term_title%' => 'ESP8266 Wireless Terminal',
@ -28,4 +33,10 @@ return [
'%sta_enable%' => '1',
'%opmode%' => '3',
'%vers_fw%' => '1.2.3',
'%date%' => date('Y-m-d'),
'%time%' => date('G:i'),
'%vers_httpd%' => '4.5.6',
'%vers_sdk%' => '1.52',
'%githubrepo%' => 'https://github.com/MightyPork/esp-vt100-firmware',
];

@ -13,14 +13,16 @@ function pg($key, $bc, $path) {
}
pg('cfg_wifi', 'cfg', '/cfg/wifi');
pg('cfg_wifi_conn', '', '/wifi/connecting'); // page without menu that tries to show the connection progress
pg('cfg_network', 'cfg', '/cfg/network');
pg('cfg_term', 'cfg', '/cfg/term');
pg('about', 'cfg', '/about');
pg('help', 'cfg', '/help');
pg('about', 'cfg page-about', '/about');
pg('help', 'cfg page-help', '/help');
pg('term', 'term', '/');
// technical
// ajax API
pg('wifi_set', '', '/wifi/set');//'/cfg/wifi/set');
pg('wifi_scan', '', '/wifi/scan');//'/cfg/wifi/scan');
pg('wifi_connstatus', '', '/wifi/connstatus');
return $pages;

@ -343,6 +343,10 @@ a:hover {
[onclick] {
cursor: pointer; }
ul > * {
padding-top: .2em;
padding-bottom: .2em; }
/* Main outer container */
#outer {
display: flex;

@ -1,15 +1,19 @@
<?php
if (! file_exists('_env.php')) {
die("Copy <b>_env.php.example</b> to <b>_env.php</b> and check the settings inside!");
}
require '_env.php';
$prod = defined('STDIN');
define ('DEBUG', !$prod);
$root = DEBUG ? ESP_IP : '';
define ('LIVE_ROOT', $root);
$root = DEBUG ? json_encode(ESP_IP) : 'window.location.href';
define ('JS_WEB_ROOT', $root);
define('CUR_PAGE', $_GET['page'] ?: 'term');
define('LOCALE', $_GET['locale'] ?: 'en');
$_messages = require(__DIR__ . '/messages/' . LOCALE . '.php');
$_messages = require(__DIR__ . '/lang/' . LOCALE . '.php');
$_pages = require('_pages.php');
define('APP_NAME', 'ESPTerm');
@ -17,9 +21,9 @@ define('PAGE_TITLE', $_pages[CUR_PAGE]->label . ' :: ' . APP_NAME);
define('BODYCLASS', $_pages[CUR_PAGE]->bodyclass);
/** URL (dev or production) */
function url($name, $root=false) {
function url($name, $relative=false) {
global $_pages;
if ($root) return $_pages[$name]->path;
if ($relative) return $_pages[$name]->path;
if (DEBUG) return "/index.php?page=$name";
else return $_pages[$name]->path;

@ -705,20 +705,16 @@
function mk(e) {return document.createElement(e)}
/** Find one by query */
function qq(s) {return document.querySelector(s)}
function qs(s) {return document.querySelector(s)}
/** Find all by query */
function qa(s) {return document.querySelectorAll(s)}
function qsa(s) {return document.querySelectorAll(s)}
/** Convert any to bool safely */
function bool(x) {
return (x === 1 || x === '1' || x === true || x === 'true');
}
function intval(x) {
return parseInt(x);
}
/** Extend an objects with options */
function extend(defaults, options) {
var target = {};
@ -739,23 +735,23 @@ function rgxe(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}
/** Format number to N decimal places, output as string */
function numfmt(x, places) {
var pow = Math.pow(10, places);
return Math.round(x*pow) / pow;
}
function estimateLoadTime(fs, n) {
return (1000/fs)*n+1500;
}
/** Get millisecond timestamp */
function msNow() {
return +(new Date);
}
/** Get ms elapsed since msNow() */
function msElapsed(start) {
return msNow() - start;
}
/** Shim for log base 10 */
Math.log10 = Math.log10 || function(x) {
return Math.log(x) / Math.LN10;
};
@ -796,18 +792,25 @@ String.prototype.format = function () {
return out;
};
/** HTML escape */
function e(str) {
return String(str)
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
return $.htmlEscape(str);
}
/** Check for undefined */
function undef(x) {
return typeof x == 'undefined';
}
/** Safe json parse */
function jsp() {
try {
return JSON.parse(e);
} catch(e) {
console.error(e);
return null;
}
}
/** Module for toggling a modal overlay */
(function () {
var modal = {};
@ -851,6 +854,27 @@ function undef(x) {
})();
/** Global generic init */
$.ready(function () {
// Checkbox UI (checkbox CSS and hidden input with int value)
$('.Row.checkbox').forEach(function(x) {
var inp = x.querySelector('input');
var box = x.querySelector('.box');
$(box).toggleClass('checked', inp.value);
$(x).on('click', function() {
inp.value = 1 - inp.value;
$(box).toggleClass('checked', inp.value)
});
});
// Expanding boxes on mobile
$('.Box.mobcol').forEach(function(x) {
var h = x.querySelector('h2');
$(h).on('click', function() {
$(x).toggleClass('expanded');
});
});
// loader dots...
setInterval(function () {
$('.anim-dots').each(function (x) {
@ -863,12 +887,13 @@ $.ready(function () {
// flipping number boxes with the mouse wheel
$('input[type=number]').on('mousewheel', function(e) {
var val = +$(this).val();
var $this = $(this);
var val = +$this.val();
if (isNaN(val)) val = 1;
var step = +($(this).attr('step') || 1);
var min = +$(this).attr('min');
var max = +$(this).attr('max');
var step = +($this.attr('step') || 1);
var min = +$this.attr('min');
var max = +$this.attr('max');
if(e.wheelDelta > 0) {
val += step;
} else {
@ -877,14 +902,14 @@ $.ready(function () {
if (typeof min != 'undefined') val = Math.max(val, +min);
if (typeof max != 'undefined') val = Math.min(val, +max);
$(this).val(val);
$this.val(val);
if ("createEvent" in document) {
var evt = document.createEvent("HTMLEvents");
evt.initEvent("change", false, true);
$(this)[0].dispatchEvent(evt);
$this[0].dispatchEvent(evt);
} else {
$(this)[0].fireEvent("onchange");
$this[0].fireEvent("onchange");
}
e.preventDefault();
@ -894,10 +919,7 @@ $.ready(function () {
});
$._loader = function(vis) {
if(vis)
$('#loader').addClass('show');
else
$('#loader').removeClass('show');
$('#loader').toggleClass('show', vis);
};
(function() {
/**
@ -1036,7 +1058,7 @@ $._loader = function(vis) {
H = obj.h;
/* Build screen & show */
var e, cell, scr = qq('#screen');
var e, cell, scr = qs('#screen');
// Empty the screen node
while (scr.firstChild) scr.removeChild(scr.firstChild);
@ -1182,7 +1204,7 @@ $._loader = function(vis) {
}
});
qa('#buttons button').forEach(function(s) {
qsa('#buttons button').forEach(function(s) {
s.addEventListener('click', function() {
sendBtnMsg(+this.dataset['n']);
});
@ -1202,181 +1224,3 @@ $._loader = function(vis) {
Input.init();
}
})();
/** Wifi page */
(function () {
var authStr = ['Open', 'WEP', 'WPA', 'WPA2', 'WPA/WPA2'];
var curSSID;
/** Update display for received response */
function onScan(resp, status) {
if (status != 200) {
// bad response
rescan(5000); // wait 5sm then retry
return;
}
resp = JSON.parse(resp);
var done = !bool(resp.result.inProgress) && (resp.result.APs.length > 0);
rescan(done ? 15000 : 1000);
if (!done) return; // no redraw yet
// clear the AP list
var $list = $('#ap-list');
// remove old APs
$('#ap-list .AP').remove();
$list.toggle(done);
$('#ap-loader').toggle(!done);
// scan done
resp.result.APs.sort(function (a, b) {
return b.rssi - a.rssi;
}).forEach(function (ap) {
ap.enc = intval(ap.enc);
if (ap.enc > 4) return; // hide unsupported auths
var item = document.createElement('div');
var $item = $(item)
.data('ssid', ap.essid)
.data('pwd', ap.enc != 0)
.addClass('AP');
// mark current SSID
if (ap.essid == curSSID) {
$item.addClass('selected');
}
var inner = document.createElement('div');
$(inner).addClass('inner')
.htmlAppend('<div class="rssi">{0}</div>'.format(ap.rssi_perc))
.htmlAppend('<div class="essid" title="{0}">{0}</div>'.format($.htmlEscape(ap.essid)))
.htmlAppend('<div class="auth">{0}</div>'.format(authStr[ap.enc]));
$item.on('click', function () {
var $th = $(this);
// populate the form
$('#conn-essid').val($th.data('ssid'));
$('#conn-passwd').val(''); // clear
if ($th.data('pwd')) {
// this AP needs a password
Modal.show('#psk-modal');
} else {
Modal.show('#reset-modal');
$('#conn-form').submit();
}
});
item.appendChild(inner);
$list[0].appendChild(item);
});
}
/** Ask the CGI what APs are visible (async) */
function scanAPs() {
$.get('http://'+_root+'/wifi/scan', onScan);
}
function rescan(time) {
setTimeout(scanAPs, time);
}
/** Set up the WiFi page */
window.wifiInit = function (obj) {
//var ap_json = {
// "result": {
// "inProgress": "0",
// "APs": [
// {"essid": "Chlivek", "bssid": "88:f7:c7:52:b3:99", "rssi": "204", "enc": "4", "channel": "1"},
// {"essid": "TyNikdy", "bssid": "5c:f4:ab:0d:f1:1b", "rssi": "164", "enc": "3", "channel": "1"},
// ]
// }
//};
// Hide what should be hidden in this mode
$('.x-hide-'+obj.mode).addClass('hidden');
obj.mode = +obj.mode;
// Channel writable only in AP mode
if (obj.mode != 2) $('#channel').attr('readonly', 1);
curSSID = obj.staSSID;
// add SSID to the opmode field
if (curSSID) {
var box = $('#opmodebox');
box.html(box.html() + ' (' + curSSID + ')');
}
// hide IP if IP not received
if (!obj.staIP) $('.x-hide-noip').addClass('hidden');
// scan if not AP
if (obj.mode != 2) {
scanAPs();
}
$('#modeswitch').html([
'<a class="button" href="/wifi/set?opmode=3">Client+AP</a>&nbsp;<a class="button" href="/wifi/set?opmode=2">AP only</a>',
'<a class="button" href="/wifi/set?opmode=3">Client+AP</a>',
'<a class="button" href="/wifi/set?opmode=1">Client only</a>&nbsp;<a class="button" href="/wifi/set?opmode=2">AP only</a>'
][obj.mode-1]);
};
window.wifiConn = function () {
var xhr = new XMLHttpRequest();
var abortTmeo;
function getStatus() {
xhr.open("GET", 'http://'+_root+"/wifi/connstatus");
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status >= 200 && xhr.status < 300) {
clearTimeout(abortTmeo);
var data = JSON.parse(xhr.responseText);
var done = false;
var msg = '...';
if (data.status == "idle") {
msg = "Preparing to connect";
}
else if (data.status == "success") {
msg = "Connected! Received IP " + data.ip + ".";
done = true;
}
else if (data.status == "working") {
msg = "Connecting to selected AP";
}
else if (data.status == "fail") {
msg = "Connection failed, check your password and try again.";
done = true;
}
$("#status").html(msg);
if (done) {
$('#backbtn').removeClass('hidden');
$('.anim-dots').addClass('hidden');
} else {
window.setTimeout(getStatus, 1000);
}
}
};
abortTmeo = setTimeout(function () {
xhr.abort();
$("#status").html("Telemetry lost, try reconnecting to the AP.");
$('#backbtn').removeClass('hidden');
$('.anim-dots').addClass('hidden');
}, 4000);
xhr.send();
}
getStatus();
};
})();

@ -1,5 +1,26 @@
/** Global generic init */
$.ready(function () {
// Checkbox UI (checkbox CSS and hidden input with int value)
$('.Row.checkbox').forEach(function(x) {
var inp = x.querySelector('input');
var box = x.querySelector('.box');
$(box).toggleClass('checked', inp.value);
$(x).on('click', function() {
inp.value = 1 - inp.value;
$(box).toggleClass('checked', inp.value)
});
});
// Expanding boxes on mobile
$('.Box.mobcol').forEach(function(x) {
var h = x.querySelector('h2');
$(h).on('click', function() {
$(x).toggleClass('expanded');
});
});
// loader dots...
setInterval(function () {
$('.anim-dots').each(function (x) {
@ -12,12 +33,13 @@ $.ready(function () {
// flipping number boxes with the mouse wheel
$('input[type=number]').on('mousewheel', function(e) {
var val = +$(this).val();
var $this = $(this);
var val = +$this.val();
if (isNaN(val)) val = 1;
var step = +($(this).attr('step') || 1);
var min = +$(this).attr('min');
var max = +$(this).attr('max');
var step = +($this.attr('step') || 1);
var min = +$this.attr('min');
var max = +$this.attr('max');
if(e.wheelDelta > 0) {
val += step;
} else {
@ -26,14 +48,14 @@ $.ready(function () {
if (typeof min != 'undefined') val = Math.max(val, +min);
if (typeof max != 'undefined') val = Math.min(val, +max);
$(this).val(val);
$this.val(val);
if ("createEvent" in document) {
var evt = document.createEvent("HTMLEvents");
evt.initEvent("change", false, true);
$(this)[0].dispatchEvent(evt);
$this[0].dispatchEvent(evt);
} else {
$(this)[0].fireEvent("onchange");
$this[0].fireEvent("onchange");
}
e.preventDefault();
@ -43,8 +65,5 @@ $.ready(function () {
});
$._loader = function(vis) {
if(vis)
$('#loader').addClass('show');
else
$('#loader').removeClass('show');
$('#loader').toggleClass('show', vis);
};

@ -0,0 +1,32 @@
(function (nt) {
var sel = '#notif';
var hideTmeo1; // timeout to start hiding (transition)
var hideTmeo2; // timeout to add the hidden class
nt.show = function (message, timeout) {
$(sel).html(message);
Modal.show(sel);
clearTimeout(hideTmeo1);
clearTimeout(hideTmeo2);
if (undef(timeout)) timeout = 2500;
hideTmeo1 = setTimeout(nt.hide, timeout);
};
nt.hide = function () {
var $m = $(sel);
$m.removeClass('visible');
hideTmeo2 = setTimeout(function () {
$m.addClass('hidden');
}, 250); // transition time
};
nt.init = function() {
$(sel).on('click', function() {
nt.hide(this);
});
};
})(window.Notify = {});

@ -135,7 +135,7 @@
H = obj.h;
/* Build screen & show */
var e, cell, scr = qq('#screen');
var e, cell, scr = qs('#screen');
// Empty the screen node
while (scr.firstChild) scr.removeChild(scr.firstChild);
@ -281,7 +281,7 @@
}
});
qa('#buttons button').forEach(function(s) {
qsa('#buttons button').forEach(function(s) {
s.addEventListener('click', function() {
sendBtnMsg(+this.dataset['n']);
});

@ -2,20 +2,16 @@
function mk(e) {return document.createElement(e)}
/** Find one by query */
function qq(s) {return document.querySelector(s)}
function qs(s) {return document.querySelector(s)}
/** Find all by query */
function qa(s) {return document.querySelectorAll(s)}
function qsa(s) {return document.querySelectorAll(s)}
/** Convert any to bool safely */
function bool(x) {
return (x === 1 || x === '1' || x === true || x === 'true');
}
function intval(x) {
return parseInt(x);
}
/** Extend an objects with options */
function extend(defaults, options) {
var target = {};
@ -36,23 +32,23 @@ function rgxe(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}
/** Format number to N decimal places, output as string */
function numfmt(x, places) {
var pow = Math.pow(10, places);
return Math.round(x*pow) / pow;
}
function estimateLoadTime(fs, n) {
return (1000/fs)*n+1500;
}
/** Get millisecond timestamp */
function msNow() {
return +(new Date);
}
/** Get ms elapsed since msNow() */
function msElapsed(start) {
return msNow() - start;
}
/** Shim for log base 10 */
Math.log10 = Math.log10 || function(x) {
return Math.log(x) / Math.LN10;
};
@ -93,15 +89,22 @@ String.prototype.format = function () {
return out;
};
/** HTML escape */
function e(str) {
return String(str)
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
return $.htmlEscape(str);
}
/** Check for undefined */
function undef(x) {
return typeof x == 'undefined';
}
/** Safe json parse */
function jsp() {
try {
return JSON.parse(e);
} catch(e) {
console.error(e);
return null;
}
}

@ -11,9 +11,9 @@
return;
}
resp = JSON.parse(resp);
resp = jsp(resp);
var done = !bool(resp.result.inProgress) && (resp.result.APs.length > 0);
var done = resp && !bool(resp.result.inProgress) && (resp.result.APs.length > 0);
rescan(done ? 15000 : 1000);
if (!done) return; // no redraw yet
@ -29,7 +29,7 @@
resp.result.APs.sort(function (a, b) {
return b.rssi - a.rssi;
}).forEach(function (ap) {
ap.enc = intval(ap.enc);
ap.enc = parseInt(ap.enc);
if (ap.enc > 4) return; // hide unsupported auths
@ -124,55 +124,5 @@
][obj.mode-1]);
};
window.wifiConn = function () {
var xhr = new XMLHttpRequest();
var abortTmeo;
function getStatus() {
xhr.open("GET", 'http://'+_root+"/wifi/connstatus");
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status >= 200 && xhr.status < 300) {
clearTimeout(abortTmeo);
var data = JSON.parse(xhr.responseText);
var done = false;
var msg = '...';
if (data.status == "idle") {
msg = "Preparing to connect";
}
else if (data.status == "success") {
msg = "Connected! Received IP " + data.ip + ".";
done = true;
}
else if (data.status == "working") {
msg = "Connecting to selected AP";
}
else if (data.status == "fail") {
msg = "Connection failed, check your password and try again.";
done = true;
}
$("#status").html(msg);
if (done) {
$('#backbtn').removeClass('hidden');
$('.anim-dots').addClass('hidden');
} else {
window.setTimeout(getStatus, 1000);
}
}
};
abortTmeo = setTimeout(function () {
xhr.abort();
$("#status").html("Telemetry lost, try reconnecting to the AP.");
$('#backbtn').removeClass('hidden');
$('.anim-dots').addClass('hidden');
}, 4000);
xhr.send();
}
getStatus();
};
})();

@ -7,8 +7,9 @@ return [
'menu.cfg_network' => 'Network Configuration',
'menu.cfg_term' => 'Terminal Settings',
'menu.about' => 'About ESPTerm',
'menu.help' => 'Help',
'menu.help' => 'Terminal Help',
'menu.term' => 'Back to Terminal',
'menu.cfg_wifi_conn' => 'Connecting to External Network',
'box.ap' => 'Built-in Access Point',
'box.sta' => 'Connect to External Network',
@ -32,6 +33,15 @@ return [
'wifi.cant_scan_no_sta' => 'Can\'t scan with Client mode disabled.',
'wifi.select_ssid' => 'Available networks:',
'wifi.conn.status' => 'Status:',
'wifi.conn.back_to_config' => 'Back to WiFi config',
'wifi.conn.telemetry_lost' => 'Telemetry lost, something went wrong. Try again...',
'wifi.conn.idle' =>"Preparing to connect",
'wifi.conn.success' => "Connected! Received IP ",
'wifi.conn.working' => "Connecting to selected AP",
'wifi.conn.fail' => "Connection failed, check your password and try again.",
'enabled' => 'Enabled',
'disabled' => 'Disabled',
'yes' => 'Yes',

@ -6,5 +6,4 @@ cat jssrc/chibi.js \
jssrc/utils.js \
jssrc/modal.js \
jssrc/appcommon.js \
jssrc/term.js \
jssrc/wifi.js > js/app.js
jssrc/term.js > js/app.js

@ -4,7 +4,8 @@
<?php
// generate the menu
foreach($_pages as $k => $page) {
if ($page->bodyclass !== 'cfg') continue;
if (strpos($page->bodyclass, 'cfg') === false) continue;
$sel = (CUR_PAGE == $k) ? ' class="selected"' : '';
$text = $page->label;
$url = e(url($k));

@ -7,13 +7,13 @@
<title><?= PAGE_TITLE ?></title>
<link href="/css/app.css" rel="stylesheet">
<script src="/js/app.js"></script>
<script>var _root = <?= json_encode(LIVE_ROOT) ?>;</script>
<script>var _root = <?= JS_WEB_ROOT ?>;</script>
</head>
<body class="<?= BODYCLASS ?>">
<div id="outer">
<?php
$cfg = false;
if ($_pages[CUR_PAGE]->bodyclass == 'cfg') {
if (strpos($_pages[CUR_PAGE]->bodyclass, 'cfg') !== false) {
$cfg = true;
require __DIR__ . '/_cfg_menu.php';
}

@ -1,34 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>About - ESP8266 Remote Terminal</title>
<meta name="viewport" content="width=device-width,shrink-to-fit=no,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0">
<link rel="stylesheet" href="/css/app.css">
<script src="/js/app.js"></script>
</head>
<body class="page-about">
<h1 onclick="location.href='/'">About</h1>
<div class="Box">
<img src="/img/cvut.svg" id="logo" class="mq-tablet-min">
<h2>ESP8266 Remote Terminal</h2>
<img src="/img/cvut.svg" id="logo2" class="mq-phone">
<p>&copy; Ondřej Hruška, 2017 &lt;<a href="mailto:ondra@ondrovo.com">ondra@ondrovo.com</a>&gt;</p>
<p>
&copy; Ondřej Hruška, 2016-2017
&lt;<a href="mailto:ondra@ondrovo.com">ondra@ondrovo.com</a>&gt;
</p>
<p><a href="http://measure.feld.cvut.cz/" target="blank">Katedra měření, FEL ČVUT</a><br>Department of Measurement, FEE CTU</p>
<p>
<a href="http://measure.feld.cvut.cz/" target="blank">Katedra měření, FEL ČVUT</a><br>
Department of Measurement, FEE CTU
</p>
</div>
<div class="Box">
<h2>Firmware</h2>
<h2>Version</h2>
<table>
<tr>
<th>Firmware</th>
<td>v%vers_fw%, build <i>%date%</i> at <i>%time%</i></td>
<th>ESPTerm</th>
<td>v%vers_fw%, build %date% at %time%</td>
</tr>
<tr>
<th>libesphttpd</th>
@ -55,7 +47,14 @@
<div class="Box">
<h2>Contributing</h2>
<p>
Submit your improvements and ideas to the project on <a href="%githubrepo%">GitHub</a>.<br>
Submit your improvements and ideas to the project on
<a href="%githubrepo%">GitHub</a>.
</p>
<p>
You can donate on <a href="https://paypal.me/mightypork">PayPal</a> or
<a href="https://liberapay.com/MightyPork/">LiberaPay</a> to help keep
the project going.
</p>
</div>
@ -66,15 +65,7 @@
<a href="https://github.com/Spritetm/esphttpd">esphttpd</a> library by Jeroen Domburg (Sprite_tm).
</p>
<p>
Using (modified) JS library <a href="https://github.com/kylebarrow/chibi">chibi.js</a> by Kyle Barrow as a lightweight jQuery alternative.
Using (modified) JS library <a href="https://github.com/kylebarrow/chibi">chibi.js</a> by
Kyle Barrow as a lightweight jQuery alternative.
</p>
</div>
<nav id="botnav">
<a href="/">Terminal</a><!--
--><a href="/help">Help</a><!--
--><a href="/wifi">WiFi config</a>
</nav>
</body>
</html>

@ -1,4 +1,4 @@
<form class="Box str mobcol" action="<?= e(url('wifi_set')) ?>" method="POST">
<form class="Box str mobcol" action="<?= e(url('wifi_set')) ?>" method="GET">
<h2><?= tr('box.ap') ?></h2>
<div class="Row buttons mq-phone">
@ -46,7 +46,7 @@
</div>
</form>
<form class="Box str mobcol" action="<?= e(url('wifi_set')) ?>" method="POST">
<form class="Box str mobcol" action="<?= e(url('wifi_set')) ?>" method="GET">
<h2><?= tr('box.sta') ?></h2>
<div class="Row buttons mq-phone">
@ -103,27 +103,6 @@
</div>
<script>
// Magic checkbox
$('.Row.checkbox').forEach(function(x) {
var inp = x.querySelector('input');
var box = x.querySelector('.box');
$(box).toggleClass('checked', inp.value);
$(x).on('click', function() {
inp.value = 1 - inp.value;
$(box).toggleClass('checked', inp.value)
});
});
// Expanding boxes on mobile
$('.Box.mobcol').forEach(function(x) {
var h = x.querySelector('h2');
$(h).on('click', function() {
$(x).toggleClass('expanded');
});
});
// Get XX % for a slider input
function rangePt(inp) {
return Math.round(((inp.value / inp.max)*100)) + '%';
@ -213,7 +192,7 @@
resp.result.APs.sort(function (a, b) {
return b.rssi - a.rssi;
}).forEach(function (ap) {
ap.enc = intval(ap.enc);
ap.enc = parseInt(ap.enc);
if (ap.enc > 4) return; // hide unsupported auths

@ -0,0 +1,53 @@
<h1><?= tr('menu.cfg_wifi_conn') ?></h1>
<div class="Box">
<p><b><?= tr('wifi.conn.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>
</div>
<script>
(function() {
var xhr = new XMLHttpRequest();
var abortTmeo;
var messages = <?= json_encode([
'idle' => tr('wifi.conn.idle'),
'success' => tr('wifi.conn.success'),
'working' => tr('wifi.conn.working'),
'fail' => tr('wifi.conn.fail'),
]) ?>;
function getStatus() {
xhr.open("GET", 'http://'+_root+'<?= url('wifi_connstatus', true) ?>');
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status >= 200 && xhr.status < 300) {
clearTimeout(abortTmeo);
var data = JSON.parse(xhr.responseText);
var done = false;
var msg = messages[data.status] || '...';
if (data.status == 'success') msg += data.ip;
$("#status").html(msg);
if (done) {
// $('#backbtn').removeClass('hidden');
$('.anim-dots').addClass('hidden');
} else {
window.setTimeout(getStatus, 1000);
}
}
};
abortTmeo = setTimeout(function () {
xhr.abort();
$("#status").html(<?= json_encode(tr('wifi.conn.telemetry_lost')) ?>);
// $('#backbtn').removeClass('hidden');
$('.anim-dots').addClass('hidden');
}, 4000);
xhr.send();
}
getStatus();
})();
</script>

@ -1,29 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Help - ESP8266 Remote Terminal</title>
<meta name="viewport" content="width=device-width,shrink-to-fit=no,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0">
<link rel="stylesheet" href="/css/app.css">
<script src="/js/app.js"></script>
</head>
<body class="page-help">
<h1 onclick="location.href='/'">Quick Reference</h1>
<div class="Box">
<h2>Wiring</h2>
<ul>
<li>Communication UART on pins <b>Rx, Tx</b> at 115200-8-1-N</li>
<li>Debug log on pin <b>GPIO2</b> at 115200-8-1-N</li>
<li>Use 3.3V logic, or 5V with protection resistors (470R or more)</li>
<li>If the "LVD" LED on the ESP Term board lights up, the module doesn't get enough power. Check your connections.</li>
<li>Communication UART is on pins <b>Rx, Tx</b> at 115200-8-1-N. The baud rate can be changed in Terminal Settings.
<li>Debug log is on pin <b>GPIO2</b> (P2) at 115200-8-1-N. This baud rate is fixed.
<li>Compatible with 3.3&nbsp;V and 5&nbsp;V logic. For 5&nbsp;V, 470&nbsp;R protection resistors are recommended.
<li>If the "LVD" LED on the ESPTerm module lights up, it doesn't get enough power to run correctly. Check your connections.
<li>Connect Rx and Tx with a piece of wire to test the terminal alone, you should see what you type in the browser.
NOTE: This won't work if your ESP8266 board has a built-in USB-serial (like NodeMCU).</li>
<li>For best performance, use the module in the Client mode. In AP mode, check that the channel used is clear;
interference may cause lag in the terminal.</li>
<i>NOTE: This won't work if your ESP8266 board has a built-in USB-serial converter (like NodeMCU).</i>
<li>For best performance, use the module in Client mode (connected to external network).
<li>In AP mode, check that the channel used is clear; interference may cause a flaky connection.
</ul>
</div>
@ -328,12 +314,3 @@
</tbody>
</table>
</div>
<nav id="botnav">
<a href="/">Terminal</a><!--
--><a href="/wifi">WiFi config</a><!--
--><a href="/about">About</a>
</nav>
</body>
</html>

@ -29,3 +29,8 @@ a:hover {
[onclick] {
cursor: pointer;
}
ul > * {
padding-top: .2em;
padding-bottom: .2em;
}

@ -1,76 +0,0 @@
ul > * {
padding-top: .1em;
padding-bottom: .1em;
}
h1,h2 {
@include noselect();
}
h1 {
text-align: center;
font-size: fsize(6);
margin-top: 0;
margin-bottom: dist(0);
@include media($phone) {
font-size: fsize(3);
margin-bottom: dist(-1);
}
@include media($tablet) {
font-size: fsize(5);
}
}
h2 {
font-size: fsize(2);
margin-bottom: dist(-1);
//&:first-child{margin-top:0}
}
td, th {
padding: dist(-2);
white-space: nowrap;
@include media($phone) {
padding: dist(-3);
}
}
tbody th {
text-align: right;
width: $form-label-w;
color: $c-form-label-fg;
@include media($phone) {
width: auto;
}
}
tbody td {
input[type="text"], input[type="number"] {
width: 10em;
@include media($phone) {
width: 8em;
}
}
}
body {
position: relative;
padding: dist(0);
@include media($phone) {
padding: dist(-1);
}
overflow-y: auto;
& > * {
margin-left: auto;
margin-right: auto;
}
}

@ -1,53 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ESP8266 Remote Terminal</title>
<meta name="viewport" content="width=device-width,shrink-to-fit=no,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0">
<script>
// Workaround for badly loaded page
setTimeout(function() {
if (typeof termInit == 'undefined' || typeof $ == 'undefined') {
location.reload(true);
}
}, 2000);
</script>
<link rel="stylesheet" href="/css/app.css">
<script src="/js/app.js"></script>
</head>
<body class="page-term">
<h1 onclick="location.href='/wifi'">ESP8266 Remote Terminal</h1>
<div id="termwrap">
<div id="screen"></div>
<div id="buttons">
<button data-n="1" class="btn-blue">1</button><!--
--><button data-n="2" class="btn-blue">2</button><!--
--><button data-n="3" class="btn-blue">3</button><!--
--><button data-n="4" class="btn-blue">4</button><!--
--><button data-n="5" class="btn-blue">5</button>
</div>
</div>
<nav id="botnav">
<a href="/wifi">WiFi config</a><!--
--><a href="/help">Help</a><!--
--><a href="/about">About</a>
</nav>
<script>
try {
_root = window.location.host;
termInit(%screenData%);
} catch(e) {
console.error("Fail, reloading...");
location.reload(true);
}
</script>
</body>
</html>

@ -1,17 +0,0 @@
<?php
require '_test_env.php';
$f = file_get_contents('term.html');
$f = str_replace('%screenData%',
'{
"w": 26, "h": 10,
"x": 0, "y": 0,
"cv": 1,
"screen": "70 t259"
}', $f);
$f = str_replace('window.location.host', json_encode(ESP_IP), $f);
echo $f;

@ -1,89 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,shrink-to-fit=no,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0">
<title>WiFi Settings - ESP8266 Remote Terminal</title>
<link rel="stylesheet" href="/css/app.css">
<script src="/js/app.js"></script>
</head>
<body class="page-wifi">
<img src="/img/loader.gif" alt="Loading…" id="loader">
<h1 onclick="location.href='/'">WiFi settings</h1>
<div class="Box" id="wificonfbox">
<table>
<tr>
<th>WiFi mode</th>
<td id="opmodebox">%WiFiMode%</td>
</tr>
<tr class="x-hide-noip x-hide-2">
<th>IP</th>
<td>%StaIP%</td>
</tr>
<tr>
<th>Switch to</th>
<td id="modeswitch"></td>
</tr>
<tr class="x-hide-1">
<th><label for="channel">AP channel</label></th>
<td>
<form action="/wifi/set" method="GET">
<input name="ap_ch" id="channel" type="number" step=1 min=1 max=14 value="%WiFiChannel%"><!--
--><input type="submit" value="Set" class="narrow btn-green x-hide-3">
</form>
</td>
</tr>
<tr class="x-hide-1">
<th><label for="channel">AP name</label></th>
<td>
<form action="/wifi/set" method="GET">
<input name="ap_ssid" type="text" value="%APName%"><!--
--><input type="submit" value="Set" class="narrow btn-green">
</form>
</td>
</tr>
<tr><td colspan=2 style="white-space: normal;">
<p>Some changes require a reboot, dropping connection. It can take a while to re-connect.</p>
<p>
<b>If you lose access</b>, hold the BOOT button for 2 seconds (the Tx LED starts blinking) to re-enable AP mode.
If that fails, hold the BOOT button for over 5 seconds (rapid Tx LED flashing) to perform a factory reset.
<p>
</td></tr>
</table>
</div>
<div class="Box" id="ap-box">
<h2>Select AP to join</h2>
<div id="ap-loader" class="x-hide-2">Scanning<span class="anim-dots">.</span></div>
<div id="ap-noscan" class="x-hide-1 x-hide-3">Can't scan in AP-only mode.</div>
<div id="ap-list" style="display:none"></div>
</div>
<nav id="botnav">
<a href="/">Terminal</a><!--
--><a href="/help">Help</a><!--
--><a href="/about">About</a>
</nav>
<div class="Modal hidden" id="psk-modal">
<div class="Dialog">
<form action="/wifi/connect" method="post" id="conn-form">
<input type="hidden" id="conn-essid" name="essid"><!--
--><label for="conn-passwd">Password:</label><!--
--><input type="password" id="conn-passwd" name="passwd"><!--
--><input type="submit" value="Connect!">
</form>
</div>
</div>
<script>
_root = window.location.host;
wifiInit({staSSID: '%StaSSID%', staIP: '%StaIP%', mode: '%WiFiModeNum%'});
</script>
</body>
</html>

@ -1,26 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,shrink-to-fit=no,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0">
<title>Connecting…</title>
<link rel="stylesheet" href="css/app.css">
<script src="js/app.js"></script>
</head>
<body>
<h1>Connecting to network</h1>
<div class="Box">
<p><b>Status:</b><br><span id="status"></span><span class="anim-dots">.</span></p>
<a href="/wifi" id="backbtn" class="hidden button">Back to WiFi config</a>
</div>
<script>
_root = window.location.host;
wifiConn();
</script>
</body>
</html>

@ -1,15 +0,0 @@
<?php
require '_test_env.php';
$f = file_get_contents('wifi.html');
$f = str_replace('%StaSSID%', 'Chlivek', $f);
$f = str_replace('%StaIP%', json_encode(ESP_IP), $f);
$f = str_replace('%WiFiModeNum%', '1', $f);
$f = str_replace('%WiFiMode%', 'Client', $f);
$f = str_replace('%WiFiChannel%', '1', $f);
$f = str_replace('window.location.host', json_encode(ESP_IP), $f);
echo $f;
Loading…
Cancel
Save