luci-app-cshark: Add template
[project/luci.git] / modules / luci-base / htdocs / luci-static / resources / tools / widgets.js
1 'use strict';
2 'require ui';
3 'require form';
4 'require network';
5 'require firewall';
6
7 var CBIZoneSelect = form.ListValue.extend({
8 __name__: 'CBI.ZoneSelect',
9
10 load: function(section_id) {
11 return Promise.all([ firewall.getZones(), network.getNetworks() ]).then(L.bind(function(zn) {
12 this.zones = zn[0];
13 this.networks = zn[1];
14
15 return this.super('load', section_id);
16 }, this));
17 },
18
19 filter: function(section_id, value) {
20 return true;
21 },
22
23 lookupZone: function(name) {
24 return this.zones.filter(function(zone) { return zone.getName() == name })[0];
25 },
26
27 lookupNetwork: function(name) {
28 return this.networks.filter(function(network) { return network.getName() == name })[0];
29 },
30
31 renderWidget: function(section_id, option_index, cfgvalue) {
32 var values = L.toArray((cfgvalue != null) ? cfgvalue : this.default),
33 isOutputOnly = false,
34 choices = {};
35
36 if (this.option == 'dest') {
37 for (var i = 0; i < this.section.children.length; i++) {
38 var opt = this.section.children[i];
39 if (opt.option == 'src') {
40 var val = opt.cfgvalue(section_id) || opt.default;
41 isOutputOnly = (val == null || val == '');
42 break;
43 }
44 }
45
46 this.title = isOutputOnly ? _('Output zone') : _('Destination zone');
47 }
48
49 if (this.allowlocal) {
50 choices[''] = E('span', {
51 'class': 'zonebadge',
52 'style': 'background-color:' + firewall.getColorForName(null)
53 }, [
54 E('strong', _('Device')),
55 (this.allowany || this.allowlocal)
56 ? ' (%s)'.format(this.option != 'dest' ? _('output') : _('input')) : ''
57 ]);
58 }
59 else if (!this.multiple && (this.rmempty || this.optional)) {
60 choices[''] = E('span', {
61 'class': 'zonebadge',
62 'style': 'background-color:' + firewall.getColorForName(null)
63 }, E('em', _('unspecified')));
64 }
65
66 if (this.allowany) {
67 choices['*'] = E('span', {
68 'class': 'zonebadge',
69 'style': 'background-color:' + firewall.getColorForName(null)
70 }, [
71 E('strong', _('Any zone')),
72 (this.allowany && this.allowlocal && !isOutputOnly) ? ' (%s)'.format(_('forward')) : ''
73 ]);
74 }
75
76 for (var i = 0; i < this.zones.length; i++) {
77 var zone = this.zones[i],
78 name = zone.getName(),
79 networks = zone.getNetworks(),
80 ifaces = [];
81
82 if (!this.filter(section_id, name))
83 continue;
84
85 for (var j = 0; j < networks.length; j++) {
86 var network = this.lookupNetwork(networks[j]);
87
88 if (!network)
89 continue;
90
91 var span = E('span', {
92 'class': 'ifacebadge' + (network.getName() == this.network ? ' ifacebadge-active' : '')
93 }, network.getName() + ': ');
94
95 var devices = network.isBridge() ? network.getDevices() : L.toArray(network.getDevice());
96
97 for (var k = 0; k < devices.length; k++) {
98 span.appendChild(E('img', {
99 'title': devices[k].getI18n(),
100 'src': L.resource('icons/%s%s.png'.format(devices[k].getType(), devices[k].isUp() ? '' : '_disabled'))
101 }));
102 }
103
104 if (!devices.length)
105 span.appendChild(E('em', _('(empty)')));
106
107 ifaces.push(span);
108 }
109
110 if (!ifaces.length)
111 ifaces.push(E('em', _('(empty)')));
112
113 choices[name] = E('span', {
114 'class': 'zonebadge',
115 'style': 'background-color:' + zone.getColor()
116 }, [ E('strong', name) ].concat(ifaces));
117 }
118
119 var widget = new ui.Dropdown(values, choices, {
120 id: this.cbid(section_id),
121 sort: true,
122 multiple: this.multiple,
123 optional: this.optional || this.rmempty,
124 select_placeholder: E('em', _('unspecified')),
125 display_items: this.display_size || this.size || 3,
126 dropdown_items: this.dropdown_size || this.size || 5,
127 validate: L.bind(this.validate, this, section_id),
128 create: !this.nocreate,
129 create_markup: '' +
130 '<li data-value="{{value}}">' +
131 '<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)">' +
132 '<strong>{{value}}:</strong> <em>('+_('create')+')</em>' +
133 '</span>' +
134 '</li>'
135 });
136
137 var elem = widget.render();
138
139 if (this.option == 'src') {
140 elem.addEventListener('cbi-dropdown-change', L.bind(function(ev) {
141 var opt = this.map.lookupOption('dest', section_id),
142 val = ev.detail.instance.getValue();
143
144 if (opt == null)
145 return;
146
147 var cbid = opt[0].cbid(section_id),
148 label = document.querySelector('label[for="widget.%s"]'.format(cbid)),
149 node = document.getElementById(cbid);
150
151 L.dom.content(label, val == '' ? _('Output zone') : _('Destination zone'));
152
153 if (val == '') {
154 if (L.dom.callClassMethod(node, 'getValue') == '')
155 L.dom.callClassMethod(node, 'setValue', '*');
156
157 var emptyval = node.querySelector('[data-value=""]'),
158 anyval = node.querySelector('[data-value="*"]');
159
160 L.dom.content(anyval.querySelector('span'), E('strong', _('Any zone')));
161
162 if (emptyval != null)
163 emptyval.parentNode.removeChild(emptyval);
164 }
165 else {
166 var anyval = node.querySelector('[data-value="*"]'),
167 emptyval = node.querySelector('[data-value=""]');
168
169 if (emptyval == null) {
170 emptyval = anyval.cloneNode(true);
171 emptyval.removeAttribute('display');
172 emptyval.removeAttribute('selected');
173 emptyval.setAttribute('data-value', '');
174 }
175
176 L.dom.content(emptyval.querySelector('span'), [
177 E('strong', _('Device')), ' (%s)'.format(_('input'))
178 ]);
179
180 L.dom.content(anyval.querySelector('span'), [
181 E('strong', _('Any zone')), ' (%s)'.format(_('forward'))
182 ]);
183
184 anyval.parentNode.insertBefore(emptyval, anyval);
185 }
186
187 }, this));
188 }
189 else if (isOutputOnly) {
190 var emptyval = elem.querySelector('[data-value=""]');
191 emptyval.parentNode.removeChild(emptyval);
192 }
193
194 return elem;
195 },
196 });
197
198 var CBIZoneForwards = form.DummyValue.extend({
199 __name__: 'CBI.ZoneForwards',
200
201 load: function(section_id) {
202 return Promise.all([ firewall.getDefaults(), firewall.getZones(), network.getNetworks() ]).then(L.bind(function(dzn) {
203 this.defaults = dzn[0];
204 this.zones = dzn[1];
205 this.networks = dzn[2];
206
207 return this.super('load', section_id);
208 }, this));
209 },
210
211 renderZone: function(zone) {
212 var name = zone.getName(),
213 networks = zone.getNetworks(),
214 ifaces = [];
215
216 for (var j = 0; j < networks.length; j++) {
217 var network = this.networks.filter(function(net) { return net.getName() == networks[j] })[0];
218
219 if (!network)
220 continue;
221
222 var span = E('span', {
223 'class': 'ifacebadge' + (network.getName() == this.network ? ' ifacebadge-active' : '')
224 }, network.getName() + ': ');
225
226 var devices = network.isBridge() ? network.getDevices() : L.toArray(network.getDevice());
227
228 for (var k = 0; k < devices.length && devices[k]; k++) {
229 span.appendChild(E('img', {
230 'title': devices[k].getI18n(),
231 'src': L.resource('icons/%s%s.png'.format(devices[k].getType(), devices[k].isUp() ? '' : '_disabled'))
232 }));
233 }
234
235 if (!devices.length)
236 span.appendChild(E('em', _('(empty)')));
237
238 ifaces.push(span);
239 }
240
241 if (!ifaces.length)
242 ifaces.push(E('span', { 'class': 'ifacebadge' }, E('em', _('(empty)'))));
243
244 return E('label', {
245 'class': 'zonebadge cbi-tooltip-container',
246 'style': 'background-color:' + zone.getColor()
247 }, [
248 E('strong', name),
249 E('div', { 'class': 'cbi-tooltip' }, ifaces)
250 ]);
251 },
252
253 renderWidget: function(section_id, option_index, cfgvalue) {
254 var value = (cfgvalue != null) ? cfgvalue : this.default,
255 zone = this.zones.filter(function(z) { return z.getName() == value })[0];
256
257 if (!zone)
258 return E([]);
259
260 var forwards = zone.getForwardingsBy('src'),
261 dzones = [];
262
263 for (var i = 0; i < forwards.length; i++) {
264 var dzone = forwards[i].getDestinationZone();
265
266 if (!dzone)
267 continue;
268
269 dzones.push(this.renderZone(dzone));
270 }
271
272 if (!dzones.length)
273 dzones.push(E('label', { 'class': 'zonebadge zonebadge-empty' },
274 E('strong', this.defaults.getForward())));
275
276 return E('div', { 'class': 'zone-forwards' }, [
277 E('div', { 'class': 'zone-src' }, this.renderZone(zone)),
278 E('span', '⇒'),
279 E('div', { 'class': 'zone-dest' }, dzones)
280 ]);
281 },
282 });
283
284 var CBINetworkSelect = form.ListValue.extend({
285 __name__: 'CBI.NetworkSelect',
286
287 load: function(section_id) {
288 return network.getNetworks().then(L.bind(function(networks) {
289 this.networks = networks;
290
291 return this.super('load', section_id);
292 }, this));
293 },
294
295 filter: function(section_id, value) {
296 return true;
297 },
298
299 renderIfaceBadge: function(network) {
300 var span = E('span', { 'class': 'ifacebadge' }, network.getName() + ': '),
301 devices = network.isBridge() ? network.getDevices() : L.toArray(network.getDevice());
302
303 for (var j = 0; j < devices.length && devices[j]; j++) {
304 span.appendChild(E('img', {
305 'title': devices[j].getI18n(),
306 'src': L.resource('icons/%s%s.png'.format(devices[j].getType(), devices[j].isUp() ? '' : '_disabled'))
307 }));
308 }
309
310 if (!devices.length) {
311 span.appendChild(E('em', { 'class': 'hide-close' }, _('(no interfaces attached)')));
312 span.appendChild(E('em', { 'class': 'hide-open' }, '-'));
313 }
314
315 return span;
316 },
317
318 renderWidget: function(section_id, option_index, cfgvalue) {
319 var values = L.toArray((cfgvalue != null) ? cfgvalue : this.default),
320 choices = {},
321 checked = {};
322
323 for (var i = 0; i < values.length; i++)
324 checked[values[i]] = true;
325
326 values = [];
327
328 if (!this.multiple && (this.rmempty || this.optional))
329 choices[''] = E('em', _('unspecified'));
330
331 for (var i = 0; i < this.networks.length; i++) {
332 var network = this.networks[i],
333 name = network.getName();
334
335 if (name == 'loopback' || !this.filter(section_id, name))
336 continue;
337
338 if (this.novirtual && network.isVirtual())
339 continue;
340
341 if (checked[name])
342 values.push(name);
343
344 choices[name] = this.renderIfaceBadge(network);
345 }
346
347 var widget = new ui.Dropdown(this.multiple ? values : values[0], choices, {
348 id: this.cbid(section_id),
349 sort: true,
350 multiple: this.multiple,
351 optional: this.optional || this.rmempty,
352 select_placeholder: E('em', _('unspecified')),
353 display_items: this.display_size || this.size || 3,
354 dropdown_items: this.dropdown_size || this.size || 5,
355 validate: L.bind(this.validate, this, section_id),
356 create: !this.nocreate,
357 create_markup: '' +
358 '<li data-value="{{value}}">' +
359 '<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)">' +
360 '{{value}}: <em>('+_('create')+')</em>' +
361 '</span>' +
362 '</li>'
363 });
364
365 return widget.render();
366 },
367
368 textvalue: function(section_id) {
369 var cfgvalue = this.cfgvalue(section_id),
370 values = L.toArray((cfgvalue != null) ? cfgvalue : this.default),
371 rv = E([]);
372
373 for (var i = 0; i < (this.networks || []).length; i++) {
374 var network = this.networks[i],
375 name = network.getName();
376
377 if (values.indexOf(name) == -1)
378 continue;
379
380 if (rv.length)
381 L.dom.append(rv, ' ');
382
383 L.dom.append(rv, this.renderIfaceBadge(network));
384 }
385
386 if (!rv.firstChild)
387 rv.appendChild(E('em', _('unspecified')));
388
389 return rv;
390 },
391 });
392
393
394 return L.Class.extend({
395 ZoneSelect: CBIZoneSelect,
396 ZoneForwards: CBIZoneForwards,
397 NetworkSelect: CBINetworkSelect
398 });