Much better wifi config, added display for IP etc, improved "connecting" page

This commit is contained in:
2017-01-27 00:11:57 +01:00
parent 78c2f9ee52
commit 0f7fb9430c
25 changed files with 965 additions and 220 deletions
+113 -16
View File
@@ -319,6 +319,78 @@ html {
[onclick] {
cursor: pointer; }
.Modal {
position: fixed;
width: 100%;
height: 100%;
left: 0;
top: 0;
right: 0;
bottom: 0;
display: flex;
justify-content: center;
align-items: center;
transition: opacity .5s;
background: rgba(0, 0, 0, 0.65);
opacity: 0; }
.Modal.visible {
opacity: 1; }
.Modal.hidden {
display: none; }
.Dialog {
margin: 0.61805rem;
padding: 1rem;
overflow: hidden;
max-width: 100%;
max-height: 100%;
flex: 0 1 30rem;
background: #1c1c1e;
border-left: 6px solid #2972ba;
border-right: 6px solid #2972ba;
box-shadow: 0 0 2px 0 #434349, 0 0 6px 0 black;
border-radius: 6px; }
.Dialog h1, .Dialog h2 {
margin-top: 0; }
.Dialog p:last-child {
margin-bottom: 0; }
/*
// "toast"
.NotifyMsg {
position: fixed;
bottom: dist(2);
padding: dist(-1) dist(0);
// center horizontally
left: 50%;
@include translate(-50%,0);
// hack to remove blur in chrome
-webkit-font-smoothing: subpixel-antialiased;
-webkit-transform: translateZ(0) scale(1.0, 1.0);
background: #37a349;
&.error {
background: #d03e42;
}
color: white;
text-shadow: 0 0 2px black;
box-shadow: 0 0 6px 0 rgba(black, .6);
border-radius: 5px;
max-width: 80%;
@include media($phone) {
width: calc(100% - 1rem);
}
transition: opacity .5s;
opacity: 0;
&.visible { opacity: 1 }
&.hidden { display: none }
}
*/
html {
font-family: Arial, sans-serif;
color: #D0D0D0;
@@ -353,6 +425,8 @@ a:hover {
.Box {
margin-top: 0.61805rem;
padding: 0.23608rem 0.38198rem; } }
.Box p:first-child {
margin-top: 0; }
body {
position: relative;
@@ -376,6 +450,11 @@ body {
@media screen and (min-width: 545px) and (max-width: 1000px) {
body h1 {
font-size: 1.80203em; } }
body h2 {
font-size: 1.26563em;
margin-bottom: 0.61805rem; }
body h2:first-child {
margin-top: 0; }
body td, body th {
padding: 0.38198rem;
white-space: nowrap; }
@@ -408,7 +487,7 @@ body {
#loader.show {
opacity: 1; }
button, input[type=submit] {
button, input[type=submit], .button {
text-align: center;
cursor: pointer;
display: inline-block;
@@ -428,22 +507,22 @@ button, input[type=submit] {
background-color: #3983cd;
box-shadow: 0 3px 0 #265f98;
text-decoration: none !important; }
button:active, input[type=submit]:active {
button:active, input[type=submit]:active, .button:active {
position: relative;
top: 2px; }
button.narrow, input[type=submit].narrow {
button.narrow, input[type=submit].narrow, .button.narrow {
min-width: initial; }
button, button:link, button:visited, input[type=submit], input[type=submit]:link, input[type=submit]:visited {
button, button:link, button:visited, input[type=submit], input[type=submit]:link, input[type=submit]:visited, .button, .button:link, .button:visited {
color: #FEFEFE; }
button:hover, button:active, button.active, button.selected, input[type=submit]:hover, input[type=submit]:active, input[type=submit].active, input[type=submit].selected {
button:hover, button:active, button.active, button.selected, input[type=submit]:hover, input[type=submit]:active, input[type=submit].active, input[type=submit].selected, .button:hover, .button:active, .button.active, .button.selected {
background-color: #2076C6;
color: #FEFEFE; }
button:hover, button.selected, button.active, input[type=submit]:hover, input[type=submit].selected, input[type=submit].active {
button:hover, button.selected, button.active, input[type=submit]:hover, input[type=submit].selected, input[type=submit].active, .button:hover, .button.selected, .button.active {
box-shadow: 0 3px 0 #154c80; }
button:active, input[type=submit]:active {
button:active, input[type=submit]:active, .button:active {
box-shadow: 0 1px 0 #154c80; }
button, input[type=submit] {
button, input[type=submit], .button {
text-align: center;
cursor: pointer;
display: inline-block;
@@ -463,19 +542,19 @@ button, input[type=submit] {
background-color: #3983cd;
box-shadow: 0 3px 0 #265f98;
text-decoration: none !important; }
button:active, input[type=submit]:active {
button:active, input[type=submit]:active, .button:active {
position: relative;
top: 2px; }
button.narrow, input[type=submit].narrow {
button.narrow, input[type=submit].narrow, .button.narrow {
min-width: initial; }
button, button:link, button:visited, input[type=submit], input[type=submit]:link, input[type=submit]:visited {
button, button:link, button:visited, input[type=submit], input[type=submit]:link, input[type=submit]:visited, .button, .button:link, .button:visited {
color: #FEFEFE; }
button:hover, button:active, button.active, button.selected, input[type=submit]:hover, input[type=submit]:active, input[type=submit].active, input[type=submit].selected {
button:hover, button:active, button.active, button.selected, input[type=submit]:hover, input[type=submit]:active, input[type=submit].active, input[type=submit].selected, .button:hover, .button:active, .button.active, .button.selected {
background-color: #2076C6;
color: #FEFEFE; }
button:hover, button.selected, button.active, input[type=submit]:hover, input[type=submit].selected, input[type=submit].active {
button:hover, button.selected, button.active, input[type=submit]:hover, input[type=submit].selected, input[type=submit].active, .button:hover, .button.selected, .button.active {
box-shadow: 0 3px 0 #154c80; }
button:active, input[type=submit]:active {
button:active, input[type=submit]:active, .button:active {
box-shadow: 0 1px 0 #154c80; }
input[type="number"], input[type="password"], input[type="text"], textarea, select {
@@ -491,6 +570,11 @@ input[type="number"], input[type="password"], input[type="text"], textarea, sele
input[type="number"]:focus, input[type="number"]:hover, input[type="password"]:focus, input[type="password"]:hover, input[type="text"]:focus, input[type="text"]:hover, textarea:focus, textarea:hover, select:focus, select:hover {
border-bottom-color: #2972ba; }
#psk-modal form > *, #wificonfbox form > * {
margin-right: 0.38198rem; }
#psk-modal form > *:last-child, #wificonfbox form > *:last-child {
margin-right: 0; }
#ap-list {
column-count: 3;
column-gap: 0;
@@ -502,15 +586,25 @@ input[type="number"], input[type="password"], input[type="text"], textarea, sele
#ap-list {
column-count: 1; } }
#ap-loader {
#ap-loader, #ap-noscan {
background: rgba(255, 255, 255, 0.1);
border-radius: 5px;
padding: 0.38198rem;
margin-bottom: 0.38198rem; }
#ap-noscan {
font-weight: bold; }
#ap-box {
padding-bottom: 0.38198rem; }
#psk-modal form {
display: flex;
align-items: center;
margin: 0.38198rem; }
#psk-modal form input[type=password] {
min-width: 5rem; }
.AP {
break-inside: avoid-column;
max-width: 500px;
@@ -569,7 +663,10 @@ input[type="number"], input[type="password"], input[type="text"], textarea, sele
white-space: pre;
cursor: pointer; }
.page-term #screen span:hover {
outline: 1px solid rgba(255, 255, 255, 0.5); }
outline: 1px solid rgba(255, 255, 255, 0.4); }
@media screen and (max-width: 1000px) {
.page-term #screen span:hover {
outline: 0 none; } }
.page-term #buttons {
margin-top: 10px;
white-space: nowrap; }
+161 -29
View File
@@ -170,18 +170,35 @@
classarray = classes.split(/\s+/);
nodeLoop(function (elm) {
for (i = 0; i < classarray.length; i += 1) {
search = new RegExp('\\b' + classarray[i] + '\\b', 'g');
replace = new RegExp(' *' + classarray[i] + '\\b', 'g');
var clz = classarray[i];
if (action === 'remove') {
elm.className = elm.className.replace(search, '');
} else if (action === 'toggle') {
elm.className = (elm.className.match(search)) ? elm.className.replace(replace, '') : elm.className + ' ' + classarray[i];
} else if (action === 'has') {
if (elm.className.match(search)) {
elm.classList.remove(clz);
}
else if (action === 'add') {
elm.classList.add(clz);
}
else if (action === 'toggle') {
elm.classList.toggle(clz);
}
else if (action === 'has') {
if (elm.classList.contains(clz)) {
has = true;
break;
}
}
// search = new RegExp('\\b' + classarray[i] + '\\b', 'g');
// replace = new RegExp(' *' + classarray[i] + '\\b', 'g');
// if (action === 'remove') {
// elm.className = elm.className.replace(search, '');
// } else if (action === 'toggle') {
// elm.className = (elm.className.match(search)) ? elm.className.replace(replace, '') : elm.className + ' ' + classarray[i];
// } else if (action === 'has') {
// if (elm.className.match(search)) {
// has = true;
// break;
// }
// }
}
}, nodes);
}
@@ -362,11 +379,12 @@
};
// Add class
cb.addClass = function (classes) {
if (classes) {
nodeLoop(function (elm) {
elm.className += ' ' + classes;
}, nodes);
}
classHelper(classes, 'add', nodes);
// if (classes) {
// nodeLoop(function (elm) {
// elm.className += ' ' + classes;
// }, nodes);
// }
return cb;
};
// Remove class
@@ -777,6 +795,47 @@ String.prototype.format = function () {
return out;
};
/** Module for toggling a modal overlay */
(function () {
var modal = {};
modal.show = function (sel) {
var $m = $(sel);
$m.removeClass('hidden visible');
setTimeout(function () {
$m.addClass('visible');
}, 1);
};
modal.hide = function (sel) {
var $m = $(sel);
$m.removeClass('visible');
setTimeout(function () {
$m.addClass('hidden');
}, 500); // transition time
};
modal.init = function () {
// close modal by click outside the dialog
$('.Modal').on('click', function () {
if ($(this).hasClass('no-close')) return; // this is a no-close modal
modal.hide(this);
});
$('.Dialog').on('click', function (e) {
e.stopImmediatePropagation();
});
// Hide all modals on esc
$(window).on('keydown', function (e) {
if (e.which == 27) {
modal.hide('.Modal');
}
});
};
window.Modal = modal;
})();
/** Global generic init */
$.ready(function () {
// loader dots...
@@ -789,6 +848,7 @@ $.ready(function () {
});
}, 1000);
// flipping number boxes with the mouse wheel
$('input[type=number]').on('mousewheel', function(e) {
var val = +$(this).val();
if (isNaN(val)) val = 1;
@@ -816,10 +876,11 @@ $.ready(function () {
e.preventDefault();
});
Modal.init();
});
$._loader = function(vis) {
console.log("loader fn", vis);
if(vis)
$('#loader').addClass('show');
else
@@ -1202,23 +1263,17 @@ $._loader = function(vis) {
$item.on('click', function () {
var $th = $(this);
var ssid = $th.data('ssid');
var pass = '';
// populate the form
$('#conn-essid').val($th.data('ssid'));
$('#conn-passwd').val(''); // clear
if ($th.data('pwd')) {
// this AP needs a password
pass = prompt("Password for \""+ssid+"\":");
if (pass === null) {
return;
}
Modal.show('#psk-modal');
} else {
Modal.show('#reset-modal');
$('#conn-form').submit();
}
$.post('http://'+_root+'/wifi/connect', null, {
data: {
essid: ssid,
passwd: pass
}
});
});
@@ -1229,7 +1284,7 @@ $._loader = function(vis) {
/** Ask the CGI what APs are visible (async) */
function scanAPs() {
$.get('http://'+_root+'/wifi/scan', onScan);
$.get('/wifi/scan', onScan);
}
function rescan(time) {
@@ -1248,8 +1303,85 @@ $._loader = function(vis) {
// }
//};
curSSID = obj.curSSID;
// Hide what should be hidden in this mode
$('.x-hide-'+obj.mode).addClass('hidden');
obj.mode = +obj.mode;
scanAPs();
// 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/setmode?mode=3">Client+AP</a>&nbsp;<a class="button" href="/wifi/setmode?mode=2">AP only</a>',
'<a class="button" href="/wifi/setmode?mode=3">Client+AP</a>',
'<a class="button" href="/wifi/setmode?mode=1">Client only</a>&nbsp;<a class="button" href="/wifi/setmode?mode=2">AP only</a>'
][obj.mode-1]);
};
window.wifiConn = function () {
var xhr = new XMLHttpRequest();
var abortTmeo;
function getStatus() {
xhr.open("GET", "/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();
};
})();
+38 -15
View File
@@ -15,46 +15,69 @@
<h1 onclick="location.href='/'">WiFi settings</h1>
<div class="Box">
<div class="Box" id="wificonfbox">
<table>
<tr>
<th>WiFi mode:</th>
<td>%WiFiMode%</td>
<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></th>
<td>%WiFiapwarn%</td>
<th>Switch to</th>
<td id="modeswitch"></td>
</tr>
<tr>
<th><label for="channel">AP channel:</label></th>
<tr class="x-hide-1">
<th><label for="channel">AP channel</label></th>
<td>
<form action="/wifi/setchannel" method="GET">
<input name="ch" id="channel" type="number" step=1 min=1 max=14 value="%WiFiChannel%">&nbsp;
<input type="submit" value="Set" class="narrow btn-green">
<input name="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>
<th><label for="channel">AP name:</label></th>
<tr class="x-hide-1">
<th><label for="channel">AP name</label></th>
<td>
<form action="/wifi/setname" method="GET">
<input name="name" type="text" value="%APName%">&nbsp;
<input type="submit" value="Set" class="narrow btn-green">
<input name="name" 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>, connect GPIO0 to GND for 5 seconds to enter Client+AP mode.
If that fails, try the UART factory reset command "<code>\e]FR\a</code>".
<p>
</td></tr>
</table>
</div>
<div class="Box" id="ap-box">
<h2>Select AP to join</h2>
<div id="ap-loader">Scanning<span class="anim-dots">.</span></div>
<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>
<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({curSSID: '%currSsid%'});
wifiInit({staSSID: '%StaSSID%', staIP: '%StaIP%', mode: '%WiFiModeNum%'});
</script>
</body>
</html>
+26
View File
@@ -0,0 +1,26 @@
<!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>