luci-0.9: backport CBI from trunk, adds tab support, auto-hiding of commit notifications and better layout control
This commit is contained in:
parent
2c59a118ed
commit
3441fe8c82
7 changed files with 272 additions and 27 deletions
|
@ -2,7 +2,7 @@
|
|||
LuCI - Lua Configuration Interface
|
||||
|
||||
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
|
||||
Copyright 2008-2009 Jo-Philipp Wich <xm@subsignal.org>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -14,6 +14,8 @@
|
|||
*/
|
||||
|
||||
var cbi_d = [];
|
||||
var cbi_t = [];
|
||||
var cbi_c = [];
|
||||
|
||||
function cbi_d_add(field, dep, next) {
|
||||
var obj = document.getElementById(field);
|
||||
|
@ -43,7 +45,18 @@ function cbi_d_checkvalue(target, ref) {
|
|||
var t = document.getElementById(target);
|
||||
var value;
|
||||
|
||||
if (!t || !t.value) {
|
||||
if (!t) {
|
||||
var tl = document.getElementsByName(target);
|
||||
|
||||
if( tl.length > 0 && tl[0].type == 'radio' )
|
||||
for( var i = 0; i < tl.length; i++ )
|
||||
if( tl[i].checked ) {
|
||||
value = tl[i].value;
|
||||
break;
|
||||
}
|
||||
|
||||
value = value ? value : "";
|
||||
} else if (!t.value) {
|
||||
value = "";
|
||||
} else {
|
||||
value = t.value;
|
||||
|
@ -57,15 +70,26 @@ function cbi_d_checkvalue(target, ref) {
|
|||
}
|
||||
|
||||
function cbi_d_check(deps) {
|
||||
var reverse;
|
||||
var def = false;
|
||||
for (var i=0; i<deps.length; i++) {
|
||||
var istat = true
|
||||
var istat = true;
|
||||
reverse = false;
|
||||
for (var j in deps[i]) {
|
||||
istat = (istat && cbi_d_checkvalue(j, deps[i][j]))
|
||||
if (j == "!reverse") {
|
||||
reverse = true;
|
||||
} else if (j == "!default") {
|
||||
def = true;
|
||||
istat = false;
|
||||
} else {
|
||||
istat = (istat && cbi_d_checkvalue(j, deps[i][j]))
|
||||
}
|
||||
}
|
||||
if (istat) {
|
||||
return true
|
||||
return !reverse;
|
||||
}
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
function cbi_d_update() {
|
||||
|
@ -78,16 +102,25 @@ function cbi_d_update() {
|
|||
|
||||
if (node && node.parentNode && !cbi_d_check(entry.deps)) {
|
||||
node.parentNode.removeChild(node);
|
||||
state = (state || !node.parentNode)
|
||||
state = true;
|
||||
if( entry.parent )
|
||||
cbi_c[entry.parent]--;
|
||||
} else if ((!node || !node.parentNode) && cbi_d_check(entry.deps)) {
|
||||
if (!next) {
|
||||
parent.appendChild(entry.node);
|
||||
} else {
|
||||
next.parentNode.insertBefore(entry.node, next);
|
||||
}
|
||||
state = (state || (node && node.parentNode))
|
||||
state = true;
|
||||
if( entry.parent )
|
||||
cbi_c[entry.parent]++;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry.parent) {
|
||||
cbi_t_update();
|
||||
}
|
||||
|
||||
if (state) {
|
||||
cbi_d_update();
|
||||
}
|
||||
|
@ -219,3 +252,50 @@ function cbi_hijack_forms(layer, win, fail, load) {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function cbi_t_add(section, tab) {
|
||||
var t = document.getElementById('tab.' + section + '.' + tab);
|
||||
var c = document.getElementById('container.' + section + '.' + tab);
|
||||
|
||||
if( t && c ) {
|
||||
cbi_t[section] = (cbi_t[section] || [ ]);
|
||||
cbi_t[section][tab] = { 'tab': t, 'container': c, 'cid': c.id };
|
||||
}
|
||||
}
|
||||
|
||||
function cbi_t_switch(section, tab) {
|
||||
if( cbi_t[section] && cbi_t[section][tab] ) {
|
||||
var o = cbi_t[section][tab];
|
||||
var h = document.getElementById('tab.' + section);
|
||||
for( var tid in cbi_t[section] ) {
|
||||
var o2 = cbi_t[section][tid];
|
||||
if( o.tab.id != o2.tab.id ) {
|
||||
o2.tab.className = o2.tab.className.replace(/(^| )cbi-tab( |$)/, " cbi-tab-disabled ");
|
||||
o2.container.style.display = 'none';
|
||||
}
|
||||
else {
|
||||
if(h) h.value = tab;
|
||||
o2.tab.className = o2.tab.className.replace(/(^| )cbi-tab-disabled( |$)/, " cbi-tab ");
|
||||
o2.container.style.display = 'block';
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function cbi_t_update() {
|
||||
for( var sid in cbi_t )
|
||||
for( var tid in cbi_t[sid] )
|
||||
if( cbi_c[cbi_t[sid][tid].cid] == 0 ) {
|
||||
cbi_t[sid][tid].tab.style.display = 'none';
|
||||
}
|
||||
else if( cbi_t[sid][tid].tab && cbi_t[sid][tid].tab.style.display == 'none' ) {
|
||||
cbi_t[sid][tid].tab.style.display = '';
|
||||
|
||||
var t = cbi_t[sid][tid].tab;
|
||||
window.setTimeout(function() { t.className = t.className.replace(/ cbi-tab-highlighted/g, '') }, 750);
|
||||
cbi_t[sid][tid].tab.className += ' cbi-tab-highlighted';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -230,7 +230,7 @@ function Node._i18n(self, config, section, option, title, description)
|
|||
|
||||
local key = config and config:gsub("[^%w]+", "") or ""
|
||||
|
||||
if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
|
||||
if section then key = key .. "_" .. section:lower():gsub("[^%w]+", "") end
|
||||
if option then key = key .. "_" .. tostring(option):lower():gsub("[^%w]+", "") end
|
||||
|
||||
self.title = title or luci.i18n.translate( key, option or section or config )
|
||||
|
@ -238,6 +238,25 @@ function Node._i18n(self, config, section, option, title, description)
|
|||
end
|
||||
end
|
||||
|
||||
-- hook helper
|
||||
function Node._run_hook(self, hook)
|
||||
if type(self[hook]) == "function" then
|
||||
return self[hook](self)
|
||||
end
|
||||
end
|
||||
|
||||
function Node._run_hooks(self, ...)
|
||||
local f
|
||||
local r = false
|
||||
for _, f in ipairs(arg) do
|
||||
if type(self[f]) == "function" then
|
||||
self[f](self)
|
||||
r = true
|
||||
end
|
||||
end
|
||||
return r
|
||||
end
|
||||
|
||||
-- Prepare nodes
|
||||
function Node.prepare(self, ...)
|
||||
for k, child in ipairs(self.children) do
|
||||
|
@ -357,6 +376,7 @@ end
|
|||
-- Use optimized UCI writing
|
||||
function Map.parse(self, readinput, ...)
|
||||
self.readinput = (readinput ~= false)
|
||||
self:_run_hooks("on_parse")
|
||||
|
||||
if self:formvalue("cbi.skip") then
|
||||
self.state = FORM_SKIP
|
||||
|
@ -370,14 +390,17 @@ function Map.parse(self, readinput, ...)
|
|||
self.uci:save(config)
|
||||
end
|
||||
if self:submitstate() and ((not self.proceed and self.flow.autoapply) or luci.http.formvalue("cbi.apply")) then
|
||||
self:_run_hooks("on_before_commit")
|
||||
for i, config in ipairs(self.parsechain) do
|
||||
self.uci:commit(config)
|
||||
|
||||
-- Refresh data because commit changes section names
|
||||
self.uci:load(config)
|
||||
end
|
||||
self:_run_hooks("on_commit", "on_after_commit", "on_before_apply")
|
||||
if self.apply_on_parse then
|
||||
self.uci:apply(self.parsechain)
|
||||
self:_run_hooks("on_apply", "on_after_apply")
|
||||
else
|
||||
self._apply = function()
|
||||
local cmd = self.uci:apply(self.parsechain, true)
|
||||
|
@ -413,11 +436,13 @@ function Map.parse(self, readinput, ...)
|
|||
end
|
||||
|
||||
function Map.render(self, ...)
|
||||
self:_run_hooks("on_init")
|
||||
Node.render(self, ...)
|
||||
if self._apply then
|
||||
local fp = self._apply()
|
||||
fp:read("*a")
|
||||
fp:close()
|
||||
self:_run_hooks("on_apply")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -506,16 +531,13 @@ function Delegator.__init__(self, ...)
|
|||
self.pageaction = false
|
||||
self.readinput = true
|
||||
self.allow_reset = false
|
||||
self.allow_cancel = false
|
||||
self.allow_back = false
|
||||
self.allow_finish = false
|
||||
self.template = "cbi/delegator"
|
||||
end
|
||||
|
||||
function Delegator.set(self, name, node)
|
||||
if type(node) == "table" and getmetatable(node) == nil then
|
||||
node = Compound(unpack(node))
|
||||
end
|
||||
assert(type(node) == "function" or instanceof(node, Compound), "Invalid")
|
||||
assert(not self.nodes[name], "Duplicate entry")
|
||||
|
||||
self.nodes[name] = node
|
||||
|
@ -527,9 +549,9 @@ function Delegator.add(self, name, node)
|
|||
end
|
||||
|
||||
function Delegator.insert_after(self, name, after)
|
||||
local n = #self.chain
|
||||
local n = #self.chain + 1
|
||||
for k, v in ipairs(self.chain) do
|
||||
if v == state then
|
||||
if v == after then
|
||||
n = k + 1
|
||||
break
|
||||
end
|
||||
|
@ -555,10 +577,30 @@ function Delegator.set_route(self, ...)
|
|||
end
|
||||
|
||||
function Delegator.get(self, name)
|
||||
return self.nodes[name]
|
||||
local node = self.nodes[name]
|
||||
|
||||
if type(node) == "string" then
|
||||
node = load(node, name)
|
||||
end
|
||||
|
||||
if type(node) == "table" and getmetatable(node) == nil then
|
||||
node = Compound(unpack(node))
|
||||
end
|
||||
|
||||
return node
|
||||
end
|
||||
|
||||
function Delegator.parse(self, ...)
|
||||
if self.allow_cancel and Map.formvalue(self, "cbi.cancel") then
|
||||
if self:_run_hooks("on_cancel") then
|
||||
return FORM_DONE
|
||||
end
|
||||
end
|
||||
|
||||
if not Map.formvalue(self, "cbi.delg.current") then
|
||||
self:_run_hooks("on_init")
|
||||
end
|
||||
|
||||
local newcurrent
|
||||
self.chain = self.chain or self:get_chain()
|
||||
self.current = self.current or self:get_active()
|
||||
|
@ -586,14 +628,20 @@ function Delegator.parse(self, ...)
|
|||
|
||||
if not Map.formvalue(self, "cbi.submit") then
|
||||
return FORM_NODATA
|
||||
elseif not newcurrent or not self:get(newcurrent) then
|
||||
return FORM_DONE
|
||||
elseif stat > FORM_PROCEED
|
||||
and (not newcurrent or not self:get(newcurrent)) then
|
||||
return self:_run_hook("on_done") or FORM_DONE
|
||||
else
|
||||
self.current = newcurrent
|
||||
self.current = newcurrent or self.current
|
||||
self.active = self:get(self.current)
|
||||
if type(self.active) ~= "function" then
|
||||
self.active:parse(false)
|
||||
return FROM_PROCEED
|
||||
self.active:populate_delegator(self)
|
||||
local stat = self.active:parse(false)
|
||||
if stat == FORM_SKIP then
|
||||
return self:parse(...)
|
||||
else
|
||||
return FORM_PROCEED
|
||||
end
|
||||
else
|
||||
return self:parse(...)
|
||||
end
|
||||
|
@ -659,6 +707,10 @@ function SimpleForm.parse(self, readinput, ...)
|
|||
return FORM_SKIP
|
||||
end
|
||||
|
||||
if self:formvalue("cbi.cancel") and self:_run_hooks("on_cancel") then
|
||||
return FORM_DONE
|
||||
end
|
||||
|
||||
if self:submitstate() then
|
||||
Node.parse(self, 1, ...)
|
||||
end
|
||||
|
@ -781,6 +833,19 @@ function AbstractSection.__init__(self, map, sectiontype, ...)
|
|||
self.dynamic = false
|
||||
end
|
||||
|
||||
-- Define a tab for the section
|
||||
function AbstractSection.tab(self, tab, title, desc)
|
||||
self.tabs = self.tabs or { }
|
||||
self.tab_names = self.tab_names or { }
|
||||
|
||||
self.tab_names[#self.tab_names+1] = tab
|
||||
self.tabs[tab] = {
|
||||
title = title,
|
||||
description = desc,
|
||||
childs = { }
|
||||
}
|
||||
end
|
||||
|
||||
-- Appends a new option
|
||||
function AbstractSection.option(self, class, option, ...)
|
||||
-- Autodetect from UVL
|
||||
|
@ -812,6 +877,31 @@ function AbstractSection.option(self, class, option, ...)
|
|||
end
|
||||
end
|
||||
|
||||
-- Appends a new tabbed option
|
||||
function AbstractSection.taboption(self, tab, ...)
|
||||
|
||||
assert(tab and self.tabs and self.tabs[tab],
|
||||
"Cannot assign option to not existing tab %q" % tostring(tab))
|
||||
|
||||
local l = self.tabs[tab].childs
|
||||
local o = AbstractSection.option(self, ...)
|
||||
|
||||
if o then l[#l+1] = o end
|
||||
|
||||
return o
|
||||
end
|
||||
|
||||
-- Render a single tab
|
||||
function AbstractSection.render_tab(self, tab, ...)
|
||||
|
||||
assert(tab and self.tabs and self.tabs[tab],
|
||||
"Cannot render not existing tab %q" % tostring(tab))
|
||||
|
||||
for _, node in ipairs(self.tabs[tab].childs) do
|
||||
node:render(...)
|
||||
end
|
||||
end
|
||||
|
||||
-- Parse optional options
|
||||
function AbstractSection.parse_optionals(self, section)
|
||||
if not self.optional then
|
||||
|
@ -1215,6 +1305,7 @@ function AbstractValue.__init__(self, map, section, option, ...)
|
|||
self.tag_reqerror = {}
|
||||
self.tag_error = {}
|
||||
self.deps = {}
|
||||
self.subdeps = {}
|
||||
--self.cast = "string"
|
||||
|
||||
self.track_missing = false
|
||||
|
@ -1356,6 +1447,7 @@ function AbstractValue.render(self, s, scope)
|
|||
scope.section = s
|
||||
scope.cbid = self:cbid(s)
|
||||
scope.striptags = luci.util.striptags
|
||||
scope.pcdata = luci.util.pcdata
|
||||
|
||||
scope.ifattr = function(cond,key,val)
|
||||
if cond then
|
||||
|
@ -1544,7 +1636,7 @@ function ListValue.value(self, key, val, ...)
|
|||
table.insert(self.vallist, tostring(val))
|
||||
|
||||
for i, deps in ipairs({...}) do
|
||||
table.insert(self.deps, {add = "-"..key, deps=deps})
|
||||
self.subdeps[#self.subdeps + 1] = {add = "-"..key, deps=deps}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -14,10 +14,25 @@ $Id$
|
|||
-%>
|
||||
|
||||
<div class="cbi-map" id="cbi-<%=self.config%>">
|
||||
<h2><a id="content" name="content"><%=self.title%></a></h2>
|
||||
<div class="cbi-map-descr"><%=self.description%></div>
|
||||
<% if self.title and #self.title > 0 then %>
|
||||
<h2>
|
||||
<%
|
||||
if self.breadcrumb then
|
||||
local elem
|
||||
for _, elem in ipairs(self.breadcrumb) do
|
||||
-%>
|
||||
<a href="<%=luci.util.pcdata(elem[1])%>"><%=luci.util.pcdata(elem[2])%></a> »
|
||||
<%
|
||||
end
|
||||
end
|
||||
-%>
|
||||
<a id="content" name="content"><%=self.title%></a>
|
||||
</h2>
|
||||
<% end %>
|
||||
|
||||
<% if self.description and #self.description > 0 then %><div class="cbi-map-descr"><%=self.description%></div><% end %>
|
||||
<%- if self._apply then -%>
|
||||
<fieldset class="cbi-section">
|
||||
<fieldset class="cbi-section" id="cbi-apply-<%=self.config%>">
|
||||
<legend><%:cbi_applying%></legend>
|
||||
<ul class="cbi-apply"><%-
|
||||
local fp = self._apply()
|
||||
|
@ -30,6 +45,12 @@ $Id$
|
|||
fp:close()
|
||||
-%></ul>
|
||||
</fieldset>
|
||||
<script type="text/javascript">
|
||||
window.setTimeout(function() {
|
||||
var e = document.getElementById('cbi-apply-<%=self.config%>');
|
||||
if(e) e.style.display = 'none';
|
||||
}, 2000);
|
||||
</script>
|
||||
<%- end -%>
|
||||
<%- self:render_children() %>
|
||||
<br />
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<%#
|
||||
LuCI - Lua Configuration Interface
|
||||
Copyright 2008 Steven Barth <steven@midlink.org>
|
||||
Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
|
||||
Copyright 2008-2009 Jo-Philipp Wich <xm@subsignal>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -26,6 +26,7 @@ $Id$
|
|||
<input type="submit" name="cbi.rns.<%=self.config%>.<%=section%>" value="<%:cbi_del%>" />
|
||||
</div>
|
||||
<%- end %>
|
||||
<%+cbi/tabmenu%>
|
||||
<div class="cbi-section-node" id="cbi-<%=self.config%>-<%=section%>">
|
||||
<%+cbi/ucisection%>
|
||||
</div>
|
||||
|
|
21
libs/cbi/luasrc/view/cbi/tabcontainer.htm
Normal file
21
libs/cbi/luasrc/view/cbi/tabcontainer.htm
Normal file
|
@ -0,0 +1,21 @@
|
|||
<%#
|
||||
LuCI - Lua Configuration Interface
|
||||
Copyright 2009 Jo-Philipp Wich <xm@subsignal.org>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
$Id: tabcontainer.htm 5269 2009-08-16 03:29:46Z jow $
|
||||
|
||||
-%>
|
||||
|
||||
<% for tab, data in pairs(self.tabs) do %>
|
||||
<div 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 %>
|
||||
<% self:render_tab(tab, section, scope or {}) %>
|
||||
</div>
|
||||
<script type="text/javascript">cbi_t_add('<%=self.config%>.<%=section%>', '<%=tab%>')</script>
|
||||
<% end %>
|
27
libs/cbi/luasrc/view/cbi/tabmenu.htm
Normal file
27
libs/cbi/luasrc/view/cbi/tabmenu.htm
Normal file
|
@ -0,0 +1,27 @@
|
|||
<%#
|
||||
LuCI - Lua Configuration Interface
|
||||
Copyright 2009 Jo-Philipp Wich <xm@subsignal.org>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
$Id: tabmenu.htm 5398 2009-10-10 22:18:50Z jow $
|
||||
|
||||
-%>
|
||||
|
||||
<%- 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 %>
|
||||
<script type="text/javascript">cbi_c['container.<%=self.config%>.<%=section%>.<%=tab%>'] = <%=#self.tabs[tab].childs%>;</script>
|
||||
<%- 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 -%>
|
|
@ -24,12 +24,15 @@ $Id$
|
|||
<input type="submit" name="cbi.rts.<%=self.config%>.<%=k%>" value="<%:cbi_del%>" />
|
||||
</div>
|
||||
<%- end %>
|
||||
<% section = k; isempty = false %>
|
||||
|
||||
<%- section = k; isempty = false -%>
|
||||
|
||||
<% if not self.anonymous then -%>
|
||||
<h3><%=k:upper()%></h3>
|
||||
<h3><%=section:upper()%></h3>
|
||||
<%- end %>
|
||||
|
||||
<%+cbi/tabmenu%>
|
||||
|
||||
<fieldset class="cbi-section-node" id="cbi-<%=self.config%>-<%=section%>">
|
||||
<%+cbi/ucisection%>
|
||||
</fieldset>
|
||||
|
|
Loading…
Reference in a new issue