summaryrefslogtreecommitdiffhomepage
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/luci-base/htdocs/luci-static/resources/cbi.js516
-rw-r--r--modules/luci-base/luasrc/cbi.lua35
-rw-r--r--modules/luci-base/luasrc/view/cbi/dropdown.htm42
-rw-r--r--modules/luci-base/luasrc/view/cbi/firewall_zonelist.htm79
-rw-r--r--modules/luci-base/luasrc/view/cbi/network_ifacelist.htm102
-rw-r--r--modules/luci-base/luasrc/view/cbi/network_netlist.htm90
-rw-r--r--modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/ifaces.lua17
-rw-r--r--modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/wifi.lua35
-rw-r--r--modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/wifi_add.lua11
9 files changed, 728 insertions, 199 deletions
diff --git a/modules/luci-base/htdocs/luci-static/resources/cbi.js b/modules/luci-base/htdocs/luci-static/resources/cbi.js
index 0a1961916c..a7f999d876 100644
--- a/modules/luci-base/htdocs/luci-static/resources/cbi.js
+++ b/modules/luci-base/htdocs/luci-static/resources/cbi.js
@@ -465,31 +465,16 @@ function cbi_d_add(field, dep, index) {
}
function cbi_d_checkvalue(target, ref) {
- var t = document.getElementById(target);
- var value;
+ var value = null,
+ query = 'input[id="'+target+'"], input[name="'+target+'"], ' +
+ 'select[id="'+target+'"], select[name="'+target+'"]';
- if (!t) {
- var tl = document.getElementsByName(target);
-
- if( tl.length > 0 && (tl[0].type == 'radio' || tl[0].type == 'checkbox'))
- 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;
-
- if (t.type == "checkbox") {
- value = t.checked ? value : "";
- }
- }
+ document.querySelectorAll(query).forEach(function(i) {
+ if (value === null && ((i.type !== 'radio' && i.type !== 'checkbox') || i.checked === true))
+ value = i.value;
+ });
- return (value == ref)
+ return (((value !== null) ? value : "") == ref);
}
function cbi_d_check(deps) {
@@ -634,6 +619,10 @@ function cbi_init() {
node.getAttribute('data-type'));
}
+ document.querySelectorAll('.cbi-dropdown').forEach(function(s) {
+ cbi_dropdown_init(s);
+ });
+
cbi_d_update();
}
@@ -1574,3 +1563,484 @@ function E()
return elem;
}
+
+if (typeof(window.CustomEvent) !== 'function') {
+ function CustomEvent(event, params) {
+ params = params || { bubbles: false, cancelable: false, detail: undefined };
+ var evt = document.createEvent('CustomEvent');
+ evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
+ return evt;
+ }
+
+ CustomEvent.prototype = window.Event.prototype;
+ window.CustomEvent = CustomEvent;
+}
+
+CBIDropdown = {
+ openDropdown: function(sb) {
+ var st = window.getComputedStyle(sb, null),
+ ul = sb.querySelector('ul'),
+ li = ul.querySelectorAll('li'),
+ sel = ul.querySelector('[selected]'),
+ rect = sb.getBoundingClientRect(),
+ h = sb.clientHeight - parseFloat(st.paddingTop) - parseFloat(st.paddingBottom),
+ mh = this.dropdown_items * h,
+ eh = Math.min(mh, li.length * h);
+
+ document.querySelectorAll('.cbi-dropdown[open]').forEach(function(s) {
+ s.dispatchEvent(new CustomEvent('cbi-dropdown-close', {}));
+ });
+
+ ul.style.maxHeight = mh + 'px';
+ sb.setAttribute('open', '');
+
+ ul.scrollTop = sel ? Math.max(sel.offsetTop - sel.offsetHeight, 0) : 0;
+ ul.querySelectorAll('[selected] input[type="checkbox"]').forEach(function(c) {
+ c.checked = true;
+ });
+
+ ul.style.top = ul.style.bottom = '';
+ ul.style[((sb.getBoundingClientRect().top + eh) > window.innerHeight) ? 'bottom' : 'top'] = rect.height + 'px';
+ ul.classList.add('dropdown');
+
+ var pv = ul.cloneNode(true);
+ pv.classList.remove('dropdown');
+ pv.classList.add('preview');
+
+ sb.insertBefore(pv, ul.nextElementSibling);
+
+ li.forEach(function(l) {
+ l.setAttribute('tabindex', 0);
+ });
+
+ sb.lastElementChild.setAttribute('tabindex', 0);
+
+ this.setFocus(sb, sel || li[0], true);
+ },
+
+ closeDropdown: function(sb, no_focus) {
+ if (!sb.hasAttribute('open'))
+ return;
+
+ var pv = sb.querySelector('ul.preview'),
+ ul = sb.querySelector('ul.dropdown'),
+ li = ul.querySelectorAll('li');
+
+ li.forEach(function(l) { l.removeAttribute('tabindex'); });
+ sb.lastElementChild.removeAttribute('tabindex');
+
+ sb.removeChild(pv);
+ sb.removeAttribute('open');
+ sb.style.width = sb.style.height = '';
+
+ ul.classList.remove('dropdown');
+
+ if (!no_focus)
+ this.setFocus(sb, sb);
+
+ this.saveValues(sb, ul);
+ },
+
+ toggleItem: function(sb, li, force_state) {
+ if (li.hasAttribute('unselectable'))
+ return;
+
+ if (this.multi) {
+ var cbox = li.querySelector('input[type="checkbox"]'),
+ items = li.parentNode.querySelectorAll('li'),
+ label = sb.querySelector('ul.preview'),
+ sel = li.parentNode.querySelectorAll('[selected]').length,
+ more = sb.querySelector('.more'),
+ ndisplay = this.display_items,
+ n = 0;
+
+ if (li.hasAttribute('selected')) {
+ if (force_state !== true) {
+ if (sel > 1 || this.optional) {
+ li.removeAttribute('selected');
+ cbox.checked = cbox.disabled = false;
+ sel--;
+ }
+ else {
+ cbox.disabled = true;
+ }
+ }
+ }
+ else {
+ if (force_state !== false) {
+ li.setAttribute('selected', '');
+ cbox.checked = true;
+ cbox.disabled = false;
+ sel++;
+ }
+ }
+
+ while (label.firstElementChild)
+ label.removeChild(label.firstElementChild);
+
+ for (var i = 0; i < items.length; i++) {
+ items[i].removeAttribute('display');
+ if (items[i].hasAttribute('selected')) {
+ if (ndisplay-- > 0) {
+ items[i].setAttribute('display', n++);
+ label.appendChild(items[i].cloneNode(true));
+ }
+ var c = items[i].querySelector('input[type="checkbox"]');
+ if (c)
+ c.disabled = (sel == 1 && !this.optional);
+ }
+ }
+
+ if (ndisplay < 0)
+ sb.setAttribute('more', '');
+ else
+ sb.removeAttribute('more');
+
+ if (ndisplay === this.display_items)
+ sb.setAttribute('empty', '');
+ else
+ sb.removeAttribute('empty');
+
+ more.innerHTML = (ndisplay === this.display_items) ? this.placeholder : '···';
+ }
+ else {
+ var sel = li.parentNode.querySelector('[selected]');
+ if (sel) {
+ sel.removeAttribute('display');
+ sel.removeAttribute('selected');
+ }
+
+ li.setAttribute('display', 0);
+ li.setAttribute('selected', '');
+
+ this.closeDropdown(sb, true);
+ }
+
+ this.saveValues(sb, li.parentNode);
+ },
+
+ transformItem: function(sb, li) {
+ var cbox = E('form', {}, E('input', { type: 'checkbox', tabindex: -1, onclick: 'event.preventDefault()' })),
+ label = E('label');
+
+ while (li.firstChild)
+ label.appendChild(li.firstChild);
+
+ li.appendChild(cbox);
+ li.appendChild(label);
+ },
+
+ saveValues: function(sb, ul) {
+ var sel = ul.querySelectorAll('[selected]'),
+ div = sb.lastElementChild;
+
+ while (div.lastElementChild)
+ div.removeChild(div.lastElementChild);
+
+ sel.forEach(function (s) {
+ div.appendChild(E('input', {
+ type: 'hidden',
+ name: s.hasAttribute('name') ? s.getAttribute('name') : (sb.getAttribute('name') || ''),
+ value: s.hasAttribute('value') ? s.getAttribute('value') : s.innerText
+ }));
+ });
+
+ cbi_d_update();
+ },
+
+ setFocus: function(sb, elem, scroll) {
+ if (sb && sb.hasAttribute && sb.hasAttribute('locked-in'))
+ return;
+
+ document.querySelectorAll('.focus').forEach(function(e) {
+ if (e.nodeName.toLowerCase() !== 'input') {
+ e.classList.remove('focus');
+ e.blur();
+ }
+ });
+
+ if (elem) {
+ elem.focus();
+ elem.classList.add('focus');
+
+ if (scroll)
+ elem.parentNode.scrollTop = elem.offsetTop - elem.parentNode.offsetTop;
+ }
+ },
+
+ createItems: function(sb, value) {
+ var sbox = this,
+ val = (value || '').trim().split(/\s+/),
+ ul = sb.querySelector('ul');
+
+ if (!sbox.multi)
+ val.length = Math.min(val.length, 1);
+
+ val.forEach(function(item) {
+ var new_item = null;
+
+ ul.childNodes.forEach(function(li) {
+ if (li.getAttribute && li.getAttribute('value') === item)
+ new_item = li;
+ });
+
+ if (!new_item) {
+ var markup,
+ tpl = sb.querySelector(sbox.template);
+
+ if (tpl)
+ markup = (tpl.textContent || tpl.innerHTML || tpl.firstChild.data).replace(/^<!--|-->$/, '').trim();
+ else
+ markup = '<li value="{{value}}">{{value}}</li>';
+
+ new_item = E(markup.replace(/{{value}}/g, item));
+
+ if (sbox.multi) {
+ sbox.transformItem(sb, new_item);
+ }
+ else {
+ var old = ul.querySelector('li[created]');
+ if (old)
+ ul.removeChild(old);
+
+ new_item.setAttribute('created', '');
+ }
+
+ new_item = ul.insertBefore(new_item, ul.lastElementChild);
+ }
+
+ sbox.toggleItem(sb, new_item, true);
+ sbox.setFocus(sb, new_item, true);
+ });
+ },
+
+ closeAllDropdowns: function() {
+ document.querySelectorAll('.cbi-dropdown[open]').forEach(function(s) {
+ s.dispatchEvent(new CustomEvent('cbi-dropdown-close', {}));
+ });
+ },
+
+ findParent: function(node, selector) {
+ while (node)
+ if (node.msMatchesSelector && node.msMatchesSelector(selector))
+ return node;
+ else if (node.matches && node.matches(selector))
+ return node;
+ else
+ node = node.parentNode;
+
+ return null;
+ }
+};
+
+function cbi_dropdown_init(sb) {
+ if (!(this instanceof cbi_dropdown_init))
+ return new cbi_dropdown_init(sb);
+
+ this.multi = sb.hasAttribute('multiple');
+ this.optional = sb.hasAttribute('optional');
+ this.placeholder = sb.getAttribute('placeholder') || '---';
+ this.display_items = parseInt(sb.getAttribute('display-items') || 3);
+ this.dropdown_items = parseInt(sb.getAttribute('dropdown-items') || 5);
+ this.create = sb.getAttribute('item-create') || '.create-item-input';
+ this.template = sb.getAttribute('item-template') || 'script[type="item-template"]';
+
+ var sbox = this,
+ ul = sb.querySelector('ul'),
+ items = ul.querySelectorAll('li'),
+ more = sb.appendChild(E('span', { class: 'more', tabindex: -1 }, '···')),
+ open = sb.appendChild(E('span', { class: 'open', tabindex: -1 }, '▾')),
+ canary = sb.appendChild(E('div')),
+ create = sb.querySelector(this.create),
+ ndisplay = this.display_items,
+ n = 0;
+
+ if (this.multi) {
+ for (var i = 0; i < items.length; i++) {
+ sbox.transformItem(sb, items[i]);
+
+ if (items[i].hasAttribute('selected') && ndisplay-- > 0)
+ items[i].setAttribute('display', n++);
+ }
+ }
+ else {
+ var sel = sb.querySelectorAll('[selected]');
+
+ sel.forEach(function(s) {
+ s.removeAttribute('selected');
+ });
+
+ var s = sel[0] || items[0];
+ if (s) {
+ s.setAttribute('selected', '');
+ s.setAttribute('display', n++);
+ }
+
+ ndisplay--;
+
+ if (this.optional && !ul.querySelector('li[value=""]')) {
+ var placeholder = E('li', { placeholder: '' }, this.placeholder);
+ ul.firstChild ? ul.insertBefore(placeholder, ul.firstChild) : ul.appendChild(placeholder);
+ }
+ }
+
+ sbox.saveValues(sb, ul);
+
+ ul.setAttribute('tabindex', -1);
+ sb.setAttribute('tabindex', 0);
+
+ if (ndisplay < 0)
+ sb.setAttribute('more', '')
+ else
+ sb.removeAttribute('more');
+
+ if (ndisplay === this.display_items)
+ sb.setAttribute('empty', '')
+ else
+ sb.removeAttribute('empty');
+
+ more.innerHTML = (ndisplay === this.display_items) ? this.placeholder : '···';
+
+
+ sb.addEventListener('click', function(ev) {
+ if (!this.hasAttribute('open')) {
+ if (ev.target.nodeName.toLowerCase() !== 'input')
+ sbox.openDropdown(this);
+ }
+ else {
+ var li = sbox.findParent(ev.target, 'li');
+ if (li && li.parentNode.classList.contains('dropdown'))
+ sbox.toggleItem(this, li);
+ }
+
+ ev.preventDefault();
+ ev.stopPropagation();
+ });
+
+ sb.addEventListener('keydown', function(ev) {
+ if (ev.target.nodeName.toLowerCase() === 'input')
+ return;
+
+ if (!this.hasAttribute('open')) {
+ switch (ev.keyCode) {
+ case 37:
+ case 38:
+ case 39:
+ case 40:
+ sbox.openDropdown(this);
+ ev.preventDefault();
+ }
+ }
+ else
+ {
+ var active = sbox.findParent(document.activeElement, 'li');
+
+ switch (ev.keyCode) {
+ case 27:
+ sbox.closeDropdown(this);
+ break;
+
+ case 13:
+ if (active) {
+ if (!active.hasAttribute('selected'))
+ sbox.toggleItem(this, active);
+ sbox.closeDropdown(this);
+ ev.preventDefault();
+ }
+ break;
+
+ case 32:
+ if (active) {
+ sbox.toggleItem(this, active);
+ ev.preventDefault();
+ }
+ break;
+
+ case 38:
+ if (active && active.previousElementSibling) {
+ sbox.setFocus(this, active.previousElementSibling);
+ ev.preventDefault();
+ }
+ break;
+
+ case 40:
+ if (active && active.nextElementSibling) {
+ sbox.setFocus(this, active.nextElementSibling);
+ ev.preventDefault();
+ }
+ break;
+ }
+ }
+ });
+
+ sb.addEventListener('cbi-dropdown-close', function(ev) {
+ sbox.closeDropdown(this, true);
+ });
+
+ if ('ontouchstart' in window) {
+ sb.addEventListener('touchstart', function(ev) { ev.stopPropagation(); });
+ window.addEventListener('touchstart', sbox.closeAllDropdowns);
+ }
+ else {
+ sb.addEventListener('mouseover', function(ev) {
+ if (!this.hasAttribute('open'))
+ return;
+
+ var li = sbox.findParent(ev.target, 'li');
+ if (li) {
+ if (li.parentNode.classList.contains('dropdown'))
+ sbox.setFocus(this, li);
+
+ ev.stopPropagation();
+ }
+ });
+
+ sb.addEventListener('focus', function(ev) {
+ document.querySelectorAll('.cbi-dropdown[open]').forEach(function(s) {
+ if (s !== this || this.hasAttribute('open'))
+ s.dispatchEvent(new CustomEvent('cbi-dropdown-close', {}));
+ });
+ });
+
+ canary.addEventListener('focus', function(ev) {
+ sbox.closeDropdown(this.parentNode);
+ });
+
+ window.addEventListener('mouseover', sbox.setFocus);
+ window.addEventListener('click', sbox.closeAllDropdowns);
+ }
+
+ if (create) {
+ create.addEventListener('keydown', function(ev) {
+ switch (ev.keyCode) {
+ case 13:
+ sbox.createItems(sb, this.value);
+ ev.preventDefault();
+ this.value = '';
+ this.blur();
+ break;
+ }
+ });
+
+ create.addEventListener('focus', function(ev) {
+ var cbox = sbox.findParent(this, 'li').querySelector('input[type="checkbox"]');
+ if (cbox) cbox.checked = true;
+ sb.setAttribute('locked-in', '');
+ });
+
+ create.addEventListener('blur', function(ev) {
+ var cbox = sbox.findParent(this, 'li').querySelector('input[type="checkbox"]');
+ if (cbox) cbox.checked = false;
+ sb.removeAttribute('locked-in');
+ });
+
+ var li = sbox.findParent(create, 'li');
+
+ li.setAttribute('unselectable', '');
+ li.addEventListener('click', function(ev) {
+ this.querySelector(sbox.create).focus();
+ });
+ }
+}
+
+cbi_dropdown_init.prototype = CBIDropdown;
diff --git a/modules/luci-base/luasrc/cbi.lua b/modules/luci-base/luasrc/cbi.lua
index 4800d2aa72..d275c5b275 100644
--- a/modules/luci-base/luasrc/cbi.lua
+++ b/modules/luci-base/luasrc/cbi.lua
@@ -1417,6 +1417,12 @@ function AbstractValue.parse(self, section, novld)
self:add_error(section, "invalid", val_err)
end
+ if self.alias then
+ self.section.aliased = self.section.aliased or {}
+ self.section.aliased[section] = self.section.aliased[section] or {}
+ self.section.aliased[section][self.alias] = true
+ end
+
if fvalue and (self.forcewrite or not (fvalue == cvalue)) then
if self:write(section, fvalue) then
-- Push events
@@ -1426,10 +1432,16 @@ function AbstractValue.parse(self, section, novld)
end
else -- Unset the UCI or error
if self.rmempty or self.optional then
- if self:remove(section) then
- -- Push events
- self.section.changed = true
- --luci.util.append(self.map.events, self.events)
+ if not self.alias or
+ not self.section.aliased or
+ not self.section.aliased[section] or
+ not self.section.aliased[section][self.alias]
+ then
+ if self:remove(section) then
+ -- Push events
+ self.section.changed = true
+ --luci.util.append(self.map.events, self.events)
+ end
end
elseif cvalue ~= fvalue and not novld then
-- trigger validator with nil value to get custom user error msg.
@@ -1455,7 +1467,7 @@ function AbstractValue.cfgvalue(self, section)
if self.tag_error[section] then
value = self:formvalue(section)
else
- value = self.map:get(section, self.option)
+ value = self.map:get(section, self.alias or self.option)
end
if not value then
@@ -1496,12 +1508,12 @@ AbstractValue.transform = AbstractValue.validate
-- Write to UCI
function AbstractValue.write(self, section, value)
- return self.map:set(section, self.option, value)
+ return self.map:set(section, self.alias or self.option, value)
end
-- Remove from UCI
function AbstractValue.remove(self, section)
- return self.map:del(section, self.option)
+ return self.map:del(section, self.alias or self.option)
end
@@ -1834,6 +1846,15 @@ function DynamicList.formvalue(self, section)
end
+DropDown = class(MultiValue)
+
+function DropDown.__init__(self, ...)
+ ListValue.__init__(self, ...)
+ self.template = "cbi/dropdown"
+ self.delimiter = " "
+end
+
+
--[[
TextValue - A multi-line value
rows: Rows
diff --git a/modules/luci-base/luasrc/view/cbi/dropdown.htm b/modules/luci-base/luasrc/view/cbi/dropdown.htm
new file mode 100644
index 0000000000..bdf7248375
--- /dev/null
+++ b/modules/luci-base/luasrc/view/cbi/dropdown.htm
@@ -0,0 +1,42 @@
+<%+cbi/valueheader%>
+
+<%-
+ local selected = { }
+
+ if self.multiple then
+ local val
+ for val in luci.util.imatch(self:cfgvalue(section)) do
+ selected[val] = true
+ end
+ else
+ selected[self:cfgvalue(section)] = true
+ end
+
+ if not next(selected) and self.default then
+ selected[self.default] = true
+ end
+-%>
+
+<div class="cbi-dropdown"<%=
+ attr("name", cbid) ..
+ attr("display-items", self.display or self.size or 3) ..
+ attr("dropdown-items", self.dropdown or self.display or self.size or 5) ..
+ attr("placeholder", self.placeholder or translate("-- please select --")) ..
+ ifattr(self.multiple, "multiple", "multiple") ..
+ ifattr(self.optional or self.rmempty, "optional", "optional")
+%>>
+ <ul>
+ <% local i, key; for i, key in pairs(self.keylist) do %>
+ <li<%=
+ attr("data-index", i) ..
+ attr("data-depends", self:deplist2json(section, self.deplist[i])) ..
+ attr("value", key) ..
+ ifattr(selected[key], "selected", "selected")
+ %>>
+ <%=pcdata(self.vallist[i])%>
+ </li>
+ <% end %>
+ </ul>
+</div>
+
+<%+cbi/valuefooter%>
diff --git a/modules/luci-base/luasrc/view/cbi/firewall_zonelist.htm b/modules/luci-base/luasrc/view/cbi/firewall_zonelist.htm
index b4260707ef..3a108020b6 100644
--- a/modules/luci-base/luasrc/view/cbi/firewall_zonelist.htm
+++ b/modules/luci-base/luasrc/view/cbi/firewall_zonelist.htm
@@ -24,26 +24,42 @@
end
-%>
-<span>
- <ul style="margin:0; list-style-type:none; text-align:left">
+<div class="cbi-dropdown" dropdown-items="5" placeholder="<%:-- please select -- %>"<%=
+ attr("name", cbid) ..
+ ifattr(self.widget == "checkbox", "multiple", "multiple") ..
+ ifattr(self.rmempty or self.optional, "optional", "optional")
+%>>
+ <script type="item-template"><!--
+ <li 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>
+ </li>
+ --></script>
+ <ul>
<% if self.allowlocal then %>
- <li style="padding:0.5em">
- <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")%>></label>
- <label<%=attr("for", cbid .. "_empty")%> style="background-color:<%=fwm.zone.get_color()%>" class="zonebadge">
+ <li 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 %>(<%:input%>)<% end %>
- </label>
+ <% if self.allowany and self.allowlocal then -%>
+ (<%= self.alias ~= "dest"
+ and translate("output") or translate("input") %>)
+ <%- end %>
+ </span>
+ </li>
+ <% elseif self.widget ~= "checkbox" and (self.rmempty or self.optional) then %>
+ <li value=""<%=ifattr(checked[""], "selected", "selected")%>>
+ <span class="zonebadge">
+ <em><%:unspecified%></em>
+ </span>
</li>
<% end %>
<% if self.allowany then %>
- <li style="padding:0.5em">
- <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")%>></label>
- <label<%=attr("for", cbid .. "_any")%> style="background-color:<%=fwm.zone.get_color()%>" class="zonebadge">
+ <li 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 %>
- </label>
+ </span>
</li>
<% end %>
<%
@@ -51,45 +67,42 @@
if zone:name() ~= self.exclude then
selected = selected or (value == zone:name())
%>
- <li style="padding:0.5em">
- <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())%>></label>
- <label<%=attr("for", cbid .. "." .. zone:name())%> style="background-color:<%=zone:get_color()%>" class="zonebadge">
+ <li<%=attr("value", zone:name()) .. ifattr(checked[zone:name()], "selected", "selected")%>>
+ <span style="background-color:<%=zone:get_color()%>" class="zonebadge">
<strong><%=zone:name()%>:</strong>
- <%
+ <%-
local zempty = true
for _, net in ipairs(zone:get_networks()) do
net = nwm:get_network(net)
if net then
zempty = false
- %>
+ -%>
<span class="ifacebadge<% if net:name() == self.network then %> ifacebadge-active<% end %>"><%=net:name()%>:
- <%
+ <%-
local nempty = true
for _, iface in ipairs(net:is_bridge() and net:get_interfaces() or { net:get_interface() }) do
nempty = false
%>
- <img<%=attr("title", iface:get_i18n())%> style="width:16px; height:16px; vertical-align:middle" src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
+ <img<%=attr("title", iface:get_i18n())%> src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
<% end %>
- <% if nempty then %><em><%:(empty)%></em><% end %>
+ <% if nempty then %><em><%:(empty)%></em><% end -%>
</span>
- <% end end %>
- <% if zempty then %><em><%:(empty)%></em><% end %>
- </label>
+ <%- end end -%>
+ <%- if zempty then %><em><%:(empty)%></em><% end -%>
+ </span>
</li>
<% end end %>
<% if self.widget ~= "checkbox" and not self.nocreate then %>
- <li style="padding:0.5em">
- <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;
- <label<%=attr("for", cbid .. "_new")%>></label>
- <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" />
- </div>
+ <li value="-">
+ <span class="zonebadge">
+ <em><%:create%>:</em>
+ <input type="password" style="display:none" />
+ <input class="create-item-input" type="text" />
+ </span>
</li>
<% end %>
</ul>
-</span>
+</div>
<%+cbi/valuefooter%>
diff --git a/modules/luci-base/luasrc/view/cbi/network_ifacelist.htm b/modules/luci-base/luasrc/view/cbi/network_ifacelist.htm
index 62dbde7dd4..abfa33e1ed 100644
--- a/modules/luci-base/luasrc/view/cbi/network_ifacelist.htm
+++ b/modules/luci-base/luasrc/view/cbi/network_ifacelist.htm
@@ -19,7 +19,9 @@
if value then
for value in utl.imatch(value) do
- checked[value] = true
+ for value in utl.imatch(value) do
+ checked[value] = true
+ end
end
else
local n = self.network and net:get_network(self.network)
@@ -33,57 +35,51 @@
-%>
<input type="hidden" name="<%=cbeid%>" value="1" />
-<ul style="margin:0; list-style-type:none">
- <% for _, iface in ipairs(ifaces) do
- local link = iface:adminlink()
- if (not self.nobridges or not iface:is_bridge()) and
- (not self.noinactive or iface:is_up()) and
- iface:name() ~= self.exclude
- then %>
- <li>
- <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()) ..
- ifattr(checked[iface:name()], "checked", "checked")
- %> />
- <%- if not self.widget or self.widget == "checkbox" or self.widget == "radio" then -%>
- <label<%=attr("for", cbid .. "." .. iface:name())%>></label>
- <%- end -%>
- &#160;
- <label<%=attr("for", cbid .. "." .. iface:name())%>>
- <% if link then -%><a href="<%=link%>"><% end -%>
- <img<%=attr("title", iface:get_i18n())%> style="width:16px; height:16px; vertical-align:middle" src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
- <% if link then -%></a><% end -%>
- <%=pcdata(iface:get_i18n())%>
- <% local ns = iface:get_networks(); if #ns > 0 then %>(
- <%- local i, n; for i, n in ipairs(ns) do -%>
- <%-= (i>1) and ', ' -%>
- <a href="<%=n:adminlink()%>"><%=n:name()%></a>
- <%- end -%>
- )<% end %>
- </label>
- </li>
- <% end end %>
- <% if not self.nocreate then %>
- <li>
- <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) ..
- attr("value", " ")
- %> />
- <%- if not self.widget or self.widget == "checkbox" or self.widget == "radio" then -%>
- <label<%=attr("for", cbid .. "_custom")%>></label>
- <%- end -%>
- &#160;
- <label<%=attr("for", cbid .. "_custom")%>>
- <img title="<%:Custom Interface%>" style="width:16px; height:16px; vertical-align:middle" src="<%=resource%>/icons/ethernet_disabled.png" />
- <%:Custom Interface%>:
- </label>
- <input type="text" style="width:50px" onfocus="document.getElementById('<%=cbid%>_custom').checked=true" onblur="var x=document.getElementById('<%=cbid%>_custom'); x.value=this.value; x.checked=true" />
- </li>
- <% end %>
-</ul>
+
+<div class="cbi-dropdown" display-items="5" 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}}">
+ <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>
+ </li>
+ --></script>
+ <ul>
+ <% for _, iface in ipairs(ifaces) do
+ if (not self.nobridges or not iface:is_bridge()) and
+ (not self.noinactive or iface:is_up()) and
+ iface:name() ~= self.exclude
+ then %>
+ <li<%=
+ attr("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" />
+ <span class="hide-open"><%=pcdata(iface:name())%></span>
+ <span class="hide-close">
+ <%=pcdata(iface:get_i18n())%>
+ <% local ns = iface:get_networks(); if #ns > 0 then %>(
+ <%- local i, n; for i, n in ipairs(ns) do -%>
+ <%-= (i>1) and ', ' -%>
+ <a href="<%=n:adminlink()%>"><%=n:name()%></a>
+ <%- end -%>
+ )<% end %>
+ </span>
+ </li>
+ <% end end %>
+ <% if not self.nocreate then %>
+ <li value="">
+ <img title="<%:Custom Interface%>" src="<%=resource%>/icons/ethernet_disabled.png" />
+ <span><%:Custom Interface%>:</span>
+ <input type="password" style="display:none" />
+ <input class="create-item-input" type="text" />
+ </li>
+ <% end %>
+ </ul>
+</div>
<%+cbi/valuefooter%>
diff --git a/modules/luci-base/luasrc/view/cbi/network_netlist.htm b/modules/luci-base/luasrc/view/cbi/network_netlist.htm
index 8bf1a70a20..ba6ebb8434 100644
--- a/modules/luci-base/luasrc/view/cbi/network_netlist.htm
+++ b/modules/luci-base/luasrc/view/cbi/network_netlist.htm
@@ -20,66 +20,62 @@
end
-%>
-<ul style="margin:0; list-style-type:none; text-align:left">
- <% for _, net in ipairs(networks) do
- if (net:name() ~= "loopback") and
- (net:name() ~= self.exclude) and
- (not self.novirtual or not net:is_virtual())
- then %>
- <li style="padding:0.25em 0">
- <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()) ..
- ifattr(checked[net:name()], "checked", "checked")
- %> /> &#160;
- <label<%=attr("for", cbid .. "." .. net:name())%>>
+<div class="cbi-dropdown" display-items="5" 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}}">
+ <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>
+ </li>
+ --></script>
+ <ul>
+ <% if self.widget ~= "checkbox" then %>
+ <li value=""<%= ifattr(not value, "selected", "selected") %>>
+ <em><%:unspecified%></em>
+ </li>
+ <% end %>
+
+ <% for _, net in ipairs(networks) do
+ if (net:name() ~= "loopback") and
+ (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") %>>
<span class="ifacebadge"><%=net:name()%>:
<%
local empty = true
for _, iface in ipairs(net:is_bridge() and net:get_interfaces() or { net:get_interface() }) do
if not iface:is_bridge() then
empty = false
- %>
+ -%>
<img<%=attr("title", iface:get_i18n())%> style="width:16px; height:16px; vertical-align:middle" src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
- <% end end %>
- <% if empty then %><em><%:(no interfaces attached)%></em><% end %>
+ <%- end end %>
+ <% if empty then %>
+ <em class="hide-close"><%:(no interfaces attached)%></em>
+ <em class="hide-open">-</em>
+ <% end %>
</span>
- </label>
- </li>
- <% end end %>
+ </li>
+ <% end end %>
- <% if not self.nocreate then %>
- <li style="padding:0.25em 0">
- <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;
- <%- if not self.widget or self.widget == "checkbox" or self.widget == "radio" then -%>
- <label<%=attr("for", cbid .. "_new")%>></label>
- <%- end -%>
- <div style="padding:0.5em; display:inline">
- <label<%=attr("for", cbid .. "_new")%>><em>
+ <% if not self.nocreate then %>
+ <li value="-"<%= ifattr(not value and self.widget ~= "checkbox", "selected", "selected") %>>
+ <em>
<%- if self.widget == "checkbox" then -%>
<%:create:%>
<%- else -%>
<%:unspecified -or- create:%>
- <%- end -%>&#160;</em></label>
+ <%- end -%>
+ </em>
<input style="display:none" type="password" />
- <input style="width:6em" type="text"<%=attr("name", cbid .. ".newnet")%> onfocus="document.getElementById('<%=cbid%>_new').checked=true" />
- </div>
- </li>
- <% elseif self.widget ~= "checkbox" and self.unspecified then %>
- <li style="padding:0.25em 0">
- <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) ..
- attr("value", "") ..
- ifattr(not value or #value == 0, "checked", "checked")
- %> /> &#160;
- <div style="padding:0.5em; display:inline">
- <label<%=attr("for", cbid .. "_uns")%>><em><%:unspecified%></em></label>
- </div>
- </li>
- <% end %>
-</ul>
+ <input class="create-item-input" type="text" />
+ </li>
+ <% end %>
+ </ul>
+</div>
<%+cbi/valuefooter%>
diff --git a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/ifaces.lua b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/ifaces.lua
index 38e5de7b39..5c630bb5ce 100644
--- a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/ifaces.lua
+++ b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/ifaces.lua
@@ -351,7 +351,6 @@ if has_firewall then
fwzone.template = "cbi/firewall_zonelist"
fwzone.network = arg[1]
- fwzone.rmempty = false
function fwzone.cfgvalue(self, section)
self.iface = section
@@ -360,22 +359,16 @@ if has_firewall then
end
function fwzone.write(self, section, value)
- local zone = fw:get_zone(value)
-
- if not zone and value == '-' then
- value = m:formvalue(self:cbid(section) .. ".newzone")
- if value and #value > 0 then
- zone = fw:add_zone(value)
- else
- fw:del_network(section)
- end
- end
-
+ local zone = fw:get_zone(value) or fw:add_zone(value)
if zone then
fw:del_network(section)
zone:add_network(section)
end
end
+
+ function fwzone.remove(self, section)
+ fw:del_network(section)
+ end
end
diff --git a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/wifi.lua b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/wifi.lua
index cacaa25958..d51a72aba1 100644
--- a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/wifi.lua
+++ b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/wifi.lua
@@ -390,22 +390,16 @@ network.novirtual = true
function network.write(self, section, value)
local i = nw:get_interface(section)
if i then
- if value == '-' then
- value = m:formvalue(self:cbid(section) .. ".newnet")
- if value and #value > 0 then
- local n = nw:add_network(value, {proto="none"})
- if n then n:add_interface(i) end
- else
- local n = i:get_network()
- if n then n:del_interface(i) end
- end
- else
- local v
- for _, v in ipairs(i:get_networks()) do
- v:del_interface(i)
- end
- for v in ut.imatch(value) do
- local n = nw:get_network(v)
+ local _, net, old, new = nil, nil, {}, {}
+
+ for _, net in ipairs(i:get_networks()) do
+ old[net:name()] = true
+ end
+
+ for net in ut.imatch(value) do
+ new[net] = true
+ if not old[net] then
+ local n = nw:get_network(net) or nw:add_network(net, { proto = "none" })
if n then
if not n:is_empty() then
n:set("type", "bridge")
@@ -414,6 +408,15 @@ function network.write(self, section, value)
end
end
end
+
+ for net, _ in pairs(old) do
+ if not new[net] then
+ local n = nw:get_network(net)
+ if n then
+ n:del_interface(i)
+ end
+ end
+ end
end
end
diff --git a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/wifi_add.lua b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/wifi_add.lua
index 8277deb2f6..e8a3058826 100644
--- a/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/wifi_add.lua
+++ b/modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/wifi_add.lua
@@ -94,14 +94,9 @@ function newnet.parse(self, section)
local net, zone
if has_firewall then
- local zval = fwzone:formvalue(section)
- zone = fw:get_zone(zval)
-
- if not zone and zval == '-' then
- zval = m:formvalue(fwzone:cbid(section) .. ".newzone")
- if zval and #zval > 0 then
- zone = fw:add_zone(zval)
- end
+ local value = fwzone:formvalue(section)
+ if value and #value > 0 then
+ zone = fw:get_zone(value) or fw:add_zone(value)
end
end