Merge pull request #1735 from sumpfralle/olsr-jsoninfo-parser-handle-empty-result
[oweals/luci.git] / modules / luci-base / htdocs / luci-static / resources / protocol / static.js
1 'use strict';
2 'require form';
3 'require network';
4 'require validation';
5
6 function isCIDR(value) {
7         return Array.isArray(value) || /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/(\d{1,2}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/.test(value);
8 }
9
10 function calculateBroadcast(s, use_cfgvalue) {
11         var readfn = use_cfgvalue ? 'cfgvalue' : 'formvalue',
12             addropt = s.children.filter(function(o) { return o.option == 'ipaddr'})[0],
13             addrvals = addropt ? L.toArray(addropt[readfn](s.section)) : [],
14             maskopt = s.children.filter(function(o) { return o.option == 'netmask'})[0],
15             maskval = maskopt ? maskopt[readfn](s.section) : null,
16             firstsubnet = maskval ? addrvals[0] + '/' + maskval : addrvals.filter(function(a) { return a.indexOf('/') > 0 })[0];
17
18         if (firstsubnet == null)
19                 return null;
20
21         var addr_mask = firstsubnet.split('/'),
22             addr = validation.parseIPv4(addr_mask[0]),
23             mask = addr_mask[1];
24
25         if (!isNaN(mask))
26                 mask = validation.parseIPv4(network.prefixToMask(+mask));
27         else
28                 mask = validation.parseIPv4(mask);
29
30         var bc = [
31                 addr[0] | (~mask[0] >>> 0 & 255),
32                 addr[1] | (~mask[1] >>> 0 & 255),
33                 addr[2] | (~mask[2] >>> 0 & 255),
34                 addr[3] | (~mask[3] >>> 0 & 255)
35         ];
36
37         return bc.join('.');
38 }
39
40 function validateBroadcast(section_id, value) {
41         var opt = this.map.lookupOption('broadcast', section_id),
42             node = opt ? this.map.findElement('id', opt[0].cbid(section_id)) : null,
43             addr = node ? calculateBroadcast(this.section, false) : null;
44
45         if (node != null) {
46                 if (addr != null)
47                         node.querySelector('input').setAttribute('placeholder', addr);
48                 else
49                         node.querySelector('input').removeAttribute('placeholder');
50         }
51
52         return true;
53 }
54
55 return network.registerProtocol('static', {
56         CBIIPValue: form.Value.extend({
57                 handleSwitch: function(section_id, option_index, ev) {
58                         var maskopt = this.map.lookupOption('netmask', section_id);
59
60                         if (maskopt == null || !this.isValid(section_id))
61                                 return;
62
63                         var maskval = maskopt[0].formvalue(section_id),
64                             addrval = this.formvalue(section_id),
65                             prefix = maskval ? network.maskToPrefix(maskval) : 32;
66
67                         if (prefix == null)
68                                 return;
69
70                         this.datatype = 'or(cidr4,ipmask4)';
71
72                         var parent = L.dom.parent(ev.target, '.cbi-value-field');
73                         L.dom.content(parent, form.DynamicList.prototype.renderWidget.apply(this, [
74                                 section_id,
75                                 option_index,
76                                 addrval ? '%s/%d'.format(addrval, prefix) : ''
77                         ]));
78
79                         var masknode = this.map.findElement('id', maskopt[0].cbid(section_id));
80                         if (masknode) {
81                                 parent = L.dom.parent(masknode, '.cbi-value');
82                                 parent.parentNode.removeChild(parent);
83                         }
84                 },
85
86                 renderWidget: function(section_id, option_index, cfgvalue) {
87                         var maskopt = this.map.lookupOption('netmask', section_id),
88                             widget = isCIDR(cfgvalue) ? 'DynamicList' : 'Value';
89
90                         if (widget == 'DynamicList') {
91                                 this.datatype = 'or(cidr4,ipmask4)';
92                                 this.placeholder = _('Add IPv4 address…');
93                         }
94                         else {
95                                 this.datatype = 'ip4addr("nomask")';
96                         }
97
98                         var node = form[widget].prototype.renderWidget.apply(this, [ section_id, option_index, cfgvalue ]);
99
100                         if (widget == 'Value')
101                                 L.dom.append(node, E('button', {
102                                         'class': 'cbi-button cbi-button-neutral',
103                                         'title': _('Switch to CIDR list notation'),
104                                         'aria-label': _('Switch to CIDR list notation'),
105                                         'click': L.bind(this.handleSwitch, this, section_id, option_index)
106                                 }, '…'));
107
108                         return node;
109                 },
110
111                 validate: validateBroadcast
112         }),
113
114         CBINetmaskValue: form.Value.extend({
115                 render: function(option_index, section_id, in_table) {
116                         var addropt = this.section.children.filter(function(o) { return o.option == 'ipaddr' })[0],
117                             addrval = addropt ? addropt.cfgvalue(section_id) : null;
118
119                         if (addrval != null && isCIDR(addrval))
120                                 return E([]);
121
122                         this.value('255.255.255.0');
123                         this.value('255.255.0.0');
124                         this.value('255.0.0.0');
125
126                         return form.Value.prototype.render.apply(this, [ option_index, section_id, in_table ]);
127                 },
128
129                 validate: validateBroadcast
130         }),
131
132         CBIGatewayValue: form.Value.extend({
133                 datatype: 'ip4addr("nomask")',
134
135                 render: function(option_index, section_id, in_table) {
136                         return network.getWANNetworks().then(L.bind(function(wans) {
137                                 if (wans.length == 1) {
138                                         var gwaddr = wans[0].getGatewayAddr();
139                                         this.placeholder = gwaddr ? '%s (%s)'.format(gwaddr, wans[0].getName()) : '';
140                                 }
141
142                                 return form.Value.prototype.render.apply(this, [ option_index, section_id, in_table ]);
143                         }, this));
144                 },
145
146                 validate: function(section_id, value) {
147                         var addropt = this.section.children.filter(function(o) { return o.option == 'ipaddr' })[0],
148                             addrval = addropt ? L.toArray(addropt.cfgvalue(section_id)) : null;
149
150                         if (addrval != null) {
151                                 for (var i = 0; i < addrval.length; i++) {
152                                         var addr = addrval[i].split('/')[0];
153                                         if (value == addr)
154                                                 return _('The gateway address must not be a local IP address');
155                                 }
156                         }
157
158                         return true;
159                 }
160         }),
161
162         CBIBroadcastValue: form.Value.extend({
163                 datatype: 'ip4addr("nomask")',
164
165                 render: function(option_index, section_id, in_table) {
166                         this.placeholder = calculateBroadcast(this.section, true);
167                         return form.Value.prototype.render.apply(this, [ option_index, section_id, in_table ]);
168                 }
169         }),
170
171         getI18n: function() {
172                 return _('Static address');
173         },
174
175         renderFormOptions: function(s) {
176                 var dev = this.getL2Device() || this.getDevice(), o;
177
178                 s.taboption('general', this.CBIIPValue, 'ipaddr', _('IPv4 address'));
179                 s.taboption('general', this.CBINetmaskValue, 'netmask', _('IPv4 netmask'));
180                 s.taboption('general', this.CBIGatewayValue, 'gateway', _('IPv4 gateway'));
181                 s.taboption('general', this.CBIBroadcastValue, 'broadcast', _('IPv4 broadcast'));
182                 s.taboption('general', form.DynamicList, 'dns', _('Use custom DNS servers'));
183
184                 o = s.taboption('general', form.Value, 'ip6assign', _('IPv6 assignment length'), _('Assign a part of given length of every public IPv6-prefix to this interface'));
185                 o.value('', _('disabled'));
186                 o.value('64');
187                 o.datatype = 'max(64)';
188
189                 o = s.taboption('general', form.Value, 'ip6hint', _('IPv6 assignment hint'), _('Assign prefix parts using this hexadecimal subprefix ID for this interface.'));
190                 o.placeholder = '0';
191                 o.validate = function(section_id, value) {
192                         var n = parseInt(value, 16);
193
194                         if (!/^(0x)?[0-9a-fA-F]+$/.test(value) || isNaN(n) || n >= 0xffffffff)
195                                 return _('Expecting an hexadecimal assignment hint');
196
197                         return true;
198                 };
199                 for (var i = 33; i <= 64; i++)
200                         o.depends('ip6assign', String(i));
201
202                 o = s.taboption('general', form.DynamicList, 'ip6addr', _('IPv6 address'));
203                 o.datatype = 'ip6addr';
204                 o.placeholder = _('Add IPv6 address…');
205                 o.depends('ip6assign', '');
206
207                 o = s.taboption('general', form.Value, 'ip6gw', _('IPv6 gateway'));
208                 o.datatype = 'ip6addr("nomask")';
209                 o.depends('ip6assign', '');
210
211                 o = s.taboption('general', form.Value, 'ip6prefix', _('IPv6 routed prefix'), _('Public prefix routed to this device for distribution to clients.'));
212                 o.datatype = 'ip6addr';
213                 o.depends('ip6assign', '');
214
215                 o = s.taboption('general', form.Value, 'ip6ifaceid', _('IPv6 suffix'), _("Optional. Allowed values: 'eui64', 'random', fixed value like '::1' or '::1:2'. When IPv6 prefix (like 'a:b:c:d::') is received from a delegating server, use the suffix (like '::1') to form the IPv6 address ('a:b:c:d::1') for the interface."));
216                 o.datatype = 'ip6hostid';
217                 o.placeholder = '::1';
218
219                 o = s.taboption('advanced', form.Value, 'macaddr', _('Override MAC address'));
220                 o.datatype = 'macaddr';
221                 o.placeholder = dev ? (dev.getMAC() || '') : '';
222
223                 o = s.taboption('advanced', form.Value, 'mtu', _('Override MTU'));
224                 o.datatype = 'max(9200)';
225                 o.placeholder = dev ? (dev.getMTU() || '1500') : '1500';
226
227                 o = s.taboption('advanced', form.Value, 'metric', _('Use gateway metric'));
228                 o.placeholder = this.getMetric() || '0';
229                 o.datatype = 'uinteger';
230         }
231 });