luci/modules/luci-mod-admin-full/luasrc/view/admin_network/wifi_join.htm
Jo-Philipp Wich 9b4efaefa1 luci-mod-admin-full: use incremental background scanning for wireless join
The previous approach of synchroneously scanning while building the result
page was suboptimal since it frequently led to connection resets when
accessing LuCI via wireless.

It also exhibited problems when accessed via SSL on recent Firefox versions
where the page were only loaded partially.

Rework the wireless scanning to gather scan results in a background process
and put them into the ubus session data area where they can be readily
accessed without causing network interruptions.

Subsequently rebuild the wireless join page to use XHR polling to
incrementally fetch updated scan results.

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
2018-07-18 14:43:27 +02:00

224 lines
6.3 KiB
HTML

<%#
Copyright 2009-2015 Jo-Philipp Wich <jow@openwrt.org>
Licensed to the public under the Apache License 2.0.
-%>
<%-
local sys = require "luci.sys"
local utl = require "luci.util"
local dev = luci.http.formvalue("device")
local iw = luci.sys.wifi.getiwinfo(dev)
if not iw then
luci.http.redirect(luci.dispatcher.build_url("admin/network/wireless"))
return
end
-%>
<%+header%>
<script type="text/javascript">//<![CDATA[
var xhr = new XHR(),
poll = null;
function format_signal(bss) {
var qval = bss.quality || 0,
qmax = bss.quality_max || 100,
scale = 100 / qmax * qval,
range = 'none';
if (!bss.bssid || bss.bssid == '00:00:00:00:00:00')
range = 'none';
else if (scale < 15)
range = '0';
else if (scale < 35)
range = '0-25';
else if (scale < 55)
range = '25-50';
else if (scale < 75)
range = '50-75';
else
range = '75-100';
return E('span', {
class: 'ifacebadge',
title: '<%:Signal%>: %d<%:dB%> / <%:Quality%>: %d/%d'.format(bss.signal, qval, qmax)
}, [
E('img', { src: '<%=resource%>/icons/signal-%s.png'.format(range) }),
' %d%%'.format(scale)
]);
}
function format_encryption(bss) {
var enc = bss.encryption || { }
if (enc.wep === true)
return 'WEP';
else if (enc.wpa > 0)
return E('abbr', {
title: 'Pairwise: %h / Group: %h'.format(
enc.pair_ciphers.join(', '),
enc.group_ciphers.join(', '))
},
'%h - %h'.format(
(enc.wpa === 3) ? '<%:mixed WPA/WPA2%>' : (enc.wpa === 2 ? 'WPA2' : 'WPA'),
enc.auth_suites.join(', ')));
else if (enc.enabled)
return '<em><%:unknown%></em>';
else
return '<em><%:open%></em>';
}
function format_actions(bss) {
var enc = bss.encryption || { },
input = [
E('input', { type: 'submit', class: 'cbi-button cbi-button-action important', value: '<%:Join Network%>' }),
E('input', { type: 'hidden', name: 'token', value: '<%=token%>' }),
E('input', { type: 'hidden', name: 'device', value: '<%=dev%>' }),
E('input', { type: 'hidden', name: 'join', value: bss.ssid }),
E('input', { type: 'hidden', name: 'mode', value: bss.mode }),
E('input', { type: 'hidden', name: 'bssid', value: bss.bssid }),
E('input', { type: 'hidden', name: 'channel', value: bss.channel }),
E('input', { type: 'hidden', name: 'clbridge', value: <%=iw.type == "wl" and 1 or 0%> }),
E('input', { type: 'hidden', name: 'wep', value: enc.wep ? 1 : 0 })
];
if (enc.wpa) {
input.push(E('input', { type: 'hidden', name: 'wpa_version', value: enc.wpa }));
enc.auth_suites.forEach(function(s) {
input.push(E('input', { type: 'hidden', name: 'wpa_suites', value: s }));
});
enc.group_ciphers.forEach(function(s) {
input.push(E('input', { type: 'hidden', name: 'wpa_group', value: s }));
});
enc.pair_ciphers.forEach(function(s) {
input.push(E('input', { type: 'hidden', name: 'wpa_pairwise', value: s }));
});
}
return E('form', {
class: 'inline',
method: 'post',
action: '<%=url("admin/network/wireless_join")%>'
}, input);
}
function fade(bss, content) {
if (bss.stale)
return E('span', { style: 'opacity:0.5' }, content);
else
return content;
}
function flush() {
XHR.stop(poll);
XHR.halt();
scan();
}
function scan() {
var tbl = document.getElementById('scan_results');
cbi_update_table(tbl, [], '<em><img src="<%=resource%>/icons/loading.gif" class="middle" /> <%:Starting wireless scan...%></em>');
xhr.post('<%=url("admin/network/wireless_scan_trigger", dev)%>', { token: '<%=token%>' },
function(s) {
if (s.status !== 200) {
cbi_update_table(tbl, [], '<em><%:Scan request failed%></em>');
return;
}
var count = 0;
poll = XHR.poll(3, '<%=url("admin/network/wireless_scan_results", dev)%>', null,
function(s, results) {
if (Array.isArray(results)) {
var bss = [];
results.sort(function(a, b) {
var diff = (b.quality - a.quality) || (a.channel - b.channel);
if (diff)
return diff;
if (a.ssid < b.ssid)
return -1;
else if (a.ssid > b.ssid)
return 1;
if (a.bssid < b.bssid)
return -1;
else if (a.bssid > b.bssid)
return 1;
}).forEach(function(res) {
bss.push([
fade(res, format_signal(res)),
fade(res, res.ssid ? '%h'.format(res.ssid) : E('em', {}, '<%:hidden%>')),
fade(res, res.channel),
fade(res, res.mode),
fade(res, res.bssid),
fade(res, format_encryption(res)),
format_actions(res)
]);
});
cbi_update_table(tbl, bss, '<em><img src="<%=resource%>/icons/loading.gif" class="middle" /> <%:No scan results available yet...%>');
}
if (count++ >= 3) {
count = 0;
xhr.post('<%=url("admin/network/wireless_scan_trigger", dev, "1")%>',
{ token: '<%=token%>' }, function() { });
}
});
XHR.run();
});
}
document.addEventListener('DOMContentLoaded', scan);
//]]></script>
<h2 name="content"><%:Join Network: Wireless Scan%></h2>
<div class="cbi-map">
<div class="cbi-section">
<div class="table" id="scan_results">
<div class="tr table-titles">
<div class="th col-1 middle center"><%:Signal%></div>
<div class="th col-5 middle left"><%:SSID%></div>
<div class="th col-2 middle center"><%:Channel%></div>
<div class="th col-2 middle left"><%:Mode%></div>
<div class="th col-3 middle left"><%:BSSID%></div>
<div class="th col-2 middle left"><%:Encryption%></div>
<div class="th cbi-section-actions">&#160;</div>
</div>
<div class="tr placeholder">
<div class="td">
<img src="<%=resource%>/icons/loading.gif" class="middle" />
<em><%:Collecting data...%></em>
</div>
</div>
</div>
</div>
</div>
<div class="cbi-page-actions right">
<form class="inline" action="<%=url("admin/network/wireless")%>" method="get">
<input class="cbi-button cbi-button-neutral" type="submit" value="<%:Back to overview%>" />
</form>
<form class="inline" action="<%=url('admin/network/wireless_join')%>" method="post">
<input type="hidden" name="token" value="<%=token%>" />
<input type="hidden" name="device" value="<%=utl.pcdata(dev)%>" />
<input type="button" class="cbi-button cbi-button-action" value="<%:Repeat scan%>" onclick="flush()" />
</form>
</div>
<%+footer%>