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.
*/
var cbi_d = [];
+var cbi_t = [];
+var cbi_c = [];
function cbi_d_add(field, dep, next) {
var obj = document.getElementById(field);
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;
}
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() {
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();
}
});
}
}
+
+
+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';
+ }
+}
+
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 )
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
-- 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
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)
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
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
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
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()
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
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
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
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
self.tag_reqerror = {}
self.tag_error = {}
self.deps = {}
+ self.subdeps = {}
--self.cast = "string"
self.track_missing = false
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
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
-%>
<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()
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 />
<%#
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.
<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>
--- /dev/null
+<%#
+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 %>
--- /dev/null
+<%#
+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 -%>
<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>