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