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