Merge pull request #626 from dangowrt/improve-wpa-eap
authorJo-Philipp Wich <jow@openwrt.org>
Tue, 19 Jan 2016 10:08:46 +0000 (11:08 +0100)
committerJo-Philipp Wich <jow@openwrt.org>
Tue, 19 Jan 2016 10:08:46 +0000 (11:08 +0100)
luci-admin-full: improve WPA(2)-Enterprise client support

24 files changed:
modules/luci-base/htdocs/luci-static/resources/cbi.js
modules/luci-base/luasrc/cbi.lua
modules/luci-base/luasrc/view/cbi/cell_valuefooter.htm
modules/luci-base/luasrc/view/cbi/cell_valueheader.htm
modules/luci-base/luasrc/view/cbi/dynlist.htm
modules/luci-base/luasrc/view/cbi/firewall_zonelist.htm
modules/luci-base/luasrc/view/cbi/footer.htm
modules/luci-base/luasrc/view/cbi/full_valuefooter.htm
modules/luci-base/luasrc/view/cbi/full_valueheader.htm
modules/luci-base/luasrc/view/cbi/fvalue.htm
modules/luci-base/luasrc/view/cbi/lvalue.htm
modules/luci-base/luasrc/view/cbi/map.htm
modules/luci-base/luasrc/view/cbi/mvalue.htm
modules/luci-base/luasrc/view/cbi/network_ifacelist.htm
modules/luci-base/luasrc/view/cbi/network_netlist.htm
modules/luci-base/luasrc/view/cbi/tabmenu.htm
modules/luci-base/luasrc/view/cbi/tblsection.htm
modules/luci-base/luasrc/view/cbi/tsection.htm
modules/luci-base/luasrc/view/cbi/tvalue.htm
modules/luci-base/luasrc/view/cbi/upload.htm
modules/luci-base/luasrc/view/cbi/value.htm
modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/wifi.lua
themes/luci-theme-bootstrap/htdocs/luci-static/bootstrap/cascade.css
themes/luci-theme-bootstrap/luasrc/view/themes/bootstrap/header.htm

index 1c4123b..2274610 100644 (file)
@@ -13,7 +13,6 @@
 
 var cbi_d = [];
 var cbi_t = [];
-var cbi_c = [];
 
 var cbi_validators = {
 
@@ -345,7 +344,7 @@ var cbi_validators = {
        {
                return (this.match(/^[0-9\*#!\.]+$/) != null);
        },
-        'timehhmmss': function()
+       'timehhmmss': function()
        {
                return (this.match(/^[0-6][0-9]:[0-6][0-9]:[0-6][0-9]$/) != null);
        },
@@ -389,12 +388,12 @@ var cbi_validators = {
 };
 
 
-function cbi_d_add(field, dep, next) {
-       var obj = document.getElementById(field);
+function cbi_d_add(field, dep, index) {
+       var obj = (typeof(field) === 'string') ? document.getElementById(field) : field;
        if (obj) {
                var entry
                for (var i=0; i<cbi_d.length; i++) {
-                       if (cbi_d[i].id == field) {
+                       if (cbi_d[i].id == obj.id) {
                                entry = cbi_d[i];
                                break;
                        }
@@ -402,10 +401,10 @@ function cbi_d_add(field, dep, next) {
                if (!entry) {
                        entry = {
                                "node": obj,
-                               "id": field,
+                               "id": obj.id,
                                "parent": obj.parentNode.id,
-                               "next": next,
-                               "deps": []
+                               "deps": [],
+                               "index": index
                        };
                        cbi_d.unshift(entry);
                }
@@ -468,24 +467,28 @@ function cbi_d_update() {
        var state = false;
        for (var i=0; i<cbi_d.length; i++) {
                var entry = cbi_d[i];
-               var next  = document.getElementById(entry.next)
-               var node  = document.getElementById(entry.id)
-               var parent = document.getElementById(entry.parent)
+               var node  = document.getElementById(entry.id);
+               var parent = document.getElementById(entry.parent);
 
                if (node && node.parentNode && !cbi_d_check(entry.deps)) {
                        node.parentNode.removeChild(node);
                        state = true;
-                       if( entry.parent )
-                               cbi_c[entry.parent]--;
                } else if ((!node || !node.parentNode) && cbi_d_check(entry.deps)) {
+                       var next = undefined;
+
+                       for (next = parent.firstChild; next; next = next.nextSibling) {
+                               if (next.getAttribute && parseInt(next.getAttribute('data-index'), 10) > entry.index) {
+                                       break;
+                               }
+                       }
+
                        if (!next) {
                                parent.appendChild(entry.node);
                        } else {
-                               next.parentNode.insertBefore(entry.node, next);
+                               parent.insertBefore(entry.node, next);
                        }
+
                        state = true;
-                       if( entry.parent )
-                               cbi_c[entry.parent]++;
                }
        }
 
@@ -499,6 +502,31 @@ function cbi_d_update() {
        }
 }
 
+function cbi_init() {
+       var nodes = document.querySelectorAll('[data-depends]');
+
+       for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
+               var index = parseInt(node.getAttribute('data-index'), 10);
+               var depends = JSON.parse(node.getAttribute('data-depends'));
+               if (!isNaN(index) && depends.length > 0) {
+                       for (var alt = 0; alt < depends.length; alt++) {
+                               cbi_d_add(node, depends[alt], index);
+                       }
+               }
+       }
+
+       nodes = document.querySelectorAll('[data-update]');
+
+       for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
+               var events = node.getAttribute('data-update').split(' ');
+               for (var j = 0, event; (event = events[j]) !== undefined; j++) {
+                       cbi_bind(node, event, cbi_d_update);
+               }
+       }
+
+       cbi_d_update();
+}
+
 function cbi_bind(obj, type, callback, mode) {
        if (!obj.addEventListener) {
                obj.attachEvent('on' + type,
@@ -517,7 +545,7 @@ function cbi_bind(obj, type, callback, mode) {
        return obj;
 }
 
-function cbi_combobox(id, values, def, man) {
+function cbi_combobox(id, values, def, man, focus) {
        var selid = "cbi.combobox." + id;
        if (document.getElementById(selid)) {
                return
@@ -577,6 +605,7 @@ function cbi_combobox(id, values, def, man) {
        cbi_bind(sel, "change", function() {
                if (sel.selectedIndex == sel.options.length - 1) {
                        obj.style.display = "inline";
+                       sel.blur();
                        sel.parentNode.removeChild(sel);
                        obj.focus();
                } else {
@@ -591,16 +620,18 @@ function cbi_combobox(id, values, def, man) {
        })
 
        // Retrigger validation in select
-       sel.focus();
-       sel.blur();
+       if (focus) {
+               sel.focus();
+               sel.blur();
+       }
 }
 
 function cbi_combobox_init(id, values, def, man) {
        var obj = document.getElementById(id);
        cbi_bind(obj, "blur", function() {
-               cbi_combobox(id, values, def, man)
+               cbi_combobox(id, values, def, man, true);
        });
-       cbi_combobox(id, values, def, man);
+       cbi_combobox(id, values, def, man, false);
 }
 
 function cbi_filebrowser(id, url, defpath) {
@@ -876,27 +907,6 @@ function cbi_dynlist_init(name, respath, datatype, optional, url, defpath, choic
        cbi_dynlist_redraw(NaN, -1, -1);
 }
 
-//Hijacks the CBI form to send via XHR (requires Prototype)
-function cbi_hijack_forms(layer, win, fail, load) {
-       var forms = layer.getElementsByTagName('form');
-       for (var i=0; i<forms.length; i++) {
-               $(forms[i]).observe('submit', function(event) {
-                       // Prevent the form from also submitting the regular way
-                       event.stop();
-
-                       // Submit via XHR
-                       event.element().request({
-                               onSuccess: win,
-                               onFailure: fail
-                       });
-
-                       if (load) {
-                               load();
-                       }
-               });
-       }
-}
-
 
 function cbi_t_add(section, tab) {
        var t = document.getElementById('tab.' + section + '.' + tab);
@@ -935,22 +945,23 @@ 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;
+                       var c = cbi_t[sid][tid].container;
 
-                               var t = cbi_t[sid][tid].tab;
+                       if (!c.firstElementChild) {
+                               t.style.display = 'none';
+                       }
+                       else if (t.style.display == 'none') {
+                               t.style.display = '';
                                t.className += ' cbi-tab-highlighted';
                                hl_tabs.push(t);
                        }
 
-                       cbi_tag_last(cbi_t[sid][tid].container);
+                       cbi_tag_last(c);
                        updated = true;
                }
 
-       if( hl_tabs.length > 0 )
+       if (hl_tabs.length > 0)
                window.setTimeout(function() {
                        for( var i = 0; i < hl_tabs.length; i++ )
                                hl_tabs[i].className = hl_tabs[i].className.replace(/ cbi-tab-highlighted/g, '');
index 738431b..4dc1f2a 100644 (file)
@@ -262,6 +262,7 @@ function Node.render_children(self, ...)
        local k, node
        for k, node in ipairs(self.children) do
                node.last_child = (k == #self.children)
+               node.index = k
                node:render(...)
        end
 end
@@ -885,6 +886,7 @@ function AbstractSection.render_tab(self, tab, ...)
        local k, node
        for k, node in ipairs(self.tabs[tab].childs) do
                node.last_child = (k == #self.tabs[tab].childs)
+               node.index = k
                node:render(...)
        end
 end
@@ -1294,7 +1296,6 @@ function AbstractValue.__init__(self, map, section, option, ...)
        self.tag_reqerror = {}
        self.tag_error = {}
        self.deps = {}
-       self.subdeps = {}
        --self.cast = "string"
 
        self.track_missing = false
@@ -1318,7 +1319,30 @@ function AbstractValue.depends(self, field, value)
                deps = field
        end
 
-       table.insert(self.deps, {deps=deps, add=""})
+       table.insert(self.deps, deps)
+end
+
+-- Serialize dependencies
+function AbstractValue.deplist2json(self, section, deplist)
+       local deps, i, d = { }
+
+       if type(self.deps) == "table" then
+               for i, d in ipairs(deplist or self.deps) do
+                       local a, k, v = { }
+                       for k, v in pairs(d) do
+                               if k:find("!", 1, true) then
+                                       a[k] = v
+                               elseif k:find(".", 1, true) then
+                                       a['cbid.%s' % k] = v
+                               else
+                                       a['cbid.%s.%s.%s' %{ self.config, section, k }] = v
+                               end
+                       end
+                       deps[#deps+1] = a
+               end
+       end
+
+       return util.serialize_json(deps)
 end
 
 -- Generates the unique CBID
@@ -1601,6 +1625,7 @@ function ListValue.__init__(self, ...)
 
        self.keylist = {}
        self.vallist = {}
+       self.deplist = {}
        self.size   = 1
        self.widget = "select"
 end
@@ -1618,10 +1643,7 @@ function ListValue.value(self, key, val, ...)
        val = val or key
        table.insert(self.keylist, tostring(key))
        table.insert(self.vallist, tostring(val))
-
-       for i, deps in ipairs({...}) do
-               self.subdeps[#self.subdeps + 1] = {add = "-"..key, deps=deps}
-       end
+       table.insert(self.deplist, {...})
 end
 
 function ListValue.validate(self, val)
index 220ebd4..786ee43 100644 (file)
@@ -1,20 +1,2 @@
 </div>
-<div id="cbip-<%=self.config.."-"..section.."-"..self.option%>"></div>
 </td>
-
-<% if #self.deps > 0 then -%>
-       <script type="text/javascript">
-               <% for j, d in ipairs(self.deps) do -%>
-                       cbi_d_add("cbi-<%=self.config.."-"..section.."-"..self.option..d.add%>", {
-               <%-
-                       for k,v in pairs(d.deps) do
-               -%>
-                       <%-=string.format('"cbid.%s.%s.%s"', self.config, section, k) .. ":" .. string.format("%q", v)-%>
-                       <%-if next(d.deps, k) then-%>,<%-end-%>
-               <%-
-                       end
-               -%>
-                       }, "cbip-<%=self.config.."-"..section.."-"..self.option%>");
-               <%- end %>
-       </script>
-<%- end %>
index 9e2e145..9c9c218 100644 (file)
@@ -1,2 +1,2 @@
 <td class="cbi-value-field<% if self.error and self.error[section] then %> cbi-value-error<% end %>">
-<div id="cbi-<%=self.config.."-"..section.."-"..self.option%>">
+<div id="cbi-<%=self.config.."-"..section.."-"..self.option%>" data-index="<%=self.index%>" data-depends="<%=pcdata(self:deplist2json(section))%>">
index e936c0c..80cbee8 100644 (file)
@@ -6,7 +6,7 @@
                local val = vals[i]
                if (val and #val > 0) or (i == 1) then
 %>
-       <input class="cbi-input-text" value="<%=pcdata(val)%>" onchange="cbi_d_update(this.id)" type="text"<%=
+       <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 />
index 7973437..b3b4540 100644 (file)
@@ -27,7 +27,7 @@
 <ul style="margin:0; list-style-type:none; text-align:left">
        <% if self.allowlocal then %>
        <li style="padding:0.5em">
-               <input class="cbi-input-radio" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "_empty") .. attr("name", cbid) .. attr("value", "") .. ifattr(checked[""], "checked", "checked")%> /> &#160;
+               <input class="cbi-input-radio" data-update="click change"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "_empty") .. attr("name", cbid) .. attr("value", "") .. ifattr(checked[""], "checked", "checked")%> /> &#160;
                <label<%=attr("for", cbid .. "_empty")%> style="background-color:<%=fwm.zone.get_color()%>" class="zonebadge">
                        <strong><%:Device%></strong>
                        <% if self.allowany and self.allowlocal then %>(<%:input%>)<% end %>
@@ -36,7 +36,7 @@
        <% end %>
        <% if self.allowany then %>
        <li style="padding:0.5em">
-               <input class="cbi-input-radio" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "_any") .. attr("name", cbid) .. attr("value", "*") .. ifattr(checked["*"], "checked", "checked")%> /> &#160;
+               <input class="cbi-input-radio" data-update="click change"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "_any") .. attr("name", cbid) .. attr("value", "*") .. ifattr(checked["*"], "checked", "checked")%> /> &#160;
                <label<%=attr("for", cbid .. "_any")%> style="background-color:<%=fwm.zone.get_color()%>" class="zonebadge">
                        <strong><%:Any zone%></strong>
                        <% if self.allowany and self.allowlocal then %>(<%:forward%>)<% end %>
@@ -49,7 +49,7 @@
                                selected = selected or (value == zone:name())
        %>
        <li style="padding:0.5em">
-               <input class="cbi-input-radio" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "." .. zone:name()) .. attr("name", cbid) .. attr("value", zone:name()) .. ifattr(checked[zone:name()], "checked", "checked")%> /> &#160;
+               <input class="cbi-input-radio" data-update="click change"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "." .. zone:name()) .. attr("name", cbid) .. attr("value", zone:name()) .. ifattr(checked[zone:name()], "checked", "checked")%> /> &#160;
                <label<%=attr("for", cbid .. "." .. zone:name())%> style="background-color:<%=zone:get_color()%>" class="zonebadge">
                        <strong><%=zone:name()%>:</strong>
                        <%
@@ -77,7 +77,7 @@
 
        <% if self.widget ~= "checkbox" and not self.nocreate then %>
        <li style="padding:0.5em">
-               <input class="cbi-input-radio" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)" type="radio"<%=attr("id", cbid .. "_new") .. attr("name", cbid) .. attr("value", "-") .. ifattr(not selected, "checked", "checked")%> /> &#160;
+               <input class="cbi-input-radio" data-update="click change" type="radio"<%=attr("id", cbid .. "_new") .. attr("name", cbid) .. attr("value", "-") .. ifattr(not selected, "checked", "checked")%> /> &#160;
                <div onclick="document.getElementById('<%=cbid%>_new').checked=true" class="zonebadge" style="background-color:<%=fwm.zone.get_color()%>">
                        <em><%:unspecified -or- create:%>&#160;</em>
                        <input type="text"<%=attr("name", cbid .. ".newzone") .. ifattr(not selected, "value", luci.http.formvalue(cbid .. ".newzone") or self.default)%> onfocus="document.getElementById('<%=cbid%>_new').checked=true" />
index 115250a..42bf98b 100644 (file)
@@ -19,7 +19,7 @@
                        <input class="cbi-button cbi-button-reset" type="button" value="<%:Reset%>" onclick="location.href='<%=REQUEST_URI%>'" />
                <% end %>
 
-               <script type="text/javascript">cbi_d_update();</script>
+               <script type="text/javascript">cbi_init();</script>
        </div>
        <%- end -%>
 </form>
index 4876fbc..f780936 100644 (file)
        </div>
        <%- end -%>
 </div>
-
-
-<% if #self.deps > 0 or #self.subdeps > 0 then -%>
-       <script type="text/javascript" id="cbip-<%=self.config.."-"..section.."-"..self.option%>">
-               <% for j, d in ipairs(self.subdeps) do -%>
-                       cbi_d_add("cbi-<%=self.config.."-"..section.."-"..self.option..d.add%>", {
-               <%-
-                       for k,v in pairs(d.deps) do
-                               local depk
-                               if k:find("!", 1, true) then
-                                       depk = string.format('"%s"', k)
-                               elseif k:find(".", 1, true) then
-                                       depk = string.format('"cbid.%s"', k)
-                               else
-                                       depk = string.format('"cbid.%s.%s.%s"', self.config, section, k)
-                               end
-               -%>
-                       <%-= depk .. ":" .. string.format("%q", v)-%>
-                       <%-if next(d.deps, k) then-%>,<%-end-%>
-               <%-
-                       end
-               -%>
-                       }, "cbip-<%=self.config.."-"..section.."-"..self.option..d.add%>");
-               <%- end %>
-               <% for j, d in ipairs(self.deps) do -%>
-                       cbi_d_add("cbi-<%=self.config.."-"..section.."-"..self.option..d.add%>", {
-               <%-
-                       for k,v in pairs(d.deps) do
-                               local depk
-                               if k:find("!", 1, true) then
-                                       depk = string.format('"%s"', k)
-                               elseif k:find(".", 1, true) then
-                                       depk = string.format('"cbid.%s"', k)
-                               else
-                                       depk = string.format('"cbid.%s.%s.%s"', self.config, section, k)
-                               end
-               -%>
-                       <%-= depk .. ":" .. string.format("%q", v)-%>
-                       <%-if next(d.deps, k) then-%>,<%-end-%>
-               <%-
-                       end
-               -%>
-                       }, "cbip-<%=self.config.."-"..section.."-"..self.option..d.add%>");
-               <%- end %>
-       </script>
-<%- end %>
index aaf0854..10a5543 100644 (file)
@@ -1,4 +1,4 @@
-<div class="cbi-value<% if self.error and self.error[section] then %> cbi-value-error<% end %><% if self.last_child then %> cbi-value-last<% end %>" id="cbi-<%=self.config.."-"..section.."-"..self.option%>">
+<div class="cbi-value<% if self.error and self.error[section] then %> cbi-value-error<% end %><% if self.last_child then %> cbi-value-last<% end %>" id="cbi-<%=self.config.."-"..section.."-"..self.option%>" data-index="<%=self.index%>" data-depends="<%=pcdata(self:deplist2json(section))%>">
        <%- if self.title and #self.title > 0 then -%>
        <label class="cbi-value-title"<%= attr("for", cbid) %>>
        <%- if self.titleref then -%><a title="<%=self.titledesc or translate('Go to relevant configuration page')%>" class="cbi-title-ref" href="<%=self.titleref%>"><%- end -%>
index a1e0808..5eddcf2 100644 (file)
@@ -2,7 +2,7 @@
        <input type="hidden" value="1"<%=
                attr("name", "cbi.cbe." .. self.config .. "." .. section .. "." .. self.option)
        %> />
-       <input class="cbi-input-checkbox" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)" type="checkbox"<%=
+       <input class="cbi-input-checkbox" data-update="click change" type="checkbox"<%=
                attr("id", cbid) .. attr("name", cbid) .. attr("value", self.enabled or 1) ..
                ifattr((self:cfgvalue(section) or self.default) == self.enabled, "checked", "checked")
        %> />
index 8cc086d..ac63e9e 100644 (file)
@@ -1,8 +1,8 @@
 <%+cbi/valueheader%>
 <% if self.widget == "select" then %>
-       <select class="cbi-input-select" onchange="cbi_d_update(this.id)"<%= attr("id", cbid) .. attr("name", cbid) .. ifattr(self.size, "size") %>>
+       <select class="cbi-input-select" data-update="change"<%= attr("id", cbid) .. attr("name", cbid) .. ifattr(self.size, "size") %>>
        <% for i, key in pairs(self.keylist) do -%>
-               <option id="cbi-<%=self.config.."-"..section.."-"..self.option.."-"..key%>"<%= attr("value", key) .. ifattr(tostring(self:cfgvalue(section) or self.default) == key, "selected", "selected") %>><%=striptags(self.vallist[i])%></option>
+               <option id="cbi-<%=self.config.."-"..section.."-"..self.option.."-"..key%>"<%= attr("value", key) .. ifattr(tostring(self:cfgvalue(section) or self.default) == key, "selected", "selected") .. attr("data-index", i) .. attr("data-depends", self:deplist2json(section, self.deplist[i])) %>><%=striptags(self.vallist[i])%></option>
        <%- end %>
        </select>
 <% elseif self.widget == "radio" then
@@ -10,8 +10,8 @@
        for i, key in pairs(self.keylist) do
        c = c + 1
 %>
-       <input class="cbi-input-radio" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)" type="radio"<%= attr("id", cbid..c) .. attr("name", cbid) .. attr("value", key) .. ifattr((self:cfgvalue(section) or self.default) == key, "checked", "checked") %> />
-       <label<%= attr("for", cbid..c) %>><%=self.vallist[i]%></label>
+       <input class="cbi-input-radio" data-update="click change" type="radio"<%= attr("id", cbid.."-"..key) .. attr("name", cbid) .. attr("value", key) .. ifattr((self:cfgvalue(section) or self.default) == key, "checked", "checked") .. attr("data-index", i) .. attr("data-depends", self:deplist2json(section, self.deplist[i])) %> />
+       <label<%= attr("for", cbid.."-"..key) %>><%=self.vallist[i]%></label>
 <% if c == self.size then c = 0 %><% if self.orientation == "horizontal" then %>&#160;<% else %><br /><% end %>
 <% end end %>
 <% end %>
index 7f256ad..48403f1 100644 (file)
        <%- if firstmap and applymap then cbi_apply_xhr(self.config, parsechain, redirect) end -%>
 
        <% if self.tabbed then %>
-               <ul class="cbi-tabmenu">
+               <ul class="cbi-tabmenu map">
                        <%- self.selected_tab = luci.http.formvalue("tab.m-" .. self.config) %>
                        <% for i, section in ipairs(self.children) do %>
-                               <script type="text/javascript">cbi_c['container.m-<%=self.config%>.<%=section.sectiontype%>'] = 1;</script>
                                <%- if not self.selected_tab then self.selected_tab = section.sectiontype end %>
                                <li id="tab.m-<%=self.config%>.<%=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.sectiontype%>')" href="<%=REQUEST_URI%>?tab.m-<%=self.config%>=<%=section.sectiontype%>"><%=section.title or section.sectiontype %></a>
index 5d09261..79950ce 100644 (file)
@@ -1,9 +1,9 @@
 <% local v = self:valuelist(section) or {} -%>
 <%+cbi/valueheader%>
 <% if self.widget == "select" then %>
-       <select class="cbi-input-select" multiple="multiple" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%= attr("name", cbid) .. ifattr(self.size, "size") %>>
+       <select class="cbi-input-select" multiple="multiple" data-update="click change"<%= attr("name", cbid) .. ifattr(self.size, "size") %>>
        <% for i, key in pairs(self.keylist) do -%>
-               <option<%= attr("value", key) .. ifattr(luci.util.contains(v, key), "selected", "selected") %>><%=striptags(self.vallist[i])%></option>
+               <option<%= attr("id", cbid.."-"..key) .. attr("value", key) .. ifattr(luci.util.contains(v, key), "selected", "selected") .. attr("data-index", i) .. attr("data-depends", self:deplist2json(section, self.deplist[i])) %>><%=striptags(self.vallist[i])%></option>
        <%- end %>
        </select>
 <% elseif self.widget == "checkbox" then
@@ -11,8 +11,8 @@
        for i, key in pairs(self.keylist) do
        c = c + 1
 %>
-       <input class="cbi-input-checkbox" type="checkbox" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%= attr("id", cbid..c) .. attr("name", cbid) .. attr("value", key) .. ifattr(luci.util.contains(v, key), "checked", "checked") %> />
-       <label<%= attr("for", cbid..c) %>><%=self.vallist[i]%></label><% if not self.oneline then %><br /><% else %> <% end %>
+       <input class="cbi-input-checkbox" type="checkbox" data-update="click change"<%= attr("id", cbid.."-"..key) .. attr("name", cbid) .. attr("value", key) .. ifattr(luci.util.contains(v, key), "checked", "checked") .. attr("data-index", i) .. attr("data-depends", self:deplist2json(section, self.deplist[i])) %> />
+       <label<%= attr("for", cbid.."-"..key) %>><%=self.vallist[i]%></label><br />
 <% if c == self.size then c = 0 %><br />
 <% end end %>
 <% end %>
index 643d849..db61129 100644 (file)
@@ -41,7 +41,7 @@
                    iface:name() ~= self.exclude
                 then %>
        <li>
-               <input class="cbi-input-<%=self.widget or "radio"%>" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%=
+               <input class="cbi-input-<%=self.widget or "radio"%>" data-update="click change"<%=
                        attr("type", self.widget or "radio") ..
                        attr("id", cbid .. "." .. iface:name()) ..
                        attr("name", cbid) .. attr("value", iface:name()) ..
@@ -63,7 +63,7 @@
        <% end end %>
        <% if not self.nocreate then %>
        <li>
-               <input class="cbi-input-<%=self.widget or "radio"%>" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%=
+               <input class="cbi-input-<%=self.widget or "radio"%>" data-update="click change"<%=
                        attr("type", self.widget or "radio") ..
                        attr("id", cbid .. "_custom") ..
                        attr("name", cbid) ..
index 4f186ca..f8a2b72 100644 (file)
@@ -27,7 +27,7 @@
                      (not self.novirtual or not net:is_virtual())
                   then %>
        <li style="padding:0.25em 0">
-               <input class="cbi-input-<%=self.widget or "radio"%>" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%=
+               <input class="cbi-input-<%=self.widget or "radio"%>" data-update="click change"<%=
                        attr("type", self.widget or "radio") ..
                        attr("id", cbid .. "." .. net:name()) ..
                        attr("name", cbid) .. attr("value", net:name()) ..
@@ -51,7 +51,7 @@
 
        <% if not self.nocreate then %>
        <li style="padding:0.25em 0">
-               <input class="cbi-input-<%=self.widget or "radio"%>" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "_new") .. attr("name", cbid) .. attr("value", "-") .. ifattr(not value and self.widget ~= "checkbox", "checked", "checked")%> /> &#160;
+               <input class="cbi-input-<%=self.widget or "radio"%>" data-update="click change"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "_new") .. attr("name", cbid) .. attr("value", "-") .. ifattr(not value and self.widget ~= "checkbox", "checked", "checked")%> /> &#160;
                <div style="padding:0.5em; display:inline">
                        <label<%=attr("for", cbid .. "_new")%>><em>
                                <%- if self.widget == "checkbox" then -%>
@@ -65,7 +65,7 @@
        </li>
        <% elseif self.widget ~= "checkbox" and self.unspecified then %>
        <li style="padding:0.25em 0">
-               <input class="cbi-input-<%=self.widget or "radio"%>" onclick="cbi_d_update(this.id)" onchange="cbi_d_update(this.id)"<%=
+               <input class="cbi-input-<%=self.widget or "radio"%>" data-update="click change"<%=
                        attr("type", self.widget or "radio") ..
                        attr("id", cbid .. "_uns") ..
                        attr("name", cbid) ..
index b96ac9c..06c1414 100644 (file)
@@ -2,7 +2,6 @@
        <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>
index d928791..fcf2161 100644 (file)
@@ -99,7 +99,7 @@ end
                                                        %> onclick="location.href='<%=self.extedit:format(section)%>'"
                                                        <%- elseif type(self.extedit) == "function" then
                                                        %> onclick="location.href='<%=self:extedit(section)%>'"
-                                                       <%- end 
+                                                       <%- end
                                                        %> alt="<%:Edit%>" title="<%:Edit%>" />
                                                <%- end; if self.addremove then %>
                                                        <input class="cbi-button cbi-button-remove" type="submit" value="<%:Delete%>"  onclick="this.form.cbi_state='del-section'; return true" name="cbi.rts.<%=self.config%>.<%=k%>" alt="<%:Delete%>" title="<%:Delete%>" />
index 087548b..fcffbe0 100644 (file)
@@ -2,7 +2,9 @@
        <% if self.title and #self.title > 0 then -%>
                <legend><%=self.title%></legend>
        <%- end %>
-       <div class="cbi-section-descr"><%=self.description%></div>
+       <% if self.description and #self.description > 0 then -%>
+               <div class="cbi-section-descr"><%=self.description%></div>
+       <%- end %>
        <% local isempty = true for i, k in ipairs(self:cfgsections()) do -%>
                <% if self.addremove then -%>
                        <div class="cbi-section-remove right">
index 4841c37..d1e9e66 100644 (file)
@@ -1,5 +1,5 @@
 <%+cbi/valueheader%>
-       <textarea class="cbi-input-textarea" <% if not self.size then %> style="width: 100%"<% else %> cols="<%=self.size%>"<% end %> onchange="cbi_d_update(this.id)"<%= attr("name", cbid) .. attr("id", cbid) .. ifattr(self.rows, "rows") .. ifattr(self.wrap, "wrap")  .. ifattr(self.readonly, "readonly") %>>
+       <textarea class="cbi-input-textarea" <% if not self.size then %> style="width: 100%"<% else %> cols="<%=self.size%>"<% end %> data-update="change"<%= attr("name", cbid) .. attr("id", cbid) .. ifattr(self.rows, "rows") .. ifattr(self.wrap, "wrap")  .. ifattr(self.readonly, "readonly") %>>
        <%-=pcdata(self:cfgvalue(section))-%>
        </textarea>
 <%+cbi/valuefooter%>
index 157f3b3..4fb5201 100644 (file)
         <% if not self.unsafeupload then %>
                <input type="hidden"<%= attr("value", v) .. attr("name", "cbi.rlf." .. section .. "." .. self.option) .. attr("id", "cbi.rlf." .. section .. "." .. self.option) %> />
        <% end %>
-               
+
        <% if (not s) or (s and not self.unsafeupload) then %>
                <input class="cbi-input-file" type="file"<%= attr("name", cbid) .. attr("id", cbid) %> />
        <% end %>
-       <input type="text" class="cbi-input-text" onchange="cbi_d_update(this.id)"<%=
+       <input type="text" class="cbi-input-text" data-update="change"<%=
                attr("name", cbid .. ".textbox") .. attr("id", cbid .. ".textbox") .. attr("value", luci.cbi.AbstractValue.cfgvalue(self, section) or self.default) .. ifattr(self.size, "size") .. ifattr(self.placeholder, "placeholder") .. ifattr(self.readonly, "readonly") .. ifattr(self.maxlength, "maxlength") %> />
 <%+cbi/valuefooter%>
index c43dab5..9bb4f3b 100644 (file)
@@ -1,8 +1,14 @@
 <%+cbi/valueheader%>
-       <input type="<%=self.password and 'password" class="cbi-input-password' or 'text" class="cbi-input-text' %>" onchange="cbi_d_update(this.id)"<%=
-               attr("name", cbid) .. attr("id", cbid) .. attr("value", self:cfgvalue(section) or self.default) ..
-               ifattr(self.size, "size") .. ifattr(self.placeholder, "placeholder") ..
-               ifattr(self.readonly, "readonly") .. ifattr(self.maxlength, "maxlength")
+       <input data-update="change"<%=
+               attr("id", cbid) ..
+               attr("name", cbid) ..
+               attr("type", self.password and "password" or "text") ..
+               attr("class", self.password and "cbi-input-password" or "cbi-input-text") ..
+               attr("value", self:cfgvalue(section) or self.default) ..
+               ifattr(self.size, "size") ..
+               ifattr(self.placeholder, "placeholder") ..
+               ifattr(self.readonly, "readonly") ..
+               ifattr(self.maxlength, "maxlength")
        %> />
        <% if self.password then %><img src="<%=resource%>/cbi/reload.gif" style="vertical-align:middle" title="<%:Reveal/hide password%>" onclick="var e = document.getElementById('<%=cbid%>'); e.type = (e.type=='password') ? 'text' : 'password';" /><% end %>
        <% if #self.keylist > 0 or self.datatype then -%>
index 6f52293..022d4c4 100644 (file)
@@ -156,8 +156,8 @@ end
 
 if found_sta then
        ch = s:taboption("general", DummyValue, "choice", translate("Channel"))
-       ch.value = translatef("Locked to channel %d used by: %s",
-               found_sta.channel, table.concat(found_sta.names, ", "))
+       ch.value = translatef("Locked to channel %s used by: %s",
+               found_sta.channel or "(auto)", table.concat(found_sta.names, ", "))
 else
        ch = s:taboption("general", Value, "_mode_freq", '<br />'..translate("Operating frequency"))
        ch.hwmodes = hw_modes
index b19a6a0..e587178 100644 (file)
@@ -137,6 +137,13 @@ input[type="submit"] {
        -webkit-appearance: button;
 }
 
+button[disabled],
+input[type="button"][disabled],
+input[type="reset"][disabled],
+input[type="submit"][disabled] {
+       opacity: 0.7;
+}
+
 input[type="search"] {
        -webkit-appearance: textfield;
        -webkit-box-sizing: content-box;
@@ -1101,6 +1108,19 @@ header .dropdown-menu a.hover,
        margin-bottom: -1px;
 }
 
+.cbi-tabmenu.map {
+       margin: 0;
+}
+
+.cbi-tabmenu.map > li {
+       font-size: 16.5px;
+       font-weight: bold;
+}
+
+.cbi-tabcontainer > fieldset.cbi-section[id] > legend {
+       display: none;
+}
+
 .tabs > li > a,
 .cbi-tabmenu > li > a {
        padding: 0 15px;
index 3b40ad4..d4d0a2b 100644 (file)
@@ -1,6 +1,6 @@
 <%#
  Copyright 2008 Steven Barth <steven@midlink.org>
- Copyright 2008 Jo-Philipp Wich <jow@openwrt.org>
+ Copyright 2008-2016 Jo-Philipp Wich <jow@openwrt.org>
  Copyright 2012 David Menting <david@nut-bolt.nl>
  Licensed to the public under the Apache License 2.0.
 -%>
                        c._menu_selected = true
                end
        end
-    
-    -- send as HTML5
+
+       -- send as HTML5
        http.prepare_content("text/html")
 
        local function nodeurl(prefix, name, query)
-               local url = controller .. prefix .. name .. "/"
+               local u = url(prefix, name)
                if query then
-                       url = url .. http.build_querystring(query)
+                       u = u .. http.build_querystring(query)
                end
-               return pcdata(url)
+               return pcdata(u)
        end
 
-       local function subtree(prefix, node, level)
+       local function render_tabmenu(prefix, node, level)
                if not level then
                        level = 1
                end
 
                local childs = disp.node_childs(node)
                if #childs > 0 then
-        
-            if level > 2 then
-%>
-       <ul class="tabs">
-               <%  
-            end
+                       if level > 2 then
+                               write('<ul class="tabs">')
+                       end
 
                        local selected_node
                        local selected_name
                                        selected_node = nnode
                                        selected_name = v
                                end
-                if level > 2 then
-               %>
-                       <li class="tabmenu-item-<%=v%><%- if nnode._menu_selected or (node.leaf and v == leaf) then %> active<% end %>">
-                           <a href="<%=nodeurl(prefix, v, nnode.query)%>"><%=striptags(translate(nnode.title))%></a>
-                       </li>
-               <%      end
+
+                               if level > 2 then
+                                       write('<li class="tabmenu-item-%s %s"><a href="%s">%s</a></li>' %{
+                                               v, (nnode._menu_selected or (node.leaf and v == leaf)) and 'active' or '',
+                                               nodeurl(prefix, v, nnode.query),
+                                               striptags(translate(nnode.title))
+                                       })
+                               end
+                       end
+
+                       if level > 2 then
+                               write('</ul>')
                        end
-            
-            if level > 2 then
-               %>
-       </ul>
-<%          end
 
                        if selected_node then
-                               subtree(prefix .. selected_name .. "/", selected_node, level + 1)
+                               render_tabmenu(prefix .. "/" .. selected_name, selected_node, level + 1)
                        end
                end
        end
--%>
-<!DOCTYPE html>
-<html lang="<%=luci.i18n.context.lang%>">
- <head>
-  <meta charset="utf-8">
-  <title><%=striptags( (boardinfo.hostname or "?") .. ( (node and node.title) and ' - ' .. translate(node.title) or '')) %> - LuCI</title>
-  <!--[if lt IE 9]><script src="<%=media%>/html5.js"></script><![endif]-->
-  <meta name="viewport" content="initial-scale=1.0">
-  <link rel="stylesheet" href="<%=media%>/cascade.css">
-  <link rel="stylesheet" media="only screen and (max-device-width: 854px)" href="<%=media%>/mobile.css" type="text/css" />
-  <link rel="shortcut icon" href="<%=media%>/favicon.ico">
-  <% if node and node.css then %><link rel="stylesheet" href="<%=resource%>/<%=node.css%>">
-  <% end -%>
-  <% if css then %><style title="text/css">
-  <%-= css %>
-  </style>
-  <% end -%>
-  <script src="<%=resource%>/xhr.js"></script>
- </head>
-
- <body class="lang_<%=luci.i18n.context.lang%> <%- if node then %><%= striptags( node.title ) %><%- end %>">
-  <header>
-   <div class="fill">
-    <div class="container">
-     <a class="brand" href="#"><%=boardinfo.hostname or "?"%></a>
-     <ul class="nav">
-        <%-
-               local function submenu(prefix, node)
-                       local childs = disp.node_childs(node)
-                       if #childs > 0 then
-        %>
-        <ul class="dropdown-menu">
-                       <%-
-                               for i, r in ipairs(childs) do
-                                       local nnode = node.nodes[r]
-                                       local href  = controller .. prefix .. r ..
-                                               (nnode.query and http.build_querystring(nnode.query) or "")
-                       %>
-         <li><a href="<%=pcdata(href)%>"><%=pcdata(striptags(translate(nnode.title)))%></a></li>
-                       <%-
-                               end
-                       %>
-       </ul>
-       <%-
+
+       local function render_submenu(prefix, node)
+               local childs = disp.node_childs(node)
+               if #childs > 0 then
+                       write('<ul class="dropdown-menu">')
+
+                       for i, r in ipairs(childs) do
+                               local nnode = node.nodes[r]
+                               write('<li><a href="%s">%s</a></li>' %{
+                                       nodeurl(prefix, r, nnode.query),
+                                       pcdata(striptags(translate(nnode.title)))
+                               })
                        end
-               end
 
-               childs = disp.node_childs(cattree)
+                       write('</ul>')
+               end
+       end
 
+       local function render_topmenu()
+               local childs = disp.node_childs(cattree)
                if #childs > 0 then
+                       write('<ul class="nav">')
+
                        for i, r in ipairs(childs) do
                                local nnode = cattree.nodes[r]
-                               local href  = controller .. "/" .. category .. "/" .. r ..
-                                       (nnode.query and http.build_querystring(k.query) or "")
-                local grandchildren = disp.node_childs(nnode)
-                
-                if #grandchildren > 0 then
-       %>
-        <li class="dropdown">
-            <a class="menu" href="#"><%=pcdata(striptags(translate(nnode.title)))%></a>
-            <%- submenu("/" .. category .. "/" .. r .. "/", nnode) %>
-        </li>
-       <%          else %>
-         <li>
-               <a href="<%=pcdata(href)%>"><%=pcdata(striptags(translate(nnode.title)))%></a>
-        </li> 
-    <%
-                end
+                               local grandchildren = disp.node_childs(nnode)
+
+                               if #grandchildren > 0 then
+                                       write('<li class="dropdown"><a class="menu" href="#">%s</a>' % pcdata(striptags(translate(nnode.title))))
+                                       render_submenu(category .. "/" .. r, nnode)
+                                       write('</li>')
+                               else
+                                       write('<li><a href="%s">%s</a></li>' %{
+                                               nodeurl(category, r, nnode.query),
+                                               pcdata(striptags(translate(nnode.title)))
+                                       })
+                               end
                        end
+
+                       write('</ul>')
                end
-       %>
-       </ul>
+       end
 
-       <%
-        -- calculate the number of unsaved changes
+       local function render_changes()
+               -- calculate the number of unsaved changes
                if tree.nodes[category] and tree.nodes[category].ucidata then
                        local ucichanges = 0
 
                                        end
                                end
                        end
-       %>
-       <div class="pull-right">
-       <% if ucichanges > 0 then %>
-         <a class="label notice" href="<%=controller%>/<%=category%>/uci/changes?redir=<%=http.urlencode(http.formvalue("redir") or REQUEST_URI)%>"><%:Unsaved Changes%>: <%=ucichanges%></a>
-       <% end %>
-      <span id="xhr_poll_status" style="display:none" onclick="XHR.running() ? XHR.halt() : XHR.run()">
-       <span class="label success" id="xhr_poll_status_on"><%:Auto Refresh%> <%:on%></span>
-       <span class="label" id="xhr_poll_status_off" style="display:none"><%:Auto Refresh%> <%:off%></span>
-      </span>
-     </div>
-       <% end %>
-    </div>
-   </div>
-  </header>
-
-<%- if luci.sys.process.info("uid") == 0 and luci.sys.user.getuser("root") and not luci.sys.user.getpasswd("root") then -%>
-<div class="container">
-       <div class="alert-message warning">
-               <h4><%:No password set!%></h4>
-               <%:There is no password set on this router. Please configure a root password to protect the web interface and enable SSH.%><br>
-        <a href="<%=pcdata(luci.dispatcher.build_url("admin/system/admin"))%>"><%:Go to password configuration...%></a>
-       </div>
-</div>
-<%- end -%>
-
-<div id="maincontent" class="container">
-<% if category then subtree("/" .. category .. "/", cattree) end %>
+
+                       if ucichanges > 0 then
+                               write('<a class="label notice" href="%s?redir=%s">%s: %d</a>' %{
+                                       url(category, 'uci/changes'),
+                                       http.urlencode(http.formvalue('redir') or REQUEST_URI),
+                                       translate('Unsaved Changes'),
+                                       ucichanges
+                               })
+                       end
+               end
+       end
+-%>
+<!DOCTYPE html>
+<html lang="<%=luci.i18n.context.lang%>">
+       <head>
+               <meta charset="utf-8">
+               <title><%=striptags( (boardinfo.hostname or "?") .. ( (node and node.title) and ' - ' .. translate(node.title) or '')) %> - LuCI</title>
+               <!--[if lt IE 9]><script src="<%=media%>/html5.js"></script><![endif]-->
+               <meta name="viewport" content="initial-scale=1.0">
+               <link rel="stylesheet" href="<%=media%>/cascade.css">
+               <link rel="stylesheet" media="only screen and (max-device-width: 854px)" href="<%=media%>/mobile.css" type="text/css" />
+               <link rel="shortcut icon" href="<%=media%>/favicon.ico">
+               <% if node and node.css then %>
+                       <link rel="stylesheet" href="<%=resource%>/<%=node.css%>">
+               <% end -%>
+               <% if css then %>
+                       <style title="text/css"><%= css %></style>
+               <% end -%>
+               <script src="<%=resource%>/xhr.js"></script>
+               <script src="<%=resource%>/jql.min.js"></script>
+       </head>
+
+       <body class="lang_<%=luci.i18n.context.lang%> <%- if node then %><%= striptags( node.title ) %><%- end %>">
+               <header>
+                       <div class="fill">
+                               <div class="container">
+                                       <a class="brand" href="#"><%=boardinfo.hostname or "?"%></a>
+                                       <% render_topmenu() %>
+                                       <div class="pull-right">
+                                               <% render_changes() %>
+                                               <span id="xhr_poll_status" style="display:none" onclick="XHR.running() ? XHR.halt() : XHR.run()">
+                                                       <span class="label success" id="xhr_poll_status_on"><%:Auto Refresh%> <%:on%></span>
+                                                       <span class="label" id="xhr_poll_status_off" style="display:none"><%:Auto Refresh%> <%:off%></span>
+                                               </span>
+                                       </div>
+                               </div>
+                       </div>
+               </header>
+
+               <%- if luci.sys.process.info("uid") == 0 and luci.sys.user.getuser("root") and not luci.sys.user.getpasswd("root") then -%>
+                       <div class="container">
+                               <div class="alert-message warning">
+                                       <h4><%:No password set!%></h4>
+                                       <%:There is no password set on this router. Please configure a root password to protect the web interface and enable SSH.%><br>
+                                       <a href="<%=pcdata(luci.dispatcher.build_url("admin/system/admin"))%>"><%:Go to password configuration...%></a>
+                               </div>
+                       </div>
+               <%- end -%>
+
+               <div id="maincontent" class="container">
+                       <% if category then render_tabmenu(category, cattree) end %>