Translated using Weblate (French)
[oweals/luci.git] / docs / jsapi / network.js.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4     <meta charset="utf-8">
5     <title>Source: network.js</title>
6     
7     
8     <script src="scripts/prettify/prettify.js"></script>
9     <script src="scripts/prettify/lang-css.js"></script>
10     <script src="scripts/jquery.min.js"></script>
11     <!--[if lt IE 9]>
12       <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
13     <![endif]-->
14     <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
15     <link type="text/css" rel="stylesheet" href="styles/bootstrap.min.css">
16     <link type="text/css" rel="stylesheet" href="styles/jaguar.css">
17     
18     
19     <script>
20     var config = {"monospaceLinks":true,"cleverLinks":true,"default":{"outputSourceFiles":true}};
21     </script>
22     
23
24     
25 </head>
26 <body>
27 <div id="wrap" class="clearfix">
28     
29 <div class="navigation">
30     <h3 class="applicationName"><a href="index.html"></a></h3>
31
32     <div class="search">
33         <input id="search" type="text" class="form-control input-sm" placeholder="Search Documentations">
34     </div>
35     <ul class="list">
36     
37         <li class="item" data-name="LuCI">
38             <span class="title">
39                 <a href="LuCI.html">LuCI</a>
40                 
41             </span>
42             <ul class="members itemMembers">
43             
44             <span class="subtitle">Members</span>
45             
46                 <li data-name="LuCI#env"><a href="LuCI.html#env">env</a></li>
47             
48             </ul>
49             <ul class="typedefs itemMembers">
50             
51             <span class="subtitle">Typedefs</span>
52             
53                 <li data-name="LuCI.requestCallbackFn"><a href="LuCI.html#.requestCallbackFn">requestCallbackFn</a></li>
54             
55             </ul>
56             <ul class="typedefs itemMembers">
57             
58             </ul>
59             <ul class="methods itemMembers">
60             
61             <span class="subtitle">Methods</span>
62             
63                 <li data-name="LuCI#bind"><a href="LuCI.html#bind">bind</a></li>
64             
65                 <li data-name="LuCI#error"><a href="LuCI.html#error">error</a></li>
66             
67                 <li data-name="LuCI#get"><a href="LuCI.html#get">get</a></li>
68             
69                 <li data-name="LuCI#halt"><a href="LuCI.html#halt">halt</a></li>
70             
71                 <li data-name="LuCI#hasSystemFeature"><a href="LuCI.html#hasSystemFeature">hasSystemFeature</a></li>
72             
73                 <li data-name="LuCI#isObject"><a href="LuCI.html#isObject">isObject</a></li>
74             
75                 <li data-name="LuCI#location"><a href="LuCI.html#location">location</a></li>
76             
77                 <li data-name="LuCI#path"><a href="LuCI.html#path">path</a></li>
78             
79                 <li data-name="LuCI#poll"><a href="LuCI.html#poll">poll</a></li>
80             
81                 <li data-name="LuCI#post"><a href="LuCI.html#post">post</a></li>
82             
83                 <li data-name="LuCI#raise"><a href="LuCI.html#raise">raise</a></li>
84             
85                 <li data-name="LuCI#require"><a href="LuCI.html#require">require</a></li>
86             
87                 <li data-name="LuCI#resolveDefault"><a href="LuCI.html#resolveDefault">resolveDefault</a></li>
88             
89                 <li data-name="LuCI#resource"><a href="LuCI.html#resource">resource</a></li>
90             
91                 <li data-name="LuCI#run"><a href="LuCI.html#run">run</a></li>
92             
93                 <li data-name="LuCI#sortedKeys"><a href="LuCI.html#sortedKeys">sortedKeys</a></li>
94             
95                 <li data-name="LuCI#stop"><a href="LuCI.html#stop">stop</a></li>
96             
97                 <li data-name="LuCI#toArray"><a href="LuCI.html#toArray">toArray</a></li>
98             
99                 <li data-name="LuCI#url"><a href="LuCI.html#url">url</a></li>
100             
101             </ul>
102             <ul class="events itemMembers">
103             
104             </ul>
105         </li>
106     
107         <li class="item" data-name="LuCI.Class">
108             <span class="title">
109                 <a href="LuCI.Class.html">LuCI.Class</a>
110                 
111             </span>
112             <ul class="members itemMembers">
113             
114             </ul>
115             <ul class="typedefs itemMembers">
116             
117             </ul>
118             <ul class="typedefs itemMembers">
119             
120             </ul>
121             <ul class="methods itemMembers">
122             
123             <span class="subtitle">Methods</span>
124             
125                 <li data-name="LuCI.Class.extend"><a href="LuCI.Class.html#.extend">extend</a></li>
126             
127                 <li data-name="LuCI.Class.instantiate"><a href="LuCI.Class.html#.instantiate">instantiate</a></li>
128             
129                 <li data-name="LuCI.Class.isSubclass"><a href="LuCI.Class.html#.isSubclass">isSubclass</a></li>
130             
131                 <li data-name="LuCI.Class.singleton"><a href="LuCI.Class.html#.singleton">singleton</a></li>
132             
133                 <li data-name="LuCI.Class#super"><a href="LuCI.Class.html#super">super</a></li>
134             
135                 <li data-name="LuCI.Class#varargs"><a href="LuCI.Class.html#varargs">varargs</a></li>
136             
137             </ul>
138             <ul class="events itemMembers">
139             
140             </ul>
141         </li>
142     
143         <li class="item" data-name="LuCI.dom">
144             <span class="title">
145                 <a href="LuCI.dom.html">LuCI.dom</a>
146                 
147             </span>
148             <ul class="members itemMembers">
149             
150             </ul>
151             <ul class="typedefs itemMembers">
152             
153             <span class="subtitle">Typedefs</span>
154             
155                 <li data-name="LuCI.dom~ignoreCallbackFn"><a href="LuCI.dom.html#~ignoreCallbackFn">ignoreCallbackFn</a></li>
156             
157             </ul>
158             <ul class="typedefs itemMembers">
159             
160             </ul>
161             <ul class="methods itemMembers">
162             
163             <span class="subtitle">Methods</span>
164             
165                 <li data-name="LuCI.dom#append"><a href="LuCI.dom.html#append">append</a></li>
166             
167                 <li data-name="LuCI.dom#attr"><a href="LuCI.dom.html#attr">attr</a></li>
168             
169                 <li data-name="LuCI.dom#bindClassInstance"><a href="LuCI.dom.html#bindClassInstance">bindClassInstance</a></li>
170             
171                 <li data-name="LuCI.dom#callClassMethod"><a href="LuCI.dom.html#callClassMethod">callClassMethod</a></li>
172             
173                 <li data-name="LuCI.dom#content"><a href="LuCI.dom.html#content">content</a></li>
174             
175                 <li data-name="LuCI.dom#create"><a href="LuCI.dom.html#create">create</a></li>
176             
177                 <li data-name="LuCI.dom#data"><a href="LuCI.dom.html#data">data</a></li>
178             
179                 <li data-name="LuCI.dom#elem"><a href="LuCI.dom.html#elem">elem</a></li>
180             
181                 <li data-name="LuCI.dom#findClassInstance"><a href="LuCI.dom.html#findClassInstance">findClassInstance</a></li>
182             
183                 <li data-name="LuCI.dom#isEmpty"><a href="LuCI.dom.html#isEmpty">isEmpty</a></li>
184             
185                 <li data-name="LuCI.dom#matches"><a href="LuCI.dom.html#matches">matches</a></li>
186             
187                 <li data-name="LuCI.dom#parent"><a href="LuCI.dom.html#parent">parent</a></li>
188             
189                 <li data-name="LuCI.dom#parse"><a href="LuCI.dom.html#parse">parse</a></li>
190             
191             </ul>
192             <ul class="events itemMembers">
193             
194             </ul>
195         </li>
196     
197         <li class="item" data-name="LuCI.fs">
198             <span class="title">
199                 <a href="LuCI.fs.html">LuCI.fs</a>
200                 
201             </span>
202             <ul class="members itemMembers">
203             
204             </ul>
205             <ul class="typedefs itemMembers">
206             
207             <span class="subtitle">Typedefs</span>
208             
209                 <li data-name="LuCI.fs.FileExecResult"><a href="LuCI.fs.html#.FileExecResult">FileExecResult</a></li>
210             
211                 <li data-name="LuCI.fs.FileStatEntry"><a href="LuCI.fs.html#.FileStatEntry">FileStatEntry</a></li>
212             
213             </ul>
214             <ul class="typedefs itemMembers">
215             
216             </ul>
217             <ul class="methods itemMembers">
218             
219             <span class="subtitle">Methods</span>
220             
221                 <li data-name="LuCI.fs#exec"><a href="LuCI.fs.html#exec">exec</a></li>
222             
223                 <li data-name="LuCI.fs#lines"><a href="LuCI.fs.html#lines">lines</a></li>
224             
225                 <li data-name="LuCI.fs#list"><a href="LuCI.fs.html#list">list</a></li>
226             
227                 <li data-name="LuCI.fs#read"><a href="LuCI.fs.html#read">read</a></li>
228             
229                 <li data-name="LuCI.fs#remove"><a href="LuCI.fs.html#remove">remove</a></li>
230             
231                 <li data-name="LuCI.fs#stat"><a href="LuCI.fs.html#stat">stat</a></li>
232             
233                 <li data-name="LuCI.fs#trimmed"><a href="LuCI.fs.html#trimmed">trimmed</a></li>
234             
235                 <li data-name="LuCI.fs#write"><a href="LuCI.fs.html#write">write</a></li>
236             
237             </ul>
238             <ul class="events itemMembers">
239             
240             </ul>
241         </li>
242     
243         <li class="item" data-name="LuCI.Headers">
244             <span class="title">
245                 <a href="LuCI.Headers.html">LuCI.Headers</a>
246                 
247             </span>
248             <ul class="members itemMembers">
249             
250             </ul>
251             <ul class="typedefs itemMembers">
252             
253             </ul>
254             <ul class="typedefs itemMembers">
255             
256             </ul>
257             <ul class="methods itemMembers">
258             
259             <span class="subtitle">Methods</span>
260             
261                 <li data-name="LuCI.Headers#get"><a href="LuCI.Headers.html#get">get</a></li>
262             
263                 <li data-name="LuCI.Headers#has"><a href="LuCI.Headers.html#has">has</a></li>
264             
265             </ul>
266             <ul class="events itemMembers">
267             
268             </ul>
269         </li>
270     
271         <li class="item" data-name="LuCI.Network">
272             <span class="title">
273                 <a href="LuCI.Network.html">LuCI.Network</a>
274                 
275             </span>
276             <ul class="members itemMembers">
277             
278             </ul>
279             <ul class="typedefs itemMembers">
280             
281             <span class="subtitle">Typedefs</span>
282             
283                 <li data-name="LuCI.Network.SwitchTopology"><a href="LuCI.Network.html#.SwitchTopology">SwitchTopology</a></li>
284             
285                 <li data-name="LuCI.Network.WifiEncryption"><a href="LuCI.Network.html#.WifiEncryption">WifiEncryption</a></li>
286             
287                 <li data-name="LuCI.Network.WifiPeerEntry"><a href="LuCI.Network.html#.WifiPeerEntry">WifiPeerEntry</a></li>
288             
289                 <li data-name="LuCI.Network.WifiRateEntry"><a href="LuCI.Network.html#.WifiRateEntry">WifiRateEntry</a></li>
290             
291                 <li data-name="LuCI.Network.WifiScanResult"><a href="LuCI.Network.html#.WifiScanResult">WifiScanResult</a></li>
292             
293             </ul>
294             <ul class="typedefs itemMembers">
295             
296             </ul>
297             <ul class="methods itemMembers">
298             
299             <span class="subtitle">Methods</span>
300             
301                 <li data-name="LuCI.Network#addNetwork"><a href="LuCI.Network.html#addNetwork">addNetwork</a></li>
302             
303                 <li data-name="LuCI.Network#addWifiNetwork"><a href="LuCI.Network.html#addWifiNetwork">addWifiNetwork</a></li>
304             
305                 <li data-name="LuCI.Network#deleteNetwork"><a href="LuCI.Network.html#deleteNetwork">deleteNetwork</a></li>
306             
307                 <li data-name="LuCI.Network#deleteWifiNetwork"><a href="LuCI.Network.html#deleteWifiNetwork">deleteWifiNetwork</a></li>
308             
309                 <li data-name="LuCI.Network#flushCache"><a href="LuCI.Network.html#flushCache">flushCache</a></li>
310             
311                 <li data-name="LuCI.Network#formatWifiEncryption"><a href="LuCI.Network.html#formatWifiEncryption">formatWifiEncryption</a></li>
312             
313                 <li data-name="LuCI.Network#getDevice"><a href="LuCI.Network.html#getDevice">getDevice</a></li>
314             
315                 <li data-name="LuCI.Network#getDevices"><a href="LuCI.Network.html#getDevices">getDevices</a></li>
316             
317                 <li data-name="LuCI.Network#getDSLModemType"><a href="LuCI.Network.html#getDSLModemType">getDSLModemType</a></li>
318             
319                 <li data-name="LuCI.Network#getHostHints"><a href="LuCI.Network.html#getHostHints">getHostHints</a></li>
320             
321                 <li data-name="LuCI.Network#getIfnameOf"><a href="LuCI.Network.html#getIfnameOf">getIfnameOf</a></li>
322             
323                 <li data-name="LuCI.Network#getNetwork"><a href="LuCI.Network.html#getNetwork">getNetwork</a></li>
324             
325                 <li data-name="LuCI.Network#getNetworks"><a href="LuCI.Network.html#getNetworks">getNetworks</a></li>
326             
327                 <li data-name="LuCI.Network#getProtocol"><a href="LuCI.Network.html#getProtocol">getProtocol</a></li>
328             
329                 <li data-name="LuCI.Network#getProtocols"><a href="LuCI.Network.html#getProtocols">getProtocols</a></li>
330             
331                 <li data-name="LuCI.Network#getSwitchTopologies"><a href="LuCI.Network.html#getSwitchTopologies">getSwitchTopologies</a></li>
332             
333                 <li data-name="LuCI.Network#getWAN6Networks"><a href="LuCI.Network.html#getWAN6Networks">getWAN6Networks</a></li>
334             
335                 <li data-name="LuCI.Network#getWANNetworks"><a href="LuCI.Network.html#getWANNetworks">getWANNetworks</a></li>
336             
337                 <li data-name="LuCI.Network#getWifiDevice"><a href="LuCI.Network.html#getWifiDevice">getWifiDevice</a></li>
338             
339                 <li data-name="LuCI.Network#getWifiDevices"><a href="LuCI.Network.html#getWifiDevices">getWifiDevices</a></li>
340             
341                 <li data-name="LuCI.Network#getWifiNetwork"><a href="LuCI.Network.html#getWifiNetwork">getWifiNetwork</a></li>
342             
343                 <li data-name="LuCI.Network#getWifiNetworks"><a href="LuCI.Network.html#getWifiNetworks">getWifiNetworks</a></li>
344             
345                 <li data-name="LuCI.Network#isIgnoredDevice"><a href="LuCI.Network.html#isIgnoredDevice">isIgnoredDevice</a></li>
346             
347                 <li data-name="LuCI.Network#maskToPrefix"><a href="LuCI.Network.html#maskToPrefix">maskToPrefix</a></li>
348             
349                 <li data-name="LuCI.Network#prefixToMask"><a href="LuCI.Network.html#prefixToMask">prefixToMask</a></li>
350             
351                 <li data-name="LuCI.Network#registerErrorCode"><a href="LuCI.Network.html#registerErrorCode">registerErrorCode</a></li>
352             
353                 <li data-name="LuCI.Network#registerPatternVirtual"><a href="LuCI.Network.html#registerPatternVirtual">registerPatternVirtual</a></li>
354             
355                 <li data-name="LuCI.Network#registerProtocol"><a href="LuCI.Network.html#registerProtocol">registerProtocol</a></li>
356             
357                 <li data-name="LuCI.Network#renameNetwork"><a href="LuCI.Network.html#renameNetwork">renameNetwork</a></li>
358             
359             </ul>
360             <ul class="events itemMembers">
361             
362             </ul>
363         </li>
364     
365         <li class="item" data-name="LuCI.Network.Device">
366             <span class="title">
367                 <a href="LuCI.Network.Device.html">LuCI.Network.Device</a>
368                 
369             </span>
370             <ul class="members itemMembers">
371             
372             </ul>
373             <ul class="typedefs itemMembers">
374             
375             </ul>
376             <ul class="typedefs itemMembers">
377             
378             </ul>
379             <ul class="methods itemMembers">
380             
381             <span class="subtitle">Methods</span>
382             
383                 <li data-name="LuCI.Network.Device#getBridgeID"><a href="LuCI.Network.Device.html#getBridgeID">getBridgeID</a></li>
384             
385                 <li data-name="LuCI.Network.Device#getBridgeSTP"><a href="LuCI.Network.Device.html#getBridgeSTP">getBridgeSTP</a></li>
386             
387                 <li data-name="LuCI.Network.Device#getI18n"><a href="LuCI.Network.Device.html#getI18n">getI18n</a></li>
388             
389                 <li data-name="LuCI.Network.Device#getIP6Addrs"><a href="LuCI.Network.Device.html#getIP6Addrs">getIP6Addrs</a></li>
390             
391                 <li data-name="LuCI.Network.Device#getIPAddrs"><a href="LuCI.Network.Device.html#getIPAddrs">getIPAddrs</a></li>
392             
393                 <li data-name="LuCI.Network.Device#getMAC"><a href="LuCI.Network.Device.html#getMAC">getMAC</a></li>
394             
395                 <li data-name="LuCI.Network.Device#getMTU"><a href="LuCI.Network.Device.html#getMTU">getMTU</a></li>
396             
397                 <li data-name="LuCI.Network.Device#getName"><a href="LuCI.Network.Device.html#getName">getName</a></li>
398             
399                 <li data-name="LuCI.Network.Device#getNetwork"><a href="LuCI.Network.Device.html#getNetwork">getNetwork</a></li>
400             
401                 <li data-name="LuCI.Network.Device#getNetworks"><a href="LuCI.Network.Device.html#getNetworks">getNetworks</a></li>
402             
403                 <li data-name="LuCI.Network.Device#getPorts"><a href="LuCI.Network.Device.html#getPorts">getPorts</a></li>
404             
405                 <li data-name="LuCI.Network.Device#getRXBytes"><a href="LuCI.Network.Device.html#getRXBytes">getRXBytes</a></li>
406             
407                 <li data-name="LuCI.Network.Device#getRXPackets"><a href="LuCI.Network.Device.html#getRXPackets">getRXPackets</a></li>
408             
409                 <li data-name="LuCI.Network.Device#getShortName"><a href="LuCI.Network.Device.html#getShortName">getShortName</a></li>
410             
411                 <li data-name="LuCI.Network.Device#getTXBytes"><a href="LuCI.Network.Device.html#getTXBytes">getTXBytes</a></li>
412             
413                 <li data-name="LuCI.Network.Device#getTXPackets"><a href="LuCI.Network.Device.html#getTXPackets">getTXPackets</a></li>
414             
415                 <li data-name="LuCI.Network.Device#getType"><a href="LuCI.Network.Device.html#getType">getType</a></li>
416             
417                 <li data-name="LuCI.Network.Device#getTypeI18n"><a href="LuCI.Network.Device.html#getTypeI18n">getTypeI18n</a></li>
418             
419                 <li data-name="LuCI.Network.Device#getWifiNetwork"><a href="LuCI.Network.Device.html#getWifiNetwork">getWifiNetwork</a></li>
420             
421                 <li data-name="LuCI.Network.Device#isBridge"><a href="LuCI.Network.Device.html#isBridge">isBridge</a></li>
422             
423                 <li data-name="LuCI.Network.Device#isBridgePort"><a href="LuCI.Network.Device.html#isBridgePort">isBridgePort</a></li>
424             
425                 <li data-name="LuCI.Network.Device#isUp"><a href="LuCI.Network.Device.html#isUp">isUp</a></li>
426             
427             </ul>
428             <ul class="events itemMembers">
429             
430             </ul>
431         </li>
432     
433         <li class="item" data-name="LuCI.Network.Hosts">
434             <span class="title">
435                 <a href="LuCI.Network.Hosts.html">LuCI.Network.Hosts</a>
436                 
437             </span>
438             <ul class="members itemMembers">
439             
440             </ul>
441             <ul class="typedefs itemMembers">
442             
443             </ul>
444             <ul class="typedefs itemMembers">
445             
446             </ul>
447             <ul class="methods itemMembers">
448             
449             <span class="subtitle">Methods</span>
450             
451                 <li data-name="LuCI.Network.Hosts#getHostnameByIP6Addr"><a href="LuCI.Network.Hosts.html#getHostnameByIP6Addr">getHostnameByIP6Addr</a></li>
452             
453                 <li data-name="LuCI.Network.Hosts#getHostnameByIPAddr"><a href="LuCI.Network.Hosts.html#getHostnameByIPAddr">getHostnameByIPAddr</a></li>
454             
455                 <li data-name="LuCI.Network.Hosts#getHostnameByMACAddr"><a href="LuCI.Network.Hosts.html#getHostnameByMACAddr">getHostnameByMACAddr</a></li>
456             
457                 <li data-name="LuCI.Network.Hosts#getIP6AddrByMACAddr"><a href="LuCI.Network.Hosts.html#getIP6AddrByMACAddr">getIP6AddrByMACAddr</a></li>
458             
459                 <li data-name="LuCI.Network.Hosts#getIPAddrByMACAddr"><a href="LuCI.Network.Hosts.html#getIPAddrByMACAddr">getIPAddrByMACAddr</a></li>
460             
461                 <li data-name="LuCI.Network.Hosts#getMACAddrByIP6Addr"><a href="LuCI.Network.Hosts.html#getMACAddrByIP6Addr">getMACAddrByIP6Addr</a></li>
462             
463                 <li data-name="LuCI.Network.Hosts#getMACAddrByIPAddr"><a href="LuCI.Network.Hosts.html#getMACAddrByIPAddr">getMACAddrByIPAddr</a></li>
464             
465                 <li data-name="LuCI.Network.Hosts#getMACHints"><a href="LuCI.Network.Hosts.html#getMACHints">getMACHints</a></li>
466             
467             </ul>
468             <ul class="events itemMembers">
469             
470             </ul>
471         </li>
472     
473         <li class="item" data-name="LuCI.Network.Protocol">
474             <span class="title">
475                 <a href="LuCI.Network.Protocol.html">LuCI.Network.Protocol</a>
476                 
477             </span>
478             <ul class="members itemMembers">
479             
480             </ul>
481             <ul class="typedefs itemMembers">
482             
483             </ul>
484             <ul class="typedefs itemMembers">
485             
486             </ul>
487             <ul class="methods itemMembers">
488             
489             <span class="subtitle">Methods</span>
490             
491                 <li data-name="LuCI.Network.Protocol#addDevice"><a href="LuCI.Network.Protocol.html#addDevice">addDevice</a></li>
492             
493                 <li data-name="LuCI.Network.Protocol#containsDevice"><a href="LuCI.Network.Protocol.html#containsDevice">containsDevice</a></li>
494             
495                 <li data-name="LuCI.Network.Protocol#deleteDevice"><a href="LuCI.Network.Protocol.html#deleteDevice">deleteDevice</a></li>
496             
497                 <li data-name="LuCI.Network.Protocol#get"><a href="LuCI.Network.Protocol.html#get">get</a></li>
498             
499                 <li data-name="LuCI.Network.Protocol#getDevice"><a href="LuCI.Network.Protocol.html#getDevice">getDevice</a></li>
500             
501                 <li data-name="LuCI.Network.Protocol#getDevices"><a href="LuCI.Network.Protocol.html#getDevices">getDevices</a></li>
502             
503                 <li data-name="LuCI.Network.Protocol#getDNS6Addrs"><a href="LuCI.Network.Protocol.html#getDNS6Addrs">getDNS6Addrs</a></li>
504             
505                 <li data-name="LuCI.Network.Protocol#getDNSAddrs"><a href="LuCI.Network.Protocol.html#getDNSAddrs">getDNSAddrs</a></li>
506             
507                 <li data-name="LuCI.Network.Protocol#getErrors"><a href="LuCI.Network.Protocol.html#getErrors">getErrors</a></li>
508             
509                 <li data-name="LuCI.Network.Protocol#getExpiry"><a href="LuCI.Network.Protocol.html#getExpiry">getExpiry</a></li>
510             
511                 <li data-name="LuCI.Network.Protocol#getGateway6Addr"><a href="LuCI.Network.Protocol.html#getGateway6Addr">getGateway6Addr</a></li>
512             
513                 <li data-name="LuCI.Network.Protocol#getGatewayAddr"><a href="LuCI.Network.Protocol.html#getGatewayAddr">getGatewayAddr</a></li>
514             
515                 <li data-name="LuCI.Network.Protocol#getI18n"><a href="LuCI.Network.Protocol.html#getI18n">getI18n</a></li>
516             
517                 <li data-name="LuCI.Network.Protocol#getIfname"><a href="LuCI.Network.Protocol.html#getIfname">getIfname</a></li>
518             
519                 <li data-name="LuCI.Network.Protocol#getIP6Addr"><a href="LuCI.Network.Protocol.html#getIP6Addr">getIP6Addr</a></li>
520             
521                 <li data-name="LuCI.Network.Protocol#getIP6Addrs"><a href="LuCI.Network.Protocol.html#getIP6Addrs">getIP6Addrs</a></li>
522             
523                 <li data-name="LuCI.Network.Protocol#getIP6Prefix"><a href="LuCI.Network.Protocol.html#getIP6Prefix">getIP6Prefix</a></li>
524             
525                 <li data-name="LuCI.Network.Protocol#getIPAddr"><a href="LuCI.Network.Protocol.html#getIPAddr">getIPAddr</a></li>
526             
527                 <li data-name="LuCI.Network.Protocol#getIPAddrs"><a href="LuCI.Network.Protocol.html#getIPAddrs">getIPAddrs</a></li>
528             
529                 <li data-name="LuCI.Network.Protocol#getL2Device"><a href="LuCI.Network.Protocol.html#getL2Device">getL2Device</a></li>
530             
531                 <li data-name="LuCI.Network.Protocol#getL3Device"><a href="LuCI.Network.Protocol.html#getL3Device">getL3Device</a></li>
532             
533                 <li data-name="LuCI.Network.Protocol#getMetric"><a href="LuCI.Network.Protocol.html#getMetric">getMetric</a></li>
534             
535                 <li data-name="LuCI.Network.Protocol#getName"><a href="LuCI.Network.Protocol.html#getName">getName</a></li>
536             
537                 <li data-name="LuCI.Network.Protocol#getNetmask"><a href="LuCI.Network.Protocol.html#getNetmask">getNetmask</a></li>
538             
539                 <li data-name="LuCI.Network.Protocol#getOpkgPackage"><a href="LuCI.Network.Protocol.html#getOpkgPackage">getOpkgPackage</a></li>
540             
541                 <li data-name="LuCI.Network.Protocol#getProtocol"><a href="LuCI.Network.Protocol.html#getProtocol">getProtocol</a></li>
542             
543                 <li data-name="LuCI.Network.Protocol#getType"><a href="LuCI.Network.Protocol.html#getType">getType</a></li>
544             
545                 <li data-name="LuCI.Network.Protocol#getUptime"><a href="LuCI.Network.Protocol.html#getUptime">getUptime</a></li>
546             
547                 <li data-name="LuCI.Network.Protocol#getZoneName"><a href="LuCI.Network.Protocol.html#getZoneName">getZoneName</a></li>
548             
549                 <li data-name="LuCI.Network.Protocol#isAlias"><a href="LuCI.Network.Protocol.html#isAlias">isAlias</a></li>
550             
551                 <li data-name="LuCI.Network.Protocol#isBridge"><a href="LuCI.Network.Protocol.html#isBridge">isBridge</a></li>
552             
553                 <li data-name="LuCI.Network.Protocol#isDynamic"><a href="LuCI.Network.Protocol.html#isDynamic">isDynamic</a></li>
554             
555                 <li data-name="LuCI.Network.Protocol#isEmpty"><a href="LuCI.Network.Protocol.html#isEmpty">isEmpty</a></li>
556             
557                 <li data-name="LuCI.Network.Protocol#isFloating"><a href="LuCI.Network.Protocol.html#isFloating">isFloating</a></li>
558             
559                 <li data-name="LuCI.Network.Protocol#isInstalled"><a href="LuCI.Network.Protocol.html#isInstalled">isInstalled</a></li>
560             
561                 <li data-name="LuCI.Network.Protocol#isUp"><a href="LuCI.Network.Protocol.html#isUp">isUp</a></li>
562             
563                 <li data-name="LuCI.Network.Protocol#isVirtual"><a href="LuCI.Network.Protocol.html#isVirtual">isVirtual</a></li>
564             
565                 <li data-name="LuCI.Network.Protocol#set"><a href="LuCI.Network.Protocol.html#set">set</a></li>
566             
567             </ul>
568             <ul class="events itemMembers">
569             
570             </ul>
571         </li>
572     
573         <li class="item" data-name="LuCI.Network.WifiDevice">
574             <span class="title">
575                 <a href="LuCI.Network.WifiDevice.html">LuCI.Network.WifiDevice</a>
576                 
577             </span>
578             <ul class="members itemMembers">
579             
580             </ul>
581             <ul class="typedefs itemMembers">
582             
583             </ul>
584             <ul class="typedefs itemMembers">
585             
586             </ul>
587             <ul class="methods itemMembers">
588             
589             <span class="subtitle">Methods</span>
590             
591                 <li data-name="LuCI.Network.WifiDevice#addWifiNetwork"><a href="LuCI.Network.WifiDevice.html#addWifiNetwork">addWifiNetwork</a></li>
592             
593                 <li data-name="LuCI.Network.WifiDevice#deleteWifiNetwork"><a href="LuCI.Network.WifiDevice.html#deleteWifiNetwork">deleteWifiNetwork</a></li>
594             
595                 <li data-name="LuCI.Network.WifiDevice#get"><a href="LuCI.Network.WifiDevice.html#get">get</a></li>
596             
597                 <li data-name="LuCI.Network.WifiDevice#getHTModes"><a href="LuCI.Network.WifiDevice.html#getHTModes">getHTModes</a></li>
598             
599                 <li data-name="LuCI.Network.WifiDevice#getHWModes"><a href="LuCI.Network.WifiDevice.html#getHWModes">getHWModes</a></li>
600             
601                 <li data-name="LuCI.Network.WifiDevice#getI18n"><a href="LuCI.Network.WifiDevice.html#getI18n">getI18n</a></li>
602             
603                 <li data-name="LuCI.Network.WifiDevice#getName"><a href="LuCI.Network.WifiDevice.html#getName">getName</a></li>
604             
605                 <li data-name="LuCI.Network.WifiDevice#getScanList"><a href="LuCI.Network.WifiDevice.html#getScanList">getScanList</a></li>
606             
607                 <li data-name="LuCI.Network.WifiDevice#getWifiNetwork"><a href="LuCI.Network.WifiDevice.html#getWifiNetwork">getWifiNetwork</a></li>
608             
609                 <li data-name="LuCI.Network.WifiDevice#getWifiNetworks"><a href="LuCI.Network.WifiDevice.html#getWifiNetworks">getWifiNetworks</a></li>
610             
611                 <li data-name="LuCI.Network.WifiDevice#isDisabled"><a href="LuCI.Network.WifiDevice.html#isDisabled">isDisabled</a></li>
612             
613                 <li data-name="LuCI.Network.WifiDevice#isUp"><a href="LuCI.Network.WifiDevice.html#isUp">isUp</a></li>
614             
615                 <li data-name="LuCI.Network.WifiDevice#set"><a href="LuCI.Network.WifiDevice.html#set">set</a></li>
616             
617             </ul>
618             <ul class="events itemMembers">
619             
620             </ul>
621         </li>
622     
623         <li class="item" data-name="LuCI.Network.WifiNetwork">
624             <span class="title">
625                 <a href="LuCI.Network.WifiNetwork.html">LuCI.Network.WifiNetwork</a>
626                 
627             </span>
628             <ul class="members itemMembers">
629             
630             </ul>
631             <ul class="typedefs itemMembers">
632             
633             </ul>
634             <ul class="typedefs itemMembers">
635             
636             </ul>
637             <ul class="methods itemMembers">
638             
639             <span class="subtitle">Methods</span>
640             
641                 <li data-name="LuCI.Network.WifiNetwork#disconnectClient"><a href="LuCI.Network.WifiNetwork.html#disconnectClient">disconnectClient</a></li>
642             
643                 <li data-name="LuCI.Network.WifiNetwork#get"><a href="LuCI.Network.WifiNetwork.html#get">get</a></li>
644             
645                 <li data-name="LuCI.Network.WifiNetwork#getActiveBSSID"><a href="LuCI.Network.WifiNetwork.html#getActiveBSSID">getActiveBSSID</a></li>
646             
647                 <li data-name="LuCI.Network.WifiNetwork#getActiveEncryption"><a href="LuCI.Network.WifiNetwork.html#getActiveEncryption">getActiveEncryption</a></li>
648             
649                 <li data-name="LuCI.Network.WifiNetwork#getActiveMode"><a href="LuCI.Network.WifiNetwork.html#getActiveMode">getActiveMode</a></li>
650             
651                 <li data-name="LuCI.Network.WifiNetwork#getActiveModeI18n"><a href="LuCI.Network.WifiNetwork.html#getActiveModeI18n">getActiveModeI18n</a></li>
652             
653                 <li data-name="LuCI.Network.WifiNetwork#getActiveSSID"><a href="LuCI.Network.WifiNetwork.html#getActiveSSID">getActiveSSID</a></li>
654             
655                 <li data-name="LuCI.Network.WifiNetwork#getAssocList"><a href="LuCI.Network.WifiNetwork.html#getAssocList">getAssocList</a></li>
656             
657                 <li data-name="LuCI.Network.WifiNetwork#getBitRate"><a href="LuCI.Network.WifiNetwork.html#getBitRate">getBitRate</a></li>
658             
659                 <li data-name="LuCI.Network.WifiNetwork#getBSSID"><a href="LuCI.Network.WifiNetwork.html#getBSSID">getBSSID</a></li>
660             
661                 <li data-name="LuCI.Network.WifiNetwork#getChannel"><a href="LuCI.Network.WifiNetwork.html#getChannel">getChannel</a></li>
662             
663                 <li data-name="LuCI.Network.WifiNetwork#getCountryCode"><a href="LuCI.Network.WifiNetwork.html#getCountryCode">getCountryCode</a></li>
664             
665                 <li data-name="LuCI.Network.WifiNetwork#getDevice"><a href="LuCI.Network.WifiNetwork.html#getDevice">getDevice</a></li>
666             
667                 <li data-name="LuCI.Network.WifiNetwork#getFrequency"><a href="LuCI.Network.WifiNetwork.html#getFrequency">getFrequency</a></li>
668             
669                 <li data-name="LuCI.Network.WifiNetwork#getI18n"><a href="LuCI.Network.WifiNetwork.html#getI18n">getI18n</a></li>
670             
671                 <li data-name="LuCI.Network.WifiNetwork#getID"><a href="LuCI.Network.WifiNetwork.html#getID">getID</a></li>
672             
673                 <li data-name="LuCI.Network.WifiNetwork#getIfname"><a href="LuCI.Network.WifiNetwork.html#getIfname">getIfname</a></li>
674             
675                 <li data-name="LuCI.Network.WifiNetwork#getMeshID"><a href="LuCI.Network.WifiNetwork.html#getMeshID">getMeshID</a></li>
676             
677                 <li data-name="LuCI.Network.WifiNetwork#getMode"><a href="LuCI.Network.WifiNetwork.html#getMode">getMode</a></li>
678             
679                 <li data-name="LuCI.Network.WifiNetwork#getName"><a href="LuCI.Network.WifiNetwork.html#getName">getName</a></li>
680             
681                 <li data-name="LuCI.Network.WifiNetwork#getNetwork"><a href="LuCI.Network.WifiNetwork.html#getNetwork">getNetwork</a></li>
682             
683                 <li data-name="LuCI.Network.WifiNetwork#getNetworkNames"><a href="LuCI.Network.WifiNetwork.html#getNetworkNames">getNetworkNames</a></li>
684             
685                 <li data-name="LuCI.Network.WifiNetwork#getNetworks"><a href="LuCI.Network.WifiNetwork.html#getNetworks">getNetworks</a></li>
686             
687                 <li data-name="LuCI.Network.WifiNetwork#getNoise"><a href="LuCI.Network.WifiNetwork.html#getNoise">getNoise</a></li>
688             
689                 <li data-name="LuCI.Network.WifiNetwork#getShortName"><a href="LuCI.Network.WifiNetwork.html#getShortName">getShortName</a></li>
690             
691                 <li data-name="LuCI.Network.WifiNetwork#getSignal"><a href="LuCI.Network.WifiNetwork.html#getSignal">getSignal</a></li>
692             
693                 <li data-name="LuCI.Network.WifiNetwork#getSignalLevel"><a href="LuCI.Network.WifiNetwork.html#getSignalLevel">getSignalLevel</a></li>
694             
695                 <li data-name="LuCI.Network.WifiNetwork#getSignalPercent"><a href="LuCI.Network.WifiNetwork.html#getSignalPercent">getSignalPercent</a></li>
696             
697                 <li data-name="LuCI.Network.WifiNetwork#getSSID"><a href="LuCI.Network.WifiNetwork.html#getSSID">getSSID</a></li>
698             
699                 <li data-name="LuCI.Network.WifiNetwork#getTXPower"><a href="LuCI.Network.WifiNetwork.html#getTXPower">getTXPower</a></li>
700             
701                 <li data-name="LuCI.Network.WifiNetwork#getTXPowerOffset"><a href="LuCI.Network.WifiNetwork.html#getTXPowerOffset">getTXPowerOffset</a></li>
702             
703                 <li data-name="LuCI.Network.WifiNetwork#getWifiDevice"><a href="LuCI.Network.WifiNetwork.html#getWifiDevice">getWifiDevice</a></li>
704             
705                 <li data-name="LuCI.Network.WifiNetwork#getWifiDeviceName"><a href="LuCI.Network.WifiNetwork.html#getWifiDeviceName">getWifiDeviceName</a></li>
706             
707                 <li data-name="LuCI.Network.WifiNetwork#isClientDisconnectSupported"><a href="LuCI.Network.WifiNetwork.html#isClientDisconnectSupported">isClientDisconnectSupported</a></li>
708             
709                 <li data-name="LuCI.Network.WifiNetwork#isDisabled"><a href="LuCI.Network.WifiNetwork.html#isDisabled">isDisabled</a></li>
710             
711                 <li data-name="LuCI.Network.WifiNetwork#isUp"><a href="LuCI.Network.WifiNetwork.html#isUp">isUp</a></li>
712             
713                 <li data-name="LuCI.Network.WifiNetwork#set"><a href="LuCI.Network.WifiNetwork.html#set">set</a></li>
714             
715             </ul>
716             <ul class="events itemMembers">
717             
718             </ul>
719         </li>
720     
721         <li class="item" data-name="LuCI.Poll">
722             <span class="title">
723                 <a href="LuCI.Poll.html">LuCI.Poll</a>
724                 
725             </span>
726             <ul class="members itemMembers">
727             
728             </ul>
729             <ul class="typedefs itemMembers">
730             
731             </ul>
732             <ul class="typedefs itemMembers">
733             
734             </ul>
735             <ul class="methods itemMembers">
736             
737             <span class="subtitle">Methods</span>
738             
739                 <li data-name="LuCI.Poll#active"><a href="LuCI.Poll.html#active">active</a></li>
740             
741                 <li data-name="LuCI.Poll#add"><a href="LuCI.Poll.html#add">add</a></li>
742             
743                 <li data-name="LuCI.Poll#remove"><a href="LuCI.Poll.html#remove">remove</a></li>
744             
745                 <li data-name="LuCI.Poll#start"><a href="LuCI.Poll.html#start">start</a></li>
746             
747                 <li data-name="LuCI.Poll#stop"><a href="LuCI.Poll.html#stop">stop</a></li>
748             
749             </ul>
750             <ul class="events itemMembers">
751             
752             </ul>
753         </li>
754     
755         <li class="item" data-name="LuCI.Request">
756             <span class="title">
757                 <a href="LuCI.Request.html">LuCI.Request</a>
758                 
759             </span>
760             <ul class="members itemMembers">
761             
762             </ul>
763             <ul class="typedefs itemMembers">
764             
765             <span class="subtitle">Typedefs</span>
766             
767                 <li data-name="LuCI.Request.interceptorFn"><a href="LuCI.Request.html#.interceptorFn">interceptorFn</a></li>
768             
769                 <li data-name="LuCI.Request.RequestOptions"><a href="LuCI.Request.html#.RequestOptions">RequestOptions</a></li>
770             
771             </ul>
772             <ul class="typedefs itemMembers">
773             
774             </ul>
775             <ul class="methods itemMembers">
776             
777             <span class="subtitle">Methods</span>
778             
779                 <li data-name="LuCI.Request#addInterceptor"><a href="LuCI.Request.html#addInterceptor">addInterceptor</a></li>
780             
781                 <li data-name="LuCI.Request#expandURL"><a href="LuCI.Request.html#expandURL">expandURL</a></li>
782             
783                 <li data-name="LuCI.Request#get"><a href="LuCI.Request.html#get">get</a></li>
784             
785                 <li data-name="LuCI.Request#post"><a href="LuCI.Request.html#post">post</a></li>
786             
787                 <li data-name="LuCI.Request#removeInterceptor"><a href="LuCI.Request.html#removeInterceptor">removeInterceptor</a></li>
788             
789                 <li data-name="LuCI.Request#request"><a href="LuCI.Request.html#request">request</a></li>
790             
791             </ul>
792             <ul class="events itemMembers">
793             
794             </ul>
795         </li>
796     
797         <li class="item" data-name="LuCI.Request.poll">
798             <span class="title">
799                 <a href="LuCI.Request.poll.html">LuCI.Request.poll</a>
800                 
801             </span>
802             <ul class="members itemMembers">
803             
804             </ul>
805             <ul class="typedefs itemMembers">
806             
807             <span class="subtitle">Typedefs</span>
808             
809                 <li data-name="LuCI.Request.poll~callbackFn"><a href="LuCI.Request.poll.html#~callbackFn">callbackFn</a></li>
810             
811             </ul>
812             <ul class="typedefs itemMembers">
813             
814             </ul>
815             <ul class="methods itemMembers">
816             
817             <span class="subtitle">Methods</span>
818             
819                 <li data-name="LuCI.Request.poll#active"><a href="LuCI.Request.poll.html#active">active</a></li>
820             
821                 <li data-name="LuCI.Request.poll#add"><a href="LuCI.Request.poll.html#add">add</a></li>
822             
823                 <li data-name="LuCI.Request.poll#remove"><a href="LuCI.Request.poll.html#remove">remove</a></li>
824             
825                 <li data-name="LuCI.Request.poll#start"><a href="LuCI.Request.poll.html#start">start</a></li>
826             
827                 <li data-name="LuCI.Request.poll#stop"><a href="LuCI.Request.poll.html#stop">stop</a></li>
828             
829             </ul>
830             <ul class="events itemMembers">
831             
832             </ul>
833         </li>
834     
835         <li class="item" data-name="LuCI.Response">
836             <span class="title">
837                 <a href="LuCI.Response.html">LuCI.Response</a>
838                 
839             </span>
840             <ul class="members itemMembers">
841             
842             <span class="subtitle">Members</span>
843             
844                 <li data-name="LuCI.Response#duration"><a href="LuCI.Response.html#duration">duration</a></li>
845             
846                 <li data-name="LuCI.Response#headers"><a href="LuCI.Response.html#headers">headers</a></li>
847             
848                 <li data-name="LuCI.Response#ok"><a href="LuCI.Response.html#ok">ok</a></li>
849             
850                 <li data-name="LuCI.Response#status"><a href="LuCI.Response.html#status">status</a></li>
851             
852                 <li data-name="LuCI.Response#statusText"><a href="LuCI.Response.html#statusText">statusText</a></li>
853             
854                 <li data-name="LuCI.Response#url"><a href="LuCI.Response.html#url">url</a></li>
855             
856             </ul>
857             <ul class="typedefs itemMembers">
858             
859             </ul>
860             <ul class="typedefs itemMembers">
861             
862             </ul>
863             <ul class="methods itemMembers">
864             
865             <span class="subtitle">Methods</span>
866             
867                 <li data-name="LuCI.Response#clone"><a href="LuCI.Response.html#clone">clone</a></li>
868             
869                 <li data-name="LuCI.Response#json"><a href="LuCI.Response.html#json">json</a></li>
870             
871                 <li data-name="LuCI.Response#text"><a href="LuCI.Response.html#text">text</a></li>
872             
873             </ul>
874             <ul class="events itemMembers">
875             
876             </ul>
877         </li>
878     
879         <li class="item" data-name="LuCI.rpc">
880             <span class="title">
881                 <a href="LuCI.rpc.html">LuCI.rpc</a>
882                 
883             </span>
884             <ul class="members itemMembers">
885             
886             </ul>
887             <ul class="typedefs itemMembers">
888             
889             <span class="subtitle">Typedefs</span>
890             
891                 <li data-name="LuCI.rpc.DeclareOptions"><a href="LuCI.rpc.html#.DeclareOptions">DeclareOptions</a></li>
892             
893                 <li data-name="LuCI.rpc~filterFn"><a href="LuCI.rpc.html#~filterFn">filterFn</a></li>
894             
895                 <li data-name="LuCI.rpc~interceptorFn"><a href="LuCI.rpc.html#~interceptorFn">interceptorFn</a></li>
896             
897                 <li data-name="LuCI.rpc~invokeFn"><a href="LuCI.rpc.html#~invokeFn">invokeFn</a></li>
898             
899             </ul>
900             <ul class="typedefs itemMembers">
901             
902             </ul>
903             <ul class="methods itemMembers">
904             
905             <span class="subtitle">Methods</span>
906             
907                 <li data-name="LuCI.rpc#addInterceptor"><a href="LuCI.rpc.html#addInterceptor">addInterceptor</a></li>
908             
909                 <li data-name="LuCI.rpc#declare"><a href="LuCI.rpc.html#declare">declare</a></li>
910             
911                 <li data-name="LuCI.rpc#getBaseURL"><a href="LuCI.rpc.html#getBaseURL">getBaseURL</a></li>
912             
913                 <li data-name="LuCI.rpc#getSessionID"><a href="LuCI.rpc.html#getSessionID">getSessionID</a></li>
914             
915                 <li data-name="LuCI.rpc#getStatusText"><a href="LuCI.rpc.html#getStatusText">getStatusText</a></li>
916             
917                 <li data-name="LuCI.rpc#list"><a href="LuCI.rpc.html#list">list</a></li>
918             
919                 <li data-name="LuCI.rpc#removeInterceptor"><a href="LuCI.rpc.html#removeInterceptor">removeInterceptor</a></li>
920             
921                 <li data-name="LuCI.rpc#setBaseURL"><a href="LuCI.rpc.html#setBaseURL">setBaseURL</a></li>
922             
923                 <li data-name="LuCI.rpc#setSessionID"><a href="LuCI.rpc.html#setSessionID">setSessionID</a></li>
924             
925             </ul>
926             <ul class="events itemMembers">
927             
928             </ul>
929         </li>
930     
931         <li class="item" data-name="LuCI.uci">
932             <span class="title">
933                 <a href="LuCI.uci.html">LuCI.uci</a>
934                 
935             </span>
936             <ul class="members itemMembers">
937             
938             </ul>
939             <ul class="typedefs itemMembers">
940             
941             <span class="subtitle">Typedefs</span>
942             
943                 <li data-name="LuCI.uci.ChangeRecord"><a href="LuCI.uci.html#.ChangeRecord">ChangeRecord</a></li>
944             
945                 <li data-name="LuCI.uci.SectionObject"><a href="LuCI.uci.html#.SectionObject">SectionObject</a></li>
946             
947                 <li data-name="LuCI.uci~sectionsFn"><a href="LuCI.uci.html#~sectionsFn">sectionsFn</a></li>
948             
949             </ul>
950             <ul class="typedefs itemMembers">
951             
952             </ul>
953             <ul class="methods itemMembers">
954             
955             <span class="subtitle">Methods</span>
956             
957                 <li data-name="LuCI.uci#add"><a href="LuCI.uci.html#add">add</a></li>
958             
959                 <li data-name="LuCI.uci#apply"><a href="LuCI.uci.html#apply">apply</a></li>
960             
961                 <li data-name="LuCI.uci#changes"><a href="LuCI.uci.html#changes">changes</a></li>
962             
963                 <li data-name="LuCI.uci#createSID"><a href="LuCI.uci.html#createSID">createSID</a></li>
964             
965                 <li data-name="LuCI.uci#get"><a href="LuCI.uci.html#get">get</a></li>
966             
967                 <li data-name="LuCI.uci#get_first"><a href="LuCI.uci.html#get_first">get_first</a></li>
968             
969                 <li data-name="LuCI.uci#load"><a href="LuCI.uci.html#load">load</a></li>
970             
971                 <li data-name="LuCI.uci#move"><a href="LuCI.uci.html#move">move</a></li>
972             
973                 <li data-name="LuCI.uci#remove"><a href="LuCI.uci.html#remove">remove</a></li>
974             
975                 <li data-name="LuCI.uci#resolveSID"><a href="LuCI.uci.html#resolveSID">resolveSID</a></li>
976             
977                 <li data-name="LuCI.uci#save"><a href="LuCI.uci.html#save">save</a></li>
978             
979                 <li data-name="LuCI.uci#sections"><a href="LuCI.uci.html#sections">sections</a></li>
980             
981                 <li data-name="LuCI.uci#set"><a href="LuCI.uci.html#set">set</a></li>
982             
983                 <li data-name="LuCI.uci#set_first"><a href="LuCI.uci.html#set_first">set_first</a></li>
984             
985                 <li data-name="LuCI.uci#unload"><a href="LuCI.uci.html#unload">unload</a></li>
986             
987                 <li data-name="LuCI.uci#unset"><a href="LuCI.uci.html#unset">unset</a></li>
988             
989                 <li data-name="LuCI.uci#unset_first"><a href="LuCI.uci.html#unset_first">unset_first</a></li>
990             
991             </ul>
992             <ul class="events itemMembers">
993             
994             </ul>
995         </li>
996     
997         <li class="item" data-name="LuCI.view">
998             <span class="title">
999                 <a href="LuCI.view.html">LuCI.view</a>
1000                 
1001             </span>
1002             <ul class="members itemMembers">
1003             
1004             </ul>
1005             <ul class="typedefs itemMembers">
1006             
1007             </ul>
1008             <ul class="typedefs itemMembers">
1009             
1010             </ul>
1011             <ul class="methods itemMembers">
1012             
1013             <span class="subtitle">Methods</span>
1014             
1015                 <li data-name="LuCI.view#addFooter"><a href="LuCI.view.html#addFooter">addFooter</a></li>
1016             
1017                 <li data-name="LuCI.view#handleReset"><a href="LuCI.view.html#handleReset">handleReset</a></li>
1018             
1019                 <li data-name="LuCI.view#handleSave"><a href="LuCI.view.html#handleSave">handleSave</a></li>
1020             
1021                 <li data-name="LuCI.view#handleSaveApply"><a href="LuCI.view.html#handleSaveApply">handleSaveApply</a></li>
1022             
1023                 <li data-name="LuCI.view#load"><a href="LuCI.view.html#load">load</a></li>
1024             
1025                 <li data-name="LuCI.view#render"><a href="LuCI.view.html#render">render</a></li>
1026             
1027             </ul>
1028             <ul class="events itemMembers">
1029             
1030             </ul>
1031         </li>
1032     
1033         <li class="item" data-name="LuCI.XHR">
1034             <span class="title">
1035                 <a href="LuCI.XHR.html">LuCI.XHR</a>
1036                 
1037             </span>
1038             <ul class="members itemMembers">
1039             
1040             </ul>
1041             <ul class="typedefs itemMembers">
1042             
1043             </ul>
1044             <ul class="typedefs itemMembers">
1045             
1046             </ul>
1047             <ul class="methods itemMembers">
1048             
1049             <span class="subtitle">Methods</span>
1050             
1051                 <li data-name="LuCI.XHR#abort"><a href="LuCI.XHR.html#abort">abort</a></li>
1052             
1053                 <li data-name="LuCI.XHR#busy"><a href="LuCI.XHR.html#busy">busy</a></li>
1054             
1055                 <li data-name="LuCI.XHR#cancel"><a href="LuCI.XHR.html#cancel">cancel</a></li>
1056             
1057                 <li data-name="LuCI.XHR#get"><a href="LuCI.XHR.html#get">get</a></li>
1058             
1059                 <li data-name="LuCI.XHR#post"><a href="LuCI.XHR.html#post">post</a></li>
1060             
1061                 <li data-name="LuCI.XHR#send_form"><a href="LuCI.XHR.html#send_form">send_form</a></li>
1062             
1063             </ul>
1064             <ul class="events itemMembers">
1065             
1066             </ul>
1067         </li>
1068     
1069     </ul>
1070 </div>
1071     <div class="main">
1072         <h1 class="page-title" data-filename="network.js.html">Source: network.js</h1>
1073         
1074
1075
1076     
1077     <section>
1078         <article>
1079             <pre id="source-code" class="prettyprint source "><code>'use strict';
1080 'require uci';
1081 'require rpc';
1082 'require validation';
1083
1084 var proto_errors = {
1085         CONNECT_FAILED:                 _('Connection attempt failed'),
1086         INVALID_ADDRESS:                _('IP address in invalid'),
1087         INVALID_GATEWAY:                _('Gateway address is invalid'),
1088         INVALID_LOCAL_ADDRESS:  _('Local IP address is invalid'),
1089         MISSING_ADDRESS:                _('IP address is missing'),
1090         MISSING_PEER_ADDRESS:   _('Peer address is missing'),
1091         NO_DEVICE:                              _('Network device is not present'),
1092         NO_IFACE:                               _('Unable to determine device name'),
1093         NO_IFNAME:                              _('Unable to determine device name'),
1094         NO_WAN_ADDRESS:                 _('Unable to determine external IP address'),
1095         NO_WAN_LINK:                    _('Unable to determine upstream interface'),
1096         PEER_RESOLVE_FAIL:              _('Unable to resolve peer host name'),
1097         PIN_FAILED:                             _('PIN code rejected')
1098 };
1099
1100 var iface_patterns_ignore = [
1101         /^wmaster\d+/,
1102         /^wifi\d+/,
1103         /^hwsim\d+/,
1104         /^imq\d+/,
1105         /^ifb\d+/,
1106         /^mon\.wlan\d+/,
1107         /^sit\d+/,
1108         /^gre\d+/,
1109         /^gretap\d+/,
1110         /^ip6gre\d+/,
1111         /^ip6tnl\d+/,
1112         /^tunl\d+/,
1113         /^lo$/
1114 ];
1115
1116 var iface_patterns_wireless = [
1117         /^wlan\d+/,
1118         /^wl\d+/,
1119         /^ath\d+/,
1120         /^\w+\.network\d+/
1121 ];
1122
1123 var iface_patterns_virtual = [ ];
1124
1125 var callLuciNetworkDevices = rpc.declare({
1126         object: 'luci-rpc',
1127         method: 'getNetworkDevices',
1128         expect: { '': {} }
1129 });
1130
1131 var callLuciWirelessDevices = rpc.declare({
1132         object: 'luci-rpc',
1133         method: 'getWirelessDevices',
1134         expect: { '': {} }
1135 });
1136
1137 var callLuciBoardJSON = rpc.declare({
1138         object: 'luci-rpc',
1139         method: 'getBoardJSON'
1140 });
1141
1142 var callLuciHostHints = rpc.declare({
1143         object: 'luci-rpc',
1144         method: 'getHostHints',
1145         expect: { '': {} }
1146 });
1147
1148 var callIwinfoAssoclist = rpc.declare({
1149         object: 'iwinfo',
1150         method: 'assoclist',
1151         params: [ 'device', 'mac' ],
1152         expect: { results: [] }
1153 });
1154
1155 var callIwinfoScan = rpc.declare({
1156         object: 'iwinfo',
1157         method: 'scan',
1158         params: [ 'device' ],
1159         nobatch: true,
1160         expect: { results: [] }
1161 });
1162
1163 var callNetworkInterfaceDump = rpc.declare({
1164         object: 'network.interface',
1165         method: 'dump',
1166         expect: { 'interface': [] }
1167 });
1168
1169 var callNetworkProtoHandlers = rpc.declare({
1170         object: 'network',
1171         method: 'get_proto_handlers',
1172         expect: { '': {} }
1173 });
1174
1175 var _init = null,
1176     _state = null,
1177     _protocols = {},
1178     _protospecs = {};
1179
1180 function getProtocolHandlers(cache) {
1181         return callNetworkProtoHandlers().then(function(protos) {
1182                 /* Register "none" protocol */
1183                 if (!protos.hasOwnProperty('none'))
1184                         Object.assign(protos, { none: { no_device: false } });
1185
1186                 /* Hack: emulate relayd protocol */
1187                 if (!protos.hasOwnProperty('relay'))
1188                         Object.assign(protos, { relay: { no_device: true } });
1189
1190                 Object.assign(_protospecs, protos);
1191
1192                 return Promise.all(Object.keys(protos).map(function(p) {
1193                         return Promise.resolve(L.require('protocol.%s'.format(p))).catch(function(err) {
1194                                 if (L.isObject(err) &amp;&amp; err.name != 'NetworkError')
1195                                         L.error(err);
1196                         });
1197                 })).then(function() {
1198                         return protos;
1199                 });
1200         }).catch(function() {
1201                 return {};
1202         });
1203 }
1204
1205 function getWifiStateBySid(sid) {
1206         var s = uci.get('wireless', sid);
1207
1208         if (s != null &amp;&amp; s['.type'] == 'wifi-iface') {
1209                 for (var radioname in _state.radios) {
1210                         for (var i = 0; i &lt; _state.radios[radioname].interfaces.length; i++) {
1211                                 var netstate = _state.radios[radioname].interfaces[i];
1212
1213                                 if (typeof(netstate.section) != 'string')
1214                                         continue;
1215
1216                                 var s2 = uci.get('wireless', netstate.section);
1217
1218                                 if (s2 != null &amp;&amp; s['.type'] == s2['.type'] &amp;&amp; s['.name'] == s2['.name']) {
1219                                         if (s2['.anonymous'] == false &amp;&amp; netstate.section.charAt(0) == '@')
1220                                                 return null;
1221
1222                                         return [ radioname, _state.radios[radioname], netstate ];
1223                                 }
1224                         }
1225                 }
1226         }
1227
1228         return null;
1229 }
1230
1231 function getWifiStateByIfname(ifname) {
1232         for (var radioname in _state.radios) {
1233                 for (var i = 0; i &lt; _state.radios[radioname].interfaces.length; i++) {
1234                         var netstate = _state.radios[radioname].interfaces[i];
1235
1236                         if (typeof(netstate.ifname) != 'string')
1237                                 continue;
1238
1239                         if (netstate.ifname == ifname)
1240                                 return [ radioname, _state.radios[radioname], netstate ];
1241                 }
1242         }
1243
1244         return null;
1245 }
1246
1247 function isWifiIfname(ifname) {
1248         for (var i = 0; i &lt; iface_patterns_wireless.length; i++)
1249                 if (iface_patterns_wireless[i].test(ifname))
1250                         return true;
1251
1252         return false;
1253 }
1254
1255 function getWifiSidByNetid(netid) {
1256         var m = /^(\w+)\.network(\d+)$/.exec(netid);
1257         if (m) {
1258                 var sections = uci.sections('wireless', 'wifi-iface');
1259                 for (var i = 0, n = 0; i &lt; sections.length; i++) {
1260                         if (sections[i].device != m[1])
1261                                 continue;
1262
1263                         if (++n == +m[2])
1264                                 return sections[i]['.name'];
1265                 }
1266         }
1267
1268         return null;
1269 }
1270
1271 function getWifiSidByIfname(ifname) {
1272         var sid = getWifiSidByNetid(ifname);
1273
1274         if (sid != null)
1275                 return sid;
1276
1277         var res = getWifiStateByIfname(ifname);
1278
1279         if (res != null &amp;&amp; L.isObject(res[2]) &amp;&amp; typeof(res[2].section) == 'string')
1280                 return res[2].section;
1281
1282         return null;
1283 }
1284
1285 function getWifiNetidBySid(sid) {
1286         var s = uci.get('wireless', sid);
1287         if (s != null &amp;&amp; s['.type'] == 'wifi-iface') {
1288                 var radioname = s.device;
1289                 if (typeof(s.device) == 'string') {
1290                         var i = 0, netid = null, sections = uci.sections('wireless', 'wifi-iface');
1291                         for (var i = 0, n = 0; i &lt; sections.length; i++) {
1292                                 if (sections[i].device != s.device)
1293                                         continue;
1294
1295                                 n++;
1296
1297                                 if (sections[i]['.name'] != s['.name'])
1298                                         continue;
1299
1300                                 return [ '%s.network%d'.format(s.device, n), s.device ];
1301                         }
1302
1303                 }
1304         }
1305
1306         return null;
1307 }
1308
1309 function getWifiNetidByNetname(name) {
1310         var sections = uci.sections('wireless', 'wifi-iface');
1311         for (var i = 0; i &lt; sections.length; i++) {
1312                 if (typeof(sections[i].network) != 'string')
1313                         continue;
1314
1315                 var nets = sections[i].network.split(/\s+/);
1316                 for (var j = 0; j &lt; nets.length; j++) {
1317                         if (nets[j] != name)
1318                                 continue;
1319
1320                         return getWifiNetidBySid(sections[i]['.name']);
1321                 }
1322         }
1323
1324         return null;
1325 }
1326
1327 function isVirtualIfname(ifname) {
1328         for (var i = 0; i &lt; iface_patterns_virtual.length; i++)
1329                 if (iface_patterns_virtual[i].test(ifname))
1330                         return true;
1331
1332         return false;
1333 }
1334
1335 function isIgnoredIfname(ifname) {
1336         for (var i = 0; i &lt; iface_patterns_ignore.length; i++)
1337                 if (iface_patterns_ignore[i].test(ifname))
1338                         return true;
1339
1340         return false;
1341 }
1342
1343 function appendValue(config, section, option, value) {
1344         var values = uci.get(config, section, option),
1345             isArray = Array.isArray(values),
1346             rv = false;
1347
1348         if (isArray == false)
1349                 values = L.toArray(values);
1350
1351         if (values.indexOf(value) == -1) {
1352                 values.push(value);
1353                 rv = true;
1354         }
1355
1356         uci.set(config, section, option, isArray ? values : values.join(' '));
1357
1358         return rv;
1359 }
1360
1361 function removeValue(config, section, option, value) {
1362         var values = uci.get(config, section, option),
1363             isArray = Array.isArray(values),
1364             rv = false;
1365
1366         if (isArray == false)
1367                 values = L.toArray(values);
1368
1369         for (var i = values.length - 1; i >= 0; i--) {
1370                 if (values[i] == value) {
1371                         values.splice(i, 1);
1372                         rv = true;
1373                 }
1374         }
1375
1376         if (values.length > 0)
1377                 uci.set(config, section, option, isArray ? values : values.join(' '));
1378         else
1379                 uci.unset(config, section, option);
1380
1381         return rv;
1382 }
1383
1384 function prefixToMask(bits, v6) {
1385         var w = v6 ? 128 : 32,
1386             m = [];
1387
1388         if (bits > w)
1389                 return null;
1390
1391         for (var i = 0; i &lt; w / 16; i++) {
1392                 var b = Math.min(16, bits);
1393                 m.push((0xffff &lt;&lt; (16 - b)) &amp; 0xffff);
1394                 bits -= b;
1395         }
1396
1397         if (v6)
1398                 return String.prototype.format.apply('%x:%x:%x:%x:%x:%x:%x:%x', m).replace(/:0(?::0)+$/, '::');
1399         else
1400                 return '%d.%d.%d.%d'.format(m[0] >>> 8, m[0] &amp; 0xff, m[1] >>> 8, m[1] &amp; 0xff);
1401 }
1402
1403 function maskToPrefix(mask, v6) {
1404         var m = v6 ? validation.parseIPv6(mask) : validation.parseIPv4(mask);
1405
1406         if (!m)
1407                 return null;
1408
1409         var bits = 0;
1410
1411         for (var i = 0, z = false; i &lt; m.length; i++) {
1412                 z = z || !m[i];
1413
1414                 while (!z &amp;&amp; (m[i] &amp; (v6 ? 0x8000 : 0x80))) {
1415                         m[i] = (m[i] &lt;&lt; 1) &amp; (v6 ? 0xffff : 0xff);
1416                         bits++;
1417                 }
1418
1419                 if (m[i])
1420                         return null;
1421         }
1422
1423         return bits;
1424 }
1425
1426 function initNetworkState(refresh) {
1427         if (_state == null || refresh) {
1428                 _init = _init || Promise.all([
1429                         L.resolveDefault(callNetworkInterfaceDump(), []),
1430                         L.resolveDefault(callLuciBoardJSON(), {}),
1431                         L.resolveDefault(callLuciNetworkDevices(), {}),
1432                         L.resolveDefault(callLuciWirelessDevices(), {}),
1433                         L.resolveDefault(callLuciHostHints(), {}),
1434                         getProtocolHandlers(),
1435                         uci.load(['network', 'wireless', 'luci'])
1436                 ]).then(function(data) {
1437                         var netifd_ifaces = data[0],
1438                             board_json    = data[1],
1439                             luci_devs     = data[2];
1440
1441                         var s = {
1442                                 isTunnel: {}, isBridge: {}, isSwitch: {}, isWifi: {},
1443                                 ifaces: netifd_ifaces, radios: data[3], hosts: data[4],
1444                                 netdevs: {}, bridges: {}, switches: {}, hostapd: {}
1445                         };
1446
1447                         for (var name in luci_devs) {
1448                                 var dev = luci_devs[name];
1449
1450                                 if (isVirtualIfname(name))
1451                                         s.isTunnel[name] = true;
1452
1453                                 if (!s.isTunnel[name] &amp;&amp; isIgnoredIfname(name))
1454                                         continue;
1455
1456                                 s.netdevs[name] = s.netdevs[name] || {
1457                                         idx:      dev.ifindex,
1458                                         name:     name,
1459                                         rawname:  name,
1460                                         flags:    dev.flags,
1461                                         stats:    dev.stats,
1462                                         macaddr:  dev.mac,
1463                                         type:     dev.type,
1464                                         mtu:      dev.mtu,
1465                                         qlen:     dev.qlen,
1466                                         ipaddrs:  [],
1467                                         ip6addrs: []
1468                                 };
1469
1470                                 if (Array.isArray(dev.ipaddrs))
1471                                         for (var i = 0; i &lt; dev.ipaddrs.length; i++)
1472                                                 s.netdevs[name].ipaddrs.push(dev.ipaddrs[i].address + '/' + dev.ipaddrs[i].netmask);
1473
1474                                 if (Array.isArray(dev.ip6addrs))
1475                                         for (var i = 0; i &lt; dev.ip6addrs.length; i++)
1476                                                 s.netdevs[name].ip6addrs.push(dev.ip6addrs[i].address + '/' + dev.ip6addrs[i].netmask);
1477                         }
1478
1479                         for (var name in luci_devs) {
1480                                 var dev = luci_devs[name];
1481
1482                                 if (!dev.bridge)
1483                                         continue;
1484
1485                                 var b = {
1486                                         name:    name,
1487                                         id:      dev.id,
1488                                         stp:     dev.stp,
1489                                         ifnames: []
1490                                 };
1491
1492                                 for (var i = 0; dev.ports &amp;&amp; i &lt; dev.ports.length; i++) {
1493                                         var subdev = s.netdevs[dev.ports[i]];
1494
1495                                         if (subdev == null)
1496                                                 continue;
1497
1498                                         b.ifnames.push(subdev);
1499                                         subdev.bridge = b;
1500                                 }
1501
1502                                 s.bridges[name] = b;
1503                                 s.isBridge[name] = true;
1504                         }
1505
1506                         if (L.isObject(board_json.switch)) {
1507                                 for (var switchname in board_json.switch) {
1508                                         var layout = board_json.switch[switchname],
1509                                             netdevs = {},
1510                                             nports = {},
1511                                             ports = [],
1512                                             pnum = null,
1513                                             role = null;
1514
1515                                         if (L.isObject(layout) &amp;&amp; Array.isArray(layout.ports)) {
1516                                                 for (var i = 0, port; (port = layout.ports[i]) != null; i++) {
1517                                                         if (typeof(port) == 'object' &amp;&amp; typeof(port.num) == 'number' &amp;&amp;
1518                                                                 (typeof(port.role) == 'string' || typeof(port.device) == 'string')) {
1519                                                                 var spec = {
1520                                                                         num:   port.num,
1521                                                                         role:  port.role || 'cpu',
1522                                                                         index: (port.index != null) ? port.index : port.num
1523                                                                 };
1524
1525                                                                 if (port.device != null) {
1526                                                                         spec.device = port.device;
1527                                                                         spec.tagged = spec.need_tag;
1528                                                                         netdevs[port.num] = port.device;
1529                                                                 }
1530
1531                                                                 ports.push(spec);
1532
1533                                                                 if (port.role != null)
1534                                                                         nports[port.role] = (nports[port.role] || 0) + 1;
1535                                                         }
1536                                                 }
1537
1538                                                 ports.sort(function(a, b) {
1539                                                         if (a.role != b.role)
1540                                                                 return (a.role &lt; b.role) ? -1 : 1;
1541
1542                                                         return (a.index - b.index);
1543                                                 });
1544
1545                                                 for (var i = 0, port; (port = ports[i]) != null; i++) {
1546                                                         if (port.role != role) {
1547                                                                 role = port.role;
1548                                                                 pnum = 1;
1549                                                         }
1550
1551                                                         if (role == 'cpu')
1552                                                                 port.label = 'CPU (%s)'.format(port.device);
1553                                                         else if (nports[role] > 1)
1554                                                                 port.label = '%s %d'.format(role.toUpperCase(), pnum++);
1555                                                         else
1556                                                                 port.label = role.toUpperCase();
1557
1558                                                         delete port.role;
1559                                                         delete port.index;
1560                                                 }
1561
1562                                                 s.switches[switchname] = {
1563                                                         ports: ports,
1564                                                         netdevs: netdevs
1565                                                 };
1566                                         }
1567                                 }
1568                         }
1569
1570                         if (L.isObject(board_json.dsl) &amp;&amp; L.isObject(board_json.dsl.modem)) {
1571                                 s.hasDSLModem = board_json.dsl.modem;
1572                         }
1573
1574                         _init = null;
1575
1576                         var objects = [];
1577
1578                         if (L.isObject(s.radios))
1579                                 for (var radio in s.radios)
1580                                         if (L.isObject(s.radios[radio]) &amp;&amp; Array.isArray(s.radios[radio].interfaces))
1581                                                 for (var i = 0; i &lt; s.radios[radio].interfaces.length; i++)
1582                                                         if (L.isObject(s.radios[radio].interfaces[i]) &amp;&amp; s.radios[radio].interfaces[i].ifname)
1583                                                                 objects.push('hostapd.%s'.format(s.radios[radio].interfaces[i].ifname));
1584
1585                         return (objects.length ? L.resolveDefault(rpc.list.apply(rpc, objects), {}) : Promise.resolve({})).then(function(res) {
1586                                 for (var k in res) {
1587                                         var m = k.match(/^hostapd\.(.+)$/);
1588                                         if (m)
1589                                                 s.hostapd[m[1]] = res[k];
1590                                 }
1591
1592                                 return (_state = s);
1593                         });
1594                 });
1595         }
1596
1597         return (_state != null ? Promise.resolve(_state) : _init);
1598 }
1599
1600 function ifnameOf(obj) {
1601         if (obj instanceof Protocol)
1602                 return obj.getIfname();
1603         else if (obj instanceof Device)
1604                 return obj.getName();
1605         else if (obj instanceof WifiDevice)
1606                 return obj.getName();
1607         else if (obj instanceof WifiNetwork)
1608                 return obj.getIfname();
1609         else if (typeof(obj) == 'string')
1610                 return obj.replace(/:.+$/, '');
1611
1612         return null;
1613 }
1614
1615 function networkSort(a, b) {
1616         return a.getName() > b.getName();
1617 }
1618
1619 function deviceSort(a, b) {
1620         var typeWeigth = { wifi: 2, alias: 3 },
1621         weightA = typeWeigth[a.getType()] || 1,
1622         weightB = typeWeigth[b.getType()] || 1;
1623
1624     if (weightA != weightB)
1625         return weightA - weightB;
1626
1627         return a.getName() > b.getName();
1628 }
1629
1630 function formatWifiEncryption(enc) {
1631         if (!L.isObject(enc))
1632                 return null;
1633
1634         if (!enc.enabled)
1635                 return 'None';
1636
1637         var ciphers = Array.isArray(enc.ciphers)
1638                 ? enc.ciphers.map(function(c) { return c.toUpperCase() }) : [ 'NONE' ];
1639
1640         if (Array.isArray(enc.wep)) {
1641                 var has_open = false,
1642                     has_shared = false;
1643
1644                 for (var i = 0; i &lt; enc.wep.length; i++)
1645                         if (enc.wep[i] == 'open')
1646                                 has_open = true;
1647                         else if (enc.wep[i] == 'shared')
1648                                 has_shared = true;
1649
1650                 if (has_open &amp;&amp; has_shared)
1651                         return 'WEP Open/Shared (%s)'.format(ciphers.join(', '));
1652                 else if (has_open)
1653                         return 'WEP Open System (%s)'.format(ciphers.join(', '));
1654                 else if (has_shared)
1655                         return 'WEP Shared Auth (%s)'.format(ciphers.join(', '));
1656
1657                 return 'WEP';
1658         }
1659
1660         if (Array.isArray(enc.wpa)) {
1661                 var versions = [],
1662                     suites = Array.isArray(enc.authentication)
1663                         ? enc.authentication.map(function(a) { return a.toUpperCase() }) : [ 'NONE' ];
1664
1665                 for (var i = 0; i &lt; enc.wpa.length; i++)
1666                         switch (enc.wpa[i]) {
1667                         case 1:
1668                                 versions.push('WPA');
1669                                 break;
1670
1671                         default:
1672                                 versions.push('WPA%d'.format(enc.wpa[i]));
1673                                 break;
1674                         }
1675
1676                 if (versions.length > 1)
1677                         return 'mixed %s %s (%s)'.format(versions.join('/'), suites.join(', '), ciphers.join(', '));
1678
1679                 return '%s %s (%s)'.format(versions[0], suites.join(', '), ciphers.join(', '));
1680         }
1681
1682         return 'Unknown';
1683 }
1684
1685 function enumerateNetworks() {
1686         var uciInterfaces = uci.sections('network', 'interface'),
1687             networks = {};
1688
1689         for (var i = 0; i &lt; uciInterfaces.length; i++)
1690                 networks[uciInterfaces[i]['.name']] = this.instantiateNetwork(uciInterfaces[i]['.name']);
1691
1692         for (var i = 0; i &lt; _state.ifaces.length; i++)
1693                 if (networks[_state.ifaces[i].interface] == null)
1694                         networks[_state.ifaces[i].interface] =
1695                                 this.instantiateNetwork(_state.ifaces[i].interface, _state.ifaces[i].proto);
1696
1697         var rv = [];
1698
1699         for (var network in networks)
1700                 if (networks.hasOwnProperty(network))
1701                         rv.push(networks[network]);
1702
1703         rv.sort(networkSort);
1704
1705         return rv;
1706 }
1707
1708
1709 var Hosts, Network, Protocol, Device, WifiDevice, WifiNetwork;
1710
1711 /**
1712  * @class
1713  * @memberof LuCI
1714  * @hideconstructor
1715  * @classdesc
1716  *
1717  * The `LuCI.Network` class combines data from multiple `ubus` apis to
1718  * provide an abstraction of the current network configuration state.
1719  *
1720  * It provides methods to enumerate interfaces and devices, to query
1721  * current configuration details and to manipulate settings.
1722  */
1723 Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
1724         /**
1725          * Converts the given prefix size in bits to a netmask.
1726          *
1727          * @method
1728          *
1729          * @param {number} bits
1730          * The prefix size in bits.
1731          *
1732          * @param {boolean} [v6=false]
1733          * Whether to convert the bits value into an IPv4 netmask (`false`) or
1734          * an IPv6 netmask (`true`).
1735          *
1736          * @returns {null|string}
1737          * Returns a string containing the netmask corresponding to the bit count
1738          * or `null` when the given amount of bits exceeds the maximum possible
1739          * value of `32` for IPv4 or `128` for IPv6.
1740          */
1741         prefixToMask: prefixToMask,
1742
1743         /**
1744          * Converts the given netmask to a prefix size in bits.
1745          *
1746          * @method
1747          *
1748          * @param {string} netmask
1749          * The netmask to convert into a bit count.
1750          *
1751          * @param {boolean} [v6=false]
1752          * Whether to parse the given netmask as IPv4 (`false`) or IPv6 (`true`)
1753          * address.
1754          *
1755          * @returns {null|number}
1756          * Returns the number of prefix bits contained in the netmask or `null`
1757          * if the given netmask value was invalid.
1758          */
1759         maskToPrefix: maskToPrefix,
1760
1761         /**
1762          * An encryption entry describes active wireless encryption settings
1763          * such as the used key management protocols, active ciphers and
1764          * protocol versions.
1765          *
1766          * @typedef {Object&lt;string, boolean|Array&lt;number|string>>} LuCI.Network.WifiEncryption
1767          * @memberof LuCI.Network
1768          *
1769          * @property {boolean} enabled
1770          * Specifies whether any kind of encryption, such as `WEP` or `WPA` is
1771          * enabled. If set to `false`, then no encryption is active and the
1772          * corresponding network is open.
1773          *
1774          * @property {string[]} [wep]
1775          * When the `wep` property exists, the network uses WEP encryption.
1776          * In this case, the property is set to an array of active WEP modes
1777          * which might be either `open`, `shared` or both.
1778          *
1779          * @property {number[]} [wpa]
1780          * When the `wpa` property exists, the network uses WPA security.
1781          * In this case, the property is set to an array containing the WPA
1782          * protocol versions used, e.g. `[ 1, 2 ]` for WPA/WPA2 mixed mode or
1783          * `[ 3 ]` for WPA3-SAE.
1784          *
1785          * @property {string[]} [authentication]
1786          * The `authentication` property only applies to WPA encryption and
1787          * is defined when the `wpa` property is set as well. It points to
1788          * an array of active authentication suites used by the network, e.g.
1789          * `[ "psk" ]` for a WPA(2)-PSK network or `[ "psk", "sae" ]` for
1790          * mixed WPA2-PSK/WPA3-SAE encryption.
1791          *
1792          * @property {string[]} [ciphers]
1793          * If either WEP or WPA encryption is active, then the `ciphers`
1794          * property will be set to an array describing the active encryption
1795          * ciphers used by the network, e.g. `[ "tkip", "ccmp" ]` for a
1796          * WPA/WPA2-PSK mixed network or `[ "wep-40", "wep-104" ]` for an
1797          * WEP network.
1798          */
1799
1800         /**
1801          * Converts a given {@link LuCI.Network.WifiEncryption encryption entry}
1802          * into a human readable string such as `mixed WPA/WPA2 PSK (TKIP, CCMP)`
1803          * or `WPA3 SAE (CCMP)`.
1804          *
1805          * @method
1806          *
1807          * @param {LuCI.Network.WifiEncryption} encryption
1808          * The wireless encryption entry to convert.
1809          *
1810          * @returns {null|string}
1811          * Returns the description string for the given encryption entry or
1812          * `null` if the given entry was invalid.
1813          */
1814         formatWifiEncryption: formatWifiEncryption,
1815
1816         /**
1817          * Flushes the local network state cache and fetches updated information
1818          * from the remote `ubus` apis.
1819          *
1820          * @returns {Promise&lt;Object>}
1821          * Returns a promise resolving to the internal network state object.
1822          */
1823         flushCache: function() {
1824                 initNetworkState(true);
1825                 return _init;
1826         },
1827
1828         /**
1829          * Instantiates the given {@link LuCI.Network.Protocol Protocol} backend,
1830          * optionally using the given network name.
1831          *
1832          * @param {string} protoname
1833          * The protocol backend to use, e.g. `static` or `dhcp`.
1834          *
1835          * @param {string} [netname=__dummy__]
1836          * The network name to use for the instantiated protocol. This should be
1837          * usually set to one of the interfaces described in /etc/config/network
1838          * but it is allowed to omit it, e.g. to query protocol capabilities
1839          * without the need for an existing interface.
1840          *
1841          * @returns {null|LuCI.Network.Protocol}
1842          * Returns the instantiated protocol backend class or `null` if the given
1843          * protocol isn't known.
1844          */
1845         getProtocol: function(protoname, netname) {
1846                 var v = _protocols[protoname];
1847                 if (v != null)
1848                         return new v(netname || '__dummy__');
1849
1850                 return null;
1851         },
1852
1853         /**
1854          * Obtains instances of all known {@link LuCI.Network.Protocol Protocol}
1855          * backend classes.
1856          *
1857          * @returns {Array&lt;LuCI.Network.Protocol>}
1858          * Returns an array of protocol class instances.
1859          */
1860         getProtocols: function() {
1861                 var rv = [];
1862
1863                 for (var protoname in _protocols)
1864                         rv.push(new _protocols[protoname]('__dummy__'));
1865
1866                 return rv;
1867         },
1868
1869         /**
1870          * Registers a new {@link LuCI.Network.Protocol Protocol} subclass
1871          * with the given methods and returns the resulting subclass value.
1872          *
1873          * This functions internally calls
1874          * {@link LuCI.Class.extend Class.extend()} on the `Network.Protocol`
1875          * base class.
1876          *
1877          * @param {string} protoname
1878          * The name of the new protocol to register.
1879          *
1880          * @param {Object&lt;string, *>} methods
1881          * The member methods and values of the new `Protocol` subclass to
1882          * be passed to {@link LuCI.Class.extend Class.extend()}.
1883          *
1884          * @returns {LuCI.Network.Protocol}
1885          * Returns the new `Protocol` subclass.
1886          */
1887         registerProtocol: function(protoname, methods) {
1888                 var spec = L.isObject(_protospecs) ? _protospecs[protoname] : null;
1889                 var proto = Protocol.extend(Object.assign({
1890                         getI18n: function() {
1891                                 return protoname;
1892                         },
1893
1894                         isFloating: function() {
1895                                 return false;
1896                         },
1897
1898                         isVirtual: function() {
1899                                 return (L.isObject(spec) &amp;&amp; spec.no_device == true);
1900                         },
1901
1902                         renderFormOptions: function(section) {
1903
1904                         }
1905                 }, methods, {
1906                         __init__: function(name) {
1907                                 this.sid = name;
1908                         },
1909
1910                         getProtocol: function() {
1911                                 return protoname;
1912                         }
1913                 }));
1914
1915                 _protocols[protoname] = proto;
1916
1917                 return proto;
1918         },
1919
1920         /**
1921          * Registers a new regular expression pattern to recognize
1922          * virtual interfaces.
1923          *
1924          * @param {RegExp} pat
1925          * A `RegExp` instance to match a virtual interface name
1926          * such as `6in4-wan` or `tun0`.
1927          */
1928         registerPatternVirtual: function(pat) {
1929                 iface_patterns_virtual.push(pat);
1930         },
1931
1932         /**
1933          * Registers a new human readable translation string for a `Protocol`
1934          * error code.
1935          *
1936          * @param {string} code
1937          * The `ubus` protocol error code to register a translation for, e.g.
1938          * `NO_DEVICE`.
1939          *
1940          * @param {string} message
1941          * The message to use as translation for the given protocol error code.
1942          *
1943          * @returns {boolean}
1944          * Returns `true` if the error code description has been added or `false`
1945          * if either the arguments were invalid or if there already was a
1946          * description for the given code.
1947          */
1948         registerErrorCode: function(code, message) {
1949                 if (typeof(code) == 'string' &amp;&amp;
1950                     typeof(message) == 'string' &amp;&amp;
1951                     !proto_errors.hasOwnProperty(code)) {
1952                         proto_errors[code] = message;
1953                         return true;
1954                 }
1955
1956                 return false;
1957         },
1958
1959         /**
1960          * Adds a new network of the given name and update it with the given
1961          * uci option values.
1962          *
1963          * If a network with the given name already exist but is empty, then
1964          * this function will update its option, otherwise it will do nothing.
1965          *
1966          * @param {string} name
1967          * The name of the network to add. Must be in the format `[a-zA-Z0-9_]+`.
1968          *
1969          * @param {Object&lt;string, string|string[]>} [options]
1970          * An object of uci option values to set on the new network or to
1971          * update in an existing, empty network.
1972          *
1973          * @returns {Promise&lt;null|LuCI.Network.Protocol>}
1974          * Returns a promise resolving to the `Protocol` subclass instance
1975          * describing the added network or resolving to `null` if the name
1976          * was invalid or if a non-empty network of the given name already
1977          * existed.
1978          */
1979         addNetwork: function(name, options) {
1980                 return this.getNetwork(name).then(L.bind(function(existingNetwork) {
1981                         if (name != null &amp;&amp; /^[a-zA-Z0-9_]+$/.test(name) &amp;&amp; existingNetwork == null) {
1982                                 var sid = uci.add('network', 'interface', name);
1983
1984                                 if (sid != null) {
1985                                         if (L.isObject(options))
1986                                                 for (var key in options)
1987                                                         if (options.hasOwnProperty(key))
1988                                                                 uci.set('network', sid, key, options[key]);
1989
1990                                         return this.instantiateNetwork(sid);
1991                                 }
1992                         }
1993                         else if (existingNetwork != null &amp;&amp; existingNetwork.isEmpty()) {
1994                                 if (L.isObject(options))
1995                                         for (var key in options)
1996                                                 if (options.hasOwnProperty(key))
1997                                                         existingNetwork.set(key, options[key]);
1998
1999                                 return existingNetwork;
2000                         }
2001                 }, this));
2002         },
2003
2004         /**
2005          * Get a {@link LuCI.Network.Protocol Protocol} instance describing
2006          * the network with the given name.
2007          *
2008          * @param {string} name
2009          * The logical interface name of the network get, e.g. `lan` or `wan`.
2010          *
2011          * @returns {Promise&lt;null|LuCI.Network.Protocol>}
2012          * Returns a promise resolving to a
2013          * {@link LuCI.Network.Protocol Protocol} subclass instance describing
2014          * the network or `null` if the network did not exist.
2015          */
2016         getNetwork: function(name) {
2017                 return initNetworkState().then(L.bind(function() {
2018                         var section = (name != null) ? uci.get('network', name) : null;
2019
2020                         if (section != null &amp;&amp; section['.type'] == 'interface') {
2021                                 return this.instantiateNetwork(name);
2022                         }
2023                         else if (name != null) {
2024                                 for (var i = 0; i &lt; _state.ifaces.length; i++)
2025                                         if (_state.ifaces[i].interface == name)
2026                                                 return this.instantiateNetwork(name, _state.ifaces[i].proto);
2027                         }
2028
2029                         return null;
2030                 }, this));
2031         },
2032
2033         /**
2034          * Gets an array containing all known networks.
2035          *
2036          * @returns {Promise&lt;Array&lt;LuCI.Network.Protocol>>}
2037          * Returns a promise resolving to a name-sorted array of
2038          * {@link LuCI.Network.Protocol Protocol} subclass instances
2039          * describing all known networks.
2040          */
2041         getNetworks: function() {
2042                 return initNetworkState().then(L.bind(enumerateNetworks, this));
2043         },
2044
2045         /**
2046          * Deletes the given network and its references from the network and
2047          * firewall configuration.
2048          *
2049          * @param {string} name
2050          * The name of the network to delete.
2051          *
2052          * @returns {Promise&lt;boolean>}
2053          * Returns a promise resolving to either `true` if the network and
2054          * references to it were successfully deleted from the configuration or
2055          * `false` if the given network could not be found.
2056          */
2057         deleteNetwork: function(name) {
2058                 var requireFirewall = Promise.resolve(L.require('firewall')).catch(function() {});
2059
2060                 return Promise.all([ requireFirewall, initNetworkState() ]).then(function() {
2061                         var uciInterface = uci.get('network', name);
2062
2063                         if (uciInterface != null &amp;&amp; uciInterface['.type'] == 'interface') {
2064                                 uci.remove('network', name);
2065
2066                                 uci.sections('luci', 'ifstate', function(s) {
2067                                         if (s.interface == name)
2068                                                 uci.remove('luci', s['.name']);
2069                                 });
2070
2071                                 uci.sections('network', 'alias', function(s) {
2072                                         if (s.interface == name)
2073                                                 uci.remove('network', s['.name']);
2074                                 });
2075
2076                                 uci.sections('network', 'route', function(s) {
2077                                         if (s.interface == name)
2078                                                 uci.remove('network', s['.name']);
2079                                 });
2080
2081                                 uci.sections('network', 'route6', function(s) {
2082                                         if (s.interface == name)
2083                                                 uci.remove('network', s['.name']);
2084                                 });
2085
2086                                 uci.sections('wireless', 'wifi-iface', function(s) {
2087                                         var networks = L.toArray(s.network).filter(function(network) { return network != name });
2088
2089                                         if (networks.length > 0)
2090                                                 uci.set('wireless', s['.name'], 'network', networks.join(' '));
2091                                         else
2092                                                 uci.unset('wireless', s['.name'], 'network');
2093                                 });
2094
2095                                 if (L.firewall)
2096                                         return L.firewall.deleteNetwork(name).then(function() { return true });
2097
2098                                 return true;
2099                         }
2100
2101                         return false;
2102                 });
2103         },
2104
2105         /**
2106          * Rename the given network and its references to a new name.
2107          *
2108          * @param {string} oldName
2109          * The current name of the network.
2110          *
2111          * @param {string} newName
2112          * The name to rename the network to, must be in the format
2113          * `[a-z-A-Z0-9_]+`.
2114          *
2115          * @returns {Promise&lt;boolean>}
2116          * Returns a promise resolving to either `true` if the network was
2117          * successfully renamed or `false` if the new name was invalid, if
2118          * a network with the new name already exists or if the network to
2119          * rename could not be found.
2120          */
2121         renameNetwork: function(oldName, newName) {
2122                 return initNetworkState().then(function() {
2123                         if (newName == null || !/^[a-zA-Z0-9_]+$/.test(newName) || uci.get('network', newName) != null)
2124                                 return false;
2125
2126                         var oldNetwork = uci.get('network', oldName);
2127
2128                         if (oldNetwork == null || oldNetwork['.type'] != 'interface')
2129                                 return false;
2130
2131                         var sid = uci.add('network', 'interface', newName);
2132
2133                         for (var key in oldNetwork)
2134                                 if (oldNetwork.hasOwnProperty(key) &amp;&amp; key.charAt(0) != '.')
2135                                         uci.set('network', sid, key, oldNetwork[key]);
2136
2137                         uci.sections('luci', 'ifstate', function(s) {
2138                                 if (s.interface == oldName)
2139                                         uci.set('luci', s['.name'], 'interface', newName);
2140                         });
2141
2142                         uci.sections('network', 'alias', function(s) {
2143                                 if (s.interface == oldName)
2144                                         uci.set('network', s['.name'], 'interface', newName);
2145                         });
2146
2147                         uci.sections('network', 'route', function(s) {
2148                                 if (s.interface == oldName)
2149                                         uci.set('network', s['.name'], 'interface', newName);
2150                         });
2151
2152                         uci.sections('network', 'route6', function(s) {
2153                                 if (s.interface == oldName)
2154                                         uci.set('network', s['.name'], 'interface', newName);
2155                         });
2156
2157                         uci.sections('wireless', 'wifi-iface', function(s) {
2158                                 var networks = L.toArray(s.network).map(function(network) { return (network == oldName ? newName : network) });
2159
2160                                 if (networks.length > 0)
2161                                         uci.set('wireless', s['.name'], 'network', networks.join(' '));
2162                         });
2163
2164                         uci.remove('network', oldName);
2165
2166                         return true;
2167                 });
2168         },
2169
2170         /**
2171          * Get a {@link LuCI.Network.Device Device} instance describing the
2172          * given network device.
2173          *
2174          * @param {string} name
2175          * The name of the network device to get, e.g. `eth0` or `br-lan`.
2176          *
2177          * @returns {Promise&lt;null|LuCI.Network.Device>}
2178          * Returns a promise resolving to the `Device` instance describing
2179          * the network device or `null` if the given device name could not
2180          * be found.
2181          */
2182         getDevice: function(name) {
2183                 return initNetworkState().then(L.bind(function() {
2184                         if (name == null)
2185                                 return null;
2186
2187                         if (_state.netdevs.hasOwnProperty(name) || isWifiIfname(name))
2188                                 return this.instantiateDevice(name);
2189
2190                         var netid = getWifiNetidBySid(name);
2191                         if (netid != null)
2192                                 return this.instantiateDevice(netid[0]);
2193
2194                         return null;
2195                 }, this));
2196         },
2197
2198         /**
2199          * Get a sorted list of all found network devices.
2200          *
2201          * @returns {Promise&lt;Array&lt;LuCI.Network.Device>>}
2202          * Returns a promise resolving to a sorted array of `Device` class
2203          * instances describing the network devices found on the system.
2204          */
2205         getDevices: function() {
2206                 return initNetworkState().then(L.bind(function() {
2207                         var devices = {};
2208
2209                         /* find simple devices */
2210                         var uciInterfaces = uci.sections('network', 'interface');
2211                         for (var i = 0; i &lt; uciInterfaces.length; i++) {
2212                                 var ifnames = L.toArray(uciInterfaces[i].ifname);
2213
2214                                 for (var j = 0; j &lt; ifnames.length; j++) {
2215                                         if (ifnames[j].charAt(0) == '@')
2216                                                 continue;
2217
2218                                         if (isIgnoredIfname(ifnames[j]) || isVirtualIfname(ifnames[j]) || isWifiIfname(ifnames[j]))
2219                                                 continue;
2220
2221                                         devices[ifnames[j]] = this.instantiateDevice(ifnames[j]);
2222                                 }
2223                         }
2224
2225                         for (var ifname in _state.netdevs) {
2226                                 if (devices.hasOwnProperty(ifname))
2227                                         continue;
2228
2229                                 if (isIgnoredIfname(ifname) || isVirtualIfname(ifname) || isWifiIfname(ifname))
2230                                         continue;
2231
2232                                 devices[ifname] = this.instantiateDevice(ifname);
2233                         }
2234
2235                         /* find VLAN devices */
2236                         var uciSwitchVLANs = uci.sections('network', 'switch_vlan');
2237                         for (var i = 0; i &lt; uciSwitchVLANs.length; i++) {
2238                                 if (typeof(uciSwitchVLANs[i].ports) != 'string' ||
2239                                     typeof(uciSwitchVLANs[i].device) != 'string' ||
2240                                     !_state.switches.hasOwnProperty(uciSwitchVLANs[i].device))
2241                                         continue;
2242
2243                                 var ports = uciSwitchVLANs[i].ports.split(/\s+/);
2244                                 for (var j = 0; j &lt; ports.length; j++) {
2245                                         var m = ports[j].match(/^(\d+)([tu]?)$/);
2246                                         if (m == null)
2247                                                 continue;
2248
2249                                         var netdev = _state.switches[uciSwitchVLANs[i].device].netdevs[m[1]];
2250                                         if (netdev == null)
2251                                                 continue;
2252
2253                                         if (!devices.hasOwnProperty(netdev))
2254                                                 devices[netdev] = this.instantiateDevice(netdev);
2255
2256                                         _state.isSwitch[netdev] = true;
2257
2258                                         if (m[2] != 't')
2259                                                 continue;
2260
2261                                         var vid = uciSwitchVLANs[i].vid || uciSwitchVLANs[i].vlan;
2262                                             vid = (vid != null ? +vid : null);
2263
2264                                         if (vid == null || vid &lt; 0 || vid > 4095)
2265                                                 continue;
2266
2267                                         var vlandev = '%s.%d'.format(netdev, vid);
2268
2269                                         if (!devices.hasOwnProperty(vlandev))
2270                                                 devices[vlandev] = this.instantiateDevice(vlandev);
2271
2272                                         _state.isSwitch[vlandev] = true;
2273                                 }
2274                         }
2275
2276                         /* find wireless interfaces */
2277                         var uciWifiIfaces = uci.sections('wireless', 'wifi-iface'),
2278                             networkCount = {};
2279
2280                         for (var i = 0; i &lt; uciWifiIfaces.length; i++) {
2281                                 if (typeof(uciWifiIfaces[i].device) != 'string')
2282                                         continue;
2283
2284                                 networkCount[uciWifiIfaces[i].device] = (networkCount[uciWifiIfaces[i].device] || 0) + 1;
2285
2286                                 var netid = '%s.network%d'.format(uciWifiIfaces[i].device, networkCount[uciWifiIfaces[i].device]);
2287
2288                                 devices[netid] = this.instantiateDevice(netid);
2289                         }
2290
2291                         var rv = [];
2292
2293                         for (var netdev in devices)
2294                                 if (devices.hasOwnProperty(netdev))
2295                                         rv.push(devices[netdev]);
2296
2297                         rv.sort(deviceSort);
2298
2299                         return rv;
2300                 }, this));
2301         },
2302
2303         /**
2304          * Test if a given network device name is in the list of patterns for
2305          * device names to ignore.
2306          *
2307          * Ignored device names are usually Linux network devices which are
2308          * spawned implicitly by kernel modules such as `tunl0` or `hwsim0`
2309          * and which are unsuitable for use in network configuration.
2310          *
2311          * @param {string} name
2312          * The device name to test.
2313          *
2314          * @returns {boolean}
2315          * Returns `true` if the given name is in the ignore pattern list,
2316          * else returns `false`.
2317          */
2318         isIgnoredDevice: function(name) {
2319                 return isIgnoredIfname(name);
2320         },
2321
2322         /**
2323          * Get a {@link LuCI.Network.WifiDevice WifiDevice} instance describing
2324          * the given wireless radio.
2325          *
2326          * @param {string} devname
2327          * The configuration name of the wireless radio to lookup, e.g. `radio0`
2328          * for the first mac80211 phy on the system.
2329          *
2330          * @returns {Promise&lt;null|LuCI.Network.WifiDevice>}
2331          * Returns a promise resolving to the `WifiDevice` instance describing
2332          * the underlying radio device or `null` if the wireless radio could not
2333          * be found.
2334          */
2335         getWifiDevice: function(devname) {
2336                 return initNetworkState().then(L.bind(function() {
2337                         var existingDevice = uci.get('wireless', devname);
2338
2339                         if (existingDevice == null || existingDevice['.type'] != 'wifi-device')
2340                                 return null;
2341
2342                         return this.instantiateWifiDevice(devname, _state.radios[devname] || {});
2343                 }, this));
2344         },
2345
2346         /**
2347          * Obtain a list of all configured radio devices.
2348          *
2349          * @returns {Promise&lt;Array&lt;LuCI.Network.WifiDevice>>}
2350          * Returns a promise resolving to an array of `WifiDevice` instances
2351          * describing the wireless radios configured in the system.
2352          * The order of the array corresponds to the order of the radios in
2353          * the configuration.
2354          */
2355         getWifiDevices: function() {
2356                 return initNetworkState().then(L.bind(function() {
2357                         var uciWifiDevices = uci.sections('wireless', 'wifi-device'),
2358                             rv = [];
2359
2360                         for (var i = 0; i &lt; uciWifiDevices.length; i++) {
2361                                 var devname = uciWifiDevices[i]['.name'];
2362                                 rv.push(this.instantiateWifiDevice(devname, _state.radios[devname] || {}));
2363                         }
2364
2365                         return rv;
2366                 }, this));
2367         },
2368
2369         /**
2370          * Get a {@link LuCI.Network.WifiNetwork WifiNetwork} instance describing
2371          * the given wireless network.
2372          *
2373          * @param {string} netname
2374          * The name of the wireless network to lookup. This may be either an uci
2375          * configuration section ID, a network ID in the form `radio#.network#`
2376          * or a Linux network device name like `wlan0` which is resolved to the
2377          * corresponding configuration section through `ubus` runtime information.
2378          *
2379          * @returns {Promise&lt;null|LuCI.Network.WifiNetwork>}
2380          * Returns a promise resolving to the `WifiNetwork` instance describing
2381          * the wireless network or `null` if the corresponding network could not
2382          * be found.
2383          */
2384         getWifiNetwork: function(netname) {
2385                 return initNetworkState()
2386                         .then(L.bind(this.lookupWifiNetwork, this, netname));
2387         },
2388
2389         /**
2390          * Get an array of all {@link LuCI.Network.WifiNetwork WifiNetwork}
2391          * instances describing the wireless networks present on the system.
2392          *
2393          * @returns {Promise&lt;Array&lt;LuCI.Network.WifiNetwork>>}
2394          * Returns a promise resolving to an array of `WifiNetwork` instances
2395          * describing the wireless networks. The array will be empty if no networks
2396          * are found.
2397          */
2398         getWifiNetworks: function() {
2399                 return initNetworkState().then(L.bind(function() {
2400                         var wifiIfaces = uci.sections('wireless', 'wifi-iface'),
2401                             rv = [];
2402
2403                         for (var i = 0; i &lt; wifiIfaces.length; i++)
2404                                 rv.push(this.lookupWifiNetwork(wifiIfaces[i]['.name']));
2405
2406                         rv.sort(function(a, b) {
2407                                 return (a.getID() > b.getID());
2408                         });
2409
2410                         return rv;
2411                 }, this));
2412         },
2413
2414         /**
2415          * Adds a new wireless network to the configuration and sets its options
2416          * to the provided values.
2417          *
2418          * @param {Object&lt;string, string|string[]>} options
2419          * The options to set for the newly added wireless network. This object
2420          * must at least contain a `device` property which is set to the radio
2421          * name the new network belongs to.
2422          *
2423          * @returns {Promise&lt;null|LuCI.Network.WifiNetwork>}
2424          * Returns a promise resolving to a `WifiNetwork` instance describing
2425          * the newly added wireless network or `null` if the given options
2426          * were invalid or if the associated radio device could not be found.
2427          */
2428         addWifiNetwork: function(options) {
2429                 return initNetworkState().then(L.bind(function() {
2430                         if (options == null ||
2431                             typeof(options) != 'object' ||
2432                             typeof(options.device) != 'string')
2433                             return null;
2434
2435                         var existingDevice = uci.get('wireless', options.device);
2436                         if (existingDevice == null || existingDevice['.type'] != 'wifi-device')
2437                                 return null;
2438
2439                         /* XXX: need to add a named section (wifinet#) here */
2440                         var sid = uci.add('wireless', 'wifi-iface');
2441                         for (var key in options)
2442                                 if (options.hasOwnProperty(key))
2443                                         uci.set('wireless', sid, key, options[key]);
2444
2445                         var radioname = existingDevice['.name'],
2446                             netid = getWifiNetidBySid(sid) || [];
2447
2448                         return this.instantiateWifiNetwork(sid, radioname, _state.radios[radioname], netid[0], null);
2449                 }, this));
2450         },
2451
2452         /**
2453          * Deletes the given wireless network from the configuration.
2454          *
2455          * @param {string} netname
2456          * The name of the network to remove. This may be either a
2457          * network ID in the form `radio#.network#` or a Linux network device
2458          * name like `wlan0` which is resolved to the corresponding configuration
2459          * section through `ubus` runtime information.
2460          *
2461          * @returns {Promise&lt;boolean>}
2462          * Returns a promise resolving to `true` if the wireless network has been
2463          * successfully deleted from the configuration or `false` if it could not
2464          * be found.
2465          */
2466         deleteWifiNetwork: function(netname) {
2467                 return initNetworkState().then(L.bind(function() {
2468                         var sid = getWifiSidByIfname(netname);
2469
2470                         if (sid == null)
2471                                 return false;
2472
2473                         uci.remove('wireless', sid);
2474                         return true;
2475                 }, this));
2476         },
2477
2478         /* private */
2479         getStatusByRoute: function(addr, mask) {
2480                 return initNetworkState().then(L.bind(function() {
2481                         var rv = [];
2482
2483                         for (var i = 0; i &lt; _state.ifaces.length; i++) {
2484                                 if (!Array.isArray(_state.ifaces[i].route))
2485                                         continue;
2486
2487                                 for (var j = 0; j &lt; _state.ifaces[i].route.length; j++) {
2488                                         if (typeof(_state.ifaces[i].route[j]) != 'object' ||
2489                                             typeof(_state.ifaces[i].route[j].target) != 'string' ||
2490                                             typeof(_state.ifaces[i].route[j].mask) != 'number')
2491                                             continue;
2492
2493                                         if (_state.ifaces[i].route[j].table)
2494                                                 continue;
2495
2496                                         if (_state.ifaces[i].route[j].target != addr ||
2497                                             _state.ifaces[i].route[j].mask != mask)
2498                                             continue;
2499
2500                                         rv.push(_state.ifaces[i]);
2501                                 }
2502                         }
2503
2504                         return rv;
2505                 }, this));
2506         },
2507
2508         /* private */
2509         getStatusByAddress: function(addr) {
2510                 return initNetworkState().then(L.bind(function() {
2511                         var rv = [];
2512
2513                         for (var i = 0; i &lt; _state.ifaces.length; i++) {
2514                                 if (Array.isArray(_state.ifaces[i]['ipv4-address']))
2515                                         for (var j = 0; j &lt; _state.ifaces[i]['ipv4-address'].length; j++)
2516                                                 if (typeof(_state.ifaces[i]['ipv4-address'][j]) == 'object' &amp;&amp;
2517                                                     _state.ifaces[i]['ipv4-address'][j].address == addr)
2518                                                         return _state.ifaces[i];
2519
2520                                 if (Array.isArray(_state.ifaces[i]['ipv6-address']))
2521                                         for (var j = 0; j &lt; _state.ifaces[i]['ipv6-address'].length; j++)
2522                                                 if (typeof(_state.ifaces[i]['ipv6-address'][j]) == 'object' &amp;&amp;
2523                                                     _state.ifaces[i]['ipv6-address'][j].address == addr)
2524                                                         return _state.ifaces[i];
2525
2526                                 if (Array.isArray(_state.ifaces[i]['ipv6-prefix-assignment']))
2527                                         for (var j = 0; j &lt; _state.ifaces[i]['ipv6-prefix-assignment'].length; j++)
2528                                                 if (typeof(_state.ifaces[i]['ipv6-prefix-assignment'][j]) == 'object' &amp;&amp;
2529                                                         typeof(_state.ifaces[i]['ipv6-prefix-assignment'][j]['local-address']) == 'object' &amp;&amp;
2530                                                     _state.ifaces[i]['ipv6-prefix-assignment'][j]['local-address'].address == addr)
2531                                                         return _state.ifaces[i];
2532                         }
2533
2534                         return null;
2535                 }, this));
2536         },
2537
2538         /**
2539          * Get IPv4 wan networks.
2540          *
2541          * This function looks up all networks having a default `0.0.0.0/0` route
2542          * and returns them as array.
2543          *
2544          * @returns {Promise&lt;Array&lt;LuCI.Network.Protocol>>}
2545          * Returns a promise resolving to an array of `Protocol` subclass
2546          * instances describing the found default route interfaces.
2547          */
2548         getWANNetworks: function() {
2549                 return this.getStatusByRoute('0.0.0.0', 0).then(L.bind(function(statuses) {
2550                         var rv = [], seen = {};
2551
2552                         for (var i = 0; i &lt; statuses.length; i++) {
2553                                 if (!seen.hasOwnProperty(statuses[i].interface)) {
2554                                         rv.push(this.instantiateNetwork(statuses[i].interface, statuses[i].proto));
2555                                         seen[statuses[i].interface] = true;
2556                                 }
2557                         }
2558
2559                         return rv;
2560                 }, this));
2561         },
2562
2563         /**
2564          * Get IPv6 wan networks.
2565          *
2566          * This function looks up all networks having a default `::/0` route
2567          * and returns them as array.
2568          *
2569          * @returns {Promise&lt;Array&lt;LuCI.Network.Protocol>>}
2570          * Returns a promise resolving to an array of `Protocol` subclass
2571          * instances describing the found IPv6 default route interfaces.
2572          */
2573         getWAN6Networks: function() {
2574                 return this.getStatusByRoute('::', 0).then(L.bind(function(statuses) {
2575                         var rv = [], seen = {};
2576
2577                         for (var i = 0; i &lt; statuses.length; i++) {
2578                                 if (!seen.hasOwnProperty(statuses[i].interface)) {
2579                                         rv.push(this.instantiateNetwork(statuses[i].interface, statuses[i].proto));
2580                                         seen[statuses[i].interface] = true;
2581                                 }
2582                         }
2583
2584                         return rv;
2585                 }, this));
2586         },
2587
2588         /**
2589          * Describes an swconfig switch topology by specifying the CPU
2590          * connections and external port labels of a switch.
2591          *
2592          * @typedef {Object&lt;string, Object|Array>} SwitchTopology
2593          * @memberof LuCI.Network
2594          *
2595          * @property {Object&lt;number, string>} netdevs
2596          * The `netdevs` property points to an object describing the CPU port
2597          * connections of the switch. The numeric key of the enclosed object is
2598          * the port number, the value contains the Linux network device name the
2599          * port is hardwired to.
2600          *
2601          * @property {Array&lt;Object&lt;string, boolean|number|string>>} ports
2602          * The `ports` property points to an array describing the populated
2603          * ports of the switch in the external label order. Each array item is
2604          * an object containg the following keys:
2605          *  - `num` - the internal switch port number
2606          *  - `label` - the label of the port, e.g. `LAN 1` or `CPU (eth0)`
2607          *  - `device` - the connected Linux network device name (CPU ports only)
2608          *  - `tagged` - a boolean indicating whether the port must be tagged to
2609          *     function (CPU ports only)
2610          */
2611
2612         /**
2613          * Returns the topologies of all swconfig switches found on the system.
2614          *
2615          * @returns {Promise&lt;Object&lt;string, LuCI.Network.SwitchTopology>>}
2616          * Returns a promise resolving to an object containing the topologies
2617          * of each switch. The object keys correspond to the name of the switches
2618          * such as `switch0`, the values are
2619          * {@link LuCI.Network.SwitchTopology SwitchTopology} objects describing
2620          * the layout.
2621          */
2622         getSwitchTopologies: function() {
2623                 return initNetworkState().then(function() {
2624                         return _state.switches;
2625                 });
2626         },
2627
2628         /* private */
2629         instantiateNetwork: function(name, proto) {
2630                 if (name == null)
2631                         return null;
2632
2633                 proto = (proto == null ? uci.get('network', name, 'proto') : proto);
2634
2635                 var protoClass = _protocols[proto] || Protocol;
2636                 return new protoClass(name);
2637         },
2638
2639         /* private */
2640         instantiateDevice: function(name, network, extend) {
2641                 if (extend != null)
2642                         return new (Device.extend(extend))(name, network);
2643
2644                 return new Device(name, network);
2645         },
2646
2647         /* private */
2648         instantiateWifiDevice: function(radioname, radiostate) {
2649                 return new WifiDevice(radioname, radiostate);
2650         },
2651
2652         /* private */
2653         instantiateWifiNetwork: function(sid, radioname, radiostate, netid, netstate, hostapd) {
2654                 return new WifiNetwork(sid, radioname, radiostate, netid, netstate, hostapd);
2655         },
2656
2657         /* private */
2658         lookupWifiNetwork: function(netname) {
2659                 var sid, res, netid, radioname, radiostate, netstate;
2660
2661                 sid = getWifiSidByNetid(netname);
2662
2663                 if (sid != null) {
2664                         res        = getWifiStateBySid(sid);
2665                         netid      = netname;
2666                         radioname  = res ? res[0] : null;
2667                         radiostate = res ? res[1] : null;
2668                         netstate   = res ? res[2] : null;
2669                 }
2670                 else {
2671                         res = getWifiStateByIfname(netname);
2672
2673                         if (res != null) {
2674                                 radioname  = res[0];
2675                                 radiostate = res[1];
2676                                 netstate   = res[2];
2677                                 sid        = netstate.section;
2678                                 netid      = L.toArray(getWifiNetidBySid(sid))[0];
2679                         }
2680                         else {
2681                                 res = getWifiStateBySid(netname);
2682
2683                                 if (res != null) {
2684                                         radioname  = res[0];
2685                                         radiostate = res[1];
2686                                         netstate   = res[2];
2687                                         sid        = netname;
2688                                         netid      = L.toArray(getWifiNetidBySid(sid))[0];
2689                                 }
2690                                 else {
2691                                         res = getWifiNetidBySid(netname);
2692
2693                                         if (res != null) {
2694                                                 netid     = res[0];
2695                                                 radioname = res[1];
2696                                                 sid       = netname;
2697                                         }
2698                                 }
2699                         }
2700                 }
2701
2702                 return this.instantiateWifiNetwork(sid || netname, radioname,
2703                         radiostate, netid, netstate,
2704                         netstate ? _state.hostapd[netstate.ifname] : null);
2705         },
2706
2707         /**
2708          * Obtains the the network device name of the given object.
2709          *
2710          * @param {LuCI.Network.Protocol|LuCI.Network.Device|LuCI.Network.WifiDevice|LuCI.Network.WifiNetwork|string} obj
2711          * The object to get the device name from.
2712          *
2713          * @returns {null|string}
2714          * Returns a string containing the device name or `null` if the given
2715          * object could not be converted to a name.
2716          */
2717         getIfnameOf: function(obj) {
2718                 return ifnameOf(obj);
2719         },
2720
2721         /**
2722          * Queries the internal DSL modem type from board information.
2723          *
2724          * @returns {Promise&lt;null|string>}
2725          * Returns a promise resolving to the type of the internal modem
2726          * (e.g. `vdsl`) or to `null` if no internal modem is present.
2727          */
2728         getDSLModemType: function() {
2729                 return initNetworkState().then(function() {
2730                         return _state.hasDSLModem ? _state.hasDSLModem.type : null;
2731                 });
2732         },
2733
2734         /**
2735          * Queries aggregated information about known hosts.
2736          *
2737          * This function aggregates information from various sources such as
2738          * DHCP lease databases, ARP and IPv6 neighbour entries, wireless
2739          * association list etc. and returns a {@link LuCI.Network.Hosts Hosts}
2740          * class instance describing the found hosts.
2741          *
2742          * @returns {Promise&lt;LuCI.Network.Hosts>}
2743          * Returns a `Hosts` instance describing host known on the system.
2744          */
2745         getHostHints: function() {
2746                 return initNetworkState().then(function() {
2747                         return new Hosts(_state.hosts);
2748                 });
2749         }
2750 });
2751
2752 /**
2753  * @class
2754  * @memberof LuCI.Network
2755  * @hideconstructor
2756  * @classdesc
2757  *
2758  * The `LuCI.Network.Hosts` class encapsulates host information aggregated
2759  * from multiple sources and provides convenience functions to access the
2760  * host information by different criteria.
2761  */
2762 Hosts = L.Class.extend(/** @lends LuCI.Network.Hosts.prototype */ {
2763         __init__: function(hosts) {
2764                 this.hosts = hosts;
2765         },
2766
2767         /**
2768          * Lookup the hostname associated with the given MAC address.
2769          *
2770          * @param {string} mac
2771          * The MAC address to lookup.
2772          *
2773          * @returns {null|string}
2774          * Returns the hostname associated with the given MAC or `null` if
2775          * no matching host could be found or if no hostname is known for
2776          * the corresponding host.
2777          */
2778         getHostnameByMACAddr: function(mac) {
2779                 return this.hosts[mac] ? this.hosts[mac].name : null;
2780         },
2781
2782         /**
2783          * Lookup the IPv4 address associated with the given MAC address.
2784          *
2785          * @param {string} mac
2786          * The MAC address to lookup.
2787          *
2788          * @returns {null|string}
2789          * Returns the IPv4 address associated with the given MAC or `null` if
2790          * no matching host could be found or if no IPv4 address is known for
2791          * the corresponding host.
2792          */
2793         getIPAddrByMACAddr: function(mac) {
2794                 return this.hosts[mac] ? this.hosts[mac].ipv4 : null;
2795         },
2796
2797         /**
2798          * Lookup the IPv6 address associated with the given MAC address.
2799          *
2800          * @param {string} mac
2801          * The MAC address to lookup.
2802          *
2803          * @returns {null|string}
2804          * Returns the IPv6 address associated with the given MAC or `null` if
2805          * no matching host could be found or if no IPv6 address is known for
2806          * the corresponding host.
2807          */
2808         getIP6AddrByMACAddr: function(mac) {
2809                 return this.hosts[mac] ? this.hosts[mac].ipv6 : null;
2810         },
2811
2812         /**
2813          * Lookup the hostname associated with the given IPv4 address.
2814          *
2815          * @param {string} ipaddr
2816          * The IPv4 address to lookup.
2817          *
2818          * @returns {null|string}
2819          * Returns the hostname associated with the given IPv4 or `null` if
2820          * no matching host could be found or if no hostname is known for
2821          * the corresponding host.
2822          */
2823         getHostnameByIPAddr: function(ipaddr) {
2824                 for (var mac in this.hosts)
2825                         if (this.hosts[mac].ipv4 == ipaddr &amp;&amp; this.hosts[mac].name != null)
2826                                 return this.hosts[mac].name;
2827                 return null;
2828         },
2829
2830         /**
2831          * Lookup the MAC address associated with the given IPv4 address.
2832          *
2833          * @param {string} ipaddr
2834          * The IPv4 address to lookup.
2835          *
2836          * @returns {null|string}
2837          * Returns the MAC address associated with the given IPv4 or `null` if
2838          * no matching host could be found or if no MAC address is known for
2839          * the corresponding host.
2840          */
2841         getMACAddrByIPAddr: function(ipaddr) {
2842                 for (var mac in this.hosts)
2843                         if (this.hosts[mac].ipv4 == ipaddr)
2844                                 return mac;
2845                 return null;
2846         },
2847
2848         /**
2849          * Lookup the hostname associated with the given IPv6 address.
2850          *
2851          * @param {string} ipaddr
2852          * The IPv6 address to lookup.
2853          *
2854          * @returns {null|string}
2855          * Returns the hostname associated with the given IPv6 or `null` if
2856          * no matching host could be found or if no hostname is known for
2857          * the corresponding host.
2858          */
2859         getHostnameByIP6Addr: function(ip6addr) {
2860                 for (var mac in this.hosts)
2861                         if (this.hosts[mac].ipv6 == ip6addr &amp;&amp; this.hosts[mac].name != null)
2862                                 return this.hosts[mac].name;
2863                 return null;
2864         },
2865
2866         /**
2867          * Lookup the MAC address associated with the given IPv6 address.
2868          *
2869          * @param {string} ipaddr
2870          * The IPv6 address to lookup.
2871          *
2872          * @returns {null|string}
2873          * Returns the MAC address associated with the given IPv6 or `null` if
2874          * no matching host could be found or if no MAC address is known for
2875          * the corresponding host.
2876          */
2877         getMACAddrByIP6Addr: function(ip6addr) {
2878                 for (var mac in this.hosts)
2879                         if (this.hosts[mac].ipv6 == ip6addr)
2880                                 return mac;
2881                 return null;
2882         },
2883
2884         /**
2885          * Return an array of (MAC address, name hint) tuples sorted by
2886          * MAC address.
2887          *
2888          * @param {boolean} [preferIp6=false]
2889          * Whether to prefer IPv6 addresses (`true`) or IPv4 addresses (`false`)
2890          * as name hint when no hostname is known for a specific MAC address.
2891          *
2892          * @returns {Array&lt;Array&lt;string>>}
2893          * Returns an array of arrays containing a name hint for each found
2894          * MAC address on the system. The array is sorted ascending by MAC.
2895          *
2896          * Each item of the resulting array is a two element array with the
2897          * MAC being the first element and the name hint being the second
2898          * element. The name hint is either the hostname, an IPv4 or an IPv6
2899          * address related to the MAC address.
2900          *
2901          * If no hostname but both IPv4 and IPv6 addresses are known, the
2902          * `preferIP6` flag specifies whether the IPv6 or the IPv4 address
2903          * is used as hint.
2904          */
2905         getMACHints: function(preferIp6) {
2906                 var rv = [];
2907                 for (var mac in this.hosts) {
2908                         var hint = this.hosts[mac].name ||
2909                                 this.hosts[mac][preferIp6 ? 'ipv6' : 'ipv4'] ||
2910                                 this.hosts[mac][preferIp6 ? 'ipv4' : 'ipv6'];
2911
2912                         rv.push([mac, hint]);
2913                 }
2914                 return rv.sort(function(a, b) { return a[0] > b[0] });
2915         }
2916 });
2917
2918 /**
2919  * @class
2920  * @memberof LuCI.Network
2921  * @hideconstructor
2922  * @classdesc
2923  *
2924  * The `Network.Protocol` class serves as base for protocol specific
2925  * subclasses which describe logical UCI networks defined by `config
2926  * interface` sections in `/etc/config/network`.
2927  */
2928 Protocol = L.Class.extend(/** @lends LuCI.Network.Protocol.prototype */ {
2929         __init__: function(name) {
2930                 this.sid = name;
2931         },
2932
2933         _get: function(opt) {
2934                 var val = uci.get('network', this.sid, opt);
2935
2936                 if (Array.isArray(val))
2937                         return val.join(' ');
2938
2939                 return val || '';
2940         },
2941
2942         _ubus: function(field) {
2943                 for (var i = 0; i &lt; _state.ifaces.length; i++) {
2944                         if (_state.ifaces[i].interface != this.sid)
2945                                 continue;
2946
2947                         return (field != null ? _state.ifaces[i][field] : _state.ifaces[i]);
2948                 }
2949         },
2950
2951         /**
2952          * Read the given UCI option value of this network.
2953          *
2954          * @param {string} opt
2955          * The UCI option name to read.
2956          *
2957          * @returns {null|string|string[]}
2958          * Returns the UCI option value or `null` if the requested option is
2959          * not found.
2960          */
2961         get: function(opt) {
2962                 return uci.get('network', this.sid, opt);
2963         },
2964
2965         /**
2966          * Set the given UCI option of this network to the given value.
2967          *
2968          * @param {string} opt
2969          * The name of the UCI option to set.
2970          *
2971          * @param {null|string|string[]} val
2972          * The value to set or `null` to remove the given option from the
2973          * configuration.
2974          */
2975         set: function(opt, val) {
2976                 return uci.set('network', this.sid, opt, val);
2977         },
2978
2979         /**
2980          * Get the associared Linux network device of this network.
2981          *
2982          * @returns {null|string}
2983          * Returns the name of the associated network device or `null` if
2984          * it could not be determined.
2985          */
2986         getIfname: function() {
2987                 var ifname;
2988
2989                 if (this.isFloating())
2990                         ifname = this._ubus('l3_device');
2991                 else
2992                         ifname = this._ubus('device') || this._ubus('l3_device');
2993
2994                 if (ifname != null)
2995                         return ifname;
2996
2997                 var res = getWifiNetidByNetname(this.sid);
2998                 return (res != null ? res[0] : null);
2999         },
3000
3001         /**
3002          * Get the name of this network protocol class.
3003          *
3004          * This function will be overwritten by subclasses created by
3005          * {@link LuCI.Network#registerProtocol Network.registerProtocol()}.
3006          *
3007          * @abstract
3008          * @returns {string}
3009          * Returns the name of the network protocol implementation, e.g.
3010          * `static` or `dhcp`.
3011          */
3012         getProtocol: function() {
3013                 return null;
3014         },
3015
3016         /**
3017          * Return a human readable description for the protcol, such as
3018          * `Static address` or `DHCP client`.
3019          *
3020          * This function should be overwritten by subclasses.
3021          *
3022          * @abstract
3023          * @returns {string}
3024          * Returns the description string.
3025          */
3026         getI18n: function() {
3027                 switch (this.getProtocol()) {
3028                 case 'none':   return _('Unmanaged');
3029                 case 'static': return _('Static address');
3030                 case 'dhcp':   return _('DHCP client');
3031                 default:       return _('Unknown');
3032                 }
3033         },
3034
3035         /**
3036          * Get the type of the underlying interface.
3037          *
3038          * This function actually is a convenience wrapper around
3039          * `proto.get("type")` and is mainly used by other `LuCI.Network` code
3040          * to check whether the interface is declared as bridge in UCI.
3041          *
3042          * @returns {null|string}
3043          * Returns the value of the `type` option of the associated logical
3044          * interface or `null` if no `type` option is set.
3045          */
3046         getType: function() {
3047                 return this._get('type');
3048         },
3049
3050         /**
3051          * Get the name of the associated logical interface.
3052          *
3053          * @returns {string}
3054          * Returns the logical interface name, such as `lan` or `wan`.
3055          */
3056         getName: function() {
3057                 return this.sid;
3058         },
3059
3060         /**
3061          * Get the uptime of the logical interface.
3062          *
3063          * @returns {number}
3064          * Returns the uptime of the associated interface in seconds.
3065          */
3066         getUptime: function() {
3067                 return this._ubus('uptime') || 0;
3068         },
3069
3070         /**
3071          * Get the logical interface expiry time in seconds.
3072          *
3073          * For protocols that have a concept of a lease, such as DHCP or
3074          * DHCPv6, this function returns the remaining time in seconds
3075          * until the lease expires.
3076          *
3077          * @returns {number}
3078          * Returns the amount of seconds until the lease expires or `-1`
3079          * if it isn't applicable to the associated protocol.
3080          */
3081         getExpiry: function() {
3082                 var u = this._ubus('uptime'),
3083                     d = this._ubus('data');
3084
3085                 if (typeof(u) == 'number' &amp;&amp; d != null &amp;&amp;
3086                     typeof(d) == 'object' &amp;&amp; typeof(d.leasetime) == 'number') {
3087                         var r = d.leasetime - (u % d.leasetime);
3088                         return (r > 0 ? r : 0);
3089                 }
3090
3091                 return -1;
3092         },
3093
3094         /**
3095          * Get the metric value of the logical interface.
3096          *
3097          * @returns {number}
3098          * Returns the current metric value used for device and network
3099          * routes spawned by the associated logical interface.
3100          */
3101         getMetric: function() {
3102                 return this._ubus('metric') || 0;
3103         },
3104
3105         /**
3106          * Get the requested firewall zone name of the logical interface.
3107          *
3108          * Some protocol implementations request a specific firewall zone
3109          * to trigger inclusion of their resulting network devices into the
3110          * firewall rule set.
3111          *
3112          * @returns {null|string}
3113          * Returns the requested firewall zone name as published in the
3114          * `ubus` runtime information or `null` if the remote protocol
3115          * handler didn't request a zone.
3116          */
3117         getZoneName: function() {
3118                 var d = this._ubus('data');
3119
3120                 if (L.isObject(d) &amp;&amp; typeof(d.zone) == 'string')
3121                         return d.zone;
3122
3123                 return null;
3124         },
3125
3126         /**
3127          * Query the first (primary) IPv4 address of the logical interface.
3128          *
3129          * @returns {null|string}
3130          * Returns the primary IPv4 address registered by the protocol handler
3131          * or `null` if no IPv4 addresses were set.
3132          */
3133         getIPAddr: function() {
3134                 var addrs = this._ubus('ipv4-address');
3135                 return ((Array.isArray(addrs) &amp;&amp; addrs.length) ? addrs[0].address : null);
3136         },
3137
3138         /**
3139          * Query all IPv4 addresses of the logical interface.
3140          *
3141          * @returns {string[]}
3142          * Returns an array of IPv4 addresses in CIDR notation which have been
3143          * registered by the protocol handler. The order of the resulting array
3144          * follows the order of the addresses in `ubus` runtime information.
3145          */
3146         getIPAddrs: function() {
3147                 var addrs = this._ubus('ipv4-address'),
3148                     rv = [];
3149
3150                 if (Array.isArray(addrs))
3151                         for (var i = 0; i &lt; addrs.length; i++)
3152                                 rv.push('%s/%d'.format(addrs[i].address, addrs[i].mask));
3153
3154                 return rv;
3155         },
3156
3157         /**
3158          * Query the first (primary) IPv4 netmask of the logical interface.
3159          *
3160          * @returns {null|string}
3161          * Returns the netmask of the primary IPv4 address registered by the
3162          * protocol handler or `null` if no IPv4 addresses were set.
3163          */
3164         getNetmask: function() {
3165                 var addrs = this._ubus('ipv4-address');
3166                 if (Array.isArray(addrs) &amp;&amp; addrs.length)
3167                         return prefixToMask(addrs[0].mask, false);
3168         },
3169
3170         /**
3171          * Query the gateway (nexthop) of the default route associated with
3172          * this logical interface.
3173          *
3174          * @returns {string}
3175          * Returns a string containing the IPv4 nexthop address of the associated
3176          * default route or `null` if no default route was found.
3177          */
3178         getGatewayAddr: function() {
3179                 var routes = this._ubus('route');
3180
3181                 if (Array.isArray(routes))
3182                         for (var i = 0; i &lt; routes.length; i++)
3183                                 if (typeof(routes[i]) == 'object' &amp;&amp;
3184                                     routes[i].target == '0.0.0.0' &amp;&amp;
3185                                     routes[i].mask == 0)
3186                                     return routes[i].nexthop;
3187
3188                 return null;
3189         },
3190
3191         /**
3192          * Query the IPv4 DNS servers associated with the logical interface.
3193          *
3194          * @returns {string[]}
3195          * Returns an array of IPv4 DNS servers registered by the remote
3196          * protocol backend.
3197          */
3198         getDNSAddrs: function() {
3199                 var addrs = this._ubus('dns-server'),
3200                     rv = [];
3201
3202                 if (Array.isArray(addrs))
3203                         for (var i = 0; i &lt; addrs.length; i++)
3204                                 if (!/:/.test(addrs[i]))
3205                                         rv.push(addrs[i]);
3206
3207                 return rv;
3208         },
3209
3210         /**
3211          * Query the first (primary) IPv6 address of the logical interface.
3212          *
3213          * @returns {null|string}
3214          * Returns the primary IPv6 address registered by the protocol handler
3215          * in CIDR notation or `null` if no IPv6 addresses were set.
3216          */
3217         getIP6Addr: function() {
3218                 var addrs = this._ubus('ipv6-address');
3219
3220                 if (Array.isArray(addrs) &amp;&amp; L.isObject(addrs[0]))
3221                         return '%s/%d'.format(addrs[0].address, addrs[0].mask);
3222
3223                 addrs = this._ubus('ipv6-prefix-assignment');
3224
3225                 if (Array.isArray(addrs) &amp;&amp; L.isObject(addrs[0]) &amp;&amp; L.isObject(addrs[0]['local-address']))
3226                         return '%s/%d'.format(addrs[0]['local-address'].address, addrs[0]['local-address'].mask);
3227
3228                 return null;
3229         },
3230
3231         /**
3232          * Query all IPv6 addresses of the logical interface.
3233          *
3234          * @returns {string[]}
3235          * Returns an array of IPv6 addresses in CIDR notation which have been
3236          * registered by the protocol handler. The order of the resulting array
3237          * follows the order of the addresses in `ubus` runtime information.
3238          */
3239         getIP6Addrs: function() {
3240                 var addrs = this._ubus('ipv6-address'),
3241                     rv = [];
3242
3243                 if (Array.isArray(addrs))
3244                         for (var i = 0; i &lt; addrs.length; i++)
3245                                 if (L.isObject(addrs[i]))
3246                                         rv.push('%s/%d'.format(addrs[i].address, addrs[i].mask));
3247
3248                 addrs = this._ubus('ipv6-prefix-assignment');
3249
3250                 if (Array.isArray(addrs))
3251                         for (var i = 0; i &lt; addrs.length; i++)
3252                                 if (L.isObject(addrs[i]) &amp;&amp; L.isObject(addrs[i]['local-address']))
3253                                         rv.push('%s/%d'.format(addrs[i]['local-address'].address, addrs[i]['local-address'].mask));
3254
3255                 return rv;
3256         },
3257
3258         /**
3259          * Query the gateway (nexthop) of the IPv6 default route associated with
3260          * this logical interface.
3261          *
3262          * @returns {string}
3263          * Returns a string containing the IPv6 nexthop address of the associated
3264          * default route or `null` if no default route was found.
3265          */
3266         getGateway6Addr: function() {
3267                 var routes = this._ubus('route');
3268
3269                 if (Array.isArray(routes))
3270                         for (var i = 0; i &lt; routes.length; i++)
3271                                 if (typeof(routes[i]) == 'object' &amp;&amp;
3272                                     routes[i].target == '::' &amp;&amp;
3273                                     routes[i].mask == 0)
3274                                     return routes[i].nexthop;
3275
3276                 return null;
3277         },
3278
3279         /**
3280          * Query the IPv6 DNS servers associated with the logical interface.
3281          *
3282          * @returns {string[]}
3283          * Returns an array of IPv6 DNS servers registered by the remote
3284          * protocol backend.
3285          */
3286         getDNS6Addrs: function() {
3287                 var addrs = this._ubus('dns-server'),
3288                     rv = [];
3289
3290                 if (Array.isArray(addrs))
3291                         for (var i = 0; i &lt; addrs.length; i++)
3292                                 if (/:/.test(addrs[i]))
3293                                         rv.push(addrs[i]);
3294
3295                 return rv;
3296         },
3297
3298         /**
3299          * Query the routed IPv6 prefix associated with the logical interface.
3300          *
3301          * @returns {null|string}
3302          * Returns the routed IPv6 prefix registered by the remote protocol
3303          * handler or `null` if no prefix is present.
3304          */
3305         getIP6Prefix: function() {
3306                 var prefixes = this._ubus('ipv6-prefix');
3307
3308                 if (Array.isArray(prefixes) &amp;&amp; L.isObject(prefixes[0]))
3309                         return '%s/%d'.format(prefixes[0].address, prefixes[0].mask);
3310
3311                 return null;
3312         },
3313
3314         /**
3315          * Query interface error messages published in `ubus` runtime state.
3316          *
3317          * Interface errors are emitted by remote protocol handlers if the setup
3318          * of the underlying logical interface failed, e.g. due to bad
3319          * configuration or network connectivity issues.
3320          *
3321          * This function will translate the found error codes to human readable
3322          * messages using the descriptions registered by
3323          * {@link LuCI.Network#registerErrorCode Network.registerErrorCode()}
3324          * and fall back to `"Unknown error (%s)"` where `%s` is replaced by the
3325          * error code in case no translation can be found.
3326          *
3327          * @returns {string[]}
3328          * Returns an array of translated interface error messages.
3329          */
3330         getErrors: function() {
3331                 var errors = this._ubus('errors'),
3332                     rv = null;
3333
3334                 if (Array.isArray(errors)) {
3335                         for (var i = 0; i &lt; errors.length; i++) {
3336                                 if (!L.isObject(errors[i]) || typeof(errors[i].code) != 'string')
3337                                         continue;
3338
3339                                 rv = rv || [];
3340                                 rv.push(proto_errors[errors[i].code] || _('Unknown error (%s)').format(errors[i].code));
3341                         }
3342                 }
3343
3344                 return rv;
3345         },
3346
3347         /**
3348          * Checks whether the underlying logical interface is declared as bridge.
3349          *
3350          * @returns {boolean}
3351          * Returns `true` when the interface is declared with `option type bridge`
3352          * and when the associated protocol implementation is not marked virtual
3353          * or `false` when the logical interface is no bridge.
3354          */
3355         isBridge: function() {
3356                 return (!this.isVirtual() &amp;&amp; this.getType() == 'bridge');
3357         },
3358
3359         /**
3360          * Get the name of the opkg package providing the protocol functionality.
3361          *
3362          * This function should be overwritten by protocol specific subclasses.
3363          *
3364          * @abstract
3365          *
3366          * @returns {string}
3367          * Returns the name of the opkg package required for the protocol to
3368          * function, e.g. `odhcp6c` for the `dhcpv6` prototocol.
3369          */
3370         getOpkgPackage: function() {
3371                 return null;
3372         },
3373
3374         /**
3375          * Checks whether the protocol functionality is installed.
3376          *
3377          * This function exists for compatibility with old code, it always
3378          * returns `true`.
3379          *
3380          * @deprecated
3381          * @abstract
3382          *
3383          * @returns {boolean}
3384          * Returns `true` if the protocol support is installed, else `false`.
3385          */
3386         isInstalled: function() {
3387                 return true;
3388         },
3389
3390         /**
3391          * Checks whether this protocol is "virtual".
3392          *
3393          * A "virtual" protocol is a protocol which spawns its own interfaces
3394          * on demand instead of using existing physical interfaces.
3395          *
3396          * Examples for virtual protocols are `6in4` which `gre` spawn tunnel
3397          * network device on startup, examples for non-virtual protcols are
3398          * `dhcp` or `static` which apply IP configuration to existing interfaces.
3399          *
3400          * This function should be overwritten by subclasses.
3401          *
3402          * @returns {boolean}
3403          * Returns a boolean indicating whether the underlying protocol spawns
3404          * dynamic interfaces (`true`) or not (`false`).
3405          */
3406         isVirtual: function() {
3407                 return false;
3408         },
3409
3410         /**
3411          * Checks whether this protocol is "floating".
3412          *
3413          * A "floating" protocol is a protocol which spawns its own interfaces
3414          * on demand, like a virtual one but which relies on an existinf lower
3415          * level interface to initiate the connection.
3416          *
3417          * An example for such a protocol is "pppoe".
3418          *
3419          * This function exists for backwards compatibility with older code
3420          * but should not be used anymore.
3421          *
3422          * @deprecated
3423          * @returns {boolean}
3424          * Returns a boolean indicating whether this protocol is floating (`true`)
3425          * or not (`false`).
3426          */
3427         isFloating: function() {
3428                 return false;
3429         },
3430
3431         /**
3432          * Checks whether this logical interface is dynamic.
3433          *
3434          * A dynamic interface is an interface which has been created at runtime,
3435          * e.g. as sub-interface of another interface, but which is not backed by
3436          * any user configuration. Such dynamic interfaces cannot be edited but
3437          * only brought down or restarted.
3438          *
3439          * @returns {boolean}
3440          * Returns a boolean indicating whether this interface is dynamic (`true`)
3441          * or not (`false`).
3442          */
3443         isDynamic: function() {
3444                 return (this._ubus('dynamic') == true);
3445         },
3446
3447         /**
3448          * Checks whether this interface is an alias interface.
3449          *
3450          * Alias interfaces are interfaces layering on top of another interface
3451          * and are denoted by a special `@interfacename` notation in the
3452          * underlying `ifname` option.
3453          *
3454          * @returns {null|string}
3455          * Returns the name of the parent interface if this logical interface
3456          * is an alias or `null` if it is not an alias interface.
3457          */
3458         isAlias: function() {
3459                 var ifnames = L.toArray(uci.get('network', this.sid, 'ifname')),
3460                     parent = null;
3461
3462                 for (var i = 0; i &lt; ifnames.length; i++)
3463                         if (ifnames[i].charAt(0) == '@')
3464                                 parent = ifnames[i].substr(1);
3465                         else if (parent != null)
3466                                 parent = null;
3467
3468                 return parent;
3469         },
3470
3471         /**
3472          * Checks whether this logical interface is "empty", meaning that ut
3473          * has no network devices attached.
3474          *
3475          * @returns {boolean}
3476          * Returns `true` if this logical interface is empty, else `false`.
3477          */
3478         isEmpty: function() {
3479                 if (this.isFloating())
3480                         return false;
3481
3482                 var empty = true,
3483                     ifname = this._get('ifname');
3484
3485                 if (ifname != null &amp;&amp; ifname.match(/\S+/))
3486                         empty = false;
3487
3488                 if (empty == true &amp;&amp; getWifiNetidBySid(this.sid) != null)
3489                         empty = false;
3490
3491                 return empty;
3492         },
3493
3494         /**
3495          * Checks whether this logical interface is configured and running.
3496          *
3497          * @returns {boolean}
3498          * Returns `true` when the interface is active or `false` when it is not.
3499          */
3500         isUp: function() {
3501                 return (this._ubus('up') == true);
3502         },
3503
3504         /**
3505          * Add the given network device to the logical interface.
3506          *
3507          * @param {LuCI.Network.Protocol|LuCI.Network.Device|LuCI.Network.WifiDevice|LuCI.Network.WifiNetwork|string} device
3508          * The object or device name to add to the logical interface. In case the
3509          * given argument is not a string, it is resolved though the
3510          * {@link LuCI.Network#getIfnameOf Network.getIfnameOf()} function.
3511          *
3512          * @returns {boolean}
3513          * Returns `true` if the device name has been added or `false` if any
3514          * argument was invalid, if the device was already part of the logical
3515          * interface or if the logical interface is virtual.
3516          */
3517         addDevice: function(ifname) {
3518                 ifname = ifnameOf(ifname);
3519
3520                 if (ifname == null || this.isFloating())
3521                         return false;
3522
3523                 var wif = getWifiSidByIfname(ifname);
3524
3525                 if (wif != null)
3526                         return appendValue('wireless', wif, 'network', this.sid);
3527
3528                 return appendValue('network', this.sid, 'ifname', ifname);
3529         },
3530
3531         /**
3532          * Remove the given network device from the logical interface.
3533          *
3534          * @param {LuCI.Network.Protocol|LuCI.Network.Device|LuCI.Network.WifiDevice|LuCI.Network.WifiNetwork|string} device
3535          * The object or device name to remove from the logical interface. In case
3536          * the given argument is not a string, it is resolved though the
3537          * {@link LuCI.Network#getIfnameOf Network.getIfnameOf()} function.
3538          *
3539          * @returns {boolean}
3540          * Returns `true` if the device name has been added or `false` if any
3541          * argument was invalid, if the device was already part of the logical
3542          * interface or if the logical interface is virtual.
3543          */
3544         deleteDevice: function(ifname) {
3545                 var rv = false;
3546
3547                 ifname = ifnameOf(ifname);
3548
3549                 if (ifname == null || this.isFloating())
3550                         return false;
3551
3552                 var wif = getWifiSidByIfname(ifname);
3553
3554                 if (wif != null)
3555                         rv = removeValue('wireless', wif, 'network', this.sid);
3556
3557                 if (removeValue('network', this.sid, 'ifname', ifname))
3558                         rv = true;
3559
3560                 return rv;
3561         },
3562
3563         /**
3564          * Returns the Linux network device associated with this logical
3565          * interface.
3566          *
3567          * @returns {LuCI.Network.Device}
3568          * Returns a `Network.Device` class instance representing the
3569          * expected Linux network device according to the configuration.
3570          */
3571         getDevice: function() {
3572                 if (this.isVirtual()) {
3573                         var ifname = '%s-%s'.format(this.getProtocol(), this.sid);
3574                         _state.isTunnel[this.getProtocol() + '-' + this.sid] = true;
3575                         return L.network.instantiateDevice(ifname, this);
3576                 }
3577                 else if (this.isBridge()) {
3578                         var ifname = 'br-%s'.format(this.sid);
3579                         _state.isBridge[ifname] = true;
3580                         return new Device(ifname, this);
3581                 }
3582                 else {
3583                         var ifnames = L.toArray(uci.get('network', this.sid, 'ifname'));
3584
3585                         for (var i = 0; i &lt; ifnames.length; i++) {
3586                                 var m = ifnames[i].match(/^([^:/]+)/);
3587                                 return ((m &amp;&amp; m[1]) ? L.network.instantiateDevice(m[1], this) : null);
3588                         }
3589
3590                         ifname = getWifiNetidByNetname(this.sid);
3591
3592                         return (ifname != null ? L.network.instantiateDevice(ifname[0], this) : null);
3593                 }
3594         },
3595
3596         /**
3597          * Returns the layer 2 linux network device currently associated
3598          * with this logical interface.
3599          *
3600          * @returns {LuCI.Network.Device}
3601          * Returns a `Network.Device` class instance representing the Linux
3602          * network device currently associated with the logical interface.
3603          */
3604         getL2Device: function() {
3605                 var ifname = this._ubus('device');
3606                 return (ifname != null ? L.network.instantiateDevice(ifname, this) : null);
3607         },
3608
3609         /**
3610          * Returns the layer 3 linux network device currently associated
3611          * with this logical interface.
3612          *
3613          * @returns {LuCI.Network.Device}
3614          * Returns a `Network.Device` class instance representing the Linux
3615          * network device currently associated with the logical interface.
3616          */
3617         getL3Device: function() {
3618                 var ifname = this._ubus('l3_device');
3619                 return (ifname != null ? L.network.instantiateDevice(ifname, this) : null);
3620         },
3621
3622         /**
3623          * Returns a list of network sub-devices associated with this logical
3624          * interface.
3625          *
3626          * @returns {null|Array&lt;LuCI.Network.Device>}
3627          * Returns an array of of `Network.Device` class instances representing
3628          * the sub-devices attached to this logical interface or `null` if the
3629          * logical interface does not support sub-devices, e.g. because it is
3630          * virtual and not a bridge.
3631          */
3632         getDevices: function() {
3633                 var rv = [];
3634
3635                 if (!this.isBridge() &amp;&amp; !(this.isVirtual() &amp;&amp; !this.isFloating()))
3636                         return null;
3637
3638                 var ifnames = L.toArray(uci.get('network', this.sid, 'ifname'));
3639
3640                 for (var i = 0; i &lt; ifnames.length; i++) {
3641                         if (ifnames[i].charAt(0) == '@')
3642                                 continue;
3643
3644                         var m = ifnames[i].match(/^([^:/]+)/);
3645                         if (m != null)
3646                                 rv.push(L.network.instantiateDevice(m[1], this));
3647                 }
3648
3649                 var uciWifiIfaces = uci.sections('wireless', 'wifi-iface');
3650
3651                 for (var i = 0; i &lt; uciWifiIfaces.length; i++) {
3652                         if (typeof(uciWifiIfaces[i].device) != 'string')
3653                                 continue;
3654
3655                         var networks = L.toArray(uciWifiIfaces[i].network);
3656
3657                         for (var j = 0; j &lt; networks.length; j++) {
3658                                 if (networks[j] != this.sid)
3659                                         continue;
3660
3661                                 var netid = getWifiNetidBySid(uciWifiIfaces[i]['.name']);
3662
3663                                 if (netid != null)
3664                                         rv.push(L.network.instantiateDevice(netid[0], this));
3665                         }
3666                 }
3667
3668                 rv.sort(deviceSort);
3669
3670                 return rv;
3671         },
3672
3673         /**
3674          * Checks whether this logical interface contains the given device
3675          * object.
3676          *
3677          * @param {LuCI.Network.Protocol|LuCI.Network.Device|LuCI.Network.WifiDevice|LuCI.Network.WifiNetwork|string} device
3678          * The object or device name to check. In case the given argument is not
3679          * a string, it is resolved though the
3680          * {@link LuCI.Network#getIfnameOf Network.getIfnameOf()} function.
3681          *
3682          * @returns {boolean}
3683          * Returns `true` when this logical interface contains the given network
3684          * device or `false` if not.
3685          */
3686         containsDevice: function(ifname) {
3687                 ifname = ifnameOf(ifname);
3688
3689                 if (ifname == null)
3690                         return false;
3691                 else if (this.isVirtual() &amp;&amp; '%s-%s'.format(this.getProtocol(), this.sid) == ifname)
3692                         return true;
3693                 else if (this.isBridge() &amp;&amp; 'br-%s'.format(this.sid) == ifname)
3694                         return true;
3695
3696                 var ifnames = L.toArray(uci.get('network', this.sid, 'ifname'));
3697
3698                 for (var i = 0; i &lt; ifnames.length; i++) {
3699                         var m = ifnames[i].match(/^([^:/]+)/);
3700                         if (m != null &amp;&amp; m[1] == ifname)
3701                                 return true;
3702                 }
3703
3704                 var wif = getWifiSidByIfname(ifname);
3705
3706                 if (wif != null) {
3707                         var networks = L.toArray(uci.get('wireless', wif, 'network'));
3708
3709                         for (var i = 0; i &lt; networks.length; i++)
3710                                 if (networks[i] == this.sid)
3711                                         return true;
3712                 }
3713
3714                 return false;
3715         }
3716 });
3717
3718 /**
3719  * @class
3720  * @memberof LuCI.Network
3721  * @hideconstructor
3722  * @classdesc
3723  *
3724  * A `Network.Device` class instance represents an underlying Linux network
3725  * device and allows querying device details such as packet statistics or MTU.
3726  */
3727 Device = L.Class.extend(/** @lends LuCI.Network.Device.prototype */ {
3728         __init__: function(ifname, network) {
3729                 var wif = getWifiSidByIfname(ifname);
3730
3731                 if (wif != null) {
3732                         var res = getWifiStateBySid(wif) || [],
3733                             netid = getWifiNetidBySid(wif) || [];
3734
3735                         this.wif    = new WifiNetwork(wif, res[0], res[1], netid[0], res[2], { ifname: ifname });
3736                         this.ifname = this.wif.getIfname();
3737                 }
3738
3739                 this.ifname  = this.ifname || ifname;
3740                 this.dev     = _state.netdevs[this.ifname];
3741                 this.network = network;
3742         },
3743
3744         _devstate: function(/* ... */) {
3745                 var rv = this.dev;
3746
3747                 for (var i = 0; i &lt; arguments.length; i++)
3748                         if (L.isObject(rv))
3749                                 rv = rv[arguments[i]];
3750                         else
3751                                 return null;
3752
3753                 return rv;
3754         },
3755
3756         /**
3757          * Get the name of the network device.
3758          *
3759          * @returns {string}
3760          * Returns the name of the device, e.g. `eth0` or `wlan0`.
3761          */
3762         getName: function() {
3763                 return (this.wif != null ? this.wif.getIfname() : this.ifname);
3764         },
3765
3766         /**
3767          * Get the MAC address of the device.
3768          *
3769          * @returns {null|string}
3770          * Returns the MAC address of the device or `null` if not applicable,
3771          * e.g. for non-ethernet tunnel devices.
3772          */
3773         getMAC: function() {
3774                 var mac = this._devstate('macaddr');
3775                 return mac ? mac.toUpperCase() : null;
3776         },
3777
3778         /**
3779          * Get the MTU of the device.
3780          *
3781          * @returns {number}
3782          * Returns the MTU of the device.
3783          */
3784         getMTU: function() {
3785                 return this._devstate('mtu');
3786         },
3787
3788         /**
3789          * Get the IPv4 addresses configured on the device.
3790          *
3791          * @returns {string[]}
3792          * Returns an array of IPv4 address strings.
3793          */
3794         getIPAddrs: function() {
3795                 var addrs = this._devstate('ipaddrs');
3796                 return (Array.isArray(addrs) ? addrs : []);
3797         },
3798
3799         /**
3800          * Get the IPv6 addresses configured on the device.
3801          *
3802          * @returns {string[]}
3803          * Returns an array of IPv6 address strings.
3804          */
3805         getIP6Addrs: function() {
3806                 var addrs = this._devstate('ip6addrs');
3807                 return (Array.isArray(addrs) ? addrs : []);
3808         },
3809
3810         /**
3811          * Get the type of the device..
3812          *
3813          * @returns {string}
3814          * Returns a string describing the type of the network device:
3815          *  - `alias` if it is an abstract alias device (`@` notation)
3816          *  - `wifi` if it is a wireless interface (e.g. `wlan0`)
3817          *  - `bridge` if it is a bridge device (e.g. `br-lan`)
3818          *  - `tunnel` if it is a tun or tap device (e.g. `tun0`)
3819          *  - `vlan` if it is a vlan device (e.g. `eth0.1`)
3820          *  - `switch` if it is a switch device (e.g.`eth1` connected to switch0)
3821          *  - `ethernet` for all other device types
3822          */
3823         getType: function() {
3824                 if (this.ifname != null &amp;&amp; this.ifname.charAt(0) == '@')
3825                         return 'alias';
3826                 else if (this.wif != null || isWifiIfname(this.ifname))
3827                         return 'wifi';
3828                 else if (_state.isBridge[this.ifname])
3829                         return 'bridge';
3830                 else if (_state.isTunnel[this.ifname])
3831                         return 'tunnel';
3832                 else if (this.ifname.indexOf('.') > -1)
3833                         return 'vlan';
3834                 else if (_state.isSwitch[this.ifname])
3835                         return 'switch';
3836                 else
3837                         return 'ethernet';
3838         },
3839
3840         /**
3841          * Get a short description string for the device.
3842          *
3843          * @returns {string}
3844          * Returns the device name for non-wifi devices or a string containing
3845          * the operation mode and SSID for wifi devices.
3846          */
3847         getShortName: function() {
3848                 if (this.wif != null)
3849                         return this.wif.getShortName();
3850
3851                 return this.ifname;
3852         },
3853
3854         /**
3855          * Get a long description string for the device.
3856          *
3857          * @returns {string}
3858          * Returns a string containing the type description and device name
3859          * for non-wifi devices or operation mode and ssid for wifi ones.
3860          */
3861         getI18n: function() {
3862                 if (this.wif != null) {
3863                         return '%s: %s "%s"'.format(
3864                                 _('Wireless Network'),
3865                                 this.wif.getActiveMode(),
3866                                 this.wif.getActiveSSID() || this.wif.getActiveBSSID() || this.wif.getID() || '?');
3867                 }
3868
3869                 return '%s: "%s"'.format(this.getTypeI18n(), this.getName());
3870         },
3871
3872         /**
3873          * Get a string describing the device type.
3874          *
3875          * @returns {string}
3876          * Returns a string describing the type, e.g. "Wireless Adapter" or
3877          * "Bridge".
3878          */
3879         getTypeI18n: function() {
3880                 switch (this.getType()) {
3881                 case 'alias':
3882                         return _('Alias Interface');
3883
3884                 case 'wifi':
3885                         return _('Wireless Adapter');
3886
3887                 case 'bridge':
3888                         return _('Bridge');
3889
3890                 case 'switch':
3891                         return _('Ethernet Switch');
3892
3893                 case 'vlan':
3894                         return (_state.isSwitch[this.ifname] ? _('Switch VLAN') : _('Software VLAN'));
3895
3896                 case 'tunnel':
3897                         return _('Tunnel Interface');
3898
3899                 default:
3900                         return _('Ethernet Adapter');
3901                 }
3902         },
3903
3904         /**
3905          * Get the associated bridge ports of the device.
3906          *
3907          * @returns {null|Array&lt;LuCI.Network.Device>}
3908          * Returns an array of `Network.Device` instances representing the ports
3909          * (slave interfaces) of the bridge or `null` when this device isn't
3910          * a Linux bridge.
3911          */
3912         getPorts: function() {
3913                 var br = _state.bridges[this.ifname],
3914                     rv = [];
3915
3916                 if (br == null || !Array.isArray(br.ifnames))
3917                         return null;
3918
3919                 for (var i = 0; i &lt; br.ifnames.length; i++)
3920                         rv.push(L.network.instantiateDevice(br.ifnames[i].name));
3921
3922                 rv.sort(deviceSort);
3923
3924                 return rv;
3925         },
3926
3927         /**
3928          * Get the bridge ID
3929          *
3930          * @returns {null|string}
3931          * Returns the ID of this network bridge or `null` if this network
3932          * device is not a Linux bridge.
3933          */
3934         getBridgeID: function() {
3935                 var br = _state.bridges[this.ifname];
3936                 return (br != null ? br.id : null);
3937         },
3938
3939         /**
3940          * Get the bridge STP setting
3941          *
3942          * @returns {boolean}
3943          * Returns `true` when this device is a Linux bridge and has `stp`
3944          * enabled, else `false`.
3945          */
3946         getBridgeSTP: function() {
3947                 var br = _state.bridges[this.ifname];
3948                 return (br != null ? !!br.stp : false);
3949         },
3950
3951         /**
3952          * Checks whether this device is up.
3953          *
3954          * @returns {boolean}
3955          * Returns `true` when the associated device is running pr `false`
3956          * when it is down or absent.
3957          */
3958         isUp: function() {
3959                 var up = this._devstate('flags', 'up');
3960
3961                 if (up == null)
3962                         up = (this.getType() == 'alias');
3963
3964                 return up;
3965         },
3966
3967         /**
3968          * Checks whether this device is a Linux bridge.
3969          *
3970          * @returns {boolean}
3971          * Returns `true` when the network device is present and a Linux bridge,
3972          * else `false`.
3973          */
3974         isBridge: function() {
3975                 return (this.getType() == 'bridge');
3976         },
3977
3978         /**
3979          * Checks whether this device is part of a Linux bridge.
3980          *
3981          * @returns {boolean}
3982          * Returns `true` when this network device is part of a bridge,
3983          * else `false`.
3984          */
3985         isBridgePort: function() {
3986                 return (this._devstate('bridge') != null);
3987         },
3988
3989         /**
3990          * Get the amount of transmitted bytes.
3991          *
3992          * @returns {number}
3993          * Returns the amount of bytes transmitted by the network device.
3994          */
3995         getTXBytes: function() {
3996                 var stat = this._devstate('stats');
3997                 return (stat != null ? stat.tx_bytes || 0 : 0);
3998         },
3999
4000         /**
4001          * Get the amount of received bytes.
4002          *
4003          * @returns {number}
4004          * Returns the amount of bytes received by the network device.
4005          */
4006         getRXBytes: function() {
4007                 var stat = this._devstate('stats');
4008                 return (stat != null ? stat.rx_bytes || 0 : 0);
4009         },
4010
4011         /**
4012          * Get the amount of transmitted packets.
4013          *
4014          * @returns {number}
4015          * Returns the amount of packets transmitted by the network device.
4016          */
4017         getTXPackets: function() {
4018                 var stat = this._devstate('stats');
4019                 return (stat != null ? stat.tx_packets || 0 : 0);
4020         },
4021
4022         /**
4023          * Get the amount of received packets.
4024          *
4025          * @returns {number}
4026          * Returns the amount of packets received by the network device.
4027          */
4028         getRXPackets: function() {
4029                 var stat = this._devstate('stats');
4030                 return (stat != null ? stat.rx_packets || 0 : 0);
4031         },
4032
4033         /**
4034          * Get the primary logical interface this device is assigned to.
4035          *
4036          * @returns {null|LuCI.Network.Protocol}
4037          * Returns a `Network.Protocol` instance representing the logical
4038          * interface this device is attached to or `null` if it is not
4039          * assigned to any logical interface.
4040          */
4041         getNetwork: function() {
4042                 return this.getNetworks()[0];
4043         },
4044
4045         /**
4046          * Get the logical interfaces this device is assigned to.
4047          *
4048          * @returns {Array&lt;LuCI.Network.Protocol>}
4049          * Returns an array of `Network.Protocol` instances representing the
4050          * logical interfaces this device is assigned to.
4051          */
4052         getNetworks: function() {
4053                 if (this.networks == null) {
4054                         this.networks = [];
4055
4056                         var networks = enumerateNetworks.apply(L.network);
4057
4058                         for (var i = 0; i &lt; networks.length; i++)
4059                                 if (networks[i].containsDevice(this.ifname) || networks[i].getIfname() == this.ifname)
4060                                         this.networks.push(networks[i]);
4061
4062                         this.networks.sort(networkSort);
4063                 }
4064
4065                 return this.networks;
4066         },
4067
4068         /**
4069          * Get the related wireless network this device is related to.
4070          *
4071          * @returns {null|LuCI.Network.WifiNetwork}
4072          * Returns a `Network.WifiNetwork` instance representing the wireless
4073          * network corresponding to this network device or `null` if this device
4074          * is not a wireless device.
4075          */
4076         getWifiNetwork: function() {
4077                 return (this.wif != null ? this.wif : null);
4078         }
4079 });
4080
4081 /**
4082  * @class
4083  * @memberof LuCI.Network
4084  * @hideconstructor
4085  * @classdesc
4086  *
4087  * A `Network.WifiDevice` class instance represents a wireless radio device
4088  * present on the system and provides wireless capability information as
4089  * well as methods for enumerating related wireless networks.
4090  */
4091 WifiDevice = L.Class.extend(/** @lends LuCI.Network.WifiDevice.prototype */ {
4092         __init__: function(name, radiostate) {
4093                 var uciWifiDevice = uci.get('wireless', name);
4094
4095                 if (uciWifiDevice != null &amp;&amp;
4096                     uciWifiDevice['.type'] == 'wifi-device' &amp;&amp;
4097                     uciWifiDevice['.name'] != null) {
4098                         this.sid    = uciWifiDevice['.name'];
4099                 }
4100
4101                 this.sid    = this.sid || name;
4102                 this._ubusdata = {
4103                         radio: name,
4104                         dev:   radiostate
4105                 };
4106         },
4107
4108         /* private */
4109         ubus: function(/* ... */) {
4110                 var v = this._ubusdata;
4111
4112                 for (var i = 0; i &lt; arguments.length; i++)
4113                         if (L.isObject(v))
4114                                 v = v[arguments[i]];
4115                         else
4116                                 return null;
4117
4118                 return v;
4119         },
4120
4121         /**
4122          * Read the given UCI option value of this wireless device.
4123          *
4124          * @param {string} opt
4125          * The UCI option name to read.
4126          *
4127          * @returns {null|string|string[]}
4128          * Returns the UCI option value or `null` if the requested option is
4129          * not found.
4130          */
4131         get: function(opt) {
4132                 return uci.get('wireless', this.sid, opt);
4133         },
4134
4135         /**
4136          * Set the given UCI option of this network to the given value.
4137          *
4138          * @param {string} opt
4139          * The name of the UCI option to set.
4140          *
4141          * @param {null|string|string[]} val
4142          * The value to set or `null` to remove the given option from the
4143          * configuration.
4144          */
4145         set: function(opt, value) {
4146                 return uci.set('wireless', this.sid, opt, value);
4147         },
4148
4149         /**
4150          * Checks whether this wireless radio is disabled.
4151          *
4152          * @returns {boolean}
4153          * Returns `true` when the wireless radio is marked as disabled in `ubus`
4154          * runtime state or when the `disabled` option is set in the corresponding
4155          * UCI configuration.
4156          */
4157         isDisabled: function() {
4158                 return this.ubus('dev', 'disabled') || this.get('disabled') == '1';
4159         },
4160
4161         /**
4162          * Get the configuration name of this wireless radio.
4163          *
4164          * @returns {string}
4165          * Returns the UCI section name (e.g. `radio0`) of the corresponding
4166          * radio configuration which also serves as unique logical identifier
4167          * for the wireless phy.
4168          */
4169         getName: function() {
4170                 return this.sid;
4171         },
4172
4173         /**
4174          * Gets a list of supported hwmodes.
4175          *
4176          * The hwmode values describe the frequency band and wireless standard
4177          * versions supported by the wireless phy.
4178          *
4179          * @returns {string[]}
4180          * Returns an array of valid hwmode values for this radio. Currently
4181          * known mode values are:
4182          *  - `a` - Legacy 802.11a mode, 5 GHz, up to 54 Mbit/s
4183          *  - `b` - Legacy 802.11b mode, 2.4 GHz, up to 11 Mbit/s
4184          *  - `g` - Legacy 802.11g mode, 2.4 GHz, up to 54 Mbit/s
4185          *  - `n` - IEEE 802.11n mode, 2.4 or 5 GHz, up to 600 Mbit/s
4186          *  - `ac` - IEEE 802.11ac mode, 5 GHz, up to 6770 Mbit/s
4187          */
4188         getHWModes: function() {
4189                 var hwmodes = this.ubus('dev', 'iwinfo', 'hwmodes');
4190                 return Array.isArray(hwmodes) ? hwmodes : [ 'b', 'g' ];
4191         },
4192
4193         /**
4194          * Gets a list of supported htmodes.
4195          *
4196          * The htmode values describe the wide-frequency options supported by
4197          * the wireless phy.
4198          *
4199          * @returns {string[]}
4200          * Returns an array of valid htmode values for this radio. Currently
4201          * known mode values are:
4202          *  - `HT20` - applicable to IEEE 802.11n, 20 MHz wide channels
4203          *  - `HT40` - applicable to IEEE 802.11n, 40 MHz wide channels
4204          *  - `VHT20` - applicable to IEEE 802.11ac, 20 MHz wide channels
4205          *  - `VHT40` - applicable to IEEE 802.11ac, 40 MHz wide channels
4206          *  - `VHT80` - applicable to IEEE 802.11ac, 80 MHz wide channels
4207          *  - `VHT160` - applicable to IEEE 802.11ac, 160 MHz wide channels
4208          */
4209         getHTModes: function() {
4210                 var htmodes = this.ubus('dev', 'iwinfo', 'htmodes');
4211                 return (Array.isArray(htmodes) &amp;&amp; htmodes.length) ? htmodes : null;
4212         },
4213
4214         /**
4215          * Get a string describing the wireless radio hardware.
4216          *
4217          * @returns {string}
4218          * Returns the description string.
4219          */
4220         getI18n: function() {
4221                 var hw = this.ubus('dev', 'iwinfo', 'hardware'),
4222                     type = L.isObject(hw) ? hw.name : null;
4223
4224                 if (this.ubus('dev', 'iwinfo', 'type') == 'wl')
4225                         type = 'Broadcom';
4226
4227                 var hwmodes = this.getHWModes(),
4228                     modestr = '';
4229
4230                 hwmodes.sort(function(a, b) {
4231                         return (a.length != b.length ? a.length > b.length : a > b);
4232                 });
4233
4234                 modestr = hwmodes.join('');
4235
4236                 return '%s 802.11%s Wireless Controller (%s)'.format(type || 'Generic', modestr, this.getName());
4237         },
4238
4239         /**
4240          * A wireless scan result object describes a neighbouring wireless
4241          * network found in the vincinity.
4242          *
4243          * @typedef {Object&lt;string, number|string|LuCI.Network.WifiEncryption>} WifiScanResult
4244          * @memberof LuCI.Network
4245          *
4246          * @property {string} ssid
4247          * The SSID / Mesh ID of the network.
4248          *
4249          * @property {string} bssid
4250          * The BSSID if the network.
4251          *
4252          * @property {string} mode
4253          * The operation mode of the network (`Master`, `Ad-Hoc`, `Mesh Point`).
4254          *
4255          * @property {number} channel
4256          * The wireless channel of the network.
4257          *
4258          * @property {number} signal
4259          * The received signal strength of the network in dBm.
4260          *
4261          * @property {number} quality
4262          * The numeric quality level of the signal, can be used in conjunction
4263          * with `quality_max` to calculate a quality percentage.
4264          *
4265          * @property {number} quality_max
4266          * The maximum possible quality level of the signal, can be used in
4267          * conjunction with `quality` to calculate a quality percentage.
4268          *
4269          * @property {LuCI.Network.WifiEncryption} encryption
4270          * The encryption used by the wireless network.
4271          */
4272
4273         /**
4274          * Trigger a wireless scan on this radio device and obtain a list of
4275          * nearby networks.
4276          *
4277          * @returns {Promise&lt;Array&lt;LuCI.Network.WifiScanResult>>}
4278          * Returns a promise resolving to an array of scan result objects
4279          * describing the networks found in the vincinity.
4280          */
4281         getScanList: function() {
4282                 return callIwinfoScan(this.sid);
4283         },
4284
4285         /**
4286          * Check whether the wireless radio is marked as up in the `ubus`
4287          * runtime state.
4288          *
4289          * @returns {boolean}
4290          * Returns `true` when the radio device is up, else `false`.
4291          */
4292         isUp: function() {
4293                 if (L.isObject(_state.radios[this.sid]))
4294                         return (_state.radios[this.sid].up == true);
4295
4296                 return false;
4297         },
4298
4299         /**
4300          * Get the wifi network of the given name belonging to this radio device
4301          *
4302          * @param {string} network
4303          * The name of the wireless network to lookup. This may be either an uci
4304          * configuration section ID, a network ID in the form `radio#.network#`
4305          * or a Linux network device name like `wlan0` which is resolved to the
4306          * corresponding configuration section through `ubus` runtime information.
4307          *
4308          * @returns {Promise&lt;LuCI.Network.WifiNetwork>}
4309          * Returns a promise resolving to a `Network.WifiNetwork` instance
4310          * representing the wireless network and rejecting with `null` if
4311          * the given network could not be found or is not associated with
4312          * this radio device.
4313          */
4314         getWifiNetwork: function(network) {
4315                 return L.network.getWifiNetwork(network).then(L.bind(function(networkInstance) {
4316                         var uciWifiIface = (networkInstance.sid ? uci.get('wireless', networkInstance.sid) : null);
4317
4318                         if (uciWifiIface == null || uciWifiIface['.type'] != 'wifi-iface' || uciWifiIface.device != this.sid)
4319                                 return Promise.reject();
4320
4321                         return networkInstance;
4322                 }, this));
4323         },
4324
4325         /**
4326          * Get all wireless networks associated with this wireless radio device.
4327          *
4328          * @returns {Promise&lt;Array&lt;LuCI.Network.WifiNetwork>>}
4329          * Returns a promise resolving to an array of `Network.WifiNetwork`
4330          * instances respresenting the wireless networks associated with this
4331          * radio device.
4332          */
4333         getWifiNetworks: function() {
4334                 return L.network.getWifiNetworks().then(L.bind(function(networks) {
4335                         var rv = [];
4336
4337                         for (var i = 0; i &lt; networks.length; i++)
4338                                 if (networks[i].getWifiDeviceName() == this.getName())
4339                                         rv.push(networks[i]);
4340
4341                         return rv;
4342                 }, this));
4343         },
4344
4345         /**
4346          * Adds a new wireless network associated with this radio device to the
4347          * configuration and sets its options to the provided values.
4348          *
4349          * @param {Object&lt;string, string|string[]>} [options]
4350          * The options to set for the newly added wireless network.
4351          *
4352          * @returns {Promise&lt;null|LuCI.Network.WifiNetwork>}
4353          * Returns a promise resolving to a `WifiNetwork` instance describing
4354          * the newly added wireless network or `null` if the given options
4355          * were invalid.
4356          */
4357         addWifiNetwork: function(options) {
4358                 if (!L.isObject(options))
4359                         options = {};
4360
4361                 options.device = this.sid;
4362
4363                 return L.network.addWifiNetwork(options);
4364         },
4365
4366         /**
4367          * Deletes the wireless network with the given name associated with this
4368          * radio device.
4369          *
4370          * @param {string} network
4371          * The name of the wireless network to lookup. This may be either an uci
4372          * configuration section ID, a network ID in the form `radio#.network#`
4373          * or a Linux network device name like `wlan0` which is resolved to the
4374          * corresponding configuration section through `ubus` runtime information.
4375          *
4376          * @returns {Promise&lt;boolean>}
4377          * Returns a promise resolving to `true` when the wireless network was
4378          * successfully deleted from the configuration or `false` when the given
4379          * network could not be found or if the found network was not associated
4380          * with this wireless radio device.
4381          */
4382         deleteWifiNetwork: function(network) {
4383                 var sid = null;
4384
4385                 if (network instanceof WifiNetwork) {
4386                         sid = network.sid;
4387                 }
4388                 else {
4389                         var uciWifiIface = uci.get('wireless', network);
4390
4391                         if (uciWifiIface == null || uciWifiIface['.type'] != 'wifi-iface')
4392                                 sid = getWifiSidByIfname(network);
4393                 }
4394
4395                 if (sid == null || uci.get('wireless', sid, 'device') != this.sid)
4396                         return Promise.resolve(false);
4397
4398                 uci.delete('wireless', network);
4399
4400                 return Promise.resolve(true);
4401         }
4402 });
4403
4404 /**
4405  * @class
4406  * @memberof LuCI.Network
4407  * @hideconstructor
4408  * @classdesc
4409  *
4410  * A `Network.WifiNetwork` instance represents a wireless network (vif)
4411  * configured on top of a radio device and provides functions for querying
4412  * the runtime state of the network. Most radio devices support multiple
4413  * such networks in parallel.
4414  */
4415 WifiNetwork = L.Class.extend(/** @lends LuCI.Network.WifiNetwork.prototype */ {
4416         __init__: function(sid, radioname, radiostate, netid, netstate, hostapd) {
4417                 this.sid    = sid;
4418                 this.netid  = netid;
4419                 this._ubusdata = {
4420                         hostapd: hostapd,
4421                         radio:   radioname,
4422                         dev:     radiostate,
4423                         net:     netstate
4424                 };
4425         },
4426
4427         ubus: function(/* ... */) {
4428                 var v = this._ubusdata;
4429
4430                 for (var i = 0; i &lt; arguments.length; i++)
4431                         if (L.isObject(v))
4432                                 v = v[arguments[i]];
4433                         else
4434                                 return null;
4435
4436                 return v;
4437         },
4438
4439         /**
4440          * Read the given UCI option value of this wireless network.
4441          *
4442          * @param {string} opt
4443          * The UCI option name to read.
4444          *
4445          * @returns {null|string|string[]}
4446          * Returns the UCI option value or `null` if the requested option is
4447          * not found.
4448          */
4449         get: function(opt) {
4450                 return uci.get('wireless', this.sid, opt);
4451         },
4452
4453         /**
4454          * Set the given UCI option of this network to the given value.
4455          *
4456          * @param {string} opt
4457          * The name of the UCI option to set.
4458          *
4459          * @param {null|string|string[]} val
4460          * The value to set or `null` to remove the given option from the
4461          * configuration.
4462          */
4463         set: function(opt, value) {
4464                 return uci.set('wireless', this.sid, opt, value);
4465         },
4466
4467         /**
4468          * Checks whether this wireless network is disabled.
4469          *
4470          * @returns {boolean}
4471          * Returns `true` when the wireless radio is marked as disabled in `ubus`
4472          * runtime state or when the `disabled` option is set in the corresponding
4473          * UCI configuration.
4474          */
4475         isDisabled: function() {
4476                 return this.ubus('dev', 'disabled') || this.get('disabled') == '1';
4477         },
4478
4479         /**
4480          * Get the configured operation mode of the wireless network.
4481          *
4482          * @returns {string}
4483          * Returns the configured operation mode. Possible values are:
4484          *  - `ap` - Master (Access Point) mode
4485          *  - `sta` - Station (client) mode
4486          *  - `adhoc` - Ad-Hoc (IBSS) mode
4487          *  - `mesh` - Mesh (IEEE 802.11s) mode
4488          *  - `monitor` - Monitor mode
4489          */
4490         getMode: function() {
4491                 return this.ubus('net', 'config', 'mode') || this.get('mode') || 'ap';
4492         },
4493
4494         /**
4495          * Get the configured SSID of the wireless network.
4496          *
4497          * @returns {null|string}
4498          * Returns the configured SSID value or `null` when this network is
4499          * in mesh mode.
4500          */
4501         getSSID: function() {
4502                 if (this.getMode() == 'mesh')
4503                         return null;
4504
4505                 return this.ubus('net', 'config', 'ssid') || this.get('ssid');
4506         },
4507
4508         /**
4509          * Get the configured Mesh ID of the wireless network.
4510          *
4511          * @returns {null|string}
4512          * Returns the configured mesh ID value or `null` when this network
4513          * is not in mesh mode.
4514          */
4515         getMeshID: function() {
4516                 if (this.getMode() != 'mesh')
4517                         return null;
4518
4519                 return this.ubus('net', 'config', 'mesh_id') || this.get('mesh_id');
4520         },
4521
4522         /**
4523          * Get the configured BSSID of the wireless network.
4524          *
4525          * @returns {null|string}
4526          * Returns the BSSID value or `null` if none has been specified.
4527          */
4528         getBSSID: function() {
4529                 return this.ubus('net', 'config', 'bssid') || this.get('bssid');
4530         },
4531
4532         /**
4533          * Get the names of the logical interfaces this wireless network is
4534          * attached to.
4535          *
4536          * @returns {string[]}
4537          * Returns an array of logical interface names.
4538          */
4539         getNetworkNames: function() {
4540                 return L.toArray(this.ubus('net', 'config', 'network') || this.get('network'));
4541         },
4542
4543         /**
4544          * Get the internal network ID of this wireless network.
4545          *
4546          * The network ID is a LuCI specific identifer in the form
4547          * `radio#.network#` to identify wireless networks by their corresponding
4548          * radio and network index numbers.
4549          *
4550          * @returns {string}
4551          * Returns the LuCI specific network ID.
4552          */
4553         getID: function() {
4554                 return this.netid;
4555         },
4556
4557         /**
4558          * Get the configuration ID of this wireless network.
4559          *
4560          * @returns {string}
4561          * Returns the corresponding UCI section ID of the network.
4562          */
4563         getName: function() {
4564                 return this.sid;
4565         },
4566
4567         /**
4568          * Get the Linux network device name.
4569          *
4570          * @returns {null|string}
4571          * Returns the current Linux network device name as resolved from
4572          * `ubus` runtime information or `null` if this network has no
4573          * associated network device, e.g. when not configured or up.
4574          */
4575         getIfname: function() {
4576                 var ifname = this.ubus('net', 'ifname') || this.ubus('net', 'iwinfo', 'ifname');
4577
4578                 if (ifname == null || ifname.match(/^(wifi|radio)\d/))
4579                         ifname = this.netid;
4580
4581                 return ifname;
4582         },
4583
4584         /**
4585          * Get the name of the corresponding wifi radio device.
4586          *
4587          * @returns {null|string}
4588          * Returns the name of the radio device this network is configured on
4589          * or `null` if it cannot be determined.
4590          */
4591         getWifiDeviceName: function() {
4592                 return this.ubus('radio') || this.get('device');
4593         },
4594
4595         /**
4596          * Get the corresponding wifi radio device.
4597          *
4598          * @returns {null|LuCI.Network.WifiDevice}
4599          * Returns a `Network.WifiDevice` instance representing the corresponding
4600          * wifi radio device or `null` if the related radio device could not be
4601          * found.
4602          */
4603         getWifiDevice: function() {
4604                 var radioname = this.getWifiDeviceName();
4605
4606                 if (radioname == null)
4607                         return Promise.reject();
4608
4609                 return L.network.getWifiDevice(radioname);
4610         },
4611
4612         /**
4613          * Check whether the radio network is up.
4614          *
4615          * This function actually queries the up state of the related radio
4616          * device and assumes this network to be up as well when the parent
4617          * radio is up. This is due to the fact that OpenWrt does not control
4618          * virtual interfaces individually but within one common hostapd
4619          * instance.
4620          *
4621          * @returns {boolean}
4622          * Returns `true` when the network is up, else `false`.
4623          */
4624         isUp: function() {
4625                 var device = this.getDevice();
4626
4627                 if (device == null)
4628                         return false;
4629
4630                 return device.isUp();
4631         },
4632
4633         /**
4634          * Query the current operation mode from runtime information.
4635          *
4636          * @returns {string}
4637          * Returns the human readable mode name as reported by `ubus` runtime
4638          * state. Possible returned values are:
4639          *  - `Master`
4640          *  - `Ad-Hoc`
4641          *  - `Client`
4642          *  - `Monitor`
4643          *  - `Master (VLAN)`
4644          *  - `WDS`
4645          *  - `Mesh Point`
4646          *  - `P2P Client`
4647          *  - `P2P Go`
4648          *  - `Unknown`
4649          */
4650         getActiveMode: function() {
4651                 var mode = this.ubus('net', 'iwinfo', 'mode') || this.ubus('net', 'config', 'mode') || this.get('mode') || 'ap';
4652
4653                 switch (mode) {
4654                 case 'ap':      return 'Master';
4655                 case 'sta':     return 'Client';
4656                 case 'adhoc':   return 'Ad-Hoc';
4657                 case 'mesh':    return 'Mesh';
4658                 case 'monitor': return 'Monitor';
4659                 default:        return mode;
4660                 }
4661         },
4662
4663         /**
4664          * Query the current operation mode from runtime information as
4665          * translated string.
4666          *
4667          * @returns {string}
4668          * Returns the translated, human readable mode name as reported by
4669          *`ubus` runtime state.
4670          */
4671         getActiveModeI18n: function() {
4672                 var mode = this.getActiveMode();
4673
4674                 switch (mode) {
4675                 case 'Master':  return _('Master');
4676                 case 'Client':  return _('Client');
4677                 case 'Ad-Hoc':  return _('Ad-Hoc');
4678                 case 'Mash':    return _('Mesh');
4679                 case 'Monitor': return _('Monitor');
4680                 default:        return mode;
4681                 }
4682         },
4683
4684         /**
4685          * Query the current SSID from runtime information.
4686          *
4687          * @returns {string}
4688          * Returns the current SSID or Mesh ID as reported by `ubus` runtime
4689          * information.
4690          */
4691         getActiveSSID: function() {
4692                 return this.ubus('net', 'iwinfo', 'ssid') || this.ubus('net', 'config', 'ssid') || this.get('ssid');
4693         },
4694
4695         /**
4696          * Query the current BSSID from runtime information.
4697          *
4698          * @returns {string}
4699          * Returns the current BSSID or Mesh ID as reported by `ubus` runtime
4700          * information.
4701          */
4702         getActiveBSSID: function() {
4703                 return this.ubus('net', 'iwinfo', 'bssid') || this.ubus('net', 'config', 'bssid') || this.get('bssid');
4704         },
4705
4706         /**
4707          * Query the current encryption settings from runtime information.
4708          *
4709          * @returns {string}
4710          * Returns a string describing the current encryption or `-` if the the
4711          * encryption state could not be found in `ubus` runtime information.
4712          */
4713         getActiveEncryption: function() {
4714                 return formatWifiEncryption(this.ubus('net', 'iwinfo', 'encryption')) || '-';
4715         },
4716
4717         /**
4718          * A wireless peer entry describes the properties of a remote wireless
4719          * peer associated with a local network.
4720          *
4721          * @typedef {Object&lt;string, boolean|number|string|LuCI.Network.WifiRateEntry>} WifiPeerEntry
4722          * @memberof LuCI.Network
4723          *
4724          * @property {string} mac
4725          * The MAC address (BSSID).
4726          *
4727          * @property {number} signal
4728          * The received signal strength.
4729          *
4730          * @property {number} [signal_avg]
4731          * The average signal strength if supported by the driver.
4732          *
4733          * @property {number} [noise]
4734          * The current noise floor of the radio. May be `0` or absent if not
4735          * supported by the driver.
4736          *
4737          * @property {number} inactive
4738          * The amount of milliseconds the peer has been inactive, e.g. due
4739          * to powersave.
4740          *
4741          * @property {number} connected_time
4742          * The amount of milliseconds the peer is associated to this network.
4743          *
4744          * @property {number} [thr]
4745          * The estimated throughput of the peer, May be `0` or absent if not
4746          * supported by the driver.
4747          *
4748          * @property {boolean} authorized
4749          * Specifies whether the peer is authorized to associate to this network.
4750          *
4751          * @property {boolean} authenticated
4752          * Specifies whether the peer completed authentication to this network.
4753          *
4754          * @property {string} preamble
4755          * The preamble mode used by the peer. May be `long` or `short`.
4756          *
4757          * @property {boolean} wme
4758          * Specifies whether the peer supports WME/WMM capabilities.
4759          *
4760          * @property {boolean} mfp
4761          * Specifies whether management frame protection is active.
4762          *
4763          * @property {boolean} tdls
4764          * Specifies whether TDLS is active.
4765          *
4766          * @property {number} [mesh llid]
4767          * The mesh LLID, may be `0` or absent if not applicable or supported
4768          * by the driver.
4769          *
4770          * @property {number} [mesh plid]
4771          * The mesh PLID, may be `0` or absent if not applicable or supported
4772          * by the driver.
4773          *
4774          * @property {string} [mesh plink]
4775          * The mesh peer link state description, may be an empty string (`''`)
4776          * or absent if not applicable or supported by the driver.
4777          *
4778          * The following states are known:
4779          *  - `LISTEN`
4780          *  - `OPN_SNT`
4781          *  - `OPN_RCVD`
4782          *  - `CNF_RCVD`
4783          *  - `ESTAB`
4784          *  - `HOLDING`
4785          *  - `BLOCKED`
4786          *  - `UNKNOWN`
4787          *
4788          * @property {number} [mesh local PS]
4789          * The local powersafe mode for the peer link, may be an empty
4790          * string (`''`) or absent if not applicable or supported by
4791          * the driver.
4792          *
4793          * The following modes are known:
4794          *  - `ACTIVE` (no power save)
4795          *  - `LIGHT SLEEP`
4796          *  - `DEEP SLEEP`
4797          *  - `UNKNOWN`
4798          *
4799          * @property {number} [mesh peer PS]
4800          * The remote powersafe mode for the peer link, may be an empty
4801          * string (`''`) or absent if not applicable or supported by
4802          * the driver.
4803          *
4804          * The following modes are known:
4805          *  - `ACTIVE` (no power save)
4806          *  - `LIGHT SLEEP`
4807          *  - `DEEP SLEEP`
4808          *  - `UNKNOWN`
4809          *
4810          * @property {number} [mesh non-peer PS]
4811          * The powersafe mode for all non-peer neigbours, may be an empty
4812          * string (`''`) or absent if not applicable or supported by the driver.
4813          *
4814          * The following modes are known:
4815          *  - `ACTIVE` (no power save)
4816          *  - `LIGHT SLEEP`
4817          *  - `DEEP SLEEP`
4818          *  - `UNKNOWN`
4819          *
4820          * @property {LuCI.Network.WifiRateEntry} rx
4821          * Describes the receiving wireless rate from the peer.
4822          *
4823          * @property {LuCI.Network.WifiRateEntry} tx
4824          * Describes the transmitting wireless rate to the peer.
4825          */
4826
4827         /**
4828          * A wireless rate entry describes the properties of a wireless
4829          * transmission rate to or from a peer.
4830          *
4831          * @typedef {Object&lt;string, boolean|number>} WifiRateEntry
4832          * @memberof LuCI.Network
4833          *
4834          * @property {number} [drop_misc]
4835          * The amount of received misc. packages that have been dropped, e.g.
4836          * due to corruption or missing authentication. Only applicable to
4837          * receiving rates.
4838          *
4839          * @property {number} packets
4840          * The amount of packets that have been received or sent.
4841          *
4842          * @property {number} bytes
4843          * The amount of bytes that have been received or sent.
4844          *
4845          * @property {number} [failed]
4846          * The amount of failed tranmission attempts. Only applicable to
4847          * transmit rates.
4848          *
4849          * @property {number} [retries]
4850          * The amount of retried transmissions. Only applicable to transmit
4851          * rates.
4852          *
4853          * @property {boolean} is_ht
4854          * Specifies whether this rate is an HT (IEEE 802.11n) rate.
4855          *
4856          * @property {boolean} is_vht
4857          * Specifies whether this rate is an VHT (IEEE 802.11ac) rate.
4858          *
4859          * @property {number} mhz
4860          * The channel width in MHz used for the transmission.
4861          *
4862          * @property {number} rate
4863          * The bitrate in bit/s of the transmission.
4864          *
4865          * @property {number} [mcs]
4866          * The MCS index of the used transmission rate. Only applicable to
4867          * HT or VHT rates.
4868          *
4869          * @property {number} [40mhz]
4870          * Specifies whether the tranmission rate used 40MHz wide channel.
4871          * Only applicable to HT or VHT rates.
4872          *
4873          * Note: this option exists for backwards compatibility only and its
4874          * use is discouraged. The `mhz` field should be used instead to
4875          * determine the channel width.
4876          *
4877          * @property {boolean} [short_gi]
4878          * Specifies whether a short guard interval is used for the transmission.
4879          * Only applicable to HT or VHT rates.
4880          *
4881          * @property {number} [nss]
4882          * Specifies the number of spatial streams used by the transmission.
4883          * Only applicable to VHT rates.
4884          */
4885
4886         /**
4887          * Fetch the list of associated peers.
4888          *
4889          * @returns {Promise&lt;Array&lt;LuCI.Network.WifiPeerEntry>>}
4890          * Returns a promise resolving to an array of wireless peers associated
4891          * with this network.
4892          */
4893         getAssocList: function() {
4894                 return callIwinfoAssoclist(this.getIfname());
4895         },
4896
4897         /**
4898          * Query the current operating frequency of the wireless network.
4899          *
4900          * @returns {null|string}
4901          * Returns the current operating frequency of the network from `ubus`
4902          * runtime information in GHz or `null` if the information is not
4903          * available.
4904          */
4905         getFrequency: function() {
4906                 var freq = this.ubus('net', 'iwinfo', 'frequency');
4907
4908                 if (freq != null &amp;&amp; freq > 0)
4909                         return '%.03f'.format(freq / 1000);
4910
4911                 return null;
4912         },
4913
4914         /**
4915          * Query the current average bitrate of all peers associated to this
4916          * wireless network.
4917          *
4918          * @returns {null|number}
4919          * Returns the average bit rate among all peers associated to the network
4920          * as reported by `ubus` runtime information or `null` if the information
4921          * is not available.
4922          */
4923         getBitRate: function() {
4924                 var rate = this.ubus('net', 'iwinfo', 'bitrate');
4925
4926                 if (rate != null &amp;&amp; rate > 0)
4927                         return (rate / 1000);
4928
4929                 return null;
4930         },
4931
4932         /**
4933          * Query the current wireless channel.
4934          *
4935          * @returns {null|number}
4936          * Returns the wireless channel as reported by `ubus` runtime information
4937          * or `null` if it cannot be determined.
4938          */
4939         getChannel: function() {
4940                 return this.ubus('net', 'iwinfo', 'channel') || this.ubus('dev', 'config', 'channel') || this.get('channel');
4941         },
4942
4943         /**
4944          * Query the current wireless signal.
4945          *
4946          * @returns {null|number}
4947          * Returns the wireless signal in dBm as reported by `ubus` runtime
4948          * information or `null` if it cannot be determined.
4949          */
4950         getSignal: function() {
4951                 return this.ubus('net', 'iwinfo', 'signal') || 0;
4952         },
4953
4954         /**
4955          * Query the current radio noise floor.
4956          *
4957          * @returns {number}
4958          * Returns the radio noise floor in dBm as reported by `ubus` runtime
4959          * information or `0` if it cannot be determined.
4960          */
4961         getNoise: function() {
4962                 return this.ubus('net', 'iwinfo', 'noise') || 0;
4963         },
4964
4965         /**
4966          * Query the current country code.
4967          *
4968          * @returns {string}
4969          * Returns the wireless country code as reported by `ubus` runtime
4970          * information or `00` if it cannot be determined.
4971          */
4972         getCountryCode: function() {
4973                 return this.ubus('net', 'iwinfo', 'country') || this.ubus('dev', 'config', 'country') || '00';
4974         },
4975
4976         /**
4977          * Query the current radio TX power.
4978          *
4979          * @returns {null|number}
4980          * Returns the wireless network transmit power in dBm as reported by
4981          * `ubus` runtime information or `null` if it cannot be determined.
4982          */
4983         getTXPower: function() {
4984                 return this.ubus('net', 'iwinfo', 'txpower');
4985         },
4986
4987         /**
4988          * Query the radio TX power offset.
4989          *
4990          * Some wireless radios have a fixed power offset, e.g. due to the
4991          * use of external amplifiers.
4992          *
4993          * @returns {number}
4994          * Returns the wireless network transmit power offset in dBm as reported
4995          * by `ubus` runtime information or `0` if there is no offset, or if it
4996          * cannot be determined.
4997          */
4998         getTXPowerOffset: function() {
4999                 return this.ubus('net', 'iwinfo', 'txpower_offset') || 0;
5000         },
5001
5002         /**
5003          * Calculate the current signal.
5004          *
5005          * @deprecated
5006          * @returns {number}
5007          * Returns the calculated signal level, which is the difference between
5008          * noise and signal (SNR), divided by 5.
5009          */
5010         getSignalLevel: function(signal, noise) {
5011                 if (this.getActiveBSSID() == '00:00:00:00:00:00')
5012                         return -1;
5013
5014                 signal = signal || this.getSignal();
5015                 noise  = noise  || this.getNoise();
5016
5017                 if (signal &lt; 0 &amp;&amp; noise &lt; 0) {
5018                         var snr = -1 * (noise - signal);
5019                         return Math.floor(snr / 5);
5020                 }
5021
5022                 return 0;
5023         },
5024
5025         /**
5026          * Calculate the current signal quality percentage.
5027          *
5028          * @returns {number}
5029          * Returns the calculated signal quality in percent. The value is
5030          * calculated from the `quality` and `quality_max` indicators reported
5031          * by `ubus` runtime state.
5032          */
5033         getSignalPercent: function() {
5034                 var qc = this.ubus('net', 'iwinfo', 'quality') || 0,
5035                     qm = this.ubus('net', 'iwinfo', 'quality_max') || 0;
5036
5037                 if (qc > 0 &amp;&amp; qm > 0)
5038                         return Math.floor((100 / qm) * qc);
5039
5040                 return 0;
5041         },
5042
5043         /**
5044          * Get a short description string for this wireless network.
5045          *
5046          * @returns {string}
5047          * Returns a string describing this network, consisting of the
5048          * active operation mode, followed by either the SSID, BSSID or
5049          * internal network ID, depending on which information is available.
5050          */
5051         getShortName: function() {
5052                 return '%s "%s"'.format(
5053                         this.getActiveModeI18n(),
5054                         this.getActiveSSID() || this.getActiveBSSID() || this.getID());
5055         },
5056
5057         /**
5058          * Get a description string for this wireless network.
5059          *
5060          * @returns {string}
5061          * Returns a string describing this network, consisting of the
5062          * term `Wireless Network`, followed by the active operation mode,
5063          * the SSID, BSSID or internal network ID and the Linux network device
5064          * name, depending on which information is available.
5065          */
5066         getI18n: function() {
5067                 return '%s: %s "%s" (%s)'.format(
5068                         _('Wireless Network'),
5069                         this.getActiveModeI18n(),
5070                         this.getActiveSSID() || this.getActiveBSSID() || this.getID(),
5071                         this.getIfname());
5072         },
5073
5074         /**
5075          * Get the primary logical interface this wireless network is attached to.
5076          *
5077          * @returns {null|LuCI.Network.Protocol}
5078          * Returns a `Network.Protocol` instance representing the logical
5079          * interface or `null` if this network is not attached to any logical
5080          * interface.
5081          */
5082         getNetwork: function() {
5083                 return this.getNetworks()[0];
5084         },
5085
5086         /**
5087          * Get the logical interfaces this wireless network is attached to.
5088          *
5089          * @returns {Array&lt;LuCI.Network.Protocol>}
5090          * Returns an array of `Network.Protocol` instances representing the
5091          * logical interfaces this wireless network is attached to.
5092          */
5093         getNetworks: function() {
5094                 var networkNames = this.getNetworkNames(),
5095                     networks = [];
5096
5097                 for (var i = 0; i &lt; networkNames.length; i++) {
5098                         var uciInterface = uci.get('network', networkNames[i]);
5099
5100                         if (uciInterface == null || uciInterface['.type'] != 'interface')
5101                                 continue;
5102
5103                         networks.push(L.network.instantiateNetwork(networkNames[i]));
5104                 }
5105
5106                 networks.sort(networkSort);
5107
5108                 return networks;
5109         },
5110
5111         /**
5112          * Get the associated Linux network device.
5113          *
5114          * @returns {LuCI.Network.Device}
5115          * Returns a `Network.Device` instance representing the Linux network
5116          * device associted with this wireless network.
5117          */
5118         getDevice: function() {
5119                 return L.network.instantiateDevice(this.getIfname());
5120         },
5121
5122         /**
5123          * Check whether this wifi network supports deauthenticating clients.
5124          *
5125          * @returns {boolean}
5126          * Returns `true` when this wifi network instance supports forcibly
5127          * deauthenticating clients, otherwise `false`.
5128          */
5129         isClientDisconnectSupported: function() {
5130                 return L.isObject(this.ubus('hostapd', 'del_client'));
5131         },
5132
5133         /**
5134          * Forcibly disconnect the given client from the wireless network.
5135          *
5136          * @param {string} mac
5137          * The MAC address of the client to disconnect.
5138          *
5139          * @param {boolean} [deauth=false]
5140          * Specifies whether to deauthenticate (`true`) or disassociate (`false`)
5141          * the client.
5142          *
5143          * @param {number} [reason=1]
5144          * Specifies the IEEE 802.11 reason code to disassoc/deauth the client
5145          * with. Default is `1` which corresponds to `Unspecified reason`.
5146          *
5147          * @param {number} [ban_time=0]
5148          * Specifies the amount of milliseconds to ban the client from
5149          * reconnecting. By default, no ban time is set which allows the client
5150          * to reassociate / reauthenticate immediately.
5151          *
5152          * @returns {Promise&lt;number>}
5153          * Returns a promise resolving to the underlying ubus call result code
5154          * which is typically `0`, even for not existing MAC addresses.
5155          * The promise might reject with an error in case invalid arguments
5156          * are passed.
5157          */
5158         disconnectClient: function(mac, deauth, reason, ban_time) {
5159                 if (reason == null || reason == 0)
5160                         reason = 1;
5161
5162                 if (ban_time == 0)
5163                         ban_time = null;
5164
5165                 return rpc.declare({
5166                         object: 'hostapd.%s'.format(this.getIfname()),
5167                         method: 'del_client',
5168                         params: [ 'addr', 'deauth', 'reason', 'ban_time' ]
5169                 })(mac, deauth, reason, ban_time);
5170         }
5171 });
5172
5173 return Network;
5174 </code></pre>
5175         </article>
5176     </section>
5177
5178
5179
5180
5181
5182
5183         
5184
5185         <footer>
5186             Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.6.3</a> on Thu Nov 07 2019 12:36:05 GMT+0100 (Central European Standard Time)
5187         </footer>
5188     </div>
5189 </div>
5190 <script>prettyPrint();</script>
5191 <script src="scripts/jaguar.js"></script>
5192 </body>
5193 </html>