docs: update js api docs
[oweals/luci.git] / docs / jsapi / ui.js.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4     <meta charset="utf-8">
5     <title>Source: ui.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#Class"><a href="LuCI.html#Class">Class</a></li>
47             
48                 <li data-name="LuCI#dom"><a href="LuCI.html#dom">dom</a></li>
49             
50                 <li data-name="LuCI#env"><a href="LuCI.html#env">env</a></li>
51             
52                 <li data-name="LuCI#Poll"><a href="LuCI.html#Poll">Poll</a></li>
53             
54                 <li data-name="LuCI#Request"><a href="LuCI.html#Request">Request</a></li>
55             
56                 <li data-name="LuCI#view"><a href="LuCI.html#view">view</a></li>
57             
58             </ul>
59             <ul class="typedefs itemMembers">
60             
61             <span class="subtitle">Typedefs</span>
62             
63                 <li data-name="LuCI.requestCallbackFn"><a href="LuCI.html#.requestCallbackFn">requestCallbackFn</a></li>
64             
65             </ul>
66             <ul class="typedefs itemMembers">
67             
68             </ul>
69             <ul class="methods itemMembers">
70             
71             <span class="subtitle">Methods</span>
72             
73                 <li data-name="LuCI#bind"><a href="LuCI.html#bind">bind</a></li>
74             
75                 <li data-name="LuCI#error"><a href="LuCI.html#error">error</a></li>
76             
77                 <li data-name="LuCI#get"><a href="LuCI.html#get">get</a></li>
78             
79                 <li data-name="LuCI#halt"><a href="LuCI.html#halt">halt</a></li>
80             
81                 <li data-name="LuCI#hasSystemFeature"><a href="LuCI.html#hasSystemFeature">hasSystemFeature</a></li>
82             
83                 <li data-name="LuCI#isObject"><a href="LuCI.html#isObject">isObject</a></li>
84             
85                 <li data-name="LuCI#location"><a href="LuCI.html#location">location</a></li>
86             
87                 <li data-name="LuCI#media"><a href="LuCI.html#media">media</a></li>
88             
89                 <li data-name="LuCI#path"><a href="LuCI.html#path">path</a></li>
90             
91                 <li data-name="LuCI#poll"><a href="LuCI.html#poll">poll</a></li>
92             
93                 <li data-name="LuCI#post"><a href="LuCI.html#post">post</a></li>
94             
95                 <li data-name="LuCI#raise"><a href="LuCI.html#raise">raise</a></li>
96             
97                 <li data-name="LuCI#require"><a href="LuCI.html#require">require</a></li>
98             
99                 <li data-name="LuCI#resolveDefault"><a href="LuCI.html#resolveDefault">resolveDefault</a></li>
100             
101                 <li data-name="LuCI#resource"><a href="LuCI.html#resource">resource</a></li>
102             
103                 <li data-name="LuCI#run"><a href="LuCI.html#run">run</a></li>
104             
105                 <li data-name="LuCI#sortedKeys"><a href="LuCI.html#sortedKeys">sortedKeys</a></li>
106             
107                 <li data-name="LuCI#stop"><a href="LuCI.html#stop">stop</a></li>
108             
109                 <li data-name="LuCI#toArray"><a href="LuCI.html#toArray">toArray</a></li>
110             
111                 <li data-name="LuCI#url"><a href="LuCI.html#url">url</a></li>
112             
113             </ul>
114             <ul class="events itemMembers">
115             
116             </ul>
117         </li>
118     
119         <li class="item" data-name="LuCI.baseclass">
120             <span class="title">
121                 <a href="LuCI.baseclass.html">LuCI.baseclass</a>
122                 
123             </span>
124             <ul class="members itemMembers">
125             
126             </ul>
127             <ul class="typedefs itemMembers">
128             
129             </ul>
130             <ul class="typedefs itemMembers">
131             
132             </ul>
133             <ul class="methods itemMembers">
134             
135             <span class="subtitle">Methods</span>
136             
137                 <li data-name="LuCI.baseclass.extend"><a href="LuCI.baseclass.html#.extend">extend</a></li>
138             
139                 <li data-name="LuCI.baseclass.instantiate"><a href="LuCI.baseclass.html#.instantiate">instantiate</a></li>
140             
141                 <li data-name="LuCI.baseclass.isSubclass"><a href="LuCI.baseclass.html#.isSubclass">isSubclass</a></li>
142             
143                 <li data-name="LuCI.baseclass.singleton"><a href="LuCI.baseclass.html#.singleton">singleton</a></li>
144             
145                 <li data-name="LuCI.baseclass#super"><a href="LuCI.baseclass.html#super">super</a></li>
146             
147                 <li data-name="LuCI.baseclass#varargs"><a href="LuCI.baseclass.html#varargs">varargs</a></li>
148             
149             </ul>
150             <ul class="events itemMembers">
151             
152             </ul>
153         </li>
154     
155         <li class="item" data-name="LuCI.dom">
156             <span class="title">
157                 <a href="LuCI.dom.html">LuCI.dom</a>
158                 
159             </span>
160             <ul class="members itemMembers">
161             
162             </ul>
163             <ul class="typedefs itemMembers">
164             
165             <span class="subtitle">Typedefs</span>
166             
167                 <li data-name="LuCI.dom~ignoreCallbackFn"><a href="LuCI.dom.html#~ignoreCallbackFn">ignoreCallbackFn</a></li>
168             
169             </ul>
170             <ul class="typedefs itemMembers">
171             
172             </ul>
173             <ul class="methods itemMembers">
174             
175             <span class="subtitle">Methods</span>
176             
177                 <li data-name="LuCI.dom#append"><a href="LuCI.dom.html#append">append</a></li>
178             
179                 <li data-name="LuCI.dom#attr"><a href="LuCI.dom.html#attr">attr</a></li>
180             
181                 <li data-name="LuCI.dom#bindClassInstance"><a href="LuCI.dom.html#bindClassInstance">bindClassInstance</a></li>
182             
183                 <li data-name="LuCI.dom#callClassMethod"><a href="LuCI.dom.html#callClassMethod">callClassMethod</a></li>
184             
185                 <li data-name="LuCI.dom#content"><a href="LuCI.dom.html#content">content</a></li>
186             
187                 <li data-name="LuCI.dom#create"><a href="LuCI.dom.html#create">create</a></li>
188             
189                 <li data-name="LuCI.dom#data"><a href="LuCI.dom.html#data">data</a></li>
190             
191                 <li data-name="LuCI.dom#elem"><a href="LuCI.dom.html#elem">elem</a></li>
192             
193                 <li data-name="LuCI.dom#findClassInstance"><a href="LuCI.dom.html#findClassInstance">findClassInstance</a></li>
194             
195                 <li data-name="LuCI.dom#isEmpty"><a href="LuCI.dom.html#isEmpty">isEmpty</a></li>
196             
197                 <li data-name="LuCI.dom#matches"><a href="LuCI.dom.html#matches">matches</a></li>
198             
199                 <li data-name="LuCI.dom#parent"><a href="LuCI.dom.html#parent">parent</a></li>
200             
201                 <li data-name="LuCI.dom#parse"><a href="LuCI.dom.html#parse">parse</a></li>
202             
203             </ul>
204             <ul class="events itemMembers">
205             
206             </ul>
207         </li>
208     
209         <li class="item" data-name="LuCI.form">
210             <span class="title">
211                 <a href="LuCI.form.html">LuCI.form</a>
212                 
213             </span>
214             <ul class="members itemMembers">
215             
216             </ul>
217             <ul class="typedefs itemMembers">
218             
219             </ul>
220             <ul class="typedefs itemMembers">
221             
222             </ul>
223             <ul class="methods itemMembers">
224             
225             </ul>
226             <ul class="events itemMembers">
227             
228             </ul>
229         </li>
230     
231         <li class="item" data-name="LuCI.form.AbstractElement">
232             <span class="title">
233                 <a href="LuCI.form.AbstractElement.html">LuCI.form.AbstractElement</a>
234                 
235             </span>
236             <ul class="members itemMembers">
237             
238             </ul>
239             <ul class="typedefs itemMembers">
240             
241             </ul>
242             <ul class="typedefs itemMembers">
243             
244             </ul>
245             <ul class="methods itemMembers">
246             
247             <span class="subtitle">Methods</span>
248             
249                 <li data-name="LuCI.form.AbstractElement#append"><a href="LuCI.form.AbstractElement.html#append">append</a></li>
250             
251                 <li data-name="LuCI.form.AbstractElement#parse"><a href="LuCI.form.AbstractElement.html#parse">parse</a></li>
252             
253                 <li data-name="LuCI.form.AbstractElement#render"><a href="LuCI.form.AbstractElement.html#render">render</a></li>
254             
255                 <li data-name="LuCI.form.AbstractElement#stripTags"><a href="LuCI.form.AbstractElement.html#stripTags">stripTags</a></li>
256             
257                 <li data-name="LuCI.form.AbstractElement#titleFn"><a href="LuCI.form.AbstractElement.html#titleFn">titleFn</a></li>
258             
259             </ul>
260             <ul class="events itemMembers">
261             
262             </ul>
263         </li>
264     
265         <li class="item" data-name="LuCI.form.AbstractSection">
266             <span class="title">
267                 <a href="LuCI.form.AbstractSection.html">LuCI.form.AbstractSection</a>
268                 
269             </span>
270             <ul class="members itemMembers">
271             
272             <span class="subtitle">Members</span>
273             
274                 <li data-name="LuCI.form.AbstractSection##parentoption"><a href="LuCI.form.AbstractSection.html#parentoption">parentoption</a></li>
275             
276             </ul>
277             <ul class="typedefs itemMembers">
278             
279             </ul>
280             <ul class="typedefs itemMembers">
281             
282             </ul>
283             <ul class="methods itemMembers">
284             
285             <span class="subtitle">Methods</span>
286             
287                 <li data-name="LuCI.form.AbstractSection#append"><a href="LuCI.form.AbstractSection.html#append">append</a></li>
288             
289                 <li data-name="LuCI.form.AbstractSection#cfgsections"><a href="LuCI.form.AbstractSection.html#cfgsections">cfgsections</a></li>
290             
291                 <li data-name="LuCI.form.AbstractSection#filter"><a href="LuCI.form.AbstractSection.html#filter">filter</a></li>
292             
293                 <li data-name="LuCI.form.AbstractSection#load"><a href="LuCI.form.AbstractSection.html#load">load</a></li>
294             
295                 <li data-name="LuCI.form.AbstractSection#option"><a href="LuCI.form.AbstractSection.html#option">option</a></li>
296             
297                 <li data-name="LuCI.form.AbstractSection#parse"><a href="LuCI.form.AbstractSection.html#parse">parse</a></li>
298             
299                 <li data-name="LuCI.form.AbstractSection#render"><a href="LuCI.form.AbstractSection.html#render">render</a></li>
300             
301                 <li data-name="LuCI.form.AbstractSection#stripTags"><a href="LuCI.form.AbstractSection.html#stripTags">stripTags</a></li>
302             
303                 <li data-name="LuCI.form.AbstractSection#tab"><a href="LuCI.form.AbstractSection.html#tab">tab</a></li>
304             
305                 <li data-name="LuCI.form.AbstractSection#taboption"><a href="LuCI.form.AbstractSection.html#taboption">taboption</a></li>
306             
307                 <li data-name="LuCI.form.AbstractSection#titleFn"><a href="LuCI.form.AbstractSection.html#titleFn">titleFn</a></li>
308             
309             </ul>
310             <ul class="events itemMembers">
311             
312             </ul>
313         </li>
314     
315         <li class="item" data-name="LuCI.form.AbstractValue">
316             <span class="title">
317                 <a href="LuCI.form.AbstractValue.html">LuCI.form.AbstractValue</a>
318                 
319             </span>
320             <ul class="members itemMembers">
321             
322             <span class="subtitle">Members</span>
323             
324                 <li data-name="LuCI.form.AbstractValue##datatype"><a href="LuCI.form.AbstractValue.html#datatype">datatype</a></li>
325             
326                 <li data-name="LuCI.form.AbstractValue##default"><a href="LuCI.form.AbstractValue.html#default">default</a></li>
327             
328                 <li data-name="LuCI.form.AbstractValue##editable"><a href="LuCI.form.AbstractValue.html#editable">editable</a></li>
329             
330                 <li data-name="LuCI.form.AbstractValue##modalonly"><a href="LuCI.form.AbstractValue.html#modalonly">modalonly</a></li>
331             
332                 <li data-name="LuCI.form.AbstractValue##optional"><a href="LuCI.form.AbstractValue.html#optional">optional</a></li>
333             
334                 <li data-name="LuCI.form.AbstractValue##rmempty"><a href="LuCI.form.AbstractValue.html#rmempty">rmempty</a></li>
335             
336                 <li data-name="LuCI.form.AbstractValue##uciconfig"><a href="LuCI.form.AbstractValue.html#uciconfig">uciconfig</a></li>
337             
338                 <li data-name="LuCI.form.AbstractValue##ucioption"><a href="LuCI.form.AbstractValue.html#ucioption">ucioption</a></li>
339             
340                 <li data-name="LuCI.form.AbstractValue##ucisection"><a href="LuCI.form.AbstractValue.html#ucisection">ucisection</a></li>
341             
342                 <li data-name="LuCI.form.AbstractValue##validate"><a href="LuCI.form.AbstractValue.html#validate">validate</a></li>
343             
344                 <li data-name="LuCI.form.AbstractValue##width"><a href="LuCI.form.AbstractValue.html#width">width</a></li>
345             
346             </ul>
347             <ul class="typedefs itemMembers">
348             
349             </ul>
350             <ul class="typedefs itemMembers">
351             
352             </ul>
353             <ul class="methods itemMembers">
354             
355             <span class="subtitle">Methods</span>
356             
357                 <li data-name="LuCI.form.AbstractValue#append"><a href="LuCI.form.AbstractValue.html#append">append</a></li>
358             
359                 <li data-name="LuCI.form.AbstractValue#cbid"><a href="LuCI.form.AbstractValue.html#cbid">cbid</a></li>
360             
361                 <li data-name="LuCI.form.AbstractValue#cfgvalue"><a href="LuCI.form.AbstractValue.html#cfgvalue">cfgvalue</a></li>
362             
363                 <li data-name="LuCI.form.AbstractValue#depends"><a href="LuCI.form.AbstractValue.html#depends">depends</a></li>
364             
365                 <li data-name="LuCI.form.AbstractValue#formvalue"><a href="LuCI.form.AbstractValue.html#formvalue">formvalue</a></li>
366             
367                 <li data-name="LuCI.form.AbstractValue#getUIElement"><a href="LuCI.form.AbstractValue.html#getUIElement">getUIElement</a></li>
368             
369                 <li data-name="LuCI.form.AbstractValue#isActive"><a href="LuCI.form.AbstractValue.html#isActive">isActive</a></li>
370             
371                 <li data-name="LuCI.form.AbstractValue#isValid"><a href="LuCI.form.AbstractValue.html#isValid">isValid</a></li>
372             
373                 <li data-name="LuCI.form.AbstractValue#load"><a href="LuCI.form.AbstractValue.html#load">load</a></li>
374             
375                 <li data-name="LuCI.form.AbstractValue#parse"><a href="LuCI.form.AbstractValue.html#parse">parse</a></li>
376             
377                 <li data-name="LuCI.form.AbstractValue#remove"><a href="LuCI.form.AbstractValue.html#remove">remove</a></li>
378             
379                 <li data-name="LuCI.form.AbstractValue#render"><a href="LuCI.form.AbstractValue.html#render">render</a></li>
380             
381                 <li data-name="LuCI.form.AbstractValue#stripTags"><a href="LuCI.form.AbstractValue.html#stripTags">stripTags</a></li>
382             
383                 <li data-name="LuCI.form.AbstractValue#textvalue"><a href="LuCI.form.AbstractValue.html#textvalue">textvalue</a></li>
384             
385                 <li data-name="LuCI.form.AbstractValue#titleFn"><a href="LuCI.form.AbstractValue.html#titleFn">titleFn</a></li>
386             
387                 <li data-name="LuCI.form.AbstractValue#validate"><a href="LuCI.form.AbstractValue.html#validate">validate</a></li>
388             
389                 <li data-name="LuCI.form.AbstractValue#write"><a href="LuCI.form.AbstractValue.html#write">write</a></li>
390             
391             </ul>
392             <ul class="events itemMembers">
393             
394             </ul>
395         </li>
396     
397         <li class="item" data-name="LuCI.form.ButtonValue">
398             <span class="title">
399                 <a href="LuCI.form.ButtonValue.html">LuCI.form.ButtonValue</a>
400                 
401             </span>
402             <ul class="members itemMembers">
403             
404             <span class="subtitle">Members</span>
405             
406                 <li data-name="LuCI.form.ButtonValue##inputstyle"><a href="LuCI.form.ButtonValue.html#inputstyle">inputstyle</a></li>
407             
408                 <li data-name="LuCI.form.ButtonValue##inputtitle"><a href="LuCI.form.ButtonValue.html#inputtitle">inputtitle</a></li>
409             
410                 <li data-name="LuCI.form.ButtonValue##onclick"><a href="LuCI.form.ButtonValue.html#onclick">onclick</a></li>
411             
412                 <li data-name="LuCI.form.ButtonValue#datatype"><a href="LuCI.form.ButtonValue.html#datatype">datatype</a></li>
413             
414                 <li data-name="LuCI.form.ButtonValue#default"><a href="LuCI.form.ButtonValue.html#default">default</a></li>
415             
416                 <li data-name="LuCI.form.ButtonValue#editable"><a href="LuCI.form.ButtonValue.html#editable">editable</a></li>
417             
418                 <li data-name="LuCI.form.ButtonValue#modalonly"><a href="LuCI.form.ButtonValue.html#modalonly">modalonly</a></li>
419             
420                 <li data-name="LuCI.form.ButtonValue#optional"><a href="LuCI.form.ButtonValue.html#optional">optional</a></li>
421             
422                 <li data-name="LuCI.form.ButtonValue#password"><a href="LuCI.form.ButtonValue.html#password">password</a></li>
423             
424                 <li data-name="LuCI.form.ButtonValue#placeholder"><a href="LuCI.form.ButtonValue.html#placeholder">placeholder</a></li>
425             
426                 <li data-name="LuCI.form.ButtonValue#rmempty"><a href="LuCI.form.ButtonValue.html#rmempty">rmempty</a></li>
427             
428                 <li data-name="LuCI.form.ButtonValue#uciconfig"><a href="LuCI.form.ButtonValue.html#uciconfig">uciconfig</a></li>
429             
430                 <li data-name="LuCI.form.ButtonValue#ucioption"><a href="LuCI.form.ButtonValue.html#ucioption">ucioption</a></li>
431             
432                 <li data-name="LuCI.form.ButtonValue#ucisection"><a href="LuCI.form.ButtonValue.html#ucisection">ucisection</a></li>
433             
434                 <li data-name="LuCI.form.ButtonValue#validate"><a href="LuCI.form.ButtonValue.html#validate">validate</a></li>
435             
436                 <li data-name="LuCI.form.ButtonValue#width"><a href="LuCI.form.ButtonValue.html#width">width</a></li>
437             
438             </ul>
439             <ul class="typedefs itemMembers">
440             
441             </ul>
442             <ul class="typedefs itemMembers">
443             
444             </ul>
445             <ul class="methods itemMembers">
446             
447             <span class="subtitle">Methods</span>
448             
449                 <li data-name="LuCI.form.ButtonValue#append"><a href="LuCI.form.ButtonValue.html#append">append</a></li>
450             
451                 <li data-name="LuCI.form.ButtonValue#cbid"><a href="LuCI.form.ButtonValue.html#cbid">cbid</a></li>
452             
453                 <li data-name="LuCI.form.ButtonValue#cfgvalue"><a href="LuCI.form.ButtonValue.html#cfgvalue">cfgvalue</a></li>
454             
455                 <li data-name="LuCI.form.ButtonValue#depends"><a href="LuCI.form.ButtonValue.html#depends">depends</a></li>
456             
457                 <li data-name="LuCI.form.ButtonValue#formvalue"><a href="LuCI.form.ButtonValue.html#formvalue">formvalue</a></li>
458             
459                 <li data-name="LuCI.form.ButtonValue#getUIElement"><a href="LuCI.form.ButtonValue.html#getUIElement">getUIElement</a></li>
460             
461                 <li data-name="LuCI.form.ButtonValue#isActive"><a href="LuCI.form.ButtonValue.html#isActive">isActive</a></li>
462             
463                 <li data-name="LuCI.form.ButtonValue#isValid"><a href="LuCI.form.ButtonValue.html#isValid">isValid</a></li>
464             
465                 <li data-name="LuCI.form.ButtonValue#load"><a href="LuCI.form.ButtonValue.html#load">load</a></li>
466             
467                 <li data-name="LuCI.form.ButtonValue#parse"><a href="LuCI.form.ButtonValue.html#parse">parse</a></li>
468             
469                 <li data-name="LuCI.form.ButtonValue#remove"><a href="LuCI.form.ButtonValue.html#remove">remove</a></li>
470             
471                 <li data-name="LuCI.form.ButtonValue#stripTags"><a href="LuCI.form.ButtonValue.html#stripTags">stripTags</a></li>
472             
473                 <li data-name="LuCI.form.ButtonValue#textvalue"><a href="LuCI.form.ButtonValue.html#textvalue">textvalue</a></li>
474             
475                 <li data-name="LuCI.form.ButtonValue#titleFn"><a href="LuCI.form.ButtonValue.html#titleFn">titleFn</a></li>
476             
477                 <li data-name="LuCI.form.ButtonValue#value"><a href="LuCI.form.ButtonValue.html#value">value</a></li>
478             
479                 <li data-name="LuCI.form.ButtonValue#write"><a href="LuCI.form.ButtonValue.html#write">write</a></li>
480             
481             </ul>
482             <ul class="events itemMembers">
483             
484             </ul>
485         </li>
486     
487         <li class="item" data-name="LuCI.form.DummyValue">
488             <span class="title">
489                 <a href="LuCI.form.DummyValue.html">LuCI.form.DummyValue</a>
490                 
491             </span>
492             <ul class="members itemMembers">
493             
494             <span class="subtitle">Members</span>
495             
496                 <li data-name="LuCI.form.DummyValue##href"><a href="LuCI.form.DummyValue.html#href">href</a></li>
497             
498                 <li data-name="LuCI.form.DummyValue##rawhtml"><a href="LuCI.form.DummyValue.html#rawhtml">rawhtml</a></li>
499             
500                 <li data-name="LuCI.form.DummyValue#datatype"><a href="LuCI.form.DummyValue.html#datatype">datatype</a></li>
501             
502                 <li data-name="LuCI.form.DummyValue#default"><a href="LuCI.form.DummyValue.html#default">default</a></li>
503             
504                 <li data-name="LuCI.form.DummyValue#editable"><a href="LuCI.form.DummyValue.html#editable">editable</a></li>
505             
506                 <li data-name="LuCI.form.DummyValue#modalonly"><a href="LuCI.form.DummyValue.html#modalonly">modalonly</a></li>
507             
508                 <li data-name="LuCI.form.DummyValue#optional"><a href="LuCI.form.DummyValue.html#optional">optional</a></li>
509             
510                 <li data-name="LuCI.form.DummyValue#password"><a href="LuCI.form.DummyValue.html#password">password</a></li>
511             
512                 <li data-name="LuCI.form.DummyValue#placeholder"><a href="LuCI.form.DummyValue.html#placeholder">placeholder</a></li>
513             
514                 <li data-name="LuCI.form.DummyValue#rmempty"><a href="LuCI.form.DummyValue.html#rmempty">rmempty</a></li>
515             
516                 <li data-name="LuCI.form.DummyValue#uciconfig"><a href="LuCI.form.DummyValue.html#uciconfig">uciconfig</a></li>
517             
518                 <li data-name="LuCI.form.DummyValue#ucioption"><a href="LuCI.form.DummyValue.html#ucioption">ucioption</a></li>
519             
520                 <li data-name="LuCI.form.DummyValue#ucisection"><a href="LuCI.form.DummyValue.html#ucisection">ucisection</a></li>
521             
522                 <li data-name="LuCI.form.DummyValue#validate"><a href="LuCI.form.DummyValue.html#validate">validate</a></li>
523             
524                 <li data-name="LuCI.form.DummyValue#width"><a href="LuCI.form.DummyValue.html#width">width</a></li>
525             
526             </ul>
527             <ul class="typedefs itemMembers">
528             
529             </ul>
530             <ul class="typedefs itemMembers">
531             
532             </ul>
533             <ul class="methods itemMembers">
534             
535             <span class="subtitle">Methods</span>
536             
537                 <li data-name="LuCI.form.DummyValue#append"><a href="LuCI.form.DummyValue.html#append">append</a></li>
538             
539                 <li data-name="LuCI.form.DummyValue#cbid"><a href="LuCI.form.DummyValue.html#cbid">cbid</a></li>
540             
541                 <li data-name="LuCI.form.DummyValue#cfgvalue"><a href="LuCI.form.DummyValue.html#cfgvalue">cfgvalue</a></li>
542             
543                 <li data-name="LuCI.form.DummyValue#depends"><a href="LuCI.form.DummyValue.html#depends">depends</a></li>
544             
545                 <li data-name="LuCI.form.DummyValue#formvalue"><a href="LuCI.form.DummyValue.html#formvalue">formvalue</a></li>
546             
547                 <li data-name="LuCI.form.DummyValue#getUIElement"><a href="LuCI.form.DummyValue.html#getUIElement">getUIElement</a></li>
548             
549                 <li data-name="LuCI.form.DummyValue#isActive"><a href="LuCI.form.DummyValue.html#isActive">isActive</a></li>
550             
551                 <li data-name="LuCI.form.DummyValue#isValid"><a href="LuCI.form.DummyValue.html#isValid">isValid</a></li>
552             
553                 <li data-name="LuCI.form.DummyValue#load"><a href="LuCI.form.DummyValue.html#load">load</a></li>
554             
555                 <li data-name="LuCI.form.DummyValue#parse"><a href="LuCI.form.DummyValue.html#parse">parse</a></li>
556             
557                 <li data-name="LuCI.form.DummyValue#remove"><a href="LuCI.form.DummyValue.html#remove">remove</a></li>
558             
559                 <li data-name="LuCI.form.DummyValue#stripTags"><a href="LuCI.form.DummyValue.html#stripTags">stripTags</a></li>
560             
561                 <li data-name="LuCI.form.DummyValue#textvalue"><a href="LuCI.form.DummyValue.html#textvalue">textvalue</a></li>
562             
563                 <li data-name="LuCI.form.DummyValue#titleFn"><a href="LuCI.form.DummyValue.html#titleFn">titleFn</a></li>
564             
565                 <li data-name="LuCI.form.DummyValue#value"><a href="LuCI.form.DummyValue.html#value">value</a></li>
566             
567                 <li data-name="LuCI.form.DummyValue#write"><a href="LuCI.form.DummyValue.html#write">write</a></li>
568             
569             </ul>
570             <ul class="events itemMembers">
571             
572             </ul>
573         </li>
574     
575         <li class="item" data-name="LuCI.form.DynamicList">
576             <span class="title">
577                 <a href="LuCI.form.DynamicList.html">LuCI.form.DynamicList</a>
578                 
579             </span>
580             <ul class="members itemMembers">
581             
582             <span class="subtitle">Members</span>
583             
584                 <li data-name="LuCI.form.DynamicList#datatype"><a href="LuCI.form.DynamicList.html#datatype">datatype</a></li>
585             
586                 <li data-name="LuCI.form.DynamicList#default"><a href="LuCI.form.DynamicList.html#default">default</a></li>
587             
588                 <li data-name="LuCI.form.DynamicList#editable"><a href="LuCI.form.DynamicList.html#editable">editable</a></li>
589             
590                 <li data-name="LuCI.form.DynamicList#modalonly"><a href="LuCI.form.DynamicList.html#modalonly">modalonly</a></li>
591             
592                 <li data-name="LuCI.form.DynamicList#optional"><a href="LuCI.form.DynamicList.html#optional">optional</a></li>
593             
594                 <li data-name="LuCI.form.DynamicList#password"><a href="LuCI.form.DynamicList.html#password">password</a></li>
595             
596                 <li data-name="LuCI.form.DynamicList#placeholder"><a href="LuCI.form.DynamicList.html#placeholder">placeholder</a></li>
597             
598                 <li data-name="LuCI.form.DynamicList#rmempty"><a href="LuCI.form.DynamicList.html#rmempty">rmempty</a></li>
599             
600                 <li data-name="LuCI.form.DynamicList#uciconfig"><a href="LuCI.form.DynamicList.html#uciconfig">uciconfig</a></li>
601             
602                 <li data-name="LuCI.form.DynamicList#ucioption"><a href="LuCI.form.DynamicList.html#ucioption">ucioption</a></li>
603             
604                 <li data-name="LuCI.form.DynamicList#ucisection"><a href="LuCI.form.DynamicList.html#ucisection">ucisection</a></li>
605             
606                 <li data-name="LuCI.form.DynamicList#validate"><a href="LuCI.form.DynamicList.html#validate">validate</a></li>
607             
608                 <li data-name="LuCI.form.DynamicList#width"><a href="LuCI.form.DynamicList.html#width">width</a></li>
609             
610             </ul>
611             <ul class="typedefs itemMembers">
612             
613             </ul>
614             <ul class="typedefs itemMembers">
615             
616             </ul>
617             <ul class="methods itemMembers">
618             
619             <span class="subtitle">Methods</span>
620             
621                 <li data-name="LuCI.form.DynamicList#append"><a href="LuCI.form.DynamicList.html#append">append</a></li>
622             
623                 <li data-name="LuCI.form.DynamicList#cbid"><a href="LuCI.form.DynamicList.html#cbid">cbid</a></li>
624             
625                 <li data-name="LuCI.form.DynamicList#cfgvalue"><a href="LuCI.form.DynamicList.html#cfgvalue">cfgvalue</a></li>
626             
627                 <li data-name="LuCI.form.DynamicList#depends"><a href="LuCI.form.DynamicList.html#depends">depends</a></li>
628             
629                 <li data-name="LuCI.form.DynamicList#formvalue"><a href="LuCI.form.DynamicList.html#formvalue">formvalue</a></li>
630             
631                 <li data-name="LuCI.form.DynamicList#getUIElement"><a href="LuCI.form.DynamicList.html#getUIElement">getUIElement</a></li>
632             
633                 <li data-name="LuCI.form.DynamicList#isActive"><a href="LuCI.form.DynamicList.html#isActive">isActive</a></li>
634             
635                 <li data-name="LuCI.form.DynamicList#isValid"><a href="LuCI.form.DynamicList.html#isValid">isValid</a></li>
636             
637                 <li data-name="LuCI.form.DynamicList#load"><a href="LuCI.form.DynamicList.html#load">load</a></li>
638             
639                 <li data-name="LuCI.form.DynamicList#parse"><a href="LuCI.form.DynamicList.html#parse">parse</a></li>
640             
641                 <li data-name="LuCI.form.DynamicList#remove"><a href="LuCI.form.DynamicList.html#remove">remove</a></li>
642             
643                 <li data-name="LuCI.form.DynamicList#stripTags"><a href="LuCI.form.DynamicList.html#stripTags">stripTags</a></li>
644             
645                 <li data-name="LuCI.form.DynamicList#textvalue"><a href="LuCI.form.DynamicList.html#textvalue">textvalue</a></li>
646             
647                 <li data-name="LuCI.form.DynamicList#titleFn"><a href="LuCI.form.DynamicList.html#titleFn">titleFn</a></li>
648             
649                 <li data-name="LuCI.form.DynamicList#value"><a href="LuCI.form.DynamicList.html#value">value</a></li>
650             
651                 <li data-name="LuCI.form.DynamicList#write"><a href="LuCI.form.DynamicList.html#write">write</a></li>
652             
653             </ul>
654             <ul class="events itemMembers">
655             
656             </ul>
657         </li>
658     
659         <li class="item" data-name="LuCI.form.FileUpload">
660             <span class="title">
661                 <a href="LuCI.form.FileUpload.html">LuCI.form.FileUpload</a>
662                 
663             </span>
664             <ul class="members itemMembers">
665             
666             <span class="subtitle">Members</span>
667             
668                 <li data-name="LuCI.form.FileUpload##enable_remove"><a href="LuCI.form.FileUpload.html#enable_remove">enable_remove</a></li>
669             
670                 <li data-name="LuCI.form.FileUpload##enable_upload"><a href="LuCI.form.FileUpload.html#enable_upload">enable_upload</a></li>
671             
672                 <li data-name="LuCI.form.FileUpload##root_directory"><a href="LuCI.form.FileUpload.html#root_directory">root_directory</a></li>
673             
674                 <li data-name="LuCI.form.FileUpload##show_hidden"><a href="LuCI.form.FileUpload.html#show_hidden">show_hidden</a></li>
675             
676                 <li data-name="LuCI.form.FileUpload#datatype"><a href="LuCI.form.FileUpload.html#datatype">datatype</a></li>
677             
678                 <li data-name="LuCI.form.FileUpload#default"><a href="LuCI.form.FileUpload.html#default">default</a></li>
679             
680                 <li data-name="LuCI.form.FileUpload#editable"><a href="LuCI.form.FileUpload.html#editable">editable</a></li>
681             
682                 <li data-name="LuCI.form.FileUpload#modalonly"><a href="LuCI.form.FileUpload.html#modalonly">modalonly</a></li>
683             
684                 <li data-name="LuCI.form.FileUpload#optional"><a href="LuCI.form.FileUpload.html#optional">optional</a></li>
685             
686                 <li data-name="LuCI.form.FileUpload#password"><a href="LuCI.form.FileUpload.html#password">password</a></li>
687             
688                 <li data-name="LuCI.form.FileUpload#placeholder"><a href="LuCI.form.FileUpload.html#placeholder">placeholder</a></li>
689             
690                 <li data-name="LuCI.form.FileUpload#rmempty"><a href="LuCI.form.FileUpload.html#rmempty">rmempty</a></li>
691             
692                 <li data-name="LuCI.form.FileUpload#uciconfig"><a href="LuCI.form.FileUpload.html#uciconfig">uciconfig</a></li>
693             
694                 <li data-name="LuCI.form.FileUpload#ucioption"><a href="LuCI.form.FileUpload.html#ucioption">ucioption</a></li>
695             
696                 <li data-name="LuCI.form.FileUpload#ucisection"><a href="LuCI.form.FileUpload.html#ucisection">ucisection</a></li>
697             
698                 <li data-name="LuCI.form.FileUpload#validate"><a href="LuCI.form.FileUpload.html#validate">validate</a></li>
699             
700                 <li data-name="LuCI.form.FileUpload#width"><a href="LuCI.form.FileUpload.html#width">width</a></li>
701             
702             </ul>
703             <ul class="typedefs itemMembers">
704             
705             </ul>
706             <ul class="typedefs itemMembers">
707             
708             </ul>
709             <ul class="methods itemMembers">
710             
711             <span class="subtitle">Methods</span>
712             
713                 <li data-name="LuCI.form.FileUpload#append"><a href="LuCI.form.FileUpload.html#append">append</a></li>
714             
715                 <li data-name="LuCI.form.FileUpload#cbid"><a href="LuCI.form.FileUpload.html#cbid">cbid</a></li>
716             
717                 <li data-name="LuCI.form.FileUpload#cfgvalue"><a href="LuCI.form.FileUpload.html#cfgvalue">cfgvalue</a></li>
718             
719                 <li data-name="LuCI.form.FileUpload#depends"><a href="LuCI.form.FileUpload.html#depends">depends</a></li>
720             
721                 <li data-name="LuCI.form.FileUpload#formvalue"><a href="LuCI.form.FileUpload.html#formvalue">formvalue</a></li>
722             
723                 <li data-name="LuCI.form.FileUpload#getUIElement"><a href="LuCI.form.FileUpload.html#getUIElement">getUIElement</a></li>
724             
725                 <li data-name="LuCI.form.FileUpload#isActive"><a href="LuCI.form.FileUpload.html#isActive">isActive</a></li>
726             
727                 <li data-name="LuCI.form.FileUpload#isValid"><a href="LuCI.form.FileUpload.html#isValid">isValid</a></li>
728             
729                 <li data-name="LuCI.form.FileUpload#load"><a href="LuCI.form.FileUpload.html#load">load</a></li>
730             
731                 <li data-name="LuCI.form.FileUpload#parse"><a href="LuCI.form.FileUpload.html#parse">parse</a></li>
732             
733                 <li data-name="LuCI.form.FileUpload#remove"><a href="LuCI.form.FileUpload.html#remove">remove</a></li>
734             
735                 <li data-name="LuCI.form.FileUpload#stripTags"><a href="LuCI.form.FileUpload.html#stripTags">stripTags</a></li>
736             
737                 <li data-name="LuCI.form.FileUpload#textvalue"><a href="LuCI.form.FileUpload.html#textvalue">textvalue</a></li>
738             
739                 <li data-name="LuCI.form.FileUpload#titleFn"><a href="LuCI.form.FileUpload.html#titleFn">titleFn</a></li>
740             
741                 <li data-name="LuCI.form.FileUpload#value"><a href="LuCI.form.FileUpload.html#value">value</a></li>
742             
743                 <li data-name="LuCI.form.FileUpload#write"><a href="LuCI.form.FileUpload.html#write">write</a></li>
744             
745             </ul>
746             <ul class="events itemMembers">
747             
748             </ul>
749         </li>
750     
751         <li class="item" data-name="LuCI.form.FlagValue">
752             <span class="title">
753                 <a href="LuCI.form.FlagValue.html">LuCI.form.FlagValue</a>
754                 
755             </span>
756             <ul class="members itemMembers">
757             
758             <span class="subtitle">Members</span>
759             
760                 <li data-name="LuCI.form.FlagValue##disabled"><a href="LuCI.form.FlagValue.html#disabled">disabled</a></li>
761             
762                 <li data-name="LuCI.form.FlagValue##enabled"><a href="LuCI.form.FlagValue.html#enabled">enabled</a></li>
763             
764                 <li data-name="LuCI.form.FlagValue#datatype"><a href="LuCI.form.FlagValue.html#datatype">datatype</a></li>
765             
766                 <li data-name="LuCI.form.FlagValue#default"><a href="LuCI.form.FlagValue.html#default">default</a></li>
767             
768                 <li data-name="LuCI.form.FlagValue#editable"><a href="LuCI.form.FlagValue.html#editable">editable</a></li>
769             
770                 <li data-name="LuCI.form.FlagValue#modalonly"><a href="LuCI.form.FlagValue.html#modalonly">modalonly</a></li>
771             
772                 <li data-name="LuCI.form.FlagValue#optional"><a href="LuCI.form.FlagValue.html#optional">optional</a></li>
773             
774                 <li data-name="LuCI.form.FlagValue#password"><a href="LuCI.form.FlagValue.html#password">password</a></li>
775             
776                 <li data-name="LuCI.form.FlagValue#placeholder"><a href="LuCI.form.FlagValue.html#placeholder">placeholder</a></li>
777             
778                 <li data-name="LuCI.form.FlagValue#rmempty"><a href="LuCI.form.FlagValue.html#rmempty">rmempty</a></li>
779             
780                 <li data-name="LuCI.form.FlagValue#uciconfig"><a href="LuCI.form.FlagValue.html#uciconfig">uciconfig</a></li>
781             
782                 <li data-name="LuCI.form.FlagValue#ucioption"><a href="LuCI.form.FlagValue.html#ucioption">ucioption</a></li>
783             
784                 <li data-name="LuCI.form.FlagValue#ucisection"><a href="LuCI.form.FlagValue.html#ucisection">ucisection</a></li>
785             
786                 <li data-name="LuCI.form.FlagValue#validate"><a href="LuCI.form.FlagValue.html#validate">validate</a></li>
787             
788                 <li data-name="LuCI.form.FlagValue#width"><a href="LuCI.form.FlagValue.html#width">width</a></li>
789             
790             </ul>
791             <ul class="typedefs itemMembers">
792             
793             </ul>
794             <ul class="typedefs itemMembers">
795             
796             </ul>
797             <ul class="methods itemMembers">
798             
799             <span class="subtitle">Methods</span>
800             
801                 <li data-name="LuCI.form.FlagValue#append"><a href="LuCI.form.FlagValue.html#append">append</a></li>
802             
803                 <li data-name="LuCI.form.FlagValue#cbid"><a href="LuCI.form.FlagValue.html#cbid">cbid</a></li>
804             
805                 <li data-name="LuCI.form.FlagValue#cfgvalue"><a href="LuCI.form.FlagValue.html#cfgvalue">cfgvalue</a></li>
806             
807                 <li data-name="LuCI.form.FlagValue#depends"><a href="LuCI.form.FlagValue.html#depends">depends</a></li>
808             
809                 <li data-name="LuCI.form.FlagValue#formvalue"><a href="LuCI.form.FlagValue.html#formvalue">formvalue</a></li>
810             
811                 <li data-name="LuCI.form.FlagValue#getUIElement"><a href="LuCI.form.FlagValue.html#getUIElement">getUIElement</a></li>
812             
813                 <li data-name="LuCI.form.FlagValue#isActive"><a href="LuCI.form.FlagValue.html#isActive">isActive</a></li>
814             
815                 <li data-name="LuCI.form.FlagValue#isValid"><a href="LuCI.form.FlagValue.html#isValid">isValid</a></li>
816             
817                 <li data-name="LuCI.form.FlagValue#load"><a href="LuCI.form.FlagValue.html#load">load</a></li>
818             
819                 <li data-name="LuCI.form.FlagValue#parse"><a href="LuCI.form.FlagValue.html#parse">parse</a></li>
820             
821                 <li data-name="LuCI.form.FlagValue#remove"><a href="LuCI.form.FlagValue.html#remove">remove</a></li>
822             
823                 <li data-name="LuCI.form.FlagValue#stripTags"><a href="LuCI.form.FlagValue.html#stripTags">stripTags</a></li>
824             
825                 <li data-name="LuCI.form.FlagValue#textvalue"><a href="LuCI.form.FlagValue.html#textvalue">textvalue</a></li>
826             
827                 <li data-name="LuCI.form.FlagValue#titleFn"><a href="LuCI.form.FlagValue.html#titleFn">titleFn</a></li>
828             
829                 <li data-name="LuCI.form.FlagValue#value"><a href="LuCI.form.FlagValue.html#value">value</a></li>
830             
831                 <li data-name="LuCI.form.FlagValue#write"><a href="LuCI.form.FlagValue.html#write">write</a></li>
832             
833             </ul>
834             <ul class="events itemMembers">
835             
836             </ul>
837         </li>
838     
839         <li class="item" data-name="LuCI.form.GridSection">
840             <span class="title">
841                 <a href="LuCI.form.GridSection.html">LuCI.form.GridSection</a>
842                 
843             </span>
844             <ul class="members itemMembers">
845             
846             <span class="subtitle">Members</span>
847             
848                 <li data-name="LuCI.form.GridSection#addbtntitle"><a href="LuCI.form.GridSection.html#addbtntitle">addbtntitle</a></li>
849             
850                 <li data-name="LuCI.form.GridSection#addremove"><a href="LuCI.form.GridSection.html#addremove">addremove</a></li>
851             
852                 <li data-name="LuCI.form.GridSection#anonymous"><a href="LuCI.form.GridSection.html#anonymous">anonymous</a></li>
853             
854                 <li data-name="LuCI.form.GridSection#extedit"><a href="LuCI.form.GridSection.html#extedit">extedit</a></li>
855             
856                 <li data-name="LuCI.form.GridSection#max_cols"><a href="LuCI.form.GridSection.html#max_cols">max_cols</a></li>
857             
858                 <li data-name="LuCI.form.GridSection#modaltitle"><a href="LuCI.form.GridSection.html#modaltitle">modaltitle</a></li>
859             
860                 <li data-name="LuCI.form.GridSection#parentoption"><a href="LuCI.form.GridSection.html#parentoption">parentoption</a></li>
861             
862                 <li data-name="LuCI.form.GridSection#rowcolors"><a href="LuCI.form.GridSection.html#rowcolors">rowcolors</a></li>
863             
864                 <li data-name="LuCI.form.GridSection#sectiontitle"><a href="LuCI.form.GridSection.html#sectiontitle">sectiontitle</a></li>
865             
866                 <li data-name="LuCI.form.GridSection#sortable"><a href="LuCI.form.GridSection.html#sortable">sortable</a></li>
867             
868                 <li data-name="LuCI.form.GridSection#tabbed"><a href="LuCI.form.GridSection.html#tabbed">tabbed</a></li>
869             
870                 <li data-name="LuCI.form.GridSection#uciconfig"><a href="LuCI.form.GridSection.html#uciconfig">uciconfig</a></li>
871             
872             </ul>
873             <ul class="typedefs itemMembers">
874             
875             </ul>
876             <ul class="typedefs itemMembers">
877             
878             </ul>
879             <ul class="methods itemMembers">
880             
881             <span class="subtitle">Methods</span>
882             
883                 <li data-name="LuCI.form.GridSection#addModalOptions"><a href="LuCI.form.GridSection.html#addModalOptions">addModalOptions</a></li>
884             
885                 <li data-name="LuCI.form.GridSection#append"><a href="LuCI.form.GridSection.html#append">append</a></li>
886             
887                 <li data-name="LuCI.form.GridSection#filter"><a href="LuCI.form.GridSection.html#filter">filter</a></li>
888             
889                 <li data-name="LuCI.form.GridSection#load"><a href="LuCI.form.GridSection.html#load">load</a></li>
890             
891                 <li data-name="LuCI.form.GridSection#option"><a href="LuCI.form.GridSection.html#option">option</a></li>
892             
893                 <li data-name="LuCI.form.GridSection#parse"><a href="LuCI.form.GridSection.html#parse">parse</a></li>
894             
895                 <li data-name="LuCI.form.GridSection#stripTags"><a href="LuCI.form.GridSection.html#stripTags">stripTags</a></li>
896             
897                 <li data-name="LuCI.form.GridSection#tab"><a href="LuCI.form.GridSection.html#tab">tab</a></li>
898             
899                 <li data-name="LuCI.form.GridSection#taboption"><a href="LuCI.form.GridSection.html#taboption">taboption</a></li>
900             
901                 <li data-name="LuCI.form.GridSection#titleFn"><a href="LuCI.form.GridSection.html#titleFn">titleFn</a></li>
902             
903             </ul>
904             <ul class="events itemMembers">
905             
906             </ul>
907         </li>
908     
909         <li class="item" data-name="LuCI.form.HiddenValue">
910             <span class="title">
911                 <a href="LuCI.form.HiddenValue.html">LuCI.form.HiddenValue</a>
912                 
913             </span>
914             <ul class="members itemMembers">
915             
916             <span class="subtitle">Members</span>
917             
918                 <li data-name="LuCI.form.HiddenValue#datatype"><a href="LuCI.form.HiddenValue.html#datatype">datatype</a></li>
919             
920                 <li data-name="LuCI.form.HiddenValue#default"><a href="LuCI.form.HiddenValue.html#default">default</a></li>
921             
922                 <li data-name="LuCI.form.HiddenValue#editable"><a href="LuCI.form.HiddenValue.html#editable">editable</a></li>
923             
924                 <li data-name="LuCI.form.HiddenValue#modalonly"><a href="LuCI.form.HiddenValue.html#modalonly">modalonly</a></li>
925             
926                 <li data-name="LuCI.form.HiddenValue#optional"><a href="LuCI.form.HiddenValue.html#optional">optional</a></li>
927             
928                 <li data-name="LuCI.form.HiddenValue#password"><a href="LuCI.form.HiddenValue.html#password">password</a></li>
929             
930                 <li data-name="LuCI.form.HiddenValue#placeholder"><a href="LuCI.form.HiddenValue.html#placeholder">placeholder</a></li>
931             
932                 <li data-name="LuCI.form.HiddenValue#rmempty"><a href="LuCI.form.HiddenValue.html#rmempty">rmempty</a></li>
933             
934                 <li data-name="LuCI.form.HiddenValue#uciconfig"><a href="LuCI.form.HiddenValue.html#uciconfig">uciconfig</a></li>
935             
936                 <li data-name="LuCI.form.HiddenValue#ucioption"><a href="LuCI.form.HiddenValue.html#ucioption">ucioption</a></li>
937             
938                 <li data-name="LuCI.form.HiddenValue#ucisection"><a href="LuCI.form.HiddenValue.html#ucisection">ucisection</a></li>
939             
940                 <li data-name="LuCI.form.HiddenValue#validate"><a href="LuCI.form.HiddenValue.html#validate">validate</a></li>
941             
942                 <li data-name="LuCI.form.HiddenValue#width"><a href="LuCI.form.HiddenValue.html#width">width</a></li>
943             
944             </ul>
945             <ul class="typedefs itemMembers">
946             
947             </ul>
948             <ul class="typedefs itemMembers">
949             
950             </ul>
951             <ul class="methods itemMembers">
952             
953             <span class="subtitle">Methods</span>
954             
955                 <li data-name="LuCI.form.HiddenValue#append"><a href="LuCI.form.HiddenValue.html#append">append</a></li>
956             
957                 <li data-name="LuCI.form.HiddenValue#cbid"><a href="LuCI.form.HiddenValue.html#cbid">cbid</a></li>
958             
959                 <li data-name="LuCI.form.HiddenValue#cfgvalue"><a href="LuCI.form.HiddenValue.html#cfgvalue">cfgvalue</a></li>
960             
961                 <li data-name="LuCI.form.HiddenValue#depends"><a href="LuCI.form.HiddenValue.html#depends">depends</a></li>
962             
963                 <li data-name="LuCI.form.HiddenValue#formvalue"><a href="LuCI.form.HiddenValue.html#formvalue">formvalue</a></li>
964             
965                 <li data-name="LuCI.form.HiddenValue#getUIElement"><a href="LuCI.form.HiddenValue.html#getUIElement">getUIElement</a></li>
966             
967                 <li data-name="LuCI.form.HiddenValue#isActive"><a href="LuCI.form.HiddenValue.html#isActive">isActive</a></li>
968             
969                 <li data-name="LuCI.form.HiddenValue#isValid"><a href="LuCI.form.HiddenValue.html#isValid">isValid</a></li>
970             
971                 <li data-name="LuCI.form.HiddenValue#load"><a href="LuCI.form.HiddenValue.html#load">load</a></li>
972             
973                 <li data-name="LuCI.form.HiddenValue#parse"><a href="LuCI.form.HiddenValue.html#parse">parse</a></li>
974             
975                 <li data-name="LuCI.form.HiddenValue#remove"><a href="LuCI.form.HiddenValue.html#remove">remove</a></li>
976             
977                 <li data-name="LuCI.form.HiddenValue#stripTags"><a href="LuCI.form.HiddenValue.html#stripTags">stripTags</a></li>
978             
979                 <li data-name="LuCI.form.HiddenValue#textvalue"><a href="LuCI.form.HiddenValue.html#textvalue">textvalue</a></li>
980             
981                 <li data-name="LuCI.form.HiddenValue#titleFn"><a href="LuCI.form.HiddenValue.html#titleFn">titleFn</a></li>
982             
983                 <li data-name="LuCI.form.HiddenValue#value"><a href="LuCI.form.HiddenValue.html#value">value</a></li>
984             
985                 <li data-name="LuCI.form.HiddenValue#write"><a href="LuCI.form.HiddenValue.html#write">write</a></li>
986             
987             </ul>
988             <ul class="events itemMembers">
989             
990             </ul>
991         </li>
992     
993         <li class="item" data-name="LuCI.form.JSONMap">
994             <span class="title">
995                 <a href="LuCI.form.JSONMap.html">LuCI.form.JSONMap</a>
996                 
997             </span>
998             <ul class="members itemMembers">
999             
1000             </ul>
1001             <ul class="typedefs itemMembers">
1002             
1003             </ul>
1004             <ul class="typedefs itemMembers">
1005             
1006             </ul>
1007             <ul class="methods itemMembers">
1008             
1009             <span class="subtitle">Methods</span>
1010             
1011                 <li data-name="LuCI.form.JSONMap#append"><a href="LuCI.form.JSONMap.html#append">append</a></li>
1012             
1013                 <li data-name="LuCI.form.JSONMap#chain"><a href="LuCI.form.JSONMap.html#chain">chain</a></li>
1014             
1015                 <li data-name="LuCI.form.JSONMap#findElement"><a href="LuCI.form.JSONMap.html#findElement">findElement</a></li>
1016             
1017                 <li data-name="LuCI.form.JSONMap#findElements"><a href="LuCI.form.JSONMap.html#findElements">findElements</a></li>
1018             
1019                 <li data-name="LuCI.form.JSONMap#load"><a href="LuCI.form.JSONMap.html#load">load</a></li>
1020             
1021                 <li data-name="LuCI.form.JSONMap#lookupOption"><a href="LuCI.form.JSONMap.html#lookupOption">lookupOption</a></li>
1022             
1023                 <li data-name="LuCI.form.JSONMap#parse"><a href="LuCI.form.JSONMap.html#parse">parse</a></li>
1024             
1025                 <li data-name="LuCI.form.JSONMap#render"><a href="LuCI.form.JSONMap.html#render">render</a></li>
1026             
1027                 <li data-name="LuCI.form.JSONMap#reset"><a href="LuCI.form.JSONMap.html#reset">reset</a></li>
1028             
1029                 <li data-name="LuCI.form.JSONMap#save"><a href="LuCI.form.JSONMap.html#save">save</a></li>
1030             
1031                 <li data-name="LuCI.form.JSONMap#section"><a href="LuCI.form.JSONMap.html#section">section</a></li>
1032             
1033                 <li data-name="LuCI.form.JSONMap#stripTags"><a href="LuCI.form.JSONMap.html#stripTags">stripTags</a></li>
1034             
1035                 <li data-name="LuCI.form.JSONMap#titleFn"><a href="LuCI.form.JSONMap.html#titleFn">titleFn</a></li>
1036             
1037             </ul>
1038             <ul class="events itemMembers">
1039             
1040             </ul>
1041         </li>
1042     
1043         <li class="item" data-name="LuCI.form.ListValue">
1044             <span class="title">
1045                 <a href="LuCI.form.ListValue.html">LuCI.form.ListValue</a>
1046                 
1047             </span>
1048             <ul class="members itemMembers">
1049             
1050             <span class="subtitle">Members</span>
1051             
1052                 <li data-name="LuCI.form.ListValue##size"><a href="LuCI.form.ListValue.html#size">size</a></li>
1053             
1054                 <li data-name="LuCI.form.ListValue#datatype"><a href="LuCI.form.ListValue.html#datatype">datatype</a></li>
1055             
1056                 <li data-name="LuCI.form.ListValue#default"><a href="LuCI.form.ListValue.html#default">default</a></li>
1057             
1058                 <li data-name="LuCI.form.ListValue#editable"><a href="LuCI.form.ListValue.html#editable">editable</a></li>
1059             
1060                 <li data-name="LuCI.form.ListValue#modalonly"><a href="LuCI.form.ListValue.html#modalonly">modalonly</a></li>
1061             
1062                 <li data-name="LuCI.form.ListValue#optional"><a href="LuCI.form.ListValue.html#optional">optional</a></li>
1063             
1064                 <li data-name="LuCI.form.ListValue#password"><a href="LuCI.form.ListValue.html#password">password</a></li>
1065             
1066                 <li data-name="LuCI.form.ListValue#placeholder"><a href="LuCI.form.ListValue.html#placeholder">placeholder</a></li>
1067             
1068                 <li data-name="LuCI.form.ListValue#rmempty"><a href="LuCI.form.ListValue.html#rmempty">rmempty</a></li>
1069             
1070                 <li data-name="LuCI.form.ListValue#uciconfig"><a href="LuCI.form.ListValue.html#uciconfig">uciconfig</a></li>
1071             
1072                 <li data-name="LuCI.form.ListValue#ucioption"><a href="LuCI.form.ListValue.html#ucioption">ucioption</a></li>
1073             
1074                 <li data-name="LuCI.form.ListValue#ucisection"><a href="LuCI.form.ListValue.html#ucisection">ucisection</a></li>
1075             
1076                 <li data-name="LuCI.form.ListValue#validate"><a href="LuCI.form.ListValue.html#validate">validate</a></li>
1077             
1078                 <li data-name="LuCI.form.ListValue#width"><a href="LuCI.form.ListValue.html#width">width</a></li>
1079             
1080             </ul>
1081             <ul class="typedefs itemMembers">
1082             
1083             </ul>
1084             <ul class="typedefs itemMembers">
1085             
1086             </ul>
1087             <ul class="methods itemMembers">
1088             
1089             <span class="subtitle">Methods</span>
1090             
1091                 <li data-name="LuCI.form.ListValue#append"><a href="LuCI.form.ListValue.html#append">append</a></li>
1092             
1093                 <li data-name="LuCI.form.ListValue#cbid"><a href="LuCI.form.ListValue.html#cbid">cbid</a></li>
1094             
1095                 <li data-name="LuCI.form.ListValue#cfgvalue"><a href="LuCI.form.ListValue.html#cfgvalue">cfgvalue</a></li>
1096             
1097                 <li data-name="LuCI.form.ListValue#depends"><a href="LuCI.form.ListValue.html#depends">depends</a></li>
1098             
1099                 <li data-name="LuCI.form.ListValue#formvalue"><a href="LuCI.form.ListValue.html#formvalue">formvalue</a></li>
1100             
1101                 <li data-name="LuCI.form.ListValue#getUIElement"><a href="LuCI.form.ListValue.html#getUIElement">getUIElement</a></li>
1102             
1103                 <li data-name="LuCI.form.ListValue#isActive"><a href="LuCI.form.ListValue.html#isActive">isActive</a></li>
1104             
1105                 <li data-name="LuCI.form.ListValue#isValid"><a href="LuCI.form.ListValue.html#isValid">isValid</a></li>
1106             
1107                 <li data-name="LuCI.form.ListValue#load"><a href="LuCI.form.ListValue.html#load">load</a></li>
1108             
1109                 <li data-name="LuCI.form.ListValue#parse"><a href="LuCI.form.ListValue.html#parse">parse</a></li>
1110             
1111                 <li data-name="LuCI.form.ListValue#remove"><a href="LuCI.form.ListValue.html#remove">remove</a></li>
1112             
1113                 <li data-name="LuCI.form.ListValue#stripTags"><a href="LuCI.form.ListValue.html#stripTags">stripTags</a></li>
1114             
1115                 <li data-name="LuCI.form.ListValue#textvalue"><a href="LuCI.form.ListValue.html#textvalue">textvalue</a></li>
1116             
1117                 <li data-name="LuCI.form.ListValue#titleFn"><a href="LuCI.form.ListValue.html#titleFn">titleFn</a></li>
1118             
1119                 <li data-name="LuCI.form.ListValue#value"><a href="LuCI.form.ListValue.html#value">value</a></li>
1120             
1121                 <li data-name="LuCI.form.ListValue#write"><a href="LuCI.form.ListValue.html#write">write</a></li>
1122             
1123             </ul>
1124             <ul class="events itemMembers">
1125             
1126             </ul>
1127         </li>
1128     
1129         <li class="item" data-name="LuCI.form.Map">
1130             <span class="title">
1131                 <a href="LuCI.form.Map.html">LuCI.form.Map</a>
1132                 
1133             </span>
1134             <ul class="members itemMembers">
1135             
1136             </ul>
1137             <ul class="typedefs itemMembers">
1138             
1139             </ul>
1140             <ul class="typedefs itemMembers">
1141             
1142             </ul>
1143             <ul class="methods itemMembers">
1144             
1145             <span class="subtitle">Methods</span>
1146             
1147                 <li data-name="LuCI.form.Map#append"><a href="LuCI.form.Map.html#append">append</a></li>
1148             
1149                 <li data-name="LuCI.form.Map#chain"><a href="LuCI.form.Map.html#chain">chain</a></li>
1150             
1151                 <li data-name="LuCI.form.Map#findElement"><a href="LuCI.form.Map.html#findElement">findElement</a></li>
1152             
1153                 <li data-name="LuCI.form.Map#findElements"><a href="LuCI.form.Map.html#findElements">findElements</a></li>
1154             
1155                 <li data-name="LuCI.form.Map#load"><a href="LuCI.form.Map.html#load">load</a></li>
1156             
1157                 <li data-name="LuCI.form.Map#lookupOption"><a href="LuCI.form.Map.html#lookupOption">lookupOption</a></li>
1158             
1159                 <li data-name="LuCI.form.Map#parse"><a href="LuCI.form.Map.html#parse">parse</a></li>
1160             
1161                 <li data-name="LuCI.form.Map#render"><a href="LuCI.form.Map.html#render">render</a></li>
1162             
1163                 <li data-name="LuCI.form.Map#reset"><a href="LuCI.form.Map.html#reset">reset</a></li>
1164             
1165                 <li data-name="LuCI.form.Map#save"><a href="LuCI.form.Map.html#save">save</a></li>
1166             
1167                 <li data-name="LuCI.form.Map#section"><a href="LuCI.form.Map.html#section">section</a></li>
1168             
1169                 <li data-name="LuCI.form.Map#stripTags"><a href="LuCI.form.Map.html#stripTags">stripTags</a></li>
1170             
1171                 <li data-name="LuCI.form.Map#titleFn"><a href="LuCI.form.Map.html#titleFn">titleFn</a></li>
1172             
1173             </ul>
1174             <ul class="events itemMembers">
1175             
1176             </ul>
1177         </li>
1178     
1179         <li class="item" data-name="LuCI.form.MultiValue">
1180             <span class="title">
1181                 <a href="LuCI.form.MultiValue.html">LuCI.form.MultiValue</a>
1182                 
1183             </span>
1184             <ul class="members itemMembers">
1185             
1186             <span class="subtitle">Members</span>
1187             
1188                 <li data-name="LuCI.form.MultiValue##display_size"><a href="LuCI.form.MultiValue.html#display_size">display_size</a></li>
1189             
1190                 <li data-name="LuCI.form.MultiValue##dropdown_size"><a href="LuCI.form.MultiValue.html#dropdown_size">dropdown_size</a></li>
1191             
1192                 <li data-name="LuCI.form.MultiValue#datatype"><a href="LuCI.form.MultiValue.html#datatype">datatype</a></li>
1193             
1194                 <li data-name="LuCI.form.MultiValue#default"><a href="LuCI.form.MultiValue.html#default">default</a></li>
1195             
1196                 <li data-name="LuCI.form.MultiValue#editable"><a href="LuCI.form.MultiValue.html#editable">editable</a></li>
1197             
1198                 <li data-name="LuCI.form.MultiValue#modalonly"><a href="LuCI.form.MultiValue.html#modalonly">modalonly</a></li>
1199             
1200                 <li data-name="LuCI.form.MultiValue#optional"><a href="LuCI.form.MultiValue.html#optional">optional</a></li>
1201             
1202                 <li data-name="LuCI.form.MultiValue#password"><a href="LuCI.form.MultiValue.html#password">password</a></li>
1203             
1204                 <li data-name="LuCI.form.MultiValue#placeholder"><a href="LuCI.form.MultiValue.html#placeholder">placeholder</a></li>
1205             
1206                 <li data-name="LuCI.form.MultiValue#rmempty"><a href="LuCI.form.MultiValue.html#rmempty">rmempty</a></li>
1207             
1208                 <li data-name="LuCI.form.MultiValue#uciconfig"><a href="LuCI.form.MultiValue.html#uciconfig">uciconfig</a></li>
1209             
1210                 <li data-name="LuCI.form.MultiValue#ucioption"><a href="LuCI.form.MultiValue.html#ucioption">ucioption</a></li>
1211             
1212                 <li data-name="LuCI.form.MultiValue#ucisection"><a href="LuCI.form.MultiValue.html#ucisection">ucisection</a></li>
1213             
1214                 <li data-name="LuCI.form.MultiValue#validate"><a href="LuCI.form.MultiValue.html#validate">validate</a></li>
1215             
1216                 <li data-name="LuCI.form.MultiValue#width"><a href="LuCI.form.MultiValue.html#width">width</a></li>
1217             
1218             </ul>
1219             <ul class="typedefs itemMembers">
1220             
1221             </ul>
1222             <ul class="typedefs itemMembers">
1223             
1224             </ul>
1225             <ul class="methods itemMembers">
1226             
1227             <span class="subtitle">Methods</span>
1228             
1229                 <li data-name="LuCI.form.MultiValue#append"><a href="LuCI.form.MultiValue.html#append">append</a></li>
1230             
1231                 <li data-name="LuCI.form.MultiValue#cbid"><a href="LuCI.form.MultiValue.html#cbid">cbid</a></li>
1232             
1233                 <li data-name="LuCI.form.MultiValue#cfgvalue"><a href="LuCI.form.MultiValue.html#cfgvalue">cfgvalue</a></li>
1234             
1235                 <li data-name="LuCI.form.MultiValue#depends"><a href="LuCI.form.MultiValue.html#depends">depends</a></li>
1236             
1237                 <li data-name="LuCI.form.MultiValue#formvalue"><a href="LuCI.form.MultiValue.html#formvalue">formvalue</a></li>
1238             
1239                 <li data-name="LuCI.form.MultiValue#getUIElement"><a href="LuCI.form.MultiValue.html#getUIElement">getUIElement</a></li>
1240             
1241                 <li data-name="LuCI.form.MultiValue#isActive"><a href="LuCI.form.MultiValue.html#isActive">isActive</a></li>
1242             
1243                 <li data-name="LuCI.form.MultiValue#isValid"><a href="LuCI.form.MultiValue.html#isValid">isValid</a></li>
1244             
1245                 <li data-name="LuCI.form.MultiValue#load"><a href="LuCI.form.MultiValue.html#load">load</a></li>
1246             
1247                 <li data-name="LuCI.form.MultiValue#parse"><a href="LuCI.form.MultiValue.html#parse">parse</a></li>
1248             
1249                 <li data-name="LuCI.form.MultiValue#remove"><a href="LuCI.form.MultiValue.html#remove">remove</a></li>
1250             
1251                 <li data-name="LuCI.form.MultiValue#stripTags"><a href="LuCI.form.MultiValue.html#stripTags">stripTags</a></li>
1252             
1253                 <li data-name="LuCI.form.MultiValue#textvalue"><a href="LuCI.form.MultiValue.html#textvalue">textvalue</a></li>
1254             
1255                 <li data-name="LuCI.form.MultiValue#titleFn"><a href="LuCI.form.MultiValue.html#titleFn">titleFn</a></li>
1256             
1257                 <li data-name="LuCI.form.MultiValue#value"><a href="LuCI.form.MultiValue.html#value">value</a></li>
1258             
1259                 <li data-name="LuCI.form.MultiValue#write"><a href="LuCI.form.MultiValue.html#write">write</a></li>
1260             
1261             </ul>
1262             <ul class="events itemMembers">
1263             
1264             </ul>
1265         </li>
1266     
1267         <li class="item" data-name="LuCI.form.NamedSection">
1268             <span class="title">
1269                 <a href="LuCI.form.NamedSection.html">LuCI.form.NamedSection</a>
1270                 
1271             </span>
1272             <ul class="members itemMembers">
1273             
1274             <span class="subtitle">Members</span>
1275             
1276                 <li data-name="LuCI.form.NamedSection##addremove"><a href="LuCI.form.NamedSection.html#addremove">addremove</a></li>
1277             
1278                 <li data-name="LuCI.form.NamedSection##uciconfig"><a href="LuCI.form.NamedSection.html#uciconfig">uciconfig</a></li>
1279             
1280                 <li data-name="LuCI.form.NamedSection#parentoption"><a href="LuCI.form.NamedSection.html#parentoption">parentoption</a></li>
1281             
1282             </ul>
1283             <ul class="typedefs itemMembers">
1284             
1285             </ul>
1286             <ul class="typedefs itemMembers">
1287             
1288             </ul>
1289             <ul class="methods itemMembers">
1290             
1291             <span class="subtitle">Methods</span>
1292             
1293                 <li data-name="LuCI.form.NamedSection#append"><a href="LuCI.form.NamedSection.html#append">append</a></li>
1294             
1295                 <li data-name="LuCI.form.NamedSection#cfgsections"><a href="LuCI.form.NamedSection.html#cfgsections">cfgsections</a></li>
1296             
1297                 <li data-name="LuCI.form.NamedSection#filter"><a href="LuCI.form.NamedSection.html#filter">filter</a></li>
1298             
1299                 <li data-name="LuCI.form.NamedSection#load"><a href="LuCI.form.NamedSection.html#load">load</a></li>
1300             
1301                 <li data-name="LuCI.form.NamedSection#option"><a href="LuCI.form.NamedSection.html#option">option</a></li>
1302             
1303                 <li data-name="LuCI.form.NamedSection#parse"><a href="LuCI.form.NamedSection.html#parse">parse</a></li>
1304             
1305                 <li data-name="LuCI.form.NamedSection#render"><a href="LuCI.form.NamedSection.html#render">render</a></li>
1306             
1307                 <li data-name="LuCI.form.NamedSection#stripTags"><a href="LuCI.form.NamedSection.html#stripTags">stripTags</a></li>
1308             
1309                 <li data-name="LuCI.form.NamedSection#tab"><a href="LuCI.form.NamedSection.html#tab">tab</a></li>
1310             
1311                 <li data-name="LuCI.form.NamedSection#taboption"><a href="LuCI.form.NamedSection.html#taboption">taboption</a></li>
1312             
1313                 <li data-name="LuCI.form.NamedSection#titleFn"><a href="LuCI.form.NamedSection.html#titleFn">titleFn</a></li>
1314             
1315             </ul>
1316             <ul class="events itemMembers">
1317             
1318             </ul>
1319         </li>
1320     
1321         <li class="item" data-name="LuCI.form.SectionValue">
1322             <span class="title">
1323                 <a href="LuCI.form.SectionValue.html">LuCI.form.SectionValue</a>
1324                 
1325             </span>
1326             <ul class="members itemMembers">
1327             
1328             <span class="subtitle">Members</span>
1329             
1330                 <li data-name="LuCI.form.SectionValue##subsection"><a href="LuCI.form.SectionValue.html#subsection">subsection</a></li>
1331             
1332                 <li data-name="LuCI.form.SectionValue#datatype"><a href="LuCI.form.SectionValue.html#datatype">datatype</a></li>
1333             
1334                 <li data-name="LuCI.form.SectionValue#default"><a href="LuCI.form.SectionValue.html#default">default</a></li>
1335             
1336                 <li data-name="LuCI.form.SectionValue#editable"><a href="LuCI.form.SectionValue.html#editable">editable</a></li>
1337             
1338                 <li data-name="LuCI.form.SectionValue#modalonly"><a href="LuCI.form.SectionValue.html#modalonly">modalonly</a></li>
1339             
1340                 <li data-name="LuCI.form.SectionValue#optional"><a href="LuCI.form.SectionValue.html#optional">optional</a></li>
1341             
1342                 <li data-name="LuCI.form.SectionValue#password"><a href="LuCI.form.SectionValue.html#password">password</a></li>
1343             
1344                 <li data-name="LuCI.form.SectionValue#placeholder"><a href="LuCI.form.SectionValue.html#placeholder">placeholder</a></li>
1345             
1346                 <li data-name="LuCI.form.SectionValue#rmempty"><a href="LuCI.form.SectionValue.html#rmempty">rmempty</a></li>
1347             
1348                 <li data-name="LuCI.form.SectionValue#uciconfig"><a href="LuCI.form.SectionValue.html#uciconfig">uciconfig</a></li>
1349             
1350                 <li data-name="LuCI.form.SectionValue#ucioption"><a href="LuCI.form.SectionValue.html#ucioption">ucioption</a></li>
1351             
1352                 <li data-name="LuCI.form.SectionValue#ucisection"><a href="LuCI.form.SectionValue.html#ucisection">ucisection</a></li>
1353             
1354                 <li data-name="LuCI.form.SectionValue#validate"><a href="LuCI.form.SectionValue.html#validate">validate</a></li>
1355             
1356                 <li data-name="LuCI.form.SectionValue#width"><a href="LuCI.form.SectionValue.html#width">width</a></li>
1357             
1358             </ul>
1359             <ul class="typedefs itemMembers">
1360             
1361             </ul>
1362             <ul class="typedefs itemMembers">
1363             
1364             </ul>
1365             <ul class="methods itemMembers">
1366             
1367             <span class="subtitle">Methods</span>
1368             
1369                 <li data-name="LuCI.form.SectionValue#append"><a href="LuCI.form.SectionValue.html#append">append</a></li>
1370             
1371                 <li data-name="LuCI.form.SectionValue#cbid"><a href="LuCI.form.SectionValue.html#cbid">cbid</a></li>
1372             
1373                 <li data-name="LuCI.form.SectionValue#cfgvalue"><a href="LuCI.form.SectionValue.html#cfgvalue">cfgvalue</a></li>
1374             
1375                 <li data-name="LuCI.form.SectionValue#depends"><a href="LuCI.form.SectionValue.html#depends">depends</a></li>
1376             
1377                 <li data-name="LuCI.form.SectionValue#formvalue"><a href="LuCI.form.SectionValue.html#formvalue">formvalue</a></li>
1378             
1379                 <li data-name="LuCI.form.SectionValue#getUIElement"><a href="LuCI.form.SectionValue.html#getUIElement">getUIElement</a></li>
1380             
1381                 <li data-name="LuCI.form.SectionValue#isActive"><a href="LuCI.form.SectionValue.html#isActive">isActive</a></li>
1382             
1383                 <li data-name="LuCI.form.SectionValue#isValid"><a href="LuCI.form.SectionValue.html#isValid">isValid</a></li>
1384             
1385                 <li data-name="LuCI.form.SectionValue#load"><a href="LuCI.form.SectionValue.html#load">load</a></li>
1386             
1387                 <li data-name="LuCI.form.SectionValue#parse"><a href="LuCI.form.SectionValue.html#parse">parse</a></li>
1388             
1389                 <li data-name="LuCI.form.SectionValue#remove"><a href="LuCI.form.SectionValue.html#remove">remove</a></li>
1390             
1391                 <li data-name="LuCI.form.SectionValue#stripTags"><a href="LuCI.form.SectionValue.html#stripTags">stripTags</a></li>
1392             
1393                 <li data-name="LuCI.form.SectionValue#textvalue"><a href="LuCI.form.SectionValue.html#textvalue">textvalue</a></li>
1394             
1395                 <li data-name="LuCI.form.SectionValue#titleFn"><a href="LuCI.form.SectionValue.html#titleFn">titleFn</a></li>
1396             
1397                 <li data-name="LuCI.form.SectionValue#value"><a href="LuCI.form.SectionValue.html#value">value</a></li>
1398             
1399                 <li data-name="LuCI.form.SectionValue#write"><a href="LuCI.form.SectionValue.html#write">write</a></li>
1400             
1401             </ul>
1402             <ul class="events itemMembers">
1403             
1404             </ul>
1405         </li>
1406     
1407         <li class="item" data-name="LuCI.form.TableSection">
1408             <span class="title">
1409                 <a href="LuCI.form.TableSection.html">LuCI.form.TableSection</a>
1410                 
1411             </span>
1412             <ul class="members itemMembers">
1413             
1414             <span class="subtitle">Members</span>
1415             
1416                 <li data-name="LuCI.form.TableSection##addbtntitle"><a href="LuCI.form.TableSection.html#addbtntitle">addbtntitle</a></li>
1417             
1418                 <li data-name="LuCI.form.TableSection##addremove"><a href="LuCI.form.TableSection.html#addremove">addremove</a></li>
1419             
1420                 <li data-name="LuCI.form.TableSection##anonymous"><a href="LuCI.form.TableSection.html#anonymous">anonymous</a></li>
1421             
1422                 <li data-name="LuCI.form.TableSection##extedit"><a href="LuCI.form.TableSection.html#extedit">extedit</a></li>
1423             
1424                 <li data-name="LuCI.form.TableSection##max_cols"><a href="LuCI.form.TableSection.html#max_cols">max_cols</a></li>
1425             
1426                 <li data-name="LuCI.form.TableSection##modaltitle"><a href="LuCI.form.TableSection.html#modaltitle">modaltitle</a></li>
1427             
1428                 <li data-name="LuCI.form.TableSection##rowcolors"><a href="LuCI.form.TableSection.html#rowcolors">rowcolors</a></li>
1429             
1430                 <li data-name="LuCI.form.TableSection##sectiontitle"><a href="LuCI.form.TableSection.html#sectiontitle">sectiontitle</a></li>
1431             
1432                 <li data-name="LuCI.form.TableSection##sortable"><a href="LuCI.form.TableSection.html#sortable">sortable</a></li>
1433             
1434                 <li data-name="LuCI.form.TableSection##uciconfig"><a href="LuCI.form.TableSection.html#uciconfig">uciconfig</a></li>
1435             
1436                 <li data-name="LuCI.form.TableSection#addbtntitle"><a href="LuCI.form.TableSection.html#addbtntitle">addbtntitle</a></li>
1437             
1438                 <li data-name="LuCI.form.TableSection#addremove"><a href="LuCI.form.TableSection.html#addremove">addremove</a></li>
1439             
1440                 <li data-name="LuCI.form.TableSection#anonymous"><a href="LuCI.form.TableSection.html#anonymous">anonymous</a></li>
1441             
1442                 <li data-name="LuCI.form.TableSection#parentoption"><a href="LuCI.form.TableSection.html#parentoption">parentoption</a></li>
1443             
1444                 <li data-name="LuCI.form.TableSection#tabbed"><a href="LuCI.form.TableSection.html#tabbed">tabbed</a></li>
1445             
1446                 <li data-name="LuCI.form.TableSection#uciconfig"><a href="LuCI.form.TableSection.html#uciconfig">uciconfig</a></li>
1447             
1448             </ul>
1449             <ul class="typedefs itemMembers">
1450             
1451             </ul>
1452             <ul class="typedefs itemMembers">
1453             
1454             </ul>
1455             <ul class="methods itemMembers">
1456             
1457             <span class="subtitle">Methods</span>
1458             
1459                 <li data-name="LuCI.form.TableSection#addModalOptions"><a href="LuCI.form.TableSection.html#addModalOptions">addModalOptions</a></li>
1460             
1461                 <li data-name="LuCI.form.TableSection#append"><a href="LuCI.form.TableSection.html#append">append</a></li>
1462             
1463                 <li data-name="LuCI.form.TableSection#filter"><a href="LuCI.form.TableSection.html#filter">filter</a></li>
1464             
1465                 <li data-name="LuCI.form.TableSection#load"><a href="LuCI.form.TableSection.html#load">load</a></li>
1466             
1467                 <li data-name="LuCI.form.TableSection#option"><a href="LuCI.form.TableSection.html#option">option</a></li>
1468             
1469                 <li data-name="LuCI.form.TableSection#parse"><a href="LuCI.form.TableSection.html#parse">parse</a></li>
1470             
1471                 <li data-name="LuCI.form.TableSection#stripTags"><a href="LuCI.form.TableSection.html#stripTags">stripTags</a></li>
1472             
1473                 <li data-name="LuCI.form.TableSection#tab"><a href="LuCI.form.TableSection.html#tab">tab</a></li>
1474             
1475                 <li data-name="LuCI.form.TableSection#taboption"><a href="LuCI.form.TableSection.html#taboption">taboption</a></li>
1476             
1477                 <li data-name="LuCI.form.TableSection#titleFn"><a href="LuCI.form.TableSection.html#titleFn">titleFn</a></li>
1478             
1479             </ul>
1480             <ul class="events itemMembers">
1481             
1482             </ul>
1483         </li>
1484     
1485         <li class="item" data-name="LuCI.form.TextValue">
1486             <span class="title">
1487                 <a href="LuCI.form.TextValue.html">LuCI.form.TextValue</a>
1488                 
1489             </span>
1490             <ul class="members itemMembers">
1491             
1492             <span class="subtitle">Members</span>
1493             
1494                 <li data-name="LuCI.form.TextValue##cols"><a href="LuCI.form.TextValue.html#cols">cols</a></li>
1495             
1496                 <li data-name="LuCI.form.TextValue##monospace"><a href="LuCI.form.TextValue.html#monospace">monospace</a></li>
1497             
1498                 <li data-name="LuCI.form.TextValue##rows"><a href="LuCI.form.TextValue.html#rows">rows</a></li>
1499             
1500                 <li data-name="LuCI.form.TextValue##wrap"><a href="LuCI.form.TextValue.html#wrap">wrap</a></li>
1501             
1502                 <li data-name="LuCI.form.TextValue#datatype"><a href="LuCI.form.TextValue.html#datatype">datatype</a></li>
1503             
1504                 <li data-name="LuCI.form.TextValue#default"><a href="LuCI.form.TextValue.html#default">default</a></li>
1505             
1506                 <li data-name="LuCI.form.TextValue#editable"><a href="LuCI.form.TextValue.html#editable">editable</a></li>
1507             
1508                 <li data-name="LuCI.form.TextValue#modalonly"><a href="LuCI.form.TextValue.html#modalonly">modalonly</a></li>
1509             
1510                 <li data-name="LuCI.form.TextValue#optional"><a href="LuCI.form.TextValue.html#optional">optional</a></li>
1511             
1512                 <li data-name="LuCI.form.TextValue#password"><a href="LuCI.form.TextValue.html#password">password</a></li>
1513             
1514                 <li data-name="LuCI.form.TextValue#placeholder"><a href="LuCI.form.TextValue.html#placeholder">placeholder</a></li>
1515             
1516                 <li data-name="LuCI.form.TextValue#rmempty"><a href="LuCI.form.TextValue.html#rmempty">rmempty</a></li>
1517             
1518                 <li data-name="LuCI.form.TextValue#uciconfig"><a href="LuCI.form.TextValue.html#uciconfig">uciconfig</a></li>
1519             
1520                 <li data-name="LuCI.form.TextValue#ucioption"><a href="LuCI.form.TextValue.html#ucioption">ucioption</a></li>
1521             
1522                 <li data-name="LuCI.form.TextValue#ucisection"><a href="LuCI.form.TextValue.html#ucisection">ucisection</a></li>
1523             
1524                 <li data-name="LuCI.form.TextValue#validate"><a href="LuCI.form.TextValue.html#validate">validate</a></li>
1525             
1526                 <li data-name="LuCI.form.TextValue#width"><a href="LuCI.form.TextValue.html#width">width</a></li>
1527             
1528             </ul>
1529             <ul class="typedefs itemMembers">
1530             
1531             </ul>
1532             <ul class="typedefs itemMembers">
1533             
1534             </ul>
1535             <ul class="methods itemMembers">
1536             
1537             <span class="subtitle">Methods</span>
1538             
1539                 <li data-name="LuCI.form.TextValue#append"><a href="LuCI.form.TextValue.html#append">append</a></li>
1540             
1541                 <li data-name="LuCI.form.TextValue#cbid"><a href="LuCI.form.TextValue.html#cbid">cbid</a></li>
1542             
1543                 <li data-name="LuCI.form.TextValue#cfgvalue"><a href="LuCI.form.TextValue.html#cfgvalue">cfgvalue</a></li>
1544             
1545                 <li data-name="LuCI.form.TextValue#depends"><a href="LuCI.form.TextValue.html#depends">depends</a></li>
1546             
1547                 <li data-name="LuCI.form.TextValue#formvalue"><a href="LuCI.form.TextValue.html#formvalue">formvalue</a></li>
1548             
1549                 <li data-name="LuCI.form.TextValue#getUIElement"><a href="LuCI.form.TextValue.html#getUIElement">getUIElement</a></li>
1550             
1551                 <li data-name="LuCI.form.TextValue#isActive"><a href="LuCI.form.TextValue.html#isActive">isActive</a></li>
1552             
1553                 <li data-name="LuCI.form.TextValue#isValid"><a href="LuCI.form.TextValue.html#isValid">isValid</a></li>
1554             
1555                 <li data-name="LuCI.form.TextValue#load"><a href="LuCI.form.TextValue.html#load">load</a></li>
1556             
1557                 <li data-name="LuCI.form.TextValue#parse"><a href="LuCI.form.TextValue.html#parse">parse</a></li>
1558             
1559                 <li data-name="LuCI.form.TextValue#remove"><a href="LuCI.form.TextValue.html#remove">remove</a></li>
1560             
1561                 <li data-name="LuCI.form.TextValue#stripTags"><a href="LuCI.form.TextValue.html#stripTags">stripTags</a></li>
1562             
1563                 <li data-name="LuCI.form.TextValue#textvalue"><a href="LuCI.form.TextValue.html#textvalue">textvalue</a></li>
1564             
1565                 <li data-name="LuCI.form.TextValue#titleFn"><a href="LuCI.form.TextValue.html#titleFn">titleFn</a></li>
1566             
1567                 <li data-name="LuCI.form.TextValue#write"><a href="LuCI.form.TextValue.html#write">write</a></li>
1568             
1569             </ul>
1570             <ul class="events itemMembers">
1571             
1572             </ul>
1573         </li>
1574     
1575         <li class="item" data-name="LuCI.form.TypedSection">
1576             <span class="title">
1577                 <a href="LuCI.form.TypedSection.html">LuCI.form.TypedSection</a>
1578                 
1579             </span>
1580             <ul class="members itemMembers">
1581             
1582             <span class="subtitle">Members</span>
1583             
1584                 <li data-name="LuCI.form.TypedSection##addbtntitle"><a href="LuCI.form.TypedSection.html#addbtntitle">addbtntitle</a></li>
1585             
1586                 <li data-name="LuCI.form.TypedSection##addremove"><a href="LuCI.form.TypedSection.html#addremove">addremove</a></li>
1587             
1588                 <li data-name="LuCI.form.TypedSection##anonymous"><a href="LuCI.form.TypedSection.html#anonymous">anonymous</a></li>
1589             
1590                 <li data-name="LuCI.form.TypedSection##tabbed"><a href="LuCI.form.TypedSection.html#tabbed">tabbed</a></li>
1591             
1592                 <li data-name="LuCI.form.TypedSection##uciconfig"><a href="LuCI.form.TypedSection.html#uciconfig">uciconfig</a></li>
1593             
1594                 <li data-name="LuCI.form.TypedSection#parentoption"><a href="LuCI.form.TypedSection.html#parentoption">parentoption</a></li>
1595             
1596             </ul>
1597             <ul class="typedefs itemMembers">
1598             
1599             </ul>
1600             <ul class="typedefs itemMembers">
1601             
1602             </ul>
1603             <ul class="methods itemMembers">
1604             
1605             <span class="subtitle">Methods</span>
1606             
1607                 <li data-name="LuCI.form.TypedSection#append"><a href="LuCI.form.TypedSection.html#append">append</a></li>
1608             
1609                 <li data-name="LuCI.form.TypedSection#cfgsections"><a href="LuCI.form.TypedSection.html#cfgsections">cfgsections</a></li>
1610             
1611                 <li data-name="LuCI.form.TypedSection#filter"><a href="LuCI.form.TypedSection.html#filter">filter</a></li>
1612             
1613                 <li data-name="LuCI.form.TypedSection#load"><a href="LuCI.form.TypedSection.html#load">load</a></li>
1614             
1615                 <li data-name="LuCI.form.TypedSection#option"><a href="LuCI.form.TypedSection.html#option">option</a></li>
1616             
1617                 <li data-name="LuCI.form.TypedSection#parse"><a href="LuCI.form.TypedSection.html#parse">parse</a></li>
1618             
1619                 <li data-name="LuCI.form.TypedSection#render"><a href="LuCI.form.TypedSection.html#render">render</a></li>
1620             
1621                 <li data-name="LuCI.form.TypedSection#stripTags"><a href="LuCI.form.TypedSection.html#stripTags">stripTags</a></li>
1622             
1623                 <li data-name="LuCI.form.TypedSection#tab"><a href="LuCI.form.TypedSection.html#tab">tab</a></li>
1624             
1625                 <li data-name="LuCI.form.TypedSection#taboption"><a href="LuCI.form.TypedSection.html#taboption">taboption</a></li>
1626             
1627                 <li data-name="LuCI.form.TypedSection#titleFn"><a href="LuCI.form.TypedSection.html#titleFn">titleFn</a></li>
1628             
1629             </ul>
1630             <ul class="events itemMembers">
1631             
1632             </ul>
1633         </li>
1634     
1635         <li class="item" data-name="LuCI.form.Value">
1636             <span class="title">
1637                 <a href="LuCI.form.Value.html">LuCI.form.Value</a>
1638                 
1639             </span>
1640             <ul class="members itemMembers">
1641             
1642             <span class="subtitle">Members</span>
1643             
1644                 <li data-name="LuCI.form.Value##password"><a href="LuCI.form.Value.html#password">password</a></li>
1645             
1646                 <li data-name="LuCI.form.Value##placeholder"><a href="LuCI.form.Value.html#placeholder">placeholder</a></li>
1647             
1648                 <li data-name="LuCI.form.Value#datatype"><a href="LuCI.form.Value.html#datatype">datatype</a></li>
1649             
1650                 <li data-name="LuCI.form.Value#default"><a href="LuCI.form.Value.html#default">default</a></li>
1651             
1652                 <li data-name="LuCI.form.Value#editable"><a href="LuCI.form.Value.html#editable">editable</a></li>
1653             
1654                 <li data-name="LuCI.form.Value#modalonly"><a href="LuCI.form.Value.html#modalonly">modalonly</a></li>
1655             
1656                 <li data-name="LuCI.form.Value#optional"><a href="LuCI.form.Value.html#optional">optional</a></li>
1657             
1658                 <li data-name="LuCI.form.Value#rmempty"><a href="LuCI.form.Value.html#rmempty">rmempty</a></li>
1659             
1660                 <li data-name="LuCI.form.Value#uciconfig"><a href="LuCI.form.Value.html#uciconfig">uciconfig</a></li>
1661             
1662                 <li data-name="LuCI.form.Value#ucioption"><a href="LuCI.form.Value.html#ucioption">ucioption</a></li>
1663             
1664                 <li data-name="LuCI.form.Value#ucisection"><a href="LuCI.form.Value.html#ucisection">ucisection</a></li>
1665             
1666                 <li data-name="LuCI.form.Value#validate"><a href="LuCI.form.Value.html#validate">validate</a></li>
1667             
1668                 <li data-name="LuCI.form.Value#width"><a href="LuCI.form.Value.html#width">width</a></li>
1669             
1670             </ul>
1671             <ul class="typedefs itemMembers">
1672             
1673             </ul>
1674             <ul class="typedefs itemMembers">
1675             
1676             </ul>
1677             <ul class="methods itemMembers">
1678             
1679             <span class="subtitle">Methods</span>
1680             
1681                 <li data-name="LuCI.form.Value#append"><a href="LuCI.form.Value.html#append">append</a></li>
1682             
1683                 <li data-name="LuCI.form.Value#cbid"><a href="LuCI.form.Value.html#cbid">cbid</a></li>
1684             
1685                 <li data-name="LuCI.form.Value#cfgvalue"><a href="LuCI.form.Value.html#cfgvalue">cfgvalue</a></li>
1686             
1687                 <li data-name="LuCI.form.Value#depends"><a href="LuCI.form.Value.html#depends">depends</a></li>
1688             
1689                 <li data-name="LuCI.form.Value#formvalue"><a href="LuCI.form.Value.html#formvalue">formvalue</a></li>
1690             
1691                 <li data-name="LuCI.form.Value#getUIElement"><a href="LuCI.form.Value.html#getUIElement">getUIElement</a></li>
1692             
1693                 <li data-name="LuCI.form.Value#isActive"><a href="LuCI.form.Value.html#isActive">isActive</a></li>
1694             
1695                 <li data-name="LuCI.form.Value#isValid"><a href="LuCI.form.Value.html#isValid">isValid</a></li>
1696             
1697                 <li data-name="LuCI.form.Value#load"><a href="LuCI.form.Value.html#load">load</a></li>
1698             
1699                 <li data-name="LuCI.form.Value#parse"><a href="LuCI.form.Value.html#parse">parse</a></li>
1700             
1701                 <li data-name="LuCI.form.Value#remove"><a href="LuCI.form.Value.html#remove">remove</a></li>
1702             
1703                 <li data-name="LuCI.form.Value#render"><a href="LuCI.form.Value.html#render">render</a></li>
1704             
1705                 <li data-name="LuCI.form.Value#stripTags"><a href="LuCI.form.Value.html#stripTags">stripTags</a></li>
1706             
1707                 <li data-name="LuCI.form.Value#textvalue"><a href="LuCI.form.Value.html#textvalue">textvalue</a></li>
1708             
1709                 <li data-name="LuCI.form.Value#titleFn"><a href="LuCI.form.Value.html#titleFn">titleFn</a></li>
1710             
1711                 <li data-name="LuCI.form.Value#value"><a href="LuCI.form.Value.html#value">value</a></li>
1712             
1713                 <li data-name="LuCI.form.Value#write"><a href="LuCI.form.Value.html#write">write</a></li>
1714             
1715             </ul>
1716             <ul class="events itemMembers">
1717             
1718             </ul>
1719         </li>
1720     
1721         <li class="item" data-name="LuCI.fs">
1722             <span class="title">
1723                 <a href="LuCI.fs.html">LuCI.fs</a>
1724                 
1725             </span>
1726             <ul class="members itemMembers">
1727             
1728             </ul>
1729             <ul class="typedefs itemMembers">
1730             
1731             <span class="subtitle">Typedefs</span>
1732             
1733                 <li data-name="LuCI.fs.FileExecResult"><a href="LuCI.fs.html#.FileExecResult">FileExecResult</a></li>
1734             
1735                 <li data-name="LuCI.fs.FileStatEntry"><a href="LuCI.fs.html#.FileStatEntry">FileStatEntry</a></li>
1736             
1737             </ul>
1738             <ul class="typedefs itemMembers">
1739             
1740             </ul>
1741             <ul class="methods itemMembers">
1742             
1743             <span class="subtitle">Methods</span>
1744             
1745                 <li data-name="LuCI.fs#exec"><a href="LuCI.fs.html#exec">exec</a></li>
1746             
1747                 <li data-name="LuCI.fs#exec_direct"><a href="LuCI.fs.html#exec_direct">exec_direct</a></li>
1748             
1749                 <li data-name="LuCI.fs#lines"><a href="LuCI.fs.html#lines">lines</a></li>
1750             
1751                 <li data-name="LuCI.fs#list"><a href="LuCI.fs.html#list">list</a></li>
1752             
1753                 <li data-name="LuCI.fs#read"><a href="LuCI.fs.html#read">read</a></li>
1754             
1755                 <li data-name="LuCI.fs#read_direct"><a href="LuCI.fs.html#read_direct">read_direct</a></li>
1756             
1757                 <li data-name="LuCI.fs#remove"><a href="LuCI.fs.html#remove">remove</a></li>
1758             
1759                 <li data-name="LuCI.fs#stat"><a href="LuCI.fs.html#stat">stat</a></li>
1760             
1761                 <li data-name="LuCI.fs#trimmed"><a href="LuCI.fs.html#trimmed">trimmed</a></li>
1762             
1763                 <li data-name="LuCI.fs#write"><a href="LuCI.fs.html#write">write</a></li>
1764             
1765             </ul>
1766             <ul class="events itemMembers">
1767             
1768             </ul>
1769         </li>
1770     
1771         <li class="item" data-name="LuCI.headers">
1772             <span class="title">
1773                 <a href="LuCI.headers.html">LuCI.headers</a>
1774                 
1775             </span>
1776             <ul class="members itemMembers">
1777             
1778             </ul>
1779             <ul class="typedefs itemMembers">
1780             
1781             </ul>
1782             <ul class="typedefs itemMembers">
1783             
1784             </ul>
1785             <ul class="methods itemMembers">
1786             
1787             <span class="subtitle">Methods</span>
1788             
1789                 <li data-name="LuCI.headers#get"><a href="LuCI.headers.html#get">get</a></li>
1790             
1791                 <li data-name="LuCI.headers#has"><a href="LuCI.headers.html#has">has</a></li>
1792             
1793             </ul>
1794             <ul class="events itemMembers">
1795             
1796             </ul>
1797         </li>
1798     
1799         <li class="item" data-name="LuCI.network">
1800             <span class="title">
1801                 <a href="LuCI.network.html">LuCI.network</a>
1802                 
1803             </span>
1804             <ul class="members itemMembers">
1805             
1806             </ul>
1807             <ul class="typedefs itemMembers">
1808             
1809             <span class="subtitle">Typedefs</span>
1810             
1811                 <li data-name="LuCI.network.SwitchTopology"><a href="LuCI.network.html#.SwitchTopology">SwitchTopology</a></li>
1812             
1813                 <li data-name="LuCI.network.WifiEncryption"><a href="LuCI.network.html#.WifiEncryption">WifiEncryption</a></li>
1814             
1815                 <li data-name="LuCI.network.WifiPeerEntry"><a href="LuCI.network.html#.WifiPeerEntry">WifiPeerEntry</a></li>
1816             
1817                 <li data-name="LuCI.network.WifiRateEntry"><a href="LuCI.network.html#.WifiRateEntry">WifiRateEntry</a></li>
1818             
1819                 <li data-name="LuCI.network.WifiScanResult"><a href="LuCI.network.html#.WifiScanResult">WifiScanResult</a></li>
1820             
1821             </ul>
1822             <ul class="typedefs itemMembers">
1823             
1824             </ul>
1825             <ul class="methods itemMembers">
1826             
1827             <span class="subtitle">Methods</span>
1828             
1829                 <li data-name="LuCI.network#addNetwork"><a href="LuCI.network.html#addNetwork">addNetwork</a></li>
1830             
1831                 <li data-name="LuCI.network#addWifiNetwork"><a href="LuCI.network.html#addWifiNetwork">addWifiNetwork</a></li>
1832             
1833                 <li data-name="LuCI.network#deleteNetwork"><a href="LuCI.network.html#deleteNetwork">deleteNetwork</a></li>
1834             
1835                 <li data-name="LuCI.network#deleteWifiNetwork"><a href="LuCI.network.html#deleteWifiNetwork">deleteWifiNetwork</a></li>
1836             
1837                 <li data-name="LuCI.network#flushCache"><a href="LuCI.network.html#flushCache">flushCache</a></li>
1838             
1839                 <li data-name="LuCI.network#formatWifiEncryption"><a href="LuCI.network.html#formatWifiEncryption">formatWifiEncryption</a></li>
1840             
1841                 <li data-name="LuCI.network#getDevice"><a href="LuCI.network.html#getDevice">getDevice</a></li>
1842             
1843                 <li data-name="LuCI.network#getDevices"><a href="LuCI.network.html#getDevices">getDevices</a></li>
1844             
1845                 <li data-name="LuCI.network#getDSLModemType"><a href="LuCI.network.html#getDSLModemType">getDSLModemType</a></li>
1846             
1847                 <li data-name="LuCI.network#getHostHints"><a href="LuCI.network.html#getHostHints">getHostHints</a></li>
1848             
1849                 <li data-name="LuCI.network#getIfnameOf"><a href="LuCI.network.html#getIfnameOf">getIfnameOf</a></li>
1850             
1851                 <li data-name="LuCI.network#getNetwork"><a href="LuCI.network.html#getNetwork">getNetwork</a></li>
1852             
1853                 <li data-name="LuCI.network#getNetworks"><a href="LuCI.network.html#getNetworks">getNetworks</a></li>
1854             
1855                 <li data-name="LuCI.network#getProtocol"><a href="LuCI.network.html#getProtocol">getProtocol</a></li>
1856             
1857                 <li data-name="LuCI.network#getProtocols"><a href="LuCI.network.html#getProtocols">getProtocols</a></li>
1858             
1859                 <li data-name="LuCI.network#getSwitchTopologies"><a href="LuCI.network.html#getSwitchTopologies">getSwitchTopologies</a></li>
1860             
1861                 <li data-name="LuCI.network#getWAN6Networks"><a href="LuCI.network.html#getWAN6Networks">getWAN6Networks</a></li>
1862             
1863                 <li data-name="LuCI.network#getWANNetworks"><a href="LuCI.network.html#getWANNetworks">getWANNetworks</a></li>
1864             
1865                 <li data-name="LuCI.network#getWifiDevice"><a href="LuCI.network.html#getWifiDevice">getWifiDevice</a></li>
1866             
1867                 <li data-name="LuCI.network#getWifiDevices"><a href="LuCI.network.html#getWifiDevices">getWifiDevices</a></li>
1868             
1869                 <li data-name="LuCI.network#getWifiNetwork"><a href="LuCI.network.html#getWifiNetwork">getWifiNetwork</a></li>
1870             
1871                 <li data-name="LuCI.network#getWifiNetworks"><a href="LuCI.network.html#getWifiNetworks">getWifiNetworks</a></li>
1872             
1873                 <li data-name="LuCI.network#isIgnoredDevice"><a href="LuCI.network.html#isIgnoredDevice">isIgnoredDevice</a></li>
1874             
1875                 <li data-name="LuCI.network#maskToPrefix"><a href="LuCI.network.html#maskToPrefix">maskToPrefix</a></li>
1876             
1877                 <li data-name="LuCI.network#prefixToMask"><a href="LuCI.network.html#prefixToMask">prefixToMask</a></li>
1878             
1879                 <li data-name="LuCI.network#registerErrorCode"><a href="LuCI.network.html#registerErrorCode">registerErrorCode</a></li>
1880             
1881                 <li data-name="LuCI.network#registerPatternVirtual"><a href="LuCI.network.html#registerPatternVirtual">registerPatternVirtual</a></li>
1882             
1883                 <li data-name="LuCI.network#registerProtocol"><a href="LuCI.network.html#registerProtocol">registerProtocol</a></li>
1884             
1885                 <li data-name="LuCI.network#renameNetwork"><a href="LuCI.network.html#renameNetwork">renameNetwork</a></li>
1886             
1887             </ul>
1888             <ul class="events itemMembers">
1889             
1890             </ul>
1891         </li>
1892     
1893         <li class="item" data-name="LuCI.network.Device">
1894             <span class="title">
1895                 <a href="LuCI.network.Device.html">LuCI.network.Device</a>
1896                 
1897             </span>
1898             <ul class="members itemMembers">
1899             
1900             </ul>
1901             <ul class="typedefs itemMembers">
1902             
1903             </ul>
1904             <ul class="typedefs itemMembers">
1905             
1906             </ul>
1907             <ul class="methods itemMembers">
1908             
1909             <span class="subtitle">Methods</span>
1910             
1911                 <li data-name="LuCI.network.Device#getBridgeID"><a href="LuCI.network.Device.html#getBridgeID">getBridgeID</a></li>
1912             
1913                 <li data-name="LuCI.network.Device#getBridgeSTP"><a href="LuCI.network.Device.html#getBridgeSTP">getBridgeSTP</a></li>
1914             
1915                 <li data-name="LuCI.network.Device#getI18n"><a href="LuCI.network.Device.html#getI18n">getI18n</a></li>
1916             
1917                 <li data-name="LuCI.network.Device#getIP6Addrs"><a href="LuCI.network.Device.html#getIP6Addrs">getIP6Addrs</a></li>
1918             
1919                 <li data-name="LuCI.network.Device#getIPAddrs"><a href="LuCI.network.Device.html#getIPAddrs">getIPAddrs</a></li>
1920             
1921                 <li data-name="LuCI.network.Device#getMAC"><a href="LuCI.network.Device.html#getMAC">getMAC</a></li>
1922             
1923                 <li data-name="LuCI.network.Device#getMTU"><a href="LuCI.network.Device.html#getMTU">getMTU</a></li>
1924             
1925                 <li data-name="LuCI.network.Device#getName"><a href="LuCI.network.Device.html#getName">getName</a></li>
1926             
1927                 <li data-name="LuCI.network.Device#getNetwork"><a href="LuCI.network.Device.html#getNetwork">getNetwork</a></li>
1928             
1929                 <li data-name="LuCI.network.Device#getNetworks"><a href="LuCI.network.Device.html#getNetworks">getNetworks</a></li>
1930             
1931                 <li data-name="LuCI.network.Device#getPorts"><a href="LuCI.network.Device.html#getPorts">getPorts</a></li>
1932             
1933                 <li data-name="LuCI.network.Device#getRXBytes"><a href="LuCI.network.Device.html#getRXBytes">getRXBytes</a></li>
1934             
1935                 <li data-name="LuCI.network.Device#getRXPackets"><a href="LuCI.network.Device.html#getRXPackets">getRXPackets</a></li>
1936             
1937                 <li data-name="LuCI.network.Device#getShortName"><a href="LuCI.network.Device.html#getShortName">getShortName</a></li>
1938             
1939                 <li data-name="LuCI.network.Device#getTXBytes"><a href="LuCI.network.Device.html#getTXBytes">getTXBytes</a></li>
1940             
1941                 <li data-name="LuCI.network.Device#getTXPackets"><a href="LuCI.network.Device.html#getTXPackets">getTXPackets</a></li>
1942             
1943                 <li data-name="LuCI.network.Device#getType"><a href="LuCI.network.Device.html#getType">getType</a></li>
1944             
1945                 <li data-name="LuCI.network.Device#getTypeI18n"><a href="LuCI.network.Device.html#getTypeI18n">getTypeI18n</a></li>
1946             
1947                 <li data-name="LuCI.network.Device#getWifiNetwork"><a href="LuCI.network.Device.html#getWifiNetwork">getWifiNetwork</a></li>
1948             
1949                 <li data-name="LuCI.network.Device#isBridge"><a href="LuCI.network.Device.html#isBridge">isBridge</a></li>
1950             
1951                 <li data-name="LuCI.network.Device#isBridgePort"><a href="LuCI.network.Device.html#isBridgePort">isBridgePort</a></li>
1952             
1953                 <li data-name="LuCI.network.Device#isUp"><a href="LuCI.network.Device.html#isUp">isUp</a></li>
1954             
1955             </ul>
1956             <ul class="events itemMembers">
1957             
1958             </ul>
1959         </li>
1960     
1961         <li class="item" data-name="LuCI.network.Hosts">
1962             <span class="title">
1963                 <a href="LuCI.network.Hosts.html">LuCI.network.Hosts</a>
1964                 
1965             </span>
1966             <ul class="members itemMembers">
1967             
1968             </ul>
1969             <ul class="typedefs itemMembers">
1970             
1971             </ul>
1972             <ul class="typedefs itemMembers">
1973             
1974             </ul>
1975             <ul class="methods itemMembers">
1976             
1977             <span class="subtitle">Methods</span>
1978             
1979                 <li data-name="LuCI.network.Hosts#getHostnameByIP6Addr"><a href="LuCI.network.Hosts.html#getHostnameByIP6Addr">getHostnameByIP6Addr</a></li>
1980             
1981                 <li data-name="LuCI.network.Hosts#getHostnameByIPAddr"><a href="LuCI.network.Hosts.html#getHostnameByIPAddr">getHostnameByIPAddr</a></li>
1982             
1983                 <li data-name="LuCI.network.Hosts#getHostnameByMACAddr"><a href="LuCI.network.Hosts.html#getHostnameByMACAddr">getHostnameByMACAddr</a></li>
1984             
1985                 <li data-name="LuCI.network.Hosts#getIP6AddrByMACAddr"><a href="LuCI.network.Hosts.html#getIP6AddrByMACAddr">getIP6AddrByMACAddr</a></li>
1986             
1987                 <li data-name="LuCI.network.Hosts#getIPAddrByMACAddr"><a href="LuCI.network.Hosts.html#getIPAddrByMACAddr">getIPAddrByMACAddr</a></li>
1988             
1989                 <li data-name="LuCI.network.Hosts#getMACAddrByIP6Addr"><a href="LuCI.network.Hosts.html#getMACAddrByIP6Addr">getMACAddrByIP6Addr</a></li>
1990             
1991                 <li data-name="LuCI.network.Hosts#getMACAddrByIPAddr"><a href="LuCI.network.Hosts.html#getMACAddrByIPAddr">getMACAddrByIPAddr</a></li>
1992             
1993                 <li data-name="LuCI.network.Hosts#getMACHints"><a href="LuCI.network.Hosts.html#getMACHints">getMACHints</a></li>
1994             
1995             </ul>
1996             <ul class="events itemMembers">
1997             
1998             </ul>
1999         </li>
2000     
2001         <li class="item" data-name="LuCI.network.Protocol">
2002             <span class="title">
2003                 <a href="LuCI.network.Protocol.html">LuCI.network.Protocol</a>
2004                 
2005             </span>
2006             <ul class="members itemMembers">
2007             
2008             </ul>
2009             <ul class="typedefs itemMembers">
2010             
2011             </ul>
2012             <ul class="typedefs itemMembers">
2013             
2014             </ul>
2015             <ul class="methods itemMembers">
2016             
2017             <span class="subtitle">Methods</span>
2018             
2019                 <li data-name="LuCI.network.Protocol#addDevice"><a href="LuCI.network.Protocol.html#addDevice">addDevice</a></li>
2020             
2021                 <li data-name="LuCI.network.Protocol#containsDevice"><a href="LuCI.network.Protocol.html#containsDevice">containsDevice</a></li>
2022             
2023                 <li data-name="LuCI.network.Protocol#deleteConfiguration"><a href="LuCI.network.Protocol.html#deleteConfiguration">deleteConfiguration</a></li>
2024             
2025                 <li data-name="LuCI.network.Protocol#deleteDevice"><a href="LuCI.network.Protocol.html#deleteDevice">deleteDevice</a></li>
2026             
2027                 <li data-name="LuCI.network.Protocol#get"><a href="LuCI.network.Protocol.html#get">get</a></li>
2028             
2029                 <li data-name="LuCI.network.Protocol#getDevice"><a href="LuCI.network.Protocol.html#getDevice">getDevice</a></li>
2030             
2031                 <li data-name="LuCI.network.Protocol#getDevices"><a href="LuCI.network.Protocol.html#getDevices">getDevices</a></li>
2032             
2033                 <li data-name="LuCI.network.Protocol#getDNS6Addrs"><a href="LuCI.network.Protocol.html#getDNS6Addrs">getDNS6Addrs</a></li>
2034             
2035                 <li data-name="LuCI.network.Protocol#getDNSAddrs"><a href="LuCI.network.Protocol.html#getDNSAddrs">getDNSAddrs</a></li>
2036             
2037                 <li data-name="LuCI.network.Protocol#getErrors"><a href="LuCI.network.Protocol.html#getErrors">getErrors</a></li>
2038             
2039                 <li data-name="LuCI.network.Protocol#getExpiry"><a href="LuCI.network.Protocol.html#getExpiry">getExpiry</a></li>
2040             
2041                 <li data-name="LuCI.network.Protocol#getGateway6Addr"><a href="LuCI.network.Protocol.html#getGateway6Addr">getGateway6Addr</a></li>
2042             
2043                 <li data-name="LuCI.network.Protocol#getGatewayAddr"><a href="LuCI.network.Protocol.html#getGatewayAddr">getGatewayAddr</a></li>
2044             
2045                 <li data-name="LuCI.network.Protocol#getI18n"><a href="LuCI.network.Protocol.html#getI18n">getI18n</a></li>
2046             
2047                 <li data-name="LuCI.network.Protocol#getIfname"><a href="LuCI.network.Protocol.html#getIfname">getIfname</a></li>
2048             
2049                 <li data-name="LuCI.network.Protocol#getIP6Addr"><a href="LuCI.network.Protocol.html#getIP6Addr">getIP6Addr</a></li>
2050             
2051                 <li data-name="LuCI.network.Protocol#getIP6Addrs"><a href="LuCI.network.Protocol.html#getIP6Addrs">getIP6Addrs</a></li>
2052             
2053                 <li data-name="LuCI.network.Protocol#getIP6Prefix"><a href="LuCI.network.Protocol.html#getIP6Prefix">getIP6Prefix</a></li>
2054             
2055                 <li data-name="LuCI.network.Protocol#getIPAddr"><a href="LuCI.network.Protocol.html#getIPAddr">getIPAddr</a></li>
2056             
2057                 <li data-name="LuCI.network.Protocol#getIPAddrs"><a href="LuCI.network.Protocol.html#getIPAddrs">getIPAddrs</a></li>
2058             
2059                 <li data-name="LuCI.network.Protocol#getL2Device"><a href="LuCI.network.Protocol.html#getL2Device">getL2Device</a></li>
2060             
2061                 <li data-name="LuCI.network.Protocol#getL3Device"><a href="LuCI.network.Protocol.html#getL3Device">getL3Device</a></li>
2062             
2063                 <li data-name="LuCI.network.Protocol#getMetric"><a href="LuCI.network.Protocol.html#getMetric">getMetric</a></li>
2064             
2065                 <li data-name="LuCI.network.Protocol#getName"><a href="LuCI.network.Protocol.html#getName">getName</a></li>
2066             
2067                 <li data-name="LuCI.network.Protocol#getNetmask"><a href="LuCI.network.Protocol.html#getNetmask">getNetmask</a></li>
2068             
2069                 <li data-name="LuCI.network.Protocol#getOpkgPackage"><a href="LuCI.network.Protocol.html#getOpkgPackage">getOpkgPackage</a></li>
2070             
2071                 <li data-name="LuCI.network.Protocol#getProtocol"><a href="LuCI.network.Protocol.html#getProtocol">getProtocol</a></li>
2072             
2073                 <li data-name="LuCI.network.Protocol#getType"><a href="LuCI.network.Protocol.html#getType">getType</a></li>
2074             
2075                 <li data-name="LuCI.network.Protocol#getUptime"><a href="LuCI.network.Protocol.html#getUptime">getUptime</a></li>
2076             
2077                 <li data-name="LuCI.network.Protocol#getZoneName"><a href="LuCI.network.Protocol.html#getZoneName">getZoneName</a></li>
2078             
2079                 <li data-name="LuCI.network.Protocol#isAlias"><a href="LuCI.network.Protocol.html#isAlias">isAlias</a></li>
2080             
2081                 <li data-name="LuCI.network.Protocol#isBridge"><a href="LuCI.network.Protocol.html#isBridge">isBridge</a></li>
2082             
2083                 <li data-name="LuCI.network.Protocol#isCreateable"><a href="LuCI.network.Protocol.html#isCreateable">isCreateable</a></li>
2084             
2085                 <li data-name="LuCI.network.Protocol#isDynamic"><a href="LuCI.network.Protocol.html#isDynamic">isDynamic</a></li>
2086             
2087                 <li data-name="LuCI.network.Protocol#isEmpty"><a href="LuCI.network.Protocol.html#isEmpty">isEmpty</a></li>
2088             
2089                 <li data-name="LuCI.network.Protocol#isFloating"><a href="LuCI.network.Protocol.html#isFloating">isFloating</a></li>
2090             
2091                 <li data-name="LuCI.network.Protocol#isInstalled"><a href="LuCI.network.Protocol.html#isInstalled">isInstalled</a></li>
2092             
2093                 <li data-name="LuCI.network.Protocol#isUp"><a href="LuCI.network.Protocol.html#isUp">isUp</a></li>
2094             
2095                 <li data-name="LuCI.network.Protocol#isVirtual"><a href="LuCI.network.Protocol.html#isVirtual">isVirtual</a></li>
2096             
2097                 <li data-name="LuCI.network.Protocol#set"><a href="LuCI.network.Protocol.html#set">set</a></li>
2098             
2099             </ul>
2100             <ul class="events itemMembers">
2101             
2102             </ul>
2103         </li>
2104     
2105         <li class="item" data-name="LuCI.network.WifiDevice">
2106             <span class="title">
2107                 <a href="LuCI.network.WifiDevice.html">LuCI.network.WifiDevice</a>
2108                 
2109             </span>
2110             <ul class="members itemMembers">
2111             
2112             </ul>
2113             <ul class="typedefs itemMembers">
2114             
2115             </ul>
2116             <ul class="typedefs itemMembers">
2117             
2118             </ul>
2119             <ul class="methods itemMembers">
2120             
2121             <span class="subtitle">Methods</span>
2122             
2123                 <li data-name="LuCI.network.WifiDevice#addWifiNetwork"><a href="LuCI.network.WifiDevice.html#addWifiNetwork">addWifiNetwork</a></li>
2124             
2125                 <li data-name="LuCI.network.WifiDevice#deleteWifiNetwork"><a href="LuCI.network.WifiDevice.html#deleteWifiNetwork">deleteWifiNetwork</a></li>
2126             
2127                 <li data-name="LuCI.network.WifiDevice#get"><a href="LuCI.network.WifiDevice.html#get">get</a></li>
2128             
2129                 <li data-name="LuCI.network.WifiDevice#getHTModes"><a href="LuCI.network.WifiDevice.html#getHTModes">getHTModes</a></li>
2130             
2131                 <li data-name="LuCI.network.WifiDevice#getHWModes"><a href="LuCI.network.WifiDevice.html#getHWModes">getHWModes</a></li>
2132             
2133                 <li data-name="LuCI.network.WifiDevice#getI18n"><a href="LuCI.network.WifiDevice.html#getI18n">getI18n</a></li>
2134             
2135                 <li data-name="LuCI.network.WifiDevice#getName"><a href="LuCI.network.WifiDevice.html#getName">getName</a></li>
2136             
2137                 <li data-name="LuCI.network.WifiDevice#getScanList"><a href="LuCI.network.WifiDevice.html#getScanList">getScanList</a></li>
2138             
2139                 <li data-name="LuCI.network.WifiDevice#getWifiNetwork"><a href="LuCI.network.WifiDevice.html#getWifiNetwork">getWifiNetwork</a></li>
2140             
2141                 <li data-name="LuCI.network.WifiDevice#getWifiNetworks"><a href="LuCI.network.WifiDevice.html#getWifiNetworks">getWifiNetworks</a></li>
2142             
2143                 <li data-name="LuCI.network.WifiDevice#isDisabled"><a href="LuCI.network.WifiDevice.html#isDisabled">isDisabled</a></li>
2144             
2145                 <li data-name="LuCI.network.WifiDevice#isUp"><a href="LuCI.network.WifiDevice.html#isUp">isUp</a></li>
2146             
2147                 <li data-name="LuCI.network.WifiDevice#set"><a href="LuCI.network.WifiDevice.html#set">set</a></li>
2148             
2149             </ul>
2150             <ul class="events itemMembers">
2151             
2152             </ul>
2153         </li>
2154     
2155         <li class="item" data-name="LuCI.network.WifiNetwork">
2156             <span class="title">
2157                 <a href="LuCI.network.WifiNetwork.html">LuCI.network.WifiNetwork</a>
2158                 
2159             </span>
2160             <ul class="members itemMembers">
2161             
2162             </ul>
2163             <ul class="typedefs itemMembers">
2164             
2165             </ul>
2166             <ul class="typedefs itemMembers">
2167             
2168             </ul>
2169             <ul class="methods itemMembers">
2170             
2171             <span class="subtitle">Methods</span>
2172             
2173                 <li data-name="LuCI.network.WifiNetwork#disconnectClient"><a href="LuCI.network.WifiNetwork.html#disconnectClient">disconnectClient</a></li>
2174             
2175                 <li data-name="LuCI.network.WifiNetwork#get"><a href="LuCI.network.WifiNetwork.html#get">get</a></li>
2176             
2177                 <li data-name="LuCI.network.WifiNetwork#getActiveBSSID"><a href="LuCI.network.WifiNetwork.html#getActiveBSSID">getActiveBSSID</a></li>
2178             
2179                 <li data-name="LuCI.network.WifiNetwork#getActiveEncryption"><a href="LuCI.network.WifiNetwork.html#getActiveEncryption">getActiveEncryption</a></li>
2180             
2181                 <li data-name="LuCI.network.WifiNetwork#getActiveMode"><a href="LuCI.network.WifiNetwork.html#getActiveMode">getActiveMode</a></li>
2182             
2183                 <li data-name="LuCI.network.WifiNetwork#getActiveModeI18n"><a href="LuCI.network.WifiNetwork.html#getActiveModeI18n">getActiveModeI18n</a></li>
2184             
2185                 <li data-name="LuCI.network.WifiNetwork#getActiveSSID"><a href="LuCI.network.WifiNetwork.html#getActiveSSID">getActiveSSID</a></li>
2186             
2187                 <li data-name="LuCI.network.WifiNetwork#getAssocList"><a href="LuCI.network.WifiNetwork.html#getAssocList">getAssocList</a></li>
2188             
2189                 <li data-name="LuCI.network.WifiNetwork#getBitRate"><a href="LuCI.network.WifiNetwork.html#getBitRate">getBitRate</a></li>
2190             
2191                 <li data-name="LuCI.network.WifiNetwork#getBSSID"><a href="LuCI.network.WifiNetwork.html#getBSSID">getBSSID</a></li>
2192             
2193                 <li data-name="LuCI.network.WifiNetwork#getChannel"><a href="LuCI.network.WifiNetwork.html#getChannel">getChannel</a></li>
2194             
2195                 <li data-name="LuCI.network.WifiNetwork#getCountryCode"><a href="LuCI.network.WifiNetwork.html#getCountryCode">getCountryCode</a></li>
2196             
2197                 <li data-name="LuCI.network.WifiNetwork#getDevice"><a href="LuCI.network.WifiNetwork.html#getDevice">getDevice</a></li>
2198             
2199                 <li data-name="LuCI.network.WifiNetwork#getFrequency"><a href="LuCI.network.WifiNetwork.html#getFrequency">getFrequency</a></li>
2200             
2201                 <li data-name="LuCI.network.WifiNetwork#getI18n"><a href="LuCI.network.WifiNetwork.html#getI18n">getI18n</a></li>
2202             
2203                 <li data-name="LuCI.network.WifiNetwork#getID"><a href="LuCI.network.WifiNetwork.html#getID">getID</a></li>
2204             
2205                 <li data-name="LuCI.network.WifiNetwork#getIfname"><a href="LuCI.network.WifiNetwork.html#getIfname">getIfname</a></li>
2206             
2207                 <li data-name="LuCI.network.WifiNetwork#getMeshID"><a href="LuCI.network.WifiNetwork.html#getMeshID">getMeshID</a></li>
2208             
2209                 <li data-name="LuCI.network.WifiNetwork#getMode"><a href="LuCI.network.WifiNetwork.html#getMode">getMode</a></li>
2210             
2211                 <li data-name="LuCI.network.WifiNetwork#getName"><a href="LuCI.network.WifiNetwork.html#getName">getName</a></li>
2212             
2213                 <li data-name="LuCI.network.WifiNetwork#getNetwork"><a href="LuCI.network.WifiNetwork.html#getNetwork">getNetwork</a></li>
2214             
2215                 <li data-name="LuCI.network.WifiNetwork#getNetworkNames"><a href="LuCI.network.WifiNetwork.html#getNetworkNames">getNetworkNames</a></li>
2216             
2217                 <li data-name="LuCI.network.WifiNetwork#getNetworks"><a href="LuCI.network.WifiNetwork.html#getNetworks">getNetworks</a></li>
2218             
2219                 <li data-name="LuCI.network.WifiNetwork#getNoise"><a href="LuCI.network.WifiNetwork.html#getNoise">getNoise</a></li>
2220             
2221                 <li data-name="LuCI.network.WifiNetwork#getShortName"><a href="LuCI.network.WifiNetwork.html#getShortName">getShortName</a></li>
2222             
2223                 <li data-name="LuCI.network.WifiNetwork#getSignal"><a href="LuCI.network.WifiNetwork.html#getSignal">getSignal</a></li>
2224             
2225                 <li data-name="LuCI.network.WifiNetwork#getSignalLevel"><a href="LuCI.network.WifiNetwork.html#getSignalLevel">getSignalLevel</a></li>
2226             
2227                 <li data-name="LuCI.network.WifiNetwork#getSignalPercent"><a href="LuCI.network.WifiNetwork.html#getSignalPercent">getSignalPercent</a></li>
2228             
2229                 <li data-name="LuCI.network.WifiNetwork#getSSID"><a href="LuCI.network.WifiNetwork.html#getSSID">getSSID</a></li>
2230             
2231                 <li data-name="LuCI.network.WifiNetwork#getTXPower"><a href="LuCI.network.WifiNetwork.html#getTXPower">getTXPower</a></li>
2232             
2233                 <li data-name="LuCI.network.WifiNetwork#getTXPowerOffset"><a href="LuCI.network.WifiNetwork.html#getTXPowerOffset">getTXPowerOffset</a></li>
2234             
2235                 <li data-name="LuCI.network.WifiNetwork#getWifiDevice"><a href="LuCI.network.WifiNetwork.html#getWifiDevice">getWifiDevice</a></li>
2236             
2237                 <li data-name="LuCI.network.WifiNetwork#getWifiDeviceName"><a href="LuCI.network.WifiNetwork.html#getWifiDeviceName">getWifiDeviceName</a></li>
2238             
2239                 <li data-name="LuCI.network.WifiNetwork#isClientDisconnectSupported"><a href="LuCI.network.WifiNetwork.html#isClientDisconnectSupported">isClientDisconnectSupported</a></li>
2240             
2241                 <li data-name="LuCI.network.WifiNetwork#isDisabled"><a href="LuCI.network.WifiNetwork.html#isDisabled">isDisabled</a></li>
2242             
2243                 <li data-name="LuCI.network.WifiNetwork#isUp"><a href="LuCI.network.WifiNetwork.html#isUp">isUp</a></li>
2244             
2245                 <li data-name="LuCI.network.WifiNetwork#set"><a href="LuCI.network.WifiNetwork.html#set">set</a></li>
2246             
2247             </ul>
2248             <ul class="events itemMembers">
2249             
2250             </ul>
2251         </li>
2252     
2253         <li class="item" data-name="LuCI.poll">
2254             <span class="title">
2255                 <a href="LuCI.poll.html">LuCI.poll</a>
2256                 
2257             </span>
2258             <ul class="members itemMembers">
2259             
2260             </ul>
2261             <ul class="typedefs itemMembers">
2262             
2263             </ul>
2264             <ul class="typedefs itemMembers">
2265             
2266             </ul>
2267             <ul class="methods itemMembers">
2268             
2269             <span class="subtitle">Methods</span>
2270             
2271                 <li data-name="LuCI.poll#active"><a href="LuCI.poll.html#active">active</a></li>
2272             
2273                 <li data-name="LuCI.poll#add"><a href="LuCI.poll.html#add">add</a></li>
2274             
2275                 <li data-name="LuCI.poll#remove"><a href="LuCI.poll.html#remove">remove</a></li>
2276             
2277                 <li data-name="LuCI.poll#start"><a href="LuCI.poll.html#start">start</a></li>
2278             
2279                 <li data-name="LuCI.poll#stop"><a href="LuCI.poll.html#stop">stop</a></li>
2280             
2281             </ul>
2282             <ul class="events itemMembers">
2283             
2284             </ul>
2285         </li>
2286     
2287         <li class="item" data-name="LuCI.request">
2288             <span class="title">
2289                 <a href="LuCI.request.html">LuCI.request</a>
2290                 
2291             </span>
2292             <ul class="members itemMembers">
2293             
2294             </ul>
2295             <ul class="typedefs itemMembers">
2296             
2297             <span class="subtitle">Typedefs</span>
2298             
2299                 <li data-name="LuCI.request.interceptorFn"><a href="LuCI.request.html#.interceptorFn">interceptorFn</a></li>
2300             
2301                 <li data-name="LuCI.request.RequestOptions"><a href="LuCI.request.html#.RequestOptions">RequestOptions</a></li>
2302             
2303             </ul>
2304             <ul class="typedefs itemMembers">
2305             
2306             </ul>
2307             <ul class="methods itemMembers">
2308             
2309             <span class="subtitle">Methods</span>
2310             
2311                 <li data-name="LuCI.request#addInterceptor"><a href="LuCI.request.html#addInterceptor">addInterceptor</a></li>
2312             
2313                 <li data-name="LuCI.request#expandURL"><a href="LuCI.request.html#expandURL">expandURL</a></li>
2314             
2315                 <li data-name="LuCI.request#get"><a href="LuCI.request.html#get">get</a></li>
2316             
2317                 <li data-name="LuCI.request#post"><a href="LuCI.request.html#post">post</a></li>
2318             
2319                 <li data-name="LuCI.request#removeInterceptor"><a href="LuCI.request.html#removeInterceptor">removeInterceptor</a></li>
2320             
2321                 <li data-name="LuCI.request#request"><a href="LuCI.request.html#request">request</a></li>
2322             
2323             </ul>
2324             <ul class="events itemMembers">
2325             
2326             </ul>
2327         </li>
2328     
2329         <li class="item" data-name="LuCI.request.poll">
2330             <span class="title">
2331                 <a href="LuCI.request.poll.html">LuCI.request.poll</a>
2332                 
2333             </span>
2334             <ul class="members itemMembers">
2335             
2336             </ul>
2337             <ul class="typedefs itemMembers">
2338             
2339             <span class="subtitle">Typedefs</span>
2340             
2341                 <li data-name="LuCI.request.poll~callbackFn"><a href="LuCI.request.poll.html#~callbackFn">callbackFn</a></li>
2342             
2343             </ul>
2344             <ul class="typedefs itemMembers">
2345             
2346             </ul>
2347             <ul class="methods itemMembers">
2348             
2349             <span class="subtitle">Methods</span>
2350             
2351                 <li data-name="LuCI.request.poll#active"><a href="LuCI.request.poll.html#active">active</a></li>
2352             
2353                 <li data-name="LuCI.request.poll#add"><a href="LuCI.request.poll.html#add">add</a></li>
2354             
2355                 <li data-name="LuCI.request.poll#remove"><a href="LuCI.request.poll.html#remove">remove</a></li>
2356             
2357                 <li data-name="LuCI.request.poll#start"><a href="LuCI.request.poll.html#start">start</a></li>
2358             
2359                 <li data-name="LuCI.request.poll#stop"><a href="LuCI.request.poll.html#stop">stop</a></li>
2360             
2361             </ul>
2362             <ul class="events itemMembers">
2363             
2364             </ul>
2365         </li>
2366     
2367         <li class="item" data-name="LuCI.response">
2368             <span class="title">
2369                 <a href="LuCI.response.html">LuCI.response</a>
2370                 
2371             </span>
2372             <ul class="members itemMembers">
2373             
2374             <span class="subtitle">Members</span>
2375             
2376                 <li data-name="LuCI.response#duration"><a href="LuCI.response.html#duration">duration</a></li>
2377             
2378                 <li data-name="LuCI.response#headers"><a href="LuCI.response.html#headers">headers</a></li>
2379             
2380                 <li data-name="LuCI.response#ok"><a href="LuCI.response.html#ok">ok</a></li>
2381             
2382                 <li data-name="LuCI.response#status"><a href="LuCI.response.html#status">status</a></li>
2383             
2384                 <li data-name="LuCI.response#statusText"><a href="LuCI.response.html#statusText">statusText</a></li>
2385             
2386                 <li data-name="LuCI.response#url"><a href="LuCI.response.html#url">url</a></li>
2387             
2388             </ul>
2389             <ul class="typedefs itemMembers">
2390             
2391             </ul>
2392             <ul class="typedefs itemMembers">
2393             
2394             </ul>
2395             <ul class="methods itemMembers">
2396             
2397             <span class="subtitle">Methods</span>
2398             
2399                 <li data-name="LuCI.response#blob"><a href="LuCI.response.html#blob">blob</a></li>
2400             
2401                 <li data-name="LuCI.response#clone"><a href="LuCI.response.html#clone">clone</a></li>
2402             
2403                 <li data-name="LuCI.response#json"><a href="LuCI.response.html#json">json</a></li>
2404             
2405                 <li data-name="LuCI.response#text"><a href="LuCI.response.html#text">text</a></li>
2406             
2407             </ul>
2408             <ul class="events itemMembers">
2409             
2410             </ul>
2411         </li>
2412     
2413         <li class="item" data-name="LuCI.rpc">
2414             <span class="title">
2415                 <a href="LuCI.rpc.html">LuCI.rpc</a>
2416                 
2417             </span>
2418             <ul class="members itemMembers">
2419             
2420             </ul>
2421             <ul class="typedefs itemMembers">
2422             
2423             <span class="subtitle">Typedefs</span>
2424             
2425                 <li data-name="LuCI.rpc.DeclareOptions"><a href="LuCI.rpc.html#.DeclareOptions">DeclareOptions</a></li>
2426             
2427                 <li data-name="LuCI.rpc~filterFn"><a href="LuCI.rpc.html#~filterFn">filterFn</a></li>
2428             
2429                 <li data-name="LuCI.rpc~interceptorFn"><a href="LuCI.rpc.html#~interceptorFn">interceptorFn</a></li>
2430             
2431                 <li data-name="LuCI.rpc~invokeFn"><a href="LuCI.rpc.html#~invokeFn">invokeFn</a></li>
2432             
2433             </ul>
2434             <ul class="typedefs itemMembers">
2435             
2436             </ul>
2437             <ul class="methods itemMembers">
2438             
2439             <span class="subtitle">Methods</span>
2440             
2441                 <li data-name="LuCI.rpc#addInterceptor"><a href="LuCI.rpc.html#addInterceptor">addInterceptor</a></li>
2442             
2443                 <li data-name="LuCI.rpc#declare"><a href="LuCI.rpc.html#declare">declare</a></li>
2444             
2445                 <li data-name="LuCI.rpc#getBaseURL"><a href="LuCI.rpc.html#getBaseURL">getBaseURL</a></li>
2446             
2447                 <li data-name="LuCI.rpc#getSessionID"><a href="LuCI.rpc.html#getSessionID">getSessionID</a></li>
2448             
2449                 <li data-name="LuCI.rpc#getStatusText"><a href="LuCI.rpc.html#getStatusText">getStatusText</a></li>
2450             
2451                 <li data-name="LuCI.rpc#list"><a href="LuCI.rpc.html#list">list</a></li>
2452             
2453                 <li data-name="LuCI.rpc#removeInterceptor"><a href="LuCI.rpc.html#removeInterceptor">removeInterceptor</a></li>
2454             
2455                 <li data-name="LuCI.rpc#setBaseURL"><a href="LuCI.rpc.html#setBaseURL">setBaseURL</a></li>
2456             
2457                 <li data-name="LuCI.rpc#setSessionID"><a href="LuCI.rpc.html#setSessionID">setSessionID</a></li>
2458             
2459             </ul>
2460             <ul class="events itemMembers">
2461             
2462             </ul>
2463         </li>
2464     
2465         <li class="item" data-name="LuCI.uci">
2466             <span class="title">
2467                 <a href="LuCI.uci.html">LuCI.uci</a>
2468                 
2469             </span>
2470             <ul class="members itemMembers">
2471             
2472             </ul>
2473             <ul class="typedefs itemMembers">
2474             
2475             <span class="subtitle">Typedefs</span>
2476             
2477                 <li data-name="LuCI.uci.ChangeRecord"><a href="LuCI.uci.html#.ChangeRecord">ChangeRecord</a></li>
2478             
2479                 <li data-name="LuCI.uci.SectionObject"><a href="LuCI.uci.html#.SectionObject">SectionObject</a></li>
2480             
2481                 <li data-name="LuCI.uci~sectionsFn"><a href="LuCI.uci.html#~sectionsFn">sectionsFn</a></li>
2482             
2483             </ul>
2484             <ul class="typedefs itemMembers">
2485             
2486             </ul>
2487             <ul class="methods itemMembers">
2488             
2489             <span class="subtitle">Methods</span>
2490             
2491                 <li data-name="LuCI.uci#add"><a href="LuCI.uci.html#add">add</a></li>
2492             
2493                 <li data-name="LuCI.uci#apply"><a href="LuCI.uci.html#apply">apply</a></li>
2494             
2495                 <li data-name="LuCI.uci#changes"><a href="LuCI.uci.html#changes">changes</a></li>
2496             
2497                 <li data-name="LuCI.uci#createSID"><a href="LuCI.uci.html#createSID">createSID</a></li>
2498             
2499                 <li data-name="LuCI.uci#get"><a href="LuCI.uci.html#get">get</a></li>
2500             
2501                 <li data-name="LuCI.uci#get_first"><a href="LuCI.uci.html#get_first">get_first</a></li>
2502             
2503                 <li data-name="LuCI.uci#load"><a href="LuCI.uci.html#load">load</a></li>
2504             
2505                 <li data-name="LuCI.uci#move"><a href="LuCI.uci.html#move">move</a></li>
2506             
2507                 <li data-name="LuCI.uci#remove"><a href="LuCI.uci.html#remove">remove</a></li>
2508             
2509                 <li data-name="LuCI.uci#resolveSID"><a href="LuCI.uci.html#resolveSID">resolveSID</a></li>
2510             
2511                 <li data-name="LuCI.uci#save"><a href="LuCI.uci.html#save">save</a></li>
2512             
2513                 <li data-name="LuCI.uci#sections"><a href="LuCI.uci.html#sections">sections</a></li>
2514             
2515                 <li data-name="LuCI.uci#set"><a href="LuCI.uci.html#set">set</a></li>
2516             
2517                 <li data-name="LuCI.uci#set_first"><a href="LuCI.uci.html#set_first">set_first</a></li>
2518             
2519                 <li data-name="LuCI.uci#unload"><a href="LuCI.uci.html#unload">unload</a></li>
2520             
2521                 <li data-name="LuCI.uci#unset"><a href="LuCI.uci.html#unset">unset</a></li>
2522             
2523                 <li data-name="LuCI.uci#unset_first"><a href="LuCI.uci.html#unset_first">unset_first</a></li>
2524             
2525             </ul>
2526             <ul class="events itemMembers">
2527             
2528             </ul>
2529         </li>
2530     
2531         <li class="item" data-name="LuCI.ui">
2532             <span class="title">
2533                 <a href="LuCI.ui.html">LuCI.ui</a>
2534                 
2535             </span>
2536             <ul class="members itemMembers">
2537             
2538             </ul>
2539             <ul class="typedefs itemMembers">
2540             
2541             <span class="subtitle">Typedefs</span>
2542             
2543                 <li data-name="LuCI.ui.FileUploadReply"><a href="LuCI.ui.html#.FileUploadReply">FileUploadReply</a></li>
2544             
2545             </ul>
2546             <ul class="typedefs itemMembers">
2547             
2548             </ul>
2549             <ul class="methods itemMembers">
2550             
2551             <span class="subtitle">Methods</span>
2552             
2553                 <li data-name="LuCI.ui#addNotification"><a href="LuCI.ui.html#addNotification">addNotification</a></li>
2554             
2555                 <li data-name="LuCI.ui#addValidator"><a href="LuCI.ui.html#addValidator">addValidator</a></li>
2556             
2557                 <li data-name="LuCI.ui#awaitReconnect"><a href="LuCI.ui.html#awaitReconnect">awaitReconnect</a></li>
2558             
2559                 <li data-name="LuCI.ui#createHandlerFn"><a href="LuCI.ui.html#createHandlerFn">createHandlerFn</a></li>
2560             
2561                 <li data-name="LuCI.ui#hideIndicator"><a href="LuCI.ui.html#hideIndicator">hideIndicator</a></li>
2562             
2563                 <li data-name="LuCI.ui#hideModal"><a href="LuCI.ui.html#hideModal">hideModal</a></li>
2564             
2565                 <li data-name="LuCI.ui#instantiateView"><a href="LuCI.ui.html#instantiateView">instantiateView</a></li>
2566             
2567                 <li data-name="LuCI.ui#itemlist"><a href="LuCI.ui.html#itemlist">itemlist</a></li>
2568             
2569                 <li data-name="LuCI.ui#pingDevice"><a href="LuCI.ui.html#pingDevice">pingDevice</a></li>
2570             
2571                 <li data-name="LuCI.ui#showIndicator"><a href="LuCI.ui.html#showIndicator">showIndicator</a></li>
2572             
2573                 <li data-name="LuCI.ui#showModal"><a href="LuCI.ui.html#showModal">showModal</a></li>
2574             
2575                 <li data-name="LuCI.ui#uploadFile"><a href="LuCI.ui.html#uploadFile">uploadFile</a></li>
2576             
2577             </ul>
2578             <ul class="events itemMembers">
2579             
2580             </ul>
2581         </li>
2582     
2583         <li class="item" data-name="LuCI.ui.AbstractElement">
2584             <span class="title">
2585                 <a href="LuCI.ui.AbstractElement.html">LuCI.ui.AbstractElement</a>
2586                 
2587             </span>
2588             <ul class="members itemMembers">
2589             
2590             </ul>
2591             <ul class="typedefs itemMembers">
2592             
2593             <span class="subtitle">Typedefs</span>
2594             
2595                 <li data-name="LuCI.ui.AbstractElement.InitOptions"><a href="LuCI.ui.AbstractElement.html#.InitOptions">InitOptions</a></li>
2596             
2597             </ul>
2598             <ul class="typedefs itemMembers">
2599             
2600             </ul>
2601             <ul class="methods itemMembers">
2602             
2603             <span class="subtitle">Methods</span>
2604             
2605                 <li data-name="LuCI.ui.AbstractElement#getValue"><a href="LuCI.ui.AbstractElement.html#getValue">getValue</a></li>
2606             
2607                 <li data-name="LuCI.ui.AbstractElement#isValid"><a href="LuCI.ui.AbstractElement.html#isValid">isValid</a></li>
2608             
2609                 <li data-name="LuCI.ui.AbstractElement#registerEvents"><a href="LuCI.ui.AbstractElement.html#registerEvents">registerEvents</a></li>
2610             
2611                 <li data-name="LuCI.ui.AbstractElement#render"><a href="LuCI.ui.AbstractElement.html#render">render</a></li>
2612             
2613                 <li data-name="LuCI.ui.AbstractElement#setChangeEvents"><a href="LuCI.ui.AbstractElement.html#setChangeEvents">setChangeEvents</a></li>
2614             
2615                 <li data-name="LuCI.ui.AbstractElement#setUpdateEvents"><a href="LuCI.ui.AbstractElement.html#setUpdateEvents">setUpdateEvents</a></li>
2616             
2617                 <li data-name="LuCI.ui.AbstractElement#setValue"><a href="LuCI.ui.AbstractElement.html#setValue">setValue</a></li>
2618             
2619                 <li data-name="LuCI.ui.AbstractElement#triggerValidation"><a href="LuCI.ui.AbstractElement.html#triggerValidation">triggerValidation</a></li>
2620             
2621             </ul>
2622             <ul class="events itemMembers">
2623             
2624             </ul>
2625         </li>
2626     
2627         <li class="item" data-name="LuCI.ui.changes">
2628             <span class="title">
2629                 <a href="LuCI.ui.changes.html">LuCI.ui.changes</a>
2630                 
2631             </span>
2632             <ul class="members itemMembers">
2633             
2634             </ul>
2635             <ul class="typedefs itemMembers">
2636             
2637             </ul>
2638             <ul class="typedefs itemMembers">
2639             
2640             </ul>
2641             <ul class="methods itemMembers">
2642             
2643             <span class="subtitle">Methods</span>
2644             
2645                 <li data-name="LuCI.ui.changes#apply"><a href="LuCI.ui.changes.html#apply">apply</a></li>
2646             
2647                 <li data-name="LuCI.ui.changes#displayChanges"><a href="LuCI.ui.changes.html#displayChanges">displayChanges</a></li>
2648             
2649                 <li data-name="LuCI.ui.changes#renderChangeIndicator"><a href="LuCI.ui.changes.html#renderChangeIndicator">renderChangeIndicator</a></li>
2650             
2651                 <li data-name="LuCI.ui.changes#revert"><a href="LuCI.ui.changes.html#revert">revert</a></li>
2652             
2653                 <li data-name="LuCI.ui.changes#setIndicator"><a href="LuCI.ui.changes.html#setIndicator">setIndicator</a></li>
2654             
2655             </ul>
2656             <ul class="events itemMembers">
2657             
2658             </ul>
2659         </li>
2660     
2661         <li class="item" data-name="LuCI.ui.Checkbox">
2662             <span class="title">
2663                 <a href="LuCI.ui.Checkbox.html">LuCI.ui.Checkbox</a>
2664                 
2665             </span>
2666             <ul class="members itemMembers">
2667             
2668             </ul>
2669             <ul class="typedefs itemMembers">
2670             
2671             <span class="subtitle">Typedefs</span>
2672             
2673                 <li data-name="LuCI.ui.Checkbox.InitOptions"><a href="LuCI.ui.Checkbox.html#.InitOptions">InitOptions</a></li>
2674             
2675             </ul>
2676             <ul class="typedefs itemMembers">
2677             
2678             </ul>
2679             <ul class="methods itemMembers">
2680             
2681             <span class="subtitle">Methods</span>
2682             
2683                 <li data-name="LuCI.ui.Checkbox#getValue"><a href="LuCI.ui.Checkbox.html#getValue">getValue</a></li>
2684             
2685                 <li data-name="LuCI.ui.Checkbox#isChecked"><a href="LuCI.ui.Checkbox.html#isChecked">isChecked</a></li>
2686             
2687                 <li data-name="LuCI.ui.Checkbox#isValid"><a href="LuCI.ui.Checkbox.html#isValid">isValid</a></li>
2688             
2689                 <li data-name="LuCI.ui.Checkbox#registerEvents"><a href="LuCI.ui.Checkbox.html#registerEvents">registerEvents</a></li>
2690             
2691                 <li data-name="LuCI.ui.Checkbox#render"><a href="LuCI.ui.Checkbox.html#render">render</a></li>
2692             
2693                 <li data-name="LuCI.ui.Checkbox#setChangeEvents"><a href="LuCI.ui.Checkbox.html#setChangeEvents">setChangeEvents</a></li>
2694             
2695                 <li data-name="LuCI.ui.Checkbox#setUpdateEvents"><a href="LuCI.ui.Checkbox.html#setUpdateEvents">setUpdateEvents</a></li>
2696             
2697                 <li data-name="LuCI.ui.Checkbox#setValue"><a href="LuCI.ui.Checkbox.html#setValue">setValue</a></li>
2698             
2699                 <li data-name="LuCI.ui.Checkbox#triggerValidation"><a href="LuCI.ui.Checkbox.html#triggerValidation">triggerValidation</a></li>
2700             
2701             </ul>
2702             <ul class="events itemMembers">
2703             
2704             </ul>
2705         </li>
2706     
2707         <li class="item" data-name="LuCI.ui.Combobox">
2708             <span class="title">
2709                 <a href="LuCI.ui.Combobox.html">LuCI.ui.Combobox</a>
2710                 
2711             </span>
2712             <ul class="members itemMembers">
2713             
2714             </ul>
2715             <ul class="typedefs itemMembers">
2716             
2717             <span class="subtitle">Typedefs</span>
2718             
2719                 <li data-name="LuCI.ui.Combobox.InitOptions"><a href="LuCI.ui.Combobox.html#.InitOptions">InitOptions</a></li>
2720             
2721             </ul>
2722             <ul class="typedefs itemMembers">
2723             
2724             </ul>
2725             <ul class="methods itemMembers">
2726             
2727             <span class="subtitle">Methods</span>
2728             
2729                 <li data-name="LuCI.ui.Combobox#addChoices"><a href="LuCI.ui.Combobox.html#addChoices">addChoices</a></li>
2730             
2731                 <li data-name="LuCI.ui.Combobox#clearChoices"><a href="LuCI.ui.Combobox.html#clearChoices">clearChoices</a></li>
2732             
2733                 <li data-name="LuCI.ui.Combobox#closeAllDropdowns"><a href="LuCI.ui.Combobox.html#closeAllDropdowns">closeAllDropdowns</a></li>
2734             
2735                 <li data-name="LuCI.ui.Combobox#isValid"><a href="LuCI.ui.Combobox.html#isValid">isValid</a></li>
2736             
2737                 <li data-name="LuCI.ui.Combobox#registerEvents"><a href="LuCI.ui.Combobox.html#registerEvents">registerEvents</a></li>
2738             
2739                 <li data-name="LuCI.ui.Combobox#setChangeEvents"><a href="LuCI.ui.Combobox.html#setChangeEvents">setChangeEvents</a></li>
2740             
2741                 <li data-name="LuCI.ui.Combobox#setUpdateEvents"><a href="LuCI.ui.Combobox.html#setUpdateEvents">setUpdateEvents</a></li>
2742             
2743                 <li data-name="LuCI.ui.Combobox#triggerValidation"><a href="LuCI.ui.Combobox.html#triggerValidation">triggerValidation</a></li>
2744             
2745             </ul>
2746             <ul class="events itemMembers">
2747             
2748             </ul>
2749         </li>
2750     
2751         <li class="item" data-name="LuCI.ui.ComboButton">
2752             <span class="title">
2753                 <a href="LuCI.ui.ComboButton.html">LuCI.ui.ComboButton</a>
2754                 
2755             </span>
2756             <ul class="members itemMembers">
2757             
2758             </ul>
2759             <ul class="typedefs itemMembers">
2760             
2761             <span class="subtitle">Typedefs</span>
2762             
2763                 <li data-name="LuCI.ui.ComboButton.InitOptions"><a href="LuCI.ui.ComboButton.html#.InitOptions">InitOptions</a></li>
2764             
2765             </ul>
2766             <ul class="typedefs itemMembers">
2767             
2768             </ul>
2769             <ul class="methods itemMembers">
2770             
2771             <span class="subtitle">Methods</span>
2772             
2773                 <li data-name="LuCI.ui.ComboButton#addChoices"><a href="LuCI.ui.ComboButton.html#addChoices">addChoices</a></li>
2774             
2775                 <li data-name="LuCI.ui.ComboButton#clearChoices"><a href="LuCI.ui.ComboButton.html#clearChoices">clearChoices</a></li>
2776             
2777                 <li data-name="LuCI.ui.ComboButton#closeAllDropdowns"><a href="LuCI.ui.ComboButton.html#closeAllDropdowns">closeAllDropdowns</a></li>
2778             
2779                 <li data-name="LuCI.ui.ComboButton#isValid"><a href="LuCI.ui.ComboButton.html#isValid">isValid</a></li>
2780             
2781                 <li data-name="LuCI.ui.ComboButton#registerEvents"><a href="LuCI.ui.ComboButton.html#registerEvents">registerEvents</a></li>
2782             
2783                 <li data-name="LuCI.ui.ComboButton#setChangeEvents"><a href="LuCI.ui.ComboButton.html#setChangeEvents">setChangeEvents</a></li>
2784             
2785                 <li data-name="LuCI.ui.ComboButton#setUpdateEvents"><a href="LuCI.ui.ComboButton.html#setUpdateEvents">setUpdateEvents</a></li>
2786             
2787                 <li data-name="LuCI.ui.ComboButton#triggerValidation"><a href="LuCI.ui.ComboButton.html#triggerValidation">triggerValidation</a></li>
2788             
2789             </ul>
2790             <ul class="events itemMembers">
2791             
2792             </ul>
2793         </li>
2794     
2795         <li class="item" data-name="LuCI.ui.Dropdown">
2796             <span class="title">
2797                 <a href="LuCI.ui.Dropdown.html">LuCI.ui.Dropdown</a>
2798                 
2799             </span>
2800             <ul class="members itemMembers">
2801             
2802             </ul>
2803             <ul class="typedefs itemMembers">
2804             
2805             <span class="subtitle">Typedefs</span>
2806             
2807                 <li data-name="LuCI.ui.Dropdown.InitOptions"><a href="LuCI.ui.Dropdown.html#.InitOptions">InitOptions</a></li>
2808             
2809             </ul>
2810             <ul class="typedefs itemMembers">
2811             
2812             </ul>
2813             <ul class="methods itemMembers">
2814             
2815             <span class="subtitle">Methods</span>
2816             
2817                 <li data-name="LuCI.ui.Dropdown#addChoices"><a href="LuCI.ui.Dropdown.html#addChoices">addChoices</a></li>
2818             
2819                 <li data-name="LuCI.ui.Dropdown#clearChoices"><a href="LuCI.ui.Dropdown.html#clearChoices">clearChoices</a></li>
2820             
2821                 <li data-name="LuCI.ui.Dropdown#closeAllDropdowns"><a href="LuCI.ui.Dropdown.html#closeAllDropdowns">closeAllDropdowns</a></li>
2822             
2823                 <li data-name="LuCI.ui.Dropdown#getValue"><a href="LuCI.ui.Dropdown.html#getValue">getValue</a></li>
2824             
2825                 <li data-name="LuCI.ui.Dropdown#isValid"><a href="LuCI.ui.Dropdown.html#isValid">isValid</a></li>
2826             
2827                 <li data-name="LuCI.ui.Dropdown#registerEvents"><a href="LuCI.ui.Dropdown.html#registerEvents">registerEvents</a></li>
2828             
2829                 <li data-name="LuCI.ui.Dropdown#render"><a href="LuCI.ui.Dropdown.html#render">render</a></li>
2830             
2831                 <li data-name="LuCI.ui.Dropdown#setChangeEvents"><a href="LuCI.ui.Dropdown.html#setChangeEvents">setChangeEvents</a></li>
2832             
2833                 <li data-name="LuCI.ui.Dropdown#setUpdateEvents"><a href="LuCI.ui.Dropdown.html#setUpdateEvents">setUpdateEvents</a></li>
2834             
2835                 <li data-name="LuCI.ui.Dropdown#setValue"><a href="LuCI.ui.Dropdown.html#setValue">setValue</a></li>
2836             
2837                 <li data-name="LuCI.ui.Dropdown#triggerValidation"><a href="LuCI.ui.Dropdown.html#triggerValidation">triggerValidation</a></li>
2838             
2839             </ul>
2840             <ul class="events itemMembers">
2841             
2842             </ul>
2843         </li>
2844     
2845         <li class="item" data-name="LuCI.ui.DynamicList">
2846             <span class="title">
2847                 <a href="LuCI.ui.DynamicList.html">LuCI.ui.DynamicList</a>
2848                 
2849             </span>
2850             <ul class="members itemMembers">
2851             
2852             </ul>
2853             <ul class="typedefs itemMembers">
2854             
2855             <span class="subtitle">Typedefs</span>
2856             
2857                 <li data-name="LuCI.ui.DynamicList.InitOptions"><a href="LuCI.ui.DynamicList.html#.InitOptions">InitOptions</a></li>
2858             
2859             </ul>
2860             <ul class="typedefs itemMembers">
2861             
2862             </ul>
2863             <ul class="methods itemMembers">
2864             
2865             <span class="subtitle">Methods</span>
2866             
2867                 <li data-name="LuCI.ui.DynamicList#addChoices"><a href="LuCI.ui.DynamicList.html#addChoices">addChoices</a></li>
2868             
2869                 <li data-name="LuCI.ui.DynamicList#clearChoices"><a href="LuCI.ui.DynamicList.html#clearChoices">clearChoices</a></li>
2870             
2871                 <li data-name="LuCI.ui.DynamicList#getValue"><a href="LuCI.ui.DynamicList.html#getValue">getValue</a></li>
2872             
2873                 <li data-name="LuCI.ui.DynamicList#isValid"><a href="LuCI.ui.DynamicList.html#isValid">isValid</a></li>
2874             
2875                 <li data-name="LuCI.ui.DynamicList#registerEvents"><a href="LuCI.ui.DynamicList.html#registerEvents">registerEvents</a></li>
2876             
2877                 <li data-name="LuCI.ui.DynamicList#render"><a href="LuCI.ui.DynamicList.html#render">render</a></li>
2878             
2879                 <li data-name="LuCI.ui.DynamicList#setChangeEvents"><a href="LuCI.ui.DynamicList.html#setChangeEvents">setChangeEvents</a></li>
2880             
2881                 <li data-name="LuCI.ui.DynamicList#setUpdateEvents"><a href="LuCI.ui.DynamicList.html#setUpdateEvents">setUpdateEvents</a></li>
2882             
2883                 <li data-name="LuCI.ui.DynamicList#setValue"><a href="LuCI.ui.DynamicList.html#setValue">setValue</a></li>
2884             
2885                 <li data-name="LuCI.ui.DynamicList#triggerValidation"><a href="LuCI.ui.DynamicList.html#triggerValidation">triggerValidation</a></li>
2886             
2887             </ul>
2888             <ul class="events itemMembers">
2889             
2890             </ul>
2891         </li>
2892     
2893         <li class="item" data-name="LuCI.ui.FileUpload">
2894             <span class="title">
2895                 <a href="LuCI.ui.FileUpload.html">LuCI.ui.FileUpload</a>
2896                 
2897             </span>
2898             <ul class="members itemMembers">
2899             
2900             </ul>
2901             <ul class="typedefs itemMembers">
2902             
2903             <span class="subtitle">Typedefs</span>
2904             
2905                 <li data-name="LuCI.ui.FileUpload.InitOptions"><a href="LuCI.ui.FileUpload.html#.InitOptions">InitOptions</a></li>
2906             
2907             </ul>
2908             <ul class="typedefs itemMembers">
2909             
2910             </ul>
2911             <ul class="methods itemMembers">
2912             
2913             <span class="subtitle">Methods</span>
2914             
2915                 <li data-name="LuCI.ui.FileUpload#getValue"><a href="LuCI.ui.FileUpload.html#getValue">getValue</a></li>
2916             
2917                 <li data-name="LuCI.ui.FileUpload#isValid"><a href="LuCI.ui.FileUpload.html#isValid">isValid</a></li>
2918             
2919                 <li data-name="LuCI.ui.FileUpload#registerEvents"><a href="LuCI.ui.FileUpload.html#registerEvents">registerEvents</a></li>
2920             
2921                 <li data-name="LuCI.ui.FileUpload#render"><a href="LuCI.ui.FileUpload.html#render">render</a></li>
2922             
2923                 <li data-name="LuCI.ui.FileUpload#setChangeEvents"><a href="LuCI.ui.FileUpload.html#setChangeEvents">setChangeEvents</a></li>
2924             
2925                 <li data-name="LuCI.ui.FileUpload#setUpdateEvents"><a href="LuCI.ui.FileUpload.html#setUpdateEvents">setUpdateEvents</a></li>
2926             
2927                 <li data-name="LuCI.ui.FileUpload#setValue"><a href="LuCI.ui.FileUpload.html#setValue">setValue</a></li>
2928             
2929                 <li data-name="LuCI.ui.FileUpload#triggerValidation"><a href="LuCI.ui.FileUpload.html#triggerValidation">triggerValidation</a></li>
2930             
2931             </ul>
2932             <ul class="events itemMembers">
2933             
2934             </ul>
2935         </li>
2936     
2937         <li class="item" data-name="LuCI.ui.Hiddenfield">
2938             <span class="title">
2939                 <a href="LuCI.ui.Hiddenfield.html">LuCI.ui.Hiddenfield</a>
2940                 
2941             </span>
2942             <ul class="members itemMembers">
2943             
2944             </ul>
2945             <ul class="typedefs itemMembers">
2946             
2947             </ul>
2948             <ul class="typedefs itemMembers">
2949             
2950             </ul>
2951             <ul class="methods itemMembers">
2952             
2953             <span class="subtitle">Methods</span>
2954             
2955                 <li data-name="LuCI.ui.Hiddenfield#getValue"><a href="LuCI.ui.Hiddenfield.html#getValue">getValue</a></li>
2956             
2957                 <li data-name="LuCI.ui.Hiddenfield#isValid"><a href="LuCI.ui.Hiddenfield.html#isValid">isValid</a></li>
2958             
2959                 <li data-name="LuCI.ui.Hiddenfield#registerEvents"><a href="LuCI.ui.Hiddenfield.html#registerEvents">registerEvents</a></li>
2960             
2961                 <li data-name="LuCI.ui.Hiddenfield#render"><a href="LuCI.ui.Hiddenfield.html#render">render</a></li>
2962             
2963                 <li data-name="LuCI.ui.Hiddenfield#setChangeEvents"><a href="LuCI.ui.Hiddenfield.html#setChangeEvents">setChangeEvents</a></li>
2964             
2965                 <li data-name="LuCI.ui.Hiddenfield#setUpdateEvents"><a href="LuCI.ui.Hiddenfield.html#setUpdateEvents">setUpdateEvents</a></li>
2966             
2967                 <li data-name="LuCI.ui.Hiddenfield#setValue"><a href="LuCI.ui.Hiddenfield.html#setValue">setValue</a></li>
2968             
2969                 <li data-name="LuCI.ui.Hiddenfield#triggerValidation"><a href="LuCI.ui.Hiddenfield.html#triggerValidation">triggerValidation</a></li>
2970             
2971             </ul>
2972             <ul class="events itemMembers">
2973             
2974             </ul>
2975         </li>
2976     
2977         <li class="item" data-name="LuCI.ui.Select">
2978             <span class="title">
2979                 <a href="LuCI.ui.Select.html">LuCI.ui.Select</a>
2980                 
2981             </span>
2982             <ul class="members itemMembers">
2983             
2984             </ul>
2985             <ul class="typedefs itemMembers">
2986             
2987             <span class="subtitle">Typedefs</span>
2988             
2989                 <li data-name="LuCI.ui.Select.InitOptions"><a href="LuCI.ui.Select.html#.InitOptions">InitOptions</a></li>
2990             
2991             </ul>
2992             <ul class="typedefs itemMembers">
2993             
2994             </ul>
2995             <ul class="methods itemMembers">
2996             
2997             <span class="subtitle">Methods</span>
2998             
2999                 <li data-name="LuCI.ui.Select#getValue"><a href="LuCI.ui.Select.html#getValue">getValue</a></li>
3000             
3001                 <li data-name="LuCI.ui.Select#isValid"><a href="LuCI.ui.Select.html#isValid">isValid</a></li>
3002             
3003                 <li data-name="LuCI.ui.Select#registerEvents"><a href="LuCI.ui.Select.html#registerEvents">registerEvents</a></li>
3004             
3005                 <li data-name="LuCI.ui.Select#render"><a href="LuCI.ui.Select.html#render">render</a></li>
3006             
3007                 <li data-name="LuCI.ui.Select#setChangeEvents"><a href="LuCI.ui.Select.html#setChangeEvents">setChangeEvents</a></li>
3008             
3009                 <li data-name="LuCI.ui.Select#setUpdateEvents"><a href="LuCI.ui.Select.html#setUpdateEvents">setUpdateEvents</a></li>
3010             
3011                 <li data-name="LuCI.ui.Select#setValue"><a href="LuCI.ui.Select.html#setValue">setValue</a></li>
3012             
3013                 <li data-name="LuCI.ui.Select#triggerValidation"><a href="LuCI.ui.Select.html#triggerValidation">triggerValidation</a></li>
3014             
3015             </ul>
3016             <ul class="events itemMembers">
3017             
3018             </ul>
3019         </li>
3020     
3021         <li class="item" data-name="LuCI.ui.tabs">
3022             <span class="title">
3023                 <a href="LuCI.ui.tabs.html">LuCI.ui.tabs</a>
3024                 
3025             </span>
3026             <ul class="members itemMembers">
3027             
3028             </ul>
3029             <ul class="typedefs itemMembers">
3030             
3031             </ul>
3032             <ul class="typedefs itemMembers">
3033             
3034             </ul>
3035             <ul class="methods itemMembers">
3036             
3037             <span class="subtitle">Methods</span>
3038             
3039                 <li data-name="LuCI.ui.tabs#initTabGroup"><a href="LuCI.ui.tabs.html#initTabGroup">initTabGroup</a></li>
3040             
3041                 <li data-name="LuCI.ui.tabs#isEmptyPane"><a href="LuCI.ui.tabs.html#isEmptyPane">isEmptyPane</a></li>
3042             
3043             </ul>
3044             <ul class="events itemMembers">
3045             
3046             </ul>
3047         </li>
3048     
3049         <li class="item" data-name="LuCI.ui.Textarea">
3050             <span class="title">
3051                 <a href="LuCI.ui.Textarea.html">LuCI.ui.Textarea</a>
3052                 
3053             </span>
3054             <ul class="members itemMembers">
3055             
3056             </ul>
3057             <ul class="typedefs itemMembers">
3058             
3059             <span class="subtitle">Typedefs</span>
3060             
3061                 <li data-name="LuCI.ui.Textarea.InitOptions"><a href="LuCI.ui.Textarea.html#.InitOptions">InitOptions</a></li>
3062             
3063             </ul>
3064             <ul class="typedefs itemMembers">
3065             
3066             </ul>
3067             <ul class="methods itemMembers">
3068             
3069             <span class="subtitle">Methods</span>
3070             
3071                 <li data-name="LuCI.ui.Textarea#getValue"><a href="LuCI.ui.Textarea.html#getValue">getValue</a></li>
3072             
3073                 <li data-name="LuCI.ui.Textarea#isValid"><a href="LuCI.ui.Textarea.html#isValid">isValid</a></li>
3074             
3075                 <li data-name="LuCI.ui.Textarea#registerEvents"><a href="LuCI.ui.Textarea.html#registerEvents">registerEvents</a></li>
3076             
3077                 <li data-name="LuCI.ui.Textarea#render"><a href="LuCI.ui.Textarea.html#render">render</a></li>
3078             
3079                 <li data-name="LuCI.ui.Textarea#setChangeEvents"><a href="LuCI.ui.Textarea.html#setChangeEvents">setChangeEvents</a></li>
3080             
3081                 <li data-name="LuCI.ui.Textarea#setUpdateEvents"><a href="LuCI.ui.Textarea.html#setUpdateEvents">setUpdateEvents</a></li>
3082             
3083                 <li data-name="LuCI.ui.Textarea#setValue"><a href="LuCI.ui.Textarea.html#setValue">setValue</a></li>
3084             
3085                 <li data-name="LuCI.ui.Textarea#triggerValidation"><a href="LuCI.ui.Textarea.html#triggerValidation">triggerValidation</a></li>
3086             
3087             </ul>
3088             <ul class="events itemMembers">
3089             
3090             </ul>
3091         </li>
3092     
3093         <li class="item" data-name="LuCI.ui.Textfield">
3094             <span class="title">
3095                 <a href="LuCI.ui.Textfield.html">LuCI.ui.Textfield</a>
3096                 
3097             </span>
3098             <ul class="members itemMembers">
3099             
3100             </ul>
3101             <ul class="typedefs itemMembers">
3102             
3103             <span class="subtitle">Typedefs</span>
3104             
3105                 <li data-name="LuCI.ui.Textfield.InitOptions"><a href="LuCI.ui.Textfield.html#.InitOptions">InitOptions</a></li>
3106             
3107             </ul>
3108             <ul class="typedefs itemMembers">
3109             
3110             </ul>
3111             <ul class="methods itemMembers">
3112             
3113             <span class="subtitle">Methods</span>
3114             
3115                 <li data-name="LuCI.ui.Textfield#getValue"><a href="LuCI.ui.Textfield.html#getValue">getValue</a></li>
3116             
3117                 <li data-name="LuCI.ui.Textfield#isValid"><a href="LuCI.ui.Textfield.html#isValid">isValid</a></li>
3118             
3119                 <li data-name="LuCI.ui.Textfield#registerEvents"><a href="LuCI.ui.Textfield.html#registerEvents">registerEvents</a></li>
3120             
3121                 <li data-name="LuCI.ui.Textfield#render"><a href="LuCI.ui.Textfield.html#render">render</a></li>
3122             
3123                 <li data-name="LuCI.ui.Textfield#setChangeEvents"><a href="LuCI.ui.Textfield.html#setChangeEvents">setChangeEvents</a></li>
3124             
3125                 <li data-name="LuCI.ui.Textfield#setUpdateEvents"><a href="LuCI.ui.Textfield.html#setUpdateEvents">setUpdateEvents</a></li>
3126             
3127                 <li data-name="LuCI.ui.Textfield#setValue"><a href="LuCI.ui.Textfield.html#setValue">setValue</a></li>
3128             
3129                 <li data-name="LuCI.ui.Textfield#triggerValidation"><a href="LuCI.ui.Textfield.html#triggerValidation">triggerValidation</a></li>
3130             
3131             </ul>
3132             <ul class="events itemMembers">
3133             
3134             </ul>
3135         </li>
3136     
3137         <li class="item" data-name="LuCI.view">
3138             <span class="title">
3139                 <a href="LuCI.view.html">LuCI.view</a>
3140                 
3141             </span>
3142             <ul class="members itemMembers">
3143             
3144             </ul>
3145             <ul class="typedefs itemMembers">
3146             
3147             </ul>
3148             <ul class="typedefs itemMembers">
3149             
3150             </ul>
3151             <ul class="methods itemMembers">
3152             
3153             <span class="subtitle">Methods</span>
3154             
3155                 <li data-name="LuCI.view#addFooter"><a href="LuCI.view.html#addFooter">addFooter</a></li>
3156             
3157                 <li data-name="LuCI.view#handleReset"><a href="LuCI.view.html#handleReset">handleReset</a></li>
3158             
3159                 <li data-name="LuCI.view#handleSave"><a href="LuCI.view.html#handleSave">handleSave</a></li>
3160             
3161                 <li data-name="LuCI.view#handleSaveApply"><a href="LuCI.view.html#handleSaveApply">handleSaveApply</a></li>
3162             
3163                 <li data-name="LuCI.view#load"><a href="LuCI.view.html#load">load</a></li>
3164             
3165                 <li data-name="LuCI.view#render"><a href="LuCI.view.html#render">render</a></li>
3166             
3167             </ul>
3168             <ul class="events itemMembers">
3169             
3170             </ul>
3171         </li>
3172     
3173         <li class="item" data-name="LuCI.xhr">
3174             <span class="title">
3175                 <a href="LuCI.xhr.html">LuCI.xhr</a>
3176                 
3177             </span>
3178             <ul class="members itemMembers">
3179             
3180             </ul>
3181             <ul class="typedefs itemMembers">
3182             
3183             </ul>
3184             <ul class="typedefs itemMembers">
3185             
3186             </ul>
3187             <ul class="methods itemMembers">
3188             
3189             <span class="subtitle">Methods</span>
3190             
3191                 <li data-name="LuCI.xhr#abort"><a href="LuCI.xhr.html#abort">abort</a></li>
3192             
3193                 <li data-name="LuCI.xhr#busy"><a href="LuCI.xhr.html#busy">busy</a></li>
3194             
3195                 <li data-name="LuCI.xhr#cancel"><a href="LuCI.xhr.html#cancel">cancel</a></li>
3196             
3197                 <li data-name="LuCI.xhr#get"><a href="LuCI.xhr.html#get">get</a></li>
3198             
3199                 <li data-name="LuCI.xhr#post"><a href="LuCI.xhr.html#post">post</a></li>
3200             
3201                 <li data-name="LuCI.xhr#send_form"><a href="LuCI.xhr.html#send_form">send_form</a></li>
3202             
3203             </ul>
3204             <ul class="events itemMembers">
3205             
3206             </ul>
3207         </li>
3208     
3209     </ul>
3210 </div>
3211     <div class="main">
3212         <h1 class="page-title" data-filename="ui.js.html">Source: ui.js</h1>
3213         
3214
3215
3216     
3217     <section>
3218         <article>
3219             <pre id="source-code" class="prettyprint source "><code>'use strict';
3220 'require validation';
3221 'require baseclass';
3222 'require request';
3223 'require poll';
3224 'require dom';
3225 'require rpc';
3226 'require uci';
3227 'require fs';
3228
3229 var modalDiv = null,
3230     tooltipDiv = null,
3231     indicatorDiv = null,
3232     tooltipTimeout = null;
3233
3234 /**
3235  * @class AbstractElement
3236  * @memberof LuCI.ui
3237  * @hideconstructor
3238  * @classdesc
3239  *
3240  * The `AbstractElement` class serves as abstract base for the different widgets
3241  * implemented by `LuCI.ui`. It provides the common logic for getting and
3242  * setting values, for checking the validity state and for wiring up required
3243  * events.
3244  *
3245  * UI widget instances are usually not supposed to be created by view code
3246  * directly, instead they're implicitely created by `LuCI.form` when
3247  * instantiating CBI forms.
3248  *
3249  * This class is automatically instantiated as part of `LuCI.ui`. To use it
3250  * in views, use `'require ui'` and refer to `ui.AbstractElement`. To import
3251  * it in external JavaScript, use `L.require("ui").then(...)` and access the
3252  * `AbstractElement` property of the class instance value.
3253  */
3254 var UIElement = baseclass.extend(/** @lends LuCI.ui.AbstractElement.prototype */ {
3255         /**
3256          * @typedef {Object} InitOptions
3257          * @memberof LuCI.ui.AbstractElement
3258          *
3259          * @property {string} [id]
3260          * Specifies the widget ID to use. It will be used as HTML `id` attribute
3261          * on the toplevel widget DOM node.
3262          *
3263          * @property {string} [name]
3264          * Specifies the widget name which is set as HTML `name` attribute on the
3265          * corresponding `&lt;input>` element.
3266          *
3267          * @property {boolean} [optional=true]
3268          * Specifies whether the input field allows empty values.
3269          *
3270          * @property {string} [datatype=string]
3271          * An expression describing the input data validation constraints.
3272          * It defaults to `string` which will allow any value.
3273          * See {@link LuCI.validation} for details on the expression format.
3274          *
3275          * @property {function} [validator]
3276          * Specifies a custom validator function which is invoked after the
3277          * standard validation constraints are checked. The function should return
3278          * `true` to accept the given input value. Any other return value type is
3279          * converted to a string and treated as validation error message.
3280          */
3281
3282         /**
3283          * Read the current value of the input widget.
3284          *
3285          * @instance
3286          * @memberof LuCI.ui.AbstractElement
3287          * @returns {string|string[]|null}
3288          * The current value of the input element. For simple inputs like text
3289          * fields or selects, the return value type will be a - possibly empty -
3290          * string. Complex widgets such as `DynamicList` instances may result in
3291          * an array of strings or `null` for unset values.
3292          */
3293         getValue: function() {
3294                 if (dom.matches(this.node, 'select') || dom.matches(this.node, 'input'))
3295                         return this.node.value;
3296
3297                 return null;
3298         },
3299
3300         /**
3301          * Set the current value of the input widget.
3302          *
3303          * @instance
3304          * @memberof LuCI.ui.AbstractElement
3305          * @param {string|string[]|null} value
3306          * The value to set the input element to. For simple inputs like text
3307          * fields or selects, the value should be a - possibly empty - string.
3308          * Complex widgets such as `DynamicList` instances may accept string array
3309          * or `null` values.
3310          */
3311         setValue: function(value) {
3312                 if (dom.matches(this.node, 'select') || dom.matches(this.node, 'input'))
3313                         this.node.value = value;
3314         },
3315
3316         /**
3317          * Check whether the current input value is valid.
3318          *
3319          * @instance
3320          * @memberof LuCI.ui.AbstractElement
3321          * @returns {boolean}
3322          * Returns `true` if the current input value is valid or `false` if it does
3323          * not meet the validation constraints.
3324          */
3325         isValid: function() {
3326                 return (this.validState !== false);
3327         },
3328
3329         /**
3330          * Force validation of the current input value.
3331          *
3332          * Usually input validation is automatically triggered by various DOM events
3333          * bound to the input widget. In some cases it is required though to manually
3334          * trigger validation runs, e.g. when programmatically altering values.
3335          *
3336          * @instance
3337          * @memberof LuCI.ui.AbstractElement
3338          */
3339         triggerValidation: function() {
3340                 if (typeof(this.vfunc) != 'function')
3341                         return false;
3342
3343                 var wasValid = this.isValid();
3344
3345                 this.vfunc();
3346
3347                 return (wasValid != this.isValid());
3348         },
3349
3350         /**
3351          * Dispatch a custom (synthetic) event in response to received events.
3352          *
3353          * Sets up event handlers on the given target DOM node for the given event
3354          * names that dispatch a custom event of the given type to the widget root
3355          * DOM node.
3356          *
3357          * The primary purpose of this function is to set up a series of custom
3358          * uniform standard events such as `widget-update`, `validation-success`,
3359          * `validation-failure` etc. which are triggered by various different
3360          * widget specific native DOM events.
3361          *
3362          * @instance
3363          * @memberof LuCI.ui.AbstractElement
3364          * @param {Node} targetNode
3365          * Specifies the DOM node on which the native event listeners should be
3366          * registered.
3367          *
3368          * @param {string} synevent
3369          * The name of the custom event to dispatch to the widget root DOM node.
3370          *
3371          * @param {string[]} events
3372          * The native DOM events for which event handlers should be registered.
3373          */
3374         registerEvents: function(targetNode, synevent, events) {
3375                 var dispatchFn = L.bind(function(ev) {
3376                         this.node.dispatchEvent(new CustomEvent(synevent, { bubbles: true }));
3377                 }, this);
3378
3379                 for (var i = 0; i &lt; events.length; i++)
3380                         targetNode.addEventListener(events[i], dispatchFn);
3381         },
3382
3383         /**
3384          * Setup listeners for native DOM events that may update the widget value.
3385          *
3386          * Sets up event handlers on the given target DOM node for the given event
3387          * names which may cause the input value to update, such as `keyup` or
3388          * `onclick` events. In contrast to change events, such update events will
3389          * trigger input value validation.
3390          *
3391          * @instance
3392          * @memberof LuCI.ui.AbstractElement
3393          * @param {Node} targetNode
3394          * Specifies the DOM node on which the event listeners should be registered.
3395          *
3396          * @param {...string} events
3397          * The DOM events for which event handlers should be registered.
3398          */
3399         setUpdateEvents: function(targetNode /*, ... */) {
3400                 var datatype = this.options.datatype,
3401                     optional = this.options.hasOwnProperty('optional') ? this.options.optional : true,
3402                     validate = this.options.validate,
3403                     events = this.varargs(arguments, 1);
3404
3405                 this.registerEvents(targetNode, 'widget-update', events);
3406
3407                 if (!datatype &amp;&amp; !validate)
3408                         return;
3409
3410                 this.vfunc = UI.prototype.addValidator.apply(UI.prototype, [
3411                         targetNode, datatype || 'string',
3412                         optional, validate
3413                 ].concat(events));
3414
3415                 this.node.addEventListener('validation-success', L.bind(function(ev) {
3416                         this.validState = true;
3417                 }, this));
3418
3419                 this.node.addEventListener('validation-failure', L.bind(function(ev) {
3420                         this.validState = false;
3421                 }, this));
3422         },
3423
3424         /**
3425          * Setup listeners for native DOM events that may change the widget value.
3426          *
3427          * Sets up event handlers on the given target DOM node for the given event
3428          * names which may cause the input value to change completely, such as
3429          * `change` events in a select menu. In contrast to update events, such
3430          * change events will not trigger input value validation but they may cause
3431          * field dependencies to get re-evaluated and will mark the input widget
3432          * as dirty.
3433          *
3434          * @instance
3435          * @memberof LuCI.ui.AbstractElement
3436          * @param {Node} targetNode
3437          * Specifies the DOM node on which the event listeners should be registered.
3438          *
3439          * @param {...string} events
3440          * The DOM events for which event handlers should be registered.
3441          */
3442         setChangeEvents: function(targetNode /*, ... */) {
3443                 var tag_changed = L.bind(function(ev) { this.setAttribute('data-changed', true) }, this.node);
3444
3445                 for (var i = 1; i &lt; arguments.length; i++)
3446                         targetNode.addEventListener(arguments[i], tag_changed);
3447
3448                 this.registerEvents(targetNode, 'widget-change', this.varargs(arguments, 1));
3449         },
3450
3451         /**
3452          * Render the widget, setup event listeners and return resulting markup.
3453          *
3454          * @instance
3455          * @memberof LuCI.ui.AbstractElement
3456          *
3457          * @returns {Node}
3458          * Returns a DOM Node or DocumentFragment containing the rendered
3459          * widget markup.
3460          */
3461         render: function() {}
3462 });
3463
3464 /**
3465  * Instantiate a text input widget.
3466  *
3467  * @constructor Textfield
3468  * @memberof LuCI.ui
3469  * @augments LuCI.ui.AbstractElement
3470  *
3471  * @classdesc
3472  *
3473  * The `Textfield` class implements a standard single line text input field.
3474  *
3475  * UI widget instances are usually not supposed to be created by view code
3476  * directly, instead they're implicitely created by `LuCI.form` when
3477  * instantiating CBI forms.
3478  *
3479  * This class is automatically instantiated as part of `LuCI.ui`. To use it
3480  * in views, use `'require ui'` and refer to `ui.Textfield`. To import it in
3481  * external JavaScript, use `L.require("ui").then(...)` and access the
3482  * `Textfield` property of the class instance value.
3483  *
3484  * @param {string} [value=null]
3485  * The initial input value.
3486  *
3487  * @param {LuCI.ui.Textfield.InitOptions} [options]
3488  * Object describing the widget specific options to initialize the input.
3489  */
3490 var UITextfield = UIElement.extend(/** @lends LuCI.ui.Textfield.prototype */ {
3491         /**
3492          * In addition to the [AbstractElement.InitOptions]{@link LuCI.ui.AbstractElement.InitOptions}
3493          * the following properties are recognized:
3494          *
3495          * @typedef {LuCI.ui.AbstractElement.InitOptions} InitOptions
3496          * @memberof LuCI.ui.Textfield
3497          *
3498          * @property {boolean} [password=false]
3499          * Specifies whether the input should be rendered as concealed password field.
3500          *
3501          * @property {boolean} [readonly=false]
3502          * Specifies whether the input widget should be rendered readonly.
3503          *
3504          * @property {number} [maxlength]
3505          * Specifies the HTML `maxlength` attribute to set on the corresponding
3506          * `&lt;input>` element. Note that this a legacy property that exists for
3507          * compatibility reasons. It is usually better to `maxlength(N)` validation
3508          * expression.
3509          *
3510          * @property {string} [placeholder]
3511          * Specifies the HTML `placeholder` attribute which is displayed when the
3512          * corresponding `&lt;input>` element is empty.
3513          */
3514         __init__: function(value, options) {
3515                 this.value = value;
3516                 this.options = Object.assign({
3517                         optional: true,
3518                         password: false
3519                 }, options);
3520         },
3521
3522         /** @override */
3523         render: function() {
3524                 var frameEl = E('div', { 'id': this.options.id });
3525
3526                 if (this.options.password) {
3527                         frameEl.classList.add('nowrap');
3528                         frameEl.appendChild(E('input', {
3529                                 'type': 'password',
3530                                 'style': 'position:absolute; left:-100000px',
3531                                 'aria-hidden': true,
3532                                 'tabindex': -1,
3533                                 'name': this.options.name ? 'password.%s'.format(this.options.name) : null
3534                         }));
3535                 }
3536
3537                 frameEl.appendChild(E('input', {
3538                         'id': this.options.id ? 'widget.' + this.options.id : null,
3539                         'name': this.options.name,
3540                         'type': this.options.password ? 'password' : 'text',
3541                         'class': this.options.password ? 'cbi-input-password' : 'cbi-input-text',
3542                         'readonly': this.options.readonly ? '' : null,
3543                         'maxlength': this.options.maxlength,
3544                         'placeholder': this.options.placeholder,
3545                         'value': this.value,
3546                 }));
3547
3548                 if (this.options.password)
3549                         frameEl.appendChild(E('button', {
3550                                 'class': 'cbi-button cbi-button-neutral',
3551                                 'title': _('Reveal/hide password'),
3552                                 'aria-label': _('Reveal/hide password'),
3553                                 'click': function(ev) {
3554                                         var e = this.previousElementSibling;
3555                                         e.type = (e.type === 'password') ? 'text' : 'password';
3556                                         ev.preventDefault();
3557                                 }
3558                         }, '∗'));
3559
3560                 return this.bind(frameEl);
3561         },
3562
3563         /** @private */
3564         bind: function(frameEl) {
3565                 var inputEl = frameEl.childNodes[+!!this.options.password];
3566
3567                 this.node = frameEl;
3568
3569                 this.setUpdateEvents(inputEl, 'keyup', 'blur');
3570                 this.setChangeEvents(inputEl, 'change');
3571
3572                 dom.bindClassInstance(frameEl, this);
3573
3574                 return frameEl;
3575         },
3576
3577         /** @override */
3578         getValue: function() {
3579                 var inputEl = this.node.childNodes[+!!this.options.password];
3580                 return inputEl.value;
3581         },
3582
3583         /** @override */
3584         setValue: function(value) {
3585                 var inputEl = this.node.childNodes[+!!this.options.password];
3586                 inputEl.value = value;
3587         }
3588 });
3589
3590 /**
3591  * Instantiate a textarea widget.
3592  *
3593  * @constructor Textarea
3594  * @memberof LuCI.ui
3595  * @augments LuCI.ui.AbstractElement
3596  *
3597  * @classdesc
3598  *
3599  * The `Textarea` class implements a multiline text area input field.
3600  *
3601  * UI widget instances are usually not supposed to be created by view code
3602  * directly, instead they're implicitely created by `LuCI.form` when
3603  * instantiating CBI forms.
3604  *
3605  * This class is automatically instantiated as part of `LuCI.ui`. To use it
3606  * in views, use `'require ui'` and refer to `ui.Textarea`. To import it in
3607  * external JavaScript, use `L.require("ui").then(...)` and access the
3608  * `Textarea` property of the class instance value.
3609  *
3610  * @param {string} [value=null]
3611  * The initial input value.
3612  *
3613  * @param {LuCI.ui.Textarea.InitOptions} [options]
3614  * Object describing the widget specific options to initialize the input.
3615  */
3616 var UITextarea = UIElement.extend(/** @lends LuCI.ui.Textarea.prototype */ {
3617         /**
3618          * In addition to the [AbstractElement.InitOptions]{@link LuCI.ui.AbstractElement.InitOptions}
3619          * the following properties are recognized:
3620          *
3621          * @typedef {LuCI.ui.AbstractElement.InitOptions} InitOptions
3622          * @memberof LuCI.ui.Textarea
3623          *
3624          * @property {boolean} [readonly=false]
3625          * Specifies whether the input widget should be rendered readonly.
3626          *
3627          * @property {string} [placeholder]
3628          * Specifies the HTML `placeholder` attribute which is displayed when the
3629          * corresponding `&lt;textarea>` element is empty.
3630          *
3631          * @property {boolean} [monospace=false]
3632          * Specifies whether a monospace font should be forced for the textarea
3633          * contents.
3634          *
3635          * @property {number} [cols]
3636          * Specifies the HTML `cols` attribute to set on the corresponding
3637          * `&lt;textarea>` element.
3638          *
3639          * @property {number} [rows]
3640          * Specifies the HTML `rows` attribute to set on the corresponding
3641          * `&lt;textarea>` element.
3642          *
3643          * @property {boolean} [wrap=false]
3644          * Specifies whether the HTML `wrap` attribute should be set.
3645          */
3646         __init__: function(value, options) {
3647                 this.value = value;
3648                 this.options = Object.assign({
3649                         optional: true,
3650                         wrap: false,
3651                         cols: null,
3652                         rows: null
3653                 }, options);
3654         },
3655
3656         /** @override */
3657         render: function() {
3658                 var frameEl = E('div', { 'id': this.options.id }),
3659                     value = (this.value != null) ? String(this.value) : '';
3660
3661                 frameEl.appendChild(E('textarea', {
3662                         'id': this.options.id ? 'widget.' + this.options.id : null,
3663                         'name': this.options.name,
3664                         'class': 'cbi-input-textarea',
3665                         'readonly': this.options.readonly ? '' : null,
3666                         'placeholder': this.options.placeholder,
3667                         'style': !this.options.cols ? 'width:100%' : null,
3668                         'cols': this.options.cols,
3669                         'rows': this.options.rows,
3670                         'wrap': this.options.wrap ? '' : null
3671                 }, [ value ]));
3672
3673                 if (this.options.monospace)
3674                         frameEl.firstElementChild.style.fontFamily = 'monospace';
3675
3676                 return this.bind(frameEl);
3677         },
3678
3679         /** @private */
3680         bind: function(frameEl) {
3681                 var inputEl = frameEl.firstElementChild;
3682
3683                 this.node = frameEl;
3684
3685                 this.setUpdateEvents(inputEl, 'keyup', 'blur');
3686                 this.setChangeEvents(inputEl, 'change');
3687
3688                 dom.bindClassInstance(frameEl, this);
3689
3690                 return frameEl;
3691         },
3692
3693         /** @override */
3694         getValue: function() {
3695                 return this.node.firstElementChild.value;
3696         },
3697
3698         /** @override */
3699         setValue: function(value) {
3700                 this.node.firstElementChild.value = value;
3701         }
3702 });
3703
3704 /**
3705  * Instantiate a checkbox widget.
3706  *
3707  * @constructor Checkbox
3708  * @memberof LuCI.ui
3709  * @augments LuCI.ui.AbstractElement
3710  *
3711  * @classdesc
3712  *
3713  * The `Checkbox` class implements a simple checkbox input field.
3714  *
3715  * UI widget instances are usually not supposed to be created by view code
3716  * directly, instead they're implicitely created by `LuCI.form` when
3717  * instantiating CBI forms.
3718  *
3719  * This class is automatically instantiated as part of `LuCI.ui`. To use it
3720  * in views, use `'require ui'` and refer to `ui.Checkbox`. To import it in
3721  * external JavaScript, use `L.require("ui").then(...)` and access the
3722  * `Checkbox` property of the class instance value.
3723  *
3724  * @param {string} [value=null]
3725  * The initial input value.
3726  *
3727  * @param {LuCI.ui.Checkbox.InitOptions} [options]
3728  * Object describing the widget specific options to initialize the input.
3729  */
3730 var UICheckbox = UIElement.extend(/** @lends LuCI.ui.Checkbox.prototype */ {
3731         /**
3732          * In addition to the [AbstractElement.InitOptions]{@link LuCI.ui.AbstractElement.InitOptions}
3733          * the following properties are recognized:
3734          *
3735          * @typedef {LuCI.ui.AbstractElement.InitOptions} InitOptions
3736          * @memberof LuCI.ui.Checkbox
3737          *
3738          * @property {string} [value_enabled=1]
3739          * Specifies the value corresponding to a checked checkbox.
3740          *
3741          * @property {string} [value_disabled=0]
3742          * Specifies the value corresponding to an unchecked checkbox.
3743          *
3744          * @property {string} [hiddenname]
3745          * Specifies the HTML `name` attribute of the hidden input backing the
3746          * checkbox. This is a legacy property existing for compatibility reasons,
3747          * it is required for HTML based form submissions.
3748          */
3749         __init__: function(value, options) {
3750                 this.value = value;
3751                 this.options = Object.assign({
3752                         value_enabled: '1',
3753                         value_disabled: '0'
3754                 }, options);
3755         },
3756
3757         /** @override */
3758         render: function() {
3759                 var id = 'cb%08x'.format(Math.random() * 0xffffffff);
3760                 var frameEl = E('div', {
3761                         'id': this.options.id,
3762                         'class': 'cbi-checkbox'
3763                 });
3764
3765                 if (this.options.hiddenname)
3766                         frameEl.appendChild(E('input', {
3767                                 'type': 'hidden',
3768                                 'name': this.options.hiddenname,
3769                                 'value': 1
3770                         }));
3771
3772                 frameEl.appendChild(E('input', {
3773                         'id': id,
3774                         'name': this.options.name,
3775                         'type': 'checkbox',
3776                         'value': this.options.value_enabled,
3777                         'checked': (this.value == this.options.value_enabled) ? '' : null,
3778                         'data-widget-id': this.options.id ? 'widget.' + this.options.id : null
3779                 }));
3780
3781                 frameEl.appendChild(E('label', { 'for': id }));
3782
3783                 return this.bind(frameEl);
3784         },
3785
3786         /** @private */
3787         bind: function(frameEl) {
3788                 this.node = frameEl;
3789
3790                 this.setUpdateEvents(frameEl.lastElementChild.previousElementSibling, 'click', 'blur');
3791                 this.setChangeEvents(frameEl.lastElementChild.previousElementSibling, 'change');
3792
3793                 dom.bindClassInstance(frameEl, this);
3794
3795                 return frameEl;
3796         },
3797
3798         /**
3799          * Test whether the checkbox is currently checked.
3800          *
3801          * @instance
3802          * @memberof LuCI.ui.Checkbox
3803          * @returns {boolean}
3804          * Returns `true` when the checkbox is currently checked, otherwise `false`.
3805          */
3806         isChecked: function() {
3807                 return this.node.lastElementChild.previousElementSibling.checked;
3808         },
3809
3810         /** @override */
3811         getValue: function() {
3812                 return this.isChecked()
3813                         ? this.options.value_enabled
3814                         : this.options.value_disabled;
3815         },
3816
3817         /** @override */
3818         setValue: function(value) {
3819                 this.node.lastElementChild.previousElementSibling.checked = (value == this.options.value_enabled);
3820         }
3821 });
3822
3823 /**
3824  * Instantiate a select dropdown or checkbox/radiobutton group.
3825  *
3826  * @constructor Select
3827  * @memberof LuCI.ui
3828  * @augments LuCI.ui.AbstractElement
3829  *
3830  * @classdesc
3831  *
3832  * The `Select` class implements either a traditional HTML `&lt;select>` element
3833  * or a group of checkboxes or radio buttons, depending on whether multiple
3834  * values are enabled or not.
3835  *
3836  * UI widget instances are usually not supposed to be created by view code
3837  * directly, instead they're implicitely created by `LuCI.form` when
3838  * instantiating CBI forms.
3839  *
3840  * This class is automatically instantiated as part of `LuCI.ui`. To use it
3841  * in views, use `'require ui'` and refer to `ui.Select`. To import it in
3842  * external JavaScript, use `L.require("ui").then(...)` and access the
3843  * `Select` property of the class instance value.
3844  *
3845  * @param {string|string[]} [value=null]
3846  * The initial input value(s).
3847  *
3848  * @param {Object&lt;string, string>} choices
3849  * Object containing the selectable choices of the widget. The object keys
3850  * serve as values for the different choices while the values are used as
3851  * choice labels.
3852  *
3853  * @param {LuCI.ui.Select.InitOptions} [options]
3854  * Object describing the widget specific options to initialize the inputs.
3855  */
3856 var UISelect = UIElement.extend(/** @lends LuCI.ui.Select.prototype */ {
3857         /**
3858          * In addition to the [AbstractElement.InitOptions]{@link LuCI.ui.AbstractElement.InitOptions}
3859          * the following properties are recognized:
3860          *
3861          * @typedef {LuCI.ui.AbstractElement.InitOptions} InitOptions
3862          * @memberof LuCI.ui.Select
3863          *
3864          * @property {boolean} [multiple=false]
3865          * Specifies whether multiple choice values may be selected.
3866          *
3867          * @property {string} [widget=select]
3868          * Specifies the kind of widget to render. May be either `select` or
3869          * `individual`. When set to `select` an HTML `&lt;select>` element will be
3870          * used, otherwise a group of checkbox or radio button elements is created,
3871          * depending on the value of the `multiple` option.
3872          *
3873          * @property {string} [orientation=horizontal]
3874          * Specifies whether checkbox / radio button groups should be rendered
3875          * in a `horizontal` or `vertical` manner. Does not apply to the `select`
3876          * widget type.
3877          *
3878          * @property {boolean|string[]} [sort=false]
3879          * Specifies if and how to sort choice values. If set to `true`, the choice
3880          * values will be sorted alphabetically. If set to an array of strings, the
3881          * choice sort order is derived from the array.
3882          *
3883          * @property {number} [size]
3884          * Specifies the HTML `size` attribute to set on the `&lt;select>` element.
3885          * Only applicable to the `select` widget type.
3886          *
3887          * @property {string} [placeholder=-- Please choose --]
3888          * Specifies a placeholder text which is displayed when no choice is
3889          * selected yet. Only applicable to the `select` widget type.
3890          */
3891         __init__: function(value, choices, options) {
3892                 if (!L.isObject(choices))
3893                         choices = {};
3894
3895                 if (!Array.isArray(value))
3896                         value = (value != null &amp;&amp; value != '') ? [ value ] : [];
3897
3898                 if (!options.multiple &amp;&amp; value.length > 1)
3899                         value.length = 1;
3900
3901                 this.values = value;
3902                 this.choices = choices;
3903                 this.options = Object.assign({
3904                         multiple: false,
3905                         widget: 'select',
3906                         orientation: 'horizontal'
3907                 }, options);
3908
3909                 if (this.choices.hasOwnProperty(''))
3910                         this.options.optional = true;
3911         },
3912
3913         /** @override */
3914         render: function() {
3915                 var frameEl = E('div', { 'id': this.options.id }),
3916                     keys = Object.keys(this.choices);
3917
3918                 if (this.options.sort === true)
3919                         keys.sort();
3920                 else if (Array.isArray(this.options.sort))
3921                         keys = this.options.sort;
3922
3923                 if (this.options.widget == 'select') {
3924                         frameEl.appendChild(E('select', {
3925                                 'id': this.options.id ? 'widget.' + this.options.id : null,
3926                                 'name': this.options.name,
3927                                 'size': this.options.size,
3928                                 'class': 'cbi-input-select',
3929                                 'multiple': this.options.multiple ? '' : null
3930                         }));
3931
3932                         if (this.options.optional)
3933                                 frameEl.lastChild.appendChild(E('option', {
3934                                         'value': '',
3935                                         'selected': (this.values.length == 0 || this.values[0] == '') ? '' : null
3936                                 }, [ this.choices[''] || this.options.placeholder || _('-- Please choose --') ]));
3937
3938                         for (var i = 0; i &lt; keys.length; i++) {
3939                                 if (keys[i] == null || keys[i] == '')
3940                                         continue;
3941
3942                                 frameEl.lastChild.appendChild(E('option', {
3943                                         'value': keys[i],
3944                                         'selected': (this.values.indexOf(keys[i]) > -1) ? '' : null
3945                                 }, [ this.choices[keys[i]] || keys[i] ]));
3946                         }
3947                 }
3948                 else {
3949                         var brEl = (this.options.orientation === 'horizontal') ? document.createTextNode(' ') : E('br');
3950
3951                         for (var i = 0; i &lt; keys.length; i++) {
3952                                 frameEl.appendChild(E('label', {}, [
3953                                         E('input', {
3954                                                 'id': this.options.id ? 'widget.' + this.options.id : null,
3955                                                 'name': this.options.id || this.options.name,
3956                                                 'type': this.options.multiple ? 'checkbox' : 'radio',
3957                                                 'class': this.options.multiple ? 'cbi-input-checkbox' : 'cbi-input-radio',
3958                                                 'value': keys[i],
3959                                                 'checked': (this.values.indexOf(keys[i]) > -1) ? '' : null
3960                                         }),
3961                                         this.choices[keys[i]] || keys[i]
3962                                 ]));
3963
3964                                 if (i + 1 == this.options.size)
3965                                         frameEl.appendChild(brEl);
3966                         }
3967                 }
3968
3969                 return this.bind(frameEl);
3970         },
3971
3972         /** @private */
3973         bind: function(frameEl) {
3974                 this.node = frameEl;
3975
3976                 if (this.options.widget == 'select') {
3977                         this.setUpdateEvents(frameEl.firstChild, 'change', 'click', 'blur');
3978                         this.setChangeEvents(frameEl.firstChild, 'change');
3979                 }
3980                 else {
3981                         var radioEls = frameEl.querySelectorAll('input[type="radio"]');
3982                         for (var i = 0; i &lt; radioEls.length; i++) {
3983                                 this.setUpdateEvents(radioEls[i], 'change', 'click', 'blur');
3984                                 this.setChangeEvents(radioEls[i], 'change', 'click', 'blur');
3985                         }
3986                 }
3987
3988                 dom.bindClassInstance(frameEl, this);
3989
3990                 return frameEl;
3991         },
3992
3993         /** @override */
3994         getValue: function() {
3995                 if (this.options.widget == 'select')
3996                         return this.node.firstChild.value;
3997
3998                 var radioEls = frameEl.querySelectorAll('input[type="radio"]');
3999                 for (var i = 0; i &lt; radioEls.length; i++)
4000                         if (radioEls[i].checked)
4001                                 return radioEls[i].value;
4002
4003                 return null;
4004         },
4005
4006         /** @override */
4007         setValue: function(value) {
4008                 if (this.options.widget == 'select') {
4009                         if (value == null)
4010                                 value = '';
4011
4012                         for (var i = 0; i &lt; this.node.firstChild.options.length; i++)
4013                                 this.node.firstChild.options[i].selected = (this.node.firstChild.options[i].value == value);
4014
4015                         return;
4016                 }
4017
4018                 var radioEls = frameEl.querySelectorAll('input[type="radio"]');
4019                 for (var i = 0; i &lt; radioEls.length; i++)
4020                         radioEls[i].checked = (radioEls[i].value == value);
4021         }
4022 });
4023
4024 /**
4025  * Instantiate a rich dropdown choice widget.
4026  *
4027  * @constructor Dropdown
4028  * @memberof LuCI.ui
4029  * @augments LuCI.ui.AbstractElement
4030  *
4031  * @classdesc
4032  *
4033  * The `Dropdown` class implements a rich, stylable dropdown menu which
4034  * supports non-text choice labels.
4035  *
4036  * UI widget instances are usually not supposed to be created by view code
4037  * directly, instead they're implicitely created by `LuCI.form` when
4038  * instantiating CBI forms.
4039  *
4040  * This class is automatically instantiated as part of `LuCI.ui`. To use it
4041  * in views, use `'require ui'` and refer to `ui.Dropdown`. To import it in
4042  * external JavaScript, use `L.require("ui").then(...)` and access the
4043  * `Dropdown` property of the class instance value.
4044  *
4045  * @param {string|string[]} [value=null]
4046  * The initial input value(s).
4047  *
4048  * @param {Object&lt;string, *>} choices
4049  * Object containing the selectable choices of the widget. The object keys
4050  * serve as values for the different choices while the values are used as
4051  * choice labels.
4052  *
4053  * @param {LuCI.ui.Dropdown.InitOptions} [options]
4054  * Object describing the widget specific options to initialize the dropdown.
4055  */
4056 var UIDropdown = UIElement.extend(/** @lends LuCI.ui.Dropdown.prototype */ {
4057         /**
4058          * In addition to the [AbstractElement.InitOptions]{@link LuCI.ui.AbstractElement.InitOptions}
4059          * the following properties are recognized:
4060          *
4061          * @typedef {LuCI.ui.AbstractElement.InitOptions} InitOptions
4062          * @memberof LuCI.ui.Dropdown
4063          *
4064          * @property {boolean} [optional=true]
4065          * Specifies whether the dropdown selection is optional. In contrast to
4066          * other widgets, the `optional` constraint of dropdowns works differently;
4067          * instead of marking the widget invalid on empty values when set to `false`,
4068          * the user is not allowed to deselect all choices.
4069          *
4070          * For single value dropdowns that means that no empty "please select"
4071          * choice is offered and for multi value dropdowns, the last selected choice
4072          * may not be deselected without selecting another choice first.
4073          *
4074          * @property {boolean} [multiple]
4075          * Specifies whether multiple choice values may be selected. It defaults
4076          * to `true` when an array is passed as input value to the constructor.
4077          *
4078          * @property {boolean|string[]} [sort=false]
4079          * Specifies if and how to sort choice values. If set to `true`, the choice
4080          * values will be sorted alphabetically. If set to an array of strings, the
4081          * choice sort order is derived from the array.
4082          *
4083          * @property {string} [select_placeholder=-- Please choose --]
4084          * Specifies a placeholder text which is displayed when no choice is
4085          * selected yet.
4086          *
4087          * @property {string} [custom_placeholder=-- custom --]
4088          * Specifies a placeholder text which is displayed in the text input
4089          * field allowing to enter custom choice values. Only applicable if the
4090          * `create` option is set to `true`.
4091          *
4092          * @property {boolean} [create=false]
4093          * Specifies whether custom choices may be entered into the dropdown
4094          * widget.
4095          *
4096          * @property {string} [create_query=.create-item-input]
4097          * Specifies a CSS selector expression used to find the input element
4098          * which is used to enter custom choice values. This should not normally
4099          * be used except by widgets derived from the Dropdown class.
4100          *
4101          * @property {string} [create_template=script[type="item-template"]]
4102          * Specifies a CSS selector expression used to find an HTML element
4103          * serving as template for newly added custom choice values.
4104          *
4105          * Any `{{value}}` placeholder string within the template elements text
4106          * content will be replaced by the user supplied choice value, the
4107          * resulting string is parsed as HTML and appended to the end of the
4108          * choice list. The template markup may specify one HTML element with a
4109          * `data-label-placeholder` attribute which is replaced by a matching
4110          * label value from the `choices` object or with the user supplied value
4111          * itself in case `choices` contains no matching choice label.
4112          *
4113          * If the template element is not found or if no `create_template` selector
4114          * expression is specified, the default markup for newly created elements is
4115          * `&lt;li data-value="{{value}}">&lt;span data-label-placeholder="true" />&lt;/li>`.
4116          *
4117          * @property {string} [create_markup]
4118          * This property allows specifying the markup for custom choices directly
4119          * instead of referring to a template element through CSS selectors.
4120          *
4121          * Apart from that it works exactly like `create_template`.
4122          *
4123          * @property {number} [display_items=3]
4124          * Specifies the maximum amount of choice labels that should be shown in
4125          * collapsed dropdown state before further selected choices are cut off.
4126          *
4127          * Only applicable when `multiple` is `true`.
4128          *
4129          * @property {number} [dropdown_items=-1]
4130          * Specifies the maximum amount of choices that should be shown when the
4131          * dropdown is open. If the amount of available choices exceeds this number,
4132          * the dropdown area must be scrolled to reach further items.
4133          *
4134          * If set to `-1`, the dropdown menu will attempt to show all choice values
4135          * and only resort to scrolling if the amount of choices exceeds the available
4136          * screen space above and below the dropdown widget.
4137          *
4138          * @property {string} [placeholder]
4139          * This property serves as a shortcut to set both `select_placeholder` and
4140          * `custom_placeholder`. Either of these properties will fallback to
4141          * `placeholder` if not specified.
4142          *
4143          * @property {boolean} [readonly=false]
4144          * Specifies whether the custom choice input field should be rendered
4145          * readonly. Only applicable when `create` is `true`.
4146          *
4147          * @property {number} [maxlength]
4148          * Specifies the HTML `maxlength` attribute to set on the custom choice
4149          * `&lt;input>` element. Note that this a legacy property that exists for
4150          * compatibility reasons. It is usually better to `maxlength(N)` validation
4151          * expression. Only applicable when `create` is `true`.
4152          */
4153         __init__: function(value, choices, options) {
4154                 if (typeof(choices) != 'object')
4155                         choices = {};
4156
4157                 if (!Array.isArray(value))
4158                         this.values = (value != null &amp;&amp; value != '') ? [ value ] : [];
4159                 else
4160                         this.values = value;
4161
4162                 this.choices = choices;
4163                 this.options = Object.assign({
4164                         sort:               true,
4165                         multiple:           Array.isArray(value),
4166                         optional:           true,
4167                         select_placeholder: _('-- Please choose --'),
4168                         custom_placeholder: _('-- custom --'),
4169                         display_items:      3,
4170                         dropdown_items:     -1,
4171                         create:             false,
4172                         create_query:       '.create-item-input',
4173                         create_template:    'script[type="item-template"]'
4174                 }, options);
4175         },
4176
4177         /** @override */
4178         render: function() {
4179                 var sb = E('div', {
4180                         'id': this.options.id,
4181                         'class': 'cbi-dropdown',
4182                         'multiple': this.options.multiple ? '' : null,
4183                         'optional': this.options.optional ? '' : null,
4184                 }, E('ul'));
4185
4186                 var keys = Object.keys(this.choices);
4187
4188                 if (this.options.sort === true)
4189                         keys.sort();
4190                 else if (Array.isArray(this.options.sort))
4191                         keys = this.options.sort;
4192
4193                 if (this.options.create)
4194                         for (var i = 0; i &lt; this.values.length; i++)
4195                                 if (!this.choices.hasOwnProperty(this.values[i]))
4196                                         keys.push(this.values[i]);
4197
4198                 for (var i = 0; i &lt; keys.length; i++) {
4199                         var label = this.choices[keys[i]];
4200
4201                         if (dom.elem(label))
4202                                 label = label.cloneNode(true);
4203
4204                         sb.lastElementChild.appendChild(E('li', {
4205                                 'data-value': keys[i],
4206                                 'selected': (this.values.indexOf(keys[i]) > -1) ? '' : null
4207                         }, [ label || keys[i] ]));
4208                 }
4209
4210                 if (this.options.create) {
4211                         var createEl = E('input', {
4212                                 'type': 'text',
4213                                 'class': 'create-item-input',
4214                                 'readonly': this.options.readonly ? '' : null,
4215                                 'maxlength': this.options.maxlength,
4216                                 'placeholder': this.options.custom_placeholder || this.options.placeholder
4217                         });
4218
4219                         if (this.options.datatype || this.options.validate)
4220                                 UI.prototype.addValidator(createEl, this.options.datatype || 'string',
4221                                                           true, this.options.validate, 'blur', 'keyup');
4222
4223                         sb.lastElementChild.appendChild(E('li', { 'data-value': '-' }, createEl));
4224                 }
4225
4226                 if (this.options.create_markup)
4227                         sb.appendChild(E('script', { type: 'item-template' },
4228                                 this.options.create_markup));
4229
4230                 return this.bind(sb);
4231         },
4232
4233         /** @private */
4234         bind: function(sb) {
4235                 var o = this.options;
4236
4237                 o.multiple = sb.hasAttribute('multiple');
4238                 o.optional = sb.hasAttribute('optional');
4239                 o.placeholder = sb.getAttribute('placeholder') || o.placeholder;
4240                 o.display_items = parseInt(sb.getAttribute('display-items') || o.display_items);
4241                 o.dropdown_items = parseInt(sb.getAttribute('dropdown-items') || o.dropdown_items);
4242                 o.create_query = sb.getAttribute('item-create') || o.create_query;
4243                 o.create_template = sb.getAttribute('item-template') || o.create_template;
4244
4245                 var ul = sb.querySelector('ul'),
4246                     more = sb.appendChild(E('span', { class: 'more', tabindex: -1 }, '···')),
4247                     open = sb.appendChild(E('span', { class: 'open', tabindex: -1 }, 'â–¾')),
4248                     canary = sb.appendChild(E('div')),
4249                     create = sb.querySelector(this.options.create_query),
4250                     ndisplay = this.options.display_items,
4251                     n = 0;
4252
4253                 if (this.options.multiple) {
4254                         var items = ul.querySelectorAll('li');
4255
4256                         for (var i = 0; i &lt; items.length; i++) {
4257                                 this.transformItem(sb, items[i]);
4258
4259                                 if (items[i].hasAttribute('selected') &amp;&amp; ndisplay-- > 0)
4260                                         items[i].setAttribute('display', n++);
4261                         }
4262                 }
4263                 else {
4264                         if (this.options.optional &amp;&amp; !ul.querySelector('li[data-value=""]')) {
4265                                 var placeholder = E('li', { placeholder: '' },
4266                                         this.options.select_placeholder || this.options.placeholder);
4267
4268                                 ul.firstChild
4269                                         ? ul.insertBefore(placeholder, ul.firstChild)
4270                                         : ul.appendChild(placeholder);
4271                         }
4272
4273                         var items = ul.querySelectorAll('li'),
4274                             sel = sb.querySelectorAll('[selected]');
4275
4276                         sel.forEach(function(s) {
4277                                 s.removeAttribute('selected');
4278                         });
4279
4280                         var s = sel[0] || items[0];
4281                         if (s) {
4282                                 s.setAttribute('selected', '');
4283                                 s.setAttribute('display', n++);
4284                         }
4285
4286                         ndisplay--;
4287                 }
4288
4289                 this.saveValues(sb, ul);
4290
4291                 ul.setAttribute('tabindex', -1);
4292                 sb.setAttribute('tabindex', 0);
4293
4294                 if (ndisplay &lt; 0)
4295                         sb.setAttribute('more', '')
4296                 else
4297                         sb.removeAttribute('more');
4298
4299                 if (ndisplay == this.options.display_items)
4300                         sb.setAttribute('empty', '')
4301                 else
4302                         sb.removeAttribute('empty');
4303
4304                 dom.content(more, (ndisplay == this.options.display_items)
4305                         ? (this.options.select_placeholder || this.options.placeholder) : '···');
4306
4307
4308                 sb.addEventListener('click', this.handleClick.bind(this));
4309                 sb.addEventListener('keydown', this.handleKeydown.bind(this));
4310                 sb.addEventListener('cbi-dropdown-close', this.handleDropdownClose.bind(this));
4311                 sb.addEventListener('cbi-dropdown-select', this.handleDropdownSelect.bind(this));
4312
4313                 if ('ontouchstart' in window) {
4314                         sb.addEventListener('touchstart', function(ev) { ev.stopPropagation(); });
4315                         window.addEventListener('touchstart', this.closeAllDropdowns);
4316                 }
4317                 else {
4318                         sb.addEventListener('mouseover', this.handleMouseover.bind(this));
4319                         sb.addEventListener('focus', this.handleFocus.bind(this));
4320
4321                         canary.addEventListener('focus', this.handleCanaryFocus.bind(this));
4322
4323                         window.addEventListener('mouseover', this.setFocus);
4324                         window.addEventListener('click', this.closeAllDropdowns);
4325                 }
4326
4327                 if (create) {
4328                         create.addEventListener('keydown', this.handleCreateKeydown.bind(this));
4329                         create.addEventListener('focus', this.handleCreateFocus.bind(this));
4330                         create.addEventListener('blur', this.handleCreateBlur.bind(this));
4331
4332                         var li = findParent(create, 'li');
4333
4334                         li.setAttribute('unselectable', '');
4335                         li.addEventListener('click', this.handleCreateClick.bind(this));
4336                 }
4337
4338                 this.node = sb;
4339
4340                 this.setUpdateEvents(sb, 'cbi-dropdown-open', 'cbi-dropdown-close');
4341                 this.setChangeEvents(sb, 'cbi-dropdown-change', 'cbi-dropdown-close');
4342
4343                 dom.bindClassInstance(sb, this);
4344
4345                 return sb;
4346         },
4347
4348         /** @private */
4349         openDropdown: function(sb) {
4350                 var st = window.getComputedStyle(sb, null),
4351                     ul = sb.querySelector('ul'),
4352                     li = ul.querySelectorAll('li'),
4353                     fl = findParent(sb, '.cbi-value-field'),
4354                     sel = ul.querySelector('[selected]'),
4355                     rect = sb.getBoundingClientRect(),
4356                     items = Math.min(this.options.dropdown_items, li.length);
4357
4358                 document.querySelectorAll('.cbi-dropdown[open]').forEach(function(s) {
4359                         s.dispatchEvent(new CustomEvent('cbi-dropdown-close', {}));
4360                 });
4361
4362                 sb.setAttribute('open', '');
4363
4364                 var pv = ul.cloneNode(true);
4365                     pv.classList.add('preview');
4366
4367                 if (fl)
4368                         fl.classList.add('cbi-dropdown-open');
4369
4370                 if ('ontouchstart' in window) {
4371                         var vpWidth = Math.max(document.documentElement.clientWidth, window.innerWidth || 0),
4372                             vpHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0),
4373                             start = null;
4374
4375                         ul.style.top = sb.offsetHeight + 'px';
4376                         ul.style.left = -rect.left + 'px';
4377                         ul.style.right = (rect.right - vpWidth) + 'px';
4378                         ul.style.maxHeight = (vpHeight * 0.5) + 'px';
4379                         ul.style.WebkitOverflowScrolling = 'touch';
4380
4381                         function getScrollParent(element) {
4382                                 var parent = element,
4383                                     style = getComputedStyle(element),
4384                                     excludeStaticParent = (style.position === 'absolute');
4385
4386                                 if (style.position === 'fixed')
4387                                         return document.body;
4388
4389                                 while ((parent = parent.parentElement) != null) {
4390                                         style = getComputedStyle(parent);
4391
4392                                         if (excludeStaticParent &amp;&amp; style.position === 'static')
4393                                                 continue;
4394
4395                                         if (/(auto|scroll)/.test(style.overflow + style.overflowY + style.overflowX))
4396                                                 return parent;
4397                                 }
4398
4399                                 return document.body;
4400                         }
4401
4402                         var scrollParent = getScrollParent(sb),
4403                             scrollFrom = scrollParent.scrollTop,
4404                             scrollTo = scrollFrom + rect.top - vpHeight * 0.5;
4405
4406                         var scrollStep = function(timestamp) {
4407                                 if (!start) {
4408                                         start = timestamp;
4409                                         ul.scrollTop = sel ? Math.max(sel.offsetTop - sel.offsetHeight, 0) : 0;
4410                                 }
4411
4412                                 var duration = Math.max(timestamp - start, 1);
4413                                 if (duration &lt; 100) {
4414                                         scrollParent.scrollTop = scrollFrom + (scrollTo - scrollFrom) * (duration / 100);
4415                                         window.requestAnimationFrame(scrollStep);
4416                                 }
4417                                 else {
4418                                         scrollParent.scrollTop = scrollTo;
4419                                 }
4420                         };
4421
4422                         window.requestAnimationFrame(scrollStep);
4423                 }
4424                 else {
4425                         ul.style.maxHeight = '1px';
4426                         ul.style.top = ul.style.bottom = '';
4427
4428                         window.requestAnimationFrame(function() {
4429                                 var itemHeight = li[Math.max(0, li.length - 2)].getBoundingClientRect().height,
4430                                     fullHeight = 0,
4431                                     spaceAbove = rect.top,
4432                                     spaceBelow = window.innerHeight - rect.height - rect.top;
4433
4434                                 for (var i = 0; i &lt; (items == -1 ? li.length : items); i++)
4435                                         fullHeight += li[i].getBoundingClientRect().height;
4436
4437                                 if (fullHeight &lt;= spaceBelow) {
4438                                         ul.style.top = rect.height + 'px';
4439                                         ul.style.maxHeight = spaceBelow + 'px';
4440                                 }
4441                                 else if (fullHeight &lt;= spaceAbove) {
4442                                         ul.style.bottom = rect.height + 'px';
4443                                         ul.style.maxHeight = spaceAbove + 'px';
4444                                 }
4445                                 else if (spaceBelow >= spaceAbove) {
4446                                         ul.style.top = rect.height + 'px';
4447                                         ul.style.maxHeight = (spaceBelow - (spaceBelow % itemHeight)) + 'px';
4448                                 }
4449                                 else {
4450                                         ul.style.bottom = rect.height + 'px';
4451                                         ul.style.maxHeight = (spaceAbove - (spaceAbove % itemHeight)) + 'px';
4452                                 }
4453
4454                                 ul.scrollTop = sel ? Math.max(sel.offsetTop - sel.offsetHeight, 0) : 0;
4455                         });
4456                 }
4457
4458                 var cboxes = ul.querySelectorAll('[selected] input[type="checkbox"]');
4459                 for (var i = 0; i &lt; cboxes.length; i++) {
4460                         cboxes[i].checked = true;
4461                         cboxes[i].disabled = (cboxes.length == 1 &amp;&amp; !this.options.optional);
4462                 };
4463
4464                 ul.classList.add('dropdown');
4465
4466                 sb.insertBefore(pv, ul.nextElementSibling);
4467
4468                 li.forEach(function(l) {
4469                         l.setAttribute('tabindex', 0);
4470                 });
4471
4472                 sb.lastElementChild.setAttribute('tabindex', 0);
4473
4474                 this.setFocus(sb, sel || li[0], true);
4475         },
4476
4477         /** @private */
4478         closeDropdown: function(sb, no_focus) {
4479                 if (!sb.hasAttribute('open'))
4480                         return;
4481
4482                 var pv = sb.querySelector('ul.preview'),
4483                     ul = sb.querySelector('ul.dropdown'),
4484                     li = ul.querySelectorAll('li'),
4485                     fl = findParent(sb, '.cbi-value-field');
4486
4487                 li.forEach(function(l) { l.removeAttribute('tabindex'); });
4488                 sb.lastElementChild.removeAttribute('tabindex');
4489
4490                 sb.removeChild(pv);
4491                 sb.removeAttribute('open');
4492                 sb.style.width = sb.style.height = '';
4493
4494                 ul.classList.remove('dropdown');
4495                 ul.style.top = ul.style.bottom = ul.style.maxHeight = '';
4496
4497                 if (fl)
4498                         fl.classList.remove('cbi-dropdown-open');
4499
4500                 if (!no_focus)
4501                         this.setFocus(sb, sb);
4502
4503                 this.saveValues(sb, ul);
4504         },
4505
4506         /** @private */
4507         toggleItem: function(sb, li, force_state) {
4508                 if (li.hasAttribute('unselectable'))
4509                         return;
4510
4511                 if (this.options.multiple) {
4512                         var cbox = li.querySelector('input[type="checkbox"]'),
4513                             items = li.parentNode.querySelectorAll('li'),
4514                             label = sb.querySelector('ul.preview'),
4515                             sel = li.parentNode.querySelectorAll('[selected]').length,
4516                             more = sb.querySelector('.more'),
4517                             ndisplay = this.options.display_items,
4518                             n = 0;
4519
4520                         if (li.hasAttribute('selected')) {
4521                                 if (force_state !== true) {
4522                                         if (sel > 1 || this.options.optional) {
4523                                                 li.removeAttribute('selected');
4524                                                 cbox.checked = cbox.disabled = false;
4525                                                 sel--;
4526                                         }
4527                                         else {
4528                                                 cbox.disabled = true;
4529                                         }
4530                                 }
4531                         }
4532                         else {
4533                                 if (force_state !== false) {
4534                                         li.setAttribute('selected', '');
4535                                         cbox.checked = true;
4536                                         cbox.disabled = false;
4537                                         sel++;
4538                                 }
4539                         }
4540
4541                         while (label &amp;&amp; label.firstElementChild)
4542                                 label.removeChild(label.firstElementChild);
4543
4544                         for (var i = 0; i &lt; items.length; i++) {
4545                                 items[i].removeAttribute('display');
4546                                 if (items[i].hasAttribute('selected')) {
4547                                         if (ndisplay-- > 0) {
4548                                                 items[i].setAttribute('display', n++);
4549                                                 if (label)
4550                                                         label.appendChild(items[i].cloneNode(true));
4551                                         }
4552                                         var c = items[i].querySelector('input[type="checkbox"]');
4553                                         if (c)
4554                                                 c.disabled = (sel == 1 &amp;&amp; !this.options.optional);
4555                                 }
4556                         }
4557
4558                         if (ndisplay &lt; 0)
4559                                 sb.setAttribute('more', '');
4560                         else
4561                                 sb.removeAttribute('more');
4562
4563                         if (ndisplay === this.options.display_items)
4564                                 sb.setAttribute('empty', '');
4565                         else
4566                                 sb.removeAttribute('empty');
4567
4568                         dom.content(more, (ndisplay === this.options.display_items)
4569                                 ? (this.options.select_placeholder || this.options.placeholder) : '···');
4570                 }
4571                 else {
4572                         var sel = li.parentNode.querySelector('[selected]');
4573                         if (sel) {
4574                                 sel.removeAttribute('display');
4575                                 sel.removeAttribute('selected');
4576                         }
4577
4578                         li.setAttribute('display', 0);
4579                         li.setAttribute('selected', '');
4580
4581                         this.closeDropdown(sb, true);
4582                 }
4583
4584                 this.saveValues(sb, li.parentNode);
4585         },
4586
4587         /** @private */
4588         transformItem: function(sb, li) {
4589                 var cbox = E('form', {}, E('input', { type: 'checkbox', tabindex: -1, onclick: 'event.preventDefault()' })),
4590                     label = E('label');
4591
4592                 while (li.firstChild)
4593                         label.appendChild(li.firstChild);
4594
4595                 li.appendChild(cbox);
4596                 li.appendChild(label);
4597         },
4598
4599         /** @private */
4600         saveValues: function(sb, ul) {
4601                 var sel = ul.querySelectorAll('li[selected]'),
4602                     div = sb.lastElementChild,
4603                     name = this.options.name,
4604                     strval = '',
4605                     values = [];
4606
4607                 while (div.lastElementChild)
4608                         div.removeChild(div.lastElementChild);
4609
4610                 sel.forEach(function (s) {
4611                         if (s.hasAttribute('placeholder'))
4612                                 return;
4613
4614                         var v = {
4615                                 text: s.innerText,
4616                                 value: s.hasAttribute('data-value') ? s.getAttribute('data-value') : s.innerText,
4617                                 element: s
4618                         };
4619
4620                         div.appendChild(E('input', {
4621                                 type: 'hidden',
4622                                 name: name,
4623                                 value: v.value
4624                         }));
4625
4626                         values.push(v);
4627
4628                         strval += strval.length ? ' ' + v.value : v.value;
4629                 });
4630
4631                 var detail = {
4632                         instance: this,
4633                         element: sb
4634                 };
4635
4636                 if (this.options.multiple)
4637                         detail.values = values;
4638                 else
4639                         detail.value = values.length ? values[0] : null;
4640
4641                 sb.value = strval;
4642
4643                 sb.dispatchEvent(new CustomEvent('cbi-dropdown-change', {
4644                         bubbles: true,
4645                         detail: detail
4646                 }));
4647         },
4648
4649         /** @private */
4650         setValues: function(sb, values) {
4651                 var ul = sb.querySelector('ul');
4652
4653                 if (this.options.create) {
4654                         for (var value in values) {
4655                                 this.createItems(sb, value);
4656
4657                                 if (!this.options.multiple)
4658                                         break;
4659                         }
4660                 }
4661
4662                 if (this.options.multiple) {
4663                         var lis = ul.querySelectorAll('li[data-value]');
4664                         for (var i = 0; i &lt; lis.length; i++) {
4665                                 var value = lis[i].getAttribute('data-value');
4666                                 if (values === null || !(value in values))
4667                                         this.toggleItem(sb, lis[i], false);
4668                                 else
4669                                         this.toggleItem(sb, lis[i], true);
4670                         }
4671                 }
4672                 else {
4673                         var ph = ul.querySelector('li[placeholder]');
4674                         if (ph)
4675                                 this.toggleItem(sb, ph);
4676
4677                         var lis = ul.querySelectorAll('li[data-value]');
4678                         for (var i = 0; i &lt; lis.length; i++) {
4679                                 var value = lis[i].getAttribute('data-value');
4680                                 if (values !== null &amp;&amp; (value in values))
4681                                         this.toggleItem(sb, lis[i]);
4682                         }
4683                 }
4684         },
4685
4686         /** @private */
4687         setFocus: function(sb, elem, scroll) {
4688                 if (sb &amp;&amp; sb.hasAttribute &amp;&amp; sb.hasAttribute('locked-in'))
4689                         return;
4690
4691                 if (sb.target &amp;&amp; findParent(sb.target, 'ul.dropdown'))
4692                         return;
4693
4694                 document.querySelectorAll('.focus').forEach(function(e) {
4695                         if (!matchesElem(e, 'input')) {
4696                                 e.classList.remove('focus');
4697                                 e.blur();
4698                         }
4699                 });
4700
4701                 if (elem) {
4702                         elem.focus();
4703                         elem.classList.add('focus');
4704
4705                         if (scroll)
4706                                 elem.parentNode.scrollTop = elem.offsetTop - elem.parentNode.offsetTop;
4707                 }
4708         },
4709
4710         /** @private */
4711         createChoiceElement: function(sb, value, label) {
4712                 var tpl = sb.querySelector(this.options.create_template),
4713                     markup = null;
4714
4715                 if (tpl)
4716                         markup = (tpl.textContent || tpl.innerHTML || tpl.firstChild.data).replace(/^&lt;!--|-->$/, '').trim();
4717                 else
4718                         markup = '&lt;li data-value="{{value}}">&lt;span data-label-placeholder="true" />&lt;/li>';
4719
4720                 var new_item = E(markup.replace(/{{value}}/g, '%h'.format(value))),
4721                     placeholder = new_item.querySelector('[data-label-placeholder]');
4722
4723                 if (placeholder) {
4724                         var content = E('span', {}, label || this.choices[value] || [ value ]);
4725
4726                         while (content.firstChild)
4727                                 placeholder.parentNode.insertBefore(content.firstChild, placeholder);
4728
4729                         placeholder.parentNode.removeChild(placeholder);
4730                 }
4731
4732                 if (this.options.multiple)
4733                         this.transformItem(sb, new_item);
4734
4735                 return new_item;
4736         },
4737
4738         /** @private */
4739         createItems: function(sb, value) {
4740                 var sbox = this,
4741                     val = (value || '').trim(),
4742                     ul = sb.querySelector('ul');
4743
4744                 if (!sbox.options.multiple)
4745                         val = val.length ? [ val ] : [];
4746                 else
4747                         val = val.length ? val.split(/\s+/) : [];
4748
4749                 val.forEach(function(item) {
4750                         var new_item = null;
4751
4752                         ul.childNodes.forEach(function(li) {
4753                                 if (li.getAttribute &amp;&amp; li.getAttribute('data-value') === item)
4754                                         new_item = li;
4755                         });
4756
4757                         if (!new_item) {
4758                                 new_item = sbox.createChoiceElement(sb, item);
4759
4760                                 if (!sbox.options.multiple) {
4761                                         var old = ul.querySelector('li[created]');
4762                                         if (old)
4763                                                 ul.removeChild(old);
4764
4765                                         new_item.setAttribute('created', '');
4766                                 }
4767
4768                                 new_item = ul.insertBefore(new_item, ul.lastElementChild);
4769                         }
4770
4771                         sbox.toggleItem(sb, new_item, true);
4772                         sbox.setFocus(sb, new_item, true);
4773                 });
4774         },
4775
4776         /**
4777          * Remove all existing choices from the dropdown menu.
4778          *
4779          * This function removes all preexisting dropdown choices from the widget,
4780          * keeping only choices currently being selected unless `reset_values` is
4781          * given, in which case all choices and deselected and removed.
4782          *
4783          * @instance
4784          * @memberof LuCI.ui.Dropdown
4785          * @param {boolean} [reset_value=false]
4786          * If set to `true`, deselect and remove selected choices as well instead
4787          * of keeping them.
4788          */
4789         clearChoices: function(reset_value) {
4790                 var ul = this.node.querySelector('ul'),
4791                     lis = ul ? ul.querySelectorAll('li[data-value]') : [],
4792                     len = lis.length - (this.options.create ? 1 : 0),
4793                     val = reset_value ? null : this.getValue();
4794
4795                 for (var i = 0; i &lt; len; i++) {
4796                         var lival = lis[i].getAttribute('data-value');
4797                         if (val == null ||
4798                                 (!this.options.multiple &amp;&amp; val != lival) ||
4799                                 (this.options.multiple &amp;&amp; val.indexOf(lival) == -1))
4800                                 ul.removeChild(lis[i]);
4801                 }
4802
4803                 if (reset_value)
4804                         this.setValues(this.node, {});
4805         },
4806
4807         /**
4808          * Add new choices to the dropdown menu.
4809          *
4810          * This function adds further choices to an existing dropdown menu,
4811          * ignoring choice values which are already present.
4812          *
4813          * @instance
4814          * @memberof LuCI.ui.Dropdown
4815          * @param {string[]} values
4816          * The choice values to add to the dropdown widget.
4817          *
4818          * @param {Object&lt;string, *>} labels
4819          * The choice label values to use when adding dropdown choices. If no
4820          * label is found for a particular choice value, the value itself is used
4821          * as label text. Choice labels may be any valid value accepted by
4822          * {@link LuCI.dom#content}.
4823          */
4824         addChoices: function(values, labels) {
4825                 var sb = this.node,
4826                     ul = sb.querySelector('ul'),
4827                     lis = ul ? ul.querySelectorAll('li[data-value]') : [];
4828
4829                 if (!Array.isArray(values))
4830                         values = L.toArray(values);
4831
4832                 if (!L.isObject(labels))
4833                         labels = {};
4834
4835                 for (var i = 0; i &lt; values.length; i++) {
4836                         var found = false;
4837
4838                         for (var j = 0; j &lt; lis.length; j++) {
4839                                 if (lis[j].getAttribute('data-value') === values[i]) {
4840                                         found = true;
4841                                         break;
4842                                 }
4843                         }
4844
4845                         if (found)
4846                                 continue;
4847
4848                         ul.insertBefore(
4849                                 this.createChoiceElement(sb, values[i], labels[values[i]]),
4850                                 ul.lastElementChild);
4851                 }
4852         },
4853
4854         /**
4855          * Close all open dropdown widgets in the current document.
4856          */
4857         closeAllDropdowns: function() {
4858                 document.querySelectorAll('.cbi-dropdown[open]').forEach(function(s) {
4859                         s.dispatchEvent(new CustomEvent('cbi-dropdown-close', {}));
4860                 });
4861         },
4862
4863         /** @private */
4864         handleClick: function(ev) {
4865                 var sb = ev.currentTarget;
4866
4867                 if (!sb.hasAttribute('open')) {
4868                         if (!matchesElem(ev.target, 'input'))
4869                                 this.openDropdown(sb);
4870                 }
4871                 else {
4872                         var li = findParent(ev.target, 'li');
4873                         if (li &amp;&amp; li.parentNode.classList.contains('dropdown'))
4874                                 this.toggleItem(sb, li);
4875                         else if (li &amp;&amp; li.parentNode.classList.contains('preview'))
4876                                 this.closeDropdown(sb);
4877                         else if (matchesElem(ev.target, 'span.open, span.more'))
4878                                 this.closeDropdown(sb);
4879                 }
4880
4881                 ev.preventDefault();
4882                 ev.stopPropagation();
4883         },
4884
4885         /** @private */
4886         handleKeydown: function(ev) {
4887                 var sb = ev.currentTarget;
4888
4889                 if (matchesElem(ev.target, 'input'))
4890                         return;
4891
4892                 if (!sb.hasAttribute('open')) {
4893                         switch (ev.keyCode) {
4894                         case 37:
4895                         case 38:
4896                         case 39:
4897                         case 40:
4898                                 this.openDropdown(sb);
4899                                 ev.preventDefault();
4900                         }
4901                 }
4902                 else {
4903                         var active = findParent(document.activeElement, 'li');
4904
4905                         switch (ev.keyCode) {
4906                         case 27:
4907                                 this.closeDropdown(sb);
4908                                 break;
4909
4910                         case 13:
4911                                 if (active) {
4912                                         if (!active.hasAttribute('selected'))
4913                                                 this.toggleItem(sb, active);
4914                                         this.closeDropdown(sb);
4915                                         ev.preventDefault();
4916                                 }
4917                                 break;
4918
4919                         case 32:
4920                                 if (active) {
4921                                         this.toggleItem(sb, active);
4922                                         ev.preventDefault();
4923                                 }
4924                                 break;
4925
4926                         case 38:
4927                                 if (active &amp;&amp; active.previousElementSibling) {
4928                                         this.setFocus(sb, active.previousElementSibling);
4929                                         ev.preventDefault();
4930                                 }
4931                                 break;
4932
4933                         case 40:
4934                                 if (active &amp;&amp; active.nextElementSibling) {
4935                                         this.setFocus(sb, active.nextElementSibling);
4936                                         ev.preventDefault();
4937                                 }
4938                                 break;
4939                         }
4940                 }
4941         },
4942
4943         /** @private */
4944         handleDropdownClose: function(ev) {
4945                 var sb = ev.currentTarget;
4946
4947                 this.closeDropdown(sb, true);
4948         },
4949
4950         /** @private */
4951         handleDropdownSelect: function(ev) {
4952                 var sb = ev.currentTarget,
4953                     li = findParent(ev.target, 'li');
4954
4955                 if (!li)
4956                         return;
4957
4958                 this.toggleItem(sb, li);
4959                 this.closeDropdown(sb, true);
4960         },
4961
4962         /** @private */
4963         handleMouseover: function(ev) {
4964                 var sb = ev.currentTarget;
4965
4966                 if (!sb.hasAttribute('open'))
4967                         return;
4968
4969                 var li = findParent(ev.target, 'li');
4970
4971                 if (li &amp;&amp; li.parentNode.classList.contains('dropdown'))
4972                         this.setFocus(sb, li);
4973         },
4974
4975         /** @private */
4976         handleFocus: function(ev) {
4977                 var sb = ev.currentTarget;
4978
4979                 document.querySelectorAll('.cbi-dropdown[open]').forEach(function(s) {
4980                         if (s !== sb || sb.hasAttribute('open'))
4981                                 s.dispatchEvent(new CustomEvent('cbi-dropdown-close', {}));
4982                 });
4983         },
4984
4985         /** @private */
4986         handleCanaryFocus: function(ev) {
4987                 this.closeDropdown(ev.currentTarget.parentNode);
4988         },
4989
4990         /** @private */
4991         handleCreateKeydown: function(ev) {
4992                 var input = ev.currentTarget,
4993                     sb = findParent(input, '.cbi-dropdown');
4994
4995                 switch (ev.keyCode) {
4996                 case 13:
4997                         ev.preventDefault();
4998
4999                         if (input.classList.contains('cbi-input-invalid'))
5000                                 return;
5001
5002                         this.createItems(sb, input.value);
5003                         input.value = '';
5004                         input.blur();
5005                         break;
5006                 }
5007         },
5008
5009         /** @private */
5010         handleCreateFocus: function(ev) {
5011                 var input = ev.currentTarget,
5012                     cbox = findParent(input, 'li').querySelector('input[type="checkbox"]'),
5013                     sb = findParent(input, '.cbi-dropdown');
5014
5015                 if (cbox)
5016                         cbox.checked = true;
5017
5018                 sb.setAttribute('locked-in', '');
5019         },
5020
5021         /** @private */
5022         handleCreateBlur: function(ev) {
5023                 var input = ev.currentTarget,
5024                     cbox = findParent(input, 'li').querySelector('input[type="checkbox"]'),
5025                     sb = findParent(input, '.cbi-dropdown');
5026
5027                 if (cbox)
5028                         cbox.checked = false;
5029
5030                 sb.removeAttribute('locked-in');
5031         },
5032
5033         /** @private */
5034         handleCreateClick: function(ev) {
5035                 ev.currentTarget.querySelector(this.options.create_query).focus();
5036         },
5037
5038         /** @override */
5039         setValue: function(values) {
5040                 if (this.options.multiple) {
5041                         if (!Array.isArray(values))
5042                                 values = (values != null &amp;&amp; values != '') ? [ values ] : [];
5043
5044                         var v = {};
5045
5046                         for (var i = 0; i &lt; values.length; i++)
5047                                 v[values[i]] = true;
5048
5049                         this.setValues(this.node, v);
5050                 }
5051                 else {
5052                         var v = {};
5053
5054                         if (values != null) {
5055                                 if (Array.isArray(values))
5056                                         v[values[0]] = true;
5057                                 else
5058                                         v[values] = true;
5059                         }
5060
5061                         this.setValues(this.node, v);
5062                 }
5063         },
5064
5065         /** @override */
5066         getValue: function() {
5067                 var div = this.node.lastElementChild,
5068                     h = div.querySelectorAll('input[type="hidden"]'),
5069                         v = [];
5070
5071                 for (var i = 0; i &lt; h.length; i++)
5072                         v.push(h[i].value);
5073
5074                 return this.options.multiple ? v : v[0];
5075         }
5076 });
5077
5078 /**
5079  * Instantiate a rich dropdown choice widget allowing custom values.
5080  *
5081  * @constructor Combobox
5082  * @memberof LuCI.ui
5083  * @augments LuCI.ui.Dropdown
5084  *
5085  * @classdesc
5086  *
5087  * The `Combobox` class implements a rich, stylable dropdown menu which allows
5088  * to enter custom values. Historically, comboboxes used to be a dedicated
5089  * widget type in LuCI but nowadays they are direct aliases of dropdown widgets
5090  * with a set of enforced default properties for easier instantiation.
5091  *
5092  * UI widget instances are usually not supposed to be created by view code
5093  * directly, instead they're implicitely created by `LuCI.form` when
5094  * instantiating CBI forms.
5095  *
5096  * This class is automatically instantiated as part of `LuCI.ui`. To use it
5097  * in views, use `'require ui'` and refer to `ui.Combobox`. To import it in
5098  * external JavaScript, use `L.require("ui").then(...)` and access the
5099  * `Combobox` property of the class instance value.
5100  *
5101  * @param {string|string[]} [value=null]
5102  * The initial input value(s).
5103  *
5104  * @param {Object&lt;string, *>} choices
5105  * Object containing the selectable choices of the widget. The object keys
5106  * serve as values for the different choices while the values are used as
5107  * choice labels.
5108  *
5109  * @param {LuCI.ui.Combobox.InitOptions} [options]
5110  * Object describing the widget specific options to initialize the dropdown.
5111  */
5112 var UICombobox = UIDropdown.extend(/** @lends LuCI.ui.Combobox.prototype */ {
5113         /**
5114          * Comboboxes support the same properties as
5115          * [Dropdown.InitOptions]{@link LuCI.ui.Dropdown.InitOptions} but enforce
5116          * specific values for the following properties:
5117          *
5118          * @typedef {LuCI.ui.Dropdown.InitOptions} InitOptions
5119          * @memberof LuCI.ui.Combobox
5120          *
5121          * @property {boolean} multiple=false
5122          * Since Comboboxes never allow selecting multiple values, this property
5123          * is forcibly set to `false`.
5124          *
5125          * @property {boolean} create=true
5126          * Since Comboboxes always allow custom choice values, this property is
5127          * forcibly set to `true`.
5128          *
5129          * @property {boolean} optional=true
5130          * Since Comboboxes are always optional, this property is forcibly set to
5131          * `true`.
5132          */
5133         __init__: function(value, choices, options) {
5134                 this.super('__init__', [ value, choices, Object.assign({
5135                         select_placeholder: _('-- Please choose --'),
5136                         custom_placeholder: _('-- custom --'),
5137                         dropdown_items: -1,
5138                         sort: true
5139                 }, options, {
5140                         multiple: false,
5141                         create: true,
5142                         optional: true
5143                 }) ]);
5144         }
5145 });
5146
5147 /**
5148  * Instantiate a combo button widget offering multiple action choices.
5149  *
5150  * @constructor ComboButton
5151  * @memberof LuCI.ui
5152  * @augments LuCI.ui.Dropdown
5153  *
5154  * @classdesc
5155  *
5156  * The `ComboButton` class implements a button element which can be expanded
5157  * into a dropdown to chose from a set of different action choices.
5158  *
5159  * UI widget instances are usually not supposed to be created by view code
5160  * directly, instead they're implicitely created by `LuCI.form` when
5161  * instantiating CBI forms.
5162  *
5163  * This class is automatically instantiated as part of `LuCI.ui`. To use it
5164  * in views, use `'require ui'` and refer to `ui.ComboButton`. To import it in
5165  * external JavaScript, use `L.require("ui").then(...)` and access the
5166  * `ComboButton` property of the class instance value.
5167  *
5168  * @param {string|string[]} [value=null]
5169  * The initial input value(s).
5170  *
5171  * @param {Object&lt;string, *>} choices
5172  * Object containing the selectable choices of the widget. The object keys
5173  * serve as values for the different choices while the values are used as
5174  * choice labels.
5175  *
5176  * @param {LuCI.ui.ComboButton.InitOptions} [options]
5177  * Object describing the widget specific options to initialize the button.
5178  */
5179 var UIComboButton = UIDropdown.extend(/** @lends LuCI.ui.ComboButton.prototype */ {
5180         /**
5181          * ComboButtons support the same properties as
5182          * [Dropdown.InitOptions]{@link LuCI.ui.Dropdown.InitOptions} but enforce
5183          * specific values for some properties and add aditional button specific
5184          * properties.
5185          *
5186          * @typedef {LuCI.ui.Dropdown.InitOptions} InitOptions
5187          * @memberof LuCI.ui.ComboButton
5188          *
5189          * @property {boolean} multiple=false
5190          * Since ComboButtons never allow selecting multiple actions, this property
5191          * is forcibly set to `false`.
5192          *
5193          * @property {boolean} create=false
5194          * Since ComboButtons never allow creating custom choices, this property
5195          * is forcibly set to `false`.
5196          *
5197          * @property {boolean} optional=false
5198          * Since ComboButtons must always select one action, this property is
5199          * forcibly set to `false`.
5200          *
5201          * @property {Object&lt;string, string>} [classes]
5202          * Specifies a mapping of choice values to CSS class names. If an action
5203          * choice is selected by the user and if a corresponding entry exists in
5204          * the `classes` object, the class names corresponding to the selected
5205          * value are set on the button element.
5206          *
5207          * This is useful to apply different button styles, such as colors, to the
5208          * combined button depending on the selected action.
5209          *
5210          * @property {function} [click]
5211          * Specifies a handler function to invoke when the user clicks the button.
5212          * This function will be called with the button DOM node as `this` context
5213          * and receive the DOM click event as first as well as the selected action
5214          * choice value as second argument.
5215          */
5216         __init__: function(value, choices, options) {
5217                 this.super('__init__', [ value, choices, Object.assign({
5218                         sort: true
5219                 }, options, {
5220                         multiple: false,
5221                         create: false,
5222                         optional: false
5223                 }) ]);
5224         },
5225
5226         /** @override */
5227         render: function(/* ... */) {
5228                 var node = UIDropdown.prototype.render.apply(this, arguments),
5229                     val = this.getValue();
5230
5231                 if (L.isObject(this.options.classes) &amp;&amp; this.options.classes.hasOwnProperty(val))
5232                         node.setAttribute('class', 'cbi-dropdown ' + this.options.classes[val]);
5233
5234                 return node;
5235         },
5236
5237         /** @private */
5238         handleClick: function(ev) {
5239                 var sb = ev.currentTarget,
5240                     t = ev.target;
5241
5242                 if (sb.hasAttribute('open') || dom.matches(t, '.cbi-dropdown > span.open'))
5243                         return UIDropdown.prototype.handleClick.apply(this, arguments);
5244
5245                 if (this.options.click)
5246                         return this.options.click.call(sb, ev, this.getValue());
5247         },
5248
5249         /** @private */
5250         toggleItem: function(sb /*, ... */) {
5251                 var rv = UIDropdown.prototype.toggleItem.apply(this, arguments),
5252                     val = this.getValue();
5253
5254                 if (L.isObject(this.options.classes) &amp;&amp; this.options.classes.hasOwnProperty(val))
5255                         sb.setAttribute('class', 'cbi-dropdown ' + this.options.classes[val]);
5256                 else
5257                         sb.setAttribute('class', 'cbi-dropdown');
5258
5259                 return rv;
5260         }
5261 });
5262
5263 /**
5264  * Instantiate a dynamic list widget.
5265  *
5266  * @constructor DynamicList
5267  * @memberof LuCI.ui
5268  * @augments LuCI.ui.AbstractElement
5269  *
5270  * @classdesc
5271  *
5272  * The `DynamicList` class implements a widget which allows the user to specify
5273  * an arbitrary amount of input values, either from free formed text input or
5274  * from a set of predefined choices.
5275  *
5276  * UI widget instances are usually not supposed to be created by view code
5277  * directly, instead they're implicitely created by `LuCI.form` when
5278  * instantiating CBI forms.
5279  *
5280  * This class is automatically instantiated as part of `LuCI.ui`. To use it
5281  * in views, use `'require ui'` and refer to `ui.DynamicList`. To import it in
5282  * external JavaScript, use `L.require("ui").then(...)` and access the
5283  * `DynamicList` property of the class instance value.
5284  *
5285  * @param {string|string[]} [value=null]
5286  * The initial input value(s).
5287  *
5288  * @param {Object&lt;string, *>} [choices]
5289  * Object containing the selectable choices of the widget. The object keys
5290  * serve as values for the different choices while the values are used as
5291  * choice labels. If omitted, no default choices are presented to the user,
5292  * instead a plain text input field is rendered allowing the user to add
5293  * arbitrary values to the dynamic list.
5294  *
5295  * @param {LuCI.ui.DynamicList.InitOptions} [options]
5296  * Object describing the widget specific options to initialize the dynamic list.
5297  */
5298 var UIDynamicList = UIElement.extend(/** @lends LuCI.ui.DynamicList.prototype */ {
5299         /**
5300          * In case choices are passed to the dynamic list contructor, the widget
5301          * supports the same properties as [Dropdown.InitOptions]{@link LuCI.ui.Dropdown.InitOptions}
5302          * but enforces specific values for some dropdown properties.
5303          *
5304          * @typedef {LuCI.ui.Dropdown.InitOptions} InitOptions
5305          * @memberof LuCI.ui.DynamicList
5306          *
5307          * @property {boolean} multiple=false
5308          * Since dynamic lists never allow selecting multiple choices when adding
5309          * another list item, this property is forcibly set to `false`.
5310          *
5311          * @property {boolean} optional=true
5312          * Since dynamic lists use an embedded dropdown to present a list of
5313          * predefined choice values, the dropdown must be made optional to allow
5314          * it to remain unselected.
5315          */
5316         __init__: function(values, choices, options) {
5317                 if (!Array.isArray(values))
5318                         values = (values != null &amp;&amp; values != '') ? [ values ] : [];
5319
5320                 if (typeof(choices) != 'object')
5321                         choices = null;
5322
5323                 this.values = values;
5324                 this.choices = choices;
5325                 this.options = Object.assign({}, options, {
5326                         multiple: false,
5327                         optional: true
5328                 });
5329         },
5330
5331         /** @override */
5332         render: function() {
5333                 var dl = E('div', {
5334                         'id': this.options.id,
5335                         'class': 'cbi-dynlist'
5336                 }, E('div', { 'class': 'add-item' }));
5337
5338                 if (this.choices) {
5339                         if (this.options.placeholder != null)
5340                                 this.options.select_placeholder = this.options.placeholder;
5341
5342                         var cbox = new UICombobox(null, this.choices, this.options);
5343
5344                         dl.lastElementChild.appendChild(cbox.render());
5345                 }
5346                 else {
5347                         var inputEl = E('input', {
5348                                 'id': this.options.id ? 'widget.' + this.options.id : null,
5349                                 'type': 'text',
5350                                 'class': 'cbi-input-text',
5351                                 'placeholder': this.options.placeholder
5352                         });
5353
5354                         dl.lastElementChild.appendChild(inputEl);
5355                         dl.lastElementChild.appendChild(E('div', { 'class': 'btn cbi-button cbi-button-add' }, '+'));
5356
5357                         if (this.options.datatype || this.options.validate)
5358                                 UI.prototype.addValidator(inputEl, this.options.datatype || 'string',
5359                                                           true, this.options.validate, 'blur', 'keyup');
5360                 }
5361
5362                 for (var i = 0; i &lt; this.values.length; i++) {
5363                         var label = this.choices ? this.choices[this.values[i]] : null;
5364
5365                         if (dom.elem(label))
5366                                 label = label.cloneNode(true);
5367
5368                         this.addItem(dl, this.values[i], label);
5369                 }
5370
5371                 return this.bind(dl);
5372         },
5373
5374         /** @private */
5375         bind: function(dl) {
5376                 dl.addEventListener('click', L.bind(this.handleClick, this));
5377                 dl.addEventListener('keydown', L.bind(this.handleKeydown, this));
5378                 dl.addEventListener('cbi-dropdown-change', L.bind(this.handleDropdownChange, this));
5379
5380                 this.node = dl;
5381
5382                 this.setUpdateEvents(dl, 'cbi-dynlist-change');
5383                 this.setChangeEvents(dl, 'cbi-dynlist-change');
5384
5385                 dom.bindClassInstance(dl, this);
5386
5387                 return dl;
5388         },
5389
5390         /** @private */
5391         addItem: function(dl, value, text, flash) {
5392                 var exists = false,
5393                     new_item = E('div', { 'class': flash ? 'item flash' : 'item', 'tabindex': 0 }, [
5394                                 E('span', {}, [ text || value ]),
5395                                 E('input', {
5396                                         'type': 'hidden',
5397                                         'name': this.options.name,
5398                                         'value': value })]);
5399
5400                 dl.querySelectorAll('.item').forEach(function(item) {
5401                         if (exists)
5402                                 return;
5403
5404                         var hidden = item.querySelector('input[type="hidden"]');
5405
5406                         if (hidden &amp;&amp; hidden.parentNode !== item)
5407                                 hidden = null;
5408
5409                         if (hidden &amp;&amp; hidden.value === value)
5410                                 exists = true;
5411                 });
5412
5413                 if (!exists) {
5414                         var ai = dl.querySelector('.add-item');
5415                         ai.parentNode.insertBefore(new_item, ai);
5416                 }
5417
5418                 dl.dispatchEvent(new CustomEvent('cbi-dynlist-change', {
5419                         bubbles: true,
5420                         detail: {
5421                                 instance: this,
5422                                 element: dl,
5423                                 value: value,
5424                                 add: true
5425                         }
5426                 }));
5427         },
5428
5429         /** @private */
5430         removeItem: function(dl, item) {
5431                 var value = item.querySelector('input[type="hidden"]').value;
5432                 var sb = dl.querySelector('.cbi-dropdown');
5433                 if (sb)
5434                         sb.querySelectorAll('ul > li').forEach(function(li) {
5435                                 if (li.getAttribute('data-value') === value) {
5436                                         if (li.hasAttribute('dynlistcustom'))
5437                                                 li.parentNode.removeChild(li);
5438                                         else
5439                                                 li.removeAttribute('unselectable');
5440                                 }
5441                         });
5442
5443                 item.parentNode.removeChild(item);
5444
5445                 dl.dispatchEvent(new CustomEvent('cbi-dynlist-change', {
5446                         bubbles: true,
5447                         detail: {
5448                                 instance: this,
5449                                 element: dl,
5450                                 value: value,
5451                                 remove: true
5452                         }
5453                 }));
5454         },
5455
5456         /** @private */
5457         handleClick: function(ev) {
5458                 var dl = ev.currentTarget,
5459                     item = findParent(ev.target, '.item');
5460
5461                 if (item) {
5462                         this.removeItem(dl, item);
5463                 }
5464                 else if (matchesElem(ev.target, '.cbi-button-add')) {
5465                         var input = ev.target.previousElementSibling;
5466                         if (input.value.length &amp;&amp; !input.classList.contains('cbi-input-invalid')) {
5467                                 this.addItem(dl, input.value, null, true);
5468                                 input.value = '';
5469                         }
5470                 }
5471         },
5472
5473         /** @private */
5474         handleDropdownChange: function(ev) {
5475                 var dl = ev.currentTarget,
5476                     sbIn = ev.detail.instance,
5477                     sbEl = ev.detail.element,
5478                     sbVal = ev.detail.value;
5479
5480                 if (sbVal === null)
5481                         return;
5482
5483                 sbIn.setValues(sbEl, null);
5484                 sbVal.element.setAttribute('unselectable', '');
5485
5486                 if (sbVal.element.hasAttribute('created')) {
5487                         sbVal.element.removeAttribute('created');
5488                         sbVal.element.setAttribute('dynlistcustom', '');
5489                 }
5490
5491                 var label = sbVal.text;
5492
5493                 if (sbVal.element) {
5494                         label = E([]);
5495
5496                         for (var i = 0; i &lt; sbVal.element.childNodes.length; i++)
5497                                 label.appendChild(sbVal.element.childNodes[i].cloneNode(true));
5498                 }
5499
5500                 this.addItem(dl, sbVal.value, label, true);
5501         },
5502
5503         /** @private */
5504         handleKeydown: function(ev) {
5505                 var dl = ev.currentTarget,
5506                     item = findParent(ev.target, '.item');
5507
5508                 if (item) {
5509                         switch (ev.keyCode) {
5510                         case 8: /* backspace */
5511                                 if (item.previousElementSibling)
5512                                         item.previousElementSibling.focus();
5513
5514                                 this.removeItem(dl, item);
5515                                 break;
5516
5517                         case 46: /* delete */
5518                                 if (item.nextElementSibling) {
5519                                         if (item.nextElementSibling.classList.contains('item'))
5520                                                 item.nextElementSibling.focus();
5521                                         else
5522                                                 item.nextElementSibling.firstElementChild.focus();
5523                                 }
5524
5525                                 this.removeItem(dl, item);
5526                                 break;
5527                         }
5528                 }
5529                 else if (matchesElem(ev.target, '.cbi-input-text')) {
5530                         switch (ev.keyCode) {
5531                         case 13: /* enter */
5532                                 if (ev.target.value.length &amp;&amp; !ev.target.classList.contains('cbi-input-invalid')) {
5533                                         this.addItem(dl, ev.target.value, null, true);
5534                                         ev.target.value = '';
5535                                         ev.target.blur();
5536                                         ev.target.focus();
5537                                 }
5538
5539                                 ev.preventDefault();
5540                                 break;
5541                         }
5542                 }
5543         },
5544
5545         /** @override */
5546         getValue: function() {
5547                 var items = this.node.querySelectorAll('.item > input[type="hidden"]'),
5548                     input = this.node.querySelector('.add-item > input[type="text"]'),
5549                     v = [];
5550
5551                 for (var i = 0; i &lt; items.length; i++)
5552                         v.push(items[i].value);
5553
5554                 if (input &amp;&amp; input.value != null &amp;&amp; input.value.match(/\S/) &amp;&amp;
5555                     input.classList.contains('cbi-input-invalid') == false &amp;&amp;
5556                     v.filter(function(s) { return s == input.value }).length == 0)
5557                         v.push(input.value);
5558
5559                 return v;
5560         },
5561
5562         /** @override */
5563         setValue: function(values) {
5564                 if (!Array.isArray(values))
5565                         values = (values != null &amp;&amp; values != '') ? [ values ] : [];
5566
5567                 var items = this.node.querySelectorAll('.item');
5568
5569                 for (var i = 0; i &lt; items.length; i++)
5570                         if (items[i].parentNode === this.node)
5571                                 this.removeItem(this.node, items[i]);
5572
5573                 for (var i = 0; i &lt; values.length; i++)
5574                         this.addItem(this.node, values[i],
5575                                 this.choices ? this.choices[values[i]] : null);
5576         },
5577
5578         /**
5579          * Add new suggested choices to the dynamic list.
5580          *
5581          * This function adds further choices to an existing dynamic list,
5582          * ignoring choice values which are already present.
5583          *
5584          * @instance
5585          * @memberof LuCI.ui.DynamicList
5586          * @param {string[]} values
5587          * The choice values to add to the dynamic lists suggestion dropdown.
5588          *
5589          * @param {Object&lt;string, *>} labels
5590          * The choice label values to use when adding suggested choices. If no
5591          * label is found for a particular choice value, the value itself is used
5592          * as label text. Choice labels may be any valid value accepted by
5593          * {@link LuCI.dom#content}.
5594          */
5595         addChoices: function(values, labels) {
5596                 var dl = this.node.lastElementChild.firstElementChild;
5597                 dom.callClassMethod(dl, 'addChoices', values, labels);
5598         },
5599
5600         /**
5601          * Remove all existing choices from the dynamic list.
5602          *
5603          * This function removes all preexisting suggested choices from the widget.
5604          *
5605          * @instance
5606          * @memberof LuCI.ui.DynamicList
5607          */
5608         clearChoices: function() {
5609                 var dl = this.node.lastElementChild.firstElementChild;
5610                 dom.callClassMethod(dl, 'clearChoices');
5611         }
5612 });
5613
5614 /**
5615  * Instantiate a hidden input field widget.
5616  *
5617  * @constructor Hiddenfield
5618  * @memberof LuCI.ui
5619  * @augments LuCI.ui.AbstractElement
5620  *
5621  * @classdesc
5622  *
5623  * The `Hiddenfield` class implements an HTML `&lt;input type="hidden">` field
5624  * which allows to store form data without exposing it to the user.
5625  *
5626  * UI widget instances are usually not supposed to be created by view code
5627  * directly, instead they're implicitely created by `LuCI.form` when
5628  * instantiating CBI forms.
5629  *
5630  * This class is automatically instantiated as part of `LuCI.ui`. To use it
5631  * in views, use `'require ui'` and refer to `ui.Hiddenfield`. To import it in
5632  * external JavaScript, use `L.require("ui").then(...)` and access the
5633  * `Hiddenfield` property of the class instance value.
5634  *
5635  * @param {string|string[]} [value=null]
5636  * The initial input value.
5637  *
5638  * @param {LuCI.ui.AbstractElement.InitOptions} [options]
5639  * Object describing the widget specific options to initialize the hidden input.
5640  */
5641 var UIHiddenfield = UIElement.extend(/** @lends LuCI.ui.Hiddenfield.prototype */ {
5642         __init__: function(value, options) {
5643                 this.value = value;
5644                 this.options = Object.assign({
5645
5646                 }, options);
5647         },
5648
5649         /** @override */
5650         render: function() {
5651                 var hiddenEl = E('input', {
5652                         'id': this.options.id,
5653                         'type': 'hidden',
5654                         'value': this.value
5655                 });
5656
5657                 return this.bind(hiddenEl);
5658         },
5659
5660         /** @private */
5661         bind: function(hiddenEl) {
5662                 this.node = hiddenEl;
5663
5664                 dom.bindClassInstance(hiddenEl, this);
5665
5666                 return hiddenEl;
5667         },
5668
5669         /** @override */
5670         getValue: function() {
5671                 return this.node.value;
5672         },
5673
5674         /** @override */
5675         setValue: function(value) {
5676                 this.node.value = value;
5677         }
5678 });
5679
5680 /**
5681  * Instantiate a file upload widget.
5682  *
5683  * @constructor FileUpload
5684  * @memberof LuCI.ui
5685  * @augments LuCI.ui.AbstractElement
5686  *
5687  * @classdesc
5688  *
5689  * The `FileUpload` class implements a widget which allows the user to upload,
5690  * browse, select and delete files beneath a predefined remote directory.
5691  *
5692  * UI widget instances are usually not supposed to be created by view code
5693  * directly, instead they're implicitely created by `LuCI.form` when
5694  * instantiating CBI forms.
5695  *
5696  * This class is automatically instantiated as part of `LuCI.ui`. To use it
5697  * in views, use `'require ui'` and refer to `ui.FileUpload`. To import it in
5698  * external JavaScript, use `L.require("ui").then(...)` and access the
5699  * `FileUpload` property of the class instance value.
5700  *
5701  * @param {string|string[]} [value=null]
5702  * The initial input value.
5703  *
5704  * @param {LuCI.ui.DynamicList.InitOptions} [options]
5705  * Object describing the widget specific options to initialize the file
5706  * upload control.
5707  */
5708 var UIFileUpload = UIElement.extend(/** @lends LuCI.ui.FileUpload.prototype */ {
5709         /**
5710          * In addition to the [AbstractElement.InitOptions]{@link LuCI.ui.AbstractElement.InitOptions}
5711          * the following properties are recognized:
5712          *
5713          * @typedef {LuCI.ui.AbstractElement.InitOptions} InitOptions
5714          * @memberof LuCI.ui.FileUpload
5715          *
5716          * @property {boolean} [show_hidden=false]
5717          * Specifies whether hidden files should be displayed when browsing remote
5718          * files. Note that this is not a security feature, hidden files are always
5719          * present in the remote file listings received, this option merely controls
5720          * whether they're displayed or not.
5721          *
5722          * @property {boolean} [enable_upload=true]
5723          * Specifies whether the widget allows the user to upload files. If set to
5724          * `false`, only existing files may be selected. Note that this is not a
5725          * security feature. Whether file upload requests are accepted remotely
5726          * depends on the ACL setup for the current session. This option merely
5727          * controls whether the upload controls are rendered or not.
5728          *
5729          * @property {boolean} [enable_remove=true]
5730          * Specifies whether the widget allows the user to delete remove files.
5731          * If set to `false`, existing files may not be removed. Note that this is
5732          * not a security feature. Whether file delete requests are accepted
5733          * remotely depends on the ACL setup for the current session. This option
5734          * merely controls whether the file remove controls are rendered or not.
5735          *
5736          * @property {string} [root_directory=/etc/luci-uploads]
5737          * Specifies the remote directory the upload and file browsing actions take
5738          * place in. Browsing to directories outside of the root directory is
5739          * prevented by the widget. Note that this is not a security feature.
5740          * Whether remote directories are browseable or not solely depends on the
5741          * ACL setup for the current session.
5742          */
5743         __init__: function(value, options) {
5744                 this.value = value;
5745                 this.options = Object.assign({
5746                         show_hidden: false,
5747                         enable_upload: true,
5748                         enable_remove: true,
5749                         root_directory: '/etc/luci-uploads'
5750                 }, options);
5751         },
5752
5753         /** @private */
5754         bind: function(browserEl) {
5755                 this.node = browserEl;
5756
5757                 this.setUpdateEvents(browserEl, 'cbi-fileupload-select', 'cbi-fileupload-cancel');
5758                 this.setChangeEvents(browserEl, 'cbi-fileupload-select', 'cbi-fileupload-cancel');
5759
5760                 dom.bindClassInstance(browserEl, this);
5761
5762                 return browserEl;
5763         },
5764
5765         /** @override */
5766         render: function() {
5767                 return L.resolveDefault(this.value != null ? fs.stat(this.value) : null).then(L.bind(function(stat) {
5768                         var label;
5769
5770                         if (L.isObject(stat) &amp;&amp; stat.type != 'directory')
5771                                 this.stat = stat;
5772
5773                         if (this.stat != null)
5774                                 label = [ this.iconForType(this.stat.type), ' %s (%1000mB)'.format(this.truncatePath(this.stat.path), this.stat.size) ];
5775                         else if (this.value != null)
5776                                 label = [ this.iconForType('file'), ' %s (%s)'.format(this.truncatePath(this.value), _('File not accessible')) ];
5777                         else
5778                                 label = [ _('Select file…') ];
5779
5780                         return this.bind(E('div', { 'id': this.options.id }, [
5781                                 E('button', {
5782                                         'class': 'btn',
5783                                         'click': UI.prototype.createHandlerFn(this, 'handleFileBrowser')
5784                                 }, label),
5785                                 E('div', {
5786                                         'class': 'cbi-filebrowser'
5787                                 }),
5788                                 E('input', {
5789                                         'type': 'hidden',
5790                                         'name': this.options.name,
5791                                         'value': this.value
5792                                 })
5793                         ]));
5794                 }, this));
5795         },
5796
5797         /** @private */
5798         truncatePath: function(path) {
5799                 if (path.length > 50)
5800                         path = path.substring(0, 25) + '…' + path.substring(path.length - 25);
5801
5802                 return path;
5803         },
5804
5805         /** @private */
5806         iconForType: function(type) {
5807                 switch (type) {
5808                 case 'symlink':
5809                         return E('img', {
5810                                 'src': L.resource('cbi/link.gif'),
5811                                 'title': _('Symbolic link'),
5812                                 'class': 'middle'
5813                         });
5814
5815                 case 'directory':
5816                         return E('img', {
5817                                 'src': L.resource('cbi/folder.gif'),
5818                                 'title': _('Directory'),
5819                                 'class': 'middle'
5820                         });
5821
5822                 default:
5823                         return E('img', {
5824                                 'src': L.resource('cbi/file.gif'),
5825                                 'title': _('File'),
5826                                 'class': 'middle'
5827                         });
5828                 }
5829         },
5830
5831         /** @private */
5832         canonicalizePath: function(path) {
5833                 return path.replace(/\/{2,}/, '/')
5834                         .replace(/\/\.(\/|$)/g, '/')
5835                         .replace(/[^\/]+\/\.\.(\/|$)/g, '/')
5836                         .replace(/\/$/, '');
5837         },
5838
5839         /** @private */
5840         splitPath: function(path) {
5841                 var croot = this.canonicalizePath(this.options.root_directory || '/'),
5842                     cpath = this.canonicalizePath(path || '/');
5843
5844                 if (cpath.length &lt;= croot.length)
5845                         return [ croot ];
5846
5847                 if (cpath.charAt(croot.length) != '/')
5848                         return [ croot ];
5849
5850                 var parts = cpath.substring(croot.length + 1).split(/\//);
5851
5852                 parts.unshift(croot);
5853
5854                 return parts;
5855         },
5856
5857         /** @private */
5858         handleUpload: function(path, list, ev) {
5859                 var form = ev.target.parentNode,
5860                     fileinput = form.querySelector('input[type="file"]'),
5861                     nameinput = form.querySelector('input[type="text"]'),
5862                     filename = (nameinput.value != null ? nameinput.value : '').trim();
5863
5864                 ev.preventDefault();
5865
5866                 if (filename == '' || filename.match(/\//) || fileinput.files[0] == null)
5867                         return;
5868
5869                 var existing = list.filter(function(e) { return e.name == filename })[0];
5870
5871                 if (existing != null &amp;&amp; existing.type == 'directory')
5872                         return alert(_('A directory with the same name already exists.'));
5873                 else if (existing != null &amp;&amp; !confirm(_('Overwrite existing file "%s" ?').format(filename)))
5874                         return;
5875
5876                 var data = new FormData();
5877
5878                 data.append('sessionid', L.env.sessionid);
5879                 data.append('filename', path + '/' + filename);
5880                 data.append('filedata', fileinput.files[0]);
5881
5882                 return request.post(L.env.cgi_base + '/cgi-upload', data, {
5883                         progress: L.bind(function(btn, ev) {
5884                                 btn.firstChild.data = '%.2f%%'.format((ev.loaded / ev.total) * 100);
5885                         }, this, ev.target)
5886                 }).then(L.bind(function(path, ev, res) {
5887                         var reply = res.json();
5888
5889                         if (L.isObject(reply) &amp;&amp; reply.failure)
5890                                 alert(_('Upload request failed: %s').format(reply.message));
5891
5892                         return this.handleSelect(path, null, ev);
5893                 }, this, path, ev));
5894         },
5895
5896         /** @private */
5897         handleDelete: function(path, fileStat, ev) {
5898                 var parent = path.replace(/\/[^\/]+$/, '') || '/',
5899                     name = path.replace(/^.+\//, ''),
5900                     msg;
5901
5902                 ev.preventDefault();
5903
5904                 if (fileStat.type == 'directory')
5905                         msg = _('Do you really want to recursively delete the directory "%s" ?').format(name);
5906                 else
5907                         msg = _('Do you really want to delete "%s" ?').format(name);
5908
5909                 if (confirm(msg)) {
5910                         var button = this.node.firstElementChild,
5911                             hidden = this.node.lastElementChild;
5912
5913                         if (path == hidden.value) {
5914                                 dom.content(button, _('Select file…'));
5915                                 hidden.value = '';
5916                         }
5917
5918                         return fs.remove(path).then(L.bind(function(parent, ev) {
5919                                 return this.handleSelect(parent, null, ev);
5920                         }, this, parent, ev)).catch(function(err) {
5921                                 alert(_('Delete request failed: %s').format(err.message));
5922                         });
5923                 }
5924         },
5925
5926         /** @private */
5927         renderUpload: function(path, list) {
5928                 if (!this.options.enable_upload)
5929                         return E([]);
5930
5931                 return E([
5932                         E('a', {
5933                                 'href': '#',
5934                                 'class': 'btn cbi-button-positive',
5935                                 'click': function(ev) {
5936                                         var uploadForm = ev.target.nextElementSibling,
5937                                             fileInput = uploadForm.querySelector('input[type="file"]');
5938
5939                                         ev.target.style.display = 'none';
5940                                         uploadForm.style.display = '';
5941                                         fileInput.click();
5942                                 }
5943                         }, _('Upload file…')),
5944                         E('div', { 'class': 'upload', 'style': 'display:none' }, [
5945                                 E('input', {
5946                                         'type': 'file',
5947                                         'style': 'display:none',
5948                                         'change': function(ev) {
5949                                                 var nameinput = ev.target.parentNode.querySelector('input[type="text"]'),
5950                                                     uploadbtn = ev.target.parentNode.querySelector('button.cbi-button-save');
5951
5952                                                 nameinput.value = ev.target.value.replace(/^.+[\/\\]/, '');
5953                                                 uploadbtn.disabled = false;
5954                                         }
5955                                 }),
5956                                 E('button', {
5957                                         'class': 'btn',
5958                                         'click': function(ev) {
5959                                                 ev.preventDefault();
5960                                                 ev.target.previousElementSibling.click();
5961                                         }
5962                                 }, [ _('Browse…') ]),
5963                                 E('div', {}, E('input', { 'type': 'text', 'placeholder': _('Filename') })),
5964                                 E('button', {
5965                                         'class': 'btn cbi-button-save',
5966                                         'click': UI.prototype.createHandlerFn(this, 'handleUpload', path, list),
5967                                         'disabled': true
5968                                 }, [ _('Upload file') ])
5969                         ])
5970                 ]);
5971         },
5972
5973         /** @private */
5974         renderListing: function(container, path, list) {
5975                 var breadcrumb = E('p'),
5976                     rows = E('ul');
5977
5978                 list.sort(function(a, b) {
5979                         var isDirA = (a.type == 'directory'),
5980                             isDirB = (b.type == 'directory');
5981
5982                         if (isDirA != isDirB)
5983                                 return isDirA &lt; isDirB;
5984
5985                         return a.name > b.name;
5986                 });
5987
5988                 for (var i = 0; i &lt; list.length; i++) {
5989                         if (!this.options.show_hidden &amp;&amp; list[i].name.charAt(0) == '.')
5990                                 continue;
5991
5992                         var entrypath = this.canonicalizePath(path + '/' + list[i].name),
5993                             selected = (entrypath == this.node.lastElementChild.value),
5994                             mtime = new Date(list[i].mtime * 1000);
5995
5996                         rows.appendChild(E('li', [
5997                                 E('div', { 'class': 'name' }, [
5998                                         this.iconForType(list[i].type),
5999                                         ' ',
6000                                         E('a', {
6001                                                 'href': '#',
6002                                                 'style': selected ? 'font-weight:bold' : null,
6003                                                 'click': UI.prototype.createHandlerFn(this, 'handleSelect',
6004                                                         entrypath, list[i].type != 'directory' ? list[i] : null)
6005                                         }, '%h'.format(list[i].name))
6006                                 ]),
6007                                 E('div', { 'class': 'mtime hide-xs' }, [
6008                                         ' %04d-%02d-%02d %02d:%02d:%02d '.format(
6009                                                 mtime.getFullYear(),
6010                                                 mtime.getMonth() + 1,
6011                                                 mtime.getDate(),
6012                                                 mtime.getHours(),
6013                                                 mtime.getMinutes(),
6014                                                 mtime.getSeconds())
6015                                 ]),
6016                                 E('div', [
6017                                         selected ? E('button', {
6018                                                 'class': 'btn',
6019                                                 'click': UI.prototype.createHandlerFn(this, 'handleReset')
6020                                         }, [ _('Deselect') ]) : '',
6021                                         this.options.enable_remove ? E('button', {
6022                                                 'class': 'btn cbi-button-negative',
6023                                                 'click': UI.prototype.createHandlerFn(this, 'handleDelete', entrypath, list[i])
6024                                         }, [ _('Delete') ]) : ''
6025                                 ])
6026                         ]));
6027                 }
6028
6029                 if (!rows.firstElementChild)
6030                         rows.appendChild(E('em', _('No entries in this directory')));
6031
6032                 var dirs = this.splitPath(path),
6033                     cur = '';
6034
6035                 for (var i = 0; i &lt; dirs.length; i++) {
6036                         cur = cur ? cur + '/' + dirs[i] : dirs[i];
6037                         dom.append(breadcrumb, [
6038                                 i ? ' Â» ' : '',
6039                                 E('a', {
6040                                         'href': '#',
6041                                         'click': UI.prototype.createHandlerFn(this, 'handleSelect', cur || '/', null)
6042                                 }, dirs[i] != '' ? '%h'.format(dirs[i]) : E('em', '(root)')),
6043                         ]);
6044                 }
6045
6046                 dom.content(container, [
6047                         breadcrumb,
6048                         rows,
6049                         E('div', { 'class': 'right' }, [
6050                                 this.renderUpload(path, list),
6051                                 E('a', {
6052                                         'href': '#',
6053                                         'class': 'btn',
6054                                         'click': UI.prototype.createHandlerFn(this, 'handleCancel')
6055                                 }, _('Cancel'))
6056                         ]),
6057                 ]);
6058         },
6059
6060         /** @private */
6061         handleCancel: function(ev) {
6062                 var button = this.node.firstElementChild,
6063                     browser = button.nextElementSibling;
6064
6065                 browser.classList.remove('open');
6066                 button.style.display = '';
6067
6068                 this.node.dispatchEvent(new CustomEvent('cbi-fileupload-cancel', {}));
6069
6070                 ev.preventDefault();
6071         },
6072
6073         /** @private */
6074         handleReset: function(ev) {
6075                 var button = this.node.firstElementChild,
6076                     hidden = this.node.lastElementChild;
6077
6078                 hidden.value = '';
6079                 dom.content(button, _('Select file…'));
6080
6081                 this.handleCancel(ev);
6082         },
6083
6084         /** @private */
6085         handleSelect: function(path, fileStat, ev) {
6086                 var browser = dom.parent(ev.target, '.cbi-filebrowser'),
6087                     ul = browser.querySelector('ul');
6088
6089                 if (fileStat == null) {
6090                         dom.content(ul, E('em', { 'class': 'spinning' }, _('Loading directory contents…')));
6091                         L.resolveDefault(fs.list(path), []).then(L.bind(this.renderListing, this, browser, path));
6092                 }
6093                 else {
6094                         var button = this.node.firstElementChild,
6095                             hidden = this.node.lastElementChild;
6096
6097                         path = this.canonicalizePath(path);
6098
6099                         dom.content(button, [
6100                                 this.iconForType(fileStat.type),
6101                                 ' %s (%1000mB)'.format(this.truncatePath(path), fileStat.size)
6102                         ]);
6103
6104                         browser.classList.remove('open');
6105                         button.style.display = '';
6106                         hidden.value = path;
6107
6108                         this.stat = Object.assign({ path: path }, fileStat);
6109                         this.node.dispatchEvent(new CustomEvent('cbi-fileupload-select', { detail: this.stat }));
6110                 }
6111         },
6112
6113         /** @private */
6114         handleFileBrowser: function(ev) {
6115                 var button = ev.target,
6116                     browser = button.nextElementSibling,
6117                     path = this.stat ? this.stat.path.replace(/\/[^\/]+$/, '') : (this.options.initial_directory || this.options.root_directory);
6118
6119                 if (path.indexOf(this.options.root_directory) != 0)
6120                         path = this.options.root_directory;
6121
6122                 ev.preventDefault();
6123
6124                 return L.resolveDefault(fs.list(path), []).then(L.bind(function(button, browser, path, list) {
6125                         document.querySelectorAll('.cbi-filebrowser.open').forEach(function(browserEl) {
6126                                 dom.findClassInstance(browserEl).handleCancel(ev);
6127                         });
6128
6129                         button.style.display = 'none';
6130                         browser.classList.add('open');
6131
6132                         return this.renderListing(browser, path, list);
6133                 }, this, button, browser, path));
6134         },
6135
6136         /** @override */
6137         getValue: function() {
6138                 return this.node.lastElementChild.value;
6139         },
6140
6141         /** @override */
6142         setValue: function(value) {
6143                 this.node.lastElementChild.value = value;
6144         }
6145 });
6146
6147 /**
6148  * @class ui
6149  * @memberof LuCI
6150  * @hideconstructor
6151  * @classdesc
6152  *
6153  * Provides high level UI helper functionality.
6154  * To import the class in views, use `'require ui'`, to import it in
6155  * external JavaScript, use `L.require("ui").then(...)`.
6156  */
6157 var UI = baseclass.extend(/** @lends LuCI.ui.prototype */ {
6158         __init__: function() {
6159                 modalDiv = document.body.appendChild(
6160                         dom.create('div', { id: 'modal_overlay' },
6161                                 dom.create('div', { class: 'modal', role: 'dialog', 'aria-modal': true })));
6162
6163                 tooltipDiv = document.body.appendChild(
6164                         dom.create('div', { class: 'cbi-tooltip' }));
6165
6166                 /* setup old aliases */
6167                 L.showModal = this.showModal;
6168                 L.hideModal = this.hideModal;
6169                 L.showTooltip = this.showTooltip;
6170                 L.hideTooltip = this.hideTooltip;
6171                 L.itemlist = this.itemlist;
6172
6173                 document.addEventListener('mouseover', this.showTooltip.bind(this), true);
6174                 document.addEventListener('mouseout', this.hideTooltip.bind(this), true);
6175                 document.addEventListener('focus', this.showTooltip.bind(this), true);
6176                 document.addEventListener('blur', this.hideTooltip.bind(this), true);
6177
6178                 document.addEventListener('luci-loaded', this.tabs.init.bind(this.tabs));
6179                 document.addEventListener('luci-loaded', this.changes.init.bind(this.changes));
6180                 document.addEventListener('uci-loaded', this.changes.init.bind(this.changes));
6181         },
6182
6183         /**
6184          * Display a modal overlay dialog with the specified contents.
6185          *
6186          * The modal overlay dialog covers the current view preventing interaction
6187          * with the underlying view contents. Only one modal dialog instance can
6188          * be opened. Invoking showModal() while a modal dialog is already open will
6189          * replace the open dialog with a new one having the specified contents.
6190          *
6191          * Additional CSS class names may be passed to influence the appearence of
6192          * the dialog. Valid values for the classes depend on the underlying theme.
6193          *
6194          * @see LuCI.dom.content
6195          *
6196          * @param {string} [title]
6197          * The title of the dialog. If `null`, no title element will be rendered.
6198          *
6199          * @param {*} contents
6200          * The contents to add to the modal dialog. This should be a DOM node or
6201          * a document fragment in most cases. The value is passed as-is to the
6202          * `dom.content()` function - refer to its documentation for applicable
6203          * values.
6204          *
6205          * @param {...string} [classes]
6206          * A number of extra CSS class names which are set on the modal dialog
6207          * element.
6208          *
6209          * @returns {Node}
6210          * Returns a DOM Node representing the modal dialog element.
6211          */
6212         showModal: function(title, children /* , ... */) {
6213                 var dlg = modalDiv.firstElementChild;
6214
6215                 dlg.setAttribute('class', 'modal');
6216
6217                 for (var i = 2; i &lt; arguments.length; i++)
6218                         dlg.classList.add(arguments[i]);
6219
6220                 dom.content(dlg, dom.create('h4', {}, title));
6221                 dom.append(dlg, children);
6222
6223                 document.body.classList.add('modal-overlay-active');
6224
6225                 return dlg;
6226         },
6227
6228         /**
6229          * Close the open modal overlay dialog.
6230          *
6231          * This function will close an open modal dialog and restore the normal view
6232          * behaviour. It has no effect if no modal dialog is currently open.
6233          *
6234          * Note that this function is stand-alone, it does not rely on `this` and
6235          * will not invoke other class functions so it suitable to be used as event
6236          * handler as-is without the need to bind it first.
6237          */
6238         hideModal: function() {
6239                 document.body.classList.remove('modal-overlay-active');
6240         },
6241
6242         /** @private */
6243         showTooltip: function(ev) {
6244                 var target = findParent(ev.target, '[data-tooltip]');
6245
6246                 if (!target)
6247                         return;
6248
6249                 if (tooltipTimeout !== null) {
6250                         window.clearTimeout(tooltipTimeout);
6251                         tooltipTimeout = null;
6252                 }
6253
6254                 var rect = target.getBoundingClientRect(),
6255                     x = rect.left              + window.pageXOffset,
6256                     y = rect.top + rect.height + window.pageYOffset;
6257
6258                 tooltipDiv.className = 'cbi-tooltip';
6259                 tooltipDiv.innerHTML = 'â–² ';
6260                 tooltipDiv.firstChild.data += target.getAttribute('data-tooltip');
6261
6262                 if (target.hasAttribute('data-tooltip-style'))
6263                         tooltipDiv.classList.add(target.getAttribute('data-tooltip-style'));
6264
6265                 if ((y + tooltipDiv.offsetHeight) > (window.innerHeight + window.pageYOffset)) {
6266                         y -= (tooltipDiv.offsetHeight + target.offsetHeight);
6267                         tooltipDiv.firstChild.data = 'â–¼ ' + tooltipDiv.firstChild.data.substr(2);
6268                 }
6269
6270                 tooltipDiv.style.top = y + 'px';
6271                 tooltipDiv.style.left = x + 'px';
6272                 tooltipDiv.style.opacity = 1;
6273
6274                 tooltipDiv.dispatchEvent(new CustomEvent('tooltip-open', {
6275                         bubbles: true,
6276                         detail: { target: target }
6277                 }));
6278         },
6279
6280         /** @private */
6281         hideTooltip: function(ev) {
6282                 if (ev.target === tooltipDiv || ev.relatedTarget === tooltipDiv ||
6283                     tooltipDiv.contains(ev.target) || tooltipDiv.contains(ev.relatedTarget))
6284                         return;
6285
6286                 if (tooltipTimeout !== null) {
6287                         window.clearTimeout(tooltipTimeout);
6288                         tooltipTimeout = null;
6289                 }
6290
6291                 tooltipDiv.style.opacity = 0;
6292                 tooltipTimeout = window.setTimeout(function() { tooltipDiv.removeAttribute('style'); }, 250);
6293
6294                 tooltipDiv.dispatchEvent(new CustomEvent('tooltip-close', { bubbles: true }));
6295         },
6296
6297         /**
6298          * Add a notification banner at the top of the current view.
6299          *
6300          * A notification banner is an alert message usually displayed at the
6301          * top of the current view, spanning the entire availibe width.
6302          * Notification banners will stay in place until dismissed by the user.
6303          * Multiple banners may be shown at the same time.
6304          *
6305          * Additional CSS class names may be passed to influence the appearence of
6306          * the banner. Valid values for the classes depend on the underlying theme.
6307          *
6308          * @see LuCI.dom.content
6309          *
6310          * @param {string} [title]
6311          * The title of the notification banner. If `null`, no title element
6312          * will be rendered.
6313          *
6314          * @param {*} contents
6315          * The contents to add to the notification banner. This should be a DOM
6316          * node or a document fragment in most cases. The value is passed as-is
6317          * to the `dom.content()` function - refer to its documentation for
6318          * applicable values.
6319          *
6320          * @param {...string} [classes]
6321          * A number of extra CSS class names which are set on the notification
6322          * banner element.
6323          *
6324          * @returns {Node}
6325          * Returns a DOM Node representing the notification banner element.
6326          */
6327         addNotification: function(title, children /*, ... */) {
6328                 var mc = document.querySelector('#maincontent') || document.body;
6329                 var msg = E('div', {
6330                         'class': 'alert-message fade-in',
6331                         'style': 'display:flex',
6332                         'transitionend': function(ev) {
6333                                 var node = ev.currentTarget;
6334                                 if (node.parentNode &amp;&amp; node.classList.contains('fade-out'))
6335                                         node.parentNode.removeChild(node);
6336                         }
6337                 }, [
6338                         E('div', { 'style': 'flex:10' }),
6339                         E('div', { 'style': 'flex:1 1 auto; display:flex' }, [
6340                                 E('button', {
6341                                         'class': 'btn',
6342                                         'style': 'margin-left:auto; margin-top:auto',
6343                                         'click': function(ev) {
6344                                                 dom.parent(ev.target, '.alert-message').classList.add('fade-out');
6345                                         },
6346
6347                                 }, [ _('Dismiss') ])
6348                         ])
6349                 ]);
6350
6351                 if (title != null)
6352                         dom.append(msg.firstElementChild, E('h4', {}, title));
6353
6354                 dom.append(msg.firstElementChild, children);
6355
6356                 for (var i = 2; i &lt; arguments.length; i++)
6357                         msg.classList.add(arguments[i]);
6358
6359                 mc.insertBefore(msg, mc.firstElementChild);
6360
6361                 return msg;
6362         },
6363
6364         /**
6365          * Display or update an header area indicator.
6366          *
6367          * An indicator is a small label displayed in the header area of the screen
6368          * providing few amounts of status information such as item counts or state
6369          * toggle indicators.
6370          *
6371          * Multiple indicators may be shown at the same time and indicator labels
6372          * may be made clickable to display extended information or to initiate
6373          * further actions.
6374          *
6375          * Indicators can either use a default `active` or a less accented `inactive`
6376          * style which is useful for indicators representing state toggles.
6377          *
6378          * @param {string} id
6379          * The ID of the indicator. If an indicator with the given ID already exists,
6380          * it is updated with the given label and style.
6381          *
6382          * @param {string} label
6383          * The text to display in the indicator label.
6384          *
6385          * @param {function} [handler]
6386          * A handler function to invoke when the indicator label is clicked/touched
6387          * by the user. If omitted, the indicator is not clickable/touchable.
6388          *
6389          * Note that this parameter only applies to new indicators, when updating
6390          * existing labels it is ignored.
6391          *
6392          * @param {string} [style=active]
6393          * The indicator style to use. May be either `active` or `inactive`.
6394          *
6395          * @returns {boolean}
6396          * Returns `true` when the indicator has been updated or `false` when no
6397          * changes were made.
6398          */
6399         showIndicator: function(id, label, handler, style) {
6400                 if (indicatorDiv == null) {
6401                         indicatorDiv = document.body.querySelector('#indicators');
6402
6403                         if (indicatorDiv == null)
6404                                 return false;
6405                 }
6406
6407                 var handlerFn = (typeof(handler) == 'function') ? handler : null,
6408                     indicatorElem = indicatorDiv.querySelector('span[data-indicator="%s"]'.format(id)) ||
6409                         indicatorDiv.appendChild(E('span', {
6410                                 'data-indicator': id,
6411                                 'data-clickable': handlerFn ? true : null,
6412                                 'click': handlerFn
6413                         }, ['']));
6414
6415                 if (label == indicatorElem.firstChild.data &amp;&amp; style == indicatorElem.getAttribute('data-style'))
6416                         return false;
6417
6418                 indicatorElem.firstChild.data = label;
6419                 indicatorElem.setAttribute('data-style', (style == 'inactive') ? 'inactive' : 'active');
6420                 return true;
6421         },
6422
6423         /**
6424          * Remove an header area indicator.
6425          *
6426          * This function removes the given indicator label from the header indicator
6427          * area. When the given indicator is not found, this function does nothing.
6428          *
6429          * @param {string} id
6430          * The ID of the indicator to remove.
6431          *
6432          * @returns {boolean}
6433          * Returns `true` when the indicator has been removed or `false` when the
6434          * requested indicator was not found.
6435          */
6436         hideIndicator: function(id) {
6437                 var indicatorElem = indicatorDiv ? indicatorDiv.querySelector('span[data-indicator="%s"]'.format(id)) : null;
6438
6439                 if (indicatorElem == null)
6440                         return false;
6441
6442                 indicatorDiv.removeChild(indicatorElem);
6443                 return true;
6444         },
6445
6446         /**
6447          * Formats a series of label/value pairs into list-like markup.
6448          *
6449          * This function transforms a flat array of alternating label and value
6450          * elements into a list-like markup, using the values in `separators` as
6451          * separators and appends the resulting nodes to the given parent DOM node.
6452          *
6453          * Each label is suffixed with `: ` and wrapped into a `&lt;strong>` tag, the
6454          * `&lt;strong>` element and the value corresponding to the label are
6455          * subsequently wrapped into a `&lt;span class="nowrap">` element.
6456          *
6457          * The resulting `&lt;span>` element tuples are joined by the given separators
6458          * to form the final markup which is appened to the given parent DOM node.
6459          *
6460          * @param {Node} node
6461          * The parent DOM node to append the markup to. Any previous child elements
6462          * will be removed.
6463          *
6464          * @param {Array&lt;*>} items
6465          * An alternating array of labels and values. The label values will be
6466          * converted to plain strings, the values are used as-is and may be of
6467          * any type accepted by `LuCI.dom.content()`.
6468          *
6469          * @param {*|Array&lt;*>} [separators=[E('br')]]
6470          * A single value or an array of separator values to separate each
6471          * label/value pair with. The function will cycle through the separators
6472          * when joining the pairs. If omitted, the default separator is a sole HTML
6473          * `&lt;br>` element. Separator values are used as-is and may be of any type
6474          * accepted by `LuCI.dom.content()`.
6475          *
6476          * @returns {Node}
6477          * Returns the parent DOM node the formatted markup has been added to.
6478          */
6479         itemlist: function(node, items, separators) {
6480                 var children = [];
6481
6482                 if (!Array.isArray(separators))
6483                         separators = [ separators || E('br') ];
6484
6485                 for (var i = 0; i &lt; items.length; i += 2) {
6486                         if (items[i+1] !== null &amp;&amp; items[i+1] !== undefined) {
6487                                 var sep = separators[(i/2) % separators.length],
6488                                     cld = [];
6489
6490                                 children.push(E('span', { class: 'nowrap' }, [
6491                                         items[i] ? E('strong', items[i] + ': ') : '',
6492                                         items[i+1]
6493                                 ]));
6494
6495                                 if ((i+2) &lt; items.length)
6496                                         children.push(dom.elem(sep) ? sep.cloneNode(true) : sep);
6497                         }
6498                 }
6499
6500                 dom.content(node, children);
6501
6502                 return node;
6503         },
6504
6505         /**
6506          * @class
6507          * @memberof LuCI.ui
6508          * @hideconstructor
6509          * @classdesc
6510          *
6511          * The `tabs` class handles tab menu groups used throughout the view area.
6512          * It takes care of setting up tab groups, tracking their state and handling
6513          * related events.
6514          *
6515          * This class is automatically instantiated as part of `LuCI.ui`. To use it
6516          * in views, use `'require ui'` and refer to `ui.tabs`. To import it in
6517          * external JavaScript, use `L.require("ui").then(...)` and access the
6518          * `tabs` property of the class instance value.
6519          */
6520         tabs: baseclass.singleton(/* @lends LuCI.ui.tabs.prototype */ {
6521                 /** @private */
6522                 init: function() {
6523                         var groups = [], prevGroup = null, currGroup = null;
6524
6525                         document.querySelectorAll('[data-tab]').forEach(function(tab) {
6526                                 var parent = tab.parentNode;
6527
6528                                 if (dom.matches(tab, 'li') &amp;&amp; dom.matches(parent, 'ul.cbi-tabmenu'))
6529                                         return;
6530
6531                                 if (!parent.hasAttribute('data-tab-group'))
6532                                         parent.setAttribute('data-tab-group', groups.length);
6533
6534                                 currGroup = +parent.getAttribute('data-tab-group');
6535
6536                                 if (currGroup !== prevGroup) {
6537                                         prevGroup = currGroup;
6538
6539                                         if (!groups[currGroup])
6540                                                 groups[currGroup] = [];
6541                                 }
6542
6543                                 groups[currGroup].push(tab);
6544                         });
6545
6546                         for (var i = 0; i &lt; groups.length; i++)
6547                                 this.initTabGroup(groups[i]);
6548
6549                         document.addEventListener('dependency-update', this.updateTabs.bind(this));
6550
6551                         this.updateTabs();
6552                 },
6553
6554                 /**
6555                  * Initializes a new tab group from the given tab pane collection.
6556                  *
6557                  * This function cycles through the given tab pane DOM nodes, extracts
6558                  * their tab IDs, titles and active states, renders a corresponding
6559                  * tab menu and prepends it to the tab panes common parent DOM node.
6560                  *
6561                  * The tab menu labels will be set to the value of the `data-tab-title`
6562                  * attribute of each corresponding pane. The last pane with the
6563                  * `data-tab-active` attribute set to `true` will be selected by default.
6564                  *
6565                  * If no pane is marked as active, the first one will be preselected.
6566                  *
6567                  * @instance
6568                  * @memberof LuCI.ui.tabs
6569                  * @param {Array&lt;Node>|NodeList} panes
6570                  * A collection of tab panes to build a tab group menu for. May be a
6571                  * plain array of DOM nodes or a NodeList collection, such as the result
6572                  * of a `querySelectorAll()` call or the `.childNodes` property of a
6573                  * DOM node.
6574                  */
6575                 initTabGroup: function(panes) {
6576                         if (typeof(panes) != 'object' || !('length' in panes) || panes.length === 0)
6577                                 return;
6578
6579                         var menu = E('ul', { 'class': 'cbi-tabmenu' }),
6580                             group = panes[0].parentNode,
6581                             groupId = +group.getAttribute('data-tab-group'),
6582                             selected = null;
6583
6584                         if (group.getAttribute('data-initialized') === 'true')
6585                                 return;
6586
6587                         for (var i = 0, pane; pane = panes[i]; i++) {
6588                                 var name = pane.getAttribute('data-tab'),
6589                                     title = pane.getAttribute('data-tab-title'),
6590                                     active = pane.getAttribute('data-tab-active') === 'true';
6591
6592                                 menu.appendChild(E('li', {
6593                                         'style': this.isEmptyPane(pane) ? 'display:none' : null,
6594                                         'class': active ? 'cbi-tab' : 'cbi-tab-disabled',
6595                                         'data-tab': name
6596                                 }, E('a', {
6597                                         'href': '#',
6598                                         'click': this.switchTab.bind(this)
6599                                 }, title)));
6600
6601                                 if (active)
6602                                         selected = i;
6603                         }
6604
6605                         group.parentNode.insertBefore(menu, group);
6606                         group.setAttribute('data-initialized', true);
6607
6608                         if (selected === null) {
6609                                 selected = this.getActiveTabId(panes[0]);
6610
6611                                 if (selected &lt; 0 || selected >= panes.length || this.isEmptyPane(panes[selected])) {
6612                                         for (var i = 0; i &lt; panes.length; i++) {
6613                                                 if (!this.isEmptyPane(panes[i])) {
6614                                                         selected = i;
6615                                                         break;
6616                                                 }
6617                                         }
6618                                 }
6619
6620                                 menu.childNodes[selected].classList.add('cbi-tab');
6621                                 menu.childNodes[selected].classList.remove('cbi-tab-disabled');
6622                                 panes[selected].setAttribute('data-tab-active', 'true');
6623
6624                                 this.setActiveTabId(panes[selected], selected);
6625                         }
6626
6627                         panes[selected].dispatchEvent(new CustomEvent('cbi-tab-active', {
6628                                 detail: { tab: panes[selected].getAttribute('data-tab') }
6629                         }));
6630
6631                         this.updateTabs(group);
6632                 },
6633
6634                 /**
6635                  * Checks whether the given tab pane node is empty.
6636                  *
6637                  * @instance
6638                  * @memberof LuCI.ui.tabs
6639                  * @param {Node} pane
6640                  * The tab pane to check.
6641                  *
6642                  * @returns {boolean}
6643                  * Returns `true` if the pane is empty, else `false`.
6644                  */
6645                 isEmptyPane: function(pane) {
6646                         return dom.isEmpty(pane, function(n) { return n.classList.contains('cbi-tab-descr') });
6647                 },
6648
6649                 /** @private */
6650                 getPathForPane: function(pane) {
6651                         var path = [], node = null;
6652
6653                         for (node = pane ? pane.parentNode : null;
6654                              node != null &amp;&amp; node.hasAttribute != null;
6655                              node = node.parentNode)
6656                         {
6657                                 if (node.hasAttribute('data-tab'))
6658                                         path.unshift(node.getAttribute('data-tab'));
6659                                 else if (node.hasAttribute('data-section-id'))
6660                                         path.unshift(node.getAttribute('data-section-id'));
6661                         }
6662
6663                         return path.join('/');
6664                 },
6665
6666                 /** @private */
6667                 getActiveTabState: function() {
6668                         var page = document.body.getAttribute('data-page');
6669
6670                         try {
6671                                 var val = JSON.parse(window.sessionStorage.getItem('tab'));
6672                                 if (val.page === page &amp;&amp; L.isObject(val.paths))
6673                                         return val;
6674                         }
6675                         catch(e) {}
6676
6677                         window.sessionStorage.removeItem('tab');
6678                         return { page: page, paths: {} };
6679                 },
6680
6681                 /** @private */
6682                 getActiveTabId: function(pane) {
6683                         var path = this.getPathForPane(pane);
6684                         return +this.getActiveTabState().paths[path] || 0;
6685                 },
6686
6687                 /** @private */
6688                 setActiveTabId: function(pane, tabIndex) {
6689                         var path = this.getPathForPane(pane);
6690
6691                         try {
6692                                 var state = this.getActiveTabState();
6693                                     state.paths[path] = tabIndex;
6694
6695                             window.sessionStorage.setItem('tab', JSON.stringify(state));
6696                         }
6697                         catch (e) { return false; }
6698
6699                         return true;
6700                 },
6701
6702                 /** @private */
6703                 updateTabs: function(ev, root) {
6704                         (root || document).querySelectorAll('[data-tab-title]').forEach(L.bind(function(pane) {
6705                                 var menu = pane.parentNode.previousElementSibling,
6706                                     tab = menu ? menu.querySelector('[data-tab="%s"]'.format(pane.getAttribute('data-tab'))) : null,
6707                                     n_errors = pane.querySelectorAll('.cbi-input-invalid').length;
6708
6709                                 if (!menu || !tab)
6710                                         return;
6711
6712                                 if (this.isEmptyPane(pane)) {
6713                                         tab.style.display = 'none';
6714                                         tab.classList.remove('flash');
6715                                 }
6716                                 else if (tab.style.display === 'none') {
6717                                         tab.style.display = '';
6718                                         requestAnimationFrame(function() { tab.classList.add('flash') });
6719                                 }
6720
6721                                 if (n_errors) {
6722                                         tab.setAttribute('data-errors', n_errors);
6723                                         tab.setAttribute('data-tooltip', _('%d invalid field(s)').format(n_errors));
6724                                         tab.setAttribute('data-tooltip-style', 'error');
6725                                 }
6726                                 else {
6727                                         tab.removeAttribute('data-errors');
6728                                         tab.removeAttribute('data-tooltip');
6729                                 }
6730                         }, this));
6731                 },
6732
6733                 /** @private */
6734                 switchTab: function(ev) {
6735                         var tab = ev.target.parentNode,
6736                             name = tab.getAttribute('data-tab'),
6737                             menu = tab.parentNode,
6738                             group = menu.nextElementSibling,
6739                             groupId = +group.getAttribute('data-tab-group'),
6740                             index = 0;
6741
6742                         ev.preventDefault();
6743
6744                         if (!tab.classList.contains('cbi-tab-disabled'))
6745                                 return;
6746
6747                         menu.querySelectorAll('[data-tab]').forEach(function(tab) {
6748                                 tab.classList.remove('cbi-tab');
6749                                 tab.classList.remove('cbi-tab-disabled');
6750                                 tab.classList.add(
6751                                         tab.getAttribute('data-tab') === name ? 'cbi-tab' : 'cbi-tab-disabled');
6752                         });
6753
6754                         group.childNodes.forEach(function(pane) {
6755                                 if (dom.matches(pane, '[data-tab]')) {
6756                                         if (pane.getAttribute('data-tab') === name) {
6757                                                 pane.setAttribute('data-tab-active', 'true');
6758                                                 pane.dispatchEvent(new CustomEvent('cbi-tab-active', { detail: { tab: name } }));
6759                                                 UI.prototype.tabs.setActiveTabId(pane, index);
6760                                         }
6761                                         else {
6762                                                 pane.setAttribute('data-tab-active', 'false');
6763                                         }
6764
6765                                         index++;
6766                                 }
6767                         });
6768                 }
6769         }),
6770
6771         /**
6772          * @typedef {Object} FileUploadReply
6773          * @memberof LuCI.ui
6774
6775          * @property {string} name - Name of the uploaded file without directory components
6776          * @property {number} size - Size of the uploaded file in bytes
6777          * @property {string} checksum - The MD5 checksum of the received file data
6778          * @property {string} sha256sum - The SHA256 checksum of the received file data
6779          */
6780
6781         /**
6782          * Display a modal file upload prompt.
6783          *
6784          * This function opens a modal dialog prompting the user to select and
6785          * upload a file to a predefined remote destination path.
6786          *
6787          * @param {string} path
6788          * The remote file path to upload the local file to.
6789          *
6790          * @param {Node} [progessStatusNode]
6791          * An optional DOM text node whose content text is set to the progress
6792          * percentage value during file upload.
6793          *
6794          * @returns {Promise&lt;LuCI.ui.FileUploadReply>}
6795          * Returns a promise resolving to a file upload status object on success
6796          * or rejecting with an error in case the upload failed or has been
6797          * cancelled by the user.
6798          */
6799         uploadFile: function(path, progressStatusNode) {
6800                 return new Promise(function(resolveFn, rejectFn) {
6801                         UI.prototype.showModal(_('Uploading file…'), [
6802                                 E('p', _('Please select the file to upload.')),
6803                                 E('div', { 'style': 'display:flex' }, [
6804                                         E('div', { 'class': 'left', 'style': 'flex:1' }, [
6805                                                 E('input', {
6806                                                         type: 'file',
6807                                                         style: 'display:none',
6808                                                         change: function(ev) {
6809                                                                 var modal = dom.parent(ev.target, '.modal'),
6810                                                                     body = modal.querySelector('p'),
6811                                                                     upload = modal.querySelector('.cbi-button-action.important'),
6812                                                                     file = ev.currentTarget.files[0];
6813
6814                                                                 if (file == null)
6815                                                                         return;
6816
6817                                                                 dom.content(body, [
6818                                                                         E('ul', {}, [
6819                                                                                 E('li', {}, [ '%s: %s'.format(_('Name'), file.name.replace(/^.*[\\\/]/, '')) ]),
6820                                                                                 E('li', {}, [ '%s: %1024mB'.format(_('Size'), file.size) ])
6821                                                                         ])
6822                                                                 ]);
6823
6824                                                                 upload.disabled = false;
6825                                                                 upload.focus();
6826                                                         }
6827                                                 }),
6828                                                 E('button', {
6829                                                         'class': 'btn',
6830                                                         'click': function(ev) {
6831                                                                 ev.target.previousElementSibling.click();
6832                                                         }
6833                                                 }, [ _('Browse…') ])
6834                                         ]),
6835                                         E('div', { 'class': 'right', 'style': 'flex:1' }, [
6836                                                 E('button', {
6837                                                         'class': 'btn',
6838                                                         'click': function() {
6839                                                                 UI.prototype.hideModal();
6840                                                                 rejectFn(new Error('Upload has been cancelled'));
6841                                                         }
6842                                                 }, [ _('Cancel') ]),
6843                                                 ' ',
6844                                                 E('button', {
6845                                                         'class': 'btn cbi-button-action important',
6846                                                         'disabled': true,
6847                                                         'click': function(ev) {
6848                                                                 var input = dom.parent(ev.target, '.modal').querySelector('input[type="file"]');
6849
6850                                                                 if (!input.files[0])
6851                                                                         return;
6852
6853                                                                 var progress = E('div', { 'class': 'cbi-progressbar', 'title': '0%' }, E('div', { 'style': 'width:0' }));
6854
6855                                                                 UI.prototype.showModal(_('Uploading file…'), [ progress ]);
6856
6857                                                                 var data = new FormData();
6858
6859                                                                 data.append('sessionid', rpc.getSessionID());
6860                                                                 data.append('filename', path);
6861                                                                 data.append('filedata', input.files[0]);
6862
6863                                                                 var filename = input.files[0].name;
6864
6865                                                                 request.post(L.env.cgi_base + '/cgi-upload', data, {
6866                                                                         timeout: 0,
6867                                                                         progress: function(pev) {
6868                                                                                 var percent = (pev.loaded / pev.total) * 100;
6869
6870                                                                                 if (progressStatusNode)
6871                                                                                         progressStatusNode.data = '%.2f%%'.format(percent);
6872
6873                                                                                 progress.setAttribute('title', '%.2f%%'.format(percent));
6874                                                                                 progress.firstElementChild.style.width = '%.2f%%'.format(percent);
6875                                                                         }
6876                                                                 }).then(function(res) {
6877                                                                         var reply = res.json();
6878
6879                                                                         UI.prototype.hideModal();
6880
6881                                                                         if (L.isObject(reply) &amp;&amp; reply.failure) {
6882                                                                                 UI.prototype.addNotification(null, E('p', _('Upload request failed: %s').format(reply.message)));
6883                                                                                 rejectFn(new Error(reply.failure));
6884                                                                         }
6885                                                                         else {
6886                                                                                 reply.name = filename;
6887                                                                                 resolveFn(reply);
6888                                                                         }
6889                                                                 }, function(err) {
6890                                                                         UI.prototype.hideModal();
6891                                                                         rejectFn(err);
6892                                                                 });
6893                                                         }
6894                                                 }, [ _('Upload') ])
6895                                         ])
6896                                 ])
6897                         ]);
6898                 });
6899         },
6900
6901         /**
6902          * Perform a device connectivity test.
6903          *
6904          * Attempt to fetch a well known ressource from the remote device via HTTP
6905          * in order to test connectivity. This function is mainly useful to wait
6906          * for the router to come back online after a reboot or reconfiguration.
6907          *
6908          * @param {string} [proto=http]
6909          * The protocol to use for fetching the resource. May be either `http`
6910          * (the default) or `https`.
6911          *
6912          * @param {string} [host=window.location.host]
6913          * Override the host address to probe. By default the current host as seen
6914          * in the address bar is probed.
6915          *
6916          * @returns {Promise&lt;Event>}
6917          * Returns a promise resolving to a `load` event in case the device is
6918          * reachable or rejecting with an `error` event in case it is not reachable
6919          * or rejecting with `null` when the connectivity check timed out.
6920          */
6921         pingDevice: function(proto, ipaddr) {
6922                 var target = '%s://%s%s?%s'.format(proto || 'http', ipaddr || window.location.host, L.resource('icons/loading.gif'), Math.random());
6923
6924                 return new Promise(function(resolveFn, rejectFn) {
6925                         var img = new Image();
6926
6927                         img.onload = resolveFn;
6928                         img.onerror = rejectFn;
6929
6930                         window.setTimeout(rejectFn, 1000);
6931
6932                         img.src = target;
6933                 });
6934         },
6935
6936         /**
6937          * Wait for device to come back online and reconnect to it.
6938          *
6939          * Poll each given hostname or IP address and navigate to it as soon as
6940          * one of the addresses becomes reachable.
6941          *
6942          * @param {...string} [hosts=[window.location.host]]
6943          * The list of IP addresses and host names to check for reachability.
6944          * If omitted, the current value of `window.location.host` is used by
6945          * default.
6946          */
6947         awaitReconnect: function(/* ... */) {
6948                 var ipaddrs = arguments.length ? arguments : [ window.location.host ];
6949
6950                 window.setTimeout(L.bind(function() {
6951                         poll.add(L.bind(function() {
6952                                 var tasks = [], reachable = false;
6953
6954                                 for (var i = 0; i &lt; 2; i++)
6955                                         for (var j = 0; j &lt; ipaddrs.length; j++)
6956                                                 tasks.push(this.pingDevice(i ? 'https' : 'http', ipaddrs[j])
6957                                                         .then(function(ev) { reachable = ev.target.src.replace(/^(https?:\/\/[^\/]+).*$/, '$1/') }, function() {}));
6958
6959                                 return Promise.all(tasks).then(function() {
6960                                         if (reachable) {
6961                                                 poll.stop();
6962                                                 window.location = reachable;
6963                                         }
6964                                 });
6965                         }, this));
6966                 }, this), 5000);
6967         },
6968
6969         /**
6970          * @class
6971          * @memberof LuCI.ui
6972          * @hideconstructor
6973          * @classdesc
6974          *
6975          * The `changes` class encapsulates logic for visualizing, applying,
6976          * confirming and reverting staged UCI changesets.
6977          *
6978          * This class is automatically instantiated as part of `LuCI.ui`. To use it
6979          * in views, use `'require ui'` and refer to `ui.changes`. To import it in
6980          * external JavaScript, use `L.require("ui").then(...)` and access the
6981          * `changes` property of the class instance value.
6982          */
6983         changes: baseclass.singleton(/* @lends LuCI.ui.changes.prototype */ {
6984                 init: function() {
6985                         if (!L.env.sessionid)
6986                                 return;
6987
6988                         return uci.changes().then(L.bind(this.renderChangeIndicator, this));
6989                 },
6990
6991                 /**
6992                  * Set the change count indicator.
6993                  *
6994                  * This function updates or hides the UCI change count indicator,
6995                  * depending on the passed change count. When the count is greater
6996                  * than 0, the change indicator is displayed or updated, otherwise it
6997                  * is removed.
6998                  *
6999                  * @instance
7000                  * @memberof LuCI.ui.changes
7001                  * @param {number} numChanges
7002                  * The number of changes to indicate.
7003                  */
7004                 setIndicator: function(n) {
7005                         var i = document.querySelector('.uci_change_indicator');
7006                         if (i == null) {
7007                                 var poll = document.getElementById('xhr_poll_status');
7008                                 i = poll.parentNode.insertBefore(E('a', {
7009                                         'href': '#',
7010                                         'class': 'uci_change_indicator label notice',
7011                                         'click': L.bind(this.displayChanges, this)
7012                                 }), poll);
7013                         }
7014
7015                         if (n > 0) {
7016                                 dom.content(i, [ _('Unsaved Changes'), ': ', n ]);
7017                                 i.classList.add('flash');
7018                                 i.style.display = '';
7019                                 document.dispatchEvent(new CustomEvent('uci-new-changes'));
7020                         }
7021                         else {
7022                                 i.classList.remove('flash');
7023                                 i.style.display = 'none';
7024                                 document.dispatchEvent(new CustomEvent('uci-clear-changes'));
7025                         }
7026                 },
7027
7028                 /**
7029                  * Update the change count indicator.
7030                  *
7031                  * This function updates the UCI change count indicator from the given
7032                  * UCI changeset structure.
7033                  *
7034                  * @instance
7035                  * @memberof LuCI.ui.changes
7036                  * @param {Object&lt;string, Array&lt;LuCI.uci.ChangeRecord>>} changes
7037                  * The UCI changeset to count.
7038                  */
7039                 renderChangeIndicator: function(changes) {
7040                         var n_changes = 0;
7041
7042                         for (var config in changes)
7043                                 if (changes.hasOwnProperty(config))
7044                                         n_changes += changes[config].length;
7045
7046                         this.changes = changes;
7047                         this.setIndicator(n_changes);
7048                 },
7049
7050                 /** @private */
7051                 changeTemplates: {
7052                         'add-3':      '&lt;ins>uci add %0 &lt;strong>%3&lt;/strong> # =%2&lt;/ins>',
7053                         'set-3':      '&lt;ins>uci set %0.&lt;strong>%2&lt;/strong>=%3&lt;/ins>',
7054                         'set-4':      '&lt;var>&lt;ins>uci set %0.%2.%3=&lt;strong>%4&lt;/strong>&lt;/ins>&lt;/var>',
7055                         'remove-2':   '&lt;del>uci del %0.&lt;strong>%2&lt;/strong>&lt;/del>',
7056                         'remove-3':   '&lt;var>&lt;del>uci del %0.%2.&lt;strong>%3&lt;/strong>&lt;/del>&lt;/var>',
7057                         'order-3':    '&lt;var>uci reorder %0.%2=&lt;strong>%3&lt;/strong>&lt;/var>',
7058                         'list-add-4': '&lt;var>&lt;ins>uci add_list %0.%2.%3=&lt;strong>%4&lt;/strong>&lt;/ins>&lt;/var>',
7059                         'list-del-4': '&lt;var>&lt;del>uci del_list %0.%2.%3=&lt;strong>%4&lt;/strong>&lt;/del>&lt;/var>',
7060                         'rename-3':   '&lt;var>uci rename %0.%2=&lt;strong>%3&lt;/strong>&lt;/var>',
7061                         'rename-4':   '&lt;var>uci rename %0.%2.%3=&lt;strong>%4&lt;/strong>&lt;/var>'
7062                 },
7063
7064                 /**
7065                  * Display the current changelog.
7066                  *
7067                  * Open a modal dialog visualizing the currently staged UCI changes
7068                  * and offer options to revert or apply the shown changes.
7069                  *
7070                  * @instance
7071                  * @memberof LuCI.ui.changes
7072                  */
7073                 displayChanges: function() {
7074                         var list = E('div', { 'class': 'uci-change-list' }),
7075                             dlg = UI.prototype.showModal(_('Configuration') + ' / ' + _('Changes'), [
7076                                 E('div', { 'class': 'cbi-section' }, [
7077                                         E('strong', _('Legend:')),
7078                                         E('div', { 'class': 'uci-change-legend' }, [
7079                                                 E('div', { 'class': 'uci-change-legend-label' }, [
7080                                                         E('ins', '&amp;#160;'), ' ', _('Section added') ]),
7081                                                 E('div', { 'class': 'uci-change-legend-label' }, [
7082                                                         E('del', '&amp;#160;'), ' ', _('Section removed') ]),
7083                                                 E('div', { 'class': 'uci-change-legend-label' }, [
7084                                                         E('var', {}, E('ins', '&amp;#160;')), ' ', _('Option changed') ]),
7085                                                 E('div', { 'class': 'uci-change-legend-label' }, [
7086                                                         E('var', {}, E('del', '&amp;#160;')), ' ', _('Option removed') ])]),
7087                                         E('br'), list,
7088                                         E('div', { 'class': 'right' }, [
7089                                                 E('button', {
7090                                                         'class': 'btn',
7091                                                         'click': UI.prototype.hideModal
7092                                                 }, [ _('Dismiss') ]), ' ',
7093                                                 E('button', {
7094                                                         'class': 'cbi-button cbi-button-positive important',
7095                                                         'click': L.bind(this.apply, this, true)
7096                                                 }, [ _('Save &amp; Apply') ]), ' ',
7097                                                 E('button', {
7098                                                         'class': 'cbi-button cbi-button-reset',
7099                                                         'click': L.bind(this.revert, this)
7100                                                 }, [ _('Revert') ])])])
7101                         ]);
7102
7103                         for (var config in this.changes) {
7104                                 if (!this.changes.hasOwnProperty(config))
7105                                         continue;
7106
7107                                 list.appendChild(E('h5', '# /etc/config/%s'.format(config)));
7108
7109                                 for (var i = 0, added = null; i &lt; this.changes[config].length; i++) {
7110                                         var chg = this.changes[config][i],
7111                                             tpl = this.changeTemplates['%s-%d'.format(chg[0], chg.length)];
7112
7113                                         list.appendChild(E(tpl.replace(/%([01234])/g, function(m0, m1) {
7114                                                 switch (+m1) {
7115                                                 case 0:
7116                                                         return config;
7117
7118                                                 case 2:
7119                                                         if (added != null &amp;&amp; chg[1] == added[0])
7120                                                                 return '@' + added[1] + '[-1]';
7121                                                         else
7122                                                                 return chg[1];
7123
7124                                                 case 4:
7125                                                         return "'%h'".format(chg[3].replace(/'/g, "'\"'\"'"));
7126
7127                                                 default:
7128                                                         return chg[m1-1];
7129                                                 }
7130                                         })));
7131
7132                                         if (chg[0] == 'add')
7133                                                 added = [ chg[1], chg[2] ];
7134                                 }
7135                         }
7136
7137                         list.appendChild(E('br'));
7138                         dlg.classList.add('uci-dialog');
7139                 },
7140
7141                 /** @private */
7142                 displayStatus: function(type, content) {
7143                         if (type) {
7144                                 var message = UI.prototype.showModal('', '');
7145
7146                                 message.classList.add('alert-message');
7147                                 DOMTokenList.prototype.add.apply(message.classList, type.split(/\s+/));
7148
7149                                 if (content)
7150                                         dom.content(message, content);
7151
7152                                 if (!this.was_polling) {
7153                                         this.was_polling = request.poll.active();
7154                                         request.poll.stop();
7155                                 }
7156                         }
7157                         else {
7158                                 UI.prototype.hideModal();
7159
7160                                 if (this.was_polling)
7161                                         request.poll.start();
7162                         }
7163                 },
7164
7165                 /** @private */
7166                 rollback: function(checked) {
7167                         if (checked) {
7168                                 this.displayStatus('warning spinning',
7169                                         E('p', _('Failed to confirm apply within %ds, waiting for rollback…')
7170                                                 .format(L.env.apply_rollback)));
7171
7172                                 var call = function(r, data, duration) {
7173                                         if (r.status === 204) {
7174                                                 UI.prototype.changes.displayStatus('warning', [
7175                                                         E('h4', _('Configuration changes have been rolled back!')),
7176                                                         E('p', _('The device could not be reached within %d seconds after applying the pending changes, which caused the configuration to be rolled back for safety reasons. If you believe that the configuration changes are correct nonetheless, perform an unchecked configuration apply. Alternatively, you can dismiss this warning and edit changes before attempting to apply again, or revert all pending changes to keep the currently working configuration state.').format(L.env.apply_rollback)),
7177                                                         E('div', { 'class': 'right' }, [
7178                                                                 E('button', {
7179                                                                         'class': 'btn',
7180                                                                         'click': L.bind(UI.prototype.changes.displayStatus, UI.prototype.changes, false)
7181                                                                 }, [ _('Dismiss') ]), ' ',
7182                                                                 E('button', {
7183                                                                         'class': 'btn cbi-button-action important',
7184                                                                         'click': L.bind(UI.prototype.changes.revert, UI.prototype.changes)
7185                                                                 }, [ _('Revert changes') ]), ' ',
7186                                                                 E('button', {
7187                                                                         'class': 'btn cbi-button-negative important',
7188                                                                         'click': L.bind(UI.prototype.changes.apply, UI.prototype.changes, false)
7189                                                                 }, [ _('Apply unchecked') ])
7190                                                         ])
7191                                                 ]);
7192
7193                                                 return;
7194                                         }
7195
7196                                         var delay = isNaN(duration) ? 0 : Math.max(1000 - duration, 0);
7197                                         window.setTimeout(function() {
7198                                                 request.request(L.url('admin/uci/confirm'), {
7199                                                         method: 'post',
7200                                                         timeout: L.env.apply_timeout * 1000,
7201                                                         query: { sid: L.env.sessionid, token: L.env.token }
7202                                                 }).then(call);
7203                                         }, delay);
7204                                 };
7205
7206                                 call({ status: 0 });
7207                         }
7208                         else {
7209                                 this.displayStatus('warning', [
7210                                         E('h4', _('Device unreachable!')),
7211                                         E('p', _('Could not regain access to the device after applying the configuration changes. You might need to reconnect if you modified network related settings such as the IP address or wireless security credentials.'))
7212                                 ]);
7213                         }
7214                 },
7215
7216                 /** @private */
7217                 confirm: function(checked, deadline, override_token) {
7218                         var tt;
7219                         var ts = Date.now();
7220
7221                         this.displayStatus('notice');
7222
7223                         if (override_token)
7224                                 this.confirm_auth = { token: override_token };
7225
7226                         var call = function(r, data, duration) {
7227                                 if (Date.now() >= deadline) {
7228                                         window.clearTimeout(tt);
7229                                         UI.prototype.changes.rollback(checked);
7230                                         return;
7231                                 }
7232                                 else if (r &amp;&amp; (r.status === 200 || r.status === 204)) {
7233                                         document.dispatchEvent(new CustomEvent('uci-applied'));
7234
7235                                         UI.prototype.changes.setIndicator(0);
7236                                         UI.prototype.changes.displayStatus('notice',
7237                                                 E('p', _('Configuration changes applied.')));
7238
7239                                         window.clearTimeout(tt);
7240                                         window.setTimeout(function() {
7241                                                 //UI.prototype.changes.displayStatus(false);
7242                                                 window.location = window.location.href.split('#')[0];
7243                                         }, L.env.apply_display * 1000);
7244
7245                                         return;
7246                                 }
7247
7248                                 var delay = isNaN(duration) ? 0 : Math.max(1000 - duration, 0);
7249                                 window.setTimeout(function() {
7250                                         request.request(L.url('admin/uci/confirm'), {
7251                                                 method: 'post',
7252                                                 timeout: L.env.apply_timeout * 1000,
7253                                                 query: UI.prototype.changes.confirm_auth
7254                                         }).then(call, call);
7255                                 }, delay);
7256                         };
7257
7258                         var tick = function() {
7259                                 var now = Date.now();
7260
7261                                 UI.prototype.changes.displayStatus('notice spinning',
7262                                         E('p', _('Applying configuration changes… %ds')
7263                                                 .format(Math.max(Math.floor((deadline - Date.now()) / 1000), 0))));
7264
7265                                 if (now >= deadline)
7266                                         return;
7267
7268                                 tt = window.setTimeout(tick, 1000 - (now - ts));
7269                                 ts = now;
7270                         };
7271
7272                         tick();
7273
7274                         /* wait a few seconds for the settings to become effective */
7275                         window.setTimeout(call, Math.max(L.env.apply_holdoff * 1000 - ((ts + L.env.apply_rollback * 1000) - deadline), 1));
7276                 },
7277
7278                 /**
7279                  * Apply the staged configuration changes.
7280                  *
7281                  * Start applying staged configuration changes and open a modal dialog
7282                  * with a progress indication to prevent interaction with the view
7283                  * during the apply process. The modal dialog will be automatically
7284                  * closed and the current view reloaded once the apply process is
7285                  * complete.
7286                  *
7287                  * @instance
7288                  * @memberof LuCI.ui.changes
7289                  * @param {boolean} [checked=false]
7290                  * Whether to perform a checked (`true`) configuration apply or an
7291                  * unchecked (`false`) one.
7292
7293                  * In case of a checked apply, the configuration changes must be
7294                  * confirmed within a specific time interval, otherwise the device
7295                  * will begin to roll back the changes in order to restore the previous
7296                  * settings.
7297                  */
7298                 apply: function(checked) {
7299                         this.displayStatus('notice spinning',
7300                                 E('p', _('Starting configuration apply…')));
7301
7302                         request.request(L.url('admin/uci', checked ? 'apply_rollback' : 'apply_unchecked'), {
7303                                 method: 'post',
7304                                 query: { sid: L.env.sessionid, token: L.env.token }
7305                         }).then(function(r) {
7306                                 if (r.status === (checked ? 200 : 204)) {
7307                                         var tok = null; try { tok = r.json(); } catch(e) {}
7308                                         if (checked &amp;&amp; tok !== null &amp;&amp; typeof(tok) === 'object' &amp;&amp; typeof(tok.token) === 'string')
7309                                                 UI.prototype.changes.confirm_auth = tok;
7310
7311                                         UI.prototype.changes.confirm(checked, Date.now() + L.env.apply_rollback * 1000);
7312                                 }
7313                                 else if (checked &amp;&amp; r.status === 204) {
7314                                         UI.prototype.changes.displayStatus('notice',
7315                                                 E('p', _('There are no changes to apply')));
7316
7317                                         window.setTimeout(function() {
7318                                                 UI.prototype.changes.displayStatus(false);
7319                                         }, L.env.apply_display * 1000);
7320                                 }
7321                                 else {
7322                                         UI.prototype.changes.displayStatus('warning',
7323                                                 E('p', _('Apply request failed with status &lt;code>%h&lt;/code>')
7324                                                         .format(r.responseText || r.statusText || r.status)));
7325
7326                                         window.setTimeout(function() {
7327                                                 UI.prototype.changes.displayStatus(false);
7328                                         }, L.env.apply_display * 1000);
7329                                 }
7330                         });
7331                 },
7332
7333                 /**
7334                  * Revert the staged configuration changes.
7335                  *
7336                  * Start reverting staged configuration changes and open a modal dialog
7337                  * with a progress indication to prevent interaction with the view
7338                  * during the revert process. The modal dialog will be automatically
7339                  * closed and the current view reloaded once the revert process is
7340                  * complete.
7341                  *
7342                  * @instance
7343                  * @memberof LuCI.ui.changes
7344                  */
7345                 revert: function() {
7346                         this.displayStatus('notice spinning',
7347                                 E('p', _('Reverting configuration…')));
7348
7349                         request.request(L.url('admin/uci/revert'), {
7350                                 method: 'post',
7351                                 query: { sid: L.env.sessionid, token: L.env.token }
7352                         }).then(function(r) {
7353                                 if (r.status === 200) {
7354                                         document.dispatchEvent(new CustomEvent('uci-reverted'));
7355
7356                                         UI.prototype.changes.setIndicator(0);
7357                                         UI.prototype.changes.displayStatus('notice',
7358                                                 E('p', _('Changes have been reverted.')));
7359
7360                                         window.setTimeout(function() {
7361                                                 //UI.prototype.changes.displayStatus(false);
7362                                                 window.location = window.location.href.split('#')[0];
7363                                         }, L.env.apply_display * 1000);
7364                                 }
7365                                 else {
7366                                         UI.prototype.changes.displayStatus('warning',
7367                                                 E('p', _('Revert request failed with status &lt;code>%h&lt;/code>')
7368                                                         .format(r.statusText || r.status)));
7369
7370                                         window.setTimeout(function() {
7371                                                 UI.prototype.changes.displayStatus(false);
7372                                         }, L.env.apply_display * 1000);
7373                                 }
7374                         });
7375                 }
7376         }),
7377
7378         /**
7379          * Add validation constraints to an input element.
7380          *
7381          * Compile the given type expression and optional validator function into
7382          * a validation function and bind it to the specified input element events.
7383          *
7384          * @param {Node} field
7385          * The DOM input element node to bind the validation constraints to.
7386          *
7387          * @param {string} type
7388          * The datatype specification to describe validation constraints.
7389          * Refer to the `LuCI.validation` class documentation for details.
7390          *
7391          * @param {boolean} [optional=false]
7392          * Specifies whether empty values are allowed (`true`) or not (`false`).
7393          * If an input element is not marked optional it must not be empty,
7394          * otherwise it will be marked as invalid.
7395          *
7396          * @param {function} [vfunc]
7397          * Specifies a custom validation function which is invoked after the
7398          * other validation constraints are applied. The validation must return
7399          * `true` to accept the passed value. Any other return type is converted
7400          * to a string and treated as validation error message.
7401          *
7402          * @param {...string} [events=blur, keyup]
7403          * The list of events to bind. Each received event will trigger a field
7404          * validation. If omitted, the `keyup` and `blur` events are bound by
7405          * default.
7406          *
7407          * @returns {function}
7408          * Returns the compiled validator function which can be used to manually
7409          * trigger field validation or to bind it to further events.
7410          *
7411          * @see LuCI.validation
7412          */
7413         addValidator: function(field, type, optional, vfunc /*, ... */) {
7414                 if (type == null)
7415                         return;
7416
7417                 var events = this.varargs(arguments, 3);
7418                 if (events.length == 0)
7419                         events.push('blur', 'keyup');
7420
7421                 try {
7422                         var cbiValidator = validation.create(field, type, optional, vfunc),
7423                             validatorFn = cbiValidator.validate.bind(cbiValidator);
7424
7425                         for (var i = 0; i &lt; events.length; i++)
7426                                 field.addEventListener(events[i], validatorFn);
7427
7428                         validatorFn();
7429
7430                         return validatorFn;
7431                 }
7432                 catch (e) { }
7433         },
7434
7435         /**
7436          * Create a pre-bound event handler function.
7437          *
7438          * Generate and bind a function suitable for use in event handlers. The
7439          * generated function automatically disables the event source element
7440          * and adds an active indication to it by adding appropriate CSS classes.
7441          *
7442          * It will also await any promises returned by the wrapped function and
7443          * re-enable the source element after the promises ran to completion.
7444          *
7445          * @param {*} ctx
7446          * The `this` context to use for the wrapped function.
7447          *
7448          * @param {function|string} fn
7449          * Specifies the function to wrap. In case of a function value, the
7450          * function is used as-is. If a string is specified instead, it is looked
7451          * up in `ctx` to obtain the function to wrap. In both cases the bound
7452          * function will be invoked with `ctx` as `this` context
7453          *
7454          * @param {...*} extra_args
7455          * Any further parameter as passed as-is to the bound event handler
7456          * function in the same order as passed to `createHandlerFn()`.
7457          *
7458          * @returns {function|null}
7459          * Returns the pre-bound handler function which is suitable to be passed
7460          * to `addEventListener()`. Returns `null` if the given `fn` argument is
7461          * a string which could not be found in `ctx` or if `ctx[fn]` is not a
7462          * valid function value.
7463          */
7464         createHandlerFn: function(ctx, fn /*, ... */) {
7465                 if (typeof(fn) == 'string')
7466                         fn = ctx[fn];
7467
7468                 if (typeof(fn) != 'function')
7469                         return null;
7470
7471                 var arg_offset = arguments.length - 2;
7472
7473                 return Function.prototype.bind.apply(function() {
7474                         var t = arguments[arg_offset].currentTarget;
7475
7476                         t.classList.add('spinning');
7477                         t.disabled = true;
7478
7479                         if (t.blur)
7480                                 t.blur();
7481
7482                         Promise.resolve(fn.apply(ctx, arguments)).finally(function() {
7483                                 t.classList.remove('spinning');
7484                                 t.disabled = false;
7485                         });
7486                 }, this.varargs(arguments, 2, ctx));
7487         },
7488
7489         /**
7490          * Load specified view class path and set it up.
7491          *
7492          * Transforms the given view path into a class name, requires it
7493          * using [LuCI.require()]{@link LuCI#require} and asserts that the
7494          * resulting class instance is a descendant of
7495          * [LuCI.view]{@link LuCI.view}.
7496          *
7497          * By instantiating the view class, its corresponding contents are
7498          * rendered and included into the view area. Any runtime errors are
7499          * catched and rendered using [LuCI.error()]{@link LuCI#error}.
7500          *
7501          * @param {string} path
7502          * The view path to render.
7503          *
7504          * @returns {Promise&lt;LuCI.view>}
7505          * Returns a promise resolving to the loaded view instance.
7506          */
7507         instantiateView: function(path) {
7508                 var className = 'view.%s'.format(path.replace(/\//g, '.'));
7509
7510                 return L.require(className).then(function(view) {
7511                         if (!(view instanceof View))
7512                                 throw new TypeError('Loaded class %s is not a descendant of View'.format(className));
7513
7514                         return view;
7515                 }).catch(function(err) {
7516                         dom.content(document.querySelector('#view'), null);
7517                         L.error(err);
7518                 });
7519         },
7520
7521         AbstractElement: UIElement,
7522
7523         /* Widgets */
7524         Textfield: UITextfield,
7525         Textarea: UITextarea,
7526         Checkbox: UICheckbox,
7527         Select: UISelect,
7528         Dropdown: UIDropdown,
7529         DynamicList: UIDynamicList,
7530         Combobox: UICombobox,
7531         ComboButton: UIComboButton,
7532         Hiddenfield: UIHiddenfield,
7533         FileUpload: UIFileUpload
7534 });
7535
7536 return UI;
7537 </code></pre>
7538         </article>
7539     </section>
7540
7541
7542
7543
7544
7545
7546         
7547
7548         <footer>
7549             Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.6.3</a> on Tue Apr 07 2020 16:33:40 GMT+0200 (Central European Summer Time)
7550         </footer>
7551     </div>
7552 </div>
7553 <script>prettyPrint();</script>
7554 <script src="scripts/jaguar.js"></script>
7555 </body>
7556 </html>