1
0
Fork 0
mirror of https://github.com/Ysurac/openmptcprouter-feeds.git synced 2025-03-09 15:40:03 +00:00

Update luci and theme

This commit is contained in:
Ycarus 2019-05-28 21:51:29 +02:00
parent fe03553aae
commit 4d7962337f
165 changed files with 74180 additions and 13802 deletions

View file

@ -14,13 +14,17 @@ LUCI_BASENAME:=base
LUCI_TITLE:=LuCI core libraries
LUCI_DEPENDS:=+lua +luci-lib-nixio +luci-lib-ip +rpcd +libubus-lua +luci-lib-jsonc +liblucihttp-lua
LUCI_LUASRCDIET_VERSION:=1.0.0
PKG_SOURCE:=v1.0.0.tar.gz
PKG_SOURCE_URL:=https://github.com/jirutka/luasrcdiet/archive/
PKG_HASH:=48162e63e77d009f5848f18a5cabffbdfc867d0e5e73c6d407f6af5d6880151b
PKG_SOURCE_URL:=https://github.com/jirutka/luasrcdiet.git
PKG_SOURCE_VERSION:=f138fc9359821d9201cd6b57cfa2fcbed5b9af97
PKG_SOURCE_SUBDIR:=luasrcdiet-$(LUCI_LUASRCDIET_VERSION)
PKG_SOURCE_PROTO:=git
PKG_SOURCE:=$(PKG_SOURCE_SUBDIR).tar.gz
PKG_MIRROR_HASH:=a5c9d098549fbef618e6022b701e66c8c6fb16c910e63219adad3a4e71341f72
PKG_LICENSE:=MIT
HOST_BUILD_DIR:=$(BUILD_DIR_HOST)/luasrcdiet-1.0.0
HOST_BUILD_DIR:=$(BUILD_DIR_HOST)/$(PKG_SOURCE_SUBDIR)
include $(INCLUDE_DIR)/host-build.mk
@ -36,13 +40,14 @@ define Host/Configure
endef
define Host/Compile
$(MAKE) -C src/ clean po2lmo
$(MAKE) -C src/ clean po2lmo jsmin
endef
define Host/Install
$(INSTALL_DIR) $(1)/bin
$(INSTALL_DIR) $(1)/lib/lua/5.1
$(INSTALL_BIN) src/po2lmo $(1)/bin/po2lmo
$(INSTALL_BIN) src/jsmin $(1)/bin/jsmin
$(INSTALL_BIN) $(HOST_BUILD_DIR)/bin/luasrcdiet $(1)/bin/luasrcdiet
$(CP) $(HOST_BUILD_DIR)/luasrcdiet $(1)/lib/lua/5.1/
endef

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,511 @@
(function(window, document, undefined) {
var modalDiv = null,
tooltipDiv = null,
tooltipTimeout = null,
dummyElem = null,
domParser = null;
LuCI.prototype = {
/* URL construction helpers */
path: function(prefix, parts) {
var url = [ prefix || '' ];
for (var i = 0; i < parts.length; i++)
if (/^(?:[a-zA-Z0-9_.%,;-]+\/)*[a-zA-Z0-9_.%,;-]+$/.test(parts[i]))
url.push('/', parts[i]);
if (url.length === 1)
url.push('/');
return url.join('');
},
url: function() {
return this.path(this.env.scriptname, arguments);
},
resource: function() {
return this.path(this.env.resource, arguments);
},
location: function() {
return this.path(this.env.scriptname, this.env.requestpath);
},
/* HTTP resource fetching */
get: function(url, args, cb) {
return this.poll(0, url, args, cb, false);
},
post: function(url, args, cb) {
return this.poll(0, url, args, cb, true);
},
poll: function(interval, url, args, cb, post) {
var data = post ? { token: this.env.token } : null;
if (!/^(?:\/|\S+:\/\/)/.test(url))
url = this.url(url);
if (typeof(args) === 'object' && args !== null) {
data = data || {};
for (var key in args)
if (args.hasOwnProperty(key))
switch (typeof(args[key])) {
case 'string':
case 'number':
case 'boolean':
data[key] = args[key];
break;
case 'object':
data[key] = JSON.stringify(args[key]);
break;
}
}
if (interval > 0)
return XHR.poll(interval, url, data, cb, post);
else if (post)
return XHR.post(url, data, cb);
else
return XHR.get(url, data, cb);
},
stop: function(entry) { XHR.stop(entry) },
halt: function() { XHR.halt() },
run: function() { XHR.run() },
/* Modal dialog */
showModal: function(title, children) {
var dlg = modalDiv.firstElementChild;
dlg.setAttribute('class', 'modal');
this.dom.content(dlg, this.dom.create('h4', {}, title));
this.dom.append(dlg, children);
document.body.classList.add('modal-overlay-active');
return dlg;
},
hideModal: function() {
document.body.classList.remove('modal-overlay-active');
},
/* Tooltip */
showTooltip: function(ev) {
var target = findParent(ev.target, '[data-tooltip]');
if (!target)
return;
if (tooltipTimeout !== null) {
window.clearTimeout(tooltipTimeout);
tooltipTimeout = null;
}
var rect = target.getBoundingClientRect(),
x = rect.left + window.pageXOffset,
y = rect.top + rect.height + window.pageYOffset;
tooltipDiv.className = 'cbi-tooltip';
tooltipDiv.innerHTML = '▲ ';
tooltipDiv.firstChild.data += target.getAttribute('data-tooltip');
if (target.hasAttribute('data-tooltip-style'))
tooltipDiv.classList.add(target.getAttribute('data-tooltip-style'));
if ((y + tooltipDiv.offsetHeight) > (window.innerHeight + window.pageYOffset)) {
y -= (tooltipDiv.offsetHeight + target.offsetHeight);
tooltipDiv.firstChild.data = '▼ ' + tooltipDiv.firstChild.data.substr(2);
}
tooltipDiv.style.top = y + 'px';
tooltipDiv.style.left = x + 'px';
tooltipDiv.style.opacity = 1;
tooltipDiv.dispatchEvent(new CustomEvent('tooltip-open', {
bubbles: true,
detail: { target: target }
}));
},
hideTooltip: function(ev) {
if (ev.target === tooltipDiv || ev.relatedTarget === tooltipDiv ||
tooltipDiv.contains(ev.target) || tooltipDiv.contains(ev.relatedTarget))
return;
if (tooltipTimeout !== null) {
window.clearTimeout(tooltipTimeout);
tooltipTimeout = null;
}
tooltipDiv.style.opacity = 0;
tooltipTimeout = window.setTimeout(function() { tooltipDiv.removeAttribute('style'); }, 250);
tooltipDiv.dispatchEvent(new CustomEvent('tooltip-close', { bubbles: true }));
},
/* Widget helper */
itemlist: function(node, items, separators) {
var children = [];
if (!Array.isArray(separators))
separators = [ separators || E('br') ];
for (var i = 0; i < items.length; i += 2) {
if (items[i+1] !== null && items[i+1] !== undefined) {
var sep = separators[(i/2) % separators.length],
cld = [];
children.push(E('span', { class: 'nowrap' }, [
items[i] ? E('strong', items[i] + ': ') : '',
items[i+1]
]));
if ((i+2) < items.length)
children.push(this.dom.elem(sep) ? sep.cloneNode(true) : sep);
}
}
this.dom.content(node, children);
return node;
}
};
/* Tabs */
LuCI.prototype.tabs = {
init: function() {
var groups = [], prevGroup = null, currGroup = null;
document.querySelectorAll('[data-tab]').forEach(function(tab) {
var parent = tab.parentNode;
if (!parent.hasAttribute('data-tab-group'))
parent.setAttribute('data-tab-group', groups.length);
currGroup = +parent.getAttribute('data-tab-group');
if (currGroup !== prevGroup) {
prevGroup = currGroup;
if (!groups[currGroup])
groups[currGroup] = [];
}
groups[currGroup].push(tab);
});
for (var i = 0; i < groups.length; i++)
this.initTabGroup(groups[i]);
document.addEventListener('dependency-update', this.updateTabs.bind(this));
this.updateTabs();
if (!groups.length)
this.setActiveTabId(-1, -1);
},
initTabGroup: function(panes) {
if (!Array.isArray(panes) || panes.length === 0)
return;
var menu = E('ul', { 'class': 'cbi-tabmenu' }),
group = panes[0].parentNode,
groupId = +group.getAttribute('data-tab-group'),
selected = null;
for (var i = 0, pane; pane = panes[i]; i++) {
var name = pane.getAttribute('data-tab'),
title = pane.getAttribute('data-tab-title'),
active = pane.getAttribute('data-tab-active') === 'true';
menu.appendChild(E('li', {
'class': active ? 'cbi-tab' : 'cbi-tab-disabled',
'data-tab': name
}, E('a', {
'href': '#',
'click': this.switchTab.bind(this)
}, title)));
if (active)
selected = i;
}
group.parentNode.insertBefore(menu, group);
if (selected === null) {
selected = this.getActiveTabId(groupId);
if (selected < 0 || selected >= panes.length)
selected = 0;
menu.childNodes[selected].classList.add('cbi-tab');
menu.childNodes[selected].classList.remove('cbi-tab-disabled');
panes[selected].setAttribute('data-tab-active', 'true');
this.setActiveTabId(groupId, selected);
}
},
getActiveTabState: function() {
var page = document.body.getAttribute('data-page');
try {
var val = JSON.parse(window.sessionStorage.getItem('tab'));
if (val.page === page && Array.isArray(val.groups))
return val;
}
catch(e) {}
window.sessionStorage.removeItem('tab');
return { page: page, groups: [] };
},
getActiveTabId: function(groupId) {
return +this.getActiveTabState().groups[groupId] || 0;
},
setActiveTabId: function(groupId, tabIndex) {
try {
var state = this.getActiveTabState();
state.groups[groupId] = tabIndex;
window.sessionStorage.setItem('tab', JSON.stringify(state));
}
catch (e) { return false; }
return true;
},
updateTabs: function(ev) {
document.querySelectorAll('[data-tab-title]').forEach(function(pane) {
var menu = pane.parentNode.previousElementSibling,
tab = menu.querySelector('[data-tab="%s"]'.format(pane.getAttribute('data-tab'))),
n_errors = pane.querySelectorAll('.cbi-input-invalid').length;
if (!pane.firstElementChild) {
tab.style.display = 'none';
tab.classList.remove('flash');
}
else if (tab.style.display === 'none') {
tab.style.display = '';
requestAnimationFrame(function() { tab.classList.add('flash') });
}
if (n_errors) {
tab.setAttribute('data-errors', n_errors);
tab.setAttribute('data-tooltip', _('%d invalid field(s)').format(n_errors));
tab.setAttribute('data-tooltip-style', 'error');
}
else {
tab.removeAttribute('data-errors');
tab.removeAttribute('data-tooltip');
}
});
},
switchTab: function(ev) {
var tab = ev.target.parentNode,
name = tab.getAttribute('data-tab'),
menu = tab.parentNode,
group = menu.nextElementSibling,
groupId = +group.getAttribute('data-tab-group'),
index = 0;
ev.preventDefault();
if (!tab.classList.contains('cbi-tab-disabled'))
return;
menu.querySelectorAll('[data-tab]').forEach(function(tab) {
tab.classList.remove('cbi-tab');
tab.classList.remove('cbi-tab-disabled');
tab.classList.add(
tab.getAttribute('data-tab') === name ? 'cbi-tab' : 'cbi-tab-disabled');
});
group.childNodes.forEach(function(pane) {
if (L.dom.matches(pane, '[data-tab]')) {
if (pane.getAttribute('data-tab') === name) {
pane.setAttribute('data-tab-active', 'true');
L.tabs.setActiveTabId(groupId, index);
}
else {
pane.setAttribute('data-tab-active', 'false');
}
index++;
}
});
}
};
/* DOM manipulation */
LuCI.prototype.dom = {
elem: function(e) {
return (typeof(e) === 'object' && e !== null && 'nodeType' in e);
},
parse: function(s) {
var elem;
try {
domParser = domParser || new DOMParser();
elem = domParser.parseFromString(s, 'text/html').body.firstChild;
}
catch(e) {}
if (!elem) {
try {
dummyElem = dummyElem || document.createElement('div');
dummyElem.innerHTML = s;
elem = dummyElem.firstChild;
}
catch (e) {}
}
return elem || null;
},
matches: function(node, selector) {
var m = this.elem(node) ? node.matches || node.msMatchesSelector : null;
return m ? m.call(node, selector) : false;
},
parent: function(node, selector) {
if (this.elem(node) && node.closest)
return node.closest(selector);
while (this.elem(node))
if (this.matches(node, selector))
return node;
else
node = node.parentNode;
return null;
},
append: function(node, children) {
if (!this.elem(node))
return null;
if (Array.isArray(children)) {
for (var i = 0; i < children.length; i++)
if (this.elem(children[i]))
node.appendChild(children[i]);
else if (children !== null && children !== undefined)
node.appendChild(document.createTextNode('' + children[i]));
return node.lastChild;
}
else if (typeof(children) === 'function') {
return this.append(node, children(node));
}
else if (this.elem(children)) {
return node.appendChild(children);
}
else if (children !== null && children !== undefined) {
node.innerHTML = '' + children;
return node.lastChild;
}
return null;
},
content: function(node, children) {
if (!this.elem(node))
return null;
while (node.firstChild)
node.removeChild(node.firstChild);
return this.append(node, children);
},
attr: function(node, key, val) {
if (!this.elem(node))
return null;
var attr = null;
if (typeof(key) === 'object' && key !== null)
attr = key;
else if (typeof(key) === 'string')
attr = {}, attr[key] = val;
for (key in attr) {
if (!attr.hasOwnProperty(key) || attr[key] === null || attr[key] === undefined)
continue;
switch (typeof(attr[key])) {
case 'function':
node.addEventListener(key, attr[key]);
break;
case 'object':
node.setAttribute(key, JSON.stringify(attr[key]));
break;
default:
node.setAttribute(key, attr[key]);
}
}
},
create: function() {
var html = arguments[0],
attr = (arguments[1] instanceof Object && !Array.isArray(arguments[1])) ? arguments[1] : null,
data = attr ? arguments[2] : arguments[1],
elem;
if (this.elem(html))
elem = html;
else if (html.charCodeAt(0) === 60)
elem = this.parse(html);
else
elem = document.createElement(html);
if (!elem)
return null;
this.attr(elem, attr);
this.append(elem, data);
return elem;
}
};
/* Setup */
LuCI.prototype.setupDOM = function(ev) {
this.tabs.init();
};
function LuCI(env) {
this.env = env;
modalDiv = document.body.appendChild(
this.dom.create('div', { id: 'modal_overlay' },
this.dom.create('div', { class: 'modal', role: 'dialog', 'aria-modal': true })));
tooltipDiv = document.body.appendChild(this.dom.create('div', { class: 'cbi-tooltip' }));
document.addEventListener('mouseover', this.showTooltip.bind(this), true);
document.addEventListener('mouseout', this.hideTooltip.bind(this), true);
document.addEventListener('focus', this.showTooltip.bind(this), true);
document.addEventListener('blur', this.hideTooltip.bind(this), true);
document.addEventListener('DOMContentLoaded', this.setupDOM.bind(this));
}
window.LuCI = LuCI;
})(window, document);

View file

@ -1,24 +1,65 @@
/*
* xhr.js - XMLHttpRequest helper class
* (c) 2008-2010 Jo-Philipp Wich
* (c) 2008-2018 Jo-Philipp Wich <jo@mein.io>
*/
XHR = function()
{
this.reinit = function()
{
if (window.XMLHttpRequest) {
this._xmlHttp = new XMLHttpRequest();
}
else if (window.ActiveXObject) {
this._xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
else {
alert("xhr.js: XMLHttpRequest is not supported by this browser!");
}
}
XHR.prototype = {
_encode: function(obj) {
obj = obj ? obj : { };
obj['_'] = Math.random();
this.busy = function() {
if (typeof obj == 'object') {
var code = '';
var self = this;
for (var k in obj)
code += (code ? '&' : '') +
k + '=' + encodeURIComponent(obj[k]);
return code;
}
return obj;
},
_response: function(callback, ts) {
if (this._xmlHttp.readyState !== 4)
return;
var status = this._xmlHttp.status,
login = this._xmlHttp.getResponseHeader("X-LuCI-Login-Required"),
type = this._xmlHttp.getResponseHeader("Content-Type"),
json = null;
if (status === 403 && login === 'yes') {
XHR.halt();
showModal(_('Session expired'), [
E('div', { class: 'alert-message warning' },
_('A new login is required since the authentication session expired.')),
E('div', { class: 'right' },
E('div', {
class: 'btn primary',
click: function() {
var loc = window.location;
window.location = loc.protocol + '//' + loc.host + loc.pathname + loc.search;
}
}, _('To login…')))
]);
}
else if (type && type.toLowerCase().match(/^application\/json\b/)) {
try {
json = JSON.parse(this._xmlHttp.responseText);
}
catch(e) {
json = null;
}
}
callback(this._xmlHttp, json, Date.now() - ts);
},
busy: function() {
if (!this._xmlHttp)
return false;
@ -32,20 +73,18 @@ XHR = function()
default:
return false;
}
}
},
this.abort = function() {
abort: function() {
if (this.busy())
this._xmlHttp.abort();
}
},
this.get = function(url,data,callback,timeout)
{
this.reinit();
get: function(url, data, callback, timeout) {
this._xmlHttp = new XMLHttpRequest();
var ts = Date.now();
var xhr = this._xmlHttp;
var code = this._encode(data);
var xhr = this._xmlHttp,
code = this._encode(data);
url = location.protocol + '//' + location.host + url;
@ -60,83 +99,51 @@ XHR = function()
if (!isNaN(timeout))
xhr.timeout = timeout;
xhr.onreadystatechange = function()
{
if (xhr.readyState == 4) {
var json = null;
if (xhr.getResponseHeader("Content-Type") == "application/json") {
try { json = JSON.parse(xhr.responseText); }
catch(e) { json = null; }
}
callback(xhr, json, Date.now() - ts);
}
}
xhr.onreadystatechange = this._response.bind(this, callback, Date.now());
xhr.send(null);
}
},
this.post = function(url,data,callback,timeout)
{
this.reinit();
post: function(url, data, callback, timeout) {
this._xmlHttp = new XMLHttpRequest();
var ts = Date.now();
var xhr = this._xmlHttp;
var code = this._encode(data);
xhr.onreadystatechange = function()
{
if (xhr.readyState == 4) {
var json = null;
if (xhr.getResponseHeader("Content-Type") == "application/json") {
try { json = JSON.parse(xhr.responseText); }
catch(e) { json = null; }
}
callback(xhr, json, Date.now() - ts);
}
}
var xhr = this._xmlHttp,
code = this._encode(data);
xhr.open('POST', url, true);
if (!isNaN(timeout))
xhr.timeout = timeout;
xhr.onreadystatechange = this._response.bind(this, callback, Date.now());
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send(code);
}
},
this.cancel = function()
{
this._xmlHttp.onreadystatechange = function(){};
cancel: function() {
this._xmlHttp.onreadystatechange = function() {};
this._xmlHttp.abort();
}
},
this.send_form = function(form,callback,extra_values)
{
send_form: function(form, callback, extra_values) {
var code = '';
for (var i = 0; i < form.elements.length; i++)
{
for (var i = 0; i < form.elements.length; i++) {
var e = form.elements[i];
if (e.options)
{
if (e.options) {
code += (code ? '&' : '') +
form.elements[i].name + '=' + encodeURIComponent(
e.options[e.selectedIndex].value
);
}
else if (e.length)
{
else if (e.length) {
for (var j = 0; j < e.length; j++)
if (e[j].name) {
code += (code ? '&' : '') +
e[j].name + '=' + encodeURIComponent(e[j].value);
}
}
else
{
else {
code += (code ? '&' : '') +
e.name + '=' + encodeURIComponent(e.value);
}
@ -147,46 +154,25 @@ XHR = function()
code += (code ? '&' : '') +
key + '=' + encodeURIComponent(extra_values[key]);
return(
(form.method == 'get')
? this.get(form.getAttribute('action'), code, callback)
: this.post(form.getAttribute('action'), code, callback)
);
}
this._encode = function(obj)
{
obj = obj ? obj : { };
obj['_'] = Math.random();
if (typeof obj == 'object')
{
var code = '';
var self = this;
for (var k in obj)
code += (code ? '&' : '') +
k + '=' + encodeURIComponent(obj[k]);
return code;
}
return obj;
return (form.method == 'get'
? this.get(form.getAttribute('action'), code, callback)
: this.post(form.getAttribute('action'), code, callback));
}
}
XHR.get = function(url, data, callback)
{
XHR.get = function(url, data, callback) {
(new XHR()).get(url, data, callback);
}
XHR.poll = function(interval, url, data, callback, post)
{
if (isNaN(interval) || interval < 1)
interval = 5;
XHR.post = function(url, data, callback) {
(new XHR()).post(url, data, callback);
}
if (!XHR._q)
{
XHR.poll = function(interval, url, data, callback, post) {
if (isNaN(interval) || interval <= 0)
interval = L.env.pollinterval;
if (!XHR._q) {
XHR._t = 0;
XHR._q = [ ];
XHR._r = function() {
@ -213,8 +199,7 @@ XHR.poll = function(interval, url, data, callback, post)
return e;
}
XHR.stop = function(e)
{
XHR.stop = function(e) {
for (var i = 0; XHR._q && XHR._q[i]; i++) {
if (XHR._q[i] === e) {
e.xhr.cancel();
@ -226,10 +211,8 @@ XHR.stop = function(e)
return false;
}
XHR.halt = function()
{
if (XHR._i)
{
XHR.halt = function() {
if (XHR._i) {
/* show & set poll indicator */
try {
document.getElementById('xhr_poll_status').style.display = '';
@ -242,10 +225,8 @@ XHR.halt = function()
}
}
XHR.run = function()
{
if (XHR._r && !XHR._i)
{
XHR.run = function() {
if (XHR._r && !XHR._i) {
/* show & set poll indicator */
try {
document.getElementById('xhr_poll_status').style.display = '';
@ -260,9 +241,10 @@ XHR.run = function()
}
}
XHR.running = function()
{
XHR.running = function() {
return !!(XHR._r && XHR._i);
}
function XHR() {}
document.addEventListener('DOMContentLoaded', XHR.run);

View file

@ -1199,19 +1199,20 @@ function TypedSection.parse(self, novld)
if name then
-- Ignore if it already exists
if self:cfgvalue(name) then
name = nil;
end
name = self:checkscope(name)
if not name then
name = nil
self.err_invalid = true
end
else
name = self:checkscope(name)
if name and #name > 0 then
created = self:create(name, origin) and name
if not created then
self.invalid_cts = true
if not name then
self.err_invalid = true
end
if name and #name > 0 then
created = self:create(name, origin) and name
if not created then
self.invalid_cts = true
end
end
end
end

View file

@ -132,6 +132,10 @@ function ip6prefix(val)
return ( val and val >= 0 and val <= 128 )
end
function cidr(val)
return cidr4(val) or cidr6(val)
end
function cidr4(val)
local ip, mask = val:match("^([^/]+)/([^/]+)$")
@ -460,3 +464,7 @@ function dateyyyymmdd(val)
end
return false
end
function unique(val)
return true
end

View file

@ -0,0 +1,164 @@
-- Copyright 2008 Steven Barth <steven@midlink.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.controller.admin.index", package.seeall)
function index()
function toplevel_page(page, preflookup, preftarget)
if preflookup and preftarget then
if lookup(preflookup) then
page.target = preftarget
end
end
if not page.target then
page.target = firstchild()
end
end
local uci = require("luci.model.uci").cursor()
local root = node()
if not root.target then
root.target = alias("admin")
root.index = true
end
local page = node("admin")
page.title = _("Administration")
page.order = 10
page.sysauth = "root"
page.sysauth_authenticator = "htmlauth"
page.ucidata = true
page.index = true
page.target = firstnode()
-- Empty menu tree to be populated by addons and modules
page = node("admin", "status")
page.title = _("Status")
page.order = 10
page.index = true
-- overview is from mod-admin-full
toplevel_page(page, "admin/status/overview", alias("admin", "status", "overview"))
page = node("admin", "system")
page.title = _("System")
page.order = 20
page.index = true
-- system/system is from mod-admin-full
toplevel_page(page, "admin/system/system", alias("admin", "system", "system"))
-- Only used if applications add items
page = node("admin", "services")
page.title = _("Services")
page.order = 40
page.index = true
toplevel_page(page, false, false)
-- Even for mod-admin-full network just uses first submenu item as landing
page = node("admin", "network")
page.title = _("Network")
page.order = 50
page.index = true
toplevel_page(page, false, false)
if nixio.fs.access("/etc/config/dhcp") then
page = entry({"admin", "dhcplease_status"}, call("lease_status"), nil)
page.leaf = true
end
local has_wifi = false
uci:foreach("wireless", "wifi-device",
function(s)
has_wifi = true
return false
end)
if has_wifi then
page = entry({"admin", "wireless_assoclist"}, call("wifi_assoclist"), nil)
page.leaf = true
page = entry({"admin", "wireless_deauth"}, post("wifi_deauth"), nil)
page.leaf = true
end
page = entry({"admin", "translations"}, call("action_translations"), nil)
page.leaf = true
-- Logout is last
entry({"admin", "logout"}, call("action_logout"), _("Logout"), 999)
end
function action_logout()
local dsp = require "luci.dispatcher"
local utl = require "luci.util"
local sid = dsp.context.authsession
if sid then
utl.ubus("session", "destroy", { ubus_rpc_session = sid })
luci.http.header("Set-Cookie", "sysauth=%s; expires=%s; path=%s" %{
'', 'Thu, 01 Jan 1970 01:00:00 GMT', dsp.build_url()
})
end
luci.http.redirect(dsp.build_url())
end
function action_translations(lang)
local i18n = require "luci.i18n"
local http = require "luci.http"
local fs = require "nixio".fs
if lang and #lang > 0 then
lang = i18n.setlanguage(lang)
if lang then
local s = fs.stat("%s/base.%s.lmo" %{ i18n.i18ndir, lang })
if s then
http.header("Cache-Control", "public, max-age=31536000")
http.header("ETag", "%x-%x-%x" %{ s["ino"], s["size"], s["mtime"] })
end
end
end
http.prepare_content("application/javascript; charset=utf-8")
http.write("window.TR=")
http.write_json(i18n.dump())
end
function lease_status()
local s = require "luci.tools.status"
luci.http.prepare_content("application/json")
luci.http.write('[')
luci.http.write_json(s.dhcp_leases())
luci.http.write(',')
luci.http.write_json(s.dhcp6_leases())
luci.http.write(']')
end
function wifi_assoclist()
local s = require "luci.tools.status"
luci.http.prepare_content("application/json")
luci.http.write_json(s.wifi_assoclist())
end
function wifi_deauth()
local iface = luci.http.formvalue("iface")
local bssid = luci.http.formvalue("bssid")
if iface and bssid then
luci.util.ubus("hostapd.%s" % iface, "del_client", {
addr = bssid,
deauth = true,
reason = 5,
ban_time = 60000
})
end
luci.http.status(200, "OK")
end

View file

@ -0,0 +1,109 @@
-- Copyright 2008 Steven Barth <steven@midlink.org>
-- Copyright 2010-2015 Jo-Philipp Wich <jow@openwrt.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.controller.admin.uci", package.seeall)
function index()
local redir = luci.http.formvalue("redir", true)
or table.concat(luci.dispatcher.context.request, "/")
entry({"admin", "uci"}, nil, _("Configuration"))
entry({"admin", "uci", "changes"}, post_on({ trigger_apply = true }, "action_changes"), _("Changes"), 40).query = {redir=redir}
entry({"admin", "uci", "revert"}, post("action_revert"), _("Revert"), 30).query = {redir=redir}
local node
local authen = function(checkpass, allowed_users)
return "root", luci.http.formvalue("sid")
end
node = entry({"admin", "uci", "apply_rollback"}, post("action_apply_rollback"), nil)
node.cors = true
node.sysauth_authenticator = authen
node = entry({"admin", "uci", "apply_unchecked"}, post("action_apply_unchecked"), nil)
node.cors = true
node.sysauth_authenticator = authen
node = entry({"admin", "uci", "confirm"}, call("action_confirm"), nil)
node.cors = true
node.sysauth = false
end
function action_changes()
local uci = require "luci.model.uci"
local changes = uci:changes()
luci.template.render("admin_uci/changes", {
changes = next(changes) and changes,
timeout = timeout,
trigger_apply = luci.http.formvalue("trigger_apply") and true or false
})
end
function action_revert()
local uci = require "luci.model.uci"
local changes = uci:changes()
-- Collect files to be reverted
local r, tbl
for r, tbl in pairs(changes) do
uci:revert(r)
end
luci.template.render("admin_uci/revert", {
changes = next(changes) and changes,
trigger_revert = true
})
end
local function ubus_state_to_http(errstr)
local map = {
["Invalid command"] = 400,
["Invalid argument"] = 400,
["Method not found"] = 404,
["Entry not found"] = 404,
["No data"] = 204,
["Permission denied"] = 403,
["Timeout"] = 504,
["Not supported"] = 500,
["Unknown error"] = 500,
["Connection failed"] = 503
}
local code = map[errstr] or 200
local msg = errstr or "OK"
luci.http.status(code, msg)
if code ~= 204 then
luci.http.prepare_content("text/plain")
luci.http.write(msg)
end
end
function action_apply_rollback()
local uci = require "luci.model.uci"
local token, errstr = uci:apply(true)
if token then
luci.http.prepare_content("application/json")
luci.http.write_json({ token = token })
else
ubus_state_to_http(errstr)
end
end
function action_apply_unchecked()
local uci = require "luci.model.uci"
local _, errstr = uci:apply(false)
ubus_state_to_http(errstr)
end
function action_confirm()
local uci = require "luci.model.uci"
local token = luci.http.formvalue("token")
local _, errstr = uci:confirm(token)
ubus_state_to_http(errstr)
end

View file

@ -40,6 +40,28 @@ function build_url(...)
return table.concat(url, "")
end
function _ordered_children(node)
local name, child, children = nil, nil, {}
for name, child in pairs(node.nodes) do
children[#children+1] = {
name = name,
node = child,
order = child.order or 100
}
end
table.sort(children, function(a, b)
if a.order == b.order then
return a.name < b.name
else
return a.order < b.order
end
end)
return children
end
function node_visible(node)
if node then
return not (
@ -55,15 +77,10 @@ end
function node_childs(node)
local rv = { }
if node then
local k, v
for k, v in util.spairs(node.nodes,
function(a, b)
return (node.nodes[a].order or 100)
< (node.nodes[b].order or 100)
end)
do
if node_visible(v) then
rv[#rv+1] = k
local _, child
for _, child in ipairs(_ordered_children(node)) do
if node_visible(child.node) then
rv[#rv+1] = child.name
end
end
end
@ -296,10 +313,6 @@ function dispatch(request)
ctx.requestpath = ctx.requestpath or freq
ctx.path = preq
if track.i18n then
i18n.loadc(track.i18n)
end
-- Init template engine
if (c and c.index) or not track.notemplate then
local tpl = require("luci.template")
@ -315,7 +328,7 @@ function dispatch(request)
assert(media, "No valid theme found")
end
local function _ifattr(cond, key, val)
local function _ifattr(cond, key, val, noescape)
if cond then
local env = getfenv(3)
local scope = (type(env.self) == "table") and env.self
@ -326,13 +339,16 @@ function dispatch(request)
val = util.serialize_json(val)
end
end
return string.format(
' %s="%s"', tostring(key),
util.pcdata(tostring( val
or (type(env[key]) ~= "function" and env[key])
or (scope and type(scope[key]) ~= "function" and scope[key])
or "" ))
)
val = tostring(val or
(type(env[key]) ~= "function" and env[key]) or
(scope and type(scope[key]) ~= "function" and scope[key]) or "")
if noescape ~= true then
val = util.pcdata(val)
end
return string.format(' %s="%s"', tostring(key), val)
else
return ''
end
@ -421,6 +437,7 @@ function dispatch(request)
context.path = {}
http.status(403, "Forbidden")
http.header("X-LuCI-Login-Required", "yes")
tmpl.render(track.sysauth_template or "sysauth", {
duser = default_user,
fuser = user
@ -437,6 +454,7 @@ function dispatch(request)
if not sid or not sdat then
http.status(403, "Forbidden")
http.header("X-LuCI-Login-Required", "yes")
return
end
@ -597,14 +615,9 @@ function createtree()
local ctx = context
local tree = {nodes={}, inreq=true}
local modi = {}
ctx.treecache = setmetatable({}, {__mode="v"})
ctx.tree = tree
ctx.modifiers = modi
-- Load default translation
require "luci.i18n".loadc("base")
local scope = setmetatable({}, {__index = luci.dispatcher})
@ -614,28 +627,9 @@ function createtree()
v()
end
local function modisort(a,b)
return modi[a].order < modi[b].order
end
for _, v in util.spairs(modi, modisort) do
scope._NAME = v.module
setfenv(v.func, scope)
v.func()
end
return tree
end
function modifier(func, order)
context.modifiers[#context.modifiers+1] = {
func = func,
order = order or 0,
module
= getfenv(2)._NAME
}
end
function assign(path, clone, title, order)
local obj = node(unpack(path))
obj.nodes = nil
@ -724,32 +718,66 @@ end
-- Subdispatchers --
function _find_eligible_node(root, prefix, deep, types, descend)
local children = _ordered_children(root)
if not root.leaf and deep ~= nil then
local sub_path = { unpack(prefix) }
if deep == false then
deep = nil
end
local _, child
for _, child in ipairs(children) do
sub_path[#prefix+1] = child.name
local res_path = _find_eligible_node(child.node, sub_path,
deep, types, true)
if res_path then
return res_path
end
end
end
if descend and
(not types or
(type(root.target) == "table" and
util.contains(types, root.target.type)))
then
return prefix
end
end
function _find_node(recurse, types)
local path = { unpack(context.path) }
local name = table.concat(path, ".")
local node = context.treecache[name]
path = _find_eligible_node(node, path, recurse, types)
if path then
dispatch(path)
else
require "luci.template".render("empty_node_placeholder")
end
end
function _firstchild()
local path = { unpack(context.path) }
local name = table.concat(path, ".")
local node = context.treecache[name]
local lowest
if node and node.nodes and next(node.nodes) then
local k, v
for k, v in pairs(node.nodes) do
if not lowest or
(v.order or 100) < (node.nodes[lowest].order or 100)
then
lowest = k
end
end
end
assert(lowest ~= nil,
"The requested node contains no childs, unable to redispatch")
path[#path+1] = lowest
dispatch(path)
return _find_node(false, nil)
end
function firstchild()
return { type = "firstchild", target = _firstchild }
return { type = "firstchild", target = _firstchild }
end
function _firstnode()
return _find_node(true, { "cbi", "form", "template", "arcombine" })
end
function firstnode()
return { type = "firstnode", target = _firstnode }
end
function alias(...)

View file

@ -22,7 +22,7 @@ Check whether a dispatch node shall be visible
]]
---[[
Return a sorted table of visible childs within a given node
Return a sorted table of visible children within a given node
@class function
@name node_childs
@ -81,15 +81,6 @@ Build the index before if it does not exist yet.
@name createtree
]]
---[[
Register a tree modifier.
@class function
@name modifier
@param func Modifier function
@param order Modifier order value (optional)
]]
---[[
Clone a node of the dispatching tree to another position.

View file

@ -335,13 +335,13 @@ end
-- Content-Type. Stores all extracted data associated with its parameter name
-- in the params table within the given message object. Multiple parameter
-- values are stored as tables, ordinary ones as strings.
-- If an optional file callback function is given then it is feeded with the
-- If an optional file callback function is given then it is fed with the
-- file contents chunk by chunk and only the extracted file name is stored
-- within the params table. The callback function will be called subsequently
-- with three arguments:
-- o Table containing decoded (name, file) and raw (headers) mime header data
-- o String value containing a chunk of the file data
-- o Boolean which indicates wheather the current chunk is the last one (eof)
-- o Boolean which indicates whether the current chunk is the last one (eof)
function mimedecode_message_body(src, msg, file_cb)
local parser, header, field
local len, maxlen = 0, tonumber(msg.env.CONTENT_LENGTH or nil)

View file

@ -204,13 +204,13 @@ Stores all extracted data associated with its parameter name
in the params table within the given message object. Multiple parameter
values are stored as tables, ordinary ones as strings.
If an optional file callback function is given then it is feeded with the
If an optional file callback function is given then it is fed with the
file contents chunk by chunk and only the extracted file name is stored
within the params table. The callback function will be called subsequently
with three arguments:
o Table containing decoded (name, file) and raw (headers) mime header data
o String value containing a chunk of the file data
o Boolean which indicates wheather the current chunk is the last one (eof)
o Boolean which indicates whether the current chunk is the last one (eof)
@class function
@name mimedecode_message_body

View file

@ -1,37 +1,43 @@
-- Copyright 2008 Steven Barth <steven@midlink.org>
-- Licensed to the public under the Apache License 2.0.
module("luci.i18n", package.seeall)
require("luci.util")
local tparser = require "luci.template.parser"
local util = require "luci.util"
local tostring = tostring
local tparser = require "luci.template.parser"
module "luci.i18n"
table = {}
i18ndir = luci.util.libpath() .. "/i18n/"
loaded = {}
context = luci.util.threadlocal()
i18ndir = util.libpath() .. "/i18n/"
context = util.threadlocal()
default = "en"
function clear()
end
function load(file, lang, force)
end
-- Alternatively load the translation of the fallback language.
function loadc(file, force)
end
function setlanguage(lang)
context.lang = lang:gsub("_", "-")
context.parent = (context.lang:match("^([a-z][a-z])_"))
if not tparser.load_catalog(context.lang, i18ndir) then
if context.parent then
tparser.load_catalog(context.parent, i18ndir)
local code, subcode = lang:match("^([A-Za-z][A-Za-z])[%-_]([A-Za-z][A-Za-z])$")
if not (code and subcode) then
subcode = lang:match("^([A-Za-z][A-Za-z])$")
if not subcode then
return nil
end
end
context.parent = code and code:lower()
context.lang = context.parent and context.parent.."-"..subcode:lower() or subcode:lower()
if tparser.load_catalog(context.lang, i18ndir) and
tparser.change_catalog(context.lang)
then
return context.lang
elseif context.parent then
if tparser.load_catalog(context.parent, i18ndir) and
tparser.change_catalog(context.parent)
then
return context.parent
end
end
return context.lang
return nil
end
function translate(key)
@ -42,14 +48,8 @@ function translatef(key, ...)
return tostring(translate(key)):format(...)
end
-- and ensure that the returned value is a Lua string value.
-- This is the same as calling <code>tostring(translate(...))</code>
function string(key)
return tostring(translate(key))
end
-- Ensure that the returned value is a Lua string value.
-- This is the same as calling <code>tostring(translatef(...))</code>
function stringf(key, ...)
return tostring(translate(key)):format(...)
function dump()
local rv = {}
tparser.get_translations(function(k, v) rv[k] = v end)
return rv
end

View file

@ -3,41 +3,13 @@ LuCI translation library.
]]
module "luci.i18n"
---[[
Clear the translation table.
@class function
@name clear
]]
---[[
Load a translation and copy its data into the translation table.
@class function
@name load
@param file Language file
@param lang Two-letter language code
@param force Force reload even if already loaded (optional)
@return Success status
]]
---[[
Load a translation file using the default translation language.
Alternatively load the translation of the fallback language.
@class function
@name loadc
@param file Language file
@param force Force reload even if already loaded (optional)
]]
---[[
Set the context default translation language.
@class function
@name setlanguage
@param lang Two-letter language code
@param lang An IETF/BCP 47 language tag or ISO3166 country code, e.g. "en-US" or "de"
@return The effective loaded language, e.g. "en" for "en-US" - or nil on failure
]]
---[[
@ -60,25 +32,11 @@ Return the translated value for a specific translation key and use it as sprintf
]]
---[[
Return the translated value for a specific translation key
Return all currently loaded translation strings as a key-value table. The key is the
hexadecimal representation of the translation key while the value is the translated
text content.
and ensure that the returned value is a Lua string value.
This is the same as calling <code>tostring(translate(...))</code>
@class function
@name string
@param key Default translation text
@return Translated string
@name dump
@return Key-value translation string table.
]]
---[[
Return the translated value for a specific translation key and use it as sprintf pattern.
Ensure that the returned value is a Lua string value.
This is the same as calling <code>tostring(translatef(...))</code>
@class function
@name stringf
@param key Default translation text
@param ... Format parameters
@return Translated and formatted string
]]

View file

@ -53,6 +53,7 @@ metric.datatype = "uinteger"
clientid = section:taboption("advanced", Value, "clientid",
translate("Client ID to send when requesting DHCP"))
clientid.datatype = "hexstring"
vendorclass = section:taboption("advanced", Value, "vendorid",

View file

@ -4,17 +4,93 @@
local map, section, net = ...
local ifc = net:get_interface()
local ipaddr, netmask, gateway, broadcast, dns, accept_ra, send_rs, ip6addr, ip6gw
local mtu, metric
local netmask, gateway, broadcast, dns, accept_ra, send_rs, ip6addr, ip6gw
local mtu, metric, usecidr, ipaddr_single, ipaddr_multi
ipaddr = section:taboption("general", Value, "ipaddr", translate("IPv4 address"))
ipaddr.datatype = "ip4addr"
local function is_cidr(s)
return (type(s) == "string" and luci.ip.IPv4(s) and s:find("/"))
end
usecidr = section:taboption("general", Value, "ipaddr_usecidr")
usecidr.forcewrite = true
usecidr.cfgvalue = function(self, section)
local cfgvalue = self.map:get(section, "ipaddr")
return (type(cfgvalue) == "table" or is_cidr(cfgvalue)) and "1" or "0"
end
usecidr.render = function(self, section, scope)
luci.template.Template(nil, [[
<input type="hidden"<%= attr("id", cbid) .. attr("name", cbid) .. attr("value", value) %> />
]]):render({
cbid = self:cbid(section),
value = self:cfgvalue(section)
})
end
usecidr.write = function(self, section)
local cfgvalue = self.map:get(section, "ipaddr")
local formvalue = (self:formvalue(section) == "1") and ipaddr_multi:formvalue(section) or ipaddr_single:formvalue(section)
local equal = (cfgvalue == formvalue)
if not equal and type(cfgvalue) == "table" and type(formvalue) == "table" and #cfgvalue == #formvalue then
equal = true
local _, v
for _, v in ipairs(cfgvalue) do
if v ~= formvalue[_] then
equal = false
break
end
end
end
if not equal then
self.map:set(section, "ipaddr", formvalue or "")
end
return not equal
end
netmask = section:taboption("general", Value, "netmask",
translate("IPv4 netmask"))
ipaddr_multi = section:taboption("general", DynamicList, "ipaddrs", translate("IPv4 address"))
ipaddr_multi:depends("ipaddr_usecidr", "1")
ipaddr_multi.datatype = "or(cidr4,ipnet4)"
ipaddr_multi.placeholder = translate("Add IPv4 address…")
ipaddr_multi.alias = "ipaddr"
ipaddr_multi.write = function() end
ipaddr_multi.remove = function() end
ipaddr_multi.cfgvalue = function(self, section)
local addr = self.map:get(section, "ipaddr")
local mask = self.map:get(section, "netmask")
if is_cidr(addr) then
return { addr }
elseif type(addr) == "string" and
type(mask) == "string" and
#addr > 0 and #mask > 0
then
return { "%s/%s" %{ addr, mask } }
elseif type(addr) == "table" then
return addr
else
return {}
end
end
ipaddr_single = section:taboption("general", Value, "ipaddr", translate("IPv4 address"))
ipaddr_single:depends("ipaddr_usecidr", "0")
ipaddr_single.datatype = "ip4addr"
ipaddr_single.template = "cbi/ipaddr"
ipaddr_single.write = function() end
ipaddr_single.remove = function() end
netmask = section:taboption("general", Value, "netmask", translate("IPv4 netmask"))
netmask:depends("ipaddr_usecidr", "0")
netmask.datatype = "ip4addr"
netmask:value("255.255.255.0")
netmask:value("255.255.0.0")
@ -48,8 +124,9 @@ if luci.model.network:has_ipv6() then
translate("Assign prefix parts using this hexadecimal subprefix ID for this interface."))
for i=33,64 do ip6hint:depends("ip6assign", i) end
ip6addr = section:taboption("general", Value, "ip6addr", translate("IPv6 address"))
ip6addr = section:taboption("general", DynamicList, "ip6addr", translate("IPv6 address"))
ip6addr.datatype = "ip6addr"
ip6addr.placeholder = translate("Add IPv6 address…")
ip6addr:depends("ip6assign", "")
@ -83,14 +160,8 @@ mtu.placeholder = "1500"
mtu.datatype = "max(9200)"
--metric = section:taboption("advanced", Value, "metric",
-- translate("Use gateway metric"))
--metric.default = "1"
--metric.datatype = "uinteger"
metric = section:taboption("advanced", Value, "metric",
translate("Use gateway metric"))
--local nw = require "luci.model.network".init()
--for _, network in ipairs(nw:get_networks()) do
-- if network:proto() == "static" and network:type() == "macvlan" and tonumber(network:metric()) >= tonumber(metric.default) then
-- metric.default = network:metric() + 1
-- end
--end
metric.placeholder = "0"
metric.datatype = "uinteger"

View file

@ -1,247 +0,0 @@
-- Copyright 2008-2011 Jo-Philipp Wich <jow@openwrt.org>
-- Copyright 2008 Steven Barth <steven@midlink.org>
-- Licensed to the public under the Apache License 2.0.
local os = require "os"
local io = require "io"
local fs = require "nixio.fs"
local util = require "luci.util"
local type = type
local pairs = pairs
local error = error
local table = table
local ipkg = "opkg --force-removal-of-dependent-packages --force-overwrite --nocase"
local icfg = "/etc/opkg.conf"
module "luci.model.ipkg"
-- Internal action function
local function _action(cmd, ...)
local cmdline = { ipkg, cmd }
local k, v
for k, v in pairs({...}) do
cmdline[#cmdline+1] = util.shellquote(v)
end
local c = "%s >/tmp/opkg.stdout 2>/tmp/opkg.stderr" % table.concat(cmdline, " ")
local r = os.execute(c)
local e = fs.readfile("/tmp/opkg.stderr")
local o = fs.readfile("/tmp/opkg.stdout")
fs.unlink("/tmp/opkg.stderr")
fs.unlink("/tmp/opkg.stdout")
return r, o or "", e or ""
end
-- Internal parser function
local function _parselist(rawdata)
if type(rawdata) ~= "function" then
error("OPKG: Invalid rawdata given")
end
local data = {}
local c = {}
local l = nil
for line in rawdata do
if line:sub(1, 1) ~= " " then
local key, val = line:match("(.-): ?(.*)%s*")
if key and val then
if key == "Package" then
c = {Package = val}
data[val] = c
elseif key == "Status" then
c.Status = {}
for j in val:gmatch("([^ ]+)") do
c.Status[j] = true
end
else
c[key] = val
end
l = key
end
else
-- Multi-line field
c[l] = c[l] .. "\n" .. line
end
end
return data
end
-- Internal lookup function
local function _lookup(cmd, pkg)
local cmdline = { ipkg, cmd }
if pkg then
cmdline[#cmdline+1] = util.shellquote(pkg)
end
-- OPKG sometimes kills the whole machine because it sucks
-- Therefore we have to use a sucky approach too and use
-- tmpfiles instead of directly reading the output
local tmpfile = os.tmpname()
os.execute("%s >%s 2>/dev/null" %{ table.concat(cmdline, " "), tmpfile })
local data = _parselist(io.lines(tmpfile))
os.remove(tmpfile)
return data
end
function info(pkg)
return _lookup("info", pkg)
end
function status(pkg)
return _lookup("status", pkg)
end
function install(...)
return _action("install", ...)
end
function installed(pkg)
local p = status(pkg)[pkg]
return (p and p.Status and p.Status.installed)
end
function remove(...)
return _action("remove", ...)
end
function update()
return _action("update")
end
function upgrade()
return _action("upgrade")
end
-- List helper
local function _list(action, pat, cb)
local cmdline = { ipkg, action }
if pat then
cmdline[#cmdline+1] = util.shellquote(pat)
end
local fd = io.popen(table.concat(cmdline, " "))
if fd then
local name, version, sz, desc
while true do
local line = fd:read("*l")
if not line then break end
name, version, sz, desc = line:match("^(.-) %- (.-) %- (.-) %- (.+)")
if not name then
name, version, sz = line:match("^(.-) %- (.-) %- (.+)")
desc = ""
end
if name and version then
if #version > 26 then
version = version:sub(1,21) .. ".." .. version:sub(-3,-1)
end
cb(name, version, sz, desc)
end
name = nil
version = nil
sz = nil
desc = nil
end
fd:close()
end
end
function list_all(pat, cb)
_list("list --size", pat, cb)
end
function list_installed(pat, cb)
_list("list_installed --size", pat, cb)
end
function find(pat, cb)
_list("find --size", pat, cb)
end
function overlay_root()
local od = "/"
local fd = io.open(icfg, "r")
if fd then
local ln
repeat
ln = fd:read("*l")
if ln and ln:match("^%s*option%s+overlay_root%s+") then
od = ln:match("^%s*option%s+overlay_root%s+(%S+)")
local s = fs.stat(od)
if not s or s.type ~= "dir" then
od = "/"
end
break
end
until not ln
fd:close()
end
return od
end
function compare_versions(ver1, comp, ver2)
if not ver1 or not ver2
or not comp or not (#comp > 0) then
error("Invalid parameters")
return nil
end
-- correct compare string
if comp == "<>" or comp == "><" or comp == "!=" or comp == "~=" then comp = "~="
elseif comp == "<=" or comp == "<" or comp == "=<" then comp = "<="
elseif comp == ">=" or comp == ">" or comp == "=>" then comp = ">="
elseif comp == "=" or comp == "==" then comp = "=="
elseif comp == "<<" then comp = "<"
elseif comp == ">>" then comp = ">"
else
error("Invalid compare string")
return nil
end
local av1 = util.split(ver1, "[%.%-]", nil, true)
local av2 = util.split(ver2, "[%.%-]", nil, true)
local max = table.getn(av1)
if (table.getn(av1) < table.getn(av2)) then
max = table.getn(av2)
end
for i = 1, max, 1 do
local s1 = av1[i] or ""
local s2 = av2[i] or ""
-- first "not equal" found return true
if comp == "~=" and (s1 ~= s2) then return true end
-- first "lower" found return true
if (comp == "<" or comp == "<=") and (s1 < s2) then return true end
-- first "greater" found return true
if (comp == ">" or comp == ">=") and (s1 > s2) then return true end
-- not equal then return false
if (s1 ~= s2) then return false end
end
-- all equal and not compare greater or lower then true
return not (comp == "<" or comp == ">")
end

View file

@ -1,125 +0,0 @@
---[[
LuCI OPKG call abstraction library
]]
module "luci.model.ipkg"
---[[
Return information about installed and available packages.
@class function
@name info
@param pkg Limit output to a (set of) packages
@return Table containing package information
]]
---[[
Return the package status of one or more packages.
@class function
@name status
@param pkg Limit output to a (set of) packages
@return Table containing package status information
]]
---[[
Install one or more packages.
@class function
@name install
@param ... List of packages to install
@return Boolean indicating the status of the action
@return OPKG return code, STDOUT and STDERR
]]
---[[
Determine whether a given package is installed.
@class function
@name installed
@param pkg Package
@return Boolean
]]
---[[
Remove one or more packages.
@class function
@name remove
@param ... List of packages to install
@return Boolean indicating the status of the action
@return OPKG return code, STDOUT and STDERR
]]
---[[
Update package lists.
@class function
@name update
@return Boolean indicating the status of the action
@return OPKG return code, STDOUT and STDERR
]]
---[[
Upgrades all installed packages.
@class function
@name upgrade
@return Boolean indicating the status of the action
@return OPKG return code, STDOUT and STDERR
]]
---[[
List all packages known to opkg.
@class function
@name list_all
@param pat Only find packages matching this pattern, nil lists all packages
@param cb Callback function invoked for each package, receives name, version and description as arguments
@return nothing
]]
---[[
List installed packages.
@class function
@name list_installed
@param pat Only find packages matching this pattern, nil lists all packages
@param cb Callback function invoked for each package, receives name, version and description as arguments
@return nothing
]]
---[[
Find packages that match the given pattern.
@class function
@name find
@param pat Find packages whose names or descriptions match this pattern, nil results in zero results
@param cb Callback function invoked for each patckage, receives name, version and description as arguments
@return nothing
]]
---[[
Determines the overlay root used by opkg.
@class function
@name overlay_root
@return String containing the directory path of the overlay root.
]]
---[[
lua version of opkg compare-versions
@class function
@name compare_versions
@param ver1 string version 1
@param ver2 string version 2
@param comp string compare versions using
"<=" or "<" lower-equal
">" or ">=" greater-equal
"=" equal
"<<" lower
">>" greater
"~=" not equal
@return Boolean indicating the status of the compare
]]

View file

@ -20,7 +20,7 @@ module "luci.model.network"
IFACE_PATTERNS_VIRTUAL = { }
IFACE_PATTERNS_IGNORE = { "^wmaster%d", "^wifi%d", "^hwsim%d", "^imq%d", "^ifb%d", "^mon%.wlan%d", "^sit%d", "^gre%d", "^gretap%d", "^ip6gre%d", "^ip6tnl%d", "^tunl%d", "^lo$" }
IFACE_PATTERNS_IGNORE = { "^wmaster%d", "^wifi%d", "^hwsim%d", "^imq%d", "^ifb%d", "^mon%.wlan%d", "^sit%d", "^gre%d", "^gretap%d", "^ip6gre%d", "^ip6tnl%d", "^tunl%d", "^lo$", "^teql%d" }
IFACE_PATTERNS_WIRELESS = { "^wlan%d", "^wl%d", "^ath%d", "^%w+%.network%d" }
IFACE_ERRORS = {
@ -622,6 +622,12 @@ function del_network(self, n)
_uci:delete("wireless", s['.name'], "network")
end
end)
local ok, fw = pcall(require, "luci.model.firewall")
if ok then
fw.init()
fw:del_network(n)
end
end
return r
end
@ -813,6 +819,7 @@ function del_wifinet(self, net)
end
function get_status_by_route(self, addr, mask)
local route_statuses = { }
local _, object
for _, object in ipairs(utl.ubus()) do
local net = object:match("^network%.interface%.(.+)")
@ -822,12 +829,14 @@ function get_status_by_route(self, addr, mask)
local rt
for _, rt in ipairs(s.route) do
if not rt.table and rt.target == addr and rt.mask == mask then
return net, s
route_statuses[net] = s
end
end
end
end
end
return route_statuses
end
function get_status_by_address(self, addr)
@ -852,28 +861,40 @@ function get_status_by_address(self, addr)
end
end
end
if s and s['ipv6-prefix-assignment'] then
local a
for _, a in ipairs(s['ipv6-prefix-assignment']) do
if a and a['local-address'] and a['local-address'].address == addr then
return net, s
end
end
end
end
end
end
function get_wannet(self)
local net, stat = self:get_status_by_route("0.0.0.0", 0)
return net and network(net, stat.proto)
function get_wan_networks(self)
local k, v
local wan_nets = { }
local route_statuses = self:get_status_by_route("0.0.0.0", 0)
for k, v in pairs(route_statuses) do
wan_nets[#wan_nets+1] = network(k, v.proto)
end
return wan_nets
end
function get_wandev(self)
local _, stat = self:get_status_by_route("0.0.0.0", 0)
return stat and interface(stat.l3_device or stat.device)
end
function get_wan6_networks(self)
local k, v
local wan6_nets = { }
local route_statuses = self:get_status_by_route("::", 0)
function get_wan6net(self)
local net, stat = self:get_status_by_route("::", 0)
return net and network(net, stat.proto)
end
for k, v in pairs(route_statuses) do
wan6_nets[#wan6_nets+1] = network(k, v.proto)
end
function get_wan6dev(self)
local _, stat = self:get_status_by_route("::", 0)
return stat and interface(stat.l3_device or stat.device)
return wan6_nets
end
function get_switch_topologies(self)
@ -1144,6 +1165,10 @@ function protocol.is_dynamic(self)
return (self:_ubus("dynamic") == true)
end
function protocol.is_auto(self)
return (self:_get("auto") ~= "0")
end
function protocol.is_alias(self)
local ifn, parent = nil, nil

View file

@ -15,7 +15,7 @@ local type, tostring, tonumber, unpack = type, tostring, tonumber, unpack
-- cursor factory, modify data (via Cursor.add, Cursor.delete, etc.),
-- save the changes to the staging area via Cursor.save and finally
-- Cursor.commit the data to the actual config files.
-- LuCI then needs to Cursor.apply the changes so deamons etc. are
-- LuCI then needs to Cursor.apply the changes so daemons etc. are
-- reloaded.
module "luci.model.uci"
@ -95,41 +95,15 @@ end
function changes(self, config)
local rv = call("changes", { config = config })
local res = {}
local rv, err = call("changes", { config = config })
if type(rv) == "table" and type(rv.changes) == "table" then
local package, changes
for package, changes in pairs(rv.changes) do
res[package] = {}
local _, change
for _, change in ipairs(changes) do
local operation, section, option, value = unpack(change)
if option and operation ~= "add" then
res[package][section] = res[package][section] or { }
if operation == "list-add" then
local v = res[package][section][option]
if type(v) == "table" then
v[#v+1] = value or ""
elseif v ~= nil then
res[package][section][option] = { v, value }
else
res[package][section][option] = { value }
end
else
res[package][section][option] = value or ""
end
else
res[package][section] = res[package][section] or {}
res[package][section][".type"] = option or ""
end
end
end
return rv.changes
elseif err then
return nil, ERRSTR[err]
else
return { }
end
return res
end

View file

@ -5,7 +5,7 @@ The typical workflow for UCI is: Get a cursor instance from the
cursor factory, modify data (via Cursor.add, Cursor.delete, etc.),
save the changes to the staging area via Cursor.save and finally
Cursor.commit the data to the actual config files.
LuCI then needs to Cursor.apply the changes so deamons etc. are
LuCI then needs to Cursor.apply the changes so daemons etc. are
reloaded.
@cstyle instance
]]
@ -172,7 +172,7 @@ has the same effect as deleting the option.
---[[
Create a sub-state of this cursor.
The sub-state is tied to the parent curser, means it the parent unloads or
The sub-state is tied to the parent cursor, means it the parent unloads or
loads configs, the sub state will do so as well.
@class function
@ -339,7 +339,7 @@ Set the configuration directory.
]]
---[[
Set the directory for uncommited changes.
Set the directory for uncommitted changes.
@class function
@name Cursor.set_savedir

View file

@ -13,8 +13,8 @@ local luci = {}
luci.util = require "luci.util"
luci.ip = require "luci.ip"
local tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select =
tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select
local tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select, unpack =
tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select, unpack
module "luci.sys"
@ -70,6 +70,24 @@ function mounts()
return data
end
function mtds()
local data = {}
if fs.access("/proc/mtd") then
for l in io.lines("/proc/mtd") do
local d, s, e, n = l:match('^([^%s]+)%s+([^%s]+)%s+([^%s]+)%s+"([^%s]+)"')
if s and n then
local d = {}
d.size = tonumber(s, 16)
d.name = n
table.insert(data, d)
end
end
end
return data
end
-- containing the whole environment is returned otherwise this function returns
-- the corresponding string value for the given name or nil if no such variable
-- exists.
@ -87,9 +105,9 @@ end
function httpget(url, stream, target)
if not target then
local source = stream and io.popen or luci.util.exec
return source("wget -4 -T 20 -qO- %s" % luci.util.shellquote(url))
return source("wget -qO- %s" % luci.util.shellquote(url))
else
return os.execute("wget -4 -T 20 -qO %s %s" %
return os.execute("wget -qO %s %s" %
{luci.util.shellquote(target), luci.util.shellquote(url)})
end
end
@ -386,8 +404,8 @@ function process.list()
end
for line in ps do
local pid, ppid, user, stat, vsz, mem, cpun, cpu, cmd = line:match(
"^ *(%d+) +(%d+) +(%S.-%S) +([RSDZTW][W ][<N ]) +(%d+) +(%d+.%d) +(%d+) +(%d+.%d) +(.+)"
local pid, ppid, user, stat, vsz, mem, cpu, cmd = line:match(
"^ *(%d+) +(%d+) +(%S.-%S) +([RSDZTW][W ][<N ]) +(%d+) +(%d+%%) +(%d+%%) +(.+)"
)
local idx = tonumber(pid)
@ -418,6 +436,96 @@ end
process.signal = nixio.kill
local function xclose(fd)
if fd and fd:fileno() > 2 then
fd:close()
end
end
function process.exec(command, stdout, stderr, nowait)
local out_r, out_w, err_r, err_w
if stdout then out_r, out_w = nixio.pipe() end
if stderr then err_r, err_w = nixio.pipe() end
local pid = nixio.fork()
if pid == 0 then
nixio.chdir("/")
local null = nixio.open("/dev/null", "w+")
if null then
nixio.dup(out_w or null, nixio.stdout)
nixio.dup(err_w or null, nixio.stderr)
nixio.dup(null, nixio.stdin)
xclose(out_w)
xclose(out_r)
xclose(err_w)
xclose(err_r)
xclose(null)
end
nixio.exec(unpack(command))
os.exit(-1)
end
local _, pfds, rv = nil, {}, { code = -1, pid = pid }
xclose(out_w)
xclose(err_w)
if out_r then
pfds[#pfds+1] = {
fd = out_r,
cb = type(stdout) == "function" and stdout,
name = "stdout",
events = nixio.poll_flags("in", "err", "hup")
}
end
if err_r then
pfds[#pfds+1] = {
fd = err_r,
cb = type(stderr) == "function" and stderr,
name = "stderr",
events = nixio.poll_flags("in", "err", "hup")
}
end
while #pfds > 0 do
local nfds, err = nixio.poll(pfds, -1)
if not nfds and err ~= nixio.const.EINTR then
break
end
local i
for i = #pfds, 1, -1 do
local rfd = pfds[i]
if rfd.revents > 0 then
local chunk, err = rfd.fd:read(4096)
if chunk and #chunk > 0 then
if rfd.cb then
rfd.cb(chunk)
else
rfd.buf = rfd.buf or {}
rfd.buf[#rfd.buf + 1] = chunk
end
else
table.remove(pfds, i)
if rfd.buf then
rv[rfd.name] = table.concat(rfd.buf, "")
end
rfd.fd:close()
end
end
end
end
if not nowait then
_, _, rv.code = nixio.waitpid(pid)
end
return rv
end
user = {}

View file

@ -18,7 +18,7 @@ Execute a given shell command and capture its standard output
@class function
@name exec
@param command Command to call
@return String containg the return the output of the command
@return String containing the return the output of the command
]]
---[[
@ -38,7 +38,7 @@ exists.
@class function
@name getenv
@param var Name of the environment variable to retrieve (optional)
@return String containg the value of the specified variable
@return String containing the value of the specified variable
@return Table containing all variables if no variable name is given
]]
@ -271,6 +271,42 @@ Send a signal to a process identified by given pid.
@return Number containing the error code if failed
]]
---[[
Execute a process, optionally capturing stdio.
Executes the process specified by the given argv vector, e.g.
`{ "/bin/sh", "-c", "echo 1" }` and waits for it to terminate unless a true
value has been passed for the "nowait" parameter.
When a function value is passed for the stdout or stderr arguments, the passed
function is repeatedly called for each chunk read from the corresponding stdio
stream. The read data is passed as string containing at most 4096 bytes at a
time.
When a true, non-function value is passed for the stdout or stderr arguments,
the data of the corresponding stdio stream is read into an internal string
buffer and returned as "stdout" or "stderr" field respectively in the result
table.
When a true value is passed to the nowait parameter, the function does not
await process termination but returns as soon as all captured stdio streams
have been closed or - if no streams are captured - immediately after launching
the process.
@class function
@name process.exec
@param commend Table containing the argv vector to execute
@param stdout Callback function or boolean to indicate capturing (optional)
@param stderr Callback function or boolean to indicate capturing (optional)
@param nowait Don't wait for process termination when true (optional)
@return Table containing at least the fields "code" which holds the exit
status of the invoked process or "-1" on error and "pid", which
contains the process id assigned to the spawned process. When
stdout and/or stderr capturing has been requested, it additionally
contains "stdout" and "stderr" fields respectively, holding the
captured stdio data as string.
]]
---[[
LuCI system utilities / user related functions.
@ -279,7 +315,7 @@ LuCI system utilities / user related functions.
]]
---[[
Retrieve user informations for given uid.
Retrieve user information for given uid.
@class function
@name getuser
@ -305,7 +341,7 @@ Test whether given string matches the password of a given system user.
@name user.checkpasswd
@param username String containing the Unix user name
@param pass String containing the password to compare
@return Boolean indicating wheather the passwords are equal
@return Boolean indicating whether the passwords are equal
]]
---[[

View file

@ -16,14 +16,12 @@ TZ = {
{ 'Africa/Brazzaville', 'WAT-1' },
{ 'Africa/Bujumbura', 'CAT-2' },
{ 'Africa/Cairo', 'EET-2' },
{ 'Africa/Casablanca', 'WET0WEST,M3.5.0,M10.5.0/3' },
{ 'Africa/Ceuta', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Africa/Conakry', 'GMT0' },
{ 'Africa/Dakar', 'GMT0' },
{ 'Africa/Dar es Salaam', 'EAT-3' },
{ 'Africa/Djibouti', 'EAT-3' },
{ 'Africa/Douala', 'WAT-1' },
{ 'Africa/El Aaiun', 'WET0WEST,M3.5.0,M10.5.0/3' },
{ 'Africa/Freetown', 'GMT0' },
{ 'Africa/Gaborone', 'CAT-2' },
{ 'Africa/Harare', 'CAT-2' },
@ -51,7 +49,7 @@ TZ = {
{ 'Africa/Nouakchott', 'GMT0' },
{ 'Africa/Ouagadougou', 'GMT0' },
{ 'Africa/Porto-Novo', 'WAT-1' },
{ 'Africa/Sao Tome', 'WAT-1' },
{ 'Africa/Sao Tome', 'GMT0' },
{ 'Africa/Tripoli', 'EET-2' },
{ 'Africa/Tunis', 'CET-1' },
{ 'Africa/Windhoek', 'CAT-2' },
@ -179,7 +177,7 @@ TZ = {
{ 'America/Resolute', 'CST6CDT,M3.2.0,M11.1.0' },
{ 'America/Rio Branco', '<-05>5' },
{ 'America/Santarem', '<-03>3' },
{ 'America/Santiago', '<-04>4<-03>,M8.2.6/24,M5.2.6/24' },
{ 'America/Santiago', '<-04>4<-03>,M9.1.6/24,M4.1.6/24' },
{ 'America/Santo Domingo', 'AST4' },
{ 'America/Sao Paulo', '<-03>3<-02>,M11.1.0/0,M2.3.0/0' },
{ 'America/Scoresbysund', '<-01>1<+00>,M3.5.0/0,M10.5.0/1' },
@ -261,7 +259,7 @@ TZ = {
{ 'Asia/Macau', 'CST-8' },
{ 'Asia/Magadan', '<+11>-11' },
{ 'Asia/Makassar', 'WITA-8' },
{ 'Asia/Manila', '<+08>-8' },
{ 'Asia/Manila', 'PST-8' },
{ 'Asia/Muscat', '<+04>-4' },
{ 'Asia/Nicosia', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
{ 'Asia/Novokuznetsk', '<+07>-7' },
@ -270,9 +268,10 @@ TZ = {
{ 'Asia/Oral', '<+05>-5' },
{ 'Asia/Phnom Penh', '<+07>-7' },
{ 'Asia/Pontianak', 'WIB-7' },
{ 'Asia/Pyongyang', 'KST-8:30' },
{ 'Asia/Pyongyang', 'KST-9' },
{ 'Asia/Qatar', '<+03>-3' },
{ 'Asia/Qyzylorda', '<+06>-6' },
{ 'Asia/Qostanay', '<+06>-6' },
{ 'Asia/Qyzylorda', '<+05>-5' },
{ 'Asia/Riyadh', '<+03>-3' },
{ 'Asia/Sakhalin', '<+11>-11' },
{ 'Asia/Samarkand', '<+05>-5' },
@ -283,7 +282,7 @@ TZ = {
{ 'Asia/Taipei', 'CST-8' },
{ 'Asia/Tashkent', '<+05>-5' },
{ 'Asia/Tbilisi', '<+04>-4' },
{ 'Asia/Tehran', '<+0330>-3:30<+0430>,J80/0,J264/0' },
{ 'Asia/Tehran', '<+0330>-3:30<+0430>,J79/24,J263/24' },
{ 'Asia/Thimphu', '<+06>-6' },
{ 'Asia/Tokyo', 'JST-9' },
{ 'Asia/Tomsk', '<+07>-7' },
@ -358,7 +357,7 @@ TZ = {
{ 'Europe/Busingen', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Chisinau', 'EET-2EEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Copenhagen', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Dublin', 'GMT0IST,M3.5.0/1,M10.5.0' },
{ 'Europe/Dublin', 'IST-1GMT0,M10.5.0,M3.5.0/1' },
{ 'Europe/Gibraltar', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Guernsey', 'GMT0BST,M3.5.0/1,M10.5.0' },
{ 'Europe/Helsinki', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
@ -400,7 +399,7 @@ TZ = {
{ 'Europe/Vatican', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Vienna', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Vilnius', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
{ 'Europe/Volgograd', '<+03>-3' },
{ 'Europe/Volgograd', '<+04>-4' },
{ 'Europe/Warsaw', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Zagreb', 'CET-1CEST,M3.5.0,M10.5.0/3' },
{ 'Europe/Zaporozhye', 'EET-2EEST,M3.5.0/3,M10.5.0/4' },
@ -421,11 +420,11 @@ TZ = {
{ 'Pacific/Bougainville', '<+11>-11' },
{ 'Pacific/Chatham', '<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45' },
{ 'Pacific/Chuuk', '<+10>-10' },
{ 'Pacific/Easter', '<-06>6<-05>,M8.2.6/22,M5.2.6/22' },
{ 'Pacific/Easter', '<-06>6<-05>,M9.1.6/22,M4.1.6/22' },
{ 'Pacific/Efate', '<+11>-11' },
{ 'Pacific/Enderbury', '<+13>-13' },
{ 'Pacific/Fakaofo', '<+13>-13' },
{ 'Pacific/Fiji', '<+12>-12<+13>,M11.1.0,M1.2.1/147' },
{ 'Pacific/Fiji', '<+12>-12<+13>,M11.1.0,M1.2.2/123' },
{ 'Pacific/Funafuti', '<+12>-12' },
{ 'Pacific/Galapagos', '<-06>6' },
{ 'Pacific/Gambier', '<-09>9' },

View file

@ -9,7 +9,6 @@ OFFSET = {
wat = 3600, -- WAT
cat = 7200, -- CAT
eet = 7200, -- EET
wet = 0, -- WET
sast = 7200, -- SAST
hst = -36000, -- HST
hdt = -32400, -- HDT
@ -34,8 +33,9 @@ OFFSET = {
idt = 10800, -- IDT
pkt = 18000, -- PKT
wita = 28800, -- WITA
kst = 30600, -- KST
kst = 32400, -- KST
jst = 32400, -- JST
wet = 0, -- WET
acst = 34200, -- ACST
acdt = 37800, -- ACDT
aest = 36000, -- AEST

View file

@ -95,6 +95,6 @@ function Template.render(self, scope)
local stat, err = util.copcall(self.template)
if not stat then
error("Failed to execute template '" .. self.name .. "'.\n" ..
"A runtime error occured: " .. tostring(err or "(nil)"))
"A runtime error occurred: " .. tostring(err or "(nil)"))
end
end

View file

@ -262,7 +262,7 @@ end
-- one token per invocation, the tokens are separated by whitespace. If the
-- input value is a table, it is transformed into a string first. A nil value
-- will result in a valid interator which aborts with the first invocation.
-- will result in a valid iterator which aborts with the first invocation.
function imatch(v)
if type(v) == "table" then
local k = nil

View file

@ -158,7 +158,7 @@ Return a matching iterator for the given value.
The iterator will return one token per invocation, the tokens are separated by
whitespace. If the input value is a table, it is transformed into a string first.
A nil value will result in a valid interator which aborts with the first invocation.
A nil value will result in a valid iterator which aborts with the first invocation.
@class function
@name imatch
@ -289,7 +289,7 @@ will be stripped before it is returned.
]]
---[[
Strips unnescessary lua bytecode from given string.
Strips unnecessary lua bytecode from given string.
Information like line numbers and debugging numbers will be discarded.
Original version by Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html)

View file

@ -0,0 +1,66 @@
<%#
Copyright 2010 Jo-Philipp Wich <jo@mein.io>
Licensed to the public under the Apache License 2.0.
-%>
<% export("uci_changelog", function(changes) -%>
<div class="cbi-section">
<strong><%:Legend:%></strong>
<div class="uci-change-legend">
<div class="uci-change-legend-label"><ins>&#160;</ins> <%:Section added%></div>
<div class="uci-change-legend-label"><del>&#160;</del> <%:Section removed%></div>
<div class="uci-change-legend-label"><var><ins>&#160;</ins></var> <%:Option changed%></div>
<div class="uci-change-legend-label"><var><del>&#160;</del></var> <%:Option removed%></div>
<br style="clear:both" />
</div>
<br />
<div class="uci-change-list"><%
local util = luci.util
local tpl = {
["add-3"] = "<ins>uci add %0 <strong>%3</strong> # =%2</ins>",
["set-3"] = "<ins>uci set %0.<strong>%2</strong>=%3</ins>",
["set-4"] = "<var><ins>uci set %0.%2.%3=<strong>%4</strong></ins></var>",
["remove-2"] = "<del>uci del %0.<strong>%2</strong></del>",
["remove-3"] = "<var><del>uci del %0.%2.<strong>%3</strong></del></var>",
["order-3"] = "<var>uci reorder %0.%2=<strong>%3</strong></var>",
["list-add-4"] = "<var><ins>uci add_list %0.%2.%3=<strong>%4</strong></ins></var>",
["list-del-4"] = "<var><del>uci del_list %0.%2.%3=<strong>%4</strong></del></var>",
["rename-3"] = "<var>uci rename %0.%2=<strong>%3</strong></var>",
["rename-4"] = "<var>uci rename %0.%2.%3=<strong>%4</strong></var>"
}
local conf, deltas
for conf, deltas in util.kspairs(changes) do
write("<h3># /etc/config/%s</h3>" % conf)
local _, delta, added
for _, delta in pairs(deltas) do
local t = tpl["%s-%d" %{ delta[1], #delta }]
write(t:gsub("%%(%d)", function(n)
if n == "0" then
return conf
elseif n == "2" then
if added and delta[2] == added[1] then
return "@%s[-1]" % added[2]
else
return delta[2]
end
elseif n == "4" then
return util.shellquote(delta[4])
else
return delta[tonumber(n)]
end
end))
if delta[1] == "add" then
added = { delta[2], delta[3] }
end
end
write("<br />")
end
%></div>
</div>
<%- end) %>

View file

@ -0,0 +1,45 @@
<%#
Copyright 2008 Steven Barth <steven@midlink.org>
Copyright 2008-2018 Jo-Philipp Wich <jo@mein.io>
Licensed to the public under the Apache License 2.0.
-%>
<%+header%>
<%-
local node, redir_url = luci.dispatcher.lookup(luci.http.formvalue("redir"))
export("redirect", redir_url or url("admin/uci/changes"))
include("admin_uci/changelog")
-%>
<h2 name="content"><%:Configuration%> / <%:Changes%></h2>
<% if changes then %>
<%- uci_changelog(changes) -%>
<% else %>
<p><strong><%:There are no pending changes!%></strong></p>
<% end %>
<div class="alert-message" id="cbi_apply_status" style="display:none"></div>
<div class="cbi-page-actions">
<% if redir_url then %>
<form method="get" action="<%=luci.util.pcdata(redir_url)%>">
<input class="cbi-button cbi-button-link" type="submit" value="<%:Back%>" />
</form>
<% end %>
<form method="post" action="<%=url("admin/uci/changes")%>">
<input type="hidden" name="token" value="<%=token%>" />
<input type="hidden" name="redir" value="<%=pcdata(luci.http.formvalue("redir"))%>" />
<input class="cbi-button cbi-button-save" type="submit" name="trigger_apply" value="<%:Save & Apply%>" />
</form>
<form method="post" action="<%=url("admin/uci/revert")%>">
<input type="hidden" name="token" value="<%=token%>" />
<input type="hidden" name="redir" value="<%=pcdata(luci.http.formvalue("redir"))%>" />
<input class="cbi-button cbi-button-reset" type="submit" value="<%:Revert%>" />
</form>
</div>
<%+footer%>

View file

@ -0,0 +1,33 @@
<%#
Copyright 2008 Steven Barth <steven@midlink.org>
Copyright 2008-2018 Jo-Philipp Wich <jo@mein.io>
Licensed to the public under the Apache License 2.0.
-%>
<%+header%>
<%-
local node, redir_url = luci.dispatcher.lookup(luci.http.formvalue("redir"))
export("redirect", redir_url or url("admin/uci/changes"))
include("admin_uci/changelog")
-%>
<h2 name="content"><%:Configuration%> / <%:Revert%></h2>
<% if changes then %>
<p><strong><%:The following changes have been reverted%>:</strong></p>
<%- uci_changelog(changes) -%>
<% else %>
<p><strong><%:There are no pending changes to revert!%></strong></p>
<% end %>
<% if redir_url then %>
<div class="cbi-page-actions">
<form class="inline" method="get" action="<%=luci.util.pcdata(redir_url)%>">
<input class="cbi-button cbi-button-link" style="margin:0" type="submit" value="<%:Back%>" />
</form>
</div>
<% end %>
<%+footer%>

View file

@ -1,49 +1,4 @@
<% export("cbi_apply_widget", function(redirect_ok, rollback_token) -%>
<style type="text/css">
#cbi_apply_overlay {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: rgba(0, 0, 0, 0.7);
display: none;
z-index: 20000;
}
#cbi_apply_overlay .alert-message {
position: relative;
top: 10%;
width: 60%;
margin: auto;
display: flex;
flex-wrap: wrap;
min-height: 32px;
align-items: center;
}
#cbi_apply_overlay .alert-message > h4,
#cbi_apply_overlay .alert-message > p,
#cbi_apply_overlay .alert-message > div {
flex-basis: 100%;
}
#cbi_apply_overlay .alert-message > img {
margin-right: 1em;
flex-basis: 32px;
}
body.apply-overlay-active {
overflow: hidden;
height: 100vh;
}
body.apply-overlay-active #cbi_apply_overlay {
display: block;
}
</style>
<script type="text/javascript" src="<%=resource%>/cbi.js?v=git-18.138.59467-72fe5dd"></script>
<script type="text/javascript">//<![CDATA[
var xhr = new XHR(),
uci_apply_auth = { sid: '<%=luci.dispatcher.context.authsession%>', token: '<%=token%>' },
@ -55,28 +10,22 @@
was_xhr_poll_running = false;
function uci_status_message(type, content) {
var overlay = document.getElementById('cbi_apply_overlay') || document.body.appendChild(E('<div id="cbi_apply_overlay"><div class="alert-message"></div></div>')),
message = overlay.querySelector('.alert-message');
if (type) {
var message = showModal('', '');
if (message && type) {
if (!message.classList.contains(type)) {
message.classList.remove('notice');
message.classList.remove('warning');
message.classList.add(type);
}
message.classList.add('alert-message');
DOMTokenList.prototype.add.apply(message.classList, type.split(/\s+/));
if (content)
message.innerHTML = content;
document.body.classList.add('apply-overlay-active');
if (!was_xhr_poll_running) {
was_xhr_poll_running = XHR.running();
XHR.halt();
}
}
else {
document.body.classList.remove('apply-overlay-active');
hideModal();
if (was_xhr_poll_running)
XHR.run();
@ -85,19 +34,18 @@
function uci_rollback(checked) {
if (checked) {
uci_status_message('warning',
'<img src="<%=resource%>/icons/loading.gif" alt="" style="vertical-align:middle" /> ' +
'<%:Failed to confirm apply within %ds, waiting for rollback…%>'.format(uci_apply_rollback));
uci_status_message('warning spinning',
'<p><%:Failed to confirm apply within %ds, waiting for rollback…%></p>'.format(uci_apply_rollback));
var call = function(r, data, duration) {
if (r.status === 204) {
uci_status_message('warning',
'<h4><%:Configuration has been rolled back!%></h4>' +
'<p><%:The device could not be reached within %d seconds after applying the pending changes, which caused the configuration to be rolled back for safety reasons. If you believe that the configuration changes are correct nonetheless, perform an unchecked configuration apply. Alternatively, you can dismiss this warning and edit changes before attempting to apply again, or revert all pending changes to keep the currently working configuration state.%></p>'.format(uci_apply_rollback) +
'<p><%:The device could not be reached within %d seconds after applying the pending changes, which caused the configuration to be rolled back for safety reasons. If you believe that the configuration changes are correct nonetheless, proceed by applying anyway. Alternatively, you can dismiss this warning and edit changes before attempting to apply again, or revert all pending changes to keep the currently working configuration state.%></p>'.format(uci_apply_rollback) +
'<div class="right">' +
'<input type="button" class="btn" onclick="uci_status_message(false)" value="<%:Dismiss%>" /> ' +
'<input type="button" class="btn cbi-button-action important" onclick="uci_revert()" value="<%:Revert changes%>" /> ' +
'<input type="button" class="btn cbi-button-negative important" onclick="uci_apply(false)" value="<%:Apply unchecked%>" />' +
'<input type="button" class="btn cbi-button-negative important" onclick="uci_apply(false)" value="<%:Apply anyway%>" />' +
'</div>');
return;
@ -126,6 +74,7 @@
var call = function(r, data, duration) {
if (Date.now() >= deadline) {
window.clearTimeout(tt);
uci_rollback(checked);
return;
}
@ -133,7 +82,7 @@
var indicator = document.querySelector('.uci_change_indicator');
if (indicator) indicator.style.display = 'none';
uci_status_message('notice', '<%:Configuration has been applied.%>');
uci_status_message('notice', '<p><%:Configuration has been applied.%></p>');
window.clearTimeout(tt);
window.setTimeout(function() {
@ -156,9 +105,8 @@
var tick = function() {
var now = Date.now();
uci_status_message('notice',
'<img src="<%=resource%>/icons/loading.gif" alt="" style="vertical-align:middle" /> ' +
'<%:Waiting for configuration to get applied… %ds%>'.format(Math.max(Math.floor((deadline - Date.now()) / 1000), 0)));
uci_status_message('notice spinning',
'<p><%:Waiting for configuration to be applied… %ds%></p>'.format(Math.max(Math.floor((deadline - Date.now()) / 1000), 0)));
if (now >= deadline)
return;
@ -174,9 +122,7 @@
}
function uci_apply(checked) {
uci_status_message('notice',
'<img src="<%=resource%>/icons/loading.gif" alt="" style="vertical-align:middle" /> ' +
'<%:Starting configuration apply…%>');
uci_status_message('notice spinning', '<p><%:Starting configuration apply…%></p>');
xhr.post('<%=url("admin/uci")%>/' + (checked ? 'apply_rollback' : 'apply_unchecked'), uci_apply_auth, function(r, tok) {
if (r.status === (checked ? 200 : 204)) {
@ -186,7 +132,7 @@
uci_confirm(checked, Date.now() + uci_apply_rollback * 1000);
}
else if (checked && r.status === 204) {
uci_status_message('notice', '<%:There are no changes to apply.%>');
uci_status_message('notice', '<p><%:There are no changes to apply.%></p>');
window.setTimeout(function() {
<% if redirect_ok then -%>
location.href = decodeURIComponent('<%=luci.util.urlencode(redirect_ok)%>');
@ -196,20 +142,18 @@
}, uci_apply_display * 1000);
}
else {
uci_status_message('warning', '<%_Apply request failed with status <code>%h</code>%>'.format(r.responseText || r.statusText || r.status));
uci_status_message('warning', '<p><%_Apply request failed with status <code>%h</code>%></p>'.format(r.responseText || r.statusText || r.status));
window.setTimeout(function() { uci_status_message(false); }, uci_apply_display * 1000);
}
});
}
function uci_revert() {
uci_status_message('notice',
'<img src="<%=resource%>/icons/loading.gif" alt="" style="vertical-align:middle" /> ' +
'<%:Reverting configuration…%>');
uci_status_message('notice spinning', '<p><%:Reverting configuration…%></p>');
xhr.post('<%=url("admin/uci/revert")%>', uci_apply_auth, function(r) {
if (r.status === 200) {
uci_status_message('notice', '<%:Changes have been reverted.%>');
uci_status_message('notice', '<p><%:Changes have been reverted.%></p>');
window.setTimeout(function() {
<% if redirect_ok then -%>
location.href = decodeURIComponent('<%=luci.util.urlencode(redirect_ok)%>');
@ -219,7 +163,7 @@
}, uci_apply_display * 1000);
}
else {
uci_status_message('warning', '<%_Revert request failed with status <code>%h</code>%>'.format(r.statusText || r.status));
uci_status_message('warning', '<p><%_Revert request failed with status <code>%h</code>%></p>'.format(r.statusText || r.status));
window.setTimeout(function() { uci_status_message(false); }, uci_apply_display * 1000);
}
});

View file

@ -1,8 +1,10 @@
<% local v = self:cfgvalue(section) -%>
<%+cbi/valueheader%>
<input class="cbi-input-text" type="text"<%= attr("value", v) .. attr("name", cbid) .. attr("id", cbid) %> />
<script type="text/javascript">
cbi_init()
cbi_browser_init('<%=cbid%>', '<%=resource%>', '<%=url('admin/filebrowser')%>'<%=self.default_path and ", '"..self.default_path.."'"%>);
</script>
<input class="cbi-input-text" type="text"<%=
attr("id", cbid) ..
attr("name", cbid) ..
attr("value", self:cfgvalue(section) or self.default) ..
attr("data-browser", self.default_path or "")
%> />
<%+cbi/valuefooter%>

View file

@ -6,7 +6,7 @@
<div class="td cbi-value-field<% if self.error and self.error[section] then %> cbi-value-error<% end %>"<%=
attr("data-name", self.option) ..
ifattr(ftype and #ftype > 0, "data-type", ftype) ..
ifattr(title and #title > 0, "data-title", title) ..
ifattr(descr and #descr > 0, "data-description", descr)
ifattr(title and #title > 0, "data-title", title, true) ..
ifattr(descr and #descr > 0, "data-description", descr, true)
%>>
<div id="cbi-<%=self.config.."-"..section.."-"..self.option%>" data-index="<%=self.index%>" data-depends="<%=pcdata(self:deplist2json(section))%>">

View file

@ -30,7 +30,7 @@
<li<%=
attr("data-index", i) ..
attr("data-depends", self:deplist2json(section, self.deplist[i])) ..
attr("value", key) ..
attr("data-value", key) ..
ifattr(selected[key], "selected", "selected")
%>>
<%=pcdata(self.vallist[i])%>

View file

@ -6,22 +6,8 @@
self.keylist, self.vallist,
self.datatype, self.optional or self.rmempty
})) ..
attr("data-values", luci.util.serialize_json(self:cfgvalue(section))) ..
ifattr(self.size, "data-size", self.size) ..
ifattr(self.placeholder, "data-placeholder", self.placeholder)
%>>
<%
local vals = self:cfgvalue(section) or {}
for i=1, #vals + 1 do
local val = vals[i]
if (val and #val > 0) or (i == 1) then
%>
<input class="cbi-input-text" value="<%=pcdata(val)%>" data-update="change" type="text"<%=
attr("id", cbid .. "." .. i) ..
attr("name", cbid) ..
ifattr(self.size, "size") ..
ifattr(i == 1 and self.placeholder, "placeholder", self.placeholder)
%> /><br />
<% end end %>
</div>
%>></div>
<%+cbi/valuefooter%>

View file

@ -63,7 +63,7 @@
if empty then
%>
<label class="zonebadge zonebadge-empty">
<strong><%=zone:forward():upper()%></strong>
<strong><%=def:forward():upper()%></strong>
</label>
<% end %>
</div>

View file

@ -30,7 +30,7 @@
ifattr(self.rmempty or self.optional, "optional", "optional")
%>>
<script type="item-template"><!--
<li value="{{value}}">
<li data-value="{{value}}">
<span class="zonebadge" style="background:repeating-linear-gradient(45deg,rgba(204,204,204,0.5),rgba(204,204,204,0.5) 5px,rgba(255,255,255,0.5) 5px,rgba(255,255,255,0.5) 10px)">
<strong>{{value}}:</strong><em>(<%:create%>)</em>
</span>
@ -38,7 +38,7 @@
--></script>
<ul>
<% if self.allowlocal then %>
<li value=""<%=ifattr(checked[""], "selected", "selected")%>>
<li data-value=""<%=ifattr(checked[""], "selected", "selected")%>>
<span style="background-color:<%=fwm.zone.get_color()%>" class="zonebadge">
<strong><%:Device%></strong>
<% if self.allowany and self.allowlocal then -%>
@ -48,14 +48,14 @@
</span>
</li>
<% elseif self.widget ~= "checkbox" and (self.rmempty or self.optional) then %>
<li value=""<%=ifattr(checked[""], "selected", "selected")%>>
<li data-value=""<%=ifattr(checked[""], "selected", "selected")%>>
<span class="zonebadge">
<em><%:unspecified%></em>
</span>
</li>
<% end %>
<% if self.allowany then %>
<li value="*"<%=ifattr(checked["*"], "selected", "selected")%>>
<li data-value="*"<%=ifattr(checked["*"], "selected", "selected")%>>
<span style="background-color:<%=fwm.zone.get_color()%>" class="zonebadge">
<strong><%:Any zone%></strong>
<% if self.allowany and self.allowlocal then %>(<%:forward%>)<% end %>
@ -67,7 +67,7 @@
if zone:name() ~= self.exclude then
selected = selected or (value == zone:name())
%>
<li<%=attr("value", zone:name()) .. ifattr(checked[zone:name()], "selected", "selected")%>>
<li<%=attr("data-value", zone:name()) .. ifattr(checked[zone:name()], "selected", "selected")%>>
<span style="background-color:<%=zone:get_color()%>" class="zonebadge">
<strong><%=zone:name()%>:</strong>
<%-
@ -94,11 +94,11 @@
<% end end %>
<% if self.widget ~= "checkbox" and not self.nocreate then %>
<li value="-">
<li data-value="-">
<span class="zonebadge">
<em><%:create%>:</em>
<input type="password" style="display:none" />
<input class="create-item-input" type="text" />
<input class="create-item-input" type="text" data-type="and(uciname,maxlength(11))" data-optional="true" />
</span>
</li>
<% end %>

View file

@ -0,0 +1,27 @@
<%+cbi/valueheader%>
<script type="text/javascript">
function switchToCIDRList(ev) {
var input = ev.target.previousElementSibling,
usecidr = document.getElementById(input.id + '_usecidr');
ev.preventDefault();
usecidr.value = '1';
cbi_d_update();
}
</script>
<input data-update="change"<%=
attr("id", cbid) ..
attr("name", cbid) ..
attr("type", "text") ..
attr("class", "cbi-input-text") ..
attr("value", self:cfgvalue(section) or self.default) ..
ifattr(self.size, "size") ..
ifattr(self.placeholder, "placeholder") ..
ifattr(self.datatype, "data-type", self.datatype) ..
ifattr(self.datatype, "data-optional", self.optional or self.rmempty) ..
ifattr(self.combobox_manual, "data-manual", self.combobox_manual) ..
ifattr(#self.keylist > 0, "data-choices", { self.keylist, self.vallist })
%> /><!--
--><button class="cbi-button cbi-button-neutral" title="<%:Switch to CIDR list notation%>" aria-label="<%:Switch to CIDR list notation%>" onclick="switchToCIDRList(event)"></button>
<%+cbi/valuefooter%>

View file

@ -3,25 +3,25 @@
<%- end end -%>
<div class="cbi-map" id="cbi-<%=self.config%>">
<% if self.title and #self.title > 0 then %><h2 name="content"><%=self.title%></h2><% end %>
<% if self.description and #self.description > 0 then %><div class="cbi-map-descr"><%=self.description%></div><% end %>
<% if self.title and #self.title > 0 then %>
<h2 name="content"><%=self.title%></h2>
<% end %>
<% if self.description and #self.description > 0 then %>
<div class="cbi-map-descr"><%=self.description%></div>
<% end %>
<% if self.tabbed then %>
<ul class="cbi-tabmenu map">
<%- self.selected_tab = luci.http.formvalue("tab.m-" .. self.config) %>
<% for i, section in ipairs(self.children) do %>
<%- if not self.selected_tab then self.selected_tab = section.sectiontype end %>
<li id="tab.m-<%=self.config%>.<%=section.section or section.sectiontype%>" class="cbi-tab<%=(section.sectiontype == self.selected_tab) and '' or '-disabled'%>">
<a onclick="this.blur(); return cbi_t_switch('m-<%=self.config%>', '<%=section.section or section.sectiontype%>')" href="<%=REQUEST_URI%>?tab.m-<%=self.config%>=<%=section.section or section.sectiontype%>"><%=section.title or section.section or section.sectiontype %></a>
<% if section.sectiontype == self.selected_tab then %><input type="hidden" id="tab.m-<%=self.config%>" name="tab.m-<%=self.config%>" value="<%=section.section or section.sectiontype%>" /><% end %>
</li>
<div>
<% for i, section in ipairs(self.children) do
tab = section.section or section.sectiontype %>
<div class="cbi-tabcontainer"<%=
attr("id", "container.m-%s.%s" %{ self.config, tab }) ..
attr("data-tab", tab) ..
attr("data-tab-title", section.title or tab)
%>>
<% section:render() %>
</div>
<% end %>
</ul>
<% for i, section in ipairs(self.children) do %>
<div class="cbi-tabcontainer" id="container.m-<%=self.config%>.<%=section.section or section.sectiontype%>"<% if section.sectiontype ~= self.selected_tab then %> style="display:none"<% end %>>
<% section:render() %>
</div>
<script type="text/javascript">cbi_t_add('m-<%=self.config%>', '<%=section.section or section.sectiontype%>')</script>
<% end %>
</div>
<% if not self.save then -%>
<div class="cbi-section-error">

View file

@ -41,13 +41,13 @@
<input type="hidden" name="<%=cbeid%>" value="1" />
<div class="cbi-dropdown" display-items="5" placeholder="<%:-- please select -- %>"<%=
<div class="cbi-dropdown" display-items="10" placeholder="<%:-- please select -- %>"<%=
attr("name", cbid) ..
ifattr(self.widget == "checkbox", "multiple", "multiple") ..
ifattr(self.widget == "checkbox", "optional", "optional")
%>>
<script type="item-template"><!--
<li value="{{value}}">
<li data-value="{{value}}">
<img title="<%:Custom Interface%>: &quot;{{value}}&quot;" src="<%=resource%>/icons/ethernet_disabled.png" />
<span class="hide-open">{{value}}</span>
<span class="hide-close"><%:Custom Interface%>: "{{value}}"</span>
@ -61,7 +61,7 @@
iface:name() ~= self.exclude
then %>
<li<%=
attr("value", iface:name()) ..
attr("data-value", iface:name()) ..
ifattr(checked[iface:name()], "selected", "selected")
%>>
<img<%=attr("title", iface:get_i18n())%> src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
@ -78,7 +78,7 @@
</li>
<% end end %>
<% if not self.nocreate then %>
<li value="">
<li data-value="">
<img title="<%:Custom Interface%>" src="<%=resource%>/icons/ethernet_disabled.png" />
<span><%:Custom Interface%>:</span>
<input type="password" style="display:none" />

View file

@ -20,13 +20,13 @@
end
-%>
<div class="cbi-dropdown" display-items="5" placeholder="<%:-- please select -- %>"<%=
<div class="cbi-dropdown" display-items="10" placeholder="<%:-- please select -- %>"<%=
attr("name", cbid) ..
ifattr(self.widget == "checkbox", "multiple", "multiple") ..
ifattr(self.widget == "checkbox", "optional", "optional")
%>>
<script type="item-template"><!--
<li value="{{value}}">
<li data-value="{{value}}">
<span class="ifacebadge" style="background:repeating-linear-gradient(45deg,rgba(204,204,204,0.5),rgba(204,204,204,0.5) 5px,rgba(255,255,255,0.5) 5px,rgba(255,255,255,0.5) 10px)">
{{value}}: <em>(<%:create%>)</em>
</span>
@ -34,7 +34,7 @@
--></script>
<ul>
<% if self.widget ~= "checkbox" then %>
<li value=""<%= ifattr(not value, "selected", "selected") %>>
<li data-value=""<%= ifattr(not value, "selected", "selected") %>>
<em><%:unspecified%></em>
</li>
<% end %>
@ -44,7 +44,7 @@
(net:name() ~= self.exclude) and
(not self.novirtual or not net:is_virtual())
then %>
<li<%= attr("value", net:name()) .. ifattr(checked[net:name()], "selected", "selected") %>>
<li<%= attr("data-value", net:name()) .. ifattr(checked[net:name()], "selected", "selected") %>>
<span class="ifacebadge"><%=net:name()%>:
<%
local empty = true
@ -63,7 +63,7 @@
<% end end %>
<% if not self.nocreate then %>
<li value="-"<%= ifattr(not value and self.widget ~= "checkbox", "selected", "selected") %>>
<li data-value="-"<%= ifattr(not value and self.widget ~= "checkbox", "selected", "selected") %>>
<em>
<%- if self.widget == "checkbox" then -%>
<%:create:%>

View file

@ -11,7 +11,6 @@
<input type="submit" class="cbi-button" name="cbi.rns.<%=self.config%>.<%=section%>" value="<%:Delete%>" />
</div>
<%- end %>
<%+cbi/tabmenu%>
<div class="cbi-section-node<% if self.tabs then %> cbi-section-node-tabbed<% end %>" id="cbi-<%=self.config%>-<%=section%>">
<%+cbi/ucisection%>
</div>

View file

@ -1,7 +1,14 @@
<% for tab, data in pairs(self.tabs) do %>
<div class="cbi-tabcontainer" id="container.<%=self.config%>.<%=section%>.<%=tab%>"<% if tab ~= self.selected_tab then %> style="display:none"<% end %>>
<% if data.description then %><div class="cbi-tab-descr"><%=data.description%></div><% end %>
<% for _, tab in ipairs(self.tab_names) do data = self.tabs[tab] %>
<div class="cbi-tabcontainer"<%=
attr("id", "container.%s.%s.%s" %{ self.config, section, tab }) ..
attr("data-tab", tab) ..
attr("data-tab-title", data.title) ..
attr("data-tab-active", tostring(tab == self.selected_tab))
%>>
<% if data.description then %>
<div class="cbi-tab-descr"><%=data.description%></div>
<% end %>
<% self:render_tab(tab, section, scope or {}) %>
</div>
<script type="text/javascript">cbi_t_add('<%=self.config%>.<%=section%>', '<%=tab%>')</script>
<% end %>

View file

@ -1,12 +0,0 @@
<%- if self.tabs then %>
<ul class="cbi-tabmenu">
<%- self.selected_tab = luci.http.formvalue("tab." .. self.config .. "." .. section) %>
<%- for _, tab in ipairs(self.tab_names) do if #self.tabs[tab].childs > 0 then %>
<%- if not self.selected_tab then self.selected_tab = tab end %>
<li id="tab.<%=self.config%>.<%=section%>.<%=tab%>" class="cbi-tab<%=(tab == self.selected_tab) and '' or '-disabled'%>">
<a onclick="this.blur(); return cbi_t_switch('<%=self.config%>.<%=section%>', '<%=tab%>')" href="<%=REQUEST_URI%>?tab.<%=self.config%>.<%=section%>=<%=tab%>"><%=self.tabs[tab].title%></a>
<% if tab == self.selected_tab then %><input type="hidden" id="tab.<%=self.config%>.<%=section%>" name="tab.<%=self.config%>.<%=section%>" value="<%=tab%>" /><% end %>
</li>
<% end end -%>
</ul>
<% end -%>

View file

@ -127,7 +127,7 @@ end
section = k
local sectionname = striptags((type(self.sectiontitle) == "function") and self:sectiontitle(section) or k)
local sectiontitle = ifattr(sectionname and (not self.anonymous or self.sectiontitle), "data-title", sectionname)
local sectiontitle = ifattr(sectionname and (not self.anonymous or self.sectiontitle), "data-title", sectionname, true)
local colorclass = (self.extedit or self.rowcolors) and rowstyle() or ""
local scope = {
valueheader = "cbi/cell_valueheader",

View file

@ -2,6 +2,11 @@
<% if self.title and #self.title > 0 then -%>
<legend><%=self.title%></legend>
<%- end %>
<% if self.error_msg and #self.error_msg > 0 then -%>
<div class="cbi-section-error">
<%=self.error_msg%>
</div>
<%- end %>
<% if self.description and #self.description > 0 then -%>
<div class="cbi-section-descr"><%=self.description%></div>
<%- end %>
@ -18,8 +23,6 @@
<h3><%=section:upper()%></h3>
<%- end %>
<%+cbi/tabmenu%>
<div class="cbi-section-node<% if self.tabs then %> cbi-section-node-tabbed<% end %>" id="cbi-<%=self.config%>-<%=section%>">
<%+cbi/ucisection%>
</div>

View file

@ -1,6 +1,6 @@
<%+cbi/valueheader%>
<%- if self.password then -%>
<input type="password" style="position:absolute; left:-4000px"<%=
<input type="password" style="position:absolute; left:-1000px" aria-hidden="true" tabindex="-1"<%=
attr("name", "password." .. cbid)
%> />
<%- end -%>
@ -21,6 +21,6 @@
ifattr(#self.keylist > 0, "data-choices", { self.keylist, self.vallist })
%> />
<%- if self.password then -%>
<div class="cbi-button cbi-button-neutral" title="<%:Reveal/hide password%>" onclick="var e = this.previousElementSibling; e.type = (e.type === 'password') ? 'text' : 'password'"></div>
<button class="cbi-button cbi-button-neutral" title="<%:Reveal/hide password%>" aria-label="<%:Reveal/hide password%>" onclick="var e = this.previousElementSibling; e.type = (e.type === 'password') ? 'text' : 'password'; event.preventDefault()"></button>
<% end %>
<%+cbi/valuefooter%>

View file

@ -0,0 +1,168 @@
<%+cbi/valueheader%>
<script type="text/javascript">//<![CDATA[
var freqlist = <%= luci.http.write_json(self.iwinfo.freqlist) %>;
var hwmodes = <%= luci.http.write_json(self.iwinfo.hwmodelist or {}) %>;
var htmodes = <%= luci.http.write_json(self.iwinfo.htmodelist) %>;
var channels = {
'11g': [
'auto', 'auto', true
],
'11a': [
'auto', 'auto', true
]
};
for (var i = 0; i < freqlist.length; i++)
channels[(freqlist[i].mhz > 2484) ? '11a' : '11g'].push(
freqlist[i].channel,
'%d (%d MHz)'.format(freqlist[i].channel, freqlist[i].mhz),
!freqlist[i].restricted
);
var modes = [
'', 'Legacy', true,
'n', 'N', hwmodes.n,
'ac', 'AC', hwmodes.ac
];
var htmodes = {
'': [
'', '-', true
],
'n': [
'HT20', '20 MHz', htmodes.HT20,
'HT40', '40 MHz', htmodes.HT40
],
'ac': [
'VHT20', '20 MHz', htmodes.VHT20,
'VHT40', '40 MHz', htmodes.VHT40,
'VHT80', '80 MHz', htmodes.VHT80,
'VHT160', '160 MHz', htmodes.VHT160
]
};
var bands = {
'': [
'11g', '2.4 GHz', (channels['11g'].length > 3),
'11a', '5 GHz', (channels['11a'].length > 3)
],
'n': [
'11g', '2.4 GHz', (channels['11g'].length > 3),
'11a', '5 GHz', (channels['11a'].length > 3)
],
'ac': [
'11a', '5 GHz', true
]
};
function cbi_set_values(sel, vals)
{
if (sel.vals)
sel.vals.selected = sel.selectedIndex;
while (sel.options[0])
sel.remove(0);
for (var i = 0; vals && i < vals.length; i += 3)
{
if (!vals[i+2])
continue;
var opt = document.createElement('option');
opt.value = vals[i+0];
opt.text = vals[i+1];
sel.add(opt);
}
if (!isNaN(vals.selected))
sel.selectedIndex = vals.selected;
sel.parentNode.style.display = (sel.options.length <= 1) ? 'none' : '';
sel.vals = vals;
}
function cbi_toggle_wifi_mode(id)
{
cbi_toggle_wifi_htmode(id);
cbi_toggle_wifi_band(id);
}
function cbi_toggle_wifi_htmode(id)
{
var mode = document.getElementById(id + '.mode');
var bwdt = document.getElementById(id + '.htmode');
cbi_set_values(bwdt, htmodes[mode.value]);
}
function cbi_toggle_wifi_band(id)
{
var mode = document.getElementById(id + '.mode');
var band = document.getElementById(id + '.band');
cbi_set_values(band, bands[mode.value]);
cbi_toggle_wifi_channel(id);
}
function cbi_toggle_wifi_channel(id)
{
var band = document.getElementById(id + '.band');
var chan = document.getElementById(id + '.channel');
cbi_set_values(chan, channels[band.value]);
}
function cbi_init_wifi(id)
{
var mode = document.getElementById(id + '.mode');
var band = document.getElementById(id + '.band');
var chan = document.getElementById(id + '.channel');
var bwdt = document.getElementById(id + '.htmode');
cbi_set_values(mode, modes);
if (/VHT20|VHT40|VHT80|VHT160/.test(<%= luci.http.write_json(self.map:get(section, "htmode")) %>))
mode.value = 'ac';
else if (/HT20|HT40/.test(<%= luci.http.write_json(self.map:get(section, "htmode")) %>))
mode.value = 'n';
else
mode.value = '';
cbi_toggle_wifi_mode(id);
if (/a/.test(<%= luci.http.write_json(self.map:get(section, "hwmode")) %>))
band.value = '11a';
else
band.value = '11g';
cbi_toggle_wifi_band(id);
bwdt.value = <%= luci.http.write_json(self.map:get(section, "htmode")) %>;
chan.value = <%= luci.http.write_json(self.map:get(section, "channel")) %>;
}
//]]></script>
<label style="float:left; margin-right:3px">
<%:Mode%><br />
<select style="width:auto" id="<%= cbid %>.mode" name="<%= cbid %>.mode" onchange="cbi_toggle_wifi_mode('<%= cbid %>')"></select>
</label>
<label style="float:left; margin-right:3px">
<%:Band%><br />
<select style="width:auto" id="<%= cbid %>.band" name="<%= cbid %>.band" onchange="cbi_toggle_wifi_band('<%= cbid %>')"></select>
</label>
<label style="float:left; margin-right:3px">
<%:Channel%><br />
<select style="width:auto" id="<%= cbid %>.channel" name="<%= cbid %>.channel"></select>
</label>
<label style="float:left; margin-right:3px">
<%:Width%><br />
<select style="width:auto" id="<%= cbid %>.htmode" name="<%= cbid %>.htmode"></select>
</label>
<br style="clear:left" />
<script type="text/javascript">cbi_init_wifi('<%= cbid %>');</script>
<%+cbi/valuefooter%>

View file

@ -0,0 +1,11 @@
<%#
Copyright 2010 Jo-Philipp Wich <jow@openwrt.org>
Copyright 2018 Daniel F. Dickinson <cshored@thecshore.com>
Licensed to the public under the Apache License 2.0.
-%>
<%+header%>
<p>Component not present.</p>
<%+footer%>

View file

@ -10,3 +10,15 @@
luci.dispatcher.context.template_header_sent = true
end
%>
<script type="text/javascript" src="<%=resource%>/luci.js"></script>
<script type="text/javascript">
L = new LuCI(<%= luci.http.write_json({
token = token,
resource = resource,
scriptname = luci.http.getenv("SCRIPT_NAME"),
pathinfo = luci.http.getenv("PATH_INFO"),
requestpath = luci.dispatcher.context.requestpath,
pollinterval = luci.config.main.pollinterval or 5
}) %>);
</script>

View file

@ -0,0 +1,95 @@
<script type="text/javascript">//<![CDATA[
XHR.poll(-1, '<%=url('admin/dhcplease_status')%>', null,
function(x, st)
{
var tb = document.getElementById('lease_status_table');
if (st && st[0] && tb)
{
var rows = [];
for (var i = 0; i < st[0].length; i++)
{
var timestr;
if (st[0][i].expires === false)
timestr = '<em><%:unlimited%></em>';
else if (st[0][i].expires <= 0)
timestr = '<em><%:expired%></em>';
else
timestr = String.format('%t', st[0][i].expires);
rows.push([
st[0][i].hostname || '?',
st[0][i].ipaddr,
st[0][i].macaddr,
timestr
]);
}
cbi_update_table(tb, rows, '<em><%:There are no active leases.%></em>');
}
var tb6 = document.getElementById('lease6_status_table');
if (st && st[1] && tb6)
{
tb6.parentNode.style.display = 'block';
var rows = [];
for (var i = 0; i < st[1].length; i++)
{
var timestr;
if (st[1][i].expires === false)
timestr = '<em><%:unlimited%></em>';
else if (st[1][i].expires <= 0)
timestr = '<em><%:expired%></em>';
else
timestr = String.format('%t', st[1][i].expires);
var name = st[1][i].hostname,
hint = st[1][i].host_hint;
rows.push([
hint ? '%h (%h)'.format(name || '?', hint) : (name || '?'),
st[1][i].ip6addr,
st[1][i].duid,
timestr
]);
}
cbi_update_table(tb6, rows, '<em><%:There are no active leases.%></em>');
}
}
);
//]]></script>
<div class="cbi-section">
<h3><%:Active DHCP Leases%></h3>
<div class="table" id="lease_status_table">
<div class="tr table-titles">
<div class="th"><%:Hostname%></div>
<div class="th"><%:IPv4-Address%></div>
<div class="th"><%:MAC-Address%></div>
<div class="th"><%:Leasetime remaining%></div>
</div>
<div class="tr placeholder">
<div class="td"><em><%:Collecting data...%></em></div>
</div>
</div>
</div>
<div class="cbi-section" style="display:none">
<h3><%:Active DHCPv6 Leases%></h3>
<div class="table" id="lease6_status_table">
<div class="tr table-titles">
<div class="th"><%:Host%></div>
<div class="th"><%:IPv6-Address%></div>
<div class="th"><%:DUID%></div>
<div class="th"><%:Leasetime remaining%></div>
</div>
<div class="tr placeholder">
<div class="td"><em><%:Collecting data...%></em></div>
</div>
</div>
</div>

View file

@ -0,0 +1,120 @@
<%
local supports_deauth = {}
local _, v
for _, v in ipairs(luci.util.ubus()) do
local iface = v:match("^hostapd%.(.+)$")
if iface then
local funcs = luci.util.ubus(v)
if type(funcs) == "table" and funcs.del_client then
supports_deauth[iface] = true
end
end
end
%>
<script type="text/javascript">//<![CDATA[
var supports_deauth = <%= luci.http.write_json(supports_deauth) %>;
function wifirate(bss, rx) {
var p = rx ? 'rx_' : 'tx_',
s = '%.1f <%:Mbit/s%>, %d<%:MHz%>'
.format(bss[p+'rate'] / 1000, bss[p+'mhz']),
ht = bss[p+'ht'], vht = bss[p+'vht'],
mhz = bss[p+'mhz'], nss = bss[p+'nss'],
mcs = bss[p+'mcs'], sgi = bss[p+'short_gi'];
if (ht || vht) {
if (vht) s += ', VHT-MCS %d'.format(mcs);
if (nss) s += ', VHT-NSS %d'.format(nss);
if (ht) s += ', MCS %s'.format(mcs);
if (sgi) s += ', <%:Short GI%>';
}
return s;
}
function handleDeauth(ev) {
(new XHR()).post('<%=url('admin/wireless_deauth')%>', {
token: '<%=token%>',
iface: ev.target.getAttribute('data-iface'),
bssid: ev.target.getAttribute('data-bssid')
}, function() {
ev.target.disabled = true;
});
}
XHR.poll(-1, '<%=url('admin/wireless_assoclist')%>', null,
function(x, st)
{
var tb = document.getElementById('wifi_assoclist_table');
if (st && tb)
{
var rows = [];
st.forEach(function(bss) {
var icon;
var q = (-1 * (bss.noise - bss.signal)) / 5;
if (q < 1)
icon = "<%=resource%>/icons/signal-0.png";
else if (q < 2)
icon = "<%=resource%>/icons/signal-0-25.png";
else if (q < 3)
icon = "<%=resource%>/icons/signal-25-50.png";
else if (q < 4)
icon = "<%=resource%>/icons/signal-50-75.png";
else
icon = "<%=resource%>/icons/signal-75-100.png";
rows.push([
'<span class="ifacebadge" title="%q"><img src="<%=resource%>/icons/wifi.png" /> <a href="%s">%h</a><small>&#160;(%h)</small></span>'.format(
bss.radio,
bss.link,
bss.name,
bss.ifname),
bss.bssid,
bss.host_hint ? '%h (%h)'.format(bss.host_name || '?', bss.host_hint) : (bss.host_name || '?'),
'<span class="ifacebadge" title="<%:Signal%>: %d <%:dBm%> / <%:Noise%>: %d <%:dBm%> / <%:SNR%>: %d"><img src="%s" /> %d / %d <%:dBm%></span>'.format(
bss.signal,
bss.noise,
bss.signal - bss.noise,
icon,
bss.signal,
bss.noise),
E('span', {}, [
E('span', wifirate(bss, true)),
E('br'),
E('span', wifirate(bss, false))
]),
supports_deauth[bss.ifname] ? E('input', {
type: 'button',
class: 'cbi-button cbi-button-remove',
value: '<%:Disconnect%>',
'data-bssid': bss.bssid,
'data-iface': bss.ifname,
click: handleDeauth
}) : '-'
]);
});
cbi_update_table(tb, rows, '<em><%:No information available%></em>');
}
}
);
//]]></script>
<div class="table" id="wifi_assoclist_table">
<div class="tr table-titles">
<div class="th nowrap"><%:Network%></div>
<div class="th hide-xs"><%:MAC-Address%></div>
<div class="th nowrap"><%:Host%></div>
<div class="th nowrap"><%:Signal%> / <%:Noise%></div>
<div class="th nowrap"><%:RX Rate%> / <%:TX Rate%></div>
<% if next(supports_deauth) then %>
<div class="th right"><%:Disconnect%></div>
<% end %>
</div>
<div class="tr placeholder">
<div class="td"><em><%:Collecting data...%></em></div>
</div>
</div>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -3,9 +3,9 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Cache-Control" content="no-cache" />
<meta http-equiv="refresh" content="0; URL=/cgi-bin/luci" />
<meta http-equiv="refresh" content="0; URL=/cgi-bin/luci/" />
</head>
<body style="background-color: white">
<a style="color: black; font-family: arial, helvetica, sans-serif;" href="/cgi-bin/luci">LuCI - Lua Configuration Interface</a>
<a style="color: black; font-family: arial, helvetica, sans-serif;" href="/cgi-bin/luci/">LuCI - Lua Configuration Interface</a>
</body>
</html>

View file

@ -4,6 +4,9 @@
clean:
rm -f po2lmo parser.so version.lua *.o
jsmin: jsmin.o
$(CC) $(LDFLAGS) -o $@ $^
po2lmo: po2lmo.o template_lmo.o
$(CC) $(LDFLAGS) -o $@ $^

292
luci-base/src/jsmin.c Normal file
View file

@ -0,0 +1,292 @@
/* jsmin.c
2011-09-30
Copyright (c) 2002 Douglas Crockford (www.crockford.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
static int theA;
static int theB;
static int theLookahead = EOF;
/* isAlphanum -- return true if the character is a letter, digit, underscore,
dollar sign, or non-ASCII character.
*/
static int
isAlphanum(int c)
{
return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'Z') || c == '_' || c == '$' || c == '\\' ||
c > 126);
}
/* get -- return the next character from stdin. Watch out for lookahead. If
the character is a control character, translate it to a space or
linefeed.
*/
static int
get()
{
int c = theLookahead;
theLookahead = EOF;
if (c == EOF) {
c = getc(stdin);
}
if (c >= ' ' || c == '\n' || c == EOF) {
return c;
}
if (c == '\r') {
return '\n';
}
return ' ';
}
/* peek -- get the next character without getting it.
*/
static int
peek()
{
theLookahead = get();
return theLookahead;
}
/* next -- get the next character, excluding comments. peek() is used to see
if a '/' is followed by a '/' or '*'.
*/
static int
next()
{
int c = get();
if (c == '/') {
switch (peek()) {
case '/':
for (;;) {
c = get();
if (c <= '\n') {
return c;
}
}
case '*':
get();
for (;;) {
switch (get()) {
case '*':
if (peek() == '/') {
get();
return ' ';
}
break;
case EOF:
fprintf(stderr, "Error: JSMIN Unterminated comment.\n");
exit(1);
}
}
default:
return c;
}
}
return c;
}
/* action -- do something! What you do is determined by the argument:
1 Output A. Copy B to A. Get the next B.
2 Copy B to A. Get the next B. (Delete A).
3 Get the next B. (Delete B).
action treats a string as a single character. Wow!
action recognizes a regular expression if it is preceded by ( or , or =.
*/
static void
action(int d)
{
switch (d) {
case 1:
putc(theA, stdout);
case 2:
theA = theB;
if (theA == '\'' || theA == '"' || theA == '`') {
for (;;) {
putc(theA, stdout);
theA = get();
if (theA == theB) {
break;
}
if (theA == '\\') {
putc(theA, stdout);
theA = get();
}
if (theA == EOF) {
fprintf(stderr, "Error: JSMIN unterminated string literal.");
exit(1);
}
}
}
case 3:
theB = next();
if (theB == '/' && (theA == '(' || theA == ',' || theA == '=' ||
theA == ':' || theA == '[' || theA == '!' ||
theA == '&' || theA == '|' || theA == '?' ||
theA == '{' || theA == '}' || theA == ';' ||
theA == '\n')) {
putc(theA, stdout);
putc(theB, stdout);
for (;;) {
theA = get();
if (theA == '[') {
for (;;) {
putc(theA, stdout);
theA = get();
if (theA == ']') {
break;
}
if (theA == '\\') {
putc(theA, stdout);
theA = get();
}
if (theA == EOF) {
fprintf(stderr,
"Error: JSMIN unterminated set in Regular Expression literal.\n");
exit(1);
}
}
} else if (theA == '/') {
break;
} else if (theA =='\\') {
putc(theA, stdout);
theA = get();
}
if (theA == EOF) {
fprintf(stderr,
"Error: JSMIN unterminated Regular Expression literal.\n");
exit(1);
}
putc(theA, stdout);
}
theB = next();
}
}
}
/* jsmin -- Copy the input to the output, deleting the characters which are
insignificant to JavaScript. Comments will be removed. Tabs will be
replaced with spaces. Carriage returns will be replaced with linefeeds.
Most spaces and linefeeds will be removed.
*/
static void
jsmin()
{
theA = '\n';
action(3);
while (theA != EOF) {
switch (theA) {
case ' ':
if (isAlphanum(theB)) {
action(1);
} else {
action(2);
}
break;
case '\n':
switch (theB) {
case '{':
case '[':
case '(':
case '+':
case '-':
action(1);
break;
case ' ':
action(3);
break;
default:
if (isAlphanum(theB)) {
action(1);
} else {
action(2);
}
}
break;
default:
switch (theB) {
case ' ':
if (isAlphanum(theA)) {
action(1);
break;
}
action(3);
break;
case '\n':
switch (theA) {
case '}':
case ']':
case ')':
case '+':
case '-':
case '"':
case '\'':
case '`':
action(1);
break;
default:
if (isAlphanum(theA)) {
action(1);
} else {
action(3);
}
}
break;
default:
action(1);
break;
}
}
}
}
/* main -- Output any command line arguments as comments
and then minify the input.
*/
extern int
main(int argc, char* argv[])
{
int i;
for (i = 1; i < argc; i += 1) {
fprintf(stdout, "// %s\n", argv[i]);
}
jsmin();
return 0;
}

View file

@ -46,14 +46,14 @@ uint32_t sfh_hash(const char *data, int len)
switch (rem) {
case 3: hash += sfh_get16(data);
hash ^= hash << 16;
hash ^= data[sizeof(uint16_t)] << 18;
hash ^= (signed char)data[sizeof(uint16_t)] << 18;
hash += hash >> 11;
break;
case 2: hash += sfh_get16(data);
hash ^= hash << 11;
hash += hash >> 17;
break;
case 1: hash += *data;
case 1: hash += (signed char)*data;
hash ^= hash << 10;
hash += hash >> 1;
}
@ -216,7 +216,7 @@ int lmo_load_catalog(const char *lang, const char *dir)
if (!_lmo_active_catalog)
_lmo_active_catalog = cat;
return 0;
return cat->archives ? 0 : -1;
err:
if (dh) closedir(dh);
@ -301,6 +301,20 @@ int lmo_translate(const char *key, int keylen, char **out, int *outlen)
return -1;
}
void lmo_iterate(lmo_iterate_cb_t cb, void *priv)
{
unsigned int i;
lmo_entry_t *e;
lmo_archive_t *ar;
if (!_lmo_active_catalog)
return;
for (ar = _lmo_active_catalog->archives; ar; ar = ar->next)
for (i = 0, e = &ar->index[0]; i < ar->length; e = &ar->index[++i])
cb(ntohl(e->key_id), ar->mmap + ntohl(e->offset), ntohl(e->length), priv);
}
void lmo_close_catalog(const char *lang)
{
lmo_archive_t *ar, *next;

View file

@ -73,6 +73,7 @@ struct lmo_catalog {
typedef struct lmo_catalog lmo_catalog_t;
typedef void (*lmo_iterate_cb_t)(uint32_t, const char *, int, void *);
uint32_t sfh_hash(const char *data, int len);
uint32_t lmo_canon_hash(const char *data, int len);
@ -87,6 +88,7 @@ extern lmo_catalog_t *_lmo_active_catalog;
int lmo_load_catalog(const char *lang, const char *dir);
int lmo_change_catalog(const char *lang);
int lmo_translate(const char *key, int keylen, char **out, int *outlen);
void lmo_iterate(lmo_iterate_cb_t cb, void *priv);
void lmo_close_catalog(const char *lang);
#endif

View file

@ -129,6 +129,24 @@ static int template_L_change_catalog(lua_State *L) {
return 1;
}
static void template_L_get_translations_cb(uint32_t key, const char *val, int len, void *priv) {
lua_State *L = priv;
char hex[9];
luaL_checktype(L, 1, LUA_TFUNCTION);
snprintf(hex, sizeof(hex), "%08x", key);
lua_pushvalue(L, 1);
lua_pushstring(L, hex);
lua_pushlstring(L, val, len);
lua_call(L, 2, 0);
}
static int template_L_get_translations(lua_State *L) {
lmo_iterate(template_L_get_translations_cb, L);
return 0;
}
static int template_L_translate(lua_State *L) {
size_t len;
char *tr;
@ -168,6 +186,7 @@ static const luaL_reg R[] = {
{ "load_catalog", template_L_load_catalog },
{ "close_catalog", template_L_close_catalog },
{ "change_catalog", template_L_change_catalog },
{ "get_translations", template_L_get_translations },
{ "translate", template_L_translate },
{ "hash", template_L_hash },
{ NULL, NULL }

View file

@ -258,7 +258,7 @@ static int _validate_utf8(unsigned char **s, int l, struct template_buffer *buf)
break;
}
/* advance beyound the last found valid continuation char */
/* advance beyond the last found valid continuation char */
o = v;
ptr += v;
}