diff --git a/build_web.sh b/build_web.sh index 734fd12..b86d192 100755 --- a/build_web.sh +++ b/build_web.sh @@ -6,6 +6,7 @@ echo "-- Preparing WWW files --" DD=html_orig/jssrc cat $DD/chibi.js \ $DD/utils.js \ + $DD/modal.js \ $DD/appcommon.js \ $DD/term.js \ $DD/wifi.js > html/js/app.js @@ -14,5 +15,6 @@ cat $DD/chibi.js \ cp html_orig/css/app.css html/css/app.css cp html_orig/term.html html/term.tpl cp html_orig/wifi.html html/wifi.tpl +cp html_orig/wifi_conn.html html/wifi_conn.tpl cp html_orig/img/loader.gif html/img/loader.gif cp html_orig/favicon.ico html/favicon.ico diff --git a/html/css/app.css b/html/css/app.css index 11d0084..5e0927c 100644 --- a/html/css/app.css +++ b/html/css/app.css @@ -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; } diff --git a/html/js/app.js b/html/js/app.js index fb910e2..aae60c2 100644 --- a/html/js/app.js +++ b/html/js/app.js @@ -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; + + // 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([ + 'Client+AP AP only', + 'Client+AP', + 'Client only AP only' + ][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(); + } - scanAPs(); + getStatus(); }; })(); diff --git a/html/wifi.tpl b/html/wifi.tpl index 60a4669..6cc9cef 100644 --- a/html/wifi.tpl +++ b/html/wifi.tpl @@ -15,46 +15,69 @@
WiFi mode: | -%WiFiMode% | +WiFi mode | +%WiFiMode% |
---|---|---|---|
- | %WiFiapwarn% | +||
+ | Switch to | ++ | |
+ | |||
+ Some changes require a reboot, dropping connection. It can take a while to re-connect. +
+ If you lose access, connect GPIO0 to GND for 5 seconds to enter Client+AP mode.
+ If that fails, try the UART factory reset command " + |