+ },
+
+ transformHostHints: function(family, hosts) {
+ var choice_values = [], choice_labels = {};
+
+ if (!family || family == 'ipv4') {
+ L.sortedKeys(hosts, 'ipv4', 'addr').forEach(function(mac) {
+ var val = hosts[mac].ipv4,
+ txt = hosts[mac].name || mac;
+
+ choice_values.push(val);
+ choice_labels[val] = E([], [ val, ' (', E('strong', {}, [txt]), ')' ]);
+ });
+ }
+
+ if (!family || family == 'ipv6') {
+ L.sortedKeys(hosts, 'ipv6', 'addr').forEach(function(mac) {
+ var val = hosts[mac].ipv6,
+ txt = hosts[mac].name || mac;
+
+ choice_values.push(val);
+ choice_labels[val] = E([], [ val, ' (', E('strong', {}, [txt]), ')' ]);
+ });
+ }
+
+ return [choice_values, choice_labels];
+ },
+
+ updateHostHints: function(map, section_id, option, family, hosts) {
+ var opt = map.lookupOption(option, section_id)[0].getUIElement(section_id),
+ choices = this.transformHostHints(family, hosts);
+
+ opt.clearChoices();
+ opt.addChoices(choices[0], choices[1]);
+ },
+
+ addIPOption: function(s, tab, name, label, description, family, hosts, multiple) {
+ var o = s.taboption(tab, multiple ? form.DynamicList : form.Value, name, label, description);
+
+ o.modalonly = true;
+ o.datatype = 'list(neg(ipmask))';
+ o.placeholder = multiple ? _('-- add IP --') : _('any');
+
+ if (family != null) {
+ var choices = this.transformHostHints(family, hosts);
+
+ for (var i = 0; i < choices[0].length; i++)
+ o.value(choices[0][i], choices[1][choices[0][i]]);
+ }
+
+ /* force combobox rendering */
+ o.transformChoices = function() {
+ return this.super('transformChoices', []) || {};
+ };
+
+ return o;
+ },
+
+ addLocalIPOption: function(s, tab, name, label, description, devices) {
+ var o = s.taboption(tab, form.Value, name, label, description);
+
+ o.modalonly = true;
+ o.datatype = 'ip4addr("nomask")';
+ o.placeholder = _('any');
+
+ L.sortedKeys(devices, 'name').forEach(function(dev) {
+ var ip4addrs = devices[dev].ipaddrs;
+
+ if (!L.isObject(devices[dev].flags) || !Array.isArray(ip4addrs) || devices[dev].flags.loopback)
+ return;
+
+ for (var i = 0; i < ip4addrs.length; i++) {
+ if (!L.isObject(ip4addrs[i]) || !ip4addrs[i].address)
+ continue;
+
+ o.value(ip4addrs[i].address, E([], [
+ ip4addrs[i].address, ' (', E('strong', {}, [dev]), ')'
+ ]));
+ }
+ });
+
+ return o;
+ },
+
+ addMACOption: function(s, tab, name, label, description, hosts) {
+ var o = s.taboption(tab, form.DynamicList, name, label, description);
+
+ o.modalonly = true;
+ o.datatype = 'list(macaddr)';
+ o.placeholder = _('-- add MAC --');
+
+ L.sortedKeys(hosts).forEach(function(mac) {
+ o.value(mac, E([], [ mac, ' (', E('strong', {}, [
+ hosts[mac].name || hosts[mac].ipv4 || hosts[mac].ipv6 || '?'
+ ]), ')' ]));
+ });
+
+ return o;
+ },
+
+ CBIProtocolSelect: form.MultiValue.extend({
+ __name__: 'CBI.ProtocolSelect',
+
+ addChoice: function(value, label) {
+ if (!Array.isArray(this.keylist) || this.keylist.indexOf(value) == -1)
+ this.value(value, label);
+ },
+
+ load: function(section_id) {
+ var cfgvalue = L.toArray(this.super('load', [section_id]) || this.default).sort();
+
+ ['all', 'tcp', 'udp', 'icmp'].concat(cfgvalue).forEach(L.bind(function(value) {
+ switch (value) {
+ case 'all':
+ case 'any':
+ case '*':
+ this.addChoice('all', _('Any'));
+ break;
+
+ case 'tcpudp':
+ this.addChoice('tcp', 'TCP');
+ this.addChoice('udp', 'UDP');
+ break;
+
+ default:
+ var m = value.match(/^(0x[0-9a-f]{1,2}|[0-9]{1,3})$/),
+ p = lookupProto(m ? +m[1] : value);
+
+ this.addChoice(p[2], p[1]);
+ break;
+ }
+ }, this));
+
+ return cfgvalue;
+ },
+
+ renderWidget: function(section_id, option_index, cfgvalue) {
+ var value = (cfgvalue != null) ? cfgvalue : this.default,
+ choices = this.transformChoices();
+
+ var widget = new ui.Dropdown(L.toArray(value), choices, {
+ id: this.cbid(section_id),
+ sort: this.keylist,
+ multiple: true,
+ optional: false,
+ display_items: 10,
+ dropdown_items: -1,
+ create: true,
+ validate: function(value) {
+ var v = L.toArray(value);
+
+ for (var i = 0; i < v.length; i++) {
+ if (v[i] == 'all')
+ continue;
+
+ var m = v[i].match(/^(0x[0-9a-f]{1,2}|[0-9]{1,3})$/);
+
+ if (m ? (+m[1] > 255) : (lookupProto(v[i])[0] == -1))
+ return _('Unrecognized protocol');
+ }
+
+ return true;
+ }
+ });
+
+ widget.createChoiceElement = function(sb, value) {
+ var m = value.match(/^(0x[0-9a-f]{1,2}|[0-9]{1,3})$/),
+ p = lookupProto(lookupProto(m ? +m[1] : value)[0]);
+
+ return ui.Dropdown.prototype.createChoiceElement.call(this, sb, p[2], p[1]);
+ };
+
+ widget.createItems = function(sb, value) {
+ var values = L.toArray(value).map(function(value) {
+ var m = value.match(/^(0x[0-9a-f]{1,2}|[0-9]{1,3})$/),
+ p = lookupProto(m ? +m[1] : value);
+
+ return (p[0] > -1) ? p[2] : value;
+ });
+
+ return ui.Dropdown.prototype.createItems.call(this, sb, values.join(' '));
+ };
+
+ widget.toggleItem = function(sb, li) {
+ var value = li.getAttribute('data-value'),
+ toggleFn = ui.Dropdown.prototype.toggleItem;
+
+ toggleFn.call(this, sb, li);
+
+ if (value == 'all') {
+ var items = li.parentNode.querySelectorAll('li[data-value]');
+
+ for (var j = 0; j < items.length; j++)
+ if (items[j] !== li)
+ toggleFn.call(this, sb, items[j], false);
+ }
+ else {
+ toggleFn.call(this, sb, li.parentNode.querySelector('li[data-value="all"]'), false);
+ }
+ };
+
+ return widget.render();
+ }
+ })